6 |
* |
* |
7 |
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
8 |
* |
* |
|
* |
|
9 |
* Changes: |
* Changes: |
10 |
* |
* |
11 |
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses |
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses |
13 |
* Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag |
* Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag |
14 |
*/ |
*/ |
15 |
|
|
|
#include "libbb.h" |
|
|
#include <sys/socket.h> |
|
|
#include <sys/ioctl.h> |
|
|
|
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
|
|
16 |
#include <netinet/ip.h> |
#include <netinet/ip.h> |
|
|
|
17 |
#include <net/if.h> |
#include <net/if.h> |
18 |
#include <net/if_arp.h> |
#include <net/if_arp.h> |
|
|
|
19 |
#include <asm/types.h> |
#include <asm/types.h> |
20 |
|
|
21 |
#ifndef __constant_htons |
#ifndef __constant_htons |
22 |
#define __constant_htons htons |
#define __constant_htons htons |
23 |
#endif |
#endif |
|
#include <linux/if_tunnel.h> |
|
24 |
|
|
25 |
|
// FYI: #define SIOCDEVPRIVATE 0x89F0 |
26 |
|
|
27 |
|
/* From linux/if_tunnel.h. #including it proved troublesome |
28 |
|
* (redefiniton errors due to name collisions in linux/ and net[inet]/) */ |
29 |
|
#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) |
30 |
|
#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1) |
31 |
|
#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2) |
32 |
|
#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3) |
33 |
|
//#define SIOCGETPRL (SIOCDEVPRIVATE + 4) |
34 |
|
//#define SIOCADDPRL (SIOCDEVPRIVATE + 5) |
35 |
|
//#define SIOCDELPRL (SIOCDEVPRIVATE + 6) |
36 |
|
//#define SIOCCHGPRL (SIOCDEVPRIVATE + 7) |
37 |
|
#define GRE_CSUM __constant_htons(0x8000) |
38 |
|
//#define GRE_ROUTING __constant_htons(0x4000) |
39 |
|
#define GRE_KEY __constant_htons(0x2000) |
40 |
|
#define GRE_SEQ __constant_htons(0x1000) |
41 |
|
//#define GRE_STRICT __constant_htons(0x0800) |
42 |
|
//#define GRE_REC __constant_htons(0x0700) |
43 |
|
//#define GRE_FLAGS __constant_htons(0x00F8) |
44 |
|
//#define GRE_VERSION __constant_htons(0x0007) |
45 |
|
struct ip_tunnel_parm { |
46 |
|
char name[IFNAMSIZ]; |
47 |
|
int link; |
48 |
|
uint16_t i_flags; |
49 |
|
uint16_t o_flags; |
50 |
|
uint32_t i_key; |
51 |
|
uint32_t o_key; |
52 |
|
struct iphdr iph; |
53 |
|
}; |
54 |
|
/* SIT-mode i_flags */ |
55 |
|
//#define SIT_ISATAP 0x0001 |
56 |
|
//struct ip_tunnel_prl { |
57 |
|
// uint32_t addr; |
58 |
|
// uint16_t flags; |
59 |
|
// uint16_t __reserved; |
60 |
|
// uint32_t datalen; |
61 |
|
// uint32_t __reserved2; |
62 |
|
// /* data follows */ |
63 |
|
//}; |
64 |
|
///* PRL flags */ |
65 |
|
//#define PRL_DEFAULT 0x0001 |
66 |
|
|
67 |
|
#include "ip_common.h" /* #include "libbb.h" is inside */ |
68 |
#include "rt_names.h" |
#include "rt_names.h" |
69 |
#include "utils.h" |
#include "utils.h" |
|
#include "ip_common.h" |
|
70 |
|
|
71 |
|
|
72 |
|
/* Dies on error */ |
73 |
static int do_ioctl_get_ifindex(char *dev) |
static int do_ioctl_get_ifindex(char *dev) |
74 |
{ |
{ |
75 |
struct ifreq ifr; |
struct ifreq ifr; |
77 |
|
|
78 |
strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); |
79 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
80 |
if (ioctl(fd, SIOCGIFINDEX, &ifr)) { |
xioctl(fd, SIOCGIFINDEX, &ifr); |
|
bb_perror_msg("ioctl"); |
|
|
return 0; |
|
|
} |
|
81 |
close(fd); |
close(fd); |
82 |
return ifr.ifr_ifindex; |
return ifr.ifr_ifindex; |
83 |
} |
} |
86 |
{ |
{ |
87 |
struct ifreq ifr; |
struct ifreq ifr; |
88 |
int fd; |
int fd; |
89 |
|
int err; |
90 |
|
|
91 |
strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); |
92 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
93 |
if (ioctl(fd, SIOCGIFHWADDR, &ifr)) { |
err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr); |
|
bb_perror_msg("ioctl"); |
|
|
return -1; |
|
|
} |
|
94 |
close(fd); |
close(fd); |
95 |
return ifr.ifr_addr.sa_family; |
return err ? -1 : ifr.ifr_addr.sa_family; |
96 |
} |
} |
97 |
|
|
|
|
|
98 |
static char *do_ioctl_get_ifname(int idx) |
static char *do_ioctl_get_ifname(int idx) |
99 |
{ |
{ |
100 |
static struct ifreq ifr; |
struct ifreq ifr; |
101 |
int fd; |
int fd; |
102 |
|
int err; |
103 |
|
|
104 |
ifr.ifr_ifindex = idx; |
ifr.ifr_ifindex = idx; |
105 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
106 |
if (ioctl(fd, SIOCGIFNAME, &ifr)) { |
err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr); |
|
bb_perror_msg("ioctl"); |
|
|
return NULL; |
|
|
} |
|
107 |
close(fd); |
close(fd); |
108 |
return ifr.ifr_name; |
return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name)); |
109 |
} |
} |
110 |
|
|
111 |
|
static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p) |
|
|
|
|
static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p) |
|
112 |
{ |
{ |
113 |
struct ifreq ifr; |
struct ifreq ifr; |
114 |
int fd; |
int fd; |
117 |
strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); |
118 |
ifr.ifr_ifru.ifru_data = (void*)p; |
ifr.ifr_ifru.ifru_data = (void*)p; |
119 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
120 |
err = ioctl(fd, SIOCGETTUNNEL, &ifr); |
err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr); |
|
if (err) { |
|
|
bb_perror_msg("ioctl"); |
|
|
} |
|
121 |
close(fd); |
close(fd); |
122 |
return err; |
return err; |
123 |
} |
} |
124 |
|
|
125 |
static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p) |
/* Dies on error, otherwise returns 0 */ |
126 |
|
static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p) |
127 |
{ |
{ |
128 |
struct ifreq ifr; |
struct ifreq ifr; |
129 |
int fd; |
int fd; |
|
int err; |
|
130 |
|
|
131 |
if (cmd == SIOCCHGTUNNEL && p->name[0]) { |
if (cmd == SIOCCHGTUNNEL && p->name[0]) { |
132 |
strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); |
135 |
} |
} |
136 |
ifr.ifr_ifru.ifru_data = (void*)p; |
ifr.ifr_ifru.ifru_data = (void*)p; |
137 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
138 |
err = ioctl(fd, cmd, &ifr); |
#if ENABLE_IOCTL_HEX2STR_ERROR |
139 |
if (err) { |
/* #define magic will turn ioctl# into string */ |
140 |
bb_perror_msg("ioctl"); |
if (cmd == SIOCCHGTUNNEL) |
141 |
} |
xioctl(fd, SIOCCHGTUNNEL, &ifr); |
142 |
|
else |
143 |
|
xioctl(fd, SIOCADDTUNNEL, &ifr); |
144 |
|
#else |
145 |
|
xioctl(fd, cmd, &ifr); |
146 |
|
#endif |
147 |
close(fd); |
close(fd); |
148 |
return err; |
return 0; |
149 |
} |
} |
150 |
|
|
151 |
static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p) |
/* Dies on error, otherwise returns 0 */ |
152 |
|
static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p) |
153 |
{ |
{ |
154 |
struct ifreq ifr; |
struct ifreq ifr; |
155 |
int fd; |
int fd; |
|
int err; |
|
156 |
|
|
157 |
if (p->name[0]) { |
if (p->name[0]) { |
158 |
strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); |
strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); |
161 |
} |
} |
162 |
ifr.ifr_ifru.ifru_data = (void*)p; |
ifr.ifr_ifru.ifru_data = (void*)p; |
163 |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
fd = xsocket(AF_INET, SOCK_DGRAM, 0); |
164 |
err = ioctl(fd, SIOCDELTUNNEL, &ifr); |
xioctl(fd, SIOCDELTUNNEL, &ifr); |
|
if (err) { |
|
|
bb_perror_msg("ioctl"); |
|
|
} |
|
165 |
close(fd); |
close(fd); |
166 |
return err; |
return 0; |
167 |
} |
} |
168 |
|
|
169 |
static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) |
/* Dies on error */ |
170 |
|
static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p) |
171 |
{ |
{ |
172 |
|
static const char keywords[] ALIGN1 = |
173 |
|
"mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0" |
174 |
|
"key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0" |
175 |
|
"csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0" |
176 |
|
"remote\0""any\0""local\0""dev\0" |
177 |
|
"ttl\0""inherit\0""tos\0""dsfield\0" |
178 |
|
"name\0"; |
179 |
|
enum { |
180 |
|
ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip, |
181 |
|
ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq, |
182 |
|
ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc, |
183 |
|
ARG_remote, ARG_any, ARG_local, ARG_dev, |
184 |
|
ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield, |
185 |
|
ARG_name |
186 |
|
}; |
187 |
int count = 0; |
int count = 0; |
188 |
char medium[IFNAMSIZ]; |
char medium[IFNAMSIZ]; |
189 |
|
int key; |
190 |
|
|
191 |
memset(p, 0, sizeof(*p)); |
memset(p, 0, sizeof(*p)); |
192 |
memset(&medium, 0, sizeof(medium)); |
memset(&medium, 0, sizeof(medium)); |
193 |
|
|
194 |
p->iph.version = 4; |
p->iph.version = 4; |
195 |
p->iph.ihl = 5; |
p->iph.ihl = 5; |
196 |
#ifndef IP_DF |
#ifndef IP_DF |
197 |
#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ |
#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ |
198 |
#endif |
#endif |
199 |
p->iph.frag_off = htons(IP_DF); |
p->iph.frag_off = htons(IP_DF); |
200 |
|
|
201 |
while (argc > 0) { |
while (*argv) { |
202 |
if (strcmp(*argv, "mode") == 0) { |
key = index_in_strings(keywords, *argv); |
203 |
|
if (key == ARG_mode) { |
204 |
NEXT_ARG(); |
NEXT_ARG(); |
205 |
if (strcmp(*argv, "ipip") == 0 || |
key = index_in_strings(keywords, *argv); |
206 |
strcmp(*argv, "ip/ip") == 0) { |
if (key == ARG_ipip || |
207 |
|
key == ARG_ip_ip) { |
208 |
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { |
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { |
209 |
bb_error_msg("you managed to ask for more than one tunnel mode"); |
bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one"); |
|
exit(-1); |
|
210 |
} |
} |
211 |
p->iph.protocol = IPPROTO_IPIP; |
p->iph.protocol = IPPROTO_IPIP; |
212 |
} else if (strcmp(*argv, "gre") == 0 || |
} else if (key == ARG_gre || |
213 |
strcmp(*argv, "gre/ip") == 0) { |
key == ARG_gre_ip) { |
214 |
if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { |
if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { |
215 |
bb_error_msg("you managed to ask for more than one tunnel mode"); |
bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one"); |
|
exit(-1); |
|
216 |
} |
} |
217 |
p->iph.protocol = IPPROTO_GRE; |
p->iph.protocol = IPPROTO_GRE; |
218 |
} else if (strcmp(*argv, "sit") == 0 || |
} else if (key == ARG_sit || |
219 |
strcmp(*argv, "ipv6/ip") == 0) { |
key == ARG_ip6_ip) { |
220 |
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { |
if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { |
221 |
bb_error_msg("you managed to ask for more than one tunnel mode"); |
bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one"); |
|
exit(-1); |
|
222 |
} |
} |
223 |
p->iph.protocol = IPPROTO_IPV6; |
p->iph.protocol = IPPROTO_IPV6; |
224 |
} else { |
} else { |
225 |
bb_error_msg("cannot guess tunnel mode"); |
bb_error_msg_and_die("%s tunnel mode", "cannot guess"); |
|
exit(-1); |
|
226 |
} |
} |
227 |
} else if (strcmp(*argv, "key") == 0) { |
} else if (key == ARG_key) { |
228 |
unsigned uval; |
unsigned uval; |
229 |
NEXT_ARG(); |
NEXT_ARG(); |
230 |
p->i_flags |= GRE_KEY; |
p->i_flags |= GRE_KEY; |
232 |
if (strchr(*argv, '.')) |
if (strchr(*argv, '.')) |
233 |
p->i_key = p->o_key = get_addr32(*argv); |
p->i_key = p->o_key = get_addr32(*argv); |
234 |
else { |
else { |
235 |
if (get_unsigned(&uval, *argv, 0)<0) { |
if (get_unsigned(&uval, *argv, 0) < 0) { |
236 |
bb_error_msg("invalid value of \"key\""); |
invarg(*argv, "key"); |
|
exit(-1); |
|
237 |
} |
} |
238 |
p->i_key = p->o_key = htonl(uval); |
p->i_key = p->o_key = htonl(uval); |
239 |
} |
} |
240 |
} else if (strcmp(*argv, "ikey") == 0) { |
} else if (key == ARG_ikey) { |
241 |
unsigned uval; |
unsigned uval; |
242 |
NEXT_ARG(); |
NEXT_ARG(); |
243 |
p->i_flags |= GRE_KEY; |
p->i_flags |= GRE_KEY; |
244 |
if (strchr(*argv, '.')) |
if (strchr(*argv, '.')) |
245 |
p->o_key = get_addr32(*argv); |
p->o_key = get_addr32(*argv); |
246 |
else { |
else { |
247 |
if (get_unsigned(&uval, *argv, 0)<0) { |
if (get_unsigned(&uval, *argv, 0) < 0) { |
248 |
bb_error_msg("invalid value of \"ikey\""); |
invarg(*argv, "ikey"); |
|
exit(-1); |
|
249 |
} |
} |
250 |
p->i_key = htonl(uval); |
p->i_key = htonl(uval); |
251 |
} |
} |
252 |
} else if (strcmp(*argv, "okey") == 0) { |
} else if (key == ARG_okey) { |
253 |
unsigned uval; |
unsigned uval; |
254 |
NEXT_ARG(); |
NEXT_ARG(); |
255 |
p->o_flags |= GRE_KEY; |
p->o_flags |= GRE_KEY; |
256 |
if (strchr(*argv, '.')) |
if (strchr(*argv, '.')) |
257 |
p->o_key = get_addr32(*argv); |
p->o_key = get_addr32(*argv); |
258 |
else { |
else { |
259 |
if (get_unsigned(&uval, *argv, 0)<0) { |
if (get_unsigned(&uval, *argv, 0) < 0) { |
260 |
bb_error_msg("invalid value of \"okey\""); |
invarg(*argv, "okey"); |
|
exit(-1); |
|
261 |
} |
} |
262 |
p->o_key = htonl(uval); |
p->o_key = htonl(uval); |
263 |
} |
} |
264 |
} else if (strcmp(*argv, "seq") == 0) { |
} else if (key == ARG_seq) { |
265 |
p->i_flags |= GRE_SEQ; |
p->i_flags |= GRE_SEQ; |
266 |
p->o_flags |= GRE_SEQ; |
p->o_flags |= GRE_SEQ; |
267 |
} else if (strcmp(*argv, "iseq") == 0) { |
} else if (key == ARG_iseq) { |
268 |
p->i_flags |= GRE_SEQ; |
p->i_flags |= GRE_SEQ; |
269 |
} else if (strcmp(*argv, "oseq") == 0) { |
} else if (key == ARG_oseq) { |
270 |
p->o_flags |= GRE_SEQ; |
p->o_flags |= GRE_SEQ; |
271 |
} else if (strcmp(*argv, "csum") == 0) { |
} else if (key == ARG_csum) { |
272 |
p->i_flags |= GRE_CSUM; |
p->i_flags |= GRE_CSUM; |
273 |
p->o_flags |= GRE_CSUM; |
p->o_flags |= GRE_CSUM; |
274 |
} else if (strcmp(*argv, "icsum") == 0) { |
} else if (key == ARG_icsum) { |
275 |
p->i_flags |= GRE_CSUM; |
p->i_flags |= GRE_CSUM; |
276 |
} else if (strcmp(*argv, "ocsum") == 0) { |
} else if (key == ARG_ocsum) { |
277 |
p->o_flags |= GRE_CSUM; |
p->o_flags |= GRE_CSUM; |
278 |
} else if (strcmp(*argv, "nopmtudisc") == 0) { |
} else if (key == ARG_nopmtudisc) { |
279 |
p->iph.frag_off = 0; |
p->iph.frag_off = 0; |
280 |
} else if (strcmp(*argv, "pmtudisc") == 0) { |
} else if (key == ARG_pmtudisc) { |
281 |
p->iph.frag_off = htons(IP_DF); |
p->iph.frag_off = htons(IP_DF); |
282 |
} else if (strcmp(*argv, "remote") == 0) { |
} else if (key == ARG_remote) { |
283 |
NEXT_ARG(); |
NEXT_ARG(); |
284 |
if (strcmp(*argv, "any")) |
key = index_in_strings(keywords, *argv); |
285 |
|
if (key != ARG_any) |
286 |
p->iph.daddr = get_addr32(*argv); |
p->iph.daddr = get_addr32(*argv); |
287 |
} else if (strcmp(*argv, "local") == 0) { |
} else if (key == ARG_local) { |
288 |
NEXT_ARG(); |
NEXT_ARG(); |
289 |
if (strcmp(*argv, "any")) |
key = index_in_strings(keywords, *argv); |
290 |
|
if (key != ARG_any) |
291 |
p->iph.saddr = get_addr32(*argv); |
p->iph.saddr = get_addr32(*argv); |
292 |
} else if (strcmp(*argv, "dev") == 0) { |
} else if (key == ARG_dev) { |
293 |
NEXT_ARG(); |
NEXT_ARG(); |
294 |
strncpy(medium, *argv, IFNAMSIZ-1); |
strncpy(medium, *argv, IFNAMSIZ-1); |
295 |
} else if (strcmp(*argv, "ttl") == 0) { |
} else if (key == ARG_ttl) { |
296 |
unsigned uval; |
unsigned uval; |
297 |
NEXT_ARG(); |
NEXT_ARG(); |
298 |
if (strcmp(*argv, "inherit") != 0) { |
key = index_in_strings(keywords, *argv); |
299 |
|
if (key != ARG_inherit) { |
300 |
if (get_unsigned(&uval, *argv, 0)) |
if (get_unsigned(&uval, *argv, 0)) |
301 |
invarg(*argv, "TTL"); |
invarg(*argv, "TTL"); |
302 |
if (uval > 255) |
if (uval > 255) |
303 |
invarg(*argv, "TTL must be <=255"); |
invarg(*argv, "TTL must be <=255"); |
304 |
p->iph.ttl = uval; |
p->iph.ttl = uval; |
305 |
} |
} |
306 |
} else if (strcmp(*argv, "tos") == 0 || |
} else if (key == ARG_tos || |
307 |
matches(*argv, "dsfield") == 0) { |
key == ARG_dsfield) { |
308 |
uint32_t uval; |
uint32_t uval; |
309 |
NEXT_ARG(); |
NEXT_ARG(); |
310 |
if (strcmp(*argv, "inherit") != 0) { |
key = index_in_strings(keywords, *argv); |
311 |
|
if (key != ARG_inherit) { |
312 |
if (rtnl_dsfield_a2n(&uval, *argv)) |
if (rtnl_dsfield_a2n(&uval, *argv)) |
313 |
invarg(*argv, "TOS"); |
invarg(*argv, "TOS"); |
314 |
p->iph.tos = uval; |
p->iph.tos = uval; |
315 |
} else |
} else |
316 |
p->iph.tos = 1; |
p->iph.tos = 1; |
317 |
} else { |
} else { |
318 |
if (strcmp(*argv, "name") == 0) { |
if (key == ARG_name) { |
319 |
NEXT_ARG(); |
NEXT_ARG(); |
320 |
} |
} |
321 |
if (p->name[0]) |
if (p->name[0]) |
325 |
struct ip_tunnel_parm old_p; |
struct ip_tunnel_parm old_p; |
326 |
memset(&old_p, 0, sizeof(old_p)); |
memset(&old_p, 0, sizeof(old_p)); |
327 |
if (do_get_ioctl(*argv, &old_p)) |
if (do_get_ioctl(*argv, &old_p)) |
328 |
return -1; |
exit(EXIT_FAILURE); |
329 |
*p = old_p; |
*p = old_p; |
330 |
} |
} |
331 |
} |
} |
332 |
count++; |
count++; |
333 |
argc--; argv++; |
argv++; |
334 |
} |
} |
335 |
|
|
|
|
|
336 |
if (p->iph.protocol == 0) { |
if (p->iph.protocol == 0) { |
337 |
if (memcmp(p->name, "gre", 3) == 0) |
if (memcmp(p->name, "gre", 3) == 0) |
338 |
p->iph.protocol = IPPROTO_GRE; |
p->iph.protocol = IPPROTO_GRE; |
344 |
|
|
345 |
if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { |
if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { |
346 |
if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { |
if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { |
347 |
bb_error_msg("keys are not allowed with ipip and sit"); |
bb_error_msg_and_die("keys are not allowed with ipip and sit"); |
|
return -1; |
|
348 |
} |
} |
349 |
} |
} |
350 |
|
|
351 |
if (medium[0]) { |
if (medium[0]) { |
352 |
p->link = do_ioctl_get_ifindex(medium); |
p->link = do_ioctl_get_ifindex(medium); |
|
if (p->link == 0) |
|
|
return -1; |
|
353 |
} |
} |
354 |
|
|
355 |
if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { |
if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { |
361 |
p->o_flags |= GRE_KEY; |
p->o_flags |= GRE_KEY; |
362 |
} |
} |
363 |
if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { |
if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { |
364 |
bb_error_msg("broadcast tunnel requires a source address"); |
bb_error_msg_and_die("broadcast tunnel requires a source address"); |
|
return -1; |
|
365 |
} |
} |
|
return 0; |
|
366 |
} |
} |
367 |
|
|
368 |
|
|
369 |
static int do_add(int cmd, int argc, char **argv) |
/* Return value becomes exitcode. It's okay to not return at all */ |
370 |
|
static int do_add(int cmd, char **argv) |
371 |
{ |
{ |
372 |
struct ip_tunnel_parm p; |
struct ip_tunnel_parm p; |
373 |
|
|
374 |
if (parse_args(argc, argv, cmd, &p) < 0) |
parse_args(argv, cmd, &p); |
|
return -1; |
|
375 |
|
|
376 |
if (p.iph.ttl && p.iph.frag_off == 0) { |
if (p.iph.ttl && p.iph.frag_off == 0) { |
377 |
bb_error_msg("ttl != 0 and noptmudisc are incompatible"); |
bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible"); |
|
return -1; |
|
378 |
} |
} |
379 |
|
|
380 |
switch (p.iph.protocol) { |
switch (p.iph.protocol) { |
385 |
case IPPROTO_IPV6: |
case IPPROTO_IPV6: |
386 |
return do_add_ioctl(cmd, "sit0", &p); |
return do_add_ioctl(cmd, "sit0", &p); |
387 |
default: |
default: |
388 |
bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)"); |
bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)"); |
|
return -1; |
|
389 |
} |
} |
|
return -1; |
|
390 |
} |
} |
391 |
|
|
392 |
static int do_del(int argc, char **argv) |
/* Return value becomes exitcode. It's okay to not return at all */ |
393 |
|
static int do_del(char **argv) |
394 |
{ |
{ |
395 |
struct ip_tunnel_parm p; |
struct ip_tunnel_parm p; |
396 |
|
|
397 |
if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) |
parse_args(argv, SIOCDELTUNNEL, &p); |
|
return -1; |
|
398 |
|
|
399 |
switch (p.iph.protocol) { |
switch (p.iph.protocol) { |
400 |
case IPPROTO_IPIP: |
case IPPROTO_IPIP: |
406 |
default: |
default: |
407 |
return do_del_ioctl(p.name, &p); |
return do_del_ioctl(p.name, &p); |
408 |
} |
} |
|
return -1; |
|
409 |
} |
} |
410 |
|
|
411 |
static void print_tunnel(struct ip_tunnel_parm *p) |
static void print_tunnel(struct ip_tunnel_parm *p) |
428 |
p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); |
p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); |
429 |
if (p->link) { |
if (p->link) { |
430 |
char *n = do_ioctl_get_ifname(p->link); |
char *n = do_ioctl_get_ifname(p->link); |
431 |
if (n) |
if (n) { |
432 |
printf(" dev %s ", n); |
printf(" dev %s ", n); |
433 |
|
free(n); |
434 |
|
} |
435 |
} |
} |
436 |
if (p->iph.ttl) |
if (p->iph.ttl) |
437 |
printf(" ttl %d ", p->iph.ttl); |
printf(" ttl %d ", p->iph.ttl); |
459 |
} |
} |
460 |
|
|
461 |
if (p->i_flags & GRE_SEQ) |
if (p->i_flags & GRE_SEQ) |
462 |
printf("%s Drop packets out of sequence.\n", _SL_); |
printf("%c Drop packets out of sequence.\n", _SL_); |
463 |
if (p->i_flags & GRE_CSUM) |
if (p->i_flags & GRE_CSUM) |
464 |
printf("%s Checksum in received packet is required.", _SL_); |
printf("%c Checksum in received packet is required.", _SL_); |
465 |
if (p->o_flags & GRE_SEQ) |
if (p->o_flags & GRE_SEQ) |
466 |
printf("%s Sequence packets on output.", _SL_); |
printf("%c Sequence packets on output.", _SL_); |
467 |
if (p->o_flags & GRE_CSUM) |
if (p->o_flags & GRE_CSUM) |
468 |
printf("%s Checksum output packets.", _SL_); |
printf("%c Checksum output packets.", _SL_); |
469 |
} |
} |
470 |
|
|
471 |
static int do_tunnels_list(struct ip_tunnel_parm *p) |
static void do_tunnels_list(struct ip_tunnel_parm *p) |
472 |
{ |
{ |
473 |
char name[IFNAMSIZ]; |
char name[IFNAMSIZ]; |
474 |
unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, |
unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, |
478 |
int type; |
int type; |
479 |
struct ip_tunnel_parm p1; |
struct ip_tunnel_parm p1; |
480 |
char buf[512]; |
char buf[512]; |
481 |
FILE *fp = fopen("/proc/net/dev", "r"); |
FILE *fp = fopen_or_warn("/proc/net/dev", "r"); |
482 |
|
|
483 |
if (fp == NULL) { |
if (fp == NULL) { |
484 |
perror("fopen"); |
return; |
|
return -1; |
|
485 |
} |
} |
486 |
|
/* skip headers */ |
487 |
fgets(buf, sizeof(buf), fp); |
fgets(buf, sizeof(buf), fp); |
488 |
fgets(buf, sizeof(buf), fp); |
fgets(buf, sizeof(buf), fp); |
489 |
|
|
494 |
ptr = strchr(buf, ':'); |
ptr = strchr(buf, ':'); |
495 |
if (ptr == NULL || |
if (ptr == NULL || |
496 |
(*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { |
(*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { |
497 |
bb_error_msg("wrong format of /proc/net/dev. Sorry"); |
bb_error_msg("wrong format of /proc/net/dev"); |
498 |
return -1; |
return; |
499 |
} |
} |
500 |
if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", |
if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", |
501 |
&rx_bytes, &rx_packets, &rx_errs, &rx_drops, |
&rx_bytes, &rx_packets, &rx_errs, &rx_drops, |
507 |
continue; |
continue; |
508 |
type = do_ioctl_get_iftype(name); |
type = do_ioctl_get_iftype(name); |
509 |
if (type == -1) { |
if (type == -1) { |
510 |
bb_error_msg("failed to get type of [%s]", name); |
bb_error_msg("cannot get type of [%s]", name); |
511 |
continue; |
continue; |
512 |
} |
} |
513 |
if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) |
if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) |
522 |
(p->i_key && p1.i_key != p->i_key)) |
(p->i_key && p1.i_key != p->i_key)) |
523 |
continue; |
continue; |
524 |
print_tunnel(&p1); |
print_tunnel(&p1); |
525 |
puts(""); |
bb_putchar('\n'); |
526 |
} |
} |
|
return 0; |
|
527 |
} |
} |
528 |
|
|
529 |
static int do_show(int argc, char **argv) |
/* Return value becomes exitcode. It's okay to not return at all */ |
530 |
|
static int do_show(char **argv) |
531 |
{ |
{ |
532 |
int err; |
int err; |
533 |
struct ip_tunnel_parm p; |
struct ip_tunnel_parm p; |
534 |
|
|
535 |
if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) |
parse_args(argv, SIOCGETTUNNEL, &p); |
|
return -1; |
|
536 |
|
|
537 |
switch (p.iph.protocol) { |
switch (p.iph.protocol) { |
538 |
case IPPROTO_IPIP: |
case IPPROTO_IPIP: |
552 |
return -1; |
return -1; |
553 |
|
|
554 |
print_tunnel(&p); |
print_tunnel(&p); |
555 |
puts(""); |
bb_putchar('\n'); |
556 |
return 0; |
return 0; |
557 |
} |
} |
558 |
|
|
559 |
int do_iptunnel(int argc, char **argv) |
/* Return value becomes exitcode. It's okay to not return at all */ |
560 |
|
int do_iptunnel(char **argv) |
561 |
{ |
{ |
562 |
if (argc > 0) { |
static const char keywords[] ALIGN1 = |
563 |
if (matches(*argv, "add") == 0) |
"add\0""change\0""delete\0""show\0""list\0""lst\0"; |
564 |
return do_add(SIOCADDTUNNEL, argc-1, argv+1); |
enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst }; |
565 |
if (matches(*argv, "change") == 0) |
int key; |
566 |
return do_add(SIOCCHGTUNNEL, argc-1, argv+1); |
|
567 |
if (matches(*argv, "del") == 0) |
if (*argv) { |
568 |
return do_del(argc-1, argv+1); |
key = index_in_substrings(keywords, *argv); |
569 |
if (matches(*argv, "show") == 0 || |
if (key < 0) |
570 |
matches(*argv, "lst") == 0 || |
bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name); |
571 |
matches(*argv, "list") == 0) |
argv++; |
572 |
return do_show(argc-1, argv+1); |
if (key == ARG_add) |
573 |
} else |
return do_add(SIOCADDTUNNEL, argv); |
574 |
return do_show(0, NULL); |
if (key == ARG_change) |
575 |
|
return do_add(SIOCCHGTUNNEL, argv); |
576 |
bb_error_msg_and_die("command \"%s\" is unknown", *argv); |
if (key == ARG_del) |
577 |
|
return do_del(argv); |
578 |
|
} |
579 |
|
return do_show(argv); |
580 |
} |
} |