diff options
| -rw-r--r-- | net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 13 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 9 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_core.c | 26 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_expect.c | 4 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_helper.c | 2 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 34 | ||||
| -rw-r--r-- | net/netfilter/nf_conntrack_proto_gre.c | 2 |
7 files changed, 61 insertions, 29 deletions
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index fd62a41d69cc..6dc72a815f77 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | |||
| @@ -133,6 +133,7 @@ static unsigned int ipv4_conntrack_help(unsigned int hooknum, | |||
| 133 | struct nf_conn *ct; | 133 | struct nf_conn *ct; |
| 134 | enum ip_conntrack_info ctinfo; | 134 | enum ip_conntrack_info ctinfo; |
| 135 | struct nf_conn_help *help; | 135 | struct nf_conn_help *help; |
| 136 | struct nf_conntrack_helper *helper; | ||
| 136 | 137 | ||
| 137 | /* This is where we call the helper: as the packet goes out. */ | 138 | /* This is where we call the helper: as the packet goes out. */ |
| 138 | ct = nf_ct_get(*pskb, &ctinfo); | 139 | ct = nf_ct_get(*pskb, &ctinfo); |
| @@ -140,12 +141,14 @@ static unsigned int ipv4_conntrack_help(unsigned int hooknum, | |||
| 140 | return NF_ACCEPT; | 141 | return NF_ACCEPT; |
| 141 | 142 | ||
| 142 | help = nfct_help(ct); | 143 | help = nfct_help(ct); |
| 143 | if (!help || !help->helper) | 144 | if (!help) |
| 144 | return NF_ACCEPT; | 145 | return NF_ACCEPT; |
| 145 | 146 | /* rcu_read_lock()ed by nf_hook_slow */ | |
| 146 | return help->helper->help(pskb, | 147 | helper = rcu_dereference(help->helper); |
| 147 | skb_network_offset(*pskb) + ip_hdrlen(*pskb), | 148 | if (!helper) |
| 148 | ct, ctinfo); | 149 | return NF_ACCEPT; |
| 150 | return helper->help(pskb, skb_network_offset(*pskb) + ip_hdrlen(*pskb), | ||
| 151 | ct, ctinfo); | ||
| 149 | } | 152 | } |
| 150 | 153 | ||
| 151 | static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, | 154 | static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index dc442fb791b0..1b1797f1f33d 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
| @@ -160,6 +160,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, | |||
| 160 | { | 160 | { |
| 161 | struct nf_conn *ct; | 161 | struct nf_conn *ct; |
| 162 | struct nf_conn_help *help; | 162 | struct nf_conn_help *help; |
| 163 | struct nf_conntrack_helper *helper; | ||
| 163 | enum ip_conntrack_info ctinfo; | 164 | enum ip_conntrack_info ctinfo; |
| 164 | unsigned int ret, protoff; | 165 | unsigned int ret, protoff; |
| 165 | unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data; | 166 | unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data; |
| @@ -172,7 +173,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum, | |||
| 172 | goto out; | 173 | goto out; |
| 173 | 174 | ||
| 174 | help = nfct_help(ct); | 175 | help = nfct_help(ct); |
| 175 | if (!help || !help->helper) | 176 | if (!help) |
| 177 | goto out; | ||
| 178 | /* rcu_read_lock()ed by nf_hook_slow */ | ||
| 179 | helper = rcu_dereference(help->helper); | ||
| 180 | if (!helper) | ||
| 176 | goto out; | 181 | goto out; |
| 177 | 182 | ||
| 178 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, | 183 | protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum, |
| @@ -182,7 +187,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, | |||
| 182 | return NF_ACCEPT; | 187 | return NF_ACCEPT; |
| 183 | } | 188 | } |
| 184 | 189 | ||
| 185 | ret = help->helper->help(pskb, protoff, ct, ctinfo); | 190 | ret = helper->help(pskb, protoff, ct, ctinfo); |
| 186 | if (ret != NF_ACCEPT) | 191 | if (ret != NF_ACCEPT) |
| 187 | return ret; | 192 | return ret; |
| 188 | out: | 193 | out: |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 483e927a9ca4..7a15e30356f2 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
| @@ -350,9 +350,15 @@ static void death_by_timeout(unsigned long ul_conntrack) | |||
| 350 | { | 350 | { |
| 351 | struct nf_conn *ct = (void *)ul_conntrack; | 351 | struct nf_conn *ct = (void *)ul_conntrack; |
| 352 | struct nf_conn_help *help = nfct_help(ct); | 352 | struct nf_conn_help *help = nfct_help(ct); |
| 353 | struct nf_conntrack_helper *helper; | ||
| 353 | 354 | ||
| 354 | if (help && help->helper && help->helper->destroy) | 355 | if (help) { |
| 355 | help->helper->destroy(ct); | 356 | rcu_read_lock(); |
| 357 | helper = rcu_dereference(help->helper); | ||
| 358 | if (helper && helper->destroy) | ||
| 359 | helper->destroy(ct); | ||
| 360 | rcu_read_unlock(); | ||
| 361 | } | ||
| 356 | 362 | ||
| 357 | write_lock_bh(&nf_conntrack_lock); | 363 | write_lock_bh(&nf_conntrack_lock); |
| 358 | /* Inside lock so preempt is disabled on module removal path. | 364 | /* Inside lock so preempt is disabled on module removal path. |
| @@ -661,6 +667,7 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, | |||
| 661 | unsigned int dataoff) | 667 | unsigned int dataoff) |
| 662 | { | 668 | { |
| 663 | struct nf_conn *conntrack; | 669 | struct nf_conn *conntrack; |
| 670 | struct nf_conn_help *help; | ||
| 664 | struct nf_conntrack_tuple repl_tuple; | 671 | struct nf_conntrack_tuple repl_tuple; |
| 665 | struct nf_conntrack_expect *exp; | 672 | struct nf_conntrack_expect *exp; |
| 666 | u_int32_t features = 0; | 673 | u_int32_t features = 0; |
| @@ -691,6 +698,7 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, | |||
| 691 | write_lock_bh(&nf_conntrack_lock); | 698 | write_lock_bh(&nf_conntrack_lock); |
| 692 | exp = find_expectation(tuple); | 699 | exp = find_expectation(tuple); |
| 693 | 700 | ||
| 701 | help = nfct_help(conntrack); | ||
| 694 | if (exp) { | 702 | if (exp) { |
| 695 | DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", | 703 | DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", |
| 696 | conntrack, exp); | 704 | conntrack, exp); |
| @@ -698,7 +706,7 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, | |||
| 698 | __set_bit(IPS_EXPECTED_BIT, &conntrack->status); | 706 | __set_bit(IPS_EXPECTED_BIT, &conntrack->status); |
| 699 | conntrack->master = exp->master; | 707 | conntrack->master = exp->master; |
| 700 | if (exp->helper) | 708 | if (exp->helper) |
| 701 | nfct_help(conntrack)->helper = exp->helper; | 709 | rcu_assign_pointer(help->helper, exp->helper); |
| 702 | #ifdef CONFIG_NF_CONNTRACK_MARK | 710 | #ifdef CONFIG_NF_CONNTRACK_MARK |
| 703 | conntrack->mark = exp->master->mark; | 711 | conntrack->mark = exp->master->mark; |
| 704 | #endif | 712 | #endif |
| @@ -708,10 +716,11 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, | |||
| 708 | nf_conntrack_get(&conntrack->master->ct_general); | 716 | nf_conntrack_get(&conntrack->master->ct_general); |
| 709 | NF_CT_STAT_INC(expect_new); | 717 | NF_CT_STAT_INC(expect_new); |
| 710 | } else { | 718 | } else { |
| 711 | struct nf_conn_help *help = nfct_help(conntrack); | 719 | if (help) { |
| 712 | 720 | /* not in hash table yet, so not strictly necessary */ | |
| 713 | if (help) | 721 | rcu_assign_pointer(help->helper, |
| 714 | help->helper = __nf_ct_helper_find(&repl_tuple); | 722 | __nf_ct_helper_find(&repl_tuple)); |
| 723 | } | ||
| 715 | NF_CT_STAT_INC(new); | 724 | NF_CT_STAT_INC(new); |
| 716 | } | 725 | } |
| 717 | 726 | ||
| @@ -893,7 +902,8 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, | |||
| 893 | helper = __nf_ct_helper_find(newreply); | 902 | helper = __nf_ct_helper_find(newreply); |
| 894 | if (helper) | 903 | if (helper) |
| 895 | memset(&help->help, 0, sizeof(help->help)); | 904 | memset(&help->help, 0, sizeof(help->help)); |
| 896 | help->helper = helper; | 905 | /* not in hash table yet, so not strictly necessary */ |
| 906 | rcu_assign_pointer(help->helper, helper); | ||
| 897 | } | 907 | } |
| 898 | write_unlock_bh(&nf_conntrack_lock); | 908 | write_unlock_bh(&nf_conntrack_lock); |
| 899 | } | 909 | } |
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 117cbfdb910c..504fb6c083f9 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c | |||
| @@ -337,6 +337,10 @@ int nf_conntrack_expect_related(struct nf_conntrack_expect *expect) | |||
| 337 | NF_CT_ASSERT(master_help); | 337 | NF_CT_ASSERT(master_help); |
| 338 | 338 | ||
| 339 | write_lock_bh(&nf_conntrack_lock); | 339 | write_lock_bh(&nf_conntrack_lock); |
| 340 | if (!master_help->helper) { | ||
| 341 | ret = -ESHUTDOWN; | ||
| 342 | goto out; | ||
| 343 | } | ||
| 340 | list_for_each_entry(i, &nf_conntrack_expect_list, list) { | 344 | list_for_each_entry(i, &nf_conntrack_expect_list, list) { |
| 341 | if (expect_matches(i, expect)) { | 345 | if (expect_matches(i, expect)) { |
| 342 | /* Refresh timer: if it's dying, ignore.. */ | 346 | /* Refresh timer: if it's dying, ignore.. */ |
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 0743be4434b0..f868b7fbd9b4 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c | |||
| @@ -93,7 +93,7 @@ static inline int unhelp(struct nf_conntrack_tuple_hash *i, | |||
| 93 | 93 | ||
| 94 | if (help && help->helper == me) { | 94 | if (help && help->helper == me) { |
| 95 | nf_conntrack_event(IPCT_HELPER, ct); | 95 | nf_conntrack_event(IPCT_HELPER, ct); |
| 96 | help->helper = NULL; | 96 | rcu_assign_pointer(help->helper, NULL); |
| 97 | } | 97 | } |
| 98 | return 0; | 98 | return 0; |
| 99 | } | 99 | } |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d6d39e241327..3f73327794ab 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
| @@ -171,21 +171,29 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct) | |||
| 171 | { | 171 | { |
| 172 | struct nfattr *nest_helper; | 172 | struct nfattr *nest_helper; |
| 173 | const struct nf_conn_help *help = nfct_help(ct); | 173 | const struct nf_conn_help *help = nfct_help(ct); |
| 174 | struct nf_conntrack_helper *helper; | ||
| 174 | 175 | ||
| 175 | if (!help || !help->helper) | 176 | if (!help) |
| 176 | return 0; | 177 | return 0; |
| 177 | 178 | ||
| 179 | rcu_read_lock(); | ||
| 180 | helper = rcu_dereference(help->helper); | ||
| 181 | if (!helper) | ||
| 182 | goto out; | ||
| 183 | |||
| 178 | nest_helper = NFA_NEST(skb, CTA_HELP); | 184 | nest_helper = NFA_NEST(skb, CTA_HELP); |
| 179 | NFA_PUT(skb, CTA_HELP_NAME, strlen(help->helper->name), help->helper->name); | 185 | NFA_PUT(skb, CTA_HELP_NAME, strlen(helper->name), helper->name); |
| 180 | 186 | ||
| 181 | if (help->helper->to_nfattr) | 187 | if (helper->to_nfattr) |
| 182 | help->helper->to_nfattr(skb, ct); | 188 | helper->to_nfattr(skb, ct); |
| 183 | 189 | ||
| 184 | NFA_NEST_END(skb, nest_helper); | 190 | NFA_NEST_END(skb, nest_helper); |
| 185 | 191 | out: | |
| 192 | rcu_read_unlock(); | ||
| 186 | return 0; | 193 | return 0; |
| 187 | 194 | ||
| 188 | nfattr_failure: | 195 | nfattr_failure: |
| 196 | rcu_read_unlock(); | ||
| 189 | return -1; | 197 | return -1; |
| 190 | } | 198 | } |
| 191 | 199 | ||
| @@ -842,7 +850,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[]) | |||
| 842 | if (help && help->helper) { | 850 | if (help && help->helper) { |
| 843 | /* we had a helper before ... */ | 851 | /* we had a helper before ... */ |
| 844 | nf_ct_remove_expectations(ct); | 852 | nf_ct_remove_expectations(ct); |
| 845 | help->helper = NULL; | 853 | rcu_assign_pointer(help->helper, NULL); |
| 846 | } | 854 | } |
| 847 | 855 | ||
| 848 | return 0; | 856 | return 0; |
| @@ -866,7 +874,7 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[]) | |||
| 866 | 874 | ||
| 867 | /* need to zero data of old helper */ | 875 | /* need to zero data of old helper */ |
| 868 | memset(&help->help, 0, sizeof(help->help)); | 876 | memset(&help->help, 0, sizeof(help->help)); |
| 869 | help->helper = helper; | 877 | rcu_assign_pointer(help->helper, helper); |
| 870 | 878 | ||
| 871 | return 0; | 879 | return 0; |
| 872 | } | 880 | } |
| @@ -950,6 +958,7 @@ ctnetlink_create_conntrack(struct nfattr *cda[], | |||
| 950 | struct nf_conn *ct; | 958 | struct nf_conn *ct; |
| 951 | int err = -EINVAL; | 959 | int err = -EINVAL; |
| 952 | struct nf_conn_help *help; | 960 | struct nf_conn_help *help; |
| 961 | struct nf_conntrack_helper *helper = NULL; | ||
| 953 | 962 | ||
| 954 | ct = nf_conntrack_alloc(otuple, rtuple); | 963 | ct = nf_conntrack_alloc(otuple, rtuple); |
| 955 | if (ct == NULL || IS_ERR(ct)) | 964 | if (ct == NULL || IS_ERR(ct)) |
| @@ -980,14 +989,17 @@ ctnetlink_create_conntrack(struct nfattr *cda[], | |||
| 980 | #endif | 989 | #endif |
| 981 | 990 | ||
| 982 | help = nfct_help(ct); | 991 | help = nfct_help(ct); |
| 983 | if (help) | 992 | if (help) { |
| 984 | help->helper = nf_ct_helper_find_get(rtuple); | 993 | helper = nf_ct_helper_find_get(rtuple); |
| 994 | /* not in hash table yet so not strictly necessary */ | ||
| 995 | rcu_assign_pointer(help->helper, helper); | ||
| 996 | } | ||
| 985 | 997 | ||
| 986 | add_timer(&ct->timeout); | 998 | add_timer(&ct->timeout); |
| 987 | nf_conntrack_hash_insert(ct); | 999 | nf_conntrack_hash_insert(ct); |
| 988 | 1000 | ||
| 989 | if (help && help->helper) | 1001 | if (helper) |
| 990 | nf_ct_helper_put(help->helper); | 1002 | nf_ct_helper_put(helper); |
| 991 | 1003 | ||
| 992 | return 0; | 1004 | return 0; |
| 993 | 1005 | ||
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 5434472420fe..339c397d1b5f 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c | |||
| @@ -100,7 +100,6 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, | |||
| 100 | struct nf_conn_help *help = nfct_help(ct); | 100 | struct nf_conn_help *help = nfct_help(ct); |
| 101 | struct nf_ct_gre_keymap **kmp, *km; | 101 | struct nf_ct_gre_keymap **kmp, *km; |
| 102 | 102 | ||
| 103 | BUG_ON(strcmp(help->helper->name, "pptp")); | ||
| 104 | kmp = &help->help.ct_pptp_info.keymap[dir]; | 103 | kmp = &help->help.ct_pptp_info.keymap[dir]; |
| 105 | if (*kmp) { | 104 | if (*kmp) { |
| 106 | /* check whether it's a retransmission */ | 105 | /* check whether it's a retransmission */ |
| @@ -137,7 +136,6 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) | |||
| 137 | enum ip_conntrack_dir dir; | 136 | enum ip_conntrack_dir dir; |
| 138 | 137 | ||
| 139 | DEBUGP("entering for ct %p\n", ct); | 138 | DEBUGP("entering for ct %p\n", ct); |
| 140 | BUG_ON(strcmp(help->helper->name, "pptp")); | ||
| 141 | 139 | ||
| 142 | write_lock_bh(&nf_ct_gre_lock); | 140 | write_lock_bh(&nf_ct_gre_lock); |
| 143 | for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { | 141 | for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { |
