diff options
author | Patrick McHardy <kaber@trash.net> | 2010-02-03 08:13:03 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-02-03 08:40:17 -0500 |
commit | b2a15a604d379af323645e330638e2cfcc696aff (patch) | |
tree | 75a863636fae6f5e3fcf3dacbdee3ccf1f8b7c8d | |
parent | 0cebe4b4163b6373c9d24c1a192939777bc27e55 (diff) |
netfilter: nf_conntrack: support conntrack templates
Support initializing selected parameters of new conntrack entries from a
"conntrack template", which is a specially marked conntrack entry attached
to the skb.
Currently the helper and the event delivery masks can be initialized this
way.
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/linux/netfilter/nf_conntrack_common.h | 4 | ||||
-rw-r--r-- | include/net/netfilter/nf_conntrack.h | 5 | ||||
-rw-r--r-- | include/net/netfilter/nf_conntrack_helper.h | 3 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_defrag_ipv4.c | 2 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 50 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_helper.c | 17 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 2 |
8 files changed, 61 insertions, 24 deletions
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index ebfed90733f7..c608677dda60 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h | |||
@@ -72,6 +72,10 @@ enum ip_conntrack_status { | |||
72 | /* Connection has fixed timeout. */ | 72 | /* Connection has fixed timeout. */ |
73 | IPS_FIXED_TIMEOUT_BIT = 10, | 73 | IPS_FIXED_TIMEOUT_BIT = 10, |
74 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), | 74 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), |
75 | |||
76 | /* Conntrack is a template */ | ||
77 | IPS_TEMPLATE_BIT = 11, | ||
78 | IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT), | ||
75 | }; | 79 | }; |
76 | 80 | ||
77 | /* Connection tracking event types */ | 81 | /* Connection tracking event types */ |
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index a0904adfb8f7..5043d61c99a7 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h | |||
@@ -272,6 +272,11 @@ nf_conntrack_alloc(struct net *net, | |||
272 | const struct nf_conntrack_tuple *repl, | 272 | const struct nf_conntrack_tuple *repl, |
273 | gfp_t gfp); | 273 | gfp_t gfp); |
274 | 274 | ||
275 | static inline int nf_ct_is_template(const struct nf_conn *ct) | ||
276 | { | ||
277 | return test_bit(IPS_TEMPLATE_BIT, &ct->status); | ||
278 | } | ||
279 | |||
275 | /* It's confirmed if it is, or has been in the hash table. */ | 280 | /* It's confirmed if it is, or has been in the hash table. */ |
276 | static inline int nf_ct_is_confirmed(struct nf_conn *ct) | 281 | static inline int nf_ct_is_confirmed(struct nf_conn *ct) |
277 | { | 282 | { |
diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 86be7c4816d6..e17aaa3e19fd 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h | |||
@@ -47,7 +47,8 @@ extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); | |||
47 | 47 | ||
48 | extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); | 48 | extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); |
49 | 49 | ||
50 | extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags); | 50 | extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, |
51 | gfp_t flags); | ||
51 | 52 | ||
52 | extern void nf_ct_helper_destroy(struct nf_conn *ct); | 53 | extern void nf_ct_helper_destroy(struct nf_conn *ct); |
53 | 54 | ||
diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index 331ead3ebd1b..77627fa80561 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c | |||
@@ -59,7 +59,7 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, | |||
59 | #if !defined(CONFIG_NF_NAT) && !defined(CONFIG_NF_NAT_MODULE) | 59 | #if !defined(CONFIG_NF_NAT) && !defined(CONFIG_NF_NAT_MODULE) |
60 | /* Previously seen (loopback)? Ignore. Do this before | 60 | /* Previously seen (loopback)? Ignore. Do this before |
61 | fragment check. */ | 61 | fragment check. */ |
62 | if (skb->nfct) | 62 | if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) |
63 | return NF_ACCEPT; | 63 | return NF_ACCEPT; |
64 | #endif | 64 | #endif |
65 | #endif | 65 | #endif |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 0956ebabbff2..55ce22e5de49 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -212,7 +212,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum, | |||
212 | struct sk_buff *reasm; | 212 | struct sk_buff *reasm; |
213 | 213 | ||
214 | /* Previously seen (loopback)? */ | 214 | /* Previously seen (loopback)? */ |
215 | if (skb->nfct) | 215 | if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) |
216 | return NF_ACCEPT; | 216 | return NF_ACCEPT; |
217 | 217 | ||
218 | reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); | 218 | reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 53b8da6ad6b7..471e2a79d26f 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -618,7 +618,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_free); | |||
618 | /* Allocate a new conntrack: we return -ENOMEM if classification | 618 | /* Allocate a new conntrack: we return -ENOMEM if classification |
619 | failed due to stress. Otherwise it really is unclassifiable. */ | 619 | failed due to stress. Otherwise it really is unclassifiable. */ |
620 | static struct nf_conntrack_tuple_hash * | 620 | static struct nf_conntrack_tuple_hash * |
621 | init_conntrack(struct net *net, | 621 | init_conntrack(struct net *net, struct nf_conn *tmpl, |
622 | const struct nf_conntrack_tuple *tuple, | 622 | const struct nf_conntrack_tuple *tuple, |
623 | struct nf_conntrack_l3proto *l3proto, | 623 | struct nf_conntrack_l3proto *l3proto, |
624 | struct nf_conntrack_l4proto *l4proto, | 624 | struct nf_conntrack_l4proto *l4proto, |
@@ -628,6 +628,7 @@ init_conntrack(struct net *net, | |||
628 | struct nf_conn *ct; | 628 | struct nf_conn *ct; |
629 | struct nf_conn_help *help; | 629 | struct nf_conn_help *help; |
630 | struct nf_conntrack_tuple repl_tuple; | 630 | struct nf_conntrack_tuple repl_tuple; |
631 | struct nf_conntrack_ecache *ecache; | ||
631 | struct nf_conntrack_expect *exp; | 632 | struct nf_conntrack_expect *exp; |
632 | 633 | ||
633 | if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) { | 634 | if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) { |
@@ -648,7 +649,11 @@ init_conntrack(struct net *net, | |||
648 | } | 649 | } |
649 | 650 | ||
650 | nf_ct_acct_ext_add(ct, GFP_ATOMIC); | 651 | nf_ct_acct_ext_add(ct, GFP_ATOMIC); |
651 | nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); | 652 | |
653 | ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; | ||
654 | nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, | ||
655 | ecache ? ecache->expmask : 0, | ||
656 | GFP_ATOMIC); | ||
652 | 657 | ||
653 | spin_lock_bh(&nf_conntrack_lock); | 658 | spin_lock_bh(&nf_conntrack_lock); |
654 | exp = nf_ct_find_expectation(net, tuple); | 659 | exp = nf_ct_find_expectation(net, tuple); |
@@ -673,7 +678,7 @@ init_conntrack(struct net *net, | |||
673 | nf_conntrack_get(&ct->master->ct_general); | 678 | nf_conntrack_get(&ct->master->ct_general); |
674 | NF_CT_STAT_INC(net, expect_new); | 679 | NF_CT_STAT_INC(net, expect_new); |
675 | } else { | 680 | } else { |
676 | __nf_ct_try_assign_helper(ct, GFP_ATOMIC); | 681 | __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC); |
677 | NF_CT_STAT_INC(net, new); | 682 | NF_CT_STAT_INC(net, new); |
678 | } | 683 | } |
679 | 684 | ||
@@ -694,7 +699,7 @@ init_conntrack(struct net *net, | |||
694 | 699 | ||
695 | /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ | 700 | /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ |
696 | static inline struct nf_conn * | 701 | static inline struct nf_conn * |
697 | resolve_normal_ct(struct net *net, | 702 | resolve_normal_ct(struct net *net, struct nf_conn *tmpl, |
698 | struct sk_buff *skb, | 703 | struct sk_buff *skb, |
699 | unsigned int dataoff, | 704 | unsigned int dataoff, |
700 | u_int16_t l3num, | 705 | u_int16_t l3num, |
@@ -718,7 +723,8 @@ resolve_normal_ct(struct net *net, | |||
718 | /* look for tuple match */ | 723 | /* look for tuple match */ |
719 | h = nf_conntrack_find_get(net, &tuple); | 724 | h = nf_conntrack_find_get(net, &tuple); |
720 | if (!h) { | 725 | if (!h) { |
721 | h = init_conntrack(net, &tuple, l3proto, l4proto, skb, dataoff); | 726 | h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto, |
727 | skb, dataoff); | ||
722 | if (!h) | 728 | if (!h) |
723 | return NULL; | 729 | return NULL; |
724 | if (IS_ERR(h)) | 730 | if (IS_ERR(h)) |
@@ -755,7 +761,7 @@ unsigned int | |||
755 | nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, | 761 | nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, |
756 | struct sk_buff *skb) | 762 | struct sk_buff *skb) |
757 | { | 763 | { |
758 | struct nf_conn *ct; | 764 | struct nf_conn *ct, *tmpl = NULL; |
759 | enum ip_conntrack_info ctinfo; | 765 | enum ip_conntrack_info ctinfo; |
760 | struct nf_conntrack_l3proto *l3proto; | 766 | struct nf_conntrack_l3proto *l3proto; |
761 | struct nf_conntrack_l4proto *l4proto; | 767 | struct nf_conntrack_l4proto *l4proto; |
@@ -764,10 +770,14 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, | |||
764 | int set_reply = 0; | 770 | int set_reply = 0; |
765 | int ret; | 771 | int ret; |
766 | 772 | ||
767 | /* Previously seen (loopback or untracked)? Ignore. */ | ||
768 | if (skb->nfct) { | 773 | if (skb->nfct) { |
769 | NF_CT_STAT_INC_ATOMIC(net, ignore); | 774 | /* Previously seen (loopback or untracked)? Ignore. */ |
770 | return NF_ACCEPT; | 775 | tmpl = (struct nf_conn *)skb->nfct; |
776 | if (!nf_ct_is_template(tmpl)) { | ||
777 | NF_CT_STAT_INC_ATOMIC(net, ignore); | ||
778 | return NF_ACCEPT; | ||
779 | } | ||
780 | skb->nfct = NULL; | ||
771 | } | 781 | } |
772 | 782 | ||
773 | /* rcu_read_lock()ed by nf_hook_slow */ | 783 | /* rcu_read_lock()ed by nf_hook_slow */ |
@@ -778,7 +788,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, | |||
778 | pr_debug("not prepared to track yet or error occured\n"); | 788 | pr_debug("not prepared to track yet or error occured\n"); |
779 | NF_CT_STAT_INC_ATOMIC(net, error); | 789 | NF_CT_STAT_INC_ATOMIC(net, error); |
780 | NF_CT_STAT_INC_ATOMIC(net, invalid); | 790 | NF_CT_STAT_INC_ATOMIC(net, invalid); |
781 | return -ret; | 791 | ret = -ret; |
792 | goto out; | ||
782 | } | 793 | } |
783 | 794 | ||
784 | l4proto = __nf_ct_l4proto_find(pf, protonum); | 795 | l4proto = __nf_ct_l4proto_find(pf, protonum); |
@@ -791,22 +802,25 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, | |||
791 | if (ret <= 0) { | 802 | if (ret <= 0) { |
792 | NF_CT_STAT_INC_ATOMIC(net, error); | 803 | NF_CT_STAT_INC_ATOMIC(net, error); |
793 | NF_CT_STAT_INC_ATOMIC(net, invalid); | 804 | NF_CT_STAT_INC_ATOMIC(net, invalid); |
794 | return -ret; | 805 | ret = -ret; |
806 | goto out; | ||
795 | } | 807 | } |
796 | } | 808 | } |
797 | 809 | ||
798 | ct = resolve_normal_ct(net, skb, dataoff, pf, protonum, | 810 | ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, |
799 | l3proto, l4proto, &set_reply, &ctinfo); | 811 | l3proto, l4proto, &set_reply, &ctinfo); |
800 | if (!ct) { | 812 | if (!ct) { |
801 | /* Not valid part of a connection */ | 813 | /* Not valid part of a connection */ |
802 | NF_CT_STAT_INC_ATOMIC(net, invalid); | 814 | NF_CT_STAT_INC_ATOMIC(net, invalid); |
803 | return NF_ACCEPT; | 815 | ret = NF_ACCEPT; |
816 | goto out; | ||
804 | } | 817 | } |
805 | 818 | ||
806 | if (IS_ERR(ct)) { | 819 | if (IS_ERR(ct)) { |
807 | /* Too stressed to deal. */ | 820 | /* Too stressed to deal. */ |
808 | NF_CT_STAT_INC_ATOMIC(net, drop); | 821 | NF_CT_STAT_INC_ATOMIC(net, drop); |
809 | return NF_DROP; | 822 | ret = NF_DROP; |
823 | goto out; | ||
810 | } | 824 | } |
811 | 825 | ||
812 | NF_CT_ASSERT(skb->nfct); | 826 | NF_CT_ASSERT(skb->nfct); |
@@ -821,11 +835,15 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, | |||
821 | NF_CT_STAT_INC_ATOMIC(net, invalid); | 835 | NF_CT_STAT_INC_ATOMIC(net, invalid); |
822 | if (ret == -NF_DROP) | 836 | if (ret == -NF_DROP) |
823 | NF_CT_STAT_INC_ATOMIC(net, drop); | 837 | NF_CT_STAT_INC_ATOMIC(net, drop); |
824 | return -ret; | 838 | ret = -ret; |
839 | goto out; | ||
825 | } | 840 | } |
826 | 841 | ||
827 | if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) | 842 | if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) |
828 | nf_conntrack_event_cache(IPCT_REPLY, ct); | 843 | nf_conntrack_event_cache(IPCT_REPLY, ct); |
844 | out: | ||
845 | if (tmpl) | ||
846 | nf_ct_put(tmpl); | ||
829 | 847 | ||
830 | return ret; | 848 | return ret; |
831 | } | 849 | } |
@@ -864,7 +882,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, | |||
864 | return; | 882 | return; |
865 | 883 | ||
866 | rcu_read_lock(); | 884 | rcu_read_lock(); |
867 | __nf_ct_try_assign_helper(ct, GFP_ATOMIC); | 885 | __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC); |
868 | rcu_read_unlock(); | 886 | rcu_read_unlock(); |
869 | } | 887 | } |
870 | EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); | 888 | EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); |
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index c0e461f466ae..8144b0da5515 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c | |||
@@ -96,13 +96,22 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) | |||
96 | } | 96 | } |
97 | EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); | 97 | EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); |
98 | 98 | ||
99 | int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags) | 99 | int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, |
100 | gfp_t flags) | ||
100 | { | 101 | { |
102 | struct nf_conntrack_helper *helper = NULL; | ||
103 | struct nf_conn_help *help; | ||
101 | int ret = 0; | 104 | int ret = 0; |
102 | struct nf_conntrack_helper *helper; | ||
103 | struct nf_conn_help *help = nfct_help(ct); | ||
104 | 105 | ||
105 | helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); | 106 | if (tmpl != NULL) { |
107 | help = nfct_help(tmpl); | ||
108 | if (help != NULL) | ||
109 | helper = help->helper; | ||
110 | } | ||
111 | |||
112 | help = nfct_help(ct); | ||
113 | if (helper == NULL) | ||
114 | helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
106 | if (helper == NULL) { | 115 | if (helper == NULL) { |
107 | if (help) | 116 | if (help) |
108 | rcu_assign_pointer(help->helper, NULL); | 117 | rcu_assign_pointer(help->helper, NULL); |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index f5c0b09e12f1..09044f9f4b2e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -1249,7 +1249,7 @@ ctnetlink_create_conntrack(struct net *net, | |||
1249 | } | 1249 | } |
1250 | } else { | 1250 | } else { |
1251 | /* try an implicit helper assignation */ | 1251 | /* try an implicit helper assignation */ |
1252 | err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC); | 1252 | err = __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC); |
1253 | if (err < 0) | 1253 | if (err < 0) |
1254 | goto err2; | 1254 | goto err2; |
1255 | } | 1255 | } |