aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-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