Skip to content

Commit

Permalink
Merge branch 'fib-rule-selftest'
Browse files Browse the repository at this point in the history
Roopa Prabhu says:

====================
fib rule selftest

This series adds a new test to test fib rules.
ip route get is used to test fib rule matches.
This series also extends ip route get to match on
sport and dport to test recent support of sport
and dport fib rule match.

v2 - address ido's commemt to make sport dport
ip route get to work correctly for input route
get. I don't support ip route get on ip-proto match yet.
ip route get creates a udp packet and i have left
it at that. We could extend ip route get to support
a few ip proto matches in followup patches.

v3 - Support ip_proto (only tcp and udp) match in getroute.
dropped printing of new match attrs in ip route get,
because ipv6 does not print it. And ipv6 currrently shares
the dump api with ipv6 notify and its better to not add them
to the notify api. dropped it to keep the api consistent between
ipv4 and ipv6 (though uid is already printed in the ipv4 case).
If we need it, both ipv4 and ipv6 can be enhanced to provide
a separate get api. Moved skb creation for ipv4 to a separate func.

v4 - drop separate skb for netlink and fix concerns around rcu and netlink
     reply (as pointed out by DaveM). I now try to reset the skb after the route
     lookup and before the netlink send (testing shows this is ok. More eyes and
     any feedback here will be helpful)

v5 - dropped RTA_TABLE ipv4_rtm_policy update from this series and posted
     it separately for net (feedback from Eric)
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed May 23, 2018
2 parents 273de02 + 65b2b49 commit 8fcb097
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 41 deletions.
3 changes: 3 additions & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -664,4 +664,7 @@ extern int sysctl_icmp_msgs_burst;
int ip_misc_proc_init(void);
#endif

int rtm_getroute_parse_ip_proto(struct nlattr *attr, u8 *ip_proto,
struct netlink_ext_ack *extack);

#endif /* _IP_H */
3 changes: 3 additions & 0 deletions include/uapi/linux/rtnetlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ enum rtattr_type_t {
RTA_PAD,
RTA_UID,
RTA_TTL_PROPAGATE,
RTA_IP_PROTO,
RTA_SPORT,
RTA_DPORT,
__RTA_MAX
};

Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ obj-y := route.o inetpeer.o protocol.o \
udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \
inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \
metrics.o
metrics.o netlink.o

obj-$(CONFIG_BPFILTER) += bpfilter/

Expand Down
3 changes: 3 additions & 0 deletions net/ipv4/fib_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,9 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = {
[RTA_ENCAP] = { .type = NLA_NESTED },
[RTA_UID] = { .type = NLA_U32 },
[RTA_MARK] = { .type = NLA_U32 },
[RTA_IP_PROTO] = { .type = NLA_U8 },
[RTA_SPORT] = { .type = NLA_U16 },
[RTA_DPORT] = { .type = NLA_U16 },
};

static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
Expand Down
23 changes: 23 additions & 0 deletions net/ipv4/netlink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/types.h>
#include <net/net_namespace.h>
#include <net/netlink.h>
#include <net/ip.h>

int rtm_getroute_parse_ip_proto(struct nlattr *attr, u8 *ip_proto,
struct netlink_ext_ack *extack)
{
*ip_proto = nla_get_u8(attr);

switch (*ip_proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_ICMP:
return 0;
default:
NL_SET_ERR_MSG(extack, "Unsupported ip proto");
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL_GPL(rtm_getroute_parse_ip_proto);
146 changes: 107 additions & 39 deletions net/ipv4/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -2574,11 +2574,10 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4,
EXPORT_SYMBOL_GPL(ip_route_output_flow);

/* called with rcu_read_lock held */
static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
struct flowi4 *fl4, struct sk_buff *skb, u32 portid,
u32 seq)
static int rt_fill_info(struct net *net, __be32 dst, __be32 src,
struct rtable *rt, u32 table_id, struct flowi4 *fl4,
struct sk_buff *skb, u32 portid, u32 seq)
{
struct rtable *rt = skb_rtable(skb);
struct rtmsg *r;
struct nlmsghdr *nlh;
unsigned long expires = 0;
Expand Down Expand Up @@ -2674,7 +2673,7 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
}
} else
#endif
if (nla_put_u32(skb, RTA_IIF, skb->dev->ifindex))
if (nla_put_u32(skb, RTA_IIF, fl4->flowi4_iif))
goto nla_put_failure;
}

