linux/net/netfilter/nft_rt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com>
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/netlink.h>
   8#include <linux/netfilter.h>
   9#include <linux/netfilter/nf_tables.h>
  10#include <net/dst.h>
  11#include <net/ip6_route.h>
  12#include <net/route.h>
  13#include <net/netfilter/nf_tables.h>
  14#include <net/netfilter/nf_tables_core.h>
  15
  16struct nft_rt {
  17        enum nft_rt_keys        key:8;
  18        u8                      dreg;
  19};
  20
  21static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst)
  22{
  23        u32 minlen = sizeof(struct ipv6hdr), mtu = dst_mtu(skbdst);
  24        const struct sk_buff *skb = pkt->skb;
  25        struct dst_entry *dst = NULL;
  26        struct flowi fl;
  27
  28        memset(&fl, 0, sizeof(fl));
  29
  30        switch (nft_pf(pkt)) {
  31        case NFPROTO_IPV4:
  32                fl.u.ip4.daddr = ip_hdr(skb)->saddr;
  33                minlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
  34                break;
  35        case NFPROTO_IPV6:
  36                fl.u.ip6.daddr = ipv6_hdr(skb)->saddr;
  37                minlen = sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
  38                break;
  39        }
  40
  41        nf_route(nft_net(pkt), &dst, &fl, false, nft_pf(pkt));
  42        if (dst) {
  43                mtu = min(mtu, dst_mtu(dst));
  44                dst_release(dst);
  45        }
  46
  47        if (mtu <= minlen || mtu > 0xffff)
  48                return TCP_MSS_DEFAULT;
  49
  50        return mtu - minlen;
  51}
  52
  53void nft_rt_get_eval(const struct nft_expr *expr,
  54                     struct nft_regs *regs,
  55                     const struct nft_pktinfo *pkt)
  56{
  57        const struct nft_rt *priv = nft_expr_priv(expr);
  58        const struct sk_buff *skb = pkt->skb;
  59        u32 *dest = &regs->data[priv->dreg];
  60        const struct dst_entry *dst;
  61
  62        dst = skb_dst(skb);
  63        if (!dst)
  64                goto err;
  65
  66        switch (priv->key) {
  67#ifdef CONFIG_IP_ROUTE_CLASSID
  68        case NFT_RT_CLASSID:
  69                *dest = dst->tclassid;
  70                break;
  71#endif
  72        case NFT_RT_NEXTHOP4:
  73                if (nft_pf(pkt) != NFPROTO_IPV4)
  74                        goto err;
  75
  76                *dest = (__force u32)rt_nexthop((const struct rtable *)dst,
  77                                                ip_hdr(skb)->daddr);
  78                break;
  79        case NFT_RT_NEXTHOP6:
  80                if (nft_pf(pkt) != NFPROTO_IPV6)
  81                        goto err;
  82
  83                memcpy(dest, rt6_nexthop((struct rt6_info *)dst,
  84                                         &ipv6_hdr(skb)->daddr),
  85                       sizeof(struct in6_addr));
  86                break;
  87        case NFT_RT_TCPMSS:
  88                nft_reg_store16(dest, get_tcpmss(pkt, dst));
  89                break;
  90#ifdef CONFIG_XFRM
  91        case NFT_RT_XFRM:
  92                nft_reg_store8(dest, !!dst->xfrm);
  93                break;
  94#endif
  95        default:
  96                WARN_ON(1);
  97                goto err;
  98        }
  99        return;
 100
 101err:
 102        regs->verdict.code = NFT_BREAK;
 103}
 104
 105static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = {
 106        [NFTA_RT_DREG]          = { .type = NLA_U32 },
 107        [NFTA_RT_KEY]           = { .type = NLA_U32 },
 108};
 109
 110static int nft_rt_get_init(const struct nft_ctx *ctx,
 111                           const struct nft_expr *expr,
 112                           const struct nlattr * const tb[])
 113{
 114        struct nft_rt *priv = nft_expr_priv(expr);
 115        unsigned int len;
 116
 117        if (tb[NFTA_RT_KEY] == NULL ||
 118            tb[NFTA_RT_DREG] == NULL)
 119                return -EINVAL;
 120
 121        priv->key = ntohl(nla_get_be32(tb[NFTA_RT_KEY]));
 122        switch (priv->key) {
 123#ifdef CONFIG_IP_ROUTE_CLASSID
 124        case NFT_RT_CLASSID:
 125#endif
 126        case NFT_RT_NEXTHOP4:
 127                len = sizeof(u32);
 128                break;
 129        case NFT_RT_NEXTHOP6:
 130                len = sizeof(struct in6_addr);
 131                break;
 132        case NFT_RT_TCPMSS:
 133                len = sizeof(u16);
 134                break;
 135#ifdef CONFIG_XFRM
 136        case NFT_RT_XFRM:
 137                len = sizeof(u8);
 138                break;
 139#endif
 140        default:
 141                return -EOPNOTSUPP;
 142        }
 143
 144        return nft_parse_register_store(ctx, tb[NFTA_RT_DREG], &priv->dreg,
 145                                        NULL, NFT_DATA_VALUE, len);
 146}
 147
 148static int nft_rt_get_dump(struct sk_buff *skb,
 149                           const struct nft_expr *expr)
 150{
 151        const struct nft_rt *priv = nft_expr_priv(expr);
 152
 153        if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key)))
 154                goto nla_put_failure;
 155        if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg))
 156                goto nla_put_failure;
 157        return 0;
 158
 159nla_put_failure:
 160        return -1;
 161}
 162
 163static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 164                           const struct nft_data **data)
 165{
 166        const struct nft_rt *priv = nft_expr_priv(expr);
 167        unsigned int hooks;
 168
 169        switch (priv->key) {
 170        case NFT_RT_NEXTHOP4:
 171        case NFT_RT_NEXTHOP6:
 172        case NFT_RT_CLASSID:
 173        case NFT_RT_XFRM:
 174                return 0;
 175        case NFT_RT_TCPMSS:
 176                hooks = (1 << NF_INET_FORWARD) |
 177                        (1 << NF_INET_LOCAL_OUT) |
 178                        (1 << NF_INET_POST_ROUTING);
 179                break;
 180        default:
 181                return -EINVAL;
 182        }
 183
 184        return nft_chain_validate_hooks(ctx->chain, hooks);
 185}
 186
 187static const struct nft_expr_ops nft_rt_get_ops = {
 188        .type           = &nft_rt_type,
 189        .size           = NFT_EXPR_SIZE(sizeof(struct nft_rt)),
 190        .eval           = nft_rt_get_eval,
 191        .init           = nft_rt_get_init,
 192        .dump           = nft_rt_get_dump,
 193        .validate       = nft_rt_validate,
 194};
 195
 196struct nft_expr_type nft_rt_type __read_mostly = {
 197        .name           = "rt",
 198        .ops            = &nft_rt_get_ops,
 199        .policy         = nft_rt_policy,
 200        .maxattr        = NFTA_RT_MAX,
 201        .owner          = THIS_MODULE,
 202};
 203