Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: add connlabel conntrack extension

similar to connmarks, except labels are bit-based; i.e.
all labels may be attached to a flow at the same time.

Up to 128 labels are supported. Supporting more labels
is possible, but requires increasing the ct offset delta
from u8 to u16 type due to increased extension sizes.

Mapping of bit-identifier to label name is done in userspace.

The extension is enabled at run-time once "-m connlabel" netfilter
rules are added.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
c539f017 7266507d

+281
+4
include/net/netfilter/nf_conntrack_extend.h
··· 23 23 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT 24 24 NF_CT_EXT_TIMEOUT, 25 25 #endif 26 + #ifdef CONFIG_NF_CONNTRACK_LABELS 27 + NF_CT_EXT_LABELS, 28 + #endif 26 29 NF_CT_EXT_NUM, 27 30 }; 28 31 ··· 36 33 #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone 37 34 #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp 38 35 #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout 36 + #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels 39 37 40 38 /* Extensions: optional stuff which isn't permanently in struct. */ 41 39 struct nf_ct_ext {
+55
include/net/netfilter/nf_conntrack_labels.h
··· 1 + #include <linux/types.h> 2 + #include <net/net_namespace.h> 3 + #include <linux/netfilter/nf_conntrack_common.h> 4 + #include <linux/netfilter/nf_conntrack_tuple_common.h> 5 + #include <net/netfilter/nf_conntrack.h> 6 + #include <net/netfilter/nf_conntrack_extend.h> 7 + 8 + #include <uapi/linux/netfilter/xt_connlabel.h> 9 + 10 + struct nf_conn_labels { 11 + u8 words; 12 + unsigned long bits[]; 13 + }; 14 + 15 + static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct) 16 + { 17 + #ifdef CONFIG_NF_CONNTRACK_LABELS 18 + return nf_ct_ext_find(ct, NF_CT_EXT_LABELS); 19 + #else 20 + return NULL; 21 + #endif 22 + } 23 + 24 + static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct) 25 + { 26 + #ifdef CONFIG_NF_CONNTRACK_LABELS 27 + struct nf_conn_labels *cl_ext; 28 + struct net *net = nf_ct_net(ct); 29 + u8 words; 30 + 31 + words = ACCESS_ONCE(net->ct.label_words); 32 + if (words == 0 || WARN_ON_ONCE(words > 8)) 33 + return NULL; 34 + 35 + cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS, 36 + words * sizeof(long), GFP_ATOMIC); 37 + if (cl_ext != NULL) 38 + cl_ext->words = words; 39 + 40 + return cl_ext; 41 + #else 42 + return NULL; 43 + #endif 44 + } 45 + 46 + bool nf_connlabel_match(const struct nf_conn *ct, u16 bit); 47 + int nf_connlabel_set(struct nf_conn *ct, u16 bit); 48 + 49 + #ifdef CONFIG_NF_CONNTRACK_LABELS 50 + int nf_conntrack_labels_init(struct net *net); 51 + void nf_conntrack_labels_fini(struct net *net); 52 + #else 53 + static inline int nf_conntrack_labels_init(struct net *n) { return 0; } 54 + static inline void nf_conntrack_labels_fini(struct net *net) {} 55 + #endif
+4
include/net/netns/conntrack.h
··· 84 84 int sysctl_auto_assign_helper; 85 85 bool auto_assign_helper_warned; 86 86 struct nf_ip_net nf_ct_proto; 87 + #if defined(CONFIG_NF_CONNTRACK_LABELS) 88 + unsigned int labels_used; 89 + u8 label_words; 90 + #endif 87 91 #ifdef CONFIG_NF_NAT_NEEDED 88 92 struct hlist_head *nat_bysource; 89 93 unsigned int nat_htable_size;
+12
include/uapi/linux/netfilter/xt_connlabel.h
··· 1 + #include <linux/types.h> 2 + 3 + #define XT_CONNLABEL_MAXBIT 127 4 + enum xt_connlabel_mtopts { 5 + XT_CONNLABEL_OP_INVERT = 1 << 0, 6 + XT_CONNLABEL_OP_SET = 1 << 1, 7 + }; 8 + 9 + struct xt_connlabel_mtinfo { 10 + __u16 bit; 11 + __u16 options; 12 + };
+18
net/netfilter/Kconfig
··· 124 124 125 125 If unsure, say `N'. 126 126 127 + config NF_CONNTRACK_LABELS 128 + bool 129 + help 130 + This option enables support for assigning user-defined flag bits 131 + to connection tracking entries. It selected by the connlabel match. 132 + 127 133 config NF_CT_PROTO_DCCP 128 134 tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' 129 135 depends on EXPERIMENTAL ··· 847 841 848 842 If you want to compile it as a module, say M here and read 849 843 <file:Documentation/kbuild/modules.txt>. If unsure, say `N'. 844 + 845 + config NETFILTER_XT_MATCH_CONNLABEL 846 + tristate '"connlabel" match support' 847 + select NF_CONNTRACK_LABELS 848 + depends on NETFILTER_ADVANCED 849 + ---help--- 850 + This match allows you to test and assign userspace-defined labels names 851 + to a connection. The kernel only stores bit values - mapping 852 + names to bits is done by userspace. 853 + 854 + Unlike connmark, more than 32 flag bits may be assigned to a 855 + connection simultaneously. 850 856 851 857 config NETFILTER_XT_MATCH_CONNLIMIT 852 858 tristate '"connlimit" match support"'
+2
net/netfilter/Makefile
··· 4 4 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o 5 5 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o 6 6 nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o 7 + nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o 7 8 8 9 obj-$(CONFIG_NETFILTER) = netfilter.o 9 10 ··· 102 101 obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o 103 102 obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o 104 103 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o 104 + obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLABEL) += xt_connlabel.o 105 105 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o 106 106 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o 107 107 obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o
+12
net/netfilter/nf_conntrack_core.c
··· 45 45 #include <net/netfilter/nf_conntrack_zones.h> 46 46 #include <net/netfilter/nf_conntrack_timestamp.h> 47 47 #include <net/netfilter/nf_conntrack_timeout.h> 48 + #include <net/netfilter/nf_conntrack_labels.h> 48 49 #include <net/netfilter/nf_nat.h> 49 50 #include <net/netfilter/nf_nat_core.h> 50 51 ··· 764 763 } 765 764 EXPORT_SYMBOL_GPL(nf_conntrack_free); 766 765 766 + 767 767 /* Allocate a new conntrack: we return -ENOMEM if classification 768 768 failed due to stress. Otherwise it really is unclassifiable. */ 769 769 static struct nf_conntrack_tuple_hash * ··· 811 809 812 810 nf_ct_acct_ext_add(ct, GFP_ATOMIC); 813 811 nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); 812 + nf_ct_labels_ext_add(ct); 814 813 815 814 ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; 816 815 nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, ··· 1355 1352 } 1356 1353 1357 1354 nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); 1355 + nf_conntrack_labels_fini(net); 1358 1356 nf_conntrack_helper_fini(net); 1359 1357 nf_conntrack_timeout_fini(net); 1360 1358 nf_conntrack_ecache_fini(net); ··· 1587 1583 ret = nf_conntrack_helper_init(net); 1588 1584 if (ret < 0) 1589 1585 goto err_helper; 1586 + 1587 + ret = nf_conntrack_labels_init(net); 1588 + if (ret < 0) 1589 + goto err_labels; 1590 + 1590 1591 return 0; 1592 + 1593 + err_labels: 1594 + nf_conntrack_helper_fini(net); 1591 1595 err_helper: 1592 1596 nf_conntrack_timeout_fini(net); 1593 1597 err_timeout:
+72
net/netfilter/nf_conntrack_labels.c
··· 1 + /* 2 + * test/set flag bits stored in conntrack extension area. 3 + * 4 + * (C) 2013 Astaro GmbH & Co KG 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/ctype.h> 12 + #include <linux/export.h> 13 + #include <linux/jhash.h> 14 + #include <linux/spinlock.h> 15 + #include <linux/types.h> 16 + #include <linux/slab.h> 17 + 18 + #include <net/netfilter/nf_conntrack_ecache.h> 19 + #include <net/netfilter/nf_conntrack_labels.h> 20 + 21 + static unsigned int label_bits(const struct nf_conn_labels *l) 22 + { 23 + unsigned int longs = l->words; 24 + return longs * BITS_PER_LONG; 25 + } 26 + 27 + bool nf_connlabel_match(const struct nf_conn *ct, u16 bit) 28 + { 29 + struct nf_conn_labels *labels = nf_ct_labels_find(ct); 30 + 31 + if (!labels) 32 + return false; 33 + 34 + return bit < label_bits(labels) && test_bit(bit, labels->bits); 35 + } 36 + EXPORT_SYMBOL_GPL(nf_connlabel_match); 37 + 38 + int nf_connlabel_set(struct nf_conn *ct, u16 bit) 39 + { 40 + struct nf_conn_labels *labels = nf_ct_labels_find(ct); 41 + 42 + if (!labels || bit >= label_bits(labels)) 43 + return -ENOSPC; 44 + 45 + if (test_bit(bit, labels->bits)) 46 + return 0; 47 + 48 + if (test_and_set_bit(bit, labels->bits)) 49 + return 0; 50 + 51 + return 0; 52 + } 53 + EXPORT_SYMBOL_GPL(nf_connlabel_set); 54 + 55 + static struct nf_ct_ext_type labels_extend __read_mostly = { 56 + .len = sizeof(struct nf_conn_labels), 57 + .align = __alignof__(struct nf_conn_labels), 58 + .id = NF_CT_EXT_LABELS, 59 + }; 60 + 61 + int nf_conntrack_labels_init(struct net *net) 62 + { 63 + if (net_eq(net, &init_net)) 64 + return nf_ct_extend_register(&labels_extend); 65 + return 0; 66 + } 67 + 68 + void nf_conntrack_labels_fini(struct net *net) 69 + { 70 + if (net_eq(net, &init_net)) 71 + nf_ct_extend_unregister(&labels_extend); 72 + }
+3
net/netfilter/nf_conntrack_netlink.c
··· 43 43 #include <net/netfilter/nf_conntrack_acct.h> 44 44 #include <net/netfilter/nf_conntrack_zones.h> 45 45 #include <net/netfilter/nf_conntrack_timestamp.h> 46 + #include <net/netfilter/nf_conntrack_labels.h> 46 47 #ifdef CONFIG_NF_NAT_NEEDED 47 48 #include <net/netfilter/nf_nat_core.h> 48 49 #include <net/netfilter/nf_nat_l4proto.h> ··· 1599 1598 nf_ct_acct_ext_add(ct, GFP_ATOMIC); 1600 1599 nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); 1601 1600 nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); 1601 + nf_ct_labels_ext_add(ct); 1602 + 1602 1603 /* we must add conntrack extensions before confirmation. */ 1603 1604 ct->status |= IPS_CONFIRMED; 1604 1605
+99
net/netfilter/xt_connlabel.c
··· 1 + /* 2 + * (C) 2013 Astaro GmbH & Co KG 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + #include <linux/module.h> 10 + #include <linux/skbuff.h> 11 + #include <net/netfilter/nf_conntrack.h> 12 + #include <net/netfilter/nf_conntrack_labels.h> 13 + #include <linux/netfilter/x_tables.h> 14 + 15 + MODULE_LICENSE("GPL"); 16 + MODULE_AUTHOR("Florian Westphal <fw@strlen.de>"); 17 + MODULE_DESCRIPTION("Xtables: add/match connection trackling labels"); 18 + MODULE_ALIAS("ipt_connlabel"); 19 + MODULE_ALIAS("ip6t_connlabel"); 20 + 21 + static bool 22 + connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par) 23 + { 24 + const struct xt_connlabel_mtinfo *info = par->matchinfo; 25 + enum ip_conntrack_info ctinfo; 26 + struct nf_conn *ct; 27 + bool invert = info->options & XT_CONNLABEL_OP_INVERT; 28 + 29 + ct = nf_ct_get(skb, &ctinfo); 30 + if (ct == NULL || nf_ct_is_untracked(ct)) 31 + return invert; 32 + 33 + if (info->options & XT_CONNLABEL_OP_SET) 34 + return (nf_connlabel_set(ct, info->bit) == 0) ^ invert; 35 + 36 + return nf_connlabel_match(ct, info->bit) ^ invert; 37 + } 38 + 39 + static int connlabel_mt_check(const struct xt_mtchk_param *par) 40 + { 41 + const int options = XT_CONNLABEL_OP_INVERT | 42 + XT_CONNLABEL_OP_SET; 43 + struct xt_connlabel_mtinfo *info = par->matchinfo; 44 + int ret; 45 + size_t words; 46 + 47 + if (info->bit > XT_CONNLABEL_MAXBIT) 48 + return -ERANGE; 49 + 50 + if (info->options & ~options) { 51 + pr_err("Unknown options in mask %x\n", info->options); 52 + return -EINVAL; 53 + } 54 + 55 + ret = nf_ct_l3proto_try_module_get(par->family); 56 + if (ret < 0) { 57 + pr_info("cannot load conntrack support for proto=%u\n", 58 + par->family); 59 + return ret; 60 + } 61 + 62 + par->net->ct.labels_used++; 63 + words = BITS_TO_LONGS(info->bit+1); 64 + if (words > par->net->ct.label_words) 65 + par->net->ct.label_words = words; 66 + 67 + return ret; 68 + } 69 + 70 + static void connlabel_mt_destroy(const struct xt_mtdtor_param *par) 71 + { 72 + par->net->ct.labels_used--; 73 + if (par->net->ct.labels_used == 0) 74 + par->net->ct.label_words = 0; 75 + nf_ct_l3proto_module_put(par->family); 76 + } 77 + 78 + static struct xt_match connlabels_mt_reg __read_mostly = { 79 + .name = "connlabel", 80 + .family = NFPROTO_UNSPEC, 81 + .checkentry = connlabel_mt_check, 82 + .match = connlabel_mt, 83 + .matchsize = sizeof(struct xt_connlabel_mtinfo), 84 + .destroy = connlabel_mt_destroy, 85 + .me = THIS_MODULE, 86 + }; 87 + 88 + static int __init connlabel_mt_init(void) 89 + { 90 + return xt_register_match(&connlabels_mt_reg); 91 + } 92 + 93 + static void __exit connlabel_mt_exit(void) 94 + { 95 + xt_unregister_match(&connlabels_mt_reg); 96 + } 97 + 98 + module_init(connlabel_mt_init); 99 + module_exit(connlabel_mt_exit);