int handle_create_tap() { char name[CINDER_MAX_NAMELEN], id[12]; char *retstr; unsigned int len; int ret, srcReserve, destReserve; printf("Please enter the name for the tap: "); retstr = fgets(name, sizeof(name), stdin); if (!retstr) { printf("Error reading name.\n"); return -EINVAL; } name[CINDER_MAX_NAMELEN - 1] = 0; chomp(name); len = strlen(name) + 1; memset(id, 0, sizeof(id)); printf("Please enter the source reserve id.\n"); retstr = fgets(id, sizeof(id), stdin); if (!retstr) { printf("Error reading id.\n"); return -EINVAL; } srcReserve = atoi(id); memset(id, 0, sizeof(id)); printf("Please enter the destination reserve id.\n"); retstr = fgets(id, sizeof(id), stdin); if (!retstr) { printf("Error reading id.\n"); return -EINVAL; } destReserve = atoi(id); ret = create_tap(name, len, srcReserve, destReserve); if (ret < 0) { printf("Error creating tap: %d\n", ret); } else printf("Created tap with id %d\n", ret); return ret; }
int main(int argc, char *argv[]) { const char *ifname = "xxx"; struct pollfd p; int fd; fd = create_tap(ifname); if (fd < 0) return 1; if (inet_ifup(ifname) < 0) { close(fd); return 1; } memset(&p, 0, sizeof(p)); p.fd = fd; p.events = POLLHUP | POLLIN; while (1) { unsigned char buf[2048]; int len; len = poll(&p, 1, -1); if (len < 0) break; if (len == 0) continue; len = read(fd, buf, sizeof(buf)); if (len < 0) break; decode_packet(buf, len); } return 0; }
int run_tunnel(struct args *args, sigset_t *orig_mask) { char device[IFNAMSIZ]; int fd = create_tap(args->iface, device, args->mtu); if (fd < 0) { fprintf(stderr, "unable to create tap device\n"); return 1; } printf("tap device is: %s\n", device); if (args->up_script) { int ret = run_updown(args->up_script, device); if (ret != 0) { fprintf(stderr, "up script exited with status: %d\n", ret); return 1; } } // TODO: make the port and bind address configurable int sockfd = setup_socket(inet_addr("0.0.0.0"), 1234); if (sockfd < 0) { fprintf(stderr, "unable to create socket\n"); return 1; } if (drop_privileges(args->uid, args->gid) != 0) { fprintf(stderr, "couldn't drop privileges\n"); return 1; } // circular queues struct frame recv_queue[RECV_QUEUE] = {0}; size_t recv_idx = 0; size_t recv_len = 0; struct frame send_queue[SEND_QUEUE] = {0}; size_t send_idx = 0; size_t send_len = 0; struct timespec tm; memset(&tm, 0, sizeof tm); tm.tv_nsec = 10000000; // 0.01 seconds struct pollfd fds[2]; memset(&fds, 0, sizeof fds); fds[0].fd = fd; fds[1].fd = sockfd; struct sockaddr_in remote; memset(&remote, 0, sizeof remote); char has_remote = 0; if (args->remote) { remote.sin_family = AF_INET; remote.sin_port = htons(1234); has_remote = 1; remote.sin_addr.s_addr = inet_addr(args->remote); if (remote.sin_addr.s_addr == INADDR_NONE) { fprintf(stderr, "failed to parse remote: %s\n", args->remote); return 2; } fprintf(stderr, "running in client mode with remote: %s\n", args->remote); } fprintf(stderr, "tunnel is up\n"); for (;;) { fds[0].events = POLLIN; if (recv_len > 0) { fds[0].events |= POLLOUT; } fds[1].events = POLLIN; if (send_len > 0 && has_remote) { fds[1].events |= POLLOUT; } int result = ppoll(fds, 2, &tm, orig_mask); if (result < 0) { if (errno != EINTR) { perror("ppoll"); return 3; } } if (exit_wanted) { fprintf(stderr, "\nreceived signal %d, stopping tunnel\n", received_signal); break; } // tap can handle a write if (fds[0].revents & POLLOUT) { struct frame *f = &recv_queue[recv_idx]; assert(f->len <= args->mtu + ETHERNET_HEADER); recv_idx = (recv_idx + 1) % RECV_QUEUE; recv_len -= 1; ssize_t n = write(fd, f->data, f->len); if (n < 0) { if (errno == EINVAL) { fprintf(stderr, "received garbage frame\n"); } else { perror("write"); return 4; } } else if (n < f->len) { printf("[error] only wrote %zd bytes to tap (out of %zd bytes)\n", n, f->len); } } // udp socket can handle a write if (fds[1].revents & POLLOUT) { struct frame *f = &send_queue[send_idx]; assert(f->len <= args->mtu + ETHERNET_HEADER); send_idx = (send_idx + 1) % SEND_QUEUE; send_len -= 1; ssize_t n = sendto(sockfd, f->data, f->len, 0, (struct sockaddr *) &remote, sizeof remote); if (n < 0) { perror("sendto"); return 4; } else if (n < f->len) { printf("[error] only sent %zd bytes to peer (out of %zd bytes)\n", n, f->len); } } // tap has data for us to read if (fds[0].revents & POLLIN) { size_t idx = (send_idx + send_len) % SEND_QUEUE; if (send_len < SEND_QUEUE) { send_len += 1; } else { assert(send_len == SEND_QUEUE); printf("dropping frame from send queue\n"); // put this packet at the end of the queue; // drop the first frame in the queue send_idx += 1; } struct frame *f = &send_queue[idx]; memset(f, 0, sizeof(struct frame)); ssize_t n = read(fd, &f->data, args->mtu + ETHERNET_HEADER); f->len = n; } // udp socket has data for us to read if (fds[1].revents & POLLIN) { size_t idx = (recv_idx + recv_len) % RECV_QUEUE; if (recv_len < RECV_QUEUE) { recv_len += 1; } else { assert(recv_len == RECV_QUEUE); printf("dropping frame from recv queue\n"); // put this packet at the end of the queue; // drop the first frame in the queue recv_idx += 1; } struct frame *f = &recv_queue[idx]; memset(f, 0, sizeof(struct frame)); // TODO: handle case where remote changes, in both server+client mode socklen_t l = sizeof(remote); has_remote = 1; ssize_t n = recvfrom( sockfd, &f->data, args->mtu + ETHERNET_HEADER, 0, (struct sockaddr *) &remote, &l ); f->len = n; } } if (args->down_script) { int ret = run_updown(args->down_script, device); if (ret != 0) { fprintf(stderr, "down script exited with status: %d\n", ret); return 1; } } return 0; }