1*ea7e0400Sbluhm /* $OpenBSD: raw_ip6.c,v 1.186 2024/11/08 10:24:13 bluhm Exp $ */ 20c5914f6Sitojun /* $KAME: raw_ip6.c,v 1.69 2001/03/04 15:55:44 itojun Exp $ */ 3ac1b2aeeSitojun 4ac1b2aeeSitojun /* 5ac1b2aeeSitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6ac1b2aeeSitojun * All rights reserved. 7ac1b2aeeSitojun * 8ac1b2aeeSitojun * Redistribution and use in source and binary forms, with or without 9ac1b2aeeSitojun * modification, are permitted provided that the following conditions 10ac1b2aeeSitojun * are met: 11ac1b2aeeSitojun * 1. Redistributions of source code must retain the above copyright 12ac1b2aeeSitojun * notice, this list of conditions and the following disclaimer. 13ac1b2aeeSitojun * 2. Redistributions in binary form must reproduce the above copyright 14ac1b2aeeSitojun * notice, this list of conditions and the following disclaimer in the 15ac1b2aeeSitojun * documentation and/or other materials provided with the distribution. 16ac1b2aeeSitojun * 3. Neither the name of the project nor the names of its contributors 17ac1b2aeeSitojun * may be used to endorse or promote products derived from this software 18ac1b2aeeSitojun * without specific prior written permission. 19ac1b2aeeSitojun * 20ac1b2aeeSitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21ac1b2aeeSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22ac1b2aeeSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23ac1b2aeeSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24ac1b2aeeSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25ac1b2aeeSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26ac1b2aeeSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27ac1b2aeeSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28ac1b2aeeSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29ac1b2aeeSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30ac1b2aeeSitojun * SUCH DAMAGE. 31ac1b2aeeSitojun */ 32ac1b2aeeSitojun 33ac1b2aeeSitojun /* 34ac1b2aeeSitojun * Copyright (c) 1982, 1986, 1988, 1993 35ac1b2aeeSitojun * The Regents of the University of California. All rights reserved. 36ac1b2aeeSitojun * 37ac1b2aeeSitojun * Redistribution and use in source and binary forms, with or without 38ac1b2aeeSitojun * modification, are permitted provided that the following conditions 39ac1b2aeeSitojun * are met: 40ac1b2aeeSitojun * 1. Redistributions of source code must retain the above copyright 41ac1b2aeeSitojun * notice, this list of conditions and the following disclaimer. 42ac1b2aeeSitojun * 2. Redistributions in binary form must reproduce the above copyright 43ac1b2aeeSitojun * notice, this list of conditions and the following disclaimer in the 44ac1b2aeeSitojun * documentation and/or other materials provided with the distribution. 4529295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 46ac1b2aeeSitojun * may be used to endorse or promote products derived from this software 47ac1b2aeeSitojun * without specific prior written permission. 48ac1b2aeeSitojun * 49ac1b2aeeSitojun * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50ac1b2aeeSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51ac1b2aeeSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52ac1b2aeeSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53ac1b2aeeSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54ac1b2aeeSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55ac1b2aeeSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56ac1b2aeeSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57ac1b2aeeSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58ac1b2aeeSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59ac1b2aeeSitojun * SUCH DAMAGE. 60ac1b2aeeSitojun * 61ac1b2aeeSitojun * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 62ac1b2aeeSitojun */ 63ac1b2aeeSitojun 64f49dc28fSbluhm #include "pf.h" 65f49dc28fSbluhm 66ac1b2aeeSitojun #include <sys/param.h> 67ac1b2aeeSitojun #include <sys/malloc.h> 68ac1b2aeeSitojun #include <sys/mbuf.h> 69ac1b2aeeSitojun #include <sys/socket.h> 70ac1b2aeeSitojun #include <sys/protosw.h> 71ac1b2aeeSitojun #include <sys/socketvar.h> 72ac1b2aeeSitojun #include <sys/errno.h> 73ac1b2aeeSitojun #include <sys/systm.h> 74ab46c28dSderaadt #include <sys/sysctl.h> 75ac1b2aeeSitojun 76ac1b2aeeSitojun #include <net/if.h> 770deb6685Smpi #include <net/if_var.h> 78ac1b2aeeSitojun #include <net/route.h> 79ac1b2aeeSitojun 80ac1b2aeeSitojun #include <netinet/in.h> 81dc572864Sbluhm #include <netinet6/in6_var.h> 82ac1b2aeeSitojun #include <netinet/ip6.h> 83ac1b2aeeSitojun #include <netinet6/ip6_var.h> 84d9c6379eSclaudio #ifdef MROUTING 85ac1b2aeeSitojun #include <netinet6/ip6_mroute.h> 86d9c6379eSclaudio #endif 87ac1b2aeeSitojun #include <netinet/icmp6.h> 88ac1b2aeeSitojun #include <netinet/ip.h> 89ac1b2aeeSitojun #include <netinet/in_pcb.h> 90ac1b2aeeSitojun #include <netinet6/nd6.h> 91ac1b2aeeSitojun #include <netinet6/ip6protosw.h> 92f5bfbd04Sitojun #include <netinet6/raw_ip6.h> 93ac1b2aeeSitojun 94377da2e0Sderaadt #if NPF > 0 95377da2e0Sderaadt #include <net/pfvar.h> 96377da2e0Sderaadt #endif 97377da2e0Sderaadt 987034f964Sespie #include <sys/stdarg.h> 99ac1b2aeeSitojun 100ac1b2aeeSitojun /* 101ac1b2aeeSitojun * Raw interface to IP6 protocol. 102ac1b2aeeSitojun */ 103ac1b2aeeSitojun 104ac1b2aeeSitojun struct inpcbtable rawin6pcbtable; 105ac1b2aeeSitojun 10607ba5f7cSjca struct cpumem *rip6counters; 107f5bfbd04Sitojun 1087985bfd0Smvs const struct pr_usrreqs rip6_usrreqs = { 1097985bfd0Smvs .pru_attach = rip6_attach, 1107985bfd0Smvs .pru_detach = rip6_detach, 111121fc5cfSmvs .pru_bind = rip6_bind, 112074c8388Smvs .pru_connect = rip6_connect, 113e00787e6Smvs .pru_disconnect = rip6_disconnect, 11486e05c94Smvs .pru_shutdown = rip6_shutdown, 11590b3510cSmvs .pru_send = rip6_send, 1163f68dcd3Smvs .pru_control = in6_control, 1170dc53d81Smvs .pru_sockaddr = in6_sockaddr, 118c3a3d609Smvs .pru_peeraddr = in6_peeraddr, 1197985bfd0Smvs }; 1207985bfd0Smvs 121*ea7e0400Sbluhm void rip6_sbappend(struct inpcb *, struct mbuf *, struct ip6_hdr *, int, 122*ea7e0400Sbluhm struct sockaddr_in6 *); 123*ea7e0400Sbluhm 124ac1b2aeeSitojun /* 125ac1b2aeeSitojun * Initialize raw connection block queue. 126ac1b2aeeSitojun */ 127ac1b2aeeSitojun void 128c799dc6dSnaddy rip6_init(void) 129ac1b2aeeSitojun { 130ac1b2aeeSitojun in_pcbinit(&rawin6pcbtable, 1); 13107ba5f7cSjca rip6counters = counters_alloc(rip6s_ncounters); 132ac1b2aeeSitojun } 133ac1b2aeeSitojun 134ac1b2aeeSitojun int 135459fa0feSbluhm rip6_input(struct mbuf **mp, int *offp, int proto, int af) 136ac1b2aeeSitojun { 137ac1b2aeeSitojun struct mbuf *m = *mp; 138ac1b2aeeSitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 139*ea7e0400Sbluhm struct inpcb_iterator iter = { .inp_table = NULL }; 140*ea7e0400Sbluhm struct inpcb *inp, *last; 141c7745dcbSbluhm struct in6_addr *key; 142ac1b2aeeSitojun struct sockaddr_in6 rip6src; 143bf088e2bSbluhm uint8_t type; 144ac1b2aeeSitojun 14566467af9Sbluhm KASSERT(af == AF_INET6); 14666467af9Sbluhm 147bf088e2bSbluhm if (proto == IPPROTO_ICMPV6) { 148bf088e2bSbluhm struct icmp6_hdr *icmp6; 149bf088e2bSbluhm 150bf088e2bSbluhm IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, *offp, 151bf088e2bSbluhm sizeof(*icmp6)); 152bf088e2bSbluhm if (icmp6 == NULL) 153bf088e2bSbluhm return IPPROTO_DONE; 154bf088e2bSbluhm type = icmp6->icmp6_type; 155bf088e2bSbluhm } else 15607ba5f7cSjca rip6stat_inc(rip6s_ipackets); 157f5bfbd04Sitojun 158a3425987Sbluhm memset(&rip6src, 0, sizeof(rip6src)); 159ac1b2aeeSitojun rip6src.sin6_family = AF_INET6; 160a3425987Sbluhm rip6src.sin6_len = sizeof(rip6src); 161ac1b2aeeSitojun /* KAME hack: recover scopeid */ 1626c8dd0e9Sclaudio in6_recoverscope(&rip6src, &ip6->ip6_src); 163ac1b2aeeSitojun 164c7745dcbSbluhm key = &ip6->ip6_dst; 165c7745dcbSbluhm #if NPF > 0 166c7745dcbSbluhm if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) { 167c7745dcbSbluhm struct pf_divert *divert; 168c7745dcbSbluhm 169c7745dcbSbluhm divert = pf_find_divert(m); 170c7745dcbSbluhm KASSERT(divert != NULL); 171c7745dcbSbluhm switch (divert->type) { 172c7745dcbSbluhm case PF_DIVERT_TO: 173c7745dcbSbluhm key = &divert->addr.v6; 174c7745dcbSbluhm break; 175c7745dcbSbluhm case PF_DIVERT_REPLY: 176c7745dcbSbluhm break; 177c7745dcbSbluhm default: 178c7745dcbSbluhm panic("%s: unknown divert type %d, mbuf %p, divert %p", 179c7745dcbSbluhm __func__, divert->type, m, divert); 180c7745dcbSbluhm } 181c7745dcbSbluhm } 182c7745dcbSbluhm #endif 183dc28d33fSbluhm mtx_enter(&rawin6pcbtable.inpt_mtx); 184*ea7e0400Sbluhm last = inp = NULL; 185*ea7e0400Sbluhm while ((inp = in_pcb_iterator(&rawin6pcbtable, inp, &iter)) != NULL) { 186e3a7b014Sbluhm KASSERT(ISSET(inp->inp_flags, INP_IPV6)); 187e3a7b014Sbluhm 188a3425987Sbluhm /* 189a3425987Sbluhm * Packet must not be inserted after disconnected wakeup 190a3425987Sbluhm * call. To avoid race, check again when holding receive 191a3425987Sbluhm * buffer mutex. 192a3425987Sbluhm */ 193a3425987Sbluhm if (ISSET(READ_ONCE(inp->inp_socket->so_rcv.sb_state), 194a3425987Sbluhm SS_CANTRCVMORE)) 1958a4cdb97Sclaudio continue; 196921ffa12Sbluhm if (rtable_l2(inp->inp_rtableid) != 19730c05804Skn rtable_l2(m->m_pkthdr.ph_rtableid)) 19830c05804Skn continue; 19930c05804Skn 200921ffa12Sbluhm if ((inp->inp_ipv6.ip6_nxt || proto == IPPROTO_ICMPV6) && 201921ffa12Sbluhm inp->inp_ipv6.ip6_nxt != proto) 202ac1b2aeeSitojun continue; 203921ffa12Sbluhm if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) && 204921ffa12Sbluhm !IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key)) 205ac1b2aeeSitojun continue; 206921ffa12Sbluhm if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6) && 207921ffa12Sbluhm !IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, &ip6->ip6_src)) 208ac1b2aeeSitojun continue; 209921ffa12Sbluhm if (proto == IPPROTO_ICMPV6 && inp->inp_icmp6filt) { 210921ffa12Sbluhm if (ICMP6_FILTER_WILLBLOCK(type, inp->inp_icmp6filt)) 2112ed633ffSbluhm continue; 2122ed633ffSbluhm } 213921ffa12Sbluhm if (proto != IPPROTO_ICMPV6 && inp->inp_cksum6 != -1) { 21407ba5f7cSjca rip6stat_inc(rip6s_isum); 215c526b7b2Sbluhm /* 216c526b7b2Sbluhm * Although in6_cksum() does not need the position of 217c526b7b2Sbluhm * the checksum field for verification, enforce that it 218c526b7b2Sbluhm * is located within the packet. Userland has given 219c526b7b2Sbluhm * a checksum offset, a packet too short for that is 220c526b7b2Sbluhm * invalid. Avoid overflow with user supplied offset. 221c526b7b2Sbluhm */ 222c526b7b2Sbluhm if (m->m_pkthdr.len < *offp + 2 || 223921ffa12Sbluhm m->m_pkthdr.len - *offp - 2 < inp->inp_cksum6 || 224c526b7b2Sbluhm in6_cksum(m, proto, *offp, 225f5bfbd04Sitojun m->m_pkthdr.len - *offp)) { 22607ba5f7cSjca rip6stat_inc(rip6s_badsum); 227ac1b2aeeSitojun continue; 228ac1b2aeeSitojun } 229f5bfbd04Sitojun } 2302be5be2cSbluhm 231*ea7e0400Sbluhm if (last != NULL) { 232*ea7e0400Sbluhm struct mbuf *n; 233*ea7e0400Sbluhm 234*ea7e0400Sbluhm mtx_leave(&rawin6pcbtable.inpt_mtx); 235*ea7e0400Sbluhm 236*ea7e0400Sbluhm n = m_copym(m, 0, M_COPYALL, M_NOWAIT); 237*ea7e0400Sbluhm if (n != NULL) 238*ea7e0400Sbluhm rip6_sbappend(last, n, ip6, *offp, &rip6src); 239*ea7e0400Sbluhm in_pcbunref(last); 240*ea7e0400Sbluhm 241*ea7e0400Sbluhm mtx_enter(&rawin6pcbtable.inpt_mtx); 242*ea7e0400Sbluhm } 243*ea7e0400Sbluhm last = in_pcbref(inp); 244ac1b2aeeSitojun } 245dc28d33fSbluhm mtx_leave(&rawin6pcbtable.inpt_mtx); 246dc28d33fSbluhm 247*ea7e0400Sbluhm if (last == NULL) { 24831e14cacSjca struct counters_ref ref; 24931e14cacSjca uint64_t *counters; 25031e14cacSjca 2512ed633ffSbluhm if (proto != IPPROTO_ICMPV6) { 25207ba5f7cSjca rip6stat_inc(rip6s_nosock); 253f5bfbd04Sitojun if (m->m_flags & M_MCAST) 25407ba5f7cSjca rip6stat_inc(rip6s_nosockmcast); 2552ed633ffSbluhm } 2562ed633ffSbluhm if (proto == IPPROTO_NONE || proto == IPPROTO_ICMPV6) { 257ac1b2aeeSitojun m_freem(m); 2582ed633ffSbluhm } else { 2598398ca50Sbluhm int prvnxt = ip6_get_prevhdr(m, *offp); 2608398ca50Sbluhm 261ac1b2aeeSitojun icmp6_error(m, ICMP6_PARAM_PROB, 2628398ca50Sbluhm ICMP6_PARAMPROB_NEXTHEADER, prvnxt); 263ac1b2aeeSitojun } 26431e14cacSjca counters = counters_enter(&ref, ip6counters); 26531e14cacSjca counters[ip6s_delivered]--; 26631e14cacSjca counters_leave(&ref, ip6counters); 267da3acf12Sbluhm 268da3acf12Sbluhm return IPPROTO_DONE; 269ac1b2aeeSitojun } 2702be5be2cSbluhm 271*ea7e0400Sbluhm rip6_sbappend(last, m, ip6, *offp, &rip6src); 272*ea7e0400Sbluhm in_pcbunref(last); 2732be5be2cSbluhm 274*ea7e0400Sbluhm return IPPROTO_DONE; 275*ea7e0400Sbluhm } 276*ea7e0400Sbluhm 277*ea7e0400Sbluhm void 278*ea7e0400Sbluhm rip6_sbappend(struct inpcb *inp, struct mbuf *m, struct ip6_hdr *ip6, int hlen, 279*ea7e0400Sbluhm struct sockaddr_in6 *rip6src) 280*ea7e0400Sbluhm { 281c75e434fSmvs struct socket *so = inp->inp_socket; 282*ea7e0400Sbluhm struct mbuf *opts = NULL; 283a3425987Sbluhm int ret = 0; 284d0e7fa22Smvs 285921ffa12Sbluhm if (inp->inp_flags & IN6P_CONTROLOPTS) 286*ea7e0400Sbluhm ip6_savecontrol(inp, m, &opts); 2872be5be2cSbluhm /* strip intermediate headers */ 288*ea7e0400Sbluhm m_adj(m, hlen); 289d0e7fa22Smvs 290c75e434fSmvs mtx_enter(&so->so_rcv.sb_mtx); 291*ea7e0400Sbluhm if (!ISSET(inp->inp_socket->so_rcv.sb_state, SS_CANTRCVMORE)) 292*ea7e0400Sbluhm ret = sbappendaddr(so, &so->so_rcv, sin6tosa(rip6src), m, opts); 293c75e434fSmvs mtx_leave(&so->so_rcv.sb_mtx); 294d0e7fa22Smvs 295d0e7fa22Smvs if (ret == 0) { 296*ea7e0400Sbluhm m_freem(m); 2972be5be2cSbluhm m_freem(opts); 2982be5be2cSbluhm rip6stat_inc(rip6s_fullsock); 2992be5be2cSbluhm } else 300c75e434fSmvs sorwakeup(so); 3012be5be2cSbluhm } 302ac1b2aeeSitojun 303ac1b2aeeSitojun void 3044da85d9aSbluhm rip6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *d) 305ac1b2aeeSitojun { 306ac1b2aeeSitojun struct ip6_hdr *ip6; 3077ee01f6fSitojun struct ip6ctlparam *ip6cp = NULL; 30855851b2eSbluhm struct sockaddr_in6 *sa6 = satosin6(sa); 3097ee01f6fSitojun const struct sockaddr_in6 *sa6_src = NULL; 3107ee01f6fSitojun void *cmdarg; 3118e5060ecSderaadt void (*notify)(struct inpcb *, int) = in_rtchange; 3127ee01f6fSitojun int nxt; 313ac1b2aeeSitojun 314ac1b2aeeSitojun if (sa->sa_family != AF_INET6 || 315ac1b2aeeSitojun sa->sa_len != sizeof(struct sockaddr_in6)) 316ac1b2aeeSitojun return; 317ac1b2aeeSitojun 318ac1b2aeeSitojun if ((unsigned)cmd >= PRC_NCMDS) 319ac1b2aeeSitojun return; 320ac1b2aeeSitojun if (PRC_IS_REDIRECT(cmd)) 3218e5060ecSderaadt notify = in_rtchange, d = NULL; 322ac1b2aeeSitojun else if (cmd == PRC_HOSTDEAD) 323ac1b2aeeSitojun d = NULL; 3247ee01f6fSitojun else if (cmd == PRC_MSGSIZE) 3257ee01f6fSitojun ; /* special code is present, see below */ 326ac1b2aeeSitojun else if (inet6ctlerrmap[cmd] == 0) 327ac1b2aeeSitojun return; 328ac1b2aeeSitojun 329ac1b2aeeSitojun /* if the parameter is from icmp6, decode it. */ 330ac1b2aeeSitojun if (d != NULL) { 3317ee01f6fSitojun ip6cp = (struct ip6ctlparam *)d; 332ac1b2aeeSitojun ip6 = ip6cp->ip6c_ip6; 3337ee01f6fSitojun cmdarg = ip6cp->ip6c_cmdarg; 3347ee01f6fSitojun sa6_src = ip6cp->ip6c_src; 3357ee01f6fSitojun nxt = ip6cp->ip6c_nxt; 336ac1b2aeeSitojun } else { 337ac1b2aeeSitojun ip6 = NULL; 3387ee01f6fSitojun cmdarg = NULL; 3397ee01f6fSitojun sa6_src = &sa6_any; 3407ee01f6fSitojun nxt = -1; 341ac1b2aeeSitojun } 342ac1b2aeeSitojun 3437ee01f6fSitojun if (ip6 && cmd == PRC_MSGSIZE) { 3447ee01f6fSitojun int valid = 0; 345921ffa12Sbluhm struct inpcb *inp; 346ac1b2aeeSitojun 347ac1b2aeeSitojun /* 3487ee01f6fSitojun * Check to see if we have a valid raw IPv6 socket 3497ee01f6fSitojun * corresponding to the address in the ICMPv6 message 3507ee01f6fSitojun * payload, and the protocol (ip6_nxt) meets the socket. 3517ee01f6fSitojun * XXX chase extension headers, or pass final nxt value 3527ee01f6fSitojun * from icmp6_notify_error() 353ac1b2aeeSitojun */ 354921ffa12Sbluhm inp = in6_pcblookup(&rawin6pcbtable, &sa6->sin6_addr, 0, 355dc01e491Sphessler &sa6_src->sin6_addr, 0, rdomain); 3567ee01f6fSitojun 357921ffa12Sbluhm if (inp && inp->inp_ipv6.ip6_nxt && 358921ffa12Sbluhm inp->inp_ipv6.ip6_nxt == nxt) 3599e8a1cdfSbluhm valid = 1; 3607ee01f6fSitojun 3617ee01f6fSitojun /* 3627ee01f6fSitojun * Depending on the value of "valid" and routing table 3637ee01f6fSitojun * size (mtudisc_{hi,lo}wat), we will: 364f5bfbd04Sitojun * - recalculate the new MTU and create the 3657ee01f6fSitojun * corresponding routing entry, or 3667ee01f6fSitojun * - ignore the MTU change notification. 3677ee01f6fSitojun */ 3687ee01f6fSitojun icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); 369921ffa12Sbluhm in_pcbunref(inp); 3707ee01f6fSitojun 3717ee01f6fSitojun /* 3727ee01f6fSitojun * regardless of if we called icmp6_mtudisc_update(), 3737ee01f6fSitojun * we need to call in6_pcbnotify(), to notify path 3747ee01f6fSitojun * MTU change to the userland (2292bis-02), because 3757ee01f6fSitojun * some unconnected sockets may share the same 3767ee01f6fSitojun * destination and want to know the path MTU. 3777ee01f6fSitojun */ 3787ee01f6fSitojun } 3797ee01f6fSitojun 3801941b8b5Sbluhm in6_pcbnotify(&rawin6pcbtable, sa6, 0, 381dc01e491Sphessler sa6_src, 0, rdomain, cmd, cmdarg, notify); 382ac1b2aeeSitojun } 383ac1b2aeeSitojun 384ac1b2aeeSitojun /* 385ac1b2aeeSitojun * Generate IPv6 header and pass packet to ip6_output. 386ac1b2aeeSitojun * Tack on options user may have setup with control call. 387ac1b2aeeSitojun */ 388ac1b2aeeSitojun int 3896539a3bcSbluhm rip6_output(struct mbuf *m, struct socket *so, struct sockaddr *dstaddr, 3906539a3bcSbluhm struct mbuf *control) 391ac1b2aeeSitojun { 392ac1b2aeeSitojun struct in6_addr *dst; 393ac1b2aeeSitojun struct ip6_hdr *ip6; 394921ffa12Sbluhm struct inpcb *inp; 395ac1b2aeeSitojun u_int plen = m->m_pkthdr.len; 396ac1b2aeeSitojun int error = 0; 397952c6363Sbluhm struct ip6_pktopts opt, *optp = NULL; 39847c74071Schl int type; /* for ICMPv6 output statistics only */ 399ac1b2aeeSitojun int priv = 0; 400ac1b2aeeSitojun int flags; 401ac1b2aeeSitojun 402921ffa12Sbluhm inp = sotoinpcb(so); 403ac1b2aeeSitojun 404ac1b2aeeSitojun priv = 0; 405ac1b2aeeSitojun if ((so->so_state & SS_PRIV) != 0) 406ac1b2aeeSitojun priv = 1; 407ac1b2aeeSitojun if (control) { 4086b532452Sitojun if ((error = ip6_setpktopts(control, &opt, 409921ffa12Sbluhm inp->inp_outputopts6, 4106b532452Sitojun priv, so->so_proto->pr_protocol)) != 0) 411ac1b2aeeSitojun goto bad; 412ac1b2aeeSitojun optp = &opt; 413ac1b2aeeSitojun } else 414921ffa12Sbluhm optp = inp->inp_outputopts6; 415ac1b2aeeSitojun 41663ec1ab0Sbluhm if (dstaddr->sa_family != AF_INET6) { 41763ec1ab0Sbluhm error = EAFNOSUPPORT; 41863ec1ab0Sbluhm goto bad; 41963ec1ab0Sbluhm } 42063ec1ab0Sbluhm dst = &satosin6(dstaddr)->sin6_addr; 42163ec1ab0Sbluhm if (IN6_IS_ADDR_V4MAPPED(dst)) { 42263ec1ab0Sbluhm error = EADDRNOTAVAIL; 42363ec1ab0Sbluhm goto bad; 42463ec1ab0Sbluhm } 42563ec1ab0Sbluhm 426ac1b2aeeSitojun /* 427ac1b2aeeSitojun * For an ICMPv6 packet, we should know its type and code 428ac1b2aeeSitojun * to update statistics. 429ac1b2aeeSitojun */ 430ac1b2aeeSitojun if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 431ac1b2aeeSitojun struct icmp6_hdr *icmp6; 432ac1b2aeeSitojun if (m->m_len < sizeof(struct icmp6_hdr) && 433ac1b2aeeSitojun (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { 434ac1b2aeeSitojun error = ENOBUFS; 435ac1b2aeeSitojun goto bad; 436ac1b2aeeSitojun } 437ac1b2aeeSitojun icmp6 = mtod(m, struct icmp6_hdr *); 438ac1b2aeeSitojun type = icmp6->icmp6_type; 439ac1b2aeeSitojun } 440ac1b2aeeSitojun 441331e8cd7Sitojun M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); 442331e8cd7Sitojun if (!m) { 443331e8cd7Sitojun error = ENOBUFS; 444331e8cd7Sitojun goto bad; 445331e8cd7Sitojun } 446ac1b2aeeSitojun ip6 = mtod(m, struct ip6_hdr *); 447ac1b2aeeSitojun 448ac1b2aeeSitojun /* 449ac1b2aeeSitojun * Next header might not be ICMP6 but use its pseudo header anyway. 450ac1b2aeeSitojun */ 451ac1b2aeeSitojun ip6->ip6_dst = *dst; 452ac1b2aeeSitojun 453ac1b2aeeSitojun /* KAME hack: embed scopeid */ 454952c6363Sbluhm if (in6_embedscope(&ip6->ip6_dst, satosin6(dstaddr), 455921ffa12Sbluhm optp, inp->inp_moptions6) != 0) { 456ac1b2aeeSitojun error = EINVAL; 457ac1b2aeeSitojun goto bad; 458ac1b2aeeSitojun } 459ac1b2aeeSitojun 460ac1b2aeeSitojun /* 461ac1b2aeeSitojun * Source address selection. 462ac1b2aeeSitojun */ 463ac1b2aeeSitojun { 464cff23a6bSbluhm const struct in6_addr *in6a; 465ac1b2aeeSitojun 466921ffa12Sbluhm error = in6_pcbselsrc(&in6a, satosin6(dstaddr), inp, optp); 4670039ae51Sjca if (error) 468ac1b2aeeSitojun goto bad; 4690039ae51Sjca 470ac1b2aeeSitojun ip6->ip6_src = *in6a; 471ac1b2aeeSitojun } 472ac1b2aeeSitojun 473921ffa12Sbluhm ip6->ip6_flow = inp->inp_flowinfo & IPV6_FLOWINFO_MASK; 474ac1b2aeeSitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 475ac1b2aeeSitojun ip6->ip6_vfc |= IPV6_VERSION; 476ac1b2aeeSitojun #if 0 /* ip6_plen will be filled in ip6_output. */ 477ac1b2aeeSitojun ip6->ip6_plen = htons((u_short)plen); 478ac1b2aeeSitojun #endif 479921ffa12Sbluhm ip6->ip6_nxt = inp->inp_ipv6.ip6_nxt; 480921ffa12Sbluhm ip6->ip6_hlim = in6_selecthlim(inp); 481ac1b2aeeSitojun 482ac1b2aeeSitojun if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || 483921ffa12Sbluhm inp->inp_cksum6 != -1) { 484af7b461eSitojun struct mbuf *n; 485ac1b2aeeSitojun int off; 486af7b461eSitojun u_int16_t *sump; 487af7b461eSitojun int sumoff; 488ac1b2aeeSitojun 489ac1b2aeeSitojun /* compute checksum */ 490ac1b2aeeSitojun if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 491ac1b2aeeSitojun off = offsetof(struct icmp6_hdr, icmp6_cksum); 492ac1b2aeeSitojun else 493921ffa12Sbluhm off = inp->inp_cksum6; 494c526b7b2Sbluhm if (plen < 2 || plen - 2 < off) { 495ac1b2aeeSitojun error = EINVAL; 496ac1b2aeeSitojun goto bad; 497ac1b2aeeSitojun } 498ac1b2aeeSitojun off += sizeof(struct ip6_hdr); 499ac1b2aeeSitojun 500af7b461eSitojun n = m_pulldown(m, off, sizeof(*sump), &sumoff); 501af7b461eSitojun if (n == NULL) { 502af7b461eSitojun m = NULL; 503af7b461eSitojun error = ENOBUFS; 504af7b461eSitojun goto bad; 505af7b461eSitojun } 506af7b461eSitojun sump = (u_int16_t *)(mtod(n, caddr_t) + sumoff); 507af7b461eSitojun *sump = 0; 508af7b461eSitojun *sump = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); 509ac1b2aeeSitojun } 510ac1b2aeeSitojun 511ac1b2aeeSitojun flags = 0; 512921ffa12Sbluhm if (inp->inp_flags & IN6P_MINMTU) 513ac1b2aeeSitojun flags |= IPV6_MINMTU; 514ac1b2aeeSitojun 5155ee8afe3Smpi /* force routing table */ 516921ffa12Sbluhm m->m_pkthdr.ph_rtableid = inp->inp_rtableid; 517ba79ddd5Ssperreault 51854c5291fSbluhm #if NPF > 0 519921ffa12Sbluhm if (inp->inp_socket->so_state & SS_ISCONNECTED && 520fbc14a14Sbluhm so->so_proto->pr_protocol != IPPROTO_ICMPV6) 521921ffa12Sbluhm pf_mbuf_link_inpcb(m, inp); 52254c5291fSbluhm #endif 52354c5291fSbluhm 52494c0e2bdSbluhm error = ip6_output(m, optp, &inp->inp_route, flags, 525ace0f189Sbluhm inp->inp_moptions6, &inp->inp_seclevel); 526ac1b2aeeSitojun if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { 52707ba5f7cSjca icmp6stat_inc(icp6s_outhist + type); 528f5bfbd04Sitojun } else 52907ba5f7cSjca rip6stat_inc(rip6s_opackets); 530ac1b2aeeSitojun 531ac1b2aeeSitojun goto freectl; 532ac1b2aeeSitojun 533ac1b2aeeSitojun bad: 534ac1b2aeeSitojun m_freem(m); 535ac1b2aeeSitojun 536ac1b2aeeSitojun freectl: 5376b532452Sitojun if (control) { 5386b532452Sitojun ip6_clearpktopts(&opt, -1); 539ac1b2aeeSitojun m_freem(control); 5406b532452Sitojun } 541ac1b2aeeSitojun return (error); 542ac1b2aeeSitojun } 543ac1b2aeeSitojun 544ac1b2aeeSitojun /* 545ac1b2aeeSitojun * Raw IPv6 socket option processing. 546ac1b2aeeSitojun */ 547ac1b2aeeSitojun int 548ee37ea65Smcbride rip6_ctloutput(int op, struct socket *so, int level, int optname, 549490e0738Sdhill struct mbuf *m) 550ac1b2aeeSitojun { 55164b11953Sderaadt #ifdef MROUTING 5528b4596e6Sbluhm int error; 55364b11953Sderaadt #endif 554ac1b2aeeSitojun 555ac1b2aeeSitojun switch (level) { 556ac1b2aeeSitojun case IPPROTO_IPV6: 557ac1b2aeeSitojun switch (optname) { 558d9c6379eSclaudio #ifdef MROUTING 559ac1b2aeeSitojun case MRT6_INIT: 560ac1b2aeeSitojun case MRT6_DONE: 561ac1b2aeeSitojun case MRT6_ADD_MIF: 562ac1b2aeeSitojun case MRT6_DEL_MIF: 563ac1b2aeeSitojun case MRT6_ADD_MFC: 564ac1b2aeeSitojun case MRT6_DEL_MFC: 565ac1b2aeeSitojun if (op == PRCO_SETOPT) { 566490e0738Sdhill error = ip6_mrouter_set(optname, so, m); 567f5bfbd04Sitojun } else if (op == PRCO_GETOPT) 568490e0738Sdhill error = ip6_mrouter_get(optname, so, m); 569f5bfbd04Sitojun else 570ac1b2aeeSitojun error = EINVAL; 571ac1b2aeeSitojun return (error); 572d9c6379eSclaudio #endif 573f5bfbd04Sitojun case IPV6_CHECKSUM: 574490e0738Sdhill return (ip6_raw_ctloutput(op, so, level, optname, m)); 575f5bfbd04Sitojun default: 576490e0738Sdhill return (ip6_ctloutput(op, so, level, optname, m)); 577ac1b2aeeSitojun } 578ac1b2aeeSitojun 579ac1b2aeeSitojun case IPPROTO_ICMPV6: 580ac1b2aeeSitojun /* 581ac1b2aeeSitojun * XXX: is it better to call icmp6_ctloutput() directly 582ac1b2aeeSitojun * from protosw? 583ac1b2aeeSitojun */ 584490e0738Sdhill return (icmp6_ctloutput(op, so, level, optname, m)); 585ac1b2aeeSitojun 586ac1b2aeeSitojun default: 587f5bfbd04Sitojun return EINVAL; 588ac1b2aeeSitojun } 589ac1b2aeeSitojun } 590ac1b2aeeSitojun 591ac1b2aeeSitojun extern u_long rip6_sendspace; 592ac1b2aeeSitojun extern u_long rip6_recvspace; 593ac1b2aeeSitojun 594ac1b2aeeSitojun int 59562440853Sbluhm rip6_attach(struct socket *so, int proto, int wait) 5963d7f610cSclaudio { 597921ffa12Sbluhm struct inpcb *inp; 5983d7f610cSclaudio int error; 5993d7f610cSclaudio 6003d7f610cSclaudio if (so->so_pcb) 601ab0ea4a9Snayden panic("%s", __func__); 6023d7f610cSclaudio if ((so->so_state & SS_PRIV) == 0) 6033d7f610cSclaudio return (EACCES); 6043d7f610cSclaudio if (proto < 0 || proto >= IPPROTO_MAX) 6053d7f610cSclaudio return EPROTONOSUPPORT; 6063d7f610cSclaudio 60794334c66Smpi if ((error = soreserve(so, rip6_sendspace, rip6_recvspace))) 60894334c66Smpi return error; 60994334c66Smpi NET_ASSERT_LOCKED(); 61062440853Sbluhm if ((error = in_pcballoc(so, &rawin6pcbtable, wait))) 6113d7f610cSclaudio return error; 6123d7f610cSclaudio 613921ffa12Sbluhm inp = sotoinpcb(so); 614921ffa12Sbluhm inp->inp_ipv6.ip6_nxt = proto; 615921ffa12Sbluhm inp->inp_cksum6 = -1; 6163d7f610cSclaudio 617921ffa12Sbluhm inp->inp_icmp6filt = malloc(sizeof(struct icmp6_filter), M_PCB, 61862440853Sbluhm wait == M_WAIT ? M_WAITOK : M_NOWAIT); 619921ffa12Sbluhm if (inp->inp_icmp6filt == NULL) { 620921ffa12Sbluhm in_pcbdetach(inp); 6213d7f610cSclaudio return ENOMEM; 6223d7f610cSclaudio } 623921ffa12Sbluhm ICMP6_FILTER_SETPASSALL(inp->inp_icmp6filt); 6243d7f610cSclaudio return 0; 6253d7f610cSclaudio } 6263d7f610cSclaudio 6273d7f610cSclaudio int 6282c5f89aaSflorian rip6_detach(struct socket *so) 6292c5f89aaSflorian { 630921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 6312c5f89aaSflorian 6322c5f89aaSflorian soassertlocked(so); 6332c5f89aaSflorian 634921ffa12Sbluhm if (inp == NULL) 635ab0ea4a9Snayden panic("%s", __func__); 6362c5f89aaSflorian #ifdef MROUTING 637921ffa12Sbluhm if (so == ip6_mrouter[inp->inp_rtableid]) 6382c5f89aaSflorian ip6_mrouter_done(so); 6392c5f89aaSflorian #endif 640921ffa12Sbluhm free(inp->inp_icmp6filt, M_PCB, sizeof(struct icmp6_filter)); 641921ffa12Sbluhm inp->inp_icmp6filt = NULL; 6422c5f89aaSflorian 643921ffa12Sbluhm in_pcbdetach(inp); 6442c5f89aaSflorian 6452c5f89aaSflorian return (0); 6462c5f89aaSflorian } 6472c5f89aaSflorian 648104c0a83Smvs int 649121fc5cfSmvs rip6_bind(struct socket *so, struct mbuf *nam, struct proc *p) 650121fc5cfSmvs { 651921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 652121fc5cfSmvs struct sockaddr_in6 *addr; 653121fc5cfSmvs int error; 654121fc5cfSmvs 655121fc5cfSmvs soassertlocked(so); 656121fc5cfSmvs 657121fc5cfSmvs if ((error = in6_nam2sin6(nam, &addr))) 658121fc5cfSmvs return (error); 659121fc5cfSmvs 660121fc5cfSmvs /* 661121fc5cfSmvs * Make sure to not enter in_pcblookup_local(), local ports 662121fc5cfSmvs * are non-sensical for raw sockets. 663121fc5cfSmvs */ 664121fc5cfSmvs addr->sin6_port = 0; 665121fc5cfSmvs 666921ffa12Sbluhm if ((error = in6_pcbaddrisavail(inp, addr, 0, p))) 667121fc5cfSmvs return (error); 668121fc5cfSmvs 669990f2b24Sbluhm mtx_enter(&rawin6pcbtable.inpt_mtx); 670921ffa12Sbluhm inp->inp_laddr6 = addr->sin6_addr; 671990f2b24Sbluhm mtx_leave(&rawin6pcbtable.inpt_mtx); 672990f2b24Sbluhm 673121fc5cfSmvs return (0); 674121fc5cfSmvs } 675121fc5cfSmvs 676121fc5cfSmvs int 677074c8388Smvs rip6_connect(struct socket *so, struct mbuf *nam) 678074c8388Smvs { 679921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 680074c8388Smvs struct sockaddr_in6 *addr; 681cff23a6bSbluhm const struct in6_addr *in6a; 682074c8388Smvs int error; 683074c8388Smvs 684074c8388Smvs soassertlocked(so); 685074c8388Smvs 686074c8388Smvs if ((error = in6_nam2sin6(nam, &addr))) 687074c8388Smvs return (error); 688074c8388Smvs 689074c8388Smvs /* Source address selection. XXX: need pcblookup? */ 690921ffa12Sbluhm error = in6_pcbselsrc(&in6a, addr, inp, inp->inp_outputopts6); 691074c8388Smvs if (error) 692074c8388Smvs return (error); 693074c8388Smvs 694990f2b24Sbluhm mtx_enter(&rawin6pcbtable.inpt_mtx); 695921ffa12Sbluhm inp->inp_laddr6 = *in6a; 696921ffa12Sbluhm inp->inp_faddr6 = addr->sin6_addr; 697990f2b24Sbluhm mtx_leave(&rawin6pcbtable.inpt_mtx); 698074c8388Smvs soisconnected(so); 699990f2b24Sbluhm 700074c8388Smvs return (0); 701074c8388Smvs } 702074c8388Smvs 703074c8388Smvs int 704e00787e6Smvs rip6_disconnect(struct socket *so) 705e00787e6Smvs { 706921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 707e00787e6Smvs 708e00787e6Smvs soassertlocked(so); 709e00787e6Smvs 710e00787e6Smvs if ((so->so_state & SS_ISCONNECTED) == 0) 711e00787e6Smvs return (ENOTCONN); 712e00787e6Smvs 713a3425987Sbluhm soisdisconnected(so); 714990f2b24Sbluhm mtx_enter(&rawin6pcbtable.inpt_mtx); 715990f2b24Sbluhm inp->inp_faddr6 = in6addr_any; 716990f2b24Sbluhm mtx_leave(&rawin6pcbtable.inpt_mtx); 717990f2b24Sbluhm 718e00787e6Smvs return (0); 719e00787e6Smvs } 720e00787e6Smvs 721e00787e6Smvs int 72286e05c94Smvs rip6_shutdown(struct socket *so) 72386e05c94Smvs { 72486e05c94Smvs /* 72586e05c94Smvs * Mark the connection as being incapable of further input. 72686e05c94Smvs */ 72786e05c94Smvs soassertlocked(so); 72886e05c94Smvs socantsendmore(so); 72986e05c94Smvs return (0); 73086e05c94Smvs } 73186e05c94Smvs 73286e05c94Smvs int 73390b3510cSmvs rip6_send(struct socket *so, struct mbuf *m, struct mbuf *nam, 73490b3510cSmvs struct mbuf *control) 73590b3510cSmvs { 736921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 73790b3510cSmvs struct sockaddr_in6 dst; 73890b3510cSmvs int error; 73990b3510cSmvs 74090b3510cSmvs soassertlocked(so); 74190b3510cSmvs 74290b3510cSmvs /* 74390b3510cSmvs * Ship a packet out. The appropriate raw output 74490b3510cSmvs * routine handles any messaging necessary. 74590b3510cSmvs */ 74690b3510cSmvs 74790b3510cSmvs /* always copy sockaddr to avoid overwrites */ 74890b3510cSmvs memset(&dst, 0, sizeof(dst)); 74990b3510cSmvs dst.sin6_family = AF_INET6; 75090b3510cSmvs dst.sin6_len = sizeof(dst); 75190b3510cSmvs if (so->so_state & SS_ISCONNECTED) { 75290b3510cSmvs if (nam) { 75390b3510cSmvs error = EISCONN; 75490b3510cSmvs goto out; 75590b3510cSmvs } 756921ffa12Sbluhm dst.sin6_addr = inp->inp_faddr6; 75790b3510cSmvs } else { 75890b3510cSmvs struct sockaddr_in6 *addr6; 75990b3510cSmvs 76090b3510cSmvs if (nam == NULL) { 76190b3510cSmvs error = ENOTCONN; 76290b3510cSmvs goto out; 76390b3510cSmvs } 76490b3510cSmvs if ((error = in6_nam2sin6(nam, &addr6))) 76590b3510cSmvs goto out; 76690b3510cSmvs dst.sin6_addr = addr6->sin6_addr; 76790b3510cSmvs dst.sin6_scope_id = addr6->sin6_scope_id; 76890b3510cSmvs } 76990b3510cSmvs error = rip6_output(m, so, sin6tosa(&dst), control); 77090b3510cSmvs control = NULL; 77190b3510cSmvs m = NULL; 77290b3510cSmvs 77390b3510cSmvs out: 77490b3510cSmvs m_freem(control); 77590b3510cSmvs m_freem(m); 77690b3510cSmvs 77790b3510cSmvs return (error); 77890b3510cSmvs } 77990b3510cSmvs 78090b3510cSmvs int 78107ba5f7cSjca rip6_sysctl_rip6stat(void *oldp, size_t *oldplen, void *newp) 78207ba5f7cSjca { 78307ba5f7cSjca struct rip6stat rip6stat; 78407ba5f7cSjca 78507ba5f7cSjca CTASSERT(sizeof(rip6stat) == rip6s_ncounters * sizeof(uint64_t)); 786bf0d449cSmpi counters_read(rip6counters, (uint64_t *)&rip6stat, rip6s_ncounters, 787bf0d449cSmpi NULL); 78807ba5f7cSjca 78907ba5f7cSjca return (sysctl_rdstruct(oldp, oldplen, newp, 79007ba5f7cSjca &rip6stat, sizeof(rip6stat))); 79107ba5f7cSjca } 79207ba5f7cSjca 79307ba5f7cSjca int 794ab46c28dSderaadt rip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 795ab46c28dSderaadt void *newp, size_t newlen) 796ab46c28dSderaadt { 797ab46c28dSderaadt /* All sysctl names at this level are terminal. */ 798ab46c28dSderaadt if (namelen != 1) 799ab46c28dSderaadt return ENOTDIR; 800ab46c28dSderaadt 801ab46c28dSderaadt switch (name[0]) { 802ab46c28dSderaadt case RIPV6CTL_STATS: 80307ba5f7cSjca return (rip6_sysctl_rip6stat(oldp, oldlenp, newp)); 804ab46c28dSderaadt default: 805ab46c28dSderaadt return (EOPNOTSUPP); 806ab46c28dSderaadt } 807ab46c28dSderaadt /* NOTREACHED */ 808ab46c28dSderaadt } 809