Blame view

net/mpls/af_mpls.c 63.3 KB
09c434b8a   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
0189197f4   Eric W. Biederman   mpls: Basic routi...
2
3
4
  #include <linux/types.h>
  #include <linux/skbuff.h>
  #include <linux/socket.h>
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
5
  #include <linux/sysctl.h>
0189197f4   Eric W. Biederman   mpls: Basic routi...
6
7
8
9
10
  #include <linux/net.h>
  #include <linux/module.h>
  #include <linux/if_arp.h>
  #include <linux/ipv6.h>
  #include <linux/mpls.h>
24045a03b   David Ahern   net: mpls: Add su...
11
  #include <linux/netconf.h>
3968523f8   Dan Williams   mpls, nospec: San...
12
  #include <linux/nospec.h>
4b5edb2f4   Stephen Rothwell   mpls: using vzall...
13
  #include <linux/vmalloc.h>
27d691056   Robert Shearman   mpls: Packet stats
14
  #include <linux/percpu.h>
0189197f4   Eric W. Biederman   mpls: Basic routi...
15
16
17
18
19
20
  #include <net/ip.h>
  #include <net/dst.h>
  #include <net/sock.h>
  #include <net/arp.h>
  #include <net/ip_fib.h>
  #include <net/netevent.h>
bdc476413   Amine Kherbouche   ip_tunnel: add mp...
21
  #include <net/ip_tunnels.h>
0189197f4   Eric W. Biederman   mpls: Basic routi...
22
  #include <net/netns/generic.h>
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
23
24
  #if IS_ENABLED(CONFIG_IPV6)
  #include <net/ipv6.h>
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
25
  #endif
3616d08bc   David Ahern   ipv6: Move ipv6 s...
26
  #include <net/ipv6_stubs.h>
3c618c1db   David Ahern   net: Rename net/n...
27
  #include <net/rtnh.h>
0189197f4   Eric W. Biederman   mpls: Basic routi...
28
  #include "internal.h"
df1c63164   David Ahern   net: mpls: Limit ...
29
30
  /* max memory we will use for mpls_route */
  #define MAX_MPLS_ROUTE_MEM	4096
1c78efa83   Robert Shearman   mpls: flow-based ...
31
32
33
34
  /* Maximum number of labels to look ahead at when selecting a path of
   * a multipath route
   */
  #define MAX_MP_SELECT_LABELS 4
eb7809f09   Robert Shearman   mpls: fix out-of-...
35
  #define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1)
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
36
  static int label_limit = (1 << 20) - 1;
a59166e47   Robert Shearman   mpls: allow TTL p...
37
  static int ttl_max = 255;
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
38

bdc476413   Amine Kherbouche   ip_tunnel: add mp...
39
  #if IS_ENABLED(CONFIG_NET_IP_TUNNEL)
14c68c43b   Colin Ian King   net: mpls: make f...
40
  static size_t ipgre_mpls_encap_hlen(struct ip_tunnel_encap *e)
bdc476413   Amine Kherbouche   ip_tunnel: add mp...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  {
  	return sizeof(struct mpls_shim_hdr);
  }
  
  static const struct ip_tunnel_encap_ops mpls_iptun_ops = {
  	.encap_hlen	= ipgre_mpls_encap_hlen,
  };
  
  static int ipgre_tunnel_encap_add_mpls_ops(void)
  {
  	return ip_tunnel_encap_add_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS);
  }
  
  static void ipgre_tunnel_encap_del_mpls_ops(void)
  {
  	ip_tunnel_encap_del_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS);
  }
  #else
  static int ipgre_tunnel_encap_add_mpls_ops(void)
  {
  	return 0;
  }
  
  static void ipgre_tunnel_encap_del_mpls_ops(void)
  {
  }
  #endif
8de147dc8   Eric W. Biederman   mpls: Multicast r...
68
69
70
  static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt,
  		       struct nlmsghdr *nlh, struct net *net, u32 portid,
  		       unsigned int nlm_flags);
0189197f4   Eric W. Biederman   mpls: Basic routi...
71
72
73
74
75
76
77
78
79
80
81
  static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
  {
  	struct mpls_route *rt = NULL;
  
  	if (index < net->mpls.platform_labels) {
  		struct mpls_route __rcu **platform_label =
  			rcu_dereference(net->mpls.platform_label);
  		rt = rcu_dereference(platform_label[index]);
  	}
  	return rt;
  }
face0188e   Roopa Prabhu   mpls: export mpls...
82
  bool mpls_output_possible(const struct net_device *dev)
0189197f4   Eric W. Biederman   mpls: Basic routi...
83
84
85
  {
  	return dev && (dev->flags & IFF_UP) && netif_carrier_ok(dev);
  }
face0188e   Roopa Prabhu   mpls: export mpls...
86
  EXPORT_SYMBOL_GPL(mpls_output_possible);
0189197f4   Eric W. Biederman   mpls: Basic routi...
87

cf4b24f00   Robert Shearman   mpls: reduce memo...
88
89
  static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh)
  {
59b209667   David Ahern   net: mpls: change...
90
  	return (u8 *)nh + rt->rt_via_offset;
cf4b24f00   Robert Shearman   mpls: reduce memo...
91
92
93
94
95
96
97
  }
  
  static const u8 *mpls_nh_via(const struct mpls_route *rt,
  			     const struct mpls_nh *nh)
  {
  	return __mpls_nh_via((struct mpls_route *)rt, (struct mpls_nh *)nh);
  }
f8efb73c9   Roopa Prabhu   mpls: multipath r...
98
  static unsigned int mpls_nh_header_size(const struct mpls_nh *nh)
0189197f4   Eric W. Biederman   mpls: Basic routi...
99
100
  {
  	/* The size of the layer 2.5 labels to be added for this route */
f8efb73c9   Roopa Prabhu   mpls: multipath r...
101
  	return nh->nh_labels * sizeof(struct mpls_shim_hdr);
0189197f4   Eric W. Biederman   mpls: Basic routi...
102
  }
face0188e   Roopa Prabhu   mpls: export mpls...
103
  unsigned int mpls_dev_mtu(const struct net_device *dev)
0189197f4   Eric W. Biederman   mpls: Basic routi...
104
105
106
107
  {
  	/* The amount of data the layer 2 frame can hold */
  	return dev->mtu;
  }
face0188e   Roopa Prabhu   mpls: export mpls...
108
  EXPORT_SYMBOL_GPL(mpls_dev_mtu);
0189197f4   Eric W. Biederman   mpls: Basic routi...
109

face0188e   Roopa Prabhu   mpls: export mpls...
110
  bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
0189197f4   Eric W. Biederman   mpls: Basic routi...
111
112
113
  {
  	if (skb->len <= mtu)
  		return false;
779b7931b   Daniel Axtens   net: rename skb_g...
114
  	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
0189197f4   Eric W. Biederman   mpls: Basic routi...
115
116
117
118
  		return false;
  
  	return true;
  }
face0188e   Roopa Prabhu   mpls: export mpls...
119
  EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
0189197f4   Eric W. Biederman   mpls: Basic routi...
120

27d691056   Robert Shearman   mpls: Packet stats
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  void mpls_stats_inc_outucastpkts(struct net_device *dev,
  				 const struct sk_buff *skb)
  {
  	struct mpls_dev *mdev;
  
  	if (skb->protocol == htons(ETH_P_MPLS_UC)) {
  		mdev = mpls_dev_get(dev);
  		if (mdev)
  			MPLS_INC_STATS_LEN(mdev, skb->len,
  					   tx_packets,
  					   tx_bytes);
  	} else if (skb->protocol == htons(ETH_P_IP)) {
  		IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
  #if IS_ENABLED(CONFIG_IPV6)
  	} else if (skb->protocol == htons(ETH_P_IPV6)) {
  		struct inet6_dev *in6dev = __in6_dev_get(dev);
  
  		if (in6dev)
  			IP6_UPD_PO_STATS(dev_net(dev), in6dev,
  					 IPSTATS_MIB_OUT, skb->len);
  #endif
  	}
  }
  EXPORT_SYMBOL_GPL(mpls_stats_inc_outucastpkts);
9f427a0e4   David Ahern   net: mpls: Fix mu...
145
  static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
146
  {
1c78efa83   Robert Shearman   mpls: flow-based ...
147
  	struct mpls_entry_decoded dec;
9f427a0e4   David Ahern   net: mpls: Fix mu...
148
  	unsigned int mpls_hdr_len = 0;
1c78efa83   Robert Shearman   mpls: flow-based ...
149
150
151
  	struct mpls_shim_hdr *hdr;
  	bool eli_seen = false;
  	int label_index;
1c78efa83   Robert Shearman   mpls: flow-based ...
152
  	u32 hash = 0;
9f427a0e4   David Ahern   net: mpls: Fix mu...
153
  	for (label_index = 0; label_index < MAX_MP_SELECT_LABELS;
1c78efa83   Robert Shearman   mpls: flow-based ...
154
  	     label_index++) {
9f427a0e4   David Ahern   net: mpls: Fix mu...
155
156
  		mpls_hdr_len += sizeof(*hdr);
  		if (!pskb_may_pull(skb, mpls_hdr_len))
1c78efa83   Robert Shearman   mpls: flow-based ...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  			break;
  
  		/* Read and decode the current label */
  		hdr = mpls_hdr(skb) + label_index;
  		dec = mpls_entry_decode(hdr);
  
  		/* RFC6790 - reserved labels MUST NOT be used as keys
  		 * for the load-balancing function
  		 */
  		if (likely(dec.label >= MPLS_LABEL_FIRST_UNRESERVED)) {
  			hash = jhash_1word(dec.label, hash);
  
  			/* The entropy label follows the entropy label
  			 * indicator, so this means that the entropy
  			 * label was just added to the hash - no need to
  			 * go any deeper either in the label stack or in the
  			 * payload
  			 */
  			if (eli_seen)
  				break;
  		} else if (dec.label == MPLS_LABEL_ENTROPY) {
  			eli_seen = true;
  		}
9f427a0e4   David Ahern   net: mpls: Fix mu...
180
181
182
183
184
  		if (!dec.bos)
  			continue;
  
  		/* found bottom label; does skb have room for a header? */
  		if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
1c78efa83   Robert Shearman   mpls: flow-based ...
185
  			const struct iphdr *v4hdr;
9f427a0e4   David Ahern   net: mpls: Fix mu...
186
  			v4hdr = (const struct iphdr *)(hdr + 1);
1c78efa83   Robert Shearman   mpls: flow-based ...
187
188
189
190
191
  			if (v4hdr->version == 4) {
  				hash = jhash_3words(ntohl(v4hdr->saddr),
  						    ntohl(v4hdr->daddr),
  						    v4hdr->protocol, hash);
  			} else if (v4hdr->version == 6 &&
9f427a0e4   David Ahern   net: mpls: Fix mu...
192
193
  				   pskb_may_pull(skb, mpls_hdr_len +
  						 sizeof(struct ipv6hdr))) {
1c78efa83   Robert Shearman   mpls: flow-based ...
194
  				const struct ipv6hdr *v6hdr;
9f427a0e4   David Ahern   net: mpls: Fix mu...
195
  				v6hdr = (const struct ipv6hdr *)(hdr + 1);
1c78efa83   Robert Shearman   mpls: flow-based ...
196
197
198
199
200
  				hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
  				hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
  				hash = jhash_1word(v6hdr->nexthdr, hash);
  			}
  		}
9f427a0e4   David Ahern   net: mpls: Fix mu...
201
202
  
  		break;
1c78efa83   Robert Shearman   mpls: flow-based ...
203
  	}
c89359a42   Roopa Prabhu   mpls: support for...
204
205
  	return hash;
  }
59b209667   David Ahern   net: mpls: change...
206
207
208
209
  static struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index)
  {
  	return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size);
  }
39eb8cd17   David Ahern   net: mpls: rt_nhn...
210
211
212
213
214
  /* number of alive nexthops (rt->rt_nhn_alive) and the flags for
   * a next hop (nh->nh_flags) are modified by netdev event handlers.
   * Since those fields can change at any moment, use READ_ONCE to
   * access both.
   */
c89359a42   Roopa Prabhu   mpls: support for...
215
  static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
9f427a0e4   David Ahern   net: mpls: Fix mu...
216
  					     struct sk_buff *skb)
c89359a42   Roopa Prabhu   mpls: support for...
217
  {
c89359a42   Roopa Prabhu   mpls: support for...
218
219
220
  	u32 hash = 0;
  	int nh_index = 0;
  	int n = 0;
77ef013aa   David Ahern   net: mpls: Conver...
221
  	u8 alive;
c89359a42   Roopa Prabhu   mpls: support for...
222
223
224
225
226
  
  	/* No need to look further into packet if there's only
  	 * one path
  	 */
  	if (rt->rt_nhn == 1)
59b209667   David Ahern   net: mpls: change...
227
  		return rt->rt_nh;
c89359a42   Roopa Prabhu   mpls: support for...
228

39eb8cd17   David Ahern   net: mpls: rt_nhn...
229
230
  	alive = READ_ONCE(rt->rt_nhn_alive);
  	if (alive == 0)
c89359a42   Roopa Prabhu   mpls: support for...
231
  		return NULL;
9f427a0e4   David Ahern   net: mpls: Fix mu...
232
  	hash = mpls_multipath_hash(rt, skb);
c89359a42   Roopa Prabhu   mpls: support for...
233
234
235
236
  	nh_index = hash % alive;
  	if (alive == rt->rt_nhn)
  		goto out;
  	for_nexthops(rt) {
39eb8cd17   David Ahern   net: mpls: rt_nhn...
237
238
239
  		unsigned int nh_flags = READ_ONCE(nh->nh_flags);
  
  		if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
c89359a42   Roopa Prabhu   mpls: support for...
240
241
242
243
244
  			continue;
  		if (n == nh_index)
  			return nh;
  		n++;
  	} endfor_nexthops(rt);
1c78efa83   Robert Shearman   mpls: flow-based ...
245
  out:
59b209667   David Ahern   net: mpls: change...
246
  	return mpls_get_nexthop(rt, nh_index);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
247
  }
5b441ac87   Robert Shearman   mpls: allow TTL p...
248
249
  static bool mpls_egress(struct net *net, struct mpls_route *rt,
  			struct sk_buff *skb, struct mpls_entry_decoded dec)
0189197f4   Eric W. Biederman   mpls: Basic routi...
250
  {
118d52346   Robert Shearman   mpls: Enforce pay...
251
252
  	enum mpls_payload_type payload_type;
  	bool success = false;
0189197f4   Eric W. Biederman   mpls: Basic routi...
253

76fecd827   Eric W. Biederman   mpls: In mpls_egr...
254
255
256
257
258
259
260
261
262
263
264
265
  	/* The IPv4 code below accesses through the IPv4 header
  	 * checksum, which is 12 bytes into the packet.
  	 * The IPv6 code below accesses through the IPv6 hop limit
  	 * which is 8 bytes into the packet.
  	 *
  	 * For all supported cases there should always be at least 12
  	 * bytes of packet data present.  The IPv4 header is 20 bytes
  	 * without options and the IPv6 header is always 40 bytes
  	 * long.
  	 */
  	if (!pskb_may_pull(skb, 12))
  		return false;
118d52346   Robert Shearman   mpls: Enforce pay...
266
267
268
269
270
271
272
  	payload_type = rt->rt_payload_type;
  	if (payload_type == MPT_UNSPEC)
  		payload_type = ip_hdr(skb)->version;
  
  	switch (payload_type) {
  	case MPT_IPV4: {
  		struct iphdr *hdr4 = ip_hdr(skb);
5b441ac87   Robert Shearman   mpls: allow TTL p...
273
  		u8 new_ttl;
0189197f4   Eric W. Biederman   mpls: Basic routi...
274
  		skb->protocol = htons(ETH_P_IP);
5b441ac87   Robert Shearman   mpls: allow TTL p...
275
276
277
278
279
280
281
282
283
284
285
  
  		/* If propagating TTL, take the decremented TTL from
  		 * the incoming MPLS header, otherwise decrement the
  		 * TTL, but only if not 0 to avoid underflow.
  		 */
  		if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED ||
  		    (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
  		     net->mpls.ip_ttl_propagate))
  			new_ttl = dec.ttl;
  		else
  			new_ttl = hdr4->ttl ? hdr4->ttl - 1 : 0;
0189197f4   Eric W. Biederman   mpls: Basic routi...
286
287
  		csum_replace2(&hdr4->check,
  			      htons(hdr4->ttl << 8),
5b441ac87   Robert Shearman   mpls: allow TTL p...
288
289
  			      htons(new_ttl << 8));
  		hdr4->ttl = new_ttl;
118d52346   Robert Shearman   mpls: Enforce pay...
290
291
  		success = true;
  		break;
0189197f4   Eric W. Biederman   mpls: Basic routi...
292
  	}
118d52346   Robert Shearman   mpls: Enforce pay...
293
  	case MPT_IPV6: {
0189197f4   Eric W. Biederman   mpls: Basic routi...
294
295
  		struct ipv6hdr *hdr6 = ipv6_hdr(skb);
  		skb->protocol = htons(ETH_P_IPV6);
5b441ac87   Robert Shearman   mpls: allow TTL p...
296
297
298
299
300
301
302
303
304
305
306
  
  		/* If propagating TTL, take the decremented TTL from
  		 * the incoming MPLS header, otherwise decrement the
  		 * hop limit, but only if not 0 to avoid underflow.
  		 */
  		if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED ||
  		    (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT &&
  		     net->mpls.ip_ttl_propagate))
  			hdr6->hop_limit = dec.ttl;
  		else if (hdr6->hop_limit)
  			hdr6->hop_limit = hdr6->hop_limit - 1;
118d52346   Robert Shearman   mpls: Enforce pay...
307
308
  		success = true;
  		break;
0189197f4   Eric W. Biederman   mpls: Basic routi...
309
  	}
