raw_ip6.c revision 174510
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 3253541Sshin * The Regents of the University of California. All rights reserved. 3353541Sshin * 3453541Sshin * Redistribution and use in source and binary forms, with or without 3553541Sshin * modification, are permitted provided that the following conditions 3653541Sshin * are met: 3753541Sshin * 1. Redistributions of source code must retain the above copyright 3853541Sshin * notice, this list of conditions and the following disclaimer. 3953541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4053541Sshin * notice, this list of conditions and the following disclaimer in the 4153541Sshin * documentation and/or other materials provided with the distribution. 4253541Sshin * 4. Neither the name of the University nor the names of its contributors 4353541Sshin * may be used to endorse or promote products derived from this software 4453541Sshin * without specific prior written permission. 4553541Sshin * 4653541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4753541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4853541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4953541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5053541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5153541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5253541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5353541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5453541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5553541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5653541Sshin * SUCH DAMAGE. 5753541Sshin * 5853541Sshin * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 5953541Sshin */ 6053541Sshin 61174510Sobrien#include <sys/cdefs.h> 62174510Sobrien__FBSDID("$FreeBSD: head/sys/netinet6/raw_ip6.c 174510 2007-12-10 16:03:40Z obrien $"); 63174510Sobrien 6455009Sshin#include "opt_ipsec.h" 6578064Sume#include "opt_inet6.h" 6655009Sshin 6753541Sshin#include <sys/param.h> 6895759Stanimura#include <sys/errno.h> 6995759Stanimura#include <sys/lock.h> 7053541Sshin#include <sys/malloc.h> 7195759Stanimura#include <sys/mbuf.h> 72170689Srwatson#include <sys/priv.h> 7353541Sshin#include <sys/proc.h> 7495759Stanimura#include <sys/protosw.h> 7595759Stanimura#include <sys/signalvar.h> 7653541Sshin#include <sys/socket.h> 7753541Sshin#include <sys/socketvar.h> 7895759Stanimura#include <sys/sx.h> 79148385Sume#include <sys/syslog.h> 8053541Sshin 8153541Sshin#include <net/if.h> 8295759Stanimura#include <net/if_types.h> 8353541Sshin#include <net/route.h> 8453541Sshin 8553541Sshin#include <netinet/in.h> 8653541Sshin#include <netinet/in_var.h> 8753541Sshin#include <netinet/in_systm.h> 8895759Stanimura#include <netinet/icmp6.h> 8995759Stanimura#include <netinet/in_pcb.h> 9062587Sitojun#include <netinet/ip6.h> 9195759Stanimura#include <netinet6/ip6protosw.h> 9256723Sshin#include <netinet6/ip6_mroute.h> 9353541Sshin#include <netinet6/in6_pcb.h> 9495759Stanimura#include <netinet6/ip6_var.h> 9553541Sshin#include <netinet6/nd6.h> 9695759Stanimura#include <netinet6/raw_ip6.h> 9762587Sitojun#include <netinet6/scope6_var.h> 9853541Sshin 99171167Sgnn#ifdef IPSEC 100105199Ssam#include <netipsec/ipsec.h> 101105199Ssam#include <netipsec/ipsec6.h> 102171167Sgnn#endif /* IPSEC */ 103105199Ssam 10453541Sshin#include <machine/stdarg.h> 10553541Sshin 10653541Sshin#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 10753541Sshin#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 10853541Sshin 10953541Sshin/* 11053541Sshin * Raw interface to IP6 protocol. 11153541Sshin */ 11253541Sshin 11353541Sshinextern struct inpcbhead ripcb; 11453541Sshinextern struct inpcbinfo ripcbinfo; 11553541Sshinextern u_long rip_sendspace; 11653541Sshinextern u_long rip_recvspace; 11753541Sshin 11878064Sumestruct rip6stat rip6stat; 11978064Sume 12053541Sshin/* 121166938Sbms * Hooks for multicast forwarding. 122166938Sbms */ 123166948Sbmsstruct socket *ip6_mrouter = NULL; 124166938Sbmsint (*ip6_mrouter_set)(struct socket *, struct sockopt *); 125166938Sbmsint (*ip6_mrouter_get)(struct socket *, struct sockopt *); 126166938Sbmsint (*ip6_mrouter_done)(void); 127166938Sbmsint (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); 128166938Sbmsint (*mrt6_ioctl)(int, caddr_t); 129166938Sbms 130166938Sbms/* 13153541Sshin * Setup generic address and protocol structures 13253541Sshin * for raw_input routine, then pass them along with 13353541Sshin * mbuf chain. 13453541Sshin */ 13553541Sshinint 136171259Sdelphijrip6_input(struct mbuf **mp, int *offp, int proto) 13753541Sshin{ 13853541Sshin struct mbuf *m = *mp; 13953541Sshin register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 14053541Sshin register struct inpcb *in6p; 14153541Sshin struct inpcb *last = 0; 14278064Sume struct mbuf *opts = NULL; 143121901Sume struct sockaddr_in6 fromsa; 14453541Sshin 14578064Sume rip6stat.rip6s_ipackets++; 14678064Sume 14783934Sbrooks if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 14878064Sume /* XXX send icmp6 host/port unreach? */ 14978064Sume m_freem(m); 15078064Sume return IPPROTO_DONE; 15153541Sshin } 15278064Sume 153121901Sume init_sin6(&fromsa, m); /* general init */ 15453541Sshin 155132714Srwatson INP_INFO_RLOCK(&ripcbinfo); 15653541Sshin LIST_FOREACH(in6p, &ripcb, inp_list) { 157132714Srwatson INP_LOCK(in6p); 158132714Srwatson if ((in6p->in6p_vflag & INP_IPV6) == 0) { 159132714Srwatsondocontinue: 160132714Srwatson INP_UNLOCK(in6p); 16153541Sshin continue; 162132714Srwatson } 16353541Sshin if (in6p->in6p_ip6_nxt && 16453541Sshin in6p->in6p_ip6_nxt != proto) 165132714Srwatson goto docontinue; 16653541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 16753541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 168132714Srwatson goto docontinue; 16953541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 17053541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 171132714Srwatson goto docontinue; 17278064Sume if (in6p->in6p_cksum != -1) { 17378064Sume rip6stat.rip6s_isum++; 174151459Ssuz if (in6_cksum(m, proto, *offp, 17578064Sume m->m_pkthdr.len - *offp)) { 17678064Sume rip6stat.rip6s_badsum++; 177132714Srwatson goto docontinue; 17878064Sume } 17953541Sshin } 18053541Sshin if (last) { 18153541Sshin struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 18278064Sume 183171167Sgnn#ifdef IPSEC 18478064Sume /* 18578064Sume * Check AH/ESP integrity. 18678064Sume */ 187125396Sume if (n && ipsec6_in_reject(n, last)) { 18878064Sume m_freem(n); 18978064Sume ipsec6stat.in_polvio++; 190105199Ssam /* do not inject data into pcb */ 191105199Ssam } else 192171167Sgnn#endif /* IPSEC */ 19353541Sshin if (n) { 19497658Stanimura if (last->in6p_flags & IN6P_CONTROLOPTS || 19597658Stanimura last->in6p_socket->so_options & SO_TIMESTAMP) 196121674Sume ip6_savecontrol(last, n, &opts); 19753541Sshin /* strip intermediate headers */ 19853541Sshin m_adj(n, *offp); 19953541Sshin if (sbappendaddr(&last->in6p_socket->so_rcv, 200121901Sume (struct sockaddr *)&fromsa, 20153541Sshin n, opts) == 0) { 20253541Sshin m_freem(n); 20353541Sshin if (opts) 20453541Sshin m_freem(opts); 20578064Sume rip6stat.rip6s_fullsock++; 20697658Stanimura } else 20753541Sshin sorwakeup(last->in6p_socket); 20853541Sshin opts = NULL; 20953541Sshin } 210132714Srwatson INP_UNLOCK(last); 21153541Sshin } 21253541Sshin last = in6p; 21353541Sshin } 214171167Sgnn#ifdef IPSEC 21578064Sume /* 21678064Sume * Check AH/ESP integrity. 21778064Sume */ 218125396Sume if (last && ipsec6_in_reject(m, last)) { 21978064Sume m_freem(m); 22078064Sume ipsec6stat.in_polvio++; 221105199Ssam ip6stat.ip6s_delivered--; 222105199Ssam /* do not inject data into pcb */ 223149224Ssuz INP_UNLOCK(last); 224105199Ssam } else 225171167Sgnn#endif /* IPSEC */ 22653541Sshin if (last) { 22797658Stanimura if (last->in6p_flags & IN6P_CONTROLOPTS || 22897658Stanimura last->in6p_socket->so_options & SO_TIMESTAMP) 229121674Sume ip6_savecontrol(last, m, &opts); 23053541Sshin /* strip intermediate headers */ 23153541Sshin m_adj(m, *offp); 23253541Sshin if (sbappendaddr(&last->in6p_socket->so_rcv, 233121901Sume (struct sockaddr *)&fromsa, m, opts) == 0) { 23453541Sshin m_freem(m); 23553541Sshin if (opts) 23653541Sshin m_freem(opts); 23778064Sume rip6stat.rip6s_fullsock++; 23897658Stanimura } else 23953541Sshin sorwakeup(last->in6p_socket); 240132714Srwatson INP_UNLOCK(last); 24153541Sshin } else { 24278064Sume rip6stat.rip6s_nosock++; 24378064Sume if (m->m_flags & M_MCAST) 24478064Sume rip6stat.rip6s_nosockmcast++; 24553541Sshin if (proto == IPPROTO_NONE) 24653541Sshin m_freem(m); 24753541Sshin else { 24853541Sshin char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ 24953541Sshin icmp6_error(m, ICMP6_PARAM_PROB, 25053541Sshin ICMP6_PARAMPROB_NEXTHEADER, 25153541Sshin prvnxtp - mtod(m, char *)); 25253541Sshin } 25353541Sshin ip6stat.ip6s_delivered--; 25453541Sshin } 255134655Srwatson INP_INFO_RUNLOCK(&ripcbinfo); 25653541Sshin return IPPROTO_DONE; 25753541Sshin} 25853541Sshin 25962587Sitojunvoid 260171259Sdelphijrip6_ctlinput(int cmd, struct sockaddr *sa, void *d) 26162587Sitojun{ 26262587Sitojun struct ip6_hdr *ip6; 26362587Sitojun struct mbuf *m; 26462587Sitojun int off = 0; 26578064Sume struct ip6ctlparam *ip6cp = NULL; 26678064Sume const struct sockaddr_in6 *sa6_src = NULL; 267125776Sume void *cmdarg; 26898211Shsu struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange; 26962587Sitojun 27062587Sitojun if (sa->sa_family != AF_INET6 || 27162587Sitojun sa->sa_len != sizeof(struct sockaddr_in6)) 27262587Sitojun return; 27362587Sitojun 27462587Sitojun if ((unsigned)cmd >= PRC_NCMDS) 27562587Sitojun return; 27662587Sitojun if (PRC_IS_REDIRECT(cmd)) 27762587Sitojun notify = in6_rtchange, d = NULL; 27862587Sitojun else if (cmd == PRC_HOSTDEAD) 27962587Sitojun d = NULL; 28062587Sitojun else if (inet6ctlerrmap[cmd] == 0) 28162587Sitojun return; 28262587Sitojun 28362587Sitojun /* if the parameter is from icmp6, decode it. */ 28462587Sitojun if (d != NULL) { 28578064Sume ip6cp = (struct ip6ctlparam *)d; 28662587Sitojun m = ip6cp->ip6c_m; 28762587Sitojun ip6 = ip6cp->ip6c_ip6; 28862587Sitojun off = ip6cp->ip6c_off; 289125776Sume cmdarg = ip6cp->ip6c_cmdarg; 29078064Sume sa6_src = ip6cp->ip6c_src; 29162587Sitojun } else { 29262587Sitojun m = NULL; 29362587Sitojun ip6 = NULL; 294125776Sume cmdarg = NULL; 29578064Sume sa6_src = &sa6_any; 29662587Sitojun } 29762587Sitojun 298133192Srwatson (void) in6_pcbnotify(&ripcbinfo, sa, 0, 299133192Srwatson (const struct sockaddr *)sa6_src, 300125776Sume 0, cmd, cmdarg, notify); 30162587Sitojun} 30262587Sitojun 30353541Sshin/* 30453541Sshin * Generate IPv6 header and pass packet to ip6_output. 30553541Sshin * Tack on options user may have setup with control call. 30653541Sshin */ 30753541Sshinint 30853541Sshin#if __STDC__ 30953541Sshinrip6_output(struct mbuf *m, ...) 31053541Sshin#else 31153541Sshinrip6_output(m, va_alist) 31253541Sshin struct mbuf *m; 31353541Sshin va_dcl 31453541Sshin#endif 31553541Sshin{ 316120941Sume struct mbuf *control; 31753541Sshin struct socket *so; 31853541Sshin struct sockaddr_in6 *dstsock; 31953541Sshin struct in6_addr *dst; 32053541Sshin struct ip6_hdr *ip6; 32153541Sshin struct inpcb *in6p; 32253541Sshin u_int plen = m->m_pkthdr.len; 32353541Sshin int error = 0; 324148247Sume struct ip6_pktopts opt, *optp; 32553541Sshin struct ifnet *oifp = NULL; 32653541Sshin int type = 0, code = 0; /* for ICMPv6 output statistics only */ 32753541Sshin int priv = 0; 328148385Sume int scope_ambiguous = 0; 329121472Sume struct in6_addr *in6a; 33053541Sshin va_list ap; 33153541Sshin 33253541Sshin va_start(ap, m); 33353541Sshin so = va_arg(ap, struct socket *); 33453541Sshin dstsock = va_arg(ap, struct sockaddr_in6 *); 33553541Sshin control = va_arg(ap, struct mbuf *); 33653541Sshin va_end(ap); 33753541Sshin 33853541Sshin in6p = sotoin6pcb(so); 339132714Srwatson INP_LOCK(in6p); 34053541Sshin 34153541Sshin priv = 0; 342170587Srwatson if (suser_cred(so->so_cred, 0) == 0) 34353541Sshin priv = 1; 34453541Sshin dst = &dstsock->sin6_addr; 34553541Sshin if (control) { 346148242Sume if ((error = ip6_setpktopts(control, &opt, 347148250Sume in6p->in6p_outputopts, priv, so->so_proto->pr_protocol)) 348121472Sume != 0) { 34953541Sshin goto bad; 350121472Sume } 351148247Sume optp = &opt; 352148247Sume } else 353148247Sume optp = in6p->in6p_outputopts; 35453541Sshin 35553541Sshin /* 356148385Sume * Check and convert scope zone ID into internal form. 357148385Sume * XXX: we may still need to determine the zone later. 358148385Sume */ 359148385Sume if (!(so->so_state & SS_ISCONNECTED)) { 360148385Sume if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone) 361148385Sume scope_ambiguous = 1; 362148385Sume if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0) 363148385Sume goto bad; 364148385Sume } 365148385Sume 366148385Sume /* 36753541Sshin * For an ICMPv6 packet, we should know its type and code 36853541Sshin * to update statistics. 36953541Sshin */ 37053541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 37153541Sshin struct icmp6_hdr *icmp6; 37253541Sshin if (m->m_len < sizeof(struct icmp6_hdr) && 37353541Sshin (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 37453541Sshin error = ENOBUFS; 37553541Sshin goto bad; 37653541Sshin } 37753541Sshin icmp6 = mtod(m, struct icmp6_hdr *); 37853541Sshin type = icmp6->icmp6_type; 37953541Sshin code = icmp6->icmp6_code; 38053541Sshin } 38153541Sshin 382133592Srwatson M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 383133592Srwatson if (m == NULL) { 384133592Srwatson error = ENOBUFS; 385133592Srwatson goto bad; 386133592Srwatson } 38753541Sshin ip6 = mtod(m, struct ip6_hdr *); 38853541Sshin 38953541Sshin /* 39053541Sshin * Source address selection. 39153541Sshin */ 392148247Sume if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL, 393148385Sume &in6p->in6p_laddr, &oifp, &error)) == NULL) { 394121472Sume if (error == 0) 395121472Sume error = EADDRNOTAVAIL; 396121472Sume goto bad; 39753541Sshin } 398121472Sume ip6->ip6_src = *in6a; 399148385Sume 400148385Sume if (oifp && scope_ambiguous) { 401148385Sume /* 402148385Sume * Application should provide a proper zone ID or the use of 403148385Sume * default zone IDs should be enabled. Unfortunately, some 404148385Sume * applications do not behave as it should, so we need a 405148385Sume * workaround. Even if an appropriate ID is not determined 406148385Sume * (when it's required), if we can determine the outgoing 407148385Sume * interface. determine the zone ID based on the interface. 408148385Sume */ 409148385Sume error = in6_setscope(&dstsock->sin6_addr, oifp, NULL); 410148385Sume if (error != 0) 411148385Sume goto bad; 412148385Sume } 413148385Sume ip6->ip6_dst = dstsock->sin6_addr; 414148385Sume 415148385Sume /* fill in the rest of the IPv6 header fields */ 41655009Sshin ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | 41755009Sshin (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); 41855009Sshin ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | 41955009Sshin (IPV6_VERSION & IPV6_VERSION_MASK); 42053541Sshin /* ip6_plen will be filled in ip6_output, so not fill it here. */ 42153541Sshin ip6->ip6_nxt = in6p->in6p_ip6_nxt; 42253541Sshin ip6->ip6_hlim = in6_selecthlim(in6p, oifp); 42353541Sshin 42453541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 42553541Sshin in6p->in6p_cksum != -1) { 42653541Sshin struct mbuf *n; 42753541Sshin int off; 42853541Sshin u_int16_t *p; 42953541Sshin 43053541Sshin /* compute checksum */ 43153541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 43253541Sshin off = offsetof(struct icmp6_hdr, icmp6_cksum); 43353541Sshin else 43453541Sshin off = in6p->in6p_cksum; 43553541Sshin if (plen < off + 1) { 43653541Sshin error = EINVAL; 43753541Sshin goto bad; 43853541Sshin } 43953541Sshin off += sizeof(struct ip6_hdr); 44053541Sshin 44153541Sshin n = m; 44253541Sshin while (n && n->m_len <= off) { 44353541Sshin off -= n->m_len; 44453541Sshin n = n->m_next; 44553541Sshin } 44653541Sshin if (!n) 44753541Sshin goto bad; 44853541Sshin p = (u_int16_t *)(mtod(n, caddr_t) + off); 44953541Sshin *p = 0; 45053541Sshin *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 45153541Sshin } 45253541Sshin 453148247Sume error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); 45453541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 45553541Sshin if (oifp) 45653541Sshin icmp6_ifoutstat_inc(oifp, type, code); 45753541Sshin icmp6stat.icp6s_outhist[type]++; 45878064Sume } else 45978064Sume rip6stat.rip6s_opackets++; 46053541Sshin 46153541Sshin goto freectl; 46253541Sshin 46353541Sshin bad: 46453541Sshin if (m) 46553541Sshin m_freem(m); 46653541Sshin 46753541Sshin freectl: 46878064Sume if (control) { 469148247Sume ip6_clearpktopts(&opt, -1); 47053541Sshin m_freem(control); 47178064Sume } 472132714Srwatson INP_UNLOCK(in6p); 473120856Sume return (error); 47453541Sshin} 47553541Sshin 47653541Sshin/* 47753541Sshin * Raw IPv6 socket option processing. 47853541Sshin */ 47953541Sshinint 480171259Sdelphijrip6_ctloutput(struct socket *so, struct sockopt *sopt) 48153541Sshin{ 48253541Sshin int error; 48353541Sshin 48453541Sshin if (sopt->sopt_level == IPPROTO_ICMPV6) 48553541Sshin /* 48653541Sshin * XXX: is it better to call icmp6_ctloutput() directly 48753541Sshin * from protosw? 48853541Sshin */ 489120856Sume return (icmp6_ctloutput(so, sopt)); 49053541Sshin else if (sopt->sopt_level != IPPROTO_IPV6) 49153541Sshin return (EINVAL); 49253541Sshin 49353541Sshin error = 0; 49453541Sshin 49553541Sshin switch (sopt->sopt_dir) { 49653541Sshin case SOPT_GET: 49753541Sshin switch (sopt->sopt_name) { 49856723Sshin case MRT6_INIT: 49956723Sshin case MRT6_DONE: 50056723Sshin case MRT6_ADD_MIF: 50156723Sshin case MRT6_DEL_MIF: 50256723Sshin case MRT6_ADD_MFC: 50356723Sshin case MRT6_DEL_MFC: 50456723Sshin case MRT6_PIM: 505166938Sbms error = ip6_mrouter_get ? ip6_mrouter_get(so, sopt) : 506166938Sbms EOPNOTSUPP; 50756723Sshin break; 508121578Sume case IPV6_CHECKSUM: 509121578Sume error = ip6_raw_ctloutput(so, sopt); 510121578Sume break; 51153541Sshin default: 51253541Sshin error = ip6_ctloutput(so, sopt); 51353541Sshin break; 51453541Sshin } 51553541Sshin break; 51653541Sshin 51753541Sshin case SOPT_SET: 51853541Sshin switch (sopt->sopt_name) { 51956723Sshin case MRT6_INIT: 52056723Sshin case MRT6_DONE: 52156723Sshin case MRT6_ADD_MIF: 52256723Sshin case MRT6_DEL_MIF: 52356723Sshin case MRT6_ADD_MFC: 52456723Sshin case MRT6_DEL_MFC: 52556723Sshin case MRT6_PIM: 526166938Sbms error = ip6_mrouter_set ? ip6_mrouter_set(so, sopt) : 527166938Sbms EOPNOTSUPP; 52856723Sshin break; 529121578Sume case IPV6_CHECKSUM: 530121578Sume error = ip6_raw_ctloutput(so, sopt); 531121578Sume break; 53253541Sshin default: 53353541Sshin error = ip6_ctloutput(so, sopt); 53453541Sshin break; 53553541Sshin } 53653541Sshin break; 53753541Sshin } 53853541Sshin 53953541Sshin return (error); 54053541Sshin} 54153541Sshin 54253541Sshinstatic int 54383366Sjulianrip6_attach(struct socket *so, int proto, struct thread *td) 54453541Sshin{ 54553541Sshin struct inpcb *inp; 546144261Ssam struct icmp6_filter *filter; 547157676Srwatson int error; 54853541Sshin 54953541Sshin inp = sotoinpcb(so); 550157374Srwatson KASSERT(inp == NULL, ("rip6_attach: inp != NULL")); 551157374Srwatson if (td && (error = suser(td)) != 0) 55253541Sshin return error; 55355009Sshin error = soreserve(so, rip_sendspace, rip_recvspace); 554157374Srwatson if (error) 55555009Sshin return error; 556144261Ssam MALLOC(filter, struct icmp6_filter *, 557144261Ssam sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); 558157374Srwatson if (filter == NULL) 559144261Ssam return ENOMEM; 560157374Srwatson INP_INFO_WLOCK(&ripcbinfo); 561160491Sups error = in_pcballoc(so, &ripcbinfo); 562132714Srwatson if (error) { 563132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 564144261Ssam FREE(filter, M_PCB); 56553541Sshin return error; 566132714Srwatson } 56753541Sshin inp = (struct inpcb *)so->so_pcb; 568132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 56953541Sshin inp->inp_vflag |= INP_IPV6; 57053541Sshin inp->in6p_ip6_nxt = (long)proto; 57153541Sshin inp->in6p_hops = -1; /* use kernel default */ 57253541Sshin inp->in6p_cksum = -1; 573144261Ssam inp->in6p_icmp6filt = filter; 57453541Sshin ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); 575132714Srwatson INP_UNLOCK(inp); 57653541Sshin return 0; 57753541Sshin} 57853541Sshin 579157370Srwatsonstatic void 58053541Sshinrip6_detach(struct socket *so) 58153541Sshin{ 58253541Sshin struct inpcb *inp; 58353541Sshin 58453541Sshin inp = sotoinpcb(so); 585157374Srwatson KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); 586160549Srwatson 587166938Sbms if (so == ip6_mrouter && ip6_mrouter_done) 588166938Sbms ip6_mrouter_done(); 58953541Sshin /* xxx: RSVP */ 590160549Srwatson INP_INFO_WLOCK(&ripcbinfo); 591160549Srwatson INP_LOCK(inp); 59253541Sshin if (inp->in6p_icmp6filt) { 59353541Sshin FREE(inp->in6p_icmp6filt, M_PCB); 59453541Sshin inp->in6p_icmp6filt = NULL; 59553541Sshin } 59653541Sshin in6_pcbdetach(inp); 597157374Srwatson in6_pcbfree(inp); 598132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 59953541Sshin} 60053541Sshin 601160549Srwatson/* XXXRW: This can't ever be called. */ 602157366Srwatsonstatic void 60353541Sshinrip6_abort(struct socket *so) 60453541Sshin{ 605160549Srwatson struct inpcb *inp; 606160549Srwatson 607160549Srwatson inp = sotoinpcb(so); 608160549Srwatson KASSERT(inp != NULL, ("rip6_abort: inp == NULL")); 609160549Srwatson 61053541Sshin soisdisconnected(so); 61153541Sshin} 61253541Sshin 613160549Srwatsonstatic void 614160549Srwatsonrip6_close(struct socket *so) 615160549Srwatson{ 616160549Srwatson struct inpcb *inp; 617160549Srwatson 618160549Srwatson inp = sotoinpcb(so); 619160549Srwatson KASSERT(inp != NULL, ("rip6_close: inp == NULL")); 620160549Srwatson 621160549Srwatson soisdisconnected(so); 622160549Srwatson} 623160549Srwatson 62453541Sshinstatic int 62553541Sshinrip6_disconnect(struct socket *so) 62653541Sshin{ 62753541Sshin struct inpcb *inp = sotoinpcb(so); 62853541Sshin 62997658Stanimura if ((so->so_state & SS_ISCONNECTED) == 0) 63053541Sshin return ENOTCONN; 63153541Sshin inp->in6p_faddr = in6addr_any; 632157366Srwatson rip6_abort(so); 633157374Srwatson return (0); 63453541Sshin} 63553541Sshin 63653541Sshinstatic int 63783366Sjulianrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 63853541Sshin{ 63953541Sshin struct inpcb *inp = sotoinpcb(so); 64053541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 64153541Sshin struct ifaddr *ia = NULL; 642148385Sume int error = 0; 64353541Sshin 644157374Srwatson KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); 64553541Sshin if (nam->sa_len != sizeof(*addr)) 64653541Sshin return EINVAL; 64753541Sshin if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) 64853541Sshin return EADDRNOTAVAIL; 649148385Sume if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) 650148385Sume return(error); 651148385Sume 65253541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && 65353541Sshin (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) 65453541Sshin return EADDRNOTAVAIL; 65553541Sshin if (ia && 65653541Sshin ((struct in6_ifaddr *)ia)->ia6_flags & 65753541Sshin (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| 65853541Sshin IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { 659120856Sume return (EADDRNOTAVAIL); 66053541Sshin } 661132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 662132714Srwatson INP_LOCK(inp); 66353541Sshin inp->in6p_laddr = addr->sin6_addr; 664132714Srwatson INP_UNLOCK(inp); 665132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 66653541Sshin return 0; 66753541Sshin} 66853541Sshin 66953541Sshinstatic int 67083366Sjulianrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 67153541Sshin{ 67253541Sshin struct inpcb *inp = sotoinpcb(so); 67353541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 67453541Sshin struct in6_addr *in6a = NULL; 675148385Sume struct ifnet *ifp = NULL; 676148385Sume int error = 0, scope_ambiguous = 0; 67753541Sshin 678157374Srwatson KASSERT(inp != NULL, ("rip6_connect: inp == NULL")); 67953541Sshin if (nam->sa_len != sizeof(*addr)) 68053541Sshin return EINVAL; 68153541Sshin if (TAILQ_EMPTY(&ifnet)) 68253541Sshin return EADDRNOTAVAIL; 68353541Sshin if (addr->sin6_family != AF_INET6) 68453541Sshin return EAFNOSUPPORT; 685148385Sume 686148385Sume /* 687148385Sume * Application should provide a proper zone ID or the use of 688148385Sume * default zone IDs should be enabled. Unfortunately, some 689148385Sume * applications do not behave as it should, so we need a 690148385Sume * workaround. Even if an appropriate ID is not determined, 691148385Sume * we'll see if we can determine the outgoing interface. If we 692148385Sume * can, determine the zone ID based on the interface below. 693148385Sume */ 694148385Sume if (addr->sin6_scope_id == 0 && !ip6_use_defzone) 695148385Sume scope_ambiguous = 1; 696148385Sume if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) 697148385Sume return(error); 698148385Sume 699132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 700132714Srwatson INP_LOCK(inp); 70153541Sshin /* Source address selection. XXX: need pcblookup? */ 70253541Sshin in6a = in6_selectsrc(addr, inp->in6p_outputopts, 703122927Sandre inp->in6p_moptions, NULL, 704148385Sume &inp->in6p_laddr, &ifp, &error); 705132714Srwatson if (in6a == NULL) { 706132714Srwatson INP_UNLOCK(inp); 707132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 70853541Sshin return (error ? error : EADDRNOTAVAIL); 709132714Srwatson } 710148385Sume 711148385Sume /* XXX: see above */ 712148385Sume if (ifp && scope_ambiguous && 713148385Sume (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) { 714148385Sume INP_UNLOCK(inp); 715148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 716148385Sume return(error); 717148385Sume } 718148385Sume inp->in6p_faddr = addr->sin6_addr; 71953541Sshin inp->in6p_laddr = *in6a; 72053541Sshin soisconnected(so); 721132714Srwatson INP_UNLOCK(inp); 722132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 72353541Sshin return 0; 72453541Sshin} 72553541Sshin 72653541Sshinstatic int 72753541Sshinrip6_shutdown(struct socket *so) 72853541Sshin{ 729132714Srwatson struct inpcb *inp; 730132714Srwatson 731132714Srwatson inp = sotoinpcb(so); 732157374Srwatson KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL")); 733132714Srwatson INP_LOCK(inp); 73453541Sshin socantsendmore(so); 735132714Srwatson INP_UNLOCK(inp); 73653541Sshin return 0; 73753541Sshin} 73853541Sshin 73953541Sshinstatic int 74053541Sshinrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 741171260Sdelphij struct mbuf *control, struct thread *td) 74253541Sshin{ 74353541Sshin struct inpcb *inp = sotoinpcb(so); 74453541Sshin struct sockaddr_in6 tmp; 74553541Sshin struct sockaddr_in6 *dst; 746132714Srwatson int ret; 74753541Sshin 748157374Srwatson KASSERT(inp != NULL, ("rip6_send: inp == NULL")); 749132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 75062587Sitojun /* always copy sockaddr to avoid overwrites */ 751132714Srwatson /* Unlocked read. */ 75253541Sshin if (so->so_state & SS_ISCONNECTED) { 75353541Sshin if (nam) { 754132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 75553541Sshin m_freem(m); 75653541Sshin return EISCONN; 75753541Sshin } 75853541Sshin /* XXX */ 75953541Sshin bzero(&tmp, sizeof(tmp)); 76053541Sshin tmp.sin6_family = AF_INET6; 76153541Sshin tmp.sin6_len = sizeof(struct sockaddr_in6); 76253541Sshin bcopy(&inp->in6p_faddr, &tmp.sin6_addr, 76353541Sshin sizeof(struct in6_addr)); 76453541Sshin dst = &tmp; 76553541Sshin } else { 76653541Sshin if (nam == NULL) { 767132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 76853541Sshin m_freem(m); 76953541Sshin return ENOTCONN; 77053541Sshin } 771148385Sume if (nam->sa_len != sizeof(struct sockaddr_in6)) { 772148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 773148385Sume m_freem(m); 774148385Sume return(EINVAL); 775148385Sume } 77662587Sitojun tmp = *(struct sockaddr_in6 *)nam; 77762587Sitojun dst = &tmp; 778148385Sume 779148385Sume if (dst->sin6_family == AF_UNSPEC) { 780148385Sume /* 781148385Sume * XXX: we allow this case for backward 782148385Sume * compatibility to buggy applications that 783148385Sume * rely on old (and wrong) kernel behavior. 784148385Sume */ 785148385Sume log(LOG_INFO, "rip6 SEND: address family is " 786148385Sume "unspec. Assume AF_INET6\n"); 787148385Sume dst->sin6_family = AF_INET6; 788148385Sume } else if (dst->sin6_family != AF_INET6) { 789148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 790148385Sume m_freem(m); 791148385Sume return(EAFNOSUPPORT); 792148385Sume } 79353541Sshin } 794132714Srwatson ret = rip6_output(m, so, dst, control); 795132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 796132714Srwatson return (ret); 79753541Sshin} 79853541Sshin 79953541Sshinstruct pr_usrreqs rip6_usrreqs = { 800137386Sphk .pru_abort = rip6_abort, 801137386Sphk .pru_attach = rip6_attach, 802137386Sphk .pru_bind = rip6_bind, 803137386Sphk .pru_connect = rip6_connect, 804137386Sphk .pru_control = in6_control, 805137386Sphk .pru_detach = rip6_detach, 806137386Sphk .pru_disconnect = rip6_disconnect, 807169462Srwatson .pru_peeraddr = in6_getpeeraddr, 808137386Sphk .pru_send = rip6_send, 809137386Sphk .pru_shutdown = rip6_shutdown, 810169462Srwatson .pru_sockaddr = in6_getsockaddr, 811160549Srwatson .pru_close = rip6_close, 81253541Sshin}; 813