10189197fSEric W. Biederman #include <linux/types.h> 20189197fSEric W. Biederman #include <linux/skbuff.h> 30189197fSEric W. Biederman #include <linux/socket.h> 47720c01fSEric W. Biederman #include <linux/sysctl.h> 50189197fSEric W. Biederman #include <linux/net.h> 60189197fSEric W. Biederman #include <linux/module.h> 70189197fSEric W. Biederman #include <linux/if_arp.h> 80189197fSEric W. Biederman #include <linux/ipv6.h> 90189197fSEric W. Biederman #include <linux/mpls.h> 1024045a03SDavid Ahern #include <linux/netconf.h> 114b5edb2fSStephen Rothwell #include <linux/vmalloc.h> 1227d69105SRobert Shearman #include <linux/percpu.h> 130189197fSEric W. Biederman #include <net/ip.h> 140189197fSEric W. Biederman #include <net/dst.h> 150189197fSEric W. Biederman #include <net/sock.h> 160189197fSEric W. Biederman #include <net/arp.h> 170189197fSEric W. Biederman #include <net/ip_fib.h> 180189197fSEric W. Biederman #include <net/netevent.h> 190189197fSEric W. Biederman #include <net/netns/generic.h> 20bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_IPV6) 21bf21563aSRoopa Prabhu #include <net/ipv6.h> 22bf21563aSRoopa Prabhu #endif 2327d69105SRobert Shearman #include <net/addrconf.h> 24f8efb73cSRoopa Prabhu #include <net/nexthop.h> 250189197fSEric W. Biederman #include "internal.h" 260189197fSEric W. Biederman 27df1c6316SDavid Ahern /* max memory we will use for mpls_route */ 28df1c6316SDavid Ahern #define MAX_MPLS_ROUTE_MEM 4096 29df1c6316SDavid Ahern 301c78efa8SRobert Shearman /* Maximum number of labels to look ahead at when selecting a path of 311c78efa8SRobert Shearman * a multipath route 321c78efa8SRobert Shearman */ 331c78efa8SRobert Shearman #define MAX_MP_SELECT_LABELS 4 341c78efa8SRobert Shearman 35eb7809f0SRobert Shearman #define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1) 36eb7809f0SRobert Shearman 377720c01fSEric W. Biederman static int zero = 0; 385b441ac8SRobert Shearman static int one = 1; 397720c01fSEric W. Biederman static int label_limit = (1 << 20) - 1; 40a59166e4SRobert Shearman static int ttl_max = 255; 417720c01fSEric W. Biederman 428de147dcSEric W. Biederman static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, 438de147dcSEric W. Biederman struct nlmsghdr *nlh, struct net *net, u32 portid, 448de147dcSEric W. Biederman unsigned int nlm_flags); 458de147dcSEric W. Biederman 460189197fSEric W. Biederman static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) 470189197fSEric W. Biederman { 480189197fSEric W. Biederman struct mpls_route *rt = NULL; 490189197fSEric W. Biederman 500189197fSEric W. Biederman if (index < net->mpls.platform_labels) { 510189197fSEric W. Biederman struct mpls_route __rcu **platform_label = 520189197fSEric W. Biederman rcu_dereference(net->mpls.platform_label); 530189197fSEric W. Biederman rt = rcu_dereference(platform_label[index]); 540189197fSEric W. Biederman } 550189197fSEric W. Biederman return rt; 560189197fSEric W. Biederman } 570189197fSEric W. Biederman 58face0188SRoopa Prabhu bool mpls_output_possible(const struct net_device *dev) 590189197fSEric W. Biederman { 600189197fSEric W. Biederman return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); 610189197fSEric W. Biederman } 62face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_output_possible); 630189197fSEric W. Biederman 64cf4b24f0SRobert Shearman static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh) 65cf4b24f0SRobert Shearman { 6659b20966SDavid Ahern return (u8 *)nh + rt->rt_via_offset; 67cf4b24f0SRobert Shearman } 68cf4b24f0SRobert Shearman 69cf4b24f0SRobert Shearman static const u8 *mpls_nh_via(const struct mpls_route *rt, 70cf4b24f0SRobert Shearman const struct mpls_nh *nh) 71cf4b24f0SRobert Shearman { 72cf4b24f0SRobert Shearman return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh); 73cf4b24f0SRobert Shearman } 74cf4b24f0SRobert Shearman 75f8efb73cSRoopa Prabhu static unsigned int mpls_nh_header_size(const struct mpls_nh *nh) 760189197fSEric W. Biederman { 770189197fSEric W. Biederman /* The size of the layer 2.5 labels to be added for this route */ 78f8efb73cSRoopa Prabhu return nh->nh_labels * sizeof(struct mpls_shim_hdr); 790189197fSEric W. Biederman } 800189197fSEric W. Biederman 81face0188SRoopa Prabhu unsigned int mpls_dev_mtu(const struct net_device *dev) 820189197fSEric W. Biederman { 830189197fSEric W. Biederman /* The amount of data the layer 2 frame can hold */ 840189197fSEric W. Biederman return dev->mtu; 850189197fSEric W. Biederman } 86face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_dev_mtu); 870189197fSEric W. Biederman 88face0188SRoopa Prabhu bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) 890189197fSEric W. Biederman { 900189197fSEric W. Biederman if (skb->len <= mtu) 910189197fSEric W. Biederman return false; 920189197fSEric W. Biederman 93ae7ef81eSMarcelo Ricardo Leitner if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu)) 940189197fSEric W. Biederman return false; 950189197fSEric W. Biederman 960189197fSEric W. Biederman return true; 970189197fSEric W. Biederman } 98face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_pkt_too_big); 990189197fSEric W. Biederman 10027d69105SRobert Shearman void mpls_stats_inc_outucastpkts(struct net_device *dev, 10127d69105SRobert Shearman const struct sk_buff *skb) 10227d69105SRobert Shearman { 10327d69105SRobert Shearman struct mpls_dev *mdev; 10427d69105SRobert Shearman 10527d69105SRobert Shearman if (skb->protocol == htons(ETH_P_MPLS_UC)) { 10627d69105SRobert Shearman mdev = mpls_dev_get(dev); 10727d69105SRobert Shearman if (mdev) 10827d69105SRobert Shearman MPLS_INC_STATS_LEN(mdev, skb->len, 10927d69105SRobert Shearman tx_packets, 11027d69105SRobert Shearman tx_bytes); 11127d69105SRobert Shearman } else if (skb->protocol == htons(ETH_P_IP)) { 11227d69105SRobert Shearman IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len); 11327d69105SRobert Shearman #if IS_ENABLED(CONFIG_IPV6) 11427d69105SRobert Shearman } else if (skb->protocol == htons(ETH_P_IPV6)) { 11527d69105SRobert Shearman struct inet6_dev *in6dev = __in6_dev_get(dev); 11627d69105SRobert Shearman 11727d69105SRobert Shearman if (in6dev) 11827d69105SRobert Shearman IP6_UPD_PO_STATS(dev_net(dev), in6dev, 11927d69105SRobert Shearman IPSTATS_MIB_OUT, skb->len); 12027d69105SRobert Shearman #endif 12127d69105SRobert Shearman } 12227d69105SRobert Shearman } 12327d69105SRobert Shearman EXPORT_SYMBOL_GPL(mpls_stats_inc_outucastpkts); 12427d69105SRobert Shearman 1259f427a0eSDavid Ahern static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) 126f8efb73cSRoopa Prabhu { 1271c78efa8SRobert Shearman struct mpls_entry_decoded dec; 1289f427a0eSDavid Ahern unsigned int mpls_hdr_len = 0; 1291c78efa8SRobert Shearman struct mpls_shim_hdr *hdr; 1301c78efa8SRobert Shearman bool eli_seen = false; 1311c78efa8SRobert Shearman int label_index; 1321c78efa8SRobert Shearman u32 hash = 0; 1331c78efa8SRobert Shearman 1349f427a0eSDavid Ahern for (label_index = 0; label_index < MAX_MP_SELECT_LABELS; 1351c78efa8SRobert Shearman label_index++) { 1369f427a0eSDavid Ahern mpls_hdr_len += sizeof(*hdr); 1379f427a0eSDavid Ahern if (!pskb_may_pull(skb, mpls_hdr_len)) 1381c78efa8SRobert Shearman break; 1391c78efa8SRobert Shearman 1401c78efa8SRobert Shearman /* Read and decode the current label */ 1411c78efa8SRobert Shearman hdr = mpls_hdr(skb) + label_index; 1421c78efa8SRobert Shearman dec = mpls_entry_decode(hdr); 1431c78efa8SRobert Shearman 1441c78efa8SRobert Shearman /* RFC6790 - reserved labels MUST NOT be used as keys 1451c78efa8SRobert Shearman * for the load-balancing function 1461c78efa8SRobert Shearman */ 1471c78efa8SRobert Shearman if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) { 1481c78efa8SRobert Shearman hash = jhash_1word(dec.label, hash); 1491c78efa8SRobert Shearman 1501c78efa8SRobert Shearman /* The entropy label follows the entropy label 1511c78efa8SRobert Shearman * indicator, so this means that the entropy 1521c78efa8SRobert Shearman * label was just added to the hash - no need to 1531c78efa8SRobert Shearman * go any deeper either in the label stack or in the 1541c78efa8SRobert Shearman * payload 1551c78efa8SRobert Shearman */ 1561c78efa8SRobert Shearman if (eli_seen) 1571c78efa8SRobert Shearman break; 1581c78efa8SRobert Shearman } else if (dec.label == MPLS_LABEL_ENTROPY) { 1591c78efa8SRobert Shearman eli_seen = true; 1601c78efa8SRobert Shearman } 1611c78efa8SRobert Shearman 1629f427a0eSDavid Ahern if (!dec.bos) 1639f427a0eSDavid Ahern continue; 1649f427a0eSDavid Ahern 1659f427a0eSDavid Ahern /* found bottom label; does skb have room for a header? */ 1669f427a0eSDavid Ahern if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) { 1671c78efa8SRobert Shearman const struct iphdr *v4hdr; 1681c78efa8SRobert Shearman 1699f427a0eSDavid Ahern v4hdr = (const struct iphdr *)(hdr + 1); 1701c78efa8SRobert Shearman if (v4hdr->version == 4) { 1711c78efa8SRobert Shearman hash = jhash_3words(ntohl(v4hdr->saddr), 1721c78efa8SRobert Shearman ntohl(v4hdr->daddr), 1731c78efa8SRobert Shearman v4hdr->protocol, hash); 1741c78efa8SRobert Shearman } else if (v4hdr->version == 6 && 1759f427a0eSDavid Ahern pskb_may_pull(skb, mpls_hdr_len + 1761c78efa8SRobert Shearman sizeof(struct ipv6hdr))) { 1771c78efa8SRobert Shearman const struct ipv6hdr *v6hdr; 1781c78efa8SRobert Shearman 1799f427a0eSDavid Ahern v6hdr = (const struct ipv6hdr *)(hdr + 1); 1801c78efa8SRobert Shearman hash = __ipv6_addr_jhash(&v6hdr->saddr, hash); 1811c78efa8SRobert Shearman hash = __ipv6_addr_jhash(&v6hdr->daddr, hash); 1821c78efa8SRobert Shearman hash = jhash_1word(v6hdr->nexthdr, hash); 1831c78efa8SRobert Shearman } 1841c78efa8SRobert Shearman } 1859f427a0eSDavid Ahern 1869f427a0eSDavid Ahern break; 1871c78efa8SRobert Shearman } 1881c78efa8SRobert Shearman 189c89359a4SRoopa Prabhu return hash; 190c89359a4SRoopa Prabhu } 191c89359a4SRoopa Prabhu 19259b20966SDavid Ahern static struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index) 19359b20966SDavid Ahern { 19459b20966SDavid Ahern return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size); 19559b20966SDavid Ahern } 19659b20966SDavid Ahern 19739eb8cd1SDavid Ahern /* number of alive nexthops (rt->rt_nhn_alive) and the flags for 19839eb8cd1SDavid Ahern * a next hop (nh->nh_flags) are modified by netdev event handlers. 19939eb8cd1SDavid Ahern * Since those fields can change at any moment, use READ_ONCE to 20039eb8cd1SDavid Ahern * access both. 20139eb8cd1SDavid Ahern */ 202c89359a4SRoopa Prabhu static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, 2039f427a0eSDavid Ahern struct sk_buff *skb) 204c89359a4SRoopa Prabhu { 205c89359a4SRoopa Prabhu u32 hash = 0; 206c89359a4SRoopa Prabhu int nh_index = 0; 207c89359a4SRoopa Prabhu int n = 0; 20877ef013aSDavid Ahern u8 alive; 209c89359a4SRoopa Prabhu 210c89359a4SRoopa Prabhu /* No need to look further into packet if there's only 211c89359a4SRoopa Prabhu * one path 212c89359a4SRoopa Prabhu */ 213c89359a4SRoopa Prabhu if (rt->rt_nhn == 1) 21459b20966SDavid Ahern return rt->rt_nh; 215c89359a4SRoopa Prabhu 21639eb8cd1SDavid Ahern alive = READ_ONCE(rt->rt_nhn_alive); 21739eb8cd1SDavid Ahern if (alive == 0) 218c89359a4SRoopa Prabhu return NULL; 219c89359a4SRoopa Prabhu 2209f427a0eSDavid Ahern hash = mpls_multipath_hash(rt, skb); 221c89359a4SRoopa Prabhu nh_index = hash % alive; 222c89359a4SRoopa Prabhu if (alive == rt->rt_nhn) 223c89359a4SRoopa Prabhu goto out; 224c89359a4SRoopa Prabhu for_nexthops(rt) { 22539eb8cd1SDavid Ahern unsigned int nh_flags = READ_ONCE(nh->nh_flags); 22639eb8cd1SDavid Ahern 22739eb8cd1SDavid Ahern if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 228c89359a4SRoopa Prabhu continue; 229c89359a4SRoopa Prabhu if (n == nh_index) 230c89359a4SRoopa Prabhu return nh; 231c89359a4SRoopa Prabhu n++; 232c89359a4SRoopa Prabhu } endfor_nexthops(rt); 233c89359a4SRoopa Prabhu 2341c78efa8SRobert Shearman out: 23559b20966SDavid Ahern return mpls_get_nexthop(rt, nh_index); 236f8efb73cSRoopa Prabhu } 237f8efb73cSRoopa Prabhu 2385b441ac8SRobert Shearman static bool mpls_egress(struct net *net, struct mpls_route *rt, 2395b441ac8SRobert Shearman struct sk_buff *skb, struct mpls_entry_decoded dec) 2400189197fSEric W. Biederman { 241118d5234SRobert Shearman enum mpls_payload_type payload_type; 242118d5234SRobert Shearman bool success = false; 2430189197fSEric W. Biederman 24476fecd82SEric W. Biederman /* The IPv4 code below accesses through the IPv4 header 24576fecd82SEric W. Biederman * checksum, which is 12 bytes into the packet. 24676fecd82SEric W. Biederman * The IPv6 code below accesses through the IPv6 hop limit 24776fecd82SEric W. Biederman * which is 8 bytes into the packet. 24876fecd82SEric W. Biederman * 24976fecd82SEric W. Biederman * For all supported cases there should always be at least 12 25076fecd82SEric W. Biederman * bytes of packet data present. The IPv4 header is 20 bytes 25176fecd82SEric W. Biederman * without options and the IPv6 header is always 40 bytes 25276fecd82SEric W. Biederman * long. 25376fecd82SEric W. Biederman */ 25476fecd82SEric W. Biederman if (!pskb_may_pull(skb, 12)) 25576fecd82SEric W. Biederman return false; 25676fecd82SEric W. Biederman 257118d5234SRobert Shearman payload_type = rt->rt_payload_type; 258118d5234SRobert Shearman if (payload_type == MPT_UNSPEC) 259118d5234SRobert Shearman payload_type = ip_hdr(skb)->version; 260118d5234SRobert Shearman 261118d5234SRobert Shearman switch (payload_type) { 262118d5234SRobert Shearman case MPT_IPV4: { 263118d5234SRobert Shearman struct iphdr *hdr4 = ip_hdr(skb); 2645b441ac8SRobert Shearman u8 new_ttl; 2650189197fSEric W. Biederman skb->protocol = htons(ETH_P_IP); 2665b441ac8SRobert Shearman 2675b441ac8SRobert Shearman /* If propagating TTL, take the decremented TTL from 2685b441ac8SRobert Shearman * the incoming MPLS header, otherwise decrement the 2695b441ac8SRobert Shearman * TTL, but only if not 0 to avoid underflow. 2705b441ac8SRobert Shearman */ 2715b441ac8SRobert Shearman if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || 2725b441ac8SRobert Shearman (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && 2735b441ac8SRobert Shearman net->mpls.ip_ttl_propagate)) 2745b441ac8SRobert Shearman new_ttl = dec.ttl; 2755b441ac8SRobert Shearman else 2765b441ac8SRobert Shearman new_ttl = hdr4->ttl ? hdr4->ttl - 1 : 0; 2775b441ac8SRobert Shearman 2780189197fSEric W. Biederman csum_replace2(&hdr4->check, 2790189197fSEric W. Biederman htons(hdr4->ttl << 8), 2805b441ac8SRobert Shearman htons(new_ttl << 8)); 2815b441ac8SRobert Shearman hdr4->ttl = new_ttl; 282118d5234SRobert Shearman success = true; 283118d5234SRobert Shearman break; 2840189197fSEric W. Biederman } 285118d5234SRobert Shearman case MPT_IPV6: { 2860189197fSEric W. Biederman struct ipv6hdr *hdr6 = ipv6_hdr(skb); 2870189197fSEric W. Biederman skb->protocol = htons(ETH_P_IPV6); 2885b441ac8SRobert Shearman 2895b441ac8SRobert Shearman /* If propagating TTL, take the decremented TTL from 2905b441ac8SRobert Shearman * the incoming MPLS header, otherwise decrement the 2915b441ac8SRobert Shearman * hop limit, but only if not 0 to avoid underflow. 2925b441ac8SRobert Shearman */ 2935b441ac8SRobert Shearman if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || 2945b441ac8SRobert Shearman (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && 2955b441ac8SRobert Shearman net->mpls.ip_ttl_propagate)) 2960189197fSEric W. Biederman hdr6->hop_limit = dec.ttl; 2975b441ac8SRobert Shearman else if (hdr6->hop_limit) 2985b441ac8SRobert Shearman hdr6->hop_limit = hdr6->hop_limit - 1; 299118d5234SRobert Shearman success = true; 300118d5234SRobert Shearman break; 3010189197fSEric W. Biederman } 302118d5234SRobert Shearman case MPT_UNSPEC: 3035b441ac8SRobert Shearman /* Should have decided which protocol it is by now */ 304118d5234SRobert Shearman break; 305118d5234SRobert Shearman } 306118d5234SRobert Shearman 3070189197fSEric W. Biederman return success; 3080189197fSEric W. Biederman } 3090189197fSEric W. Biederman 3100189197fSEric W. Biederman static int mpls_forward(struct sk_buff *skb, struct net_device *dev, 3110189197fSEric W. Biederman struct packet_type *pt, struct net_device *orig_dev) 3120189197fSEric W. Biederman { 3130189197fSEric W. Biederman struct net *net = dev_net(dev); 3140189197fSEric W. Biederman struct mpls_shim_hdr *hdr; 3150189197fSEric W. Biederman struct mpls_route *rt; 316f8efb73cSRoopa Prabhu struct mpls_nh *nh; 3170189197fSEric W. Biederman struct mpls_entry_decoded dec; 3180189197fSEric W. Biederman struct net_device *out_dev; 31927d69105SRobert Shearman struct mpls_dev *out_mdev; 32003c57747SRobert Shearman struct mpls_dev *mdev; 3210189197fSEric W. Biederman unsigned int hh_len; 3220189197fSEric W. Biederman unsigned int new_header_size; 3230189197fSEric W. Biederman unsigned int mtu; 3240189197fSEric W. Biederman int err; 3250189197fSEric W. Biederman 3260189197fSEric W. Biederman /* Careful this entire function runs inside of an rcu critical section */ 3270189197fSEric W. Biederman 32803c57747SRobert Shearman mdev = mpls_dev_get(dev); 32927d69105SRobert Shearman if (!mdev) 33003c57747SRobert Shearman goto drop; 33103c57747SRobert Shearman 33227d69105SRobert Shearman MPLS_INC_STATS_LEN(mdev, skb->len, rx_packets, 33327d69105SRobert Shearman rx_bytes); 33427d69105SRobert Shearman 33527d69105SRobert Shearman if (!mdev->input_enabled) { 33627d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_dropped); 33727d69105SRobert Shearman goto drop; 33827d69105SRobert Shearman } 33927d69105SRobert Shearman 3400189197fSEric W. Biederman if (skb->pkt_type != PACKET_HOST) 34127d69105SRobert Shearman goto err; 3420189197fSEric W. Biederman 3430189197fSEric W. Biederman if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) 34427d69105SRobert Shearman goto err; 3450189197fSEric W. Biederman 3460189197fSEric W. Biederman if (!pskb_may_pull(skb, sizeof(*hdr))) 34727d69105SRobert Shearman goto err; 3480189197fSEric W. Biederman 3490189197fSEric W. Biederman /* Read and decode the label */ 3500189197fSEric W. Biederman hdr = mpls_hdr(skb); 3510189197fSEric W. Biederman dec = mpls_entry_decode(hdr); 3520189197fSEric W. Biederman 3530189197fSEric W. Biederman rt = mpls_route_input_rcu(net, dec.label); 35427d69105SRobert Shearman if (!rt) { 35527d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_noroute); 3560189197fSEric W. Biederman goto drop; 35727d69105SRobert Shearman } 3580189197fSEric W. Biederman 3599f427a0eSDavid Ahern nh = mpls_select_multipath(rt, skb); 360f8efb73cSRoopa Prabhu if (!nh) 36127d69105SRobert Shearman goto err; 3620189197fSEric W. Biederman 3639f427a0eSDavid Ahern /* Pop the label */ 3649f427a0eSDavid Ahern skb_pull(skb, sizeof(*hdr)); 3659f427a0eSDavid Ahern skb_reset_network_header(skb); 3669f427a0eSDavid Ahern 3679f427a0eSDavid Ahern skb_orphan(skb); 3689f427a0eSDavid Ahern 3690189197fSEric W. Biederman if (skb_warn_if_lro(skb)) 37027d69105SRobert Shearman goto err; 3710189197fSEric W. Biederman 3720189197fSEric W. Biederman skb_forward_csum(skb); 3730189197fSEric W. Biederman 3740189197fSEric W. Biederman /* Verify ttl is valid */ 375aa7da937SEric W. Biederman if (dec.ttl <= 1) 37627d69105SRobert Shearman goto err; 3770189197fSEric W. Biederman dec.ttl -= 1; 3780189197fSEric W. Biederman 37927d69105SRobert Shearman /* Find the output device */ 38027d69105SRobert Shearman out_dev = rcu_dereference(nh->nh_dev); 38127d69105SRobert Shearman if (!mpls_output_possible(out_dev)) 38227d69105SRobert Shearman goto tx_err; 38327d69105SRobert Shearman 3840189197fSEric W. Biederman /* Verify the destination can hold the packet */ 385f8efb73cSRoopa Prabhu new_header_size = mpls_nh_header_size(nh); 3860189197fSEric W. Biederman mtu = mpls_dev_mtu(out_dev); 3870189197fSEric W. Biederman if (mpls_pkt_too_big(skb, mtu - new_header_size)) 38827d69105SRobert Shearman goto tx_err; 3890189197fSEric W. Biederman 3900189197fSEric W. Biederman hh_len = LL_RESERVED_SPACE(out_dev); 3910189197fSEric W. Biederman if (!out_dev->header_ops) 3920189197fSEric W. Biederman hh_len = 0; 3930189197fSEric W. Biederman 3940189197fSEric W. Biederman /* Ensure there is enough space for the headers in the skb */ 3950189197fSEric W. Biederman if (skb_cow(skb, hh_len + new_header_size)) 39627d69105SRobert Shearman goto tx_err; 3970189197fSEric W. Biederman 3980189197fSEric W. Biederman skb->dev = out_dev; 3990189197fSEric W. Biederman skb->protocol = htons(ETH_P_MPLS_UC); 4000189197fSEric W. Biederman 4010189197fSEric W. Biederman if (unlikely(!new_header_size && dec.bos)) { 4020189197fSEric W. Biederman /* Penultimate hop popping */ 4035b441ac8SRobert Shearman if (!mpls_egress(dev_net(out_dev), rt, skb, dec)) 40427d69105SRobert Shearman goto err; 4050189197fSEric W. Biederman } else { 4060189197fSEric W. Biederman bool bos; 4070189197fSEric W. Biederman int i; 4080189197fSEric W. Biederman skb_push(skb, new_header_size); 4090189197fSEric W. Biederman skb_reset_network_header(skb); 4100189197fSEric W. Biederman /* Push the new labels */ 4110189197fSEric W. Biederman hdr = mpls_hdr(skb); 4120189197fSEric W. Biederman bos = dec.bos; 413f8efb73cSRoopa Prabhu for (i = nh->nh_labels - 1; i >= 0; i--) { 414f8efb73cSRoopa Prabhu hdr[i] = mpls_entry_encode(nh->nh_label[i], 415f8efb73cSRoopa Prabhu dec.ttl, 0, bos); 4160189197fSEric W. Biederman bos = false; 4170189197fSEric W. Biederman } 4180189197fSEric W. Biederman } 4190189197fSEric W. Biederman 42027d69105SRobert Shearman mpls_stats_inc_outucastpkts(out_dev, skb); 42127d69105SRobert Shearman 422eb7809f0SRobert Shearman /* If via wasn't specified then send out using device address */ 423eb7809f0SRobert Shearman if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC) 424eb7809f0SRobert Shearman err = neigh_xmit(NEIGH_LINK_TABLE, out_dev, 425eb7809f0SRobert Shearman out_dev->dev_addr, skb); 426eb7809f0SRobert Shearman else 427eb7809f0SRobert Shearman err = neigh_xmit(nh->nh_via_table, out_dev, 428eb7809f0SRobert Shearman mpls_nh_via(rt, nh), skb); 4290189197fSEric W. Biederman if (err) 4300189197fSEric W. Biederman net_dbg_ratelimited("%s: packet transmission failed: %d\n", 4310189197fSEric W. Biederman __func__, err); 4320189197fSEric W. Biederman return 0; 4330189197fSEric W. Biederman 43427d69105SRobert Shearman tx_err: 43527d69105SRobert Shearman out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL; 43627d69105SRobert Shearman if (out_mdev) 43727d69105SRobert Shearman MPLS_INC_STATS(out_mdev, tx_errors); 43827d69105SRobert Shearman goto drop; 43927d69105SRobert Shearman err: 44027d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_errors); 4410189197fSEric W. Biederman drop: 4420189197fSEric W. Biederman kfree_skb(skb); 4430189197fSEric W. Biederman return NET_RX_DROP; 4440189197fSEric W. Biederman } 4450189197fSEric W. Biederman 4460189197fSEric W. Biederman static struct packet_type mpls_packet_type __read_mostly = { 4470189197fSEric W. Biederman .type = cpu_to_be16(ETH_P_MPLS_UC), 4480189197fSEric W. Biederman .func = mpls_forward, 4490189197fSEric W. Biederman }; 4500189197fSEric W. Biederman 451f0126539SWu Fengguang static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { 45203c05665SEric W. Biederman [RTA_DST] = { .type = NLA_U32 }, 45303c05665SEric W. Biederman [RTA_OIF] = { .type = NLA_U32 }, 4545b441ac8SRobert Shearman [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, 45503c05665SEric W. Biederman }; 45603c05665SEric W. Biederman 457a2519929SEric W. Biederman struct mpls_route_config { 458a2519929SEric W. Biederman u32 rc_protocol; 459a2519929SEric W. Biederman u32 rc_ifindex; 460f8efb73cSRoopa Prabhu u8 rc_via_table; 461f8efb73cSRoopa Prabhu u8 rc_via_alen; 462a2519929SEric W. Biederman u8 rc_via[MAX_VIA_ALEN]; 463a2519929SEric W. Biederman u32 rc_label; 4645b441ac8SRobert Shearman u8 rc_ttl_propagate; 465f8efb73cSRoopa Prabhu u8 rc_output_labels; 466a2519929SEric W. Biederman u32 rc_output_label[MAX_NEW_LABELS]; 467a2519929SEric W. Biederman u32 rc_nlflags; 468118d5234SRobert Shearman enum mpls_payload_type rc_payload_type; 469a2519929SEric W. Biederman struct nl_info rc_nlinfo; 470f8efb73cSRoopa Prabhu struct rtnexthop *rc_mp; 471f8efb73cSRoopa Prabhu int rc_mp_len; 472a2519929SEric W. Biederman }; 473a2519929SEric W. Biederman 47459b20966SDavid Ahern /* all nexthops within a route have the same size based on max 47559b20966SDavid Ahern * number of labels and max via length for a hop 47659b20966SDavid Ahern */ 47759b20966SDavid Ahern static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels) 4780189197fSEric W. Biederman { 47959b20966SDavid Ahern u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen); 4800189197fSEric W. Biederman struct mpls_route *rt; 481df1c6316SDavid Ahern size_t size; 4820189197fSEric W. Biederman 483df1c6316SDavid Ahern size = sizeof(*rt) + num_nh * nh_size; 484df1c6316SDavid Ahern if (size > MAX_MPLS_ROUTE_MEM) 485df1c6316SDavid Ahern return ERR_PTR(-EINVAL); 486df1c6316SDavid Ahern 487df1c6316SDavid Ahern rt = kzalloc(size, GFP_KERNEL); 488df1c6316SDavid Ahern if (!rt) 489df1c6316SDavid Ahern return ERR_PTR(-ENOMEM); 490df1c6316SDavid Ahern 491f8efb73cSRoopa Prabhu rt->rt_nhn = num_nh; 492c89359a4SRoopa Prabhu rt->rt_nhn_alive = num_nh; 49359b20966SDavid Ahern rt->rt_nh_size = nh_size; 49459b20966SDavid Ahern rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); 495f8efb73cSRoopa Prabhu 4960189197fSEric W. Biederman return rt; 4970189197fSEric W. Biederman } 4980189197fSEric W. Biederman 4990189197fSEric W. Biederman static void mpls_rt_free(struct mpls_route *rt) 5000189197fSEric W. Biederman { 5010189197fSEric W. Biederman if (rt) 5020189197fSEric W. Biederman kfree_rcu(rt, rt_rcu); 5030189197fSEric W. Biederman } 5040189197fSEric W. Biederman 5058de147dcSEric W. Biederman static void mpls_notify_route(struct net *net, unsigned index, 5068de147dcSEric W. Biederman struct mpls_route *old, struct mpls_route *new, 5078de147dcSEric W. Biederman const struct nl_info *info) 5088de147dcSEric W. Biederman { 5098de147dcSEric W. Biederman struct nlmsghdr *nlh = info ? info->nlh : NULL; 5108de147dcSEric W. Biederman unsigned portid = info ? info->portid : 0; 5118de147dcSEric W. Biederman int event = new ? RTM_NEWROUTE : RTM_DELROUTE; 5128de147dcSEric W. Biederman struct mpls_route *rt = new ? new : old; 5138de147dcSEric W. Biederman unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; 5148de147dcSEric W. Biederman /* Ignore reserved labels for now */ 515a6affd24SRobert Shearman if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED)) 5168de147dcSEric W. Biederman rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); 5178de147dcSEric W. Biederman } 5188de147dcSEric W. Biederman 5190189197fSEric W. Biederman static void mpls_route_update(struct net *net, unsigned index, 520f8efb73cSRoopa Prabhu struct mpls_route *new, 5210189197fSEric W. Biederman const struct nl_info *info) 5220189197fSEric W. Biederman { 52319d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 524f8efb73cSRoopa Prabhu struct mpls_route *rt; 5250189197fSEric W. Biederman 5260189197fSEric W. Biederman ASSERT_RTNL(); 5270189197fSEric W. Biederman 52819d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 52919d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 53019d0c341SEric W. Biederman rcu_assign_pointer(platform_label[index], new); 5310189197fSEric W. Biederman 532f8efb73cSRoopa Prabhu mpls_notify_route(net, index, rt, new, info); 5338de147dcSEric W. Biederman 5340189197fSEric W. Biederman /* If we removed a route free it now */ 535f8efb73cSRoopa Prabhu mpls_rt_free(rt); 5360189197fSEric W. Biederman } 5370189197fSEric W. Biederman 538a2519929SEric W. Biederman static unsigned find_free_label(struct net *net) 539a2519929SEric W. Biederman { 54019d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 54119d0c341SEric W. Biederman size_t platform_labels; 542a2519929SEric W. Biederman unsigned index; 54319d0c341SEric W. Biederman 54419d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 54519d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 546a6affd24SRobert Shearman for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels; 547a6affd24SRobert Shearman index++) { 54819d0c341SEric W. Biederman if (!rtnl_dereference(platform_label[index])) 549a2519929SEric W. Biederman return index; 550a2519929SEric W. Biederman } 551a2519929SEric W. Biederman return LABEL_NOT_SPECIFIED; 552a2519929SEric W. Biederman } 553a2519929SEric W. Biederman 554bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_INET) 555cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 556cf4b24f0SRobert Shearman const void *addr) 55701faef2cSRoopa Prabhu { 5585a9348b5SDan Carpenter struct net_device *dev; 55901faef2cSRoopa Prabhu struct rtable *rt; 56001faef2cSRoopa Prabhu struct in_addr daddr; 56101faef2cSRoopa Prabhu 56201faef2cSRoopa Prabhu memcpy(&daddr, addr, sizeof(struct in_addr)); 56301faef2cSRoopa Prabhu rt = ip_route_output(net, daddr.s_addr, 0, 0, 0); 56401faef2cSRoopa Prabhu if (IS_ERR(rt)) 5655a9348b5SDan Carpenter return ERR_CAST(rt); 56601faef2cSRoopa Prabhu 56701faef2cSRoopa Prabhu dev = rt->dst.dev; 56801faef2cSRoopa Prabhu dev_hold(dev); 56901faef2cSRoopa Prabhu 57001faef2cSRoopa Prabhu ip_rt_put(rt); 57101faef2cSRoopa Prabhu 57201faef2cSRoopa Prabhu return dev; 57301faef2cSRoopa Prabhu } 574bf21563aSRoopa Prabhu #else 575cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 576cf4b24f0SRobert Shearman const void *addr) 577bf21563aSRoopa Prabhu { 578bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 579bf21563aSRoopa Prabhu } 580bf21563aSRoopa Prabhu #endif 58101faef2cSRoopa Prabhu 582bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_IPV6) 583cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 584cf4b24f0SRobert Shearman const void *addr) 58501faef2cSRoopa Prabhu { 5865a9348b5SDan Carpenter struct net_device *dev; 58701faef2cSRoopa Prabhu struct dst_entry *dst; 58801faef2cSRoopa Prabhu struct flowi6 fl6; 589bf21563aSRoopa Prabhu int err; 590bf21563aSRoopa Prabhu 591bf21563aSRoopa Prabhu if (!ipv6_stub) 592bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 59301faef2cSRoopa Prabhu 59401faef2cSRoopa Prabhu memset(&fl6, 0, sizeof(fl6)); 59501faef2cSRoopa Prabhu memcpy(&fl6.daddr, addr, sizeof(struct in6_addr)); 596bf21563aSRoopa Prabhu err = ipv6_stub->ipv6_dst_lookup(net, NULL, &dst, &fl6); 597bf21563aSRoopa Prabhu if (err) 5985a9348b5SDan Carpenter return ERR_PTR(err); 59901faef2cSRoopa Prabhu 60001faef2cSRoopa Prabhu dev = dst->dev; 60101faef2cSRoopa Prabhu dev_hold(dev); 60201faef2cSRoopa Prabhu dst_release(dst); 60301faef2cSRoopa Prabhu 60401faef2cSRoopa Prabhu return dev; 60501faef2cSRoopa Prabhu } 606bf21563aSRoopa Prabhu #else 607cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 608cf4b24f0SRobert Shearman const void *addr) 609bf21563aSRoopa Prabhu { 610bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 611bf21563aSRoopa Prabhu } 612bf21563aSRoopa Prabhu #endif 61301faef2cSRoopa Prabhu 61401faef2cSRoopa Prabhu static struct net_device *find_outdev(struct net *net, 615cf4b24f0SRobert Shearman struct mpls_route *rt, 616f8efb73cSRoopa Prabhu struct mpls_nh *nh, int oif) 61701faef2cSRoopa Prabhu { 61801faef2cSRoopa Prabhu struct net_device *dev = NULL; 61901faef2cSRoopa Prabhu 620f8efb73cSRoopa Prabhu if (!oif) { 621f8efb73cSRoopa Prabhu switch (nh->nh_via_table) { 62201faef2cSRoopa Prabhu case NEIGH_ARP_TABLE: 623cf4b24f0SRobert Shearman dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 62401faef2cSRoopa Prabhu break; 62501faef2cSRoopa Prabhu case NEIGH_ND_TABLE: 626cf4b24f0SRobert Shearman dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 62701faef2cSRoopa Prabhu break; 62801faef2cSRoopa Prabhu case NEIGH_LINK_TABLE: 62901faef2cSRoopa Prabhu break; 63001faef2cSRoopa Prabhu } 63101faef2cSRoopa Prabhu } else { 632f8efb73cSRoopa Prabhu dev = dev_get_by_index(net, oif); 63301faef2cSRoopa Prabhu } 63401faef2cSRoopa Prabhu 6353dcb615eSRoopa Prabhu if (!dev) 6363dcb615eSRoopa Prabhu return ERR_PTR(-ENODEV); 6373dcb615eSRoopa Prabhu 63894a57f1fSRoopa Prabhu if (IS_ERR(dev)) 63994a57f1fSRoopa Prabhu return dev; 64094a57f1fSRoopa Prabhu 641f8efb73cSRoopa Prabhu /* The caller is holding rtnl anyways, so release the dev reference */ 642f8efb73cSRoopa Prabhu dev_put(dev); 643f8efb73cSRoopa Prabhu 64401faef2cSRoopa Prabhu return dev; 64501faef2cSRoopa Prabhu } 64601faef2cSRoopa Prabhu 647cf4b24f0SRobert Shearman static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt, 648cf4b24f0SRobert Shearman struct mpls_nh *nh, int oif) 649f8efb73cSRoopa Prabhu { 650f8efb73cSRoopa Prabhu struct net_device *dev = NULL; 651f8efb73cSRoopa Prabhu int err = -ENODEV; 652f8efb73cSRoopa Prabhu 653cf4b24f0SRobert Shearman dev = find_outdev(net, rt, nh, oif); 654f8efb73cSRoopa Prabhu if (IS_ERR(dev)) { 655f8efb73cSRoopa Prabhu err = PTR_ERR(dev); 656f8efb73cSRoopa Prabhu dev = NULL; 657f8efb73cSRoopa Prabhu goto errout; 658f8efb73cSRoopa Prabhu } 659f8efb73cSRoopa Prabhu 660f8efb73cSRoopa Prabhu /* Ensure this is a supported device */ 661f8efb73cSRoopa Prabhu err = -EINVAL; 662f8efb73cSRoopa Prabhu if (!mpls_dev_get(dev)) 663f8efb73cSRoopa Prabhu goto errout; 664f8efb73cSRoopa Prabhu 665a3e948e8SRobert Shearman if ((nh->nh_via_table == NEIGH_LINK_TABLE) && 666a3e948e8SRobert Shearman (dev->addr_len != nh->nh_via_alen)) 667a3e948e8SRobert Shearman goto errout; 668a3e948e8SRobert Shearman 669f8efb73cSRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, dev); 670f8efb73cSRoopa Prabhu 671c89359a4SRoopa Prabhu if (!(dev->flags & IFF_UP)) { 672c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_DEAD; 673c89359a4SRoopa Prabhu } else { 674c89359a4SRoopa Prabhu unsigned int flags; 675c89359a4SRoopa Prabhu 676c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 677c89359a4SRoopa Prabhu if (!(flags & (IFF_RUNNING | IFF_LOWER_UP))) 678c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_LINKDOWN; 679c89359a4SRoopa Prabhu } 680c89359a4SRoopa Prabhu 681f8efb73cSRoopa Prabhu return 0; 682f8efb73cSRoopa Prabhu 683f8efb73cSRoopa Prabhu errout: 684f8efb73cSRoopa Prabhu return err; 685f8efb73cSRoopa Prabhu } 686f8efb73cSRoopa Prabhu 687f8efb73cSRoopa Prabhu static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, 688f8efb73cSRoopa Prabhu struct mpls_route *rt) 689f8efb73cSRoopa Prabhu { 690f8efb73cSRoopa Prabhu struct net *net = cfg->rc_nlinfo.nl_net; 691f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 692f8efb73cSRoopa Prabhu int err; 693f8efb73cSRoopa Prabhu int i; 694f8efb73cSRoopa Prabhu 695f8efb73cSRoopa Prabhu if (!nh) 696f8efb73cSRoopa Prabhu return -ENOMEM; 697f8efb73cSRoopa Prabhu 698f8efb73cSRoopa Prabhu err = -EINVAL; 699f8efb73cSRoopa Prabhu 700f8efb73cSRoopa Prabhu nh->nh_labels = cfg->rc_output_labels; 701f8efb73cSRoopa Prabhu for (i = 0; i < nh->nh_labels; i++) 702f8efb73cSRoopa Prabhu nh->nh_label[i] = cfg->rc_output_label[i]; 703f8efb73cSRoopa Prabhu 704f8efb73cSRoopa Prabhu nh->nh_via_table = cfg->rc_via_table; 705cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen); 706f8efb73cSRoopa Prabhu nh->nh_via_alen = cfg->rc_via_alen; 707f8efb73cSRoopa Prabhu 708cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex); 709f8efb73cSRoopa Prabhu if (err) 710f8efb73cSRoopa Prabhu goto errout; 711f8efb73cSRoopa Prabhu 712c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 713c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 714c89359a4SRoopa Prabhu 715f8efb73cSRoopa Prabhu return 0; 716f8efb73cSRoopa Prabhu 717f8efb73cSRoopa Prabhu errout: 718f8efb73cSRoopa Prabhu return err; 719f8efb73cSRoopa Prabhu } 720f8efb73cSRoopa Prabhu 721cf4b24f0SRobert Shearman static int mpls_nh_build(struct net *net, struct mpls_route *rt, 722c89359a4SRoopa Prabhu struct mpls_nh *nh, int oif, struct nlattr *via, 723a4ac8c98SDavid Ahern struct nlattr *newdst, u8 max_labels) 724f8efb73cSRoopa Prabhu { 725f8efb73cSRoopa Prabhu int err = -ENOMEM; 726f8efb73cSRoopa Prabhu 727f8efb73cSRoopa Prabhu if (!nh) 728f8efb73cSRoopa Prabhu goto errout; 729f8efb73cSRoopa Prabhu 730f8efb73cSRoopa Prabhu if (newdst) { 731a4ac8c98SDavid Ahern err = nla_get_labels(newdst, max_labels, 732f8efb73cSRoopa Prabhu &nh->nh_labels, nh->nh_label); 733f8efb73cSRoopa Prabhu if (err) 734f8efb73cSRoopa Prabhu goto errout; 735f8efb73cSRoopa Prabhu } 736f8efb73cSRoopa Prabhu 737f20367dfSRobert Shearman if (via) { 738f8efb73cSRoopa Prabhu err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table, 739cf4b24f0SRobert Shearman __mpls_nh_via(rt, nh)); 740f8efb73cSRoopa Prabhu if (err) 741f8efb73cSRoopa Prabhu goto errout; 742f20367dfSRobert Shearman } else { 743f20367dfSRobert Shearman nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC; 744f20367dfSRobert Shearman } 745f8efb73cSRoopa Prabhu 746cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, oif); 747f8efb73cSRoopa Prabhu if (err) 748f8efb73cSRoopa Prabhu goto errout; 749f8efb73cSRoopa Prabhu 750f8efb73cSRoopa Prabhu return 0; 751f8efb73cSRoopa Prabhu 752f8efb73cSRoopa Prabhu errout: 753f8efb73cSRoopa Prabhu return err; 754f8efb73cSRoopa Prabhu } 755f8efb73cSRoopa Prabhu 75677ef013aSDavid Ahern static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, 757a4ac8c98SDavid Ahern u8 cfg_via_alen, u8 *max_via_alen, 758a4ac8c98SDavid Ahern u8 *max_labels) 759f8efb73cSRoopa Prabhu { 760f8efb73cSRoopa Prabhu int remaining = len; 76177ef013aSDavid Ahern u8 nhs = 0; 762f8efb73cSRoopa Prabhu 763cf4b24f0SRobert Shearman *max_via_alen = 0; 764a4ac8c98SDavid Ahern *max_labels = 0; 765cf4b24f0SRobert Shearman 766f8efb73cSRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 767cf4b24f0SRobert Shearman struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 768cf4b24f0SRobert Shearman int attrlen; 769a4ac8c98SDavid Ahern u8 n_labels = 0; 770cf4b24f0SRobert Shearman 771cf4b24f0SRobert Shearman attrlen = rtnh_attrlen(rtnh); 772cf4b24f0SRobert Shearman nla = nla_find(attrs, attrlen, RTA_VIA); 773cf4b24f0SRobert Shearman if (nla && nla_len(nla) >= 774cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr)) { 775cf4b24f0SRobert Shearman int via_alen = nla_len(nla) - 776cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr); 777cf4b24f0SRobert Shearman 778cf4b24f0SRobert Shearman if (via_alen <= MAX_VIA_ALEN) 779cf4b24f0SRobert Shearman *max_via_alen = max_t(u16, *max_via_alen, 780cf4b24f0SRobert Shearman via_alen); 781cf4b24f0SRobert Shearman } 782cf4b24f0SRobert Shearman 783a4ac8c98SDavid Ahern nla = nla_find(attrs, attrlen, RTA_NEWDST); 784a4ac8c98SDavid Ahern if (nla && 785a4ac8c98SDavid Ahern nla_get_labels(nla, MAX_NEW_LABELS, &n_labels, NULL) != 0) 786a4ac8c98SDavid Ahern return 0; 787a4ac8c98SDavid Ahern 788a4ac8c98SDavid Ahern *max_labels = max_t(u8, *max_labels, n_labels); 789a4ac8c98SDavid Ahern 79077ef013aSDavid Ahern /* number of nexthops is tracked by a u8. 79177ef013aSDavid Ahern * Check for overflow. 79277ef013aSDavid Ahern */ 79377ef013aSDavid Ahern if (nhs == 255) 79477ef013aSDavid Ahern return 0; 795f8efb73cSRoopa Prabhu nhs++; 79677ef013aSDavid Ahern 797f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 798f8efb73cSRoopa Prabhu } 799f8efb73cSRoopa Prabhu 800f8efb73cSRoopa Prabhu /* leftover implies invalid nexthop configuration, discard it */ 801f8efb73cSRoopa Prabhu return remaining > 0 ? 0 : nhs; 802f8efb73cSRoopa Prabhu } 803f8efb73cSRoopa Prabhu 804f8efb73cSRoopa Prabhu static int mpls_nh_build_multi(struct mpls_route_config *cfg, 805a4ac8c98SDavid Ahern struct mpls_route *rt, u8 max_labels) 806f8efb73cSRoopa Prabhu { 807f8efb73cSRoopa Prabhu struct rtnexthop *rtnh = cfg->rc_mp; 808f8efb73cSRoopa Prabhu struct nlattr *nla_via, *nla_newdst; 809f8efb73cSRoopa Prabhu int remaining = cfg->rc_mp_len; 810f8efb73cSRoopa Prabhu int err = 0; 81177ef013aSDavid Ahern u8 nhs = 0; 812f8efb73cSRoopa Prabhu 813f8efb73cSRoopa Prabhu change_nexthops(rt) { 814f8efb73cSRoopa Prabhu int attrlen; 815f8efb73cSRoopa Prabhu 816f8efb73cSRoopa Prabhu nla_via = NULL; 817f8efb73cSRoopa Prabhu nla_newdst = NULL; 818f8efb73cSRoopa Prabhu 819f8efb73cSRoopa Prabhu err = -EINVAL; 820f8efb73cSRoopa Prabhu if (!rtnh_ok(rtnh, remaining)) 821f8efb73cSRoopa Prabhu goto errout; 822f8efb73cSRoopa Prabhu 8231c78efa8SRobert Shearman /* neither weighted multipath nor any flags 8241c78efa8SRobert Shearman * are supported 8251c78efa8SRobert Shearman */ 8261c78efa8SRobert Shearman if (rtnh->rtnh_hops || rtnh->rtnh_flags) 8271c78efa8SRobert Shearman goto errout; 8281c78efa8SRobert Shearman 829f8efb73cSRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 830f8efb73cSRoopa Prabhu if (attrlen > 0) { 831f8efb73cSRoopa Prabhu struct nlattr *attrs = rtnh_attrs(rtnh); 832f8efb73cSRoopa Prabhu 833f8efb73cSRoopa Prabhu nla_via = nla_find(attrs, attrlen, RTA_VIA); 834f8efb73cSRoopa Prabhu nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST); 835f8efb73cSRoopa Prabhu } 836f8efb73cSRoopa Prabhu 837cf4b24f0SRobert Shearman err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh, 838a4ac8c98SDavid Ahern rtnh->rtnh_ifindex, nla_via, nla_newdst, 839a4ac8c98SDavid Ahern max_labels); 840f8efb73cSRoopa Prabhu if (err) 841f8efb73cSRoopa Prabhu goto errout; 842f8efb73cSRoopa Prabhu 843c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 844c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 845c89359a4SRoopa Prabhu 846f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 847f8efb73cSRoopa Prabhu nhs++; 848f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 849f8efb73cSRoopa Prabhu 850f8efb73cSRoopa Prabhu rt->rt_nhn = nhs; 851f8efb73cSRoopa Prabhu 852f8efb73cSRoopa Prabhu return 0; 853f8efb73cSRoopa Prabhu 854f8efb73cSRoopa Prabhu errout: 855f8efb73cSRoopa Prabhu return err; 856f8efb73cSRoopa Prabhu } 857f8efb73cSRoopa Prabhu 858a2519929SEric W. Biederman static int mpls_route_add(struct mpls_route_config *cfg) 859a2519929SEric W. Biederman { 86019d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 861a2519929SEric W. Biederman struct net *net = cfg->rc_nlinfo.nl_net; 862a2519929SEric W. Biederman struct mpls_route *rt, *old; 863a2519929SEric W. Biederman int err = -EINVAL; 864cf4b24f0SRobert Shearman u8 max_via_alen; 865f8efb73cSRoopa Prabhu unsigned index; 866a4ac8c98SDavid Ahern u8 max_labels; 86777ef013aSDavid Ahern u8 nhs; 868a2519929SEric W. Biederman 869a2519929SEric W. Biederman index = cfg->rc_label; 870a2519929SEric W. Biederman 871a2519929SEric W. Biederman /* If a label was not specified during insert pick one */ 872a2519929SEric W. Biederman if ((index == LABEL_NOT_SPECIFIED) && 873a2519929SEric W. Biederman (cfg->rc_nlflags & NLM_F_CREATE)) { 874a2519929SEric W. Biederman index = find_free_label(net); 875a2519929SEric W. Biederman } 876a2519929SEric W. Biederman 877a6affd24SRobert Shearman /* Reserved labels may not be set */ 878a6affd24SRobert Shearman if (index < MPLS_LABEL_FIRST_UNRESERVED) 879a2519929SEric W. Biederman goto errout; 880a2519929SEric W. Biederman 881a2519929SEric W. Biederman /* The full 20 bit range may not be supported. */ 882a2519929SEric W. Biederman if (index >= net->mpls.platform_labels) 883a2519929SEric W. Biederman goto errout; 884a2519929SEric W. Biederman 885a2519929SEric W. Biederman /* Append makes no sense with mpls */ 8860f7bbd58SEric W. Biederman err = -EOPNOTSUPP; 887a2519929SEric W. Biederman if (cfg->rc_nlflags & NLM_F_APPEND) 888a2519929SEric W. Biederman goto errout; 889a2519929SEric W. Biederman 890a2519929SEric W. Biederman err = -EEXIST; 89119d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 89219d0c341SEric W. Biederman old = rtnl_dereference(platform_label[index]); 893a2519929SEric W. Biederman if ((cfg->rc_nlflags & NLM_F_EXCL) && old) 894a2519929SEric W. Biederman goto errout; 895a2519929SEric W. Biederman 896a2519929SEric W. Biederman err = -EEXIST; 897a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_REPLACE) && old) 898a2519929SEric W. Biederman goto errout; 899a2519929SEric W. Biederman 900a2519929SEric W. Biederman err = -ENOENT; 901a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old) 902a2519929SEric W. Biederman goto errout; 903a2519929SEric W. Biederman 904f8efb73cSRoopa Prabhu err = -EINVAL; 905a4ac8c98SDavid Ahern if (cfg->rc_mp) { 906cf4b24f0SRobert Shearman nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, 907a4ac8c98SDavid Ahern cfg->rc_via_alen, &max_via_alen, 908a4ac8c98SDavid Ahern &max_labels); 909a4ac8c98SDavid Ahern } else { 910a4ac8c98SDavid Ahern max_via_alen = cfg->rc_via_alen; 911a4ac8c98SDavid Ahern max_labels = cfg->rc_output_labels; 912a4ac8c98SDavid Ahern nhs = 1; 913a4ac8c98SDavid Ahern } 914a4ac8c98SDavid Ahern 915f8efb73cSRoopa Prabhu if (nhs == 0) 916f8efb73cSRoopa Prabhu goto errout; 917f8efb73cSRoopa Prabhu 918a2519929SEric W. Biederman err = -ENOMEM; 919a4ac8c98SDavid Ahern rt = mpls_rt_alloc(nhs, max_via_alen, max_labels); 920df1c6316SDavid Ahern if (IS_ERR(rt)) { 921df1c6316SDavid Ahern err = PTR_ERR(rt); 922a2519929SEric W. Biederman goto errout; 923df1c6316SDavid Ahern } 924a2519929SEric W. Biederman 925a2519929SEric W. Biederman rt->rt_protocol = cfg->rc_protocol; 926118d5234SRobert Shearman rt->rt_payload_type = cfg->rc_payload_type; 9275b441ac8SRobert Shearman rt->rt_ttl_propagate = cfg->rc_ttl_propagate; 928a2519929SEric W. Biederman 929f8efb73cSRoopa Prabhu if (cfg->rc_mp) 930a4ac8c98SDavid Ahern err = mpls_nh_build_multi(cfg, rt, max_labels); 931f8efb73cSRoopa Prabhu else 932f8efb73cSRoopa Prabhu err = mpls_nh_build_from_cfg(cfg, rt); 933f8efb73cSRoopa Prabhu if (err) 934f8efb73cSRoopa Prabhu goto freert; 935a2519929SEric W. Biederman 936f8efb73cSRoopa Prabhu mpls_route_update(net, index, rt, &cfg->rc_nlinfo); 937f8efb73cSRoopa Prabhu 938a2519929SEric W. Biederman return 0; 939a2519929SEric W. Biederman 940f8efb73cSRoopa Prabhu freert: 941f8efb73cSRoopa Prabhu mpls_rt_free(rt); 942a2519929SEric W. Biederman errout: 943a2519929SEric W. Biederman return err; 944a2519929SEric W. Biederman } 945a2519929SEric W. Biederman 946a2519929SEric W. Biederman static int mpls_route_del(struct mpls_route_config *cfg) 947a2519929SEric W. Biederman { 948a2519929SEric W. Biederman struct net *net = cfg->rc_nlinfo.nl_net; 949a2519929SEric W. Biederman unsigned index; 950a2519929SEric W. Biederman int err = -EINVAL; 951a2519929SEric W. Biederman 952a2519929SEric W. Biederman index = cfg->rc_label; 953a2519929SEric W. Biederman 954a6affd24SRobert Shearman /* Reserved labels may not be removed */ 955a6affd24SRobert Shearman if (index < MPLS_LABEL_FIRST_UNRESERVED) 956a2519929SEric W. Biederman goto errout; 957a2519929SEric W. Biederman 958a2519929SEric W. Biederman /* The full 20 bit range may not be supported */ 959a2519929SEric W. Biederman if (index >= net->mpls.platform_labels) 960a2519929SEric W. Biederman goto errout; 961a2519929SEric W. Biederman 962f8efb73cSRoopa Prabhu mpls_route_update(net, index, NULL, &cfg->rc_nlinfo); 963a2519929SEric W. Biederman 964a2519929SEric W. Biederman err = 0; 965a2519929SEric W. Biederman errout: 966a2519929SEric W. Biederman return err; 967a2519929SEric W. Biederman } 968a2519929SEric W. Biederman 96927d69105SRobert Shearman static void mpls_get_stats(struct mpls_dev *mdev, 97027d69105SRobert Shearman struct mpls_link_stats *stats) 97127d69105SRobert Shearman { 97227d69105SRobert Shearman struct mpls_pcpu_stats *p; 97327d69105SRobert Shearman int i; 97427d69105SRobert Shearman 97527d69105SRobert Shearman memset(stats, 0, sizeof(*stats)); 97627d69105SRobert Shearman 97727d69105SRobert Shearman for_each_possible_cpu(i) { 97827d69105SRobert Shearman struct mpls_link_stats local; 97927d69105SRobert Shearman unsigned int start; 98027d69105SRobert Shearman 98127d69105SRobert Shearman p = per_cpu_ptr(mdev->stats, i); 98227d69105SRobert Shearman do { 98327d69105SRobert Shearman start = u64_stats_fetch_begin(&p->syncp); 98427d69105SRobert Shearman local = p->stats; 98527d69105SRobert Shearman } while (u64_stats_fetch_retry(&p->syncp, start)); 98627d69105SRobert Shearman 98727d69105SRobert Shearman stats->rx_packets += local.rx_packets; 98827d69105SRobert Shearman stats->rx_bytes += local.rx_bytes; 98927d69105SRobert Shearman stats->tx_packets += local.tx_packets; 99027d69105SRobert Shearman stats->tx_bytes += local.tx_bytes; 99127d69105SRobert Shearman stats->rx_errors += local.rx_errors; 99227d69105SRobert Shearman stats->tx_errors += local.tx_errors; 99327d69105SRobert Shearman stats->rx_dropped += local.rx_dropped; 99427d69105SRobert Shearman stats->tx_dropped += local.tx_dropped; 99527d69105SRobert Shearman stats->rx_noroute += local.rx_noroute; 99627d69105SRobert Shearman } 99727d69105SRobert Shearman } 99827d69105SRobert Shearman 99927d69105SRobert Shearman static int mpls_fill_stats_af(struct sk_buff *skb, 100027d69105SRobert Shearman const struct net_device *dev) 100127d69105SRobert Shearman { 100227d69105SRobert Shearman struct mpls_link_stats *stats; 100327d69105SRobert Shearman struct mpls_dev *mdev; 100427d69105SRobert Shearman struct nlattr *nla; 100527d69105SRobert Shearman 100627d69105SRobert Shearman mdev = mpls_dev_get(dev); 100727d69105SRobert Shearman if (!mdev) 100827d69105SRobert Shearman return -ENODATA; 100927d69105SRobert Shearman 101027d69105SRobert Shearman nla = nla_reserve_64bit(skb, MPLS_STATS_LINK, 101127d69105SRobert Shearman sizeof(struct mpls_link_stats), 101227d69105SRobert Shearman MPLS_STATS_UNSPEC); 101327d69105SRobert Shearman if (!nla) 101427d69105SRobert Shearman return -EMSGSIZE; 101527d69105SRobert Shearman 101627d69105SRobert Shearman stats = nla_data(nla); 101727d69105SRobert Shearman mpls_get_stats(mdev, stats); 101827d69105SRobert Shearman 101927d69105SRobert Shearman return 0; 102027d69105SRobert Shearman } 102127d69105SRobert Shearman 102227d69105SRobert Shearman static size_t mpls_get_stats_af_size(const struct net_device *dev) 102327d69105SRobert Shearman { 102427d69105SRobert Shearman struct mpls_dev *mdev; 102527d69105SRobert Shearman 102627d69105SRobert Shearman mdev = mpls_dev_get(dev); 102727d69105SRobert Shearman if (!mdev) 102827d69105SRobert Shearman return 0; 102927d69105SRobert Shearman 103027d69105SRobert Shearman return nla_total_size_64bit(sizeof(struct mpls_link_stats)); 103127d69105SRobert Shearman } 103227d69105SRobert Shearman 103324045a03SDavid Ahern static int mpls_netconf_fill_devconf(struct sk_buff *skb, struct mpls_dev *mdev, 103424045a03SDavid Ahern u32 portid, u32 seq, int event, 103524045a03SDavid Ahern unsigned int flags, int type) 103624045a03SDavid Ahern { 103724045a03SDavid Ahern struct nlmsghdr *nlh; 103824045a03SDavid Ahern struct netconfmsg *ncm; 103924045a03SDavid Ahern bool all = false; 104024045a03SDavid Ahern 104124045a03SDavid Ahern nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 104224045a03SDavid Ahern flags); 104324045a03SDavid Ahern if (!nlh) 104424045a03SDavid Ahern return -EMSGSIZE; 104524045a03SDavid Ahern 104624045a03SDavid Ahern if (type == NETCONFA_ALL) 104724045a03SDavid Ahern all = true; 104824045a03SDavid Ahern 104924045a03SDavid Ahern ncm = nlmsg_data(nlh); 105024045a03SDavid Ahern ncm->ncm_family = AF_MPLS; 105124045a03SDavid Ahern 105224045a03SDavid Ahern if (nla_put_s32(skb, NETCONFA_IFINDEX, mdev->dev->ifindex) < 0) 105324045a03SDavid Ahern goto nla_put_failure; 105424045a03SDavid Ahern 105524045a03SDavid Ahern if ((all || type == NETCONFA_INPUT) && 105624045a03SDavid Ahern nla_put_s32(skb, NETCONFA_INPUT, 105724045a03SDavid Ahern mdev->input_enabled) < 0) 105824045a03SDavid Ahern goto nla_put_failure; 105924045a03SDavid Ahern 106024045a03SDavid Ahern nlmsg_end(skb, nlh); 106124045a03SDavid Ahern return 0; 106224045a03SDavid Ahern 106324045a03SDavid Ahern nla_put_failure: 106424045a03SDavid Ahern nlmsg_cancel(skb, nlh); 106524045a03SDavid Ahern return -EMSGSIZE; 106624045a03SDavid Ahern } 106724045a03SDavid Ahern 106824045a03SDavid Ahern static int mpls_netconf_msgsize_devconf(int type) 106924045a03SDavid Ahern { 107024045a03SDavid Ahern int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 107124045a03SDavid Ahern + nla_total_size(4); /* NETCONFA_IFINDEX */ 107224045a03SDavid Ahern bool all = false; 107324045a03SDavid Ahern 107424045a03SDavid Ahern if (type == NETCONFA_ALL) 107524045a03SDavid Ahern all = true; 107624045a03SDavid Ahern 107724045a03SDavid Ahern if (all || type == NETCONFA_INPUT) 107824045a03SDavid Ahern size += nla_total_size(4); 107924045a03SDavid Ahern 108024045a03SDavid Ahern return size; 108124045a03SDavid Ahern } 108224045a03SDavid Ahern 1083823566aeSDavid Ahern static void mpls_netconf_notify_devconf(struct net *net, int event, 1084823566aeSDavid Ahern int type, struct mpls_dev *mdev) 108524045a03SDavid Ahern { 108624045a03SDavid Ahern struct sk_buff *skb; 108724045a03SDavid Ahern int err = -ENOBUFS; 108824045a03SDavid Ahern 108924045a03SDavid Ahern skb = nlmsg_new(mpls_netconf_msgsize_devconf(type), GFP_KERNEL); 109024045a03SDavid Ahern if (!skb) 109124045a03SDavid Ahern goto errout; 109224045a03SDavid Ahern 1093823566aeSDavid Ahern err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, event, 0, type); 109424045a03SDavid Ahern if (err < 0) { 109524045a03SDavid Ahern /* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */ 109624045a03SDavid Ahern WARN_ON(err == -EMSGSIZE); 109724045a03SDavid Ahern kfree_skb(skb); 109824045a03SDavid Ahern goto errout; 109924045a03SDavid Ahern } 110024045a03SDavid Ahern 110124045a03SDavid Ahern rtnl_notify(skb, net, 0, RTNLGRP_MPLS_NETCONF, NULL, GFP_KERNEL); 110224045a03SDavid Ahern return; 110324045a03SDavid Ahern errout: 110424045a03SDavid Ahern if (err < 0) 110524045a03SDavid Ahern rtnl_set_sk_err(net, RTNLGRP_MPLS_NETCONF, err); 110624045a03SDavid Ahern } 110724045a03SDavid Ahern 110824045a03SDavid Ahern static const struct nla_policy devconf_mpls_policy[NETCONFA_MAX + 1] = { 110924045a03SDavid Ahern [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 111024045a03SDavid Ahern }; 111124045a03SDavid Ahern 111224045a03SDavid Ahern static int mpls_netconf_get_devconf(struct sk_buff *in_skb, 1113c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 1114c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 111524045a03SDavid Ahern { 111624045a03SDavid Ahern struct net *net = sock_net(in_skb->sk); 111724045a03SDavid Ahern struct nlattr *tb[NETCONFA_MAX + 1]; 111824045a03SDavid Ahern struct netconfmsg *ncm; 111924045a03SDavid Ahern struct net_device *dev; 112024045a03SDavid Ahern struct mpls_dev *mdev; 112124045a03SDavid Ahern struct sk_buff *skb; 112224045a03SDavid Ahern int ifindex; 112324045a03SDavid Ahern int err; 112424045a03SDavid Ahern 112524045a03SDavid Ahern err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 1126fceb6435SJohannes Berg devconf_mpls_policy, NULL); 112724045a03SDavid Ahern if (err < 0) 112824045a03SDavid Ahern goto errout; 112924045a03SDavid Ahern 113024045a03SDavid Ahern err = -EINVAL; 113124045a03SDavid Ahern if (!tb[NETCONFA_IFINDEX]) 113224045a03SDavid Ahern goto errout; 113324045a03SDavid Ahern 113424045a03SDavid Ahern ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 113524045a03SDavid Ahern dev = __dev_get_by_index(net, ifindex); 113624045a03SDavid Ahern if (!dev) 113724045a03SDavid Ahern goto errout; 113824045a03SDavid Ahern 113924045a03SDavid Ahern mdev = mpls_dev_get(dev); 114024045a03SDavid Ahern if (!mdev) 114124045a03SDavid Ahern goto errout; 114224045a03SDavid Ahern 114324045a03SDavid Ahern err = -ENOBUFS; 114424045a03SDavid Ahern skb = nlmsg_new(mpls_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 114524045a03SDavid Ahern if (!skb) 114624045a03SDavid Ahern goto errout; 114724045a03SDavid Ahern 114824045a03SDavid Ahern err = mpls_netconf_fill_devconf(skb, mdev, 114924045a03SDavid Ahern NETLINK_CB(in_skb).portid, 115024045a03SDavid Ahern nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 115124045a03SDavid Ahern NETCONFA_ALL); 115224045a03SDavid Ahern if (err < 0) { 115324045a03SDavid Ahern /* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */ 115424045a03SDavid Ahern WARN_ON(err == -EMSGSIZE); 115524045a03SDavid Ahern kfree_skb(skb); 115624045a03SDavid Ahern goto errout; 115724045a03SDavid Ahern } 115824045a03SDavid Ahern err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 115924045a03SDavid Ahern errout: 116024045a03SDavid Ahern return err; 116124045a03SDavid Ahern } 116224045a03SDavid Ahern 116324045a03SDavid Ahern static int mpls_netconf_dump_devconf(struct sk_buff *skb, 116424045a03SDavid Ahern struct netlink_callback *cb) 116524045a03SDavid Ahern { 116624045a03SDavid Ahern struct net *net = sock_net(skb->sk); 116724045a03SDavid Ahern struct hlist_head *head; 116824045a03SDavid Ahern struct net_device *dev; 116924045a03SDavid Ahern struct mpls_dev *mdev; 117024045a03SDavid Ahern int idx, s_idx; 117124045a03SDavid Ahern int h, s_h; 117224045a03SDavid Ahern 117324045a03SDavid Ahern s_h = cb->args[0]; 117424045a03SDavid Ahern s_idx = idx = cb->args[1]; 117524045a03SDavid Ahern 117624045a03SDavid Ahern for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 117724045a03SDavid Ahern idx = 0; 117824045a03SDavid Ahern head = &net->dev_index_head[h]; 117924045a03SDavid Ahern rcu_read_lock(); 118024045a03SDavid Ahern cb->seq = net->dev_base_seq; 118124045a03SDavid Ahern hlist_for_each_entry_rcu(dev, head, index_hlist) { 118224045a03SDavid Ahern if (idx < s_idx) 118324045a03SDavid Ahern goto cont; 118424045a03SDavid Ahern mdev = mpls_dev_get(dev); 118524045a03SDavid Ahern if (!mdev) 118624045a03SDavid Ahern goto cont; 118724045a03SDavid Ahern if (mpls_netconf_fill_devconf(skb, mdev, 118824045a03SDavid Ahern NETLINK_CB(cb->skb).portid, 118924045a03SDavid Ahern cb->nlh->nlmsg_seq, 119024045a03SDavid Ahern RTM_NEWNETCONF, 119124045a03SDavid Ahern NLM_F_MULTI, 119224045a03SDavid Ahern NETCONFA_ALL) < 0) { 119324045a03SDavid Ahern rcu_read_unlock(); 119424045a03SDavid Ahern goto done; 119524045a03SDavid Ahern } 119624045a03SDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 119724045a03SDavid Ahern cont: 119824045a03SDavid Ahern idx++; 119924045a03SDavid Ahern } 120024045a03SDavid Ahern rcu_read_unlock(); 120124045a03SDavid Ahern } 120224045a03SDavid Ahern done: 120324045a03SDavid Ahern cb->args[0] = h; 120424045a03SDavid Ahern cb->args[1] = idx; 120524045a03SDavid Ahern 120624045a03SDavid Ahern return skb->len; 120724045a03SDavid Ahern } 120824045a03SDavid Ahern 120937bde799SRobert Shearman #define MPLS_PERDEV_SYSCTL_OFFSET(field) \ 121037bde799SRobert Shearman (&((struct mpls_dev *)0)->field) 121137bde799SRobert Shearman 121224045a03SDavid Ahern static int mpls_conf_proc(struct ctl_table *ctl, int write, 121324045a03SDavid Ahern void __user *buffer, 121424045a03SDavid Ahern size_t *lenp, loff_t *ppos) 121524045a03SDavid Ahern { 121624045a03SDavid Ahern int oval = *(int *)ctl->data; 121724045a03SDavid Ahern int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 121824045a03SDavid Ahern 121924045a03SDavid Ahern if (write) { 122024045a03SDavid Ahern struct mpls_dev *mdev = ctl->extra1; 122124045a03SDavid Ahern int i = (int *)ctl->data - (int *)mdev; 122224045a03SDavid Ahern struct net *net = ctl->extra2; 122324045a03SDavid Ahern int val = *(int *)ctl->data; 122424045a03SDavid Ahern 122524045a03SDavid Ahern if (i == offsetof(struct mpls_dev, input_enabled) && 122624045a03SDavid Ahern val != oval) { 1227823566aeSDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, 1228823566aeSDavid Ahern NETCONFA_INPUT, mdev); 122924045a03SDavid Ahern } 123024045a03SDavid Ahern } 123124045a03SDavid Ahern 123224045a03SDavid Ahern return ret; 123324045a03SDavid Ahern } 123424045a03SDavid Ahern 123537bde799SRobert Shearman static const struct ctl_table mpls_dev_table[] = { 123637bde799SRobert Shearman { 123737bde799SRobert Shearman .procname = "input", 123837bde799SRobert Shearman .maxlen = sizeof(int), 123937bde799SRobert Shearman .mode = 0644, 124024045a03SDavid Ahern .proc_handler = mpls_conf_proc, 124137bde799SRobert Shearman .data = MPLS_PERDEV_SYSCTL_OFFSET(input_enabled), 124237bde799SRobert Shearman }, 124337bde799SRobert Shearman { } 124437bde799SRobert Shearman }; 124537bde799SRobert Shearman 124637bde799SRobert Shearman static int mpls_dev_sysctl_register(struct net_device *dev, 124737bde799SRobert Shearman struct mpls_dev *mdev) 124837bde799SRobert Shearman { 124937bde799SRobert Shearman char path[sizeof("net/mpls/conf/") + IFNAMSIZ]; 125024045a03SDavid Ahern struct net *net = dev_net(dev); 125137bde799SRobert Shearman struct ctl_table *table; 125237bde799SRobert Shearman int i; 125337bde799SRobert Shearman 125437bde799SRobert Shearman table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL); 125537bde799SRobert Shearman if (!table) 125637bde799SRobert Shearman goto out; 125737bde799SRobert Shearman 125837bde799SRobert Shearman /* Table data contains only offsets relative to the base of 125937bde799SRobert Shearman * the mdev at this point, so make them absolute. 126037bde799SRobert Shearman */ 126124045a03SDavid Ahern for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) { 126237bde799SRobert Shearman table[i].data = (char *)mdev + (uintptr_t)table[i].data; 126324045a03SDavid Ahern table[i].extra1 = mdev; 126424045a03SDavid Ahern table[i].extra2 = net; 126524045a03SDavid Ahern } 126637bde799SRobert Shearman 126737bde799SRobert Shearman snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); 126837bde799SRobert Shearman 12691182e4d0SDavid Ahern mdev->sysctl = register_net_sysctl(net, path, table); 127037bde799SRobert Shearman if (!mdev->sysctl) 127137bde799SRobert Shearman goto free; 127237bde799SRobert Shearman 12731182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev); 127437bde799SRobert Shearman return 0; 127537bde799SRobert Shearman 127637bde799SRobert Shearman free: 127737bde799SRobert Shearman kfree(table); 127837bde799SRobert Shearman out: 127937bde799SRobert Shearman return -ENOBUFS; 128037bde799SRobert Shearman } 128137bde799SRobert Shearman 12821182e4d0SDavid Ahern static void mpls_dev_sysctl_unregister(struct net_device *dev, 12831182e4d0SDavid Ahern struct mpls_dev *mdev) 128437bde799SRobert Shearman { 12851182e4d0SDavid Ahern struct net *net = dev_net(dev); 128637bde799SRobert Shearman struct ctl_table *table; 128737bde799SRobert Shearman 128837bde799SRobert Shearman table = mdev->sysctl->ctl_table_arg; 128937bde799SRobert Shearman unregister_net_sysctl_table(mdev->sysctl); 129037bde799SRobert Shearman kfree(table); 12911182e4d0SDavid Ahern 12921182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev); 129337bde799SRobert Shearman } 129437bde799SRobert Shearman 129503c57747SRobert Shearman static struct mpls_dev *mpls_add_dev(struct net_device *dev) 129603c57747SRobert Shearman { 129703c57747SRobert Shearman struct mpls_dev *mdev; 129803c57747SRobert Shearman int err = -ENOMEM; 129927d69105SRobert Shearman int i; 130003c57747SRobert Shearman 130103c57747SRobert Shearman ASSERT_RTNL(); 130203c57747SRobert Shearman 130303c57747SRobert Shearman mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 130403c57747SRobert Shearman if (!mdev) 130503c57747SRobert Shearman return ERR_PTR(err); 130603c57747SRobert Shearman 130727d69105SRobert Shearman mdev->stats = alloc_percpu(struct mpls_pcpu_stats); 130827d69105SRobert Shearman if (!mdev->stats) 130927d69105SRobert Shearman goto free; 131027d69105SRobert Shearman 131127d69105SRobert Shearman for_each_possible_cpu(i) { 131227d69105SRobert Shearman struct mpls_pcpu_stats *mpls_stats; 131327d69105SRobert Shearman 131427d69105SRobert Shearman mpls_stats = per_cpu_ptr(mdev->stats, i); 131527d69105SRobert Shearman u64_stats_init(&mpls_stats->syncp); 131627d69105SRobert Shearman } 131727d69105SRobert Shearman 13181182e4d0SDavid Ahern mdev->dev = dev; 13191182e4d0SDavid Ahern 132037bde799SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 132137bde799SRobert Shearman if (err) 132237bde799SRobert Shearman goto free; 132337bde799SRobert Shearman 132403c57747SRobert Shearman rcu_assign_pointer(dev->mpls_ptr, mdev); 132503c57747SRobert Shearman 132603c57747SRobert Shearman return mdev; 132737bde799SRobert Shearman 132837bde799SRobert Shearman free: 132927d69105SRobert Shearman free_percpu(mdev->stats); 133037bde799SRobert Shearman kfree(mdev); 133137bde799SRobert Shearman return ERR_PTR(err); 133203c57747SRobert Shearman } 133303c57747SRobert Shearman 133427d69105SRobert Shearman static void mpls_dev_destroy_rcu(struct rcu_head *head) 133527d69105SRobert Shearman { 133627d69105SRobert Shearman struct mpls_dev *mdev = container_of(head, struct mpls_dev, rcu); 133727d69105SRobert Shearman 133827d69105SRobert Shearman free_percpu(mdev->stats); 133927d69105SRobert Shearman kfree(mdev); 134027d69105SRobert Shearman } 134127d69105SRobert Shearman 1342c89359a4SRoopa Prabhu static void mpls_ifdown(struct net_device *dev, int event) 13430189197fSEric W. Biederman { 134419d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 13450189197fSEric W. Biederman struct net *net = dev_net(dev); 134677ef013aSDavid Ahern u8 alive, deleted; 13470189197fSEric W. Biederman unsigned index; 13480189197fSEric W. Biederman 134919d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 13500189197fSEric W. Biederman for (index = 0; index < net->mpls.platform_labels; index++) { 135119d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 1352c89359a4SRoopa Prabhu 13530189197fSEric W. Biederman if (!rt) 13540189197fSEric W. Biederman continue; 1355c89359a4SRoopa Prabhu 135661733c91SDavid Ahern alive = 0; 13574ea8efadSDavid Ahern deleted = 0; 1358c89359a4SRoopa Prabhu change_nexthops(rt) { 135939eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 136039eb8cd1SDavid Ahern 1361f8efb73cSRoopa Prabhu if (rtnl_dereference(nh->nh_dev) != dev) 136261733c91SDavid Ahern goto next; 136361733c91SDavid Ahern 1364c89359a4SRoopa Prabhu switch (event) { 1365c89359a4SRoopa Prabhu case NETDEV_DOWN: 1366c89359a4SRoopa Prabhu case NETDEV_UNREGISTER: 136739eb8cd1SDavid Ahern nh_flags |= RTNH_F_DEAD; 1368c89359a4SRoopa Prabhu /* fall through */ 1369c89359a4SRoopa Prabhu case NETDEV_CHANGE: 137039eb8cd1SDavid Ahern nh_flags |= RTNH_F_LINKDOWN; 1371c89359a4SRoopa Prabhu break; 1372c89359a4SRoopa Prabhu } 1373c89359a4SRoopa Prabhu if (event == NETDEV_UNREGISTER) 1374c89359a4SRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, NULL); 137539eb8cd1SDavid Ahern 137639eb8cd1SDavid Ahern if (nh->nh_flags != nh_flags) 137739eb8cd1SDavid Ahern WRITE_ONCE(nh->nh_flags, nh_flags); 137861733c91SDavid Ahern next: 137939eb8cd1SDavid Ahern if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))) 138061733c91SDavid Ahern alive++; 13814ea8efadSDavid Ahern if (!rtnl_dereference(nh->nh_dev)) 13824ea8efadSDavid Ahern deleted++; 1383f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 138461733c91SDavid Ahern 138561733c91SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 13864ea8efadSDavid Ahern 13874ea8efadSDavid Ahern /* if there are no more nexthops, delete the route */ 13884ea8efadSDavid Ahern if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn) 13894ea8efadSDavid Ahern mpls_route_update(net, index, NULL, NULL); 13900189197fSEric W. Biederman } 1391c89359a4SRoopa Prabhu } 139203c57747SRobert Shearman 139339eb8cd1SDavid Ahern static void mpls_ifup(struct net_device *dev, unsigned int flags) 1394c89359a4SRoopa Prabhu { 1395c89359a4SRoopa Prabhu struct mpls_route __rcu **platform_label; 1396c89359a4SRoopa Prabhu struct net *net = dev_net(dev); 1397c89359a4SRoopa Prabhu unsigned index; 139877ef013aSDavid Ahern u8 alive; 139937bde799SRobert Shearman 1400c89359a4SRoopa Prabhu platform_label = rtnl_dereference(net->mpls.platform_label); 1401c89359a4SRoopa Prabhu for (index = 0; index < net->mpls.platform_labels; index++) { 1402c89359a4SRoopa Prabhu struct mpls_route *rt = rtnl_dereference(platform_label[index]); 140303c57747SRobert Shearman 1404c89359a4SRoopa Prabhu if (!rt) 1405c89359a4SRoopa Prabhu continue; 1406c89359a4SRoopa Prabhu 1407c89359a4SRoopa Prabhu alive = 0; 1408c89359a4SRoopa Prabhu change_nexthops(rt) { 140939eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 1410c89359a4SRoopa Prabhu struct net_device *nh_dev = 1411c89359a4SRoopa Prabhu rtnl_dereference(nh->nh_dev); 1412c89359a4SRoopa Prabhu 141339eb8cd1SDavid Ahern if (!(nh_flags & flags)) { 1414c89359a4SRoopa Prabhu alive++; 1415c89359a4SRoopa Prabhu continue; 1416c89359a4SRoopa Prabhu } 1417c89359a4SRoopa Prabhu if (nh_dev != dev) 1418c89359a4SRoopa Prabhu continue; 1419c89359a4SRoopa Prabhu alive++; 142039eb8cd1SDavid Ahern nh_flags &= ~flags; 1421*c2e8471dSRoopa Prabhu WRITE_ONCE(nh->nh_flags, nh_flags); 1422c89359a4SRoopa Prabhu } endfor_nexthops(rt); 1423c89359a4SRoopa Prabhu 142439eb8cd1SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 1425c89359a4SRoopa Prabhu } 14260189197fSEric W. Biederman } 14270189197fSEric W. Biederman 14280189197fSEric W. Biederman static int mpls_dev_notify(struct notifier_block *this, unsigned long event, 14290189197fSEric W. Biederman void *ptr) 14300189197fSEric W. Biederman { 14310189197fSEric W. Biederman struct net_device *dev = netdev_notifier_info_to_dev(ptr); 143203c57747SRobert Shearman struct mpls_dev *mdev; 1433c89359a4SRoopa Prabhu unsigned int flags; 14340189197fSEric W. Biederman 1435c89359a4SRoopa Prabhu if (event == NETDEV_REGISTER) { 1436407f31beSSimon Horman /* For now just support Ethernet, IPGRE, SIT and IPIP devices */ 14370d227a86SSimon Horman if (dev->type == ARPHRD_ETHER || 14380d227a86SSimon Horman dev->type == ARPHRD_LOOPBACK || 1439407f31beSSimon Horman dev->type == ARPHRD_IPGRE || 1440407f31beSSimon Horman dev->type == ARPHRD_SIT || 1441407f31beSSimon Horman dev->type == ARPHRD_TUNNEL) { 144203c57747SRobert Shearman mdev = mpls_add_dev(dev); 144303c57747SRobert Shearman if (IS_ERR(mdev)) 144403c57747SRobert Shearman return notifier_from_errno(PTR_ERR(mdev)); 144503c57747SRobert Shearman } 1446c89359a4SRoopa Prabhu return NOTIFY_OK; 1447c89359a4SRoopa Prabhu } 144803c57747SRobert Shearman 1449c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1450c89359a4SRoopa Prabhu if (!mdev) 1451c89359a4SRoopa Prabhu return NOTIFY_OK; 1452c89359a4SRoopa Prabhu 1453c89359a4SRoopa Prabhu switch (event) { 1454c89359a4SRoopa Prabhu case NETDEV_DOWN: 1455c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1456c89359a4SRoopa Prabhu break; 1457c89359a4SRoopa Prabhu case NETDEV_UP: 1458c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1459c89359a4SRoopa Prabhu if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1460c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1461c89359a4SRoopa Prabhu else 1462c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD); 1463c89359a4SRoopa Prabhu break; 1464c89359a4SRoopa Prabhu case NETDEV_CHANGE: 1465c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1466c89359a4SRoopa Prabhu if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1467c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1468c89359a4SRoopa Prabhu else 1469c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1470c89359a4SRoopa Prabhu break; 14710189197fSEric W. Biederman case NETDEV_UNREGISTER: 1472c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1473c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1474c89359a4SRoopa Prabhu if (mdev) { 14751182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 1476c89359a4SRoopa Prabhu RCU_INIT_POINTER(dev->mpls_ptr, NULL); 147727d69105SRobert Shearman call_rcu(&mdev->rcu, mpls_dev_destroy_rcu); 1478c89359a4SRoopa Prabhu } 14790189197fSEric W. Biederman break; 14800fae3bf0SRobert Shearman case NETDEV_CHANGENAME: 14810fae3bf0SRobert Shearman mdev = mpls_dev_get(dev); 14820fae3bf0SRobert Shearman if (mdev) { 14830fae3bf0SRobert Shearman int err; 14840fae3bf0SRobert Shearman 14851182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 14860fae3bf0SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 14870fae3bf0SRobert Shearman if (err) 14880fae3bf0SRobert Shearman return notifier_from_errno(err); 14890fae3bf0SRobert Shearman } 14900fae3bf0SRobert Shearman break; 14910189197fSEric W. Biederman } 14920189197fSEric W. Biederman return NOTIFY_OK; 14930189197fSEric W. Biederman } 14940189197fSEric W. Biederman 14950189197fSEric W. Biederman static struct notifier_block mpls_dev_notifier = { 14960189197fSEric W. Biederman .notifier_call = mpls_dev_notify, 14970189197fSEric W. Biederman }; 14980189197fSEric W. Biederman 149903c05665SEric W. Biederman static int nla_put_via(struct sk_buff *skb, 1500b79bda3dSEric W. Biederman u8 table, const void *addr, int alen) 150103c05665SEric W. Biederman { 1502b79bda3dSEric W. Biederman static const int table_to_family[NEIGH_NR_TABLES + 1] = { 1503b79bda3dSEric W. Biederman AF_INET, AF_INET6, AF_DECnet, AF_PACKET, 1504b79bda3dSEric W. Biederman }; 150503c05665SEric W. Biederman struct nlattr *nla; 150603c05665SEric W. Biederman struct rtvia *via; 1507b79bda3dSEric W. Biederman int family = AF_UNSPEC; 150803c05665SEric W. Biederman 150903c05665SEric W. Biederman nla = nla_reserve(skb, RTA_VIA, alen + 2); 151003c05665SEric W. Biederman if (!nla) 151103c05665SEric W. Biederman return -EMSGSIZE; 151203c05665SEric W. Biederman 1513b79bda3dSEric W. Biederman if (table <= NEIGH_NR_TABLES) 1514b79bda3dSEric W. Biederman family = table_to_family[table]; 1515b79bda3dSEric W. Biederman 151603c05665SEric W. Biederman via = nla_data(nla); 151703c05665SEric W. Biederman via->rtvia_family = family; 151803c05665SEric W. Biederman memcpy(via->rtvia_addr, addr, alen); 151903c05665SEric W. Biederman return 0; 152003c05665SEric W. Biederman } 152103c05665SEric W. Biederman 1522966bae33SEric W. Biederman int nla_put_labels(struct sk_buff *skb, int attrtype, 1523966bae33SEric W. Biederman u8 labels, const u32 label[]) 1524966bae33SEric W. Biederman { 1525966bae33SEric W. Biederman struct nlattr *nla; 1526966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1527966bae33SEric W. Biederman bool bos; 1528966bae33SEric W. Biederman int i; 1529966bae33SEric W. Biederman nla = nla_reserve(skb, attrtype, labels*4); 1530966bae33SEric W. Biederman if (!nla) 1531966bae33SEric W. Biederman return -EMSGSIZE; 1532966bae33SEric W. Biederman 1533966bae33SEric W. Biederman nla_label = nla_data(nla); 1534966bae33SEric W. Biederman bos = true; 1535966bae33SEric W. Biederman for (i = labels - 1; i >= 0; i--) { 1536966bae33SEric W. Biederman nla_label[i] = mpls_entry_encode(label[i], 0, 0, bos); 1537966bae33SEric W. Biederman bos = false; 1538966bae33SEric W. Biederman } 1539966bae33SEric W. Biederman 1540966bae33SEric W. Biederman return 0; 1541966bae33SEric W. Biederman } 1542face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_put_labels); 1543966bae33SEric W. Biederman 1544966bae33SEric W. Biederman int nla_get_labels(const struct nlattr *nla, 1545a4ac8c98SDavid Ahern u8 max_labels, u8 *labels, u32 label[]) 1546966bae33SEric W. Biederman { 1547966bae33SEric W. Biederman unsigned len = nla_len(nla); 1548966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1549a4ac8c98SDavid Ahern u8 nla_labels; 1550966bae33SEric W. Biederman bool bos; 1551966bae33SEric W. Biederman int i; 1552966bae33SEric W. Biederman 1553a4ac8c98SDavid Ahern /* len needs to be an even multiple of 4 (the label size). Number 1554a4ac8c98SDavid Ahern * of labels is a u8 so check for overflow. 1555a4ac8c98SDavid Ahern */ 1556a4ac8c98SDavid Ahern if (len & 3 || len / 4 > 255) 1557966bae33SEric W. Biederman return -EINVAL; 1558966bae33SEric W. Biederman 1559966bae33SEric W. Biederman /* Limit the number of new labels allowed */ 1560966bae33SEric W. Biederman nla_labels = len/4; 1561966bae33SEric W. Biederman if (nla_labels > max_labels) 1562966bae33SEric W. Biederman return -EINVAL; 1563966bae33SEric W. Biederman 1564a4ac8c98SDavid Ahern /* when label == NULL, caller wants number of labels */ 1565a4ac8c98SDavid Ahern if (!label) 1566a4ac8c98SDavid Ahern goto out; 1567a4ac8c98SDavid Ahern 1568966bae33SEric W. Biederman nla_label = nla_data(nla); 1569966bae33SEric W. Biederman bos = true; 1570966bae33SEric W. Biederman for (i = nla_labels - 1; i >= 0; i--, bos = false) { 1571966bae33SEric W. Biederman struct mpls_entry_decoded dec; 1572966bae33SEric W. Biederman dec = mpls_entry_decode(nla_label + i); 1573966bae33SEric W. Biederman 1574966bae33SEric W. Biederman /* Ensure the bottom of stack flag is properly set 1575966bae33SEric W. Biederman * and ttl and tc are both clear. 1576966bae33SEric W. Biederman */ 1577966bae33SEric W. Biederman if ((dec.bos != bos) || dec.ttl || dec.tc) 1578966bae33SEric W. Biederman return -EINVAL; 1579966bae33SEric W. Biederman 15805a9ab017SRobert Shearman switch (dec.label) { 158178f5b899STom Herbert case MPLS_LABEL_IMPLNULL: 15825a9ab017SRobert Shearman /* RFC3032: This is a label that an LSR may 15835a9ab017SRobert Shearman * assign and distribute, but which never 15845a9ab017SRobert Shearman * actually appears in the encapsulation. 15855a9ab017SRobert Shearman */ 15865a9ab017SRobert Shearman return -EINVAL; 15875a9ab017SRobert Shearman } 15885a9ab017SRobert Shearman 1589966bae33SEric W. Biederman label[i] = dec.label; 1590966bae33SEric W. Biederman } 1591a4ac8c98SDavid Ahern out: 1592966bae33SEric W. Biederman *labels = nla_labels; 1593966bae33SEric W. Biederman return 0; 1594966bae33SEric W. Biederman } 1595face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_get_labels); 1596966bae33SEric W. Biederman 1597f8efb73cSRoopa Prabhu int nla_get_via(const struct nlattr *nla, u8 *via_alen, 1598f8efb73cSRoopa Prabhu u8 *via_table, u8 via_addr[]) 1599f8efb73cSRoopa Prabhu { 1600f8efb73cSRoopa Prabhu struct rtvia *via = nla_data(nla); 1601f8efb73cSRoopa Prabhu int err = -EINVAL; 1602f8efb73cSRoopa Prabhu int alen; 1603f8efb73cSRoopa Prabhu 1604f8efb73cSRoopa Prabhu if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) 1605f8efb73cSRoopa Prabhu goto errout; 1606f8efb73cSRoopa Prabhu alen = nla_len(nla) - 1607f8efb73cSRoopa Prabhu offsetof(struct rtvia, rtvia_addr); 1608f8efb73cSRoopa Prabhu if (alen > MAX_VIA_ALEN) 1609f8efb73cSRoopa Prabhu goto errout; 1610f8efb73cSRoopa Prabhu 1611f8efb73cSRoopa Prabhu /* Validate the address family */ 1612f8efb73cSRoopa Prabhu switch (via->rtvia_family) { 1613f8efb73cSRoopa Prabhu case AF_PACKET: 1614f8efb73cSRoopa Prabhu *via_table = NEIGH_LINK_TABLE; 1615f8efb73cSRoopa Prabhu break; 1616f8efb73cSRoopa Prabhu case AF_INET: 1617f8efb73cSRoopa Prabhu *via_table = NEIGH_ARP_TABLE; 1618f8efb73cSRoopa Prabhu if (alen != 4) 1619f8efb73cSRoopa Prabhu goto errout; 1620f8efb73cSRoopa Prabhu break; 1621f8efb73cSRoopa Prabhu case AF_INET6: 1622f8efb73cSRoopa Prabhu *via_table = NEIGH_ND_TABLE; 1623f8efb73cSRoopa Prabhu if (alen != 16) 1624f8efb73cSRoopa Prabhu goto errout; 1625f8efb73cSRoopa Prabhu break; 1626f8efb73cSRoopa Prabhu default: 1627f8efb73cSRoopa Prabhu /* Unsupported address family */ 1628f8efb73cSRoopa Prabhu goto errout; 1629f8efb73cSRoopa Prabhu } 1630f8efb73cSRoopa Prabhu 1631f8efb73cSRoopa Prabhu memcpy(via_addr, via->rtvia_addr, alen); 1632f8efb73cSRoopa Prabhu *via_alen = alen; 1633f8efb73cSRoopa Prabhu err = 0; 1634f8efb73cSRoopa Prabhu 1635f8efb73cSRoopa Prabhu errout: 1636f8efb73cSRoopa Prabhu return err; 1637f8efb73cSRoopa Prabhu } 1638f8efb73cSRoopa Prabhu 163903c05665SEric W. Biederman static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, 164003c05665SEric W. Biederman struct mpls_route_config *cfg) 164103c05665SEric W. Biederman { 164203c05665SEric W. Biederman struct rtmsg *rtm; 164303c05665SEric W. Biederman struct nlattr *tb[RTA_MAX+1]; 164403c05665SEric W. Biederman int index; 164503c05665SEric W. Biederman int err; 164603c05665SEric W. Biederman 1647fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_mpls_policy, 1648fceb6435SJohannes Berg NULL); 164903c05665SEric W. Biederman if (err < 0) 165003c05665SEric W. Biederman goto errout; 165103c05665SEric W. Biederman 165203c05665SEric W. Biederman err = -EINVAL; 165303c05665SEric W. Biederman rtm = nlmsg_data(nlh); 165403c05665SEric W. Biederman 165503c05665SEric W. Biederman if (rtm->rtm_family != AF_MPLS) 165603c05665SEric W. Biederman goto errout; 165703c05665SEric W. Biederman if (rtm->rtm_dst_len != 20) 165803c05665SEric W. Biederman goto errout; 165903c05665SEric W. Biederman if (rtm->rtm_src_len != 0) 166003c05665SEric W. Biederman goto errout; 166103c05665SEric W. Biederman if (rtm->rtm_tos != 0) 166203c05665SEric W. Biederman goto errout; 166303c05665SEric W. Biederman if (rtm->rtm_table != RT_TABLE_MAIN) 166403c05665SEric W. Biederman goto errout; 166503c05665SEric W. Biederman /* Any value is acceptable for rtm_protocol */ 166603c05665SEric W. Biederman 166703c05665SEric W. Biederman /* As mpls uses destination specific addresses 166803c05665SEric W. Biederman * (or source specific address in the case of multicast) 166903c05665SEric W. Biederman * all addresses have universal scope. 167003c05665SEric W. Biederman */ 167103c05665SEric W. Biederman if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) 167203c05665SEric W. Biederman goto errout; 167303c05665SEric W. Biederman if (rtm->rtm_type != RTN_UNICAST) 167403c05665SEric W. Biederman goto errout; 167503c05665SEric W. Biederman if (rtm->rtm_flags != 0) 167603c05665SEric W. Biederman goto errout; 167703c05665SEric W. Biederman 167803c05665SEric W. Biederman cfg->rc_label = LABEL_NOT_SPECIFIED; 167903c05665SEric W. Biederman cfg->rc_protocol = rtm->rtm_protocol; 1680eb7809f0SRobert Shearman cfg->rc_via_table = MPLS_NEIGH_TABLE_UNSPEC; 16815b441ac8SRobert Shearman cfg->rc_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 168203c05665SEric W. Biederman cfg->rc_nlflags = nlh->nlmsg_flags; 168303c05665SEric W. Biederman cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; 168403c05665SEric W. Biederman cfg->rc_nlinfo.nlh = nlh; 168503c05665SEric W. Biederman cfg->rc_nlinfo.nl_net = sock_net(skb->sk); 168603c05665SEric W. Biederman 168703c05665SEric W. Biederman for (index = 0; index <= RTA_MAX; index++) { 168803c05665SEric W. Biederman struct nlattr *nla = tb[index]; 168903c05665SEric W. Biederman if (!nla) 169003c05665SEric W. Biederman continue; 169103c05665SEric W. Biederman 169203c05665SEric W. Biederman switch (index) { 169303c05665SEric W. Biederman case RTA_OIF: 169403c05665SEric W. Biederman cfg->rc_ifindex = nla_get_u32(nla); 169503c05665SEric W. Biederman break; 169603c05665SEric W. Biederman case RTA_NEWDST: 169703c05665SEric W. Biederman if (nla_get_labels(nla, MAX_NEW_LABELS, 169803c05665SEric W. Biederman &cfg->rc_output_labels, 169903c05665SEric W. Biederman cfg->rc_output_label)) 170003c05665SEric W. Biederman goto errout; 170103c05665SEric W. Biederman break; 170203c05665SEric W. Biederman case RTA_DST: 170303c05665SEric W. Biederman { 1704f8efb73cSRoopa Prabhu u8 label_count; 170503c05665SEric W. Biederman if (nla_get_labels(nla, 1, &label_count, 170603c05665SEric W. Biederman &cfg->rc_label)) 170703c05665SEric W. Biederman goto errout; 170803c05665SEric W. Biederman 1709a6affd24SRobert Shearman /* Reserved labels may not be set */ 1710a6affd24SRobert Shearman if (cfg->rc_label < MPLS_LABEL_FIRST_UNRESERVED) 171103c05665SEric W. Biederman goto errout; 171203c05665SEric W. Biederman 171303c05665SEric W. Biederman break; 171403c05665SEric W. Biederman } 171503c05665SEric W. Biederman case RTA_VIA: 171603c05665SEric W. Biederman { 1717f8efb73cSRoopa Prabhu if (nla_get_via(nla, &cfg->rc_via_alen, 1718f8efb73cSRoopa Prabhu &cfg->rc_via_table, cfg->rc_via)) 171903c05665SEric W. Biederman goto errout; 172003c05665SEric W. Biederman break; 172103c05665SEric W. Biederman } 1722f8efb73cSRoopa Prabhu case RTA_MULTIPATH: 1723f8efb73cSRoopa Prabhu { 1724f8efb73cSRoopa Prabhu cfg->rc_mp = nla_data(nla); 1725f8efb73cSRoopa Prabhu cfg->rc_mp_len = nla_len(nla); 172603c05665SEric W. Biederman break; 172703c05665SEric W. Biederman } 17285b441ac8SRobert Shearman case RTA_TTL_PROPAGATE: 17295b441ac8SRobert Shearman { 17305b441ac8SRobert Shearman u8 ttl_propagate = nla_get_u8(nla); 17315b441ac8SRobert Shearman 17325b441ac8SRobert Shearman if (ttl_propagate > 1) 17335b441ac8SRobert Shearman goto errout; 17345b441ac8SRobert Shearman cfg->rc_ttl_propagate = ttl_propagate ? 17355b441ac8SRobert Shearman MPLS_TTL_PROP_ENABLED : 17365b441ac8SRobert Shearman MPLS_TTL_PROP_DISABLED; 17375b441ac8SRobert Shearman break; 17385b441ac8SRobert Shearman } 173903c05665SEric W. Biederman default: 174003c05665SEric W. Biederman /* Unsupported attribute */ 174103c05665SEric W. Biederman goto errout; 174203c05665SEric W. Biederman } 174303c05665SEric W. Biederman } 174403c05665SEric W. Biederman 174503c05665SEric W. Biederman err = 0; 174603c05665SEric W. Biederman errout: 174703c05665SEric W. Biederman return err; 174803c05665SEric W. Biederman } 174903c05665SEric W. Biederman 1750c21ef3e3SDavid Ahern static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1751c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 175203c05665SEric W. Biederman { 1753a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 175403c05665SEric W. Biederman int err; 175503c05665SEric W. Biederman 1756a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1757a4ac8c98SDavid Ahern if (!cfg) 1758a4ac8c98SDavid Ahern return -ENOMEM; 175903c05665SEric W. Biederman 1760a4ac8c98SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg); 1761a4ac8c98SDavid Ahern if (err < 0) 1762a4ac8c98SDavid Ahern goto out; 1763a4ac8c98SDavid Ahern 1764a4ac8c98SDavid Ahern err = mpls_route_del(cfg); 1765a4ac8c98SDavid Ahern out: 1766a4ac8c98SDavid Ahern kfree(cfg); 1767a4ac8c98SDavid Ahern 1768a4ac8c98SDavid Ahern return err; 176903c05665SEric W. Biederman } 177003c05665SEric W. Biederman 177103c05665SEric W. Biederman 1772c21ef3e3SDavid Ahern static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1773c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 177403c05665SEric W. Biederman { 1775a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 177603c05665SEric W. Biederman int err; 177703c05665SEric W. Biederman 1778a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1779a4ac8c98SDavid Ahern if (!cfg) 1780a4ac8c98SDavid Ahern return -ENOMEM; 178103c05665SEric W. Biederman 1782a4ac8c98SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg); 1783a4ac8c98SDavid Ahern if (err < 0) 1784a4ac8c98SDavid Ahern goto out; 1785a4ac8c98SDavid Ahern 1786a4ac8c98SDavid Ahern err = mpls_route_add(cfg); 1787a4ac8c98SDavid Ahern out: 1788a4ac8c98SDavid Ahern kfree(cfg); 1789a4ac8c98SDavid Ahern 1790a4ac8c98SDavid Ahern return err; 179103c05665SEric W. Biederman } 179203c05665SEric W. Biederman 179303c05665SEric W. Biederman static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, 179403c05665SEric W. Biederman u32 label, struct mpls_route *rt, int flags) 179503c05665SEric W. Biederman { 179619d0c341SEric W. Biederman struct net_device *dev; 179703c05665SEric W. Biederman struct nlmsghdr *nlh; 179803c05665SEric W. Biederman struct rtmsg *rtm; 179903c05665SEric W. Biederman 180003c05665SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); 180103c05665SEric W. Biederman if (nlh == NULL) 180203c05665SEric W. Biederman return -EMSGSIZE; 180303c05665SEric W. Biederman 180403c05665SEric W. Biederman rtm = nlmsg_data(nlh); 180503c05665SEric W. Biederman rtm->rtm_family = AF_MPLS; 180603c05665SEric W. Biederman rtm->rtm_dst_len = 20; 180703c05665SEric W. Biederman rtm->rtm_src_len = 0; 180803c05665SEric W. Biederman rtm->rtm_tos = 0; 180903c05665SEric W. Biederman rtm->rtm_table = RT_TABLE_MAIN; 181003c05665SEric W. Biederman rtm->rtm_protocol = rt->rt_protocol; 181103c05665SEric W. Biederman rtm->rtm_scope = RT_SCOPE_UNIVERSE; 181203c05665SEric W. Biederman rtm->rtm_type = RTN_UNICAST; 181303c05665SEric W. Biederman rtm->rtm_flags = 0; 181403c05665SEric W. Biederman 181503c05665SEric W. Biederman if (nla_put_labels(skb, RTA_DST, 1, &label)) 181603c05665SEric W. Biederman goto nla_put_failure; 18175b441ac8SRobert Shearman 18185b441ac8SRobert Shearman if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) { 18195b441ac8SRobert Shearman bool ttl_propagate = 18205b441ac8SRobert Shearman rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED; 18215b441ac8SRobert Shearman 18225b441ac8SRobert Shearman if (nla_put_u8(skb, RTA_TTL_PROPAGATE, 18235b441ac8SRobert Shearman ttl_propagate)) 18245b441ac8SRobert Shearman goto nla_put_failure; 18255b441ac8SRobert Shearman } 1826f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 1827cf4b24f0SRobert Shearman const struct mpls_nh *nh = rt->rt_nh; 1828f8efb73cSRoopa Prabhu 1829f8efb73cSRoopa Prabhu if (nh->nh_labels && 1830f8efb73cSRoopa Prabhu nla_put_labels(skb, RTA_NEWDST, nh->nh_labels, 1831f8efb73cSRoopa Prabhu nh->nh_label)) 1832f8efb73cSRoopa Prabhu goto nla_put_failure; 1833eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 183472dcac96SRobert Shearman nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), 1835f8efb73cSRoopa Prabhu nh->nh_via_alen)) 1836f8efb73cSRoopa Prabhu goto nla_put_failure; 1837f8efb73cSRoopa Prabhu dev = rtnl_dereference(nh->nh_dev); 1838f8efb73cSRoopa Prabhu if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) 1839f8efb73cSRoopa Prabhu goto nla_put_failure; 1840c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) 1841c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 1842c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) 1843c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 1844f8efb73cSRoopa Prabhu } else { 1845f8efb73cSRoopa Prabhu struct rtnexthop *rtnh; 1846f8efb73cSRoopa Prabhu struct nlattr *mp; 184777ef013aSDavid Ahern u8 linkdown = 0; 184877ef013aSDavid Ahern u8 dead = 0; 1849f8efb73cSRoopa Prabhu 1850f8efb73cSRoopa Prabhu mp = nla_nest_start(skb, RTA_MULTIPATH); 1851f8efb73cSRoopa Prabhu if (!mp) 1852f8efb73cSRoopa Prabhu goto nla_put_failure; 1853f8efb73cSRoopa Prabhu 1854f8efb73cSRoopa Prabhu for_nexthops(rt) { 1855c00e51ddSDavid Ahern dev = rtnl_dereference(nh->nh_dev); 1856c00e51ddSDavid Ahern if (!dev) 1857c00e51ddSDavid Ahern continue; 1858c00e51ddSDavid Ahern 1859f8efb73cSRoopa Prabhu rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 1860f8efb73cSRoopa Prabhu if (!rtnh) 1861f8efb73cSRoopa Prabhu goto nla_put_failure; 1862f8efb73cSRoopa Prabhu 1863f8efb73cSRoopa Prabhu rtnh->rtnh_ifindex = dev->ifindex; 1864c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) { 1865c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_LINKDOWN; 1866c89359a4SRoopa Prabhu linkdown++; 1867c89359a4SRoopa Prabhu } 1868c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) { 1869c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_DEAD; 1870c89359a4SRoopa Prabhu dead++; 1871c89359a4SRoopa Prabhu } 1872c89359a4SRoopa Prabhu 1873f8efb73cSRoopa Prabhu if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST, 1874f8efb73cSRoopa Prabhu nh->nh_labels, 1875f8efb73cSRoopa Prabhu nh->nh_label)) 1876f8efb73cSRoopa Prabhu goto nla_put_failure; 1877f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 1878f20367dfSRobert Shearman nla_put_via(skb, nh->nh_via_table, 1879cf4b24f0SRobert Shearman mpls_nh_via(rt, nh), 1880f8efb73cSRoopa Prabhu nh->nh_via_alen)) 1881f8efb73cSRoopa Prabhu goto nla_put_failure; 1882f8efb73cSRoopa Prabhu 1883f8efb73cSRoopa Prabhu /* length of rtnetlink header + attributes */ 1884f8efb73cSRoopa Prabhu rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 1885f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 1886f8efb73cSRoopa Prabhu 1887c89359a4SRoopa Prabhu if (linkdown == rt->rt_nhn) 1888c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 1889c89359a4SRoopa Prabhu if (dead == rt->rt_nhn) 1890c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 1891c89359a4SRoopa Prabhu 1892f8efb73cSRoopa Prabhu nla_nest_end(skb, mp); 1893f8efb73cSRoopa Prabhu } 189403c05665SEric W. Biederman 189503c05665SEric W. Biederman nlmsg_end(skb, nlh); 189603c05665SEric W. Biederman return 0; 189703c05665SEric W. Biederman 189803c05665SEric W. Biederman nla_put_failure: 189903c05665SEric W. Biederman nlmsg_cancel(skb, nlh); 190003c05665SEric W. Biederman return -EMSGSIZE; 190103c05665SEric W. Biederman } 190203c05665SEric W. Biederman 190303c05665SEric W. Biederman static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) 190403c05665SEric W. Biederman { 190503c05665SEric W. Biederman struct net *net = sock_net(skb->sk); 190619d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 190719d0c341SEric W. Biederman size_t platform_labels; 190803c05665SEric W. Biederman unsigned int index; 190903c05665SEric W. Biederman 191003c05665SEric W. Biederman ASSERT_RTNL(); 191103c05665SEric W. Biederman 191203c05665SEric W. Biederman index = cb->args[0]; 1913a6affd24SRobert Shearman if (index < MPLS_LABEL_FIRST_UNRESERVED) 1914a6affd24SRobert Shearman index = MPLS_LABEL_FIRST_UNRESERVED; 191503c05665SEric W. Biederman 191619d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 191719d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 191819d0c341SEric W. Biederman for (; index < platform_labels; index++) { 191903c05665SEric W. Biederman struct mpls_route *rt; 192019d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 192103c05665SEric W. Biederman if (!rt) 192203c05665SEric W. Biederman continue; 192303c05665SEric W. Biederman 192403c05665SEric W. Biederman if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid, 192503c05665SEric W. Biederman cb->nlh->nlmsg_seq, RTM_NEWROUTE, 192603c05665SEric W. Biederman index, rt, NLM_F_MULTI) < 0) 192703c05665SEric W. Biederman break; 192803c05665SEric W. Biederman } 192903c05665SEric W. Biederman cb->args[0] = index; 193003c05665SEric W. Biederman 193103c05665SEric W. Biederman return skb->len; 193203c05665SEric W. Biederman } 193303c05665SEric W. Biederman 19348de147dcSEric W. Biederman static inline size_t lfib_nlmsg_size(struct mpls_route *rt) 19358de147dcSEric W. Biederman { 19368de147dcSEric W. Biederman size_t payload = 19378de147dcSEric W. Biederman NLMSG_ALIGN(sizeof(struct rtmsg)) 19385b441ac8SRobert Shearman + nla_total_size(4) /* RTA_DST */ 19395b441ac8SRobert Shearman + nla_total_size(1); /* RTA_TTL_PROPAGATE */ 1940f8efb73cSRoopa Prabhu 1941f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 1942f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 1943f8efb73cSRoopa Prabhu 1944f8efb73cSRoopa Prabhu if (nh->nh_dev) 1945f8efb73cSRoopa Prabhu payload += nla_total_size(4); /* RTA_OIF */ 1946eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */ 194772dcac96SRobert Shearman payload += nla_total_size(2 + nh->nh_via_alen); 1948f8efb73cSRoopa Prabhu if (nh->nh_labels) /* RTA_NEWDST */ 1949f8efb73cSRoopa Prabhu payload += nla_total_size(nh->nh_labels * 4); 1950f8efb73cSRoopa Prabhu } else { 1951f8efb73cSRoopa Prabhu /* each nexthop is packed in an attribute */ 1952f8efb73cSRoopa Prabhu size_t nhsize = 0; 1953f8efb73cSRoopa Prabhu 1954f8efb73cSRoopa Prabhu for_nexthops(rt) { 1955e944e97aSDavid Ahern if (!rtnl_dereference(nh->nh_dev)) 1956e944e97aSDavid Ahern continue; 1957f8efb73cSRoopa Prabhu nhsize += nla_total_size(sizeof(struct rtnexthop)); 1958f20367dfSRobert Shearman /* RTA_VIA */ 1959f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) 1960f8efb73cSRoopa Prabhu nhsize += nla_total_size(2 + nh->nh_via_alen); 1961f8efb73cSRoopa Prabhu if (nh->nh_labels) 1962f8efb73cSRoopa Prabhu nhsize += nla_total_size(nh->nh_labels * 4); 1963f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 1964f8efb73cSRoopa Prabhu /* nested attribute */ 1965f8efb73cSRoopa Prabhu payload += nla_total_size(nhsize); 1966f8efb73cSRoopa Prabhu } 1967f8efb73cSRoopa Prabhu 19688de147dcSEric W. Biederman return payload; 19698de147dcSEric W. Biederman } 19708de147dcSEric W. Biederman 19718de147dcSEric W. Biederman static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, 19728de147dcSEric W. Biederman struct nlmsghdr *nlh, struct net *net, u32 portid, 19738de147dcSEric W. Biederman unsigned int nlm_flags) 19748de147dcSEric W. Biederman { 19758de147dcSEric W. Biederman struct sk_buff *skb; 19768de147dcSEric W. Biederman u32 seq = nlh ? nlh->nlmsg_seq : 0; 19778de147dcSEric W. Biederman int err = -ENOBUFS; 19788de147dcSEric W. Biederman 19798de147dcSEric W. Biederman skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); 19808de147dcSEric W. Biederman if (skb == NULL) 19818de147dcSEric W. Biederman goto errout; 19828de147dcSEric W. Biederman 19838de147dcSEric W. Biederman err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags); 19848de147dcSEric W. Biederman if (err < 0) { 19858de147dcSEric W. Biederman /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ 19868de147dcSEric W. Biederman WARN_ON(err == -EMSGSIZE); 19878de147dcSEric W. Biederman kfree_skb(skb); 19888de147dcSEric W. Biederman goto errout; 19898de147dcSEric W. Biederman } 19908de147dcSEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL); 19918de147dcSEric W. Biederman 19928de147dcSEric W. Biederman return; 19938de147dcSEric W. Biederman errout: 19948de147dcSEric W. Biederman if (err < 0) 19958de147dcSEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err); 19968de147dcSEric W. Biederman } 19978de147dcSEric W. Biederman 19987720c01fSEric W. Biederman static int resize_platform_label_table(struct net *net, size_t limit) 19997720c01fSEric W. Biederman { 20007720c01fSEric W. Biederman size_t size = sizeof(struct mpls_route *) * limit; 20017720c01fSEric W. Biederman size_t old_limit; 20027720c01fSEric W. Biederman size_t cp_size; 20037720c01fSEric W. Biederman struct mpls_route __rcu **labels = NULL, **old; 20047720c01fSEric W. Biederman struct mpls_route *rt0 = NULL, *rt2 = NULL; 20057720c01fSEric W. Biederman unsigned index; 20067720c01fSEric W. Biederman 20077720c01fSEric W. Biederman if (size) { 2008752ade68SMichal Hocko labels = kvzalloc(size, GFP_KERNEL); 20097720c01fSEric W. Biederman if (!labels) 20107720c01fSEric W. Biederman goto nolabels; 20117720c01fSEric W. Biederman } 20127720c01fSEric W. Biederman 20137720c01fSEric W. Biederman /* In case the predefined labels need to be populated */ 201478f5b899STom Herbert if (limit > MPLS_LABEL_IPV4NULL) { 20157720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2016a4ac8c98SDavid Ahern rt0 = mpls_rt_alloc(1, lo->addr_len, 0); 2017df1c6316SDavid Ahern if (IS_ERR(rt0)) 20187720c01fSEric W. Biederman goto nort0; 2019f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); 20207720c01fSEric W. Biederman rt0->rt_protocol = RTPROT_KERNEL; 2021118d5234SRobert Shearman rt0->rt_payload_type = MPT_IPV4; 20225b441ac8SRobert Shearman rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2023f8efb73cSRoopa Prabhu rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2024b4e04fc7SRobert Shearman rt0->rt_nh->nh_via_alen = lo->addr_len; 2025cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr, 2026cf4b24f0SRobert Shearman lo->addr_len); 20277720c01fSEric W. Biederman } 202878f5b899STom Herbert if (limit > MPLS_LABEL_IPV6NULL) { 20297720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2030a4ac8c98SDavid Ahern rt2 = mpls_rt_alloc(1, lo->addr_len, 0); 2031df1c6316SDavid Ahern if (IS_ERR(rt2)) 20327720c01fSEric W. Biederman goto nort2; 2033f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); 20347720c01fSEric W. Biederman rt2->rt_protocol = RTPROT_KERNEL; 2035118d5234SRobert Shearman rt2->rt_payload_type = MPT_IPV6; 20366a18c312SDavid Ahern rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2037f8efb73cSRoopa Prabhu rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2038b4e04fc7SRobert Shearman rt2->rt_nh->nh_via_alen = lo->addr_len; 2039cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr, 2040cf4b24f0SRobert Shearman lo->addr_len); 20417720c01fSEric W. Biederman } 20427720c01fSEric W. Biederman 20437720c01fSEric W. Biederman rtnl_lock(); 20447720c01fSEric W. Biederman /* Remember the original table */ 204519d0c341SEric W. Biederman old = rtnl_dereference(net->mpls.platform_label); 20467720c01fSEric W. Biederman old_limit = net->mpls.platform_labels; 20477720c01fSEric W. Biederman 20487720c01fSEric W. Biederman /* Free any labels beyond the new table */ 20497720c01fSEric W. Biederman for (index = limit; index < old_limit; index++) 2050f8efb73cSRoopa Prabhu mpls_route_update(net, index, NULL, NULL); 20517720c01fSEric W. Biederman 20527720c01fSEric W. Biederman /* Copy over the old labels */ 20537720c01fSEric W. Biederman cp_size = size; 20547720c01fSEric W. Biederman if (old_limit < limit) 20557720c01fSEric W. Biederman cp_size = old_limit * sizeof(struct mpls_route *); 20567720c01fSEric W. Biederman 20577720c01fSEric W. Biederman memcpy(labels, old, cp_size); 20587720c01fSEric W. Biederman 20597720c01fSEric W. Biederman /* If needed set the predefined labels */ 206078f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV6NULL) && 206178f5b899STom Herbert (limit > MPLS_LABEL_IPV6NULL)) { 206278f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2); 20637720c01fSEric W. Biederman rt2 = NULL; 20647720c01fSEric W. Biederman } 20657720c01fSEric W. Biederman 206678f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV4NULL) && 206778f5b899STom Herbert (limit > MPLS_LABEL_IPV4NULL)) { 206878f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0); 20697720c01fSEric W. Biederman rt0 = NULL; 20707720c01fSEric W. Biederman } 20717720c01fSEric W. Biederman 20727720c01fSEric W. Biederman /* Update the global pointers */ 20737720c01fSEric W. Biederman net->mpls.platform_labels = limit; 207419d0c341SEric W. Biederman rcu_assign_pointer(net->mpls.platform_label, labels); 20757720c01fSEric W. Biederman 20767720c01fSEric W. Biederman rtnl_unlock(); 20777720c01fSEric W. Biederman 20787720c01fSEric W. Biederman mpls_rt_free(rt2); 20797720c01fSEric W. Biederman mpls_rt_free(rt0); 20807720c01fSEric W. Biederman 20817720c01fSEric W. Biederman if (old) { 20827720c01fSEric W. Biederman synchronize_rcu(); 20837720c01fSEric W. Biederman kvfree(old); 20847720c01fSEric W. Biederman } 20857720c01fSEric W. Biederman return 0; 20867720c01fSEric W. Biederman 20877720c01fSEric W. Biederman nort2: 20887720c01fSEric W. Biederman mpls_rt_free(rt0); 20897720c01fSEric W. Biederman nort0: 20907720c01fSEric W. Biederman kvfree(labels); 20917720c01fSEric W. Biederman nolabels: 20927720c01fSEric W. Biederman return -ENOMEM; 20937720c01fSEric W. Biederman } 20947720c01fSEric W. Biederman 20957720c01fSEric W. Biederman static int mpls_platform_labels(struct ctl_table *table, int write, 20967720c01fSEric W. Biederman void __user *buffer, size_t *lenp, loff_t *ppos) 20977720c01fSEric W. Biederman { 20987720c01fSEric W. Biederman struct net *net = table->data; 20997720c01fSEric W. Biederman int platform_labels = net->mpls.platform_labels; 21007720c01fSEric W. Biederman int ret; 21017720c01fSEric W. Biederman struct ctl_table tmp = { 21027720c01fSEric W. Biederman .procname = table->procname, 21037720c01fSEric W. Biederman .data = &platform_labels, 21047720c01fSEric W. Biederman .maxlen = sizeof(int), 21057720c01fSEric W. Biederman .mode = table->mode, 21067720c01fSEric W. Biederman .extra1 = &zero, 21077720c01fSEric W. Biederman .extra2 = &label_limit, 21087720c01fSEric W. Biederman }; 21097720c01fSEric W. Biederman 21107720c01fSEric W. Biederman ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 21117720c01fSEric W. Biederman 21127720c01fSEric W. Biederman if (write && ret == 0) 21137720c01fSEric W. Biederman ret = resize_platform_label_table(net, platform_labels); 21147720c01fSEric W. Biederman 21157720c01fSEric W. Biederman return ret; 21167720c01fSEric W. Biederman } 21177720c01fSEric W. Biederman 21185b441ac8SRobert Shearman #define MPLS_NS_SYSCTL_OFFSET(field) \ 21195b441ac8SRobert Shearman (&((struct net *)0)->field) 21205b441ac8SRobert Shearman 212137bde799SRobert Shearman static const struct ctl_table mpls_table[] = { 21227720c01fSEric W. Biederman { 21237720c01fSEric W. Biederman .procname = "platform_labels", 21247720c01fSEric W. Biederman .data = NULL, 21257720c01fSEric W. Biederman .maxlen = sizeof(int), 21267720c01fSEric W. Biederman .mode = 0644, 21277720c01fSEric W. Biederman .proc_handler = mpls_platform_labels, 21287720c01fSEric W. Biederman }, 21295b441ac8SRobert Shearman { 21305b441ac8SRobert Shearman .procname = "ip_ttl_propagate", 21315b441ac8SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate), 21325b441ac8SRobert Shearman .maxlen = sizeof(int), 21335b441ac8SRobert Shearman .mode = 0644, 21345b441ac8SRobert Shearman .proc_handler = proc_dointvec_minmax, 21355b441ac8SRobert Shearman .extra1 = &zero, 21365b441ac8SRobert Shearman .extra2 = &one, 21375b441ac8SRobert Shearman }, 2138a59166e4SRobert Shearman { 2139a59166e4SRobert Shearman .procname = "default_ttl", 2140a59166e4SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl), 2141a59166e4SRobert Shearman .maxlen = sizeof(int), 2142a59166e4SRobert Shearman .mode = 0644, 2143a59166e4SRobert Shearman .proc_handler = proc_dointvec_minmax, 2144a59166e4SRobert Shearman .extra1 = &one, 2145a59166e4SRobert Shearman .extra2 = &ttl_max, 2146a59166e4SRobert Shearman }, 21477720c01fSEric W. Biederman { } 21487720c01fSEric W. Biederman }; 21497720c01fSEric W. Biederman 21500189197fSEric W. Biederman static int mpls_net_init(struct net *net) 21510189197fSEric W. Biederman { 21527720c01fSEric W. Biederman struct ctl_table *table; 21535b441ac8SRobert Shearman int i; 21547720c01fSEric W. Biederman 21550189197fSEric W. Biederman net->mpls.platform_labels = 0; 21560189197fSEric W. Biederman net->mpls.platform_label = NULL; 21575b441ac8SRobert Shearman net->mpls.ip_ttl_propagate = 1; 2158a59166e4SRobert Shearman net->mpls.default_ttl = 255; 21590189197fSEric W. Biederman 21607720c01fSEric W. Biederman table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); 21617720c01fSEric W. Biederman if (table == NULL) 21627720c01fSEric W. Biederman return -ENOMEM; 21637720c01fSEric W. Biederman 21645b441ac8SRobert Shearman /* Table data contains only offsets relative to the base of 21655b441ac8SRobert Shearman * the mdev at this point, so make them absolute. 21665b441ac8SRobert Shearman */ 21675b441ac8SRobert Shearman for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++) 21685b441ac8SRobert Shearman table[i].data = (char *)net + (uintptr_t)table[i].data; 21695b441ac8SRobert Shearman 21707720c01fSEric W. Biederman net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); 21716ea3c9d5SNikolay Aleksandrov if (net->mpls.ctl == NULL) { 21726ea3c9d5SNikolay Aleksandrov kfree(table); 21737720c01fSEric W. Biederman return -ENOMEM; 21746ea3c9d5SNikolay Aleksandrov } 21757720c01fSEric W. Biederman 21760189197fSEric W. Biederman return 0; 21770189197fSEric W. Biederman } 21780189197fSEric W. Biederman 21790189197fSEric W. Biederman static void mpls_net_exit(struct net *net) 21800189197fSEric W. Biederman { 218119d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 218219d0c341SEric W. Biederman size_t platform_labels; 21837720c01fSEric W. Biederman struct ctl_table *table; 21840189197fSEric W. Biederman unsigned int index; 21850189197fSEric W. Biederman 21867720c01fSEric W. Biederman table = net->mpls.ctl->ctl_table_arg; 21877720c01fSEric W. Biederman unregister_net_sysctl_table(net->mpls.ctl); 21887720c01fSEric W. Biederman kfree(table); 21897720c01fSEric W. Biederman 219019d0c341SEric W. Biederman /* An rcu grace period has passed since there was a device in 219119d0c341SEric W. Biederman * the network namespace (and thus the last in flight packet) 21920189197fSEric W. Biederman * left this network namespace. This is because 21930189197fSEric W. Biederman * unregister_netdevice_many and netdev_run_todo has completed 21940189197fSEric W. Biederman * for each network device that was in this network namespace. 21950189197fSEric W. Biederman * 21960189197fSEric W. Biederman * As such no additional rcu synchronization is necessary when 21970189197fSEric W. Biederman * freeing the platform_label table. 21980189197fSEric W. Biederman */ 21990189197fSEric W. Biederman rtnl_lock(); 220019d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 220119d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 220219d0c341SEric W. Biederman for (index = 0; index < platform_labels; index++) { 220319d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 220419d0c341SEric W. Biederman RCU_INIT_POINTER(platform_label[index], NULL); 2205e37791ecSDavid Ahern mpls_notify_route(net, index, rt, NULL, NULL); 22060189197fSEric W. Biederman mpls_rt_free(rt); 22070189197fSEric W. Biederman } 22080189197fSEric W. Biederman rtnl_unlock(); 22090189197fSEric W. Biederman 221019d0c341SEric W. Biederman kvfree(platform_label); 22110189197fSEric W. Biederman } 22120189197fSEric W. Biederman 22130189197fSEric W. Biederman static struct pernet_operations mpls_net_ops = { 22140189197fSEric W. Biederman .init = mpls_net_init, 22150189197fSEric W. Biederman .exit = mpls_net_exit, 22160189197fSEric W. Biederman }; 22170189197fSEric W. Biederman 221827d69105SRobert Shearman static struct rtnl_af_ops mpls_af_ops __read_mostly = { 221927d69105SRobert Shearman .family = AF_MPLS, 222027d69105SRobert Shearman .fill_stats_af = mpls_fill_stats_af, 222127d69105SRobert Shearman .get_stats_af_size = mpls_get_stats_af_size, 222227d69105SRobert Shearman }; 222327d69105SRobert Shearman 22240189197fSEric W. Biederman static int __init mpls_init(void) 22250189197fSEric W. Biederman { 22260189197fSEric W. Biederman int err; 22270189197fSEric W. Biederman 22280189197fSEric W. Biederman BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4); 22290189197fSEric W. Biederman 22300189197fSEric W. Biederman err = register_pernet_subsys(&mpls_net_ops); 22310189197fSEric W. Biederman if (err) 22320189197fSEric W. Biederman goto out; 22330189197fSEric W. Biederman 22340189197fSEric W. Biederman err = register_netdevice_notifier(&mpls_dev_notifier); 22350189197fSEric W. Biederman if (err) 22360189197fSEric W. Biederman goto out_unregister_pernet; 22370189197fSEric W. Biederman 22380189197fSEric W. Biederman dev_add_pack(&mpls_packet_type); 22390189197fSEric W. Biederman 224027d69105SRobert Shearman rtnl_af_register(&mpls_af_ops); 224127d69105SRobert Shearman 224203c05665SEric W. Biederman rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, NULL); 224303c05665SEric W. Biederman rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, NULL); 224403c05665SEric W. Biederman rtnl_register(PF_MPLS, RTM_GETROUTE, NULL, mpls_dump_routes, NULL); 224524045a03SDavid Ahern rtnl_register(PF_MPLS, RTM_GETNETCONF, mpls_netconf_get_devconf, 224624045a03SDavid Ahern mpls_netconf_dump_devconf, NULL); 22470189197fSEric W. Biederman err = 0; 22480189197fSEric W. Biederman out: 22490189197fSEric W. Biederman return err; 22500189197fSEric W. Biederman 22510189197fSEric W. Biederman out_unregister_pernet: 22520189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 22530189197fSEric W. Biederman goto out; 22540189197fSEric W. Biederman } 22550189197fSEric W. Biederman module_init(mpls_init); 22560189197fSEric W. Biederman 22570189197fSEric W. Biederman static void __exit mpls_exit(void) 22580189197fSEric W. Biederman { 225903c05665SEric W. Biederman rtnl_unregister_all(PF_MPLS); 226027d69105SRobert Shearman rtnl_af_unregister(&mpls_af_ops); 22610189197fSEric W. Biederman dev_remove_pack(&mpls_packet_type); 22620189197fSEric W. Biederman unregister_netdevice_notifier(&mpls_dev_notifier); 22630189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 22640189197fSEric W. Biederman } 22650189197fSEric W. Biederman module_exit(mpls_exit); 22660189197fSEric W. Biederman 22670189197fSEric W. Biederman MODULE_DESCRIPTION("MultiProtocol Label Switching"); 22680189197fSEric W. Biederman MODULE_LICENSE("GPL v2"); 22690189197fSEric W. Biederman MODULE_ALIAS_NETPROTO(PF_MPLS); 2270