2005-01-07 02:30:35 +00:00
|
|
|
/*-
|
1999-11-22 02:45:11 +00:00
|
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
2007-12-10 16:03:40 +00:00
|
|
|
*
|
|
|
|
* $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
|
|
|
|
2005-01-07 02:30:35 +00:00
|
|
|
/*-
|
1999-11-22 02:45:11 +00:00
|
|
|
* Copyright (c) 1982, 1986, 1988, 1990, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
|
|
|
|
*/
|
|
|
|
|
2007-12-10 16:03:40 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_inet6.h"
|
2011-08-20 17:05:11 +00:00
|
|
|
#include "opt_ipfw.h"
|
2000-07-04 16:35:15 +00:00
|
|
|
#include "opt_ipsec.h"
|
2010-03-12 08:10:30 +00:00
|
|
|
#include "opt_sctp.h"
|
2010-05-09 20:32:00 +00:00
|
|
|
#include "opt_route.h"
|
2014-07-12 05:46:33 +00:00
|
|
|
#include "opt_rss.h"
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
#include <sys/param.h>
|
2008-02-02 14:11:31 +00:00
|
|
|
#include <sys/kernel.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/errno.h>
|
2007-06-13 22:42:43 +00:00
|
|
|
#include <sys/priv.h>
|
2008-02-02 14:11:31 +00:00
|
|
|
#include <sys/proc.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
2009-03-03 13:12:12 +00:00
|
|
|
#include <sys/syslog.h>
|
2008-02-02 14:11:31 +00:00
|
|
|
#include <sys/ucred.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2012-05-25 02:17:16 +00:00
|
|
|
#include <machine/in_cksum.h>
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <net/if.h>
|
2013-10-26 17:58:36 +00:00
|
|
|
#include <net/if_var.h>
|
2005-04-18 18:35:05 +00:00
|
|
|
#include <net/netisr.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <net/route.h>
|
2000-07-31 13:11:42 +00:00
|
|
|
#include <net/pfil.h>
|
2015-01-18 18:06:40 +00:00
|
|
|
#include <net/rss_config.h>
|
2008-12-02 21:37:28 +00:00
|
|
|
#include <net/vnet.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_var.h>
|
2011-08-20 17:05:11 +00:00
|
|
|
#include <netinet/ip_var.h>
|
2016-01-04 18:32:24 +00:00
|
|
|
#include <netinet6/in6_fib.h>
|
2001-06-11 12:39:29 +00:00
|
|
|
#include <netinet6/in6_var.h>
|
2000-07-04 16:35:15 +00:00
|
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <netinet/icmp6.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <netinet6/ip6_var.h>
|
2000-07-04 16:35:15 +00:00
|
|
|
#include <netinet/in_pcb.h>
|
2003-11-20 20:07:39 +00:00
|
|
|
#include <netinet/tcp_var.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
#include <netinet6/nd6.h>
|
2015-01-18 18:06:40 +00:00
|
|
|
#include <netinet6/in6_rss.h>
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2007-07-03 12:13:45 +00:00
|
|
|
#ifdef IPSEC
|
2002-10-16 02:25:05 +00:00
|
|
|
#include <netipsec/ipsec.h>
|
|
|
|
#include <netipsec/ipsec6.h>
|
|
|
|
#include <netipsec/key.h>
|
2007-07-01 11:41:27 +00:00
|
|
|
#include <netinet6/ip6_ipsec.h>
|
2007-07-03 12:13:45 +00:00
|
|
|
#endif /* IPSEC */
|
2010-03-12 08:10:30 +00:00
|
|
|
#ifdef SCTP
|
|
|
|
#include <netinet/sctp.h>
|
|
|
|
#include <netinet/sctp_crc32.h>
|
|
|
|
#endif
|
2002-10-16 02:25:05 +00:00
|
|
|
|
2000-08-12 18:14:13 +00:00
|
|
|
#include <netinet6/ip6protosw.h>
|
2005-07-25 12:31:43 +00:00
|
|
|
#include <netinet6/scope6_var.h>
|
2000-08-12 18:14:13 +00:00
|
|
|
|
2010-05-09 20:32:00 +00:00
|
|
|
#ifdef FLOWTABLE
|
|
|
|
#include <net/flowtable.h>
|
|
|
|
#endif
|
|
|
|
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
extern int in6_mcast_loop;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
struct ip6_exthdrs {
|
2000-07-04 16:35:15 +00:00
|
|
|
struct mbuf *ip6e_ip6;
|
|
|
|
struct mbuf *ip6e_hbh;
|
|
|
|
struct mbuf *ip6e_dest1;
|
|
|
|
struct mbuf *ip6e_rthdr;
|
|
|
|
struct mbuf *ip6e_dest2;
|
1999-11-22 02:45:11 +00:00
|
|
|
};
|
|
|
|
|
2016-05-20 04:45:08 +00:00
|
|
|
static MALLOC_DEFINE(M_IP6OPT, "ip6opt", "IPv6 options");
|
|
|
|
|
2012-10-22 21:49:56 +00:00
|
|
|
static int ip6_pcbopt(int, u_char *, int, struct ip6_pktopts **,
|
|
|
|
struct ucred *, int);
|
|
|
|
static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *,
|
|
|
|
struct socket *, struct sockopt *);
|
2008-01-08 19:08:58 +00:00
|
|
|
static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *);
|
2012-10-22 21:49:56 +00:00
|
|
|
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *,
|
|
|
|
struct ucred *, int, int, int);
|
2003-10-24 18:26:30 +00:00
|
|
|
|
2008-01-08 19:08:58 +00:00
|
|
|
static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
|
2012-10-22 21:49:56 +00:00
|
|
|
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
|
|
|
|
struct ip6_frag **);
|
2008-01-08 19:08:58 +00:00
|
|
|
static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
|
|
|
|
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
|
2016-01-03 09:54:03 +00:00
|
|
|
static int ip6_getpmtu(struct route_in6 *, int,
|
2016-05-19 12:45:20 +00:00
|
|
|
struct ifnet *, const struct in6_addr *, u_long *, int *, u_int);
|
2016-01-03 09:54:03 +00:00
|
|
|
static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long,
|
|
|
|
u_long *, int *);
|
2016-05-19 12:45:20 +00:00
|
|
|
static int ip6_getpmtu_ctl(u_int, const struct in6_addr *, u_long *);
|
2008-01-08 19:08:58 +00:00
|
|
|
static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-08 18:26:08 +00:00
|
|
|
|
2007-07-01 11:41:27 +00:00
|
|
|
/*
|
|
|
|
* Make an extension header from option data. hp is the source, and
|
|
|
|
* mp is the destination.
|
|
|
|
*/
|
|
|
|
#define MAKE_EXTHDR(hp, mp) \
|
|
|
|
do { \
|
|
|
|
if (hp) { \
|
|
|
|
struct ip6_ext *eh = (struct ip6_ext *)(hp); \
|
|
|
|
error = ip6_copyexthdr((mp), (caddr_t)(hp), \
|
|
|
|
((eh)->ip6e_len + 1) << 3); \
|
|
|
|
if (error) \
|
|
|
|
goto freehdrs; \
|
|
|
|
} \
|
|
|
|
} while (/*CONSTCOND*/ 0)
|
|
|
|
|
|
|
|
/*
|
2007-07-05 16:29:40 +00:00
|
|
|
* Form a chain of extension headers.
|
2007-07-01 11:41:27 +00:00
|
|
|
* m is the extension header mbuf
|
|
|
|
* mp is the previous mbuf in the chain
|
|
|
|
* p is the next header
|
|
|
|
* i is the type of option.
|
|
|
|
*/
|
|
|
|
#define MAKE_CHAIN(m, mp, p, i)\
|
|
|
|
do {\
|
|
|
|
if (m) {\
|
|
|
|
if (!hdrsplit) \
|
|
|
|
panic("assumption failed: hdr not split"); \
|
|
|
|
*mtod((m), u_char *) = *(p);\
|
|
|
|
*(p) = (i);\
|
|
|
|
p = mtod((m), u_char *);\
|
|
|
|
(m)->m_next = (mp)->m_next;\
|
|
|
|
(mp)->m_next = (m);\
|
|
|
|
(mp) = (m);\
|
|
|
|
}\
|
|
|
|
} while (/*CONSTCOND*/ 0)
|
|
|
|
|
2014-05-28 12:45:27 +00:00
|
|
|
void
|
2012-05-25 02:17:16 +00:00
|
|
|
in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset)
|
|
|
|
{
|
|
|
|
u_short csum;
|
|
|
|
|
2012-05-26 23:58:51 +00:00
|
|
|
csum = in_cksum_skip(m, offset + plen, offset);
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6 && csum == 0)
|
2012-05-25 02:17:16 +00:00
|
|
|
csum = 0xffff;
|
|
|
|
offset += m->m_pkthdr.csum_data; /* checksum offset */
|
|
|
|
|
|
|
|
if (offset + sizeof(u_short) > m->m_len) {
|
2012-07-31 05:34:54 +00:00
|
|
|
printf("%s: delayed m_pullup, m->len: %d plen %u off %u "
|
2013-08-24 19:51:18 +00:00
|
|
|
"csum_flags=%b\n", __func__, m->m_len, plen, offset,
|
|
|
|
(int)m->m_pkthdr.csum_flags, CSUM_BITS);
|
2012-05-25 02:17:16 +00:00
|
|
|
/*
|
|
|
|
* XXX this should not happen, but if it does, the correct
|
|
|
|
* behavior may be to insert the checksum in the appropriate
|
|
|
|
* next mbuf in the chain.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*(u_short *)(m->m_data + offset) = csum;
|
|
|
|
}
|
|
|
|
|
2015-02-16 06:30:27 +00:00
|
|
|
int
|
|
|
|
ip6_fragment(struct ifnet *ifp, struct mbuf *m0, int hlen, u_char nextproto,
|
2015-04-01 12:15:01 +00:00
|
|
|
int mtu, uint32_t id)
|
2015-02-16 06:30:27 +00:00
|
|
|
{
|
|
|
|
struct mbuf *m, **mnext, *m_frgpart;
|
|
|
|
struct ip6_hdr *ip6, *mhip6;
|
|
|
|
struct ip6_frag *ip6f;
|
|
|
|
int off;
|
|
|
|
int error;
|
|
|
|
int tlen = m0->m_pkthdr.len;
|
|
|
|
|
|
|
|
m = m0;
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
mnext = &m->m_nextpkt;
|
|
|
|
|
|
|
|
for (off = hlen; off < tlen; off += mtu) {
|
|
|
|
m = m_gethdr(M_NOWAIT, MT_DATA);
|
|
|
|
if (!m) {
|
|
|
|
IP6STAT_INC(ip6s_odropped);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
m->m_flags = m0->m_flags & M_COPYFLAGS;
|
|
|
|
*mnext = m;
|
|
|
|
mnext = &m->m_nextpkt;
|
|
|
|
m->m_data += max_linkhdr;
|
|
|
|
mhip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
*mhip6 = *ip6;
|
|
|
|
m->m_len = sizeof(*mhip6);
|
|
|
|
error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
|
|
|
|
if (error) {
|
|
|
|
IP6STAT_INC(ip6s_odropped);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));
|
|
|
|
if (off + mtu >= tlen)
|
|
|
|
mtu = tlen - off;
|
|
|
|
else
|
|
|
|
ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
|
|
|
|
mhip6->ip6_plen = htons((u_short)(mtu + hlen +
|
|
|
|
sizeof(*ip6f) - sizeof(struct ip6_hdr)));
|
2016-04-15 17:30:33 +00:00
|
|
|
if ((m_frgpart = m_copy(m0, off, mtu)) == NULL) {
|
2015-02-16 06:30:27 +00:00
|
|
|
IP6STAT_INC(ip6s_odropped);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
m_cat(m, m_frgpart);
|
|
|
|
m->m_pkthdr.len = mtu + hlen + sizeof(*ip6f);
|
|
|
|
m->m_pkthdr.fibnum = m0->m_pkthdr.fibnum;
|
|
|
|
m->m_pkthdr.rcvif = NULL;
|
|
|
|
ip6f->ip6f_reserved = 0;
|
|
|
|
ip6f->ip6f_ident = id;
|
|
|
|
ip6f->ip6f_nxt = nextproto;
|
|
|
|
IP6STAT_INC(ip6s_ofragments);
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_fragcreat);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* IP6 output. The packet in mbuf chain m contains a skeletal IP6
|
|
|
|
* header (with pri, len, nxt, hlim, src, dst).
|
|
|
|
* This function may modify ver and hlim only.
|
|
|
|
* The mbuf chain containing the packet will be freed.
|
|
|
|
* The mbuf opt, if present, will not be freed.
|
2012-07-04 07:37:53 +00:00
|
|
|
* If route_in6 ro is present and has ro_rt initialized, route lookup would be
|
|
|
|
* skipped and ro->ro_rt would be used. If ro is present but ro->ro_rt is NULL,
|
|
|
|
* then result of route lookup is stored in ro->ro_rt.
|
2001-06-11 12:39:29 +00:00
|
|
|
*
|
2014-03-05 01:17:47 +00:00
|
|
|
* type of "mtu": rt_mtu is u_long, ifnet.ifr_mtu is int, and
|
2001-06-11 12:39:29 +00:00
|
|
|
* nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one,
|
2014-03-05 01:17:47 +00:00
|
|
|
* which is rt_mtu.
|
2007-07-05 16:23:49 +00:00
|
|
|
*
|
|
|
|
* ifpp - XXX: just for statistics
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2014-09-09 00:21:21 +00:00
|
|
|
/*
|
|
|
|
* XXX TODO: no flowid is assigned for outbound flows?
|
|
|
|
*/
|
1999-11-22 02:45:11 +00:00
|
|
|
int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
|
|
|
|
struct route_in6 *ro, int flags, struct ip6_moptions *im6o,
|
|
|
|
struct ifnet **ifpp, struct inpcb *inp)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
2015-02-16 06:30:27 +00:00
|
|
|
struct ip6_hdr *ip6;
|
2000-07-04 16:35:15 +00:00
|
|
|
struct ifnet *ifp, *origifp;
|
1999-11-22 02:45:11 +00:00
|
|
|
struct mbuf *m = m0;
|
2007-07-01 11:41:27 +00:00
|
|
|
struct mbuf *mprev = NULL;
|
2015-02-16 06:30:27 +00:00
|
|
|
int hlen, tlen, len;
|
1999-11-22 02:45:11 +00:00
|
|
|
struct route_in6 ip6route;
|
2005-07-25 12:31:43 +00:00
|
|
|
struct rtentry *rt = NULL;
|
|
|
|
struct sockaddr_in6 *dst, src_sa, dst_sa;
|
2005-04-18 18:35:05 +00:00
|
|
|
struct in6_addr odst;
|
1999-11-22 02:45:11 +00:00
|
|
|
int error = 0;
|
2000-10-19 23:15:54 +00:00
|
|
|
struct in6_ifaddr *ia = NULL;
|
1999-11-22 02:45:11 +00:00
|
|
|
u_long mtu;
|
2003-10-24 18:26:30 +00:00
|
|
|
int alwaysfrag, dontfrag;
|
1999-11-22 02:45:11 +00:00
|
|
|
u_int32_t optlen = 0, plen = 0, unfragpartlen = 0;
|
|
|
|
struct ip6_exthdrs exthdrs;
|
2016-05-19 12:45:20 +00:00
|
|
|
struct in6_addr src0, dst0;
|
2005-07-25 12:31:43 +00:00
|
|
|
u_int32_t zone;
|
1999-11-22 02:45:11 +00:00
|
|
|
struct route_in6 *ro_pmtu = NULL;
|
|
|
|
int hdrsplit = 0;
|
2012-05-25 02:17:16 +00:00
|
|
|
int sw_csum, tso;
|
2014-10-02 00:25:57 +00:00
|
|
|
int needfiblookup;
|
|
|
|
uint32_t fibnum;
|
2012-12-19 17:28:17 +00:00
|
|
|
struct m_tag *fwd_tag = NULL;
|
2015-04-01 12:15:01 +00:00
|
|
|
uint32_t id;
|
2002-10-16 02:25:05 +00:00
|
|
|
|
2014-09-09 00:21:21 +00:00
|
|
|
if (inp != NULL) {
|
2012-02-03 13:08:44 +00:00
|
|
|
M_SETFIB(m, inp->inp_inc.inc_fibnum);
|
2014-12-01 11:45:24 +00:00
|
|
|
if ((flags & IP_NODEFAULTFLOWID) == 0) {
|
|
|
|
/* unconditionally set flowid */
|
2014-09-09 00:21:21 +00:00
|
|
|
m->m_pkthdr.flowid = inp->inp_flowid;
|
2014-12-01 11:45:24 +00:00
|
|
|
M_HASHTYPE_SET(m, inp->inp_flowtype);
|
2014-09-09 00:21:21 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-03 13:08:44 +00:00
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
bzero(&exthdrs, sizeof(exthdrs));
|
|
|
|
if (opt) {
|
|
|
|
/* Hop-by-Hop options header */
|
|
|
|
MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh);
|
|
|
|
/* Destination options header(1st part) */
|
2003-10-31 16:32:12 +00:00
|
|
|
if (opt->ip6po_rthdr) {
|
|
|
|
/*
|
|
|
|
* Destination options header(1st part)
|
2007-07-01 11:41:27 +00:00
|
|
|
* This only makes sense with a routing header.
|
2003-10-31 16:32:12 +00:00
|
|
|
* See Section 9.2 of RFC 3542.
|
|
|
|
* Disabling this part just for MIP6 convenience is
|
|
|
|
* a bad idea. We need to think carefully about a
|
|
|
|
* way to make the advanced API coexist with MIP6
|
|
|
|
* options, which might automatically be inserted in
|
|
|
|
* the kernel.
|
|
|
|
*/
|
|
|
|
MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1);
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
/* Routing header */
|
|
|
|
MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr);
|
|
|
|
/* Destination options header(2nd part) */
|
|
|
|
MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
|
|
|
|
}
|
|
|
|
|
2012-07-29 00:45:24 +00:00
|
|
|
#ifdef IPSEC
|
2007-07-05 16:29:40 +00:00
|
|
|
/*
|
2007-07-01 11:41:27 +00:00
|
|
|
* IPSec checking which handles several cases.
|
|
|
|
* FAST IPSEC: We re-injected the packet.
|
2014-12-11 18:35:34 +00:00
|
|
|
* XXX: need scope argument.
|
2007-07-01 11:41:27 +00:00
|
|
|
*/
|
2014-12-11 18:35:34 +00:00
|
|
|
switch(ip6_ipsec_output(&m, inp, &error))
|
2007-07-01 11:41:27 +00:00
|
|
|
{
|
|
|
|
case 1: /* Bad packet */
|
2002-10-16 02:25:05 +00:00
|
|
|
goto freehdrs;
|
2014-05-28 12:45:27 +00:00
|
|
|
case -1: /* IPSec done */
|
|
|
|
goto done;
|
2007-07-01 11:41:27 +00:00
|
|
|
case 0: /* No IPSec */
|
2002-10-16 02:25:05 +00:00
|
|
|
default:
|
2007-07-01 11:41:27 +00:00
|
|
|
break;
|
2002-10-16 02:25:05 +00:00
|
|
|
}
|
2007-07-03 12:13:45 +00:00
|
|
|
#endif /* IPSEC */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the total length of the extension header chain.
|
|
|
|
* Keep the length of the unfragmentable part for fragmentation.
|
|
|
|
*/
|
|
|
|
optlen = 0;
|
2007-07-05 16:29:40 +00:00
|
|
|
if (exthdrs.ip6e_hbh)
|
2007-07-01 11:41:27 +00:00
|
|
|
optlen += exthdrs.ip6e_hbh->m_len;
|
2007-07-05 16:29:40 +00:00
|
|
|
if (exthdrs.ip6e_dest1)
|
2007-07-01 11:41:27 +00:00
|
|
|
optlen += exthdrs.ip6e_dest1->m_len;
|
2007-07-05 16:29:40 +00:00
|
|
|
if (exthdrs.ip6e_rthdr)
|
2007-07-01 11:41:27 +00:00
|
|
|
optlen += exthdrs.ip6e_rthdr->m_len;
|
1999-11-22 02:45:11 +00:00
|
|
|
unfragpartlen = optlen + sizeof(struct ip6_hdr);
|
2007-07-01 11:41:27 +00:00
|
|
|
|
2014-05-28 12:45:27 +00:00
|
|
|
/* NOTE: we don't add AH/ESP length here (done in ip6_ipsec_output) */
|
2007-07-05 16:29:40 +00:00
|
|
|
if (exthdrs.ip6e_dest2)
|
2007-07-01 11:41:27 +00:00
|
|
|
optlen += exthdrs.ip6e_dest2->m_len;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/*
|
2014-05-28 12:45:27 +00:00
|
|
|
* If there is at least one extension header,
|
1999-11-22 02:45:11 +00:00
|
|
|
* separate IP6 header from the payload.
|
|
|
|
*/
|
2014-05-28 12:45:27 +00:00
|
|
|
if (optlen && !hdrsplit) {
|
1999-11-22 02:45:11 +00:00
|
|
|
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
|
|
|
|
m = NULL;
|
|
|
|
goto freehdrs;
|
|
|
|
}
|
|
|
|
m = exthdrs.ip6e_ip6;
|
|
|
|
hdrsplit++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
|
|
|
|
/* adjust mbuf packet header length */
|
|
|
|
m->m_pkthdr.len += optlen;
|
|
|
|
plen = m->m_pkthdr.len - sizeof(*ip6);
|
|
|
|
|
|
|
|
/* If this is a jumbo payload, insert a jumbo payload option. */
|
|
|
|
if (plen > IPV6_MAXPACKET) {
|
|
|
|
if (!hdrsplit) {
|
|
|
|
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
|
|
|
|
m = NULL;
|
|
|
|
goto freehdrs;
|
|
|
|
}
|
|
|
|
m = exthdrs.ip6e_ip6;
|
|
|
|
hdrsplit++;
|
|
|
|
}
|
|
|
|
/* adjust pointer */
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
|
|
|
|
goto freehdrs;
|
|
|
|
ip6->ip6_plen = 0;
|
|
|
|
} else
|
|
|
|
ip6->ip6_plen = htons(plen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Concatenate headers and fill in next header fields.
|
|
|
|
* Here we have, on "m"
|
|
|
|
* IPv6 payload
|
|
|
|
* and we insert headers accordingly. Finally, we should be getting:
|
|
|
|
* IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
|
|
|
|
*
|
|
|
|
* during the header composing process, "m" points to IPv6 header.
|
|
|
|
* "mprev" points to an extension header prior to esp.
|
|
|
|
*/
|
2007-07-01 11:41:27 +00:00
|
|
|
u_char *nexthdrp = &ip6->ip6_nxt;
|
|
|
|
mprev = m;
|
2007-07-05 16:29:40 +00:00
|
|
|
|
2007-07-01 11:41:27 +00:00
|
|
|
/*
|
|
|
|
* we treat dest2 specially. this makes IPsec processing
|
|
|
|
* much easier. the goal here is to make mprev point the
|
|
|
|
* mbuf prior to dest2.
|
|
|
|
*
|
|
|
|
* result: IPv6 dest2 payload
|
|
|
|
* m and mprev will point to IPv6 header.
|
|
|
|
*/
|
|
|
|
if (exthdrs.ip6e_dest2) {
|
|
|
|
if (!hdrsplit)
|
|
|
|
panic("assumption failed: hdr not split");
|
|
|
|
exthdrs.ip6e_dest2->m_next = m->m_next;
|
|
|
|
m->m_next = exthdrs.ip6e_dest2;
|
|
|
|
*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
|
|
|
|
ip6->ip6_nxt = IPPROTO_DSTOPTS;
|
|
|
|
}
|
2007-07-05 16:29:40 +00:00
|
|
|
|
2007-07-01 11:41:27 +00:00
|
|
|
/*
|
|
|
|
* result: IPv6 hbh dest1 rthdr dest2 payload
|
|
|
|
* m will point to IPv6 header. mprev will point to the
|
|
|
|
* extension header prior to dest2 (rthdr in the above case).
|
|
|
|
*/
|
|
|
|
MAKE_CHAIN(exthdrs.ip6e_hbh, mprev, nexthdrp, IPPROTO_HOPOPTS);
|
|
|
|
MAKE_CHAIN(exthdrs.ip6e_dest1, mprev, nexthdrp,
|
|
|
|
IPPROTO_DSTOPTS);
|
|
|
|
MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp,
|
|
|
|
IPPROTO_ROUTING);
|
2007-07-05 16:29:40 +00:00
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
2009-05-09 18:25:58 +00:00
|
|
|
* If there is a routing header, discard the packet.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
|
|
|
if (exthdrs.ip6e_rthdr) {
|
2009-05-09 18:25:58 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto bad;
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Source address validation */
|
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) &&
|
2005-10-21 15:45:13 +00:00
|
|
|
(flags & IPV6_UNSPECSRC) == 0) {
|
1999-11-22 02:45:11 +00:00
|
|
|
error = EOPNOTSUPP;
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_badscope);
|
1999-11-22 02:45:11 +00:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
|
|
|
|
error = EOPNOTSUPP;
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_badscope);
|
1999-11-22 02:45:11 +00:00
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_localout);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Route packet.
|
|
|
|
*/
|
2016-04-15 17:30:33 +00:00
|
|
|
if (ro == NULL) {
|
1999-11-22 02:45:11 +00:00
|
|
|
ro = &ip6route;
|
|
|
|
bzero((caddr_t)ro, sizeof(*ro));
|
|
|
|
}
|
|
|
|
ro_pmtu = ro;
|
|
|
|
if (opt && opt->ip6po_rthdr)
|
|
|
|
ro = &opt->ip6po_route;
|
|
|
|
dst = (struct sockaddr_in6 *)&ro->ro_dst;
|
2010-05-09 20:32:00 +00:00
|
|
|
#ifdef FLOWTABLE
|
2014-02-17 11:50:56 +00:00
|
|
|
if (ro->ro_rt == NULL)
|
|
|
|
(void )flowtable_lookup(AF_INET6, m, (struct route *)ro);
|
2012-05-30 20:56:07 +00:00
|
|
|
#endif
|
2014-10-02 00:25:57 +00:00
|
|
|
fibnum = (inp != NULL) ? inp->inp_inc.inc_fibnum : M_GETFIB(m);
|
2005-04-18 18:35:05 +00:00
|
|
|
again:
|
2007-07-05 16:29:40 +00:00
|
|
|
/*
|
2003-10-24 18:26:30 +00:00
|
|
|
* if specified, try to fill in the traffic class field.
|
|
|
|
* do not override if a non-zero value is already set.
|
|
|
|
* we check the diffserv field and the ecn field separately.
|
|
|
|
*/
|
|
|
|
if (opt && opt->ip6po_tclass >= 0) {
|
|
|
|
int mask = 0;
|
|
|
|
|
|
|
|
if ((ip6->ip6_flow & htonl(0xfc << 20)) == 0)
|
|
|
|
mask |= 0xfc;
|
|
|
|
if ((ip6->ip6_flow & htonl(0x03 << 20)) == 0)
|
|
|
|
mask |= 0x03;
|
|
|
|
if (mask != 0)
|
|
|
|
ip6->ip6_flow |= htonl((opt->ip6po_tclass & mask) << 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in or override the hop limit field, if necessary. */
|
|
|
|
if (opt && opt->ip6po_hlim != -1)
|
|
|
|
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
|
|
|
|
else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
|
|
|
if (im6o != NULL)
|
|
|
|
ip6->ip6_hlim = im6o->im6o_multicast_hlim;
|
|
|
|
else
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
ip6->ip6_hlim = V_ip6_defmcasthlim;
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
/* adjust pointer */
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
|
2016-03-24 07:54:56 +00:00
|
|
|
/*
|
|
|
|
* Validate route against routing table additions;
|
|
|
|
* a better/more specific route might have been added.
|
|
|
|
* Make sure address family is set in route.
|
|
|
|
*/
|
|
|
|
if (inp) {
|
|
|
|
ro->ro_dst.sin6_family = AF_INET6;
|
|
|
|
RT_VALIDATE((struct route *)ro, &inp->inp_rt_cookie, fibnum);
|
|
|
|
}
|
|
|
|
if (ro->ro_rt && fwd_tag == NULL && (ro->ro_rt->rt_flags & RTF_UP) &&
|
|
|
|
ro->ro_dst.sin6_family == AF_INET6 &&
|
|
|
|
IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, &ip6->ip6_dst)) {
|
2010-05-09 20:32:00 +00:00
|
|
|
rt = ro->ro_rt;
|
|
|
|
ifp = ro->ro_rt->rt_ifp;
|
2012-12-19 17:08:49 +00:00
|
|
|
} else {
|
2012-12-19 17:28:17 +00:00
|
|
|
if (fwd_tag == NULL) {
|
|
|
|
bzero(&dst_sa, sizeof(dst_sa));
|
|
|
|
dst_sa.sin6_family = AF_INET6;
|
|
|
|
dst_sa.sin6_len = sizeof(dst_sa);
|
|
|
|
dst_sa.sin6_addr = ip6->ip6_dst;
|
|
|
|
}
|
2012-12-19 17:08:49 +00:00
|
|
|
error = in6_selectroute_fib(&dst_sa, opt, im6o, ro, &ifp,
|
2014-10-02 00:25:57 +00:00
|
|
|
&rt, fibnum);
|
2012-12-19 17:08:49 +00:00
|
|
|
if (error != 0) {
|
|
|
|
if (ifp != NULL)
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_discard);
|
|
|
|
goto bad;
|
|
|
|
}
|
2005-07-25 12:31:43 +00:00
|
|
|
}
|
|
|
|
if (rt == NULL) {
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
2005-07-25 12:31:43 +00:00
|
|
|
* If in6_selectroute() does not return a route entry,
|
|
|
|
* dst may not have been updated.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2005-07-25 12:31:43 +00:00
|
|
|
*dst = dst_sa; /* XXX */
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
/*
|
|
|
|
* then rt (for unicast) and ifp must be non-NULL valid values.
|
|
|
|
*/
|
|
|
|
if ((flags & IPV6_FORWARDING) == 0) {
|
|
|
|
/* XXX: the FORWARDING flag can be set for mrouting. */
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_request);
|
|
|
|
}
|
|
|
|
if (rt != NULL) {
|
|
|
|
ia = (struct in6_ifaddr *)(rt->rt_ifa);
|
2014-03-05 01:17:47 +00:00
|
|
|
counter_u64_add(rt->rt_pksent, 1);
|
2005-07-25 12:31:43 +00:00
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2009-09-05 16:43:16 +00:00
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
/*
|
|
|
|
* The outgoing interface must be in the zone of source and
|
2012-05-30 20:56:07 +00:00
|
|
|
* destination addresses.
|
2005-07-25 12:31:43 +00:00
|
|
|
*/
|
2009-09-05 16:43:16 +00:00
|
|
|
origifp = ifp;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
src0 = ip6->ip6_src;
|
|
|
|
if (in6_setscope(&src0, origifp, &zone))
|
|
|
|
goto badscope;
|
|
|
|
bzero(&src_sa, sizeof(src_sa));
|
|
|
|
src_sa.sin6_family = AF_INET6;
|
|
|
|
src_sa.sin6_len = sizeof(src_sa);
|
|
|
|
src_sa.sin6_addr = ip6->ip6_src;
|
|
|
|
if (sa6_recoverscope(&src_sa) || zone != src_sa.sin6_scope_id)
|
|
|
|
goto badscope;
|
|
|
|
|
|
|
|
dst0 = ip6->ip6_dst;
|
|
|
|
if (in6_setscope(&dst0, origifp, &zone))
|
|
|
|
goto badscope;
|
|
|
|
/* re-initialize to be sure */
|
|
|
|
bzero(&dst_sa, sizeof(dst_sa));
|
|
|
|
dst_sa.sin6_family = AF_INET6;
|
|
|
|
dst_sa.sin6_len = sizeof(dst_sa);
|
|
|
|
dst_sa.sin6_addr = ip6->ip6_dst;
|
|
|
|
if (sa6_recoverscope(&dst_sa) || zone != dst_sa.sin6_scope_id) {
|
|
|
|
goto badscope;
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2012-05-30 20:56:07 +00:00
|
|
|
/* We should use ia_ifp to support the case of
|
2009-09-05 16:43:16 +00:00
|
|
|
* sending packets to an address of our own.
|
|
|
|
*/
|
|
|
|
if (ia != NULL && ia->ia_ifp)
|
|
|
|
ifp = ia->ia_ifp;
|
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
/* scope check is done. */
|
|
|
|
goto routefound;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
badscope:
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_badscope);
|
2005-07-25 12:31:43 +00:00
|
|
|
in6_ifstat_inc(origifp, ifs6_out_discard);
|
|
|
|
if (error == 0)
|
|
|
|
error = EHOSTUNREACH; /* XXX */
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
routefound:
|
|
|
|
if (rt && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
|
|
|
if (opt && opt->ip6po_nextroute.ro_rt) {
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
2005-07-25 12:31:43 +00:00
|
|
|
* The nexthop is explicitly specified by the
|
|
|
|
* application. We assume the next hop is an IPv6
|
|
|
|
* address.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2005-07-25 12:31:43 +00:00
|
|
|
dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
2005-07-25 12:31:43 +00:00
|
|
|
else if ((rt->rt_flags & RTF_GATEWAY))
|
|
|
|
dst = (struct sockaddr_in6 *)rt->rt_gateway;
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-25 12:31:43 +00:00
|
|
|
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
|
|
|
|
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
|
|
|
|
} else {
|
|
|
|
m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
|
1999-11-22 02:45:11 +00:00
|
|
|
in6_ifstat_inc(ifp, ifs6_out_mcast);
|
|
|
|
/*
|
|
|
|
* Confirm that the outgoing interface supports multicast.
|
|
|
|
*/
|
2005-07-25 12:31:43 +00:00
|
|
|
if (!(ifp->if_flags & IFF_MULTICAST)) {
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_noroute);
|
1999-11-22 02:45:11 +00:00
|
|
|
in6_ifstat_inc(ifp, ifs6_out_discard);
|
|
|
|
error = ENETUNREACH;
|
|
|
|
goto bad;
|
|
|
|
}
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
if ((im6o == NULL && in6_mcast_loop) ||
|
|
|
|
(im6o && im6o->im6o_multicast_loop)) {
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
* Loop back multicast datagram if not expressly
|
|
|
|
* forbidden to do so, even if we have not joined
|
|
|
|
* the address; protocols will filter it later,
|
|
|
|
* thus deferring a hash lookup and lock acquisition
|
|
|
|
* at the expense of an m_copym().
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2015-08-08 15:58:35 +00:00
|
|
|
ip6_mloopback(ifp, m);
|
2000-01-28 05:27:14 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we are acting as a multicast router, perform
|
|
|
|
* multicast forwarding as if the packet had just
|
|
|
|
* arrived on the interface to which we are about
|
|
|
|
* to send. The multicast forwarding function
|
|
|
|
* recursively calls this function, using the
|
|
|
|
* IPV6_FORWARDING flag to prevent infinite recursion.
|
|
|
|
*
|
|
|
|
* Multicasts that are looped back by ip6_mloopback(),
|
|
|
|
* above, will be forwarded by the ip6_input() routine,
|
|
|
|
* if necessary.
|
|
|
|
*/
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
if (V_ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
|
2005-07-25 12:31:43 +00:00
|
|
|
/*
|
|
|
|
* XXX: ip6_mforward expects that rcvif is NULL
|
|
|
|
* when it is called from the originating path.
|
2013-03-15 12:50:29 +00:00
|
|
|
* However, it may not always be the case.
|
2005-07-25 12:31:43 +00:00
|
|
|
*/
|
|
|
|
m->m_pkthdr.rcvif = NULL;
|
2000-07-04 16:35:15 +00:00
|
|
|
if (ip6_mforward(ip6, ifp, m) != 0) {
|
2000-01-28 05:27:14 +00:00
|
|
|
m_freem(m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Multicasts with a hoplimit of zero may be looped back,
|
|
|
|
* above, but must not be transmitted on a network.
|
|
|
|
* Also, multicasts addressed to the loopback interface
|
|
|
|
* are not sent -- the above call to ip6_mloopback() will
|
|
|
|
* loop back a copy if this host actually belongs to the
|
|
|
|
* destination group on the loopback interface.
|
|
|
|
*/
|
2003-10-24 18:26:30 +00:00
|
|
|
if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
|
|
|
|
IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
|
1999-11-22 02:45:11 +00:00
|
|
|
m_freem(m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill the outgoing inteface to tell the upper layer
|
|
|
|
* to increment per-interface statistics.
|
|
|
|
*/
|
|
|
|
if (ifpp)
|
|
|
|
*ifpp = ifp;
|
|
|
|
|
2003-10-20 15:27:48 +00:00
|
|
|
/* Determine path MTU. */
|
2016-05-19 12:45:20 +00:00
|
|
|
if ((error = ip6_getpmtu(ro_pmtu, ro != ro_pmtu, ifp, &ip6->ip6_dst,
|
|
|
|
&mtu, &alwaysfrag, fibnum)) != 0)
|
2003-10-20 15:27:48 +00:00
|
|
|
goto bad;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
2004-02-08 18:22:27 +00:00
|
|
|
* The caller of this function may specify to use the minimum MTU
|
|
|
|
* in some cases.
|
|
|
|
* An advanced API option (IPV6_USE_MIN_MTU) can also override MTU
|
|
|
|
* setting. The logic is a bit complicated; by default, unicast
|
|
|
|
* packets will follow path MTU while multicast packets will be sent at
|
|
|
|
* the minimum MTU. If IP6PO_MINMTU_ALL is specified, all packets
|
|
|
|
* including unicast ones will be sent at the minimum MTU. Multicast
|
|
|
|
* packets will always be sent at the minimum MTU unless
|
|
|
|
* IP6PO_MINMTU_DISABLE is explicitly specified.
|
|
|
|
* See RFC 3542 for more details.
|
2001-06-11 12:39:29 +00:00
|
|
|
*/
|
2004-02-08 18:22:27 +00:00
|
|
|
if (mtu > IPV6_MMTU) {
|
|
|
|
if ((flags & IPV6_MINMTU))
|
|
|
|
mtu = IPV6_MMTU;
|
|
|
|
else if (opt && opt->ip6po_minmtu == IP6PO_MINMTU_ALL)
|
|
|
|
mtu = IPV6_MMTU;
|
|
|
|
else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) &&
|
|
|
|
(opt == NULL ||
|
|
|
|
opt->ip6po_minmtu != IP6PO_MINMTU_DISABLE)) {
|
|
|
|
mtu = IPV6_MMTU;
|
|
|
|
}
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* clear embedded scope identifiers if necessary.
|
|
|
|
* in6_clearscope will touch the addresses only when necessary.
|
|
|
|
*/
|
|
|
|
in6_clearscope(&ip6->ip6_src);
|
|
|
|
in6_clearscope(&ip6->ip6_dst);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the outgoing packet contains a hop-by-hop options header,
|
|
|
|
* it must be examined and processed even by the source node.
|
|
|
|
* (RFC 2460, section 4.)
|
|
|
|
*/
|
|
|
|
if (exthdrs.ip6e_hbh) {
|
2001-06-11 12:39:29 +00:00
|
|
|
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
|
2005-02-27 18:07:18 +00:00
|
|
|
u_int32_t dummy; /* XXX unused */
|
|
|
|
u_int32_t plen = 0; /* XXX: ip6_process will check the value */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)
|
2010-11-27 21:51:39 +00:00
|
|
|
panic("ip6e_hbh is not contiguous");
|
2001-06-11 12:39:29 +00:00
|
|
|
#endif
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* XXX: if we have to send an ICMPv6 error to the sender,
|
|
|
|
* we need the M_LOOP flag since icmp6_error() expects
|
|
|
|
* the IPv6 and the hop-by-hop options header are
|
2010-11-27 21:51:39 +00:00
|
|
|
* contiguous unless the flag is set.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
|
|
|
m->m_flags |= M_LOOP;
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
2003-10-08 18:26:08 +00:00
|
|
|
if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1),
|
|
|
|
((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
|
2005-02-27 18:07:18 +00:00
|
|
|
&dummy, &plen) < 0) {
|
1999-11-22 02:45:11 +00:00
|
|
|
/* m was already freed at this point */
|
|
|
|
error = EINVAL;/* better error? */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
m->m_flags &= ~M_LOOP; /* XXX */
|
|
|
|
m->m_pkthdr.rcvif = NULL;
|
|
|
|
}
|
|
|
|
|
2004-08-27 15:16:24 +00:00
|
|
|
/* Jump over all PFIL processing if hooks are not active. */
|
2009-10-11 05:59:43 +00:00
|
|
|
if (!PFIL_HOOKED(&V_inet6_pfil_hook))
|
2004-08-27 15:16:24 +00:00
|
|
|
goto passout;
|
|
|
|
|
2005-04-18 18:35:05 +00:00
|
|
|
odst = ip6->ip6_dst;
|
2004-08-27 15:16:24 +00:00
|
|
|
/* Run through list of hooks for output packets. */
|
2009-10-11 05:59:43 +00:00
|
|
|
error = pfil_run_hooks(&V_inet6_pfil_hook, &m, ifp, PFIL_OUT, inp);
|
2003-09-30 04:46:08 +00:00
|
|
|
if (error != 0 || m == NULL)
|
2003-09-23 17:54:04 +00:00
|
|
|
goto done;
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
2003-10-08 18:26:08 +00:00
|
|
|
|
2014-10-02 00:25:57 +00:00
|
|
|
needfiblookup = 0;
|
2005-04-18 18:35:05 +00:00
|
|
|
/* See if destination IP address was changed by packet filter. */
|
|
|
|
if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {
|
|
|
|
m->m_flags |= M_SKIP_FIREWALL;
|
|
|
|
/* If destination is now ourself drop to ip6_input(). */
|
2011-08-20 17:05:11 +00:00
|
|
|
if (in6_localip(&ip6->ip6_dst)) {
|
|
|
|
m->m_flags |= M_FASTFWD_OURS;
|
2005-04-18 18:35:05 +00:00
|
|
|
if (m->m_pkthdr.rcvif == NULL)
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
m->m_pkthdr.rcvif = V_loif;
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
|
2005-04-18 18:35:05 +00:00
|
|
|
m->m_pkthdr.csum_flags |=
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
|
2005-04-18 18:35:05 +00:00
|
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
2010-03-12 08:10:30 +00:00
|
|
|
#ifdef SCTP
|
2012-05-30 20:56:07 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
|
2010-03-12 08:10:30 +00:00
|
|
|
m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
|
|
|
|
#endif
|
2005-04-18 18:35:05 +00:00
|
|
|
error = netisr_queue(NETISR_IPV6, m);
|
|
|
|
goto done;
|
2016-05-17 14:06:55 +00:00
|
|
|
} else {
|
|
|
|
RO_RTFREE(ro);
|
2014-10-02 00:25:57 +00:00
|
|
|
needfiblookup = 1; /* Redo the routing table lookup. */
|
2016-05-17 14:06:55 +00:00
|
|
|
}
|
2005-04-18 18:35:05 +00:00
|
|
|
}
|
2014-10-02 00:25:57 +00:00
|
|
|
/* See if fib was changed by packet filter. */
|
|
|
|
if (fibnum != M_GETFIB(m)) {
|
|
|
|
m->m_flags |= M_SKIP_FIREWALL;
|
|
|
|
fibnum = M_GETFIB(m);
|
|
|
|
RO_RTFREE(ro);
|
|
|
|
needfiblookup = 1;
|
|
|
|
}
|
|
|
|
if (needfiblookup)
|
|
|
|
goto again;
|
2005-04-18 18:35:05 +00:00
|
|
|
|
2011-08-20 17:05:11 +00:00
|
|
|
/* See if local, if yes, send it to netisr. */
|
|
|
|
if (m->m_flags & M_FASTFWD_OURS) {
|
|
|
|
if (m->m_pkthdr.rcvif == NULL)
|
|
|
|
m->m_pkthdr.rcvif = V_loif;
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
|
2011-08-20 17:05:11 +00:00
|
|
|
m->m_pkthdr.csum_flags |=
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR;
|
2011-08-20 17:05:11 +00:00
|
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
|
|
|
#ifdef SCTP
|
2012-05-30 20:56:07 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6)
|
2012-05-25 02:17:16 +00:00
|
|
|
m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
|
2012-05-30 20:56:07 +00:00
|
|
|
#endif
|
2011-08-20 17:05:11 +00:00
|
|
|
error = netisr_queue(NETISR_IPV6, m);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* Or forward to some other address? */
|
2012-11-02 01:20:55 +00:00
|
|
|
if ((m->m_flags & M_IP6_NEXTHOP) &&
|
|
|
|
(fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) {
|
2011-08-20 17:05:11 +00:00
|
|
|
dst = (struct sockaddr_in6 *)&ro->ro_dst;
|
2012-12-19 17:28:17 +00:00
|
|
|
bcopy((fwd_tag+1), &dst_sa, sizeof(struct sockaddr_in6));
|
2011-08-20 17:05:11 +00:00
|
|
|
m->m_flags |= M_SKIP_FIREWALL;
|
2012-11-02 01:20:55 +00:00
|
|
|
m->m_flags &= ~M_IP6_NEXTHOP;
|
2011-08-20 17:05:11 +00:00
|
|
|
m_tag_delete(m, fwd_tag);
|
|
|
|
goto again;
|
|
|
|
}
|
2005-04-18 18:35:05 +00:00
|
|
|
|
2004-08-27 15:16:24 +00:00
|
|
|
passout:
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* Send the packet to the outgoing interface.
|
|
|
|
* If necessary, do IPv6 fragmentation before sending.
|
2003-10-24 18:26:30 +00:00
|
|
|
*
|
|
|
|
* the logic here is rather complex:
|
|
|
|
* 1: normal case (dontfrag == 0, alwaysfrag == 0)
|
|
|
|
* 1-a: send as is if tlen <= path mtu
|
|
|
|
* 1-b: fragment if tlen > path mtu
|
|
|
|
*
|
|
|
|
* 2: if user asks us not to fragment (dontfrag == 1)
|
|
|
|
* 2-a: send as is if tlen <= interface mtu
|
|
|
|
* 2-b: error if tlen > interface mtu
|
|
|
|
*
|
|
|
|
* 3: if we always need to attach fragment header (alwaysfrag == 1)
|
|
|
|
* always fragment
|
|
|
|
*
|
|
|
|
* 4: if dontfrag == 1 && alwaysfrag == 1
|
|
|
|
* error, as we cannot handle this conflicting request
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2012-05-25 02:17:16 +00:00
|
|
|
sw_csum = m->m_pkthdr.csum_flags;
|
|
|
|
if (!hdrsplit) {
|
|
|
|
tso = ((sw_csum & ifp->if_hwassist & CSUM_TSO) != 0) ? 1 : 0;
|
|
|
|
sw_csum &= ~ifp->if_hwassist;
|
|
|
|
} else
|
|
|
|
tso = 0;
|
|
|
|
/*
|
|
|
|
* If we added extension headers, we will not do TSO and calculate the
|
|
|
|
* checksums ourselves for now.
|
|
|
|
* XXX-BZ Need a framework to know when the NIC can handle it, even
|
|
|
|
* with ext. hdrs.
|
|
|
|
*/
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
if (sw_csum & CSUM_DELAY_DATA_IPV6) {
|
|
|
|
sw_csum &= ~CSUM_DELAY_DATA_IPV6;
|
2012-05-26 23:58:51 +00:00
|
|
|
in6_delayed_cksum(m, plen, sizeof(struct ip6_hdr));
|
2012-05-25 02:17:16 +00:00
|
|
|
}
|
2010-03-12 08:10:30 +00:00
|
|
|
#ifdef SCTP
|
2012-05-30 20:56:07 +00:00
|
|
|
if (sw_csum & CSUM_SCTP_IPV6) {
|
|
|
|
sw_csum &= ~CSUM_SCTP_IPV6;
|
2012-05-25 02:17:16 +00:00
|
|
|
sctp_delayed_cksum(m, sizeof(struct ip6_hdr));
|
2010-03-12 08:10:30 +00:00
|
|
|
}
|
|
|
|
#endif
|
2012-05-25 02:17:16 +00:00
|
|
|
m->m_pkthdr.csum_flags &= ifp->if_hwassist;
|
1999-11-22 02:45:11 +00:00
|
|
|
tlen = m->m_pkthdr.len;
|
2003-10-24 18:26:30 +00:00
|
|
|
|
2012-05-25 02:17:16 +00:00
|
|
|
if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso)
|
2003-10-24 18:26:30 +00:00
|
|
|
dontfrag = 1;
|
|
|
|
else
|
|
|
|
dontfrag = 0;
|
|
|
|
if (dontfrag && alwaysfrag) { /* case 4 */
|
|
|
|
/* conflicting request - can't transmit */
|
|
|
|
error = EMSGSIZE;
|
|
|
|
goto bad;
|
|
|
|
}
|
2012-05-25 02:17:16 +00:00
|
|
|
if (dontfrag && tlen > IN6_LINKMTU(ifp) && !tso) { /* case 2-b */
|
2003-10-24 18:26:30 +00:00
|
|
|
/*
|
|
|
|
* Even if the DONTFRAG option is specified, we cannot send the
|
|
|
|
* packet when the data length is larger than the MTU of the
|
|
|
|
* outgoing interface.
|
2015-03-04 11:20:01 +00:00
|
|
|
* Notify the error by sending IPV6_PATHMTU ancillary data if
|
|
|
|
* application wanted to know the MTU value. Also return an
|
|
|
|
* error code (this is not described in the API spec).
|
2003-10-24 18:26:30 +00:00
|
|
|
*/
|
2015-03-04 11:20:01 +00:00
|
|
|
if (inp != NULL)
|
|
|
|
ip6_notify_pmtu(inp, &dst_sa, (u_int32_t)mtu);
|
2003-10-24 18:26:30 +00:00
|
|
|
error = EMSGSIZE;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* transmit packet without fragmentation
|
|
|
|
*/
|
|
|
|
if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */
|
|
|
|
struct in6_ifaddr *ia6;
|
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
|
|
|
|
if (ia6) {
|
|
|
|
/* Record statistics for this interface address. */
|
2013-10-15 11:37:57 +00:00
|
|
|
counter_u64_add(ia6->ia_ifa.ifa_opackets, 1);
|
|
|
|
counter_u64_add(ia6->ia_ifa.ifa_obytes,
|
|
|
|
m->m_pkthdr.len);
|
2009-06-23 20:19:09 +00:00
|
|
|
ifa_free(&ia6->ia_ifa);
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
2016-03-24 07:54:56 +00:00
|
|
|
error = nd6_output_ifp(ifp, origifp, m, dst,
|
|
|
|
(struct route *)ro);
|
1999-11-22 02:45:11 +00:00
|
|
|
goto done;
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* try to fragment the packet. case 1-b and 3
|
|
|
|
*/
|
|
|
|
if (mtu < IPV6_MMTU) {
|
|
|
|
/* path MTU cannot be less than IPV6_MMTU */
|
1999-11-22 02:45:11 +00:00
|
|
|
error = EMSGSIZE;
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_fragfail);
|
|
|
|
goto bad;
|
2003-10-08 18:26:08 +00:00
|
|
|
} else if (ip6->ip6_plen == 0) {
|
|
|
|
/* jumbo payload cannot be fragmented */
|
1999-11-22 02:45:11 +00:00
|
|
|
error = EMSGSIZE;
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_fragfail);
|
|
|
|
goto bad;
|
|
|
|
} else {
|
|
|
|
u_char nextproto;
|
2007-07-01 11:41:27 +00:00
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* Too large for the destination or interface;
|
|
|
|
* fragment if possible.
|
|
|
|
* Must be able to put at least 8 bytes per fragment.
|
|
|
|
*/
|
|
|
|
hlen = unfragpartlen;
|
|
|
|
if (mtu > IPV6_MAXPACKET)
|
|
|
|
mtu = IPV6_MAXPACKET;
|
2001-06-11 12:39:29 +00:00
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
|
|
|
|
if (len < 8) {
|
|
|
|
error = EMSGSIZE;
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_fragfail);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2012-05-25 02:17:16 +00:00
|
|
|
/*
|
|
|
|
* If the interface will not calculate checksums on
|
|
|
|
* fragmented packets, then do it here.
|
|
|
|
* XXX-BZ handle the hw offloading case. Need flags.
|
|
|
|
*/
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
|
2012-05-26 23:58:51 +00:00
|
|
|
in6_delayed_cksum(m, plen, hlen);
|
It turns out that too many drivers are not only parsing the L2/3/4
headers for TSO but also for generic checksum offloading. Ideally we
would only have one common function shared amongst all drivers, and
perhaps when updating them for IPv6 we should introduce that.
Eventually we should provide the meta information along with mbufs to
avoid (re-)parsing entirely.
To not break IPv6 (checksums and offload) and to be able to MFC the
changes without risking to hurt 3rd party drivers, duplicate the v4
framework, as other OSes have done as well.
Introduce interface capability flags for TX/RX checksum offload with
IPv6, to allow independent toggling (where possible). Add CSUM_*_IPV6
flags for UDP/TCP over IPv6, and reserve further for SCTP, and IPv6
fragmentation. Define CSUM_DELAY_DATA_IPV6 as we do for legacy IP and
add an alias for CSUM_DATA_VALID_IPV6.
This pretty much brings IPv6 handling in line with IPv4.
TSO is still handled in a different way and not via if_hwassist.
Update ifconfig to allow (un)setting of the new capability flags.
Update loopback to announce the new capabilities and if_hwassist flags.
Individual driver updates will have to follow, as will SCTP.
Reported by: gallatin, dim, ..
Reviewed by: gallatin (glanced at?)
MFC after: 3 days
X-MFC with: r235961,235959,235958
2012-05-28 09:30:13 +00:00
|
|
|
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
|
2012-05-25 02:17:16 +00:00
|
|
|
}
|
|
|
|
#ifdef SCTP
|
2012-05-30 20:56:07 +00:00
|
|
|
if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
|
2012-05-25 02:17:16 +00:00
|
|
|
sctp_delayed_cksum(m, hlen);
|
2012-05-30 20:56:07 +00:00
|
|
|
m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
|
2012-05-25 02:17:16 +00:00
|
|
|
}
|
|
|
|
#endif
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* Change the next header field of the last header in the
|
|
|
|
* unfragmentable part.
|
|
|
|
*/
|
|
|
|
if (exthdrs.ip6e_rthdr) {
|
|
|
|
nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
|
|
|
|
*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
|
|
|
|
} else if (exthdrs.ip6e_dest1) {
|
|
|
|
nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
|
|
|
|
*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
|
|
|
|
} else if (exthdrs.ip6e_hbh) {
|
|
|
|
nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
|
|
|
|
*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
|
|
|
|
} else {
|
|
|
|
nextproto = ip6->ip6_nxt;
|
|
|
|
ip6->ip6_nxt = IPPROTO_FRAGMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop through length of segment after first fragment,
|
2002-04-19 04:46:24 +00:00
|
|
|
* make new header and copy data of each part and link onto
|
|
|
|
* chain.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
|
|
|
m0 = m;
|
2015-04-01 12:15:01 +00:00
|
|
|
id = htonl(ip6_randomid());
|
|
|
|
if ((error = ip6_fragment(ifp, m, hlen, nextproto, len, id)))
|
2015-02-16 06:30:27 +00:00
|
|
|
goto sendorfree;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
in6_ifstat_inc(ifp, ifs6_out_fragok);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove leading garbages.
|
|
|
|
*/
|
|
|
|
sendorfree:
|
|
|
|
m = m0->m_nextpkt;
|
|
|
|
m0->m_nextpkt = 0;
|
|
|
|
m_freem(m0);
|
|
|
|
for (m0 = m; m; m = m0) {
|
|
|
|
m0 = m->m_nextpkt;
|
|
|
|
m->m_nextpkt = 0;
|
|
|
|
if (error == 0) {
|
2007-07-05 16:29:40 +00:00
|
|
|
/* Record statistics for this interface address. */
|
|
|
|
if (ia) {
|
2013-10-15 11:37:57 +00:00
|
|
|
counter_u64_add(ia->ia_ifa.ifa_opackets, 1);
|
|
|
|
counter_u64_add(ia->ia_ifa.ifa_obytes,
|
|
|
|
m->m_pkthdr.len);
|
2007-07-05 16:29:40 +00:00
|
|
|
}
|
2016-03-24 07:54:56 +00:00
|
|
|
error = nd6_output_ifp(ifp, origifp, m, dst,
|
|
|
|
(struct route *)ro);
|
1999-11-22 02:45:11 +00:00
|
|
|
} else
|
|
|
|
m_freem(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error == 0)
|
2013-04-09 07:11:22 +00:00
|
|
|
IP6STAT_INC(ip6s_fragmented);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
done:
|
2012-07-04 07:37:53 +00:00
|
|
|
if (ro == &ip6route)
|
|
|
|
RO_RTFREE(ro);
|
2003-10-06 14:02:09 +00:00
|
|
|
return (error);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
freehdrs:
|
|
|
|
m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
|
|
|
|
m_freem(exthdrs.ip6e_dest1);
|
|
|
|
m_freem(exthdrs.ip6e_rthdr);
|
|
|
|
m_freem(exthdrs.ip6e_dest2);
|
2003-10-08 18:26:08 +00:00
|
|
|
/* FALLTHROUGH */
|
1999-11-22 02:45:11 +00:00
|
|
|
bad:
|
2007-07-01 11:41:27 +00:00
|
|
|
if (m)
|
|
|
|
m_freem(m);
|
1999-11-22 02:45:11 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
if (hlen > MCLBYTES)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS); /* XXX */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2013-03-15 13:48:53 +00:00
|
|
|
if (hlen > MLEN)
|
|
|
|
m = m_getcl(M_NOWAIT, MT_DATA, 0);
|
|
|
|
else
|
|
|
|
m = m_get(M_NOWAIT, MT_DATA);
|
|
|
|
if (m == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
1999-11-22 02:45:11 +00:00
|
|
|
m->m_len = hlen;
|
|
|
|
if (hdr)
|
|
|
|
bcopy(hdr, mtod(m, caddr_t), hlen);
|
|
|
|
|
|
|
|
*mp = m;
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert jumbo payload option.
|
|
|
|
*/
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
|
|
|
struct mbuf *mopt;
|
|
|
|
u_char *optbuf;
|
2001-06-11 12:39:29 +00:00
|
|
|
u_int32_t v;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is no hop-by-hop options header, allocate new one.
|
|
|
|
* If there is one but it doesn't have enough space to store the
|
|
|
|
* jumbo payload option, allocate a cluster to store the whole options.
|
|
|
|
* Otherwise, use it to store the options.
|
|
|
|
*/
|
2016-04-15 17:30:33 +00:00
|
|
|
if (exthdrs->ip6e_hbh == NULL) {
|
2013-03-15 13:48:53 +00:00
|
|
|
mopt = m_get(M_NOWAIT, MT_DATA);
|
|
|
|
if (mopt == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
1999-11-22 02:45:11 +00:00
|
|
|
mopt->m_len = JUMBOOPTLEN;
|
|
|
|
optbuf = mtod(mopt, u_char *);
|
|
|
|
optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
|
|
|
|
exthdrs->ip6e_hbh = mopt;
|
|
|
|
} else {
|
|
|
|
struct ip6_hbh *hbh;
|
|
|
|
|
|
|
|
mopt = exthdrs->ip6e_hbh;
|
|
|
|
if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
|
|
|
* XXX assumption:
|
|
|
|
* - exthdrs->ip6e_hbh is not referenced from places
|
|
|
|
* other than exthdrs.
|
|
|
|
* - exthdrs->ip6e_hbh is not an mbuf chain.
|
|
|
|
*/
|
1999-11-22 02:45:11 +00:00
|
|
|
int oldoptlen = mopt->m_len;
|
2001-06-11 12:39:29 +00:00
|
|
|
struct mbuf *n;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
|
|
|
* XXX: give up if the whole (new) hbh header does
|
|
|
|
* not fit even in an mbuf cluster.
|
|
|
|
*/
|
|
|
|
if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
|
|
|
* As a consequence, we must always prepare a cluster
|
|
|
|
* at this point.
|
|
|
|
*/
|
2013-03-15 13:48:53 +00:00
|
|
|
n = m_getcl(M_NOWAIT, MT_DATA, 0);
|
|
|
|
if (n == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
2001-06-11 12:39:29 +00:00
|
|
|
n->m_len = oldoptlen + JUMBOOPTLEN;
|
|
|
|
bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t),
|
2003-10-08 18:26:08 +00:00
|
|
|
oldoptlen);
|
2001-06-11 12:39:29 +00:00
|
|
|
optbuf = mtod(n, caddr_t) + oldoptlen;
|
|
|
|
m_freem(mopt);
|
|
|
|
mopt = exthdrs->ip6e_hbh = n;
|
1999-11-22 02:45:11 +00:00
|
|
|
} else {
|
|
|
|
optbuf = mtod(mopt, u_char *) + mopt->m_len;
|
|
|
|
mopt->m_len += JUMBOOPTLEN;
|
|
|
|
}
|
|
|
|
optbuf[0] = IP6OPT_PADN;
|
|
|
|
optbuf[1] = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust the header length according to the pad and
|
|
|
|
* the jumbo payload option.
|
|
|
|
*/
|
|
|
|
hbh = mtod(mopt, struct ip6_hbh *);
|
|
|
|
hbh->ip6h_len += (JUMBOOPTLEN >> 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill in the option. */
|
|
|
|
optbuf[2] = IP6OPT_JUMBO;
|
|
|
|
optbuf[3] = 4;
|
2001-06-11 12:39:29 +00:00
|
|
|
v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
|
|
|
|
bcopy(&v, &optbuf[4], sizeof(u_int32_t));
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/* finally, adjust the packet header length */
|
|
|
|
exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
|
|
|
|
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
#undef JUMBOOPTLEN
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert fragment header and copy unfragmentable header portions.
|
|
|
|
*/
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen,
|
|
|
|
struct ip6_frag **frghdrp)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
|
|
|
struct mbuf *n, *mlast;
|
|
|
|
|
|
|
|
if (hlen > sizeof(struct ip6_hdr)) {
|
|
|
|
n = m_copym(m0, sizeof(struct ip6_hdr),
|
2012-12-05 08:04:20 +00:00
|
|
|
hlen - sizeof(struct ip6_hdr), M_NOWAIT);
|
2016-04-15 17:30:33 +00:00
|
|
|
if (n == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
1999-11-22 02:45:11 +00:00
|
|
|
m->m_next = n;
|
|
|
|
} else
|
|
|
|
n = m;
|
|
|
|
|
|
|
|
/* Search for the last mbuf of unfragmentable part. */
|
|
|
|
for (mlast = n; mlast->m_next; mlast = mlast->m_next)
|
|
|
|
;
|
|
|
|
|
2014-10-12 15:49:52 +00:00
|
|
|
if (M_WRITABLE(mlast) &&
|
2000-07-04 16:35:15 +00:00
|
|
|
M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {
|
1999-11-22 02:45:11 +00:00
|
|
|
/* use the trailing space of the last mbuf for the fragment hdr */
|
2003-10-08 18:26:08 +00:00
|
|
|
*frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) +
|
|
|
|
mlast->m_len);
|
1999-11-22 02:45:11 +00:00
|
|
|
mlast->m_len += sizeof(struct ip6_frag);
|
|
|
|
m->m_pkthdr.len += sizeof(struct ip6_frag);
|
|
|
|
} else {
|
|
|
|
/* allocate a new mbuf for the fragment header */
|
|
|
|
struct mbuf *mfrg;
|
|
|
|
|
2013-03-15 13:48:53 +00:00
|
|
|
mfrg = m_get(M_NOWAIT, MT_DATA);
|
|
|
|
if (mfrg == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENOBUFS);
|
1999-11-22 02:45:11 +00:00
|
|
|
mfrg->m_len = sizeof(struct ip6_frag);
|
|
|
|
*frghdrp = mtod(mfrg, struct ip6_frag *);
|
|
|
|
mlast->m_next = mfrg;
|
|
|
|
}
|
|
|
|
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
2016-01-03 09:54:03 +00:00
|
|
|
/*
|
|
|
|
* Calculates IPv6 path mtu for destination @dst.
|
|
|
|
* Resulting MTU is stored in @mtup.
|
|
|
|
*
|
|
|
|
* Returns 0 on success.
|
|
|
|
*/
|
2003-10-20 15:27:48 +00:00
|
|
|
static int
|
2016-05-19 12:45:20 +00:00
|
|
|
ip6_getpmtu_ctl(u_int fibnum, const struct in6_addr *dst, u_long *mtup)
|
2016-01-03 09:54:03 +00:00
|
|
|
{
|
2016-01-04 18:32:24 +00:00
|
|
|
struct nhop6_extended nh6;
|
|
|
|
struct in6_addr kdst;
|
|
|
|
uint32_t scopeid;
|
2016-01-03 09:54:03 +00:00
|
|
|
struct ifnet *ifp;
|
|
|
|
u_long mtu;
|
2016-01-04 18:32:24 +00:00
|
|
|
int error;
|
2016-01-03 09:54:03 +00:00
|
|
|
|
2016-01-04 18:32:24 +00:00
|
|
|
in6_splitscope(dst, &kdst, &scopeid);
|
|
|
|
if (fib6_lookup_nh_ext(fibnum, &kdst, scopeid, NHR_REF, 0, &nh6) != 0)
|
2016-01-03 09:54:03 +00:00
|
|
|
return (EHOSTUNREACH);
|
|
|
|
|
2016-01-04 18:32:24 +00:00
|
|
|
ifp = nh6.nh_ifp;
|
|
|
|
mtu = nh6.nh_mtu;
|
2016-01-03 09:54:03 +00:00
|
|
|
|
2016-01-04 18:32:24 +00:00
|
|
|
error = ip6_calcmtu(ifp, dst, mtu, mtup, NULL);
|
|
|
|
fib6_free_nh_ext(fibnum, &nh6);
|
|
|
|
|
|
|
|
return (error);
|
2016-01-03 09:54:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculates IPv6 path MTU for @dst based on transmit @ifp,
|
|
|
|
* and cached data in @ro_pmtu.
|
|
|
|
* MTU from (successful) route lookup is saved (along with dst)
|
|
|
|
* inside @ro_pmtu to avoid subsequent route lookups after packet
|
|
|
|
* filter processing.
|
|
|
|
*
|
|
|
|
* Stores mtu and always-frag value into @mtup and @alwaysfragp.
|
|
|
|
* Returns 0 on success.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ip6_getpmtu(struct route_in6 *ro_pmtu, int do_lookup,
|
2016-05-19 12:45:20 +00:00
|
|
|
struct ifnet *ifp, const struct in6_addr *dst, u_long *mtup,
|
2012-02-24 20:06:04 +00:00
|
|
|
int *alwaysfragp, u_int fibnum)
|
2003-10-20 15:27:48 +00:00
|
|
|
{
|
2016-01-04 18:32:24 +00:00
|
|
|
struct nhop6_basic nh6;
|
|
|
|
struct in6_addr kdst;
|
|
|
|
uint32_t scopeid;
|
2016-01-03 09:54:03 +00:00
|
|
|
struct sockaddr_in6 *sa6_dst;
|
|
|
|
u_long mtu;
|
2003-10-20 15:27:48 +00:00
|
|
|
|
2016-01-03 09:54:03 +00:00
|
|
|
mtu = 0;
|
|
|
|
if (do_lookup) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here ro_pmtu has final destination address, while
|
|
|
|
* ro might represent immediate destination.
|
|
|
|
* Use ro_pmtu destination since mtu might differ.
|
|
|
|
*/
|
|
|
|
sa6_dst = (struct sockaddr_in6 *)&ro_pmtu->ro_dst;
|
|
|
|
if (!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))
|
|
|
|
ro_pmtu->ro_mtu = 0;
|
|
|
|
|
|
|
|
if (ro_pmtu->ro_mtu == 0) {
|
2003-10-20 15:27:48 +00:00
|
|
|
bzero(sa6_dst, sizeof(*sa6_dst));
|
|
|
|
sa6_dst->sin6_family = AF_INET6;
|
|
|
|
sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
|
|
|
|
sa6_dst->sin6_addr = *dst;
|
|
|
|
|
2016-01-04 18:32:24 +00:00
|
|
|
in6_splitscope(dst, &kdst, &scopeid);
|
|
|
|
if (fib6_lookup_nh_basic(fibnum, &kdst, scopeid, 0, 0,
|
|
|
|
&nh6) == 0)
|
|
|
|
ro_pmtu->ro_mtu = nh6.nh_mtu;
|
2003-10-20 15:27:48 +00:00
|
|
|
}
|
2016-01-04 18:32:24 +00:00
|
|
|
|
|
|
|
mtu = ro_pmtu->ro_mtu;
|
2003-10-20 15:27:48 +00:00
|
|
|
}
|
2016-01-03 09:54:03 +00:00
|
|
|
|
|
|
|
if (ro_pmtu->ro_rt)
|
|
|
|
mtu = ro_pmtu->ro_rt->rt_mtu;
|
|
|
|
|
|
|
|
return (ip6_calcmtu(ifp, dst, mtu, mtup, alwaysfragp));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate MTU based on transmit @ifp, route mtu @rt_mtu and
|
|
|
|
* hostcache data for @dst.
|
|
|
|
* Stores mtu and always-frag value into @mtup and @alwaysfragp.
|
|
|
|
*
|
|
|
|
* Returns 0 on success.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ip6_calcmtu(struct ifnet *ifp, const struct in6_addr *dst, u_long rt_mtu,
|
|
|
|
u_long *mtup, int *alwaysfragp)
|
|
|
|
{
|
|
|
|
u_long mtu = 0;
|
|
|
|
int alwaysfrag = 0;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (rt_mtu > 0) {
|
2003-10-20 15:27:48 +00:00
|
|
|
u_int32_t ifmtu;
|
2003-11-20 20:07:39 +00:00
|
|
|
struct in_conninfo inc;
|
|
|
|
|
|
|
|
bzero(&inc, sizeof(inc));
|
2008-12-17 12:52:34 +00:00
|
|
|
inc.inc_flags |= INC_ISIPV6;
|
2003-11-20 20:07:39 +00:00
|
|
|
inc.inc6_faddr = *dst;
|
2003-10-20 15:27:48 +00:00
|
|
|
|
|
|
|
ifmtu = IN6_LINKMTU(ifp);
|
2003-11-20 20:07:39 +00:00
|
|
|
mtu = tcp_hc_getmtu(&inc);
|
|
|
|
if (mtu)
|
2016-01-03 09:54:03 +00:00
|
|
|
mtu = min(mtu, rt_mtu);
|
2003-11-20 20:07:39 +00:00
|
|
|
else
|
2016-01-03 09:54:03 +00:00
|
|
|
mtu = rt_mtu;
|
2003-10-20 15:27:48 +00:00
|
|
|
if (mtu == 0)
|
|
|
|
mtu = ifmtu;
|
2003-10-24 18:26:30 +00:00
|
|
|
else if (mtu < IPV6_MMTU) {
|
|
|
|
/*
|
|
|
|
* RFC2460 section 5, last paragraph:
|
|
|
|
* if we record ICMPv6 too big message with
|
|
|
|
* mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU
|
|
|
|
* or smaller, with framgent header attached.
|
|
|
|
* (fragment header is needed regardless from the
|
|
|
|
* packet size, for translators to identify packets)
|
|
|
|
*/
|
|
|
|
alwaysfrag = 1;
|
|
|
|
mtu = IPV6_MMTU;
|
2003-10-20 15:27:48 +00:00
|
|
|
}
|
|
|
|
} else if (ifp) {
|
|
|
|
mtu = IN6_LINKMTU(ifp);
|
|
|
|
} else
|
|
|
|
error = EHOSTUNREACH; /* XXX */
|
|
|
|
|
|
|
|
*mtup = mtu;
|
2003-10-24 18:26:30 +00:00
|
|
|
if (alwaysfragp)
|
|
|
|
*alwaysfragp = alwaysfrag;
|
2003-10-20 15:27:48 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* IP6 socket option processing.
|
|
|
|
*/
|
|
|
|
int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_ctloutput(struct socket *so, struct sockopt *sopt)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
2008-01-24 08:25:59 +00:00
|
|
|
int optdatalen, uproto;
|
2003-10-24 18:26:30 +00:00
|
|
|
void *optdata;
|
2001-06-11 12:39:29 +00:00
|
|
|
struct inpcb *in6p = sotoinpcb(so);
|
1999-11-22 02:45:11 +00:00
|
|
|
int error, optval;
|
|
|
|
int level, op, optname;
|
|
|
|
int optlen;
|
2001-09-12 08:38:13 +00:00
|
|
|
struct thread *td;
|
2014-07-12 05:46:33 +00:00
|
|
|
#ifdef RSS
|
|
|
|
uint32_t rss_bucket;
|
|
|
|
int retval;
|
|
|
|
#endif
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2008-07-29 09:31:03 +00:00
|
|
|
level = sopt->sopt_level;
|
|
|
|
op = sopt->sopt_dir;
|
|
|
|
optname = sopt->sopt_name;
|
|
|
|
optlen = sopt->sopt_valsize;
|
|
|
|
td = sopt->sopt_td;
|
|
|
|
error = 0;
|
|
|
|
optval = 0;
|
2003-10-24 18:26:30 +00:00
|
|
|
uproto = (int)so->so_proto->pr_protocol;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2011-11-06 10:47:20 +00:00
|
|
|
if (level != IPPROTO_IPV6) {
|
|
|
|
error = EINVAL;
|
|
|
|
|
|
|
|
if (sopt->sopt_level == SOL_SOCKET &&
|
|
|
|
sopt->sopt_dir == SOPT_SET) {
|
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case SO_REUSEADDR:
|
|
|
|
INP_WLOCK(in6p);
|
2013-07-04 18:38:00 +00:00
|
|
|
if ((so->so_options & SO_REUSEADDR) != 0)
|
|
|
|
in6p->inp_flags2 |= INP_REUSEADDR;
|
|
|
|
else
|
|
|
|
in6p->inp_flags2 &= ~INP_REUSEADDR;
|
2011-11-06 10:47:20 +00:00
|
|
|
INP_WUNLOCK(in6p);
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SO_REUSEPORT:
|
|
|
|
INP_WLOCK(in6p);
|
|
|
|
if ((so->so_options & SO_REUSEPORT) != 0)
|
|
|
|
in6p->inp_flags2 |= INP_REUSEPORT;
|
|
|
|
else
|
|
|
|
in6p->inp_flags2 &= ~INP_REUSEPORT;
|
|
|
|
INP_WUNLOCK(in6p);
|
|
|
|
error = 0;
|
|
|
|
break;
|
2012-02-03 11:00:53 +00:00
|
|
|
case SO_SETFIB:
|
|
|
|
INP_WLOCK(in6p);
|
|
|
|
in6p->inp_inc.inc_fibnum = so->so_fibnum;
|
|
|
|
INP_WUNLOCK(in6p);
|
|
|
|
error = 0;
|
|
|
|
break;
|
2011-11-06 10:47:20 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { /* level == IPPROTO_IPV6 */
|
1999-11-22 02:45:11 +00:00
|
|
|
switch (op) {
|
2001-06-11 12:39:29 +00:00
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
case SOPT_SET:
|
|
|
|
switch (optname) {
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292PKTOPTIONS:
|
|
|
|
#ifdef IPV6_PKTOPTIONS
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_PKTOPTIONS:
|
2003-10-24 18:26:30 +00:00
|
|
|
#endif
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
1999-11-22 02:45:11 +00:00
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
error = soopt_getm(sopt, &m); /* XXX */
|
2003-12-23 02:36:43 +00:00
|
|
|
if (error != 0)
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
error = soopt_mcopyin(sopt, m); /* XXX */
|
2003-12-23 02:36:43 +00:00
|
|
|
if (error != 0)
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
2001-06-11 12:39:29 +00:00
|
|
|
error = ip6_pcbopts(&in6p->in6p_outputopts,
|
|
|
|
m, so, sopt);
|
|
|
|
m_freem(m); /* XXX */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use of some Hop-by-Hop options or some
|
|
|
|
* Destination options, might require special
|
|
|
|
* privilege. That is, normal applications
|
|
|
|
* (without special privilege) might be forbidden
|
|
|
|
* from setting certain options in outgoing packets,
|
|
|
|
* and might never see certain options in received
|
|
|
|
* packets. [RFC 2292 Section 6]
|
|
|
|
* KAME specific note:
|
|
|
|
* KAME prevents non-privileged users from sending or
|
|
|
|
* receiving ANY hbh/dst options in order to avoid
|
|
|
|
* overhead of parsing options in the kernel.
|
|
|
|
*/
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVHOPOPTS:
|
|
|
|
case IPV6_RECVDSTOPTS:
|
|
|
|
case IPV6_RECVRTHDRDSTOPTS:
|
2008-01-24 08:25:59 +00:00
|
|
|
if (td != NULL) {
|
|
|
|
error = priv_check(td,
|
|
|
|
PRIV_NETINET_SETHDROPTS);
|
|
|
|
if (error)
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_UNICAST_HOPS:
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_HOPLIMIT:
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVPKTINFO:
|
|
|
|
case IPV6_RECVHOPLIMIT:
|
|
|
|
case IPV6_RECVRTHDR:
|
|
|
|
case IPV6_RECVPATHMTU:
|
|
|
|
case IPV6_RECVTCLASS:
|
2015-09-06 20:57:57 +00:00
|
|
|
case IPV6_RECVFLOWID:
|
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RECVRSSBUCKETID:
|
|
|
|
#endif
|
2001-06-11 12:39:29 +00:00
|
|
|
case IPV6_V6ONLY:
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_AUTOFLOWLABEL:
|
2009-06-01 10:30:00 +00:00
|
|
|
case IPV6_BINDANY:
|
2014-07-12 05:46:33 +00:00
|
|
|
case IPV6_BINDMULTI:
|
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RSS_LISTEN_BUCKET:
|
|
|
|
#endif
|
2009-06-01 10:30:00 +00:00
|
|
|
if (optname == IPV6_BINDANY && td != NULL) {
|
|
|
|
error = priv_check(td,
|
|
|
|
PRIV_NETINET_BINDANY);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
if (optlen != sizeof(int)) {
|
1999-11-22 02:45:11 +00:00
|
|
|
error = EINVAL;
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = sooptcopyin(sopt, &optval,
|
|
|
|
sizeof optval, sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
switch (optname) {
|
|
|
|
|
|
|
|
case IPV6_UNICAST_HOPS:
|
|
|
|
if (optval < -1 || optval >= 256)
|
|
|
|
error = EINVAL;
|
|
|
|
else {
|
|
|
|
/* -1 = kernel default */
|
|
|
|
in6p->in6p_hops = optval;
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
if ((in6p->inp_vflag &
|
2001-06-11 12:39:29 +00:00
|
|
|
INP_IPV4) != 0)
|
|
|
|
in6p->inp_ip_ttl = optval;
|
|
|
|
}
|
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
#define OPTSET(bit) \
|
2001-06-11 12:39:29 +00:00
|
|
|
do { \
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WLOCK(in6p); \
|
1999-11-22 02:45:11 +00:00
|
|
|
if (optval) \
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_flags |= (bit); \
|
1999-11-22 02:45:11 +00:00
|
|
|
else \
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_flags &= ~(bit); \
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WUNLOCK(in6p); \
|
2003-10-08 18:26:08 +00:00
|
|
|
} while (/*CONSTCOND*/ 0)
|
2003-10-24 18:26:30 +00:00
|
|
|
#define OPTSET2292(bit) \
|
|
|
|
do { \
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WLOCK(in6p); \
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_flags |= IN6P_RFC2292; \
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optval) \
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_flags |= (bit); \
|
2003-10-24 18:26:30 +00:00
|
|
|
else \
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_flags &= ~(bit); \
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WUNLOCK(in6p); \
|
2003-10-24 18:26:30 +00:00
|
|
|
} while (/*CONSTCOND*/ 0)
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
#define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0)
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2014-07-12 05:46:33 +00:00
|
|
|
#define OPTSET2(bit, val) do { \
|
|
|
|
INP_WLOCK(in6p); \
|
|
|
|
if (val) \
|
|
|
|
in6p->inp_flags2 |= bit; \
|
|
|
|
else \
|
|
|
|
in6p->inp_flags2 &= ~bit; \
|
|
|
|
INP_WUNLOCK(in6p); \
|
|
|
|
} while (0)
|
|
|
|
#define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0)
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVPKTINFO:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_PKTINFO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_HOPLIMIT:
|
|
|
|
{
|
|
|
|
struct ip6_pktopts **optp;
|
|
|
|
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
optp = &in6p->in6p_outputopts;
|
|
|
|
error = ip6_pcbopt(IPV6_HOPLIMIT,
|
2008-01-24 08:25:59 +00:00
|
|
|
(u_char *)&optval, sizeof(optval),
|
|
|
|
optp, (td != NULL) ? td->td_ucred :
|
|
|
|
NULL, uproto);
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_RECVHOPLIMIT:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_HOPLIMIT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVHOPOPTS:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_HOPOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVDSTOPTS:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_DSTOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVRTHDRDSTOPTS:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_RTHDRDSTOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVRTHDR:
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_RTHDR);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVPATHMTU:
|
|
|
|
/*
|
|
|
|
* We ignore this option for TCP
|
|
|
|
* sockets.
|
2005-07-20 08:59:45 +00:00
|
|
|
* (RFC3542 leaves this case
|
2003-10-24 18:26:30 +00:00
|
|
|
* unspecified.)
|
|
|
|
*/
|
|
|
|
if (uproto != IPPROTO_TCP)
|
|
|
|
OPTSET(IN6P_MTU);
|
|
|
|
break;
|
|
|
|
|
2015-09-06 20:57:57 +00:00
|
|
|
case IPV6_RECVFLOWID:
|
|
|
|
OPTSET2(INP_RECVFLOWID, optval);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RECVRSSBUCKETID:
|
|
|
|
OPTSET2(INP_RECVRSSBUCKETID, optval);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
case IPV6_V6ONLY:
|
2001-06-24 20:25:38 +00:00
|
|
|
/*
|
|
|
|
* make setsockopt(IPV6_V6ONLY)
|
|
|
|
* available only prior to bind(2).
|
|
|
|
* see ipng mailing list, Jun 22 2001.
|
|
|
|
*/
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
if (in6p->inp_lport ||
|
2003-10-08 18:26:08 +00:00
|
|
|
!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
|
2001-06-24 20:25:38 +00:00
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
OPTSET(IN6P_IPV6_V6ONLY);
|
2002-07-24 19:19:53 +00:00
|
|
|
if (optval)
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_vflag &= ~INP_IPV4;
|
2002-07-24 19:19:53 +00:00
|
|
|
else
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
in6p->inp_vflag |= INP_IPV4;
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVTCLASS:
|
|
|
|
/* cannot mix with RFC2292 XXX */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OPTSET(IN6P_TCLASS);
|
|
|
|
break;
|
|
|
|
case IPV6_AUTOFLOWLABEL:
|
|
|
|
OPTSET(IN6P_AUTOFLOWLABEL);
|
|
|
|
break;
|
|
|
|
|
2009-06-01 10:30:00 +00:00
|
|
|
case IPV6_BINDANY:
|
|
|
|
OPTSET(INP_BINDANY);
|
|
|
|
break;
|
2014-07-12 05:46:33 +00:00
|
|
|
|
|
|
|
case IPV6_BINDMULTI:
|
|
|
|
OPTSET2(INP_BINDMULTI, optval);
|
|
|
|
break;
|
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RSS_LISTEN_BUCKET:
|
|
|
|
if ((optval >= 0) &&
|
|
|
|
(optval < rss_getnumbuckets())) {
|
|
|
|
in6p->inp_rss_listen_bucket = optval;
|
|
|
|
OPTSET2(INP_RSS_BUCKET_SET, 1);
|
|
|
|
} else {
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_TCLASS:
|
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
case IPV6_USE_MIN_MTU:
|
|
|
|
case IPV6_PREFER_TEMPADDR:
|
|
|
|
if (optlen != sizeof(optval)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = sooptcopyin(sopt, &optval,
|
|
|
|
sizeof optval, sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
{
|
|
|
|
struct ip6_pktopts **optp;
|
|
|
|
optp = &in6p->in6p_outputopts;
|
|
|
|
error = ip6_pcbopt(optname,
|
2008-01-24 08:25:59 +00:00
|
|
|
(u_char *)&optval, sizeof(optval),
|
|
|
|
optp, (td != NULL) ? td->td_ucred :
|
|
|
|
NULL, uproto);
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_2292PKTINFO:
|
|
|
|
case IPV6_2292HOPLIMIT:
|
|
|
|
case IPV6_2292HOPOPTS:
|
|
|
|
case IPV6_2292DSTOPTS:
|
|
|
|
case IPV6_2292RTHDR:
|
2001-06-11 12:39:29 +00:00
|
|
|
/* RFC 2292 */
|
|
|
|
if (optlen != sizeof(int)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = sooptcopyin(sopt, &optval,
|
|
|
|
sizeof optval, sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
switch (optname) {
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292PKTINFO:
|
|
|
|
OPTSET2292(IN6P_PKTINFO);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292HOPLIMIT:
|
|
|
|
OPTSET2292(IN6P_HOPLIMIT);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292HOPOPTS:
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
|
|
|
* Check super-user privilege.
|
|
|
|
* See comments for IPV6_RECVHOPOPTS.
|
|
|
|
*/
|
2008-01-24 08:25:59 +00:00
|
|
|
if (td != NULL) {
|
|
|
|
error = priv_check(td,
|
|
|
|
PRIV_NETINET_SETHDROPTS);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
OPTSET2292(IN6P_HOPOPTS);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292DSTOPTS:
|
2008-01-24 08:25:59 +00:00
|
|
|
if (td != NULL) {
|
|
|
|
error = priv_check(td,
|
|
|
|
PRIV_NETINET_SETHDROPTS);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292RTHDR:
|
|
|
|
OPTSET2292(IN6P_RTHDR);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_PKTINFO:
|
|
|
|
case IPV6_HOPOPTS:
|
|
|
|
case IPV6_RTHDR:
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
case IPV6_NEXTHOP:
|
|
|
|
{
|
2005-07-20 08:59:45 +00:00
|
|
|
/* new advanced API (RFC3542) */
|
2003-10-24 18:26:30 +00:00
|
|
|
u_char *optbuf;
|
2005-07-28 18:07:07 +00:00
|
|
|
u_char optbuf_storage[MCLBYTES];
|
2003-10-24 18:26:30 +00:00
|
|
|
int optlen;
|
|
|
|
struct ip6_pktopts **optp;
|
|
|
|
|
|
|
|
/* cannot mix with RFC2292 */
|
|
|
|
if (OPTBIT(IN6P_RFC2292)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-07-28 18:07:07 +00:00
|
|
|
/*
|
|
|
|
* We only ensure valsize is not too large
|
|
|
|
* here. Further validation will be done
|
|
|
|
* later.
|
|
|
|
*/
|
|
|
|
error = sooptcopyin(sopt, optbuf_storage,
|
|
|
|
sizeof(optbuf_storage), 0);
|
2004-03-26 19:52:18 +00:00
|
|
|
if (error)
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
optlen = sopt->sopt_valsize;
|
2005-07-28 18:07:07 +00:00
|
|
|
optbuf = optbuf_storage;
|
2003-10-24 18:26:30 +00:00
|
|
|
optp = &in6p->in6p_outputopts;
|
2008-01-24 08:25:59 +00:00
|
|
|
error = ip6_pcbopt(optname, optbuf, optlen,
|
|
|
|
optp, (td != NULL) ? td->td_ucred : NULL,
|
|
|
|
uproto);
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
#undef OPTSET
|
|
|
|
|
|
|
|
case IPV6_MULTICAST_IF:
|
|
|
|
case IPV6_MULTICAST_HOPS:
|
|
|
|
case IPV6_MULTICAST_LOOP:
|
|
|
|
case IPV6_JOIN_GROUP:
|
|
|
|
case IPV6_LEAVE_GROUP:
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
case IPV6_MSFILTER:
|
|
|
|
case MCAST_BLOCK_SOURCE:
|
|
|
|
case MCAST_UNBLOCK_SOURCE:
|
|
|
|
case MCAST_JOIN_GROUP:
|
|
|
|
case MCAST_LEAVE_GROUP:
|
|
|
|
case MCAST_JOIN_SOURCE_GROUP:
|
|
|
|
case MCAST_LEAVE_SOURCE_GROUP:
|
|
|
|
error = ip6_setmoptions(in6p, sopt);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
case IPV6_PORTRANGE:
|
|
|
|
error = sooptcopyin(sopt, &optval,
|
|
|
|
sizeof optval, sizeof optval);
|
|
|
|
if (error)
|
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WLOCK(in6p);
|
2000-07-04 16:35:15 +00:00
|
|
|
switch (optval) {
|
|
|
|
case IPV6_PORTRANGE_DEFAULT:
|
2008-12-17 13:00:18 +00:00
|
|
|
in6p->inp_flags &= ~(INP_LOWPORT);
|
|
|
|
in6p->inp_flags &= ~(INP_HIGHPORT);
|
2000-07-04 16:35:15 +00:00
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
case IPV6_PORTRANGE_HIGH:
|
2008-12-17 13:00:18 +00:00
|
|
|
in6p->inp_flags &= ~(INP_LOWPORT);
|
|
|
|
in6p->inp_flags |= INP_HIGHPORT;
|
2000-07-04 16:35:15 +00:00
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
case IPV6_PORTRANGE_LOW:
|
2008-12-17 13:00:18 +00:00
|
|
|
in6p->inp_flags &= ~(INP_HIGHPORT);
|
|
|
|
in6p->inp_flags |= INP_LOWPORT;
|
2000-07-04 16:35:15 +00:00
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2012-08-19 08:16:13 +00:00
|
|
|
INP_WUNLOCK(in6p);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
2007-07-03 12:13:45 +00:00
|
|
|
#ifdef IPSEC
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_IPSEC_POLICY:
|
2008-02-02 14:11:31 +00:00
|
|
|
{
|
|
|
|
caddr_t req;
|
1999-11-22 02:45:11 +00:00
|
|
|
struct mbuf *m;
|
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
2000-07-04 16:35:15 +00:00
|
|
|
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
2008-02-02 14:11:31 +00:00
|
|
|
req = mtod(m, caddr_t);
|
2009-02-08 09:27:07 +00:00
|
|
|
error = ipsec_set_policy(in6p, optname, req,
|
2008-02-02 14:11:31 +00:00
|
|
|
m->m_len, (sopt->sopt_td != NULL) ?
|
|
|
|
sopt->sopt_td->td_ucred : NULL);
|
1999-11-22 02:45:11 +00:00
|
|
|
m_freem(m);
|
|
|
|
break;
|
2008-02-02 14:11:31 +00:00
|
|
|
}
|
2007-07-03 12:13:45 +00:00
|
|
|
#endif /* IPSEC */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOPT_GET:
|
|
|
|
switch (optname) {
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292PKTOPTIONS:
|
|
|
|
#ifdef IPV6_PKTOPTIONS
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_PKTOPTIONS:
|
2003-10-24 18:26:30 +00:00
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* RFC3542 (effectively) deprecated the
|
|
|
|
* semantics of the 2292-style pktoptions.
|
|
|
|
* Since it was not reliable in nature (i.e.,
|
|
|
|
* applications had to expect the lack of some
|
|
|
|
* information after all), it would make sense
|
|
|
|
* to simplify this part by always returning
|
|
|
|
* empty data.
|
|
|
|
*/
|
|
|
|
sopt->sopt_valsize = 0;
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVHOPOPTS:
|
|
|
|
case IPV6_RECVDSTOPTS:
|
|
|
|
case IPV6_RECVRTHDRDSTOPTS:
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_UNICAST_HOPS:
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVPKTINFO:
|
|
|
|
case IPV6_RECVHOPLIMIT:
|
|
|
|
case IPV6_RECVRTHDR:
|
|
|
|
case IPV6_RECVPATHMTU:
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
case IPV6_V6ONLY:
|
2000-01-13 05:07:42 +00:00
|
|
|
case IPV6_PORTRANGE:
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVTCLASS:
|
|
|
|
case IPV6_AUTOFLOWLABEL:
|
2010-09-24 14:38:54 +00:00
|
|
|
case IPV6_BINDANY:
|
2014-07-12 05:46:33 +00:00
|
|
|
case IPV6_FLOWID:
|
|
|
|
case IPV6_FLOWTYPE:
|
2015-09-06 20:57:57 +00:00
|
|
|
case IPV6_RECVFLOWID:
|
2014-07-12 05:46:33 +00:00
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RSSBUCKETID:
|
2015-09-06 20:57:57 +00:00
|
|
|
case IPV6_RECVRSSBUCKETID:
|
2014-07-12 05:46:33 +00:00
|
|
|
#endif
|
2015-12-30 18:08:05 +00:00
|
|
|
case IPV6_BINDMULTI:
|
1999-11-22 02:45:11 +00:00
|
|
|
switch (optname) {
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVHOPOPTS:
|
|
|
|
optval = OPTBIT(IN6P_HOPOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVDSTOPTS:
|
|
|
|
optval = OPTBIT(IN6P_DSTOPTS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVRTHDRDSTOPTS:
|
|
|
|
optval = OPTBIT(IN6P_RTHDRDSTOPTS);
|
|
|
|
break;
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_UNICAST_HOPS:
|
|
|
|
optval = in6p->in6p_hops;
|
|
|
|
break;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVPKTINFO:
|
|
|
|
optval = OPTBIT(IN6P_PKTINFO);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVHOPLIMIT:
|
|
|
|
optval = OPTBIT(IN6P_HOPLIMIT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVRTHDR:
|
|
|
|
optval = OPTBIT(IN6P_RTHDR);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_RECVPATHMTU:
|
|
|
|
optval = OPTBIT(IN6P_MTU);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
case IPV6_V6ONLY:
|
2002-07-22 15:51:02 +00:00
|
|
|
optval = OPTBIT(IN6P_IPV6_V6ONLY);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_PORTRANGE:
|
|
|
|
{
|
|
|
|
int flags;
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
flags = in6p->inp_flags;
|
2008-12-17 13:00:18 +00:00
|
|
|
if (flags & INP_HIGHPORT)
|
1999-11-22 02:45:11 +00:00
|
|
|
optval = IPV6_PORTRANGE_HIGH;
|
2008-12-17 13:00:18 +00:00
|
|
|
else if (flags & INP_LOWPORT)
|
1999-11-22 02:45:11 +00:00
|
|
|
optval = IPV6_PORTRANGE_LOW;
|
|
|
|
else
|
|
|
|
optval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_RECVTCLASS:
|
|
|
|
optval = OPTBIT(IN6P_TCLASS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_AUTOFLOWLABEL:
|
|
|
|
optval = OPTBIT(IN6P_AUTOFLOWLABEL);
|
|
|
|
break;
|
2009-06-01 10:30:00 +00:00
|
|
|
|
|
|
|
case IPV6_BINDANY:
|
|
|
|
optval = OPTBIT(INP_BINDANY);
|
|
|
|
break;
|
2014-07-12 05:46:33 +00:00
|
|
|
|
|
|
|
case IPV6_FLOWID:
|
|
|
|
optval = in6p->inp_flowid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_FLOWTYPE:
|
|
|
|
optval = in6p->inp_flowtype;
|
|
|
|
break;
|
2015-09-06 20:57:57 +00:00
|
|
|
|
|
|
|
case IPV6_RECVFLOWID:
|
|
|
|
optval = OPTBIT2(INP_RECVFLOWID);
|
|
|
|
break;
|
2014-07-12 05:46:33 +00:00
|
|
|
#ifdef RSS
|
|
|
|
case IPV6_RSSBUCKETID:
|
|
|
|
retval =
|
|
|
|
rss_hash2bucket(in6p->inp_flowid,
|
|
|
|
in6p->inp_flowtype,
|
|
|
|
&rss_bucket);
|
|
|
|
if (retval == 0)
|
|
|
|
optval = rss_bucket;
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
2015-09-06 20:57:57 +00:00
|
|
|
|
|
|
|
case IPV6_RECVRSSBUCKETID:
|
|
|
|
optval = OPTBIT2(INP_RECVRSSBUCKETID);
|
|
|
|
break;
|
2014-07-12 05:46:33 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case IPV6_BINDMULTI:
|
|
|
|
optval = OPTBIT2(INP_BINDMULTI);
|
|
|
|
break;
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (error)
|
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
error = sooptcopyout(sopt, &optval,
|
|
|
|
sizeof optval);
|
|
|
|
break;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_PATHMTU:
|
|
|
|
{
|
|
|
|
u_long pmtu = 0;
|
|
|
|
struct ip6_mtuinfo mtuinfo;
|
|
|
|
|
|
|
|
if (!(so->so_state & SS_ISCONNECTED))
|
|
|
|
return (ENOTCONN);
|
|
|
|
/*
|
|
|
|
* XXX: we dot not consider the case of source
|
|
|
|
* routing, or optional information to specify
|
|
|
|
* the outgoing interface.
|
|
|
|
*/
|
2016-01-03 09:54:03 +00:00
|
|
|
error = ip6_getpmtu_ctl(so->so_fibnum,
|
|
|
|
&in6p->in6p_faddr, &pmtu);
|
2003-10-24 18:26:30 +00:00
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
if (pmtu > IPV6_MAXPACKET)
|
|
|
|
pmtu = IPV6_MAXPACKET;
|
|
|
|
|
|
|
|
bzero(&mtuinfo, sizeof(mtuinfo));
|
|
|
|
mtuinfo.ip6m_mtu = (u_int32_t)pmtu;
|
|
|
|
optdata = (void *)&mtuinfo;
|
|
|
|
optdatalen = sizeof(mtuinfo);
|
|
|
|
error = sooptcopyout(sopt, optdata,
|
|
|
|
optdatalen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_2292PKTINFO:
|
|
|
|
case IPV6_2292HOPLIMIT:
|
|
|
|
case IPV6_2292HOPOPTS:
|
|
|
|
case IPV6_2292RTHDR:
|
|
|
|
case IPV6_2292DSTOPTS:
|
2001-06-11 12:39:29 +00:00
|
|
|
switch (optname) {
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292PKTINFO:
|
2001-06-11 12:39:29 +00:00
|
|
|
optval = OPTBIT(IN6P_PKTINFO);
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292HOPLIMIT:
|
2001-06-11 12:39:29 +00:00
|
|
|
optval = OPTBIT(IN6P_HOPLIMIT);
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292HOPOPTS:
|
2001-06-11 12:39:29 +00:00
|
|
|
optval = OPTBIT(IN6P_HOPOPTS);
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292RTHDR:
|
2001-06-11 12:39:29 +00:00
|
|
|
optval = OPTBIT(IN6P_RTHDR);
|
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292DSTOPTS:
|
2001-06-11 12:39:29 +00:00
|
|
|
optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = sooptcopyout(sopt, &optval,
|
2003-10-24 18:26:30 +00:00
|
|
|
sizeof optval);
|
|
|
|
break;
|
|
|
|
case IPV6_PKTINFO:
|
|
|
|
case IPV6_HOPOPTS:
|
|
|
|
case IPV6_RTHDR:
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
case IPV6_NEXTHOP:
|
|
|
|
case IPV6_TCLASS:
|
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
case IPV6_USE_MIN_MTU:
|
|
|
|
case IPV6_PREFER_TEMPADDR:
|
|
|
|
error = ip6_getpcbopt(in6p->in6p_outputopts,
|
|
|
|
optname, sopt);
|
2001-06-11 12:39:29 +00:00
|
|
|
break;
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_MULTICAST_IF:
|
|
|
|
case IPV6_MULTICAST_HOPS:
|
|
|
|
case IPV6_MULTICAST_LOOP:
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
case IPV6_MSFILTER:
|
|
|
|
error = ip6_getmoptions(in6p, sopt);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
|
2007-07-03 12:13:45 +00:00
|
|
|
#ifdef IPSEC
|
1999-11-22 02:45:11 +00:00
|
|
|
case IPV6_IPSEC_POLICY:
|
|
|
|
{
|
|
|
|
caddr_t req = NULL;
|
2000-07-04 16:35:15 +00:00
|
|
|
size_t len = 0;
|
|
|
|
struct mbuf *m = NULL;
|
|
|
|
struct mbuf **mp = &m;
|
2003-10-24 18:26:30 +00:00
|
|
|
size_t ovalsize = sopt->sopt_valsize;
|
|
|
|
caddr_t oval = (caddr_t)sopt->sopt_val;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2000-07-04 16:35:15 +00:00
|
|
|
error = soopt_getm(sopt, &m); /* XXX */
|
2003-12-23 11:01:17 +00:00
|
|
|
if (error != 0)
|
2000-07-04 16:35:15 +00:00
|
|
|
break;
|
|
|
|
error = soopt_mcopyin(sopt, m); /* XXX */
|
2003-12-23 11:01:17 +00:00
|
|
|
if (error != 0)
|
2000-07-04 16:35:15 +00:00
|
|
|
break;
|
2003-10-24 18:26:30 +00:00
|
|
|
sopt->sopt_valsize = ovalsize;
|
|
|
|
sopt->sopt_val = oval;
|
2000-07-04 16:35:15 +00:00
|
|
|
if (m) {
|
1999-11-22 02:45:11 +00:00
|
|
|
req = mtod(m, caddr_t);
|
2000-07-04 16:35:15 +00:00
|
|
|
len = m->m_len;
|
|
|
|
}
|
2009-02-08 09:27:07 +00:00
|
|
|
error = ipsec_get_policy(in6p, req, len, mp);
|
1999-11-22 02:45:11 +00:00
|
|
|
if (error == 0)
|
2003-10-08 18:26:08 +00:00
|
|
|
error = soopt_mcopyout(sopt, m); /* XXX */
|
2001-06-11 12:39:29 +00:00
|
|
|
if (error == 0 && m)
|
|
|
|
m_freem(m);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-07-03 12:13:45 +00:00
|
|
|
#endif /* IPSEC */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-10-06 14:02:09 +00:00
|
|
|
return (error);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
2003-10-26 18:17:01 +00:00
|
|
|
int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_raw_ctloutput(struct socket *so, struct sockopt *sopt)
|
2003-10-26 18:17:01 +00:00
|
|
|
{
|
|
|
|
int error = 0, optval, optlen;
|
|
|
|
const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
struct inpcb *in6p = sotoinpcb(so);
|
2003-10-26 18:17:01 +00:00
|
|
|
int level, op, optname;
|
|
|
|
|
2008-07-29 09:31:03 +00:00
|
|
|
level = sopt->sopt_level;
|
|
|
|
op = sopt->sopt_dir;
|
|
|
|
optname = sopt->sopt_name;
|
|
|
|
optlen = sopt->sopt_valsize;
|
2003-10-26 18:17:01 +00:00
|
|
|
|
|
|
|
if (level != IPPROTO_IPV6) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_CHECKSUM:
|
|
|
|
/*
|
|
|
|
* For ICMPv6 sockets, no modification allowed for checksum
|
|
|
|
* offset, permit "no change" values to help existing apps.
|
|
|
|
*
|
2005-07-20 08:59:45 +00:00
|
|
|
* RFC3542 says: "An attempt to set IPV6_CHECKSUM
|
2003-10-26 18:17:01 +00:00
|
|
|
* for an ICMPv6 socket will fail."
|
2005-07-20 08:59:45 +00:00
|
|
|
* The current behavior does not meet RFC3542.
|
2003-10-26 18:17:01 +00:00
|
|
|
*/
|
|
|
|
switch (op) {
|
|
|
|
case SOPT_SET:
|
|
|
|
if (optlen != sizeof(int)) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = sooptcopyin(sopt, &optval, sizeof(optval),
|
|
|
|
sizeof(optval));
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
if ((optval % 2) != 0) {
|
|
|
|
/* the API assumes even offset values */
|
|
|
|
error = EINVAL;
|
|
|
|
} else if (so->so_proto->pr_protocol ==
|
|
|
|
IPPROTO_ICMPV6) {
|
|
|
|
if (optval != icmp6off)
|
|
|
|
error = EINVAL;
|
|
|
|
} else
|
|
|
|
in6p->in6p_cksum = optval;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOPT_GET:
|
|
|
|
if (so->so_proto->pr_protocol == IPPROTO_ICMPV6)
|
|
|
|
optval = icmp6off;
|
|
|
|
else
|
|
|
|
optval = in6p->in6p_cksum;
|
|
|
|
|
|
|
|
error = sooptcopyout(sopt, &optval, sizeof(optval));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
2001-06-11 12:39:29 +00:00
|
|
|
* Set up IP6 options in pcb for insertion in output packets or
|
|
|
|
* specifying behavior of outgoing packets.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m,
|
|
|
|
struct socket *so, struct sockopt *sopt)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
2001-06-11 12:39:29 +00:00
|
|
|
struct ip6_pktopts *opt = *pktopt;
|
1999-11-22 02:45:11 +00:00
|
|
|
int error = 0;
|
2001-09-12 08:38:13 +00:00
|
|
|
struct thread *td = sopt->sopt_td;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/* turn off any old options. */
|
|
|
|
if (opt) {
|
2001-06-11 12:39:29 +00:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (opt->ip6po_pktinfo || opt->ip6po_nexthop ||
|
|
|
|
opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 ||
|
|
|
|
opt->ip6po_rhinfo.ip6po_rhi_rthdr)
|
|
|
|
printf("ip6_pcbopts: all specified options are cleared.\n");
|
|
|
|
#endif
|
2003-10-24 18:26:30 +00:00
|
|
|
ip6_clearpktopts(opt, -1);
|
1999-11-22 02:45:11 +00:00
|
|
|
} else
|
2003-02-19 05:47:46 +00:00
|
|
|
opt = malloc(sizeof(*opt), M_IP6OPT, M_WAITOK);
|
2001-06-11 12:39:29 +00:00
|
|
|
*pktopt = NULL;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
if (!m || m->m_len == 0) {
|
|
|
|
/*
|
2002-10-31 19:45:48 +00:00
|
|
|
* Only turning off any previous options, regardless of
|
|
|
|
* whether the opt is just created or given.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2002-10-31 19:45:48 +00:00
|
|
|
free(opt, M_IP6OPT);
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set options specified by user. */
|
2008-01-24 08:25:59 +00:00
|
|
|
if ((error = ip6_setpktopts(m, opt, NULL, (td != NULL) ?
|
|
|
|
td->td_ucred : NULL, so->so_proto->pr_protocol)) != 0) {
|
2003-10-24 18:26:30 +00:00
|
|
|
ip6_clearpktopts(opt, -1); /* XXX: discard all options */
|
2002-10-31 19:45:48 +00:00
|
|
|
free(opt, M_IP6OPT);
|
2003-10-06 14:02:09 +00:00
|
|
|
return (error);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
*pktopt = opt;
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
|
|
|
* initialize ip6_pktopts. beware that there are non-zero default values in
|
|
|
|
* the struct.
|
|
|
|
*/
|
|
|
|
void
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_initpktopts(struct ip6_pktopts *opt)
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
bzero(opt, sizeof(*opt));
|
|
|
|
opt->ip6po_hlim = -1; /* -1 means default hop limit */
|
2003-10-24 18:26:30 +00:00
|
|
|
opt->ip6po_tclass = -1; /* -1 means default traffic class */
|
|
|
|
opt->ip6po_minmtu = IP6PO_MINMTU_MCASTONLY;
|
|
|
|
opt->ip6po_prefer_tempaddr = IP6PO_TEMPADDR_SYSTEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt,
|
2008-01-24 08:25:59 +00:00
|
|
|
struct ucred *cred, int uproto)
|
2003-10-24 18:26:30 +00:00
|
|
|
{
|
|
|
|
struct ip6_pktopts *opt;
|
|
|
|
|
|
|
|
if (*pktopt == NULL) {
|
|
|
|
*pktopt = malloc(sizeof(struct ip6_pktopts), M_IP6OPT,
|
|
|
|
M_WAITOK);
|
2005-07-21 15:06:32 +00:00
|
|
|
ip6_initpktopts(*pktopt);
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
|
|
|
opt = *pktopt;
|
|
|
|
|
2008-01-24 08:25:59 +00:00
|
|
|
return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto));
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
|
2003-10-24 18:26:30 +00:00
|
|
|
{
|
|
|
|
void *optdata = NULL;
|
|
|
|
int optdatalen = 0;
|
|
|
|
struct ip6_ext *ip6e;
|
|
|
|
int error = 0;
|
|
|
|
struct in6_pktinfo null_pktinfo;
|
|
|
|
int deftclass = 0, on;
|
|
|
|
int defminmtu = IP6PO_MINMTU_MCASTONLY;
|
|
|
|
int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
|
|
|
|
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_PKTINFO:
|
2015-07-03 19:01:38 +00:00
|
|
|
optdata = (void *)&null_pktinfo;
|
|
|
|
if (pktopt && pktopt->ip6po_pktinfo) {
|
|
|
|
bcopy(pktopt->ip6po_pktinfo, &null_pktinfo,
|
|
|
|
sizeof(null_pktinfo));
|
|
|
|
in6_clearscope(&null_pktinfo.ipi6_addr);
|
|
|
|
} else {
|
2003-10-24 18:26:30 +00:00
|
|
|
/* XXX: we don't have to do this every time... */
|
|
|
|
bzero(&null_pktinfo, sizeof(null_pktinfo));
|
|
|
|
}
|
|
|
|
optdatalen = sizeof(struct in6_pktinfo);
|
|
|
|
break;
|
|
|
|
case IPV6_TCLASS:
|
|
|
|
if (pktopt && pktopt->ip6po_tclass >= 0)
|
|
|
|
optdata = (void *)&pktopt->ip6po_tclass;
|
|
|
|
else
|
|
|
|
optdata = (void *)&deftclass;
|
|
|
|
optdatalen = sizeof(int);
|
|
|
|
break;
|
|
|
|
case IPV6_HOPOPTS:
|
|
|
|
if (pktopt && pktopt->ip6po_hbh) {
|
|
|
|
optdata = (void *)pktopt->ip6po_hbh;
|
|
|
|
ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
|
|
|
|
optdatalen = (ip6e->ip6e_len + 1) << 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPV6_RTHDR:
|
|
|
|
if (pktopt && pktopt->ip6po_rthdr) {
|
|
|
|
optdata = (void *)pktopt->ip6po_rthdr;
|
|
|
|
ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
|
|
|
|
optdatalen = (ip6e->ip6e_len + 1) << 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
if (pktopt && pktopt->ip6po_dest1) {
|
|
|
|
optdata = (void *)pktopt->ip6po_dest1;
|
|
|
|
ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
|
|
|
|
optdatalen = (ip6e->ip6e_len + 1) << 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
if (pktopt && pktopt->ip6po_dest2) {
|
|
|
|
optdata = (void *)pktopt->ip6po_dest2;
|
|
|
|
ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
|
|
|
|
optdatalen = (ip6e->ip6e_len + 1) << 3;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPV6_NEXTHOP:
|
|
|
|
if (pktopt && pktopt->ip6po_nexthop) {
|
|
|
|
optdata = (void *)pktopt->ip6po_nexthop;
|
|
|
|
optdatalen = pktopt->ip6po_nexthop->sa_len;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IPV6_USE_MIN_MTU:
|
|
|
|
if (pktopt)
|
|
|
|
optdata = (void *)&pktopt->ip6po_minmtu;
|
|
|
|
else
|
|
|
|
optdata = (void *)&defminmtu;
|
|
|
|
optdatalen = sizeof(int);
|
|
|
|
break;
|
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
if (pktopt && ((pktopt->ip6po_flags) & IP6PO_DONTFRAG))
|
|
|
|
on = 1;
|
|
|
|
else
|
|
|
|
on = 0;
|
|
|
|
optdata = (void *)&on;
|
|
|
|
optdatalen = sizeof(on);
|
|
|
|
break;
|
|
|
|
case IPV6_PREFER_TEMPADDR:
|
|
|
|
if (pktopt)
|
|
|
|
optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
|
|
|
|
else
|
|
|
|
optdata = (void *)&defpreftemp;
|
|
|
|
optdatalen = sizeof(int);
|
|
|
|
break;
|
|
|
|
default: /* should not happen */
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
panic("ip6_getpcbopt: unexpected option\n");
|
|
|
|
#endif
|
|
|
|
return (ENOPROTOOPT);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = sooptcopyout(sopt, optdata, optdatalen);
|
|
|
|
|
|
|
|
return (error);
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_clearpktopts(struct ip6_pktopts *pktopt, int optname)
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
2003-11-24 01:53:36 +00:00
|
|
|
if (pktopt == NULL)
|
|
|
|
return;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_PKTINFO) {
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_pktinfo)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_pktinfo, M_IP6OPT);
|
|
|
|
pktopt->ip6po_pktinfo = NULL;
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_HOPLIMIT)
|
2001-06-11 12:39:29 +00:00
|
|
|
pktopt->ip6po_hlim = -1;
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_TCLASS)
|
|
|
|
pktopt->ip6po_tclass = -1;
|
|
|
|
if (optname == -1 || optname == IPV6_NEXTHOP) {
|
|
|
|
if (pktopt->ip6po_nextroute.ro_rt) {
|
|
|
|
RTFREE(pktopt->ip6po_nextroute.ro_rt);
|
|
|
|
pktopt->ip6po_nextroute.ro_rt = NULL;
|
|
|
|
}
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_nexthop)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_nexthop, M_IP6OPT);
|
|
|
|
pktopt->ip6po_nexthop = NULL;
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_HOPOPTS) {
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_hbh)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_hbh, M_IP6OPT);
|
|
|
|
pktopt->ip6po_hbh = NULL;
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) {
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_dest1)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_dest1, M_IP6OPT);
|
|
|
|
pktopt->ip6po_dest1 = NULL;
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_RTHDR) {
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_rhinfo.ip6po_rhi_rthdr)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT);
|
|
|
|
pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL;
|
|
|
|
if (pktopt->ip6po_route.ro_rt) {
|
|
|
|
RTFREE(pktopt->ip6po_route.ro_rt);
|
|
|
|
pktopt->ip6po_route.ro_rt = NULL;
|
|
|
|
}
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == -1 || optname == IPV6_DSTOPTS) {
|
2005-07-21 16:39:23 +00:00
|
|
|
if (pktopt->ip6po_dest2)
|
2001-06-11 12:39:29 +00:00
|
|
|
free(pktopt->ip6po_dest2, M_IP6OPT);
|
|
|
|
pktopt->ip6po_dest2 = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PKTOPT_EXTHDRCPY(type) \
|
|
|
|
do {\
|
|
|
|
if (src->type) {\
|
2003-10-08 18:26:08 +00:00
|
|
|
int hlen = (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\
|
2001-06-11 12:39:29 +00:00
|
|
|
dst->type = malloc(hlen, M_IP6OPT, canwait);\
|
|
|
|
if (dst->type == NULL && canwait == M_NOWAIT)\
|
|
|
|
goto bad;\
|
|
|
|
bcopy(src->type, dst->type, hlen);\
|
|
|
|
}\
|
2003-10-08 18:26:08 +00:00
|
|
|
} while (/*CONSTCOND*/ 0)
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2005-07-21 16:39:23 +00:00
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait)
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
2005-07-21 16:39:23 +00:00
|
|
|
if (dst == NULL || src == NULL) {
|
2001-06-11 12:39:29 +00:00
|
|
|
printf("ip6_clearpktopts: invalid argument\n");
|
2005-07-21 16:39:23 +00:00
|
|
|
return (EINVAL);
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dst->ip6po_hlim = src->ip6po_hlim;
|
2003-10-24 18:26:30 +00:00
|
|
|
dst->ip6po_tclass = src->ip6po_tclass;
|
|
|
|
dst->ip6po_flags = src->ip6po_flags;
|
2011-09-20 00:29:17 +00:00
|
|
|
dst->ip6po_minmtu = src->ip6po_minmtu;
|
|
|
|
dst->ip6po_prefer_tempaddr = src->ip6po_prefer_tempaddr;
|
2001-06-11 12:39:29 +00:00
|
|
|
if (src->ip6po_pktinfo) {
|
|
|
|
dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo),
|
2003-10-08 18:26:08 +00:00
|
|
|
M_IP6OPT, canwait);
|
2007-07-01 11:41:27 +00:00
|
|
|
if (dst->ip6po_pktinfo == NULL)
|
2001-06-11 12:39:29 +00:00
|
|
|
goto bad;
|
|
|
|
*dst->ip6po_pktinfo = *src->ip6po_pktinfo;
|
|
|
|
}
|
|
|
|
if (src->ip6po_nexthop) {
|
|
|
|
dst->ip6po_nexthop = malloc(src->ip6po_nexthop->sa_len,
|
2003-10-08 18:26:08 +00:00
|
|
|
M_IP6OPT, canwait);
|
2005-05-15 02:28:30 +00:00
|
|
|
if (dst->ip6po_nexthop == NULL)
|
2001-06-11 12:39:29 +00:00
|
|
|
goto bad;
|
|
|
|
bcopy(src->ip6po_nexthop, dst->ip6po_nexthop,
|
2003-10-08 18:26:08 +00:00
|
|
|
src->ip6po_nexthop->sa_len);
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
|
|
|
PKTOPT_EXTHDRCPY(ip6po_hbh);
|
|
|
|
PKTOPT_EXTHDRCPY(ip6po_dest1);
|
|
|
|
PKTOPT_EXTHDRCPY(ip6po_dest2);
|
|
|
|
PKTOPT_EXTHDRCPY(ip6po_rthdr); /* not copy the cached route */
|
2005-07-21 16:39:23 +00:00
|
|
|
return (0);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
bad:
|
2007-11-21 16:01:42 +00:00
|
|
|
ip6_clearpktopts(dst, -1);
|
2005-07-21 16:39:23 +00:00
|
|
|
return (ENOBUFS);
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
|
|
|
#undef PKTOPT_EXTHDRCPY
|
|
|
|
|
2005-07-21 16:39:23 +00:00
|
|
|
struct ip6_pktopts *
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_copypktopts(struct ip6_pktopts *src, int canwait)
|
2005-07-21 16:39:23 +00:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
struct ip6_pktopts *dst;
|
|
|
|
|
|
|
|
dst = malloc(sizeof(*dst), M_IP6OPT, canwait);
|
2007-07-01 11:41:27 +00:00
|
|
|
if (dst == NULL)
|
2005-07-21 16:39:23 +00:00
|
|
|
return (NULL);
|
|
|
|
ip6_initpktopts(dst);
|
|
|
|
|
|
|
|
if ((error = copypktopts(dst, src, canwait)) != 0) {
|
|
|
|
free(dst, M_IP6OPT);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (dst);
|
|
|
|
}
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
void
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_freepcbopts(struct ip6_pktopts *pktopt)
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
|
|
|
if (pktopt == NULL)
|
|
|
|
return;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
ip6_clearpktopts(pktopt, -1);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
|
|
|
free(pktopt, M_IP6OPT);
|
|
|
|
}
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
/*
|
|
|
|
* Set IPv6 outgoing packet options based on advanced API.
|
|
|
|
*/
|
|
|
|
int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt,
|
2008-01-24 08:25:59 +00:00
|
|
|
struct ip6_pktopts *stickyopt, struct ucred *cred, int uproto)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
2016-04-15 17:30:33 +00:00
|
|
|
struct cmsghdr *cm = NULL;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-21 14:57:53 +00:00
|
|
|
if (control == NULL || opt == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2005-07-21 16:39:23 +00:00
|
|
|
ip6_initpktopts(opt);
|
2003-10-24 18:26:30 +00:00
|
|
|
if (stickyopt) {
|
2005-07-21 16:39:23 +00:00
|
|
|
int error;
|
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/*
|
|
|
|
* If stickyopt is provided, make a local copy of the options
|
|
|
|
* for this particular packet, then override them by ancillary
|
|
|
|
* objects.
|
2005-07-21 16:39:23 +00:00
|
|
|
* XXX: copypktopts() does not copy the cached route to a next
|
|
|
|
* hop (if any). This is not very good in terms of efficiency,
|
|
|
|
* but we can allow this since this option should be rarely
|
|
|
|
* used.
|
2003-10-24 18:26:30 +00:00
|
|
|
*/
|
2005-07-21 16:39:23 +00:00
|
|
|
if ((error = copypktopts(opt, stickyopt, M_NOWAIT)) != 0)
|
|
|
|
return (error);
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: Currently, we assume all the optional information is stored
|
|
|
|
* in a single mbuf.
|
|
|
|
*/
|
|
|
|
if (control->m_next)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2008-10-15 19:24:18 +00:00
|
|
|
for (; control->m_len > 0; control->m_data += CMSG_ALIGN(cm->cmsg_len),
|
2003-10-08 18:26:08 +00:00
|
|
|
control->m_len -= CMSG_ALIGN(cm->cmsg_len)) {
|
2003-10-24 18:26:30 +00:00
|
|
|
int error;
|
|
|
|
|
|
|
|
if (control->m_len < CMSG_LEN(0))
|
|
|
|
return (EINVAL);
|
|
|
|
|
1999-11-22 02:45:11 +00:00
|
|
|
cm = mtod(control, struct cmsghdr *);
|
|
|
|
if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
1999-11-22 02:45:11 +00:00
|
|
|
if (cm->cmsg_level != IPPROTO_IPV6)
|
|
|
|
continue;
|
|
|
|
|
2005-07-21 15:06:32 +00:00
|
|
|
error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm),
|
2008-01-24 08:25:59 +00:00
|
|
|
cm->cmsg_len - CMSG_LEN(0), opt, cred, 0, 1, uproto);
|
2003-10-24 18:26:30 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set a particular packet option, as a sticky option or an ancillary data
|
|
|
|
* item. "len" can be 0 only when it's a sticky option.
|
|
|
|
* We have 4 cases of combination of "sticky" and "cmsg":
|
|
|
|
* "sticky=0, cmsg=0": impossible
|
2005-07-20 08:59:45 +00:00
|
|
|
* "sticky=0, cmsg=1": RFC2292 or RFC3542 ancillary data
|
|
|
|
* "sticky=1, cmsg=0": RFC3542 socket option
|
2003-10-24 18:26:30 +00:00
|
|
|
* "sticky=1, cmsg=1": RFC2292 socket option
|
|
|
|
*/
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
|
2008-01-24 08:25:59 +00:00
|
|
|
struct ucred *cred, int sticky, int cmsg, int uproto)
|
2003-10-24 18:26:30 +00:00
|
|
|
{
|
|
|
|
int minmtupolicy, preftemp;
|
2008-01-24 08:25:59 +00:00
|
|
|
int error;
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
if (!sticky && !cmsg) {
|
|
|
|
#ifdef DIAGNOSTIC
|
2005-07-21 15:06:32 +00:00
|
|
|
printf("ip6_setpktopt: impossible case\n");
|
2003-10-24 18:26:30 +00:00
|
|
|
#endif
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IPV6_2292xxx is for backward compatibility to RFC2292, and should
|
2005-07-20 08:59:45 +00:00
|
|
|
* not be specified in the context of RFC3542. Conversely,
|
|
|
|
* RFC3542 types should not be specified in the context of RFC2292.
|
2003-10-24 18:26:30 +00:00
|
|
|
*/
|
|
|
|
if (!cmsg) {
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_2292PKTINFO:
|
|
|
|
case IPV6_2292HOPLIMIT:
|
|
|
|
case IPV6_2292NEXTHOP:
|
|
|
|
case IPV6_2292HOPOPTS:
|
|
|
|
case IPV6_2292DSTOPTS:
|
|
|
|
case IPV6_2292RTHDR:
|
|
|
|
case IPV6_2292PKTOPTIONS:
|
|
|
|
return (ENOPROTOOPT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sticky && cmsg) {
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_PKTINFO:
|
|
|
|
case IPV6_HOPLIMIT:
|
|
|
|
case IPV6_NEXTHOP:
|
|
|
|
case IPV6_HOPOPTS:
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
case IPV6_RTHDR:
|
|
|
|
case IPV6_USE_MIN_MTU:
|
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
case IPV6_TCLASS:
|
2005-07-20 08:59:45 +00:00
|
|
|
case IPV6_PREFER_TEMPADDR: /* XXX: not an RFC3542 option */
|
2003-10-24 18:26:30 +00:00
|
|
|
return (ENOPROTOOPT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_2292PKTINFO:
|
|
|
|
case IPV6_PKTINFO:
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = NULL;
|
|
|
|
struct in6_pktinfo *pktinfo;
|
|
|
|
|
|
|
|
if (len != sizeof(struct in6_pktinfo))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
pktinfo = (struct in6_pktinfo *)buf;
|
|
|
|
|
2001-06-11 12:39:29 +00:00
|
|
|
/*
|
2003-10-24 18:26:30 +00:00
|
|
|
* An application can clear any sticky IPV6_PKTINFO option by
|
|
|
|
* doing a "regular" setsockopt with ipi6_addr being
|
|
|
|
* in6addr_any and ipi6_ifindex being zero.
|
|
|
|
* [RFC 3542, Section 6]
|
2001-06-11 12:39:29 +00:00
|
|
|
*/
|
2003-10-24 18:26:30 +00:00
|
|
|
if (optname == IPV6_PKTINFO && opt->ip6po_pktinfo &&
|
|
|
|
pktinfo->ipi6_ifindex == 0 &&
|
|
|
|
IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
|
|
|
|
ip6_clearpktopts(opt, optname);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uproto == IPPROTO_TCP && optname == IPV6_PKTINFO &&
|
|
|
|
sticky && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
2014-09-10 14:32:07 +00:00
|
|
|
if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr))
|
|
|
|
return (EINVAL);
|
2003-10-24 18:26:30 +00:00
|
|
|
/* validate the interface index if specified. */
|
2013-10-15 10:12:19 +00:00
|
|
|
if (pktinfo->ipi6_ifindex > V_if_index)
|
2003-10-24 18:26:30 +00:00
|
|
|
return (ENXIO);
|
|
|
|
if (pktinfo->ipi6_ifindex) {
|
|
|
|
ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
|
|
|
|
if (ifp == NULL)
|
2003-10-06 14:02:09 +00:00
|
|
|
return (ENXIO);
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
2014-09-10 14:32:07 +00:00
|
|
|
if (ifp != NULL && (
|
|
|
|
ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED))
|
|
|
|
return (ENETDOWN);
|
|
|
|
|
|
|
|
if (ifp != NULL &&
|
|
|
|
!IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
|
|
|
|
struct in6_ifaddr *ia;
|
|
|
|
|
2015-07-03 19:01:38 +00:00
|
|
|
in6_setscope(&pktinfo->ipi6_addr, ifp, NULL);
|
2014-09-10 14:32:07 +00:00
|
|
|
ia = in6ifa_ifpwithaddr(ifp, &pktinfo->ipi6_addr);
|
|
|
|
if (ia == NULL)
|
|
|
|
return (EADDRNOTAVAIL);
|
|
|
|
ifa_free(&ia->ia_ifa);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
/*
|
|
|
|
* We store the address anyway, and let in6_selectsrc()
|
|
|
|
* validate the specified address. This is because ipi6_addr
|
|
|
|
* may not have enough information about its scope zone, and
|
|
|
|
* we may need additional information (such as outgoing
|
|
|
|
* interface or the scope zone of a destination address) to
|
|
|
|
* disambiguate the scope.
|
|
|
|
* XXX: the delay of the validation may confuse the
|
|
|
|
* application when it is used as a sticky option.
|
|
|
|
*/
|
2005-07-21 16:39:23 +00:00
|
|
|
if (opt->ip6po_pktinfo == NULL) {
|
|
|
|
opt->ip6po_pktinfo = malloc(sizeof(*pktinfo),
|
|
|
|
M_IP6OPT, M_NOWAIT);
|
|
|
|
if (opt->ip6po_pktinfo == NULL)
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo));
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292HOPLIMIT:
|
|
|
|
case IPV6_HOPLIMIT:
|
|
|
|
{
|
|
|
|
int *hlimp;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/*
|
|
|
|
* RFC 3542 deprecated the usage of sticky IPV6_HOPLIMIT
|
|
|
|
* to simplify the ordering among hoplimit options.
|
|
|
|
*/
|
|
|
|
if (optname == IPV6_HOPLIMIT && sticky)
|
|
|
|
return (ENOPROTOOPT);
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
if (len != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
hlimp = (int *)buf;
|
|
|
|
if (*hlimp < -1 || *hlimp > 255)
|
|
|
|
return (EINVAL);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
opt->ip6po_hlim = *hlimp;
|
|
|
|
break;
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_TCLASS:
|
|
|
|
{
|
|
|
|
int tclass;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
if (len != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
tclass = *(int *)buf;
|
|
|
|
if (tclass < -1 || tclass > 255)
|
|
|
|
return (EINVAL);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
opt->ip6po_tclass = tclass;
|
|
|
|
break;
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_2292NEXTHOP:
|
|
|
|
case IPV6_NEXTHOP:
|
2008-01-24 08:25:59 +00:00
|
|
|
if (cred != NULL) {
|
|
|
|
error = priv_check_cred(cred,
|
|
|
|
PRIV_NETINET_SETHDROPTS, 0);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
if (len == 0) { /* just remove the option */
|
|
|
|
ip6_clearpktopts(opt, IPV6_NEXTHOP);
|
1999-11-22 02:45:11 +00:00
|
|
|
break;
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/* check if cmsg_len is large enough for sa_len */
|
|
|
|
if (len < sizeof(struct sockaddr) || len < *buf)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
switch (((struct sockaddr *)buf)->sa_family) {
|
|
|
|
case AF_INET6:
|
2001-06-11 12:39:29 +00:00
|
|
|
{
|
2003-10-24 18:26:30 +00:00
|
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)buf;
|
|
|
|
int error;
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
if (sa6->sin6_len != sizeof(struct sockaddr_in6))
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr) ||
|
|
|
|
IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) {
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
2003-10-24 18:26:30 +00:00
|
|
|
}
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
if ((error = sa6_embedscope(sa6, V_ip6_use_defzone))
|
2003-10-24 18:26:30 +00:00
|
|
|
!= 0) {
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AF_LINK: /* should eventually be supported */
|
|
|
|
default:
|
|
|
|
return (EAFNOSUPPORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* turn off the previous option, then set the new option. */
|
|
|
|
ip6_clearpktopts(opt, IPV6_NEXTHOP);
|
2005-10-21 16:23:01 +00:00
|
|
|
opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_NOWAIT);
|
|
|
|
if (opt->ip6po_nexthop == NULL)
|
|
|
|
return (ENOBUFS);
|
2005-07-21 16:39:23 +00:00
|
|
|
bcopy(buf, opt->ip6po_nexthop, *buf);
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_2292HOPOPTS:
|
|
|
|
case IPV6_HOPOPTS:
|
|
|
|
{
|
|
|
|
struct ip6_hbh *hbh;
|
|
|
|
int hbhlen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: We don't allow a non-privileged user to set ANY HbH
|
|
|
|
* options, since per-option restriction has too much
|
|
|
|
* overhead.
|
|
|
|
*/
|
2008-01-24 08:25:59 +00:00
|
|
|
if (cred != NULL) {
|
|
|
|
error = priv_check_cred(cred,
|
|
|
|
PRIV_NETINET_SETHDROPTS, 0);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
ip6_clearpktopts(opt, IPV6_HOPOPTS);
|
|
|
|
break; /* just remove the option */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* message length validation */
|
|
|
|
if (len < sizeof(struct ip6_hbh))
|
|
|
|
return (EINVAL);
|
|
|
|
hbh = (struct ip6_hbh *)buf;
|
|
|
|
hbhlen = (hbh->ip6h_len + 1) << 3;
|
|
|
|
if (len != hbhlen)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* turn off the previous option, then set the new option. */
|
|
|
|
ip6_clearpktopts(opt, IPV6_HOPOPTS);
|
2005-10-21 16:23:01 +00:00
|
|
|
opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT);
|
|
|
|
if (opt->ip6po_hbh == NULL)
|
|
|
|
return (ENOBUFS);
|
2005-07-21 16:39:23 +00:00
|
|
|
bcopy(hbh, opt->ip6po_hbh, hbhlen);
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_2292DSTOPTS:
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
{
|
|
|
|
struct ip6_dest *dest, **newdest = NULL;
|
|
|
|
int destlen;
|
|
|
|
|
2008-01-24 08:25:59 +00:00
|
|
|
if (cred != NULL) { /* XXX: see the comment for IPV6_HOPOPTS */
|
|
|
|
error = priv_check_cred(cred,
|
|
|
|
PRIV_NETINET_SETHDROPTS, 0);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
ip6_clearpktopts(opt, optname);
|
|
|
|
break; /* just remove the option */
|
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/* message length validation */
|
|
|
|
if (len < sizeof(struct ip6_dest))
|
|
|
|
return (EINVAL);
|
|
|
|
dest = (struct ip6_dest *)buf;
|
|
|
|
destlen = (dest->ip6d_len + 1) << 3;
|
|
|
|
if (len != destlen)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the position that the destination options header
|
|
|
|
* should be inserted; before or after the routing header.
|
|
|
|
*/
|
|
|
|
switch (optname) {
|
|
|
|
case IPV6_2292DSTOPTS:
|
|
|
|
/*
|
|
|
|
* The old advacned API is ambiguous on this point.
|
|
|
|
* Our approach is to determine the position based
|
|
|
|
* according to the existence of a routing header.
|
|
|
|
* Note, however, that this depends on the order of the
|
|
|
|
* extension headers in the ancillary data; the 1st
|
|
|
|
* part of the destination options header must appear
|
|
|
|
* before the routing header in the ancillary data,
|
|
|
|
* too.
|
2005-07-20 08:59:45 +00:00
|
|
|
* RFC3542 solved the ambiguity by introducing
|
2003-10-24 18:26:30 +00:00
|
|
|
* separate ancillary data or option types.
|
1999-11-22 02:45:11 +00:00
|
|
|
*/
|
2001-06-11 12:39:29 +00:00
|
|
|
if (opt->ip6po_rthdr == NULL)
|
|
|
|
newdest = &opt->ip6po_dest1;
|
|
|
|
else
|
|
|
|
newdest = &opt->ip6po_dest2;
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
case IPV6_RTHDRDSTOPTS:
|
|
|
|
newdest = &opt->ip6po_dest1;
|
|
|
|
break;
|
|
|
|
case IPV6_DSTOPTS:
|
|
|
|
newdest = &opt->ip6po_dest2;
|
|
|
|
break;
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/* turn off the previous option, then set the new option. */
|
|
|
|
ip6_clearpktopts(opt, optname);
|
2005-10-21 16:23:01 +00:00
|
|
|
*newdest = malloc(destlen, M_IP6OPT, M_NOWAIT);
|
2006-01-14 00:09:41 +00:00
|
|
|
if (*newdest == NULL)
|
2005-10-21 16:23:01 +00:00
|
|
|
return (ENOBUFS);
|
2005-07-21 16:39:23 +00:00
|
|
|
bcopy(dest, *newdest, destlen);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IPV6_2292RTHDR:
|
|
|
|
case IPV6_RTHDR:
|
|
|
|
{
|
|
|
|
struct ip6_rthdr *rth;
|
|
|
|
int rthlen;
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
ip6_clearpktopts(opt, IPV6_RTHDR);
|
|
|
|
break; /* just remove the option */
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/* message length validation */
|
|
|
|
if (len < sizeof(struct ip6_rthdr))
|
|
|
|
return (EINVAL);
|
|
|
|
rth = (struct ip6_rthdr *)buf;
|
|
|
|
rthlen = (rth->ip6r_len + 1) << 3;
|
|
|
|
if (len != rthlen)
|
|
|
|
return (EINVAL);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
switch (rth->ip6r_type) {
|
|
|
|
case IPV6_RTHDR_TYPE_0:
|
|
|
|
if (rth->ip6r_len == 0) /* must contain one addr */
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
2003-10-24 18:26:30 +00:00
|
|
|
if (rth->ip6r_len % 2) /* length must be even */
|
2003-10-06 14:02:09 +00:00
|
|
|
return (EINVAL);
|
2003-10-24 18:26:30 +00:00
|
|
|
if (rth->ip6r_len / 2 != rth->ip6r_segleft)
|
|
|
|
return (EINVAL);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL); /* not supported */
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
/* turn off the previous option */
|
|
|
|
ip6_clearpktopts(opt, IPV6_RTHDR);
|
2005-10-21 16:23:01 +00:00
|
|
|
opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT);
|
|
|
|
if (opt->ip6po_rthdr == NULL)
|
|
|
|
return (ENOBUFS);
|
2005-07-21 16:39:23 +00:00
|
|
|
bcopy(rth, opt->ip6po_rthdr, rthlen);
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_USE_MIN_MTU:
|
|
|
|
if (len != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
minmtupolicy = *(int *)buf;
|
|
|
|
if (minmtupolicy != IP6PO_MINMTU_MCASTONLY &&
|
|
|
|
minmtupolicy != IP6PO_MINMTU_DISABLE &&
|
|
|
|
minmtupolicy != IP6PO_MINMTU_ALL) {
|
|
|
|
return (EINVAL);
|
2001-06-11 12:39:29 +00:00
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
opt->ip6po_minmtu = minmtupolicy;
|
|
|
|
break;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-24 18:26:30 +00:00
|
|
|
case IPV6_DONTFRAG:
|
|
|
|
if (len != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (uproto == IPPROTO_TCP || *(int *)buf == 0) {
|
|
|
|
/*
|
|
|
|
* we ignore this option for TCP sockets.
|
2005-07-20 08:59:45 +00:00
|
|
|
* (RFC3542 leaves this case unspecified.)
|
2003-10-24 18:26:30 +00:00
|
|
|
*/
|
|
|
|
opt->ip6po_flags &= ~IP6PO_DONTFRAG;
|
|
|
|
} else
|
|
|
|
opt->ip6po_flags |= IP6PO_DONTFRAG;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IPV6_PREFER_TEMPADDR:
|
|
|
|
if (len != sizeof(int))
|
|
|
|
return (EINVAL);
|
|
|
|
preftemp = *(int *)buf;
|
|
|
|
if (preftemp != IP6PO_TEMPADDR_SYSTEM &&
|
|
|
|
preftemp != IP6PO_TEMPADDR_NOTPREFER &&
|
|
|
|
preftemp != IP6PO_TEMPADDR_PREFER) {
|
|
|
|
return (EINVAL);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
2003-10-24 18:26:30 +00:00
|
|
|
opt->ip6po_prefer_tempaddr = preftemp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (ENOPROTOOPT);
|
|
|
|
} /* end of switch */
|
1999-11-22 02:45:11 +00:00
|
|
|
|
2003-10-06 14:02:09 +00:00
|
|
|
return (0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine called from ip6_output() to loop back a copy of an IP6 multicast
|
|
|
|
* packet to the input queue of a specified interface. Note that this
|
|
|
|
* calls the output routine of the loopback "driver", but with an interface
|
|
|
|
* pointer that might NOT be &loif -- easier than replicating that code here.
|
|
|
|
*/
|
|
|
|
void
|
2016-03-01 00:17:14 +00:00
|
|
|
ip6_mloopback(struct ifnet *ifp, struct mbuf *m)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
2000-07-04 16:35:15 +00:00
|
|
|
struct mbuf *copym;
|
|
|
|
struct ip6_hdr *ip6;
|
1999-11-22 02:45:11 +00:00
|
|
|
|
|
|
|
copym = m_copy(m, 0, M_COPYALL);
|
2000-07-04 16:35:15 +00:00
|
|
|
if (copym == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure to deep-copy IPv6 header portion in case the data
|
|
|
|
* is in an mbuf cluster, so that we can safely override the IPv6
|
|
|
|
* header portion later.
|
|
|
|
*/
|
2014-10-12 15:49:52 +00:00
|
|
|
if (!M_WRITABLE(copym) ||
|
2000-07-04 16:35:15 +00:00
|
|
|
copym->m_len < sizeof(struct ip6_hdr)) {
|
|
|
|
copym = m_pullup(copym, sizeof(struct ip6_hdr));
|
|
|
|
if (copym == NULL)
|
|
|
|
return;
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
2001-06-11 12:39:29 +00:00
|
|
|
ip6 = mtod(copym, struct ip6_hdr *);
|
|
|
|
/*
|
|
|
|
* clear embedded scope identifiers if necessary.
|
|
|
|
* in6_clearscope will touch the addresses only when necessary.
|
|
|
|
*/
|
|
|
|
in6_clearscope(&ip6->ip6_src);
|
|
|
|
in6_clearscope(&ip6->ip6_dst);
|
2015-05-07 14:17:43 +00:00
|
|
|
if (copym->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
|
|
|
|
copym->m_pkthdr.csum_flags |= CSUM_DATA_VALID_IPV6 |
|
|
|
|
CSUM_PSEUDO_HDR;
|
|
|
|
copym->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
2015-08-08 15:58:35 +00:00
|
|
|
if_simloop(ifp, copym, AF_INET6, 0);
|
1999-11-22 02:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Chop IPv6 header off from the payload.
|
|
|
|
*/
|
|
|
|
static int
|
2007-07-05 16:23:49 +00:00
|
|
|
ip6_splithdr(struct mbuf *m, struct ip6_exthdrs *exthdrs)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
|
|
|
struct mbuf *mh;
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
if (m->m_len > sizeof(*ip6)) {
|
2013-03-15 12:50:29 +00:00
|
|
|
mh = m_gethdr(M_NOWAIT, MT_DATA);
|
|
|
|
if (mh == NULL) {
|
1999-11-22 02:45:11 +00:00
|
|
|
m_freem(m);
|
|
|
|
return ENOBUFS;
|
|
|
|
}
|
2013-03-15 13:48:53 +00:00
|
|
|
m_move_pkthdr(mh, m);
|
To ease changes to underlying mbuf structure and the mbuf allocator, reduce
the knowledge of mbuf layout, and in particular constants such as M_EXT,
MLEN, MHLEN, and so on, in mbuf consumers by unifying various alignment
utility functions (M_ALIGN(), MH_ALIGN(), MEXT_ALIGN() in a single
M_ALIGN() macro, implemented by a now-inlined m_align() function:
- Move m_align() from uipc_mbuf.c to mbuf.h; mark as __inline.
- Reimplement M_ALIGN(), MH_ALIGN(), and MEXT_ALIGN() using m_align().
- Update consumers around the tree to simply use M_ALIGN().
This change eliminates a number of cases where mbuf consumers must be aware
of whether or not mbufs returned by the allocator use external storage, but
also assumptions about the size of the returned mbuf. This will make it
easier to introduce changes in how we use external storage, as well as
features such as variable-size mbufs.
Differential Revision: https://reviews.freebsd.org/D1436
Reviewed by: glebius, trasz, gnn, bz
Sponsored by: EMC / Isilon Storage Division
2015-01-05 09:58:32 +00:00
|
|
|
M_ALIGN(mh, sizeof(*ip6));
|
1999-11-22 02:45:11 +00:00
|
|
|
m->m_len -= sizeof(*ip6);
|
|
|
|
m->m_data += sizeof(*ip6);
|
|
|
|
mh->m_next = m;
|
|
|
|
m = mh;
|
|
|
|
m->m_len = sizeof(*ip6);
|
|
|
|
bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
|
|
|
|
}
|
|
|
|
exthdrs->ip6e_ip6 = m;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute IPv6 extension header length.
|
|
|
|
*/
|
|
|
|
int
|
Another step assimilating IPv[46] PCB code - directly use
the inpcb names rather than the following IPv6 compat macros:
in6pcb,in6p_sp, in6p_ip6_nxt,in6p_flowinfo,in6p_vflag,
in6p_flags,in6p_socket,in6p_lport,in6p_fport,in6p_ppcb and
sotoin6pcb().
Apart from removing duplicate code in netipsec, this is a pure
whitespace, not a functional change.
Discussed with: rwatson
Reviewed by: rwatson (version before review requested changes)
MFC after: 4 weeks (set the timer and see then)
2008-12-15 21:50:54 +00:00
|
|
|
ip6_optlen(struct inpcb *in6p)
|
1999-11-22 02:45:11 +00:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!in6p->in6p_outputopts)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
#define elen(x) \
|
|
|
|
(((struct ip6_ext *)(x)) ? (((struct ip6_ext *)(x))->ip6e_len + 1) << 3 : 0)
|
|
|
|
|
|
|
|
len += elen(in6p->in6p_outputopts->ip6po_hbh);
|
2001-06-11 12:39:29 +00:00
|
|
|
if (in6p->in6p_outputopts->ip6po_rthdr)
|
|
|
|
/* dest1 is valid with rthdr only */
|
|
|
|
len += elen(in6p->in6p_outputopts->ip6po_dest1);
|
1999-11-22 02:45:11 +00:00
|
|
|
len += elen(in6p->in6p_outputopts->ip6po_rthdr);
|
|
|
|
len += elen(in6p->in6p_outputopts->ip6po_dest2);
|
|
|
|
return len;
|
|
|
|
#undef elen
|
|
|
|
}
|