blob: bc8c3348f835b1321f18847cc7f9c4f6f73d91be [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
17#include <net/sock.h>
18#include <net/genetlink.h>
19#include <net/udp.h>
20#include <linux/in.h>
21#include <linux/udp.h>
22#include <linux/socket.h>
23#include <linux/module.h>
24#include <linux/list.h>
25#include <net/net_namespace.h>
26
27#include <linux/l2tp.h>
28
29#include "l2tp_core.h"
30
31
32static struct genl_family l2tp_nl_family = {
33 .id = GENL_ID_GENERATE,
34 .name = L2TP_GENL_NAME,
35 .version = L2TP_GENL_VERSION,
36 .hdrsize = 0,
37 .maxattr = L2TP_ATTR_MAX,
38};
39
40/* Accessed under genl lock */
41static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
42
43static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info)
44{
45 u32 tunnel_id;
46 u32 session_id;
47 char *ifname;
48 struct l2tp_tunnel *tunnel;
49 struct l2tp_session *session = NULL;
50 struct net *net = genl_info_net(info);
51
52 if (info->attrs[L2TP_ATTR_IFNAME]) {
53 ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
54 session = l2tp_session_find_by_ifname(net, ifname);
55 } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
56 (info->attrs[L2TP_ATTR_CONN_ID])) {
57 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
58 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
59 tunnel = l2tp_tunnel_find(net, tunnel_id);
60 if (tunnel)
61 session = l2tp_session_find(net, tunnel, session_id);
62 }
63
64 return session;
65}
66
67static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
68{
69 struct sk_buff *msg;
70 void *hdr;
71 int ret = -ENOBUFS;
72
73 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
74 if (!msg) {
75 ret = -ENOMEM;
76 goto out;
77 }
78
79 hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
80 &l2tp_nl_family, 0, L2TP_CMD_NOOP);
81 if (IS_ERR(hdr)) {
82 ret = PTR_ERR(hdr);
83 goto err_out;
84 }
85
86 genlmsg_end(msg, hdr);
87
88 return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
89
90err_out:
91 nlmsg_free(msg);
92
93out:
94 return ret;
95}
96
97static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info)
98{
99 u32 tunnel_id;
100 u32 peer_tunnel_id;
101 int proto_version;
102 int fd;
103 int ret = 0;
104 struct l2tp_tunnel_cfg cfg = { 0, };
105 struct l2tp_tunnel *tunnel;
106 struct net *net = genl_info_net(info);
107
108 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
109 ret = -EINVAL;
110 goto out;
111 }
112 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
113
114 if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) {
115 ret = -EINVAL;
116 goto out;
117 }
118 peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]);
119
120 if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) {
121 ret = -EINVAL;
122 goto out;
123 }
124 proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]);
125
126 if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) {
127 ret = -EINVAL;
128 goto out;
129 }
130 cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]);
131
James Chapman789a4a22010-04-02 06:19:40 +0000132 fd = -1;
133 if (info->attrs[L2TP_ATTR_FD]) {
134 fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
135 } else {
136 if (info->attrs[L2TP_ATTR_IP_SADDR])
137 cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]);
138 if (info->attrs[L2TP_ATTR_IP_DADDR])
139 cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]);
140 if (info->attrs[L2TP_ATTR_UDP_SPORT])
141 cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]);
142 if (info->attrs[L2TP_ATTR_UDP_DPORT])
143 cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
144 if (info->attrs[L2TP_ATTR_UDP_CSUM])
145 cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]);
James Chapman309795f2010-04-02 06:19:10 +0000146 }
James Chapman309795f2010-04-02 06:19:10 +0000147
148 if (info->attrs[L2TP_ATTR_DEBUG])
149 cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
150
151 tunnel = l2tp_tunnel_find(net, tunnel_id);
152 if (tunnel != NULL) {
153 ret = -EEXIST;
154 goto out;
155 }
156
157 ret = -EINVAL;
158 switch (cfg.encap) {
159 case L2TP_ENCAPTYPE_UDP:
160 case L2TP_ENCAPTYPE_IP:
161 ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id,
162 peer_tunnel_id, &cfg, &tunnel);
163 break;
164 }
165
166out:
167 return ret;
168}
169
170static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info)
171{
172 struct l2tp_tunnel *tunnel;
173 u32 tunnel_id;
174 int ret = 0;
175 struct net *net = genl_info_net(info);
176
177 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
178 ret = -EINVAL;
179 goto out;
180 }
181 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
182
183 tunnel = l2tp_tunnel_find(net, tunnel_id);
184 if (tunnel == NULL) {
185 ret = -ENODEV;
186 goto out;
187 }
188
189 (void) l2tp_tunnel_delete(tunnel);
190
191out:
192 return ret;
193}
194
195static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info)
196{
197 struct l2tp_tunnel *tunnel;
198 u32 tunnel_id;
199 int ret = 0;
200 struct net *net = genl_info_net(info);
201
202 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
203 ret = -EINVAL;
204 goto out;
205 }
206 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
207
208 tunnel = l2tp_tunnel_find(net, tunnel_id);
209 if (tunnel == NULL) {
210 ret = -ENODEV;
211 goto out;
212 }
213
214 if (info->attrs[L2TP_ATTR_DEBUG])
215 tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
216
217out:
218 return ret;
219}
220
221static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
222 struct l2tp_tunnel *tunnel)
223{
224 void *hdr;
225 struct nlattr *nest;
226 struct sock *sk = NULL;
227 struct inet_sock *inet;
228
229 hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags,
230 L2TP_CMD_TUNNEL_GET);
231 if (IS_ERR(hdr))
232 return PTR_ERR(hdr);
233
David S. Miller60aed2a2012-04-01 19:59:31 -0400234 if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
235 nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
236 nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
237 nla_put_u32(skb, L2TP_ATTR_DEBUG, tunnel->debug) ||
238 nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap))
239 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000240
241 nest = nla_nest_start(skb, L2TP_ATTR_STATS);
242 if (nest == NULL)
243 goto nla_put_failure;
244
David S. Miller60aed2a2012-04-01 19:59:31 -0400245 if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, tunnel->stats.tx_packets) ||
246 nla_put_u64(skb, L2TP_ATTR_TX_BYTES, tunnel->stats.tx_bytes) ||
247 nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, tunnel->stats.tx_errors) ||
248 nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, tunnel->stats.rx_packets) ||
249 nla_put_u64(skb, L2TP_ATTR_RX_BYTES, tunnel->stats.rx_bytes) ||
250 nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
251 tunnel->stats.rx_seq_discards) ||
252 nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS,
253 tunnel->stats.rx_oos_packets) ||
254 nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, tunnel->stats.rx_errors))
255 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000256 nla_nest_end(skb, nest);
257
258 sk = tunnel->sock;
259 if (!sk)
260 goto out;
261
262 inet = inet_sk(sk);
263
264 switch (tunnel->encap) {
265 case L2TP_ENCAPTYPE_UDP:
David S. Miller60aed2a2012-04-01 19:59:31 -0400266 if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
267 nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) ||
268 nla_put_u8(skb, L2TP_ATTR_UDP_CSUM,
269 (sk->sk_no_check != UDP_CSUM_NOXMIT)))
270 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000271 /* NOBREAK */
272 case L2TP_ENCAPTYPE_IP:
David S. Miller60aed2a2012-04-01 19:59:31 -0400273 if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) ||
274 nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr))
275 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000276 break;
277 }
278
279out:
280 return genlmsg_end(skb, hdr);
281
282nla_put_failure:
283 genlmsg_cancel(skb, hdr);
284 return -1;
285}
286
287static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
288{
289 struct l2tp_tunnel *tunnel;
290 struct sk_buff *msg;
291 u32 tunnel_id;
292 int ret = -ENOBUFS;
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
300 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
301
302 tunnel = l2tp_tunnel_find(net, tunnel_id);
303 if (tunnel == NULL) {
304 ret = -ENODEV;
305 goto out;
306 }
307
308 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
309 if (!msg) {
310 ret = -ENOMEM;
311 goto out;
312 }
313
314 ret = l2tp_nl_tunnel_send(msg, info->snd_pid, info->snd_seq,
315 NLM_F_ACK, tunnel);
316 if (ret < 0)
317 goto err_out;
318
319 return genlmsg_unicast(net, msg, info->snd_pid);
320
321err_out:
322 nlmsg_free(msg);
323
324out:
325 return ret;
326}
327
328static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb)
329{
330 int ti = cb->args[0];
331 struct l2tp_tunnel *tunnel;
332 struct net *net = sock_net(skb->sk);
333
334 for (;;) {
335 tunnel = l2tp_tunnel_find_nth(net, ti);
336 if (tunnel == NULL)
337 goto out;
338
339 if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).pid,
340 cb->nlh->nlmsg_seq, NLM_F_MULTI,
341 tunnel) <= 0)
342 goto out;
343
344 ti++;
345 }
346
347out:
348 cb->args[0] = ti;
349
350 return skb->len;
351}
352
353static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info)
354{
355 u32 tunnel_id = 0;
356 u32 session_id;
357 u32 peer_session_id;
358 int ret = 0;
359 struct l2tp_tunnel *tunnel;
360 struct l2tp_session *session;
361 struct l2tp_session_cfg cfg = { 0, };
362 struct net *net = genl_info_net(info);
363
364 if (!info->attrs[L2TP_ATTR_CONN_ID]) {
365 ret = -EINVAL;
366 goto out;
367 }
368 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
369 tunnel = l2tp_tunnel_find(net, tunnel_id);
370 if (!tunnel) {
371 ret = -ENODEV;
372 goto out;
373 }
374
375 if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
376 ret = -EINVAL;
377 goto out;
378 }
379 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
380 session = l2tp_session_find(net, tunnel, session_id);
381 if (session) {
382 ret = -EEXIST;
383 goto out;
384 }
385
386 if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
387 ret = -EINVAL;
388 goto out;
389 }
390 peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
391
392 if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
393 ret = -EINVAL;
394 goto out;
395 }
396 cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
397 if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
398 ret = -EINVAL;
399 goto out;
400 }
401
402 if (tunnel->version > 2) {
403 if (info->attrs[L2TP_ATTR_OFFSET])
404 cfg.offset = nla_get_u16(info->attrs[L2TP_ATTR_OFFSET]);
405
406 if (info->attrs[L2TP_ATTR_DATA_SEQ])
407 cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
408
409 cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT;
410 if (info->attrs[L2TP_ATTR_L2SPEC_TYPE])
411 cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]);
412
413 cfg.l2specific_len = 4;
414 if (info->attrs[L2TP_ATTR_L2SPEC_LEN])
415 cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]);
416
417 if (info->attrs[L2TP_ATTR_COOKIE]) {
418 u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
419 if (len > 8) {
420 ret = -EINVAL;
421 goto out;
422 }
423 cfg.cookie_len = len;
424 memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
425 }
426 if (info->attrs[L2TP_ATTR_PEER_COOKIE]) {
427 u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
428 if (len > 8) {
429 ret = -EINVAL;
430 goto out;
431 }
432 cfg.peer_cookie_len = len;
433 memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
434 }
435 if (info->attrs[L2TP_ATTR_IFNAME])
436 cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
437
438 if (info->attrs[L2TP_ATTR_VLAN_ID])
439 cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]);
440 }
441
442 if (info->attrs[L2TP_ATTR_DEBUG])
443 cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
444
445 if (info->attrs[L2TP_ATTR_RECV_SEQ])
446 cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
447
448 if (info->attrs[L2TP_ATTR_SEND_SEQ])
449 cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
450
451 if (info->attrs[L2TP_ATTR_LNS_MODE])
452 cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
453
454 if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
455 cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
456
457 if (info->attrs[L2TP_ATTR_MTU])
458 cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
459
460 if (info->attrs[L2TP_ATTR_MRU])
461 cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
462
463 if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
464 (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
465 ret = -EPROTONOSUPPORT;
466 goto out;
467 }
468
469 /* Check that pseudowire-specific params are present */
470 switch (cfg.pw_type) {
471 case L2TP_PWTYPE_NONE:
472 break;
473 case L2TP_PWTYPE_ETH_VLAN:
474 if (!info->attrs[L2TP_ATTR_VLAN_ID]) {
475 ret = -EINVAL;
476 goto out;
477 }
478 break;
479 case L2TP_PWTYPE_ETH:
480 break;
481 case L2TP_PWTYPE_PPP:
482 case L2TP_PWTYPE_PPP_AC:
483 break;
484 case L2TP_PWTYPE_IP:
485 default:
486 ret = -EPROTONOSUPPORT;
487 break;
488 }
489
490 ret = -EPROTONOSUPPORT;
491 if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create)
492 ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id,
493 session_id, peer_session_id, &cfg);
494
495out:
496 return ret;
497}
498
499static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info)
500{
501 int ret = 0;
502 struct l2tp_session *session;
503 u16 pw_type;
504
505 session = l2tp_nl_session_find(info);
506 if (session == NULL) {
507 ret = -ENODEV;
508 goto out;
509 }
510
511 pw_type = session->pwtype;
512 if (pw_type < __L2TP_PWTYPE_MAX)
513 if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
514 ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
515
516out:
517 return ret;
518}
519
520static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info)
521{
522 int ret = 0;
523 struct l2tp_session *session;
524
525 session = l2tp_nl_session_find(info);
526 if (session == NULL) {
527 ret = -ENODEV;
528 goto out;
529 }
530
531 if (info->attrs[L2TP_ATTR_DEBUG])
532 session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
533
534 if (info->attrs[L2TP_ATTR_DATA_SEQ])
535 session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
536
537 if (info->attrs[L2TP_ATTR_RECV_SEQ])
538 session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
539
540 if (info->attrs[L2TP_ATTR_SEND_SEQ])
541 session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
542
543 if (info->attrs[L2TP_ATTR_LNS_MODE])
544 session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
545
546 if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
547 session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
548
549 if (info->attrs[L2TP_ATTR_MTU])
550 session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
551
552 if (info->attrs[L2TP_ATTR_MRU])
553 session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
554
555out:
556 return ret;
557}
558
559static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
560 struct l2tp_session *session)
561{
562 void *hdr;
563 struct nlattr *nest;
564 struct l2tp_tunnel *tunnel = session->tunnel;
565 struct sock *sk = NULL;
566
567 sk = tunnel->sock;
568
569 hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET);
570 if (IS_ERR(hdr))
571 return PTR_ERR(hdr);
572
David S. Miller60aed2a2012-04-01 19:59:31 -0400573 if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
574 nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
575 nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
576 nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID,
577 session->peer_session_id) ||
578 nla_put_u32(skb, L2TP_ATTR_DEBUG, session->debug) ||
579 nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype) ||
580 nla_put_u16(skb, L2TP_ATTR_MTU, session->mtu) ||
581 (session->mru &&
582 nla_put_u16(skb, L2TP_ATTR_MRU, session->mru)))
583 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000584
David S. Miller60aed2a2012-04-01 19:59:31 -0400585 if ((session->ifname && session->ifname[0] &&
586 nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
587 (session->cookie_len &&
588 nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
589 &session->cookie[0])) ||
590 (session->peer_cookie_len &&
591 nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len,
592 &session->peer_cookie[0])) ||
593 nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) ||
594 nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) ||
595 nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) ||
James Chapman309795f2010-04-02 06:19:10 +0000596#ifdef CONFIG_XFRM
David S. Miller60aed2a2012-04-01 19:59:31 -0400597 (((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) &&
598 nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) ||
James Chapman309795f2010-04-02 06:19:10 +0000599#endif
David S. Miller60aed2a2012-04-01 19:59:31 -0400600 (session->reorder_timeout &&
601 nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT, session->reorder_timeout)))
602 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000603 nest = nla_nest_start(skb, L2TP_ATTR_STATS);
604 if (nest == NULL)
605 goto nla_put_failure;
David S. Miller60aed2a2012-04-01 19:59:31 -0400606 if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, session->stats.tx_packets) ||
607 nla_put_u64(skb, L2TP_ATTR_TX_BYTES, session->stats.tx_bytes) ||
608 nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, session->stats.tx_errors) ||
609 nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, session->stats.rx_packets) ||
610 nla_put_u64(skb, L2TP_ATTR_RX_BYTES, session->stats.rx_bytes) ||
611 nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
612 session->stats.rx_seq_discards) ||
613 nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS,
614 session->stats.rx_oos_packets) ||
615 nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, session->stats.rx_errors))
616 goto nla_put_failure;
James Chapman309795f2010-04-02 06:19:10 +0000617 nla_nest_end(skb, nest);
618
619 return genlmsg_end(skb, hdr);
620
621 nla_put_failure:
622 genlmsg_cancel(skb, hdr);
623 return -1;
624}
625
626static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
627{
628 struct l2tp_session *session;
629 struct sk_buff *msg;
630 int ret;
631
632 session = l2tp_nl_session_find(info);
633 if (session == NULL) {
634 ret = -ENODEV;
635 goto out;
636 }
637
638 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
639 if (!msg) {
640 ret = -ENOMEM;
641 goto out;
642 }
643
644 ret = l2tp_nl_session_send(msg, info->snd_pid, info->snd_seq,
645 0, session);
646 if (ret < 0)
647 goto err_out;
648
649 return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
650
651err_out:
652 nlmsg_free(msg);
653
654out:
655 return ret;
656}
657
658static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb)
659{
660 struct net *net = sock_net(skb->sk);
661 struct l2tp_session *session;
662 struct l2tp_tunnel *tunnel = NULL;
663 int ti = cb->args[0];
664 int si = cb->args[1];
665
666 for (;;) {
667 if (tunnel == NULL) {
668 tunnel = l2tp_tunnel_find_nth(net, ti);
669 if (tunnel == NULL)
670 goto out;
671 }
672
673 session = l2tp_session_find_nth(tunnel, si);
674 if (session == NULL) {
675 ti++;
676 tunnel = NULL;
677 si = 0;
678 continue;
679 }
680
681 if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).pid,
682 cb->nlh->nlmsg_seq, NLM_F_MULTI,
683 session) <= 0)
684 break;
685
686 si++;
687 }
688
689out:
690 cb->args[0] = ti;
691 cb->args[1] = si;
692
693 return skb->len;
694}
695
696static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
697 [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, },
698 [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, },
699 [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, },
700 [L2TP_ATTR_OFFSET] = { .type = NLA_U16, },
701 [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, },
702 [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, },
703 [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, },
704 [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, },
705 [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, },
706 [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, },
707 [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, },
708 [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, },
709 [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, },
710 [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, },
711 [L2TP_ATTR_DEBUG] = { .type = NLA_U32, },
712 [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, },
713 [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, },
714 [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, },
715 [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, },
716 [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, },
717 [L2TP_ATTR_FD] = { .type = NLA_U32, },
718 [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, },
719 [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, },
720 [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, },
721 [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, },
722 [L2TP_ATTR_MTU] = { .type = NLA_U16, },
723 [L2TP_ATTR_MRU] = { .type = NLA_U16, },
724 [L2TP_ATTR_STATS] = { .type = NLA_NESTED, },
725 [L2TP_ATTR_IFNAME] = {
726 .type = NLA_NUL_STRING,
727 .len = IFNAMSIZ - 1,
728 },
729 [L2TP_ATTR_COOKIE] = {
730 .type = NLA_BINARY,
731 .len = 8,
732 },
733 [L2TP_ATTR_PEER_COOKIE] = {
734 .type = NLA_BINARY,
735 .len = 8,
736 },
737};
738
739static struct genl_ops l2tp_nl_ops[] = {
740 {
741 .cmd = L2TP_CMD_NOOP,
742 .doit = l2tp_nl_cmd_noop,
743 .policy = l2tp_nl_policy,
744 /* can be retrieved by unprivileged users */
745 },
746 {
747 .cmd = L2TP_CMD_TUNNEL_CREATE,
748 .doit = l2tp_nl_cmd_tunnel_create,
749 .policy = l2tp_nl_policy,
750 .flags = GENL_ADMIN_PERM,
751 },
752 {
753 .cmd = L2TP_CMD_TUNNEL_DELETE,
754 .doit = l2tp_nl_cmd_tunnel_delete,
755 .policy = l2tp_nl_policy,
756 .flags = GENL_ADMIN_PERM,
757 },
758 {
759 .cmd = L2TP_CMD_TUNNEL_MODIFY,
760 .doit = l2tp_nl_cmd_tunnel_modify,
761 .policy = l2tp_nl_policy,
762 .flags = GENL_ADMIN_PERM,
763 },
764 {
765 .cmd = L2TP_CMD_TUNNEL_GET,
766 .doit = l2tp_nl_cmd_tunnel_get,
767 .dumpit = l2tp_nl_cmd_tunnel_dump,
768 .policy = l2tp_nl_policy,
769 .flags = GENL_ADMIN_PERM,
770 },
771 {
772 .cmd = L2TP_CMD_SESSION_CREATE,
773 .doit = l2tp_nl_cmd_session_create,
774 .policy = l2tp_nl_policy,
775 .flags = GENL_ADMIN_PERM,
776 },
777 {
778 .cmd = L2TP_CMD_SESSION_DELETE,
779 .doit = l2tp_nl_cmd_session_delete,
780 .policy = l2tp_nl_policy,
781 .flags = GENL_ADMIN_PERM,
782 },
783 {
784 .cmd = L2TP_CMD_SESSION_MODIFY,
785 .doit = l2tp_nl_cmd_session_modify,
786 .policy = l2tp_nl_policy,
787 .flags = GENL_ADMIN_PERM,
788 },
789 {
790 .cmd = L2TP_CMD_SESSION_GET,
791 .doit = l2tp_nl_cmd_session_get,
792 .dumpit = l2tp_nl_cmd_session_dump,
793 .policy = l2tp_nl_policy,
794 .flags = GENL_ADMIN_PERM,
795 },
796};
797
798int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops)
799{
800 int ret;
801
802 ret = -EINVAL;
803 if (pw_type >= __L2TP_PWTYPE_MAX)
804 goto err;
805
806 genl_lock();
807 ret = -EBUSY;
808 if (l2tp_nl_cmd_ops[pw_type])
809 goto out;
810
811 l2tp_nl_cmd_ops[pw_type] = ops;
David S. Miller8cb49012011-04-17 17:01:05 -0700812 ret = 0;
James Chapman309795f2010-04-02 06:19:10 +0000813
814out:
815 genl_unlock();
816err:
David S. Miller8cb49012011-04-17 17:01:05 -0700817 return ret;
James Chapman309795f2010-04-02 06:19:10 +0000818}
819EXPORT_SYMBOL_GPL(l2tp_nl_register_ops);
820
821void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type)
822{
823 if (pw_type < __L2TP_PWTYPE_MAX) {
824 genl_lock();
825 l2tp_nl_cmd_ops[pw_type] = NULL;
826 genl_unlock();
827 }
828}
829EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
830
831static int l2tp_nl_init(void)
832{
833 int err;
834
835 printk(KERN_INFO "L2TP netlink interface\n");
836 err = genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops,
837 ARRAY_SIZE(l2tp_nl_ops));
838
839 return err;
840}
841
842static void l2tp_nl_cleanup(void)
843{
844 genl_unregister_family(&l2tp_nl_family);
845}
846
847module_init(l2tp_nl_init);
848module_exit(l2tp_nl_cleanup);
849
850MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
851MODULE_DESCRIPTION("L2TP netlink");
852MODULE_LICENSE("GPL");
853MODULE_VERSION("1.0");
854MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK) "-proto-" \
David S. Millerf481c0d2010-04-03 14:58:07 -0700855 __stringify(NETLINK_GENERIC) "-type-" "l2tp");