1 /* $OpenBSD: dhcpleasectl.c,v 1.7 2021/09/16 06:23:01 jmc Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Florian Obser <florian@openbsd.org> 5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 7 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/types.h> 23 #include <sys/ioctl.h> 24 #include <sys/queue.h> 25 #include <sys/socket.h> 26 #include <sys/time.h> 27 #include <sys/un.h> 28 29 #include <arpa/inet.h> 30 31 #include <net/if.h> 32 33 #include <netinet/in.h> 34 #include <netinet/if_ether.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <event.h> 39 #include <imsg.h> 40 #include <limits.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <unistd.h> 46 47 #include "dhcpleased.h" 48 49 __dead void usage(void); 50 void show_interface_msg(struct ctl_engine_info *); 51 52 struct imsgbuf *ibuf; 53 54 __dead void 55 usage(void) 56 { 57 extern char *__progname; 58 59 fprintf(stderr, "usage: %s [-l] [-s socket] [-w maxwait] interface\n", 60 __progname); 61 exit(1); 62 } 63 64 int 65 main(int argc, char *argv[]) 66 { 67 struct sockaddr_un sun; 68 struct imsg imsg; 69 struct ctl_engine_info *cei; 70 int ctl_sock; 71 int n, lFlag = 0, maxwait_set = 0, didot = 0; 72 int ch, if_index = 0, maxwait = 10, bound = 0; 73 char *sockname; 74 const char *errstr; 75 76 sockname = _PATH_DHCPLEASED_SOCKET; 77 while ((ch = getopt(argc, argv, "ls:w:")) != -1) { 78 switch (ch) { 79 case 'l': 80 lFlag = 1; 81 break; 82 case 's': 83 sockname = optarg; 84 break; 85 case 'w': 86 maxwait_set = 1; 87 maxwait = strtonum(optarg, 1, INT_MAX, &errstr); 88 if (errstr) 89 errx(1, "maxwait value is %s: %s", 90 errstr, optarg); 91 break; 92 93 default: 94 usage(); 95 } 96 } 97 argc -= optind; 98 argv += optind; 99 100 if (argc != 1) 101 usage(); 102 103 if ((if_index = if_nametoindex(argv[0])) == 0) 104 errx(1, "unknown interface"); 105 106 if (!lFlag) { 107 struct ifreq ifr, ifr_x; 108 int ioctl_sock; 109 110 if ((ioctl_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 111 err(1, NULL); 112 113 strlcpy(ifr.ifr_name, argv[0], sizeof(ifr.ifr_name)); 114 strlcpy(ifr_x.ifr_name, argv[0], sizeof(ifr.ifr_name)); 115 116 if (ioctl(ioctl_sock, SIOCGIFFLAGS, &ifr) == -1) 117 err(1, "SIOCGIFFLAGS"); 118 119 if (ioctl(ioctl_sock, SIOCGIFXFLAGS, &ifr_x) == -1) 120 err(1, "SIOCGIFFLAGS"); 121 122 if (!(ifr.ifr_flags & IFF_UP) || 123 !(ifr_x.ifr_flags & IFXF_AUTOCONF4)) { 124 if (geteuid()) 125 errx(1, "need root privileges"); 126 } 127 128 if (!(ifr.ifr_flags & IFF_UP)) { 129 ifr.ifr_flags |= IFF_UP; 130 if (ioctl(ioctl_sock, SIOCSIFFLAGS, &ifr) == -1) 131 err(1, "SIOCSIFFLAGS"); 132 } 133 if (!(ifr_x.ifr_flags & IFXF_AUTOCONF4)) { 134 ifr_x.ifr_flags |= IFXF_AUTOCONF4; 135 if (ioctl(ioctl_sock, SIOCSIFXFLAGS, &ifr_x) == -1) 136 err(1, "SIOCSIFFLAGS"); 137 } 138 } 139 140 if (lFlag && !maxwait_set) 141 maxwait = 0; 142 143 /* Connect to control socket. */ 144 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 145 err(1, "socket"); 146 147 memset(&sun, 0, sizeof(sun)); 148 sun.sun_family = AF_UNIX; 149 strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)); 150 151 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 152 err(1, "connect: %s", sockname); 153 154 if (pledge("stdio", NULL) == -1) 155 err(1, "pledge"); 156 157 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 158 err(1, NULL); 159 imsg_init(ibuf, ctl_sock); 160 161 if (!lFlag) { 162 imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1, 163 &if_index, sizeof(if_index)); 164 while (ibuf->w.queued) 165 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 166 err(1, "write error"); 167 168 } 169 170 for(;;) { 171 imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1, 172 &if_index, sizeof(if_index)); 173 174 while (ibuf->w.queued) 175 if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) 176 err(1, "write error"); 177 178 179 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 180 errx(1, "imsg_read error"); 181 if (n == 0) 182 errx(1, "pipe closed"); 183 184 if ((n = imsg_get(ibuf, &imsg)) == -1) 185 errx(1, "imsg_get error"); 186 if (n == 0) 187 break; 188 189 if (imsg.hdr.type == IMSG_CTL_END) { 190 if (lFlag) 191 errx(1, "non-autoconf interface %s", argv[0]); 192 else if (--maxwait < 0) 193 break; 194 else 195 continue; 196 } 197 198 cei = imsg.data; 199 if (strcmp(cei->state, "Bound") == 0) 200 bound = 1; 201 202 if (bound || --maxwait < 0) { 203 if (didot) 204 putchar('\n'); 205 show_interface_msg(cei); 206 break; 207 } else { 208 didot = 1; 209 putchar('.'); 210 fflush(stdout); 211 } 212 imsg_free(&imsg); 213 sleep(1); 214 } 215 close(ctl_sock); 216 free(ibuf); 217 218 return (0); 219 } 220 221 void 222 show_interface_msg(struct ctl_engine_info *cei) 223 { 224 struct timespec now, diff; 225 time_t d, h, m, s; 226 int i; 227 char buf[IF_NAMESIZE], *bufp; 228 char ipbuf[INET_ADDRSTRLEN]; 229 char maskbuf[INET_ADDRSTRLEN]; 230 char gwbuf[INET_ADDRSTRLEN]; 231 232 bufp = if_indextoname(cei->if_index, buf); 233 printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state); 234 memset(ipbuf, 0, sizeof(ipbuf)); 235 if (cei->requested_ip.s_addr != INADDR_ANY) { 236 clock_gettime(CLOCK_MONOTONIC, &now); 237 timespecsub(&now, &cei->request_time, &diff); 238 memset(ipbuf, 0, sizeof(ipbuf)); 239 memset(maskbuf, 0, sizeof(maskbuf)); 240 memset(gwbuf, 0, sizeof(gwbuf)); 241 if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf, 242 sizeof(ipbuf)) == NULL) 243 ipbuf[0] = '\0'; 244 if (inet_ntop(AF_INET, &cei->mask, maskbuf, sizeof(maskbuf)) 245 == NULL) 246 maskbuf[0] = '\0'; 247 printf("\tinet %s netmask %s\n", ipbuf, maskbuf); 248 for (i = 0; i < cei->routes_len; i++) { 249 if (inet_ntop(AF_INET, &cei->routes[i].dst, ipbuf, 250 sizeof(ipbuf)) == NULL) 251 ipbuf[0] = '\0'; 252 if (inet_ntop(AF_INET, &cei->routes[i].mask, maskbuf, 253 sizeof(maskbuf)) == NULL) 254 maskbuf[0] = '\0'; 255 if (inet_ntop(AF_INET, &cei->routes[i].gw, 256 gwbuf, sizeof(gwbuf)) == NULL) 257 gwbuf[0] = '\0'; 258 259 if (cei->routes[i].dst.s_addr == INADDR_ANY 260 && cei->routes[i].mask.s_addr == INADDR_ANY) 261 printf("\tdefault gateway %s\n", gwbuf); 262 else 263 printf("\troute %s/%d gateway %s\n", 264 ipbuf, 33 - 265 ffs(ntohl(cei->routes[i].mask.s_addr)), 266 gwbuf); 267 } 268 if (cei->nameservers[0].s_addr != INADDR_ANY) { 269 printf("\tnameservers"); 270 for (i = 0; i < MAX_RDNS_COUNT && 271 cei->nameservers[i].s_addr != INADDR_ANY; 272 i++) { 273 if (inet_ntop(AF_INET, &cei->nameservers[i], 274 ipbuf, sizeof(ipbuf)) == NULL) 275 continue; 276 printf(" %s", ipbuf); 277 } 278 printf("\n"); 279 } 280 s = cei->lease_time - diff.tv_sec; 281 if (s < 0) 282 s = 0; 283 284 if ( s > 86400 ) { 285 d = s / 86400; 286 287 /* round up */ 288 if (s - d * 86400 > 43200) 289 d++; 290 printf("\tlease %lld days\n", d); 291 } else if (s > 3600) { 292 h = s / 3600; 293 294 /* round up */ 295 if (s - h * 3600 > 1800) 296 h++; 297 printf("\tlease %lld hours\n", h); 298 } else if (s > 60) { 299 m = s / 60; 300 301 /* round up */ 302 if (s - m * 60 > 30) 303 m++; 304 printf("\tlease %lld minutes\n", m); 305 } else 306 printf("\tlease %lld seconds\n", s); 307 } 308 if (cei->server_identifier.s_addr != INADDR_ANY) { 309 if (inet_ntop(AF_INET, &cei->server_identifier, ipbuf, 310 sizeof(ipbuf)) == NULL) 311 ipbuf[0] = '\0'; 312 } else if (cei->dhcp_server.s_addr != INADDR_ANY) { 313 if (inet_ntop(AF_INET, &cei->dhcp_server, ipbuf, sizeof(ipbuf)) 314 == NULL) 315 ipbuf[0] = '\0'; 316 } 317 if (ipbuf[0] != '\0') 318 printf("\tdhcp server %s\n", ipbuf); 319 } 320