118d52346   Robert Shearman   mpls: Enforce pay...
310
  	case MPT_UNSPEC:
5b441ac87   Robert Shearman   mpls: allow TTL p...
311
  		/* Should have decided which protocol it is by now */
118d52346   Robert Shearman   mpls: Enforce pay...
312
313
  		break;
  	}
0189197f4   Eric W. Biederman   mpls: Basic routi...
314
315
316
317
318
319
320
321
322
  	return success;
  }
  
  static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
  			struct packet_type *pt, struct net_device *orig_dev)
  {
  	struct net *net = dev_net(dev);
  	struct mpls_shim_hdr *hdr;
  	struct mpls_route *rt;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
323
  	struct mpls_nh *nh;
0189197f4   Eric W. Biederman   mpls: Basic routi...
324
325
  	struct mpls_entry_decoded dec;
  	struct net_device *out_dev;
27d691056   Robert Shearman   mpls: Packet stats
326
  	struct mpls_dev *out_mdev;
03c57747a   Robert Shearman   mpls: Per-device ...
327
  	struct mpls_dev *mdev;
0189197f4   Eric W. Biederman   mpls: Basic routi...
328
329
330
331
332
333
  	unsigned int hh_len;
  	unsigned int new_header_size;
  	unsigned int mtu;
  	int err;
  
  	/* Careful this entire function runs inside of an rcu critical section */
03c57747a   Robert Shearman   mpls: Per-device ...
334
  	mdev = mpls_dev_get(dev);
27d691056   Robert Shearman   mpls: Packet stats
335
  	if (!mdev)
03c57747a   Robert Shearman   mpls: Per-device ...
336
  		goto drop;
27d691056   Robert Shearman   mpls: Packet stats
337
338
339
340
341
  	MPLS_INC_STATS_LEN(mdev, skb->len, rx_packets,
  			   rx_bytes);
  
  	if (!mdev->input_enabled) {
  		MPLS_INC_STATS(mdev, rx_dropped);
0189197f4   Eric W. Biederman   mpls: Basic routi...
342
  		goto drop;
27d691056   Robert Shearman   mpls: Packet stats
343
344
345
346
  	}
  
  	if (skb->pkt_type != PACKET_HOST)
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
347
348
  
  	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
27d691056   Robert Shearman   mpls: Packet stats
349
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
350
351
  
  	if (!pskb_may_pull(skb, sizeof(*hdr)))
27d691056   Robert Shearman   mpls: Packet stats
352
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
353
354
355
356
  
  	/* Read and decode the label */
  	hdr = mpls_hdr(skb);
  	dec = mpls_entry_decode(hdr);
0189197f4   Eric W. Biederman   mpls: Basic routi...
357
  	rt = mpls_route_input_rcu(net, dec.label);
27d691056   Robert Shearman   mpls: Packet stats
358
359
  	if (!rt) {
  		MPLS_INC_STATS(mdev, rx_noroute);
0189197f4   Eric W. Biederman   mpls: Basic routi...
360
  		goto drop;
27d691056   Robert Shearman   mpls: Packet stats
361
  	}
0189197f4   Eric W. Biederman   mpls: Basic routi...
362

9f427a0e4   David Ahern   net: mpls: Fix mu...
363
  	nh = mpls_select_multipath(rt, skb);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
364
  	if (!nh)
27d691056   Robert Shearman   mpls: Packet stats
365
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
366

9f427a0e4   David Ahern   net: mpls: Fix mu...
367
368
369
370
371
  	/* Pop the label */
  	skb_pull(skb, sizeof(*hdr));
  	skb_reset_network_header(skb);
  
  	skb_orphan(skb);
0189197f4   Eric W. Biederman   mpls: Basic routi...
372
  	if (skb_warn_if_lro(skb))
27d691056   Robert Shearman   mpls: Packet stats
373
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
374
375
376
377
  
  	skb_forward_csum(skb);
  
  	/* Verify ttl is valid */
aa7da9375   Eric W. Biederman   mpls: Correct the...
378
  	if (dec.ttl <= 1)
27d691056   Robert Shearman   mpls: Packet stats
379
  		goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
380
  	dec.ttl -= 1;
27d691056   Robert Shearman   mpls: Packet stats
381
382
383
384
  	/* Find the output device */
  	out_dev = rcu_dereference(nh->nh_dev);
  	if (!mpls_output_possible(out_dev))
  		goto tx_err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
385
  	/* Verify the destination can hold the packet */
f8efb73c9   Roopa Prabhu   mpls: multipath r...
386
  	new_header_size = mpls_nh_header_size(nh);
0189197f4   Eric W. Biederman   mpls: Basic routi...
387
388
  	mtu = mpls_dev_mtu(out_dev);
  	if (mpls_pkt_too_big(skb, mtu - new_header_size))
27d691056   Robert Shearman   mpls: Packet stats
389
  		goto tx_err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
390
391
392
393
394
395
396
  
  	hh_len = LL_RESERVED_SPACE(out_dev);
  	if (!out_dev->header_ops)
  		hh_len = 0;
  
  	/* Ensure there is enough space for the headers in the skb */
  	if (skb_cow(skb, hh_len + new_header_size))
27d691056   Robert Shearman   mpls: Packet stats
397
  		goto tx_err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
398
399
400
401
402
403
  
  	skb->dev = out_dev;
  	skb->protocol = htons(ETH_P_MPLS_UC);
  
  	if (unlikely(!new_header_size && dec.bos)) {
  		/* Penultimate hop popping */
5b441ac87   Robert Shearman   mpls: allow TTL p...
404
  		if (!mpls_egress(dev_net(out_dev), rt, skb, dec))
27d691056   Robert Shearman   mpls: Packet stats
405
  			goto err;
0189197f4   Eric W. Biederman   mpls: Basic routi...
406
407
408
409
410
411
412
413
  	} else {
  		bool bos;
  		int i;
  		skb_push(skb, new_header_size);
  		skb_reset_network_header(skb);
  		/* Push the new labels */
  		hdr = mpls_hdr(skb);
  		bos = dec.bos;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
414
415
416
  		for (i = nh->nh_labels - 1; i >= 0; i--) {
  			hdr[i] = mpls_entry_encode(nh->nh_label[i],
  						   dec.ttl, 0, bos);
0189197f4   Eric W. Biederman   mpls: Basic routi...
417
418
419
  			bos = false;
  		}
  	}
27d691056   Robert Shearman   mpls: Packet stats
420
  	mpls_stats_inc_outucastpkts(out_dev, skb);
eb7809f09   Robert Shearman   mpls: fix out-of-...
421
422
423
424
425
426
427
  	/* If via wasn't specified then send out using device address */
  	if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC)
  		err = neigh_xmit(NEIGH_LINK_TABLE, out_dev,
  				 out_dev->dev_addr, skb);
  	else
  		err = neigh_xmit(nh->nh_via_table, out_dev,
  				 mpls_nh_via(rt, nh), skb);
0189197f4   Eric W. Biederman   mpls: Basic routi...
428
429
430
431
432
  	if (err)
  		net_dbg_ratelimited("%s: packet transmission failed: %d
  ",
  				    __func__, err);
  	return 0;
27d691056   Robert Shearman   mpls: Packet stats
433
434
435
436
437
438
439
  tx_err:
  	out_mdev = out_dev ? mpls_dev_get(out_dev) : NULL;
  	if (out_mdev)
  		MPLS_INC_STATS(out_mdev, tx_errors);
  	goto drop;
  err:
  	MPLS_INC_STATS(mdev, rx_errors);
0189197f4   Eric W. Biederman   mpls: Basic routi...
440
441
442
443
444
445
446
447
448
  drop:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  static struct packet_type mpls_packet_type __read_mostly = {
  	.type = cpu_to_be16(ETH_P_MPLS_UC),
  	.func = mpls_forward,
  };
f0126539c   Wu Fengguang   mpls: rtm_mpls_po...
449
  static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = {
03c056654   Eric W. Biederman   mpls: Netlink com...
450
451
  	[RTA_DST]		= { .type = NLA_U32 },
  	[RTA_OIF]		= { .type = NLA_U32 },
5b441ac87   Robert Shearman   mpls: allow TTL p...
452
  	[RTA_TTL_PROPAGATE]	= { .type = NLA_U8 },
03c056654   Eric W. Biederman   mpls: Netlink com...
453
  };
a2519929a   Eric W. Biederman   mpls: Basic suppo...
454
  struct mpls_route_config {
118d52346   Robert Shearman   mpls: Enforce pay...
455
456
  	u32			rc_protocol;
  	u32			rc_ifindex;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
457
458
  	u8			rc_via_table;
  	u8			rc_via_alen;
118d52346   Robert Shearman   mpls: Enforce pay...
459
460
  	u8			rc_via[MAX_VIA_ALEN];
  	u32			rc_label;
5b441ac87   Robert Shearman   mpls: allow TTL p...
461
  	u8			rc_ttl_propagate;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
462
  	u8			rc_output_labels;
118d52346   Robert Shearman   mpls: Enforce pay...
463
464
465
466
  	u32			rc_output_label[MAX_NEW_LABELS];
  	u32			rc_nlflags;
  	enum mpls_payload_type	rc_payload_type;
  	struct nl_info		rc_nlinfo;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
467
468
  	struct rtnexthop	*rc_mp;
  	int			rc_mp_len;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
469
  };
59b209667   David Ahern   net: mpls: change...
470
471
472
473
  /* all nexthops within a route have the same size based on max
   * number of labels and max via length for a hop
   */
  static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels)
0189197f4   Eric W. Biederman   mpls: Basic routi...
474
  {
59b209667   David Ahern   net: mpls: change...
475
  	u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen);
0189197f4   Eric W. Biederman   mpls: Basic routi...
476
  	struct mpls_route *rt;
df1c63164   David Ahern   net: mpls: Limit ...
477
  	size_t size;
0189197f4   Eric W. Biederman   mpls: Basic routi...
478

df1c63164   David Ahern   net: mpls: Limit ...
479
480
481
482
483
484
485
486
487
488
489
490
  	size = sizeof(*rt) + num_nh * nh_size;
  	if (size > MAX_MPLS_ROUTE_MEM)
  		return ERR_PTR(-EINVAL);
  
  	rt = kzalloc(size, GFP_KERNEL);
  	if (!rt)
  		return ERR_PTR(-ENOMEM);
  
  	rt->rt_nhn = num_nh;
  	rt->rt_nhn_alive = num_nh;
  	rt->rt_nh_size = nh_size;
  	rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
491

0189197f4   Eric W. Biederman   mpls: Basic routi...
492
493
494
495
496
497
498
499
  	return rt;
  }
  
  static void mpls_rt_free(struct mpls_route *rt)
  {
  	if (rt)
  		kfree_rcu(rt, rt_rcu);
  }
8de147dc8   Eric W. Biederman   mpls: Multicast r...
500
501
502
503
504
505
506
507
508
509
  static void mpls_notify_route(struct net *net, unsigned index,
  			      struct mpls_route *old, struct mpls_route *new,
  			      const struct nl_info *info)
  {
  	struct nlmsghdr *nlh = info ? info->nlh : NULL;
  	unsigned portid = info ? info->portid : 0;
  	int event = new ? RTM_NEWROUTE : RTM_DELROUTE;
  	struct mpls_route *rt = new ? new : old;
  	unsigned nlm_flags = (old && new) ? NLM_F_REPLACE : 0;
  	/* Ignore reserved labels for now */
a6affd24f   Robert Shearman   mpls: Use definit...
510
  	if (rt && (index >= MPLS_LABEL_FIRST_UNRESERVED))
8de147dc8   Eric W. Biederman   mpls: Multicast r...
511
512
  		rtmsg_lfib(event, index, rt, nlh, net, portid, nlm_flags);
  }
0189197f4   Eric W. Biederman   mpls: Basic routi...
513
  static void mpls_route_update(struct net *net, unsigned index,
f8efb73c9   Roopa Prabhu   mpls: multipath r...
514
  			      struct mpls_route *new,
0189197f4   Eric W. Biederman   mpls: Basic routi...
515
516
  			      const struct nl_info *info)
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
517
  	struct mpls_route __rcu **platform_label;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
518
  	struct mpls_route *rt;
0189197f4   Eric W. Biederman   mpls: Basic routi...
519
520
  
  	ASSERT_RTNL();
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
521
522
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	rt = rtnl_dereference(platform_label[index]);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
523
  	rcu_assign_pointer(platform_label[index], new);
0189197f4   Eric W. Biederman   mpls: Basic routi...
524

f8efb73c9   Roopa Prabhu   mpls: multipath r...
525
  	mpls_notify_route(net, index, rt, new, info);
8de147dc8   Eric W. Biederman   mpls: Multicast r...
526

0189197f4   Eric W. Biederman   mpls: Basic routi...
527
  	/* If we removed a route free it now */
f8efb73c9   Roopa Prabhu   mpls: multipath r...
528
  	mpls_rt_free(rt);
0189197f4   Eric W. Biederman   mpls: Basic routi...
529
  }
a2519929a   Eric W. Biederman   mpls: Basic suppo...
530
531
  static unsigned find_free_label(struct net *net)
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
532
533
  	struct mpls_route __rcu **platform_label;
  	size_t platform_labels;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
534
  	unsigned index;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
535
536
537
  
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	platform_labels = net->mpls.platform_labels;
a6affd24f   Robert Shearman   mpls: Use definit...
538
539
  	for (index = MPLS_LABEL_FIRST_UNRESERVED; index < platform_labels;
  	     index++) {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
540
  		if (!rtnl_dereference(platform_label[index]))
a2519929a   Eric W. Biederman   mpls: Basic suppo...
541
542
543
544
  			return index;
  	}
  	return LABEL_NOT_SPECIFIED;
  }
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
545
  #if IS_ENABLED(CONFIG_INET)
cf4b24f00   Robert Shearman   mpls: reduce memo...
546
547
  static struct net_device *inet_fib_lookup_dev(struct net *net,
  					      const void *addr)
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
548
  {
5a9348b54   Dan Carpenter   mpls: small clean...
549
  	struct net_device *dev;
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
550
551
552
553
554
555
  	struct rtable *rt;
  	struct in_addr daddr;
  
  	memcpy(&daddr, addr, sizeof(struct in_addr));
  	rt = ip_route_output(net, daddr.s_addr, 0, 0, 0);
  	if (IS_ERR(rt))
5a9348b54   Dan Carpenter   mpls: small clean...
556
  		return ERR_CAST(rt);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
557
558
559
560
561
  
  	dev = rt->dst.dev;
  	dev_hold(dev);
  
  	ip_rt_put(rt);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
562
  	return dev;
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
563
564
  }
  #else
cf4b24f00   Robert Shearman   mpls: reduce memo...
565
566
  static struct net_device *inet_fib_lookup_dev(struct net *net,
  					      const void *addr)
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
567
568
  {
  	return ERR_PTR(-EAFNOSUPPORT);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
569
  }
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
570
  #endif
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
571

bf21563ac   Roopa Prabhu   af_mpls: fix unde...
572
  #if IS_ENABLED(CONFIG_IPV6)
cf4b24f00   Robert Shearman   mpls: reduce memo...
573
574
  static struct net_device *inet6_fib_lookup_dev(struct net *net,
  					       const void *addr)
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
575
  {
5a9348b54   Dan Carpenter   mpls: small clean...
576
  	struct net_device *dev;
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
577
578
  	struct dst_entry *dst;
  	struct flowi6 fl6;
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
579
580
581
  
  	if (!ipv6_stub)
  		return ERR_PTR(-EAFNOSUPPORT);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
582
583
584
  
  	memset(&fl6, 0, sizeof(fl6));
  	memcpy(&fl6.daddr, addr, sizeof(struct in6_addr));
6c8991f41   Sabrina Dubroca   net: ipv6_stub: u...
585
586
587
  	dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &fl6, NULL);
  	if (IS_ERR(dst))
  		return ERR_CAST(dst);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
588
589
590
  
  	dev = dst->dev;
  	dev_hold(dev);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
591
592
593
594
  	dst_release(dst);
  
  	return dev;
  }
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
595
  #else
cf4b24f00   Robert Shearman   mpls: reduce memo...
596
597
  static struct net_device *inet6_fib_lookup_dev(struct net *net,
  					       const void *addr)
