linux/net/netfilter/nft_xfrm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *
   4 * Generic part shared by ipv4 and ipv6 backends.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/init.h>
   9#include <linux/module.h>
  10#include <linux/netlink.h>
  11#include <linux/netfilter.h>
  12#include <linux/netfilter/nf_tables.h>
  13#include <net/netfilter/nf_tables_core.h>
  14#include <net/netfilter/nf_tables.h>
  15#include <linux/in.h>
  16#include <net/xfrm.h>
  17
  18static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
  19        [NFTA_XFRM_KEY]         = { .type = NLA_U32 },
  20        [NFTA_XFRM_DIR]         = { .type = NLA_U8 },
  21        [NFTA_XFRM_SPNUM]       = { .type = NLA_U32 },
  22        [NFTA_XFRM_DREG]        = { .type = NLA_U32 },
  23};
  24
  25struct nft_xfrm {
  26        enum nft_xfrm_keys      key:8;
  27        u8                      dreg;
  28        u8                      dir;
  29        u8                      spnum;
  30};
  31
  32static int nft_xfrm_get_init(const struct nft_ctx *ctx,
  33                             const struct nft_expr *expr,
  34                             const struct nlattr * const tb[])
  35{
  36        struct nft_xfrm *priv = nft_expr_priv(expr);
  37        unsigned int len = 0;
  38        u32 spnum = 0;
  39        u8 dir;
  40
  41        if (!tb[NFTA_XFRM_KEY] || !tb[NFTA_XFRM_DIR] || !tb[NFTA_XFRM_DREG])
  42                return -EINVAL;
  43
  44        switch (ctx->family) {
  45        case NFPROTO_IPV4:
  46        case NFPROTO_IPV6:
  47        case NFPROTO_INET:
  48                break;
  49        default:
  50                return -EOPNOTSUPP;
  51        }
  52
  53        priv->key = ntohl(nla_get_u32(tb[NFTA_XFRM_KEY]));
  54        switch (priv->key) {
  55        case NFT_XFRM_KEY_REQID:
  56        case NFT_XFRM_KEY_SPI:
  57                len = sizeof(u32);
  58                break;
  59        case NFT_XFRM_KEY_DADDR_IP4:
  60        case NFT_XFRM_KEY_SADDR_IP4:
  61                len = sizeof(struct in_addr);
  62                break;
  63        case NFT_XFRM_KEY_DADDR_IP6:
  64        case NFT_XFRM_KEY_SADDR_IP6:
  65                len = sizeof(struct in6_addr);
  66                break;
  67        default:
  68                return -EINVAL;
  69        }
  70
  71        dir = nla_get_u8(tb[NFTA_XFRM_DIR]);
  72        switch (dir) {
  73        case XFRM_POLICY_IN:
  74        case XFRM_POLICY_OUT:
  75                priv->dir = dir;
  76                break;
  77        default:
  78                return -EINVAL;
  79        }
  80
  81        if (tb[NFTA_XFRM_SPNUM])
  82                spnum = ntohl(nla_get_be32(tb[NFTA_XFRM_SPNUM]));
  83
  84        if (spnum >= XFRM_MAX_DEPTH)
  85                return -ERANGE;
  86
  87        priv->spnum = spnum;
  88
  89        return nft_parse_register_store(ctx, tb[NFTA_XFRM_DREG], &priv->dreg,
  90                                        NULL, NFT_DATA_VALUE, len);
  91}
  92
  93/* Return true if key asks for daddr/saddr and current
  94 * state does have a valid address (BEET, TUNNEL).
  95 */
  96static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode)
  97{
  98        switch (k) {
  99        case NFT_XFRM_KEY_DADDR_IP4:
 100        case NFT_XFRM_KEY_SADDR_IP4:
 101                if (family == NFPROTO_IPV4)
 102                        break;
 103                return false;
 104        case NFT_XFRM_KEY_DADDR_IP6:
 105        case NFT_XFRM_KEY_SADDR_IP6:
 106                if (family == NFPROTO_IPV6)
 107                        break;
 108                return false;
 109        default:
 110                return true;
 111        }
 112
 113        return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL;
 114}
 115
 116static void nft_xfrm_state_get_key(const struct nft_xfrm *priv,
 117                                   struct nft_regs *regs,
 118                                   const struct xfrm_state *state)
 119{
 120        u32 *dest = &regs->data[priv->dreg];
 121
 122        if (!xfrm_state_addr_ok(priv->key,
 123                                state->props.family,
 124                                state->props.mode)) {
 125                regs->verdict.code = NFT_BREAK;
 126                return;
 127        }
 128
 129        switch (priv->key) {
 130        case NFT_XFRM_KEY_UNSPEC:
 131        case __NFT_XFRM_KEY_MAX:
 132                WARN_ON_ONCE(1);
 133                break;
 134        case NFT_XFRM_KEY_DADDR_IP4:
 135                *dest = state->id.daddr.a4;
 136                return;
 137        case NFT_XFRM_KEY_DADDR_IP6:
 138                memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr));
 139                return;
 140        case NFT_XFRM_KEY_SADDR_IP4:
 141                *dest = state->props.saddr.a4;
 142                return;
 143        case NFT_XFRM_KEY_SADDR_IP6:
 144                memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr));
 145                return;
 146        case NFT_XFRM_KEY_REQID:
 147                *dest = state->props.reqid;
 148                return;
 149        case NFT_XFRM_KEY_SPI:
 150                *dest = state->id.spi;
 151                return;
 152        }
 153
 154        regs->verdict.code = NFT_BREAK;
 155}
 156
 157static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
 158                                    struct nft_regs *regs,
 159                                    const struct nft_pktinfo *pkt)
 160{
 161        const struct sec_path *sp = skb_sec_path(pkt->skb);
 162        const struct xfrm_state *state;
 163
 164        if (sp == NULL || sp->len <= priv->spnum) {
 165                regs->verdict.code = NFT_BREAK;
 166                return;
 167        }
 168
 169        state = sp->xvec[priv->spnum];
 170        nft_xfrm_state_get_key(priv, regs, state);
 171}
 172
 173static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
 174                                  struct nft_regs *regs,
 175                                  const struct nft_pktinfo *pkt)
 176{
 177        const struct dst_entry *dst = skb_dst(pkt->skb);
 178        int i;
 179
 180        for (i = 0; dst && dst->xfrm;
 181             dst = ((const struct xfrm_dst *)dst)->child, i++) {
 182                if (i < priv->spnum)
 183                        continue;
 184
 185                nft_xfrm_state_get_key(priv, regs, dst->xfrm);
 186                return;
 187        }
 188
 189        regs->verdict.code = NFT_BREAK;
 190}
 191
 192static void nft_xfrm_get_eval(const struct nft_expr *expr,
 193                              struct nft_regs *regs,
 194                              const struct nft_pktinfo *pkt)
 195{
 196        const struct nft_xfrm *priv = nft_expr_priv(expr);
 197
 198        switch (priv->dir) {
 199        case XFRM_POLICY_IN:
 200                nft_xfrm_get_eval_in(priv, regs, pkt);
 201                break;
 202        case XFRM_POLICY_OUT:
 203                nft_xfrm_get_eval_out(priv, regs, pkt);
 204                break;
 205        default:
 206                WARN_ON_ONCE(1);
 207                regs->verdict.code = NFT_BREAK;
 208                break;
 209        }
 210}
 211
 212static int nft_xfrm_get_dump(struct sk_buff *skb,
 213                             const struct nft_expr *expr)
 214{
 215        const struct nft_xfrm *priv = nft_expr_priv(expr);
 216
 217        if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg))
 218                return -1;
 219
 220        if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key)))
 221                return -1;
 222        if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir))
 223                return -1;
 224        if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum)))
 225                return -1;
 226
 227        return 0;
 228}
 229
 230static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 231                             const struct nft_data **data)
 232{
 233        const struct nft_xfrm *priv = nft_expr_priv(expr);
 234        unsigned int hooks;
 235
 236        switch (priv->dir) {
 237        case XFRM_POLICY_IN:
 238                hooks = (1 << NF_INET_FORWARD) |
 239                        (1 << NF_INET_LOCAL_IN) |
 240                        (1 << NF_INET_PRE_ROUTING);
 241                break;
 242        case XFRM_POLICY_OUT:
 243                hooks = (1 << NF_INET_FORWARD) |
 244                        (1 << NF_INET_LOCAL_OUT) |
 245                        (1 << NF_INET_POST_ROUTING);
 246                break;
 247        default:
 248                WARN_ON_ONCE(1);
 249                return -EINVAL;
 250        }
 251
 252        return nft_chain_validate_hooks(ctx->chain, hooks);
 253}
 254
 255
 256static struct nft_expr_type nft_xfrm_type;
 257static const struct nft_expr_ops nft_xfrm_get_ops = {
 258        .type           = &nft_xfrm_type,
 259        .size           = NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
 260        .eval           = nft_xfrm_get_eval,
 261        .init           = nft_xfrm_get_init,
 262        .dump           = nft_xfrm_get_dump,
 263        .validate       = nft_xfrm_validate,
 264};
 265
 266static struct nft_expr_type nft_xfrm_type __read_mostly = {
 267        .name           = "xfrm",
 268        .ops            = &nft_xfrm_get_ops,
 269        .policy         = nft_xfrm_policy,
 270        .maxattr        = NFTA_XFRM_MAX,
 271        .owner          = THIS_MODULE,
 272};
 273
 274static int __init nft_xfrm_module_init(void)
 275{
 276        return nft_register_expr(&nft_xfrm_type);
 277}
 278
 279static void __exit nft_xfrm_module_exit(void)
 280{
 281        nft_unregister_expr(&nft_xfrm_type);
 282}
 283
 284module_init(nft_xfrm_module_init);
 285module_exit(nft_xfrm_module_exit);
 286
 287MODULE_LICENSE("GPL");
 288MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
 289MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 290MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
 291MODULE_ALIAS_NFT_EXPR("xfrm");
 292