Expand All @@ -2689,43 +2688,93 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
return -EMSGSIZE;
}

static struct sk_buff *inet_rtm_getroute_build_skb(__be32 src, __be32 dst,
u8 ip_proto, __be16 sport,
__be16 dport)
{
struct sk_buff *skb;
struct iphdr *iph;

skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb)
return NULL;

/* Reserve room for dummy headers, this skb can pass
* through good chunk of routing engine.
*/
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->protocol = htons(ETH_P_IP);
iph = skb_put(skb, sizeof(struct iphdr));
iph->protocol = ip_proto;
iph->saddr = src;
iph->daddr = dst;
iph->version = 0x4;
iph->frag_off = 0;
iph->ihl = 0x5;
skb_set_transport_header(skb, skb->len);

switch (iph->protocol) {
case IPPROTO_UDP: {
struct udphdr *udph;

udph = skb_put_zero(skb, sizeof(struct udphdr));
udph->source = sport;
udph->dest = dport;
udph->len = sizeof(struct udphdr);
udph->check = 0;
break;
}
case IPPROTO_TCP: {
struct tcphdr *tcph;

tcph = skb_put_zero(skb, sizeof(struct tcphdr));
tcph->source = sport;
tcph->dest = dport;
tcph->doff = sizeof(struct tcphdr) / 4;
tcph->rst = 1;
tcph->check = ~tcp_v4_check(sizeof(struct tcphdr),
src, dst, 0);
break;
}
case IPPROTO_ICMP: {
struct icmphdr *icmph;

icmph = skb_put_zero(skb, sizeof(struct icmphdr));
icmph->type = ICMP_ECHO;
icmph->code = 0;
}
}

return skb;
}

static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(in_skb->sk);
struct rtmsg *rtm;
struct nlattr *tb[RTA_MAX+1];
u32 table_id = RT_TABLE_MAIN;
__be16 sport = 0, dport = 0;
struct fib_result res = {};
u8 ip_proto = IPPROTO_UDP;
struct rtable *rt = NULL;
struct sk_buff *skb;
struct rtmsg *rtm;
struct flowi4 fl4;
__be32 dst = 0;
__be32 src = 0;
kuid_t uid;
u32 iif;
int err;
int mark;
struct sk_buff *skb;
u32 table_id = RT_TABLE_MAIN;
kuid_t uid;

err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy,
extack);
if (err < 0)
goto errout;
return err;

rtm = nlmsg_data(nlh);

skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
err = -ENOBUFS;
goto errout;
}

/* Reserve room for dummy headers, this skb can pass
through good chunk of routing engine.
*/
skb_reset_mac_header(skb);
skb_reset_network_header(skb);

src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0;
dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0;
iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0;
Expand All @@ -2735,14 +2784,22 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
else
uid = (iif ? INVALID_UID : current_uid());

/* Bugfix: need to give ip_route_input enough of an IP header to
* not gag.
*/
ip_hdr(skb)->protocol = IPPROTO_UDP;
ip_hdr(skb)->saddr = src;
ip_hdr(skb)->daddr = dst;
if (tb[RTA_IP_PROTO]) {
err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
&ip_proto, extack);
if (err)
return err;
}

if (tb[RTA_SPORT])
sport = nla_get_be16(tb[RTA_SPORT]);

skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
if (tb[RTA_DPORT])
dport = nla_get_be16(tb[RTA_DPORT]);