bf21563ac   Roopa Prabhu   af_mpls: fix unde...
598
599
600
601
  {
  	return ERR_PTR(-EAFNOSUPPORT);
  }
  #endif
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
602
603
  
  static struct net_device *find_outdev(struct net *net,
cf4b24f00   Robert Shearman   mpls: reduce memo...
604
  				      struct mpls_route *rt,
f8efb73c9   Roopa Prabhu   mpls: multipath r...
605
  				      struct mpls_nh *nh, int oif)
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
606
607
  {
  	struct net_device *dev = NULL;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
608
609
  	if (!oif) {
  		switch (nh->nh_via_table) {
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
610
  		case NEIGH_ARP_TABLE:
cf4b24f00   Robert Shearman   mpls: reduce memo...
611
  			dev = inet_fib_lookup_dev(net, mpls_nh_via(rt, nh));
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
612
613
  			break;
  		case NEIGH_ND_TABLE:
cf4b24f00   Robert Shearman   mpls: reduce memo...
614
  			dev = inet6_fib_lookup_dev(net, mpls_nh_via(rt, nh));
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
615
616
617
618
619
  			break;
  		case NEIGH_LINK_TABLE:
  			break;
  		}
  	} else {
f8efb73c9   Roopa Prabhu   mpls: multipath r...
620
  		dev = dev_get_by_index(net, oif);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
621
  	}
3dcb615e6   Roopa Prabhu   af_mpls: add null...
622
623
  	if (!dev)
  		return ERR_PTR(-ENODEV);
94a57f1f8   Roopa Prabhu   mpls: find_outdev...
624
625
  	if (IS_ERR(dev))
  		return dev;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
626
627
  	/* The caller is holding rtnl anyways, so release the dev reference */
  	dev_put(dev);
01faef2ce   Roopa Prabhu   mpls: make RTA_OI...
628
629
  	return dev;
  }
cf4b24f00   Robert Shearman   mpls: reduce memo...
630
631
  static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
  			      struct mpls_nh *nh, int oif)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
632
633
634
  {
  	struct net_device *dev = NULL;
  	int err = -ENODEV;
cf4b24f00   Robert Shearman   mpls: reduce memo...
635
  	dev = find_outdev(net, rt, nh, oif);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
636
637
638
639
640
641
642
643
644
645
  	if (IS_ERR(dev)) {
  		err = PTR_ERR(dev);
  		dev = NULL;
  		goto errout;
  	}
  
  	/* Ensure this is a supported device */
  	err = -EINVAL;
  	if (!mpls_dev_get(dev))
  		goto errout;
a3e948e83   Robert Shearman   mpls: validate L2...
646
647
648
  	if ((nh->nh_via_table == NEIGH_LINK_TABLE) &&
  	    (dev->addr_len != nh->nh_via_alen))
  		goto errout;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
649
  	RCU_INIT_POINTER(nh->nh_dev, dev);
c89359a42   Roopa Prabhu   mpls: support for...
650
651
652
653
654
655
656
657
658
  	if (!(dev->flags & IFF_UP)) {
  		nh->nh_flags |= RTNH_F_DEAD;
  	} else {
  		unsigned int flags;
  
  		flags = dev_get_flags(dev);
  		if (!(flags & (IFF_RUNNING | IFF_LOWER_UP)))
  			nh->nh_flags |= RTNH_F_LINKDOWN;
  	}
f8efb73c9   Roopa Prabhu   mpls: multipath r...
659
660
661
662
663
  	return 0;
  
  errout:
  	return err;
  }
d4e725600   David Ahern   net: mpls: Make n...
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
  static int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table,
  		       u8 via_addr[], struct netlink_ext_ack *extack)
  {
  	struct rtvia *via = nla_data(nla);
  	int err = -EINVAL;
  	int alen;
  
  	if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) {
  		NL_SET_ERR_MSG_ATTR(extack, nla,
  				    "Invalid attribute length for RTA_VIA");
  		goto errout;
  	}
  	alen = nla_len(nla) -
  			offsetof(struct rtvia, rtvia_addr);
  	if (alen > MAX_VIA_ALEN) {
  		NL_SET_ERR_MSG_ATTR(extack, nla,
  				    "Invalid address length for RTA_VIA");
  		goto errout;
  	}
  
  	/* Validate the address family */
  	switch (via->rtvia_family) {
  	case AF_PACKET:
  		*via_table = NEIGH_LINK_TABLE;
  		break;
  	case AF_INET:
  		*via_table = NEIGH_ARP_TABLE;
  		if (alen != 4)
  			goto errout;
  		break;
  	case AF_INET6:
  		*via_table = NEIGH_ND_TABLE;
  		if (alen != 16)
  			goto errout;
  		break;
  	default:
  		/* Unsupported address family */
  		goto errout;
  	}
  
  	memcpy(via_addr, via->rtvia_addr, alen);
  	*via_alen = alen;
  	err = 0;
  
  errout:
  	return err;
  }
f8efb73c9   Roopa Prabhu   mpls: multipath r...
711
712
713
714
715
716
717
718
719
720
  static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
  				  struct mpls_route *rt)
  {
  	struct net *net = cfg->rc_nlinfo.nl_net;
  	struct mpls_nh *nh = rt->rt_nh;
  	int err;
  	int i;
  
  	if (!nh)
  		return -ENOMEM;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
721
722
723
724
725
  	nh->nh_labels = cfg->rc_output_labels;
  	for (i = 0; i < nh->nh_labels; i++)
  		nh->nh_label[i] = cfg->rc_output_label[i];
  
  	nh->nh_via_table = cfg->rc_via_table;
cf4b24f00   Robert Shearman   mpls: reduce memo...
726
  	memcpy(__mpls_nh_via(rt, nh), cfg->rc_via, cfg->rc_via_alen);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
727
  	nh->nh_via_alen = cfg->rc_via_alen;
cf4b24f00   Robert Shearman   mpls: reduce memo...
728
  	err = mpls_nh_assign_dev(net, rt, nh, cfg->rc_ifindex);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
729
730
  	if (err)
  		goto errout;
c89359a42   Roopa Prabhu   mpls: support for...
731
732
  	if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
  		rt->rt_nhn_alive--;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
733
734
735
736
737
  	return 0;
  
  errout:
  	return err;
  }
cf4b24f00   Robert Shearman   mpls: reduce memo...
738
  static int mpls_nh_build(struct net *net, struct mpls_route *rt,
c89359a42   Roopa Prabhu   mpls: support for...
739
  			 struct mpls_nh *nh, int oif, struct nlattr *via,
074350e2e   David Ahern   net: mpls: Add ex...
740
741
  			 struct nlattr *newdst, u8 max_labels,
  			 struct netlink_ext_ack *extack)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
742
743
744
745
746
747
748
  {
  	int err = -ENOMEM;
  
  	if (!nh)
  		goto errout;
  
  	if (newdst) {
a1f10abe1   David Ahern   net: Fill in exta...
749
  		err = nla_get_labels(newdst, max_labels, &nh->nh_labels,
074350e2e   David Ahern   net: mpls: Add ex...
750
  				     nh->nh_label, extack);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
751
752
753
  		if (err)
  			goto errout;
  	}
f20367df1   Robert Shearman   mpls: make via ad...
754
755
  	if (via) {
  		err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
074350e2e   David Ahern   net: mpls: Add ex...
756
  				  __mpls_nh_via(rt, nh), extack);
f20367df1   Robert Shearman   mpls: make via ad...
757
758
759
760
761
  		if (err)
  			goto errout;
  	} else {
  		nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC;
  	}
f8efb73c9   Roopa Prabhu   mpls: multipath r...
762

cf4b24f00   Robert Shearman   mpls: reduce memo...
763
  	err = mpls_nh_assign_dev(net, rt, nh, oif);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
764
765
766
767
768
769
770
771
  	if (err)
  		goto errout;
  
  	return 0;
  
  errout:
  	return err;
  }
77ef013aa   David Ahern   net: mpls: Conver...
772
  static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len,
a4ac8c986   David Ahern   net: mpls: bump m...
773
774
  			      u8 cfg_via_alen, u8 *max_via_alen,
  			      u8 *max_labels)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
775
  {
f8efb73c9   Roopa Prabhu   mpls: multipath r...
776
  	int remaining = len;
77ef013aa   David Ahern   net: mpls: Conver...
777
  	u8 nhs = 0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
778

cf4b24f00   Robert Shearman   mpls: reduce memo...
779
  	*max_via_alen = 0;
a4ac8c986   David Ahern   net: mpls: bump m...
780
  	*max_labels = 0;
cf4b24f00   Robert Shearman   mpls: reduce memo...
781

f8efb73c9   Roopa Prabhu   mpls: multipath r...
782
  	while (rtnh_ok(rtnh, remaining)) {
cf4b24f00   Robert Shearman   mpls: reduce memo...
783
784
  		struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
  		int attrlen;
a4ac8c986   David Ahern   net: mpls: bump m...
785
  		u8 n_labels = 0;
cf4b24f00   Robert Shearman   mpls: reduce memo...
786
787
788
789
790
791
792
793
794
795
796
797
  
  		attrlen = rtnh_attrlen(rtnh);
  		nla = nla_find(attrs, attrlen, RTA_VIA);
  		if (nla && nla_len(nla) >=
  		    offsetof(struct rtvia, rtvia_addr)) {
  			int via_alen = nla_len(nla) -
  				offsetof(struct rtvia, rtvia_addr);
  
  			if (via_alen <= MAX_VIA_ALEN)
  				*max_via_alen = max_t(u16, *max_via_alen,
  						      via_alen);
  		}
a4ac8c986   David Ahern   net: mpls: bump m...
798
799
  		nla = nla_find(attrs, attrlen, RTA_NEWDST);
  		if (nla &&
a1f10abe1   David Ahern   net: Fill in exta...
800
801
  		    nla_get_labels(nla, MAX_NEW_LABELS, &n_labels,
  				   NULL, NULL) != 0)
a4ac8c986   David Ahern   net: mpls: bump m...
802
803
804
  			return 0;
  
  		*max_labels = max_t(u8, *max_labels, n_labels);
77ef013aa   David Ahern   net: mpls: Conver...
805
806
807
808
809
  		/* number of nexthops is tracked by a u8.
  		 * Check for overflow.
  		 */
  		if (nhs == 255)
  			return 0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
810
  		nhs++;
77ef013aa   David Ahern   net: mpls: Conver...
811

f8efb73c9   Roopa Prabhu   mpls: multipath r...
812
813
814
815
816
817
818
819
  		rtnh = rtnh_next(rtnh, &remaining);
  	}
  
  	/* leftover implies invalid nexthop configuration, discard it */
  	return remaining > 0 ? 0 : nhs;
  }
  
  static int mpls_nh_build_multi(struct mpls_route_config *cfg,
074350e2e   David Ahern   net: mpls: Add ex...
820
821
  			       struct mpls_route *rt, u8 max_labels,
  			       struct netlink_ext_ack *extack)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
822
823
824
825
  {
  	struct rtnexthop *rtnh = cfg->rc_mp;
  	struct nlattr *nla_via, *nla_newdst;
  	int remaining = cfg->rc_mp_len;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
826
  	int err = 0;
77ef013aa   David Ahern   net: mpls: Conver...
827
  	u8 nhs = 0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
828
829
830
831
832
833
834
835
836
837
  
  	change_nexthops(rt) {
  		int attrlen;
  
  		nla_via = NULL;
  		nla_newdst = NULL;
  
  		err = -EINVAL;
  		if (!rtnh_ok(rtnh, remaining))
  			goto errout;
1c78efa83   Robert Shearman   mpls: flow-based ...
838
839
840
841
842
  		/* neither weighted multipath nor any flags
  		 * are supported
  		 */
  		if (rtnh->rtnh_hops || rtnh->rtnh_flags)
  			goto errout;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
843
844
845
846
847
848
849
  		attrlen = rtnh_attrlen(rtnh);
  		if (attrlen > 0) {
  			struct nlattr *attrs = rtnh_attrs(rtnh);
  
  			nla_via = nla_find(attrs, attrlen, RTA_VIA);
  			nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST);
  		}
cf4b24f00   Robert Shearman   mpls: reduce memo...
850
  		err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
a4ac8c986   David Ahern   net: mpls: bump m...
851
  				    rtnh->rtnh_ifindex, nla_via, nla_newdst,
074350e2e   David Ahern   net: mpls: Add ex...
852
  				    max_labels, extack);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
853
854
  		if (err)
  			goto errout;
c89359a42   Roopa Prabhu   mpls: support for...
855
856
  		if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
  			rt->rt_nhn_alive--;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
857
858
859
860
861
862
863
864
865
866
867
  		rtnh = rtnh_next(rtnh, &remaining);
  		nhs++;
  	} endfor_nexthops(rt);
  
  	rt->rt_nhn = nhs;
  
  	return 0;
  
  errout:
  	return err;
  }
3968523f8   Dan Williams   mpls, nospec: San...
868
  static bool mpls_label_ok(struct net *net, unsigned int *index,
074350e2e   David Ahern   net: mpls: Add ex...
869
  			  struct netlink_ext_ack *extack)
b7b386f42   David Ahern   net: mpls: Pull c...
870
  {
3968523f8   Dan Williams   mpls, nospec: San...
871
  	bool is_ok = true;
b7b386f42   David Ahern   net: mpls: Pull c...
872
  	/* Reserved labels may not be set */
3968523f8   Dan Williams   mpls, nospec: San...
873
  	if (*index < MPLS_LABEL_FIRST_UNRESERVED) {
074350e2e   David Ahern   net: mpls: Add ex...
874
875
  		NL_SET_ERR_MSG(extack,
  			       "Invalid label - must be MPLS_LABEL_FIRST_UNRESERVED or higher");
3968523f8   Dan Williams   mpls, nospec: San...
876
  		is_ok = false;
074350e2e   David Ahern   net: mpls: Add ex...
877
  	}
b7b386f42   David Ahern   net: mpls: Pull c...
878
879
  
  	/* The full 20 bit range may not be supported. */
3968523f8   Dan Williams   mpls, nospec: San...
880
  	if (is_ok && *index >= net->mpls.platform_labels) {
074350e2e   David Ahern   net: mpls: Add ex...
881
882
  		NL_SET_ERR_MSG(extack,
  			       "Label >= configured maximum in platform_labels");
3968523f8   Dan Williams   mpls, nospec: San...
883
  		is_ok = false;
074350e2e   David Ahern   net: mpls: Add ex...
884
  	}
b7b386f42   David Ahern   net: mpls: Pull c...
885

3968523f8   Dan Williams   mpls, nospec: San...
886
887
  	*index = array_index_nospec(*index, net->mpls.platform_labels);
  	return is_ok;
b7b386f42   David Ahern   net: mpls: Pull c...
888
  }
074350e2e   David Ahern   net: mpls: Add ex...
889
890
  static int mpls_route_add(struct mpls_route_config *cfg,
  			  struct netlink_ext_ack *extack)
a2519929a   Eric W. Biederman   mpls: Basic suppo...
891
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
892
  	struct mpls_route __rcu **platform_label;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
893
  	struct net *net = cfg->rc_nlinfo.nl_net;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
894
  	struct mpls_route *rt, *old;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
895
  	int err = -EINVAL;
cf4b24f00   Robert Shearman   mpls: reduce memo...
896
  	u8 max_via_alen;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
897
  	unsigned index;
a4ac8c986   David Ahern   net: mpls: bump m...
898
  	u8 max_labels;
77ef013aa   David Ahern   net: mpls: Conver...
899
  	u8 nhs;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
900
901
902
903
904
905
906
907
  
  	index = cfg->rc_label;
  
  	/* If a label was not specified during insert pick one */
  	if ((index == LABEL_NOT_SPECIFIED) &&
  	    (cfg->rc_nlflags & NLM_F_CREATE)) {
  		index = find_free_label(net);
  	}
3968523f8   Dan Williams   mpls, nospec: San...
908
  	if (!mpls_label_ok(net, &index, extack))
a2519929a   Eric W. Biederman   mpls: Basic suppo...
909
  		goto errout;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
910
  	/* Append makes no sense with mpls */
0f7bbd580   Eric W. Biederman   mpls: Better erro...
911
  	err = -EOPNOTSUPP;
074350e2e   David Ahern   net: mpls: Add ex...
912
913
  	if (cfg->rc_nlflags & NLM_F_APPEND) {
  		NL_SET_ERR_MSG(extack, "MPLS does not support route append");
a2519929a   Eric W. Biederman   mpls: Basic suppo...
914
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
915
  	}
a2519929a   Eric W. Biederman   mpls: Basic suppo...
916
917
  
  	err = -EEXIST;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
918
919
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	old = rtnl_dereference(platform_label[index]);
a2519929a   Eric W. Biederman   mpls: Basic suppo...
920
921
922
923
924
925
926
927
928
929
  	if ((cfg->rc_nlflags & NLM_F_EXCL) && old)
  		goto errout;
  
  	err = -EEXIST;
  	if (!(cfg->rc_nlflags & NLM_F_REPLACE) && old)
  		goto errout;
  
  	err = -ENOENT;
  	if (!(cfg->rc_nlflags & NLM_F_CREATE) && !old)
  		goto errout;
