raw_ip6.c revision 144261
155714Skris/*- 255714Skris * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3238405Sjkim * All rights reserved. 4238405Sjkim * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 855714Skris * 1. Redistributions of source code must retain the above copyright 9160814Ssimon * notice, this list of conditions and the following disclaimer. 10160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright 11160814Ssimon * notice, this list of conditions and the following disclaimer in the 12160814Ssimon * documentation and/or other materials provided with the distribution. 13160814Ssimon * 3. Neither the name of the project nor the names of its contributors 1455714Skris * may be used to endorse or promote products derived from this software 1555714Skris * without specific prior written permission. 1655714Skris * 1755714Skris * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1855714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1955714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20160814Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2155714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2255714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2355714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2455714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2555714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2655714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2755714Skris * SUCH DAMAGE. 28238405Sjkim * 2955714Skris * $FreeBSD: head/sys/netinet6/raw_ip6.c 144261 2005-03-29 01:26:27Z sam $ 30238405Sjkim */ 31238405Sjkim 32238405Sjkim/*- 3355714Skris * Copyright (c) 1982, 1986, 1988, 1993 34160814Ssimon * The Regents of the University of California. All rights reserved. 35160814Ssimon * 36160814Ssimon * Redistribution and use in source and binary forms, with or without 37238405Sjkim * modification, are permitted provided that the following conditions 38160814Ssimon * are met: 39238405Sjkim * 1. Redistributions of source code must retain the above copyright 40238405Sjkim * notice, this list of conditions and the following disclaimer. 41238405Sjkim * 2. Redistributions in binary form must reproduce the above copyright 42238405Sjkim * notice, this list of conditions and the following disclaimer in the 43160814Ssimon * documentation and/or other materials provided with the distribution. 44238405Sjkim * 4. Neither the name of the University nor the names of its contributors 45238405Sjkim * may be used to endorse or promote products derived from this software 46238405Sjkim * without specific prior written permission. 47160814Ssimon * 48160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49160814Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50160814Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51160814Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52160814Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53160814Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54160814Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55160814Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56160814Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57160814Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58160814Ssimon * SUCH DAMAGE. 59160814Ssimon * 60160814Ssimon * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 61160814Ssimon */ 62160814Ssimon 63160814Ssimon#include "opt_ipsec.h" 64160814Ssimon#include "opt_inet6.h" 65160814Ssimon 66160814Ssimon#include <sys/param.h> 67160814Ssimon#include <sys/errno.h> 68160814Ssimon#include <sys/lock.h> 69160814Ssimon#include <sys/malloc.h> 70160814Ssimon#include <sys/mbuf.h> 71160814Ssimon#include <sys/proc.h> 72160814Ssimon#include <sys/protosw.h> 73160814Ssimon#include <sys/signalvar.h> 74160814Ssimon#include <sys/socket.h> 75160814Ssimon#include <sys/socketvar.h> 76160814Ssimon#include <sys/sx.h> 77160814Ssimon#include <sys/systm.h> 78160814Ssimon 79160814Ssimon#include <net/if.h> 80160814Ssimon#include <net/if_types.h> 81160814Ssimon#include <net/route.h> 82160814Ssimon 83160814Ssimon#include <netinet/in.h> 84160814Ssimon#include <netinet/in_var.h> 85160814Ssimon#include <netinet/in_systm.h> 86160814Ssimon#include <netinet/icmp6.h> 87160814Ssimon#include <netinet/in_pcb.h> 88160814Ssimon#include <netinet/ip6.h> 89160814Ssimon#include <netinet6/ip6protosw.h> 90160814Ssimon#include <netinet6/ip6_mroute.h> 91160814Ssimon#include <netinet6/in6_pcb.h> 92160814Ssimon#include <netinet6/ip6_var.h> 93160814Ssimon#include <netinet6/nd6.h> 94160814Ssimon#include <netinet6/raw_ip6.h> 95160814Ssimon#ifdef ENABLE_DEFAULT_SCOPE 96160814Ssimon#include <netinet6/scope6_var.h> 97160814Ssimon#endif 98160814Ssimon 99160814Ssimon#ifdef IPSEC 100160814Ssimon#include <netinet6/ipsec.h> 101160814Ssimon#include <netinet6/ipsec6.h> 102160814Ssimon#endif /*IPSEC*/ 103238405Sjkim 104160814Ssimon#ifdef FAST_IPSEC 105160814Ssimon#include <netipsec/ipsec.h> 106238405Sjkim#include <netipsec/ipsec6.h> 107238405Sjkim#endif /* FAST_IPSEC */ 108238405Sjkim 109238405Sjkim#include <machine/stdarg.h> 110238405Sjkim 111238405Sjkim#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 112238405Sjkim#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 113238405Sjkim 114238405Sjkim/* 115238405Sjkim * Raw interface to IP6 protocol. 116238405Sjkim */ 117238405Sjkim 118238405Sjkimextern struct inpcbhead ripcb; 119238405Sjkimextern struct inpcbinfo ripcbinfo; 120238405Sjkimextern u_long rip_sendspace; 121238405Sjkimextern u_long rip_recvspace; 122238405Sjkim 123160814Ssimonstruct rip6stat rip6stat; 124238405Sjkim 125238405Sjkim/* 126160814Ssimon * Setup generic address and protocol structures 127238405Sjkim * for raw_input routine, then pass them along with 128160814Ssimon * mbuf chain. 129238405Sjkim */ 130160814Ssimonint 131160814Ssimonrip6_input(mp, offp, proto) 132238405Sjkim struct mbuf **mp; 133238405Sjkim int *offp, proto; 134238405Sjkim{ 135238405Sjkim struct mbuf *m = *mp; 136238405Sjkim register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 13755714Skris register struct inpcb *in6p; 138238405Sjkim struct inpcb *last = 0; 139238405Sjkim struct mbuf *opts = NULL; 140238405Sjkim struct sockaddr_in6 fromsa; 141238405Sjkim 142238405Sjkim rip6stat.rip6s_ipackets++; 143238405Sjkim 144238405Sjkim if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 14555714Skris /* XXX send icmp6 host/port unreach? */ 146238405Sjkim m_freem(m); 147238405Sjkim return IPPROTO_DONE; 148238405Sjkim } 149238405Sjkim 150238405Sjkim init_sin6(&fromsa, m); /* general init */ 151238405Sjkim 152238405Sjkim INP_INFO_RLOCK(&ripcbinfo); 153238405Sjkim LIST_FOREACH(in6p, &ripcb, inp_list) { 154238405Sjkim INP_LOCK(in6p); 155238405Sjkim if ((in6p->in6p_vflag & INP_IPV6) == 0) { 156238405Sjkimdocontinue: 157238405Sjkim INP_UNLOCK(in6p); 158238405Sjkim continue; 159238405Sjkim } 160238405Sjkim if (in6p->in6p_ip6_nxt && 16155714Skris in6p->in6p_ip6_nxt != proto) 16255714Skris goto docontinue; 16355714Skris if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 16455714Skris !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 165238405Sjkim goto docontinue; 16655714Skris if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 167238405Sjkim !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 16855714Skris goto docontinue; 169238405Sjkim if (in6p->in6p_cksum != -1) { 17055714Skris rip6stat.rip6s_isum++; 171238405Sjkim if (in6_cksum(m, ip6->ip6_nxt, *offp, 17255714Skris m->m_pkthdr.len - *offp)) { 17355714Skris rip6stat.rip6s_badsum++; 17455714Skris goto docontinue; 17555714Skris } 17655714Skris } 177238405Sjkim if (last) { 178238405Sjkim struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 17955714Skris 18055714Skris#if defined(IPSEC) || defined(FAST_IPSEC) 18155714Skris /* 18255714Skris * Check AH/ESP integrity. 18355714Skris */ 18455714Skris if (n && ipsec6_in_reject(n, last)) { 18555714Skris m_freem(n); 18655714Skris#ifdef IPSEC 18755714Skris ipsec6stat.in_polvio++; 18855714Skris#endif /*IPSEC*/ 18955714Skris /* do not inject data into pcb */ 19055714Skris } else 191238405Sjkim#endif /*IPSEC || FAST_IPSEC*/ 19255714Skris if (n) { 19355714Skris if (last->in6p_flags & IN6P_CONTROLOPTS || 19455714Skris last->in6p_socket->so_options & SO_TIMESTAMP) 195238405Sjkim ip6_savecontrol(last, n, &opts); 19655714Skris /* strip intermediate headers */ 19755714Skris m_adj(n, *offp); 198238405Sjkim if (sbappendaddr(&last->in6p_socket->so_rcv, 199238405Sjkim (struct sockaddr *)&fromsa, 20055714Skris n, opts) == 0) { 20155714Skris m_freem(n); 20255714Skris if (opts) 20355714Skris m_freem(opts); 20455714Skris rip6stat.rip6s_fullsock++; 20555714Skris } else 20655714Skris sorwakeup(last->in6p_socket); 20755714Skris opts = NULL; 20855714Skris } 20955714Skris INP_UNLOCK(last); 21055714Skris } 21155714Skris last = in6p; 21255714Skris } 21355714Skris#if defined(IPSEC) || defined(FAST_IPSEC) 214238405Sjkim /* 21555714Skris * Check AH/ESP integrity. 216238405Sjkim */ 217238405Sjkim if (last && ipsec6_in_reject(m, last)) { 218238405Sjkim m_freem(m); 219238405Sjkim#ifdef IPSEC 220238405Sjkim ipsec6stat.in_polvio++; 221238405Sjkim#endif /*IPSEC*/ 222238405Sjkim ip6stat.ip6s_delivered--; 223238405Sjkim /* do not inject data into pcb */ 224238405Sjkim } else 225238405Sjkim#endif /*IPSEC || FAST_IPSEC*/ 226238405Sjkim if (last) { 227238405Sjkim if (last->in6p_flags & IN6P_CONTROLOPTS || 228238405Sjkim last->in6p_socket->so_options & SO_TIMESTAMP) 229238405Sjkim ip6_savecontrol(last, m, &opts); 230238405Sjkim /* strip intermediate headers */ 231238405Sjkim m_adj(m, *offp); 232238405Sjkim if (sbappendaddr(&last->in6p_socket->so_rcv, 233238405Sjkim (struct sockaddr *)&fromsa, m, opts) == 0) { 234238405Sjkim m_freem(m); 235238405Sjkim if (opts) 236238405Sjkim m_freem(opts); 237238405Sjkim rip6stat.rip6s_fullsock++; 238238405Sjkim } else 239238405Sjkim sorwakeup(last->in6p_socket); 240238405Sjkim INP_UNLOCK(last); 241238405Sjkim } else { 242238405Sjkim rip6stat.rip6s_nosock++; 243238405Sjkim if (m->m_flags & M_MCAST) 244238405Sjkim rip6stat.rip6s_nosockmcast++; 245238405Sjkim if (proto == IPPROTO_NONE) 246238405Sjkim m_freem(m); 247238405Sjkim else { 248238405Sjkim char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ 249238405Sjkim icmp6_error(m, ICMP6_PARAM_PROB, 250238405Sjkim ICMP6_PARAMPROB_NEXTHEADER, 251238405Sjkim prvnxtp - mtod(m, char *)); 252238405Sjkim } 253238405Sjkim ip6stat.ip6s_delivered--; 25455714Skris } 25555714Skris INP_INFO_RUNLOCK(&ripcbinfo); 25655714Skris return IPPROTO_DONE; 25755714Skris} 25855714Skris 25955714Skrisvoid 26055714Skrisrip6_ctlinput(cmd, sa, d) 26155714Skris int cmd; 26255714Skris struct sockaddr *sa; 26355714Skris void *d; 26455714Skris{ 26555714Skris struct ip6_hdr *ip6; 26655714Skris struct mbuf *m; 26755714Skris int off = 0; 26855714Skris struct ip6ctlparam *ip6cp = NULL; 26955714Skris const struct sockaddr_in6 *sa6_src = NULL; 27055714Skris void *cmdarg; 27155714Skris struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange; 27255714Skris 27355714Skris if (sa->sa_family != AF_INET6 || 27455714Skris sa->sa_len != sizeof(struct sockaddr_in6)) 27555714Skris return; 27655714Skris 27755714Skris if ((unsigned)cmd >= PRC_NCMDS) 27855714Skris return; 27955714Skris if (PRC_IS_REDIRECT(cmd)) 28055714Skris notify = in6_rtchange, d = NULL; 28155714Skris else if (cmd == PRC_HOSTDEAD) 28255714Skris d = NULL; 28355714Skris else if (inet6ctlerrmap[cmd] == 0) 28455714Skris return; 28555714Skris 28655714Skris /* if the parameter is from icmp6, decode it. */ 28755714Skris if (d != NULL) { 28855714Skris ip6cp = (struct ip6ctlparam *)d; 28955714Skris m = ip6cp->ip6c_m; 29055714Skris ip6 = ip6cp->ip6c_ip6; 29155714Skris off = ip6cp->ip6c_off; 29255714Skris cmdarg = ip6cp->ip6c_cmdarg; 29355714Skris sa6_src = ip6cp->ip6c_src; 29455714Skris } else { 29555714Skris m = NULL; 29655714Skris ip6 = NULL; 29755714Skris cmdarg = NULL; 29855714Skris sa6_src = &sa6_any; 29955714Skris } 30055714Skris 30155714Skris (void) in6_pcbnotify(&ripcbinfo, sa, 0, 30255714Skris (const struct sockaddr *)sa6_src, 30355714Skris 0, cmd, cmdarg, notify); 30455714Skris} 30555714Skris 30655714Skris/* 30755714Skris * Generate IPv6 header and pass packet to ip6_output. 30855714Skris * Tack on options user may have setup with control call. 30955714Skris */ 31055714Skrisint 31155714Skris#if __STDC__ 31255714Skrisrip6_output(struct mbuf *m, ...) 31355714Skris#else 31455714Skrisrip6_output(m, va_alist) 31555714Skris struct mbuf *m; 31655714Skris va_dcl 31755714Skris#endif 31855714Skris{ 31955714Skris struct mbuf *control; 32055714Skris struct socket *so; 32155714Skris struct sockaddr_in6 *dstsock; 32255714Skris struct in6_addr *dst; 32355714Skris struct ip6_hdr *ip6; 32455714Skris struct inpcb *in6p; 325238405Sjkim u_int plen = m->m_pkthdr.len; 32655714Skris int error = 0; 327238405Sjkim struct ip6_pktopts opt, *stickyopt = NULL; 328238405Sjkim struct ifnet *oifp = NULL; 329238405Sjkim int type = 0, code = 0; /* for ICMPv6 output statistics only */ 330238405Sjkim int priv = 0; 331238405Sjkim struct in6_addr *in6a; 332238405Sjkim va_list ap; 333238405Sjkim 334238405Sjkim va_start(ap, m); 335238405Sjkim so = va_arg(ap, struct socket *); 336238405Sjkim dstsock = va_arg(ap, struct sockaddr_in6 *); 337238405Sjkim control = va_arg(ap, struct mbuf *); 338238405Sjkim va_end(ap); 339238405Sjkim 340238405Sjkim in6p = sotoin6pcb(so); 341238405Sjkim INP_LOCK(in6p); 342238405Sjkim stickyopt = in6p->in6p_outputopts; 343238405Sjkim 344238405Sjkim priv = 0; 345238405Sjkim if (so->so_cred->cr_uid == 0) 346238405Sjkim priv = 1; 347238405Sjkim dst = &dstsock->sin6_addr; 348238405Sjkim if (control) { 349238405Sjkim if ((error = ip6_setpktoptions(control, &opt, 350238405Sjkim stickyopt, priv, 0, 351238405Sjkim so->so_proto->pr_protocol)) 352238405Sjkim != 0) { 353238405Sjkim goto bad; 354238405Sjkim } 355238405Sjkim in6p->in6p_outputopts = &opt; 356238405Sjkim } 357238405Sjkim 358238405Sjkim /* 359238405Sjkim * For an ICMPv6 packet, we should know its type and code 36055714Skris * to update statistics. 36155714Skris */ 36255714Skris if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 36355714Skris struct icmp6_hdr *icmp6; 36455714Skris if (m->m_len < sizeof(struct icmp6_hdr) && 36555714Skris (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 36655714Skris error = ENOBUFS; 36755714Skris goto bad; 36855714Skris } 36955714Skris icmp6 = mtod(m, struct icmp6_hdr *); 37055714Skris type = icmp6->icmp6_type; 37155714Skris code = icmp6->icmp6_code; 37255714Skris } 37355714Skris 37455714Skris M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 37555714Skris if (m == NULL) { 37655714Skris error = ENOBUFS; 37755714Skris goto bad; 37855714Skris } 37955714Skris ip6 = mtod(m, struct ip6_hdr *); 38055714Skris 38155714Skris /* 38255714Skris * Next header might not be ICMP6 but use its pseudo header anyway. 38355714Skris */ 38455714Skris ip6->ip6_dst = *dst; 38555714Skris 38655714Skris /* 38755714Skris * If the scope of the destination is link-local, embed the interface 38855714Skris * index in the address. 38955714Skris * 39055714Skris * XXX advanced-api value overrides sin6_scope_id 39155714Skris */ 39255714Skris if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 39355714Skris struct in6_pktinfo *pi; 39455714Skris 39555714Skris /* 39655714Skris * XXX Boundary check is assumed to be already done in 39755714Skris * ip6_setpktoptions(). 39855714Skris */ 39955714Skris if (in6p->in6p_outputopts && 40055714Skris (pi = in6p->in6p_outputopts->ip6po_pktinfo) && 40155714Skris pi->ipi6_ifindex) { 40255714Skris ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex); 40355714Skris oifp = ifnet_byindex(pi->ipi6_ifindex); 40455714Skris } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && 40555714Skris in6p->in6p_moptions && 40655714Skris in6p->in6p_moptions->im6o_multicast_ifp) { 40755714Skris oifp = in6p->in6p_moptions->im6o_multicast_ifp; 40855714Skris ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); 40955714Skris } else if (dstsock->sin6_scope_id) { 41055714Skris /* boundary check */ 41155714Skris if (dstsock->sin6_scope_id < 0 || 41255714Skris if_index < dstsock->sin6_scope_id) { 41355714Skris error = ENXIO; /* XXX EINVAL? */ 414238405Sjkim goto bad; 41555714Skris } 41655714Skris ip6->ip6_dst.s6_addr16[1] = 417238405Sjkim htons(dstsock->sin6_scope_id & 0xffff); /* XXX */ 418238405Sjkim } 419238405Sjkim } 420238405Sjkim 42155714Skris /* 42255714Skris * Source address selection. 42355714Skris */ 42455714Skris if ((in6a = in6_selectsrc(dstsock, in6p->in6p_outputopts, 42555714Skris in6p->in6p_moptions, NULL, &in6p->in6p_laddr, &error)) == 0) { 42655714Skris if (error == 0) 42755714Skris error = EADDRNOTAVAIL; 42855714Skris goto bad; 42955714Skris } 43055714Skris ip6->ip6_src = *in6a; 43155714Skris ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | 43255714Skris (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); 43355714Skris ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | 43455714Skris (IPV6_VERSION & IPV6_VERSION_MASK); 43555714Skris /* ip6_plen will be filled in ip6_output, so not fill it here. */ 43655714Skris ip6->ip6_nxt = in6p->in6p_ip6_nxt; 43755714Skris ip6->ip6_hlim = in6_selecthlim(in6p, oifp); 43855714Skris 43955714Skris if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 44055714Skris in6p->in6p_cksum != -1) { 44155714Skris struct mbuf *n; 44255714Skris int off; 44355714Skris u_int16_t *p; 44455714Skris 44555714Skris /* compute checksum */ 44655714Skris if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 44755714Skris off = offsetof(struct icmp6_hdr, icmp6_cksum); 44855714Skris else 44955714Skris off = in6p->in6p_cksum; 45055714Skris if (plen < off + 1) { 45155714Skris error = EINVAL; 45255714Skris goto bad; 45355714Skris } 45455714Skris off += sizeof(struct ip6_hdr); 45555714Skris 45655714Skris n = m; 45755714Skris while (n && n->m_len <= off) { 45855714Skris off -= n->m_len; 45955714Skris n = n->m_next; 46055714Skris } 46155714Skris if (!n) 46255714Skris goto bad; 46355714Skris p = (u_int16_t *)(mtod(n, caddr_t) + off); 46455714Skris *p = 0; 46555714Skris *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 46655714Skris } 46755714Skris 46855714Skris error = ip6_output(m, in6p->in6p_outputopts, NULL, 0, 46955714Skris in6p->in6p_moptions, &oifp, in6p); 47055714Skris if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 47155714Skris if (oifp) 47255714Skris icmp6_ifoutstat_inc(oifp, type, code); 47355714Skris icmp6stat.icp6s_outhist[type]++; 47455714Skris } else 47555714Skris rip6stat.rip6s_opackets++; 47655714Skris 47755714Skris goto freectl; 47855714Skris 47955714Skris bad: 48055714Skris if (m) 48155714Skris m_freem(m); 48255714Skris 48355714Skris freectl: 48455714Skris if (control) { 485109998Smarkm ip6_clearpktopts(in6p->in6p_outputopts, -1); 48655714Skris in6p->in6p_outputopts = stickyopt; 48755714Skris m_freem(control); 48855714Skris } 48955714Skris INP_UNLOCK(in6p); 49055714Skris return (error); 49155714Skris} 49255714Skris 49355714Skris/* 49455714Skris * Raw IPv6 socket option processing. 49555714Skris */ 49655714Skrisint 49755714Skrisrip6_ctloutput(so, sopt) 49855714Skris struct socket *so; 49955714Skris struct sockopt *sopt; 50055714Skris{ 50155714Skris int error; 50255714Skris 50355714Skris if (sopt->sopt_level == IPPROTO_ICMPV6) 50455714Skris /* 50555714Skris * XXX: is it better to call icmp6_ctloutput() directly 50655714Skris * from protosw? 50755714Skris */ 50855714Skris return (icmp6_ctloutput(so, sopt)); 50955714Skris else if (sopt->sopt_level != IPPROTO_IPV6) 51055714Skris return (EINVAL); 51155714Skris 51255714Skris error = 0; 51355714Skris 51455714Skris switch (sopt->sopt_dir) { 51555714Skris case SOPT_GET: 51655714Skris switch (sopt->sopt_name) { 51755714Skris case MRT6_INIT: 51855714Skris case MRT6_DONE: 51955714Skris case MRT6_ADD_MIF: 52055714Skris case MRT6_DEL_MIF: 52155714Skris case MRT6_ADD_MFC: 52255714Skris case MRT6_DEL_MFC: 52355714Skris case MRT6_PIM: 52455714Skris error = ip6_mrouter_get(so, sopt); 52555714Skris break; 52655714Skris case IPV6_CHECKSUM: 52755714Skris error = ip6_raw_ctloutput(so, sopt); 52855714Skris break; 52955714Skris default: 53055714Skris error = ip6_ctloutput(so, sopt); 53155714Skris break; 53255714Skris } 53355714Skris break; 53455714Skris 53555714Skris case SOPT_SET: 53655714Skris switch (sopt->sopt_name) { 53755714Skris case MRT6_INIT: 53855714Skris case MRT6_DONE: 53955714Skris case MRT6_ADD_MIF: 54055714Skris case MRT6_DEL_MIF: 54155714Skris case MRT6_ADD_MFC: 54255714Skris case MRT6_DEL_MFC: 54355714Skris case MRT6_PIM: 54455714Skris error = ip6_mrouter_set(so, sopt); 54555714Skris break; 54655714Skris case IPV6_CHECKSUM: 54755714Skris error = ip6_raw_ctloutput(so, sopt); 54855714Skris break; 54955714Skris default: 55055714Skris error = ip6_ctloutput(so, sopt); 55155714Skris break; 55255714Skris } 55355714Skris break; 55455714Skris } 55555714Skris 55655714Skris return (error); 557109998Smarkm} 55855714Skris 55955714Skrisstatic int 56055714Skrisrip6_attach(struct socket *so, int proto, struct thread *td) 56155714Skris{ 56255714Skris struct inpcb *inp; 56355714Skris struct icmp6_filter *filter; 56455714Skris int error, s; 56555714Skris 56655714Skris INP_INFO_WLOCK(&ripcbinfo); 567109998Smarkm inp = sotoinpcb(so); 568109998Smarkm if (inp) { 569109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 570109998Smarkm panic("rip6_attach"); 571109998Smarkm } 572109998Smarkm if (td && (error = suser(td)) != 0) { 573109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 574109998Smarkm return error; 575109998Smarkm } 576109998Smarkm error = soreserve(so, rip_sendspace, rip_recvspace); 577109998Smarkm if (error) { 578109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 579109998Smarkm return error; 580109998Smarkm } 581109998Smarkm MALLOC(filter, struct icmp6_filter *, 582109998Smarkm sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); 583109998Smarkm if (filter == NULL) 584109998Smarkm return ENOMEM; 585109998Smarkm s = splnet(); 586109998Smarkm error = in_pcballoc(so, &ripcbinfo, "raw6inp"); 587109998Smarkm splx(s); 588109998Smarkm if (error) { 589109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 590109998Smarkm FREE(filter, M_PCB); 591109998Smarkm return error; 592109998Smarkm } 593109998Smarkm inp = (struct inpcb *)so->so_pcb; 594109998Smarkm INP_LOCK(inp); 595109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 596109998Smarkm inp->inp_vflag |= INP_IPV6; 597109998Smarkm inp->in6p_ip6_nxt = (long)proto; 598109998Smarkm inp->in6p_hops = -1; /* use kernel default */ 599109998Smarkm inp->in6p_cksum = -1; 600109998Smarkm inp->in6p_icmp6filt = filter; 601109998Smarkm ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); 602109998Smarkm INP_UNLOCK(inp); 603109998Smarkm return 0; 604109998Smarkm} 605109998Smarkm 606109998Smarkmstatic int 607109998Smarkmrip6_detach(struct socket *so) 608109998Smarkm{ 609109998Smarkm struct inpcb *inp; 610109998Smarkm 611109998Smarkm INP_INFO_WLOCK(&ripcbinfo); 612109998Smarkm inp = sotoinpcb(so); 613109998Smarkm if (inp == 0) { 614109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 615109998Smarkm panic("rip6_detach"); 616109998Smarkm } 617109998Smarkm /* xxx: RSVP */ 618109998Smarkm if (so == ip6_mrouter) 619109998Smarkm ip6_mrouter_done(); 620109998Smarkm if (inp->in6p_icmp6filt) { 621109998Smarkm FREE(inp->in6p_icmp6filt, M_PCB); 622109998Smarkm inp->in6p_icmp6filt = NULL; 623109998Smarkm } 624109998Smarkm INP_LOCK(inp); 625109998Smarkm in6_pcbdetach(inp); 626109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 627109998Smarkm return 0; 628109998Smarkm} 629109998Smarkm 630109998Smarkmstatic int 631109998Smarkmrip6_abort(struct socket *so) 632109998Smarkm{ 633109998Smarkm soisdisconnected(so); 634109998Smarkm return rip6_detach(so); 635109998Smarkm} 636109998Smarkm 637109998Smarkmstatic int 638109998Smarkmrip6_disconnect(struct socket *so) 639109998Smarkm{ 640109998Smarkm struct inpcb *inp = sotoinpcb(so); 641109998Smarkm 642109998Smarkm if ((so->so_state & SS_ISCONNECTED) == 0) 643109998Smarkm return ENOTCONN; 644109998Smarkm inp->in6p_faddr = in6addr_any; 645109998Smarkm return rip6_abort(so); 646109998Smarkm} 647109998Smarkm 648109998Smarkmstatic int 649109998Smarkmrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 650109998Smarkm{ 651109998Smarkm struct inpcb *inp = sotoinpcb(so); 652109998Smarkm struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 653109998Smarkm struct ifaddr *ia = NULL; 654109998Smarkm 655109998Smarkm if (nam->sa_len != sizeof(*addr)) 656109998Smarkm return EINVAL; 657109998Smarkm if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) 658109998Smarkm return EADDRNOTAVAIL; 659109998Smarkm#ifdef ENABLE_DEFAULT_SCOPE 660109998Smarkm if (addr->sin6_scope_id == 0) { /* not change if specified */ 661109998Smarkm addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); 662109998Smarkm } 663109998Smarkm#endif 664109998Smarkm if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && 665109998Smarkm (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) 666109998Smarkm return EADDRNOTAVAIL; 667109998Smarkm if (ia && 668109998Smarkm ((struct in6_ifaddr *)ia)->ia6_flags & 669109998Smarkm (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| 670109998Smarkm IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { 671109998Smarkm return (EADDRNOTAVAIL); 672109998Smarkm } 673109998Smarkm INP_INFO_WLOCK(&ripcbinfo); 674109998Smarkm INP_LOCK(inp); 675109998Smarkm inp->in6p_laddr = addr->sin6_addr; 676109998Smarkm INP_UNLOCK(inp); 677109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 678109998Smarkm return 0; 679109998Smarkm} 680109998Smarkm 681109998Smarkmstatic int 682109998Smarkmrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 683109998Smarkm{ 684109998Smarkm struct inpcb *inp = sotoinpcb(so); 685109998Smarkm struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 686109998Smarkm struct in6_addr *in6a = NULL; 687109998Smarkm int error = 0; 688109998Smarkm#ifdef ENABLE_DEFAULT_SCOPE 689109998Smarkm struct sockaddr_in6 tmp; 690109998Smarkm#endif 691109998Smarkm 692109998Smarkm if (nam->sa_len != sizeof(*addr)) 693109998Smarkm return EINVAL; 694109998Smarkm if (TAILQ_EMPTY(&ifnet)) 695109998Smarkm return EADDRNOTAVAIL; 696109998Smarkm if (addr->sin6_family != AF_INET6) 697109998Smarkm return EAFNOSUPPORT; 698109998Smarkm#ifdef ENABLE_DEFAULT_SCOPE 699109998Smarkm if (addr->sin6_scope_id == 0) { /* not change if specified */ 700109998Smarkm /* avoid overwrites */ 701109998Smarkm tmp = *addr; 702109998Smarkm addr = &tmp; 703109998Smarkm addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); 704109998Smarkm } 705109998Smarkm#endif 706109998Smarkm INP_INFO_WLOCK(&ripcbinfo); 707109998Smarkm INP_LOCK(inp); 708109998Smarkm /* Source address selection. XXX: need pcblookup? */ 709109998Smarkm in6a = in6_selectsrc(addr, inp->in6p_outputopts, 710109998Smarkm inp->in6p_moptions, NULL, 711109998Smarkm &inp->in6p_laddr, &error); 712109998Smarkm if (in6a == NULL) { 713109998Smarkm INP_UNLOCK(inp); 714109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 715109998Smarkm return (error ? error : EADDRNOTAVAIL); 716109998Smarkm } 717109998Smarkm inp->in6p_laddr = *in6a; 718109998Smarkm inp->in6p_faddr = addr->sin6_addr; 719109998Smarkm soisconnected(so); 720109998Smarkm INP_UNLOCK(inp); 721109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 722109998Smarkm return 0; 723109998Smarkm} 724109998Smarkm 725109998Smarkmstatic int 726109998Smarkmrip6_shutdown(struct socket *so) 727109998Smarkm{ 728109998Smarkm struct inpcb *inp; 729109998Smarkm 730109998Smarkm INP_INFO_RLOCK(&ripcbinfo); 731109998Smarkm inp = sotoinpcb(so); 732109998Smarkm INP_LOCK(inp); 733109998Smarkm INP_INFO_RUNLOCK(&ripcbinfo); 734109998Smarkm socantsendmore(so); 735109998Smarkm INP_UNLOCK(inp); 736109998Smarkm return 0; 737109998Smarkm} 738109998Smarkm 739109998Smarkmstatic int 740109998Smarkmrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 741109998Smarkm struct mbuf *control, struct thread *td) 742109998Smarkm{ 743109998Smarkm struct inpcb *inp = sotoinpcb(so); 744109998Smarkm struct sockaddr_in6 tmp; 745109998Smarkm struct sockaddr_in6 *dst; 746109998Smarkm int ret; 747109998Smarkm 748109998Smarkm INP_INFO_WLOCK(&ripcbinfo); 749109998Smarkm /* always copy sockaddr to avoid overwrites */ 750109998Smarkm /* Unlocked read. */ 751109998Smarkm if (so->so_state & SS_ISCONNECTED) { 752109998Smarkm if (nam) { 753109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 754109998Smarkm m_freem(m); 755109998Smarkm return EISCONN; 756109998Smarkm } 757109998Smarkm /* XXX */ 758109998Smarkm bzero(&tmp, sizeof(tmp)); 759109998Smarkm tmp.sin6_family = AF_INET6; 760109998Smarkm tmp.sin6_len = sizeof(struct sockaddr_in6); 761109998Smarkm bcopy(&inp->in6p_faddr, &tmp.sin6_addr, 762109998Smarkm sizeof(struct in6_addr)); 763109998Smarkm dst = &tmp; 764109998Smarkm } else { 765109998Smarkm if (nam == NULL) { 766109998Smarkm INP_INFO_WUNLOCK(&ripcbinfo); 767109998Smarkm m_freem(m); 768109998Smarkm return ENOTCONN; 769109998Smarkm } 770109998Smarkm tmp = *(struct sockaddr_in6 *)nam; 771109998Smarkm dst = &tmp; 772109998Smarkm } 773109998Smarkm#ifdef ENABLE_DEFAULT_SCOPE 774109998Smarkm if (dst->sin6_scope_id == 0) { /* not change if specified */ 775 dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); 776 } 777#endif 778 ret = rip6_output(m, so, dst, control); 779 INP_INFO_WUNLOCK(&ripcbinfo); 780 return (ret); 781} 782 783struct pr_usrreqs rip6_usrreqs = { 784 .pru_abort = rip6_abort, 785 .pru_attach = rip6_attach, 786 .pru_bind = rip6_bind, 787 .pru_connect = rip6_connect, 788 .pru_control = in6_control, 789 .pru_detach = rip6_detach, 790 .pru_disconnect = rip6_disconnect, 791 .pru_peeraddr = in6_setpeeraddr, 792 .pru_send = rip6_send, 793 .pru_shutdown = rip6_shutdown, 794 .pru_sockaddr = in6_setsockaddr, 795}; 796