Blame view

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