cf4b24f00   Robert Shearman   mpls: reduce memo...
930
  	err = -EINVAL;
a4ac8c986   David Ahern   net: mpls: bump m...
931
932
933
934
935
936
937
938
939
  	if (cfg->rc_mp) {
  		nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len,
  					  cfg->rc_via_alen, &max_via_alen,
  					  &max_labels);
  	} else {
  		max_via_alen = cfg->rc_via_alen;
  		max_labels = cfg->rc_output_labels;
  		nhs = 1;
  	}
074350e2e   David Ahern   net: mpls: Add ex...
940
941
  	if (nhs == 0) {
  		NL_SET_ERR_MSG(extack, "Route does not contain a nexthop");
cf4b24f00   Robert Shearman   mpls: reduce memo...
942
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
943
  	}
f8efb73c9   Roopa Prabhu   mpls: multipath r...
944

a2519929a   Eric W. Biederman   mpls: Basic suppo...
945
  	err = -ENOMEM;
a4ac8c986   David Ahern   net: mpls: bump m...
946
  	rt = mpls_rt_alloc(nhs, max_via_alen, max_labels);
df1c63164   David Ahern   net: mpls: Limit ...
947
948
  	if (IS_ERR(rt)) {
  		err = PTR_ERR(rt);
a2519929a   Eric W. Biederman   mpls: Basic suppo...
949
  		goto errout;
df1c63164   David Ahern   net: mpls: Limit ...
950
  	}
a2519929a   Eric W. Biederman   mpls: Basic suppo...
951

a2519929a   Eric W. Biederman   mpls: Basic suppo...
952
  	rt->rt_protocol = cfg->rc_protocol;
118d52346   Robert Shearman   mpls: Enforce pay...
953
  	rt->rt_payload_type = cfg->rc_payload_type;
5b441ac87   Robert Shearman   mpls: allow TTL p...
954
  	rt->rt_ttl_propagate = cfg->rc_ttl_propagate;
a2519929a   Eric W. Biederman   mpls: Basic suppo...
955

f8efb73c9   Roopa Prabhu   mpls: multipath r...
956
  	if (cfg->rc_mp)
074350e2e   David Ahern   net: mpls: Add ex...
957
  		err = mpls_nh_build_multi(cfg, rt, max_labels, extack);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
958
959
960
961
962
963
  	else
  		err = mpls_nh_build_from_cfg(cfg, rt);
  	if (err)
  		goto freert;
  
  	mpls_route_update(net, index, rt, &cfg->rc_nlinfo);
a2519929a   Eric W. Biederman   mpls: Basic suppo...
964

a2519929a   Eric W. Biederman   mpls: Basic suppo...
965
  	return 0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
966
967
  freert:
  	mpls_rt_free(rt);
a2519929a   Eric W. Biederman   mpls: Basic suppo...
968
  errout:
a2519929a   Eric W. Biederman   mpls: Basic suppo...
969
970
  	return err;
  }
074350e2e   David Ahern   net: mpls: Add ex...
971
972
  static int mpls_route_del(struct mpls_route_config *cfg,
  			  struct netlink_ext_ack *extack)
a2519929a   Eric W. Biederman   mpls: Basic suppo...
973
974
975
976
977
978
  {
  	struct net *net = cfg->rc_nlinfo.nl_net;
  	unsigned index;
  	int err = -EINVAL;
  
  	index = cfg->rc_label;
3968523f8   Dan Williams   mpls, nospec: San...
979
  	if (!mpls_label_ok(net, &index, extack))
a2519929a   Eric W. Biederman   mpls: Basic suppo...
980
  		goto errout;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
981
  	mpls_route_update(net, index, NULL, &cfg->rc_nlinfo);
a2519929a   Eric W. Biederman   mpls: Basic suppo...
982
983
984
985
986
  
  	err = 0;
  errout:
  	return err;
  }
27d691056   Robert Shearman   mpls: Packet stats
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  static void mpls_get_stats(struct mpls_dev *mdev,
  			   struct mpls_link_stats *stats)
  {
  	struct mpls_pcpu_stats *p;
  	int i;
  
  	memset(stats, 0, sizeof(*stats));
  
  	for_each_possible_cpu(i) {
  		struct mpls_link_stats local;
  		unsigned int start;
  
  		p = per_cpu_ptr(mdev->stats, i);
  		do {
  			start = u64_stats_fetch_begin(&p->syncp);
  			local = p->stats;
  		} while (u64_stats_fetch_retry(&p->syncp, start));
  
  		stats->rx_packets	+= local.rx_packets;
  		stats->rx_bytes		+= local.rx_bytes;
  		stats->tx_packets	+= local.tx_packets;
  		stats->tx_bytes		+= local.tx_bytes;
  		stats->rx_errors	+= local.rx_errors;
  		stats->tx_errors	+= local.tx_errors;
  		stats->rx_dropped	+= local.rx_dropped;
  		stats->tx_dropped	+= local.tx_dropped;
  		stats->rx_noroute	+= local.rx_noroute;
  	}
  }
  
  static int mpls_fill_stats_af(struct sk_buff *skb,
  			      const struct net_device *dev)
  {
  	struct mpls_link_stats *stats;
  	struct mpls_dev *mdev;
  	struct nlattr *nla;
  
  	mdev = mpls_dev_get(dev);
  	if (!mdev)
  		return -ENODATA;
  
  	nla = nla_reserve_64bit(skb, MPLS_STATS_LINK,
  				sizeof(struct mpls_link_stats),
  				MPLS_STATS_UNSPEC);
  	if (!nla)
  		return -EMSGSIZE;
  
  	stats = nla_data(nla);
  	mpls_get_stats(mdev, stats);
  
  	return 0;
  }
  
  static size_t mpls_get_stats_af_size(const struct net_device *dev)
  {
  	struct mpls_dev *mdev;
  
  	mdev = mpls_dev_get(dev);
  	if (!mdev)
  		return 0;
  
  	return nla_total_size_64bit(sizeof(struct mpls_link_stats));
  }
24045a03b   David Ahern   net: mpls: Add su...
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
  static int mpls_netconf_fill_devconf(struct sk_buff *skb, struct mpls_dev *mdev,
  				     u32 portid, u32 seq, int event,
  				     unsigned int flags, int type)
  {
  	struct nlmsghdr  *nlh;
  	struct netconfmsg *ncm;
  	bool all = false;
  
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
  			flags);
  	if (!nlh)
  		return -EMSGSIZE;
  
  	if (type == NETCONFA_ALL)
  		all = true;
  
  	ncm = nlmsg_data(nlh);
  	ncm->ncm_family = AF_MPLS;
  
  	if (nla_put_s32(skb, NETCONFA_IFINDEX, mdev->dev->ifindex) < 0)
  		goto nla_put_failure;
  
  	if ((all || type == NETCONFA_INPUT) &&
  	    nla_put_s32(skb, NETCONFA_INPUT,
  			mdev->input_enabled) < 0)
  		goto nla_put_failure;
  
  	nlmsg_end(skb, nlh);
  	return 0;
  
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
  }
  
  static int mpls_netconf_msgsize_devconf(int type)
  {
  	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
  			+ nla_total_size(4); /* NETCONFA_IFINDEX */
  	bool all = false;
  
  	if (type == NETCONFA_ALL)
  		all = true;
  
  	if (all || type == NETCONFA_INPUT)
  		size += nla_total_size(4);
  
  	return size;
  }
823566aee   David Ahern   net:mpls: Refacto...
1099
1100
  static void mpls_netconf_notify_devconf(struct net *net, int event,
  					int type, struct mpls_dev *mdev)
24045a03b   David Ahern   net: mpls: Add su...
1101
1102
1103
1104
1105
1106
1107
  {
  	struct sk_buff *skb;
  	int err = -ENOBUFS;
  
  	skb = nlmsg_new(mpls_netconf_msgsize_devconf(type), GFP_KERNEL);
  	if (!skb)
  		goto errout;
823566aee   David Ahern   net:mpls: Refacto...
1108
  	err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, event, 0, type);
24045a03b   David Ahern   net: mpls: Add su...
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
  
  	rtnl_notify(skb, net, 0, RTNLGRP_MPLS_NETCONF, NULL, GFP_KERNEL);
  	return;
  errout:
  	if (err < 0)
  		rtnl_set_sk_err(net, RTNLGRP_MPLS_NETCONF, err);
  }
  
  static const struct nla_policy devconf_mpls_policy[NETCONFA_MAX + 1] = {
  	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
  };
0c4056ee8   Jakub Kicinski   net: mpls: netcon...
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
  static int mpls_netconf_valid_get_req(struct sk_buff *skb,
  				      const struct nlmsghdr *nlh,
  				      struct nlattr **tb,
  				      struct netlink_ext_ack *extack)
  {
  	int i, err;
  
  	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
  		NL_SET_ERR_MSG_MOD(extack,
  				   "Invalid header for netconf get request");
  		return -EINVAL;
  	}
  
  	if (!netlink_strict_get_check(skb))
8cb081746   Johannes Berg   netlink: make val...
1140
1141
1142
  		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
  					      tb, NETCONFA_MAX,
  					      devconf_mpls_policy, extack);
0c4056ee8   Jakub Kicinski   net: mpls: netcon...
1143

8cb081746   Johannes Berg   netlink: make val...
1144
1145
1146
  	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
  					    tb, NETCONFA_MAX,
  					    devconf_mpls_policy, extack);
0c4056ee8   Jakub Kicinski   net: mpls: netcon...
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
  	if (err)
  		return err;
  
  	for (i = 0; i <= NETCONFA_MAX; i++) {
  		if (!tb[i])
  			continue;
  
  		switch (i) {
  		case NETCONFA_IFINDEX:
  			break;
  		default:
  			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request");
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
24045a03b   David Ahern   net: mpls: Add su...
1165
  static int mpls_netconf_get_devconf(struct sk_buff *in_skb,
c21ef3e34   David Ahern   net: rtnetlink: p...
1166
1167
  				    struct nlmsghdr *nlh,
  				    struct netlink_ext_ack *extack)
24045a03b   David Ahern   net: mpls: Add su...
1168
1169
1170
  {
  	struct net *net = sock_net(in_skb->sk);
  	struct nlattr *tb[NETCONFA_MAX + 1];
24045a03b   David Ahern   net: mpls: Add su...
1171
1172
1173
1174
1175
  	struct net_device *dev;
  	struct mpls_dev *mdev;
  	struct sk_buff *skb;
  	int ifindex;
  	int err;
0c4056ee8   Jakub Kicinski   net: mpls: netcon...
1176
  	err = mpls_netconf_valid_get_req(in_skb, nlh, tb, extack);
24045a03b   David Ahern   net: mpls: Add su...
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
  	if (err < 0)
  		goto errout;
  
  	err = -EINVAL;
  	if (!tb[NETCONFA_IFINDEX])
  		goto errout;
  
  	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
  	dev = __dev_get_by_index(net, ifindex);
  	if (!dev)
  		goto errout;
  
  	mdev = mpls_dev_get(dev);
  	if (!mdev)
  		goto errout;
  
  	err = -ENOBUFS;
  	skb = nlmsg_new(mpls_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
  	if (!skb)
  		goto errout;
  
  	err = mpls_netconf_fill_devconf(skb, mdev,
  					NETLINK_CB(in_skb).portid,
  					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
  					NETCONFA_ALL);
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
  	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
  errout:
  	return err;
  }
  
  static int mpls_netconf_dump_devconf(struct sk_buff *skb,
  				     struct netlink_callback *cb)
  {
addd383f5   David Ahern   net: Update netco...
1216
  	const struct nlmsghdr *nlh = cb->nlh;
24045a03b   David Ahern   net: mpls: Add su...
1217
1218
1219
1220
1221
1222
  	struct net *net = sock_net(skb->sk);
  	struct hlist_head *head;
  	struct net_device *dev;
  	struct mpls_dev *mdev;
  	int idx, s_idx;
  	int h, s_h;
addd383f5   David Ahern   net: Update netco...
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
  	if (cb->strict_check) {
  		struct netlink_ext_ack *extack = cb->extack;
  		struct netconfmsg *ncm;
  
  		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
  			NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request");
  			return -EINVAL;
  		}
  
  		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
  			NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request");
  			return -EINVAL;
  		}
  	}
24045a03b   David Ahern   net: mpls: Add su...
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
  	s_h = cb->args[0];
  	s_idx = idx = cb->args[1];
  
  	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
  		idx = 0;
  		head = &net->dev_index_head[h];
  		rcu_read_lock();
  		cb->seq = net->dev_base_seq;
  		hlist_for_each_entry_rcu(dev, head, index_hlist) {
  			if (idx < s_idx)
  				goto cont;
  			mdev = mpls_dev_get(dev);
  			if (!mdev)
  				goto cont;
  			if (mpls_netconf_fill_devconf(skb, mdev,
  						      NETLINK_CB(cb->skb).portid,
addd383f5   David Ahern   net: Update netco...
1253
  						      nlh->nlmsg_seq,
24045a03b   David Ahern   net: mpls: Add su...
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
  						      RTM_NEWNETCONF,
  						      NLM_F_MULTI,
  						      NETCONFA_ALL) < 0) {
  				rcu_read_unlock();
  				goto done;
  			}
  			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
  cont:
  			idx++;
  		}
  		rcu_read_unlock();
  	}
  done:
  	cb->args[0] = h;
  	cb->args[1] = idx;
  
  	return skb->len;
  }
37bde7997   Robert Shearman   mpls: Per-device ...
1272
1273
  #define MPLS_PERDEV_SYSCTL_OFFSET(field)	\
  	(&((struct mpls_dev *)0)->field)
24045a03b   David Ahern   net: mpls: Add su...
1274
  static int mpls_conf_proc(struct ctl_table *ctl, int write,
32927393d   Christoph Hellwig   sysctl: pass kern...
1275
  			  void *buffer, size_t *lenp, loff_t *ppos)
24045a03b   David Ahern   net: mpls: Add su...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
  {
  	int oval = *(int *)ctl->data;
  	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
  
  	if (write) {
  		struct mpls_dev *mdev = ctl->extra1;
  		int i = (int *)ctl->data - (int *)mdev;
  		struct net *net = ctl->extra2;
  		int val = *(int *)ctl->data;
  
  		if (i == offsetof(struct mpls_dev, input_enabled) &&
  		    val != oval) {
823566aee   David Ahern   net:mpls: Refacto...
1288
1289
  			mpls_netconf_notify_devconf(net, RTM_NEWNETCONF,
  						    NETCONFA_INPUT, mdev);
24045a03b   David Ahern   net: mpls: Add su...
1290
1291
1292
1293
1294
  		}
  	}
  
  	return ret;
  }
37bde7997   Robert Shearman   mpls: Per-device ...
1295
1296
1297
1298
1299
  static const struct ctl_table mpls_dev_table[] = {
  	{
  		.procname	= "input",
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
24045a03b   David Ahern   net: mpls: Add su...
1300
  		.proc_handler	= mpls_conf_proc,
37bde7997   Robert Shearman   mpls: Per-device ...
1301
1302
1303
1304
1305
1306
1307
1308
1309
  		.data		= MPLS_PERDEV_SYSCTL_OFFSET(input_enabled),
  	},
  	{ }
  };
  
  static int mpls_dev_sysctl_register(struct net_device *dev,
  				    struct mpls_dev *mdev)
  {
  	char path[sizeof("net/mpls/conf/") + IFNAMSIZ];
24045a03b   David Ahern   net: mpls: Add su...
1310
  	struct net *net = dev_net(dev);
37bde7997   Robert Shearman   mpls: Per-device ...
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
  	struct ctl_table *table;
  	int i;
  
  	table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL);
  	if (!table)
  		goto out;
  
  	/* Table data contains only offsets relative to the base of
  	 * the mdev at this point, so make them absolute.
  	 */
24045a03b   David Ahern   net: mpls: Add su...
1321
  	for (i = 0; i < ARRAY_SIZE(mpls_dev_table); i++) {
37bde7997   Robert Shearman   mpls: Per-device ...
1322
  		table[i].data = (char *)mdev + (uintptr_t)table[i].data;
24045a03b   David Ahern   net: mpls: Add su...
1323
1324
1325
  		table[i].extra1 = mdev;
  		table[i].extra2 = net;
  	}
37bde7997   Robert Shearman   mpls: Per-device ...
1326
1327
  
  	snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name);
1182e4d0b   David Ahern   net: mpls: Send n...
1328
  	mdev->sysctl = register_net_sysctl(net, path, table);
37bde7997   Robert Shearman   mpls: Per-device ...
1329
1330
  	if (!mdev->sysctl)
  		goto free;
1182e4d0b   David Ahern   net: mpls: Send n...
1331
  	mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev);
37bde7997   Robert Shearman   mpls: Per-device ...
1332
1333
1334
1335
1336
1337
1338
  	return 0;
  
  free:
  	kfree(table);
  out:
  	return -ENOBUFS;
  }
