]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/tunnel.c
Use libbsd for strlcpy if available
[mirror_iproute2.git] / ip / tunnel.c
1 /*
2 * Copyright (C)2006 USAGI/WIDE Project
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses>.
16 */
17 /*
18 * split from ip_tunnel.c
19 */
20 /*
21 * Author:
22 * Masahide NAKAMURA @USAGI
23 */
24
25 #include <stdio.h>
26 #include <string.h>
27 #ifdef HAVE_LIBBSD
28 #include <bsd/string.h>
29 #endif
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <netinet/in.h>
36 #include <linux/if.h>
37 #include <linux/ip.h>
38 #include <linux/if_tunnel.h>
39 #include <linux/if_arp.h>
40
41 #include "utils.h"
42 #include "tunnel.h"
43 #include "json_print.h"
44
45 const char *tnl_strproto(__u8 proto)
46 {
47 switch (proto) {
48 case IPPROTO_IPIP:
49 return "ip";
50 case IPPROTO_GRE:
51 return "gre";
52 case IPPROTO_IPV6:
53 return "ipv6";
54 case IPPROTO_ESP:
55 return "esp";
56 case IPPROTO_MPLS:
57 return "mpls";
58 case 0:
59 return "any";
60 default:
61 return "unknown";
62 }
63 }
64
65 int tnl_get_ioctl(const char *basedev, void *p)
66 {
67 struct ifreq ifr;
68 int fd;
69 int err;
70
71 strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
72 ifr.ifr_ifru.ifru_data = (void *)p;
73
74 fd = socket(preferred_family, SOCK_DGRAM, 0);
75 if (fd < 0) {
76 fprintf(stderr, "create socket failed: %s\n", strerror(errno));
77 return -1;
78 }
79
80 err = ioctl(fd, SIOCGETTUNNEL, &ifr);
81 if (err)
82 fprintf(stderr, "get tunnel \"%s\" failed: %s\n", basedev,
83 strerror(errno));
84
85 close(fd);
86 return err;
87 }
88
89 int tnl_add_ioctl(int cmd, const char *basedev, const char *name, void *p)
90 {
91 struct ifreq ifr;
92 int fd;
93 int err;
94
95 if (cmd == SIOCCHGTUNNEL && name[0])
96 strlcpy(ifr.ifr_name, name, IFNAMSIZ);
97 else
98 strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
99 ifr.ifr_ifru.ifru_data = p;
100
101 fd = socket(preferred_family, SOCK_DGRAM, 0);
102 if (fd < 0) {
103 fprintf(stderr, "create socket failed: %s\n", strerror(errno));
104 return -1;
105 }
106
107 err = ioctl(fd, cmd, &ifr);
108 if (err)
109 fprintf(stderr, "add tunnel \"%s\" failed: %s\n", ifr.ifr_name,
110 strerror(errno));
111 close(fd);
112 return err;
113 }
114
115 int tnl_del_ioctl(const char *basedev, const char *name, void *p)
116 {
117 struct ifreq ifr;
118 int fd;
119 int err;
120
121 if (name[0])
122 strlcpy(ifr.ifr_name, name, IFNAMSIZ);
123 else
124 strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
125
126 ifr.ifr_ifru.ifru_data = p;
127
128 fd = socket(preferred_family, SOCK_DGRAM, 0);
129 if (fd < 0) {
130 fprintf(stderr, "create socket failed: %s\n", strerror(errno));
131 return -1;
132 }
133
134 err = ioctl(fd, SIOCDELTUNNEL, &ifr);
135 if (err)
136 fprintf(stderr, "delete tunnel \"%s\" failed: %s\n",
137 ifr.ifr_name, strerror(errno));
138 close(fd);
139 return err;
140 }
141
142 static int tnl_gen_ioctl(int cmd, const char *name,
143 void *p, int skiperr)
144 {
145 struct ifreq ifr;
146 int fd;
147 int err;
148
149 strlcpy(ifr.ifr_name, name, IFNAMSIZ);
150 ifr.ifr_ifru.ifru_data = p;
151
152 fd = socket(preferred_family, SOCK_DGRAM, 0);
153 if (fd < 0) {
154 fprintf(stderr, "create socket failed: %s\n", strerror(errno));
155 return -1;
156 }
157
158 err = ioctl(fd, cmd, &ifr);
159 if (err && errno != skiperr)
160 fprintf(stderr, "%s: ioctl %x failed: %s\n", name,
161 cmd, strerror(errno));
162 close(fd);
163 return err;
164 }
165
166 int tnl_prl_ioctl(int cmd, const char *name, void *p)
167 {
168 return tnl_gen_ioctl(cmd, name, p, -1);
169 }
170
171 int tnl_6rd_ioctl(int cmd, const char *name, void *p)
172 {
173 return tnl_gen_ioctl(cmd, name, p, -1);
174 }
175
176 int tnl_ioctl_get_6rd(const char *name, void *p)
177 {
178 return tnl_gen_ioctl(SIOCGET6RD, name, p, EINVAL);
179 }
180
181 __be32 tnl_parse_key(const char *name, const char *key)
182 {
183 unsigned int uval;
184
185 if (strchr(key, '.'))
186 return get_addr32(key);
187
188 if (get_unsigned(&uval, key, 0) < 0) {
189 fprintf(stderr,
190 "invalid value for \"%s\": \"%s\"; it should be an unsigned integer\n",
191 name, key);
192 exit(-1);
193 }
194 return htonl(uval);
195 }
196
197 static const char *tnl_encap_str(const char *name, int enabled, int port)
198 {
199 static const char ne[][sizeof("no")] = {
200 [0] = "no",
201 [1] = "",
202 };
203 static char buf[32];
204 char b1[16];
205 const char *val;
206
207 if (!port) {
208 val = "auto ";
209 } else if (port < 0) {
210 val = "";
211 } else {
212 snprintf(b1, sizeof(b1), "%u ", port - 1);
213 val = b1;
214 }
215
216 snprintf(buf, sizeof(buf), "%sencap-%s %s", ne[!!enabled], name, val);
217 return buf;
218 }
219
220 void tnl_print_encap(struct rtattr *tb[],
221 int encap_type, int encap_flags,
222 int encap_sport, int encap_dport)
223 {
224 __u16 type, flags, sport, dport;
225
226 if (!tb[encap_type])
227 return;
228
229 type = rta_getattr_u16(tb[encap_type]);
230 if (type == TUNNEL_ENCAP_NONE)
231 return;
232
233 flags = rta_getattr_u16(tb[encap_flags]);
234 sport = rta_getattr_u16(tb[encap_sport]);
235 dport = rta_getattr_u16(tb[encap_dport]);
236
237 open_json_object("encap");
238 print_string(PRINT_FP, NULL, "encap ", NULL);
239
240 switch (type) {
241 case TUNNEL_ENCAP_FOU:
242 print_string(PRINT_ANY, "type", "%s ", "fou");
243 break;
244 case TUNNEL_ENCAP_GUE:
245 print_string(PRINT_ANY, "type", "%s ", "gue");
246 break;
247 default:
248 print_null(PRINT_ANY, "type", "%s ", "unknown");
249 break;
250 }
251
252 if (is_json_context()) {
253 print_uint(PRINT_JSON, "sport", NULL, ntohs(sport));
254 print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
255 print_bool(PRINT_JSON, "csum", NULL,
256 flags & TUNNEL_ENCAP_FLAG_CSUM);
257 print_bool(PRINT_JSON, "csum6", NULL,
258 flags & TUNNEL_ENCAP_FLAG_CSUM6);
259 print_bool(PRINT_JSON, "remcsum", NULL,
260 flags & TUNNEL_ENCAP_FLAG_REMCSUM);
261 close_json_object();
262 } else {
263 int t;
264
265 t = sport ? ntohs(sport) + 1 : 0;
266 print_string(PRINT_FP, NULL, "%s",
267 tnl_encap_str("sport", 1, t));
268
269 t = ntohs(dport) + 1;
270 print_string(PRINT_FP, NULL, "%s",
271 tnl_encap_str("dport", 1, t));
272
273 t = flags & TUNNEL_ENCAP_FLAG_CSUM;
274 print_string(PRINT_FP, NULL, "%s",
275 tnl_encap_str("csum", t, -1));
276
277 t = flags & TUNNEL_ENCAP_FLAG_CSUM6;
278 print_string(PRINT_FP, NULL, "%s",
279 tnl_encap_str("csum6", t, -1));
280
281 t = flags & TUNNEL_ENCAP_FLAG_REMCSUM;
282 print_string(PRINT_FP, NULL, "%s",
283 tnl_encap_str("remcsum", t, -1));
284 }
285 }
286
287 void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family)
288 {
289 const char *value;
290 inet_prefix dst;
291
292 if (!rta) {
293 value = "any";
294 } else if (get_addr_rta(&dst, rta, family)) {
295 value = "unknown";
296 } else if (dst.flags & ADDRTYPE_UNSPEC) {
297 value = "any";
298 } else {
299 value = format_host(family, dst.bytelen, dst.data);
300 if (!value)
301 value = "unknown";
302 }
303
304 if (is_json_context()) {
305 print_string(PRINT_JSON, name, NULL, value);
306 } else {
307 SPRINT_BUF(b1);
308
309 snprintf(b1, sizeof(b1), "%s %%s ", name);
310 print_string(PRINT_FP, NULL, b1, value);
311 }
312 }
313
314 static void tnl_print_stats(const struct rtnl_link_stats64 *s)
315 {
316 printf("%s", _SL_);
317 printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
318 printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-8lld%s",
319 s->rx_packets, s->rx_bytes, s->rx_errors, s->rx_frame_errors,
320 s->rx_fifo_errors, s->multicast, _SL_);
321 printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_);
322 printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-6lld",
323 s->tx_packets, s->tx_bytes, s->tx_errors, s->collisions,
324 s->tx_carrier_errors, s->tx_dropped);
325 }
326
327 static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg)
328 {
329 struct tnl_print_nlmsg_info *info = arg;
330 struct ifinfomsg *ifi = NLMSG_DATA(n);
331 struct rtattr *tb[IFLA_MAX+1];
332 const char *name, *n1;
333
334 if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
335 return 0;
336
337 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
338 return -1;
339
340 if (preferred_family == AF_INET) {
341 switch (ifi->ifi_type) {
342 case ARPHRD_TUNNEL:
343 case ARPHRD_IPGRE:
344 case ARPHRD_SIT:
345 break;
346 default:
347 return 0;
348 }
349 } else {
350 switch (ifi->ifi_type) {
351 case ARPHRD_TUNNEL6:
352 case ARPHRD_IP6GRE:
353 break;
354 default:
355 return 0;
356 }
357 }
358
359 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
360
361 if (!tb[IFLA_IFNAME])
362 return 0;
363
364 name = rta_getattr_str(tb[IFLA_IFNAME]);
365
366 /* Assume p1->name[IFNAMSIZ] is first field of structure */
367 n1 = info->p1;
368 if (n1[0] && strcmp(n1, name))
369 return 0;
370
371 info->ifi = ifi;
372 info->init(info);
373
374 /* TODO: parse netlink attributes */
375 if (tnl_get_ioctl(name, info->p2))
376 return 0;
377
378 if (!info->match(info))
379 return 0;
380
381 info->print(info->p2);
382 if (show_stats) {
383 struct rtnl_link_stats64 s;
384
385 if (get_rtnl_link_stats_rta(&s, tb) <= 0)
386 return -1;
387
388 tnl_print_stats(&s);
389 }
390 fputc('\n', stdout);
391
392 return 0;
393 }
394
395 int do_tunnels_list(struct tnl_print_nlmsg_info *info)
396 {
397 if (rtnl_linkdump_req(&rth, preferred_family) < 0) {
398 perror("Cannot send dump request\n");
399 return -1;
400 }
401
402 if (rtnl_dump_filter(&rth, print_nlmsg_tunnel, info) < 0) {
403 fprintf(stderr, "Dump terminated\n");
404 return -1;
405 }
406
407 return 0;
408 }