Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
20dcb110 | 2 | /* L2TP netlink layer, for management |
309795f4 JC |
3 | * |
4 | * Copyright (c) 2008,2009,2010 Katalix Systems Ltd | |
5 | * | |
6 | * Partly based on the IrDA nelink implementation | |
7 | * (see net/irda/irnetlink.c) which is: | |
8 | * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org> | |
9 | * which is in turn partly based on the wireless netlink code: | |
10 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | |
309795f4 JC |
11 | */ |
12 | ||
a4ca44fa JP |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | ||
309795f4 JC |
15 | #include <net/sock.h> |
16 | #include <net/genetlink.h> | |
17 | #include <net/udp.h> | |
18 | #include <linux/in.h> | |
19 | #include <linux/udp.h> | |
20 | #include <linux/socket.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/list.h> | |
23 | #include <net/net_namespace.h> | |
24 | ||
25 | #include <linux/l2tp.h> | |
26 | ||
27 | #include "l2tp_core.h" | |
28 | ||
489111e5 | 29 | static struct genl_family l2tp_nl_family; |
309795f4 | 30 | |
33f72e6f BH |
31 | static const struct genl_multicast_group l2tp_multicast_group[] = { |
32 | { | |
33 | .name = L2TP_GENL_MCGROUP, | |
34 | }, | |
35 | }; | |
36 | ||
37 | static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, | |
38 | int flags, struct l2tp_tunnel *tunnel, u8 cmd); | |
39 | static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, | |
40 | int flags, struct l2tp_session *session, | |
41 | u8 cmd); | |
42 | ||
309795f4 JC |
43 | /* Accessed under genl lock */ |
44 | static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; | |
45 | ||
a4346210 | 46 | static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info) |
309795f4 JC |
47 | { |
48 | u32 tunnel_id; | |
49 | u32 session_id; | |
50 | char *ifname; | |
51 | struct l2tp_tunnel *tunnel; | |
52 | struct l2tp_session *session = NULL; | |
53 | struct net *net = genl_info_net(info); | |
54 | ||
55 | if (info->attrs[L2TP_ATTR_IFNAME]) { | |
56 | ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); | |
a4346210 | 57 | session = l2tp_session_get_by_ifname(net, ifname); |
309795f4 JC |
58 | } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && |
59 | (info->attrs[L2TP_ATTR_CONN_ID])) { | |
60 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); | |
61 | session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); | |
54652eb1 GN |
62 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
63 | if (tunnel) { | |
01e28b92 | 64 | session = l2tp_tunnel_get_session(tunnel, session_id); |
54652eb1 GN |
65 | l2tp_tunnel_dec_refcount(tunnel); |
66 | } | |
309795f4 JC |
67 | } |
68 | ||
69 | return session; | |
70 | } | |
71 | ||
72 | static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) | |
73 | { | |
74 | struct sk_buff *msg; | |
75 | void *hdr; | |
76 | int ret = -ENOBUFS; | |
77 | ||
58050fce | 78 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
309795f4 JC |
79 | if (!msg) { |
80 | ret = -ENOMEM; | |
81 | goto out; | |
82 | } | |
83 | ||
15e47304 | 84 | hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, |
309795f4 | 85 | &l2tp_nl_family, 0, L2TP_CMD_NOOP); |
7f8436a1 WY |
86 | if (!hdr) { |
87 | ret = -EMSGSIZE; | |
309795f4 JC |
88 | goto err_out; |
89 | } | |
90 | ||
91 | genlmsg_end(msg, hdr); | |
92 | ||
15e47304 | 93 | return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); |
309795f4 JC |
94 | |
95 | err_out: | |
96 | nlmsg_free(msg); | |
97 | ||
98 | out: | |
99 | return ret; | |
100 | } | |
101 | ||
33f72e6f BH |
102 | static int l2tp_tunnel_notify(struct genl_family *family, |
103 | struct genl_info *info, | |
104 | struct l2tp_tunnel *tunnel, | |
105 | u8 cmd) | |
106 | { | |
107 | struct sk_buff *msg; | |
108 | int ret; | |
109 | ||
110 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
111 | if (!msg) | |
112 | return -ENOMEM; | |
113 | ||
114 | ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, | |
115 | NLM_F_ACK, tunnel, cmd); | |
116 | ||
853effc5 MT |
117 | if (ret >= 0) { |
118 | ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); | |
119 | /* We don't care if no one is listening */ | |
120 | if (ret == -ESRCH) | |
121 | ret = 0; | |
122 | return ret; | |
123 | } | |
33f72e6f BH |
124 | |
125 | nlmsg_free(msg); | |
126 | ||
127 | return ret; | |
128 | } | |
129 | ||
130 | static int l2tp_session_notify(struct genl_family *family, | |
131 | struct genl_info *info, | |
132 | struct l2tp_session *session, | |
133 | u8 cmd) | |
134 | { | |
135 | struct sk_buff *msg; | |
136 | int ret; | |
137 | ||
138 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
139 | if (!msg) | |
140 | return -ENOMEM; | |
141 | ||
142 | ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, | |
143 | NLM_F_ACK, session, cmd); | |
144 | ||
853effc5 MT |
145 | if (ret >= 0) { |
146 | ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC); | |
147 | /* We don't care if no one is listening */ | |
148 | if (ret == -ESRCH) | |
149 | ret = 0; | |
150 | return ret; | |
151 | } | |
33f72e6f BH |
152 | |
153 | nlmsg_free(msg); | |
154 | ||
155 | return ret; | |
156 | } | |
157 | ||
0787840d TP |
158 | static int l2tp_nl_cmd_tunnel_create_get_addr(struct nlattr **attrs, struct l2tp_tunnel_cfg *cfg) |
159 | { | |
160 | if (attrs[L2TP_ATTR_UDP_SPORT]) | |
161 | cfg->local_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_SPORT]); | |
162 | if (attrs[L2TP_ATTR_UDP_DPORT]) | |
163 | cfg->peer_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_DPORT]); | |
164 | cfg->use_udp_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_CSUM]); | |
165 | ||
166 | /* Must have either AF_INET or AF_INET6 address for source and destination */ | |
167 | #if IS_ENABLED(CONFIG_IPV6) | |
168 | if (attrs[L2TP_ATTR_IP6_SADDR] && attrs[L2TP_ATTR_IP6_DADDR]) { | |
169 | cfg->local_ip6 = nla_data(attrs[L2TP_ATTR_IP6_SADDR]); | |
170 | cfg->peer_ip6 = nla_data(attrs[L2TP_ATTR_IP6_DADDR]); | |
171 | cfg->udp6_zero_tx_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); | |
172 | cfg->udp6_zero_rx_checksums = nla_get_flag(attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]); | |
173 | return 0; | |
174 | } | |
175 | #endif | |
176 | if (attrs[L2TP_ATTR_IP_SADDR] && attrs[L2TP_ATTR_IP_DADDR]) { | |
177 | cfg->local_ip.s_addr = nla_get_in_addr(attrs[L2TP_ATTR_IP_SADDR]); | |
178 | cfg->peer_ip.s_addr = nla_get_in_addr(attrs[L2TP_ATTR_IP_DADDR]); | |
179 | return 0; | |
180 | } | |
181 | return -EINVAL; | |
182 | } | |
183 | ||
309795f4 JC |
184 | static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) |
185 | { | |
186 | u32 tunnel_id; | |
187 | u32 peer_tunnel_id; | |
188 | int proto_version; | |
0787840d | 189 | int fd = -1; |
309795f4 JC |
190 | int ret = 0; |
191 | struct l2tp_tunnel_cfg cfg = { 0, }; | |
192 | struct l2tp_tunnel *tunnel; | |
193 | struct net *net = genl_info_net(info); | |
9f7da9a0 | 194 | struct nlattr **attrs = info->attrs; |
309795f4 | 195 | |
9f7da9a0 | 196 | if (!attrs[L2TP_ATTR_CONN_ID]) { |
309795f4 JC |
197 | ret = -EINVAL; |
198 | goto out; | |
199 | } | |
9f7da9a0 | 200 | tunnel_id = nla_get_u32(attrs[L2TP_ATTR_CONN_ID]); |
309795f4 | 201 | |
9f7da9a0 | 202 | if (!attrs[L2TP_ATTR_PEER_CONN_ID]) { |
309795f4 JC |
203 | ret = -EINVAL; |
204 | goto out; | |
205 | } | |
9f7da9a0 | 206 | peer_tunnel_id = nla_get_u32(attrs[L2TP_ATTR_PEER_CONN_ID]); |
309795f4 | 207 | |
9f7da9a0 | 208 | if (!attrs[L2TP_ATTR_PROTO_VERSION]) { |
309795f4 JC |
209 | ret = -EINVAL; |
210 | goto out; | |
211 | } | |
9f7da9a0 | 212 | proto_version = nla_get_u8(attrs[L2TP_ATTR_PROTO_VERSION]); |
309795f4 | 213 | |
9f7da9a0 | 214 | if (!attrs[L2TP_ATTR_ENCAP_TYPE]) { |
309795f4 JC |
215 | ret = -EINVAL; |
216 | goto out; | |
217 | } | |
9f7da9a0 | 218 | cfg.encap = nla_get_u16(attrs[L2TP_ATTR_ENCAP_TYPE]); |
309795f4 | 219 | |
0787840d TP |
220 | /* Managed tunnels take the tunnel socket from userspace. |
221 | * Unmanaged tunnels must call out the source and destination addresses | |
222 | * for the kernel to create the tunnel socket itself. | |
223 | */ | |
9f7da9a0 TP |
224 | if (attrs[L2TP_ATTR_FD]) { |
225 | fd = nla_get_u32(attrs[L2TP_ATTR_FD]); | |
789a4a2c | 226 | } else { |
0787840d TP |
227 | ret = l2tp_nl_cmd_tunnel_create_get_addr(attrs, &cfg); |
228 | if (ret < 0) | |
f9bac8df | 229 | goto out; |
309795f4 | 230 | } |
309795f4 | 231 | |
309795f4 JC |
232 | ret = -EINVAL; |
233 | switch (cfg.encap) { | |
234 | case L2TP_ENCAPTYPE_UDP: | |
235 | case L2TP_ENCAPTYPE_IP: | |
c9ccd4c6 | 236 | ret = l2tp_tunnel_create(fd, proto_version, tunnel_id, |
309795f4 JC |
237 | peer_tunnel_id, &cfg, &tunnel); |
238 | break; | |
239 | } | |
240 | ||
6b9f3423 GN |
241 | if (ret < 0) |
242 | goto out; | |
243 | ||
244 | l2tp_tunnel_inc_refcount(tunnel); | |
245 | ret = l2tp_tunnel_register(tunnel, net, &cfg); | |
246 | if (ret < 0) { | |
247 | kfree(tunnel); | |
248 | goto out; | |
249 | } | |
250 | ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel, | |
251 | L2TP_CMD_TUNNEL_CREATE); | |
252 | l2tp_tunnel_dec_refcount(tunnel); | |
253 | ||
309795f4 JC |
254 | out: |
255 | return ret; | |
256 | } | |
257 | ||
258 | static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info) | |
259 | { | |
260 | struct l2tp_tunnel *tunnel; | |
261 | u32 tunnel_id; | |
262 | int ret = 0; | |
263 | struct net *net = genl_info_net(info); | |
264 | ||
265 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { | |
266 | ret = -EINVAL; | |
267 | goto out; | |
268 | } | |
269 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); | |
270 | ||
bb0a32ce GN |
271 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
272 | if (!tunnel) { | |
309795f4 JC |
273 | ret = -ENODEV; |
274 | goto out; | |
275 | } | |
276 | ||
33f72e6f BH |
277 | l2tp_tunnel_notify(&l2tp_nl_family, info, |
278 | tunnel, L2TP_CMD_TUNNEL_DELETE); | |
279 | ||
4dc12ffe | 280 | l2tp_tunnel_delete(tunnel); |
309795f4 | 281 | |
bb0a32ce GN |
282 | l2tp_tunnel_dec_refcount(tunnel); |
283 | ||
309795f4 JC |
284 | out: |
285 | return ret; | |
286 | } | |
287 | ||
288 | static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info) | |
289 | { | |
290 | struct l2tp_tunnel *tunnel; | |
291 | u32 tunnel_id; | |
292 | int ret = 0; | |
293 | struct net *net = genl_info_net(info); | |
294 | ||
295 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { | |
296 | ret = -EINVAL; | |
297 | goto out; | |
298 | } | |
299 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); | |
300 | ||
8c0e4215 GN |
301 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
302 | if (!tunnel) { | |
309795f4 JC |
303 | ret = -ENODEV; |
304 | goto out; | |
305 | } | |
306 | ||
33f72e6f BH |
307 | ret = l2tp_tunnel_notify(&l2tp_nl_family, info, |
308 | tunnel, L2TP_CMD_TUNNEL_MODIFY); | |
309 | ||
8c0e4215 GN |
310 | l2tp_tunnel_dec_refcount(tunnel); |
311 | ||
309795f4 JC |
312 | out: |
313 | return ret; | |
314 | } | |
315 | ||
584ca31f TP |
316 | #if IS_ENABLED(CONFIG_IPV6) |
317 | static int l2tp_nl_tunnel_send_addr6(struct sk_buff *skb, struct sock *sk, | |
318 | enum l2tp_encap_type encap) | |
319 | { | |
320 | struct inet_sock *inet = inet_sk(sk); | |
321 | struct ipv6_pinfo *np = inet6_sk(sk); | |
322 | ||
323 | switch (encap) { | |
324 | case L2TP_ENCAPTYPE_UDP: | |
325 | if (udp_get_no_check6_tx(sk) && | |
326 | nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_TX)) | |
327 | return -1; | |
328 | if (udp_get_no_check6_rx(sk) && | |
329 | nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_RX)) | |
330 | return -1; | |
331 | if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || | |
332 | nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) | |
333 | return -1; | |
334 | fallthrough; | |
335 | case L2TP_ENCAPTYPE_IP: | |
336 | if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR, &np->saddr) || | |
337 | nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR, &sk->sk_v6_daddr)) | |
338 | return -1; | |
339 | break; | |
340 | } | |
341 | return 0; | |
342 | } | |
343 | #endif | |
344 | ||
345 | static int l2tp_nl_tunnel_send_addr4(struct sk_buff *skb, struct sock *sk, | |
346 | enum l2tp_encap_type encap) | |
347 | { | |
348 | struct inet_sock *inet = inet_sk(sk); | |
349 | ||
350 | switch (encap) { | |
351 | case L2TP_ENCAPTYPE_UDP: | |
352 | if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx) || | |
353 | nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || | |
354 | nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport))) | |
355 | return -1; | |
356 | fallthrough; | |
357 | case L2TP_ENCAPTYPE_IP: | |
358 | if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) || | |
359 | nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr)) | |
360 | return -1; | |
361 | break; | |
362 | } | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
367 | /* Append attributes for the tunnel address, handling the different attribute types | |
368 | * used for different tunnel encapsulation and AF_INET v.s. AF_INET6. | |
369 | */ | |
370 | static int l2tp_nl_tunnel_send_addr(struct sk_buff *skb, struct l2tp_tunnel *tunnel) | |
371 | { | |
372 | struct sock *sk = tunnel->sock; | |
373 | ||
374 | if (!sk) | |
375 | return 0; | |
376 | ||
377 | #if IS_ENABLED(CONFIG_IPV6) | |
378 | if (sk->sk_family == AF_INET6) | |
379 | return l2tp_nl_tunnel_send_addr6(skb, sk, tunnel->encap); | |
380 | #endif | |
381 | return l2tp_nl_tunnel_send_addr4(skb, sk, tunnel->encap); | |
382 | } | |
383 | ||
15e47304 | 384 | static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
33f72e6f | 385 | struct l2tp_tunnel *tunnel, u8 cmd) |
309795f4 JC |
386 | { |
387 | void *hdr; | |
388 | struct nlattr *nest; | |
309795f4 | 389 | |
33f72e6f | 390 | hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); |
7f8436a1 WY |
391 | if (!hdr) |
392 | return -EMSGSIZE; | |
309795f4 | 393 | |
60aed2ab DM |
394 | if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) || |
395 | nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || | |
396 | nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || | |
eee049c0 | 397 | nla_put_u32(skb, L2TP_ATTR_DEBUG, 0) || |
60aed2ab DM |
398 | nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap)) |
399 | goto nla_put_failure; | |
309795f4 | 400 | |
ae0be8de | 401 | nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS); |
0febc7b3 | 402 | if (!nest) |
309795f4 JC |
403 | goto nla_put_failure; |
404 | ||
1c714a92 ND |
405 | if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS, |
406 | atomic_long_read(&tunnel->stats.tx_packets), | |
407 | L2TP_ATTR_STATS_PAD) || | |
408 | nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES, | |
409 | atomic_long_read(&tunnel->stats.tx_bytes), | |
410 | L2TP_ATTR_STATS_PAD) || | |
411 | nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS, | |
412 | atomic_long_read(&tunnel->stats.tx_errors), | |
413 | L2TP_ATTR_STATS_PAD) || | |
414 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS, | |
415 | atomic_long_read(&tunnel->stats.rx_packets), | |
416 | L2TP_ATTR_STATS_PAD) || | |
417 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES, | |
418 | atomic_long_read(&tunnel->stats.rx_bytes), | |
419 | L2TP_ATTR_STATS_PAD) || | |
420 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS, | |
421 | atomic_long_read(&tunnel->stats.rx_seq_discards), | |
422 | L2TP_ATTR_STATS_PAD) || | |
3f47cb4c TP |
423 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_COOKIE_DISCARDS, |
424 | atomic_long_read(&tunnel->stats.rx_cookie_discards), | |
425 | L2TP_ATTR_STATS_PAD) || | |
1c714a92 ND |
426 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS, |
427 | atomic_long_read(&tunnel->stats.rx_oos_packets), | |
428 | L2TP_ATTR_STATS_PAD) || | |
429 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS, | |
430 | atomic_long_read(&tunnel->stats.rx_errors), | |
431 | L2TP_ATTR_STATS_PAD)) | |
60aed2ab | 432 | goto nla_put_failure; |
309795f4 JC |
433 | nla_nest_end(skb, nest); |
434 | ||
584ca31f TP |
435 | if (l2tp_nl_tunnel_send_addr(skb, tunnel)) |
436 | goto nla_put_failure; | |
309795f4 | 437 | |
053c095a JB |
438 | genlmsg_end(skb, hdr); |
439 | return 0; | |
309795f4 JC |
440 | |
441 | nla_put_failure: | |
442 | genlmsg_cancel(skb, hdr); | |
443 | return -1; | |
444 | } | |
445 | ||
446 | static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) | |
447 | { | |
448 | struct l2tp_tunnel *tunnel; | |
449 | struct sk_buff *msg; | |
450 | u32 tunnel_id; | |
451 | int ret = -ENOBUFS; | |
452 | struct net *net = genl_info_net(info); | |
453 | ||
454 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { | |
455 | ret = -EINVAL; | |
4e4b21da | 456 | goto err; |
309795f4 JC |
457 | } |
458 | ||
459 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); | |
460 | ||
58050fce | 461 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
309795f4 JC |
462 | if (!msg) { |
463 | ret = -ENOMEM; | |
4e4b21da GN |
464 | goto err; |
465 | } | |
466 | ||
467 | tunnel = l2tp_tunnel_get(net, tunnel_id); | |
468 | if (!tunnel) { | |
469 | ret = -ENODEV; | |
470 | goto err_nlmsg; | |
309795f4 JC |
471 | } |
472 | ||
15e47304 | 473 | ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, |
33f72e6f | 474 | NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET); |
309795f4 | 475 | if (ret < 0) |
4e4b21da GN |
476 | goto err_nlmsg_tunnel; |
477 | ||
478 | l2tp_tunnel_dec_refcount(tunnel); | |
309795f4 | 479 | |
15e47304 | 480 | return genlmsg_unicast(net, msg, info->snd_portid); |
309795f4 | 481 | |
4e4b21da GN |
482 | err_nlmsg_tunnel: |
483 | l2tp_tunnel_dec_refcount(tunnel); | |
484 | err_nlmsg: | |
309795f4 | 485 | nlmsg_free(msg); |
4e4b21da | 486 | err: |
309795f4 JC |
487 | return ret; |
488 | } | |
489 | ||
490 | static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) | |
491 | { | |
492 | int ti = cb->args[0]; | |
493 | struct l2tp_tunnel *tunnel; | |
494 | struct net *net = sock_net(skb->sk); | |
495 | ||
496 | for (;;) { | |
5846c131 | 497 | tunnel = l2tp_tunnel_get_nth(net, ti); |
0febc7b3 | 498 | if (!tunnel) |
309795f4 JC |
499 | goto out; |
500 | ||
15e47304 | 501 | if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, |
309795f4 | 502 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
5846c131 GN |
503 | tunnel, L2TP_CMD_TUNNEL_GET) < 0) { |
504 | l2tp_tunnel_dec_refcount(tunnel); | |
309795f4 | 505 | goto out; |
5846c131 GN |
506 | } |
507 | l2tp_tunnel_dec_refcount(tunnel); | |
309795f4 JC |
508 | |
509 | ti++; | |
510 | } | |
511 | ||
512 | out: | |
513 | cb->args[0] = ti; | |
514 | ||
515 | return skb->len; | |
516 | } | |
517 | ||
518 | static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) | |
519 | { | |
520 | u32 tunnel_id = 0; | |
521 | u32 session_id; | |
522 | u32 peer_session_id; | |
523 | int ret = 0; | |
524 | struct l2tp_tunnel *tunnel; | |
525 | struct l2tp_session *session; | |
526 | struct l2tp_session_cfg cfg = { 0, }; | |
527 | struct net *net = genl_info_net(info); | |
528 | ||
529 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { | |
530 | ret = -EINVAL; | |
531 | goto out; | |
532 | } | |
e702c120 | 533 | |
309795f4 | 534 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
e702c120 | 535 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
309795f4 JC |
536 | if (!tunnel) { |
537 | ret = -ENODEV; | |
538 | goto out; | |
539 | } | |
540 | ||
541 | if (!info->attrs[L2TP_ATTR_SESSION_ID]) { | |
542 | ret = -EINVAL; | |
e702c120 | 543 | goto out_tunnel; |
309795f4 JC |
544 | } |
545 | session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); | |
309795f4 JC |
546 | |
547 | if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { | |
548 | ret = -EINVAL; | |
e702c120 | 549 | goto out_tunnel; |
309795f4 JC |
550 | } |
551 | peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]); | |
552 | ||
553 | if (!info->attrs[L2TP_ATTR_PW_TYPE]) { | |
554 | ret = -EINVAL; | |
e702c120 | 555 | goto out_tunnel; |
309795f4 JC |
556 | } |
557 | cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]); | |
558 | if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { | |
559 | ret = -EINVAL; | |
e702c120 | 560 | goto out_tunnel; |
309795f4 JC |
561 | } |
562 | ||
de9bada5 GN |
563 | /* L2TPv2 only accepts PPP pseudo-wires */ |
564 | if (tunnel->version == 2 && cfg.pw_type != L2TP_PWTYPE_PPP) { | |
565 | ret = -EPROTONOSUPPORT; | |
566 | goto out_tunnel; | |
567 | } | |
568 | ||
309795f4 | 569 | if (tunnel->version > 2) { |
dfffc97d | 570 | if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) { |
309795f4 | 571 | cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]); |
dfffc97d LB |
572 | if (cfg.l2specific_type != L2TP_L2SPECTYPE_DEFAULT && |
573 | cfg.l2specific_type != L2TP_L2SPECTYPE_NONE) { | |
574 | ret = -EINVAL; | |
575 | goto out_tunnel; | |
576 | } | |
577 | } else { | |
578 | cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; | |
579 | } | |
309795f4 | 580 | |
309795f4 JC |
581 | if (info->attrs[L2TP_ATTR_COOKIE]) { |
582 | u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]); | |
b71a61cc | 583 | |
309795f4 JC |
584 | if (len > 8) { |
585 | ret = -EINVAL; | |
e702c120 | 586 | goto out_tunnel; |
309795f4 JC |
587 | } |
588 | cfg.cookie_len = len; | |
589 | memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); | |
590 | } | |
591 | if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { | |
592 | u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]); | |
b71a61cc | 593 | |
309795f4 JC |
594 | if (len > 8) { |
595 | ret = -EINVAL; | |
e702c120 | 596 | goto out_tunnel; |
309795f4 JC |
597 | } |
598 | cfg.peer_cookie_len = len; | |
599 | memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); | |
600 | } | |
601 | if (info->attrs[L2TP_ATTR_IFNAME]) | |
602 | cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); | |
309795f4 JC |
603 | } |
604 | ||
309795f4 JC |
605 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
606 | cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); | |
607 | ||
608 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) | |
609 | cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); | |
610 | ||
611 | if (info->attrs[L2TP_ATTR_LNS_MODE]) | |
612 | cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); | |
613 | ||
614 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) | |
615 | cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); | |
616 | ||
f1f39f91 | 617 | #ifdef CONFIG_MODULES |
0febc7b3 | 618 | if (!l2tp_nl_cmd_ops[cfg.pw_type]) { |
f1f39f91 | 619 | genl_unlock(); |
620 | request_module("net-l2tp-type-%u", cfg.pw_type); | |
621 | genl_lock(); | |
622 | } | |
623 | #endif | |
0febc7b3 | 624 | if (!l2tp_nl_cmd_ops[cfg.pw_type] || !l2tp_nl_cmd_ops[cfg.pw_type]->session_create) { |
309795f4 | 625 | ret = -EPROTONOSUPPORT; |
e702c120 | 626 | goto out_tunnel; |
309795f4 JC |
627 | } |
628 | ||
f026bc29 GN |
629 | ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel, |
630 | session_id, | |
631 | peer_session_id, | |
632 | &cfg); | |
309795f4 | 633 | |
33f72e6f | 634 | if (ret >= 0) { |
01e28b92 | 635 | session = l2tp_tunnel_get_session(tunnel, session_id); |
5e6a9e5a | 636 | if (session) { |
33f72e6f BH |
637 | ret = l2tp_session_notify(&l2tp_nl_family, info, session, |
638 | L2TP_CMD_SESSION_CREATE); | |
5e6a9e5a GN |
639 | l2tp_session_dec_refcount(session); |
640 | } | |
33f72e6f BH |
641 | } |
642 | ||
e702c120 GN |
643 | out_tunnel: |
644 | l2tp_tunnel_dec_refcount(tunnel); | |
309795f4 JC |
645 | out: |
646 | return ret; | |
647 | } | |
648 | ||
649 | static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) | |
650 | { | |
651 | int ret = 0; | |
652 | struct l2tp_session *session; | |
653 | u16 pw_type; | |
654 | ||
a4346210 | 655 | session = l2tp_nl_session_get(info); |
0febc7b3 | 656 | if (!session) { |
309795f4 JC |
657 | ret = -ENODEV; |
658 | goto out; | |
659 | } | |
660 | ||
33f72e6f BH |
661 | l2tp_session_notify(&l2tp_nl_family, info, |
662 | session, L2TP_CMD_SESSION_DELETE); | |
663 | ||
309795f4 JC |
664 | pw_type = session->pwtype; |
665 | if (pw_type < __L2TP_PWTYPE_MAX) | |
666 | if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) | |
628703f5 | 667 | l2tp_nl_cmd_ops[pw_type]->session_delete(session); |
309795f4 | 668 | |
2777e2ab GN |
669 | l2tp_session_dec_refcount(session); |
670 | ||
309795f4 JC |
671 | out: |
672 | return ret; | |
673 | } | |
674 | ||
675 | static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) | |
676 | { | |
677 | int ret = 0; | |
678 | struct l2tp_session *session; | |
679 | ||
a4346210 | 680 | session = l2tp_nl_session_get(info); |
0febc7b3 | 681 | if (!session) { |
309795f4 JC |
682 | ret = -ENODEV; |
683 | goto out; | |
684 | } | |
685 | ||
309795f4 JC |
686 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
687 | session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); | |
688 | ||
bb5016ea | 689 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) { |
309795f4 | 690 | session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); |
bb5016ea GN |
691 | l2tp_session_set_header_len(session, session->tunnel->version); |
692 | } | |
309795f4 JC |
693 | |
694 | if (info->attrs[L2TP_ATTR_LNS_MODE]) | |
695 | session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); | |
696 | ||
697 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) | |
698 | session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); | |
699 | ||
33f72e6f BH |
700 | ret = l2tp_session_notify(&l2tp_nl_family, info, |
701 | session, L2TP_CMD_SESSION_MODIFY); | |
702 | ||
2777e2ab GN |
703 | l2tp_session_dec_refcount(session); |
704 | ||
309795f4 JC |
705 | out: |
706 | return ret; | |
707 | } | |
708 | ||
15e47304 | 709 | static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
33f72e6f | 710 | struct l2tp_session *session, u8 cmd) |
309795f4 JC |
711 | { |
712 | void *hdr; | |
713 | struct nlattr *nest; | |
714 | struct l2tp_tunnel *tunnel = session->tunnel; | |
309795f4 | 715 | |
33f72e6f | 716 | hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd); |
7f8436a1 WY |
717 | if (!hdr) |
718 | return -EMSGSIZE; | |
309795f4 | 719 | |
60aed2ab DM |
720 | if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || |
721 | nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) || | |
722 | nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || | |
9f7da9a0 | 723 | nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id) || |
eee049c0 | 724 | nla_put_u32(skb, L2TP_ATTR_DEBUG, 0) || |
e9697e2e | 725 | nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype)) |
60aed2ab DM |
726 | goto nla_put_failure; |
727 | ||
e269ed26 | 728 | if ((session->ifname[0] && |
60aed2ab DM |
729 | nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) || |
730 | (session->cookie_len && | |
9f7da9a0 | 731 | nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len, session->cookie)) || |
60aed2ab | 732 | (session->peer_cookie_len && |
9f7da9a0 | 733 | nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, session->peer_cookie)) || |
60aed2ab DM |
734 | nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) || |
735 | nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) || | |
736 | nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) || | |
d6a61ec9 | 737 | (l2tp_tunnel_uses_xfrm(tunnel) && |
60aed2ab | 738 | nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) || |
60aed2ab | 739 | (session->reorder_timeout && |
2175d87c ND |
740 | nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT, |
741 | session->reorder_timeout, L2TP_ATTR_PAD))) | |
60aed2ab | 742 | goto nla_put_failure; |
5de7aee5 | 743 | |
ae0be8de | 744 | nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS); |
0febc7b3 | 745 | if (!nest) |
309795f4 | 746 | goto nla_put_failure; |
5de7aee5 | 747 | |
1c714a92 ND |
748 | if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS, |
749 | atomic_long_read(&session->stats.tx_packets), | |
750 | L2TP_ATTR_STATS_PAD) || | |
751 | nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES, | |
752 | atomic_long_read(&session->stats.tx_bytes), | |
753 | L2TP_ATTR_STATS_PAD) || | |
754 | nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS, | |
755 | atomic_long_read(&session->stats.tx_errors), | |
756 | L2TP_ATTR_STATS_PAD) || | |
757 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS, | |
758 | atomic_long_read(&session->stats.rx_packets), | |
759 | L2TP_ATTR_STATS_PAD) || | |
760 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES, | |
761 | atomic_long_read(&session->stats.rx_bytes), | |
762 | L2TP_ATTR_STATS_PAD) || | |
763 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS, | |
764 | atomic_long_read(&session->stats.rx_seq_discards), | |
765 | L2TP_ATTR_STATS_PAD) || | |
3f47cb4c TP |
766 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_COOKIE_DISCARDS, |
767 | atomic_long_read(&session->stats.rx_cookie_discards), | |
768 | L2TP_ATTR_STATS_PAD) || | |
1c714a92 ND |
769 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS, |
770 | atomic_long_read(&session->stats.rx_oos_packets), | |
771 | L2TP_ATTR_STATS_PAD) || | |
772 | nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS, | |
773 | atomic_long_read(&session->stats.rx_errors), | |
774 | L2TP_ATTR_STATS_PAD)) | |
60aed2ab | 775 | goto nla_put_failure; |
309795f4 JC |
776 | nla_nest_end(skb, nest); |
777 | ||
053c095a JB |
778 | genlmsg_end(skb, hdr); |
779 | return 0; | |
309795f4 JC |
780 | |
781 | nla_put_failure: | |
782 | genlmsg_cancel(skb, hdr); | |
783 | return -1; | |
784 | } | |
785 | ||
786 | static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) | |
787 | { | |
788 | struct l2tp_session *session; | |
789 | struct sk_buff *msg; | |
790 | int ret; | |
791 | ||
a4346210 | 792 | session = l2tp_nl_session_get(info); |
0febc7b3 | 793 | if (!session) { |
309795f4 | 794 | ret = -ENODEV; |
2777e2ab | 795 | goto err; |
309795f4 JC |
796 | } |
797 | ||
58050fce | 798 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
309795f4 JC |
799 | if (!msg) { |
800 | ret = -ENOMEM; | |
2777e2ab | 801 | goto err_ref; |
309795f4 JC |
802 | } |
803 | ||
15e47304 | 804 | ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, |
33f72e6f | 805 | 0, session, L2TP_CMD_SESSION_GET); |
309795f4 | 806 | if (ret < 0) |
2777e2ab | 807 | goto err_ref_msg; |
309795f4 | 808 | |
2777e2ab | 809 | ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); |
309795f4 | 810 | |
2777e2ab | 811 | l2tp_session_dec_refcount(session); |
309795f4 | 812 | |
2777e2ab GN |
813 | return ret; |
814 | ||
815 | err_ref_msg: | |
816 | nlmsg_free(msg); | |
817 | err_ref: | |
818 | l2tp_session_dec_refcount(session); | |
819 | err: | |
309795f4 JC |
820 | return ret; |
821 | } | |
822 | ||
823 | static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) | |
824 | { | |
825 | struct net *net = sock_net(skb->sk); | |
826 | struct l2tp_session *session; | |
827 | struct l2tp_tunnel *tunnel = NULL; | |
828 | int ti = cb->args[0]; | |
829 | int si = cb->args[1]; | |
830 | ||
831 | for (;;) { | |
0febc7b3 | 832 | if (!tunnel) { |
5846c131 | 833 | tunnel = l2tp_tunnel_get_nth(net, ti); |
0febc7b3 | 834 | if (!tunnel) |
309795f4 JC |
835 | goto out; |
836 | } | |
837 | ||
a4346210 | 838 | session = l2tp_session_get_nth(tunnel, si); |
0febc7b3 | 839 | if (!session) { |
309795f4 | 840 | ti++; |
5846c131 | 841 | l2tp_tunnel_dec_refcount(tunnel); |
309795f4 JC |
842 | tunnel = NULL; |
843 | si = 0; | |
844 | continue; | |
845 | } | |
846 | ||
15e47304 | 847 | if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, |
309795f4 | 848 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
e08293a4 GN |
849 | session, L2TP_CMD_SESSION_GET) < 0) { |
850 | l2tp_session_dec_refcount(session); | |
5846c131 | 851 | l2tp_tunnel_dec_refcount(tunnel); |
309795f4 | 852 | break; |
e08293a4 GN |
853 | } |
854 | l2tp_session_dec_refcount(session); | |
309795f4 JC |
855 | |
856 | si++; | |
857 | } | |
858 | ||
859 | out: | |
860 | cb->args[0] = ti; | |
861 | cb->args[1] = si; | |
862 | ||
863 | return skb->len; | |
864 | } | |
865 | ||
f5bb341e | 866 | static const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { |
309795f4 JC |
867 | [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, |
868 | [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, | |
869 | [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, | |
870 | [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, | |
871 | [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, | |
872 | [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, | |
873 | [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, | |
874 | [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, | |
875 | [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, | |
876 | [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, | |
877 | [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, | |
878 | [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, | |
879 | [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, | |
880 | [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, | |
881 | [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, | |
882 | [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, | |
883 | [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, | |
884 | [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, | |
885 | [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, | |
886 | [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, | |
887 | [L2TP_ATTR_FD] = { .type = NLA_U32, }, | |
888 | [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, | |
889 | [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, | |
890 | [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, | |
891 | [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, | |
892 | [L2TP_ATTR_MTU] = { .type = NLA_U16, }, | |
893 | [L2TP_ATTR_MRU] = { .type = NLA_U16, }, | |
894 | [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, | |
f9bac8df CE |
895 | [L2TP_ATTR_IP6_SADDR] = { |
896 | .type = NLA_BINARY, | |
897 | .len = sizeof(struct in6_addr), | |
898 | }, | |
899 | [L2TP_ATTR_IP6_DADDR] = { | |
900 | .type = NLA_BINARY, | |
901 | .len = sizeof(struct in6_addr), | |
902 | }, | |
309795f4 JC |
903 | [L2TP_ATTR_IFNAME] = { |
904 | .type = NLA_NUL_STRING, | |
905 | .len = IFNAMSIZ - 1, | |
906 | }, | |
907 | [L2TP_ATTR_COOKIE] = { | |
908 | .type = NLA_BINARY, | |
909 | .len = 8, | |
910 | }, | |
911 | [L2TP_ATTR_PEER_COOKIE] = { | |
912 | .type = NLA_BINARY, | |
913 | .len = 8, | |
914 | }, | |
915 | }; | |
916 | ||
66a9b928 | 917 | static const struct genl_small_ops l2tp_nl_ops[] = { |
309795f4 JC |
918 | { |
919 | .cmd = L2TP_CMD_NOOP, | |
ef6243ac | 920 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 921 | .doit = l2tp_nl_cmd_noop, |
309795f4 JC |
922 | /* can be retrieved by unprivileged users */ |
923 | }, | |
924 | { | |
925 | .cmd = L2TP_CMD_TUNNEL_CREATE, | |
ef6243ac | 926 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 927 | .doit = l2tp_nl_cmd_tunnel_create, |
2abe0523 | 928 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
929 | }, |
930 | { | |
931 | .cmd = L2TP_CMD_TUNNEL_DELETE, | |
ef6243ac | 932 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 933 | .doit = l2tp_nl_cmd_tunnel_delete, |
2abe0523 | 934 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
935 | }, |
936 | { | |
937 | .cmd = L2TP_CMD_TUNNEL_MODIFY, | |
ef6243ac | 938 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 939 | .doit = l2tp_nl_cmd_tunnel_modify, |
2abe0523 | 940 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
941 | }, |
942 | { | |
943 | .cmd = L2TP_CMD_TUNNEL_GET, | |
ef6243ac | 944 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 JC |
945 | .doit = l2tp_nl_cmd_tunnel_get, |
946 | .dumpit = l2tp_nl_cmd_tunnel_dump, | |
2abe0523 | 947 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
948 | }, |
949 | { | |
950 | .cmd = L2TP_CMD_SESSION_CREATE, | |
ef6243ac | 951 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 952 | .doit = l2tp_nl_cmd_session_create, |
2abe0523 | 953 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
954 | }, |
955 | { | |
956 | .cmd = L2TP_CMD_SESSION_DELETE, | |
ef6243ac | 957 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 958 | .doit = l2tp_nl_cmd_session_delete, |
2abe0523 | 959 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
960 | }, |
961 | { | |
962 | .cmd = L2TP_CMD_SESSION_MODIFY, | |
ef6243ac | 963 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 | 964 | .doit = l2tp_nl_cmd_session_modify, |
2abe0523 | 965 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
966 | }, |
967 | { | |
968 | .cmd = L2TP_CMD_SESSION_GET, | |
ef6243ac | 969 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
309795f4 JC |
970 | .doit = l2tp_nl_cmd_session_get, |
971 | .dumpit = l2tp_nl_cmd_session_dump, | |
2abe0523 | 972 | .flags = GENL_UNS_ADMIN_PERM, |
309795f4 JC |
973 | }, |
974 | }; | |
975 | ||
56989f6d | 976 | static struct genl_family l2tp_nl_family __ro_after_init = { |
489111e5 JB |
977 | .name = L2TP_GENL_NAME, |
978 | .version = L2TP_GENL_VERSION, | |
979 | .hdrsize = 0, | |
980 | .maxattr = L2TP_ATTR_MAX, | |
3b0f31f2 | 981 | .policy = l2tp_nl_policy, |
489111e5 JB |
982 | .netnsok = true, |
983 | .module = THIS_MODULE, | |
66a9b928 JK |
984 | .small_ops = l2tp_nl_ops, |
985 | .n_small_ops = ARRAY_SIZE(l2tp_nl_ops), | |
489111e5 JB |
986 | .mcgrps = l2tp_multicast_group, |
987 | .n_mcgrps = ARRAY_SIZE(l2tp_multicast_group), | |
988 | }; | |
989 | ||
309795f4 JC |
990 | int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) |
991 | { | |
992 | int ret; | |
993 | ||
994 | ret = -EINVAL; | |
995 | if (pw_type >= __L2TP_PWTYPE_MAX) | |
996 | goto err; | |
997 | ||
998 | genl_lock(); | |
999 | ret = -EBUSY; | |
1000 | if (l2tp_nl_cmd_ops[pw_type]) | |
1001 | goto out; | |
1002 | ||
1003 | l2tp_nl_cmd_ops[pw_type] = ops; | |
8cb49014 | 1004 | ret = 0; |
309795f4 JC |
1005 | |
1006 | out: | |
1007 | genl_unlock(); | |
1008 | err: | |
8cb49014 | 1009 | return ret; |
309795f4 JC |
1010 | } |
1011 | EXPORT_SYMBOL_GPL(l2tp_nl_register_ops); | |
1012 | ||
1013 | void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) | |
1014 | { | |
1015 | if (pw_type < __L2TP_PWTYPE_MAX) { | |
1016 | genl_lock(); | |
1017 | l2tp_nl_cmd_ops[pw_type] = NULL; | |
1018 | genl_unlock(); | |
1019 | } | |
1020 | } | |
1021 | EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); | |
1022 | ||
56989f6d | 1023 | static int __init l2tp_nl_init(void) |
309795f4 | 1024 | { |
a4ca44fa | 1025 | pr_info("L2TP netlink interface\n"); |
489111e5 | 1026 | return genl_register_family(&l2tp_nl_family); |
309795f4 JC |
1027 | } |
1028 | ||
1029 | static void l2tp_nl_cleanup(void) | |
1030 | { | |
1031 | genl_unregister_family(&l2tp_nl_family); | |
1032 | } | |
1033 | ||
1034 | module_init(l2tp_nl_init); | |
1035 | module_exit(l2tp_nl_cleanup); | |
1036 | ||
1037 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); | |
1038 | MODULE_DESCRIPTION("L2TP netlink"); | |
1039 | MODULE_LICENSE("GPL"); | |
1040 | MODULE_VERSION("1.0"); | |
e9412c37 | 1041 | MODULE_ALIAS_GENL_FAMILY("l2tp"); |