1182e4d0b   David Ahern   net: mpls: Send n...
1339
1340
  static void mpls_dev_sysctl_unregister(struct net_device *dev,
  				       struct mpls_dev *mdev)
37bde7997   Robert Shearman   mpls: Per-device ...
1341
  {
1182e4d0b   David Ahern   net: mpls: Send n...
1342
  	struct net *net = dev_net(dev);
37bde7997   Robert Shearman   mpls: Per-device ...
1343
1344
1345
1346
1347
  	struct ctl_table *table;
  
  	table = mdev->sysctl->ctl_table_arg;
  	unregister_net_sysctl_table(mdev->sysctl);
  	kfree(table);
1182e4d0b   David Ahern   net: mpls: Send n...
1348
1349
  
  	mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev);
37bde7997   Robert Shearman   mpls: Per-device ...
1350
  }
03c57747a   Robert Shearman   mpls: Per-device ...
1351
1352
1353
1354
  static struct mpls_dev *mpls_add_dev(struct net_device *dev)
  {
  	struct mpls_dev *mdev;
  	int err = -ENOMEM;
27d691056   Robert Shearman   mpls: Packet stats
1355
  	int i;
03c57747a   Robert Shearman   mpls: Per-device ...
1356
1357
1358
1359
1360
1361
  
  	ASSERT_RTNL();
  
  	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
  	if (!mdev)
  		return ERR_PTR(err);
27d691056   Robert Shearman   mpls: Packet stats
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
  	mdev->stats = alloc_percpu(struct mpls_pcpu_stats);
  	if (!mdev->stats)
  		goto free;
  
  	for_each_possible_cpu(i) {
  		struct mpls_pcpu_stats *mpls_stats;
  
  		mpls_stats = per_cpu_ptr(mdev->stats, i);
  		u64_stats_init(&mpls_stats->syncp);
  	}
1182e4d0b   David Ahern   net: mpls: Send n...
1372
  	mdev->dev = dev;
37bde7997   Robert Shearman   mpls: Per-device ...
1373
1374
1375
  	err = mpls_dev_sysctl_register(dev, mdev);
  	if (err)
  		goto free;
03c57747a   Robert Shearman   mpls: Per-device ...
1376
1377
1378
  	rcu_assign_pointer(dev->mpls_ptr, mdev);
  
  	return mdev;
37bde7997   Robert Shearman   mpls: Per-device ...
1379
1380
  
  free:
27d691056   Robert Shearman   mpls: Packet stats
1381
  	free_percpu(mdev->stats);
37bde7997   Robert Shearman   mpls: Per-device ...
1382
1383
  	kfree(mdev);
  	return ERR_PTR(err);
03c57747a   Robert Shearman   mpls: Per-device ...
1384
  }
27d691056   Robert Shearman   mpls: Packet stats
1385
1386
1387
1388
1389
1390
1391
  static void mpls_dev_destroy_rcu(struct rcu_head *head)
  {
  	struct mpls_dev *mdev = container_of(head, struct mpls_dev, rcu);
  
  	free_percpu(mdev->stats);
  	kfree(mdev);
  }
c89359a42   Roopa Prabhu   mpls: support for...
1392
  static void mpls_ifdown(struct net_device *dev, int event)
0189197f4   Eric W. Biederman   mpls: Basic routi...
1393
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
1394
  	struct mpls_route __rcu **platform_label;
0189197f4   Eric W. Biederman   mpls: Basic routi...
1395
  	struct net *net = dev_net(dev);
77ef013aa   David Ahern   net: mpls: Conver...
1396
  	u8 alive, deleted;
0189197f4   Eric W. Biederman   mpls: Basic routi...
1397
  	unsigned index;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
1398
  	platform_label = rtnl_dereference(net->mpls.platform_label);
0189197f4   Eric W. Biederman   mpls: Basic routi...
1399
  	for (index = 0; index < net->mpls.platform_labels; index++) {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
1400
  		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
c89359a42   Roopa Prabhu   mpls: support for...
1401

0189197f4   Eric W. Biederman   mpls: Basic routi...
1402
1403
  		if (!rt)
  			continue;
c89359a42   Roopa Prabhu   mpls: support for...
1404

61733c91c   David Ahern   net: mpls: Fix ne...
1405
  		alive = 0;
4ea8efadf   David Ahern   net: mpls: Delete...
1406
  		deleted = 0;
c89359a42   Roopa Prabhu   mpls: support for...
1407
  		change_nexthops(rt) {
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1408
  			unsigned int nh_flags = nh->nh_flags;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1409
  			if (rtnl_dereference(nh->nh_dev) != dev)
61733c91c   David Ahern   net: mpls: Fix ne...
1410
  				goto next;
c89359a42   Roopa Prabhu   mpls: support for...
1411
1412
1413
  			switch (event) {
  			case NETDEV_DOWN:
  			case NETDEV_UNREGISTER:
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1414
  				nh_flags |= RTNH_F_DEAD;
c89359a42   Roopa Prabhu   mpls: support for...
1415
1416
  				/* fall through */
  			case NETDEV_CHANGE:
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1417
  				nh_flags |= RTNH_F_LINKDOWN;
c89359a42   Roopa Prabhu   mpls: support for...
1418
1419
1420
1421
  				break;
  			}
  			if (event == NETDEV_UNREGISTER)
  				RCU_INIT_POINTER(nh->nh_dev, NULL);
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1422
1423
1424
  
  			if (nh->nh_flags != nh_flags)
  				WRITE_ONCE(nh->nh_flags, nh_flags);
61733c91c   David Ahern   net: mpls: Fix ne...
1425
  next:
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1426
  			if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)))
61733c91c   David Ahern   net: mpls: Fix ne...
1427
  				alive++;
4ea8efadf   David Ahern   net: mpls: Delete...
1428
1429
  			if (!rtnl_dereference(nh->nh_dev))
  				deleted++;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1430
  		} endfor_nexthops(rt);
61733c91c   David Ahern   net: mpls: Fix ne...
1431
1432
  
  		WRITE_ONCE(rt->rt_nhn_alive, alive);
4ea8efadf   David Ahern   net: mpls: Delete...
1433
1434
1435
1436
  
  		/* if there are no more nexthops, delete the route */
  		if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn)
  			mpls_route_update(net, index, NULL, NULL);
0189197f4   Eric W. Biederman   mpls: Basic routi...
1437
  	}
c89359a42   Roopa Prabhu   mpls: support for...
1438
  }
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1439
  static void mpls_ifup(struct net_device *dev, unsigned int flags)
c89359a42   Roopa Prabhu   mpls: support for...
1440
1441
1442
1443
  {
  	struct mpls_route __rcu **platform_label;
  	struct net *net = dev_net(dev);
  	unsigned index;
77ef013aa   David Ahern   net: mpls: Conver...
1444
  	u8 alive;
c89359a42   Roopa Prabhu   mpls: support for...
1445
1446
1447
1448
1449
1450
1451
  
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	for (index = 0; index < net->mpls.platform_labels; index++) {
  		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
  
  		if (!rt)
  			continue;
37bde7997   Robert Shearman   mpls: Per-device ...
1452

c89359a42   Roopa Prabhu   mpls: support for...
1453
1454
  		alive = 0;
  		change_nexthops(rt) {
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1455
  			unsigned int nh_flags = nh->nh_flags;
c89359a42   Roopa Prabhu   mpls: support for...
1456
1457
  			struct net_device *nh_dev =
  				rtnl_dereference(nh->nh_dev);
03c57747a   Robert Shearman   mpls: Per-device ...
1458

39eb8cd17   David Ahern   net: mpls: rt_nhn...
1459
  			if (!(nh_flags & flags)) {
c89359a42   Roopa Prabhu   mpls: support for...
1460
1461
1462
1463
1464
1465
  				alive++;
  				continue;
  			}
  			if (nh_dev != dev)
  				continue;
  			alive++;
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1466
  			nh_flags &= ~flags;
c2e8471d9   Roopa Prabhu   mpls: fix clearin...
1467
  			WRITE_ONCE(nh->nh_flags, nh_flags);
c89359a42   Roopa Prabhu   mpls: support for...
1468
  		} endfor_nexthops(rt);
39eb8cd17   David Ahern   net: mpls: rt_nhn...
1469
  		WRITE_ONCE(rt->rt_nhn_alive, alive);
c89359a42   Roopa Prabhu   mpls: support for...
1470
  	}
0189197f4   Eric W. Biederman   mpls: Basic routi...
1471
1472
1473
1474
1475
1476
  }
  
  static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
  			   void *ptr)
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
03c57747a   Robert Shearman   mpls: Per-device ...
1477
  	struct mpls_dev *mdev;
c89359a42   Roopa Prabhu   mpls: support for...
1478
  	unsigned int flags;
0189197f4   Eric W. Biederman   mpls: Basic routi...
1479

c89359a42   Roopa Prabhu   mpls: support for...
1480
  	if (event == NETDEV_REGISTER) {
350e7ab92   Martin Varghese   net: Removed the ...
1481
1482
1483
  		mdev = mpls_add_dev(dev);
  		if (IS_ERR(mdev))
  			return notifier_from_errno(PTR_ERR(mdev));
d8e2262a5   Saif Hasan   mpls: allow route...
1484

c89359a42   Roopa Prabhu   mpls: support for...
1485
1486
  		return NOTIFY_OK;
  	}
03c57747a   Robert Shearman   mpls: Per-device ...
1487

c89359a42   Roopa Prabhu   mpls: support for...
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
  	mdev = mpls_dev_get(dev);
  	if (!mdev)
  		return NOTIFY_OK;
  
  	switch (event) {
  	case NETDEV_DOWN:
  		mpls_ifdown(dev, event);
  		break;
  	case NETDEV_UP:
  		flags = dev_get_flags(dev);
  		if (flags & (IFF_RUNNING | IFF_LOWER_UP))
  			mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
  		else
  			mpls_ifup(dev, RTNH_F_DEAD);
  		break;
  	case NETDEV_CHANGE:
  		flags = dev_get_flags(dev);
  		if (flags & (IFF_RUNNING | IFF_LOWER_UP))
  			mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
  		else
  			mpls_ifdown(dev, event);
  		break;
0189197f4   Eric W. Biederman   mpls: Basic routi...
1510
  	case NETDEV_UNREGISTER:
c89359a42   Roopa Prabhu   mpls: support for...
1511
1512
1513
  		mpls_ifdown(dev, event);
  		mdev = mpls_dev_get(dev);
  		if (mdev) {
1182e4d0b   David Ahern   net: mpls: Send n...
1514
  			mpls_dev_sysctl_unregister(dev, mdev);
c89359a42   Roopa Prabhu   mpls: support for...
1515
  			RCU_INIT_POINTER(dev->mpls_ptr, NULL);
27d691056   Robert Shearman   mpls: Packet stats
1516
  			call_rcu(&mdev->rcu, mpls_dev_destroy_rcu);
c89359a42   Roopa Prabhu   mpls: support for...
1517
  		}
0189197f4   Eric W. Biederman   mpls: Basic routi...
1518
  		break;
0fae3bf01   Robert Shearman   mpls: handle devi...
1519
1520
1521
1522
  	case NETDEV_CHANGENAME:
  		mdev = mpls_dev_get(dev);
  		if (mdev) {
  			int err;
1182e4d0b   David Ahern   net: mpls: Send n...
1523
  			mpls_dev_sysctl_unregister(dev, mdev);
0fae3bf01   Robert Shearman   mpls: handle devi...
1524
1525
1526
1527
1528
  			err = mpls_dev_sysctl_register(dev, mdev);
  			if (err)
  				return notifier_from_errno(err);
  		}
  		break;
0189197f4   Eric W. Biederman   mpls: Basic routi...
1529
1530
1531
1532
1533
1534
1535
  	}
  	return NOTIFY_OK;
  }
  
  static struct notifier_block mpls_dev_notifier = {
  	.notifier_call = mpls_dev_notify,
  };
03c056654   Eric W. Biederman   mpls: Netlink com...
1536
  static int nla_put_via(struct sk_buff *skb,
b79bda3d3   Eric W. Biederman   neigh: Use neigh ...
1537
  		       u8 table, const void *addr, int alen)
03c056654   Eric W. Biederman   mpls: Netlink com...
1538
  {
b79bda3d3   Eric W. Biederman   neigh: Use neigh ...
1539
1540
1541
  	static const int table_to_family[NEIGH_NR_TABLES + 1] = {
  		AF_INET, AF_INET6, AF_DECnet, AF_PACKET,
  	};
03c056654   Eric W. Biederman   mpls: Netlink com...
1542
1543
  	struct nlattr *nla;
  	struct rtvia *via;
b79bda3d3   Eric W. Biederman   neigh: Use neigh ...
1544
  	int family = AF_UNSPEC;
03c056654   Eric W. Biederman   mpls: Netlink com...
1545
1546
1547
1548
  
  	nla = nla_reserve(skb, RTA_VIA, alen + 2);
  	if (!nla)
  		return -EMSGSIZE;
b79bda3d3   Eric W. Biederman   neigh: Use neigh ...
1549
1550
  	if (table <= NEIGH_NR_TABLES)
  		family = table_to_family[table];
03c056654   Eric W. Biederman   mpls: Netlink com...
1551
1552
1553
1554
1555
  	via = nla_data(nla);
  	via->rtvia_family = family;
  	memcpy(via->rtvia_addr, addr, alen);
  	return 0;
  }
966bae334   Eric W. Biederman   mpls: Functions f...
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
  int nla_put_labels(struct sk_buff *skb, int attrtype,
  		   u8 labels, const u32 label[])
  {
  	struct nlattr *nla;
  	struct mpls_shim_hdr *nla_label;
  	bool bos;
  	int i;
  	nla = nla_reserve(skb, attrtype, labels*4);
  	if (!nla)
  		return -EMSGSIZE;
  
  	nla_label = nla_data(nla);
  	bos = true;
  	for (i = labels - 1; i >= 0; i--) {
  		nla_label[i] = mpls_entry_encode(label[i], 0, 0, bos);
  		bos = false;
  	}
  
  	return 0;
  }
face0188e   Roopa Prabhu   mpls: export mpls...
1576
  EXPORT_SYMBOL_GPL(nla_put_labels);
966bae334   Eric W. Biederman   mpls: Functions f...
1577

a1f10abe1   David Ahern   net: Fill in exta...
1578
1579
  int nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels,
  		   u32 label[], struct netlink_ext_ack *extack)
966bae334   Eric W. Biederman   mpls: Functions f...
1580
1581
  {
  	unsigned len = nla_len(nla);
966bae334   Eric W. Biederman   mpls: Functions f...
1582
  	struct mpls_shim_hdr *nla_label;
a4ac8c986   David Ahern   net: mpls: bump m...
1583
  	u8 nla_labels;
966bae334   Eric W. Biederman   mpls: Functions f...
1584
1585
  	bool bos;
  	int i;
a4ac8c986   David Ahern   net: mpls: bump m...
1586
1587
1588
  	/* len needs to be an even multiple of 4 (the label size). Number
  	 * of labels is a u8 so check for overflow.
  	 */
a1f10abe1   David Ahern   net: Fill in exta...
1589
1590
1591
  	if (len & 3 || len / 4 > 255) {
  		NL_SET_ERR_MSG_ATTR(extack, nla,
  				    "Invalid length for labels attribute");
966bae334   Eric W. Biederman   mpls: Functions f...
1592
  		return -EINVAL;
a1f10abe1   David Ahern   net: Fill in exta...
1593
  	}
966bae334   Eric W. Biederman   mpls: Functions f...
1594
1595
1596
  
  	/* Limit the number of new labels allowed */
  	nla_labels = len/4;
a1f10abe1   David Ahern   net: Fill in exta...
1597
1598
  	if (nla_labels > max_labels) {
  		NL_SET_ERR_MSG(extack, "Too many labels");
966bae334   Eric W. Biederman   mpls: Functions f...
1599
  		return -EINVAL;
a1f10abe1   David Ahern   net: Fill in exta...
1600
  	}
966bae334   Eric W. Biederman   mpls: Functions f...
1601

a4ac8c986   David Ahern   net: mpls: bump m...
1602
1603
1604
  	/* when label == NULL, caller wants number of labels */
  	if (!label)
  		goto out;
966bae334   Eric W. Biederman   mpls: Functions f...
1605
1606
1607
1608
1609
1610
1611
1612
1613
  	nla_label = nla_data(nla);
  	bos = true;
  	for (i = nla_labels - 1; i >= 0; i--, bos = false) {
  		struct mpls_entry_decoded dec;
  		dec = mpls_entry_decode(nla_label + i);
  
  		/* Ensure the bottom of stack flag is properly set
  		 * and ttl and tc are both clear.
  		 */
a1f10abe1   David Ahern   net: Fill in exta...
1614
1615
1616
1617
1618
1619
1620
1621
1622
  		if (dec.ttl) {
  			NL_SET_ERR_MSG_ATTR(extack, nla,
  					    "TTL in label must be 0");
  			return -EINVAL;
  		}
  
  		if (dec.tc) {
  			NL_SET_ERR_MSG_ATTR(extack, nla,
  					    "Traffic class in label must be 0");
966bae334   Eric W. Biederman   mpls: Functions f...
1623
  			return -EINVAL;
a1f10abe1   David Ahern   net: Fill in exta...
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
  		}
  
  		if (dec.bos != bos) {
  			NL_SET_BAD_ATTR(extack, nla);
  			if (bos) {
  				NL_SET_ERR_MSG(extack,
  					       "BOS bit must be set in first label");
  			} else {
  				NL_SET_ERR_MSG(extack,
  					       "BOS bit can only be set in first label");
  			}
  			return -EINVAL;
  		}
966bae334   Eric W. Biederman   mpls: Functions f...
1637

5a9ab0176   Robert Shearman   mpls: Prevent use...
1638
  		switch (dec.label) {
78f5b8991   Tom Herbert   mpls: Change rese...
1639
  		case MPLS_LABEL_IMPLNULL:
5a9ab0176   Robert Shearman   mpls: Prevent use...
1640
1641
1642
1643
  			/* RFC3032: This is a label that an LSR may
  			 * assign and distribute, but which never
  			 * actually appears in the encapsulation.
  			 */
a1f10abe1   David Ahern   net: Fill in exta...
1644
1645
  			NL_SET_ERR_MSG_ATTR(extack, nla,
  					    "Implicit NULL Label (3) can not be used in encapsulation");
5a9ab0176   Robert Shearman   mpls: Prevent use...
1646
1647
  			return -EINVAL;
  		}
966bae334   Eric W. Biederman   mpls: Functions f...
1648
1649
  		label[i] = dec.label;
  	}
a4ac8c986   David Ahern   net: mpls: bump m...
1650
  out:
966bae334   Eric W. Biederman   mpls: Functions f...
1651
1652
1653
  	*labels = nla_labels;
  	return 0;
  }
