On 10/22/2019 11:47 PM, Pablo Neira Ayuso wrote: > This patch adds encapsulation support through the encapsulation object, > that specifies the encapsulation policy. > > Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> > --- > include/uapi/linux/netfilter/nf_tables.h | 40 +++++- > net/netfilter/nft_encap.c | 224 ++++++++++++++++++++++++++++++- > 2 files changed, 262 insertions(+), 2 deletions(-) > > diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h > index 25e26340a0ba..e5997a13ba71 100644 > --- a/include/uapi/linux/netfilter/nf_tables.h > +++ b/include/uapi/linux/netfilter/nf_tables.h > @@ -1484,7 +1484,8 @@ enum nft_ct_expectation_attributes { > #define NFT_OBJECT_SECMARK 8 > #define NFT_OBJECT_CT_EXPECT 9 > #define NFT_OBJECT_SYNPROXY 10 > -#define __NFT_OBJECT_MAX 11 > +#define NFT_OBJECT_ENCAP 11 > +#define __NFT_OBJECT_MAX 12 > #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) > > /** > @@ -1629,6 +1630,43 @@ enum nft_encap_type { > NFT_ENCAP_VLAN = 0, > }; > > +enum nft_encap_op { > + NFT_ENCAP_ADD = 0, > + NFT_ENCAP_UPDATE, > +}; > + > +/** > + * enum nft_encap_vlan_attributes - nf_tables VLAN encapsulation expression netlink attributes > + * > + * @NFTA_ENCAP_VLAN_ID: VLAN id (NLA_U16) > + * @NFTA_ENCAP_VLAN_PROTO: VLAN protocol (NLA_U16) > + * @NFTA_ENCAP_VLAN_PRIO: VLAN priority (NLA_U8) > + */ > +enum nft_encap_vlan_attributes { > + NFTA_ENCAP_VLAN_UNSPEC, > + NFTA_ENCAP_VLAN_ID, > + NFTA_ENCAP_VLAN_PROTO, > + NFTA_ENCAP_VLAN_PRIO, > + __NFTA_ENCAP_VLAN_MAX > +}; > +#define NFTA_ENCAP_VLAN_MAX (__NFTA_ENCAP_VLAN_MAX - 1) > + > +/** > + * enum nft_encap_attributes - nf_tables encapsulation expression netlink attributes > + * > + * @NFTA_ENCAP_TYPE: encapsulation type (NLA_U32) > + * @NFTA_ENCAP_OP: encapsulation operation (NLA_U32) > + * @NFTA_ENCAP_DATA: encapsulation data (NLA_NESTED) > + */ > +enum nft_encap_attributes { > + NFTA_ENCAP_UNSPEC, > + NFTA_ENCAP_TYPE, > + NFTA_ENCAP_OP, > + NFTA_ENCAP_DATA, > + __NFTA_ENCAP_MAX > +}; > +#define NFTA_ENCAP_MAX (__NFTA_ENCAP_MAX - 1) > + > /** > * enum nft_decap_attributes - nf_tables decapsulation expression netlink attributes > * > diff --git a/net/netfilter/nft_encap.c b/net/netfilter/nft_encap.c > index 657a62e4c283..13643b3daf85 100644 > --- a/net/netfilter/nft_encap.c > +++ b/net/netfilter/nft_encap.c > @@ -2,6 +2,7 @@ > #include <linux/kernel.h> > #include <linux/init.h> > #include <linux/module.h> > +#include <linux/if_vlan.h> > #include <linux/netlink.h> > #include <linux/netfilter.h> > #include <linux/netfilter/nf_tables.h> > @@ -101,14 +102,235 @@ static struct nft_expr_type nft_decap_type __read_mostly = { > .owner = THIS_MODULE, > }; > > +struct nft_encap { > + enum nft_encap_type type; > + enum nft_encap_op op; > + > + union { > + struct { > + __u16 id; > + __be16 proto; > + __u8 prio; > + } vlan; > + }; > +}; > + > +static u16 nft_encap_vlan_tci(struct nft_encap *priv) > +{ > + return priv->vlan.id | (priv->vlan.prio << VLAN_PRIO_SHIFT); > +} > + > +static int nft_encap_vlan_eval(struct nft_encap *priv, > + struct nft_regs *regs, > + const struct nft_pktinfo *pkt) > +{ > + struct sk_buff *skb = pkt->skb; > + int err; > + u16 tci; > + > + switch (priv->op) { > + case NFT_ENCAP_ADD: > + err = skb_vlan_push(skb, priv->vlan.proto, > + nft_encap_vlan_tci(priv)); > + if (err) > + return err; > + break; > + case NFT_ENCAP_UPDATE: > + if (!skb_vlan_tagged(skb)) > + return -1; > + > + err = 0; > + if (skb_vlan_tag_present(skb)) { > + tci = skb_vlan_tag_get(skb); > + __vlan_hwaccel_clear_tag(skb); > + } else { > + err = __skb_vlan_pop(skb, &tci); > + } > + if (err) > + return err; > + > + tci = (tci & ~VLAN_VID_MASK) | priv->vlan.id; > + if (priv->vlan.prio) { > + tci &= ~VLAN_PRIO_MASK; > + tci |= priv->vlan.prio << VLAN_PRIO_SHIFT; > + } > + > + __vlan_hwaccel_put_tag(skb, priv->vlan.proto, tci); > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static void nft_encap_obj_eval(struct nft_object *obj, > + struct nft_regs *regs, > + const struct nft_pktinfo *pkt) > +{ > + struct nft_encap *priv = nft_obj_data(obj); > + int err; > + > + switch (priv->type) { > + case NFT_ENCAP_VLAN: > + err = nft_encap_vlan_eval(priv, regs, pkt); > + break; > + default: > + WARN_ON_ONCE(1); > + err = -1; > + } > + > + if (err < 0) > + regs->verdict.code = NFT_BREAK; > +} > + > +static const struct nla_policy nft_encap_vlan_policy[NFTA_ENCAP_VLAN_MAX + 1] = { > + [NFTA_ENCAP_VLAN_ID] = { .type = NLA_U16 }, > + [NFTA_ENCAP_VLAN_PROTO] = { .type = NLA_U16 }, > + [NFTA_ENCAP_VLAN_PRIO] = { .type = NLA_U8 }, > +}; > + > +static int nft_encap_vlan_parse(const struct nlattr *attr, > + struct nft_encap *priv) > +{ > + struct nlattr *tb[NFTA_ENCAP_VLAN_MAX + 1]; > + int err; > + > + err = nla_parse_nested_deprecated(tb, NFTA_ENCAP_VLAN_MAX, attr, > + nft_encap_vlan_policy, NULL); > + if (err < 0) > + return err; > + > + if (!tb[NFTA_ENCAP_VLAN_PRIO] || > + !tb[NFTA_ENCAP_VLAN_PROTO] || > + !tb[NFTA_ENCAP_VLAN_ID]) > + return -EINVAL; > + > + priv->vlan.id = ntohs(nla_get_be16(tb[NFTA_ENCAP_VLAN_ID])); > + priv->vlan.proto = nla_get_be16(tb[NFTA_ENCAP_VLAN_PROTO]); Maybe it is better to check the vlan.proto is ETH_P_8021Q or ETH_P_8021AD? > + priv->vlan.prio = nla_get_u8(tb[NFTA_ENCAP_VLAN_PRIO]); > + > + return 0; > +} > + > +static int nft_encap_obj_init(const struct nft_ctx *ctx, > + const struct nlattr * const tb[], > + struct nft_object *obj) > +{ > + struct nft_encap *priv = nft_obj_data(obj); > + int err = 0; > + > + if (!tb[NFTA_ENCAP_TYPE] || > + !tb[NFTA_ENCAP_OP]) > + return -EINVAL; > + > + priv->type = ntohl(nla_get_be32(tb[NFTA_ENCAP_TYPE])); > + priv->op = ntohl(nla_get_be32(tb[NFTA_ENCAP_OP])); > + > + switch (priv->type) { > + case NFT_ENCAP_VLAN: > + if (priv->op != NFT_ENCAP_ADD && > + priv->op != NFT_ENCAP_UPDATE) > + return -EOPNOTSUPP; > + > + err = nft_encap_vlan_parse(tb[NFTA_ENCAP_DATA], priv); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + return err; > +} > + >