aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/netfilter/nf_conntrack_helper.h5
-rw-r--r--net/netfilter/nf_conntrack_core.c28
-rw-r--r--net/netfilter/nf_conntrack_helper.c32
-rw-r--r--net/netfilter/nf_conntrack_netlink.c70
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
41extern struct nf_conntrack_helper * 41extern struct nf_conntrack_helper *
42__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple);
43
44extern struct nf_conntrack_helper *
45__nf_conntrack_helper_find_byname(const char *name); 42__nf_conntrack_helper_find_byname(const char *name);
46 43
47extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); 44extern 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
50extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); 47extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
51 48
49extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags);
50
52static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) 51static 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);
804out:
805 rcu_read_unlock(); 781 rcu_read_unlock();
806} 782}
807EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); 783EXPORT_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
47struct nf_conntrack_helper * 47static 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}
65EXPORT_SYMBOL_GPL(__nf_ct_helper_find);
66 65
67struct nf_conntrack_helper * 66struct 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}
95EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 94EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
96 95
96int __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);
120out:
121 return ret;
122}
123EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
124
97static inline int unhelp(struct nf_conntrack_tuple_hash *i, 125static 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]) {