Blame view

net/sched/act_nat.c 7.71 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
2
3
4
5
  /*
   * Stateless NAT actions
   *
   * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   */
  
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/netfilter.h>
  #include <linux/rtnetlink.h>
  #include <linux/skbuff.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/string.h>
  #include <linux/tc_act/tc_nat.h>
  #include <net/act_api.h>
1e45d043a   Davide Caratti   net/sched: act_na...
20
  #include <net/pkt_cls.h>
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
21
22
23
24
25
26
  #include <net/icmp.h>
  #include <net/ip.h>
  #include <net/netlink.h>
  #include <net/tc_act/tc_nat.h>
  #include <net/tcp.h>
  #include <net/udp.h>
c7d03a00b   Alexey Dobriyan   netns: make struc...
27
  static unsigned int nat_net_id;
a85a970af   WANG Cong   net_sched: move t...
28
  static struct tc_action_ops act_nat_ops;
ddf97ccdd   WANG Cong   net_sched: add ne...
29

53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
30
31
32
  static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
  	[TCA_NAT_PARMS]	= { .len = sizeof(struct tc_nat) },
  };
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
33
  static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
589dad6d7   Alexander Aring   net: sched: act: ...
34
  			struct tc_action **a, int ovr, int bind,
85d0966fa   Davide Caratti   net/sched: prepar...
35
  			bool rtnl_held,	struct tcf_proto *tp,
abbb0d336   Vlad Buslov   net: sched: exten...
36
  			u32 flags, struct netlink_ext_ack *extack)
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
37
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
38
  	struct tc_action_net *tn = net_generic(net, nat_net_id);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
39
  	struct nlattr *tb[TCA_NAT_MAX + 1];
1e45d043a   Davide Caratti   net/sched: act_na...
40
  	struct tcf_chain *goto_ch = NULL;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
41
  	struct tc_nat *parm;
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
42
  	int ret = 0, err;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
43
  	struct tcf_nat *p;
7be8ef2cd   Dmytro Linkin   net: sched: use t...
44
  	u32 index;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
45

cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
46
  	if (nla == NULL)
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
47
  		return -EINVAL;
8cb081746   Johannes Berg   netlink: make val...
48
49
  	err = nla_parse_nested_deprecated(tb, TCA_NAT_MAX, nla, nat_policy,
  					  NULL);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
50
51
  	if (err < 0)
  		return err;
53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
52
  	if (tb[TCA_NAT_PARMS] == NULL)
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
53
  		return -EINVAL;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
54
  	parm = nla_data(tb[TCA_NAT_PARMS]);
7be8ef2cd   Dmytro Linkin   net: sched: use t...
55
56
  	index = parm->index;
  	err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
57
  	if (!err) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
58
  		ret = tcf_idr_create(tn, index, est, a,
e38226786   Vlad Buslov   net: sched: updat...
59
  				     &act_nat_ops, bind, false, 0);
0190c1d45   Vlad Buslov   net: sched: atomi...
60
  		if (ret) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
61
  			tcf_idr_cleanup(tn, index);
86062033f   WANG Cong   net_sched: act: h...
62
  			return ret;
0190c1d45   Vlad Buslov   net: sched: atomi...
63
  		}
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
64
  		ret = ACT_P_CREATED;
0190c1d45   Vlad Buslov   net: sched: atomi...
65
  	} else if (err > 0) {
1a29321ed   Jamal Hadi Salim   net_sched: act: D...
66
67
  		if (bind)
  			return 0;
4e8ddd7f1   Vlad Buslov   net: sched: don't...
68
69
  		if (!ovr) {
  			tcf_idr_release(*a, bind);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
70
  			return -EEXIST;
4e8ddd7f1   Vlad Buslov   net: sched: don't...
71
  		}
0190c1d45   Vlad Buslov   net: sched: atomi...
72
73
  	} else {
  		return err;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
74
  	}
1e45d043a   Davide Caratti   net/sched: act_na...
75
76
77
  	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
  	if (err < 0)
  		goto release_idr;
a85a970af   WANG Cong   net_sched: move t...
78
  	p = to_tcf_nat(*a);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
79
80
81
82
83
84
  
  	spin_lock_bh(&p->tcf_lock);
  	p->old_addr = parm->old_addr;
  	p->new_addr = parm->new_addr;
  	p->mask = parm->mask;
  	p->flags = parm->flags;
