Introduction
Introduction Statistics Contact Development Disclaimer Help
sdhcp.c - sdhcp - simple dhcp client
git clone git://git.codemadness.org/sdhcp
Log
Files
Refs
LICENSE
---
sdhcp.c (13364B)
---
1 #include <sys/ioctl.h>
2 #include <sys/socket.h>
3 #include <sys/timerfd.h>
4
5 #include <netinet/in.h>
6 #include <net/if.h>
7 #include <net/route.h>
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <limits.h>
12 #include <poll.h>
13 #include <signal.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19 #include <unistd.h>
20
21 #include "arg.h"
22 #include "util.h"
23
24 typedef struct bootp {
25 unsigned char op [1];
26 unsigned char htype [1];
27 unsigned char hlen [1];
28 unsigned char hops [1];
29 unsigned char xid [4];
30 unsigned char secs [2];
31 unsigned char flags [2];
32 unsigned char ciaddr [4];
33 unsigned char yiaddr [4];
34 unsigned char siaddr [4];
35 unsigned char giaddr [4];
36 unsigned char chaddr [16];
37 unsigned char sname [64];
38 unsigned char file [128];
39 unsigned char magic [4];
40 unsigned char optdata [312-4];
41 } Bootp;
42
43 enum {
44 DHCPdiscover = 1,
45 DHCPoffer,
46 DHCPrequest,
47 DHCPdecline,
48 DHCPack,
49 DHCPnak,
50 DHCPrelease,
51 DHCPinform,
52 Timeout0 = 200,
53 Timeout1,
54 Timeout2,
55
56 Bootrequest = 1,
57 Bootreply = 2,
58 /* bootp flags */
59 Fbroadcast = 1 << 15,
60
61 OBpad = 0,
62 OBmask = 1,
63 OBrouter = 3,
64 OBnameserver = 5,
65 OBdnsserver = 6,
66 OBhostname = 12,
67 OBbaddr = 28,
68 ODipaddr = 50, /* 0x32 */
69 ODlease = 51,
70 ODoverload = 52,
71 ODtype = 53, /* 0x35 */
72 ODserverid = 54, /* 0x36 */
73 ODparams = 55, /* 0x37 */
74 ODmessage = 56,
75 ODmaxmsg = 57,
76 ODrenewaltime = 58,
77 ODrebindingtime = 59,
78 ODvendorclass = 60,
79 ODclientid = 61, /* 0x3d */
80 ODtftpserver = 66,
81 ODbootfile = 67,
82 OBend = 255,
83 };
84
85 enum { Broadcast, Unicast };
86
87 static Bootp bp;
88 static unsigned char magic[] = { 99, 130, 83, 99 };
89
90 /* conf */
91 static unsigned char xid[sizeof(bp.xid)];
92 static unsigned char hwaddr[16];
93 static char hostname[HOST_NAME_MAX + 1];
94 static time_t starttime;
95 static char *ifname = "eth0";
96 static unsigned char cid[16];
97 static char *program = "";
98 static int sock, timers[3];
99 /* sav */
100 static unsigned char server[4];
101 static unsigned char client[4];
102 static unsigned char mask[4];
103 static unsigned char router[4];
104 static unsigned char dns[4];
105
106 static int dflag = 1; /* change DNS in /etc/resolv.conf ? */
107 static int iflag = 1; /* set IP ? */
108 static int fflag = 0; /* run in foreground */
109
110 #define IP(a, b, c, d) (unsigned char[4]){ a, b, c, d }
111
112 static void
113 hnput(unsigned char *dst, uint32_t src, size_t n)
114 {
115 unsigned int i;
116
117 for (i = 0; n--; i++)
118 dst[i] = (src >> (n * 8)) & 0xff;
119 }
120
121 static struct sockaddr *
122 iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port)
123 {
124 struct sockaddr_in *in = (struct sockaddr_in *)ifaddr;
125
126 in->sin_family = AF_INET;
127 in->sin_port = htons(port);
128 memcpy(&(in->sin_addr), ip, sizeof(in->sin_addr));
129
130 return ifaddr;
131 }
132
133 /* sendto UDP wrapper */
134 static ssize_t
135 udpsend(unsigned char ip[4], int fd, void *data, size_t n)
136 {
137 struct sockaddr addr;
138 socklen_t addrlen = sizeof(addr);
139 ssize_t sent;
140
141 iptoaddr(&addr, ip, 67); /* bootp server */
142 if ((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1)
143 eprintf("sendto:");
144
145 return sent;
146 }
147
148 /* recvfrom UDP wrapper */
149 static ssize_t
150 udprecv(unsigned char ip[4], int fd, void *data, size_t n)
151 {
152 struct sockaddr addr;
153 socklen_t addrlen = sizeof(addr);
154 ssize_t r;
155
156 iptoaddr(&addr, ip, 68); /* bootp client */
157 if ((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1)
158 eprintf("recvfrom:");
159
160 return r;
161 }
162
163 static void
164 setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[…
165 {
166 struct ifreq ifreq;
167 struct rtentry rtreq;
168 int fd;
169
170 memset(&ifreq, 0, sizeof(ifreq));
171 memset(&rtreq, 0, sizeof(rtreq));
172
173 strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
174 iptoaddr(&(ifreq.ifr_addr), ip, 0);
175 if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1)
176 eprintf("can't set ip, socket:");
177 ioctl(fd, SIOCSIFADDR, &ifreq);
178 iptoaddr(&(ifreq.ifr_netmask), mask, 0);
179 ioctl(fd, SIOCSIFNETMASK, &ifreq);
180 ifreq.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MUL…
181 ioctl(fd, SIOCSIFFLAGS, &ifreq);
182 /* gw */
183 rtreq.rt_flags = (RTF_UP | RTF_GATEWAY);
184 iptoaddr(&(rtreq.rt_gateway), gateway, 0);
185 iptoaddr(&(rtreq.rt_genmask), IP(0, 0, 0, 0), 0);
186 iptoaddr(&(rtreq.rt_dst), IP(0, 0, 0, 0), 0);
187 ioctl(fd, SIOCADDRT, &rtreq);
188
189 close(fd);
190 }
191
192 static void
193 cat(int dfd, char *src)
194 {
195 char buf[BUFSIZ];
196 int n, fd;
197
198 if ((fd = open(src, O_RDONLY)) == -1)
199 return; /* can't read, but don't error out */
200 while ((n = read(fd, buf, sizeof(buf))) > 0)
201 write(dfd, buf, n);
202 close(fd);
203 }
204
205 static void
206 setdns(unsigned char dns[4])
207 {
208 char buf[128];
209 int fd;
210
211 if ((fd = creat("/etc/resolv.conf", 0644)) == -1) {
212 weprintf("can't change /etc/resolv.conf:");
213 return;
214 }
215 cat(fd, "/etc/resolv.conf.head");
216 if (snprintf(buf, sizeof(buf) - 1, "\nnameserver %d.%d.%d.%d\n",
217 dns[0], dns[1], dns[2], dns[3]) > 0)
218 write(fd, buf, strlen(buf));
219 cat(fd, "/etc/resolv.conf.tail");
220 close(fd);
221 }
222
223 static void
224 optget(Bootp *bp, void *data, int opt, int n)
225 {
226 unsigned char *p = bp->optdata;
227 unsigned char *top = ((unsigned char *)bp) + sizeof(*bp);
228 int code, len;
229
230 while (p < top) {
231 code = *p++;
232 if (code == OBpad)
233 continue;
234 if (code == OBend || p == top)
235 break;
236 len = *p++;
237 if (len > top - p)
238 break;
239 if (code == opt) {
240 memcpy(data, p, MIN(len, n));
241 break;
242 }
243 p += len;
244 }
245 }
246
247 static unsigned char *
248 optput(unsigned char *p, int opt, unsigned char *data, size_t len)
249 {
250 *p++ = opt;
251 *p++ = (unsigned char)len;
252 memcpy(p, data, len);
253
254 return p + len;
255 }
256
257 static unsigned char *
258 hnoptput(unsigned char *p, int opt, uint32_t data, size_t len)
259 {
260 *p++ = opt;
261 *p++ = (unsigned char)len;
262 hnput(p, data, len);
263
264 return p + len;
265 }
266
267 static void
268 dhcpsend(int type, int how)
269 {
270 unsigned char *ip, *p;
271
272 memset(&bp, 0, sizeof(bp));
273 hnput(bp.op, Bootrequest, 1);
274 hnput(bp.htype, 1, 1);
275 hnput(bp.hlen, 6, 1);
276 memcpy(bp.xid, xid, sizeof(xid));
277 hnput(bp.flags, Fbroadcast, sizeof(bp.flags));
278 hnput(bp.secs, time(NULL) - starttime, sizeof(bp.secs));
279 memcpy(bp.magic, magic, sizeof(bp.magic));
280 memcpy(bp.chaddr, hwaddr, sizeof(bp.chaddr));
281 p = bp.optdata;
282 p = hnoptput(p, ODtype, type, 1);
283 p = optput(p, ODclientid, cid, sizeof(cid));
284 p = optput(p, OBhostname, (unsigned char *)hostname, strlen(host…
285
286 switch (type) {
287 case DHCPdiscover:
288 break;
289 case DHCPrequest:
290 /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */
291 p = optput(p, ODipaddr, client, sizeof(client));
292 p = optput(p, ODserverid, server, sizeof(server));
293 break;
294 case DHCPrelease:
295 memcpy(bp.ciaddr, client, sizeof(client));
296 p = optput(p, ODipaddr, client, sizeof(client));
297 p = optput(p, ODserverid, server, sizeof(server));
298 break;
299 }
300 *p++ = OBend;
301
302 ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server;
303 udpsend(ip, sock, &bp, p - (unsigned char *)&bp);
304 }
305
306 static int
307 dhcprecv(void)
308 {
309 unsigned char type;
310 struct pollfd pfd[] = {
311 { .fd = sock, .events = POLLIN },
312 { .fd = timers[0], .events = POLLIN },
313 { .fd = timers[1], .events = POLLIN },
314 { .fd = timers[2], .events = POLLIN },
315 };
316 uint64_t n;
317
318 if (poll(pfd, LEN(pfd), -1) == -1)
319 eprintf("poll:");
320 if (pfd[0].revents) {
321 memset(&bp, 0, sizeof(bp));
322 udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof(bp));
323 optget(&bp, &type, ODtype, sizeof(type));
324 return type;
325 }
326 if (pfd[1].revents) {
327 type = Timeout0;
328 read(timers[0], &n, sizeof(n));
329 }
330 if (pfd[2].revents) {
331 type = Timeout1;
332 read(timers[1], &n, sizeof(n));
333 }
334 if (pfd[3].revents) {
335 type = Timeout2;
336 read(timers[2], &n, sizeof(n));
337 }
338 return type;
339 }
340
341 static void
342 acceptlease(void)
343 {
344 char buf[128];
345
346 if (iflag)
347 setip(client, mask, router);
348 if (dflag)
349 setdns(dns);
350 if (*program) {
351 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", server[0], ser…
352 setenv("SERVER", buf, 1);
353 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", client[0], cli…
354 setenv("CLIENT", buf, 1);
355 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", mask[0], mask[…
356 setenv("MASK", buf, 1);
357 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", router[0], rou…
358 setenv("ROUTER", buf, 1);
359 snprintf(buf, sizeof(buf), "%d.%d.%d.%d", dns[0], dns[1]…
360 setenv("DNS", buf, 1);
361 system(program);
362 }
363 }
364
365 static void
366 settimeout(int n, const struct itimerspec *ts)
367 {
368 if (timerfd_settime(timers[n], 0, ts, NULL) < 0)
369 eprintf("timerfd_settime:");
370 }
371
372 /* sets ts to expire halfway to the expiration of timer n, minimum of 60…
373 static void
374 calctimeout(int n, struct itimerspec *ts)
375 {
376 if (timerfd_gettime(timers[n], ts) < 0)
377 eprintf("timerfd_gettime:");
378 ts->it_value.tv_nsec /= 2;
379 if (ts->it_value.tv_sec % 2)
380 ts->it_value.tv_nsec += 500000000;
381 ts->it_value.tv_sec /= 2;
382 if (ts->it_value.tv_sec < 60) {
383 ts->it_value.tv_sec = 60;
384 ts->it_value.tv_nsec = 0;
385 }
386 }
387
388 static void
389 run(void)
390 {
391 int forked = 0, t;
392 struct itimerspec timeout = { 0 };
393 uint32_t renewaltime, rebindingtime, lease;
394
395 Init:
396 dhcpsend(DHCPdiscover, Broadcast);
397 timeout.it_value.tv_sec = 1;
398 timeout.it_value.tv_nsec = 0;
399 settimeout(0, &timeout);
400 goto Selecting;
401 Selecting:
402 for (;;) {
403 switch (dhcprecv()) {
404 case DHCPoffer:
405 memcpy(client, bp.yiaddr, sizeof(client));
406 optget(&bp, server, ODserverid, sizeof(server));
407 goto Requesting;
408 case Timeout0:
409 goto Init;
410 }
411 }
412 Requesting:
413 for (t = 4; t <= 64; t *= 2) {
414 dhcpsend(DHCPrequest, Broadcast);
415 timeout.it_value.tv_sec = t;
416 settimeout(0, &timeout);
417 for (;;) {
418 switch (dhcprecv()) {
419 case DHCPack:
420 goto Bound;
421 case DHCPnak:
422 goto Init;
423 case Timeout0:
424 break;
425 default:
426 continue;
427 }
428 break;
429 }
430 }
431 /* no response from DHCPREQUEST after several attempts, go to IN…
432 goto Init;
433 Bound:
434 optget(&bp, mask, OBmask, sizeof(mask));
435 optget(&bp, router, OBrouter, sizeof(router));
436 optget(&bp, dns, OBdnsserver, sizeof(dns));
437 optget(&bp, &renewaltime, ODrenewaltime, sizeof(renewaltime));
438 optget(&bp, &rebindingtime, ODrebindingtime, sizeof(rebindingtim…
439 optget(&bp, &lease, ODlease, sizeof(lease));
440 renewaltime = ntohl(renewaltime);
441 rebindingtime = ntohl(rebindingtime);
442 lease = ntohl(lease);
443 acceptlease();
444 fputs("Congrats! You should be on the 'net.\n", stdout);
445 if (!fflag && !forked) {
446 if (fork())
447 exit(0);
448 forked = 1;
449 }
450 timeout.it_value.tv_sec = renewaltime;
451 settimeout(0, &timeout);
452 timeout.it_value.tv_sec = rebindingtime;
453 settimeout(1, &timeout);
454 timeout.it_value.tv_sec = lease;;
455 settimeout(2, &timeout);
456 for (;;) {
457 switch (dhcprecv()) {
458 case Timeout0: /* t1 elapsed */
459 goto Renewing;
460 case Timeout1: /* t2 elapsed */
461 goto Rebinding;
462 case Timeout2: /* lease expired */
463 goto Init;
464 }
465 }
466 Renewing:
467 dhcpsend(DHCPrequest, Unicast);
468 calctimeout(1, &timeout);
469 settimeout(0, &timeout);
470 for (;;) {
471 switch (dhcprecv()) {
472 case DHCPack:
473 goto Bound;
474 case Timeout0: /* resend request */
475 goto Renewing;
476 case Timeout1: /* t2 elapsed */
477 goto Rebinding;
478 case Timeout2:
479 case DHCPnak:
480 goto Init;
481 }
482 }
483 Rebinding:
484 calctimeout(2, &timeout);
485 settimeout(0, &timeout);
486 dhcpsend(DHCPrequest, Broadcast);
487 for (;;) {
488 switch (dhcprecv()) {
489 case DHCPack:
490 goto Bound;
491 case Timeout0: /* resend request */
492 goto Rebinding;
493 case Timeout2: /* lease expired */
494 case DHCPnak:
495 goto Init;
496 }
497 }
498 }
499
500 static void
501 cleanexit(int unused)
502 {
503 (void)unused;
504 dhcpsend(DHCPrelease, Unicast);
505 _exit(0);
506 }
507
508 static void
509 usage(void)
510 {
511 eprintf("usage: %s [-d] [-e program] [-f] [-i] [ifname] [clienti…
512 }
513
514 int
515 main(int argc, char *argv[])
516 {
517 int bcast = 1;
518 struct ifreq ifreq;
519 struct sockaddr addr;
520 int rnd;
521 size_t i;
522
523 ARGBEGIN {
524 case 'd': /* don't update DNS in /etc/resolv.conf */
525 dflag = 0;
526 break;
527 case 'e': /* run program */
528 program = EARGF(usage());
529 break;
530 case 'f': /* run in foreground */
531 fflag = 1;
532 break;
533 case 'i': /* don't set ip */
534 iflag = 0;
535 break;
536 default:
537 usage();
538 break;
539 } ARGEND;
540
541 if (argc)
542 ifname = argv[0]; /* interface name */
543 if (argc >= 2)
544 strlcpy((char *)cid, argv[1], sizeof(cid)); /* client-id…
545
546 memset(&ifreq, 0, sizeof(ifreq));
547 signal(SIGTERM, cleanexit);
548
549 if (gethostname(hostname, sizeof(hostname)) == -1)
550 eprintf("gethostname:");
551
552 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
553 eprintf("socket:");
554 if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bc…
555 eprintf("setsockopt:");
556
557 strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
558 ioctl(sock, SIOCGIFINDEX, &ifreq);
559 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof…
560 eprintf("setsockopt:");
561 iptoaddr(&addr, IP(255, 255, 255, 255), 68);
562 if (bind(sock, (void*)&addr, sizeof(addr)) != 0)
563 eprintf("bind:");
564 ioctl(sock, SIOCGIFHWADDR, &ifreq);
565 memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr…
566 if (!cid[0])
567 memcpy(cid, hwaddr, sizeof(cid));
568
569 if ((rnd = open("/dev/urandom", O_RDONLY)) == -1)
570 eprintf("can't open /dev/urandom to generate unique tran…
571 read(rnd, xid, sizeof(xid));
572 close(rnd);
573
574 for (i = 0; i < LEN(timers); ++i) {
575 timers[i] = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC);
576 if (timers[i] == -1)
577 eprintf("timerfd_create:");
578 }
579
580 starttime = time(NULL);
581 run();
582
583 return 0;
584 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.