}
EXPORT_SYMBOL_GPL(mpls_output_possible);
+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);
+ 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,
+ const struct mpls_nh *nh)
+{
+ return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh);
+}
+
static unsigned int mpls_nh_header_size(const struct mpls_nh *nh)
{
/* The size of the layer 2.5 labels to be added for this route */
}
}
- err = neigh_xmit(nh->nh_via_table, out_dev, nh->nh_via, skb);
+ err = neigh_xmit(nh->nh_via_table, out_dev, mpls_nh_via(rt, nh), skb);
if (err)
net_dbg_ratelimited("%s: packet transmission failed: %d\n",
__func__, err);
int rc_mp_len;
};
-static struct mpls_route *mpls_rt_alloc(int num_nh)
+static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen)
{
+ u8 max_alen_aligned = ALIGN(max_alen, VIA_ALEN_ALIGN);
struct mpls_route *rt;
- rt = kzalloc(sizeof(*rt) + (num_nh * sizeof(struct mpls_nh)),
+ rt = kzalloc(ALIGN(sizeof(*rt) + num_nh * sizeof(*rt->rt_nh),
+ VIA_ALEN_ALIGN) +
+ num_nh * max_alen_aligned,
GFP_KERNEL);
- if (rt)
+ if (rt) {
rt->rt_nhn = num_nh;
+ rt->rt_max_alen = max_alen_aligned;
+ }
return rt;
}
}
#if IS_ENABLED(CONFIG_INET)
-static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet_fib_lookup_dev(struct net *net,
+ const void *addr)
{
struct net_device *dev;
struct rtable *rt;
return dev;
}
#else
-static struct net_device *inet_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet_fib_lookup_dev(struct net *net,
+ const void *addr)
{
return ERR_PTR(-EAFNOSUPPORT);
}
#endif
#if IS_ENABLED(CONFIG_IPV6)
-static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet6_fib_lookup_dev(struct net *net,
+ const void *addr)
{
struct net_device *dev;
struct dst_entry *dst;
return dev;
}
#else
-static struct net_device *inet6_fib_lookup_dev(struct net *net, void *addr)
+static struct net_device *inet6_fib_lookup_dev(struct net *net,
+ const void *addr)
{
return ERR_PTR(-EAFNOSUPPORT);
}
#endif
static struct net_device *find_outdev(struct net *net,
+ struct mpls_route *rt,
struct mpls_nh *nh, int oif)
{
struct net_device *dev = NULL;
if (!oif) {
switch (nh->nh_via_table) {
case NEIGH_ARP_TABLE:
- dev = inet_fib_lookup_dev(net, nh->nh_via);
+ dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh));
break;
case NEIGH_ND_TABLE:
- dev = inet6_fib_lookup_dev(net, nh->nh_via);
+ dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh));
break;
case NEIGH_LINK_TABLE:
break;
return dev;
}
-static int mpls_nh_assign_dev(struct net *net, struct mpls_nh *nh, int oif)
+static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
+ struct mpls_nh *nh, int oif)
{
struct net_device *dev = NULL;
int err = -ENODEV;
- dev = find_outdev(net, nh, oif);
+ dev = find_outdev(net, rt, nh, oif);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
dev = NULL;
nh->nh_label[i] = cfg->rc_output_label[i];
nh->nh_via_table = cfg->rc_via_table;
- memcpy(nh->nh_via, cfg->rc_via, cfg->rc_via_alen);
+ memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen);
nh->nh_via_alen = cfg->rc_via_alen;
- err = mpls_nh_assign_dev(net, nh, cfg->rc_ifindex);
+ err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex);
if (err)
goto errout;
return err;
}
-static int mpls_nh_build(struct net *net, struct mpls_nh *nh,
- int oif, struct nlattr *via, struct nlattr *newdst)
+static int mpls_nh_build(struct net *net, struct mpls_route *rt,
+ struct mpls_nh *nh, int oif,
+ struct nlattr *via, struct nlattr *newdst)
{
int err = -ENOMEM;
}
err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
- nh->nh_via);
+ __mpls_nh_via(rt, nh));
if (err)
goto errout;
- err = mpls_nh_assign_dev(net, nh, oif);
+ err = mpls_nh_assign_dev(net, rt, nh, oif);
if (err)
goto errout;
return err;
}
-static int mpls_count_nexthops(struct rtnexthop *rtnh, int len)
+static int mpls_count_nexthops(struct rtnexthop *rtnh, int len,
+ u8 cfg_via_alen, u8 *max_via_alen)
{
int nhs = 0;
int remaining = len;
+ if (!rtnh) {
+ *max_via_alen = cfg_via_alen;
+ return 1;
+ }
+
+ *max_via_alen = 0;
+
while (rtnh_ok(rtnh, remaining)) {
+ struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
+ int attrlen;
+
+ attrlen = rtnh_attrlen(rtnh);
+ nla = nla_find(attrs, attrlen, RTA_VIA);
+ if (nla && nla_len(nla) >=
+ offsetof(struct rtvia, rtvia_addr)) {
+ int via_alen = nla_len(nla) -
+ offsetof(struct rtvia, rtvia_addr);
+
+ if (via_alen <= MAX_VIA_ALEN)
+ *max_via_alen = max_t(u16, *max_via_alen,
+ via_alen);
+ }
+
nhs++;
rtnh = rtnh_next(rtnh, &remaining);
}
if (!nla_via)
goto errout;
- err = mpls_nh_build(cfg->rc_nlinfo.nl_net, nh,
+ err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
rtnh->rtnh_ifindex, nla_via,
nla_newdst);
if (err)
struct net *net = cfg->rc_nlinfo.nl_net;
struct mpls_route *rt, *old;
int err = -EINVAL;
+ u8 max_via_alen;
unsigned index;
- int nhs = 1; /* default to one nexthop */
+ int nhs;
index = cfg->rc_label;
if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old)
goto errout;
- if (cfg->rc_mp) {
- err = -EINVAL;
- nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len);
- if (nhs == 0)
- goto errout;
- }
+ err = -EINVAL;
+ nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
+ cfg->rc_via_alen, &max_via_alen);
+ if (nhs == 0)
+ goto errout;
err = -ENOMEM;
- rt = mpls_rt_alloc(nhs);
+ rt = mpls_rt_alloc(nhs, max_via_alen);
if (!rt)
goto errout;
if (nla_put_labels(skb, RTA_DST, 1, &label))
goto nla_put_failure;
if (rt->rt_nhn == 1) {
- struct mpls_nh *nh = rt->rt_nh;
+ const struct mpls_nh *nh = rt->rt_nh;
if (nh->nh_labels &&
nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
nh->nh_label))
goto nla_put_failure;
- if (nla_put_via(skb, nh->nh_via_table, nh->nh_via,
+ if (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);
nh->nh_label))
goto nla_put_failure;
if (nla_put_via(skb, nh->nh_via_table,
- nh->nh_via,
+ mpls_nh_via(rt, nh),
nh->nh_via_alen))
goto nla_put_failure;
/* In case the predefined labels need to be populated */
if (limit > MPLS_LABEL_IPV4NULL) {
struct net_device *lo = net->loopback_dev;
- rt0 = mpls_rt_alloc(1);
+ rt0 = mpls_rt_alloc(1, lo->addr_len);
if (!rt0)
goto nort0;
RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo);
rt0->rt_protocol = RTPROT_KERNEL;
rt0->rt_payload_type = MPT_IPV4;
rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
- memcpy(rt0->rt_nh->nh_via, lo->dev_addr, lo->addr_len);
+ rt0->rt_nh->nh_via_alen = lo->addr_len;
+ memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr,
+ lo->addr_len);
}
if (limit > MPLS_LABEL_IPV6NULL) {
struct net_device *lo = net->loopback_dev;
- rt2 = mpls_rt_alloc(1);
+ rt2 = mpls_rt_alloc(1, lo->addr_len);
if (!rt2)
goto nort2;
RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo);
rt2->rt_protocol = RTPROT_KERNEL;
rt2->rt_payload_type = MPT_IPV6;
rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
- memcpy(rt2->rt_nh->nh_via, lo->dev_addr, lo->addr_len);
+ rt2->rt_nh->nh_via_alen = lo->addr_len;
+ memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr,
+ lo->addr_len);
}
rtnl_lock();