diff --git a/configure.ac b/configure.ac index 904265fe..ae0e18d0 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,8 @@ AC_CHECK_HEADERS([net/route.h], [], AC_MSG_ERROR([Required header not found]), [ # Checks for optional header files. AC_CHECK_HEADERS([ \ libutil.h \ +linux/if_ppp.h \ +linux/if_tun.h \ mach/mach.h \ pty.h \ semaphore.h \ diff --git a/src/io.c b/src/io.c index b1b43eb1..3ec47a88 100644 --- a/src/io.c +++ b/src/io.c @@ -35,7 +35,9 @@ #include #include #include +#if HAVE_LINUX_IF_PPP_H #include +#endif #include #include @@ -238,18 +240,21 @@ struct lcp_option_conf { int len; const char *name; }; -int default_mru = 1534; -const struct lcp_option_conf lcp_valid_options[256] = { + +static const int default_mru = 1534; + +static const struct lcp_option_conf lcp_valid_options[256] = { [0] = { 0, 0, "RESERVED", }, - [1] = {LCP_OF_VALID, 4, "MRU", }, /* RFC 1661 */ - [2] = {LCP_OF_VALID, 6, "Async-Control-Character-Map",}, /* RFC 1172 */ + [1] = {LCP_OF_VALID, 4, "MRU", }, /* RFC 1661 */ + [2] = {LCP_OF_VALID, 6, "Async-Control-Character-Map",}, /* RFC 1172 */ [3] = {LCP_OF_VALID, 4, "Auth-Protocol", }, [4] = {LCP_OF_VALID, 4, "Quality-Protocol", }, [5] = {LCP_OF_VALID, 6, "Magic-Num", }, - [6] = {LCP_OF_VALID, 6, "Link-Quality-Monitoring", }, /* RFC 1172 */ + [6] = {LCP_OF_VALID, 6, "Link-Quality-Monitoring", }, /* RFC 1172 */ [7] = {LCP_OF_VALID, 2, "Protocol-Field-Comp", }, - [8] = {LCP_OF_VALID, 2, "Addr&Ctrl-Field-Comp", }, + [8] = {LCP_OF_VALID, 2, "Addr&Ctrl-Field-Comp", }, }; + struct lcp_option_value { int flag; #define LCP_OF_DEFAULT 0x20000 @@ -262,131 +267,130 @@ struct lcp_option_value { } u; void *extra; }; -int magic_seed = 0x64696E67; -/* -struct conf_option lcp_default[256] = { - [1] = {LCP_OF_DEFAULT, {.number = 1500}, }, - [2] = {LCP_OF_DEFAULT, {.number = 0xFFFFFFFF}, }, - [3] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [4] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [5] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [6] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [7] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [8] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -}; -*/ -struct conf_option *lcp_self[256] = { + +static const uint32_t magic_seed = 0x64696E67; /* ['d', 'i', 'n', 'g'] */ + +static struct conf_option *lcp_self[256] = { NULL, }; -struct conf_option *lcp_peer[256] = { +static struct conf_option *lcp_peer[256] = { NULL, }; -static int lcp_id = 0; -int conf_option_get(struct conf_option **options, int type, void *data, int len) +static int lcp_id; + +static int conf_option_get(struct conf_option **options, int type, void *data, int len) { struct conf_option *opt = options[type]; + if (opt != NULL) { int copy = opt->length - 2; - if (copy > len) { + + if (copy > len) copy = len; - } memcpy(data, opt->data, copy); return 0; } return -1; } -int conf_option_set(struct conf_option **options, int type, int len, void *data) + +static int conf_option_set(struct conf_option **options, int type, int len, void *data) { struct conf_option *opt = options[type]; + if (len == 0) { options[type] = NULL; free(opt); return 0; } - if (opt == NULL) { + if (opt == NULL) opt = malloc(len); - } else if (opt->length != len) { + else if (opt->length != len) opt = realloc(opt, len); - } if (opt != NULL) { options[type] = opt; opt->type = type; opt->length = len; - if (len > 2) { + if (len > 2) memcpy(opt->data, data, len - 2); - } } return (opt == NULL) ? -1 : 0; } -struct conf_option *conf_option_init(struct conf_option_list *optlist) + +static struct conf_option *conf_option_init(struct conf_option_list *optlist) { int header = sizeof(struct lcp_header) + sizeof(uint16_t); + optlist->head = malloc(header + default_mru); if (optlist->head) { memset(optlist->head, 0, header + default_mru); - optlist->head = (struct conf_option *)(((uint8_t *)optlist->head) + header); + optlist->head = (struct conf_option *)(((uint8_t *)optlist->head) + + header); optlist->tail = optlist->head; } return optlist->head; } -int conf_option_encode(struct conf_option_list *optlist, int type, int len, void *data) + +static int conf_option_encode(struct conf_option_list *optlist, int type, int len, + void *data) { optlist->tail->type = type; optlist->tail->length = len; - if (len > 2) { + if (len > 2) memcpy(optlist->tail->data, data, len - 2); - } optlist->tail = (struct conf_option *)(optlist->tail->data + len - 2); return 0; } -int conf_option_length(struct conf_option_list *optlist) + +static int conf_option_length(const struct conf_option_list *optlist) { - int len = 0; - if (optlist) { - len = (uint8_t *)optlist->tail - (uint8_t *)optlist->head; - } - return len; + if (optlist) + return (uint8_t *)optlist->tail - (uint8_t *)optlist->head; + else + return 0; } -int conf_option_free(struct conf_option_list *optlist) + +static int conf_option_free(struct conf_option_list *optlist) { if (optlist->head != NULL) { int header = sizeof(struct lcp_header) + sizeof(uint16_t); + free((uint8_t *)optlist->head - header); optlist->head = optlist->tail = NULL; } return 0; } -int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_list *optlist, int force) + +static int lcp_option_send(struct tunnel *tunnel, int id, + int code, struct conf_option_list *optlist, int force) { int ret = -1; + if (optlist && optlist->head) { uint8_t *head = (uint8_t *)optlist->head; struct lcp_header *header = ((struct lcp_header *)head) - 1; unsigned short *ppp_type = ((unsigned short *)header) - 1; - int len = conf_option_length(optlist); - int hdrlen = sizeof(struct lcp_header) + sizeof(uint16_t); + const size_t hdrlen = sizeof(struct lcp_header) + sizeof(uint16_t); + const int len = conf_option_length(optlist); if (len > 0 || force) { - ssize_t pktsize; + const ssize_t pktsize = hdrlen + len; struct ppp_packet *packet = NULL; *ppp_type = htons(PPP_LCP); header->code = code; - header->id = id ? id : lcp_id ++; + header->id = id ? id : lcp_id++; header->length = htons(len + sizeof(*header)); - pktsize = hdrlen + len; packet = malloc(sizeof(*packet) + 6 + pktsize); - if (packet == NULL) { + if (packet == NULL) goto out; - } packet->len = pktsize; memcpy(pkt_data(packet), ppp_type, pktsize); log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, - packet->len); + packet->len); #if HAVE_USR_SBIN_PPPD log_packet("pppd: ", packet->len, pkt_data(packet)); #else @@ -400,12 +404,14 @@ int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_ out: return ret; } -int conf_request(struct tunnel *tunnel) + +static int conf_request(struct tunnel *tunnel) { int ret = 0; struct conf_option_list request; - uint16_t mru = htons(default_mru); - int magic = htonl(magic_seed); + uint16_t mru = htons((uint16_t)default_mru); + uint32_t magic = htonl(magic_seed); + conf_option_init(&request); conf_option_encode(&request, LCP_COPT_MRU, 4, &mru); conf_option_encode(&request, LCP_COPT_MAGIC, 6, &magic); @@ -413,38 +419,39 @@ int conf_request(struct tunnel *tunnel) conf_option_free(&request); return ret; } -int lcp_packet(struct tunnel *tunnel, void *packet, int len) + +static int lcp_packet(struct tunnel *tunnel, void *packet, int len) { struct lcp_header *header = packet; log_debug("packet %s\n", lcp_code_name[header->code]); switch (header->code) { - case LCP_CONF_REQUEST: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; + case LCP_CONF_REQUEST: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); struct conf_option_list ack; struct conf_option_list nack; struct conf_option_list reject; + conf_option_init(&ack); conf_option_init(&nack); conf_option_init(&reject); - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); switch (co->type) { case LCP_COPT_ACCM: p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); - conf_option_set(lcp_peer, co->type, co->length, co_data(co)); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_set(lcp_peer, co->type, + co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case LCP_COPT_AUTH: switch (ntohs(*(uint16_t *)co_data(co))) { - case PPP_CHAP: - { + case PPP_CHAP: { struct { uint16_t chap; uint8_t algo; @@ -453,24 +460,32 @@ int lcp_packet(struct tunnel *tunnel, void *packet, int len) break; } default: - p += sprintf(p, "%x", ntohs(*(uint16_t *)co_data(co))); + p += sprintf(p, "%x", + ntohs(*(uint16_t *)co_data(co))); break; } - conf_option_set(lcp_peer, co->type, co->length, co_data(co)); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_set(lcp_peer, co->type, + co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case LCP_COPT_MAGIC: p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); - conf_option_set(lcp_peer, co->type, co->length, co_data(co)); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_set(lcp_peer, co->type, + co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case LCP_COPT_PFC: case LCP_COPT_ACFC: - conf_option_set(lcp_peer, co->type, co->length, co_data(co)); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_set(lcp_peer, co->type, + co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; default: - conf_option_encode(&reject, co->type, co->length, co_data(co)); + conf_option_encode(&reject, co->type, + co->length, co_data(co)); break; } log_debug("%s\n", buff); @@ -478,21 +493,27 @@ int lcp_packet(struct tunnel *tunnel, void *packet, int len) co = (struct conf_option *)((uint8_t *)co + co->length); } if (header->code == LCP_CONF_REQUEST) { - int ret = -1; - ret = lcp_option_send(tunnel, header->id, LCP_CONF_ACK, &ack, 0); + int ret = lcp_option_send(tunnel, header->id, LCP_CONF_ACK, + &ack, 0); + if (ret < 0) { - log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_error("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } - ret = lcp_option_send(tunnel, header->id, LCP_CONF_NAK, &nack, 0); + ret = lcp_option_send(tunnel, header->id, LCP_CONF_NAK, + &nack, 0); if (ret < 0) { - log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_error("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } - ret = lcp_option_send(tunnel, header->id, LCP_CONF_REJECT, &reject, 0); + ret = lcp_option_send(tunnel, header->id, LCP_CONF_REJECT, + &reject, 0); if (ret < 0) { - log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_error("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } switch (tunnel->tun_state) { case TUN_PPP_LCP: @@ -508,15 +529,14 @@ int lcp_packet(struct tunnel *tunnel, void *packet, int len) break; } - case LCP_CONF_ACK: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); + case LCP_CONF_ACK: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); + while (olen > 0) { char buff[128]; char *p = buff; + conf_option_set(lcp_self, co->type, co->length, co_data(co)); p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); switch (co->type) { @@ -549,15 +569,14 @@ int lcp_packet(struct tunnel *tunnel, void *packet, int len) } break; } - case LCP_CONF_NAK: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); + case LCP_CONF_NAK: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); + while (olen > 0) { char buff[128]; char *p = buff; + conf_option_set(lcp_self, co->type, 0, NULL); p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); switch (co->type) { @@ -585,15 +604,14 @@ int lcp_packet(struct tunnel *tunnel, void *packet, int len) } break; } - case LCP_CONF_REJECT: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); + case LCP_CONF_REJECT: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); + while (olen > 0) { char buff[128]; char *p = buff; + conf_option_set(lcp_self, co->type, 0, NULL); p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); switch (co->type) { @@ -681,17 +699,16 @@ const char *ipcp_code_name[] = { "IPCP Code-Reject", }; -int nroutes = 0; -char **routes = NULL; -uint32_t ip_address = 0; -uint32_t peer_address = 0; -uint32_t primary_dns = 0; -uint32_t secondary_dns = 0; +static uint32_t ip_address; +static uint32_t peer_address; +static uint32_t primary_dns; +static uint32_t secondary_dns; int ipcp_add_route(struct tunnel *tunnel, uint32_t dst, uint32_t mask, uint32_t gw) { int ret = 0; + int sd = -1; struct rtentry rt; struct sockaddr_in *sin = NULL; @@ -714,47 +731,53 @@ int ipcp_add_route(struct tunnel *tunnel, uint32_t dst, uint32_t mask, uint32_t sin->sin_port = 0; sin->sin_addr.s_addr = htonl(mask); - int sd = socket(AF_INET, SOCK_DGRAM, 0); + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) { + log_error("socket: %s\n", strerror(errno)); + return sd; + } + ret = ioctl(sd, SIOCADDRT, &rt); - if (ret == 0) { + if (ret == 0) log_debug("route add success\n"); - } else { + else log_error("route add failed %d: %s\n", errno, strerror(errno)); - } - close(sd); + if (close(sd)) + log_warn("close: %s\n", strerror(errno)); return ret; } -int ipcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_list *optlist, int force) + +int ipcp_option_send(struct tunnel *tunnel, int id, int code, + struct conf_option_list *optlist, int force) { int ret = -1; + if (optlist && optlist->head) { uint8_t *packet = (uint8_t *)optlist->head; struct ipcp_header *header = ((struct ipcp_header *)packet) - 1; unsigned short *ppp_type = ((unsigned short *)header) - 1; - int len = conf_option_length(optlist); - int hdrlen = sizeof(struct ipcp_header) + sizeof(uint16_t); + const size_t hdrlen = sizeof(struct ipcp_header) + sizeof(uint16_t); + const int len = conf_option_length(optlist); if (len > 0 || force) { - ssize_t pktsize; + const ssize_t pktsize = hdrlen + len; struct ppp_packet *packet = NULL; *ppp_type = htons(PPP_IPCP); header->code = code; - header->id = id ? id : lcp_id ++; + header->id = id ? id : lcp_id++; header->length = htons(len + sizeof(*header)); log_debug("send ipcp %d\n", len); - pktsize = hdrlen + len; packet = malloc(sizeof(*packet) + 6 + pktsize); - if (packet == NULL) { + if (packet == NULL) goto out; - } packet->len = pktsize; memcpy(pkt_data(packet), ppp_type, pktsize); log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, - packet->len); + packet->len); #if HAVE_USR_SBIN_PPPD log_packet("pppd: ", packet->len, pkt_data(packet)); #else @@ -768,6 +791,7 @@ int ipcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option out: return ret; } + int ipcp_packet(struct tunnel *tunnel, void *packet, int len) { int ret = 0; @@ -775,35 +799,38 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) log_debug("packet %s\n", ipcp_code_name[header->code]); switch (header->code) { - case IPCP_CONF_REQUEST: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; + case IPCP_CONF_REQUEST: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct ipcp_header); struct conf_option_list ack; struct conf_option_list nack; struct conf_option_list reject; struct conf_option_list request; + conf_option_init(&ack); conf_option_init(&nack); conf_option_init(&reject); conf_option_init(&request); - olen -= sizeof(struct ipcp_header); - co = (struct conf_option *)(header + 1); + while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); switch (co->type) { case IPCP_COPT_ADDRESSES: p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case IPCP_COPT_COMPRESS: - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case IPCP_COPT_ADDRESS: p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); peer_address = *(uint32_t *)co_data(co); break; default: @@ -815,32 +842,46 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) } if (header->code == IPCP_CONF_REQUEST) { int ret = -1; - ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_ACK, &ack, 0); + + ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_ACK, + &ack, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } - ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_NAK, &nack, 0); + ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_NAK, + &nack, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } - ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_REJECT, &reject, 0); + ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_REJECT, + &reject, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } do { uint32_t compress = htonl(0x002d0f01); - conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); - conf_option_encode(&request, IPCP_COPT_COMPRESS, 6, &compress); - // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); - // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); - ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); + + conf_option_encode(&request, IPCP_COPT_ADDRESS, + 6, &ip_address); + conf_option_encode(&request, IPCP_COPT_COMPRESS, + 6, &compress); + // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, + // 6, &primary_dns); + // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, + // 6, &secondary_dns); + ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, + &request, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } } while (0); } @@ -851,15 +892,16 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) break; } - case IPCP_CONF_ACK: - { + case IPCP_CONF_ACK: { int olen = ntohs(header->length); struct conf_option *co = NULL; + olen -= sizeof(struct lcp_header); co = (struct conf_option *)(header + 1); while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); switch (co->type) { case IPCP_COPT_ADDRESSES: @@ -887,21 +929,19 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) co = (struct conf_option *)((uint8_t *)co + co->length); } - int tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr); tun_ifup(tunnel->tun_iface, ip_address, peer_address); ipv4_set_tunnel_routes(tunnel); break; } - case IPCP_CONF_NAK: - { + case IPCP_CONF_NAK: { int send_request = 0; - int olen = ntohs(header->length); - struct conf_option *co = NULL; - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); + while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); switch (co->type) { case IPCP_COPT_ADDRESSES: @@ -940,28 +980,34 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) if (send_request) { struct conf_option_list request; + conf_option_init(&request); - conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); - // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); - // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); - ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); + conf_option_encode(&request, IPCP_COPT_ADDRESS, + 6, &ip_address); + // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, + // 6, &primary_dns); + // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, + // 6, &secondary_dns); + ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, + &request, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } conf_option_free(&request); } break; } - case IPCP_CONF_REJECT: - { + case IPCP_CONF_REJECT: { + struct conf_option *co = (struct conf_option *)(header + 1); int olen = ntohs(header->length); - struct conf_option *co = NULL; + olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); switch (co->type) { case IPCP_COPT_ADDRESSES: @@ -988,14 +1034,20 @@ int ipcp_packet(struct tunnel *tunnel, void *packet, int len) do { struct conf_option_list request; + conf_option_init(&request); - conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); - // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); - // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); - ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); + conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, + &ip_address); + // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, + // &primary_dns); + // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, + // &secondary_dns); + ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, + &request, 0); if (ret < 0) { - log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); - exit(1); + log_debug("send conf_ack failed %d: %s\n", + errno, strerror(errno)); + exit(EXIT_FAILURE); } } while (0); break; @@ -1070,21 +1122,19 @@ static void *pppd_read(void *arg) first_time = 0; } if (tunnel->use_tun) { - ssize_t pktsize; + ssize_t pktsize = n + 2; struct ppp_packet *packet = NULL; - pktsize = n + 2; packet = malloc(sizeof(*packet) + 6 + pktsize); - if (packet == NULL) { + if (packet == NULL) goto exit; - } packet->len = pktsize; pkt_data(packet)[0] = 0x00; pkt_data(packet)[1] = 0x21; memcpy(pkt_data(packet) + 2, buf, n); log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, - packet->len); + packet->len); #if HAVE_USR_SBIN_PPPD log_packet("pppd: ", packet->len, pkt_data(packet)); #else @@ -1217,7 +1267,7 @@ static void *pppd_write(void *arg) break; } len = hdlc_encode(hdlc_buffer, hdlc_bufsize, - pkt_data(packet), packet->len); + pkt_data(packet), packet->len); if (len < 0) { log_error("Failed to encode PPP packet into HDLC frame.\n"); goto err_free_buf; @@ -1402,10 +1452,9 @@ static void *ssl_read(void *arg) set_tunnel_ips(tunnel, packet); - if (tunnel->use_tun) { - int tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr); - tun_ifup(tunnel->tun_iface, tunnel->ipv4.ip_addr.s_addr, 0); - } + if (tunnel->use_tun) + tun_ifup(tunnel->tun_iface, + tunnel->ipv4.ip_addr.s_addr, 0); strcpy(line, "["); strncat(line, inet_ntoa(tunnel->ipv4.ip_addr), 15); strcat(line, "], ns ["); diff --git a/src/tunnel.c b/src/tunnel.c index adbb24e8..c3d5094f 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -158,24 +158,23 @@ static int on_ppp_if_down(struct tunnel *tunnel) return 0; } -#define TUN_PATH "/dev/net/tun" +static const char TUN_PATH[] = "/dev/net/tun"; -#define TUN_ASSERT(cond, fmt, ...) do { \ - if (! (cond)) { \ - log_info(fmt "\n", ##__VA_ARGS__); \ - exit(1); \ - } \ +#define TUN_ASSERT(cond, fmt, ...) do { \ + if (!(cond)) { \ + log_info(fmt "\n", ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } \ } while (0) -static int -tun_open(struct ifreq *ifr) +static int tun_open(struct ifreq *ifr) { int ret = 0; int fd = -1; fd = open(TUN_PATH, O_RDWR, 0); if (fd >= 0) { - ret = ioctl(fd, TUNSETIFF, (unsigned long)ifr); + ret = ioctl(fd, TUNSETIFF, ifr); if (ret < 0) { log_error("ioctl: %s\n", strerror(errno)); close(fd); @@ -189,53 +188,53 @@ tun_open(struct ifreq *ifr) return fd; } -static int -__attribute__((unused)) -tun_close(int fd) +static int tun_close(int fd) { return close(fd); } -int -tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr) +int tun_ifup(const char *ifname, uint32_t ip_addr, uint32_t peer_addr) { struct ifreq ifreq = { .ifr_flags = IFF_UP, }; - struct sockaddr_in *sin = NULL; int fd = -1; int ret = -1; fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { + if (fd < 0) return fd; - } - strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); - ret = ioctl(fd, SIOCGIFFLAGS, (unsigned long)&ifreq); - TUN_ASSERT(ret == 0, "ioctl get ifflags", 0); + + TUN_ASSERT(strlen(ifname) < IFNAMSIZ, "ifname length"); + strcpy(ifreq.ifr_name, ifname); + + ret = ioctl(fd, SIOCGIFFLAGS, &ifreq); + TUN_ASSERT(ret == 0, "ioctl get ifflags"); if (ret == 0) { ifreq.ifr_flags |= IFF_UP; - ret = ioctl(fd, SIOCSIFFLAGS, (unsigned long)&ifreq); - TUN_ASSERT(ret == 0, "ioctl set ifflags", 0); + ret = ioctl(fd, SIOCSIFFLAGS, &ifreq); + TUN_ASSERT(ret == 0, "ioctl set ifflags"); } if (ip_addr != 0) { - sin = (struct sockaddr_in *)&ifreq.ifr_addr; + struct sockaddr_in *sin = (struct sockaddr_in *)&ifreq.ifr_addr; + sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = ip_addr; - ret = ioctl(fd, SIOCSIFADDR, (unsigned long)&ifreq); + ret = ioctl(fd, SIOCSIFADDR, &ifreq); TUN_ASSERT(ret == 0, "ioctl set ifaddr err: %s", strerror(errno)); log_info("setup <%s> ip addr to %s\n", ifname, inet_ntoa(sin->sin_addr)); } if (peer_addr != 0) { - sin = (struct sockaddr_in *)&ifreq.ifr_addr; + struct sockaddr_in *sin = (struct sockaddr_in *)&ifreq.ifr_addr; + sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = peer_addr; - ret = ioctl(fd, SIOCSIFDSTADDR, (unsigned long)&ifreq); - TUN_ASSERT(ret == 0, "ioctl set dst ifaddr", 0); + ret = ioctl(fd, SIOCSIFDSTADDR, &ifreq); + TUN_ASSERT(ret == 0, "ioctl set dst ifaddr"); } close(fd); @@ -243,17 +242,15 @@ tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr) return ret; } -static int -__attribute__((unused)) -tun_setup(struct tunnel *tunnel) +static int tun_setup(struct tunnel *tunnel) { int flags = 0; int tun_fd = -1; - struct ifreq ifreq = { - .ifr_flags = IFF_TUN | IFF_NO_PI, - }; + struct ifreq ifreq = { + .ifr_flags = IFF_TUN | IFF_NO_PI, + }; - tun_fd = tun_open(&ifreq); + tun_fd = tun_open(&ifreq); if (tun_fd < 0) { log_error("tun_open failed: %s\n", strerror(errno)); return 1; @@ -264,6 +261,7 @@ tun_setup(struct tunnel *tunnel) flags = 0; if (fcntl(tun_fd, F_SETFL, flags | O_NONBLOCK) == -1) { log_error("fcntl failed: %s\n", strerror(errno)); + tun_close(tun_fd); return 1; } @@ -276,9 +274,7 @@ tun_setup(struct tunnel *tunnel) return 0; } -static int -__attribute__((unused)) -pppd_run(struct tunnel *tunnel) +static int pppd_run(struct tunnel *tunnel) { pid_t pid; int amaster; @@ -790,6 +786,8 @@ static int tcp_connect(struct tunnel *tunnel) >= IF_NAMESIZE) { log_error("interface name too long\n"); goto err_post_socket; + } else { + strcpy(ifr.ifr_name, tunnel->config->iface_name); } ifr.ifr_addr.sa_family = AF_INET; if (ioctl(handle, SIOCGIFADDR, &ifr) == -1) { @@ -1436,16 +1434,15 @@ int run_tunnel(struct vpn_config *config) // Step 4: run a pppd process log_info("Establishing the tunnel\n"); - if (tunnel.use_tun) { + if (tunnel.use_tun) ret = tun_setup(&tunnel); - } else { + else ret = pppd_run(&tunnel); - } if (ret) goto err_tunnel; // Step 5: ask gateway to start tunneling - log_info("Switch to tunneling mode\n"); + log_debug("Switch to tunneling mode\n"); ret = http_send(&tunnel, "GET /remote/sslvpn-tunnel HTTP/1.1\r\n" "Host: sslvpn\r\n" @@ -1459,7 +1456,7 @@ int run_tunnel(struct vpn_config *config) tunnel.state = STATE_CONNECTING; // Step 6: perform io between pppd and the gateway, while tunnel is up - log_info("Starting IO through the tunnel\n"); + log_debug("Starting IO through the tunnel\n"); io_loop(&tunnel); log_debug("disconnecting\n"); diff --git a/src/tunnel.h b/src/tunnel.h index af4d64e2..6c790f6b 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -93,6 +93,8 @@ struct token { int ppp_interface_is_up(struct tunnel *tunnel); +int tun_ifup(const char *ifname, uint32_t ip_addr, uint32_t peer_addr); + int ssl_connect(struct tunnel *tunnel); int run_tunnel(struct vpn_config *config); diff --git a/tun.patch b/tun.patch deleted file mode 100644 index 77052302..00000000 --- a/tun.patch +++ /dev/null @@ -1,1241 +0,0 @@ -diff --git a/src/http.c b/src/http.c -index e2dbb24..b65f622 100644 ---- a/src/http.c -+++ b/src/http.c -@@ -824,6 +824,11 @@ static int parse_xml_config(struct tunnel *tunnel, const char *buffer) - if (!gateway) - log_warn("No gateway address, using interface for routing\n"); - -+ if (tunnel->use_tun) { -+ tunnel->ipv4.ip_addr.s_addr = inet_addr(gateway); -+ tunnel->ipv4.peer_addr.s_addr = inet_addr("192.0.2.1"); -+ } -+ - // The dns search string - val = buffer; - while ((val = xml_find('<', "dns", val, 2))) { -diff --git a/src/io.c b/src/io.c -index 577623b..86600c5 100644 ---- a/src/io.c -+++ b/src/io.c -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -175,6 +176,845 @@ static os_semaphore_t sem_pppd_ready; - static os_semaphore_t sem_if_config; - static os_semaphore_t sem_stop_io; - -+struct lcp_header { -+ uint8_t code; -+#define LCP_CONF_REQUEST 1 -+#define LCP_CONF_ACK 2 -+#define LCP_CONF_NAK 3 -+#define LCP_CONF_REJECT 4 -+#define LCP_TERM_REQUEST 5 -+#define LCP_TERM_ACK 6 -+#define LCP_CODE_REJECT 7 -+#define LCP_PROT_REJECT 8 -+#define LCP_ECHO_REQUEST 9 -+#define LCP_ECHO_REPLY 10 -+#define LCP_DISCARD 11 -+ uint8_t id; -+ uint16_t length; -+}; -+ -+const char *lcp_code_name[] = { -+ "", -+ "Configure-Request", -+ "Configure-Ack", -+ "Configure-Nak", -+ "Configure-Reject", -+ "Terminate-Request", -+ "Terminate-Ack", -+ "Code-Reject", -+ "Protocol-Reject", -+ "Echo-Request", -+ "Echo-Reply", -+ "Discard-Request", -+}; -+struct lcp_conf_request { -+ struct lcp_header header; -+}; -+struct conf_option { -+ uint8_t type; -+#define LCP_COPT_MRU 1 -+#define LCP_COPT_ACCM 2 -+#define LCP_COPT_AUTH 3 -+#define LCP_COPT_QUALITY 4 -+#define LCP_COPT_MAGIC 5 -+#define LCP_COPT_PFC 7 -+#define LCP_COPT_ACFC 8 -+ uint8_t length; -+ uint8_t data[]; -+}; -+static void *co_data(struct conf_option *co) -+{ -+ return co->data; -+} -+ -+struct conf_option_list { -+ struct conf_option *head; -+ struct conf_option *tail; -+}; -+ -+struct lcp_option_conf { -+ int flag; -+#define LCP_OF_VALID 0x10000 -+ int len; -+ const char *name; -+}; -+int default_mru = 1534; -+const struct lcp_option_conf lcp_valid_options[256] = { -+ [0] = { 0, 0, "RESERVED", }, -+ [1] = {LCP_OF_VALID, 4, "MRU", }, /* RFC 1661 */ -+ [2] = {LCP_OF_VALID, 6, "Async-Control-Character-Map",}, /* RFC 1172 */ -+ [3] = {LCP_OF_VALID, 4, "Auth-Protocol", }, -+ [4] = {LCP_OF_VALID, 4, "Quality-Protocol", }, -+ [5] = {LCP_OF_VALID, 6, "Magic-Num", }, -+ [6] = {LCP_OF_VALID, 6, "Link-Quality-Monitoring", }, /* RFC 1172 */ -+ [7] = {LCP_OF_VALID, 2, "Protocol-Field-Comp", }, -+ [8] = {LCP_OF_VALID, 2, "Addr&Ctrl-Field-Comp", }, -+}; -+struct lcp_option_value { -+ int flag; -+#define LCP_OF_DEFAULT 0x20000 -+#define LCP_OF_DISABLE 0x00100 -+#define LCP_OF_LINK 0x000FF -+ int len; -+ union { -+ int boolean; -+ int number; -+ } u; -+ void *extra; -+}; -+int magic_seed = 0x64696E67; -+/* -+struct conf_option lcp_default[256] = { -+ [1] = {LCP_OF_DEFAULT, {.number = 1500}, }, -+ [2] = {LCP_OF_DEFAULT, {.number = 0xFFFFFFFF}, }, -+ [3] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+ [4] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+ [5] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+ [6] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+ [7] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+ [8] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -+}; -+*/ -+struct conf_option *lcp_self[256] = { -+ NULL, -+}; -+struct conf_option *lcp_peer[256] = { -+ NULL, -+}; -+ -+static int lcp_id = 0; -+int conf_option_get(struct conf_option **options, int type, void *data, int len) -+{ -+ struct conf_option *opt = options[type]; -+ if (opt != NULL) { -+ int copy = opt->length - 2; -+ if (copy > len) { -+ copy = len; -+ } -+ memcpy(data, opt->data, copy); -+ return 0; -+ } -+ return -1; -+} -+int conf_option_set(struct conf_option **options, int type, int len, void *data) -+{ -+ struct conf_option *opt = options[type]; -+ if (len == 0) { -+ options[type] = NULL; -+ free(opt); -+ return 0; -+ } -+ -+ if (opt == NULL) { -+ opt = malloc(len); -+ } else if (opt->length != len) { -+ opt = realloc(opt, len); -+ } -+ if (opt != NULL) { -+ options[type] = opt; -+ opt->type = type; -+ opt->length = len; -+ if (len > 2) { -+ memcpy(opt->data, data, len - 2); -+ } -+ } -+ return (opt == NULL) ? -1 : 0; -+} -+struct conf_option *conf_option_init(struct conf_option_list *optlist) -+{ -+ int header = sizeof(struct lcp_header) + sizeof(uint16_t); -+ optlist->head = malloc(header + default_mru); -+ if (optlist->head) { -+ memset(optlist->head, 0, header + default_mru); -+ optlist->head = (struct conf_option *)(((uint8_t *)optlist->head) + header); -+ optlist->tail = optlist->head; -+ } -+ return optlist->head; -+} -+int conf_option_encode(struct conf_option_list *optlist, int type, int len, void *data) -+{ -+ optlist->tail->type = type; -+ optlist->tail->length = len; -+ if (len > 2) { -+ memcpy(optlist->tail->data, data, len - 2); -+ } -+ optlist->tail = (struct conf_option *)(optlist->tail->data + len - 2); -+ return 0; -+} -+int conf_option_length(struct conf_option_list *optlist) -+{ -+ int len = 0; -+ if (optlist) { -+ len = (uint8_t *)optlist->tail - (uint8_t *)optlist->head; -+ } -+ return len; -+} -+int conf_option_free(struct conf_option_list *optlist) -+{ -+ if (optlist->head != NULL) { -+ int header = sizeof(struct lcp_header) + sizeof(uint16_t); -+ free((uint8_t *)optlist->head - header); -+ optlist->head = optlist->tail = NULL; -+ } -+ return 0; -+} -+int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_list *optlist, int force) -+{ -+ int ret = -1; -+ if (optlist && optlist->head) { -+ uint8_t *head = (uint8_t *)optlist->head; -+ struct lcp_header *header = ((struct lcp_header *)head) - 1; -+ unsigned short *ppp_type = ((unsigned short *)header) - 1; -+ int len = conf_option_length(optlist); -+ int hdrlen = sizeof(struct lcp_header) + sizeof(uint16_t); -+ -+ if (len > 0 || force) { -+ ssize_t pktsize; -+ struct ppp_packet *packet = NULL; -+ -+ *ppp_type = htons(PPP_LCP); -+ header->code = code; -+ header->id = id ? id : lcp_id ++; -+ header->length = htons(len + sizeof(*header)); -+ -+ pktsize = hdrlen + len; -+ packet = malloc(sizeof(*packet) + 6 + pktsize); -+ if (packet == NULL) { -+ goto out; -+ } -+ packet->len = pktsize; -+ memcpy(pkt_data(packet), ppp_type, pktsize); -+ -+ log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, -+ packet->len); -+#if HAVE_USR_SBIN_PPPD -+ log_packet("pppd: ", packet->len, pkt_data(packet)); -+#else -+ log_packet("ppp: ", packet->len, pkt_data(packet)); -+#endif -+ pool_push(&tunnel->pty_to_ssl_pool, packet); -+ } -+ ret = 0; -+ } -+ -+out: -+ return ret; -+} -+int conf_request(struct tunnel *tunnel) -+{ -+ int ret = 0; -+ struct conf_option_list request; -+ uint16_t mru = htons(default_mru); -+ int magic = htonl(magic_seed); -+ conf_option_init(&request); -+ conf_option_encode(&request, LCP_COPT_MRU, 4, &mru); -+ conf_option_encode(&request, LCP_COPT_MAGIC, 6, &magic); -+ ret = lcp_option_send(tunnel, 0, LCP_CONF_REQUEST, &request, 1); -+ conf_option_free(&request); -+ return ret; -+} -+int lcp_packet(struct tunnel *tunnel, void *packet, int len) -+{ -+ struct lcp_header *header = packet; -+ -+ log_debug("packet %s\n", lcp_code_name[header->code]); -+ switch (header->code) { -+ case LCP_CONF_REQUEST: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ struct conf_option_list ack; -+ struct conf_option_list nack; -+ struct conf_option_list reject; -+ conf_option_init(&ack); -+ conf_option_init(&nack); -+ conf_option_init(&reject); -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); -+ switch (co->type) { -+ case LCP_COPT_ACCM: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ conf_option_set(lcp_peer, co->type, co->length, co_data(co)); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ case LCP_COPT_AUTH: -+ switch (ntohs(*(uint16_t *)co_data(co))) { -+ case PPP_CHAP: -+ { -+ struct { -+ uint16_t chap; -+ uint8_t algo; -+ } *payload = (typeof(*payload) *)co_data(co); -+ p += sprintf(p, "CHAP %d", payload->algo); -+ break; -+ } -+ default: -+ p += sprintf(p, "%x", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ } -+ conf_option_set(lcp_peer, co->type, co->length, co_data(co)); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ case LCP_COPT_MAGIC: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ conf_option_set(lcp_peer, co->type, co->length, co_data(co)); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ case LCP_COPT_PFC: -+ case LCP_COPT_ACFC: -+ conf_option_set(lcp_peer, co->type, co->length, co_data(co)); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ default: -+ conf_option_encode(&reject, co->type, co->length, co_data(co)); -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ if (header->code == LCP_CONF_REQUEST) { -+ int ret = -1; -+ ret = lcp_option_send(tunnel, header->id, LCP_CONF_ACK, &ack, 0); -+ if (ret < 0) { -+ log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ ret = lcp_option_send(tunnel, header->id, LCP_CONF_NAK, &nack, 0); -+ if (ret < 0) { -+ log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ ret = lcp_option_send(tunnel, header->id, LCP_CONF_REJECT, &reject, 0); -+ if (ret < 0) { -+ log_error("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ switch (tunnel->tun_state) { -+ case TUN_PPP_LCP: -+ log_debug("\n\nmove to establishment phase\n"); -+ tunnel->tun_state = TUN_PPP_IPCP; -+ default: -+ break; -+ } -+ } -+ conf_option_free(&ack); -+ conf_option_free(&nack); -+ conf_option_free(&reject); -+ -+ break; -+ } -+ case LCP_CONF_ACK: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ conf_option_set(lcp_self, co->type, co->length, co_data(co)); -+ p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); -+ switch (co->type) { -+ case LCP_COPT_MRU: -+ p += sprintf(p, "%d", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_ACCM: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_AUTH: -+ p += sprintf(p, "%x", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_MAGIC: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_PFC: -+ case LCP_COPT_ACFC: -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ -+ if (tunnel->tun_state == TUN_PPP_LCP) { -+ log_debug("\n\nentering authenticate phase\n"); -+ tunnel->tun_state = TUN_PPP_IPCP; -+ } -+ break; -+ } -+ case LCP_CONF_NAK: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ conf_option_set(lcp_self, co->type, 0, NULL); -+ p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); -+ switch (co->type) { -+ case LCP_COPT_MRU: -+ p += sprintf(p, "%d", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_ACCM: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_AUTH: -+ p += sprintf(p, "%x", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_MAGIC: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_PFC: -+ case LCP_COPT_ACFC: -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ break; -+ } -+ case LCP_CONF_REJECT: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ conf_option_set(lcp_self, co->type, 0, NULL); -+ p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); -+ switch (co->type) { -+ case LCP_COPT_MRU: -+ p += sprintf(p, "%d", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_ACCM: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_AUTH: -+ p += sprintf(p, "%x", ntohs(*(uint16_t *)co_data(co))); -+ break; -+ case LCP_COPT_MAGIC: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case LCP_COPT_PFC: -+ case LCP_COPT_ACFC: -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ break; -+ } -+ case LCP_TERM_REQUEST: -+ break; -+ case LCP_TERM_ACK: -+ break; -+ case LCP_CODE_REJECT: -+ break; -+ case LCP_PROT_REJECT: -+ break; -+ case LCP_ECHO_REQUEST: -+ break; -+ case LCP_ECHO_REPLY: -+ break; -+ case LCP_DISCARD: -+ break; -+ default: -+ /* ignore */ -+ break; -+ } -+ -+ return 0; -+} -+ -+struct ipcp_header { -+ uint8_t code; -+#define IPCP_CONF_REQUEST 1 -+#define IPCP_CONF_ACK 2 -+#define IPCP_CONF_NAK 3 -+#define IPCP_CONF_REJECT 4 -+#define IPCP_TERM_REQUEST 5 -+#define IPCP_TERM_ACK 6 -+#define IPCP_CODE_REJECT 7 -+ uint8_t id; -+ uint16_t length; -+}; -+ -+#define IPCP_COPT_ADDRESSES 1 -+#define IPCP_COPT_COMPRESS 2 -+#define IPCP_COPT_ADDRESS 3 -+#define IPCP_COPT_PRIMARY_DNS 129 -+#define IPCP_COPT_SECONDARY_DNS 131 -+const char *ipcp_valid_options[256] = { -+ [0] = "", -+ [1] = "IPCP Option addresses", -+ [2] = "IPCP Option compress", -+ [3] = "IPCP Option address", -+ [129] = "IPCP Option primary dns", -+ [131] = "IPCP Option secondary dns", -+}; -+ -+const char *ipcp_code_name[] = { -+ "", -+ "IPCP Configure-Request", -+ "IPCP Configure-Ack", -+ "IPCP Configure-Nak", -+ "IPCP Configure-Reject", -+ "IPCP Terminate-Request", -+ "IPCP Terminate-Ack", -+ "IPCP Code-Reject", -+}; -+ -+int nroutes = 0; -+char **routes = NULL; -+ -+uint32_t ip_address = 0; -+uint32_t peer_address = 0; -+uint32_t primary_dns = 0; -+uint32_t secondary_dns = 0; -+ -+int ipcp_add_route(struct tunnel *tunnel, uint32_t dst, uint32_t mask, uint32_t gw) -+{ -+ int ret = 0; -+ struct rtentry rt; -+ struct sockaddr_in *sin = NULL; -+ -+ memset(&rt, 0, sizeof(rt)); -+ rt.rt_dev = tunnel->tun_iface; -+ rt.rt_flags = RTF_GATEWAY; -+ -+ sin = (struct sockaddr_in *)&rt.rt_dst; -+ sin->sin_family = AF_INET; -+ sin->sin_port = 0; -+ sin->sin_addr.s_addr = dst & htonl(mask); -+ -+ sin = (struct sockaddr_in *)&rt.rt_gateway; -+ sin->sin_family = AF_INET; -+ sin->sin_port = 0; -+ sin->sin_addr.s_addr = gw; -+ -+ sin = (struct sockaddr_in *)&rt.rt_genmask; -+ sin->sin_family = AF_INET; -+ sin->sin_port = 0; -+ sin->sin_addr.s_addr = htonl(mask); -+ -+ int sd = socket(AF_INET, SOCK_DGRAM, 0); -+ ret = ioctl(sd, SIOCADDRT, &rt); -+ if (ret == 0) { -+ log_debug("route add success\n"); -+ } else { -+ log_error("route add failed %d: %s\n", errno, strerror(errno)); -+ } -+ close(sd); -+ -+ return ret; -+} -+int ipcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_list *optlist, int force) -+{ -+ int ret = -1; -+ if (optlist && optlist->head) { -+ uint8_t *packet = (uint8_t *)optlist->head; -+ struct ipcp_header *header = ((struct ipcp_header *)packet) - 1; -+ unsigned short *ppp_type = ((unsigned short *)header) - 1; -+ int len = conf_option_length(optlist); -+ int hdrlen = sizeof(struct ipcp_header) + sizeof(uint16_t); -+ -+ if (len > 0 || force) { -+ ssize_t pktsize; -+ struct ppp_packet *packet = NULL; -+ -+ *ppp_type = htons(PPP_IPCP); -+ header->code = code; -+ header->id = id ? id : lcp_id ++; -+ header->length = htons(len + sizeof(*header)); -+ log_debug("send ipcp %d\n", len); -+ -+ pktsize = hdrlen + len; -+ packet = malloc(sizeof(*packet) + 6 + pktsize); -+ if (packet == NULL) { -+ goto out; -+ } -+ packet->len = pktsize; -+ memcpy(pkt_data(packet), ppp_type, pktsize); -+ -+ log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, -+ packet->len); -+#if HAVE_USR_SBIN_PPPD -+ log_packet("pppd: ", packet->len, pkt_data(packet)); -+#else -+ log_packet("ppp: ", packet->len, pkt_data(packet)); -+#endif -+ pool_push(&tunnel->pty_to_ssl_pool, packet); -+ } -+ ret = 0; -+ } -+ -+out: -+ return ret; -+} -+int ipcp_packet(struct tunnel *tunnel, void *packet, int len) -+{ -+ int ret = 0; -+ struct ipcp_header *header = packet; -+ -+ log_debug("packet %s\n", ipcp_code_name[header->code]); -+ switch (header->code) { -+ case IPCP_CONF_REQUEST: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ struct conf_option_list ack; -+ struct conf_option_list nack; -+ struct conf_option_list reject; -+ struct conf_option_list request; -+ conf_option_init(&ack); -+ conf_option_init(&nack); -+ conf_option_init(&reject); -+ conf_option_init(&request); -+ olen -= sizeof(struct ipcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); -+ switch (co->type) { -+ case IPCP_COPT_ADDRESSES: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ case IPCP_COPT_COMPRESS: -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ break; -+ case IPCP_COPT_ADDRESS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ conf_option_encode(&ack, co->type, co->length, co_data(co)); -+ peer_address = *(uint32_t *)co_data(co); -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ if (header->code == IPCP_CONF_REQUEST) { -+ int ret = -1; -+ ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_ACK, &ack, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_NAK, &nack, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ ret = ipcp_option_send(tunnel, header->id, IPCP_CONF_REJECT, &reject, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ -+ do { -+ uint32_t compress = htonl(0x002d0f01); -+ conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); -+ conf_option_encode(&request, IPCP_COPT_COMPRESS, 6, &compress); -+ // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); -+ // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); -+ ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ } while (0); -+ } -+ conf_option_free(&ack); -+ conf_option_free(&nack); -+ conf_option_free(&reject); -+ conf_option_free(&request); -+ -+ break; -+ } -+ case IPCP_CONF_ACK: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); -+ switch (co->type) { -+ case IPCP_COPT_ADDRESSES: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case IPCP_COPT_COMPRESS: -+ break; -+ case IPCP_COPT_ADDRESS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ ip_address = *(uint32_t *)co_data(co); -+ break; -+ case IPCP_COPT_PRIMARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ primary_dns = *(uint32_t *)co_data(co); -+ break; -+ case IPCP_COPT_SECONDARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ secondary_dns = *(uint32_t *)co_data(co); -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ -+ int tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr); -+ tun_ifup(tunnel->tun_iface, ip_address, peer_address); -+ ipv4_set_tunnel_routes(tunnel); -+ break; -+ } -+ case IPCP_CONF_NAK: -+ { -+ int send_request = 0; -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); -+ switch (co->type) { -+ case IPCP_COPT_ADDRESSES: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case IPCP_COPT_COMPRESS: -+ break; -+ case IPCP_COPT_ADDRESS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ if (ip_address != *(uint32_t *)co_data(co)) { -+ ip_address = *(uint32_t *)co_data(co); -+ send_request = 1; -+ } -+ break; -+ case IPCP_COPT_PRIMARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ if (primary_dns != *(uint32_t *)co_data(co)) { -+ primary_dns = *(uint32_t *)co_data(co); -+ send_request = 1; -+ } -+ break; -+ case IPCP_COPT_SECONDARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ if (secondary_dns != *(uint32_t *)co_data(co)) { -+ secondary_dns = *(uint32_t *)co_data(co); -+ send_request = 1; -+ } -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ -+ if (send_request) { -+ struct conf_option_list request; -+ conf_option_init(&request); -+ conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); -+ // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); -+ // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); -+ ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ conf_option_free(&request); -+ } -+ break; -+ } -+ case IPCP_CONF_REJECT: -+ { -+ int olen = ntohs(header->length); -+ struct conf_option *co = NULL; -+ olen -= sizeof(struct lcp_header); -+ co = (struct conf_option *)(header + 1); -+ while (olen > 0) { -+ char buff[128]; -+ char *p = buff; -+ p += sprintf(p, "option %s: ", ipcp_valid_options[co->type]); -+ switch (co->type) { -+ case IPCP_COPT_ADDRESSES: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case IPCP_COPT_COMPRESS: -+ break; -+ case IPCP_COPT_ADDRESS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case IPCP_COPT_PRIMARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ case IPCP_COPT_SECONDARY_DNS: -+ p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); -+ break; -+ default: -+ break; -+ } -+ log_debug("%s\n", buff); -+ olen -= co->length; -+ co = (struct conf_option *)((uint8_t *)co + co->length); -+ } -+ -+ do { -+ struct conf_option_list request; -+ conf_option_init(&request); -+ conf_option_encode(&request, IPCP_COPT_ADDRESS, 6, &ip_address); -+ // conf_option_encode(&request, IPCP_COPT_PRIMARY_DNS, 6, &primary_dns); -+ // conf_option_encode(&request, IPCP_COPT_SECONDARY_DNS, 6, &secondary_dns); -+ ret = ipcp_option_send(tunnel, 0, IPCP_CONF_REQUEST, &request, 0); -+ if (ret < 0) { -+ log_debug("send conf_ack failed %d: %s\n", errno, strerror(errno)); -+ exit(1); -+ } -+ } while (0); -+ break; -+ } -+ case IPCP_TERM_REQUEST: -+ break; -+ case IPCP_TERM_ACK: -+ break; -+ case IPCP_CODE_REJECT: -+ break; -+ default: -+ /* ignore */ -+ break; -+ } -+ -+ return 0; -+} -+ -+ - /* - * Thread to read bytes from the pppd pty, convert them to ppp packets and add - * them to the 'pty_to_ssl' pool. -@@ -192,6 +1032,17 @@ static void *pppd_read(void *arg) - - log_debug("%s thread\n", __func__); - -+ if (tunnel->use_tun) { -+ switch (tunnel->tun_state) { -+ case TUN_PPP_LCP: -+ conf_request(tunnel); -+ break; -+ case TUN_PPP_IPCP: -+ case TUN_PPP_SESSION: -+ break; -+ } -+ } -+ - // Wait for pppd to be ready - off_w = 0; - while (1) { -@@ -218,6 +1069,31 @@ static void *pppd_read(void *arg) - SEM_POST(&sem_pppd_ready); - first_time = 0; - } -+ if (tunnel->use_tun) { -+ ssize_t pktsize; -+ struct ppp_packet *packet = NULL; -+ -+ pktsize = n + 2; -+ packet = malloc(sizeof(*packet) + 6 + pktsize); -+ if (packet == NULL) { -+ goto exit; -+ } -+ packet->len = pktsize; -+ pkt_data(packet)[0] = 0x00; -+ pkt_data(packet)[1] = 0x21; -+ memcpy(pkt_data(packet) + 2, buf, n); -+ -+ log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, -+ packet->len); -+#if HAVE_USR_SBIN_PPPD -+ log_packet("pppd: ", packet->len, pkt_data(packet)); -+#else -+ log_packet("ppp: ", packet->len, pkt_data(packet)); -+#endif -+ pool_push(&tunnel->pty_to_ssl_pool, packet); -+ continue; -+ } -+ - off_w += n; - - // We have data in the buffer, there may be zero, one or many -@@ -308,17 +1184,44 @@ static void *pppd_write(void *arg) - // This waits until a packet has arrived from the gateway - packet = pool_pop(&tunnel->ssl_to_pty_pool); - -- hdlc_bufsize = estimated_encoded_size(packet->len); -- hdlc_buffer = malloc(hdlc_bufsize); -- if (hdlc_buffer == NULL) { -- log_error("malloc: %s\n", strerror(errno)); -- break; -- } -- len = hdlc_encode(hdlc_buffer, hdlc_bufsize, -- pkt_data(packet), packet->len); -- if (len < 0) { -- log_error("Failed to encode PPP packet into HDLC frame.\n"); -- goto err_free_buf; -+ if (tunnel->use_tun) { -+ void *pkt_type = pkt_data(packet); -+ -+ hdlc_bufsize = len = packet->len; -+ switch (ntohs(*(uint16_t *)pkt_type)) { -+ case PPP_LCP: -+ lcp_packet(tunnel, pkt_data(packet) + 2, len - 2); -+ continue; -+ case PPP_IPCP: -+ ipcp_packet(tunnel, pkt_data(packet) + 2, len - 2); -+ continue; -+ case PPP_IP: -+ case PPP_IPV6: -+ break; -+ default: -+ goto out_free_packet; -+ } -+ -+ hdlc_buffer = malloc(packet->len); -+ if (hdlc_buffer == NULL) { -+ log_error("malloc: %s\n", strerror(errno)); -+ break; -+ } -+ -+ memcpy(hdlc_buffer, pkt_data(packet) + 2, packet->len - 2); -+ } else { -+ hdlc_bufsize = estimated_encoded_size(packet->len); -+ hdlc_buffer = malloc(hdlc_bufsize); -+ if (hdlc_buffer == NULL) { -+ log_error("malloc: %s\n", strerror(errno)); -+ break; -+ } -+ len = hdlc_encode(hdlc_buffer, hdlc_bufsize, -+ pkt_data(packet), packet->len); -+ if (len < 0) { -+ log_error("Failed to encode PPP packet into HDLC frame.\n"); -+ goto err_free_buf; -+ } - } - - written = 0; -@@ -349,6 +1252,7 @@ static void *pppd_write(void *arg) - } - - free(hdlc_buffer); -+out_free_packet: - free(packet); - continue; - err_free_buf: -@@ -497,6 +1401,11 @@ static void *ssl_read(void *arg) - char line[ARRAY_SIZE("[xxx.xxx.xxx.xxx], ns [xxx.xxx.xxx.xxx, xxx.xxx.xxx.xxx], ns_suffix []") + MAX_DOMAIN_LENGTH]; - - set_tunnel_ips(tunnel, packet); -+ -+ if (tunnel->use_tun) { -+ int tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr); -+ tun_ifup(tunnel->tun_iface, tunnel->ipv4.ip_addr.s_addr, 0); -+ } - strcpy(line, "["); - strncat(line, inet_ntoa(tunnel->ipv4.ip_addr), 15); - strcat(line, "], ns ["); -diff --git a/src/ipv4.h b/src/ipv4.h -index fc1406b..7fd492e 100644 ---- a/src/ipv4.h -+++ b/src/ipv4.h -@@ -57,6 +57,7 @@ struct rtentry { - - struct ipv4_config { - struct in_addr ip_addr; -+ struct in_addr peer_addr; - - struct in_addr ns1_addr; - struct in_addr ns2_addr; -diff --git a/src/tunnel.c b/src/tunnel.c -index 32a5e39..a0e7bd7 100644 ---- a/src/tunnel.c -+++ b/src/tunnel.c -@@ -64,6 +64,7 @@ - #include - #include - #include -+#include - - - struct ofv_varr { -@@ -154,7 +155,127 @@ static int on_ppp_if_down(struct tunnel *tunnel) - return 0; - } - --static int pppd_run(struct tunnel *tunnel) -+#define TUN_PATH "/dev/net/tun" -+ -+#define TUN_ASSERT(cond, fmt, ...) do { \ -+ if (! (cond)) { \ -+ log_info(fmt "\n", ##__VA_ARGS__); \ -+ exit(1); \ -+ } \ -+ } while (0) -+ -+static int -+tun_open(struct ifreq *ifr) -+{ -+ int ret = 0; -+ int fd = -1; -+ -+ fd = open(TUN_PATH, O_RDWR, 0); -+ if (fd >= 0) { -+ ret = ioctl(fd, TUNSETIFF, (unsigned long)ifr); -+ if (ret < 0) { -+ log_error("ioctl: %s\n", strerror(errno)); -+ close(fd); -+ return ret; -+ } -+ log_info("interface <%s> created\n", ifr->ifr_name); -+ } else { -+ log_error("fcntl: %s\n", strerror(errno)); -+ } -+ -+ return fd; -+} -+ -+static int -+__attribute__((unused)) -+tun_close(int fd) -+{ -+ return close(fd); -+} -+ -+int -+tun_ifup(char *ifname, uint32_t ip_addr, uint32_t peer_addr) -+{ -+ struct ifreq ifreq = { -+ .ifr_flags = IFF_UP, -+ }; -+ struct sockaddr_in *sin = NULL; -+ int fd = -1; -+ int ret = -1; -+ -+ fd = socket(AF_INET, SOCK_DGRAM, 0); -+ if (fd < 0) { -+ return fd; -+ } -+ strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); -+ ret = ioctl(fd, SIOCGIFFLAGS, (unsigned long)&ifreq); -+ TUN_ASSERT(ret == 0, "ioctl get ifflags", 0); -+ if (ret == 0) { -+ ifreq.ifr_flags |= IFF_UP; -+ ret = ioctl(fd, SIOCSIFFLAGS, (unsigned long)&ifreq); -+ TUN_ASSERT(ret == 0, "ioctl set ifflags", 0); -+ } -+ -+ if (ip_addr != 0) { -+ sin = (struct sockaddr_in *)&ifreq.ifr_addr; -+ sin->sin_family = AF_INET; -+ sin->sin_port = 0; -+ sin->sin_addr.s_addr = ip_addr; -+ ret = ioctl(fd, SIOCSIFADDR, (unsigned long)&ifreq); -+ TUN_ASSERT(ret == 0, "ioctl set ifaddr err: %s", strerror(errno)); -+ log_info("setup <%s> ip addr to %s\n", ifname, inet_ntoa(sin->sin_addr)); -+ } -+ -+ if (peer_addr != 0) { -+ sin = (struct sockaddr_in *)&ifreq.ifr_addr; -+ sin->sin_family = AF_INET; -+ sin->sin_port = 0; -+ sin->sin_addr.s_addr = peer_addr; -+ ret = ioctl(fd, SIOCSIFDSTADDR, (unsigned long)&ifreq); -+ TUN_ASSERT(ret == 0, "ioctl set dst ifaddr", 0); -+ } -+ -+ close(fd); -+ -+ return ret; -+} -+ -+static int -+__attribute__((unused)) -+tun_setup(struct tunnel *tunnel) -+{ -+ int flags = 0; -+ int tun_fd = -1; -+ struct ifreq ifreq = { -+ .ifr_flags = IFF_TUN | IFF_NO_PI, -+ }; -+ -+ tun_fd = tun_open(&ifreq); -+ if (tun_fd < 0) { -+ log_error("tun_open failed: %s\n", strerror(errno)); -+ return 1; -+ } -+ -+ flags = fcntl(tun_fd, F_GETFL, 0); -+ if (flags == -1) -+ flags = 0; -+ if (fcntl(tun_fd, F_SETFL, flags | O_NONBLOCK) == -1) { -+ log_error("fcntl failed: %s\n", strerror(errno)); -+ return 1; -+ } -+ -+ strcpy(tunnel->tun_iface, ifreq.ifr_name); -+ tun_ifup(tunnel->tun_iface, 0, 0); -+ -+ tunnel->pppd_pid = -1; -+ tunnel->pppd_pty = tun_fd; -+ -+ return 0; -+} -+ -+static int -+__attribute__((unused)) -+pppd_run(struct tunnel *tunnel) - { - pid_t pid; - int amaster; -@@ -232,7 +353,7 @@ static int pppd_run(struct tunnel *tunnel) - static const char *const v[] = { - ppp_path, - "115200", // speed -- ":192.0.2.1", // : -+ ":10.168.1.71", // : - "noipdefault", - "noaccomp", - "noauth", -@@ -464,6 +585,7 @@ int ppp_interface_is_up(struct tunnel *tunnel) - && strstr(ifa->ifa_name, tunnel->config->pppd_ifname) - != NULL) - || strstr(ifa->ifa_name, "ppp") != NULL -+ || strstr(ifa->ifa_name, "tun") != NULL - #endif - #if HAVE_USR_SBIN_PPP - strstr(ifa->ifa_name, "tun") != NULL -@@ -1128,7 +1250,8 @@ int run_tunnel(struct vpn_config *config) - goto err_tunnel; - - // Step 3: get configuration -- log_debug("Retrieving configuration\n"); -+ tunnel.use_tun = 1; -+ log_info("Retrieving configuration\n"); - ret = auth_get_config(&tunnel); - if (ret != 1) { - log_error("Could not get VPN configuration (%s).\n", -@@ -1138,13 +1261,17 @@ int run_tunnel(struct vpn_config *config) - } - - // Step 4: run a pppd process -- log_debug("Establishing the tunnel\n"); -- ret = pppd_run(&tunnel); -+ log_info("Establishing the tunnel\n"); -+ if (tunnel.use_tun) { -+ ret = tun_setup(&tunnel); -+ } else { -+ ret = pppd_run(&tunnel); -+ } - if (ret) - goto err_tunnel; - - // Step 5: ask gateway to start tunneling -- log_debug("Switch to tunneling mode\n"); -+ log_info("Switch to tunneling mode\n"); - ret = http_send(&tunnel, - "GET /remote/sslvpn-tunnel HTTP/1.1\r\n" - "Host: sslvpn\r\n" -@@ -1160,7 +1287,7 @@ int run_tunnel(struct vpn_config *config) - ret = 0; - - // Step 6: perform io between pppd and the gateway, while tunnel is up -- log_debug("Starting IO through the tunnel\n"); -+ log_info("Starting IO through the tunnel\n"); - io_loop(&tunnel); - - log_debug("disconnecting\n"); -diff --git a/src/tunnel.h b/src/tunnel.h -index 31eb045..af4d64e 100644 ---- a/src/tunnel.h -+++ b/src/tunnel.h -@@ -54,10 +54,17 @@ enum tunnel_state { - STATE_DISCONNECTING - }; - -+enum tun_ppp_state { -+ TUN_PPP_LCP, -+ TUN_PPP_IPCP, -+ TUN_PPP_SESSION, -+}; -+ - struct tunnel { - struct vpn_config *config; - - enum tunnel_state state; -+ enum tun_ppp_state tun_state; - char cookie[COOKIE_SIZE + 1]; - - struct ppp_packet_pool ssl_to_pty_pool; -@@ -65,6 +72,8 @@ struct tunnel { - - pid_t pppd_pid; - pid_t pppd_pty; -+ int use_tun; -+ char tun_iface[ROUTE_IFACE_LEN]; - char ppp_iface[ROUTE_IFACE_LEN]; - - int ssl_socket;