blob: 1f84c6149f3912f5b68a22f067f5adab921a34d6 [file] [log] [blame]
Stephen Hemminger38cd3112011-12-23 20:52:10 -08001/*
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
Stephen Hemminger38cd3112011-12-23 20:52:10 -080026#include <linux/genetlink.h>
27#include <linux/l2tp.h>
Julian Anastasov4ef9ff22012-09-11 12:04:34 +030028#include "libgenl.h"
Stephen Hemminger38cd3112011-12-23 20:52:10 -080029
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;
45 uint32_t offset;
46 uint32_t peer_offset;
47 enum l2tp_encap_type encap;
48 uint16_t local_udp_port;
49 uint16_t peer_udp_port;
50 int cookie_len;
51 uint8_t cookie[8];
52 int peer_cookie_len;
53 uint8_t peer_cookie[8];
Chris Elston6618e332012-05-01 04:25:22 +000054 inet_prefix local_ip;
55 inet_prefix peer_ip;
Stephen Hemminger38cd3112011-12-23 20:52:10 -080056
57 uint16_t pw_type;
58 uint16_t mtu;
59 int udp_csum:1;
60 int recv_seq:1;
61 int send_seq:1;
62 int lns_mode:1;
63 int data_seq:2;
64 int tunnel:1;
65 int session:1;
66 int reorder_timeout;
67 const char *ifname;
James Chapmandd10baa2013-03-26 06:49:22 +000068 uint8_t l2spec_type;
69 uint8_t l2spec_len;
Stephen Hemminger38cd3112011-12-23 20:52:10 -080070};
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{
Chris Elston6618e332012-05-01 04:25:22 +000098 uint32_t local_attr = L2TP_ATTR_IP_SADDR;
99 uint32_t peer_attr = L2TP_ATTR_IP_DADDR;
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800100
Julian Anastasov328d4822012-09-12 09:15:19 +0300101 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
102 L2TP_CMD_TUNNEL_CREATE, NLM_F_REQUEST | NLM_F_ACK);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800103
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
Chris Elston6618e332012-05-01 04:25:22 +0000109 if (p->local_ip.family == AF_INET6)
110 local_attr = L2TP_ATTR_IP6_SADDR;
111 addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, p->local_ip.bytelen);
112
113 if (p->peer_ip.family == AF_INET6)
114 peer_attr = L2TP_ATTR_IP6_DADDR;
115 addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, p->peer_ip.bytelen);
116
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800117 if (p->encap == L2TP_ENCAPTYPE_UDP) {
118 addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
119 addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
120 }
121
Stephen Hemmingerc079e122015-05-27 12:26:14 -0700122 if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800123 return -2;
124
125 return 0;
126}
127
128static int delete_tunnel(struct l2tp_parm *p)
129{
Julian Anastasov328d4822012-09-12 09:15:19 +0300130 GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
131 L2TP_CMD_TUNNEL_DELETE, NLM_F_REQUEST | NLM_F_ACK);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800132
133 addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->tunnel_id);
134
Stephen Hemmingerc079e122015-05-27 12:26:14 -0700135 if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800136 return -2;
137
138 return 0;
139}
140
141static int create_session(struct l2tp_parm *p)
142{
Julian Anastasov328d4822012-09-12 09:15:19 +0300143 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
144 L2TP_CMD_SESSION_CREATE, NLM_F_REQUEST | NLM_F_ACK);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800145
146 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
147 addattr32(&req.n, 1024, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
148 addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
149 addattr32(&req.n, 1024, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
150 addattr16(&req.n, 1024, L2TP_ATTR_PW_TYPE, p->pw_type);
James Chapmandd10baa2013-03-26 06:49:22 +0000151 addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type);
152 addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800153
154 if (p->mtu) addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu);
155 if (p->recv_seq) addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ);
156 if (p->send_seq) addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ);
157 if (p->lns_mode) addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE);
158 if (p->data_seq) addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq);
159 if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT,
160 p->reorder_timeout);
161 if (p->offset) addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset);
162 if (p->cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE,
163 p->cookie, p->cookie_len);
164 if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
165 p->peer_cookie, p->peer_cookie_len);
166 if (p->ifname && p->ifname[0])
167 addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
168
Stephen Hemmingerc079e122015-05-27 12:26:14 -0700169 if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800170 return -2;
171
172 return 0;
173}
174
175static int delete_session(struct l2tp_parm *p)
176{
Julian Anastasov328d4822012-09-12 09:15:19 +0300177 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
178 L2TP_CMD_SESSION_DELETE, NLM_F_REQUEST | NLM_F_ACK);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800179
180 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->tunnel_id);
181 addattr32(&req.n, 1024, L2TP_ATTR_SESSION_ID, p->session_id);
Stephen Hemmingerc079e122015-05-27 12:26:14 -0700182 if (rtnl_talk(&genl_rth, &req.n, NULL, 0) < 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800183 return -2;
184
185 return 0;
186}
187
188static void print_cookie(char *name, const uint8_t *cookie, int len)
189{
190 printf(" %s %02x%02x%02x%02x", name,
191 cookie[0], cookie[1],
192 cookie[2], cookie[3]);
193 if (len == 8)
194 printf("%02x%02x%02x%02x",
195 cookie[4], cookie[5],
196 cookie[6], cookie[7]);
197}
198
199static void print_tunnel(const struct l2tp_data *data)
200{
201 const struct l2tp_parm *p = &data->config;
Chris Elston6618e332012-05-01 04:25:22 +0000202 char buf[INET6_ADDRSTRLEN];
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800203
204 printf("Tunnel %u, encap %s\n",
205 p->tunnel_id,
206 p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
207 p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
Chris Elston6618e332012-05-01 04:25:22 +0000208 printf(" From %s ", inet_ntop(p->local_ip.family, p->local_ip.data, buf, sizeof(buf)));
209 printf("to %s\n", inet_ntop(p->peer_ip.family, p->peer_ip.data, buf, sizeof(buf)));
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800210 printf(" Peer tunnel %u\n",
211 p->peer_tunnel_id);
212
213 if (p->encap == L2TP_ENCAPTYPE_UDP)
214 printf(" UDP source / dest ports: %hu/%hu\n",
215 p->local_udp_port, p->peer_udp_port);
216}
217
218static void print_session(struct l2tp_data *data)
219{
220 struct l2tp_parm *p = &data->config;
221
222 printf("Session %u in tunnel %u\n",
223 p->session_id, p->tunnel_id);
224 printf(" Peer session %u, tunnel %u\n",
225 p->peer_session_id, p->peer_tunnel_id);
226
227 if (p->ifname != NULL) {
228 printf(" interface name: %s\n", p->ifname);
229 }
230 printf(" offset %u, peer offset %u\n",
231 p->offset, p->peer_offset);
232 if (p->cookie_len > 0)
233 print_cookie("cookie", p->cookie, p->cookie_len);
234 if (p->peer_cookie_len > 0)
235 print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
236
Stephen Hemminger3649d012015-09-11 15:26:58 -0700237 if (p->reorder_timeout != 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800238 printf(" reorder timeout: %u\n", p->reorder_timeout);
Stephen Hemminger3649d012015-09-11 15:26:58 -0700239 else
240 printf("\n");
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800241}
242
243static int get_response(struct nlmsghdr *n, void *arg)
244{
245 struct genlmsghdr *ghdr;
246 struct l2tp_data *data = arg;
247 struct l2tp_parm *p = &data->config;
248 struct rtattr *attrs[L2TP_ATTR_MAX + 1];
249 struct rtattr *nla_stats;
250 int len;
251
252 /* Validate message and parse attributes */
253 if (n->nlmsg_type == NLMSG_ERROR)
254 return -EBADMSG;
255
256 ghdr = NLMSG_DATA(n);
257 len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ghdr));
258 if (len < 0)
259 return -1;
260
261 parse_rtattr(attrs, L2TP_ATTR_MAX, (void *)ghdr + GENL_HDRLEN, len);
262
263 if (attrs[L2TP_ATTR_PW_TYPE])
264 p->pw_type = rta_getattr_u16(attrs[L2TP_ATTR_PW_TYPE]);
265 if (attrs[L2TP_ATTR_ENCAP_TYPE])
266 p->encap = rta_getattr_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
267 if (attrs[L2TP_ATTR_OFFSET])
268 p->offset = rta_getattr_u16(attrs[L2TP_ATTR_OFFSET]);
269 if (attrs[L2TP_ATTR_DATA_SEQ])
270 p->data_seq = rta_getattr_u16(attrs[L2TP_ATTR_DATA_SEQ]);
271 if (attrs[L2TP_ATTR_CONN_ID])
272 p->tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_CONN_ID]);
273 if (attrs[L2TP_ATTR_PEER_CONN_ID])
274 p->peer_tunnel_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
275 if (attrs[L2TP_ATTR_SESSION_ID])
276 p->session_id = rta_getattr_u32(attrs[L2TP_ATTR_SESSION_ID]);
277 if (attrs[L2TP_ATTR_PEER_SESSION_ID])
278 p->peer_session_id = rta_getattr_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
James Chapmandd10baa2013-03-26 06:49:22 +0000279 if (attrs[L2TP_ATTR_L2SPEC_TYPE])
280 p->l2spec_type = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_TYPE]);
281 if (attrs[L2TP_ATTR_L2SPEC_LEN])
282 p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800283
284 p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM];
285 if (attrs[L2TP_ATTR_COOKIE])
286 memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]),
287 p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE]));
288
289 if (attrs[L2TP_ATTR_PEER_COOKIE])
290 memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]),
291 p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE]));
292
293 p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ];
294 p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ];
295
296 if (attrs[L2TP_ATTR_RECV_TIMEOUT])
297 p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]);
Chris Elston6618e332012-05-01 04:25:22 +0000298 if (attrs[L2TP_ATTR_IP_SADDR]) {
299 p->local_ip.family = AF_INET;
300 p->local_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_SADDR]);
301 p->local_ip.bytelen = 4;
302 p->local_ip.bitlen = -1;
303 }
304 if (attrs[L2TP_ATTR_IP_DADDR]) {
305 p->peer_ip.family = AF_INET;
306 p->peer_ip.data[0] = rta_getattr_u32(attrs[L2TP_ATTR_IP_DADDR]);
307 p->peer_ip.bytelen = 4;
308 p->peer_ip.bitlen = -1;
309 }
310 if (attrs[L2TP_ATTR_IP6_SADDR]) {
311 p->local_ip.family = AF_INET6;
312 memcpy(&p->local_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_SADDR]),
313 p->local_ip.bytelen = 16);
314 p->local_ip.bitlen = -1;
315 }
316 if (attrs[L2TP_ATTR_IP6_DADDR]) {
317 p->peer_ip.family = AF_INET6;
318 memcpy(&p->peer_ip.data, RTA_DATA(attrs[L2TP_ATTR_IP6_DADDR]),
319 p->peer_ip.bytelen = 16);
320 p->peer_ip.bitlen = -1;
321 }
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800322 if (attrs[L2TP_ATTR_UDP_SPORT])
323 p->local_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_SPORT]);
324 if (attrs[L2TP_ATTR_UDP_DPORT])
325 p->peer_udp_port = rta_getattr_u16(attrs[L2TP_ATTR_UDP_DPORT]);
326 if (attrs[L2TP_ATTR_MTU])
327 p->mtu = rta_getattr_u16(attrs[L2TP_ATTR_MTU]);
328 if (attrs[L2TP_ATTR_IFNAME])
329 p->ifname = rta_getattr_str(attrs[L2TP_ATTR_IFNAME]);
330
331 nla_stats = attrs[L2TP_ATTR_STATS];
332 if (nla_stats) {
333 struct rtattr *tb[L2TP_ATTR_STATS_MAX + 1];
334
335 parse_rtattr_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats);
336
337 if (tb[L2TP_ATTR_TX_PACKETS])
338 data->stats.data_tx_packets = rta_getattr_u64(tb[L2TP_ATTR_TX_PACKETS]);
339 if (tb[L2TP_ATTR_TX_BYTES])
340 data->stats.data_tx_bytes = rta_getattr_u64(tb[L2TP_ATTR_TX_BYTES]);
341 if (tb[L2TP_ATTR_TX_ERRORS])
342 data->stats.data_tx_errors = rta_getattr_u64(tb[L2TP_ATTR_TX_ERRORS]);
343 if (tb[L2TP_ATTR_RX_PACKETS])
344 data->stats.data_rx_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_PACKETS]);
345 if (tb[L2TP_ATTR_RX_BYTES])
346 data->stats.data_rx_bytes = rta_getattr_u64(tb[L2TP_ATTR_RX_BYTES]);
347 if (tb[L2TP_ATTR_RX_ERRORS])
348 data->stats.data_rx_errors = rta_getattr_u64(tb[L2TP_ATTR_RX_ERRORS]);
349 if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
350 data->stats.data_rx_oos_discards = rta_getattr_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
351 if (tb[L2TP_ATTR_RX_OOS_PACKETS])
352 data->stats.data_rx_oos_packets = rta_getattr_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
353 }
354
355 return 0;
356}
357
358static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
359{
360 int ret = get_response(n, arg);
361
362 if (ret == 0)
363 print_session(arg);
364
365 return ret;
366}
367
368static int get_session(struct l2tp_data *p)
369{
Julian Anastasov328d4822012-09-12 09:15:19 +0300370 GENL_REQUEST(req, 128, genl_family, 0, L2TP_GENL_VERSION,
371 L2TP_CMD_SESSION_GET,
372 NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800373
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800374 req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
375
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800376 if (p->config.tunnel_id && p->config.session_id) {
377 addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
378 addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id);
379 }
380
381 if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
382 return -2;
383
384 if (rtnl_dump_filter(&genl_rth, session_nlmsg, p) < 0) {
385 fprintf(stderr, "Dump terminated\n");
386 exit(1);
387 }
388
389 return 0;
390}
391
392static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
393{
394 int ret = get_response(n, arg);
395
396 if (ret == 0)
397 print_tunnel(arg);
398
399 return ret;
400}
401
402static int get_tunnel(struct l2tp_data *p)
403{
Julian Anastasov328d4822012-09-12 09:15:19 +0300404 GENL_REQUEST(req, 1024, genl_family, 0, L2TP_GENL_VERSION,
405 L2TP_CMD_TUNNEL_GET,
406 NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800407
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800408 req.n.nlmsg_seq = genl_rth.dump = ++genl_rth.seq;
409
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800410 if (p->config.tunnel_id)
411 addattr32(&req.n, 1024, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
412
413 if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0)
414 return -2;
415
416 if (rtnl_dump_filter(&genl_rth, tunnel_nlmsg, p) < 0) {
417 fprintf(stderr, "Dump terminated\n");
418 exit(1);
419 }
420
421 return 0;
422}
423
424/*****************************************************************************
425 * Command parser
426 *****************************************************************************/
427
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800428static int hex2mem(const char *buf, uint8_t *mem, int count)
429{
430 int i, j;
431 int c;
432
433 for (i = 0, j = 0; i < count; i++, j += 2) {
Sabrina Dubroca609640f2016-06-03 16:45:47 +0200434 c = get_hex(buf[j]);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800435 if (c < 0)
436 goto err;
437
438 mem[i] = c << 4;
439
Sabrina Dubroca609640f2016-06-03 16:45:47 +0200440 c = get_hex(buf[j + 1]);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800441 if (c < 0)
442 goto err;
443
444 mem[i] |= c;
445 }
446
447 return 0;
448
449err:
450 return -1;
451}
452
453static void usage(void) __attribute__((noreturn));
454
455static void usage(void)
456{
457 fprintf(stderr, "Usage: ip l2tp add tunnel\n");
458 fprintf(stderr, " remote ADDR local ADDR\n");
459 fprintf(stderr, " tunnel_id ID peer_tunnel_id ID\n");
460 fprintf(stderr, " [ encap { ip | udp } ]\n");
461 fprintf(stderr, " [ udp_sport PORT ] [ udp_dport PORT ]\n");
João Valverdeae5555d2012-03-26 21:00:08 +0100462 fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n");
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800463 fprintf(stderr, " tunnel_id ID\n");
464 fprintf(stderr, " session_id ID peer_session_id ID\n");
465 fprintf(stderr, " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
466 fprintf(stderr, " [ offset OFFSET ] [ peer_offset OFFSET ]\n");
James Chapmandd10baa2013-03-26 06:49:22 +0000467 fprintf(stderr, " [ l2spec_type L2SPEC ]\n");
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800468 fprintf(stderr, " ip l2tp del tunnel tunnel_id ID\n");
469 fprintf(stderr, " ip l2tp del session tunnel_id ID session_id ID\n");
470 fprintf(stderr, " ip l2tp show tunnel [ tunnel_id ID ]\n");
471 fprintf(stderr, " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n");
472 fprintf(stderr, "\n");
473 fprintf(stderr, "Where: NAME := STRING\n");
474 fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n");
475 fprintf(stderr, " PORT := { 0..65535 }\n");
476 fprintf(stderr, " ID := { 1..4294967295 }\n");
477 fprintf(stderr, " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n");
James Chapmandd10baa2013-03-26 06:49:22 +0000478 fprintf(stderr, " L2SPEC := { none | default }\n");
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800479 exit(-1);
480}
481
482static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
483{
484 memset(p, 0, sizeof(*p));
485
486 if (argc == 0)
487 usage();
488
James Chapmandd10baa2013-03-26 06:49:22 +0000489 /* Defaults */
490 p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
491 p->l2spec_len = 4;
492
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800493 while (argc > 0) {
494 if (strcmp(*argv, "encap") == 0) {
495 NEXT_ARG();
496 if (strcmp(*argv, "ip") == 0) {
497 p->encap = L2TP_ENCAPTYPE_IP;
498 } else if (strcmp(*argv, "udp") == 0) {
499 p->encap = L2TP_ENCAPTYPE_UDP;
500 } else {
Kees van Reeuwijk14645ec2013-02-08 03:32:36 +0000501 fprintf(stderr, "Unknown tunnel encapsulation \"%s\"\n", *argv);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800502 exit(-1);
503 }
João Valverdeae5555d2012-03-26 21:00:08 +0100504 } else if (strcmp(*argv, "name") == 0) {
505 NEXT_ARG();
506 p->ifname = *argv;
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800507 } else if (strcmp(*argv, "remote") == 0) {
508 NEXT_ARG();
Chris Elston6618e332012-05-01 04:25:22 +0000509 if (get_addr(&p->peer_ip, *argv, AF_UNSPEC))
510 invarg("invalid remote address\n", *argv);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800511 } else if (strcmp(*argv, "local") == 0) {
512 NEXT_ARG();
Chris Elston6618e332012-05-01 04:25:22 +0000513 if (get_addr(&p->local_ip, *argv, AF_UNSPEC))
514 invarg("invalid local address\n", *argv);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800515 } else if ((strcmp(*argv, "tunnel_id") == 0) ||
516 (strcmp(*argv, "tid") == 0)) {
517 __u32 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700518
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800519 NEXT_ARG();
520 if (get_u32(&uval, *argv, 0))
521 invarg("invalid ID\n", *argv);
522 p->tunnel_id = uval;
523 } else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
524 (strcmp(*argv, "ptid") == 0)) {
525 __u32 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700526
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800527 NEXT_ARG();
528 if (get_u32(&uval, *argv, 0))
529 invarg("invalid ID\n", *argv);
530 p->peer_tunnel_id = uval;
531 } else if ((strcmp(*argv, "session_id") == 0) ||
532 (strcmp(*argv, "sid") == 0)) {
533 __u32 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700534
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800535 NEXT_ARG();
536 if (get_u32(&uval, *argv, 0))
537 invarg("invalid ID\n", *argv);
538 p->session_id = uval;
539 } else if ((strcmp(*argv, "peer_session_id") == 0) ||
540 (strcmp(*argv, "psid") == 0)) {
541 __u32 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700542
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800543 NEXT_ARG();
544 if (get_u32(&uval, *argv, 0))
545 invarg("invalid ID\n", *argv);
546 p->peer_session_id = uval;
547 } else if (strcmp(*argv, "udp_sport") == 0) {
548 __u16 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700549
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800550 NEXT_ARG();
551 if (get_u16(&uval, *argv, 0))
552 invarg("invalid port\n", *argv);
553 p->local_udp_port = uval;
554 } else if (strcmp(*argv, "udp_dport") == 0) {
555 __u16 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700556
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800557 NEXT_ARG();
558 if (get_u16(&uval, *argv, 0))
559 invarg("invalid port\n", *argv);
560 p->peer_udp_port = uval;
561 } else if (strcmp(*argv, "offset") == 0) {
562 __u8 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700563
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800564 NEXT_ARG();
565 if (get_u8(&uval, *argv, 0))
566 invarg("invalid offset\n", *argv);
567 p->offset = uval;
568 } else if (strcmp(*argv, "peer_offset") == 0) {
569 __u8 uval;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700570
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800571 NEXT_ARG();
572 if (get_u8(&uval, *argv, 0))
573 invarg("invalid offset\n", *argv);
574 p->peer_offset = uval;
575 } else if (strcmp(*argv, "cookie") == 0) {
576 int slen;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700577
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800578 NEXT_ARG();
579 slen = strlen(*argv);
580 if ((slen != 8) && (slen != 16))
581 invarg("cookie must be either 8 or 16 hex digits\n", *argv);
582
583 p->cookie_len = slen / 2;
584 if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
585 invarg("cookie must be a hex string\n", *argv);
586 } else if (strcmp(*argv, "peer_cookie") == 0) {
587 int slen;
Stephen Hemminger56f5daa2016-03-21 11:52:19 -0700588
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800589 NEXT_ARG();
590 slen = strlen(*argv);
591 if ((slen != 8) && (slen != 16))
592 invarg("cookie must be either 8 or 16 hex digits\n", *argv);
593
594 p->peer_cookie_len = slen / 2;
595 if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
596 invarg("cookie must be a hex string\n", *argv);
James Chapmandd10baa2013-03-26 06:49:22 +0000597 } else if (strcmp(*argv, "l2spec_type") == 0) {
598 NEXT_ARG();
599 if (strcasecmp(*argv, "default") == 0) {
600 p->l2spec_type = L2TP_L2SPECTYPE_DEFAULT;
601 p->l2spec_len = 4;
602 } else if (strcasecmp(*argv, "none") == 0) {
603 p->l2spec_type = L2TP_L2SPECTYPE_NONE;
604 p->l2spec_len = 0;
605 } else {
606 fprintf(stderr, "Unknown layer2specific header type \"%s\"\n", *argv);
607 exit(-1);
608 }
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800609 } else if (strcmp(*argv, "tunnel") == 0) {
610 p->tunnel = 1;
611 } else if (strcmp(*argv, "session") == 0) {
612 p->session = 1;
613 } else if (matches(*argv, "help") == 0) {
614 usage();
615 } else {
616 fprintf(stderr, "Unknown command: %s\n", *argv);
617 usage();
618 }
619
620 argc--; argv++;
621 }
622
623 return 0;
624}
625
626
627static int do_add(int argc, char **argv)
628{
629 struct l2tp_parm p;
630 int ret = 0;
631
632 if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
633 return -1;
634
635 if (!p.tunnel && !p.session)
636 missarg("tunnel or session");
637
638 if (p.tunnel_id == 0)
639 missarg("tunnel_id");
640
641 /* session_id and peer_session_id must be provided for sessions */
642 if ((p.session) && (p.peer_session_id == 0))
643 missarg("peer_session_id");
644 if ((p.session) && (p.session_id == 0))
645 missarg("session_id");
646
647 /* peer_tunnel_id is needed for tunnels */
648 if ((p.tunnel) && (p.peer_tunnel_id == 0))
649 missarg("peer_tunnel_id");
650
651 if (p.tunnel) {
Chris Elston6618e332012-05-01 04:25:22 +0000652 if (p.local_ip.family == AF_UNSPEC)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800653 missarg("local");
654
Chris Elston6618e332012-05-01 04:25:22 +0000655 if (p.peer_ip.family == AF_UNSPEC)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800656 missarg("remote");
657
658 if (p.encap == L2TP_ENCAPTYPE_UDP) {
659 if (p.local_udp_port == 0)
660 missarg("udp_sport");
661 if (p.peer_udp_port == 0)
662 missarg("udp_dport");
663 }
664
665 ret = create_tunnel(&p);
666 }
667
668 if (p.session) {
669 /* Only ethernet pseudowires supported */
670 p.pw_type = L2TP_PWTYPE_ETH;
671
672 ret = create_session(&p);
673 }
674
675 return ret;
676}
677
678static int do_del(int argc, char **argv)
679{
680 struct l2tp_parm p;
681
682 if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
683 return -1;
684
685 if (!p.tunnel && !p.session)
686 missarg("tunnel or session");
687
688 if ((p.tunnel) && (p.tunnel_id == 0))
689 missarg("tunnel_id");
690 if ((p.session) && (p.session_id == 0))
691 missarg("session_id");
692
693 if (p.session_id)
694 return delete_session(&p);
695 else
696 return delete_tunnel(&p);
697
698 return -1;
699}
700
701static int do_show(int argc, char **argv)
702{
703 struct l2tp_data data;
704 struct l2tp_parm *p = &data.config;
705
706 if (parse_args(argc, argv, L2TP_GET, p) < 0)
707 return -1;
708
709 if (!p->tunnel && !p->session)
710 missarg("tunnel or session");
711
712 if (p->session)
713 get_session(&data);
714 else
715 get_tunnel(&data);
716
717 return 0;
718}
719
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800720int do_ipl2tp(int argc, char **argv)
721{
Phil Suttere8977762016-02-24 09:12:47 +0100722 if (argc < 1 || !matches(*argv, "help"))
723 usage();
724
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800725 if (genl_family < 0) {
726 if (rtnl_open_byproto(&genl_rth, 0, NETLINK_GENERIC) < 0) {
727 fprintf(stderr, "Cannot open generic netlink socket\n");
728 exit(1);
729 }
730
Julian Anastasov4ef9ff22012-09-11 12:04:34 +0300731 genl_family = genl_resolve_family(&genl_rth, L2TP_GENL_NAME);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800732 if (genl_family < 0)
733 exit(1);
734 }
735
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800736 if (matches(*argv, "add") == 0)
737 return do_add(argc-1, argv+1);
Andreas Henriksson6e304612012-05-19 16:08:21 +0200738 if (matches(*argv, "delete") == 0)
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800739 return do_del(argc-1, argv+1);
740 if (matches(*argv, "show") == 0 ||
741 matches(*argv, "lst") == 0 ||
742 matches(*argv, "list") == 0)
743 return do_show(argc-1, argv+1);
Stephen Hemminger38cd3112011-12-23 20:52:10 -0800744
745 fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
746 exit(-1);
747}