raw_ip6.c revision 190964
1139826Simp/*- 253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 353541Sshin * All rights reserved. 453541Sshin * 553541Sshin * Redistribution and use in source and binary forms, with or without 653541Sshin * modification, are permitted provided that the following conditions 753541Sshin * are met: 853541Sshin * 1. Redistributions of source code must retain the above copyright 953541Sshin * notice, this list of conditions and the following disclaimer. 1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1153541Sshin * notice, this list of conditions and the following disclaimer in the 1253541Sshin * documentation and/or other materials provided with the distribution. 1353541Sshin * 3. Neither the name of the project nor the names of its contributors 1453541Sshin * may be used to endorse or promote products derived from this software 1553541Sshin * without specific prior written permission. 1653541Sshin * 1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2053541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2753541Sshin * SUCH DAMAGE. 2853541Sshin */ 2953541Sshin 30139826Simp/*- 3153541Sshin * Copyright (c) 1982, 1986, 1988, 1993 32180305Srwatson * The Regents of the University of California. 33180305Srwatson * All rights reserved. 3453541Sshin * 3553541Sshin * Redistribution and use in source and binary forms, with or without 3653541Sshin * modification, are permitted provided that the following conditions 3753541Sshin * are met: 3853541Sshin * 1. Redistributions of source code must retain the above copyright 3953541Sshin * notice, this list of conditions and the following disclaimer. 4053541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4153541Sshin * notice, this list of conditions and the following disclaimer in the 4253541Sshin * documentation and/or other materials provided with the distribution. 4353541Sshin * 4. Neither the name of the University nor the names of its contributors 4453541Sshin * may be used to endorse or promote products derived from this software 4553541Sshin * without specific prior written permission. 4653541Sshin * 4753541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5053541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5753541Sshin * SUCH DAMAGE. 5853541Sshin * 5953541Sshin * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 6053541Sshin */ 6153541Sshin 62174510Sobrien#include <sys/cdefs.h> 63174510Sobrien__FBSDID("$FreeBSD: head/sys/netinet6/raw_ip6.c 190964 2009-04-12 13:22:33Z rwatson $"); 64174510Sobrien 6555009Sshin#include "opt_ipsec.h" 6678064Sume#include "opt_inet6.h" 67189106Sbz#include "opt_route.h" 6855009Sshin 6953541Sshin#include <sys/param.h> 7095759Stanimura#include <sys/errno.h> 71185435Sbz#include <sys/jail.h> 7295759Stanimura#include <sys/lock.h> 7353541Sshin#include <sys/malloc.h> 7495759Stanimura#include <sys/mbuf.h> 75170689Srwatson#include <sys/priv.h> 7653541Sshin#include <sys/proc.h> 7795759Stanimura#include <sys/protosw.h> 7895759Stanimura#include <sys/signalvar.h> 7953541Sshin#include <sys/socket.h> 8053541Sshin#include <sys/socketvar.h> 8195759Stanimura#include <sys/sx.h> 82148385Sume#include <sys/syslog.h> 83181803Sbz#include <sys/vimage.h> 8453541Sshin 8553541Sshin#include <net/if.h> 8695759Stanimura#include <net/if_types.h> 8753541Sshin#include <net/route.h> 88185571Sbz#include <net/vnet.h> 8953541Sshin 9053541Sshin#include <netinet/in.h> 9153541Sshin#include <netinet/in_var.h> 9253541Sshin#include <netinet/in_systm.h> 93185571Sbz#include <netinet/in_pcb.h> 94185571Sbz#include <netinet/vinet.h> 95185571Sbz 9695759Stanimura#include <netinet/icmp6.h> 9762587Sitojun#include <netinet/ip6.h> 9895759Stanimura#include <netinet6/ip6protosw.h> 9956723Sshin#include <netinet6/ip6_mroute.h> 10053541Sshin#include <netinet6/in6_pcb.h> 10195759Stanimura#include <netinet6/ip6_var.h> 10253541Sshin#include <netinet6/nd6.h> 10395759Stanimura#include <netinet6/raw_ip6.h> 10462587Sitojun#include <netinet6/scope6_var.h> 105185571Sbz#include <netinet6/vinet6.h> 10653541Sshin 107171167Sgnn#ifdef IPSEC 108105199Ssam#include <netipsec/ipsec.h> 109105199Ssam#include <netipsec/ipsec6.h> 110171167Sgnn#endif /* IPSEC */ 111105199Ssam 11253541Sshin#include <machine/stdarg.h> 11353541Sshin 11453541Sshin#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 11553541Sshin#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 11653541Sshin 11753541Sshin/* 11853541Sshin * Raw interface to IP6 protocol. 11953541Sshin */ 12053541Sshin 121185348Szec#ifdef VIMAGE_GLOBALS 12253541Sshinextern struct inpcbhead ripcb; 12353541Sshinextern struct inpcbinfo ripcbinfo; 124185348Szecstruct rip6stat rip6stat; 125185348Szec#endif 126185348Szec 12753541Sshinextern u_long rip_sendspace; 12853541Sshinextern u_long rip_recvspace; 12953541Sshin 13053541Sshin/* 131166938Sbms * Hooks for multicast forwarding. 132166938Sbms */ 133166948Sbmsstruct socket *ip6_mrouter = NULL; 134166938Sbmsint (*ip6_mrouter_set)(struct socket *, struct sockopt *); 135166938Sbmsint (*ip6_mrouter_get)(struct socket *, struct sockopt *); 136166938Sbmsint (*ip6_mrouter_done)(void); 137166938Sbmsint (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); 138166938Sbmsint (*mrt6_ioctl)(int, caddr_t); 139166938Sbms 140166938Sbms/* 141180305Srwatson * Setup generic address and protocol structures for raw_input routine, then 142180305Srwatson * pass them along with mbuf chain. 14353541Sshin */ 14453541Sshinint 145171259Sdelphijrip6_input(struct mbuf **mp, int *offp, int proto) 14653541Sshin{ 147183550Szec INIT_VNET_INET(curvnet); 148183550Szec INIT_VNET_INET6(curvnet); 149183550Szec#ifdef IPSEC 150183550Szec INIT_VNET_IPSEC(curvnet); 151183550Szec#endif 15253541Sshin struct mbuf *m = *mp; 15353541Sshin register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 15453541Sshin register struct inpcb *in6p; 15553541Sshin struct inpcb *last = 0; 15678064Sume struct mbuf *opts = NULL; 157121901Sume struct sockaddr_in6 fromsa; 15853541Sshin 159181803Sbz V_rip6stat.rip6s_ipackets++; 16078064Sume 16183934Sbrooks if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 162180305Srwatson /* XXX Send icmp6 host/port unreach? */ 16378064Sume m_freem(m); 164180305Srwatson return (IPPROTO_DONE); 16553541Sshin } 16678064Sume 167121901Sume init_sin6(&fromsa, m); /* general init */ 16853541Sshin 169181803Sbz INP_INFO_RLOCK(&V_ripcbinfo); 170181803Sbz LIST_FOREACH(in6p, &V_ripcb, inp_list) { 171185435Sbz /* XXX inp locking */ 172186141Sbz if ((in6p->inp_vflag & INP_IPV6) == 0) 17353541Sshin continue; 174186141Sbz if (in6p->inp_ip_p && 175186141Sbz in6p->inp_ip_p != proto) 176180850Smav continue; 17753541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 17853541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 179180850Smav continue; 18053541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 18153541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 182180850Smav continue; 183188144Sjamie if (prison_check_ip6(in6p->inp_cred, &ip6->ip6_dst) != 0) 184188144Sjamie continue; 185180932Smav INP_RLOCK(in6p); 18678064Sume if (in6p->in6p_cksum != -1) { 187181803Sbz V_rip6stat.rip6s_isum++; 188151459Ssuz if (in6_cksum(m, proto, *offp, 18978064Sume m->m_pkthdr.len - *offp)) { 190180932Smav INP_RUNLOCK(in6p); 191181803Sbz V_rip6stat.rip6s_badsum++; 192180850Smav continue; 19378064Sume } 19453541Sshin } 195186163Skmacy if (last != NULL) { 19653541Sshin struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 19778064Sume 198171167Sgnn#ifdef IPSEC 19978064Sume /* 20078064Sume * Check AH/ESP integrity. 20178064Sume */ 202125396Sume if (n && ipsec6_in_reject(n, last)) { 20378064Sume m_freem(n); 204181803Sbz V_ipsec6stat.in_polvio++; 205180305Srwatson /* Do not inject data into pcb. */ 206105199Ssam } else 207171167Sgnn#endif /* IPSEC */ 20853541Sshin if (n) { 209186223Sbz if (last->inp_flags & INP_CONTROLOPTS || 210186141Sbz last->inp_socket->so_options & SO_TIMESTAMP) 211121674Sume ip6_savecontrol(last, n, &opts); 21253541Sshin /* strip intermediate headers */ 21353541Sshin m_adj(n, *offp); 214186141Sbz if (sbappendaddr(&last->inp_socket->so_rcv, 215121901Sume (struct sockaddr *)&fromsa, 21653541Sshin n, opts) == 0) { 21753541Sshin m_freem(n); 21853541Sshin if (opts) 21953541Sshin m_freem(opts); 220181803Sbz V_rip6stat.rip6s_fullsock++; 22197658Stanimura } else 222186141Sbz sorwakeup(last->inp_socket); 22353541Sshin opts = NULL; 22453541Sshin } 225178377Srwatson INP_RUNLOCK(last); 22653541Sshin } 22753541Sshin last = in6p; 22853541Sshin } 229181803Sbz INP_INFO_RUNLOCK(&V_ripcbinfo); 230171167Sgnn#ifdef IPSEC 23178064Sume /* 23278064Sume * Check AH/ESP integrity. 23378064Sume */ 234186170Skmacy if ((last != NULL) && ipsec6_in_reject(m, last)) { 23578064Sume m_freem(m); 236181803Sbz V_ipsec6stat.in_polvio++; 237181803Sbz V_ip6stat.ip6s_delivered--; 238180305Srwatson /* Do not inject data into pcb. */ 239178377Srwatson INP_RUNLOCK(last); 240105199Ssam } else 241171167Sgnn#endif /* IPSEC */ 242186163Skmacy if (last != NULL) { 243186223Sbz if (last->inp_flags & INP_CONTROLOPTS || 244186141Sbz last->inp_socket->so_options & SO_TIMESTAMP) 245121674Sume ip6_savecontrol(last, m, &opts); 246180305Srwatson /* Strip intermediate headers. */ 24753541Sshin m_adj(m, *offp); 248186141Sbz if (sbappendaddr(&last->inp_socket->so_rcv, 249180305Srwatson (struct sockaddr *)&fromsa, m, opts) == 0) { 25053541Sshin m_freem(m); 25153541Sshin if (opts) 25253541Sshin m_freem(opts); 253181803Sbz V_rip6stat.rip6s_fullsock++; 25497658Stanimura } else 255186141Sbz sorwakeup(last->inp_socket); 256178377Srwatson INP_RUNLOCK(last); 25753541Sshin } else { 258181803Sbz V_rip6stat.rip6s_nosock++; 25978064Sume if (m->m_flags & M_MCAST) 260181803Sbz V_rip6stat.rip6s_nosockmcast++; 26153541Sshin if (proto == IPPROTO_NONE) 26253541Sshin m_freem(m); 26353541Sshin else { 26453541Sshin char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ 26553541Sshin icmp6_error(m, ICMP6_PARAM_PROB, 266180305Srwatson ICMP6_PARAMPROB_NEXTHEADER, 267180305Srwatson prvnxtp - mtod(m, char *)); 26853541Sshin } 269181803Sbz V_ip6stat.ip6s_delivered--; 27053541Sshin } 271180305Srwatson return (IPPROTO_DONE); 27253541Sshin} 27353541Sshin 27462587Sitojunvoid 275171259Sdelphijrip6_ctlinput(int cmd, struct sockaddr *sa, void *d) 27662587Sitojun{ 277183550Szec INIT_VNET_INET(curvnet); 27862587Sitojun struct ip6_hdr *ip6; 27962587Sitojun struct mbuf *m; 28062587Sitojun int off = 0; 28178064Sume struct ip6ctlparam *ip6cp = NULL; 28278064Sume const struct sockaddr_in6 *sa6_src = NULL; 283125776Sume void *cmdarg; 284175162Sobrien struct inpcb *(*notify)(struct inpcb *, int) = in6_rtchange; 28562587Sitojun 28662587Sitojun if (sa->sa_family != AF_INET6 || 28762587Sitojun sa->sa_len != sizeof(struct sockaddr_in6)) 28862587Sitojun return; 28962587Sitojun 29062587Sitojun if ((unsigned)cmd >= PRC_NCMDS) 29162587Sitojun return; 29262587Sitojun if (PRC_IS_REDIRECT(cmd)) 29362587Sitojun notify = in6_rtchange, d = NULL; 29462587Sitojun else if (cmd == PRC_HOSTDEAD) 29562587Sitojun d = NULL; 29662587Sitojun else if (inet6ctlerrmap[cmd] == 0) 29762587Sitojun return; 29862587Sitojun 299180305Srwatson /* 300180305Srwatson * If the parameter is from icmp6, decode it. 301180305Srwatson */ 30262587Sitojun if (d != NULL) { 30378064Sume ip6cp = (struct ip6ctlparam *)d; 30462587Sitojun m = ip6cp->ip6c_m; 30562587Sitojun ip6 = ip6cp->ip6c_ip6; 30662587Sitojun off = ip6cp->ip6c_off; 307125776Sume cmdarg = ip6cp->ip6c_cmdarg; 30878064Sume sa6_src = ip6cp->ip6c_src; 30962587Sitojun } else { 31062587Sitojun m = NULL; 31162587Sitojun ip6 = NULL; 312125776Sume cmdarg = NULL; 31378064Sume sa6_src = &sa6_any; 31462587Sitojun } 31562587Sitojun 316181803Sbz (void) in6_pcbnotify(&V_ripcbinfo, sa, 0, 317180305Srwatson (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); 31862587Sitojun} 31962587Sitojun 32053541Sshin/* 321180305Srwatson * Generate IPv6 header and pass packet to ip6_output. Tack on options user 322180305Srwatson * may have setup with control call. 32353541Sshin */ 32453541Sshinint 32553541Sshin#if __STDC__ 32653541Sshinrip6_output(struct mbuf *m, ...) 32753541Sshin#else 32853541Sshinrip6_output(m, va_alist) 32953541Sshin struct mbuf *m; 33053541Sshin va_dcl 33153541Sshin#endif 33253541Sshin{ 333183550Szec INIT_VNET_INET6(curvnet); 334120941Sume struct mbuf *control; 33553541Sshin struct socket *so; 33653541Sshin struct sockaddr_in6 *dstsock; 33753541Sshin struct in6_addr *dst; 33853541Sshin struct ip6_hdr *ip6; 33953541Sshin struct inpcb *in6p; 34053541Sshin u_int plen = m->m_pkthdr.len; 34153541Sshin int error = 0; 342148247Sume struct ip6_pktopts opt, *optp; 34353541Sshin struct ifnet *oifp = NULL; 34453541Sshin int type = 0, code = 0; /* for ICMPv6 output statistics only */ 345148385Sume int scope_ambiguous = 0; 346121472Sume struct in6_addr *in6a; 34753541Sshin va_list ap; 34853541Sshin 34953541Sshin va_start(ap, m); 35053541Sshin so = va_arg(ap, struct socket *); 35153541Sshin dstsock = va_arg(ap, struct sockaddr_in6 *); 35253541Sshin control = va_arg(ap, struct mbuf *); 35353541Sshin va_end(ap); 35453541Sshin 355186141Sbz in6p = sotoinpcb(so); 356178285Srwatson INP_WLOCK(in6p); 35753541Sshin 35853541Sshin dst = &dstsock->sin6_addr; 359186170Skmacy if (control != NULL) { 360148242Sume if ((error = ip6_setpktopts(control, &opt, 361175630Sbz in6p->in6p_outputopts, so->so_cred, 362175630Sbz so->so_proto->pr_protocol)) != 0) { 36353541Sshin goto bad; 364121472Sume } 365148247Sume optp = &opt; 366148247Sume } else 367148247Sume optp = in6p->in6p_outputopts; 36853541Sshin 36953541Sshin /* 370148385Sume * Check and convert scope zone ID into internal form. 371180305Srwatson * 372148385Sume * XXX: we may still need to determine the zone later. 373148385Sume */ 374148385Sume if (!(so->so_state & SS_ISCONNECTED)) { 375181803Sbz if (dstsock->sin6_scope_id == 0 && !V_ip6_use_defzone) 376148385Sume scope_ambiguous = 1; 377181803Sbz if ((error = sa6_embedscope(dstsock, V_ip6_use_defzone)) != 0) 378148385Sume goto bad; 379148385Sume } 380148385Sume 381148385Sume /* 382180305Srwatson * For an ICMPv6 packet, we should know its type and code to update 383180305Srwatson * statistics. 38453541Sshin */ 38553541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 38653541Sshin struct icmp6_hdr *icmp6; 38753541Sshin if (m->m_len < sizeof(struct icmp6_hdr) && 38853541Sshin (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 38953541Sshin error = ENOBUFS; 39053541Sshin goto bad; 39153541Sshin } 39253541Sshin icmp6 = mtod(m, struct icmp6_hdr *); 39353541Sshin type = icmp6->icmp6_type; 39453541Sshin code = icmp6->icmp6_code; 39553541Sshin } 39653541Sshin 397133592Srwatson M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 398133592Srwatson if (m == NULL) { 399133592Srwatson error = ENOBUFS; 400133592Srwatson goto bad; 401133592Srwatson } 40253541Sshin ip6 = mtod(m, struct ip6_hdr *); 40353541Sshin 40453541Sshin /* 40553541Sshin * Source address selection. 40653541Sshin */ 407180371Sbz if ((in6a = in6_selectsrc(dstsock, optp, in6p, NULL, so->so_cred, 408180371Sbz &oifp, &error)) == NULL) { 409121472Sume if (error == 0) 410121472Sume error = EADDRNOTAVAIL; 411121472Sume goto bad; 41253541Sshin } 413188144Sjamie error = prison_get_ip6(in6p->inp_cred, in6a); 414188144Sjamie if (error != 0) 415188144Sjamie goto bad; 416121472Sume ip6->ip6_src = *in6a; 417148385Sume 418148385Sume if (oifp && scope_ambiguous) { 419148385Sume /* 420148385Sume * Application should provide a proper zone ID or the use of 421148385Sume * default zone IDs should be enabled. Unfortunately, some 422148385Sume * applications do not behave as it should, so we need a 423148385Sume * workaround. Even if an appropriate ID is not determined 424148385Sume * (when it's required), if we can determine the outgoing 425148385Sume * interface. determine the zone ID based on the interface. 426148385Sume */ 427148385Sume error = in6_setscope(&dstsock->sin6_addr, oifp, NULL); 428148385Sume if (error != 0) 429148385Sume goto bad; 430148385Sume } 431148385Sume ip6->ip6_dst = dstsock->sin6_addr; 432148385Sume 433180305Srwatson /* 434180305Srwatson * Fill in the rest of the IPv6 header fields. 435180305Srwatson */ 43655009Sshin ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | 437186141Sbz (in6p->inp_flow & IPV6_FLOWINFO_MASK); 43855009Sshin ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | 439180305Srwatson (IPV6_VERSION & IPV6_VERSION_MASK); 440180305Srwatson 441180305Srwatson /* 442180305Srwatson * ip6_plen will be filled in ip6_output, so not fill it here. 443180305Srwatson */ 444186141Sbz ip6->ip6_nxt = in6p->inp_ip_p; 44553541Sshin ip6->ip6_hlim = in6_selecthlim(in6p, oifp); 44653541Sshin 44753541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 44853541Sshin in6p->in6p_cksum != -1) { 44953541Sshin struct mbuf *n; 45053541Sshin int off; 45153541Sshin u_int16_t *p; 45253541Sshin 453180305Srwatson /* Compute checksum. */ 45453541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 45553541Sshin off = offsetof(struct icmp6_hdr, icmp6_cksum); 45653541Sshin else 45753541Sshin off = in6p->in6p_cksum; 45853541Sshin if (plen < off + 1) { 45953541Sshin error = EINVAL; 46053541Sshin goto bad; 46153541Sshin } 46253541Sshin off += sizeof(struct ip6_hdr); 46353541Sshin 46453541Sshin n = m; 46553541Sshin while (n && n->m_len <= off) { 46653541Sshin off -= n->m_len; 46753541Sshin n = n->m_next; 46853541Sshin } 46953541Sshin if (!n) 47053541Sshin goto bad; 47153541Sshin p = (u_int16_t *)(mtod(n, caddr_t) + off); 47253541Sshin *p = 0; 47353541Sshin *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 47453541Sshin } 47553541Sshin 476148247Sume error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); 47753541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 47853541Sshin if (oifp) 47953541Sshin icmp6_ifoutstat_inc(oifp, type, code); 480190964Srwatson ICMP6STAT_INC(icp6s_outhist[type]); 48178064Sume } else 482181803Sbz V_rip6stat.rip6s_opackets++; 48353541Sshin 48453541Sshin goto freectl; 48553541Sshin 48653541Sshin bad: 48753541Sshin if (m) 48853541Sshin m_freem(m); 48953541Sshin 49053541Sshin freectl: 491186170Skmacy if (control != NULL) { 492148247Sume ip6_clearpktopts(&opt, -1); 49353541Sshin m_freem(control); 49478064Sume } 495178285Srwatson INP_WUNLOCK(in6p); 496120856Sume return (error); 49753541Sshin} 49853541Sshin 49953541Sshin/* 50053541Sshin * Raw IPv6 socket option processing. 50153541Sshin */ 50253541Sshinint 503171259Sdelphijrip6_ctloutput(struct socket *so, struct sockopt *sopt) 50453541Sshin{ 50553541Sshin int error; 50653541Sshin 50753541Sshin if (sopt->sopt_level == IPPROTO_ICMPV6) 50853541Sshin /* 50953541Sshin * XXX: is it better to call icmp6_ctloutput() directly 51053541Sshin * from protosw? 51153541Sshin */ 512120856Sume return (icmp6_ctloutput(so, sopt)); 51353541Sshin else if (sopt->sopt_level != IPPROTO_IPV6) 51453541Sshin return (EINVAL); 51553541Sshin 51653541Sshin error = 0; 51753541Sshin 51853541Sshin switch (sopt->sopt_dir) { 51953541Sshin case SOPT_GET: 52053541Sshin switch (sopt->sopt_name) { 52156723Sshin case MRT6_INIT: 52256723Sshin case MRT6_DONE: 52356723Sshin case MRT6_ADD_MIF: 52456723Sshin case MRT6_DEL_MIF: 52556723Sshin case MRT6_ADD_MFC: 52656723Sshin case MRT6_DEL_MFC: 52756723Sshin case MRT6_PIM: 528166938Sbms error = ip6_mrouter_get ? ip6_mrouter_get(so, sopt) : 529166938Sbms EOPNOTSUPP; 53056723Sshin break; 531121578Sume case IPV6_CHECKSUM: 532121578Sume error = ip6_raw_ctloutput(so, sopt); 533121578Sume break; 53453541Sshin default: 53553541Sshin error = ip6_ctloutput(so, sopt); 53653541Sshin break; 53753541Sshin } 53853541Sshin break; 53953541Sshin 54053541Sshin case SOPT_SET: 54153541Sshin switch (sopt->sopt_name) { 54256723Sshin case MRT6_INIT: 54356723Sshin case MRT6_DONE: 54456723Sshin case MRT6_ADD_MIF: 54556723Sshin case MRT6_DEL_MIF: 54656723Sshin case MRT6_ADD_MFC: 54756723Sshin case MRT6_DEL_MFC: 54856723Sshin case MRT6_PIM: 549166938Sbms error = ip6_mrouter_set ? ip6_mrouter_set(so, sopt) : 550166938Sbms EOPNOTSUPP; 55156723Sshin break; 552121578Sume case IPV6_CHECKSUM: 553121578Sume error = ip6_raw_ctloutput(so, sopt); 554121578Sume break; 55553541Sshin default: 55653541Sshin error = ip6_ctloutput(so, sopt); 55753541Sshin break; 55853541Sshin } 55953541Sshin break; 56053541Sshin } 56153541Sshin 56253541Sshin return (error); 56353541Sshin} 56453541Sshin 56553541Sshinstatic int 56683366Sjulianrip6_attach(struct socket *so, int proto, struct thread *td) 56753541Sshin{ 568183550Szec INIT_VNET_INET(so->so_vnet); 56953541Sshin struct inpcb *inp; 570144261Ssam struct icmp6_filter *filter; 571157676Srwatson int error; 57253541Sshin 57353541Sshin inp = sotoinpcb(so); 574157374Srwatson KASSERT(inp == NULL, ("rip6_attach: inp != NULL")); 575180305Srwatson 576175630Sbz error = priv_check(td, PRIV_NETINET_RAW); 577175630Sbz if (error) 578180305Srwatson return (error); 57955009Sshin error = soreserve(so, rip_sendspace, rip_recvspace); 580157374Srwatson if (error) 581180305Srwatson return (error); 582184214Sdes filter = malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); 583157374Srwatson if (filter == NULL) 584180305Srwatson return (ENOMEM); 585181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 586181803Sbz error = in_pcballoc(so, &V_ripcbinfo); 587132714Srwatson if (error) { 588181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 589184205Sdes free(filter, M_PCB); 590180305Srwatson return (error); 591132714Srwatson } 59253541Sshin inp = (struct inpcb *)so->so_pcb; 593181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 59453541Sshin inp->inp_vflag |= INP_IPV6; 595186141Sbz inp->inp_ip_p = (long)proto; 59653541Sshin inp->in6p_hops = -1; /* use kernel default */ 59753541Sshin inp->in6p_cksum = -1; 598144261Ssam inp->in6p_icmp6filt = filter; 59953541Sshin ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); 600178285Srwatson INP_WUNLOCK(inp); 601180305Srwatson return (0); 60253541Sshin} 60353541Sshin 604157370Srwatsonstatic void 60553541Sshinrip6_detach(struct socket *so) 60653541Sshin{ 607183550Szec INIT_VNET_INET(so->so_vnet); 60853541Sshin struct inpcb *inp; 60953541Sshin 61053541Sshin inp = sotoinpcb(so); 611157374Srwatson KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); 612160549Srwatson 613166938Sbms if (so == ip6_mrouter && ip6_mrouter_done) 614166938Sbms ip6_mrouter_done(); 61553541Sshin /* xxx: RSVP */ 616181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 617178285Srwatson INP_WLOCK(inp); 618184205Sdes free(inp->in6p_icmp6filt, M_PCB); 619185344Sbz in_pcbdetach(inp); 620185370Sbz in_pcbfree(inp); 621181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 62253541Sshin} 62353541Sshin 624160549Srwatson/* XXXRW: This can't ever be called. */ 625157366Srwatsonstatic void 62653541Sshinrip6_abort(struct socket *so) 62753541Sshin{ 628160549Srwatson struct inpcb *inp; 629160549Srwatson 630160549Srwatson inp = sotoinpcb(so); 631160549Srwatson KASSERT(inp != NULL, ("rip6_abort: inp == NULL")); 632160549Srwatson 63353541Sshin soisdisconnected(so); 63453541Sshin} 63553541Sshin 636160549Srwatsonstatic void 637160549Srwatsonrip6_close(struct socket *so) 638160549Srwatson{ 639160549Srwatson struct inpcb *inp; 640160549Srwatson 641160549Srwatson inp = sotoinpcb(so); 642160549Srwatson KASSERT(inp != NULL, ("rip6_close: inp == NULL")); 643160549Srwatson 644160549Srwatson soisdisconnected(so); 645160549Srwatson} 646160549Srwatson 64753541Sshinstatic int 64853541Sshinrip6_disconnect(struct socket *so) 64953541Sshin{ 650180305Srwatson struct inpcb *inp; 65153541Sshin 652180305Srwatson inp = sotoinpcb(so); 653180305Srwatson KASSERT(inp != NULL, ("rip6_disconnect: inp == NULL")); 654180305Srwatson 65597658Stanimura if ((so->so_state & SS_ISCONNECTED) == 0) 656180305Srwatson return (ENOTCONN); 65753541Sshin inp->in6p_faddr = in6addr_any; 658157366Srwatson rip6_abort(so); 659157374Srwatson return (0); 66053541Sshin} 66153541Sshin 66253541Sshinstatic int 66383366Sjulianrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 66453541Sshin{ 665183550Szec INIT_VNET_NET(so->so_vnet); 666183550Szec INIT_VNET_INET(so->so_vnet); 667183550Szec INIT_VNET_INET6(so->so_vnet); 668180305Srwatson struct inpcb *inp; 66953541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 67053541Sshin struct ifaddr *ia = NULL; 671148385Sume int error = 0; 67253541Sshin 673180305Srwatson inp = sotoinpcb(so); 674157374Srwatson KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); 675180305Srwatson 67653541Sshin if (nam->sa_len != sizeof(*addr)) 677180305Srwatson return (EINVAL); 678188144Sjamie if ((error = prison_check_ip6(td->td_ucred, &addr->sin6_addr)) != 0) 679188144Sjamie return (error); 680181803Sbz if (TAILQ_EMPTY(&V_ifnet) || addr->sin6_family != AF_INET6) 681180305Srwatson return (EADDRNOTAVAIL); 682181803Sbz if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) 683180305Srwatson return (error); 684148385Sume 68553541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && 68653541Sshin (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) 687180305Srwatson return (EADDRNOTAVAIL); 68853541Sshin if (ia && 68953541Sshin ((struct in6_ifaddr *)ia)->ia6_flags & 69053541Sshin (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| 69153541Sshin IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { 692120856Sume return (EADDRNOTAVAIL); 69353541Sshin } 694181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 695178285Srwatson INP_WLOCK(inp); 69653541Sshin inp->in6p_laddr = addr->sin6_addr; 697178285Srwatson INP_WUNLOCK(inp); 698181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 699180305Srwatson return (0); 70053541Sshin} 70153541Sshin 70253541Sshinstatic int 70383366Sjulianrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 70453541Sshin{ 705183550Szec INIT_VNET_NET(so->so_vnet); 706183550Szec INIT_VNET_INET(so->so_vnet); 707183550Szec INIT_VNET_INET6(so->so_vnet); 708180305Srwatson struct inpcb *inp; 70953541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 71053541Sshin struct in6_addr *in6a = NULL; 711148385Sume struct ifnet *ifp = NULL; 712148385Sume int error = 0, scope_ambiguous = 0; 71353541Sshin 714180305Srwatson inp = sotoinpcb(so); 715157374Srwatson KASSERT(inp != NULL, ("rip6_connect: inp == NULL")); 716180305Srwatson 71753541Sshin if (nam->sa_len != sizeof(*addr)) 718180305Srwatson return (EINVAL); 719181803Sbz if (TAILQ_EMPTY(&V_ifnet)) 720180305Srwatson return (EADDRNOTAVAIL); 72153541Sshin if (addr->sin6_family != AF_INET6) 722180305Srwatson return (EAFNOSUPPORT); 723148385Sume 724148385Sume /* 725180305Srwatson * Application should provide a proper zone ID or the use of default 726180305Srwatson * zone IDs should be enabled. Unfortunately, some applications do 727180305Srwatson * not behave as it should, so we need a workaround. Even if an 728180305Srwatson * appropriate ID is not determined, we'll see if we can determine 729180305Srwatson * the outgoing interface. If we can, determine the zone ID based on 730180305Srwatson * the interface below. 731148385Sume */ 732181803Sbz if (addr->sin6_scope_id == 0 && !V_ip6_use_defzone) 733148385Sume scope_ambiguous = 1; 734181803Sbz if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) 735180305Srwatson return (error); 736148385Sume 737181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 738178285Srwatson INP_WLOCK(inp); 73953541Sshin /* Source address selection. XXX: need pcblookup? */ 74053541Sshin in6a = in6_selectsrc(addr, inp->in6p_outputopts, 741180371Sbz inp, NULL, so->so_cred, 742180371Sbz &ifp, &error); 743132714Srwatson if (in6a == NULL) { 744178285Srwatson INP_WUNLOCK(inp); 745181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 74653541Sshin return (error ? error : EADDRNOTAVAIL); 747132714Srwatson } 748148385Sume 749148385Sume /* XXX: see above */ 750148385Sume if (ifp && scope_ambiguous && 751148385Sume (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) { 752178285Srwatson INP_WUNLOCK(inp); 753181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 754180305Srwatson return (error); 755148385Sume } 756148385Sume inp->in6p_faddr = addr->sin6_addr; 75753541Sshin inp->in6p_laddr = *in6a; 75853541Sshin soisconnected(so); 759178285Srwatson INP_WUNLOCK(inp); 760181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 761180305Srwatson return (0); 76253541Sshin} 76353541Sshin 76453541Sshinstatic int 76553541Sshinrip6_shutdown(struct socket *so) 76653541Sshin{ 767132714Srwatson struct inpcb *inp; 768132714Srwatson 769132714Srwatson inp = sotoinpcb(so); 770157374Srwatson KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL")); 771180305Srwatson 772178285Srwatson INP_WLOCK(inp); 77353541Sshin socantsendmore(so); 774178285Srwatson INP_WUNLOCK(inp); 775180305Srwatson return (0); 77653541Sshin} 77753541Sshin 77853541Sshinstatic int 77953541Sshinrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 780171260Sdelphij struct mbuf *control, struct thread *td) 78153541Sshin{ 782180305Srwatson struct inpcb *inp; 78353541Sshin struct sockaddr_in6 tmp; 78453541Sshin struct sockaddr_in6 *dst; 785132714Srwatson int ret; 78653541Sshin 787180305Srwatson inp = sotoinpcb(so); 788157374Srwatson KASSERT(inp != NULL, ("rip6_send: inp == NULL")); 789180305Srwatson 790180305Srwatson /* Always copy sockaddr to avoid overwrites. */ 791132714Srwatson /* Unlocked read. */ 79253541Sshin if (so->so_state & SS_ISCONNECTED) { 79353541Sshin if (nam) { 79453541Sshin m_freem(m); 795180305Srwatson return (EISCONN); 79653541Sshin } 79753541Sshin /* XXX */ 79853541Sshin bzero(&tmp, sizeof(tmp)); 79953541Sshin tmp.sin6_family = AF_INET6; 80053541Sshin tmp.sin6_len = sizeof(struct sockaddr_in6); 801180990Srwatson INP_RLOCK(inp); 80253541Sshin bcopy(&inp->in6p_faddr, &tmp.sin6_addr, 803180990Srwatson sizeof(struct in6_addr)); 804180990Srwatson INP_RUNLOCK(inp); 80553541Sshin dst = &tmp; 80653541Sshin } else { 80753541Sshin if (nam == NULL) { 80853541Sshin m_freem(m); 809180305Srwatson return (ENOTCONN); 81053541Sshin } 811148385Sume if (nam->sa_len != sizeof(struct sockaddr_in6)) { 812148385Sume m_freem(m); 813180305Srwatson return (EINVAL); 814148385Sume } 81562587Sitojun tmp = *(struct sockaddr_in6 *)nam; 81662587Sitojun dst = &tmp; 817148385Sume 818148385Sume if (dst->sin6_family == AF_UNSPEC) { 819148385Sume /* 820148385Sume * XXX: we allow this case for backward 821148385Sume * compatibility to buggy applications that 822148385Sume * rely on old (and wrong) kernel behavior. 823148385Sume */ 824148385Sume log(LOG_INFO, "rip6 SEND: address family is " 825148385Sume "unspec. Assume AF_INET6\n"); 826148385Sume dst->sin6_family = AF_INET6; 827148385Sume } else if (dst->sin6_family != AF_INET6) { 828148385Sume m_freem(m); 829148385Sume return(EAFNOSUPPORT); 830148385Sume } 83153541Sshin } 832132714Srwatson ret = rip6_output(m, so, dst, control); 833132714Srwatson return (ret); 83453541Sshin} 83553541Sshin 83653541Sshinstruct pr_usrreqs rip6_usrreqs = { 837137386Sphk .pru_abort = rip6_abort, 838137386Sphk .pru_attach = rip6_attach, 839137386Sphk .pru_bind = rip6_bind, 840137386Sphk .pru_connect = rip6_connect, 841137386Sphk .pru_control = in6_control, 842137386Sphk .pru_detach = rip6_detach, 843137386Sphk .pru_disconnect = rip6_disconnect, 844169462Srwatson .pru_peeraddr = in6_getpeeraddr, 845137386Sphk .pru_send = rip6_send, 846137386Sphk .pru_shutdown = rip6_shutdown, 847169462Srwatson .pru_sockaddr = in6_getsockaddr, 848160549Srwatson .pru_close = rip6_close, 84953541Sshin}; 850