Blame view

net/l2tp/l2tp_netlink.c 26.8 KB
309795f4b   James Chapman   l2tp: Add netlink...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * L2TP netlink layer, for management
   *
   * Copyright (c) 2008,2009,2010 Katalix Systems Ltd
   *
   * Partly based on the IrDA nelink implementation
   * (see net/irda/irnetlink.c) which is:
   * Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
   * which is in turn partly based on the wireless netlink code:
   * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
a4ca44fa5   Joe Perches   net: l2tp: Standa...
16
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
309795f4b   James Chapman   l2tp: Add netlink...
17
18
19
20
21
22
23
24
25
26
27
28
29
  #include <net/sock.h>
  #include <net/genetlink.h>
  #include <net/udp.h>
  #include <linux/in.h>
  #include <linux/udp.h>
  #include <linux/socket.h>
  #include <linux/module.h>
  #include <linux/list.h>
  #include <net/net_namespace.h>
  
  #include <linux/l2tp.h>
  
  #include "l2tp_core.h"
489111e5c   Johannes Berg   genetlink: static...
30
  static struct genl_family l2tp_nl_family;
309795f4b   James Chapman   l2tp: Add netlink...
31

33f72e6f0   Bill Hong   l2tp : multicast ...
32
33
34
35
36
37
38
39
40
41
42
  static const struct genl_multicast_group l2tp_multicast_group[] = {
  	{
  		.name = L2TP_GENL_MCGROUP,
  	},
  };
  
  static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq,
  			       int flags, struct l2tp_tunnel *tunnel, u8 cmd);
  static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq,
  				int flags, struct l2tp_session *session,
  				u8 cmd);
309795f4b   James Chapman   l2tp: Add netlink...
43
44
  /* Accessed under genl lock */
  static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
2777e2ab5   Guillaume Nault   l2tp: take a refe...
45
46
  static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
  						bool do_ref)
309795f4b   James Chapman   l2tp: Add netlink...
47
48
49
50
51
52
53
54
55
56
  {
  	u32 tunnel_id;
  	u32 session_id;
  	char *ifname;
  	struct l2tp_tunnel *tunnel;
  	struct l2tp_session *session = NULL;
  	struct net *net = genl_info_net(info);
  
  	if (info->attrs[L2TP_ATTR_IFNAME]) {
  		ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
2777e2ab5   Guillaume Nault   l2tp: take a refe...
57
  		session = l2tp_session_get_by_ifname(net, ifname, do_ref);
309795f4b   James Chapman   l2tp: Add netlink...
58
59
60
61
  	} else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
  		   (info->attrs[L2TP_ATTR_CONN_ID])) {
  		tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
  		session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
54652eb12   Guillaume Nault   l2tp: hold tunnel...
62
63
  		tunnel = l2tp_tunnel_get(net, tunnel_id);
  		if (tunnel) {
2777e2ab5   Guillaume Nault   l2tp: take a refe...
64
65
  			session = l2tp_session_get(net, tunnel, session_id,
  						   do_ref);
54652eb12   Guillaume Nault   l2tp: hold tunnel...
66
67
  			l2tp_tunnel_dec_refcount(tunnel);
  		}
309795f4b   James Chapman   l2tp: Add netlink...
68
69
70
71
72
73
74
75
76
77
  	}
  
  	return session;
  }
  
  static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
  {
  	struct sk_buff *msg;
  	void *hdr;
  	int ret = -ENOBUFS;
58050fce3   Thomas Graf   net: Use NLMSG_DE...
78
  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
309795f4b   James Chapman   l2tp: Add netlink...
79
80
81
82
  	if (!msg) {
  		ret = -ENOMEM;
  		goto out;
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
83
  	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
309795f4b   James Chapman   l2tp: Add netlink...
84
  			  &l2tp_nl_family, 0, L2TP_CMD_NOOP);
7f8436a12   Wei Yongjun   l2tp: fix return ...
85
86
  	if (!hdr) {
  		ret = -EMSGSIZE;
309795f4b   James Chapman   l2tp: Add netlink...
87
88
89
90
  		goto err_out;
  	}
  
  	genlmsg_end(msg, hdr);
15e473046   Eric W. Biederman   netlink: Rename p...
91
  	return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
309795f4b   James Chapman   l2tp: Add netlink...
92
93
94
95
96
97
98
  
  err_out:
  	nlmsg_free(msg);
  
  out:
  	return ret;
  }
33f72e6f0   Bill Hong   l2tp : multicast ...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  static int l2tp_tunnel_notify(struct genl_family *family,
  			      struct genl_info *info,
  			      struct l2tp_tunnel *tunnel,
  			      u8 cmd)
  {
  	struct sk_buff *msg;
  	int ret;
  
  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
  	if (!msg)
  		return -ENOMEM;
  
  	ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
  				  NLM_F_ACK, tunnel, cmd);
853effc55   Mark Tomlinson   l2tp: Fix error c...
113
114
115
116
117
118
119
  	if (ret >= 0) {
  		ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
  		/* We don't care if no one is listening */
  		if (ret == -ESRCH)
  			ret = 0;
  		return ret;
  	}
33f72e6f0   Bill Hong   l2tp : multicast ...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  
  	nlmsg_free(msg);
  
  	return ret;
  }
  
  static int l2tp_session_notify(struct genl_family *family,
  			       struct genl_info *info,
  			       struct l2tp_session *session,
  			       u8 cmd)
  {
  	struct sk_buff *msg;
  	int ret;
  
  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
  	if (!msg)
  		return -ENOMEM;
  
  	ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
  				   NLM_F_ACK, session, cmd);
853effc55   Mark Tomlinson   l2tp: Fix error c...
140
141
142
143
144
145
146
  	if (ret >= 0) {
  		ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
  		/* We don't care if no one is listening */
  		if (ret == -ESRCH)
  			ret = 0;
  		return ret;
  	}
