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