face0188e   Roopa Prabhu   mpls: export mpls...
1654
  EXPORT_SYMBOL_GPL(nla_get_labels);
966bae334   Eric W. Biederman   mpls: Functions f...
1655

074350e2e   David Ahern   net: mpls: Add ex...
1656
1657
1658
1659
  static int rtm_to_route_config(struct sk_buff *skb,
  			       struct nlmsghdr *nlh,
  			       struct mpls_route_config *cfg,
  			       struct netlink_ext_ack *extack)
03c056654   Eric W. Biederman   mpls: Netlink com...
1660
1661
1662
1663
1664
  {
  	struct rtmsg *rtm;
  	struct nlattr *tb[RTA_MAX+1];
  	int index;
  	int err;
8cb081746   Johannes Berg   netlink: make val...
1665
1666
  	err = nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
  				     rtm_mpls_policy, extack);
03c056654   Eric W. Biederman   mpls: Netlink com...
1667
1668
1669
1670
1671
  	if (err < 0)
  		goto errout;
  
  	err = -EINVAL;
  	rtm = nlmsg_data(nlh);
03c056654   Eric W. Biederman   mpls: Netlink com...
1672

074350e2e   David Ahern   net: mpls: Add ex...
1673
1674
  	if (rtm->rtm_family != AF_MPLS) {
  		NL_SET_ERR_MSG(extack, "Invalid address family in rtmsg");
03c056654   Eric W. Biederman   mpls: Netlink com...
1675
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1676
1677
1678
  	}
  	if (rtm->rtm_dst_len != 20) {
  		NL_SET_ERR_MSG(extack, "rtm_dst_len must be 20 for MPLS");
03c056654   Eric W. Biederman   mpls: Netlink com...
1679
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1680
1681
1682
  	}
  	if (rtm->rtm_src_len != 0) {
  		NL_SET_ERR_MSG(extack, "rtm_src_len must be 0 for MPLS");
03c056654   Eric W. Biederman   mpls: Netlink com...
1683
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1684
1685
1686
  	}
  	if (rtm->rtm_tos != 0) {
  		NL_SET_ERR_MSG(extack, "rtm_tos must be 0 for MPLS");
03c056654   Eric W. Biederman   mpls: Netlink com...
1687
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1688
1689
1690
1691
  	}
  	if (rtm->rtm_table != RT_TABLE_MAIN) {
  		NL_SET_ERR_MSG(extack,
  			       "MPLS only supports the main route table");
03c056654   Eric W. Biederman   mpls: Netlink com...
1692
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1693
  	}
03c056654   Eric W. Biederman   mpls: Netlink com...
1694
1695
1696
1697
1698
1699
  	/* Any value is acceptable for rtm_protocol */
  
  	/* As mpls uses destination specific addresses
  	 * (or source specific address in the case of multicast)
  	 * all addresses have universal scope.
  	 */
074350e2e   David Ahern   net: mpls: Add ex...
1700
1701
1702
  	if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) {
  		NL_SET_ERR_MSG(extack,
  			       "Invalid route scope  - MPLS only supports UNIVERSE");
03c056654   Eric W. Biederman   mpls: Netlink com...
1703
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1704
1705
1706
1707
  	}
  	if (rtm->rtm_type != RTN_UNICAST) {
  		NL_SET_ERR_MSG(extack,
  			       "Invalid route type - MPLS only supports UNICAST");
03c056654   Eric W. Biederman   mpls: Netlink com...
1708
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1709
1710
1711
  	}
  	if (rtm->rtm_flags != 0) {
  		NL_SET_ERR_MSG(extack, "rtm_flags must be 0 for MPLS");
03c056654   Eric W. Biederman   mpls: Netlink com...
1712
  		goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1713
  	}
03c056654   Eric W. Biederman   mpls: Netlink com...
1714
1715
1716
  
  	cfg->rc_label		= LABEL_NOT_SPECIFIED;
  	cfg->rc_protocol	= rtm->rtm_protocol;
eb7809f09   Robert Shearman   mpls: fix out-of-...
1717
  	cfg->rc_via_table	= MPLS_NEIGH_TABLE_UNSPEC;
5b441ac87   Robert Shearman   mpls: allow TTL p...
1718
  	cfg->rc_ttl_propagate	= MPLS_TTL_PROP_DEFAULT;
03c056654   Eric W. Biederman   mpls: Netlink com...
1719
1720
1721
1722
1723
1724
1725
1726
1727
  	cfg->rc_nlflags		= nlh->nlmsg_flags;
  	cfg->rc_nlinfo.portid	= NETLINK_CB(skb).portid;
  	cfg->rc_nlinfo.nlh	= nlh;
  	cfg->rc_nlinfo.nl_net	= sock_net(skb->sk);
  
  	for (index = 0; index <= RTA_MAX; index++) {
  		struct nlattr *nla = tb[index];
  		if (!nla)
  			continue;
14dd3e1b9   Suraj Deshmukh   net: af_mpls.c ad...
1728
  		switch (index) {
03c056654   Eric W. Biederman   mpls: Netlink com...
1729
1730
1731
1732
1733
1734
  		case RTA_OIF:
  			cfg->rc_ifindex = nla_get_u32(nla);
  			break;
  		case RTA_NEWDST:
  			if (nla_get_labels(nla, MAX_NEW_LABELS,
  					   &cfg->rc_output_labels,
074350e2e   David Ahern   net: mpls: Add ex...
1735
  					   cfg->rc_output_label, extack))
03c056654   Eric W. Biederman   mpls: Netlink com...
1736
1737
1738
1739
  				goto errout;
  			break;
  		case RTA_DST:
  		{
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1740
  			u8 label_count;
03c056654   Eric W. Biederman   mpls: Netlink com...
1741
  			if (nla_get_labels(nla, 1, &label_count,
074350e2e   David Ahern   net: mpls: Add ex...
1742
  					   &cfg->rc_label, extack))
03c056654   Eric W. Biederman   mpls: Netlink com...
1743
  				goto errout;
b7b386f42   David Ahern   net: mpls: Pull c...
1744
  			if (!mpls_label_ok(cfg->rc_nlinfo.nl_net,
3968523f8   Dan Williams   mpls, nospec: San...
1745
  					   &cfg->rc_label, extack))
03c056654   Eric W. Biederman   mpls: Netlink com...
1746
  				goto errout;
03c056654   Eric W. Biederman   mpls: Netlink com...
1747
1748
  			break;
  		}
be48220ed   David Ahern   mpls: Return erro...
1749
1750
1751
  		case RTA_GATEWAY:
  			NL_SET_ERR_MSG(extack, "MPLS does not support RTA_GATEWAY attribute");
  			goto errout;
03c056654   Eric W. Biederman   mpls: Netlink com...
1752
1753
  		case RTA_VIA:
  		{
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1754
  			if (nla_get_via(nla, &cfg->rc_via_alen,
074350e2e   David Ahern   net: mpls: Add ex...
1755
1756
  					&cfg->rc_via_table, cfg->rc_via,
  					extack))
03c056654   Eric W. Biederman   mpls: Netlink com...
1757
  				goto errout;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1758
1759
1760
1761
1762
1763
  			break;
  		}
  		case RTA_MULTIPATH:
  		{
  			cfg->rc_mp = nla_data(nla);
  			cfg->rc_mp_len = nla_len(nla);
03c056654   Eric W. Biederman   mpls: Netlink com...
1764
1765
  			break;
  		}
5b441ac87   Robert Shearman   mpls: allow TTL p...
1766
1767
1768
  		case RTA_TTL_PROPAGATE:
  		{
  			u8 ttl_propagate = nla_get_u8(nla);
074350e2e   David Ahern   net: mpls: Add ex...
1769
1770
1771
  			if (ttl_propagate > 1) {
  				NL_SET_ERR_MSG_ATTR(extack, nla,
  						    "RTA_TTL_PROPAGATE can only be 0 or 1");
5b441ac87   Robert Shearman   mpls: allow TTL p...
1772
  				goto errout;
074350e2e   David Ahern   net: mpls: Add ex...
1773
  			}
5b441ac87   Robert Shearman   mpls: allow TTL p...
1774
1775
1776
1777
1778
  			cfg->rc_ttl_propagate = ttl_propagate ?
  				MPLS_TTL_PROP_ENABLED :
  				MPLS_TTL_PROP_DISABLED;
  			break;
  		}
03c056654   Eric W. Biederman   mpls: Netlink com...
1779
  		default:
074350e2e   David Ahern   net: mpls: Add ex...
1780
  			NL_SET_ERR_MSG_ATTR(extack, nla, "Unknown attribute");
03c056654   Eric W. Biederman   mpls: Netlink com...
1781
1782
1783
1784
1785
1786
1787
1788
1789
  			/* Unsupported attribute */
  			goto errout;
  		}
  	}
  
  	err = 0;
  errout:
  	return err;
  }
c21ef3e34   David Ahern   net: rtnetlink: p...
1790
1791
  static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
  			     struct netlink_ext_ack *extack)
03c056654   Eric W. Biederman   mpls: Netlink com...
1792
  {
a4ac8c986   David Ahern   net: mpls: bump m...
1793
  	struct mpls_route_config *cfg;
03c056654   Eric W. Biederman   mpls: Netlink com...
1794
  	int err;
a4ac8c986   David Ahern   net: mpls: bump m...
1795
1796
1797
  	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
  	if (!cfg)
  		return -ENOMEM;
074350e2e   David Ahern   net: mpls: Add ex...
1798
  	err = rtm_to_route_config(skb, nlh, cfg, extack);
03c056654   Eric W. Biederman   mpls: Netlink com...
1799
  	if (err < 0)
a4ac8c986   David Ahern   net: mpls: bump m...
1800
  		goto out;
03c056654   Eric W. Biederman   mpls: Netlink com...
1801

074350e2e   David Ahern   net: mpls: Add ex...
1802
  	err = mpls_route_del(cfg, extack);
a4ac8c986   David Ahern   net: mpls: bump m...
1803
1804
1805
1806
  out:
  	kfree(cfg);
  
  	return err;
03c056654   Eric W. Biederman   mpls: Netlink com...
1807
  }
c21ef3e34   David Ahern   net: rtnetlink: p...
1808
1809
  static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
  			     struct netlink_ext_ack *extack)
03c056654   Eric W. Biederman   mpls: Netlink com...
1810
  {
a4ac8c986   David Ahern   net: mpls: bump m...
1811
  	struct mpls_route_config *cfg;
03c056654   Eric W. Biederman   mpls: Netlink com...
1812
  	int err;
a4ac8c986   David Ahern   net: mpls: bump m...
1813
1814
1815
  	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
  	if (!cfg)
  		return -ENOMEM;
074350e2e   David Ahern   net: mpls: Add ex...
1816
  	err = rtm_to_route_config(skb, nlh, cfg, extack);
03c056654   Eric W. Biederman   mpls: Netlink com...
1817
  	if (err < 0)
a4ac8c986   David Ahern   net: mpls: bump m...
1818
  		goto out;
03c056654   Eric W. Biederman   mpls: Netlink com...
1819

074350e2e   David Ahern   net: mpls: Add ex...
1820
  	err = mpls_route_add(cfg, extack);
a4ac8c986   David Ahern   net: mpls: bump m...
1821
1822
1823
1824
  out:
  	kfree(cfg);
  
  	return err;
03c056654   Eric W. Biederman   mpls: Netlink com...
1825
1826
1827
1828
1829
  }
  
  static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
  			   u32 label, struct mpls_route *rt, int flags)
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
1830
  	struct net_device *dev;
03c056654   Eric W. Biederman   mpls: Netlink com...
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
  	struct nlmsghdr *nlh;
  	struct rtmsg *rtm;
  
  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags);
  	if (nlh == NULL)
  		return -EMSGSIZE;
  
  	rtm = nlmsg_data(nlh);
  	rtm->rtm_family = AF_MPLS;
  	rtm->rtm_dst_len = 20;
  	rtm->rtm_src_len = 0;
  	rtm->rtm_tos = 0;
  	rtm->rtm_table = RT_TABLE_MAIN;
  	rtm->rtm_protocol = rt->rt_protocol;
  	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
  	rtm->rtm_type = RTN_UNICAST;
  	rtm->rtm_flags = 0;
03c056654   Eric W. Biederman   mpls: Netlink com...
1848
1849
  	if (nla_put_labels(skb, RTA_DST, 1, &label))
  		goto nla_put_failure;
5b441ac87   Robert Shearman   mpls: allow TTL p...
1850
1851
1852
1853
1854
1855
1856
1857
1858
  
  	if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) {
  		bool ttl_propagate =
  			rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED;
  
  		if (nla_put_u8(skb, RTA_TTL_PROPAGATE,
  			       ttl_propagate))
  			goto nla_put_failure;
  	}
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1859
  	if (rt->rt_nhn == 1) {
cf4b24f00   Robert Shearman   mpls: reduce memo...
1860
  		const struct mpls_nh *nh = rt->rt_nh;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1861
1862
1863
1864
1865
  
  		if (nh->nh_labels &&
  		    nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
  				   nh->nh_label))
  			goto nla_put_failure;
eb7809f09   Robert Shearman   mpls: fix out-of-...
1866
  		if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
72dcac96c   Robert Shearman   mpls: don't dump ...
1867
  		    nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1868
1869
1870
1871
1872
  				nh->nh_via_alen))
  			goto nla_put_failure;
  		dev = rtnl_dereference(nh->nh_dev);
  		if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
  			goto nla_put_failure;
c89359a42   Roopa Prabhu   mpls: support for...
1873
1874
1875
1876
  		if (nh->nh_flags & RTNH_F_LINKDOWN)
  			rtm->rtm_flags |= RTNH_F_LINKDOWN;
  		if (nh->nh_flags & RTNH_F_DEAD)
  			rtm->rtm_flags |= RTNH_F_DEAD;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1877
1878
1879
  	} else {
  		struct rtnexthop *rtnh;
  		struct nlattr *mp;
77ef013aa   David Ahern   net: mpls: Conver...
1880
1881
  		u8 linkdown = 0;
  		u8 dead = 0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1882

ae0be8de9   Michal Kubecek   netlink: make nla...
1883
  		mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1884
1885
1886
1887
  		if (!mp)
  			goto nla_put_failure;
  
  		for_nexthops(rt) {
c00e51ddb   David Ahern   net: mpls: Don't ...
1888
1889
1890
  			dev = rtnl_dereference(nh->nh_dev);
  			if (!dev)
  				continue;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1891
1892
1893
  			rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
  			if (!rtnh)
  				goto nla_put_failure;
c00e51ddb   David Ahern   net: mpls: Don't ...
1894
  			rtnh->rtnh_ifindex = dev->ifindex;
c89359a42   Roopa Prabhu   mpls: support for...
1895
1896
1897
1898
1899
1900
1901
1902
  			if (nh->nh_flags & RTNH_F_LINKDOWN) {
  				rtnh->rtnh_flags |= RTNH_F_LINKDOWN;
  				linkdown++;
  			}
  			if (nh->nh_flags & RTNH_F_DEAD) {
  				rtnh->rtnh_flags |= RTNH_F_DEAD;
  				dead++;
  			}
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1903
1904
1905
1906
  			if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST,
  							    nh->nh_labels,
  							    nh->nh_label))
  				goto nla_put_failure;
