ipv4: Reject routes specifying ECN bits in rtm_tos

Use the new dscp_t type to replace the fc_tos field of fib_config, to
ensure IPv4 routes aren't influenced by ECN bits when configured with
non-zero rtm_tos.

Before this patch, IPv4 routes specifying an rtm_tos with some of the
ECN bits set were accepted. However they wouldn't work (never match) as
IPv4 normally clears the ECN bits with IPTOS_RT_MASK before doing a FIB
lookup (although a few buggy code paths don't).

After this patch, IPv4 routes specifying an rtm_tos with any ECN bit
set is rejected.

Note: IPv6 routes ignore rtm_tos altogether, any rtm_tos is accepted,
but treated as if it were 0.

Signed-off-by: Guillaume Nault <gnault@redhat.com>
Acked-by: David Ahern <dsahern@kernel.org>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Guillaume Nault 2022-02-04 14:58:16 +01:00 committed by Jakub Kicinski
parent 563f8e97e0
commit f55fbb6afb
4 changed files with 93 additions and 4 deletions

View File

@ -17,6 +17,7 @@
#include <linux/rcupdate.h>
#include <net/fib_notifier.h>
#include <net/fib_rules.h>
#include <net/inet_dscp.h>
#include <net/inetpeer.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
@ -24,7 +25,7 @@
struct fib_config {
u8 fc_dst_len;
u8 fc_tos;
dscp_t fc_dscp;
u8 fc_protocol;
u8 fc_scope;
u8 fc_type;

View File

@ -32,6 +32,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <net/inet_dscp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
@ -735,8 +736,16 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
memset(cfg, 0, sizeof(*cfg));
rtm = nlmsg_data(nlh);
if (!inet_validate_dscp(rtm->rtm_tos)) {
NL_SET_ERR_MSG(extack,
"Invalid dsfield (tos): ECN bits must be 0");
err = -EINVAL;
goto errout;
}
cfg->fc_dscp = inet_dsfield_to_dscp(rtm->rtm_tos);
cfg->fc_dst_len = rtm->rtm_dst_len;
cfg->fc_tos = rtm->rtm_tos;
cfg->fc_table = rtm->rtm_table;
cfg->fc_protocol = rtm->rtm_protocol;
cfg->fc_scope = rtm->rtm_scope;

View File

@ -61,6 +61,7 @@
#include <linux/vmalloc.h>
#include <linux/notifier.h>
#include <net/net_namespace.h>
#include <net/inet_dscp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
@ -1210,9 +1211,9 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
struct fib_info *fi;
u8 plen = cfg->fc_dst_len;
u8 slen = KEYLENGTH - plen;
u8 tos = cfg->fc_tos;
u32 key;
int err;
u8 tos;
key = ntohl(cfg->fc_dst);
@ -1227,6 +1228,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
goto err;
}
tos = inet_dscp_to_dsfield(cfg->fc_dscp);
l = fib_find_node(t, &tp, key);
fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority,
tb->tb_id, false) : NULL;
@ -1703,8 +1705,8 @@ int fib_table_delete(struct net *net, struct fib_table *tb,
struct key_vector *l, *tp;
u8 plen = cfg->fc_dst_len;
u8 slen = KEYLENGTH - plen;
u8 tos = cfg->fc_tos;
u32 key;
u8 tos;
key = ntohl(cfg->fc_dst);
@ -1715,6 +1717,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb,
if (!l)
return -ESRCH;
tos = inet_dscp_to_dsfield(cfg->fc_dscp);
fa = fib_find_alias(&l->leaf, slen, tos, 0, tb->tb_id, false);
if (!fa)
return -ESRCH;

View File

@ -1447,6 +1447,81 @@ ipv4_local_rt_cache()
log_test $? 0 "Cached route removed from VRF port device"
}
ipv4_rt_dsfield()
{
echo
echo "IPv4 route with dsfield tests"
run_cmd "$IP route flush 172.16.102.0/24"
# New routes should reject dsfield options that interfere with ECN
run_cmd "$IP route add 172.16.102.0/24 dsfield 0x01 via 172.16.101.2"
log_test $? 2 "Reject route with dsfield 0x01"
run_cmd "$IP route add 172.16.102.0/24 dsfield 0x02 via 172.16.101.2"
log_test $? 2 "Reject route with dsfield 0x02"
run_cmd "$IP route add 172.16.102.0/24 dsfield 0x03 via 172.16.101.2"
log_test $? 2 "Reject route with dsfield 0x03"
# A generic route that doesn't take DSCP into account
run_cmd "$IP route add 172.16.102.0/24 via 172.16.101.2"
# A more specific route for DSCP 0x10
run_cmd "$IP route add 172.16.102.0/24 dsfield 0x10 via 172.16.103.2"
# DSCP 0x10 should match the specific route, no matter the ECN bits
$IP route get fibmatch 172.16.102.1 dsfield 0x10 | \
grep -q "via 172.16.103.2"
log_test $? 0 "IPv4 route with DSCP and ECN:Not-ECT"
$IP route get fibmatch 172.16.102.1 dsfield 0x11 | \
grep -q "via 172.16.103.2"
log_test $? 0 "IPv4 route with DSCP and ECN:ECT(1)"
$IP route get fibmatch 172.16.102.1 dsfield 0x12 | \
grep -q "via 172.16.103.2"
log_test $? 0 "IPv4 route with DSCP and ECN:ECT(0)"
$IP route get fibmatch 172.16.102.1 dsfield 0x13 | \
grep -q "via 172.16.103.2"
log_test $? 0 "IPv4 route with DSCP and ECN:CE"
# Unknown DSCP should match the generic route, no matter the ECN bits
$IP route get fibmatch 172.16.102.1 dsfield 0x14 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with unknown DSCP and ECN:Not-ECT"
$IP route get fibmatch 172.16.102.1 dsfield 0x15 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(1)"
$IP route get fibmatch 172.16.102.1 dsfield 0x16 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with unknown DSCP and ECN:ECT(0)"
$IP route get fibmatch 172.16.102.1 dsfield 0x17 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with unknown DSCP and ECN:CE"
# Null DSCP should match the generic route, no matter the ECN bits
$IP route get fibmatch 172.16.102.1 dsfield 0x00 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with no DSCP and ECN:Not-ECT"
$IP route get fibmatch 172.16.102.1 dsfield 0x01 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(1)"
$IP route get fibmatch 172.16.102.1 dsfield 0x02 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with no DSCP and ECN:ECT(0)"
$IP route get fibmatch 172.16.102.1 dsfield 0x03 | \
grep -q "via 172.16.101.2"
log_test $? 0 "IPv4 route with no DSCP and ECN:CE"
}
ipv4_route_test()
{
route_setup
@ -1454,6 +1529,7 @@ ipv4_route_test()
ipv4_rt_add
ipv4_rt_replace
ipv4_local_rt_cache
ipv4_rt_dsfield
route_cleanup
}