33f72e6f0   Bill Hong   l2tp : multicast ...
147
148
149
150
151
  
  	nlmsg_free(msg);
  
  	return ret;
  }
309795f4b   James Chapman   l2tp: Add netlink...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info)
  {
  	u32 tunnel_id;
  	u32 peer_tunnel_id;
  	int proto_version;
  	int fd;
  	int ret = 0;
  	struct l2tp_tunnel_cfg cfg = { 0, };
  	struct l2tp_tunnel *tunnel;
  	struct net *net = genl_info_net(info);
  
  	if (!info->attrs[L2TP_ATTR_CONN_ID]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
  
  	if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]);
  
  	if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]);
  
  	if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]);
789a4a2c6   James Chapman   l2tp: Add support...
186
187
188
189
  	fd = -1;
  	if (info->attrs[L2TP_ATTR_FD]) {
  		fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
  	} else {
f9bac8df9   Chris Elston   l2tp: netlink api...
190
191
192
193
194
195
196
197
198
199
200
  #if IS_ENABLED(CONFIG_IPV6)
  		if (info->attrs[L2TP_ATTR_IP6_SADDR] &&
  		    info->attrs[L2TP_ATTR_IP6_DADDR]) {
  			cfg.local_ip6 = nla_data(
  				info->attrs[L2TP_ATTR_IP6_SADDR]);
  			cfg.peer_ip6 = nla_data(
  				info->attrs[L2TP_ATTR_IP6_DADDR]);
  		} else
  #endif
  		if (info->attrs[L2TP_ATTR_IP_SADDR] &&
  		    info->attrs[L2TP_ATTR_IP_DADDR]) {
67b61f6c1   Jiri Benc   netlink: implemen...
201
  			cfg.local_ip.s_addr = nla_get_in_addr(
f9bac8df9   Chris Elston   l2tp: netlink api...
202
  				info->attrs[L2TP_ATTR_IP_SADDR]);
67b61f6c1   Jiri Benc   netlink: implemen...
203
  			cfg.peer_ip.s_addr = nla_get_in_addr(
f9bac8df9   Chris Elston   l2tp: netlink api...
204
205
206
207
208
  				info->attrs[L2TP_ATTR_IP_DADDR]);
  		} else {
  			ret = -EINVAL;
  			goto out;
  		}
789a4a2c6   James Chapman   l2tp: Add support...
209
210
211
212
  		if (info->attrs[L2TP_ATTR_UDP_SPORT])
  			cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]);
  		if (info->attrs[L2TP_ATTR_UDP_DPORT])
  			cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
57ceb8611   Asbjørn Sloth Tønnesen   net: l2tp: cleanu...
213
214
  		cfg.use_udp_checksums = nla_get_flag(
  			info->attrs[L2TP_ATTR_UDP_CSUM]);
6b649feaf   Tom Herbert   l2tp: Add support...
215
216
  
  #if IS_ENABLED(CONFIG_IPV6)
57ceb8611   Asbjørn Sloth Tønnesen   net: l2tp: cleanu...
217
218
219
220
  		cfg.udp6_zero_tx_checksums = nla_get_flag(
  			info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
  		cfg.udp6_zero_rx_checksums = nla_get_flag(
  			info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
6b649feaf   Tom Herbert   l2tp: Add support...
221
  #endif
309795f4b   James Chapman   l2tp: Add netlink...
222
  	}
309795f4b   James Chapman   l2tp: Add netlink...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  
  	if (info->attrs[L2TP_ATTR_DEBUG])
  		cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
  
  	tunnel = l2tp_tunnel_find(net, tunnel_id);
  	if (tunnel != NULL) {
  		ret = -EEXIST;
  		goto out;
  	}
  
  	ret = -EINVAL;
  	switch (cfg.encap) {
  	case L2TP_ENCAPTYPE_UDP:
  	case L2TP_ENCAPTYPE_IP:
  		ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id,
  					 peer_tunnel_id, &cfg, &tunnel);
  		break;
  	}
33f72e6f0   Bill Hong   l2tp : multicast ...
241
242
243
  	if (ret >= 0)
  		ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
  					 tunnel, L2TP_CMD_TUNNEL_CREATE);
309795f4b   James Chapman   l2tp: Add netlink...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  out:
  	return ret;
  }
  
  static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info)
  {
  	struct l2tp_tunnel *tunnel;
  	u32 tunnel_id;
  	int ret = 0;
  	struct net *net = genl_info_net(info);
  
  	if (!info->attrs[L2TP_ATTR_CONN_ID]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
bb0a32ce4   Guillaume Nault   l2tp: hold tunnel...
260
261
  	tunnel = l2tp_tunnel_get(net, tunnel_id);
  	if (!tunnel) {
309795f4b   James Chapman   l2tp: Add netlink...
262
263
264
  		ret = -ENODEV;
  		goto out;
  	}
33f72e6f0   Bill Hong   l2tp : multicast ...
265
266
  	l2tp_tunnel_notify(&l2tp_nl_family, info,
  			   tunnel, L2TP_CMD_TUNNEL_DELETE);
ff62605e0   Jiri Slaby   l2tp: cleanup l2t...
267
  	l2tp_tunnel_delete(tunnel);
309795f4b   James Chapman   l2tp: Add netlink...
268

bb0a32ce4   Guillaume Nault   l2tp: hold tunnel...
269
  	l2tp_tunnel_dec_refcount(tunnel);
309795f4b   James Chapman   l2tp: Add netlink...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  out:
  	return ret;
  }
  
  static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info)
  {
  	struct l2tp_tunnel *tunnel;
  	u32 tunnel_id;
  	int ret = 0;
  	struct net *net = genl_info_net(info);
  
  	if (!info->attrs[L2TP_ATTR_CONN_ID]) {
  		ret = -EINVAL;
  		goto out;
  	}
  	tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
8c0e42152   Guillaume Nault   l2tp: hold tunnel...
286
287
  	tunnel = l2tp_tunnel_get(net, tunnel_id);
  	if (!tunnel) {
309795f4b   James Chapman   l2tp: Add netlink...
288
289
290
291
292
293
  		ret = -ENODEV;
  		goto out;
  	}
  
  	if (info->attrs[L2TP_ATTR_DEBUG])
  		tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
33f72e6f0   Bill Hong   l2tp : multicast ...
294
295
  	ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
  				 tunnel, L2TP_CMD_TUNNEL_MODIFY);
