From 2c60af63042a98d9d3ee540d8e401508167c775d 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 01/12] 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 5856dc2697e21cf50efa271983bda34260f513e7 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 02/12] 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 a2e342f9..4817490d 100644 --- a/src/http.c +++ b/src/http.c @@ -897,6 +897,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 ac162126..21f61469 100644 --- a/src/io.c +++ b/src/io.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -178,6 +179,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. @@ -195,6 +1035,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) { @@ -221,6 +1072,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 @@ -311,17 +1187,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; @@ -352,6 +1255,7 @@ static void *pppd_write(void *arg) } free(hdlc_buffer); +out_free_packet: free(packet); continue; err_free_buf: @@ -500,6 +1404,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 d8337b01..faab6b5f 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 48ec1960..1d07e0f1 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -64,6 +64,9 @@ #if HAVE_LIBUTIL_H #include #endif +#if HAVE_LINUX_IF_TUN_H +#include +#endif #include #include @@ -167,7 +170,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; @@ -587,7 +710,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 && @@ -1405,7 +1529,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", @@ -1415,13 +1540,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" @@ -1435,7 +1564,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 e64af816..af11bbc9 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 b6b5794cdc7be697a4e1cc175ffa4f4aad6defd5 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 03/12] Clean up after applying patch --- configure.ac | 2 + src/io.c | 415 +++++++++-------- src/tunnel.c | 81 ++-- src/tunnel.h | 2 + tun.patch | 1241 -------------------------------------------------- 5 files changed, 276 insertions(+), 1465 deletions(-) delete mode 100644 tun.patch diff --git a/configure.ac b/configure.ac index 0ef0b6f0..eab293b4 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,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 21f61469..76468c9e 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 @@ -241,18 +243,21 @@ struct lcp_option_conf { int len; const char *name; }; -int default_mru = 1534; -const struct lcp_option_conf lcp_valid_options[256] = { + +static const int default_mru = 1534; + +static const struct lcp_option_conf lcp_valid_options[256] = { [0] = { 0, 0, "RESERVED", }, - [1] = {LCP_OF_VALID, 4, "MRU", }, /* RFC 1661 */ - [2] = {LCP_OF_VALID, 6, "Async-Control-Character-Map",}, /* RFC 1172 */ + [1] = {LCP_OF_VALID, 4, "MRU", }, /* RFC 1661 */ + [2] = {LCP_OF_VALID, 6, "Async-Control-Character-Map",}, /* RFC 1172 */ [3] = {LCP_OF_VALID, 4, "Auth-Protocol", }, [4] = {LCP_OF_VALID, 4, "Quality-Protocol", }, [5] = {LCP_OF_VALID, 6, "Magic-Num", }, - [6] = {LCP_OF_VALID, 6, "Link-Quality-Monitoring", }, /* RFC 1172 */ + [6] = {LCP_OF_VALID, 6, "Link-Quality-Monitoring", }, /* RFC 1172 */ [7] = {LCP_OF_VALID, 2, "Protocol-Field-Comp", }, - [8] = {LCP_OF_VALID, 2, "Addr&Ctrl-Field-Comp", }, + [8] = {LCP_OF_VALID, 2, "Addr&Ctrl-Field-Comp", }, }; + struct lcp_option_value { int flag; #define LCP_OF_DEFAULT 0x20000 @@ -265,131 +270,132 @@ struct lcp_option_value { } u; void *extra; }; -int magic_seed = 0x64696E67; -/* -struct conf_option lcp_default[256] = { - [1] = {LCP_OF_DEFAULT, {.number = 1500}, }, - [2] = {LCP_OF_DEFAULT, {.number = 0xFFFFFFFF}, }, - [3] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [4] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [5] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [6] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [7] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, - [8] = {LCP_OF_DEFAULT | LCP_OF_DISABLE, }, -}; -*/ -struct conf_option *lcp_self[256] = { + +static const uint32_t magic_seed = 0x64696E67; /* ['d', 'i', 'n', 'g'] */ + +static struct conf_option *lcp_self[256] = { NULL, }; -struct conf_option *lcp_peer[256] = { +static struct conf_option *lcp_peer[256] = { NULL, }; -static int lcp_id = 0; -int conf_option_get(struct conf_option **options, int type, void *data, int len) +static int lcp_id; + +#ifdef OPENFORTIVPN_DEFINE_DEAD_CODE +static int conf_option_get(struct conf_option **options, int type, void *data, int len) { struct conf_option *opt = options[type]; + if (opt != NULL) { int copy = opt->length - 2; - if (copy > len) { + + if (copy > len) copy = len; - } memcpy(data, opt->data, copy); return 0; } return -1; } -int conf_option_set(struct conf_option **options, int type, int len, void *data) +#endif + +static int conf_option_set(struct conf_option **options, int type, int len, void *data) { struct conf_option *opt = options[type]; + if (len == 0) { options[type] = NULL; free(opt); return 0; } - if (opt == NULL) { + if (opt == NULL) opt = malloc(len); - } else if (opt->length != len) { + else if (opt->length != len) opt = realloc(opt, len); - } if (opt != NULL) { options[type] = opt; opt->type = type; opt->length = len; - if (len > 2) { + if (len > 2) memcpy(opt->data, data, len - 2); - } } return (opt == NULL) ? -1 : 0; } -struct conf_option *conf_option_init(struct conf_option_list *optlist) + +static struct conf_option *conf_option_init(struct conf_option_list *optlist) { int header = sizeof(struct lcp_header) + sizeof(uint16_t); + optlist->head = malloc(header + default_mru); if (optlist->head) { memset(optlist->head, 0, header + default_mru); - optlist->head = (struct conf_option *)(((uint8_t *)optlist->head) + header); + optlist->head = (struct conf_option *)(((uint8_t *)optlist->head) + + header); optlist->tail = optlist->head; } return optlist->head; } -int conf_option_encode(struct conf_option_list *optlist, int type, int len, void *data) + +static int conf_option_encode(struct conf_option_list *optlist, int type, int len, + void *data) { optlist->tail->type = type; optlist->tail->length = len; - if (len > 2) { + if (len > 2) memcpy(optlist->tail->data, data, len - 2); - } optlist->tail = (struct conf_option *)(optlist->tail->data + len - 2); return 0; } -int conf_option_length(struct conf_option_list *optlist) + +static int conf_option_length(const struct conf_option_list *optlist) { - int len = 0; - if (optlist) { - len = (uint8_t *)optlist->tail - (uint8_t *)optlist->head; - } - return len; + if (optlist) + return (uint8_t *)optlist->tail - (uint8_t *)optlist->head; + else + return 0; } -int conf_option_free(struct conf_option_list *optlist) + +static int conf_option_free(struct conf_option_list *optlist) { if (optlist->head != NULL) { int header = sizeof(struct lcp_header) + sizeof(uint16_t); + free((uint8_t *)optlist->head - header); optlist->head = optlist->tail = NULL; } return 0; } -int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_list *optlist, int force) + +static int lcp_option_send(struct tunnel *tunnel, int id, + int code, struct conf_option_list *optlist, int force) { int ret = -1; + if (optlist && optlist->head) { uint8_t *head = (uint8_t *)optlist->head; struct lcp_header *header = ((struct lcp_header *)head) - 1; unsigned short *ppp_type = ((unsigned short *)header) - 1; - int len = conf_option_length(optlist); - int hdrlen = sizeof(struct lcp_header) + sizeof(uint16_t); + const size_t hdrlen = sizeof(struct lcp_header) + sizeof(uint16_t); + const int len = conf_option_length(optlist); if (len > 0 || force) { - ssize_t pktsize; + const ssize_t pktsize = hdrlen + len; struct ppp_packet *packet = NULL; *ppp_type = htons(PPP_LCP); header->code = code; - header->id = id ? id : lcp_id ++; + header->id = id ? id : lcp_id++; header->length = htons(len + sizeof(*header)); - pktsize = hdrlen + len; packet = malloc(sizeof(*packet) + 6 + pktsize); - if (packet == NULL) { + if (packet == NULL) goto out; - } packet->len = pktsize; memcpy(pkt_data(packet), ppp_type, pktsize); log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON, - packet->len); + packet->len); #if HAVE_USR_SBIN_PPPD log_packet("pppd: ", packet->len, pkt_data(packet)); #else @@ -403,12 +409,14 @@ int lcp_option_send(struct tunnel *tunnel, int id, int code, struct conf_option_ out: return ret; } -int conf_request(struct tunnel *tunnel) + +static int conf_request(struct tunnel *tunnel) { int ret = 0; struct conf_option_list request; - uint16_t mru = htons(default_mru); - int magic = htonl(magic_seed); + uint16_t mru = htons((uint16_t)default_mru); + uint32_t magic = htonl(magic_seed); + conf_option_init(&request); conf_option_encode(&request, LCP_COPT_MRU, 4, &mru); conf_option_encode(&request, LCP_COPT_MAGIC, 6, &magic); @@ -416,38 +424,39 @@ int conf_request(struct tunnel *tunnel) conf_option_free(&request); return ret; } -int lcp_packet(struct tunnel *tunnel, void *packet, int len) + +static int lcp_packet(struct tunnel *tunnel, void *packet, int len) { struct lcp_header *header = packet; log_debug("packet %s\n", lcp_code_name[header->code]); switch (header->code) { - case LCP_CONF_REQUEST: - { - int olen = ntohs(header->length); - struct conf_option *co = NULL; + case LCP_CONF_REQUEST: { + struct conf_option *co = (struct conf_option *)(header + 1); + int olen = ntohs(header->length) - sizeof(struct lcp_header); struct conf_option_list ack; struct conf_option_list nack; struct conf_option_list reject; + conf_option_init(&ack); conf_option_init(&nack); conf_option_init(&reject); - olen -= sizeof(struct lcp_header); - co = (struct conf_option *)(header + 1); while (olen > 0) { char buff[128]; char *p = buff; + p += sprintf(p, "option %s: ", lcp_valid_options[co->type].name); switch (co->type) { case LCP_COPT_ACCM: p += sprintf(p, "%x", ntohl(*(uint32_t *)co_data(co))); - conf_option_set(lcp_peer, co->type, co->length, co_data(co)); - conf_option_encode(&ack, co->type, co->length, co_data(co)); + conf_option_set(lcp_peer, co->type, + co->length, co_data(co)); + conf_option_encode(&ack, co->type, + co->length, co_data(co)); break; case LCP_COPT_AUTH: switch (ntohs(*(uint16_t *)co_data(co))) { - case PPP_CHAP: - { + case PPP_CHAP: { struct { uint16_t chap; uint8_t algo; @@ -456,24 +465,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); @@ -481,21 +498,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: @@ -511,15 +534,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) { @@ -552,15 +574,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) { @@ -588,15 +609,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) { @@ -684,17 +704,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; @@ -717,47 +736,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 @@ -771,6 +796,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; @@ -778,35 +804,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: @@ -818,32 +847,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); } @@ -854,15 +897,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: @@ -890,21 +934,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: @@ -943,28 +985,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: @@ -991,14 +1039,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; @@ -1073,21 +1127,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 @@ -1220,7 +1272,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; @@ -1405,10 +1457,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 1d07e0f1..c5855a2a 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -170,24 +170,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); @@ -201,53 +200,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); @@ -255,17 +254,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; @@ -276,6 +273,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; } @@ -288,9 +286,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; @@ -795,6 +791,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) { @@ -1541,16 +1539,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" @@ -1564,7 +1561,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 af11bbc9..bc900f80 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; From b6079e510c65f769827499d7d182a0538d049000 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:58:11 +0100 Subject: [PATCH 04/12] When using a tun device, do not try to kill pppd --- src/tunnel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tunnel.c b/src/tunnel.c index c5855a2a..1d356ad5 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1572,8 +1572,10 @@ int run_tunnel(struct vpn_config *config) tunnel.state = STATE_DISCONNECTING; err_start_tunnel: - ret = pppd_terminate(&tunnel); - log_info("Terminated %s.\n", PPP_DAEMON); + if (!tunnel.use_tun) { + ret = pppd_terminate(&tunnel); + log_info("Terminated %s.\n", PPP_DAEMON); + } err_tunnel: log_info("Closed connection to gateway.\n"); tunnel.state = STATE_DOWN; From 0a7dbded4bf50388c5d09e639340c140bc74bdf8 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:55:36 +0200 Subject: [PATCH 05/12] Add experimental option --tun Switch from pppd/ppp to a tun device and internal PPP code. --- src/config.c | 1 + src/config.h | 1 + src/http.c | 2 +- src/io.c | 8 ++++---- src/main.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/tunnel.c | 5 ++--- src/tunnel.h | 1 - 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/config.c b/src/config.c index d423e9db..e8e8b776 100644 --- a/src/config.c +++ b/src/config.c @@ -53,6 +53,7 @@ const struct vpn_config invalid_cfg = { .no_ftm_push = -1, .pinentry = NULL, .realm = {'\0'}, + .tun = -1, .iface_name = {'\0'}, .sni = {'\0'}, .set_routes = -1, diff --git a/src/config.h b/src/config.h index fdf11220..1dca5d15 100644 --- a/src/config.h +++ b/src/config.h @@ -80,6 +80,7 @@ struct vpn_config { unsigned int otp_delay; int no_ftm_push; char *pinentry; + int tun; char iface_name[IF_NAMESIZE]; char realm[REALM_SIZE + 1]; diff --git a/src/http.c b/src/http.c index 4817490d..b898ce47 100644 --- a/src/http.c +++ b/src/http.c @@ -897,7 +897,7 @@ 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) { + if (tunnel->config->tun) { tunnel->ipv4.ip_addr.s_addr = inet_addr(gateway); tunnel->ipv4.peer_addr.s_addr = inet_addr("192.0.2.1"); } diff --git a/src/io.c b/src/io.c index 76468c9e..d3c61d30 100644 --- a/src/io.c +++ b/src/io.c @@ -1089,7 +1089,7 @@ static void *pppd_read(void *arg) log_debug("%s thread\n", __func__); - if (tunnel->use_tun) { + if (tunnel->config->tun) { switch (tunnel->tun_state) { case TUN_PPP_LCP: conf_request(tunnel); @@ -1126,7 +1126,7 @@ static void *pppd_read(void *arg) SEM_POST(&sem_pppd_ready); first_time = 0; } - if (tunnel->use_tun) { + if (tunnel->config->tun) { ssize_t pktsize = n + 2; struct ppp_packet *packet = NULL; @@ -1239,7 +1239,7 @@ static void *pppd_write(void *arg) // This waits until a packet has arrived from the gateway packet = pool_pop(&tunnel->ssl_to_pty_pool); - if (tunnel->use_tun) { + if (tunnel->config->tun) { void *pkt_type = pkt_data(packet); hdlc_bufsize = len = packet->len; @@ -1457,7 +1457,7 @@ static void *ssl_read(void *arg) set_tunnel_ips(tunnel, packet); - if (tunnel->use_tun) + if (tunnel->config->tun) tun_ifup(tunnel->tun_iface, tunnel->ipv4.ip_addr.s_addr, 0); strcpy(line, "["); diff --git a/src/main.c b/src/main.c index c374c52d..17810129 100644 --- a/src/main.c +++ b/src/main.c @@ -81,7 +81,7 @@ " [--cookie=] [--cookie-on-stdin] [--saml-login]\n" \ " [--otp=] [--otp-delay=] [--otp-prompt=]\n" \ " [--pinentry=] [--realm=]\n" \ -" [--ifname=] [--set-routes=<0|1>]\n" \ +" [--tun=<0|1>] [--ifname=] [--set-routes=<0|1>]\n" \ " [--half-internet-routes=<0|1>] [--set-dns=<0|1>]\n" \ PPPD_USAGE \ " " RESOLVCONF_USAGE "[--ca-file=]\n" \ @@ -125,6 +125,7 @@ PPPD_USAGE \ " --no-ftm-push Do not use FTM push if the server provides the option.\n" \ " --pinentry= Use the program to supply a secret instead of asking for it.\n" \ " --realm= Use specified authentication realm.\n" \ +" --tun=[01] Create a TUN device and use internal PPP code (experimental).\n" \ " --ifname= Bind to interface.\n" \ " --set-routes=[01] Set if openfortivpn should configure routes\n" \ " when tunnel is up.\n" \ @@ -295,6 +296,7 @@ int main(int argc, char *argv[]) {"otp-prompt", required_argument, NULL, 0}, {"otp-delay", required_argument, NULL, 0}, {"no-ftm-push", no_argument, &cli_cfg.no_ftm_push, 1}, + {"tun", required_argument, NULL, 0}, {"ifname", required_argument, NULL, 0}, {"set-routes", required_argument, NULL, 0}, {"sni", required_argument, NULL, 0}, @@ -508,6 +510,18 @@ int main(int argc, char *argv[]) cli_cfg.otp_prompt = strdup(optarg); break; } + if (strcmp(long_options[option_index].name, + "tun") == 0) { + int tun = strtob(optarg); + + if (tun < 0) { + log_warn("Bad tun option: \"%s\"\n", + optarg); + break; + } + cli_cfg.tun = tun; + break; + } if (strcmp(long_options[option_index].name, "ifname") == 0) { strncpy(cli_cfg.iface_name, optarg, IF_NAMESIZE - 1); @@ -686,6 +700,41 @@ int main(int argc, char *argv[]) merge_config(&cfg, &cli_cfg); set_syslog(cfg.use_syslog); + if (cfg.tun) { +#if HAVE_USR_SBIN_PPPD + if (cfg.pppd_use_peerdns) { + log_error("Option pppd_use_peerdns is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } + if (cfg.pppd_plugin) { + log_error("Option pppd_plugin is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } + if (cfg.pppd_ifname) { + log_error("Option pppd_ifname is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } + if (cfg.pppd_ipparam) { + log_error("Option pppd_ipparam is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } + if (cfg.pppd_call) { + log_error("Option pppd_call is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } + if (cfg.pppd_plugin) { + log_error("Option pppd_plugin is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } +#endif +#if HAVE_USR_SBIN_PPP + if (cfg.ppp_system) { + log_error("Option ppp_system is not compatible with option tun\n"); + exit(EXIT_FAILURE); + } +#endif + } + // Set default UA if (cfg.user_agent == NULL) cfg.user_agent = strdup("Mozilla/5.0 SV1"); diff --git a/src/tunnel.c b/src/tunnel.c index 1d356ad5..1a7ac55a 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1527,7 +1527,6 @@ int run_tunnel(struct vpn_config *config) goto err_tunnel; // Step 3: get configuration - tunnel.use_tun = 1; log_info("Retrieving configuration\n"); ret = auth_get_config(&tunnel); if (ret != 1) { @@ -1539,7 +1538,7 @@ int run_tunnel(struct vpn_config *config) // Step 4: run a pppd process log_info("Establishing the tunnel\n"); - if (tunnel.use_tun) + if (config->tun) ret = tun_setup(&tunnel); else ret = pppd_run(&tunnel); @@ -1572,7 +1571,7 @@ int run_tunnel(struct vpn_config *config) tunnel.state = STATE_DISCONNECTING; err_start_tunnel: - if (!tunnel.use_tun) { + if (!config->tun) { ret = pppd_terminate(&tunnel); log_info("Terminated %s.\n", PPP_DAEMON); } diff --git a/src/tunnel.h b/src/tunnel.h index bc900f80..6cca06d4 100644 --- a/src/tunnel.h +++ b/src/tunnel.h @@ -72,7 +72,6 @@ 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]; From 08f9a71dc28521bc03924cac8b0e2b5363b8f511 Mon Sep 17 00:00:00 2001 From: DarioM Date: Mon, 25 Sep 2023 10:45:45 +0200 Subject: [PATCH 06/12] Merge 'tun' config value if set in source configuration This commit modifies the `merge_config` function to include the 'tun' value from the source configuration if it is set. Previously, this value was not being merged, leading to inconsistencies in the final configuration. The change checks if 'tun' in the source configuration is different from its default value before merging it into the destination configuration. This ensures that the 'tun' setting is correctly propagated when configurations are merged, allowing for more flexible and accurate VPN setups. --- src/config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.c b/src/config.c index e8e8b776..ffb23ffd 100644 --- a/src/config.c +++ b/src/config.c @@ -551,6 +551,8 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src) free(dst->pinentry); dst->pinentry = src->pinentry; } + if (src->tun != invalid_cfg.tun) + dst->tun = src->tun; if (src->realm[0]) strcpy(dst->realm, src->realm); if (src->iface_name[0]) From aece8ab201b18494509cfc28f08de18d435de031 Mon Sep 17 00:00:00 2001 From: Caeies Date: Fri, 29 Aug 2025 11:25:38 +0200 Subject: [PATCH 07/12] Tun: properly release resources at end of connection. --- src/tunnel.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tunnel.c b/src/tunnel.c index 1a7ac55a..d1149172 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1574,6 +1574,13 @@ int run_tunnel(struct vpn_config *config) if (!config->tun) { ret = pppd_terminate(&tunnel); log_info("Terminated %s.\n", PPP_DAEMON); + } else { + if (tunnel.pppd_pty > 0) + if (tun_close(tunnel.pppd_pty)) + log_error("Cannot properly close tun interface (%d)", + errno); + tunnel.pppd_pty = -1; + log_info("Closing tun interface.\n"); } err_tunnel: log_info("Closed connection to gateway.\n"); From a578ab96910e0bb26f88bf49f15afd2c9a86183f Mon Sep 17 00:00:00 2001 From: Caeies Date: Wed, 27 Aug 2025 11:35:12 +0200 Subject: [PATCH 08/12] Tun: allow to rename interface. * NOTE: the tun options are not yet integrated in the config file. * adding the --tun-ifname option to allow choosing interface name to ease script integrations. --- src/config.c | 6 ++++++ src/config.h | 1 + src/main.c | 11 ++++++++++- src/tunnel.c | 4 ++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/config.c b/src/config.c index ffb23ffd..80b2646f 100644 --- a/src/config.c +++ b/src/config.c @@ -54,6 +54,7 @@ const struct vpn_config invalid_cfg = { .pinentry = NULL, .realm = {'\0'}, .tun = -1, + .tun_ifname = NULL, .iface_name = {'\0'}, .sni = {'\0'}, .set_routes = -1, @@ -498,6 +499,7 @@ void destroy_vpn_config(struct vpn_config *cfg) free(cfg->otp_prompt); free(cfg->pinentry); free(cfg->cookie); + free(cfg->tun_ifname); #if HAVE_USR_SBIN_PPPD free(cfg->pppd_log); free(cfg->pppd_plugin); @@ -553,6 +555,10 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src) } if (src->tun != invalid_cfg.tun) dst->tun = src->tun; + if (src->tun_ifname) { + free(dst->tun_ifname); + dst->tun_ifname = src->tun_ifname; + } if (src->realm[0]) strcpy(dst->realm, src->realm); if (src->iface_name[0]) diff --git a/src/config.h b/src/config.h index 1dca5d15..2d2647df 100644 --- a/src/config.h +++ b/src/config.h @@ -81,6 +81,7 @@ struct vpn_config { int no_ftm_push; char *pinentry; int tun; + char *tun_ifname; char iface_name[IF_NAMESIZE]; char realm[REALM_SIZE + 1]; diff --git a/src/main.c b/src/main.c index 17810129..c1b4674c 100644 --- a/src/main.c +++ b/src/main.c @@ -81,7 +81,7 @@ " [--cookie=] [--cookie-on-stdin] [--saml-login]\n" \ " [--otp=] [--otp-delay=] [--otp-prompt=]\n" \ " [--pinentry=] [--realm=]\n" \ -" [--tun=<0|1>] [--ifname=] [--set-routes=<0|1>]\n" \ +" [--tun=<0|1>] [--tun-ifname=] [--ifname=] [--set-routes=<0|1>]\n" \ " [--half-internet-routes=<0|1>] [--set-dns=<0|1>]\n" \ PPPD_USAGE \ " " RESOLVCONF_USAGE "[--ca-file=]\n" \ @@ -126,6 +126,8 @@ PPPD_USAGE \ " --pinentry= Use the program to supply a secret instead of asking for it.\n" \ " --realm= Use specified authentication realm.\n" \ " --tun=[01] Create a TUN device and use internal PPP code (experimental).\n" \ +" --tun-ifname= Choose the tunX interface name to ease custom scripts routing\n" \ +" handling (experimental).\n" \ " --ifname= Bind to interface.\n" \ " --set-routes=[01] Set if openfortivpn should configure routes\n" \ " when tunnel is up.\n" \ @@ -297,6 +299,7 @@ int main(int argc, char *argv[]) {"otp-delay", required_argument, NULL, 0}, {"no-ftm-push", no_argument, &cli_cfg.no_ftm_push, 1}, {"tun", required_argument, NULL, 0}, + {"tun-ifname", required_argument, NULL, 0}, {"ifname", required_argument, NULL, 0}, {"set-routes", required_argument, NULL, 0}, {"sni", required_argument, NULL, 0}, @@ -522,6 +525,12 @@ int main(int argc, char *argv[]) cli_cfg.tun = tun; break; } + if (strcmp(long_options[option_index].name, + "tun-ifname") == 0) { + free(cli_cfg.tun_ifname); + cli_cfg.tun_ifname = strdup(optarg); + break; + } if (strcmp(long_options[option_index].name, "ifname") == 0) { strncpy(cli_cfg.iface_name, optarg, IF_NAMESIZE - 1); diff --git a/src/tunnel.c b/src/tunnel.c index d1149172..e5584e4b 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -262,6 +262,10 @@ static int tun_setup(struct tunnel *tunnel) .ifr_flags = IFF_TUN | IFF_NO_PI, }; + /* Renamme the interface by default if asked to do so */ + if (tunnel->config->tun_ifname) + strncpy(ifreq.ifr_name, tunnel->config->tun_ifname, IFNAMSIZ - 1); + tun_fd = tun_open(&ifreq); if (tun_fd < 0) { log_error("tun_open failed: %s\n", strerror(errno)); From 6f3419e4e89f7ff661b3219b6894eeb870c3e07e Mon Sep 17 00:00:00 2001 From: Caeies Date: Wed, 27 Aug 2025 19:54:31 +0200 Subject: [PATCH 09/12] Adding a sample script for tun additionnal ressources configuration. --- .../dispatcher.d/30-pppVPN-rules.sh | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh diff --git a/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh b/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh new file mode 100755 index 00000000..b94667c3 --- /dev/null +++ b/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Sample script to add in the /etc/NetworkManager/dispatcher.d folder. +# Allowing you to properly configure additionnal routes if needed +# There might be better way to do this, but I'm not aware of them in my specific case. + + +# The UUID of the connection profile we want to target. Use it if the UUID is fixed in your case. +#TARGET_CONNECTION_UUID="a1b2c3d4-e5f6-7890-1234-56789abcdef0" + +# The interface name (if UUID is not fixed). don't forget to use the --tun-ifname=pppVPN to your config file. +TARGET_INTERFACE="pppVPN" + +# The interface name (e.g., ppp0, enp3s0) is the first argument +INTERFACE="$1" +# The action (e.g., "up", "down") is the second argument +ACTION="$2" + +# Check if the script is being run for our target connection +#if [ "$CONNECTION_UUID" = "$TARGET_CONNECTION_UUID" ]; then +if [ "$INTERFACE" = "$TARGET_INTERFACE" ]; then + case "$ACTION" in + up) + # This code runs when the connection comes up + logger "NetworkManager Dispatcher: Applying custom rules for $INTERFACE" + # Add specific ressources route rules here (more easy than the +ipv4.route using the via which could be random): + #ip route add 10.10.10.0/24 dev $TARGET_INTERFACE scope link + #ip route add 10.10.20.0/24 dev $TARGET_INTERFACE scope link + #ip route add 10.10.30.120/32 dev $TARGET_INTERFACE scope link + ;; + down) + # This code runs when the connection goes down + logger "NetworkManager Dispatcher: Removing custom rules for $INTERFACE" + # if using the dev $TARGET_INTERFACE nothing to do, if not, you should remove your rules here + ;; + esac +fi + +true From 20a86fefc991600ab5e685596849f3e2708cee8e Mon Sep 17 00:00:00 2001 From: Caeies Date: Wed, 27 Aug 2025 20:08:13 +0200 Subject: [PATCH 10/12] Tun: allow the tun option in config file. --- src/config.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/config.c b/src/config.c index 80b2646f..cffe6329 100644 --- a/src/config.c +++ b/src/config.c @@ -296,6 +296,18 @@ int load_config(struct vpn_config *cfg, const char *filename) } else if (strcmp(key, "realm") == 0) { strncpy(cfg->realm, val, REALM_SIZE); cfg->realm[REALM_SIZE] = '\0'; + } else if (strcmp(key, "tun") == 0) { + long tun = strtol(val, NULL, 0); + + if (tun < 0 || tun > 1) { + log_warn("Bad tun option in configuration file: \"%ld\".\n", + tun); + continue; + } + cfg->tun = tun; + } else if (strcmp(key, "tun-ifname") == 0) { + free(cfg->tun_ifname); + cfg->tun_ifname = strdup(val); } else if (strcmp(key, "set-dns") == 0) { int set_dns = strtob(val); From 158b1bc63675a12fce12315c13925511af13ad65 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 30 Aug 2025 13:59:39 +0300 Subject: [PATCH 11/12] Tun: fix typos found by codespell --- etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh b/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh index b94667c3..864af840 100755 --- a/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh +++ b/etc/NetworkManager/dispatcher.d/30-pppVPN-rules.sh @@ -1,6 +1,6 @@ #!/bin/bash # Sample script to add in the /etc/NetworkManager/dispatcher.d folder. -# Allowing you to properly configure additionnal routes if needed +# Allowing you to properly configure additional routes if needed # There might be better way to do this, but I'm not aware of them in my specific case. @@ -22,7 +22,7 @@ if [ "$INTERFACE" = "$TARGET_INTERFACE" ]; then up) # This code runs when the connection comes up logger "NetworkManager Dispatcher: Applying custom rules for $INTERFACE" - # Add specific ressources route rules here (more easy than the +ipv4.route using the via which could be random): + # Add specific resources route rules here (more easy than the +ipv4.route using the via which could be random): #ip route add 10.10.10.0/24 dev $TARGET_INTERFACE scope link #ip route add 10.10.20.0/24 dev $TARGET_INTERFACE scope link #ip route add 10.10.30.120/32 dev $TARGET_INTERFACE scope link From c967c24a5587527f1d791e094c9cfa561301f669 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Mon, 1 Sep 2025 00:24:26 +0300 Subject: [PATCH 12/12] Fix Coverity defect CID 488054 RESOURCE_LEAK Overwriting handle `tunnel.pppd_pty` in `tunnel.pppd_pty = -1` leaks the handle. --- src/tunnel.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tunnel.c b/src/tunnel.c index e5584e4b..0579f36c 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -1579,12 +1579,13 @@ int run_tunnel(struct vpn_config *config) ret = pppd_terminate(&tunnel); log_info("Terminated %s.\n", PPP_DAEMON); } else { - if (tunnel.pppd_pty > 0) + if (tunnel.pppd_pty >= 0) { + log_info("Closing tun interface.\n"); if (tun_close(tunnel.pppd_pty)) - log_error("Cannot properly close tun interface (%d)", + log_error("Cannot properly close tun interface (%d).\n", errno); + } tunnel.pppd_pty = -1; - log_info("Closing tun interface.\n"); } err_tunnel: log_info("Closed connection to gateway.\n");