ip6_output.c revision 56743
160484Sobrien/* 277298Sobrien * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3130561Sobrien * All rights reserved. 433965Sjdp * 533965Sjdp * Redistribution and use in source and binary forms, with or without 660484Sobrien * modification, are permitted provided that the following conditions 760484Sobrien * are met: 833965Sjdp * 1. Redistributions of source code must retain the above copyright 933965Sjdp * notice, this list of conditions and the following disclaimer. 1033965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1133965Sjdp * notice, this list of conditions and the following disclaimer in the 1233965Sjdp * documentation and/or other materials provided with the distribution. 1333965Sjdp * 3. Neither the name of the project nor the names of its contributors 14130561Sobrien * may be used to endorse or promote products derived from this software 15130561Sobrien * without specific prior written permission. 16130561Sobrien * 17130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18130561Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19130561Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20130561Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21130561Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22130561Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2333965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2433965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2533965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2633965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2733965Sjdp * SUCH DAMAGE. 2833965Sjdp * 2933965Sjdp * $FreeBSD: head/sys/netinet6/ip6_output.c 56743 2000-01-28 13:16:34Z shin $ 30218822Sdim */ 31218822Sdim 3233965Sjdp/* 3333965Sjdp * Copyright (c) 1982, 1986, 1988, 1990, 1993 3433965Sjdp * The Regents of the University of California. All rights reserved. 3533965Sjdp * 3633965Sjdp * Redistribution and use in source and binary forms, with or without 3733965Sjdp * modification, are permitted provided that the following conditions 3833965Sjdp * are met: 3933965Sjdp * 1. Redistributions of source code must retain the above copyright 4033965Sjdp * notice, this list of conditions and the following disclaimer. 4133965Sjdp * 2. Redistributions in binary form must reproduce the above copyright 4260484Sobrien * notice, this list of conditions and the following disclaimer in the 4360484Sobrien * documentation and/or other materials provided with the distribution. 4460484Sobrien * 3. All advertising materials mentioning features or use of this software 4560484Sobrien * must display the following acknowledgement: 4677298Sobrien * This product includes software developed by the University of 4777298Sobrien * California, Berkeley and its contributors. 4860484Sobrien * 4. Neither the name of the University nor the names of its contributors 4933965Sjdp * may be used to endorse or promote products derived from this software 5033965Sjdp * without specific prior written permission. 5133965Sjdp * 5260484Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5360484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5460484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5560484Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5660484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5760484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5860484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5933965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6033965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6133965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6233965Sjdp * SUCH DAMAGE. 6360484Sobrien * 6433965Sjdp * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 65218822Sdim */ 6677298Sobrien 6760484Sobrien#include "opt_ipsec.h" 6860484Sobrien 6960484Sobrien#include <sys/param.h> 7060484Sobrien#include <sys/malloc.h> 7160484Sobrien#include <sys/mbuf.h> 7260484Sobrien#include <sys/errno.h> 73218822Sdim#include <sys/protosw.h> 7460484Sobrien#include <sys/socket.h> 7533965Sjdp#include <sys/socketvar.h> 7633965Sjdp#include <sys/systm.h> 7733965Sjdp#include <sys/kernel.h> 7833965Sjdp#include <sys/proc.h> 7933965Sjdp 8033965Sjdp#include <net/if.h> 8133965Sjdp#include <net/route.h> 8233965Sjdp 8333965Sjdp#include <netinet/in.h> 8433965Sjdp#include <netinet/in_var.h> 8533965Sjdp#include <netinet6/ip6.h> 8633965Sjdp#include <netinet6/icmp6.h> 8733965Sjdp#include <netinet/in_pcb.h> 8833965Sjdp#include <netinet6/ip6_var.h> 8933965Sjdp#include <netinet6/nd6.h> 9033965Sjdp 9133965Sjdp#ifdef IPSEC 9233965Sjdp#include <netinet6/ipsec.h> 9333965Sjdp#include <netinet6/ipsec6.h> 9433965Sjdp#include <netkey/key.h> 9577298Sobrien#ifdef IPSEC_DEBUG 9633965Sjdp#include <netkey/key_debug.h> 9733965Sjdp#else 9833965Sjdp#define KEYDEBUG(lev,arg) 9960484Sobrien#endif 10060484Sobrien#endif /* IPSEC */ 10133965Sjdp 102218822Sdim#include "loop.h" 10333965Sjdp 10433965Sjdp#include <net/net_osdep.h> 10533965Sjdp 10633965Sjdp#ifdef IPV6FIREWALL 10760484Sobrien#include <netinet6/ip6_fw.h> 10860484Sobrien#endif 10960484Sobrien 11060484Sobrienstatic MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); 11160484Sobrien 11260484Sobrienstruct ip6_exthdrs { 11360484Sobrien struct mbuf *ip6e_ip6; 11433965Sjdp struct mbuf *ip6e_hbh; 11533965Sjdp struct mbuf *ip6e_dest1; 11633965Sjdp struct mbuf *ip6e_rthdr; 11733965Sjdp struct mbuf *ip6e_dest2; 11833965Sjdp}; 11933965Sjdp 12033965Sjdpstatic int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, 12160484Sobrien struct socket *, struct sockopt *sopt)); 12260484Sobrienstatic int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); 12360484Sobrienstatic int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); 12460484Sobrienstatic int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); 12560484Sobrienstatic int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, 12660484Sobrien struct ip6_frag **)); 12733965Sjdpstatic int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); 12833965Sjdpstatic int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); 12933965Sjdp 13033965Sjdp/* 13133965Sjdp * IP6 output. The packet in mbuf chain m contains a skeletal IP6 13260484Sobrien * header (with pri, len, nxt, hlim, src, dst). 13360484Sobrien * This function may modify ver and hlim only. 13460484Sobrien * The mbuf chain containing the packet will be freed. 13538889Sjdp * The mbuf opt, if present, will not be freed. 13638889Sjdp */ 13760484Sobrienint 13860484Sobrienip6_output(m0, opt, ro, flags, im6o, ifpp) 13960484Sobrien struct mbuf *m0; 14060484Sobrien struct ip6_pktopts *opt; 14160484Sobrien struct route_in6 *ro; 14233965Sjdp int flags; 14333965Sjdp struct ip6_moptions *im6o; 14433965Sjdp struct ifnet **ifpp; /* XXX: just for statistics */ 14533965Sjdp{ 14633965Sjdp struct ip6_hdr *ip6, *mhip6; 14733965Sjdp struct ifnet *ifp; 14833965Sjdp struct mbuf *m = m0; 14989857Sobrien int hlen, tlen, len, off; 15089857Sobrien struct route_in6 ip6route; 15189857Sobrien struct sockaddr_in6 *dst; 15233965Sjdp int error = 0; 15333965Sjdp struct in6_ifaddr *ia; 15433965Sjdp u_long mtu; 15533965Sjdp u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; 15633965Sjdp struct ip6_exthdrs exthdrs; 15733965Sjdp struct in6_addr finaldst; 15833965Sjdp struct route_in6 *ro_pmtu = NULL; 15933965Sjdp int hdrsplit = 0; 16033965Sjdp int needipsec = 0; 16133965Sjdp#ifdef IPSEC 16233965Sjdp int needipsectun = 0; 16333965Sjdp struct socket *so; 16433965Sjdp struct secpolicy *sp = NULL; 16533965Sjdp 16633965Sjdp /* for AH processing. stupid to have "socket" variable in IP layer... */ 16733965Sjdp if ((flags & IPV6_SOCKINMRCVIF) != 0) { 16833965Sjdp so = (struct socket *)m->m_pkthdr.rcvif; 16933965Sjdp m->m_pkthdr.rcvif = NULL; 17033965Sjdp } else 17133965Sjdp so = NULL; 17233965Sjdp ip6 = mtod(m, struct ip6_hdr *); 17333965Sjdp#endif /* IPSEC */ 17433965Sjdp 17533965Sjdp#define MAKE_EXTHDR(hp,mp) \ 17633965Sjdp { \ 17733965Sjdp if (hp) { \ 17833965Sjdp struct ip6_ext *eh = (struct ip6_ext *)(hp); \ 17933965Sjdp error = ip6_copyexthdr((mp), (caddr_t)(hp), \ 18033965Sjdp ((eh)->ip6e_len + 1) << 3); \ 18133965Sjdp if (error) \ 18233965Sjdp goto freehdrs; \ 18333965Sjdp } \ 18433965Sjdp } 18533965Sjdp 18633965Sjdp bzero(&exthdrs, sizeof(exthdrs)); 18733965Sjdp if (opt) { 18833965Sjdp /* Hop-by-Hop options header */ 18933965Sjdp MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); 19033965Sjdp /* Destination options header(1st part) */ 19133965Sjdp MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); 19233965Sjdp /* Routing header */ 19333965Sjdp MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); 19433965Sjdp /* Destination options header(2nd part) */ 19533965Sjdp MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); 19633965Sjdp } 19733965Sjdp 19833965Sjdp#ifdef IPSEC 19933965Sjdp /* get a security policy for this packet */ 20033965Sjdp if (so == NULL) 20133965Sjdp sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); 20233965Sjdp else 20333965Sjdp sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); 20433965Sjdp 20533965Sjdp if (sp == NULL) { 20633965Sjdp ipsec6stat.out_inval++; 20733965Sjdp goto bad; 20833965Sjdp } 20933965Sjdp 21033965Sjdp error = 0; 21133965Sjdp 21233965Sjdp /* check policy */ 21333965Sjdp switch (sp->policy) { 21433965Sjdp case IPSEC_POLICY_DISCARD: 21533965Sjdp /* 21633965Sjdp * This packet is just discarded. 21733965Sjdp */ 21833965Sjdp ipsec6stat.out_polvio++; 21933965Sjdp goto bad; 22033965Sjdp 22133965Sjdp case IPSEC_POLICY_BYPASS: 22233965Sjdp case IPSEC_POLICY_NONE: 22333965Sjdp /* no need to do IPsec. */ 22433965Sjdp needipsec = 0; 22533965Sjdp break; 22633965Sjdp 22733965Sjdp case IPSEC_POLICY_IPSEC: 22833965Sjdp if (sp->req == NULL) { 22933965Sjdp /* XXX should be panic ? */ 23060484Sobrien printf("ip6_output: No IPsec request specified.\n"); 23160484Sobrien error = EINVAL; 23233965Sjdp goto bad; 23333965Sjdp } 23460484Sobrien needipsec = 1; 23560484Sobrien break; 23660484Sobrien 23760484Sobrien case IPSEC_POLICY_ENTRUST: 23860484Sobrien default: 23960484Sobrien printf("ip6_output: Invalid policy found. %d\n", sp->policy); 24060484Sobrien } 24160484Sobrien#endif /* IPSEC */ 24260484Sobrien 24360484Sobrien /* 24460484Sobrien * Calculate the total length of the extension header chain. 24560484Sobrien * Keep the length of the unfragmentable part for fragmentation. 24660484Sobrien */ 24733965Sjdp optlen = 0; 24889857Sobrien if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len; 24968765Sobrien if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len; 25068765Sobrien if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len; 25189857Sobrien unfragpartlen = optlen + sizeof(struct ip6_hdr); 25289857Sobrien /* NOTE: we don't add AH/ESP length here. do that later. */ 25389857Sobrien if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len; 25489857Sobrien 25589857Sobrien /* 25689857Sobrien * If we need IPsec, or there is at least one extension header, 25768765Sobrien * separate IP6 header from the payload. 25868765Sobrien */ 25968765Sobrien if ((needipsec || optlen) && !hdrsplit) { 26068765Sobrien if ((error = ip6_splithdr(m, &exthdrs)) != 0) { 26168765Sobrien m = NULL; 26268765Sobrien goto freehdrs; 26368765Sobrien } 26468765Sobrien m = exthdrs.ip6e_ip6; 26568765Sobrien hdrsplit++; 26668765Sobrien } 26768765Sobrien 26868765Sobrien /* adjust pointer */ 26968765Sobrien ip6 = mtod(m, struct ip6_hdr *); 27068765Sobrien 27168765Sobrien /* adjust mbuf packet header length */ 27268765Sobrien m->m_pkthdr.len += optlen; 27368765Sobrien plen = m->m_pkthdr.len - sizeof(*ip6); 27468765Sobrien 27568765Sobrien /* If this is a jumbo payload, insert a jumbo payload option. */ 27668765Sobrien if (plen > IPV6_MAXPACKET) { 27768765Sobrien if (!hdrsplit) { 27868765Sobrien if ((error = ip6_splithdr(m, &exthdrs)) != 0) { 27968765Sobrien m = NULL; 28068765Sobrien goto freehdrs; 28168765Sobrien } 28268765Sobrien m = exthdrs.ip6e_ip6; 28368765Sobrien hdrsplit++; 28468765Sobrien } 28568765Sobrien /* adjust pointer */ 28668765Sobrien ip6 = mtod(m, struct ip6_hdr *); 28768765Sobrien if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0) 28868765Sobrien goto freehdrs; 28968765Sobrien ip6->ip6_plen = 0; 29068765Sobrien } else 29168765Sobrien ip6->ip6_plen = htons(plen); 29268765Sobrien 29377298Sobrien /* 29477298Sobrien * Concatenate headers and fill in next header fields. 29577298Sobrien * Here we have, on "m" 29668765Sobrien * IPv6 payload 29768765Sobrien * and we insert headers accordingly. Finally, we should be getting: 29868765Sobrien * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] 29977298Sobrien * 30077298Sobrien * during the header composing process, "m" points to IPv6 header. 30177298Sobrien * "mprev" points to an extension header prior to esp. 30277298Sobrien */ 30377298Sobrien { 30477298Sobrien u_char *nexthdrp = &ip6->ip6_nxt; 30577298Sobrien struct mbuf *mprev = m; 30677298Sobrien 30777298Sobrien /* 30877298Sobrien * we treat dest2 specially. this makes IPsec processing 30977298Sobrien * much easier. 31077298Sobrien * 31168765Sobrien * result: IPv6 dest2 payload 31268765Sobrien * m and mprev will point to IPv6 header. 31368765Sobrien */ 31468765Sobrien if (exthdrs.ip6e_dest2) { 31533965Sjdp if (!hdrsplit) 31633965Sjdp panic("assumption failed: hdr not split"); 31733965Sjdp exthdrs.ip6e_dest2->m_next = m->m_next; 31860484Sobrien m->m_next = exthdrs.ip6e_dest2; 31933965Sjdp *mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt; 32060484Sobrien ip6->ip6_nxt = IPPROTO_DSTOPTS; 32160484Sobrien } 32260484Sobrien 32360484Sobrien#define MAKE_CHAIN(m,mp,p,i)\ 32433965Sjdp {\ 32533965Sjdp if (m) {\ 32633965Sjdp if (!hdrsplit) \ 32733965Sjdp panic("assumption failed: hdr not split"); \ 32833965Sjdp *mtod((m), u_char *) = *(p);\ 329218822Sdim *(p) = (i);\ 33068765Sobrien p = mtod((m), u_char *);\ 331218822Sdim (m)->m_next = (mp)->m_next;\ 33268765Sobrien (mp)->m_next = (m);\ 333218822Sdim (mp) = (m);\ 33433965Sjdp }\ 335218822Sdim } 33660484Sobrien /* 337218822Sdim * result: IPv6 hbh dest1 rthdr dest2 payload 33868765Sobrien * m will point to IPv6 header. mprev will point to the 33933965Sjdp * extension header prior to dest2 (rthdr in the above case). 34033965Sjdp */ 341218822Sdim MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, 34233965Sjdp nexthdrp, IPPROTO_HOPOPTS); 34333965Sjdp MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, 34460484Sobrien nexthdrp, IPPROTO_DSTOPTS); 345218822Sdim MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, 34660484Sobrien nexthdrp, IPPROTO_ROUTING); 34733965Sjdp 348218822Sdim#ifdef IPSEC 349218822Sdim if (!needipsec) 35060484Sobrien goto skip_ipsec2; 35160484Sobrien 352218822Sdim /* 353218822Sdim * pointers after IPsec headers are not valid any more. 35433965Sjdp * other pointers need a great care too. 35533965Sjdp * (IPsec routines should not mangle mbufs prior to AH/ESP) 356218822Sdim */ 357218822Sdim exthdrs.ip6e_dest2 = NULL; 35833965Sjdp 35933965Sjdp { 360218822Sdim struct ip6_rthdr *rh = NULL; 36133965Sjdp int segleft_org = 0; 36233965Sjdp struct ipsec_output_state state; 363218822Sdim 364218822Sdim if (exthdrs.ip6e_rthdr) { 36533965Sjdp rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *); 366218822Sdim segleft_org = rh->ip6r_segleft; 36733965Sjdp rh->ip6r_segleft = 0; 368218822Sdim } 36933965Sjdp 370218822Sdim bzero(&state, sizeof(state)); 37133965Sjdp state.m = m; 372218822Sdim error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags, 37333965Sjdp &needipsectun); 374218822Sdim m = state.m; 37533965Sjdp if (error) { 376218822Sdim /* mbuf is already reclaimed in ipsec6_output_trans. */ 37733965Sjdp m = NULL; 378218822Sdim switch (error) { 37933965Sjdp case EHOSTUNREACH: 380218822Sdim case ENETUNREACH: 38133965Sjdp case EMSGSIZE: 38233965Sjdp case ENOBUFS: 383218822Sdim case ENOMEM: 38433965Sjdp break; 385218822Sdim default: 38633965Sjdp printf("ip6_output (ipsec): error code %d\n", error); 38733965Sjdp /*fall through*/ 388218822Sdim case ENOENT: 38933965Sjdp /* don't show these error codes to the user */ 39033965Sjdp error = 0; 391218822Sdim break; 39233965Sjdp } 393218822Sdim goto bad; 39433965Sjdp } 395218822Sdim if (exthdrs.ip6e_rthdr) { 39633965Sjdp /* ah6_output doesn't modify mbuf chain */ 397218822Sdim rh->ip6r_segleft = segleft_org; 39833965Sjdp } 399218822Sdim } 40033965Sjdpskip_ipsec2:; 401218822Sdim#endif 40260484Sobrien } 403218822Sdim 40433965Sjdp /* 405218822Sdim * If there is a routing header, replace destination address field 40633965Sjdp * with the first hop of the routing header. 407218822Sdim */ 40838889Sjdp if (exthdrs.ip6e_rthdr) { 409218822Sdim struct ip6_rthdr *rh = 41033965Sjdp (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, 411218822Sdim struct ip6_rthdr *)); 41260484Sobrien struct ip6_rthdr0 *rh0; 413218822Sdim 41433965Sjdp finaldst = ip6->ip6_dst; 415218822Sdim switch(rh->ip6r_type) { 41633965Sjdp case IPV6_RTHDR_TYPE_0: 41733965Sjdp rh0 = (struct ip6_rthdr0 *)rh; 418218822Sdim ip6->ip6_dst = rh0->ip6r0_addr[0]; 419218822Sdim bcopy((caddr_t)&rh0->ip6r0_addr[1], 42033965Sjdp (caddr_t)&rh0->ip6r0_addr[0], 42168765Sobrien sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) 422218822Sdim ); 423218822Sdim rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; 42468765Sobrien break; 425218822Sdim default: /* is it possible? */ 42633965Sjdp error = EINVAL; 427218822Sdim goto bad; 42860484Sobrien } 429218822Sdim } 43060484Sobrien 431218822Sdim /* Source address validation */ 43260484Sobrien if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && 433218822Sdim (flags & IPV6_DADOUTPUT) == 0) { 43433965Sjdp error = EOPNOTSUPP; 435218822Sdim ip6stat.ip6s_badscope++; 43660484Sobrien goto bad; 437218822Sdim } 43833965Sjdp if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { 43960484Sobrien error = EOPNOTSUPP; 440218822Sdim ip6stat.ip6s_badscope++; 441218822Sdim goto bad; 44233965Sjdp } 44333965Sjdp 444218822Sdim ip6stat.ip6s_localout++; 44560484Sobrien 44660484Sobrien /* 447218822Sdim * Route packet. 44860484Sobrien */ 449218822Sdim if (ro == 0) { 45060484Sobrien ro = &ip6route; 45160484Sobrien bzero((caddr_t)ro, sizeof(*ro)); 45260484Sobrien } 45360484Sobrien ro_pmtu = ro; 45460484Sobrien if (opt && opt->ip6po_rthdr) 45560484Sobrien ro = &opt->ip6po_route; 45660484Sobrien dst = (struct sockaddr_in6 *)&ro->ro_dst; 45760484Sobrien /* 45860484Sobrien * If there is a cached route, 45960484Sobrien * check that it is to the same destination 460218822Sdim * and is still up. If not, free it and try again. 46160484Sobrien */ 462218822Sdim if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 46360484Sobrien !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { 464218822Sdim RTFREE(ro->ro_rt); 46560484Sobrien ro->ro_rt = (struct rtentry *)0; 466218822Sdim } 467218822Sdim if (ro->ro_rt == 0) { 46860484Sobrien bzero(dst, sizeof(*dst)); 46960484Sobrien dst->sin6_family = AF_INET6; 470218822Sdim dst->sin6_len = sizeof(struct sockaddr_in6); 47160484Sobrien dst->sin6_addr = ip6->ip6_dst; 47260484Sobrien } 473218822Sdim#ifdef IPSEC 47460484Sobrien if (needipsec && needipsectun) { 47560484Sobrien struct ipsec_output_state state; 476218822Sdim 47760484Sobrien /* 47860484Sobrien * All the extension headers will become inaccessible 479218822Sdim * (since they can be encrypted). 48060484Sobrien * Don't panic, we need no more updates to extension headers 481218822Sdim * on inner IPv6 packet (since they are now encapsulated). 48277298Sobrien * 48360484Sobrien * IPv6 [ESP|AH] IPv6 [extension headers] payload 48460484Sobrien */ 48560484Sobrien bzero(&exthdrs, sizeof(exthdrs)); 48660484Sobrien exthdrs.ip6e_ip6 = m; 48760484Sobrien 48860484Sobrien bzero(&state, sizeof(state)); 48960484Sobrien state.m = m; 49060484Sobrien state.ro = (struct route *)ro; 49160484Sobrien state.dst = (struct sockaddr *)dst; 492218822Sdim 49333965Sjdp error = ipsec6_output_tunnel(&state, sp, flags); 49433965Sjdp 49533965Sjdp m = state.m; 49677298Sobrien ro = (struct route_in6 *)state.ro; 49760484Sobrien dst = (struct sockaddr_in6 *)state.dst; 49860484Sobrien if (error) { 49977298Sobrien /* mbuf is already reclaimed in ipsec6_output_tunnel. */ 50033965Sjdp m0 = m = NULL; 50133965Sjdp m = NULL; 50260484Sobrien switch (error) { 50360484Sobrien case EHOSTUNREACH: 50460484Sobrien case ENETUNREACH: 50560484Sobrien case EMSGSIZE: 50660484Sobrien case ENOBUFS: 50760484Sobrien case ENOMEM: 50860484Sobrien break; 50960484Sobrien default: 51077298Sobrien printf("ip6_output (ipsec): error code %d\n", error); 51160484Sobrien /*fall through*/ 51260484Sobrien case ENOENT: 51360484Sobrien /* don't show these error codes to the user */ 51460484Sobrien error = 0; 51533965Sjdp break; 51633965Sjdp } 51733965Sjdp goto bad; 51860484Sobrien } 51989857Sobrien 52089857Sobrien exthdrs.ip6e_ip6 = m; 52189857Sobrien } 52233965Sjdp#endif /*IPESC*/ 52333965Sjdp 52433965Sjdp if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 52538889Sjdp /* Unicast */ 52660484Sobrien 52738889Sjdp#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) 52838889Sjdp#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) 52938889Sjdp /* xxx 53038889Sjdp * interface selection comes here 531218822Sdim * if an interface is specified from an upper layer, 53238889Sjdp * ifp must point it. 53338889Sjdp */ 53438889Sjdp if (ro->ro_rt == 0) 53538889Sjdp rtalloc((struct route *)ro); 53638889Sjdp if (ro->ro_rt == 0) { 53738889Sjdp ip6stat.ip6s_noroute++; 53877298Sobrien error = EHOSTUNREACH; 53938889Sjdp /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */ 54038889Sjdp goto bad; 54138889Sjdp } 54238889Sjdp ia = ifatoia6(ro->ro_rt->rt_ifa); 54338889Sjdp ifp = ro->ro_rt->rt_ifp; 54438889Sjdp ro->ro_rt->rt_use++; 54560484Sobrien if (ro->ro_rt->rt_flags & RTF_GATEWAY) 54638889Sjdp dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway; 54738889Sjdp m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ 54838889Sjdp 54938889Sjdp in6_ifstat_inc(ifp, ifs6_out_request); 55038889Sjdp 55138889Sjdp /* 55260484Sobrien * Check if there is the outgoing interface conflicts with 55338889Sjdp * the interface specified by ifi6_ifindex(if specified). 55438889Sjdp * Note that loopback interface is always okay. 55538889Sjdp * (this happens when we are sending packet toward my 55638889Sjdp * interface) 55738889Sjdp */ 55838889Sjdp if (opt && opt->ip6po_pktinfo 55938889Sjdp && opt->ip6po_pktinfo->ipi6_ifindex) { 56060484Sobrien if (!(ifp->if_flags & IFF_LOOPBACK) 56160484Sobrien && ifp->if_index != opt->ip6po_pktinfo->ipi6_ifindex) { 56260484Sobrien ip6stat.ip6s_noroute++; 56360484Sobrien in6_ifstat_inc(ifp, ifs6_out_discard); 564218822Sdim error = EHOSTUNREACH; 56560484Sobrien goto bad; 56660484Sobrien } 56760484Sobrien } 56860484Sobrien 56960484Sobrien if (opt && opt->ip6po_hlim != -1) 57060484Sobrien ip6->ip6_hlim = opt->ip6po_hlim & 0xff; 57160484Sobrien } else { 57260484Sobrien /* Multicast */ 57360484Sobrien struct in6_multi *in6m; 57460484Sobrien 57560484Sobrien m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST; 57660484Sobrien 57760484Sobrien /* 57860484Sobrien * See if the caller provided any multicast options 57960484Sobrien */ 58060484Sobrien ifp = NULL; 58160484Sobrien if (im6o != NULL) { 58260484Sobrien ip6->ip6_hlim = im6o->im6o_multicast_hlim; 58360484Sobrien if (im6o->im6o_multicast_ifp != NULL) 58460484Sobrien ifp = im6o->im6o_multicast_ifp; 58560484Sobrien } else 58660484Sobrien ip6->ip6_hlim = ip6_defmcasthlim; 58760484Sobrien 58860484Sobrien /* 589218822Sdim * See if the caller provided the outgoing interface 59060484Sobrien * as an ancillary data. 59160484Sobrien * Boundary check for ifindex is assumed to be already done. 59260484Sobrien */ 59360484Sobrien if (opt && opt->ip6po_pktinfo && opt->ip6po_pktinfo->ipi6_ifindex) 59460484Sobrien ifp = ifindex2ifnet[opt->ip6po_pktinfo->ipi6_ifindex]; 59560484Sobrien 59660484Sobrien /* 59760484Sobrien * If the destination is a node-local scope multicast, 59860484Sobrien * the packet should be loop-backed only. 59960484Sobrien */ 60060484Sobrien if (IN6_IS_ADDR_MC_NODELOCAL(&ip6->ip6_dst)) { 60160484Sobrien /* 60260484Sobrien * If the outgoing interface is already specified, 60360484Sobrien * it should be a loopback interface. 60460484Sobrien */ 60560484Sobrien if (ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) { 60660484Sobrien ip6stat.ip6s_badscope++; 60760484Sobrien error = ENETUNREACH; /* XXX: better error? */ 60860484Sobrien /* XXX correct ifp? */ 60960484Sobrien in6_ifstat_inc(ifp, ifs6_out_discard); 61060484Sobrien goto bad; 61160484Sobrien } else { 61260484Sobrien ifp = &loif[0]; 61360484Sobrien } 61460484Sobrien } 61560484Sobrien 61660484Sobrien if (opt && opt->ip6po_hlim != -1) 61760484Sobrien ip6->ip6_hlim = opt->ip6po_hlim & 0xff; 61860484Sobrien 61960484Sobrien /* 62060484Sobrien * If caller did not provide an interface lookup a 62160484Sobrien * default in the routing table. This is either a 62260484Sobrien * default for the speicfied group (i.e. a host 62360484Sobrien * route), or a multicast default (a route for the 62460484Sobrien * ``net'' ff00::/8). 62560484Sobrien */ 62660484Sobrien if (ifp == NULL) { 62760484Sobrien if (ro->ro_rt == 0) { 62860484Sobrien ro->ro_rt = rtalloc1((struct sockaddr *) 62960484Sobrien &ro->ro_dst, 0, 0UL); 630218822Sdim } 63160484Sobrien if (ro->ro_rt == 0) { 63260484Sobrien ip6stat.ip6s_noroute++; 63360484Sobrien error = EHOSTUNREACH; 63460484Sobrien /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */ 63533965Sjdp goto bad; 636218822Sdim } 63733965Sjdp ia = ifatoia6(ro->ro_rt->rt_ifa); 63860484Sobrien ifp = ro->ro_rt->rt_ifp; 63933965Sjdp ro->ro_rt->rt_use++; 64033965Sjdp } 64133965Sjdp 64233965Sjdp if ((flags & IPV6_FORWARDING) == 0) 64333965Sjdp in6_ifstat_inc(ifp, ifs6_out_request); 64433965Sjdp in6_ifstat_inc(ifp, ifs6_out_mcast); 64533965Sjdp 64660484Sobrien /* 64733965Sjdp * Confirm that the outgoing interface supports multicast. 64860484Sobrien */ 64933965Sjdp if ((ifp->if_flags & IFF_MULTICAST) == 0) { 65033965Sjdp ip6stat.ip6s_noroute++; 65133965Sjdp in6_ifstat_inc(ifp, ifs6_out_discard); 65233965Sjdp error = ENETUNREACH; 65333965Sjdp goto bad; 65433965Sjdp } 65533965Sjdp IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m); 65633965Sjdp if (in6m != NULL && 65733965Sjdp (im6o == NULL || im6o->im6o_multicast_loop)) { 65833965Sjdp /* 65933965Sjdp * If we belong to the destination multicast group 66033965Sjdp * on the outgoing interface, and the caller did not 66133965Sjdp * forbid loopback, loop back a copy. 66233965Sjdp */ 66333965Sjdp ip6_mloopback(ifp, m, dst); 66477298Sobrien } else { 66577298Sobrien /* 66633965Sjdp * If we are acting as a multicast router, perform 66733965Sjdp * multicast forwarding as if the packet had just 66833965Sjdp * arrived on the interface to which we are about 66933965Sjdp * to send. The multicast forwarding function 67060484Sobrien * recursively calls this function, using the 67177298Sobrien * IPV6_FORWARDING flag to prevent infinite recursion. 67233965Sjdp * 67333965Sjdp * Multicasts that are looped back by ip6_mloopback(), 67433965Sjdp * above, will be forwarded by the ip6_input() routine, 67533965Sjdp * if necessary. 67633965Sjdp */ 67733965Sjdp if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) { 67833965Sjdp if (ip6_mforward(ip6, ifp, m) != NULL) { 67933965Sjdp m_freem(m); 68033965Sjdp goto done; 68133965Sjdp } 68233965Sjdp } 68333965Sjdp } 68433965Sjdp /* 68533965Sjdp * Multicasts with a hoplimit of zero may be looped back, 68633965Sjdp * above, but must not be transmitted on a network. 68733965Sjdp * Also, multicasts addressed to the loopback interface 68860484Sobrien * are not sent -- the above call to ip6_mloopback() will 68977298Sobrien * loop back a copy if this host actually belongs to the 69033965Sjdp * destination group on the loopback interface. 69133965Sjdp */ 69233965Sjdp if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK)) { 69333965Sjdp m_freem(m); 69433965Sjdp goto done; 69533965Sjdp } 69633965Sjdp } 69733965Sjdp 69860484Sobrien /* 69933965Sjdp * Fill the outgoing inteface to tell the upper layer 70033965Sjdp * to increment per-interface statistics. 70133965Sjdp */ 70233965Sjdp if (ifpp) 70360484Sobrien *ifpp = ifp; 70433965Sjdp 70533965Sjdp /* 70633965Sjdp * Determine path MTU. 70733965Sjdp */ 70833965Sjdp if (ro_pmtu != ro) { 70933965Sjdp /* The first hop and the final destination may differ. */ 71033965Sjdp struct sockaddr_in6 *sin6_fin = 71133965Sjdp (struct sockaddr_in6 *)&ro_pmtu->ro_dst; 71260484Sobrien if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 71377298Sobrien !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, 71433965Sjdp &finaldst))) { 71533965Sjdp RTFREE(ro_pmtu->ro_rt); 71660484Sobrien ro_pmtu->ro_rt = (struct rtentry *)0; 71733965Sjdp } 71833965Sjdp if (ro_pmtu->ro_rt == 0) { 71933965Sjdp bzero(sin6_fin, sizeof(*sin6_fin)); 72033965Sjdp sin6_fin->sin6_family = AF_INET6; 72133965Sjdp sin6_fin->sin6_len = sizeof(struct sockaddr_in6); 72233965Sjdp sin6_fin->sin6_addr = finaldst; 72333965Sjdp 72433965Sjdp rtalloc((struct route *)ro_pmtu); 72533965Sjdp } 72633965Sjdp } 72733965Sjdp if (ro_pmtu->ro_rt != NULL) { 72833965Sjdp u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; 72960484Sobrien 73077298Sobrien mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; 73133965Sjdp if (mtu > ifmtu) { 73233965Sjdp /* 73360484Sobrien * The MTU on the route is larger than the MTU on 73433965Sjdp * the interface! This shouldn't happen, unless the 73533965Sjdp * MTU of the interface has been changed after the 73633965Sjdp * interface was brought up. Change the MTU in the 73733965Sjdp * route to match the interface MTU (as long as the 73833965Sjdp * field isn't locked). 73933965Sjdp */ 74033965Sjdp mtu = ifmtu; 74133965Sjdp if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) 74233965Sjdp ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */ 74333965Sjdp } 74433965Sjdp } else { 74533965Sjdp mtu = nd_ifinfo[ifp->if_index].linkmtu; 74633965Sjdp } 74733965Sjdp 74833965Sjdp /* 74933965Sjdp * Fake link-local scope-class addresses 75033965Sjdp */ 75133965Sjdp if ((ifp->if_flags & IFF_LOOPBACK) == 0) { 75233965Sjdp if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) 75333965Sjdp ip6->ip6_src.s6_addr16[1] = 0; 75433965Sjdp if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) 75533965Sjdp ip6->ip6_dst.s6_addr16[1] = 0; 75633965Sjdp } 75760484Sobrien 75833965Sjdp#ifdef IPV6FIREWALL 75933965Sjdp /* 76033965Sjdp * Check with the firewall... 76168765Sobrien */ 76233965Sjdp if (ip6_fw_chk_ptr) { 76333965Sjdp u_short port = 0; 76433965Sjdp /* If ipfw says divert, we have to just drop packet */ 76533965Sjdp if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) { 76633965Sjdp m_freem(m); 76733965Sjdp goto done; 76833965Sjdp } 769218822Sdim if (!m) { 77033965Sjdp error = EACCES; 77160484Sobrien goto done; 77233965Sjdp } 77333965Sjdp } 77433965Sjdp#endif 77577298Sobrien 77633965Sjdp /* 77760484Sobrien * If the outgoing packet contains a hop-by-hop options header, 77833965Sjdp * it must be examined and processed even by the source node. 77933965Sjdp * (RFC 2460, section 4.) 78033965Sjdp */ 78133965Sjdp if (exthdrs.ip6e_hbh) { 78233965Sjdp struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, 78333965Sjdp struct ip6_hbh *); 78433965Sjdp u_int32_t dummy1; /* XXX unused */ 78568765Sobrien u_int32_t dummy2; /* XXX unused */ 78668765Sobrien 78768765Sobrien /* 78868765Sobrien * XXX: if we have to send an ICMPv6 error to the sender, 789218822Sdim * we need the M_LOOP flag since icmp6_error() expects 79068765Sobrien * the IPv6 and the hop-by-hop options header are 79189857Sobrien * continuous unless the flag is set. 79268765Sobrien */ 79368765Sobrien m->m_flags |= M_LOOP; 79468765Sobrien m->m_pkthdr.rcvif = ifp; 79568765Sobrien if (ip6_process_hopopts(m, 79668765Sobrien (u_int8_t *)(hbh + 1), 79768765Sobrien ((hbh->ip6h_len + 1) << 3) - 79868765Sobrien sizeof(struct ip6_hbh), 79968765Sobrien &dummy1, &dummy2) < 0) { 80068765Sobrien /* m was already freed at this point */ 80168765Sobrien error = EINVAL;/* better error? */ 80268765Sobrien goto done; 80368765Sobrien } 80468765Sobrien m->m_flags &= ~M_LOOP; /* XXX */ 80568765Sobrien m->m_pkthdr.rcvif = NULL; 806218822Sdim } 80768765Sobrien 80889857Sobrien /* 80968765Sobrien * Send the packet to the outgoing interface. 81068765Sobrien * If necessary, do IPv6 fragmentation before sending. 81168765Sobrien */ 81268765Sobrien tlen = m->m_pkthdr.len; 81368765Sobrien if (tlen <= mtu 81468765Sobrien#ifdef notyet 81568765Sobrien /* 81668765Sobrien * On any link that cannot convey a 1280-octet packet in one piece, 81733965Sjdp * link-specific fragmentation and reassembly must be provided at 81833965Sjdp * a layer below IPv6. [RFC 2460, sec.5] 81933965Sjdp * Thus if the interface has ability of link-level fragmentation, 82089857Sobrien * we can just send the packet even if the packet size is 82133965Sjdp * larger than the link's MTU. 82233965Sjdp * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet... 82333965Sjdp */ 82433965Sjdp 82533965Sjdp || ifp->if_flags & IFF_FRAGMENTABLE 82633965Sjdp#endif 82733965Sjdp ) 82833965Sjdp { 82933965Sjdp#if defined(__NetBSD__) && defined(IFA_STATS) 83033965Sjdp if (IFA_STATS) { 83133965Sjdp struct in6_ifaddr *ia6; 83260484Sobrien ip6 = mtod(m, struct ip6_hdr *); 83333965Sjdp ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); 83433965Sjdp if (ia6) { 83533965Sjdp ia->ia_ifa.ifa_data.ifad_outbytes += 83633965Sjdp m->m_pkthdr.len; 83733965Sjdp } 83833965Sjdp } 83933965Sjdp#endif 84033965Sjdp error = nd6_output(ifp, m, dst, ro->ro_rt); 84133965Sjdp goto done; 84233965Sjdp } else if (mtu < IPV6_MMTU) { 84333965Sjdp /* 84433965Sjdp * note that path MTU is never less than IPV6_MMTU 84533965Sjdp * (see icmp6_input). 846218822Sdim */ 84733965Sjdp error = EMSGSIZE; 84860484Sobrien in6_ifstat_inc(ifp, ifs6_out_fragfail); 84960484Sobrien goto bad; 85089857Sobrien } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */ 85189857Sobrien error = EMSGSIZE; 85289857Sobrien in6_ifstat_inc(ifp, ifs6_out_fragfail); 85389857Sobrien goto bad; 85460484Sobrien } else { 85577298Sobrien struct mbuf **mnext, *m_frgpart; 85677298Sobrien struct ip6_frag *ip6f; 85777298Sobrien u_int32_t id = htonl(ip6_id++); 85860484Sobrien u_char nextproto; 85977298Sobrien 86077298Sobrien /* 86177298Sobrien * Too large for the destination or interface; 86291041Sobrien * fragment if possible. 86377298Sobrien * Must be able to put at least 8 bytes per fragment. 86477298Sobrien */ 86577298Sobrien hlen = unfragpartlen; 86668765Sobrien if (mtu > IPV6_MAXPACKET) 86789857Sobrien mtu = IPV6_MAXPACKET; 86889857Sobrien len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; 86989857Sobrien if (len < 8) { 87089857Sobrien error = EMSGSIZE; 87189857Sobrien in6_ifstat_inc(ifp, ifs6_out_fragfail); 87289857Sobrien goto bad; 87389857Sobrien } 87477298Sobrien 87577298Sobrien mnext = &m->m_nextpkt; 87677298Sobrien 87760484Sobrien /* 87860484Sobrien * Change the next header field of the last header in the 87960484Sobrien * unfragmentable part. 88060484Sobrien */ 88160484Sobrien if (exthdrs.ip6e_rthdr) { 88260484Sobrien nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); 88377298Sobrien *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; 88477298Sobrien } else if (exthdrs.ip6e_dest1) { 88577298Sobrien nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); 88677298Sobrien *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; 88777298Sobrien } else if (exthdrs.ip6e_hbh) { 888218822Sdim nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); 88977298Sobrien *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; 89077298Sobrien } else { 89177298Sobrien nextproto = ip6->ip6_nxt; 89277298Sobrien ip6->ip6_nxt = IPPROTO_FRAGMENT; 89377298Sobrien } 89477298Sobrien 895218822Sdim /* 89677298Sobrien * Loop through length of segment after first fragment, 89777298Sobrien * make new header and copy data of each part and link onto chain. 89877298Sobrien */ 89977298Sobrien m0 = m; 90077298Sobrien for (off = hlen; off < tlen; off += len) { 90177298Sobrien MGETHDR(m, M_DONTWAIT, MT_HEADER); 90277298Sobrien if (!m) { 90377298Sobrien error = ENOBUFS; 90477298Sobrien ip6stat.ip6s_odropped++; 90577298Sobrien goto sendorfree; 90677298Sobrien } 90777298Sobrien m->m_flags = m0->m_flags & M_COPYFLAGS; 908218822Sdim *mnext = m; 90977298Sobrien mnext = &m->m_nextpkt; 91077298Sobrien m->m_data += max_linkhdr; 91177298Sobrien mhip6 = mtod(m, struct ip6_hdr *); 91277298Sobrien *mhip6 = *ip6; 91377298Sobrien m->m_len = sizeof(*mhip6); 91477298Sobrien error = ip6_insertfraghdr(m0, m, hlen, &ip6f); 915130561Sobrien if (error) { 91677298Sobrien ip6stat.ip6s_odropped++; 91777298Sobrien goto sendorfree; 91877298Sobrien } 91977298Sobrien ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7)); 92077298Sobrien if (off + len >= tlen) 92177298Sobrien len = tlen - off; 92277298Sobrien else 92377298Sobrien ip6f->ip6f_offlg |= IP6F_MORE_FRAG; 92477298Sobrien mhip6->ip6_plen = htons((u_short)(len + hlen + 92577298Sobrien sizeof(*ip6f) - 92677298Sobrien sizeof(struct ip6_hdr))); 92777298Sobrien if ((m_frgpart = m_copy(m0, off, len)) == 0) { 92877298Sobrien error = ENOBUFS; 92977298Sobrien ip6stat.ip6s_odropped++; 93077298Sobrien goto sendorfree; 93177298Sobrien } 93277298Sobrien m_cat(m, m_frgpart); 93377298Sobrien m->m_pkthdr.len = len + hlen + sizeof(*ip6f); 93477298Sobrien m->m_pkthdr.rcvif = (struct ifnet *)0; 93577298Sobrien ip6f->ip6f_reserved = 0; 93677298Sobrien ip6f->ip6f_ident = id; 93777298Sobrien ip6f->ip6f_nxt = nextproto; 93877298Sobrien ip6stat.ip6s_ofragments++; 93977298Sobrien in6_ifstat_inc(ifp, ifs6_out_fragcreat); 94077298Sobrien } 94177298Sobrien 94277298Sobrien in6_ifstat_inc(ifp, ifs6_out_fragok); 943130561Sobrien } 944130561Sobrien 94577298Sobrien /* 94677298Sobrien * Remove leading garbages. 94777298Sobrien */ 94877298Sobriensendorfree: 94977298Sobrien m = m0->m_nextpkt; 95077298Sobrien m0->m_nextpkt = 0; 95177298Sobrien m_freem(m0); 95277298Sobrien for (m0 = m; m; m = m0) { 95377298Sobrien m0 = m->m_nextpkt; 95477298Sobrien m->m_nextpkt = 0; 95577298Sobrien if (error == 0) { 95677298Sobrien#if defined(__NetBSD__) && defined(IFA_STATS) 95777298Sobrien if (IFA_STATS) { 95877298Sobrien struct in6_ifaddr *ia6; 95977298Sobrien ip6 = mtod(m, struct ip6_hdr *); 96077298Sobrien ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); 96177298Sobrien if (ia6) { 96277298Sobrien ia->ia_ifa.ifa_data.ifad_outbytes += 96377298Sobrien m->m_pkthdr.len; 96477298Sobrien } 96577298Sobrien } 96677298Sobrien#endif 96777298Sobrien error = nd6_output(ifp, m, dst, ro->ro_rt); 96877298Sobrien } else 96977298Sobrien m_freem(m); 97077298Sobrien } 971218822Sdim 97277298Sobrien if (error == 0) 97377298Sobrien ip6stat.ip6s_fragmented++; 97477298Sobrien 97577298Sobriendone: 97677298Sobrien if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */ 97777298Sobrien RTFREE(ro->ro_rt); 97877298Sobrien } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) { 97977298Sobrien RTFREE(ro_pmtu->ro_rt); 98077298Sobrien } 98177298Sobrien 98277298Sobrien#ifdef IPSEC 98377298Sobrien if (sp != NULL) 98477298Sobrien key_freesp(sp); 98577298Sobrien#endif /* IPSEC */ 98677298Sobrien 98777298Sobrien return(error); 98877298Sobrien 98977298Sobrienfreehdrs: 99077298Sobrien m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */ 99177298Sobrien m_freem(exthdrs.ip6e_dest1); 992130561Sobrien m_freem(exthdrs.ip6e_rthdr); 993130561Sobrien m_freem(exthdrs.ip6e_dest2); 99477298Sobrien /* fall through */ 995130561Sobrienbad: 99677298Sobrien m_freem(m); 99777298Sobrien goto done; 99877298Sobrien} 99977298Sobrien 100077298Sobrienstatic int 100177298Sobrienip6_copyexthdr(mp, hdr, hlen) 100277298Sobrien struct mbuf **mp; 100377298Sobrien caddr_t hdr; 100460484Sobrien int hlen; 100560484Sobrien{ 100660484Sobrien struct mbuf *m; 1007130561Sobrien 100860484Sobrien if (hlen > MCLBYTES) 100960484Sobrien return(ENOBUFS); /* XXX */ 101060484Sobrien 1011218822Sdim MGET(m, M_DONTWAIT, MT_DATA); 101260484Sobrien if (!m) 101360484Sobrien return(ENOBUFS); 101433965Sjdp 101533965Sjdp if (hlen > MLEN) { 101633965Sjdp MCLGET(m, M_DONTWAIT); 101777298Sobrien if ((m->m_flags & M_EXT) == 0) { 101860484Sobrien m_free(m); 101960484Sobrien return(ENOBUFS); 102060484Sobrien } 102160484Sobrien } 102260484Sobrien m->m_len = hlen; 102360484Sobrien if (hdr) 102460484Sobrien bcopy(hdr, mtod(m, caddr_t), hlen); 102533965Sjdp 102633965Sjdp *mp = m; 102733965Sjdp return(0); 102833965Sjdp} 102933965Sjdp 103033965Sjdp/* 103133965Sjdp * Insert jumbo payload option. 103233965Sjdp */ 103333965Sjdpstatic int 103433965Sjdpip6_insert_jumboopt(exthdrs, plen) 103533965Sjdp struct ip6_exthdrs *exthdrs; 103633965Sjdp u_int32_t plen; 103733965Sjdp{ 103833965Sjdp struct mbuf *mopt; 103933965Sjdp u_char *optbuf; 104033965Sjdp 104133965Sjdp#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ 104233965Sjdp 104333965Sjdp /* 104433965Sjdp * If there is no hop-by-hop options header, allocate new one. 104533965Sjdp * If there is one but it doesn't have enough space to store the 104633965Sjdp * jumbo payload option, allocate a cluster to store the whole options. 104733965Sjdp * Otherwise, use it to store the options. 104833965Sjdp */ 104933965Sjdp if (exthdrs->ip6e_hbh == 0) { 105033965Sjdp MGET(mopt, M_DONTWAIT, MT_DATA); 105160484Sobrien if (mopt == 0) 105233965Sjdp return(ENOBUFS); 105333965Sjdp mopt->m_len = JUMBOOPTLEN; 105433965Sjdp optbuf = mtod(mopt, u_char *); 105533965Sjdp optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ 105660484Sobrien exthdrs->ip6e_hbh = mopt; 105733965Sjdp } else { 105833965Sjdp struct ip6_hbh *hbh; 105960484Sobrien 106060484Sobrien mopt = exthdrs->ip6e_hbh; 106160484Sobrien if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { 106260484Sobrien caddr_t oldoptp = mtod(mopt, caddr_t); 106360484Sobrien int oldoptlen = mopt->m_len; 106433965Sjdp 106533965Sjdp if (mopt->m_flags & M_EXT) 106660484Sobrien return(ENOBUFS); /* XXX */ 106760484Sobrien MCLGET(mopt, M_DONTWAIT); 106860484Sobrien if ((mopt->m_flags & M_EXT) == 0) 106960484Sobrien return(ENOBUFS); 107077298Sobrien 107133965Sjdp bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); 107233965Sjdp optbuf = mtod(mopt, caddr_t) + oldoptlen; 107360484Sobrien mopt->m_len = oldoptlen + JUMBOOPTLEN; 107460484Sobrien } else { 107560484Sobrien optbuf = mtod(mopt, u_char *) + mopt->m_len; 1076218822Sdim mopt->m_len += JUMBOOPTLEN; 107760484Sobrien } 107860484Sobrien optbuf[0] = IP6OPT_PADN; 107960484Sobrien optbuf[1] = 1; 108060484Sobrien 108160484Sobrien /* 108260484Sobrien * Adjust the header length according to the pad and 108360484Sobrien * the jumbo payload option. 108460484Sobrien */ 108560484Sobrien hbh = mtod(mopt, struct ip6_hbh *); 108660484Sobrien hbh->ip6h_len += (JUMBOOPTLEN >> 3); 108760484Sobrien } 108860484Sobrien 108960484Sobrien /* fill in the option. */ 109060484Sobrien optbuf[2] = IP6OPT_JUMBO; 109168765Sobrien optbuf[3] = 4; 109268765Sobrien *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); 109368765Sobrien 1094218822Sdim /* finally, adjust the packet header length */ 109568765Sobrien exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; 109668765Sobrien 109768765Sobrien return(0); 109868765Sobrien#undef JUMBOOPTLEN 109968765Sobrien} 110068765Sobrien 110168765Sobrien/* 110268765Sobrien * Insert fragment header and copy unfragmentable header portions. 110368765Sobrien */ 110468765Sobrienstatic int 1105218822Sdimip6_insertfraghdr(m0, m, hlen, frghdrp) 110668765Sobrien struct mbuf *m0, *m; 110768765Sobrien int hlen; 110868765Sobrien struct ip6_frag **frghdrp; 110968765Sobrien{ 111068765Sobrien struct mbuf *n, *mlast; 1111218822Sdim 111268765Sobrien if (hlen > sizeof(struct ip6_hdr)) { 111368765Sobrien n = m_copym(m0, sizeof(struct ip6_hdr), 111468765Sobrien hlen - sizeof(struct ip6_hdr), M_DONTWAIT); 111568765Sobrien if (n == 0) 1116218822Sdim return(ENOBUFS); 111768765Sobrien m->m_next = n; 111868765Sobrien } else 111968765Sobrien n = m; 112068765Sobrien 112168765Sobrien /* Search for the last mbuf of unfragmentable part. */ 1122218822Sdim for (mlast = n; mlast->m_next; mlast = mlast->m_next) 112368765Sobrien ; 112468765Sobrien 112568765Sobrien if ((mlast->m_flags & M_EXT) == 0 && 112668765Sobrien M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) { 1127218822Sdim /* use the trailing space of the last mbuf for the fragment hdr */ 112868765Sobrien *frghdrp = 112968765Sobrien (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); 113068765Sobrien mlast->m_len += sizeof(struct ip6_frag); 113168765Sobrien m->m_pkthdr.len += sizeof(struct ip6_frag); 113268765Sobrien } else { 1133218822Sdim /* allocate a new mbuf for the fragment header */ 113468765Sobrien struct mbuf *mfrg; 113568765Sobrien 113668765Sobrien MGET(mfrg, M_DONTWAIT, MT_DATA); 113768765Sobrien if (mfrg == 0) 1138218822Sdim return(ENOBUFS); 113968765Sobrien mfrg->m_len = sizeof(struct ip6_frag); 114068765Sobrien *frghdrp = mtod(mfrg, struct ip6_frag *); 114168765Sobrien mlast->m_next = mfrg; 114268765Sobrien } 114368765Sobrien 1144218822Sdim return(0); 114568765Sobrien} 114668765Sobrien 114768765Sobrien/* 114868765Sobrien * IP6 socket option processing. 114968765Sobrien */ 1150218822Sdimint 115168765Sobrienip6_ctloutput(so, sopt) 115268765Sobrien struct socket *so; 115368765Sobrien struct sockopt *sopt; 115468765Sobrien{ 115568765Sobrien int privileged; 115668765Sobrien register struct inpcb *in6p = sotoinpcb(so); 115768765Sobrien int error, optval; 115868765Sobrien int level, op, optname; 115968765Sobrien int optlen; 1160218822Sdim struct proc *p; 116133965Sjdp 116233965Sjdp if (sopt) { 116360484Sobrien level = sopt->sopt_level; 116433965Sjdp op = sopt->sopt_dir; 116533965Sjdp optname = sopt->sopt_name; 116633965Sjdp optlen = sopt->sopt_valsize; 116733965Sjdp p = sopt->sopt_p; 116860484Sobrien } else { 116960484Sobrien panic("ip6_ctloutput: arg soopt is NULL"); 117033965Sjdp } 117138889Sjdp error = optval = 0; 117238889Sjdp 117338889Sjdp privileged = (p == 0 || suser(p)) ? 0 : 1; 117438889Sjdp 117538889Sjdp if (level == IPPROTO_IPV6) { 117638889Sjdp switch (op) { 117738889Sjdp case SOPT_SET: 117860484Sobrien switch (optname) { 117938889Sjdp case IPV6_PKTOPTIONS: 118060484Sobrien { 118138889Sjdp struct mbuf *m; 118260484Sobrien 118360484Sobrien error = soopt_getm(sopt, &m); /* XXX */ 118460484Sobrien if (error != 0) 118560484Sobrien break; 118660484Sobrien error = soopt_mcopyin(sopt, m); /* XXX */ 118760484Sobrien if (error != 0) 118868765Sobrien break; 118938889Sjdp return (ip6_pcbopts(&in6p->in6p_outputopts, 119068765Sobrien m, so, sopt)); 119168765Sobrien } 119268765Sobrien case IPV6_HOPOPTS: 1193218822Sdim case IPV6_DSTOPTS: 119468765Sobrien if (!privileged) { 119568765Sobrien error = EPERM; 119668765Sobrien break; 119768765Sobrien } 119868765Sobrien /* fall through */ 119968765Sobrien case IPV6_UNICAST_HOPS: 120068765Sobrien case IPV6_RECVOPTS: 120168765Sobrien case IPV6_RECVRETOPTS: 120268765Sobrien case IPV6_RECVDSTADDR: 1203218822Sdim case IPV6_PKTINFO: 120468765Sobrien case IPV6_HOPLIMIT: 120568765Sobrien case IPV6_RTHDR: 120668765Sobrien case IPV6_CHECKSUM: 120768765Sobrien case IPV6_FAITH: 120868765Sobrien case IPV6_BINDV6ONLY: 120933965Sjdp if (optlen != sizeof(int)) 121033965Sjdp error = EINVAL; 121160484Sobrien else { 121233965Sjdp error = sooptcopyin(sopt, &optval, 121333965Sjdp sizeof optval, sizeof optval); 121433965Sjdp if (error) 121533965Sjdp break; 121633965Sjdp switch (optname) { 121733965Sjdp 121833965Sjdp case IPV6_UNICAST_HOPS: 121977298Sobrien if (optval < -1 || optval >= 256) 122033965Sjdp error = EINVAL; 122133965Sjdp else { 122233965Sjdp /* -1 = kernel default */ 122333965Sjdp in6p->in6p_hops = optval; 122433965Sjdp if ((in6p->in6p_vflag & 122533965Sjdp INP_IPV4) != 0) 122633965Sjdp in6p->inp_ip_ttl = optval; 122733965Sjdp } 122833965Sjdp break; 122933965Sjdp#define OPTSET(bit) \ 123033965Sjdp if (optval) \ 123133965Sjdp in6p->in6p_flags |= bit; \ 123233965Sjdp else \ 123333965Sjdp in6p->in6p_flags &= ~bit; 123433965Sjdp 123533965Sjdp case IPV6_RECVOPTS: 123633965Sjdp OPTSET(IN6P_RECVOPTS); 123733965Sjdp break; 123833965Sjdp 123933965Sjdp case IPV6_RECVRETOPTS: 124033965Sjdp OPTSET(IN6P_RECVRETOPTS); 124133965Sjdp break; 124233965Sjdp 124333965Sjdp case IPV6_RECVDSTADDR: 124433965Sjdp OPTSET(IN6P_RECVDSTADDR); 124533965Sjdp break; 124633965Sjdp 124733965Sjdp case IPV6_PKTINFO: 124833965Sjdp OPTSET(IN6P_PKTINFO); 124933965Sjdp break; 125033965Sjdp 125133965Sjdp case IPV6_HOPLIMIT: 125233965Sjdp OPTSET(IN6P_HOPLIMIT); 125333965Sjdp break; 125433965Sjdp 1255218822Sdim case IPV6_HOPOPTS: 1256218822Sdim OPTSET(IN6P_HOPOPTS); 125733965Sjdp break; 125833965Sjdp 125933965Sjdp case IPV6_DSTOPTS: 126033965Sjdp OPTSET(IN6P_DSTOPTS); 126138889Sjdp break; 126233965Sjdp 126333965Sjdp case IPV6_RTHDR: 126433965Sjdp OPTSET(IN6P_RTHDR); 126533965Sjdp break; 126633965Sjdp 126733965Sjdp case IPV6_CHECKSUM: 126833965Sjdp in6p->in6p_cksum = optval; 126933965Sjdp break; 127033965Sjdp 127133965Sjdp case IPV6_FAITH: 127233965Sjdp OPTSET(IN6P_FAITH); 127333965Sjdp break; 127460484Sobrien 127533965Sjdp case IPV6_BINDV6ONLY: 127660484Sobrien OPTSET(IN6P_BINDV6ONLY); 127760484Sobrien break; 127860484Sobrien } 127960484Sobrien } 128060484Sobrien break; 128160484Sobrien#undef OPTSET 128260484Sobrien 128360484Sobrien case IPV6_MULTICAST_IF: 128433965Sjdp case IPV6_MULTICAST_HOPS: 128533965Sjdp case IPV6_MULTICAST_LOOP: 128633965Sjdp case IPV6_JOIN_GROUP: 128733965Sjdp case IPV6_LEAVE_GROUP: 128833965Sjdp { 128960484Sobrien struct mbuf *m; 129033965Sjdp if (sopt->sopt_valsize > MLEN) { 129133965Sjdp error = EMSGSIZE; 129233965Sjdp break; 129333965Sjdp } 129433965Sjdp /* XXX */ 129533965Sjdp MGET(m, sopt->sopt_p ? M_WAIT : M_DONTWAIT, MT_HEADER); 129633965Sjdp if (m == 0) { 129733965Sjdp error = ENOBUFS; 129833965Sjdp break; 129933965Sjdp } 130033965Sjdp m->m_len = sopt->sopt_valsize; 130160484Sobrien error = sooptcopyin(sopt, mtod(m, char *), 130260484Sobrien m->m_len, m->m_len); 130360484Sobrien error = ip6_setmoptions(sopt->sopt_name, 130460484Sobrien &in6p->in6p_moptions, 130560484Sobrien m); 130633965Sjdp (void)m_free(m); 130760484Sobrien } 130860484Sobrien break; 130960484Sobrien 131060484Sobrien case IPV6_PORTRANGE: 131160484Sobrien error = sooptcopyin(sopt, &optval, sizeof optval, 131260484Sobrien sizeof optval); 131360484Sobrien if (error) 131433965Sjdp break; 131560484Sobrien 131660484Sobrien switch (optval) { 131760484Sobrien case IPV6_PORTRANGE_DEFAULT: 131860484Sobrien in6p->in6p_flags &= ~(IN6P_LOWPORT); 131960484Sobrien in6p->in6p_flags &= ~(IN6P_HIGHPORT); 132060484Sobrien break; 132133965Sjdp 132260484Sobrien case IPV6_PORTRANGE_HIGH: 132360484Sobrien in6p->in6p_flags &= ~(IN6P_LOWPORT); 132433965Sjdp in6p->in6p_flags |= IN6P_HIGHPORT; 132560484Sobrien break; 132633965Sjdp 132733965Sjdp case IPV6_PORTRANGE_LOW: 132833965Sjdp in6p->in6p_flags &= ~(IN6P_HIGHPORT); 132933965Sjdp in6p->in6p_flags |= IN6P_LOWPORT; 133033965Sjdp break; 133133965Sjdp 133260484Sobrien default: 133333965Sjdp error = EINVAL; 133433965Sjdp break; 133533965Sjdp } 133633965Sjdp break; 133733965Sjdp 133860484Sobrien#ifdef IPSEC 133933965Sjdp case IPV6_IPSEC_POLICY: 134060484Sobrien { 134160484Sobrien caddr_t req = NULL; 134260484Sobrien struct mbuf *m; 134360484Sobrien 134433965Sjdp if ((error = soopt_getm(sopt, &m)) 134533965Sjdp != 0) /* XXX */ 134633965Sjdp break; 134760484Sobrien if ((error = soopt_mcopyin(sopt, m)) 134860484Sobrien != 0) /* XXX */ 134960484Sobrien break; 135060484Sobrien if (m != 0) 135160484Sobrien req = mtod(m, caddr_t); 135260484Sobrien error = ipsec6_set_policy(in6p, optname, req, 135360484Sobrien privileged); 135460484Sobrien m_freem(m); 135560484Sobrien } 1356130561Sobrien break; 135760484Sobrien#endif /* IPSEC */ 135860484Sobrien 135960484Sobrien#ifdef IPV6FIREWALL 136060484Sobrien case IPV6_FW_ADD: 136160484Sobrien case IPV6_FW_DEL: 136260484Sobrien case IPV6_FW_FLUSH: 136333965Sjdp case IPV6_FW_ZERO: 136433965Sjdp { 136560484Sobrien struct mbuf *m; 136633965Sjdp struct mbuf **mp = &m; 136733965Sjdp 136833965Sjdp if (ip6_fw_ctl_ptr == NULL) 136933965Sjdp return EINVAL; 137033965Sjdp if ((error = soopt_getm(sopt, &m)) 137133965Sjdp != 0) /* XXX */ 137233965Sjdp break; 137333965Sjdp if ((error = soopt_mcopyin(sopt, m)) 137460484Sobrien != 0) /* XXX */ 137533965Sjdp break; 137633965Sjdp error = (*ip6_fw_ctl_ptr)(optname, mp); 137733965Sjdp m = *mp; 137833965Sjdp } 137960484Sobrien break; 138033965Sjdp#endif 138133965Sjdp 138233965Sjdp default: 138333965Sjdp error = ENOPROTOOPT; 138460484Sobrien break; 138560484Sobrien } 138660484Sobrien break; 138760484Sobrien 138860484Sobrien case SOPT_GET: 138960484Sobrien switch (optname) { 139060484Sobrien 139160484Sobrien case IPV6_OPTIONS: 139260484Sobrien case IPV6_RETOPTS: 139360484Sobrien error = ENOPROTOOPT; 139460484Sobrien break; 139533965Sjdp 139660484Sobrien case IPV6_PKTOPTIONS: 139733965Sjdp if (in6p->in6p_options) { 139833965Sjdp error = soopt_mcopyout(sopt, 139960484Sobrien in6p->in6p_options); 140033965Sjdp } else 140133965Sjdp sopt->sopt_valsize = 0; 140233965Sjdp break; 140333965Sjdp 140433965Sjdp case IPV6_HOPOPTS: 140560484Sobrien case IPV6_DSTOPTS: 140660484Sobrien if (!privileged) { 140733965Sjdp error = EPERM; 140833965Sjdp break; 140933965Sjdp } 141033965Sjdp /* fall through */ 141160484Sobrien case IPV6_UNICAST_HOPS: 141260484Sobrien case IPV6_RECVOPTS: 141333965Sjdp case IPV6_RECVRETOPTS: 141433965Sjdp case IPV6_RECVDSTADDR: 141533965Sjdp case IPV6_PKTINFO: 141633965Sjdp case IPV6_HOPLIMIT: 141733965Sjdp case IPV6_RTHDR: 141833965Sjdp case IPV6_CHECKSUM: 141933965Sjdp case IPV6_FAITH: 142033965Sjdp case IPV6_BINDV6ONLY: 142133965Sjdp case IPV6_PORTRANGE: 142233965Sjdp switch (optname) { 142333965Sjdp 142433965Sjdp case IPV6_UNICAST_HOPS: 142533965Sjdp optval = in6p->in6p_hops; 142633965Sjdp break; 142733965Sjdp 142833965Sjdp#define OPTBIT(bit) (in6p->in6p_flags & bit ? 1 : 0) 142933965Sjdp 143033965Sjdp case IPV6_RECVOPTS: 143133965Sjdp optval = OPTBIT(IN6P_RECVOPTS); 143268765Sobrien break; 143338889Sjdp 143438889Sjdp case IPV6_RECVRETOPTS: 143538889Sjdp optval = OPTBIT(IN6P_RECVRETOPTS); 143638889Sjdp break; 143738889Sjdp 143838889Sjdp case IPV6_RECVDSTADDR: 143938889Sjdp optval = OPTBIT(IN6P_RECVDSTADDR); 144038889Sjdp break; 144138889Sjdp 144238889Sjdp case IPV6_PKTINFO: 144338889Sjdp optval = OPTBIT(IN6P_PKTINFO); 144438889Sjdp break; 144538889Sjdp 144638889Sjdp case IPV6_HOPLIMIT: 144738889Sjdp optval = OPTBIT(IN6P_HOPLIMIT); 144838889Sjdp break; 144938889Sjdp 145038889Sjdp case IPV6_HOPOPTS: 145160484Sobrien optval = OPTBIT(IN6P_HOPOPTS); 145260484Sobrien break; 145360484Sobrien 145460484Sobrien case IPV6_DSTOPTS: 145560484Sobrien optval = OPTBIT(IN6P_DSTOPTS); 145660484Sobrien break; 145777298Sobrien 145860484Sobrien case IPV6_RTHDR: 145960484Sobrien optval = OPTBIT(IN6P_RTHDR); 146060484Sobrien break; 146160484Sobrien 146233965Sjdp case IPV6_CHECKSUM: 146333965Sjdp optval = in6p->in6p_cksum; 146438889Sjdp break; 146568765Sobrien 146638889Sjdp case IPV6_FAITH: 146738889Sjdp optval = OPTBIT(IN6P_FAITH); 146860484Sobrien break; 146960484Sobrien 147038889Sjdp case IPV6_BINDV6ONLY: 147138889Sjdp optval = OPTBIT(IN6P_BINDV6ONLY); 147238889Sjdp break; 147338889Sjdp 147438889Sjdp case IPV6_PORTRANGE: 147538889Sjdp { 147638889Sjdp int flags; 147760484Sobrien 147838889Sjdp flags = in6p->in6p_flags; 147933965Sjdp if (flags & IN6P_HIGHPORT) 148033965Sjdp optval = IPV6_PORTRANGE_HIGH; 148133965Sjdp else if (flags & IN6P_LOWPORT) 148233965Sjdp optval = IPV6_PORTRANGE_LOW; 148333965Sjdp else 148433965Sjdp optval = 0; 148533965Sjdp break; 148633965Sjdp } 148733965Sjdp } 148833965Sjdp error = sooptcopyout(sopt, &optval, 148933965Sjdp sizeof optval); 149033965Sjdp break; 149160484Sobrien 149233965Sjdp case IPV6_MULTICAST_IF: 149333965Sjdp case IPV6_MULTICAST_HOPS: 149433965Sjdp case IPV6_MULTICAST_LOOP: 149533965Sjdp case IPV6_JOIN_GROUP: 149633965Sjdp case IPV6_LEAVE_GROUP: 149733965Sjdp { 149833965Sjdp struct mbuf *m; 149933965Sjdp error = ip6_getmoptions(sopt->sopt_name, 150033965Sjdp in6p->in6p_moptions, &m); 150133965Sjdp if (error == 0) 150233965Sjdp error = sooptcopyout(sopt, 150333965Sjdp mtod(m, char *), m->m_len); 150460484Sobrien m_freem(m); 150560484Sobrien } 150660484Sobrien break; 150760484Sobrien 150833965Sjdp#ifdef IPSEC 150938889Sjdp case IPV6_IPSEC_POLICY: 151038889Sjdp { 151138889Sjdp caddr_t req = NULL; 151238889Sjdp int len = 0; 151333965Sjdp struct mbuf *m; 151433965Sjdp struct mbuf **mp = &m; 151533965Sjdp 151633965Sjdp if (m != 0) { 151733965Sjdp req = mtod(m, caddr_t); 151833965Sjdp len = m->m_len; 151933965Sjdp } 152033965Sjdp error = ipsec6_get_policy(in6p, req, mp); 152133965Sjdp if (error == 0) 152233965Sjdp error = soopt_mcopyout(sopt, m); /*XXX*/ 152360484Sobrien m_freem(m); 152433965Sjdp break; 152533965Sjdp } 152633965Sjdp#endif /* IPSEC */ 152733965Sjdp 152833965Sjdp#ifdef IPV6FIREWALL 152960484Sobrien case IPV6_FW_GET: 153033965Sjdp { 153160484Sobrien struct mbuf *m; 153260484Sobrien struct mbuf **mp = &m; 153360484Sobrien 153460484Sobrien if (ip6_fw_ctl_ptr == NULL) 153560484Sobrien { 153660484Sobrien return EINVAL; 153760484Sobrien } 153833965Sjdp error = (*ip6_fw_ctl_ptr)(optname, mp); 153960484Sobrien if (error == 0) 154033965Sjdp error = soopt_mcopyout(sopt, m); /* XXX */ 154133965Sjdp if (m) 154233965Sjdp m_freem(m); 154333965Sjdp } 154433965Sjdp break; 154533965Sjdp#endif 1546218822Sdim 1547218822Sdim default: 154833965Sjdp error = ENOPROTOOPT; 154933965Sjdp break; 155033965Sjdp } 155133965Sjdp break; 155233965Sjdp } 155333965Sjdp } else { 155433965Sjdp error = EINVAL; 155533965Sjdp } 155633965Sjdp return(error); 155733965Sjdp} 155833965Sjdp 155933965Sjdp/* 156033965Sjdp * Set up IP6 options in pcb for insertion in output packets. 156133965Sjdp * Store in mbuf with pointer in pcbopt, adding pseudo-option 156233965Sjdp * with destination address if source routed. 156333965Sjdp */ 156433965Sjdpstatic int 156533965Sjdpip6_pcbopts(pktopt, m, so, sopt) 156633965Sjdp struct ip6_pktopts **pktopt; 1567218822Sdim register struct mbuf *m; 1568218822Sdim struct socket *so; 156960484Sobrien struct sockopt *sopt; 157060484Sobrien{ 157160484Sobrien register struct ip6_pktopts *opt = *pktopt; 157260484Sobrien int error = 0; 157360484Sobrien struct proc *p = sopt->sopt_p; 157460484Sobrien int priv = 0; 157560484Sobrien 157660484Sobrien /* turn off any old options. */ 157760484Sobrien if (opt) { 157860484Sobrien if (opt->ip6po_m) 157960484Sobrien (void)m_free(opt->ip6po_m); 158060484Sobrien } else 158160484Sobrien opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK); 158260484Sobrien *pktopt = 0; 158360484Sobrien 158460484Sobrien if (!m || m->m_len == 0) { 158560484Sobrien /* 158660484Sobrien * Only turning off any previous options. 158760484Sobrien */ 158860484Sobrien if (opt) 158960484Sobrien free(opt, M_IP6OPT); 159060484Sobrien if (m) 159160484Sobrien (void)m_free(m); 159260484Sobrien return(0); 159360484Sobrien } 159460484Sobrien 159560484Sobrien /* set options specified by user. */ 159660484Sobrien if (p && !suser(p)) 159760484Sobrien priv = 1; 159860484Sobrien if ((error = ip6_setpktoptions(m, opt, priv)) != 0) { 159960484Sobrien (void)m_free(m); 160060484Sobrien return(error); 160160484Sobrien } 160260484Sobrien *pktopt = opt; 160360484Sobrien return(0); 160460484Sobrien} 160560484Sobrien 160660484Sobrien/* 160760484Sobrien * Set the IP6 multicast options in response to user setsockopt(). 160860484Sobrien */ 160960484Sobrienstatic int 161060484Sobrienip6_setmoptions(optname, im6op, m) 161160484Sobrien int optname; 161260484Sobrien struct ip6_moptions **im6op; 161360484Sobrien struct mbuf *m; 161460484Sobrien{ 161560484Sobrien int error = 0; 161660484Sobrien u_int loop, ifindex; 161760484Sobrien struct ipv6_mreq *mreq; 161860484Sobrien struct ifnet *ifp; 161960484Sobrien struct ip6_moptions *im6o = *im6op; 162060484Sobrien struct route_in6 ro; 162160484Sobrien struct sockaddr_in6 *dst; 162260484Sobrien struct in6_multi_mship *imm; 162360484Sobrien struct proc *p = curproc; /* XXX */ 162460484Sobrien 162560484Sobrien if (im6o == NULL) { 162660484Sobrien /* 162760484Sobrien * No multicast option buffer attached to the pcb; 162860484Sobrien * allocate one and initialize to default values. 1629218822Sdim */ 1630218822Sdim im6o = (struct ip6_moptions *) 163160484Sobrien malloc(sizeof(*im6o), M_IPMOPTS, M_WAITOK); 163260484Sobrien 163360484Sobrien if (im6o == NULL) 163460484Sobrien return(ENOBUFS); 163560484Sobrien *im6op = im6o; 163660484Sobrien im6o->im6o_multicast_ifp = NULL; 163760484Sobrien im6o->im6o_multicast_hlim = ip6_defmcasthlim; 163860484Sobrien im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; 163960484Sobrien LIST_INIT(&im6o->im6o_memberships); 164060484Sobrien } 164160484Sobrien 164260484Sobrien switch (optname) { 164360484Sobrien 164460484Sobrien case IPV6_MULTICAST_IF: 164560484Sobrien /* 164660484Sobrien * Select the interface for outgoing multicast packets. 164760484Sobrien */ 164860484Sobrien if (m == NULL || m->m_len != sizeof(u_int)) { 164977298Sobrien error = EINVAL; 165060484Sobrien break; 165160484Sobrien } 165260484Sobrien ifindex = *(mtod(m, u_int *)); 165360484Sobrien if (ifindex < 0 || if_index < ifindex) { 165460484Sobrien error = ENXIO; /* XXX EINVAL? */ 165560484Sobrien break; 165660484Sobrien } 165760484Sobrien ifp = ifindex2ifnet[ifindex]; 165860484Sobrien if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { 165960484Sobrien error = EADDRNOTAVAIL; 166060484Sobrien break; 166160484Sobrien } 166260484Sobrien im6o->im6o_multicast_ifp = ifp; 166360484Sobrien break; 166460484Sobrien 166560484Sobrien case IPV6_MULTICAST_HOPS: 166660484Sobrien { 166760484Sobrien /* 166860484Sobrien * Set the IP6 hoplimit for outgoing multicast packets. 166960484Sobrien */ 167060484Sobrien int optval; 167160484Sobrien if (m == NULL || m->m_len != sizeof(int)) { 167260484Sobrien error = EINVAL; 167360484Sobrien break; 167460484Sobrien } 167560484Sobrien optval = *(mtod(m, u_int *)); 167660484Sobrien if (optval < -1 || optval >= 256) 167760484Sobrien error = EINVAL; 167860484Sobrien else if (optval == -1) 167960484Sobrien im6o->im6o_multicast_hlim = ip6_defmcasthlim; 168060484Sobrien else 168160484Sobrien im6o->im6o_multicast_hlim = optval; 168260484Sobrien break; 168360484Sobrien } 168460484Sobrien 168560484Sobrien case IPV6_MULTICAST_LOOP: 1686218822Sdim /* 1687218822Sdim * Set the loopback flag for outgoing multicast packets. 168860484Sobrien * Must be zero or one. 168960484Sobrien */ 169060484Sobrien if (m == NULL || m->m_len != sizeof(u_int) || 169160484Sobrien (loop = *(mtod(m, u_int *))) > 1) { 169260484Sobrien error = EINVAL; 169360484Sobrien break; 169460484Sobrien } 169560484Sobrien im6o->im6o_multicast_loop = loop; 169660484Sobrien break; 169760484Sobrien 169860484Sobrien case IPV6_JOIN_GROUP: 169968765Sobrien /* 170068765Sobrien * Add a multicast group membership. 1701104834Sobrien * Group must be a valid IP6 multicast address. 170268765Sobrien */ 170368765Sobrien if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { 170460484Sobrien error = EINVAL; 170560484Sobrien break; 1706130561Sobrien } 1707130561Sobrien mreq = mtod(m, struct ipv6_mreq *); 1708130561Sobrien if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { 1709130561Sobrien /* 1710130561Sobrien * We use the unspecified address to specify to accept 1711130561Sobrien * all multicast addresses. Only super user is allowed 1712130561Sobrien * to do this. 1713130561Sobrien */ 1714130561Sobrien if (suser(p)) { 1715130561Sobrien error = EACCES; 1716130561Sobrien break; 1717130561Sobrien } 1718130561Sobrien } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { 1719130561Sobrien error = EINVAL; 1720130561Sobrien break; 1721130561Sobrien } 1722130561Sobrien 1723130561Sobrien /* 1724130561Sobrien * If the interface is specified, validate it. 172560484Sobrien */ 1726104834Sobrien if (mreq->ipv6mr_interface < 0 1727104834Sobrien || if_index < mreq->ipv6mr_interface) { 1728130561Sobrien error = ENXIO; /* XXX EINVAL? */ 1729130561Sobrien break; 1730130561Sobrien } 1731130561Sobrien /* 1732130561Sobrien * If no interface was explicitly specified, choose an 1733130561Sobrien * appropriate one according to the given multicast address. 1734104834Sobrien */ 1735104834Sobrien if (mreq->ipv6mr_interface == 0) { 1736104834Sobrien /* 1737104834Sobrien * If the multicast address is in node-local scope, 1738104834Sobrien * the interface should be a loopback interface. 1739104834Sobrien * Otherwise, look up the routing table for the 1740104834Sobrien * address, and choose the outgoing interface. 1741104834Sobrien * XXX: is it a good approach? 1742104834Sobrien */ 174360484Sobrien if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) { 174468765Sobrien ifp = &loif[0]; 174568765Sobrien } else { 174668765Sobrien ro.ro_rt = NULL; 174768765Sobrien dst = (struct sockaddr_in6 *)&ro.ro_dst; 1748104834Sobrien bzero(dst, sizeof(*dst)); 174968765Sobrien dst->sin6_len = sizeof(struct sockaddr_in6); 175068765Sobrien dst->sin6_family = AF_INET6; 175168765Sobrien dst->sin6_addr = mreq->ipv6mr_multiaddr; 175268765Sobrien rtalloc((struct route *)&ro); 175360484Sobrien if (ro.ro_rt == NULL) { 175460484Sobrien error = EADDRNOTAVAIL; 175560484Sobrien break; 175660484Sobrien } 175760484Sobrien ifp = ro.ro_rt->rt_ifp; 175860484Sobrien rtfree(ro.ro_rt); 175968765Sobrien } 176068765Sobrien } else 176168765Sobrien ifp = ifindex2ifnet[mreq->ipv6mr_interface]; 176268765Sobrien 176368765Sobrien /* 176468765Sobrien * See if we found an interface, and confirm that it 176568765Sobrien * supports multicast 1766104834Sobrien */ 176768765Sobrien if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { 176868765Sobrien error = EADDRNOTAVAIL; 176960484Sobrien break; 177060484Sobrien } 177160484Sobrien /* 177260484Sobrien * Put interface index into the multicast address, 177360484Sobrien * if the address has link-local scope. 1774130561Sobrien */ 177560484Sobrien if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { 177660484Sobrien mreq->ipv6mr_multiaddr.s6_addr16[1] 177760484Sobrien = htons(mreq->ipv6mr_interface); 177860484Sobrien } 177960484Sobrien /* 178060484Sobrien * See if the membership already exists. 178160484Sobrien */ 1782218822Sdim LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) 1783218822Sdim if (imm->i6mm_maddr->in6m_ifp == ifp && 178460484Sobrien IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, 178560484Sobrien &mreq->ipv6mr_multiaddr)) 178660484Sobrien break; 178760484Sobrien if (imm != NULL) { 178860484Sobrien error = EADDRINUSE; 178960484Sobrien break; 179060484Sobrien } 179160484Sobrien /* 179260484Sobrien * Everything looks good; add a new record to the multicast 179377298Sobrien * address list for the given interface. 179460484Sobrien */ 179560484Sobrien imm = malloc(sizeof(*imm), M_IPMADDR, M_WAITOK); 179660484Sobrien if (imm == NULL) { 179760484Sobrien error = ENOBUFS; 179860484Sobrien break; 179960484Sobrien } 180060484Sobrien if ((imm->i6mm_maddr = 180160484Sobrien in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) { 180277298Sobrien free(imm, M_IPMADDR); 180360484Sobrien break; 180460484Sobrien } 180560484Sobrien LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); 180660484Sobrien break; 180760484Sobrien 180860484Sobrien case IPV6_LEAVE_GROUP: 180960484Sobrien /* 181060484Sobrien * Drop a multicast group membership. 181160484Sobrien * Group must be a valid IP6 multicast address. 181277298Sobrien */ 181360484Sobrien if (m == NULL || m->m_len != sizeof(struct ipv6_mreq)) { 181460484Sobrien error = EINVAL; 181560484Sobrien break; 181660484Sobrien } 181760484Sobrien mreq = mtod(m, struct ipv6_mreq *); 181860484Sobrien if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { 181960484Sobrien if (suser(p)) { 182060484Sobrien error = EACCES; 182160484Sobrien break; 182260484Sobrien } 1823218822Sdim } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { 1824218822Sdim error = EINVAL; 182560484Sobrien break; 182660484Sobrien } 182760484Sobrien /* 182860484Sobrien * If an interface address was specified, get a pointer 182960484Sobrien * to its ifnet structure. 183060484Sobrien */ 183160484Sobrien if (mreq->ipv6mr_interface < 0 183260484Sobrien || if_index < mreq->ipv6mr_interface) { 183360484Sobrien error = ENXIO; /* XXX EINVAL? */ 183460484Sobrien break; 183560484Sobrien } 183660484Sobrien ifp = ifindex2ifnet[mreq->ipv6mr_interface]; 183760484Sobrien /* 183860484Sobrien * Put interface index into the multicast address, 183960484Sobrien * if the address has link-local scope. 184060484Sobrien */ 184160484Sobrien if (IN6_IS_ADDR_MC_LINKLOCAL(&mreq->ipv6mr_multiaddr)) { 184260484Sobrien mreq->ipv6mr_multiaddr.s6_addr16[1] 184360484Sobrien = htons(mreq->ipv6mr_interface); 184460484Sobrien } 184560484Sobrien /* 184660484Sobrien * Find the membership in the membership list. 184760484Sobrien */ 184860484Sobrien LIST_FOREACH(imm, &im6o->im6o_memberships, i6mm_chain) { 184960484Sobrien if ((ifp == NULL || 185060484Sobrien imm->i6mm_maddr->in6m_ifp == ifp) && 185160484Sobrien IN6_ARE_ADDR_EQUAL(&imm->i6mm_maddr->in6m_addr, 185260484Sobrien &mreq->ipv6mr_multiaddr)) 185360484Sobrien break; 185460484Sobrien } 185560484Sobrien if (imm == NULL) { 185660484Sobrien /* Unable to resolve interface */ 185760484Sobrien error = EADDRNOTAVAIL; 185860484Sobrien break; 185960484Sobrien } 186060484Sobrien /* 186160484Sobrien * Give up the multicast address record to which the 186260484Sobrien * membership points. 186360484Sobrien */ 186460484Sobrien LIST_REMOVE(imm, i6mm_chain); 186560484Sobrien in6_delmulti(imm->i6mm_maddr); 186660484Sobrien free(imm, M_IPMADDR); 186760484Sobrien break; 186860484Sobrien 186960484Sobrien default: 187060484Sobrien error = EOPNOTSUPP; 187160484Sobrien break; 187260484Sobrien } 187360484Sobrien 187460484Sobrien /* 187560484Sobrien * If all options have default values, no need to keep the mbuf. 187660484Sobrien */ 187760484Sobrien if (im6o->im6o_multicast_ifp == NULL && 187860484Sobrien im6o->im6o_multicast_hlim == ip6_defmcasthlim && 187960484Sobrien im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && 188060484Sobrien LIST_EMPTY(&im6o->im6o_memberships)) { 188160484Sobrien free(*im6op, M_IPMOPTS); 188260484Sobrien *im6op = NULL; 188360484Sobrien } 188460484Sobrien 188560484Sobrien return(error); 188660484Sobrien} 188760484Sobrien 188860484Sobrien/* 188960484Sobrien * Return the IP6 multicast options in response to user getsockopt(). 189060484Sobrien */ 189160484Sobrienstatic int 189260484Sobrienip6_getmoptions(optname, im6o, mp) 189360484Sobrien int optname; 1894218822Sdim register struct ip6_moptions *im6o; 189560484Sobrien register struct mbuf **mp; 189660484Sobrien{ 189760484Sobrien u_int *hlim, *loop, *ifindex; 189860484Sobrien 189960484Sobrien *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/ 190060484Sobrien 190160484Sobrien switch (optname) { 190260484Sobrien 190360484Sobrien case IPV6_MULTICAST_IF: 190460484Sobrien ifindex = mtod(*mp, u_int *); 190560484Sobrien (*mp)->m_len = sizeof(u_int); 190660484Sobrien if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) 190760484Sobrien *ifindex = 0; 190860484Sobrien else 190960484Sobrien *ifindex = im6o->im6o_multicast_ifp->if_index; 191060484Sobrien return(0); 191160484Sobrien 191260484Sobrien case IPV6_MULTICAST_HOPS: 191360484Sobrien hlim = mtod(*mp, u_int *); 191460484Sobrien (*mp)->m_len = sizeof(u_int); 191560484Sobrien if (im6o == NULL) 191660484Sobrien *hlim = ip6_defmcasthlim; 191760484Sobrien else 191860484Sobrien *hlim = im6o->im6o_multicast_hlim; 191960484Sobrien return(0); 192060484Sobrien 192160484Sobrien case IPV6_MULTICAST_LOOP: 192260484Sobrien loop = mtod(*mp, u_int *); 192360484Sobrien (*mp)->m_len = sizeof(u_int); 192460484Sobrien if (im6o == NULL) 192560484Sobrien *loop = ip6_defmcasthlim; 192660484Sobrien else 192760484Sobrien *loop = im6o->im6o_multicast_loop; 192868765Sobrien return(0); 192960484Sobrien 193060484Sobrien default: 193160484Sobrien return(EOPNOTSUPP); 1932218822Sdim } 1933218822Sdim} 1934218822Sdim 193533965Sjdp/* 193633965Sjdp * Discard the IP6 multicast options. 193733965Sjdp */ 193833965Sjdpvoid 193933965Sjdpip6_freemoptions(im6o) 194038889Sjdp register struct ip6_moptions *im6o; 194133965Sjdp{ 194233965Sjdp struct in6_multi_mship *imm; 194333965Sjdp 194438889Sjdp if (im6o == NULL) 194533965Sjdp return; 194638889Sjdp 194760484Sobrien while ((imm = LIST_FIRST(&im6o->im6o_memberships)) != NULL) { 194838889Sjdp LIST_REMOVE(imm, i6mm_chain); 194960484Sobrien if (imm->i6mm_maddr) 195060484Sobrien in6_delmulti(imm->i6mm_maddr); 195160484Sobrien free(imm, M_IPMADDR); 195260484Sobrien } 195360484Sobrien free(im6o, M_IPMOPTS); 195460484Sobrien} 195560484Sobrien 195660484Sobrien/* 195760484Sobrien * Set IPv6 outgoing packet options based on advanced API. 195860484Sobrien */ 195960484Sobrienint 196060484Sobrienip6_setpktoptions(control, opt, priv) 196160484Sobrien struct mbuf *control; 196260484Sobrien struct ip6_pktopts *opt; 196360484Sobrien int priv; 196460484Sobrien{ 196560484Sobrien register struct cmsghdr *cm = 0; 196660484Sobrien 196760484Sobrien if (control == 0 || opt == 0) 196860484Sobrien return(EINVAL); 196960484Sobrien 197060484Sobrien bzero(opt, sizeof(*opt)); 197138889Sjdp opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ 197260484Sobrien 197338889Sjdp /* 197460484Sobrien * XXX: Currently, we assume all the optional information is stored 197560484Sobrien * in a single mbuf. 197660484Sobrien */ 197760484Sobrien if (control->m_next) 197860484Sobrien return(EINVAL); 197960484Sobrien 198060484Sobrien opt->ip6po_m = control; 198160484Sobrien 198260484Sobrien for (; control->m_len; control->m_data += CMSG_ALIGN(cm->cmsg_len), 198360484Sobrien control->m_len -= CMSG_ALIGN(cm->cmsg_len)) { 198460484Sobrien cm = mtod(control, struct cmsghdr *); 198560484Sobrien if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) 198660484Sobrien return(EINVAL); 198760484Sobrien if (cm->cmsg_level != IPPROTO_IPV6) 198838889Sjdp continue; 198933965Sjdp 199038889Sjdp switch(cm->cmsg_type) { 199138889Sjdp case IPV6_PKTINFO: 199233965Sjdp if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) 199333965Sjdp return(EINVAL); 199433965Sjdp opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); 199533965Sjdp if (opt->ip6po_pktinfo->ipi6_ifindex && 199633965Sjdp IN6_IS_ADDR_LINKLOCAL(&opt->ip6po_pktinfo->ipi6_addr)) 199738889Sjdp opt->ip6po_pktinfo->ipi6_addr.s6_addr16[1] = 199838889Sjdp htons(opt->ip6po_pktinfo->ipi6_ifindex); 199938889Sjdp 2000218822Sdim if (opt->ip6po_pktinfo->ipi6_ifindex > if_index 200138889Sjdp || opt->ip6po_pktinfo->ipi6_ifindex < 0) { 200238889Sjdp return(ENXIO); 200338889Sjdp } 200438889Sjdp 200533965Sjdp if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { 200633965Sjdp struct ifaddr *ia; 200733965Sjdp struct sockaddr_in6 sin6; 200833965Sjdp 200933965Sjdp bzero(&sin6, sizeof(sin6)); 201033965Sjdp sin6.sin6_len = sizeof(sin6); 201133965Sjdp sin6.sin6_family = AF_INET6; 201233965Sjdp sin6.sin6_addr = 201333965Sjdp opt->ip6po_pktinfo->ipi6_addr; 201433965Sjdp ia = ifa_ifwithaddr(sin6tosa(&sin6)); 201533965Sjdp if (ia == NULL || 201633965Sjdp (opt->ip6po_pktinfo->ipi6_ifindex && 201733965Sjdp (ia->ifa_ifp->if_index != 201833965Sjdp opt->ip6po_pktinfo->ipi6_ifindex))) { 201933965Sjdp return(EADDRNOTAVAIL); 202038889Sjdp } 202138889Sjdp /* 202238889Sjdp * Check if the requested source address is 202338889Sjdp * indeed a unicast address assigned to the 202438889Sjdp * node. 2025218822Sdim */ 202638889Sjdp if (IN6_IS_ADDR_MULTICAST(&opt->ip6po_pktinfo->ipi6_addr)) 202738889Sjdp return(EADDRNOTAVAIL); 202838889Sjdp } 202933965Sjdp break; 203033965Sjdp 203133965Sjdp case IPV6_HOPLIMIT: 203233965Sjdp if (cm->cmsg_len != CMSG_LEN(sizeof(int))) 203333965Sjdp return(EINVAL); 203433965Sjdp 203533965Sjdp opt->ip6po_hlim = *(int *)CMSG_DATA(cm); 203660484Sobrien if (opt->ip6po_hlim < -1 || opt->ip6po_hlim > 255) 203760484Sobrien return(EINVAL); 203860484Sobrien break; 203960484Sobrien 204060484Sobrien case IPV6_NEXTHOP: 204160484Sobrien if (!priv) 204260484Sobrien return(EPERM); 204360484Sobrien if (cm->cmsg_len < sizeof(u_char) || 204460484Sobrien cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) 204560484Sobrien return(EINVAL); 204660484Sobrien 204760484Sobrien opt->ip6po_nexthop = (struct sockaddr *)CMSG_DATA(cm); 204860484Sobrien 204960484Sobrien break; 205060484Sobrien 205160484Sobrien case IPV6_HOPOPTS: 205260484Sobrien if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_hbh))) 2053218822Sdim return(EINVAL); 205460484Sobrien opt->ip6po_hbh = (struct ip6_hbh *)CMSG_DATA(cm); 205560484Sobrien if (cm->cmsg_len != 205660484Sobrien CMSG_LEN((opt->ip6po_hbh->ip6h_len + 1) << 3)) 205760484Sobrien return(EINVAL); 205860484Sobrien break; 205960484Sobrien 206060484Sobrien case IPV6_DSTOPTS: 206160484Sobrien if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) 206260484Sobrien return(EINVAL); 206360484Sobrien 206433965Sjdp /* 206533965Sjdp * If there is no routing header yet, the destination 206638889Sjdp * options header should be put on the 1st part. 206738889Sjdp * Otherwise, the header should be on the 2nd part. 206838889Sjdp * (See RFC 2460, section 4.1) 206933965Sjdp */ 207060484Sobrien if (opt->ip6po_rthdr == NULL) { 207133965Sjdp opt->ip6po_dest1 = 207233965Sjdp (struct ip6_dest *)CMSG_DATA(cm); 207333965Sjdp if (cm->cmsg_len != 207433965Sjdp CMSG_LEN((opt->ip6po_dest1->ip6d_len + 1) 207560484Sobrien << 3)) 207638889Sjdp return(EINVAL); 207738889Sjdp } else { 207838889Sjdp opt->ip6po_dest2 = 207938889Sjdp (struct ip6_dest *)CMSG_DATA(cm); 208038889Sjdp if (cm->cmsg_len != 208138889Sjdp CMSG_LEN((opt->ip6po_dest2->ip6d_len + 1) 208238889Sjdp << 3)) 208338889Sjdp return(EINVAL); 208438889Sjdp } 208560484Sobrien break; 208660484Sobrien 208738889Sjdp case IPV6_RTHDR: 208860484Sobrien if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_rthdr))) 208938889Sjdp return(EINVAL); 209060484Sobrien opt->ip6po_rthdr = (struct ip6_rthdr *)CMSG_DATA(cm); 209160484Sobrien if (cm->cmsg_len != 209260484Sobrien CMSG_LEN((opt->ip6po_rthdr->ip6r_len + 1) << 3)) 209360484Sobrien return(EINVAL); 209433965Sjdp switch(opt->ip6po_rthdr->ip6r_type) { 209560484Sobrien case IPV6_RTHDR_TYPE_0: 209638889Sjdp if (opt->ip6po_rthdr->ip6r_segleft == 0) 209738889Sjdp return(EINVAL); 209838889Sjdp break; 2099218822Sdim default: 210038889Sjdp return(EINVAL); 210138889Sjdp } 210260484Sobrien break; 210338889Sjdp 210438889Sjdp default: 210538889Sjdp return(ENOPROTOOPT); 210633965Sjdp } 210733965Sjdp } 210833965Sjdp 210938889Sjdp return(0); 211038889Sjdp} 211138889Sjdp 211238889Sjdp/* 211338889Sjdp * Routine called from ip6_output() to loop back a copy of an IP6 multicast 211438889Sjdp * packet to the input queue of a specified interface. Note that this 211538889Sjdp * calls the output routine of the loopback "driver", but with an interface 211638889Sjdp * pointer that might NOT be &loif -- easier than replicating that code here. 211738889Sjdp */ 211838889Sjdpvoid 211960484Sobrienip6_mloopback(ifp, m, dst) 212060484Sobrien struct ifnet *ifp; 2121130561Sobrien register struct mbuf *m; 2122130561Sobrien register struct sockaddr_in6 *dst; 2123130561Sobrien{ 2124130561Sobrien struct mbuf *copym; 212560484Sobrien 212633965Sjdp copym = m_copy(m, 0, M_COPYALL); 212733965Sjdp if (copym != NULL) { 212833965Sjdp (void)if_simloop(ifp, copym, (struct sockaddr *)dst, 0); 212933965Sjdp } 213033965Sjdp} 213133965Sjdp 213233965Sjdp/* 213333965Sjdp * Chop IPv6 header off from the payload. 213433965Sjdp */ 213533965Sjdpstatic int 213633965Sjdpip6_splithdr(m, exthdrs) 213733965Sjdp struct mbuf *m; 213833965Sjdp struct ip6_exthdrs *exthdrs; 213933965Sjdp{ 214033965Sjdp struct mbuf *mh; 214133965Sjdp struct ip6_hdr *ip6; 214233965Sjdp 2143218822Sdim ip6 = mtod(m, struct ip6_hdr *); 2144218822Sdim if (m->m_len > sizeof(*ip6)) { 214533965Sjdp MGETHDR(mh, M_DONTWAIT, MT_HEADER); 214660484Sobrien if (mh == 0) { 214760484Sobrien m_freem(m); 214889857Sobrien return ENOBUFS; 214933965Sjdp } 215033965Sjdp M_COPY_PKTHDR(mh, m); 215133965Sjdp MH_ALIGN(mh, sizeof(*ip6)); 215233965Sjdp m->m_flags &= ~M_PKTHDR; 215360484Sobrien m->m_len -= sizeof(*ip6); 215460484Sobrien m->m_data += sizeof(*ip6); 215533965Sjdp mh->m_next = m; 215633965Sjdp m = mh; 215733965Sjdp m->m_len = sizeof(*ip6); 215833965Sjdp bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6)); 215933965Sjdp } 216033965Sjdp exthdrs->ip6e_ip6 = m; 216160484Sobrien return 0; 216260484Sobrien} 216389857Sobrien 216489857Sobrien/* 216589857Sobrien * Compute IPv6 extension header length. 216660484Sobrien */ 216760484Sobrienint 216860484Sobrienip6_optlen(in6p) 216960484Sobrien struct in6pcb *in6p; 217060484Sobrien{ 217160484Sobrien int len; 217260484Sobrien 217360484Sobrien if (!in6p->in6p_outputopts) 217460484Sobrien return 0; 217560484Sobrien 217660484Sobrien len = 0; 217760484Sobrien#define elen(x) \ 217889857Sobrien (((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0) 217960484Sobrien 218060484Sobrien len += elen(in6p->in6p_outputopts->ip6po_hbh); 218160484Sobrien len += elen(in6p->in6p_outputopts->ip6po_dest1); 218260484Sobrien len += elen(in6p->in6p_outputopts->ip6po_rthdr); 218360484Sobrien len += elen(in6p->in6p_outputopts->ip6po_dest2); 218460484Sobrien return len; 218560484Sobrien#undef elen 218660484Sobrien} 218760484Sobrien 218860484Sobrien