Skip to content

Commit cf5c130

Browse files
committed
add support for IPv6 as payload and delivery protocol
1 parent 23bb6ec commit cf5c130

File tree

2 files changed

+107
-32
lines changed

2 files changed

+107
-32
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ sudo ./gre <tun dev> remote local
3030

3131
Copyright (C) 2015 - 2017, Xiaoxiao <[email protected]>
3232

33+
Copyright (C) 2019, Mikael Magnusson <[email protected]>
34+
3335
This program is free software: you can redistribute it and/or modify
3436
it under the terms of the GNU General Public License as published by
3537
the Free Software Foundation, either version 3 of the License, or

gre.c

Lines changed: 105 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* gre.c - userspace GRE tunnel
33
*
44
* Copyright (C) 2015 - 2017, Xiaoxiao <[email protected]>
5+
* Copyright (C) 2019, Mikael Magnusson <[email protected]>
56
*
67
* This program is free software; you can redistribute it and/or modify
78
* it under the terms of the GNU General Public License as published by
@@ -21,7 +22,9 @@
2122
#include <errno.h>
2223
#include <fcntl.h>
2324
#include <linux/if_tun.h>
25+
#include <net/ethernet.h>
2426
#include <net/if.h>
27+
#include <netdb.h>
2528
#include <netinet/in.h>
2629
#include <pwd.h>
2730
#include <stdint.h>
@@ -37,16 +40,21 @@
3740

3841
static int tun;
3942
static int sock;
40-
static struct sockaddr_in remote;
43+
static struct sockaddr_storage remote;
44+
static size_t remote_len;
4145

4246
uint8_t buf[4096];
4347

4448
static void gre_cb(void);
49+
static void gre_ipv4(const uint8_t *buf, int n);
50+
static void gre_ipv6(const uint8_t *buf, int n, const struct sockaddr_in6 *src);
51+
static void gre_any(const uint8_t *buf, int n);
4552
static int tun_cb(void);
4653
static int tun_new(const char *dev);
4754
static int setnonblock(int fd);
4855
static int runas(const char *user);
4956
static int daemonize(void);
57+
static int inet_addr_storage(const char *cp, struct sockaddr_storage *sp, size_t *sp_len);
5058

5159
int main(int argc, char **argv)
5260
{
@@ -65,35 +73,28 @@ int main(int argc, char **argv)
6573
return EXIT_FAILURE;
6674
}
6775

68-
sock = socket(AF_INET, SOCK_RAW, IPPROTO_GRE);
69-
if (sock < 0)
76+
struct sockaddr_storage local;
77+
size_t local_len = 0;
78+
if (inet_addr_storage(argv[3], &local, &local_len))
7079
{
71-
perror("socket");
80+
fprintf(stderr, "bad local address\n");
7281
return EXIT_FAILURE;
7382
}
7483

75-
struct sockaddr_in local;
76-
local.sin_family = AF_INET;
77-
local.sin_port = htons(IPPROTO_GRE);
78-
local.sin_addr.s_addr = inet_addr(argv[3]);
79-
if (local.sin_addr.s_addr == INADDR_NONE)
84+
sock = socket(local.ss_family, SOCK_RAW, IPPROTO_GRE);
85+
if (sock < 0)
8086
{
81-
fprintf(stderr, "bad local address\n");
87+
perror("socket");
8288
return EXIT_FAILURE;
8389
}
84-
else
90+
91+
if (bind(sock, (struct sockaddr *)&local, local_len) != 0)
8592
{
86-
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) != 0)
87-
{
88-
perror("bind");
89-
return EXIT_FAILURE;
90-
}
93+
perror("bind");
94+
return EXIT_FAILURE;
9195
}
9296