1e45d043a   Davide Caratti   net/sched: act_na...
85
  	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
86
  	spin_unlock_bh(&p->tcf_lock);
1e45d043a   Davide Caratti   net/sched: act_na...
87
88
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
89

b42199523   Herbert Xu   [PKT_SCHED]: Add ...
90
  	return ret;
1e45d043a   Davide Caratti   net/sched: act_na...
91
92
93
  release_idr:
  	tcf_idr_release(*a, bind);
  	return err;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
94
  }
0390514fe   Jamal Hadi Salim   net: sched: act_n...
95
96
  static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a,
  		       struct tcf_result *res)
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
97
  {
a85a970af   WANG Cong   net_sched: move t...
98
  	struct tcf_nat *p = to_tcf_nat(a);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
99
100
101
102
103
104
105
106
  	struct iphdr *iph;
  	__be32 old_addr;
  	__be32 new_addr;
  	__be32 mask;
  	__be32 addr;
  	int egress;
  	int action;
  	int ihl;
36d12690a   Changli Gao   act_nat: fix on t...
107
  	int noff;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
108
109
  
  	spin_lock(&p->tcf_lock);
9c4a4e488   Jamal Hadi Salim   net sched: action...
110
  	tcf_lastuse_update(&p->tcf_tm);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
111
112
113
114
115
  	old_addr = p->old_addr;
  	new_addr = p->new_addr;
  	mask = p->mask;
  	egress = p->flags & TCA_NAT_FLAG_EGRESS;
  	action = p->tcf_action;
bfe0d0298   Eric Dumazet   net_sched: factor...
116
  	bstats_update(&p->tcf_bstats, skb);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
117
118
119
120
121
  
  	spin_unlock(&p->tcf_lock);
  
  	if (unlikely(action == TC_ACT_SHOT))
  		goto drop;
36d12690a   Changli Gao   act_nat: fix on t...
122
123
  	noff = skb_network_offset(skb);
  	if (!pskb_may_pull(skb, sizeof(*iph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
124
125
126
127
128
129
130
131
132
133
  		goto drop;
  
  	iph = ip_hdr(skb);
  
  	if (egress)
  		addr = iph->saddr;
  	else
  		addr = iph->daddr;
  
  	if (!((old_addr ^ addr) & mask)) {
3697649ff   Daniel Borkmann   bpf: try harder o...
134
  		if (skb_try_make_writable(skb, sizeof(*iph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
135
136
137
138
139
140
141
142
143
144
145
  			goto drop;
  
  		new_addr &= mask;
  		new_addr |= addr & ~mask;
  
  		/* Rewrite IP header */
  		iph = ip_hdr(skb);
  		if (egress)
  			iph->saddr = new_addr;
  		else
  			iph->daddr = new_addr;
be0ea7d5d   Patrick McHardy   [NETFILTER]: Conv...
146
  		csum_replace4(&iph->check, addr, new_addr);
33c29dde7   Changli Gao   act_nat: fix the ...
147
148
149
  	} else if ((iph->frag_off & htons(IP_OFFSET)) ||
  		   iph->protocol != IPPROTO_ICMP) {
  		goto out;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
150
151
152
153
154
155
156
157
158
  	}
  
  	ihl = iph->ihl * 4;
  
  	/* It would be nice to share code with stateful NAT. */
  	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
  	case IPPROTO_TCP:
  	{
  		struct tcphdr *tcph;
36d12690a   Changli Gao   act_nat: fix on t...
159
  		if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
3697649ff   Daniel Borkmann   bpf: try harder o...
160
  		    skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
161
162
163
  			goto drop;
  
  		tcph = (void *)(skb_network_header(skb) + ihl);
4b048d6d9   Tom Herbert   net: Change pseud...
164
165
  		inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr,
  					 true);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
166
167
168
169
170
  		break;
  	}
  	case IPPROTO_UDP:
  	{
  		struct udphdr *udph;
36d12690a   Changli Gao   act_nat: fix on t...
171
  		if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
3697649ff   Daniel Borkmann   bpf: try harder o...
172
  		    skb_try_make_writable(skb, ihl + sizeof(*udph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
173
174
175
176
  			goto drop;
  
  		udph = (void *)(skb_network_header(skb) + ihl);
  		if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
be0ea7d5d   Patrick McHardy   [NETFILTER]: Conv...
177
  			inet_proto_csum_replace4(&udph->check, skb, addr,
4b048d6d9   Tom Herbert   net: Change pseud...
178
  						 new_addr, true);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
179
180
181
182
183
184
185
186
  			if (!udph->check)
  				udph->check = CSUM_MANGLED_0;
  		}
  		break;
  	}
  	case IPPROTO_ICMP:
  	{
  		struct icmphdr *icmph;
36d12690a   Changli Gao   act_nat: fix on t...
187
  		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
188
189
190
  			goto drop;
  
  		icmph = (void *)(skb_network_header(skb) + ihl);
54074f1db   Matteo Croce   icmp: remove dupl...
191
  		if (!icmp_is_err(icmph->type))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
192
  			break;
36d12690a   Changli Gao   act_nat: fix on t...
193
194
  		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
  					noff))
70c2efa5a   Changli Gao   act_nat: not all ...
195
  			goto drop;
072d79a31   Changli Gao   act_nat: fix wild...
196
  		icmph = (void *)(skb_network_header(skb) + ihl);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
197
198
199
200
201
202
203
204
  		iph = (void *)(icmph + 1);
  		if (egress)
  			addr = iph->daddr;
  		else
  			addr = iph->saddr;
  
  		if ((old_addr ^ addr) & mask)
  			break;
3697649ff   Daniel Borkmann   bpf: try harder o...
205
206
  		if (skb_try_make_writable(skb, ihl + sizeof(*icmph) +
  					  sizeof(*iph) + noff))
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
207
208
209
210
211
212
213
214
215
216
217
218
219
  			goto drop;
  
  		icmph = (void *)(skb_network_header(skb) + ihl);
  		iph = (void *)(icmph + 1);
  
  		new_addr &= mask;
  		new_addr |= addr & ~mask;
  
  		/* XXX Fix up the inner checksums. */
  		if (egress)
  			iph->daddr = new_addr;
  		else
  			iph->saddr = new_addr;
be0ea7d5d   Patrick McHardy   [NETFILTER]: Conv...
220
  		inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
4b048d6d9   Tom Herbert   net: Change pseud...
221
  					 false);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
222
223
224
225
226
  		break;
  	}
  	default:
  		break;
  	}
33c29dde7   Changli Gao   act_nat: fix the ...
227
  out:
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
228
229
230
231
232
233
234
235
236
237
238
239
240
  	return action;
  
  drop:
  	spin_lock(&p->tcf_lock);
  	p->tcf_qstats.drops++;
  	spin_unlock(&p->tcf_lock);
  	return TC_ACT_SHOT;
  }
  
  static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
  			int bind, int ref)
  {
  	unsigned char *b = skb_tail_pointer(skb);
a85a970af   WANG Cong   net_sched: move t...
241
  	struct tcf_nat *p = to_tcf_nat(a);
1c40be12f   Eric Dumazet   net sched: fix so...
242
  	struct tc_nat opt = {
1c40be12f   Eric Dumazet   net sched: fix so...
243
  		.index    = p->tcf_index,
036bb4432   Vlad Buslov   net: sched: chang...
244
245
  		.refcnt   = refcount_read(&p->tcf_refcnt) - ref,
  		.bindcnt  = atomic_read(&p->tcf_bindcnt) - bind,
1c40be12f   Eric Dumazet   net sched: fix so...
246
  	};
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
247
  	struct tcf_t t;
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
248

f20a4d011   Vlad Buslov   net: sched: act_n...
249
250
251
252
253
254
  	spin_lock_bh(&p->tcf_lock);
  	opt.old_addr = p->old_addr;
  	opt.new_addr = p->new_addr;
  	opt.mask = p->mask;
  	opt.flags = p->flags;
  	opt.action = p->tcf_action;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
255
256
  	if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt))
  		goto nla_put_failure;
