linux/net/netfilter/nft_rt.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   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/dst.h>
  16#include <net/ip6_route.h>
  17#include <net/route.h>
  18#include <net/netfilter/nf_tables.h>
  19#include <net/netfilter/nf_tables_core.h>
  20
  21struct nft_rt {
  22        enum nft_rt_keys        key:8;
  23        enum nft_registers      dreg:8;
  24};
  25
  26static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst)
  27{
  28        u32 minlen = sizeof(struct ipv6hdr), mtu = dst_mtu(skbdst);
  29        const struct sk_buff *skb = pkt->skb;
  30        struct dst_entry *dst = NULL;
  31        struct flowi fl;
  32
  33        memset(&fl, 0, sizeof(fl));
  34
  35        switch (nft_pf(pkt)) {
  36        case NFPROTO_IPV4:
  37                fl.u.ip4.daddr = ip_hdr(skb)->saddr;
  38                minlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
  39                break;
  40        case NFPROTO_IPV6:
  41                fl.u.ip6.daddr = ipv6_hdr(skb)->saddr;
  42                minlen = sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
  43                break;
  44        }
  45
  46        nf_route(nft_net(pkt), &dst, &fl, false, nft_pf(pkt));
  47        if (dst) {
  48                mtu = min(mtu, dst_mtu(dst));
  49                dst_release(dst);
  50        }
  51
  52        if (mtu <= minlen || mtu > 0xffff)
  53                return TCP_MSS_DEFAULT;
  54
  55        return mtu - minlen;
  56}
  57
  58static void nft_rt_get_eval(const struct nft_expr *expr,
  59                            struct nft_regs *regs,
  60                            const struct nft_pktinfo *pkt)
  61{
  62        const struct nft_rt *priv = nft_expr_priv(expr);
  63        const struct sk_buff *skb = pkt->skb;
  64        u32 *dest = &regs->data[priv->dreg];
  65        const struct dst_entry *dst;
  66
  67        dst = skb_dst(skb);
  68        if (!dst)
  69                goto err;
  70
  71        switch (priv->key) {
  72#ifdef CONFIG_IP_ROUTE_CLASSID
  73        case NFT_RT_CLASSID:
  74                *dest = dst->tclassid;
  75                break;
  76#endif
  77        case NFT_RT_NEXTHOP4:
  78                if (nft_pf(pkt) != NFPROTO_IPV4)
  79                        goto err;
  80
  81                *dest = (__force u32)rt_nexthop((const struct rtable *)dst,
  82                                                ip_hdr(skb)->daddr);
  83                break;
  84        case NFT_RT_NEXTHOP6:
  85                if (nft_pf(pkt) != NFPROTO_IPV6)
  86                        goto err;
  87
  88                memcpy(dest, rt6_nexthop((struct rt6_info *)dst,
  89                                         &ipv6_hdr(skb)->daddr),
  90                       sizeof(struct in6_addr));
  91                break;
  92        case NFT_RT_TCPMSS:
  93                nft_reg_store16(dest, get_tcpmss(pkt, dst));
  94                break;
  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        default:
 136                return -EOPNOTSUPP;
 137        }
 138
 139        priv->dreg = nft_parse_register(tb[NFTA_RT_DREG]);
 140        return nft_validate_register_store(ctx, priv->dreg, NULL,
 141                                           NFT_DATA_VALUE, len);
 142}
 143
 144static int nft_rt_get_dump(struct sk_buff *skb,
 145                           const struct nft_expr *expr)
 146{
 147        const struct nft_rt *priv = nft_expr_priv(expr);
 148
 149        if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key)))
 150                goto nla_put_failure;
 151        if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg))
 152                goto nla_put_failure;
 153        return 0;
 154
 155nla_put_failure:
 156        return -1;
 157}
 158
 159static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
 160                           const struct nft_data **data)
 161{
 162        const struct nft_rt *priv = nft_expr_priv(expr);
 163        unsigned int hooks;
 164
 165        switch (priv->key) {
 166        case NFT_RT_NEXTHOP4:
 167        case NFT_RT_NEXTHOP6:
 168        case NFT_RT_CLASSID:
 169                return 0;
 170        case NFT_RT_TCPMSS:
 171                hooks = (1 << NF_INET_FORWARD) |
 172                        (1 << NF_INET_LOCAL_OUT) |
 173                        (1 << NF_INET_POST_ROUTING);
 174                break;
 175        default:
 176                return -EINVAL;
 177        }
 178
 179        return nft_chain_validate_hooks(ctx->chain, hooks);
 180}
 181
 182static struct nft_expr_type nft_rt_type;
 183static const struct nft_expr_ops nft_rt_get_ops = {
 184        .type           = &nft_rt_type,
 185        .size           = NFT_EXPR_SIZE(sizeof(struct nft_rt)),
 186        .eval           = nft_rt_get_eval,
 187        .init           = nft_rt_get_init,
 188        .dump           = nft_rt_get_dump,
 189        .validate       = nft_rt_validate,
 190};
 191
 192static struct nft_expr_type nft_rt_type __read_mostly = {
 193        .name           = "rt",
 194        .ops            = &nft_rt_get_ops,
 195        .policy         = nft_rt_policy,
 196        .maxattr        = NFTA_RT_MAX,
 197        .owner          = THIS_MODULE,
 198};
 199
 200static int __init nft_rt_module_init(void)
 201{
 202        return nft_register_expr(&nft_rt_type);
 203}
 204
 205static void __exit nft_rt_module_exit(void)
 206{
 207        nft_unregister_expr(&nft_rt_type);
 208}
 209
 210module_init(nft_rt_module_init);
 211module_exit(nft_rt_module_exit);
 212
 213MODULE_LICENSE("GPL");
 214MODULE_AUTHOR("Anders K. Pedersen <akp@cohaesio.com>");
 215MODULE_ALIAS_NFT_EXPR("rt");
 216