From f0b4776bf1fdee45e01503a8721feb0aa45cb42f Mon Sep 17 00:00:00 2001 From: bolvan Date: Tue, 14 May 2019 18:34:39 +0300 Subject: [PATCH] fixing lots of problems with getting link local address after reboot --- tpws/tpws.c | 302 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 179 insertions(+), 123 deletions(-) diff --git a/tpws/tpws.c b/tpws/tpws.c index e0268e6..c37d41d 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -32,8 +32,10 @@ enum splithttpreq { split_none = 0, split_method, split_host }; struct params_s { - char bindaddr[64],bindiface4[16],bindiface6[16]; + char bindaddr[64],bindiface[IFNAMSIZ]; + bool bind_if6; bool bindll,bindll_force; + int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll; uid_t uid; gid_t gid; uint16_t port; @@ -501,6 +503,23 @@ int8_t block_sigpipe() { return 0; } + +bool is_interface_online(const char *ifname) +{ + struct ifreq ifr; + int sock; + + if ((sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP))==-1) + return false; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ] = 0; + ioctl(sock, SIOCGIFFLAGS, &ifr); + close(sock); + return !!(ifr.ifr_flags & IFF_UP); +} + + void exithelp() { printf( @@ -508,22 +527,25 @@ void exithelp() " --bind-iface4=\t; bind to the first ipv4 addr of interface\n" " --bind-iface6=\t; bind to the first ipv6 addr of interface\n" " --bind-linklocal=prefer|force\t; prefer or force ipv6 link local\n" + " --bind-wait-ifup=\t\t; wait for interface to appear and up\n" + " --bind-wait-ip=\t\t; after ifup wait for ip address to appear up to N seconds\n" + " --bind-wait-ip-linklocal=\t; accept only link locals first N seconds then any\n" " --port=\n" " --maxconn=\n" - " --hostlist=\t; only act on host in the list (one host per line, subdomains auto apply)\n" + " --hostlist=\t\t; only act on host in the list (one host per line, subdomains auto apply)\n" " --split-http-req=method|host\n" " --split-pos=\t; split at specified pos. invalidates split-http-req.\n" - " --hostcase\t\t; change Host: => host:\n" - " --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" - " --hostdot\t\t; add \".\" after Host: name\n" - " --hosttab\t\t; add tab after Host: name\n" - " --hostnospace\t\t; remove space after Host:\n" - " --methodspace\t\t; add extra space after method\n" - " --methodeol\t\t; add end-of-line before method\n" - " --unixeol\t\t; replace 0D0A to 0A\n" - " --daemon\t\t; daemonize\n" - " --pidfile=\t; write pid to file\n" - " --user=\t; drop root privs\n" + " --hostcase\t\t\t; change Host: => host:\n" + " --hostspell\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" + " --hostdot\t\t\t; add \".\" after Host: name\n" + " --hosttab\t\t\t; add tab after Host: name\n" + " --hostnospace\t\t\t; remove space after Host:\n" + " --methodspace\t\t\t; add extra space after method\n" + " --methodeol\t\t\t; add end-of-line before method\n" + " --unixeol\t\t\t; replace 0D0A to 0A\n" + " --daemon\t\t\t; daemonize\n" + " --pidfile=\t\t; write pid to file\n" + " --user=\t\t; drop root privs\n" ); exit(1); } @@ -561,22 +583,25 @@ void parse_params(int argc, char *argv[]) { "bind-iface4",required_argument,0,0 },// optidx=3 { "bind-iface6",required_argument,0,0 },// optidx=4 { "bind-linklocal",required_argument,0,0 },// optidx=5 - { "port",required_argument,0,0 },// optidx=6 - { "daemon",no_argument,0,0 },// optidx=7 - { "user",required_argument,0,0 },// optidx=8 - { "maxconn",required_argument,0,0 },// optidx=9 - { "hostcase",no_argument,0,0 },// optidx=10 - { "hostspell",required_argument,0,0 },// optidx=11 - { "hostdot",no_argument,0,0 },// optidx=12 - { "hostnospace",no_argument,0,0 },// optidx=13 - { "split-http-req",required_argument,0,0 },// optidx=14 - { "split-pos",required_argument,0,0 },// optidx=15 - { "methodspace",no_argument,0,0 },// optidx=16 - { "methodeol",no_argument,0,0 },// optidx=17 - { "hosttab",no_argument,0,0 },// optidx=18 - { "unixeol",no_argument,0,0 },// optidx=19 - { "hostlist",required_argument,0,0 },// optidx=20 - { "pidfile",required_argument,0,0 },// optidx=21 + { "bind-wait-ifup",required_argument,0,0 },// optidx=6 + { "bind-wait-ip",required_argument,0,0 },// optidx=7 + { "bind-wait-ip-linklocal",required_argument,0,0 },// optidx=8 + { "port",required_argument,0,0 },// optidx=9 + { "daemon",no_argument,0,0 },// optidx=10 + { "user",required_argument,0,0 },// optidx=11 + { "maxconn",required_argument,0,0 },// optidx=12 + { "hostcase",no_argument,0,0 },// optidx=13 + { "hostspell",required_argument,0,0 },// optidx=14 + { "hostdot",no_argument,0,0 },// optidx=15 + { "hostnospace",no_argument,0,0 },// optidx=16 + { "split-http-req",required_argument,0,0 },// optidx=17 + { "split-pos",required_argument,0,0 },// optidx=18 + { "methodspace",no_argument,0,0 },// optidx=19 + { "methodeol",no_argument,0,0 },// optidx=20 + { "hosttab",no_argument,0,0 },// optidx=21 + { "unixeol",no_argument,0,0 },// optidx=22 + { "hostlist",required_argument,0,0 },// optidx=23 + { "pidfile",required_argument,0,0 },// optidx=24 { NULL,0,NULL,0 } }; while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) @@ -593,22 +618,14 @@ void parse_params(int argc, char *argv[]) params.bindaddr[sizeof(params.bindaddr) - 1] = 0; break; case 3: /* bind-iface4 */ - if (*params.bindiface6) - { - fprintf(stderr, "can bind only to single ip address\n"); - exit_clean(1); - } - strncpy(params.bindiface4, optarg, sizeof(params.bindiface4)); - params.bindiface4[sizeof(params.bindiface4) - 1] = 0; + params.bind_if6=false; + strncpy(params.bindiface, optarg, sizeof(params.bindiface)); + params.bindiface[sizeof(params.bindiface) - 1] = 0; break; case 4: /* bind-iface6 */ - if (*params.bindiface4) - { - fprintf(stderr, "can bind only to single ip address\n"); - exit_clean(1); - } - strncpy(params.bindiface6, optarg, sizeof(params.bindiface6)); - params.bindiface6[sizeof(params.bindiface6) - 1] = 0; + params.bind_if6=true; + strncpy(params.bindiface, optarg, sizeof(params.bindiface)); + params.bindiface[sizeof(params.bindiface) - 1] = 0; break; case 5: /* bind-linklocal */ params.bindll = true; @@ -620,7 +637,16 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 6: /* port */ + case 6: /* bind-wait-ifup */ + params.bind_wait_ifup = atoi(optarg); + break; + case 7: /* bind-wait-ip */ + params.bind_wait_ip = atoi(optarg); + break; + case 8: /* bind-wait-ip-linklocal */ + params.bind_wait_ip_ll = atoi(optarg); + break; + case 9: /* port */ i = atoi(optarg); if (i <= 0 || i > 65535) { @@ -629,10 +655,10 @@ void parse_params(int argc, char *argv[]) } params.port = (uint16_t)i; break; - case 7: /* daemon */ + case 10: /* daemon */ params.daemon = true; break; - case 8: /* user */ + case 11: /* user */ { struct passwd *pwd = getpwnam(optarg); if (!pwd) @@ -644,7 +670,7 @@ void parse_params(int argc, char *argv[]) params.gid = pwd->pw_gid; break; } - case 9: /* maxconn */ + case 12: /* maxconn */ params.maxconn = atoi(optarg); if (params.maxconn <= 0) { @@ -652,10 +678,10 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 10: /* hostcase */ + case 13: /* hostcase */ params.hostcase = true; break; - case 11: /* hostspell */ + case 14: /* hostspell */ if (strlen(optarg) != 4) { fprintf(stderr, "hostspell must be exactly 4 chars long\n"); @@ -664,13 +690,13 @@ void parse_params(int argc, char *argv[]) params.hostcase = true; memcpy(params.hostspell, optarg, 4); break; - case 12: /* hostdot */ + case 15: /* hostdot */ params.hostdot = true; break; - case 13: /* hostnospace */ + case 16: /* hostnospace */ params.hostnospace = true; break; - case 14: /* split-http-req */ + case 17: /* split-http-req */ if (!strcmp(optarg, "method")) params.split_http_req = split_method; else if (!strcmp(optarg, "host")) @@ -681,7 +707,7 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 15: /* split-pos */ + case 18: /* split-pos */ i = atoi(optarg); if (i) params.split_pos = i; @@ -691,25 +717,25 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; - case 16: /* methodspace */ + case 19: /* methodspace */ params.methodspace = true; break; - case 17: /* methodeol */ + case 20: /* methodeol */ params.methodeol = true; break; - case 18: /* hosttab */ + case 21: /* hosttab */ params.hosttab = true; break; - case 19: /* unixeol */ + case 22: /* unixeol */ params.unixeol = true; break; - case 20: /* hostlist */ + case 23: /* hostlist */ if (!LoadHostList(¶ms.hostlist, optarg)) exit_clean(1); strncpy(params.hostfile,optarg,sizeof(params.hostfile)); params.hostfile[sizeof(params.hostfile)-1]='\0'; break; - case 21: /* pidfile */ + case 24: /* pidfile */ strncpy(params.pidfile,optarg,sizeof(params.pidfile)); params.pidfile[sizeof(params.pidfile)-1]='\0'; break; @@ -779,31 +805,88 @@ bool writepid(const char *filename) return true; } + + +bool find_listen_addr(struct sockaddr_storage *salisten, bool bindll, int *if_index) +{ + struct ifaddrs *addrs,*a; + bool found=false; + + if (getifaddrs(&addrs)<0) + return false; + + a = addrs; + while (a) + { + if (a->ifa_addr) + { + if (a->ifa_addr->sa_family==AF_INET && + *params.bindiface && !params.bind_if6 && !strcmp(a->ifa_name, params.bindiface)) + { + salisten->ss_family = AF_INET; + memcpy(&((struct sockaddr_in*)salisten)->sin_addr, &((struct sockaddr_in*)a->ifa_addr)->sin_addr, sizeof(struct in_addr)); + found=true; + break; + } + // ipv6 links locals are fe80::/10 + else if (a->ifa_addr->sa_family==AF_INET6 + && + (!*params.bindiface && bindll || + *params.bindiface && params.bind_if6 && !strcmp(a->ifa_name, params.bindiface)) + && + (!bindll || + ((struct sockaddr_in6*)a->ifa_addr)->sin6_addr.s6_addr[0]==0xFE && + (((struct sockaddr_in6*)a->ifa_addr)->sin6_addr.s6_addr[1] & 0xC0)==0x80)) + { + salisten->ss_family = AF_INET6; + memcpy(&((struct sockaddr_in6*)salisten)->sin6_addr, &((struct sockaddr_in6*)a->ifa_addr)->sin6_addr, sizeof(struct in6_addr)); + if (if_index) *if_index = if_nametoindex(a->ifa_name); + found=true; + break; + } + } + a = a->ifa_next; + } + freeifaddrs(addrs); + return found; +} + + int main(int argc, char *argv[]) { int listen_fd = -1; int yes = 1, retval = 0; int r; struct sockaddr_storage salisten; socklen_t salisten_len; - int ipv6_only=0,if_index6=0; + int ipv6_only=0,if_index=0; parse_params(argc, argv); memset(&salisten, 0, sizeof(salisten)); - if (*params.bindiface4) + if (*params.bindiface) { - if (!if_nametoindex(params.bindiface4)) + if (params.bind_wait_ifup > 0) { - printf("bad iface %s\n",params.bindiface4); - goto exiterr; + int sec=0; + if (!is_interface_online(params.bindiface)) + { + fprintf(stderr,"waiting ifup of %s for up to %d seconds...\n",params.bindiface,params.bind_wait_ifup); + do + { + sleep(1); + sec++; + } + while (!is_interface_online(params.bindiface) && sec=params.bind_wait_ifup) + { + printf("wait timed out\n"); + goto exiterr; + } + } } - } - if (*params.bindiface6) - { - if_index6 = if_nametoindex(params.bindiface6); - if (!if_index6) + if (!(if_index = if_nametoindex(params.bindiface)) && params.bind_wait_ip<=0) { - printf("bad iface %s\n",params.bindiface6); + printf("bad iface %s\n",params.bindiface); goto exiterr; } } @@ -820,72 +903,45 @@ int main(int argc, char *argv[]) { } else { - printf("bad bind addr\n"); + printf("bad bind addr : %s\n", params.bindaddr); goto exiterr; } } else { - if (*params.bindiface4 || *params.bindiface6 || params.bindll) + if (*params.bindiface || params.bindll) { - struct ifaddrs *addrs,*a; - bool found=0; - - if (getifaddrs(&addrs)<0) + bool found; + int sec=0; + + if (params.bind_wait_ip > 0) { - printf("getifaddrs failed\n"); - goto exiterr; + fprintf(stderr,"waiting for ip for %d seconds...\n", params.bind_wait_ip); + if (params.bindll && !params.bindll_force && params.bind_wait_ip_ll>0) + fprintf(stderr,"during the first %d seconds accepting only link locals...\n", params.bind_wait_ip_ll); } + + for(;;) + { + found = find_listen_addr(&salisten,params.bindll,&if_index); + if (found) break; + + if (params.bindll && !params.bindll_force && sec>=params.bind_wait_ip_ll) + if (found = find_listen_addr(&salisten,false,&if_index)) break; + + if (sec>=params.bind_wait_ip) + break; + + sleep(1); + sec++; + } - for (;;) - { - a = addrs; - while (a) - { - if (a->ifa_addr) - { - if (a->ifa_addr->sa_family==AF_INET && - *params.bindiface4 && !strcmp(a->ifa_name, params.bindiface4)) - { - salisten.ss_family = AF_INET; - memcpy(&((struct sockaddr_in*)&salisten)->sin_addr, &((struct sockaddr_in*)a->ifa_addr)->sin_addr, sizeof(struct in_addr)); - found=1; - break; - } - // ipv6 links locals are fe80::/10 - else if (a->ifa_addr->sa_family==AF_INET6 - && - (!*params.bindiface6 && params.bindll || - *params.bindiface6 && !strcmp(a->ifa_name, params.bindiface6)) - && - (!params.bindll || - ((struct sockaddr_in6*)a->ifa_addr)->sin6_addr.s6_addr[0]==0xFE && - (((struct sockaddr_in6*)a->ifa_addr)->sin6_addr.s6_addr[1] & 0xC0)==0x80)) - { - salisten.ss_family = AF_INET6; - memcpy(&((struct sockaddr_in6*)&salisten)->sin6_addr, &((struct sockaddr_in6*)a->ifa_addr)->sin6_addr, sizeof(struct in6_addr)); - if_index6 = if_nametoindex(a->ifa_name); - ipv6_only = 1; - found=1; - break; - } - } - a = a->ifa_next; - } - if (!found && params.bindll && !params.bindll_force) - { - params.bindll=false; - // give it another try without bindll - continue; - } - break; - } - freeifaddrs(addrs); if (!found) { printf("suitable ip address not found\n"); goto exiterr; } + ipv6_only=1; } else { @@ -897,7 +953,7 @@ int main(int argc, char *argv[]) { { salisten_len = sizeof(struct sockaddr_in6); ((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); - ((struct sockaddr_in6*)&salisten)->sin6_scope_id = if_index6; + ((struct sockaddr_in6*)&salisten)->sin6_scope_id = if_index; } else {