8c0e42152   Guillaume Nault   l2tp: hold tunnel...
296
  	l2tp_tunnel_dec_refcount(tunnel);
309795f4b   James Chapman   l2tp: Add netlink...
297
298
299
  out:
  	return ret;
  }
15e473046   Eric W. Biederman   netlink: Rename p...
300
  static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
33f72e6f0   Bill Hong   l2tp : multicast ...
301
  			       struct l2tp_tunnel *tunnel, u8 cmd)
309795f4b   James Chapman   l2tp: Add netlink...
302
303
304
305
306
  {
  	void *hdr;
  	struct nlattr *nest;
  	struct sock *sk = NULL;
  	struct inet_sock *inet;
f9bac8df9   Chris Elston   l2tp: netlink api...
307
308
309
  #if IS_ENABLED(CONFIG_IPV6)
  	struct ipv6_pinfo *np = NULL;
  #endif
309795f4b   James Chapman   l2tp: Add netlink...
310

33f72e6f0   Bill Hong   l2tp : multicast ...
311
  	hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
7f8436a12   Wei Yongjun   l2tp: fix return ...
312
313
  	if (!hdr)
  		return -EMSGSIZE;
309795f4b   James Chapman   l2tp: Add netlink...
314

60aed2abb   David S. Miller   l2tp: Stop using ...
315
316
317
318
319
320
  	if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
  	    nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_DEBUG, tunnel->debug) ||
  	    nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap))
  		goto nla_put_failure;
309795f4b   James Chapman   l2tp: Add netlink...
321
322
323
324
  
  	nest = nla_nest_start(skb, L2TP_ATTR_STATS);
  	if (nest == NULL)
  		goto nla_put_failure;
1c714a928   Nicolas Dichtel   l2tp: use nla_put...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  	if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
  			      atomic_long_read(&tunnel->stats.tx_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
  			      atomic_long_read(&tunnel->stats.tx_bytes),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
  			      atomic_long_read(&tunnel->stats.tx_errors),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
  			      atomic_long_read(&tunnel->stats.rx_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
  			      atomic_long_read(&tunnel->stats.rx_bytes),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
  			      atomic_long_read(&tunnel->stats.rx_seq_discards),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
  			      atomic_long_read(&tunnel->stats.rx_oos_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
  			      atomic_long_read(&tunnel->stats.rx_errors),
  			      L2TP_ATTR_STATS_PAD))
60aed2abb   David S. Miller   l2tp: Stop using ...
349
  		goto nla_put_failure;
309795f4b   James Chapman   l2tp: Add netlink...
350
351
352
353
354
  	nla_nest_end(skb, nest);
  
  	sk = tunnel->sock;
  	if (!sk)
  		goto out;
f9bac8df9   Chris Elston   l2tp: netlink api...
355
356
357
358
  #if IS_ENABLED(CONFIG_IPV6)
  	if (sk->sk_family == AF_INET6)
  		np = inet6_sk(sk);
  #endif
309795f4b   James Chapman   l2tp: Add netlink...
359
360
361
362
  	inet = inet_sk(sk);
  
  	switch (tunnel->encap) {
  	case L2TP_ENCAPTYPE_UDP:
7ff516ffe   Asbjørn Sloth Tønnesen   net: l2tp: only s...
363
364
365
366
367
  		switch (sk->sk_family) {
  		case AF_INET:
  			if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx))
  				goto nla_put_failure;
  			break;
97b7af097   Asbjørn Sloth Tønnesen   net: l2tp: netlin...
368
369
370
371
372
373
374
375
376
377
  #if IS_ENABLED(CONFIG_IPV6)
  		case AF_INET6:
  			if (udp_get_no_check6_tx(sk) &&
  			    nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_TX))
  				goto nla_put_failure;
  			if (udp_get_no_check6_rx(sk) &&
  			    nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_RX))
  				goto nla_put_failure;
  			break;
  #endif
7ff516ffe   Asbjørn Sloth Tønnesen   net: l2tp: only s...
378
  		}
60aed2abb   David S. Miller   l2tp: Stop using ...
379
  		if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
7ff516ffe   Asbjørn Sloth Tønnesen   net: l2tp: only s...
380
  		    nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)))
60aed2abb   David S. Miller   l2tp: Stop using ...
381
  			goto nla_put_failure;
309795f4b   James Chapman   l2tp: Add netlink...
382
383
  		/* NOBREAK */
  	case L2TP_ENCAPTYPE_IP:
f9bac8df9   Chris Elston   l2tp: netlink api...
384
385
  #if IS_ENABLED(CONFIG_IPV6)
  		if (np) {
930345ea6   Jiri Benc   netlink: implemen...
386
387
388
389
  			if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR,
  					     &np->saddr) ||
  			    nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR,
  					     &sk->sk_v6_daddr))
f9bac8df9   Chris Elston   l2tp: netlink api...
390
391
392
  				goto nla_put_failure;
  		} else
  #endif
930345ea6   Jiri Benc   netlink: implemen...
393
394
395
396
  		if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR,
  				    inet->inet_saddr) ||
  		    nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR,
  				    inet->inet_daddr))
60aed2abb   David S. Miller   l2tp: Stop using ...
397
  			goto nla_put_failure;
