From 335dbd863825dd55ed19b1e6bbadc597bc28083f Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Fri, 18 Dec 2020 15:59:30 +0100 Subject: [PATCH] Add patch provided by @rain2fog in #801 --- tun.patch | 1241 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1241 insertions(+) create mode 100644 tun.patch diff --git a/tun.patch b/tun.patch new file mode 100644 index 00000000..77052302 --- /dev/null +++ b/tun.patch @@ -0,0 +1,1241 @@ +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;