18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/types.h>
38c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
48c2ecf20Sopenharmony_ci#include <linux/socket.h>
58c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
68c2ecf20Sopenharmony_ci#include <linux/net.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
98c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
108c2ecf20Sopenharmony_ci#include <linux/mpls.h>
118c2ecf20Sopenharmony_ci#include <linux/netconf.h>
128c2ecf20Sopenharmony_ci#include <linux/nospec.h>
138c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
148c2ecf20Sopenharmony_ci#include <linux/percpu.h>
158c2ecf20Sopenharmony_ci#include <net/ip.h>
168c2ecf20Sopenharmony_ci#include <net/dst.h>
178c2ecf20Sopenharmony_ci#include <net/sock.h>
188c2ecf20Sopenharmony_ci#include <net/arp.h>
198c2ecf20Sopenharmony_ci#include <net/ip_fib.h>
208c2ecf20Sopenharmony_ci#include <net/netevent.h>
218c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
228c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
238c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
248c2ecf20Sopenharmony_ci#include <net/ipv6.h>
258c2ecf20Sopenharmony_ci#endif
268c2ecf20Sopenharmony_ci#include <net/ipv6_stubs.h>
278c2ecf20Sopenharmony_ci#include <net/rtnh.h>
288c2ecf20Sopenharmony_ci#include "internal.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* max memory we will use for mpls_route */
318c2ecf20Sopenharmony_ci#define MAX_MPLS_ROUTE_MEM	4096
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Maximum number of labels to look ahead at when selecting a path of
348c2ecf20Sopenharmony_ci * a multipath route
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci#define MAX_MP_SELECT_LABELS 4
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int label_limit = (1 << 20) - 1;
418c2ecf20Sopenharmony_cistatic int ttl_max = 255;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IP_TUNNEL)
448c2ecf20Sopenharmony_cistatic size_t ipgre_mpls_encap_hlen(struct ip_tunnel_encap *e)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	return sizeof(struct mpls_shim_hdr);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic const struct ip_tunnel_encap_ops mpls_iptun_ops = {
508c2ecf20Sopenharmony_ci	.encap_hlen	= ipgre_mpls_encap_hlen,
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int ipgre_tunnel_encap_add_mpls_ops(void)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	return ip_tunnel_encap_add_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void ipgre_tunnel_encap_del_mpls_ops(void)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	ip_tunnel_encap_del_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci#else
638c2ecf20Sopenharmony_cistatic int ipgre_tunnel_encap_add_mpls_ops(void)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void ipgre_tunnel_encap_del_mpls_ops(void)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void rtmsg_lfib(int event, u32 label, struct mpls_route *rt,
748c2ecf20Sopenharmony_ci		       struct nlmsghdr *nlh, struct net *net, u32 portid,
758c2ecf20Sopenharmony_ci		       unsigned int nlm_flags);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct mpls_route *rt = NULL;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (index < net->mpls.platform_labels) {
828c2ecf20Sopenharmony_ci		struct mpls_route __rcu **platform_label =
838c2ecf20Sopenharmony_ci			rcu_dereference(net->mpls.platform_label);
848c2ecf20Sopenharmony_ci		rt = rcu_dereference(platform_label[index]);
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	return rt;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cibool mpls_output_possible(const struct net_device *dev)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpls_output_possible);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	return (u8 *)nh + rt->rt_via_offset;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic const u8 *mpls_nh_via(const struct mpls_route *rt,
1018c2ecf20Sopenharmony_ci			     const struct mpls_nh *nh)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic unsigned int mpls_nh_header_size(const struct mpls_nh *nh)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	/* The size of the layer 2.5 labels to be added for this route */
1098c2ecf20Sopenharmony_ci	return nh->nh_labels * sizeof(struct mpls_shim_hdr);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciunsigned int mpls_dev_mtu(const struct net_device *dev)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	/* The amount of data the layer 2 frame can hold */
1158c2ecf20Sopenharmony_ci	return dev->mtu;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpls_dev_mtu);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cibool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	if (skb->len <= mtu)
1228c2ecf20Sopenharmony_ci		return false;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
1258c2ecf20Sopenharmony_ci		return false;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return true;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpls_pkt_too_big);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civoid mpls_stats_inc_outucastpkts(struct net_device *dev,
1328c2ecf20Sopenharmony_ci				 const struct sk_buff *skb)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_MPLS_UC)) {
1378c2ecf20Sopenharmony_ci		mdev = mpls_dev_get(dev);
1388c2ecf20Sopenharmony_ci		if (mdev)
1398c2ecf20Sopenharmony_ci			MPLS_INC_STATS_LEN(mdev, skb->len,
1408c2ecf20Sopenharmony_ci					   tx_packets,
1418c2ecf20Sopenharmony_ci					   tx_bytes);
1428c2ecf20Sopenharmony_ci	} else if (skb->protocol == htons(ETH_P_IP)) {
1438c2ecf20Sopenharmony_ci		IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
1448c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1458c2ecf20Sopenharmony_ci	} else if (skb->protocol == htons(ETH_P_IPV6)) {
1468c2ecf20Sopenharmony_ci		struct inet6_dev *in6dev = __in6_dev_get(dev);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		if (in6dev)
1498c2ecf20Sopenharmony_ci			IP6_UPD_PO_STATS(dev_net(dev), in6dev,
1508c2ecf20Sopenharmony_ci					 IPSTATS_MIB_OUT, skb->len);
1518c2ecf20Sopenharmony_ci#endif
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpls_stats_inc_outucastpkts);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct mpls_entry_decoded dec;
1598c2ecf20Sopenharmony_ci	unsigned int mpls_hdr_len = 0;
1608c2ecf20Sopenharmony_ci	struct mpls_shim_hdr *hdr;
1618c2ecf20Sopenharmony_ci	bool eli_seen = false;
1628c2ecf20Sopenharmony_ci	int label_index;
1638c2ecf20Sopenharmony_ci	u32 hash = 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (label_index = 0; label_index < MAX_MP_SELECT_LABELS;
1668c2ecf20Sopenharmony_ci	     label_index++) {
1678c2ecf20Sopenharmony_ci		mpls_hdr_len += sizeof(*hdr);
1688c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, mpls_hdr_len))
1698c2ecf20Sopenharmony_ci			break;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		/* Read and decode the current label */
1728c2ecf20Sopenharmony_ci		hdr = mpls_hdr(skb) + label_index;
1738c2ecf20Sopenharmony_ci		dec = mpls_entry_decode(hdr);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		/* RFC6790 - reserved labels MUST NOT be used as keys
1768c2ecf20Sopenharmony_ci		 * for the load-balancing function
1778c2ecf20Sopenharmony_ci		 */
1788c2ecf20Sopenharmony_ci		if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) {
1798c2ecf20Sopenharmony_ci			hash = jhash_1word(dec.label, hash);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci			/* The entropy label follows the entropy label
1828c2ecf20Sopenharmony_ci			 * indicator, so this means that the entropy
1838c2ecf20Sopenharmony_ci			 * label was just added to the hash - no need to
1848c2ecf20Sopenharmony_ci			 * go any deeper either in the label stack or in the
1858c2ecf20Sopenharmony_ci			 * payload
1868c2ecf20Sopenharmony_ci			 */
1878c2ecf20Sopenharmony_ci			if (eli_seen)
1888c2ecf20Sopenharmony_ci				break;
1898c2ecf20Sopenharmony_ci		} else if (dec.label == MPLS_LABEL_ENTROPY) {
1908c2ecf20Sopenharmony_ci			eli_seen = true;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		if (!dec.bos)
1948c2ecf20Sopenharmony_ci			continue;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		/* found bottom label; does skb have room for a header? */
1978c2ecf20Sopenharmony_ci		if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
1988c2ecf20Sopenharmony_ci			const struct iphdr *v4hdr;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci			v4hdr = (const struct iphdr *)(hdr + 1);
2018c2ecf20Sopenharmony_ci			if (v4hdr->version == 4) {
2028c2ecf20Sopenharmony_ci				hash = jhash_3words(ntohl(v4hdr->saddr),
2038c2ecf20Sopenharmony_ci						    ntohl(v4hdr->daddr),
2048c2ecf20Sopenharmony_ci						    v4hdr->protocol, hash);
2058c2ecf20Sopenharmony_ci			} else if (v4hdr->version == 6 &&
2068c2ecf20Sopenharmony_ci				   pskb_may_pull(skb, mpls_hdr_len +
2078c2ecf20Sopenharmony_ci						 sizeof(struct ipv6hdr))) {
2088c2ecf20Sopenharmony_ci				const struct ipv6hdr *v6hdr;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci				v6hdr = (const struct ipv6hdr *)(hdr + 1);
2118c2ecf20Sopenharmony_ci				hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
2128c2ecf20Sopenharmony_ci				hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
2138c2ecf20Sopenharmony_ci				hash = jhash_1word(v6hdr->nexthdr, hash);
2148c2ecf20Sopenharmony_ci			}
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return hash;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* number of alive nexthops (rt->rt_nhn_alive) and the flags for
2298c2ecf20Sopenharmony_ci * a next hop (nh->nh_flags) are modified by netdev event handlers.
2308c2ecf20Sopenharmony_ci * Since those fields can change at any moment, use READ_ONCE to
2318c2ecf20Sopenharmony_ci * access both.
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_cistatic struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
2348c2ecf20Sopenharmony_ci					     struct sk_buff *skb)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	u32 hash = 0;
2378c2ecf20Sopenharmony_ci	int nh_index = 0;
2388c2ecf20Sopenharmony_ci	int n = 0;
2398c2ecf20Sopenharmony_ci	u8 alive;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* No need to look further into packet if there's only
2428c2ecf20Sopenharmony_ci	 * one path
2438c2ecf20Sopenharmony_ci	 */
2448c2ecf20Sopenharmony_ci	if (rt->rt_nhn == 1)
2458c2ecf20Sopenharmony_ci		return rt->rt_nh;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	alive = READ_ONCE(rt->rt_nhn_alive);
2488c2ecf20Sopenharmony_ci	if (alive == 0)
2498c2ecf20Sopenharmony_ci		return NULL;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	hash = mpls_multipath_hash(rt, skb);
2528c2ecf20Sopenharmony_ci	nh_index = hash % alive;
2538c2ecf20Sopenharmony_ci	if (alive == rt->rt_nhn)
2548c2ecf20Sopenharmony_ci		goto out;
2558c2ecf20Sopenharmony_ci	for_nexthops(rt) {
2568c2ecf20Sopenharmony_ci		unsigned int nh_flags = READ_ONCE(nh->nh_flags);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
2598c2ecf20Sopenharmony_ci			continue;
2608c2ecf20Sopenharmony_ci		if (n == nh_index)
2618c2ecf20Sopenharmony_ci			return nh;
2628c2ecf20Sopenharmony_ci		n++;
2638c2ecf20Sopenharmony_ci	} endfor_nexthops(rt);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciout:
2668c2ecf20Sopenharmony_ci	return mpls_get_nexthop(rt, nh_index);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic bool mpls_egress(struct net *net, struct mpls_route *rt,
2708c2ecf20Sopenharmony_ci			struct sk_buff *skb, struct mpls_entry_decoded dec)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	enum mpls_payload_type payload_type;
2738c2ecf20Sopenharmony_ci	bool success = false;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* The IPv4 code below accesses through the IPv4 header
2768c2ecf20Sopenharmony_ci	 * checksum, which is 12 bytes into the packet.
2778c2ecf20Sopenharmony_ci	 * The IPv6 code below accesses through the IPv6 hop limit
2788c2ecf20Sopenharmony_ci	 * which is 8 bytes into the packet.
2798c2ecf20Sopenharmony_ci	 *
2808c2ecf20Sopenharmony_ci	 * For all supported cases there should always be at least 12
2818c2ecf20Sopenharmony_ci	 * bytes of packet data present.  The IPv4 header is 20 bytes
2828c2ecf20Sopenharmony_ci	 * without options and the IPv6 header is always 40 bytes
2838c2ecf20Sopenharmony_ci	 * long.
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, 12))
2868c2ecf20Sopenharmony_ci		return false;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	payload_type = rt->rt_payload_type;
2898c2ecf20Sopenharmony_ci	if (payload_type == MPT_UNSPEC)
2908c2ecf20Sopenharmony_ci		payload_type = ip_hdr(skb)->version;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	switch (payload_type) {
2938c2ecf20Sopenharmony_ci	case MPT_IPV4: {
2948c2ecf20Sopenharmony_ci		struct iphdr *hdr4 = ip_hdr(skb);
2958c2ecf20Sopenharmony_ci		u8 new_ttl;
2968c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_IP);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		/* If propagating TTL, take the decremented TTL from
2998c2ecf20Sopenharmony_ci		 * the incoming MPLS header, otherwise decrement the
3008c2ecf20Sopenharmony_ci		 * TTL, but only if not 0 to avoid underflow.
3018c2ecf20Sopenharmony_ci		 */
3028c2ecf20Sopenharmony_ci		if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED ||
3038c2ecf20Sopenharmony_ci		    (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
3048c2ecf20Sopenharmony_ci		     net->mpls.ip_ttl_propagate))
3058c2ecf20Sopenharmony_ci			new_ttl = dec.ttl;
3068c2ecf20Sopenharmony_ci		else
3078c2ecf20Sopenharmony_ci			new_ttl = hdr4->ttl ? hdr4->ttl - 1 : 0;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		csum_replace2(&hdr4->check,
3108c2ecf20Sopenharmony_ci			      htons(hdr4->ttl << 8),
3118c2ecf20Sopenharmony_ci			      htons(new_ttl << 8));
3128c2ecf20Sopenharmony_ci		hdr4->ttl = new_ttl;
3138c2ecf20Sopenharmony_ci		success = true;
3148c2ecf20Sopenharmony_ci		break;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	case MPT_IPV6: {
3178c2ecf20Sopenharmony_ci		struct ipv6hdr *hdr6 = ipv6_hdr(skb);
3188c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_IPV6);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		/* If propagating TTL, take the decremented TTL from
3218c2ecf20Sopenharmony_ci		 * the incoming MPLS header, otherwise decrement the
3228c2ecf20Sopenharmony_ci		 * hop limit, but only if not 0 to avoid underflow.
3238c2ecf20Sopenharmony_ci		 */
3248c2ecf20Sopenharmony_ci		if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED ||
3258c2ecf20Sopenharmony_ci		    (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
3268c2ecf20Sopenharmony_ci		     net->mpls.ip_ttl_propagate))
3278c2ecf20Sopenharmony_ci			hdr6->hop_limit = dec.ttl;
3288c2ecf20Sopenharmony_ci		else if (hdr6->hop_limit)
3298c2ecf20Sopenharmony_ci			hdr6->hop_limit = hdr6->hop_limit - 1;
3308c2ecf20Sopenharmony_ci		success = true;
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	case MPT_UNSPEC:
3348c2ecf20Sopenharmony_ci		/* Should have decided which protocol it is by now */
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return success;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int mpls_forward(struct sk_buff *skb, struct net_device *dev,
3428c2ecf20Sopenharmony_ci			struct packet_type *pt, struct net_device *orig_dev)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
3458c2ecf20Sopenharmony_ci	struct mpls_shim_hdr *hdr;
3468c2ecf20Sopenharmony_ci	struct mpls_route *rt;
3478c2ecf20Sopenharmony_ci	struct mpls_nh *nh;
3488c2ecf20Sopenharmony_ci	struct mpls_entry_decoded dec;
3498c2ecf20Sopenharmony_ci	struct net_device *out_dev;
3508c2ecf20Sopenharmony_ci	struct mpls_dev *out_mdev;
3518c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
3528c2ecf20Sopenharmony_ci	unsigned int hh_len;
3538c2ecf20Sopenharmony_ci	unsigned int new_header_size;
3548c2ecf20Sopenharmony_ci	unsigned int mtu;
3558c2ecf20Sopenharmony_ci	int err;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* Careful this entire function runs inside of an rcu critical section */
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	mdev = mpls_dev_get(dev);
3608c2ecf20Sopenharmony_ci	if (!mdev)
3618c2ecf20Sopenharmony_ci		goto drop;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	MPLS_INC_STATS_LEN(mdev, skb->len, rx_packets,
3648c2ecf20Sopenharmony_ci			   rx_bytes);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (!mdev->input_enabled) {
3678c2ecf20Sopenharmony_ci		MPLS_INC_STATS(mdev, rx_dropped);
3688c2ecf20Sopenharmony_ci		goto drop;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (skb->pkt_type != PACKET_HOST)
3728c2ecf20Sopenharmony_ci		goto err;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
3758c2ecf20Sopenharmony_ci		goto err;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(*hdr)))
3788c2ecf20Sopenharmony_ci		goto err;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Read and decode the label */
3818c2ecf20Sopenharmony_ci	hdr = mpls_hdr(skb);
3828c2ecf20Sopenharmony_ci	dec = mpls_entry_decode(hdr);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	rt = mpls_route_input_rcu(net, dec.label);
3858c2ecf20Sopenharmony_ci	if (!rt) {
3868c2ecf20Sopenharmony_ci		MPLS_INC_STATS(mdev, rx_noroute);
3878c2ecf20Sopenharmony_ci		goto drop;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	nh = mpls_select_multipath(rt, skb);
3918c2ecf20Sopenharmony_ci	if (!nh)
3928c2ecf20Sopenharmony_ci		goto err;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Pop the label */
3958c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(*hdr));
3968c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	skb_orphan(skb);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (skb_warn_if_lro(skb))
4018c2ecf20Sopenharmony_ci		goto err;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	skb_forward_csum(skb);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* Verify ttl is valid */
4068c2ecf20Sopenharmony_ci	if (dec.ttl <= 1)
4078c2ecf20Sopenharmony_ci		goto err;
4088c2ecf20Sopenharmony_ci	dec.ttl -= 1;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* Find the output device */
4118c2ecf20Sopenharmony_ci	out_dev = rcu_dereference(nh->nh_dev);
4128c2ecf20Sopenharmony_ci	if (!mpls_output_possible(out_dev))
4138c2ecf20Sopenharmony_ci		goto tx_err;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* Verify the destination can hold the packet */
4168c2ecf20Sopenharmony_ci	new_header_size = mpls_nh_header_size(nh);
4178c2ecf20Sopenharmony_ci	mtu = mpls_dev_mtu(out_dev);
4188c2ecf20Sopenharmony_ci	if (mpls_pkt_too_big(skb, mtu - new_header_size))
4198c2ecf20Sopenharmony_ci		goto tx_err;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	hh_len = LL_RESERVED_SPACE(out_dev);
4228c2ecf20Sopenharmony_ci	if (!out_dev->header_ops)
4238c2ecf20Sopenharmony_ci		hh_len = 0;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Ensure there is enough space for the headers in the skb */
4268c2ecf20Sopenharmony_ci	if (skb_cow(skb, hh_len + new_header_size))
4278c2ecf20Sopenharmony_ci		goto tx_err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	skb->dev = out_dev;
4308c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_MPLS_UC);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (unlikely(!new_header_size && dec.bos)) {
4338c2ecf20Sopenharmony_ci		/* Penultimate hop popping */
4348c2ecf20Sopenharmony_ci		if (!mpls_egress(dev_net(out_dev), rt, skb, dec))
4358c2ecf20Sopenharmony_ci			goto err;
4368c2ecf20Sopenharmony_ci	} else {
4378c2ecf20Sopenharmony_ci		bool bos;
4388c2ecf20Sopenharmony_ci		int i;
4398c2ecf20Sopenharmony_ci		skb_push(skb, new_header_size);
4408c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
4418c2ecf20Sopenharmony_ci		/* Push the new labels */
4428c2ecf20Sopenharmony_ci		hdr = mpls_hdr(skb);
4438c2ecf20Sopenharmony_ci		bos = dec.bos;
4448c2ecf20Sopenharmony_ci		for (i = nh->nh_labels - 1; i >= 0; i--) {
4458c2ecf20Sopenharmony_ci			hdr[i] = mpls_entry_encode(nh->nh_label[i],
4468c2ecf20Sopenharmony_ci						   dec.ttl, 0, bos);
4478c2ecf20Sopenharmony_ci			bos = false;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	mpls_stats_inc_outucastpkts(out_dev, skb);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* If via wasn't specified then send out using device address */
4548c2ecf20Sopenharmony_ci	if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC)
4558c2ecf20Sopenharmony_ci		err = neigh_xmit(NEIGH_LINK_TABLE, out_dev,
4568c2ecf20Sopenharmony_ci				 out_dev->dev_addr, skb);
4578c2ecf20Sopenharmony_ci	else
4588c2ecf20Sopenharmony_ci		err = neigh_xmit(nh->nh_via_table, out_dev,
4598c2ecf20Sopenharmony_ci				 mpls_nh_via(rt, nh), skb);
4608c2ecf20Sopenharmony_ci	if (err)
4618c2ecf20Sopenharmony_ci		net_dbg_ratelimited("%s: packet transmission failed: %d\n",
4628c2ecf20Sopenharmony_ci				    __func__, err);
4638c2ecf20Sopenharmony_ci	return 0;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_citx_err:
4668c2ecf20Sopenharmony_ci	out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL;
4678c2ecf20Sopenharmony_ci	if (out_mdev)
4688c2ecf20Sopenharmony_ci		MPLS_INC_STATS(out_mdev, tx_errors);
4698c2ecf20Sopenharmony_ci	goto drop;
4708c2ecf20Sopenharmony_cierr:
4718c2ecf20Sopenharmony_ci	MPLS_INC_STATS(mdev, rx_errors);
4728c2ecf20Sopenharmony_cidrop:
4738c2ecf20Sopenharmony_ci	kfree_skb(skb);
4748c2ecf20Sopenharmony_ci	return NET_RX_DROP;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic struct packet_type mpls_packet_type __read_mostly = {
4788c2ecf20Sopenharmony_ci	.type = cpu_to_be16(ETH_P_MPLS_UC),
4798c2ecf20Sopenharmony_ci	.func = mpls_forward,
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = {
4838c2ecf20Sopenharmony_ci	[RTA_DST]		= { .type = NLA_U32 },
4848c2ecf20Sopenharmony_ci	[RTA_OIF]		= { .type = NLA_U32 },
4858c2ecf20Sopenharmony_ci	[RTA_TTL_PROPAGATE]	= { .type = NLA_U8 },
4868c2ecf20Sopenharmony_ci};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistruct mpls_route_config {
4898c2ecf20Sopenharmony_ci	u32			rc_protocol;
4908c2ecf20Sopenharmony_ci	u32			rc_ifindex;
4918c2ecf20Sopenharmony_ci	u8			rc_via_table;
4928c2ecf20Sopenharmony_ci	u8			rc_via_alen;
4938c2ecf20Sopenharmony_ci	u8			rc_via[MAX_VIA_ALEN];
4948c2ecf20Sopenharmony_ci	u32			rc_label;
4958c2ecf20Sopenharmony_ci	u8			rc_ttl_propagate;
4968c2ecf20Sopenharmony_ci	u8			rc_output_labels;
4978c2ecf20Sopenharmony_ci	u32			rc_output_label[MAX_NEW_LABELS];
4988c2ecf20Sopenharmony_ci	u32			rc_nlflags;
4998c2ecf20Sopenharmony_ci	enum mpls_payload_type	rc_payload_type;
5008c2ecf20Sopenharmony_ci	struct nl_info		rc_nlinfo;
5018c2ecf20Sopenharmony_ci	struct rtnexthop	*rc_mp;
5028c2ecf20Sopenharmony_ci	int			rc_mp_len;
5038c2ecf20Sopenharmony_ci};
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/* all nexthops within a route have the same size based on max
5068c2ecf20Sopenharmony_ci * number of labels and max via length for a hop
5078c2ecf20Sopenharmony_ci */
5088c2ecf20Sopenharmony_cistatic struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen);
5118c2ecf20Sopenharmony_ci	struct mpls_route *rt;
5128c2ecf20Sopenharmony_ci	size_t size;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	size = sizeof(*rt) + num_nh * nh_size;
5158c2ecf20Sopenharmony_ci	if (size > MAX_MPLS_ROUTE_MEM)
5168c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	rt = kzalloc(size, GFP_KERNEL);
5198c2ecf20Sopenharmony_ci	if (!rt)
5208c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	rt->rt_nhn = num_nh;
5238c2ecf20Sopenharmony_ci	rt->rt_nhn_alive = num_nh;
5248c2ecf20Sopenharmony_ci	rt->rt_nh_size = nh_size;
5258c2ecf20Sopenharmony_ci	rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return rt;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic void mpls_rt_free(struct mpls_route *rt)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	if (rt)
5338c2ecf20Sopenharmony_ci		kfree_rcu(rt, rt_rcu);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void mpls_notify_route(struct net *net, unsigned index,
5378c2ecf20Sopenharmony_ci			      struct mpls_route *old, struct mpls_route *new,
5388c2ecf20Sopenharmony_ci			      const struct nl_info *info)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = info ? info->nlh : NULL;
5418c2ecf20Sopenharmony_ci	unsigned portid = info ? info->portid : 0;
5428c2ecf20Sopenharmony_ci	int event = new ? RTM_NEWROUTE : RTM_DELROUTE;
5438c2ecf20Sopenharmony_ci	struct mpls_route *rt = new ? new : old;
5448c2ecf20Sopenharmony_ci	unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0;
5458c2ecf20Sopenharmony_ci	/* Ignore reserved labels for now */
5468c2ecf20Sopenharmony_ci	if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED))
5478c2ecf20Sopenharmony_ci		rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void mpls_route_update(struct net *net, unsigned index,
5518c2ecf20Sopenharmony_ci			      struct mpls_route *new,
5528c2ecf20Sopenharmony_ci			      const struct nl_info *info)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
5558c2ecf20Sopenharmony_ci	struct mpls_route *rt;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	ASSERT_RTNL();
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
5608c2ecf20Sopenharmony_ci	rt = rtnl_dereference(platform_label[index]);
5618c2ecf20Sopenharmony_ci	rcu_assign_pointer(platform_label[index], new);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	mpls_notify_route(net, index, rt, new, info);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* If we removed a route free it now */
5668c2ecf20Sopenharmony_ci	mpls_rt_free(rt);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic unsigned find_free_label(struct net *net)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
5728c2ecf20Sopenharmony_ci	size_t platform_labels;
5738c2ecf20Sopenharmony_ci	unsigned index;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
5768c2ecf20Sopenharmony_ci	platform_labels = net->mpls.platform_labels;
5778c2ecf20Sopenharmony_ci	for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels;
5788c2ecf20Sopenharmony_ci	     index++) {
5798c2ecf20Sopenharmony_ci		if (!rtnl_dereference(platform_label[index]))
5808c2ecf20Sopenharmony_ci			return index;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	return LABEL_NOT_SPECIFIED;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET)
5868c2ecf20Sopenharmony_cistatic struct net_device *inet_fib_lookup_dev(struct net *net,
5878c2ecf20Sopenharmony_ci					      const void *addr)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct net_device *dev;
5908c2ecf20Sopenharmony_ci	struct rtable *rt;
5918c2ecf20Sopenharmony_ci	struct in_addr daddr;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	memcpy(&daddr, addr, sizeof(struct in_addr));
5948c2ecf20Sopenharmony_ci	rt = ip_route_output(net, daddr.s_addr, 0, 0, 0);
5958c2ecf20Sopenharmony_ci	if (IS_ERR(rt))
5968c2ecf20Sopenharmony_ci		return ERR_CAST(rt);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	dev = rt->dst.dev;
5998c2ecf20Sopenharmony_ci	dev_hold(dev);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	ip_rt_put(rt);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return dev;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci#else
6068c2ecf20Sopenharmony_cistatic struct net_device *inet_fib_lookup_dev(struct net *net,
6078c2ecf20Sopenharmony_ci					      const void *addr)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	return ERR_PTR(-EAFNOSUPPORT);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci#endif
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6148c2ecf20Sopenharmony_cistatic struct net_device *inet6_fib_lookup_dev(struct net *net,
6158c2ecf20Sopenharmony_ci					       const void *addr)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct net_device *dev;
6188c2ecf20Sopenharmony_ci	struct dst_entry *dst;
6198c2ecf20Sopenharmony_ci	struct flowi6 fl6;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (!ipv6_stub)
6228c2ecf20Sopenharmony_ci		return ERR_PTR(-EAFNOSUPPORT);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	memset(&fl6, 0, sizeof(fl6));
6258c2ecf20Sopenharmony_ci	memcpy(&fl6.daddr, addr, sizeof(struct in6_addr));
6268c2ecf20Sopenharmony_ci	dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &fl6, NULL);
6278c2ecf20Sopenharmony_ci	if (IS_ERR(dst))
6288c2ecf20Sopenharmony_ci		return ERR_CAST(dst);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	dev = dst->dev;
6318c2ecf20Sopenharmony_ci	dev_hold(dev);
6328c2ecf20Sopenharmony_ci	dst_release(dst);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	return dev;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci#else
6378c2ecf20Sopenharmony_cistatic struct net_device *inet6_fib_lookup_dev(struct net *net,
6388c2ecf20Sopenharmony_ci					       const void *addr)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	return ERR_PTR(-EAFNOSUPPORT);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci#endif
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic struct net_device *find_outdev(struct net *net,
6458c2ecf20Sopenharmony_ci				      struct mpls_route *rt,
6468c2ecf20Sopenharmony_ci				      struct mpls_nh *nh, int oif)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (!oif) {
6518c2ecf20Sopenharmony_ci		switch (nh->nh_via_table) {
6528c2ecf20Sopenharmony_ci		case NEIGH_ARP_TABLE:
6538c2ecf20Sopenharmony_ci			dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh));
6548c2ecf20Sopenharmony_ci			break;
6558c2ecf20Sopenharmony_ci		case NEIGH_ND_TABLE:
6568c2ecf20Sopenharmony_ci			dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh));
6578c2ecf20Sopenharmony_ci			break;
6588c2ecf20Sopenharmony_ci		case NEIGH_LINK_TABLE:
6598c2ecf20Sopenharmony_ci			break;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci	} else {
6628c2ecf20Sopenharmony_ci		dev = dev_get_by_index(net, oif);
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!dev)
6668c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	if (IS_ERR(dev))
6698c2ecf20Sopenharmony_ci		return dev;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* The caller is holding rtnl anyways, so release the dev reference */
6728c2ecf20Sopenharmony_ci	dev_put(dev);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return dev;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
6788c2ecf20Sopenharmony_ci			      struct mpls_nh *nh, int oif)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
6818c2ecf20Sopenharmony_ci	int err = -ENODEV;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	dev = find_outdev(net, rt, nh, oif);
6848c2ecf20Sopenharmony_ci	if (IS_ERR(dev)) {
6858c2ecf20Sopenharmony_ci		err = PTR_ERR(dev);
6868c2ecf20Sopenharmony_ci		dev = NULL;
6878c2ecf20Sopenharmony_ci		goto errout;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* Ensure this is a supported device */
6918c2ecf20Sopenharmony_ci	err = -EINVAL;
6928c2ecf20Sopenharmony_ci	if (!mpls_dev_get(dev))
6938c2ecf20Sopenharmony_ci		goto errout;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if ((nh->nh_via_table == NEIGH_LINK_TABLE) &&
6968c2ecf20Sopenharmony_ci	    (dev->addr_len != nh->nh_via_alen))
6978c2ecf20Sopenharmony_ci		goto errout;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(nh->nh_dev, dev);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP)) {
7028c2ecf20Sopenharmony_ci		nh->nh_flags |= RTNH_F_DEAD;
7038c2ecf20Sopenharmony_ci	} else {
7048c2ecf20Sopenharmony_ci		unsigned int flags;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		flags = dev_get_flags(dev);
7078c2ecf20Sopenharmony_ci		if (!(flags & (IFF_RUNNING | IFF_LOWER_UP)))
7088c2ecf20Sopenharmony_ci			nh->nh_flags |= RTNH_F_LINKDOWN;
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	return 0;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cierrout:
7148c2ecf20Sopenharmony_ci	return err;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table,
7188c2ecf20Sopenharmony_ci		       u8 via_addr[], struct netlink_ext_ack *extack)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	struct rtvia *via = nla_data(nla);
7218c2ecf20Sopenharmony_ci	int err = -EINVAL;
7228c2ecf20Sopenharmony_ci	int alen;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) {
7258c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nla,
7268c2ecf20Sopenharmony_ci				    "Invalid attribute length for RTA_VIA");
7278c2ecf20Sopenharmony_ci		goto errout;
7288c2ecf20Sopenharmony_ci	}
7298c2ecf20Sopenharmony_ci	alen = nla_len(nla) -
7308c2ecf20Sopenharmony_ci			offsetof(struct rtvia, rtvia_addr);
7318c2ecf20Sopenharmony_ci	if (alen > MAX_VIA_ALEN) {
7328c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nla,
7338c2ecf20Sopenharmony_ci				    "Invalid address length for RTA_VIA");
7348c2ecf20Sopenharmony_ci		goto errout;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Validate the address family */
7388c2ecf20Sopenharmony_ci	switch (via->rtvia_family) {
7398c2ecf20Sopenharmony_ci	case AF_PACKET:
7408c2ecf20Sopenharmony_ci		*via_table = NEIGH_LINK_TABLE;
7418c2ecf20Sopenharmony_ci		break;
7428c2ecf20Sopenharmony_ci	case AF_INET:
7438c2ecf20Sopenharmony_ci		*via_table = NEIGH_ARP_TABLE;
7448c2ecf20Sopenharmony_ci		if (alen != 4)
7458c2ecf20Sopenharmony_ci			goto errout;
7468c2ecf20Sopenharmony_ci		break;
7478c2ecf20Sopenharmony_ci	case AF_INET6:
7488c2ecf20Sopenharmony_ci		*via_table = NEIGH_ND_TABLE;
7498c2ecf20Sopenharmony_ci		if (alen != 16)
7508c2ecf20Sopenharmony_ci			goto errout;
7518c2ecf20Sopenharmony_ci		break;
7528c2ecf20Sopenharmony_ci	default:
7538c2ecf20Sopenharmony_ci		/* Unsupported address family */
7548c2ecf20Sopenharmony_ci		goto errout;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	memcpy(via_addr, via->rtvia_addr, alen);
7588c2ecf20Sopenharmony_ci	*via_alen = alen;
7598c2ecf20Sopenharmony_ci	err = 0;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cierrout:
7628c2ecf20Sopenharmony_ci	return err;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
7668c2ecf20Sopenharmony_ci				  struct mpls_route *rt)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct net *net = cfg->rc_nlinfo.nl_net;
7698c2ecf20Sopenharmony_ci	struct mpls_nh *nh = rt->rt_nh;
7708c2ecf20Sopenharmony_ci	int err;
7718c2ecf20Sopenharmony_ci	int i;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	if (!nh)
7748c2ecf20Sopenharmony_ci		return -ENOMEM;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	nh->nh_labels = cfg->rc_output_labels;
7778c2ecf20Sopenharmony_ci	for (i = 0; i < nh->nh_labels; i++)
7788c2ecf20Sopenharmony_ci		nh->nh_label[i] = cfg->rc_output_label[i];
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	nh->nh_via_table = cfg->rc_via_table;
7818c2ecf20Sopenharmony_ci	memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen);
7828c2ecf20Sopenharmony_ci	nh->nh_via_alen = cfg->rc_via_alen;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex);
7858c2ecf20Sopenharmony_ci	if (err)
7868c2ecf20Sopenharmony_ci		goto errout;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
7898c2ecf20Sopenharmony_ci		rt->rt_nhn_alive--;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return 0;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cierrout:
7948c2ecf20Sopenharmony_ci	return err;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int mpls_nh_build(struct net *net, struct mpls_route *rt,
7988c2ecf20Sopenharmony_ci			 struct mpls_nh *nh, int oif, struct nlattr *via,
7998c2ecf20Sopenharmony_ci			 struct nlattr *newdst, u8 max_labels,
8008c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	int err = -ENOMEM;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (!nh)
8058c2ecf20Sopenharmony_ci		goto errout;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (newdst) {
8088c2ecf20Sopenharmony_ci		err = nla_get_labels(newdst, max_labels, &nh->nh_labels,
8098c2ecf20Sopenharmony_ci				     nh->nh_label, extack);
8108c2ecf20Sopenharmony_ci		if (err)
8118c2ecf20Sopenharmony_ci			goto errout;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (via) {
8158c2ecf20Sopenharmony_ci		err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
8168c2ecf20Sopenharmony_ci				  __mpls_nh_via(rt, nh), extack);
8178c2ecf20Sopenharmony_ci		if (err)
8188c2ecf20Sopenharmony_ci			goto errout;
8198c2ecf20Sopenharmony_ci	} else {
8208c2ecf20Sopenharmony_ci		nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	err = mpls_nh_assign_dev(net, rt, nh, oif);
8248c2ecf20Sopenharmony_ci	if (err)
8258c2ecf20Sopenharmony_ci		goto errout;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	return 0;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cierrout:
8308c2ecf20Sopenharmony_ci	return err;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len,
8348c2ecf20Sopenharmony_ci			      u8 cfg_via_alen, u8 *max_via_alen,
8358c2ecf20Sopenharmony_ci			      u8 *max_labels)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	int remaining = len;
8388c2ecf20Sopenharmony_ci	u8 nhs = 0;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	*max_via_alen = 0;
8418c2ecf20Sopenharmony_ci	*max_labels = 0;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	while (rtnh_ok(rtnh, remaining)) {
8448c2ecf20Sopenharmony_ci		struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
8458c2ecf20Sopenharmony_ci		int attrlen;
8468c2ecf20Sopenharmony_ci		u8 n_labels = 0;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
8498c2ecf20Sopenharmony_ci		nla = nla_find(attrs, attrlen, RTA_VIA);
8508c2ecf20Sopenharmony_ci		if (nla && nla_len(nla) >=
8518c2ecf20Sopenharmony_ci		    offsetof(struct rtvia, rtvia_addr)) {
8528c2ecf20Sopenharmony_ci			int via_alen = nla_len(nla) -
8538c2ecf20Sopenharmony_ci				offsetof(struct rtvia, rtvia_addr);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci			if (via_alen <= MAX_VIA_ALEN)
8568c2ecf20Sopenharmony_ci				*max_via_alen = max_t(u16, *max_via_alen,
8578c2ecf20Sopenharmony_ci						      via_alen);
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		nla = nla_find(attrs, attrlen, RTA_NEWDST);
8618c2ecf20Sopenharmony_ci		if (nla &&
8628c2ecf20Sopenharmony_ci		    nla_get_labels(nla, MAX_NEW_LABELS, &n_labels,
8638c2ecf20Sopenharmony_ci				   NULL, NULL) != 0)
8648c2ecf20Sopenharmony_ci			return 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		*max_labels = max_t(u8, *max_labels, n_labels);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci		/* number of nexthops is tracked by a u8.
8698c2ecf20Sopenharmony_ci		 * Check for overflow.
8708c2ecf20Sopenharmony_ci		 */
8718c2ecf20Sopenharmony_ci		if (nhs == 255)
8728c2ecf20Sopenharmony_ci			return 0;
8738c2ecf20Sopenharmony_ci		nhs++;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/* leftover implies invalid nexthop configuration, discard it */
8798c2ecf20Sopenharmony_ci	return remaining > 0 ? 0 : nhs;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic int mpls_nh_build_multi(struct mpls_route_config *cfg,
8838c2ecf20Sopenharmony_ci			       struct mpls_route *rt, u8 max_labels,
8848c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct rtnexthop *rtnh = cfg->rc_mp;
8878c2ecf20Sopenharmony_ci	struct nlattr *nla_via, *nla_newdst;
8888c2ecf20Sopenharmony_ci	int remaining = cfg->rc_mp_len;
8898c2ecf20Sopenharmony_ci	int err = 0;
8908c2ecf20Sopenharmony_ci	u8 nhs = 0;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	change_nexthops(rt) {
8938c2ecf20Sopenharmony_ci		int attrlen;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		nla_via = NULL;
8968c2ecf20Sopenharmony_ci		nla_newdst = NULL;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		err = -EINVAL;
8998c2ecf20Sopenharmony_ci		if (!rtnh_ok(rtnh, remaining))
9008c2ecf20Sopenharmony_ci			goto errout;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		/* neither weighted multipath nor any flags
9038c2ecf20Sopenharmony_ci		 * are supported
9048c2ecf20Sopenharmony_ci		 */
9058c2ecf20Sopenharmony_ci		if (rtnh->rtnh_hops || rtnh->rtnh_flags)
9068c2ecf20Sopenharmony_ci			goto errout;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		attrlen = rtnh_attrlen(rtnh);
9098c2ecf20Sopenharmony_ci		if (attrlen > 0) {
9108c2ecf20Sopenharmony_ci			struct nlattr *attrs = rtnh_attrs(rtnh);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci			nla_via = nla_find(attrs, attrlen, RTA_VIA);
9138c2ecf20Sopenharmony_ci			nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST);
9148c2ecf20Sopenharmony_ci		}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
9178c2ecf20Sopenharmony_ci				    rtnh->rtnh_ifindex, nla_via, nla_newdst,
9188c2ecf20Sopenharmony_ci				    max_labels, extack);
9198c2ecf20Sopenharmony_ci		if (err)
9208c2ecf20Sopenharmony_ci			goto errout;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
9238c2ecf20Sopenharmony_ci			rt->rt_nhn_alive--;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci		rtnh = rtnh_next(rtnh, &remaining);
9268c2ecf20Sopenharmony_ci		nhs++;
9278c2ecf20Sopenharmony_ci	} endfor_nexthops(rt);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	rt->rt_nhn = nhs;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	return 0;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cierrout:
9348c2ecf20Sopenharmony_ci	return err;
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic bool mpls_label_ok(struct net *net, unsigned int *index,
9388c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	bool is_ok = true;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	/* Reserved labels may not be set */
9438c2ecf20Sopenharmony_ci	if (*index < MPLS_LABEL_FIRST_UNRESERVED) {
9448c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
9458c2ecf20Sopenharmony_ci			       "Invalid label - must be MPLS_LABEL_FIRST_UNRESERVED or higher");
9468c2ecf20Sopenharmony_ci		is_ok = false;
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	/* The full 20 bit range may not be supported. */
9508c2ecf20Sopenharmony_ci	if (is_ok && *index >= net->mpls.platform_labels) {
9518c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
9528c2ecf20Sopenharmony_ci			       "Label >= configured maximum in platform_labels");
9538c2ecf20Sopenharmony_ci		is_ok = false;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	*index = array_index_nospec(*index, net->mpls.platform_labels);
9578c2ecf20Sopenharmony_ci	return is_ok;
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic int mpls_route_add(struct mpls_route_config *cfg,
9618c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
9648c2ecf20Sopenharmony_ci	struct net *net = cfg->rc_nlinfo.nl_net;
9658c2ecf20Sopenharmony_ci	struct mpls_route *rt, *old;
9668c2ecf20Sopenharmony_ci	int err = -EINVAL;
9678c2ecf20Sopenharmony_ci	u8 max_via_alen;
9688c2ecf20Sopenharmony_ci	unsigned index;
9698c2ecf20Sopenharmony_ci	u8 max_labels;
9708c2ecf20Sopenharmony_ci	u8 nhs;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	index = cfg->rc_label;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* If a label was not specified during insert pick one */
9758c2ecf20Sopenharmony_ci	if ((index == LABEL_NOT_SPECIFIED) &&
9768c2ecf20Sopenharmony_ci	    (cfg->rc_nlflags & NLM_F_CREATE)) {
9778c2ecf20Sopenharmony_ci		index = find_free_label(net);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (!mpls_label_ok(net, &index, extack))
9818c2ecf20Sopenharmony_ci		goto errout;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* Append makes no sense with mpls */
9848c2ecf20Sopenharmony_ci	err = -EOPNOTSUPP;
9858c2ecf20Sopenharmony_ci	if (cfg->rc_nlflags & NLM_F_APPEND) {
9868c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "MPLS does not support route append");
9878c2ecf20Sopenharmony_ci		goto errout;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	err = -EEXIST;
9918c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
9928c2ecf20Sopenharmony_ci	old = rtnl_dereference(platform_label[index]);
9938c2ecf20Sopenharmony_ci	if ((cfg->rc_nlflags & NLM_F_EXCL) && old)
9948c2ecf20Sopenharmony_ci		goto errout;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	err = -EEXIST;
9978c2ecf20Sopenharmony_ci	if (!(cfg->rc_nlflags & NLM_F_REPLACE) && old)
9988c2ecf20Sopenharmony_ci		goto errout;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	err = -ENOENT;
10018c2ecf20Sopenharmony_ci	if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old)
10028c2ecf20Sopenharmony_ci		goto errout;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	err = -EINVAL;
10058c2ecf20Sopenharmony_ci	if (cfg->rc_mp) {
10068c2ecf20Sopenharmony_ci		nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
10078c2ecf20Sopenharmony_ci					  cfg->rc_via_alen, &max_via_alen,
10088c2ecf20Sopenharmony_ci					  &max_labels);
10098c2ecf20Sopenharmony_ci	} else {
10108c2ecf20Sopenharmony_ci		max_via_alen = cfg->rc_via_alen;
10118c2ecf20Sopenharmony_ci		max_labels = cfg->rc_output_labels;
10128c2ecf20Sopenharmony_ci		nhs = 1;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (nhs == 0) {
10168c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Route does not contain a nexthop");
10178c2ecf20Sopenharmony_ci		goto errout;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	err = -ENOMEM;
10218c2ecf20Sopenharmony_ci	rt = mpls_rt_alloc(nhs, max_via_alen, max_labels);
10228c2ecf20Sopenharmony_ci	if (IS_ERR(rt)) {
10238c2ecf20Sopenharmony_ci		err = PTR_ERR(rt);
10248c2ecf20Sopenharmony_ci		goto errout;
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	rt->rt_protocol = cfg->rc_protocol;
10288c2ecf20Sopenharmony_ci	rt->rt_payload_type = cfg->rc_payload_type;
10298c2ecf20Sopenharmony_ci	rt->rt_ttl_propagate = cfg->rc_ttl_propagate;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (cfg->rc_mp)
10328c2ecf20Sopenharmony_ci		err = mpls_nh_build_multi(cfg, rt, max_labels, extack);
10338c2ecf20Sopenharmony_ci	else
10348c2ecf20Sopenharmony_ci		err = mpls_nh_build_from_cfg(cfg, rt);
10358c2ecf20Sopenharmony_ci	if (err)
10368c2ecf20Sopenharmony_ci		goto freert;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	mpls_route_update(net, index, rt, &cfg->rc_nlinfo);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	return 0;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cifreert:
10438c2ecf20Sopenharmony_ci	mpls_rt_free(rt);
10448c2ecf20Sopenharmony_cierrout:
10458c2ecf20Sopenharmony_ci	return err;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic int mpls_route_del(struct mpls_route_config *cfg,
10498c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	struct net *net = cfg->rc_nlinfo.nl_net;
10528c2ecf20Sopenharmony_ci	unsigned index;
10538c2ecf20Sopenharmony_ci	int err = -EINVAL;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	index = cfg->rc_label;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	if (!mpls_label_ok(net, &index, extack))
10588c2ecf20Sopenharmony_ci		goto errout;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	err = 0;
10638c2ecf20Sopenharmony_cierrout:
10648c2ecf20Sopenharmony_ci	return err;
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic void mpls_get_stats(struct mpls_dev *mdev,
10688c2ecf20Sopenharmony_ci			   struct mpls_link_stats *stats)
10698c2ecf20Sopenharmony_ci{
10708c2ecf20Sopenharmony_ci	struct mpls_pcpu_stats *p;
10718c2ecf20Sopenharmony_ci	int i;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	memset(stats, 0, sizeof(*stats));
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
10768c2ecf20Sopenharmony_ci		struct mpls_link_stats local;
10778c2ecf20Sopenharmony_ci		unsigned int start;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci		p = per_cpu_ptr(mdev->stats, i);
10808c2ecf20Sopenharmony_ci		do {
10818c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&p->syncp);
10828c2ecf20Sopenharmony_ci			local = p->stats;
10838c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&p->syncp, start));
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		stats->rx_packets	+= local.rx_packets;
10868c2ecf20Sopenharmony_ci		stats->rx_bytes		+= local.rx_bytes;
10878c2ecf20Sopenharmony_ci		stats->tx_packets	+= local.tx_packets;
10888c2ecf20Sopenharmony_ci		stats->tx_bytes		+= local.tx_bytes;
10898c2ecf20Sopenharmony_ci		stats->rx_errors	+= local.rx_errors;
10908c2ecf20Sopenharmony_ci		stats->tx_errors	+= local.tx_errors;
10918c2ecf20Sopenharmony_ci		stats->rx_dropped	+= local.rx_dropped;
10928c2ecf20Sopenharmony_ci		stats->tx_dropped	+= local.tx_dropped;
10938c2ecf20Sopenharmony_ci		stats->rx_noroute	+= local.rx_noroute;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic int mpls_fill_stats_af(struct sk_buff *skb,
10988c2ecf20Sopenharmony_ci			      const struct net_device *dev)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	struct mpls_link_stats *stats;
11018c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
11028c2ecf20Sopenharmony_ci	struct nlattr *nla;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	mdev = mpls_dev_get(dev);
11058c2ecf20Sopenharmony_ci	if (!mdev)
11068c2ecf20Sopenharmony_ci		return -ENODATA;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	nla = nla_reserve_64bit(skb, MPLS_STATS_LINK,
11098c2ecf20Sopenharmony_ci				sizeof(struct mpls_link_stats),
11108c2ecf20Sopenharmony_ci				MPLS_STATS_UNSPEC);
11118c2ecf20Sopenharmony_ci	if (!nla)
11128c2ecf20Sopenharmony_ci		return -EMSGSIZE;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	stats = nla_data(nla);
11158c2ecf20Sopenharmony_ci	mpls_get_stats(mdev, stats);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	return 0;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic size_t mpls_get_stats_af_size(const struct net_device *dev)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	mdev = mpls_dev_get(dev);
11258c2ecf20Sopenharmony_ci	if (!mdev)
11268c2ecf20Sopenharmony_ci		return 0;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	return nla_total_size_64bit(sizeof(struct mpls_link_stats));
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic int mpls_netconf_fill_devconf(struct sk_buff *skb, struct mpls_dev *mdev,
11328c2ecf20Sopenharmony_ci				     u32 portid, u32 seq, int event,
11338c2ecf20Sopenharmony_ci				     unsigned int flags, int type)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct nlmsghdr  *nlh;
11368c2ecf20Sopenharmony_ci	struct netconfmsg *ncm;
11378c2ecf20Sopenharmony_ci	bool all = false;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
11408c2ecf20Sopenharmony_ci			flags);
11418c2ecf20Sopenharmony_ci	if (!nlh)
11428c2ecf20Sopenharmony_ci		return -EMSGSIZE;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	if (type == NETCONFA_ALL)
11458c2ecf20Sopenharmony_ci		all = true;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	ncm = nlmsg_data(nlh);
11488c2ecf20Sopenharmony_ci	ncm->ncm_family = AF_MPLS;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	if (nla_put_s32(skb, NETCONFA_IFINDEX, mdev->dev->ifindex) < 0)
11518c2ecf20Sopenharmony_ci		goto nla_put_failure;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if ((all || type == NETCONFA_INPUT) &&
11548c2ecf20Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_INPUT,
11558c2ecf20Sopenharmony_ci			mdev->input_enabled) < 0)
11568c2ecf20Sopenharmony_ci		goto nla_put_failure;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
11598c2ecf20Sopenharmony_ci	return 0;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_cinla_put_failure:
11628c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
11638c2ecf20Sopenharmony_ci	return -EMSGSIZE;
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cistatic int mpls_netconf_msgsize_devconf(int type)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
11698c2ecf20Sopenharmony_ci			+ nla_total_size(4); /* NETCONFA_IFINDEX */
11708c2ecf20Sopenharmony_ci	bool all = false;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	if (type == NETCONFA_ALL)
11738c2ecf20Sopenharmony_ci		all = true;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	if (all || type == NETCONFA_INPUT)
11768c2ecf20Sopenharmony_ci		size += nla_total_size(4);
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	return size;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cistatic void mpls_netconf_notify_devconf(struct net *net, int event,
11828c2ecf20Sopenharmony_ci					int type, struct mpls_dev *mdev)
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11858c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	skb = nlmsg_new(mpls_netconf_msgsize_devconf(type), GFP_KERNEL);
11888c2ecf20Sopenharmony_ci	if (!skb)
11898c2ecf20Sopenharmony_ci		goto errout;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, event, 0, type);
11928c2ecf20Sopenharmony_ci	if (err < 0) {
11938c2ecf20Sopenharmony_ci		/* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */
11948c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
11958c2ecf20Sopenharmony_ci		kfree_skb(skb);
11968c2ecf20Sopenharmony_ci		goto errout;
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_MPLS_NETCONF, NULL, GFP_KERNEL);
12008c2ecf20Sopenharmony_ci	return;
12018c2ecf20Sopenharmony_cierrout:
12028c2ecf20Sopenharmony_ci	if (err < 0)
12038c2ecf20Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_MPLS_NETCONF, err);
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic const struct nla_policy devconf_mpls_policy[NETCONFA_MAX + 1] = {
12078c2ecf20Sopenharmony_ci	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
12088c2ecf20Sopenharmony_ci};
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_cistatic int mpls_netconf_valid_get_req(struct sk_buff *skb,
12118c2ecf20Sopenharmony_ci				      const struct nlmsghdr *nlh,
12128c2ecf20Sopenharmony_ci				      struct nlattr **tb,
12138c2ecf20Sopenharmony_ci				      struct netlink_ext_ack *extack)
12148c2ecf20Sopenharmony_ci{
12158c2ecf20Sopenharmony_ci	int i, err;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
12188c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
12198c2ecf20Sopenharmony_ci				   "Invalid header for netconf get request");
12208c2ecf20Sopenharmony_ci		return -EINVAL;
12218c2ecf20Sopenharmony_ci	}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (!netlink_strict_get_check(skb))
12248c2ecf20Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
12258c2ecf20Sopenharmony_ci					      tb, NETCONFA_MAX,
12268c2ecf20Sopenharmony_ci					      devconf_mpls_policy, extack);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
12298c2ecf20Sopenharmony_ci					    tb, NETCONFA_MAX,
12308c2ecf20Sopenharmony_ci					    devconf_mpls_policy, extack);
12318c2ecf20Sopenharmony_ci	if (err)
12328c2ecf20Sopenharmony_ci		return err;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	for (i = 0; i <= NETCONFA_MAX; i++) {
12358c2ecf20Sopenharmony_ci		if (!tb[i])
12368c2ecf20Sopenharmony_ci			continue;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		switch (i) {
12398c2ecf20Sopenharmony_ci		case NETCONFA_IFINDEX:
12408c2ecf20Sopenharmony_ci			break;
12418c2ecf20Sopenharmony_ci		default:
12428c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request");
12438c2ecf20Sopenharmony_ci			return -EINVAL;
12448c2ecf20Sopenharmony_ci		}
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	return 0;
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic int mpls_netconf_get_devconf(struct sk_buff *in_skb,
12518c2ecf20Sopenharmony_ci				    struct nlmsghdr *nlh,
12528c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
12558c2ecf20Sopenharmony_ci	struct nlattr *tb[NETCONFA_MAX + 1];
12568c2ecf20Sopenharmony_ci	struct net_device *dev;
12578c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
12588c2ecf20Sopenharmony_ci	struct sk_buff *skb;
12598c2ecf20Sopenharmony_ci	int ifindex;
12608c2ecf20Sopenharmony_ci	int err;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	err = mpls_netconf_valid_get_req(in_skb, nlh, tb, extack);
12638c2ecf20Sopenharmony_ci	if (err < 0)
12648c2ecf20Sopenharmony_ci		goto errout;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	err = -EINVAL;
12678c2ecf20Sopenharmony_ci	if (!tb[NETCONFA_IFINDEX])
12688c2ecf20Sopenharmony_ci		goto errout;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
12718c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(net, ifindex);
12728c2ecf20Sopenharmony_ci	if (!dev)
12738c2ecf20Sopenharmony_ci		goto errout;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	mdev = mpls_dev_get(dev);
12768c2ecf20Sopenharmony_ci	if (!mdev)
12778c2ecf20Sopenharmony_ci		goto errout;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	err = -ENOBUFS;
12808c2ecf20Sopenharmony_ci	skb = nlmsg_new(mpls_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
12818c2ecf20Sopenharmony_ci	if (!skb)
12828c2ecf20Sopenharmony_ci		goto errout;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	err = mpls_netconf_fill_devconf(skb, mdev,
12858c2ecf20Sopenharmony_ci					NETLINK_CB(in_skb).portid,
12868c2ecf20Sopenharmony_ci					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
12878c2ecf20Sopenharmony_ci					NETCONFA_ALL);
12888c2ecf20Sopenharmony_ci	if (err < 0) {
12898c2ecf20Sopenharmony_ci		/* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */
12908c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
12918c2ecf20Sopenharmony_ci		kfree_skb(skb);
12928c2ecf20Sopenharmony_ci		goto errout;
12938c2ecf20Sopenharmony_ci	}
12948c2ecf20Sopenharmony_ci	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
12958c2ecf20Sopenharmony_cierrout:
12968c2ecf20Sopenharmony_ci	return err;
12978c2ecf20Sopenharmony_ci}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_cistatic int mpls_netconf_dump_devconf(struct sk_buff *skb,
13008c2ecf20Sopenharmony_ci				     struct netlink_callback *cb)
13018c2ecf20Sopenharmony_ci{
13028c2ecf20Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
13038c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
13048c2ecf20Sopenharmony_ci	struct hlist_head *head;
13058c2ecf20Sopenharmony_ci	struct net_device *dev;
13068c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
13078c2ecf20Sopenharmony_ci	int idx, s_idx;
13088c2ecf20Sopenharmony_ci	int h, s_h;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	if (cb->strict_check) {
13118c2ecf20Sopenharmony_ci		struct netlink_ext_ack *extack = cb->extack;
13128c2ecf20Sopenharmony_ci		struct netconfmsg *ncm;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
13158c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request");
13168c2ecf20Sopenharmony_ci			return -EINVAL;
13178c2ecf20Sopenharmony_ci		}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
13208c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request");
13218c2ecf20Sopenharmony_ci			return -EINVAL;
13228c2ecf20Sopenharmony_ci		}
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	s_h = cb->args[0];
13268c2ecf20Sopenharmony_ci	s_idx = idx = cb->args[1];
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
13298c2ecf20Sopenharmony_ci		idx = 0;
13308c2ecf20Sopenharmony_ci		head = &net->dev_index_head[h];
13318c2ecf20Sopenharmony_ci		rcu_read_lock();
13328c2ecf20Sopenharmony_ci		cb->seq = net->dev_base_seq;
13338c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
13348c2ecf20Sopenharmony_ci			if (idx < s_idx)
13358c2ecf20Sopenharmony_ci				goto cont;
13368c2ecf20Sopenharmony_ci			mdev = mpls_dev_get(dev);
13378c2ecf20Sopenharmony_ci			if (!mdev)
13388c2ecf20Sopenharmony_ci				goto cont;
13398c2ecf20Sopenharmony_ci			if (mpls_netconf_fill_devconf(skb, mdev,
13408c2ecf20Sopenharmony_ci						      NETLINK_CB(cb->skb).portid,
13418c2ecf20Sopenharmony_ci						      nlh->nlmsg_seq,
13428c2ecf20Sopenharmony_ci						      RTM_NEWNETCONF,
13438c2ecf20Sopenharmony_ci						      NLM_F_MULTI,
13448c2ecf20Sopenharmony_ci						      NETCONFA_ALL) < 0) {
13458c2ecf20Sopenharmony_ci				rcu_read_unlock();
13468c2ecf20Sopenharmony_ci				goto done;
13478c2ecf20Sopenharmony_ci			}
13488c2ecf20Sopenharmony_ci			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
13498c2ecf20Sopenharmony_cicont:
13508c2ecf20Sopenharmony_ci			idx++;
13518c2ecf20Sopenharmony_ci		}
13528c2ecf20Sopenharmony_ci		rcu_read_unlock();
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_cidone:
13558c2ecf20Sopenharmony_ci	cb->args[0] = h;
13568c2ecf20Sopenharmony_ci	cb->args[1] = idx;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	return skb->len;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci#define MPLS_PERDEV_SYSCTL_OFFSET(field)	\
13628c2ecf20Sopenharmony_ci	(&((struct mpls_dev *)0)->field)
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_cistatic int mpls_conf_proc(struct ctl_table *ctl, int write,
13658c2ecf20Sopenharmony_ci			  void *buffer, size_t *lenp, loff_t *ppos)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	int oval = *(int *)ctl->data;
13688c2ecf20Sopenharmony_ci	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (write) {
13718c2ecf20Sopenharmony_ci		struct mpls_dev *mdev = ctl->extra1;
13728c2ecf20Sopenharmony_ci		int i = (int *)ctl->data - (int *)mdev;
13738c2ecf20Sopenharmony_ci		struct net *net = ctl->extra2;
13748c2ecf20Sopenharmony_ci		int val = *(int *)ctl->data;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		if (i == offsetof(struct mpls_dev, input_enabled) &&
13778c2ecf20Sopenharmony_ci		    val != oval) {
13788c2ecf20Sopenharmony_ci			mpls_netconf_notify_devconf(net, RTM_NEWNETCONF,
13798c2ecf20Sopenharmony_ci						    NETCONFA_INPUT, mdev);
13808c2ecf20Sopenharmony_ci		}
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	return ret;
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic const struct ctl_table mpls_dev_table[] = {
13878c2ecf20Sopenharmony_ci	{
13888c2ecf20Sopenharmony_ci		.procname	= "input",
13898c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
13908c2ecf20Sopenharmony_ci		.mode		= 0644,
13918c2ecf20Sopenharmony_ci		.proc_handler	= mpls_conf_proc,
13928c2ecf20Sopenharmony_ci		.data		= MPLS_PERDEV_SYSCTL_OFFSET(input_enabled),
13938c2ecf20Sopenharmony_ci	},
13948c2ecf20Sopenharmony_ci	{ }
13958c2ecf20Sopenharmony_ci};
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_cistatic int mpls_dev_sysctl_register(struct net_device *dev,
13988c2ecf20Sopenharmony_ci				    struct mpls_dev *mdev)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	char path[sizeof("net/mpls/conf/") + IFNAMSIZ];
14018c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
14028c2ecf20Sopenharmony_ci	struct ctl_table *table;
14038c2ecf20Sopenharmony_ci	int i;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL);
14068c2ecf20Sopenharmony_ci	if (!table)
14078c2ecf20Sopenharmony_ci		goto out;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	/* Table data contains only offsets relative to the base of
14108c2ecf20Sopenharmony_ci	 * the mdev at this point, so make them absolute.
14118c2ecf20Sopenharmony_ci	 */
14128c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) {
14138c2ecf20Sopenharmony_ci		table[i].data = (char *)mdev + (uintptr_t)table[i].data;
14148c2ecf20Sopenharmony_ci		table[i].extra1 = mdev;
14158c2ecf20Sopenharmony_ci		table[i].extra2 = net;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	mdev->sysctl = register_net_sysctl(net, path, table);
14218c2ecf20Sopenharmony_ci	if (!mdev->sysctl)
14228c2ecf20Sopenharmony_ci		goto free;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev);
14258c2ecf20Sopenharmony_ci	return 0;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_cifree:
14288c2ecf20Sopenharmony_ci	kfree(table);
14298c2ecf20Sopenharmony_ciout:
14308c2ecf20Sopenharmony_ci	mdev->sysctl = NULL;
14318c2ecf20Sopenharmony_ci	return -ENOBUFS;
14328c2ecf20Sopenharmony_ci}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_cistatic void mpls_dev_sysctl_unregister(struct net_device *dev,
14358c2ecf20Sopenharmony_ci				       struct mpls_dev *mdev)
14368c2ecf20Sopenharmony_ci{
14378c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
14388c2ecf20Sopenharmony_ci	struct ctl_table *table;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	if (!mdev->sysctl)
14418c2ecf20Sopenharmony_ci		return;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	table = mdev->sysctl->ctl_table_arg;
14448c2ecf20Sopenharmony_ci	unregister_net_sysctl_table(mdev->sysctl);
14458c2ecf20Sopenharmony_ci	kfree(table);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev);
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_cistatic struct mpls_dev *mpls_add_dev(struct net_device *dev)
14518c2ecf20Sopenharmony_ci{
14528c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
14538c2ecf20Sopenharmony_ci	int err = -ENOMEM;
14548c2ecf20Sopenharmony_ci	int i;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	ASSERT_RTNL();
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
14598c2ecf20Sopenharmony_ci	if (!mdev)
14608c2ecf20Sopenharmony_ci		return ERR_PTR(err);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	mdev->stats = alloc_percpu(struct mpls_pcpu_stats);
14638c2ecf20Sopenharmony_ci	if (!mdev->stats)
14648c2ecf20Sopenharmony_ci		goto free;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
14678c2ecf20Sopenharmony_ci		struct mpls_pcpu_stats *mpls_stats;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		mpls_stats = per_cpu_ptr(mdev->stats, i);
14708c2ecf20Sopenharmony_ci		u64_stats_init(&mpls_stats->syncp);
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	mdev->dev = dev;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	err = mpls_dev_sysctl_register(dev, mdev);
14768c2ecf20Sopenharmony_ci	if (err)
14778c2ecf20Sopenharmony_ci		goto free;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	rcu_assign_pointer(dev->mpls_ptr, mdev);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	return mdev;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cifree:
14848c2ecf20Sopenharmony_ci	free_percpu(mdev->stats);
14858c2ecf20Sopenharmony_ci	kfree(mdev);
14868c2ecf20Sopenharmony_ci	return ERR_PTR(err);
14878c2ecf20Sopenharmony_ci}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_cistatic void mpls_dev_destroy_rcu(struct rcu_head *head)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct mpls_dev *mdev = container_of(head, struct mpls_dev, rcu);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	free_percpu(mdev->stats);
14948c2ecf20Sopenharmony_ci	kfree(mdev);
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic int mpls_ifdown(struct net_device *dev, int event)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
15008c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
15018c2ecf20Sopenharmony_ci	unsigned index;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
15048c2ecf20Sopenharmony_ci	for (index = 0; index < net->mpls.platform_labels; index++) {
15058c2ecf20Sopenharmony_ci		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
15068c2ecf20Sopenharmony_ci		bool nh_del = false;
15078c2ecf20Sopenharmony_ci		u8 alive = 0;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci		if (!rt)
15108c2ecf20Sopenharmony_ci			continue;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci		if (event == NETDEV_UNREGISTER) {
15138c2ecf20Sopenharmony_ci			u8 deleted = 0;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci			for_nexthops(rt) {
15168c2ecf20Sopenharmony_ci				struct net_device *nh_dev =
15178c2ecf20Sopenharmony_ci					rtnl_dereference(nh->nh_dev);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci				if (!nh_dev || nh_dev == dev)
15208c2ecf20Sopenharmony_ci					deleted++;
15218c2ecf20Sopenharmony_ci				if (nh_dev == dev)
15228c2ecf20Sopenharmony_ci					nh_del = true;
15238c2ecf20Sopenharmony_ci			} endfor_nexthops(rt);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci			/* if there are no more nexthops, delete the route */
15268c2ecf20Sopenharmony_ci			if (deleted == rt->rt_nhn) {
15278c2ecf20Sopenharmony_ci				mpls_route_update(net, index, NULL, NULL);
15288c2ecf20Sopenharmony_ci				continue;
15298c2ecf20Sopenharmony_ci			}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci			if (nh_del) {
15328c2ecf20Sopenharmony_ci				size_t size = sizeof(*rt) + rt->rt_nhn *
15338c2ecf20Sopenharmony_ci					rt->rt_nh_size;
15348c2ecf20Sopenharmony_ci				struct mpls_route *orig = rt;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci				rt = kmalloc(size, GFP_KERNEL);
15378c2ecf20Sopenharmony_ci				if (!rt)
15388c2ecf20Sopenharmony_ci					return -ENOMEM;
15398c2ecf20Sopenharmony_ci				memcpy(rt, orig, size);
15408c2ecf20Sopenharmony_ci			}
15418c2ecf20Sopenharmony_ci		}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		change_nexthops(rt) {
15448c2ecf20Sopenharmony_ci			unsigned int nh_flags = nh->nh_flags;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci			if (rtnl_dereference(nh->nh_dev) != dev)
15478c2ecf20Sopenharmony_ci				goto next;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci			switch (event) {
15508c2ecf20Sopenharmony_ci			case NETDEV_DOWN:
15518c2ecf20Sopenharmony_ci			case NETDEV_UNREGISTER:
15528c2ecf20Sopenharmony_ci				nh_flags |= RTNH_F_DEAD;
15538c2ecf20Sopenharmony_ci				fallthrough;
15548c2ecf20Sopenharmony_ci			case NETDEV_CHANGE:
15558c2ecf20Sopenharmony_ci				nh_flags |= RTNH_F_LINKDOWN;
15568c2ecf20Sopenharmony_ci				break;
15578c2ecf20Sopenharmony_ci			}
15588c2ecf20Sopenharmony_ci			if (event == NETDEV_UNREGISTER)
15598c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(nh->nh_dev, NULL);
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci			if (nh->nh_flags != nh_flags)
15628c2ecf20Sopenharmony_ci				WRITE_ONCE(nh->nh_flags, nh_flags);
15638c2ecf20Sopenharmony_cinext:
15648c2ecf20Sopenharmony_ci			if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
15658c2ecf20Sopenharmony_ci				alive++;
15668c2ecf20Sopenharmony_ci		} endfor_nexthops(rt);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci		WRITE_ONCE(rt->rt_nhn_alive, alive);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		if (nh_del)
15718c2ecf20Sopenharmony_ci			mpls_route_update(net, index, rt, NULL);
15728c2ecf20Sopenharmony_ci	}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	return 0;
15758c2ecf20Sopenharmony_ci}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_cistatic void mpls_ifup(struct net_device *dev, unsigned int flags)
15788c2ecf20Sopenharmony_ci{
15798c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
15808c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
15818c2ecf20Sopenharmony_ci	unsigned index;
15828c2ecf20Sopenharmony_ci	u8 alive;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
15858c2ecf20Sopenharmony_ci	for (index = 0; index < net->mpls.platform_labels; index++) {
15868c2ecf20Sopenharmony_ci		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci		if (!rt)
15898c2ecf20Sopenharmony_ci			continue;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci		alive = 0;
15928c2ecf20Sopenharmony_ci		change_nexthops(rt) {
15938c2ecf20Sopenharmony_ci			unsigned int nh_flags = nh->nh_flags;
15948c2ecf20Sopenharmony_ci			struct net_device *nh_dev =
15958c2ecf20Sopenharmony_ci				rtnl_dereference(nh->nh_dev);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci			if (!(nh_flags & flags)) {
15988c2ecf20Sopenharmony_ci				alive++;
15998c2ecf20Sopenharmony_ci				continue;
16008c2ecf20Sopenharmony_ci			}
16018c2ecf20Sopenharmony_ci			if (nh_dev != dev)
16028c2ecf20Sopenharmony_ci				continue;
16038c2ecf20Sopenharmony_ci			alive++;
16048c2ecf20Sopenharmony_ci			nh_flags &= ~flags;
16058c2ecf20Sopenharmony_ci			WRITE_ONCE(nh->nh_flags, nh_flags);
16068c2ecf20Sopenharmony_ci		} endfor_nexthops(rt);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		WRITE_ONCE(rt->rt_nhn_alive, alive);
16098c2ecf20Sopenharmony_ci	}
16108c2ecf20Sopenharmony_ci}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_cistatic int mpls_dev_notify(struct notifier_block *this, unsigned long event,
16138c2ecf20Sopenharmony_ci			   void *ptr)
16148c2ecf20Sopenharmony_ci{
16158c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
16168c2ecf20Sopenharmony_ci	struct mpls_dev *mdev;
16178c2ecf20Sopenharmony_ci	unsigned int flags;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	if (event == NETDEV_REGISTER) {
16208c2ecf20Sopenharmony_ci		mdev = mpls_add_dev(dev);
16218c2ecf20Sopenharmony_ci		if (IS_ERR(mdev))
16228c2ecf20Sopenharmony_ci			return notifier_from_errno(PTR_ERR(mdev));
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		return NOTIFY_OK;
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	mdev = mpls_dev_get(dev);
16288c2ecf20Sopenharmony_ci	if (!mdev)
16298c2ecf20Sopenharmony_ci		return NOTIFY_OK;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	switch (event) {
16328c2ecf20Sopenharmony_ci		int err;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
16358c2ecf20Sopenharmony_ci		err = mpls_ifdown(dev, event);
16368c2ecf20Sopenharmony_ci		if (err)
16378c2ecf20Sopenharmony_ci			return notifier_from_errno(err);
16388c2ecf20Sopenharmony_ci		break;
16398c2ecf20Sopenharmony_ci	case NETDEV_UP:
16408c2ecf20Sopenharmony_ci		flags = dev_get_flags(dev);
16418c2ecf20Sopenharmony_ci		if (flags & (IFF_RUNNING | IFF_LOWER_UP))
16428c2ecf20Sopenharmony_ci			mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
16438c2ecf20Sopenharmony_ci		else
16448c2ecf20Sopenharmony_ci			mpls_ifup(dev, RTNH_F_DEAD);
16458c2ecf20Sopenharmony_ci		break;
16468c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
16478c2ecf20Sopenharmony_ci		flags = dev_get_flags(dev);
16488c2ecf20Sopenharmony_ci		if (flags & (IFF_RUNNING | IFF_LOWER_UP)) {
16498c2ecf20Sopenharmony_ci			mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
16508c2ecf20Sopenharmony_ci		} else {
16518c2ecf20Sopenharmony_ci			err = mpls_ifdown(dev, event);
16528c2ecf20Sopenharmony_ci			if (err)
16538c2ecf20Sopenharmony_ci				return notifier_from_errno(err);
16548c2ecf20Sopenharmony_ci		}
16558c2ecf20Sopenharmony_ci		break;
16568c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
16578c2ecf20Sopenharmony_ci		err = mpls_ifdown(dev, event);
16588c2ecf20Sopenharmony_ci		if (err)
16598c2ecf20Sopenharmony_ci			return notifier_from_errno(err);
16608c2ecf20Sopenharmony_ci		mdev = mpls_dev_get(dev);
16618c2ecf20Sopenharmony_ci		if (mdev) {
16628c2ecf20Sopenharmony_ci			mpls_dev_sysctl_unregister(dev, mdev);
16638c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(dev->mpls_ptr, NULL);
16648c2ecf20Sopenharmony_ci			call_rcu(&mdev->rcu, mpls_dev_destroy_rcu);
16658c2ecf20Sopenharmony_ci		}
16668c2ecf20Sopenharmony_ci		break;
16678c2ecf20Sopenharmony_ci	case NETDEV_CHANGENAME:
16688c2ecf20Sopenharmony_ci		mdev = mpls_dev_get(dev);
16698c2ecf20Sopenharmony_ci		if (mdev) {
16708c2ecf20Sopenharmony_ci			mpls_dev_sysctl_unregister(dev, mdev);
16718c2ecf20Sopenharmony_ci			err = mpls_dev_sysctl_register(dev, mdev);
16728c2ecf20Sopenharmony_ci			if (err)
16738c2ecf20Sopenharmony_ci				return notifier_from_errno(err);
16748c2ecf20Sopenharmony_ci		}
16758c2ecf20Sopenharmony_ci		break;
16768c2ecf20Sopenharmony_ci	}
16778c2ecf20Sopenharmony_ci	return NOTIFY_OK;
16788c2ecf20Sopenharmony_ci}
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_cistatic struct notifier_block mpls_dev_notifier = {
16818c2ecf20Sopenharmony_ci	.notifier_call = mpls_dev_notify,
16828c2ecf20Sopenharmony_ci};
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic int nla_put_via(struct sk_buff *skb,
16858c2ecf20Sopenharmony_ci		       u8 table, const void *addr, int alen)
16868c2ecf20Sopenharmony_ci{
16878c2ecf20Sopenharmony_ci	static const int table_to_family[NEIGH_NR_TABLES + 1] = {
16888c2ecf20Sopenharmony_ci		AF_INET, AF_INET6, AF_DECnet, AF_PACKET,
16898c2ecf20Sopenharmony_ci	};
16908c2ecf20Sopenharmony_ci	struct nlattr *nla;
16918c2ecf20Sopenharmony_ci	struct rtvia *via;
16928c2ecf20Sopenharmony_ci	int family = AF_UNSPEC;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	nla = nla_reserve(skb, RTA_VIA, alen + 2);
16958c2ecf20Sopenharmony_ci	if (!nla)
16968c2ecf20Sopenharmony_ci		return -EMSGSIZE;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (table <= NEIGH_NR_TABLES)
16998c2ecf20Sopenharmony_ci		family = table_to_family[table];
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	via = nla_data(nla);
17028c2ecf20Sopenharmony_ci	via->rtvia_family = family;
17038c2ecf20Sopenharmony_ci	memcpy(via->rtvia_addr, addr, alen);
17048c2ecf20Sopenharmony_ci	return 0;
17058c2ecf20Sopenharmony_ci}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ciint nla_put_labels(struct sk_buff *skb, int attrtype,
17088c2ecf20Sopenharmony_ci		   u8 labels, const u32 label[])
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	struct nlattr *nla;
17118c2ecf20Sopenharmony_ci	struct mpls_shim_hdr *nla_label;
17128c2ecf20Sopenharmony_ci	bool bos;
17138c2ecf20Sopenharmony_ci	int i;
17148c2ecf20Sopenharmony_ci	nla = nla_reserve(skb, attrtype, labels*4);
17158c2ecf20Sopenharmony_ci	if (!nla)
17168c2ecf20Sopenharmony_ci		return -EMSGSIZE;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	nla_label = nla_data(nla);
17198c2ecf20Sopenharmony_ci	bos = true;
17208c2ecf20Sopenharmony_ci	for (i = labels - 1; i >= 0; i--) {
17218c2ecf20Sopenharmony_ci		nla_label[i] = mpls_entry_encode(label[i], 0, 0, bos);
17228c2ecf20Sopenharmony_ci		bos = false;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	return 0;
17268c2ecf20Sopenharmony_ci}
17278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nla_put_labels);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ciint nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels,
17308c2ecf20Sopenharmony_ci		   u32 label[], struct netlink_ext_ack *extack)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	unsigned len = nla_len(nla);
17338c2ecf20Sopenharmony_ci	struct mpls_shim_hdr *nla_label;
17348c2ecf20Sopenharmony_ci	u8 nla_labels;
17358c2ecf20Sopenharmony_ci	bool bos;
17368c2ecf20Sopenharmony_ci	int i;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	/* len needs to be an even multiple of 4 (the label size). Number
17398c2ecf20Sopenharmony_ci	 * of labels is a u8 so check for overflow.
17408c2ecf20Sopenharmony_ci	 */
17418c2ecf20Sopenharmony_ci	if (len & 3 || len / 4 > 255) {
17428c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nla,
17438c2ecf20Sopenharmony_ci				    "Invalid length for labels attribute");
17448c2ecf20Sopenharmony_ci		return -EINVAL;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	/* Limit the number of new labels allowed */
17488c2ecf20Sopenharmony_ci	nla_labels = len/4;
17498c2ecf20Sopenharmony_ci	if (nla_labels > max_labels) {
17508c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Too many labels");
17518c2ecf20Sopenharmony_ci		return -EINVAL;
17528c2ecf20Sopenharmony_ci	}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	/* when label == NULL, caller wants number of labels */
17558c2ecf20Sopenharmony_ci	if (!label)
17568c2ecf20Sopenharmony_ci		goto out;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	nla_label = nla_data(nla);
17598c2ecf20Sopenharmony_ci	bos = true;
17608c2ecf20Sopenharmony_ci	for (i = nla_labels - 1; i >= 0; i--, bos = false) {
17618c2ecf20Sopenharmony_ci		struct mpls_entry_decoded dec;
17628c2ecf20Sopenharmony_ci		dec = mpls_entry_decode(nla_label + i);
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci		/* Ensure the bottom of stack flag is properly set
17658c2ecf20Sopenharmony_ci		 * and ttl and tc are both clear.
17668c2ecf20Sopenharmony_ci		 */
17678c2ecf20Sopenharmony_ci		if (dec.ttl) {
17688c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, nla,
17698c2ecf20Sopenharmony_ci					    "TTL in label must be 0");
17708c2ecf20Sopenharmony_ci			return -EINVAL;
17718c2ecf20Sopenharmony_ci		}
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci		if (dec.tc) {
17748c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, nla,
17758c2ecf20Sopenharmony_ci					    "Traffic class in label must be 0");
17768c2ecf20Sopenharmony_ci			return -EINVAL;
17778c2ecf20Sopenharmony_ci		}
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci		if (dec.bos != bos) {
17808c2ecf20Sopenharmony_ci			NL_SET_BAD_ATTR(extack, nla);
17818c2ecf20Sopenharmony_ci			if (bos) {
17828c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack,
17838c2ecf20Sopenharmony_ci					       "BOS bit must be set in first label");
17848c2ecf20Sopenharmony_ci			} else {
17858c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG(extack,
17868c2ecf20Sopenharmony_ci					       "BOS bit can only be set in first label");
17878c2ecf20Sopenharmony_ci			}
17888c2ecf20Sopenharmony_ci			return -EINVAL;
17898c2ecf20Sopenharmony_ci		}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci		switch (dec.label) {
17928c2ecf20Sopenharmony_ci		case MPLS_LABEL_IMPLNULL:
17938c2ecf20Sopenharmony_ci			/* RFC3032: This is a label that an LSR may
17948c2ecf20Sopenharmony_ci			 * assign and distribute, but which never
17958c2ecf20Sopenharmony_ci			 * actually appears in the encapsulation.
17968c2ecf20Sopenharmony_ci			 */
17978c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, nla,
17988c2ecf20Sopenharmony_ci					    "Implicit NULL Label (3) can not be used in encapsulation");
17998c2ecf20Sopenharmony_ci			return -EINVAL;
18008c2ecf20Sopenharmony_ci		}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci		label[i] = dec.label;
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ciout:
18058c2ecf20Sopenharmony_ci	*labels = nla_labels;
18068c2ecf20Sopenharmony_ci	return 0;
18078c2ecf20Sopenharmony_ci}
18088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nla_get_labels);
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_cistatic int rtm_to_route_config(struct sk_buff *skb,
18118c2ecf20Sopenharmony_ci			       struct nlmsghdr *nlh,
18128c2ecf20Sopenharmony_ci			       struct mpls_route_config *cfg,
18138c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
18148c2ecf20Sopenharmony_ci{
18158c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
18168c2ecf20Sopenharmony_ci	struct nlattr *tb[RTA_MAX+1];
18178c2ecf20Sopenharmony_ci	int index;
18188c2ecf20Sopenharmony_ci	int err;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
18218c2ecf20Sopenharmony_ci				     rtm_mpls_policy, extack);
18228c2ecf20Sopenharmony_ci	if (err < 0)
18238c2ecf20Sopenharmony_ci		goto errout;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	err = -EINVAL;
18268c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	if (rtm->rtm_family != AF_MPLS) {
18298c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid address family in rtmsg");
18308c2ecf20Sopenharmony_ci		goto errout;
18318c2ecf20Sopenharmony_ci	}
18328c2ecf20Sopenharmony_ci	if (rtm->rtm_dst_len != 20) {
18338c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "rtm_dst_len must be 20 for MPLS");
18348c2ecf20Sopenharmony_ci		goto errout;
18358c2ecf20Sopenharmony_ci	}
18368c2ecf20Sopenharmony_ci	if (rtm->rtm_src_len != 0) {
18378c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "rtm_src_len must be 0 for MPLS");
18388c2ecf20Sopenharmony_ci		goto errout;
18398c2ecf20Sopenharmony_ci	}
18408c2ecf20Sopenharmony_ci	if (rtm->rtm_tos != 0) {
18418c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "rtm_tos must be 0 for MPLS");
18428c2ecf20Sopenharmony_ci		goto errout;
18438c2ecf20Sopenharmony_ci	}
18448c2ecf20Sopenharmony_ci	if (rtm->rtm_table != RT_TABLE_MAIN) {
18458c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
18468c2ecf20Sopenharmony_ci			       "MPLS only supports the main route table");
18478c2ecf20Sopenharmony_ci		goto errout;
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci	/* Any value is acceptable for rtm_protocol */
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	/* As mpls uses destination specific addresses
18528c2ecf20Sopenharmony_ci	 * (or source specific address in the case of multicast)
18538c2ecf20Sopenharmony_ci	 * all addresses have universal scope.
18548c2ecf20Sopenharmony_ci	 */
18558c2ecf20Sopenharmony_ci	if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) {
18568c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
18578c2ecf20Sopenharmony_ci			       "Invalid route scope  - MPLS only supports UNIVERSE");
18588c2ecf20Sopenharmony_ci		goto errout;
18598c2ecf20Sopenharmony_ci	}
18608c2ecf20Sopenharmony_ci	if (rtm->rtm_type != RTN_UNICAST) {
18618c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
18628c2ecf20Sopenharmony_ci			       "Invalid route type - MPLS only supports UNICAST");
18638c2ecf20Sopenharmony_ci		goto errout;
18648c2ecf20Sopenharmony_ci	}
18658c2ecf20Sopenharmony_ci	if (rtm->rtm_flags != 0) {
18668c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "rtm_flags must be 0 for MPLS");
18678c2ecf20Sopenharmony_ci		goto errout;
18688c2ecf20Sopenharmony_ci	}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	cfg->rc_label		= LABEL_NOT_SPECIFIED;
18718c2ecf20Sopenharmony_ci	cfg->rc_protocol	= rtm->rtm_protocol;
18728c2ecf20Sopenharmony_ci	cfg->rc_via_table	= MPLS_NEIGH_TABLE_UNSPEC;
18738c2ecf20Sopenharmony_ci	cfg->rc_ttl_propagate	= MPLS_TTL_PROP_DEFAULT;
18748c2ecf20Sopenharmony_ci	cfg->rc_nlflags		= nlh->nlmsg_flags;
18758c2ecf20Sopenharmony_ci	cfg->rc_nlinfo.portid	= NETLINK_CB(skb).portid;
18768c2ecf20Sopenharmony_ci	cfg->rc_nlinfo.nlh	= nlh;
18778c2ecf20Sopenharmony_ci	cfg->rc_nlinfo.nl_net	= sock_net(skb->sk);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	for (index = 0; index <= RTA_MAX; index++) {
18808c2ecf20Sopenharmony_ci		struct nlattr *nla = tb[index];
18818c2ecf20Sopenharmony_ci		if (!nla)
18828c2ecf20Sopenharmony_ci			continue;
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci		switch (index) {
18858c2ecf20Sopenharmony_ci		case RTA_OIF:
18868c2ecf20Sopenharmony_ci			cfg->rc_ifindex = nla_get_u32(nla);
18878c2ecf20Sopenharmony_ci			break;
18888c2ecf20Sopenharmony_ci		case RTA_NEWDST:
18898c2ecf20Sopenharmony_ci			if (nla_get_labels(nla, MAX_NEW_LABELS,
18908c2ecf20Sopenharmony_ci					   &cfg->rc_output_labels,
18918c2ecf20Sopenharmony_ci					   cfg->rc_output_label, extack))
18928c2ecf20Sopenharmony_ci				goto errout;
18938c2ecf20Sopenharmony_ci			break;
18948c2ecf20Sopenharmony_ci		case RTA_DST:
18958c2ecf20Sopenharmony_ci		{
18968c2ecf20Sopenharmony_ci			u8 label_count;
18978c2ecf20Sopenharmony_ci			if (nla_get_labels(nla, 1, &label_count,
18988c2ecf20Sopenharmony_ci					   &cfg->rc_label, extack))
18998c2ecf20Sopenharmony_ci				goto errout;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci			if (!mpls_label_ok(cfg->rc_nlinfo.nl_net,
19028c2ecf20Sopenharmony_ci					   &cfg->rc_label, extack))
19038c2ecf20Sopenharmony_ci				goto errout;
19048c2ecf20Sopenharmony_ci			break;
19058c2ecf20Sopenharmony_ci		}
19068c2ecf20Sopenharmony_ci		case RTA_GATEWAY:
19078c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "MPLS does not support RTA_GATEWAY attribute");
19088c2ecf20Sopenharmony_ci			goto errout;
19098c2ecf20Sopenharmony_ci		case RTA_VIA:
19108c2ecf20Sopenharmony_ci		{
19118c2ecf20Sopenharmony_ci			if (nla_get_via(nla, &cfg->rc_via_alen,
19128c2ecf20Sopenharmony_ci					&cfg->rc_via_table, cfg->rc_via,
19138c2ecf20Sopenharmony_ci					extack))
19148c2ecf20Sopenharmony_ci				goto errout;
19158c2ecf20Sopenharmony_ci			break;
19168c2ecf20Sopenharmony_ci		}
19178c2ecf20Sopenharmony_ci		case RTA_MULTIPATH:
19188c2ecf20Sopenharmony_ci		{
19198c2ecf20Sopenharmony_ci			cfg->rc_mp = nla_data(nla);
19208c2ecf20Sopenharmony_ci			cfg->rc_mp_len = nla_len(nla);
19218c2ecf20Sopenharmony_ci			break;
19228c2ecf20Sopenharmony_ci		}
19238c2ecf20Sopenharmony_ci		case RTA_TTL_PROPAGATE:
19248c2ecf20Sopenharmony_ci		{
19258c2ecf20Sopenharmony_ci			u8 ttl_propagate = nla_get_u8(nla);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci			if (ttl_propagate > 1) {
19288c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_ATTR(extack, nla,
19298c2ecf20Sopenharmony_ci						    "RTA_TTL_PROPAGATE can only be 0 or 1");
19308c2ecf20Sopenharmony_ci				goto errout;
19318c2ecf20Sopenharmony_ci			}
19328c2ecf20Sopenharmony_ci			cfg->rc_ttl_propagate = ttl_propagate ?
19338c2ecf20Sopenharmony_ci				MPLS_TTL_PROP_ENABLED :
19348c2ecf20Sopenharmony_ci				MPLS_TTL_PROP_DISABLED;
19358c2ecf20Sopenharmony_ci			break;
19368c2ecf20Sopenharmony_ci		}
19378c2ecf20Sopenharmony_ci		default:
19388c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, nla, "Unknown attribute");
19398c2ecf20Sopenharmony_ci			/* Unsupported attribute */
19408c2ecf20Sopenharmony_ci			goto errout;
19418c2ecf20Sopenharmony_ci		}
19428c2ecf20Sopenharmony_ci	}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	err = 0;
19458c2ecf20Sopenharmony_cierrout:
19468c2ecf20Sopenharmony_ci	return err;
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
19508c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	struct mpls_route_config *cfg;
19538c2ecf20Sopenharmony_ci	int err;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
19568c2ecf20Sopenharmony_ci	if (!cfg)
19578c2ecf20Sopenharmony_ci		return -ENOMEM;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	err = rtm_to_route_config(skb, nlh, cfg, extack);
19608c2ecf20Sopenharmony_ci	if (err < 0)
19618c2ecf20Sopenharmony_ci		goto out;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	err = mpls_route_del(cfg, extack);
19648c2ecf20Sopenharmony_ciout:
19658c2ecf20Sopenharmony_ci	kfree(cfg);
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	return err;
19688c2ecf20Sopenharmony_ci}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_cistatic int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
19728c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
19738c2ecf20Sopenharmony_ci{
19748c2ecf20Sopenharmony_ci	struct mpls_route_config *cfg;
19758c2ecf20Sopenharmony_ci	int err;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
19788c2ecf20Sopenharmony_ci	if (!cfg)
19798c2ecf20Sopenharmony_ci		return -ENOMEM;
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	err = rtm_to_route_config(skb, nlh, cfg, extack);
19828c2ecf20Sopenharmony_ci	if (err < 0)
19838c2ecf20Sopenharmony_ci		goto out;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	err = mpls_route_add(cfg, extack);
19868c2ecf20Sopenharmony_ciout:
19878c2ecf20Sopenharmony_ci	kfree(cfg);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	return err;
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_cistatic int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
19938c2ecf20Sopenharmony_ci			   u32 label, struct mpls_route *rt, int flags)
19948c2ecf20Sopenharmony_ci{
19958c2ecf20Sopenharmony_ci	struct net_device *dev;
19968c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
19978c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags);
20008c2ecf20Sopenharmony_ci	if (nlh == NULL)
20018c2ecf20Sopenharmony_ci		return -EMSGSIZE;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
20048c2ecf20Sopenharmony_ci	rtm->rtm_family = AF_MPLS;
20058c2ecf20Sopenharmony_ci	rtm->rtm_dst_len = 20;
20068c2ecf20Sopenharmony_ci	rtm->rtm_src_len = 0;
20078c2ecf20Sopenharmony_ci	rtm->rtm_tos = 0;
20088c2ecf20Sopenharmony_ci	rtm->rtm_table = RT_TABLE_MAIN;
20098c2ecf20Sopenharmony_ci	rtm->rtm_protocol = rt->rt_protocol;
20108c2ecf20Sopenharmony_ci	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
20118c2ecf20Sopenharmony_ci	rtm->rtm_type = RTN_UNICAST;
20128c2ecf20Sopenharmony_ci	rtm->rtm_flags = 0;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (nla_put_labels(skb, RTA_DST, 1, &label))
20158c2ecf20Sopenharmony_ci		goto nla_put_failure;
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) {
20188c2ecf20Sopenharmony_ci		bool ttl_propagate =
20198c2ecf20Sopenharmony_ci			rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, RTA_TTL_PROPAGATE,
20228c2ecf20Sopenharmony_ci			       ttl_propagate))
20238c2ecf20Sopenharmony_ci			goto nla_put_failure;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci	if (rt->rt_nhn == 1) {
20268c2ecf20Sopenharmony_ci		const struct mpls_nh *nh = rt->rt_nh;
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci		if (nh->nh_labels &&
20298c2ecf20Sopenharmony_ci		    nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
20308c2ecf20Sopenharmony_ci				   nh->nh_label))
20318c2ecf20Sopenharmony_ci			goto nla_put_failure;
20328c2ecf20Sopenharmony_ci		if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
20338c2ecf20Sopenharmony_ci		    nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
20348c2ecf20Sopenharmony_ci				nh->nh_via_alen))
20358c2ecf20Sopenharmony_ci			goto nla_put_failure;
20368c2ecf20Sopenharmony_ci		dev = rtnl_dereference(nh->nh_dev);
20378c2ecf20Sopenharmony_ci		if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
20388c2ecf20Sopenharmony_ci			goto nla_put_failure;
20398c2ecf20Sopenharmony_ci		if (nh->nh_flags & RTNH_F_LINKDOWN)
20408c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTNH_F_LINKDOWN;
20418c2ecf20Sopenharmony_ci		if (nh->nh_flags & RTNH_F_DEAD)
20428c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTNH_F_DEAD;
20438c2ecf20Sopenharmony_ci	} else {
20448c2ecf20Sopenharmony_ci		struct rtnexthop *rtnh;
20458c2ecf20Sopenharmony_ci		struct nlattr *mp;
20468c2ecf20Sopenharmony_ci		u8 linkdown = 0;
20478c2ecf20Sopenharmony_ci		u8 dead = 0;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
20508c2ecf20Sopenharmony_ci		if (!mp)
20518c2ecf20Sopenharmony_ci			goto nla_put_failure;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci		for_nexthops(rt) {
20548c2ecf20Sopenharmony_ci			dev = rtnl_dereference(nh->nh_dev);
20558c2ecf20Sopenharmony_ci			if (!dev)
20568c2ecf20Sopenharmony_ci				continue;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci			rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
20598c2ecf20Sopenharmony_ci			if (!rtnh)
20608c2ecf20Sopenharmony_ci				goto nla_put_failure;
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci			rtnh->rtnh_ifindex = dev->ifindex;
20638c2ecf20Sopenharmony_ci			if (nh->nh_flags & RTNH_F_LINKDOWN) {
20648c2ecf20Sopenharmony_ci				rtnh->rtnh_flags |= RTNH_F_LINKDOWN;
20658c2ecf20Sopenharmony_ci				linkdown++;
20668c2ecf20Sopenharmony_ci			}
20678c2ecf20Sopenharmony_ci			if (nh->nh_flags & RTNH_F_DEAD) {
20688c2ecf20Sopenharmony_ci				rtnh->rtnh_flags |= RTNH_F_DEAD;
20698c2ecf20Sopenharmony_ci				dead++;
20708c2ecf20Sopenharmony_ci			}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci			if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST,
20738c2ecf20Sopenharmony_ci							    nh->nh_labels,
20748c2ecf20Sopenharmony_ci							    nh->nh_label))
20758c2ecf20Sopenharmony_ci				goto nla_put_failure;
20768c2ecf20Sopenharmony_ci			if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
20778c2ecf20Sopenharmony_ci			    nla_put_via(skb, nh->nh_via_table,
20788c2ecf20Sopenharmony_ci					mpls_nh_via(rt, nh),
20798c2ecf20Sopenharmony_ci					nh->nh_via_alen))
20808c2ecf20Sopenharmony_ci				goto nla_put_failure;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci			/* length of rtnetlink header + attributes */
20838c2ecf20Sopenharmony_ci			rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
20848c2ecf20Sopenharmony_ci		} endfor_nexthops(rt);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci		if (linkdown == rt->rt_nhn)
20878c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTNH_F_LINKDOWN;
20888c2ecf20Sopenharmony_ci		if (dead == rt->rt_nhn)
20898c2ecf20Sopenharmony_ci			rtm->rtm_flags |= RTNH_F_DEAD;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci		nla_nest_end(skb, mp);
20928c2ecf20Sopenharmony_ci	}
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
20958c2ecf20Sopenharmony_ci	return 0;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_cinla_put_failure:
20988c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
20998c2ecf20Sopenharmony_ci	return -EMSGSIZE;
21008c2ecf20Sopenharmony_ci}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET)
21038c2ecf20Sopenharmony_cistatic int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
21048c2ecf20Sopenharmony_ci				   struct fib_dump_filter *filter,
21058c2ecf20Sopenharmony_ci				   struct netlink_callback *cb)
21068c2ecf20Sopenharmony_ci{
21078c2ecf20Sopenharmony_ci	return ip_valid_fib_dump_req(net, nlh, filter, cb);
21088c2ecf20Sopenharmony_ci}
21098c2ecf20Sopenharmony_ci#else
21108c2ecf20Sopenharmony_cistatic int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
21118c2ecf20Sopenharmony_ci				   struct fib_dump_filter *filter,
21128c2ecf20Sopenharmony_ci				   struct netlink_callback *cb)
21138c2ecf20Sopenharmony_ci{
21148c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack = cb->extack;
21158c2ecf20Sopenharmony_ci	struct nlattr *tb[RTA_MAX + 1];
21168c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
21178c2ecf20Sopenharmony_ci	int err, i;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
21208c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for FIB dump request");
21218c2ecf20Sopenharmony_ci		return -EINVAL;
21228c2ecf20Sopenharmony_ci	}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
21258c2ecf20Sopenharmony_ci	if (rtm->rtm_dst_len || rtm->rtm_src_len  || rtm->rtm_tos   ||
21268c2ecf20Sopenharmony_ci	    rtm->rtm_table   || rtm->rtm_scope    || rtm->rtm_type  ||
21278c2ecf20Sopenharmony_ci	    rtm->rtm_flags) {
21288c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for FIB dump request");
21298c2ecf20Sopenharmony_ci		return -EINVAL;
21308c2ecf20Sopenharmony_ci	}
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	if (rtm->rtm_protocol) {
21338c2ecf20Sopenharmony_ci		filter->protocol = rtm->rtm_protocol;
21348c2ecf20Sopenharmony_ci		filter->filter_set = 1;
21358c2ecf20Sopenharmony_ci		cb->answer_flags = NLM_F_DUMP_FILTERED;
21368c2ecf20Sopenharmony_ci	}
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
21398c2ecf20Sopenharmony_ci					    rtm_mpls_policy, extack);
21408c2ecf20Sopenharmony_ci	if (err < 0)
21418c2ecf20Sopenharmony_ci		return err;
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	for (i = 0; i <= RTA_MAX; ++i) {
21448c2ecf20Sopenharmony_ci		int ifindex;
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci		if (i == RTA_OIF) {
21478c2ecf20Sopenharmony_ci			ifindex = nla_get_u32(tb[i]);
21488c2ecf20Sopenharmony_ci			filter->dev = __dev_get_by_index(net, ifindex);
21498c2ecf20Sopenharmony_ci			if (!filter->dev)
21508c2ecf20Sopenharmony_ci				return -ENODEV;
21518c2ecf20Sopenharmony_ci			filter->filter_set = 1;
21528c2ecf20Sopenharmony_ci		} else if (tb[i]) {
21538c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request");
21548c2ecf20Sopenharmony_ci			return -EINVAL;
21558c2ecf20Sopenharmony_ci		}
21568c2ecf20Sopenharmony_ci	}
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	return 0;
21598c2ecf20Sopenharmony_ci}
21608c2ecf20Sopenharmony_ci#endif
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_cistatic bool mpls_rt_uses_dev(struct mpls_route *rt,
21638c2ecf20Sopenharmony_ci			     const struct net_device *dev)
21648c2ecf20Sopenharmony_ci{
21658c2ecf20Sopenharmony_ci	struct net_device *nh_dev;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	if (rt->rt_nhn == 1) {
21688c2ecf20Sopenharmony_ci		struct mpls_nh *nh = rt->rt_nh;
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci		nh_dev = rtnl_dereference(nh->nh_dev);
21718c2ecf20Sopenharmony_ci		if (dev == nh_dev)
21728c2ecf20Sopenharmony_ci			return true;
21738c2ecf20Sopenharmony_ci	} else {
21748c2ecf20Sopenharmony_ci		for_nexthops(rt) {
21758c2ecf20Sopenharmony_ci			nh_dev = rtnl_dereference(nh->nh_dev);
21768c2ecf20Sopenharmony_ci			if (nh_dev == dev)
21778c2ecf20Sopenharmony_ci				return true;
21788c2ecf20Sopenharmony_ci		} endfor_nexthops(rt);
21798c2ecf20Sopenharmony_ci	}
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	return false;
21828c2ecf20Sopenharmony_ci}
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_cistatic int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
21858c2ecf20Sopenharmony_ci{
21868c2ecf20Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
21878c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
21888c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
21898c2ecf20Sopenharmony_ci	struct fib_dump_filter filter = {};
21908c2ecf20Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
21918c2ecf20Sopenharmony_ci	size_t platform_labels;
21928c2ecf20Sopenharmony_ci	unsigned int index;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	ASSERT_RTNL();
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	if (cb->strict_check) {
21978c2ecf20Sopenharmony_ci		int err;
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci		err = mpls_valid_fib_dump_req(net, nlh, &filter, cb);
22008c2ecf20Sopenharmony_ci		if (err < 0)
22018c2ecf20Sopenharmony_ci			return err;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci		/* for MPLS, there is only 1 table with fixed type and flags.
22048c2ecf20Sopenharmony_ci		 * If either are set in the filter then return nothing.
22058c2ecf20Sopenharmony_ci		 */
22068c2ecf20Sopenharmony_ci		if ((filter.table_id && filter.table_id != RT_TABLE_MAIN) ||
22078c2ecf20Sopenharmony_ci		    (filter.rt_type && filter.rt_type != RTN_UNICAST) ||
22088c2ecf20Sopenharmony_ci		     filter.flags)
22098c2ecf20Sopenharmony_ci			return skb->len;
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	index = cb->args[0];
22138c2ecf20Sopenharmony_ci	if (index < MPLS_LABEL_FIRST_UNRESERVED)
22148c2ecf20Sopenharmony_ci		index = MPLS_LABEL_FIRST_UNRESERVED;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
22178c2ecf20Sopenharmony_ci	platform_labels = net->mpls.platform_labels;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	if (filter.filter_set)
22208c2ecf20Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci	for (; index < platform_labels; index++) {
22238c2ecf20Sopenharmony_ci		struct mpls_route *rt;
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci		rt = rtnl_dereference(platform_label[index]);
22268c2ecf20Sopenharmony_ci		if (!rt)
22278c2ecf20Sopenharmony_ci			continue;
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci		if ((filter.dev && !mpls_rt_uses_dev(rt, filter.dev)) ||
22308c2ecf20Sopenharmony_ci		    (filter.protocol && rt->rt_protocol != filter.protocol))
22318c2ecf20Sopenharmony_ci			continue;
22328c2ecf20Sopenharmony_ci
22338c2ecf20Sopenharmony_ci		if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid,
22348c2ecf20Sopenharmony_ci				    cb->nlh->nlmsg_seq, RTM_NEWROUTE,
22358c2ecf20Sopenharmony_ci				    index, rt, flags) < 0)
22368c2ecf20Sopenharmony_ci			break;
22378c2ecf20Sopenharmony_ci	}
22388c2ecf20Sopenharmony_ci	cb->args[0] = index;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	return skb->len;
22418c2ecf20Sopenharmony_ci}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_cistatic inline size_t lfib_nlmsg_size(struct mpls_route *rt)
22448c2ecf20Sopenharmony_ci{
22458c2ecf20Sopenharmony_ci	size_t payload =
22468c2ecf20Sopenharmony_ci		NLMSG_ALIGN(sizeof(struct rtmsg))
22478c2ecf20Sopenharmony_ci		+ nla_total_size(4)			/* RTA_DST */
22488c2ecf20Sopenharmony_ci		+ nla_total_size(1);			/* RTA_TTL_PROPAGATE */
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	if (rt->rt_nhn == 1) {
22518c2ecf20Sopenharmony_ci		struct mpls_nh *nh = rt->rt_nh;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		if (nh->nh_dev)
22548c2ecf20Sopenharmony_ci			payload += nla_total_size(4); /* RTA_OIF */
22558c2ecf20Sopenharmony_ci		if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */
22568c2ecf20Sopenharmony_ci			payload += nla_total_size(2 + nh->nh_via_alen);
22578c2ecf20Sopenharmony_ci		if (nh->nh_labels) /* RTA_NEWDST */
22588c2ecf20Sopenharmony_ci			payload += nla_total_size(nh->nh_labels * 4);
22598c2ecf20Sopenharmony_ci	} else {
22608c2ecf20Sopenharmony_ci		/* each nexthop is packed in an attribute */
22618c2ecf20Sopenharmony_ci		size_t nhsize = 0;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci		for_nexthops(rt) {
22648c2ecf20Sopenharmony_ci			if (!rtnl_dereference(nh->nh_dev))
22658c2ecf20Sopenharmony_ci				continue;
22668c2ecf20Sopenharmony_ci			nhsize += nla_total_size(sizeof(struct rtnexthop));
22678c2ecf20Sopenharmony_ci			/* RTA_VIA */
22688c2ecf20Sopenharmony_ci			if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC)
22698c2ecf20Sopenharmony_ci				nhsize += nla_total_size(2 + nh->nh_via_alen);
22708c2ecf20Sopenharmony_ci			if (nh->nh_labels)
22718c2ecf20Sopenharmony_ci				nhsize += nla_total_size(nh->nh_labels * 4);
22728c2ecf20Sopenharmony_ci		} endfor_nexthops(rt);
22738c2ecf20Sopenharmony_ci		/* nested attribute */
22748c2ecf20Sopenharmony_ci		payload += nla_total_size(nhsize);
22758c2ecf20Sopenharmony_ci	}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	return payload;
22788c2ecf20Sopenharmony_ci}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_cistatic void rtmsg_lfib(int event, u32 label, struct mpls_route *rt,
22818c2ecf20Sopenharmony_ci		       struct nlmsghdr *nlh, struct net *net, u32 portid,
22828c2ecf20Sopenharmony_ci		       unsigned int nlm_flags)
22838c2ecf20Sopenharmony_ci{
22848c2ecf20Sopenharmony_ci	struct sk_buff *skb;
22858c2ecf20Sopenharmony_ci	u32 seq = nlh ? nlh->nlmsg_seq : 0;
22868c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
22878c2ecf20Sopenharmony_ci
22888c2ecf20Sopenharmony_ci	skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL);
22898c2ecf20Sopenharmony_ci	if (skb == NULL)
22908c2ecf20Sopenharmony_ci		goto errout;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags);
22938c2ecf20Sopenharmony_ci	if (err < 0) {
22948c2ecf20Sopenharmony_ci		/* -EMSGSIZE implies BUG in lfib_nlmsg_size */
22958c2ecf20Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
22968c2ecf20Sopenharmony_ci		kfree_skb(skb);
22978c2ecf20Sopenharmony_ci		goto errout;
22988c2ecf20Sopenharmony_ci	}
22998c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	return;
23028c2ecf20Sopenharmony_cierrout:
23038c2ecf20Sopenharmony_ci	if (err < 0)
23048c2ecf20Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err);
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_cistatic int mpls_valid_getroute_req(struct sk_buff *skb,
23088c2ecf20Sopenharmony_ci				   const struct nlmsghdr *nlh,
23098c2ecf20Sopenharmony_ci				   struct nlattr **tb,
23108c2ecf20Sopenharmony_ci				   struct netlink_ext_ack *extack)
23118c2ecf20Sopenharmony_ci{
23128c2ecf20Sopenharmony_ci	struct rtmsg *rtm;
23138c2ecf20Sopenharmony_ci	int i, err;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
23168c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
23178c2ecf20Sopenharmony_ci				   "Invalid header for get route request");
23188c2ecf20Sopenharmony_ci		return -EINVAL;
23198c2ecf20Sopenharmony_ci	}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	if (!netlink_strict_get_check(skb))
23228c2ecf20Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
23238c2ecf20Sopenharmony_ci					      rtm_mpls_policy, extack);
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_ci	rtm = nlmsg_data(nlh);
23268c2ecf20Sopenharmony_ci	if ((rtm->rtm_dst_len && rtm->rtm_dst_len != 20) ||
23278c2ecf20Sopenharmony_ci	    rtm->rtm_src_len || rtm->rtm_tos || rtm->rtm_table ||
23288c2ecf20Sopenharmony_ci	    rtm->rtm_protocol || rtm->rtm_scope || rtm->rtm_type) {
23298c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
23308c2ecf20Sopenharmony_ci		return -EINVAL;
23318c2ecf20Sopenharmony_ci	}
23328c2ecf20Sopenharmony_ci	if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
23338c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
23348c2ecf20Sopenharmony_ci				   "Invalid flags for get route request");
23358c2ecf20Sopenharmony_ci		return -EINVAL;
23368c2ecf20Sopenharmony_ci	}
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
23398c2ecf20Sopenharmony_ci					    rtm_mpls_policy, extack);
23408c2ecf20Sopenharmony_ci	if (err)
23418c2ecf20Sopenharmony_ci		return err;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	if ((tb[RTA_DST] || tb[RTA_NEWDST]) && !rtm->rtm_dst_len) {
23448c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "rtm_dst_len must be 20 for MPLS");
23458c2ecf20Sopenharmony_ci		return -EINVAL;
23468c2ecf20Sopenharmony_ci	}
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	for (i = 0; i <= RTA_MAX; i++) {
23498c2ecf20Sopenharmony_ci		if (!tb[i])
23508c2ecf20Sopenharmony_ci			continue;
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci		switch (i) {
23538c2ecf20Sopenharmony_ci		case RTA_DST:
23548c2ecf20Sopenharmony_ci		case RTA_NEWDST:
23558c2ecf20Sopenharmony_ci			break;
23568c2ecf20Sopenharmony_ci		default:
23578c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
23588c2ecf20Sopenharmony_ci			return -EINVAL;
23598c2ecf20Sopenharmony_ci		}
23608c2ecf20Sopenharmony_ci	}
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	return 0;
23638c2ecf20Sopenharmony_ci}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_cistatic int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
23668c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
23678c2ecf20Sopenharmony_ci{
23688c2ecf20Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
23698c2ecf20Sopenharmony_ci	u32 portid = NETLINK_CB(in_skb).portid;
23708c2ecf20Sopenharmony_ci	u32 in_label = LABEL_NOT_SPECIFIED;
23718c2ecf20Sopenharmony_ci	struct nlattr *tb[RTA_MAX + 1];
23728c2ecf20Sopenharmony_ci	u32 labels[MAX_NEW_LABELS];
23738c2ecf20Sopenharmony_ci	struct mpls_shim_hdr *hdr;
23748c2ecf20Sopenharmony_ci	unsigned int hdr_size = 0;
23758c2ecf20Sopenharmony_ci	struct net_device *dev;
23768c2ecf20Sopenharmony_ci	struct mpls_route *rt;
23778c2ecf20Sopenharmony_ci	struct rtmsg *rtm, *r;
23788c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
23798c2ecf20Sopenharmony_ci	struct sk_buff *skb;
23808c2ecf20Sopenharmony_ci	struct mpls_nh *nh;
23818c2ecf20Sopenharmony_ci	u8 n_labels;
23828c2ecf20Sopenharmony_ci	int err;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	err = mpls_valid_getroute_req(in_skb, in_nlh, tb, extack);
23858c2ecf20Sopenharmony_ci	if (err < 0)
23868c2ecf20Sopenharmony_ci		goto errout;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	rtm = nlmsg_data(in_nlh);
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	if (tb[RTA_DST]) {
23918c2ecf20Sopenharmony_ci		u8 label_count;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci		if (nla_get_labels(tb[RTA_DST], 1, &label_count,
23948c2ecf20Sopenharmony_ci				   &in_label, extack)) {
23958c2ecf20Sopenharmony_ci			err = -EINVAL;
23968c2ecf20Sopenharmony_ci			goto errout;
23978c2ecf20Sopenharmony_ci		}
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci		if (!mpls_label_ok(net, &in_label, extack)) {
24008c2ecf20Sopenharmony_ci			err = -EINVAL;
24018c2ecf20Sopenharmony_ci			goto errout;
24028c2ecf20Sopenharmony_ci		}
24038c2ecf20Sopenharmony_ci	}
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	rt = mpls_route_input_rcu(net, in_label);
24068c2ecf20Sopenharmony_ci	if (!rt) {
24078c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
24088c2ecf20Sopenharmony_ci		goto errout;
24098c2ecf20Sopenharmony_ci	}
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
24128c2ecf20Sopenharmony_ci		skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL);
24138c2ecf20Sopenharmony_ci		if (!skb) {
24148c2ecf20Sopenharmony_ci			err = -ENOBUFS;
24158c2ecf20Sopenharmony_ci			goto errout;
24168c2ecf20Sopenharmony_ci		}
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci		err = mpls_dump_route(skb, portid, in_nlh->nlmsg_seq,
24198c2ecf20Sopenharmony_ci				      RTM_NEWROUTE, in_label, rt, 0);
24208c2ecf20Sopenharmony_ci		if (err < 0) {
24218c2ecf20Sopenharmony_ci			/* -EMSGSIZE implies BUG in lfib_nlmsg_size */
24228c2ecf20Sopenharmony_ci			WARN_ON(err == -EMSGSIZE);
24238c2ecf20Sopenharmony_ci			goto errout_free;
24248c2ecf20Sopenharmony_ci		}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci		return rtnl_unicast(skb, net, portid);
24278c2ecf20Sopenharmony_ci	}
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	if (tb[RTA_NEWDST]) {
24308c2ecf20Sopenharmony_ci		if (nla_get_labels(tb[RTA_NEWDST], MAX_NEW_LABELS, &n_labels,
24318c2ecf20Sopenharmony_ci				   labels, extack) != 0) {
24328c2ecf20Sopenharmony_ci			err = -EINVAL;
24338c2ecf20Sopenharmony_ci			goto errout;
24348c2ecf20Sopenharmony_ci		}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci		hdr_size = n_labels * sizeof(struct mpls_shim_hdr);
24378c2ecf20Sopenharmony_ci	}
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
24408c2ecf20Sopenharmony_ci	if (!skb) {
24418c2ecf20Sopenharmony_ci		err = -ENOBUFS;
24428c2ecf20Sopenharmony_ci		goto errout;
24438c2ecf20Sopenharmony_ci	}
24448c2ecf20Sopenharmony_ci
24458c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_MPLS_UC);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	if (hdr_size) {
24488c2ecf20Sopenharmony_ci		bool bos;
24498c2ecf20Sopenharmony_ci		int i;
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci		if (skb_cow(skb, hdr_size)) {
24528c2ecf20Sopenharmony_ci			err = -ENOBUFS;
24538c2ecf20Sopenharmony_ci			goto errout_free;
24548c2ecf20Sopenharmony_ci		}
24558c2ecf20Sopenharmony_ci
24568c2ecf20Sopenharmony_ci		skb_reserve(skb, hdr_size);
24578c2ecf20Sopenharmony_ci		skb_push(skb, hdr_size);
24588c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci		/* Push new labels */
24618c2ecf20Sopenharmony_ci		hdr = mpls_hdr(skb);
24628c2ecf20Sopenharmony_ci		bos = true;
24638c2ecf20Sopenharmony_ci		for (i = n_labels - 1; i >= 0; i--) {
24648c2ecf20Sopenharmony_ci			hdr[i] = mpls_entry_encode(labels[i],
24658c2ecf20Sopenharmony_ci						   1, 0, bos);
24668c2ecf20Sopenharmony_ci			bos = false;
24678c2ecf20Sopenharmony_ci		}
24688c2ecf20Sopenharmony_ci	}
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci	nh = mpls_select_multipath(rt, skb);
24718c2ecf20Sopenharmony_ci	if (!nh) {
24728c2ecf20Sopenharmony_ci		err = -ENETUNREACH;
24738c2ecf20Sopenharmony_ci		goto errout_free;
24748c2ecf20Sopenharmony_ci	}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	if (hdr_size) {
24778c2ecf20Sopenharmony_ci		skb_pull(skb, hdr_size);
24788c2ecf20Sopenharmony_ci		skb_reset_network_header(skb);
24798c2ecf20Sopenharmony_ci	}
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, portid, in_nlh->nlmsg_seq,
24828c2ecf20Sopenharmony_ci			RTM_NEWROUTE, sizeof(*r), 0);
24838c2ecf20Sopenharmony_ci	if (!nlh) {
24848c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
24858c2ecf20Sopenharmony_ci		goto errout_free;
24868c2ecf20Sopenharmony_ci	}
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	r = nlmsg_data(nlh);
24898c2ecf20Sopenharmony_ci	r->rtm_family	 = AF_MPLS;
24908c2ecf20Sopenharmony_ci	r->rtm_dst_len	= 20;
24918c2ecf20Sopenharmony_ci	r->rtm_src_len	= 0;
24928c2ecf20Sopenharmony_ci	r->rtm_table	= RT_TABLE_MAIN;
24938c2ecf20Sopenharmony_ci	r->rtm_type	= RTN_UNICAST;
24948c2ecf20Sopenharmony_ci	r->rtm_scope	= RT_SCOPE_UNIVERSE;
24958c2ecf20Sopenharmony_ci	r->rtm_protocol = rt->rt_protocol;
24968c2ecf20Sopenharmony_ci	r->rtm_flags	= 0;
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci	if (nla_put_labels(skb, RTA_DST, 1, &in_label))
24998c2ecf20Sopenharmony_ci		goto nla_put_failure;
25008c2ecf20Sopenharmony_ci
25018c2ecf20Sopenharmony_ci	if (nh->nh_labels &&
25028c2ecf20Sopenharmony_ci	    nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
25038c2ecf20Sopenharmony_ci			   nh->nh_label))
25048c2ecf20Sopenharmony_ci		goto nla_put_failure;
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci	if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
25078c2ecf20Sopenharmony_ci	    nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
25088c2ecf20Sopenharmony_ci			nh->nh_via_alen))
25098c2ecf20Sopenharmony_ci		goto nla_put_failure;
25108c2ecf20Sopenharmony_ci	dev = rtnl_dereference(nh->nh_dev);
25118c2ecf20Sopenharmony_ci	if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
25128c2ecf20Sopenharmony_ci		goto nla_put_failure;
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci	err = rtnl_unicast(skb, net, portid);
25178c2ecf20Sopenharmony_cierrout:
25188c2ecf20Sopenharmony_ci	return err;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_cinla_put_failure:
25218c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
25228c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
25238c2ecf20Sopenharmony_cierrout_free:
25248c2ecf20Sopenharmony_ci	kfree_skb(skb);
25258c2ecf20Sopenharmony_ci	return err;
25268c2ecf20Sopenharmony_ci}
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_cistatic int resize_platform_label_table(struct net *net, size_t limit)
25298c2ecf20Sopenharmony_ci{
25308c2ecf20Sopenharmony_ci	size_t size = sizeof(struct mpls_route *) * limit;
25318c2ecf20Sopenharmony_ci	size_t old_limit;
25328c2ecf20Sopenharmony_ci	size_t cp_size;
25338c2ecf20Sopenharmony_ci	struct mpls_route __rcu **labels = NULL, **old;
25348c2ecf20Sopenharmony_ci	struct mpls_route *rt0 = NULL, *rt2 = NULL;
25358c2ecf20Sopenharmony_ci	unsigned index;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	if (size) {
25388c2ecf20Sopenharmony_ci		labels = kvzalloc(size, GFP_KERNEL);
25398c2ecf20Sopenharmony_ci		if (!labels)
25408c2ecf20Sopenharmony_ci			goto nolabels;
25418c2ecf20Sopenharmony_ci	}
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	/* In case the predefined labels need to be populated */
25448c2ecf20Sopenharmony_ci	if (limit > MPLS_LABEL_IPV4NULL) {
25458c2ecf20Sopenharmony_ci		struct net_device *lo = net->loopback_dev;
25468c2ecf20Sopenharmony_ci		rt0 = mpls_rt_alloc(1, lo->addr_len, 0);
25478c2ecf20Sopenharmony_ci		if (IS_ERR(rt0))
25488c2ecf20Sopenharmony_ci			goto nort0;
25498c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo);
25508c2ecf20Sopenharmony_ci		rt0->rt_protocol = RTPROT_KERNEL;
25518c2ecf20Sopenharmony_ci		rt0->rt_payload_type = MPT_IPV4;
25528c2ecf20Sopenharmony_ci		rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
25538c2ecf20Sopenharmony_ci		rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
25548c2ecf20Sopenharmony_ci		rt0->rt_nh->nh_via_alen = lo->addr_len;
25558c2ecf20Sopenharmony_ci		memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr,
25568c2ecf20Sopenharmony_ci		       lo->addr_len);
25578c2ecf20Sopenharmony_ci	}
25588c2ecf20Sopenharmony_ci	if (limit > MPLS_LABEL_IPV6NULL) {
25598c2ecf20Sopenharmony_ci		struct net_device *lo = net->loopback_dev;
25608c2ecf20Sopenharmony_ci		rt2 = mpls_rt_alloc(1, lo->addr_len, 0);
25618c2ecf20Sopenharmony_ci		if (IS_ERR(rt2))
25628c2ecf20Sopenharmony_ci			goto nort2;
25638c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo);
25648c2ecf20Sopenharmony_ci		rt2->rt_protocol = RTPROT_KERNEL;
25658c2ecf20Sopenharmony_ci		rt2->rt_payload_type = MPT_IPV6;
25668c2ecf20Sopenharmony_ci		rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
25678c2ecf20Sopenharmony_ci		rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
25688c2ecf20Sopenharmony_ci		rt2->rt_nh->nh_via_alen = lo->addr_len;
25698c2ecf20Sopenharmony_ci		memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr,
25708c2ecf20Sopenharmony_ci		       lo->addr_len);
25718c2ecf20Sopenharmony_ci	}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	rtnl_lock();
25748c2ecf20Sopenharmony_ci	/* Remember the original table */
25758c2ecf20Sopenharmony_ci	old = rtnl_dereference(net->mpls.platform_label);
25768c2ecf20Sopenharmony_ci	old_limit = net->mpls.platform_labels;
25778c2ecf20Sopenharmony_ci
25788c2ecf20Sopenharmony_ci	/* Free any labels beyond the new table */
25798c2ecf20Sopenharmony_ci	for (index = limit; index < old_limit; index++)
25808c2ecf20Sopenharmony_ci		mpls_route_update(net, index, NULL, NULL);
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	/* Copy over the old labels */
25838c2ecf20Sopenharmony_ci	cp_size = size;
25848c2ecf20Sopenharmony_ci	if (old_limit < limit)
25858c2ecf20Sopenharmony_ci		cp_size = old_limit * sizeof(struct mpls_route *);
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	memcpy(labels, old, cp_size);
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	/* If needed set the predefined labels */
25908c2ecf20Sopenharmony_ci	if ((old_limit <= MPLS_LABEL_IPV6NULL) &&
25918c2ecf20Sopenharmony_ci	    (limit > MPLS_LABEL_IPV6NULL)) {
25928c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2);
25938c2ecf20Sopenharmony_ci		rt2 = NULL;
25948c2ecf20Sopenharmony_ci	}
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	if ((old_limit <= MPLS_LABEL_IPV4NULL) &&
25978c2ecf20Sopenharmony_ci	    (limit > MPLS_LABEL_IPV4NULL)) {
25988c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0);
25998c2ecf20Sopenharmony_ci		rt0 = NULL;
26008c2ecf20Sopenharmony_ci	}
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	/* Update the global pointers */
26038c2ecf20Sopenharmony_ci	net->mpls.platform_labels = limit;
26048c2ecf20Sopenharmony_ci	rcu_assign_pointer(net->mpls.platform_label, labels);
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	rtnl_unlock();
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	mpls_rt_free(rt2);
26098c2ecf20Sopenharmony_ci	mpls_rt_free(rt0);
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	if (old) {
26128c2ecf20Sopenharmony_ci		synchronize_rcu();
26138c2ecf20Sopenharmony_ci		kvfree(old);
26148c2ecf20Sopenharmony_ci	}
26158c2ecf20Sopenharmony_ci	return 0;
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_cinort2:
26188c2ecf20Sopenharmony_ci	mpls_rt_free(rt0);
26198c2ecf20Sopenharmony_cinort0:
26208c2ecf20Sopenharmony_ci	kvfree(labels);
26218c2ecf20Sopenharmony_cinolabels:
26228c2ecf20Sopenharmony_ci	return -ENOMEM;
26238c2ecf20Sopenharmony_ci}
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_cistatic int mpls_platform_labels(struct ctl_table *table, int write,
26268c2ecf20Sopenharmony_ci				void *buffer, size_t *lenp, loff_t *ppos)
26278c2ecf20Sopenharmony_ci{
26288c2ecf20Sopenharmony_ci	struct net *net = table->data;
26298c2ecf20Sopenharmony_ci	int platform_labels = net->mpls.platform_labels;
26308c2ecf20Sopenharmony_ci	int ret;
26318c2ecf20Sopenharmony_ci	struct ctl_table tmp = {
26328c2ecf20Sopenharmony_ci		.procname	= table->procname,
26338c2ecf20Sopenharmony_ci		.data		= &platform_labels,
26348c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
26358c2ecf20Sopenharmony_ci		.mode		= table->mode,
26368c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
26378c2ecf20Sopenharmony_ci		.extra2		= &label_limit,
26388c2ecf20Sopenharmony_ci	};
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	if (write && ret == 0)
26438c2ecf20Sopenharmony_ci		ret = resize_platform_label_table(net, platform_labels);
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	return ret;
26468c2ecf20Sopenharmony_ci}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci#define MPLS_NS_SYSCTL_OFFSET(field)		\
26498c2ecf20Sopenharmony_ci	(&((struct net *)0)->field)
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic const struct ctl_table mpls_table[] = {
26528c2ecf20Sopenharmony_ci	{
26538c2ecf20Sopenharmony_ci		.procname	= "platform_labels",
26548c2ecf20Sopenharmony_ci		.data		= NULL,
26558c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
26568c2ecf20Sopenharmony_ci		.mode		= 0644,
26578c2ecf20Sopenharmony_ci		.proc_handler	= mpls_platform_labels,
26588c2ecf20Sopenharmony_ci	},
26598c2ecf20Sopenharmony_ci	{
26608c2ecf20Sopenharmony_ci		.procname	= "ip_ttl_propagate",
26618c2ecf20Sopenharmony_ci		.data		= MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate),
26628c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
26638c2ecf20Sopenharmony_ci		.mode		= 0644,
26648c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
26658c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
26668c2ecf20Sopenharmony_ci		.extra2		= SYSCTL_ONE,
26678c2ecf20Sopenharmony_ci	},
26688c2ecf20Sopenharmony_ci	{
26698c2ecf20Sopenharmony_ci		.procname	= "default_ttl",
26708c2ecf20Sopenharmony_ci		.data		= MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl),
26718c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
26728c2ecf20Sopenharmony_ci		.mode		= 0644,
26738c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
26748c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ONE,
26758c2ecf20Sopenharmony_ci		.extra2		= &ttl_max,
26768c2ecf20Sopenharmony_ci	},
26778c2ecf20Sopenharmony_ci	{ }
26788c2ecf20Sopenharmony_ci};
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_cistatic int mpls_net_init(struct net *net)
26818c2ecf20Sopenharmony_ci{
26828c2ecf20Sopenharmony_ci	struct ctl_table *table;
26838c2ecf20Sopenharmony_ci	int i;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	net->mpls.platform_labels = 0;
26868c2ecf20Sopenharmony_ci	net->mpls.platform_label = NULL;
26878c2ecf20Sopenharmony_ci	net->mpls.ip_ttl_propagate = 1;
26888c2ecf20Sopenharmony_ci	net->mpls.default_ttl = 255;
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_ci	table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL);
26918c2ecf20Sopenharmony_ci	if (table == NULL)
26928c2ecf20Sopenharmony_ci		return -ENOMEM;
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	/* Table data contains only offsets relative to the base of
26958c2ecf20Sopenharmony_ci	 * the mdev at this point, so make them absolute.
26968c2ecf20Sopenharmony_ci	 */
26978c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++)
26988c2ecf20Sopenharmony_ci		table[i].data = (char *)net + (uintptr_t)table[i].data;
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	net->mpls.ctl = register_net_sysctl(net, "net/mpls", table);
27018c2ecf20Sopenharmony_ci	if (net->mpls.ctl == NULL) {
27028c2ecf20Sopenharmony_ci		kfree(table);
27038c2ecf20Sopenharmony_ci		return -ENOMEM;
27048c2ecf20Sopenharmony_ci	}
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	return 0;
27078c2ecf20Sopenharmony_ci}
27088c2ecf20Sopenharmony_ci
27098c2ecf20Sopenharmony_cistatic void mpls_net_exit(struct net *net)
27108c2ecf20Sopenharmony_ci{
27118c2ecf20Sopenharmony_ci	struct mpls_route __rcu **platform_label;
27128c2ecf20Sopenharmony_ci	size_t platform_labels;
27138c2ecf20Sopenharmony_ci	struct ctl_table *table;
27148c2ecf20Sopenharmony_ci	unsigned int index;
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci	table = net->mpls.ctl->ctl_table_arg;
27178c2ecf20Sopenharmony_ci	unregister_net_sysctl_table(net->mpls.ctl);
27188c2ecf20Sopenharmony_ci	kfree(table);
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	/* An rcu grace period has passed since there was a device in
27218c2ecf20Sopenharmony_ci	 * the network namespace (and thus the last in flight packet)
27228c2ecf20Sopenharmony_ci	 * left this network namespace.  This is because
27238c2ecf20Sopenharmony_ci	 * unregister_netdevice_many and netdev_run_todo has completed
27248c2ecf20Sopenharmony_ci	 * for each network device that was in this network namespace.
27258c2ecf20Sopenharmony_ci	 *
27268c2ecf20Sopenharmony_ci	 * As such no additional rcu synchronization is necessary when
27278c2ecf20Sopenharmony_ci	 * freeing the platform_label table.
27288c2ecf20Sopenharmony_ci	 */
27298c2ecf20Sopenharmony_ci	rtnl_lock();
27308c2ecf20Sopenharmony_ci	platform_label = rtnl_dereference(net->mpls.platform_label);
27318c2ecf20Sopenharmony_ci	platform_labels = net->mpls.platform_labels;
27328c2ecf20Sopenharmony_ci	for (index = 0; index < platform_labels; index++) {
27338c2ecf20Sopenharmony_ci		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
27348c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(platform_label[index], NULL);
27358c2ecf20Sopenharmony_ci		mpls_notify_route(net, index, rt, NULL, NULL);
27368c2ecf20Sopenharmony_ci		mpls_rt_free(rt);
27378c2ecf20Sopenharmony_ci	}
27388c2ecf20Sopenharmony_ci	rtnl_unlock();
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	kvfree(platform_label);
27418c2ecf20Sopenharmony_ci}
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_cistatic struct pernet_operations mpls_net_ops = {
27448c2ecf20Sopenharmony_ci	.init = mpls_net_init,
27458c2ecf20Sopenharmony_ci	.exit = mpls_net_exit,
27468c2ecf20Sopenharmony_ci};
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_cistatic struct rtnl_af_ops mpls_af_ops __read_mostly = {
27498c2ecf20Sopenharmony_ci	.family		   = AF_MPLS,
27508c2ecf20Sopenharmony_ci	.fill_stats_af	   = mpls_fill_stats_af,
27518c2ecf20Sopenharmony_ci	.get_stats_af_size = mpls_get_stats_af_size,
27528c2ecf20Sopenharmony_ci};
27538c2ecf20Sopenharmony_ci
27548c2ecf20Sopenharmony_cistatic int __init mpls_init(void)
27558c2ecf20Sopenharmony_ci{
27568c2ecf20Sopenharmony_ci	int err;
27578c2ecf20Sopenharmony_ci
27588c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4);
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&mpls_net_ops);
27618c2ecf20Sopenharmony_ci	if (err)
27628c2ecf20Sopenharmony_ci		goto out;
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci	err = register_netdevice_notifier(&mpls_dev_notifier);
27658c2ecf20Sopenharmony_ci	if (err)
27668c2ecf20Sopenharmony_ci		goto out_unregister_pernet;
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	dev_add_pack(&mpls_packet_type);
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci	rtnl_af_register(&mpls_af_ops);
27718c2ecf20Sopenharmony_ci
27728c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_NEWROUTE,
27738c2ecf20Sopenharmony_ci			     mpls_rtm_newroute, NULL, 0);
27748c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_DELROUTE,
27758c2ecf20Sopenharmony_ci			     mpls_rtm_delroute, NULL, 0);
27768c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETROUTE,
27778c2ecf20Sopenharmony_ci			     mpls_getroute, mpls_dump_routes, 0);
27788c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETNETCONF,
27798c2ecf20Sopenharmony_ci			     mpls_netconf_get_devconf,
27808c2ecf20Sopenharmony_ci			     mpls_netconf_dump_devconf, 0);
27818c2ecf20Sopenharmony_ci	err = ipgre_tunnel_encap_add_mpls_ops();
27828c2ecf20Sopenharmony_ci	if (err)
27838c2ecf20Sopenharmony_ci		pr_err("Can't add mpls over gre tunnel ops\n");
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci	err = 0;
27868c2ecf20Sopenharmony_ciout:
27878c2ecf20Sopenharmony_ci	return err;
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ciout_unregister_pernet:
27908c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&mpls_net_ops);
27918c2ecf20Sopenharmony_ci	goto out;
27928c2ecf20Sopenharmony_ci}
27938c2ecf20Sopenharmony_cimodule_init(mpls_init);
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_cistatic void __exit mpls_exit(void)
27968c2ecf20Sopenharmony_ci{
27978c2ecf20Sopenharmony_ci	rtnl_unregister_all(PF_MPLS);
27988c2ecf20Sopenharmony_ci	rtnl_af_unregister(&mpls_af_ops);
27998c2ecf20Sopenharmony_ci	dev_remove_pack(&mpls_packet_type);
28008c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&mpls_dev_notifier);
28018c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&mpls_net_ops);
28028c2ecf20Sopenharmony_ci	ipgre_tunnel_encap_del_mpls_ops();
28038c2ecf20Sopenharmony_ci}
28048c2ecf20Sopenharmony_cimodule_exit(mpls_exit);
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MultiProtocol Label Switching");
28078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
28088c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_MPLS);
2809