Skip to content

Commit 7720c01

Browse files
ebiedermdavem330
authored andcommitted
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" <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 0189197 commit 7720c01

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/proc/sys/net/mpls/* Variables:
2+
3+
platform_labels - INTEGER
4+
Number of entries in the platform label table. It is not
5+
possible to configure forwarding for label values equal to or
6+
greater than the number of platform labels.
7+
8+
A dense utliziation of the entries in the platform label table
9+
is possible and expected aas the platform labels are locally
10+
allocated.
11+
12+
If the number of platform label table entries is set to 0 no
13+
label will be recognized by the kernel and mpls forwarding
14+
will be disabled.
15+
16+
Reducing this value will remove all label routing entries that
17+
no longer fit in the table.
18+
19+
Possible values: 0 - 1048575
20+
Default: 0

include/net/netns/mpls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#define __NETNS_MPLS_H__
77

88
struct mpls_route;
9+
struct ctl_table_header;
910

1011
struct netns_mpls {
1112
size_t platform_labels;
1213
struct mpls_route __rcu * __rcu *platform_label;
14+
struct ctl_table_header *ctl;
1315
};
1416

1517
#endif /* __NETNS_MPLS_H__ */

net/mpls/af_mpls.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <linux/types.h>
22
#include <linux/skbuff.h>
33
#include <linux/socket.h>
4+
#include <linux/sysctl.h>
45
#include <linux/net.h>
56
#include <linux/module.h>
67
#include <linux/if_arp.h>
@@ -31,6 +32,9 @@ struct mpls_route { /* next hop label forwarding entry */
3132
u8 rt_via[0];
3233
};
3334

35+
static int zero = 0;
36+
static int label_limit = (1 << 20) - 1;
37+
3438
static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
3539
{
3640
struct mpls_route *rt = NULL;
@@ -273,18 +277,160 @@ static struct notifier_block mpls_dev_notifier = {
273277
.notifier_call = mpls_dev_notify,
274278
};
275279

280+
static 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+
364+
nort2:
365+
mpls_rt_free(rt0);
366+
nort0:
367+
kvfree(labels);
368+
nolabels:
369+
return -ENOMEM;
370+
}
371+
372+
static 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+
395+
static 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+
276406
static int mpls_net_init(struct net *net)
277407
{
408+
struct ctl_table *table;
409+
278410
net->mpls.platform_labels = 0;
279411
net->mpls.platform_label = NULL;
280412

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+
281422
return 0;
282423
}
283424

284425
static void mpls_net_exit(struct net *net)
285426
{
427+
struct ctl_table *table;
286428
unsigned int index;
287429

430+
table = net->mpls.ctl->ctl_table_arg;
431+
unregister_net_sysctl_table(net->mpls.ctl);
432+
kfree(table);
433+
288434
/* An rcu grace period haselapsed since there was a device in
289435
* the network namespace (and thus the last in fqlight packet)
290436
* left this network namespace. This is because

0 commit comments

Comments
 (0)