fixing lots of problems with getting link local address after reboot

This commit is contained in:
bolvan 2019-05-14 18:34:39 +03:00
parent 38d5b639de
commit f0b4776bf1

View File

@ -32,8 +32,10 @@ enum splithttpreq { split_none = 0, split_method, split_host };
struct params_s struct params_s
{ {
char bindaddr[64],bindiface4[16],bindiface6[16]; char bindaddr[64],bindiface[IFNAMSIZ];
bool bind_if6;
bool bindll,bindll_force; bool bindll,bindll_force;
int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
uint16_t port; uint16_t port;
@ -501,6 +503,23 @@ int8_t block_sigpipe() {
return 0; 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() void exithelp()
{ {
printf( printf(
@ -508,22 +527,25 @@ void exithelp()
" --bind-iface4=<interface_name>\t; bind to the first ipv4 addr of interface\n" " --bind-iface4=<interface_name>\t; bind to the first ipv4 addr of interface\n"
" --bind-iface6=<interface_name>\t; bind to the first ipv6 addr of interface\n" " --bind-iface6=<interface_name>\t; bind to the first ipv6 addr of interface\n"
" --bind-linklocal=prefer|force\t; prefer or force ipv6 link local\n" " --bind-linklocal=prefer|force\t; prefer or force ipv6 link local\n"
" --bind-wait-ifup=<sec>\t\t; wait for interface to appear and up\n"
" --bind-wait-ip=<sec>\t\t; after ifup wait for ip address to appear up to N seconds\n"
" --bind-wait-ip-linklocal=<sec>\t; accept only link locals first N seconds then any\n"
" --port=<port>\n" " --port=<port>\n"
" --maxconn=<max_connections>\n" " --maxconn=<max_connections>\n"
" --hostlist=<filename>\t; only act on host in the list (one host per line, subdomains auto apply)\n" " --hostlist=<filename>\t\t; only act on host in the list (one host per line, subdomains auto apply)\n"
" --split-http-req=method|host\n" " --split-http-req=method|host\n"
" --split-pos=<numeric_offset>\t; split at specified pos. invalidates split-http-req.\n" " --split-pos=<numeric_offset>\t; split at specified pos. invalidates split-http-req.\n"
" --hostcase\t\t; change Host: => host:\n" " --hostcase\t\t\t; change Host: => host:\n"
" --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostspell\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostdot\t\t; add \".\" after Host: name\n" " --hostdot\t\t\t; add \".\" after Host: name\n"
" --hosttab\t\t; add tab after Host: name\n" " --hosttab\t\t\t; add tab after Host: name\n"
" --hostnospace\t\t; remove space after Host:\n" " --hostnospace\t\t\t; remove space after Host:\n"
" --methodspace\t\t; add extra space after method\n" " --methodspace\t\t\t; add extra space after method\n"
" --methodeol\t\t; add end-of-line before method\n" " --methodeol\t\t\t; add end-of-line before method\n"
" --unixeol\t\t; replace 0D0A to 0A\n" " --unixeol\t\t\t; replace 0D0A to 0A\n"
" --daemon\t\t; daemonize\n" " --daemon\t\t\t; daemonize\n"
" --pidfile=<filename>\t; write pid to file\n" " --pidfile=<filename>\t\t; write pid to file\n"
" --user=<username>\t; drop root privs\n" " --user=<username>\t\t; drop root privs\n"
); );
exit(1); exit(1);
} }
@ -561,22 +583,25 @@ void parse_params(int argc, char *argv[])
{ "bind-iface4",required_argument,0,0 },// optidx=3 { "bind-iface4",required_argument,0,0 },// optidx=3
{ "bind-iface6",required_argument,0,0 },// optidx=4 { "bind-iface6",required_argument,0,0 },// optidx=4
{ "bind-linklocal",required_argument,0,0 },// optidx=5 { "bind-linklocal",required_argument,0,0 },// optidx=5
{ "port",required_argument,0,0 },// optidx=6 { "bind-wait-ifup",required_argument,0,0 },// optidx=6
{ "daemon",no_argument,0,0 },// optidx=7 { "bind-wait-ip",required_argument,0,0 },// optidx=7
{ "user",required_argument,0,0 },// optidx=8 { "bind-wait-ip-linklocal",required_argument,0,0 },// optidx=8
{ "maxconn",required_argument,0,0 },// optidx=9 { "port",required_argument,0,0 },// optidx=9
{ "hostcase",no_argument,0,0 },// optidx=10 { "daemon",no_argument,0,0 },// optidx=10
{ "hostspell",required_argument,0,0 },// optidx=11 { "user",required_argument,0,0 },// optidx=11
{ "hostdot",no_argument,0,0 },// optidx=12 { "maxconn",required_argument,0,0 },// optidx=12
{ "hostnospace",no_argument,0,0 },// optidx=13 { "hostcase",no_argument,0,0 },// optidx=13
{ "split-http-req",required_argument,0,0 },// optidx=14 { "hostspell",required_argument,0,0 },// optidx=14
{ "split-pos",required_argument,0,0 },// optidx=15 { "hostdot",no_argument,0,0 },// optidx=15
{ "methodspace",no_argument,0,0 },// optidx=16 { "hostnospace",no_argument,0,0 },// optidx=16
{ "methodeol",no_argument,0,0 },// optidx=17 { "split-http-req",required_argument,0,0 },// optidx=17
{ "hosttab",no_argument,0,0 },// optidx=18 { "split-pos",required_argument,0,0 },// optidx=18
{ "unixeol",no_argument,0,0 },// optidx=19 { "methodspace",no_argument,0,0 },// optidx=19
{ "hostlist",required_argument,0,0 },// optidx=20 { "methodeol",no_argument,0,0 },// optidx=20
{ "pidfile",required_argument,0,0 },// optidx=21 { "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 } { NULL,0,NULL,0 }
}; };
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) 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; params.bindaddr[sizeof(params.bindaddr) - 1] = 0;
break; break;
case 3: /* bind-iface4 */ case 3: /* bind-iface4 */
if (*params.bindiface6) params.bind_if6=false;
{ strncpy(params.bindiface, optarg, sizeof(params.bindiface));
fprintf(stderr, "can bind only to single ip address\n"); params.bindiface[sizeof(params.bindiface) - 1] = 0;
exit_clean(1);
}
strncpy(params.bindiface4, optarg, sizeof(params.bindiface4));
params.bindiface4[sizeof(params.bindiface4) - 1] = 0;
break; break;
case 4: /* bind-iface6 */ case 4: /* bind-iface6 */
if (*params.bindiface4) params.bind_if6=true;
{ strncpy(params.bindiface, optarg, sizeof(params.bindiface));
fprintf(stderr, "can bind only to single ip address\n"); params.bindiface[sizeof(params.bindiface) - 1] = 0;
exit_clean(1);
}
strncpy(params.bindiface6, optarg, sizeof(params.bindiface6));
params.bindiface6[sizeof(params.bindiface6) - 1] = 0;
break; break;
case 5: /* bind-linklocal */ case 5: /* bind-linklocal */
params.bindll = true; params.bindll = true;
@ -620,7 +637,16 @@ void parse_params(int argc, char *argv[])
exit_clean(1); exit_clean(1);
} }
break; 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); i = atoi(optarg);
if (i <= 0 || i > 65535) if (i <= 0 || i > 65535)
{ {
@ -629,10 +655,10 @@ void parse_params(int argc, char *argv[])
} }
params.port = (uint16_t)i; params.port = (uint16_t)i;
break; break;
case 7: /* daemon */ case 10: /* daemon */
params.daemon = true; params.daemon = true;
break; break;
case 8: /* user */ case 11: /* user */
{ {
struct passwd *pwd = getpwnam(optarg); struct passwd *pwd = getpwnam(optarg);
if (!pwd) if (!pwd)
@ -644,7 +670,7 @@ void parse_params(int argc, char *argv[])
params.gid = pwd->pw_gid; params.gid = pwd->pw_gid;
break; break;
} }
case 9: /* maxconn */ case 12: /* maxconn */
params.maxconn = atoi(optarg); params.maxconn = atoi(optarg);
if (params.maxconn <= 0) if (params.maxconn <= 0)
{ {
@ -652,10 +678,10 @@ void parse_params(int argc, char *argv[])
exit_clean(1); exit_clean(1);
} }
break; break;
case 10: /* hostcase */ case 13: /* hostcase */
params.hostcase = true; params.hostcase = true;
break; break;
case 11: /* hostspell */ case 14: /* hostspell */
if (strlen(optarg) != 4) if (strlen(optarg) != 4)
{ {
fprintf(stderr, "hostspell must be exactly 4 chars long\n"); fprintf(stderr, "hostspell must be exactly 4 chars long\n");
@ -664,13 +690,13 @@ void parse_params(int argc, char *argv[])
params.hostcase = true; params.hostcase = true;
memcpy(params.hostspell, optarg, 4); memcpy(params.hostspell, optarg, 4);
break; break;
case 12: /* hostdot */ case 15: /* hostdot */
params.hostdot = true; params.hostdot = true;
break; break;
case 13: /* hostnospace */ case 16: /* hostnospace */
params.hostnospace = true; params.hostnospace = true;
break; break;
case 14: /* split-http-req */ case 17: /* split-http-req */
if (!strcmp(optarg, "method")) if (!strcmp(optarg, "method"))
params.split_http_req = split_method; params.split_http_req = split_method;
else if (!strcmp(optarg, "host")) else if (!strcmp(optarg, "host"))
@ -681,7 +707,7 @@ void parse_params(int argc, char *argv[])
exit_clean(1); exit_clean(1);
} }
break; break;
case 15: /* split-pos */ case 18: /* split-pos */
i = atoi(optarg); i = atoi(optarg);
if (i) if (i)
params.split_pos = i; params.split_pos = i;
@ -691,25 +717,25 @@ void parse_params(int argc, char *argv[])
exit_clean(1); exit_clean(1);
} }
break; break;
case 16: /* methodspace */ case 19: /* methodspace */
params.methodspace = true; params.methodspace = true;
break; break;
case 17: /* methodeol */ case 20: /* methodeol */
params.methodeol = true; params.methodeol = true;
break; break;
case 18: /* hosttab */ case 21: /* hosttab */
params.hosttab = true; params.hosttab = true;
break; break;
case 19: /* unixeol */ case 22: /* unixeol */
params.unixeol = true; params.unixeol = true;
break; break;
case 20: /* hostlist */ case 23: /* hostlist */
if (!LoadHostList(&params.hostlist, optarg)) if (!LoadHostList(&params.hostlist, optarg))
exit_clean(1); exit_clean(1);
strncpy(params.hostfile,optarg,sizeof(params.hostfile)); strncpy(params.hostfile,optarg,sizeof(params.hostfile));
params.hostfile[sizeof(params.hostfile)-1]='\0'; params.hostfile[sizeof(params.hostfile)-1]='\0';
break; break;
case 21: /* pidfile */ case 24: /* pidfile */
strncpy(params.pidfile,optarg,sizeof(params.pidfile)); strncpy(params.pidfile,optarg,sizeof(params.pidfile));
params.pidfile[sizeof(params.pidfile)-1]='\0'; params.pidfile[sizeof(params.pidfile)-1]='\0';
break; break;
@ -779,31 +805,88 @@ bool writepid(const char *filename)
return true; 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 main(int argc, char *argv[]) {
int listen_fd = -1; int listen_fd = -1;
int yes = 1, retval = 0; int yes = 1, retval = 0;
int r; int r;
struct sockaddr_storage salisten; struct sockaddr_storage salisten;
socklen_t salisten_len; socklen_t salisten_len;
int ipv6_only=0,if_index6=0; int ipv6_only=0,if_index=0;
parse_params(argc, argv); parse_params(argc, argv);
memset(&salisten, 0, sizeof(salisten)); 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); int sec=0;
goto exiterr; 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);
if (sec>=params.bind_wait_ifup)
{
printf("wait timed out\n");
goto exiterr;
}
}
} }
} if (!(if_index = if_nametoindex(params.bindiface)) && params.bind_wait_ip<=0)
if (*params.bindiface6)
{
if_index6 = if_nametoindex(params.bindiface6);
if (!if_index6)
{ {
printf("bad iface %s\n",params.bindiface6); printf("bad iface %s\n",params.bindiface);
goto exiterr; goto exiterr;
} }
} }
@ -820,72 +903,45 @@ int main(int argc, char *argv[]) {
} }
else else
{ {
printf("bad bind addr\n"); printf("bad bind addr : %s\n", params.bindaddr);
goto exiterr; goto exiterr;
} }
} }
else else
{ {
if (*params.bindiface4 || *params.bindiface6 || params.bindll) if (*params.bindiface || params.bindll)
{ {
struct ifaddrs *addrs,*a; bool found;
bool found=0; int sec=0;
if (getifaddrs(&addrs)<0) if (params.bind_wait_ip > 0)
{ {
printf("getifaddrs failed\n"); fprintf(stderr,"waiting for ip for %d seconds...\n", params.bind_wait_ip);
goto exiterr; 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 (;;) for(;;)
{ {
a = addrs; found = find_listen_addr(&salisten,params.bindll,&if_index);
while (a) if (found) break;
{
if (a->ifa_addr) if (params.bindll && !params.bindll_force && sec>=params.bind_wait_ip_ll)
{ if (found = find_listen_addr(&salisten,false,&if_index)) break;
if (a->ifa_addr->sa_family==AF_INET &&
*params.bindiface4 && !strcmp(a->ifa_name, params.bindiface4)) if (sec>=params.bind_wait_ip)
{ break;
salisten.ss_family = AF_INET;
memcpy(&((struct sockaddr_in*)&salisten)->sin_addr, &((struct sockaddr_in*)a->ifa_addr)->sin_addr, sizeof(struct in_addr)); sleep(1);
found=1; sec++;
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) if (!found)
{ {
printf("suitable ip address not found\n"); printf("suitable ip address not found\n");
goto exiterr; goto exiterr;
} }
ipv6_only=1;
} }
else else
{ {
@ -897,7 +953,7 @@ int main(int argc, char *argv[]) {
{ {
salisten_len = sizeof(struct sockaddr_in6); salisten_len = sizeof(struct sockaddr_in6);
((struct sockaddr_in6*)&salisten)->sin6_port = htons(params.port); ((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 else
{ {