2
* Copyright (C)2006 USAGI/WIDE Project
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.
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.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
* $Id: s.ipv6tunnel.c 1.7 02/12/11 11:21:51+02:00 antti@traci.mipl.mediapoli.com $
25
* Masahide NAKAMURA @USAGI
32
#include <sys/types.h>
33
#include <sys/socket.h>
34
#include <arpa/inet.h>
35
#include <sys/ioctl.h>
38
#include <linux/if_arp.h>
39
#include <linux/if_tunnel.h>
40
#include <linux/ip6_tunnel.h>
45
#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000)
46
#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
48
#define DEFAULT_TNL_HOP_LIMIT (64)
50
static void usage(void) __attribute__((noreturn));
52
static void usage(void)
54
fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n");
55
fprintf(stderr, " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n");
56
fprintf(stderr, " [ encaplimit ELIM ]\n");
57
fprintf(stderr ," [ hoplimit HLIM ] [ tc TC ] [ fl FL ]\n");
58
fprintf(stderr, " [ dscp inherit ]\n");
59
fprintf(stderr, "\n");
60
fprintf(stderr, "Where: NAME := STRING\n");
61
fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
62
fprintf(stderr, " ELIM := { none | 0..255 }(default=%d)\n",
63
IPV6_DEFAULT_TNL_ENCAP_LIMIT);
64
fprintf(stderr, " HLIM := 0..255 (default=%d)\n",
65
DEFAULT_TNL_HOP_LIMIT);
66
fprintf(stderr, " TC := { 0x0..0xff | inherit }\n");
67
fprintf(stderr, " FL := { 0x0..0xfffff | inherit }\n");
71
static void print_tunnel(struct ip6_tnl_parm *p)
76
inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote));
77
inet_ntop(AF_INET6, &p->laddr, local, sizeof(local));
79
printf("%s: %s/ipv6 remote %s local %s",
80
p->name, tnl_strproto(p->proto), remote, local);
82
char *n = tnl_ioctl_get_ifname(p->link);
87
if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
88
printf(" encaplimit none");
90
printf(" encaplimit %u", p->encap_limit);
92
printf(" hoplimit %u", p->hop_limit);
94
if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS)
95
printf(" tc inherit");
97
__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS);
98
printf(" tc 0x%02x", (__u8)(val >> 20));
101
if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
102
printf(" fl inherit");
104
printf(" fl 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL));
106
printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo));
108
if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
109
printf(" dscp inherit");
112
static int parse_args(int argc, char **argv, struct ip6_tnl_parm *p)
114
char medium[IFNAMSIZ];
116
memset(medium, 0, sizeof(medium));
119
if (strcmp(*argv, "remote") == 0) {
122
get_prefix(&raddr, *argv, preferred_family);
123
if (raddr.family == AF_UNSPEC)
124
invarg("\"remote\" address family is AF_UNSPEC", *argv);
125
memcpy(&p->raddr, &raddr.data, sizeof(p->raddr));
126
} else if (strcmp(*argv, "local") == 0) {
129
get_prefix(&laddr, *argv, preferred_family);
130
if (laddr.family == AF_UNSPEC)
131
invarg("\"local\" address family is AF_UNSPEC", *argv);
132
memcpy(&p->laddr, &laddr.data, sizeof(p->laddr));
133
} else if (strcmp(*argv, "dev") == 0) {
135
strncpy(medium, *argv, IFNAMSIZ - 1);
136
} else if (strcmp(*argv, "encaplimit") == 0) {
138
if (strcmp(*argv, "none") == 0) {
139
p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
142
if (get_u8(&uval, *argv, 0) < -1)
143
invarg("invalid ELIM", *argv);
144
p->encap_limit = uval;
146
} else if (strcmp(*argv, "hoplimit") == 0) {
149
if (get_u8(&uval, *argv, 0))
150
invarg("invalid HLIM", *argv);
152
} else if (strcmp(*argv, "tc") == 0) {
155
if (strcmp(*argv, "inherit") == 0)
156
p->flags |= IP6_TNL_F_USE_ORIG_TCLASS;
158
if (get_u8(&uval, *argv, 16))
159
invarg("invalid TC", *argv);
160
p->flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
161
p->flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
163
} else if (strcmp(*argv, "fl") == 0) {
166
if (strcmp(*argv, "inherit") == 0)
167
p->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
169
if (get_u32(&uval, *argv, 16))
170
invarg("invalid FL", *argv);
172
invarg("invalid FL", *argv);
173
p->flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
174
p->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
176
} else if (strcmp(*argv, "dscp") == 0) {
178
if (strcmp(*argv, "inherit") != 0)
179
invarg("not inherit", *argv);
180
p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
182
if (strcmp(*argv, "name") == 0) {
185
if (matches(*argv, "help") == 0)
188
duparg2("name", *argv);
189
strncpy(p->name, *argv, IFNAMSIZ - 1);
194
p->link = tnl_ioctl_get_ifindex(medium);
201
static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
203
memset(p, 0, sizeof(*p));
204
p->proto = IPPROTO_IPV6;
206
p->hop_limit = DEFAULT_TNL_HOP_LIMIT;
207
p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
212
* @p1: user specified parameter
213
* @p2: database entry
215
static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
216
const struct ip6_tnl_parm *p2)
218
return ((!p1->link || p1->link == p2->link) &&
219
(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
220
(memcmp(&p1->laddr, &in6addr_any, sizeof(p1->laddr)) == 0 ||
221
memcmp(&p1->laddr, &p2->laddr, sizeof(p1->laddr)) == 0) &&
222
(memcmp(&p1->raddr, &in6addr_any, sizeof(p1->raddr)) == 0 ||
223
memcmp(&p1->raddr, &p2->raddr, sizeof(p1->raddr)) == 0) &&
224
(!p1->proto || !p2->proto || p1->proto == p2->proto) &&
225
(!p1->encap_limit || p1->encap_limit == p2->encap_limit) &&
226
(!p1->hop_limit || p1->hop_limit == p2->hop_limit) &&
227
(!(p1->flowinfo & IP6_FLOWINFO_TCLASS) ||
228
!((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_TCLASS)) &&
229
(!(p1->flowinfo & IP6_FLOWINFO_FLOWLABEL) ||
230
!((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_FLOWLABEL)) &&
231
(!p1->flags || (p1->flags & p2->flags)));
234
static int do_tunnels_list(struct ip6_tnl_parm *p)
238
FILE *fp = fopen("/proc/net/dev", "r");
244
/* skip two lines at the begenning of the file */
245
fgets(buf, sizeof(buf), fp);
246
fgets(buf, sizeof(buf), fp);
248
while (fgets(buf, sizeof(buf), fp) != NULL) {
251
unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
253
tx_bytes, tx_packets, tx_errs, tx_drops,
254
tx_fifo, tx_colls, tx_carrier, rx_multi;
255
struct ip6_tnl_parm p1;
258
buf[sizeof(buf) - 1] = '\0';
259
if ((ptr = strchr(buf, ':')) == NULL ||
260
(*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
261
fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
264
if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
265
&rx_bytes, &rx_packets, &rx_errs, &rx_drops,
266
&rx_fifo, &rx_frame, &rx_multi,
267
&tx_bytes, &tx_packets, &tx_errs, &tx_drops,
268
&tx_fifo, &tx_colls, &tx_carrier) != 14)
270
if (p->name[0] && strcmp(p->name, name))
272
type = tnl_ioctl_get_iftype(name);
274
fprintf(stderr, "Failed to get type of [%s]\n", name);
277
if (type != ARPHRD_TUNNEL6)
279
memset(&p1, 0, sizeof(p1));
280
ip6_tnl_parm_init(&p1, 0);
281
strcpy(p1.name, name);
282
p1.link = tnl_ioctl_get_ifindex(p1.name);
285
if (tnl_get_ioctl(p1.name, &p1))
287
if (!ip6_tnl_parm_match(p, &p1))
292
printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
293
printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
294
rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
295
printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_);
296
printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
297
tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
309
static int do_show(int argc, char **argv)
311
struct ip6_tnl_parm p;
313
ip6_tnl_parm_init(&p, 0);
315
if (parse_args(argc, argv, &p) < 0)
318
if (!p.name[0] || show_stats)
321
if (tnl_get_ioctl(p.name, &p))
330
static int do_add(int cmd, int argc, char **argv)
332
struct ip6_tnl_parm p;
334
ip6_tnl_parm_init(&p, 1);
336
if (parse_args(argc, argv, &p) < 0)
339
return tnl_add_ioctl(cmd,
340
cmd == SIOCCHGTUNNEL && p.name[0] ?
341
p.name : "ip6tnl0", p.name, &p);
344
static int do_del(int argc, char **argv)
346
struct ip6_tnl_parm p;
348
ip6_tnl_parm_init(&p, 1);
350
if (parse_args(argc, argv, &p) < 0)
353
return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p);
356
int do_ip6tunnel(int argc, char **argv)
358
switch (preferred_family) {
360
preferred_family = AF_INET6;
365
fprintf(stderr, "Unsupported family:%d\n", preferred_family);
370
if (matches(*argv, "add") == 0)
371
return do_add(SIOCADDTUNNEL, argc - 1, argv + 1);
372
if (matches(*argv, "change") == 0)
373
return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1);
374
if (matches(*argv, "del") == 0)
375
return do_del(argc - 1, argv + 1);
376
if (matches(*argv, "show") == 0 ||
377
matches(*argv, "lst") == 0 ||
378
matches(*argv, "list") == 0)
379
return do_show(argc - 1, argv + 1);
380
if (matches(*argv, "help") == 0)
383
return do_show(0, NULL);
385
fprintf(stderr, "Command \"%s\" is unknown, try \"ip -f inet6 tunnel help\".\n", *argv);