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/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
17 struct nft_rt {
18 enum nft_rt_keys key:8;
19 u8 dreg;
20 };
21
get_tcpmss(const struct nft_pktinfo * pkt,const struct dst_entry * skbdst)22 static 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
nft_rt_get_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)54 void 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 = ®s->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
103 err:
104 regs->verdict.code = NFT_BREAK;
105 }
106
107 static 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
nft_rt_get_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])112 static 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
nft_rt_get_dump(struct sk_buff * skb,const struct nft_expr * expr,bool reset)150 static 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
161 nla_put_failure:
162 return -1;
163 }
164
nft_rt_validate(const struct nft_ctx * ctx,const struct nft_expr * expr)165 static 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
193 static 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
202 struct 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 };
209