Root/
1 | /* |
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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | |
19 | #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 | |
34 | static 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, |
40 | .netnsok = true, |
41 | }; |
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_find(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(info->attrs[L2TP_ATTR_IFNAME]); |
57 | session = l2tp_session_find_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(info->attrs[L2TP_ATTR_CONN_ID]); |
61 | session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); |
62 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
63 | if (tunnel) |
64 | session = l2tp_session_find(net, tunnel, session_id); |
65 | } |
66 | |
67 | return session; |
68 | } |
69 | |
70 | static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) |
71 | { |
72 | struct sk_buff *msg; |
73 | void *hdr; |
74 | int ret = -ENOBUFS; |
75 | |
76 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
77 | if (!msg) { |
78 | ret = -ENOMEM; |
79 | goto out; |
80 | } |
81 | |
82 | hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, |
83 | &l2tp_nl_family, 0, L2TP_CMD_NOOP); |
84 | if (!hdr) { |
85 | ret = -EMSGSIZE; |
86 | goto err_out; |
87 | } |
88 | |
89 | genlmsg_end(msg, hdr); |
90 | |
91 | return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); |
92 | |
93 | err_out: |
94 | nlmsg_free(msg); |
95 | |
96 | out: |
97 | return ret; |
98 | } |
99 | |
100 | static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info) |
101 | { |
102 | u32 tunnel_id; |
103 | u32 peer_tunnel_id; |
104 | int proto_version; |
105 | int fd; |
106 | int ret = 0; |
107 | struct l2tp_tunnel_cfg cfg = { 0, }; |
108 | struct l2tp_tunnel *tunnel; |
109 | struct net *net = genl_info_net(info); |
110 | |
111 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
112 | ret = -EINVAL; |
113 | goto out; |
114 | } |
115 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
116 | |
117 | if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) { |
118 | ret = -EINVAL; |
119 | goto out; |
120 | } |
121 | peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]); |
122 | |
123 | if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) { |
124 | ret = -EINVAL; |
125 | goto out; |
126 | } |
127 | proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]); |
128 | |
129 | if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) { |
130 | ret = -EINVAL; |
131 | goto out; |
132 | } |
133 | cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]); |
134 | |
135 | fd = -1; |
136 | if (info->attrs[L2TP_ATTR_FD]) { |
137 | fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); |
138 | } else { |
139 | #if IS_ENABLED(CONFIG_IPV6) |
140 | if (info->attrs[L2TP_ATTR_IP6_SADDR] && |
141 | info->attrs[L2TP_ATTR_IP6_DADDR]) { |
142 | cfg.local_ip6 = nla_data( |
143 | info->attrs[L2TP_ATTR_IP6_SADDR]); |
144 | cfg.peer_ip6 = nla_data( |
145 | info->attrs[L2TP_ATTR_IP6_DADDR]); |
146 | } else |
147 | #endif |
148 | if (info->attrs[L2TP_ATTR_IP_SADDR] && |
149 | info->attrs[L2TP_ATTR_IP_DADDR]) { |
150 | cfg.local_ip.s_addr = nla_get_be32( |
151 | info->attrs[L2TP_ATTR_IP_SADDR]); |
152 | cfg.peer_ip.s_addr = nla_get_be32( |
153 | info->attrs[L2TP_ATTR_IP_DADDR]); |
154 | } else { |
155 | ret = -EINVAL; |
156 | goto out; |
157 | } |
158 | if (info->attrs[L2TP_ATTR_UDP_SPORT]) |
159 | cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]); |
160 | if (info->attrs[L2TP_ATTR_UDP_DPORT]) |
161 | cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]); |
162 | if (info->attrs[L2TP_ATTR_UDP_CSUM]) |
163 | cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]); |
164 | } |
165 | |
166 | if (info->attrs[L2TP_ATTR_DEBUG]) |
167 | cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); |
168 | |
169 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
170 | if (tunnel != NULL) { |
171 | ret = -EEXIST; |
172 | goto out; |
173 | } |
174 | |
175 | ret = -EINVAL; |
176 | switch (cfg.encap) { |
177 | case L2TP_ENCAPTYPE_UDP: |
178 | case L2TP_ENCAPTYPE_IP: |
179 | ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id, |
180 | peer_tunnel_id, &cfg, &tunnel); |
181 | break; |
182 | } |
183 | |
184 | out: |
185 | return ret; |
186 | } |
187 | |
188 | static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info) |
189 | { |
190 | struct l2tp_tunnel *tunnel; |
191 | u32 tunnel_id; |
192 | int ret = 0; |
193 | struct net *net = genl_info_net(info); |
194 | |
195 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
196 | ret = -EINVAL; |
197 | goto out; |
198 | } |
199 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
200 | |
201 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
202 | if (tunnel == NULL) { |
203 | ret = -ENODEV; |
204 | goto out; |
205 | } |
206 | |
207 | (void) l2tp_tunnel_delete(tunnel); |
208 | |
209 | out: |
210 | return ret; |
211 | } |
212 | |
213 | static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info) |
214 | { |
215 | struct l2tp_tunnel *tunnel; |
216 | u32 tunnel_id; |
217 | int ret = 0; |
218 | struct net *net = genl_info_net(info); |
219 | |
220 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
221 | ret = -EINVAL; |
222 | goto out; |
223 | } |
224 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
225 | |
226 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
227 | if (tunnel == NULL) { |
228 | ret = -ENODEV; |
229 | goto out; |
230 | } |
231 | |
232 | if (info->attrs[L2TP_ATTR_DEBUG]) |
233 | tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); |
234 | |
235 | out: |
236 | return ret; |
237 | } |
238 | |
239 | static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
240 | struct l2tp_tunnel *tunnel) |
241 | { |
242 | void *hdr; |
243 | struct nlattr *nest; |
244 | struct sock *sk = NULL; |
245 | struct inet_sock *inet; |
246 | #if IS_ENABLED(CONFIG_IPV6) |
247 | struct ipv6_pinfo *np = NULL; |
248 | #endif |
249 | |
250 | hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, |
251 | L2TP_CMD_TUNNEL_GET); |
252 | if (!hdr) |
253 | return -EMSGSIZE; |
254 | |
255 | if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) || |
256 | nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || |
257 | nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || |
258 | nla_put_u32(skb, L2TP_ATTR_DEBUG, tunnel->debug) || |
259 | nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap)) |
260 | goto nla_put_failure; |
261 | |
262 | nest = nla_nest_start(skb, L2TP_ATTR_STATS); |
263 | if (nest == NULL) |
264 | goto nla_put_failure; |
265 | |
266 | if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, |
267 | atomic_long_read(&tunnel->stats.tx_packets)) || |
268 | nla_put_u64(skb, L2TP_ATTR_TX_BYTES, |
269 | atomic_long_read(&tunnel->stats.tx_bytes)) || |
270 | nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, |
271 | atomic_long_read(&tunnel->stats.tx_errors)) || |
272 | nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, |
273 | atomic_long_read(&tunnel->stats.rx_packets)) || |
274 | nla_put_u64(skb, L2TP_ATTR_RX_BYTES, |
275 | atomic_long_read(&tunnel->stats.rx_bytes)) || |
276 | nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, |
277 | atomic_long_read(&tunnel->stats.rx_seq_discards)) || |
278 | nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS, |
279 | atomic_long_read(&tunnel->stats.rx_oos_packets)) || |
280 | nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, |
281 | atomic_long_read(&tunnel->stats.rx_errors))) |
282 | goto nla_put_failure; |
283 | nla_nest_end(skb, nest); |
284 | |
285 | sk = tunnel->sock; |
286 | if (!sk) |
287 | goto out; |
288 | |
289 | #if IS_ENABLED(CONFIG_IPV6) |
290 | if (sk->sk_family == AF_INET6) |
291 | np = inet6_sk(sk); |
292 | #endif |
293 | |
294 | inet = inet_sk(sk); |
295 | |
296 | switch (tunnel->encap) { |
297 | case L2TP_ENCAPTYPE_UDP: |
298 | if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) || |
299 | nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) || |
300 | nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, |
301 | (sk->sk_no_check != UDP_CSUM_NOXMIT))) |
302 | goto nla_put_failure; |
303 | /* NOBREAK */ |
304 | case L2TP_ENCAPTYPE_IP: |
305 | #if IS_ENABLED(CONFIG_IPV6) |
306 | if (np) { |
307 | if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr), |
308 | &np->saddr) || |
309 | nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(sk->sk_v6_daddr), |
310 | &sk->sk_v6_daddr)) |
311 | goto nla_put_failure; |
312 | } else |
313 | #endif |
314 | if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) || |
315 | nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr)) |
316 | goto nla_put_failure; |
317 | break; |
318 | } |
319 | |
320 | out: |
321 | return genlmsg_end(skb, hdr); |
322 | |
323 | nla_put_failure: |
324 | genlmsg_cancel(skb, hdr); |
325 | return -1; |
326 | } |
327 | |
328 | static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) |
329 | { |
330 | struct l2tp_tunnel *tunnel; |
331 | struct sk_buff *msg; |
332 | u32 tunnel_id; |
333 | int ret = -ENOBUFS; |
334 | struct net *net = genl_info_net(info); |
335 | |
336 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
337 | ret = -EINVAL; |
338 | goto out; |
339 | } |
340 | |
341 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
342 | |
343 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
344 | if (tunnel == NULL) { |
345 | ret = -ENODEV; |
346 | goto out; |
347 | } |
348 | |
349 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
350 | if (!msg) { |
351 | ret = -ENOMEM; |
352 | goto out; |
353 | } |
354 | |
355 | ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq, |
356 | NLM_F_ACK, tunnel); |
357 | if (ret < 0) |
358 | goto err_out; |
359 | |
360 | return genlmsg_unicast(net, msg, info->snd_portid); |
361 | |
362 | err_out: |
363 | nlmsg_free(msg); |
364 | |
365 | out: |
366 | return ret; |
367 | } |
368 | |
369 | static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb) |
370 | { |
371 | int ti = cb->args[0]; |
372 | struct l2tp_tunnel *tunnel; |
373 | struct net *net = sock_net(skb->sk); |
374 | |
375 | for (;;) { |
376 | tunnel = l2tp_tunnel_find_nth(net, ti); |
377 | if (tunnel == NULL) |
378 | goto out; |
379 | |
380 | if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid, |
381 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
382 | tunnel) <= 0) |
383 | goto out; |
384 | |
385 | ti++; |
386 | } |
387 | |
388 | out: |
389 | cb->args[0] = ti; |
390 | |
391 | return skb->len; |
392 | } |
393 | |
394 | static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info) |
395 | { |
396 | u32 tunnel_id = 0; |
397 | u32 session_id; |
398 | u32 peer_session_id; |
399 | int ret = 0; |
400 | struct l2tp_tunnel *tunnel; |
401 | struct l2tp_session *session; |
402 | struct l2tp_session_cfg cfg = { 0, }; |
403 | struct net *net = genl_info_net(info); |
404 | |
405 | if (!info->attrs[L2TP_ATTR_CONN_ID]) { |
406 | ret = -EINVAL; |
407 | goto out; |
408 | } |
409 | tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); |
410 | tunnel = l2tp_tunnel_find(net, tunnel_id); |
411 | if (!tunnel) { |
412 | ret = -ENODEV; |
413 | goto out; |
414 | } |
415 | |
416 | if (!info->attrs[L2TP_ATTR_SESSION_ID]) { |
417 | ret = -EINVAL; |
418 | goto out; |
419 | } |
420 | session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); |
421 | session = l2tp_session_find(net, tunnel, session_id); |
422 | if (session) { |
423 | ret = -EEXIST; |
424 | goto out; |
425 | } |
426 | |
427 | if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { |
428 | ret = -EINVAL; |
429 | goto out; |
430 | } |
431 | peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]); |
432 | |
433 | if (!info->attrs[L2TP_ATTR_PW_TYPE]) { |
434 | ret = -EINVAL; |
435 | goto out; |
436 | } |
437 | cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]); |
438 | if (cfg.pw_type >= __L2TP_PWTYPE_MAX) { |
439 | ret = -EINVAL; |
440 | goto out; |
441 | } |
442 | |
443 | if (tunnel->version > 2) { |
444 | if (info->attrs[L2TP_ATTR_OFFSET]) |
445 | cfg.offset = nla_get_u16(info->attrs[L2TP_ATTR_OFFSET]); |
446 | |
447 | if (info->attrs[L2TP_ATTR_DATA_SEQ]) |
448 | cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); |
449 | |
450 | cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT; |
451 | if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) |
452 | cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]); |
453 | |
454 | cfg.l2specific_len = 4; |
455 | if (info->attrs[L2TP_ATTR_L2SPEC_LEN]) |
456 | cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]); |
457 | |
458 | if (info->attrs[L2TP_ATTR_COOKIE]) { |
459 | u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]); |
460 | if (len > 8) { |
461 | ret = -EINVAL; |
462 | goto out; |
463 | } |
464 | cfg.cookie_len = len; |
465 | memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len); |
466 | } |
467 | if (info->attrs[L2TP_ATTR_PEER_COOKIE]) { |
468 | u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]); |
469 | if (len > 8) { |
470 | ret = -EINVAL; |
471 | goto out; |
472 | } |
473 | cfg.peer_cookie_len = len; |
474 | memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len); |
475 | } |
476 | if (info->attrs[L2TP_ATTR_IFNAME]) |
477 | cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); |
478 | |
479 | if (info->attrs[L2TP_ATTR_VLAN_ID]) |
480 | cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]); |
481 | } |
482 | |
483 | if (info->attrs[L2TP_ATTR_DEBUG]) |
484 | cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); |
485 | |
486 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
487 | cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); |
488 | |
489 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) |
490 | cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); |
491 | |
492 | if (info->attrs[L2TP_ATTR_LNS_MODE]) |
493 | cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); |
494 | |
495 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) |
496 | cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); |
497 | |
498 | if (info->attrs[L2TP_ATTR_MTU]) |
499 | cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); |
500 | |
501 | if (info->attrs[L2TP_ATTR_MRU]) |
502 | cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); |
503 | |
504 | if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) || |
505 | (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) { |
506 | ret = -EPROTONOSUPPORT; |
507 | goto out; |
508 | } |
509 | |
510 | /* Check that pseudowire-specific params are present */ |
511 | switch (cfg.pw_type) { |
512 | case L2TP_PWTYPE_NONE: |
513 | break; |
514 | case L2TP_PWTYPE_ETH_VLAN: |
515 | if (!info->attrs[L2TP_ATTR_VLAN_ID]) { |
516 | ret = -EINVAL; |
517 | goto out; |
518 | } |
519 | break; |
520 | case L2TP_PWTYPE_ETH: |
521 | break; |
522 | case L2TP_PWTYPE_PPP: |
523 | case L2TP_PWTYPE_PPP_AC: |
524 | break; |
525 | case L2TP_PWTYPE_IP: |
526 | default: |
527 | ret = -EPROTONOSUPPORT; |
528 | break; |
529 | } |
530 | |
531 | ret = -EPROTONOSUPPORT; |
532 | if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create) |
533 | ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, |
534 | session_id, peer_session_id, &cfg); |
535 | |
536 | out: |
537 | return ret; |
538 | } |
539 | |
540 | static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info) |
541 | { |
542 | int ret = 0; |
543 | struct l2tp_session *session; |
544 | u16 pw_type; |
545 | |
546 | session = l2tp_nl_session_find(info); |
547 | if (session == NULL) { |
548 | ret = -ENODEV; |
549 | goto out; |
550 | } |
551 | |
552 | pw_type = session->pwtype; |
553 | if (pw_type < __L2TP_PWTYPE_MAX) |
554 | if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) |
555 | ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session); |
556 | |
557 | out: |
558 | return ret; |
559 | } |
560 | |
561 | static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info) |
562 | { |
563 | int ret = 0; |
564 | struct l2tp_session *session; |
565 | |
566 | session = l2tp_nl_session_find(info); |
567 | if (session == NULL) { |
568 | ret = -ENODEV; |
569 | goto out; |
570 | } |
571 | |
572 | if (info->attrs[L2TP_ATTR_DEBUG]) |
573 | session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]); |
574 | |
575 | if (info->attrs[L2TP_ATTR_DATA_SEQ]) |
576 | session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]); |
577 | |
578 | if (info->attrs[L2TP_ATTR_RECV_SEQ]) |
579 | session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]); |
580 | |
581 | if (info->attrs[L2TP_ATTR_SEND_SEQ]) |
582 | session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]); |
583 | |
584 | if (info->attrs[L2TP_ATTR_LNS_MODE]) |
585 | session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]); |
586 | |
587 | if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) |
588 | session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); |
589 | |
590 | if (info->attrs[L2TP_ATTR_MTU]) |
591 | session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]); |
592 | |
593 | if (info->attrs[L2TP_ATTR_MRU]) |
594 | session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]); |
595 | |
596 | out: |
597 | return ret; |
598 | } |
599 | |
600 | static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags, |
601 | struct l2tp_session *session) |
602 | { |
603 | void *hdr; |
604 | struct nlattr *nest; |
605 | struct l2tp_tunnel *tunnel = session->tunnel; |
606 | struct sock *sk = NULL; |
607 | |
608 | sk = tunnel->sock; |
609 | |
610 | hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET); |
611 | if (!hdr) |
612 | return -EMSGSIZE; |
613 | |
614 | if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) || |
615 | nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) || |
616 | nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) || |
617 | nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID, |
618 | session->peer_session_id) || |
619 | nla_put_u32(skb, L2TP_ATTR_DEBUG, session->debug) || |
620 | nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype) || |
621 | nla_put_u16(skb, L2TP_ATTR_MTU, session->mtu) || |
622 | (session->mru && |
623 | nla_put_u16(skb, L2TP_ATTR_MRU, session->mru))) |
624 | goto nla_put_failure; |
625 | |
626 | if ((session->ifname[0] && |
627 | nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) || |
628 | (session->cookie_len && |
629 | nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len, |
630 | &session->cookie[0])) || |
631 | (session->peer_cookie_len && |
632 | nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len, |
633 | &session->peer_cookie[0])) || |
634 | nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) || |
635 | nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) || |
636 | nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) || |
637 | #ifdef CONFIG_XFRM |
638 | (((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) && |
639 | nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) || |
640 | #endif |
641 | (session->reorder_timeout && |
642 | nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT, session->reorder_timeout))) |
643 | goto nla_put_failure; |
644 | |
645 | nest = nla_nest_start(skb, L2TP_ATTR_STATS); |
646 | if (nest == NULL) |
647 | goto nla_put_failure; |
648 | |
649 | if (nla_put_u64(skb, L2TP_ATTR_TX_PACKETS, |
650 | atomic_long_read(&session->stats.tx_packets)) || |
651 | nla_put_u64(skb, L2TP_ATTR_TX_BYTES, |
652 | atomic_long_read(&session->stats.tx_bytes)) || |
653 | nla_put_u64(skb, L2TP_ATTR_TX_ERRORS, |
654 | atomic_long_read(&session->stats.tx_errors)) || |
655 | nla_put_u64(skb, L2TP_ATTR_RX_PACKETS, |
656 | atomic_long_read(&session->stats.rx_packets)) || |
657 | nla_put_u64(skb, L2TP_ATTR_RX_BYTES, |
658 | atomic_long_read(&session->stats.rx_bytes)) || |
659 | nla_put_u64(skb, L2TP_ATTR_RX_SEQ_DISCARDS, |
660 | atomic_long_read(&session->stats.rx_seq_discards)) || |
661 | nla_put_u64(skb, L2TP_ATTR_RX_OOS_PACKETS, |
662 | atomic_long_read(&session->stats.rx_oos_packets)) || |
663 | nla_put_u64(skb, L2TP_ATTR_RX_ERRORS, |
664 | atomic_long_read(&session->stats.rx_errors))) |
665 | goto nla_put_failure; |
666 | nla_nest_end(skb, nest); |
667 | |
668 | return genlmsg_end(skb, hdr); |
669 | |
670 | nla_put_failure: |
671 | genlmsg_cancel(skb, hdr); |
672 | return -1; |
673 | } |
674 | |
675 | static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) |
676 | { |
677 | struct l2tp_session *session; |
678 | struct sk_buff *msg; |
679 | int ret; |
680 | |
681 | session = l2tp_nl_session_find(info); |
682 | if (session == NULL) { |
683 | ret = -ENODEV; |
684 | goto out; |
685 | } |
686 | |
687 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
688 | if (!msg) { |
689 | ret = -ENOMEM; |
690 | goto out; |
691 | } |
692 | |
693 | ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, |
694 | 0, session); |
695 | if (ret < 0) |
696 | goto err_out; |
697 | |
698 | return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); |
699 | |
700 | err_out: |
701 | nlmsg_free(msg); |
702 | |
703 | out: |
704 | return ret; |
705 | } |
706 | |
707 | static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb) |
708 | { |
709 | struct net *net = sock_net(skb->sk); |
710 | struct l2tp_session *session; |
711 | struct l2tp_tunnel *tunnel = NULL; |
712 | int ti = cb->args[0]; |
713 | int si = cb->args[1]; |
714 | |
715 | for (;;) { |
716 | if (tunnel == NULL) { |
717 | tunnel = l2tp_tunnel_find_nth(net, ti); |
718 | if (tunnel == NULL) |
719 | goto out; |
720 | } |
721 | |
722 | session = l2tp_session_find_nth(tunnel, si); |
723 | if (session == NULL) { |
724 | ti++; |
725 | tunnel = NULL; |
726 | si = 0; |
727 | continue; |
728 | } |
729 | |
730 | if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid, |
731 | cb->nlh->nlmsg_seq, NLM_F_MULTI, |
732 | session) <= 0) |
733 | break; |
734 | |
735 | si++; |
736 | } |
737 | |
738 | out: |
739 | cb->args[0] = ti; |
740 | cb->args[1] = si; |
741 | |
742 | return skb->len; |
743 | } |
744 | |
745 | static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { |
746 | [L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, }, |
747 | [L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, }, |
748 | [L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, }, |
749 | [L2TP_ATTR_OFFSET] = { .type = NLA_U16, }, |
750 | [L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, }, |
751 | [L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, }, |
752 | [L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, }, |
753 | [L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, }, |
754 | [L2TP_ATTR_CONN_ID] = { .type = NLA_U32, }, |
755 | [L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, }, |
756 | [L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, }, |
757 | [L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, }, |
758 | [L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, }, |
759 | [L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, }, |
760 | [L2TP_ATTR_DEBUG] = { .type = NLA_U32, }, |
761 | [L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, }, |
762 | [L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, }, |
763 | [L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, }, |
764 | [L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, }, |
765 | [L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, }, |
766 | [L2TP_ATTR_FD] = { .type = NLA_U32, }, |
767 | [L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, }, |
768 | [L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, }, |
769 | [L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, }, |
770 | [L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, }, |
771 | [L2TP_ATTR_MTU] = { .type = NLA_U16, }, |
772 | [L2TP_ATTR_MRU] = { .type = NLA_U16, }, |
773 | [L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, |
774 | [L2TP_ATTR_IP6_SADDR] = { |
775 | .type = NLA_BINARY, |
776 | .len = sizeof(struct in6_addr), |
777 | }, |
778 | [L2TP_ATTR_IP6_DADDR] = { |
779 | .type = NLA_BINARY, |
780 | .len = sizeof(struct in6_addr), |
781 | }, |
782 | [L2TP_ATTR_IFNAME] = { |
783 | .type = NLA_NUL_STRING, |
784 | .len = IFNAMSIZ - 1, |
785 | }, |
786 | [L2TP_ATTR_COOKIE] = { |
787 | .type = NLA_BINARY, |
788 | .len = 8, |
789 | }, |
790 | [L2TP_ATTR_PEER_COOKIE] = { |
791 | .type = NLA_BINARY, |
792 | .len = 8, |
793 | }, |
794 | }; |
795 | |
796 | static const struct genl_ops l2tp_nl_ops[] = { |
797 | { |
798 | .cmd = L2TP_CMD_NOOP, |
799 | .doit = l2tp_nl_cmd_noop, |
800 | .policy = l2tp_nl_policy, |
801 | /* can be retrieved by unprivileged users */ |
802 | }, |
803 | { |
804 | .cmd = L2TP_CMD_TUNNEL_CREATE, |
805 | .doit = l2tp_nl_cmd_tunnel_create, |
806 | .policy = l2tp_nl_policy, |
807 | .flags = GENL_ADMIN_PERM, |
808 | }, |
809 | { |
810 | .cmd = L2TP_CMD_TUNNEL_DELETE, |
811 | .doit = l2tp_nl_cmd_tunnel_delete, |
812 | .policy = l2tp_nl_policy, |
813 | .flags = GENL_ADMIN_PERM, |
814 | }, |
815 | { |
816 | .cmd = L2TP_CMD_TUNNEL_MODIFY, |
817 | .doit = l2tp_nl_cmd_tunnel_modify, |
818 | .policy = l2tp_nl_policy, |
819 | .flags = GENL_ADMIN_PERM, |
820 | }, |
821 | { |
822 | .cmd = L2TP_CMD_TUNNEL_GET, |
823 | .doit = l2tp_nl_cmd_tunnel_get, |
824 | .dumpit = l2tp_nl_cmd_tunnel_dump, |
825 | .policy = l2tp_nl_policy, |
826 | .flags = GENL_ADMIN_PERM, |
827 | }, |
828 | { |
829 | .cmd = L2TP_CMD_SESSION_CREATE, |
830 | .doit = l2tp_nl_cmd_session_create, |
831 | .policy = l2tp_nl_policy, |
832 | .flags = GENL_ADMIN_PERM, |
833 | }, |
834 | { |
835 | .cmd = L2TP_CMD_SESSION_DELETE, |
836 | .doit = l2tp_nl_cmd_session_delete, |
837 | .policy = l2tp_nl_policy, |
838 | .flags = GENL_ADMIN_PERM, |
839 | }, |
840 | { |
841 | .cmd = L2TP_CMD_SESSION_MODIFY, |
842 | .doit = l2tp_nl_cmd_session_modify, |
843 | .policy = l2tp_nl_policy, |
844 | .flags = GENL_ADMIN_PERM, |
845 | }, |
846 | { |
847 | .cmd = L2TP_CMD_SESSION_GET, |
848 | .doit = l2tp_nl_cmd_session_get, |
849 | .dumpit = l2tp_nl_cmd_session_dump, |
850 | .policy = l2tp_nl_policy, |
851 | .flags = GENL_ADMIN_PERM, |
852 | }, |
853 | }; |
854 | |
855 | int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops) |
856 | { |
857 | int ret; |
858 | |
859 | ret = -EINVAL; |
860 | if (pw_type >= __L2TP_PWTYPE_MAX) |
861 | goto err; |
862 | |
863 | genl_lock(); |
864 | ret = -EBUSY; |
865 | if (l2tp_nl_cmd_ops[pw_type]) |
866 | goto out; |
867 | |
868 | l2tp_nl_cmd_ops[pw_type] = ops; |
869 | ret = 0; |
870 | |
871 | out: |
872 | genl_unlock(); |
873 | err: |
874 | return ret; |
875 | } |
876 | EXPORT_SYMBOL_GPL(l2tp_nl_register_ops); |
877 | |
878 | void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type) |
879 | { |
880 | if (pw_type < __L2TP_PWTYPE_MAX) { |
881 | genl_lock(); |
882 | l2tp_nl_cmd_ops[pw_type] = NULL; |
883 | genl_unlock(); |
884 | } |
885 | } |
886 | EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops); |
887 | |
888 | static int l2tp_nl_init(void) |
889 | { |
890 | pr_info("L2TP netlink interface\n"); |
891 | return genl_register_family_with_ops(&l2tp_nl_family, l2tp_nl_ops); |
892 | } |
893 | |
894 | static void l2tp_nl_cleanup(void) |
895 | { |
896 | genl_unregister_family(&l2tp_nl_family); |
897 | } |
898 | |
899 | module_init(l2tp_nl_init); |
900 | module_exit(l2tp_nl_cleanup); |
901 | |
902 | MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); |
903 | MODULE_DESCRIPTION("L2TP netlink"); |
904 | MODULE_LICENSE("GPL"); |
905 | MODULE_VERSION("1.0"); |
906 | MODULE_ALIAS_GENL_FAMILY("l2tp"); |
907 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9