Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com>
0004  */
0005 
0006 #include <linux/kernel.h>
0007 #include <linux/netlink.h>
0008 #include <linux/netfilter.h>
0009 #include <linux/netfilter/nf_tables.h>
0010 #include <net/dst.h>
0011 #include <net/ip6_route.h>
0012 #include <net/route.h>
0013 #include <net/netfilter/nf_tables.h>
0014 #include <net/netfilter/nf_tables_core.h>
0015 
0016 struct nft_rt {
0017     enum nft_rt_keys    key:8;
0018     u8          dreg;
0019 };
0020 
0021 static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst)
0022 {
0023     u32 minlen = sizeof(struct ipv6hdr), mtu = dst_mtu(skbdst);
0024     const struct sk_buff *skb = pkt->skb;
0025     struct dst_entry *dst = NULL;
0026     struct flowi fl;
0027 
0028     memset(&fl, 0, sizeof(fl));
0029 
0030     switch (nft_pf(pkt)) {
0031     case NFPROTO_IPV4:
0032         fl.u.ip4.daddr = ip_hdr(skb)->saddr;
0033         minlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
0034         break;
0035     case NFPROTO_IPV6:
0036         fl.u.ip6.daddr = ipv6_hdr(skb)->saddr;
0037         minlen = sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
0038         break;
0039     }
0040 
0041     nf_route(nft_net(pkt), &dst, &fl, false, nft_pf(pkt));
0042     if (dst) {
0043         mtu = min(mtu, dst_mtu(dst));
0044         dst_release(dst);
0045     }
0046 
0047     if (mtu <= minlen || mtu > 0xffff)
0048         return TCP_MSS_DEFAULT;
0049 
0050     return mtu - minlen;
0051 }
0052 
0053 void nft_rt_get_eval(const struct nft_expr *expr,
0054              struct nft_regs *regs,
0055              const struct nft_pktinfo *pkt)
0056 {
0057     const struct nft_rt *priv = nft_expr_priv(expr);
0058     const struct sk_buff *skb = pkt->skb;
0059     u32 *dest = &regs->data[priv->dreg];
0060     const struct dst_entry *dst;
0061 
0062     dst = skb_dst(skb);
0063     if (!dst)
0064         goto err;
0065 
0066     switch (priv->key) {
0067 #ifdef CONFIG_IP_ROUTE_CLASSID
0068     case NFT_RT_CLASSID:
0069         *dest = dst->tclassid;
0070         break;
0071 #endif
0072     case NFT_RT_NEXTHOP4:
0073         if (nft_pf(pkt) != NFPROTO_IPV4)
0074             goto err;
0075 
0076         *dest = (__force u32)rt_nexthop((const struct rtable *)dst,
0077                         ip_hdr(skb)->daddr);
0078         break;
0079     case NFT_RT_NEXTHOP6:
0080         if (nft_pf(pkt) != NFPROTO_IPV6)
0081             goto err;
0082 
0083         memcpy(dest, rt6_nexthop((struct rt6_info *)dst,
0084                      &ipv6_hdr(skb)->daddr),
0085                sizeof(struct in6_addr));
0086         break;
0087     case NFT_RT_TCPMSS:
0088         nft_reg_store16(dest, get_tcpmss(pkt, dst));
0089         break;
0090 #ifdef CONFIG_XFRM
0091     case NFT_RT_XFRM:
0092         nft_reg_store8(dest, !!dst->xfrm);
0093         break;
0094 #endif
0095     default:
0096         WARN_ON(1);
0097         goto err;
0098     }
0099     return;
0100 
0101 err:
0102     regs->verdict.code = NFT_BREAK;
0103 }
0104 
0105 static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = {
0106     [NFTA_RT_DREG]      = { .type = NLA_U32 },
0107     [NFTA_RT_KEY]       = { .type = NLA_U32 },
0108 };
0109 
0110 static int nft_rt_get_init(const struct nft_ctx *ctx,
0111                const struct nft_expr *expr,
0112                const struct nlattr * const tb[])
0113 {
0114     struct nft_rt *priv = nft_expr_priv(expr);
0115     unsigned int len;
0116 
0117     if (tb[NFTA_RT_KEY] == NULL ||
0118         tb[NFTA_RT_DREG] == NULL)
0119         return -EINVAL;
0120 
0121     priv->key = ntohl(nla_get_be32(tb[NFTA_RT_KEY]));
0122     switch (priv->key) {
0123 #ifdef CONFIG_IP_ROUTE_CLASSID
0124     case NFT_RT_CLASSID:
0125 #endif
0126     case NFT_RT_NEXTHOP4:
0127         len = sizeof(u32);
0128         break;
0129     case NFT_RT_NEXTHOP6:
0130         len = sizeof(struct in6_addr);
0131         break;
0132     case NFT_RT_TCPMSS:
0133         len = sizeof(u16);
0134         break;
0135 #ifdef CONFIG_XFRM
0136     case NFT_RT_XFRM:
0137         len = sizeof(u8);
0138         break;
0139 #endif
0140     default:
0141         return -EOPNOTSUPP;
0142     }
0143 
0144     return nft_parse_register_store(ctx, tb[NFTA_RT_DREG], &priv->dreg,
0145                     NULL, NFT_DATA_VALUE, len);
0146 }
0147 
0148 static int nft_rt_get_dump(struct sk_buff *skb,
0149                const struct nft_expr *expr)
0150 {
0151     const struct nft_rt *priv = nft_expr_priv(expr);
0152 
0153     if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key)))
0154         goto nla_put_failure;
0155     if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg))
0156         goto nla_put_failure;
0157     return 0;
0158 
0159 nla_put_failure:
0160     return -1;
0161 }
0162 
0163 static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
0164                const struct nft_data **data)
0165 {
0166     const struct nft_rt *priv = nft_expr_priv(expr);
0167     unsigned int hooks;
0168 
0169     switch (priv->key) {
0170     case NFT_RT_NEXTHOP4:
0171     case NFT_RT_NEXTHOP6:
0172     case NFT_RT_CLASSID:
0173     case NFT_RT_XFRM:
0174         return 0;
0175     case NFT_RT_TCPMSS:
0176         hooks = (1 << NF_INET_FORWARD) |
0177             (1 << NF_INET_LOCAL_OUT) |
0178             (1 << NF_INET_POST_ROUTING);
0179         break;
0180     default:
0181         return -EINVAL;
0182     }
0183 
0184     return nft_chain_validate_hooks(ctx->chain, hooks);
0185 }
0186 
0187 static const struct nft_expr_ops nft_rt_get_ops = {
0188     .type       = &nft_rt_type,
0189     .size       = NFT_EXPR_SIZE(sizeof(struct nft_rt)),
0190     .eval       = nft_rt_get_eval,
0191     .init       = nft_rt_get_init,
0192     .dump       = nft_rt_get_dump,
0193     .validate   = nft_rt_validate,
0194     .reduce     = NFT_REDUCE_READONLY,
0195 };
0196 
0197 struct nft_expr_type nft_rt_type __read_mostly = {
0198     .name       = "rt",
0199     .ops        = &nft_rt_get_ops,
0200     .policy     = nft_rt_policy,
0201     .maxattr    = NFTA_RT_MAX,
0202     .owner      = THIS_MODULE,
0203 };