new file mode 100644
@@ -0,0 +1,17 @@
+#include <linux/types.h>
+
+#define XT_CONNLABEL_MAXLEN 22
+
+struct xt_connlabel_mtinfo {
+ char labelname[XT_CONNLABEL_MAXLEN-1]; /* not null terminated */
+ bool invert;
+ /* used internally by kernel: */
+ __u16 bit;
+};
+
+struct xt_connlabel_tginfo {
+ char labelname[XT_CONNLABEL_MAXLEN];
+ /* used internally by kernel: */
+ __u16 bit;
+};
+
@@ -23,6 +23,9 @@ enum nf_ct_ext_id {
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
NF_CT_EXT_TIMEOUT,
#endif
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+ NF_CT_EXT_LABELS,
+#endif
NF_CT_EXT_NUM,
};
@@ -33,6 +36,7 @@ enum nf_ct_ext_id {
#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
#define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp
#define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
+#define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
/* Extensions: optional stuff which isn't permanently in struct. */
struct nf_ct_ext {
new file mode 100644
@@ -0,0 +1,80 @@
+#include <linux/types.h>
+#include <net/net_namespace.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+
+#define NF_CONNLABEL_MAX 32768
+
+#define NF_CONNLABEL_NOT_ALLOCATED 1
+#define NF_CONNLABEL_NOT_ALLOCATED_BIT 0
+
+struct nf_conn_labels {
+ union {
+ unsigned long *label_bits_ptr;
+ unsigned long label_bits;
+ } u;
+ unsigned int bits;
+};
+
+static inline
+struct nf_conn_labels *nf_conn_labels_find(const struct nf_conn *ct)
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+ return nf_ct_ext_find(ct, NF_CT_EXT_LABELS);
+#else
+ return NULL;
+#endif
+}
+
+static inline
+struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+ struct nf_conn_labels *cl_ext;
+ struct net *net = nf_ct_net(ct);
+ unsigned int label_count = ACCESS_ONCE(net->ct.labels.max);
+ unsigned long *label_bits = NULL;
+ unsigned int len;
+
+ if (label_count == 0) /* extension is disabled */
+ return NULL;
+
+ len = label_count / BITS_PER_LONG;
+ if (label_count > BITS_PER_LONG)
+ label_bits = kcalloc(len, sizeof(*label_bits), GFP_ATOMIC);
+
+ cl_ext = nf_ct_ext_add(ct, NF_CT_EXT_LABELS, GFP_ATOMIC);
+ if (cl_ext == NULL) {
+ kfree(label_bits);
+ return NULL;
+ }
+
+ if (label_bits) {
+ cl_ext->u.label_bits_ptr = label_bits;
+ cl_ext->bits = len * BITS_PER_LONG;
+ } else {
+ cl_ext->u.label_bits = NF_CONNLABEL_NOT_ALLOCATED;
+ cl_ext->bits = BITS_PER_LONG;
+ }
+
+ BUILD_BUG_ON(NF_CONNLABEL_MAX < BITS_PER_LONG);
+ BUILD_BUG_ON(NF_CONNLABEL_MAX / BITS_PER_LONG * sizeof(unsigned long) > 8192);
+ return cl_ext;
+#else
+ return NULL;
+#endif
+};
+
+void nf_conntrack_labels_fini(struct net *net);
+int nf_conntrack_labels_init(struct net *net);
+
+int nf_connlabel_add(struct net *net, const char *label);
+void nf_connlabel_del(struct net *net, u16 bit);
+
+bool nf_connlabel_match(const struct nf_conn *ct, u16 bit);
+void __nf_connlabel_set(const struct nf_conn *ct, u16 bit);
+
+int nf_connlabels_attach(struct nf_conn *ct, const char *label);
+
@@ -50,6 +50,12 @@ struct nf_icmp_net {
unsigned int timeout;
};
+struct nf_connlabels_net {
+ struct hlist_head *ct_htable;
+ struct hlist_head *ct_htable_bits;
+ unsigned int max;
+};
+
struct nf_ip_net {
struct nf_generic_net generic;
struct nf_tcp_net tcp;
@@ -83,6 +89,9 @@ struct netns_ct {
int sysctl_auto_assign_helper;
bool auto_assign_helper_warned;
struct nf_ip_net nf_ct_proto;
+#if defined(CONFIG_NF_CONNTRACK_LABELS)
+ struct nf_connlabels_net labels;
+#endif
#ifdef CONFIG_NF_NAT_NEEDED
struct hlist_head *nat_bysource;
unsigned int nat_htable_size;
@@ -124,6 +124,15 @@ config NF_CONNTRACK_TIMESTAMP
If unsure, say `N'.
+config NF_CONNTRACK_LABELS
+ bool 'Connection tracking labels'
+ depends on NETFILTER_ADVANCED
+ help
+ This option enables support for assigning user-defined text strings
+ to connection tracking entries.
+
+ If unsure, say `N'.
+
config NF_CT_PROTO_DCCP
tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)'
depends on EXPERIMENTAL
@@ -472,6 +481,13 @@ config NETFILTER_XT_SET
To compile it as a module, choose M here. If unsure, say N.
+config NETFILTER_XT_CONNLABELS
+ tristate '"connlimit" target and match support"'
+ depends on NF_CONNTRACK_LABELS
+ ---help---
+ This match allows you to match against connlabels assigned to the
+ connection.
+
# alphabetically ordered list of targets
comment "Xtables targets"
@@ -4,6 +4,7 @@ nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
+nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o
obj-$(CONFIG_NETFILTER) = netfilter.o
@@ -70,6 +71,7 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o
obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o
obj-$(CONFIG_NETFILTER_XT_SET) += xt_set.o
+obj-$(CONFIG_NETFILTER_XT_CONNLABELS) += xt_connlabel.o
obj-$(CONFIG_NF_NAT) += xt_nat.o
# targets
@@ -45,6 +45,7 @@
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h>
#include <net/netfilter/nf_conntrack_timeout.h>
+#include <net/netfilter/nf_conntrack_labels.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h>
@@ -816,6 +817,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
nf_ct_acct_ext_add(ct, GFP_ATOMIC);
nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
+ nf_ct_labels_ext_add(ct);
ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
@@ -1359,6 +1361,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
}
nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
+ nf_conntrack_labels_fini(net);
nf_conntrack_helper_fini(net);
nf_conntrack_timeout_fini(net);
nf_conntrack_ecache_fini(net);
@@ -1585,7 +1588,15 @@ static int nf_conntrack_init_net(struct net *net)
ret = nf_conntrack_helper_init(net);
if (ret < 0)
goto err_helper;
+
+ ret = nf_conntrack_labels_init(net);
+ if (ret < 0)
+ goto err_labels;
+
return 0;
+
+err_labels:
+ nf_conntrack_helper_fini(net);
err_helper:
nf_conntrack_timeout_fini(net);
err_timeout:
new file mode 100644
@@ -0,0 +1,468 @@
+#define DEBUG
+#include <linux/ctype.h>
+#include <linux/export.h>
+#include <linux/jhash.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include <linux/netfilter/xt_connlabel.h>
+#include <net/netfilter/nf_conntrack_labels.h>
+
+#define HT_SIZE (NF_CONNLABEL_MAX / 32u)
+#define HT_MASK (HT_SIZE - 1)
+
+static DEFINE_SPINLOCK(connlabels_lock);
+
+struct nf_connlabel_entry {
+ struct rcu_head rcu;
+ struct hlist_node node;
+ struct hlist_node node_bit;
+ atomic_t refcnt;
+ int bit;
+ unsigned int added;
+ char name[1];
+};
+
+static u32 hash_rnd __read_mostly;
+
+static bool __nf_connlabel_entry_get(struct nf_connlabel_entry *e)
+{
+ return atomic_inc_not_zero(&e->refcnt);
+}
+
+static void __nf_connlabel_entry_free(struct nf_connlabel_entry *ent)
+{
+ hlist_del_rcu(&ent->node_bit);
+ pr_debug("%s: free label %s, bit %d\n", __func__, ent->name, ent->bit);
+ kfree_rcu(ent, rcu);
+}
+
+static void nf_connlabel_entry_free(struct nf_connlabel_entry *ent)
+{
+ spin_lock_bh(&connlabels_lock);
+ __nf_connlabel_entry_free(ent);
+ spin_unlock_bh(&connlabels_lock);
+}
+
+static void __nf_connlabel_entry_put(struct nf_connlabel_entry *ent)
+{
+ if (atomic_dec_and_test(&ent->refcnt))
+ __nf_connlabel_entry_free(ent);
+}
+
+static void nf_connlabel_entry_put(struct nf_connlabel_entry *ent)
+{
+ if (atomic_dec_and_test(&ent->refcnt))
+ nf_connlabel_entry_free(ent);
+}
+
+static u32 label_hash(const char *str)
+{
+ return jhash(str, strlen(str), hash_rnd) & HT_MASK;
+}
+
+static u32 label_bit_hash(unsigned int bit)
+{
+ return bit & HT_MASK;
+}
+
+static struct nf_connlabels_net *net_connlabels(struct net *net)
+{
+ return &net->ct.labels;
+}
+
+static struct hlist_head *
+__nf_connlabels_hlist(struct nf_connlabels_net *cl_net, const char *label)
+{
+ return &cl_net->ct_htable[label_hash(label)];
+}
+
+static bool labels_equal(struct nf_connlabel_entry *ent, const char *label)
+{
+ return strcmp(ent->name, label) == 0;
+}
+
+static struct hlist_head *cl_alloc_hlist_table(void)
+{
+ struct hlist_head *h = vmalloc(sizeof(struct hlist_head) * HT_SIZE);
+ if (h) {
+ unsigned int i;
+ for (i = 0; i < HT_SIZE; i++)
+ INIT_HLIST_HEAD(&h[i]);
+ }
+ return h;
+}
+
+/*
+ * slow path; only called when a new label is added to the store.
+ */
+static int __find_free_bit(struct nf_connlabels_net *cl_net)
+{
+ /* protected by connlabels_lock */
+ static unsigned long label_bits_used[NF_CONNLABEL_MAX / BITS_PER_LONG];
+ unsigned long bit;
+ struct hlist_head *list;
+ struct hlist_node *pos;
+ struct nf_connlabel_entry *ent;
+ int i;
+
+ memset(label_bits_used, 0, sizeof(label_bits_used));
+
+ /* bit 0 cannot be used. It is reserved to indicate if
+ * labels->u is kmalloc'd or not.
+ */
+ __set_bit(NF_CONNLABEL_NOT_ALLOCATED_BIT, label_bits_used);
+
+ for (i = 0; i < HT_SIZE ; i++) {
+ list = &cl_net->ct_htable_bits[i];
+ if (i && hlist_empty(list))
+ break;
+ hlist_for_each_entry(ent, pos, list, node_bit) {
+ if (WARN_ON(ent->bit >= NF_CONNLABEL_MAX))
+ return -ENOSPC;
+ __set_bit(ent->bit, label_bits_used);
+ }
+ }
+ bit = find_first_zero_bit(label_bits_used, NF_CONNLABEL_MAX);
+ if (bit >= NF_CONNLABEL_MAX)
+ return -ENOSPC;
+
+ if (bit >= cl_net->max) {
+ cl_net->max = roundup(bit, BITS_PER_LONG);
+ pr_debug("cl_net->max increased to %u\n", cl_net->max);
+ }
+ return bit;
+}
+
+static int __nf_connlabels_add(struct nf_connlabels_net *cl_net,
+ struct hlist_head *list,
+ struct nf_connlabel_entry *ent)
+{
+ ent->bit = __find_free_bit(cl_net);
+ if (ent->bit < 0)
+ return -ENOSPC;
+
+ atomic_set(&ent->refcnt, 1);
+ ent->added = 0;
+
+ hlist_add_head_rcu(&ent->node, list);
+ hlist_add_head_rcu(&ent->node_bit,
+ &cl_net->ct_htable_bits[label_bit_hash(ent->bit)]);
+
+ pr_debug("%s: created label %s with bit %d\n",
+ __func__, ent->name, ent->bit);
+ return ent->bit;
+}
+
+static int label_valid_name(const char *name)
+{
+ if (*name == '\0')
+ return -EINVAL;
+
+ if (strnlen(name, XT_CONNLABEL_MAXLEN) >= XT_CONNLABEL_MAXLEN)
+ return -ENAMETOOLONG;
+
+ while (*name) {
+ if (*name == '/' || isspace(*name))
+ return -EINVAL;
+ name++;
+ }
+
+ return 0;
+}
+
+static int __nf_connlabel_find_get(const char *label,
+ const struct hlist_head *list)
+{
+ struct hlist_node *pos;
+ struct nf_connlabel_entry *ent;
+
+ hlist_for_each_entry(ent, pos, list, node) {
+ if (!labels_equal(ent, label))
+ continue;
+ ent->added++;
+ return ent->bit;
+ }
+ return -ENOENT;
+}
+
+int nf_connlabel_add(struct net *net, const char *label)
+{
+ struct nf_connlabels_net *cl_net = net_connlabels(net);
+ struct hlist_head *list;
+ struct nf_connlabel_entry *ent;
+ int ret = label_valid_name(label);
+
+ if (ret)
+ return ret;
+
+ list = __nf_connlabels_hlist(cl_net, label);
+
+ spin_lock_bh(&connlabels_lock);
+ ret = __nf_connlabel_find_get(label, list);
+ spin_unlock_bh(&connlabels_lock);
+ if (ret >= 0)
+ return ret;
+
+ ent = kmalloc(sizeof(*ent) + strlen(label), GFP_KERNEL);
+ if (!ent)
+ return -ENOMEM;
+
+ strcpy(ent->name, label);
+
+ /* check if label was added while lock was dropped */
+ spin_lock_bh(&connlabels_lock);
+ ret = __nf_connlabel_find_get(label, list);
+ if (ret >= 0) /* yes: we raced, free duplicate */
+ goto out_free;
+
+ ret = __nf_connlabels_add(cl_net, list, ent);
+ if (ret >= 0)
+ goto out;
+ out_free:
+ kfree(ent);
+ out:
+ spin_unlock_bh(&connlabels_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nf_connlabel_add);
+
+
+/*
+ * remove label from name_to_bit table if user count drops to zero.
+ * memory will not be released (yet) if there are conntracks that
+ * reference the label.
+ */
+void nf_connlabel_del(struct net *net, u16 bit)
+{
+ struct nf_connlabels_net *cl_net = net_connlabels(net);
+ struct nf_connlabel_entry *ent;
+ struct hlist_node *pos;
+ struct hlist_node *tmp;
+ struct hlist_head *listb;
+
+ spin_lock_bh(&connlabels_lock);
+
+ listb = &cl_net->ct_htable_bits[label_bit_hash(bit)];
+
+ hlist_for_each_entry_safe(ent, pos, tmp, listb, node_bit) {
+ if (ent->bit != bit)
+ continue;
+ if (ent->added == 0) {
+ hlist_del_rcu(&ent->node);
+ __nf_connlabel_entry_put(ent);
+ } else
+ ent->added--;
+ }
+ spin_unlock_bh(&connlabels_lock);
+}
+EXPORT_SYMBOL_GPL(nf_connlabel_del);
+
+static int labels_test_bit(unsigned int b, const struct nf_conn_labels *labels)
+{
+ if (b >= labels->bits)
+ return 0;
+
+ if (labels->u.label_bits & NF_CONNLABEL_NOT_ALLOCATED)
+ return test_bit(b, &labels->u.label_bits);
+
+ return test_bit(b, labels->u.label_bits_ptr);
+}
+
+static unsigned long *labels_get_bits(struct nf_conn_labels *labels)
+{
+ if (labels->u.label_bits & NF_CONNLABEL_NOT_ALLOCATED)
+ return &labels->u.label_bits;
+ return labels->u.label_bits_ptr;
+}
+
+static int __nf_connlabel_entry_get_cond(int b, struct nf_conn_labels *labels,
+ struct nf_connlabel_entry *e)
+{
+ unsigned long *bitv;
+
+ if (b >= labels->bits)
+ return -ENOSPC;
+
+ bitv = labels_get_bits(labels);
+
+ if (test_and_set_bit(b, bitv) == 0) {
+ if (__nf_connlabel_entry_get(e))
+ return 0;
+ clear_bit(b, bitv);
+ return -ENOENT;
+ }
+ return -EINVAL;
+}
+
+static void labels_free_bits(struct nf_conn_labels *labels)
+{
+ if (labels->u.label_bits & NF_CONNLABEL_NOT_ALLOCATED)
+ kfree(labels->u.label_bits_ptr);
+}
+
+/* look up label in internal store and attach its identifier to ct */
+int nf_connlabels_attach(struct nf_conn *ct, const char *label)
+{
+ struct nf_conn_labels *labels;
+ struct nf_connlabels_net *cl_net;
+ struct hlist_head *list;
+ struct nf_connlabel_entry *ent;
+ struct hlist_node *pos;
+ int ret = -ENOENT;
+
+ labels = nf_conn_labels_find(ct);
+ if (!labels)
+ return -ENOSPC;
+
+ cl_net = net_connlabels(nf_ct_net(ct));
+
+ rcu_read_lock();
+
+ list = __nf_connlabels_hlist(cl_net, label);
+ hlist_for_each_entry_rcu(ent, pos, list, node) {
+ if (labels_equal(ent, label)) {
+ ret = __nf_connlabel_entry_get_cond(ent->bit,
+ labels, ent);
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nf_connlabels_attach);
+
+/* must hold rcu_read_lock */
+void __nf_connlabel_set(const struct nf_conn *ct, u16 bit)
+{
+ struct nf_connlabel_entry *ent;
+ struct nf_connlabels_net *cl_net;
+ struct nf_conn_labels *labels;
+ struct hlist_head *list;
+ struct hlist_node *pos;
+
+ labels = nf_conn_labels_find(ct);
+ if (!labels)
+ return;
+ cl_net = net_connlabels(nf_ct_net(ct));
+
+ if (WARN_ON_ONCE(labels->bits <= bit))
+ return;
+
+ if (labels_test_bit(bit, labels))
+ return;
+
+ list = &cl_net->ct_htable_bits[label_bit_hash(bit)];
+
+ hlist_for_each_entry_rcu(ent, pos, list, node_bit) {
+ if (ent->bit == (int)bit) {
+ __nf_connlabel_entry_get_cond(bit, labels, ent);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(__nf_connlabel_set);
+
+/* rcu_read_locked via __nf_ct_ext_destroy() */
+static void nf_ct_ext_label_destroy(struct nf_conn *ct)
+{
+ struct nf_conn_labels *labels;
+ struct nf_connlabels_net *cl_net;
+ unsigned long b, *bits;
+
+ labels = nf_conn_labels_find(ct);
+ if (!labels)
+ return;
+
+ cl_net = net_connlabels(nf_ct_net(ct));
+
+ bits = labels_get_bits(labels);
+ for_each_set_bit(b, bits, labels->bits) {
+ struct nf_connlabel_entry *ent;
+ struct hlist_head *list;
+ struct hlist_node *pos;
+
+ list = &cl_net->ct_htable_bits[label_bit_hash(b)];
+
+ hlist_for_each_entry_rcu(ent, pos, list, node_bit) {
+ if ((unsigned long) ent->bit == b) {
+ nf_connlabel_entry_put(ent);
+ break;
+ }
+ }
+ }
+ labels_free_bits(labels);
+}
+
+bool nf_connlabel_match(const struct nf_conn *ct, u16 bit)
+{
+ struct nf_conn_labels *labels;
+ struct nf_connlabels_net *cl_net;
+
+ labels = nf_conn_labels_find(ct);
+ if (!labels)
+ return false;
+
+ cl_net = net_connlabels(nf_ct_net(ct));
+ return labels_test_bit(bit, labels);
+}
+EXPORT_SYMBOL_GPL(nf_connlabel_match);
+
+static struct nf_ct_ext_type labels_extend __read_mostly = {
+ .len = sizeof(struct nf_conn_labels),
+ .align = __alignof__(struct nf_conn_labels),
+ .destroy = nf_ct_ext_label_destroy,
+ .id = NF_CT_EXT_LABELS,
+};
+
+int nf_conntrack_labels_init(struct net *net)
+{
+ struct nf_connlabels_net *connlabels_net;
+ int ret = -ENOMEM;
+
+ BUILD_BUG_ON(sizeof(struct nf_conn_labels) > 255);
+
+ connlabels_net = net_connlabels(net);
+ connlabels_net->ct_htable = cl_alloc_hlist_table();
+ if (!connlabels_net->ct_htable)
+ goto err;
+ connlabels_net->ct_htable_bits = cl_alloc_hlist_table();
+ if (!connlabels_net->ct_htable_bits) {
+ vfree(connlabels_net->ct_htable);
+ goto err;
+ }
+
+ if (net_eq(net, &init_net)) {
+ hash_rnd = net_random();
+
+ ret = nf_ct_extend_register(&labels_extend);
+ if (ret < 0)
+ goto err_free;
+ }
+ return 0;
+ err_free:
+ vfree(connlabels_net->ct_htable);
+ vfree(connlabels_net->ct_htable_bits);
+ err:
+ pr_err("nf_ct_connlabels: Unable to register extension\n");
+ return ret;
+}
+
+void nf_conntrack_labels_fini(struct net *net)
+{
+ unsigned int i;
+ struct nf_connlabels_net *connlabels_net = net_connlabels(net);
+
+ if (net_eq(net, &init_net))
+ nf_ct_extend_unregister(&labels_extend);
+
+ for (i = 0; i < HT_SIZE; i++) {
+ BUG_ON(!hlist_empty(&connlabels_net->ct_htable[i]));
+ BUG_ON(!hlist_empty(&connlabels_net->ct_htable_bits[i]));
+ }
+ vfree(connlabels_net->ct_htable);
+ vfree(connlabels_net->ct_htable_bits);
+}
+
@@ -43,6 +43,7 @@
#include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h>
+#include <net/netfilter/nf_conntrack_labels.h>
#ifdef CONFIG_NF_NAT_NEEDED
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_l4proto.h>
@@ -1488,6 +1489,7 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
nf_ct_acct_ext_add(ct, GFP_ATOMIC);
nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
+ nf_ct_labels_ext_add(ct);
nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
/* we must add conntrack extensions before confirmation. */
ct->status |= IPS_CONFIRMED;
new file mode 100644
@@ -0,0 +1,141 @@
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_labels.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_connlabel.h>
+
+static bool
+connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_connlabel_mtinfo *info = par->matchinfo;
+ enum ip_conntrack_info ctinfo;
+ const struct nf_conn *ct;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct == NULL || nf_ct_is_untracked(ct))
+ return false;
+
+ return nf_connlabel_match(ct, info->bit) ^ info->invert;
+}
+
+static unsigned int
+connlabel_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ const struct xt_connlabel_tginfo *info = par->targinfo;
+ enum ip_conntrack_info ctinfo;
+ const struct nf_conn *ct;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (ct == NULL || nf_ct_is_untracked(ct))
+ return XT_CONTINUE;
+
+ __nf_connlabel_set(ct, info->bit);
+ return XT_CONTINUE;
+}
+
+static int connlabels_common_checks(struct net *net, unsigned int family, const char *label)
+{
+ int ret;
+
+ if (strnlen(label, XT_CONNLABEL_MAXLEN) >= XT_CONNLABEL_MAXLEN)
+ return -ENAMETOOLONG;
+
+ ret = nf_ct_l3proto_try_module_get(family);
+ if (ret < 0) {
+ pr_info("cannot load conntrack support for proto=%u\n", family);
+ return ret;
+ }
+
+ ret = nf_connlabel_add(net, label);
+ if (ret < 0)
+ nf_ct_l3proto_module_put(family);
+ return ret;
+}
+
+static int connlabel_tg_check(const struct xt_tgchk_param *par)
+{
+ struct xt_connlabel_tginfo *info = par->targinfo;
+ int ret;
+
+ ret = connlabels_common_checks(par->net, par->family, info->labelname);
+ if (ret < 0)
+ return ret;
+ info->bit = ret;
+ return 0;
+}
+
+static void connlabel_tg_destroy(const struct xt_tgdtor_param *par)
+{
+ struct xt_connlabel_tginfo *info = par->targinfo;
+
+ nf_connlabel_del(par->net, info->bit);
+ nf_ct_l3proto_module_put(par->family);
+}
+
+static int connlabel_mt_check(const struct xt_mtchk_param *par)
+{
+ struct xt_connlabel_mtinfo *info = par->matchinfo;
+ char name[XT_CONNLABEL_MAXLEN];
+ int ret;
+
+ strlcpy(name, info->labelname, sizeof(name));
+
+ ret = connlabels_common_checks(par->net, par->family, name);
+ if (ret < 0)
+ return ret;
+ info->bit = ret;
+ return 0;
+}
+
+static void connlabel_mt_destroy(const struct xt_mtdtor_param *par)
+{
+ struct xt_connlabel_mtinfo *info = par->matchinfo;
+
+ nf_connlabel_del(par->net, info->bit);
+ nf_ct_l3proto_module_put(par->family);
+}
+
+static struct xt_target connlabels_tg_reg __read_mostly = {
+ .name = "CONNLABEL",
+ .family = NFPROTO_UNSPEC,
+ .checkentry = connlabel_tg_check,
+ .target = connlabel_tg,
+ .targetsize = sizeof(struct xt_connlabel_tginfo),
+ .destroy = connlabel_tg_destroy,
+ .me = THIS_MODULE,
+};
+
+static struct xt_match connlabels_mt_reg __read_mostly = {
+ .name = "connlabel",
+ .family = NFPROTO_UNSPEC,
+ .checkentry = connlabel_mt_check,
+ .match = connlabel_mt,
+ .matchsize = sizeof(struct xt_connlabel_mtinfo),
+ .destroy = connlabel_mt_destroy,
+ .me = THIS_MODULE,
+};
+
+static int __init connlabel_mt_init(void)
+{
+ int ret;
+
+ ret = xt_register_target(&connlabels_tg_reg);
+ if (ret < 0)
+ return ret;
+ ret = xt_register_match(&connlabels_mt_reg);
+ if (ret < 0) {
+ xt_unregister_target(&connlabels_tg_reg);
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit connlabel_mt_exit(void)
+{
+ xt_unregister_target(&connlabels_tg_reg);
+ xt_unregister_match(&connlabels_mt_reg);
+}
+
+module_init(connlabel_mt_init);
+module_exit(connlabel_mt_exit);