309795f4b   James Chapman   l2tp: Add netlink...
398
399
400
401
  		break;
  	}
  
  out:
053c095a8   Johannes Berg   netlink: make nlm...
402
403
  	genlmsg_end(skb, hdr);
  	return 0;
309795f4b   James Chapman   l2tp: Add netlink...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  
  nla_put_failure:
  	genlmsg_cancel(skb, hdr);
  	return -1;
  }
  
  static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
  {
  	struct l2tp_tunnel *tunnel;
  	struct sk_buff *msg;
  	u32 tunnel_id;
  	int ret = -ENOBUFS;
  	struct net *net = genl_info_net(info);
  
  	if (!info->attrs[L2TP_ATTR_CONN_ID]) {
  		ret = -EINVAL;
4e4b21da3   Guillaume Nault   l2tp: hold tunnel...
420
  		goto err;
309795f4b   James Chapman   l2tp: Add netlink...
421
422
423
  	}
  
  	tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
58050fce3   Thomas Graf   net: Use NLMSG_DE...
424
  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
309795f4b   James Chapman   l2tp: Add netlink...
425
426
  	if (!msg) {
  		ret = -ENOMEM;
4e4b21da3   Guillaume Nault   l2tp: hold tunnel...
427
428
429
430
431
432
433
  		goto err;
  	}
  
  	tunnel = l2tp_tunnel_get(net, tunnel_id);
  	if (!tunnel) {
  		ret = -ENODEV;
  		goto err_nlmsg;
309795f4b   James Chapman   l2tp: Add netlink...
434
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
435
  	ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
33f72e6f0   Bill Hong   l2tp : multicast ...
436
  				  NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
309795f4b   James Chapman   l2tp: Add netlink...
437
  	if (ret < 0)
4e4b21da3   Guillaume Nault   l2tp: hold tunnel...
438
439
440
  		goto err_nlmsg_tunnel;
  
  	l2tp_tunnel_dec_refcount(tunnel);
309795f4b   James Chapman   l2tp: Add netlink...
441

15e473046   Eric W. Biederman   netlink: Rename p...
442
  	return genlmsg_unicast(net, msg, info->snd_portid);
309795f4b   James Chapman   l2tp: Add netlink...
443

4e4b21da3   Guillaume Nault   l2tp: hold tunnel...
444
445
446
  err_nlmsg_tunnel:
  	l2tp_tunnel_dec_refcount(tunnel);
  err_nlmsg:
309795f4b   James Chapman   l2tp: Add netlink...
447
  	nlmsg_free(msg);
4e4b21da3   Guillaume Nault   l2tp: hold tunnel...
448
  err:
309795f4b   James Chapman   l2tp: Add netlink...
449
450
451
452
453
454
455
456
457
458
459
460
461
  	return ret;
  }
  
  static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	int ti = cb->args[0];
  	struct l2tp_tunnel *tunnel;
  	struct net *net = sock_net(skb->sk);
  
  	for (;;) {
  		tunnel = l2tp_tunnel_find_nth(net, ti);
  		if (tunnel == NULL)
  			goto out;
15e473046   Eric W. Biederman   netlink: Rename p...
462
  		if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
309795f4b   James Chapman   l2tp: Add netlink...
463
  					cb->nlh->nlmsg_seq, NLM_F_MULTI,
053c095a8   Johannes Berg   netlink: make nlm...
464
  					tunnel, L2TP_CMD_TUNNEL_GET) < 0)
309795f4b   James Chapman   l2tp: Add netlink...
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  			goto out;
  
  		ti++;
  	}
  
  out:
  	cb->args[0] = ti;
  
  	return skb->len;
  }
  
  static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info)
  {
  	u32 tunnel_id = 0;
  	u32 session_id;
  	u32 peer_session_id;
  	int ret = 0;
  	struct l2tp_tunnel *tunnel;
  	struct l2tp_session *session;
  	struct l2tp_session_cfg cfg = { 0, };
  	struct net *net = genl_info_net(info);
  
  	if (!info->attrs[L2TP_ATTR_CONN_ID]) {
  		ret = -EINVAL;
  		goto out;
  	}
e702c1204   Guillaume Nault   l2tp: hold tunnel...
491

309795f4b   James Chapman   l2tp: Add netlink...
492
  	tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
e702c1204   Guillaume Nault   l2tp: hold tunnel...
493
  	tunnel = l2tp_tunnel_get(net, tunnel_id);
309795f4b   James Chapman   l2tp: Add netlink...
494
495
496
497
498
499
500
  	if (!tunnel) {
  		ret = -ENODEV;
  		goto out;
  	}
  
  	if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
  		ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
501
  		goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
502
503
  	}
  	session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
309795f4b   James Chapman   l2tp: Add netlink...
504
505
506
  
  	if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
  		ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
507
  		goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
508
509
510
511
512
  	}
  	peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
  
  	if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
  		ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
513
  		goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
514
515
516
517
  	}
  	cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
  	if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
  		ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