skb = inet_rtm_getroute_build_skb(src, dst, ip_proto, sport, dport);
if (!skb)
return -ENOBUFS;

memset(&fl4, 0, sizeof(fl4));
fl4.daddr = dst;
Expand All @@ -2751,6 +2808,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0;
fl4.flowi4_mark = mark;
fl4.flowi4_uid = uid;
if (sport)
fl4.fl4_sport = sport;
if (dport)
fl4.fl4_dport = dport;
fl4.flowi4_proto = ip_proto;

rcu_read_lock();

Expand All @@ -2760,10 +2822,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
dev = dev_get_by_index_rcu(net, iif);
if (!dev) {
err = -ENODEV;
goto errout_free;
goto errout_rcu;
}

skb->protocol = htons(ETH_P_IP);
fl4.flowi4_iif = iif; /* for rt_fill_info */
skb->dev = dev;
skb->mark = mark;
err = ip_route_input_rcu(skb, dst, src, rtm->rtm_tos,
Expand All @@ -2783,42 +2845,48 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
}

if (err)
goto errout_free;
goto errout_rcu;

if (rtm->rtm_flags & RTM_F_NOTIFY)
rt->rt_flags |= RTCF_NOTIFY;

if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
table_id = res.table ? res.table->tb_id : 0;

/* reset skb for netlink reply msg */
skb_trim(skb, 0);
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
skb_reset_mac_header(skb);

if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
if (!res.fi) {
err = fib_props[res.type].error;
if (!err)
err = -EHOSTUNREACH;
goto errout_free;
goto errout_rcu;
}
err = fib_dump_info(skb, NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, RTM_NEWROUTE, table_id,
rt->rt_type, res.prefix, res.prefixlen,
fl4.flowi4_tos, res.fi, 0);
} else {
err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
err = rt_fill_info(net, dst, src, rt, table_id, &fl4, skb,
NETLINK_CB(in_skb).portid, nlh->nlmsg_seq);
}
if (err < 0)
goto errout_free;
goto errout_rcu;

rcu_read_unlock();

err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
errout:
return err;

errout_free:
return err;
errout_rcu:
rcu_read_unlock();
kfree_skb(skb);
goto errout;
goto errout_free;
}

void ip_rt_multicast_event(struct in_device *in_dev)
Expand Down
17 changes: 17 additions & 0 deletions net/ipv6/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include <net/lwtunnel.h>
#include <net/ip_tunnels.h>
#include <net/l3mdev.h>
#include <net/ip.h>
#include <trace/events/fib6.h>

#include <linux/uaccess.h>
Expand Down Expand Up @@ -4083,6 +4084,9 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_UID] = { .type = NLA_U32 },
[RTA_MARK] = { .type = NLA_U32 },
[RTA_TABLE] = { .type = NLA_U32 },
[RTA_IP_PROTO] = { .type = NLA_U8 },
[RTA_SPORT] = { .type = NLA_U16 },
[RTA_DPORT] = { .type = NLA_U16 },
};

static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
Expand Down Expand Up @@ -4795,6 +4799,19 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
else
fl6.flowi6_uid = iif ? INVALID_UID : current_uid();

if (tb[RTA_SPORT])
fl6.fl6_sport = nla_get_be16(tb[RTA_SPORT]);

if (tb[RTA_DPORT])
fl6.fl6_dport = nla_get_be16(tb[RTA_DPORT]);

if (tb[RTA_IP_PROTO]) {
err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO],
&fl6.flowi6_proto, extack);
if (err)
goto errout;
}

if (iif) {
struct net_device *dev;
int flags = 0;
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/selftests/net/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CFLAGS += -I../../../../usr/include/

TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh
TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh udpgso.sh
TEST_PROGS += udpgso_bench.sh
TEST_PROGS += udpgso_bench.sh fib_rule_tests.sh
TEST_PROGS_EXTENDED := in_netns.sh
TEST_GEN_FILES = socket
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
Expand Down
Loading

0 comments on commit 8fcb097

Please sign in to comment.