Annotation of sys/netinet6/ip6_output.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ip6_output.c,v 1.99 2007/06/01 00:52:38 henning Exp $ */
! 2: /* $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $ */
! 3:
! 4: /*
! 5: * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the project nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: */
! 32:
! 33: /*
! 34: * Copyright (c) 1982, 1986, 1988, 1990, 1993
! 35: * The Regents of the University of California. All rights reserved.
! 36: *
! 37: * Redistribution and use in source and binary forms, with or without
! 38: * modification, are permitted provided that the following conditions
! 39: * are met:
! 40: * 1. Redistributions of source code must retain the above copyright
! 41: * notice, this list of conditions and the following disclaimer.
! 42: * 2. Redistributions in binary form must reproduce the above copyright
! 43: * notice, this list of conditions and the following disclaimer in the
! 44: * documentation and/or other materials provided with the distribution.
! 45: * 3. Neither the name of the University nor the names of its contributors
! 46: * may be used to endorse or promote products derived from this software
! 47: * without specific prior written permission.
! 48: *
! 49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 59: * SUCH DAMAGE.
! 60: *
! 61: * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
! 62: */
! 63:
! 64: #include "pf.h"
! 65:
! 66: #include <sys/param.h>
! 67: #include <sys/malloc.h>
! 68: #include <sys/mbuf.h>
! 69: #include <sys/errno.h>
! 70: #include <sys/protosw.h>
! 71: #include <sys/socket.h>
! 72: #include <sys/socketvar.h>
! 73: #include <sys/systm.h>
! 74: #include <sys/proc.h>
! 75:
! 76: #include <net/if.h>
! 77: #include <net/route.h>
! 78:
! 79: #include <netinet/in.h>
! 80: #include <netinet/in_var.h>
! 81: #include <netinet/in_systm.h>
! 82: #include <netinet/ip.h>
! 83: #include <netinet/in_pcb.h>
! 84:
! 85: #include <netinet/ip6.h>
! 86: #include <netinet/icmp6.h>
! 87: #include <netinet6/ip6_var.h>
! 88: #include <netinet6/nd6.h>
! 89: #include <netinet6/ip6protosw.h>
! 90:
! 91: #if NPF > 0
! 92: #include <net/pfvar.h>
! 93: #endif
! 94:
! 95: #ifdef IPSEC
! 96: #include <netinet/ip_ipsp.h>
! 97: #include <netinet/ip_ah.h>
! 98: #include <netinet/ip_esp.h>
! 99: #include <netinet/udp.h>
! 100: #include <netinet/tcp.h>
! 101: #include <net/pfkeyv2.h>
! 102:
! 103: extern u_int8_t get_sa_require(struct inpcb *);
! 104:
! 105: extern int ipsec_auth_default_level;
! 106: extern int ipsec_esp_trans_default_level;
! 107: extern int ipsec_esp_network_default_level;
! 108: extern int ipsec_ipcomp_default_level;
! 109: #endif /* IPSEC */
! 110:
! 111: struct ip6_exthdrs {
! 112: struct mbuf *ip6e_ip6;
! 113: struct mbuf *ip6e_hbh;
! 114: struct mbuf *ip6e_dest1;
! 115: struct mbuf *ip6e_rthdr;
! 116: struct mbuf *ip6e_dest2;
! 117: };
! 118:
! 119: static int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **, int, int);
! 120: static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *, struct socket *);
! 121: static int ip6_getpcbopt(struct ip6_pktopts *, int, struct mbuf **);
! 122: static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, int, int,
! 123: int, int);
! 124: static int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *);
! 125: static int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf **);
! 126: static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
! 127: static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
! 128: struct ip6_frag **);
! 129: static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
! 130: static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
! 131: static int ip6_getpmtu(struct route_in6 *, struct route_in6 *,
! 132: struct ifnet *, struct in6_addr *, u_long *, int *);
! 133: static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
! 134:
! 135: /*
! 136: * IP6 output. The packet in mbuf chain m contains a skeletal IP6
! 137: * header (with pri, len, nxt, hlim, src, dst).
! 138: * This function may modify ver and hlim only.
! 139: * The mbuf chain containing the packet will be freed.
! 140: * The mbuf opt, if present, will not be freed.
! 141: *
! 142: * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and
! 143: * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one,
! 144: * which is rt_rmx.rmx_mtu.
! 145: */
! 146: int
! 147: ip6_output(m0, opt, ro, flags, im6o, ifpp, inp)
! 148: struct mbuf *m0;
! 149: struct ip6_pktopts *opt;
! 150: struct route_in6 *ro;
! 151: int flags;
! 152: struct ip6_moptions *im6o;
! 153: struct ifnet **ifpp; /* XXX: just for statistics */
! 154: struct inpcb *inp;
! 155: {
! 156: struct ip6_hdr *ip6, *mhip6;
! 157: struct ifnet *ifp, *origifp = NULL;
! 158: struct mbuf *m = m0;
! 159: int hlen, tlen, len, off;
! 160: struct route_in6 ip6route;
! 161: struct rtentry *rt = NULL;
! 162: struct sockaddr_in6 *dst, dstsock;
! 163: int error = 0;
! 164: struct in6_ifaddr *ia = NULL;
! 165: u_long mtu;
! 166: int alwaysfrag, dontfrag;
! 167: u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
! 168: struct ip6_exthdrs exthdrs;
! 169: struct in6_addr finaldst;
! 170: struct route_in6 *ro_pmtu = NULL;
! 171: int hdrsplit = 0;
! 172: u_int8_t sproto = 0;
! 173: #ifdef IPSEC
! 174: struct m_tag *mtag;
! 175: union sockaddr_union sdst;
! 176: struct tdb_ident *tdbi;
! 177: u_int32_t sspi;
! 178: struct tdb *tdb;
! 179: int s;
! 180: #endif /* IPSEC */
! 181:
! 182: #ifdef IPSEC
! 183: if (inp && (inp->inp_flags & INP_IPV6) == 0)
! 184: panic("ip6_output: IPv4 pcb is passed");
! 185: #endif /* IPSEC */
! 186:
! 187: ip6 = mtod(m, struct ip6_hdr *);
! 188: finaldst = ip6->ip6_dst;
! 189:
! 190: #define MAKE_EXTHDR(hp, mp) \
! 191: do { \
! 192: if (hp) { \
! 193: struct ip6_ext *eh = (struct ip6_ext *)(hp); \
! 194: error = ip6_copyexthdr((mp), (caddr_t)(hp), \
! 195: ((eh)->ip6e_len + 1) << 3); \
! 196: if (error) \
! 197: goto freehdrs; \
! 198: } \
! 199: } while (0)
! 200:
! 201: bzero(&exthdrs, sizeof(exthdrs));
! 202:
! 203: if (opt) {
! 204: /* Hop-by-Hop options header */
! 205: MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
! 206: /* Destination options header(1st part) */
! 207: MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
! 208: /* Routing header */
! 209: MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
! 210: /* Destination options header(2nd part) */
! 211: MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
! 212: }
! 213:
! 214: #ifdef IPSEC
! 215: if (!ipsec_in_use && !inp)
! 216: goto done_spd;
! 217:
! 218: /*
! 219: * splnet is chosen over spltdb because we are not allowed to
! 220: * lower the level, and udp6_output calls us in splnet(). XXX check
! 221: */
! 222: s = splnet();
! 223:
! 224: /*
! 225: * Check if there was an outgoing SA bound to the flow
! 226: * from a transport protocol.
! 227: */
! 228: ip6 = mtod(m, struct ip6_hdr *);
! 229:
! 230: /* Do we have any pending SAs to apply ? */
! 231: mtag = m_tag_find(m, PACKET_TAG_IPSEC_PENDING_TDB, NULL);
! 232: if (mtag != NULL) {
! 233: #ifdef DIAGNOSTIC
! 234: if (mtag->m_tag_len != sizeof (struct tdb_ident))
! 235: panic("ip6_output: tag of length %d (should be %d",
! 236: mtag->m_tag_len, sizeof (struct tdb_ident));
! 237: #endif
! 238: tdbi = (struct tdb_ident *)(mtag + 1);
! 239: tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
! 240: if (tdb == NULL)
! 241: error = -EINVAL;
! 242: m_tag_delete(m, mtag);
! 243: } else
! 244: tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
! 245: &error, IPSP_DIRECTION_OUT, NULL, inp);
! 246:
! 247: if (tdb == NULL) {
! 248: splx(s);
! 249:
! 250: if (error == 0) {
! 251: /*
! 252: * No IPsec processing required, we'll just send the
! 253: * packet out.
! 254: */
! 255: sproto = 0;
! 256:
! 257: /* Fall through to routing/multicast handling */
! 258: } else {
! 259: /*
! 260: * -EINVAL is used to indicate that the packet should
! 261: * be silently dropped, typically because we've asked
! 262: * key management for an SA.
! 263: */
! 264: if (error == -EINVAL) /* Should silently drop packet */
! 265: error = 0;
! 266:
! 267: goto freehdrs;
! 268: }
! 269: } else {
! 270: /* Loop detection */
! 271: for (mtag = m_tag_first(m); mtag != NULL;
! 272: mtag = m_tag_next(m, mtag)) {
! 273: if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
! 274: mtag->m_tag_id !=
! 275: PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
! 276: continue;
! 277: tdbi = (struct tdb_ident *)(mtag + 1);
! 278: if (tdbi->spi == tdb->tdb_spi &&
! 279: tdbi->proto == tdb->tdb_sproto &&
! 280: !bcmp(&tdbi->dst, &tdb->tdb_dst,
! 281: sizeof(union sockaddr_union))) {
! 282: splx(s);
! 283: sproto = 0; /* mark as no-IPsec-needed */
! 284: goto done_spd;
! 285: }
! 286: }
! 287:
! 288: /* We need to do IPsec */
! 289: bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst));
! 290: sspi = tdb->tdb_spi;
! 291: sproto = tdb->tdb_sproto;
! 292: splx(s);
! 293: }
! 294:
! 295: /* Fall through to the routing/multicast handling code */
! 296: done_spd:
! 297: #endif /* IPSEC */
! 298:
! 299: /*
! 300: * Calculate the total length of the extension header chain.
! 301: * Keep the length of the unfragmentable part for fragmentation.
! 302: */
! 303: optlen = 0;
! 304: if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
! 305: if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
! 306: if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
! 307: unfragpartlen = optlen + sizeof(struct ip6_hdr);
! 308: /* NOTE: we don't add AH/ESP length here. do that later. */
! 309: if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
! 310:
! 311: /*
! 312: * If we need IPsec, or there is at least one extension header,
! 313: * separate IP6 header from the payload.
! 314: */
! 315: if ((sproto || optlen) && !hdrsplit) {
! 316: if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
! 317: m = NULL;
! 318: goto freehdrs;
! 319: }
! 320: m = exthdrs.ip6e_ip6;
! 321: hdrsplit++;
! 322: }
! 323:
! 324: /* adjust pointer */
! 325: ip6 = mtod(m, struct ip6_hdr *);
! 326:
! 327: /* adjust mbuf packet header length */
! 328: m->m_pkthdr.len += optlen;
! 329: plen = m->m_pkthdr.len - sizeof(*ip6);
! 330:
! 331: /* If this is a jumbo payload, insert a jumbo payload option. */
! 332: if (plen > IPV6_MAXPACKET) {
! 333: if (!hdrsplit) {
! 334: if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
! 335: m = NULL;
! 336: goto freehdrs;
! 337: }
! 338: m = exthdrs.ip6e_ip6;
! 339: hdrsplit++;
! 340: }
! 341: /* adjust pointer */
! 342: ip6 = mtod(m, struct ip6_hdr *);
! 343: if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
! 344: goto freehdrs;
! 345: ip6->ip6_plen = 0;
! 346: } else
! 347: ip6->ip6_plen = htons(plen);
! 348:
! 349: /*
! 350: * Concatenate headers and fill in next header fields.
! 351: * Here we have, on "m"
! 352: * IPv6 payload
! 353: * and we insert headers accordingly. Finally, we should be getting:
! 354: * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
! 355: *
! 356: * during the header composing process, "m" points to IPv6 header.
! 357: * "mprev" points to an extension header prior to esp.
! 358: */
! 359: {
! 360: u_char *nexthdrp = &ip6->ip6_nxt;
! 361: struct mbuf *mprev = m;
! 362:
! 363: /*
! 364: * we treat dest2 specially. this makes IPsec processing
! 365: * much easier. the goal here is to make mprev point the
! 366: * mbuf prior to dest2.
! 367: *
! 368: * result: IPv6 dest2 payload
! 369: * m and mprev will point to IPv6 header.
! 370: */
! 371: if (exthdrs.ip6e_dest2) {
! 372: if (!hdrsplit)
! 373: panic("assumption failed: hdr not split");
! 374: exthdrs.ip6e_dest2->m_next = m->m_next;
! 375: m->m_next = exthdrs.ip6e_dest2;
! 376: *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
! 377: ip6->ip6_nxt = IPPROTO_DSTOPTS;
! 378: }
! 379:
! 380: #define MAKE_CHAIN(m, mp, p, i)\
! 381: do {\
! 382: if (m) {\
! 383: if (!hdrsplit) \
! 384: panic("assumption failed: hdr not split"); \
! 385: *mtod((m), u_char *) = *(p);\
! 386: *(p) = (i);\
! 387: p = mtod((m), u_char *);\
! 388: (m)->m_next = (mp)->m_next;\
! 389: (mp)->m_next = (m);\
! 390: (mp) = (m);\
! 391: }\
! 392: } while (0)
! 393: /*
! 394: * result: IPv6 hbh dest1 rthdr dest2 payload
! 395: * m will point to IPv6 header. mprev will point to the
! 396: * extension header prior to dest2 (rthdr in the above case).
! 397: */
! 398: MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS);
! 399: MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp,
! 400: IPPROTO_DSTOPTS);
! 401: MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp,
! 402: IPPROTO_ROUTING);
! 403: }
! 404:
! 405: /*
! 406: * If there is a routing header, replace the destination address field
! 407: * with the first hop of the routing header.
! 408: */
! 409: if (exthdrs.ip6e_rthdr) {
! 410: struct ip6_rthdr *rh;
! 411: struct ip6_rthdr0 *rh0;
! 412: struct in6_addr *addr;
! 413:
! 414: rh = (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
! 415: struct ip6_rthdr *));
! 416: switch (rh->ip6r_type) {
! 417: case IPV6_RTHDR_TYPE_0:
! 418: rh0 = (struct ip6_rthdr0 *)rh;
! 419: addr = (struct in6_addr *)(rh0 + 1);
! 420: ip6->ip6_dst = addr[0];
! 421: bcopy(&addr[1], &addr[0],
! 422: sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1));
! 423: addr[rh0->ip6r0_segleft - 1] = finaldst;
! 424: break;
! 425: default: /* is it possible? */
! 426: error = EINVAL;
! 427: goto bad;
! 428: }
! 429: }
! 430:
! 431: /* Source address validation */
! 432: if (!(flags & IPV6_UNSPECSRC) &&
! 433: IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
! 434: /*
! 435: * XXX: we can probably assume validation in the caller, but
! 436: * we explicitly check the address here for safety.
! 437: */
! 438: error = EOPNOTSUPP;
! 439: ip6stat.ip6s_badscope++;
! 440: goto bad;
! 441: }
! 442: if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
! 443: error = EOPNOTSUPP;
! 444: ip6stat.ip6s_badscope++;
! 445: goto bad;
! 446: }
! 447:
! 448: ip6stat.ip6s_localout++;
! 449:
! 450: /*
! 451: * Route packet.
! 452: */
! 453: /* initialize cached route */
! 454: if (ro == 0) {
! 455: ro = &ip6route;
! 456: bzero((caddr_t)ro, sizeof(*ro));
! 457: }
! 458: ro_pmtu = ro;
! 459: if (opt && opt->ip6po_rthdr)
! 460: ro = &opt->ip6po_route;
! 461: dst = (struct sockaddr_in6 *)&ro->ro_dst;
! 462:
! 463: /*
! 464: * if specified, try to fill in the traffic class field.
! 465: * do not override if a non-zero value is already set.
! 466: * we check the diffserv field and the ecn field separately.
! 467: */
! 468: if (opt && opt->ip6po_tclass >= 0) {
! 469: int mask = 0;
! 470:
! 471: if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
! 472: mask |= 0xfc;
! 473: if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
! 474: mask |= 0x03;
! 475: if (mask != 0)
! 476: ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20);
! 477: }
! 478:
! 479: /* fill in or override the hop limit field, if necessary. */
! 480: if (opt && opt->ip6po_hlim != -1)
! 481: ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
! 482: else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
! 483: if (im6o != NULL)
! 484: ip6->ip6_hlim = im6o->im6o_multicast_hlim;
! 485: else
! 486: ip6->ip6_hlim = ip6_defmcasthlim;
! 487: }
! 488:
! 489: #ifdef IPSEC
! 490: /*
! 491: * Check if the packet needs encapsulation.
! 492: * ipsp_process_packet will never come back to here.
! 493: */
! 494: if (sproto != 0) {
! 495: s = splnet();
! 496:
! 497: /*
! 498: * XXX what should we do if ip6_hlim == 0 and the
! 499: * packet gets tunneled?
! 500: */
! 501:
! 502: tdb = gettdb(sspi, &sdst, sproto);
! 503: if (tdb == NULL) {
! 504: splx(s);
! 505: error = EHOSTUNREACH;
! 506: m_freem(m);
! 507: goto done;
! 508: }
! 509:
! 510: /* Latch to PCB */
! 511: if (inp)
! 512: tdb_add_inp(tdb, inp, 0);
! 513:
! 514: m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
! 515:
! 516: /* Callee frees mbuf */
! 517: /*
! 518: * if we are source-routing, do not attempt to tunnel the
! 519: * packet just because ip6_dst is different from what tdb has.
! 520: * XXX
! 521: */
! 522: error = ipsp_process_packet(m, tdb, AF_INET6,
! 523: exthdrs.ip6e_rthdr ? 1 : 0);
! 524: splx(s);
! 525:
! 526: return error; /* Nothing more to be done */
! 527: }
! 528: #endif /* IPSEC */
! 529:
! 530: bzero(&dstsock, sizeof(dstsock));
! 531: dstsock.sin6_family = AF_INET6;
! 532: dstsock.sin6_addr = ip6->ip6_dst;
! 533: dstsock.sin6_len = sizeof(dstsock);
! 534: if ((error = in6_selectroute(&dstsock, opt, im6o, ro, &ifp,
! 535: &rt)) != 0) {
! 536: switch (error) {
! 537: case EHOSTUNREACH:
! 538: ip6stat.ip6s_noroute++;
! 539: break;
! 540: case EADDRNOTAVAIL:
! 541: default:
! 542: break; /* XXX statistics? */
! 543: }
! 544: if (ifp != NULL)
! 545: in6_ifstat_inc(ifp, ifs6_out_discard);
! 546: goto bad;
! 547: }
! 548: if (rt == NULL) {
! 549: /*
! 550: * If in6_selectroute() does not return a route entry,
! 551: * dst may not have been updated.
! 552: */
! 553: *dst = dstsock; /* XXX */
! 554: }
! 555:
! 556: /*
! 557: * then rt (for unicast) and ifp must be non-NULL valid values.
! 558: */
! 559: if (rt) {
! 560: ia = (struct in6_ifaddr *)(rt->rt_ifa);
! 561: rt->rt_use++;
! 562: }
! 563:
! 564: if ((flags & IPV6_FORWARDING) == 0) {
! 565: /* XXX: the FORWARDING flag can be set for mrouting. */
! 566: in6_ifstat_inc(ifp, ifs6_out_request);
! 567: }
! 568:
! 569: /*
! 570: * The outgoing interface must be in the zone of source and
! 571: * destination addresses. We should use ia_ifp to support the
! 572: * case of sending packets to an address of our own.
! 573: */
! 574: if (ia != NULL && ia->ia_ifp)
! 575: origifp = ia->ia_ifp;
! 576: else
! 577: origifp = ifp;
! 578:
! 579: if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
! 580: if (opt && opt->ip6po_nextroute.ro_rt) {
! 581: /*
! 582: * The nexthop is explicitly specified by the
! 583: * application. We assume the next hop is an IPv6
! 584: * address.
! 585: */
! 586: dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
! 587: } else if ((rt->rt_flags & RTF_GATEWAY))
! 588: dst = (struct sockaddr_in6 *)rt->rt_gateway;
! 589: }
! 590:
! 591: if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
! 592: /* Unicast */
! 593:
! 594: m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
! 595: } else {
! 596: /* Multicast */
! 597: struct in6_multi *in6m;
! 598:
! 599: m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
! 600:
! 601: in6_ifstat_inc(ifp, ifs6_out_mcast);
! 602:
! 603: /*
! 604: * Confirm that the outgoing interface supports multicast.
! 605: */
! 606: if ((ifp->if_flags & IFF_MULTICAST) == 0) {
! 607: ip6stat.ip6s_noroute++;
! 608: in6_ifstat_inc(ifp, ifs6_out_discard);
! 609: error = ENETUNREACH;
! 610: goto bad;
! 611: }
! 612: IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
! 613: if (in6m != NULL &&
! 614: (im6o == NULL || im6o->im6o_multicast_loop)) {
! 615: /*
! 616: * If we belong to the destination multicast group
! 617: * on the outgoing interface, and the caller did not
! 618: * forbid loopback, loop back a copy.
! 619: */
! 620: ip6_mloopback(ifp, m, dst);
! 621: } else {
! 622: /*
! 623: * If we are acting as a multicast router, perform
! 624: * multicast forwarding as if the packet had just
! 625: * arrived on the interface to which we are about
! 626: * to send. The multicast forwarding function
! 627: * recursively calls this function, using the
! 628: * IPV6_FORWARDING flag to prevent infinite recursion.
! 629: *
! 630: * Multicasts that are looped back by ip6_mloopback(),
! 631: * above, will be forwarded by the ip6_input() routine,
! 632: * if necessary.
! 633: */
! 634: #ifdef MROUTING
! 635: if (ip6_mforwarding && ip6_mrouter &&
! 636: (flags & IPV6_FORWARDING) == 0) {
! 637: if (ip6_mforward(ip6, ifp, m) != 0) {
! 638: m_freem(m);
! 639: goto done;
! 640: }
! 641: }
! 642: #endif
! 643: }
! 644: /*
! 645: * Multicasts with a hoplimit of zero may be looped back,
! 646: * above, but must not be transmitted on a network.
! 647: * Also, multicasts addressed to the loopback interface
! 648: * are not sent -- the above call to ip6_mloopback() will
! 649: * loop back a copy if this host actually belongs to the
! 650: * destination group on the loopback interface.
! 651: */
! 652: if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
! 653: IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
! 654: m_freem(m);
! 655: goto done;
! 656: }
! 657: }
! 658:
! 659: /*
! 660: * Fill the outgoing interface to tell the upper layer
! 661: * to increment per-interface statistics.
! 662: */
! 663: if (ifpp)
! 664: *ifpp = ifp;
! 665:
! 666: /* Determine path MTU. */
! 667: if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu,
! 668: &alwaysfrag)) != 0)
! 669: goto bad;
! 670:
! 671: /*
! 672: * The caller of this function may specify to use the minimum MTU
! 673: * in some cases.
! 674: * An advanced API option (IPV6_USE_MIN_MTU) can also override MTU
! 675: * setting. The logic is a bit complicated; by default, unicast
! 676: * packets will follow path MTU while multicast packets will be sent at
! 677: * the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets
! 678: * including unicast ones will be sent at the minimum MTU. Multicast
! 679: * packets will always be sent at the minimum MTU unless
! 680: * IP6PO_MINMTU_DISABLE is explicitly specified.
! 681: * See RFC 3542 for more details.
! 682: */
! 683: if (mtu > IPV6_MMTU) {
! 684: if ((flags & IPV6_MINMTU))
! 685: mtu = IPV6_MMTU;
! 686: else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL)
! 687: mtu = IPV6_MMTU;
! 688: else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
! 689: (opt == NULL ||
! 690: opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) {
! 691: mtu = IPV6_MMTU;
! 692: }
! 693: }
! 694:
! 695: /* Fake scoped addresses */
! 696: if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
! 697: /*
! 698: * If source or destination address is a scoped address, and
! 699: * the packet is going to be sent to a loopback interface,
! 700: * we should keep the original interface.
! 701: */
! 702:
! 703: /*
! 704: * XXX: this is a very experimental and temporary solution.
! 705: * We eventually have sockaddr_in6 and use the sin6_scope_id
! 706: * field of the structure here.
! 707: * We rely on the consistency between two scope zone ids
! 708: * of source add destination, which should already be assured
! 709: * Larger scopes than link will be supported in the near
! 710: * future.
! 711: */
! 712: origifp = NULL;
! 713: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
! 714: origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])];
! 715: else if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
! 716: origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];
! 717: /*
! 718: * XXX: origifp can be NULL even in those two cases above.
! 719: * For example, if we remove the (only) link-local address
! 720: * from the loopback interface, and try to send a link-local
! 721: * address without link-id information. Then the source
! 722: * address is ::1, and the destination address is the
! 723: * link-local address with its s6_addr16[1] being zero.
! 724: * What is worse, if the packet goes to the loopback interface
! 725: * by a default rejected route, the null pointer would be
! 726: * passed to looutput, and the kernel would hang.
! 727: * The following last resort would prevent such disaster.
! 728: */
! 729: if (origifp == NULL)
! 730: origifp = ifp;
! 731: } else
! 732: origifp = ifp;
! 733: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
! 734: ip6->ip6_src.s6_addr16[1] = 0;
! 735: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
! 736: ip6->ip6_dst.s6_addr16[1] = 0;
! 737:
! 738: /*
! 739: * If the outgoing packet contains a hop-by-hop options header,
! 740: * it must be examined and processed even by the source node.
! 741: * (RFC 2460, section 4.)
! 742: */
! 743: if (exthdrs.ip6e_hbh) {
! 744: struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
! 745: u_int32_t dummy1; /* XXX unused */
! 746: u_int32_t dummy2; /* XXX unused */
! 747:
! 748: /*
! 749: * XXX: if we have to send an ICMPv6 error to the sender,
! 750: * we need the M_LOOP flag since icmp6_error() expects
! 751: * the IPv6 and the hop-by-hop options header are
! 752: * continuous unless the flag is set.
! 753: */
! 754: m->m_flags |= M_LOOP;
! 755: m->m_pkthdr.rcvif = ifp;
! 756: if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1),
! 757: ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
! 758: &dummy1, &dummy2) < 0) {
! 759: /* m was already freed at this point */
! 760: error = EINVAL;/* better error? */
! 761: goto done;
! 762: }
! 763: m->m_flags &= ~M_LOOP; /* XXX */
! 764: m->m_pkthdr.rcvif = NULL;
! 765: }
! 766:
! 767: #if NPF > 0
! 768: if (pf_test6(PF_OUT, ifp, &m, NULL) != PF_PASS) {
! 769: error = EHOSTUNREACH;
! 770: m_freem(m);
! 771: goto done;
! 772: }
! 773: if (m == NULL)
! 774: goto done;
! 775: ip6 = mtod(m, struct ip6_hdr *);
! 776: #endif
! 777:
! 778: /*
! 779: * Send the packet to the outgoing interface.
! 780: * If necessary, do IPv6 fragmentation before sending.
! 781: *
! 782: * the logic here is rather complex:
! 783: * 1: normal case (dontfrag == 0, alwaysfrag == 0)
! 784: * 1-a: send as is if tlen <= path mtu
! 785: * 1-b: fragment if tlen > path mtu
! 786: *
! 787: * 2: if user asks us not to fragment (dontfrag == 1)
! 788: * 2-a: send as is if tlen <= interface mtu
! 789: * 2-b: error if tlen > interface mtu
! 790: *
! 791: * 3: if we always need to attach fragment header (alwaysfrag == 1)
! 792: * always fragment
! 793: *
! 794: * 4: if dontfrag == 1 && alwaysfrag == 1
! 795: * error, as we cannot handle this conflicting request
! 796: */
! 797: tlen = m->m_pkthdr.len;
! 798:
! 799: if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
! 800: dontfrag = 1;
! 801: else
! 802: dontfrag = 0;
! 803: if (dontfrag && alwaysfrag) { /* case 4 */
! 804: /* conflicting request - can't transmit */
! 805: error = EMSGSIZE;
! 806: goto bad;
! 807: }
! 808: if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */
! 809: /*
! 810: * Even if the DONTFRAG option is specified, we cannot send the
! 811: * packet when the data length is larger than the MTU of the
! 812: * outgoing interface.
! 813: * Notify the error by sending IPV6_PATHMTU ancillary data as
! 814: * well as returning an error code (the latter is not described
! 815: * in the API spec.)
! 816: */
! 817: #if 0
! 818: u_int32_t mtu32;
! 819: struct ip6ctlparam ip6cp;
! 820:
! 821: mtu32 = (u_int32_t)mtu;
! 822: bzero(&ip6cp, sizeof(ip6cp));
! 823: ip6cp.ip6c_cmdarg = (void *)&mtu32;
! 824: pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst,
! 825: (void *)&ip6cp);
! 826: #endif
! 827:
! 828: error = EMSGSIZE;
! 829: goto bad;
! 830: }
! 831:
! 832: /*
! 833: * transmit packet without fragmentation
! 834: */
! 835: if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
! 836: error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
! 837: goto done;
! 838: }
! 839:
! 840: /*
! 841: * try to fragment the packet. case 1-b and 3
! 842: */
! 843: if (mtu < IPV6_MMTU) {
! 844: /* path MTU cannot be less than IPV6_MMTU */
! 845: error = EMSGSIZE;
! 846: in6_ifstat_inc(ifp, ifs6_out_fragfail);
! 847: goto bad;
! 848: } else if (ip6->ip6_plen == 0) {
! 849: /* jumbo payload cannot be fragmented */
! 850: error = EMSGSIZE;
! 851: in6_ifstat_inc(ifp, ifs6_out_fragfail);
! 852: goto bad;
! 853: } else {
! 854: struct mbuf **mnext, *m_frgpart;
! 855: struct ip6_frag *ip6f;
! 856: u_int32_t id = htonl(ip6_randomid());
! 857: u_char nextproto;
! 858: #if 0
! 859: struct ip6ctlparam ip6cp;
! 860: u_int32_t mtu32;
! 861: #endif
! 862:
! 863: /*
! 864: * Too large for the destination or interface;
! 865: * fragment if possible.
! 866: * Must be able to put at least 8 bytes per fragment.
! 867: */
! 868: hlen = unfragpartlen;
! 869: if (mtu > IPV6_MAXPACKET)
! 870: mtu = IPV6_MAXPACKET;
! 871:
! 872: #if 0
! 873: /* Notify a proper path MTU to applications. */
! 874: mtu32 = (u_int32_t)mtu;
! 875: bzero(&ip6cp, sizeof(ip6cp));
! 876: ip6cp.ip6c_cmdarg = (void *)&mtu32;
! 877: pfctlinput2(PRC_MSGSIZE, (struct sockaddr *)&ro_pmtu->ro_dst,
! 878: (void *)&ip6cp);
! 879: #endif
! 880:
! 881: len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
! 882: if (len < 8) {
! 883: error = EMSGSIZE;
! 884: in6_ifstat_inc(ifp, ifs6_out_fragfail);
! 885: goto bad;
! 886: }
! 887:
! 888: mnext = &m->m_nextpkt;
! 889:
! 890: /*
! 891: * Change the next header field of the last header in the
! 892: * unfragmentable part.
! 893: */
! 894: if (exthdrs.ip6e_rthdr) {
! 895: nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
! 896: *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
! 897: } else if (exthdrs.ip6e_dest1) {
! 898: nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
! 899: *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
! 900: } else if (exthdrs.ip6e_hbh) {
! 901: nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
! 902: *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
! 903: } else {
! 904: nextproto = ip6->ip6_nxt;
! 905: ip6->ip6_nxt = IPPROTO_FRAGMENT;
! 906: }
! 907:
! 908: /*
! 909: * Loop through length of segment after first fragment,
! 910: * make new header and copy data of each part and link onto
! 911: * chain.
! 912: */
! 913: m0 = m;
! 914: for (off = hlen; off < tlen; off += len) {
! 915: struct mbuf *mlast;
! 916:
! 917: MGETHDR(m, M_DONTWAIT, MT_HEADER);
! 918: if (!m) {
! 919: error = ENOBUFS;
! 920: ip6stat.ip6s_odropped++;
! 921: goto sendorfree;
! 922: }
! 923: m->m_pkthdr.rcvif = NULL;
! 924: m->m_flags = m0->m_flags & M_COPYFLAGS;
! 925: *mnext = m;
! 926: mnext = &m->m_nextpkt;
! 927: m->m_data += max_linkhdr;
! 928: mhip6 = mtod(m, struct ip6_hdr *);
! 929: *mhip6 = *ip6;
! 930: m->m_len = sizeof(*mhip6);
! 931: error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
! 932: if (error) {
! 933: ip6stat.ip6s_odropped++;
! 934: goto sendorfree;
! 935: }
! 936: ip6f->ip6f_offlg = htons((u_int16_t)((off - hlen) & ~7));
! 937: if (off + len >= tlen)
! 938: len = tlen - off;
! 939: else
! 940: ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
! 941: mhip6->ip6_plen = htons((u_int16_t)(len + hlen +
! 942: sizeof(*ip6f) - sizeof(struct ip6_hdr)));
! 943: if ((m_frgpart = m_copy(m0, off, len)) == 0) {
! 944: error = ENOBUFS;
! 945: ip6stat.ip6s_odropped++;
! 946: goto sendorfree;
! 947: }
! 948: for (mlast = m; mlast->m_next; mlast = mlast->m_next)
! 949: ;
! 950: mlast->m_next = m_frgpart;
! 951: m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
! 952: m->m_pkthdr.rcvif = (struct ifnet *)0;
! 953: ip6f->ip6f_reserved = 0;
! 954: ip6f->ip6f_ident = id;
! 955: ip6f->ip6f_nxt = nextproto;
! 956: ip6stat.ip6s_ofragments++;
! 957: in6_ifstat_inc(ifp, ifs6_out_fragcreat);
! 958: }
! 959:
! 960: in6_ifstat_inc(ifp, ifs6_out_fragok);
! 961: }
! 962:
! 963: /*
! 964: * Remove leading garbages.
! 965: */
! 966: sendorfree:
! 967: m = m0->m_nextpkt;
! 968: m0->m_nextpkt = 0;
! 969: m_freem(m0);
! 970: for (m0 = m; m; m = m0) {
! 971: m0 = m->m_nextpkt;
! 972: m->m_nextpkt = 0;
! 973: if (error == 0) {
! 974: error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
! 975: } else
! 976: m_freem(m);
! 977: }
! 978:
! 979: if (error == 0)
! 980: ip6stat.ip6s_fragmented++;
! 981:
! 982: done:
! 983: if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
! 984: RTFREE(ro->ro_rt);
! 985: } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
! 986: RTFREE(ro_pmtu->ro_rt);
! 987: }
! 988:
! 989: return (error);
! 990:
! 991: freehdrs:
! 992: m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
! 993: m_freem(exthdrs.ip6e_dest1);
! 994: m_freem(exthdrs.ip6e_rthdr);
! 995: m_freem(exthdrs.ip6e_dest2);
! 996: /* FALLTHROUGH */
! 997: bad:
! 998: m_freem(m);
! 999: goto done;
! 1000: }
! 1001:
! 1002: static int
! 1003: ip6_copyexthdr(mp, hdr, hlen)
! 1004: struct mbuf **mp;
! 1005: caddr_t hdr;
! 1006: int hlen;
! 1007: {
! 1008: struct mbuf *m;
! 1009:
! 1010: if (hlen > MCLBYTES)
! 1011: return (ENOBUFS); /* XXX */
! 1012:
! 1013: MGET(m, M_DONTWAIT, MT_DATA);
! 1014: if (!m)
! 1015: return (ENOBUFS);
! 1016:
! 1017: if (hlen > MLEN) {
! 1018: MCLGET(m, M_DONTWAIT);
! 1019: if ((m->m_flags & M_EXT) == 0) {
! 1020: m_free(m);
! 1021: return (ENOBUFS);
! 1022: }
! 1023: }
! 1024: m->m_len = hlen;
! 1025: if (hdr)
! 1026: bcopy(hdr, mtod(m, caddr_t), hlen);
! 1027:
! 1028: *mp = m;
! 1029: return (0);
! 1030: }
! 1031:
! 1032: /*
! 1033: * Insert jumbo payload option.
! 1034: */
! 1035: static int
! 1036: ip6_insert_jumboopt(exthdrs, plen)
! 1037: struct ip6_exthdrs *exthdrs;
! 1038: u_int32_t plen;
! 1039: {
! 1040: struct mbuf *mopt;
! 1041: u_int8_t *optbuf;
! 1042: u_int32_t v;
! 1043:
! 1044: #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
! 1045:
! 1046: /*
! 1047: * If there is no hop-by-hop options header, allocate new one.
! 1048: * If there is one but it doesn't have enough space to store the
! 1049: * jumbo payload option, allocate a cluster to store the whole options.
! 1050: * Otherwise, use it to store the options.
! 1051: */
! 1052: if (exthdrs->ip6e_hbh == 0) {
! 1053: MGET(mopt, M_DONTWAIT, MT_DATA);
! 1054: if (mopt == 0)
! 1055: return (ENOBUFS);
! 1056: mopt->m_len = JUMBOOPTLEN;
! 1057: optbuf = mtod(mopt, u_int8_t *);
! 1058: optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
! 1059: exthdrs->ip6e_hbh = mopt;
! 1060: } else {
! 1061: struct ip6_hbh *hbh;
! 1062:
! 1063: mopt = exthdrs->ip6e_hbh;
! 1064: if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
! 1065: /*
! 1066: * XXX assumption:
! 1067: * - exthdrs->ip6e_hbh is not referenced from places
! 1068: * other than exthdrs.
! 1069: * - exthdrs->ip6e_hbh is not an mbuf chain.
! 1070: */
! 1071: int oldoptlen = mopt->m_len;
! 1072: struct mbuf *n;
! 1073:
! 1074: /*
! 1075: * XXX: give up if the whole (new) hbh header does
! 1076: * not fit even in an mbuf cluster.
! 1077: */
! 1078: if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
! 1079: return (ENOBUFS);
! 1080:
! 1081: /*
! 1082: * As a consequence, we must always prepare a cluster
! 1083: * at this point.
! 1084: */
! 1085: MGET(n, M_DONTWAIT, MT_DATA);
! 1086: if (n) {
! 1087: MCLGET(n, M_DONTWAIT);
! 1088: if ((n->m_flags & M_EXT) == 0) {
! 1089: m_freem(n);
! 1090: n = NULL;
! 1091: }
! 1092: }
! 1093: if (!n)
! 1094: return (ENOBUFS);
! 1095: n->m_len = oldoptlen + JUMBOOPTLEN;
! 1096: bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t),
! 1097: oldoptlen);
! 1098: optbuf = mtod(n, u_int8_t *) + oldoptlen;
! 1099: m_freem(mopt);
! 1100: mopt = exthdrs->ip6e_hbh = n;
! 1101: } else {
! 1102: optbuf = mtod(mopt, u_int8_t *) + mopt->m_len;
! 1103: mopt->m_len += JUMBOOPTLEN;
! 1104: }
! 1105: optbuf[0] = IP6OPT_PADN;
! 1106: optbuf[1] = 0;
! 1107:
! 1108: /*
! 1109: * Adjust the header length according to the pad and
! 1110: * the jumbo payload option.
! 1111: */
! 1112: hbh = mtod(mopt, struct ip6_hbh *);
! 1113: hbh->ip6h_len += (JUMBOOPTLEN >> 3);
! 1114: }
! 1115:
! 1116: /* fill in the option. */
! 1117: optbuf[2] = IP6OPT_JUMBO;
! 1118: optbuf[3] = 4;
! 1119: v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
! 1120: bcopy(&v, &optbuf[4], sizeof(u_int32_t));
! 1121:
! 1122: /* finally, adjust the packet header length */
! 1123: exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
! 1124:
! 1125: return (0);
! 1126: #undef JUMBOOPTLEN
! 1127: }
! 1128:
! 1129: /*
! 1130: * Insert fragment header and copy unfragmentable header portions.
! 1131: */
! 1132: static int
! 1133: ip6_insertfraghdr(m0, m, hlen, frghdrp)
! 1134: struct mbuf *m0, *m;
! 1135: int hlen;
! 1136: struct ip6_frag **frghdrp;
! 1137: {
! 1138: struct mbuf *n, *mlast;
! 1139:
! 1140: if (hlen > sizeof(struct ip6_hdr)) {
! 1141: n = m_copym(m0, sizeof(struct ip6_hdr),
! 1142: hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
! 1143: if (n == 0)
! 1144: return (ENOBUFS);
! 1145: m->m_next = n;
! 1146: } else
! 1147: n = m;
! 1148:
! 1149: /* Search for the last mbuf of unfragmentable part. */
! 1150: for (mlast = n; mlast->m_next; mlast = mlast->m_next)
! 1151: ;
! 1152:
! 1153: if ((mlast->m_flags & M_EXT) == 0 &&
! 1154: M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {
! 1155: /* use the trailing space of the last mbuf for the fragment hdr */
! 1156: *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) +
! 1157: mlast->m_len);
! 1158: mlast->m_len += sizeof(struct ip6_frag);
! 1159: m->m_pkthdr.len += sizeof(struct ip6_frag);
! 1160: } else {
! 1161: /* allocate a new mbuf for the fragment header */
! 1162: struct mbuf *mfrg;
! 1163:
! 1164: MGET(mfrg, M_DONTWAIT, MT_DATA);
! 1165: if (mfrg == 0)
! 1166: return (ENOBUFS);
! 1167: mfrg->m_len = sizeof(struct ip6_frag);
! 1168: *frghdrp = mtod(mfrg, struct ip6_frag *);
! 1169: mlast->m_next = mfrg;
! 1170: }
! 1171:
! 1172: return (0);
! 1173: }
! 1174:
! 1175: static int
! 1176: ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup, alwaysfragp)
! 1177: struct route_in6 *ro_pmtu, *ro;
! 1178: struct ifnet *ifp;
! 1179: struct in6_addr *dst;
! 1180: u_long *mtup;
! 1181: int *alwaysfragp;
! 1182: {
! 1183: u_int32_t mtu = 0;
! 1184: int alwaysfrag = 0;
! 1185: int error = 0;
! 1186:
! 1187: if (ro_pmtu != ro) {
! 1188: /* The first hop and the final destination may differ. */
! 1189: struct sockaddr_in6 *sa6_dst =
! 1190: (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
! 1191: if (ro_pmtu->ro_rt &&
! 1192: ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 ||
! 1193: !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
! 1194: RTFREE(ro_pmtu->ro_rt);
! 1195: ro_pmtu->ro_rt = (struct rtentry *)NULL;
! 1196: }
! 1197: if (ro_pmtu->ro_rt == 0) {
! 1198: bzero(sa6_dst, sizeof(*sa6_dst));
! 1199: sa6_dst->sin6_family = AF_INET6;
! 1200: sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
! 1201: sa6_dst->sin6_addr = *dst;
! 1202:
! 1203: rtalloc((struct route *)ro_pmtu);
! 1204: }
! 1205: }
! 1206: if (ro_pmtu->ro_rt) {
! 1207: u_int32_t ifmtu;
! 1208:
! 1209: if (ifp == NULL)
! 1210: ifp = ro_pmtu->ro_rt->rt_ifp;
! 1211: ifmtu = IN6_LINKMTU(ifp);
! 1212: mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
! 1213: if (mtu == 0)
! 1214: mtu = ifmtu;
! 1215: else if (mtu < IPV6_MMTU) {
! 1216: /*
! 1217: * RFC2460 section 5, last paragraph:
! 1218: * if we record ICMPv6 too big message with
! 1219: * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
! 1220: * or smaller, with fragment header attached.
! 1221: * (fragment header is needed regardless from the
! 1222: * packet size, for translators to identify packets)
! 1223: */
! 1224: alwaysfrag = 1;
! 1225: mtu = IPV6_MMTU;
! 1226: } else if (mtu > ifmtu) {
! 1227: /*
! 1228: * The MTU on the route is larger than the MTU on
! 1229: * the interface! This shouldn't happen, unless the
! 1230: * MTU of the interface has been changed after the
! 1231: * interface was brought up. Change the MTU in the
! 1232: * route to match the interface MTU (as long as the
! 1233: * field isn't locked).
! 1234: */
! 1235: mtu = ifmtu;
! 1236: if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU))
! 1237: ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu;
! 1238: }
! 1239: } else if (ifp) {
! 1240: mtu = IN6_LINKMTU(ifp);
! 1241: } else
! 1242: error = EHOSTUNREACH; /* XXX */
! 1243:
! 1244: *mtup = mtu;
! 1245: if (alwaysfragp)
! 1246: *alwaysfragp = alwaysfrag;
! 1247: return (error);
! 1248: }
! 1249:
! 1250: /*
! 1251: * IP6 socket option processing.
! 1252: */
! 1253: int
! 1254: ip6_ctloutput(op, so, level, optname, mp)
! 1255: int op;
! 1256: struct socket *so;
! 1257: int level, optname;
! 1258: struct mbuf **mp;
! 1259: {
! 1260: int privileged, optdatalen, uproto;
! 1261: void *optdata;
! 1262: struct inpcb *inp = sotoinpcb(so);
! 1263: struct mbuf *m = *mp;
! 1264: int error, optval;
! 1265: int optlen;
! 1266: #ifdef IPSEC
! 1267: struct proc *p = curproc; /* XXX */
! 1268: struct tdb *tdb;
! 1269: struct tdb_ident *tdbip, tdbi;
! 1270: int s;
! 1271: #endif
! 1272:
! 1273: optlen = m ? m->m_len : 0;
! 1274: error = optval = 0;
! 1275:
! 1276: privileged = (inp->inp_socket->so_state & SS_PRIV);
! 1277: uproto = (int)so->so_proto->pr_protocol;
! 1278:
! 1279: if (level == IPPROTO_IPV6) {
! 1280: switch (op) {
! 1281: case PRCO_SETOPT:
! 1282: switch (optname) {
! 1283: case IPV6_2292PKTOPTIONS:
! 1284: {
! 1285: error = ip6_pcbopts(&inp->inp_outputopts6,
! 1286: m, so);
! 1287: break;
! 1288: }
! 1289:
! 1290: /*
! 1291: * Use of some Hop-by-Hop options or some
! 1292: * Destination options, might require special
! 1293: * privilege. That is, normal applications
! 1294: * (without special privilege) might be forbidden
! 1295: * from setting certain options in outgoing packets,
! 1296: * and might never see certain options in received
! 1297: * packets. [RFC 2292 Section 6]
! 1298: * KAME specific note:
! 1299: * KAME prevents non-privileged users from sending or
! 1300: * receiving ANY hbh/dst options in order to avoid
! 1301: * overhead of parsing options in the kernel.
! 1302: */
! 1303: case IPV6_RECVHOPOPTS:
! 1304: case IPV6_RECVDSTOPTS:
! 1305: case IPV6_RECVRTHDRDSTOPTS:
! 1306: if (!privileged) {
! 1307: error = EPERM;
! 1308: break;
! 1309: }
! 1310: /* FALLTHROUGH */
! 1311: case IPV6_UNICAST_HOPS:
! 1312: case IPV6_HOPLIMIT:
! 1313: case IPV6_FAITH:
! 1314:
! 1315: case IPV6_RECVPKTINFO:
! 1316: case IPV6_RECVHOPLIMIT:
! 1317: case IPV6_RECVRTHDR:
! 1318: case IPV6_RECVPATHMTU:
! 1319: case IPV6_RECVTCLASS:
! 1320: case IPV6_V6ONLY:
! 1321: case IPV6_AUTOFLOWLABEL:
! 1322: if (optlen != sizeof(int)) {
! 1323: error = EINVAL;
! 1324: break;
! 1325: }
! 1326: optval = *mtod(m, int *);
! 1327: switch (optname) {
! 1328:
! 1329: case IPV6_UNICAST_HOPS:
! 1330: if (optval < -1 || optval >= 256)
! 1331: error = EINVAL;
! 1332: else {
! 1333: /* -1 = kernel default */
! 1334: inp->inp_hops = optval;
! 1335: }
! 1336: break;
! 1337: #define OPTSET(bit) \
! 1338: do { \
! 1339: if (optval) \
! 1340: inp->inp_flags |= (bit); \
! 1341: else \
! 1342: inp->inp_flags &= ~(bit); \
! 1343: } while (/*CONSTCOND*/ 0)
! 1344: #define OPTSET2292(bit) \
! 1345: do { \
! 1346: inp->inp_flags |= IN6P_RFC2292; \
! 1347: if (optval) \
! 1348: inp->inp_flags |= (bit); \
! 1349: else \
! 1350: inp->inp_flags &= ~(bit); \
! 1351: } while (/*CONSTCOND*/ 0)
! 1352: #define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0)
! 1353:
! 1354: case IPV6_RECVPKTINFO:
! 1355: /* cannot mix with RFC2292 */
! 1356: if (OPTBIT(IN6P_RFC2292)) {
! 1357: error = EINVAL;
! 1358: break;
! 1359: }
! 1360: OPTSET(IN6P_PKTINFO);
! 1361: break;
! 1362:
! 1363: case IPV6_HOPLIMIT:
! 1364: {
! 1365: struct ip6_pktopts **optp;
! 1366:
! 1367: /* cannot mix with RFC2292 */
! 1368: if (OPTBIT(IN6P_RFC2292)) {
! 1369: error = EINVAL;
! 1370: break;
! 1371: }
! 1372: optp = &inp->inp_outputopts6;
! 1373: error = ip6_pcbopt(IPV6_HOPLIMIT,
! 1374: (u_char *)&optval,
! 1375: sizeof(optval),
! 1376: optp,
! 1377: privileged, uproto);
! 1378: break;
! 1379: }
! 1380:
! 1381: case IPV6_RECVHOPLIMIT:
! 1382: /* cannot mix with RFC2292 */
! 1383: if (OPTBIT(IN6P_RFC2292)) {
! 1384: error = EINVAL;
! 1385: break;
! 1386: }
! 1387: OPTSET(IN6P_HOPLIMIT);
! 1388: break;
! 1389:
! 1390: case IPV6_RECVHOPOPTS:
! 1391: /* cannot mix with RFC2292 */
! 1392: if (OPTBIT(IN6P_RFC2292)) {
! 1393: error = EINVAL;
! 1394: break;
! 1395: }
! 1396: OPTSET(IN6P_HOPOPTS);
! 1397: break;
! 1398:
! 1399: case IPV6_RECVDSTOPTS:
! 1400: /* cannot mix with RFC2292 */
! 1401: if (OPTBIT(IN6P_RFC2292)) {
! 1402: error = EINVAL;
! 1403: break;
! 1404: }
! 1405: OPTSET(IN6P_DSTOPTS);
! 1406: break;
! 1407:
! 1408: case IPV6_RECVRTHDRDSTOPTS:
! 1409: /* cannot mix with RFC2292 */
! 1410: if (OPTBIT(IN6P_RFC2292)) {
! 1411: error = EINVAL;
! 1412: break;
! 1413: }
! 1414: OPTSET(IN6P_RTHDRDSTOPTS);
! 1415: break;
! 1416:
! 1417: case IPV6_RECVRTHDR:
! 1418: /* cannot mix with RFC2292 */
! 1419: if (OPTBIT(IN6P_RFC2292)) {
! 1420: error = EINVAL;
! 1421: break;
! 1422: }
! 1423: OPTSET(IN6P_RTHDR);
! 1424: break;
! 1425:
! 1426: case IPV6_FAITH:
! 1427: OPTSET(IN6P_FAITH);
! 1428: break;
! 1429:
! 1430: case IPV6_RECVPATHMTU:
! 1431: /*
! 1432: * We ignore this option for TCP
! 1433: * sockets.
! 1434: * (RFC3542 leaves this case
! 1435: * unspecified.)
! 1436: */
! 1437: if (uproto != IPPROTO_TCP)
! 1438: OPTSET(IN6P_MTU);
! 1439: break;
! 1440:
! 1441: case IPV6_V6ONLY:
! 1442: /*
! 1443: * make setsockopt(IPV6_V6ONLY)
! 1444: * available only prior to bind(2).
! 1445: * see ipng mailing list, Jun 22 2001.
! 1446: */
! 1447: if (inp->inp_lport ||
! 1448: !IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
! 1449: error = EINVAL;
! 1450: break;
! 1451: }
! 1452: if ((ip6_v6only && optval) ||
! 1453: (!ip6_v6only && !optval))
! 1454: error = 0;
! 1455: else
! 1456: error = EINVAL;
! 1457: break;
! 1458: case IPV6_RECVTCLASS:
! 1459: /* cannot mix with RFC2292 XXX */
! 1460: if (OPTBIT(IN6P_RFC2292)) {
! 1461: error = EINVAL;
! 1462: break;
! 1463: }
! 1464: OPTSET(IN6P_TCLASS);
! 1465: break;
! 1466: case IPV6_AUTOFLOWLABEL:
! 1467: OPTSET(IN6P_AUTOFLOWLABEL);
! 1468: break;
! 1469:
! 1470: }
! 1471: break;
! 1472:
! 1473: case IPV6_TCLASS:
! 1474: case IPV6_DONTFRAG:
! 1475: case IPV6_USE_MIN_MTU:
! 1476: if (optlen != sizeof(optval)) {
! 1477: error = EINVAL;
! 1478: break;
! 1479: }
! 1480: optval = *mtod(m, int *);
! 1481: {
! 1482: struct ip6_pktopts **optp;
! 1483: optp = &inp->inp_outputopts6;
! 1484: error = ip6_pcbopt(optname,
! 1485: (u_char *)&optval,
! 1486: sizeof(optval),
! 1487: optp,
! 1488: privileged, uproto);
! 1489: break;
! 1490: }
! 1491:
! 1492: case IPV6_2292PKTINFO:
! 1493: case IPV6_2292HOPLIMIT:
! 1494: case IPV6_2292HOPOPTS:
! 1495: case IPV6_2292DSTOPTS:
! 1496: case IPV6_2292RTHDR:
! 1497: /* RFC 2292 */
! 1498: if (optlen != sizeof(int)) {
! 1499: error = EINVAL;
! 1500: break;
! 1501: }
! 1502: optval = *mtod(m, int *);
! 1503: switch (optname) {
! 1504: case IPV6_2292PKTINFO:
! 1505: OPTSET2292(IN6P_PKTINFO);
! 1506: break;
! 1507: case IPV6_2292HOPLIMIT:
! 1508: OPTSET2292(IN6P_HOPLIMIT);
! 1509: break;
! 1510: case IPV6_2292HOPOPTS:
! 1511: /*
! 1512: * Check super-user privilege.
! 1513: * See comments for IPV6_RECVHOPOPTS.
! 1514: */
! 1515: if (!privileged)
! 1516: return (EPERM);
! 1517: OPTSET2292(IN6P_HOPOPTS);
! 1518: break;
! 1519: case IPV6_2292DSTOPTS:
! 1520: if (!privileged)
! 1521: return (EPERM);
! 1522: OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
! 1523: break;
! 1524: case IPV6_2292RTHDR:
! 1525: OPTSET2292(IN6P_RTHDR);
! 1526: break;
! 1527: }
! 1528: break;
! 1529: case IPV6_PKTINFO:
! 1530: case IPV6_HOPOPTS:
! 1531: case IPV6_RTHDR:
! 1532: case IPV6_DSTOPTS:
! 1533: case IPV6_RTHDRDSTOPTS:
! 1534: case IPV6_NEXTHOP:
! 1535: {
! 1536: /* new advanced API (RFC3542) */
! 1537: u_char *optbuf;
! 1538: int optbuflen;
! 1539: struct ip6_pktopts **optp;
! 1540:
! 1541: /* cannot mix with RFC2292 */
! 1542: if (OPTBIT(IN6P_RFC2292)) {
! 1543: error = EINVAL;
! 1544: break;
! 1545: }
! 1546:
! 1547: if (m && m->m_next) {
! 1548: error = EINVAL; /* XXX */
! 1549: break;
! 1550: }
! 1551: if (m) {
! 1552: optbuf = mtod(m, u_char *);
! 1553: optbuflen = m->m_len;
! 1554: } else {
! 1555: optbuf = NULL;
! 1556: optbuflen = 0;
! 1557: }
! 1558: optp = &inp->inp_outputopts6;
! 1559: error = ip6_pcbopt(optname,
! 1560: optbuf, optbuflen,
! 1561: optp, privileged, uproto);
! 1562: break;
! 1563: }
! 1564: #undef OPTSET
! 1565:
! 1566: case IPV6_MULTICAST_IF:
! 1567: case IPV6_MULTICAST_HOPS:
! 1568: case IPV6_MULTICAST_LOOP:
! 1569: case IPV6_JOIN_GROUP:
! 1570: case IPV6_LEAVE_GROUP:
! 1571: error = ip6_setmoptions(optname,
! 1572: &inp->inp_moptions6,
! 1573: m);
! 1574: break;
! 1575:
! 1576: case IPV6_PORTRANGE:
! 1577: optval = *mtod(m, int *);
! 1578:
! 1579: switch (optval) {
! 1580: case IPV6_PORTRANGE_DEFAULT:
! 1581: inp->inp_flags &= ~(IN6P_LOWPORT);
! 1582: inp->inp_flags &= ~(IN6P_HIGHPORT);
! 1583: break;
! 1584:
! 1585: case IPV6_PORTRANGE_HIGH:
! 1586: inp->inp_flags &= ~(IN6P_LOWPORT);
! 1587: inp->inp_flags |= IN6P_HIGHPORT;
! 1588: break;
! 1589:
! 1590: case IPV6_PORTRANGE_LOW:
! 1591: inp->inp_flags &= ~(IN6P_HIGHPORT);
! 1592: inp->inp_flags |= IN6P_LOWPORT;
! 1593: break;
! 1594:
! 1595: default:
! 1596: error = EINVAL;
! 1597: break;
! 1598: }
! 1599: break;
! 1600:
! 1601: case IPSEC6_OUTSA:
! 1602: #ifndef IPSEC
! 1603: error = EINVAL;
! 1604: #else
! 1605: s = spltdb();
! 1606: if (m == 0 || m->m_len != sizeof(struct tdb_ident)) {
! 1607: error = EINVAL;
! 1608: } else {
! 1609: tdbip = mtod(m, struct tdb_ident *);
! 1610: tdb = gettdb(tdbip->spi, &tdbip->dst,
! 1611: tdbip->proto);
! 1612: if (tdb == NULL)
! 1613: error = ESRCH;
! 1614: else
! 1615: tdb_add_inp(tdb, inp, 0);
! 1616: }
! 1617: splx(s);
! 1618: #endif
! 1619: break;
! 1620:
! 1621: case IPV6_AUTH_LEVEL:
! 1622: case IPV6_ESP_TRANS_LEVEL:
! 1623: case IPV6_ESP_NETWORK_LEVEL:
! 1624: case IPV6_IPCOMP_LEVEL:
! 1625: #ifndef IPSEC
! 1626: error = EINVAL;
! 1627: #else
! 1628: if (m == 0 || m->m_len != sizeof(int)) {
! 1629: error = EINVAL;
! 1630: break;
! 1631: }
! 1632: optval = *mtod(m, int *);
! 1633:
! 1634: if (optval < IPSEC_LEVEL_BYPASS ||
! 1635: optval > IPSEC_LEVEL_UNIQUE) {
! 1636: error = EINVAL;
! 1637: break;
! 1638: }
! 1639:
! 1640: switch (optname) {
! 1641: case IPV6_AUTH_LEVEL:
! 1642: if (optval < ipsec_auth_default_level &&
! 1643: suser(p, 0)) {
! 1644: error = EACCES;
! 1645: break;
! 1646: }
! 1647: inp->inp_seclevel[SL_AUTH] = optval;
! 1648: break;
! 1649:
! 1650: case IPV6_ESP_TRANS_LEVEL:
! 1651: if (optval < ipsec_esp_trans_default_level &&
! 1652: suser(p, 0)) {
! 1653: error = EACCES;
! 1654: break;
! 1655: }
! 1656: inp->inp_seclevel[SL_ESP_TRANS] = optval;
! 1657: break;
! 1658:
! 1659: case IPV6_ESP_NETWORK_LEVEL:
! 1660: if (optval < ipsec_esp_network_default_level &&
! 1661: suser(p, 0)) {
! 1662: error = EACCES;
! 1663: break;
! 1664: }
! 1665: inp->inp_seclevel[SL_ESP_NETWORK] = optval;
! 1666: break;
! 1667:
! 1668: case IPV6_IPCOMP_LEVEL:
! 1669: if (optval < ipsec_ipcomp_default_level &&
! 1670: suser(p, 0)) {
! 1671: error = EACCES;
! 1672: break;
! 1673: }
! 1674: inp->inp_seclevel[SL_IPCOMP] = optval;
! 1675: break;
! 1676: }
! 1677: if (!error)
! 1678: inp->inp_secrequire = get_sa_require(inp);
! 1679: #endif
! 1680: break;
! 1681:
! 1682: default:
! 1683: error = ENOPROTOOPT;
! 1684: break;
! 1685: }
! 1686: if (m)
! 1687: (void)m_free(m);
! 1688: break;
! 1689:
! 1690: case PRCO_GETOPT:
! 1691: switch (optname) {
! 1692:
! 1693: case IPV6_2292PKTOPTIONS:
! 1694: /*
! 1695: * RFC3542 (effectively) deprecated the
! 1696: * semantics of the 2292-style pktoptions.
! 1697: * Since it was not reliable in nature (i.e.,
! 1698: * applications had to expect the lack of some
! 1699: * information after all), it would make sense
! 1700: * to simplify this part by always returning
! 1701: * empty data.
! 1702: */
! 1703: *mp = m_get(M_WAIT, MT_SOOPTS);
! 1704: (*mp)->m_len = 0;
! 1705: break;
! 1706:
! 1707: case IPV6_RECVHOPOPTS:
! 1708: case IPV6_RECVDSTOPTS:
! 1709: case IPV6_RECVRTHDRDSTOPTS:
! 1710: case IPV6_UNICAST_HOPS:
! 1711: case IPV6_RECVPKTINFO:
! 1712: case IPV6_RECVHOPLIMIT:
! 1713: case IPV6_RECVRTHDR:
! 1714: case IPV6_RECVPATHMTU:
! 1715:
! 1716: case IPV6_FAITH:
! 1717: case IPV6_V6ONLY:
! 1718: case IPV6_PORTRANGE:
! 1719: case IPV6_RECVTCLASS:
! 1720: case IPV6_AUTOFLOWLABEL:
! 1721: switch (optname) {
! 1722:
! 1723: case IPV6_RECVHOPOPTS:
! 1724: optval = OPTBIT(IN6P_HOPOPTS);
! 1725: break;
! 1726:
! 1727: case IPV6_RECVDSTOPTS:
! 1728: optval = OPTBIT(IN6P_DSTOPTS);
! 1729: break;
! 1730:
! 1731: case IPV6_RECVRTHDRDSTOPTS:
! 1732: optval = OPTBIT(IN6P_RTHDRDSTOPTS);
! 1733: break;
! 1734:
! 1735: case IPV6_UNICAST_HOPS:
! 1736: optval = inp->inp_hops;
! 1737: break;
! 1738:
! 1739: case IPV6_RECVPKTINFO:
! 1740: optval = OPTBIT(IN6P_PKTINFO);
! 1741: break;
! 1742:
! 1743: case IPV6_RECVHOPLIMIT:
! 1744: optval = OPTBIT(IN6P_HOPLIMIT);
! 1745: break;
! 1746:
! 1747: case IPV6_RECVRTHDR:
! 1748: optval = OPTBIT(IN6P_RTHDR);
! 1749: break;
! 1750:
! 1751: case IPV6_RECVPATHMTU:
! 1752: optval = OPTBIT(IN6P_MTU);
! 1753: break;
! 1754:
! 1755: case IPV6_FAITH:
! 1756: optval = OPTBIT(IN6P_FAITH);
! 1757: break;
! 1758:
! 1759: case IPV6_V6ONLY:
! 1760: optval = (ip6_v6only != 0); /* XXX */
! 1761: break;
! 1762:
! 1763: case IPV6_PORTRANGE:
! 1764: {
! 1765: int flags;
! 1766: flags = inp->inp_flags;
! 1767: if (flags & IN6P_HIGHPORT)
! 1768: optval = IPV6_PORTRANGE_HIGH;
! 1769: else if (flags & IN6P_LOWPORT)
! 1770: optval = IPV6_PORTRANGE_LOW;
! 1771: else
! 1772: optval = 0;
! 1773: break;
! 1774: }
! 1775: case IPV6_RECVTCLASS:
! 1776: optval = OPTBIT(IN6P_TCLASS);
! 1777: break;
! 1778:
! 1779: case IPV6_AUTOFLOWLABEL:
! 1780: optval = OPTBIT(IN6P_AUTOFLOWLABEL);
! 1781: break;
! 1782: }
! 1783: if (error)
! 1784: break;
! 1785: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 1786: m->m_len = sizeof(int);
! 1787: *mtod(m, int *) = optval;
! 1788: break;
! 1789:
! 1790: case IPV6_PATHMTU:
! 1791: {
! 1792: u_long pmtu = 0;
! 1793: struct ip6_mtuinfo mtuinfo;
! 1794: struct route_in6 *ro = (struct route_in6 *)&inp->inp_route6;
! 1795:
! 1796: if (!(so->so_state & SS_ISCONNECTED))
! 1797: return (ENOTCONN);
! 1798: /*
! 1799: * XXX: we dot not consider the case of source
! 1800: * routing, or optional information to specify
! 1801: * the outgoing interface.
! 1802: */
! 1803: error = ip6_getpmtu(ro, NULL, NULL,
! 1804: &inp->inp_faddr6, &pmtu, NULL);
! 1805: if (error)
! 1806: break;
! 1807: if (pmtu > IPV6_MAXPACKET)
! 1808: pmtu = IPV6_MAXPACKET;
! 1809:
! 1810: bzero(&mtuinfo, sizeof(mtuinfo));
! 1811: mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
! 1812: optdata = (void *)&mtuinfo;
! 1813: optdatalen = sizeof(mtuinfo);
! 1814: if (optdatalen > MCLBYTES)
! 1815: return (EMSGSIZE); /* XXX */
! 1816: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 1817: if (optdatalen > MLEN)
! 1818: MCLGET(m, M_WAIT);
! 1819: m->m_len = optdatalen;
! 1820: bcopy(optdata, mtod(m, void *), optdatalen);
! 1821: break;
! 1822: }
! 1823:
! 1824: case IPV6_2292PKTINFO:
! 1825: case IPV6_2292HOPLIMIT:
! 1826: case IPV6_2292HOPOPTS:
! 1827: case IPV6_2292RTHDR:
! 1828: case IPV6_2292DSTOPTS:
! 1829: switch (optname) {
! 1830: case IPV6_2292PKTINFO:
! 1831: optval = OPTBIT(IN6P_PKTINFO);
! 1832: break;
! 1833: case IPV6_2292HOPLIMIT:
! 1834: optval = OPTBIT(IN6P_HOPLIMIT);
! 1835: break;
! 1836: case IPV6_2292HOPOPTS:
! 1837: optval = OPTBIT(IN6P_HOPOPTS);
! 1838: break;
! 1839: case IPV6_2292RTHDR:
! 1840: optval = OPTBIT(IN6P_RTHDR);
! 1841: break;
! 1842: case IPV6_2292DSTOPTS:
! 1843: optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
! 1844: break;
! 1845: }
! 1846: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 1847: m->m_len = sizeof(int);
! 1848: *mtod(m, int *) = optval;
! 1849: break;
! 1850: case IPV6_PKTINFO:
! 1851: case IPV6_HOPOPTS:
! 1852: case IPV6_RTHDR:
! 1853: case IPV6_DSTOPTS:
! 1854: case IPV6_RTHDRDSTOPTS:
! 1855: case IPV6_NEXTHOP:
! 1856: case IPV6_TCLASS:
! 1857: case IPV6_DONTFRAG:
! 1858: case IPV6_USE_MIN_MTU:
! 1859: error = ip6_getpcbopt(inp->inp_outputopts6,
! 1860: optname, mp);
! 1861: break;
! 1862:
! 1863: case IPV6_MULTICAST_IF:
! 1864: case IPV6_MULTICAST_HOPS:
! 1865: case IPV6_MULTICAST_LOOP:
! 1866: case IPV6_JOIN_GROUP:
! 1867: case IPV6_LEAVE_GROUP:
! 1868: error = ip6_getmoptions(optname,
! 1869: inp->inp_moptions6, mp);
! 1870: break;
! 1871:
! 1872: case IPSEC6_OUTSA:
! 1873: #ifndef IPSEC
! 1874: error = EINVAL;
! 1875: #else
! 1876: s = spltdb();
! 1877: if (inp->inp_tdb_out == NULL) {
! 1878: error = ENOENT;
! 1879: } else {
! 1880: tdbi.spi = inp->inp_tdb_out->tdb_spi;
! 1881: tdbi.dst = inp->inp_tdb_out->tdb_dst;
! 1882: tdbi.proto = inp->inp_tdb_out->tdb_sproto;
! 1883: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 1884: m->m_len = sizeof(tdbi);
! 1885: bcopy((caddr_t)&tdbi, mtod(m, caddr_t),
! 1886: (unsigned)m->m_len);
! 1887: }
! 1888: splx(s);
! 1889: #endif
! 1890: break;
! 1891:
! 1892: case IPV6_AUTH_LEVEL:
! 1893: case IPV6_ESP_TRANS_LEVEL:
! 1894: case IPV6_ESP_NETWORK_LEVEL:
! 1895: case IPV6_IPCOMP_LEVEL:
! 1896: #ifndef IPSEC
! 1897: m->m_len = sizeof(int);
! 1898: *mtod(m, int *) = IPSEC_LEVEL_NONE;
! 1899: #else
! 1900: m->m_len = sizeof(int);
! 1901: switch (optname) {
! 1902: case IPV6_AUTH_LEVEL:
! 1903: optval = inp->inp_seclevel[SL_AUTH];
! 1904: break;
! 1905:
! 1906: case IPV6_ESP_TRANS_LEVEL:
! 1907: optval =
! 1908: inp->inp_seclevel[SL_ESP_TRANS];
! 1909: break;
! 1910:
! 1911: case IPV6_ESP_NETWORK_LEVEL:
! 1912: optval =
! 1913: inp->inp_seclevel[SL_ESP_NETWORK];
! 1914: break;
! 1915:
! 1916: case IPV6_IPCOMP_LEVEL:
! 1917: optval = inp->inp_seclevel[SL_IPCOMP];
! 1918: break;
! 1919: }
! 1920: *mtod(m, int *) = optval;
! 1921: #endif
! 1922: break;
! 1923:
! 1924: default:
! 1925: error = ENOPROTOOPT;
! 1926: break;
! 1927: }
! 1928: break;
! 1929: }
! 1930: } else {
! 1931: error = EINVAL;
! 1932: if (op == PRCO_SETOPT && *mp)
! 1933: (void)m_free(*mp);
! 1934: }
! 1935: return (error);
! 1936: }
! 1937:
! 1938: int
! 1939: ip6_raw_ctloutput(op, so, level, optname, mp)
! 1940: int op;
! 1941: struct socket *so;
! 1942: int level, optname;
! 1943: struct mbuf **mp;
! 1944: {
! 1945: int error = 0, optval, optlen;
! 1946: const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
! 1947: struct inpcb *inp = sotoinpcb(so);
! 1948: struct mbuf *m = *mp;
! 1949:
! 1950: optlen = m ? m->m_len : 0;
! 1951:
! 1952: if (level != IPPROTO_IPV6) {
! 1953: if (op == PRCO_SETOPT && *mp)
! 1954: (void)m_free(*mp);
! 1955: return (EINVAL);
! 1956: }
! 1957:
! 1958: switch (optname) {
! 1959: case IPV6_CHECKSUM:
! 1960: /*
! 1961: * For ICMPv6 sockets, no modification allowed for checksum
! 1962: * offset, permit "no change" values to help existing apps.
! 1963: *
! 1964: * RFC3542 says: "An attempt to set IPV6_CHECKSUM
! 1965: * for an ICMPv6 socket will fail."
! 1966: * The current behavior does not meet RFC3542.
! 1967: */
! 1968: switch (op) {
! 1969: case PRCO_SETOPT:
! 1970: if (optlen != sizeof(int)) {
! 1971: error = EINVAL;
! 1972: break;
! 1973: }
! 1974: optval = *mtod(m, int *);
! 1975: if ((optval % 2) != 0) {
! 1976: /* the API assumes even offset values */
! 1977: error = EINVAL;
! 1978: } else if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) {
! 1979: if (optval != icmp6off)
! 1980: error = EINVAL;
! 1981: } else
! 1982: inp->in6p_cksum = optval;
! 1983: break;
! 1984:
! 1985: case PRCO_GETOPT:
! 1986: if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
! 1987: optval = icmp6off;
! 1988: else
! 1989: optval = inp->in6p_cksum;
! 1990:
! 1991: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 1992: m->m_len = sizeof(int);
! 1993: *mtod(m, int *) = optval;
! 1994: break;
! 1995:
! 1996: default:
! 1997: error = EINVAL;
! 1998: break;
! 1999: }
! 2000: break;
! 2001:
! 2002: default:
! 2003: error = ENOPROTOOPT;
! 2004: break;
! 2005: }
! 2006:
! 2007: if (op == PRCO_SETOPT && m)
! 2008: (void)m_free(m);
! 2009:
! 2010: return (error);
! 2011: }
! 2012:
! 2013: /*
! 2014: * Set up IP6 options in pcb for insertion in output packets.
! 2015: * Store in mbuf with pointer in pcbopt, adding pseudo-option
! 2016: * with destination address if source routed.
! 2017: */
! 2018: static int
! 2019: ip6_pcbopts(pktopt, m, so)
! 2020: struct ip6_pktopts **pktopt;
! 2021: struct mbuf *m;
! 2022: struct socket *so;
! 2023: {
! 2024: struct ip6_pktopts *opt = *pktopt;
! 2025: int error = 0;
! 2026: struct proc *p = curproc; /* XXX */
! 2027: int priv = 0;
! 2028:
! 2029: /* turn off any old options. */
! 2030: if (opt)
! 2031: ip6_clearpktopts(opt, -1);
! 2032: else
! 2033: opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
! 2034: *pktopt = 0;
! 2035:
! 2036: if (!m || m->m_len == 0) {
! 2037: /*
! 2038: * Only turning off any previous options, regardless of
! 2039: * whether the opt is just created or given.
! 2040: */
! 2041: free(opt, M_IP6OPT);
! 2042: return (0);
! 2043: }
! 2044:
! 2045: /* set options specified by user. */
! 2046: if (p && !suser(p, 0))
! 2047: priv = 1;
! 2048: if ((error = ip6_setpktopts(m, opt, NULL, priv,
! 2049: so->so_proto->pr_protocol)) != 0) {
! 2050: ip6_clearpktopts(opt, -1); /* XXX discard all options */
! 2051: free(opt, M_IP6OPT);
! 2052: return (error);
! 2053: }
! 2054: *pktopt = opt;
! 2055: return (0);
! 2056: }
! 2057:
! 2058: /*
! 2059: * initialize ip6_pktopts. beware that there are non-zero default values in
! 2060: * the struct.
! 2061: */
! 2062: void
! 2063: ip6_initpktopts(opt)
! 2064: struct ip6_pktopts *opt;
! 2065: {
! 2066:
! 2067: bzero(opt, sizeof(*opt));
! 2068: opt->ip6po_hlim = -1; /* -1 means default hop limit */
! 2069: opt->ip6po_tclass = -1; /* -1 means default traffic class */
! 2070: opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
! 2071: }
! 2072:
! 2073: #define sin6tosa(sin6) ((struct sockaddr *)(sin6)) /* XXX */
! 2074: static int
! 2075: ip6_pcbopt(optname, buf, len, pktopt, priv, uproto)
! 2076: int optname, len, priv;
! 2077: u_char *buf;
! 2078: struct ip6_pktopts **pktopt;
! 2079: int uproto;
! 2080: {
! 2081: struct ip6_pktopts *opt;
! 2082:
! 2083: if (*pktopt == NULL) {
! 2084: *pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
! 2085: M_WAITOK);
! 2086: ip6_initpktopts(*pktopt);
! 2087: }
! 2088: opt = *pktopt;
! 2089:
! 2090: return (ip6_setpktopt(optname, buf, len, opt, priv, 1, 0, uproto));
! 2091: }
! 2092:
! 2093: static int
! 2094: ip6_getpcbopt(pktopt, optname, mp)
! 2095: struct ip6_pktopts *pktopt;
! 2096: int optname;
! 2097: struct mbuf **mp;
! 2098: {
! 2099: void *optdata = NULL;
! 2100: int optdatalen = 0;
! 2101: struct ip6_ext *ip6e;
! 2102: int error = 0;
! 2103: struct in6_pktinfo null_pktinfo;
! 2104: int deftclass = 0, on;
! 2105: int defminmtu = IP6PO_MINMTU_MCASTONLY;
! 2106: struct mbuf *m;
! 2107:
! 2108: switch (optname) {
! 2109: case IPV6_PKTINFO:
! 2110: if (pktopt && pktopt->ip6po_pktinfo)
! 2111: optdata = (void *)pktopt->ip6po_pktinfo;
! 2112: else {
! 2113: /* XXX: we don't have to do this every time... */
! 2114: bzero(&null_pktinfo, sizeof(null_pktinfo));
! 2115: optdata = (void *)&null_pktinfo;
! 2116: }
! 2117: optdatalen = sizeof(struct in6_pktinfo);
! 2118: break;
! 2119: case IPV6_TCLASS:
! 2120: if (pktopt && pktopt->ip6po_tclass >= 0)
! 2121: optdata = (void *)&pktopt->ip6po_tclass;
! 2122: else
! 2123: optdata = (void *)&deftclass;
! 2124: optdatalen = sizeof(int);
! 2125: break;
! 2126: case IPV6_HOPOPTS:
! 2127: if (pktopt && pktopt->ip6po_hbh) {
! 2128: optdata = (void *)pktopt->ip6po_hbh;
! 2129: ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
! 2130: optdatalen = (ip6e->ip6e_len + 1) << 3;
! 2131: }
! 2132: break;
! 2133: case IPV6_RTHDR:
! 2134: if (pktopt && pktopt->ip6po_rthdr) {
! 2135: optdata = (void *)pktopt->ip6po_rthdr;
! 2136: ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
! 2137: optdatalen = (ip6e->ip6e_len + 1) << 3;
! 2138: }
! 2139: break;
! 2140: case IPV6_RTHDRDSTOPTS:
! 2141: if (pktopt && pktopt->ip6po_dest1) {
! 2142: optdata = (void *)pktopt->ip6po_dest1;
! 2143: ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
! 2144: optdatalen = (ip6e->ip6e_len + 1) << 3;
! 2145: }
! 2146: break;
! 2147: case IPV6_DSTOPTS:
! 2148: if (pktopt && pktopt->ip6po_dest2) {
! 2149: optdata = (void *)pktopt->ip6po_dest2;
! 2150: ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
! 2151: optdatalen = (ip6e->ip6e_len + 1) << 3;
! 2152: }
! 2153: break;
! 2154: case IPV6_NEXTHOP:
! 2155: if (pktopt && pktopt->ip6po_nexthop) {
! 2156: optdata = (void *)pktopt->ip6po_nexthop;
! 2157: optdatalen = pktopt->ip6po_nexthop->sa_len;
! 2158: }
! 2159: break;
! 2160: case IPV6_USE_MIN_MTU:
! 2161: if (pktopt)
! 2162: optdata = (void *)&pktopt->ip6po_minmtu;
! 2163: else
! 2164: optdata = (void *)&defminmtu;
! 2165: optdatalen = sizeof(int);
! 2166: break;
! 2167: case IPV6_DONTFRAG:
! 2168: if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
! 2169: on = 1;
! 2170: else
! 2171: on = 0;
! 2172: optdata = (void *)&on;
! 2173: optdatalen = sizeof(on);
! 2174: break;
! 2175: default: /* should not happen */
! 2176: #ifdef DIAGNOSTIC
! 2177: panic("ip6_getpcbopt: unexpected option\n");
! 2178: #endif
! 2179: return (ENOPROTOOPT);
! 2180: }
! 2181:
! 2182: if (optdatalen > MCLBYTES)
! 2183: return (EMSGSIZE); /* XXX */
! 2184: *mp = m = m_get(M_WAIT, MT_SOOPTS);
! 2185: if (optdatalen > MLEN)
! 2186: MCLGET(m, M_WAIT);
! 2187: m->m_len = optdatalen;
! 2188: if (optdatalen)
! 2189: bcopy(optdata, mtod(m, void *), optdatalen);
! 2190:
! 2191: return (error);
! 2192: }
! 2193:
! 2194: void
! 2195: ip6_clearpktopts(pktopt, optname)
! 2196: struct ip6_pktopts *pktopt;
! 2197: int optname;
! 2198: {
! 2199: if (optname == -1 || optname == IPV6_PKTINFO) {
! 2200: if (pktopt->ip6po_pktinfo)
! 2201: free(pktopt->ip6po_pktinfo, M_IP6OPT);
! 2202: pktopt->ip6po_pktinfo = NULL;
! 2203: }
! 2204: if (optname == -1 || optname == IPV6_HOPLIMIT)
! 2205: pktopt->ip6po_hlim = -1;
! 2206: if (optname == -1 || optname == IPV6_TCLASS)
! 2207: pktopt->ip6po_tclass = -1;
! 2208: if (optname == -1 || optname == IPV6_NEXTHOP) {
! 2209: if (pktopt->ip6po_nextroute.ro_rt) {
! 2210: RTFREE(pktopt->ip6po_nextroute.ro_rt);
! 2211: pktopt->ip6po_nextroute.ro_rt = NULL;
! 2212: }
! 2213: if (pktopt->ip6po_nexthop)
! 2214: free(pktopt->ip6po_nexthop, M_IP6OPT);
! 2215: pktopt->ip6po_nexthop = NULL;
! 2216: }
! 2217: if (optname == -1 || optname == IPV6_HOPOPTS) {
! 2218: if (pktopt->ip6po_hbh)
! 2219: free(pktopt->ip6po_hbh, M_IP6OPT);
! 2220: pktopt->ip6po_hbh = NULL;
! 2221: }
! 2222: if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
! 2223: if (pktopt->ip6po_dest1)
! 2224: free(pktopt->ip6po_dest1, M_IP6OPT);
! 2225: pktopt->ip6po_dest1 = NULL;
! 2226: }
! 2227: if (optname == -1 || optname == IPV6_RTHDR) {
! 2228: if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
! 2229: free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
! 2230: pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
! 2231: if (pktopt->ip6po_route.ro_rt) {
! 2232: RTFREE(pktopt->ip6po_route.ro_rt);
! 2233: pktopt->ip6po_route.ro_rt = NULL;
! 2234: }
! 2235: }
! 2236: if (optname == -1 || optname == IPV6_DSTOPTS) {
! 2237: if (pktopt->ip6po_dest2)
! 2238: free(pktopt->ip6po_dest2, M_IP6OPT);
! 2239: pktopt->ip6po_dest2 = NULL;
! 2240: }
! 2241: }
! 2242:
! 2243: #define PKTOPT_EXTHDRCPY(type) \
! 2244: do {\
! 2245: if (src->type) {\
! 2246: int hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\
! 2247: dst->type = malloc(hlen, M_IP6OPT, canwait);\
! 2248: if (dst->type == NULL && canwait == M_NOWAIT)\
! 2249: goto bad;\
! 2250: bcopy(src->type, dst->type, hlen);\
! 2251: }\
! 2252: } while (/*CONSTCOND*/ 0)
! 2253:
! 2254: static int
! 2255: copypktopts(dst, src, canwait)
! 2256: struct ip6_pktopts *dst, *src;
! 2257: int canwait;
! 2258: {
! 2259: dst->ip6po_hlim = src->ip6po_hlim;
! 2260: dst->ip6po_tclass = src->ip6po_tclass;
! 2261: dst->ip6po_flags = src->ip6po_flags;
! 2262: if (src->ip6po_pktinfo) {
! 2263: dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
! 2264: M_IP6OPT, canwait);
! 2265: if (dst->ip6po_pktinfo == NULL && canwait == M_NOWAIT)
! 2266: goto bad;
! 2267: *dst->ip6po_pktinfo = *src->ip6po_pktinfo;
! 2268: }
! 2269: if (src->ip6po_nexthop) {
! 2270: dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len,
! 2271: M_IP6OPT, canwait);
! 2272: if (dst->ip6po_nexthop == NULL && canwait == M_NOWAIT)
! 2273: goto bad;
! 2274: bcopy(src->ip6po_nexthop, dst->ip6po_nexthop,
! 2275: src->ip6po_nexthop->sa_len);
! 2276: }
! 2277: PKTOPT_EXTHDRCPY(ip6po_hbh);
! 2278: PKTOPT_EXTHDRCPY(ip6po_dest1);
! 2279: PKTOPT_EXTHDRCPY(ip6po_dest2);
! 2280: PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */
! 2281: return (0);
! 2282:
! 2283: bad:
! 2284: if (dst->ip6po_pktinfo) free(dst->ip6po_pktinfo, M_IP6OPT);
! 2285: if (dst->ip6po_nexthop) free(dst->ip6po_nexthop, M_IP6OPT);
! 2286: if (dst->ip6po_hbh) free(dst->ip6po_hbh, M_IP6OPT);
! 2287: if (dst->ip6po_dest1) free(dst->ip6po_dest1, M_IP6OPT);
! 2288: if (dst->ip6po_dest2) free(dst->ip6po_dest2, M_IP6OPT);
! 2289: if (dst->ip6po_rthdr) free(dst->ip6po_rthdr, M_IP6OPT);
! 2290:
! 2291: return (ENOBUFS);
! 2292: }
! 2293: #undef PKTOPT_EXTHDRCPY
! 2294:
! 2295: struct ip6_pktopts *
! 2296: ip6_copypktopts(src, canwait)
! 2297: struct ip6_pktopts *src;
! 2298: int canwait;
! 2299: {
! 2300: int error;
! 2301: struct ip6_pktopts *dst;
! 2302:
! 2303: dst = malloc(sizeof(*dst), M_IP6OPT, canwait);
! 2304: if (dst == NULL && canwait == M_NOWAIT)
! 2305: return (NULL);
! 2306: ip6_initpktopts(dst);
! 2307:
! 2308: if ((error = copypktopts(dst, src, canwait)) != 0) {
! 2309: free(dst, M_IP6OPT);
! 2310: return (NULL);
! 2311: }
! 2312:
! 2313: return (dst);
! 2314: }
! 2315:
! 2316: void
! 2317: ip6_freepcbopts(pktopt)
! 2318: struct ip6_pktopts *pktopt;
! 2319: {
! 2320: if (pktopt == NULL)
! 2321: return;
! 2322:
! 2323: ip6_clearpktopts(pktopt, -1);
! 2324:
! 2325: free(pktopt, M_IP6OPT);
! 2326: }
! 2327:
! 2328: /*
! 2329: * Set the IP6 multicast options in response to user setsockopt().
! 2330: */
! 2331: static int
! 2332: ip6_setmoptions(optname, im6op, m)
! 2333: int optname;
! 2334: struct ip6_moptions **im6op;
! 2335: struct mbuf *m;
! 2336: {
! 2337: int error = 0;
! 2338: u_int loop, ifindex;
! 2339: struct ipv6_mreq *mreq;
! 2340: struct ifnet *ifp;
! 2341: struct ip6_moptions *im6o = *im6op;
! 2342: struct route_in6 ro;
! 2343: struct sockaddr_in6 *dst;
! 2344: struct in6_multi_mship *imm;
! 2345: struct proc *p = curproc; /* XXX */
! 2346:
! 2347: if (im6o == NULL) {
! 2348: /*
! 2349: * No multicast option buffer attached to the pcb;
! 2350: * allocate one and initialize to default values.
! 2351: */
! 2352: im6o = (struct ip6_moptions *)
! 2353: malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK);
! 2354:
! 2355: if (im6o == NULL)
! 2356: return (ENOBUFS);
! 2357: *im6op = im6o;
! 2358: im6o->im6o_multicast_ifp = NULL;
! 2359: im6o->im6o_multicast_hlim = ip6_defmcasthlim;
! 2360: im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
! 2361: LIST_INIT(&im6o->im6o_memberships);
! 2362: }
! 2363:
! 2364: switch (optname) {
! 2365:
! 2366: case IPV6_MULTICAST_IF:
! 2367: /*
! 2368: * Select the interface for outgoing multicast packets.
! 2369: */
! 2370: if (m == NULL || m->m_len != sizeof(u_int)) {
! 2371: error = EINVAL;
! 2372: break;
! 2373: }
! 2374: bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex));
! 2375: if (ifindex == 0)
! 2376: ifp = NULL;
! 2377: else {
! 2378: if (ifindex < 0 || if_indexlim <= ifindex ||
! 2379: !ifindex2ifnet[ifindex]) {
! 2380: error = ENXIO; /* XXX EINVAL? */
! 2381: break;
! 2382: }
! 2383: ifp = ifindex2ifnet[ifindex];
! 2384: if (ifp == NULL ||
! 2385: (ifp->if_flags & IFF_MULTICAST) == 0) {
! 2386: error = EADDRNOTAVAIL;
! 2387: break;
! 2388: }
! 2389: }
! 2390: im6o->im6o_multicast_ifp = ifp;
! 2391: break;
! 2392:
! 2393: case IPV6_MULTICAST_HOPS:
! 2394: {
! 2395: /*
! 2396: * Set the IP6 hoplimit for outgoing multicast packets.
! 2397: */
! 2398: int optval;
! 2399: if (m == NULL || m->m_len != sizeof(int)) {
! 2400: error = EINVAL;
! 2401: break;
! 2402: }
! 2403: bcopy(mtod(m, u_int *), &optval, sizeof(optval));
! 2404: if (optval < -1 || optval >= 256)
! 2405: error = EINVAL;
! 2406: else if (optval == -1)
! 2407: im6o->im6o_multicast_hlim = ip6_defmcasthlim;
! 2408: else
! 2409: im6o->im6o_multicast_hlim = optval;
! 2410: break;
! 2411: }
! 2412:
! 2413: case IPV6_MULTICAST_LOOP:
! 2414: /*
! 2415: * Set the loopback flag for outgoing multicast packets.
! 2416: * Must be zero or one.
! 2417: */
! 2418: if (m == NULL || m->m_len != sizeof(u_int)) {
! 2419: error = EINVAL;
! 2420: break;
! 2421: }
! 2422: bcopy(mtod(m, u_int *), &loop, sizeof(loop));
! 2423: if (loop > 1) {
! 2424: error = EINVAL;
! 2425: break;
! 2426: }
! 2427: im6o->im6o_multicast_loop = loop;
! 2428: break;
! 2429:
! 2430: case IPV6_JOIN_GROUP:
! 2431: /*
! 2432: * Add a multicast group membership.
! 2433: * Group must be a valid IP6 multicast address.
! 2434: */
! 2435: if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
! 2436: error = EINVAL;
! 2437: break;
! 2438: }
! 2439: mreq = mtod(m, struct ipv6_mreq *);
! 2440: if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
! 2441: /*
! 2442: * We use the unspecified address to specify to accept
! 2443: * all multicast addresses. Only super user is allowed
! 2444: * to do this.
! 2445: */
! 2446: if (suser(p, 0))
! 2447: {
! 2448: error = EACCES;
! 2449: break;
! 2450: }
! 2451: } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
! 2452: error = EINVAL;
! 2453: break;
! 2454: }
! 2455:
! 2456: /*
! 2457: * If no interface was explicitly specified, choose an
! 2458: * appropriate one according to the given multicast address.
! 2459: */
! 2460: if (mreq->ipv6mr_interface == 0) {
! 2461: /*
! 2462: * Look up the routing table for the
! 2463: * address, and choose the outgoing interface.
! 2464: * XXX: is it a good approach?
! 2465: */
! 2466: ro.ro_rt = NULL;
! 2467: dst = (struct sockaddr_in6 *)&ro.ro_dst;
! 2468: bzero(dst, sizeof(*dst));
! 2469: dst->sin6_len = sizeof(struct sockaddr_in6);
! 2470: dst->sin6_family = AF_INET6;
! 2471: dst->sin6_addr = mreq->ipv6mr_multiaddr;
! 2472: rtalloc((struct route *)&ro);
! 2473: if (ro.ro_rt == NULL) {
! 2474: error = EADDRNOTAVAIL;
! 2475: break;
! 2476: }
! 2477: ifp = ro.ro_rt->rt_ifp;
! 2478: rtfree(ro.ro_rt);
! 2479: } else {
! 2480: /*
! 2481: * If the interface is specified, validate it.
! 2482: */
! 2483: if (mreq->ipv6mr_interface < 0 ||
! 2484: if_indexlim <= mreq->ipv6mr_interface ||
! 2485: !ifindex2ifnet[mreq->ipv6mr_interface]) {
! 2486: error = ENXIO; /* XXX EINVAL? */
! 2487: break;
! 2488: }
! 2489: ifp = ifindex2ifnet[mreq->ipv6mr_interface];
! 2490: }
! 2491:
! 2492: /*
! 2493: * See if we found an interface, and confirm that it
! 2494: * supports multicast
! 2495: */
! 2496: if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
! 2497: error = EADDRNOTAVAIL;
! 2498: break;
! 2499: }
! 2500: /*
! 2501: * Put interface index into the multicast address,
! 2502: * if the address has link/interface-local scope.
! 2503: */
! 2504: if (IN6_IS_SCOPE_EMBED(&mreq->ipv6mr_multiaddr)) {
! 2505: mreq->ipv6mr_multiaddr.s6_addr16[1] =
! 2506: htons(ifp->if_index);
! 2507: }
! 2508: /*
! 2509: * See if the membership already exists.
! 2510: */
! 2511: LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain)
! 2512: if (imm->i6mm_maddr->in6m_ifp == ifp &&
! 2513: IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
! 2514: &mreq->ipv6mr_multiaddr))
! 2515: break;
! 2516: if (imm != NULL) {
! 2517: error = EADDRINUSE;
! 2518: break;
! 2519: }
! 2520: /*
! 2521: * Everything looks good; add a new record to the multicast
! 2522: * address list for the given interface.
! 2523: */
! 2524: imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error);
! 2525: if (!imm)
! 2526: break;
! 2527: LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
! 2528: break;
! 2529:
! 2530: case IPV6_LEAVE_GROUP:
! 2531: /*
! 2532: * Drop a multicast group membership.
! 2533: * Group must be a valid IP6 multicast address.
! 2534: */
! 2535: if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) {
! 2536: error = EINVAL;
! 2537: break;
! 2538: }
! 2539: mreq = mtod(m, struct ipv6_mreq *);
! 2540: if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
! 2541: if (suser(p, 0))
! 2542: {
! 2543: error = EACCES;
! 2544: break;
! 2545: }
! 2546: } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) {
! 2547: error = EINVAL;
! 2548: break;
! 2549: }
! 2550: /*
! 2551: * If an interface address was specified, get a pointer
! 2552: * to its ifnet structure.
! 2553: */
! 2554: if (mreq->ipv6mr_interface == 0)
! 2555: ifp = NULL;
! 2556: else {
! 2557: if (mreq->ipv6mr_interface < 0 ||
! 2558: if_indexlim <= mreq->ipv6mr_interface ||
! 2559: !ifindex2ifnet[mreq->ipv6mr_interface]) {
! 2560: error = ENXIO; /* XXX EINVAL? */
! 2561: break;
! 2562: }
! 2563: ifp = ifindex2ifnet[mreq->ipv6mr_interface];
! 2564: }
! 2565:
! 2566: /*
! 2567: * Put interface index into the multicast address,
! 2568: * if the address has link-local scope.
! 2569: */
! 2570: if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) {
! 2571: mreq->ipv6mr_multiaddr.s6_addr16[1] =
! 2572: htons(mreq->ipv6mr_interface);
! 2573: }
! 2574: /*
! 2575: * Find the membership in the membership list.
! 2576: */
! 2577: LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) {
! 2578: if ((ifp == NULL || imm->i6mm_maddr->in6m_ifp == ifp) &&
! 2579: IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr,
! 2580: &mreq->ipv6mr_multiaddr))
! 2581: break;
! 2582: }
! 2583: if (imm == NULL) {
! 2584: /* Unable to resolve interface */
! 2585: error = EADDRNOTAVAIL;
! 2586: break;
! 2587: }
! 2588: /*
! 2589: * Give up the multicast address record to which the
! 2590: * membership points.
! 2591: */
! 2592: LIST_REMOVE(imm, i6mm_chain);
! 2593: in6_leavegroup(imm);
! 2594: break;
! 2595:
! 2596: default:
! 2597: error = EOPNOTSUPP;
! 2598: break;
! 2599: }
! 2600:
! 2601: /*
! 2602: * If all options have default values, no need to keep the option
! 2603: * structure.
! 2604: */
! 2605: if (im6o->im6o_multicast_ifp == NULL &&
! 2606: im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
! 2607: im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
! 2608: LIST_EMPTY(&im6o->im6o_memberships)) {
! 2609: free(*im6op, M_IPMOPTS);
! 2610: *im6op = NULL;
! 2611: }
! 2612:
! 2613: return (error);
! 2614: }
! 2615:
! 2616: /*
! 2617: * Return the IP6 multicast options in response to user getsockopt().
! 2618: */
! 2619: static int
! 2620: ip6_getmoptions(optname, im6o, mp)
! 2621: int optname;
! 2622: struct ip6_moptions *im6o;
! 2623: struct mbuf **mp;
! 2624: {
! 2625: u_int *hlim, *loop, *ifindex;
! 2626:
! 2627: *mp = m_get(M_WAIT, MT_SOOPTS);
! 2628:
! 2629: switch (optname) {
! 2630:
! 2631: case IPV6_MULTICAST_IF:
! 2632: ifindex = mtod(*mp, u_int *);
! 2633: (*mp)->m_len = sizeof(u_int);
! 2634: if (im6o == NULL || im6o->im6o_multicast_ifp == NULL)
! 2635: *ifindex = 0;
! 2636: else
! 2637: *ifindex = im6o->im6o_multicast_ifp->if_index;
! 2638: return (0);
! 2639:
! 2640: case IPV6_MULTICAST_HOPS:
! 2641: hlim = mtod(*mp, u_int *);
! 2642: (*mp)->m_len = sizeof(u_int);
! 2643: if (im6o == NULL)
! 2644: *hlim = ip6_defmcasthlim;
! 2645: else
! 2646: *hlim = im6o->im6o_multicast_hlim;
! 2647: return (0);
! 2648:
! 2649: case IPV6_MULTICAST_LOOP:
! 2650: loop = mtod(*mp, u_int *);
! 2651: (*mp)->m_len = sizeof(u_int);
! 2652: if (im6o == NULL)
! 2653: *loop = ip6_defmcasthlim;
! 2654: else
! 2655: *loop = im6o->im6o_multicast_loop;
! 2656: return (0);
! 2657:
! 2658: default:
! 2659: return (EOPNOTSUPP);
! 2660: }
! 2661: }
! 2662:
! 2663: /*
! 2664: * Discard the IP6 multicast options.
! 2665: */
! 2666: void
! 2667: ip6_freemoptions(im6o)
! 2668: struct ip6_moptions *im6o;
! 2669: {
! 2670: struct in6_multi_mship *imm;
! 2671:
! 2672: if (im6o == NULL)
! 2673: return;
! 2674:
! 2675: while (!LIST_EMPTY(&im6o->im6o_memberships)) {
! 2676: imm = LIST_FIRST(&im6o->im6o_memberships);
! 2677: LIST_REMOVE(imm, i6mm_chain);
! 2678: in6_leavegroup(imm);
! 2679: }
! 2680: free(im6o, M_IPMOPTS);
! 2681: }
! 2682:
! 2683: /*
! 2684: * Set IPv6 outgoing packet options based on advanced API.
! 2685: */
! 2686: int
! 2687: ip6_setpktopts(control, opt, stickyopt, priv, uproto)
! 2688: struct mbuf *control;
! 2689: struct ip6_pktopts *opt, *stickyopt;
! 2690: int priv, uproto;
! 2691: {
! 2692: struct cmsghdr *cm = 0;
! 2693:
! 2694: if (control == NULL || opt == NULL)
! 2695: return (EINVAL);
! 2696:
! 2697: ip6_initpktopts(opt);
! 2698: if (stickyopt) {
! 2699: int error;
! 2700:
! 2701: /*
! 2702: * If stickyopt is provided, make a local copy of the options
! 2703: * for this particular packet, then override them by ancillary
! 2704: * objects.
! 2705: * XXX: copypktopts() does not copy the cached route to a next
! 2706: * hop (if any). This is not very good in terms of efficiency,
! 2707: * but we can allow this since this option should be rarely
! 2708: * used.
! 2709: */
! 2710: if ((error = copypktopts(opt, stickyopt, M_NOWAIT)) != 0)
! 2711: return (error);
! 2712: }
! 2713:
! 2714: /*
! 2715: * XXX: Currently, we assume all the optional information is stored
! 2716: * in a single mbuf.
! 2717: */
! 2718: if (control->m_next)
! 2719: return (EINVAL);
! 2720:
! 2721: for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len),
! 2722: control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
! 2723: int error;
! 2724:
! 2725: if (control->m_len < CMSG_LEN(0))
! 2726: return (EINVAL);
! 2727:
! 2728: cm = mtod(control, struct cmsghdr *);
! 2729: if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
! 2730: return (EINVAL);
! 2731: if (cm->cmsg_level != IPPROTO_IPV6)
! 2732: continue;
! 2733:
! 2734: error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm),
! 2735: cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, 1, uproto);
! 2736: if (error)
! 2737: return (error);
! 2738: }
! 2739:
! 2740: return (0);
! 2741: }
! 2742:
! 2743: /*
! 2744: * Set a particular packet option, as a sticky option or an ancillary data
! 2745: * item. "len" can be 0 only when it's a sticky option.
! 2746: * We have 4 cases of combination of "sticky" and "cmsg":
! 2747: * "sticky=0, cmsg=0": impossible
! 2748: * "sticky=0, cmsg=1": RFC2292 or RFC3542 ancillary data
! 2749: * "sticky=1, cmsg=0": RFC3542 socket option
! 2750: * "sticky=1, cmsg=1": RFC2292 socket option
! 2751: */
! 2752: static int
! 2753: ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
! 2754: int optname, len, priv, sticky, cmsg, uproto;
! 2755: u_char *buf;
! 2756: struct ip6_pktopts *opt;
! 2757: {
! 2758: int minmtupolicy;
! 2759:
! 2760: if (!sticky && !cmsg) {
! 2761: #ifdef DIAGNOSTIC
! 2762: printf("ip6_setpktopt: impossible case\n");
! 2763: #endif
! 2764: return (EINVAL);
! 2765: }
! 2766:
! 2767: /*
! 2768: * IPV6_2292xxx is for backward compatibility to RFC2292, and should
! 2769: * not be specified in the context of RFC3542. Conversely,
! 2770: * RFC3542 types should not be specified in the context of RFC2292.
! 2771: */
! 2772: if (!cmsg) {
! 2773: switch (optname) {
! 2774: case IPV6_2292PKTINFO:
! 2775: case IPV6_2292HOPLIMIT:
! 2776: case IPV6_2292NEXTHOP:
! 2777: case IPV6_2292HOPOPTS:
! 2778: case IPV6_2292DSTOPTS:
! 2779: case IPV6_2292RTHDR:
! 2780: case IPV6_2292PKTOPTIONS:
! 2781: return (ENOPROTOOPT);
! 2782: }
! 2783: }
! 2784: if (sticky && cmsg) {
! 2785: switch (optname) {
! 2786: case IPV6_PKTINFO:
! 2787: case IPV6_HOPLIMIT:
! 2788: case IPV6_NEXTHOP:
! 2789: case IPV6_HOPOPTS:
! 2790: case IPV6_DSTOPTS:
! 2791: case IPV6_RTHDRDSTOPTS:
! 2792: case IPV6_RTHDR:
! 2793: case IPV6_USE_MIN_MTU:
! 2794: case IPV6_DONTFRAG:
! 2795: case IPV6_TCLASS:
! 2796: return (ENOPROTOOPT);
! 2797: }
! 2798: }
! 2799:
! 2800: switch (optname) {
! 2801: case IPV6_2292PKTINFO:
! 2802: case IPV6_PKTINFO:
! 2803: {
! 2804: struct ifnet *ifp = NULL;
! 2805: struct in6_pktinfo *pktinfo;
! 2806:
! 2807: if (len != sizeof(struct in6_pktinfo))
! 2808: return (EINVAL);
! 2809:
! 2810: pktinfo = (struct in6_pktinfo *)buf;
! 2811:
! 2812: /*
! 2813: * An application can clear any sticky IPV6_PKTINFO option by
! 2814: * doing a "regular" setsockopt with ipi6_addr being
! 2815: * in6addr_any and ipi6_ifindex being zero.
! 2816: * [RFC 3542, Section 6]
! 2817: */
! 2818: if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo &&
! 2819: pktinfo->ipi6_ifindex == 0 &&
! 2820: IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
! 2821: ip6_clearpktopts(opt, optname);
! 2822: break;
! 2823: }
! 2824:
! 2825: if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO &&
! 2826: sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
! 2827: return (EINVAL);
! 2828: }
! 2829:
! 2830: /* validate the interface index if specified. */
! 2831: if (pktinfo->ipi6_ifindex >= if_indexlim ||
! 2832: pktinfo->ipi6_ifindex < 0) {
! 2833: return (ENXIO);
! 2834: }
! 2835: if (pktinfo->ipi6_ifindex) {
! 2836: ifp = ifindex2ifnet[pktinfo->ipi6_ifindex];
! 2837: if (ifp == NULL)
! 2838: return (ENXIO);
! 2839: }
! 2840:
! 2841: /*
! 2842: * We store the address anyway, and let in6_selectsrc()
! 2843: * validate the specified address. This is because ipi6_addr
! 2844: * may not have enough information about its scope zone, and
! 2845: * we may need additional information (such as outgoing
! 2846: * interface or the scope zone of a destination address) to
! 2847: * disambiguate the scope.
! 2848: * XXX: the delay of the validation may confuse the
! 2849: * application when it is used as a sticky option.
! 2850: */
! 2851: if (opt->ip6po_pktinfo == NULL) {
! 2852: opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
! 2853: M_IP6OPT, M_NOWAIT);
! 2854: if (opt->ip6po_pktinfo == NULL)
! 2855: return (ENOBUFS);
! 2856: }
! 2857: bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
! 2858: break;
! 2859: }
! 2860:
! 2861: case IPV6_2292HOPLIMIT:
! 2862: case IPV6_HOPLIMIT:
! 2863: {
! 2864: int *hlimp;
! 2865:
! 2866: /*
! 2867: * RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
! 2868: * to simplify the ordering among hoplimit options.
! 2869: */
! 2870: if (optname == IPV6_HOPLIMIT && sticky)
! 2871: return (ENOPROTOOPT);
! 2872:
! 2873: if (len != sizeof(int))
! 2874: return (EINVAL);
! 2875: hlimp = (int *)buf;
! 2876: if (*hlimp < -1 || *hlimp > 255)
! 2877: return (EINVAL);
! 2878:
! 2879: opt->ip6po_hlim = *hlimp;
! 2880: break;
! 2881: }
! 2882:
! 2883: case IPV6_TCLASS:
! 2884: {
! 2885: int tclass;
! 2886:
! 2887: if (len != sizeof(int))
! 2888: return (EINVAL);
! 2889: tclass = *(int *)buf;
! 2890: if (tclass < -1 || tclass > 255)
! 2891: return (EINVAL);
! 2892:
! 2893: opt->ip6po_tclass = tclass;
! 2894: break;
! 2895: }
! 2896:
! 2897: case IPV6_2292NEXTHOP:
! 2898: case IPV6_NEXTHOP:
! 2899: if (!priv)
! 2900: return (EPERM);
! 2901:
! 2902: if (len == 0) { /* just remove the option */
! 2903: ip6_clearpktopts(opt, IPV6_NEXTHOP);
! 2904: break;
! 2905: }
! 2906:
! 2907: /* check if cmsg_len is large enough for sa_len */
! 2908: if (len < sizeof(struct sockaddr) || len < *buf)
! 2909: return (EINVAL);
! 2910:
! 2911: switch (((struct sockaddr *)buf)->sa_family) {
! 2912: case AF_INET6:
! 2913: {
! 2914: struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
! 2915:
! 2916: if (sa6->sin6_len != sizeof(struct sockaddr_in6))
! 2917: return (EINVAL);
! 2918:
! 2919: if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
! 2920: IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
! 2921: return (EINVAL);
! 2922: }
! 2923: if (IN6_IS_SCOPE_EMBED(&sa6->sin6_addr)) {
! 2924: if (sa6->sin6_scope_id < 0 ||
! 2925: if_indexlim <= sa6->sin6_scope_id ||
! 2926: !ifindex2ifnet[sa6->sin6_scope_id])
! 2927: return (EINVAL);
! 2928: sa6->sin6_addr.s6_addr16[1] =
! 2929: htonl(sa6->sin6_scope_id);
! 2930: } else if (sa6->sin6_scope_id)
! 2931: return (EINVAL);
! 2932: break;
! 2933: }
! 2934: case AF_LINK: /* eventually be supported? */
! 2935: default:
! 2936: return (EAFNOSUPPORT);
! 2937: }
! 2938:
! 2939: /* turn off the previous option, then set the new option. */
! 2940: ip6_clearpktopts(opt, IPV6_NEXTHOP);
! 2941: opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_NOWAIT);
! 2942: if (opt->ip6po_nexthop == NULL)
! 2943: return (ENOBUFS);
! 2944: bcopy(buf, opt->ip6po_nexthop, *buf);
! 2945: break;
! 2946:
! 2947: case IPV6_2292HOPOPTS:
! 2948: case IPV6_HOPOPTS:
! 2949: {
! 2950: struct ip6_hbh *hbh;
! 2951: int hbhlen;
! 2952:
! 2953: /*
! 2954: * XXX: We don't allow a non-privileged user to set ANY HbH
! 2955: * options, since per-option restriction has too much
! 2956: * overhead.
! 2957: */
! 2958: if (!priv)
! 2959: return (EPERM);
! 2960:
! 2961: if (len == 0) {
! 2962: ip6_clearpktopts(opt, IPV6_HOPOPTS);
! 2963: break; /* just remove the option */
! 2964: }
! 2965:
! 2966: /* message length validation */
! 2967: if (len < sizeof(struct ip6_hbh))
! 2968: return (EINVAL);
! 2969: hbh = (struct ip6_hbh *)buf;
! 2970: hbhlen = (hbh->ip6h_len + 1) << 3;
! 2971: if (len != hbhlen)
! 2972: return (EINVAL);
! 2973:
! 2974: /* turn off the previous option, then set the new option. */
! 2975: ip6_clearpktopts(opt, IPV6_HOPOPTS);
! 2976: opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT);
! 2977: if (opt->ip6po_hbh == NULL)
! 2978: return (ENOBUFS);
! 2979: bcopy(hbh, opt->ip6po_hbh, hbhlen);
! 2980:
! 2981: break;
! 2982: }
! 2983:
! 2984: case IPV6_2292DSTOPTS:
! 2985: case IPV6_DSTOPTS:
! 2986: case IPV6_RTHDRDSTOPTS:
! 2987: {
! 2988: struct ip6_dest *dest, **newdest = NULL;
! 2989: int destlen;
! 2990:
! 2991: if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */
! 2992: return (EPERM);
! 2993:
! 2994: if (len == 0) {
! 2995: ip6_clearpktopts(opt, optname);
! 2996: break; /* just remove the option */
! 2997: }
! 2998:
! 2999: /* message length validation */
! 3000: if (len < sizeof(struct ip6_dest))
! 3001: return (EINVAL);
! 3002: dest = (struct ip6_dest *)buf;
! 3003: destlen = (dest->ip6d_len + 1) << 3;
! 3004: if (len != destlen)
! 3005: return (EINVAL);
! 3006: /*
! 3007: * Determine the position that the destination options header
! 3008: * should be inserted; before or after the routing header.
! 3009: */
! 3010: switch (optname) {
! 3011: case IPV6_2292DSTOPTS:
! 3012: /*
! 3013: * The old advanced API is ambiguous on this point.
! 3014: * Our approach is to determine the position based
! 3015: * according to the existence of a routing header.
! 3016: * Note, however, that this depends on the order of the
! 3017: * extension headers in the ancillary data; the 1st
! 3018: * part of the destination options header must appear
! 3019: * before the routing header in the ancillary data,
! 3020: * too.
! 3021: * RFC3542 solved the ambiguity by introducing
! 3022: * separate ancillary data or option types.
! 3023: */
! 3024: if (opt->ip6po_rthdr == NULL)
! 3025: newdest = &opt->ip6po_dest1;
! 3026: else
! 3027: newdest = &opt->ip6po_dest2;
! 3028: break;
! 3029: case IPV6_RTHDRDSTOPTS:
! 3030: newdest = &opt->ip6po_dest1;
! 3031: break;
! 3032: case IPV6_DSTOPTS:
! 3033: newdest = &opt->ip6po_dest2;
! 3034: break;
! 3035: }
! 3036:
! 3037: /* turn off the previous option, then set the new option. */
! 3038: ip6_clearpktopts(opt, optname);
! 3039: *newdest = malloc(destlen, M_IP6OPT, M_NOWAIT);
! 3040: if (*newdest == NULL)
! 3041: return (ENOBUFS);
! 3042: bcopy(dest, *newdest, destlen);
! 3043:
! 3044: break;
! 3045: }
! 3046:
! 3047: case IPV6_2292RTHDR:
! 3048: case IPV6_RTHDR:
! 3049: {
! 3050: struct ip6_rthdr *rth;
! 3051: int rthlen;
! 3052:
! 3053: if (len == 0) {
! 3054: ip6_clearpktopts(opt, IPV6_RTHDR);
! 3055: break; /* just remove the option */
! 3056: }
! 3057:
! 3058: /* message length validation */
! 3059: if (len < sizeof(struct ip6_rthdr))
! 3060: return (EINVAL);
! 3061: rth = (struct ip6_rthdr *)buf;
! 3062: rthlen = (rth->ip6r_len + 1) << 3;
! 3063: if (len != rthlen)
! 3064: return (EINVAL);
! 3065:
! 3066: switch (rth->ip6r_type) {
! 3067: case IPV6_RTHDR_TYPE_0:
! 3068: if (rth->ip6r_len == 0) /* must contain one addr */
! 3069: return (EINVAL);
! 3070: if (rth->ip6r_len % 2) /* length must be even */
! 3071: return (EINVAL);
! 3072: if (rth->ip6r_len / 2 != rth->ip6r_segleft)
! 3073: return (EINVAL);
! 3074: break;
! 3075: default:
! 3076: return (EINVAL); /* not supported */
! 3077: }
! 3078: /* turn off the previous option */
! 3079: ip6_clearpktopts(opt, IPV6_RTHDR);
! 3080: opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT);
! 3081: if (opt->ip6po_rthdr == NULL)
! 3082: return (ENOBUFS);
! 3083: bcopy(rth, opt->ip6po_rthdr, rthlen);
! 3084: break;
! 3085: }
! 3086:
! 3087: case IPV6_USE_MIN_MTU:
! 3088: if (len != sizeof(int))
! 3089: return (EINVAL);
! 3090: minmtupolicy = *(int *)buf;
! 3091: if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
! 3092: minmtupolicy != IP6PO_MINMTU_DISABLE &&
! 3093: minmtupolicy != IP6PO_MINMTU_ALL) {
! 3094: return (EINVAL);
! 3095: }
! 3096: opt->ip6po_minmtu = minmtupolicy;
! 3097: break;
! 3098:
! 3099: case IPV6_DONTFRAG:
! 3100: if (len != sizeof(int))
! 3101: return (EINVAL);
! 3102:
! 3103: if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
! 3104: /*
! 3105: * we ignore this option for TCP sockets.
! 3106: * (RFC3542 leaves this case unspecified.)
! 3107: */
! 3108: opt->ip6po_flags &= ~IP6PO_DONTFRAG;
! 3109: } else
! 3110: opt->ip6po_flags |= IP6PO_DONTFRAG;
! 3111: break;
! 3112:
! 3113: default:
! 3114: return (ENOPROTOOPT);
! 3115: } /* end of switch */
! 3116:
! 3117: return (0);
! 3118: }
! 3119:
! 3120: /*
! 3121: * Routine called from ip6_output() to loop back a copy of an IP6 multicast
! 3122: * packet to the input queue of a specified interface. Note that this
! 3123: * calls the output routine of the loopback "driver", but with an interface
! 3124: * pointer that might NOT be lo0ifp -- easier than replicating that code here.
! 3125: */
! 3126: void
! 3127: ip6_mloopback(ifp, m, dst)
! 3128: struct ifnet *ifp;
! 3129: struct mbuf *m;
! 3130: struct sockaddr_in6 *dst;
! 3131: {
! 3132: struct mbuf *copym;
! 3133: struct ip6_hdr *ip6;
! 3134:
! 3135: /*
! 3136: * Duplicate the packet.
! 3137: */
! 3138: copym = m_copy(m, 0, M_COPYALL);
! 3139: if (copym == NULL)
! 3140: return;
! 3141:
! 3142: /*
! 3143: * Make sure to deep-copy IPv6 header portion in case the data
! 3144: * is in an mbuf cluster, so that we can safely override the IPv6
! 3145: * header portion later.
! 3146: */
! 3147: if ((copym->m_flags & M_EXT) != 0 ||
! 3148: copym->m_len < sizeof(struct ip6_hdr)) {
! 3149: copym = m_pullup(copym, sizeof(struct ip6_hdr));
! 3150: if (copym == NULL)
! 3151: return;
! 3152: }
! 3153:
! 3154: #ifdef DIAGNOSTIC
! 3155: if (copym->m_len < sizeof(*ip6)) {
! 3156: m_freem(copym);
! 3157: return;
! 3158: }
! 3159: #endif
! 3160:
! 3161: ip6 = mtod(copym, struct ip6_hdr *);
! 3162: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src))
! 3163: ip6->ip6_src.s6_addr16[1] = 0;
! 3164: if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst))
! 3165: ip6->ip6_dst.s6_addr16[1] = 0;
! 3166:
! 3167: (void)looutput(ifp, copym, (struct sockaddr *)dst, NULL);
! 3168: }
! 3169:
! 3170: /*
! 3171: * Chop IPv6 header off from the payload.
! 3172: */
! 3173: static int
! 3174: ip6_splithdr(m, exthdrs)
! 3175: struct mbuf *m;
! 3176: struct ip6_exthdrs *exthdrs;
! 3177: {
! 3178: struct mbuf *mh;
! 3179: struct ip6_hdr *ip6;
! 3180:
! 3181: ip6 = mtod(m, struct ip6_hdr *);
! 3182: if (m->m_len > sizeof(*ip6)) {
! 3183: MGETHDR(mh, M_DONTWAIT, MT_HEADER);
! 3184: if (mh == 0) {
! 3185: m_freem(m);
! 3186: return ENOBUFS;
! 3187: }
! 3188: M_MOVE_PKTHDR(mh, m);
! 3189: MH_ALIGN(mh, sizeof(*ip6));
! 3190: m->m_len -= sizeof(*ip6);
! 3191: m->m_data += sizeof(*ip6);
! 3192: mh->m_next = m;
! 3193: m = mh;
! 3194: m->m_len = sizeof(*ip6);
! 3195: bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
! 3196: }
! 3197: exthdrs->ip6e_ip6 = m;
! 3198: return 0;
! 3199: }
! 3200:
! 3201: /*
! 3202: * Compute IPv6 extension header length.
! 3203: */
! 3204: int
! 3205: ip6_optlen(inp)
! 3206: struct inpcb *inp;
! 3207: {
! 3208: int len;
! 3209:
! 3210: if (!inp->inp_outputopts6)
! 3211: return 0;
! 3212:
! 3213: len = 0;
! 3214: #define elen(x) \
! 3215: (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0)
! 3216:
! 3217: len += elen(inp->inp_outputopts6->ip6po_hbh);
! 3218: len += elen(inp->inp_outputopts6->ip6po_dest1);
! 3219: len += elen(inp->inp_outputopts6->ip6po_rthdr);
! 3220: len += elen(inp->inp_outputopts6->ip6po_dest2);
! 3221: return len;
! 3222: #undef elen
! 3223: }
CVSweb