93-
remote.sin_family = AF_INET;
94-
remote.sin_port = htons(IPPROTO_GRE);
95-
remote.sin_addr.s_addr = inet_addr(argv[2]);
96-
if (remote.sin_addr.s_addr == INADDR_NONE)
97+
if (inet_addr_storage(argv[2], &remote, &remote_len))
9798
{
9899
fprintf(stderr, "bad remote address\n");
99100
return EXIT_FAILURE;
@@ -142,46 +143,81 @@ int main(int argc, char **argv)
142143

143144
static void gre_cb(void)
144145
{
145-
int ihl; // IP header length
146146
int n;
147+
struct sockaddr_storage src;
148+
size_t src_len = sizeof(src);
147149

148-
n = recv(sock, buf, sizeof(buf), 0);
150+
memset(&src, 0, src_len);
151+
n = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&src, &src_len);
149152
if (n < 0)
150153
{
151154
perror("recv");
152155
return;
153156
}
157+
158+
switch (remote.ss_family) {
159+
case AF_INET: gre_ipv4(buf, n); break;
160+
case AF_INET6: gre_ipv6(buf, n, (const struct sockaddr_in6*)&src); break;
161+
}
162+
}
163+
164+
static void gre_ipv4(const uint8_t *buf, int n)
165+
{
166+
int ihl; // IP header length
167+
154168
ihl = 4 * (buf[0] & 0x0f);
155169
if (ihl > 60 || ihl < 20)
156170
{
157171
printf("IPv4 header too long\n");
158172
return;
159173
}
160174
// check source IPv4 address
161-
if (*(uint32_t *)(buf + 12) != remote.sin_addr.s_addr)
175+
const struct sockaddr_in *remote_in = (const struct sockaddr_in *)&remote;
176+
if (*(uint32_t *)(buf + 12) != remote_in->sin_addr.s_addr)
162177
{
163178
return;
164179
}
165180

181+
gre_any(buf + ihl, n - ihl);
182+
}
183+
184+
static void gre_ipv6(const uint8_t *buf, int n, const struct sockaddr_in6 *src)
185+
{
186+
if (n < 40)
187+
{
188+
return;
189+
}
190+
// check source IPv6 address
191+
const struct sockaddr_in6 *remote_in6 = (const struct sockaddr_in6 *)&remote;
192+
if (memcmp(src->sin6_addr.s6_addr, remote_in6->sin6_addr.s6_addr, 16) != 0)
193+
{
194+
return;
195+
}
196+
197+
gre_any(buf, n);
198+
}
199+
200+
static void gre_any(const uint8_t *buf, int n)
201+
{
166202
// parse GRE header
167-
if (*(uint16_t *)(buf + ihl) != 0)
203+
if (*(uint16_t *)(buf) != 0)
168204
{
169205
return;
170206
}
171-
uint16_t protocol = ntohs(*(uint16_t *)(buf + ihl + 2));
172-
if (protocol != 0x0800)
207+
uint16_t protocol = ntohs(*(uint16_t *)(buf + 2));
208+
if (protocol != ETHERTYPE_IP && protocol != ETHERTYPE_IPV6)
173209
{
174210
return;
175211
}
176212

177-
write(tun, buf + ihl + 4, n - ihl - 4);
213+
write(tun, buf, n);
178214
}
179215

180216
static int tun_cb(void)
181217
{
182218
int n;
183219

184-
n = read(tun, buf + 4, sizeof(buf) - 4);
220+
n = read(tun, buf, sizeof(buf));
185221
if (n < 0)
186222
{
187223
int err = errno;
@@ -191,9 +227,14 @@ static int tun_cb(void)
191227

192228
return 0;
193229
}
194-
*(uint16_t *)(buf) = 0;
195-
*(uint16_t *)(buf + 2) = htons(0x0800);
196-
sendto(sock, buf, n + 4, 0, (struct sockaddr *)&remote, sizeof(struct sockaddr));
230+
buf[0] = 0;
231+
buf[1] = 0;
232+
uint16_t proto = ntohs(*(uint16_t *)(buf + 2));
233+
if (proto != ETHERTYPE_IP && proto != ETHERTYPE_IPV6)
234+
{
235+
return 0;
236+
}
237+
sendto(sock, buf, n, 0, (struct sockaddr *)&remote, remote_len);
197238
return 0;
198239
}
199240

@@ -210,7 +251,7 @@ static int tun_new(const char *dev)
210251

211252
bzero(&ifr, sizeof(struct ifreq));
212253

213-
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
254+
ifr.ifr_flags = IFF_TUN;
214255
if (*dev != '\0')
215256
{
216257
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
@@ -284,3 +325,35 @@ static int daemonize(void)
284325

285326
return 0;
286327
}
328+
329+
static int inet_addr_storage(const char *cp, struct sockaddr_storage *sp, size_t *sp_len)
330+
{
331+
struct addrinfo hints;
332+
struct addrinfo *result = NULL;
333+
int res;
334+
335+
memset(&hints, 0, sizeof(hints));
336+
hints.ai_family = AF_UNSPEC;
337+
hints.ai_flags = AI_NUMERICHOST | AI_ADDRCONFIG;
338+
res = getaddrinfo(cp, NULL, &hints, &result);
339+
if (res != 0) {
340+
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res));
341+
return -1;
342+
}
343+
344+
memcpy(sp, result->ai_addr, result->ai_addrlen);
345+
*sp_len = result->ai_addrlen;
346+
347+
freeaddrinfo(result);
348+
result = NULL;
349+
350+
if (sp->ss_family == AF_INET) {
351+
struct sockaddr_in *sin = (struct sockaddr_in *)sp;
352+
sin->sin_port = htons(IPPROTO_GRE);
353+
} else if (sp->ss_family == AF_INET6) {
354+
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sp;
355+
sin->sin6_port = htons(IPPROTO_GRE);
356+
}
357+
358+
return 0;
359+
}

0 commit comments

Comments
 (0)