raw_ip6.c revision 171259
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 * $FreeBSD: head/sys/netinet6/raw_ip6.c 171259 2007-07-05 16:23:49Z delphij $ 3053541Sshin */ 3153541Sshin 32139826Simp/*- 3353541Sshin * Copyright (c) 1982, 1986, 1988, 1993 3453541Sshin * The Regents of the University of California. All rights reserved. 3553541Sshin * 3653541Sshin * Redistribution and use in source and binary forms, with or without 3753541Sshin * modification, are permitted provided that the following conditions 3853541Sshin * are met: 3953541Sshin * 1. Redistributions of source code must retain the above copyright 4053541Sshin * notice, this list of conditions and the following disclaimer. 4153541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4253541Sshin * notice, this list of conditions and the following disclaimer in the 4353541Sshin * documentation and/or other materials provided with the distribution. 4453541Sshin * 4. Neither the name of the University nor the names of its contributors 4553541Sshin * may be used to endorse or promote products derived from this software 4653541Sshin * without specific prior written permission. 4753541Sshin * 4853541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4953541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5053541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5153541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5253541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5353541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5453541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5553541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5653541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5753541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5853541Sshin * SUCH DAMAGE. 5953541Sshin * 6053541Sshin * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 6153541Sshin */ 6253541Sshin 6355009Sshin#include "opt_ipsec.h" 6478064Sume#include "opt_inet6.h" 6555009Sshin 6653541Sshin#include <sys/param.h> 6795759Stanimura#include <sys/errno.h> 6895759Stanimura#include <sys/lock.h> 6953541Sshin#include <sys/malloc.h> 7095759Stanimura#include <sys/mbuf.h> 71170689Srwatson#include <sys/priv.h> 7253541Sshin#include <sys/proc.h> 7395759Stanimura#include <sys/protosw.h> 7495759Stanimura#include <sys/signalvar.h> 7553541Sshin#include <sys/socket.h> 7653541Sshin#include <sys/socketvar.h> 7795759Stanimura#include <sys/sx.h> 78148385Sume#include <sys/syslog.h> 7953541Sshin 8053541Sshin#include <net/if.h> 8195759Stanimura#include <net/if_types.h> 8253541Sshin#include <net/route.h> 8353541Sshin 8453541Sshin#include <netinet/in.h> 8553541Sshin#include <netinet/in_var.h> 8653541Sshin#include <netinet/in_systm.h> 8795759Stanimura#include <netinet/icmp6.h> 8895759Stanimura#include <netinet/in_pcb.h> 8962587Sitojun#include <netinet/ip6.h> 9095759Stanimura#include <netinet6/ip6protosw.h> 9156723Sshin#include <netinet6/ip6_mroute.h> 9253541Sshin#include <netinet6/in6_pcb.h> 9395759Stanimura#include <netinet6/ip6_var.h> 9453541Sshin#include <netinet6/nd6.h> 9595759Stanimura#include <netinet6/raw_ip6.h> 9662587Sitojun#include <netinet6/scope6_var.h> 9753541Sshin 98171167Sgnn#ifdef IPSEC 99105199Ssam#include <netipsec/ipsec.h> 100105199Ssam#include <netipsec/ipsec6.h> 101171167Sgnn#endif /* IPSEC */ 102105199Ssam 10353541Sshin#include <machine/stdarg.h> 10453541Sshin 10553541Sshin#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 10653541Sshin#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 10753541Sshin 10853541Sshin/* 10953541Sshin * Raw interface to IP6 protocol. 11053541Sshin */ 11153541Sshin 11253541Sshinextern struct inpcbhead ripcb; 11353541Sshinextern struct inpcbinfo ripcbinfo; 11453541Sshinextern u_long rip_sendspace; 11553541Sshinextern u_long rip_recvspace; 11653541Sshin 11778064Sumestruct rip6stat rip6stat; 11878064Sume 11953541Sshin/* 120166938Sbms * Hooks for multicast forwarding. 121166938Sbms */ 122166948Sbmsstruct socket *ip6_mrouter = NULL; 123166938Sbmsint (*ip6_mrouter_set)(struct socket *, struct sockopt *); 124166938Sbmsint (*ip6_mrouter_get)(struct socket *, struct sockopt *); 125166938Sbmsint (*ip6_mrouter_done)(void); 126166938Sbmsint (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); 127166938Sbmsint (*mrt6_ioctl)(int, caddr_t); 128166938Sbms 129166938Sbms/* 13053541Sshin * Setup generic address and protocol structures 13153541Sshin * for raw_input routine, then pass them along with 13253541Sshin * mbuf chain. 13353541Sshin */ 13453541Sshinint 135171259Sdelphijrip6_input(struct mbuf **mp, int *offp, int proto) 13653541Sshin{ 13753541Sshin struct mbuf *m = *mp; 13853541Sshin register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 13953541Sshin register struct inpcb *in6p; 14053541Sshin struct inpcb *last = 0; 14178064Sume struct mbuf *opts = NULL; 142121901Sume struct sockaddr_in6 fromsa; 14353541Sshin 14478064Sume rip6stat.rip6s_ipackets++; 14578064Sume 14683934Sbrooks if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 14778064Sume /* XXX send icmp6 host/port unreach? */ 14878064Sume m_freem(m); 14978064Sume return IPPROTO_DONE; 15053541Sshin } 15178064Sume 152121901Sume init_sin6(&fromsa, m); /* general init */ 15353541Sshin 154132714Srwatson INP_INFO_RLOCK(&ripcbinfo); 15553541Sshin LIST_FOREACH(in6p, &ripcb, inp_list) { 156132714Srwatson INP_LOCK(in6p); 157132714Srwatson if ((in6p->in6p_vflag & INP_IPV6) == 0) { 158132714Srwatsondocontinue: 159132714Srwatson INP_UNLOCK(in6p); 16053541Sshin continue; 161132714Srwatson } 16253541Sshin if (in6p->in6p_ip6_nxt && 16353541Sshin in6p->in6p_ip6_nxt != proto) 164132714Srwatson goto docontinue; 16553541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 16653541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 167132714Srwatson goto docontinue; 16853541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 16953541Sshin !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 170132714Srwatson goto docontinue; 17178064Sume if (in6p->in6p_cksum != -1) { 17278064Sume rip6stat.rip6s_isum++; 173151459Ssuz if (in6_cksum(m, proto, *offp, 17478064Sume m->m_pkthdr.len - *offp)) { 17578064Sume rip6stat.rip6s_badsum++; 176132714Srwatson goto docontinue; 17778064Sume } 17853541Sshin } 17953541Sshin if (last) { 18053541Sshin struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 18178064Sume 182171167Sgnn#ifdef IPSEC 18378064Sume /* 18478064Sume * Check AH/ESP integrity. 18578064Sume */ 186125396Sume if (n && ipsec6_in_reject(n, last)) { 18778064Sume m_freem(n); 18878064Sume ipsec6stat.in_polvio++; 189105199Ssam /* do not inject data into pcb */ 190105199Ssam } else 191171167Sgnn#endif /* IPSEC */ 19253541Sshin if (n) { 19397658Stanimura if (last->in6p_flags & IN6P_CONTROLOPTS || 19497658Stanimura last->in6p_socket->so_options & SO_TIMESTAMP) 195121674Sume ip6_savecontrol(last, n, &opts); 19653541Sshin /* strip intermediate headers */ 19753541Sshin m_adj(n, *offp); 19853541Sshin if (sbappendaddr(&last->in6p_socket->so_rcv, 199121901Sume (struct sockaddr *)&fromsa, 20053541Sshin n, opts) == 0) { 20153541Sshin m_freem(n); 20253541Sshin if (opts) 20353541Sshin m_freem(opts); 20478064Sume rip6stat.rip6s_fullsock++; 20597658Stanimura } else 20653541Sshin sorwakeup(last->in6p_socket); 20753541Sshin opts = NULL; 20853541Sshin } 209132714Srwatson INP_UNLOCK(last); 21053541Sshin } 21153541Sshin last = in6p; 21253541Sshin } 213171167Sgnn#ifdef IPSEC 21478064Sume /* 21578064Sume * Check AH/ESP integrity. 21678064Sume */ 217125396Sume if (last && ipsec6_in_reject(m, last)) { 21878064Sume m_freem(m); 21978064Sume ipsec6stat.in_polvio++; 220105199Ssam ip6stat.ip6s_delivered--; 221105199Ssam /* do not inject data into pcb */ 222149224Ssuz INP_UNLOCK(last); 223105199Ssam } else 224171167Sgnn#endif /* IPSEC */ 22553541Sshin if (last) { 22697658Stanimura if (last->in6p_flags & IN6P_CONTROLOPTS || 22797658Stanimura last->in6p_socket->so_options & SO_TIMESTAMP) 228121674Sume ip6_savecontrol(last, m, &opts); 22953541Sshin /* strip intermediate headers */ 23053541Sshin m_adj(m, *offp); 23153541Sshin if (sbappendaddr(&last->in6p_socket->so_rcv, 232121901Sume (struct sockaddr *)&fromsa, m, opts) == 0) { 23353541Sshin m_freem(m); 23453541Sshin if (opts) 23553541Sshin m_freem(opts); 23678064Sume rip6stat.rip6s_fullsock++; 23797658Stanimura } else 23853541Sshin sorwakeup(last->in6p_socket); 239132714Srwatson INP_UNLOCK(last); 24053541Sshin } else { 24178064Sume rip6stat.rip6s_nosock++; 24278064Sume if (m->m_flags & M_MCAST) 24378064Sume rip6stat.rip6s_nosockmcast++; 24453541Sshin if (proto == IPPROTO_NONE) 24553541Sshin m_freem(m); 24653541Sshin else { 24753541Sshin char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ 24853541Sshin icmp6_error(m, ICMP6_PARAM_PROB, 24953541Sshin ICMP6_PARAMPROB_NEXTHEADER, 25053541Sshin prvnxtp - mtod(m, char *)); 25153541Sshin } 25253541Sshin ip6stat.ip6s_delivered--; 25353541Sshin } 254134655Srwatson INP_INFO_RUNLOCK(&ripcbinfo); 25553541Sshin return IPPROTO_DONE; 25653541Sshin} 25753541Sshin 25862587Sitojunvoid 259171259Sdelphijrip6_ctlinput(int cmd, struct sockaddr *sa, void *d) 26062587Sitojun{ 26162587Sitojun struct ip6_hdr *ip6; 26262587Sitojun struct mbuf *m; 26362587Sitojun int off = 0; 26478064Sume struct ip6ctlparam *ip6cp = NULL; 26578064Sume const struct sockaddr_in6 *sa6_src = NULL; 266125776Sume void *cmdarg; 26798211Shsu struct inpcb *(*notify) __P((struct inpcb *, int)) = in6_rtchange; 26862587Sitojun 26962587Sitojun if (sa->sa_family != AF_INET6 || 27062587Sitojun sa->sa_len != sizeof(struct sockaddr_in6)) 27162587Sitojun return; 27262587Sitojun 27362587Sitojun if ((unsigned)cmd >= PRC_NCMDS) 27462587Sitojun return; 27562587Sitojun if (PRC_IS_REDIRECT(cmd)) 27662587Sitojun notify = in6_rtchange, d = NULL; 27762587Sitojun else if (cmd == PRC_HOSTDEAD) 27862587Sitojun d = NULL; 27962587Sitojun else if (inet6ctlerrmap[cmd] == 0) 28062587Sitojun return; 28162587Sitojun 28262587Sitojun /* if the parameter is from icmp6, decode it. */ 28362587Sitojun if (d != NULL) { 28478064Sume ip6cp = (struct ip6ctlparam *)d; 28562587Sitojun m = ip6cp->ip6c_m; 28662587Sitojun ip6 = ip6cp->ip6c_ip6; 28762587Sitojun off = ip6cp->ip6c_off; 288125776Sume cmdarg = ip6cp->ip6c_cmdarg; 28978064Sume sa6_src = ip6cp->ip6c_src; 29062587Sitojun } else { 29162587Sitojun m = NULL; 29262587Sitojun ip6 = NULL; 293125776Sume cmdarg = NULL; 29478064Sume sa6_src = &sa6_any; 29562587Sitojun } 29662587Sitojun 297133192Srwatson (void) in6_pcbnotify(&ripcbinfo, sa, 0, 298133192Srwatson (const struct sockaddr *)sa6_src, 299125776Sume 0, cmd, cmdarg, notify); 30062587Sitojun} 30162587Sitojun 30253541Sshin/* 30353541Sshin * Generate IPv6 header and pass packet to ip6_output. 30453541Sshin * Tack on options user may have setup with control call. 30553541Sshin */ 30653541Sshinint 30753541Sshin#if __STDC__ 30853541Sshinrip6_output(struct mbuf *m, ...) 30953541Sshin#else 31053541Sshinrip6_output(m, va_alist) 31153541Sshin struct mbuf *m; 31253541Sshin va_dcl 31353541Sshin#endif 31453541Sshin{ 315120941Sume struct mbuf *control; 31653541Sshin struct socket *so; 31753541Sshin struct sockaddr_in6 *dstsock; 31853541Sshin struct in6_addr *dst; 31953541Sshin struct ip6_hdr *ip6; 32053541Sshin struct inpcb *in6p; 32153541Sshin u_int plen = m->m_pkthdr.len; 32253541Sshin int error = 0; 323148247Sume struct ip6_pktopts opt, *optp; 32453541Sshin struct ifnet *oifp = NULL; 32553541Sshin int type = 0, code = 0; /* for ICMPv6 output statistics only */ 32653541Sshin int priv = 0; 327148385Sume int scope_ambiguous = 0; 328121472Sume struct in6_addr *in6a; 32953541Sshin va_list ap; 33053541Sshin 33153541Sshin va_start(ap, m); 33253541Sshin so = va_arg(ap, struct socket *); 33353541Sshin dstsock = va_arg(ap, struct sockaddr_in6 *); 33453541Sshin control = va_arg(ap, struct mbuf *); 33553541Sshin va_end(ap); 33653541Sshin 33753541Sshin in6p = sotoin6pcb(so); 338132714Srwatson INP_LOCK(in6p); 33953541Sshin 34053541Sshin priv = 0; 341170587Srwatson if (suser_cred(so->so_cred, 0) == 0) 34253541Sshin priv = 1; 34353541Sshin dst = &dstsock->sin6_addr; 34453541Sshin if (control) { 345148242Sume if ((error = ip6_setpktopts(control, &opt, 346148250Sume in6p->in6p_outputopts, priv, so->so_proto->pr_protocol)) 347121472Sume != 0) { 34853541Sshin goto bad; 349121472Sume } 350148247Sume optp = &opt; 351148247Sume } else 352148247Sume optp = in6p->in6p_outputopts; 35353541Sshin 35453541Sshin /* 355148385Sume * Check and convert scope zone ID into internal form. 356148385Sume * XXX: we may still need to determine the zone later. 357148385Sume */ 358148385Sume if (!(so->so_state & SS_ISCONNECTED)) { 359148385Sume if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone) 360148385Sume scope_ambiguous = 1; 361148385Sume if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0) 362148385Sume goto bad; 363148385Sume } 364148385Sume 365148385Sume /* 36653541Sshin * For an ICMPv6 packet, we should know its type and code 36753541Sshin * to update statistics. 36853541Sshin */ 36953541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 37053541Sshin struct icmp6_hdr *icmp6; 37153541Sshin if (m->m_len < sizeof(struct icmp6_hdr) && 37253541Sshin (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 37353541Sshin error = ENOBUFS; 37453541Sshin goto bad; 37553541Sshin } 37653541Sshin icmp6 = mtod(m, struct icmp6_hdr *); 37753541Sshin type = icmp6->icmp6_type; 37853541Sshin code = icmp6->icmp6_code; 37953541Sshin } 38053541Sshin 381133592Srwatson M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 382133592Srwatson if (m == NULL) { 383133592Srwatson error = ENOBUFS; 384133592Srwatson goto bad; 385133592Srwatson } 38653541Sshin ip6 = mtod(m, struct ip6_hdr *); 38753541Sshin 38853541Sshin /* 38953541Sshin * Source address selection. 39053541Sshin */ 391148247Sume if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, NULL, 392148385Sume &in6p->in6p_laddr, &oifp, &error)) == NULL) { 393121472Sume if (error == 0) 394121472Sume error = EADDRNOTAVAIL; 395121472Sume goto bad; 39653541Sshin } 397121472Sume ip6->ip6_src = *in6a; 398148385Sume 399148385Sume if (oifp && scope_ambiguous) { 400148385Sume /* 401148385Sume * Application should provide a proper zone ID or the use of 402148385Sume * default zone IDs should be enabled. Unfortunately, some 403148385Sume * applications do not behave as it should, so we need a 404148385Sume * workaround. Even if an appropriate ID is not determined 405148385Sume * (when it's required), if we can determine the outgoing 406148385Sume * interface. determine the zone ID based on the interface. 407148385Sume */ 408148385Sume error = in6_setscope(&dstsock->sin6_addr, oifp, NULL); 409148385Sume if (error != 0) 410148385Sume goto bad; 411148385Sume } 412148385Sume ip6->ip6_dst = dstsock->sin6_addr; 413148385Sume 414148385Sume /* fill in the rest of the IPv6 header fields */ 41555009Sshin ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | 41655009Sshin (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); 41755009Sshin ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | 41855009Sshin (IPV6_VERSION & IPV6_VERSION_MASK); 41953541Sshin /* ip6_plen will be filled in ip6_output, so not fill it here. */ 42053541Sshin ip6->ip6_nxt = in6p->in6p_ip6_nxt; 42153541Sshin ip6->ip6_hlim = in6_selecthlim(in6p, oifp); 42253541Sshin 42353541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 42453541Sshin in6p->in6p_cksum != -1) { 42553541Sshin struct mbuf *n; 42653541Sshin int off; 42753541Sshin u_int16_t *p; 42853541Sshin 42953541Sshin /* compute checksum */ 43053541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 43153541Sshin off = offsetof(struct icmp6_hdr, icmp6_cksum); 43253541Sshin else 43353541Sshin off = in6p->in6p_cksum; 43453541Sshin if (plen < off + 1) { 43553541Sshin error = EINVAL; 43653541Sshin goto bad; 43753541Sshin } 43853541Sshin off += sizeof(struct ip6_hdr); 43953541Sshin 44053541Sshin n = m; 44153541Sshin while (n && n->m_len <= off) { 44253541Sshin off -= n->m_len; 44353541Sshin n = n->m_next; 44453541Sshin } 44553541Sshin if (!n) 44653541Sshin goto bad; 44753541Sshin p = (u_int16_t *)(mtod(n, caddr_t) + off); 44853541Sshin *p = 0; 44953541Sshin *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 45053541Sshin } 45153541Sshin 452148247Sume error = ip6_output(m, optp, NULL, 0, in6p->in6p_moptions, &oifp, in6p); 45353541Sshin if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 45453541Sshin if (oifp) 45553541Sshin icmp6_ifoutstat_inc(oifp, type, code); 45653541Sshin icmp6stat.icp6s_outhist[type]++; 45778064Sume } else 45878064Sume rip6stat.rip6s_opackets++; 45953541Sshin 46053541Sshin goto freectl; 46153541Sshin 46253541Sshin bad: 46353541Sshin if (m) 46453541Sshin m_freem(m); 46553541Sshin 46653541Sshin freectl: 46778064Sume if (control) { 468148247Sume ip6_clearpktopts(&opt, -1); 46953541Sshin m_freem(control); 47078064Sume } 471132714Srwatson INP_UNLOCK(in6p); 472120856Sume return (error); 47353541Sshin} 47453541Sshin 47553541Sshin/* 47653541Sshin * Raw IPv6 socket option processing. 47753541Sshin */ 47853541Sshinint 479171259Sdelphijrip6_ctloutput(struct socket *so, struct sockopt *sopt) 48053541Sshin{ 48153541Sshin int error; 48253541Sshin 48353541Sshin if (sopt->sopt_level == IPPROTO_ICMPV6) 48453541Sshin /* 48553541Sshin * XXX: is it better to call icmp6_ctloutput() directly 48653541Sshin * from protosw? 48753541Sshin */ 488120856Sume return (icmp6_ctloutput(so, sopt)); 48953541Sshin else if (sopt->sopt_level != IPPROTO_IPV6) 49053541Sshin return (EINVAL); 49153541Sshin 49253541Sshin error = 0; 49353541Sshin 49453541Sshin switch (sopt->sopt_dir) { 49553541Sshin case SOPT_GET: 49653541Sshin switch (sopt->sopt_name) { 49756723Sshin case MRT6_INIT: 49856723Sshin case MRT6_DONE: 49956723Sshin case MRT6_ADD_MIF: 50056723Sshin case MRT6_DEL_MIF: 50156723Sshin case MRT6_ADD_MFC: 50256723Sshin case MRT6_DEL_MFC: 50356723Sshin case MRT6_PIM: 504166938Sbms error = ip6_mrouter_get ? ip6_mrouter_get(so, sopt) : 505166938Sbms EOPNOTSUPP; 50656723Sshin break; 507121578Sume case IPV6_CHECKSUM: 508121578Sume error = ip6_raw_ctloutput(so, sopt); 509121578Sume break; 51053541Sshin default: 51153541Sshin error = ip6_ctloutput(so, sopt); 51253541Sshin break; 51353541Sshin } 51453541Sshin break; 51553541Sshin 51653541Sshin case SOPT_SET: 51753541Sshin switch (sopt->sopt_name) { 51856723Sshin case MRT6_INIT: 51956723Sshin case MRT6_DONE: 52056723Sshin case MRT6_ADD_MIF: 52156723Sshin case MRT6_DEL_MIF: 52256723Sshin case MRT6_ADD_MFC: 52356723Sshin case MRT6_DEL_MFC: 52456723Sshin case MRT6_PIM: 525166938Sbms error = ip6_mrouter_set ? ip6_mrouter_set(so, sopt) : 526166938Sbms EOPNOTSUPP; 52756723Sshin break; 528121578Sume case IPV6_CHECKSUM: 529121578Sume error = ip6_raw_ctloutput(so, sopt); 530121578Sume break; 53153541Sshin default: 53253541Sshin error = ip6_ctloutput(so, sopt); 53353541Sshin break; 53453541Sshin } 53553541Sshin break; 53653541Sshin } 53753541Sshin 53853541Sshin return (error); 53953541Sshin} 54053541Sshin 54153541Sshinstatic int 54283366Sjulianrip6_attach(struct socket *so, int proto, struct thread *td) 54353541Sshin{ 54453541Sshin struct inpcb *inp; 545144261Ssam struct icmp6_filter *filter; 546157676Srwatson int error; 54753541Sshin 54853541Sshin inp = sotoinpcb(so); 549157374Srwatson KASSERT(inp == NULL, ("rip6_attach: inp != NULL")); 550157374Srwatson if (td && (error = suser(td)) != 0) 55153541Sshin return error; 55255009Sshin error = soreserve(so, rip_sendspace, rip_recvspace); 553157374Srwatson if (error) 55455009Sshin return error; 555144261Ssam MALLOC(filter, struct icmp6_filter *, 556144261Ssam sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); 557157374Srwatson if (filter == NULL) 558144261Ssam return ENOMEM; 559157374Srwatson INP_INFO_WLOCK(&ripcbinfo); 560160491Sups error = in_pcballoc(so, &ripcbinfo); 561132714Srwatson if (error) { 562132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 563144261Ssam FREE(filter, M_PCB); 56453541Sshin return error; 565132714Srwatson } 56653541Sshin inp = (struct inpcb *)so->so_pcb; 567132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 56853541Sshin inp->inp_vflag |= INP_IPV6; 56953541Sshin inp->in6p_ip6_nxt = (long)proto; 57053541Sshin inp->in6p_hops = -1; /* use kernel default */ 57153541Sshin inp->in6p_cksum = -1; 572144261Ssam inp->in6p_icmp6filt = filter; 57353541Sshin ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); 574132714Srwatson INP_UNLOCK(inp); 57553541Sshin return 0; 57653541Sshin} 57753541Sshin 578157370Srwatsonstatic void 57953541Sshinrip6_detach(struct socket *so) 58053541Sshin{ 58153541Sshin struct inpcb *inp; 58253541Sshin 58353541Sshin inp = sotoinpcb(so); 584157374Srwatson KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); 585160549Srwatson 586166938Sbms if (so == ip6_mrouter && ip6_mrouter_done) 587166938Sbms ip6_mrouter_done(); 58853541Sshin /* xxx: RSVP */ 589160549Srwatson INP_INFO_WLOCK(&ripcbinfo); 590160549Srwatson INP_LOCK(inp); 59153541Sshin if (inp->in6p_icmp6filt) { 59253541Sshin FREE(inp->in6p_icmp6filt, M_PCB); 59353541Sshin inp->in6p_icmp6filt = NULL; 59453541Sshin } 59553541Sshin in6_pcbdetach(inp); 596157374Srwatson in6_pcbfree(inp); 597132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 59853541Sshin} 59953541Sshin 600160549Srwatson/* XXXRW: This can't ever be called. */ 601157366Srwatsonstatic void 60253541Sshinrip6_abort(struct socket *so) 60353541Sshin{ 604160549Srwatson struct inpcb *inp; 605160549Srwatson 606160549Srwatson inp = sotoinpcb(so); 607160549Srwatson KASSERT(inp != NULL, ("rip6_abort: inp == NULL")); 608160549Srwatson 60953541Sshin soisdisconnected(so); 61053541Sshin} 61153541Sshin 612160549Srwatsonstatic void 613160549Srwatsonrip6_close(struct socket *so) 614160549Srwatson{ 615160549Srwatson struct inpcb *inp; 616160549Srwatson 617160549Srwatson inp = sotoinpcb(so); 618160549Srwatson KASSERT(inp != NULL, ("rip6_close: inp == NULL")); 619160549Srwatson 620160549Srwatson soisdisconnected(so); 621160549Srwatson} 622160549Srwatson 62353541Sshinstatic int 62453541Sshinrip6_disconnect(struct socket *so) 62553541Sshin{ 62653541Sshin struct inpcb *inp = sotoinpcb(so); 62753541Sshin 62897658Stanimura if ((so->so_state & SS_ISCONNECTED) == 0) 62953541Sshin return ENOTCONN; 63053541Sshin inp->in6p_faddr = in6addr_any; 631157366Srwatson rip6_abort(so); 632157374Srwatson return (0); 63353541Sshin} 63453541Sshin 63553541Sshinstatic int 63683366Sjulianrip6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 63753541Sshin{ 63853541Sshin struct inpcb *inp = sotoinpcb(so); 63953541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 64053541Sshin struct ifaddr *ia = NULL; 641148385Sume int error = 0; 64253541Sshin 643157374Srwatson KASSERT(inp != NULL, ("rip6_bind: inp == NULL")); 64453541Sshin if (nam->sa_len != sizeof(*addr)) 64553541Sshin return EINVAL; 64653541Sshin if (TAILQ_EMPTY(&ifnet) || addr->sin6_family != AF_INET6) 64753541Sshin return EADDRNOTAVAIL; 648148385Sume if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) 649148385Sume return(error); 650148385Sume 65153541Sshin if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && 65253541Sshin (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) 65353541Sshin return EADDRNOTAVAIL; 65453541Sshin if (ia && 65553541Sshin ((struct in6_ifaddr *)ia)->ia6_flags & 65653541Sshin (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| 65753541Sshin IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { 658120856Sume return (EADDRNOTAVAIL); 65953541Sshin } 660132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 661132714Srwatson INP_LOCK(inp); 66253541Sshin inp->in6p_laddr = addr->sin6_addr; 663132714Srwatson INP_UNLOCK(inp); 664132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 66553541Sshin return 0; 66653541Sshin} 66753541Sshin 66853541Sshinstatic int 66983366Sjulianrip6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 67053541Sshin{ 67153541Sshin struct inpcb *inp = sotoinpcb(so); 67253541Sshin struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; 67353541Sshin struct in6_addr *in6a = NULL; 674148385Sume struct ifnet *ifp = NULL; 675148385Sume int error = 0, scope_ambiguous = 0; 67653541Sshin 677157374Srwatson KASSERT(inp != NULL, ("rip6_connect: inp == NULL")); 67853541Sshin if (nam->sa_len != sizeof(*addr)) 67953541Sshin return EINVAL; 68053541Sshin if (TAILQ_EMPTY(&ifnet)) 68153541Sshin return EADDRNOTAVAIL; 68253541Sshin if (addr->sin6_family != AF_INET6) 68353541Sshin return EAFNOSUPPORT; 684148385Sume 685148385Sume /* 686148385Sume * Application should provide a proper zone ID or the use of 687148385Sume * default zone IDs should be enabled. Unfortunately, some 688148385Sume * applications do not behave as it should, so we need a 689148385Sume * workaround. Even if an appropriate ID is not determined, 690148385Sume * we'll see if we can determine the outgoing interface. If we 691148385Sume * can, determine the zone ID based on the interface below. 692148385Sume */ 693148385Sume if (addr->sin6_scope_id == 0 && !ip6_use_defzone) 694148385Sume scope_ambiguous = 1; 695148385Sume if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) 696148385Sume return(error); 697148385Sume 698132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 699132714Srwatson INP_LOCK(inp); 70053541Sshin /* Source address selection. XXX: need pcblookup? */ 70153541Sshin in6a = in6_selectsrc(addr, inp->in6p_outputopts, 702122927Sandre inp->in6p_moptions, NULL, 703148385Sume &inp->in6p_laddr, &ifp, &error); 704132714Srwatson if (in6a == NULL) { 705132714Srwatson INP_UNLOCK(inp); 706132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 70753541Sshin return (error ? error : EADDRNOTAVAIL); 708132714Srwatson } 709148385Sume 710148385Sume /* XXX: see above */ 711148385Sume if (ifp && scope_ambiguous && 712148385Sume (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) { 713148385Sume INP_UNLOCK(inp); 714148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 715148385Sume return(error); 716148385Sume } 717148385Sume inp->in6p_faddr = addr->sin6_addr; 71853541Sshin inp->in6p_laddr = *in6a; 71953541Sshin soisconnected(so); 720132714Srwatson INP_UNLOCK(inp); 721132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 72253541Sshin return 0; 72353541Sshin} 72453541Sshin 72553541Sshinstatic int 72653541Sshinrip6_shutdown(struct socket *so) 72753541Sshin{ 728132714Srwatson struct inpcb *inp; 729132714Srwatson 730132714Srwatson inp = sotoinpcb(so); 731157374Srwatson KASSERT(inp != NULL, ("rip6_shutdown: inp == NULL")); 732132714Srwatson INP_LOCK(inp); 73353541Sshin socantsendmore(so); 734132714Srwatson INP_UNLOCK(inp); 73553541Sshin return 0; 73653541Sshin} 73753541Sshin 73853541Sshinstatic int 73953541Sshinrip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 74083366Sjulian struct mbuf *control, struct thread *td) 74153541Sshin{ 74253541Sshin struct inpcb *inp = sotoinpcb(so); 74353541Sshin struct sockaddr_in6 tmp; 74453541Sshin struct sockaddr_in6 *dst; 745132714Srwatson int ret; 74653541Sshin 747157374Srwatson KASSERT(inp != NULL, ("rip6_send: inp == NULL")); 748132714Srwatson INP_INFO_WLOCK(&ripcbinfo); 74962587Sitojun /* always copy sockaddr to avoid overwrites */ 750132714Srwatson /* Unlocked read. */ 75153541Sshin if (so->so_state & SS_ISCONNECTED) { 75253541Sshin if (nam) { 753132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 75453541Sshin m_freem(m); 75553541Sshin return EISCONN; 75653541Sshin } 75753541Sshin /* XXX */ 75853541Sshin bzero(&tmp, sizeof(tmp)); 75953541Sshin tmp.sin6_family = AF_INET6; 76053541Sshin tmp.sin6_len = sizeof(struct sockaddr_in6); 76153541Sshin bcopy(&inp->in6p_faddr, &tmp.sin6_addr, 76253541Sshin sizeof(struct in6_addr)); 76353541Sshin dst = &tmp; 76453541Sshin } else { 76553541Sshin if (nam == NULL) { 766132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 76753541Sshin m_freem(m); 76853541Sshin return ENOTCONN; 76953541Sshin } 770148385Sume if (nam->sa_len != sizeof(struct sockaddr_in6)) { 771148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 772148385Sume m_freem(m); 773148385Sume return(EINVAL); 774148385Sume } 77562587Sitojun tmp = *(struct sockaddr_in6 *)nam; 77662587Sitojun dst = &tmp; 777148385Sume 778148385Sume if (dst->sin6_family == AF_UNSPEC) { 779148385Sume /* 780148385Sume * XXX: we allow this case for backward 781148385Sume * compatibility to buggy applications that 782148385Sume * rely on old (and wrong) kernel behavior. 783148385Sume */ 784148385Sume log(LOG_INFO, "rip6 SEND: address family is " 785148385Sume "unspec. Assume AF_INET6\n"); 786148385Sume dst->sin6_family = AF_INET6; 787148385Sume } else if (dst->sin6_family != AF_INET6) { 788148385Sume INP_INFO_WUNLOCK(&ripcbinfo); 789148385Sume m_freem(m); 790148385Sume return(EAFNOSUPPORT); 791148385Sume } 79253541Sshin } 793132714Srwatson ret = rip6_output(m, so, dst, control); 794132714Srwatson INP_INFO_WUNLOCK(&ripcbinfo); 795132714Srwatson return (ret); 79653541Sshin} 79753541Sshin 79853541Sshinstruct pr_usrreqs rip6_usrreqs = { 799137386Sphk .pru_abort = rip6_abort, 800137386Sphk .pru_attach = rip6_attach, 801137386Sphk .pru_bind = rip6_bind, 802137386Sphk .pru_connect = rip6_connect, 803137386Sphk .pru_control = in6_control, 804137386Sphk .pru_detach = rip6_detach, 805137386Sphk .pru_disconnect = rip6_disconnect, 806169462Srwatson .pru_peeraddr = in6_getpeeraddr, 807137386Sphk .pru_send = rip6_send, 808137386Sphk .pru_shutdown = rip6_shutdown, 809169462Srwatson .pru_sockaddr = in6_getsockaddr, 810160549Srwatson .pru_close = rip6_close, 81153541Sshin}; 812