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