raw_ip6.c revision 186223
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 186223 2008-12-17 13:00:18Z bz $"); 64174510Sobrien 6555009Sshin#include "opt_ipsec.h" 6678064Sume#include "opt_inet6.h" 6755009Sshin 6853541Sshin#include <sys/param.h> 6995759Stanimura#include <sys/errno.h> 70185435Sbz#include <sys/jail.h> 7195759Stanimura#include <sys/lock.h> 7253541Sshin#include <sys/malloc.h> 7395759Stanimura#include <sys/mbuf.h> 74170689Srwatson#include <sys/priv.h> 7553541Sshin#include <sys/proc.h> 7695759Stanimura#include <sys/protosw.h> 7795759Stanimura#include <sys/signalvar.h> 7853541Sshin#include <sys/socket.h> 7953541Sshin#include <sys/socketvar.h> 8095759Stanimura#include <sys/sx.h> 81148385Sume#include <sys/syslog.h> 82181803Sbz#include <sys/vimage.h> 8353541Sshin 8453541Sshin#include <net/if.h> 8595759Stanimura#include <net/if_types.h> 8653541Sshin#include <net/route.h> 87185571Sbz#include <net/vnet.h> 8853541Sshin 8953541Sshin#include <netinet/in.h> 9053541Sshin#include <netinet/in_var.h> 9153541Sshin#include <netinet/in_systm.h> 92185571Sbz#include <netinet/in_pcb.h> 93185571Sbz#include <netinet/vinet.h> 94185571Sbz 9595759Stanimura#include <netinet/icmp6.h> 9662587Sitojun#include <netinet/ip6.h> 9795759Stanimura#include <netinet6/ip6protosw.h> 9856723Sshin#include <netinet6/ip6_mroute.h> 9953541Sshin#include <netinet6/in6_pcb.h> 10095759Stanimura#include <netinet6/ip6_var.h> 10153541Sshin#include <netinet6/nd6.h> 10295759Stanimura#include <netinet6/raw_ip6.h> 10362587Sitojun#include <netinet6/scope6_var.h> 104185571Sbz#include <netinet6/vinet6.h> 10553541Sshin 106171167Sgnn#ifdef IPSEC 107105199Ssam#include <netipsec/ipsec.h> 108105199Ssam#include <netipsec/ipsec6.h> 109171167Sgnn#endif /* IPSEC */ 110105199Ssam 11153541Sshin#include <machine/stdarg.h> 11253541Sshin 11353541Sshin#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 11453541Sshin#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 11553541Sshin 11653541Sshin/* 11753541Sshin * Raw interface to IP6 protocol. 11853541Sshin */ 11953541Sshin 120185348Szec#ifdef VIMAGE_GLOBALS 12153541Sshinextern struct inpcbhead ripcb; 12253541Sshinextern struct inpcbinfo ripcbinfo; 123185348Szecstruct rip6stat rip6stat; 124185348Szec#endif 125185348Szec 12653541Sshinextern u_long rip_sendspace; 12753541Sshinextern u_long rip_recvspace; 12853541Sshin 12953541Sshin/* 130166938Sbms * Hooks for multicast forwarding. 131166938Sbms */ 132166948Sbmsstruct socket *ip6_mrouter = NULL; 133166938Sbmsint (*ip6_mrouter_set)(struct socket *, struct sockopt *); 134166938Sbmsint (*ip6_mrouter_get)(struct socket *, struct sockopt *); 135166938Sbmsint (*ip6_mrouter_done)(void); 136166938Sbmsint (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); 137166938Sbmsint (*mrt6_ioctl)(int, caddr_t); 138166938Sbms 139166938Sbms/* 140180305Srwatson * Setup generic address and protocol structures for raw_input routine, then 141180305Srwatson * pass them along with mbuf chain. 14253541Sshin */ 14353541Sshinint 144171259Sdelphijrip6_input(struct mbuf **mp, int *offp, int proto) 14553541Sshin{ 146183550Szec INIT_VNET_INET(curvnet); 147183550Szec INIT_VNET_INET6(curvnet); 148183550Szec#ifdef IPSEC 149183550Szec INIT_VNET_IPSEC(curvnet); 150183550Szec#endif 15153541Sshin struct mbuf *m = *mp; 15253541Sshin register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 15353541Sshin register struct inpcb *in6p; 15453541Sshin struct inpcb *last = 0; 15578064Sume struct mbuf *opts = NULL; 156121901Sume struct sockaddr_in6 fromsa; 15753541Sshin 158181803Sbz V_rip6stat.rip6s_ipackets++; 15978064Sume 16083934Sbrooks if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 161180305Srwatson /* XXX Send icmp6 host/port unreach? */ 16278064Sume m_freem(m); 163180305Srwatson return (IPPROTO_DONE); 16453541Sshin } 16578064Sume 166121901Sume init_sin6(&fromsa, m); /* general init */ 16753541Sshin 168181803Sbz INP_INFO_RLOCK(&V_ripcbinfo); 169181803Sbz LIST_FOREACH(in6p, &V_ripcb, inp_list) { 170185435Sbz /* XXX inp locking */ 171186141Sbz if ((in6p->inp_vflag & INP_IPV6) == 0) 17253541Sshin continue; 173186141Sbz if (in6p->inp_ip_p && 174186141Sbz in6p->inp_ip_p != proto) 175180850Smav continue; 17653541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 17753541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 178180850Smav continue; 17953541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 18053541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 181180850Smav continue; 182185435Sbz if (jailed(in6p->inp_cred)) { 183185435Sbz if (!prison_check_ip6(in6p->inp_cred, &ip6->ip6_dst)) 184185435Sbz continue; 185185435Sbz } 186180932Smav INP_RLOCK(in6p); 18778064Sume if (in6p->in6p_cksum != -1) { 188181803Sbz V_rip6stat.rip6s_isum++; 189151459Ssuz if (in6_cksum(m, proto, *offp, 19078064Sume m->m_pkthdr.len - *offp)) { 191180932Smav INP_RUNLOCK(in6p); 192181803Sbz V_rip6stat.rip6s_badsum++; 193180850Smav continue; 19478064Sume } 19553541Sshin } 196186163Skmacy if (last != NULL) { 19753541Sshin struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 19878064Sume 199171167Sgnn#ifdef IPSEC 20078064Sume /* 20178064Sume * Check AH/ESP integrity. 20278064Sume */ 203125396Sume if (n && ipsec6_in_reject(n, last)) { 20478064Sume m_freem(n); 205181803Sbz V_ipsec6stat.in_polvio++; 206180305Srwatson /* Do not inject data into pcb. */ 207105199Ssam } else 208171167Sgnn#endif /* IPSEC */ 20953541Sshin if (n) { 210186223Sbz if (last->inp_flags & INP_CONTROLOPTS || 211186141Sbz last->inp_socket->so_options & SO_TIMESTAMP) 212121674Sume ip6_savecontrol(last, n, &opts); 21353541Sshin /* strip intermediate headers */ 21453541Sshin m_adj(n, *offp); 215186141Sbz if (sbappendaddr(&last->inp_socket->so_rcv, 216121901Sume (struct sockaddr *)&fromsa, 21753541Sshin n, opts) == 0) { 21853541Sshin m_freem(n); 21953541Sshin if (opts) 22053541Sshin m_freem(opts); 221181803Sbz V_rip6stat.rip6s_fullsock++; 22297658Stanimura } else 223186141Sbz sorwakeup(last->inp_socket); 22453541Sshin opts = NULL; 22553541Sshin } 226178377Srwatson INP_RUNLOCK(last); 22753541Sshin } 22853541Sshin last = in6p; 22953541Sshin } 230181803Sbz INP_INFO_RUNLOCK(&V_ripcbinfo); 231171167Sgnn#ifdef IPSEC 23278064Sume /* 23378064Sume * Check AH/ESP integrity. 23478064Sume */ 235186170Skmacy if ((last != NULL) && ipsec6_in_reject(m, last)) { 23678064Sume m_freem(m); 237181803Sbz V_ipsec6stat.in_polvio++; 238181803Sbz V_ip6stat.ip6s_delivered--; 239180305Srwatson /* Do not inject data into pcb. */ 240178377Srwatson INP_RUNLOCK(last); 241105199Ssam } else 242171167Sgnn#endif /* IPSEC */ 243186163Skmacy if (last != NULL) { 244186223Sbz if (last->inp_flags & INP_CONTROLOPTS || 245186141Sbz last->inp_socket->so_options & SO_TIMESTAMP) 246121674Sume ip6_savecontrol(last, m, &opts); 247180305Srwatson /* Strip intermediate headers. */ 24853541Sshin m_adj(m, *offp); 249186141Sbz if (sbappendaddr(&last->inp_socket->so_rcv, 250180305Srwatson (struct sockaddr *)&fromsa, m, opts) == 0) { 25153541Sshin m_freem(m); 25253541Sshin if (opts) 25353541Sshin m_freem(opts); 254181803Sbz V_rip6stat.rip6s_fullsock++; 25597658Stanimura } else 256186141Sbz sorwakeup(last->inp_socket); 257178377Srwatson INP_RUNLOCK(last); 25853541Sshin } else { 259181803Sbz V_rip6stat.rip6s_nosock++; 26078064Sume if (m->m_flags & M_MCAST) 261181803Sbz V_rip6stat.rip6s_nosockmcast++; 26253541Sshin if (proto == IPPROTO_NONE) 26353541Sshin m_freem(m); 26453541Sshin else { 26553541Sshin char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ 26653541Sshin icmp6_error(m, ICMP6_PARAM_PROB, 267180305Srwatson ICMP6_PARAMPROB_NEXTHEADER, 268180305Srwatson prvnxtp - mtod(m, char *)); 26953541Sshin } 270181803Sbz V_ip6stat.ip6s_delivered--; 27153541Sshin } 272180305Srwatson return (IPPROTO_DONE); 27353541Sshin} 27453541Sshin 27562587Sitojunvoid 276171259Sdelphijrip6_ctlinput(int cmd, struct sockaddr *sa, void *d) 27762587Sitojun{ 278183550Szec INIT_VNET_INET(curvnet); 27962587Sitojun struct ip6_hdr *ip6; 28062587Sitojun struct mbuf *m; 28162587Sitojun int off = 0; 28278064Sume struct ip6ctlparam *ip6cp = NULL; 28378064Sume const struct sockaddr_in6 *sa6_src = NULL; 284125776Sume void *cmdarg; 285175162Sobrien struct inpcb *(*notify)(struct inpcb *, int) = in6_rtchange; 28662587Sitojun 28762587Sitojun if (sa->sa_family != AF_INET6 || 28862587Sitojun sa->sa_len != sizeof(struct sockaddr_in6)) 28962587Sitojun return; 29062587Sitojun 29162587Sitojun if ((unsigned)cmd >= PRC_NCMDS) 29262587Sitojun return; 29362587Sitojun if (PRC_IS_REDIRECT(cmd)) 29462587Sitojun notify = in6_rtchange, d = NULL; 29562587Sitojun else if (cmd == PRC_HOSTDEAD) 29662587Sitojun d = NULL; 29762587Sitojun else if (inet6ctlerrmap[cmd] == 0) 29862587Sitojun return; 29962587Sitojun 300180305Srwatson /* 301180305Srwatson * If the parameter is from icmp6, decode it. 302180305Srwatson */ 30362587Sitojun if (d != NULL) { 30478064Sume ip6cp = (struct ip6ctlparam *)d; 30562587Sitojun m = ip6cp->ip6c_m; 30662587Sitojun ip6 = ip6cp->ip6c_ip6; 30762587Sitojun off = ip6cp->ip6c_off; 308125776Sume cmdarg = ip6cp->ip6c_cmdarg; 30978064Sume sa6_src = ip6cp->ip6c_src; 31062587Sitojun } else { 31162587Sitojun m = NULL; 31262587Sitojun ip6 = NULL; 313125776Sume cmdarg = NULL; 31478064Sume sa6_src = &sa6_any; 31562587Sitojun } 31662587Sitojun 317181803Sbz (void) in6_pcbnotify(&V_ripcbinfo, sa, 0, 318180305Srwatson (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); 31962587Sitojun} 32062587Sitojun 32153541Sshin/* 322180305Srwatson * Generate IPv6 header and pass packet to ip6_output. Tack on options user 323180305Srwatson * may have setup with control call. 32453541Sshin */ 32553541Sshinint 32653541Sshin#if __STDC__ 32753541Sshinrip6_output(struct mbuf *m, ...) 32853541Sshin#else 32953541Sshinrip6_output(m, va_alist) 33053541Sshin struct mbuf *m; 33153541Sshin va_dcl 33253541Sshin#endif 33353541Sshin{ 334183550Szec INIT_VNET_INET6(curvnet); 335120941Sume struct mbuf *control; 33653541Sshin struct socket *so; 33753541Sshin struct sockaddr_in6 *dstsock; 33853541Sshin struct in6_addr *dst; 33953541Sshin struct ip6_hdr *ip6; 34053541Sshin struct inpcb *in6p; 34153541Sshin u_int plen = m->m_pkthdr.len; 34253541Sshin int error = 0; 343148247Sume struct ip6_pktopts opt, *optp; 34453541Sshin struct ifnet *oifp = NULL; 34553541Sshin int type = 0, code = 0; /* for ICMPv6 output statistics only */ 346148385Sume int scope_ambiguous = 0; 347121472Sume struct in6_addr *in6a; 34853541Sshin va_list ap; 34953541Sshin 35053541Sshin va_start(ap, m); 35153541Sshin so = va_arg(ap, struct socket *); 35253541Sshin dstsock = va_arg(ap, struct sockaddr_in6 *); 35353541Sshin control = va_arg(ap, struct mbuf *); 35453541Sshin va_end(ap); 35553541Sshin 356186141Sbz in6p = sotoinpcb(so); 357178285Srwatson INP_WLOCK(in6p); 35853541Sshin 35953541Sshin dst = &dstsock->sin6_addr; 360186170Skmacy if (control != NULL) { 361148242Sume if ((error = ip6_setpktopts(control, &opt, 362175630Sbz in6p->in6p_outputopts, so->so_cred, 363175630Sbz so->so_proto->pr_protocol)) != 0) { 36453541Sshin goto bad; 365121472Sume } 366148247Sume optp = &opt; 367148247Sume } else 368148247Sume optp = in6p->in6p_outputopts; 36953541Sshin 37053541Sshin /* 371148385Sume * Check and convert scope zone ID into internal form. 372180305Srwatson * 373148385Sume * XXX: we may still need to determine the zone later. 374148385Sume */ 375148385Sume if (!(so->so_state & SS_ISCONNECTED)) { 376181803Sbz if (dstsock->sin6_scope_id == 0 && !V_ip6_use_defzone) 377148385Sume scope_ambiguous = 1; 378181803Sbz if ((error = sa6_embedscope(dstsock, V_ip6_use_defzone)) != 0) 379148385Sume goto bad; 380148385Sume } 381148385Sume 382148385Sume /* 383180305Srwatson * For an ICMPv6 packet, we should know its type and code to update 384180305Srwatson * statistics. 38553541Sshin */ 38653541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 38753541Sshin struct icmp6_hdr *icmp6; 38853541Sshin if (m->m_len < sizeof(struct icmp6_hdr) && 38953541Sshin (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 39053541Sshin error = ENOBUFS; 39153541Sshin goto bad; 39253541Sshin } 39353541Sshin icmp6 = mtod(m, struct icmp6_hdr *); 39453541Sshin type = icmp6->icmp6_type; 39553541Sshin code = icmp6->icmp6_code; 39653541Sshin } 39753541Sshin 398133592Srwatson M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 399133592Srwatson if (m == NULL) { 400133592Srwatson error = ENOBUFS; 401133592Srwatson goto bad; 402133592Srwatson } 40353541Sshin ip6 = mtod(m, struct ip6_hdr *); 40453541Sshin 40553541Sshin /* 40653541Sshin * Source address selection. 40753541Sshin */ 408180371Sbz if ((in6a = in6_selectsrc(dstsock, optp, in6p, NULL, so->so_cred, 409180371Sbz &oifp, &error)) == NULL) { 410121472Sume if (error == 0) 411121472Sume error = EADDRNOTAVAIL; 412121472Sume goto bad; 41353541Sshin } 414185435Sbz if (jailed(in6p->inp_cred)) 415185435Sbz if (prison_getip6(in6p->inp_cred, in6a) != 0) { 416185435Sbz error = EPERM; 417185435Sbz goto bad; 418185435Sbz } 419121472Sume ip6->ip6_src = *in6a; 420148385Sume 421148385Sume if (oifp && scope_ambiguous) { 422148385Sume /* 423148385Sume * Application should provide a proper zone ID or the use of 424148385Sume * default zone IDs should be enabled. Unfortunately, some 425148385Sume * applications do not behave as it should, so we need a 426148385Sume * workaround. Even if an appropriate ID is not determined 427148385Sume * (when it's required), if we can determine the outgoing 428148385Sume * interface. determine the zone ID based on the interface. 429148385Sume */ 430148385Sume error = in6_setscope(&dstsock->sin6_addr, oifp, NULL); 431148385Sume if (error != 0) 432148385Sume goto bad; 433148385Sume } 434148385Sume ip6->ip6_dst = dstsock->sin6_addr; 435148385Sume 436180305Srwatson /* 437180305Srwatson * Fill in the rest of the IPv6 header fields. 438180305Srwatson */ 43955009Sshin ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | 440186141Sbz (in6p->inp_flow & IPV6_FLOWINFO_MASK); 44155009Sshin ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | 442180305Srwatson (IPV6_VERSION & IPV6_VERSION_MASK); 443180305Srwatson 444180305Srwatson /* 445180305Srwatson * ip6_plen will be filled in ip6_output, so not fill it here. 446180305Srwatson */ 447186141Sbz ip6->ip6_nxt = in6p->inp_ip_p; 44853541Sshin ip6->ip6_hlim = in6_selecthlim(in6p, oifp); 44953541Sshin 45053541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 45153541Sshin in6p->in6p_cksum != -1) { 45253541Sshin struct mbuf *n; 45353541Sshin int off; 45453541Sshin u_int16_t *p; 45553541Sshin 456180305Srwatson /* Compute checksum. */ 45753541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 45853541Sshin off = offsetof(struct icmp6_hdr, icmp6_cksum); 45953541Sshin else 46053541Sshin off = in6p->in6p_cksum; 46153541Sshin if (plen < off + 1) { 46253541Sshin error = EINVAL; 46353541Sshin goto bad; 46453541Sshin } 46553541Sshin off += sizeof(struct ip6_hdr); 46653541Sshin 46753541Sshin n = m; 46853541Sshin while (n && n->m_len <= off) { 46953541Sshin off -= n->m_len; 47053541Sshin n = n->m_next; 47153541Sshin } 47253541Sshin if (!n) 47353541Sshin goto bad; 47453541Sshin p = (u_int16_t *)(mtod(n, caddr_t) + off); 47553541Sshin *p = 0; 47653541Sshin *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 47753541Sshin } 47853541Sshin 479148247Sume error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); 48053541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 48153541Sshin if (oifp) 48253541Sshin icmp6_ifoutstat_inc(oifp, type, code); 483181803Sbz V_icmp6stat.icp6s_outhist[type]++; 48478064Sume } else 485181803Sbz V_rip6stat.rip6s_opackets++; 48653541Sshin 48753541Sshin goto freectl; 48853541Sshin 48953541Sshin bad: 49053541Sshin if (m) 49153541Sshin m_freem(m); 49253541Sshin 49353541Sshin freectl: 494186170Skmacy if (control != NULL) { 495148247Sume ip6_clearpktopts(&opt, -1); 49653541Sshin m_freem(control); 49778064Sume } 498178285Srwatson INP_WUNLOCK(in6p); 499120856Sume return (error); 50053541Sshin} 50153541Sshin 50253541Sshin/* 50353541Sshin * Raw IPv6 socket option processing. 50453541Sshin */ 50553541Sshinint 506171259Sdelphijrip6_ctloutput(struct socket *so, struct sockopt *sopt) 50753541Sshin{ 50853541Sshin int error; 50953541Sshin 51053541Sshin if (sopt->sopt_level == IPPROTO_ICMPV6) 51153541Sshin /* 51253541Sshin * XXX: is it better to call icmp6_ctloutput() directly 51353541Sshin * from protosw? 51453541Sshin */ 515120856Sume return (icmp6_ctloutput(so, sopt)); 51653541Sshin else if (sopt->sopt_level != IPPROTO_IPV6) 51753541Sshin return (EINVAL); 51853541Sshin 51953541Sshin error = 0; 52053541Sshin 52153541Sshin switch (sopt->sopt_dir) { 52253541Sshin case SOPT_GET: 52353541Sshin switch (sopt->sopt_name) { 52456723Sshin case MRT6_INIT: 52556723Sshin case MRT6_DONE: 52656723Sshin case MRT6_ADD_MIF: 52756723Sshin case MRT6_DEL_MIF: 52856723Sshin case MRT6_ADD_MFC: 52956723Sshin case MRT6_DEL_MFC: 53056723Sshin case MRT6_PIM: 531166938Sbms error = ip6_mrouter_get ? ip6_mrouter_get(so, sopt) : 532166938Sbms EOPNOTSUPP; 53356723Sshin break; 534121578Sume case IPV6_CHECKSUM: 535121578Sume error = ip6_raw_ctloutput(so, sopt); 536121578Sume break; 53753541Sshin default: 53853541Sshin error = ip6_ctloutput(so, sopt); 53953541Sshin break; 54053541Sshin } 54153541Sshin break; 54253541Sshin 54353541Sshin case SOPT_SET: 54453541Sshin switch (sopt->sopt_name) { 54556723Sshin case MRT6_INIT: 54656723Sshin case MRT6_DONE: 54756723Sshin case MRT6_ADD_MIF: 54856723Sshin case MRT6_DEL_MIF: 54956723Sshin case MRT6_ADD_MFC: 55056723Sshin case MRT6_DEL_MFC: 55156723Sshin case MRT6_PIM: 552166938Sbms error = ip6_mrouter_set ? ip6_mrouter_set(so, sopt) : 553166938Sbms EOPNOTSUPP; 55456723Sshin break; 555121578Sume case IPV6_CHECKSUM: 556121578Sume error = ip6_raw_ctloutput(so, sopt); 557121578Sume break; 55853541Sshin default: 55953541Sshin error = ip6_ctloutput(so, sopt); 56053541Sshin break; 56153541Sshin } 56253541Sshin break; 56353541Sshin } 56453541Sshin 56553541Sshin return (error); 56653541Sshin} 56753541Sshin 56853541Sshinstatic int 56983366Sjulianrip6_attach(struct socket *so, int proto, struct thread *td) 57053541Sshin{ 571183550Szec INIT_VNET_INET(so->so_vnet); 57253541Sshin struct inpcb *inp; 573144261Ssam struct icmp6_filter *filter; 574157676Srwatson int error; 57553541Sshin 57653541Sshin inp = sotoinpcb(so); 577157374Srwatson KASSERT(inp == NULL, ("rip6_attach: inp != NULL")); 578180305Srwatson 579175630Sbz error = priv_check(td, PRIV_NETINET_RAW); 580175630Sbz if (error) 581180305Srwatson return (error); 58255009Sshin error = soreserve(so, rip_sendspace, rip_recvspace); 583157374Srwatson if (error) 584180305Srwatson return (error); 585184214Sdes filter = malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); 586157374Srwatson if (filter == NULL) 587180305Srwatson return (ENOMEM); 588181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 589181803Sbz error = in_pcballoc(so, &V_ripcbinfo); 590132714Srwatson if (error) { 591181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 592184205Sdes free(filter, M_PCB); 593180305Srwatson return (error); 594132714Srwatson } 59553541Sshin inp = (struct inpcb *)so->so_pcb; 596181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 59753541Sshin inp->inp_vflag |= INP_IPV6; 598186141Sbz inp->inp_ip_p = (long)proto; 59953541Sshin inp->in6p_hops = -1; /* use kernel default */ 60053541Sshin inp->in6p_cksum = -1; 601144261Ssam inp->in6p_icmp6filt = filter; 60253541Sshin ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); 603178285Srwatson INP_WUNLOCK(inp); 604180305Srwatson return (0); 60553541Sshin} 60653541Sshin 607157370Srwatsonstatic void 60853541Sshinrip6_detach(struct socket *so) 60953541Sshin{ 610183550Szec INIT_VNET_INET(so->so_vnet); 61153541Sshin struct inpcb *inp; 61253541Sshin 61353541Sshin inp = sotoinpcb(so); 614157374Srwatson KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); 615160549Srwatson 616166938Sbms if (so == ip6_mrouter && ip6_mrouter_done) 617166938Sbms ip6_mrouter_done(); 61853541Sshin /* xxx: RSVP */ 619181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 620178285Srwatson INP_WLOCK(inp); 621184205Sdes free(inp->in6p_icmp6filt, M_PCB); 622185344Sbz in_pcbdetach(inp); 623185370Sbz in_pcbfree(inp); 624181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 62553541Sshin} 62653541Sshin 627160549Srwatson/* XXXRW: This can't ever be called. */ 628157366Srwatsonstatic void 62953541Sshinrip6_abort(struct socket *so) 63053541Sshin{ 631160549Srwatson struct inpcb *inp; 632160549Srwatson 633160549Srwatson inp = sotoinpcb(so); 634160549Srwatson KASSERT(inp != NULL, ("rip6_abort: inp == NULL")); 635160549Srwatson 63653541Sshin soisdisconnected(so); 63753541Sshin} 63853541Sshin 639160549Srwatsonstatic void 640160549Srwatsonrip6_close(struct socket *so) 641160549Srwatson{ 642160549Srwatson struct inpcb *inp; 643160549Srwatson 644160549Srwatson inp = sotoinpcb(so); 645160549Srwatson KASSERT(inp != NULL, ("rip6_close: inp == NULL")); 646160549Srwatson 647160549Srwatson soisdisconnected(so); 648160549Srwatson} 649160549Srwatson 65053541Sshinstatic int 65153541Sshinrip6_disconnect(struct socket *so) 65253541Sshin{ 653180305Srwatson struct inpcb *inp; 65453541Sshin 655180305Srwatson inp = sotoinpcb(so); 656180305Srwatson KASSERT(inp != NULL, ("rip6_disconnect: inp == NULL")); 657180305Srwatson 65897658Stanimura if ((so->so_state & SS_ISCONNECTED) == 0) 659180305Srwatson return (ENOTCONN); 66053541Sshin inp->in6p_faddr = in6addr_any; 661157366Srwatson rip6_abort(so); 662157374Srwatson return (0); 66353541Sshin} 66453541Sshin 66553541Sshinstatic int 66683366Sjulianrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 66753541Sshin{ 668183550Szec INIT_VNET_NET(so->so_vnet); 669183550Szec INIT_VNET_INET(so->so_vnet); 670183550Szec INIT_VNET_INET6(so->so_vnet); 671180305Srwatson struct inpcb *inp; 67253541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 67353541Sshin struct ifaddr *ia = NULL; 674148385Sume int error = 0; 67553541Sshin 676180305Srwatson inp = sotoinpcb(so); 677157374Srwatson KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); 678180305Srwatson 67953541Sshin if (nam->sa_len != sizeof(*addr)) 680180305Srwatson return (EINVAL); 681185435Sbz if (!prison_check_ip6(td->td_ucred, &addr->sin6_addr)) 682185435Sbz return (EADDRNOTAVAIL); 683181803Sbz if (TAILQ_EMPTY(&V_ifnet) || addr->sin6_family != AF_INET6) 684180305Srwatson return (EADDRNOTAVAIL); 685181803Sbz if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) 686180305Srwatson return (error); 687148385Sume 68853541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && 68953541Sshin (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) 690180305Srwatson return (EADDRNOTAVAIL); 69153541Sshin if (ia && 69253541Sshin ((struct in6_ifaddr *)ia)->ia6_flags & 69353541Sshin (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| 69453541Sshin IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { 695120856Sume return (EADDRNOTAVAIL); 69653541Sshin } 697181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 698178285Srwatson INP_WLOCK(inp); 69953541Sshin inp->in6p_laddr = addr->sin6_addr; 700178285Srwatson INP_WUNLOCK(inp); 701181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 702180305Srwatson return (0); 70353541Sshin} 70453541Sshin 70553541Sshinstatic int 70683366Sjulianrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 70753541Sshin{ 708183550Szec INIT_VNET_NET(so->so_vnet); 709183550Szec INIT_VNET_INET(so->so_vnet); 710183550Szec INIT_VNET_INET6(so->so_vnet); 711180305Srwatson struct inpcb *inp; 71253541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 71353541Sshin struct in6_addr *in6a = NULL; 714148385Sume struct ifnet *ifp = NULL; 715148385Sume int error = 0, scope_ambiguous = 0; 71653541Sshin 717180305Srwatson inp = sotoinpcb(so); 718157374Srwatson KASSERT(inp != NULL, ("rip6_connect: inp == NULL")); 719180305Srwatson 72053541Sshin if (nam->sa_len != sizeof(*addr)) 721180305Srwatson return (EINVAL); 722181803Sbz if (TAILQ_EMPTY(&V_ifnet)) 723180305Srwatson return (EADDRNOTAVAIL); 72453541Sshin if (addr->sin6_family != AF_INET6) 725180305Srwatson return (EAFNOSUPPORT); 726148385Sume 727148385Sume /* 728180305Srwatson * Application should provide a proper zone ID or the use of default 729180305Srwatson * zone IDs should be enabled. Unfortunately, some applications do 730180305Srwatson * not behave as it should, so we need a workaround. Even if an 731180305Srwatson * appropriate ID is not determined, we'll see if we can determine 732180305Srwatson * the outgoing interface. If we can, determine the zone ID based on 733180305Srwatson * the interface below. 734148385Sume */ 735181803Sbz if (addr->sin6_scope_id == 0 && !V_ip6_use_defzone) 736148385Sume scope_ambiguous = 1; 737181803Sbz if ((error = sa6_embedscope(addr, V_ip6_use_defzone)) != 0) 738180305Srwatson return (error); 739148385Sume 740181803Sbz INP_INFO_WLOCK(&V_ripcbinfo); 741178285Srwatson INP_WLOCK(inp); 74253541Sshin /* Source address selection. XXX: need pcblookup? */ 74353541Sshin in6a = in6_selectsrc(addr, inp->in6p_outputopts, 744180371Sbz inp, NULL, so->so_cred, 745180371Sbz &ifp, &error); 746132714Srwatson if (in6a == NULL) { 747178285Srwatson INP_WUNLOCK(inp); 748181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 74953541Sshin return (error ? error : EADDRNOTAVAIL); 750132714Srwatson } 751148385Sume 752148385Sume /* XXX: see above */ 753148385Sume if (ifp && scope_ambiguous && 754148385Sume (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) { 755178285Srwatson INP_WUNLOCK(inp); 756181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 757180305Srwatson return (error); 758148385Sume } 759148385Sume inp->in6p_faddr = addr->sin6_addr; 76053541Sshin inp->in6p_laddr = *in6a; 76153541Sshin soisconnected(so); 762178285Srwatson INP_WUNLOCK(inp); 763181803Sbz INP_INFO_WUNLOCK(&V_ripcbinfo); 764180305Srwatson return (0); 76553541Sshin} 76653541Sshin 76753541Sshinstatic int 76853541Sshinrip6_shutdown(struct socket *so) 76953541Sshin{ 770132714Srwatson struct inpcb *inp; 771132714Srwatson 772132714Srwatson inp = sotoinpcb(so); 773157374Srwatson KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL")); 774180305Srwatson 775178285Srwatson INP_WLOCK(inp); 77653541Sshin socantsendmore(so); 777178285Srwatson INP_WUNLOCK(inp); 778180305Srwatson return (0); 77953541Sshin} 78053541Sshin 78153541Sshinstatic int 78253541Sshinrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 783171260Sdelphij struct mbuf *control, struct thread *td) 78453541Sshin{ 785180305Srwatson struct inpcb *inp; 78653541Sshin struct sockaddr_in6 tmp; 78753541Sshin struct sockaddr_in6 *dst; 788132714Srwatson int ret; 78953541Sshin 790180305Srwatson inp = sotoinpcb(so); 791157374Srwatson KASSERT(inp != NULL, ("rip6_send: inp == NULL")); 792180305Srwatson 793180305Srwatson /* Always copy sockaddr to avoid overwrites. */ 794132714Srwatson /* Unlocked read. */ 79553541Sshin if (so->so_state & SS_ISCONNECTED) { 79653541Sshin if (nam) { 79753541Sshin m_freem(m); 798180305Srwatson return (EISCONN); 79953541Sshin } 80053541Sshin /* XXX */ 80153541Sshin bzero(&tmp, sizeof(tmp)); 80253541Sshin tmp.sin6_family = AF_INET6; 80353541Sshin tmp.sin6_len = sizeof(struct sockaddr_in6); 804180990Srwatson INP_RLOCK(inp); 80553541Sshin bcopy(&inp->in6p_faddr, &tmp.sin6_addr, 806180990Srwatson sizeof(struct in6_addr)); 807180990Srwatson INP_RUNLOCK(inp); 80853541Sshin dst = &tmp; 80953541Sshin } else { 81053541Sshin if (nam == NULL) { 81153541Sshin m_freem(m); 812180305Srwatson return (ENOTCONN); 81353541Sshin } 814148385Sume if (nam->sa_len != sizeof(struct sockaddr_in6)) { 815148385Sume m_freem(m); 816180305Srwatson return (EINVAL); 817148385Sume } 81862587Sitojun tmp = *(struct sockaddr_in6 *)nam; 81962587Sitojun dst = &tmp; 820148385Sume 821148385Sume if (dst->sin6_family == AF_UNSPEC) { 822148385Sume /* 823148385Sume * XXX: we allow this case for backward 824148385Sume * compatibility to buggy applications that 825148385Sume * rely on old (and wrong) kernel behavior. 826148385Sume */ 827148385Sume log(LOG_INFO, "rip6 SEND: address family is " 828148385Sume "unspec. Assume AF_INET6\n"); 829148385Sume dst->sin6_family = AF_INET6; 830148385Sume } else if (dst->sin6_family != AF_INET6) { 831148385Sume m_freem(m); 832148385Sume return(EAFNOSUPPORT); 833148385Sume } 83453541Sshin } 835132714Srwatson ret = rip6_output(m, so, dst, control); 836132714Srwatson return (ret); 83753541Sshin} 83853541Sshin 83953541Sshinstruct pr_usrreqs rip6_usrreqs = { 840137386Sphk .pru_abort = rip6_abort, 841137386Sphk .pru_attach = rip6_attach, 842137386Sphk .pru_bind = rip6_bind, 843137386Sphk .pru_connect = rip6_connect, 844137386Sphk .pru_control = in6_control, 845137386Sphk .pru_detach = rip6_detach, 846137386Sphk .pru_disconnect = rip6_disconnect, 847169462Srwatson .pru_peeraddr = in6_getpeeraddr, 848137386Sphk .pru_send = rip6_send, 849137386Sphk .pru_shutdown = rip6_shutdown, 850169462Srwatson .pru_sockaddr = in6_getsockaddr, 851160549Srwatson .pru_close = rip6_close, 85253541Sshin}; 853