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

netfilter: conntrack: Flush connections with a given mark

This patch adds support for selective flushing of conntrack mappings.
By adding CTA_MARK and CTA_MARK_MASK to a delete-message, the mark (and
mask) is checked before a connection is deleted while flushing.

Configuring the flush is moved out of ctnetlink_del_conntrack(), and
instead of calling nf_conntrack_flush_report(), we always call
nf_ct_iterate_cleanup(). This enables us to only make one call from the
new ctnetlink_flush_conntrack() and makes it easy to add more filter
parameters.

Filtering is done in the ctnetlink_filter_match()-function, which is
also called from ctnetlink_dump_table(). ctnetlink_dump_filter has been
renamed ctnetlink_filter, to indicated that it is no longer only used
when dumping conntrack entries.

Moreover, reject mark filters with -EOPNOTSUPP if no ct mark support is
available.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Kristian Evensen and committed by
Pablo Neira Ayuso
866476f3 b44b565c

+64 -25
+64 -25
net/netfilter/nf_conntrack_netlink.c
··· 749 749 return 0; 750 750 } 751 751 752 - struct ctnetlink_dump_filter { 752 + struct ctnetlink_filter { 753 753 struct { 754 754 u_int32_t val; 755 755 u_int32_t mask; 756 756 } mark; 757 757 }; 758 + 759 + static struct ctnetlink_filter * 760 + ctnetlink_alloc_filter(const struct nlattr * const cda[]) 761 + { 762 + #ifdef CONFIG_NF_CONNTRACK_MARK 763 + struct ctnetlink_filter *filter; 764 + 765 + filter = kzalloc(sizeof(*filter), GFP_KERNEL); 766 + if (filter == NULL) 767 + return ERR_PTR(-ENOMEM); 768 + 769 + filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK])); 770 + filter->mark.mask = ntohl(nla_get_be32(cda[CTA_MARK_MASK])); 771 + 772 + return filter; 773 + #else 774 + return ERR_PTR(-EOPNOTSUPP); 775 + #endif 776 + } 777 + 778 + static int ctnetlink_filter_match(struct nf_conn *ct, void *data) 779 + { 780 + struct ctnetlink_filter *filter = data; 781 + 782 + if (filter == NULL) 783 + return 1; 784 + 785 + #ifdef CONFIG_NF_CONNTRACK_MARK 786 + if ((ct->mark & filter->mark.mask) == filter->mark.val) 787 + return 1; 788 + #endif 789 + 790 + return 0; 791 + } 758 792 759 793 static int 760 794 ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) ··· 801 767 u_int8_t l3proto = nfmsg->nfgen_family; 802 768 int res; 803 769 spinlock_t *lockp; 804 - 805 - #ifdef CONFIG_NF_CONNTRACK_MARK 806 - const struct ctnetlink_dump_filter *filter = cb->data; 807 - #endif 808 770 809 771 last = (struct nf_conn *)cb->args[1]; 810 772 ··· 828 798 continue; 829 799 cb->args[1] = 0; 830 800 } 831 - #ifdef CONFIG_NF_CONNTRACK_MARK 832 - if (filter && !((ct->mark & filter->mark.mask) == 833 - filter->mark.val)) { 801 + if (!ctnetlink_filter_match(ct, cb->data)) 834 802 continue; 835 - } 836 - #endif 803 + 837 804 rcu_read_lock(); 838 805 res = 839 806 ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid, ··· 1028 1001 .len = NF_CT_LABELS_MAX_SIZE }, 1029 1002 }; 1030 1003 1004 + static int ctnetlink_flush_conntrack(struct net *net, 1005 + const struct nlattr * const cda[], 1006 + u32 portid, int report) 1007 + { 1008 + struct ctnetlink_filter *filter = NULL; 1009 + 1010 + if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { 1011 + filter = ctnetlink_alloc_filter(cda); 1012 + if (IS_ERR(filter)) 1013 + return PTR_ERR(filter); 1014 + } 1015 + 1016 + nf_ct_iterate_cleanup(net, ctnetlink_filter_match, filter, 1017 + portid, report); 1018 + kfree(filter); 1019 + 1020 + return 0; 1021 + } 1022 + 1031 1023 static int 1032 1024 ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, 1033 1025 const struct nlmsghdr *nlh, ··· 1070 1024 else if (cda[CTA_TUPLE_REPLY]) 1071 1025 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3); 1072 1026 else { 1073 - /* Flush the whole table */ 1074 - nf_conntrack_flush_report(net, 1075 - NETLINK_CB(skb).portid, 1076 - nlmsg_report(nlh)); 1077 - return 0; 1027 + return ctnetlink_flush_conntrack(net, cda, 1028 + NETLINK_CB(skb).portid, 1029 + nlmsg_report(nlh)); 1078 1030 } 1079 1031 1080 1032 if (err < 0) ··· 1120 1076 .dump = ctnetlink_dump_table, 1121 1077 .done = ctnetlink_done, 1122 1078 }; 1123 - #ifdef CONFIG_NF_CONNTRACK_MARK 1079 + 1124 1080 if (cda[CTA_MARK] && cda[CTA_MARK_MASK]) { 1125 - struct ctnetlink_dump_filter *filter; 1081 + struct ctnetlink_filter *filter; 1126 1082 1127 - filter = kzalloc(sizeof(struct ctnetlink_dump_filter), 1128 - GFP_ATOMIC); 1129 - if (filter == NULL) 1130 - return -ENOMEM; 1083 + filter = ctnetlink_alloc_filter(cda); 1084 + if (IS_ERR(filter)) 1085 + return PTR_ERR(filter); 1131 1086 1132 - filter->mark.val = ntohl(nla_get_be32(cda[CTA_MARK])); 1133 - filter->mark.mask = 1134 - ntohl(nla_get_be32(cda[CTA_MARK_MASK])); 1135 1087 c.data = filter; 1136 1088 } 1137 - #endif 1138 1089 return netlink_dump_start(ctnl, skb, nlh, &c); 1139 1090 } 1140 1091