48d8ee169   Jamal Hadi Salim   net sched actions...
257
258
  
  	tcf_tm_dump(&t, &p->tcf_tm);
9854518ea   Nicolas Dichtel   sched: align nlat...
259
  	if (nla_put_64bit(skb, TCA_NAT_TM, sizeof(t), &t, TCA_NAT_PAD))
1b34ec43c   David S. Miller   pkt_sched: Stop u...
260
  		goto nla_put_failure;
f20a4d011   Vlad Buslov   net: sched: act_n...
261
  	spin_unlock_bh(&p->tcf_lock);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
262

b42199523   Herbert Xu   [PKT_SCHED]: Add ...
263
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
264
  nla_put_failure:
f20a4d011   Vlad Buslov   net: sched: act_n...
265
  	spin_unlock_bh(&p->tcf_lock);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
266
  	nlmsg_trim(skb, b);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
267
268
  	return -1;
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
269
270
  static int tcf_nat_walker(struct net *net, struct sk_buff *skb,
  			  struct netlink_callback *cb, int type,
417801055   Alexander Aring   net: sched: act: ...
271
272
  			  const struct tc_action_ops *ops,
  			  struct netlink_ext_ack *extack)
ddf97ccdd   WANG Cong   net_sched: add ne...
273
274
  {
  	struct tc_action_net *tn = net_generic(net, nat_net_id);
b36201455   Alexander Aring   net: sched: act: ...
275
  	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
ddf97ccdd   WANG Cong   net_sched: add ne...
276
  }
