xref: /linux/net/netfilter/nft_rt.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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 = &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 
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