Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: ctnetlink: helper modules load-on-demand support

This patch adds module loading for helpers via ctnetlink.

* Creation path: We support explicit and implicit helper assignation. For
the explicit case, we try to load the module. If the module is correctly
loaded and the helper is present, we return EAGAIN to re-start the
creation. Otherwise, we return EOPNOTSUPP.
* Update path: release the spin lock, load the module and check. If it is
present, then return EAGAIN to re-start the update.

This patch provides a refactorized function to lookup-and-set the
connection tracking helper. The function removes the exported symbol
__nf_ct_helper_find as it has not clients anymore.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>

authored by

Pablo Neira Ayuso and committed by
Patrick McHardy
226c0c0e 4dc06f96

+95 -40
+2 -3
include/net/netfilter/nf_conntrack_helper.h
··· 39 39 }; 40 40 41 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 42 __nf_conntrack_helper_find_byname(const char *name); 46 43 47 44 extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); 48 45 extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); 49 46 50 47 extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); 48 + 49 + extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags); 51 50 52 51 static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) 53 52 {
+2 -26
net/netfilter/nf_conntrack_core.c
··· 588 588 nf_conntrack_get(&ct->master->ct_general); 589 589 NF_CT_STAT_INC(net, expect_new); 590 590 } else { 591 - struct nf_conntrack_helper *helper; 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 - } 591 + __nf_ct_try_assign_helper(ct, GFP_ATOMIC); 599 592 NF_CT_STAT_INC(net, new); 600 593 } 601 594 ··· 765 772 const struct nf_conntrack_tuple *newreply) 766 773 { 767 774 struct nf_conn_help *help = nfct_help(ct); 768 - struct nf_conntrack_helper *helper; 769 775 770 776 /* Should be unconfirmed, so not in hash table yet */ 771 777 NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); ··· 777 785 return; 778 786 779 787 rcu_read_lock(); 780 - helper = __nf_ct_helper_find(newreply); 781 - if (helper == NULL) { 782 - if (help) 783 - rcu_assign_pointer(help->helper, NULL); 784 - goto out; 785 - } 786 - 787 - if (help == NULL) { 788 - help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); 789 - if (help == NULL) 790 - goto out; 791 - } else { 792 - memset(&help->help, 0, sizeof(help->help)); 793 - } 794 - 795 - rcu_assign_pointer(help->helper, helper); 796 - out: 788 + __nf_ct_try_assign_helper(ct, GFP_ATOMIC); 797 789 rcu_read_unlock(); 798 790 } 799 791 EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
+30 -2
net/netfilter/nf_conntrack_helper.c
··· 44 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 48 __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) 49 49 { 50 50 struct nf_conntrack_helper *helper; ··· 62 62 } 63 63 return NULL; 64 64 } 65 - EXPORT_SYMBOL_GPL(__nf_ct_helper_find); 66 65 67 66 struct nf_conntrack_helper * 68 67 __nf_conntrack_helper_find_byname(const char *name) ··· 92 93 return help; 93 94 } 94 95 EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); 96 + 97 + int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags) 98 + { 99 + int ret = 0; 100 + struct nf_conntrack_helper *helper; 101 + struct nf_conn_help *help = nfct_help(ct); 102 + 103 + helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); 104 + if (helper == NULL) { 105 + if (help) 106 + rcu_assign_pointer(help->helper, NULL); 107 + goto out; 108 + } 109 + 110 + if (help == NULL) { 111 + help = nf_ct_helper_ext_add(ct, flags); 112 + if (help == NULL) { 113 + ret = -ENOMEM; 114 + goto out; 115 + } 116 + } else { 117 + memset(&help->help, 0, sizeof(help->help)); 118 + } 119 + 120 + rcu_assign_pointer(help->helper, helper); 121 + out: 122 + return ret; 123 + } 124 + EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); 95 125 96 126 static inline int unhelp(struct nf_conntrack_tuple_hash *i, 97 127 const struct nf_conntrack_helper *me)
+61 -9
net/netfilter/nf_conntrack_netlink.c
··· 917 917 } 918 918 919 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 934 return -EOPNOTSUPP; 935 + } 922 936 923 937 if (help) { 924 938 if (help->helper == helper) ··· 1096 1082 { 1097 1083 struct nf_conn *ct; 1098 1084 int err = -EINVAL; 1099 - struct nf_conn_help *help; 1100 1085 struct nf_conntrack_helper *helper; 1101 1086 1102 1087 ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_KERNEL); ··· 1110 1097 ct->status |= IPS_CONFIRMED; 1111 1098 1112 1099 rcu_read_lock(); 1113 - helper = __nf_ct_helper_find(rtuple); 1114 - if (helper) { 1115 - help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); 1116 - if (help == NULL) { 1100 + if (cda[CTA_HELP]) { 1101 + char *helpname; 1102 + 1103 + err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); 1104 + if (err < 0) { 1117 1105 rcu_read_unlock(); 1118 - err = -ENOMEM; 1119 1106 goto err; 1120 1107 } 1121 - /* not in hash table yet so not strictly necessary */ 1122 - rcu_assign_pointer(help->helper, helper); 1108 + 1109 + helper = __nf_conntrack_helper_find_byname(helpname); 1110 + if (helper == NULL) { 1111 + rcu_read_unlock(); 1112 + #ifdef CONFIG_MODULES 1113 + if (request_module("nfct-helper-%s", helpname) < 0) { 1114 + err = -EOPNOTSUPP; 1115 + goto err; 1116 + } 1117 + 1118 + rcu_read_lock(); 1119 + helper = __nf_conntrack_helper_find_byname(helpname); 1120 + if (helper) { 1121 + rcu_read_unlock(); 1122 + err = -EAGAIN; 1123 + goto err; 1124 + } 1125 + rcu_read_unlock(); 1126 + #endif 1127 + err = -EOPNOTSUPP; 1128 + goto err; 1129 + } else { 1130 + struct nf_conn_help *help; 1131 + 1132 + help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); 1133 + if (help == NULL) { 1134 + rcu_read_unlock(); 1135 + err = -ENOMEM; 1136 + goto err; 1137 + } 1138 + 1139 + /* not in hash table yet so not strictly necessary */ 1140 + rcu_assign_pointer(help->helper, helper); 1141 + } 1142 + } else { 1143 + /* try an implicit helper assignation */ 1144 + err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC); 1145 + if (err < 0) { 1146 + rcu_read_unlock(); 1147 + goto err; 1148 + } 1123 1149 } 1124 1150 1125 1151 if (cda[CTA_STATUS]) {