On Fri, May 17, 2019 at 06:40:29PM +0200, Stéphane Veyret wrote: > This patch allows to add, list and delete expectations via nft objref > infrastructure and assigning these expectations via nft rule. > > This allows manual port triggering when no helper is defined to manage a > specific protocol. For example, if I have an online game which protocol > is based on initial connection to TCP port 9753 of the server, and where > the server opens a connection to port 9876, I can set rules as follow: > > table ip filter { > ct expectation mygame { > protocol udp; > dport 9876; I think we should set a maximum number of expectations to be created, as a mandatory field, eg. size 10; > } > > chain input { > type filter hook input priority 0; policy drop; > tcp dport 9753 ct expectation set "mygame"; > } > > chain output { > type filter hook output priority 0; policy drop; > udp dport 9876 ct status expected accept; > } > } [...] > diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c > index f043936763f3..d01cb175ab30 100644 > --- a/net/netfilter/nft_ct.c > +++ b/net/netfilter/nft_ct.c > @@ -24,6 +24,7 @@ > #include <net/netfilter/nf_conntrack_labels.h> > #include <net/netfilter/nf_conntrack_timeout.h> > #include <net/netfilter/nf_conntrack_l4proto.h> > +#include <net/netfilter/nf_conntrack_expect.h> > > struct nft_ct { > enum nft_ct_keys key:8; > @@ -790,6 +791,114 @@ static struct nft_expr_type nft_notrack_type __read_mostly = { > .owner = THIS_MODULE, > }; > > +struct nft_ct_expect_obj { > + int l3num; > + u8 l4proto; > + __be16 dport; > + u32 timeout; > +}; > + > +static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, > + const struct nlattr * const tb[], > + struct nft_object *obj) > +{ > + struct nft_ct_expect_obj *priv = nft_obj_data(obj); > + int ret; > + > + if (!tb[NFTA_CT_EXPECT_L4PROTO] || > + !tb[NFTA_CT_EXPECT_DPORT] || > + !tb[NFTA_CT_EXPECT_TIMEOUT]) Coding style: Align parameter to parens: if (!tb[NFTA_CT_EXPECT_L4PROTO] || !tb[NFTA_CT_EXPECT_DPORT] || !tb[NFTA_CT_EXPECT_TIMEOUT]) return -EINVAL; > + priv->l3num = ctx->family; priv->l3num is only set and never used, remove it. You'll also have to remove NFTA_CT_EXPECT_L3PROTO. > + if (tb[NFTA_CT_EXPECT_L3PROTO]) > + priv->l3num = ntohs(nla_get_be16(tb[NFTA_CT_EXPECT_L3PROTO])); > + > + priv->l4proto = nla_get_u8(tb[NFTA_CT_EXPECT_L4PROTO]); > + priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]); > + priv->timeout = nla_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); > + > + ret = nf_ct_netns_get(ctx->net, ctx->family); Just: return nf_ct_netns_get(ctx->net, ctx->family); should be fine. > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static void nft_ct_expect_obj_destroy(const struct nft_ctx *ctx, > + struct nft_object *obj) > +{ > + nf_ct_netns_put(ctx->net, ctx->family); > +} > + > +static int nft_ct_expect_obj_dump(struct sk_buff *skb, > + struct nft_object *obj, bool reset) > +{ > + const struct nft_ct_expect_obj *priv = nft_obj_data(obj); > + > + if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || > + nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || > + nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || > + nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout)) > + return -1; Coding style: Align parameter to parens: if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout)) return -1; > + return 0; > +} > + > +static void nft_ct_expect_obj_eval(struct nft_object *obj, > + struct nft_regs *regs, > + const struct nft_pktinfo *pkt) > +{ > + const struct nft_ct_expect_obj *priv = nft_obj_data(obj); > + enum ip_conntrack_info ctinfo; > + struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); > + int dir = CTINFO2DIR(ctinfo); > + struct nf_conntrack_expect *exp; Please, revert xmas tree for variable definitions, ie. const struct nft_ct_expect_obj *priv = nft_obj_data(obj); struct nf_conntrack_expect *exp; enum ip_conntrack_info ctinfo; int dir = CTINFO2DIR(ctinfo); struct nf_conn *ct; Then, you have to check if ct is unset or is untrackedie. ct = nf_ct_get(pkt->skb, &ctinfo); if (!ct || ctinfo == IP_CT_UNTRACKED) goto err; ... err: regs->verdict.code = NFT_BREAK; > + nf_ct_helper_ext_add(ct, GFP_ATOMIC); I think you don't need nf_ct_helper_ext_add(...); > + exp = nf_ct_expect_alloc(ct); > + if (exp == NULL) { > + regs->verdict.code = NF_DROP; > + return; > + } > + > + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, priv->l3num, > + &ct->tuplehash[!dir].tuple.src.u3, > + &ct->tuplehash[!dir].tuple.dst.u3, > + priv->l4proto, NULL, &priv->dport); Coding style: Align parameter to parens: nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, priv->l3num, &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, priv->l4proto, NULL, &priv->dport); > + exp->timeout.expires = jiffies + priv->timeout * HZ; > + > + if (nf_ct_expect_related(exp) != 0) { > + regs->verdict.code = NF_DROP; > + } No need for curly braces here, with single statement, the following is fine: if (nf_ct_expect_related(exp) != 0) regs->verdict.code = NF_DROP; Thanks for submitting your patch.