diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -226,6 +226,21 @@ VNET_DEFINE(struct inpcbinfo, tcbinfo); +static u_int tcp_input_nohash; +SYSCTL_UINT(_net_inet_tcp, OID_AUTO, input_nohash, CTLFLAG_RD, + &tcp_input_nohash, 0, + "Count of input packets w/o RSS hash"); + +static u_int tcp_input_badhash; +SYSCTL_UINT(_net_inet_tcp, OID_AUTO, input_badhash, CTLFLAG_RD, + &tcp_input_badhash, 0, + "Count of input packets w/ incorrect RSS hash"); + +static u_int tcp_verify_hash; +SYSCTL_UINT(_net_inet_tcp, OID_AUTO, input_verify_hash, CTLFLAG_RW, + &tcp_verify_hash, 0, + "Verify RSS hash on incoming frames?"); + /* * TCP statistics are stored in an array of counter(9)s, which size matches * size of struct tcpstat. TCP running connection count is a regular array. @@ -590,6 +605,8 @@ } #endif /* INET6 */ +static int tcp_hash_print_limit = 50; + int tcp_input_with_port(struct mbuf **mp, int *offp, int proto, uint16_t port) { @@ -929,8 +946,8 @@ if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) { inp->inp_flowid = m->m_pkthdr.flowid; inp->inp_flowtype = M_HASHTYPE_GET(m); -#ifdef RSS } else { + atomic_add_int(&tcp_input_nohash, 1); /* assign flowid by software RSS hash */ #ifdef INET6 if (isipv6) { @@ -952,7 +969,54 @@ &inp->inp_flowid, &inp->inp_flowtype); } -#endif /* RSS */ + } + } else if (!SOLISTENING(inp->inp_socket)) { + if (tcp_verify_hash) { + uint32_t flowid, flowtype; +#ifdef INET6 + if (isipv6) { + rss_proto_software_hash_v6(&inp->in6p_faddr, + &inp->in6p_laddr, + inp->inp_fport, + inp->inp_lport, + IPPROTO_TCP, + &flowid, + &flowtype); + } else +#endif /* INET6 */ + { + rss_proto_software_hash_v4(inp->inp_faddr, + inp->inp_laddr, + inp->inp_fport, + inp->inp_lport, + IPPROTO_TCP, + &flowid, + &flowtype); + } + if (flowid != inp->inp_flowid || + flowtype != inp->inp_flowtype) { + atomic_add_int(&tcp_input_badhash, 1); + if (tcp_hash_print_limit > 0) { + atomic_add_int(&tcp_hash_print_limit, -1); + if (m == NULL || m->m_pkthdr.rcvif == NULL) + printf("bad hash, m = %p, %p\n", + m, m == NULL ? 0 : m->m_pkthdr.rcvif); + else if (isipv6) + if_printf(m->m_pkthdr.rcvif, + "bad hash6 %lx:%x %lx:%x %d vs %d (%x/%x)\n", + *(long *)&inp->in6p_faddr, inp->inp_fport, + *(long *)&inp->in6p_laddr, inp->inp_lport, + flowid, inp->inp_flowid, + flowtype, inp->inp_flowtype); + else + if_printf(m->m_pkthdr.rcvif, + "bad hash4 %x:%x %x:%x %d vs %d (%x/%x)\n", + *(int *)&inp->inp_faddr, inp->inp_fport, + *(int *)&inp->inp_laddr, inp->inp_lport, + flowid, inp->inp_flowid, + flowtype, inp->inp_flowtype); + } + } } } #if defined(IPSEC) || defined(IPSEC_SUPPORT)