diff options
-rw-r--r-- | include/net/netfilter/nf_conntrack_helper.h | 5 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 28 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_helper.c | 32 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 70 |
4 files changed, 95 insertions, 40 deletions
diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index f8060ab5a083..66d65a7caa39 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h | |||
@@ -39,9 +39,6 @@ struct nf_conntrack_helper | |||
39 | }; | 39 | }; |
40 | 40 | ||
41 | extern struct nf_conntrack_helper * | 41 | extern struct nf_conntrack_helper * |
42 | __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple); | ||
43 | |||
44 | extern struct nf_conntrack_helper * | ||
45 | __nf_conntrack_helper_find_byname(const char *name); | 42 | __nf_conntrack_helper_find_byname(const char *name); |
46 | 43 | ||
47 | extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); | 44 | extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); |
@@ -49,6 +46,8 @@ extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); | |||
49 | 46 | ||
50 | extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); | 47 | extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); |
51 | 48 | ||
49 | extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags); | ||
50 | |||
52 | static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) | 51 | static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) |
53 | { | 52 | { |
54 | return nf_ct_ext_find(ct, NF_CT_EXT_HELPER); | 53 | return nf_ct_ext_find(ct, NF_CT_EXT_HELPER); |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 622d7c671cb7..1e649fb9e0df 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -588,14 +588,7 @@ init_conntrack(struct net *net, | |||
588 | nf_conntrack_get(&ct->master->ct_general); | 588 | nf_conntrack_get(&ct->master->ct_general); |
589 | NF_CT_STAT_INC(net, expect_new); | 589 | NF_CT_STAT_INC(net, expect_new); |
590 | } else { | 590 | } else { |
591 | struct nf_conntrack_helper *helper; | 591 | __nf_ct_try_assign_helper(ct, GFP_ATOMIC); |
592 | |||
593 | helper = __nf_ct_helper_find(&repl_tuple); | ||
594 | if (helper) { | ||
595 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | ||
596 | if (help) | ||
597 | rcu_assign_pointer(help->helper, helper); | ||
598 | } | ||
599 | NF_CT_STAT_INC(net, new); | 592 | NF_CT_STAT_INC(net, new); |
600 | } | 593 | } |
601 | 594 | ||
@@ -772,7 +765,6 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, | |||
772 | const struct nf_conntrack_tuple *newreply) | 765 | const struct nf_conntrack_tuple *newreply) |
773 | { | 766 | { |
774 | struct nf_conn_help *help = nfct_help(ct); | 767 | struct nf_conn_help *help = nfct_help(ct); |
775 | struct nf_conntrack_helper *helper; | ||
776 | 768 | ||
777 | /* Should be unconfirmed, so not in hash table yet */ | 769 | /* Should be unconfirmed, so not in hash table yet */ |
778 | NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); | 770 | NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); |
@@ -785,23 +777,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, | |||
785 | return; | 777 | return; |
786 | 778 | ||
787 | rcu_read_lock(); | 779 | rcu_read_lock(); |
788 | helper = __nf_ct_helper_find(newreply); | 780 | __nf_ct_try_assign_helper(ct, GFP_ATOMIC); |
789 | if (helper == NULL) { | ||
790 | if (help) | ||
791 | rcu_assign_pointer(help->helper, NULL); | ||
792 | goto out; | ||
793 | } | ||
794 | |||
795 | if (help == NULL) { | ||
796 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | ||
797 | if (help == NULL) | ||
798 | goto out; | ||
799 | } else { | ||
800 | memset(&help->help, 0, sizeof(help->help)); | ||
801 | } | ||
802 | |||
803 | rcu_assign_pointer(help->helper, helper); | ||
804 | out: | ||
805 | rcu_read_unlock(); | 781 | rcu_read_unlock(); |
806 | } | 782 | } |
807 | EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); | 783 | EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); |
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 9c06b9f86ad4..9e4b74b95ce8 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c | |||
@@ -44,7 +44,7 @@ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) | |||
44 | (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize; | 44 | (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize; |
45 | } | 45 | } |
46 | 46 | ||
47 | struct nf_conntrack_helper * | 47 | static struct nf_conntrack_helper * |
48 | __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) | 48 | __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) |
49 | { | 49 | { |
50 | struct nf_conntrack_helper *helper; | 50 | struct nf_conntrack_helper *helper; |
@@ -62,7 +62,6 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) | |||
62 | } | 62 | } |
63 | return NULL; | 63 | return NULL; |
64 | } | 64 | } |
65 | EXPORT_SYMBOL_GPL(__nf_ct_helper_find); | ||
66 | 65 | ||
67 | struct nf_conntrack_helper * | 66 | struct nf_conntrack_helper * |
68 | __nf_conntrack_helper_find_byname(const char *name) | 67 | __nf_conntrack_helper_find_byname(const char *name) |
@@ -94,6 +93,35 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) | |||
94 | } | 93 | } |
95 | EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); | 94 | EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); |
96 | 95 | ||
96 | int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags) | ||
97 | { | ||
98 | int ret = 0; | ||
99 | struct nf_conntrack_helper *helper; | ||
100 | struct nf_conn_help *help = nfct_help(ct); | ||
101 | |||
102 | helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); | ||
103 | if (helper == NULL) { | ||
104 | if (help) | ||
105 | rcu_assign_pointer(help->helper, NULL); | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | if (help == NULL) { | ||
110 | help = nf_ct_helper_ext_add(ct, flags); | ||
111 | if (help == NULL) { | ||
112 | ret = -ENOMEM; | ||
113 | goto out; | ||
114 | } | ||
115 | } else { | ||
116 | memset(&help->help, 0, sizeof(help->help)); | ||
117 | } | ||
118 | |||
119 | rcu_assign_pointer(help->helper, helper); | ||
120 | out: | ||
121 | return ret; | ||
122 | } | ||
123 | EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); | ||
124 | |||
97 | static inline int unhelp(struct nf_conntrack_tuple_hash *i, | 125 | static inline int unhelp(struct nf_conntrack_tuple_hash *i, |
98 | const struct nf_conntrack_helper *me) | 126 | const struct nf_conntrack_helper *me) |
99 | { | 127 | { |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 49a04fa0becc..4f6486cfd337 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -917,8 +917,22 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) | |||
917 | } | 917 | } |
918 | 918 | ||
919 | helper = __nf_conntrack_helper_find_byname(helpname); | 919 | helper = __nf_conntrack_helper_find_byname(helpname); |
920 | if (helper == NULL) | 920 | if (helper == NULL) { |
921 | #ifdef CONFIG_MODULES | ||
922 | spin_unlock_bh(&nf_conntrack_lock); | ||
923 | |||
924 | if (request_module("nfct-helper-%s", helpname) < 0) { | ||
925 | spin_lock_bh(&nf_conntrack_lock); | ||
926 | return -EOPNOTSUPP; | ||
927 | } | ||
928 | |||
929 | spin_lock_bh(&nf_conntrack_lock); | ||
930 | helper = __nf_conntrack_helper_find_byname(helpname); | ||
931 | if (helper) | ||
932 | return -EAGAIN; | ||
933 | #endif | ||
921 | return -EOPNOTSUPP; | 934 | return -EOPNOTSUPP; |
935 | } | ||
922 | 936 | ||
923 | if (help) { | 937 | if (help) { |
924 | if (help->helper == helper) | 938 | if (help->helper == helper) |
@@ -1082,7 +1096,6 @@ ctnetlink_create_conntrack(struct nlattr *cda[], | |||
1082 | { | 1096 | { |
1083 | struct nf_conn *ct; | 1097 | struct nf_conn *ct; |
1084 | int err = -EINVAL; | 1098 | int err = -EINVAL; |
1085 | struct nf_conn_help *help; | ||
1086 | struct nf_conntrack_helper *helper; | 1099 | struct nf_conntrack_helper *helper; |
1087 | 1100 | ||
1088 | ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_KERNEL); | 1101 | ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_KERNEL); |
@@ -1097,16 +1110,55 @@ ctnetlink_create_conntrack(struct nlattr *cda[], | |||
1097 | ct->status |= IPS_CONFIRMED; | 1110 | ct->status |= IPS_CONFIRMED; |
1098 | 1111 | ||
1099 | rcu_read_lock(); | 1112 | rcu_read_lock(); |
1100 | helper = __nf_ct_helper_find(rtuple); | 1113 | if (cda[CTA_HELP]) { |
1101 | if (helper) { | 1114 | char *helpname; |
1102 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | 1115 | |
1103 | if (help == NULL) { | 1116 | err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); |
1117 | if (err < 0) { | ||
1118 | rcu_read_unlock(); | ||
1119 | goto err; | ||
1120 | } | ||
1121 | |||
1122 | helper = __nf_conntrack_helper_find_byname(helpname); | ||
1123 | if (helper == NULL) { | ||
1124 | rcu_read_unlock(); | ||
1125 | #ifdef CONFIG_MODULES | ||
1126 | if (request_module("nfct-helper-%s", helpname) < 0) { | ||
1127 | err = -EOPNOTSUPP; | ||
1128 | goto err; | ||
1129 | } | ||
1130 | |||
1131 | rcu_read_lock(); | ||
1132 | helper = __nf_conntrack_helper_find_byname(helpname); | ||
1133 | if (helper) { | ||
1134 | rcu_read_unlock(); | ||
1135 | err = -EAGAIN; | ||
1136 | goto err; | ||
1137 | } | ||
1138 | rcu_read_unlock(); | ||
1139 | #endif | ||
1140 | err = -EOPNOTSUPP; | ||
1141 | goto err; | ||
1142 | } else { | ||
1143 | struct nf_conn_help *help; | ||
1144 | |||
1145 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | ||
1146 | if (help == NULL) { | ||
1147 | rcu_read_unlock(); | ||
1148 | err = -ENOMEM; | ||
1149 | goto err; | ||
1150 | } | ||
1151 | |||
1152 | /* not in hash table yet so not strictly necessary */ | ||
1153 | rcu_assign_pointer(help->helper, helper); | ||
1154 | } | ||
1155 | } else { | ||
1156 | /* try an implicit helper assignation */ | ||
1157 | err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC); | ||
1158 | if (err < 0) { | ||
1104 | rcu_read_unlock(); | 1159 | rcu_read_unlock(); |
1105 | err = -ENOMEM; | ||
1106 | goto err; | 1160 | goto err; |
1107 | } | 1161 | } |
1108 | /* not in hash table yet so not strictly necessary */ | ||
1109 | rcu_assign_pointer(help->helper, helper); | ||
1110 | } | 1162 | } |
1111 | 1163 | ||
1112 | if (cda[CTA_STATUS]) { | 1164 | if (cda[CTA_STATUS]) { |