2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Extension Header handling for IPv6
|
|
|
|
* Linux INET6 implementation
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Pedro Roque <roque@di.fc.ul.pt>
|
|
|
|
* Andi Kleen <ak@muc.de>
|
|
|
|
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Changes:
|
2007-02-09 22:24:49 +08:00
|
|
|
* yoshfuji : ensure not to overrun while parsing
|
2005-04-17 06:20:36 +08:00
|
|
|
* tlv options.
|
|
|
|
* Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
|
|
|
|
* YOSHIFUJI Hideaki @USAGI Register inbound extension header
|
|
|
|
* handlers as inet6_protocol{}.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <linux/net.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/in6.h>
|
|
|
|
#include <linux/icmpv6.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-07-15 23:47:34 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-11-14 13:34:06 +08:00
|
|
|
#include <net/dst.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/sock.h>
|
|
|
|
#include <net/snmp.h>
|
|
|
|
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include <net/protocol.h>
|
|
|
|
#include <net/transp_v6.h>
|
|
|
|
#include <net/rawv6.h>
|
|
|
|
#include <net/ndisc.h>
|
|
|
|
#include <net/ip6_route.h>
|
|
|
|
#include <net/addrconf.h>
|
2016-06-28 03:06:17 +08:00
|
|
|
#include <net/calipso.h>
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:16:22 +08:00
|
|
|
#include <net/xfrm.h>
|
|
|
|
#endif
|
2016-11-08 21:57:39 +08:00
|
|
|
#include <linux/seg6.h>
|
|
|
|
#include <net/seg6.h>
|
2016-11-08 21:59:19 +08:00
|
|
|
#ifdef CONFIG_IPV6_SEG6_HMAC
|
|
|
|
#include <net/seg6_hmac.h>
|
|
|
|
#endif
|
2020-03-28 06:00:20 +08:00
|
|
|
#include <net/rpl.h>
|
ipv6: ioam: Data plane support for Pre-allocated Trace
Implement support for processing the IOAM Pre-allocated Trace with IPv6,
see [1] and [2]. Introduce a new IPv6 Hop-by-Hop TLV option, see IANA [3].
A new per-interface sysctl is introduced. The value is a boolean to accept (=1)
or ignore (=0, by default) IPv6 IOAM options on ingress for an interface:
- net.ipv6.conf.XXX.ioam6_enabled
Two other sysctls are introduced to define IOAM IDs, represented by an integer.
They are respectively per-namespace and per-interface:
- net.ipv6.ioam6_id
- net.ipv6.conf.XXX.ioam6_id
The value of the first one represents the IOAM ID of the node itself (u32; max
and default value = U32_MAX>>8, due to hop limit concatenation) while the other
represents the IOAM ID of an interface (u16; max and default value = U16_MAX).
Each "ioam6_id" sysctl has a "_wide" equivalent:
- net.ipv6.ioam6_id_wide
- net.ipv6.conf.XXX.ioam6_id_wide
The value of the first one represents the wide IOAM ID of the node itself (u64;
max and default value = U64_MAX>>8, due to hop limit concatenation) while the
other represents the wide IOAM ID of an interface (u32; max and default value
= U32_MAX).
The use of short and wide equivalents is not exclusive, a deployment could
choose to leverage both. For example, net.ipv6.conf.XXX.ioam6_id (short format)
could be an identifier for a physical interface, whereas
net.ipv6.conf.XXX.ioam6_id_wide (wide format) could be an identifier for a
logical sub-interface. Documentation about new sysctls is provided at the end
of this patchset.
Two relativistic hash tables are used: one for IOAM namespaces, the other for
IOAM schemas. A namespace can only have a single active schema and a schema
can only be attached to a single namespace (1:1 relationship).
[1] https://tools.ietf.org/html/draft-ietf-ippm-ioam-ipv6-options
[2] https://tools.ietf.org/html/draft-ietf-ippm-ioam-data
[3] https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#ipv6-parameters-2
Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-07-21 03:42:57 +08:00
|
|
|
#include <linux/ioam6.h>
|
|
|
|
#include <net/ioam6.h>
|
|
|
|
#include <net/dst_metadata.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-10-28 02:12:58 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*********************
|
|
|
|
Generic functions
|
|
|
|
*********************/
|
|
|
|
|
|
|
|
/* An unknown option is detected, decide what to do */
|
|
|
|
|
2017-10-31 05:16:00 +08:00
|
|
|
static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
|
|
|
|
bool disallow_unknowns)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-10-31 05:16:00 +08:00
|
|
|
if (disallow_unknowns) {
|
|
|
|
/* If unknown TLVs are disallowed by configuration
|
|
|
|
* then always silently drop packet. Note this also
|
|
|
|
* means no ICMP parameter problem is sent which
|
|
|
|
* could be a good property to mitigate a reflection DOS
|
|
|
|
* attack.
|
|
|
|
*/
|
|
|
|
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0: /* ignore */
|
2012-05-19 02:57:34 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
case 1: /* drop packet */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* Send ICMP if not a multicast address and drop packet */
|
|
|
|
/* Actually, it is redundant check. icmp_send
|
|
|
|
will recheck in any case.
|
|
|
|
*/
|
2007-04-26 08:54:47 +08:00
|
|
|
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2020-03-13 06:50:22 +08:00
|
|
|
fallthrough;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 2: /* send ICMP PARM PROB regardless and drop packet */
|
|
|
|
icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2007-04-21 08:09:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-31 05:16:00 +08:00
|
|
|
drop:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-08-03 23:31:05 +08:00
|
|
|
static bool ipv6_hop_ra(struct sk_buff *skb, int optoff);
|
|
|
|
static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff);
|
|
|
|
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff);
|
|
|
|
static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff);
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
|
|
|
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff);
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Parse tlv encoded option header (hop-by-hop or destination) */
|
|
|
|
|
2021-08-03 23:31:05 +08:00
|
|
|
static bool ip6_parse_tlv(bool hopbyhop,
|
2017-10-31 05:16:00 +08:00
|
|
|
struct sk_buff *skb,
|
|
|
|
int max_count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-10-31 05:16:00 +08:00
|
|
|
int len = (skb_transport_header(skb)[1] + 1) << 3;
|
2007-04-11 11:50:43 +08:00
|
|
|
const unsigned char *nh = skb_network_header(skb);
|
2007-03-17 04:26:39 +08:00
|
|
|
int off = skb_network_header_len(skb);
|
2017-10-31 05:16:00 +08:00
|
|
|
bool disallow_unknowns = false;
|
|
|
|
int tlv_count = 0;
|
2012-05-20 09:59:33 +08:00
|
|
|
int padlen = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-31 05:16:00 +08:00
|
|
|
if (unlikely(max_count < 0)) {
|
|
|
|
disallow_unknowns = true;
|
|
|
|
max_count = -max_count;
|
|
|
|
}
|
|
|
|
|
2007-04-26 08:55:53 +08:00
|
|
|
if (skb_transport_offset(skb) + len > skb_headlen(skb))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto bad;
|
|
|
|
|
|
|
|
off += 2;
|
|
|
|
len -= 2;
|
|
|
|
|
|
|
|
while (len > 0) {
|
2021-06-24 18:07:20 +08:00
|
|
|
int optlen, i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-06-24 18:07:20 +08:00
|
|
|
if (nh[off] == IPV6_TLV_PAD1) {
|
2012-05-20 09:59:33 +08:00
|
|
|
padlen++;
|
|
|
|
if (padlen > 7)
|
|
|
|
goto bad;
|
2021-06-24 18:07:20 +08:00
|
|
|
off++;
|
|
|
|
len--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (len < 2)
|
|
|
|
goto bad;
|
|
|
|
optlen = nh[off + 1] + 2;
|
|
|
|
if (optlen > len)
|
|
|
|
goto bad;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-06-24 18:07:20 +08:00
|
|
|
if (nh[off] == IPV6_TLV_PADN) {
|
2012-04-13 05:36:17 +08:00
|
|
|
/* RFC 2460 states that the purpose of PadN is
|
|
|
|
* to align the containing header to multiples
|
|
|
|
* of 8. 7 is therefore the highest valid value.
|
|
|
|
* See also RFC 4942, Section 2.1.9.5.
|
|
|
|
*/
|
2012-05-20 09:59:33 +08:00
|
|
|
padlen += optlen;
|
|
|
|
if (padlen > 7)
|
2012-04-13 05:36:17 +08:00
|
|
|
goto bad;
|
|
|
|
/* RFC 4942 recommends receiving hosts to
|
|
|
|
* actively check PadN payload to contain
|
|
|
|
* only zeroes.
|
|
|
|
*/
|
|
|
|
for (i = 2; i < optlen; i++) {
|
|
|
|
if (nh[off + i] != 0)
|
|
|
|
goto bad;
|
|
|
|
}
|
2021-06-24 18:07:20 +08:00
|
|
|
} else {
|
2017-10-31 05:16:00 +08:00
|
|
|
tlv_count++;
|
|
|
|
if (tlv_count > max_count)
|
|
|
|
goto bad;
|
|
|
|
|
2021-08-03 23:31:05 +08:00
|
|
|
if (hopbyhop) {
|
|
|
|
switch (nh[off]) {
|
|
|
|
case IPV6_TLV_ROUTERALERT:
|
|
|
|
if (!ipv6_hop_ra(skb, off))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case IPV6_TLV_IOAM:
|
|
|
|
if (!ipv6_hop_ioam(skb, off))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case IPV6_TLV_JUMBO:
|
|
|
|
if (!ipv6_hop_jumbo(skb, off))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case IPV6_TLV_CALIPSO:
|
|
|
|
if (!ipv6_hop_calipso(skb, off))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!ip6_tlvopt_unknown(skb, off,
|
|
|
|
disallow_unknowns))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (nh[off]) {
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
|
|
|
case IPV6_TLV_HAO:
|
|
|
|
if (!ipv6_dest_hao(skb, off))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
if (!ip6_tlvopt_unknown(skb, off,
|
|
|
|
disallow_unknowns))
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-05-20 09:59:33 +08:00
|
|
|
padlen = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
off += optlen;
|
|
|
|
len -= optlen;
|
|
|
|
}
|
2012-05-20 09:59:33 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (len == 0)
|
2012-05-19 02:57:34 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
bad:
|
|
|
|
kfree_skb(skb);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************
|
|
|
|
Destination options header.
|
|
|
|
*****************************/
|
|
|
|
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2012-05-19 02:57:34 +08:00
|
|
|
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
|
2006-08-24 10:24:48 +08:00
|
|
|
{
|
|
|
|
struct ipv6_destopt_hao *hao;
|
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
2007-04-26 08:54:47 +08:00
|
|
|
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
|
2006-08-24 10:24:48 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (opt->dsthao) {
|
2014-11-12 02:59:17 +08:00
|
|
|
net_dbg_ratelimited("hao duplicated\n");
|
2006-08-24 10:24:48 +08:00
|
|
|
goto discard;
|
|
|
|
}
|
|
|
|
opt->dsthao = opt->dst1;
|
|
|
|
opt->dst1 = 0;
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
|
2006-08-24 10:24:48 +08:00
|
|
|
|
|
|
|
if (hao->length != 16) {
|
2014-11-12 02:59:17 +08:00
|
|
|
net_dbg_ratelimited("hao invalid option length = %d\n",
|
|
|
|
hao->length);
|
2006-08-24 10:24:48 +08:00
|
|
|
goto discard;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
|
2014-11-12 02:59:17 +08:00
|
|
|
net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
|
|
|
|
&hao->addr);
|
2006-08-24 10:24:48 +08:00
|
|
|
goto discard;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
|
|
|
|
(xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
|
|
|
|
if (unlikely(ret < 0))
|
|
|
|
goto discard;
|
|
|
|
|
|
|
|
if (skb_cloned(skb)) {
|
2007-10-15 16:29:10 +08:00
|
|
|
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
|
2006-08-24 10:24:48 +08:00
|
|
|
goto discard;
|
|
|
|
|
|
|
|
/* update all variable using below by copied skbuff */
|
2007-10-15 16:29:10 +08:00
|
|
|
hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
|
2007-04-11 11:50:43 +08:00
|
|
|
optoff);
|
2007-10-15 16:29:10 +08:00
|
|
|
ipv6h = ipv6_hdr(skb);
|
2006-08-24 10:24:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
|
2017-10-27 12:10:35 +08:00
|
|
|
swap(ipv6h->saddr, hao->addr);
|
2006-08-24 10:24:48 +08:00
|
|
|
|
2016-12-25 18:38:40 +08:00
|
|
|
if (skb->tstamp == 0)
|
2006-08-24 10:24:48 +08:00
|
|
|
__net_timestamp(skb);
|
|
|
|
|
2012-05-19 02:57:34 +08:00
|
|
|
return true;
|
2006-08-24 10:24:48 +08:00
|
|
|
|
|
|
|
discard:
|
|
|
|
kfree_skb(skb);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2006-08-24 10:24:48 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-10-16 03:50:28 +08:00
|
|
|
static int ipv6_destopt_rcv(struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2018-04-17 01:42:16 +08:00
|
|
|
struct inet6_dev *idev = __in6_dev_get(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:24:48 +08:00
|
|
|
__u16 dstbuf;
|
|
|
|
#endif
|
2011-07-28 12:00:35 +08:00
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
2017-10-31 05:16:00 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
int extlen;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:55:53 +08:00
|
|
|
if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
|
|
|
|
!pskb_may_pull(skb, (skb_transport_offset(skb) +
|
2007-04-26 09:04:18 +08:00
|
|
|
((skb_transport_header(skb)[1] + 1) << 3)))) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(dev_net(dst->dev), idev,
|
2016-04-28 07:44:40 +08:00
|
|
|
IPSTATS_MIB_INHDRERRORS);
|
2017-10-31 05:16:00 +08:00
|
|
|
fail_and_free:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-10-31 05:16:00 +08:00
|
|
|
extlen = (skb_transport_header(skb)[1] + 1) << 3;
|
|
|
|
if (extlen > net->ipv6.sysctl.max_dst_opts_len)
|
|
|
|
goto fail_and_free;
|
|
|
|
|
2007-03-17 04:26:39 +08:00
|
|
|
opt->lastopt = opt->dst1 = skb_network_header_len(skb);
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:24:48 +08:00
|
|
|
dstbuf = opt->dst1;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-08-03 23:31:05 +08:00
|
|
|
if (ip6_parse_tlv(false, skb, net->ipv6.sysctl.max_dst_opts_cnt)) {
|
2017-10-31 05:16:00 +08:00
|
|
|
skb->transport_header += extlen;
|
2006-09-01 06:18:49 +08:00
|
|
|
opt = IP6CB(skb);
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:24:48 +08:00
|
|
|
opt->nhoff = dstbuf;
|
|
|
|
#else
|
2006-01-07 15:02:34 +08:00
|
|
|
opt->nhoff = opt->dst1;
|
2006-08-24 10:24:48 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:57:39 +08:00
|
|
|
static void seg6_update_csum(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ipv6_sr_hdr *hdr;
|
|
|
|
struct in6_addr *addr;
|
|
|
|
__be32 from, to;
|
|
|
|
|
|
|
|
/* srh is at transport offset and seg_left is already decremented
|
|
|
|
* but daddr is not yet updated with next segment
|
|
|
|
*/
|
|
|
|
|
|
|
|
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
|
|
|
|
addr = hdr->segments + hdr->segments_left;
|
|
|
|
|
|
|
|
hdr->segments_left++;
|
|
|
|
from = *(__be32 *)hdr;
|
|
|
|
|
|
|
|
hdr->segments_left--;
|
|
|
|
to = *(__be32 *)hdr;
|
|
|
|
|
|
|
|
/* update skb csum with diff resulting from seg_left decrement */
|
|
|
|
|
|
|
|
update_csum_diff4(skb, from, to);
|
|
|
|
|
|
|
|
/* compute csum diff between current and next segment and update */
|
|
|
|
|
|
|
|
update_csum_diff16(skb, (__be32 *)(&ipv6_hdr(skb)->daddr),
|
|
|
|
(__be32 *)addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipv6_srh_rcv(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
struct ipv6_sr_hdr *hdr;
|
|
|
|
struct inet6_dev *idev;
|
|
|
|
struct in6_addr *addr;
|
|
|
|
int accept_seg6;
|
|
|
|
|
|
|
|
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
|
|
|
|
|
|
|
|
idev = __in6_dev_get(skb->dev);
|
|
|
|
|
|
|
|
accept_seg6 = net->ipv6.devconf_all->seg6_enabled;
|
|
|
|
if (accept_seg6 > idev->cnf.seg6_enabled)
|
|
|
|
accept_seg6 = idev->cnf.seg6_enabled;
|
|
|
|
|
|
|
|
if (!accept_seg6) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:59:19 +08:00
|
|
|
#ifdef CONFIG_IPV6_SEG6_HMAC
|
|
|
|
if (!seg6_hmac_validate_skb(skb)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-11-08 21:57:39 +08:00
|
|
|
looped_back:
|
2017-02-02 18:29:38 +08:00
|
|
|
if (hdr->segments_left == 0) {
|
2021-03-11 23:53:18 +08:00
|
|
|
if (hdr->nexthdr == NEXTHDR_IPV6 || hdr->nexthdr == NEXTHDR_IPV4) {
|
2016-11-08 21:57:39 +08:00
|
|
|
int offset = (hdr->hdrlen + 1) << 3;
|
|
|
|
|
|
|
|
skb_postpull_rcsum(skb, skb_network_header(skb),
|
|
|
|
skb_network_header_len(skb));
|
|
|
|
|
|
|
|
if (!pskb_pull(skb, offset)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
skb_postpull_rcsum(skb, skb_transport_header(skb),
|
|
|
|
offset);
|
|
|
|
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
skb->encapsulation = 0;
|
2021-03-11 23:53:18 +08:00
|
|
|
if (hdr->nexthdr == NEXTHDR_IPV4)
|
|
|
|
skb->protocol = htons(ETH_P_IP);
|
2016-11-08 21:57:39 +08:00
|
|
|
__skb_tunnel_rx(skb, skb->dev, net);
|
|
|
|
|
|
|
|
netif_rx(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->srcrt = skb_network_header_len(skb);
|
|
|
|
opt->lastopt = opt->srcrt;
|
|
|
|
skb->transport_header += (hdr->hdrlen + 1) << 3;
|
|
|
|
opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hdr->segments_left >= (hdr->hdrlen >> 1)) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2016-11-08 21:57:39 +08:00
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
|
|
((&hdr->segments_left) -
|
|
|
|
skb_network_header(skb)));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb_cloned(skb)) {
|
|
|
|
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
|
|
|
|
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
|
|
IPSTATS_MIB_OUTDISCARDS);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
|
|
|
|
|
|
|
|
hdr->segments_left--;
|
|
|
|
addr = hdr->segments + hdr->segments_left;
|
|
|
|
|
|
|
|
skb_push(skb, sizeof(struct ipv6hdr));
|
|
|
|
|
|
|
|
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
|
|
|
seg6_update_csum(skb);
|
|
|
|
|
|
|
|
ipv6_hdr(skb)->daddr = *addr;
|
|
|
|
|
|
|
|
skb_dst_drop(skb);
|
|
|
|
|
|
|
|
ip6_route_input(skb);
|
|
|
|
|
|
|
|
if (skb_dst(skb)->error) {
|
|
|
|
dst_input(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
|
|
|
|
if (ipv6_hdr(skb)->hop_limit <= 1) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2016-11-08 21:57:39 +08:00
|
|
|
icmpv6_send(skb, ICMPV6_TIME_EXCEED,
|
|
|
|
ICMPV6_EXC_HOPLIMIT, 0);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ipv6_hdr(skb)->hop_limit--;
|
|
|
|
|
2017-02-02 18:29:38 +08:00
|
|
|
skb_pull(skb, sizeof(struct ipv6hdr));
|
|
|
|
goto looped_back;
|
2016-11-08 21:57:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dst_input(skb);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-03-28 06:00:20 +08:00
|
|
|
static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;
|
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
struct inet6_dev *idev;
|
|
|
|
struct ipv6hdr *oldhdr;
|
|
|
|
struct in6_addr addr;
|
|
|
|
unsigned char *buf;
|
|
|
|
int accept_rpl_seg;
|
|
|
|
int i, err;
|
|
|
|
u64 n = 0;
|
|
|
|
u32 r;
|
|
|
|
|
|
|
|
idev = __in6_dev_get(skb->dev);
|
|
|
|
|
|
|
|
accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled;
|
|
|
|
if (accept_rpl_seg > idev->cnf.rpl_seg_enabled)
|
|
|
|
accept_rpl_seg = idev->cnf.rpl_seg_enabled;
|
|
|
|
|
|
|
|
if (!accept_rpl_seg) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
looped_back:
|
|
|
|
hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb);
|
|
|
|
|
|
|
|
if (hdr->segments_left == 0) {
|
|
|
|
if (hdr->nexthdr == NEXTHDR_IPV6) {
|
|
|
|
int offset = (hdr->hdrlen + 1) << 3;
|
|
|
|
|
|
|
|
skb_postpull_rcsum(skb, skb_network_header(skb),
|
|
|
|
skb_network_header_len(skb));
|
|
|
|
|
|
|
|
if (!pskb_pull(skb, offset)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
skb_postpull_rcsum(skb, skb_transport_header(skb),
|
|
|
|
offset);
|
|
|
|
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
skb->encapsulation = 0;
|
|
|
|
|
|
|
|
__skb_tunnel_rx(skb, skb->dev, net);
|
|
|
|
|
|
|
|
netif_rx(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->srcrt = skb_network_header_len(skb);
|
|
|
|
opt->lastopt = opt->srcrt;
|
|
|
|
skb->transport_header += (hdr->hdrlen + 1) << 3;
|
|
|
|
opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, sizeof(*hdr))) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = (hdr->hdrlen << 3) - hdr->pad - (16 - hdr->cmpre);
|
|
|
|
r = do_div(n, (16 - hdr->cmpri));
|
|
|
|
/* checks if calculation was without remainder and n fits into
|
|
|
|
* unsigned char which is segments_left field. Should not be
|
|
|
|
* higher than that.
|
|
|
|
*/
|
|
|
|
if (r || (n + 1) > 255) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hdr->segments_left > n + 1) {
|
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
|
|
((&hdr->segments_left) -
|
|
|
|
skb_network_header(skb)));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri,
|
|
|
|
hdr->cmpre))) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr->segments_left--;
|
|
|
|
i = n - hdr->segments_left;
|
|
|
|
|
2020-06-23 07:07:41 +08:00
|
|
|
buf = kcalloc(struct_size(hdr, segments.addr, n + 2), 2, GFP_ATOMIC);
|
2020-03-28 06:00:20 +08:00
|
|
|
if (unlikely(!buf)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ohdr = (struct ipv6_rpl_sr_hdr *)buf;
|
|
|
|
ipv6_rpl_srh_decompress(ohdr, hdr, &ipv6_hdr(skb)->daddr, n);
|
|
|
|
chdr = (struct ipv6_rpl_sr_hdr *)(buf + ((ohdr->hdrlen + 1) << 3));
|
|
|
|
|
|
|
|
if ((ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST) ||
|
|
|
|
(ipv6_addr_type(&ohdr->rpl_segaddr[i]) & IPV6_ADDR_MULTICAST)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
kfree(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ipv6_chk_rpl_srh_loop(net, ohdr->rpl_segaddr, n + 1);
|
|
|
|
if (err) {
|
|
|
|
icmpv6_send(skb, ICMPV6_PARAMPROB, 0, 0);
|
|
|
|
kfree_skb(skb);
|
|
|
|
kfree(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = ipv6_hdr(skb)->daddr;
|
|
|
|
ipv6_hdr(skb)->daddr = ohdr->rpl_segaddr[i];
|
|
|
|
ohdr->rpl_segaddr[i] = addr;
|
|
|
|
|
|
|
|
ipv6_rpl_srh_compress(chdr, ohdr, &ipv6_hdr(skb)->daddr, n);
|
|
|
|
|
|
|
|
oldhdr = ipv6_hdr(skb);
|
|
|
|
|
|
|
|
skb_pull(skb, ((hdr->hdrlen + 1) << 3));
|
|
|
|
skb_postpull_rcsum(skb, oldhdr,
|
|
|
|
sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3));
|
ipv6: rpl: Fix Route of Death.
[ Upstream commit a2f4c143d76b1a47c91ef9bc46907116b111da0b ]
A remote DoS vulnerability of RPL Source Routing is assigned CVE-2023-2156.
The Source Routing Header (SRH) has the following format:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len | Routing Type | Segments Left |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CmprI | CmprE | Pad | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. .
. Addresses[1..n] .
. .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The originator of an SRH places the first hop's IPv6 address in the IPv6
header's IPv6 Destination Address and the second hop's IPv6 address as
the first address in Addresses[1..n].
The CmprI and CmprE fields indicate the number of prefix octets that are
shared with the IPv6 Destination Address. When CmprI or CmprE is not 0,
Addresses[1..n] are compressed as follows:
1..n-1 : (16 - CmprI) bytes
n : (16 - CmprE) bytes
Segments Left indicates the number of route segments remaining. When the
value is not zero, the SRH is forwarded to the next hop. Its address
is extracted from Addresses[n - Segment Left + 1] and swapped with IPv6
Destination Address.
When Segment Left is greater than or equal to 2, the size of SRH is not
changed because Addresses[1..n-1] are decompressed and recompressed with
CmprI.
OTOH, when Segment Left changes from 1 to 0, the new SRH could have a
different size because Addresses[1..n-1] are decompressed with CmprI and
recompressed with CmprE.
Let's say CmprI is 15 and CmprE is 0. When we receive SRH with Segment
Left >= 2, Addresses[1..n-1] have 1 byte for each, and Addresses[n] has
16 bytes. When Segment Left is 1, Addresses[1..n-1] is decompressed to
16 bytes and not recompressed. Finally, the new SRH will need more room
in the header, and the size is (16 - 1) * (n - 1) bytes.
Here the max value of n is 255 as Segment Left is u8, so in the worst case,
we have to allocate 3825 bytes in the skb headroom. However, now we only
allocate a small fixed buffer that is IPV6_RPL_SRH_WORST_SWAP_SIZE (16 + 7
bytes). If the decompressed size overflows the room, skb_push() hits BUG()
below [0].
Instead of allocating the fixed buffer for every packet, let's allocate
enough headroom only when we receive SRH with Segment Left 1.
[0]:
skbuff: skb_under_panic: text:ffffffff81c9f6e2 len:576 put:576 head:ffff8880070b5180 data:ffff8880070b4fb0 tail:0x70 end:0x140 dev:lo
kernel BUG at net/core/skbuff.c:200!
invalid opcode: 0000 [#1] PREEMPT SMP PTI
CPU: 0 PID: 154 Comm: python3 Not tainted 6.4.0-rc4-00190-gc308e9ec0047 #7
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014
RIP: 0010:skb_panic (net/core/skbuff.c:200)
Code: 4f 70 50 8b 87 bc 00 00 00 50 8b 87 b8 00 00 00 50 ff b7 c8 00 00 00 4c 8b 8f c0 00 00 00 48 c7 c7 80 6e 77 82 e8 ad 8b 60 ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90
RSP: 0018:ffffc90000003da0 EFLAGS: 00000246
RAX: 0000000000000085 RBX: ffff8880058a6600 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffff88807dc1c540 RDI: ffff88807dc1c540
RBP: ffffc90000003e48 R08: ffffffff82b392c8 R09: 00000000ffffdfff
R10: ffffffff82a592e0 R11: ffffffff82b092e0 R12: ffff888005b1c800
R13: ffff8880070b51b8 R14: ffff888005b1ca18 R15: ffff8880070b5190
FS: 00007f4539f0b740(0000) GS:ffff88807dc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055670baf3000 CR3: 0000000005b0e000 CR4: 00000000007506f0
PKRU: 55555554
Call Trace:
<IRQ>
skb_push (net/core/skbuff.c:210)
ipv6_rthdr_rcv (./include/linux/skbuff.h:2880 net/ipv6/exthdrs.c:634 net/ipv6/exthdrs.c:718)
ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:437 (discriminator 5))
ip6_input_finish (./include/linux/rcupdate.h:805 net/ipv6/ip6_input.c:483)
__netif_receive_skb_one_core (net/core/dev.c:5494)
process_backlog (./include/linux/rcupdate.h:805 net/core/dev.c:5934)
__napi_poll (net/core/dev.c:6496)
net_rx_action (net/core/dev.c:6565 net/core/dev.c:6696)
__do_softirq (./arch/x86/include/asm/jump_label.h:27 ./include/linux/jump_label.h:207 ./include/trace/events/irq.h:142 kernel/softirq.c:572)
do_softirq (kernel/softirq.c:472 kernel/softirq.c:459)
</IRQ>
<TASK>
__local_bh_enable_ip (kernel/softirq.c:396)
__dev_queue_xmit (net/core/dev.c:4272)
ip6_finish_output2 (./include/net/neighbour.h:544 net/ipv6/ip6_output.c:134)
rawv6_sendmsg (./include/net/dst.h:458 ./include/linux/netfilter.h:303 net/ipv6/raw.c:656 net/ipv6/raw.c:914)
sock_sendmsg (net/socket.c:724 net/socket.c:747)
__sys_sendto (net/socket.c:2144)
__x64_sys_sendto (net/socket.c:2156 net/socket.c:2152 net/socket.c:2152)
do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120)
RIP: 0033:0x7f453a138aea
Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89
RSP: 002b:00007ffcc212a1c8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
RAX: ffffffffffffffda RBX: 00007ffcc212a288 RCX: 00007f453a138aea
RDX: 0000000000000060 RSI: 00007f4539084c20 RDI: 0000000000000003
RBP: 00007f4538308e80 R08: 00007ffcc212a300 R09: 000000000000001c
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: ffffffffc4653600 R14: 0000000000000001 R15: 00007f4539712d1b
</TASK>
Modules linked in:
Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr")
Reported-by: Max VA
Closes: https://www.interruptlabs.co.uk/articles/linux-ipv6-route-of-death
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://lore.kernel.org/r/20230605180617.67284-1-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2023-06-06 02:06:17 +08:00
|
|
|
if (unlikely(!hdr->segments_left)) {
|
|
|
|
if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
|
|
|
|
GFP_ATOMIC)) {
|
|
|
|
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS);
|
|
|
|
kfree_skb(skb);
|
|
|
|
kfree(buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldhdr = ipv6_hdr(skb);
|
|
|
|
}
|
2020-03-28 06:00:20 +08:00
|
|
|
skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr));
|
|
|
|
skb_reset_network_header(skb);
|
|
|
|
skb_mac_header_rebuild(skb);
|
|
|
|
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
|
|
|
|
|
|
|
memmove(ipv6_hdr(skb), oldhdr, sizeof(struct ipv6hdr));
|
|
|
|
memcpy(skb_transport_header(skb), chdr, (chdr->hdrlen + 1) << 3);
|
|
|
|
|
|
|
|
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
|
|
|
|
skb_postpush_rcsum(skb, ipv6_hdr(skb),
|
|
|
|
sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3));
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
|
|
|
skb_dst_drop(skb);
|
|
|
|
|
|
|
|
ip6_route_input(skb);
|
|
|
|
|
|
|
|
if (skb_dst(skb)->error) {
|
|
|
|
dst_input(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
|
|
|
|
if (ipv6_hdr(skb)->hop_limit <= 1) {
|
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
|
|
|
icmpv6_send(skb, ICMPV6_TIME_EXCEED,
|
|
|
|
ICMPV6_EXC_HOPLIMIT, 0);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ipv6_hdr(skb)->hop_limit--;
|
|
|
|
|
|
|
|
skb_pull(skb, sizeof(struct ipv6hdr));
|
|
|
|
goto looped_back;
|
|
|
|
}
|
|
|
|
|
|
|
|
dst_input(skb);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/********************************
|
|
|
|
Routing header.
|
|
|
|
********************************/
|
|
|
|
|
2010-06-14 12:39:27 +08:00
|
|
|
/* called with rcu_read_lock() */
|
2007-10-16 03:50:28 +08:00
|
|
|
static int ipv6_rthdr_rcv(struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2018-04-17 01:42:16 +08:00
|
|
|
struct inet6_dev *idev = __in6_dev_get(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
2006-08-24 10:16:22 +08:00
|
|
|
struct in6_addr *addr = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct in6_addr daddr;
|
|
|
|
int n, i;
|
|
|
|
struct ipv6_rt_hdr *hdr;
|
|
|
|
struct rt0_hdr *rthdr;
|
2008-10-09 02:09:27 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
int accept_source_route = net->ipv6.devconf_all->accept_source_route;
|
2007-04-25 05:58:30 +08:00
|
|
|
|
2010-06-14 12:39:27 +08:00
|
|
|
idev = __in6_dev_get(skb->dev);
|
|
|
|
if (idev && accept_source_route > idev->cnf.accept_source_route)
|
|
|
|
accept_source_route = idev->cnf.accept_source_route;
|
2007-04-25 05:58:30 +08:00
|
|
|
|
2007-04-26 08:55:53 +08:00
|
|
|
if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
|
|
|
|
!pskb_may_pull(skb, (skb_transport_offset(skb) +
|
2007-04-26 09:04:18 +08:00
|
|
|
((skb_transport_header(skb)[1] + 1) << 3)))) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-04-26 09:04:18 +08:00
|
|
|
hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->pkt_type != PACKET_HOST) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-03-28 06:00:20 +08:00
|
|
|
switch (hdr->type) {
|
|
|
|
case IPV6_SRCRT_TYPE_4:
|
|
|
|
/* segment routing */
|
2016-11-08 21:57:39 +08:00
|
|
|
return ipv6_srh_rcv(skb);
|
2020-03-28 06:00:20 +08:00
|
|
|
case IPV6_SRCRT_TYPE_3:
|
|
|
|
/* rpl segment routing */
|
|
|
|
return ipv6_rpl_srh_rcv(skb);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-11-08 21:57:39 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
looped_back:
|
|
|
|
if (hdr->segments_left == 0) {
|
2006-08-24 10:16:22 +08:00
|
|
|
switch (hdr->type) {
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:16:22 +08:00
|
|
|
case IPV6_SRCRT_TYPE_2:
|
|
|
|
/* Silently discard type 2 header unless it was
|
|
|
|
* processed by own
|
|
|
|
*/
|
|
|
|
if (!addr) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev,
|
2016-04-28 07:44:40 +08:00
|
|
|
IPSTATS_MIB_INADDRERRORS);
|
2006-08-24 10:16:22 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-03-17 04:26:39 +08:00
|
|
|
opt->lastopt = opt->srcrt = skb_network_header_len(skb);
|
2007-04-11 12:21:55 +08:00
|
|
|
skb->transport_header += (hdr->hdrlen + 1) << 3;
|
2005-04-17 06:20:36 +08:00
|
|
|
opt->dst0 = opt->dst1;
|
|
|
|
opt->dst1 = 0;
|
2007-04-11 11:50:43 +08:00
|
|
|
opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-08-24 10:16:22 +08:00
|
|
|
switch (hdr->type) {
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:16:22 +08:00
|
|
|
case IPV6_SRCRT_TYPE_2:
|
2007-07-11 13:47:58 +08:00
|
|
|
if (accept_source_route < 0)
|
|
|
|
goto unknown_rh;
|
2006-08-24 10:16:22 +08:00
|
|
|
/* Silently discard invalid RTH type 2 */
|
|
|
|
if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2006-08-24 10:16:22 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
2007-07-11 13:47:58 +08:00
|
|
|
default:
|
|
|
|
goto unknown_rh;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the routing header forwarding algorithm from
|
|
|
|
* RFC 2460, page 16.
|
|
|
|
*/
|
|
|
|
|
|
|
|
n = hdr->hdrlen >> 1;
|
|
|
|
|
|
|
|
if (hdr->segments_left > n) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2007-04-11 11:50:43 +08:00
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
|
|
((&hdr->segments_left) -
|
|
|
|
skb_network_header(skb)));
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We are about to mangle packet header. Be careful!
|
|
|
|
Do not damage packets queued somewhere.
|
|
|
|
*/
|
|
|
|
if (skb_cloned(skb)) {
|
|
|
|
/* the copy is a forwarded packet */
|
2007-10-15 16:29:10 +08:00
|
|
|
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
|
2016-04-28 07:44:40 +08:00
|
|
|
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
|
|
|
|
IPSTATS_MIB_OUTDISCARDS);
|
2006-11-04 19:11:37 +08:00
|
|
|
kfree_skb(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2007-10-15 16:29:10 +08:00
|
|
|
hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-08-30 07:44:56 +08:00
|
|
|
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
|
|
|
|
i = n - --hdr->segments_left;
|
|
|
|
|
|
|
|
rthdr = (struct rt0_hdr *) hdr;
|
|
|
|
addr = rthdr->addr;
|
|
|
|
addr += i - 1;
|
|
|
|
|
2006-08-24 10:16:22 +08:00
|
|
|
switch (hdr->type) {
|
2012-10-30 00:23:10 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6_MIP6)
|
2006-08-24 10:16:22 +08:00
|
|
|
case IPV6_SRCRT_TYPE_2:
|
|
|
|
if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
|
2007-04-26 08:54:47 +08:00
|
|
|
(xfrm_address_t *)&ipv6_hdr(skb)->saddr,
|
2006-08-24 10:16:22 +08:00
|
|
|
IPPROTO_ROUTING) < 0) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
|
2006-08-24 10:16:22 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-02 13:19:30 +08:00
|
|
|
if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
|
2006-08-24 10:16:22 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ipv6_addr_is_multicast(addr)) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-11-21 11:39:03 +08:00
|
|
|
daddr = *addr;
|
|
|
|
*addr = ipv6_hdr(skb)->daddr;
|
|
|
|
ipv6_hdr(skb)->daddr = daddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-06-02 13:19:30 +08:00
|
|
|
skb_dst_drop(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
ip6_route_input(skb);
|
2009-06-02 13:19:30 +08:00
|
|
|
if (skb_dst(skb)->error) {
|
2007-04-11 11:50:43 +08:00
|
|
|
skb_push(skb, skb->data - skb_network_header(skb));
|
2005-04-17 06:20:36 +08:00
|
|
|
dst_input(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-06-02 13:19:30 +08:00
|
|
|
if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
|
2007-04-26 08:54:47 +08:00
|
|
|
if (ipv6_hdr(skb)->hop_limit <= 1) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
|
2010-02-18 16:25:24 +08:00
|
|
|
0);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-04-26 08:54:47 +08:00
|
|
|
ipv6_hdr(skb)->hop_limit--;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto looped_back;
|
|
|
|
}
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
skb_push(skb, skb->data - skb_network_header(skb));
|
2005-04-17 06:20:36 +08:00
|
|
|
dst_input(skb);
|
|
|
|
return -1;
|
2007-07-11 13:47:58 +08:00
|
|
|
|
|
|
|
unknown_rh:
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2007-07-11 13:47:58 +08:00
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
|
|
(&hdr->type) - skb_network_header(skb));
|
|
|
|
return -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-09-14 20:22:28 +08:00
|
|
|
static const struct inet6_protocol rthdr_protocol = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.handler = ipv6_rthdr_rcv,
|
2012-11-15 16:49:19 +08:00
|
|
|
.flags = INET6_PROTO_NOPOLICY,
|
2012-11-15 16:49:13 +08:00
|
|
|
};
|
|
|
|
|
2009-09-14 20:22:28 +08:00
|
|
|
static const struct inet6_protocol destopt_protocol = {
|
2007-12-11 18:23:54 +08:00
|
|
|
.handler = ipv6_destopt_rcv,
|
2012-11-15 16:49:19 +08:00
|
|
|
.flags = INET6_PROTO_NOPOLICY,
|
2012-11-15 16:49:13 +08:00
|
|
|
};
|
|
|
|
|
2009-09-14 20:22:28 +08:00
|
|
|
static const struct inet6_protocol nodata_protocol = {
|
2007-12-11 18:23:54 +08:00
|
|
|
.handler = dst_discard,
|
|
|
|
.flags = INET6_PROTO_NOPOLICY,
|
|
|
|
};
|
|
|
|
|
|
|
|
int __init ipv6_exthdrs_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-12-11 18:23:54 +08:00
|
|
|
int ret;
|
|
|
|
|
2012-11-15 16:49:15 +08:00
|
|
|
ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
|
|
|
|
if (ret)
|
2012-11-15 16:49:22 +08:00
|
|
|
goto out;
|
2012-11-15 16:49:15 +08:00
|
|
|
|
2007-12-11 18:23:54 +08:00
|
|
|
ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
|
|
|
|
if (ret)
|
|
|
|
goto out_rthdr;
|
|
|
|
|
|
|
|
ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE);
|
|
|
|
if (ret)
|
|
|
|
goto out_destopt;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
out_destopt:
|
|
|
|
inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
|
2012-11-15 16:49:15 +08:00
|
|
|
out_rthdr:
|
|
|
|
inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
|
2007-12-11 18:23:54 +08:00
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2007-12-11 18:23:54 +08:00
|
|
|
void ipv6_exthdrs_exit(void)
|
|
|
|
{
|
|
|
|
inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
|
|
|
|
inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
|
|
|
|
inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**********************************
|
|
|
|
Hop-by-hop options.
|
|
|
|
**********************************/
|
|
|
|
|
2007-05-10 05:01:59 +08:00
|
|
|
/*
|
2009-06-02 13:19:30 +08:00
|
|
|
* Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
|
2007-05-10 05:01:59 +08:00
|
|
|
*/
|
2010-01-14 09:27:37 +08:00
|
|
|
static inline struct net *ipv6_skb_net(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Router Alert as of RFC 2711 */
|
|
|
|
|
2012-05-19 02:57:34 +08:00
|
|
|
static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-11 11:50:43 +08:00
|
|
|
const unsigned char *nh = skb_network_header(skb);
|
2006-08-24 10:19:50 +08:00
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
if (nh[optoff + 1] == 2) {
|
2013-01-13 13:02:45 +08:00
|
|
|
IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
|
|
|
|
memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
|
2012-05-19 02:57:34 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2014-11-12 02:59:17 +08:00
|
|
|
net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
|
|
|
|
nh[optoff + 1]);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
ipv6: ioam: Data plane support for Pre-allocated Trace
Implement support for processing the IOAM Pre-allocated Trace with IPv6,
see [1] and [2]. Introduce a new IPv6 Hop-by-Hop TLV option, see IANA [3].
A new per-interface sysctl is introduced. The value is a boolean to accept (=1)
or ignore (=0, by default) IPv6 IOAM options on ingress for an interface:
- net.ipv6.conf.XXX.ioam6_enabled
Two other sysctls are introduced to define IOAM IDs, represented by an integer.
They are respectively per-namespace and per-interface:
- net.ipv6.ioam6_id
- net.ipv6.conf.XXX.ioam6_id
The value of the first one represents the IOAM ID of the node itself (u32; max
and default value = U32_MAX>>8, due to hop limit concatenation) while the other
represents the IOAM ID of an interface (u16; max and default value = U16_MAX).
Each "ioam6_id" sysctl has a "_wide" equivalent:
- net.ipv6.ioam6_id_wide
- net.ipv6.conf.XXX.ioam6_id_wide
The value of the first one represents the wide IOAM ID of the node itself (u64;
max and default value = U64_MAX>>8, due to hop limit concatenation) while the
other represents the wide IOAM ID of an interface (u32; max and default value
= U32_MAX).
The use of short and wide equivalents is not exclusive, a deployment could
choose to leverage both. For example, net.ipv6.conf.XXX.ioam6_id (short format)
could be an identifier for a physical interface, whereas
net.ipv6.conf.XXX.ioam6_id_wide (wide format) could be an identifier for a
logical sub-interface. Documentation about new sysctls is provided at the end
of this patchset.
Two relativistic hash tables are used: one for IOAM namespaces, the other for
IOAM schemas. A namespace can only have a single active schema and a schema
can only be attached to a single namespace (1:1 relationship).
[1] https://tools.ietf.org/html/draft-ietf-ippm-ioam-ipv6-options
[2] https://tools.ietf.org/html/draft-ietf-ippm-ioam-data
[3] https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#ipv6-parameters-2
Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-07-21 03:42:57 +08:00
|
|
|
/* IOAM */
|
|
|
|
|
|
|
|
static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
|
|
|
|
{
|
|
|
|
struct ioam6_trace_hdr *trace;
|
|
|
|
struct ioam6_namespace *ns;
|
|
|
|
struct ioam6_hdr *hdr;
|
|
|
|
|
|
|
|
/* Bad alignment (must be 4n-aligned) */
|
|
|
|
if (optoff & 3)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
/* Ignore if IOAM is not enabled on ingress */
|
|
|
|
if (!__in6_dev_get(skb->dev)->cnf.ioam6_enabled)
|
|
|
|
goto ignore;
|
|
|
|
|
|
|
|
/* Truncated Option header */
|
|
|
|
hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff);
|
|
|
|
if (hdr->opt_len < 2)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
switch (hdr->type) {
|
|
|
|
case IOAM6_TYPE_PREALLOC:
|
|
|
|
/* Truncated Pre-allocated Trace header */
|
|
|
|
if (hdr->opt_len < 2 + sizeof(*trace))
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
/* Malformed Pre-allocated Trace header */
|
|
|
|
trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr));
|
|
|
|
if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
/* Ignore if the IOAM namespace is unknown */
|
|
|
|
ns = ioam6_namespace(ipv6_skb_net(skb), trace->namespace_id);
|
|
|
|
if (!ns)
|
|
|
|
goto ignore;
|
|
|
|
|
|
|
|
if (!skb_valid_dst(skb))
|
|
|
|
ip6_route_input(skb);
|
|
|
|
|
|
|
|
ioam6_fill_trace_data(skb, ns, trace);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ignore:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Jumbo payload */
|
|
|
|
|
2012-05-19 02:57:34 +08:00
|
|
|
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-11 11:50:43 +08:00
|
|
|
const unsigned char *nh = skb_network_header(skb);
|
2018-04-17 01:42:16 +08:00
|
|
|
struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
|
2010-01-14 09:27:37 +08:00
|
|
|
struct net *net = ipv6_skb_net(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 pkt_len;
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
|
2014-11-12 02:59:17 +08:00
|
|
|
net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
|
|
|
|
nh[optoff+1]);
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2007-04-11 11:50:43 +08:00
|
|
|
pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pkt_len <= IPV6_MAXPLEN) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-04-26 08:54:47 +08:00
|
|
|
if (ipv6_hdr(skb)->payload_len) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
|
2005-04-17 06:20:36 +08:00
|
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
|
2018-04-17 01:42:16 +08:00
|
|
|
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
}
|
2005-09-09 03:57:43 +08:00
|
|
|
|
|
|
|
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
|
|
|
|
goto drop;
|
|
|
|
|
2017-07-31 22:52:36 +08:00
|
|
|
IP6CB(skb)->flags |= IP6SKB_JUMBOGRAM;
|
2012-05-19 02:57:34 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
2012-05-19 02:57:34 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2016-06-28 03:06:17 +08:00
|
|
|
/* CALIPSO RFC 5570 */
|
|
|
|
|
|
|
|
static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
|
|
|
|
{
|
|
|
|
const unsigned char *nh = skb_network_header(skb);
|
|
|
|
|
|
|
|
if (nh[optoff + 1] < 8)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1])
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
if (!calipso_validate(skb, nh + optoff))
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-10-16 03:50:28 +08:00
|
|
|
int ipv6_parse_hopopts(struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-01-07 15:02:34 +08:00
|
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
2017-10-31 05:16:00 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
int extlen;
|
2006-01-07 15:02:34 +08:00
|
|
|
|
2006-04-19 05:46:26 +08:00
|
|
|
/*
|
2007-04-11 11:50:43 +08:00
|
|
|
* skb_network_header(skb) is equal to skb->data, and
|
2007-03-17 04:26:39 +08:00
|
|
|
* skb_network_header_len(skb) is always equal to
|
2006-04-19 05:46:26 +08:00
|
|
|
* sizeof(struct ipv6hdr) by definition of
|
|
|
|
* hop-by-hop options.
|
|
|
|
*/
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
|
2007-04-26 09:04:18 +08:00
|
|
|
!pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
|
|
|
|
((skb_transport_header(skb)[1] + 1) << 3)))) {
|
2017-10-31 05:16:00 +08:00
|
|
|
fail_and_free:
|
2006-04-19 05:46:26 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-10-31 05:16:00 +08:00
|
|
|
extlen = (skb_transport_header(skb)[1] + 1) << 3;
|
|
|
|
if (extlen > net->ipv6.sysctl.max_hbh_opts_len)
|
|
|
|
goto fail_and_free;
|
|
|
|
|
2015-07-09 05:32:12 +08:00
|
|
|
opt->flags |= IP6SKB_HOPBYHOP;
|
2021-08-03 23:31:05 +08:00
|
|
|
if (ip6_parse_tlv(true, skb, net->ipv6.sysctl.max_hbh_opts_cnt)) {
|
2017-10-31 05:16:00 +08:00
|
|
|
skb->transport_header += extlen;
|
2006-09-01 06:18:49 +08:00
|
|
|
opt = IP6CB(skb);
|
2006-01-07 15:02:34 +08:00
|
|
|
opt->nhoff = sizeof(struct ipv6hdr);
|
2006-04-19 05:48:45 +08:00
|
|
|
return 1;
|
2006-01-07 15:02:34 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creating outbound headers.
|
|
|
|
*
|
|
|
|
* "build" functions work when skb is filled from head to tail (datagram)
|
|
|
|
* "push" functions work when headers are added from tail to head (tcp)
|
|
|
|
*
|
|
|
|
* In both cases we assume, that caller reserved enough room
|
|
|
|
* for headers.
|
|
|
|
*/
|
|
|
|
|
2016-11-08 21:59:21 +08:00
|
|
|
static void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto,
|
|
|
|
struct ipv6_rt_hdr *opt,
|
|
|
|
struct in6_addr **addr_p, struct in6_addr *saddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct rt0_hdr *phdr, *ihdr;
|
|
|
|
int hops;
|
|
|
|
|
|
|
|
ihdr = (struct rt0_hdr *) opt;
|
2007-02-09 22:24:49 +08:00
|
|
|
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
phdr = skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
|
2005-04-17 06:20:36 +08:00
|
|
|
memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
|
|
|
|
|
|
|
|
hops = ihdr->rt_hdr.hdrlen >> 1;
|
|
|
|
|
|
|
|
if (hops > 1)
|
|
|
|
memcpy(phdr->addr, ihdr->addr + 1,
|
|
|
|
(hops - 1) * sizeof(struct in6_addr));
|
|
|
|
|
2011-11-21 11:39:03 +08:00
|
|
|
phdr->addr[hops - 1] = **addr_p;
|
2005-04-17 06:20:36 +08:00
|
|
|
*addr_p = ihdr->addr;
|
|
|
|
|
|
|
|
phdr->rt_hdr.nexthdr = *proto;
|
|
|
|
*proto = NEXTHDR_ROUTING;
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:59:21 +08:00
|
|
|
static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
|
|
|
|
struct ipv6_rt_hdr *opt,
|
|
|
|
struct in6_addr **addr_p, struct in6_addr *saddr)
|
|
|
|
{
|
|
|
|
struct ipv6_sr_hdr *sr_phdr, *sr_ihdr;
|
|
|
|
int plen, hops;
|
|
|
|
|
|
|
|
sr_ihdr = (struct ipv6_sr_hdr *)opt;
|
|
|
|
plen = (sr_ihdr->hdrlen + 1) << 3;
|
|
|
|
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
sr_phdr = skb_push(skb, plen);
|
2016-11-08 21:59:21 +08:00
|
|
|
memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr));
|
|
|
|
|
|
|
|
hops = sr_ihdr->first_segment + 1;
|
|
|
|
memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1,
|
|
|
|
(hops - 1) * sizeof(struct in6_addr));
|
|
|
|
|
|
|
|
sr_phdr->segments[0] = **addr_p;
|
2017-08-05 18:38:24 +08:00
|
|
|
*addr_p = &sr_ihdr->segments[sr_ihdr->segments_left];
|
2016-11-08 21:59:21 +08:00
|
|
|
|
2018-01-10 21:35:49 +08:00
|
|
|
if (sr_ihdr->hdrlen > hops * 2) {
|
|
|
|
int tlvs_offset, tlvs_length;
|
|
|
|
|
|
|
|
tlvs_offset = (1 + hops * 2) << 3;
|
|
|
|
tlvs_length = (sr_ihdr->hdrlen - hops * 2) << 3;
|
|
|
|
memcpy((char *)sr_phdr + tlvs_offset,
|
|
|
|
(char *)sr_ihdr + tlvs_offset, tlvs_length);
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:59:21 +08:00
|
|
|
#ifdef CONFIG_IPV6_SEG6_HMAC
|
|
|
|
if (sr_has_hmac(sr_phdr)) {
|
|
|
|
struct net *net = NULL;
|
|
|
|
|
|
|
|
if (skb->dev)
|
|
|
|
net = dev_net(skb->dev);
|
|
|
|
else if (skb->sk)
|
|
|
|
net = sock_net(skb->sk);
|
|
|
|
|
|
|
|
WARN_ON(!net);
|
|
|
|
|
|
|
|
if (net)
|
|
|
|
seg6_push_hmac(net, saddr, sr_phdr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sr_phdr->nexthdr = *proto;
|
|
|
|
*proto = NEXTHDR_ROUTING;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
|
|
|
|
struct ipv6_rt_hdr *opt,
|
|
|
|
struct in6_addr **addr_p, struct in6_addr *saddr)
|
|
|
|
{
|
|
|
|
switch (opt->type) {
|
|
|
|
case IPV6_SRCRT_TYPE_0:
|
2017-04-25 21:56:50 +08:00
|
|
|
case IPV6_SRCRT_STRICT:
|
|
|
|
case IPV6_SRCRT_TYPE_2:
|
2016-11-08 21:59:21 +08:00
|
|
|
ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr);
|
|
|
|
break;
|
|
|
|
case IPV6_SRCRT_TYPE_4:
|
|
|
|
ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
|
|
|
|
{
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
struct ipv6_opt_hdr *h = skb_push(skb, ipv6_optlen(opt));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
memcpy(h, opt, ipv6_optlen(opt));
|
|
|
|
h->nexthdr = *proto;
|
|
|
|
*proto = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
|
|
|
|
u8 *proto,
|
2016-11-08 21:59:20 +08:00
|
|
|
struct in6_addr **daddr, struct in6_addr *saddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
if (opt->srcrt) {
|
2016-11-08 21:59:20 +08:00
|
|
|
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr, saddr);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
/*
|
|
|
|
* IPV6_RTHDRDSTOPTS is ignored
|
|
|
|
* unless IPV6_RTHDR is set (RFC3542).
|
|
|
|
*/
|
|
|
|
if (opt->dst0opt)
|
|
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (opt->hopopt)
|
|
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
|
|
|
|
}
|
2007-02-22 21:05:40 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
|
|
|
|
{
|
|
|
|
if (opt->dst1opt)
|
|
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
|
|
|
|
}
|
2017-05-02 03:10:20 +08:00
|
|
|
EXPORT_SYMBOL(ipv6_push_frag_opts);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct ipv6_txoptions *
|
|
|
|
ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
|
|
|
|
{
|
|
|
|
struct ipv6_txoptions *opt2;
|
|
|
|
|
|
|
|
opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
|
|
|
|
if (opt2) {
|
2012-04-01 15:49:04 +08:00
|
|
|
long dif = (char *)opt2 - (char *)opt;
|
2005-04-17 06:20:36 +08:00
|
|
|
memcpy(opt2, opt, opt->tot_len);
|
|
|
|
if (opt2->hopopt)
|
2012-04-01 15:49:04 +08:00
|
|
|
*((char **)&opt2->hopopt) += dif;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (opt2->dst0opt)
|
2012-04-01 15:49:04 +08:00
|
|
|
*((char **)&opt2->dst0opt) += dif;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (opt2->dst1opt)
|
2012-04-01 15:49:04 +08:00
|
|
|
*((char **)&opt2->dst1opt) += dif;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (opt2->srcrt)
|
2012-04-01 15:49:04 +08:00
|
|
|
*((char **)&opt2->srcrt) += dif;
|
2017-07-04 14:34:54 +08:00
|
|
|
refcount_set(&opt2->refcnt, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return opt2;
|
|
|
|
}
|
2005-12-14 15:23:20 +08:00
|
|
|
EXPORT_SYMBOL_GPL(ipv6_dup_options);
|
|
|
|
|
2018-07-04 21:58:05 +08:00
|
|
|
static void ipv6_renew_option(int renewtype,
|
|
|
|
struct ipv6_opt_hdr **dest,
|
|
|
|
struct ipv6_opt_hdr *old,
|
|
|
|
struct ipv6_opt_hdr *new,
|
|
|
|
int newtype, char **p)
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
{
|
2018-07-04 21:58:05 +08:00
|
|
|
struct ipv6_opt_hdr *src;
|
|
|
|
|
|
|
|
src = (renewtype == newtype ? new : old);
|
|
|
|
if (!src)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memcpy(*p, src, ipv6_optlen(src));
|
|
|
|
*dest = (struct ipv6_opt_hdr *)*p;
|
|
|
|
*p += CMSG_ALIGN(ipv6_optlen(*dest));
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
}
|
|
|
|
|
2016-06-28 03:02:50 +08:00
|
|
|
/**
|
|
|
|
* ipv6_renew_options - replace a specific ext hdr with a new one.
|
|
|
|
*
|
|
|
|
* @sk: sock from which to allocate memory
|
|
|
|
* @opt: original options
|
|
|
|
* @newtype: option type to replace in @opt
|
|
|
|
* @newopt: new option of type @newtype to replace (user-mem)
|
|
|
|
*
|
|
|
|
* Returns a new set of options which is a copy of @opt with the
|
|
|
|
* option type @newtype replaced with @newopt.
|
|
|
|
*
|
|
|
|
* @opt may be NULL, in which case a new set of options is returned
|
|
|
|
* containing just @newopt.
|
|
|
|
*
|
|
|
|
* @newopt may be NULL, in which case the specified option type is
|
|
|
|
* not copied into the new set of options.
|
|
|
|
*
|
|
|
|
* The new set of options is allocated from the socket option memory
|
|
|
|
* buffer of @sk.
|
|
|
|
*/
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
struct ipv6_txoptions *
|
|
|
|
ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
|
2018-07-04 21:58:05 +08:00
|
|
|
int newtype, struct ipv6_opt_hdr *newopt)
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
{
|
|
|
|
int tot_len = 0;
|
|
|
|
char *p;
|
|
|
|
struct ipv6_txoptions *opt2;
|
|
|
|
|
2006-09-01 05:52:17 +08:00
|
|
|
if (opt) {
|
|
|
|
if (newtype != IPV6_HOPOPTS && opt->hopopt)
|
|
|
|
tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
|
|
|
|
if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
|
|
|
|
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
|
|
|
|
if (newtype != IPV6_RTHDR && opt->srcrt)
|
|
|
|
tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
|
|
|
|
if (newtype != IPV6_DSTOPTS && opt->dst1opt)
|
|
|
|
tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
|
|
|
|
}
|
|
|
|
|
2018-07-04 21:58:05 +08:00
|
|
|
if (newopt)
|
|
|
|
tot_len += CMSG_ALIGN(ipv6_optlen(newopt));
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
if (!tot_len)
|
|
|
|
return NULL;
|
|
|
|
|
2005-11-20 11:18:17 +08:00
|
|
|
tot_len += sizeof(*opt2);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
|
|
|
|
if (!opt2)
|
|
|
|
return ERR_PTR(-ENOBUFS);
|
|
|
|
|
|
|
|
memset(opt2, 0, tot_len);
|
2017-07-04 14:34:54 +08:00
|
|
|
refcount_set(&opt2->refcnt, 1);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
opt2->tot_len = tot_len;
|
|
|
|
p = (char *)(opt2 + 1);
|
|
|
|
|
2018-07-04 21:58:05 +08:00
|
|
|
ipv6_renew_option(IPV6_HOPOPTS, &opt2->hopopt,
|
|
|
|
(opt ? opt->hopopt : NULL),
|
|
|
|
newopt, newtype, &p);
|
|
|
|
ipv6_renew_option(IPV6_RTHDRDSTOPTS, &opt2->dst0opt,
|
|
|
|
(opt ? opt->dst0opt : NULL),
|
|
|
|
newopt, newtype, &p);
|
|
|
|
ipv6_renew_option(IPV6_RTHDR,
|
|
|
|
(struct ipv6_opt_hdr **)&opt2->srcrt,
|
|
|
|
(opt ? (struct ipv6_opt_hdr *)opt->srcrt : NULL),
|
|
|
|
newopt, newtype, &p);
|
|
|
|
ipv6_renew_option(IPV6_DSTOPTS, &opt2->dst1opt,
|
|
|
|
(opt ? opt->dst1opt : NULL),
|
|
|
|
newopt, newtype, &p);
|
[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542).
Support several new socket options / ancillary data:
IPV6_RECVPKTINFO, IPV6_PKTINFO,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS,
IPV6_RECVDSTOPTS, IPV6_DSTOPTS, IPV6_RTHDRDSTOPTS,
IPV6_RECVRTHDR, IPV6_RTHDR,
IPV6_RECVHOPOPTS, IPV6_HOPOPTS
Old semantics are preserved as IPV6_2292xxxx so that
we can maintain backward compatibility.
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2005-09-08 08:59:17 +08:00
|
|
|
|
|
|
|
opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
|
|
|
|
(opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
|
|
|
|
(opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
|
|
|
|
opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
|
|
|
|
|
|
|
|
return opt2;
|
2016-06-28 03:02:50 +08:00
|
|
|
}
|
|
|
|
|
2005-11-20 11:23:18 +08:00
|
|
|
struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
|
|
|
|
struct ipv6_txoptions *opt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* ignore the dest before srcrt unless srcrt is being included.
|
|
|
|
* --yoshfuji
|
|
|
|
*/
|
|
|
|
if (opt && opt->dst0opt && !opt->srcrt) {
|
|
|
|
if (opt_space != opt) {
|
|
|
|
memcpy(opt_space, opt, sizeof(*opt_space));
|
|
|
|
opt = opt_space;
|
|
|
|
}
|
|
|
|
opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
|
|
|
|
opt->dst0opt = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return opt;
|
|
|
|
}
|
2012-04-30 05:48:53 +08:00
|
|
|
EXPORT_SYMBOL_GPL(ipv6_fixup_options);
|
2005-11-20 11:23:18 +08:00
|
|
|
|
2010-06-02 05:35:01 +08:00
|
|
|
/**
|
|
|
|
* fl6_update_dst - update flowi destination address with info given
|
|
|
|
* by srcrt option, if any.
|
|
|
|
*
|
2011-03-13 05:22:43 +08:00
|
|
|
* @fl6: flowi6 for which daddr is to be updated
|
2010-06-02 05:35:01 +08:00
|
|
|
* @opt: struct ipv6_txoptions in which to look for srcrt opt
|
2011-03-13 05:22:43 +08:00
|
|
|
* @orig: copy of original daddr address if modified
|
2010-06-02 05:35:01 +08:00
|
|
|
*
|
|
|
|
* Returns NULL if no txoptions or no srcrt, otherwise returns orig
|
2011-03-13 05:22:43 +08:00
|
|
|
* and initial value of fl6->daddr set in orig
|
2010-06-02 05:35:01 +08:00
|
|
|
*/
|
2011-03-13 05:22:43 +08:00
|
|
|
struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
|
2010-06-02 05:35:01 +08:00
|
|
|
const struct ipv6_txoptions *opt,
|
|
|
|
struct in6_addr *orig)
|
|
|
|
{
|
|
|
|
if (!opt || !opt->srcrt)
|
|
|
|
return NULL;
|
|
|
|
|
2011-11-21 11:39:03 +08:00
|
|
|
*orig = fl6->daddr;
|
2016-11-08 21:59:21 +08:00
|
|
|
|
|
|
|
switch (opt->srcrt->type) {
|
|
|
|
case IPV6_SRCRT_TYPE_0:
|
2017-04-25 21:56:50 +08:00
|
|
|
case IPV6_SRCRT_STRICT:
|
|
|
|
case IPV6_SRCRT_TYPE_2:
|
2016-11-08 21:59:21 +08:00
|
|
|
fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
|
|
|
|
break;
|
|
|
|
case IPV6_SRCRT_TYPE_4:
|
|
|
|
{
|
|
|
|
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt;
|
|
|
|
|
2017-08-05 18:38:24 +08:00
|
|
|
fl6->daddr = srh->segments[srh->segments_left];
|
2016-11-08 21:59:21 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-02 05:35:01 +08:00
|
|
|
return orig;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(fl6_update_dst);
|