1 /* $OpenBSD: dhcp6leasectl.c,v 1.6 2024/11/21 13:38:14 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2021, 2024 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 "dhcp6leased.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_CTRL_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 && !maxwait_set) 107 maxwait = 0; 108 109 /* Connect to control socket. */ 110 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 111 err(1, "socket"); 112 113 memset(&sun, 0, sizeof(sun)); 114 sun.sun_family = AF_UNIX; 115 strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)); 116 117 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 118 err(1, "connect: %s", sockname); 119 120 if (pledge("stdio", NULL) == -1) 121 err(1, "pledge"); 122 123 if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) 124 err(1, NULL); 125 if (imsgbuf_init(ibuf, ctl_sock) == -1) 126 err(1, NULL); 127 128 if (!lFlag) { 129 imsg_compose(ibuf, IMSG_CTL_SEND_REQUEST, 0, 0, -1, 130 &if_index, sizeof(if_index)); 131 if (imsgbuf_flush(ibuf) == -1) 132 err(1, "write error"); 133 134 } 135 136 for(;;) { 137 imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE_INFO, 0, 0, -1, 138 &if_index, sizeof(if_index)); 139 140 if (imsgbuf_flush(ibuf) == -1) 141 err(1, "write error"); 142 143 if ((n = imsgbuf_read(ibuf)) == -1) 144 err(1, "read error"); 145 if (n == 0) 146 errx(1, "pipe closed"); 147 148 if ((n = imsg_get(ibuf, &imsg)) == -1) 149 errx(1, "imsg_get error"); 150 if (n == 0) 151 break; 152 153 if (imsg.hdr.type == IMSG_CTL_END) { 154 if (lFlag) 155 errx(1, "non-autoconf interface %s", argv[0]); 156 else if (--maxwait < 0) 157 break; 158 else 159 continue; 160 } 161 162 cei = imsg.data; 163 if (strcmp(cei->state, "Bound") == 0) 164 bound = 1; 165 166 if (bound || --maxwait < 0) { 167 if (didot) 168 putchar('\n'); 169 show_interface_msg(cei); 170 break; 171 } else { 172 didot = 1; 173 putchar('.'); 174 fflush(stdout); 175 } 176 imsg_free(&imsg); 177 sleep(1); 178 } 179 close(ctl_sock); 180 free(ibuf); 181 182 return (0); 183 } 184 185 void 186 show_interface_msg(struct ctl_engine_info *cei) 187 { 188 struct timespec now, diff; 189 time_t d, h, m, s; 190 int i, has_pd = 0; 191 char buf[IF_NAMESIZE], *bufp; 192 char ntopbuf[INET6_ADDRSTRLEN]; 193 194 bufp = if_indextoname(cei->if_index, buf); 195 printf("%s [%s]\n", bufp != NULL ? bufp : "unknown", cei->state); 196 197 for (i = 0; i < MAX_IA; i++) { 198 if (cei->pds[i].prefix_len == 0) 199 continue; 200 has_pd = 1; 201 printf ("\tIA_PD %d: %s/%d\n", i, inet_ntop(AF_INET6, 202 &cei->pds[i], ntopbuf, INET6_ADDRSTRLEN), 203 cei->pds[i].prefix_len); 204 } 205 206 if (has_pd) { 207 clock_gettime(CLOCK_MONOTONIC, &now); 208 timespecsub(&now, &cei->request_time, &diff); 209 s = cei->lease_time - diff.tv_sec; 210 if (s < 0) 211 s = 0; 212 213 if ( s > 86400 ) { 214 d = s / 86400; 215 216 /* round up */ 217 if (s - d * 86400 > 43200) 218 d++; 219 printf("\tlease %lld day%s\n", d, d > 1 ? "s" : ""); 220 } else if (s > 3600) { 221 h = s / 3600; 222 223 /* round up */ 224 if (s - h * 3600 > 1800) 225 h++; 226 printf("\tlease %lld hour%s\n", h, h > 1 ? "s" : ""); 227 } else if (s > 60) { 228 m = s / 60; 229 230 /* round up */ 231 if (s - m * 60 > 30) 232 m++; 233 printf("\tlease %lld minute%s\n", m, m > 1 ? "s" : ""); 234 } else 235 printf("\tlease %lld second%s\n", s, s > 1 ? "s" : ""); 236 237 } 238 } 239