#include "opt_inet.h"
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/thread.h>
#include <sys/sysctl.h>
#include <sys/globaldata.h>
#include <net/if.h>
#include <net/netisr2.h>
#include <net/toeplitz2.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
struct initport_index {
uint32_t port_index;
} __cachealign;
static struct initport_index initport_indices[MAXCPU];
static __inline int
INP_MPORT_HASH_UDP(in_addr_t faddr, in_addr_t laddr,
in_port_t fport, in_port_t lport)
{
if (IN_MULTICAST(ntohl(faddr)) || IN_MULTICAST(ntohl(laddr))) {
return 0;
}
return toeplitz_hash(toeplitz_rawhash_addr(faddr, laddr));
}
static __inline int
INP_MPORT_HASH_TCP(in_addr_t faddr, in_addr_t laddr,
in_port_t fport, in_port_t lport)
{
return toeplitz_hash(
toeplitz_rawhash_addrport(faddr, laddr, fport, lport));
}
int
tcp_addrhash(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return (INP_MPORT_HASH_TCP(faddr, laddr, fport, lport));
}
int
udp_addrhash(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return (INP_MPORT_HASH_UDP(faddr, laddr, fport, lport));
}
int
tcp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return (netisr_hashcpu(INP_MPORT_HASH_TCP(faddr, laddr, fport, lport)));
}
int
udp_addrcpu(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return (netisr_hashcpu(INP_MPORT_HASH_UDP(faddr, laddr, fport, lport)));
}
boolean_t
ip_lengthcheck(struct mbuf **mp, int hoff)
{
struct mbuf *m = *mp;
struct ip *ip;
int len, iphlen, iplen;
struct tcphdr *th;
int thoff;
len = hoff + sizeof(struct ip);
if (m->m_pkthdr.len < len) {
ipstat.ips_tooshort++;
goto fail;
}
if (m->m_len < len) {
m = m_pullup(m, len);
if (m == NULL) {
ipstat.ips_toosmall++;
goto fail;
}
}
ip = mtodoff(m, struct ip *, hoff);
iphlen = ip->ip_hl << 2;
if (iphlen < sizeof(struct ip)) {
ipstat.ips_badhlen++;
goto fail;
}
if (m->m_len < hoff + iphlen) {
m = m_pullup(m, hoff + iphlen);
if (m == NULL) {
ipstat.ips_badhlen++;
goto fail;
}
ip = mtodoff(m, struct ip *, hoff);
}
iplen = ntohs(ip->ip_len);
if (m->m_pkthdr.len < hoff + iplen) {
ipstat.ips_tooshort++;
goto fail;
}
if (ip->ip_off & htons(IP_OFFMASK))
goto ipcheckonly;
switch (ip->ip_p) {
case IPPROTO_TCP:
if (iplen < iphlen + sizeof(struct tcphdr)) {
++tcpstat.tcps_rcvshort;
goto fail;
}
if (m->m_len < hoff + iphlen + sizeof(struct tcphdr)) {
m = m_pullup(m, hoff + iphlen + sizeof(struct tcphdr));
if (m == NULL) {
tcpstat.tcps_rcvshort++;
goto fail;
}
ip = mtodoff(m, struct ip *, hoff);
}
th = (struct tcphdr *)((caddr_t)ip + iphlen);
thoff = th->th_off << 2;
if (thoff < sizeof(struct tcphdr) ||
thoff + iphlen > ntohs(ip->ip_len)) {
tcpstat.tcps_rcvbadoff++;
goto fail;
}
if (m->m_len < hoff + iphlen + thoff) {
m = m_pullup(m, hoff + iphlen + thoff);
if (m == NULL) {
tcpstat.tcps_rcvshort++;
goto fail;
}
}
break;
case IPPROTO_UDP:
if (iplen < iphlen + sizeof(struct udphdr)) {
++udp_stat.udps_hdrops;
goto fail;
}
if (m->m_len < hoff + iphlen + sizeof(struct udphdr)) {
m = m_pullup(m, hoff + iphlen + sizeof(struct udphdr));
if (m == NULL) {
udp_stat.udps_hdrops++;
goto fail;
}
}
break;
default:
ipcheckonly:
if (iplen < iphlen) {
++ipstat.ips_badlen;
goto fail;
}
break;
}
m->m_flags |= M_LENCHECKED;
*mp = m;
return TRUE;
fail:
if (m != NULL)
m_freem(m);
*mp = NULL;
return FALSE;
}
void
ip_hashfn(struct mbuf **mptr, int hoff)
{
struct ip *ip;
int iphlen;
struct tcphdr *th;
struct udphdr *uh;
struct mbuf *m;
int hash;
if (((*mptr)->m_flags & M_LENCHECKED) == 0) {
if (!ip_lengthcheck(mptr, hoff))
return;
}
m = *mptr;
ip = mtodoff(m, struct ip *, hoff);
iphlen = ip->ip_hl << 2;
if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) {
hash = toeplitz_hash(toeplitz_rawhash_addr(
ip->ip_src.s_addr, ip->ip_dst.s_addr));
goto back;
}
switch (ip->ip_p) {
case IPPROTO_TCP:
th = (struct tcphdr *)((caddr_t)ip + iphlen);
hash = INP_MPORT_HASH_TCP(ip->ip_src.s_addr, ip->ip_dst.s_addr,
th->th_sport, th->th_dport);
break;
case IPPROTO_UDP:
uh = (struct udphdr *)((caddr_t)ip + iphlen);
hash = INP_MPORT_HASH_UDP(ip->ip_src.s_addr, ip->ip_dst.s_addr,
uh->uh_sport, uh->uh_dport);
break;
default:
hash = 0;
break;
}
back:
m_sethash(m, hash);
}
void
ip_hashcheck(struct mbuf *m, const struct pktinfo *pi)
{
KASSERT((m->m_flags & M_HASH), ("no valid packet hash"));
switch (pi->pi_l3proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
break;
default:
m->m_flags &= ~M_HASH;
break;
}
}
lwkt_port_t
tcp_soport(struct socket *so, struct sockaddr *nam,
struct mbuf **dummy __unused)
{
return(so->so_port);
}
lwkt_port_t
tcp_ctlport(int cmd, struct sockaddr *sa, void *vip, int *cpuid)
{
struct ip *ip = vip;
inp_notify_t notify;
int arg;
notify = tcp_get_inpnotify(cmd, sa, &arg, &ip, cpuid);
if (notify == NULL)
return NULL;
if (*cpuid == netisr_ncpus) {
return netisr_cpuport(0);
} else {
return netisr_cpuport(*cpuid);
}
}
lwkt_port_t
tcp_addrport(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return(netisr_cpuport(tcp_addrcpu(faddr, fport, laddr, lport)));
}
lwkt_port_t
tcp_addrport0(void)
{
return(netisr_cpuport(0));
}
lwkt_port_t
udp_addrport(in_addr_t faddr, in_port_t fport, in_addr_t laddr, in_port_t lport)
{
return(netisr_cpuport(udp_addrcpu(faddr, fport, laddr, lport)));
}
lwkt_port_t
udp_ctlport(int cmd, struct sockaddr *sa, void *vip, int *cpuid)
{
struct ip *ip = vip;
inp_notify_t notify;
notify = udp_get_inpnotify(cmd, sa, &ip, cpuid);
if (notify == NULL)
return NULL;
if (*cpuid == netisr_ncpus) {
return netisr_cpuport(0);
} else {
return netisr_cpuport(*cpuid);
}
}
static __inline struct lwkt_port *
inp_initport(void)
{
int cpu = mycpuid;
if (cpu < netisr_ncpus) {
return netisr_cpuport(cpu);
} else {
return netisr_cpuport(
((initport_indices[cpu].port_index++) + (uint32_t)cpu) %
netisr_ncpus);
}
}
struct lwkt_port *
tcp_initport(void)
{
return inp_initport();
}
struct lwkt_port *
udp_initport(void)
{
return inp_initport();
}