f20367df1   Robert Shearman   mpls: make via ad...
1907
1908
  			if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
  			    nla_put_via(skb, nh->nh_via_table,
cf4b24f00   Robert Shearman   mpls: reduce memo...
1909
  					mpls_nh_via(rt, nh),
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1910
1911
1912
1913
1914
1915
  					nh->nh_via_alen))
  				goto nla_put_failure;
  
  			/* length of rtnetlink header + attributes */
  			rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
  		} endfor_nexthops(rt);
c89359a42   Roopa Prabhu   mpls: support for...
1916
1917
1918
1919
  		if (linkdown == rt->rt_nhn)
  			rtm->rtm_flags |= RTNH_F_LINKDOWN;
  		if (dead == rt->rt_nhn)
  			rtm->rtm_flags |= RTNH_F_DEAD;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
1920
1921
  		nla_nest_end(skb, mp);
  	}
03c056654   Eric W. Biederman   mpls: Netlink com...
1922
1923
1924
1925
1926
1927
1928
1929
  
  	nlmsg_end(skb, nlh);
  	return 0;
  
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	return -EMSGSIZE;
  }
d8a66aa25   David Ahern   net/mpls: Impleme...
1930
  #if IS_ENABLED(CONFIG_INET)
4724676d5   David Ahern   net: Add struct f...
1931
1932
  static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
  				   struct fib_dump_filter *filter,
effe67926   David Ahern   net: Enable kerne...
1933
  				   struct netlink_callback *cb)
d8a66aa25   David Ahern   net/mpls: Impleme...
1934
  {
effe67926   David Ahern   net: Enable kerne...
1935
  	return ip_valid_fib_dump_req(net, nlh, filter, cb);
d8a66aa25   David Ahern   net/mpls: Impleme...
1936
1937
  }
  #else
4724676d5   David Ahern   net: Add struct f...
1938
1939
  static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
  				   struct fib_dump_filter *filter,
effe67926   David Ahern   net: Enable kerne...
1940
  				   struct netlink_callback *cb)
d8a66aa25   David Ahern   net/mpls: Impleme...
1941
  {
effe67926   David Ahern   net: Enable kerne...
1942
  	struct netlink_ext_ack *extack = cb->extack;
196cfebf8   David Ahern   net/mpls: Handle ...
1943
  	struct nlattr *tb[RTA_MAX + 1];
d8a66aa25   David Ahern   net/mpls: Impleme...
1944
  	struct rtmsg *rtm;
196cfebf8   David Ahern   net/mpls: Handle ...
1945
  	int err, i;
d8a66aa25   David Ahern   net/mpls: Impleme...
1946
1947
1948
1949
1950
1951
1952
1953
  
  	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
  		NL_SET_ERR_MSG_MOD(extack, "Invalid header for FIB dump request");
  		return -EINVAL;
  	}
  
  	rtm = nlmsg_data(nlh);
  	if (rtm->rtm_dst_len || rtm->rtm_src_len  || rtm->rtm_tos   ||
196cfebf8   David Ahern   net/mpls: Handle ...
1954
1955
  	    rtm->rtm_table   || rtm->rtm_scope    || rtm->rtm_type  ||
  	    rtm->rtm_flags) {
d8a66aa25   David Ahern   net/mpls: Impleme...
1956
1957
1958
  		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for FIB dump request");
  		return -EINVAL;
  	}
196cfebf8   David Ahern   net/mpls: Handle ...
1959
1960
1961
1962
1963
  	if (rtm->rtm_protocol) {
  		filter->protocol = rtm->rtm_protocol;
  		filter->filter_set = 1;
  		cb->answer_flags = NLM_F_DUMP_FILTERED;
  	}
8cb081746   Johannes Berg   netlink: make val...
1964
1965
  	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
  					    rtm_mpls_policy, extack);
196cfebf8   David Ahern   net/mpls: Handle ...
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
  	if (err < 0)
  		return err;
  
  	for (i = 0; i <= RTA_MAX; ++i) {
  		int ifindex;
  
  		if (i == RTA_OIF) {
  			ifindex = nla_get_u32(tb[i]);
  			filter->dev = __dev_get_by_index(net, ifindex);
  			if (!filter->dev)
  				return -ENODEV;
  			filter->filter_set = 1;
  		} else if (tb[i]) {
  			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request");
  			return -EINVAL;
  		}
d8a66aa25   David Ahern   net/mpls: Impleme...
1982
1983
1984
1985
1986
  	}
  
  	return 0;
  }
  #endif
bae9a78b1   David Ahern   net/mpls: Plumb s...
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
  static bool mpls_rt_uses_dev(struct mpls_route *rt,
  			     const struct net_device *dev)
  {
  	struct net_device *nh_dev;
  
  	if (rt->rt_nhn == 1) {
  		struct mpls_nh *nh = rt->rt_nh;
  
  		nh_dev = rtnl_dereference(nh->nh_dev);
  		if (dev == nh_dev)
  			return true;
  	} else {
  		for_nexthops(rt) {
  			nh_dev = rtnl_dereference(nh->nh_dev);
  			if (nh_dev == dev)
  				return true;
  		} endfor_nexthops(rt);
  	}
  
  	return false;
  }
03c056654   Eric W. Biederman   mpls: Netlink com...
2008
2009
  static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
  {
e8ba330ac   David Ahern   rtnetlink: Update...
2010
  	const struct nlmsghdr *nlh = cb->nlh;
03c056654   Eric W. Biederman   mpls: Netlink com...
2011
  	struct net *net = sock_net(skb->sk);
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2012
  	struct mpls_route __rcu **platform_label;
4724676d5   David Ahern   net: Add struct f...
2013
  	struct fib_dump_filter filter = {};
bae9a78b1   David Ahern   net/mpls: Plumb s...
2014
  	unsigned int flags = NLM_F_MULTI;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2015
  	size_t platform_labels;
03c056654   Eric W. Biederman   mpls: Netlink com...
2016
2017
2018
  	unsigned int index;
  
  	ASSERT_RTNL();
e8ba330ac   David Ahern   rtnetlink: Update...
2019
  	if (cb->strict_check) {
4724676d5   David Ahern   net: Add struct f...
2020
  		int err;
e8ba330ac   David Ahern   rtnetlink: Update...
2021

effe67926   David Ahern   net: Enable kerne...
2022
  		err = mpls_valid_fib_dump_req(net, nlh, &filter, cb);
e8ba330ac   David Ahern   rtnetlink: Update...
2023
2024
  		if (err < 0)
  			return err;
bae9a78b1   David Ahern   net/mpls: Plumb s...
2025
2026
2027
2028
2029
2030
2031
2032
  
  		/* for MPLS, there is only 1 table with fixed type and flags.
  		 * If either are set in the filter then return nothing.
  		 */
  		if ((filter.table_id && filter.table_id != RT_TABLE_MAIN) ||
  		    (filter.rt_type && filter.rt_type != RTN_UNICAST) ||
  		     filter.flags)
  			return skb->len;
e8ba330ac   David Ahern   rtnetlink: Update...
2033
  	}
03c056654   Eric W. Biederman   mpls: Netlink com...
2034
  	index = cb->args[0];
a6affd24f   Robert Shearman   mpls: Use definit...
2035
2036
  	if (index < MPLS_LABEL_FIRST_UNRESERVED)
  		index = MPLS_LABEL_FIRST_UNRESERVED;
03c056654   Eric W. Biederman   mpls: Netlink com...
2037

19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2038
2039
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	platform_labels = net->mpls.platform_labels;
bae9a78b1   David Ahern   net/mpls: Plumb s...
2040
2041
2042
  
  	if (filter.filter_set)
  		flags |= NLM_F_DUMP_FILTERED;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2043
  	for (; index < platform_labels; index++) {
03c056654   Eric W. Biederman   mpls: Netlink com...
2044
  		struct mpls_route *rt;
bae9a78b1   David Ahern   net/mpls: Plumb s...
2045

19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2046
  		rt = rtnl_dereference(platform_label[index]);
03c056654   Eric W. Biederman   mpls: Netlink com...
2047
2048
  		if (!rt)
  			continue;
bae9a78b1   David Ahern   net/mpls: Plumb s...
2049
2050
2051
  		if ((filter.dev && !mpls_rt_uses_dev(rt, filter.dev)) ||
  		    (filter.protocol && rt->rt_protocol != filter.protocol))
  			continue;
03c056654   Eric W. Biederman   mpls: Netlink com...
2052
2053
  		if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid,
  				    cb->nlh->nlmsg_seq, RTM_NEWROUTE,
bae9a78b1   David Ahern   net/mpls: Plumb s...
2054
  				    index, rt, flags) < 0)
03c056654   Eric W. Biederman   mpls: Netlink com...
2055
2056
2057
2058
2059
2060
  			break;
  	}
  	cb->args[0] = index;
  
  	return skb->len;
  }
8de147dc8   Eric W. Biederman   mpls: Multicast r...
2061
2062
2063
2064
  static inline size_t lfib_nlmsg_size(struct mpls_route *rt)
  {
  	size_t payload =
  		NLMSG_ALIGN(sizeof(struct rtmsg))
5b441ac87   Robert Shearman   mpls: allow TTL p...
2065
2066
  		+ nla_total_size(4)			/* RTA_DST */
  		+ nla_total_size(1);			/* RTA_TTL_PROPAGATE */
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2067
2068
2069
2070
2071
2072
  
  	if (rt->rt_nhn == 1) {
  		struct mpls_nh *nh = rt->rt_nh;
  
  		if (nh->nh_dev)
  			payload += nla_total_size(4); /* RTA_OIF */
eb7809f09   Robert Shearman   mpls: fix out-of-...
2073
  		if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */
72dcac96c   Robert Shearman   mpls: don't dump ...
2074
  			payload += nla_total_size(2 + nh->nh_via_alen);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2075
2076
2077
2078
2079
2080
2081
  		if (nh->nh_labels) /* RTA_NEWDST */
  			payload += nla_total_size(nh->nh_labels * 4);
  	} else {
  		/* each nexthop is packed in an attribute */
  		size_t nhsize = 0;
  
  		for_nexthops(rt) {
e944e97af   David Ahern   net: mpls: Update...
2082
2083
  			if (!rtnl_dereference(nh->nh_dev))
  				continue;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2084
  			nhsize += nla_total_size(sizeof(struct rtnexthop));
f20367df1   Robert Shearman   mpls: make via ad...
2085
2086
2087
  			/* RTA_VIA */
  			if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC)
  				nhsize += nla_total_size(2 + nh->nh_via_alen);
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2088
2089
2090
2091
2092
2093
  			if (nh->nh_labels)
  				nhsize += nla_total_size(nh->nh_labels * 4);
  		} endfor_nexthops(rt);
  		/* nested attribute */
  		payload += nla_total_size(nhsize);
  	}
8de147dc8   Eric W. Biederman   mpls: Multicast r...
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
  	return payload;
  }
  
  static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt,
  		       struct nlmsghdr *nlh, struct net *net, u32 portid,
  		       unsigned int nlm_flags)
  {
  	struct sk_buff *skb;
  	u32 seq = nlh ? nlh->nlmsg_seq : 0;
  	int err = -ENOBUFS;
  
  	skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL);
  	if (skb == NULL)
  		goto errout;
  
  	err = mpls_dump_route(skb, portid, seq, event, label, rt, nlm_flags);
  	if (err < 0) {
  		/* -EMSGSIZE implies BUG in lfib_nlmsg_size */
  		WARN_ON(err == -EMSGSIZE);
  		kfree_skb(skb);
  		goto errout;
  	}
  	rtnl_notify(skb, net, portid, RTNLGRP_MPLS_ROUTE, nlh, GFP_KERNEL);
  
  	return;
  errout:
  	if (err < 0)
  		rtnl_set_sk_err(net, RTNLGRP_MPLS_ROUTE, err);
  }
d77851bf6   Jakub Kicinski   net: mpls: route:...
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
  static int mpls_valid_getroute_req(struct sk_buff *skb,
  				   const struct nlmsghdr *nlh,
  				   struct nlattr **tb,
  				   struct netlink_ext_ack *extack)
  {
  	struct rtmsg *rtm;
  	int i, err;
  
  	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
  		NL_SET_ERR_MSG_MOD(extack,
  				   "Invalid header for get route request");
  		return -EINVAL;
  	}
  
  	if (!netlink_strict_get_check(skb))
8cb081746   Johannes Berg   netlink: make val...
2138
2139
  		return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX,
  					      rtm_mpls_policy, extack);
d77851bf6   Jakub Kicinski   net: mpls: route:...
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
  
  	rtm = nlmsg_data(nlh);
  	if ((rtm->rtm_dst_len && rtm->rtm_dst_len != 20) ||
  	    rtm->rtm_src_len || rtm->rtm_tos || rtm->rtm_table ||
  	    rtm->rtm_protocol || rtm->rtm_scope || rtm->rtm_type) {
  		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get route request");
  		return -EINVAL;
  	}
  	if (rtm->rtm_flags & ~RTM_F_FIB_MATCH) {
  		NL_SET_ERR_MSG_MOD(extack,
  				   "Invalid flags for get route request");
  		return -EINVAL;
  	}
8cb081746   Johannes Berg   netlink: make val...
2153
2154
  	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
  					    rtm_mpls_policy, extack);
