In IPv6 Destination options processing function check if net->ipv6.sysctl.max_dst_opts_cnt is zero up front. If is zero then drop the packet since Destination Options processing is disabled. Similarly, in IPv6 hop-by-hop options processing function check if net->ipv6.sysctl.max_hbh_opts_cnt is zero up front. If is zero then drop the packet since Hop-by-Hop Options processing is disabled. --- net/ipv6/exthdrs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index a23eb8734e15..11ff3d4df129 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -303,7 +303,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) struct net *net = dev_net(skb->dev); int extlen; - if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || + if (!net->ipv6.sysctl.max_dst_opts_cnt || + !pskb_may_pull(skb, skb_transport_offset(skb) + 8) || !pskb_may_pull(skb, (skb_transport_offset(skb) + ((skb_transport_header(skb)[1] + 1) << 3)))) { __IP6_INC_STATS(dev_net(dst_dev(dst)), idev, @@ -1040,7 +1041,8 @@ int ipv6_parse_hopopts(struct sk_buff *skb) * sizeof(struct ipv6hdr) by definition of * hop-by-hop options. */ - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || + if (!net->ipv6.sysctl.max_hbh_opts_cnt || + !pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || !pskb_may_pull(skb, (sizeof(struct ipv6hdr) + ((skb_transport_header(skb)[1] + 1) << 3)))) { fail_and_free: -- 2.43.0 Set IP6_DEFAULT_MAX_DST_OPTS_CNT to zero. This disables processing of Destinations Options extension headers by default. Processing can be enabled by setting the net.ipv6.max_dst_opts_number to a non-zero value. The rationale for this is that Destination Options pose a serious risk of Denial off Service attack. The problem is that even if the default limit is set to a small number (previously it was eight) there is still the possibility of a DoS attack. All an attacker needs to do is create and MTU size packet filled with 8 bytes Destination Options Extension Headers. Each Destination EH simply contains a single padding option with six bytes of zeroes. In a 1500 byte MTU size packet, 182 of these dummy Destination Options headers can be placed in a packet. Per RFC8200, a host must accept and process a packet with any number of Destination Options extension headers. So when the stack processes such a packet it is a lot of work and CPU cycles that provide zero benefit. The packet can be designed such that every byte after the IP header requires a conditional check and branch prediction can be rendered useless for that. This also may mean over twenty cache misses per packet. In other words, these packets filled with dummy Destination Options extension headers are the basis for what would be an effective DoS attack. Disabling Destination Options is not a major issue for the following reasons: * Linux kernel only supports one Destination Option (Home Address Option). There is no evidence this has seen any real world use * On the Internet packets with Destination Options are dropped with a high enough rate such that use of Destination Options is not feasible * It is unknown however quite possible that no one anywhere is using Destination Options for anything but experiments, class projects, or DoS. If someone is using them in their private network then it's easy enough to configure a non-zero limit for their use case --- include/net/ipv6.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 74fbf1ad8065..723a254c0b90 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -86,8 +86,11 @@ struct ip_tunnel_info; * silently discarded. */ -/* Default limits for Hop-by-Hop and Destination options */ -#define IP6_DEFAULT_MAX_DST_OPTS_CNT 8 +/* Default limits for Hop-by-Hop and Destination options. By default + * packets received with Destination Options headers are dropped to thwart + * Denial of Service attacks (see sysctl documention) + */ +#define IP6_DEFAULT_MAX_DST_OPTS_CNT 0 #define IP6_DEFAULT_MAX_HBH_OPTS_CNT 8 #define IP6_DEFAULT_MAX_DST_OPTS_LEN INT_MAX /* No limit */ #define IP6_DEFAULT_MAX_HBH_OPTS_LEN INT_MAX /* No limit */ -- 2.43.0 The Hop-by-Hop options limit was a default of 8 meaning that up to eight Hop-by-Hop options would be received in packet before the limit is exceeded and the packet is dropped. This limit is to high and makes the node susceptible to DoS attack. Note it's not just the options themselves, but a lot of padding can be used between options (.e.g. up to seven PAD1 options). It's pretty easy for an attacker to fabricate a packet with nothing but eight unknown option types and padding between the options to force over a hundred conditionals to be evaluated and at least eight cache misses per packet resulting in no productive work being done. The new limit is one. This is based on the fact that there are some hop-by-hop option in deployment like router aletrt option, however they tend to be singleton options and it's unlikely there is significant use of more than one option in a packet. From a protocol perspective, RFC9673 states: "A Source MAY, based on local configuration, allow only one Hop-by-Hop option to be included in a packet" We can infer that implies that at most one Hop-by-Hop option is sufficient. It should be noted that Hop-by-Hops are unusable in the general Internet hand packets with Hop-by-Hop Options are commonly dropped by routers. The only realistic use case for Hop-by-Hop options is limited dominas, and if a limited domain needs more than one HBH option in a packet it's easy enough to configure the sysctl to whatever limit they want. --- include/net/ipv6.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 723a254c0b90..62ed44894e96 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -91,7 +91,7 @@ struct ip_tunnel_info; * Denial of Service attacks (see sysctl documention) */ #define IP6_DEFAULT_MAX_DST_OPTS_CNT 0 -#define IP6_DEFAULT_MAX_HBH_OPTS_CNT 8 +#define IP6_DEFAULT_MAX_HBH_OPTS_CNT 1 #define IP6_DEFAULT_MAX_DST_OPTS_LEN INT_MAX /* No limit */ #define IP6_DEFAULT_MAX_HBH_OPTS_LEN INT_MAX /* No limit */ -- 2.43.0 Add a note and rationalization for setting the default maximum number of Destination options to zero. This means by default Destination Options extension headers are not processed on receive and packets with Destination Options extension headers are dropped --- Documentation/networking/ip-sysctl.rst | 38 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index bc9a01606daf..7ccfdc74dc91 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -2474,20 +2474,36 @@ mld_qrv - INTEGER Minimum: 1 (as specified by RFC6636 4.5) max_dst_opts_number - INTEGER - Maximum number of non-padding TLVs allowed in a Destination - options extension header. If this value is less than zero - then unknown options are disallowed and the number of known - TLVs allowed is the absolute value of this number. - - Default: 8 + Maximum number of non-padding TLVs allowed in a Destination + options extension header. If this value is zero then receive + Destination Options processing is disabled in which case packets + with the Destination Options extension header are dropped. If + this value is less than zero then unknown options are disallowed + and the number of known TLVs allowed is the absolute value of + this number. + + The default is zero which means the all received packets with + Destination Options extension header are dropped. The rationale is that + for the vast majority of hosts, Destination Options serve no purpose. + In the thirty years of IPv6 no broadly useful IPv6 Destination options + have been defined, they have no security or even checksum protection, + latest data shows the Destination have drop rates on the Internet + from ten percent to more than thirty percent (depending on the size of + the extension header). They also have the potential to be used as a + Denial of Service attack. + + Default: 0 max_hbh_opts_number - INTEGER Maximum number of non-padding TLVs allowed in a Hop-by-Hop - options extension header. If this value is less than zero - then unknown options are disallowed and the number of known - TLVs allowed is the absolute value of this number. - - Default: 8 + options extension header. If this value is zero then receive + Hop-by-Hop Options processing is disabled in which case packets + with the Hop-by-Hop Options extension header are dropped. + If this value is less than zero then unknown options are disallowed + and the number of known TLVs allowed is the absolute value of this + number. + + Default: 8 max_dst_opts_length - INTEGER Maximum length allowed for a Destination options extension -- 2.43.0 Changge documentation that the default limit for number of Hop-by-Hop options is one --- Documentation/networking/ip-sysctl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 7ccfdc74dc91..de078f7f6a17 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -2503,7 +2503,7 @@ max_hbh_opts_number - INTEGER and the number of known TLVs allowed is the absolute value of this number. - Default: 8 + Default: 1 max_dst_opts_length - INTEGER Maximum length allowed for a Destination options extension -- 2.43.0