Index: sys/netinet6/raw_ip6.c =================================================================== --- sys/netinet6/raw_ip6.c +++ sys/netinet6/raw_ip6.c @@ -133,6 +133,44 @@ #endif /* VIMAGE */ /* + * Hash functions + */ + +#define INP_PCBHASH_RAW_SIZE 256 +#define INP_PCBHASH_RAW(proto, laddr, faddr, mask) \ + (((proto) + IN6ADDR_HASHVAL(laddr) + IN6ADDR_HASHVAL(faddr)) % (mask) + 1) + +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_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && + IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { + hash = INP_PCBHASH_RAW(inp->inp_ip_p, &inp->in6p_laddr, + &inp->in6p_faddr, 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. */ @@ -161,10 +199,11 @@ struct ifnet *ifp; struct mbuf *m = *mp; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - struct inpcb *inp; + struct inpcb *inp = NULL; struct inpcb *last = NULL; struct mbuf *opts = NULL; struct sockaddr_in6 fromsa; + int hash; NET_EPOCH_ASSERT(); @@ -174,36 +213,22 @@ ifp = m->m_pkthdr.rcvif; - CK_LIST_FOREACH(inp, &V_ripcb, inp_list) { + hash = INP_PCBHASH_RAW(proto, &ip6->ip6_src, &ip6->ip6_dst, + 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->inp_ip_p && - inp->inp_ip_p != proto) + if (inp->in6p_laddr.s6_addr != ip6->ip6_dst.s6_addr) continue; - if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && - !IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &ip6->ip6_dst)) + if (inp->in6p_faddr.s6_addr != ip6->ip6_src.s6_addr) continue; - if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && - !IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &ip6->ip6_src)) - continue; if (last != NULL) { struct mbuf *n = m_copym(m, 0, M_COPYALL, M_NOWAIT); -#if defined(IPSEC) || defined(IPSEC_SUPPORT) - /* - * Check AH/ESP integrity. - */ - if (IPSEC_ENABLED(ipv6)) { - if (n != NULL && - IPSEC_CHECK_POLICY(ipv6, n, last) != 0) { - m_freem(n); - /* Do not inject data into pcb. */ - n = NULL; - } - } -#endif /* IPSEC */ - if (n) { + if (n != NULL) { if (last->inp_flags & INP_CONTROLOPTS || last->inp_socket->so_options & SO_TIMESTAMP) ip6_savecontrol(last, n, &opts); @@ -218,89 +243,25 @@ RIP6STAT_INC(rip6s_fullsock); } else sorwakeup(last->inp_socket); - opts = NULL; } + /* XXX count dropped packet */ INP_RUNLOCK(last); last = NULL; } INP_RLOCK(inp); if (__predict_false(inp->inp_flags2 & INP_FREED)) - goto skip_2; + goto skip_1; if (jailed_without_vnet(inp->inp_cred)) { /* - * Allow raw socket in jail to receive multicast; - * assume process had PRIV_NETINET_RAW at attach, - * and fall through into normal filter path if so. + * XXX: If faddr was bound to multicast group, + * jailed raw socket will drop datagram. */ - if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && - prison_check_ip6(inp->inp_cred, - &ip6->ip6_dst) != 0) - goto skip_2; + if (prison_check_ip6(inp->inp_cred, &ip6->ip6_dst) != 0) + goto skip_1; } - if (inp->in6p_cksum != -1) { - RIP6STAT_INC(rip6s_isum); - if (m->m_pkthdr.len - (*offp + inp->in6p_cksum) < 2 || - in6_cksum(m, proto, *offp, - m->m_pkthdr.len - *offp)) { - RIP6STAT_INC(rip6s_badsum); - /* - * Drop the received message, don't send an - * ICMP6 message. Set proto to IPPROTO_NONE - * to achieve that. - */ - proto = IPPROTO_NONE; - goto skip_2; - } - } - /* - * If this raw socket has multicast state, and we - * have received a multicast, check if this socket - * should receive it, as multicast filtering is now - * the responsibility of the transport layer. - */ - if (inp->in6p_moptions && - IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { - /* - * If the incoming datagram is for MLD, allow it - * through unconditionally to the raw socket. - * - * Use the M_RTALERT_MLD flag to check for MLD - * traffic without having to inspect the mbuf chain - * more deeply, as all MLDv1/v2 host messages MUST - * contain the Router Alert option. - * - * In the case of MLDv1, we may not have explicitly - * joined the group, and may have set IFF_ALLMULTI - * on the interface. im6o_mc_filter() may discard - * control traffic we actually need to see. - * - * Userland multicast routing daemons should continue - * filter the control traffic appropriately. - */ - int blocked; - - blocked = MCAST_PASS; - if ((m->m_flags & M_RTALERT_MLD) == 0) { - struct sockaddr_in6 mcaddr; - - bzero(&mcaddr, sizeof(struct sockaddr_in6)); - mcaddr.sin6_len = sizeof(struct sockaddr_in6); - mcaddr.sin6_family = AF_INET6; - mcaddr.sin6_addr = ip6->ip6_dst; - - blocked = im6o_mc_filter(inp->in6p_moptions, - ifp, - (struct sockaddr *)&mcaddr, - (struct sockaddr *)&fromsa); - } - if (blocked != MCAST_PASS) { - IP6STAT_INC(ip6s_notmember); - goto skip_2; - } - } last = inp; continue; -skip_2: + skip_1: INP_RUNLOCK(inp); } #if defined(IPSEC) || defined(IPSEC_SUPPORT) @@ -665,13 +626,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 +651,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 +733,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 +774,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 +786,7 @@ inp->in6p_faddr = addr->sin6_addr; inp->in6p_laddr = in6a; + rip6_inshash(inp); soisconnected(so); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_ripcbinfo); Index: tests/sys/netinet6/Makefile =================================================================== --- tests/sys/netinet6/Makefile +++ tests/sys/netinet6/Makefile @@ -14,18 +14,21 @@ forward6 \ output6 \ lpm6 \ - fibs6 + fibs6 \ + rawsocket TEST_METADATA.output6+= required_programs="python" ${PACKAGE}FILES+= exthdr.py ${PACKAGE}FILES+= mld.py ${PACKAGE}FILES+= scapyi386.py ${PACKAGE}FILES+= redirect.py +${PACKAGE}FILES+= rawsocket.py ${PACKAGE}FILESMODE_exthdr.py= 0555 ${PACKAGE}FILESMODE_mld.py= 0555 ${PACKAGE}FILESMODE_scapyi386.py=0555 ${PACKAGE}FILESMODE_redirect.py=0555 +${PACKAGE}FILESMODE_rawsocket.py=0555 TESTS_SUBDIRS+= frag6 Index: tests/sys/netinet6/rawsocket.py =================================================================== --- tests/sys/netinet6/rawsocket.py +++ tests/sys/netinet6/rawsocket.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# - +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Neel Chauhan +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +import argparse +import socket + + +def parse_args(): + parser = argparse.ArgumentParser(description='Raw Socket ICMPv6 Ping') + parser.add_argument('--ip', type=str, required=True, + help='destination ip') + return parser.parse_args() + + +def ping(ip): + proto = 58 # ICMP + + msg = b'\x80\0\0\0\0\0\0\0' # Barebones ICMPv6 message + + snd = socket.socket(socket.AF_INET6, socket.SOCK_RAW, proto) + snd.sendto(msg, (ip, 0)) + + rcv = socket.socket(socket.AF_INET6, socket.SOCK_RAW, proto) + if rcv.recv(1024) is None: + raise TypeError + + +def main(): + args = parse_args() + ping(args.ip) + + +if __name__ == '__main__': + main() Index: tests/sys/netinet6/rawsocket.sh =================================================================== --- tests/sys/netinet6/rawsocket.sh +++ tests/sys/netinet6/rawsocket.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env atf-sh +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 Neel Chauhan +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +atf_test_case "raw_ping" "cleanup" +raw_ping_head() { + + atf_set descr 'Test valid ICMPv6 raw socket' + atf_set require.user root +} + +raw_ping_body() { + + script_name="rawsocket.py" + ip="::1" + + atf_check -s exit:0 $(atf_get_srcdir)/${script_name} --ip ${ip} +} + +raw_ping_cleanup() { + +} + +atf_init_test_cases() +{ + + atf_add_test_case "raw_ping" +} + +# end +