518
  		goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
  	}
  
  	if (tunnel->version > 2) {
  		if (info->attrs[L2TP_ATTR_OFFSET])
  			cfg.offset = nla_get_u16(info->attrs[L2TP_ATTR_OFFSET]);
  
  		if (info->attrs[L2TP_ATTR_DATA_SEQ])
  			cfg.data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
  
  		cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT;
  		if (info->attrs[L2TP_ATTR_L2SPEC_TYPE])
  			cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]);
  
  		cfg.l2specific_len = 4;
  		if (info->attrs[L2TP_ATTR_L2SPEC_LEN])
  			cfg.l2specific_len = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_LEN]);
  
  		if (info->attrs[L2TP_ATTR_COOKIE]) {
  			u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
  			if (len > 8) {
  				ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
540
  				goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
541
542
543
544
545
546
547
548
  			}
  			cfg.cookie_len = len;
  			memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
  		}
  		if (info->attrs[L2TP_ATTR_PEER_COOKIE]) {
  			u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
  			if (len > 8) {
  				ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
549
  				goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
  			}
  			cfg.peer_cookie_len = len;
  			memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
  		}
  		if (info->attrs[L2TP_ATTR_IFNAME])
  			cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
  
  		if (info->attrs[L2TP_ATTR_VLAN_ID])
  			cfg.vlan_id = nla_get_u16(info->attrs[L2TP_ATTR_VLAN_ID]);
  	}
  
  	if (info->attrs[L2TP_ATTR_DEBUG])
  		cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
  
  	if (info->attrs[L2TP_ATTR_RECV_SEQ])
  		cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
  
  	if (info->attrs[L2TP_ATTR_SEND_SEQ])
  		cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
  
  	if (info->attrs[L2TP_ATTR_LNS_MODE])
  		cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
  
  	if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
  		cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
  
  	if (info->attrs[L2TP_ATTR_MTU])
  		cfg.mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
  
  	if (info->attrs[L2TP_ATTR_MRU])
  		cfg.mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
f1f39f911   stephen hemminger   l2tp: auto load t...
581
582
583
584
585
586
587
  #ifdef CONFIG_MODULES
  	if (l2tp_nl_cmd_ops[cfg.pw_type] == NULL) {
  		genl_unlock();
  		request_module("net-l2tp-type-%u", cfg.pw_type);
  		genl_lock();
  	}
  #endif
309795f4b   James Chapman   l2tp: Add netlink...
588
589
590
  	if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
  	    (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
  		ret = -EPROTONOSUPPORT;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
591
  		goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
592
593
594
595
596
597
598
599
600
  	}
  
  	/* Check that pseudowire-specific params are present */
  	switch (cfg.pw_type) {
  	case L2TP_PWTYPE_NONE:
  		break;
  	case L2TP_PWTYPE_ETH_VLAN:
  		if (!info->attrs[L2TP_ATTR_VLAN_ID]) {
  			ret = -EINVAL;
e702c1204   Guillaume Nault   l2tp: hold tunnel...
601
  			goto out_tunnel;
309795f4b   James Chapman   l2tp: Add netlink...
602
603
604
605
606
607
608
609
610
611
612
613
  		}
  		break;
  	case L2TP_PWTYPE_ETH:
  		break;
  	case L2TP_PWTYPE_PPP:
  	case L2TP_PWTYPE_PPP_AC:
  		break;
  	case L2TP_PWTYPE_IP:
  	default:
  		ret = -EPROTONOSUPPORT;
  		break;
  	}
f026bc29a   Guillaume Nault   l2tp: pass tunnel...
614
615
616
617
  	ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel,
  							   session_id,
  							   peer_session_id,
  							   &cfg);
309795f4b   James Chapman   l2tp: Add netlink...
618

33f72e6f0   Bill Hong   l2tp : multicast ...
619
  	if (ret >= 0) {
5e6a9e5a3   Guillaume Nault   l2tp: hold sessio...
620
621
  		session = l2tp_session_get(net, tunnel, session_id, false);
  		if (session) {
33f72e6f0   Bill Hong   l2tp : multicast ...
622
623
  			ret = l2tp_session_notify(&l2tp_nl_family, info, session,
  						  L2TP_CMD_SESSION_CREATE);
5e6a9e5a3   Guillaume Nault   l2tp: hold sessio...
624
625
  			l2tp_session_dec_refcount(session);
  		}
33f72e6f0   Bill Hong   l2tp : multicast ...
626
  	}
e702c1204   Guillaume Nault   l2tp: hold tunnel...
627
628
  out_tunnel:
  	l2tp_tunnel_dec_refcount(tunnel);
309795f4b   James Chapman   l2tp: Add netlink...
629
630
631
632
633
634
635
636
637
  out:
  	return ret;
  }
  
  static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info)
  {
  	int ret = 0;
  	struct l2tp_session *session;
  	u16 pw_type;
2777e2ab5   Guillaume Nault   l2tp: take a refe...
638
  	session = l2tp_nl_session_get(info, true);
309795f4b   James Chapman   l2tp: Add netlink...
639
640
641
642
  	if (session == NULL) {
  		ret = -ENODEV;
  		goto out;
  	}
33f72e6f0   Bill Hong   l2tp : multicast ...
643
644
  	l2tp_session_notify(&l2tp_nl_family, info,
  			    session, L2TP_CMD_SESSION_DELETE);
309795f4b   James Chapman   l2tp: Add netlink...
645
646
647
648
  	pw_type = session->pwtype;
  	if (pw_type < __L2TP_PWTYPE_MAX)
  		if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
  			ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
2777e2ab5   Guillaume Nault   l2tp: take a refe...
649
650
651
  	if (session->deref)
  		session->deref(session);
  	l2tp_session_dec_refcount(session);
309795f4b   James Chapman   l2tp: Add netlink...
652
653
654
655
656
657
658
659
  out:
  	return ret;
  }
  
  static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info)
  {
  	int ret = 0;
  	struct l2tp_session *session;
2777e2ab5   Guillaume Nault   l2tp: take a refe...
660
  	session = l2tp_nl_session_get(info, false);
309795f4b   James Chapman   l2tp: Add netlink...
661
662
663
664
665
666
667
668
669
670
671
672
673
  	if (session == NULL) {
  		ret = -ENODEV;
  		goto out;
  	}
  
  	if (info->attrs[L2TP_ATTR_DEBUG])
  		session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
  
  	if (info->attrs[L2TP_ATTR_DATA_SEQ])
  		session->data_seq = nla_get_u8(info->attrs[L2TP_ATTR_DATA_SEQ]);
  
  	if (info->attrs[L2TP_ATTR_RECV_SEQ])
  		session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
bb5016eac   Guillaume Nault   l2tp: fix manual ...
674
  	if (info->attrs[L2TP_ATTR_SEND_SEQ]) {
309795f4b   James Chapman   l2tp: Add netlink...
675
  		session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
bb5016eac   Guillaume Nault   l2tp: fix manual ...
676
677
  		l2tp_session_set_header_len(session, session->tunnel->version);
  	}
309795f4b   James Chapman   l2tp: Add netlink...
678
679
680
681
682
683
684
685
686
687
688
689
  
  	if (info->attrs[L2TP_ATTR_LNS_MODE])
  		session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
  
  	if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
  		session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
  
  	if (info->attrs[L2TP_ATTR_MTU])
  		session->mtu = nla_get_u16(info->attrs[L2TP_ATTR_MTU]);
  
  	if (info->attrs[L2TP_ATTR_MRU])
  		session->mru = nla_get_u16(info->attrs[L2TP_ATTR_MRU]);
33f72e6f0   Bill Hong   l2tp : multicast ...
690
691
  	ret = l2tp_session_notify(&l2tp_nl_family, info,
  				  session, L2TP_CMD_SESSION_MODIFY);
2777e2ab5   Guillaume Nault   l2tp: take a refe...
692
  	l2tp_session_dec_refcount(session);
309795f4b   James Chapman   l2tp: Add netlink...
693
694
695
  out:
  	return ret;
  }
