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        enum nft_registers      dreg:8;
  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        priv->dreg = nft_parse_register(tb[NFTA_XFRM_DREG]);
  90        return nft_validate_register_store(ctx, priv->dreg, NULL,
  91                                           NFT_DATA_VALUE, len);
  92}
  93
  94/* Return true if key asks for daddr/saddr and current
  95 * state does have a valid address (BEET, TUNNEL).
  96 */
  97static bool xfrm_state_addr_ok(enum nft_xfrm_keys k, u8 family, u8 mode)
  98{
  99        switch (k) {
 100        case NFT_XFRM_KEY_DADDR_IP4:
 101        case NFT_XFRM_KEY_SADDR_IP4:
 102                if (family == NFPROTO_IPV4)
 103                        break;
 104                return false;
 105        case NFT_XFRM_KEY_DADDR_IP6:
 106        case NFT_XFRM_KEY_SADDR_IP6:
 107                if (family == NFPROTO_IPV6)
 108                        break;
 109                return false;
 110        default:
 111                return true;
 112        }
 113
 114        return mode == XFRM_MODE_BEET || mode == XFRM_MODE_TUNNEL;
 115}
 116
 117static void nft_xfrm_state_get_key(const struct nft_xfrm *priv,
 118                                   struct nft_regs *regs,
 119                                   const struct xfrm_state *state)
 120{
 121        u32 *dest = &regs->data[priv->dreg];
 122
 123        if (!xfrm_state_addr_ok(priv->key,
 124                                state->props.family,
 125                                state->props.mode)) {
 126                regs->verdict.code = NFT_BREAK;
 127                return;
 128        }
 129
 130        switch (priv->key) {
 131        case NFT_XFRM_KEY_UNSPEC:
 132        case __NFT_XFRM_KEY_MAX:
 133                WARN_ON_ONCE(1);
 134                break;
 135        case NFT_XFRM_KEY_DADDR_IP4:
 136                *dest = state->id.daddr.a4;
 137                return;
 138        case NFT_XFRM_KEY_DADDR_IP6:
 139                memcpy(dest, &state->id.daddr.in6, sizeof(struct in6_addr));
 140                return;
 141        case NFT_XFRM_KEY_SADDR_IP4:
 142                *dest = state->props.saddr.a4;
 143                return;
 144        case NFT_XFRM_KEY_SADDR_IP6:
 145                memcpy(dest, &state->props.saddr.in6, sizeof(struct in6_addr));
 146                return;
 147        case NFT_XFRM_KEY_REQID:
 148                *dest = state->props.reqid;
 149                return;
 150        case NFT_XFRM_KEY_SPI:
 151                *dest = state->id.spi;
 152                return;
 153        }
 154
 155        regs->verdict.code = NFT_BREAK;
 156}
 157
 158static void nft_xfrm_get_eval_in(const struct nft_xfrm *priv,
 159                                    struct nft_regs *regs,
 160                                    const struct nft_pktinfo *pkt)
 161{
 162        const struct sec_path *sp = skb_sec_path(pkt->skb);
 163        const struct xfrm_state *state;
 164
 165        if (sp == NULL || sp->len <= priv->spnum) {
 166                regs->verdict.code = NFT_BREAK;
 167                return;
 168        }
 169
 170        state = sp->xvec[priv->spnum];
 171        nft_xfrm_state_get_key(priv, regs, state);
 172}
 173
 174static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
 175                                  struct nft_regs *regs,
 176                                  const struct nft_pktinfo *pkt)
 177{
 178        const struct dst_entry *dst = skb_dst(pkt->skb);
 179        int i;
 180
 181        for (i = 0; dst && dst->xfrm;
 182             dst = ((const struct xfrm_dst *)dst)->child, i++) {
 183                if (i < priv->spnum)
 184                        continue;
 185
 186                nft_xfrm_state_get_key(priv, regs, dst->xfrm);
 187                return;
 188        }
 189
 190        regs->verdict.code = NFT_BREAK;
 191}
 192
 193static void nft_xfrm_get_eval(const struct nft_expr *expr,
 194                              struct nft_regs *regs,
 195                              const struct nft_pktinfo *pkt)
 196{
 197        const struct nft_xfrm *priv = nft_expr_priv(expr);
 198
 199        switch (priv->dir) {
 200        case XFRM_POLICY_IN:
 201                nft_xfrm_get_eval_in(priv, regs, pkt);
 202                break;
 203        case XFRM_POLICY_OUT:
 204                nft_xfrm_get_eval_out(priv, regs, pkt);
 205                break;
 206        default:
 207                WARN_ON_ONCE(1);
 208                regs->verdict.code = NFT_BREAK;
 209                break;
 210        }
 211}
 212
 213static int nft_xfrm_get_dump(struct sk_buff *skb,
 214                             const struct nft_expr *expr)
 215{
 216        const struct nft_xfrm *priv = nft_expr_priv(expr);
 217
 218        if (nft_dump_register(skb, NFTA_XFRM_DREG, priv->dreg))
 219                return -1;
 220
 221        if (nla_put_be32(skb, NFTA_XFRM_KEY, htonl(priv->key)))
 222                return -1;
 223        if (nla_put_u8(skb, NFTA_XFRM_DIR, priv->dir))
 224                return -1;
 225        if (nla_put_be32(skb, NFTA_XFRM_SPNUM, htonl(priv->spnum)))
 226                return -1;
 227
 228        return 0;
 229}
 230
 231static int nft_xfrm_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 232                             const struct nft_data **data)
 233{
 234        const struct nft_xfrm *priv = nft_expr_priv(expr);
 235        unsigned int hooks;
 236
 237        switch (priv->dir) {
 238        case XFRM_POLICY_IN:
 239                hooks = (1 << NF_INET_FORWARD) |
 240                        (1 << NF_INET_LOCAL_IN) |
 241                        (1 << NF_INET_PRE_ROUTING);
 242                break;
 243        case XFRM_POLICY_OUT:
 244                hooks = (1 << NF_INET_FORWARD) |
 245                        (1 << NF_INET_LOCAL_OUT) |
 246                        (1 << NF_INET_POST_ROUTING);
 247                break;
 248        default:
 249                WARN_ON_ONCE(1);
 250                return -EINVAL;
 251        }
 252
 253        return nft_chain_validate_hooks(ctx->chain, hooks);
 254}
 255
 256
 257static struct nft_expr_type nft_xfrm_type;
 258static const struct nft_expr_ops nft_xfrm_get_ops = {
 259        .type           = &nft_xfrm_type,
 260        .size           = NFT_EXPR_SIZE(sizeof(struct nft_xfrm)),
 261        .eval           = nft_xfrm_get_eval,
 262        .init           = nft_xfrm_get_init,
 263        .dump           = nft_xfrm_get_dump,
 264        .validate       = nft_xfrm_validate,
 265};
 266
 267static struct nft_expr_type nft_xfrm_type __read_mostly = {
 268        .name           = "xfrm",
 269        .ops            = &nft_xfrm_get_ops,
 270        .policy         = nft_xfrm_policy,
 271        .maxattr        = NFTA_XFRM_MAX,
 272        .owner          = THIS_MODULE,
 273};
 274
 275static int __init nft_xfrm_module_init(void)
 276{
 277        return nft_register_expr(&nft_xfrm_type);
 278}
 279
 280static void __exit nft_xfrm_module_exit(void)
 281{
 282        nft_unregister_expr(&nft_xfrm_type);
 283}
 284
 285module_init(nft_xfrm_module_init);
 286module_exit(nft_xfrm_module_exit);
 287
 288MODULE_LICENSE("GPL");
 289MODULE_DESCRIPTION("nf_tables: xfrm/IPSec matching");
 290MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
 291MODULE_AUTHOR("Máté Eckl <ecklm94@gmail.com>");
 292MODULE_ALIAS_NFT_EXPR("xfrm");
 293