|
@@ -32,6 +32,12 @@ struct nft_ct {
|
|
|
};
|
|
|
};
|
|
|
|
|
|
+struct nft_ct_helper_obj {
|
|
|
+ struct nf_conntrack_helper *helper4;
|
|
|
+ struct nf_conntrack_helper *helper6;
|
|
|
+ u8 l4proto;
|
|
|
+};
|
|
|
+
|
|
|
#ifdef CONFIG_NF_CONNTRACK_ZONES
|
|
|
static DEFINE_PER_CPU(struct nf_conn *, nft_ct_pcpu_template);
|
|
|
static unsigned int nft_ct_pcpu_template_refcnt __read_mostly;
|
|
@@ -730,6 +736,162 @@ static struct nft_expr_type nft_notrack_type __read_mostly = {
|
|
|
.owner = THIS_MODULE,
|
|
|
};
|
|
|
|
|
|
+static int nft_ct_helper_obj_init(const struct nft_ctx *ctx,
|
|
|
+ const struct nlattr * const tb[],
|
|
|
+ struct nft_object *obj)
|
|
|
+{
|
|
|
+ struct nft_ct_helper_obj *priv = nft_obj_data(obj);
|
|
|
+ struct nf_conntrack_helper *help4, *help6;
|
|
|
+ char name[NF_CT_HELPER_NAME_LEN];
|
|
|
+ int family = ctx->afi->family;
|
|
|
+
|
|
|
+ if (!tb[NFTA_CT_HELPER_NAME] || !tb[NFTA_CT_HELPER_L4PROTO])
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ priv->l4proto = nla_get_u8(tb[NFTA_CT_HELPER_L4PROTO]);
|
|
|
+ if (!priv->l4proto)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ nla_strlcpy(name, tb[NFTA_CT_HELPER_NAME], sizeof(name));
|
|
|
+
|
|
|
+ if (tb[NFTA_CT_HELPER_L3PROTO])
|
|
|
+ family = ntohs(nla_get_be16(tb[NFTA_CT_HELPER_L3PROTO]));
|
|
|
+
|
|
|
+ help4 = NULL;
|
|
|
+ help6 = NULL;
|
|
|
+
|
|
|
+ switch (family) {
|
|
|
+ case NFPROTO_IPV4:
|
|
|
+ if (ctx->afi->family == NFPROTO_IPV6)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ help4 = nf_conntrack_helper_try_module_get(name, family,
|
|
|
+ priv->l4proto);
|
|
|
+ break;
|
|
|
+ case NFPROTO_IPV6:
|
|
|
+ if (ctx->afi->family == NFPROTO_IPV4)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ help6 = nf_conntrack_helper_try_module_get(name, family,
|
|
|
+ priv->l4proto);
|
|
|
+ break;
|
|
|
+ case NFPROTO_NETDEV: /* fallthrough */
|
|
|
+ case NFPROTO_BRIDGE: /* same */
|
|
|
+ case NFPROTO_INET:
|
|
|
+ help4 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV4,
|
|
|
+ priv->l4proto);
|
|
|
+ help6 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV6,
|
|
|
+ priv->l4proto);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EAFNOSUPPORT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* && is intentional; only error if INET found neither ipv4 or ipv6 */
|
|
|
+ if (!help4 && !help6)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ priv->helper4 = help4;
|
|
|
+ priv->helper6 = help6;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void nft_ct_helper_obj_destroy(struct nft_object *obj)
|
|
|
+{
|
|
|
+ struct nft_ct_helper_obj *priv = nft_obj_data(obj);
|
|
|
+
|
|
|
+ if (priv->helper4)
|
|
|
+ module_put(priv->helper4->me);
|
|
|
+ if (priv->helper6)
|
|
|
+ module_put(priv->helper6->me);
|
|
|
+}
|
|
|
+
|
|
|
+static void nft_ct_helper_obj_eval(struct nft_object *obj,
|
|
|
+ struct nft_regs *regs,
|
|
|
+ const struct nft_pktinfo *pkt)
|
|
|
+{
|
|
|
+ const struct nft_ct_helper_obj *priv = nft_obj_data(obj);
|
|
|
+ struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb);
|
|
|
+ struct nf_conntrack_helper *to_assign = NULL;
|
|
|
+ struct nf_conn_help *help;
|
|
|
+
|
|
|
+ if (!ct ||
|
|
|
+ nf_ct_is_confirmed(ct) ||
|
|
|
+ nf_ct_is_template(ct) ||
|
|
|
+ priv->l4proto != nf_ct_protonum(ct))
|
|
|
+ return;
|
|
|
+
|
|
|
+ switch (nf_ct_l3num(ct)) {
|
|
|
+ case NFPROTO_IPV4:
|
|
|
+ to_assign = priv->helper4;
|
|
|
+ break;
|
|
|
+ case NFPROTO_IPV6:
|
|
|
+ to_assign = priv->helper6;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!to_assign)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (test_bit(IPS_HELPER_BIT, &ct->status))
|
|
|
+ return;
|
|
|
+
|
|
|
+ help = nf_ct_helper_ext_add(ct, to_assign, GFP_ATOMIC);
|
|
|
+ if (help) {
|
|
|
+ rcu_assign_pointer(help->helper, to_assign);
|
|
|
+ set_bit(IPS_HELPER_BIT, &ct->status);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int nft_ct_helper_obj_dump(struct sk_buff *skb,
|
|
|
+ struct nft_object *obj, bool reset)
|
|
|
+{
|
|
|
+ const struct nft_ct_helper_obj *priv = nft_obj_data(obj);
|
|
|
+ const struct nf_conntrack_helper *helper = priv->helper4;
|
|
|
+ u16 family;
|
|
|
+
|
|
|
+ if (nla_put_string(skb, NFTA_CT_HELPER_NAME, helper->name))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (nla_put_u8(skb, NFTA_CT_HELPER_L4PROTO, priv->l4proto))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (priv->helper4 && priv->helper6)
|
|
|
+ family = NFPROTO_INET;
|
|
|
+ else if (priv->helper6)
|
|
|
+ family = NFPROTO_IPV6;
|
|
|
+ else
|
|
|
+ family = NFPROTO_IPV4;
|
|
|
+
|
|
|
+ if (nla_put_be16(skb, NFTA_CT_HELPER_L3PROTO, htons(family)))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = {
|
|
|
+ [NFTA_CT_HELPER_NAME] = { .type = NLA_STRING,
|
|
|
+ .len = NF_CT_HELPER_NAME_LEN - 1 },
|
|
|
+ [NFTA_CT_HELPER_L3PROTO] = { .type = NLA_U16 },
|
|
|
+ [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 },
|
|
|
+};
|
|
|
+
|
|
|
+static struct nft_object_type nft_ct_helper_obj __read_mostly = {
|
|
|
+ .type = NFT_OBJECT_CT_HELPER,
|
|
|
+ .size = sizeof(struct nft_ct_helper_obj),
|
|
|
+ .maxattr = NFTA_CT_HELPER_MAX,
|
|
|
+ .policy = nft_ct_helper_policy,
|
|
|
+ .eval = nft_ct_helper_obj_eval,
|
|
|
+ .init = nft_ct_helper_obj_init,
|
|
|
+ .destroy = nft_ct_helper_obj_destroy,
|
|
|
+ .dump = nft_ct_helper_obj_dump,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+};
|
|
|
+
|
|
|
static int __init nft_ct_module_init(void)
|
|
|
{
|
|
|
int err;
|
|
@@ -744,7 +906,14 @@ static int __init nft_ct_module_init(void)
|
|
|
if (err < 0)
|
|
|
goto err1;
|
|
|
|
|
|
+ err = nft_register_obj(&nft_ct_helper_obj);
|
|
|
+ if (err < 0)
|
|
|
+ goto err2;
|
|
|
+
|
|
|
return 0;
|
|
|
+
|
|
|
+err2:
|
|
|
+ nft_unregister_expr(&nft_notrack_type);
|
|
|
err1:
|
|
|
nft_unregister_expr(&nft_ct_type);
|
|
|
return err;
|
|
@@ -752,6 +921,7 @@ err1:
|
|
|
|
|
|
static void __exit nft_ct_module_exit(void)
|
|
|
{
|
|
|
+ nft_unregister_obj(&nft_ct_helper_obj);
|
|
|
nft_unregister_expr(&nft_notrack_type);
|
|
|
nft_unregister_expr(&nft_ct_type);
|
|
|
}
|
|
@@ -763,3 +933,4 @@ MODULE_LICENSE("GPL");
|
|
|
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
|
|
MODULE_ALIAS_NFT_EXPR("ct");
|
|
|
MODULE_ALIAS_NFT_EXPR("notrack");
|
|
|
+MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_HELPER);
|