15e473046   Eric W. Biederman   netlink: Rename p...
696
  static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
33f72e6f0   Bill Hong   l2tp : multicast ...
697
  				struct l2tp_session *session, u8 cmd)
309795f4b   James Chapman   l2tp: Add netlink...
698
699
700
701
702
703
704
  {
  	void *hdr;
  	struct nlattr *nest;
  	struct l2tp_tunnel *tunnel = session->tunnel;
  	struct sock *sk = NULL;
  
  	sk = tunnel->sock;
33f72e6f0   Bill Hong   l2tp : multicast ...
705
  	hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
7f8436a12   Wei Yongjun   l2tp: fix return ...
706
707
  	if (!hdr)
  		return -EMSGSIZE;
309795f4b   James Chapman   l2tp: Add netlink...
708

60aed2abb   David S. Miller   l2tp: Stop using ...
709
710
711
712
713
714
715
716
717
718
719
  	if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID,
  			session->peer_session_id) ||
  	    nla_put_u32(skb, L2TP_ATTR_DEBUG, session->debug) ||
  	    nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype) ||
  	    nla_put_u16(skb, L2TP_ATTR_MTU, session->mtu) ||
  	    (session->mru &&
  	     nla_put_u16(skb, L2TP_ATTR_MRU, session->mru)))
  		goto nla_put_failure;
e269ed26d   Alan Cox   l2tp: session is ...
720
  	if ((session->ifname[0] &&
60aed2abb   David S. Miller   l2tp: Stop using ...
721
722
723
724
725
726
727
728
729
730
  	     nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
  	    (session->cookie_len &&
  	     nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
  		     &session->cookie[0])) ||
  	    (session->peer_cookie_len &&
  	     nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len,
  		     &session->peer_cookie[0])) ||
  	    nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) ||
  	    nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) ||
  	    nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) ||
309795f4b   James Chapman   l2tp: Add netlink...
731
  #ifdef CONFIG_XFRM
60aed2abb   David S. Miller   l2tp: Stop using ...
732
733
  	    (((sk) && (sk->sk_policy[0] || sk->sk_policy[1])) &&
  	     nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) ||
309795f4b   James Chapman   l2tp: Add netlink...
734
  #endif
60aed2abb   David S. Miller   l2tp: Stop using ...
735
  	    (session->reorder_timeout &&
2175d87cc   Nicolas Dichtel   libnl: nla_put_ms...
736
737
  	     nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT,
  			   session->reorder_timeout, L2TP_ATTR_PAD)))
60aed2abb   David S. Miller   l2tp: Stop using ...
738
  		goto nla_put_failure;
5de7aee54   James Chapman   l2tp: fix locking...
739

309795f4b   James Chapman   l2tp: Add netlink...
740
741
742
  	nest = nla_nest_start(skb, L2TP_ATTR_STATS);
  	if (nest == NULL)
  		goto nla_put_failure;
5de7aee54   James Chapman   l2tp: fix locking...
743

1c714a928   Nicolas Dichtel   l2tp: use nla_put...
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
  	if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
  			      atomic_long_read(&session->stats.tx_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
  			      atomic_long_read(&session->stats.tx_bytes),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
  			      atomic_long_read(&session->stats.tx_errors),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
  			      atomic_long_read(&session->stats.rx_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
  			      atomic_long_read(&session->stats.rx_bytes),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
  			      atomic_long_read(&session->stats.rx_seq_discards),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
  			      atomic_long_read(&session->stats.rx_oos_packets),
  			      L2TP_ATTR_STATS_PAD) ||
  	    nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
  			      atomic_long_read(&session->stats.rx_errors),
  			      L2TP_ATTR_STATS_PAD))
60aed2abb   David S. Miller   l2tp: Stop using ...
768
  		goto nla_put_failure;
309795f4b   James Chapman   l2tp: Add netlink...
769
  	nla_nest_end(skb, nest);
053c095a8   Johannes Berg   netlink: make nlm...
770
771
  	genlmsg_end(skb, hdr);
  	return 0;
