The API allows a conntrack helper to indicate its corresponding NAT helper which then can be loaded and reference counted. Signed-off-by: Flavio Leitner <fbl@xxxxxxxxxxxx> --- include/net/netfilter/nf_conntrack_helper.h | 19 +++- net/netfilter/nf_conntrack_amanda.c | 2 + net/netfilter/nf_conntrack_ftp.c | 6 +- net/netfilter/nf_conntrack_helper.c | 108 +++++++++++++++++++- net/netfilter/nf_conntrack_irc.c | 3 +- net/netfilter/nf_conntrack_sane.c | 4 +- net/netfilter/nf_conntrack_sip.c | 12 ++- net/netfilter/nf_conntrack_tftp.c | 6 +- 8 files changed, 147 insertions(+), 13 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index e86fadf7e7c5..0d36d6bfb522 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -58,6 +58,8 @@ struct nf_conntrack_helper { unsigned int queue_num; /* length of userspace private data stored in nf_conn_help->data */ u16 data_len; + /* name of NAT helper module */ + char nat_mod_name[NF_CT_HELPER_NAME_LEN]; }; /* Must be kept in sync with the classes defined by helpers */ @@ -98,7 +100,8 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, enum ip_conntrack_info ctinfo), int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct), - struct module *module); + struct module *module, + const char *nat_mod_name); int nf_conntrack_helper_register(struct nf_conntrack_helper *); void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); @@ -157,4 +160,18 @@ nf_ct_helper_expectfn_find_by_symbol(const void *symbol); extern struct hlist_head *nf_ct_helper_hash; extern unsigned int nf_ct_helper_hsize; +struct nf_conntrack_helper_nat { + struct list_head list; + char name[NF_CT_HELPER_NAME_LEN]; + struct module *module; /* pointer to self */ +}; + +void nf_ct_helper_nat_init(struct nf_conntrack_helper_nat *nat, + const char *name, struct module *module); + +void nf_conntrack_helper_nat_register(struct nf_conntrack_helper_nat *nat); +void nf_conntrack_helper_nat_unregister(struct nf_conntrack_helper_nat *nat); +int nf_conntrack_helper_nat_try_module_get(const char *name, u16 l3num, + u8 protonum); +void nf_conntrack_helper_nat_put(struct nf_conntrack_helper *helper); #endif /*_NF_CONNTRACK_HELPER_H*/ diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index f2681ec5b5f6..b5d255897d9e 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -186,6 +186,7 @@ static struct nf_conntrack_helper amanda_helper[2] __read_mostly = { .tuple.src.u.udp.port = cpu_to_be16(10080), .tuple.dst.protonum = IPPROTO_UDP, .expect_policy = &amanda_exp_policy, + .nat_mod_name = NF_CT_NAT_HELPER_MOD_NAME("amanda"), }, { .name = "amanda", @@ -195,6 +196,7 @@ static struct nf_conntrack_helper amanda_helper[2] __read_mostly = { .tuple.src.u.udp.port = cpu_to_be16(10080), .tuple.dst.protonum = IPPROTO_UDP, .expect_policy = &amanda_exp_policy, + .nat_mod_name = NF_CT_NAT_HELPER_MOD_NAME("amanda"), }, }; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index a11c304fb771..fec9bb462071 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -590,10 +590,12 @@ static int __init nf_conntrack_ftp_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP, "ftp", FTP_PORT, ports[i], ports[i], &ftp_exp_policy, - 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE); + 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("ftp")); nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP, "ftp", FTP_PORT, ports[i], ports[i], &ftp_exp_policy, - 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE); + 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("ftp")); } ret = nf_conntrack_helpers_register(ftp, ports_c * 2); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 274baf1dab87..883a8d438503 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -42,6 +42,9 @@ module_param_named(nf_conntrack_helper, nf_ct_auto_assign_helper, bool, 0644); MODULE_PARM_DESC(nf_conntrack_helper, "Enable automatic conntrack helper assignment (default 0)"); +static struct list_head nf_ct_nat_helpers __read_mostly; +static DEFINE_SPINLOCK(nf_ct_nat_helpers_lock); + /* Stupid hash, but collision free for the default registrations of the * helpers currently in the kernel. */ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) @@ -130,6 +133,75 @@ void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) } EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); +static struct nf_conntrack_helper_nat * +nf_conntrack_helper_nat_find(const char *name) +{ + struct nf_conntrack_helper_nat *cur; + bool found = false; + + list_for_each_entry_rcu(cur, &nf_ct_nat_helpers, list) { + if (!strcmp(cur->name, name)) { + found = true; + break; + } + } + return found ? cur : NULL; +} + +int +nf_conntrack_helper_nat_try_module_get(const char *name, u16 l3num, u8 protonum) +{ + struct nf_conntrack_helper *h; + struct nf_conntrack_helper_nat *nat; + char mod_name[NF_CT_HELPER_NAME_LEN]; + int ret = 0; + + rcu_read_lock(); + h = __nf_conntrack_helper_find(name, l3num, protonum); + if (h == NULL) { + rcu_read_unlock(); + return -EINVAL; + } + + if (!strlen(h->nat_mod_name)) { + rcu_read_unlock(); + return -EOPNOTSUPP; + } + + nat = nf_conntrack_helper_nat_find(h->nat_mod_name); + if (nat == NULL) { + snprintf(mod_name, sizeof(mod_name), "%s", h->nat_mod_name); + rcu_read_unlock(); + ret = request_module(mod_name); + if (ret != 0) + return ret; + + rcu_read_lock(); + nat = nf_conntrack_helper_nat_find(mod_name); + if (nat == NULL) { + rcu_read_unlock(); + return -EINVAL; + } + } + + if (!try_module_get(nat->module)) + ret = -EINVAL; + + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(nf_conntrack_helper_nat_try_module_get); + +void nf_conntrack_helper_nat_put(struct nf_conntrack_helper *helper) +{ + struct nf_conntrack_helper_nat *nat; + + nat = nf_conntrack_helper_nat_find(helper->nat_mod_name); + BUG_ON(nat == NULL); + module_put(nat->module); +} +EXPORT_SYMBOL_GPL(nf_conntrack_helper_nat_put); + struct nf_conn_help * nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) { @@ -420,7 +492,8 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, enum ip_conntrack_info ctinfo), int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct), - struct module *module) + struct module *module, + const char *nat_mod_name) { helper->tuple.src.l3num = l3num; helper->tuple.dst.protonum = protonum; @@ -430,6 +503,10 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, helper->help = help; helper->from_nlattr = from_nlattr; helper->me = module; + helper->nat_mod_name[0] = '\0'; + if (nat_mod_name) + snprintf(helper->nat_mod_name, sizeof(helper->nat_mod_name), + "%s", nat_mod_name); if (spec_port == default_port) snprintf(helper->name, sizeof(helper->name), "%s", name); @@ -466,6 +543,34 @@ void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper, } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister); +void nf_conntrack_helper_nat_register(struct nf_conntrack_helper_nat *nat) +{ + BUG_ON(nat->module == NULL); + + spin_lock(&nf_ct_nat_helpers_lock); + list_add_rcu(&nat->list, &nf_ct_nat_helpers); + spin_unlock(&nf_ct_nat_helpers_lock); +} +EXPORT_SYMBOL_GPL(nf_conntrack_helper_nat_register); + +void nf_conntrack_helper_nat_unregister(struct nf_conntrack_helper_nat *nat) +{ + BUG_ON(nat->module == NULL); + + spin_lock(&nf_ct_nat_helpers_lock); + list_del_rcu(&nat->list); + spin_unlock(&nf_ct_nat_helpers_lock); +} +EXPORT_SYMBOL_GPL(nf_conntrack_helper_nat_unregister); + +void nf_ct_helper_nat_init(struct nf_conntrack_helper_nat *nat, + const char *name, struct module *module) +{ + nat->module = module; + snprintf(nat->name, sizeof(nat->name), "%s", name); +} +EXPORT_SYMBOL_GPL(nf_ct_helper_nat_init); + static const struct nf_ct_ext_type helper_extend = { .len = sizeof(struct nf_conn_help), .align = __alignof__(struct nf_conn_help), @@ -493,6 +598,7 @@ int nf_conntrack_helper_init(void) goto out_extend; } + INIT_LIST_HEAD(&nf_ct_nat_helpers); return 0; out_extend: kvfree(nf_ct_helper_hash); diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 4099f4d79bae..659aa2cb5493 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -261,7 +261,8 @@ static int __init nf_conntrack_irc_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&irc[i], AF_INET, IPPROTO_TCP, "irc", IRC_PORT, ports[i], i, &irc_exp_policy, - 0, help, NULL, THIS_MODULE); + 0, help, NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("irc")); } ret = nf_conntrack_helpers_register(&irc[0], ports_c); diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 5072ff96ab33..b08724b8754c 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -198,11 +198,11 @@ static int __init nf_conntrack_sane_init(void) nf_ct_helper_init(&sane[2 * i], AF_INET, IPPROTO_TCP, "sane", SANE_PORT, ports[i], ports[i], &sane_exp_policy, 0, help, NULL, - THIS_MODULE); + THIS_MODULE, NULL); nf_ct_helper_init(&sane[2 * i + 1], AF_INET6, IPPROTO_TCP, "sane", SANE_PORT, ports[i], ports[i], &sane_exp_policy, 0, help, NULL, - THIS_MODULE); + THIS_MODULE, NULL); } ret = nf_conntrack_helpers_register(sane, ports_c * 2); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index f067c6b50857..0d4fca4a329f 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1677,19 +1677,23 @@ static int __init nf_conntrack_sip_init(void) nf_ct_helper_init(&sip[4 * i], AF_INET, IPPROTO_UDP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp, - NULL, THIS_MODULE); + NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("sip")); nf_ct_helper_init(&sip[4 * i + 1], AF_INET, IPPROTO_TCP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp, - NULL, THIS_MODULE); + NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("sip")); nf_ct_helper_init(&sip[4 * i + 2], AF_INET6, IPPROTO_UDP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, SIP_EXPECT_MAX, sip_help_udp, - NULL, THIS_MODULE); + NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("sip")); nf_ct_helper_init(&sip[4 * i + 3], AF_INET6, IPPROTO_TCP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, SIP_EXPECT_MAX, sip_help_tcp, - NULL, THIS_MODULE); + NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("sip")); } ret = nf_conntrack_helpers_register(sip, ports_c * 4); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 548b673b3625..e1fbf892db70 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -121,10 +121,12 @@ static int __init nf_conntrack_tftp_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP, "tftp", TFTP_PORT, ports[i], i, &tftp_exp_policy, - 0, tftp_help, NULL, THIS_MODULE); + 0, tftp_help, NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("tftp")); nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP, "tftp", TFTP_PORT, ports[i], i, &tftp_exp_policy, - 0, tftp_help, NULL, THIS_MODULE); + 0, tftp_help, NULL, THIS_MODULE, + NF_CT_NAT_HELPER_MOD_NAME("tftp")); } ret = nf_conntrack_helpers_register(tftp, ports_c * 2); -- 2.20.1