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