aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter/nf_conntrack_common.h4
-rw-r--r--include/net/netfilter/nf_conntrack.h5
-rw-r--r--include/net/netfilter/nf_conntrack_helper.h3
-rw-r--r--net/ipv4/netfilter/nf_defrag_ipv4.c2
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c2
-rw-r--r--net/netfilter/nf_conntrack_core.c50
-rw-r--r--net/netfilter/nf_conntrack_helper.c17
-rw-r--r--net/netfilter/nf_conntrack_netlink.c2
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 ebfed90733f..c608677dda6 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 a0904adfb8f..5043d61c99a 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
275static 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. */
276static inline int nf_ct_is_confirmed(struct nf_conn *ct) 281static 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 86be7c4816d..e17aaa3e19f 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
48extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); 48extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
49 49
50extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags); 50extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
51 gfp_t flags);
51 52
52extern void nf_ct_helper_destroy(struct nf_conn *ct); 53extern 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 331ead3ebd1..77627fa8056 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 0956ebabbff..55ce22e5de4 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 53b8da6ad6b..471e2a79d26 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. */
620static struct nf_conntrack_tuple_hash * 620static struct nf_conntrack_tuple_hash *
621init_conntrack(struct net *net, 621init_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 */
696static inline struct nf_conn * 701static inline struct nf_conn *
697resolve_normal_ct(struct net *net, 702resolve_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
755nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, 761nf_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);
844out:
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}
870EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); 888EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index c0e461f466a..8144b0da551 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}
97EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 97EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
98 98
99int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags) 99int __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 f5c0b09e12f..09044f9f4b2 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 }