nft_rt.c (4517B)
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 = ®s->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 .reduce = NFT_REDUCE_READONLY, 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};