]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/ipl2tp.c
tc: Correct json output for actions
[mirror_iproute2.git] / ip / ipl2tp.c
CommitLineData
38cd311a
SH
1/*
2 * ipl2tp.c "ip l2tp"
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Original Author: James Chapman <jchapman@katalix.com>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <errno.h>
18#include <sys/types.h>
19#include <sys/socket.h>
20#include <arpa/inet.h>
21#include <sys/ioctl.h>
22#include <linux/if.h>
23#include <linux/if_arp.h>
24#include <linux/ip.h>
25
38cd311a
SH
26#include <linux/genetlink.h>
27#include <linux/l2tp.h>
4ef9ff2a 28#include "libgenl.h"
38cd311a
SH
29
30#include "utils.h"
31#include "ip_common.h"
32
33enum {
34 L2TP_ADD,
35 L2TP_CHG,
36 L2TP_DEL,
37 L2TP_GET
38};
39
40struct l2tp_parm {
41 uint32_t tunnel_id;
42 uint32_t peer_tunnel_id;
43 uint32_t session_id;
44 uint32_t peer_session_id;
38cd311a
SH
45 enum l2tp_encap_type encap;
46 uint16_t local_udp_port;
47 uint16_t peer_udp_port;
48 int cookie_len;
49 uint8_t cookie[8];
50 int peer_cookie_len;
51 uint8_t peer_cookie[8];
6618e334
CE
52 inet_prefix local_ip;
53 inet_prefix peer_ip;
38cd311a
SH
54
55 uint16_t pw_type;
56 uint16_t mtu;
31f63e7c
AST
57 unsigned int udp6_csum_tx:1;
58 unsigned int udp6_csum_rx:1;
59 unsigned int udp_csum:1;
60 unsigned int recv_seq:1;
61 unsigned int send_seq:1;
62 unsigned int lns_mode:1;
63 unsigned int data_seq:2;
64 unsigned int tunnel:1;
65 unsigned int session:1;
38cd311a
SH
66 int reorder_timeout;
67 const char *ifname;
dd10baa5
JC
68 uint8_t l2spec_type;
69 uint8_t l2spec_len;
38cd311a
SH
70};
71
72struct l2tp_stats {
73 uint64_t data_rx_packets;
74 uint64_t data_rx_bytes;
75 uint64_t data_rx_errors;
76 uint64_t data_rx_oos_packets;
77 uint64_t data_rx_oos_discards;
78 uint64_t data_tx_packets;
79 uint64_t data_tx_bytes;
80 uint64_t data_tx_errors;
81};
82
83struct l2tp_data {
84 struct l2tp_parm config;
85 struct l2tp_stats stats;
86};
87
88/* netlink socket */
89static struct rtnl_handle genl_rth;
90static int genl_family = -1;
91
92/*****************************************************************************
93 * Netlink actions
94 *****************************************************************************/
95
96static int create_tunnel(struct l2tp_parm *p)
97{
6618e334
CE
98 uint32_t local_attr = L2TP_ATTR_IP_SADDR;
99 uint32_t peer_attr = L2TP_ATTR_IP_DADDR;
38cd311a 100
328d482c
JA
101 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
102 L2TP_CMD_TUNNEL_CREATE, NLM_F_REQUEST | NLM_F_ACK);
38cd311a
SH
103
104 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
105 addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
106 addattr8(&req.n, 1024, L2TP_ATTR_PROTO_VERSION, 3);
107 addattr16(&req.n, 1024, L2TP_ATTR_ENCAP_TYPE, p->encap);
108
6618e334
CE
109 if (p->local_ip.family == AF_INET6)
110 local_attr = L2TP_ATTR_IP6_SADDR;
281db53f
SH
111 addattr_l(&req.n, 1024, local_attr, &p->local_ip.data,
112 p->local_ip.bytelen);
6618e334
CE
113
114 if (p->peer_ip.family == AF_INET6)
115 peer_attr = L2TP_ATTR_IP6_DADDR;
281db53f
SH
116 addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data,
117 p->peer_ip.bytelen);
6618e334 118
38cd311a
SH
119 if (p->encap == L2TP_ENCAPTYPE_UDP) {
120 addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
121 addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
9bf9d05b 122 if (p->udp_csum)
c73fad78 123 addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1);
9bf9d05b
SW
124 if (!p->udp6_csum_tx)
125 addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX);
126 if (!p->udp6_csum_rx)
127 addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_RX);
38cd311a
SH
128 }
129
86bf43c7 130 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
38cd311a
SH
131 return -2;
132
133 return 0;
134}
135
136static int delete_tunnel(struct l2tp_parm *p)
137{
328d482c
JA
138 GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
139 L2TP_CMD_TUNNEL_DELETE, NLM_F_REQUEST | NLM_F_ACK);
38cd311a
SH
140
141 addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
142
86bf43c7 143 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
38cd311a
SH
144 return -2;
145
146 return 0;
147}
148
149static int create_session(struct l2tp_parm *p)
150{
328d482c
JA
151 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
152 L2TP_CMD_SESSION_CREATE, NLM_F_REQUEST | NLM_F_ACK);
38cd311a
SH
153
154 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
155 addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
156 addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
157 addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
158 addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type);
dd10baa5
JC
159 addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type);
160 addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len);
38cd311a 161
281db53f
SH
162 if (p->mtu)
163 addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu);
164 if (p->recv_seq)
165 addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1);
166 if (p->send_seq)
167 addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1);
168 if (p->lns_mode)
169 addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE);
170 if (p->data_seq)
171 addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq);
172 if (p->reorder_timeout)
173 addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
38cd311a 174 p->reorder_timeout);
281db53f
SH
175 if (p->cookie_len)
176 addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
177 p->cookie, p->cookie_len);
178 if (p->peer_cookie_len)
179 addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
180 p->peer_cookie, p->peer_cookie_len);
625df645 181 if (p->ifname)
38cd311a
SH
182 addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
183
86bf43c7 184 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
38cd311a
SH
185 return -2;
186
187 return 0;
188}
189
190static int delete_session(struct l2tp_parm *p)
191{
328d482c
JA
192 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
193 L2TP_CMD_SESSION_DELETE, NLM_F_REQUEST | NLM_F_ACK);
38cd311a
SH
194
195 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
196 addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
86bf43c7 197 if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
38cd311a
SH
198 return -2;
199
200 return 0;
201}
202
98453b65
SH
203static void print_cookie(const char *name, const char *fmt,
204 const uint8_t *cookie, int len)
38cd311a 205{
98453b65
SH
206 char abuf[32];
207 size_t n;
208
209 n = snprintf(abuf, sizeof(abuf),
210 "%02x%02x%02x%02x",
211 cookie[0], cookie[1], cookie[2], cookie[3]);
38cd311a 212 if (len == 8)
98453b65
SH
213 snprintf(abuf + n, sizeof(abuf) - n,
214 "%02x%02x%02x%02x",
215 cookie[4], cookie[5],
216 cookie[6], cookie[7]);
217
218 print_string(PRINT_ANY, name, fmt, abuf);
38cd311a
SH
219}
220
221static void print_tunnel(const struct l2tp_data *data)
222{
223 const struct l2tp_parm *p = &data->config;
6618e334 224 char buf[INET6_ADDRSTRLEN];
38cd311a 225
98453b65
SH
226 open_json_object(NULL);
227 print_uint(PRINT_ANY, "tunnel_id", "Tunnel %u,", p->tunnel_id);
228 print_string(PRINT_ANY, "encap", " encap %s",
229 p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
230 p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
231 print_string(PRINT_FP, NULL, "%s", _SL_);
232
233 print_string(PRINT_ANY, "local", " From %s ",
234 inet_ntop(p->local_ip.family, p->local_ip.data,
235 buf, sizeof(buf)));
236 print_string(PRINT_ANY, "peer", "to %s",
237 inet_ntop(p->peer_ip.family, p->peer_ip.data,
238 buf, sizeof(buf)));
239 print_string(PRINT_FP, NULL, "%s", _SL_);
240
241 print_uint(PRINT_ANY, "peer_tunnel", " Peer tunnel %u",
242 p->peer_tunnel_id);
243 print_string(PRINT_FP, NULL, "%s", _SL_);
38cd311a 244
f7982f5c 245 if (p->encap == L2TP_ENCAPTYPE_UDP) {
98453b65
SH
246 print_string(PRINT_FP, NULL,
247 " UDP source / dest ports:", NULL);
248
249 print_uint(PRINT_ANY, "local_port", " %hu",
250 p->local_udp_port);
251 print_uint(PRINT_ANY, "peer_port", "/%hu",
252 p->peer_udp_port);
253 print_string(PRINT_FP, NULL, "%s", _SL_);
f7982f5c
AST
254
255 switch (p->local_ip.family) {
256 case AF_INET:
98453b65
SH
257 print_bool(PRINT_JSON, "checksum",
258 NULL, p->udp_csum);
259 print_string(PRINT_FP, NULL,
260 " UDP checksum: %s\n",
261 p->udp_csum ? "enabled" : "disabled");
f7982f5c
AST
262 break;
263 case AF_INET6:
98453b65
SH
264 if (is_json_context()) {
265 print_bool(PRINT_JSON, "checksum_tx",
266 NULL, p->udp6_csum_tx);
267
268 print_bool(PRINT_JSON, "checksum_rx",
269 NULL, p->udp6_csum_tx);
270 } else {
271 printf(" UDP checksum: %s%s%s%s\n",
272 p->udp6_csum_tx && p->udp6_csum_rx
273 ? "enabled" : "",
274 p->udp6_csum_tx && !p->udp6_csum_rx
275 ? "tx" : "",
276 !p->udp6_csum_tx && p->udp6_csum_rx
277 ? "rx" : "",
278 !p->udp6_csum_tx && !p->udp6_csum_rx
279 ? "disabled" : "");
280 }
f7982f5c
AST
281 break;
282 }
283 }
98453b65 284 close_json_object();
38cd311a
SH
285}
286
287static void print_session(struct l2tp_data *data)
288{
289 struct l2tp_parm *p = &data->config;
290
98453b65 291 open_json_object(NULL);
38cd311a 292
98453b65
SH
293 print_uint(PRINT_ANY, "session_id", "Session %u", p->session_id);
294 print_uint(PRINT_ANY, "tunnel_id", " in tunnel %u", p->tunnel_id);
295 print_string(PRINT_FP, NULL, "%s", _SL_);
296
297 print_uint(PRINT_ANY, "peer_session_id",
298 " Peer session %u,", p->peer_session_id);
299 print_uint(PRINT_ANY, "peer_tunnel_id",
300 " tunnel %u", p->peer_tunnel_id);
301 print_string(PRINT_FP, NULL, "%s", _SL_);
302
303 if (p->ifname != NULL) {
304 print_color_string(PRINT_ANY, COLOR_IFNAME,
305 "interface", " interface name: %s" , p->ifname);
306 print_string(PRINT_FP, NULL, "%s", _SL_);
307 }
308
2f75c5cf
GN
309 print_uint(PRINT_ANY, "offset", " offset %u,", 0);
310 print_uint(PRINT_ANY, "peer_offset", " peer offset %u\n", 0);
281db53f 311
38cd311a 312 if (p->cookie_len > 0)
98453b65
SH
313 print_cookie("cookie", "cookie",
314 p->cookie, p->cookie_len);
38cd311a 315 if (p->peer_cookie_len > 0)
98453b65
SH
316 print_cookie("peer_cookie", "peer cookie",
317 p->peer_cookie, p->peer_cookie_len);
38cd311a 318
3649d018 319 if (p->reorder_timeout != 0)
98453b65
SH
320 print_uint(PRINT_ANY, "reorder_timeout",
321 " reorder timeout: %u", p->reorder_timeout);
322
323
8a11421a 324 if (p->send_seq || p->recv_seq) {
98453b65
SH
325 print_string(PRINT_FP, NULL, "%s sequence numbering:", _SL_);
326
281db53f 327 if (p->send_seq)
98453b65 328 print_null(PRINT_ANY, "send_seq", " send", NULL);
281db53f 329 if (p->recv_seq)
98453b65
SH
330 print_null(PRINT_ANY, "recv_seq", " recv", NULL);
331
8a11421a 332 }
98453b65
SH
333 print_string(PRINT_FP, NULL, "\n", NULL);
334 close_json_object();
38cd311a
SH
335}
336
337static int get_response(struct nlmsghdr *n, void *arg)
338{
339 struct genlmsghdr *ghdr;
340 struct l2tp_data *data = arg;
341 struct l2tp_parm *p = &data->config;
342 struct rtattr *attrs[L2TP_ATTR_MAX + 1];
62f9f94a 343 struct rtattr *nla_stats, *rta;
38cd311a
SH
344 int len;
345
346 /* Validate message and parse attributes */
347 if (n->nlmsg_type == NLMSG_ERROR)
348 return -EBADMSG;
349
350 ghdr = NLMSG_DATA(n);
351 len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr));
352 if (len < 0)
353 return -1;
354
355 parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
356
357 if (attrs[L2TP_ATTR_PW_TYPE])
358 p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]);
359 if (attrs[L2TP_ATTR_ENCAP_TYPE])
360 p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
38cd311a
SH
361 if (attrs[L2TP_ATTR_DATA_SEQ])
362 p->data_seq = rta_getattr_u16(attrs[L2TP_ATTR_DATA_SEQ]);
363 if (attrs[L2TP_ATTR_CONN_ID])
364 p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]);
365 if (attrs[L2TP_ATTR_PEER_CONN_ID])
366 p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
367 if (attrs[L2TP_ATTR_SESSION_ID])
368 p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]);
369 if (attrs[L2TP_ATTR_PEER_SESSION_ID])
370 p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
dd10baa5
JC
371 if (attrs[L2TP_ATTR_L2SPEC_TYPE])
372 p->l2spec_type = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_TYPE]);
373 if (attrs[L2TP_ATTR_L2SPEC_LEN])
374 p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
38cd311a 375
c73fad78
AST
376 if (attrs[L2TP_ATTR_UDP_CSUM])
377 p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]);
378
35cc6ded
AST
379 p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX];
380 p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX];
381
38cd311a
SH
382 if (attrs[L2TP_ATTR_COOKIE])
383 memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
384 p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
385
386 if (attrs[L2TP_ATTR_PEER_COOKIE])
387 memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
388 p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
389
4d51b333
AST
390 if (attrs[L2TP_ATTR_RECV_SEQ])
391 p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]);
392 if (attrs[L2TP_ATTR_SEND_SEQ])
393 p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]);
38cd311a
SH
394
395 if (attrs[L2TP_ATTR_RECV_TIMEOUT])
396 p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]);
62f9f94a
SP
397
398 rta = attrs[L2TP_ATTR_IP_SADDR];
399 p->local_ip.family = AF_INET;
400 if (!rta) {
401 rta = attrs[L2TP_ATTR_IP6_SADDR];
6618e334 402 p->local_ip.family = AF_INET6;
6618e334 403 }
62f9f94a
SP
404 if (rta && get_addr_rta(&p->local_ip, rta, p->local_ip.family))
405 return -1;
406
407 rta = attrs[L2TP_ATTR_IP_DADDR];
408 p->peer_ip.family = AF_INET;
409 if (!rta) {
410 rta = attrs[L2TP_ATTR_IP6_DADDR];
6618e334 411 p->peer_ip.family = AF_INET6;
6618e334 412 }
62f9f94a
SP
413 if (rta && get_addr_rta(&p->peer_ip, rta, p->peer_ip.family))
414 return -1;
415
38cd311a
SH
416 if (attrs[L2TP_ATTR_UDP_SPORT])
417 p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]);
418 if (attrs[L2TP_ATTR_UDP_DPORT])
419 p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]);
420 if (attrs[L2TP_ATTR_MTU])
421 p->mtu = rta_getattr_u16(attrs[L2TP_ATTR_MTU]);
422 if (attrs[L2TP_ATTR_IFNAME])
423 p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]);
424
425 nla_stats = attrs[L2TP_ATTR_STATS];
426 if (nla_stats) {
427 struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1];
428
429 parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats);
430
431 if (tb[L2TP_ATTR_TX_PACKETS])
432 data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]);
433 if (tb[L2TP_ATTR_TX_BYTES])
434 data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]);
435 if (tb[L2TP_ATTR_TX_ERRORS])
436 data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]);
437 if (tb[L2TP_ATTR_RX_PACKETS])
438 data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]);
439 if (tb[L2TP_ATTR_RX_BYTES])
440 data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]);
441 if (tb[L2TP_ATTR_RX_ERRORS])
442 data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]);
443 if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
444 data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
445 if (tb[L2TP_ATTR_RX_OOS_PACKETS])
446 data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
447 }
448
449 return 0;
450}
451
281db53f
SH
452static int session_nlmsg(const struct sockaddr_nl *who,
453 struct nlmsghdr *n, void *arg)
38cd311a
SH
454{
455 int ret = get_response(n, arg);
456
457 if (ret == 0)
458 print_session(arg);
459
460 return ret;
461}
462
463static int get_session(struct l2tp_data *p)
464{
328d482c
JA
465 GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
466 L2TP_CMD_SESSION_GET,
467 NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
38cd311a 468
328d482c 469 req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
38cd311a
SH
470
471 if (p->config.tunnel_id && p->config.session_id) {
472 addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
281db53f
SH
473 addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID,
474 p->config.session_id);
38cd311a
SH
475 }
476
477 if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
478 return -2;
479
98453b65 480 new_json_obj(json);
38cd311a
SH
481 if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
482 fprintf(stderr, "Dump terminated\n");
483 exit(1);
484 }
98453b65
SH
485 delete_json_obj();
486 fflush(stdout);
38cd311a
SH
487
488 return 0;
489}
490
281db53f
SH
491static int tunnel_nlmsg(const struct sockaddr_nl *who,
492 struct nlmsghdr *n, void *arg)
38cd311a
SH
493{
494 int ret = get_response(n, arg);
495
496 if (ret == 0)
497 print_tunnel(arg);
498
499 return ret;
500}
501
502static int get_tunnel(struct l2tp_data *p)
503{
328d482c
JA
504 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
505 L2TP_CMD_TUNNEL_GET,
506 NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
38cd311a 507
328d482c 508 req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
38cd311a
SH
509
510 if (p->config.tunnel_id)
511 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
512
513 if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
514 return -2;
515
98453b65 516 new_json_obj(json);
38cd311a
SH
517 if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
518 fprintf(stderr, "Dump terminated\n");
519 exit(1);
520 }
98453b65
SH
521 delete_json_obj();
522 fflush(stdout);
38cd311a
SH
523
524 return 0;
525}
526
527/*****************************************************************************
528 * Command parser
529 *****************************************************************************/
530
38cd311a
SH
531static void usage(void) __attribute__((noreturn));
532
533static void usage(void)
534{
281db53f
SH
535 fprintf(stderr, "Usage: ip l2tp add tunnel\n"
536 " remote ADDR local ADDR\n"
537 " tunnel_id ID peer_tunnel_id ID\n"
538 " [ encap { ip | udp } ]\n"
539 " [ udp_sport PORT ] [ udp_dport PORT ]\n"
540 " [ udp_csum { on | off } ]\n"
541 " [ udp6_csum_tx { on | off } ]\n"
542 " [ udp6_csum_rx { on | off } ]\n"
543 "Usage: ip l2tp add session [ name NAME ]\n"
544 " tunnel_id ID\n"
545 " session_id ID peer_session_id ID\n"
546 " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n"
281db53f
SH
547 " [ seq { none | send | recv | both } ]\n"
548 " [ l2spec_type L2SPEC ]\n"
549 " ip l2tp del tunnel tunnel_id ID\n"
550 " ip l2tp del session tunnel_id ID session_id ID\n"
551 " ip l2tp show tunnel [ tunnel_id ID ]\n"
552 " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n"
553 "\n"
554 "Where: NAME := STRING\n"
555 " ADDR := { IP_ADDRESS | any }\n"
556 " PORT := { 0..65535 }\n"
557 " ID := { 1..4294967295 }\n"
558 " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n"
559 " L2SPEC := { none | default }\n");
560
38cd311a
SH
561 exit(-1);
562}
563
564static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
565{
566 memset(p, 0, sizeof(*p));
567
568 if (argc == 0)
569 usage();
570
dd10baa5
JC
571 /* Defaults */
572 p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
573 p->l2spec_len = 4;
9bf9d05b
SW
574 p->udp6_csum_rx = 1;
575 p->udp6_csum_tx = 1;
dd10baa5 576
38cd311a
SH
577 while (argc > 0) {
578 if (strcmp(*argv, "encap") == 0) {
579 NEXT_ARG();
580 if (strcmp(*argv, "ip") == 0) {
581 p->encap = L2TP_ENCAPTYPE_IP;
582 } else if (strcmp(*argv, "udp") == 0) {
583 p->encap = L2TP_ENCAPTYPE_UDP;
584 } else {
14645ec2 585 fprintf(stderr, "Unknown tunnel encapsulation \"%s\"\n", *argv);
38cd311a
SH
586 exit(-1);
587 }
ae5555d3
JV
588 } else if (strcmp(*argv, "name") == 0) {
589 NEXT_ARG();
625df645
PS
590 if (check_ifname(*argv))
591 invarg("\"name\" not a valid ifname", *argv);
ae5555d3 592 p->ifname = *argv;
38cd311a
SH
593 } else if (strcmp(*argv, "remote") == 0) {
594 NEXT_ARG();
6618e334
CE
595 if (get_addr(&p->peer_ip, *argv, AF_UNSPEC))
596 invarg("invalid remote address\n", *argv);
38cd311a
SH
597 } else if (strcmp(*argv, "local") == 0) {
598 NEXT_ARG();
6618e334
CE
599 if (get_addr(&p->local_ip, *argv, AF_UNSPEC))
600 invarg("invalid local address\n", *argv);
38cd311a
SH
601 } else if ((strcmp(*argv, "tunnel_id") == 0) ||
602 (strcmp(*argv, "tid") == 0)) {
603 __u32 uval;
56f5daac 604
38cd311a
SH
605 NEXT_ARG();
606 if (get_u32(&uval, *argv, 0))
607 invarg("invalid ID\n", *argv);
608 p->tunnel_id = uval;
609 } else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
610 (strcmp(*argv, "ptid") == 0)) {
611 __u32 uval;
56f5daac 612
38cd311a
SH
613 NEXT_ARG();
614 if (get_u32(&uval, *argv, 0))
615 invarg("invalid ID\n", *argv);
616 p->peer_tunnel_id = uval;
617 } else if ((strcmp(*argv, "session_id") == 0) ||
618 (strcmp(*argv, "sid") == 0)) {
619 __u32 uval;
56f5daac 620
38cd311a
SH
621 NEXT_ARG();
622 if (get_u32(&uval, *argv, 0))
623 invarg("invalid ID\n", *argv);
624 p->session_id = uval;
625 } else if ((strcmp(*argv, "peer_session_id") == 0) ||
626 (strcmp(*argv, "psid") == 0)) {
627 __u32 uval;
56f5daac 628
38cd311a
SH
629 NEXT_ARG();
630 if (get_u32(&uval, *argv, 0))
631 invarg("invalid ID\n", *argv);
632 p->peer_session_id = uval;
633 } else if (strcmp(*argv, "udp_sport") == 0) {
634 __u16 uval;
56f5daac 635
38cd311a
SH
636 NEXT_ARG();
637 if (get_u16(&uval, *argv, 0))
638 invarg("invalid port\n", *argv);
639 p->local_udp_port = uval;
640 } else if (strcmp(*argv, "udp_dport") == 0) {
641 __u16 uval;
56f5daac 642
38cd311a
SH
643 NEXT_ARG();
644 if (get_u16(&uval, *argv, 0))
645 invarg("invalid port\n", *argv);
646 p->peer_udp_port = uval;
9bf9d05b
SW
647 } else if (strcmp(*argv, "udp_csum") == 0) {
648 NEXT_ARG();
649 if (strcmp(*argv, "on") == 0)
650 p->udp_csum = 1;
651 else if (strcmp(*argv, "off") == 0)
652 p->udp_csum = 0;
653 else
654 invarg("invalid option for udp_csum\n", *argv);
655 } else if (strcmp(*argv, "udp6_csum_rx") == 0) {
656 NEXT_ARG();
657 if (strcmp(*argv, "on") == 0)
658 p->udp6_csum_rx = 1;
659 else if (strcmp(*argv, "off") == 0)
660 p->udp6_csum_rx = 0;
661 else
662 invarg("invalid option for udp6_csum_rx\n"
663 , *argv);
664 } else if (strcmp(*argv, "udp6_csum_tx") == 0) {
665 NEXT_ARG();
666 if (strcmp(*argv, "on") == 0)
667 p->udp6_csum_tx = 1;
668 else if (strcmp(*argv, "off") == 0)
669 p->udp6_csum_tx = 0;
670 else
671 invarg("invalid option for udp6_csum_tx\n"
672 , *argv);
38cd311a 673 } else if (strcmp(*argv, "offset") == 0) {
2f75c5cf 674 fprintf(stderr, "Ignoring option \"offset\"\n");
38cd311a 675 NEXT_ARG();
38cd311a 676 } else if (strcmp(*argv, "peer_offset") == 0) {
2f75c5cf 677 fprintf(stderr, "Ignoring option \"peer_offset\"\n");
38cd311a 678 NEXT_ARG();
38cd311a
SH
679 } else if (strcmp(*argv, "cookie") == 0) {
680 int slen;
56f5daac 681
38cd311a
SH
682 NEXT_ARG();
683 slen = strlen(*argv);
684 if ((slen != 8) && (slen != 16))
685 invarg("cookie must be either 8 or 16 hex digits\n", *argv);
686
687 p->cookie_len = slen / 2;
688 if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
689 invarg("cookie must be a hex string\n", *argv);
690 } else if (strcmp(*argv, "peer_cookie") == 0) {
691 int slen;
56f5daac 692
38cd311a
SH
693 NEXT_ARG();
694 slen = strlen(*argv);
695 if ((slen != 8) && (slen != 16))
696 invarg("cookie must be either 8 or 16 hex digits\n", *argv);
697
698 p->peer_cookie_len = slen / 2;
699 if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
700 invarg("cookie must be a hex string\n", *argv);
dd10baa5
JC
701 } else if (strcmp(*argv, "l2spec_type") == 0) {
702 NEXT_ARG();
703 if (strcasecmp(*argv, "default") == 0) {
704 p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
705 p->l2spec_len = 4;
706 } else if (strcasecmp(*argv, "none") == 0) {
707 p->l2spec_type = L2TP_L2SPECTYPE_NONE;
708 p->l2spec_len = 0;
709 } else {
281db53f
SH
710 fprintf(stderr,
711 "Unknown layer2specific header type \"%s\"\n",
712 *argv);
dd10baa5
JC
713 exit(-1);
714 }
8a11421a
AST
715 } else if (strcmp(*argv, "seq") == 0) {
716 NEXT_ARG();
717 if (strcasecmp(*argv, "both") == 0) {
718 p->recv_seq = 1;
719 p->send_seq = 1;
720 } else if (strcasecmp(*argv, "recv") == 0) {
721 p->recv_seq = 1;
722 } else if (strcasecmp(*argv, "send") == 0) {
723 p->send_seq = 1;
724 } else if (strcasecmp(*argv, "none") == 0) {
725 p->recv_seq = 0;
726 p->send_seq = 0;
727 } else {
281db53f
SH
728 fprintf(stderr,
729 "Unknown seq value \"%s\"\n", *argv);
8a11421a
AST
730 exit(-1);
731 }
38cd311a
SH
732 } else if (strcmp(*argv, "tunnel") == 0) {
733 p->tunnel = 1;
734 } else if (strcmp(*argv, "session") == 0) {
735 p->session = 1;
736 } else if (matches(*argv, "help") == 0) {
737 usage();
738 } else {
739 fprintf(stderr, "Unknown command: %s\n", *argv);
740 usage();
741 }
742
743 argc--; argv++;
744 }
745
746 return 0;
747}
748
749
750static int do_add(int argc, char **argv)
751{
752 struct l2tp_parm p;
753 int ret = 0;
754
755 if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
756 return -1;
757
758 if (!p.tunnel && !p.session)
759 missarg("tunnel or session");
760
761 if (p.tunnel_id == 0)
762 missarg("tunnel_id");
763
764 /* session_id and peer_session_id must be provided for sessions */
765 if ((p.session) && (p.peer_session_id == 0))
766 missarg("peer_session_id");
767 if ((p.session) && (p.session_id == 0))
768 missarg("session_id");
769
770 /* peer_tunnel_id is needed for tunnels */
771 if ((p.tunnel) && (p.peer_tunnel_id == 0))
772 missarg("peer_tunnel_id");
773
774 if (p.tunnel) {
6618e334 775 if (p.local_ip.family == AF_UNSPEC)
38cd311a
SH
776 missarg("local");
777
6618e334 778 if (p.peer_ip.family == AF_UNSPEC)
38cd311a
SH
779 missarg("remote");
780
781 if (p.encap == L2TP_ENCAPTYPE_UDP) {
782 if (p.local_udp_port == 0)
783 missarg("udp_sport");
784 if (p.peer_udp_port == 0)
785 missarg("udp_dport");
786 }
787
788 ret = create_tunnel(&p);
789 }
790
791 if (p.session) {
792 /* Only ethernet pseudowires supported */
793 p.pw_type = L2TP_PWTYPE_ETH;
794
795 ret = create_session(&p);
796 }
797
798 return ret;
799}
800
801static int do_del(int argc, char **argv)
802{
803 struct l2tp_parm p;
804
805 if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
806 return -1;
807
808 if (!p.tunnel && !p.session)
809 missarg("tunnel or session");
810
811 if ((p.tunnel) && (p.tunnel_id == 0))
812 missarg("tunnel_id");
813 if ((p.session) && (p.session_id == 0))
814 missarg("session_id");
815
816 if (p.session_id)
817 return delete_session(&p);
818 else
819 return delete_tunnel(&p);
820
821 return -1;
822}
823
824static int do_show(int argc, char **argv)
825{
826 struct l2tp_data data;
827 struct l2tp_parm *p = &data.config;
828
829 if (parse_args(argc, argv, L2TP_GET, p) < 0)
830 return -1;
831
832 if (!p->tunnel && !p->session)
833 missarg("tunnel or session");
834
835 if (p->session)
836 get_session(&data);
837 else
838 get_tunnel(&data);
839
840 return 0;
841}
842
38cd311a
SH
843int do_ipl2tp(int argc, char **argv)
844{
e8977766
PS
845 if (argc < 1 || !matches(*argv, "help"))
846 usage();
847
2b68cb77
SD
848 if (genl_init_handle(&genl_rth, L2TP_GENL_NAME, &genl_family))
849 exit(1);
38cd311a 850
38cd311a
SH
851 if (matches(*argv, "add") == 0)
852 return do_add(argc-1, argv+1);
6e30461e 853 if (matches(*argv, "delete") == 0)
38cd311a
SH
854 return do_del(argc-1, argv+1);
855 if (matches(*argv, "show") == 0 ||
856 matches(*argv, "lst") == 0 ||
857 matches(*argv, "list") == 0)
858 return do_show(argc-1, argv+1);
38cd311a 859
281db53f
SH
860 fprintf(stderr,
861 "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
38cd311a
SH
862 exit(-1);
863}