309795f4b   James Chapman   l2tp: Add netlink...
772
773
774
775
776
777
778
779
780
781
782
  
   nla_put_failure:
  	genlmsg_cancel(skb, hdr);
  	return -1;
  }
  
  static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
  {
  	struct l2tp_session *session;
  	struct sk_buff *msg;
  	int ret;
2777e2ab5   Guillaume Nault   l2tp: take a refe...
783
  	session = l2tp_nl_session_get(info, false);
309795f4b   James Chapman   l2tp: Add netlink...
784
785
  	if (session == NULL) {
  		ret = -ENODEV;
2777e2ab5   Guillaume Nault   l2tp: take a refe...
786
  		goto err;
309795f4b   James Chapman   l2tp: Add netlink...
787
  	}
58050fce3   Thomas Graf   net: Use NLMSG_DE...
788
  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
309795f4b   James Chapman   l2tp: Add netlink...
789
790
  	if (!msg) {
  		ret = -ENOMEM;
2777e2ab5   Guillaume Nault   l2tp: take a refe...
791
  		goto err_ref;
309795f4b   James Chapman   l2tp: Add netlink...
792
  	}
15e473046   Eric W. Biederman   netlink: Rename p...
793
  	ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
33f72e6f0   Bill Hong   l2tp : multicast ...
794
  				   0, session, L2TP_CMD_SESSION_GET);
309795f4b   James Chapman   l2tp: Add netlink...
795
  	if (ret < 0)
2777e2ab5   Guillaume Nault   l2tp: take a refe...
796
  		goto err_ref_msg;
309795f4b   James Chapman   l2tp: Add netlink...
797

2777e2ab5   Guillaume Nault   l2tp: take a refe...
798
  	ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
309795f4b   James Chapman   l2tp: Add netlink...
799

2777e2ab5   Guillaume Nault   l2tp: take a refe...
800
  	l2tp_session_dec_refcount(session);
309795f4b   James Chapman   l2tp: Add netlink...
801

2777e2ab5   Guillaume Nault   l2tp: take a refe...
802
803
804
805
806
807
808
  	return ret;
  
  err_ref_msg:
  	nlmsg_free(msg);
  err_ref:
  	l2tp_session_dec_refcount(session);
  err:
309795f4b   James Chapman   l2tp: Add netlink...
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
  	return ret;
  }
  
  static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb)
  {
  	struct net *net = sock_net(skb->sk);
  	struct l2tp_session *session;
  	struct l2tp_tunnel *tunnel = NULL;
  	int ti = cb->args[0];
  	int si = cb->args[1];
  
  	for (;;) {
  		if (tunnel == NULL) {
  			tunnel = l2tp_tunnel_find_nth(net, ti);
  			if (tunnel == NULL)
  				goto out;
  		}
e08293a4c   Guillaume Nault   l2tp: take refere...
826
  		session = l2tp_session_get_nth(tunnel, si, false);
309795f4b   James Chapman   l2tp: Add netlink...
827
828
829
830
831
832
  		if (session == NULL) {
  			ti++;
  			tunnel = NULL;
  			si = 0;
  			continue;
  		}
15e473046   Eric W. Biederman   netlink: Rename p...
833
  		if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
309795f4b   James Chapman   l2tp: Add netlink...
834
  					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
e08293a4c   Guillaume Nault   l2tp: take refere...
835
836
  					 session, L2TP_CMD_SESSION_GET) < 0) {
  			l2tp_session_dec_refcount(session);
309795f4b   James Chapman   l2tp: Add netlink...
837
  			break;
e08293a4c   Guillaume Nault   l2tp: take refere...
838
839
  		}
  		l2tp_session_dec_refcount(session);
309795f4b   James Chapman   l2tp: Add netlink...
840
841
842
843
844
845
846
847
848
849
  
  		si++;
  	}
  
  out:
  	cb->args[0] = ti;
  	cb->args[1] = si;
  
  	return skb->len;
  }
f5bb341e1   stephen hemminger   l2tp: make nla_po...
850
  static const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
309795f4b   James Chapman   l2tp: Add netlink...
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
  	[L2TP_ATTR_NONE]		= { .type = NLA_UNSPEC, },
  	[L2TP_ATTR_PW_TYPE]		= { .type = NLA_U16, },
  	[L2TP_ATTR_ENCAP_TYPE]		= { .type = NLA_U16, },
  	[L2TP_ATTR_OFFSET]		= { .type = NLA_U16, },
  	[L2TP_ATTR_DATA_SEQ]		= { .type = NLA_U8, },
  	[L2TP_ATTR_L2SPEC_TYPE]		= { .type = NLA_U8, },
  	[L2TP_ATTR_L2SPEC_LEN]		= { .type = NLA_U8, },
  	[L2TP_ATTR_PROTO_VERSION]	= { .type = NLA_U8, },
  	[L2TP_ATTR_CONN_ID]		= { .type = NLA_U32, },
  	[L2TP_ATTR_PEER_CONN_ID]	= { .type = NLA_U32, },
  	[L2TP_ATTR_SESSION_ID]		= { .type = NLA_U32, },
  	[L2TP_ATTR_PEER_SESSION_ID]	= { .type = NLA_U32, },
  	[L2TP_ATTR_UDP_CSUM]		= { .type = NLA_U8, },
  	[L2TP_ATTR_VLAN_ID]		= { .type = NLA_U16, },
  	[L2TP_ATTR_DEBUG]		= { .type = NLA_U32, },
  	[L2TP_ATTR_RECV_SEQ]		= { .type = NLA_U8, },
  	[L2TP_ATTR_SEND_SEQ]		= { .type = NLA_U8, },
  	[L2TP_ATTR_LNS_MODE]		= { .type = NLA_U8, },
  	[L2TP_ATTR_USING_IPSEC]		= { .type = NLA_U8, },
  	[L2TP_ATTR_RECV_TIMEOUT]	= { .type = NLA_MSECS, },
  	[L2TP_ATTR_FD]			= { .type = NLA_U32, },
  	[L2TP_ATTR_IP_SADDR]		= { .type = NLA_U32, },
  	[L2TP_ATTR_IP_DADDR]		= { .type = NLA_U32, },
  	[L2TP_ATTR_UDP_SPORT]		= { .type = NLA_U16, },
  	[L2TP_ATTR_UDP_DPORT]		= { .type = NLA_U16, },
  	[L2TP_ATTR_MTU]			= { .type = NLA_U16, },
  	[L2TP_ATTR_MRU]			= { .type = NLA_U16, },
  	[L2TP_ATTR_STATS]		= { .type = NLA_NESTED, },
