Index: sys/netinet6/raw_ip6.c =================================================================== --- sys/netinet6/raw_ip6.c +++ sys/netinet6/raw_ip6.c @@ -133,6 +133,50 @@ #endif /* VIMAGE */ /* + * Hash functions + */ + +#define INP_PCBHASH_RAW_SIZE 256 +#define INP_PCBHASH_RAW(proto, laddr, faddr, mask) \ + (((proto) + (*laddr) + (*faddr)) % (mask) + 1) + +#define IN6_ADDR_IS_NOT_ANY(in6) \ + ((in6.s6_addr32[0] != 0) && \ + (in6.s6_addr32[1] != 0) && \ + (in6.s6_addr32[2] != 0) && \ + (in6.s6_addr32[3] != 0)) + +static void +rip6_inshash(struct inpcb *inp) +{ + struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; + struct inpcbhead *pcbhash; + int hash; + + INP_INFO_WLOCK_ASSERT(pcbinfo); + INP_WLOCK_ASSERT(inp); + + if (inp->inp_ip_p != 0 && IN6_ADDR_IS_NOT_ANY(inp->in6p_laddr) && + IN6_ADDR_IS_NOT_ANY(inp->in6p_faddr)) { + hash = INP_PCBHASH_RAW(inp->inp_ip_p, inp->in6p_laddr.s6_addr, + inp->in6p_faddr.s6_addr, pcbinfo->ipi_hashmask); + } else + hash = 0; + pcbhash = &pcbinfo->ipi_hashbase[hash]; + CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash); +} + +static void +rip6_delhash(struct inpcb *inp) +{ + + INP_INFO_WLOCK_ASSERT(inp->inp_pcbinfo); + INP_WLOCK_ASSERT(inp); + + CK_LIST_REMOVE(inp, inp_hash); +} + +/* * Hooks for multicast routing. They all default to NULL, so leave them not * initialized and rely on BSS being set to 0. */ @@ -165,6 +209,7 @@ struct inpcb *last = NULL; struct mbuf *opts = NULL; struct sockaddr_in6 fromsa; + int hash; NET_EPOCH_ASSERT(); @@ -174,10 +219,63 @@ ifp = m->m_pkthdr.rcvif; - CK_LIST_FOREACH(inp, &V_ripcb, inp_list) { + hash = INP_PCBHASH_RAW(proto, ip6->ip6_src.s6_addr, + ip6->ip6_dst.s6_addr, V_ripcbinfo.ipi_hashmask); + + CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[hash], inp_hash) { + if (inp->inp_ip_p != proto) + continue; /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; + if (inp->in6p_laddr.s6_addr != ip6->ip6_dst.s6_addr) + continue; + if (inp->in6p_faddr.s6_addr != ip6->ip6_src.s6_addr) + continue; + if (last != NULL) { + struct mbuf *n = m_copym(m, 0, M_COPYALL, M_NOWAIT); + + if (n != NULL) { + if (last->inp_flags & INP_CONTROLOPTS || + last->inp_socket->so_options & SO_TIMESTAMP) + ip6_savecontrol(last, n, &opts); + /* strip intermediate headers */ + m_adj(n, *offp); + if (sbappendaddr(&last->inp_socket->so_rcv, + (struct sockaddr *)&fromsa, + n, opts) == 0) { + m_freem(n); + if (opts) + m_freem(opts); + RIP6STAT_INC(rip6s_fullsock); + } else + sorwakeup(last->inp_socket); + } + /* XXX count dropped packet */ + INP_RUNLOCK(last); + last = NULL; + } + INP_RLOCK(inp); + if (__predict_false(inp->inp_flags2 & INP_FREED)) + goto skip_1; + if (jailed_without_vnet(inp->inp_cred)) { + /* + * XXX: If faddr was bound to multicast group, + * jailed raw socket will drop datagram. + */ + if (prison_check_ip6(inp->inp_cred, &ip6->ip6_dst) != 0) + goto skip_1; + } + last = inp; + continue; + skip_1: + INP_RUNLOCK(inp); + } + + CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[0], inp_hash) { + /* XXX inp locking */ + if ((inp->inp_vflag & INP_IPV6) == 0) + continue; if (inp->inp_ip_p && inp->inp_ip_p != proto) continue; @@ -665,13 +763,14 @@ return (error); } inp = (struct inpcb *)so->so_pcb; - INP_INFO_WUNLOCK(&V_ripcbinfo); inp->inp_vflag |= INP_IPV6; inp->inp_ip_p = (long)proto; inp->in6p_hops = -1; /* use kernel default */ inp->in6p_cksum = -1; inp->in6p_icmp6filt = filter; ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); + rip6_inshash(inp); + INP_INFO_WUNLOCK(&V_ripcbinfo); INP_WUNLOCK(inp); return (0); } @@ -689,6 +788,7 @@ /* xxx: RSVP */ INP_INFO_WLOCK(&V_ripcbinfo); INP_WLOCK(inp); + rip6_delhash(inp); free(inp->in6p_icmp6filt, M_PCB); in_pcbdetach(inp); in_pcbfree(inp); @@ -770,7 +870,9 @@ NET_EPOCH_EXIT(et); INP_INFO_WLOCK(&V_ripcbinfo); INP_WLOCK(inp); + rip6_delhash(inp); inp->in6p_laddr = addr->sin6_addr; + rip6_inshash(inp); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_ripcbinfo); return (0); @@ -809,6 +911,7 @@ INP_INFO_WLOCK(&V_ripcbinfo); INP_WLOCK(inp); + rip6_delhash(inp); /* Source address selection. XXX: need pcblookup? */ error = in6_selectsrc_socket(addr, inp->in6p_outputopts, inp, so->so_cred, scope_ambiguous, &in6a, NULL); @@ -820,6 +923,7 @@ inp->in6p_faddr = addr->sin6_addr; inp->in6p_laddr = in6a; + rip6_inshash(inp); soisconnected(so); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_ripcbinfo);