xref: /freebsd/sys/netinet6/raw_ip6.c (revision 33cde13046eaba762428c55bb5d5e07c1a7c5f33)
1caf43b02SWarner Losh /*-
282cd038dSYoshinobu Inoue  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
382cd038dSYoshinobu Inoue  * All rights reserved.
482cd038dSYoshinobu Inoue  *
582cd038dSYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
682cd038dSYoshinobu Inoue  * modification, are permitted provided that the following conditions
782cd038dSYoshinobu Inoue  * are met:
882cd038dSYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
982cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
1082cd038dSYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
1182cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
1282cd038dSYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
1382cd038dSYoshinobu Inoue  * 3. Neither the name of the project nor the names of its contributors
1482cd038dSYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
1582cd038dSYoshinobu Inoue  *    without specific prior written permission.
1682cd038dSYoshinobu Inoue  *
1782cd038dSYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1882cd038dSYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1982cd038dSYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2082cd038dSYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2182cd038dSYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2282cd038dSYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2382cd038dSYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2482cd038dSYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2582cd038dSYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2682cd038dSYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2782cd038dSYoshinobu Inoue  * SUCH DAMAGE.
2882cd038dSYoshinobu Inoue  */
2982cd038dSYoshinobu Inoue 
30caf43b02SWarner Losh /*-
3182cd038dSYoshinobu Inoue  * Copyright (c) 1982, 1986, 1988, 1993
320ae76120SRobert Watson  *	The Regents of the University of California.
330ae76120SRobert Watson  * All rights reserved.
3482cd038dSYoshinobu Inoue  *
3582cd038dSYoshinobu Inoue  * Redistribution and use in source and binary forms, with or without
3682cd038dSYoshinobu Inoue  * modification, are permitted provided that the following conditions
3782cd038dSYoshinobu Inoue  * are met:
3882cd038dSYoshinobu Inoue  * 1. Redistributions of source code must retain the above copyright
3982cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer.
4082cd038dSYoshinobu Inoue  * 2. Redistributions in binary form must reproduce the above copyright
4182cd038dSYoshinobu Inoue  *    notice, this list of conditions and the following disclaimer in the
4282cd038dSYoshinobu Inoue  *    documentation and/or other materials provided with the distribution.
4382cd038dSYoshinobu Inoue  * 4. Neither the name of the University nor the names of its contributors
4482cd038dSYoshinobu Inoue  *    may be used to endorse or promote products derived from this software
4582cd038dSYoshinobu Inoue  *    without specific prior written permission.
4682cd038dSYoshinobu Inoue  *
4782cd038dSYoshinobu Inoue  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4882cd038dSYoshinobu Inoue  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4982cd038dSYoshinobu Inoue  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5082cd038dSYoshinobu Inoue  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5182cd038dSYoshinobu Inoue  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5282cd038dSYoshinobu Inoue  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5382cd038dSYoshinobu Inoue  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5482cd038dSYoshinobu Inoue  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5582cd038dSYoshinobu Inoue  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5682cd038dSYoshinobu Inoue  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5782cd038dSYoshinobu Inoue  * SUCH DAMAGE.
5882cd038dSYoshinobu Inoue  *
5982cd038dSYoshinobu Inoue  *	@(#)raw_ip.c	8.2 (Berkeley) 1/4/94
6082cd038dSYoshinobu Inoue  */
6182cd038dSYoshinobu Inoue 
62b48287a3SDavid E. O'Brien #include <sys/cdefs.h>
63b48287a3SDavid E. O'Brien __FBSDID("$FreeBSD$");
64b48287a3SDavid E. O'Brien 
656a800098SYoshinobu Inoue #include "opt_ipsec.h"
6633841545SHajimu UMEMOTO #include "opt_inet6.h"
6733553d6eSBjoern A. Zeeb #include "opt_route.h"
686a800098SYoshinobu Inoue 
6982cd038dSYoshinobu Inoue #include <sys/param.h>
7082cd038dSYoshinobu Inoue #include <sys/errno.h>
71413628a7SBjoern A. Zeeb #include <sys/jail.h>
72960ed29cSSeigo Tanimura #include <sys/lock.h>
73960ed29cSSeigo Tanimura #include <sys/malloc.h>
74960ed29cSSeigo Tanimura #include <sys/mbuf.h>
75c2259ba4SRobert Watson #include <sys/priv.h>
76960ed29cSSeigo Tanimura #include <sys/proc.h>
77960ed29cSSeigo Tanimura #include <sys/protosw.h>
78960ed29cSSeigo Tanimura #include <sys/signalvar.h>
79960ed29cSSeigo Tanimura #include <sys/socket.h>
80960ed29cSSeigo Tanimura #include <sys/socketvar.h>
81960ed29cSSeigo Tanimura #include <sys/sx.h>
82a1f7e5f8SHajimu UMEMOTO #include <sys/syslog.h>
83603724d3SBjoern A. Zeeb #include <sys/vimage.h>
8482cd038dSYoshinobu Inoue 
8582cd038dSYoshinobu Inoue #include <net/if.h>
8682cd038dSYoshinobu Inoue #include <net/if_types.h>
87960ed29cSSeigo Tanimura #include <net/route.h>
884b79449eSBjoern A. Zeeb #include <net/vnet.h>
8982cd038dSYoshinobu Inoue 
9082cd038dSYoshinobu Inoue #include <netinet/in.h>
9182cd038dSYoshinobu Inoue #include <netinet/in_var.h>
9282cd038dSYoshinobu Inoue #include <netinet/in_systm.h>
9382cd038dSYoshinobu Inoue #include <netinet/in_pcb.h>
944b79449eSBjoern A. Zeeb #include <netinet/vinet.h>
954b79449eSBjoern A. Zeeb 
964b79449eSBjoern A. Zeeb #include <netinet/icmp6.h>
97960ed29cSSeigo Tanimura #include <netinet/ip6.h>
98686cdd19SJun-ichiro itojun Hagino #include <netinet6/ip6protosw.h>
99960ed29cSSeigo Tanimura #include <netinet6/ip6_mroute.h>
100960ed29cSSeigo Tanimura #include <netinet6/in6_pcb.h>
101960ed29cSSeigo Tanimura #include <netinet6/ip6_var.h>
102960ed29cSSeigo Tanimura #include <netinet6/nd6.h>
103960ed29cSSeigo Tanimura #include <netinet6/raw_ip6.h>
104686cdd19SJun-ichiro itojun Hagino #include <netinet6/scope6_var.h>
1054b79449eSBjoern A. Zeeb #include <netinet6/vinet6.h>
10682cd038dSYoshinobu Inoue 
107b2630c29SGeorge V. Neville-Neil #ifdef IPSEC
108b9234fafSSam Leffler #include <netipsec/ipsec.h>
109b9234fafSSam Leffler #include <netipsec/ipsec6.h>
110b2630c29SGeorge V. Neville-Neil #endif /* IPSEC */
111b9234fafSSam Leffler 
11282cd038dSYoshinobu Inoue #include <machine/stdarg.h>
11382cd038dSYoshinobu Inoue 
11482cd038dSYoshinobu Inoue #define	satosin6(sa)	((struct sockaddr_in6 *)(sa))
11582cd038dSYoshinobu Inoue #define	ifatoia6(ifa)	((struct in6_ifaddr *)(ifa))
11682cd038dSYoshinobu Inoue 
11782cd038dSYoshinobu Inoue /*
11882cd038dSYoshinobu Inoue  * Raw interface to IP6 protocol.
11982cd038dSYoshinobu Inoue  */
12082cd038dSYoshinobu Inoue 
12197021c24SMarko Zec #ifdef VIMAGE_GLOBALS
12282cd038dSYoshinobu Inoue extern struct	inpcbhead ripcb;
12382cd038dSYoshinobu Inoue extern struct	inpcbinfo ripcbinfo;
12433841545SHajimu UMEMOTO struct rip6stat rip6stat;
12544e33a07SMarko Zec #endif
12633841545SHajimu UMEMOTO 
12797021c24SMarko Zec extern u_long	rip_sendspace;
12897021c24SMarko Zec extern u_long	rip_recvspace;
12997021c24SMarko Zec 
13082cd038dSYoshinobu Inoue /*
13133cde130SBruce M Simpson  * Hooks for multicast routing. They all default to NULL, so leave them not
13233cde130SBruce M Simpson  * initialized and rely on BSS being set to 0.
1336be2e366SBruce M Simpson  */
13433cde130SBruce M Simpson 
13533cde130SBruce M Simpson /*
13633cde130SBruce M Simpson  * The socket used to communicate with the multicast routing daemon.
13733cde130SBruce M Simpson  */
13833cde130SBruce M Simpson #ifdef VIMAGE_GLOBALS
13933cde130SBruce M Simpson struct socket *ip6_mrouter;
14033cde130SBruce M Simpson #endif
14133cde130SBruce M Simpson 
14233cde130SBruce M Simpson /*
14333cde130SBruce M Simpson  * The various mrouter functions.
14433cde130SBruce M Simpson  */
1456be2e366SBruce M Simpson int (*ip6_mrouter_set)(struct socket *, struct sockopt *);
1466be2e366SBruce M Simpson int (*ip6_mrouter_get)(struct socket *, struct sockopt *);
1476be2e366SBruce M Simpson int (*ip6_mrouter_done)(void);
1486be2e366SBruce M Simpson int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *);
1496be2e366SBruce M Simpson int (*mrt6_ioctl)(int, caddr_t);
1506be2e366SBruce M Simpson 
1516be2e366SBruce M Simpson /*
1520ae76120SRobert Watson  * Setup generic address and protocol structures for raw_input routine, then
1530ae76120SRobert Watson  * pass them along with mbuf chain.
15482cd038dSYoshinobu Inoue  */
15582cd038dSYoshinobu Inoue int
1561272577eSXin LI rip6_input(struct mbuf **mp, int *offp, int proto)
15782cd038dSYoshinobu Inoue {
1588b615593SMarko Zec 	INIT_VNET_INET(curvnet);
1598b615593SMarko Zec 	INIT_VNET_INET6(curvnet);
1608b615593SMarko Zec #ifdef IPSEC
1618b615593SMarko Zec 	INIT_VNET_IPSEC(curvnet);
1628b615593SMarko Zec #endif
16333cde130SBruce M Simpson 	struct ifnet *ifp;
16482cd038dSYoshinobu Inoue 	struct mbuf *m = *mp;
16582cd038dSYoshinobu Inoue 	register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
16682cd038dSYoshinobu Inoue 	register struct inpcb *in6p;
16782cd038dSYoshinobu Inoue 	struct inpcb *last = 0;
16833841545SHajimu UMEMOTO 	struct mbuf *opts = NULL;
169af12e09eSHajimu UMEMOTO 	struct sockaddr_in6 fromsa;
17082cd038dSYoshinobu Inoue 
171603724d3SBjoern A. Zeeb 	V_rip6stat.rip6s_ipackets++;
17233841545SHajimu UMEMOTO 
1739494d596SBrooks Davis 	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
1740ae76120SRobert Watson 		/* XXX Send icmp6 host/port unreach? */
17582cd038dSYoshinobu Inoue 		m_freem(m);
1760ae76120SRobert Watson 		return (IPPROTO_DONE);
17782cd038dSYoshinobu Inoue 	}
17833841545SHajimu UMEMOTO 
179af12e09eSHajimu UMEMOTO 	init_sin6(&fromsa, m); /* general init */
18082cd038dSYoshinobu Inoue 
18133cde130SBruce M Simpson 	ifp = m->m_pkthdr.rcvif;
18233cde130SBruce M Simpson 
183603724d3SBjoern A. Zeeb 	INP_INFO_RLOCK(&V_ripcbinfo);
184603724d3SBjoern A. Zeeb 	LIST_FOREACH(in6p, &V_ripcb, inp_list) {
185413628a7SBjoern A. Zeeb 		/* XXX inp locking */
186fc384fa5SBjoern A. Zeeb 		if ((in6p->inp_vflag & INP_IPV6) == 0)
18782cd038dSYoshinobu Inoue 			continue;
188fc384fa5SBjoern A. Zeeb 		if (in6p->inp_ip_p &&
189fc384fa5SBjoern A. Zeeb 		    in6p->inp_ip_p != proto)
190b11e21aeSAlexander Motin 			continue;
19182cd038dSYoshinobu Inoue 		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
19282cd038dSYoshinobu Inoue 		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
193b11e21aeSAlexander Motin 			continue;
19482cd038dSYoshinobu Inoue 		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
19582cd038dSYoshinobu Inoue 		    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
196b11e21aeSAlexander Motin 			continue;
19733cde130SBruce M Simpson 		if (jailed(in6p->inp_cred)) {
19833cde130SBruce M Simpson 			/*
19933cde130SBruce M Simpson 			 * Allow raw socket in jail to receive multicast;
20033cde130SBruce M Simpson 			 * assume process had PRIV_NETINET_RAW at attach,
20133cde130SBruce M Simpson 			 * and fall through into normal filter path if so.
20233cde130SBruce M Simpson 			 */
20333cde130SBruce M Simpson 			if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
20433cde130SBruce M Simpson 			    prison_check_ip6(in6p->inp_cred,
20533cde130SBruce M Simpson 			    &ip6->ip6_dst) != 0)
206413628a7SBjoern A. Zeeb 				continue;
20733cde130SBruce M Simpson 		}
20833841545SHajimu UMEMOTO 		if (in6p->in6p_cksum != -1) {
209603724d3SBjoern A. Zeeb 			V_rip6stat.rip6s_isum++;
2104350fcabSSUZUKI Shinsuke 			if (in6_cksum(m, proto, *offp,
21182cd038dSYoshinobu Inoue 			    m->m_pkthdr.len - *offp)) {
2126c5bbf5cSAlexander Motin 				INP_RUNLOCK(in6p);
213603724d3SBjoern A. Zeeb 				V_rip6stat.rip6s_badsum++;
214b11e21aeSAlexander Motin 				continue;
21582cd038dSYoshinobu Inoue 			}
21633841545SHajimu UMEMOTO 		}
21733cde130SBruce M Simpson 		INP_RLOCK(in6p);
21833cde130SBruce M Simpson 		/*
21933cde130SBruce M Simpson 		 * If this raw socket has multicast state, and we
22033cde130SBruce M Simpson 		 * have received a multicast, check if this socket
22133cde130SBruce M Simpson 		 * should receive it, as multicast filtering is now
22233cde130SBruce M Simpson 		 * the responsibility of the transport layer.
22333cde130SBruce M Simpson 		 */
22433cde130SBruce M Simpson 		if (in6p->in6p_moptions &&
22533cde130SBruce M Simpson 		    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
22633cde130SBruce M Simpson 			struct sockaddr_in6 mcaddr;
22733cde130SBruce M Simpson 			int blocked;
22833cde130SBruce M Simpson 
22933cde130SBruce M Simpson 			bzero(&mcaddr, sizeof(struct sockaddr_in6));
23033cde130SBruce M Simpson 			mcaddr.sin6_len = sizeof(struct sockaddr_in6);
23133cde130SBruce M Simpson 			mcaddr.sin6_family = AF_INET6;
23233cde130SBruce M Simpson 			mcaddr.sin6_addr = ip6->ip6_dst;
23333cde130SBruce M Simpson 
23433cde130SBruce M Simpson 			blocked = im6o_mc_filter(in6p->in6p_moptions, ifp,
23533cde130SBruce M Simpson 			    (struct sockaddr *)&mcaddr,
23633cde130SBruce M Simpson 			    (struct sockaddr *)&fromsa);
23733cde130SBruce M Simpson 			if (blocked != MCAST_PASS) {
23833cde130SBruce M Simpson 				IP6STAT_INC(ip6s_notmember);
23933cde130SBruce M Simpson 				continue;
24033cde130SBruce M Simpson 			}
24133cde130SBruce M Simpson 		}
242aba53ef0SKip Macy 		if (last != NULL) {
24382cd038dSYoshinobu Inoue 			struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
24433841545SHajimu UMEMOTO 
245b2630c29SGeorge V. Neville-Neil #ifdef IPSEC
246da0f4099SHajimu UMEMOTO 			/*
247da0f4099SHajimu UMEMOTO 			 * Check AH/ESP integrity.
248da0f4099SHajimu UMEMOTO 			 */
249da0f4099SHajimu UMEMOTO 			if (n && ipsec6_in_reject(n, last)) {
250da0f4099SHajimu UMEMOTO 				m_freem(n);
251603724d3SBjoern A. Zeeb 				V_ipsec6stat.in_polvio++;
2520ae76120SRobert Watson 				/* Do not inject data into pcb. */
253b9234fafSSam Leffler 			} else
254b2630c29SGeorge V. Neville-Neil #endif /* IPSEC */
25582cd038dSYoshinobu Inoue 			if (n) {
25697590249SBjoern A. Zeeb 				if (last->inp_flags & INP_CONTROLOPTS ||
257fc384fa5SBjoern A. Zeeb 				    last->inp_socket->so_options & SO_TIMESTAMP)
25811de19f4SHajimu UMEMOTO 					ip6_savecontrol(last, n, &opts);
25982cd038dSYoshinobu Inoue 				/* strip intermediate headers */
26082cd038dSYoshinobu Inoue 				m_adj(n, *offp);
261fc384fa5SBjoern A. Zeeb 				if (sbappendaddr(&last->inp_socket->so_rcv,
262af12e09eSHajimu UMEMOTO 						(struct sockaddr *)&fromsa,
26382cd038dSYoshinobu Inoue 						 n, opts) == 0) {
26482cd038dSYoshinobu Inoue 					m_freem(n);
26582cd038dSYoshinobu Inoue 					if (opts)
26682cd038dSYoshinobu Inoue 						m_freem(opts);
267603724d3SBjoern A. Zeeb 					V_rip6stat.rip6s_fullsock++;
2684cc20ab1SSeigo Tanimura 				} else
269fc384fa5SBjoern A. Zeeb 					sorwakeup(last->inp_socket);
27082cd038dSYoshinobu Inoue 				opts = NULL;
27182cd038dSYoshinobu Inoue 			}
2729ad11dd8SRobert Watson 			INP_RUNLOCK(last);
27382cd038dSYoshinobu Inoue 		}
27482cd038dSYoshinobu Inoue 		last = in6p;
27582cd038dSYoshinobu Inoue 	}
276603724d3SBjoern A. Zeeb 	INP_INFO_RUNLOCK(&V_ripcbinfo);
277b2630c29SGeorge V. Neville-Neil #ifdef IPSEC
278da0f4099SHajimu UMEMOTO 	/*
279da0f4099SHajimu UMEMOTO 	 * Check AH/ESP integrity.
280da0f4099SHajimu UMEMOTO 	 */
28175bab8b8SKip Macy 	if ((last != NULL) && ipsec6_in_reject(m, last)) {
282da0f4099SHajimu UMEMOTO 		m_freem(m);
283603724d3SBjoern A. Zeeb 		V_ipsec6stat.in_polvio++;
284603724d3SBjoern A. Zeeb 		V_ip6stat.ip6s_delivered--;
2850ae76120SRobert Watson 		/* Do not inject data into pcb. */
2869ad11dd8SRobert Watson 		INP_RUNLOCK(last);
287b9234fafSSam Leffler 	} else
288b2630c29SGeorge V. Neville-Neil #endif /* IPSEC */
289aba53ef0SKip Macy 	if (last != NULL) {
29097590249SBjoern A. Zeeb 		if (last->inp_flags & INP_CONTROLOPTS ||
291fc384fa5SBjoern A. Zeeb 		    last->inp_socket->so_options & SO_TIMESTAMP)
29211de19f4SHajimu UMEMOTO 			ip6_savecontrol(last, m, &opts);
2930ae76120SRobert Watson 		/* Strip intermediate headers. */
29482cd038dSYoshinobu Inoue 		m_adj(m, *offp);
295fc384fa5SBjoern A. Zeeb 		if (sbappendaddr(&last->inp_socket->so_rcv,
296af12e09eSHajimu UMEMOTO 		    (struct sockaddr *)&fromsa, m, opts) == 0) {
29782cd038dSYoshinobu Inoue 			m_freem(m);
29882cd038dSYoshinobu Inoue 			if (opts)
29982cd038dSYoshinobu Inoue 				m_freem(opts);
300603724d3SBjoern A. Zeeb 			V_rip6stat.rip6s_fullsock++;
3014cc20ab1SSeigo Tanimura 		} else
302fc384fa5SBjoern A. Zeeb 			sorwakeup(last->inp_socket);
3039ad11dd8SRobert Watson 		INP_RUNLOCK(last);
30482cd038dSYoshinobu Inoue 	} else {
305603724d3SBjoern A. Zeeb 		V_rip6stat.rip6s_nosock++;
30633841545SHajimu UMEMOTO 		if (m->m_flags & M_MCAST)
307603724d3SBjoern A. Zeeb 			V_rip6stat.rip6s_nosockmcast++;
30882cd038dSYoshinobu Inoue 		if (proto == IPPROTO_NONE)
30982cd038dSYoshinobu Inoue 			m_freem(m);
31082cd038dSYoshinobu Inoue 		else {
31182cd038dSYoshinobu Inoue 			char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */
31282cd038dSYoshinobu Inoue 			icmp6_error(m, ICMP6_PARAM_PROB,
31382cd038dSYoshinobu Inoue 			    ICMP6_PARAMPROB_NEXTHEADER,
31482cd038dSYoshinobu Inoue 			    prvnxtp - mtod(m, char *));
31582cd038dSYoshinobu Inoue 		}
316603724d3SBjoern A. Zeeb 		V_ip6stat.ip6s_delivered--;
31782cd038dSYoshinobu Inoue 	}
3180ae76120SRobert Watson 	return (IPPROTO_DONE);
31982cd038dSYoshinobu Inoue }
32082cd038dSYoshinobu Inoue 
321686cdd19SJun-ichiro itojun Hagino void
3221272577eSXin LI rip6_ctlinput(int cmd, struct sockaddr *sa, void *d)
323686cdd19SJun-ichiro itojun Hagino {
3248b615593SMarko Zec 	INIT_VNET_INET(curvnet);
325686cdd19SJun-ichiro itojun Hagino 	struct ip6_hdr *ip6;
326686cdd19SJun-ichiro itojun Hagino 	struct mbuf *m;
327686cdd19SJun-ichiro itojun Hagino 	int off = 0;
32833841545SHajimu UMEMOTO 	struct ip6ctlparam *ip6cp = NULL;
32933841545SHajimu UMEMOTO 	const struct sockaddr_in6 *sa6_src = NULL;
330efddf5c6SHajimu UMEMOTO 	void *cmdarg;
3319233d8f3SDavid E. O'Brien 	struct inpcb *(*notify)(struct inpcb *, int) = in6_rtchange;
332686cdd19SJun-ichiro itojun Hagino 
333686cdd19SJun-ichiro itojun Hagino 	if (sa->sa_family != AF_INET6 ||
334686cdd19SJun-ichiro itojun Hagino 	    sa->sa_len != sizeof(struct sockaddr_in6))
335686cdd19SJun-ichiro itojun Hagino 		return;
336686cdd19SJun-ichiro itojun Hagino 
337686cdd19SJun-ichiro itojun Hagino 	if ((unsigned)cmd >= PRC_NCMDS)
338686cdd19SJun-ichiro itojun Hagino 		return;
339686cdd19SJun-ichiro itojun Hagino 	if (PRC_IS_REDIRECT(cmd))
340686cdd19SJun-ichiro itojun Hagino 		notify = in6_rtchange, d = NULL;
341686cdd19SJun-ichiro itojun Hagino 	else if (cmd == PRC_HOSTDEAD)
342686cdd19SJun-ichiro itojun Hagino 		d = NULL;
343686cdd19SJun-ichiro itojun Hagino 	else if (inet6ctlerrmap[cmd] == 0)
344686cdd19SJun-ichiro itojun Hagino 		return;
345686cdd19SJun-ichiro itojun Hagino 
3460ae76120SRobert Watson 	/*
3470ae76120SRobert Watson 	 * If the parameter is from icmp6, decode it.
3480ae76120SRobert Watson 	 */
349686cdd19SJun-ichiro itojun Hagino 	if (d != NULL) {
35033841545SHajimu UMEMOTO 		ip6cp = (struct ip6ctlparam *)d;
351686cdd19SJun-ichiro itojun Hagino 		m = ip6cp->ip6c_m;
352686cdd19SJun-ichiro itojun Hagino 		ip6 = ip6cp->ip6c_ip6;
353686cdd19SJun-ichiro itojun Hagino 		off = ip6cp->ip6c_off;
354efddf5c6SHajimu UMEMOTO 		cmdarg = ip6cp->ip6c_cmdarg;
35533841545SHajimu UMEMOTO 		sa6_src = ip6cp->ip6c_src;
356686cdd19SJun-ichiro itojun Hagino 	} else {
357686cdd19SJun-ichiro itojun Hagino 		m = NULL;
358686cdd19SJun-ichiro itojun Hagino 		ip6 = NULL;
359efddf5c6SHajimu UMEMOTO 		cmdarg = NULL;
36033841545SHajimu UMEMOTO 		sa6_src = &sa6_any;
361686cdd19SJun-ichiro itojun Hagino 	}
362686cdd19SJun-ichiro itojun Hagino 
363603724d3SBjoern A. Zeeb 	(void) in6_pcbnotify(&V_ripcbinfo, sa, 0,
3640ae76120SRobert Watson 	    (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify);
365686cdd19SJun-ichiro itojun Hagino }
366686cdd19SJun-ichiro itojun Hagino 
36782cd038dSYoshinobu Inoue /*
3680ae76120SRobert Watson  * Generate IPv6 header and pass packet to ip6_output.  Tack on options user
3690ae76120SRobert Watson  * may have setup with control call.
37082cd038dSYoshinobu Inoue  */
37182cd038dSYoshinobu Inoue int
37282cd038dSYoshinobu Inoue #if __STDC__
37382cd038dSYoshinobu Inoue rip6_output(struct mbuf *m, ...)
37482cd038dSYoshinobu Inoue #else
37582cd038dSYoshinobu Inoue rip6_output(m, va_alist)
37682cd038dSYoshinobu Inoue 	struct mbuf *m;
37782cd038dSYoshinobu Inoue 	va_dcl
37882cd038dSYoshinobu Inoue #endif
37982cd038dSYoshinobu Inoue {
3808b615593SMarko Zec 	INIT_VNET_INET6(curvnet);
38107eb2995SHajimu UMEMOTO 	struct mbuf *control;
38282cd038dSYoshinobu Inoue 	struct socket *so;
38382cd038dSYoshinobu Inoue 	struct sockaddr_in6 *dstsock;
38482cd038dSYoshinobu Inoue 	struct in6_addr *dst;
38582cd038dSYoshinobu Inoue 	struct ip6_hdr *ip6;
38682cd038dSYoshinobu Inoue 	struct inpcb *in6p;
38782cd038dSYoshinobu Inoue 	u_int	plen = m->m_pkthdr.len;
38882cd038dSYoshinobu Inoue 	int error = 0;
389e07db7aaSHajimu UMEMOTO 	struct ip6_pktopts opt, *optp;
39082cd038dSYoshinobu Inoue 	struct ifnet *oifp = NULL;
39182cd038dSYoshinobu Inoue 	int type = 0, code = 0;		/* for ICMPv6 output statistics only */
392a1f7e5f8SHajimu UMEMOTO 	int scope_ambiguous = 0;
393f95d4633SHajimu UMEMOTO 	struct in6_addr *in6a;
39482cd038dSYoshinobu Inoue 	va_list ap;
39582cd038dSYoshinobu Inoue 
39682cd038dSYoshinobu Inoue 	va_start(ap, m);
39782cd038dSYoshinobu Inoue 	so = va_arg(ap, struct socket *);
39882cd038dSYoshinobu Inoue 	dstsock = va_arg(ap, struct sockaddr_in6 *);
39982cd038dSYoshinobu Inoue 	control = va_arg(ap, struct mbuf *);
40082cd038dSYoshinobu Inoue 	va_end(ap);
40182cd038dSYoshinobu Inoue 
402fc384fa5SBjoern A. Zeeb 	in6p = sotoinpcb(so);
4038501a69cSRobert Watson 	INP_WLOCK(in6p);
40482cd038dSYoshinobu Inoue 
40582cd038dSYoshinobu Inoue 	dst = &dstsock->sin6_addr;
40675bab8b8SKip Macy 	if (control != NULL) {
407d5e3406dSHajimu UMEMOTO 		if ((error = ip6_setpktopts(control, &opt,
40879ba3952SBjoern A. Zeeb 		    in6p->in6p_outputopts, so->so_cred,
40979ba3952SBjoern A. Zeeb 		    so->so_proto->pr_protocol)) != 0) {
41082cd038dSYoshinobu Inoue 			goto bad;
411f95d4633SHajimu UMEMOTO 		}
412e07db7aaSHajimu UMEMOTO 		optp = &opt;
413e07db7aaSHajimu UMEMOTO 	} else
414e07db7aaSHajimu UMEMOTO 		optp = in6p->in6p_outputopts;
41582cd038dSYoshinobu Inoue 
41682cd038dSYoshinobu Inoue 	/*
417a1f7e5f8SHajimu UMEMOTO 	 * Check and convert scope zone ID into internal form.
4180ae76120SRobert Watson 	 *
419a1f7e5f8SHajimu UMEMOTO 	 * XXX: we may still need to determine the zone later.
420a1f7e5f8SHajimu UMEMOTO 	 */
421a1f7e5f8SHajimu UMEMOTO 	if (!(so->so_state & SS_ISCONNECTED)) {
422603724d3SBjoern A. Zeeb 		if (dstsock->sin6_scope_id == 0 && !V_ip6_use_defzone)
423a1f7e5f8SHajimu UMEMOTO 			scope_ambiguous = 1;
424603724d3SBjoern A. Zeeb 		if ((error = sa6_embedscope(dstsock, V_ip6_use_defzone)) != 0)
425a1f7e5f8SHajimu UMEMOTO 			goto bad;
426a1f7e5f8SHajimu UMEMOTO 	}
427a1f7e5f8SHajimu UMEMOTO 
428a1f7e5f8SHajimu UMEMOTO 	/*
4290ae76120SRobert Watson 	 * For an ICMPv6 packet, we should know its type and code to update
4300ae76120SRobert Watson 	 * statistics.
43182cd038dSYoshinobu Inoue 	 */
43282cd038dSYoshinobu Inoue 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
43382cd038dSYoshinobu Inoue 		struct icmp6_hdr *icmp6;
43482cd038dSYoshinobu Inoue 		if (m->m_len < sizeof(struct icmp6_hdr) &&
43582cd038dSYoshinobu Inoue 		    (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) {
43682cd038dSYoshinobu Inoue 			error = ENOBUFS;
43782cd038dSYoshinobu Inoue 			goto bad;
43882cd038dSYoshinobu Inoue 		}
43982cd038dSYoshinobu Inoue 		icmp6 = mtod(m, struct icmp6_hdr *);
44082cd038dSYoshinobu Inoue 		type = icmp6->icmp6_type;
44182cd038dSYoshinobu Inoue 		code = icmp6->icmp6_code;
44282cd038dSYoshinobu Inoue 	}
44382cd038dSYoshinobu Inoue 
4448a0c4da8SRobert Watson 	M_PREPEND(m, sizeof(*ip6), M_DONTWAIT);
4458a0c4da8SRobert Watson 	if (m == NULL) {
4468a0c4da8SRobert Watson 		error = ENOBUFS;
4478a0c4da8SRobert Watson 		goto bad;
4488a0c4da8SRobert Watson 	}
44982cd038dSYoshinobu Inoue 	ip6 = mtod(m, struct ip6_hdr *);
45082cd038dSYoshinobu Inoue 
45182cd038dSYoshinobu Inoue 	/*
45282cd038dSYoshinobu Inoue 	 * Source address selection.
45382cd038dSYoshinobu Inoue 	 */
454f2f877d3SBjoern A. Zeeb 	if ((in6a = in6_selectsrc(dstsock, optp, in6p, NULL, so->so_cred,
455f2f877d3SBjoern A. Zeeb 	    &oifp, &error)) == NULL) {
45682cd038dSYoshinobu Inoue 		if (error == 0)
45782cd038dSYoshinobu Inoue 			error = EADDRNOTAVAIL;
45882cd038dSYoshinobu Inoue 		goto bad;
45982cd038dSYoshinobu Inoue 	}
460b89e82ddSJamie Gritton 	error = prison_get_ip6(in6p->inp_cred, in6a);
461b89e82ddSJamie Gritton 	if (error != 0)
462413628a7SBjoern A. Zeeb 		goto bad;
46382cd038dSYoshinobu Inoue 	ip6->ip6_src = *in6a;
464a1f7e5f8SHajimu UMEMOTO 
465a1f7e5f8SHajimu UMEMOTO 	if (oifp && scope_ambiguous) {
466a1f7e5f8SHajimu UMEMOTO 		/*
467a1f7e5f8SHajimu UMEMOTO 		 * Application should provide a proper zone ID or the use of
468a1f7e5f8SHajimu UMEMOTO 		 * default zone IDs should be enabled.  Unfortunately, some
469a1f7e5f8SHajimu UMEMOTO 		 * applications do not behave as it should, so we need a
470a1f7e5f8SHajimu UMEMOTO 		 * workaround.  Even if an appropriate ID is not determined
471a1f7e5f8SHajimu UMEMOTO 		 * (when it's required), if we can determine the outgoing
472a1f7e5f8SHajimu UMEMOTO 		 * interface. determine the zone ID based on the interface.
473a1f7e5f8SHajimu UMEMOTO 		 */
474a1f7e5f8SHajimu UMEMOTO 		error = in6_setscope(&dstsock->sin6_addr, oifp, NULL);
475a1f7e5f8SHajimu UMEMOTO 		if (error != 0)
476a1f7e5f8SHajimu UMEMOTO 			goto bad;
477a1f7e5f8SHajimu UMEMOTO 	}
478a1f7e5f8SHajimu UMEMOTO 	ip6->ip6_dst = dstsock->sin6_addr;
479a1f7e5f8SHajimu UMEMOTO 
4800ae76120SRobert Watson 	/*
4810ae76120SRobert Watson 	 * Fill in the rest of the IPv6 header fields.
4820ae76120SRobert Watson 	 */
4836a800098SYoshinobu Inoue 	ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |
484fc384fa5SBjoern A. Zeeb 	    (in6p->inp_flow & IPV6_FLOWINFO_MASK);
4856a800098SYoshinobu Inoue 	ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
4866a800098SYoshinobu Inoue 	    (IPV6_VERSION & IPV6_VERSION_MASK);
4870ae76120SRobert Watson 
4880ae76120SRobert Watson 	/*
4890ae76120SRobert Watson 	 * ip6_plen will be filled in ip6_output, so not fill it here.
4900ae76120SRobert Watson 	 */
491fc384fa5SBjoern A. Zeeb 	ip6->ip6_nxt = in6p->inp_ip_p;
49282cd038dSYoshinobu Inoue 	ip6->ip6_hlim = in6_selecthlim(in6p, oifp);
49382cd038dSYoshinobu Inoue 
49482cd038dSYoshinobu Inoue 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 ||
49582cd038dSYoshinobu Inoue 	    in6p->in6p_cksum != -1) {
49682cd038dSYoshinobu Inoue 		struct mbuf *n;
49782cd038dSYoshinobu Inoue 		int off;
49882cd038dSYoshinobu Inoue 		u_int16_t *p;
49982cd038dSYoshinobu Inoue 
5000ae76120SRobert Watson 		/* Compute checksum. */
50182cd038dSYoshinobu Inoue 		if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
50282cd038dSYoshinobu Inoue 			off = offsetof(struct icmp6_hdr, icmp6_cksum);
50382cd038dSYoshinobu Inoue 		else
50482cd038dSYoshinobu Inoue 			off = in6p->in6p_cksum;
50582cd038dSYoshinobu Inoue 		if (plen < off + 1) {
50682cd038dSYoshinobu Inoue 			error = EINVAL;
50782cd038dSYoshinobu Inoue 			goto bad;
50882cd038dSYoshinobu Inoue 		}
50982cd038dSYoshinobu Inoue 		off += sizeof(struct ip6_hdr);
51082cd038dSYoshinobu Inoue 
51182cd038dSYoshinobu Inoue 		n = m;
51282cd038dSYoshinobu Inoue 		while (n && n->m_len <= off) {
51382cd038dSYoshinobu Inoue 			off -= n->m_len;
51482cd038dSYoshinobu Inoue 			n = n->m_next;
51582cd038dSYoshinobu Inoue 		}
51682cd038dSYoshinobu Inoue 		if (!n)
51782cd038dSYoshinobu Inoue 			goto bad;
51882cd038dSYoshinobu Inoue 		p = (u_int16_t *)(mtod(n, caddr_t) + off);
51982cd038dSYoshinobu Inoue 		*p = 0;
52082cd038dSYoshinobu Inoue 		*p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);
52182cd038dSYoshinobu Inoue 	}
52282cd038dSYoshinobu Inoue 
523e07db7aaSHajimu UMEMOTO 	error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p);
52482cd038dSYoshinobu Inoue 	if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
52582cd038dSYoshinobu Inoue 		if (oifp)
52682cd038dSYoshinobu Inoue 			icmp6_ifoutstat_inc(oifp, type, code);
527e27b0c87SRobert Watson 		ICMP6STAT_INC(icp6s_outhist[type]);
52833841545SHajimu UMEMOTO 	} else
529603724d3SBjoern A. Zeeb 		V_rip6stat.rip6s_opackets++;
53082cd038dSYoshinobu Inoue 
53182cd038dSYoshinobu Inoue 	goto freectl;
53282cd038dSYoshinobu Inoue 
53382cd038dSYoshinobu Inoue  bad:
53482cd038dSYoshinobu Inoue 	if (m)
53582cd038dSYoshinobu Inoue 		m_freem(m);
53682cd038dSYoshinobu Inoue 
53782cd038dSYoshinobu Inoue  freectl:
53875bab8b8SKip Macy 	if (control != NULL) {
539e07db7aaSHajimu UMEMOTO 		ip6_clearpktopts(&opt, -1);
54082cd038dSYoshinobu Inoue 		m_freem(control);
54133841545SHajimu UMEMOTO 	}
5428501a69cSRobert Watson 	INP_WUNLOCK(in6p);
54382cd038dSYoshinobu Inoue 	return (error);
54482cd038dSYoshinobu Inoue }
54582cd038dSYoshinobu Inoue 
54682cd038dSYoshinobu Inoue /*
54782cd038dSYoshinobu Inoue  * Raw IPv6 socket option processing.
54882cd038dSYoshinobu Inoue  */
54982cd038dSYoshinobu Inoue int
5501272577eSXin LI rip6_ctloutput(struct socket *so, struct sockopt *sopt)
55182cd038dSYoshinobu Inoue {
55282cd038dSYoshinobu Inoue 	int error;
55382cd038dSYoshinobu Inoue 
55482cd038dSYoshinobu Inoue 	if (sopt->sopt_level == IPPROTO_ICMPV6)
55582cd038dSYoshinobu Inoue 		/*
55682cd038dSYoshinobu Inoue 		 * XXX: is it better to call icmp6_ctloutput() directly
55782cd038dSYoshinobu Inoue 		 * from protosw?
55882cd038dSYoshinobu Inoue 		 */
55982cd038dSYoshinobu Inoue 		return (icmp6_ctloutput(so, sopt));
56082cd038dSYoshinobu Inoue 	else if (sopt->sopt_level != IPPROTO_IPV6)
56182cd038dSYoshinobu Inoue 		return (EINVAL);
56282cd038dSYoshinobu Inoue 
56382cd038dSYoshinobu Inoue 	error = 0;
56482cd038dSYoshinobu Inoue 
56582cd038dSYoshinobu Inoue 	switch (sopt->sopt_dir) {
56682cd038dSYoshinobu Inoue 	case SOPT_GET:
56782cd038dSYoshinobu Inoue 		switch (sopt->sopt_name) {
56891ec0a1eSYoshinobu Inoue 		case MRT6_INIT:
56991ec0a1eSYoshinobu Inoue 		case MRT6_DONE:
57091ec0a1eSYoshinobu Inoue 		case MRT6_ADD_MIF:
57191ec0a1eSYoshinobu Inoue 		case MRT6_DEL_MIF:
57291ec0a1eSYoshinobu Inoue 		case MRT6_ADD_MFC:
57391ec0a1eSYoshinobu Inoue 		case MRT6_DEL_MFC:
57491ec0a1eSYoshinobu Inoue 		case MRT6_PIM:
5756be2e366SBruce M Simpson 			error = ip6_mrouter_get ?  ip6_mrouter_get(so, sopt) :
5766be2e366SBruce M Simpson 			    EOPNOTSUPP;
57791ec0a1eSYoshinobu Inoue 			break;
57802b9a206SHajimu UMEMOTO 		case IPV6_CHECKSUM:
57902b9a206SHajimu UMEMOTO 			error = ip6_raw_ctloutput(so, sopt);
58002b9a206SHajimu UMEMOTO 			break;
58182cd038dSYoshinobu Inoue 		default:
58282cd038dSYoshinobu Inoue 			error = ip6_ctloutput(so, sopt);
58382cd038dSYoshinobu Inoue 			break;
58482cd038dSYoshinobu Inoue 		}
58582cd038dSYoshinobu Inoue 		break;
58682cd038dSYoshinobu Inoue 
58782cd038dSYoshinobu Inoue 	case SOPT_SET:
58882cd038dSYoshinobu Inoue 		switch (sopt->sopt_name) {
58991ec0a1eSYoshinobu Inoue 		case MRT6_INIT:
59091ec0a1eSYoshinobu Inoue 		case MRT6_DONE:
59191ec0a1eSYoshinobu Inoue 		case MRT6_ADD_MIF:
59291ec0a1eSYoshinobu Inoue 		case MRT6_DEL_MIF:
59391ec0a1eSYoshinobu Inoue 		case MRT6_ADD_MFC:
59491ec0a1eSYoshinobu Inoue 		case MRT6_DEL_MFC:
59591ec0a1eSYoshinobu Inoue 		case MRT6_PIM:
5966be2e366SBruce M Simpson 			error = ip6_mrouter_set ?  ip6_mrouter_set(so, sopt) :
5976be2e366SBruce M Simpson 			    EOPNOTSUPP;
59891ec0a1eSYoshinobu Inoue 			break;
59902b9a206SHajimu UMEMOTO 		case IPV6_CHECKSUM:
60002b9a206SHajimu UMEMOTO 			error = ip6_raw_ctloutput(so, sopt);
60102b9a206SHajimu UMEMOTO 			break;
60282cd038dSYoshinobu Inoue 		default:
60382cd038dSYoshinobu Inoue 			error = ip6_ctloutput(so, sopt);
60482cd038dSYoshinobu Inoue 			break;
60582cd038dSYoshinobu Inoue 		}
60682cd038dSYoshinobu Inoue 		break;
60782cd038dSYoshinobu Inoue 	}
60882cd038dSYoshinobu Inoue 
60982cd038dSYoshinobu Inoue 	return (error);
61082cd038dSYoshinobu Inoue }
61182cd038dSYoshinobu Inoue 
61282cd038dSYoshinobu Inoue static int
613b40ce416SJulian Elischer rip6_attach(struct socket *so, int proto, struct thread *td)
61482cd038dSYoshinobu Inoue {
6158b615593SMarko Zec 	INIT_VNET_INET(so->so_vnet);
61682cd038dSYoshinobu Inoue 	struct inpcb *inp;
6178a9d54dfSSam Leffler 	struct icmp6_filter *filter;
618ff7425ceSRobert Watson 	int error;
61982cd038dSYoshinobu Inoue 
62082cd038dSYoshinobu Inoue 	inp = sotoinpcb(so);
62114ba8addSRobert Watson 	KASSERT(inp == NULL, ("rip6_attach: inp != NULL"));
6220ae76120SRobert Watson 
62379ba3952SBjoern A. Zeeb 	error = priv_check(td, PRIV_NETINET_RAW);
62479ba3952SBjoern A. Zeeb 	if (error)
6250ae76120SRobert Watson 		return (error);
62682cd038dSYoshinobu Inoue 	error = soreserve(so, rip_sendspace, rip_recvspace);
62714ba8addSRobert Watson 	if (error)
6280ae76120SRobert Watson 		return (error);
6291ede983cSDag-Erling Smørgrav 	filter = malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT);
63014ba8addSRobert Watson 	if (filter == NULL)
6310ae76120SRobert Watson 		return (ENOMEM);
632603724d3SBjoern A. Zeeb 	INP_INFO_WLOCK(&V_ripcbinfo);
633603724d3SBjoern A. Zeeb 	error = in_pcballoc(so, &V_ripcbinfo);
63407385abdSRobert Watson 	if (error) {
635603724d3SBjoern A. Zeeb 		INP_INFO_WUNLOCK(&V_ripcbinfo);
6361ede983cSDag-Erling Smørgrav 		free(filter, M_PCB);
6370ae76120SRobert Watson 		return (error);
63807385abdSRobert Watson 	}
63982cd038dSYoshinobu Inoue 	inp = (struct inpcb *)so->so_pcb;
640603724d3SBjoern A. Zeeb 	INP_INFO_WUNLOCK(&V_ripcbinfo);
64182cd038dSYoshinobu Inoue 	inp->inp_vflag |= INP_IPV6;
642fc384fa5SBjoern A. Zeeb 	inp->inp_ip_p = (long)proto;
64382cd038dSYoshinobu Inoue 	inp->in6p_hops = -1;	/* use kernel default */
64482cd038dSYoshinobu Inoue 	inp->in6p_cksum = -1;
6458a9d54dfSSam Leffler 	inp->in6p_icmp6filt = filter;
64682cd038dSYoshinobu Inoue 	ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt);
6478501a69cSRobert Watson 	INP_WUNLOCK(inp);
6480ae76120SRobert Watson 	return (0);
64982cd038dSYoshinobu Inoue }
65082cd038dSYoshinobu Inoue 
651bc725eafSRobert Watson static void
65282cd038dSYoshinobu Inoue rip6_detach(struct socket *so)
65382cd038dSYoshinobu Inoue {
65433cde130SBruce M Simpson 	INIT_VNET_INET6(so->so_vnet);
65582cd038dSYoshinobu Inoue 	struct inpcb *inp;
65682cd038dSYoshinobu Inoue 
65782cd038dSYoshinobu Inoue 	inp = sotoinpcb(so);
65814ba8addSRobert Watson 	KASSERT(inp != NULL, ("rip6_detach: inp == NULL"));
659a152f8a3SRobert Watson 
66033cde130SBruce M Simpson 	if (so == V_ip6_mrouter && ip6_mrouter_done)
661e7e3ecb6SYoshinobu Inoue 		ip6_mrouter_done();
6626be2e366SBruce M Simpson 	/* xxx: RSVP */
663603724d3SBjoern A. Zeeb 	INP_INFO_WLOCK(&V_ripcbinfo);
6648501a69cSRobert Watson 	INP_WLOCK(inp);
6651ede983cSDag-Erling Smørgrav 	free(inp->in6p_icmp6filt, M_PCB);
6660206cdb8SBjoern A. Zeeb 	in_pcbdetach(inp);
6676aee2fc5SBjoern A. Zeeb 	in_pcbfree(inp);
668603724d3SBjoern A. Zeeb 	INP_INFO_WUNLOCK(&V_ripcbinfo);
66982cd038dSYoshinobu Inoue }
67082cd038dSYoshinobu Inoue 
671a152f8a3SRobert Watson /* XXXRW: This can't ever be called. */
672ac45e92fSRobert Watson static void
67382cd038dSYoshinobu Inoue rip6_abort(struct socket *so)
67482cd038dSYoshinobu Inoue {
675a152f8a3SRobert Watson 	struct inpcb *inp;
676a152f8a3SRobert Watson 
677a152f8a3SRobert Watson 	inp = sotoinpcb(so);
678a152f8a3SRobert Watson 	KASSERT(inp != NULL, ("rip6_abort: inp == NULL"));
679a152f8a3SRobert Watson 
68082cd038dSYoshinobu Inoue 	soisdisconnected(so);
681a152f8a3SRobert Watson }
682a152f8a3SRobert Watson 
683a152f8a3SRobert Watson static void
684a152f8a3SRobert Watson rip6_close(struct socket *so)
685a152f8a3SRobert Watson {
686a152f8a3SRobert Watson 	struct inpcb *inp;
687a152f8a3SRobert Watson 
688a152f8a3SRobert Watson 	inp = sotoinpcb(so);
689a152f8a3SRobert Watson 	KASSERT(inp != NULL, ("rip6_close: inp == NULL"));
690a152f8a3SRobert Watson 
691a152f8a3SRobert Watson 	soisdisconnected(so);
69282cd038dSYoshinobu Inoue }
69382cd038dSYoshinobu Inoue 
69482cd038dSYoshinobu Inoue static int
69582cd038dSYoshinobu Inoue rip6_disconnect(struct socket *so)
69682cd038dSYoshinobu Inoue {
6970ae76120SRobert Watson 	struct inpcb *inp;
6980ae76120SRobert Watson 
6990ae76120SRobert Watson 	inp = sotoinpcb(so);
7000ae76120SRobert Watson 	KASSERT(inp != NULL, ("rip6_disconnect: inp == NULL"));
70182cd038dSYoshinobu Inoue 
7024cc20ab1SSeigo Tanimura 	if ((so->so_state & SS_ISCONNECTED) == 0)
7030ae76120SRobert Watson 		return (ENOTCONN);
70482cd038dSYoshinobu Inoue 	inp->in6p_faddr = in6addr_any;
705ac45e92fSRobert Watson 	rip6_abort(so);
70614ba8addSRobert Watson 	return (0);
70782cd038dSYoshinobu Inoue }
70882cd038dSYoshinobu Inoue 
70982cd038dSYoshinobu Inoue static int
710b40ce416SJulian Elischer rip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
71182cd038dSYoshinobu Inoue {
7128b615593SMarko Zec 	INIT_VNET_NET(so->so_vnet);
7138b615593SMarko Zec 	INIT_VNET_INET(so->so_vnet);
7148b615593SMarko Zec 	INIT_VNET_INET6(so->so_vnet);
7150ae76120SRobert Watson 	struct inpcb *inp;
71682cd038dSYoshinobu Inoue 	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
71782cd038dSYoshinobu Inoue 	struct ifaddr *ia = NULL;
718a1f7e5f8SHajimu UMEMOTO 	int error = 0;
71982cd038dSYoshinobu Inoue 
7200ae76120SRobert Watson 	inp = sotoinpcb(so);
72114ba8addSRobert Watson 	KASSERT(inp != NULL, ("rip6_bind: inp == NULL"));
7220ae76120SRobert Watson 
72382cd038dSYoshinobu Inoue 	if (nam->sa_len != sizeof(*addr))
7240ae76120SRobert Watson 		return (EINVAL);
725b89e82ddSJamie Gritton 	if ((error = prison_check_ip6(td->td_ucred, &addr->sin6_addr)) != 0)
726b89e82ddSJamie Gritton 		return (error);
727603724d3SBjoern A. Zeeb 	if (TAILQ_EMPTY(&V_ifnet) || addr->sin6_family != AF_INET6)
7280ae76120SRobert Watson 		return (EADDRNOTAVAIL);
729603724d3SBjoern A. Zeeb 	if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0)
730a1f7e5f8SHajimu UMEMOTO 		return (error);
731a1f7e5f8SHajimu UMEMOTO 
73282cd038dSYoshinobu Inoue 	if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) &&
73382cd038dSYoshinobu Inoue 	    (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0)
7340ae76120SRobert Watson 		return (EADDRNOTAVAIL);
73582cd038dSYoshinobu Inoue 	if (ia &&
73682cd038dSYoshinobu Inoue 	    ((struct in6_ifaddr *)ia)->ia6_flags &
73782cd038dSYoshinobu Inoue 	    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|
73882cd038dSYoshinobu Inoue 	     IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) {
73982cd038dSYoshinobu Inoue 		return (EADDRNOTAVAIL);
74082cd038dSYoshinobu Inoue 	}
741603724d3SBjoern A. Zeeb 	INP_INFO_WLOCK(&V_ripcbinfo);
7428501a69cSRobert Watson 	INP_WLOCK(inp);
74382cd038dSYoshinobu Inoue 	inp->in6p_laddr = addr->sin6_addr;
7448501a69cSRobert Watson 	INP_WUNLOCK(inp);
745603724d3SBjoern A. Zeeb 	INP_INFO_WUNLOCK(&V_ripcbinfo);
7460ae76120SRobert Watson 	return (0);
74782cd038dSYoshinobu Inoue }
74882cd038dSYoshinobu Inoue 
74982cd038dSYoshinobu Inoue static int
750b40ce416SJulian Elischer rip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
75182cd038dSYoshinobu Inoue {
7528b615593SMarko Zec 	INIT_VNET_NET(so->so_vnet);
7538b615593SMarko Zec 	INIT_VNET_INET(so->so_vnet);
7548b615593SMarko Zec 	INIT_VNET_INET6(so->so_vnet);
7550ae76120SRobert Watson 	struct inpcb *inp;
75682cd038dSYoshinobu Inoue 	struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
75782cd038dSYoshinobu Inoue 	struct in6_addr *in6a = NULL;
758a1f7e5f8SHajimu UMEMOTO 	struct ifnet *ifp = NULL;
759a1f7e5f8SHajimu UMEMOTO 	int error = 0, scope_ambiguous = 0;
76082cd038dSYoshinobu Inoue 
7610ae76120SRobert Watson 	inp = sotoinpcb(so);
76214ba8addSRobert Watson 	KASSERT(inp != NULL, ("rip6_connect: inp == NULL"));
7630ae76120SRobert Watson 
76482cd038dSYoshinobu Inoue 	if (nam->sa_len != sizeof(*addr))
7650ae76120SRobert Watson 		return (EINVAL);
766603724d3SBjoern A. Zeeb 	if (TAILQ_EMPTY(&V_ifnet))
7670ae76120SRobert Watson 		return (EADDRNOTAVAIL);
76882cd038dSYoshinobu Inoue 	if (addr->sin6_family != AF_INET6)
7690ae76120SRobert Watson 		return (EAFNOSUPPORT);
770a1f7e5f8SHajimu UMEMOTO 
771a1f7e5f8SHajimu UMEMOTO 	/*
7720ae76120SRobert Watson 	 * Application should provide a proper zone ID or the use of default
7730ae76120SRobert Watson 	 * zone IDs should be enabled.  Unfortunately, some applications do
7740ae76120SRobert Watson 	 * not behave as it should, so we need a workaround.  Even if an
7750ae76120SRobert Watson 	 * appropriate ID is not determined, we'll see if we can determine
7760ae76120SRobert Watson 	 * the outgoing interface.  If we can, determine the zone ID based on
7770ae76120SRobert Watson 	 * the interface below.
778a1f7e5f8SHajimu UMEMOTO 	 */
779603724d3SBjoern A. Zeeb 	if (addr->sin6_scope_id == 0 && !V_ip6_use_defzone)
780a1f7e5f8SHajimu UMEMOTO 		scope_ambiguous = 1;
781603724d3SBjoern A. Zeeb 	if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0)
782a1f7e5f8SHajimu UMEMOTO 		return (error);
783a1f7e5f8SHajimu UMEMOTO 
784603724d3SBjoern A. Zeeb 	INP_INFO_WLOCK(&V_ripcbinfo);
7858501a69cSRobert Watson 	INP_WLOCK(inp);
78682cd038dSYoshinobu Inoue 	/* Source address selection. XXX: need pcblookup? */
78782cd038dSYoshinobu Inoue 	in6a = in6_selectsrc(addr, inp->in6p_outputopts,
788f2f877d3SBjoern A. Zeeb 			     inp, NULL, so->so_cred,
789f2f877d3SBjoern A. Zeeb 			     &ifp, &error);
79007385abdSRobert Watson 	if (in6a == NULL) {
7918501a69cSRobert Watson 		INP_WUNLOCK(inp);
792603724d3SBjoern A. Zeeb 		INP_INFO_WUNLOCK(&V_ripcbinfo);
79382cd038dSYoshinobu Inoue 		return (error ? error : EADDRNOTAVAIL);
79407385abdSRobert Watson 	}
795a1f7e5f8SHajimu UMEMOTO 
796a1f7e5f8SHajimu UMEMOTO 	/* XXX: see above */
797a1f7e5f8SHajimu UMEMOTO 	if (ifp && scope_ambiguous &&
798a1f7e5f8SHajimu UMEMOTO 	    (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
7998501a69cSRobert Watson 		INP_WUNLOCK(inp);
800603724d3SBjoern A. Zeeb 		INP_INFO_WUNLOCK(&V_ripcbinfo);
801a1f7e5f8SHajimu UMEMOTO 		return (error);
802a1f7e5f8SHajimu UMEMOTO 	}
80382cd038dSYoshinobu Inoue 	inp->in6p_faddr = addr->sin6_addr;
804a1f7e5f8SHajimu UMEMOTO 	inp->in6p_laddr = *in6a;
80582cd038dSYoshinobu Inoue 	soisconnected(so);
8068501a69cSRobert Watson 	INP_WUNLOCK(inp);
807603724d3SBjoern A. Zeeb 	INP_INFO_WUNLOCK(&V_ripcbinfo);
8080ae76120SRobert Watson 	return (0);
80982cd038dSYoshinobu Inoue }
81082cd038dSYoshinobu Inoue 
81182cd038dSYoshinobu Inoue static int
81282cd038dSYoshinobu Inoue rip6_shutdown(struct socket *so)
81382cd038dSYoshinobu Inoue {
81407385abdSRobert Watson 	struct inpcb *inp;
81507385abdSRobert Watson 
81607385abdSRobert Watson 	inp = sotoinpcb(so);
81714ba8addSRobert Watson 	KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL"));
8180ae76120SRobert Watson 
8198501a69cSRobert Watson 	INP_WLOCK(inp);
82082cd038dSYoshinobu Inoue 	socantsendmore(so);
8218501a69cSRobert Watson 	INP_WUNLOCK(inp);
8220ae76120SRobert Watson 	return (0);
82382cd038dSYoshinobu Inoue }
82482cd038dSYoshinobu Inoue 
82582cd038dSYoshinobu Inoue static int
82682cd038dSYoshinobu Inoue rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
827b40ce416SJulian Elischer     struct mbuf *control, struct thread *td)
82882cd038dSYoshinobu Inoue {
8290ae76120SRobert Watson 	struct inpcb *inp;
83082cd038dSYoshinobu Inoue 	struct sockaddr_in6 tmp;
83182cd038dSYoshinobu Inoue 	struct sockaddr_in6 *dst;
83207385abdSRobert Watson 	int ret;
83382cd038dSYoshinobu Inoue 
8340ae76120SRobert Watson 	inp = sotoinpcb(so);
83514ba8addSRobert Watson 	KASSERT(inp != NULL, ("rip6_send: inp == NULL"));
8360ae76120SRobert Watson 
8370ae76120SRobert Watson 	/* Always copy sockaddr to avoid overwrites. */
83807385abdSRobert Watson 	/* Unlocked read. */
83982cd038dSYoshinobu Inoue 	if (so->so_state & SS_ISCONNECTED) {
84082cd038dSYoshinobu Inoue 		if (nam) {
84182cd038dSYoshinobu Inoue 			m_freem(m);
8420ae76120SRobert Watson 			return (EISCONN);
84382cd038dSYoshinobu Inoue 		}
84482cd038dSYoshinobu Inoue 		/* XXX */
84582cd038dSYoshinobu Inoue 		bzero(&tmp, sizeof(tmp));
84682cd038dSYoshinobu Inoue 		tmp.sin6_family = AF_INET6;
84782cd038dSYoshinobu Inoue 		tmp.sin6_len = sizeof(struct sockaddr_in6);
8482209e8f1SRobert Watson 		INP_RLOCK(inp);
84982cd038dSYoshinobu Inoue 		bcopy(&inp->in6p_faddr, &tmp.sin6_addr,
85082cd038dSYoshinobu Inoue 		    sizeof(struct in6_addr));
8512209e8f1SRobert Watson 		INP_RUNLOCK(inp);
85282cd038dSYoshinobu Inoue 		dst = &tmp;
85382cd038dSYoshinobu Inoue 	} else {
85482cd038dSYoshinobu Inoue 		if (nam == NULL) {
85582cd038dSYoshinobu Inoue 			m_freem(m);
8560ae76120SRobert Watson 			return (ENOTCONN);
85782cd038dSYoshinobu Inoue 		}
858a1f7e5f8SHajimu UMEMOTO 		if (nam->sa_len != sizeof(struct sockaddr_in6)) {
859a1f7e5f8SHajimu UMEMOTO 			m_freem(m);
860a1f7e5f8SHajimu UMEMOTO 			return (EINVAL);
861a1f7e5f8SHajimu UMEMOTO 		}
862686cdd19SJun-ichiro itojun Hagino 		tmp = *(struct sockaddr_in6 *)nam;
863686cdd19SJun-ichiro itojun Hagino 		dst = &tmp;
864a1f7e5f8SHajimu UMEMOTO 
865a1f7e5f8SHajimu UMEMOTO 		if (dst->sin6_family == AF_UNSPEC) {
866a1f7e5f8SHajimu UMEMOTO 			/*
867a1f7e5f8SHajimu UMEMOTO 			 * XXX: we allow this case for backward
868a1f7e5f8SHajimu UMEMOTO 			 * compatibility to buggy applications that
869a1f7e5f8SHajimu UMEMOTO 			 * rely on old (and wrong) kernel behavior.
870a1f7e5f8SHajimu UMEMOTO 			 */
871a1f7e5f8SHajimu UMEMOTO 			log(LOG_INFO, "rip6 SEND: address family is "
872a1f7e5f8SHajimu UMEMOTO 			    "unspec. Assume AF_INET6\n");
873a1f7e5f8SHajimu UMEMOTO 			dst->sin6_family = AF_INET6;
874a1f7e5f8SHajimu UMEMOTO 		} else if (dst->sin6_family != AF_INET6) {
875a1f7e5f8SHajimu UMEMOTO 			m_freem(m);
876a1f7e5f8SHajimu UMEMOTO 			return(EAFNOSUPPORT);
87782cd038dSYoshinobu Inoue 		}
878686cdd19SJun-ichiro itojun Hagino 	}
87907385abdSRobert Watson 	ret = rip6_output(m, so, dst, control);
88007385abdSRobert Watson 	return (ret);
88182cd038dSYoshinobu Inoue }
88282cd038dSYoshinobu Inoue 
88382cd038dSYoshinobu Inoue struct pr_usrreqs rip6_usrreqs = {
884756d52a1SPoul-Henning Kamp 	.pru_abort =		rip6_abort,
885756d52a1SPoul-Henning Kamp 	.pru_attach =		rip6_attach,
886756d52a1SPoul-Henning Kamp 	.pru_bind =		rip6_bind,
887756d52a1SPoul-Henning Kamp 	.pru_connect =		rip6_connect,
888756d52a1SPoul-Henning Kamp 	.pru_control =		in6_control,
889756d52a1SPoul-Henning Kamp 	.pru_detach =		rip6_detach,
890756d52a1SPoul-Henning Kamp 	.pru_disconnect =	rip6_disconnect,
89154d642bbSRobert Watson 	.pru_peeraddr =		in6_getpeeraddr,
892756d52a1SPoul-Henning Kamp 	.pru_send =		rip6_send,
893756d52a1SPoul-Henning Kamp 	.pru_shutdown =		rip6_shutdown,
89454d642bbSRobert Watson 	.pru_sockaddr =		in6_getsockaddr,
895a152f8a3SRobert Watson 	.pru_close =		rip6_close,
89682cd038dSYoshinobu Inoue };
897