1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* L2TP netlink layer, for management |
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> |
11 | */ |
12 | |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | |
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 | |
29 | static struct genl_family l2tp_nl_family; |
30 | |
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 | |
43 | /* Accessed under genl lock */ |
44 | static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; |
45 | |
46 | static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info) |
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(nla: info->attrs[L2TP_ATTR_IFNAME]); |
57 | session = l2tp_session_get_by_ifname(net, ifname); |
58 | } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && |
59 | (info->attrs[L2TP_ATTR_CONN_ID])) { |
60 | tunnel_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_CONN_ID]); |
61 | session_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_SESSION_ID]); |
62 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
63 | if (tunnel) { |
64 | session = l2tp_tunnel_get_session(tunnel, session_id); |
65 | l2tp_tunnel_dec_refcount(tunnel); |
66 | } |
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 | |
78 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
79 | if (!msg) { |
80 | ret = -ENOMEM; |
81 | goto out; |
82 | } |
83 | |
84 | hdr = genlmsg_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
85 | family: &l2tp_nl_family, flags: 0, cmd: L2TP_CMD_NOOP); |
86 | if (!hdr) { |
87 | ret = -EMSGSIZE; |
88 | goto err_out; |
89 | } |
90 | |
91 | genlmsg_end(skb: msg, hdr); |
92 | |
93 | return genlmsg_unicast(net: genl_info_net(info), skb: msg, portid: info->snd_portid); |
94 | |
95 | err_out: |
96 | nlmsg_free(skb: msg); |
97 | |
98 | out: |
99 | return ret; |
100 | } |
101 | |
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(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
115 | NLM_F_ACK, tunnel, cmd); |
116 | |
117 | if (ret >= 0) { |
118 | ret = genlmsg_multicast_allns(family, skb: msg, portid: 0, group: 0, GFP_ATOMIC); |
119 | /* We don't care if no one is listening */ |
120 | if (ret == -ESRCH) |
121 | ret = 0; |
122 | return ret; |
123 | } |
124 | |
125 | nlmsg_free(skb: 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(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
143 | NLM_F_ACK, session, cmd); |
144 | |
145 | if (ret >= 0) { |
146 | ret = genlmsg_multicast_allns(family, skb: msg, portid: 0, group: 0, GFP_ATOMIC); |
147 | /* We don't care if no one is listening */ |
148 | if (ret == -ESRCH) |
149 | ret = 0; |
150 | return ret; |
151 | } |
152 | |
153 | nlmsg_free(skb: msg); |
154 | |
155 | return ret; |
156 | } |
157 | |
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(nla: attrs[L2TP_ATTR_UDP_SPORT]); |
162 | if (attrs[L2TP_ATTR_UDP_DPORT]) |
163 | cfg->peer_udp_port = nla_get_u16(nla: attrs[L2TP_ATTR_UDP_DPORT]); |
164 | cfg->use_udp_checksums = nla_get_flag(nla: 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(nla: attrs[L2TP_ATTR_IP6_SADDR]); |
170 | cfg->peer_ip6 = nla_data(nla: attrs[L2TP_ATTR_IP6_DADDR]); |
171 | cfg->udp6_zero_tx_checksums = nla_get_flag(nla: attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]); |
172 | cfg->udp6_zero_rx_checksums = nla_get_flag(nla: 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(nla: attrs[L2TP_ATTR_IP_SADDR]); |
178 | cfg->peer_ip.s_addr = nla_get_in_addr(nla: attrs[L2TP_ATTR_IP_DADDR]); |
179 | return 0; |
180 | } |
181 | return -EINVAL; |
182 | } |
183 | |
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; |
189 | int fd = -1; |
190 | int ret = 0; |
191 | struct l2tp_tunnel_cfg cfg = { 0, }; |
192 | struct l2tp_tunnel *tunnel; |
193 | struct net *net = genl_info_net(info); |
194 | struct nlattr **attrs = info->attrs; |
195 | |
196 | if (!attrs[L2TP_ATTR_CONN_ID]) { |
197 | ret = -EINVAL; |
198 | goto out; |
199 | } |
200 | tunnel_id = nla_get_u32(nla: attrs[L2TP_ATTR_CONN_ID]); |
201 | |
202 | if (!attrs[L2TP_ATTR_PEER_CONN_ID]) { |
203 | ret = -EINVAL; |
204 | goto out; |
205 | } |
206 | peer_tunnel_id = nla_get_u32(nla: attrs[L2TP_ATTR_PEER_CONN_ID]); |
207 | |
208 | if (!attrs[L2TP_ATTR_PROTO_VERSION]) { |
209 | ret = -EINVAL; |
210 | goto out; |
211 | } |
212 | proto_version = nla_get_u8(nla: attrs[L2TP_ATTR_PROTO_VERSION]); |
213 | |
214 | if (!attrs[L2TP_ATTR_ENCAP_TYPE]) { |
215 | ret = -EINVAL; |
216 | goto out; |
217 | } |
218 | cfg.encap = nla_get_u16(nla: attrs[L2TP_ATTR_ENCAP_TYPE]); |
219 | |
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 | */ |
224 | if (attrs[L2TP_ATTR_FD]) { |
225 | fd = nla_get_u32(nla: attrs[L2TP_ATTR_FD]); |
226 | } else { |
227 | ret = l2tp_nl_cmd_tunnel_create_get_addr(attrs, cfg: &cfg); |
228 | if (ret < 0) |
229 | goto out; |
230 | } |
231 | |
232 | ret = -EINVAL; |
233 | switch (cfg.encap) { |
234 | case L2TP_ENCAPTYPE_UDP: |
235 | case L2TP_ENCAPTYPE_IP: |
236 | ret = l2tp_tunnel_create(fd, version: proto_version, tunnel_id, |
237 | peer_tunnel_id, cfg: &cfg, tunnelp: &tunnel); |
238 | break; |
239 | } |
240 | |
241 | if (ret < 0) |
242 | goto out; |
243 | |
244 | l2tp_tunnel_inc_refcount(tunnel); |
245 | ret = l2tp_tunnel_register(tunnel, net, cfg: &cfg); |
246 | if (ret < 0) { |
247 | kfree(objp: tunnel); |
248 | goto out; |
249 | } |
250 | ret = l2tp_tunnel_notify(family: &l2tp_nl_family, info, tunnel, |
251 | cmd: L2TP_CMD_TUNNEL_CREATE); |
252 | l2tp_tunnel_dec_refcount(tunnel); |
253 | |
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(nla: info->attrs[L2TP_ATTR_CONN_ID]); |
270 | |
271 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
272 | if (!tunnel) { |
273 | ret = -ENODEV; |
274 | goto out; |
275 | } |
276 | |
277 | l2tp_tunnel_notify(family: &l2tp_nl_family, info, |
278 | tunnel, cmd: L2TP_CMD_TUNNEL_DELETE); |
279 | |
280 | l2tp_tunnel_delete(tunnel); |
281 | |
282 | l2tp_tunnel_dec_refcount(tunnel); |
283 | |
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(nla: info->attrs[L2TP_ATTR_CONN_ID]); |
300 | |
301 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
302 | if (!tunnel) { |
303 | ret = -ENODEV; |
304 | goto out; |
305 | } |
306 | |
307 | ret = l2tp_tunnel_notify(family: &l2tp_nl_family, info, |
308 | tunnel, cmd: L2TP_CMD_TUNNEL_MODIFY); |
309 | |
310 | l2tp_tunnel_dec_refcount(tunnel); |
311 | |
312 | out: |
313 | return ret; |
314 | } |
315 | |
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: sk); |
322 | |
323 | switch (encap) { |
324 | case L2TP_ENCAPTYPE_UDP: |
325 | if (udp_get_no_check6_tx(sk) && |
326 | nla_put_flag(skb, attrtype: L2TP_ATTR_UDP_ZERO_CSUM6_TX)) |
327 | return -1; |
328 | if (udp_get_no_check6_rx(sk) && |
329 | nla_put_flag(skb, attrtype: L2TP_ATTR_UDP_ZERO_CSUM6_RX)) |
330 | return -1; |
331 | if (nla_put_u16(skb, attrtype: L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || |
332 | nla_put_u16(skb, attrtype: 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, attrtype: L2TP_ATTR_IP6_SADDR, addr: &np->saddr) || |
337 | nla_put_in6_addr(skb, attrtype: L2TP_ATTR_IP6_DADDR, addr: &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, attrtype: L2TP_ATTR_UDP_CSUM, value: !sk->sk_no_check_tx) || |
353 | nla_put_u16(skb, attrtype: L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || |
354 | nla_put_u16(skb, attrtype: 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, attrtype: L2TP_ATTR_IP_SADDR, addr: inet->inet_saddr) || |
359 | nla_put_in_addr(skb, attrtype: L2TP_ATTR_IP_DADDR, addr: 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, encap: tunnel->encap); |
380 | #endif |
381 | return l2tp_nl_tunnel_send_addr4(skb, sk, encap: tunnel->encap); |
382 | } |
383 | |
384 | static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
385 | struct l2tp_tunnel *tunnel, u8 cmd) |
386 | { |
387 | void *hdr; |
388 | struct nlattr *nest; |
389 | |
390 | hdr = genlmsg_put(skb, portid, seq, family: &l2tp_nl_family, flags, cmd); |
391 | if (!hdr) |
392 | return -EMSGSIZE; |
393 | |
394 | if (nla_put_u8(skb, attrtype: L2TP_ATTR_PROTO_VERSION, value: tunnel->version) || |
395 | nla_put_u32(skb, attrtype: L2TP_ATTR_CONN_ID, value: tunnel->tunnel_id) || |
396 | nla_put_u32(skb, attrtype: L2TP_ATTR_PEER_CONN_ID, value: tunnel->peer_tunnel_id) || |
397 | nla_put_u32(skb, attrtype: L2TP_ATTR_DEBUG, value: 0) || |
398 | nla_put_u16(skb, attrtype: L2TP_ATTR_ENCAP_TYPE, value: tunnel->encap)) |
399 | goto nla_put_failure; |
400 | |
401 | nest = nla_nest_start_noflag(skb, attrtype: L2TP_ATTR_STATS); |
402 | if (!nest) |
403 | goto nla_put_failure; |
404 | |
405 | if (nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_PACKETS, |
406 | value: atomic_long_read(v: &tunnel->stats.tx_packets), |
407 | padattr: L2TP_ATTR_STATS_PAD) || |
408 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_BYTES, |
409 | value: atomic_long_read(v: &tunnel->stats.tx_bytes), |
410 | padattr: L2TP_ATTR_STATS_PAD) || |
411 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_ERRORS, |
412 | value: atomic_long_read(v: &tunnel->stats.tx_errors), |
413 | padattr: L2TP_ATTR_STATS_PAD) || |
414 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_PACKETS, |
415 | value: atomic_long_read(v: &tunnel->stats.rx_packets), |
416 | padattr: L2TP_ATTR_STATS_PAD) || |
417 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_BYTES, |
418 | value: atomic_long_read(v: &tunnel->stats.rx_bytes), |
419 | padattr: L2TP_ATTR_STATS_PAD) || |
420 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_SEQ_DISCARDS, |
421 | value: atomic_long_read(v: &tunnel->stats.rx_seq_discards), |
422 | padattr: L2TP_ATTR_STATS_PAD) || |
423 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_COOKIE_DISCARDS, |
424 | value: atomic_long_read(v: &tunnel->stats.rx_cookie_discards), |
425 | padattr: L2TP_ATTR_STATS_PAD) || |
426 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_OOS_PACKETS, |
427 | value: atomic_long_read(v: &tunnel->stats.rx_oos_packets), |
428 | padattr: L2TP_ATTR_STATS_PAD) || |
429 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_ERRORS, |
430 | value: atomic_long_read(v: &tunnel->stats.rx_errors), |
431 | padattr: L2TP_ATTR_STATS_PAD) || |
432 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_INVALID, |
433 | value: atomic_long_read(v: &tunnel->stats.rx_invalid), |
434 | padattr: L2TP_ATTR_STATS_PAD)) |
435 | goto nla_put_failure; |
436 | nla_nest_end(skb, start: nest); |
437 | |
438 | if (l2tp_nl_tunnel_send_addr(skb, tunnel)) |
439 | goto nla_put_failure; |
440 | |
441 | genlmsg_end(skb, hdr); |
442 | return 0; |
443 | |
444 | nla_put_failure: |
445 | genlmsg_cancel(skb, hdr); |
446 | return -1; |
447 | } |
448 | |
449 | static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) |
450 | { |
451 | struct l2tp_tunnel *tunnel; |
452 | struct sk_buff *msg; |
453 | u32 tunnel_id; |
454 | int ret = -ENOBUFS; |
455 | struct net *net = genl_info_net(info); |
456 | |
457 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
458 | ret = -EINVAL; |
459 | goto err; |
460 | } |
461 | |
462 | tunnel_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_CONN_ID]); |
463 | |
464 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
465 | if (!msg) { |
466 | ret = -ENOMEM; |
467 | goto err; |
468 | } |
469 | |
470 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
471 | if (!tunnel) { |
472 | ret = -ENODEV; |
473 | goto err_nlmsg; |
474 | } |
475 | |
476 | ret = l2tp_nl_tunnel_send(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
477 | NLM_F_ACK, tunnel, cmd: L2TP_CMD_TUNNEL_GET); |
478 | if (ret < 0) |
479 | goto err_nlmsg_tunnel; |
480 | |
481 | l2tp_tunnel_dec_refcount(tunnel); |
482 | |
483 | return genlmsg_unicast(net, skb: msg, portid: info->snd_portid); |
484 | |
485 | err_nlmsg_tunnel: |
486 | l2tp_tunnel_dec_refcount(tunnel); |
487 | err_nlmsg: |
488 | nlmsg_free(skb: msg); |
489 | err: |
490 | return ret; |
491 | } |
492 | |
493 | static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) |
494 | { |
495 | int ti = cb->args[0]; |
496 | struct l2tp_tunnel *tunnel; |
497 | struct net *net = sock_net(sk: skb->sk); |
498 | |
499 | for (;;) { |
500 | tunnel = l2tp_tunnel_get_nth(net, nth: ti); |
501 | if (!tunnel) |
502 | goto out; |
503 | |
504 | if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, |
505 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
506 | tunnel, cmd: L2TP_CMD_TUNNEL_GET) < 0) { |
507 | l2tp_tunnel_dec_refcount(tunnel); |
508 | goto out; |
509 | } |
510 | l2tp_tunnel_dec_refcount(tunnel); |
511 | |
512 | ti++; |
513 | } |
514 | |
515 | out: |
516 | cb->args[0] = ti; |
517 | |
518 | return skb->len; |
519 | } |
520 | |
521 | static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) |
522 | { |
523 | u32 tunnel_id = 0; |
524 | u32 session_id; |
525 | u32 peer_session_id; |
526 | int ret = 0; |
527 | struct l2tp_tunnel *tunnel; |
528 | struct l2tp_session *session; |
529 | struct l2tp_session_cfg cfg = { 0, }; |
530 | struct net *net = genl_info_net(info); |
531 | |
532 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
533 | ret = -EINVAL; |
534 | goto out; |
535 | } |
536 | |
537 | tunnel_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_CONN_ID]); |
538 | tunnel = l2tp_tunnel_get(net, tunnel_id); |
539 | if (!tunnel) { |
540 | ret = -ENODEV; |
541 | goto out; |
542 | } |
543 | |
544 | if (!info->attrs[L2TP_ATTR_SESSION_ID]) { |
545 | ret = -EINVAL; |
546 | goto out_tunnel; |
547 | } |
548 | session_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_SESSION_ID]); |
549 | |
550 | if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { |
551 | ret = -EINVAL; |
552 | goto out_tunnel; |
553 | } |
554 | peer_session_id = nla_get_u32(nla: info->attrs[L2TP_ATTR_PEER_SESSION_ID]); |
555 | |
556 | if (!info->attrs[L2TP_ATTR_PW_TYPE]) { |
557 | ret = -EINVAL; |
558 | goto out_tunnel; |
559 | } |
560 | cfg.pw_type = nla_get_u16(nla: info->attrs[L2TP_ATTR_PW_TYPE]); |
561 | if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { |
562 | ret = -EINVAL; |
563 | goto out_tunnel; |
564 | } |
565 | |
566 | /* L2TPv2 only accepts PPP pseudo-wires */ |
567 | if (tunnel->version == 2 && cfg.pw_type != L2TP_PWTYPE_PPP) { |
568 | ret = -EPROTONOSUPPORT; |
569 | goto out_tunnel; |
570 | } |
571 | |
572 | if (tunnel->version > 2) { |
573 | if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) { |
574 | cfg.l2specific_type = nla_get_u8(nla: info->attrs[L2TP_ATTR_L2SPEC_TYPE]); |
575 | if (cfg.l2specific_type != L2TP_L2SPECTYPE_DEFAULT && |
576 | cfg.l2specific_type != L2TP_L2SPECTYPE_NONE) { |
577 | ret = -EINVAL; |
578 | goto out_tunnel; |
579 | } |
580 | } else { |
581 | cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; |
582 | } |
583 | |
584 | if (info->attrs[L2TP_ATTR_COOKIE]) { |
585 | u16 len = nla_len(nla: info->attrs[L2TP_ATTR_COOKIE]); |
586 | |
587 | if (len > 8) { |
588 | ret = -EINVAL; |
589 | goto out_tunnel; |
590 | } |
591 | cfg.cookie_len = len; |
592 | memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); |
593 | } |
594 | if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { |
595 | u16 len = nla_len(nla: info->attrs[L2TP_ATTR_PEER_COOKIE]); |
596 | |
597 | if (len > 8) { |
598 | ret = -EINVAL; |
599 | goto out_tunnel; |
600 | } |
601 | cfg.peer_cookie_len = len; |
602 | memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); |
603 | } |
604 | if (info->attrs[L2TP_ATTR_IFNAME]) |
605 | cfg.ifname = nla_data(nla: info->attrs[L2TP_ATTR_IFNAME]); |
606 | } |
607 | |
608 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
609 | cfg.recv_seq = nla_get_u8(nla: info->attrs[L2TP_ATTR_RECV_SEQ]); |
610 | |
611 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) |
612 | cfg.send_seq = nla_get_u8(nla: info->attrs[L2TP_ATTR_SEND_SEQ]); |
613 | |
614 | if (info->attrs[L2TP_ATTR_LNS_MODE]) |
615 | cfg.lns_mode = nla_get_u8(nla: info->attrs[L2TP_ATTR_LNS_MODE]); |
616 | |
617 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) |
618 | cfg.reorder_timeout = nla_get_msecs(nla: info->attrs[L2TP_ATTR_RECV_TIMEOUT]); |
619 | |
620 | #ifdef CONFIG_MODULES |
621 | if (!l2tp_nl_cmd_ops[cfg.pw_type]) { |
622 | genl_unlock(); |
623 | request_module("net-l2tp-type-%u" , cfg.pw_type); |
624 | genl_lock(); |
625 | } |
626 | #endif |
627 | if (!l2tp_nl_cmd_ops[cfg.pw_type] || !l2tp_nl_cmd_ops[cfg.pw_type]->session_create) { |
628 | ret = -EPROTONOSUPPORT; |
629 | goto out_tunnel; |
630 | } |
631 | |
632 | ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel, |
633 | session_id, |
634 | peer_session_id, |
635 | &cfg); |
636 | |
637 | if (ret >= 0) { |
638 | session = l2tp_tunnel_get_session(tunnel, session_id); |
639 | if (session) { |
640 | ret = l2tp_session_notify(family: &l2tp_nl_family, info, session, |
641 | cmd: L2TP_CMD_SESSION_CREATE); |
642 | l2tp_session_dec_refcount(session); |
643 | } |
644 | } |
645 | |
646 | out_tunnel: |
647 | l2tp_tunnel_dec_refcount(tunnel); |
648 | out: |
649 | return ret; |
650 | } |
651 | |
652 | static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) |
653 | { |
654 | int ret = 0; |
655 | struct l2tp_session *session; |
656 | u16 pw_type; |
657 | |
658 | session = l2tp_nl_session_get(info); |
659 | if (!session) { |
660 | ret = -ENODEV; |
661 | goto out; |
662 | } |
663 | |
664 | l2tp_session_notify(family: &l2tp_nl_family, info, |
665 | session, cmd: L2TP_CMD_SESSION_DELETE); |
666 | |
667 | pw_type = session->pwtype; |
668 | if (pw_type < __L2TP_PWTYPE_MAX) |
669 | if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) |
670 | l2tp_nl_cmd_ops[pw_type]->session_delete(session); |
671 | |
672 | l2tp_session_dec_refcount(session); |
673 | |
674 | out: |
675 | return ret; |
676 | } |
677 | |
678 | static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) |
679 | { |
680 | int ret = 0; |
681 | struct l2tp_session *session; |
682 | |
683 | session = l2tp_nl_session_get(info); |
684 | if (!session) { |
685 | ret = -ENODEV; |
686 | goto out; |
687 | } |
688 | |
689 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
690 | session->recv_seq = nla_get_u8(nla: info->attrs[L2TP_ATTR_RECV_SEQ]); |
691 | |
692 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) { |
693 | session->send_seq = nla_get_u8(nla: info->attrs[L2TP_ATTR_SEND_SEQ]); |
694 | l2tp_session_set_header_len(session, version: session->tunnel->version); |
695 | } |
696 | |
697 | if (info->attrs[L2TP_ATTR_LNS_MODE]) |
698 | session->lns_mode = nla_get_u8(nla: info->attrs[L2TP_ATTR_LNS_MODE]); |
699 | |
700 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) |
701 | session->reorder_timeout = nla_get_msecs(nla: info->attrs[L2TP_ATTR_RECV_TIMEOUT]); |
702 | |
703 | ret = l2tp_session_notify(family: &l2tp_nl_family, info, |
704 | session, cmd: L2TP_CMD_SESSION_MODIFY); |
705 | |
706 | l2tp_session_dec_refcount(session); |
707 | |
708 | out: |
709 | return ret; |
710 | } |
711 | |
712 | static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
713 | struct l2tp_session *session, u8 cmd) |
714 | { |
715 | void *hdr; |
716 | struct nlattr *nest; |
717 | struct l2tp_tunnel *tunnel = session->tunnel; |
718 | |
719 | hdr = genlmsg_put(skb, portid, seq, family: &l2tp_nl_family, flags, cmd); |
720 | if (!hdr) |
721 | return -EMSGSIZE; |
722 | |
723 | if (nla_put_u32(skb, attrtype: L2TP_ATTR_CONN_ID, value: tunnel->tunnel_id) || |
724 | nla_put_u32(skb, attrtype: L2TP_ATTR_SESSION_ID, value: session->session_id) || |
725 | nla_put_u32(skb, attrtype: L2TP_ATTR_PEER_CONN_ID, value: tunnel->peer_tunnel_id) || |
726 | nla_put_u32(skb, attrtype: L2TP_ATTR_PEER_SESSION_ID, value: session->peer_session_id) || |
727 | nla_put_u32(skb, attrtype: L2TP_ATTR_DEBUG, value: 0) || |
728 | nla_put_u16(skb, attrtype: L2TP_ATTR_PW_TYPE, value: session->pwtype)) |
729 | goto nla_put_failure; |
730 | |
731 | if ((session->ifname[0] && |
732 | nla_put_string(skb, attrtype: L2TP_ATTR_IFNAME, str: session->ifname)) || |
733 | (session->cookie_len && |
734 | nla_put(skb, attrtype: L2TP_ATTR_COOKIE, attrlen: session->cookie_len, data: session->cookie)) || |
735 | (session->peer_cookie_len && |
736 | nla_put(skb, attrtype: L2TP_ATTR_PEER_COOKIE, attrlen: session->peer_cookie_len, data: session->peer_cookie)) || |
737 | nla_put_u8(skb, attrtype: L2TP_ATTR_RECV_SEQ, value: session->recv_seq) || |
738 | nla_put_u8(skb, attrtype: L2TP_ATTR_SEND_SEQ, value: session->send_seq) || |
739 | nla_put_u8(skb, attrtype: L2TP_ATTR_LNS_MODE, value: session->lns_mode) || |
740 | (l2tp_tunnel_uses_xfrm(tunnel) && |
741 | nla_put_u8(skb, attrtype: L2TP_ATTR_USING_IPSEC, value: 1)) || |
742 | (session->reorder_timeout && |
743 | nla_put_msecs(skb, attrtype: L2TP_ATTR_RECV_TIMEOUT, |
744 | njiffies: session->reorder_timeout, padattr: L2TP_ATTR_PAD))) |
745 | goto nla_put_failure; |
746 | |
747 | nest = nla_nest_start_noflag(skb, attrtype: L2TP_ATTR_STATS); |
748 | if (!nest) |
749 | goto nla_put_failure; |
750 | |
751 | if (nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_PACKETS, |
752 | value: atomic_long_read(v: &session->stats.tx_packets), |
753 | padattr: L2TP_ATTR_STATS_PAD) || |
754 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_BYTES, |
755 | value: atomic_long_read(v: &session->stats.tx_bytes), |
756 | padattr: L2TP_ATTR_STATS_PAD) || |
757 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_TX_ERRORS, |
758 | value: atomic_long_read(v: &session->stats.tx_errors), |
759 | padattr: L2TP_ATTR_STATS_PAD) || |
760 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_PACKETS, |
761 | value: atomic_long_read(v: &session->stats.rx_packets), |
762 | padattr: L2TP_ATTR_STATS_PAD) || |
763 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_BYTES, |
764 | value: atomic_long_read(v: &session->stats.rx_bytes), |
765 | padattr: L2TP_ATTR_STATS_PAD) || |
766 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_SEQ_DISCARDS, |
767 | value: atomic_long_read(v: &session->stats.rx_seq_discards), |
768 | padattr: L2TP_ATTR_STATS_PAD) || |
769 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_COOKIE_DISCARDS, |
770 | value: atomic_long_read(v: &session->stats.rx_cookie_discards), |
771 | padattr: L2TP_ATTR_STATS_PAD) || |
772 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_OOS_PACKETS, |
773 | value: atomic_long_read(v: &session->stats.rx_oos_packets), |
774 | padattr: L2TP_ATTR_STATS_PAD) || |
775 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_ERRORS, |
776 | value: atomic_long_read(v: &session->stats.rx_errors), |
777 | padattr: L2TP_ATTR_STATS_PAD) || |
778 | nla_put_u64_64bit(skb, attrtype: L2TP_ATTR_RX_INVALID, |
779 | value: atomic_long_read(v: &session->stats.rx_invalid), |
780 | padattr: L2TP_ATTR_STATS_PAD)) |
781 | goto nla_put_failure; |
782 | nla_nest_end(skb, start: nest); |
783 | |
784 | genlmsg_end(skb, hdr); |
785 | return 0; |
786 | |
787 | nla_put_failure: |
788 | genlmsg_cancel(skb, hdr); |
789 | return -1; |
790 | } |
791 | |
792 | static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) |
793 | { |
794 | struct l2tp_session *session; |
795 | struct sk_buff *msg; |
796 | int ret; |
797 | |
798 | session = l2tp_nl_session_get(info); |
799 | if (!session) { |
800 | ret = -ENODEV; |
801 | goto err; |
802 | } |
803 | |
804 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
805 | if (!msg) { |
806 | ret = -ENOMEM; |
807 | goto err_ref; |
808 | } |
809 | |
810 | ret = l2tp_nl_session_send(skb: msg, portid: info->snd_portid, seq: info->snd_seq, |
811 | flags: 0, session, cmd: L2TP_CMD_SESSION_GET); |
812 | if (ret < 0) |
813 | goto err_ref_msg; |
814 | |
815 | ret = genlmsg_unicast(net: genl_info_net(info), skb: msg, portid: info->snd_portid); |
816 | |
817 | l2tp_session_dec_refcount(session); |
818 | |
819 | return ret; |
820 | |
821 | err_ref_msg: |
822 | nlmsg_free(skb: msg); |
823 | err_ref: |
824 | l2tp_session_dec_refcount(session); |
825 | err: |
826 | return ret; |
827 | } |
828 | |
829 | static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) |
830 | { |
831 | struct net *net = sock_net(sk: skb->sk); |
832 | struct l2tp_session *session; |
833 | struct l2tp_tunnel *tunnel = NULL; |
834 | int ti = cb->args[0]; |
835 | int si = cb->args[1]; |
836 | |
837 | for (;;) { |
838 | if (!tunnel) { |
839 | tunnel = l2tp_tunnel_get_nth(net, nth: ti); |
840 | if (!tunnel) |
841 | goto out; |
842 | } |
843 | |
844 | session = l2tp_session_get_nth(tunnel, nth: si); |
845 | if (!session) { |
846 | ti++; |
847 | l2tp_tunnel_dec_refcount(tunnel); |
848 | tunnel = NULL; |
849 | si = 0; |
850 | continue; |
851 | } |
852 | |
853 | if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, |
854 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
855 | session, cmd: L2TP_CMD_SESSION_GET) < 0) { |
856 | l2tp_session_dec_refcount(session); |
857 | l2tp_tunnel_dec_refcount(tunnel); |
858 | break; |
859 | } |
860 | l2tp_session_dec_refcount(session); |
861 | |
862 | si++; |
863 | } |
864 | |
865 | out: |
866 | cb->args[0] = ti; |
867 | cb->args[1] = si; |
868 | |
869 | return skb->len; |
870 | } |
871 | |
872 | static const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { |
873 | [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, |
874 | [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, |
875 | [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, |
876 | [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, |
877 | [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, |
878 | [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, |
879 | [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, |
880 | [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, |
881 | [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, |
882 | [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, |
883 | [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, |
884 | [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, |
885 | [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, |
886 | [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, |
887 | [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, |
888 | [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, |
889 | [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, |
890 | [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, |
891 | [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, |
892 | [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, |
893 | [L2TP_ATTR_FD] = { .type = NLA_U32, }, |
894 | [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, |
895 | [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, |
896 | [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, |
897 | [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, |
898 | [L2TP_ATTR_MTU] = { .type = NLA_U16, }, |
899 | [L2TP_ATTR_MRU] = { .type = NLA_U16, }, |
900 | [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, |
901 | [L2TP_ATTR_IP6_SADDR] = { |
902 | .type = NLA_BINARY, |
903 | .len = sizeof(struct in6_addr), |
904 | }, |
905 | [L2TP_ATTR_IP6_DADDR] = { |
906 | .type = NLA_BINARY, |
907 | .len = sizeof(struct in6_addr), |
908 | }, |
909 | [L2TP_ATTR_IFNAME] = { |
910 | .type = NLA_NUL_STRING, |
911 | .len = IFNAMSIZ - 1, |
912 | }, |
913 | [L2TP_ATTR_COOKIE] = { |
914 | .type = NLA_BINARY, |
915 | .len = 8, |
916 | }, |
917 | [L2TP_ATTR_PEER_COOKIE] = { |
918 | .type = NLA_BINARY, |
919 | .len = 8, |
920 | }, |
921 | }; |
922 | |
923 | static const struct genl_small_ops l2tp_nl_ops[] = { |
924 | { |
925 | .cmd = L2TP_CMD_NOOP, |
926 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
927 | .doit = l2tp_nl_cmd_noop, |
928 | /* can be retrieved by unprivileged users */ |
929 | }, |
930 | { |
931 | .cmd = L2TP_CMD_TUNNEL_CREATE, |
932 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
933 | .doit = l2tp_nl_cmd_tunnel_create, |
934 | .flags = GENL_UNS_ADMIN_PERM, |
935 | }, |
936 | { |
937 | .cmd = L2TP_CMD_TUNNEL_DELETE, |
938 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
939 | .doit = l2tp_nl_cmd_tunnel_delete, |
940 | .flags = GENL_UNS_ADMIN_PERM, |
941 | }, |
942 | { |
943 | .cmd = L2TP_CMD_TUNNEL_MODIFY, |
944 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
945 | .doit = l2tp_nl_cmd_tunnel_modify, |
946 | .flags = GENL_UNS_ADMIN_PERM, |
947 | }, |
948 | { |
949 | .cmd = L2TP_CMD_TUNNEL_GET, |
950 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
951 | .doit = l2tp_nl_cmd_tunnel_get, |
952 | .dumpit = l2tp_nl_cmd_tunnel_dump, |
953 | .flags = GENL_UNS_ADMIN_PERM, |
954 | }, |
955 | { |
956 | .cmd = L2TP_CMD_SESSION_CREATE, |
957 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
958 | .doit = l2tp_nl_cmd_session_create, |
959 | .flags = GENL_UNS_ADMIN_PERM, |
960 | }, |
961 | { |
962 | .cmd = L2TP_CMD_SESSION_DELETE, |
963 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
964 | .doit = l2tp_nl_cmd_session_delete, |
965 | .flags = GENL_UNS_ADMIN_PERM, |
966 | }, |
967 | { |
968 | .cmd = L2TP_CMD_SESSION_MODIFY, |
969 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
970 | .doit = l2tp_nl_cmd_session_modify, |
971 | .flags = GENL_UNS_ADMIN_PERM, |
972 | }, |
973 | { |
974 | .cmd = L2TP_CMD_SESSION_GET, |
975 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
976 | .doit = l2tp_nl_cmd_session_get, |
977 | .dumpit = l2tp_nl_cmd_session_dump, |
978 | .flags = GENL_UNS_ADMIN_PERM, |
979 | }, |
980 | }; |
981 | |
982 | static struct genl_family l2tp_nl_family __ro_after_init = { |
983 | .name = L2TP_GENL_NAME, |
984 | .version = L2TP_GENL_VERSION, |
985 | .hdrsize = 0, |
986 | .maxattr = L2TP_ATTR_MAX, |
987 | .policy = l2tp_nl_policy, |
988 | .netnsok = true, |
989 | .module = THIS_MODULE, |
990 | .small_ops = l2tp_nl_ops, |
991 | .n_small_ops = ARRAY_SIZE(l2tp_nl_ops), |
992 | .resv_start_op = L2TP_CMD_SESSION_GET + 1, |
993 | .mcgrps = l2tp_multicast_group, |
994 | .n_mcgrps = ARRAY_SIZE(l2tp_multicast_group), |
995 | }; |
996 | |
997 | int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) |
998 | { |
999 | int ret; |
1000 | |
1001 | ret = -EINVAL; |
1002 | if (pw_type >= __L2TP_PWTYPE_MAX) |
1003 | goto err; |
1004 | |
1005 | genl_lock(); |
1006 | ret = -EBUSY; |
1007 | if (l2tp_nl_cmd_ops[pw_type]) |
1008 | goto out; |
1009 | |
1010 | l2tp_nl_cmd_ops[pw_type] = ops; |
1011 | ret = 0; |
1012 | |
1013 | out: |
1014 | genl_unlock(); |
1015 | err: |
1016 | return ret; |
1017 | } |
1018 | EXPORT_SYMBOL_GPL(l2tp_nl_register_ops); |
1019 | |
1020 | void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) |
1021 | { |
1022 | if (pw_type < __L2TP_PWTYPE_MAX) { |
1023 | genl_lock(); |
1024 | l2tp_nl_cmd_ops[pw_type] = NULL; |
1025 | genl_unlock(); |
1026 | } |
1027 | } |
1028 | EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); |
1029 | |
1030 | static int __init l2tp_nl_init(void) |
1031 | { |
1032 | pr_info("L2TP netlink interface\n" ); |
1033 | return genl_register_family(family: &l2tp_nl_family); |
1034 | } |
1035 | |
1036 | static void l2tp_nl_cleanup(void) |
1037 | { |
1038 | genl_unregister_family(family: &l2tp_nl_family); |
1039 | } |
1040 | |
1041 | module_init(l2tp_nl_init); |
1042 | module_exit(l2tp_nl_cleanup); |
1043 | |
1044 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>" ); |
1045 | MODULE_DESCRIPTION("L2TP netlink" ); |
1046 | MODULE_LICENSE("GPL" ); |
1047 | MODULE_VERSION("1.0" ); |
1048 | MODULE_ALIAS_GENL_FAMILY("l2tp" ); |
1049 | |