109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20189197fSEric W. Biederman #include <linux/types.h> 30189197fSEric W. Biederman #include <linux/skbuff.h> 40189197fSEric W. Biederman #include <linux/socket.h> 57720c01fSEric W. Biederman #include <linux/sysctl.h> 60189197fSEric W. Biederman #include <linux/net.h> 70189197fSEric W. Biederman #include <linux/module.h> 80189197fSEric W. Biederman #include <linux/if_arp.h> 90189197fSEric W. Biederman #include <linux/ipv6.h> 100189197fSEric W. Biederman #include <linux/mpls.h> 1124045a03SDavid Ahern #include <linux/netconf.h> 123968523fSDan Williams #include <linux/nospec.h> 134b5edb2fSStephen Rothwell #include <linux/vmalloc.h> 1427d69105SRobert Shearman #include <linux/percpu.h> 150189197fSEric W. Biederman #include <net/ip.h> 160189197fSEric W. Biederman #include <net/dst.h> 170189197fSEric W. Biederman #include <net/sock.h> 180189197fSEric W. Biederman #include <net/arp.h> 190189197fSEric W. Biederman #include <net/ip_fib.h> 200189197fSEric W. Biederman #include <net/netevent.h> 21bdc47641SAmine Kherbouche #include <net/ip_tunnels.h> 220189197fSEric W. Biederman #include <net/netns/generic.h> 23bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_IPV6) 24bf21563aSRoopa Prabhu #include <net/ipv6.h> 25bf21563aSRoopa Prabhu #endif 263616d08bSDavid Ahern #include <net/ipv6_stubs.h> 273c618c1dSDavid Ahern #include <net/rtnh.h> 280189197fSEric W. Biederman #include "internal.h" 290189197fSEric W. Biederman 30df1c6316SDavid Ahern /* max memory we will use for mpls_route */ 31df1c6316SDavid Ahern #define MAX_MPLS_ROUTE_MEM 4096 32df1c6316SDavid Ahern 331c78efa8SRobert Shearman /* Maximum number of labels to look ahead at when selecting a path of 341c78efa8SRobert Shearman * a multipath route 351c78efa8SRobert Shearman */ 361c78efa8SRobert Shearman #define MAX_MP_SELECT_LABELS 4 371c78efa8SRobert Shearman 38eb7809f0SRobert Shearman #define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1) 39eb7809f0SRobert Shearman 407720c01fSEric W. Biederman static int label_limit = (1 << 20) - 1; 41a59166e4SRobert Shearman static int ttl_max = 255; 427720c01fSEric W. Biederman 43bdc47641SAmine Kherbouche #if IS_ENABLED(CONFIG_NET_IP_TUNNEL) 4414c68c43SColin Ian King static size_t ipgre_mpls_encap_hlen(struct ip_tunnel_encap *e) 45bdc47641SAmine Kherbouche { 46bdc47641SAmine Kherbouche return sizeof(struct mpls_shim_hdr); 47bdc47641SAmine Kherbouche } 48bdc47641SAmine Kherbouche 49bdc47641SAmine Kherbouche static const struct ip_tunnel_encap_ops mpls_iptun_ops = { 50bdc47641SAmine Kherbouche .encap_hlen = ipgre_mpls_encap_hlen, 51bdc47641SAmine Kherbouche }; 52bdc47641SAmine Kherbouche 53bdc47641SAmine Kherbouche static int ipgre_tunnel_encap_add_mpls_ops(void) 54bdc47641SAmine Kherbouche { 55bdc47641SAmine Kherbouche return ip_tunnel_encap_add_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS); 56bdc47641SAmine Kherbouche } 57bdc47641SAmine Kherbouche 58bdc47641SAmine Kherbouche static void ipgre_tunnel_encap_del_mpls_ops(void) 59bdc47641SAmine Kherbouche { 60bdc47641SAmine Kherbouche ip_tunnel_encap_del_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS); 61bdc47641SAmine Kherbouche } 62bdc47641SAmine Kherbouche #else 63bdc47641SAmine Kherbouche static int ipgre_tunnel_encap_add_mpls_ops(void) 64bdc47641SAmine Kherbouche { 65bdc47641SAmine Kherbouche return 0; 66bdc47641SAmine Kherbouche } 67bdc47641SAmine Kherbouche 68bdc47641SAmine Kherbouche static void ipgre_tunnel_encap_del_mpls_ops(void) 69bdc47641SAmine Kherbouche { 70bdc47641SAmine Kherbouche } 71bdc47641SAmine Kherbouche #endif 72bdc47641SAmine Kherbouche 738de147dcSEric W. Biederman static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, 748de147dcSEric W. Biederman struct nlmsghdr *nlh, struct net *net, u32 portid, 758de147dcSEric W. Biederman unsigned int nlm_flags); 768de147dcSEric W. Biederman 770189197fSEric W. Biederman static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) 780189197fSEric W. Biederman { 790189197fSEric W. Biederman struct mpls_route *rt = NULL; 800189197fSEric W. Biederman 810189197fSEric W. Biederman if (index < net->mpls.platform_labels) { 820189197fSEric W. Biederman struct mpls_route __rcu **platform_label = 830189197fSEric W. Biederman rcu_dereference(net->mpls.platform_label); 840189197fSEric W. Biederman rt = rcu_dereference(platform_label[index]); 850189197fSEric W. Biederman } 860189197fSEric W. Biederman return rt; 870189197fSEric W. Biederman } 880189197fSEric W. Biederman 89face0188SRoopa Prabhu bool mpls_output_possible(const struct net_device *dev) 900189197fSEric W. Biederman { 910189197fSEric W. Biederman return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev); 920189197fSEric W. Biederman } 93face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_output_possible); 940189197fSEric W. Biederman 95cf4b24f0SRobert Shearman static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh) 96cf4b24f0SRobert Shearman { 9759b20966SDavid Ahern return (u8 *)nh + rt->rt_via_offset; 98cf4b24f0SRobert Shearman } 99cf4b24f0SRobert Shearman 100cf4b24f0SRobert Shearman static const u8 *mpls_nh_via(const struct mpls_route *rt, 101cf4b24f0SRobert Shearman const struct mpls_nh *nh) 102cf4b24f0SRobert Shearman { 103cf4b24f0SRobert Shearman return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh); 104cf4b24f0SRobert Shearman } 105cf4b24f0SRobert Shearman 106f8efb73cSRoopa Prabhu static unsigned int mpls_nh_header_size(const struct mpls_nh *nh) 1070189197fSEric W. Biederman { 1080189197fSEric W. Biederman /* The size of the layer 2.5 labels to be added for this route */ 109f8efb73cSRoopa Prabhu return nh->nh_labels * sizeof(struct mpls_shim_hdr); 1100189197fSEric W. Biederman } 1110189197fSEric W. Biederman 112face0188SRoopa Prabhu unsigned int mpls_dev_mtu(const struct net_device *dev) 1130189197fSEric W. Biederman { 1140189197fSEric W. Biederman /* The amount of data the layer 2 frame can hold */ 1150189197fSEric W. Biederman return dev->mtu; 1160189197fSEric W. Biederman } 117face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_dev_mtu); 1180189197fSEric W. Biederman 119face0188SRoopa Prabhu bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu) 1200189197fSEric W. Biederman { 1210189197fSEric W. Biederman if (skb->len <= mtu) 1220189197fSEric W. Biederman return false; 1230189197fSEric W. Biederman 124779b7931SDaniel Axtens if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) 1250189197fSEric W. Biederman return false; 1260189197fSEric W. Biederman 1270189197fSEric W. Biederman return true; 1280189197fSEric W. Biederman } 129face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(mpls_pkt_too_big); 1300189197fSEric W. Biederman 13127d69105SRobert Shearman void mpls_stats_inc_outucastpkts(struct net_device *dev, 13227d69105SRobert Shearman const struct sk_buff *skb) 13327d69105SRobert Shearman { 13427d69105SRobert Shearman struct mpls_dev *mdev; 13527d69105SRobert Shearman 13627d69105SRobert Shearman if (skb->protocol == htons(ETH_P_MPLS_UC)) { 13727d69105SRobert Shearman mdev = mpls_dev_get(dev); 13827d69105SRobert Shearman if (mdev) 13927d69105SRobert Shearman MPLS_INC_STATS_LEN(mdev, skb->len, 14027d69105SRobert Shearman tx_packets, 14127d69105SRobert Shearman tx_bytes); 14227d69105SRobert Shearman } else if (skb->protocol == htons(ETH_P_IP)) { 14327d69105SRobert Shearman IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len); 14427d69105SRobert Shearman #if IS_ENABLED(CONFIG_IPV6) 14527d69105SRobert Shearman } else if (skb->protocol == htons(ETH_P_IPV6)) { 14627d69105SRobert Shearman struct inet6_dev *in6dev = __in6_dev_get(dev); 14727d69105SRobert Shearman 14827d69105SRobert Shearman if (in6dev) 14927d69105SRobert Shearman IP6_UPD_PO_STATS(dev_net(dev), in6dev, 15027d69105SRobert Shearman IPSTATS_MIB_OUT, skb->len); 15127d69105SRobert Shearman #endif 15227d69105SRobert Shearman } 15327d69105SRobert Shearman } 15427d69105SRobert Shearman EXPORT_SYMBOL_GPL(mpls_stats_inc_outucastpkts); 15527d69105SRobert Shearman 1569f427a0eSDavid Ahern static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) 157f8efb73cSRoopa Prabhu { 1581c78efa8SRobert Shearman struct mpls_entry_decoded dec; 1599f427a0eSDavid Ahern unsigned int mpls_hdr_len = 0; 1601c78efa8SRobert Shearman struct mpls_shim_hdr *hdr; 1611c78efa8SRobert Shearman bool eli_seen = false; 1621c78efa8SRobert Shearman int label_index; 1631c78efa8SRobert Shearman u32 hash = 0; 1641c78efa8SRobert Shearman 1659f427a0eSDavid Ahern for (label_index = 0; label_index < MAX_MP_SELECT_LABELS; 1661c78efa8SRobert Shearman label_index++) { 1679f427a0eSDavid Ahern mpls_hdr_len += sizeof(*hdr); 1689f427a0eSDavid Ahern if (!pskb_may_pull(skb, mpls_hdr_len)) 1691c78efa8SRobert Shearman break; 1701c78efa8SRobert Shearman 1711c78efa8SRobert Shearman /* Read and decode the current label */ 1721c78efa8SRobert Shearman hdr = mpls_hdr(skb) + label_index; 1731c78efa8SRobert Shearman dec = mpls_entry_decode(hdr); 1741c78efa8SRobert Shearman 1751c78efa8SRobert Shearman /* RFC6790 - reserved labels MUST NOT be used as keys 1761c78efa8SRobert Shearman * for the load-balancing function 1771c78efa8SRobert Shearman */ 1781c78efa8SRobert Shearman if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) { 1791c78efa8SRobert Shearman hash = jhash_1word(dec.label, hash); 1801c78efa8SRobert Shearman 1811c78efa8SRobert Shearman /* The entropy label follows the entropy label 1821c78efa8SRobert Shearman * indicator, so this means that the entropy 1831c78efa8SRobert Shearman * label was just added to the hash - no need to 1841c78efa8SRobert Shearman * go any deeper either in the label stack or in the 1851c78efa8SRobert Shearman * payload 1861c78efa8SRobert Shearman */ 1871c78efa8SRobert Shearman if (eli_seen) 1881c78efa8SRobert Shearman break; 1891c78efa8SRobert Shearman } else if (dec.label == MPLS_LABEL_ENTROPY) { 1901c78efa8SRobert Shearman eli_seen = true; 1911c78efa8SRobert Shearman } 1921c78efa8SRobert Shearman 1939f427a0eSDavid Ahern if (!dec.bos) 1949f427a0eSDavid Ahern continue; 1959f427a0eSDavid Ahern 1969f427a0eSDavid Ahern /* found bottom label; does skb have room for a header? */ 1979f427a0eSDavid Ahern if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) { 1981c78efa8SRobert Shearman const struct iphdr *v4hdr; 1991c78efa8SRobert Shearman 2009f427a0eSDavid Ahern v4hdr = (const struct iphdr *)(hdr + 1); 2011c78efa8SRobert Shearman if (v4hdr->version == 4) { 2021c78efa8SRobert Shearman hash = jhash_3words(ntohl(v4hdr->saddr), 2031c78efa8SRobert Shearman ntohl(v4hdr->daddr), 2041c78efa8SRobert Shearman v4hdr->protocol, hash); 2051c78efa8SRobert Shearman } else if (v4hdr->version == 6 && 2069f427a0eSDavid Ahern pskb_may_pull(skb, mpls_hdr_len + 2071c78efa8SRobert Shearman sizeof(struct ipv6hdr))) { 2081c78efa8SRobert Shearman const struct ipv6hdr *v6hdr; 2091c78efa8SRobert Shearman 2109f427a0eSDavid Ahern v6hdr = (const struct ipv6hdr *)(hdr + 1); 2111c78efa8SRobert Shearman hash = __ipv6_addr_jhash(&v6hdr->saddr, hash); 2121c78efa8SRobert Shearman hash = __ipv6_addr_jhash(&v6hdr->daddr, hash); 2131c78efa8SRobert Shearman hash = jhash_1word(v6hdr->nexthdr, hash); 2141c78efa8SRobert Shearman } 2151c78efa8SRobert Shearman } 2169f427a0eSDavid Ahern 2179f427a0eSDavid Ahern break; 2181c78efa8SRobert Shearman } 2191c78efa8SRobert Shearman 220c89359a4SRoopa Prabhu return hash; 221c89359a4SRoopa Prabhu } 222c89359a4SRoopa Prabhu 22359b20966SDavid Ahern static struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index) 22459b20966SDavid Ahern { 22559b20966SDavid Ahern return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size); 22659b20966SDavid Ahern } 22759b20966SDavid Ahern 22839eb8cd1SDavid Ahern /* number of alive nexthops (rt->rt_nhn_alive) and the flags for 22939eb8cd1SDavid Ahern * a next hop (nh->nh_flags) are modified by netdev event handlers. 23039eb8cd1SDavid Ahern * Since those fields can change at any moment, use READ_ONCE to 23139eb8cd1SDavid Ahern * access both. 23239eb8cd1SDavid Ahern */ 233c89359a4SRoopa Prabhu static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, 2349f427a0eSDavid Ahern struct sk_buff *skb) 235c89359a4SRoopa Prabhu { 236c89359a4SRoopa Prabhu u32 hash = 0; 237c89359a4SRoopa Prabhu int nh_index = 0; 238c89359a4SRoopa Prabhu int n = 0; 23977ef013aSDavid Ahern u8 alive; 240c89359a4SRoopa Prabhu 241c89359a4SRoopa Prabhu /* No need to look further into packet if there's only 242c89359a4SRoopa Prabhu * one path 243c89359a4SRoopa Prabhu */ 244c89359a4SRoopa Prabhu if (rt->rt_nhn == 1) 24559b20966SDavid Ahern return rt->rt_nh; 246c89359a4SRoopa Prabhu 24739eb8cd1SDavid Ahern alive = READ_ONCE(rt->rt_nhn_alive); 24839eb8cd1SDavid Ahern if (alive == 0) 249c89359a4SRoopa Prabhu return NULL; 250c89359a4SRoopa Prabhu 2519f427a0eSDavid Ahern hash = mpls_multipath_hash(rt, skb); 252c89359a4SRoopa Prabhu nh_index = hash % alive; 253c89359a4SRoopa Prabhu if (alive == rt->rt_nhn) 254c89359a4SRoopa Prabhu goto out; 255c89359a4SRoopa Prabhu for_nexthops(rt) { 25639eb8cd1SDavid Ahern unsigned int nh_flags = READ_ONCE(nh->nh_flags); 25739eb8cd1SDavid Ahern 25839eb8cd1SDavid Ahern if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 259c89359a4SRoopa Prabhu continue; 260c89359a4SRoopa Prabhu if (n == nh_index) 261c89359a4SRoopa Prabhu return nh; 262c89359a4SRoopa Prabhu n++; 263c89359a4SRoopa Prabhu } endfor_nexthops(rt); 264c89359a4SRoopa Prabhu 2651c78efa8SRobert Shearman out: 26659b20966SDavid Ahern return mpls_get_nexthop(rt, nh_index); 267f8efb73cSRoopa Prabhu } 268f8efb73cSRoopa Prabhu 2695b441ac8SRobert Shearman static bool mpls_egress(struct net *net, struct mpls_route *rt, 2705b441ac8SRobert Shearman struct sk_buff *skb, struct mpls_entry_decoded dec) 2710189197fSEric W. Biederman { 272118d5234SRobert Shearman enum mpls_payload_type payload_type; 273118d5234SRobert Shearman bool success = false; 2740189197fSEric W. Biederman 27576fecd82SEric W. Biederman /* The IPv4 code below accesses through the IPv4 header 27676fecd82SEric W. Biederman * checksum, which is 12 bytes into the packet. 27776fecd82SEric W. Biederman * The IPv6 code below accesses through the IPv6 hop limit 27876fecd82SEric W. Biederman * which is 8 bytes into the packet. 27976fecd82SEric W. Biederman * 28076fecd82SEric W. Biederman * For all supported cases there should always be at least 12 28176fecd82SEric W. Biederman * bytes of packet data present. The IPv4 header is 20 bytes 28276fecd82SEric W. Biederman * without options and the IPv6 header is always 40 bytes 28376fecd82SEric W. Biederman * long. 28476fecd82SEric W. Biederman */ 28576fecd82SEric W. Biederman if (!pskb_may_pull(skb, 12)) 28676fecd82SEric W. Biederman return false; 28776fecd82SEric W. Biederman 288118d5234SRobert Shearman payload_type = rt->rt_payload_type; 289118d5234SRobert Shearman if (payload_type == MPT_UNSPEC) 290118d5234SRobert Shearman payload_type = ip_hdr(skb)->version; 291118d5234SRobert Shearman 292118d5234SRobert Shearman switch (payload_type) { 293118d5234SRobert Shearman case MPT_IPV4: { 294118d5234SRobert Shearman struct iphdr *hdr4 = ip_hdr(skb); 2955b441ac8SRobert Shearman u8 new_ttl; 2960189197fSEric W. Biederman skb->protocol = htons(ETH_P_IP); 2975b441ac8SRobert Shearman 2985b441ac8SRobert Shearman /* If propagating TTL, take the decremented TTL from 2995b441ac8SRobert Shearman * the incoming MPLS header, otherwise decrement the 3005b441ac8SRobert Shearman * TTL, but only if not 0 to avoid underflow. 3015b441ac8SRobert Shearman */ 3025b441ac8SRobert Shearman if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || 3035b441ac8SRobert Shearman (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && 3045b441ac8SRobert Shearman net->mpls.ip_ttl_propagate)) 3055b441ac8SRobert Shearman new_ttl = dec.ttl; 3065b441ac8SRobert Shearman else 3075b441ac8SRobert Shearman new_ttl = hdr4->ttl ? hdr4->ttl - 1 : 0; 3085b441ac8SRobert Shearman 3090189197fSEric W. Biederman csum_replace2(&hdr4->check, 3100189197fSEric W. Biederman htons(hdr4->ttl << 8), 3115b441ac8SRobert Shearman htons(new_ttl << 8)); 3125b441ac8SRobert Shearman hdr4->ttl = new_ttl; 313118d5234SRobert Shearman success = true; 314118d5234SRobert Shearman break; 3150189197fSEric W. Biederman } 316118d5234SRobert Shearman case MPT_IPV6: { 3170189197fSEric W. Biederman struct ipv6hdr *hdr6 = ipv6_hdr(skb); 3180189197fSEric W. Biederman skb->protocol = htons(ETH_P_IPV6); 3195b441ac8SRobert Shearman 3205b441ac8SRobert Shearman /* If propagating TTL, take the decremented TTL from 3215b441ac8SRobert Shearman * the incoming MPLS header, otherwise decrement the 3225b441ac8SRobert Shearman * hop limit, but only if not 0 to avoid underflow. 3235b441ac8SRobert Shearman */ 3245b441ac8SRobert Shearman if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || 3255b441ac8SRobert Shearman (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && 3265b441ac8SRobert Shearman net->mpls.ip_ttl_propagate)) 3270189197fSEric W. Biederman hdr6->hop_limit = dec.ttl; 3285b441ac8SRobert Shearman else if (hdr6->hop_limit) 3295b441ac8SRobert Shearman hdr6->hop_limit = hdr6->hop_limit - 1; 330118d5234SRobert Shearman success = true; 331118d5234SRobert Shearman break; 3320189197fSEric W. Biederman } 333118d5234SRobert Shearman case MPT_UNSPEC: 3345b441ac8SRobert Shearman /* Should have decided which protocol it is by now */ 335118d5234SRobert Shearman break; 336118d5234SRobert Shearman } 337118d5234SRobert Shearman 3380189197fSEric W. Biederman return success; 3390189197fSEric W. Biederman } 3400189197fSEric W. Biederman 3410189197fSEric W. Biederman static int mpls_forward(struct sk_buff *skb, struct net_device *dev, 3420189197fSEric W. Biederman struct packet_type *pt, struct net_device *orig_dev) 3430189197fSEric W. Biederman { 3440189197fSEric W. Biederman struct net *net = dev_net(dev); 3450189197fSEric W. Biederman struct mpls_shim_hdr *hdr; 3460189197fSEric W. Biederman struct mpls_route *rt; 347f8efb73cSRoopa Prabhu struct mpls_nh *nh; 3480189197fSEric W. Biederman struct mpls_entry_decoded dec; 3490189197fSEric W. Biederman struct net_device *out_dev; 35027d69105SRobert Shearman struct mpls_dev *out_mdev; 35103c57747SRobert Shearman struct mpls_dev *mdev; 3520189197fSEric W. Biederman unsigned int hh_len; 3530189197fSEric W. Biederman unsigned int new_header_size; 3540189197fSEric W. Biederman unsigned int mtu; 3550189197fSEric W. Biederman int err; 3560189197fSEric W. Biederman 3570189197fSEric W. Biederman /* Careful this entire function runs inside of an rcu critical section */ 3580189197fSEric W. Biederman 35903c57747SRobert Shearman mdev = mpls_dev_get(dev); 36027d69105SRobert Shearman if (!mdev) 36103c57747SRobert Shearman goto drop; 36203c57747SRobert Shearman 36327d69105SRobert Shearman MPLS_INC_STATS_LEN(mdev, skb->len, rx_packets, 36427d69105SRobert Shearman rx_bytes); 36527d69105SRobert Shearman 36627d69105SRobert Shearman if (!mdev->input_enabled) { 36727d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_dropped); 36827d69105SRobert Shearman goto drop; 36927d69105SRobert Shearman } 37027d69105SRobert Shearman 3710189197fSEric W. Biederman if (skb->pkt_type != PACKET_HOST) 37227d69105SRobert Shearman goto err; 3730189197fSEric W. Biederman 3740189197fSEric W. Biederman if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) 37527d69105SRobert Shearman goto err; 3760189197fSEric W. Biederman 3770189197fSEric W. Biederman if (!pskb_may_pull(skb, sizeof(*hdr))) 37827d69105SRobert Shearman goto err; 3790189197fSEric W. Biederman 3800189197fSEric W. Biederman /* Read and decode the label */ 3810189197fSEric W. Biederman hdr = mpls_hdr(skb); 3820189197fSEric W. Biederman dec = mpls_entry_decode(hdr); 3830189197fSEric W. Biederman 3840189197fSEric W. Biederman rt = mpls_route_input_rcu(net, dec.label); 38527d69105SRobert Shearman if (!rt) { 38627d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_noroute); 3870189197fSEric W. Biederman goto drop; 38827d69105SRobert Shearman } 3890189197fSEric W. Biederman 3909f427a0eSDavid Ahern nh = mpls_select_multipath(rt, skb); 391f8efb73cSRoopa Prabhu if (!nh) 39227d69105SRobert Shearman goto err; 3930189197fSEric W. Biederman 3949f427a0eSDavid Ahern /* Pop the label */ 3959f427a0eSDavid Ahern skb_pull(skb, sizeof(*hdr)); 3969f427a0eSDavid Ahern skb_reset_network_header(skb); 3979f427a0eSDavid Ahern 3989f427a0eSDavid Ahern skb_orphan(skb); 3999f427a0eSDavid Ahern 4000189197fSEric W. Biederman if (skb_warn_if_lro(skb)) 40127d69105SRobert Shearman goto err; 4020189197fSEric W. Biederman 4030189197fSEric W. Biederman skb_forward_csum(skb); 4040189197fSEric W. Biederman 4050189197fSEric W. Biederman /* Verify ttl is valid */ 406aa7da937SEric W. Biederman if (dec.ttl <= 1) 40727d69105SRobert Shearman goto err; 4080189197fSEric W. Biederman dec.ttl -= 1; 4090189197fSEric W. Biederman 41027d69105SRobert Shearman /* Find the output device */ 41127d69105SRobert Shearman out_dev = rcu_dereference(nh->nh_dev); 41227d69105SRobert Shearman if (!mpls_output_possible(out_dev)) 41327d69105SRobert Shearman goto tx_err; 41427d69105SRobert Shearman 4150189197fSEric W. Biederman /* Verify the destination can hold the packet */ 416f8efb73cSRoopa Prabhu new_header_size = mpls_nh_header_size(nh); 4170189197fSEric W. Biederman mtu = mpls_dev_mtu(out_dev); 4180189197fSEric W. Biederman if (mpls_pkt_too_big(skb, mtu - new_header_size)) 41927d69105SRobert Shearman goto tx_err; 4200189197fSEric W. Biederman 4210189197fSEric W. Biederman hh_len = LL_RESERVED_SPACE(out_dev); 4220189197fSEric W. Biederman if (!out_dev->header_ops) 4230189197fSEric W. Biederman hh_len = 0; 4240189197fSEric W. Biederman 4250189197fSEric W. Biederman /* Ensure there is enough space for the headers in the skb */ 4260189197fSEric W. Biederman if (skb_cow(skb, hh_len + new_header_size)) 42727d69105SRobert Shearman goto tx_err; 4280189197fSEric W. Biederman 4290189197fSEric W. Biederman skb->dev = out_dev; 4300189197fSEric W. Biederman skb->protocol = htons(ETH_P_MPLS_UC); 4310189197fSEric W. Biederman 4320189197fSEric W. Biederman if (unlikely(!new_header_size && dec.bos)) { 4330189197fSEric W. Biederman /* Penultimate hop popping */ 4345b441ac8SRobert Shearman if (!mpls_egress(dev_net(out_dev), rt, skb, dec)) 43527d69105SRobert Shearman goto err; 4360189197fSEric W. Biederman } else { 4370189197fSEric W. Biederman bool bos; 4380189197fSEric W. Biederman int i; 4390189197fSEric W. Biederman skb_push(skb, new_header_size); 4400189197fSEric W. Biederman skb_reset_network_header(skb); 4410189197fSEric W. Biederman /* Push the new labels */ 4420189197fSEric W. Biederman hdr = mpls_hdr(skb); 4430189197fSEric W. Biederman bos = dec.bos; 444f8efb73cSRoopa Prabhu for (i = nh->nh_labels - 1; i >= 0; i--) { 445f8efb73cSRoopa Prabhu hdr[i] = mpls_entry_encode(nh->nh_label[i], 446f8efb73cSRoopa Prabhu dec.ttl, 0, bos); 4470189197fSEric W. Biederman bos = false; 4480189197fSEric W. Biederman } 4490189197fSEric W. Biederman } 4500189197fSEric W. Biederman 45127d69105SRobert Shearman mpls_stats_inc_outucastpkts(out_dev, skb); 45227d69105SRobert Shearman 453eb7809f0SRobert Shearman /* If via wasn't specified then send out using device address */ 454eb7809f0SRobert Shearman if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC) 455eb7809f0SRobert Shearman err = neigh_xmit(NEIGH_LINK_TABLE, out_dev, 456eb7809f0SRobert Shearman out_dev->dev_addr, skb); 457eb7809f0SRobert Shearman else 458eb7809f0SRobert Shearman err = neigh_xmit(nh->nh_via_table, out_dev, 459eb7809f0SRobert Shearman mpls_nh_via(rt, nh), skb); 4600189197fSEric W. Biederman if (err) 4610189197fSEric W. Biederman net_dbg_ratelimited("%s: packet transmission failed: %d\n", 4620189197fSEric W. Biederman __func__, err); 4630189197fSEric W. Biederman return 0; 4640189197fSEric W. Biederman 46527d69105SRobert Shearman tx_err: 46627d69105SRobert Shearman out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL; 46727d69105SRobert Shearman if (out_mdev) 46827d69105SRobert Shearman MPLS_INC_STATS(out_mdev, tx_errors); 46927d69105SRobert Shearman goto drop; 47027d69105SRobert Shearman err: 47127d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_errors); 4720189197fSEric W. Biederman drop: 4730189197fSEric W. Biederman kfree_skb(skb); 4740189197fSEric W. Biederman return NET_RX_DROP; 4750189197fSEric W. Biederman } 4760189197fSEric W. Biederman 4770189197fSEric W. Biederman static struct packet_type mpls_packet_type __read_mostly = { 4780189197fSEric W. Biederman .type = cpu_to_be16(ETH_P_MPLS_UC), 4790189197fSEric W. Biederman .func = mpls_forward, 4800189197fSEric W. Biederman }; 4810189197fSEric W. Biederman 482f0126539SWu Fengguang static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { 48303c05665SEric W. Biederman [RTA_DST] = { .type = NLA_U32 }, 48403c05665SEric W. Biederman [RTA_OIF] = { .type = NLA_U32 }, 4855b441ac8SRobert Shearman [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, 48603c05665SEric W. Biederman }; 48703c05665SEric W. Biederman 488a2519929SEric W. Biederman struct mpls_route_config { 489a2519929SEric W. Biederman u32 rc_protocol; 490a2519929SEric W. Biederman u32 rc_ifindex; 491f8efb73cSRoopa Prabhu u8 rc_via_table; 492f8efb73cSRoopa Prabhu u8 rc_via_alen; 493a2519929SEric W. Biederman u8 rc_via[MAX_VIA_ALEN]; 494a2519929SEric W. Biederman u32 rc_label; 4955b441ac8SRobert Shearman u8 rc_ttl_propagate; 496f8efb73cSRoopa Prabhu u8 rc_output_labels; 497a2519929SEric W. Biederman u32 rc_output_label[MAX_NEW_LABELS]; 498a2519929SEric W. Biederman u32 rc_nlflags; 499118d5234SRobert Shearman enum mpls_payload_type rc_payload_type; 500a2519929SEric W. Biederman struct nl_info rc_nlinfo; 501f8efb73cSRoopa Prabhu struct rtnexthop *rc_mp; 502f8efb73cSRoopa Prabhu int rc_mp_len; 503a2519929SEric W. Biederman }; 504a2519929SEric W. Biederman 50559b20966SDavid Ahern /* all nexthops within a route have the same size based on max 50659b20966SDavid Ahern * number of labels and max via length for a hop 50759b20966SDavid Ahern */ 50859b20966SDavid Ahern static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels) 5090189197fSEric W. Biederman { 51059b20966SDavid Ahern u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen); 5110189197fSEric W. Biederman struct mpls_route *rt; 512df1c6316SDavid Ahern size_t size; 5130189197fSEric W. Biederman 514df1c6316SDavid Ahern size = sizeof(*rt) + num_nh * nh_size; 515df1c6316SDavid Ahern if (size > MAX_MPLS_ROUTE_MEM) 516df1c6316SDavid Ahern return ERR_PTR(-EINVAL); 517df1c6316SDavid Ahern 518df1c6316SDavid Ahern rt = kzalloc(size, GFP_KERNEL); 519df1c6316SDavid Ahern if (!rt) 520df1c6316SDavid Ahern return ERR_PTR(-ENOMEM); 521df1c6316SDavid Ahern 522f8efb73cSRoopa Prabhu rt->rt_nhn = num_nh; 523c89359a4SRoopa Prabhu rt->rt_nhn_alive = num_nh; 52459b20966SDavid Ahern rt->rt_nh_size = nh_size; 52559b20966SDavid Ahern rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); 526f8efb73cSRoopa Prabhu 5270189197fSEric W. Biederman return rt; 5280189197fSEric W. Biederman } 5290189197fSEric W. Biederman 5300189197fSEric W. Biederman static void mpls_rt_free(struct mpls_route *rt) 5310189197fSEric W. Biederman { 5320189197fSEric W. Biederman if (rt) 5330189197fSEric W. Biederman kfree_rcu(rt, rt_rcu); 5340189197fSEric W. Biederman } 5350189197fSEric W. Biederman 5368de147dcSEric W. Biederman static void mpls_notify_route(struct net *net, unsigned index, 5378de147dcSEric W. Biederman struct mpls_route *old, struct mpls_route *new, 5388de147dcSEric W. Biederman const struct nl_info *info) 5398de147dcSEric W. Biederman { 5408de147dcSEric W. Biederman struct nlmsghdr *nlh = info ? info->nlh : NULL; 5418de147dcSEric W. Biederman unsigned portid = info ? info->portid : 0; 5428de147dcSEric W. Biederman int event = new ? RTM_NEWROUTE : RTM_DELROUTE; 5438de147dcSEric W. Biederman struct mpls_route *rt = new ? new : old; 5448de147dcSEric W. Biederman unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; 5458de147dcSEric W. Biederman /* Ignore reserved labels for now */ 546a6affd24SRobert Shearman if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED)) 5478de147dcSEric W. Biederman rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); 5488de147dcSEric W. Biederman } 5498de147dcSEric W. Biederman 5500189197fSEric W. Biederman static void mpls_route_update(struct net *net, unsigned index, 551f8efb73cSRoopa Prabhu struct mpls_route *new, 5520189197fSEric W. Biederman const struct nl_info *info) 5530189197fSEric W. Biederman { 55419d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 555f8efb73cSRoopa Prabhu struct mpls_route *rt; 5560189197fSEric W. Biederman 5570189197fSEric W. Biederman ASSERT_RTNL(); 5580189197fSEric W. Biederman 55919d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 56019d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 56119d0c341SEric W. Biederman rcu_assign_pointer(platform_label[index], new); 5620189197fSEric W. Biederman 563f8efb73cSRoopa Prabhu mpls_notify_route(net, index, rt, new, info); 5648de147dcSEric W. Biederman 5650189197fSEric W. Biederman /* If we removed a route free it now */ 566f8efb73cSRoopa Prabhu mpls_rt_free(rt); 5670189197fSEric W. Biederman } 5680189197fSEric W. Biederman 569a2519929SEric W. Biederman static unsigned find_free_label(struct net *net) 570a2519929SEric W. Biederman { 57119d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 57219d0c341SEric W. Biederman size_t platform_labels; 573a2519929SEric W. Biederman unsigned index; 57419d0c341SEric W. Biederman 57519d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 57619d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 577a6affd24SRobert Shearman for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels; 578a6affd24SRobert Shearman index++) { 57919d0c341SEric W. Biederman if (!rtnl_dereference(platform_label[index])) 580a2519929SEric W. Biederman return index; 581a2519929SEric W. Biederman } 582a2519929SEric W. Biederman return LABEL_NOT_SPECIFIED; 583a2519929SEric W. Biederman } 584a2519929SEric W. Biederman 585bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_INET) 586cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 587cf4b24f0SRobert Shearman const void *addr) 58801faef2cSRoopa Prabhu { 5895a9348b5SDan Carpenter struct net_device *dev; 59001faef2cSRoopa Prabhu struct rtable *rt; 59101faef2cSRoopa Prabhu struct in_addr daddr; 59201faef2cSRoopa Prabhu 59301faef2cSRoopa Prabhu memcpy(&daddr, addr, sizeof(struct in_addr)); 59401faef2cSRoopa Prabhu rt = ip_route_output(net, daddr.s_addr, 0, 0, 0); 59501faef2cSRoopa Prabhu if (IS_ERR(rt)) 5965a9348b5SDan Carpenter return ERR_CAST(rt); 59701faef2cSRoopa Prabhu 59801faef2cSRoopa Prabhu dev = rt->dst.dev; 59901faef2cSRoopa Prabhu dev_hold(dev); 60001faef2cSRoopa Prabhu 60101faef2cSRoopa Prabhu ip_rt_put(rt); 60201faef2cSRoopa Prabhu 60301faef2cSRoopa Prabhu return dev; 60401faef2cSRoopa Prabhu } 605bf21563aSRoopa Prabhu #else 606cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 607cf4b24f0SRobert Shearman const void *addr) 608bf21563aSRoopa Prabhu { 609bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 610bf21563aSRoopa Prabhu } 611bf21563aSRoopa Prabhu #endif 61201faef2cSRoopa Prabhu 613bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_IPV6) 614cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 615cf4b24f0SRobert Shearman const void *addr) 61601faef2cSRoopa Prabhu { 6175a9348b5SDan Carpenter struct net_device *dev; 61801faef2cSRoopa Prabhu struct dst_entry *dst; 61901faef2cSRoopa Prabhu struct flowi6 fl6; 620bf21563aSRoopa Prabhu int err; 621bf21563aSRoopa Prabhu 622bf21563aSRoopa Prabhu if (!ipv6_stub) 623bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 62401faef2cSRoopa Prabhu 62501faef2cSRoopa Prabhu memset(&fl6, 0, sizeof(fl6)); 62601faef2cSRoopa Prabhu memcpy(&fl6.daddr, addr, sizeof(struct in6_addr)); 627bf21563aSRoopa Prabhu err = ipv6_stub->ipv6_dst_lookup(net, NULL, &dst, &fl6); 628bf21563aSRoopa Prabhu if (err) 6295a9348b5SDan Carpenter return ERR_PTR(err); 63001faef2cSRoopa Prabhu 63101faef2cSRoopa Prabhu dev = dst->dev; 63201faef2cSRoopa Prabhu dev_hold(dev); 63301faef2cSRoopa Prabhu dst_release(dst); 63401faef2cSRoopa Prabhu 63501faef2cSRoopa Prabhu return dev; 63601faef2cSRoopa Prabhu } 637bf21563aSRoopa Prabhu #else 638cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 639cf4b24f0SRobert Shearman const void *addr) 640bf21563aSRoopa Prabhu { 641bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 642bf21563aSRoopa Prabhu } 643bf21563aSRoopa Prabhu #endif 64401faef2cSRoopa Prabhu 64501faef2cSRoopa Prabhu static struct net_device *find_outdev(struct net *net, 646cf4b24f0SRobert Shearman struct mpls_route *rt, 647f8efb73cSRoopa Prabhu struct mpls_nh *nh, int oif) 64801faef2cSRoopa Prabhu { 64901faef2cSRoopa Prabhu struct net_device *dev = NULL; 65001faef2cSRoopa Prabhu 651f8efb73cSRoopa Prabhu if (!oif) { 652f8efb73cSRoopa Prabhu switch (nh->nh_via_table) { 65301faef2cSRoopa Prabhu case NEIGH_ARP_TABLE: 654cf4b24f0SRobert Shearman dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 65501faef2cSRoopa Prabhu break; 65601faef2cSRoopa Prabhu case NEIGH_ND_TABLE: 657cf4b24f0SRobert Shearman dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 65801faef2cSRoopa Prabhu break; 65901faef2cSRoopa Prabhu case NEIGH_LINK_TABLE: 66001faef2cSRoopa Prabhu break; 66101faef2cSRoopa Prabhu } 66201faef2cSRoopa Prabhu } else { 663f8efb73cSRoopa Prabhu dev = dev_get_by_index(net, oif); 66401faef2cSRoopa Prabhu } 66501faef2cSRoopa Prabhu 6663dcb615eSRoopa Prabhu if (!dev) 6673dcb615eSRoopa Prabhu return ERR_PTR(-ENODEV); 6683dcb615eSRoopa Prabhu 66994a57f1fSRoopa Prabhu if (IS_ERR(dev)) 67094a57f1fSRoopa Prabhu return dev; 67194a57f1fSRoopa Prabhu 672f8efb73cSRoopa Prabhu /* The caller is holding rtnl anyways, so release the dev reference */ 673f8efb73cSRoopa Prabhu dev_put(dev); 674f8efb73cSRoopa Prabhu 67501faef2cSRoopa Prabhu return dev; 67601faef2cSRoopa Prabhu } 67701faef2cSRoopa Prabhu 678cf4b24f0SRobert Shearman static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt, 679cf4b24f0SRobert Shearman struct mpls_nh *nh, int oif) 680f8efb73cSRoopa Prabhu { 681f8efb73cSRoopa Prabhu struct net_device *dev = NULL; 682f8efb73cSRoopa Prabhu int err = -ENODEV; 683f8efb73cSRoopa Prabhu 684cf4b24f0SRobert Shearman dev = find_outdev(net, rt, nh, oif); 685f8efb73cSRoopa Prabhu if (IS_ERR(dev)) { 686f8efb73cSRoopa Prabhu err = PTR_ERR(dev); 687f8efb73cSRoopa Prabhu dev = NULL; 688f8efb73cSRoopa Prabhu goto errout; 689f8efb73cSRoopa Prabhu } 690f8efb73cSRoopa Prabhu 691f8efb73cSRoopa Prabhu /* Ensure this is a supported device */ 692f8efb73cSRoopa Prabhu err = -EINVAL; 693f8efb73cSRoopa Prabhu if (!mpls_dev_get(dev)) 694f8efb73cSRoopa Prabhu goto errout; 695f8efb73cSRoopa Prabhu 696a3e948e8SRobert Shearman if ((nh->nh_via_table == NEIGH_LINK_TABLE) && 697a3e948e8SRobert Shearman (dev->addr_len != nh->nh_via_alen)) 698a3e948e8SRobert Shearman goto errout; 699a3e948e8SRobert Shearman 700f8efb73cSRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, dev); 701f8efb73cSRoopa Prabhu 702c89359a4SRoopa Prabhu if (!(dev->flags & IFF_UP)) { 703c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_DEAD; 704c89359a4SRoopa Prabhu } else { 705c89359a4SRoopa Prabhu unsigned int flags; 706c89359a4SRoopa Prabhu 707c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 708c89359a4SRoopa Prabhu if (!(flags & (IFF_RUNNING | IFF_LOWER_UP))) 709c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_LINKDOWN; 710c89359a4SRoopa Prabhu } 711c89359a4SRoopa Prabhu 712f8efb73cSRoopa Prabhu return 0; 713f8efb73cSRoopa Prabhu 714f8efb73cSRoopa Prabhu errout: 715f8efb73cSRoopa Prabhu return err; 716f8efb73cSRoopa Prabhu } 717f8efb73cSRoopa Prabhu 718d4e72560SDavid Ahern static int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table, 719d4e72560SDavid Ahern u8 via_addr[], struct netlink_ext_ack *extack) 720d4e72560SDavid Ahern { 721d4e72560SDavid Ahern struct rtvia *via = nla_data(nla); 722d4e72560SDavid Ahern int err = -EINVAL; 723d4e72560SDavid Ahern int alen; 724d4e72560SDavid Ahern 725d4e72560SDavid Ahern if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) { 726d4e72560SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 727d4e72560SDavid Ahern "Invalid attribute length for RTA_VIA"); 728d4e72560SDavid Ahern goto errout; 729d4e72560SDavid Ahern } 730d4e72560SDavid Ahern alen = nla_len(nla) - 731d4e72560SDavid Ahern offsetof(struct rtvia, rtvia_addr); 732d4e72560SDavid Ahern if (alen > MAX_VIA_ALEN) { 733d4e72560SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 734d4e72560SDavid Ahern "Invalid address length for RTA_VIA"); 735d4e72560SDavid Ahern goto errout; 736d4e72560SDavid Ahern } 737d4e72560SDavid Ahern 738d4e72560SDavid Ahern /* Validate the address family */ 739d4e72560SDavid Ahern switch (via->rtvia_family) { 740d4e72560SDavid Ahern case AF_PACKET: 741d4e72560SDavid Ahern *via_table = NEIGH_LINK_TABLE; 742d4e72560SDavid Ahern break; 743d4e72560SDavid Ahern case AF_INET: 744d4e72560SDavid Ahern *via_table = NEIGH_ARP_TABLE; 745d4e72560SDavid Ahern if (alen != 4) 746d4e72560SDavid Ahern goto errout; 747d4e72560SDavid Ahern break; 748d4e72560SDavid Ahern case AF_INET6: 749d4e72560SDavid Ahern *via_table = NEIGH_ND_TABLE; 750d4e72560SDavid Ahern if (alen != 16) 751d4e72560SDavid Ahern goto errout; 752d4e72560SDavid Ahern break; 753d4e72560SDavid Ahern default: 754d4e72560SDavid Ahern /* Unsupported address family */ 755d4e72560SDavid Ahern goto errout; 756d4e72560SDavid Ahern } 757d4e72560SDavid Ahern 758d4e72560SDavid Ahern memcpy(via_addr, via->rtvia_addr, alen); 759d4e72560SDavid Ahern *via_alen = alen; 760d4e72560SDavid Ahern err = 0; 761d4e72560SDavid Ahern 762d4e72560SDavid Ahern errout: 763d4e72560SDavid Ahern return err; 764d4e72560SDavid Ahern } 765d4e72560SDavid Ahern 766f8efb73cSRoopa Prabhu static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, 767f8efb73cSRoopa Prabhu struct mpls_route *rt) 768f8efb73cSRoopa Prabhu { 769f8efb73cSRoopa Prabhu struct net *net = cfg->rc_nlinfo.nl_net; 770f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 771f8efb73cSRoopa Prabhu int err; 772f8efb73cSRoopa Prabhu int i; 773f8efb73cSRoopa Prabhu 774f8efb73cSRoopa Prabhu if (!nh) 775f8efb73cSRoopa Prabhu return -ENOMEM; 776f8efb73cSRoopa Prabhu 777f8efb73cSRoopa Prabhu nh->nh_labels = cfg->rc_output_labels; 778f8efb73cSRoopa Prabhu for (i = 0; i < nh->nh_labels; i++) 779f8efb73cSRoopa Prabhu nh->nh_label[i] = cfg->rc_output_label[i]; 780f8efb73cSRoopa Prabhu 781f8efb73cSRoopa Prabhu nh->nh_via_table = cfg->rc_via_table; 782cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen); 783f8efb73cSRoopa Prabhu nh->nh_via_alen = cfg->rc_via_alen; 784f8efb73cSRoopa Prabhu 785cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex); 786f8efb73cSRoopa Prabhu if (err) 787f8efb73cSRoopa Prabhu goto errout; 788f8efb73cSRoopa Prabhu 789c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 790c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 791c89359a4SRoopa Prabhu 792f8efb73cSRoopa Prabhu return 0; 793f8efb73cSRoopa Prabhu 794f8efb73cSRoopa Prabhu errout: 795f8efb73cSRoopa Prabhu return err; 796f8efb73cSRoopa Prabhu } 797f8efb73cSRoopa Prabhu 798cf4b24f0SRobert Shearman static int mpls_nh_build(struct net *net, struct mpls_route *rt, 799c89359a4SRoopa Prabhu struct mpls_nh *nh, int oif, struct nlattr *via, 800074350e2SDavid Ahern struct nlattr *newdst, u8 max_labels, 801074350e2SDavid Ahern struct netlink_ext_ack *extack) 802f8efb73cSRoopa Prabhu { 803f8efb73cSRoopa Prabhu int err = -ENOMEM; 804f8efb73cSRoopa Prabhu 805f8efb73cSRoopa Prabhu if (!nh) 806f8efb73cSRoopa Prabhu goto errout; 807f8efb73cSRoopa Prabhu 808f8efb73cSRoopa Prabhu if (newdst) { 809a1f10abeSDavid Ahern err = nla_get_labels(newdst, max_labels, &nh->nh_labels, 810074350e2SDavid Ahern nh->nh_label, extack); 811f8efb73cSRoopa Prabhu if (err) 812f8efb73cSRoopa Prabhu goto errout; 813f8efb73cSRoopa Prabhu } 814f8efb73cSRoopa Prabhu 815f20367dfSRobert Shearman if (via) { 816f8efb73cSRoopa Prabhu err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table, 817074350e2SDavid Ahern __mpls_nh_via(rt, nh), extack); 818f8efb73cSRoopa Prabhu if (err) 819f8efb73cSRoopa Prabhu goto errout; 820f20367dfSRobert Shearman } else { 821f20367dfSRobert Shearman nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC; 822f20367dfSRobert Shearman } 823f8efb73cSRoopa Prabhu 824cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, oif); 825f8efb73cSRoopa Prabhu if (err) 826f8efb73cSRoopa Prabhu goto errout; 827f8efb73cSRoopa Prabhu 828f8efb73cSRoopa Prabhu return 0; 829f8efb73cSRoopa Prabhu 830f8efb73cSRoopa Prabhu errout: 831f8efb73cSRoopa Prabhu return err; 832f8efb73cSRoopa Prabhu } 833f8efb73cSRoopa Prabhu 83477ef013aSDavid Ahern static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, 835a4ac8c98SDavid Ahern u8 cfg_via_alen, u8 *max_via_alen, 836a4ac8c98SDavid Ahern u8 *max_labels) 837f8efb73cSRoopa Prabhu { 838f8efb73cSRoopa Prabhu int remaining = len; 83977ef013aSDavid Ahern u8 nhs = 0; 840f8efb73cSRoopa Prabhu 841cf4b24f0SRobert Shearman *max_via_alen = 0; 842a4ac8c98SDavid Ahern *max_labels = 0; 843cf4b24f0SRobert Shearman 844f8efb73cSRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 845cf4b24f0SRobert Shearman struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 846cf4b24f0SRobert Shearman int attrlen; 847a4ac8c98SDavid Ahern u8 n_labels = 0; 848cf4b24f0SRobert Shearman 849cf4b24f0SRobert Shearman attrlen = rtnh_attrlen(rtnh); 850cf4b24f0SRobert Shearman nla = nla_find(attrs, attrlen, RTA_VIA); 851cf4b24f0SRobert Shearman if (nla && nla_len(nla) >= 852cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr)) { 853cf4b24f0SRobert Shearman int via_alen = nla_len(nla) - 854cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr); 855cf4b24f0SRobert Shearman 856cf4b24f0SRobert Shearman if (via_alen <= MAX_VIA_ALEN) 857cf4b24f0SRobert Shearman *max_via_alen = max_t(u16, *max_via_alen, 858cf4b24f0SRobert Shearman via_alen); 859cf4b24f0SRobert Shearman } 860cf4b24f0SRobert Shearman 861a4ac8c98SDavid Ahern nla = nla_find(attrs, attrlen, RTA_NEWDST); 862a4ac8c98SDavid Ahern if (nla && 863a1f10abeSDavid Ahern nla_get_labels(nla, MAX_NEW_LABELS, &n_labels, 864a1f10abeSDavid Ahern NULL, NULL) != 0) 865a4ac8c98SDavid Ahern return 0; 866a4ac8c98SDavid Ahern 867a4ac8c98SDavid Ahern *max_labels = max_t(u8, *max_labels, n_labels); 868a4ac8c98SDavid Ahern 86977ef013aSDavid Ahern /* number of nexthops is tracked by a u8. 87077ef013aSDavid Ahern * Check for overflow. 87177ef013aSDavid Ahern */ 87277ef013aSDavid Ahern if (nhs == 255) 87377ef013aSDavid Ahern return 0; 874f8efb73cSRoopa Prabhu nhs++; 87577ef013aSDavid Ahern 876f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 877f8efb73cSRoopa Prabhu } 878f8efb73cSRoopa Prabhu 879f8efb73cSRoopa Prabhu /* leftover implies invalid nexthop configuration, discard it */ 880f8efb73cSRoopa Prabhu return remaining > 0 ? 0 : nhs; 881f8efb73cSRoopa Prabhu } 882f8efb73cSRoopa Prabhu 883f8efb73cSRoopa Prabhu static int mpls_nh_build_multi(struct mpls_route_config *cfg, 884074350e2SDavid Ahern struct mpls_route *rt, u8 max_labels, 885074350e2SDavid Ahern struct netlink_ext_ack *extack) 886f8efb73cSRoopa Prabhu { 887f8efb73cSRoopa Prabhu struct rtnexthop *rtnh = cfg->rc_mp; 888f8efb73cSRoopa Prabhu struct nlattr *nla_via, *nla_newdst; 889f8efb73cSRoopa Prabhu int remaining = cfg->rc_mp_len; 890f8efb73cSRoopa Prabhu int err = 0; 89177ef013aSDavid Ahern u8 nhs = 0; 892f8efb73cSRoopa Prabhu 893f8efb73cSRoopa Prabhu change_nexthops(rt) { 894f8efb73cSRoopa Prabhu int attrlen; 895f8efb73cSRoopa Prabhu 896f8efb73cSRoopa Prabhu nla_via = NULL; 897f8efb73cSRoopa Prabhu nla_newdst = NULL; 898f8efb73cSRoopa Prabhu 899f8efb73cSRoopa Prabhu err = -EINVAL; 900f8efb73cSRoopa Prabhu if (!rtnh_ok(rtnh, remaining)) 901f8efb73cSRoopa Prabhu goto errout; 902f8efb73cSRoopa Prabhu 9031c78efa8SRobert Shearman /* neither weighted multipath nor any flags 9041c78efa8SRobert Shearman * are supported 9051c78efa8SRobert Shearman */ 9061c78efa8SRobert Shearman if (rtnh->rtnh_hops || rtnh->rtnh_flags) 9071c78efa8SRobert Shearman goto errout; 9081c78efa8SRobert Shearman 909f8efb73cSRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 910f8efb73cSRoopa Prabhu if (attrlen > 0) { 911f8efb73cSRoopa Prabhu struct nlattr *attrs = rtnh_attrs(rtnh); 912f8efb73cSRoopa Prabhu 913f8efb73cSRoopa Prabhu nla_via = nla_find(attrs, attrlen, RTA_VIA); 914f8efb73cSRoopa Prabhu nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST); 915f8efb73cSRoopa Prabhu } 916f8efb73cSRoopa Prabhu 917cf4b24f0SRobert Shearman err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh, 918a4ac8c98SDavid Ahern rtnh->rtnh_ifindex, nla_via, nla_newdst, 919074350e2SDavid Ahern max_labels, extack); 920f8efb73cSRoopa Prabhu if (err) 921f8efb73cSRoopa Prabhu goto errout; 922f8efb73cSRoopa Prabhu 923c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 924c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 925c89359a4SRoopa Prabhu 926f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 927f8efb73cSRoopa Prabhu nhs++; 928f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 929f8efb73cSRoopa Prabhu 930f8efb73cSRoopa Prabhu rt->rt_nhn = nhs; 931f8efb73cSRoopa Prabhu 932f8efb73cSRoopa Prabhu return 0; 933f8efb73cSRoopa Prabhu 934f8efb73cSRoopa Prabhu errout: 935f8efb73cSRoopa Prabhu return err; 936f8efb73cSRoopa Prabhu } 937f8efb73cSRoopa Prabhu 9383968523fSDan Williams static bool mpls_label_ok(struct net *net, unsigned int *index, 939074350e2SDavid Ahern struct netlink_ext_ack *extack) 940b7b386f4SDavid Ahern { 9413968523fSDan Williams bool is_ok = true; 9423968523fSDan Williams 943b7b386f4SDavid Ahern /* Reserved labels may not be set */ 9443968523fSDan Williams if (*index < MPLS_LABEL_FIRST_UNRESERVED) { 945074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 946074350e2SDavid Ahern "Invalid label - must be MPLS_LABEL_FIRST_UNRESERVED or higher"); 9473968523fSDan Williams is_ok = false; 948074350e2SDavid Ahern } 949b7b386f4SDavid Ahern 950b7b386f4SDavid Ahern /* The full 20 bit range may not be supported. */ 9513968523fSDan Williams if (is_ok && *index >= net->mpls.platform_labels) { 952074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 953074350e2SDavid Ahern "Label >= configured maximum in platform_labels"); 9543968523fSDan Williams is_ok = false; 955074350e2SDavid Ahern } 956b7b386f4SDavid Ahern 9573968523fSDan Williams *index = array_index_nospec(*index, net->mpls.platform_labels); 9583968523fSDan Williams return is_ok; 959b7b386f4SDavid Ahern } 960b7b386f4SDavid Ahern 961074350e2SDavid Ahern static int mpls_route_add(struct mpls_route_config *cfg, 962074350e2SDavid Ahern struct netlink_ext_ack *extack) 963a2519929SEric W. Biederman { 96419d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 965a2519929SEric W. Biederman struct net *net = cfg->rc_nlinfo.nl_net; 966a2519929SEric W. Biederman struct mpls_route *rt, *old; 967a2519929SEric W. Biederman int err = -EINVAL; 968cf4b24f0SRobert Shearman u8 max_via_alen; 969f8efb73cSRoopa Prabhu unsigned index; 970a4ac8c98SDavid Ahern u8 max_labels; 97177ef013aSDavid Ahern u8 nhs; 972a2519929SEric W. Biederman 973a2519929SEric W. Biederman index = cfg->rc_label; 974a2519929SEric W. Biederman 975a2519929SEric W. Biederman /* If a label was not specified during insert pick one */ 976a2519929SEric W. Biederman if ((index == LABEL_NOT_SPECIFIED) && 977a2519929SEric W. Biederman (cfg->rc_nlflags & NLM_F_CREATE)) { 978a2519929SEric W. Biederman index = find_free_label(net); 979a2519929SEric W. Biederman } 980a2519929SEric W. Biederman 9813968523fSDan Williams if (!mpls_label_ok(net, &index, extack)) 982a2519929SEric W. Biederman goto errout; 983a2519929SEric W. Biederman 984a2519929SEric W. Biederman /* Append makes no sense with mpls */ 9850f7bbd58SEric W. Biederman err = -EOPNOTSUPP; 986074350e2SDavid Ahern if (cfg->rc_nlflags & NLM_F_APPEND) { 987074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "MPLS does not support route append"); 988a2519929SEric W. Biederman goto errout; 989074350e2SDavid Ahern } 990a2519929SEric W. Biederman 991a2519929SEric W. Biederman err = -EEXIST; 99219d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 99319d0c341SEric W. Biederman old = rtnl_dereference(platform_label[index]); 994a2519929SEric W. Biederman if ((cfg->rc_nlflags & NLM_F_EXCL) && old) 995a2519929SEric W. Biederman goto errout; 996a2519929SEric W. Biederman 997a2519929SEric W. Biederman err = -EEXIST; 998a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_REPLACE) && old) 999a2519929SEric W. Biederman goto errout; 1000a2519929SEric W. Biederman 1001a2519929SEric W. Biederman err = -ENOENT; 1002a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old) 1003a2519929SEric W. Biederman goto errout; 1004a2519929SEric W. Biederman 1005f8efb73cSRoopa Prabhu err = -EINVAL; 1006a4ac8c98SDavid Ahern if (cfg->rc_mp) { 1007cf4b24f0SRobert Shearman nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, 1008a4ac8c98SDavid Ahern cfg->rc_via_alen, &max_via_alen, 1009a4ac8c98SDavid Ahern &max_labels); 1010a4ac8c98SDavid Ahern } else { 1011a4ac8c98SDavid Ahern max_via_alen = cfg->rc_via_alen; 1012a4ac8c98SDavid Ahern max_labels = cfg->rc_output_labels; 1013a4ac8c98SDavid Ahern nhs = 1; 1014a4ac8c98SDavid Ahern } 1015a4ac8c98SDavid Ahern 1016074350e2SDavid Ahern if (nhs == 0) { 1017074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "Route does not contain a nexthop"); 1018f8efb73cSRoopa Prabhu goto errout; 1019074350e2SDavid Ahern } 1020f8efb73cSRoopa Prabhu 1021a2519929SEric W. Biederman err = -ENOMEM; 1022a4ac8c98SDavid Ahern rt = mpls_rt_alloc(nhs, max_via_alen, max_labels); 1023df1c6316SDavid Ahern if (IS_ERR(rt)) { 1024df1c6316SDavid Ahern err = PTR_ERR(rt); 1025a2519929SEric W. Biederman goto errout; 1026df1c6316SDavid Ahern } 1027a2519929SEric W. Biederman 1028a2519929SEric W. Biederman rt->rt_protocol = cfg->rc_protocol; 1029118d5234SRobert Shearman rt->rt_payload_type = cfg->rc_payload_type; 10305b441ac8SRobert Shearman rt->rt_ttl_propagate = cfg->rc_ttl_propagate; 1031a2519929SEric W. Biederman 1032f8efb73cSRoopa Prabhu if (cfg->rc_mp) 1033074350e2SDavid Ahern err = mpls_nh_build_multi(cfg, rt, max_labels, extack); 1034f8efb73cSRoopa Prabhu else 1035f8efb73cSRoopa Prabhu err = mpls_nh_build_from_cfg(cfg, rt); 1036f8efb73cSRoopa Prabhu if (err) 1037f8efb73cSRoopa Prabhu goto freert; 1038a2519929SEric W. Biederman 1039f8efb73cSRoopa Prabhu mpls_route_update(net, index, rt, &cfg->rc_nlinfo); 1040f8efb73cSRoopa Prabhu 1041a2519929SEric W. Biederman return 0; 1042a2519929SEric W. Biederman 1043f8efb73cSRoopa Prabhu freert: 1044f8efb73cSRoopa Prabhu mpls_rt_free(rt); 1045a2519929SEric W. Biederman errout: 1046a2519929SEric W. Biederman return err; 1047a2519929SEric W. Biederman } 1048a2519929SEric W. Biederman 1049074350e2SDavid Ahern static int mpls_route_del(struct mpls_route_config *cfg, 1050074350e2SDavid Ahern struct netlink_ext_ack *extack) 1051a2519929SEric W. Biederman { 1052a2519929SEric W. Biederman struct net *net = cfg->rc_nlinfo.nl_net; 1053a2519929SEric W. Biederman unsigned index; 1054a2519929SEric W. Biederman int err = -EINVAL; 1055a2519929SEric W. Biederman 1056a2519929SEric W. Biederman index = cfg->rc_label; 1057a2519929SEric W. Biederman 10583968523fSDan Williams if (!mpls_label_ok(net, &index, extack)) 1059a2519929SEric W. Biederman goto errout; 1060a2519929SEric W. Biederman 1061f8efb73cSRoopa Prabhu mpls_route_update(net, index, NULL, &cfg->rc_nlinfo); 1062a2519929SEric W. Biederman 1063a2519929SEric W. Biederman err = 0; 1064a2519929SEric W. Biederman errout: 1065a2519929SEric W. Biederman return err; 1066a2519929SEric W. Biederman } 1067a2519929SEric W. Biederman 106827d69105SRobert Shearman static void mpls_get_stats(struct mpls_dev *mdev, 106927d69105SRobert Shearman struct mpls_link_stats *stats) 107027d69105SRobert Shearman { 107127d69105SRobert Shearman struct mpls_pcpu_stats *p; 107227d69105SRobert Shearman int i; 107327d69105SRobert Shearman 107427d69105SRobert Shearman memset(stats, 0, sizeof(*stats)); 107527d69105SRobert Shearman 107627d69105SRobert Shearman for_each_possible_cpu(i) { 107727d69105SRobert Shearman struct mpls_link_stats local; 107827d69105SRobert Shearman unsigned int start; 107927d69105SRobert Shearman 108027d69105SRobert Shearman p = per_cpu_ptr(mdev->stats, i); 108127d69105SRobert Shearman do { 108227d69105SRobert Shearman start = u64_stats_fetch_begin(&p->syncp); 108327d69105SRobert Shearman local = p->stats; 108427d69105SRobert Shearman } while (u64_stats_fetch_retry(&p->syncp, start)); 108527d69105SRobert Shearman 108627d69105SRobert Shearman stats->rx_packets += local.rx_packets; 108727d69105SRobert Shearman stats->rx_bytes += local.rx_bytes; 108827d69105SRobert Shearman stats->tx_packets += local.tx_packets; 108927d69105SRobert Shearman stats->tx_bytes += local.tx_bytes; 109027d69105SRobert Shearman stats->rx_errors += local.rx_errors; 109127d69105SRobert Shearman stats->tx_errors += local.tx_errors; 109227d69105SRobert Shearman stats->rx_dropped += local.rx_dropped; 109327d69105SRobert Shearman stats->tx_dropped += local.tx_dropped; 109427d69105SRobert Shearman stats->rx_noroute += local.rx_noroute; 109527d69105SRobert Shearman } 109627d69105SRobert Shearman } 109727d69105SRobert Shearman 109827d69105SRobert Shearman static int mpls_fill_stats_af(struct sk_buff *skb, 109927d69105SRobert Shearman const struct net_device *dev) 110027d69105SRobert Shearman { 110127d69105SRobert Shearman struct mpls_link_stats *stats; 110227d69105SRobert Shearman struct mpls_dev *mdev; 110327d69105SRobert Shearman struct nlattr *nla; 110427d69105SRobert Shearman 110527d69105SRobert Shearman mdev = mpls_dev_get(dev); 110627d69105SRobert Shearman if (!mdev) 110727d69105SRobert Shearman return -ENODATA; 110827d69105SRobert Shearman 110927d69105SRobert Shearman nla = nla_reserve_64bit(skb, MPLS_STATS_LINK, 111027d69105SRobert Shearman sizeof(struct mpls_link_stats), 111127d69105SRobert Shearman MPLS_STATS_UNSPEC); 111227d69105SRobert Shearman if (!nla) 111327d69105SRobert Shearman return -EMSGSIZE; 111427d69105SRobert Shearman 111527d69105SRobert Shearman stats = nla_data(nla); 111627d69105SRobert Shearman mpls_get_stats(mdev, stats); 111727d69105SRobert Shearman 111827d69105SRobert Shearman return 0; 111927d69105SRobert Shearman } 112027d69105SRobert Shearman 112127d69105SRobert Shearman static size_t mpls_get_stats_af_size(const struct net_device *dev) 112227d69105SRobert Shearman { 112327d69105SRobert Shearman struct mpls_dev *mdev; 112427d69105SRobert Shearman 112527d69105SRobert Shearman mdev = mpls_dev_get(dev); 112627d69105SRobert Shearman if (!mdev) 112727d69105SRobert Shearman return 0; 112827d69105SRobert Shearman 112927d69105SRobert Shearman return nla_total_size_64bit(sizeof(struct mpls_link_stats)); 113027d69105SRobert Shearman } 113127d69105SRobert Shearman 113224045a03SDavid Ahern static int mpls_netconf_fill_devconf(struct sk_buff *skb, struct mpls_dev *mdev, 113324045a03SDavid Ahern u32 portid, u32 seq, int event, 113424045a03SDavid Ahern unsigned int flags, int type) 113524045a03SDavid Ahern { 113624045a03SDavid Ahern struct nlmsghdr *nlh; 113724045a03SDavid Ahern struct netconfmsg *ncm; 113824045a03SDavid Ahern bool all = false; 113924045a03SDavid Ahern 114024045a03SDavid Ahern nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 114124045a03SDavid Ahern flags); 114224045a03SDavid Ahern if (!nlh) 114324045a03SDavid Ahern return -EMSGSIZE; 114424045a03SDavid Ahern 114524045a03SDavid Ahern if (type == NETCONFA_ALL) 114624045a03SDavid Ahern all = true; 114724045a03SDavid Ahern 114824045a03SDavid Ahern ncm = nlmsg_data(nlh); 114924045a03SDavid Ahern ncm->ncm_family = AF_MPLS; 115024045a03SDavid Ahern 115124045a03SDavid Ahern if (nla_put_s32(skb, NETCONFA_IFINDEX, mdev->dev->ifindex) < 0) 115224045a03SDavid Ahern goto nla_put_failure; 115324045a03SDavid Ahern 115424045a03SDavid Ahern if ((all || type == NETCONFA_INPUT) && 115524045a03SDavid Ahern nla_put_s32(skb, NETCONFA_INPUT, 115624045a03SDavid Ahern mdev->input_enabled) < 0) 115724045a03SDavid Ahern goto nla_put_failure; 115824045a03SDavid Ahern 115924045a03SDavid Ahern nlmsg_end(skb, nlh); 116024045a03SDavid Ahern return 0; 116124045a03SDavid Ahern 116224045a03SDavid Ahern nla_put_failure: 116324045a03SDavid Ahern nlmsg_cancel(skb, nlh); 116424045a03SDavid Ahern return -EMSGSIZE; 116524045a03SDavid Ahern } 116624045a03SDavid Ahern 116724045a03SDavid Ahern static int mpls_netconf_msgsize_devconf(int type) 116824045a03SDavid Ahern { 116924045a03SDavid Ahern int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 117024045a03SDavid Ahern + nla_total_size(4); /* NETCONFA_IFINDEX */ 117124045a03SDavid Ahern bool all = false; 117224045a03SDavid Ahern 117324045a03SDavid Ahern if (type == NETCONFA_ALL) 117424045a03SDavid Ahern all = true; 117524045a03SDavid Ahern 117624045a03SDavid Ahern if (all || type == NETCONFA_INPUT) 117724045a03SDavid Ahern size += nla_total_size(4); 117824045a03SDavid Ahern 117924045a03SDavid Ahern return size; 118024045a03SDavid Ahern } 118124045a03SDavid Ahern 1182823566aeSDavid Ahern static void mpls_netconf_notify_devconf(struct net *net, int event, 1183823566aeSDavid Ahern int type, struct mpls_dev *mdev) 118424045a03SDavid Ahern { 118524045a03SDavid Ahern struct sk_buff *skb; 118624045a03SDavid Ahern int err = -ENOBUFS; 118724045a03SDavid Ahern 118824045a03SDavid Ahern skb = nlmsg_new(mpls_netconf_msgsize_devconf(type), GFP_KERNEL); 118924045a03SDavid Ahern if (!skb) 119024045a03SDavid Ahern goto errout; 119124045a03SDavid Ahern 1192823566aeSDavid Ahern err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, event, 0, type); 119324045a03SDavid Ahern if (err < 0) { 119424045a03SDavid Ahern /* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */ 119524045a03SDavid Ahern WARN_ON(err == -EMSGSIZE); 119624045a03SDavid Ahern kfree_skb(skb); 119724045a03SDavid Ahern goto errout; 119824045a03SDavid Ahern } 119924045a03SDavid Ahern 120024045a03SDavid Ahern rtnl_notify(skb, net, 0, RTNLGRP_MPLS_NETCONF, NULL, GFP_KERNEL); 120124045a03SDavid Ahern return; 120224045a03SDavid Ahern errout: 120324045a03SDavid Ahern if (err < 0) 120424045a03SDavid Ahern rtnl_set_sk_err(net, RTNLGRP_MPLS_NETCONF, err); 120524045a03SDavid Ahern } 120624045a03SDavid Ahern 120724045a03SDavid Ahern static const struct nla_policy devconf_mpls_policy[NETCONFA_MAX + 1] = { 120824045a03SDavid Ahern [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 120924045a03SDavid Ahern }; 121024045a03SDavid Ahern 12110c4056eeSJakub Kicinski static int mpls_netconf_valid_get_req(struct sk_buff *skb, 12120c4056eeSJakub Kicinski const struct nlmsghdr *nlh, 12130c4056eeSJakub Kicinski struct nlattr **tb, 12140c4056eeSJakub Kicinski struct netlink_ext_ack *extack) 12150c4056eeSJakub Kicinski { 12160c4056eeSJakub Kicinski int i, err; 12170c4056eeSJakub Kicinski 12180c4056eeSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 12190c4056eeSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, 12200c4056eeSJakub Kicinski "Invalid header for netconf get request"); 12210c4056eeSJakub Kicinski return -EINVAL; 12220c4056eeSJakub Kicinski } 12230c4056eeSJakub Kicinski 12240c4056eeSJakub Kicinski if (!netlink_strict_get_check(skb)) 12258cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 12268cb08174SJohannes Berg tb, NETCONFA_MAX, 12278cb08174SJohannes Berg devconf_mpls_policy, extack); 12280c4056eeSJakub Kicinski 12298cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 12308cb08174SJohannes Berg tb, NETCONFA_MAX, 12318cb08174SJohannes Berg devconf_mpls_policy, extack); 12320c4056eeSJakub Kicinski if (err) 12330c4056eeSJakub Kicinski return err; 12340c4056eeSJakub Kicinski 12350c4056eeSJakub Kicinski for (i = 0; i <= NETCONFA_MAX; i++) { 12360c4056eeSJakub Kicinski if (!tb[i]) 12370c4056eeSJakub Kicinski continue; 12380c4056eeSJakub Kicinski 12390c4056eeSJakub Kicinski switch (i) { 12400c4056eeSJakub Kicinski case NETCONFA_IFINDEX: 12410c4056eeSJakub Kicinski break; 12420c4056eeSJakub Kicinski default: 12430c4056eeSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request"); 12440c4056eeSJakub Kicinski return -EINVAL; 12450c4056eeSJakub Kicinski } 12460c4056eeSJakub Kicinski } 12470c4056eeSJakub Kicinski 12480c4056eeSJakub Kicinski return 0; 12490c4056eeSJakub Kicinski } 12500c4056eeSJakub Kicinski 125124045a03SDavid Ahern static int mpls_netconf_get_devconf(struct sk_buff *in_skb, 1252c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 1253c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 125424045a03SDavid Ahern { 125524045a03SDavid Ahern struct net *net = sock_net(in_skb->sk); 125624045a03SDavid Ahern struct nlattr *tb[NETCONFA_MAX + 1]; 125724045a03SDavid Ahern struct net_device *dev; 125824045a03SDavid Ahern struct mpls_dev *mdev; 125924045a03SDavid Ahern struct sk_buff *skb; 126024045a03SDavid Ahern int ifindex; 126124045a03SDavid Ahern int err; 126224045a03SDavid Ahern 12630c4056eeSJakub Kicinski err = mpls_netconf_valid_get_req(in_skb, nlh, tb, extack); 126424045a03SDavid Ahern if (err < 0) 126524045a03SDavid Ahern goto errout; 126624045a03SDavid Ahern 126724045a03SDavid Ahern err = -EINVAL; 126824045a03SDavid Ahern if (!tb[NETCONFA_IFINDEX]) 126924045a03SDavid Ahern goto errout; 127024045a03SDavid Ahern 127124045a03SDavid Ahern ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 127224045a03SDavid Ahern dev = __dev_get_by_index(net, ifindex); 127324045a03SDavid Ahern if (!dev) 127424045a03SDavid Ahern goto errout; 127524045a03SDavid Ahern 127624045a03SDavid Ahern mdev = mpls_dev_get(dev); 127724045a03SDavid Ahern if (!mdev) 127824045a03SDavid Ahern goto errout; 127924045a03SDavid Ahern 128024045a03SDavid Ahern err = -ENOBUFS; 128124045a03SDavid Ahern skb = nlmsg_new(mpls_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 128224045a03SDavid Ahern if (!skb) 128324045a03SDavid Ahern goto errout; 128424045a03SDavid Ahern 128524045a03SDavid Ahern err = mpls_netconf_fill_devconf(skb, mdev, 128624045a03SDavid Ahern NETLINK_CB(in_skb).portid, 128724045a03SDavid Ahern nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 128824045a03SDavid Ahern NETCONFA_ALL); 128924045a03SDavid Ahern if (err < 0) { 129024045a03SDavid Ahern /* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */ 129124045a03SDavid Ahern WARN_ON(err == -EMSGSIZE); 129224045a03SDavid Ahern kfree_skb(skb); 129324045a03SDavid Ahern goto errout; 129424045a03SDavid Ahern } 129524045a03SDavid Ahern err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 129624045a03SDavid Ahern errout: 129724045a03SDavid Ahern return err; 129824045a03SDavid Ahern } 129924045a03SDavid Ahern 130024045a03SDavid Ahern static int mpls_netconf_dump_devconf(struct sk_buff *skb, 130124045a03SDavid Ahern struct netlink_callback *cb) 130224045a03SDavid Ahern { 1303addd383fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 130424045a03SDavid Ahern struct net *net = sock_net(skb->sk); 130524045a03SDavid Ahern struct hlist_head *head; 130624045a03SDavid Ahern struct net_device *dev; 130724045a03SDavid Ahern struct mpls_dev *mdev; 130824045a03SDavid Ahern int idx, s_idx; 130924045a03SDavid Ahern int h, s_h; 131024045a03SDavid Ahern 1311addd383fSDavid Ahern if (cb->strict_check) { 1312addd383fSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 1313addd383fSDavid Ahern struct netconfmsg *ncm; 1314addd383fSDavid Ahern 1315addd383fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 1316addd383fSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request"); 1317addd383fSDavid Ahern return -EINVAL; 1318addd383fSDavid Ahern } 1319addd383fSDavid Ahern 1320addd383fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 1321addd383fSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request"); 1322addd383fSDavid Ahern return -EINVAL; 1323addd383fSDavid Ahern } 1324addd383fSDavid Ahern } 1325addd383fSDavid Ahern 132624045a03SDavid Ahern s_h = cb->args[0]; 132724045a03SDavid Ahern s_idx = idx = cb->args[1]; 132824045a03SDavid Ahern 132924045a03SDavid Ahern for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 133024045a03SDavid Ahern idx = 0; 133124045a03SDavid Ahern head = &net->dev_index_head[h]; 133224045a03SDavid Ahern rcu_read_lock(); 133324045a03SDavid Ahern cb->seq = net->dev_base_seq; 133424045a03SDavid Ahern hlist_for_each_entry_rcu(dev, head, index_hlist) { 133524045a03SDavid Ahern if (idx < s_idx) 133624045a03SDavid Ahern goto cont; 133724045a03SDavid Ahern mdev = mpls_dev_get(dev); 133824045a03SDavid Ahern if (!mdev) 133924045a03SDavid Ahern goto cont; 134024045a03SDavid Ahern if (mpls_netconf_fill_devconf(skb, mdev, 134124045a03SDavid Ahern NETLINK_CB(cb->skb).portid, 1342addd383fSDavid Ahern nlh->nlmsg_seq, 134324045a03SDavid Ahern RTM_NEWNETCONF, 134424045a03SDavid Ahern NLM_F_MULTI, 134524045a03SDavid Ahern NETCONFA_ALL) < 0) { 134624045a03SDavid Ahern rcu_read_unlock(); 134724045a03SDavid Ahern goto done; 134824045a03SDavid Ahern } 134924045a03SDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 135024045a03SDavid Ahern cont: 135124045a03SDavid Ahern idx++; 135224045a03SDavid Ahern } 135324045a03SDavid Ahern rcu_read_unlock(); 135424045a03SDavid Ahern } 135524045a03SDavid Ahern done: 135624045a03SDavid Ahern cb->args[0] = h; 135724045a03SDavid Ahern cb->args[1] = idx; 135824045a03SDavid Ahern 135924045a03SDavid Ahern return skb->len; 136024045a03SDavid Ahern } 136124045a03SDavid Ahern 136237bde799SRobert Shearman #define MPLS_PERDEV_SYSCTL_OFFSET(field) \ 136337bde799SRobert Shearman (&((struct mpls_dev *)0)->field) 136437bde799SRobert Shearman 136524045a03SDavid Ahern static int mpls_conf_proc(struct ctl_table *ctl, int write, 136624045a03SDavid Ahern void __user *buffer, 136724045a03SDavid Ahern size_t *lenp, loff_t *ppos) 136824045a03SDavid Ahern { 136924045a03SDavid Ahern int oval = *(int *)ctl->data; 137024045a03SDavid Ahern int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 137124045a03SDavid Ahern 137224045a03SDavid Ahern if (write) { 137324045a03SDavid Ahern struct mpls_dev *mdev = ctl->extra1; 137424045a03SDavid Ahern int i = (int *)ctl->data - (int *)mdev; 137524045a03SDavid Ahern struct net *net = ctl->extra2; 137624045a03SDavid Ahern int val = *(int *)ctl->data; 137724045a03SDavid Ahern 137824045a03SDavid Ahern if (i == offsetof(struct mpls_dev, input_enabled) && 137924045a03SDavid Ahern val != oval) { 1380823566aeSDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, 1381823566aeSDavid Ahern NETCONFA_INPUT, mdev); 138224045a03SDavid Ahern } 138324045a03SDavid Ahern } 138424045a03SDavid Ahern 138524045a03SDavid Ahern return ret; 138624045a03SDavid Ahern } 138724045a03SDavid Ahern 138837bde799SRobert Shearman static const struct ctl_table mpls_dev_table[] = { 138937bde799SRobert Shearman { 139037bde799SRobert Shearman .procname = "input", 139137bde799SRobert Shearman .maxlen = sizeof(int), 139237bde799SRobert Shearman .mode = 0644, 139324045a03SDavid Ahern .proc_handler = mpls_conf_proc, 139437bde799SRobert Shearman .data = MPLS_PERDEV_SYSCTL_OFFSET(input_enabled), 139537bde799SRobert Shearman }, 139637bde799SRobert Shearman { } 139737bde799SRobert Shearman }; 139837bde799SRobert Shearman 139937bde799SRobert Shearman static int mpls_dev_sysctl_register(struct net_device *dev, 140037bde799SRobert Shearman struct mpls_dev *mdev) 140137bde799SRobert Shearman { 140237bde799SRobert Shearman char path[sizeof("net/mpls/conf/") + IFNAMSIZ]; 140324045a03SDavid Ahern struct net *net = dev_net(dev); 140437bde799SRobert Shearman struct ctl_table *table; 140537bde799SRobert Shearman int i; 140637bde799SRobert Shearman 140737bde799SRobert Shearman table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL); 140837bde799SRobert Shearman if (!table) 140937bde799SRobert Shearman goto out; 141037bde799SRobert Shearman 141137bde799SRobert Shearman /* Table data contains only offsets relative to the base of 141237bde799SRobert Shearman * the mdev at this point, so make them absolute. 141337bde799SRobert Shearman */ 141424045a03SDavid Ahern for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) { 141537bde799SRobert Shearman table[i].data = (char *)mdev + (uintptr_t)table[i].data; 141624045a03SDavid Ahern table[i].extra1 = mdev; 141724045a03SDavid Ahern table[i].extra2 = net; 141824045a03SDavid Ahern } 141937bde799SRobert Shearman 142037bde799SRobert Shearman snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); 142137bde799SRobert Shearman 14221182e4d0SDavid Ahern mdev->sysctl = register_net_sysctl(net, path, table); 142337bde799SRobert Shearman if (!mdev->sysctl) 142437bde799SRobert Shearman goto free; 142537bde799SRobert Shearman 14261182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev); 142737bde799SRobert Shearman return 0; 142837bde799SRobert Shearman 142937bde799SRobert Shearman free: 143037bde799SRobert Shearman kfree(table); 143137bde799SRobert Shearman out: 143237bde799SRobert Shearman return -ENOBUFS; 143337bde799SRobert Shearman } 143437bde799SRobert Shearman 14351182e4d0SDavid Ahern static void mpls_dev_sysctl_unregister(struct net_device *dev, 14361182e4d0SDavid Ahern struct mpls_dev *mdev) 143737bde799SRobert Shearman { 14381182e4d0SDavid Ahern struct net *net = dev_net(dev); 143937bde799SRobert Shearman struct ctl_table *table; 144037bde799SRobert Shearman 144137bde799SRobert Shearman table = mdev->sysctl->ctl_table_arg; 144237bde799SRobert Shearman unregister_net_sysctl_table(mdev->sysctl); 144337bde799SRobert Shearman kfree(table); 14441182e4d0SDavid Ahern 14451182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev); 144637bde799SRobert Shearman } 144737bde799SRobert Shearman 144803c57747SRobert Shearman static struct mpls_dev *mpls_add_dev(struct net_device *dev) 144903c57747SRobert Shearman { 145003c57747SRobert Shearman struct mpls_dev *mdev; 145103c57747SRobert Shearman int err = -ENOMEM; 145227d69105SRobert Shearman int i; 145303c57747SRobert Shearman 145403c57747SRobert Shearman ASSERT_RTNL(); 145503c57747SRobert Shearman 145603c57747SRobert Shearman mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 145703c57747SRobert Shearman if (!mdev) 145803c57747SRobert Shearman return ERR_PTR(err); 145903c57747SRobert Shearman 146027d69105SRobert Shearman mdev->stats = alloc_percpu(struct mpls_pcpu_stats); 146127d69105SRobert Shearman if (!mdev->stats) 146227d69105SRobert Shearman goto free; 146327d69105SRobert Shearman 146427d69105SRobert Shearman for_each_possible_cpu(i) { 146527d69105SRobert Shearman struct mpls_pcpu_stats *mpls_stats; 146627d69105SRobert Shearman 146727d69105SRobert Shearman mpls_stats = per_cpu_ptr(mdev->stats, i); 146827d69105SRobert Shearman u64_stats_init(&mpls_stats->syncp); 146927d69105SRobert Shearman } 147027d69105SRobert Shearman 14711182e4d0SDavid Ahern mdev->dev = dev; 14721182e4d0SDavid Ahern 147337bde799SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 147437bde799SRobert Shearman if (err) 147537bde799SRobert Shearman goto free; 147637bde799SRobert Shearman 147703c57747SRobert Shearman rcu_assign_pointer(dev->mpls_ptr, mdev); 147803c57747SRobert Shearman 147903c57747SRobert Shearman return mdev; 148037bde799SRobert Shearman 148137bde799SRobert Shearman free: 148227d69105SRobert Shearman free_percpu(mdev->stats); 148337bde799SRobert Shearman kfree(mdev); 148437bde799SRobert Shearman return ERR_PTR(err); 148503c57747SRobert Shearman } 148603c57747SRobert Shearman 148727d69105SRobert Shearman static void mpls_dev_destroy_rcu(struct rcu_head *head) 148827d69105SRobert Shearman { 148927d69105SRobert Shearman struct mpls_dev *mdev = container_of(head, struct mpls_dev, rcu); 149027d69105SRobert Shearman 149127d69105SRobert Shearman free_percpu(mdev->stats); 149227d69105SRobert Shearman kfree(mdev); 149327d69105SRobert Shearman } 149427d69105SRobert Shearman 1495c89359a4SRoopa Prabhu static void mpls_ifdown(struct net_device *dev, int event) 14960189197fSEric W. Biederman { 149719d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 14980189197fSEric W. Biederman struct net *net = dev_net(dev); 149977ef013aSDavid Ahern u8 alive, deleted; 15000189197fSEric W. Biederman unsigned index; 15010189197fSEric W. Biederman 150219d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 15030189197fSEric W. Biederman for (index = 0; index < net->mpls.platform_labels; index++) { 150419d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 1505c89359a4SRoopa Prabhu 15060189197fSEric W. Biederman if (!rt) 15070189197fSEric W. Biederman continue; 1508c89359a4SRoopa Prabhu 150961733c91SDavid Ahern alive = 0; 15104ea8efadSDavid Ahern deleted = 0; 1511c89359a4SRoopa Prabhu change_nexthops(rt) { 151239eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 151339eb8cd1SDavid Ahern 1514f8efb73cSRoopa Prabhu if (rtnl_dereference(nh->nh_dev) != dev) 151561733c91SDavid Ahern goto next; 151661733c91SDavid Ahern 1517c89359a4SRoopa Prabhu switch (event) { 1518c89359a4SRoopa Prabhu case NETDEV_DOWN: 1519c89359a4SRoopa Prabhu case NETDEV_UNREGISTER: 152039eb8cd1SDavid Ahern nh_flags |= RTNH_F_DEAD; 1521c89359a4SRoopa Prabhu /* fall through */ 1522c89359a4SRoopa Prabhu case NETDEV_CHANGE: 152339eb8cd1SDavid Ahern nh_flags |= RTNH_F_LINKDOWN; 1524c89359a4SRoopa Prabhu break; 1525c89359a4SRoopa Prabhu } 1526c89359a4SRoopa Prabhu if (event == NETDEV_UNREGISTER) 1527c89359a4SRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, NULL); 152839eb8cd1SDavid Ahern 152939eb8cd1SDavid Ahern if (nh->nh_flags != nh_flags) 153039eb8cd1SDavid Ahern WRITE_ONCE(nh->nh_flags, nh_flags); 153161733c91SDavid Ahern next: 153239eb8cd1SDavid Ahern if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))) 153361733c91SDavid Ahern alive++; 15344ea8efadSDavid Ahern if (!rtnl_dereference(nh->nh_dev)) 15354ea8efadSDavid Ahern deleted++; 1536f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 153761733c91SDavid Ahern 153861733c91SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 15394ea8efadSDavid Ahern 15404ea8efadSDavid Ahern /* if there are no more nexthops, delete the route */ 15414ea8efadSDavid Ahern if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn) 15424ea8efadSDavid Ahern mpls_route_update(net, index, NULL, NULL); 15430189197fSEric W. Biederman } 1544c89359a4SRoopa Prabhu } 154503c57747SRobert Shearman 154639eb8cd1SDavid Ahern static void mpls_ifup(struct net_device *dev, unsigned int flags) 1547c89359a4SRoopa Prabhu { 1548c89359a4SRoopa Prabhu struct mpls_route __rcu **platform_label; 1549c89359a4SRoopa Prabhu struct net *net = dev_net(dev); 1550c89359a4SRoopa Prabhu unsigned index; 155177ef013aSDavid Ahern u8 alive; 155237bde799SRobert Shearman 1553c89359a4SRoopa Prabhu platform_label = rtnl_dereference(net->mpls.platform_label); 1554c89359a4SRoopa Prabhu for (index = 0; index < net->mpls.platform_labels; index++) { 1555c89359a4SRoopa Prabhu struct mpls_route *rt = rtnl_dereference(platform_label[index]); 155603c57747SRobert Shearman 1557c89359a4SRoopa Prabhu if (!rt) 1558c89359a4SRoopa Prabhu continue; 1559c89359a4SRoopa Prabhu 1560c89359a4SRoopa Prabhu alive = 0; 1561c89359a4SRoopa Prabhu change_nexthops(rt) { 156239eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 1563c89359a4SRoopa Prabhu struct net_device *nh_dev = 1564c89359a4SRoopa Prabhu rtnl_dereference(nh->nh_dev); 1565c89359a4SRoopa Prabhu 156639eb8cd1SDavid Ahern if (!(nh_flags & flags)) { 1567c89359a4SRoopa Prabhu alive++; 1568c89359a4SRoopa Prabhu continue; 1569c89359a4SRoopa Prabhu } 1570c89359a4SRoopa Prabhu if (nh_dev != dev) 1571c89359a4SRoopa Prabhu continue; 1572c89359a4SRoopa Prabhu alive++; 157339eb8cd1SDavid Ahern nh_flags &= ~flags; 1574c2e8471dSRoopa Prabhu WRITE_ONCE(nh->nh_flags, nh_flags); 1575c89359a4SRoopa Prabhu } endfor_nexthops(rt); 1576c89359a4SRoopa Prabhu 157739eb8cd1SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 1578c89359a4SRoopa Prabhu } 15790189197fSEric W. Biederman } 15800189197fSEric W. Biederman 15810189197fSEric W. Biederman static int mpls_dev_notify(struct notifier_block *this, unsigned long event, 15820189197fSEric W. Biederman void *ptr) 15830189197fSEric W. Biederman { 15840189197fSEric W. Biederman struct net_device *dev = netdev_notifier_info_to_dev(ptr); 158503c57747SRobert Shearman struct mpls_dev *mdev; 1586c89359a4SRoopa Prabhu unsigned int flags; 15870189197fSEric W. Biederman 1588c89359a4SRoopa Prabhu if (event == NETDEV_REGISTER) { 1589d8e2262aSSaif Hasan 1590d8e2262aSSaif Hasan /* For now just support Ethernet, IPGRE, IP6GRE, SIT and 1591d8e2262aSSaif Hasan * IPIP devices 1592d8e2262aSSaif Hasan */ 15930d227a86SSimon Horman if (dev->type == ARPHRD_ETHER || 15940d227a86SSimon Horman dev->type == ARPHRD_LOOPBACK || 1595407f31beSSimon Horman dev->type == ARPHRD_IPGRE || 1596d8e2262aSSaif Hasan dev->type == ARPHRD_IP6GRE || 1597407f31beSSimon Horman dev->type == ARPHRD_SIT || 1598407f31beSSimon Horman dev->type == ARPHRD_TUNNEL) { 159903c57747SRobert Shearman mdev = mpls_add_dev(dev); 160003c57747SRobert Shearman if (IS_ERR(mdev)) 160103c57747SRobert Shearman return notifier_from_errno(PTR_ERR(mdev)); 160203c57747SRobert Shearman } 1603c89359a4SRoopa Prabhu return NOTIFY_OK; 1604c89359a4SRoopa Prabhu } 160503c57747SRobert Shearman 1606c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1607c89359a4SRoopa Prabhu if (!mdev) 1608c89359a4SRoopa Prabhu return NOTIFY_OK; 1609c89359a4SRoopa Prabhu 1610c89359a4SRoopa Prabhu switch (event) { 1611c89359a4SRoopa Prabhu case NETDEV_DOWN: 1612c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1613c89359a4SRoopa Prabhu break; 1614c89359a4SRoopa Prabhu case NETDEV_UP: 1615c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1616c89359a4SRoopa Prabhu if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1617c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1618c89359a4SRoopa Prabhu else 1619c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD); 1620c89359a4SRoopa Prabhu break; 1621c89359a4SRoopa Prabhu case NETDEV_CHANGE: 1622c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1623c89359a4SRoopa Prabhu if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1624c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1625c89359a4SRoopa Prabhu else 1626c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1627c89359a4SRoopa Prabhu break; 16280189197fSEric W. Biederman case NETDEV_UNREGISTER: 1629c89359a4SRoopa Prabhu mpls_ifdown(dev, event); 1630c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1631c89359a4SRoopa Prabhu if (mdev) { 16321182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 1633c89359a4SRoopa Prabhu RCU_INIT_POINTER(dev->mpls_ptr, NULL); 163427d69105SRobert Shearman call_rcu(&mdev->rcu, mpls_dev_destroy_rcu); 1635c89359a4SRoopa Prabhu } 16360189197fSEric W. Biederman break; 16370fae3bf0SRobert Shearman case NETDEV_CHANGENAME: 16380fae3bf0SRobert Shearman mdev = mpls_dev_get(dev); 16390fae3bf0SRobert Shearman if (mdev) { 16400fae3bf0SRobert Shearman int err; 16410fae3bf0SRobert Shearman 16421182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 16430fae3bf0SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 16440fae3bf0SRobert Shearman if (err) 16450fae3bf0SRobert Shearman return notifier_from_errno(err); 16460fae3bf0SRobert Shearman } 16470fae3bf0SRobert Shearman break; 16480189197fSEric W. Biederman } 16490189197fSEric W. Biederman return NOTIFY_OK; 16500189197fSEric W. Biederman } 16510189197fSEric W. Biederman 16520189197fSEric W. Biederman static struct notifier_block mpls_dev_notifier = { 16530189197fSEric W. Biederman .notifier_call = mpls_dev_notify, 16540189197fSEric W. Biederman }; 16550189197fSEric W. Biederman 165603c05665SEric W. Biederman static int nla_put_via(struct sk_buff *skb, 1657b79bda3dSEric W. Biederman u8 table, const void *addr, int alen) 165803c05665SEric W. Biederman { 1659b79bda3dSEric W. Biederman static const int table_to_family[NEIGH_NR_TABLES + 1] = { 1660b79bda3dSEric W. Biederman AF_INET, AF_INET6, AF_DECnet, AF_PACKET, 1661b79bda3dSEric W. Biederman }; 166203c05665SEric W. Biederman struct nlattr *nla; 166303c05665SEric W. Biederman struct rtvia *via; 1664b79bda3dSEric W. Biederman int family = AF_UNSPEC; 166503c05665SEric W. Biederman 166603c05665SEric W. Biederman nla = nla_reserve(skb, RTA_VIA, alen + 2); 166703c05665SEric W. Biederman if (!nla) 166803c05665SEric W. Biederman return -EMSGSIZE; 166903c05665SEric W. Biederman 1670b79bda3dSEric W. Biederman if (table <= NEIGH_NR_TABLES) 1671b79bda3dSEric W. Biederman family = table_to_family[table]; 1672b79bda3dSEric W. Biederman 167303c05665SEric W. Biederman via = nla_data(nla); 167403c05665SEric W. Biederman via->rtvia_family = family; 167503c05665SEric W. Biederman memcpy(via->rtvia_addr, addr, alen); 167603c05665SEric W. Biederman return 0; 167703c05665SEric W. Biederman } 167803c05665SEric W. Biederman 1679966bae33SEric W. Biederman int nla_put_labels(struct sk_buff *skb, int attrtype, 1680966bae33SEric W. Biederman u8 labels, const u32 label[]) 1681966bae33SEric W. Biederman { 1682966bae33SEric W. Biederman struct nlattr *nla; 1683966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1684966bae33SEric W. Biederman bool bos; 1685966bae33SEric W. Biederman int i; 1686966bae33SEric W. Biederman nla = nla_reserve(skb, attrtype, labels*4); 1687966bae33SEric W. Biederman if (!nla) 1688966bae33SEric W. Biederman return -EMSGSIZE; 1689966bae33SEric W. Biederman 1690966bae33SEric W. Biederman nla_label = nla_data(nla); 1691966bae33SEric W. Biederman bos = true; 1692966bae33SEric W. Biederman for (i = labels - 1; i >= 0; i--) { 1693966bae33SEric W. Biederman nla_label[i] = mpls_entry_encode(label[i], 0, 0, bos); 1694966bae33SEric W. Biederman bos = false; 1695966bae33SEric W. Biederman } 1696966bae33SEric W. Biederman 1697966bae33SEric W. Biederman return 0; 1698966bae33SEric W. Biederman } 1699face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_put_labels); 1700966bae33SEric W. Biederman 1701a1f10abeSDavid Ahern int nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels, 1702a1f10abeSDavid Ahern u32 label[], struct netlink_ext_ack *extack) 1703966bae33SEric W. Biederman { 1704966bae33SEric W. Biederman unsigned len = nla_len(nla); 1705966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1706a4ac8c98SDavid Ahern u8 nla_labels; 1707966bae33SEric W. Biederman bool bos; 1708966bae33SEric W. Biederman int i; 1709966bae33SEric W. Biederman 1710a4ac8c98SDavid Ahern /* len needs to be an even multiple of 4 (the label size). Number 1711a4ac8c98SDavid Ahern * of labels is a u8 so check for overflow. 1712a4ac8c98SDavid Ahern */ 1713a1f10abeSDavid Ahern if (len & 3 || len / 4 > 255) { 1714a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1715a1f10abeSDavid Ahern "Invalid length for labels attribute"); 1716966bae33SEric W. Biederman return -EINVAL; 1717a1f10abeSDavid Ahern } 1718966bae33SEric W. Biederman 1719966bae33SEric W. Biederman /* Limit the number of new labels allowed */ 1720966bae33SEric W. Biederman nla_labels = len/4; 1721a1f10abeSDavid Ahern if (nla_labels > max_labels) { 1722a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, "Too many labels"); 1723966bae33SEric W. Biederman return -EINVAL; 1724a1f10abeSDavid Ahern } 1725966bae33SEric W. Biederman 1726a4ac8c98SDavid Ahern /* when label == NULL, caller wants number of labels */ 1727a4ac8c98SDavid Ahern if (!label) 1728a4ac8c98SDavid Ahern goto out; 1729a4ac8c98SDavid Ahern 1730966bae33SEric W. Biederman nla_label = nla_data(nla); 1731966bae33SEric W. Biederman bos = true; 1732966bae33SEric W. Biederman for (i = nla_labels - 1; i >= 0; i--, bos = false) { 1733966bae33SEric W. Biederman struct mpls_entry_decoded dec; 1734966bae33SEric W. Biederman dec = mpls_entry_decode(nla_label + i); 1735966bae33SEric W. Biederman 1736966bae33SEric W. Biederman /* Ensure the bottom of stack flag is properly set 1737966bae33SEric W. Biederman * and ttl and tc are both clear. 1738966bae33SEric W. Biederman */ 1739a1f10abeSDavid Ahern if (dec.ttl) { 1740a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1741a1f10abeSDavid Ahern "TTL in label must be 0"); 1742966bae33SEric W. Biederman return -EINVAL; 1743a1f10abeSDavid Ahern } 1744a1f10abeSDavid Ahern 1745a1f10abeSDavid Ahern if (dec.tc) { 1746a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1747a1f10abeSDavid Ahern "Traffic class in label must be 0"); 1748a1f10abeSDavid Ahern return -EINVAL; 1749a1f10abeSDavid Ahern } 1750a1f10abeSDavid Ahern 1751a1f10abeSDavid Ahern if (dec.bos != bos) { 1752a1f10abeSDavid Ahern NL_SET_BAD_ATTR(extack, nla); 1753a1f10abeSDavid Ahern if (bos) { 1754a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, 1755a1f10abeSDavid Ahern "BOS bit must be set in first label"); 1756a1f10abeSDavid Ahern } else { 1757a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, 1758a1f10abeSDavid Ahern "BOS bit can only be set in first label"); 1759a1f10abeSDavid Ahern } 1760a1f10abeSDavid Ahern return -EINVAL; 1761a1f10abeSDavid Ahern } 1762966bae33SEric W. Biederman 17635a9ab017SRobert Shearman switch (dec.label) { 176478f5b899STom Herbert case MPLS_LABEL_IMPLNULL: 17655a9ab017SRobert Shearman /* RFC3032: This is a label that an LSR may 17665a9ab017SRobert Shearman * assign and distribute, but which never 17675a9ab017SRobert Shearman * actually appears in the encapsulation. 17685a9ab017SRobert Shearman */ 1769a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1770a1f10abeSDavid Ahern "Implicit NULL Label (3) can not be used in encapsulation"); 17715a9ab017SRobert Shearman return -EINVAL; 17725a9ab017SRobert Shearman } 17735a9ab017SRobert Shearman 1774966bae33SEric W. Biederman label[i] = dec.label; 1775966bae33SEric W. Biederman } 1776a4ac8c98SDavid Ahern out: 1777966bae33SEric W. Biederman *labels = nla_labels; 1778966bae33SEric W. Biederman return 0; 1779966bae33SEric W. Biederman } 1780face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_get_labels); 1781966bae33SEric W. Biederman 1782074350e2SDavid Ahern static int rtm_to_route_config(struct sk_buff *skb, 1783074350e2SDavid Ahern struct nlmsghdr *nlh, 1784074350e2SDavid Ahern struct mpls_route_config *cfg, 1785074350e2SDavid Ahern struct netlink_ext_ack *extack) 178603c05665SEric W. Biederman { 178703c05665SEric W. Biederman struct rtmsg *rtm; 178803c05665SEric W. Biederman struct nlattr *tb[RTA_MAX+1]; 178903c05665SEric W. Biederman int index; 179003c05665SEric W. Biederman int err; 179103c05665SEric W. Biederman 17928cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 17938cb08174SJohannes Berg rtm_mpls_policy, extack); 179403c05665SEric W. Biederman if (err < 0) 179503c05665SEric W. Biederman goto errout; 179603c05665SEric W. Biederman 179703c05665SEric W. Biederman err = -EINVAL; 179803c05665SEric W. Biederman rtm = nlmsg_data(nlh); 179903c05665SEric W. Biederman 1800074350e2SDavid Ahern if (rtm->rtm_family != AF_MPLS) { 1801074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid address family in rtmsg"); 180203c05665SEric W. Biederman goto errout; 1803074350e2SDavid Ahern } 1804074350e2SDavid Ahern if (rtm->rtm_dst_len != 20) { 1805074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_dst_len must be 20 for MPLS"); 180603c05665SEric W. Biederman goto errout; 1807074350e2SDavid Ahern } 1808074350e2SDavid Ahern if (rtm->rtm_src_len != 0) { 1809074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_src_len must be 0 for MPLS"); 181003c05665SEric W. Biederman goto errout; 1811074350e2SDavid Ahern } 1812074350e2SDavid Ahern if (rtm->rtm_tos != 0) { 1813074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_tos must be 0 for MPLS"); 181403c05665SEric W. Biederman goto errout; 1815074350e2SDavid Ahern } 1816074350e2SDavid Ahern if (rtm->rtm_table != RT_TABLE_MAIN) { 1817074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1818074350e2SDavid Ahern "MPLS only supports the main route table"); 181903c05665SEric W. Biederman goto errout; 1820074350e2SDavid Ahern } 182103c05665SEric W. Biederman /* Any value is acceptable for rtm_protocol */ 182203c05665SEric W. Biederman 182303c05665SEric W. Biederman /* As mpls uses destination specific addresses 182403c05665SEric W. Biederman * (or source specific address in the case of multicast) 182503c05665SEric W. Biederman * all addresses have universal scope. 182603c05665SEric W. Biederman */ 1827074350e2SDavid Ahern if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) { 1828074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1829074350e2SDavid Ahern "Invalid route scope - MPLS only supports UNIVERSE"); 183003c05665SEric W. Biederman goto errout; 1831074350e2SDavid Ahern } 1832074350e2SDavid Ahern if (rtm->rtm_type != RTN_UNICAST) { 1833074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1834074350e2SDavid Ahern "Invalid route type - MPLS only supports UNICAST"); 183503c05665SEric W. Biederman goto errout; 1836074350e2SDavid Ahern } 1837074350e2SDavid Ahern if (rtm->rtm_flags != 0) { 1838074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_flags must be 0 for MPLS"); 183903c05665SEric W. Biederman goto errout; 1840074350e2SDavid Ahern } 184103c05665SEric W. Biederman 184203c05665SEric W. Biederman cfg->rc_label = LABEL_NOT_SPECIFIED; 184303c05665SEric W. Biederman cfg->rc_protocol = rtm->rtm_protocol; 1844eb7809f0SRobert Shearman cfg->rc_via_table = MPLS_NEIGH_TABLE_UNSPEC; 18455b441ac8SRobert Shearman cfg->rc_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 184603c05665SEric W. Biederman cfg->rc_nlflags = nlh->nlmsg_flags; 184703c05665SEric W. Biederman cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; 184803c05665SEric W. Biederman cfg->rc_nlinfo.nlh = nlh; 184903c05665SEric W. Biederman cfg->rc_nlinfo.nl_net = sock_net(skb->sk); 185003c05665SEric W. Biederman 185103c05665SEric W. Biederman for (index = 0; index <= RTA_MAX; index++) { 185203c05665SEric W. Biederman struct nlattr *nla = tb[index]; 185303c05665SEric W. Biederman if (!nla) 185403c05665SEric W. Biederman continue; 185503c05665SEric W. Biederman 185603c05665SEric W. Biederman switch (index) { 185703c05665SEric W. Biederman case RTA_OIF: 185803c05665SEric W. Biederman cfg->rc_ifindex = nla_get_u32(nla); 185903c05665SEric W. Biederman break; 186003c05665SEric W. Biederman case RTA_NEWDST: 186103c05665SEric W. Biederman if (nla_get_labels(nla, MAX_NEW_LABELS, 186203c05665SEric W. Biederman &cfg->rc_output_labels, 1863074350e2SDavid Ahern cfg->rc_output_label, extack)) 186403c05665SEric W. Biederman goto errout; 186503c05665SEric W. Biederman break; 186603c05665SEric W. Biederman case RTA_DST: 186703c05665SEric W. Biederman { 1868f8efb73cSRoopa Prabhu u8 label_count; 186903c05665SEric W. Biederman if (nla_get_labels(nla, 1, &label_count, 1870074350e2SDavid Ahern &cfg->rc_label, extack)) 187103c05665SEric W. Biederman goto errout; 187203c05665SEric W. Biederman 1873b7b386f4SDavid Ahern if (!mpls_label_ok(cfg->rc_nlinfo.nl_net, 18743968523fSDan Williams &cfg->rc_label, extack)) 187503c05665SEric W. Biederman goto errout; 187603c05665SEric W. Biederman break; 187703c05665SEric W. Biederman } 1878be48220eSDavid Ahern case RTA_GATEWAY: 1879be48220eSDavid Ahern NL_SET_ERR_MSG(extack, "MPLS does not support RTA_GATEWAY attribute"); 1880be48220eSDavid Ahern goto errout; 188103c05665SEric W. Biederman case RTA_VIA: 188203c05665SEric W. Biederman { 1883f8efb73cSRoopa Prabhu if (nla_get_via(nla, &cfg->rc_via_alen, 1884074350e2SDavid Ahern &cfg->rc_via_table, cfg->rc_via, 1885074350e2SDavid Ahern extack)) 188603c05665SEric W. Biederman goto errout; 188703c05665SEric W. Biederman break; 188803c05665SEric W. Biederman } 1889f8efb73cSRoopa Prabhu case RTA_MULTIPATH: 1890f8efb73cSRoopa Prabhu { 1891f8efb73cSRoopa Prabhu cfg->rc_mp = nla_data(nla); 1892f8efb73cSRoopa Prabhu cfg->rc_mp_len = nla_len(nla); 189303c05665SEric W. Biederman break; 189403c05665SEric W. Biederman } 18955b441ac8SRobert Shearman case RTA_TTL_PROPAGATE: 18965b441ac8SRobert Shearman { 18975b441ac8SRobert Shearman u8 ttl_propagate = nla_get_u8(nla); 18985b441ac8SRobert Shearman 1899074350e2SDavid Ahern if (ttl_propagate > 1) { 1900074350e2SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1901074350e2SDavid Ahern "RTA_TTL_PROPAGATE can only be 0 or 1"); 19025b441ac8SRobert Shearman goto errout; 1903074350e2SDavid Ahern } 19045b441ac8SRobert Shearman cfg->rc_ttl_propagate = ttl_propagate ? 19055b441ac8SRobert Shearman MPLS_TTL_PROP_ENABLED : 19065b441ac8SRobert Shearman MPLS_TTL_PROP_DISABLED; 19075b441ac8SRobert Shearman break; 19085b441ac8SRobert Shearman } 190903c05665SEric W. Biederman default: 1910074350e2SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, "Unknown attribute"); 191103c05665SEric W. Biederman /* Unsupported attribute */ 191203c05665SEric W. Biederman goto errout; 191303c05665SEric W. Biederman } 191403c05665SEric W. Biederman } 191503c05665SEric W. Biederman 191603c05665SEric W. Biederman err = 0; 191703c05665SEric W. Biederman errout: 191803c05665SEric W. Biederman return err; 191903c05665SEric W. Biederman } 192003c05665SEric W. Biederman 1921c21ef3e3SDavid Ahern static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1922c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 192303c05665SEric W. Biederman { 1924a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 192503c05665SEric W. Biederman int err; 192603c05665SEric W. Biederman 1927a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1928a4ac8c98SDavid Ahern if (!cfg) 1929a4ac8c98SDavid Ahern return -ENOMEM; 193003c05665SEric W. Biederman 1931074350e2SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg, extack); 1932a4ac8c98SDavid Ahern if (err < 0) 1933a4ac8c98SDavid Ahern goto out; 1934a4ac8c98SDavid Ahern 1935074350e2SDavid Ahern err = mpls_route_del(cfg, extack); 1936a4ac8c98SDavid Ahern out: 1937a4ac8c98SDavid Ahern kfree(cfg); 1938a4ac8c98SDavid Ahern 1939a4ac8c98SDavid Ahern return err; 194003c05665SEric W. Biederman } 194103c05665SEric W. Biederman 194203c05665SEric W. Biederman 1943c21ef3e3SDavid Ahern static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1944c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 194503c05665SEric W. Biederman { 1946a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 194703c05665SEric W. Biederman int err; 194803c05665SEric W. Biederman 1949a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1950a4ac8c98SDavid Ahern if (!cfg) 1951a4ac8c98SDavid Ahern return -ENOMEM; 195203c05665SEric W. Biederman 1953074350e2SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg, extack); 1954a4ac8c98SDavid Ahern if (err < 0) 1955a4ac8c98SDavid Ahern goto out; 1956a4ac8c98SDavid Ahern 1957074350e2SDavid Ahern err = mpls_route_add(cfg, extack); 1958a4ac8c98SDavid Ahern out: 1959a4ac8c98SDavid Ahern kfree(cfg); 1960a4ac8c98SDavid Ahern 1961a4ac8c98SDavid Ahern return err; 196203c05665SEric W. Biederman } 196303c05665SEric W. Biederman 196403c05665SEric W. Biederman static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, 196503c05665SEric W. Biederman u32 label, struct mpls_route *rt, int flags) 196603c05665SEric W. Biederman { 196719d0c341SEric W. Biederman struct net_device *dev; 196803c05665SEric W. Biederman struct nlmsghdr *nlh; 196903c05665SEric W. Biederman struct rtmsg *rtm; 197003c05665SEric W. Biederman 197103c05665SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); 197203c05665SEric W. Biederman if (nlh == NULL) 197303c05665SEric W. Biederman return -EMSGSIZE; 197403c05665SEric W. Biederman 197503c05665SEric W. Biederman rtm = nlmsg_data(nlh); 197603c05665SEric W. Biederman rtm->rtm_family = AF_MPLS; 197703c05665SEric W. Biederman rtm->rtm_dst_len = 20; 197803c05665SEric W. Biederman rtm->rtm_src_len = 0; 197903c05665SEric W. Biederman rtm->rtm_tos = 0; 198003c05665SEric W. Biederman rtm->rtm_table = RT_TABLE_MAIN; 198103c05665SEric W. Biederman rtm->rtm_protocol = rt->rt_protocol; 198203c05665SEric W. Biederman rtm->rtm_scope = RT_SCOPE_UNIVERSE; 198303c05665SEric W. Biederman rtm->rtm_type = RTN_UNICAST; 198403c05665SEric W. Biederman rtm->rtm_flags = 0; 198503c05665SEric W. Biederman 198603c05665SEric W. Biederman if (nla_put_labels(skb, RTA_DST, 1, &label)) 198703c05665SEric W. Biederman goto nla_put_failure; 19885b441ac8SRobert Shearman 19895b441ac8SRobert Shearman if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) { 19905b441ac8SRobert Shearman bool ttl_propagate = 19915b441ac8SRobert Shearman rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED; 19925b441ac8SRobert Shearman 19935b441ac8SRobert Shearman if (nla_put_u8(skb, RTA_TTL_PROPAGATE, 19945b441ac8SRobert Shearman ttl_propagate)) 19955b441ac8SRobert Shearman goto nla_put_failure; 19965b441ac8SRobert Shearman } 1997f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 1998cf4b24f0SRobert Shearman const struct mpls_nh *nh = rt->rt_nh; 1999f8efb73cSRoopa Prabhu 2000f8efb73cSRoopa Prabhu if (nh->nh_labels && 2001f8efb73cSRoopa Prabhu nla_put_labels(skb, RTA_NEWDST, nh->nh_labels, 2002f8efb73cSRoopa Prabhu nh->nh_label)) 2003f8efb73cSRoopa Prabhu goto nla_put_failure; 2004eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 200572dcac96SRobert Shearman nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), 2006f8efb73cSRoopa Prabhu nh->nh_via_alen)) 2007f8efb73cSRoopa Prabhu goto nla_put_failure; 2008f8efb73cSRoopa Prabhu dev = rtnl_dereference(nh->nh_dev); 2009f8efb73cSRoopa Prabhu if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) 2010f8efb73cSRoopa Prabhu goto nla_put_failure; 2011c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) 2012c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 2013c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) 2014c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 2015f8efb73cSRoopa Prabhu } else { 2016f8efb73cSRoopa Prabhu struct rtnexthop *rtnh; 2017f8efb73cSRoopa Prabhu struct nlattr *mp; 201877ef013aSDavid Ahern u8 linkdown = 0; 201977ef013aSDavid Ahern u8 dead = 0; 2020f8efb73cSRoopa Prabhu 2021ae0be8deSMichal Kubecek mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 2022f8efb73cSRoopa Prabhu if (!mp) 2023f8efb73cSRoopa Prabhu goto nla_put_failure; 2024f8efb73cSRoopa Prabhu 2025f8efb73cSRoopa Prabhu for_nexthops(rt) { 2026c00e51ddSDavid Ahern dev = rtnl_dereference(nh->nh_dev); 2027c00e51ddSDavid Ahern if (!dev) 2028c00e51ddSDavid Ahern continue; 2029c00e51ddSDavid Ahern 2030f8efb73cSRoopa Prabhu rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 2031f8efb73cSRoopa Prabhu if (!rtnh) 2032f8efb73cSRoopa Prabhu goto nla_put_failure; 2033f8efb73cSRoopa Prabhu 2034f8efb73cSRoopa Prabhu rtnh->rtnh_ifindex = dev->ifindex; 2035c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) { 2036c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_LINKDOWN; 2037c89359a4SRoopa Prabhu linkdown++; 2038c89359a4SRoopa Prabhu } 2039c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) { 2040c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_DEAD; 2041c89359a4SRoopa Prabhu dead++; 2042c89359a4SRoopa Prabhu } 2043c89359a4SRoopa Prabhu 2044f8efb73cSRoopa Prabhu if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST, 2045f8efb73cSRoopa Prabhu nh->nh_labels, 2046f8efb73cSRoopa Prabhu nh->nh_label)) 2047f8efb73cSRoopa Prabhu goto nla_put_failure; 2048f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 2049f20367dfSRobert Shearman nla_put_via(skb, nh->nh_via_table, 2050cf4b24f0SRobert Shearman mpls_nh_via(rt, nh), 2051f8efb73cSRoopa Prabhu nh->nh_via_alen)) 2052f8efb73cSRoopa Prabhu goto nla_put_failure; 2053f8efb73cSRoopa Prabhu 2054f8efb73cSRoopa Prabhu /* length of rtnetlink header + attributes */ 2055f8efb73cSRoopa Prabhu rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 2056f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 2057f8efb73cSRoopa Prabhu 2058c89359a4SRoopa Prabhu if (linkdown == rt->rt_nhn) 2059c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 2060c89359a4SRoopa Prabhu if (dead == rt->rt_nhn) 2061c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 2062c89359a4SRoopa Prabhu 2063f8efb73cSRoopa Prabhu nla_nest_end(skb, mp); 2064f8efb73cSRoopa Prabhu } 206503c05665SEric W. Biederman 206603c05665SEric W. Biederman nlmsg_end(skb, nlh); 206703c05665SEric W. Biederman return 0; 206803c05665SEric W. Biederman 206903c05665SEric W. Biederman nla_put_failure: 207003c05665SEric W. Biederman nlmsg_cancel(skb, nlh); 207103c05665SEric W. Biederman return -EMSGSIZE; 207203c05665SEric W. Biederman } 207303c05665SEric W. Biederman 2074d8a66aa2SDavid Ahern #if IS_ENABLED(CONFIG_INET) 20754724676dSDavid Ahern static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, 20764724676dSDavid Ahern struct fib_dump_filter *filter, 2077effe6792SDavid Ahern struct netlink_callback *cb) 2078d8a66aa2SDavid Ahern { 2079effe6792SDavid Ahern return ip_valid_fib_dump_req(net, nlh, filter, cb); 2080d8a66aa2SDavid Ahern } 2081d8a66aa2SDavid Ahern #else 20824724676dSDavid Ahern static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, 20834724676dSDavid Ahern struct fib_dump_filter *filter, 2084effe6792SDavid Ahern struct netlink_callback *cb) 2085d8a66aa2SDavid Ahern { 2086effe6792SDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2087196cfebfSDavid Ahern struct nlattr *tb[RTA_MAX + 1]; 2088d8a66aa2SDavid Ahern struct rtmsg *rtm; 2089196cfebfSDavid Ahern int err, i; 2090d8a66aa2SDavid Ahern 2091d8a66aa2SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 2092d8a66aa2SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid header for FIB dump request"); 2093d8a66aa2SDavid Ahern return -EINVAL; 2094d8a66aa2SDavid Ahern } 2095d8a66aa2SDavid Ahern 2096d8a66aa2SDavid Ahern rtm = nlmsg_data(nlh); 2097d8a66aa2SDavid Ahern if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || 2098196cfebfSDavid Ahern rtm->rtm_table || rtm->rtm_scope || rtm->rtm_type || 2099196cfebfSDavid Ahern rtm->rtm_flags) { 2100d8a66aa2SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for FIB dump request"); 2101d8a66aa2SDavid Ahern return -EINVAL; 2102d8a66aa2SDavid Ahern } 2103d8a66aa2SDavid Ahern 2104196cfebfSDavid Ahern if (rtm->rtm_protocol) { 2105196cfebfSDavid Ahern filter->protocol = rtm->rtm_protocol; 2106196cfebfSDavid Ahern filter->filter_set = 1; 2107196cfebfSDavid Ahern cb->answer_flags = NLM_F_DUMP_FILTERED; 2108196cfebfSDavid Ahern } 2109196cfebfSDavid Ahern 21108cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 2111196cfebfSDavid Ahern rtm_mpls_policy, extack); 2112196cfebfSDavid Ahern if (err < 0) 2113196cfebfSDavid Ahern return err; 2114196cfebfSDavid Ahern 2115196cfebfSDavid Ahern for (i = 0; i <= RTA_MAX; ++i) { 2116196cfebfSDavid Ahern int ifindex; 2117196cfebfSDavid Ahern 2118196cfebfSDavid Ahern if (i == RTA_OIF) { 2119196cfebfSDavid Ahern ifindex = nla_get_u32(tb[i]); 2120196cfebfSDavid Ahern filter->dev = __dev_get_by_index(net, ifindex); 2121196cfebfSDavid Ahern if (!filter->dev) 2122196cfebfSDavid Ahern return -ENODEV; 2123196cfebfSDavid Ahern filter->filter_set = 1; 2124196cfebfSDavid Ahern } else if (tb[i]) { 2125196cfebfSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request"); 2126d8a66aa2SDavid Ahern return -EINVAL; 2127d8a66aa2SDavid Ahern } 2128196cfebfSDavid Ahern } 2129d8a66aa2SDavid Ahern 2130d8a66aa2SDavid Ahern return 0; 2131d8a66aa2SDavid Ahern } 2132d8a66aa2SDavid Ahern #endif 2133d8a66aa2SDavid Ahern 2134bae9a78bSDavid Ahern static bool mpls_rt_uses_dev(struct mpls_route *rt, 2135bae9a78bSDavid Ahern const struct net_device *dev) 2136bae9a78bSDavid Ahern { 2137bae9a78bSDavid Ahern struct net_device *nh_dev; 2138bae9a78bSDavid Ahern 2139bae9a78bSDavid Ahern if (rt->rt_nhn == 1) { 2140bae9a78bSDavid Ahern struct mpls_nh *nh = rt->rt_nh; 2141bae9a78bSDavid Ahern 2142bae9a78bSDavid Ahern nh_dev = rtnl_dereference(nh->nh_dev); 2143bae9a78bSDavid Ahern if (dev == nh_dev) 2144bae9a78bSDavid Ahern return true; 2145bae9a78bSDavid Ahern } else { 2146bae9a78bSDavid Ahern for_nexthops(rt) { 2147bae9a78bSDavid Ahern nh_dev = rtnl_dereference(nh->nh_dev); 2148bae9a78bSDavid Ahern if (nh_dev == dev) 2149bae9a78bSDavid Ahern return true; 2150bae9a78bSDavid Ahern } endfor_nexthops(rt); 2151bae9a78bSDavid Ahern } 2152bae9a78bSDavid Ahern 2153bae9a78bSDavid Ahern return false; 2154bae9a78bSDavid Ahern } 2155bae9a78bSDavid Ahern 215603c05665SEric W. Biederman static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) 215703c05665SEric W. Biederman { 2158e8ba330aSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 215903c05665SEric W. Biederman struct net *net = sock_net(skb->sk); 216019d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 21614724676dSDavid Ahern struct fib_dump_filter filter = {}; 2162bae9a78bSDavid Ahern unsigned int flags = NLM_F_MULTI; 216319d0c341SEric W. Biederman size_t platform_labels; 216403c05665SEric W. Biederman unsigned int index; 216503c05665SEric W. Biederman 216603c05665SEric W. Biederman ASSERT_RTNL(); 216703c05665SEric W. Biederman 2168e8ba330aSDavid Ahern if (cb->strict_check) { 21694724676dSDavid Ahern int err; 2170e8ba330aSDavid Ahern 2171effe6792SDavid Ahern err = mpls_valid_fib_dump_req(net, nlh, &filter, cb); 2172e8ba330aSDavid Ahern if (err < 0) 2173e8ba330aSDavid Ahern return err; 2174bae9a78bSDavid Ahern 2175bae9a78bSDavid Ahern /* for MPLS, there is only 1 table with fixed type and flags. 2176bae9a78bSDavid Ahern * If either are set in the filter then return nothing. 2177bae9a78bSDavid Ahern */ 2178bae9a78bSDavid Ahern if ((filter.table_id && filter.table_id != RT_TABLE_MAIN) || 2179bae9a78bSDavid Ahern (filter.rt_type && filter.rt_type != RTN_UNICAST) || 2180bae9a78bSDavid Ahern filter.flags) 2181bae9a78bSDavid Ahern return skb->len; 2182e8ba330aSDavid Ahern } 2183e8ba330aSDavid Ahern 218403c05665SEric W. Biederman index = cb->args[0]; 2185a6affd24SRobert Shearman if (index < MPLS_LABEL_FIRST_UNRESERVED) 2186a6affd24SRobert Shearman index = MPLS_LABEL_FIRST_UNRESERVED; 218703c05665SEric W. Biederman 218819d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 218919d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 2190bae9a78bSDavid Ahern 2191bae9a78bSDavid Ahern if (filter.filter_set) 2192bae9a78bSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 2193bae9a78bSDavid Ahern 219419d0c341SEric W. Biederman for (; index < platform_labels; index++) { 219503c05665SEric W. Biederman struct mpls_route *rt; 2196bae9a78bSDavid Ahern 219719d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 219803c05665SEric W. Biederman if (!rt) 219903c05665SEric W. Biederman continue; 220003c05665SEric W. Biederman 2201bae9a78bSDavid Ahern if ((filter.dev && !mpls_rt_uses_dev(rt, filter.dev)) || 2202bae9a78bSDavid Ahern (filter.protocol && rt->rt_protocol != filter.protocol)) 2203bae9a78bSDavid Ahern continue; 2204bae9a78bSDavid Ahern 220503c05665SEric W. Biederman if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid, 220603c05665SEric W. Biederman cb->nlh->nlmsg_seq, RTM_NEWROUTE, 2207bae9a78bSDavid Ahern index, rt, flags) < 0) 220803c05665SEric W. Biederman break; 220903c05665SEric W. Biederman } 221003c05665SEric W. Biederman cb->args[0] = index; 221103c05665SEric W. Biederman 221203c05665SEric W. Biederman return skb->len; 221303c05665SEric W. Biederman } 221403c05665SEric W. Biederman 22158de147dcSEric W. Biederman static inline size_t lfib_nlmsg_size(struct mpls_route *rt) 22168de147dcSEric W. Biederman { 22178de147dcSEric W. Biederman size_t payload = 22188de147dcSEric W. Biederman NLMSG_ALIGN(sizeof(struct rtmsg)) 22195b441ac8SRobert Shearman + nla_total_size(4) /* RTA_DST */ 22205b441ac8SRobert Shearman + nla_total_size(1); /* RTA_TTL_PROPAGATE */ 2221f8efb73cSRoopa Prabhu 2222f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 2223f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 2224f8efb73cSRoopa Prabhu 2225f8efb73cSRoopa Prabhu if (nh->nh_dev) 2226f8efb73cSRoopa Prabhu payload += nla_total_size(4); /* RTA_OIF */ 2227eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */ 222872dcac96SRobert Shearman payload += nla_total_size(2 + nh->nh_via_alen); 2229f8efb73cSRoopa Prabhu if (nh->nh_labels) /* RTA_NEWDST */ 2230f8efb73cSRoopa Prabhu payload += nla_total_size(nh->nh_labels * 4); 2231f8efb73cSRoopa Prabhu } else { 2232f8efb73cSRoopa Prabhu /* each nexthop is packed in an attribute */ 2233f8efb73cSRoopa Prabhu size_t nhsize = 0; 2234f8efb73cSRoopa Prabhu 2235f8efb73cSRoopa Prabhu for_nexthops(rt) { 2236e944e97aSDavid Ahern if (!rtnl_dereference(nh->nh_dev)) 2237e944e97aSDavid Ahern continue; 2238f8efb73cSRoopa Prabhu nhsize += nla_total_size(sizeof(struct rtnexthop)); 2239f20367dfSRobert Shearman /* RTA_VIA */ 2240f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) 2241f8efb73cSRoopa Prabhu nhsize += nla_total_size(2 + nh->nh_via_alen); 2242f8efb73cSRoopa Prabhu if (nh->nh_labels) 2243f8efb73cSRoopa Prabhu nhsize += nla_total_size(nh->nh_labels * 4); 2244f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 2245f8efb73cSRoopa Prabhu /* nested attribute */ 2246f8efb73cSRoopa Prabhu payload += nla_total_size(nhsize); 2247f8efb73cSRoopa Prabhu } 2248f8efb73cSRoopa Prabhu 22498de147dcSEric W. Biederman return payload; 22508de147dcSEric W. Biederman } 22518de147dcSEric W. Biederman 22528de147dcSEric W. Biederman static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, 22538de147dcSEric W. Biederman struct nlmsghdr *nlh, struct net *net, u32 portid, 22548de147dcSEric W. Biederman unsigned int nlm_flags) 22558de147dcSEric W. Biederman { 22568de147dcSEric W. Biederman struct sk_buff *skb; 22578de147dcSEric W. Biederman u32 seq = nlh ? nlh->nlmsg_seq : 0; 22588de147dcSEric W. Biederman int err = -ENOBUFS; 22598de147dcSEric W. Biederman 22608de147dcSEric W. Biederman skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); 22618de147dcSEric W. Biederman if (skb == NULL) 22628de147dcSEric W. Biederman goto errout; 22638de147dcSEric W. Biederman 22648de147dcSEric W. Biederman err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags); 22658de147dcSEric W. Biederman if (err < 0) { 22668de147dcSEric W. Biederman /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ 22678de147dcSEric W. Biederman WARN_ON(err == -EMSGSIZE); 22688de147dcSEric W. Biederman kfree_skb(skb); 22698de147dcSEric W. Biederman goto errout; 22708de147dcSEric W. Biederman } 22718de147dcSEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL); 22728de147dcSEric W. Biederman 22738de147dcSEric W. Biederman return; 22748de147dcSEric W. Biederman errout: 22758de147dcSEric W. Biederman if (err < 0) 22768de147dcSEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err); 22778de147dcSEric W. Biederman } 22788de147dcSEric W. Biederman 2279d77851bfSJakub Kicinski static int mpls_valid_getroute_req(struct sk_buff *skb, 2280d77851bfSJakub Kicinski const struct nlmsghdr *nlh, 2281d77851bfSJakub Kicinski struct nlattr **tb, 2282d77851bfSJakub Kicinski struct netlink_ext_ack *extack) 2283d77851bfSJakub Kicinski { 2284d77851bfSJakub Kicinski struct rtmsg *rtm; 2285d77851bfSJakub Kicinski int i, err; 2286d77851bfSJakub Kicinski 2287d77851bfSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 2288d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, 2289d77851bfSJakub Kicinski "Invalid header for get route request"); 2290d77851bfSJakub Kicinski return -EINVAL; 2291d77851bfSJakub Kicinski } 2292d77851bfSJakub Kicinski 2293d77851bfSJakub Kicinski if (!netlink_strict_get_check(skb)) 22948cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 2295d77851bfSJakub Kicinski rtm_mpls_policy, extack); 2296d77851bfSJakub Kicinski 2297d77851bfSJakub Kicinski rtm = nlmsg_data(nlh); 2298d77851bfSJakub Kicinski if ((rtm->rtm_dst_len && rtm->rtm_dst_len != 20) || 2299d77851bfSJakub Kicinski rtm->rtm_src_len || rtm->rtm_tos || rtm->rtm_table || 2300d77851bfSJakub Kicinski rtm->rtm_protocol || rtm->rtm_scope || rtm->rtm_type) { 2301d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request"); 2302d77851bfSJakub Kicinski return -EINVAL; 2303d77851bfSJakub Kicinski } 2304d77851bfSJakub Kicinski if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) { 2305d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, 2306d77851bfSJakub Kicinski "Invalid flags for get route request"); 2307d77851bfSJakub Kicinski return -EINVAL; 2308d77851bfSJakub Kicinski } 2309d77851bfSJakub Kicinski 23108cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 2311d77851bfSJakub Kicinski rtm_mpls_policy, extack); 2312d77851bfSJakub Kicinski if (err) 2313d77851bfSJakub Kicinski return err; 2314d77851bfSJakub Kicinski 2315d77851bfSJakub Kicinski if ((tb[RTA_DST] || tb[RTA_NEWDST]) && !rtm->rtm_dst_len) { 2316d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "rtm_dst_len must be 20 for MPLS"); 2317d77851bfSJakub Kicinski return -EINVAL; 2318d77851bfSJakub Kicinski } 2319d77851bfSJakub Kicinski 2320d77851bfSJakub Kicinski for (i = 0; i <= RTA_MAX; i++) { 2321d77851bfSJakub Kicinski if (!tb[i]) 2322d77851bfSJakub Kicinski continue; 2323d77851bfSJakub Kicinski 2324d77851bfSJakub Kicinski switch (i) { 2325d77851bfSJakub Kicinski case RTA_DST: 2326d77851bfSJakub Kicinski case RTA_NEWDST: 2327d77851bfSJakub Kicinski break; 2328d77851bfSJakub Kicinski default: 2329d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request"); 2330d77851bfSJakub Kicinski return -EINVAL; 2331d77851bfSJakub Kicinski } 2332d77851bfSJakub Kicinski } 2333d77851bfSJakub Kicinski 2334d77851bfSJakub Kicinski return 0; 2335d77851bfSJakub Kicinski } 2336d77851bfSJakub Kicinski 2337397fc9e5SRoopa Prabhu static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, 2338397fc9e5SRoopa Prabhu struct netlink_ext_ack *extack) 2339397fc9e5SRoopa Prabhu { 2340397fc9e5SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 2341397fc9e5SRoopa Prabhu u32 portid = NETLINK_CB(in_skb).portid; 2342a906c1aaSRoopa Prabhu u32 in_label = LABEL_NOT_SPECIFIED; 2343397fc9e5SRoopa Prabhu struct nlattr *tb[RTA_MAX + 1]; 2344397fc9e5SRoopa Prabhu u32 labels[MAX_NEW_LABELS]; 2345397fc9e5SRoopa Prabhu struct mpls_shim_hdr *hdr; 2346397fc9e5SRoopa Prabhu unsigned int hdr_size = 0; 2347397fc9e5SRoopa Prabhu struct net_device *dev; 2348397fc9e5SRoopa Prabhu struct mpls_route *rt; 2349397fc9e5SRoopa Prabhu struct rtmsg *rtm, *r; 2350397fc9e5SRoopa Prabhu struct nlmsghdr *nlh; 2351397fc9e5SRoopa Prabhu struct sk_buff *skb; 2352397fc9e5SRoopa Prabhu struct mpls_nh *nh; 2353397fc9e5SRoopa Prabhu u8 n_labels; 2354a906c1aaSRoopa Prabhu int err; 2355397fc9e5SRoopa Prabhu 2356d77851bfSJakub Kicinski err = mpls_valid_getroute_req(in_skb, in_nlh, tb, extack); 2357397fc9e5SRoopa Prabhu if (err < 0) 2358397fc9e5SRoopa Prabhu goto errout; 2359397fc9e5SRoopa Prabhu 2360397fc9e5SRoopa Prabhu rtm = nlmsg_data(in_nlh); 2361397fc9e5SRoopa Prabhu 2362397fc9e5SRoopa Prabhu if (tb[RTA_DST]) { 2363397fc9e5SRoopa Prabhu u8 label_count; 2364397fc9e5SRoopa Prabhu 2365397fc9e5SRoopa Prabhu if (nla_get_labels(tb[RTA_DST], 1, &label_count, 2366a906c1aaSRoopa Prabhu &in_label, extack)) { 2367a906c1aaSRoopa Prabhu err = -EINVAL; 2368397fc9e5SRoopa Prabhu goto errout; 2369a906c1aaSRoopa Prabhu } 2370397fc9e5SRoopa Prabhu 23713968523fSDan Williams if (!mpls_label_ok(net, &in_label, extack)) { 2372a906c1aaSRoopa Prabhu err = -EINVAL; 2373397fc9e5SRoopa Prabhu goto errout; 2374397fc9e5SRoopa Prabhu } 2375a906c1aaSRoopa Prabhu } 2376397fc9e5SRoopa Prabhu 2377397fc9e5SRoopa Prabhu rt = mpls_route_input_rcu(net, in_label); 2378397fc9e5SRoopa Prabhu if (!rt) { 2379397fc9e5SRoopa Prabhu err = -ENETUNREACH; 2380397fc9e5SRoopa Prabhu goto errout; 2381397fc9e5SRoopa Prabhu } 2382397fc9e5SRoopa Prabhu 2383397fc9e5SRoopa Prabhu if (rtm->rtm_flags & RTM_F_FIB_MATCH) { 2384397fc9e5SRoopa Prabhu skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); 2385397fc9e5SRoopa Prabhu if (!skb) { 2386397fc9e5SRoopa Prabhu err = -ENOBUFS; 2387397fc9e5SRoopa Prabhu goto errout; 2388397fc9e5SRoopa Prabhu } 2389397fc9e5SRoopa Prabhu 2390397fc9e5SRoopa Prabhu err = mpls_dump_route(skb, portid, in_nlh->nlmsg_seq, 2391397fc9e5SRoopa Prabhu RTM_NEWROUTE, in_label, rt, 0); 2392397fc9e5SRoopa Prabhu if (err < 0) { 2393397fc9e5SRoopa Prabhu /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ 2394397fc9e5SRoopa Prabhu WARN_ON(err == -EMSGSIZE); 2395397fc9e5SRoopa Prabhu goto errout_free; 2396397fc9e5SRoopa Prabhu } 2397397fc9e5SRoopa Prabhu 2398397fc9e5SRoopa Prabhu return rtnl_unicast(skb, net, portid); 2399397fc9e5SRoopa Prabhu } 2400397fc9e5SRoopa Prabhu 2401397fc9e5SRoopa Prabhu if (tb[RTA_NEWDST]) { 2402397fc9e5SRoopa Prabhu if (nla_get_labels(tb[RTA_NEWDST], MAX_NEW_LABELS, &n_labels, 2403397fc9e5SRoopa Prabhu labels, extack) != 0) { 2404397fc9e5SRoopa Prabhu err = -EINVAL; 2405397fc9e5SRoopa Prabhu goto errout; 2406397fc9e5SRoopa Prabhu } 2407397fc9e5SRoopa Prabhu 2408397fc9e5SRoopa Prabhu hdr_size = n_labels * sizeof(struct mpls_shim_hdr); 2409397fc9e5SRoopa Prabhu } 2410397fc9e5SRoopa Prabhu 2411397fc9e5SRoopa Prabhu skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 2412397fc9e5SRoopa Prabhu if (!skb) { 2413397fc9e5SRoopa Prabhu err = -ENOBUFS; 2414397fc9e5SRoopa Prabhu goto errout; 2415397fc9e5SRoopa Prabhu } 2416397fc9e5SRoopa Prabhu 2417397fc9e5SRoopa Prabhu skb->protocol = htons(ETH_P_MPLS_UC); 2418397fc9e5SRoopa Prabhu 2419397fc9e5SRoopa Prabhu if (hdr_size) { 2420397fc9e5SRoopa Prabhu bool bos; 2421397fc9e5SRoopa Prabhu int i; 2422397fc9e5SRoopa Prabhu 2423397fc9e5SRoopa Prabhu if (skb_cow(skb, hdr_size)) { 2424397fc9e5SRoopa Prabhu err = -ENOBUFS; 2425397fc9e5SRoopa Prabhu goto errout_free; 2426397fc9e5SRoopa Prabhu } 2427397fc9e5SRoopa Prabhu 2428397fc9e5SRoopa Prabhu skb_reserve(skb, hdr_size); 2429397fc9e5SRoopa Prabhu skb_push(skb, hdr_size); 2430397fc9e5SRoopa Prabhu skb_reset_network_header(skb); 2431397fc9e5SRoopa Prabhu 2432397fc9e5SRoopa Prabhu /* Push new labels */ 2433397fc9e5SRoopa Prabhu hdr = mpls_hdr(skb); 2434397fc9e5SRoopa Prabhu bos = true; 2435397fc9e5SRoopa Prabhu for (i = n_labels - 1; i >= 0; i--) { 2436397fc9e5SRoopa Prabhu hdr[i] = mpls_entry_encode(labels[i], 2437397fc9e5SRoopa Prabhu 1, 0, bos); 2438397fc9e5SRoopa Prabhu bos = false; 2439397fc9e5SRoopa Prabhu } 2440397fc9e5SRoopa Prabhu } 2441397fc9e5SRoopa Prabhu 2442397fc9e5SRoopa Prabhu nh = mpls_select_multipath(rt, skb); 2443397fc9e5SRoopa Prabhu if (!nh) { 2444397fc9e5SRoopa Prabhu err = -ENETUNREACH; 2445397fc9e5SRoopa Prabhu goto errout_free; 2446397fc9e5SRoopa Prabhu } 2447397fc9e5SRoopa Prabhu 2448397fc9e5SRoopa Prabhu if (hdr_size) { 2449397fc9e5SRoopa Prabhu skb_pull(skb, hdr_size); 2450397fc9e5SRoopa Prabhu skb_reset_network_header(skb); 2451397fc9e5SRoopa Prabhu } 2452397fc9e5SRoopa Prabhu 2453397fc9e5SRoopa Prabhu nlh = nlmsg_put(skb, portid, in_nlh->nlmsg_seq, 2454397fc9e5SRoopa Prabhu RTM_NEWROUTE, sizeof(*r), 0); 2455397fc9e5SRoopa Prabhu if (!nlh) { 2456397fc9e5SRoopa Prabhu err = -EMSGSIZE; 2457397fc9e5SRoopa Prabhu goto errout_free; 2458397fc9e5SRoopa Prabhu } 2459397fc9e5SRoopa Prabhu 2460397fc9e5SRoopa Prabhu r = nlmsg_data(nlh); 2461397fc9e5SRoopa Prabhu r->rtm_family = AF_MPLS; 2462397fc9e5SRoopa Prabhu r->rtm_dst_len = 20; 2463397fc9e5SRoopa Prabhu r->rtm_src_len = 0; 2464397fc9e5SRoopa Prabhu r->rtm_table = RT_TABLE_MAIN; 2465397fc9e5SRoopa Prabhu r->rtm_type = RTN_UNICAST; 2466397fc9e5SRoopa Prabhu r->rtm_scope = RT_SCOPE_UNIVERSE; 2467397fc9e5SRoopa Prabhu r->rtm_protocol = rt->rt_protocol; 2468397fc9e5SRoopa Prabhu r->rtm_flags = 0; 2469397fc9e5SRoopa Prabhu 2470397fc9e5SRoopa Prabhu if (nla_put_labels(skb, RTA_DST, 1, &in_label)) 2471397fc9e5SRoopa Prabhu goto nla_put_failure; 2472397fc9e5SRoopa Prabhu 2473397fc9e5SRoopa Prabhu if (nh->nh_labels && 2474397fc9e5SRoopa Prabhu nla_put_labels(skb, RTA_NEWDST, nh->nh_labels, 2475397fc9e5SRoopa Prabhu nh->nh_label)) 2476397fc9e5SRoopa Prabhu goto nla_put_failure; 2477397fc9e5SRoopa Prabhu 2478397fc9e5SRoopa Prabhu if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 2479397fc9e5SRoopa Prabhu nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), 2480397fc9e5SRoopa Prabhu nh->nh_via_alen)) 2481397fc9e5SRoopa Prabhu goto nla_put_failure; 2482397fc9e5SRoopa Prabhu dev = rtnl_dereference(nh->nh_dev); 2483397fc9e5SRoopa Prabhu if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) 2484397fc9e5SRoopa Prabhu goto nla_put_failure; 2485397fc9e5SRoopa Prabhu 2486397fc9e5SRoopa Prabhu nlmsg_end(skb, nlh); 2487397fc9e5SRoopa Prabhu 2488397fc9e5SRoopa Prabhu err = rtnl_unicast(skb, net, portid); 2489397fc9e5SRoopa Prabhu errout: 2490397fc9e5SRoopa Prabhu return err; 2491397fc9e5SRoopa Prabhu 2492397fc9e5SRoopa Prabhu nla_put_failure: 2493397fc9e5SRoopa Prabhu nlmsg_cancel(skb, nlh); 2494397fc9e5SRoopa Prabhu err = -EMSGSIZE; 2495397fc9e5SRoopa Prabhu errout_free: 2496397fc9e5SRoopa Prabhu kfree_skb(skb); 2497397fc9e5SRoopa Prabhu return err; 2498397fc9e5SRoopa Prabhu } 2499397fc9e5SRoopa Prabhu 25007720c01fSEric W. Biederman static int resize_platform_label_table(struct net *net, size_t limit) 25017720c01fSEric W. Biederman { 25027720c01fSEric W. Biederman size_t size = sizeof(struct mpls_route *) * limit; 25037720c01fSEric W. Biederman size_t old_limit; 25047720c01fSEric W. Biederman size_t cp_size; 25057720c01fSEric W. Biederman struct mpls_route __rcu **labels = NULL, **old; 25067720c01fSEric W. Biederman struct mpls_route *rt0 = NULL, *rt2 = NULL; 25077720c01fSEric W. Biederman unsigned index; 25087720c01fSEric W. Biederman 25097720c01fSEric W. Biederman if (size) { 2510752ade68SMichal Hocko labels = kvzalloc(size, GFP_KERNEL); 25117720c01fSEric W. Biederman if (!labels) 25127720c01fSEric W. Biederman goto nolabels; 25137720c01fSEric W. Biederman } 25147720c01fSEric W. Biederman 25157720c01fSEric W. Biederman /* In case the predefined labels need to be populated */ 251678f5b899STom Herbert if (limit > MPLS_LABEL_IPV4NULL) { 25177720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2518a4ac8c98SDavid Ahern rt0 = mpls_rt_alloc(1, lo->addr_len, 0); 2519df1c6316SDavid Ahern if (IS_ERR(rt0)) 25207720c01fSEric W. Biederman goto nort0; 2521f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); 25227720c01fSEric W. Biederman rt0->rt_protocol = RTPROT_KERNEL; 2523118d5234SRobert Shearman rt0->rt_payload_type = MPT_IPV4; 25245b441ac8SRobert Shearman rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2525f8efb73cSRoopa Prabhu rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2526b4e04fc7SRobert Shearman rt0->rt_nh->nh_via_alen = lo->addr_len; 2527cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr, 2528cf4b24f0SRobert Shearman lo->addr_len); 25297720c01fSEric W. Biederman } 253078f5b899STom Herbert if (limit > MPLS_LABEL_IPV6NULL) { 25317720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2532a4ac8c98SDavid Ahern rt2 = mpls_rt_alloc(1, lo->addr_len, 0); 2533df1c6316SDavid Ahern if (IS_ERR(rt2)) 25347720c01fSEric W. Biederman goto nort2; 2535f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); 25367720c01fSEric W. Biederman rt2->rt_protocol = RTPROT_KERNEL; 2537118d5234SRobert Shearman rt2->rt_payload_type = MPT_IPV6; 25386a18c312SDavid Ahern rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2539f8efb73cSRoopa Prabhu rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2540b4e04fc7SRobert Shearman rt2->rt_nh->nh_via_alen = lo->addr_len; 2541cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr, 2542cf4b24f0SRobert Shearman lo->addr_len); 25437720c01fSEric W. Biederman } 25447720c01fSEric W. Biederman 25457720c01fSEric W. Biederman rtnl_lock(); 25467720c01fSEric W. Biederman /* Remember the original table */ 254719d0c341SEric W. Biederman old = rtnl_dereference(net->mpls.platform_label); 25487720c01fSEric W. Biederman old_limit = net->mpls.platform_labels; 25497720c01fSEric W. Biederman 25507720c01fSEric W. Biederman /* Free any labels beyond the new table */ 25517720c01fSEric W. Biederman for (index = limit; index < old_limit; index++) 2552f8efb73cSRoopa Prabhu mpls_route_update(net, index, NULL, NULL); 25537720c01fSEric W. Biederman 25547720c01fSEric W. Biederman /* Copy over the old labels */ 25557720c01fSEric W. Biederman cp_size = size; 25567720c01fSEric W. Biederman if (old_limit < limit) 25577720c01fSEric W. Biederman cp_size = old_limit * sizeof(struct mpls_route *); 25587720c01fSEric W. Biederman 25597720c01fSEric W. Biederman memcpy(labels, old, cp_size); 25607720c01fSEric W. Biederman 25617720c01fSEric W. Biederman /* If needed set the predefined labels */ 256278f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV6NULL) && 256378f5b899STom Herbert (limit > MPLS_LABEL_IPV6NULL)) { 256478f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2); 25657720c01fSEric W. Biederman rt2 = NULL; 25667720c01fSEric W. Biederman } 25677720c01fSEric W. Biederman 256878f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV4NULL) && 256978f5b899STom Herbert (limit > MPLS_LABEL_IPV4NULL)) { 257078f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0); 25717720c01fSEric W. Biederman rt0 = NULL; 25727720c01fSEric W. Biederman } 25737720c01fSEric W. Biederman 25747720c01fSEric W. Biederman /* Update the global pointers */ 25757720c01fSEric W. Biederman net->mpls.platform_labels = limit; 257619d0c341SEric W. Biederman rcu_assign_pointer(net->mpls.platform_label, labels); 25777720c01fSEric W. Biederman 25787720c01fSEric W. Biederman rtnl_unlock(); 25797720c01fSEric W. Biederman 25807720c01fSEric W. Biederman mpls_rt_free(rt2); 25817720c01fSEric W. Biederman mpls_rt_free(rt0); 25827720c01fSEric W. Biederman 25837720c01fSEric W. Biederman if (old) { 25847720c01fSEric W. Biederman synchronize_rcu(); 25857720c01fSEric W. Biederman kvfree(old); 25867720c01fSEric W. Biederman } 25877720c01fSEric W. Biederman return 0; 25887720c01fSEric W. Biederman 25897720c01fSEric W. Biederman nort2: 25907720c01fSEric W. Biederman mpls_rt_free(rt0); 25917720c01fSEric W. Biederman nort0: 25927720c01fSEric W. Biederman kvfree(labels); 25937720c01fSEric W. Biederman nolabels: 25947720c01fSEric W. Biederman return -ENOMEM; 25957720c01fSEric W. Biederman } 25967720c01fSEric W. Biederman 25977720c01fSEric W. Biederman static int mpls_platform_labels(struct ctl_table *table, int write, 25987720c01fSEric W. Biederman void __user *buffer, size_t *lenp, loff_t *ppos) 25997720c01fSEric W. Biederman { 26007720c01fSEric W. Biederman struct net *net = table->data; 26017720c01fSEric W. Biederman int platform_labels = net->mpls.platform_labels; 26027720c01fSEric W. Biederman int ret; 26037720c01fSEric W. Biederman struct ctl_table tmp = { 26047720c01fSEric W. Biederman .procname = table->procname, 26057720c01fSEric W. Biederman .data = &platform_labels, 26067720c01fSEric W. Biederman .maxlen = sizeof(int), 26077720c01fSEric W. Biederman .mode = table->mode, 2608*eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 26097720c01fSEric W. Biederman .extra2 = &label_limit, 26107720c01fSEric W. Biederman }; 26117720c01fSEric W. Biederman 26127720c01fSEric W. Biederman ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 26137720c01fSEric W. Biederman 26147720c01fSEric W. Biederman if (write && ret == 0) 26157720c01fSEric W. Biederman ret = resize_platform_label_table(net, platform_labels); 26167720c01fSEric W. Biederman 26177720c01fSEric W. Biederman return ret; 26187720c01fSEric W. Biederman } 26197720c01fSEric W. Biederman 26205b441ac8SRobert Shearman #define MPLS_NS_SYSCTL_OFFSET(field) \ 26215b441ac8SRobert Shearman (&((struct net *)0)->field) 26225b441ac8SRobert Shearman 262337bde799SRobert Shearman static const struct ctl_table mpls_table[] = { 26247720c01fSEric W. Biederman { 26257720c01fSEric W. Biederman .procname = "platform_labels", 26267720c01fSEric W. Biederman .data = NULL, 26277720c01fSEric W. Biederman .maxlen = sizeof(int), 26287720c01fSEric W. Biederman .mode = 0644, 26297720c01fSEric W. Biederman .proc_handler = mpls_platform_labels, 26307720c01fSEric W. Biederman }, 26315b441ac8SRobert Shearman { 26325b441ac8SRobert Shearman .procname = "ip_ttl_propagate", 26335b441ac8SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate), 26345b441ac8SRobert Shearman .maxlen = sizeof(int), 26355b441ac8SRobert Shearman .mode = 0644, 26365b441ac8SRobert Shearman .proc_handler = proc_dointvec_minmax, 2637*eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 2638*eec4844fSMatteo Croce .extra2 = SYSCTL_ONE, 26395b441ac8SRobert Shearman }, 2640a59166e4SRobert Shearman { 2641a59166e4SRobert Shearman .procname = "default_ttl", 2642a59166e4SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl), 2643a59166e4SRobert Shearman .maxlen = sizeof(int), 2644a59166e4SRobert Shearman .mode = 0644, 2645a59166e4SRobert Shearman .proc_handler = proc_dointvec_minmax, 2646*eec4844fSMatteo Croce .extra1 = SYSCTL_ONE, 2647a59166e4SRobert Shearman .extra2 = &ttl_max, 2648a59166e4SRobert Shearman }, 26497720c01fSEric W. Biederman { } 26507720c01fSEric W. Biederman }; 26517720c01fSEric W. Biederman 26520189197fSEric W. Biederman static int mpls_net_init(struct net *net) 26530189197fSEric W. Biederman { 26547720c01fSEric W. Biederman struct ctl_table *table; 26555b441ac8SRobert Shearman int i; 26567720c01fSEric W. Biederman 26570189197fSEric W. Biederman net->mpls.platform_labels = 0; 26580189197fSEric W. Biederman net->mpls.platform_label = NULL; 26595b441ac8SRobert Shearman net->mpls.ip_ttl_propagate = 1; 2660a59166e4SRobert Shearman net->mpls.default_ttl = 255; 26610189197fSEric W. Biederman 26627720c01fSEric W. Biederman table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); 26637720c01fSEric W. Biederman if (table == NULL) 26647720c01fSEric W. Biederman return -ENOMEM; 26657720c01fSEric W. Biederman 26665b441ac8SRobert Shearman /* Table data contains only offsets relative to the base of 26675b441ac8SRobert Shearman * the mdev at this point, so make them absolute. 26685b441ac8SRobert Shearman */ 26695b441ac8SRobert Shearman for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++) 26705b441ac8SRobert Shearman table[i].data = (char *)net + (uintptr_t)table[i].data; 26715b441ac8SRobert Shearman 26727720c01fSEric W. Biederman net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); 26736ea3c9d5SNikolay Aleksandrov if (net->mpls.ctl == NULL) { 26746ea3c9d5SNikolay Aleksandrov kfree(table); 26757720c01fSEric W. Biederman return -ENOMEM; 26766ea3c9d5SNikolay Aleksandrov } 26777720c01fSEric W. Biederman 26780189197fSEric W. Biederman return 0; 26790189197fSEric W. Biederman } 26800189197fSEric W. Biederman 26810189197fSEric W. Biederman static void mpls_net_exit(struct net *net) 26820189197fSEric W. Biederman { 268319d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 268419d0c341SEric W. Biederman size_t platform_labels; 26857720c01fSEric W. Biederman struct ctl_table *table; 26860189197fSEric W. Biederman unsigned int index; 26870189197fSEric W. Biederman 26887720c01fSEric W. Biederman table = net->mpls.ctl->ctl_table_arg; 26897720c01fSEric W. Biederman unregister_net_sysctl_table(net->mpls.ctl); 26907720c01fSEric W. Biederman kfree(table); 26917720c01fSEric W. Biederman 269219d0c341SEric W. Biederman /* An rcu grace period has passed since there was a device in 269319d0c341SEric W. Biederman * the network namespace (and thus the last in flight packet) 26940189197fSEric W. Biederman * left this network namespace. This is because 26950189197fSEric W. Biederman * unregister_netdevice_many and netdev_run_todo has completed 26960189197fSEric W. Biederman * for each network device that was in this network namespace. 26970189197fSEric W. Biederman * 26980189197fSEric W. Biederman * As such no additional rcu synchronization is necessary when 26990189197fSEric W. Biederman * freeing the platform_label table. 27000189197fSEric W. Biederman */ 27010189197fSEric W. Biederman rtnl_lock(); 270219d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 270319d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 270419d0c341SEric W. Biederman for (index = 0; index < platform_labels; index++) { 270519d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 270619d0c341SEric W. Biederman RCU_INIT_POINTER(platform_label[index], NULL); 2707e37791ecSDavid Ahern mpls_notify_route(net, index, rt, NULL, NULL); 27080189197fSEric W. Biederman mpls_rt_free(rt); 27090189197fSEric W. Biederman } 27100189197fSEric W. Biederman rtnl_unlock(); 27110189197fSEric W. Biederman 271219d0c341SEric W. Biederman kvfree(platform_label); 27130189197fSEric W. Biederman } 27140189197fSEric W. Biederman 27150189197fSEric W. Biederman static struct pernet_operations mpls_net_ops = { 27160189197fSEric W. Biederman .init = mpls_net_init, 27170189197fSEric W. Biederman .exit = mpls_net_exit, 27180189197fSEric W. Biederman }; 27190189197fSEric W. Biederman 272027d69105SRobert Shearman static struct rtnl_af_ops mpls_af_ops __read_mostly = { 272127d69105SRobert Shearman .family = AF_MPLS, 272227d69105SRobert Shearman .fill_stats_af = mpls_fill_stats_af, 272327d69105SRobert Shearman .get_stats_af_size = mpls_get_stats_af_size, 272427d69105SRobert Shearman }; 272527d69105SRobert Shearman 27260189197fSEric W. Biederman static int __init mpls_init(void) 27270189197fSEric W. Biederman { 27280189197fSEric W. Biederman int err; 27290189197fSEric W. Biederman 27300189197fSEric W. Biederman BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4); 27310189197fSEric W. Biederman 27320189197fSEric W. Biederman err = register_pernet_subsys(&mpls_net_ops); 27330189197fSEric W. Biederman if (err) 27340189197fSEric W. Biederman goto out; 27350189197fSEric W. Biederman 27360189197fSEric W. Biederman err = register_netdevice_notifier(&mpls_dev_notifier); 27370189197fSEric W. Biederman if (err) 27380189197fSEric W. Biederman goto out_unregister_pernet; 27390189197fSEric W. Biederman 27400189197fSEric W. Biederman dev_add_pack(&mpls_packet_type); 27410189197fSEric W. Biederman 274227d69105SRobert Shearman rtnl_af_register(&mpls_af_ops); 274327d69105SRobert Shearman 2744c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_NEWROUTE, 2745c1c502b5SFlorian Westphal mpls_rtm_newroute, NULL, 0); 2746c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_DELROUTE, 2747c1c502b5SFlorian Westphal mpls_rtm_delroute, NULL, 0); 2748c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETROUTE, 2749c1c502b5SFlorian Westphal mpls_getroute, mpls_dump_routes, 0); 2750c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETNETCONF, 2751c1c502b5SFlorian Westphal mpls_netconf_get_devconf, 2752b97bac64SFlorian Westphal mpls_netconf_dump_devconf, 0); 2753bdc47641SAmine Kherbouche err = ipgre_tunnel_encap_add_mpls_ops(); 2754bdc47641SAmine Kherbouche if (err) 2755bdc47641SAmine Kherbouche pr_err("Can't add mpls over gre tunnel ops\n"); 2756bdc47641SAmine Kherbouche 27570189197fSEric W. Biederman err = 0; 27580189197fSEric W. Biederman out: 27590189197fSEric W. Biederman return err; 27600189197fSEric W. Biederman 27610189197fSEric W. Biederman out_unregister_pernet: 27620189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 27630189197fSEric W. Biederman goto out; 27640189197fSEric W. Biederman } 27650189197fSEric W. Biederman module_init(mpls_init); 27660189197fSEric W. Biederman 27670189197fSEric W. Biederman static void __exit mpls_exit(void) 27680189197fSEric W. Biederman { 276903c05665SEric W. Biederman rtnl_unregister_all(PF_MPLS); 277027d69105SRobert Shearman rtnl_af_unregister(&mpls_af_ops); 27710189197fSEric W. Biederman dev_remove_pack(&mpls_packet_type); 27720189197fSEric W. Biederman unregister_netdevice_notifier(&mpls_dev_notifier); 27730189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 2774bdc47641SAmine Kherbouche ipgre_tunnel_encap_del_mpls_ops(); 27750189197fSEric W. Biederman } 27760189197fSEric W. Biederman module_exit(mpls_exit); 27770189197fSEric W. Biederman 27780189197fSEric W. Biederman MODULE_DESCRIPTION("MultiProtocol Label Switching"); 27790189197fSEric W. Biederman MODULE_LICENSE("GPL v2"); 27800189197fSEric W. Biederman MODULE_ALIAS_NETPROTO(PF_MPLS); 2781