sdhcp.c (11053B)
1 #include <sys/socket.h> 2 #include <sys/ioctl.h> 3 #include <netinet/in.h> 4 #include <net/if.h> 5 #include <net/route.h> 6 #include <signal.h> 7 #include <poll.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <time.h> 14 #include <unistd.h> 15 16 #include "util.h" 17 #include "arg.h" 18 19 typedef struct bootp { 20 unsigned char op [1]; 21 unsigned char htype [1]; 22 unsigned char hlen [1]; 23 unsigned char hops [1]; 24 unsigned char xid [4]; 25 unsigned char secs [2]; 26 unsigned char flags [2]; 27 unsigned char ciaddr [4]; 28 unsigned char yiaddr [4]; 29 unsigned char siaddr [4]; 30 unsigned char giaddr [4]; 31 unsigned char chaddr [16]; 32 unsigned char sname [64]; 33 unsigned char file [128]; 34 unsigned char magic [4]; 35 unsigned char optdata [312-4]; 36 } Bootp; 37 38 enum { 39 DHCPdiscover = 1, 40 DHCPoffer, 41 DHCPrequest, 42 DHCPdecline, 43 DHCPack, 44 DHCPnak, 45 DHCPrelease, 46 DHCPinform, 47 Timeout = 200, 48 49 Bootrequest = 1, 50 Bootreply = 2, 51 /* bootp flags */ 52 Fbroadcast = 1 << 15, 53 54 OBpad = 0, 55 OBmask = 1, 56 OBrouter = 3, 57 OBnameserver = 5, 58 OBdnsserver = 6, 59 OBbaddr = 28, 60 ODipaddr = 50, /* 0x32 */ 61 ODlease = 51, 62 ODoverload = 52, 63 ODtype = 53, /* 0x35 */ 64 ODserverid = 54, /* 0x36 */ 65 ODparams = 55, /* 0x37 */ 66 ODmessage = 56, 67 ODmaxmsg = 57, 68 ODrenewaltime = 58, 69 ODrebindingtime = 59, 70 ODvendorclass = 60, 71 ODclientid = 61, /* 0x3d */ 72 ODtftpserver = 66, 73 ODbootfile = 67, 74 OBend = 255, 75 }; 76 77 enum { Broadcast, Unicast}; 78 79 Bootp bp; 80 unsigned char magic[] = {99, 130, 83, 99}; 81 82 /* conf */ 83 static unsigned char xid[sizeof bp.xid]; 84 static unsigned char hwaddr[16]; 85 static time_t starttime; 86 static char *ifname = "eth0"; 87 static char *cid = ""; 88 static char *program = ""; 89 static int sock; 90 /* sav */ 91 static unsigned char server[4]; 92 static unsigned char client[4]; 93 static unsigned char mask[4]; 94 static unsigned char router[4]; 95 static unsigned char dns[4]; 96 static unsigned long t1; 97 98 static int dflag = 1; /* change DNS in /etc/resolv.conf ? */ 99 static int iflag = 1; /* set IP ? */ 100 101 #define IP(a,b,c,d) (unsigned char[4]){a,b,c,d} 102 103 static void 104 hnput(unsigned char *dst, unsigned long long src, size_t n) 105 { 106 unsigned int i; 107 108 for(i = 0; n--; i++) 109 dst[i] = (src >> (n * 8)) & 0xff; 110 } 111 112 static struct sockaddr * 113 iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port) 114 { 115 struct sockaddr_in *in = (struct sockaddr_in *)ifaddr; 116 117 in->sin_family = AF_INET; 118 in->sin_port = htons(port); 119 memcpy(&(in->sin_addr), ip, sizeof in->sin_addr); 120 return ifaddr; 121 } 122 123 /* sendto UDP wrapper */ 124 static ssize_t 125 udpsend(unsigned char ip[4], int fd, void *data, size_t n) 126 { 127 struct sockaddr addr; 128 socklen_t addrlen = sizeof addr; 129 ssize_t sent; 130 131 iptoaddr(&addr, ip, 67); /* bootp server */ 132 if((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1) 133 eprintf("sendto:"); 134 return sent; 135 } 136 137 /* recvfrom UDP wrapper */ 138 static ssize_t 139 udprecv(unsigned char ip[4], int fd, void *data, size_t n) 140 { 141 struct sockaddr addr; 142 socklen_t addrlen = sizeof addr; 143 ssize_t r; 144 145 iptoaddr(&addr, ip, 68); /* bootp client */ 146 if((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1) 147 eprintf("recvfrom:"); 148 return r; 149 } 150 151 static void 152 setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4]) 153 { 154 struct ifreq ifreq; 155 struct rtentry rtreq; 156 int fd; 157 158 memset(&ifreq, 0, sizeof(ifreq)); 159 memset(&rtreq, 0, sizeof(rtreq)); 160 161 strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); 162 iptoaddr(&(ifreq.ifr_addr), ip, 0); 163 if((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) 164 eprintf("can't set ip, socket:"); 165 ioctl(fd, SIOCSIFADDR, &ifreq); 166 iptoaddr(&(ifreq.ifr_netmask), mask, 0); 167 ioctl(fd, SIOCSIFNETMASK, &ifreq); 168 ifreq.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST; 169 ioctl(fd, SIOCSIFFLAGS, &ifreq); 170 /* gw */ 171 rtreq.rt_flags = (RTF_UP | RTF_GATEWAY); 172 iptoaddr(&(rtreq.rt_gateway), gateway, 0); 173 iptoaddr(&(rtreq.rt_genmask), IP(0, 0, 0, 0), 0); 174 iptoaddr(&(rtreq.rt_dst), IP(0, 0, 0, 0), 0); 175 ioctl(fd, SIOCADDRT, &rtreq); 176 177 close(fd); 178 } 179 180 static void 181 cat(int dfd, char *src) 182 { 183 char buf[BUFSIZ]; 184 int n, fd; 185 186 if((fd = open(src, O_RDONLY)) == -1) 187 return; /* can't read, but don't error out */ 188 while((n = read(fd, buf, sizeof buf)) > 0) 189 write(dfd, buf, n); 190 close(fd); 191 } 192 193 static void 194 setdns(unsigned char dns[4]) 195 { 196 char buf[128]; 197 int fd; 198 199 if((fd = creat("/etc/resolv.conf", 0644)) == -1) { 200 weprintf("can't change /etc/resolv.conf:"); 201 return; 202 } 203 cat(fd, "/etc/resolv.conf.head"); 204 if(snprintf(buf, sizeof(buf) - 1, "\nnameserver %d.%d.%d.%d\n", 205 dns[0], dns[1], dns[2], dns[3]) > 0) 206 write(fd, buf, strlen(buf)); 207 cat(fd, "/etc/resolv.conf.tail"); 208 close(fd); 209 } 210 211 static void 212 optget(Bootp *bp, void *data, int opt, int n) 213 { 214 unsigned char *p = bp->optdata; 215 unsigned char *top = ((unsigned char *)bp) + sizeof *bp; 216 int code, len; 217 218 while(p < top) { 219 code = *p++; 220 if(code == OBpad) 221 continue; 222 if(code == OBend || p == top) 223 break; 224 len = *p++; 225 if(len > top - p) 226 break; 227 if(code == opt) { 228 memcpy(data, p, MIN(len, n)); 229 return; 230 } 231 p += len; 232 } 233 } 234 235 static unsigned char * 236 optput(unsigned char *p, int opt, unsigned char *data, size_t len) 237 { 238 *p++ = opt; 239 *p++ = (unsigned char)len; 240 memcpy(p, data, len); 241 return p + len; 242 } 243 244 static unsigned char * 245 hnoptput(unsigned char *p, int opt, long long data, size_t len) 246 { 247 *p++ = opt; 248 *p++ = (unsigned char)len; 249 hnput(p, data, len); 250 return p + len; 251 } 252 253 static void 254 dhcpsend(int type, int how) 255 { 256 unsigned char *ip, *p; 257 258 memset(&bp, 0, sizeof bp); 259 hnput(bp.op, Bootrequest, 1); 260 hnput(bp.htype, 1, 1); 261 hnput(bp.hlen, 6, 1); 262 memcpy(bp.xid, xid, sizeof xid); 263 hnput(bp.flags, Fbroadcast, sizeof bp.flags); 264 hnput(bp.secs, time(NULL) - starttime, sizeof bp.secs); 265 memcpy(bp.magic, magic, sizeof bp.magic); 266 memcpy(bp.chaddr, hwaddr, sizeof bp.chaddr); 267 p = bp.optdata; 268 p = hnoptput(p, ODtype, type, 1); 269 p = optput(p, ODclientid, (unsigned char*)cid, strlen(cid)); 270 271 switch(type) { 272 case DHCPdiscover: 273 break; 274 case DHCPrequest: 275 /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */ 276 p = hnoptput(p, ODlease, t1, sizeof t1); 277 p = optput(p, ODipaddr, client, sizeof client); 278 p = optput(p, ODserverid, server, sizeof server); 279 break; 280 case DHCPrelease: 281 memcpy(bp.ciaddr, client, sizeof client); 282 p = optput(p, ODipaddr, client, sizeof client); 283 p = optput(p, ODserverid, server, sizeof server); 284 break; 285 } 286 *p++ = OBend; 287 288 ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server; 289 udpsend(ip, sock, &bp, p - (unsigned char *)&bp); 290 } 291 292 static int 293 dhcprecv(void) 294 { 295 unsigned char type; 296 struct pollfd pfd; 297 298 memset(&pfd, 0, sizeof(pfd)); 299 pfd.fd = sock; 300 pfd.events = POLLIN; 301 302 memset(&bp, 0, sizeof bp); 303 if(poll(&pfd, 1, -1) == -1) { 304 if(errno != EINTR) 305 eprintf("poll:"); 306 else 307 return Timeout; 308 } 309 udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof bp); 310 optget(&bp, &type, ODtype, sizeof type); 311 return type; 312 } 313 314 static void 315 acceptlease(void) 316 { 317 char buf[128]; 318 319 if(iflag == 1) 320 setip(client, mask, router); 321 if(dflag == 1) 322 setdns(dns); 323 if(*program) { 324 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", server[0], server[1], server[2], server[3]); 325 setenv("SERVER", buf, 1); 326 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", client[0], client[1], client[2], client[3]); 327 setenv("CLIENT", buf, 1); 328 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]); 329 setenv("MASK", buf, 1); 330 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", router[0], router[1], router[2], router[3]); 331 setenv("ROUTER", buf, 1); 332 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", dns[0], dns[1], dns[2], dns[3]); 333 setenv("DNS", buf, 1); 334 system(program); 335 } 336 alarm(t1); 337 } 338 339 static void 340 run(void) 341 { 342 #if 0 343 InitReboot: 344 /* send DHCPrequest to old server */ 345 dhcpsend(DHCPrequest, Broadcast); 346 goto Rebooting; 347 Rebooting: 348 switch (dhcprecv()) { 349 case DHCPnak: 350 goto Init; 351 case DHCPack: 352 acceptoffer(); 353 goto Bound; 354 } 355 #endif 356 Init: 357 dhcpsend(DHCPdiscover, Broadcast); 358 alarm(1); 359 goto Selecting; 360 Selecting: 361 switch(dhcprecv()) { 362 case DHCPoffer: 363 alarm(0); 364 memcpy(client, bp.yiaddr, sizeof client); 365 optget(&bp, server, ODserverid, sizeof server); 366 optget(&bp, mask, OBmask, sizeof mask); 367 optget(&bp, router, OBrouter, sizeof router); 368 optget(&bp, dns, OBdnsserver, sizeof dns); 369 optget(&bp, &t1, ODlease, sizeof t1); 370 t1 = ntohl(t1); 371 dhcpsend(DHCPrequest, Broadcast); 372 goto Requesting; 373 case Timeout: 374 goto Init; 375 default: 376 goto Selecting; 377 } 378 Requesting: 379 switch(dhcprecv()) { 380 case DHCPoffer: 381 goto Requesting; /* ignore other offers. */ 382 #if 0 383 case DHCPack: /* (and you don't want it) ? */ 384 dhcpsend(DHCPdecline, Unicast); 385 goto Init; 386 #endif 387 case DHCPack: 388 acceptlease(); 389 goto Bound; 390 } 391 Bound: 392 fputs("Congrats! You should be on the 'net.\n", stdout); 393 if(fork()) 394 exit(EXIT_SUCCESS); 395 switch (dhcprecv()) { 396 case DHCPoffer: 397 case DHCPack: 398 case DHCPnak: 399 goto Bound; /* discard offer, ACK or NAK */ 400 case Timeout: 401 dhcpsend(DHCPrequest, Unicast); 402 goto Renewing; 403 } 404 Renewing: 405 switch(dhcprecv()) { 406 case DHCPack: 407 acceptlease(); 408 goto Bound; 409 case DHCPnak: 410 goto Init; 411 case Timeout: 412 dhcpsend(DHCPrequest, Broadcast); 413 goto Rebinding; 414 } 415 Rebinding: 416 switch(dhcprecv()) { 417 case DHCPnak: /* lease expired */ 418 goto Init; 419 case DHCPack: 420 acceptlease(); 421 goto Bound; 422 } 423 } 424 425 static void nop(int unused) { 426 (void) unused; 427 } 428 429 static void cleanexit(int unused) { 430 (void) unused; 431 dhcpsend(DHCPrelease, Unicast); 432 exit(EXIT_SUCCESS); 433 } 434 435 static void 436 usage(void) { 437 eprintf("usage: sdhcp [-i] [-d] [-e program] [ifname] [clientid]\n"); 438 } 439 440 int 441 main(int argc, char *argv[]) 442 { 443 int bcast = 1; 444 struct ifreq ifreq; 445 struct sockaddr addr; 446 int rnd; 447 448 ARGBEGIN { 449 case 'e': /* run program */ 450 program = EARGF(usage()); 451 break; 452 case 'i': /* don't set ip */ 453 iflag = 0; 454 break; 455 case 'd': /* don't update DNS in/etc/resolv.conf */ 456 dflag = 0; 457 break; 458 default: 459 usage(); 460 break; 461 } ARGEND; 462 463 if(argc >= 1) 464 ifname = argv[0]; /* interface name */ 465 if(argc >= 2) 466 cid = argv[1]; /* client-id */ 467 468 memset(&ifreq, 0, sizeof(ifreq)); 469 signal(SIGALRM, nop); 470 signal(SIGTERM, cleanexit); 471 472 if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 473 eprintf("socket:"); 474 if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof bcast) == -1) 475 eprintf("setsockopt:"); 476 477 strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); 478 ioctl(sock, SIOCGIFINDEX, &ifreq); 479 if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof ifreq) == -1) 480 eprintf("setsockopt:"); 481 iptoaddr(&addr, IP(255, 255, 255, 255), 68); 482 if(bind(sock, (void*)&addr, sizeof addr) != 0) 483 eprintf("bind:"); 484 ioctl(sock, SIOCGIFHWADDR, &ifreq); 485 memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof ifreq.ifr_hwaddr.sa_data); 486 487 if((rnd = open("/dev/urandom", O_RDONLY)) == -1) 488 eprintf("can't open /dev/urandom to generate unique transaction identifier:"); 489 read(rnd, xid, sizeof xid); 490 close(rnd); 491 492 starttime = time(NULL); 493 run(); 494 return EXIT_SUCCESS; 495 }