|
@@ -1,6 +1,7 @@
|
|
#include <linux/types.h>
|
|
#include <linux/types.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/socket.h>
|
|
|
|
+#include <linux/sysctl.h>
|
|
#include <linux/net.h>
|
|
#include <linux/net.h>
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/if_arp.h>
|
|
@@ -31,6 +32,9 @@ struct mpls_route { /* next hop label forwarding entry */
|
|
u8 rt_via[0];
|
|
u8 rt_via[0];
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static int zero = 0;
|
|
|
|
+static int label_limit = (1 << 20) - 1;
|
|
|
|
+
|
|
static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
|
|
static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned index)
|
|
{
|
|
{
|
|
struct mpls_route *rt = NULL;
|
|
struct mpls_route *rt = NULL;
|
|
@@ -273,18 +277,160 @@ static struct notifier_block mpls_dev_notifier = {
|
|
.notifier_call = mpls_dev_notify,
|
|
.notifier_call = mpls_dev_notify,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+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) {
|
|
|
|
+ labels = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
|
|
|
|
+ if (!labels)
|
|
|
|
+ labels = vzalloc(size);
|
|
|
|
+
|
|
|
|
+ if (!labels)
|
|
|
|
+ goto nolabels;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* In case the predefined labels need to be populated */
|
|
|
|
+ if (limit > LABEL_IPV4_EXPLICIT_NULL) {
|
|
|
|
+ struct net_device *lo = net->loopback_dev;
|
|
|
|
+ rt0 = mpls_rt_alloc(lo->addr_len);
|
|
|
|
+ if (!rt0)
|
|
|
|
+ goto nort0;
|
|
|
|
+ rt0->rt_dev = lo;
|
|
|
|
+ rt0->rt_protocol = RTPROT_KERNEL;
|
|
|
|
+ rt0->rt_via_family = AF_PACKET;
|
|
|
|
+ memcpy(rt0->rt_via, lo->dev_addr, lo->addr_len);
|
|
|
|
+ }
|
|
|
|
+ if (limit > LABEL_IPV6_EXPLICIT_NULL) {
|
|
|
|
+ struct net_device *lo = net->loopback_dev;
|
|
|
|
+ rt2 = mpls_rt_alloc(lo->addr_len);
|
|
|
|
+ if (!rt2)
|
|
|
|
+ goto nort2;
|
|
|
|
+ rt2->rt_dev = lo;
|
|
|
|
+ rt2->rt_protocol = RTPROT_KERNEL;
|
|
|
|
+ rt2->rt_via_family = AF_PACKET;
|
|
|
|
+ memcpy(rt2->rt_via, lo->dev_addr, lo->addr_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ /* Remember the original table */
|
|
|
|
+ old = net->mpls.platform_label;
|
|
|
|
+ old_limit = net->mpls.platform_labels;
|
|
|
|
+
|
|
|
|
+ /* Free any labels beyond the new table */
|
|
|
|
+ for (index = limit; index < old_limit; index++)
|
|
|
|
+ mpls_route_update(net, index, NULL, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ /* 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 */
|
|
|
|
+ if ((old_limit <= LABEL_IPV6_EXPLICIT_NULL) &&
|
|
|
|
+ (limit > LABEL_IPV6_EXPLICIT_NULL)) {
|
|
|
|
+ labels[LABEL_IPV6_EXPLICIT_NULL] = rt2;
|
|
|
|
+ rt2 = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((old_limit <= LABEL_IPV4_EXPLICIT_NULL) &&
|
|
|
|
+ (limit > LABEL_IPV4_EXPLICIT_NULL)) {
|
|
|
|
+ labels[LABEL_IPV4_EXPLICIT_NULL] = rt0;
|
|
|
|
+ rt0 = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Update the global pointers */
|
|
|
|
+ net->mpls.platform_labels = limit;
|
|
|
|
+ net->mpls.platform_label = labels;
|
|
|
|
+
|
|
|
|
+ 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,
|
|
|
|
+ void __user *buffer, size_t *lenp, loff_t *ppos)
|
|
|
|
+{
|
|
|
|
+ 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,
|
|
|
|
+ .extra1 = &zero,
|
|
|
|
+ .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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct ctl_table mpls_table[] = {
|
|
|
|
+ {
|
|
|
|
+ .procname = "platform_labels",
|
|
|
|
+ .data = NULL,
|
|
|
|
+ .maxlen = sizeof(int),
|
|
|
|
+ .mode = 0644,
|
|
|
|
+ .proc_handler = mpls_platform_labels,
|
|
|
|
+ },
|
|
|
|
+ { }
|
|
|
|
+};
|
|
|
|
+
|
|
static int mpls_net_init(struct net *net)
|
|
static int mpls_net_init(struct net *net)
|
|
{
|
|
{
|
|
|
|
+ struct ctl_table *table;
|
|
|
|
+
|
|
net->mpls.platform_labels = 0;
|
|
net->mpls.platform_labels = 0;
|
|
net->mpls.platform_label = NULL;
|
|
net->mpls.platform_label = NULL;
|
|
|
|
|
|
|
|
+ table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL);
|
|
|
|
+ if (table == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ table[0].data = net;
|
|
|
|
+ net->mpls.ctl = register_net_sysctl(net, "net/mpls", table);
|
|
|
|
+ if (net->mpls.ctl == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void mpls_net_exit(struct net *net)
|
|
static void mpls_net_exit(struct net *net)
|
|
{
|
|
{
|
|
|
|
+ struct ctl_table *table;
|
|
unsigned int index;
|
|
unsigned int index;
|
|
|
|
|
|
|
|
+ table = net->mpls.ctl->ctl_table_arg;
|
|
|
|
+ unregister_net_sysctl_table(net->mpls.ctl);
|
|
|
|
+ kfree(table);
|
|
|
|
+
|
|
/* An rcu grace period haselapsed since there was a device in
|
|
/* An rcu grace period haselapsed since there was a device in
|
|
* the network namespace (and thus the last in fqlight packet)
|
|
* the network namespace (and thus the last in fqlight packet)
|
|
* left this network namespace. This is because
|
|
* left this network namespace. This is because
|