summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gre.c110
1 files changed, 98 insertions, 12 deletions
diff --git a/gre.c b/gre.c
index d66fd36..68f155c 100644
--- a/gre.c
+++ b/gre.c
@@ -43,6 +43,7 @@ static int tun;
static int sock;
static struct sockaddr_storage remote;
static size_t remote_len;
+static int remote_any;
static short type = IFF_TUN;
static uint8_t mac[6];
static int foreground;
@@ -59,6 +60,7 @@ static int setnonblock(int fd);
static int runas(const char *user);
static int daemonize(void);
static int inet_addr_storage(const char *cp, struct sockaddr_storage *sp, size_t *sp_len);
+static int is_any(const struct sockaddr_storage *ss);
int main(int argc, char **argv)
{
@@ -101,15 +103,22 @@ int main(int argc, char **argv)
}
}
- if ( (argc - optind) != 3)
+ int optnum = argc - optind;
+
+ if (optnum < 2 || optnum > 3)
{
- printf("usage: %s [-n|--tun|-p|--tap] <tun> remote local\n", argv[0]);
+ printf("usage: %s [-n|--tun|-p|--tap] <tun> [remote] local\n", argv[0]);
return EXIT_FAILURE;
}
const char *dev = argv[optind++];
- const char *remote_opt = argv[optind++];
- const char *local_opt = argv[optind++];
+ const char *remote_opt = NULL;
+ const char *local_opt = NULL;
+
+ if (optnum == 3)
+ remote_opt = argv[optind++];
+
+ local_opt = argv[optind++];
tun = tun_new(type, dev);
if (tun < 0)
@@ -139,12 +148,36 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
+ if (!remote_opt)
+ {
+ if (local.ss_family == AF_INET)
+ remote_opt = "0.0.0.0";
+ else
+ remote_opt = "::";
+ }
+
if (inet_addr_storage(remote_opt, &remote, &remote_len))
{
fprintf(stderr, "bad remote address\n");
return EXIT_FAILURE;
}
+ remote_any = is_any(&remote);
+ if (remote_any)
+ {
+ if (type == IFF_TUN)
+ {
+ fprintf(stderr, "tun required specifying remote\n");
+ return EXIT_FAILURE;
+ }
+
+ if (local.ss_family != AF_INET)
+ {
+ fprintf(stderr, "IPv4 required for mGRE\n");
+ return EXIT_FAILURE;
+ }
+ }
+
setnonblock(sock);
setnonblock(tun);
runas("nobody");
@@ -214,7 +247,7 @@ static void gre_cb(void)
if (res < 0)
{
- printf("Failed GRE decode");
+ printf("Failed GRE decode\n");
return;
}
@@ -318,6 +351,14 @@ static int gre_any(const uint8_t *buf, int n)
static int tun_cb(void)
{
int n;
+ struct sockaddr_storage dest;
+
+ if (!remote_any)
+ {
+ memcpy(&dest, &remote, sizeof(struct sockaddr_storage));
+ } else {
+ memset(&dest, 0, sizeof(struct sockaddr_storage));
+ }
n = read(tun, buf, sizeof(buf));
if (n < 0)
@@ -330,14 +371,36 @@ static int tun_cb(void)
return 0;
}
uint16_t proto = ntohs(*(uint16_t *)(buf + 2));
- uint8_t *frame;
+ uint8_t *frame = buf;
+
+ if (remote_any)
+ {
+ printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ frame[4 + 0],
+ frame[4 + 1],
+ frame[4 + 2],
+ frame[4 + 3],
+ frame[4 + 4],
+ frame[4 + 5]
+ );
+
+ if (frame[4] != 0x2e || frame[5] != 0x2e)
+ {
+ printf("Bad magic number!\n");
+ return 0;
+ }
- if (type == IFF_TAP)
+ struct sockaddr_in *sin = (struct sockaddr_in *)&dest;
+ sin->sin_family = AF_INET;
+ memcpy(&sin->sin_addr.s_addr, frame + 4 + 2, 4);
+
+ int offset = 2 + 6 + 6;
+ frame += offset;
+ n -= offset;
+ }
+ else if (type == IFF_TAP)
{
- frame = buf;
*(uint16_t*)(frame + 2) = htons(ETH_P_TEB);
- } else {
- frame = buf;
}
frame[0] = 0;
@@ -347,13 +410,20 @@ static int tun_cb(void)
case ETHERTYPE_IPV6:
break;
case ETHERTYPE_ARP:
- if (type == IFF_TAP)
+ if (type == IFF_TAP && !remote_any)
break;
return 0;
default:
return 0;
}
- sendto(sock, frame, n, 0, (struct sockaddr *)&remote, remote_len);
+
+ if (is_any(&dest))
+ {
+ printf("Remote unset!\n");
+ return -1;
+ }
+
+ sendto(sock, frame, n, 0, (struct sockaddr *)&dest, remote_len);
return 0;
}
@@ -487,3 +557,19 @@ static int inet_addr_storage(const char *cp, struct sockaddr_storage *sp, size_t
return 0;
}
+
+static int is_any(const struct sockaddr_storage *ss)
+{
+ switch(ss->ss_family) {
+ case AF_INET: {
+ const struct sockaddr_in *sin = (const struct sockaddr_in*)ss;
+ return sin->sin_addr.s_addr == INADDR_ANY;
+ }
+ case AF_INET6: {
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6*)ss;
+ return !memcmp(&in6addr_any, sin6->sin6_addr.s6_addr, sizeof(struct in6_addr));
+ }
+ default:
+ return -1;
+ }
+}