aboutsummaryrefslogtreecommitdiffstats
path: root/net/mpls/af_mpls.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2015-03-03 20:11:20 -0500
committerDavid S. Miller <davem@davemloft.net>2015-03-04 00:26:06 -0500
commit7720c01f3f590116882e251f13c7e1d5602f8643 (patch)
treeb27e3e1b2443de3b68e4dfcf1a12e7d4e5b11198 /net/mpls/af_mpls.c
parent0189197f441602acdca3f97750d392a895b778fd (diff)
mpls: Add a sysctl to control the size of the mpls label table
This sysctl gives two benefits. By defaulting the table size to 0 mpls even when compiled in and enabled defaults to not forwarding any packets. This prevents unpleasant surprises for users. The other benefit is that as mpls labels are allocated locally a dense table a small dense label table may be used which saves memory and is extremely simple and efficient to implement. This sysctl allows userspace to choose the restrictions on the label table size userspace applications need to cope with. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mpls/af_mpls.c')
-rw-r--r--net/mpls/af_mpls.c146
1 files changed, 146 insertions, 0 deletions
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 924377736b2a..b097125dfa33 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -1,6 +1,7 @@
1#include <linux/types.h> 1#include <linux/types.h>
2#include <linux/skbuff.h> 2#include <linux/skbuff.h>
3#include <linux/socket.h> 3#include <linux/socket.h>
4#include <linux/sysctl.h>
4#include <linux/net.h> 5#include <linux/net.h>
5#include <linux/module.h> 6#include <linux/module.h>
6#include <linux/if_arp.h> 7#include <linux/if_arp.h>
@@ -31,6 +32,9 @@ struct mpls_route { /* next hop label forwarding entry */
31 u8 rt_via[0]; 32 u8 rt_via[0];
32}; 33};
33 34
35static int zero = 0;
36static int label_limit = (1 << 20) - 1;
37
34static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index) 38static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
35{ 39{
36 struct mpls_route *rt = NULL; 40 struct mpls_route *rt = NULL;
@@ -273,18 +277,160 @@ static struct notifier_block mpls_dev_notifier = {
273 .notifier_call = mpls_dev_notify, 277 .notifier_call = mpls_dev_notify,
274}; 278};
275 279
280static int resize_platform_label_table(struct net *net, size_t limit)
281{
282 size_t size = sizeof(struct mpls_route *) * limit;
283 size_t old_limit;
284 size_t cp_size;
285 struct mpls_route __rcu **labels = NULL, **old;
286 struct mpls_route *rt0 = NULL, *rt2 = NULL;
287 unsigned index;
288
289 if (size) {
290 labels = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
291 if (!labels)
292 labels = vzalloc(size);
293
294 if (!labels)
295 goto nolabels;
296 }
297
298 /* In case the predefined labels need to be populated */
299 if (limit > LABEL_IPV4_EXPLICIT_NULL) {
300 struct net_device *lo = net->loopback_dev;
301 rt0 = mpls_rt_alloc(lo->addr_len);
302 if (!rt0)
303 goto nort0;
304 rt0->rt_dev = lo;
305 rt0->rt_protocol = RTPROT_KERNEL;
306 rt0->rt_via_family = AF_PACKET;
307 memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len);
308 }
309 if (limit > LABEL_IPV6_EXPLICIT_NULL) {
310 struct net_device *lo = net->loopback_dev;
311 rt2 = mpls_rt_alloc(lo->addr_len);
312 if (!rt2)
313 goto nort2;
314 rt2->rt_dev = lo;
315 rt2->rt_protocol = RTPROT_KERNEL;
316 rt2->rt_via_family = AF_PACKET;
317 memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len);
318 }
319
320 rtnl_lock();
321 /* Remember the original table */
322 old = net->mpls.platform_label;
323 old_limit = net->mpls.platform_labels;
324
325 /* Free any labels beyond the new table */
326 for (index = limit; index < old_limit; index++)
327 mpls_route_update(net, index, NULL, NULL, NULL);
328
329 /* Copy over the old labels */
330 cp_size = size;
331 if (old_limit < limit)
332 cp_size = old_limit * sizeof(struct mpls_route *);
333
334 memcpy(labels, old, cp_size);
335
336 /* If needed set the predefined labels */
337 if ((old_limit <= LABEL_IPV6_EXPLICIT_NULL) &&
338 (limit > LABEL_IPV6_EXPLICIT_NULL)) {
339 labels[LABEL_IPV6_EXPLICIT_NULL] = rt2;
340 rt2 = NULL;
341 }
342
343 if ((old_limit <= LABEL_IPV4_EXPLICIT_NULL) &&
344 (limit > LABEL_IPV4_EXPLICIT_NULL)) {
345 labels[LABEL_IPV4_EXPLICIT_NULL] = rt0;
346 rt0 = NULL;
347 }
348
349 /* Update the global pointers */
350 net->mpls.platform_labels = limit;
351 net->mpls.platform_label = labels;
352
353 rtnl_unlock();
354
355 mpls_rt_free(rt2);
356 mpls_rt_free(rt0);
357
358 if (old) {
359 synchronize_rcu();
360 kvfree(old);
361 }
362 return 0;
363
364nort2:
365 mpls_rt_free(rt0);
366nort0:
367 kvfree(labels);
368nolabels:
369 return -ENOMEM;
370}
371
372static int mpls_platform_labels(struct ctl_table *table, int write,
373 void __user *buffer, size_t *lenp, loff_t *ppos)
374{
375 struct net *net = table->data;
376 int platform_labels = net->mpls.platform_labels;
377 int ret;
378 struct ctl_table tmp = {
379 .procname = table->procname,
380 .data = &platform_labels,
381 .maxlen = sizeof(int),
382 .mode = table->mode,
383 .extra1 = &zero,
384 .extra2 = &label_limit,
385 };
386
387 ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
388
389 if (write && ret == 0)
390 ret = resize_platform_label_table(net, platform_labels);
391
392 return ret;
393}
394
395static struct ctl_table mpls_table[] = {
396 {
397 .procname = "platform_labels",
398 .data = NULL,
399 .maxlen = sizeof(int),
400 .mode = 0644,
401 .proc_handler = mpls_platform_labels,
402 },
403 { }
404};
405
276static int mpls_net_init(struct net *net) 406static int mpls_net_init(struct net *net)
277{ 407{
408 struct ctl_table *table;
409
278 net->mpls.platform_labels = 0; 410 net->mpls.platform_labels = 0;
279 net->mpls.platform_label = NULL; 411 net->mpls.platform_label = NULL;
280 412
413 table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL);
414 if (table == NULL)
415 return -ENOMEM;
416
417 table[0].data = net;
418 net->mpls.ctl = register_net_sysctl(net, "net/mpls", table);
419 if (net->mpls.ctl == NULL)
420 return -ENOMEM;
421
281 return 0; 422 return 0;
282} 423}
283 424
284static void mpls_net_exit(struct net *net) 425static void mpls_net_exit(struct net *net)
285{ 426{
427 struct ctl_table *table;
286 unsigned int index; 428 unsigned int index;
287 429
430 table = net->mpls.ctl->ctl_table_arg;
431 unregister_net_sysctl_table(net->mpls.ctl);
432 kfree(table);
433
288 /* An rcu grace period haselapsed since there was a device in 434 /* An rcu grace period haselapsed since there was a device in
289 * the network namespace (and thus the last in fqlight packet) 435 * the network namespace (and thus the last in fqlight packet)
290 * left this network namespace. This is because 436 * left this network namespace. This is because