Commit a4ac8c98 authored by David Ahern's avatar David Ahern Committed by David S. Miller

net: mpls: bump maximum number of labels

Allow users to push down more labels per MPLS route. With the previous
patches, no memory allocations are based on MAX_NEW_LABELS; the limit
is only used to keep userspace in check.

At this point MAX_NEW_LABELS is only used for mpls_route_config (copying
route data from userspace) and processing nexthops looking for the max
number of labels across the route spec.
Signed-off-by: default avatarDavid Ahern <dsa@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent df1c6316
...@@ -24,7 +24,10 @@ ...@@ -24,7 +24,10 @@
#include <net/nexthop.h> #include <net/nexthop.h>
#include "internal.h" #include "internal.h"
#define MAX_NEW_LABELS 2 /* put a reasonable limit on the number of labels
* we will accept from userspace
*/
#define MAX_NEW_LABELS 30
/* max memory we will use for mpls_route */ /* max memory we will use for mpls_route */
#define MAX_MPLS_ROUTE_MEM 4096 #define MAX_MPLS_ROUTE_MEM 4096
...@@ -698,9 +701,6 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, ...@@ -698,9 +701,6 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
return -ENOMEM; return -ENOMEM;
err = -EINVAL; err = -EINVAL;
/* Ensure only a supported number of labels are present */
if (cfg->rc_output_labels > MAX_NEW_LABELS)
goto errout;
nh->nh_labels = cfg->rc_output_labels; nh->nh_labels = cfg->rc_output_labels;
for (i = 0; i < nh->nh_labels; i++) for (i = 0; i < nh->nh_labels; i++)
...@@ -725,7 +725,7 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, ...@@ -725,7 +725,7 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
static int mpls_nh_build(struct net *net, struct mpls_route *rt, static int mpls_nh_build(struct net *net, struct mpls_route *rt,
struct mpls_nh *nh, int oif, struct nlattr *via, struct mpls_nh *nh, int oif, struct nlattr *via,
struct nlattr *newdst) struct nlattr *newdst, u8 max_labels)
{ {
int err = -ENOMEM; int err = -ENOMEM;
...@@ -733,7 +733,7 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt, ...@@ -733,7 +733,7 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt,
goto errout; goto errout;
if (newdst) { if (newdst) {
err = nla_get_labels(newdst, MAX_NEW_LABELS, err = nla_get_labels(newdst, max_labels,
&nh->nh_labels, nh->nh_label); &nh->nh_labels, nh->nh_label);
if (err) if (err)
goto errout; goto errout;
...@@ -759,21 +759,19 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt, ...@@ -759,21 +759,19 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt,
} }
static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len,
u8 cfg_via_alen, u8 *max_via_alen) u8 cfg_via_alen, u8 *max_via_alen,
u8 *max_labels)
{ {
int remaining = len; int remaining = len;
u8 nhs = 0; u8 nhs = 0;
if (!rtnh) {
*max_via_alen = cfg_via_alen;
return 1;
}
*max_via_alen = 0; *max_via_alen = 0;
*max_labels = 0;
while (rtnh_ok(rtnh, remaining)) { while (rtnh_ok(rtnh, remaining)) {
struct nlattr *nla, *attrs = rtnh_attrs(rtnh); struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
int attrlen; int attrlen;
u8 n_labels = 0;
attrlen = rtnh_attrlen(rtnh); attrlen = rtnh_attrlen(rtnh);
nla = nla_find(attrs, attrlen, RTA_VIA); nla = nla_find(attrs, attrlen, RTA_VIA);
...@@ -787,6 +785,13 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, ...@@ -787,6 +785,13 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len,
via_alen); via_alen);
} }
nla = nla_find(attrs, attrlen, RTA_NEWDST);
if (nla &&
nla_get_labels(nla, MAX_NEW_LABELS, &n_labels, NULL) != 0)
return 0;
*max_labels = max_t(u8, *max_labels, n_labels);
/* number of nexthops is tracked by a u8. /* number of nexthops is tracked by a u8.
* Check for overflow. * Check for overflow.
*/ */
...@@ -802,7 +807,7 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, ...@@ -802,7 +807,7 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len,
} }
static int mpls_nh_build_multi(struct mpls_route_config *cfg, static int mpls_nh_build_multi(struct mpls_route_config *cfg,
struct mpls_route *rt) struct mpls_route *rt, u8 max_labels)
{ {
struct rtnexthop *rtnh = cfg->rc_mp; struct rtnexthop *rtnh = cfg->rc_mp;
struct nlattr *nla_via, *nla_newdst; struct nlattr *nla_via, *nla_newdst;
...@@ -835,7 +840,8 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, ...@@ -835,7 +840,8 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
} }
err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh, err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
rtnh->rtnh_ifindex, nla_via, nla_newdst); rtnh->rtnh_ifindex, nla_via, nla_newdst,
max_labels);
if (err) if (err)
goto errout; goto errout;
...@@ -862,6 +868,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) ...@@ -862,6 +868,7 @@ static int mpls_route_add(struct mpls_route_config *cfg)
int err = -EINVAL; int err = -EINVAL;
u8 max_via_alen; u8 max_via_alen;
unsigned index; unsigned index;
u8 max_labels;
u8 nhs; u8 nhs;
index = cfg->rc_label; index = cfg->rc_label;
...@@ -900,13 +907,21 @@ static int mpls_route_add(struct mpls_route_config *cfg) ...@@ -900,13 +907,21 @@ static int mpls_route_add(struct mpls_route_config *cfg)
goto errout; goto errout;
err = -EINVAL; err = -EINVAL;
nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, if (cfg->rc_mp) {
cfg->rc_via_alen, &max_via_alen); nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
cfg->rc_via_alen, &max_via_alen,
&max_labels);
} else {
max_via_alen = cfg->rc_via_alen;
max_labels = cfg->rc_output_labels;
nhs = 1;
}
if (nhs == 0) if (nhs == 0)
goto errout; goto errout;
err = -ENOMEM; err = -ENOMEM;
rt = mpls_rt_alloc(nhs, max_via_alen, MAX_NEW_LABELS); rt = mpls_rt_alloc(nhs, max_via_alen, max_labels);
if (IS_ERR(rt)) { if (IS_ERR(rt)) {
err = PTR_ERR(rt); err = PTR_ERR(rt);
goto errout; goto errout;
...@@ -917,7 +932,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) ...@@ -917,7 +932,7 @@ static int mpls_route_add(struct mpls_route_config *cfg)
rt->rt_ttl_propagate = cfg->rc_ttl_propagate; rt->rt_ttl_propagate = cfg->rc_ttl_propagate;
if (cfg->rc_mp) if (cfg->rc_mp)
err = mpls_nh_build_multi(cfg, rt); err = mpls_nh_build_multi(cfg, rt, max_labels);
else else
err = mpls_nh_build_from_cfg(cfg, rt); err = mpls_nh_build_from_cfg(cfg, rt);
if (err) if (err)
...@@ -1531,16 +1546,18 @@ int nla_put_labels(struct sk_buff *skb, int attrtype, ...@@ -1531,16 +1546,18 @@ int nla_put_labels(struct sk_buff *skb, int attrtype,
EXPORT_SYMBOL_GPL(nla_put_labels); EXPORT_SYMBOL_GPL(nla_put_labels);
int nla_get_labels(const struct nlattr *nla, int nla_get_labels(const struct nlattr *nla,
u32 max_labels, u8 *labels, u32 label[]) u8 max_labels, u8 *labels, u32 label[])
{ {
unsigned len = nla_len(nla); unsigned len = nla_len(nla);
unsigned nla_labels;
struct mpls_shim_hdr *nla_label; struct mpls_shim_hdr *nla_label;
u8 nla_labels;
bool bos; bool bos;
int i; int i;
/* len needs to be an even multiple of 4 (the label size) */ /* len needs to be an even multiple of 4 (the label size). Number
if (len & 3) * of labels is a u8 so check for overflow.
*/
if (len & 3 || len / 4 > 255)
return -EINVAL; return -EINVAL;
/* Limit the number of new labels allowed */ /* Limit the number of new labels allowed */
...@@ -1548,6 +1565,10 @@ int nla_get_labels(const struct nlattr *nla, ...@@ -1548,6 +1565,10 @@ int nla_get_labels(const struct nlattr *nla,
if (nla_labels > max_labels) if (nla_labels > max_labels)
return -EINVAL; return -EINVAL;
/* when label == NULL, caller wants number of labels */
if (!label)
goto out;
nla_label = nla_data(nla); nla_label = nla_data(nla);
bos = true; bos = true;
for (i = nla_labels - 1; i >= 0; i--, bos = false) { for (i = nla_labels - 1; i >= 0; i--, bos = false) {
...@@ -1571,6 +1592,7 @@ int nla_get_labels(const struct nlattr *nla, ...@@ -1571,6 +1592,7 @@ int nla_get_labels(const struct nlattr *nla,
label[i] = dec.label; label[i] = dec.label;
} }
out:
*labels = nla_labels; *labels = nla_labels;
return 0; return 0;
} }
...@@ -1632,7 +1654,6 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1632,7 +1654,6 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh,
err = -EINVAL; err = -EINVAL;
rtm = nlmsg_data(nlh); rtm = nlmsg_data(nlh);
memset(cfg, 0, sizeof(*cfg));
if (rtm->rtm_family != AF_MPLS) if (rtm->rtm_family != AF_MPLS)
goto errout; goto errout;
...@@ -1731,27 +1752,43 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -1731,27 +1752,43 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh,
static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
struct mpls_route_config cfg; struct mpls_route_config *cfg;
int err; int err;
err = rtm_to_route_config(skb, nlh, &cfg); cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
err = rtm_to_route_config(skb, nlh, cfg);
if (err < 0) if (err < 0)
return err; goto out;
return mpls_route_del(&cfg); err = mpls_route_del(cfg);
out:
kfree(cfg);
return err;
} }
static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
struct mpls_route_config cfg; struct mpls_route_config *cfg;
int err; int err;
err = rtm_to_route_config(skb, nlh, &cfg); cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
err = rtm_to_route_config(skb, nlh, cfg);
if (err < 0) if (err < 0)
return err; goto out;
return mpls_route_add(&cfg); err = mpls_route_add(cfg);
out:
kfree(cfg);
return err;
} }
static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
...@@ -1980,7 +2017,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) ...@@ -1980,7 +2017,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
/* In case the predefined labels need to be populated */ /* In case the predefined labels need to be populated */
if (limit > MPLS_LABEL_IPV4NULL) { if (limit > MPLS_LABEL_IPV4NULL) {
struct net_device *lo = net->loopback_dev; struct net_device *lo = net->loopback_dev;
rt0 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); rt0 = mpls_rt_alloc(1, lo->addr_len, 0);
if (IS_ERR(rt0)) if (IS_ERR(rt0))
goto nort0; goto nort0;
RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo);
...@@ -1994,7 +2031,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) ...@@ -1994,7 +2031,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
} }
if (limit > MPLS_LABEL_IPV6NULL) { if (limit > MPLS_LABEL_IPV6NULL) {
struct net_device *lo = net->loopback_dev; struct net_device *lo = net->loopback_dev;
rt2 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); rt2 = mpls_rt_alloc(1, lo->addr_len, 0);
if (IS_ERR(rt2)) if (IS_ERR(rt2))
goto nort2; goto nort2;
RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo);
......
...@@ -197,7 +197,7 @@ static inline struct mpls_dev *mpls_dev_get(const struct net_device *dev) ...@@ -197,7 +197,7 @@ static inline struct mpls_dev *mpls_dev_get(const struct net_device *dev)
int nla_put_labels(struct sk_buff *skb, int attrtype, u8 labels, int nla_put_labels(struct sk_buff *skb, int attrtype, u8 labels,
const u32 label[]); const u32 label[]);
int nla_get_labels(const struct nlattr *nla, u32 max_labels, u8 *labels, int nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels,
u32 label[]); u32 label[]);
int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table, int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table,
u8 via[]); u8 via[]);
......
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