f061b48c1   Cong Wang   Revert "net: sche...
277
  static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index)
ddf97ccdd   WANG Cong   net_sched: add ne...
278
279
  {
  	struct tc_action_net *tn = net_generic(net, nat_net_id);
65a206c01   Chris Mi   net/sched: Change...
280
  	return tcf_idr_search(tn, a, index);
ddf97ccdd   WANG Cong   net_sched: add ne...
281
  }
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
282
283
  static struct tc_action_ops act_nat_ops = {
  	.kind		=	"nat",
eddd2cf19   Eli Cohen   net: Change TCA_A...
284
  	.id		=	TCA_ID_NAT,
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
285
  	.owner		=	THIS_MODULE,
0390514fe   Jamal Hadi Salim   net: sched: act_n...
286
  	.act		=	tcf_nat_act,
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
287
  	.dump		=	tcf_nat_dump,
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
288
  	.init		=	tcf_nat_init,
ddf97ccdd   WANG Cong   net_sched: add ne...
289
290
  	.walk		=	tcf_nat_walker,
  	.lookup		=	tcf_nat_search,
a85a970af   WANG Cong   net_sched: move t...
291
  	.size		=	sizeof(struct tcf_nat),
ddf97ccdd   WANG Cong   net_sched: add ne...
292
293
294
295
296
  };
  
  static __net_init int nat_init_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, nat_net_id);
981471bd3   Cong Wang   net_sched: fix a ...
297
  	return tc_action_net_init(net, tn, &act_nat_ops);
ddf97ccdd   WANG Cong   net_sched: add ne...
298
  }
039af9c66   Cong Wang   net_sched: switch...
299
  static void __net_exit nat_exit_net(struct list_head *net_list)
ddf97ccdd   WANG Cong   net_sched: add ne...
300
  {
039af9c66   Cong Wang   net_sched: switch...
301
  	tc_action_net_exit(net_list, nat_net_id);
ddf97ccdd   WANG Cong   net_sched: add ne...
302
303
304
305
  }
  
  static struct pernet_operations nat_net_ops = {
  	.init = nat_init_net,
039af9c66   Cong Wang   net_sched: switch...
306
  	.exit_batch = nat_exit_net,
ddf97ccdd   WANG Cong   net_sched: add ne...
307
308
  	.id   = &nat_net_id,
  	.size = sizeof(struct tc_action_net),
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
309
310
311
312
313
314
315
  };
  
  MODULE_DESCRIPTION("Stateless NAT actions");
  MODULE_LICENSE("GPL");
  
  static int __init nat_init_module(void)
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
316
  	return tcf_register_action(&act_nat_ops, &nat_net_ops);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
317
318
319
320
  }
  
  static void __exit nat_cleanup_module(void)
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
321
  	tcf_unregister_action(&act_nat_ops, &nat_net_ops);
b42199523   Herbert Xu   [PKT_SCHED]: Add ...
322
323
324
325
  }
  
  module_init(nat_init_module);
  module_exit(nat_cleanup_module);