--- sys/netinet/ip_input.c.orig 2016-09-05 01:41:03.050600000 +0700 +++ sys/netinet/ip_input.c 2016-09-07 18:57:26.362171000 +0700 @@ -84,6 +84,11 @@ __FBSDID("$FreeBSD: stable/11/sys/netine #endif /* IPSEC */ #include <netinet/in_rss.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/sctp.h> +#include <sys/hash.h> + #include <sys/socketvar.h> #include <security/mac/mac_framework.h> @@ -137,9 +142,18 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, check VNET_DEFINE(struct pfil_head, inet_pfil_hook); /* Packet filter hooks */ +static VNET_DEFINE(int, skip_flowid_gen) = 0; +SYSCTL_INT(_net_inet_ip, OID_AUTO, skip_flowid_gen, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(skip_flowid_gen), 0, + "Disable flow id generation for input packets"); + +static VNET_DEFINE(uint32_t, flow_hashjitter); +#define V_flow_hashjitter VNET(flow_hashjitter) +static struct mbuf * ip_hash_mbuf(struct mbuf *m, uintptr_t source); static struct netisr_handler ip_nh = { .nh_name = "ip", .nh_handler = ip_input, + .nh_m2flow = ip_hash_mbuf, .nh_proto = NETISR_IP, #ifdef RSS .nh_m2cpuid = rss_soft_m2cpuid_v4, @@ -311,6 +325,9 @@ ip_init(void) /* Initialize IP reassembly queue. */ ipreass_init(); + if (V_flow_hashjitter == 0) + V_flow_hashjitter = arc4random(); + /* Initialize packet filter hooks. */ V_inet_pfil_hook.ph_type = PFIL_TYPE_AF; V_inet_pfil_hook.ph_af = AF_INET; @@ -436,6 +453,76 @@ ip_direct_input(struct mbuf *m) } #endif +static struct mbuf * +ip_hash_mbuf(struct mbuf *m, uintptr_t source) +{ + struct ip *ip; + uint8_t proto; + int iphlen, offset; + uint32_t key[3]; + struct tcphdr *th; + struct udphdr *uh; + struct sctphdr *sh; + uint16_t sport = 0, dport = 0; + uint32_t flowid, pullup_len = 0; + +#define M_CHECK(length) do { \ + pullup_len += length; \ + if ((m)->m_pkthdr.len < (pullup_len)) \ + return (m); \ + if ((m)->m_len < (pullup_len) && \ + (((m) = m_pullup((m),(pullup_len))) == NULL)) \ + return NULL; \ +} while (0) + + if (skip_flowid_gen) + return m; + + M_CHECK(sizeof(struct ip)); + ip = mtod(m, struct ip *); + + proto = ip->ip_p; + iphlen = ip->ip_hl << 2; /* XXX options? */ + + key[0] = 0; + key[1] = ip->ip_src.s_addr; + key[2] = ip->ip_dst.s_addr; + + switch (proto) { + case IPPROTO_TCP: + M_CHECK(sizeof(struct tcphdr)); + th = (struct tcphdr *)((caddr_t)ip + iphlen); + sport = th->th_sport; + dport = th->th_dport; + break; + case IPPROTO_UDP: + M_CHECK(sizeof(struct udphdr)); + uh = (struct udphdr *)((caddr_t)ip + iphlen); + sport = uh->uh_sport; + dport = uh->uh_dport; + break; + case IPPROTO_SCTP: + M_CHECK(sizeof(struct sctphdr)); + sh = (struct sctphdr *)((caddr_t)ip + iphlen); + sport = sh->src_port; + dport = sh->dest_port; + break; + } + + if (sport > 0) { + ((uint16_t *)key)[0] = sport; + ((uint16_t *)key)[1] = dport; + offset = 0; + } else + offset = V_flow_hashjitter + proto; + + flowid = jenkins_hash32(key, 3, offset); + m->m_pkthdr.flowid = flowid; + M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); + + return m; +} + /* * Ip input routine. Checksum and byte swap header. If fragmented * try to reassemble. Process options. Pass to next level.