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) + (*laddr) + (*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.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. */ @@ -151,6 +189,64 @@ int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); int (*mrt6_ioctl)(u_long, caddr_t); +static void +rip6_hash_lookup(struct mbuf *m, struct inpcb *inp, struct inpcb *last, + struct ip6_hdr *ip6, struct sockaddr_in6 fromsa, struct mbuf *opts, + int *offp, int hash, int proto) +{ + 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); + } + + +} + /* * Setup generic address and protocol structures for raw_input routine, then * pass them along with mbuf chain. @@ -161,10 +257,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,7 +271,11 @@ 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); + rip6_hash_lookup(m, inp, last, ip6, fromsa, opts, offp, hash, proto); + + CK_LIST_FOREACH(inp, &V_ripcbinfo.ipi_hashbase[0], inp_hash) { /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV6) == 0) continue; @@ -665,13 +766,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 +791,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 +873,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 +914,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 +926,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,57 @@ +#!/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 + + s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, proto) + s.sendto(msg, (ip, 0)) + + +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 +