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        enum nft_registers      dreg:8;
  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        priv->dreg = nft_parse_register(tb[NFTA_RT_DREG]);
 145        return nft_validate_register_store(ctx, priv->dreg, NULL,
 146                                           NFT_DATA_VALUE, len);
 147}
 148
 149static int nft_rt_get_dump(struct sk_buff *skb,
 150                           const struct nft_expr *expr)
 151{
 152        const struct nft_rt *priv = nft_expr_priv(expr);
 153
 154        if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key)))
 155                goto nla_put_failure;
 156        if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg))
 157                goto nla_put_failure;
 158        return 0;
 159
 160nla_put_failure:
 161        return -1;
 162}
 163
 164static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 165                           const struct nft_data **data)
 166{
 167        const struct nft_rt *priv = nft_expr_priv(expr);
 168        unsigned int hooks;
 169
 170        switch (priv->key) {
 171        case NFT_RT_NEXTHOP4:
 172        case NFT_RT_NEXTHOP6:
 173        case NFT_RT_CLASSID:
 174        case NFT_RT_XFRM:
 175                return 0;
 176        case NFT_RT_TCPMSS:
 177                hooks = (1 << NF_INET_FORWARD) |
 178                        (1 << NF_INET_LOCAL_OUT) |
 179                        (1 << NF_INET_POST_ROUTING);
 180                break;
 181        default:
 182                return -EINVAL;
 183        }
 184
 185        return nft_chain_validate_hooks(ctx->chain, hooks);
 186}
 187
 188static const struct nft_expr_ops nft_rt_get_ops = {
 189        .type           = &nft_rt_type,
 190        .size           = NFT_EXPR_SIZE(sizeof(struct nft_rt)),
 191        .eval           = nft_rt_get_eval,
 192        .init           = nft_rt_get_init,
 193        .dump           = nft_rt_get_dump,
 194        .validate       = nft_rt_validate,
 195};
 196
 197struct nft_expr_type nft_rt_type __read_mostly = {
 198        .name           = "rt",
 199        .ops            = &nft_rt_get_ops,
 200        .policy         = nft_rt_policy,
 201        .maxattr        = NFTA_RT_MAX,
 202        .owner          = THIS_MODULE,
 203};
 204