blob: 0a27f7e976f3ea605e18fc7c35c463236e534779 [file] [log] [blame]
James Chapman309795f2010-04-02 06:19:10 +00001/*
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 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 */
16
Joe Perchesa4ca44f2012-05-16 09:55:56 +000017#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
James Chapman309795f2010-04-02 06:19:10 +000019#include <net/sock.h>
20#include <net/genetlink.h>
21#include <net/udp.h>
22#include <linux/in.h>
23#include <linux/udp.h>
24#include <linux/socket.h>
25#include <linux/module.h>
26#include <linux/list.h>
27#include <net/net_namespace.h>
28
29#include <linux/l2tp.h>
30
31#include "l2tp_core.h"
32
33
34static struct genl_family l2tp_nl_family = {
35 .id = GENL_ID_GENERATE,
36 .name = L2TP_GENL_NAME,
37 .version = L2TP_GENL_VERSION,
38 .hdrsize = 0,
39 .maxattr = L2TP_ATTR_MAX,
Tom Parkinb6fdfdf2013-01-31 23:43:01 +000040 .netnsok = true,
James Chapman309795f2010-04-02 06:19:10 +000041};
42
Bill Hong33f72e62014-12-27 10:12:39 -080043static const struct genl_multicast_group l2tp_multicast_group[] = {
44 {
45 .name = L2TP_GENL_MCGROUP,
46 },
47};
48
49static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq,
50 int flags, struct l2tp_tunnel *tunnel, u8 cmd);
51static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq,
52 int flags, struct l2tp_session *session,
53 u8 cmd);
54
James Chapman309795f2010-04-02 06:19:10 +000055/* Accessed under genl lock */
56static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
57
Guillaume Nault08cb8e52017-03-31 13:02:30 +020058static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
59 bool do_ref)
James Chapman309795f2010-04-02 06:19:10 +000060{
61 u32 tunnel_id;
62 u32 session_id;
63 char *ifname;
64 struct l2tp_tunnel *tunnel;
65 struct l2tp_session *session = NULL;
66 struct net *net = genl_info_net(info);
67
68 if (info->attrs[L2TP_ATTR_IFNAME]) {
69 ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
Guillaume Nault08cb8e52017-03-31 13:02:30 +020070 session = l2tp_session_get_by_ifname(net, ifname, do_ref);
James Chapman309795f2010-04-02 06:19:10 +000071 } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
72 (info->attrs[L2TP_ATTR_CONN_ID])) {
73 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
74 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
Guillaume Nault523e6d72020-05-22 00:39:26 +010075 tunnel = l2tp_tunnel_get(net, tunnel_id);
76 if (tunnel) {
Guillaume Nault08cb8e52017-03-31 13:02:30 +020077 session = l2tp_session_get(net, tunnel, session_id,
78 do_ref);
Guillaume Nault523e6d72020-05-22 00:39:26 +010079 l2tp_tunnel_dec_refcount(tunnel);
80 }
James Chapman309795f2010-04-02 06:19:10 +000081 }
82
83 return session;
84}
85
86static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
87{
88 struct sk_buff *msg;
89 void *hdr;
90 int ret = -ENOBUFS;
91
Thomas Graf58050fc2012-06-28 03:57:45 +000092 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
James Chapman309795f2010-04-02 06:19:10 +000093 if (!msg) {
94 ret = -ENOMEM;
95 goto out;
96 }
97
Eric W. Biederman15e47302012-09-07 20:12:54 +000098 hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
James Chapman309795f2010-04-02 06:19:10 +000099 &l2tp_nl_family, 0, L2TP_CMD_NOOP);
Wei Yongjun7f8436a2012-09-24 18:29:01 +0000100 if (!hdr) {
101 ret = -EMSGSIZE;
James Chapman309795f2010-04-02 06:19:10 +0000102 goto err_out;
103 }
104
105 genlmsg_end(msg, hdr);
106
Eric W. Biederman15e47302012-09-07 20:12:54 +0000107 return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
James Chapman309795f2010-04-02 06:19:10 +0000108
109err_out:
110 nlmsg_free(msg);
111
112out:
113 return ret;
114}
115
Bill Hong33f72e62014-12-27 10:12:39 -0800116static int l2tp_tunnel_notify(struct genl_family *family,
117 struct genl_info *info,
118 struct l2tp_tunnel *tunnel,
119 u8 cmd)
120{
121 struct sk_buff *msg;
122 int ret;
123
124 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
125 if (!msg)
126 return -ENOMEM;
127
128 ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
129 NLM_F_ACK, tunnel, cmd);
130
Mark Tomlinson853effc2016-02-15 16:24:44 +1300131 if (ret >= 0) {
132 ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
133 /* We don't care if no one is listening */
134 if (ret == -ESRCH)
135 ret = 0;
136 return ret;
137 }
Bill Hong33f72e62014-12-27 10:12:39 -0800138
139 nlmsg_free(msg);
140
141 return ret;
142}
143
144static int l2tp_session_notify(struct genl_family *family,
145 struct genl_info *info,
146 struct l2tp_session *session,
147 u8 cmd)
148{
149 struct sk_buff *msg;
150 int ret;
151
152 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
153 if (!msg)
154 return -ENOMEM;
155
156 ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
157 NLM_F_ACK, session, cmd);
158
Mark Tomlinson853effc2016-02-15 16:24:44 +1300159 if (ret >= 0) {
160 ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
161 /* We don't care if no one is listening */
162 if (ret == -ESRCH)
163 ret = 0;
164 return ret;
165 }
Bill Hong33f72e62014-12-27 10:12:39 -0800166
167 nlmsg_free(msg);
168
169 return ret;
170}
171
James Chapman309795f2010-04-02 06:19:10 +0000172static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info)
173{
174 u32 tunnel_id;
175 u32 peer_tunnel_id;
176 int proto_version;
177 int fd;
178 int ret = 0;
179 struct l2tp_tunnel_cfg cfg = { 0, };
180 struct l2tp_tunnel *tunnel;
181 struct net *net = genl_info_net(info);
182
183 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
184 ret = -EINVAL;
185 goto out;
186 }
187 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
188
189 if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) {
190 ret = -EINVAL;
191 goto out;
192 }
193 peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]);
194
195 if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) {
196 ret = -EINVAL;
197 goto out;
198 }
199 proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]);
200
201 if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) {
202 ret = -EINVAL;
203 goto out;
204 }
205 cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]);
206
James Chapman789a4a2c2010-04-02 06:19:40 +0000207 fd = -1;
208 if (info->attrs[L2TP_ATTR_FD]) {
209 fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
210 } else {
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000211#if IS_ENABLED(CONFIG_IPV6)
212 if (info->attrs[L2TP_ATTR_IP6_SADDR] &&
213 info->attrs[L2TP_ATTR_IP6_DADDR]) {
214 cfg.local_ip6 = nla_data(
215 info->attrs[L2TP_ATTR_IP6_SADDR]);
216 cfg.peer_ip6 = nla_data(
217 info->attrs[L2TP_ATTR_IP6_DADDR]);
218 } else
219#endif
220 if (info->attrs[L2TP_ATTR_IP_SADDR] &&
221 info->attrs[L2TP_ATTR_IP_DADDR]) {
Jiri Benc67b61f62015-03-29 16:59:26 +0200222 cfg.local_ip.s_addr = nla_get_in_addr(
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000223 info->attrs[L2TP_ATTR_IP_SADDR]);
Jiri Benc67b61f62015-03-29 16:59:26 +0200224 cfg.peer_ip.s_addr = nla_get_in_addr(
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000225 info->attrs[L2TP_ATTR_IP_DADDR]);
226 } else {
227 ret = -EINVAL;
228 goto out;
229 }
James Chapman789a4a2c2010-04-02 06:19:40 +0000230 if (info->attrs[L2TP_ATTR_UDP_SPORT])
231 cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]);
232 if (info->attrs[L2TP_ATTR_UDP_DPORT])
233 cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
234 if (info->attrs[L2TP_ATTR_UDP_CSUM])
235 cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]);
Tom Herbert6b649fea2014-05-23 08:47:40 -0700236
237#if IS_ENABLED(CONFIG_IPV6)
238 if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX])
239 cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
240 if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX])
241 cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
242#endif
James Chapman309795f2010-04-02 06:19:10 +0000243 }
James Chapman309795f2010-04-02 06:19:10 +0000244
245 if (info->attrs[L2TP_ATTR_DEBUG])
246 cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
247
248 tunnel = l2tp_tunnel_find(net, tunnel_id);
249 if (tunnel != NULL) {
250 ret = -EEXIST;
251 goto out;
252 }
253
254 ret = -EINVAL;
255 switch (cfg.encap) {
256 case L2TP_ENCAPTYPE_UDP:
257 case L2TP_ENCAPTYPE_IP:
258 ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id,
259 peer_tunnel_id, &cfg, &tunnel);
260 break;
261 }
262
Bill Hong33f72e62014-12-27 10:12:39 -0800263 if (ret >= 0)
264 ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
265 tunnel, L2TP_CMD_TUNNEL_CREATE);
James Chapman309795f2010-04-02 06:19:10 +0000266out:
267 return ret;
268}
269
270static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info)
271{
272 struct l2tp_tunnel *tunnel;
273 u32 tunnel_id;
274 int ret = 0;
275 struct net *net = genl_info_net(info);
276
277 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
278 ret = -EINVAL;
279 goto out;
280 }
281 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
282
283 tunnel = l2tp_tunnel_find(net, tunnel_id);
284 if (tunnel == NULL) {
285 ret = -ENODEV;
286 goto out;
287 }
288
Bill Hong33f72e62014-12-27 10:12:39 -0800289 l2tp_tunnel_notify(&l2tp_nl_family, info,
290 tunnel, L2TP_CMD_TUNNEL_DELETE);
291
Jiri Slabyfc4177e2017-10-25 15:57:55 +0200292 l2tp_tunnel_delete(tunnel);
James Chapman309795f2010-04-02 06:19:10 +0000293
294out:
295 return ret;
296}
297
298static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info)
299{
300 struct l2tp_tunnel *tunnel;
301 u32 tunnel_id;
302 int ret = 0;
303 struct net *net = genl_info_net(info);
304
305 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
306 ret = -EINVAL;
307 goto out;
308 }
309 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
310
311 tunnel = l2tp_tunnel_find(net, tunnel_id);
312 if (tunnel == NULL) {
313 ret = -ENODEV;
314 goto out;
315 }
316
317 if (info->attrs[L2TP_ATTR_DEBUG])
318 tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
319
Bill Hong33f72e62014-12-27 10:12:39 -0800320 ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
321 tunnel, L2TP_CMD_TUNNEL_MODIFY);
322
James Chapman309795f2010-04-02 06:19:10 +0000323out:
324 return ret;
325}
326
Eric W. Biederman15e47302012-09-07 20:12:54 +0000327static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
Bill Hong33f72e62014-12-27 10:12:39 -0800328 struct l2tp_tunnel *tunnel, u8 cmd)
James Chapman309795f2010-04-02 06:19:10 +0000329{
330 void *hdr;
331 struct nlattr *nest;
332 struct sock *sk = NULL;
333 struct inet_sock *inet;
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000334#if IS_ENABLED(CONFIG_IPV6)
335 struct ipv6_pinfo *np = NULL;
336#endif
James Chapman309795f2010-04-02 06:19:10 +0000337
Bill Hong33f72e62014-12-27 10:12:39 -0800338 hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
Wei Yongjun7f8436a2012-09-24 18:29:01 +0000339 if (!hdr)
340 return -EMSGSIZE;
James Chapman309795f2010-04-02 06:19:10 +0000341
David S. Miller60aed2a2012-04-01 19:59:31 -0400342 if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
343 nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
344 nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
345 nla_put_u32(skb, L2TP_ATTR_DEBUG, tunnel->debug) ||
346 nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap))
347 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000348
349 nest = nla_nest_start(skb, L2TP_ATTR_STATS);
350 if (nest == NULL)
351 goto nla_put_failure;
352
Nicolas Dichtel1c714a92016-04-25 10:25:19 +0200353 if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
354 atomic_long_read(&tunnel->stats.tx_packets),
355 L2TP_ATTR_STATS_PAD) ||
356 nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
357 atomic_long_read(&tunnel->stats.tx_bytes),
358 L2TP_ATTR_STATS_PAD) ||
359 nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
360 atomic_long_read(&tunnel->stats.tx_errors),
361 L2TP_ATTR_STATS_PAD) ||
362 nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
363 atomic_long_read(&tunnel->stats.rx_packets),
364 L2TP_ATTR_STATS_PAD) ||
365 nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
366 atomic_long_read(&tunnel->stats.rx_bytes),
367 L2TP_ATTR_STATS_PAD) ||
368 nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
369 atomic_long_read(&tunnel->stats.rx_seq_discards),
370 L2TP_ATTR_STATS_PAD) ||
371 nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
372 atomic_long_read(&tunnel->stats.rx_oos_packets),
373 L2TP_ATTR_STATS_PAD) ||
374 nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
375 atomic_long_read(&tunnel->stats.rx_errors),
376 L2TP_ATTR_STATS_PAD))
David S. Miller60aed2a2012-04-01 19:59:31 -0400377 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000378 nla_nest_end(skb, nest);
379
380 sk = tunnel->sock;
381 if (!sk)
382 goto out;
383
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000384#if IS_ENABLED(CONFIG_IPV6)
385 if (sk->sk_family == AF_INET6)
386 np = inet6_sk(sk);
387#endif
388
James Chapman309795f2010-04-02 06:19:10 +0000389 inet = inet_sk(sk);
390
391 switch (tunnel->encap) {
392 case L2TP_ENCAPTYPE_UDP:
David S. Miller60aed2a2012-04-01 19:59:31 -0400393 if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
394 nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) ||
Tom Herbert28448b82014-05-23 08:47:19 -0700395 nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx))
David S. Miller60aed2a2012-04-01 19:59:31 -0400396 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000397 /* NOBREAK */
398 case L2TP_ENCAPTYPE_IP:
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000399#if IS_ENABLED(CONFIG_IPV6)
400 if (np) {
Jiri Benc930345e2015-03-29 16:59:25 +0200401 if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR,
402 &np->saddr) ||
403 nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR,
404 &sk->sk_v6_daddr))
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000405 goto nla_put_failure;
406 } else
407#endif
Jiri Benc930345e2015-03-29 16:59:25 +0200408 if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR,
409 inet->inet_saddr) ||
410 nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR,
411 inet->inet_daddr))
David S. Miller60aed2a2012-04-01 19:59:31 -0400412 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000413 break;
414 }
415
416out:
Johannes Berg053c0952015-01-16 22:09:00 +0100417 genlmsg_end(skb, hdr);
418 return 0;
James Chapman309795f2010-04-02 06:19:10 +0000419
420nla_put_failure:
421 genlmsg_cancel(skb, hdr);
422 return -1;
423}
424
425static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
426{
427 struct l2tp_tunnel *tunnel;
428 struct sk_buff *msg;
429 u32 tunnel_id;
430 int ret = -ENOBUFS;
431 struct net *net = genl_info_net(info);
432
433 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
434 ret = -EINVAL;
435 goto out;
436 }
437
438 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
439
440 tunnel = l2tp_tunnel_find(net, tunnel_id);
441 if (tunnel == NULL) {
442 ret = -ENODEV;
443 goto out;
444 }
445
Thomas Graf58050fc2012-06-28 03:57:45 +0000446 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
James Chapman309795f2010-04-02 06:19:10 +0000447 if (!msg) {
448 ret = -ENOMEM;
449 goto out;
450 }
451
Eric W. Biederman15e47302012-09-07 20:12:54 +0000452 ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
Bill Hong33f72e62014-12-27 10:12:39 -0800453 NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
James Chapman309795f2010-04-02 06:19:10 +0000454 if (ret < 0)
455 goto err_out;
456
Eric W. Biederman15e47302012-09-07 20:12:54 +0000457 return genlmsg_unicast(net, msg, info->snd_portid);
James Chapman309795f2010-04-02 06:19:10 +0000458
459err_out:
460 nlmsg_free(msg);
461
462out:
463 return ret;
464}
465
466static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb)
467{
468 int ti = cb->args[0];
469 struct l2tp_tunnel *tunnel;
470 struct net *net = sock_net(skb->sk);
471
472 for (;;) {
473 tunnel = l2tp_tunnel_find_nth(net, ti);
474 if (tunnel == NULL)
475 goto out;
476
Eric W. Biederman15e47302012-09-07 20:12:54 +0000477 if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
James Chapman309795f2010-04-02 06:19:10 +0000478 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg053c0952015-01-16 22:09:00 +0100479 tunnel, L2TP_CMD_TUNNEL_GET) < 0)
James Chapman309795f2010-04-02 06:19:10 +0000480 goto out;
481
482 ti++;
483 }
484
485out:
486 cb->args[0] = ti;
487
488 return skb->len;
489}
490
491static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info)
492{
493 u32 tunnel_id = 0;
494 u32 session_id;
495 u32 peer_session_id;
496 int ret = 0;
497 struct l2tp_tunnel *tunnel;
498 struct l2tp_session *session;
499 struct l2tp_session_cfg cfg = { 0, };
500 struct net *net = genl_info_net(info);
501
502 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
503 ret = -EINVAL;
504 goto out;
505 }
506 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
507 tunnel = l2tp_tunnel_find(net, tunnel_id);
508 if (!tunnel) {
509 ret = -ENODEV;
510 goto out;
511 }
512
513 if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
514 ret = -EINVAL;
515 goto out;
516 }
517 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
James Chapman309795f2010-04-02 06:19:10 +0000518
519 if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
520 ret = -EINVAL;
521 goto out;
522 }
523 peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
524
525 if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
526 ret = -EINVAL;
527 goto out;
528 }
529 cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
530 if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
531 ret = -EINVAL;
532 goto out;
533 }
534
535 if (tunnel->version > 2) {
James Chapman309795f2010-04-02 06:19:10 +0000536 if (info->attrs[L2TP_ATTR_DATA_SEQ])
537 cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
538
539 cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT;
540 if (info->attrs[L2TP_ATTR_L2SPEC_TYPE])
541 cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]);
542
543 cfg.l2specific_len = 4;
544 if (info->attrs[L2TP_ATTR_L2SPEC_LEN])
545 cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]);
546
547 if (info->attrs[L2TP_ATTR_COOKIE]) {
548 u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
549 if (len > 8) {
550 ret = -EINVAL;
551 goto out;
552 }
553 cfg.cookie_len = len;
554 memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
555 }
556 if (info->attrs[L2TP_ATTR_PEER_COOKIE]) {
557 u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
558 if (len > 8) {
559 ret = -EINVAL;
560 goto out;
561 }
562 cfg.peer_cookie_len = len;
563 memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
564 }
565 if (info->attrs[L2TP_ATTR_IFNAME])
566 cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
567
568 if (info->attrs[L2TP_ATTR_VLAN_ID])
569 cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]);
570 }
571
572 if (info->attrs[L2TP_ATTR_DEBUG])
573 cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
574
575 if (info->attrs[L2TP_ATTR_RECV_SEQ])
576 cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
577
578 if (info->attrs[L2TP_ATTR_SEND_SEQ])
579 cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
580
581 if (info->attrs[L2TP_ATTR_LNS_MODE])
582 cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
583
584 if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
585 cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
586
587 if (info->attrs[L2TP_ATTR_MTU])
588 cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
589
590 if (info->attrs[L2TP_ATTR_MRU])
591 cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
592
stephen hemmingerf1f39f92015-09-23 21:33:34 -0700593#ifdef CONFIG_MODULES
594 if (l2tp_nl_cmd_ops[cfg.pw_type] == NULL) {
595 genl_unlock();
596 request_module("net-l2tp-type-%u", cfg.pw_type);
597 genl_lock();
598 }
599#endif
James Chapman309795f2010-04-02 06:19:10 +0000600 if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
601 (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
602 ret = -EPROTONOSUPPORT;
603 goto out;
604 }
605
606 /* Check that pseudowire-specific params are present */
607 switch (cfg.pw_type) {
608 case L2TP_PWTYPE_NONE:
609 break;
610 case L2TP_PWTYPE_ETH_VLAN:
611 if (!info->attrs[L2TP_ATTR_VLAN_ID]) {
612 ret = -EINVAL;
613 goto out;
614 }
615 break;
616 case L2TP_PWTYPE_ETH:
617 break;
618 case L2TP_PWTYPE_PPP:
619 case L2TP_PWTYPE_PPP_AC:
620 break;
621 case L2TP_PWTYPE_IP:
622 default:
623 ret = -EPROTONOSUPPORT;
624 break;
625 }
626
627 ret = -EPROTONOSUPPORT;
628 if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create)
629 ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id,
630 session_id, peer_session_id, &cfg);
631
Bill Hong33f72e62014-12-27 10:12:39 -0800632 if (ret >= 0) {
Guillaume Nault599e6f02017-03-31 13:02:29 +0200633 session = l2tp_session_get(net, tunnel, session_id, false);
634 if (session) {
Bill Hong33f72e62014-12-27 10:12:39 -0800635 ret = l2tp_session_notify(&l2tp_nl_family, info, session,
636 L2TP_CMD_SESSION_CREATE);
Guillaume Nault599e6f02017-03-31 13:02:29 +0200637 l2tp_session_dec_refcount(session);
638 }
Bill Hong33f72e62014-12-27 10:12:39 -0800639 }
640
James Chapman309795f2010-04-02 06:19:10 +0000641out:
642 return ret;
643}
644
645static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info)
646{
647 int ret = 0;
648 struct l2tp_session *session;
649 u16 pw_type;
650
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200651 session = l2tp_nl_session_get(info, true);
James Chapman309795f2010-04-02 06:19:10 +0000652 if (session == NULL) {
653 ret = -ENODEV;
654 goto out;
655 }
656
Bill Hong33f72e62014-12-27 10:12:39 -0800657 l2tp_session_notify(&l2tp_nl_family, info,
658 session, L2TP_CMD_SESSION_DELETE);
659
James Chapman309795f2010-04-02 06:19:10 +0000660 pw_type = session->pwtype;
661 if (pw_type < __L2TP_PWTYPE_MAX)
662 if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
663 ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
664
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200665 if (session->deref)
666 session->deref(session);
667 l2tp_session_dec_refcount(session);
668
James Chapman309795f2010-04-02 06:19:10 +0000669out:
670 return ret;
671}
672
673static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info)
674{
675 int ret = 0;
676 struct l2tp_session *session;
677
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200678 session = l2tp_nl_session_get(info, false);
James Chapman309795f2010-04-02 06:19:10 +0000679 if (session == NULL) {
680 ret = -ENODEV;
681 goto out;
682 }
683
684 if (info->attrs[L2TP_ATTR_DEBUG])
685 session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
686
687 if (info->attrs[L2TP_ATTR_DATA_SEQ])
688 session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
689
690 if (info->attrs[L2TP_ATTR_RECV_SEQ])
691 session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
692
Guillaume Naultbb5016e2014-03-06 11:14:30 +0100693 if (info->attrs[L2TP_ATTR_SEND_SEQ]) {
James Chapman309795f2010-04-02 06:19:10 +0000694 session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
Guillaume Naultbb5016e2014-03-06 11:14:30 +0100695 l2tp_session_set_header_len(session, session->tunnel->version);
696 }
James Chapman309795f2010-04-02 06:19:10 +0000697
698 if (info->attrs[L2TP_ATTR_LNS_MODE])
699 session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
700
701 if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
702 session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
703
704 if (info->attrs[L2TP_ATTR_MTU])
705 session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
706
707 if (info->attrs[L2TP_ATTR_MRU])
708 session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
709
Bill Hong33f72e62014-12-27 10:12:39 -0800710 ret = l2tp_session_notify(&l2tp_nl_family, info,
711 session, L2TP_CMD_SESSION_MODIFY);
712
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200713 l2tp_session_dec_refcount(session);
714
James Chapman309795f2010-04-02 06:19:10 +0000715out:
716 return ret;
717}
718
Eric W. Biederman15e47302012-09-07 20:12:54 +0000719static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
Bill Hong33f72e62014-12-27 10:12:39 -0800720 struct l2tp_session *session, u8 cmd)
James Chapman309795f2010-04-02 06:19:10 +0000721{
722 void *hdr;
723 struct nlattr *nest;
724 struct l2tp_tunnel *tunnel = session->tunnel;
725 struct sock *sk = NULL;
726
727 sk = tunnel->sock;
728
Bill Hong33f72e62014-12-27 10:12:39 -0800729 hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
Wei Yongjun7f8436a2012-09-24 18:29:01 +0000730 if (!hdr)
731 return -EMSGSIZE;
James Chapman309795f2010-04-02 06:19:10 +0000732
David S. Miller60aed2a2012-04-01 19:59:31 -0400733 if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
734 nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
735 nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
736 nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID,
737 session->peer_session_id) ||
738 nla_put_u32(skb, L2TP_ATTR_DEBUG, session->debug) ||
739 nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype) ||
740 nla_put_u16(skb, L2TP_ATTR_MTU, session->mtu) ||
741 (session->mru &&
742 nla_put_u16(skb, L2TP_ATTR_MRU, session->mru)))
743 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000744
Alan Coxe269ed22012-10-25 05:22:01 +0000745 if ((session->ifname[0] &&
David S. Miller60aed2a2012-04-01 19:59:31 -0400746 nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
747 (session->cookie_len &&
748 nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
749 &session->cookie[0])) ||
750 (session->peer_cookie_len &&
751 nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len,
752 &session->peer_cookie[0])) ||
753 nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) ||
754 nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) ||
755 nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) ||
James Chapman309795f2010-04-02 06:19:10 +0000756#ifdef CONFIG_XFRM
David S. Miller60aed2a2012-04-01 19:59:31 -0400757 (((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) &&
758 nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) ||
James Chapman309795f2010-04-02 06:19:10 +0000759#endif
David S. Miller60aed2a2012-04-01 19:59:31 -0400760 (session->reorder_timeout &&
Nicolas Dichtel2175d872016-04-22 17:31:21 +0200761 nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT,
762 session->reorder_timeout, L2TP_ATTR_PAD)))
David S. Miller60aed2a2012-04-01 19:59:31 -0400763 goto nla_put_failure;
James Chapman5de7aee2012-04-29 21:48:46 +0000764
James Chapman309795f2010-04-02 06:19:10 +0000765 nest = nla_nest_start(skb, L2TP_ATTR_STATS);
766 if (nest == NULL)
767 goto nla_put_failure;
James Chapman5de7aee2012-04-29 21:48:46 +0000768
Nicolas Dichtel1c714a92016-04-25 10:25:19 +0200769 if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
770 atomic_long_read(&session->stats.tx_packets),
771 L2TP_ATTR_STATS_PAD) ||
772 nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
773 atomic_long_read(&session->stats.tx_bytes),
774 L2TP_ATTR_STATS_PAD) ||
775 nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
776 atomic_long_read(&session->stats.tx_errors),
777 L2TP_ATTR_STATS_PAD) ||
778 nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
779 atomic_long_read(&session->stats.rx_packets),
780 L2TP_ATTR_STATS_PAD) ||
781 nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
782 atomic_long_read(&session->stats.rx_bytes),
783 L2TP_ATTR_STATS_PAD) ||
784 nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
785 atomic_long_read(&session->stats.rx_seq_discards),
786 L2TP_ATTR_STATS_PAD) ||
787 nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
788 atomic_long_read(&session->stats.rx_oos_packets),
789 L2TP_ATTR_STATS_PAD) ||
790 nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
791 atomic_long_read(&session->stats.rx_errors),
792 L2TP_ATTR_STATS_PAD))
David S. Miller60aed2a2012-04-01 19:59:31 -0400793 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000794 nla_nest_end(skb, nest);
795
Johannes Berg053c0952015-01-16 22:09:00 +0100796 genlmsg_end(skb, hdr);
797 return 0;
James Chapman309795f2010-04-02 06:19:10 +0000798
799 nla_put_failure:
800 genlmsg_cancel(skb, hdr);
801 return -1;
802}
803
804static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
805{
806 struct l2tp_session *session;
807 struct sk_buff *msg;
808 int ret;
809
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200810 session = l2tp_nl_session_get(info, false);
James Chapman309795f2010-04-02 06:19:10 +0000811 if (session == NULL) {
812 ret = -ENODEV;
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200813 goto err;
James Chapman309795f2010-04-02 06:19:10 +0000814 }
815
Thomas Graf58050fc2012-06-28 03:57:45 +0000816 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
James Chapman309795f2010-04-02 06:19:10 +0000817 if (!msg) {
818 ret = -ENOMEM;
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200819 goto err_ref;
James Chapman309795f2010-04-02 06:19:10 +0000820 }
821
Eric W. Biederman15e47302012-09-07 20:12:54 +0000822 ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
Bill Hong33f72e62014-12-27 10:12:39 -0800823 0, session, L2TP_CMD_SESSION_GET);
James Chapman309795f2010-04-02 06:19:10 +0000824 if (ret < 0)
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200825 goto err_ref_msg;
James Chapman309795f2010-04-02 06:19:10 +0000826
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200827 ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
James Chapman309795f2010-04-02 06:19:10 +0000828
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200829 l2tp_session_dec_refcount(session);
830
831 return ret;
832
833err_ref_msg:
James Chapman309795f2010-04-02 06:19:10 +0000834 nlmsg_free(msg);
Guillaume Nault08cb8e52017-03-31 13:02:30 +0200835err_ref:
836 l2tp_session_dec_refcount(session);
837err:
James Chapman309795f2010-04-02 06:19:10 +0000838 return ret;
839}
840
841static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb)
842{
843 struct net *net = sock_net(skb->sk);
844 struct l2tp_session *session;
845 struct l2tp_tunnel *tunnel = NULL;
846 int ti = cb->args[0];
847 int si = cb->args[1];
848
849 for (;;) {
850 if (tunnel == NULL) {
851 tunnel = l2tp_tunnel_find_nth(net, ti);
852 if (tunnel == NULL)
853 goto out;
854 }
855
Guillaume Naultb7902602017-04-03 12:03:13 +0200856 session = l2tp_session_get_nth(tunnel, si, false);
James Chapman309795f2010-04-02 06:19:10 +0000857 if (session == NULL) {
858 ti++;
859 tunnel = NULL;
860 si = 0;
861 continue;
862 }
863
Eric W. Biederman15e47302012-09-07 20:12:54 +0000864 if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
James Chapman309795f2010-04-02 06:19:10 +0000865 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Guillaume Naultb7902602017-04-03 12:03:13 +0200866 session, L2TP_CMD_SESSION_GET) < 0) {
867 l2tp_session_dec_refcount(session);
James Chapman309795f2010-04-02 06:19:10 +0000868 break;
Guillaume Naultb7902602017-04-03 12:03:13 +0200869 }
870 l2tp_session_dec_refcount(session);
James Chapman309795f2010-04-02 06:19:10 +0000871
872 si++;
873 }
874
875out:
876 cb->args[0] = ti;
877 cb->args[1] = si;
878
879 return skb->len;
880}
881
stephen hemmingerf5bb3412016-08-31 23:24:41 -0700882static const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
James Chapman309795f2010-04-02 06:19:10 +0000883 [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, },
884 [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, },
885 [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, },
886 [L2TP_ATTR_OFFSET] = { .type = NLA_U16, },
887 [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, },
888 [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, },
889 [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, },
890 [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, },
891 [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, },
892 [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, },
893 [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, },
894 [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, },
895 [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, },
896 [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, },
897 [L2TP_ATTR_DEBUG] = { .type = NLA_U32, },
898 [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, },
899 [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, },
900 [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, },
901 [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, },
902 [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, },
903 [L2TP_ATTR_FD] = { .type = NLA_U32, },
904 [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, },
905 [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, },
906 [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, },
907 [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, },
908 [L2TP_ATTR_MTU] = { .type = NLA_U16, },
909 [L2TP_ATTR_MRU] = { .type = NLA_U16, },
910 [L2TP_ATTR_STATS] = { .type = NLA_NESTED, },
Chris Elstonf9bac8d2012-04-29 21:48:52 +0000911 [L2TP_ATTR_IP6_SADDR] = {
912 .type = NLA_BINARY,
913 .len = sizeof(struct in6_addr),
914 },
915 [L2TP_ATTR_IP6_DADDR] = {
916 .type = NLA_BINARY,
917 .len = sizeof(struct in6_addr),
918 },
James Chapman309795f2010-04-02 06:19:10 +0000919 [L2TP_ATTR_IFNAME] = {
920 .type = NLA_NUL_STRING,
921 .len = IFNAMSIZ - 1,
922 },
923 [L2TP_ATTR_COOKIE] = {
924 .type = NLA_BINARY,
925 .len = 8,
926 },
927 [L2TP_ATTR_PEER_COOKIE] = {
928 .type = NLA_BINARY,
929 .len = 8,
930 },
931};
932
Johannes Berg4534de82013-11-14 17:14:46 +0100933static const struct genl_ops l2tp_nl_ops[] = {
James Chapman309795f2010-04-02 06:19:10 +0000934 {
935 .cmd = L2TP_CMD_NOOP,
936 .doit = l2tp_nl_cmd_noop,
937 .policy = l2tp_nl_policy,
938 /* can be retrieved by unprivileged users */
939 },
940 {
941 .cmd = L2TP_CMD_TUNNEL_CREATE,
942 .doit = l2tp_nl_cmd_tunnel_create,
943 .policy = l2tp_nl_policy,
944 .flags = GENL_ADMIN_PERM,
945 },
946 {
947 .cmd = L2TP_CMD_TUNNEL_DELETE,
948 .doit = l2tp_nl_cmd_tunnel_delete,
949 .policy = l2tp_nl_policy,
950 .flags = GENL_ADMIN_PERM,
951 },
952 {
953 .cmd = L2TP_CMD_TUNNEL_MODIFY,
954 .doit = l2tp_nl_cmd_tunnel_modify,
955 .policy = l2tp_nl_policy,
956 .flags = GENL_ADMIN_PERM,
957 },
958 {
959 .cmd = L2TP_CMD_TUNNEL_GET,
960 .doit = l2tp_nl_cmd_tunnel_get,
961 .dumpit = l2tp_nl_cmd_tunnel_dump,
962 .policy = l2tp_nl_policy,
963 .flags = GENL_ADMIN_PERM,
964 },
965 {
966 .cmd = L2TP_CMD_SESSION_CREATE,
967 .doit = l2tp_nl_cmd_session_create,
968 .policy = l2tp_nl_policy,
969 .flags = GENL_ADMIN_PERM,
970 },
971 {
972 .cmd = L2TP_CMD_SESSION_DELETE,
973 .doit = l2tp_nl_cmd_session_delete,
974 .policy = l2tp_nl_policy,
975 .flags = GENL_ADMIN_PERM,
976 },
977 {
978 .cmd = L2TP_CMD_SESSION_MODIFY,
979 .doit = l2tp_nl_cmd_session_modify,
980 .policy = l2tp_nl_policy,
981 .flags = GENL_ADMIN_PERM,
982 },
983 {
984 .cmd = L2TP_CMD_SESSION_GET,
985 .doit = l2tp_nl_cmd_session_get,
986 .dumpit = l2tp_nl_cmd_session_dump,
987 .policy = l2tp_nl_policy,
988 .flags = GENL_ADMIN_PERM,
989 },
990};
991
992int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops)
993{
994 int ret;
995
996 ret = -EINVAL;
997 if (pw_type >= __L2TP_PWTYPE_MAX)
998 goto err;
999
1000 genl_lock();
1001 ret = -EBUSY;
1002 if (l2tp_nl_cmd_ops[pw_type])
1003 goto out;
1004
1005 l2tp_nl_cmd_ops[pw_type] = ops;
David S. Miller8cb490142011-04-17 17:01:05 -07001006 ret = 0;
James Chapman309795f2010-04-02 06:19:10 +00001007
1008out:
1009 genl_unlock();
1010err:
David S. Miller8cb490142011-04-17 17:01:05 -07001011 return ret;
James Chapman309795f2010-04-02 06:19:10 +00001012}
1013EXPORT_SYMBOL_GPL(l2tp_nl_register_ops);
1014
1015void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type)
1016{
1017 if (pw_type < __L2TP_PWTYPE_MAX) {
1018 genl_lock();
1019 l2tp_nl_cmd_ops[pw_type] = NULL;
1020 genl_unlock();
1021 }
1022}
1023EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
1024
1025static int l2tp_nl_init(void)
1026{
Joe Perchesa4ca44f2012-05-16 09:55:56 +00001027 pr_info("L2TP netlink interface\n");
Bill Hong33f72e62014-12-27 10:12:39 -08001028 return genl_register_family_with_ops_groups(&l2tp_nl_family,
1029 l2tp_nl_ops,
1030 l2tp_multicast_group);
James Chapman309795f2010-04-02 06:19:10 +00001031}
1032
1033static void l2tp_nl_cleanup(void)
1034{
1035 genl_unregister_family(&l2tp_nl_family);
1036}
1037
1038module_init(l2tp_nl_init);
1039module_exit(l2tp_nl_cleanup);
1040
1041MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
1042MODULE_DESCRIPTION("L2TP netlink");
1043MODULE_LICENSE("GPL");
1044MODULE_VERSION("1.0");
Neil Hormane9412c32012-05-29 09:30:41 +00001045MODULE_ALIAS_GENL_FAMILY("l2tp");