Commit 8ed1dc44 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller

ipv4: introduce hardened ip_no_pmtu_disc mode

This new ip_no_pmtu_disc mode only allowes fragmentation-needed errors
to be honored by protocols which do more stringent validation on the
ICMP's packet payload. This knob is useful for people who e.g. want to
run an unmodified DNS server in a namespace where they need to use pmtu
for TCP connections (as they are used for zone transfers or fallback
for requests) but don't want to use possibly spoofed UDP pmtu information.

Currently the whitelisted protocols are TCP, SCTP and DCCP as they check
if the returned packet is in the window or if the association is valid.

Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: John Heffner <johnwheffner@gmail.com>
Suggested-by: default avatarFlorian Weimer <fweimer@redhat.com>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0954cf9c
...@@ -26,7 +26,18 @@ ip_no_pmtu_disc - INTEGER ...@@ -26,7 +26,18 @@ ip_no_pmtu_disc - INTEGER
discarded. Outgoing frames are handled the same as in mode 1, discarded. Outgoing frames are handled the same as in mode 1,
implicitly setting IP_PMTUDISC_DONT on every created socket. implicitly setting IP_PMTUDISC_DONT on every created socket.
Possible values: 0-2 Mode 3 is a hardend pmtu discover mode. The kernel will only
accept fragmentation-needed errors if the underlying protocol
can verify them besides a plain socket lookup. Current
protocols for which pmtu events will be honored are TCP, SCTP
and DCCP as they verify e.g. the sequence number or the
association. This mode should not be enabled globally but is
only intended to secure e.g. name servers in namespaces where
TCP path mtu must still work but path MTU information of other
protocols should be discarded. If enabled globally this mode
could break other protocols.
Possible values: 0-3
Default: FALSE Default: FALSE
min_pmtu - INTEGER min_pmtu - INTEGER
......
...@@ -43,7 +43,12 @@ struct net_protocol { ...@@ -43,7 +43,12 @@ struct net_protocol {
int (*handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, u32 info); void (*err_handler)(struct sk_buff *skb, u32 info);
unsigned int no_policy:1, unsigned int no_policy:1,
netns_ok:1; netns_ok:1,
/* does the protocol do more stringent
* icmp tag validation than simple
* socket lookup?
*/
icmp_strict_tag_validation:1;
}; };
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
......
...@@ -989,6 +989,7 @@ static const struct net_protocol dccp_v4_protocol = { ...@@ -989,6 +989,7 @@ static const struct net_protocol dccp_v4_protocol = {
.err_handler = dccp_v4_err, .err_handler = dccp_v4_err,
.no_policy = 1, .no_policy = 1,
.netns_ok = 1, .netns_ok = 1,
.icmp_strict_tag_validation = 1,
}; };
static const struct proto_ops inet_dccp_ops = { static const struct proto_ops inet_dccp_ops = {
......
...@@ -1545,6 +1545,7 @@ static const struct net_protocol tcp_protocol = { ...@@ -1545,6 +1545,7 @@ static const struct net_protocol tcp_protocol = {
.err_handler = tcp_v4_err, .err_handler = tcp_v4_err,
.no_policy = 1, .no_policy = 1,
.netns_ok = 1, .netns_ok = 1,
.icmp_strict_tag_validation = 1,
}; };
static const struct net_protocol udp_protocol = { static const struct net_protocol udp_protocol = {
......
...@@ -668,6 +668,16 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info) ...@@ -668,6 +668,16 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
rcu_read_unlock(); rcu_read_unlock();
} }
static bool icmp_tag_validation(int proto)
{
bool ok;
rcu_read_lock();
ok = rcu_dereference(inet_protos[proto])->icmp_strict_tag_validation;
rcu_read_unlock();
return ok;
}
/* /*
* Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, ICMP_QUENCH, and * Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, ICMP_QUENCH, and
* ICMP_PARAMETERPROB. * ICMP_PARAMETERPROB.
...@@ -705,12 +715,22 @@ static void icmp_unreach(struct sk_buff *skb) ...@@ -705,12 +715,22 @@ static void icmp_unreach(struct sk_buff *skb)
case ICMP_PORT_UNREACH: case ICMP_PORT_UNREACH:
break; break;
case ICMP_FRAG_NEEDED: case ICMP_FRAG_NEEDED:
if (net->ipv4.sysctl_ip_no_pmtu_disc == 2) { /* for documentation of the ip_no_pmtu_disc
goto out; * values please see
} else if (net->ipv4.sysctl_ip_no_pmtu_disc) { * Documentation/networking/ip-sysctl.txt
*/
switch (net->ipv4.sysctl_ip_no_pmtu_disc) {
default:
LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),
&iph->daddr); &iph->daddr);
} else { break;
case 2:
goto out;
case 3:
if (!icmp_tag_validation(iph->protocol))
goto out;
/* fall through */
case 0:
info = ntohs(icmph->un.frag.mtu); info = ntohs(icmph->un.frag.mtu);
if (!info) if (!info)
goto out; goto out;
......
...@@ -1030,6 +1030,7 @@ static const struct net_protocol sctp_protocol = { ...@@ -1030,6 +1030,7 @@ static const struct net_protocol sctp_protocol = {
.err_handler = sctp_v4_err, .err_handler = sctp_v4_err,
.no_policy = 1, .no_policy = 1,
.netns_ok = 1, .netns_ok = 1,
.icmp_strict_tag_validation = 1,
}; };
/* IPv4 address related functions. */ /* IPv4 address related functions. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment