Merge branch 'mpls-more-labels'

David Ahern says:

====================
net: mpls: Allow users to configure more labels per route

Increase the maximum number of new labels for MPLS routes from 2 to 30.

To keep memory consumption in check, the labels array is moved to the end
of mpls_nh and mpls_iptunnel_encap structs as a 0-sized array. Allocations
use the maximum number of labels across all nexthops in a route for LSR
and the number of labels configured for LWT.

The mpls_route layout is changed to:

   +----------------------+
   | mpls_route           |
   +----------------------+
   | mpls_nh 0            |
   +----------------------+
   | alignment padding    |   4 bytes for odd number of labels; 0 for even
   +----------------------+
   | via[rt_max_alen] 0   |
   +----------------------+
   | alignment padding    |   via's aligned on sizeof(unsigned long)
   +----------------------+
   | ...                  |

Meaning the via follows its mpls_nh providing better locality as the
number of labels increases. UDP_RR tests with namespaces shows no impact
to a modest performance increase with this layout for 1 or 2 labels and
1 or 2 nexthops.

mpls_route allocation size is limited to 4096 bytes allowing on the
order of 30 nexthops with 30 labels (or more nexthops with fewer
labels). LWT encap shares same maximum number of labels as mpls routing.

v3
- initialize n_labels to 0 in case RTA_NEWDST is not defined; detected
  by the kbuild test robot

v2
- updates per Eric's comments
  + added patch to ensure all reads of rt_nhn_alive and nh_flags in
    the packet path use READ_ONCE and all writes via event handlers
    use WRITE_ONCE

  + limit mpls_route size to 4096 (PAGE_SIZE for most arch)

  + mostly killed use of MAX_NEW_LABELS; it exists only for common
    limit between lwt and routing paths
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-04-01 20:21:45 -07:00
commit a6fc09dff2
4 changed files with 198 additions and 95 deletions

View File

@ -14,13 +14,12 @@
#ifndef _NET_MPLS_IPTUNNEL_H #ifndef _NET_MPLS_IPTUNNEL_H
#define _NET_MPLS_IPTUNNEL_H 1 #define _NET_MPLS_IPTUNNEL_H 1
#define MAX_NEW_LABELS 2
struct mpls_iptunnel_encap { struct mpls_iptunnel_encap {
u32 label[MAX_NEW_LABELS];
u8 labels; u8 labels;
u8 ttl_propagate; u8 ttl_propagate;
u8 default_ttl; u8 default_ttl;
u8 reserved1;
u32 label[0];
}; };
static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate) static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate)

View File

@ -24,6 +24,9 @@
#include <net/nexthop.h> #include <net/nexthop.h>
#include "internal.h" #include "internal.h"
/* max memory we will use for mpls_route */
#define MAX_MPLS_ROUTE_MEM 4096
/* Maximum number of labels to look ahead at when selecting a path of /* Maximum number of labels to look ahead at when selecting a path of
* a multipath route * a multipath route
*/ */
@ -60,10 +63,7 @@ EXPORT_SYMBOL_GPL(mpls_output_possible);
static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh) static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh)
{ {
u8 *nh0_via = PTR_ALIGN((u8 *)&rt->rt_nh[rt->rt_nhn], VIA_ALEN_ALIGN); return (u8 *)nh + rt->rt_via_offset;
int nh_index = nh - rt->rt_nh;
return nh0_via + rt->rt_max_alen * nh_index;
} }
static const u8 *mpls_nh_via(const struct mpls_route *rt, static const u8 *mpls_nh_via(const struct mpls_route *rt,
@ -189,21 +189,32 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
return hash; return hash;
} }
static struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index)
{
return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size);
}
/* number of alive nexthops (rt->rt_nhn_alive) and the flags for
* a next hop (nh->nh_flags) are modified by netdev event handlers.
* Since those fields can change at any moment, use READ_ONCE to
* access both.
*/
static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
struct sk_buff *skb) struct sk_buff *skb)
{ {
int alive = ACCESS_ONCE(rt->rt_nhn_alive);
u32 hash = 0; u32 hash = 0;
int nh_index = 0; int nh_index = 0;
int n = 0; int n = 0;
u8 alive;
/* No need to look further into packet if there's only /* No need to look further into packet if there's only
* one path * one path
*/ */
if (rt->rt_nhn == 1) if (rt->rt_nhn == 1)
goto out; return rt->rt_nh;
if (alive <= 0) alive = READ_ONCE(rt->rt_nhn_alive);
if (alive == 0)
return NULL; return NULL;
hash = mpls_multipath_hash(rt, skb); hash = mpls_multipath_hash(rt, skb);
@ -211,7 +222,9 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
if (alive == rt->rt_nhn) if (alive == rt->rt_nhn)
goto out; goto out;
for_nexthops(rt) { for_nexthops(rt) {
if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) unsigned int nh_flags = READ_ONCE(nh->nh_flags);
if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
continue; continue;
if (n == nh_index) if (n == nh_index)
return nh; return nh;
@ -219,7 +232,7 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
} endfor_nexthops(rt); } endfor_nexthops(rt);
out: out:
return &rt->rt_nh[nh_index]; return mpls_get_nexthop(rt, nh_index);
} }
static bool mpls_egress(struct net *net, struct mpls_route *rt, static bool mpls_egress(struct net *net, struct mpls_route *rt,
@ -458,20 +471,27 @@ struct mpls_route_config {
int rc_mp_len; int rc_mp_len;
}; };
static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen) /* all nexthops within a route have the same size based on max
* number of labels and max via length for a hop
*/
static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels)
{ {
u8 max_alen_aligned = ALIGN(max_alen, VIA_ALEN_ALIGN); u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen);
struct mpls_route *rt; struct mpls_route *rt;
size_t size;
size = sizeof(*rt) + num_nh * nh_size;
if (size > MAX_MPLS_ROUTE_MEM)
return ERR_PTR(-EINVAL);
rt = kzalloc(size, GFP_KERNEL);
if (!rt)
return ERR_PTR(-ENOMEM);
rt = kzalloc(ALIGN(sizeof(*rt) + num_nh * sizeof(*rt->rt_nh),
VIA_ALEN_ALIGN) +
num_nh * max_alen_aligned,
GFP_KERNEL);
if (rt) {
rt->rt_nhn = num_nh; rt->rt_nhn = num_nh;
rt->rt_nhn_alive = num_nh; rt->rt_nhn_alive = num_nh;
rt->rt_max_alen = max_alen_aligned; rt->rt_nh_size = nh_size;
} rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels);
return rt; return rt;
} }
@ -676,9 +696,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++)
@ -703,7 +720,7 @@ errout:
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;
@ -711,7 +728,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;
@ -736,22 +753,20 @@ errout:
return err; return err;
} }
static int 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 nhs = 0;
int remaining = len; int remaining = len;
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);
@ -765,7 +780,20 @@ static int 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.
* Check for overflow.
*/
if (nhs == 255)
return 0;
nhs++; nhs++;
rtnh = rtnh_next(rtnh, &remaining); rtnh = rtnh_next(rtnh, &remaining);
} }
@ -774,13 +802,13 @@ static int 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;
int remaining = cfg->rc_mp_len; int remaining = cfg->rc_mp_len;
int nhs = 0;
int err = 0; int err = 0;
u8 nhs = 0;
change_nexthops(rt) { change_nexthops(rt) {
int attrlen; int attrlen;
@ -807,7 +835,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;
@ -834,7 +863,8 @@ 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;
int nhs; u8 max_labels;
u8 nhs;
index = cfg->rc_label; index = cfg->rc_label;
@ -872,22 +902,32 @@ static int mpls_route_add(struct mpls_route_config *cfg)
goto errout; goto errout;
err = -EINVAL; err = -EINVAL;
if (cfg->rc_mp) {
nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
cfg->rc_via_alen, &max_via_alen); 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); rt = mpls_rt_alloc(nhs, max_via_alen, max_labels);
if (!rt) if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto errout; goto errout;
}
rt->rt_protocol = cfg->rc_protocol; rt->rt_protocol = cfg->rc_protocol;
rt->rt_payload_type = cfg->rc_payload_type; rt->rt_payload_type = cfg->rc_payload_type;
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)
@ -1302,8 +1342,7 @@ static void mpls_ifdown(struct net_device *dev, int event)
{ {
struct mpls_route __rcu **platform_label; struct mpls_route __rcu **platform_label;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
unsigned int nh_flags = RTNH_F_DEAD | RTNH_F_LINKDOWN; u8 alive, deleted;
unsigned int alive, deleted;
unsigned index; unsigned index;
platform_label = rtnl_dereference(net->mpls.platform_label); platform_label = rtnl_dereference(net->mpls.platform_label);
@ -1316,22 +1355,27 @@ static void mpls_ifdown(struct net_device *dev, int event)
alive = 0; alive = 0;
deleted = 0; deleted = 0;
change_nexthops(rt) { change_nexthops(rt) {
unsigned int nh_flags = nh->nh_flags;
if (rtnl_dereference(nh->nh_dev) != dev) if (rtnl_dereference(nh->nh_dev) != dev)
goto next; goto next;
switch (event) { switch (event) {
case NETDEV_DOWN: case NETDEV_DOWN:
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
nh->nh_flags |= RTNH_F_DEAD; nh_flags |= RTNH_F_DEAD;
/* fall through */ /* fall through */
case NETDEV_CHANGE: case NETDEV_CHANGE:
nh->nh_flags |= RTNH_F_LINKDOWN; nh_flags |= RTNH_F_LINKDOWN;
break; break;
} }
if (event == NETDEV_UNREGISTER) if (event == NETDEV_UNREGISTER)
RCU_INIT_POINTER(nh->nh_dev, NULL); RCU_INIT_POINTER(nh->nh_dev, NULL);
if (nh->nh_flags != nh_flags)
WRITE_ONCE(nh->nh_flags, nh_flags);
next: next:
if (!(nh->nh_flags & nh_flags)) if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
alive++; alive++;
if (!rtnl_dereference(nh->nh_dev)) if (!rtnl_dereference(nh->nh_dev))
deleted++; deleted++;
@ -1345,12 +1389,12 @@ next:
} }
} }
static void mpls_ifup(struct net_device *dev, unsigned int nh_flags) static void mpls_ifup(struct net_device *dev, unsigned int flags)
{ {
struct mpls_route __rcu **platform_label; struct mpls_route __rcu **platform_label;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
unsigned index; unsigned index;
int alive; u8 alive;
platform_label = rtnl_dereference(net->mpls.platform_label); platform_label = rtnl_dereference(net->mpls.platform_label);
for (index = 0; index < net->mpls.platform_labels; index++) { for (index = 0; index < net->mpls.platform_labels; index++) {
@ -1361,20 +1405,22 @@ static void mpls_ifup(struct net_device *dev, unsigned int nh_flags)
alive = 0; alive = 0;
change_nexthops(rt) { change_nexthops(rt) {
unsigned int nh_flags = nh->nh_flags;
struct net_device *nh_dev = struct net_device *nh_dev =
rtnl_dereference(nh->nh_dev); rtnl_dereference(nh->nh_dev);
if (!(nh->nh_flags & nh_flags)) { if (!(nh_flags & flags)) {
alive++; alive++;
continue; continue;
} }
if (nh_dev != dev) if (nh_dev != dev)
continue; continue;
alive++; alive++;
nh->nh_flags &= ~nh_flags; nh_flags &= ~flags;
WRITE_ONCE(nh->nh_flags, flags);
} endfor_nexthops(rt); } endfor_nexthops(rt);
ACCESS_ONCE(rt->rt_nhn_alive) = alive; WRITE_ONCE(rt->rt_nhn_alive, alive);
} }
} }
@ -1495,16 +1541,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 */
@ -1512,6 +1560,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) {
@ -1535,6 +1587,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;
} }
@ -1596,7 +1649,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;
@ -1695,27 +1747,43 @@ errout:
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 (err < 0) if (!cfg)
return err; return -ENOMEM;
return mpls_route_del(&cfg); err = rtm_to_route_config(skb, nlh, cfg);
if (err < 0)
goto out;
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 (err < 0) if (!cfg)
return err; return -ENOMEM;
return mpls_route_add(&cfg); err = rtm_to_route_config(skb, nlh, cfg);
if (err < 0)
goto out;
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,
@ -1772,8 +1840,8 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
} else { } else {
struct rtnexthop *rtnh; struct rtnexthop *rtnh;
struct nlattr *mp; struct nlattr *mp;
int dead = 0; u8 linkdown = 0;
int linkdown = 0; u8 dead = 0;
mp = nla_nest_start(skb, RTA_MULTIPATH); mp = nla_nest_start(skb, RTA_MULTIPATH);
if (!mp) if (!mp)
@ -1944,8 +2012,8 @@ 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); rt0 = mpls_rt_alloc(1, lo->addr_len, 0);
if (!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);
rt0->rt_protocol = RTPROT_KERNEL; rt0->rt_protocol = RTPROT_KERNEL;
@ -1958,8 +2026,8 @@ 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); rt2 = mpls_rt_alloc(1, lo->addr_len, 0);
if (!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);
rt2->rt_protocol = RTPROT_KERNEL; rt2->rt_protocol = RTPROT_KERNEL;

View File

@ -2,6 +2,11 @@
#define MPLS_INTERNAL_H #define MPLS_INTERNAL_H
#include <net/mpls.h> #include <net/mpls.h>
/* put a reasonable limit on the number of labels
* we will accept from userspace
*/
#define MAX_NEW_LABELS 30
struct mpls_entry_decoded { struct mpls_entry_decoded {
u32 label; u32 label;
u8 ttl; u8 ttl;
@ -64,7 +69,6 @@ struct mpls_dev {
struct sk_buff; struct sk_buff;
#define LABEL_NOT_SPECIFIED (1 << 20) #define LABEL_NOT_SPECIFIED (1 << 20)
#define MAX_NEW_LABELS 2
/* This maximum ha length copied from the definition of struct neighbour */ /* This maximum ha length copied from the definition of struct neighbour */
#define VIA_ALEN_ALIGN sizeof(unsigned long) #define VIA_ALEN_ALIGN sizeof(unsigned long)
@ -83,13 +87,31 @@ enum mpls_payload_type {
struct mpls_nh { /* next hop label forwarding entry */ struct mpls_nh { /* next hop label forwarding entry */
struct net_device __rcu *nh_dev; struct net_device __rcu *nh_dev;
/* nh_flags is accessed under RCU in the packet path; it is
* modified handling netdev events with rtnl lock held
*/
unsigned int nh_flags; unsigned int nh_flags;
u32 nh_label[MAX_NEW_LABELS];
u8 nh_labels; u8 nh_labels;
u8 nh_via_alen; u8 nh_via_alen;
u8 nh_via_table; u8 nh_via_table;
u8 nh_reserved1;
u32 nh_label[0];
}; };
/* offset of via from beginning of mpls_nh */
#define MPLS_NH_VIA_OFF(num_labels) \
ALIGN(sizeof(struct mpls_nh) + (num_labels) * sizeof(u32), \
VIA_ALEN_ALIGN)
/* all nexthops within a route have the same size based on the
* max number of labels and max via length across all nexthops
*/
#define MPLS_NH_SIZE(num_labels, max_via_alen) \
(MPLS_NH_VIA_OFF((num_labels)) + \
ALIGN((max_via_alen), VIA_ALEN_ALIGN))
enum mpls_ttl_propagation { enum mpls_ttl_propagation {
MPLS_TTL_PROP_DEFAULT, MPLS_TTL_PROP_DEFAULT,
MPLS_TTL_PROP_ENABLED, MPLS_TTL_PROP_ENABLED,
@ -104,16 +126,16 @@ enum mpls_ttl_propagation {
* +----------------------+ * +----------------------+
* | mpls_nh 0 | * | mpls_nh 0 |
* +----------------------+ * +----------------------+
* | ... | * | alignment padding | 4 bytes for odd number of labels
* +----------------------+
* | mpls_nh n-1 |
* +----------------------+
* | alignment padding |
* +----------------------+ * +----------------------+
* | via[rt_max_alen] 0 | * | via[rt_max_alen] 0 |
* +----------------------+ * +----------------------+
* | alignment padding | via's aligned on sizeof(unsigned long)
* +----------------------+
* | ... | * | ... |
* +----------------------+ * +----------------------+
* | mpls_nh n-1 |
* +----------------------+
* | via[rt_max_alen] n-1 | * | via[rt_max_alen] n-1 |
* +----------------------+ * +----------------------+
*/ */
@ -123,22 +145,29 @@ struct mpls_route { /* next hop label forwarding entry */
u8 rt_payload_type; u8 rt_payload_type;
u8 rt_max_alen; u8 rt_max_alen;
u8 rt_ttl_propagate; u8 rt_ttl_propagate;
unsigned int rt_nhn; u8 rt_nhn;
unsigned int rt_nhn_alive; /* rt_nhn_alive is accessed under RCU in the packet path; it
* is modified handling netdev events with rtnl lock held
*/
u8 rt_nhn_alive;
u8 rt_nh_size;
u8 rt_via_offset;
u8 rt_reserved1;
struct mpls_nh rt_nh[0]; struct mpls_nh rt_nh[0];
}; };
#define for_nexthops(rt) { \ #define for_nexthops(rt) { \
int nhsel; struct mpls_nh *nh; \ int nhsel; struct mpls_nh *nh; u8 *__nh; \
for (nhsel = 0, nh = (rt)->rt_nh; \ for (nhsel = 0, nh = (rt)->rt_nh, __nh = (u8 *)((rt)->rt_nh); \
nhsel < (rt)->rt_nhn; \ nhsel < (rt)->rt_nhn; \
nh++, nhsel++) __nh += rt->rt_nh_size, nh = (struct mpls_nh *)__nh, nhsel++)
#define change_nexthops(rt) { \ #define change_nexthops(rt) { \
int nhsel; struct mpls_nh *nh; \ int nhsel; struct mpls_nh *nh; u8 *__nh; \
for (nhsel = 0, nh = (struct mpls_nh *)((rt)->rt_nh); \ for (nhsel = 0, nh = (struct mpls_nh *)((rt)->rt_nh), \
__nh = (u8 *)((rt)->rt_nh); \
nhsel < (rt)->rt_nhn; \ nhsel < (rt)->rt_nhn; \
nh++, nhsel++) __nh += rt->rt_nh_size, nh = (struct mpls_nh *)__nh, nhsel++)
#define endfor_nexthops(rt) } #define endfor_nexthops(rt) }
@ -173,7 +202,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[]);

View File

@ -164,6 +164,7 @@ static int mpls_build_state(struct nlattr *nla,
struct mpls_iptunnel_encap *tun_encap_info; struct mpls_iptunnel_encap *tun_encap_info;
struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1]; struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1];
struct lwtunnel_state *newts; struct lwtunnel_state *newts;
u8 n_labels;
int ret; int ret;
ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla,
@ -175,12 +176,18 @@ static int mpls_build_state(struct nlattr *nla,
return -EINVAL; return -EINVAL;
newts = lwtunnel_state_alloc(sizeof(*tun_encap_info)); /* determine number of labels */
if (nla_get_labels(tb[MPLS_IPTUNNEL_DST],
MAX_NEW_LABELS, &n_labels, NULL))
return -EINVAL;
newts = lwtunnel_state_alloc(sizeof(*tun_encap_info) +
n_labels * sizeof(u32));
if (!newts) if (!newts)
return -ENOMEM; return -ENOMEM;
tun_encap_info = mpls_lwtunnel_encap(newts); tun_encap_info = mpls_lwtunnel_encap(newts);
ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], MAX_NEW_LABELS, ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], n_labels,
&tun_encap_info->labels, tun_encap_info->label); &tun_encap_info->labels, tun_encap_info->label);
if (ret) if (ret)
goto errout; goto errout;
@ -257,7 +264,7 @@ static int mpls_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
a_hdr->default_ttl != b_hdr->default_ttl) a_hdr->default_ttl != b_hdr->default_ttl)
return 1; return 1;
for (l = 0; l < MAX_NEW_LABELS; l++) for (l = 0; l < a_hdr->labels; l++)
if (a_hdr->label[l] != b_hdr->label[l]) if (a_hdr->label[l] != b_hdr->label[l])
return 1; return 1;
return 0; return 0;