mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
mpls: route get support
This patch adds RTM_GETROUTE doit handler for mpls routes. Input: RTA_DST - input label RTA_NEWDST - labels in packet for multipath selection By default the getroute handler returns matched nexthop label, via and oif With RTM_F_FIB_MATCH flag, full matched route is returned. example (with patched iproute2): $ip -f mpls route show 101 nexthop as to 102/103 via inet 172.16.2.2 dev virt1-2 nexthop as to 302/303 via inet 172.16.12.2 dev virt1-12 201 nexthop as to 202/203 via inet6 2001:db8:2::2 dev virt1-2 nexthop as to 402/403 via inet6 2001:db8:12::2 dev virt1-12 $ip -f mpls route get 103 RTNETLINK answers: Network is unreachable $ip -f mpls route get 101 101 as to 102/103 via inet 172.16.2.2 dev virt1-2 $ip -f mpls route get as to 302/303 101 101 as to 302/303 via inet 172.16.12.2 dev virt1-12 $ip -f mpls route get fibmatch 103 RTNETLINK answers: Network is unreachable $ip -f mpls route get fibmatch 101 101 nexthop as to 102/103 via inet 172.16.2.2 dev virt1-2 nexthop as to 302/303 via inet 172.16.12.2 dev virt1-12 Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
7597b266c5
commit
397fc9e5ce
@ -2071,6 +2071,166 @@ errout:
|
||||
rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err);
|
||||
}
|
||||
|
||||
static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *net = sock_net(in_skb->sk);
|
||||
u32 portid = NETLINK_CB(in_skb).portid;
|
||||
struct nlattr *tb[RTA_MAX + 1];
|
||||
u32 labels[MAX_NEW_LABELS];
|
||||
struct mpls_shim_hdr *hdr;
|
||||
unsigned int hdr_size = 0;
|
||||
struct net_device *dev;
|
||||
struct mpls_route *rt;
|
||||
struct rtmsg *rtm, *r;
|
||||
struct nlmsghdr *nlh;
|
||||
struct sk_buff *skb;
|
||||
struct mpls_nh *nh;
|
||||
int err = -EINVAL;
|
||||
u32 in_label;
|
||||
u8 n_labels;
|
||||
|
||||
err = nlmsg_parse(in_nlh, sizeof(*rtm), tb, RTA_MAX,
|
||||
rtm_ipv4_policy, extack);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
rtm = nlmsg_data(in_nlh);
|
||||
|
||||
if (tb[RTA_DST]) {
|
||||
u8 label_count;
|
||||
|
||||
if (nla_get_labels(tb[RTA_DST], 1, &label_count,
|
||||
&in_label, extack))
|
||||
goto errout;
|
||||
|
||||
if (in_label < MPLS_LABEL_FIRST_UNRESERVED)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
rt = mpls_route_input_rcu(net, in_label);
|
||||
if (!rt) {
|
||||
err = -ENETUNREACH;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
|
||||
skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL);
|
||||
if (!skb) {
|
||||
err = -ENOBUFS;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
err = mpls_dump_route(skb, portid, in_nlh->nlmsg_seq,
|
||||
RTM_NEWROUTE, in_label, rt, 0);
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in lfib_nlmsg_size */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
return rtnl_unicast(skb, net, portid);
|
||||
}
|
||||
|
||||
if (tb[RTA_NEWDST]) {
|
||||
if (nla_get_labels(tb[RTA_NEWDST], MAX_NEW_LABELS, &n_labels,
|
||||
labels, extack) != 0) {
|
||||
err = -EINVAL;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
hdr_size = n_labels * sizeof(struct mpls_shim_hdr);
|
||||
}
|
||||
|
||||
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
err = -ENOBUFS;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
skb->protocol = htons(ETH_P_MPLS_UC);
|
||||
|
||||
if (hdr_size) {
|
||||
bool bos;
|
||||
int i;
|
||||
|
||||
if (skb_cow(skb, hdr_size)) {
|
||||
err = -ENOBUFS;
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
skb_reserve(skb, hdr_size);
|
||||
skb_push(skb, hdr_size);
|
||||
skb_reset_network_header(skb);
|
||||
|
||||
/* Push new labels */
|
||||
hdr = mpls_hdr(skb);
|
||||
bos = true;
|
||||
for (i = n_labels - 1; i >= 0; i--) {
|
||||
hdr[i] = mpls_entry_encode(labels[i],
|
||||
1, 0, bos);
|
||||
bos = false;
|
||||
}
|
||||
}
|
||||
|
||||
nh = mpls_select_multipath(rt, skb);
|
||||
if (!nh) {
|
||||
err = -ENETUNREACH;
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
if (hdr_size) {
|
||||
skb_pull(skb, hdr_size);
|
||||
skb_reset_network_header(skb);
|
||||
}
|
||||
|
||||
nlh = nlmsg_put(skb, portid, in_nlh->nlmsg_seq,
|
||||
RTM_NEWROUTE, sizeof(*r), 0);
|
||||
if (!nlh) {
|
||||
err = -EMSGSIZE;
|
||||
goto errout_free;
|
||||
}
|
||||
|
||||
r = nlmsg_data(nlh);
|
||||
r->rtm_family = AF_MPLS;
|
||||
r->rtm_dst_len = 20;
|
||||
r->rtm_src_len = 0;
|
||||
r->rtm_table = RT_TABLE_MAIN;
|
||||
r->rtm_type = RTN_UNICAST;
|
||||
r->rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
r->rtm_protocol = rt->rt_protocol;
|
||||
r->rtm_flags = 0;
|
||||
|
||||
if (nla_put_labels(skb, RTA_DST, 1, &in_label))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nh->nh_labels &&
|
||||
nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
|
||||
nh->nh_label))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
|
||||
nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
|
||||
nh->nh_via_alen))
|
||||
goto nla_put_failure;
|
||||
dev = rtnl_dereference(nh->nh_dev);
|
||||
if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
|
||||
goto nla_put_failure;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
|
||||
err = rtnl_unicast(skb, net, portid);
|
||||
errout:
|
||||
return err;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
err = -EMSGSIZE;
|
||||
errout_free:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int resize_platform_label_table(struct net *net, size_t limit)
|
||||
{
|
||||
size_t size = sizeof(struct mpls_route *) * limit;
|
||||
@ -2317,7 +2477,8 @@ static int __init mpls_init(void)
|
||||
|
||||
rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, NULL);
|
||||
rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, NULL);
|
||||
rtnl_register(PF_MPLS, RTM_GETROUTE, NULL, mpls_dump_routes, NULL);
|
||||
rtnl_register(PF_MPLS, RTM_GETROUTE, mpls_getroute, mpls_dump_routes,
|
||||
NULL);
|
||||
rtnl_register(PF_MPLS, RTM_GETNETCONF, mpls_netconf_get_devconf,
|
||||
mpls_netconf_dump_devconf, NULL);
|
||||
err = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user