Blame view
net/sched/act_nat.c
7.71 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
b42199523 [PKT_SCHED]: Add ... |
2 3 4 5 |
/* * Stateless NAT actions * * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> |
b42199523 [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 net/sched: act_na... |
20 |
#include <net/pkt_cls.h> |
b42199523 [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 netns: make struc... |
27 |
static unsigned int nat_net_id; |
a85a970af net_sched: move t... |
28 |
static struct tc_action_ops act_nat_ops; |
ddf97ccdd net_sched: add ne... |
29 |
|
53b2bf3f8 [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 pkt_sched: namesp... |
33 |
static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, |
589dad6d7 net: sched: act: ... |
34 |
struct tc_action **a, int ovr, int bind, |
85d0966fa net/sched: prepar... |
35 |
bool rtnl_held, struct tcf_proto *tp, |
abbb0d336 net: sched: exten... |
36 |
u32 flags, struct netlink_ext_ack *extack) |
b42199523 [PKT_SCHED]: Add ... |
37 |
{ |
ddf97ccdd net_sched: add ne... |
38 |
struct tc_action_net *tn = net_generic(net, nat_net_id); |
7ba699c60 [NET_SCHED]: Conv... |
39 |
struct nlattr *tb[TCA_NAT_MAX + 1]; |
1e45d043a net/sched: act_na... |
40 |
struct tcf_chain *goto_ch = NULL; |
b42199523 [PKT_SCHED]: Add ... |
41 |
struct tc_nat *parm; |
cee63723b [NET_SCHED]: Prop... |
42 |
int ret = 0, err; |
b42199523 [PKT_SCHED]: Add ... |
43 |
struct tcf_nat *p; |
7be8ef2cd net: sched: use t... |
44 |
u32 index; |
b42199523 [PKT_SCHED]: Add ... |
45 |
|
cee63723b [NET_SCHED]: Prop... |
46 |
if (nla == NULL) |
b42199523 [PKT_SCHED]: Add ... |
47 |
return -EINVAL; |
8cb081746 netlink: make val... |
48 49 |
err = nla_parse_nested_deprecated(tb, TCA_NAT_MAX, nla, nat_policy, NULL); |
cee63723b [NET_SCHED]: Prop... |
50 51 |
if (err < 0) return err; |
53b2bf3f8 [NET_SCHED]: Use ... |
52 |
if (tb[TCA_NAT_PARMS] == NULL) |
b42199523 [PKT_SCHED]: Add ... |
53 |
return -EINVAL; |
7ba699c60 [NET_SCHED]: Conv... |
54 |
parm = nla_data(tb[TCA_NAT_PARMS]); |
7be8ef2cd net: sched: use t... |
55 56 |
index = parm->index; err = tcf_idr_check_alloc(tn, &index, a, bind); |
0190c1d45 net: sched: atomi... |
57 |
if (!err) { |
7be8ef2cd net: sched: use t... |
58 |
ret = tcf_idr_create(tn, index, est, a, |
e38226786 net: sched: updat... |
59 |
&act_nat_ops, bind, false, 0); |
0190c1d45 net: sched: atomi... |
60 |
if (ret) { |
7be8ef2cd net: sched: use t... |
61 |
tcf_idr_cleanup(tn, index); |
86062033f net_sched: act: h... |
62 |
return ret; |
0190c1d45 net: sched: atomi... |
63 |
} |
b42199523 [PKT_SCHED]: Add ... |
64 |
ret = ACT_P_CREATED; |
0190c1d45 net: sched: atomi... |
65 |
} else if (err > 0) { |
1a29321ed net_sched: act: D... |
66 67 |
if (bind) return 0; |
4e8ddd7f1 net: sched: don't... |
68 69 |
if (!ovr) { tcf_idr_release(*a, bind); |
b42199523 [PKT_SCHED]: Add ... |
70 |
return -EEXIST; |
4e8ddd7f1 net: sched: don't... |
71 |
} |
0190c1d45 net: sched: atomi... |
72 73 |
} else { return err; |
b42199523 [PKT_SCHED]: Add ... |
74 |
} |
1e45d043a 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 net_sched: move t... |
78 |
p = to_tcf_nat(*a); |
b42199523 [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 net/sched: act_na... |
85 |
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); |
b42199523 [PKT_SCHED]: Add ... |
86 |
spin_unlock_bh(&p->tcf_lock); |
1e45d043a net/sched: act_na... |
87 88 |
if (goto_ch) tcf_chain_put_by_act(goto_ch); |
b42199523 [PKT_SCHED]: Add ... |
89 |
|
b42199523 [PKT_SCHED]: Add ... |
90 |
return ret; |
1e45d043a net/sched: act_na... |
91 92 93 |
release_idr: tcf_idr_release(*a, bind); return err; |
b42199523 [PKT_SCHED]: Add ... |
94 |
} |
0390514fe 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 [PKT_SCHED]: Add ... |
97 |
{ |
a85a970af net_sched: move t... |
98 |
struct tcf_nat *p = to_tcf_nat(a); |
b42199523 [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 act_nat: fix on t... |
107 |
int noff; |
b42199523 [PKT_SCHED]: Add ... |
108 109 |
spin_lock(&p->tcf_lock); |
9c4a4e488 net sched: action... |
110 |
tcf_lastuse_update(&p->tcf_tm); |
b42199523 [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 net_sched: factor... |
116 |
bstats_update(&p->tcf_bstats, skb); |
b42199523 [PKT_SCHED]: Add ... |
117 118 119 120 121 |
spin_unlock(&p->tcf_lock); if (unlikely(action == TC_ACT_SHOT)) goto drop; |
36d12690a act_nat: fix on t... |
122 123 |
noff = skb_network_offset(skb); if (!pskb_may_pull(skb, sizeof(*iph) + noff)) |
b42199523 [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 bpf: try harder o... |
134 |
if (skb_try_make_writable(skb, sizeof(*iph) + noff)) |
b42199523 [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 [NETFILTER]: Conv... |
146 |
csum_replace4(&iph->check, addr, new_addr); |
33c29dde7 act_nat: fix the ... |
147 148 149 |
} else if ((iph->frag_off & htons(IP_OFFSET)) || iph->protocol != IPPROTO_ICMP) { goto out; |
b42199523 [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 act_nat: fix on t... |
159 |
if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) || |
3697649ff bpf: try harder o... |
160 |
skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff)) |
b42199523 [PKT_SCHED]: Add ... |
161 162 163 |
goto drop; tcph = (void *)(skb_network_header(skb) + ihl); |
4b048d6d9 net: Change pseud... |
164 165 |
inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true); |
b42199523 [PKT_SCHED]: Add ... |
166 167 168 169 170 |
break; } case IPPROTO_UDP: { struct udphdr *udph; |
36d12690a act_nat: fix on t... |
171 |
if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) || |
3697649ff bpf: try harder o... |
172 |
skb_try_make_writable(skb, ihl + sizeof(*udph) + noff)) |
b42199523 [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 [NETFILTER]: Conv... |
177 |
inet_proto_csum_replace4(&udph->check, skb, addr, |
4b048d6d9 net: Change pseud... |
178 |
new_addr, true); |
b42199523 [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 act_nat: fix on t... |
187 |
if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff)) |
b42199523 [PKT_SCHED]: Add ... |
188 189 190 |
goto drop; icmph = (void *)(skb_network_header(skb) + ihl); |
54074f1db icmp: remove dupl... |
191 |
if (!icmp_is_err(icmph->type)) |
b42199523 [PKT_SCHED]: Add ... |
192 |
break; |
36d12690a act_nat: fix on t... |
193 194 |
if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) + noff)) |
70c2efa5a act_nat: not all ... |
195 |
goto drop; |
072d79a31 act_nat: fix wild... |
196 |
icmph = (void *)(skb_network_header(skb) + ihl); |
b42199523 [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 bpf: try harder o... |
205 206 |
if (skb_try_make_writable(skb, ihl + sizeof(*icmph) + sizeof(*iph) + noff)) |
b42199523 [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 [NETFILTER]: Conv... |
220 |
inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr, |
4b048d6d9 net: Change pseud... |
221 |
false); |
b42199523 [PKT_SCHED]: Add ... |
222 223 224 225 226 |
break; } default: break; } |
33c29dde7 act_nat: fix the ... |
227 |
out: |
b42199523 [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 net_sched: move t... |
241 |
struct tcf_nat *p = to_tcf_nat(a); |
1c40be12f net sched: fix so... |
242 |
struct tc_nat opt = { |
1c40be12f net sched: fix so... |
243 |
.index = p->tcf_index, |
036bb4432 net: sched: chang... |
244 245 |
.refcnt = refcount_read(&p->tcf_refcnt) - ref, .bindcnt = atomic_read(&p->tcf_bindcnt) - bind, |
1c40be12f net sched: fix so... |
246 |
}; |
b42199523 [PKT_SCHED]: Add ... |
247 |
struct tcf_t t; |
b42199523 [PKT_SCHED]: Add ... |
248 |
|
f20a4d011 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 pkt_sched: Stop u... |
255 256 |
if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt)) goto nla_put_failure; |
48d8ee169 net sched actions... |
257 258 |
tcf_tm_dump(&t, &p->tcf_tm); |
9854518ea sched: align nlat... |
259 |
if (nla_put_64bit(skb, TCA_NAT_TM, sizeof(t), &t, TCA_NAT_PAD)) |
1b34ec43c pkt_sched: Stop u... |
260 |
goto nla_put_failure; |
f20a4d011 net: sched: act_n... |
261 |
spin_unlock_bh(&p->tcf_lock); |
b42199523 [PKT_SCHED]: Add ... |
262 |
|
b42199523 [PKT_SCHED]: Add ... |
263 |
return skb->len; |
7ba699c60 [NET_SCHED]: Conv... |
264 |
nla_put_failure: |
f20a4d011 net: sched: act_n... |
265 |
spin_unlock_bh(&p->tcf_lock); |
b42199523 [PKT_SCHED]: Add ... |
266 |
nlmsg_trim(skb, b); |
b42199523 [PKT_SCHED]: Add ... |
267 268 |
return -1; } |
ddf97ccdd 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 net: sched: act: ... |
271 272 |
const struct tc_action_ops *ops, struct netlink_ext_ack *extack) |
ddf97ccdd net_sched: add ne... |
273 274 |
{ struct tc_action_net *tn = net_generic(net, nat_net_id); |
b36201455 net: sched: act: ... |
275 |
return tcf_generic_walker(tn, skb, cb, type, ops, extack); |
ddf97ccdd net_sched: add ne... |
276 |
} |
f061b48c1 Revert "net: sche... |
277 |
static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index) |
ddf97ccdd net_sched: add ne... |
278 279 |
{ struct tc_action_net *tn = net_generic(net, nat_net_id); |
65a206c01 net/sched: Change... |
280 |
return tcf_idr_search(tn, a, index); |
ddf97ccdd net_sched: add ne... |
281 |
} |
b42199523 [PKT_SCHED]: Add ... |
282 283 |
static struct tc_action_ops act_nat_ops = { .kind = "nat", |
eddd2cf19 net: Change TCA_A... |
284 |
.id = TCA_ID_NAT, |
b42199523 [PKT_SCHED]: Add ... |
285 |
.owner = THIS_MODULE, |
0390514fe net: sched: act_n... |
286 |
.act = tcf_nat_act, |
b42199523 [PKT_SCHED]: Add ... |
287 |
.dump = tcf_nat_dump, |
b42199523 [PKT_SCHED]: Add ... |
288 |
.init = tcf_nat_init, |
ddf97ccdd net_sched: add ne... |
289 290 |
.walk = tcf_nat_walker, .lookup = tcf_nat_search, |
a85a970af net_sched: move t... |
291 |
.size = sizeof(struct tcf_nat), |
ddf97ccdd 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 net_sched: fix a ... |
297 |
return tc_action_net_init(net, tn, &act_nat_ops); |
ddf97ccdd net_sched: add ne... |
298 |
} |
039af9c66 net_sched: switch... |
299 |
static void __net_exit nat_exit_net(struct list_head *net_list) |
ddf97ccdd net_sched: add ne... |
300 |
{ |
039af9c66 net_sched: switch... |
301 |
tc_action_net_exit(net_list, nat_net_id); |
ddf97ccdd net_sched: add ne... |
302 303 304 305 |
} static struct pernet_operations nat_net_ops = { .init = nat_init_net, |
039af9c66 net_sched: switch... |
306 |
.exit_batch = nat_exit_net, |
ddf97ccdd net_sched: add ne... |
307 308 |
.id = &nat_net_id, .size = sizeof(struct tc_action_net), |
b42199523 [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 net_sched: add ne... |
316 |
return tcf_register_action(&act_nat_ops, &nat_net_ops); |
b42199523 [PKT_SCHED]: Add ... |
317 318 319 320 |
} static void __exit nat_cleanup_module(void) { |
ddf97ccdd net_sched: add ne... |
321 |
tcf_unregister_action(&act_nat_ops, &nat_net_ops); |
b42199523 [PKT_SCHED]: Add ... |
322 323 324 325 |
} module_init(nat_init_module); module_exit(nat_cleanup_module); |