1*84d9c64aSbluhm /* $OpenBSD: ip6_output.c,v 1.294 2025/01/03 21:27:40 bluhm Exp $ */ 2672f01bcSitojun /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */ 3287546eaSitojun 4287546eaSitojun /* 5287546eaSitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6287546eaSitojun * All rights reserved. 7287546eaSitojun * 8287546eaSitojun * Redistribution and use in source and binary forms, with or without 9287546eaSitojun * modification, are permitted provided that the following conditions 10287546eaSitojun * are met: 11287546eaSitojun * 1. Redistributions of source code must retain the above copyright 12287546eaSitojun * notice, this list of conditions and the following disclaimer. 13287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 14287546eaSitojun * notice, this list of conditions and the following disclaimer in the 15287546eaSitojun * documentation and/or other materials provided with the distribution. 16287546eaSitojun * 3. Neither the name of the project nor the names of its contributors 17287546eaSitojun * may be used to endorse or promote products derived from this software 18287546eaSitojun * without specific prior written permission. 19287546eaSitojun * 20287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30287546eaSitojun * SUCH DAMAGE. 31287546eaSitojun */ 32287546eaSitojun 33287546eaSitojun /* 34287546eaSitojun * Copyright (c) 1982, 1986, 1988, 1990, 1993 35287546eaSitojun * The Regents of the University of California. All rights reserved. 36287546eaSitojun * 37287546eaSitojun * Redistribution and use in source and binary forms, with or without 38287546eaSitojun * modification, are permitted provided that the following conditions 39287546eaSitojun * are met: 40287546eaSitojun * 1. Redistributions of source code must retain the above copyright 41287546eaSitojun * notice, this list of conditions and the following disclaimer. 42287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 43287546eaSitojun * notice, this list of conditions and the following disclaimer in the 44287546eaSitojun * documentation and/or other materials provided with the distribution. 4529295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 46287546eaSitojun * may be used to endorse or promote products derived from this software 47287546eaSitojun * without specific prior written permission. 48287546eaSitojun * 49287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59287546eaSitojun * SUCH DAMAGE. 60287546eaSitojun * 61287546eaSitojun * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 62287546eaSitojun */ 63287546eaSitojun 6430620b12Sfrantzen #include "pf.h" 6530620b12Sfrantzen 66287546eaSitojun #include <sys/param.h> 67287546eaSitojun #include <sys/malloc.h> 68287546eaSitojun #include <sys/mbuf.h> 69287546eaSitojun #include <sys/errno.h> 70287546eaSitojun #include <sys/protosw.h> 71287546eaSitojun #include <sys/socket.h> 72287546eaSitojun #include <sys/socketvar.h> 73d83f8b41Sphessler #include <sys/proc.h> 74287546eaSitojun #include <sys/systm.h> 75287546eaSitojun 76287546eaSitojun #include <net/if.h> 770deb6685Smpi #include <net/if_var.h> 78ba8b7f53Smarkus #include <net/if_enc.h> 79287546eaSitojun #include <net/route.h> 80287546eaSitojun 81287546eaSitojun #include <netinet/in.h> 82287546eaSitojun #include <netinet/ip.h> 8358aa7d74Sangelos #include <netinet/in_pcb.h> 84e993eaa0Shenning #include <netinet/udp.h> 85e993eaa0Shenning #include <netinet/tcp.h> 8658aa7d74Sangelos 87095b4e20Shenning #include <netinet/ip_var.h> 88095b4e20Shenning #include <netinet/tcp_timer.h> 89095b4e20Shenning #include <netinet/tcp_var.h> 90095b4e20Shenning #include <netinet/udp_var.h> 91095b4e20Shenning 92dc572864Sbluhm #include <netinet6/in6_var.h> 93fa86ee14Sitojun #include <netinet/ip6.h> 94fa86ee14Sitojun #include <netinet/icmp6.h> 95287546eaSitojun #include <netinet6/ip6_var.h> 96287546eaSitojun #include <netinet6/nd6.h> 97287546eaSitojun 98ca6e56f2Sdjm #include <crypto/idgen.h> 99ca6e56f2Sdjm 10030620b12Sfrantzen #if NPF > 0 10130620b12Sfrantzen #include <net/pfvar.h> 10230620b12Sfrantzen #endif 10330620b12Sfrantzen 104dc48c783Sitojun #ifdef IPSEC 105537b0e2dScedric #include <netinet/ip_ipsp.h> 106dc48c783Sitojun #include <netinet/ip_ah.h> 107dc48c783Sitojun #include <netinet/ip_esp.h> 10859caf375Sbluhm 10959caf375Sbluhm #ifdef ENCDEBUG 110698a75ddSbluhm #define DPRINTF(fmt, args...) \ 111698a75ddSbluhm do { \ 112698a75ddSbluhm if (encdebug) \ 113698a75ddSbluhm printf("%s: " fmt "\n", __func__, ## args); \ 114698a75ddSbluhm } while (0) 11559caf375Sbluhm #else 116698a75ddSbluhm #define DPRINTF(fmt, args...) \ 117698a75ddSbluhm do { } while (0) 11859caf375Sbluhm #endif 119dc48c783Sitojun #endif /* IPSEC */ 120287546eaSitojun 121287546eaSitojun struct ip6_exthdrs { 122287546eaSitojun struct mbuf *ip6e_ip6; 123287546eaSitojun struct mbuf *ip6e_hbh; 124287546eaSitojun struct mbuf *ip6e_dest1; 125287546eaSitojun struct mbuf *ip6e_rthdr; 126287546eaSitojun struct mbuf *ip6e_dest2; 127287546eaSitojun }; 128287546eaSitojun 129d4984c21Sjsing int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **, int, int); 130490e0738Sdhill int ip6_getpcbopt(struct ip6_pktopts *, int, struct mbuf *); 1313ea7534eSjca int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, int, int, int); 13244a616dfSrzalamena int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *, unsigned int); 133490e0738Sdhill int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf *); 134d4984c21Sjsing int ip6_copyexthdr(struct mbuf **, caddr_t, int); 135d4984c21Sjsing int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, 136c4071fd1Smillert struct ip6_frag **); 137d4984c21Sjsing int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); 138d4984c21Sjsing int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); 139f3785e27Sbluhm int ip6_getpmtu(struct rtentry *, struct ifnet *, u_long *); 140f288bf99Sdhill int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *); 141a2c73f7dSnaddy static __inline u_int16_t __attribute__((__unused__)) 142a2c73f7dSnaddy in6_cksum_phdr(const struct in6_addr *, const struct in6_addr *, 143a2c73f7dSnaddy u_int32_t, u_int32_t); 144e3f16dcaShenning void in6_delayed_cksum(struct mbuf *, u_int8_t); 145287546eaSitojun 14694c0e2bdSbluhm int ip6_output_ipsec_pmtu_update(struct tdb *, struct route *, 1476d361195Sbluhm struct in6_addr *, int, int, int); 1486d361195Sbluhm 149ca6e56f2Sdjm /* Context for non-repeating IDs */ 150ca6e56f2Sdjm struct idgen32_ctx ip6_id_ctx; 151ca6e56f2Sdjm 152287546eaSitojun /* 153287546eaSitojun * IP6 output. The packet in mbuf chain m contains a skeletal IP6 154287546eaSitojun * header (with pri, len, nxt, hlim, src, dst). 155287546eaSitojun * This function may modify ver and hlim only. 156287546eaSitojun * The mbuf chain containing the packet will be freed. 157287546eaSitojun * The mbuf opt, if present, will not be freed. 158841d7adbSitojun * 159*84d9c64aSbluhm * type of "mtu": rt_mtu is u_int, ifnet.ifr_mtu is int. 160*84d9c64aSbluhm * We use u_long to hold largest one. XXX should be u_int 161287546eaSitojun */ 162287546eaSitojun int 16394c0e2bdSbluhm ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route *ro, 164ace0f189Sbluhm int flags, struct ip6_moptions *im6o, const struct ipsec_level *seclevel) 165287546eaSitojun { 166a8cafab0Sbluhm struct ip6_hdr *ip6; 167b9ab5338Smpi struct ifnet *ifp = NULL; 168b8646e37Sbluhm struct mbuf_list ml; 169a8cafab0Sbluhm int hlen, tlen; 17094c0e2bdSbluhm struct route iproute; 1716b532452Sitojun struct rtentry *rt = NULL; 17222723314Sbluhm struct sockaddr_in6 *dst; 173287546eaSitojun int error = 0; 174287546eaSitojun u_long mtu; 175f3785e27Sbluhm int dontfrag; 176cd5ec7edSmpi u_int16_t src_scope, dst_scope; 177287546eaSitojun u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; 178287546eaSitojun struct ip6_exthdrs exthdrs; 179287546eaSitojun struct in6_addr finaldst; 18094c0e2bdSbluhm struct route *ro_pmtu = NULL; 181287546eaSitojun int hdrsplit = 0; 182bea47bb9Sitojun u_int8_t sproto = 0; 1832818ef99Sbluhm u_char nextproto; 184dc48c783Sitojun #ifdef IPSEC 1854a931851Smarkus struct tdb *tdb = NULL; 186bea47bb9Sitojun #endif /* IPSEC */ 18758aa7d74Sangelos 1886b532452Sitojun ip6 = mtod(m, struct ip6_hdr *); 1896b532452Sitojun finaldst = ip6->ip6_dst; 1906b532452Sitojun 191287546eaSitojun #define MAKE_EXTHDR(hp, mp) \ 1922ed872ebSitojun do { \ 193287546eaSitojun if (hp) { \ 194287546eaSitojun struct ip6_ext *eh = (struct ip6_ext *)(hp); \ 195287546eaSitojun error = ip6_copyexthdr((mp), (caddr_t)(hp), \ 196287546eaSitojun ((eh)->ip6e_len + 1) << 3); \ 197287546eaSitojun if (error) \ 198287546eaSitojun goto freehdrs; \ 199287546eaSitojun } \ 2002ed872ebSitojun } while (0) 201287546eaSitojun 202287546eaSitojun bzero(&exthdrs, sizeof(exthdrs)); 2036b532452Sitojun 204287546eaSitojun if (opt) { 205287546eaSitojun /* Hop-by-Hop options header */ 206287546eaSitojun MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); 207287546eaSitojun /* Destination options header(1st part) */ 208287546eaSitojun MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); 209287546eaSitojun /* Routing header */ 210287546eaSitojun MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); 211287546eaSitojun /* Destination options header(2nd part) */ 212287546eaSitojun MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); 213287546eaSitojun } 214287546eaSitojun 215287546eaSitojun #ifdef IPSEC 2162551e577Sbluhm if (ipsec_in_use || seclevel != NULL) { 2172551e577Sbluhm error = ip6_output_ipsec_lookup(m, seclevel, &tdb); 218e7111b64Sbluhm if (error) { 219dc48c783Sitojun /* 220dc48c783Sitojun * -EINVAL is used to indicate that the packet should 221dc48c783Sitojun * be silently dropped, typically because we've asked 222dc48c783Sitojun * key management for an SA. 223dc48c783Sitojun */ 224dc48c783Sitojun if (error == -EINVAL) /* Should silently drop packet */ 225287546eaSitojun error = 0; 226287546eaSitojun 227dc48c783Sitojun goto freehdrs; 228dc48c783Sitojun } 229275d330eSangelos } 230287546eaSitojun #endif /* IPSEC */ 231287546eaSitojun 232287546eaSitojun /* 233287546eaSitojun * Calculate the total length of the extension header chain. 234287546eaSitojun * Keep the length of the unfragmentable part for fragmentation. 235287546eaSitojun */ 236287546eaSitojun optlen = 0; 237287546eaSitojun if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len; 238287546eaSitojun if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len; 239287546eaSitojun if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len; 240287546eaSitojun unfragpartlen = optlen + sizeof(struct ip6_hdr); 241287546eaSitojun /* NOTE: we don't add AH/ESP length here. do that later. */ 242287546eaSitojun if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len; 243287546eaSitojun 244287546eaSitojun /* 245287546eaSitojun * If we need IPsec, or there is at least one extension header, 246287546eaSitojun * separate IP6 header from the payload. 247287546eaSitojun */ 248dc48c783Sitojun if ((sproto || optlen) && !hdrsplit) { 249287546eaSitojun if ((error = ip6_splithdr(m, &exthdrs)) != 0) { 250287546eaSitojun m = NULL; 251287546eaSitojun goto freehdrs; 252287546eaSitojun } 253287546eaSitojun m = exthdrs.ip6e_ip6; 254287546eaSitojun hdrsplit++; 255287546eaSitojun } 256287546eaSitojun 257287546eaSitojun /* adjust pointer */ 258287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 259287546eaSitojun 260287546eaSitojun /* adjust mbuf packet header length */ 261287546eaSitojun m->m_pkthdr.len += optlen; 262287546eaSitojun plen = m->m_pkthdr.len - sizeof(*ip6); 263287546eaSitojun 264287546eaSitojun /* If this is a jumbo payload, insert a jumbo payload option. */ 265287546eaSitojun if (plen > IPV6_MAXPACKET) { 266287546eaSitojun if (!hdrsplit) { 267287546eaSitojun if ((error = ip6_splithdr(m, &exthdrs)) != 0) { 268287546eaSitojun m = NULL; 269287546eaSitojun goto freehdrs; 270287546eaSitojun } 271287546eaSitojun m = exthdrs.ip6e_ip6; 272287546eaSitojun hdrsplit++; 273287546eaSitojun } 274287546eaSitojun /* adjust pointer */ 275287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 276287546eaSitojun if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) 277287546eaSitojun goto freehdrs; 278287546eaSitojun ip6->ip6_plen = 0; 279287546eaSitojun } else 280287546eaSitojun ip6->ip6_plen = htons(plen); 281287546eaSitojun 282287546eaSitojun /* 283287546eaSitojun * Concatenate headers and fill in next header fields. 284287546eaSitojun * Here we have, on "m" 285287546eaSitojun * IPv6 payload 286287546eaSitojun * and we insert headers accordingly. Finally, we should be getting: 287287546eaSitojun * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] 288287546eaSitojun * 289287546eaSitojun * during the header composing process, "m" points to IPv6 header. 290287546eaSitojun * "mprev" points to an extension header prior to esp. 291287546eaSitojun */ 292287546eaSitojun { 293287546eaSitojun u_char *nexthdrp = &ip6->ip6_nxt; 294287546eaSitojun struct mbuf *mprev = m; 295287546eaSitojun 296287546eaSitojun /* 297287546eaSitojun * we treat dest2 specially. this makes IPsec processing 2986b532452Sitojun * much easier. the goal here is to make mprev point the 2996b532452Sitojun * mbuf prior to dest2. 300287546eaSitojun * 301287546eaSitojun * result: IPv6 dest2 payload 302287546eaSitojun * m and mprev will point to IPv6 header. 303287546eaSitojun */ 304287546eaSitojun if (exthdrs.ip6e_dest2) { 305287546eaSitojun if (!hdrsplit) 30690dc3aeaSbluhm panic("%s: assumption failed: hdr not split", 30790dc3aeaSbluhm __func__); 308287546eaSitojun exthdrs.ip6e_dest2->m_next = m->m_next; 309287546eaSitojun m->m_next = exthdrs.ip6e_dest2; 310287546eaSitojun *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; 311287546eaSitojun ip6->ip6_nxt = IPPROTO_DSTOPTS; 312287546eaSitojun } 313287546eaSitojun 314287546eaSitojun #define MAKE_CHAIN(m, mp, p, i)\ 3152ed872ebSitojun do {\ 316287546eaSitojun if (m) {\ 317287546eaSitojun if (!hdrsplit) \ 318287546eaSitojun panic("assumption failed: hdr not split"); \ 319287546eaSitojun *mtod((m), u_char *) = *(p);\ 320287546eaSitojun *(p) = (i);\ 321287546eaSitojun p = mtod((m), u_char *);\ 322287546eaSitojun (m)->m_next = (mp)->m_next;\ 323287546eaSitojun (mp)->m_next = (m);\ 324287546eaSitojun (mp) = (m);\ 325287546eaSitojun }\ 3262ed872ebSitojun } while (0) 327287546eaSitojun /* 328287546eaSitojun * result: IPv6 hbh dest1 rthdr dest2 payload 329287546eaSitojun * m will point to IPv6 header. mprev will point to the 330287546eaSitojun * extension header prior to dest2 (rthdr in the above case). 331287546eaSitojun */ 3327b2ccaf7Sitojun MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS); 3337b2ccaf7Sitojun MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp, 3347b2ccaf7Sitojun IPPROTO_DSTOPTS); 3357b2ccaf7Sitojun MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp, 3367b2ccaf7Sitojun IPPROTO_ROUTING); 337287546eaSitojun } 338287546eaSitojun 339287546eaSitojun /* 3406b532452Sitojun * If there is a routing header, replace the destination address field 341287546eaSitojun * with the first hop of the routing header. 342287546eaSitojun */ 343287546eaSitojun if (exthdrs.ip6e_rthdr) { 3447b2ccaf7Sitojun struct ip6_rthdr *rh; 345287546eaSitojun struct ip6_rthdr0 *rh0; 34622a58e88Sitojun struct in6_addr *addr; 347287546eaSitojun 3487b2ccaf7Sitojun rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, 3497b2ccaf7Sitojun struct ip6_rthdr *)); 350287546eaSitojun switch (rh->ip6r_type) { 351287546eaSitojun case IPV6_RTHDR_TYPE_0: 352287546eaSitojun rh0 = (struct ip6_rthdr0 *)rh; 35322a58e88Sitojun addr = (struct in6_addr *)(rh0 + 1); 35422a58e88Sitojun ip6->ip6_dst = addr[0]; 35522a58e88Sitojun bcopy(&addr[1], &addr[0], 3567b2ccaf7Sitojun sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1)); 35722a58e88Sitojun addr[rh0->ip6r0_segleft - 1] = finaldst; 358287546eaSitojun break; 359287546eaSitojun default: /* is it possible? */ 360287546eaSitojun error = EINVAL; 361287546eaSitojun goto bad; 362287546eaSitojun } 363287546eaSitojun } 364287546eaSitojun 365287546eaSitojun /* Source address validation */ 3666b532452Sitojun if (!(flags & IPV6_UNSPECSRC) && 3676b532452Sitojun IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { 3686b532452Sitojun /* 3696b532452Sitojun * XXX: we can probably assume validation in the caller, but 3706b532452Sitojun * we explicitly check the address here for safety. 3716b532452Sitojun */ 372287546eaSitojun error = EOPNOTSUPP; 37331e14cacSjca ip6stat_inc(ip6s_badscope); 374287546eaSitojun goto bad; 375287546eaSitojun } 376287546eaSitojun if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { 377287546eaSitojun error = EOPNOTSUPP; 37831e14cacSjca ip6stat_inc(ip6s_badscope); 379287546eaSitojun goto bad; 380287546eaSitojun } 381287546eaSitojun 38231e14cacSjca ip6stat_inc(ip6s_localout); 383287546eaSitojun 384287546eaSitojun /* 385287546eaSitojun * Route packet. 386287546eaSitojun */ 387f1638404Sclaudio #if NPF > 0 388f1638404Sclaudio reroute: 389f1638404Sclaudio #endif 390f1638404Sclaudio 3916b532452Sitojun /* initialize cached route */ 392bc31a334Skrw if (ro == NULL) { 39394c0e2bdSbluhm ro = &iproute; 39400dd3069Sbluhm ro->ro_rt = NULL; 395287546eaSitojun } 396287546eaSitojun ro_pmtu = ro; 397287546eaSitojun if (opt && opt->ip6po_rthdr) 398287546eaSitojun ro = &opt->ip6po_route; 39994c0e2bdSbluhm dst = &ro->ro_dstsin6; 4006b532452Sitojun 401287546eaSitojun /* 4026b532452Sitojun * if specified, try to fill in the traffic class field. 4036b532452Sitojun * do not override if a non-zero value is already set. 4046b532452Sitojun * we check the diffserv field and the ecn field separately. 405287546eaSitojun */ 4066b532452Sitojun if (opt && opt->ip6po_tclass >= 0) { 4076b532452Sitojun int mask = 0; 4086b532452Sitojun 4096b532452Sitojun if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0) 4106b532452Sitojun mask |= 0xfc; 4116b532452Sitojun if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0) 4126b532452Sitojun mask |= 0x03; 4136b532452Sitojun if (mask != 0) 41490dc3aeaSbluhm ip6->ip6_flow |= 41590dc3aeaSbluhm htonl((opt->ip6po_tclass & mask) << 20); 416287546eaSitojun } 4176b532452Sitojun 4186b532452Sitojun /* fill in or override the hop limit field, if necessary. */ 4196b532452Sitojun if (opt && opt->ip6po_hlim != -1) 4206b532452Sitojun ip6->ip6_hlim = opt->ip6po_hlim & 0xff; 4216b532452Sitojun else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 4226b532452Sitojun if (im6o != NULL) 4233c687758Smpi ip6->ip6_hlim = im6o->im6o_hlim; 4246b532452Sitojun else 4256b532452Sitojun ip6->ip6_hlim = ip6_defmcasthlim; 426287546eaSitojun } 4276b532452Sitojun 428bea47bb9Sitojun #ifdef IPSEC 42931a6915fSbluhm if (tdb != NULL) { 430a43d4d9bSreyk /* 431a43d4d9bSreyk * XXX what should we do if ip6_hlim == 0 and the 432a43d4d9bSreyk * packet gets tunneled? 433a43d4d9bSreyk */ 434065a3e52Sitojun /* 435065a3e52Sitojun * if we are source-routing, do not attempt to tunnel the 436065a3e52Sitojun * packet just because ip6_dst is different from what tdb has. 437065a3e52Sitojun * XXX 438065a3e52Sitojun */ 43959caf375Sbluhm error = ip6_output_ipsec_send(tdb, m, ro, 4404a931851Smarkus exthdrs.ip6e_rthdr ? 1 : 0, 0); 4414a931851Smarkus goto done; 442bea47bb9Sitojun } 443bea47bb9Sitojun #endif /* IPSEC */ 444287546eaSitojun 44522723314Sbluhm if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 446b9ab5338Smpi struct in6_pktinfo *pi = NULL; 447b9ab5338Smpi 448b9ab5338Smpi /* 449b9ab5338Smpi * If the caller specify the outgoing interface 450b9ab5338Smpi * explicitly, use it. 451b9ab5338Smpi */ 452b9ab5338Smpi if (opt != NULL && (pi = opt->ip6po_pktinfo) != NULL) 453b9ab5338Smpi ifp = if_get(pi->ipi6_ifindex); 454b9ab5338Smpi 455b9ab5338Smpi if (ifp == NULL && im6o != NULL) 456b9ab5338Smpi ifp = if_get(im6o->im6o_ifidx); 4576b532452Sitojun } 458b9ab5338Smpi 459b9ab5338Smpi if (ifp == NULL) { 46022723314Sbluhm rt = in6_selectroute(&ip6->ip6_dst, opt, ro, 46122723314Sbluhm m->m_pkthdr.ph_rtableid); 462aaf34fadSmpi if (rt == NULL) { 46331e14cacSjca ip6stat_inc(ip6s_noroute); 464aaf34fadSmpi error = EHOSTUNREACH; 4656b532452Sitojun goto bad; 4666b532452Sitojun } 4679e948f09Smpi if (ISSET(rt->rt_flags, RTF_LOCAL)) 4683a63dfc0Smpi ifp = if_get(rtable_loindex(m->m_pkthdr.ph_rtableid)); 4699e948f09Smpi else 470824e7fc7Smpi ifp = if_get(rt->rt_ifidx); 47130aa4938Sbluhm /* 47230aa4938Sbluhm * We aren't using rtisvalid() here because the UP/DOWN state 47330aa4938Sbluhm * machine is broken with some Ethernet drivers like em(4). 47430aa4938Sbluhm * As a result we might try to use an invalid cached route 47530aa4938Sbluhm * entry while an interface is being detached. 47630aa4938Sbluhm */ 47730aa4938Sbluhm if (ifp == NULL) { 47830aa4938Sbluhm ip6stat_inc(ip6s_noroute); 47930aa4938Sbluhm error = EHOSTUNREACH; 48030aa4938Sbluhm goto bad; 48130aa4938Sbluhm } 482aaf34fadSmpi } else { 483caa7f414Sbluhm route6_cache(ro, &ip6->ip6_dst, NULL, m->m_pkthdr.ph_rtableid); 4846b532452Sitojun } 4856b532452Sitojun 486cf618f1eSflorian if (rt && (rt->rt_flags & RTF_GATEWAY) && 487cf618f1eSflorian !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) 488c3c56496Sbluhm dst = satosin6(rt->rt_gateway); 4896b532452Sitojun 490287546eaSitojun if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 491287546eaSitojun /* Unicast */ 492287546eaSitojun 493287546eaSitojun m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ 494287546eaSitojun } else { 495287546eaSitojun /* Multicast */ 496287546eaSitojun 497287546eaSitojun m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; 498287546eaSitojun 499287546eaSitojun /* 500287546eaSitojun * Confirm that the outgoing interface supports multicast. 501287546eaSitojun */ 502287546eaSitojun if ((ifp->if_flags & IFF_MULTICAST) == 0) { 50331e14cacSjca ip6stat_inc(ip6s_noroute); 504287546eaSitojun error = ENETUNREACH; 505287546eaSitojun goto bad; 506287546eaSitojun } 507c91534fbSmpi 50834dbf6d6Smpi if ((im6o == NULL || im6o->im6o_loop) && 50934dbf6d6Smpi in6_hasmulti(&ip6->ip6_dst, ifp)) { 510287546eaSitojun /* 511287546eaSitojun * If we belong to the destination multicast group 512287546eaSitojun * on the outgoing interface, and the caller did not 513287546eaSitojun * forbid loopback, loop back a copy. 514c91534fbSmpi * Can't defer TCP/UDP checksumming, do the 515c91534fbSmpi * computation now. 516287546eaSitojun */ 517c91534fbSmpi in6_proto_cksum_out(m, NULL); 518287546eaSitojun ip6_mloopback(ifp, m, dst); 519c91534fbSmpi } 520c91534fbSmpi #ifdef MROUTING 521c91534fbSmpi else { 522287546eaSitojun /* 523287546eaSitojun * If we are acting as a multicast router, perform 524287546eaSitojun * multicast forwarding as if the packet had just 525287546eaSitojun * arrived on the interface to which we are about 526287546eaSitojun * to send. The multicast forwarding function 527287546eaSitojun * recursively calls this function, using the 528287546eaSitojun * IPV6_FORWARDING flag to prevent infinite recursion. 529287546eaSitojun * 530287546eaSitojun * Multicasts that are looped back by ip6_mloopback(), 531287546eaSitojun * above, will be forwarded by the ip6_input() routine, 532287546eaSitojun * if necessary. 533287546eaSitojun */ 5345d692803Srzalamena if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain] && 535d9c6379eSclaudio (flags & IPV6_FORWARDING) == 0) { 53628c60e63Sbluhm if (ip6_mforward(ip6, ifp, m, flags) != 0) { 537287546eaSitojun m_freem(m); 538287546eaSitojun goto done; 539287546eaSitojun } 540287546eaSitojun } 541287546eaSitojun } 542c91534fbSmpi #endif 543287546eaSitojun /* 544287546eaSitojun * Multicasts with a hoplimit of zero may be looped back, 545287546eaSitojun * above, but must not be transmitted on a network. 546287546eaSitojun * Also, multicasts addressed to the loopback interface 547287546eaSitojun * are not sent -- the above call to ip6_mloopback() will 548287546eaSitojun * loop back a copy if this host actually belongs to the 549287546eaSitojun * destination group on the loopback interface. 550287546eaSitojun */ 551d4070096Sitojun if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) || 552d4070096Sitojun IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) { 553287546eaSitojun m_freem(m); 554287546eaSitojun goto done; 555287546eaSitojun } 556287546eaSitojun } 557287546eaSitojun 558287546eaSitojun /* 559678831beSjsg * If this packet is going through a loopback interface we won't 560cd5ec7edSmpi * be able to restore its scope ID using the interface index. 561cd5ec7edSmpi */ 562cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) { 563cd5ec7edSmpi if (ifp->if_flags & IFF_LOOPBACK) 564cd5ec7edSmpi src_scope = ip6->ip6_src.s6_addr16[1]; 565cd5ec7edSmpi ip6->ip6_src.s6_addr16[1] = 0; 566cd5ec7edSmpi } 567cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) { 568cd5ec7edSmpi if (ifp->if_flags & IFF_LOOPBACK) 569cd5ec7edSmpi dst_scope = ip6->ip6_dst.s6_addr16[1]; 570cd5ec7edSmpi ip6->ip6_dst.s6_addr16[1] = 0; 571cd5ec7edSmpi } 572cd5ec7edSmpi 573d6b9e9b9Sitojun /* Determine path MTU. */ 574f3785e27Sbluhm if ((error = ip6_getpmtu(ro_pmtu->ro_rt, ifp, &mtu)) != 0) 575d6b9e9b9Sitojun goto bad; 576287546eaSitojun 577287546eaSitojun /* 578d6b9e9b9Sitojun * The caller of this function may specify to use the minimum MTU 579d6b9e9b9Sitojun * in some cases. 5806b532452Sitojun * An advanced API option (IPV6_USE_MIN_MTU) can also override MTU 5816b532452Sitojun * setting. The logic is a bit complicated; by default, unicast 5826b532452Sitojun * packets will follow path MTU while multicast packets will be sent at 5836b532452Sitojun * the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets 5846b532452Sitojun * including unicast ones will be sent at the minimum MTU. Multicast 5856b532452Sitojun * packets will always be sent at the minimum MTU unless 5866b532452Sitojun * IP6PO_MINMTU_DISABLE is explicitly specified. 5876b532452Sitojun * See RFC 3542 for more details. 588287546eaSitojun */ 589d6b9e9b9Sitojun if (mtu > IPV6_MMTU) { 590d6b9e9b9Sitojun if ((flags & IPV6_MINMTU)) 591d6b9e9b9Sitojun mtu = IPV6_MMTU; 5926b532452Sitojun else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL) 5936b532452Sitojun mtu = IPV6_MMTU; 59490dc3aeaSbluhm else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && (opt == NULL || 5956b532452Sitojun opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) { 5966b532452Sitojun mtu = IPV6_MMTU; 5976b532452Sitojun } 598287546eaSitojun } 599287546eaSitojun 600287546eaSitojun /* 601287546eaSitojun * If the outgoing packet contains a hop-by-hop options header, 602287546eaSitojun * it must be examined and processed even by the source node. 603287546eaSitojun * (RFC 2460, section 4.) 604287546eaSitojun */ 605287546eaSitojun if (exthdrs.ip6e_hbh) { 6067b2ccaf7Sitojun struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); 607dfec937eSkrw u_int32_t rtalert; /* returned value is ignored */ 608dfec937eSkrw u_int32_t plen = 0; /* no more than 1 jumbo payload option! */ 609287546eaSitojun 610fb492c37Smpi m->m_pkthdr.ph_ifidx = ifp->if_index; 61180467c39Sbluhm if (ip6_process_hopopts(&m, (u_int8_t *)(hbh + 1), 6127b2ccaf7Sitojun ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), 613dfec937eSkrw &rtalert, &plen) < 0) { 614287546eaSitojun /* m was already freed at this point */ 615287546eaSitojun error = EINVAL;/* better error? */ 616287546eaSitojun goto done; 617287546eaSitojun } 618fb492c37Smpi m->m_pkthdr.ph_ifidx = 0; 619287546eaSitojun } 620287546eaSitojun 62130620b12Sfrantzen #if NPF > 0 6221f52aa9fSmpi if (pf_test(AF_INET6, PF_OUT, ifp, &m) != PF_PASS) { 623840989e0Sbluhm error = EACCES; 624b13fd930Sjasoni m_freem(m); 62530620b12Sfrantzen goto done; 62630620b12Sfrantzen } 627cb3a4e31Sjasoni if (m == NULL) 628cb3a4e31Sjasoni goto done; 629a1748d09Sjasoni ip6 = mtod(m, struct ip6_hdr *); 630f1638404Sclaudio if ((m->m_pkthdr.pf.flags & (PF_TAG_REROUTE | PF_TAG_GENERATED)) == 631f1638404Sclaudio (PF_TAG_REROUTE | PF_TAG_GENERATED)) { 632f1638404Sclaudio /* already rerun the route lookup, go on */ 633f1638404Sclaudio m->m_pkthdr.pf.flags &= ~(PF_TAG_GENERATED | PF_TAG_REROUTE); 634f1638404Sclaudio } else if (m->m_pkthdr.pf.flags & PF_TAG_REROUTE) { 635f1638404Sclaudio /* tag as generated to skip over pf_test on rerun */ 636f1638404Sclaudio m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; 637f1638404Sclaudio finaldst = ip6->ip6_dst; 638f46106f1Sbluhm if (ro == &iproute) 639f46106f1Sbluhm rtfree(ro->ro_rt); 640f1638404Sclaudio ro = NULL; 6410e3d2ab2Sclaudio if_put(ifp); /* drop reference since destination changed */ 6427cf86a87Smpi ifp = NULL; 643f1638404Sclaudio goto reroute; 644f1638404Sclaudio } 64530620b12Sfrantzen #endif 646bc3c7896Smpi 64728c60e63Sbluhm #ifdef IPSEC 64828c60e63Sbluhm if (ISSET(flags, IPV6_FORWARDING) && 64928c60e63Sbluhm ISSET(flags, IPV6_FORWARDING_IPSEC) && 65028c60e63Sbluhm !ISSET(m->m_pkthdr.ph_tagsset, PACKET_TAG_IPSEC_IN_DONE)) { 65128c60e63Sbluhm error = EHOSTUNREACH; 65228c60e63Sbluhm goto bad; 65328c60e63Sbluhm } 65428c60e63Sbluhm #endif 65528c60e63Sbluhm 656bc3c7896Smpi /* 657df8d9afdSjsg * If the packet is not going on the wire it can be destined 658bc3c7896Smpi * to any local address. In this case do not clear its scopes 659bc3c7896Smpi * to let ip6_input() find a matching local route. 660bc3c7896Smpi */ 661cd5ec7edSmpi if (ifp->if_flags & IFF_LOOPBACK) { 662cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) 663cd5ec7edSmpi ip6->ip6_src.s6_addr16[1] = src_scope; 664cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) 665cd5ec7edSmpi ip6->ip6_dst.s6_addr16[1] = dst_scope; 666cd5ec7edSmpi } 667cd5ec7edSmpi 668287546eaSitojun /* 669287546eaSitojun * Send the packet to the outgoing interface. 670287546eaSitojun * If necessary, do IPv6 fragmentation before sending. 671a8662718Sitojun * 672a8662718Sitojun * the logic here is rather complex: 673f3785e27Sbluhm * 1: normal case (dontfrag == 0) 674a8662718Sitojun * 1-a: send as is if tlen <= path mtu 675a8662718Sitojun * 1-b: fragment if tlen > path mtu 676a8662718Sitojun * 677a8662718Sitojun * 2: if user asks us not to fragment (dontfrag == 1) 678a8662718Sitojun * 2-a: send as is if tlen <= interface mtu 679a8662718Sitojun * 2-b: error if tlen > interface mtu 680287546eaSitojun */ 6815ebaba9dSbluhm tlen = ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) ? 6825ebaba9dSbluhm m->m_pkthdr.ph_mss : m->m_pkthdr.len; 683a8662718Sitojun 68445ad364fSdlg if (ISSET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT)) { 68545ad364fSdlg CLR(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT); 68645ad364fSdlg dontfrag = 1; 68745ad364fSdlg } else if (opt && ISSET(opt->ip6po_flags, IP6PO_DONTFRAG)) 6886b532452Sitojun dontfrag = 1; 6896b532452Sitojun else 690a8662718Sitojun dontfrag = 0; 6915ebaba9dSbluhm 6925ebaba9dSbluhm if (dontfrag && tlen > ifp->if_mtu) { /* case 2-b */ 69389de4c79Sbluhm #ifdef IPSEC 69489de4c79Sbluhm if (ip_mtudisc) 69589de4c79Sbluhm ipsec_adjust_mtu(m, mtu); 69689de4c79Sbluhm #endif 697a8662718Sitojun error = EMSGSIZE; 698a8662718Sitojun goto bad; 699a8662718Sitojun } 700a8662718Sitojun 701a8662718Sitojun /* 702a8662718Sitojun * transmit packet without fragmentation 703a8662718Sitojun */ 704c06845b1Sbluhm if (dontfrag || tlen <= mtu) { /* case 1-a and 2-a */ 7055ebaba9dSbluhm error = if_output_tso(ifp, &m, sin6tosa(dst), ro->ro_rt, 7065ebaba9dSbluhm ifp->if_mtu); 707510f4386Sbluhm if (error || m == NULL) 708c06845b1Sbluhm goto done; 7095ebaba9dSbluhm goto bad; /* should not happen */ 7105ebaba9dSbluhm } 711c06845b1Sbluhm 712287546eaSitojun /* 713f3785e27Sbluhm * try to fragment the packet. case 1-b 714287546eaSitojun */ 715a8662718Sitojun if (mtu < IPV6_MMTU) { 716a8662718Sitojun /* path MTU cannot be less than IPV6_MMTU */ 717287546eaSitojun error = EMSGSIZE; 718287546eaSitojun goto bad; 719a8662718Sitojun } else if (ip6->ip6_plen == 0) { 720a8662718Sitojun /* jumbo payload cannot be fragmented */ 721287546eaSitojun error = EMSGSIZE; 722287546eaSitojun goto bad; 7232818ef99Sbluhm } 724287546eaSitojun 725287546eaSitojun /* 726287546eaSitojun * Too large for the destination or interface; 727287546eaSitojun * fragment if possible. 728287546eaSitojun * Must be able to put at least 8 bytes per fragment. 729287546eaSitojun */ 730287546eaSitojun hlen = unfragpartlen; 731287546eaSitojun if (mtu > IPV6_MAXPACKET) 732287546eaSitojun mtu = IPV6_MAXPACKET; 733a8662718Sitojun 734287546eaSitojun /* 735ce9d227cSbluhm * If we are doing fragmentation, we can't defer TCP/UDP 736ce9d227cSbluhm * checksumming; compute the checksum and clear the flag. 737ce9d227cSbluhm */ 738ce9d227cSbluhm in6_proto_cksum_out(m, NULL); 739ce9d227cSbluhm 740ce9d227cSbluhm /* 741287546eaSitojun * Change the next header field of the last header in the 742287546eaSitojun * unfragmentable part. 743287546eaSitojun */ 744287546eaSitojun if (exthdrs.ip6e_rthdr) { 745287546eaSitojun nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); 746287546eaSitojun *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; 7476afad192Sitojun } else if (exthdrs.ip6e_dest1) { 748287546eaSitojun nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); 749287546eaSitojun *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; 7506afad192Sitojun } else if (exthdrs.ip6e_hbh) { 751287546eaSitojun nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); 752287546eaSitojun *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; 7536afad192Sitojun } else { 754287546eaSitojun nextproto = ip6->ip6_nxt; 755287546eaSitojun ip6->ip6_nxt = IPPROTO_FRAGMENT; 756287546eaSitojun } 757287546eaSitojun 758b8646e37Sbluhm if ((error = ip6_fragment(m, &ml, hlen, nextproto, mtu)) || 759b8646e37Sbluhm (error = if_output_ml(ifp, &ml, sin6tosa(dst), ro->ro_rt))) 7602818ef99Sbluhm goto done; 76131e14cacSjca ip6stat_inc(ip6s_fragmented); 7622136a888Sbluhm goto done; 763287546eaSitojun 7642136a888Sbluhm freehdrs: 7652136a888Sbluhm m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ 7662136a888Sbluhm m_freem(exthdrs.ip6e_dest1); 7672136a888Sbluhm m_freem(exthdrs.ip6e_rthdr); 7682136a888Sbluhm m_freem(exthdrs.ip6e_dest2); 7692136a888Sbluhm bad: 7702136a888Sbluhm m_freem(m); 771287546eaSitojun done: 772f46106f1Sbluhm if (ro == &iproute) 77327ae666cSmpi rtfree(ro->ro_rt); 774f46106f1Sbluhm else if (ro_pmtu == &iproute) 77527ae666cSmpi rtfree(ro_pmtu->ro_rt); 77631a6915fSbluhm if_put(ifp); 77731a6915fSbluhm #ifdef IPSEC 77831a6915fSbluhm tdb_unref(tdb); 77931a6915fSbluhm #endif /* IPSEC */ 780287546eaSitojun return (error); 781287546eaSitojun } 782287546eaSitojun 783d4984c21Sjsing int 784b8646e37Sbluhm ip6_fragment(struct mbuf *m0, struct mbuf_list *ml, int hlen, u_char nextproto, 785b8646e37Sbluhm u_long mtu) 786a8cafab0Sbluhm { 7874daa6442Sbluhm struct ip6_hdr *ip6; 788a8cafab0Sbluhm u_int32_t id; 789a8cafab0Sbluhm int tlen, len, off; 790a8cafab0Sbluhm int error; 791a8cafab0Sbluhm 792b8646e37Sbluhm ml_init(ml); 793a8cafab0Sbluhm 7944daa6442Sbluhm ip6 = mtod(m0, struct ip6_hdr *); 795a8cafab0Sbluhm tlen = m0->m_pkthdr.len; 796a8cafab0Sbluhm len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; 7972818ef99Sbluhm if (len < 8) { 7982818ef99Sbluhm error = EMSGSIZE; 7992818ef99Sbluhm goto bad; 8002818ef99Sbluhm } 8012818ef99Sbluhm id = htonl(ip6_randomid()); 802a8cafab0Sbluhm 803a8cafab0Sbluhm /* 804b8646e37Sbluhm * Loop through length of payload, 8052818ef99Sbluhm * make new header and copy data of each part and link onto chain. 806a8cafab0Sbluhm */ 807a8cafab0Sbluhm for (off = hlen; off < tlen; off += len) { 808b8646e37Sbluhm struct mbuf *m; 809a8cafab0Sbluhm struct mbuf *mlast; 8104daa6442Sbluhm struct ip6_hdr *mhip6; 8114daa6442Sbluhm struct ip6_frag *ip6f; 812a8cafab0Sbluhm 8132818ef99Sbluhm MGETHDR(m, M_DONTWAIT, MT_HEADER); 8142818ef99Sbluhm if (m == NULL) { 8152818ef99Sbluhm error = ENOBUFS; 8162818ef99Sbluhm goto bad; 8172818ef99Sbluhm } 818b8646e37Sbluhm ml_enqueue(ml, m); 8197fbb3004Sblambert if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0) 8202818ef99Sbluhm goto bad; 821a8cafab0Sbluhm m->m_data += max_linkhdr; 822a8cafab0Sbluhm mhip6 = mtod(m, struct ip6_hdr *); 8234daa6442Sbluhm *mhip6 = *ip6; 8244daa6442Sbluhm m->m_len = sizeof(struct ip6_hdr); 8254daa6442Sbluhm 826a8cafab0Sbluhm if ((error = ip6_insertfraghdr(m0, m, hlen, &ip6f)) != 0) 8272818ef99Sbluhm goto bad; 8284daa6442Sbluhm ip6f->ip6f_offlg = htons((off - hlen) & ~7); 829a8cafab0Sbluhm if (off + len >= tlen) 830a8cafab0Sbluhm len = tlen - off; 831a8cafab0Sbluhm else 832a8cafab0Sbluhm ip6f->ip6f_offlg |= IP6F_MORE_FRAG; 8334daa6442Sbluhm 8344daa6442Sbluhm m->m_pkthdr.len = hlen + sizeof(struct ip6_frag) + len; 8354daa6442Sbluhm mhip6->ip6_plen = htons(m->m_pkthdr.len - 8364daa6442Sbluhm sizeof(struct ip6_hdr)); 8374daa6442Sbluhm for (mlast = m; mlast->m_next; mlast = mlast->m_next) 8384daa6442Sbluhm ; 8394daa6442Sbluhm mlast->m_next = m_copym(m0, off, len, M_DONTWAIT); 8404daa6442Sbluhm if (mlast->m_next == NULL) { 8412818ef99Sbluhm error = ENOBUFS; 8422818ef99Sbluhm goto bad; 8432818ef99Sbluhm } 8444daa6442Sbluhm 845a8cafab0Sbluhm ip6f->ip6f_reserved = 0; 846a8cafab0Sbluhm ip6f->ip6f_ident = id; 847a8cafab0Sbluhm ip6f->ip6f_nxt = nextproto; 848a8cafab0Sbluhm } 849a8cafab0Sbluhm 850b8646e37Sbluhm ip6stat_add(ip6s_ofragments, ml_len(ml)); 8512818ef99Sbluhm m_freem(m0); 852a8cafab0Sbluhm return (0); 8532818ef99Sbluhm 8542818ef99Sbluhm bad: 8552818ef99Sbluhm ip6stat_inc(ip6s_odropped); 856b8646e37Sbluhm ml_purge(ml); 8572818ef99Sbluhm m_freem(m0); 8582818ef99Sbluhm return (error); 859a8cafab0Sbluhm } 860a8cafab0Sbluhm 861a8cafab0Sbluhm int 862ee37ea65Smcbride ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen) 863287546eaSitojun { 864287546eaSitojun struct mbuf *m; 865287546eaSitojun 866287546eaSitojun if (hlen > MCLBYTES) 867287546eaSitojun return (ENOBUFS); /* XXX */ 868287546eaSitojun 869287546eaSitojun MGET(m, M_DONTWAIT, MT_DATA); 870287546eaSitojun if (!m) 871287546eaSitojun return (ENOBUFS); 872287546eaSitojun 873287546eaSitojun if (hlen > MLEN) { 874287546eaSitojun MCLGET(m, M_DONTWAIT); 875287546eaSitojun if ((m->m_flags & M_EXT) == 0) { 876287546eaSitojun m_free(m); 877287546eaSitojun return (ENOBUFS); 878287546eaSitojun } 879287546eaSitojun } 880287546eaSitojun m->m_len = hlen; 881287546eaSitojun if (hdr) 882e3b5aff7Sdhill memcpy(mtod(m, caddr_t), hdr, hlen); 883287546eaSitojun 884287546eaSitojun *mp = m; 885287546eaSitojun return (0); 886287546eaSitojun } 887287546eaSitojun 888287546eaSitojun /* 889287546eaSitojun * Insert jumbo payload option. 890287546eaSitojun */ 891d4984c21Sjsing int 892ee37ea65Smcbride ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen) 893287546eaSitojun { 894287546eaSitojun struct mbuf *mopt; 8956f04ba85Sitojun u_int8_t *optbuf; 8964df4d1f1Sitojun u_int32_t v; 897287546eaSitojun 898287546eaSitojun #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ 899287546eaSitojun 900287546eaSitojun /* 901287546eaSitojun * If there is no hop-by-hop options header, allocate new one. 902287546eaSitojun * If there is one but it doesn't have enough space to store the 903287546eaSitojun * jumbo payload option, allocate a cluster to store the whole options. 904287546eaSitojun * Otherwise, use it to store the options. 905287546eaSitojun */ 906287546eaSitojun if (exthdrs->ip6e_hbh == 0) { 907287546eaSitojun MGET(mopt, M_DONTWAIT, MT_DATA); 90864a3f76cSjsg if (mopt == NULL) 909287546eaSitojun return (ENOBUFS); 910287546eaSitojun mopt->m_len = JUMBOOPTLEN; 9116f04ba85Sitojun optbuf = mtod(mopt, u_int8_t *); 912287546eaSitojun optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ 913287546eaSitojun exthdrs->ip6e_hbh = mopt; 9146afad192Sitojun } else { 915287546eaSitojun struct ip6_hbh *hbh; 916287546eaSitojun 917287546eaSitojun mopt = exthdrs->ip6e_hbh; 918b5b7f62eSclaudio if (m_trailingspace(mopt) < JUMBOOPTLEN) { 9194df4d1f1Sitojun /* 9204df4d1f1Sitojun * XXX assumption: 9214df4d1f1Sitojun * - exthdrs->ip6e_hbh is not referenced from places 9224df4d1f1Sitojun * other than exthdrs. 9234df4d1f1Sitojun * - exthdrs->ip6e_hbh is not an mbuf chain. 9244df4d1f1Sitojun */ 925287546eaSitojun int oldoptlen = mopt->m_len; 9264df4d1f1Sitojun struct mbuf *n; 927287546eaSitojun 9284df4d1f1Sitojun /* 9294df4d1f1Sitojun * XXX: give up if the whole (new) hbh header does 9304df4d1f1Sitojun * not fit even in an mbuf cluster. 9314df4d1f1Sitojun */ 9324df4d1f1Sitojun if (oldoptlen + JUMBOOPTLEN > MCLBYTES) 933287546eaSitojun return (ENOBUFS); 934287546eaSitojun 9354df4d1f1Sitojun /* 9364df4d1f1Sitojun * As a consequence, we must always prepare a cluster 9374df4d1f1Sitojun * at this point. 9384df4d1f1Sitojun */ 9394df4d1f1Sitojun MGET(n, M_DONTWAIT, MT_DATA); 9404df4d1f1Sitojun if (n) { 9414df4d1f1Sitojun MCLGET(n, M_DONTWAIT); 9424df4d1f1Sitojun if ((n->m_flags & M_EXT) == 0) { 9434df4d1f1Sitojun m_freem(n); 9444df4d1f1Sitojun n = NULL; 9454df4d1f1Sitojun } 9464df4d1f1Sitojun } 9474df4d1f1Sitojun if (!n) 9484df4d1f1Sitojun return (ENOBUFS); 9494df4d1f1Sitojun n->m_len = oldoptlen + JUMBOOPTLEN; 950e3b5aff7Sdhill memcpy(mtod(n, caddr_t), mtod(mopt, caddr_t), 9514df4d1f1Sitojun oldoptlen); 9526f04ba85Sitojun optbuf = mtod(n, u_int8_t *) + oldoptlen; 9534df4d1f1Sitojun m_freem(mopt); 954672f01bcSitojun mopt = exthdrs->ip6e_hbh = n; 9556afad192Sitojun } else { 9566f04ba85Sitojun optbuf = mtod(mopt, u_int8_t *) + mopt->m_len; 957287546eaSitojun mopt->m_len += JUMBOOPTLEN; 958287546eaSitojun } 959287546eaSitojun optbuf[0] = IP6OPT_PADN; 960db177b5aSitojun optbuf[1] = 0; 961287546eaSitojun 962287546eaSitojun /* 963287546eaSitojun * Adjust the header length according to the pad and 964287546eaSitojun * the jumbo payload option. 965287546eaSitojun */ 966287546eaSitojun hbh = mtod(mopt, struct ip6_hbh *); 967287546eaSitojun hbh->ip6h_len += (JUMBOOPTLEN >> 3); 968287546eaSitojun } 969287546eaSitojun 970287546eaSitojun /* fill in the option. */ 971287546eaSitojun optbuf[2] = IP6OPT_JUMBO; 972287546eaSitojun optbuf[3] = 4; 9734df4d1f1Sitojun v = (u_int32_t)htonl(plen + JUMBOOPTLEN); 97441bfede9Stedu memcpy(&optbuf[4], &v, sizeof(u_int32_t)); 975287546eaSitojun 976287546eaSitojun /* finally, adjust the packet header length */ 977287546eaSitojun exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; 978287546eaSitojun 979287546eaSitojun return (0); 980287546eaSitojun #undef JUMBOOPTLEN 981287546eaSitojun } 982287546eaSitojun 983287546eaSitojun /* 984287546eaSitojun * Insert fragment header and copy unfragmentable header portions. 985287546eaSitojun */ 986d4984c21Sjsing int 987ee37ea65Smcbride ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, 988ee37ea65Smcbride struct ip6_frag **frghdrp) 989287546eaSitojun { 990287546eaSitojun struct mbuf *n, *mlast; 991287546eaSitojun 992287546eaSitojun if (hlen > sizeof(struct ip6_hdr)) { 993287546eaSitojun n = m_copym(m0, sizeof(struct ip6_hdr), 994287546eaSitojun hlen - sizeof(struct ip6_hdr), M_DONTWAIT); 995bc31a334Skrw if (n == NULL) 996287546eaSitojun return (ENOBUFS); 997287546eaSitojun m->m_next = n; 9986afad192Sitojun } else 999287546eaSitojun n = m; 1000287546eaSitojun 1001287546eaSitojun /* Search for the last mbuf of unfragmentable part. */ 1002287546eaSitojun for (mlast = n; mlast->m_next; mlast = mlast->m_next) 1003287546eaSitojun ; 1004287546eaSitojun 1005287546eaSitojun if ((mlast->m_flags & M_EXT) == 0 && 1006b5b7f62eSclaudio m_trailingspace(mlast) >= sizeof(struct ip6_frag)) { 100790dc3aeaSbluhm /* use the trailing space of the last mbuf for fragment hdr */ 10087b2ccaf7Sitojun *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) + 10097b2ccaf7Sitojun mlast->m_len); 1010287546eaSitojun mlast->m_len += sizeof(struct ip6_frag); 1011287546eaSitojun m->m_pkthdr.len += sizeof(struct ip6_frag); 10126afad192Sitojun } else { 1013287546eaSitojun /* allocate a new mbuf for the fragment header */ 1014287546eaSitojun struct mbuf *mfrg; 1015287546eaSitojun 1016287546eaSitojun MGET(mfrg, M_DONTWAIT, MT_DATA); 101764a3f76cSjsg if (mfrg == NULL) 1018287546eaSitojun return (ENOBUFS); 1019287546eaSitojun mfrg->m_len = sizeof(struct ip6_frag); 1020287546eaSitojun *frghdrp = mtod(mfrg, struct ip6_frag *); 1021287546eaSitojun mlast->m_next = mfrg; 1022287546eaSitojun } 1023287546eaSitojun 1024287546eaSitojun return (0); 1025287546eaSitojun } 1026287546eaSitojun 1027d4984c21Sjsing int 1028f3785e27Sbluhm ip6_getpmtu(struct rtentry *rt, struct ifnet *ifp, u_long *mtup) 1029d6b9e9b9Sitojun { 1030*84d9c64aSbluhm u_int mtu, rtmtu; 1031d6b9e9b9Sitojun int error = 0; 1032d6b9e9b9Sitojun 103373ae3de3Smpi if (rt != NULL) { 1034*84d9c64aSbluhm mtu = rtmtu = atomic_load_int(&rt->rt_mtu); 1035d69f1ffaSitojun if (mtu == 0) 1036a923c847Sflorian mtu = ifp->if_mtu; 1037a8662718Sitojun else if (mtu < IPV6_MMTU) { 1038f3785e27Sbluhm /* RFC8021 IPv6 Atomic Fragments Considered Harmful */ 1039a8662718Sitojun mtu = IPV6_MMTU; 1040a923c847Sflorian } else if (mtu > ifp->if_mtu) { 1041d6b9e9b9Sitojun /* 1042d6b9e9b9Sitojun * The MTU on the route is larger than the MTU on 1043d6b9e9b9Sitojun * the interface! This shouldn't happen, unless the 1044d6b9e9b9Sitojun * MTU of the interface has been changed after the 1045d6b9e9b9Sitojun * interface was brought up. Change the MTU in the 1046d6b9e9b9Sitojun * route to match the interface MTU (as long as the 1047d6b9e9b9Sitojun * field isn't locked). 1048d6b9e9b9Sitojun */ 1049a923c847Sflorian mtu = ifp->if_mtu; 10509b6a6412Sbluhm if (!(rt->rt_locks & RTV_MTU)) 1051*84d9c64aSbluhm atomic_cas_uint(&rt->rt_mtu, rtmtu, mtu); 1052d6b9e9b9Sitojun } 105373ae3de3Smpi } else { 105473ae3de3Smpi mtu = ifp->if_mtu; 105573ae3de3Smpi } 1056d6b9e9b9Sitojun 1057d6b9e9b9Sitojun *mtup = mtu; 1058d6b9e9b9Sitojun return (error); 1059d6b9e9b9Sitojun } 1060d6b9e9b9Sitojun 1061287546eaSitojun /* 1062287546eaSitojun * IP6 socket option processing. 1063287546eaSitojun */ 1064287546eaSitojun int 1065ee37ea65Smcbride ip6_ctloutput(int op, struct socket *so, int level, int optname, 1066490e0738Sdhill struct mbuf *m) 1067287546eaSitojun { 10686b532452Sitojun int privileged, optdatalen, uproto; 10696b532452Sitojun void *optdata; 10709063e41fSitojun struct inpcb *inp = sotoinpcb(so); 1071287546eaSitojun int error, optval; 107244c85a19Smpi struct proc *p = curproc; /* For IPsec and rdomain */ 1073f74da371Smvs u_int rtableid, rtid = 0; 1074287546eaSitojun 1075287546eaSitojun error = optval = 0; 1076287546eaSitojun 1077287546eaSitojun privileged = (inp->inp_socket->so_state & SS_PRIV); 10786b532452Sitojun uproto = (int)so->so_proto->pr_protocol; 1079287546eaSitojun 1080e09b1b7cSmpi if (level != IPPROTO_IPV6) 1081127aca78Sdhill return (EINVAL); 1082127aca78Sdhill 1083f74da371Smvs rtableid = p->p_p->ps_rtableid; 1084f74da371Smvs 1085287546eaSitojun switch (op) { 1086287546eaSitojun case PRCO_SETOPT: 1087287546eaSitojun switch (optname) { 10886b532452Sitojun /* 10896b532452Sitojun * Use of some Hop-by-Hop options or some 10906b532452Sitojun * Destination options, might require special 10916b532452Sitojun * privilege. That is, normal applications 10926b532452Sitojun * (without special privilege) might be forbidden 10936b532452Sitojun * from setting certain options in outgoing packets, 10946b532452Sitojun * and might never see certain options in received 10956b532452Sitojun * packets. [RFC 2292 Section 6] 10966b532452Sitojun * KAME specific note: 10976b532452Sitojun * KAME prevents non-privileged users from sending or 10986b532452Sitojun * receiving ANY hbh/dst options in order to avoid 10996b532452Sitojun * overhead of parsing options in the kernel. 11006b532452Sitojun */ 11016b532452Sitojun case IPV6_RECVHOPOPTS: 11026b532452Sitojun case IPV6_RECVDSTOPTS: 1103287546eaSitojun if (!privileged) { 1104287546eaSitojun error = EPERM; 1105287546eaSitojun break; 1106287546eaSitojun } 11079a9482baSitojun /* FALLTHROUGH */ 1108287546eaSitojun case IPV6_UNICAST_HOPS: 1109e5ff19c7Sjca case IPV6_MINHOPCOUNT: 1110287546eaSitojun case IPV6_HOPLIMIT: 11116b532452Sitojun 11126b532452Sitojun case IPV6_RECVPKTINFO: 11136b532452Sitojun case IPV6_RECVHOPLIMIT: 11146b532452Sitojun case IPV6_RECVRTHDR: 11156b532452Sitojun case IPV6_RECVPATHMTU: 11166b532452Sitojun case IPV6_RECVTCLASS: 11171782763dSitojun case IPV6_V6ONLY: 11186b532452Sitojun case IPV6_AUTOFLOWLABEL: 1119cee801dcSyasuoka case IPV6_RECVDSTPORT: 112053f19499Sguenther if (m == NULL || m->m_len != sizeof(int)) { 1121287546eaSitojun error = EINVAL; 11227b2ccaf7Sitojun break; 11237b2ccaf7Sitojun } 1124287546eaSitojun optval = *mtod(m, int *); 1125287546eaSitojun switch (optname) { 1126287546eaSitojun 1127287546eaSitojun case IPV6_UNICAST_HOPS: 1128287546eaSitojun if (optval < -1 || optval >= 256) 1129287546eaSitojun error = EINVAL; 1130287546eaSitojun else { 1131287546eaSitojun /* -1 = kernel default */ 1132287546eaSitojun inp->inp_hops = optval; 1133287546eaSitojun } 1134287546eaSitojun break; 1135e5ff19c7Sjca 1136e5ff19c7Sjca case IPV6_MINHOPCOUNT: 1137e5ff19c7Sjca if (optval < 0 || optval > 255) 1138e5ff19c7Sjca error = EINVAL; 1139e5ff19c7Sjca else 1140e5ff19c7Sjca inp->inp_ip6_minhlim = optval; 1141e5ff19c7Sjca break; 1142e5ff19c7Sjca 1143287546eaSitojun #define OPTSET(bit) \ 11447b2ccaf7Sitojun do { \ 1145287546eaSitojun if (optval) \ 11467b2ccaf7Sitojun inp->inp_flags |= (bit); \ 1147287546eaSitojun else \ 11487b2ccaf7Sitojun inp->inp_flags &= ~(bit); \ 11496b532452Sitojun } while (/*CONSTCOND*/ 0) 11506b532452Sitojun #define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0) 1151287546eaSitojun 11526b532452Sitojun case IPV6_RECVPKTINFO: 1153287546eaSitojun OPTSET(IN6P_PKTINFO); 1154287546eaSitojun break; 1155287546eaSitojun 1156287546eaSitojun case IPV6_HOPLIMIT: 11576b532452Sitojun { 11586b532452Sitojun struct ip6_pktopts **optp; 11596b532452Sitojun 11606b532452Sitojun optp = &inp->inp_outputopts6; 11616b532452Sitojun error = ip6_pcbopt(IPV6_HOPLIMIT, 116290dc3aeaSbluhm (u_char *)&optval, sizeof(optval), optp, 11636b532452Sitojun privileged, uproto); 11646b532452Sitojun break; 11656b532452Sitojun } 11666b532452Sitojun 11676b532452Sitojun case IPV6_RECVHOPLIMIT: 1168287546eaSitojun OPTSET(IN6P_HOPLIMIT); 1169287546eaSitojun break; 1170287546eaSitojun 11716b532452Sitojun case IPV6_RECVHOPOPTS: 1172287546eaSitojun OPTSET(IN6P_HOPOPTS); 1173287546eaSitojun break; 1174287546eaSitojun 11756b532452Sitojun case IPV6_RECVDSTOPTS: 1176287546eaSitojun OPTSET(IN6P_DSTOPTS); 1177287546eaSitojun break; 1178287546eaSitojun 11796b532452Sitojun case IPV6_RECVRTHDR: 1180287546eaSitojun OPTSET(IN6P_RTHDR); 1181287546eaSitojun break; 1182287546eaSitojun 11836b532452Sitojun case IPV6_RECVPATHMTU: 11846b532452Sitojun /* 11856b532452Sitojun * We ignore this option for TCP 11866b532452Sitojun * sockets. 11876b532452Sitojun * (RFC3542 leaves this case 11886b532452Sitojun * unspecified.) 11896b532452Sitojun */ 11906b532452Sitojun if (uproto != IPPROTO_TCP) 11916b532452Sitojun OPTSET(IN6P_MTU); 11920a46a566Sitojun break; 11930a46a566Sitojun 11941782763dSitojun case IPV6_V6ONLY: 11956b532452Sitojun /* 11966b532452Sitojun * make setsockopt(IPV6_V6ONLY) 11976b532452Sitojun * available only prior to bind(2). 11986b532452Sitojun * see ipng mailing list, Jun 22 2001. 11996b532452Sitojun */ 120090dc3aeaSbluhm if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED( 120190dc3aeaSbluhm &inp->inp_laddr6)) { 12021782763dSitojun error = EINVAL; 12031782763dSitojun break; 1204287546eaSitojun } 12057e2f83f7Sjca /* No support for IPv4-mapped addresses. */ 12067e2f83f7Sjca if (!optval) 12076b532452Sitojun error = EINVAL; 12087e2f83f7Sjca else 12097e2f83f7Sjca error = 0; 1210287546eaSitojun break; 12116b532452Sitojun case IPV6_RECVTCLASS: 12126b532452Sitojun OPTSET(IN6P_TCLASS); 12136b532452Sitojun break; 12146b532452Sitojun case IPV6_AUTOFLOWLABEL: 12156b532452Sitojun OPTSET(IN6P_AUTOFLOWLABEL); 12166b532452Sitojun break; 12176b532452Sitojun 1218cee801dcSyasuoka case IPV6_RECVDSTPORT: 1219cee801dcSyasuoka OPTSET(IN6P_RECVDSTPORT); 1220cee801dcSyasuoka break; 12216b532452Sitojun } 12226b532452Sitojun break; 12236b532452Sitojun 12246b532452Sitojun case IPV6_TCLASS: 12256b532452Sitojun case IPV6_DONTFRAG: 12266b532452Sitojun case IPV6_USE_MIN_MTU: 122753f19499Sguenther if (m == NULL || m->m_len != sizeof(optval)) { 12286b532452Sitojun error = EINVAL; 12296b532452Sitojun break; 12306b532452Sitojun } 12316b532452Sitojun optval = *mtod(m, int *); 12326b532452Sitojun { 12336b532452Sitojun struct ip6_pktopts **optp; 12346b532452Sitojun optp = &inp->inp_outputopts6; 123590dc3aeaSbluhm error = ip6_pcbopt(optname, (u_char *)&optval, 123690dc3aeaSbluhm sizeof(optval), optp, privileged, uproto); 12376b532452Sitojun break; 12386b532452Sitojun } 12396b532452Sitojun 12406b532452Sitojun case IPV6_PKTINFO: 12416b532452Sitojun case IPV6_HOPOPTS: 12426b532452Sitojun case IPV6_RTHDR: 12436b532452Sitojun case IPV6_DSTOPTS: 12446b532452Sitojun case IPV6_RTHDRDSTOPTS: 12456b532452Sitojun { 12466b532452Sitojun /* new advanced API (RFC3542) */ 12476b532452Sitojun u_char *optbuf; 12486b532452Sitojun int optbuflen; 12496b532452Sitojun struct ip6_pktopts **optp; 12506b532452Sitojun 12516b532452Sitojun if (m && m->m_next) { 12526b532452Sitojun error = EINVAL; /* XXX */ 12536b532452Sitojun break; 12546b532452Sitojun } 12556b532452Sitojun if (m) { 12566b532452Sitojun optbuf = mtod(m, u_char *); 12576b532452Sitojun optbuflen = m->m_len; 12586b532452Sitojun } else { 12596b532452Sitojun optbuf = NULL; 12606b532452Sitojun optbuflen = 0; 12616b532452Sitojun } 12626b532452Sitojun optp = &inp->inp_outputopts6; 126390dc3aeaSbluhm error = ip6_pcbopt(optname, optbuf, optbuflen, optp, 126490dc3aeaSbluhm privileged, uproto); 12656b532452Sitojun break; 12666b532452Sitojun } 1267287546eaSitojun #undef OPTSET 1268287546eaSitojun 1269287546eaSitojun case IPV6_MULTICAST_IF: 1270287546eaSitojun case IPV6_MULTICAST_HOPS: 1271287546eaSitojun case IPV6_MULTICAST_LOOP: 1272287546eaSitojun case IPV6_JOIN_GROUP: 1273287546eaSitojun case IPV6_LEAVE_GROUP: 1274287546eaSitojun error = ip6_setmoptions(optname, 12756b532452Sitojun &inp->inp_moptions6, 127644a616dfSrzalamena m, inp->inp_rtableid); 1277287546eaSitojun break; 1278287546eaSitojun 1279287546eaSitojun case IPV6_PORTRANGE: 128053f19499Sguenther if (m == NULL || m->m_len != sizeof(int)) { 128153f19499Sguenther error = EINVAL; 128253f19499Sguenther break; 128353f19499Sguenther } 1284287546eaSitojun optval = *mtod(m, int *); 1285287546eaSitojun 1286287546eaSitojun switch (optval) { 1287287546eaSitojun case IPV6_PORTRANGE_DEFAULT: 12881782763dSitojun inp->inp_flags &= ~(IN6P_LOWPORT); 12891782763dSitojun inp->inp_flags &= ~(IN6P_HIGHPORT); 1290287546eaSitojun break; 1291287546eaSitojun 1292287546eaSitojun case IPV6_PORTRANGE_HIGH: 12931782763dSitojun inp->inp_flags &= ~(IN6P_LOWPORT); 12941782763dSitojun inp->inp_flags |= IN6P_HIGHPORT; 1295287546eaSitojun break; 1296287546eaSitojun 1297287546eaSitojun case IPV6_PORTRANGE_LOW: 12981782763dSitojun inp->inp_flags &= ~(IN6P_HIGHPORT); 12991782763dSitojun inp->inp_flags |= IN6P_LOWPORT; 1300287546eaSitojun break; 1301287546eaSitojun 1302287546eaSitojun default: 1303287546eaSitojun error = EINVAL; 1304287546eaSitojun break; 1305287546eaSitojun } 1306287546eaSitojun break; 1307287546eaSitojun 1308bea47bb9Sitojun case IPSEC6_OUTSA: 1309bea47bb9Sitojun error = EINVAL; 1310bea47bb9Sitojun break; 1311bea47bb9Sitojun 1312bea47bb9Sitojun case IPV6_AUTH_LEVEL: 1313bea47bb9Sitojun case IPV6_ESP_TRANS_LEVEL: 1314bea47bb9Sitojun case IPV6_ESP_NETWORK_LEVEL: 1315781371b4Sniklas case IPV6_IPCOMP_LEVEL: 1316bea47bb9Sitojun #ifndef IPSEC 1317bea47bb9Sitojun error = EINVAL; 1318bea47bb9Sitojun #else 131964a3f76cSjsg if (m == NULL || m->m_len != sizeof(int)) { 1320bea47bb9Sitojun error = EINVAL; 1321bea47bb9Sitojun break; 1322bea47bb9Sitojun } 1323bea47bb9Sitojun optval = *mtod(m, int *); 1324bea47bb9Sitojun 1325bea47bb9Sitojun if (optval < IPSEC_LEVEL_BYPASS || 1326bea47bb9Sitojun optval > IPSEC_LEVEL_UNIQUE) { 1327bea47bb9Sitojun error = EINVAL; 1328bea47bb9Sitojun break; 1329bea47bb9Sitojun } 1330bea47bb9Sitojun 1331bea47bb9Sitojun switch (optname) { 1332781371b4Sniklas case IPV6_AUTH_LEVEL: 133372992b81Smpi if (optval < IPSEC_AUTH_LEVEL_DEFAULT && 13343e676399Smpi suser(p)) { 1335bea47bb9Sitojun error = EACCES; 1336bea47bb9Sitojun break; 1337bea47bb9Sitojun } 1338ace0f189Sbluhm inp->inp_seclevel.sl_auth = optval; 1339bea47bb9Sitojun break; 1340bea47bb9Sitojun 1341781371b4Sniklas case IPV6_ESP_TRANS_LEVEL: 134272992b81Smpi if (optval < IPSEC_ESP_TRANS_LEVEL_DEFAULT && 13433e676399Smpi suser(p)) { 1344bea47bb9Sitojun error = EACCES; 1345bea47bb9Sitojun break; 1346bea47bb9Sitojun } 1347ace0f189Sbluhm inp->inp_seclevel.sl_esp_trans = optval; 1348bea47bb9Sitojun break; 1349bea47bb9Sitojun 1350781371b4Sniklas case IPV6_ESP_NETWORK_LEVEL: 135172992b81Smpi if (optval < IPSEC_ESP_NETWORK_LEVEL_DEFAULT && 13523e676399Smpi suser(p)) { 1353bea47bb9Sitojun error = EACCES; 1354bea47bb9Sitojun break; 1355bea47bb9Sitojun } 1356ace0f189Sbluhm inp->inp_seclevel.sl_esp_network = optval; 1357bea47bb9Sitojun break; 1358781371b4Sniklas 1359781371b4Sniklas case IPV6_IPCOMP_LEVEL: 136072992b81Smpi if (optval < IPSEC_IPCOMP_LEVEL_DEFAULT && 13613e676399Smpi suser(p)) { 1362781371b4Sniklas error = EACCES; 1363781371b4Sniklas break; 1364781371b4Sniklas } 1365ace0f189Sbluhm inp->inp_seclevel.sl_ipcomp = optval; 1366781371b4Sniklas break; 1367bea47bb9Sitojun } 1368bea47bb9Sitojun #endif 1369bea47bb9Sitojun break; 1370d83f8b41Sphessler case SO_RTABLE: 1371d83f8b41Sphessler if (m == NULL || m->m_len < sizeof(u_int)) { 1372d83f8b41Sphessler error = EINVAL; 1373d83f8b41Sphessler break; 1374d83f8b41Sphessler } 1375d83f8b41Sphessler rtid = *mtod(m, u_int *); 1376d83f8b41Sphessler if (inp->inp_rtableid == rtid) 1377d83f8b41Sphessler break; 1378d83f8b41Sphessler /* needs privileges to switch when already set */ 1379f74da371Smvs if (rtableid != rtid && rtableid != 0 && 13803e676399Smpi (error = suser(p)) != 0) 1381d83f8b41Sphessler break; 1382cd28665aSbluhm error = in_pcbset_rtableid(inp, rtid); 1383d83f8b41Sphessler break; 1384e30e1ee7Syasuoka case IPV6_PIPEX: 1385e30e1ee7Syasuoka if (m != NULL && m->m_len == sizeof(int)) 1386e30e1ee7Syasuoka inp->inp_pipex = *mtod(m, int *); 1387e30e1ee7Syasuoka else 1388e30e1ee7Syasuoka error = EINVAL; 1389e30e1ee7Syasuoka break; 1390bea47bb9Sitojun 1391287546eaSitojun default: 1392287546eaSitojun error = ENOPROTOOPT; 1393287546eaSitojun break; 1394287546eaSitojun } 1395287546eaSitojun break; 1396287546eaSitojun 1397287546eaSitojun case PRCO_GETOPT: 1398287546eaSitojun switch (optname) { 1399287546eaSitojun 14006b532452Sitojun case IPV6_RECVHOPOPTS: 14016b532452Sitojun case IPV6_RECVDSTOPTS: 1402287546eaSitojun case IPV6_UNICAST_HOPS: 1403b9169547Sjca case IPV6_MINHOPCOUNT: 14046b532452Sitojun case IPV6_RECVPKTINFO: 14056b532452Sitojun case IPV6_RECVHOPLIMIT: 14066b532452Sitojun case IPV6_RECVRTHDR: 14076b532452Sitojun case IPV6_RECVPATHMTU: 14086b532452Sitojun 14091782763dSitojun case IPV6_V6ONLY: 14102510a8f8Sitojun case IPV6_PORTRANGE: 14116b532452Sitojun case IPV6_RECVTCLASS: 14126b532452Sitojun case IPV6_AUTOFLOWLABEL: 1413cee801dcSyasuoka case IPV6_RECVDSTPORT: 1414287546eaSitojun switch (optname) { 1415287546eaSitojun 14166b532452Sitojun case IPV6_RECVHOPOPTS: 14176b532452Sitojun optval = OPTBIT(IN6P_HOPOPTS); 14186b532452Sitojun break; 14196b532452Sitojun 14206b532452Sitojun case IPV6_RECVDSTOPTS: 14216b532452Sitojun optval = OPTBIT(IN6P_DSTOPTS); 14226b532452Sitojun break; 14236b532452Sitojun 1424287546eaSitojun case IPV6_UNICAST_HOPS: 1425287546eaSitojun optval = inp->inp_hops; 1426287546eaSitojun break; 1427287546eaSitojun 1428e5ff19c7Sjca case IPV6_MINHOPCOUNT: 1429e5ff19c7Sjca optval = inp->inp_ip6_minhlim; 1430e5ff19c7Sjca break; 1431e5ff19c7Sjca 14326b532452Sitojun case IPV6_RECVPKTINFO: 1433287546eaSitojun optval = OPTBIT(IN6P_PKTINFO); 1434287546eaSitojun break; 1435287546eaSitojun 14366b532452Sitojun case IPV6_RECVHOPLIMIT: 1437287546eaSitojun optval = OPTBIT(IN6P_HOPLIMIT); 1438287546eaSitojun break; 1439287546eaSitojun 14406b532452Sitojun case IPV6_RECVRTHDR: 1441287546eaSitojun optval = OPTBIT(IN6P_RTHDR); 1442287546eaSitojun break; 1443287546eaSitojun 14446b532452Sitojun case IPV6_RECVPATHMTU: 14456b532452Sitojun optval = OPTBIT(IN6P_MTU); 14466b532452Sitojun break; 14476b532452Sitojun 14481782763dSitojun case IPV6_V6ONLY: 14497e2f83f7Sjca optval = 1; 14501782763dSitojun break; 14511782763dSitojun 1452287546eaSitojun case IPV6_PORTRANGE: 1453287546eaSitojun { 1454287546eaSitojun int flags; 1455287546eaSitojun flags = inp->inp_flags; 1456287546eaSitojun if (flags & IN6P_HIGHPORT) 1457287546eaSitojun optval = IPV6_PORTRANGE_HIGH; 1458287546eaSitojun else if (flags & IN6P_LOWPORT) 1459287546eaSitojun optval = IPV6_PORTRANGE_LOW; 1460287546eaSitojun else 1461287546eaSitojun optval = 0; 1462287546eaSitojun break; 1463287546eaSitojun } 14646b532452Sitojun case IPV6_RECVTCLASS: 14656b532452Sitojun optval = OPTBIT(IN6P_TCLASS); 14666b532452Sitojun break; 14670a46a566Sitojun 14686b532452Sitojun case IPV6_AUTOFLOWLABEL: 14696b532452Sitojun optval = OPTBIT(IN6P_AUTOFLOWLABEL); 14706b532452Sitojun break; 1471cee801dcSyasuoka 1472cee801dcSyasuoka case IPV6_RECVDSTPORT: 1473cee801dcSyasuoka optval = OPTBIT(IN6P_RECVDSTPORT); 1474cee801dcSyasuoka break; 14756b532452Sitojun } 14766b532452Sitojun if (error) 14776b532452Sitojun break; 14786b532452Sitojun m->m_len = sizeof(int); 14796b532452Sitojun *mtod(m, int *) = optval; 14806b532452Sitojun break; 14816b532452Sitojun 14826b532452Sitojun case IPV6_PATHMTU: 14836b532452Sitojun { 14846b532452Sitojun u_long pmtu = 0; 14856b532452Sitojun struct ip6_mtuinfo mtuinfo; 148673ae3de3Smpi struct ifnet *ifp; 148773ae3de3Smpi struct rtentry *rt; 14886b532452Sitojun 14896b532452Sitojun if (!(so->so_state & SS_ISCONNECTED)) 14906b532452Sitojun return (ENOTCONN); 149173ae3de3Smpi 14923dc61bc4Sbluhm rt = in6_pcbrtentry(inp); 149373ae3de3Smpi if (!rtisvalid(rt)) 149473ae3de3Smpi return (EHOSTUNREACH); 149573ae3de3Smpi 149673ae3de3Smpi ifp = if_get(rt->rt_ifidx); 149773ae3de3Smpi if (ifp == NULL) 149873ae3de3Smpi return (EHOSTUNREACH); 14996b532452Sitojun /* 15006b532452Sitojun * XXX: we dot not consider the case of source 15016b532452Sitojun * routing, or optional information to specify 15026b532452Sitojun * the outgoing interface. 15036b532452Sitojun */ 1504f3785e27Sbluhm error = ip6_getpmtu(rt, ifp, &pmtu); 150573ae3de3Smpi if_put(ifp); 15066b532452Sitojun if (error) 15076b532452Sitojun break; 15086b532452Sitojun if (pmtu > IPV6_MAXPACKET) 15096b532452Sitojun pmtu = IPV6_MAXPACKET; 15106b532452Sitojun 15116b532452Sitojun bzero(&mtuinfo, sizeof(mtuinfo)); 15126b532452Sitojun mtuinfo.ip6m_mtu = (u_int32_t)pmtu; 15136b532452Sitojun optdata = (void *)&mtuinfo; 15146b532452Sitojun optdatalen = sizeof(mtuinfo); 15156b532452Sitojun if (optdatalen > MCLBYTES) 15166b532452Sitojun return (EMSGSIZE); /* XXX */ 15176b532452Sitojun if (optdatalen > MLEN) 15186b532452Sitojun MCLGET(m, M_WAIT); 15196b532452Sitojun m->m_len = optdatalen; 15206b532452Sitojun bcopy(optdata, mtod(m, void *), optdatalen); 15216b532452Sitojun break; 15226b532452Sitojun } 15236b532452Sitojun 15246b532452Sitojun case IPV6_PKTINFO: 15256b532452Sitojun case IPV6_HOPOPTS: 15266b532452Sitojun case IPV6_RTHDR: 15276b532452Sitojun case IPV6_DSTOPTS: 15286b532452Sitojun case IPV6_RTHDRDSTOPTS: 15296b532452Sitojun case IPV6_TCLASS: 15306b532452Sitojun case IPV6_DONTFRAG: 15316b532452Sitojun case IPV6_USE_MIN_MTU: 15326b532452Sitojun error = ip6_getpcbopt(inp->inp_outputopts6, 1533490e0738Sdhill optname, m); 15346b532452Sitojun break; 1535287546eaSitojun 1536287546eaSitojun case IPV6_MULTICAST_IF: 1537287546eaSitojun case IPV6_MULTICAST_HOPS: 1538287546eaSitojun case IPV6_MULTICAST_LOOP: 1539287546eaSitojun case IPV6_JOIN_GROUP: 1540287546eaSitojun case IPV6_LEAVE_GROUP: 15416b532452Sitojun error = ip6_getmoptions(optname, 1542490e0738Sdhill inp->inp_moptions6, m); 1543287546eaSitojun break; 1544287546eaSitojun 1545bea47bb9Sitojun case IPSEC6_OUTSA: 1546bea47bb9Sitojun error = EINVAL; 1547bea47bb9Sitojun break; 1548bea47bb9Sitojun 1549bea47bb9Sitojun case IPV6_AUTH_LEVEL: 1550bea47bb9Sitojun case IPV6_ESP_TRANS_LEVEL: 1551bea47bb9Sitojun case IPV6_ESP_NETWORK_LEVEL: 15525ad3f7e5Sniklas case IPV6_IPCOMP_LEVEL: 1553bea47bb9Sitojun #ifndef IPSEC 1554bea47bb9Sitojun m->m_len = sizeof(int); 1555bea47bb9Sitojun *mtod(m, int *) = IPSEC_LEVEL_NONE; 1556bea47bb9Sitojun #else 1557bea47bb9Sitojun m->m_len = sizeof(int); 1558bea47bb9Sitojun switch (optname) { 1559e6e74a97Sitojun case IPV6_AUTH_LEVEL: 1560ace0f189Sbluhm optval = inp->inp_seclevel.sl_auth; 1561bea47bb9Sitojun break; 1562bea47bb9Sitojun 1563e6e74a97Sitojun case IPV6_ESP_TRANS_LEVEL: 1564bea47bb9Sitojun optval = 1565ace0f189Sbluhm inp->inp_seclevel.sl_esp_trans; 1566bea47bb9Sitojun break; 1567bea47bb9Sitojun 1568e6e74a97Sitojun case IPV6_ESP_NETWORK_LEVEL: 1569bea47bb9Sitojun optval = 1570ace0f189Sbluhm inp->inp_seclevel.sl_esp_network; 1571bea47bb9Sitojun break; 15725ad3f7e5Sniklas 1573e6e74a97Sitojun case IPV6_IPCOMP_LEVEL: 1574ace0f189Sbluhm optval = inp->inp_seclevel.sl_ipcomp; 15755ad3f7e5Sniklas break; 1576bea47bb9Sitojun } 1577bea47bb9Sitojun *mtod(m, int *) = optval; 1578bea47bb9Sitojun #endif 1579bea47bb9Sitojun break; 1580d83f8b41Sphessler case SO_RTABLE: 1581d83f8b41Sphessler m->m_len = sizeof(u_int); 158262f883c9Skn *mtod(m, u_int *) = inp->inp_rtableid; 1583d83f8b41Sphessler break; 1584e30e1ee7Syasuoka case IPV6_PIPEX: 1585e30e1ee7Syasuoka m->m_len = sizeof(int); 158662f883c9Skn *mtod(m, int *) = inp->inp_pipex; 1587e30e1ee7Syasuoka break; 1588bea47bb9Sitojun 1589287546eaSitojun default: 1590287546eaSitojun error = ENOPROTOOPT; 1591287546eaSitojun break; 1592287546eaSitojun } 1593287546eaSitojun break; 1594287546eaSitojun } 1595287546eaSitojun return (error); 1596287546eaSitojun } 1597287546eaSitojun 1598f5bfbd04Sitojun int 1599ee37ea65Smcbride ip6_raw_ctloutput(int op, struct socket *so, int level, int optname, 1600490e0738Sdhill struct mbuf *m) 1601f5bfbd04Sitojun { 160253f19499Sguenther int error = 0, optval; 1603f5bfbd04Sitojun const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum); 1604f5bfbd04Sitojun struct inpcb *inp = sotoinpcb(so); 1605f5bfbd04Sitojun 1606e09b1b7cSmpi if (level != IPPROTO_IPV6) 1607f5bfbd04Sitojun return (EINVAL); 1608f5bfbd04Sitojun 1609f5bfbd04Sitojun switch (optname) { 1610f5bfbd04Sitojun case IPV6_CHECKSUM: 1611f5bfbd04Sitojun /* 1612f5bfbd04Sitojun * For ICMPv6 sockets, no modification allowed for checksum 1613f5bfbd04Sitojun * offset, permit "no change" values to help existing apps. 1614f5bfbd04Sitojun * 16156b532452Sitojun * RFC3542 says: "An attempt to set IPV6_CHECKSUM 1616f5bfbd04Sitojun * for an ICMPv6 socket will fail." 16176b532452Sitojun * The current behavior does not meet RFC3542. 1618f5bfbd04Sitojun */ 1619f5bfbd04Sitojun switch (op) { 1620f5bfbd04Sitojun case PRCO_SETOPT: 162153f19499Sguenther if (m == NULL || m->m_len != sizeof(int)) { 1622f5bfbd04Sitojun error = EINVAL; 1623f5bfbd04Sitojun break; 1624f5bfbd04Sitojun } 1625f5bfbd04Sitojun optval = *mtod(m, int *); 1626c526b7b2Sbluhm if (optval < -1 || 1627c526b7b2Sbluhm (optval > 0 && (optval % 2) != 0)) { 1628c526b7b2Sbluhm /* 1629c526b7b2Sbluhm * The API assumes non-negative even offset 1630c526b7b2Sbluhm * values or -1 as a special value. 1631c526b7b2Sbluhm */ 1632f5bfbd04Sitojun error = EINVAL; 163390dc3aeaSbluhm } else if (so->so_proto->pr_protocol == 163490dc3aeaSbluhm IPPROTO_ICMPV6) { 1635f5bfbd04Sitojun if (optval != icmp6off) 1636f5bfbd04Sitojun error = EINVAL; 1637f5bfbd04Sitojun } else 16388e5060ecSderaadt inp->inp_cksum6 = optval; 1639f5bfbd04Sitojun break; 1640f5bfbd04Sitojun 1641f5bfbd04Sitojun case PRCO_GETOPT: 1642f5bfbd04Sitojun if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) 1643f5bfbd04Sitojun optval = icmp6off; 1644f5bfbd04Sitojun else 16458e5060ecSderaadt optval = inp->inp_cksum6; 1646f5bfbd04Sitojun 1647f5bfbd04Sitojun m->m_len = sizeof(int); 1648f5bfbd04Sitojun *mtod(m, int *) = optval; 1649f5bfbd04Sitojun break; 1650f5bfbd04Sitojun 1651f5bfbd04Sitojun default: 1652f5bfbd04Sitojun error = EINVAL; 1653f5bfbd04Sitojun break; 1654f5bfbd04Sitojun } 1655f5bfbd04Sitojun break; 1656f5bfbd04Sitojun 1657f5bfbd04Sitojun default: 1658f5bfbd04Sitojun error = ENOPROTOOPT; 1659f5bfbd04Sitojun break; 1660f5bfbd04Sitojun } 1661f5bfbd04Sitojun 1662f5bfbd04Sitojun return (error); 1663f5bfbd04Sitojun } 1664f5bfbd04Sitojun 1665287546eaSitojun /* 16666b532452Sitojun * initialize ip6_pktopts. beware that there are non-zero default values in 16676b532452Sitojun * the struct. 16686b532452Sitojun */ 16696b532452Sitojun void 1670ee37ea65Smcbride ip6_initpktopts(struct ip6_pktopts *opt) 16716b532452Sitojun { 16726b532452Sitojun bzero(opt, sizeof(*opt)); 16736b532452Sitojun opt->ip6po_hlim = -1; /* -1 means default hop limit */ 16746b532452Sitojun opt->ip6po_tclass = -1; /* -1 means default traffic class */ 16756b532452Sitojun opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY; 16766b532452Sitojun } 16776b532452Sitojun 1678d4984c21Sjsing int 1679ee37ea65Smcbride ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, 1680ee37ea65Smcbride int priv, int uproto) 16816b532452Sitojun { 16826b532452Sitojun struct ip6_pktopts *opt; 16836b532452Sitojun 16846b532452Sitojun if (*pktopt == NULL) { 16856b532452Sitojun *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT, 16866b532452Sitojun M_WAITOK); 16876b532452Sitojun ip6_initpktopts(*pktopt); 16886b532452Sitojun } 16896b532452Sitojun opt = *pktopt; 16906b532452Sitojun 16913ea7534eSjca return (ip6_setpktopt(optname, buf, len, opt, priv, 1, uproto)); 16926b532452Sitojun } 16936b532452Sitojun 1694d4984c21Sjsing int 1695490e0738Sdhill ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct mbuf *m) 16966b532452Sitojun { 16976b532452Sitojun void *optdata = NULL; 16986b532452Sitojun int optdatalen = 0; 16996b532452Sitojun struct ip6_ext *ip6e; 17006b532452Sitojun int error = 0; 17016b532452Sitojun struct in6_pktinfo null_pktinfo; 17026b532452Sitojun int deftclass = 0, on; 17036b532452Sitojun int defminmtu = IP6PO_MINMTU_MCASTONLY; 17046b532452Sitojun 17056b532452Sitojun switch (optname) { 17066b532452Sitojun case IPV6_PKTINFO: 17076b532452Sitojun if (pktopt && pktopt->ip6po_pktinfo) 17086b532452Sitojun optdata = (void *)pktopt->ip6po_pktinfo; 17096b532452Sitojun else { 17106b532452Sitojun /* XXX: we don't have to do this every time... */ 17116b532452Sitojun bzero(&null_pktinfo, sizeof(null_pktinfo)); 17126b532452Sitojun optdata = (void *)&null_pktinfo; 17136b532452Sitojun } 17146b532452Sitojun optdatalen = sizeof(struct in6_pktinfo); 17156b532452Sitojun break; 17166b532452Sitojun case IPV6_TCLASS: 17176b532452Sitojun if (pktopt && pktopt->ip6po_tclass >= 0) 17186b532452Sitojun optdata = (void *)&pktopt->ip6po_tclass; 17196b532452Sitojun else 17206b532452Sitojun optdata = (void *)&deftclass; 17216b532452Sitojun optdatalen = sizeof(int); 17226b532452Sitojun break; 17236b532452Sitojun case IPV6_HOPOPTS: 17246b532452Sitojun if (pktopt && pktopt->ip6po_hbh) { 17256b532452Sitojun optdata = (void *)pktopt->ip6po_hbh; 17266b532452Sitojun ip6e = (struct ip6_ext *)pktopt->ip6po_hbh; 17276b532452Sitojun optdatalen = (ip6e->ip6e_len + 1) << 3; 17286b532452Sitojun } 17296b532452Sitojun break; 17306b532452Sitojun case IPV6_RTHDR: 17316b532452Sitojun if (pktopt && pktopt->ip6po_rthdr) { 17326b532452Sitojun optdata = (void *)pktopt->ip6po_rthdr; 17336b532452Sitojun ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr; 17346b532452Sitojun optdatalen = (ip6e->ip6e_len + 1) << 3; 17356b532452Sitojun } 17366b532452Sitojun break; 17376b532452Sitojun case IPV6_RTHDRDSTOPTS: 17386b532452Sitojun if (pktopt && pktopt->ip6po_dest1) { 17396b532452Sitojun optdata = (void *)pktopt->ip6po_dest1; 17406b532452Sitojun ip6e = (struct ip6_ext *)pktopt->ip6po_dest1; 17416b532452Sitojun optdatalen = (ip6e->ip6e_len + 1) << 3; 17426b532452Sitojun } 17436b532452Sitojun break; 17446b532452Sitojun case IPV6_DSTOPTS: 17456b532452Sitojun if (pktopt && pktopt->ip6po_dest2) { 17466b532452Sitojun optdata = (void *)pktopt->ip6po_dest2; 17476b532452Sitojun ip6e = (struct ip6_ext *)pktopt->ip6po_dest2; 17486b532452Sitojun optdatalen = (ip6e->ip6e_len + 1) << 3; 17496b532452Sitojun } 17506b532452Sitojun break; 17516b532452Sitojun case IPV6_USE_MIN_MTU: 17526b532452Sitojun if (pktopt) 17536b532452Sitojun optdata = (void *)&pktopt->ip6po_minmtu; 17546b532452Sitojun else 17556b532452Sitojun optdata = (void *)&defminmtu; 17566b532452Sitojun optdatalen = sizeof(int); 17576b532452Sitojun break; 17586b532452Sitojun case IPV6_DONTFRAG: 17596b532452Sitojun if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG)) 17606b532452Sitojun on = 1; 17616b532452Sitojun else 17626b532452Sitojun on = 0; 17636b532452Sitojun optdata = (void *)&on; 17646b532452Sitojun optdatalen = sizeof(on); 17656b532452Sitojun break; 17666b532452Sitojun default: /* should not happen */ 17676b532452Sitojun #ifdef DIAGNOSTIC 1768ab0ea4a9Snayden panic("%s: unexpected option", __func__); 17696b532452Sitojun #endif 17706b532452Sitojun return (ENOPROTOOPT); 17716b532452Sitojun } 17726b532452Sitojun 17736b532452Sitojun if (optdatalen > MCLBYTES) 17746b532452Sitojun return (EMSGSIZE); /* XXX */ 17756b532452Sitojun if (optdatalen > MLEN) 17766b532452Sitojun MCLGET(m, M_WAIT); 17776b532452Sitojun m->m_len = optdatalen; 17786b532452Sitojun if (optdatalen) 17796b532452Sitojun bcopy(optdata, mtod(m, void *), optdatalen); 17806b532452Sitojun 17816b532452Sitojun return (error); 17826b532452Sitojun } 17836b532452Sitojun 17846b532452Sitojun void 1785ee37ea65Smcbride ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname) 17866b532452Sitojun { 17876b532452Sitojun if (optname == -1 || optname == IPV6_PKTINFO) { 17886b532452Sitojun if (pktopt->ip6po_pktinfo) 1789dd168dc2Stedu free(pktopt->ip6po_pktinfo, M_IP6OPT, 0); 17906b532452Sitojun pktopt->ip6po_pktinfo = NULL; 17916b532452Sitojun } 17926b532452Sitojun if (optname == -1 || optname == IPV6_HOPLIMIT) 17936b532452Sitojun pktopt->ip6po_hlim = -1; 17946b532452Sitojun if (optname == -1 || optname == IPV6_TCLASS) 17956b532452Sitojun pktopt->ip6po_tclass = -1; 17966b532452Sitojun if (optname == -1 || optname == IPV6_HOPOPTS) { 17976b532452Sitojun if (pktopt->ip6po_hbh) 1798dd168dc2Stedu free(pktopt->ip6po_hbh, M_IP6OPT, 0); 17996b532452Sitojun pktopt->ip6po_hbh = NULL; 18006b532452Sitojun } 18016b532452Sitojun if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) { 18026b532452Sitojun if (pktopt->ip6po_dest1) 1803dd168dc2Stedu free(pktopt->ip6po_dest1, M_IP6OPT, 0); 18046b532452Sitojun pktopt->ip6po_dest1 = NULL; 18056b532452Sitojun } 18066b532452Sitojun if (optname == -1 || optname == IPV6_RTHDR) { 18076b532452Sitojun if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) 1808dd168dc2Stedu free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT, 0); 18096b532452Sitojun pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; 18106b532452Sitojun if (pktopt->ip6po_route.ro_rt) { 181127ae666cSmpi rtfree(pktopt->ip6po_route.ro_rt); 18126b532452Sitojun pktopt->ip6po_route.ro_rt = NULL; 18136b532452Sitojun } 18146b532452Sitojun } 18156b532452Sitojun if (optname == -1 || optname == IPV6_DSTOPTS) { 18166b532452Sitojun if (pktopt->ip6po_dest2) 1817dd168dc2Stedu free(pktopt->ip6po_dest2, M_IP6OPT, 0); 18186b532452Sitojun pktopt->ip6po_dest2 = NULL; 18196b532452Sitojun } 18206b532452Sitojun } 18216b532452Sitojun 18226b532452Sitojun #define PKTOPT_EXTHDRCPY(type) \ 18236b532452Sitojun do {\ 18246b532452Sitojun if (src->type) {\ 182590dc3aeaSbluhm size_t hlen;\ 182690dc3aeaSbluhm hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ 1827f288bf99Sdhill dst->type = malloc(hlen, M_IP6OPT, M_NOWAIT);\ 1828f288bf99Sdhill if (dst->type == NULL)\ 18296b532452Sitojun goto bad;\ 183041bfede9Stedu memcpy(dst->type, src->type, hlen);\ 18316b532452Sitojun }\ 18326b532452Sitojun } while (/*CONSTCOND*/ 0) 18336b532452Sitojun 1834d4984c21Sjsing int 1835f288bf99Sdhill copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src) 18366b532452Sitojun { 18376b532452Sitojun dst->ip6po_hlim = src->ip6po_hlim; 18386b532452Sitojun dst->ip6po_tclass = src->ip6po_tclass; 18396b532452Sitojun dst->ip6po_flags = src->ip6po_flags; 18406b532452Sitojun if (src->ip6po_pktinfo) { 18416b532452Sitojun dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), 1842f288bf99Sdhill M_IP6OPT, M_NOWAIT); 184312986e1eSchl if (dst->ip6po_pktinfo == NULL) 18446b532452Sitojun goto bad; 18456b532452Sitojun *dst->ip6po_pktinfo = *src->ip6po_pktinfo; 18466b532452Sitojun } 18476b532452Sitojun PKTOPT_EXTHDRCPY(ip6po_hbh); 18486b532452Sitojun PKTOPT_EXTHDRCPY(ip6po_dest1); 18496b532452Sitojun PKTOPT_EXTHDRCPY(ip6po_dest2); 18506b532452Sitojun PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */ 18516b532452Sitojun return (0); 18526b532452Sitojun 18536b532452Sitojun bad: 1854951dd902Scanacar ip6_clearpktopts(dst, -1); 18556b532452Sitojun return (ENOBUFS); 18566b532452Sitojun } 18576b532452Sitojun #undef PKTOPT_EXTHDRCPY 18586b532452Sitojun 18596b532452Sitojun void 1860ee37ea65Smcbride ip6_freepcbopts(struct ip6_pktopts *pktopt) 18616b532452Sitojun { 18626b532452Sitojun if (pktopt == NULL) 18636b532452Sitojun return; 18646b532452Sitojun 18656b532452Sitojun ip6_clearpktopts(pktopt, -1); 18666b532452Sitojun 1867dd168dc2Stedu free(pktopt, M_IP6OPT, 0); 18686b532452Sitojun } 18696b532452Sitojun 18706b532452Sitojun /* 1871287546eaSitojun * Set the IP6 multicast options in response to user setsockopt(). 1872287546eaSitojun */ 1873d4984c21Sjsing int 187444a616dfSrzalamena ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m, 187544a616dfSrzalamena unsigned int rtableid) 1876287546eaSitojun { 1877287546eaSitojun int error = 0; 1878287546eaSitojun u_int loop, ifindex; 1879287546eaSitojun struct ipv6_mreq *mreq; 1880287546eaSitojun struct ifnet *ifp; 1881287546eaSitojun struct ip6_moptions *im6o = *im6op; 1882287546eaSitojun struct in6_multi_mship *imm; 1883287546eaSitojun struct proc *p = curproc; /* XXX */ 1884287546eaSitojun 1885287546eaSitojun if (im6o == NULL) { 1886287546eaSitojun /* 1887287546eaSitojun * No multicast option buffer attached to the pcb; 1888287546eaSitojun * allocate one and initialize to default values. 1889287546eaSitojun */ 1890b82d192bSmpi im6o = malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK); 18917b2ccaf7Sitojun if (im6o == NULL) 18927b2ccaf7Sitojun return (ENOBUFS); 1893287546eaSitojun *im6op = im6o; 1894ec0be06bSmpi im6o->im6o_ifidx = 0; 18953c687758Smpi im6o->im6o_hlim = ip6_defmcasthlim; 18963c687758Smpi im6o->im6o_loop = IPV6_DEFAULT_MULTICAST_LOOP; 1897287546eaSitojun LIST_INIT(&im6o->im6o_memberships); 1898287546eaSitojun } 1899287546eaSitojun 1900287546eaSitojun switch (optname) { 1901287546eaSitojun 1902287546eaSitojun case IPV6_MULTICAST_IF: 1903287546eaSitojun /* 1904287546eaSitojun * Select the interface for outgoing multicast packets. 1905287546eaSitojun */ 1906287546eaSitojun if (m == NULL || m->m_len != sizeof(u_int)) { 1907287546eaSitojun error = EINVAL; 1908287546eaSitojun break; 1909287546eaSitojun } 191041bfede9Stedu memcpy(&ifindex, mtod(m, u_int *), sizeof(ifindex)); 19110e3d2ab2Sclaudio if (ifindex != 0) { 191273e65003Smpi ifp = if_get(ifindex); 191373e65003Smpi if (ifp == NULL) { 1914287546eaSitojun error = ENXIO; /* XXX EINVAL? */ 1915287546eaSitojun break; 1916287546eaSitojun } 19171d88f30eSclaudio if (ifp->if_rdomain != rtable_l2(rtableid) || 19181d88f30eSclaudio (ifp->if_flags & IFF_MULTICAST) == 0) { 1919287546eaSitojun error = EADDRNOTAVAIL; 19200e3d2ab2Sclaudio if_put(ifp); 1921287546eaSitojun break; 1922287546eaSitojun } 19230e3d2ab2Sclaudio if_put(ifp); 1924a7baa13eSdjm } 1925ec0be06bSmpi im6o->im6o_ifidx = ifindex; 1926287546eaSitojun break; 1927287546eaSitojun 1928287546eaSitojun case IPV6_MULTICAST_HOPS: 1929287546eaSitojun { 1930287546eaSitojun /* 1931287546eaSitojun * Set the IP6 hoplimit for outgoing multicast packets. 1932287546eaSitojun */ 1933287546eaSitojun int optval; 1934287546eaSitojun if (m == NULL || m->m_len != sizeof(int)) { 1935287546eaSitojun error = EINVAL; 1936287546eaSitojun break; 1937287546eaSitojun } 193841bfede9Stedu memcpy(&optval, mtod(m, u_int *), sizeof(optval)); 1939287546eaSitojun if (optval < -1 || optval >= 256) 1940287546eaSitojun error = EINVAL; 1941287546eaSitojun else if (optval == -1) 19423c687758Smpi im6o->im6o_hlim = ip6_defmcasthlim; 1943287546eaSitojun else 19443c687758Smpi im6o->im6o_hlim = optval; 1945287546eaSitojun break; 1946287546eaSitojun } 1947287546eaSitojun 1948287546eaSitojun case IPV6_MULTICAST_LOOP: 1949287546eaSitojun /* 1950287546eaSitojun * Set the loopback flag for outgoing multicast packets. 1951287546eaSitojun * Must be zero or one. 1952287546eaSitojun */ 19537b2ccaf7Sitojun if (m == NULL || m->m_len != sizeof(u_int)) { 19547b2ccaf7Sitojun error = EINVAL; 19557b2ccaf7Sitojun break; 19567b2ccaf7Sitojun } 195741bfede9Stedu memcpy(&loop, mtod(m, u_int *), sizeof(loop)); 19587b2ccaf7Sitojun if (loop > 1) { 1959287546eaSitojun error = EINVAL; 1960287546eaSitojun break; 1961287546eaSitojun } 19623c687758Smpi im6o->im6o_loop = loop; 1963287546eaSitojun break; 1964287546eaSitojun 1965287546eaSitojun case IPV6_JOIN_GROUP: 1966287546eaSitojun /* 1967287546eaSitojun * Add a multicast group membership. 1968287546eaSitojun * Group must be a valid IP6 multicast address. 1969287546eaSitojun */ 1970287546eaSitojun if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { 1971287546eaSitojun error = EINVAL; 1972287546eaSitojun break; 1973287546eaSitojun } 1974287546eaSitojun mreq = mtod(m, struct ipv6_mreq *); 1975287546eaSitojun if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { 1976287546eaSitojun /* 1977287546eaSitojun * We use the unspecified address to specify to accept 1978287546eaSitojun * all multicast addresses. Only super user is allowed 1979287546eaSitojun * to do this. 1980287546eaSitojun */ 19813e676399Smpi if (suser(p)) 198234deef1eSitojun { 1983287546eaSitojun error = EACCES; 1984287546eaSitojun break; 1985287546eaSitojun } 1986287546eaSitojun } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { 1987287546eaSitojun error = EINVAL; 1988287546eaSitojun break; 1989287546eaSitojun } 1990287546eaSitojun 1991287546eaSitojun /* 1992287546eaSitojun * If no interface was explicitly specified, choose an 1993287546eaSitojun * appropriate one according to the given multicast address. 1994287546eaSitojun */ 1995287546eaSitojun if (mreq->ipv6mr_interface == 0) { 1996b324c4e9Smpi struct rtentry *rt; 1997b324c4e9Smpi struct sockaddr_in6 dst; 1998b324c4e9Smpi 1999b324c4e9Smpi memset(&dst, 0, sizeof(dst)); 2000b324c4e9Smpi dst.sin6_len = sizeof(dst); 2001b324c4e9Smpi dst.sin6_family = AF_INET6; 2002b324c4e9Smpi dst.sin6_addr = mreq->ipv6mr_multiaddr; 200344a616dfSrzalamena rt = rtalloc(sin6tosa(&dst), RT_RESOLVE, rtableid); 2004b324c4e9Smpi if (rt == NULL) { 2005287546eaSitojun error = EADDRNOTAVAIL; 2006287546eaSitojun break; 2007287546eaSitojun } 2008b324c4e9Smpi ifp = if_get(rt->rt_ifidx); 2009b324c4e9Smpi rtfree(rt); 2010a7baa13eSdjm } else { 2011a7baa13eSdjm /* 2012a7baa13eSdjm * If the interface is specified, validate it. 2013a7baa13eSdjm */ 201473e65003Smpi ifp = if_get(mreq->ipv6mr_interface); 201573e65003Smpi if (ifp == NULL) { 2016a7baa13eSdjm error = ENXIO; /* XXX EINVAL? */ 2017a7baa13eSdjm break; 2018a7baa13eSdjm } 2019a7baa13eSdjm } 2020287546eaSitojun 2021287546eaSitojun /* 2022287546eaSitojun * See if we found an interface, and confirm that it 2023287546eaSitojun * supports multicast 2024287546eaSitojun */ 20251d88f30eSclaudio if (ifp == NULL || ifp->if_rdomain != rtable_l2(rtableid) || 20261d88f30eSclaudio (ifp->if_flags & IFF_MULTICAST) == 0) { 20270e3d2ab2Sclaudio if_put(ifp); 2028287546eaSitojun error = EADDRNOTAVAIL; 2029287546eaSitojun break; 2030287546eaSitojun } 2031287546eaSitojun /* 2032287546eaSitojun * Put interface index into the multicast address, 203355ee7a9bSitojun * if the address has link/interface-local scope. 2034287546eaSitojun */ 203555ee7a9bSitojun if (IN6_IS_SCOPE_EMBED(&mreq->ipv6mr_multiaddr)) { 20367b2ccaf7Sitojun mreq->ipv6mr_multiaddr.s6_addr16[1] = 20378b94f13dSitojun htons(ifp->if_index); 2038287546eaSitojun } 2039287546eaSitojun /* 2040287546eaSitojun * See if the membership already exists. 2041287546eaSitojun */ 20421573508eSmiod LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) 204333082e9fSmpi if (imm->i6mm_maddr->in6m_ifidx == ifp->if_index && 2044287546eaSitojun IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, 2045287546eaSitojun &mreq->ipv6mr_multiaddr)) 2046287546eaSitojun break; 2047287546eaSitojun if (imm != NULL) { 20480e3d2ab2Sclaudio if_put(ifp); 2049287546eaSitojun error = EADDRINUSE; 2050287546eaSitojun break; 2051287546eaSitojun } 2052287546eaSitojun /* 2053287546eaSitojun * Everything looks good; add a new record to the multicast 2054287546eaSitojun * address list for the given interface. 2055287546eaSitojun */ 20567b2ccaf7Sitojun imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error); 20570e3d2ab2Sclaudio if_put(ifp); 20587b2ccaf7Sitojun if (!imm) 2059287546eaSitojun break; 2060287546eaSitojun LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 2061287546eaSitojun break; 2062287546eaSitojun 2063287546eaSitojun case IPV6_LEAVE_GROUP: 2064287546eaSitojun /* 2065287546eaSitojun * Drop a multicast group membership. 2066287546eaSitojun * Group must be a valid IP6 multicast address. 2067287546eaSitojun */ 2068287546eaSitojun if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { 2069287546eaSitojun error = EINVAL; 2070287546eaSitojun break; 2071287546eaSitojun } 2072287546eaSitojun mreq = mtod(m, struct ipv6_mreq *); 2073287546eaSitojun if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { 207470646e3dSclaudio if (suser(p)) { 2075287546eaSitojun error = EACCES; 2076287546eaSitojun break; 2077287546eaSitojun } 2078287546eaSitojun } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { 2079287546eaSitojun error = EINVAL; 2080287546eaSitojun break; 2081287546eaSitojun } 20820e3d2ab2Sclaudio 20830e3d2ab2Sclaudio /* 20840e3d2ab2Sclaudio * Put interface index into the multicast address, 20850e3d2ab2Sclaudio * if the address has link-local scope. 20860e3d2ab2Sclaudio */ 20870e3d2ab2Sclaudio if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { 20880e3d2ab2Sclaudio mreq->ipv6mr_multiaddr.s6_addr16[1] = 20890e3d2ab2Sclaudio htons(mreq->ipv6mr_interface); 20900e3d2ab2Sclaudio } 20910e3d2ab2Sclaudio 2092287546eaSitojun /* 2093287546eaSitojun * If an interface address was specified, get a pointer 2094287546eaSitojun * to its ifnet structure. 2095287546eaSitojun */ 2096a7baa13eSdjm if (mreq->ipv6mr_interface == 0) 2097a7baa13eSdjm ifp = NULL; 2098a7baa13eSdjm else { 209973e65003Smpi ifp = if_get(mreq->ipv6mr_interface); 210073e65003Smpi if (ifp == NULL) { 2101287546eaSitojun error = ENXIO; /* XXX EINVAL? */ 2102287546eaSitojun break; 2103287546eaSitojun } 2104a7baa13eSdjm } 2105a7baa13eSdjm 2106287546eaSitojun /* 2107287546eaSitojun * Find the membership in the membership list. 2108287546eaSitojun */ 21091573508eSmiod LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) { 211033082e9fSmpi if ((ifp == NULL || 211133082e9fSmpi imm->i6mm_maddr->in6m_ifidx == ifp->if_index) && 2112287546eaSitojun IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, 2113287546eaSitojun &mreq->ipv6mr_multiaddr)) 2114287546eaSitojun break; 2115287546eaSitojun } 21160e3d2ab2Sclaudio 21170e3d2ab2Sclaudio if_put(ifp); 21180e3d2ab2Sclaudio 2119287546eaSitojun if (imm == NULL) { 2120287546eaSitojun /* Unable to resolve interface */ 2121287546eaSitojun error = EADDRNOTAVAIL; 2122287546eaSitojun break; 2123287546eaSitojun } 2124287546eaSitojun /* 2125287546eaSitojun * Give up the multicast address record to which the 2126287546eaSitojun * membership points. 2127287546eaSitojun */ 2128287546eaSitojun LIST_REMOVE(imm, i6mm_chain); 21297b2ccaf7Sitojun in6_leavegroup(imm); 2130287546eaSitojun break; 2131287546eaSitojun 2132287546eaSitojun default: 2133287546eaSitojun error = EOPNOTSUPP; 2134287546eaSitojun break; 2135287546eaSitojun } 2136287546eaSitojun 2137287546eaSitojun /* 213855ee7a9bSitojun * If all options have default values, no need to keep the option 213955ee7a9bSitojun * structure. 2140287546eaSitojun */ 2141ec0be06bSmpi if (im6o->im6o_ifidx == 0 && 21423c687758Smpi im6o->im6o_hlim == ip6_defmcasthlim && 21433c687758Smpi im6o->im6o_loop == IPV6_DEFAULT_MULTICAST_LOOP && 21441573508eSmiod LIST_EMPTY(&im6o->im6o_memberships)) { 2145b82d192bSmpi free(*im6op, M_IPMOPTS, sizeof(**im6op)); 2146287546eaSitojun *im6op = NULL; 2147287546eaSitojun } 2148287546eaSitojun 2149287546eaSitojun return (error); 2150287546eaSitojun } 2151287546eaSitojun 2152287546eaSitojun /* 2153287546eaSitojun * Return the IP6 multicast options in response to user getsockopt(). 2154287546eaSitojun */ 2155d4984c21Sjsing int 2156490e0738Sdhill ip6_getmoptions(int optname, struct ip6_moptions *im6o, struct mbuf *m) 2157287546eaSitojun { 2158287546eaSitojun u_int *hlim, *loop, *ifindex; 2159287546eaSitojun 2160287546eaSitojun switch (optname) { 2161287546eaSitojun case IPV6_MULTICAST_IF: 2162490e0738Sdhill ifindex = mtod(m, u_int *); 2163490e0738Sdhill m->m_len = sizeof(u_int); 2164ec0be06bSmpi if (im6o == NULL || im6o->im6o_ifidx == 0) 2165287546eaSitojun *ifindex = 0; 2166287546eaSitojun else 2167ec0be06bSmpi *ifindex = im6o->im6o_ifidx; 2168287546eaSitojun return (0); 2169287546eaSitojun 2170287546eaSitojun case IPV6_MULTICAST_HOPS: 2171490e0738Sdhill hlim = mtod(m, u_int *); 2172490e0738Sdhill m->m_len = sizeof(u_int); 2173287546eaSitojun if (im6o == NULL) 2174287546eaSitojun *hlim = ip6_defmcasthlim; 2175287546eaSitojun else 21763c687758Smpi *hlim = im6o->im6o_hlim; 2177287546eaSitojun return (0); 2178287546eaSitojun 2179287546eaSitojun case IPV6_MULTICAST_LOOP: 2180490e0738Sdhill loop = mtod(m, u_int *); 2181490e0738Sdhill m->m_len = sizeof(u_int); 2182287546eaSitojun if (im6o == NULL) 2183287546eaSitojun *loop = ip6_defmcasthlim; 2184287546eaSitojun else 21853c687758Smpi *loop = im6o->im6o_loop; 2186287546eaSitojun return (0); 2187287546eaSitojun 2188287546eaSitojun default: 2189287546eaSitojun return (EOPNOTSUPP); 2190287546eaSitojun } 2191287546eaSitojun } 2192287546eaSitojun 2193287546eaSitojun /* 2194287546eaSitojun * Discard the IP6 multicast options. 2195287546eaSitojun */ 2196287546eaSitojun void 2197ee37ea65Smcbride ip6_freemoptions(struct ip6_moptions *im6o) 2198287546eaSitojun { 2199287546eaSitojun struct in6_multi_mship *imm; 2200287546eaSitojun 2201287546eaSitojun if (im6o == NULL) 2202287546eaSitojun return; 2203287546eaSitojun 22041573508eSmiod while (!LIST_EMPTY(&im6o->im6o_memberships)) { 22051573508eSmiod imm = LIST_FIRST(&im6o->im6o_memberships); 2206287546eaSitojun LIST_REMOVE(imm, i6mm_chain); 22077b2ccaf7Sitojun in6_leavegroup(imm); 2208287546eaSitojun } 2209b82d192bSmpi free(im6o, M_IPMOPTS, sizeof(*im6o)); 2210287546eaSitojun } 2211287546eaSitojun 2212287546eaSitojun /* 2213287546eaSitojun * Set IPv6 outgoing packet options based on advanced API. 2214287546eaSitojun */ 2215287546eaSitojun int 2216ee37ea65Smcbride ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt, 2217ee37ea65Smcbride struct ip6_pktopts *stickyopt, int priv, int uproto) 2218287546eaSitojun { 2219021732e9Sderaadt u_int clen; 22209063e41fSitojun struct cmsghdr *cm = 0; 2221021732e9Sderaadt caddr_t cmsgs; 2222021732e9Sderaadt int error; 2223287546eaSitojun 22246b532452Sitojun if (control == NULL || opt == NULL) 2225287546eaSitojun return (EINVAL); 2226287546eaSitojun 22276b532452Sitojun ip6_initpktopts(opt); 22286b532452Sitojun if (stickyopt) { 22296b532452Sitojun int error; 22306b532452Sitojun 22316b532452Sitojun /* 22326b532452Sitojun * If stickyopt is provided, make a local copy of the options 22336b532452Sitojun * for this particular packet, then override them by ancillary 22346b532452Sitojun * objects. 22356b532452Sitojun * XXX: copypktopts() does not copy the cached route to a next 22366b532452Sitojun * hop (if any). This is not very good in terms of efficiency, 22376b532452Sitojun * but we can allow this since this option should be rarely 22386b532452Sitojun * used. 22396b532452Sitojun */ 2240f288bf99Sdhill if ((error = copypktopts(opt, stickyopt)) != 0) 22416b532452Sitojun return (error); 22426b532452Sitojun } 2243287546eaSitojun 2244287546eaSitojun /* 2245287546eaSitojun * XXX: Currently, we assume all the optional information is stored 2246287546eaSitojun * in a single mbuf. 2247287546eaSitojun */ 2248287546eaSitojun if (control->m_next) 2249287546eaSitojun return (EINVAL); 2250287546eaSitojun 2251021732e9Sderaadt clen = control->m_len; 2252021732e9Sderaadt cmsgs = mtod(control, caddr_t); 2253021732e9Sderaadt do { 2254021732e9Sderaadt if (clen < CMSG_LEN(0)) 22556b532452Sitojun return (EINVAL); 2256021732e9Sderaadt cm = (struct cmsghdr *)cmsgs; 2257bb110d83Sbluhm if (cm->cmsg_len < CMSG_LEN(0) || cm->cmsg_len > clen || 2258021732e9Sderaadt CMSG_ALIGN(cm->cmsg_len) > clen) 2259287546eaSitojun return (EINVAL); 2260021732e9Sderaadt if (cm->cmsg_level == IPPROTO_IPV6) { 22616b532452Sitojun error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm), 22623ea7534eSjca cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, uproto); 22636b532452Sitojun if (error) 22646b532452Sitojun return (error); 22656b532452Sitojun } 22666b532452Sitojun 2267021732e9Sderaadt clen -= CMSG_ALIGN(cm->cmsg_len); 2268021732e9Sderaadt cmsgs += CMSG_ALIGN(cm->cmsg_len); 2269021732e9Sderaadt } while (clen); 2270021732e9Sderaadt 22716b532452Sitojun return (0); 22726b532452Sitojun } 22736b532452Sitojun 22746b532452Sitojun /* 22756b532452Sitojun * Set a particular packet option, as a sticky option or an ancillary data 22766b532452Sitojun * item. "len" can be 0 only when it's a sticky option. 22776b532452Sitojun */ 2278d4984c21Sjsing int 2279ee37ea65Smcbride ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, 22803ea7534eSjca int priv, int sticky, int uproto) 22816b532452Sitojun { 22826b532452Sitojun int minmtupolicy; 22836b532452Sitojun 22846b532452Sitojun switch (optname) { 2285905e951aStedu case IPV6_PKTINFO: 2286905e951aStedu { 2287905e951aStedu struct ifnet *ifp = NULL; 2288905e951aStedu struct in6_pktinfo *pktinfo; 2289905e951aStedu 2290905e951aStedu if (len != sizeof(struct in6_pktinfo)) 2291905e951aStedu return (EINVAL); 2292905e951aStedu 2293905e951aStedu pktinfo = (struct in6_pktinfo *)buf; 2294905e951aStedu 2295905e951aStedu /* 2296905e951aStedu * An application can clear any sticky IPV6_PKTINFO option by 2297905e951aStedu * doing a "regular" setsockopt with ipi6_addr being 2298905e951aStedu * in6addr_any and ipi6_ifindex being zero. 2299905e951aStedu * [RFC 3542, Section 6] 2300905e951aStedu */ 2301245e399cStedu if (opt->ip6po_pktinfo && 2302905e951aStedu pktinfo->ipi6_ifindex == 0 && 2303905e951aStedu IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { 2304905e951aStedu ip6_clearpktopts(opt, optname); 2305905e951aStedu break; 2306905e951aStedu } 2307905e951aStedu 2308245e399cStedu if (uproto == IPPROTO_TCP && 2309905e951aStedu sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { 2310905e951aStedu return (EINVAL); 2311905e951aStedu } 2312905e951aStedu 2313905e951aStedu if (pktinfo->ipi6_ifindex) { 2314905e951aStedu ifp = if_get(pktinfo->ipi6_ifindex); 2315905e951aStedu if (ifp == NULL) 2316905e951aStedu return (ENXIO); 2317905e951aStedu if_put(ifp); 2318905e951aStedu } 2319905e951aStedu 2320905e951aStedu /* 2321905e951aStedu * We store the address anyway, and let in6_selectsrc() 2322905e951aStedu * validate the specified address. This is because ipi6_addr 2323905e951aStedu * may not have enough information about its scope zone, and 2324905e951aStedu * we may need additional information (such as outgoing 2325905e951aStedu * interface or the scope zone of a destination address) to 2326905e951aStedu * disambiguate the scope. 2327905e951aStedu * XXX: the delay of the validation may confuse the 2328905e951aStedu * application when it is used as a sticky option. 2329905e951aStedu */ 2330905e951aStedu if (opt->ip6po_pktinfo == NULL) { 2331905e951aStedu opt->ip6po_pktinfo = malloc(sizeof(*pktinfo), 2332905e951aStedu M_IP6OPT, M_NOWAIT); 2333905e951aStedu if (opt->ip6po_pktinfo == NULL) 2334905e951aStedu return (ENOBUFS); 2335905e951aStedu } 2336905e951aStedu bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo)); 2337905e951aStedu break; 2338905e951aStedu } 2339905e951aStedu 23406b532452Sitojun case IPV6_HOPLIMIT: 23416b532452Sitojun { 23426b532452Sitojun int *hlimp; 23436b532452Sitojun 23446b532452Sitojun /* 23456b532452Sitojun * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT 23466b532452Sitojun * to simplify the ordering among hoplimit options. 23476b532452Sitojun */ 2348245e399cStedu if (sticky) 23496b532452Sitojun return (ENOPROTOOPT); 23506b532452Sitojun 23516b532452Sitojun if (len != sizeof(int)) 23526b532452Sitojun return (EINVAL); 23536b532452Sitojun hlimp = (int *)buf; 23546b532452Sitojun if (*hlimp < -1 || *hlimp > 255) 23556b532452Sitojun return (EINVAL); 23566b532452Sitojun 23576b532452Sitojun opt->ip6po_hlim = *hlimp; 23586b532452Sitojun break; 23596b532452Sitojun } 23606b532452Sitojun 23616b532452Sitojun case IPV6_TCLASS: 23626b532452Sitojun { 23636b532452Sitojun int tclass; 23646b532452Sitojun 23656b532452Sitojun if (len != sizeof(int)) 23666b532452Sitojun return (EINVAL); 23676b532452Sitojun tclass = *(int *)buf; 23686b532452Sitojun if (tclass < -1 || tclass > 255) 23696b532452Sitojun return (EINVAL); 23706b532452Sitojun 23716b532452Sitojun opt->ip6po_tclass = tclass; 23726b532452Sitojun break; 23736b532452Sitojun } 2374287546eaSitojun case IPV6_HOPOPTS: 23756b532452Sitojun { 23766b532452Sitojun struct ip6_hbh *hbh; 23776b532452Sitojun int hbhlen; 2378287546eaSitojun 2379287546eaSitojun /* 23806b532452Sitojun * XXX: We don't allow a non-privileged user to set ANY HbH 23816b532452Sitojun * options, since per-option restriction has too much 23826b532452Sitojun * overhead. 2383287546eaSitojun */ 23846b532452Sitojun if (!priv) 23856b532452Sitojun return (EPERM); 2386287546eaSitojun 23876b532452Sitojun if (len == 0) { 23886b532452Sitojun ip6_clearpktopts(opt, IPV6_HOPOPTS); 23896b532452Sitojun break; /* just remove the option */ 23906b532452Sitojun } 23916b532452Sitojun 23926b532452Sitojun /* message length validation */ 23936b532452Sitojun if (len < sizeof(struct ip6_hbh)) 23946b532452Sitojun return (EINVAL); 23956b532452Sitojun hbh = (struct ip6_hbh *)buf; 23966b532452Sitojun hbhlen = (hbh->ip6h_len + 1) << 3; 23976b532452Sitojun if (len != hbhlen) 23986b532452Sitojun return (EINVAL); 23996b532452Sitojun 24006b532452Sitojun /* turn off the previous option, then set the new option. */ 24016b532452Sitojun ip6_clearpktopts(opt, IPV6_HOPOPTS); 24026b532452Sitojun opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT); 24036b532452Sitojun if (opt->ip6po_hbh == NULL) 24046b532452Sitojun return (ENOBUFS); 240541bfede9Stedu memcpy(opt->ip6po_hbh, hbh, hbhlen); 24066b532452Sitojun 24076b532452Sitojun break; 24086b532452Sitojun } 24096b532452Sitojun 24106b532452Sitojun case IPV6_DSTOPTS: 24116b532452Sitojun case IPV6_RTHDRDSTOPTS: 24126b532452Sitojun { 24136b532452Sitojun struct ip6_dest *dest, **newdest = NULL; 24146b532452Sitojun int destlen; 24156b532452Sitojun 24166b532452Sitojun if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */ 24176b532452Sitojun return (EPERM); 24186b532452Sitojun 24196b532452Sitojun if (len == 0) { 24206b532452Sitojun ip6_clearpktopts(opt, optname); 24216b532452Sitojun break; /* just remove the option */ 24226b532452Sitojun } 24236b532452Sitojun 24246b532452Sitojun /* message length validation */ 24256b532452Sitojun if (len < sizeof(struct ip6_dest)) 24266b532452Sitojun return (EINVAL); 24276b532452Sitojun dest = (struct ip6_dest *)buf; 24286b532452Sitojun destlen = (dest->ip6d_len + 1) << 3; 24296b532452Sitojun if (len != destlen) 24306b532452Sitojun return (EINVAL); 24316b532452Sitojun /* 24326b532452Sitojun * Determine the position that the destination options header 24336b532452Sitojun * should be inserted; before or after the routing header. 24346b532452Sitojun */ 24356b532452Sitojun switch (optname) { 24366b532452Sitojun case IPV6_RTHDRDSTOPTS: 24376b532452Sitojun newdest = &opt->ip6po_dest1; 24386b532452Sitojun break; 24396b532452Sitojun case IPV6_DSTOPTS: 24406b532452Sitojun newdest = &opt->ip6po_dest2; 24416b532452Sitojun break; 24426b532452Sitojun } 24436b532452Sitojun 24446b532452Sitojun /* turn off the previous option, then set the new option. */ 24456b532452Sitojun ip6_clearpktopts(opt, optname); 24466b532452Sitojun *newdest = malloc(destlen, M_IP6OPT, M_NOWAIT); 24476b532452Sitojun if (*newdest == NULL) 24486b532452Sitojun return (ENOBUFS); 244941bfede9Stedu memcpy(*newdest, dest, destlen); 24506b532452Sitojun 24516b532452Sitojun break; 24526b532452Sitojun } 24536b532452Sitojun 2454287546eaSitojun case IPV6_RTHDR: 24556b532452Sitojun { 24566b532452Sitojun struct ip6_rthdr *rth; 24576b532452Sitojun int rthlen; 24586b532452Sitojun 24596b532452Sitojun if (len == 0) { 24606b532452Sitojun ip6_clearpktopts(opt, IPV6_RTHDR); 24616b532452Sitojun break; /* just remove the option */ 24626b532452Sitojun } 24636b532452Sitojun 24646b532452Sitojun /* message length validation */ 24656b532452Sitojun if (len < sizeof(struct ip6_rthdr)) 2466287546eaSitojun return (EINVAL); 24676b532452Sitojun rth = (struct ip6_rthdr *)buf; 24686b532452Sitojun rthlen = (rth->ip6r_len + 1) << 3; 24696b532452Sitojun if (len != rthlen) 2470287546eaSitojun return (EINVAL); 24716b532452Sitojun 24726b532452Sitojun switch (rth->ip6r_type) { 2473287546eaSitojun case IPV6_RTHDR_TYPE_0: 24746b532452Sitojun if (rth->ip6r_len == 0) /* must contain one addr */ 24756b532452Sitojun return (EINVAL); 24766b532452Sitojun if (rth->ip6r_len % 2) /* length must be even */ 24776b532452Sitojun return (EINVAL); 24786b532452Sitojun if (rth->ip6r_len / 2 != rth->ip6r_segleft) 2479287546eaSitojun return (EINVAL); 2480287546eaSitojun break; 2481287546eaSitojun default: 24826b532452Sitojun return (EINVAL); /* not supported */ 24836b532452Sitojun } 24846b532452Sitojun /* turn off the previous option */ 24856b532452Sitojun ip6_clearpktopts(opt, IPV6_RTHDR); 24866b532452Sitojun opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT); 24876b532452Sitojun if (opt->ip6po_rthdr == NULL) 24886b532452Sitojun return (ENOBUFS); 248941bfede9Stedu memcpy(opt->ip6po_rthdr, rth, rthlen); 24906b532452Sitojun break; 24916b532452Sitojun } 24926b532452Sitojun 24936b532452Sitojun case IPV6_USE_MIN_MTU: 24946b532452Sitojun if (len != sizeof(int)) 24956b532452Sitojun return (EINVAL); 24966b532452Sitojun minmtupolicy = *(int *)buf; 24976b532452Sitojun if (minmtupolicy != IP6PO_MINMTU_MCASTONLY && 24986b532452Sitojun minmtupolicy != IP6PO_MINMTU_DISABLE && 24996b532452Sitojun minmtupolicy != IP6PO_MINMTU_ALL) { 2500287546eaSitojun return (EINVAL); 2501287546eaSitojun } 25026b532452Sitojun opt->ip6po_minmtu = minmtupolicy; 25036b532452Sitojun break; 25046b532452Sitojun 25056b532452Sitojun case IPV6_DONTFRAG: 25066b532452Sitojun if (len != sizeof(int)) 25076b532452Sitojun return (EINVAL); 25086b532452Sitojun 25096b532452Sitojun if (uproto == IPPROTO_TCP || *(int *)buf == 0) { 25106b532452Sitojun /* 25116b532452Sitojun * we ignore this option for TCP sockets. 25126b532452Sitojun * (RFC3542 leaves this case unspecified.) 25136b532452Sitojun */ 25146b532452Sitojun opt->ip6po_flags &= ~IP6PO_DONTFRAG; 25156b532452Sitojun } else 25166b532452Sitojun opt->ip6po_flags |= IP6PO_DONTFRAG; 2517287546eaSitojun break; 2518287546eaSitojun 2519287546eaSitojun default: 2520287546eaSitojun return (ENOPROTOOPT); 25216b532452Sitojun } /* end of switch */ 2522287546eaSitojun 2523287546eaSitojun return (0); 2524287546eaSitojun } 2525287546eaSitojun 2526287546eaSitojun /* 2527287546eaSitojun * Routine called from ip6_output() to loop back a copy of an IP6 multicast 252872064afcSmpi * packet to the input queue of a specified interface. 2529287546eaSitojun */ 2530287546eaSitojun void 2531ee37ea65Smcbride ip6_mloopback(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst) 2532287546eaSitojun { 2533287546eaSitojun struct mbuf *copym; 25342ed872ebSitojun struct ip6_hdr *ip6; 2535287546eaSitojun 253655ee7a9bSitojun /* 253755ee7a9bSitojun * Duplicate the packet. 253855ee7a9bSitojun */ 25398990c937Smpi copym = m_copym(m, 0, M_COPYALL, M_NOWAIT); 25402ed872ebSitojun if (copym == NULL) 25412ed872ebSitojun return; 25422ed872ebSitojun 25432ed872ebSitojun /* 25442ed872ebSitojun * Make sure to deep-copy IPv6 header portion in case the data 25452ed872ebSitojun * is in an mbuf cluster, so that we can safely override the IPv6 25462ed872ebSitojun * header portion later. 25472ed872ebSitojun */ 25482ed872ebSitojun if ((copym->m_flags & M_EXT) != 0 || 25492ed872ebSitojun copym->m_len < sizeof(struct ip6_hdr)) { 25502ed872ebSitojun copym = m_pullup(copym, sizeof(struct ip6_hdr)); 25512ed872ebSitojun if (copym == NULL) 25522ed872ebSitojun return; 2553287546eaSitojun } 25542ed872ebSitojun 25552ed872ebSitojun #ifdef DIAGNOSTIC 25562ed872ebSitojun if (copym->m_len < sizeof(*ip6)) { 25572ed872ebSitojun m_freem(copym); 25582ed872ebSitojun return; 25592ed872ebSitojun } 25602ed872ebSitojun #endif 25612ed872ebSitojun 25622ed872ebSitojun ip6 = mtod(copym, struct ip6_hdr *); 2563d4070096Sitojun if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) 25642ed872ebSitojun ip6->ip6_src.s6_addr16[1] = 0; 2565d4070096Sitojun if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) 25662ed872ebSitojun ip6->ip6_dst.s6_addr16[1] = 0; 25672ed872ebSitojun 25687f3eeafaSmpi if_input_local(ifp, copym, dst->sin6_family); 2569287546eaSitojun } 2570287546eaSitojun 2571287546eaSitojun /* 2572287546eaSitojun * Chop IPv6 header off from the payload. 2573287546eaSitojun */ 2574d4984c21Sjsing int 2575ee37ea65Smcbride ip6_splithdr(struct mbuf *m, struct ip6_exthdrs *exthdrs) 2576287546eaSitojun { 2577287546eaSitojun struct mbuf *mh; 2578287546eaSitojun struct ip6_hdr *ip6; 2579287546eaSitojun 2580287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 2581287546eaSitojun if (m->m_len > sizeof(*ip6)) { 25820e1f74ccSclaudio MGET(mh, M_DONTWAIT, MT_HEADER); 258364a3f76cSjsg if (mh == NULL) { 2584287546eaSitojun m_freem(m); 2585287546eaSitojun return ENOBUFS; 2586287546eaSitojun } 2587406cff18Sangelos M_MOVE_PKTHDR(mh, m); 25880e1f74ccSclaudio m_align(mh, sizeof(*ip6)); 2589287546eaSitojun m->m_len -= sizeof(*ip6); 2590287546eaSitojun m->m_data += sizeof(*ip6); 2591287546eaSitojun mh->m_next = m; 2592287546eaSitojun m = mh; 2593287546eaSitojun m->m_len = sizeof(*ip6); 2594287546eaSitojun bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6)); 2595287546eaSitojun } 2596287546eaSitojun exthdrs->ip6e_ip6 = m; 2597287546eaSitojun return 0; 2598287546eaSitojun } 2599287546eaSitojun 2600ca6e56f2Sdjm u_int32_t 2601ca6e56f2Sdjm ip6_randomid(void) 2602ca6e56f2Sdjm { 2603ca6e56f2Sdjm return idgen32(&ip6_id_ctx); 2604ca6e56f2Sdjm } 2605ca6e56f2Sdjm 2606ca6e56f2Sdjm void 2607ca6e56f2Sdjm ip6_randomid_init(void) 2608ca6e56f2Sdjm { 2609ca6e56f2Sdjm idgen32_init(&ip6_id_ctx); 2610ca6e56f2Sdjm } 2611e3f16dcaShenning 2612e3f16dcaShenning /* 2613a2c73f7dSnaddy * Compute significant parts of the IPv6 checksum pseudo-header 2614a2c73f7dSnaddy * for use in a delayed TCP/UDP checksum calculation. 2615a2c73f7dSnaddy */ 2616a2c73f7dSnaddy static __inline u_int16_t __attribute__((__unused__)) 2617a2c73f7dSnaddy in6_cksum_phdr(const struct in6_addr *src, const struct in6_addr *dst, 2618a2c73f7dSnaddy u_int32_t len, u_int32_t nxt) 2619a2c73f7dSnaddy { 2620a2c73f7dSnaddy u_int32_t sum = 0; 2621a2c73f7dSnaddy const u_int16_t *w; 2622a2c73f7dSnaddy 2623a2c73f7dSnaddy w = (const u_int16_t *) src; 2624a2c73f7dSnaddy sum += w[0]; 2625a2c73f7dSnaddy if (!IN6_IS_SCOPE_EMBED(src)) 2626a2c73f7dSnaddy sum += w[1]; 2627a2c73f7dSnaddy sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; 2628a2c73f7dSnaddy sum += w[6]; sum += w[7]; 2629a2c73f7dSnaddy 2630a2c73f7dSnaddy w = (const u_int16_t *) dst; 2631a2c73f7dSnaddy sum += w[0]; 2632a2c73f7dSnaddy if (!IN6_IS_SCOPE_EMBED(dst)) 2633a2c73f7dSnaddy sum += w[1]; 2634a2c73f7dSnaddy sum += w[2]; sum += w[3]; sum += w[4]; sum += w[5]; 2635a2c73f7dSnaddy sum += w[6]; sum += w[7]; 2636a2c73f7dSnaddy 2637a2c73f7dSnaddy sum += (u_int16_t)(len >> 16) + (u_int16_t)(len /*& 0xffff*/); 2638a2c73f7dSnaddy 2639a2c73f7dSnaddy sum += (u_int16_t)(nxt >> 16) + (u_int16_t)(nxt /*& 0xffff*/); 2640a2c73f7dSnaddy 2641a2c73f7dSnaddy sum = (u_int16_t)(sum >> 16) + (u_int16_t)(sum /*& 0xffff*/); 2642a2c73f7dSnaddy 2643a2c73f7dSnaddy if (sum > 0xffff) 2644a2c73f7dSnaddy sum -= 0xffff; 2645a2c73f7dSnaddy 2646a2c73f7dSnaddy return (sum); 2647a2c73f7dSnaddy } 2648a2c73f7dSnaddy 2649a2c73f7dSnaddy /* 2650e3f16dcaShenning * Process a delayed payload checksum calculation. 2651e3f16dcaShenning */ 2652e3f16dcaShenning void 2653e3f16dcaShenning in6_delayed_cksum(struct mbuf *m, u_int8_t nxt) 2654e3f16dcaShenning { 2655e3f16dcaShenning int nxtp, offset; 2656e3f16dcaShenning u_int16_t csum; 2657e3f16dcaShenning 2658e3f16dcaShenning offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxtp); 2659e3f16dcaShenning if (offset <= 0 || nxtp != nxt) 2660e3f16dcaShenning /* If the desired next protocol isn't found, punt. */ 2661e3f16dcaShenning return; 266224ce3f58Snaddy csum = (u_int16_t)(in6_cksum(m, 0, offset, m->m_pkthdr.len - offset)); 2663e3f16dcaShenning 2664e3f16dcaShenning switch (nxt) { 2665e3f16dcaShenning case IPPROTO_TCP: 2666e3f16dcaShenning offset += offsetof(struct tcphdr, th_sum); 2667e3f16dcaShenning break; 2668e3f16dcaShenning 2669e3f16dcaShenning case IPPROTO_UDP: 2670e3f16dcaShenning offset += offsetof(struct udphdr, uh_sum); 2671e3f16dcaShenning if (csum == 0) 2672e3f16dcaShenning csum = 0xffff; 2673e3f16dcaShenning break; 2674e3f16dcaShenning 2675e3f16dcaShenning case IPPROTO_ICMPV6: 2676e3f16dcaShenning offset += offsetof(struct icmp6_hdr, icmp6_cksum); 2677e3f16dcaShenning break; 2678e3f16dcaShenning } 2679e3f16dcaShenning 2680e3f16dcaShenning if ((offset + sizeof(u_int16_t)) > m->m_len) 2681e3f16dcaShenning m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT); 2682e3f16dcaShenning else 2683e3f16dcaShenning *(u_int16_t *)(mtod(m, caddr_t) + offset) = csum; 2684e3f16dcaShenning } 2685e3f16dcaShenning 2686e3f16dcaShenning void 2687e3f16dcaShenning in6_proto_cksum_out(struct mbuf *m, struct ifnet *ifp) 2688e3f16dcaShenning { 2689b2b8628dSnaddy struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 2690b2b8628dSnaddy 269124ce3f58Snaddy /* some hw and in6_delayed_cksum need the pseudo header cksum */ 269224ce3f58Snaddy if (m->m_pkthdr.csum_flags & 269324ce3f58Snaddy (M_TCP_CSUM_OUT|M_UDP_CSUM_OUT|M_ICMP_CSUM_OUT)) { 269424ce3f58Snaddy int nxt, offset; 269524ce3f58Snaddy u_int16_t csum; 269624ce3f58Snaddy 269724ce3f58Snaddy offset = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 269832c8d29bSbluhm if (ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) && 269932c8d29bSbluhm in_ifcap_cksum(m, ifp, IFCAP_TSOv6)) { 2700510f4386Sbluhm csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst, 2701510f4386Sbluhm htonl(0), htonl(nxt)); 2702510f4386Sbluhm } else { 270324ce3f58Snaddy csum = in6_cksum_phdr(&ip6->ip6_src, &ip6->ip6_dst, 270424ce3f58Snaddy htonl(m->m_pkthdr.len - offset), htonl(nxt)); 2705510f4386Sbluhm } 270624ce3f58Snaddy if (nxt == IPPROTO_TCP) 270724ce3f58Snaddy offset += offsetof(struct tcphdr, th_sum); 270824ce3f58Snaddy else if (nxt == IPPROTO_UDP) 270924ce3f58Snaddy offset += offsetof(struct udphdr, uh_sum); 271024ce3f58Snaddy else if (nxt == IPPROTO_ICMPV6) 271124ce3f58Snaddy offset += offsetof(struct icmp6_hdr, icmp6_cksum); 271224ce3f58Snaddy if ((offset + sizeof(u_int16_t)) > m->m_len) 271324ce3f58Snaddy m_copyback(m, offset, sizeof(csum), &csum, M_NOWAIT); 271424ce3f58Snaddy else 271524ce3f58Snaddy *(u_int16_t *)(mtod(m, caddr_t) + offset) = csum; 271624ce3f58Snaddy } 271724ce3f58Snaddy 2718e3f16dcaShenning if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) { 2719e3f16dcaShenning if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) || 2720b2b8628dSnaddy ip6->ip6_nxt != IPPROTO_TCP || 272196c4247cSmpi ifp->if_bridgeidx != 0) { 272279a86c4bSjca tcpstat_inc(tcps_outswcsum); 2723e3f16dcaShenning in6_delayed_cksum(m, IPPROTO_TCP); 2724e3f16dcaShenning m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */ 2725e3f16dcaShenning } 2726e3f16dcaShenning } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) { 2727e3f16dcaShenning if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) || 2728b2b8628dSnaddy ip6->ip6_nxt != IPPROTO_UDP || 272996c4247cSmpi ifp->if_bridgeidx != 0) { 2730967e9010Sdlg udpstat_inc(udps_outswcsum); 2731e3f16dcaShenning in6_delayed_cksum(m, IPPROTO_UDP); 2732e3f16dcaShenning m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */ 2733e3f16dcaShenning } 2734e3f16dcaShenning } else if (m->m_pkthdr.csum_flags & M_ICMP_CSUM_OUT) { 2735e3f16dcaShenning in6_delayed_cksum(m, IPPROTO_ICMPV6); 2736e3f16dcaShenning m->m_pkthdr.csum_flags &= ~M_ICMP_CSUM_OUT; /* Clear */ 2737e3f16dcaShenning } 2738e3f16dcaShenning } 27394a931851Smarkus 27404a931851Smarkus #ifdef IPSEC 2741e7111b64Sbluhm int 2742ace0f189Sbluhm ip6_output_ipsec_lookup(struct mbuf *m, const struct ipsec_level *seclevel, 27432551e577Sbluhm struct tdb **tdbout) 27444a931851Smarkus { 27454a931851Smarkus struct tdb *tdb; 27464a931851Smarkus struct m_tag *mtag; 27474a931851Smarkus struct tdb_ident *tdbi; 2748e7111b64Sbluhm int error; 27494a931851Smarkus 27504a931851Smarkus /* 27514a931851Smarkus * Check if there was an outgoing SA bound to the flow 27524a931851Smarkus * from a transport protocol. 27534a931851Smarkus */ 27544a931851Smarkus 27554a931851Smarkus /* Do we have any pending SAs to apply ? */ 2756e7111b64Sbluhm error = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr), 27572551e577Sbluhm IPSP_DIRECTION_OUT, NULL, seclevel, &tdb, NULL); 2758e7111b64Sbluhm if (error || tdb == NULL) { 2759e7111b64Sbluhm *tdbout = NULL; 2760e7111b64Sbluhm return error; 2761e7111b64Sbluhm } 27624a931851Smarkus /* Loop detection */ 27638c4a105eSflorian for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) { 27644a931851Smarkus if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE) 27654a931851Smarkus continue; 27664a931851Smarkus tdbi = (struct tdb_ident *)(mtag + 1); 27674a931851Smarkus if (tdbi->spi == tdb->tdb_spi && 27684a931851Smarkus tdbi->proto == tdb->tdb_sproto && 27694a931851Smarkus tdbi->rdomain == tdb->tdb_rdomain && 27708c4a105eSflorian !memcmp(&tdbi->dst, &tdb->tdb_dst, 27718c4a105eSflorian sizeof(union sockaddr_union))) { 27728c4a105eSflorian /* no IPsec needed */ 277331a6915fSbluhm tdb_unref(tdb); 2774e7111b64Sbluhm *tdbout = NULL; 2775e7111b64Sbluhm return 0; 27764a931851Smarkus } 27774a931851Smarkus } 2778e7111b64Sbluhm *tdbout = tdb; 2779e7111b64Sbluhm return 0; 27804a931851Smarkus } 27814a931851Smarkus 27824a931851Smarkus int 278394c0e2bdSbluhm ip6_output_ipsec_pmtu_update(struct tdb *tdb, struct route *ro, 27846d361195Sbluhm struct in6_addr *dst, int ifidx, int rtableid, int transportmode) 27856d361195Sbluhm { 27866d361195Sbluhm struct rtentry *rt = NULL; 27876d361195Sbluhm int rt_mtucloned = 0; 27886d361195Sbluhm 27896d361195Sbluhm /* Find a host route to store the mtu in */ 27906d361195Sbluhm if (ro != NULL) 27916d361195Sbluhm rt = ro->ro_rt; 27926d361195Sbluhm /* but don't add a PMTU route for transport mode SAs */ 27936d361195Sbluhm if (transportmode) 27946d361195Sbluhm rt = NULL; 27956d361195Sbluhm else if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0) { 27966d361195Sbluhm struct sockaddr_in6 sin6; 27976d361195Sbluhm int error; 27986d361195Sbluhm 27996d361195Sbluhm memset(&sin6, 0, sizeof(sin6)); 28006d361195Sbluhm sin6.sin6_family = AF_INET6; 28016d361195Sbluhm sin6.sin6_len = sizeof(sin6); 28026d361195Sbluhm sin6.sin6_addr = *dst; 28036d361195Sbluhm sin6.sin6_scope_id = in6_addr2scopeid(ifidx, dst); 2804952c6363Sbluhm error = in6_embedscope(dst, &sin6, NULL, NULL); 28056d361195Sbluhm if (error) { 28066d361195Sbluhm /* should be impossible */ 28076d361195Sbluhm return error; 28086d361195Sbluhm } 28096d361195Sbluhm rt = icmp6_mtudisc_clone(&sin6, rtableid, 1); 28106d361195Sbluhm rt_mtucloned = 1; 28116d361195Sbluhm } 28126d361195Sbluhm DPRINTF("spi %08x mtu %d rt %p cloned %d", 28136d361195Sbluhm ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned); 28146d361195Sbluhm if (rt != NULL) { 2815*84d9c64aSbluhm atomic_store_int(&rt->rt_mtu, tdb->tdb_mtu); 28166d361195Sbluhm if (ro != NULL && ro->ro_rt != NULL) { 28176d361195Sbluhm rtfree(ro->ro_rt); 281894c0e2bdSbluhm ro->ro_rt = rtalloc(&ro->ro_dstsa, RT_RESOLVE, 28196d361195Sbluhm rtableid); 28206d361195Sbluhm } 28216d361195Sbluhm if (rt_mtucloned) 28226d361195Sbluhm rtfree(rt); 28236d361195Sbluhm } 28246d361195Sbluhm return 0; 28256d361195Sbluhm } 28266d361195Sbluhm 28276d361195Sbluhm int 282894c0e2bdSbluhm ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route *ro, 282959caf375Sbluhm int tunalready, int fwd) 28304a931851Smarkus { 2831c06845b1Sbluhm struct mbuf_list ml; 2832c06845b1Sbluhm struct ifnet *encif = NULL; 283359caf375Sbluhm struct ip6_hdr *ip6; 28346d361195Sbluhm struct in6_addr dst; 2835c06845b1Sbluhm u_int len; 2836c06845b1Sbluhm int error, ifidx, rtableid, tso = 0; 28374a931851Smarkus 28384a931851Smarkus #if NPF > 0 283959caf375Sbluhm /* 284059caf375Sbluhm * Packet filter 284159caf375Sbluhm */ 28424a931851Smarkus if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL || 28434a931851Smarkus pf_test(AF_INET6, fwd ? PF_FWD : PF_OUT, encif, &m) != PF_PASS) { 28444a931851Smarkus m_freem(m); 2845840989e0Sbluhm return EACCES; 28464a931851Smarkus } 28474a931851Smarkus if (m == NULL) 28484a931851Smarkus return 0; 28494a931851Smarkus /* 28504a931851Smarkus * PF_TAG_REROUTE handling or not... 28514a931851Smarkus * Packet is entering IPsec so the routing is 28524a931851Smarkus * already overruled by the IPsec policy. 28534a931851Smarkus * Until now the change was not reconsidered. 28544a931851Smarkus * What's the behaviour? 28554a931851Smarkus */ 28564a931851Smarkus #endif 285759caf375Sbluhm 2858c06845b1Sbluhm /* Check if we can chop the TCP packet */ 285959caf375Sbluhm ip6 = mtod(m, struct ip6_hdr *); 2860c06845b1Sbluhm if (ISSET(m->m_pkthdr.csum_flags, M_TCP_TSO) && 2861c06845b1Sbluhm m->m_pkthdr.ph_mss <= tdb->tdb_mtu) { 2862c06845b1Sbluhm tso = 1; 2863c06845b1Sbluhm len = m->m_pkthdr.ph_mss; 2864c06845b1Sbluhm } else 2865c06845b1Sbluhm len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen); 2866c06845b1Sbluhm 2867c06845b1Sbluhm /* Check if we are allowed to fragment */ 28686d361195Sbluhm dst = ip6->ip6_dst; 28696d361195Sbluhm ifidx = m->m_pkthdr.ph_ifidx; 28706d361195Sbluhm rtableid = m->m_pkthdr.ph_rtableid; 287159caf375Sbluhm if (ip_mtudisc && tdb->tdb_mtu && 2872c06845b1Sbluhm len > tdb->tdb_mtu && tdb->tdb_mtutimeout > gettime()) { 28736d361195Sbluhm int transportmode; 287459caf375Sbluhm 287559caf375Sbluhm transportmode = (tdb->tdb_dst.sa.sa_family == AF_INET6) && 28766d361195Sbluhm (IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, &dst)); 28776d361195Sbluhm error = ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx, 28786d361195Sbluhm rtableid, transportmode); 287959caf375Sbluhm if (error) { 288059caf375Sbluhm ipsecstat_inc(ipsec_odrops); 2881d997d144Smvs tdbstat_inc(tdb, tdb_odrops); 288259caf375Sbluhm m_freem(m); 288359caf375Sbluhm return error; 288459caf375Sbluhm } 288559caf375Sbluhm ipsec_adjust_mtu(m, tdb->tdb_mtu); 288659caf375Sbluhm m_freem(m); 288759caf375Sbluhm return EMSGSIZE; 288859caf375Sbluhm } 288989de4c79Sbluhm /* propagate don't fragment for v6-over-v6 */ 289089de4c79Sbluhm if (ip_mtudisc) 289189de4c79Sbluhm SET(m->m_pkthdr.csum_flags, M_IPV6_DF_OUT); 289259caf375Sbluhm 289359caf375Sbluhm /* 289459caf375Sbluhm * Clear these -- they'll be set in the recursive invocation 289559caf375Sbluhm * as needed. 289659caf375Sbluhm */ 289759caf375Sbluhm m->m_flags &= ~(M_BCAST | M_MCAST); 28984a931851Smarkus 2899c06845b1Sbluhm if (tso) { 2900c06845b1Sbluhm error = tcp_chopper(m, &ml, encif, len); 2901c06845b1Sbluhm if (error) 2902c06845b1Sbluhm goto done; 2903c06845b1Sbluhm } else { 2904c06845b1Sbluhm CLR(m->m_pkthdr.csum_flags, M_TCP_TSO); 2905c06845b1Sbluhm in6_proto_cksum_out(m, encif); 2906c06845b1Sbluhm ml_init(&ml); 2907c06845b1Sbluhm ml_enqueue(&ml, m); 2908c06845b1Sbluhm } 2909c06845b1Sbluhm 29105ee194bcSbluhm KERNEL_LOCK(); 2911c06845b1Sbluhm while ((m = ml_dequeue(&ml)) != NULL) { 2912c06845b1Sbluhm /* Callee frees mbuf */ 29137ced204eSmpi error = ipsp_process_packet(m, tdb, AF_INET6, tunalready); 2914c06845b1Sbluhm if (error) 2915c06845b1Sbluhm break; 2916c06845b1Sbluhm } 29175ee194bcSbluhm KERNEL_UNLOCK(); 2918c06845b1Sbluhm done: 29192edaa7baSmpi if (error) { 2920c06845b1Sbluhm ml_purge(&ml); 29217ced204eSmpi ipsecstat_inc(ipsec_odrops); 2922d997d144Smvs tdbstat_inc(tdb, tdb_odrops); 29232edaa7baSmpi } 2924c06845b1Sbluhm if (!error && tso) 2925c06845b1Sbluhm tcpstat_inc(tcps_outswtso); 29266d361195Sbluhm if (ip_mtudisc && error == EMSGSIZE) 29276d361195Sbluhm ip6_output_ipsec_pmtu_update(tdb, ro, &dst, ifidx, rtableid, 0); 29287ced204eSmpi return error; 29294a931851Smarkus } 29304a931851Smarkus #endif /* IPSEC */ 2931