projects/linux·blob master · refs

208 lines · 4.6 KiB

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