xref: /openbsd-src/sys/netinet6/raw_ip6.c (revision ea7e0400efea7878da35d9ad4bc9092ecb637818)
1*ea7e0400Sbluhm /*	$OpenBSD: raw_ip6.c,v 1.186 2024/11/08 10:24:13 bluhm Exp $	*/
20c5914f6Sitojun /*	$KAME: raw_ip6.c,v 1.69 2001/03/04 15:55:44 itojun Exp $	*/
3ac1b2aeeSitojun 
4ac1b2aeeSitojun /*
5ac1b2aeeSitojun  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6ac1b2aeeSitojun  * All rights reserved.
7ac1b2aeeSitojun  *
8ac1b2aeeSitojun  * Redistribution and use in source and binary forms, with or without
9ac1b2aeeSitojun  * modification, are permitted provided that the following conditions
10ac1b2aeeSitojun  * are met:
11ac1b2aeeSitojun  * 1. Redistributions of source code must retain the above copyright
12ac1b2aeeSitojun  *    notice, this list of conditions and the following disclaimer.
13ac1b2aeeSitojun  * 2. Redistributions in binary form must reproduce the above copyright
14ac1b2aeeSitojun  *    notice, this list of conditions and the following disclaimer in the
15ac1b2aeeSitojun  *    documentation and/or other materials provided with the distribution.
16ac1b2aeeSitojun  * 3. Neither the name of the project nor the names of its contributors
17ac1b2aeeSitojun  *    may be used to endorse or promote products derived from this software
18ac1b2aeeSitojun  *    without specific prior written permission.
19ac1b2aeeSitojun  *
20ac1b2aeeSitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21ac1b2aeeSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22ac1b2aeeSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23ac1b2aeeSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24ac1b2aeeSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25ac1b2aeeSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26ac1b2aeeSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27ac1b2aeeSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28ac1b2aeeSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29ac1b2aeeSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30ac1b2aeeSitojun  * SUCH DAMAGE.
31ac1b2aeeSitojun  */
32ac1b2aeeSitojun 
33ac1b2aeeSitojun /*
34ac1b2aeeSitojun  * Copyright (c) 1982, 1986, 1988, 1993
35ac1b2aeeSitojun  *	The Regents of the University of California.  All rights reserved.
36ac1b2aeeSitojun  *
37ac1b2aeeSitojun  * Redistribution and use in source and binary forms, with or without
38ac1b2aeeSitojun  * modification, are permitted provided that the following conditions
39ac1b2aeeSitojun  * are met:
40ac1b2aeeSitojun  * 1. Redistributions of source code must retain the above copyright
41ac1b2aeeSitojun  *    notice, this list of conditions and the following disclaimer.
42ac1b2aeeSitojun  * 2. Redistributions in binary form must reproduce the above copyright
43ac1b2aeeSitojun  *    notice, this list of conditions and the following disclaimer in the
44ac1b2aeeSitojun  *    documentation and/or other materials provided with the distribution.
4529295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
46ac1b2aeeSitojun  *    may be used to endorse or promote products derived from this software
47ac1b2aeeSitojun  *    without specific prior written permission.
48ac1b2aeeSitojun  *
49ac1b2aeeSitojun  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50ac1b2aeeSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51ac1b2aeeSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52ac1b2aeeSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53ac1b2aeeSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54ac1b2aeeSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55ac1b2aeeSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56ac1b2aeeSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57ac1b2aeeSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58ac1b2aeeSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59ac1b2aeeSitojun  * SUCH DAMAGE.
60ac1b2aeeSitojun  *
61ac1b2aeeSitojun  *	@(#)raw_ip.c	8.2 (Berkeley) 1/4/94
62ac1b2aeeSitojun  */
63ac1b2aeeSitojun 
64f49dc28fSbluhm #include "pf.h"
65f49dc28fSbluhm 
66ac1b2aeeSitojun #include <sys/param.h>
67ac1b2aeeSitojun #include <sys/malloc.h>
68ac1b2aeeSitojun #include <sys/mbuf.h>
69ac1b2aeeSitojun #include <sys/socket.h>
70ac1b2aeeSitojun #include <sys/protosw.h>
71ac1b2aeeSitojun #include <sys/socketvar.h>
72ac1b2aeeSitojun #include <sys/errno.h>
73ac1b2aeeSitojun #include <sys/systm.h>
74ab46c28dSderaadt #include <sys/sysctl.h>
75ac1b2aeeSitojun 
76ac1b2aeeSitojun #include <net/if.h>
770deb6685Smpi #include <net/if_var.h>
78ac1b2aeeSitojun #include <net/route.h>
79ac1b2aeeSitojun 
80ac1b2aeeSitojun #include <netinet/in.h>
81dc572864Sbluhm #include <netinet6/in6_var.h>
82ac1b2aeeSitojun #include <netinet/ip6.h>
83ac1b2aeeSitojun #include <netinet6/ip6_var.h>
84d9c6379eSclaudio #ifdef MROUTING
85ac1b2aeeSitojun #include <netinet6/ip6_mroute.h>
86d9c6379eSclaudio #endif
87ac1b2aeeSitojun #include <netinet/icmp6.h>
88ac1b2aeeSitojun #include <netinet/ip.h>
89ac1b2aeeSitojun #include <netinet/in_pcb.h>
90ac1b2aeeSitojun #include <netinet6/nd6.h>
91ac1b2aeeSitojun #include <netinet6/ip6protosw.h>
92f5bfbd04Sitojun #include <netinet6/raw_ip6.h>
93ac1b2aeeSitojun 
94377da2e0Sderaadt #if NPF > 0
95377da2e0Sderaadt #include <net/pfvar.h>
96377da2e0Sderaadt #endif
97377da2e0Sderaadt 
987034f964Sespie #include <sys/stdarg.h>
99ac1b2aeeSitojun 
100ac1b2aeeSitojun /*
101ac1b2aeeSitojun  * Raw interface to IP6 protocol.
102ac1b2aeeSitojun  */
103ac1b2aeeSitojun 
104ac1b2aeeSitojun struct	inpcbtable rawin6pcbtable;
105ac1b2aeeSitojun 
10607ba5f7cSjca struct cpumem *rip6counters;
107f5bfbd04Sitojun 
1087985bfd0Smvs const struct pr_usrreqs rip6_usrreqs = {
1097985bfd0Smvs 	.pru_attach	= rip6_attach,
1107985bfd0Smvs 	.pru_detach	= rip6_detach,
111121fc5cfSmvs 	.pru_bind	= rip6_bind,
112074c8388Smvs 	.pru_connect	= rip6_connect,
113e00787e6Smvs 	.pru_disconnect	= rip6_disconnect,
11486e05c94Smvs 	.pru_shutdown	= rip6_shutdown,
11590b3510cSmvs 	.pru_send	= rip6_send,
1163f68dcd3Smvs 	.pru_control	= in6_control,
1170dc53d81Smvs 	.pru_sockaddr	= in6_sockaddr,
118c3a3d609Smvs 	.pru_peeraddr	= in6_peeraddr,
1197985bfd0Smvs };
1207985bfd0Smvs 
121*ea7e0400Sbluhm void	rip6_sbappend(struct inpcb *, struct mbuf *, struct ip6_hdr *, int,
122*ea7e0400Sbluhm 	    struct sockaddr_in6 *);
123*ea7e0400Sbluhm 
124ac1b2aeeSitojun /*
125ac1b2aeeSitojun  * Initialize raw connection block queue.
126ac1b2aeeSitojun  */
127ac1b2aeeSitojun void
128c799dc6dSnaddy rip6_init(void)
129ac1b2aeeSitojun {
130ac1b2aeeSitojun 	in_pcbinit(&rawin6pcbtable, 1);
13107ba5f7cSjca 	rip6counters = counters_alloc(rip6s_ncounters);
132ac1b2aeeSitojun }
133ac1b2aeeSitojun 
134ac1b2aeeSitojun int
135459fa0feSbluhm rip6_input(struct mbuf **mp, int *offp, int proto, int af)
136ac1b2aeeSitojun {
137ac1b2aeeSitojun 	struct mbuf *m = *mp;
138ac1b2aeeSitojun 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
139*ea7e0400Sbluhm 	struct inpcb_iterator iter = { .inp_table = NULL };
140*ea7e0400Sbluhm 	struct inpcb *inp, *last;
141c7745dcbSbluhm 	struct in6_addr *key;
142ac1b2aeeSitojun 	struct sockaddr_in6 rip6src;
143bf088e2bSbluhm 	uint8_t type;
144ac1b2aeeSitojun 
14566467af9Sbluhm 	KASSERT(af == AF_INET6);
14666467af9Sbluhm 
147bf088e2bSbluhm 	if (proto == IPPROTO_ICMPV6) {
148bf088e2bSbluhm 		struct icmp6_hdr *icmp6;
149bf088e2bSbluhm 
150bf088e2bSbluhm 		IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, *offp,
151bf088e2bSbluhm 		    sizeof(*icmp6));
152bf088e2bSbluhm 		if (icmp6 == NULL)
153bf088e2bSbluhm 			return IPPROTO_DONE;
154bf088e2bSbluhm 		type = icmp6->icmp6_type;
155bf088e2bSbluhm 	} else
15607ba5f7cSjca 		rip6stat_inc(rip6s_ipackets);
157f5bfbd04Sitojun 
158a3425987Sbluhm 	memset(&rip6src, 0, sizeof(rip6src));
159ac1b2aeeSitojun 	rip6src.sin6_family = AF_INET6;
160a3425987Sbluhm 	rip6src.sin6_len = sizeof(rip6src);
161ac1b2aeeSitojun 	/* KAME hack: recover scopeid */
1626c8dd0e9Sclaudio 	in6_recoverscope(&rip6src, &ip6->ip6_src);
163ac1b2aeeSitojun 
164c7745dcbSbluhm 	key = &ip6->ip6_dst;
165c7745dcbSbluhm #if NPF > 0
166c7745dcbSbluhm 	if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
167c7745dcbSbluhm 		struct pf_divert *divert;
168c7745dcbSbluhm 
169c7745dcbSbluhm 		divert = pf_find_divert(m);
170c7745dcbSbluhm 		KASSERT(divert != NULL);
171c7745dcbSbluhm 		switch (divert->type) {
172c7745dcbSbluhm 		case PF_DIVERT_TO:
173c7745dcbSbluhm 			key = &divert->addr.v6;
174c7745dcbSbluhm 			break;
175c7745dcbSbluhm 		case PF_DIVERT_REPLY:
176c7745dcbSbluhm 			break;
177c7745dcbSbluhm 		default:
178c7745dcbSbluhm 			panic("%s: unknown divert type %d, mbuf %p, divert %p",
179c7745dcbSbluhm 			    __func__, divert->type, m, divert);
180c7745dcbSbluhm 		}
181c7745dcbSbluhm 	}
182c7745dcbSbluhm #endif
183dc28d33fSbluhm 	mtx_enter(&rawin6pcbtable.inpt_mtx);
184*ea7e0400Sbluhm 	last = inp = NULL;
185*ea7e0400Sbluhm 	while ((inp = in_pcb_iterator(&rawin6pcbtable, inp, &iter)) != NULL) {
186e3a7b014Sbluhm 		KASSERT(ISSET(inp->inp_flags, INP_IPV6));
187e3a7b014Sbluhm 
188a3425987Sbluhm 		/*
189a3425987Sbluhm 		 * Packet must not be inserted after disconnected wakeup
190a3425987Sbluhm 		 * call.  To avoid race, check again when holding receive
191a3425987Sbluhm 		 * buffer mutex.
192a3425987Sbluhm 		 */
193a3425987Sbluhm 		if (ISSET(READ_ONCE(inp->inp_socket->so_rcv.sb_state),
194a3425987Sbluhm 		    SS_CANTRCVMORE))
1958a4cdb97Sclaudio 			continue;
196921ffa12Sbluhm 		if (rtable_l2(inp->inp_rtableid) !=
19730c05804Skn 		    rtable_l2(m->m_pkthdr.ph_rtableid))
19830c05804Skn 			continue;
19930c05804Skn 
200921ffa12Sbluhm 		if ((inp->inp_ipv6.ip6_nxt || proto == IPPROTO_ICMPV6) &&
201921ffa12Sbluhm 		    inp->inp_ipv6.ip6_nxt != proto)
202ac1b2aeeSitojun 			continue;
203921ffa12Sbluhm 		if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) &&
204921ffa12Sbluhm 		    !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key))
205ac1b2aeeSitojun 			continue;
206921ffa12Sbluhm 		if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) &&
207921ffa12Sbluhm 		    !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ip6->ip6_src))
208ac1b2aeeSitojun 			continue;
209921ffa12Sbluhm 		if (proto == IPPROTO_ICMPV6 && inp->inp_icmp6filt) {
210921ffa12Sbluhm 			if (ICMP6_FILTER_WILLBLOCK(type, inp->inp_icmp6filt))
2112ed633ffSbluhm 				continue;
2122ed633ffSbluhm 		}
213921ffa12Sbluhm 		if (proto != IPPROTO_ICMPV6 && inp->inp_cksum6 != -1) {
21407ba5f7cSjca 			rip6stat_inc(rip6s_isum);
215c526b7b2Sbluhm 			/*
216c526b7b2Sbluhm 			 * Although in6_cksum() does not need the position of
217c526b7b2Sbluhm 			 * the checksum field for verification, enforce that it
218c526b7b2Sbluhm 			 * is located within the packet.  Userland has given
219c526b7b2Sbluhm 			 * a checksum offset, a packet too short for that is
220c526b7b2Sbluhm 			 * invalid.  Avoid overflow with user supplied offset.
221c526b7b2Sbluhm 			 */
222c526b7b2Sbluhm 			if (m->m_pkthdr.len < *offp + 2 ||
223921ffa12Sbluhm 			    m->m_pkthdr.len - *offp - 2 < inp->inp_cksum6 ||
224c526b7b2Sbluhm 			    in6_cksum(m, proto, *offp,
225f5bfbd04Sitojun 			    m->m_pkthdr.len - *offp)) {
22607ba5f7cSjca 				rip6stat_inc(rip6s_badsum);
227ac1b2aeeSitojun 				continue;
228ac1b2aeeSitojun 			}
229f5bfbd04Sitojun 		}
2302be5be2cSbluhm 
231*ea7e0400Sbluhm 		if (last != NULL) {
232*ea7e0400Sbluhm 			struct mbuf *n;
233*ea7e0400Sbluhm 
234*ea7e0400Sbluhm 			mtx_leave(&rawin6pcbtable.inpt_mtx);
235*ea7e0400Sbluhm 
236*ea7e0400Sbluhm 			n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
237*ea7e0400Sbluhm 			if (n != NULL)
238*ea7e0400Sbluhm 				rip6_sbappend(last, n, ip6, *offp, &rip6src);
239*ea7e0400Sbluhm 			in_pcbunref(last);
240*ea7e0400Sbluhm 
241*ea7e0400Sbluhm 			mtx_enter(&rawin6pcbtable.inpt_mtx);
242*ea7e0400Sbluhm 		}
243*ea7e0400Sbluhm 		last = in_pcbref(inp);
244ac1b2aeeSitojun 	}
245dc28d33fSbluhm 	mtx_leave(&rawin6pcbtable.inpt_mtx);
246dc28d33fSbluhm 
247*ea7e0400Sbluhm 	if (last == NULL) {
24831e14cacSjca 		struct counters_ref ref;
24931e14cacSjca 		uint64_t *counters;
25031e14cacSjca 
2512ed633ffSbluhm 		if (proto != IPPROTO_ICMPV6) {
25207ba5f7cSjca 			rip6stat_inc(rip6s_nosock);
253f5bfbd04Sitojun 			if (m->m_flags & M_MCAST)
25407ba5f7cSjca 				rip6stat_inc(rip6s_nosockmcast);
2552ed633ffSbluhm 		}
2562ed633ffSbluhm 		if (proto == IPPROTO_NONE || proto == IPPROTO_ICMPV6) {
257ac1b2aeeSitojun 			m_freem(m);
2582ed633ffSbluhm 		} else {
2598398ca50Sbluhm 			int prvnxt = ip6_get_prevhdr(m, *offp);
2608398ca50Sbluhm 
261ac1b2aeeSitojun 			icmp6_error(m, ICMP6_PARAM_PROB,
2628398ca50Sbluhm 			    ICMP6_PARAMPROB_NEXTHEADER, prvnxt);
263ac1b2aeeSitojun 		}
26431e14cacSjca 		counters = counters_enter(&ref, ip6counters);
26531e14cacSjca 		counters[ip6s_delivered]--;
26631e14cacSjca 		counters_leave(&ref, ip6counters);
267da3acf12Sbluhm 
268da3acf12Sbluhm 		return IPPROTO_DONE;
269ac1b2aeeSitojun 	}
2702be5be2cSbluhm 
271*ea7e0400Sbluhm 	rip6_sbappend(last, m, ip6, *offp, &rip6src);
272*ea7e0400Sbluhm 	in_pcbunref(last);
2732be5be2cSbluhm 
274*ea7e0400Sbluhm 	return IPPROTO_DONE;
275*ea7e0400Sbluhm }
276*ea7e0400Sbluhm 
277*ea7e0400Sbluhm void
278*ea7e0400Sbluhm rip6_sbappend(struct inpcb *inp, struct mbuf *m, struct ip6_hdr *ip6, int hlen,
279*ea7e0400Sbluhm     struct sockaddr_in6 *rip6src)
280*ea7e0400Sbluhm {
281c75e434fSmvs 	struct socket *so = inp->inp_socket;
282*ea7e0400Sbluhm 	struct mbuf *opts = NULL;
283a3425987Sbluhm 	int ret = 0;
284d0e7fa22Smvs 
285921ffa12Sbluhm 	if (inp->inp_flags & IN6P_CONTROLOPTS)
286*ea7e0400Sbluhm 		ip6_savecontrol(inp, m, &opts);
2872be5be2cSbluhm 	/* strip intermediate headers */
288*ea7e0400Sbluhm 	m_adj(m, hlen);
289d0e7fa22Smvs 
290c75e434fSmvs 	mtx_enter(&so->so_rcv.sb_mtx);
291*ea7e0400Sbluhm 	if (!ISSET(inp->inp_socket->so_rcv.sb_state, SS_CANTRCVMORE))
292*ea7e0400Sbluhm 		ret = sbappendaddr(so, &so->so_rcv, sin6tosa(rip6src), m, opts);
293c75e434fSmvs 	mtx_leave(&so->so_rcv.sb_mtx);
294d0e7fa22Smvs 
295d0e7fa22Smvs 	if (ret == 0) {
296*ea7e0400Sbluhm 		m_freem(m);
2972be5be2cSbluhm 		m_freem(opts);
2982be5be2cSbluhm 		rip6stat_inc(rip6s_fullsock);
2992be5be2cSbluhm 	} else
300c75e434fSmvs 		sorwakeup(so);
3012be5be2cSbluhm }
302ac1b2aeeSitojun 
303ac1b2aeeSitojun void
3044da85d9aSbluhm rip6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d)
305ac1b2aeeSitojun {
306ac1b2aeeSitojun 	struct ip6_hdr *ip6;
3077ee01f6fSitojun 	struct ip6ctlparam *ip6cp = NULL;
30855851b2eSbluhm 	struct sockaddr_in6 *sa6 = satosin6(sa);
3097ee01f6fSitojun 	const struct sockaddr_in6 *sa6_src = NULL;
3107ee01f6fSitojun 	void *cmdarg;
3118e5060ecSderaadt 	void (*notify)(struct inpcb *, int) = in_rtchange;
3127ee01f6fSitojun 	int nxt;
313ac1b2aeeSitojun 
314ac1b2aeeSitojun 	if (sa->sa_family != AF_INET6 ||
315ac1b2aeeSitojun 	    sa->sa_len != sizeof(struct sockaddr_in6))
316ac1b2aeeSitojun 		return;
317ac1b2aeeSitojun 
318ac1b2aeeSitojun 	if ((unsigned)cmd >= PRC_NCMDS)
319ac1b2aeeSitojun 		return;
320ac1b2aeeSitojun 	if (PRC_IS_REDIRECT(cmd))
3218e5060ecSderaadt 		notify = in_rtchange, d = NULL;
322ac1b2aeeSitojun 	else if (cmd == PRC_HOSTDEAD)
323ac1b2aeeSitojun 		d = NULL;
3247ee01f6fSitojun 	else if (cmd == PRC_MSGSIZE)
3257ee01f6fSitojun 		; /* special code is present, see below */
326ac1b2aeeSitojun 	else if (inet6ctlerrmap[cmd] == 0)
327ac1b2aeeSitojun 		return;
328ac1b2aeeSitojun 
329ac1b2aeeSitojun 	/* if the parameter is from icmp6, decode it. */
330ac1b2aeeSitojun 	if (d != NULL) {
3317ee01f6fSitojun 		ip6cp = (struct ip6ctlparam *)d;
332ac1b2aeeSitojun 		ip6 = ip6cp->ip6c_ip6;
3337ee01f6fSitojun 		cmdarg = ip6cp->ip6c_cmdarg;
3347ee01f6fSitojun 		sa6_src = ip6cp->ip6c_src;
3357ee01f6fSitojun 		nxt = ip6cp->ip6c_nxt;
336ac1b2aeeSitojun 	} else {
337ac1b2aeeSitojun 		ip6 = NULL;
3387ee01f6fSitojun 		cmdarg = NULL;
3397ee01f6fSitojun 		sa6_src = &sa6_any;
3407ee01f6fSitojun 		nxt = -1;
341ac1b2aeeSitojun 	}
342ac1b2aeeSitojun 
3437ee01f6fSitojun 	if (ip6 && cmd == PRC_MSGSIZE) {
3447ee01f6fSitojun 		int valid = 0;
345921ffa12Sbluhm 		struct inpcb *inp;
346ac1b2aeeSitojun 
347ac1b2aeeSitojun 		/*
3487ee01f6fSitojun 		 * Check to see if we have a valid raw IPv6 socket
3497ee01f6fSitojun 		 * corresponding to the address in the ICMPv6 message
3507ee01f6fSitojun 		 * payload, and the protocol (ip6_nxt) meets the socket.
3517ee01f6fSitojun 		 * XXX chase extension headers, or pass final nxt value
3527ee01f6fSitojun 		 * from icmp6_notify_error()
353ac1b2aeeSitojun 		 */
354921ffa12Sbluhm 		inp = in6_pcblookup(&rawin6pcbtable, &sa6->sin6_addr, 0,
355dc01e491Sphessler 		    &sa6_src->sin6_addr, 0, rdomain);
3567ee01f6fSitojun 
357921ffa12Sbluhm 		if (inp && inp->inp_ipv6.ip6_nxt &&
358921ffa12Sbluhm 		    inp->inp_ipv6.ip6_nxt == nxt)
3599e8a1cdfSbluhm 			valid = 1;
3607ee01f6fSitojun 
3617ee01f6fSitojun 		/*
3627ee01f6fSitojun 		 * Depending on the value of "valid" and routing table
3637ee01f6fSitojun 		 * size (mtudisc_{hi,lo}wat), we will:
364f5bfbd04Sitojun 		 * - recalculate the new MTU and create the
3657ee01f6fSitojun 		 *   corresponding routing entry, or
3667ee01f6fSitojun 		 * - ignore the MTU change notification.
3677ee01f6fSitojun 		 */
3687ee01f6fSitojun 		icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
369921ffa12Sbluhm 		in_pcbunref(inp);
3707ee01f6fSitojun 
3717ee01f6fSitojun 		/*
3727ee01f6fSitojun 		 * regardless of if we called icmp6_mtudisc_update(),
3737ee01f6fSitojun 		 * we need to call in6_pcbnotify(), to notify path
3747ee01f6fSitojun 		 * MTU change to the userland (2292bis-02), because
3757ee01f6fSitojun 		 * some unconnected sockets may share the same
3767ee01f6fSitojun 		 * destination and want to know the path MTU.
3777ee01f6fSitojun 		 */
3787ee01f6fSitojun 	}
3797ee01f6fSitojun 
3801941b8b5Sbluhm 	in6_pcbnotify(&rawin6pcbtable, sa6, 0,
381dc01e491Sphessler 	    sa6_src, 0, rdomain, cmd, cmdarg, notify);
382ac1b2aeeSitojun }
383ac1b2aeeSitojun 
384ac1b2aeeSitojun /*
385ac1b2aeeSitojun  * Generate IPv6 header and pass packet to ip6_output.
386ac1b2aeeSitojun  * Tack on options user may have setup with control call.
387ac1b2aeeSitojun  */
388ac1b2aeeSitojun int
3896539a3bcSbluhm rip6_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr,
3906539a3bcSbluhm     struct mbuf *control)
391ac1b2aeeSitojun {
392ac1b2aeeSitojun 	struct in6_addr *dst;
393ac1b2aeeSitojun 	struct ip6_hdr *ip6;
394921ffa12Sbluhm 	struct inpcb *inp;
395ac1b2aeeSitojun 	u_int	plen = m->m_pkthdr.len;
396ac1b2aeeSitojun 	int error = 0;
397952c6363Sbluhm 	struct ip6_pktopts opt, *optp = NULL;
39847c74071Schl 	int type;		/* for ICMPv6 output statistics only */
399ac1b2aeeSitojun 	int priv = 0;
400ac1b2aeeSitojun 	int flags;
401ac1b2aeeSitojun 
402921ffa12Sbluhm 	inp = sotoinpcb(so);
403ac1b2aeeSitojun 
404ac1b2aeeSitojun 	priv = 0;
405ac1b2aeeSitojun 	if ((so->so_state & SS_PRIV) != 0)
406ac1b2aeeSitojun 		priv = 1;
407ac1b2aeeSitojun 	if (control) {
4086b532452Sitojun 		if ((error = ip6_setpktopts(control, &opt,
409921ffa12Sbluhm 		    inp->inp_outputopts6,
4106b532452Sitojun 		    priv, so->so_proto->pr_protocol)) != 0)
411ac1b2aeeSitojun 			goto bad;
412ac1b2aeeSitojun 		optp = &opt;
413ac1b2aeeSitojun 	} else
414921ffa12Sbluhm 		optp = inp->inp_outputopts6;
415ac1b2aeeSitojun 
41663ec1ab0Sbluhm 	if (dstaddr->sa_family != AF_INET6) {
41763ec1ab0Sbluhm 		error = EAFNOSUPPORT;
41863ec1ab0Sbluhm 		goto bad;
41963ec1ab0Sbluhm 	}
42063ec1ab0Sbluhm 	dst = &satosin6(dstaddr)->sin6_addr;
42163ec1ab0Sbluhm 	if (IN6_IS_ADDR_V4MAPPED(dst)) {
42263ec1ab0Sbluhm 		error = EADDRNOTAVAIL;
42363ec1ab0Sbluhm 		goto bad;
42463ec1ab0Sbluhm 	}
42563ec1ab0Sbluhm 
426ac1b2aeeSitojun 	/*
427ac1b2aeeSitojun 	 * For an ICMPv6 packet, we should know its type and code
428ac1b2aeeSitojun 	 * to update statistics.
429ac1b2aeeSitojun 	 */
430ac1b2aeeSitojun 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
431ac1b2aeeSitojun 		struct icmp6_hdr *icmp6;
432ac1b2aeeSitojun 		if (m->m_len < sizeof(struct icmp6_hdr) &&
433ac1b2aeeSitojun 		    (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
434ac1b2aeeSitojun 			error = ENOBUFS;
435ac1b2aeeSitojun 			goto bad;
436ac1b2aeeSitojun 		}
437ac1b2aeeSitojun 		icmp6 = mtod(m, struct icmp6_hdr *);
438ac1b2aeeSitojun 		type = icmp6->icmp6_type;
439ac1b2aeeSitojun 	}
440ac1b2aeeSitojun 
441331e8cd7Sitojun 	M_PREPEND(m, sizeof(*ip6), M_DONTWAIT);
442331e8cd7Sitojun 	if (!m) {
443331e8cd7Sitojun 		error = ENOBUFS;
444331e8cd7Sitojun 		goto bad;
445331e8cd7Sitojun 	}
446ac1b2aeeSitojun 	ip6 = mtod(m, struct ip6_hdr *);
447ac1b2aeeSitojun 
448ac1b2aeeSitojun 	/*
449ac1b2aeeSitojun 	 * Next header might not be ICMP6 but use its pseudo header anyway.
450ac1b2aeeSitojun 	 */
451ac1b2aeeSitojun 	ip6->ip6_dst = *dst;
452ac1b2aeeSitojun 
453ac1b2aeeSitojun 	/* KAME hack: embed scopeid */
454952c6363Sbluhm 	if (in6_embedscope(&ip6->ip6_dst, satosin6(dstaddr),
455921ffa12Sbluhm 	    optp, inp->inp_moptions6) != 0) {
456ac1b2aeeSitojun 		error = EINVAL;
457ac1b2aeeSitojun 		goto bad;
458ac1b2aeeSitojun 	}
459ac1b2aeeSitojun 
460ac1b2aeeSitojun 	/*
461ac1b2aeeSitojun 	 * Source address selection.
462ac1b2aeeSitojun 	 */
463ac1b2aeeSitojun 	{
464cff23a6bSbluhm 		const struct in6_addr *in6a;
465ac1b2aeeSitojun 
466921ffa12Sbluhm 		error = in6_pcbselsrc(&in6a, satosin6(dstaddr), inp, optp);
4670039ae51Sjca 		if (error)
468ac1b2aeeSitojun 			goto bad;
4690039ae51Sjca 
470ac1b2aeeSitojun 		ip6->ip6_src = *in6a;
471ac1b2aeeSitojun 	}
472ac1b2aeeSitojun 
473921ffa12Sbluhm 	ip6->ip6_flow = inp->inp_flowinfo & IPV6_FLOWINFO_MASK;
474ac1b2aeeSitojun 	ip6->ip6_vfc  &= ~IPV6_VERSION_MASK;
475ac1b2aeeSitojun 	ip6->ip6_vfc  |= IPV6_VERSION;
476ac1b2aeeSitojun #if 0				/* ip6_plen will be filled in ip6_output. */
477ac1b2aeeSitojun 	ip6->ip6_plen  = htons((u_short)plen);
478ac1b2aeeSitojun #endif
479921ffa12Sbluhm 	ip6->ip6_nxt   = inp->inp_ipv6.ip6_nxt;
480921ffa12Sbluhm 	ip6->ip6_hlim = in6_selecthlim(inp);
481ac1b2aeeSitojun 
482ac1b2aeeSitojun 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
483921ffa12Sbluhm 	    inp->inp_cksum6 != -1) {
484af7b461eSitojun 		struct mbuf *n;
485ac1b2aeeSitojun 		int off;
486af7b461eSitojun 		u_int16_t *sump;
487af7b461eSitojun 		int sumoff;
488ac1b2aeeSitojun 
489ac1b2aeeSitojun 		/* compute checksum */
490ac1b2aeeSitojun 		if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
491ac1b2aeeSitojun 			off = offsetof(struct icmp6_hdr, icmp6_cksum);
492ac1b2aeeSitojun 		else
493921ffa12Sbluhm 			off = inp->inp_cksum6;
494c526b7b2Sbluhm 		if (plen < 2 || plen - 2 < off) {
495ac1b2aeeSitojun 			error = EINVAL;
496ac1b2aeeSitojun 			goto bad;
497ac1b2aeeSitojun 		}
498ac1b2aeeSitojun 		off += sizeof(struct ip6_hdr);
499ac1b2aeeSitojun 
500af7b461eSitojun 		n = m_pulldown(m, off, sizeof(*sump), &sumoff);
501af7b461eSitojun 		if (n == NULL) {
502af7b461eSitojun 			m = NULL;
503af7b461eSitojun 			error = ENOBUFS;
504af7b461eSitojun 			goto bad;
505af7b461eSitojun 		}
506af7b461eSitojun 		sump = (u_int16_t *)(mtod(n, caddr_t) + sumoff);
507af7b461eSitojun 		*sump = 0;
508af7b461eSitojun 		*sump = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
509ac1b2aeeSitojun 	}
510ac1b2aeeSitojun 
511ac1b2aeeSitojun 	flags = 0;
512921ffa12Sbluhm 	if (inp->inp_flags & IN6P_MINMTU)
513ac1b2aeeSitojun 		flags |= IPV6_MINMTU;
514ac1b2aeeSitojun 
5155ee8afe3Smpi 	/* force routing table */
516921ffa12Sbluhm 	m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
517ba79ddd5Ssperreault 
51854c5291fSbluhm #if NPF > 0
519921ffa12Sbluhm 	if (inp->inp_socket->so_state & SS_ISCONNECTED &&
520fbc14a14Sbluhm 	    so->so_proto->pr_protocol != IPPROTO_ICMPV6)
521921ffa12Sbluhm 		pf_mbuf_link_inpcb(m, inp);
52254c5291fSbluhm #endif
52354c5291fSbluhm 
52494c0e2bdSbluhm 	error = ip6_output(m, optp, &inp->inp_route, flags,
525ace0f189Sbluhm 	    inp->inp_moptions6, &inp->inp_seclevel);
526ac1b2aeeSitojun 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
52707ba5f7cSjca 		icmp6stat_inc(icp6s_outhist + type);
528f5bfbd04Sitojun 	} else
52907ba5f7cSjca 		rip6stat_inc(rip6s_opackets);
530ac1b2aeeSitojun 
531ac1b2aeeSitojun 	goto freectl;
532ac1b2aeeSitojun 
533ac1b2aeeSitojun  bad:
534ac1b2aeeSitojun 	m_freem(m);
535ac1b2aeeSitojun 
536ac1b2aeeSitojun  freectl:
5376b532452Sitojun 	if (control) {
5386b532452Sitojun 		ip6_clearpktopts(&opt, -1);
539ac1b2aeeSitojun 		m_freem(control);
5406b532452Sitojun 	}
541ac1b2aeeSitojun 	return (error);
542ac1b2aeeSitojun }
543ac1b2aeeSitojun 
544ac1b2aeeSitojun /*
545ac1b2aeeSitojun  * Raw IPv6 socket option processing.
546ac1b2aeeSitojun  */
547ac1b2aeeSitojun int
548ee37ea65Smcbride rip6_ctloutput(int op, struct socket *so, int level, int optname,
549490e0738Sdhill     struct mbuf *m)
550ac1b2aeeSitojun {
55164b11953Sderaadt #ifdef MROUTING
5528b4596e6Sbluhm 	int error;
55364b11953Sderaadt #endif
554ac1b2aeeSitojun 
555ac1b2aeeSitojun 	switch (level) {
556ac1b2aeeSitojun 	case IPPROTO_IPV6:
557ac1b2aeeSitojun 		switch (optname) {
558d9c6379eSclaudio #ifdef MROUTING
559ac1b2aeeSitojun 		case MRT6_INIT:
560ac1b2aeeSitojun 		case MRT6_DONE:
561ac1b2aeeSitojun 		case MRT6_ADD_MIF:
562ac1b2aeeSitojun 		case MRT6_DEL_MIF:
563ac1b2aeeSitojun 		case MRT6_ADD_MFC:
564ac1b2aeeSitojun 		case MRT6_DEL_MFC:
565ac1b2aeeSitojun 			if (op == PRCO_SETOPT) {
566490e0738Sdhill 				error = ip6_mrouter_set(optname, so, m);
567f5bfbd04Sitojun 			} else if (op == PRCO_GETOPT)
568490e0738Sdhill 				error = ip6_mrouter_get(optname, so, m);
569f5bfbd04Sitojun 			else
570ac1b2aeeSitojun 				error = EINVAL;
571ac1b2aeeSitojun 			return (error);
572d9c6379eSclaudio #endif
573f5bfbd04Sitojun 		case IPV6_CHECKSUM:
574490e0738Sdhill 			return (ip6_raw_ctloutput(op, so, level, optname, m));
575f5bfbd04Sitojun 		default:
576490e0738Sdhill 			return (ip6_ctloutput(op, so, level, optname, m));
577ac1b2aeeSitojun 		}
578ac1b2aeeSitojun 
579ac1b2aeeSitojun 	case IPPROTO_ICMPV6:
580ac1b2aeeSitojun 		/*
581ac1b2aeeSitojun 		 * XXX: is it better to call icmp6_ctloutput() directly
582ac1b2aeeSitojun 		 * from protosw?
583ac1b2aeeSitojun 		 */
584490e0738Sdhill 		return (icmp6_ctloutput(op, so, level, optname, m));
585ac1b2aeeSitojun 
586ac1b2aeeSitojun 	default:
587f5bfbd04Sitojun 		return EINVAL;
588ac1b2aeeSitojun 	}
589ac1b2aeeSitojun }
590ac1b2aeeSitojun 
591ac1b2aeeSitojun extern	u_long rip6_sendspace;
592ac1b2aeeSitojun extern	u_long rip6_recvspace;
593ac1b2aeeSitojun 
594ac1b2aeeSitojun int
59562440853Sbluhm rip6_attach(struct socket *so, int proto, int wait)
5963d7f610cSclaudio {
597921ffa12Sbluhm 	struct inpcb *inp;
5983d7f610cSclaudio 	int error;
5993d7f610cSclaudio 
6003d7f610cSclaudio 	if (so->so_pcb)
601ab0ea4a9Snayden 		panic("%s", __func__);
6023d7f610cSclaudio 	if ((so->so_state & SS_PRIV) == 0)
6033d7f610cSclaudio 		return (EACCES);
6043d7f610cSclaudio 	if (proto < 0 || proto >= IPPROTO_MAX)
6053d7f610cSclaudio 		return EPROTONOSUPPORT;
6063d7f610cSclaudio 
60794334c66Smpi 	if ((error = soreserve(so, rip6_sendspace, rip6_recvspace)))
60894334c66Smpi 		return error;
60994334c66Smpi 	NET_ASSERT_LOCKED();
61062440853Sbluhm 	if ((error = in_pcballoc(so, &rawin6pcbtable, wait)))
6113d7f610cSclaudio 		return error;
6123d7f610cSclaudio 
613921ffa12Sbluhm 	inp = sotoinpcb(so);
614921ffa12Sbluhm 	inp->inp_ipv6.ip6_nxt = proto;
615921ffa12Sbluhm 	inp->inp_cksum6 = -1;
6163d7f610cSclaudio 
617921ffa12Sbluhm 	inp->inp_icmp6filt = malloc(sizeof(struct icmp6_filter), M_PCB,
61862440853Sbluhm 	    wait == M_WAIT ? M_WAITOK : M_NOWAIT);
619921ffa12Sbluhm 	if (inp->inp_icmp6filt == NULL) {
620921ffa12Sbluhm 		in_pcbdetach(inp);
6213d7f610cSclaudio 		return ENOMEM;
6223d7f610cSclaudio 	}
623921ffa12Sbluhm 	ICMP6_FILTER_SETPASSALL(inp->inp_icmp6filt);
6243d7f610cSclaudio 	return 0;
6253d7f610cSclaudio }
6263d7f610cSclaudio 
6273d7f610cSclaudio int
6282c5f89aaSflorian rip6_detach(struct socket *so)
6292c5f89aaSflorian {
630921ffa12Sbluhm 	struct inpcb *inp = sotoinpcb(so);
6312c5f89aaSflorian 
6322c5f89aaSflorian 	soassertlocked(so);
6332c5f89aaSflorian 
634921ffa12Sbluhm 	if (inp == NULL)
635ab0ea4a9Snayden 		panic("%s", __func__);
6362c5f89aaSflorian #ifdef MROUTING
637921ffa12Sbluhm 	if (so == ip6_mrouter[inp->inp_rtableid])
6382c5f89aaSflorian 		ip6_mrouter_done(so);
6392c5f89aaSflorian #endif
640921ffa12Sbluhm 	free(inp->inp_icmp6filt, M_PCB, sizeof(struct icmp6_filter));
641921ffa12Sbluhm 	inp->inp_icmp6filt = NULL;
6422c5f89aaSflorian 
643921ffa12Sbluhm 	in_pcbdetach(inp);
6442c5f89aaSflorian 
6452c5f89aaSflorian 	return (0);
6462c5f89aaSflorian }
6472c5f89aaSflorian 
648104c0a83Smvs int
649121fc5cfSmvs rip6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
650121fc5cfSmvs {
651921ffa12Sbluhm 	struct inpcb *inp = sotoinpcb(so);
652121fc5cfSmvs 	struct sockaddr_in6 *addr;
653121fc5cfSmvs 	int error;
654121fc5cfSmvs 
655121fc5cfSmvs 	soassertlocked(so);
656121fc5cfSmvs 
657121fc5cfSmvs 	if ((error = in6_nam2sin6(nam, &addr)))
658121fc5cfSmvs 		return (error);
659121fc5cfSmvs 
660121fc5cfSmvs 	/*
661121fc5cfSmvs 	 * Make sure to not enter in_pcblookup_local(), local ports
662121fc5cfSmvs 	 * are non-sensical for raw sockets.
663121fc5cfSmvs 	 */
664121fc5cfSmvs 	addr->sin6_port = 0;
665121fc5cfSmvs 
666921ffa12Sbluhm 	if ((error = in6_pcbaddrisavail(inp, addr, 0, p)))
667121fc5cfSmvs 		return (error);
668121fc5cfSmvs 
669990f2b24Sbluhm 	mtx_enter(&rawin6pcbtable.inpt_mtx);
670921ffa12Sbluhm 	inp->inp_laddr6 = addr->sin6_addr;
671990f2b24Sbluhm 	mtx_leave(&rawin6pcbtable.inpt_mtx);
672990f2b24Sbluhm 
673121fc5cfSmvs 	return (0);
674121fc5cfSmvs }
675121fc5cfSmvs 
676121fc5cfSmvs int
677074c8388Smvs rip6_connect(struct socket *so, struct mbuf *nam)
678074c8388Smvs {
679921ffa12Sbluhm 	struct inpcb *inp = sotoinpcb(so);
680074c8388Smvs 	struct sockaddr_in6 *addr;
681cff23a6bSbluhm 	const struct in6_addr *in6a;
682074c8388Smvs 	int error;
683074c8388Smvs 
684074c8388Smvs 	soassertlocked(so);
685074c8388Smvs 
686074c8388Smvs 	if ((error = in6_nam2sin6(nam, &addr)))
687074c8388Smvs 		return (error);
688074c8388Smvs 
689074c8388Smvs 	/* Source address selection. XXX: need pcblookup? */
690921ffa12Sbluhm 	error = in6_pcbselsrc(&in6a, addr, inp, inp->inp_outputopts6);
691074c8388Smvs 	if (error)
692074c8388Smvs 		return (error);
693074c8388Smvs 
694990f2b24Sbluhm 	mtx_enter(&rawin6pcbtable.inpt_mtx);
695921ffa12Sbluhm 	inp->inp_laddr6 = *in6a;
696921ffa12Sbluhm 	inp->inp_faddr6 = addr->sin6_addr;
697990f2b24Sbluhm 	mtx_leave(&rawin6pcbtable.inpt_mtx);
698074c8388Smvs 	soisconnected(so);
699990f2b24Sbluhm 
700074c8388Smvs 	return (0);
701074c8388Smvs }
702074c8388Smvs 
703074c8388Smvs int
704e00787e6Smvs rip6_disconnect(struct socket *so)
705e00787e6Smvs {
706921ffa12Sbluhm 	struct inpcb *inp = sotoinpcb(so);
707e00787e6Smvs 
708e00787e6Smvs 	soassertlocked(so);
709e00787e6Smvs 
710e00787e6Smvs 	if ((so->so_state & SS_ISCONNECTED) == 0)
711e00787e6Smvs 		return (ENOTCONN);
712e00787e6Smvs 
713a3425987Sbluhm 	soisdisconnected(so);
714990f2b24Sbluhm 	mtx_enter(&rawin6pcbtable.inpt_mtx);
715990f2b24Sbluhm 	inp->inp_faddr6 = in6addr_any;
716990f2b24Sbluhm 	mtx_leave(&rawin6pcbtable.inpt_mtx);
717990f2b24Sbluhm 
718e00787e6Smvs 	return (0);
719e00787e6Smvs }
720e00787e6Smvs 
721e00787e6Smvs int
72286e05c94Smvs rip6_shutdown(struct socket *so)
72386e05c94Smvs {
72486e05c94Smvs 	/*
72586e05c94Smvs 	 * Mark the connection as being incapable of further input.
72686e05c94Smvs 	 */
72786e05c94Smvs 	soassertlocked(so);
72886e05c94Smvs 	socantsendmore(so);
72986e05c94Smvs 	return (0);
73086e05c94Smvs }
73186e05c94Smvs 
73286e05c94Smvs int
73390b3510cSmvs rip6_send(struct socket *so, struct mbuf *m, struct mbuf *nam,
73490b3510cSmvs 	struct mbuf *control)
73590b3510cSmvs {
736921ffa12Sbluhm 	struct inpcb *inp = sotoinpcb(so);
73790b3510cSmvs 	struct sockaddr_in6 dst;
73890b3510cSmvs 	int error;
73990b3510cSmvs 
74090b3510cSmvs 	soassertlocked(so);
74190b3510cSmvs 
74290b3510cSmvs 	/*
74390b3510cSmvs 	 * Ship a packet out. The appropriate raw output
74490b3510cSmvs 	 * routine handles any messaging necessary.
74590b3510cSmvs 	 */
74690b3510cSmvs 
74790b3510cSmvs 	/* always copy sockaddr to avoid overwrites */
74890b3510cSmvs 	memset(&dst, 0, sizeof(dst));
74990b3510cSmvs 	dst.sin6_family = AF_INET6;
75090b3510cSmvs 	dst.sin6_len = sizeof(dst);
75190b3510cSmvs 	if (so->so_state & SS_ISCONNECTED) {
75290b3510cSmvs 		if (nam) {
75390b3510cSmvs 			error = EISCONN;
75490b3510cSmvs 			goto out;
75590b3510cSmvs 		}
756921ffa12Sbluhm 		dst.sin6_addr = inp->inp_faddr6;
75790b3510cSmvs 	} else {
75890b3510cSmvs 		struct sockaddr_in6 *addr6;
75990b3510cSmvs 
76090b3510cSmvs 		if (nam == NULL) {
76190b3510cSmvs 			error = ENOTCONN;
76290b3510cSmvs 			goto out;
76390b3510cSmvs 		}
76490b3510cSmvs 		if ((error = in6_nam2sin6(nam, &addr6)))
76590b3510cSmvs 			goto out;
76690b3510cSmvs 		dst.sin6_addr = addr6->sin6_addr;
76790b3510cSmvs 		dst.sin6_scope_id = addr6->sin6_scope_id;
76890b3510cSmvs 	}
76990b3510cSmvs 	error = rip6_output(m, so, sin6tosa(&dst), control);
77090b3510cSmvs 	control = NULL;
77190b3510cSmvs 	m = NULL;
77290b3510cSmvs 
77390b3510cSmvs out:
77490b3510cSmvs 	m_freem(control);
77590b3510cSmvs 	m_freem(m);
77690b3510cSmvs 
77790b3510cSmvs 	return (error);
77890b3510cSmvs }
77990b3510cSmvs 
78090b3510cSmvs int
78107ba5f7cSjca rip6_sysctl_rip6stat(void *oldp, size_t *oldplen, void *newp)
78207ba5f7cSjca {
78307ba5f7cSjca 	struct rip6stat rip6stat;
78407ba5f7cSjca 
78507ba5f7cSjca 	CTASSERT(sizeof(rip6stat) == rip6s_ncounters * sizeof(uint64_t));
786bf0d449cSmpi 	counters_read(rip6counters, (uint64_t *)&rip6stat, rip6s_ncounters,
787bf0d449cSmpi 	    NULL);
78807ba5f7cSjca 
78907ba5f7cSjca 	return (sysctl_rdstruct(oldp, oldplen, newp,
79007ba5f7cSjca 	    &rip6stat, sizeof(rip6stat)));
79107ba5f7cSjca }
79207ba5f7cSjca 
79307ba5f7cSjca int
794ab46c28dSderaadt rip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
795ab46c28dSderaadt     void *newp, size_t newlen)
796ab46c28dSderaadt {
797ab46c28dSderaadt 	/* All sysctl names at this level are terminal. */
798ab46c28dSderaadt 	if (namelen != 1)
799ab46c28dSderaadt 		return ENOTDIR;
800ab46c28dSderaadt 
801ab46c28dSderaadt 	switch (name[0]) {
802ab46c28dSderaadt 	case RIPV6CTL_STATS:
80307ba5f7cSjca 		return (rip6_sysctl_rip6stat(oldp, oldlenp, newp));
804ab46c28dSderaadt 	default:
805ab46c28dSderaadt 		return (EOPNOTSUPP);
806ab46c28dSderaadt 	}
807ab46c28dSderaadt 	/* NOTREACHED */
808ab46c28dSderaadt }
809