Release 4.11 net/mpls/af_mpls.c
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/sysctl.h>
#include <linux/net.h>
#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/mpls.h>
#include <linux/netconf.h>
#include <linux/vmalloc.h>
#include <linux/percpu.h>
#include <net/ip.h>
#include <net/dst.h>
#include <net/sock.h>
#include <net/arp.h>
#include <net/ip_fib.h>
#include <net/netevent.h>
#include <net/netns/generic.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#endif
#include <net/addrconf.h>
#include <net/nexthop.h>
#include "internal.h"
/* Maximum number of labels to look ahead at when selecting a path of
* a multipath route
*/
#define MAX_MP_SELECT_LABELS 4
#define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1)
static int zero = 0;
static int label_limit = (1 << 20) - 1;
static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt,
struct nlmsghdr *nlh, struct net *net, u32 portid,
unsigned int nlm_flags);
static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
{
struct mpls_route *rt = NULL;
if (index < net->mpls.platform_labels) {
struct mpls_route __rcu **platform_label =
rcu_dereference(net->mpls.platform_label);
rt = rcu_dereference(platform_label[index]);
}
return rt;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 64 | 100.00% | 1 | 100.00% |
Total | 64 | 100.00% | 1 | 100.00% |
bool mpls_output_possible(const struct net_device *dev)
{
return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 27 | 100.00% | 1 | 100.00% |
Total | 27 | 100.00% | 1 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Robert Shearman | 58 | 100.00% | 1 | 100.00% |
Total | 58 | 100.00% | 1 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Robert Shearman | 38 | 100.00% | 1 | 100.00% |
Total | 38 | 100.00% | 1 | 100.00% |
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 */
return nh->nh_labels * sizeof(struct mpls_shim_hdr);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 20 | 80.00% | 1 | 50.00% |
Roopa Prabhu | 5 | 20.00% | 1 | 50.00% |
Total | 25 | 100.00% | 2 | 100.00% |
unsigned int mpls_dev_mtu(const struct net_device *dev)
{
/* The amount of data the layer 2 frame can hold */
return dev->mtu;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 18 | 100.00% | 1 | 100.00% |
Total | 18 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(mpls_dev_mtu);
bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
{
if (skb->len <= mtu)
return false;
if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
return false;
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 43 | 93.48% | 1 | 50.00% |
Marcelo Ricardo Leitner | 3 | 6.52% | 1 | 50.00% |
Total | 46 | 100.00% | 2 | 100.00% |
EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
void mpls_stats_inc_outucastpkts(struct net_device *dev,
const struct sk_buff *skb)
{
struct mpls_dev *mdev;
if (skb->protocol == htons(ETH_P_MPLS_UC)) {
mdev = mpls_dev_get(dev);
if (mdev)
MPLS_INC_STATS_LEN(mdev, skb->len,
tx_packets,
tx_bytes);
} else if (skb->protocol == htons(ETH_P_IP)) {
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
#if IS_ENABLED(CONFIG_IPV6)
} else if (skb->protocol == htons(ETH_P_IPV6)) {
struct inet6_dev *in6dev = __in6_dev_get(dev);
if (in6dev)
IP6_UPD_PO_STATS(dev_net(dev), in6dev,
IPSTATS_MIB_OUT, skb->len);
#endif
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Robert Shearman | 138 | 100.00% | 1 | 100.00% |
Total | 138 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(mpls_stats_inc_outucastpkts);
static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
{
struct mpls_entry_decoded dec;
unsigned int mpls_hdr_len = 0;
struct mpls_shim_hdr *hdr;
bool eli_seen = false;
int label_index;
u32 hash = 0;
for (label_index = 0; label_index < MAX_MP_SELECT_LABELS;
label_index++) {
mpls_hdr_len += sizeof(*hdr);
if (!pskb_may_pull(skb, mpls_hdr_len))
break;
/* Read and decode the current label */
hdr = mpls_hdr(skb) + label_index;
dec = mpls_entry_decode(hdr);
/* RFC6790 - reserved labels MUST NOT be used as keys
* for the load-balancing function
*/
if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) {
hash = jhash_1word(dec.label, hash);
/* The entropy label follows the entropy label
* indicator, so this means that the entropy
* label was just added to the hash - no need to
* go any deeper either in the label stack or in the
* payload
*/
if (eli_seen)
break;
} else if (dec.label == MPLS_LABEL_ENTROPY) {
eli_seen = true;
}
if (!dec.bos)
continue;
/* found bottom label; does skb have room for a header? */
if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
const struct iphdr *v4hdr;
v4hdr = (const struct iphdr *)(hdr + 1);
if (v4hdr->version == 4) {
hash = jhash_3words(ntohl(v4hdr->saddr),
ntohl(v4hdr->daddr),
v4hdr->protocol, hash);
} else if (v4hdr->version == 6 &&
pskb_may_pull(skb, mpls_hdr_len +
sizeof(struct ipv6hdr))) {
const struct ipv6hdr *v6hdr;
v6hdr = (const struct ipv6hdr *)(hdr + 1);
hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
hash = jhash_1word(v6hdr->nexthdr, hash);
}
}
break;
}
return hash;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Robert Shearman | 263 | 86.23% | 1 | 25.00% |
David Ahern | 28 | 9.18% | 1 | 25.00% |
Roopa Prabhu | 14 | 4.59% | 2 | 50.00% |
Total | 305 | 100.00% | 4 | 100.00% |
static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
struct sk_buff *skb)
{
int alive = ACCESS_ONCE(rt->rt_nhn_alive);
u32 hash = 0;
int nh_index = 0;
int n = 0;
/* No need to look further into packet if there's only
* one path
*/
if (rt->rt_nhn == 1)
goto out;
if (alive <= 0)
return NULL;
hash = mpls_multipath_hash(rt, skb);
nh_index = hash % alive;
if (alive == rt->rt_nhn)
goto out;
for_nexthops(rt) {
if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
continue;
if (n == nh_index)
return nh;
n++;
} endfor_nexthops(rt);
out:
return &rt->rt_nh[nh_index];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 126 | 91.97% | 2 | 66.67% |
Robert Shearman | 11 | 8.03% | 1 | 33.33% |
Total | 137 | 100.00% | 3 | 100.00% |
static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb,
struct mpls_entry_decoded dec)
{
enum mpls_payload_type payload_type;
bool success = false;
/* The IPv4 code below accesses through the IPv4 header
* checksum, which is 12 bytes into the packet.
* The IPv6 code below accesses through the IPv6 hop limit
* which is 8 bytes into the packet.
*
* For all supported cases there should always be at least 12
* bytes of packet data present. The IPv4 header is 20 bytes
* without options and the IPv6 header is always 40 bytes
* long.
*/
if (!pskb_may_pull(skb, 12))
return false;
payload_type = rt->rt_payload_type;
if (payload_type == MPT_UNSPEC)
payload_type = ip_hdr(skb)->version;
switch (payload_type) {
case MPT_IPV4: {
struct iphdr *hdr4 = ip_hdr(skb);
skb->protocol = htons(ETH_P_IP);
csum_replace2(&hdr4->check,
htons(hdr4->ttl << 8),
htons(dec.ttl << 8));
hdr4->ttl = dec.ttl;
success = true;
break;
}
case MPT_IPV6: {
struct ipv6hdr *hdr6 = ipv6_hdr(skb);
skb->protocol = htons(ETH_P_IPV6);
hdr6->hop_limit = dec.ttl;
success = true;
break;
}
case MPT_UNSPEC:
break;
}
return success;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 126 | 71.19% | 2 | 66.67% |
Robert Shearman | 51 | 28.81% | 1 | 33.33% |
Total | 177 | 100.00% | 3 | 100.00% |
static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct net *net = dev_net(dev);
struct mpls_shim_hdr *hdr;
struct mpls_route *rt;
struct mpls_nh *nh;
struct mpls_entry_decoded dec;
struct net_device *out_dev;
struct mpls_dev *out_mdev;
struct mpls_dev *mdev;
unsigned int hh_len;
unsigned int new_header_size;
unsigned int mtu;
int err;
/* Careful this entire function runs inside of an rcu critical section */
mdev = mpls_dev_get(dev);
if (!mdev)
goto drop;
MPLS_INC_STATS_LEN(mdev, skb->len, rx_packets,
rx_bytes);
if (!mdev->input_enabled) {
MPLS_INC_STATS(mdev, rx_dropped);
goto drop;
}
if (skb->pkt_type != PACKET_HOST)
goto err;
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
goto err;
if (!pskb_may_pull(skb, sizeof(*hdr)))
goto err;
/* Read and decode the label */
hdr = mpls_hdr(skb);
dec = mpls_entry_decode(hdr);
rt = mpls_route_input_rcu(net, dec.label);
if (!rt) {
MPLS_INC_STATS(mdev, rx_noroute);
goto drop;
}
nh = mpls_select_multipath(rt, skb);
if (!nh)
goto err;
/* Pop the label */
skb_pull(skb, sizeof(*hdr));
skb_reset_network_header(skb);
skb_orphan(skb);
if (skb_warn_if_lro(skb))
goto err;
skb_forward_csum(skb);
/* Verify ttl is valid */
if (dec.ttl <= 1)
goto err;
dec.ttl -= 1;
/* Find the output device */
out_dev = rcu_dereference(nh->nh_dev);
if (!mpls_output_possible(out_dev))
goto tx_err;
/* Verify the destination can hold the packet */
new_header_size = mpls_nh_header_size(nh);
mtu = mpls_dev_mtu(out_dev);
if (mpls_pkt_too_big(skb, mtu - new_header_size))
goto tx_err;
hh_len = LL_RESERVED_SPACE(out_dev);
if (!out_dev->header_ops)
hh_len = 0;
/* Ensure there is enough space for the headers in the skb */
if (skb_cow(skb, hh_len + new_header_size))
goto tx_err;
skb->dev = out_dev;
skb->protocol = htons(ETH_P_MPLS_UC);
if (unlikely(!new_header_size && dec.bos)) {
/* Penultimate hop popping */
if (!mpls_egress(rt, skb, dec))
goto err;
} else {
bool bos;
int i;
skb_push(skb, new_header_size);
skb_reset_network_header(skb);
/* Push the new labels */
hdr = mpls_hdr(skb);
bos = dec.bos;
for (i = nh->nh_labels - 1; i >= 0; i--) {
hdr[i] = mpls_entry_encode(nh->nh_label[i],
dec.ttl, 0, bos);
bos = false;
}
}
mpls_stats_inc_outucastpkts(out_dev, skb);
/* If via wasn't specified then send out using device address */
if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC)
err = neigh_xmit(NEIGH_LINK_TABLE, out_dev,
out_dev->dev_addr, skb);
else
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);
return 0;
tx_err:
out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL;
if (out_mdev)
MPLS_INC_STATS(out_mdev, tx_errors);
goto drop;
err:
MPLS_INC_STATS(mdev, rx_errors);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 386 | 62.56% | 2 | 20.00% |
Robert Shearman | 183 | 29.66% | 6 | 60.00% |
Roopa Prabhu | 26 | 4.21% | 1 | 10.00% |
David Ahern | 22 | 3.57% | 1 | 10.00% |
Total | 617 | 100.00% | 10 | 100.00% |
static struct packet_type mpls_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_MPLS_UC),
.func = mpls_forward,
};
static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = {
[RTA_DST] = { .type = NLA_U32 },
[RTA_OIF] = { .type = NLA_U32 },
};
struct mpls_route_config {
u32 rc_protocol;
u32 rc_ifindex;
u8 rc_via_table;
u8 rc_via_alen;
u8 rc_via[MAX_VIA_ALEN];
u32 rc_label;
u8 rc_output_labels;
u32 rc_output_label[MAX_NEW_LABELS];
u32 rc_nlflags;
enum mpls_payload_type rc_payload_type;
struct nl_info rc_nlinfo;
struct rtnexthop *rc_mp;
int rc_mp_len;
};
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(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_alive = num_nh;
rt->rt_max_alen = max_alen_aligned;
}
return rt;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 39 | 44.32% | 2 | 40.00% |
Robert Shearman | 34 | 38.64% | 1 | 20.00% |
Roopa Prabhu | 15 | 17.05% | 2 | 40.00% |
Total | 88 | 100.00% | 5 | 100.00% |
static void mpls_rt_free(struct mpls_route *rt)
{
if (rt)
kfree_rcu(rt, rt_rcu);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 22 | 100.00% | 1 | 100.00% |
Total | 22 | 100.00% | 1 | 100.00% |
static void mpls_notify_route(struct net *net, unsigned index,
struct mpls_route *old, struct mpls_route *new,
const struct nl_info *info)
{
struct nlmsghdr *nlh = info ? info->nlh : NULL;
unsigned portid = info ? info->portid : 0;
int event = new ? RTM_NEWROUTE : RTM_DELROUTE;
struct mpls_route *rt = new ? new : old;
unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0;
/* Ignore reserved labels for now */
if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED))
rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 114 | 99.13% | 1 | 50.00% |
Robert Shearman | 1 | 0.87% | 1 | 50.00% |
Total | 115 | 100.00% | 2 | 100.00% |
static void mpls_route_update(struct net *net, unsigned index,
struct mpls_route *new,
const struct nl_info *info)
{
struct mpls_route __rcu **platform_label;
struct mpls_route *rt;
ASSERT_RTNL();
platform_label = rtnl_dereference(net->mpls.platform_label);
rt = rtnl_dereference(platform_label[index]);
rcu_assign_pointer(platform_label[index], new);
mpls_notify_route(net, index, rt, new, info);
/* If we removed a route free it now */
mpls_rt_free(rt);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 88 | 97.78% | 3 | 75.00% |
Roopa Prabhu | 2 | 2.22% | 1 | 25.00% |
Total | 90 | 100.00% | 4 | 100.00% |
static unsigned find_free_label(struct net *net)
{
struct mpls_route __rcu **platform_label;
size_t platform_labels;
unsigned index;
platform_label = rtnl_dereference(net->mpls.platform_label);
platform_labels = net->mpls.platform_labels;
for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels;
index++) {
if (!rtnl_dereference(platform_label[index]))
return index;
}
return LABEL_NOT_SPECIFIED;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric W. Biedermann | 74 | 98.67% | 2 | 66.67% |
Robert Shearman | 1 | 1.33% | 1 | 33.33% |
Total | 75 | 100.00% | 3 | 100.00% |
#if IS_ENABLED(CONFIG_INET)
static struct net_device *inet_fib_lookup_dev(struct net *net,
const void *addr)
{
struct net_device *dev;
struct rtable *rt;
struct in_addr daddr;
memcpy(&daddr, addr, sizeof(struct in_addr));
rt = ip_route_output(net, daddr.s_addr, 0, 0, 0);
if (IS_ERR(rt))
return ERR_CAST(rt);
dev = rt->dst.dev;
dev_hold(dev);
ip_rt_put(rt);
return dev;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 90 | 92.78% | 2 | 50.00% |
Dan Carpenter | 6 | 6.19% | 1 | 25.00% |
Robert Shearman | 1 | 1.03% | 1 | 25.00% |
Total | 97 | 100.00% | 4 | 100.00% |
#else
static struct net_device *inet_fib_lookup_dev(struct net *net,
const void *addr)
{
return ERR_PTR(-EAFNOSUPPORT);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 24 | 96.00% | 1 | 50.00% |
Robert Shearman | 1 | 4.00% | 1 | 50.00% |
Total | 25 | 100.00% | 2 | 100.00% |
#endif
#if IS_ENABLED(CONFIG_IPV6)
static struct net_device *inet6_fib_lookup_dev(struct net *net,
const void *addr)
{
struct net_device *dev;
struct dst_entry *dst;
struct flowi6 fl6;
int err;
if (!ipv6_stub)
return ERR_PTR(-EAFNOSUPPORT);
memset(&fl6, 0, sizeof(fl6));
memcpy(&fl6.daddr, addr, sizeof(struct in6_addr));
err = ipv6_stub->ipv6_dst_lookup(net, NULL, &dst, &fl6);
if (err)
return ERR_PTR(err);
dev = dst->dev;
dev_hold(dev);
dst_release(dst);
return dev;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 115 | 94.26% | 2 | 50.00% |
Dan Carpenter | 6 | 4.92% | 1 | 25.00% |
Robert Shearman | 1 | 0.82% | 1 | 25.00% |
Total | 122 | 100.00% | 4 | 100.00% |
#else
static struct net_device *inet6_fib_lookup_dev(struct net *net,
const void *addr)
{
return ERR_PTR(-EAFNOSUPPORT);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 24 | 96.00% | 2 | 66.67% |
Robert Shearman | 1 | 4.00% | 1 | 33.33% |
Total | 25 | 100.00% | 3 | 100.00% |
#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, mpls_nh_via(rt, nh));
break;
case NEIGH_ND_TABLE:
dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh));
break;
case NEIGH_LINK_TABLE:
break;
}
} else {
dev = dev_get_by_index(net, oif);
}
if (!dev)
return ERR_PTR(-ENODEV);
if (IS_ERR(dev))
return dev;
/* The caller is holding rtnl anyways, so release the dev reference */
dev_put(dev);
return dev;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 116 | 88.55% | 4 | 80.00% |
Robert Shearman | 15 | 11.45% | 1 | 20.00% |
Total | 131 | 100.00% | 5 | 100.00% |
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, rt, nh, oif);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
dev = NULL;
goto errout;
}
/* Ensure this is a supported device */
err = -EINVAL;
if (!mpls_dev_get(dev))
goto errout;
if ((nh->nh_via_table == NEIGH_LINK_TABLE) &&
(dev->addr_len != nh->nh_via_alen))
goto errout;
RCU_INIT_POINTER(nh->nh_dev, dev);
if (!(dev->flags & IFF_UP)) {
nh->nh_flags |= RTNH_F_DEAD;
} else {
unsigned int flags;
flags = dev_get_flags(dev);
if (!(flags & (IFF_RUNNING | IFF_LOWER_UP)))
nh->nh_flags |= RTNH_F_LINKDOWN;
}
return 0;
errout:
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 129 | 70.88% | 2 | 40.00% |
Robert Shearman | 30 | 16.48% | 2 | 40.00% |
Eric W. Biedermann | 23 | 12.64% | 1 | 20.00% |
Total | 182 | 100.00% | 5 | 100.00% |
static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
struct mpls_route *rt)
{
struct net *net = cfg->rc_nlinfo.nl_net;
struct mpls_nh *nh = rt->rt_nh;
int err;
int i;
if (!nh)
return -ENOMEM;
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;
for (i = 0; i < nh->nh_labels; i++)
nh->nh_label[i] = cfg->rc_output_label[i];
nh->nh_via_table = cfg->rc_via_table;
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, rt, nh, cfg->rc_ifindex);
if (err)
goto errout;
if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
rt->rt_nhn_alive--;
return 0;
errout:
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Roopa Prabhu | 177 | 95.16% | 2 | 50.00% |
Robert Shearman | 7 | 3.76% | 1 | 25.00% |
Eric W. Biedermann | 2 | 1.08% | 1 | 25.00% |
Total | 186 | 100.00% | 4 | 100.00% |
static int mpls_nh_build(struct net *net,