f9bac8df9   Chris Elston   l2tp: netlink api...
879
880
881
882
883
884
885
886
  	[L2TP_ATTR_IP6_SADDR] = {
  		.type = NLA_BINARY,
  		.len = sizeof(struct in6_addr),
  	},
  	[L2TP_ATTR_IP6_DADDR] = {
  		.type = NLA_BINARY,
  		.len = sizeof(struct in6_addr),
  	},
309795f4b   James Chapman   l2tp: Add netlink...
887
888
889
890
891
892
893
894
895
896
897
898
899
  	[L2TP_ATTR_IFNAME] = {
  		.type = NLA_NUL_STRING,
  		.len = IFNAMSIZ - 1,
  	},
  	[L2TP_ATTR_COOKIE] = {
  		.type = NLA_BINARY,
  		.len = 8,
  	},
  	[L2TP_ATTR_PEER_COOKIE] = {
  		.type = NLA_BINARY,
  		.len = 8,
  	},
  };
4534de830   Johannes Berg   genetlink: make a...
900
  static const struct genl_ops l2tp_nl_ops[] = {
309795f4b   James Chapman   l2tp: Add netlink...
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
  	{
  		.cmd = L2TP_CMD_NOOP,
  		.doit = l2tp_nl_cmd_noop,
  		.policy = l2tp_nl_policy,
  		/* can be retrieved by unprivileged users */
  	},
  	{
  		.cmd = L2TP_CMD_TUNNEL_CREATE,
  		.doit = l2tp_nl_cmd_tunnel_create,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_TUNNEL_DELETE,
  		.doit = l2tp_nl_cmd_tunnel_delete,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_TUNNEL_MODIFY,
  		.doit = l2tp_nl_cmd_tunnel_modify,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_TUNNEL_GET,
  		.doit = l2tp_nl_cmd_tunnel_get,
  		.dumpit = l2tp_nl_cmd_tunnel_dump,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_SESSION_CREATE,
  		.doit = l2tp_nl_cmd_session_create,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_SESSION_DELETE,
  		.doit = l2tp_nl_cmd_session_delete,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_SESSION_MODIFY,
  		.doit = l2tp_nl_cmd_session_modify,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  	{
  		.cmd = L2TP_CMD_SESSION_GET,
  		.doit = l2tp_nl_cmd_session_get,
  		.dumpit = l2tp_nl_cmd_session_dump,
  		.policy = l2tp_nl_policy,
  		.flags = GENL_ADMIN_PERM,
  	},
  };
56989f6d8   Johannes Berg   genetlink: mark f...
958
  static struct genl_family l2tp_nl_family __ro_after_init = {
489111e5c   Johannes Berg   genetlink: static...
959
960
961
962
963
964
965
966
967
968
969
  	.name		= L2TP_GENL_NAME,
  	.version	= L2TP_GENL_VERSION,
  	.hdrsize	= 0,
  	.maxattr	= L2TP_ATTR_MAX,
  	.netnsok	= true,
  	.module		= THIS_MODULE,
  	.ops		= l2tp_nl_ops,
  	.n_ops		= ARRAY_SIZE(l2tp_nl_ops),
  	.mcgrps		= l2tp_multicast_group,
  	.n_mcgrps	= ARRAY_SIZE(l2tp_multicast_group),
  };
309795f4b   James Chapman   l2tp: Add netlink...
970
971
972
973
974
975
976
977
978
979
980
981
982
983
  int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops)
  {
  	int ret;
  
  	ret = -EINVAL;
  	if (pw_type >= __L2TP_PWTYPE_MAX)
  		goto err;
  
  	genl_lock();
  	ret = -EBUSY;
  	if (l2tp_nl_cmd_ops[pw_type])
  		goto out;
  
  	l2tp_nl_cmd_ops[pw_type] = ops;
8cb490144   David S. Miller   l2tp: Fix set-but...
984
  	ret = 0;
309795f4b   James Chapman   l2tp: Add netlink...
985
986
987
988
  
  out:
  	genl_unlock();
  err:
8cb490144   David S. Miller   l2tp: Fix set-but...
989
  	return ret;
309795f4b   James Chapman   l2tp: Add netlink...
990
991
992
993
994
995
996
997
998
999
1000
1001
  }
  EXPORT_SYMBOL_GPL(l2tp_nl_register_ops);
  
  void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type)
  {
  	if (pw_type < __L2TP_PWTYPE_MAX) {
  		genl_lock();
  		l2tp_nl_cmd_ops[pw_type] = NULL;
  		genl_unlock();
  	}
  }
  EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
56989f6d8   Johannes Berg   genetlink: mark f...
1002
  static int __init l2tp_nl_init(void)
309795f4b   James Chapman   l2tp: Add netlink...
1003
  {
a4ca44fa5   Joe Perches   net: l2tp: Standa...
1004
1005
  	pr_info("L2TP netlink interface
  ");
489111e5c   Johannes Berg   genetlink: static...
1006
  	return genl_register_family(&l2tp_nl_family);
309795f4b   James Chapman   l2tp: Add netlink...
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
  }
  
  static void l2tp_nl_cleanup(void)
  {
  	genl_unregister_family(&l2tp_nl_family);
  }
  
  module_init(l2tp_nl_init);
  module_exit(l2tp_nl_cleanup);
  
  MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
  MODULE_DESCRIPTION("L2TP netlink");
  MODULE_LICENSE("GPL");
  MODULE_VERSION("1.0");
e9412c370   Neil Horman   genetlink: Build ...
1021
  MODULE_ALIAS_GENL_FAMILY("l2tp");