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 3800992d67bSGuillaume Nault skb_dst_drop(skb); 3810992d67bSGuillaume Nault 3820189197fSEric W. Biederman /* Read and decode the label */ 3830189197fSEric W. Biederman hdr = mpls_hdr(skb); 3840189197fSEric W. Biederman dec = mpls_entry_decode(hdr); 3850189197fSEric W. Biederman 3860189197fSEric W. Biederman rt = mpls_route_input_rcu(net, dec.label); 38727d69105SRobert Shearman if (!rt) { 38827d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_noroute); 3890189197fSEric W. Biederman goto drop; 39027d69105SRobert Shearman } 3910189197fSEric W. Biederman 3929f427a0eSDavid Ahern nh = mpls_select_multipath(rt, skb); 393f8efb73cSRoopa Prabhu if (!nh) 39427d69105SRobert Shearman goto err; 3950189197fSEric W. Biederman 3969f427a0eSDavid Ahern /* Pop the label */ 3979f427a0eSDavid Ahern skb_pull(skb, sizeof(*hdr)); 3989f427a0eSDavid Ahern skb_reset_network_header(skb); 3999f427a0eSDavid Ahern 4009f427a0eSDavid Ahern skb_orphan(skb); 4019f427a0eSDavid Ahern 4020189197fSEric W. Biederman if (skb_warn_if_lro(skb)) 40327d69105SRobert Shearman goto err; 4040189197fSEric W. Biederman 4050189197fSEric W. Biederman skb_forward_csum(skb); 4060189197fSEric W. Biederman 4070189197fSEric W. Biederman /* Verify ttl is valid */ 408aa7da937SEric W. Biederman if (dec.ttl <= 1) 40927d69105SRobert Shearman goto err; 4100189197fSEric W. Biederman 41127d69105SRobert Shearman /* Find the output device */ 41227d69105SRobert Shearman out_dev = rcu_dereference(nh->nh_dev); 41327d69105SRobert Shearman if (!mpls_output_possible(out_dev)) 41427d69105SRobert Shearman goto tx_err; 41527d69105SRobert Shearman 4160189197fSEric W. Biederman /* Verify the destination can hold the packet */ 417f8efb73cSRoopa Prabhu new_header_size = mpls_nh_header_size(nh); 4180189197fSEric W. Biederman mtu = mpls_dev_mtu(out_dev); 4190189197fSEric W. Biederman if (mpls_pkt_too_big(skb, mtu - new_header_size)) 42027d69105SRobert Shearman goto tx_err; 4210189197fSEric W. Biederman 4220189197fSEric W. Biederman hh_len = LL_RESERVED_SPACE(out_dev); 4230189197fSEric W. Biederman if (!out_dev->header_ops) 4240189197fSEric W. Biederman hh_len = 0; 4250189197fSEric W. Biederman 4260189197fSEric W. Biederman /* Ensure there is enough space for the headers in the skb */ 4270189197fSEric W. Biederman if (skb_cow(skb, hh_len + new_header_size)) 42827d69105SRobert Shearman goto tx_err; 4290189197fSEric W. Biederman 4300189197fSEric W. Biederman skb->dev = out_dev; 4310189197fSEric W. Biederman skb->protocol = htons(ETH_P_MPLS_UC); 4320189197fSEric W. Biederman 4336a6b83caSKangmin Park dec.ttl -= 1; 4340189197fSEric W. Biederman if (unlikely(!new_header_size && dec.bos)) { 4350189197fSEric W. Biederman /* Penultimate hop popping */ 4365b441ac8SRobert Shearman if (!mpls_egress(dev_net(out_dev), rt, skb, dec)) 43727d69105SRobert Shearman goto err; 4380189197fSEric W. Biederman } else { 4390189197fSEric W. Biederman bool bos; 4400189197fSEric W. Biederman int i; 4410189197fSEric W. Biederman skb_push(skb, new_header_size); 4420189197fSEric W. Biederman skb_reset_network_header(skb); 4430189197fSEric W. Biederman /* Push the new labels */ 4440189197fSEric W. Biederman hdr = mpls_hdr(skb); 4450189197fSEric W. Biederman bos = dec.bos; 446f8efb73cSRoopa Prabhu for (i = nh->nh_labels - 1; i >= 0; i--) { 447f8efb73cSRoopa Prabhu hdr[i] = mpls_entry_encode(nh->nh_label[i], 448f8efb73cSRoopa Prabhu dec.ttl, 0, bos); 4490189197fSEric W. Biederman bos = false; 4500189197fSEric W. Biederman } 4510189197fSEric W. Biederman } 4520189197fSEric W. Biederman 45327d69105SRobert Shearman mpls_stats_inc_outucastpkts(out_dev, skb); 45427d69105SRobert Shearman 455eb7809f0SRobert Shearman /* If via wasn't specified then send out using device address */ 456eb7809f0SRobert Shearman if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC) 457eb7809f0SRobert Shearman err = neigh_xmit(NEIGH_LINK_TABLE, out_dev, 458eb7809f0SRobert Shearman out_dev->dev_addr, skb); 459eb7809f0SRobert Shearman else 460eb7809f0SRobert Shearman err = neigh_xmit(nh->nh_via_table, out_dev, 461eb7809f0SRobert Shearman mpls_nh_via(rt, nh), skb); 4620189197fSEric W. Biederman if (err) 4630189197fSEric W. Biederman net_dbg_ratelimited("%s: packet transmission failed: %d\n", 4640189197fSEric W. Biederman __func__, err); 4650189197fSEric W. Biederman return 0; 4660189197fSEric W. Biederman 46727d69105SRobert Shearman tx_err: 46827d69105SRobert Shearman out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL; 46927d69105SRobert Shearman if (out_mdev) 47027d69105SRobert Shearman MPLS_INC_STATS(out_mdev, tx_errors); 47127d69105SRobert Shearman goto drop; 47227d69105SRobert Shearman err: 47327d69105SRobert Shearman MPLS_INC_STATS(mdev, rx_errors); 4740189197fSEric W. Biederman drop: 4750189197fSEric W. Biederman kfree_skb(skb); 4760189197fSEric W. Biederman return NET_RX_DROP; 4770189197fSEric W. Biederman } 4780189197fSEric W. Biederman 4790189197fSEric W. Biederman static struct packet_type mpls_packet_type __read_mostly = { 4800189197fSEric W. Biederman .type = cpu_to_be16(ETH_P_MPLS_UC), 4810189197fSEric W. Biederman .func = mpls_forward, 4820189197fSEric W. Biederman }; 4830189197fSEric W. Biederman 484f0126539SWu Fengguang static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { 48503c05665SEric W. Biederman [RTA_DST] = { .type = NLA_U32 }, 48603c05665SEric W. Biederman [RTA_OIF] = { .type = NLA_U32 }, 4875b441ac8SRobert Shearman [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, 48803c05665SEric W. Biederman }; 48903c05665SEric W. Biederman 490a2519929SEric W. Biederman struct mpls_route_config { 491a2519929SEric W. Biederman u32 rc_protocol; 492a2519929SEric W. Biederman u32 rc_ifindex; 493f8efb73cSRoopa Prabhu u8 rc_via_table; 494f8efb73cSRoopa Prabhu u8 rc_via_alen; 495a2519929SEric W. Biederman u8 rc_via[MAX_VIA_ALEN]; 496a2519929SEric W. Biederman u32 rc_label; 4975b441ac8SRobert Shearman u8 rc_ttl_propagate; 498f8efb73cSRoopa Prabhu u8 rc_output_labels; 499a2519929SEric W. Biederman u32 rc_output_label[MAX_NEW_LABELS]; 500a2519929SEric W. Biederman u32 rc_nlflags; 501118d5234SRobert Shearman enum mpls_payload_type rc_payload_type; 502a2519929SEric W. Biederman struct nl_info rc_nlinfo; 503f8efb73cSRoopa Prabhu struct rtnexthop *rc_mp; 504f8efb73cSRoopa Prabhu int rc_mp_len; 505a2519929SEric W. Biederman }; 506a2519929SEric W. Biederman 50759b20966SDavid Ahern /* all nexthops within a route have the same size based on max 50859b20966SDavid Ahern * number of labels and max via length for a hop 50959b20966SDavid Ahern */ 51059b20966SDavid Ahern static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels) 5110189197fSEric W. Biederman { 51259b20966SDavid Ahern u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen); 5130189197fSEric W. Biederman struct mpls_route *rt; 514df1c6316SDavid Ahern size_t size; 5150189197fSEric W. Biederman 516df1c6316SDavid Ahern size = sizeof(*rt) + num_nh * nh_size; 517df1c6316SDavid Ahern if (size > MAX_MPLS_ROUTE_MEM) 518df1c6316SDavid Ahern return ERR_PTR(-EINVAL); 519df1c6316SDavid Ahern 520df1c6316SDavid Ahern rt = kzalloc(size, GFP_KERNEL); 521df1c6316SDavid Ahern if (!rt) 522df1c6316SDavid Ahern return ERR_PTR(-ENOMEM); 523df1c6316SDavid Ahern 524f8efb73cSRoopa Prabhu rt->rt_nhn = num_nh; 525c89359a4SRoopa Prabhu rt->rt_nhn_alive = num_nh; 52659b20966SDavid Ahern rt->rt_nh_size = nh_size; 52759b20966SDavid Ahern rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); 528f8efb73cSRoopa Prabhu 5290189197fSEric W. Biederman return rt; 5300189197fSEric W. Biederman } 5310189197fSEric W. Biederman 5320189197fSEric W. Biederman static void mpls_rt_free(struct mpls_route *rt) 5330189197fSEric W. Biederman { 5340189197fSEric W. Biederman if (rt) 5350189197fSEric W. Biederman kfree_rcu(rt, rt_rcu); 5360189197fSEric W. Biederman } 5370189197fSEric W. Biederman 5388de147dcSEric W. Biederman static void mpls_notify_route(struct net *net, unsigned index, 5398de147dcSEric W. Biederman struct mpls_route *old, struct mpls_route *new, 5408de147dcSEric W. Biederman const struct nl_info *info) 5418de147dcSEric W. Biederman { 5428de147dcSEric W. Biederman struct nlmsghdr *nlh = info ? info->nlh : NULL; 5438de147dcSEric W. Biederman unsigned portid = info ? info->portid : 0; 5448de147dcSEric W. Biederman int event = new ? RTM_NEWROUTE : RTM_DELROUTE; 5458de147dcSEric W. Biederman struct mpls_route *rt = new ? new : old; 5468de147dcSEric W. Biederman unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0; 5478de147dcSEric W. Biederman /* Ignore reserved labels for now */ 548a6affd24SRobert Shearman if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED)) 5498de147dcSEric W. Biederman rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags); 5508de147dcSEric W. Biederman } 5518de147dcSEric W. Biederman 5520189197fSEric W. Biederman static void mpls_route_update(struct net *net, unsigned index, 553f8efb73cSRoopa Prabhu struct mpls_route *new, 5540189197fSEric W. Biederman const struct nl_info *info) 5550189197fSEric W. Biederman { 55619d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 557f8efb73cSRoopa Prabhu struct mpls_route *rt; 5580189197fSEric W. Biederman 5590189197fSEric W. Biederman ASSERT_RTNL(); 5600189197fSEric W. Biederman 56119d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 56219d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 56319d0c341SEric W. Biederman rcu_assign_pointer(platform_label[index], new); 5640189197fSEric W. Biederman 565f8efb73cSRoopa Prabhu mpls_notify_route(net, index, rt, new, info); 5668de147dcSEric W. Biederman 5670189197fSEric W. Biederman /* If we removed a route free it now */ 568f8efb73cSRoopa Prabhu mpls_rt_free(rt); 5690189197fSEric W. Biederman } 5700189197fSEric W. Biederman 571a2519929SEric W. Biederman static unsigned find_free_label(struct net *net) 572a2519929SEric W. Biederman { 57319d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 57419d0c341SEric W. Biederman size_t platform_labels; 575a2519929SEric W. Biederman unsigned index; 57619d0c341SEric W. Biederman 57719d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 57819d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 579a6affd24SRobert Shearman for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels; 580a6affd24SRobert Shearman index++) { 58119d0c341SEric W. Biederman if (!rtnl_dereference(platform_label[index])) 582a2519929SEric W. Biederman return index; 583a2519929SEric W. Biederman } 584a2519929SEric W. Biederman return LABEL_NOT_SPECIFIED; 585a2519929SEric W. Biederman } 586a2519929SEric W. Biederman 587bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_INET) 588cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 589cf4b24f0SRobert Shearman const void *addr) 59001faef2cSRoopa Prabhu { 5915a9348b5SDan Carpenter struct net_device *dev; 59201faef2cSRoopa Prabhu struct rtable *rt; 59301faef2cSRoopa Prabhu struct in_addr daddr; 59401faef2cSRoopa Prabhu 59501faef2cSRoopa Prabhu memcpy(&daddr, addr, sizeof(struct in_addr)); 59601faef2cSRoopa Prabhu rt = ip_route_output(net, daddr.s_addr, 0, 0, 0); 59701faef2cSRoopa Prabhu if (IS_ERR(rt)) 5985a9348b5SDan Carpenter return ERR_CAST(rt); 59901faef2cSRoopa Prabhu 60001faef2cSRoopa Prabhu dev = rt->dst.dev; 60101faef2cSRoopa Prabhu dev_hold(dev); 60201faef2cSRoopa Prabhu 60301faef2cSRoopa Prabhu ip_rt_put(rt); 60401faef2cSRoopa Prabhu 60501faef2cSRoopa Prabhu return dev; 60601faef2cSRoopa Prabhu } 607bf21563aSRoopa Prabhu #else 608cf4b24f0SRobert Shearman static struct net_device *inet_fib_lookup_dev(struct net *net, 609cf4b24f0SRobert Shearman const void *addr) 610bf21563aSRoopa Prabhu { 611bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 612bf21563aSRoopa Prabhu } 613bf21563aSRoopa Prabhu #endif 61401faef2cSRoopa Prabhu 615bf21563aSRoopa Prabhu #if IS_ENABLED(CONFIG_IPV6) 616cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 617cf4b24f0SRobert Shearman const void *addr) 61801faef2cSRoopa Prabhu { 6195a9348b5SDan Carpenter struct net_device *dev; 62001faef2cSRoopa Prabhu struct dst_entry *dst; 62101faef2cSRoopa Prabhu struct flowi6 fl6; 622bf21563aSRoopa Prabhu 623bf21563aSRoopa Prabhu if (!ipv6_stub) 624bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 62501faef2cSRoopa Prabhu 62601faef2cSRoopa Prabhu memset(&fl6, 0, sizeof(fl6)); 62701faef2cSRoopa Prabhu memcpy(&fl6.daddr, addr, sizeof(struct in6_addr)); 6286c8991f4SSabrina Dubroca dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &fl6, NULL); 6296c8991f4SSabrina Dubroca if (IS_ERR(dst)) 6306c8991f4SSabrina Dubroca return ERR_CAST(dst); 63101faef2cSRoopa Prabhu 63201faef2cSRoopa Prabhu dev = dst->dev; 63301faef2cSRoopa Prabhu dev_hold(dev); 63401faef2cSRoopa Prabhu dst_release(dst); 63501faef2cSRoopa Prabhu 63601faef2cSRoopa Prabhu return dev; 63701faef2cSRoopa Prabhu } 638bf21563aSRoopa Prabhu #else 639cf4b24f0SRobert Shearman static struct net_device *inet6_fib_lookup_dev(struct net *net, 640cf4b24f0SRobert Shearman const void *addr) 641bf21563aSRoopa Prabhu { 642bf21563aSRoopa Prabhu return ERR_PTR(-EAFNOSUPPORT); 643bf21563aSRoopa Prabhu } 644bf21563aSRoopa Prabhu #endif 64501faef2cSRoopa Prabhu 64601faef2cSRoopa Prabhu static struct net_device *find_outdev(struct net *net, 647cf4b24f0SRobert Shearman struct mpls_route *rt, 648f8efb73cSRoopa Prabhu struct mpls_nh *nh, int oif) 64901faef2cSRoopa Prabhu { 65001faef2cSRoopa Prabhu struct net_device *dev = NULL; 65101faef2cSRoopa Prabhu 652f8efb73cSRoopa Prabhu if (!oif) { 653f8efb73cSRoopa Prabhu switch (nh->nh_via_table) { 65401faef2cSRoopa Prabhu case NEIGH_ARP_TABLE: 655cf4b24f0SRobert Shearman dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 65601faef2cSRoopa Prabhu break; 65701faef2cSRoopa Prabhu case NEIGH_ND_TABLE: 658cf4b24f0SRobert Shearman dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh)); 65901faef2cSRoopa Prabhu break; 66001faef2cSRoopa Prabhu case NEIGH_LINK_TABLE: 66101faef2cSRoopa Prabhu break; 66201faef2cSRoopa Prabhu } 66301faef2cSRoopa Prabhu } else { 664f8efb73cSRoopa Prabhu dev = dev_get_by_index(net, oif); 66501faef2cSRoopa Prabhu } 66601faef2cSRoopa Prabhu 6673dcb615eSRoopa Prabhu if (!dev) 6683dcb615eSRoopa Prabhu return ERR_PTR(-ENODEV); 6693dcb615eSRoopa Prabhu 67094a57f1fSRoopa Prabhu if (IS_ERR(dev)) 67194a57f1fSRoopa Prabhu return dev; 67294a57f1fSRoopa Prabhu 673f8efb73cSRoopa Prabhu /* The caller is holding rtnl anyways, so release the dev reference */ 674f8efb73cSRoopa Prabhu dev_put(dev); 675f8efb73cSRoopa Prabhu 67601faef2cSRoopa Prabhu return dev; 67701faef2cSRoopa Prabhu } 67801faef2cSRoopa Prabhu 679cf4b24f0SRobert Shearman static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt, 680cf4b24f0SRobert Shearman struct mpls_nh *nh, int oif) 681f8efb73cSRoopa Prabhu { 682f8efb73cSRoopa Prabhu struct net_device *dev = NULL; 683f8efb73cSRoopa Prabhu int err = -ENODEV; 684f8efb73cSRoopa Prabhu 685cf4b24f0SRobert Shearman dev = find_outdev(net, rt, nh, oif); 686f8efb73cSRoopa Prabhu if (IS_ERR(dev)) { 687f8efb73cSRoopa Prabhu err = PTR_ERR(dev); 688f8efb73cSRoopa Prabhu dev = NULL; 689f8efb73cSRoopa Prabhu goto errout; 690f8efb73cSRoopa Prabhu } 691f8efb73cSRoopa Prabhu 692f8efb73cSRoopa Prabhu /* Ensure this is a supported device */ 693f8efb73cSRoopa Prabhu err = -EINVAL; 694f8efb73cSRoopa Prabhu if (!mpls_dev_get(dev)) 695f8efb73cSRoopa Prabhu goto errout; 696f8efb73cSRoopa Prabhu 697a3e948e8SRobert Shearman if ((nh->nh_via_table == NEIGH_LINK_TABLE) && 698a3e948e8SRobert Shearman (dev->addr_len != nh->nh_via_alen)) 699a3e948e8SRobert Shearman goto errout; 700a3e948e8SRobert Shearman 701f8efb73cSRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, dev); 702f8efb73cSRoopa Prabhu 703c89359a4SRoopa Prabhu if (!(dev->flags & IFF_UP)) { 704c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_DEAD; 705c89359a4SRoopa Prabhu } else { 706c89359a4SRoopa Prabhu unsigned int flags; 707c89359a4SRoopa Prabhu 708c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 709c89359a4SRoopa Prabhu if (!(flags & (IFF_RUNNING | IFF_LOWER_UP))) 710c89359a4SRoopa Prabhu nh->nh_flags |= RTNH_F_LINKDOWN; 711c89359a4SRoopa Prabhu } 712c89359a4SRoopa Prabhu 713f8efb73cSRoopa Prabhu return 0; 714f8efb73cSRoopa Prabhu 715f8efb73cSRoopa Prabhu errout: 716f8efb73cSRoopa Prabhu return err; 717f8efb73cSRoopa Prabhu } 718f8efb73cSRoopa Prabhu 719d4e72560SDavid Ahern static int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table, 720d4e72560SDavid Ahern u8 via_addr[], struct netlink_ext_ack *extack) 721d4e72560SDavid Ahern { 722d4e72560SDavid Ahern struct rtvia *via = nla_data(nla); 723d4e72560SDavid Ahern int err = -EINVAL; 724d4e72560SDavid Ahern int alen; 725d4e72560SDavid Ahern 726d4e72560SDavid Ahern if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) { 727d4e72560SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 728d4e72560SDavid Ahern "Invalid attribute length for RTA_VIA"); 729d4e72560SDavid Ahern goto errout; 730d4e72560SDavid Ahern } 731d4e72560SDavid Ahern alen = nla_len(nla) - 732d4e72560SDavid Ahern offsetof(struct rtvia, rtvia_addr); 733d4e72560SDavid Ahern if (alen > MAX_VIA_ALEN) { 734d4e72560SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 735d4e72560SDavid Ahern "Invalid address length for RTA_VIA"); 736d4e72560SDavid Ahern goto errout; 737d4e72560SDavid Ahern } 738d4e72560SDavid Ahern 739d4e72560SDavid Ahern /* Validate the address family */ 740d4e72560SDavid Ahern switch (via->rtvia_family) { 741d4e72560SDavid Ahern case AF_PACKET: 742d4e72560SDavid Ahern *via_table = NEIGH_LINK_TABLE; 743d4e72560SDavid Ahern break; 744d4e72560SDavid Ahern case AF_INET: 745d4e72560SDavid Ahern *via_table = NEIGH_ARP_TABLE; 746d4e72560SDavid Ahern if (alen != 4) 747d4e72560SDavid Ahern goto errout; 748d4e72560SDavid Ahern break; 749d4e72560SDavid Ahern case AF_INET6: 750d4e72560SDavid Ahern *via_table = NEIGH_ND_TABLE; 751d4e72560SDavid Ahern if (alen != 16) 752d4e72560SDavid Ahern goto errout; 753d4e72560SDavid Ahern break; 754d4e72560SDavid Ahern default: 755d4e72560SDavid Ahern /* Unsupported address family */ 756d4e72560SDavid Ahern goto errout; 757d4e72560SDavid Ahern } 758d4e72560SDavid Ahern 759d4e72560SDavid Ahern memcpy(via_addr, via->rtvia_addr, alen); 760d4e72560SDavid Ahern *via_alen = alen; 761d4e72560SDavid Ahern err = 0; 762d4e72560SDavid Ahern 763d4e72560SDavid Ahern errout: 764d4e72560SDavid Ahern return err; 765d4e72560SDavid Ahern } 766d4e72560SDavid Ahern 767f8efb73cSRoopa Prabhu static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, 768f8efb73cSRoopa Prabhu struct mpls_route *rt) 769f8efb73cSRoopa Prabhu { 770f8efb73cSRoopa Prabhu struct net *net = cfg->rc_nlinfo.nl_net; 771f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 772f8efb73cSRoopa Prabhu int err; 773f8efb73cSRoopa Prabhu int i; 774f8efb73cSRoopa Prabhu 775f8efb73cSRoopa Prabhu if (!nh) 776f8efb73cSRoopa Prabhu return -ENOMEM; 777f8efb73cSRoopa Prabhu 778f8efb73cSRoopa Prabhu nh->nh_labels = cfg->rc_output_labels; 779f8efb73cSRoopa Prabhu for (i = 0; i < nh->nh_labels; i++) 780f8efb73cSRoopa Prabhu nh->nh_label[i] = cfg->rc_output_label[i]; 781f8efb73cSRoopa Prabhu 782f8efb73cSRoopa Prabhu nh->nh_via_table = cfg->rc_via_table; 783cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen); 784f8efb73cSRoopa Prabhu nh->nh_via_alen = cfg->rc_via_alen; 785f8efb73cSRoopa Prabhu 786cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex); 787f8efb73cSRoopa Prabhu if (err) 788f8efb73cSRoopa Prabhu goto errout; 789f8efb73cSRoopa Prabhu 790c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 791c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 792c89359a4SRoopa Prabhu 793f8efb73cSRoopa Prabhu return 0; 794f8efb73cSRoopa Prabhu 795f8efb73cSRoopa Prabhu errout: 796f8efb73cSRoopa Prabhu return err; 797f8efb73cSRoopa Prabhu } 798f8efb73cSRoopa Prabhu 799cf4b24f0SRobert Shearman static int mpls_nh_build(struct net *net, struct mpls_route *rt, 800c89359a4SRoopa Prabhu struct mpls_nh *nh, int oif, struct nlattr *via, 801074350e2SDavid Ahern struct nlattr *newdst, u8 max_labels, 802074350e2SDavid Ahern struct netlink_ext_ack *extack) 803f8efb73cSRoopa Prabhu { 804f8efb73cSRoopa Prabhu int err = -ENOMEM; 805f8efb73cSRoopa Prabhu 806f8efb73cSRoopa Prabhu if (!nh) 807f8efb73cSRoopa Prabhu goto errout; 808f8efb73cSRoopa Prabhu 809f8efb73cSRoopa Prabhu if (newdst) { 810a1f10abeSDavid Ahern err = nla_get_labels(newdst, max_labels, &nh->nh_labels, 811074350e2SDavid Ahern nh->nh_label, extack); 812f8efb73cSRoopa Prabhu if (err) 813f8efb73cSRoopa Prabhu goto errout; 814f8efb73cSRoopa Prabhu } 815f8efb73cSRoopa Prabhu 816f20367dfSRobert Shearman if (via) { 817f8efb73cSRoopa Prabhu err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table, 818074350e2SDavid Ahern __mpls_nh_via(rt, nh), extack); 819f8efb73cSRoopa Prabhu if (err) 820f8efb73cSRoopa Prabhu goto errout; 821f20367dfSRobert Shearman } else { 822f20367dfSRobert Shearman nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC; 823f20367dfSRobert Shearman } 824f8efb73cSRoopa Prabhu 825cf4b24f0SRobert Shearman err = mpls_nh_assign_dev(net, rt, nh, oif); 826f8efb73cSRoopa Prabhu if (err) 827f8efb73cSRoopa Prabhu goto errout; 828f8efb73cSRoopa Prabhu 829f8efb73cSRoopa Prabhu return 0; 830f8efb73cSRoopa Prabhu 831f8efb73cSRoopa Prabhu errout: 832f8efb73cSRoopa Prabhu return err; 833f8efb73cSRoopa Prabhu } 834f8efb73cSRoopa Prabhu 83577ef013aSDavid Ahern static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, 836a4ac8c98SDavid Ahern u8 cfg_via_alen, u8 *max_via_alen, 837a4ac8c98SDavid Ahern u8 *max_labels) 838f8efb73cSRoopa Prabhu { 839f8efb73cSRoopa Prabhu int remaining = len; 84077ef013aSDavid Ahern u8 nhs = 0; 841f8efb73cSRoopa Prabhu 842cf4b24f0SRobert Shearman *max_via_alen = 0; 843a4ac8c98SDavid Ahern *max_labels = 0; 844cf4b24f0SRobert Shearman 845f8efb73cSRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 846cf4b24f0SRobert Shearman struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 847cf4b24f0SRobert Shearman int attrlen; 848a4ac8c98SDavid Ahern u8 n_labels = 0; 849cf4b24f0SRobert Shearman 850cf4b24f0SRobert Shearman attrlen = rtnh_attrlen(rtnh); 851cf4b24f0SRobert Shearman nla = nla_find(attrs, attrlen, RTA_VIA); 852cf4b24f0SRobert Shearman if (nla && nla_len(nla) >= 853cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr)) { 854cf4b24f0SRobert Shearman int via_alen = nla_len(nla) - 855cf4b24f0SRobert Shearman offsetof(struct rtvia, rtvia_addr); 856cf4b24f0SRobert Shearman 857cf4b24f0SRobert Shearman if (via_alen <= MAX_VIA_ALEN) 858cf4b24f0SRobert Shearman *max_via_alen = max_t(u16, *max_via_alen, 859cf4b24f0SRobert Shearman via_alen); 860cf4b24f0SRobert Shearman } 861cf4b24f0SRobert Shearman 862a4ac8c98SDavid Ahern nla = nla_find(attrs, attrlen, RTA_NEWDST); 863a4ac8c98SDavid Ahern if (nla && 864a1f10abeSDavid Ahern nla_get_labels(nla, MAX_NEW_LABELS, &n_labels, 865a1f10abeSDavid Ahern NULL, NULL) != 0) 866a4ac8c98SDavid Ahern return 0; 867a4ac8c98SDavid Ahern 868a4ac8c98SDavid Ahern *max_labels = max_t(u8, *max_labels, n_labels); 869a4ac8c98SDavid Ahern 87077ef013aSDavid Ahern /* number of nexthops is tracked by a u8. 87177ef013aSDavid Ahern * Check for overflow. 87277ef013aSDavid Ahern */ 87377ef013aSDavid Ahern if (nhs == 255) 87477ef013aSDavid Ahern return 0; 875f8efb73cSRoopa Prabhu nhs++; 87677ef013aSDavid Ahern 877f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 878f8efb73cSRoopa Prabhu } 879f8efb73cSRoopa Prabhu 880f8efb73cSRoopa Prabhu /* leftover implies invalid nexthop configuration, discard it */ 881f8efb73cSRoopa Prabhu return remaining > 0 ? 0 : nhs; 882f8efb73cSRoopa Prabhu } 883f8efb73cSRoopa Prabhu 884f8efb73cSRoopa Prabhu static int mpls_nh_build_multi(struct mpls_route_config *cfg, 885074350e2SDavid Ahern struct mpls_route *rt, u8 max_labels, 886074350e2SDavid Ahern struct netlink_ext_ack *extack) 887f8efb73cSRoopa Prabhu { 888f8efb73cSRoopa Prabhu struct rtnexthop *rtnh = cfg->rc_mp; 889f8efb73cSRoopa Prabhu struct nlattr *nla_via, *nla_newdst; 890f8efb73cSRoopa Prabhu int remaining = cfg->rc_mp_len; 891f8efb73cSRoopa Prabhu int err = 0; 89277ef013aSDavid Ahern u8 nhs = 0; 893f8efb73cSRoopa Prabhu 894f8efb73cSRoopa Prabhu change_nexthops(rt) { 895f8efb73cSRoopa Prabhu int attrlen; 896f8efb73cSRoopa Prabhu 897f8efb73cSRoopa Prabhu nla_via = NULL; 898f8efb73cSRoopa Prabhu nla_newdst = NULL; 899f8efb73cSRoopa Prabhu 900f8efb73cSRoopa Prabhu err = -EINVAL; 901f8efb73cSRoopa Prabhu if (!rtnh_ok(rtnh, remaining)) 902f8efb73cSRoopa Prabhu goto errout; 903f8efb73cSRoopa Prabhu 9041c78efa8SRobert Shearman /* neither weighted multipath nor any flags 9051c78efa8SRobert Shearman * are supported 9061c78efa8SRobert Shearman */ 9071c78efa8SRobert Shearman if (rtnh->rtnh_hops || rtnh->rtnh_flags) 9081c78efa8SRobert Shearman goto errout; 9091c78efa8SRobert Shearman 910f8efb73cSRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 911f8efb73cSRoopa Prabhu if (attrlen > 0) { 912f8efb73cSRoopa Prabhu struct nlattr *attrs = rtnh_attrs(rtnh); 913f8efb73cSRoopa Prabhu 914f8efb73cSRoopa Prabhu nla_via = nla_find(attrs, attrlen, RTA_VIA); 915f8efb73cSRoopa Prabhu nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST); 916f8efb73cSRoopa Prabhu } 917f8efb73cSRoopa Prabhu 918cf4b24f0SRobert Shearman err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh, 919a4ac8c98SDavid Ahern rtnh->rtnh_ifindex, nla_via, nla_newdst, 920074350e2SDavid Ahern max_labels, extack); 921f8efb73cSRoopa Prabhu if (err) 922f8efb73cSRoopa Prabhu goto errout; 923f8efb73cSRoopa Prabhu 924c89359a4SRoopa Prabhu if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) 925c89359a4SRoopa Prabhu rt->rt_nhn_alive--; 926c89359a4SRoopa Prabhu 927f8efb73cSRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 928f8efb73cSRoopa Prabhu nhs++; 929f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 930f8efb73cSRoopa Prabhu 931f8efb73cSRoopa Prabhu rt->rt_nhn = nhs; 932f8efb73cSRoopa Prabhu 933f8efb73cSRoopa Prabhu return 0; 934f8efb73cSRoopa Prabhu 935f8efb73cSRoopa Prabhu errout: 936f8efb73cSRoopa Prabhu return err; 937f8efb73cSRoopa Prabhu } 938f8efb73cSRoopa Prabhu 9393968523fSDan Williams static bool mpls_label_ok(struct net *net, unsigned int *index, 940074350e2SDavid Ahern struct netlink_ext_ack *extack) 941b7b386f4SDavid Ahern { 9423968523fSDan Williams bool is_ok = true; 9433968523fSDan Williams 944b7b386f4SDavid Ahern /* Reserved labels may not be set */ 9453968523fSDan Williams if (*index < MPLS_LABEL_FIRST_UNRESERVED) { 946074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 947074350e2SDavid Ahern "Invalid label - must be MPLS_LABEL_FIRST_UNRESERVED or higher"); 9483968523fSDan Williams is_ok = false; 949074350e2SDavid Ahern } 950b7b386f4SDavid Ahern 951b7b386f4SDavid Ahern /* The full 20 bit range may not be supported. */ 9523968523fSDan Williams if (is_ok && *index >= net->mpls.platform_labels) { 953074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 954074350e2SDavid Ahern "Label >= configured maximum in platform_labels"); 9553968523fSDan Williams is_ok = false; 956074350e2SDavid Ahern } 957b7b386f4SDavid Ahern 9583968523fSDan Williams *index = array_index_nospec(*index, net->mpls.platform_labels); 9593968523fSDan Williams return is_ok; 960b7b386f4SDavid Ahern } 961b7b386f4SDavid Ahern 962074350e2SDavid Ahern static int mpls_route_add(struct mpls_route_config *cfg, 963074350e2SDavid Ahern struct netlink_ext_ack *extack) 964a2519929SEric W. Biederman { 96519d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 966a2519929SEric W. Biederman struct net *net = cfg->rc_nlinfo.nl_net; 967a2519929SEric W. Biederman struct mpls_route *rt, *old; 968a2519929SEric W. Biederman int err = -EINVAL; 969cf4b24f0SRobert Shearman u8 max_via_alen; 970f8efb73cSRoopa Prabhu unsigned index; 971a4ac8c98SDavid Ahern u8 max_labels; 97277ef013aSDavid Ahern u8 nhs; 973a2519929SEric W. Biederman 974a2519929SEric W. Biederman index = cfg->rc_label; 975a2519929SEric W. Biederman 976a2519929SEric W. Biederman /* If a label was not specified during insert pick one */ 977a2519929SEric W. Biederman if ((index == LABEL_NOT_SPECIFIED) && 978a2519929SEric W. Biederman (cfg->rc_nlflags & NLM_F_CREATE)) { 979a2519929SEric W. Biederman index = find_free_label(net); 980a2519929SEric W. Biederman } 981a2519929SEric W. Biederman 9823968523fSDan Williams if (!mpls_label_ok(net, &index, extack)) 983a2519929SEric W. Biederman goto errout; 984a2519929SEric W. Biederman 985a2519929SEric W. Biederman /* Append makes no sense with mpls */ 9860f7bbd58SEric W. Biederman err = -EOPNOTSUPP; 987074350e2SDavid Ahern if (cfg->rc_nlflags & NLM_F_APPEND) { 988074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "MPLS does not support route append"); 989a2519929SEric W. Biederman goto errout; 990074350e2SDavid Ahern } 991a2519929SEric W. Biederman 992a2519929SEric W. Biederman err = -EEXIST; 99319d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 99419d0c341SEric W. Biederman old = rtnl_dereference(platform_label[index]); 995a2519929SEric W. Biederman if ((cfg->rc_nlflags & NLM_F_EXCL) && old) 996a2519929SEric W. Biederman goto errout; 997a2519929SEric W. Biederman 998a2519929SEric W. Biederman err = -EEXIST; 999a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_REPLACE) && old) 1000a2519929SEric W. Biederman goto errout; 1001a2519929SEric W. Biederman 1002a2519929SEric W. Biederman err = -ENOENT; 1003a2519929SEric W. Biederman if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old) 1004a2519929SEric W. Biederman goto errout; 1005a2519929SEric W. Biederman 1006f8efb73cSRoopa Prabhu err = -EINVAL; 1007a4ac8c98SDavid Ahern if (cfg->rc_mp) { 1008cf4b24f0SRobert Shearman nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, 1009a4ac8c98SDavid Ahern cfg->rc_via_alen, &max_via_alen, 1010a4ac8c98SDavid Ahern &max_labels); 1011a4ac8c98SDavid Ahern } else { 1012a4ac8c98SDavid Ahern max_via_alen = cfg->rc_via_alen; 1013a4ac8c98SDavid Ahern max_labels = cfg->rc_output_labels; 1014a4ac8c98SDavid Ahern nhs = 1; 1015a4ac8c98SDavid Ahern } 1016a4ac8c98SDavid Ahern 1017074350e2SDavid Ahern if (nhs == 0) { 1018074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "Route does not contain a nexthop"); 1019f8efb73cSRoopa Prabhu goto errout; 1020074350e2SDavid Ahern } 1021f8efb73cSRoopa Prabhu 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, 136632927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 136724045a03SDavid Ahern { 136824045a03SDavid Ahern int oval = *(int *)ctl->data; 136924045a03SDavid Ahern int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 137024045a03SDavid Ahern 137124045a03SDavid Ahern if (write) { 137224045a03SDavid Ahern struct mpls_dev *mdev = ctl->extra1; 137324045a03SDavid Ahern int i = (int *)ctl->data - (int *)mdev; 137424045a03SDavid Ahern struct net *net = ctl->extra2; 137524045a03SDavid Ahern int val = *(int *)ctl->data; 137624045a03SDavid Ahern 137724045a03SDavid Ahern if (i == offsetof(struct mpls_dev, input_enabled) && 137824045a03SDavid Ahern val != oval) { 1379823566aeSDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, 1380823566aeSDavid Ahern NETCONFA_INPUT, mdev); 138124045a03SDavid Ahern } 138224045a03SDavid Ahern } 138324045a03SDavid Ahern 138424045a03SDavid Ahern return ret; 138524045a03SDavid Ahern } 138624045a03SDavid Ahern 138737bde799SRobert Shearman static const struct ctl_table mpls_dev_table[] = { 138837bde799SRobert Shearman { 138937bde799SRobert Shearman .procname = "input", 139037bde799SRobert Shearman .maxlen = sizeof(int), 139137bde799SRobert Shearman .mode = 0644, 139224045a03SDavid Ahern .proc_handler = mpls_conf_proc, 139337bde799SRobert Shearman .data = MPLS_PERDEV_SYSCTL_OFFSET(input_enabled), 139437bde799SRobert Shearman }, 139537bde799SRobert Shearman { } 139637bde799SRobert Shearman }; 139737bde799SRobert Shearman 139837bde799SRobert Shearman static int mpls_dev_sysctl_register(struct net_device *dev, 139937bde799SRobert Shearman struct mpls_dev *mdev) 140037bde799SRobert Shearman { 140137bde799SRobert Shearman char path[sizeof("net/mpls/conf/") + IFNAMSIZ]; 140224045a03SDavid Ahern struct net *net = dev_net(dev); 140337bde799SRobert Shearman struct ctl_table *table; 140437bde799SRobert Shearman int i; 140537bde799SRobert Shearman 140637bde799SRobert Shearman table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL); 140737bde799SRobert Shearman if (!table) 140837bde799SRobert Shearman goto out; 140937bde799SRobert Shearman 141037bde799SRobert Shearman /* Table data contains only offsets relative to the base of 141137bde799SRobert Shearman * the mdev at this point, so make them absolute. 141237bde799SRobert Shearman */ 141324045a03SDavid Ahern for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) { 141437bde799SRobert Shearman table[i].data = (char *)mdev + (uintptr_t)table[i].data; 141524045a03SDavid Ahern table[i].extra1 = mdev; 141624045a03SDavid Ahern table[i].extra2 = net; 141724045a03SDavid Ahern } 141837bde799SRobert Shearman 141937bde799SRobert Shearman snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); 142037bde799SRobert Shearman 14211182e4d0SDavid Ahern mdev->sysctl = register_net_sysctl(net, path, table); 142237bde799SRobert Shearman if (!mdev->sysctl) 142337bde799SRobert Shearman goto free; 142437bde799SRobert Shearman 14251182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev); 142637bde799SRobert Shearman return 0; 142737bde799SRobert Shearman 142837bde799SRobert Shearman free: 142937bde799SRobert Shearman kfree(table); 143037bde799SRobert Shearman out: 143137bde799SRobert Shearman return -ENOBUFS; 143237bde799SRobert Shearman } 143337bde799SRobert Shearman 14341182e4d0SDavid Ahern static void mpls_dev_sysctl_unregister(struct net_device *dev, 14351182e4d0SDavid Ahern struct mpls_dev *mdev) 143637bde799SRobert Shearman { 14371182e4d0SDavid Ahern struct net *net = dev_net(dev); 143837bde799SRobert Shearman struct ctl_table *table; 143937bde799SRobert Shearman 144037bde799SRobert Shearman table = mdev->sysctl->ctl_table_arg; 144137bde799SRobert Shearman unregister_net_sysctl_table(mdev->sysctl); 144237bde799SRobert Shearman kfree(table); 14431182e4d0SDavid Ahern 14441182e4d0SDavid Ahern mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev); 144537bde799SRobert Shearman } 144637bde799SRobert Shearman 144703c57747SRobert Shearman static struct mpls_dev *mpls_add_dev(struct net_device *dev) 144803c57747SRobert Shearman { 144903c57747SRobert Shearman struct mpls_dev *mdev; 145003c57747SRobert Shearman int err = -ENOMEM; 145127d69105SRobert Shearman int i; 145203c57747SRobert Shearman 145303c57747SRobert Shearman ASSERT_RTNL(); 145403c57747SRobert Shearman 145503c57747SRobert Shearman mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); 145603c57747SRobert Shearman if (!mdev) 145703c57747SRobert Shearman return ERR_PTR(err); 145803c57747SRobert Shearman 145927d69105SRobert Shearman mdev->stats = alloc_percpu(struct mpls_pcpu_stats); 146027d69105SRobert Shearman if (!mdev->stats) 146127d69105SRobert Shearman goto free; 146227d69105SRobert Shearman 146327d69105SRobert Shearman for_each_possible_cpu(i) { 146427d69105SRobert Shearman struct mpls_pcpu_stats *mpls_stats; 146527d69105SRobert Shearman 146627d69105SRobert Shearman mpls_stats = per_cpu_ptr(mdev->stats, i); 146727d69105SRobert Shearman u64_stats_init(&mpls_stats->syncp); 146827d69105SRobert Shearman } 146927d69105SRobert Shearman 14701182e4d0SDavid Ahern mdev->dev = dev; 14711182e4d0SDavid Ahern 147237bde799SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 147337bde799SRobert Shearman if (err) 147437bde799SRobert Shearman goto free; 147537bde799SRobert Shearman 147603c57747SRobert Shearman rcu_assign_pointer(dev->mpls_ptr, mdev); 147703c57747SRobert Shearman 147803c57747SRobert Shearman return mdev; 147937bde799SRobert Shearman 148037bde799SRobert Shearman free: 148127d69105SRobert Shearman free_percpu(mdev->stats); 148237bde799SRobert Shearman kfree(mdev); 148337bde799SRobert Shearman return ERR_PTR(err); 148403c57747SRobert Shearman } 148503c57747SRobert Shearman 148627d69105SRobert Shearman static void mpls_dev_destroy_rcu(struct rcu_head *head) 148727d69105SRobert Shearman { 148827d69105SRobert Shearman struct mpls_dev *mdev = container_of(head, struct mpls_dev, rcu); 148927d69105SRobert Shearman 149027d69105SRobert Shearman free_percpu(mdev->stats); 149127d69105SRobert Shearman kfree(mdev); 149227d69105SRobert Shearman } 149327d69105SRobert Shearman 1494*7d4741eaSBenjamin Poirier static int mpls_ifdown(struct net_device *dev, int event) 14950189197fSEric W. Biederman { 149619d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 14970189197fSEric W. Biederman struct net *net = dev_net(dev); 14980189197fSEric W. Biederman unsigned index; 14990189197fSEric W. Biederman 150019d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 15010189197fSEric W. Biederman for (index = 0; index < net->mpls.platform_labels; index++) { 150219d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 1503*7d4741eaSBenjamin Poirier bool nh_del = false; 1504*7d4741eaSBenjamin Poirier u8 alive = 0; 1505c89359a4SRoopa Prabhu 15060189197fSEric W. Biederman if (!rt) 15070189197fSEric W. Biederman continue; 1508c89359a4SRoopa Prabhu 1509*7d4741eaSBenjamin Poirier if (event == NETDEV_UNREGISTER) { 1510*7d4741eaSBenjamin Poirier u8 deleted = 0; 1511*7d4741eaSBenjamin Poirier 1512*7d4741eaSBenjamin Poirier for_nexthops(rt) { 1513*7d4741eaSBenjamin Poirier struct net_device *nh_dev = 1514*7d4741eaSBenjamin Poirier rtnl_dereference(nh->nh_dev); 1515*7d4741eaSBenjamin Poirier 1516*7d4741eaSBenjamin Poirier if (!nh_dev || nh_dev == dev) 1517*7d4741eaSBenjamin Poirier deleted++; 1518*7d4741eaSBenjamin Poirier if (nh_dev == dev) 1519*7d4741eaSBenjamin Poirier nh_del = true; 1520*7d4741eaSBenjamin Poirier } endfor_nexthops(rt); 1521*7d4741eaSBenjamin Poirier 1522*7d4741eaSBenjamin Poirier /* if there are no more nexthops, delete the route */ 1523*7d4741eaSBenjamin Poirier if (deleted == rt->rt_nhn) { 1524*7d4741eaSBenjamin Poirier mpls_route_update(net, index, NULL, NULL); 1525*7d4741eaSBenjamin Poirier continue; 1526*7d4741eaSBenjamin Poirier } 1527*7d4741eaSBenjamin Poirier 1528*7d4741eaSBenjamin Poirier if (nh_del) { 1529*7d4741eaSBenjamin Poirier size_t size = sizeof(*rt) + rt->rt_nhn * 1530*7d4741eaSBenjamin Poirier rt->rt_nh_size; 1531*7d4741eaSBenjamin Poirier struct mpls_route *orig = rt; 1532*7d4741eaSBenjamin Poirier 1533*7d4741eaSBenjamin Poirier rt = kmalloc(size, GFP_KERNEL); 1534*7d4741eaSBenjamin Poirier if (!rt) 1535*7d4741eaSBenjamin Poirier return -ENOMEM; 1536*7d4741eaSBenjamin Poirier memcpy(rt, orig, size); 1537*7d4741eaSBenjamin Poirier } 1538*7d4741eaSBenjamin Poirier } 1539*7d4741eaSBenjamin Poirier 1540c89359a4SRoopa Prabhu change_nexthops(rt) { 154139eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 154239eb8cd1SDavid Ahern 1543f8efb73cSRoopa Prabhu if (rtnl_dereference(nh->nh_dev) != dev) 154461733c91SDavid Ahern goto next; 154561733c91SDavid Ahern 1546c89359a4SRoopa Prabhu switch (event) { 1547c89359a4SRoopa Prabhu case NETDEV_DOWN: 1548c89359a4SRoopa Prabhu case NETDEV_UNREGISTER: 154939eb8cd1SDavid Ahern nh_flags |= RTNH_F_DEAD; 1550df561f66SGustavo A. R. Silva fallthrough; 1551c89359a4SRoopa Prabhu case NETDEV_CHANGE: 155239eb8cd1SDavid Ahern nh_flags |= RTNH_F_LINKDOWN; 1553c89359a4SRoopa Prabhu break; 1554c89359a4SRoopa Prabhu } 1555c89359a4SRoopa Prabhu if (event == NETDEV_UNREGISTER) 1556c89359a4SRoopa Prabhu RCU_INIT_POINTER(nh->nh_dev, NULL); 155739eb8cd1SDavid Ahern 155839eb8cd1SDavid Ahern if (nh->nh_flags != nh_flags) 155939eb8cd1SDavid Ahern WRITE_ONCE(nh->nh_flags, nh_flags); 156061733c91SDavid Ahern next: 156139eb8cd1SDavid Ahern if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))) 156261733c91SDavid Ahern alive++; 1563f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 156461733c91SDavid Ahern 156561733c91SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 15664ea8efadSDavid Ahern 1567*7d4741eaSBenjamin Poirier if (nh_del) 1568*7d4741eaSBenjamin Poirier mpls_route_update(net, index, rt, NULL); 15690189197fSEric W. Biederman } 1570*7d4741eaSBenjamin Poirier 1571*7d4741eaSBenjamin Poirier return 0; 1572c89359a4SRoopa Prabhu } 157303c57747SRobert Shearman 157439eb8cd1SDavid Ahern static void mpls_ifup(struct net_device *dev, unsigned int flags) 1575c89359a4SRoopa Prabhu { 1576c89359a4SRoopa Prabhu struct mpls_route __rcu **platform_label; 1577c89359a4SRoopa Prabhu struct net *net = dev_net(dev); 1578c89359a4SRoopa Prabhu unsigned index; 157977ef013aSDavid Ahern u8 alive; 158037bde799SRobert Shearman 1581c89359a4SRoopa Prabhu platform_label = rtnl_dereference(net->mpls.platform_label); 1582c89359a4SRoopa Prabhu for (index = 0; index < net->mpls.platform_labels; index++) { 1583c89359a4SRoopa Prabhu struct mpls_route *rt = rtnl_dereference(platform_label[index]); 158403c57747SRobert Shearman 1585c89359a4SRoopa Prabhu if (!rt) 1586c89359a4SRoopa Prabhu continue; 1587c89359a4SRoopa Prabhu 1588c89359a4SRoopa Prabhu alive = 0; 1589c89359a4SRoopa Prabhu change_nexthops(rt) { 159039eb8cd1SDavid Ahern unsigned int nh_flags = nh->nh_flags; 1591c89359a4SRoopa Prabhu struct net_device *nh_dev = 1592c89359a4SRoopa Prabhu rtnl_dereference(nh->nh_dev); 1593c89359a4SRoopa Prabhu 159439eb8cd1SDavid Ahern if (!(nh_flags & flags)) { 1595c89359a4SRoopa Prabhu alive++; 1596c89359a4SRoopa Prabhu continue; 1597c89359a4SRoopa Prabhu } 1598c89359a4SRoopa Prabhu if (nh_dev != dev) 1599c89359a4SRoopa Prabhu continue; 1600c89359a4SRoopa Prabhu alive++; 160139eb8cd1SDavid Ahern nh_flags &= ~flags; 1602c2e8471dSRoopa Prabhu WRITE_ONCE(nh->nh_flags, nh_flags); 1603c89359a4SRoopa Prabhu } endfor_nexthops(rt); 1604c89359a4SRoopa Prabhu 160539eb8cd1SDavid Ahern WRITE_ONCE(rt->rt_nhn_alive, alive); 1606c89359a4SRoopa Prabhu } 16070189197fSEric W. Biederman } 16080189197fSEric W. Biederman 16090189197fSEric W. Biederman static int mpls_dev_notify(struct notifier_block *this, unsigned long event, 16100189197fSEric W. Biederman void *ptr) 16110189197fSEric W. Biederman { 16120189197fSEric W. Biederman struct net_device *dev = netdev_notifier_info_to_dev(ptr); 161303c57747SRobert Shearman struct mpls_dev *mdev; 1614c89359a4SRoopa Prabhu unsigned int flags; 16150189197fSEric W. Biederman 1616c89359a4SRoopa Prabhu if (event == NETDEV_REGISTER) { 161703c57747SRobert Shearman mdev = mpls_add_dev(dev); 161803c57747SRobert Shearman if (IS_ERR(mdev)) 161903c57747SRobert Shearman return notifier_from_errno(PTR_ERR(mdev)); 1620350e7ab9SMartin Varghese 1621c89359a4SRoopa Prabhu return NOTIFY_OK; 1622c89359a4SRoopa Prabhu } 162303c57747SRobert Shearman 1624c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1625c89359a4SRoopa Prabhu if (!mdev) 1626c89359a4SRoopa Prabhu return NOTIFY_OK; 1627c89359a4SRoopa Prabhu 1628c89359a4SRoopa Prabhu switch (event) { 1629*7d4741eaSBenjamin Poirier int err; 1630*7d4741eaSBenjamin Poirier 1631c89359a4SRoopa Prabhu case NETDEV_DOWN: 1632*7d4741eaSBenjamin Poirier err = mpls_ifdown(dev, event); 1633*7d4741eaSBenjamin Poirier if (err) 1634*7d4741eaSBenjamin Poirier return notifier_from_errno(err); 1635c89359a4SRoopa Prabhu break; 1636c89359a4SRoopa Prabhu case NETDEV_UP: 1637c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1638c89359a4SRoopa Prabhu if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1639c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1640c89359a4SRoopa Prabhu else 1641c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD); 1642c89359a4SRoopa Prabhu break; 1643c89359a4SRoopa Prabhu case NETDEV_CHANGE: 1644c89359a4SRoopa Prabhu flags = dev_get_flags(dev); 1645*7d4741eaSBenjamin Poirier if (flags & (IFF_RUNNING | IFF_LOWER_UP)) { 1646c89359a4SRoopa Prabhu mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN); 1647*7d4741eaSBenjamin Poirier } else { 1648*7d4741eaSBenjamin Poirier err = mpls_ifdown(dev, event); 1649*7d4741eaSBenjamin Poirier if (err) 1650*7d4741eaSBenjamin Poirier return notifier_from_errno(err); 1651*7d4741eaSBenjamin Poirier } 1652c89359a4SRoopa Prabhu break; 16530189197fSEric W. Biederman case NETDEV_UNREGISTER: 1654*7d4741eaSBenjamin Poirier err = mpls_ifdown(dev, event); 1655*7d4741eaSBenjamin Poirier if (err) 1656*7d4741eaSBenjamin Poirier return notifier_from_errno(err); 1657c89359a4SRoopa Prabhu mdev = mpls_dev_get(dev); 1658c89359a4SRoopa Prabhu if (mdev) { 16591182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 1660c89359a4SRoopa Prabhu RCU_INIT_POINTER(dev->mpls_ptr, NULL); 166127d69105SRobert Shearman call_rcu(&mdev->rcu, mpls_dev_destroy_rcu); 1662c89359a4SRoopa Prabhu } 16630189197fSEric W. Biederman break; 16640fae3bf0SRobert Shearman case NETDEV_CHANGENAME: 16650fae3bf0SRobert Shearman mdev = mpls_dev_get(dev); 16660fae3bf0SRobert Shearman if (mdev) { 16671182e4d0SDavid Ahern mpls_dev_sysctl_unregister(dev, mdev); 16680fae3bf0SRobert Shearman err = mpls_dev_sysctl_register(dev, mdev); 16690fae3bf0SRobert Shearman if (err) 16700fae3bf0SRobert Shearman return notifier_from_errno(err); 16710fae3bf0SRobert Shearman } 16720fae3bf0SRobert Shearman break; 16730189197fSEric W. Biederman } 16740189197fSEric W. Biederman return NOTIFY_OK; 16750189197fSEric W. Biederman } 16760189197fSEric W. Biederman 16770189197fSEric W. Biederman static struct notifier_block mpls_dev_notifier = { 16780189197fSEric W. Biederman .notifier_call = mpls_dev_notify, 16790189197fSEric W. Biederman }; 16800189197fSEric W. Biederman 168103c05665SEric W. Biederman static int nla_put_via(struct sk_buff *skb, 1682b79bda3dSEric W. Biederman u8 table, const void *addr, int alen) 168303c05665SEric W. Biederman { 1684b79bda3dSEric W. Biederman static const int table_to_family[NEIGH_NR_TABLES + 1] = { 1685b79bda3dSEric W. Biederman AF_INET, AF_INET6, AF_DECnet, AF_PACKET, 1686b79bda3dSEric W. Biederman }; 168703c05665SEric W. Biederman struct nlattr *nla; 168803c05665SEric W. Biederman struct rtvia *via; 1689b79bda3dSEric W. Biederman int family = AF_UNSPEC; 169003c05665SEric W. Biederman 169103c05665SEric W. Biederman nla = nla_reserve(skb, RTA_VIA, alen + 2); 169203c05665SEric W. Biederman if (!nla) 169303c05665SEric W. Biederman return -EMSGSIZE; 169403c05665SEric W. Biederman 1695b79bda3dSEric W. Biederman if (table <= NEIGH_NR_TABLES) 1696b79bda3dSEric W. Biederman family = table_to_family[table]; 1697b79bda3dSEric W. Biederman 169803c05665SEric W. Biederman via = nla_data(nla); 169903c05665SEric W. Biederman via->rtvia_family = family; 170003c05665SEric W. Biederman memcpy(via->rtvia_addr, addr, alen); 170103c05665SEric W. Biederman return 0; 170203c05665SEric W. Biederman } 170303c05665SEric W. Biederman 1704966bae33SEric W. Biederman int nla_put_labels(struct sk_buff *skb, int attrtype, 1705966bae33SEric W. Biederman u8 labels, const u32 label[]) 1706966bae33SEric W. Biederman { 1707966bae33SEric W. Biederman struct nlattr *nla; 1708966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1709966bae33SEric W. Biederman bool bos; 1710966bae33SEric W. Biederman int i; 1711966bae33SEric W. Biederman nla = nla_reserve(skb, attrtype, labels*4); 1712966bae33SEric W. Biederman if (!nla) 1713966bae33SEric W. Biederman return -EMSGSIZE; 1714966bae33SEric W. Biederman 1715966bae33SEric W. Biederman nla_label = nla_data(nla); 1716966bae33SEric W. Biederman bos = true; 1717966bae33SEric W. Biederman for (i = labels - 1; i >= 0; i--) { 1718966bae33SEric W. Biederman nla_label[i] = mpls_entry_encode(label[i], 0, 0, bos); 1719966bae33SEric W. Biederman bos = false; 1720966bae33SEric W. Biederman } 1721966bae33SEric W. Biederman 1722966bae33SEric W. Biederman return 0; 1723966bae33SEric W. Biederman } 1724face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_put_labels); 1725966bae33SEric W. Biederman 1726a1f10abeSDavid Ahern int nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels, 1727a1f10abeSDavid Ahern u32 label[], struct netlink_ext_ack *extack) 1728966bae33SEric W. Biederman { 1729966bae33SEric W. Biederman unsigned len = nla_len(nla); 1730966bae33SEric W. Biederman struct mpls_shim_hdr *nla_label; 1731a4ac8c98SDavid Ahern u8 nla_labels; 1732966bae33SEric W. Biederman bool bos; 1733966bae33SEric W. Biederman int i; 1734966bae33SEric W. Biederman 1735a4ac8c98SDavid Ahern /* len needs to be an even multiple of 4 (the label size). Number 1736a4ac8c98SDavid Ahern * of labels is a u8 so check for overflow. 1737a4ac8c98SDavid Ahern */ 1738a1f10abeSDavid Ahern if (len & 3 || len / 4 > 255) { 1739a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1740a1f10abeSDavid Ahern "Invalid length for labels attribute"); 1741966bae33SEric W. Biederman return -EINVAL; 1742a1f10abeSDavid Ahern } 1743966bae33SEric W. Biederman 1744966bae33SEric W. Biederman /* Limit the number of new labels allowed */ 1745966bae33SEric W. Biederman nla_labels = len/4; 1746a1f10abeSDavid Ahern if (nla_labels > max_labels) { 1747a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, "Too many labels"); 1748966bae33SEric W. Biederman return -EINVAL; 1749a1f10abeSDavid Ahern } 1750966bae33SEric W. Biederman 1751a4ac8c98SDavid Ahern /* when label == NULL, caller wants number of labels */ 1752a4ac8c98SDavid Ahern if (!label) 1753a4ac8c98SDavid Ahern goto out; 1754a4ac8c98SDavid Ahern 1755966bae33SEric W. Biederman nla_label = nla_data(nla); 1756966bae33SEric W. Biederman bos = true; 1757966bae33SEric W. Biederman for (i = nla_labels - 1; i >= 0; i--, bos = false) { 1758966bae33SEric W. Biederman struct mpls_entry_decoded dec; 1759966bae33SEric W. Biederman dec = mpls_entry_decode(nla_label + i); 1760966bae33SEric W. Biederman 1761966bae33SEric W. Biederman /* Ensure the bottom of stack flag is properly set 1762966bae33SEric W. Biederman * and ttl and tc are both clear. 1763966bae33SEric W. Biederman */ 1764a1f10abeSDavid Ahern if (dec.ttl) { 1765a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1766a1f10abeSDavid Ahern "TTL in label must be 0"); 1767966bae33SEric W. Biederman return -EINVAL; 1768a1f10abeSDavid Ahern } 1769a1f10abeSDavid Ahern 1770a1f10abeSDavid Ahern if (dec.tc) { 1771a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1772a1f10abeSDavid Ahern "Traffic class in label must be 0"); 1773a1f10abeSDavid Ahern return -EINVAL; 1774a1f10abeSDavid Ahern } 1775a1f10abeSDavid Ahern 1776a1f10abeSDavid Ahern if (dec.bos != bos) { 1777a1f10abeSDavid Ahern NL_SET_BAD_ATTR(extack, nla); 1778a1f10abeSDavid Ahern if (bos) { 1779a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, 1780a1f10abeSDavid Ahern "BOS bit must be set in first label"); 1781a1f10abeSDavid Ahern } else { 1782a1f10abeSDavid Ahern NL_SET_ERR_MSG(extack, 1783a1f10abeSDavid Ahern "BOS bit can only be set in first label"); 1784a1f10abeSDavid Ahern } 1785a1f10abeSDavid Ahern return -EINVAL; 1786a1f10abeSDavid Ahern } 1787966bae33SEric W. Biederman 17885a9ab017SRobert Shearman switch (dec.label) { 178978f5b899STom Herbert case MPLS_LABEL_IMPLNULL: 17905a9ab017SRobert Shearman /* RFC3032: This is a label that an LSR may 17915a9ab017SRobert Shearman * assign and distribute, but which never 17925a9ab017SRobert Shearman * actually appears in the encapsulation. 17935a9ab017SRobert Shearman */ 1794a1f10abeSDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1795a1f10abeSDavid Ahern "Implicit NULL Label (3) can not be used in encapsulation"); 17965a9ab017SRobert Shearman return -EINVAL; 17975a9ab017SRobert Shearman } 17985a9ab017SRobert Shearman 1799966bae33SEric W. Biederman label[i] = dec.label; 1800966bae33SEric W. Biederman } 1801a4ac8c98SDavid Ahern out: 1802966bae33SEric W. Biederman *labels = nla_labels; 1803966bae33SEric W. Biederman return 0; 1804966bae33SEric W. Biederman } 1805face0188SRoopa Prabhu EXPORT_SYMBOL_GPL(nla_get_labels); 1806966bae33SEric W. Biederman 1807074350e2SDavid Ahern static int rtm_to_route_config(struct sk_buff *skb, 1808074350e2SDavid Ahern struct nlmsghdr *nlh, 1809074350e2SDavid Ahern struct mpls_route_config *cfg, 1810074350e2SDavid Ahern struct netlink_ext_ack *extack) 181103c05665SEric W. Biederman { 181203c05665SEric W. Biederman struct rtmsg *rtm; 181303c05665SEric W. Biederman struct nlattr *tb[RTA_MAX+1]; 181403c05665SEric W. Biederman int index; 181503c05665SEric W. Biederman int err; 181603c05665SEric W. Biederman 18178cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 18188cb08174SJohannes Berg rtm_mpls_policy, extack); 181903c05665SEric W. Biederman if (err < 0) 182003c05665SEric W. Biederman goto errout; 182103c05665SEric W. Biederman 182203c05665SEric W. Biederman err = -EINVAL; 182303c05665SEric W. Biederman rtm = nlmsg_data(nlh); 182403c05665SEric W. Biederman 1825074350e2SDavid Ahern if (rtm->rtm_family != AF_MPLS) { 1826074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid address family in rtmsg"); 182703c05665SEric W. Biederman goto errout; 1828074350e2SDavid Ahern } 1829074350e2SDavid Ahern if (rtm->rtm_dst_len != 20) { 1830074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_dst_len must be 20 for MPLS"); 183103c05665SEric W. Biederman goto errout; 1832074350e2SDavid Ahern } 1833074350e2SDavid Ahern if (rtm->rtm_src_len != 0) { 1834074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_src_len must be 0 for MPLS"); 183503c05665SEric W. Biederman goto errout; 1836074350e2SDavid Ahern } 1837074350e2SDavid Ahern if (rtm->rtm_tos != 0) { 1838074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_tos must be 0 for MPLS"); 183903c05665SEric W. Biederman goto errout; 1840074350e2SDavid Ahern } 1841074350e2SDavid Ahern if (rtm->rtm_table != RT_TABLE_MAIN) { 1842074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1843074350e2SDavid Ahern "MPLS only supports the main route table"); 184403c05665SEric W. Biederman goto errout; 1845074350e2SDavid Ahern } 184603c05665SEric W. Biederman /* Any value is acceptable for rtm_protocol */ 184703c05665SEric W. Biederman 184803c05665SEric W. Biederman /* As mpls uses destination specific addresses 184903c05665SEric W. Biederman * (or source specific address in the case of multicast) 185003c05665SEric W. Biederman * all addresses have universal scope. 185103c05665SEric W. Biederman */ 1852074350e2SDavid Ahern if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) { 1853074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1854074350e2SDavid Ahern "Invalid route scope - MPLS only supports UNIVERSE"); 185503c05665SEric W. Biederman goto errout; 1856074350e2SDavid Ahern } 1857074350e2SDavid Ahern if (rtm->rtm_type != RTN_UNICAST) { 1858074350e2SDavid Ahern NL_SET_ERR_MSG(extack, 1859074350e2SDavid Ahern "Invalid route type - MPLS only supports UNICAST"); 186003c05665SEric W. Biederman goto errout; 1861074350e2SDavid Ahern } 1862074350e2SDavid Ahern if (rtm->rtm_flags != 0) { 1863074350e2SDavid Ahern NL_SET_ERR_MSG(extack, "rtm_flags must be 0 for MPLS"); 186403c05665SEric W. Biederman goto errout; 1865074350e2SDavid Ahern } 186603c05665SEric W. Biederman 186703c05665SEric W. Biederman cfg->rc_label = LABEL_NOT_SPECIFIED; 186803c05665SEric W. Biederman cfg->rc_protocol = rtm->rtm_protocol; 1869eb7809f0SRobert Shearman cfg->rc_via_table = MPLS_NEIGH_TABLE_UNSPEC; 18705b441ac8SRobert Shearman cfg->rc_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 187103c05665SEric W. Biederman cfg->rc_nlflags = nlh->nlmsg_flags; 187203c05665SEric W. Biederman cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; 187303c05665SEric W. Biederman cfg->rc_nlinfo.nlh = nlh; 187403c05665SEric W. Biederman cfg->rc_nlinfo.nl_net = sock_net(skb->sk); 187503c05665SEric W. Biederman 187603c05665SEric W. Biederman for (index = 0; index <= RTA_MAX; index++) { 187703c05665SEric W. Biederman struct nlattr *nla = tb[index]; 187803c05665SEric W. Biederman if (!nla) 187903c05665SEric W. Biederman continue; 188003c05665SEric W. Biederman 188103c05665SEric W. Biederman switch (index) { 188203c05665SEric W. Biederman case RTA_OIF: 188303c05665SEric W. Biederman cfg->rc_ifindex = nla_get_u32(nla); 188403c05665SEric W. Biederman break; 188503c05665SEric W. Biederman case RTA_NEWDST: 188603c05665SEric W. Biederman if (nla_get_labels(nla, MAX_NEW_LABELS, 188703c05665SEric W. Biederman &cfg->rc_output_labels, 1888074350e2SDavid Ahern cfg->rc_output_label, extack)) 188903c05665SEric W. Biederman goto errout; 189003c05665SEric W. Biederman break; 189103c05665SEric W. Biederman case RTA_DST: 189203c05665SEric W. Biederman { 1893f8efb73cSRoopa Prabhu u8 label_count; 189403c05665SEric W. Biederman if (nla_get_labels(nla, 1, &label_count, 1895074350e2SDavid Ahern &cfg->rc_label, extack)) 189603c05665SEric W. Biederman goto errout; 189703c05665SEric W. Biederman 1898b7b386f4SDavid Ahern if (!mpls_label_ok(cfg->rc_nlinfo.nl_net, 18993968523fSDan Williams &cfg->rc_label, extack)) 190003c05665SEric W. Biederman goto errout; 190103c05665SEric W. Biederman break; 190203c05665SEric W. Biederman } 1903be48220eSDavid Ahern case RTA_GATEWAY: 1904be48220eSDavid Ahern NL_SET_ERR_MSG(extack, "MPLS does not support RTA_GATEWAY attribute"); 1905be48220eSDavid Ahern goto errout; 190603c05665SEric W. Biederman case RTA_VIA: 190703c05665SEric W. Biederman { 1908f8efb73cSRoopa Prabhu if (nla_get_via(nla, &cfg->rc_via_alen, 1909074350e2SDavid Ahern &cfg->rc_via_table, cfg->rc_via, 1910074350e2SDavid Ahern extack)) 191103c05665SEric W. Biederman goto errout; 191203c05665SEric W. Biederman break; 191303c05665SEric W. Biederman } 1914f8efb73cSRoopa Prabhu case RTA_MULTIPATH: 1915f8efb73cSRoopa Prabhu { 1916f8efb73cSRoopa Prabhu cfg->rc_mp = nla_data(nla); 1917f8efb73cSRoopa Prabhu cfg->rc_mp_len = nla_len(nla); 191803c05665SEric W. Biederman break; 191903c05665SEric W. Biederman } 19205b441ac8SRobert Shearman case RTA_TTL_PROPAGATE: 19215b441ac8SRobert Shearman { 19225b441ac8SRobert Shearman u8 ttl_propagate = nla_get_u8(nla); 19235b441ac8SRobert Shearman 1924074350e2SDavid Ahern if (ttl_propagate > 1) { 1925074350e2SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, 1926074350e2SDavid Ahern "RTA_TTL_PROPAGATE can only be 0 or 1"); 19275b441ac8SRobert Shearman goto errout; 1928074350e2SDavid Ahern } 19295b441ac8SRobert Shearman cfg->rc_ttl_propagate = ttl_propagate ? 19305b441ac8SRobert Shearman MPLS_TTL_PROP_ENABLED : 19315b441ac8SRobert Shearman MPLS_TTL_PROP_DISABLED; 19325b441ac8SRobert Shearman break; 19335b441ac8SRobert Shearman } 193403c05665SEric W. Biederman default: 1935074350e2SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, nla, "Unknown attribute"); 193603c05665SEric W. Biederman /* Unsupported attribute */ 193703c05665SEric W. Biederman goto errout; 193803c05665SEric W. Biederman } 193903c05665SEric W. Biederman } 194003c05665SEric W. Biederman 194103c05665SEric W. Biederman err = 0; 194203c05665SEric W. Biederman errout: 194303c05665SEric W. Biederman return err; 194403c05665SEric W. Biederman } 194503c05665SEric W. Biederman 1946c21ef3e3SDavid Ahern static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1947c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 194803c05665SEric W. Biederman { 1949a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 195003c05665SEric W. Biederman int err; 195103c05665SEric W. Biederman 1952a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1953a4ac8c98SDavid Ahern if (!cfg) 1954a4ac8c98SDavid Ahern return -ENOMEM; 195503c05665SEric W. Biederman 1956074350e2SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg, extack); 1957a4ac8c98SDavid Ahern if (err < 0) 1958a4ac8c98SDavid Ahern goto out; 1959a4ac8c98SDavid Ahern 1960074350e2SDavid Ahern err = mpls_route_del(cfg, extack); 1961a4ac8c98SDavid Ahern out: 1962a4ac8c98SDavid Ahern kfree(cfg); 1963a4ac8c98SDavid Ahern 1964a4ac8c98SDavid Ahern return err; 196503c05665SEric W. Biederman } 196603c05665SEric W. Biederman 196703c05665SEric W. Biederman 1968c21ef3e3SDavid Ahern static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 1969c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 197003c05665SEric W. Biederman { 1971a4ac8c98SDavid Ahern struct mpls_route_config *cfg; 197203c05665SEric W. Biederman int err; 197303c05665SEric W. Biederman 1974a4ac8c98SDavid Ahern cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1975a4ac8c98SDavid Ahern if (!cfg) 1976a4ac8c98SDavid Ahern return -ENOMEM; 197703c05665SEric W. Biederman 1978074350e2SDavid Ahern err = rtm_to_route_config(skb, nlh, cfg, extack); 1979a4ac8c98SDavid Ahern if (err < 0) 1980a4ac8c98SDavid Ahern goto out; 1981a4ac8c98SDavid Ahern 1982074350e2SDavid Ahern err = mpls_route_add(cfg, extack); 1983a4ac8c98SDavid Ahern out: 1984a4ac8c98SDavid Ahern kfree(cfg); 1985a4ac8c98SDavid Ahern 1986a4ac8c98SDavid Ahern return err; 198703c05665SEric W. Biederman } 198803c05665SEric W. Biederman 198903c05665SEric W. Biederman static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, 199003c05665SEric W. Biederman u32 label, struct mpls_route *rt, int flags) 199103c05665SEric W. Biederman { 199219d0c341SEric W. Biederman struct net_device *dev; 199303c05665SEric W. Biederman struct nlmsghdr *nlh; 199403c05665SEric W. Biederman struct rtmsg *rtm; 199503c05665SEric W. Biederman 199603c05665SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); 199703c05665SEric W. Biederman if (nlh == NULL) 199803c05665SEric W. Biederman return -EMSGSIZE; 199903c05665SEric W. Biederman 200003c05665SEric W. Biederman rtm = nlmsg_data(nlh); 200103c05665SEric W. Biederman rtm->rtm_family = AF_MPLS; 200203c05665SEric W. Biederman rtm->rtm_dst_len = 20; 200303c05665SEric W. Biederman rtm->rtm_src_len = 0; 200403c05665SEric W. Biederman rtm->rtm_tos = 0; 200503c05665SEric W. Biederman rtm->rtm_table = RT_TABLE_MAIN; 200603c05665SEric W. Biederman rtm->rtm_protocol = rt->rt_protocol; 200703c05665SEric W. Biederman rtm->rtm_scope = RT_SCOPE_UNIVERSE; 200803c05665SEric W. Biederman rtm->rtm_type = RTN_UNICAST; 200903c05665SEric W. Biederman rtm->rtm_flags = 0; 201003c05665SEric W. Biederman 201103c05665SEric W. Biederman if (nla_put_labels(skb, RTA_DST, 1, &label)) 201203c05665SEric W. Biederman goto nla_put_failure; 20135b441ac8SRobert Shearman 20145b441ac8SRobert Shearman if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) { 20155b441ac8SRobert Shearman bool ttl_propagate = 20165b441ac8SRobert Shearman rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED; 20175b441ac8SRobert Shearman 20185b441ac8SRobert Shearman if (nla_put_u8(skb, RTA_TTL_PROPAGATE, 20195b441ac8SRobert Shearman ttl_propagate)) 20205b441ac8SRobert Shearman goto nla_put_failure; 20215b441ac8SRobert Shearman } 2022f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 2023cf4b24f0SRobert Shearman const struct mpls_nh *nh = rt->rt_nh; 2024f8efb73cSRoopa Prabhu 2025f8efb73cSRoopa Prabhu if (nh->nh_labels && 2026f8efb73cSRoopa Prabhu nla_put_labels(skb, RTA_NEWDST, nh->nh_labels, 2027f8efb73cSRoopa Prabhu nh->nh_label)) 2028f8efb73cSRoopa Prabhu goto nla_put_failure; 2029eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 203072dcac96SRobert Shearman nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), 2031f8efb73cSRoopa Prabhu nh->nh_via_alen)) 2032f8efb73cSRoopa Prabhu goto nla_put_failure; 2033f8efb73cSRoopa Prabhu dev = rtnl_dereference(nh->nh_dev); 2034f8efb73cSRoopa Prabhu if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) 2035f8efb73cSRoopa Prabhu goto nla_put_failure; 2036c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) 2037c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 2038c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) 2039c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 2040f8efb73cSRoopa Prabhu } else { 2041f8efb73cSRoopa Prabhu struct rtnexthop *rtnh; 2042f8efb73cSRoopa Prabhu struct nlattr *mp; 204377ef013aSDavid Ahern u8 linkdown = 0; 204477ef013aSDavid Ahern u8 dead = 0; 2045f8efb73cSRoopa Prabhu 2046ae0be8deSMichal Kubecek mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); 2047f8efb73cSRoopa Prabhu if (!mp) 2048f8efb73cSRoopa Prabhu goto nla_put_failure; 2049f8efb73cSRoopa Prabhu 2050f8efb73cSRoopa Prabhu for_nexthops(rt) { 2051c00e51ddSDavid Ahern dev = rtnl_dereference(nh->nh_dev); 2052c00e51ddSDavid Ahern if (!dev) 2053c00e51ddSDavid Ahern continue; 2054c00e51ddSDavid Ahern 2055f8efb73cSRoopa Prabhu rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 2056f8efb73cSRoopa Prabhu if (!rtnh) 2057f8efb73cSRoopa Prabhu goto nla_put_failure; 2058f8efb73cSRoopa Prabhu 2059f8efb73cSRoopa Prabhu rtnh->rtnh_ifindex = dev->ifindex; 2060c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_LINKDOWN) { 2061c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_LINKDOWN; 2062c89359a4SRoopa Prabhu linkdown++; 2063c89359a4SRoopa Prabhu } 2064c89359a4SRoopa Prabhu if (nh->nh_flags & RTNH_F_DEAD) { 2065c89359a4SRoopa Prabhu rtnh->rtnh_flags |= RTNH_F_DEAD; 2066c89359a4SRoopa Prabhu dead++; 2067c89359a4SRoopa Prabhu } 2068c89359a4SRoopa Prabhu 2069f8efb73cSRoopa Prabhu if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST, 2070f8efb73cSRoopa Prabhu nh->nh_labels, 2071f8efb73cSRoopa Prabhu nh->nh_label)) 2072f8efb73cSRoopa Prabhu goto nla_put_failure; 2073f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 2074f20367dfSRobert Shearman nla_put_via(skb, nh->nh_via_table, 2075cf4b24f0SRobert Shearman mpls_nh_via(rt, nh), 2076f8efb73cSRoopa Prabhu nh->nh_via_alen)) 2077f8efb73cSRoopa Prabhu goto nla_put_failure; 2078f8efb73cSRoopa Prabhu 2079f8efb73cSRoopa Prabhu /* length of rtnetlink header + attributes */ 2080f8efb73cSRoopa Prabhu rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 2081f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 2082f8efb73cSRoopa Prabhu 2083c89359a4SRoopa Prabhu if (linkdown == rt->rt_nhn) 2084c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_LINKDOWN; 2085c89359a4SRoopa Prabhu if (dead == rt->rt_nhn) 2086c89359a4SRoopa Prabhu rtm->rtm_flags |= RTNH_F_DEAD; 2087c89359a4SRoopa Prabhu 2088f8efb73cSRoopa Prabhu nla_nest_end(skb, mp); 2089f8efb73cSRoopa Prabhu } 209003c05665SEric W. Biederman 209103c05665SEric W. Biederman nlmsg_end(skb, nlh); 209203c05665SEric W. Biederman return 0; 209303c05665SEric W. Biederman 209403c05665SEric W. Biederman nla_put_failure: 209503c05665SEric W. Biederman nlmsg_cancel(skb, nlh); 209603c05665SEric W. Biederman return -EMSGSIZE; 209703c05665SEric W. Biederman } 209803c05665SEric W. Biederman 2099d8a66aa2SDavid Ahern #if IS_ENABLED(CONFIG_INET) 21004724676dSDavid Ahern static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, 21014724676dSDavid Ahern struct fib_dump_filter *filter, 2102effe6792SDavid Ahern struct netlink_callback *cb) 2103d8a66aa2SDavid Ahern { 2104effe6792SDavid Ahern return ip_valid_fib_dump_req(net, nlh, filter, cb); 2105d8a66aa2SDavid Ahern } 2106d8a66aa2SDavid Ahern #else 21074724676dSDavid Ahern static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, 21084724676dSDavid Ahern struct fib_dump_filter *filter, 2109effe6792SDavid Ahern struct netlink_callback *cb) 2110d8a66aa2SDavid Ahern { 2111effe6792SDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2112196cfebfSDavid Ahern struct nlattr *tb[RTA_MAX + 1]; 2113d8a66aa2SDavid Ahern struct rtmsg *rtm; 2114196cfebfSDavid Ahern int err, i; 2115d8a66aa2SDavid Ahern 2116d8a66aa2SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 2117d8a66aa2SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid header for FIB dump request"); 2118d8a66aa2SDavid Ahern return -EINVAL; 2119d8a66aa2SDavid Ahern } 2120d8a66aa2SDavid Ahern 2121d8a66aa2SDavid Ahern rtm = nlmsg_data(nlh); 2122d8a66aa2SDavid Ahern if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || 2123196cfebfSDavid Ahern rtm->rtm_table || rtm->rtm_scope || rtm->rtm_type || 2124196cfebfSDavid Ahern rtm->rtm_flags) { 2125d8a66aa2SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for FIB dump request"); 2126d8a66aa2SDavid Ahern return -EINVAL; 2127d8a66aa2SDavid Ahern } 2128d8a66aa2SDavid Ahern 2129196cfebfSDavid Ahern if (rtm->rtm_protocol) { 2130196cfebfSDavid Ahern filter->protocol = rtm->rtm_protocol; 2131196cfebfSDavid Ahern filter->filter_set = 1; 2132196cfebfSDavid Ahern cb->answer_flags = NLM_F_DUMP_FILTERED; 2133196cfebfSDavid Ahern } 2134196cfebfSDavid Ahern 21358cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 2136196cfebfSDavid Ahern rtm_mpls_policy, extack); 2137196cfebfSDavid Ahern if (err < 0) 2138196cfebfSDavid Ahern return err; 2139196cfebfSDavid Ahern 2140196cfebfSDavid Ahern for (i = 0; i <= RTA_MAX; ++i) { 2141196cfebfSDavid Ahern int ifindex; 2142196cfebfSDavid Ahern 2143196cfebfSDavid Ahern if (i == RTA_OIF) { 2144196cfebfSDavid Ahern ifindex = nla_get_u32(tb[i]); 2145196cfebfSDavid Ahern filter->dev = __dev_get_by_index(net, ifindex); 2146196cfebfSDavid Ahern if (!filter->dev) 2147196cfebfSDavid Ahern return -ENODEV; 2148196cfebfSDavid Ahern filter->filter_set = 1; 2149196cfebfSDavid Ahern } else if (tb[i]) { 2150196cfebfSDavid Ahern NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request"); 2151d8a66aa2SDavid Ahern return -EINVAL; 2152d8a66aa2SDavid Ahern } 2153196cfebfSDavid Ahern } 2154d8a66aa2SDavid Ahern 2155d8a66aa2SDavid Ahern return 0; 2156d8a66aa2SDavid Ahern } 2157d8a66aa2SDavid Ahern #endif 2158d8a66aa2SDavid Ahern 2159bae9a78bSDavid Ahern static bool mpls_rt_uses_dev(struct mpls_route *rt, 2160bae9a78bSDavid Ahern const struct net_device *dev) 2161bae9a78bSDavid Ahern { 2162bae9a78bSDavid Ahern struct net_device *nh_dev; 2163bae9a78bSDavid Ahern 2164bae9a78bSDavid Ahern if (rt->rt_nhn == 1) { 2165bae9a78bSDavid Ahern struct mpls_nh *nh = rt->rt_nh; 2166bae9a78bSDavid Ahern 2167bae9a78bSDavid Ahern nh_dev = rtnl_dereference(nh->nh_dev); 2168bae9a78bSDavid Ahern if (dev == nh_dev) 2169bae9a78bSDavid Ahern return true; 2170bae9a78bSDavid Ahern } else { 2171bae9a78bSDavid Ahern for_nexthops(rt) { 2172bae9a78bSDavid Ahern nh_dev = rtnl_dereference(nh->nh_dev); 2173bae9a78bSDavid Ahern if (nh_dev == dev) 2174bae9a78bSDavid Ahern return true; 2175bae9a78bSDavid Ahern } endfor_nexthops(rt); 2176bae9a78bSDavid Ahern } 2177bae9a78bSDavid Ahern 2178bae9a78bSDavid Ahern return false; 2179bae9a78bSDavid Ahern } 2180bae9a78bSDavid Ahern 218103c05665SEric W. Biederman static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) 218203c05665SEric W. Biederman { 2183e8ba330aSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 218403c05665SEric W. Biederman struct net *net = sock_net(skb->sk); 218519d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 21864724676dSDavid Ahern struct fib_dump_filter filter = {}; 2187bae9a78bSDavid Ahern unsigned int flags = NLM_F_MULTI; 218819d0c341SEric W. Biederman size_t platform_labels; 218903c05665SEric W. Biederman unsigned int index; 219003c05665SEric W. Biederman 219103c05665SEric W. Biederman ASSERT_RTNL(); 219203c05665SEric W. Biederman 2193e8ba330aSDavid Ahern if (cb->strict_check) { 21944724676dSDavid Ahern int err; 2195e8ba330aSDavid Ahern 2196effe6792SDavid Ahern err = mpls_valid_fib_dump_req(net, nlh, &filter, cb); 2197e8ba330aSDavid Ahern if (err < 0) 2198e8ba330aSDavid Ahern return err; 2199bae9a78bSDavid Ahern 2200bae9a78bSDavid Ahern /* for MPLS, there is only 1 table with fixed type and flags. 2201bae9a78bSDavid Ahern * If either are set in the filter then return nothing. 2202bae9a78bSDavid Ahern */ 2203bae9a78bSDavid Ahern if ((filter.table_id && filter.table_id != RT_TABLE_MAIN) || 2204bae9a78bSDavid Ahern (filter.rt_type && filter.rt_type != RTN_UNICAST) || 2205bae9a78bSDavid Ahern filter.flags) 2206bae9a78bSDavid Ahern return skb->len; 2207e8ba330aSDavid Ahern } 2208e8ba330aSDavid Ahern 220903c05665SEric W. Biederman index = cb->args[0]; 2210a6affd24SRobert Shearman if (index < MPLS_LABEL_FIRST_UNRESERVED) 2211a6affd24SRobert Shearman index = MPLS_LABEL_FIRST_UNRESERVED; 221203c05665SEric W. Biederman 221319d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 221419d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 2215bae9a78bSDavid Ahern 2216bae9a78bSDavid Ahern if (filter.filter_set) 2217bae9a78bSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 2218bae9a78bSDavid Ahern 221919d0c341SEric W. Biederman for (; index < platform_labels; index++) { 222003c05665SEric W. Biederman struct mpls_route *rt; 2221bae9a78bSDavid Ahern 222219d0c341SEric W. Biederman rt = rtnl_dereference(platform_label[index]); 222303c05665SEric W. Biederman if (!rt) 222403c05665SEric W. Biederman continue; 222503c05665SEric W. Biederman 2226bae9a78bSDavid Ahern if ((filter.dev && !mpls_rt_uses_dev(rt, filter.dev)) || 2227bae9a78bSDavid Ahern (filter.protocol && rt->rt_protocol != filter.protocol)) 2228bae9a78bSDavid Ahern continue; 2229bae9a78bSDavid Ahern 223003c05665SEric W. Biederman if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid, 223103c05665SEric W. Biederman cb->nlh->nlmsg_seq, RTM_NEWROUTE, 2232bae9a78bSDavid Ahern index, rt, flags) < 0) 223303c05665SEric W. Biederman break; 223403c05665SEric W. Biederman } 223503c05665SEric W. Biederman cb->args[0] = index; 223603c05665SEric W. Biederman 223703c05665SEric W. Biederman return skb->len; 223803c05665SEric W. Biederman } 223903c05665SEric W. Biederman 22408de147dcSEric W. Biederman static inline size_t lfib_nlmsg_size(struct mpls_route *rt) 22418de147dcSEric W. Biederman { 22428de147dcSEric W. Biederman size_t payload = 22438de147dcSEric W. Biederman NLMSG_ALIGN(sizeof(struct rtmsg)) 22445b441ac8SRobert Shearman + nla_total_size(4) /* RTA_DST */ 22455b441ac8SRobert Shearman + nla_total_size(1); /* RTA_TTL_PROPAGATE */ 2246f8efb73cSRoopa Prabhu 2247f8efb73cSRoopa Prabhu if (rt->rt_nhn == 1) { 2248f8efb73cSRoopa Prabhu struct mpls_nh *nh = rt->rt_nh; 2249f8efb73cSRoopa Prabhu 2250f8efb73cSRoopa Prabhu if (nh->nh_dev) 2251f8efb73cSRoopa Prabhu payload += nla_total_size(4); /* RTA_OIF */ 2252eb7809f0SRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */ 225372dcac96SRobert Shearman payload += nla_total_size(2 + nh->nh_via_alen); 2254f8efb73cSRoopa Prabhu if (nh->nh_labels) /* RTA_NEWDST */ 2255f8efb73cSRoopa Prabhu payload += nla_total_size(nh->nh_labels * 4); 2256f8efb73cSRoopa Prabhu } else { 2257f8efb73cSRoopa Prabhu /* each nexthop is packed in an attribute */ 2258f8efb73cSRoopa Prabhu size_t nhsize = 0; 2259f8efb73cSRoopa Prabhu 2260f8efb73cSRoopa Prabhu for_nexthops(rt) { 2261e944e97aSDavid Ahern if (!rtnl_dereference(nh->nh_dev)) 2262e944e97aSDavid Ahern continue; 2263f8efb73cSRoopa Prabhu nhsize += nla_total_size(sizeof(struct rtnexthop)); 2264f20367dfSRobert Shearman /* RTA_VIA */ 2265f20367dfSRobert Shearman if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) 2266f8efb73cSRoopa Prabhu nhsize += nla_total_size(2 + nh->nh_via_alen); 2267f8efb73cSRoopa Prabhu if (nh->nh_labels) 2268f8efb73cSRoopa Prabhu nhsize += nla_total_size(nh->nh_labels * 4); 2269f8efb73cSRoopa Prabhu } endfor_nexthops(rt); 2270f8efb73cSRoopa Prabhu /* nested attribute */ 2271f8efb73cSRoopa Prabhu payload += nla_total_size(nhsize); 2272f8efb73cSRoopa Prabhu } 2273f8efb73cSRoopa Prabhu 22748de147dcSEric W. Biederman return payload; 22758de147dcSEric W. Biederman } 22768de147dcSEric W. Biederman 22778de147dcSEric W. Biederman static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, 22788de147dcSEric W. Biederman struct nlmsghdr *nlh, struct net *net, u32 portid, 22798de147dcSEric W. Biederman unsigned int nlm_flags) 22808de147dcSEric W. Biederman { 22818de147dcSEric W. Biederman struct sk_buff *skb; 22828de147dcSEric W. Biederman u32 seq = nlh ? nlh->nlmsg_seq : 0; 22838de147dcSEric W. Biederman int err = -ENOBUFS; 22848de147dcSEric W. Biederman 22858de147dcSEric W. Biederman skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); 22868de147dcSEric W. Biederman if (skb == NULL) 22878de147dcSEric W. Biederman goto errout; 22888de147dcSEric W. Biederman 22898de147dcSEric W. Biederman err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags); 22908de147dcSEric W. Biederman if (err < 0) { 22918de147dcSEric W. Biederman /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ 22928de147dcSEric W. Biederman WARN_ON(err == -EMSGSIZE); 22938de147dcSEric W. Biederman kfree_skb(skb); 22948de147dcSEric W. Biederman goto errout; 22958de147dcSEric W. Biederman } 22968de147dcSEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL); 22978de147dcSEric W. Biederman 22988de147dcSEric W. Biederman return; 22998de147dcSEric W. Biederman errout: 23008de147dcSEric W. Biederman if (err < 0) 23018de147dcSEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err); 23028de147dcSEric W. Biederman } 23038de147dcSEric W. Biederman 2304d77851bfSJakub Kicinski static int mpls_valid_getroute_req(struct sk_buff *skb, 2305d77851bfSJakub Kicinski const struct nlmsghdr *nlh, 2306d77851bfSJakub Kicinski struct nlattr **tb, 2307d77851bfSJakub Kicinski struct netlink_ext_ack *extack) 2308d77851bfSJakub Kicinski { 2309d77851bfSJakub Kicinski struct rtmsg *rtm; 2310d77851bfSJakub Kicinski int i, err; 2311d77851bfSJakub Kicinski 2312d77851bfSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 2313d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, 2314d77851bfSJakub Kicinski "Invalid header for get route request"); 2315d77851bfSJakub Kicinski return -EINVAL; 2316d77851bfSJakub Kicinski } 2317d77851bfSJakub Kicinski 2318d77851bfSJakub Kicinski if (!netlink_strict_get_check(skb)) 23198cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 2320d77851bfSJakub Kicinski rtm_mpls_policy, extack); 2321d77851bfSJakub Kicinski 2322d77851bfSJakub Kicinski rtm = nlmsg_data(nlh); 2323d77851bfSJakub Kicinski if ((rtm->rtm_dst_len && rtm->rtm_dst_len != 20) || 2324d77851bfSJakub Kicinski rtm->rtm_src_len || rtm->rtm_tos || rtm->rtm_table || 2325d77851bfSJakub Kicinski rtm->rtm_protocol || rtm->rtm_scope || rtm->rtm_type) { 2326d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request"); 2327d77851bfSJakub Kicinski return -EINVAL; 2328d77851bfSJakub Kicinski } 2329d77851bfSJakub Kicinski if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) { 2330d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, 2331d77851bfSJakub Kicinski "Invalid flags for get route request"); 2332d77851bfSJakub Kicinski return -EINVAL; 2333d77851bfSJakub Kicinski } 2334d77851bfSJakub Kicinski 23358cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 2336d77851bfSJakub Kicinski rtm_mpls_policy, extack); 2337d77851bfSJakub Kicinski if (err) 2338d77851bfSJakub Kicinski return err; 2339d77851bfSJakub Kicinski 2340d77851bfSJakub Kicinski if ((tb[RTA_DST] || tb[RTA_NEWDST]) && !rtm->rtm_dst_len) { 2341d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "rtm_dst_len must be 20 for MPLS"); 2342d77851bfSJakub Kicinski return -EINVAL; 2343d77851bfSJakub Kicinski } 2344d77851bfSJakub Kicinski 2345d77851bfSJakub Kicinski for (i = 0; i <= RTA_MAX; i++) { 2346d77851bfSJakub Kicinski if (!tb[i]) 2347d77851bfSJakub Kicinski continue; 2348d77851bfSJakub Kicinski 2349d77851bfSJakub Kicinski switch (i) { 2350d77851bfSJakub Kicinski case RTA_DST: 2351d77851bfSJakub Kicinski case RTA_NEWDST: 2352d77851bfSJakub Kicinski break; 2353d77851bfSJakub Kicinski default: 2354d77851bfSJakub Kicinski NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request"); 2355d77851bfSJakub Kicinski return -EINVAL; 2356d77851bfSJakub Kicinski } 2357d77851bfSJakub Kicinski } 2358d77851bfSJakub Kicinski 2359d77851bfSJakub Kicinski return 0; 2360d77851bfSJakub Kicinski } 2361d77851bfSJakub Kicinski 2362397fc9e5SRoopa Prabhu static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, 2363397fc9e5SRoopa Prabhu struct netlink_ext_ack *extack) 2364397fc9e5SRoopa Prabhu { 2365397fc9e5SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 2366397fc9e5SRoopa Prabhu u32 portid = NETLINK_CB(in_skb).portid; 2367a906c1aaSRoopa Prabhu u32 in_label = LABEL_NOT_SPECIFIED; 2368397fc9e5SRoopa Prabhu struct nlattr *tb[RTA_MAX + 1]; 2369397fc9e5SRoopa Prabhu u32 labels[MAX_NEW_LABELS]; 2370397fc9e5SRoopa Prabhu struct mpls_shim_hdr *hdr; 2371397fc9e5SRoopa Prabhu unsigned int hdr_size = 0; 2372397fc9e5SRoopa Prabhu struct net_device *dev; 2373397fc9e5SRoopa Prabhu struct mpls_route *rt; 2374397fc9e5SRoopa Prabhu struct rtmsg *rtm, *r; 2375397fc9e5SRoopa Prabhu struct nlmsghdr *nlh; 2376397fc9e5SRoopa Prabhu struct sk_buff *skb; 2377397fc9e5SRoopa Prabhu struct mpls_nh *nh; 2378397fc9e5SRoopa Prabhu u8 n_labels; 2379a906c1aaSRoopa Prabhu int err; 2380397fc9e5SRoopa Prabhu 2381d77851bfSJakub Kicinski err = mpls_valid_getroute_req(in_skb, in_nlh, tb, extack); 2382397fc9e5SRoopa Prabhu if (err < 0) 2383397fc9e5SRoopa Prabhu goto errout; 2384397fc9e5SRoopa Prabhu 2385397fc9e5SRoopa Prabhu rtm = nlmsg_data(in_nlh); 2386397fc9e5SRoopa Prabhu 2387397fc9e5SRoopa Prabhu if (tb[RTA_DST]) { 2388397fc9e5SRoopa Prabhu u8 label_count; 2389397fc9e5SRoopa Prabhu 2390397fc9e5SRoopa Prabhu if (nla_get_labels(tb[RTA_DST], 1, &label_count, 2391a906c1aaSRoopa Prabhu &in_label, extack)) { 2392a906c1aaSRoopa Prabhu err = -EINVAL; 2393397fc9e5SRoopa Prabhu goto errout; 2394a906c1aaSRoopa Prabhu } 2395397fc9e5SRoopa Prabhu 23963968523fSDan Williams if (!mpls_label_ok(net, &in_label, extack)) { 2397a906c1aaSRoopa Prabhu err = -EINVAL; 2398397fc9e5SRoopa Prabhu goto errout; 2399397fc9e5SRoopa Prabhu } 2400a906c1aaSRoopa Prabhu } 2401397fc9e5SRoopa Prabhu 2402397fc9e5SRoopa Prabhu rt = mpls_route_input_rcu(net, in_label); 2403397fc9e5SRoopa Prabhu if (!rt) { 2404397fc9e5SRoopa Prabhu err = -ENETUNREACH; 2405397fc9e5SRoopa Prabhu goto errout; 2406397fc9e5SRoopa Prabhu } 2407397fc9e5SRoopa Prabhu 2408397fc9e5SRoopa Prabhu if (rtm->rtm_flags & RTM_F_FIB_MATCH) { 2409397fc9e5SRoopa Prabhu skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL); 2410397fc9e5SRoopa Prabhu if (!skb) { 2411397fc9e5SRoopa Prabhu err = -ENOBUFS; 2412397fc9e5SRoopa Prabhu goto errout; 2413397fc9e5SRoopa Prabhu } 2414397fc9e5SRoopa Prabhu 2415397fc9e5SRoopa Prabhu err = mpls_dump_route(skb, portid, in_nlh->nlmsg_seq, 2416397fc9e5SRoopa Prabhu RTM_NEWROUTE, in_label, rt, 0); 2417397fc9e5SRoopa Prabhu if (err < 0) { 2418397fc9e5SRoopa Prabhu /* -EMSGSIZE implies BUG in lfib_nlmsg_size */ 2419397fc9e5SRoopa Prabhu WARN_ON(err == -EMSGSIZE); 2420397fc9e5SRoopa Prabhu goto errout_free; 2421397fc9e5SRoopa Prabhu } 2422397fc9e5SRoopa Prabhu 2423397fc9e5SRoopa Prabhu return rtnl_unicast(skb, net, portid); 2424397fc9e5SRoopa Prabhu } 2425397fc9e5SRoopa Prabhu 2426397fc9e5SRoopa Prabhu if (tb[RTA_NEWDST]) { 2427397fc9e5SRoopa Prabhu if (nla_get_labels(tb[RTA_NEWDST], MAX_NEW_LABELS, &n_labels, 2428397fc9e5SRoopa Prabhu labels, extack) != 0) { 2429397fc9e5SRoopa Prabhu err = -EINVAL; 2430397fc9e5SRoopa Prabhu goto errout; 2431397fc9e5SRoopa Prabhu } 2432397fc9e5SRoopa Prabhu 2433397fc9e5SRoopa Prabhu hdr_size = n_labels * sizeof(struct mpls_shim_hdr); 2434397fc9e5SRoopa Prabhu } 2435397fc9e5SRoopa Prabhu 2436397fc9e5SRoopa Prabhu skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 2437397fc9e5SRoopa Prabhu if (!skb) { 2438397fc9e5SRoopa Prabhu err = -ENOBUFS; 2439397fc9e5SRoopa Prabhu goto errout; 2440397fc9e5SRoopa Prabhu } 2441397fc9e5SRoopa Prabhu 2442397fc9e5SRoopa Prabhu skb->protocol = htons(ETH_P_MPLS_UC); 2443397fc9e5SRoopa Prabhu 2444397fc9e5SRoopa Prabhu if (hdr_size) { 2445397fc9e5SRoopa Prabhu bool bos; 2446397fc9e5SRoopa Prabhu int i; 2447397fc9e5SRoopa Prabhu 2448397fc9e5SRoopa Prabhu if (skb_cow(skb, hdr_size)) { 2449397fc9e5SRoopa Prabhu err = -ENOBUFS; 2450397fc9e5SRoopa Prabhu goto errout_free; 2451397fc9e5SRoopa Prabhu } 2452397fc9e5SRoopa Prabhu 2453397fc9e5SRoopa Prabhu skb_reserve(skb, hdr_size); 2454397fc9e5SRoopa Prabhu skb_push(skb, hdr_size); 2455397fc9e5SRoopa Prabhu skb_reset_network_header(skb); 2456397fc9e5SRoopa Prabhu 2457397fc9e5SRoopa Prabhu /* Push new labels */ 2458397fc9e5SRoopa Prabhu hdr = mpls_hdr(skb); 2459397fc9e5SRoopa Prabhu bos = true; 2460397fc9e5SRoopa Prabhu for (i = n_labels - 1; i >= 0; i--) { 2461397fc9e5SRoopa Prabhu hdr[i] = mpls_entry_encode(labels[i], 2462397fc9e5SRoopa Prabhu 1, 0, bos); 2463397fc9e5SRoopa Prabhu bos = false; 2464397fc9e5SRoopa Prabhu } 2465397fc9e5SRoopa Prabhu } 2466397fc9e5SRoopa Prabhu 2467397fc9e5SRoopa Prabhu nh = mpls_select_multipath(rt, skb); 2468397fc9e5SRoopa Prabhu if (!nh) { 2469397fc9e5SRoopa Prabhu err = -ENETUNREACH; 2470397fc9e5SRoopa Prabhu goto errout_free; 2471397fc9e5SRoopa Prabhu } 2472397fc9e5SRoopa Prabhu 2473397fc9e5SRoopa Prabhu if (hdr_size) { 2474397fc9e5SRoopa Prabhu skb_pull(skb, hdr_size); 2475397fc9e5SRoopa Prabhu skb_reset_network_header(skb); 2476397fc9e5SRoopa Prabhu } 2477397fc9e5SRoopa Prabhu 2478397fc9e5SRoopa Prabhu nlh = nlmsg_put(skb, portid, in_nlh->nlmsg_seq, 2479397fc9e5SRoopa Prabhu RTM_NEWROUTE, sizeof(*r), 0); 2480397fc9e5SRoopa Prabhu if (!nlh) { 2481397fc9e5SRoopa Prabhu err = -EMSGSIZE; 2482397fc9e5SRoopa Prabhu goto errout_free; 2483397fc9e5SRoopa Prabhu } 2484397fc9e5SRoopa Prabhu 2485397fc9e5SRoopa Prabhu r = nlmsg_data(nlh); 2486397fc9e5SRoopa Prabhu r->rtm_family = AF_MPLS; 2487397fc9e5SRoopa Prabhu r->rtm_dst_len = 20; 2488397fc9e5SRoopa Prabhu r->rtm_src_len = 0; 2489397fc9e5SRoopa Prabhu r->rtm_table = RT_TABLE_MAIN; 2490397fc9e5SRoopa Prabhu r->rtm_type = RTN_UNICAST; 2491397fc9e5SRoopa Prabhu r->rtm_scope = RT_SCOPE_UNIVERSE; 2492397fc9e5SRoopa Prabhu r->rtm_protocol = rt->rt_protocol; 2493397fc9e5SRoopa Prabhu r->rtm_flags = 0; 2494397fc9e5SRoopa Prabhu 2495397fc9e5SRoopa Prabhu if (nla_put_labels(skb, RTA_DST, 1, &in_label)) 2496397fc9e5SRoopa Prabhu goto nla_put_failure; 2497397fc9e5SRoopa Prabhu 2498397fc9e5SRoopa Prabhu if (nh->nh_labels && 2499397fc9e5SRoopa Prabhu nla_put_labels(skb, RTA_NEWDST, nh->nh_labels, 2500397fc9e5SRoopa Prabhu nh->nh_label)) 2501397fc9e5SRoopa Prabhu goto nla_put_failure; 2502397fc9e5SRoopa Prabhu 2503397fc9e5SRoopa Prabhu if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC && 2504397fc9e5SRoopa Prabhu nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh), 2505397fc9e5SRoopa Prabhu nh->nh_via_alen)) 2506397fc9e5SRoopa Prabhu goto nla_put_failure; 2507397fc9e5SRoopa Prabhu dev = rtnl_dereference(nh->nh_dev); 2508397fc9e5SRoopa Prabhu if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex)) 2509397fc9e5SRoopa Prabhu goto nla_put_failure; 2510397fc9e5SRoopa Prabhu 2511397fc9e5SRoopa Prabhu nlmsg_end(skb, nlh); 2512397fc9e5SRoopa Prabhu 2513397fc9e5SRoopa Prabhu err = rtnl_unicast(skb, net, portid); 2514397fc9e5SRoopa Prabhu errout: 2515397fc9e5SRoopa Prabhu return err; 2516397fc9e5SRoopa Prabhu 2517397fc9e5SRoopa Prabhu nla_put_failure: 2518397fc9e5SRoopa Prabhu nlmsg_cancel(skb, nlh); 2519397fc9e5SRoopa Prabhu err = -EMSGSIZE; 2520397fc9e5SRoopa Prabhu errout_free: 2521397fc9e5SRoopa Prabhu kfree_skb(skb); 2522397fc9e5SRoopa Prabhu return err; 2523397fc9e5SRoopa Prabhu } 2524397fc9e5SRoopa Prabhu 25257720c01fSEric W. Biederman static int resize_platform_label_table(struct net *net, size_t limit) 25267720c01fSEric W. Biederman { 25277720c01fSEric W. Biederman size_t size = sizeof(struct mpls_route *) * limit; 25287720c01fSEric W. Biederman size_t old_limit; 25297720c01fSEric W. Biederman size_t cp_size; 25307720c01fSEric W. Biederman struct mpls_route __rcu **labels = NULL, **old; 25317720c01fSEric W. Biederman struct mpls_route *rt0 = NULL, *rt2 = NULL; 25327720c01fSEric W. Biederman unsigned index; 25337720c01fSEric W. Biederman 25347720c01fSEric W. Biederman if (size) { 2535752ade68SMichal Hocko labels = kvzalloc(size, GFP_KERNEL); 25367720c01fSEric W. Biederman if (!labels) 25377720c01fSEric W. Biederman goto nolabels; 25387720c01fSEric W. Biederman } 25397720c01fSEric W. Biederman 25407720c01fSEric W. Biederman /* In case the predefined labels need to be populated */ 254178f5b899STom Herbert if (limit > MPLS_LABEL_IPV4NULL) { 25427720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2543a4ac8c98SDavid Ahern rt0 = mpls_rt_alloc(1, lo->addr_len, 0); 2544df1c6316SDavid Ahern if (IS_ERR(rt0)) 25457720c01fSEric W. Biederman goto nort0; 2546f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); 25477720c01fSEric W. Biederman rt0->rt_protocol = RTPROT_KERNEL; 2548118d5234SRobert Shearman rt0->rt_payload_type = MPT_IPV4; 25495b441ac8SRobert Shearman rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2550f8efb73cSRoopa Prabhu rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2551b4e04fc7SRobert Shearman rt0->rt_nh->nh_via_alen = lo->addr_len; 2552cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr, 2553cf4b24f0SRobert Shearman lo->addr_len); 25547720c01fSEric W. Biederman } 255578f5b899STom Herbert if (limit > MPLS_LABEL_IPV6NULL) { 25567720c01fSEric W. Biederman struct net_device *lo = net->loopback_dev; 2557a4ac8c98SDavid Ahern rt2 = mpls_rt_alloc(1, lo->addr_len, 0); 2558df1c6316SDavid Ahern if (IS_ERR(rt2)) 25597720c01fSEric W. Biederman goto nort2; 2560f8efb73cSRoopa Prabhu RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); 25617720c01fSEric W. Biederman rt2->rt_protocol = RTPROT_KERNEL; 2562118d5234SRobert Shearman rt2->rt_payload_type = MPT_IPV6; 25636a18c312SDavid Ahern rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; 2564f8efb73cSRoopa Prabhu rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE; 2565b4e04fc7SRobert Shearman rt2->rt_nh->nh_via_alen = lo->addr_len; 2566cf4b24f0SRobert Shearman memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr, 2567cf4b24f0SRobert Shearman lo->addr_len); 25687720c01fSEric W. Biederman } 25697720c01fSEric W. Biederman 25707720c01fSEric W. Biederman rtnl_lock(); 25717720c01fSEric W. Biederman /* Remember the original table */ 257219d0c341SEric W. Biederman old = rtnl_dereference(net->mpls.platform_label); 25737720c01fSEric W. Biederman old_limit = net->mpls.platform_labels; 25747720c01fSEric W. Biederman 25757720c01fSEric W. Biederman /* Free any labels beyond the new table */ 25767720c01fSEric W. Biederman for (index = limit; index < old_limit; index++) 2577f8efb73cSRoopa Prabhu mpls_route_update(net, index, NULL, NULL); 25787720c01fSEric W. Biederman 25797720c01fSEric W. Biederman /* Copy over the old labels */ 25807720c01fSEric W. Biederman cp_size = size; 25817720c01fSEric W. Biederman if (old_limit < limit) 25827720c01fSEric W. Biederman cp_size = old_limit * sizeof(struct mpls_route *); 25837720c01fSEric W. Biederman 25847720c01fSEric W. Biederman memcpy(labels, old, cp_size); 25857720c01fSEric W. Biederman 25867720c01fSEric W. Biederman /* If needed set the predefined labels */ 258778f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV6NULL) && 258878f5b899STom Herbert (limit > MPLS_LABEL_IPV6NULL)) { 258978f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2); 25907720c01fSEric W. Biederman rt2 = NULL; 25917720c01fSEric W. Biederman } 25927720c01fSEric W. Biederman 259378f5b899STom Herbert if ((old_limit <= MPLS_LABEL_IPV4NULL) && 259478f5b899STom Herbert (limit > MPLS_LABEL_IPV4NULL)) { 259578f5b899STom Herbert RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0); 25967720c01fSEric W. Biederman rt0 = NULL; 25977720c01fSEric W. Biederman } 25987720c01fSEric W. Biederman 25997720c01fSEric W. Biederman /* Update the global pointers */ 26007720c01fSEric W. Biederman net->mpls.platform_labels = limit; 260119d0c341SEric W. Biederman rcu_assign_pointer(net->mpls.platform_label, labels); 26027720c01fSEric W. Biederman 26037720c01fSEric W. Biederman rtnl_unlock(); 26047720c01fSEric W. Biederman 26057720c01fSEric W. Biederman mpls_rt_free(rt2); 26067720c01fSEric W. Biederman mpls_rt_free(rt0); 26077720c01fSEric W. Biederman 26087720c01fSEric W. Biederman if (old) { 26097720c01fSEric W. Biederman synchronize_rcu(); 26107720c01fSEric W. Biederman kvfree(old); 26117720c01fSEric W. Biederman } 26127720c01fSEric W. Biederman return 0; 26137720c01fSEric W. Biederman 26147720c01fSEric W. Biederman nort2: 26157720c01fSEric W. Biederman mpls_rt_free(rt0); 26167720c01fSEric W. Biederman nort0: 26177720c01fSEric W. Biederman kvfree(labels); 26187720c01fSEric W. Biederman nolabels: 26197720c01fSEric W. Biederman return -ENOMEM; 26207720c01fSEric W. Biederman } 26217720c01fSEric W. Biederman 26227720c01fSEric W. Biederman static int mpls_platform_labels(struct ctl_table *table, int write, 262332927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 26247720c01fSEric W. Biederman { 26257720c01fSEric W. Biederman struct net *net = table->data; 26267720c01fSEric W. Biederman int platform_labels = net->mpls.platform_labels; 26277720c01fSEric W. Biederman int ret; 26287720c01fSEric W. Biederman struct ctl_table tmp = { 26297720c01fSEric W. Biederman .procname = table->procname, 26307720c01fSEric W. Biederman .data = &platform_labels, 26317720c01fSEric W. Biederman .maxlen = sizeof(int), 26327720c01fSEric W. Biederman .mode = table->mode, 2633eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 26347720c01fSEric W. Biederman .extra2 = &label_limit, 26357720c01fSEric W. Biederman }; 26367720c01fSEric W. Biederman 26377720c01fSEric W. Biederman ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 26387720c01fSEric W. Biederman 26397720c01fSEric W. Biederman if (write && ret == 0) 26407720c01fSEric W. Biederman ret = resize_platform_label_table(net, platform_labels); 26417720c01fSEric W. Biederman 26427720c01fSEric W. Biederman return ret; 26437720c01fSEric W. Biederman } 26447720c01fSEric W. Biederman 26455b441ac8SRobert Shearman #define MPLS_NS_SYSCTL_OFFSET(field) \ 26465b441ac8SRobert Shearman (&((struct net *)0)->field) 26475b441ac8SRobert Shearman 264837bde799SRobert Shearman static const struct ctl_table mpls_table[] = { 26497720c01fSEric W. Biederman { 26507720c01fSEric W. Biederman .procname = "platform_labels", 26517720c01fSEric W. Biederman .data = NULL, 26527720c01fSEric W. Biederman .maxlen = sizeof(int), 26537720c01fSEric W. Biederman .mode = 0644, 26547720c01fSEric W. Biederman .proc_handler = mpls_platform_labels, 26557720c01fSEric W. Biederman }, 26565b441ac8SRobert Shearman { 26575b441ac8SRobert Shearman .procname = "ip_ttl_propagate", 26585b441ac8SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate), 26595b441ac8SRobert Shearman .maxlen = sizeof(int), 26605b441ac8SRobert Shearman .mode = 0644, 26615b441ac8SRobert Shearman .proc_handler = proc_dointvec_minmax, 2662eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 2663eec4844fSMatteo Croce .extra2 = SYSCTL_ONE, 26645b441ac8SRobert Shearman }, 2665a59166e4SRobert Shearman { 2666a59166e4SRobert Shearman .procname = "default_ttl", 2667a59166e4SRobert Shearman .data = MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl), 2668a59166e4SRobert Shearman .maxlen = sizeof(int), 2669a59166e4SRobert Shearman .mode = 0644, 2670a59166e4SRobert Shearman .proc_handler = proc_dointvec_minmax, 2671eec4844fSMatteo Croce .extra1 = SYSCTL_ONE, 2672a59166e4SRobert Shearman .extra2 = &ttl_max, 2673a59166e4SRobert Shearman }, 26747720c01fSEric W. Biederman { } 26757720c01fSEric W. Biederman }; 26767720c01fSEric W. Biederman 26770189197fSEric W. Biederman static int mpls_net_init(struct net *net) 26780189197fSEric W. Biederman { 26797720c01fSEric W. Biederman struct ctl_table *table; 26805b441ac8SRobert Shearman int i; 26817720c01fSEric W. Biederman 26820189197fSEric W. Biederman net->mpls.platform_labels = 0; 26830189197fSEric W. Biederman net->mpls.platform_label = NULL; 26845b441ac8SRobert Shearman net->mpls.ip_ttl_propagate = 1; 2685a59166e4SRobert Shearman net->mpls.default_ttl = 255; 26860189197fSEric W. Biederman 26877720c01fSEric W. Biederman table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); 26887720c01fSEric W. Biederman if (table == NULL) 26897720c01fSEric W. Biederman return -ENOMEM; 26907720c01fSEric W. Biederman 26915b441ac8SRobert Shearman /* Table data contains only offsets relative to the base of 26925b441ac8SRobert Shearman * the mdev at this point, so make them absolute. 26935b441ac8SRobert Shearman */ 26945b441ac8SRobert Shearman for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++) 26955b441ac8SRobert Shearman table[i].data = (char *)net + (uintptr_t)table[i].data; 26965b441ac8SRobert Shearman 26977720c01fSEric W. Biederman net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); 26986ea3c9d5SNikolay Aleksandrov if (net->mpls.ctl == NULL) { 26996ea3c9d5SNikolay Aleksandrov kfree(table); 27007720c01fSEric W. Biederman return -ENOMEM; 27016ea3c9d5SNikolay Aleksandrov } 27027720c01fSEric W. Biederman 27030189197fSEric W. Biederman return 0; 27040189197fSEric W. Biederman } 27050189197fSEric W. Biederman 27060189197fSEric W. Biederman static void mpls_net_exit(struct net *net) 27070189197fSEric W. Biederman { 270819d0c341SEric W. Biederman struct mpls_route __rcu **platform_label; 270919d0c341SEric W. Biederman size_t platform_labels; 27107720c01fSEric W. Biederman struct ctl_table *table; 27110189197fSEric W. Biederman unsigned int index; 27120189197fSEric W. Biederman 27137720c01fSEric W. Biederman table = net->mpls.ctl->ctl_table_arg; 27147720c01fSEric W. Biederman unregister_net_sysctl_table(net->mpls.ctl); 27157720c01fSEric W. Biederman kfree(table); 27167720c01fSEric W. Biederman 271719d0c341SEric W. Biederman /* An rcu grace period has passed since there was a device in 271819d0c341SEric W. Biederman * the network namespace (and thus the last in flight packet) 27190189197fSEric W. Biederman * left this network namespace. This is because 27200189197fSEric W. Biederman * unregister_netdevice_many and netdev_run_todo has completed 27210189197fSEric W. Biederman * for each network device that was in this network namespace. 27220189197fSEric W. Biederman * 27230189197fSEric W. Biederman * As such no additional rcu synchronization is necessary when 27240189197fSEric W. Biederman * freeing the platform_label table. 27250189197fSEric W. Biederman */ 27260189197fSEric W. Biederman rtnl_lock(); 272719d0c341SEric W. Biederman platform_label = rtnl_dereference(net->mpls.platform_label); 272819d0c341SEric W. Biederman platform_labels = net->mpls.platform_labels; 272919d0c341SEric W. Biederman for (index = 0; index < platform_labels; index++) { 273019d0c341SEric W. Biederman struct mpls_route *rt = rtnl_dereference(platform_label[index]); 273119d0c341SEric W. Biederman RCU_INIT_POINTER(platform_label[index], NULL); 2732e37791ecSDavid Ahern mpls_notify_route(net, index, rt, NULL, NULL); 27330189197fSEric W. Biederman mpls_rt_free(rt); 27340189197fSEric W. Biederman } 27350189197fSEric W. Biederman rtnl_unlock(); 27360189197fSEric W. Biederman 273719d0c341SEric W. Biederman kvfree(platform_label); 27380189197fSEric W. Biederman } 27390189197fSEric W. Biederman 27400189197fSEric W. Biederman static struct pernet_operations mpls_net_ops = { 27410189197fSEric W. Biederman .init = mpls_net_init, 27420189197fSEric W. Biederman .exit = mpls_net_exit, 27430189197fSEric W. Biederman }; 27440189197fSEric W. Biederman 274527d69105SRobert Shearman static struct rtnl_af_ops mpls_af_ops __read_mostly = { 274627d69105SRobert Shearman .family = AF_MPLS, 274727d69105SRobert Shearman .fill_stats_af = mpls_fill_stats_af, 274827d69105SRobert Shearman .get_stats_af_size = mpls_get_stats_af_size, 274927d69105SRobert Shearman }; 275027d69105SRobert Shearman 27510189197fSEric W. Biederman static int __init mpls_init(void) 27520189197fSEric W. Biederman { 27530189197fSEric W. Biederman int err; 27540189197fSEric W. Biederman 27550189197fSEric W. Biederman BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4); 27560189197fSEric W. Biederman 27570189197fSEric W. Biederman err = register_pernet_subsys(&mpls_net_ops); 27580189197fSEric W. Biederman if (err) 27590189197fSEric W. Biederman goto out; 27600189197fSEric W. Biederman 27610189197fSEric W. Biederman err = register_netdevice_notifier(&mpls_dev_notifier); 27620189197fSEric W. Biederman if (err) 27630189197fSEric W. Biederman goto out_unregister_pernet; 27640189197fSEric W. Biederman 27650189197fSEric W. Biederman dev_add_pack(&mpls_packet_type); 27660189197fSEric W. Biederman 276727d69105SRobert Shearman rtnl_af_register(&mpls_af_ops); 276827d69105SRobert Shearman 2769c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_NEWROUTE, 2770c1c502b5SFlorian Westphal mpls_rtm_newroute, NULL, 0); 2771c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_DELROUTE, 2772c1c502b5SFlorian Westphal mpls_rtm_delroute, NULL, 0); 2773c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETROUTE, 2774c1c502b5SFlorian Westphal mpls_getroute, mpls_dump_routes, 0); 2775c1c502b5SFlorian Westphal rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETNETCONF, 2776c1c502b5SFlorian Westphal mpls_netconf_get_devconf, 2777b97bac64SFlorian Westphal mpls_netconf_dump_devconf, 0); 2778bdc47641SAmine Kherbouche err = ipgre_tunnel_encap_add_mpls_ops(); 2779bdc47641SAmine Kherbouche if (err) 2780bdc47641SAmine Kherbouche pr_err("Can't add mpls over gre tunnel ops\n"); 2781bdc47641SAmine Kherbouche 27820189197fSEric W. Biederman err = 0; 27830189197fSEric W. Biederman out: 27840189197fSEric W. Biederman return err; 27850189197fSEric W. Biederman 27860189197fSEric W. Biederman out_unregister_pernet: 27870189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 27880189197fSEric W. Biederman goto out; 27890189197fSEric W. Biederman } 27900189197fSEric W. Biederman module_init(mpls_init); 27910189197fSEric W. Biederman 27920189197fSEric W. Biederman static void __exit mpls_exit(void) 27930189197fSEric W. Biederman { 279403c05665SEric W. Biederman rtnl_unregister_all(PF_MPLS); 279527d69105SRobert Shearman rtnl_af_unregister(&mpls_af_ops); 27960189197fSEric W. Biederman dev_remove_pack(&mpls_packet_type); 27970189197fSEric W. Biederman unregister_netdevice_notifier(&mpls_dev_notifier); 27980189197fSEric W. Biederman unregister_pernet_subsys(&mpls_net_ops); 2799bdc47641SAmine Kherbouche ipgre_tunnel_encap_del_mpls_ops(); 28000189197fSEric W. Biederman } 28010189197fSEric W. Biederman module_exit(mpls_exit); 28020189197fSEric W. Biederman 28030189197fSEric W. Biederman MODULE_DESCRIPTION("MultiProtocol Label Switching"); 28040189197fSEric W. Biederman MODULE_LICENSE("GPL v2"); 28050189197fSEric W. Biederman MODULE_ALIAS_NETPROTO(PF_MPLS); 2806