d77851bf6   Jakub Kicinski   net: mpls: route:...
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
  	if (err)
  		return err;
  
  	if ((tb[RTA_DST] || tb[RTA_NEWDST]) && !rtm->rtm_dst_len) {
  		NL_SET_ERR_MSG_MOD(extack, "rtm_dst_len must be 20 for MPLS");
  		return -EINVAL;
  	}
  
  	for (i = 0; i <= RTA_MAX; i++) {
  		if (!tb[i])
  			continue;
  
  		switch (i) {
  		case RTA_DST:
  		case RTA_NEWDST:
  			break;
  		default:
  			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get route request");
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
397fc9e5c   Roopa Prabhu   mpls: route get s...
2179
2180
2181
2182
2183
  static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
  			 struct netlink_ext_ack *extack)
  {
  	struct net *net = sock_net(in_skb->sk);
  	u32 portid = NETLINK_CB(in_skb).portid;
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2184
  	u32 in_label = LABEL_NOT_SPECIFIED;
397fc9e5c   Roopa Prabhu   mpls: route get s...
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
  	struct nlattr *tb[RTA_MAX + 1];
  	u32 labels[MAX_NEW_LABELS];
  	struct mpls_shim_hdr *hdr;
  	unsigned int hdr_size = 0;
  	struct net_device *dev;
  	struct mpls_route *rt;
  	struct rtmsg *rtm, *r;
  	struct nlmsghdr *nlh;
  	struct sk_buff *skb;
  	struct mpls_nh *nh;
397fc9e5c   Roopa Prabhu   mpls: route get s...
2195
  	u8 n_labels;
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2196
  	int err;
397fc9e5c   Roopa Prabhu   mpls: route get s...
2197

d77851bf6   Jakub Kicinski   net: mpls: route:...
2198
  	err = mpls_valid_getroute_req(in_skb, in_nlh, tb, extack);
397fc9e5c   Roopa Prabhu   mpls: route get s...
2199
2200
2201
2202
2203
2204
2205
2206
2207
  	if (err < 0)
  		goto errout;
  
  	rtm = nlmsg_data(in_nlh);
  
  	if (tb[RTA_DST]) {
  		u8 label_count;
  
  		if (nla_get_labels(tb[RTA_DST], 1, &label_count,
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2208
2209
  				   &in_label, extack)) {
  			err = -EINVAL;
397fc9e5c   Roopa Prabhu   mpls: route get s...
2210
  			goto errout;
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2211
  		}
397fc9e5c   Roopa Prabhu   mpls: route get s...
2212

3968523f8   Dan Williams   mpls, nospec: San...
2213
  		if (!mpls_label_ok(net, &in_label, extack)) {
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2214
  			err = -EINVAL;
397fc9e5c   Roopa Prabhu   mpls: route get s...
2215
  			goto errout;
a906c1aa4   Roopa Prabhu   mpls: fix uniniti...
2216
  		}
397fc9e5c   Roopa Prabhu   mpls: route get s...
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
  	}
  
  	rt = mpls_route_input_rcu(net, in_label);
  	if (!rt) {
  		err = -ENETUNREACH;
  		goto errout;
  	}
  
  	if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
  		skb = nlmsg_new(lfib_nlmsg_size(rt), GFP_KERNEL);
  		if (!skb) {
  			err = -ENOBUFS;
  			goto errout;
  		}
  
  		err = mpls_dump_route(skb, portid, in_nlh->nlmsg_seq,
  				      RTM_NEWROUTE, in_label, rt, 0);
  		if (err < 0) {
  			/* -EMSGSIZE implies BUG in lfib_nlmsg_size */
  			WARN_ON(err == -EMSGSIZE);
  			goto errout_free;
  		}
  
  		return rtnl_unicast(skb, net, portid);
  	}
  
  	if (tb[RTA_NEWDST]) {
  		if (nla_get_labels(tb[RTA_NEWDST], MAX_NEW_LABELS, &n_labels,
  				   labels, extack) != 0) {
  			err = -EINVAL;
  			goto errout;
  		}
  
  		hdr_size = n_labels * sizeof(struct mpls_shim_hdr);
  	}
  
  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  	if (!skb) {
  		err = -ENOBUFS;
  		goto errout;
  	}
  
  	skb->protocol = htons(ETH_P_MPLS_UC);
  
  	if (hdr_size) {
  		bool bos;
  		int i;
  
  		if (skb_cow(skb, hdr_size)) {
  			err = -ENOBUFS;
  			goto errout_free;
  		}
  
  		skb_reserve(skb, hdr_size);
  		skb_push(skb, hdr_size);
  		skb_reset_network_header(skb);
  
  		/* Push new labels */
  		hdr = mpls_hdr(skb);
  		bos = true;
  		for (i = n_labels - 1; i >= 0; i--) {
  			hdr[i] = mpls_entry_encode(labels[i],
  						   1, 0, bos);
  			bos = false;
  		}
  	}
  
  	nh = mpls_select_multipath(rt, skb);
  	if (!nh) {
  		err = -ENETUNREACH;
  		goto errout_free;
  	}
  
  	if (hdr_size) {
  		skb_pull(skb, hdr_size);
  		skb_reset_network_header(skb);
  	}
  
  	nlh = nlmsg_put(skb, portid, in_nlh->nlmsg_seq,
  			RTM_NEWROUTE, sizeof(*r), 0);
  	if (!nlh) {
  		err = -EMSGSIZE;
  		goto errout_free;
  	}
  
  	r = nlmsg_data(nlh);
  	r->rtm_family	 = AF_MPLS;
  	r->rtm_dst_len	= 20;
  	r->rtm_src_len	= 0;
  	r->rtm_table	= RT_TABLE_MAIN;
  	r->rtm_type	= RTN_UNICAST;
  	r->rtm_scope	= RT_SCOPE_UNIVERSE;
  	r->rtm_protocol = rt->rt_protocol;
  	r->rtm_flags	= 0;
  
  	if (nla_put_labels(skb, RTA_DST, 1, &in_label))
  		goto nla_put_failure;
  
  	if (nh->nh_labels &&
  	    nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
  			   nh->nh_label))
  		goto nla_put_failure;
  
  	if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
  	    nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
  			nh->nh_via_alen))
  		goto nla_put_failure;
  	dev = rtnl_dereference(nh->nh_dev);
  	if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
  		goto nla_put_failure;
  
  	nlmsg_end(skb, nlh);
  
  	err = rtnl_unicast(skb, net, portid);
  errout:
  	return err;
  
  nla_put_failure:
  	nlmsg_cancel(skb, nlh);
  	err = -EMSGSIZE;
  errout_free:
  	kfree_skb(skb);
  	return err;
  }
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
  static int resize_platform_label_table(struct net *net, size_t limit)
  {
  	size_t size = sizeof(struct mpls_route *) * limit;
  	size_t old_limit;
  	size_t cp_size;
  	struct mpls_route __rcu **labels = NULL, **old;
  	struct mpls_route *rt0 = NULL, *rt2 = NULL;
  	unsigned index;
  
  	if (size) {
752ade68c   Michal Hocko   treewide: use kv[...
2351
  		labels = kvzalloc(size, GFP_KERNEL);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2352
2353
2354
2355
2356
  		if (!labels)
  			goto nolabels;
  	}
  
  	/* In case the predefined labels need to be populated */
78f5b8991   Tom Herbert   mpls: Change rese...
2357
  	if (limit > MPLS_LABEL_IPV4NULL) {
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2358
  		struct net_device *lo = net->loopback_dev;
a4ac8c986   David Ahern   net: mpls: bump m...
2359
  		rt0 = mpls_rt_alloc(1, lo->addr_len, 0);
df1c63164   David Ahern   net: mpls: Limit ...
2360
  		if (IS_ERR(rt0))
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2361
  			goto nort0;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2362
  		RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2363
  		rt0->rt_protocol = RTPROT_KERNEL;
118d52346   Robert Shearman   mpls: Enforce pay...
2364
  		rt0->rt_payload_type = MPT_IPV4;
5b441ac87   Robert Shearman   mpls: allow TTL p...
2365
  		rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2366
  		rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
b4e04fc73   Robert Shearman   mpls: fix forward...
2367
  		rt0->rt_nh->nh_via_alen = lo->addr_len;
cf4b24f00   Robert Shearman   mpls: reduce memo...
2368
2369
  		memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr,
  		       lo->addr_len);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2370
  	}
78f5b8991   Tom Herbert   mpls: Change rese...
2371
  	if (limit > MPLS_LABEL_IPV6NULL) {
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2372
  		struct net_device *lo = net->loopback_dev;
a4ac8c986   David Ahern   net: mpls: bump m...
2373
  		rt2 = mpls_rt_alloc(1, lo->addr_len, 0);
df1c63164   David Ahern   net: mpls: Limit ...
2374
  		if (IS_ERR(rt2))
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2375
  			goto nort2;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2376
  		RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2377
  		rt2->rt_protocol = RTPROT_KERNEL;
118d52346   Robert Shearman   mpls: Enforce pay...
2378
  		rt2->rt_payload_type = MPT_IPV6;
6a18c3123   David Ahern   net: mpls: Fix se...
2379
  		rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT;
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2380
  		rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE;
b4e04fc73   Robert Shearman   mpls: fix forward...
2381
  		rt2->rt_nh->nh_via_alen = lo->addr_len;
cf4b24f00   Robert Shearman   mpls: reduce memo...
2382
2383
  		memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr,
  		       lo->addr_len);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2384
2385
2386
2387
  	}
  
  	rtnl_lock();
  	/* Remember the original table */
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2388
  	old = rtnl_dereference(net->mpls.platform_label);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2389
2390
2391
2392
  	old_limit = net->mpls.platform_labels;
  
  	/* Free any labels beyond the new table */
  	for (index = limit; index < old_limit; index++)
f8efb73c9   Roopa Prabhu   mpls: multipath r...
2393
  		mpls_route_update(net, index, NULL, NULL);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2394
2395
2396
2397
2398
2399
2400
2401
2402
  
  	/* Copy over the old labels */
  	cp_size = size;
  	if (old_limit < limit)
  		cp_size = old_limit * sizeof(struct mpls_route *);
  
  	memcpy(labels, old, cp_size);
  
  	/* If needed set the predefined labels */
78f5b8991   Tom Herbert   mpls: Change rese...
2403
2404
2405
  	if ((old_limit <= MPLS_LABEL_IPV6NULL) &&
  	    (limit > MPLS_LABEL_IPV6NULL)) {
  		RCU_INIT_POINTER(labels[MPLS_LABEL_IPV6NULL], rt2);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2406
2407
  		rt2 = NULL;
  	}
78f5b8991   Tom Herbert   mpls: Change rese...
2408
2409
2410
  	if ((old_limit <= MPLS_LABEL_IPV4NULL) &&
  	    (limit > MPLS_LABEL_IPV4NULL)) {
  		RCU_INIT_POINTER(labels[MPLS_LABEL_IPV4NULL], rt0);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2411
2412
2413
2414
2415
  		rt0 = NULL;
  	}
  
  	/* Update the global pointers */
  	net->mpls.platform_labels = limit;
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2416
  	rcu_assign_pointer(net->mpls.platform_label, labels);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
  
  	rtnl_unlock();
  
  	mpls_rt_free(rt2);
  	mpls_rt_free(rt0);
  
  	if (old) {
  		synchronize_rcu();
  		kvfree(old);
  	}
  	return 0;
  
  nort2:
  	mpls_rt_free(rt0);
  nort0:
  	kvfree(labels);
  nolabels:
  	return -ENOMEM;
  }
  
  static int mpls_platform_labels(struct ctl_table *table, int write,
32927393d   Christoph Hellwig   sysctl: pass kern...
2438
  				void *buffer, size_t *lenp, loff_t *ppos)
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2439
2440
2441
2442
2443
2444
2445
2446
2447
  {
  	struct net *net = table->data;
  	int platform_labels = net->mpls.platform_labels;
  	int ret;
  	struct ctl_table tmp = {
  		.procname	= table->procname,
  		.data		= &platform_labels,
  		.maxlen		= sizeof(int),
  		.mode		= table->mode,
eec4844fa   Matteo Croce   proc/sysctl: add ...
2448
  		.extra1		= SYSCTL_ZERO,
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
  		.extra2		= &label_limit,
  	};
  
  	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
  
  	if (write && ret == 0)
  		ret = resize_platform_label_table(net, platform_labels);
  
  	return ret;
  }
5b441ac87   Robert Shearman   mpls: allow TTL p...
2459
2460
  #define MPLS_NS_SYSCTL_OFFSET(field)		\
  	(&((struct net *)0)->field)
37bde7997   Robert Shearman   mpls: Per-device ...
2461
  static const struct ctl_table mpls_table[] = {
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2462
2463
2464
2465
2466
2467
2468
  	{
  		.procname	= "platform_labels",
  		.data		= NULL,
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
  		.proc_handler	= mpls_platform_labels,
  	},
5b441ac87   Robert Shearman   mpls: allow TTL p...
2469
2470
2471
2472
2473
2474
  	{
  		.procname	= "ip_ttl_propagate",
  		.data		= MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate),
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
  		.proc_handler	= proc_dointvec_minmax,
eec4844fa   Matteo Croce   proc/sysctl: add ...
2475
2476
  		.extra1		= SYSCTL_ZERO,
  		.extra2		= SYSCTL_ONE,
5b441ac87   Robert Shearman   mpls: allow TTL p...
2477
  	},
a59166e47   Robert Shearman   mpls: allow TTL p...
2478
2479
2480
2481
2482
2483
  	{
  		.procname	= "default_ttl",
  		.data		= MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl),
  		.maxlen		= sizeof(int),
  		.mode		= 0644,
  		.proc_handler	= proc_dointvec_minmax,
eec4844fa   Matteo Croce   proc/sysctl: add ...
2484
  		.extra1		= SYSCTL_ONE,
a59166e47   Robert Shearman   mpls: allow TTL p...
2485
2486
  		.extra2		= &ttl_max,
  	},
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2487
2488
  	{ }
  };
0189197f4   Eric W. Biederman   mpls: Basic routi...
2489
2490
  static int mpls_net_init(struct net *net)
  {
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2491
  	struct ctl_table *table;
5b441ac87   Robert Shearman   mpls: allow TTL p...
2492
  	int i;
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2493

0189197f4   Eric W. Biederman   mpls: Basic routi...
2494
2495
  	net->mpls.platform_labels = 0;
  	net->mpls.platform_label = NULL;
5b441ac87   Robert Shearman   mpls: allow TTL p...
2496
  	net->mpls.ip_ttl_propagate = 1;
a59166e47   Robert Shearman   mpls: allow TTL p...
2497
  	net->mpls.default_ttl = 255;
0189197f4   Eric W. Biederman   mpls: Basic routi...
2498

7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2499
2500
2501
  	table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL);
  	if (table == NULL)
  		return -ENOMEM;
5b441ac87   Robert Shearman   mpls: allow TTL p...
2502
2503
2504
2505
2506
  	/* Table data contains only offsets relative to the base of
  	 * the mdev at this point, so make them absolute.
  	 */
  	for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++)
  		table[i].data = (char *)net + (uintptr_t)table[i].data;
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2507
  	net->mpls.ctl = register_net_sysctl(net, "net/mpls", table);
6ea3c9d5b   Nikolay Aleksandrov   mpls: fix mpls_ne...
2508
2509
  	if (net->mpls.ctl == NULL) {
  		kfree(table);
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2510
  		return -ENOMEM;
6ea3c9d5b   Nikolay Aleksandrov   mpls: fix mpls_ne...
2511
  	}
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2512

0189197f4   Eric W. Biederman   mpls: Basic routi...
2513
2514
2515
2516
2517
  	return 0;
  }
  
  static void mpls_net_exit(struct net *net)
  {
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2518
2519
  	struct mpls_route __rcu **platform_label;
  	size_t platform_labels;
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2520
  	struct ctl_table *table;
0189197f4   Eric W. Biederman   mpls: Basic routi...
2521
  	unsigned int index;
7720c01f3   Eric W. Biederman   mpls: Add a sysct...
2522
2523
2524
  	table = net->mpls.ctl->ctl_table_arg;
  	unregister_net_sysctl_table(net->mpls.ctl);
  	kfree(table);
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2525
2526
  	/* An rcu grace period has passed since there was a device in
  	 * the network namespace (and thus the last in flight packet)
0189197f4   Eric W. Biederman   mpls: Basic routi...
2527
2528
2529
2530
2531
2532
2533
2534
  	 * left this network namespace.  This is because
  	 * unregister_netdevice_many and netdev_run_todo has completed
  	 * for each network device that was in this network namespace.
  	 *
  	 * As such no additional rcu synchronization is necessary when
  	 * freeing the platform_label table.
  	 */
  	rtnl_lock();
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2535
2536
2537
2538
2539
  	platform_label = rtnl_dereference(net->mpls.platform_label);
  	platform_labels = net->mpls.platform_labels;
  	for (index = 0; index < platform_labels; index++) {
  		struct mpls_route *rt = rtnl_dereference(platform_label[index]);
  		RCU_INIT_POINTER(platform_label[index], NULL);
e37791ec1   David Ahern   mpls: Send route ...
2540
  		mpls_notify_route(net, index, rt, NULL, NULL);
0189197f4   Eric W. Biederman   mpls: Basic routi...
2541
2542
2543
  		mpls_rt_free(rt);
  	}
  	rtnl_unlock();
19d0c341d   Eric W. Biederman   mpls: Cleanup the...
2544
  	kvfree(platform_label);
0189197f4   Eric W. Biederman   mpls: Basic routi...
2545
2546
2547
2548
2549
2550
  }
  
  static struct pernet_operations mpls_net_ops = {
  	.init = mpls_net_init,
  	.exit = mpls_net_exit,
  };
27d691056   Robert Shearman   mpls: Packet stats
2551
2552
2553
2554
2555
  static struct rtnl_af_ops mpls_af_ops __read_mostly = {
  	.family		   = AF_MPLS,
  	.fill_stats_af	   = mpls_fill_stats_af,
  	.get_stats_af_size = mpls_get_stats_af_size,
  };
0189197f4   Eric W. Biederman   mpls: Basic routi...
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
  static int __init mpls_init(void)
  {
  	int err;
  
  	BUILD_BUG_ON(sizeof(struct mpls_shim_hdr) != 4);
  
  	err = register_pernet_subsys(&mpls_net_ops);
  	if (err)
  		goto out;
  
  	err = register_netdevice_notifier(&mpls_dev_notifier);
  	if (err)
  		goto out_unregister_pernet;
  
  	dev_add_pack(&mpls_packet_type);
27d691056   Robert Shearman   mpls: Packet stats
2571
  	rtnl_af_register(&mpls_af_ops);
c1c502b51   Florian Westphal   net: use rtnl_reg...
2572
2573
2574
2575
2576
2577
2578
2579
2580
  	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_NEWROUTE,
  			     mpls_rtm_newroute, NULL, 0);
  	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_DELROUTE,
  			     mpls_rtm_delroute, NULL, 0);
  	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETROUTE,
  			     mpls_getroute, mpls_dump_routes, 0);
  	rtnl_register_module(THIS_MODULE, PF_MPLS, RTM_GETNETCONF,
  			     mpls_netconf_get_devconf,
  			     mpls_netconf_dump_devconf, 0);
bdc476413   Amine Kherbouche   ip_tunnel: add mp...
2581
2582
2583
2584
  	err = ipgre_tunnel_encap_add_mpls_ops();
  	if (err)
  		pr_err("Can't add mpls over gre tunnel ops
  ");
0189197f4   Eric W. Biederman   mpls: Basic routi...
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
  	err = 0;
  out:
  	return err;
  
  out_unregister_pernet:
  	unregister_pernet_subsys(&mpls_net_ops);
  	goto out;
  }
  module_init(mpls_init);
  
  static void __exit mpls_exit(void)
  {
03c056654   Eric W. Biederman   mpls: Netlink com...
2597
  	rtnl_unregister_all(PF_MPLS);
27d691056   Robert Shearman   mpls: Packet stats
2598
  	rtnl_af_unregister(&mpls_af_ops);
0189197f4   Eric W. Biederman   mpls: Basic routi...
2599
2600
2601
  	dev_remove_pack(&mpls_packet_type);
  	unregister_netdevice_notifier(&mpls_dev_notifier);
  	unregister_pernet_subsys(&mpls_net_ops);
bdc476413   Amine Kherbouche   ip_tunnel: add mp...
2602
  	ipgre_tunnel_encap_del_mpls_ops();
0189197f4   Eric W. Biederman   mpls: Basic routi...
2603
2604
2605
2606
2607
2608
  }
  module_exit(mpls_exit);
  
  MODULE_DESCRIPTION("MultiProtocol Label Switching");
  MODULE_LICENSE("GPL v2");
  MODULE_ALIAS_NETPROTO(PF_MPLS);