From 0166e3285a146663bb26d137b8f783f397573875 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 1/3] 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; From 8b428c474847356e3a1431485e5cedd3dc04df30 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Tue, 8 Dec 2020 10:47:18 +0100 Subject: [PATCH 2/3] Apply patch provided by @rain2fog in #801 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pppd → tun interface + embedded PPP code --- src/http.c | 5 + src/io.c | 931 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/ipv4.h | 1 + src/tunnel.c | 143 +++++++- src/tunnel.h | 9 + 5 files changed, 1071 insertions(+), 18 deletions(-) diff --git a/src/http.c b/src/http.c index 133ffc65..6526953c 100644 --- a/src/http.c +++ b/src/http.c @@ -834,6 +834,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 388a005e..b1b43eb1 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 fc1406b6..7fd492ec 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 6bc36971..adbb24e8 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -60,6 +60,9 @@ #if HAVE_LIBUTIL_H #include #endif +#if HAVE_LINUX_IF_TUN_H +#include +#endif #include #include @@ -155,7 +158,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; @@ -514,7 +637,8 @@ int ppp_interface_is_up(struct tunnel *tunnel) #if HAVE_USR_SBIN_PPPD ((tunnel->config->pppd_ifname && strstr(ifa->ifa_name, tunnel->config->pppd_ifname) != NULL) || - strstr(ifa->ifa_name, "ppp") != 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 && @@ -1300,7 +1424,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", @@ -1310,13 +1435,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" @@ -1330,7 +1459,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_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 31eb045f..af4d64e2 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; From 6336f54a79bc6787a17730124a0e05259c6fef61 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 10 Dec 2020 14:00:25 +0100 Subject: [PATCH 3/3] Clean up after applying patch --- configure.ac | 2 + src/io.c | 386 +++++++++------- src/tunnel.c | 81 ++-- src/tunnel.h | 2 + tun.patch | 1241 -------------------------------------------------- 5 files changed, 260 insertions(+), 1452 deletions(-) delete mode 100644 tun.patch diff --git a/configure.ac b/configure.ac index 7bccdfe4..140f9b3c 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..7c64a71f 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] = { [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,19 +267,9 @@ 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, }, -}; -*/ + +static int magic_seed = 0x64696E67; + struct conf_option *lcp_self[256] = { NULL, }; @@ -282,111 +277,119 @@ struct conf_option *lcp_peer[256] = { NULL, }; -static int lcp_id = 0; +static int lcp_id; + 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) { 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) { 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) { 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) + +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) { 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 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 +403,14 @@ int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_ 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); @@ -413,38 +418,39 @@ int conf_request(struct tunnel *tunnel) 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; + 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 +459,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 +492,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 +528,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 +568,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 +603,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 +698,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 +730,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 +790,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 +798,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 +841,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 +891,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 +928,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 +979,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 +1033,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 +1121,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 +1266,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 +1451,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;