tdhcpd.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tdhcpd.c (33602B) | |
--- | |
1 #include <u.h> | |
2 #include <sys/socket.h> | |
3 #include <net/if_arp.h> | |
4 #include <netinet/ip.h> | |
5 #include <sys/ioctl.h> | |
6 #include <libc.h> | |
7 #include <ip.h> | |
8 #include <bio.h> | |
9 #include <ndb.h> | |
10 #include "dat.h" | |
11 | |
12 int bwfd; | |
13 int wfd; | |
14 | |
15 /* */ | |
16 /* ala rfc2131 */ | |
17 /* */ | |
18 | |
19 typedef struct Req Req; | |
20 struct Req | |
21 { | |
22 int fd; /* for reply */ | |
23 Bootp *bp; | |
24 Udphdr uh; | |
25 Udphdr *up; | |
26 uchar *e; /* end of received messa… | |
27 uchar *p; /* options pointer */ | |
28 uchar *max; /* max end of reply */ | |
29 | |
30 /* expanded to v6 */ | |
31 uchar ciaddr[IPaddrlen]; | |
32 uchar giaddr[IPaddrlen]; | |
33 | |
34 /* parsed options */ | |
35 int p9request; /* true if this is a bootp … | |
36 int genrequest; /* true if this is a bootp… | |
37 int dhcptype; /* dhcp message type */ | |
38 int leasetime; /* dhcp lease */ | |
39 uchar ip[IPaddrlen]; /* requested address … | |
40 uchar server[IPaddrlen]; /* server address */ | |
41 char msg[ERRMAX]; /* error message */ | |
42 char vci[32]; /* vendor class id */ | |
43 char *id; /* client id */ | |
44 uchar requested[32]; /* requested params */ | |
45 uchar vendorclass[32]; | |
46 char cputype[32-3]; | |
47 | |
48 Info gii; /* about target network … | |
49 Info ii; /* about target system */ | |
50 int staticbinding; | |
51 | |
52 uchar buf[2*1024]; /* message buffer */ | |
53 }; | |
54 | |
55 #define TFTP "/lib/tftpd" | |
56 char *blog = "ipboot"; | |
57 char mysysname[64]; | |
58 Ipifc *ipifcs; | |
59 int debug; | |
60 int nobootp; | |
61 long now; | |
62 int slow; | |
63 char net[256]; | |
64 uchar xmyipaddr[IPaddrlen]; | |
65 | |
66 int pptponly; /* only answer request that came from the pp… | |
67 int mute; | |
68 int minlease = MinLease; | |
69 | |
70 ulong start; | |
71 | |
72 /* option magic */ | |
73 char plan9opt[4] = { 'p', '9', ' ', ' ' }; | |
74 char genericopt[4] = { 0x63, 0x82, 0x53, 0x63 }; | |
75 | |
76 /* well known addresses */ | |
77 uchar zeros[Maxhwlen]; | |
78 | |
79 /* option debug buffer */ | |
80 char optbuf[1024]; | |
81 char *op; | |
82 char *oe = optbuf + sizeof(optbuf); | |
83 | |
84 char *optname[256] = | |
85 { | |
86 [OBend] "end", | |
87 [OBpad] "pad", | |
88 [OBmask] "mask", | |
89 [OBtimeoff] "timeoff", | |
90 [OBrouter] "router", | |
91 [OBtimeserver] "time", | |
92 [OBnameserver] "name", | |
93 [OBdnserver] "dns", | |
94 [OBlogserver] "log", | |
95 [OBcookieserver] "cookie", | |
96 [OBlprserver] "lpr", | |
97 [OBimpressserver] "impress", | |
98 [OBrlserver] "rl", | |
99 [OBhostname] "host", | |
100 [OBbflen] "bflen", | |
101 [OBdumpfile] "dumpfile", | |
102 [OBdomainname] "dom", | |
103 [OBswapserver] "swap", | |
104 [OBrootpath] "rootpath", | |
105 [OBextpath] "extpath", | |
106 [OBipforward] "ipforward", | |
107 [OBnonlocal] "nonlocal", | |
108 [OBpolicyfilter] "policyfilter", | |
109 [OBmaxdatagram] "maxdatagram", | |
110 [OBttl] "ttl", | |
111 [OBpathtimeout] "pathtimeout", | |
112 [OBpathplateau] "pathplateau", | |
113 [OBmtu] "mtu", | |
114 [OBsubnetslocal] "subnetslocal", | |
115 [OBbaddr] "baddr", | |
116 [OBdiscovermask] "discovermask", | |
117 [OBsupplymask] "supplymask", | |
118 [OBdiscoverrouter] "discoverrouter", | |
119 [OBrsserver] "rsserver", | |
120 [OBstaticroutes] "staticroutes", | |
121 [OBtrailerencap] "trailerencap", | |
122 [OBarptimeout] "arptimeout", | |
123 [OBetherencap] "etherencap", | |
124 [OBtcpttl] "tcpttl", | |
125 [OBtcpka] "tcpka", | |
126 [OBtcpkag] "tcpkag", | |
127 [OBnisdomain] "nisdomain", | |
128 [OBniserver] "niserver", | |
129 [OBntpserver] "ntpserver", | |
130 [OBvendorinfo] "vendorinfo", | |
131 [OBnetbiosns] "NBns", | |
132 [OBnetbiosdds] "NBdds", | |
133 [OBnetbiostype] "NBtype", | |
134 [OBnetbiosscope] "NBscope", | |
135 [OBxfontserver] "xfont", | |
136 [OBxdispmanager] "xdisp", | |
137 [OBnisplusdomain] "NPdomain", | |
138 [OBnisplusserver] "NP", | |
139 [OBhomeagent] "homeagent", | |
140 [OBsmtpserver] "smtp", | |
141 [OBpop3server] "pop3", | |
142 [OBnntpserver] "nntp", | |
143 [OBwwwserver] "www", | |
144 [OBfingerserver] "finger", | |
145 [OBircserver] "ircserver", | |
146 [OBstserver] "stserver", | |
147 [OBstdaserver] "stdaserver", | |
148 | |
149 /* dhcp options */ | |
150 [ODipaddr] "ip", | |
151 [ODlease] "leas", | |
152 [ODoverload] "overload", | |
153 [ODtype] "typ", | |
154 [ODserverid] "sid", | |
155 [ODparams] "params", | |
156 [ODmessage] "message", | |
157 [ODmaxmsg] "maxmsg", | |
158 [ODrenewaltime] "renewaltime", | |
159 [ODrebindingtime] "rebindingtime", | |
160 [ODvendorclass] "vendorclass", | |
161 [ODclientid] "cid", | |
162 [ODtftpserver] "tftpserver", | |
163 [ODbootfile] "bf" | |
164 }; | |
165 | |
166 void addropt(Req*, int, uchar*); | |
167 void addrsopt(Req*, int, uchar**, int); | |
168 void arpenter(uchar*, uchar*); | |
169 void bootp(Req*); | |
170 void byteopt(Req*, int, uchar); | |
171 void dhcp(Req*); | |
172 void fatal(int, char*, ...); | |
173 void hexopt(Req*, int, char*); | |
174 void longopt(Req*, int, long); | |
175 void maskopt(Req*, int, uchar*); | |
176 void miscoptions(Req*, uchar*); | |
177 int openlisten(char *net); | |
178 void parseoptions(Req*); | |
179 void proto(Req*, int); | |
180 void rcvdecline(Req*); | |
181 void rcvdiscover(Req*); | |
182 void rcvinform(Req*); | |
183 void rcvrelease(Req*); | |
184 void rcvrequest(Req*); | |
185 char* readsysname(void); | |
186 void remrequested(Req*, int); | |
187 void sendack(Req*, uchar*, int, int); | |
188 void sendnak(Req*, char*); | |
189 void sendoffer(Req*, uchar*, int); | |
190 void stringopt(Req*, int, char*); | |
191 void termopt(Req*); | |
192 int validip(uchar*); | |
193 void vectoropt(Req*, int, uchar*, int); | |
194 void warning(int, char*, ...); | |
195 void logdhcp(Req*); | |
196 void logdhcpout(Req *, char *); | |
197 int readlast(int, Udphdr*, uchar*, int); | |
198 | |
199 void | |
200 timestamp(char *tag) | |
201 { | |
202 ulong t; | |
203 | |
204 t = nsec()/1000; | |
205 syslog(0, blog, "%s %lud", tag, t - start); | |
206 } | |
207 | |
208 void | |
209 usage(void) | |
210 { | |
211 fprint(2, "usage: dhcp [-dmsnp] [-f directory] [-x netmtpt] [-M … | |
212 exits("usage"); | |
213 } | |
214 | |
215 void | |
216 main(int argc, char **argv) | |
217 { | |
218 int i, n, fd; | |
219 char *p; | |
220 uchar ip[IPaddrlen]; | |
221 Req r; | |
222 | |
223 fmtinstall('E', eipfmt); | |
224 fmtinstall('I', eipfmt); | |
225 fmtinstall('V', eipfmt); | |
226 fmtinstall('M', eipfmt); | |
227 ARGBEGIN { | |
228 case 'm': | |
229 mute = 1; | |
230 break; | |
231 case 'd': | |
232 debug = 1; | |
233 break; | |
234 case 'f': | |
235 p = ARGF(); | |
236 if(p == nil) | |
237 usage(); | |
238 ndbfile = p; | |
239 break; | |
240 case 's': | |
241 slow = 1; | |
242 break; | |
243 case 'n': | |
244 nobootp = 1; | |
245 break; | |
246 case 'i': | |
247 parseip(xmyipaddr,EARGF(usage())); | |
248 break; | |
249 case 'p': | |
250 pptponly = 1; | |
251 break; | |
252 case 'M': | |
253 p = ARGF(); | |
254 if(p == nil) | |
255 usage(); | |
256 minlease = atoi(p); | |
257 if(minlease <= 0) | |
258 minlease = MinLease; | |
259 break; | |
260 } ARGEND; | |
261 | |
262 while(argc > 1){ | |
263 parseip(ip, argv[0]); | |
264 if(!validip(ip)) | |
265 usage(); | |
266 n = atoi(argv[1]); | |
267 if(n <= 0) | |
268 usage(); | |
269 initbinding(ip, n); | |
270 argc -= 2; | |
271 argv += 2; | |
272 } | |
273 | |
274 /* for debugging */ | |
275 for(i = 0; i < 256; i++) | |
276 if(optname[i] == 0) | |
277 optname[i] = smprint("%d", i); | |
278 | |
279 /* what is my name? */ | |
280 p = readsysname(); | |
281 strcpy(mysysname, p); | |
282 | |
283 /* put process in background */ | |
284 if(!debug) switch(rfork(RFNOTEG|RFPROC|RFFDG)) { | |
285 case -1: | |
286 fatal(1, "fork"); | |
287 case 0: | |
288 break; | |
289 default: | |
290 exits(0); | |
291 } | |
292 | |
293 chdir(TFTP); | |
294 fd = openlisten(net); | |
295 wfd = fd; | |
296 bwfd = fd; | |
297 #ifdef SO_BINDTODEVICE | |
298 if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5) < 0) | |
299 print("setsockopt: %r\n"); | |
300 #endif | |
301 | |
302 for(;;){ | |
303 memset(&r, 0, sizeof(r)); | |
304 r.fd = fd; | |
305 n = readlast(fd, &r.uh, r.buf, sizeof(r.buf)); | |
306 if(n < 0) | |
307 fatal(1, "error reading requests"); | |
308 start = nsec()/1000; | |
309 op = optbuf; | |
310 *op = 0; | |
311 proto(&r, n); | |
312 if(r.id != nil) | |
313 free(r.id); | |
314 } | |
315 } | |
316 | |
317 void | |
318 proto(Req *rp, int n) | |
319 { | |
320 uchar relip[IPaddrlen]; | |
321 char buf[64]; | |
322 | |
323 now = time(0); | |
324 | |
325 rp->e = rp->buf + n; | |
326 rp->bp = (Bootp*)rp->buf; | |
327 rp->up = &rp->uh; | |
328 rp->max = rp->buf + MINSUPPORTED - IPUDPHDRSIZE; | |
329 rp->p = rp->bp->optdata; | |
330 v4tov6(rp->giaddr, rp->bp->giaddr); | |
331 v4tov6(rp->ciaddr, rp->bp->ciaddr); | |
332 | |
333 if(pptponly && rp->bp->htype != 0) | |
334 return; | |
335 | |
336 ipifcs = readipifc(net, ipifcs, -1); | |
337 if(validip(rp->giaddr)) | |
338 ipmove(relip, rp->giaddr); | |
339 else if(validip(rp->up->raddr)) | |
340 ipmove(relip, rp->up->raddr); | |
341 else | |
342 ipmove(relip, xmyipaddr); | |
343 ipmove(rp->up->laddr, xmyipaddr); | |
344 if(rp->e < (uchar*)rp->bp->sname){ | |
345 warning(0, "packet too short"); | |
346 return; | |
347 } | |
348 if(rp->bp->op != Bootrequest){ | |
349 warning(0, "not bootrequest"); | |
350 return; | |
351 } | |
352 | |
353 if(rp->e >= rp->bp->optdata){ | |
354 if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->opt… | |
355 rp->p9request = 1; | |
356 if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->o… | |
357 rp->genrequest = 1; | |
358 parseoptions(rp); | |
359 } | |
360 } | |
361 rp->p = rp->bp->optdata; | |
362 | |
363 /* If no id is specified, make one from the hardware address | |
364 * of the target. We assume all zeros is not a hardware address | |
365 * which could be a mistake. | |
366 */ | |
367 if(rp->id == nil){ | |
368 if(rp->bp->hlen > Maxhwlen){ | |
369 warning(0, "hlen %d", rp->bp->hlen); | |
370 return; | |
371 } | |
372 if(memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){ | |
373 warning(0, "no chaddr"); | |
374 return; | |
375 } | |
376 sprint(buf, "hwa%2.2ux_", rp->bp->htype); | |
377 rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen); | |
378 } | |
379 | |
380 /* info about gateway */ | |
381 if(lookupip(relip, &rp->gii, 1) < 0){ | |
382 warning(0, "lookupip failed"); | |
383 return; | |
384 } | |
385 | |
386 /* info about target system */ | |
387 if(lookup(rp->bp, &rp->ii, &rp->gii) == 0) | |
388 if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0) | |
389 rp->staticbinding = 1; | |
390 | |
391 if(rp->dhcptype) | |
392 dhcp(rp); | |
393 else | |
394 bootp(rp); | |
395 timestamp("done"); | |
396 } | |
397 | |
398 void | |
399 dhcp(Req *rp) | |
400 { | |
401 logdhcp(rp); | |
402 | |
403 switch(rp->dhcptype){ | |
404 case Discover: | |
405 if(slow) | |
406 sleep(500); | |
407 rcvdiscover(rp); | |
408 break; | |
409 case Request: | |
410 rcvrequest(rp); | |
411 break; | |
412 case Decline: | |
413 rcvdecline(rp); | |
414 break; | |
415 case Release: | |
416 rcvrelease(rp); | |
417 break; | |
418 case Inform: | |
419 rcvinform(rp); | |
420 break; | |
421 } | |
422 } | |
423 | |
424 void | |
425 rcvdiscover(Req *rp) | |
426 { | |
427 Binding *b, *nb; | |
428 | |
429 if(rp->staticbinding){ | |
430 sendoffer(rp, rp->ii.ipaddr, StaticLease); | |
431 return; | |
432 } | |
433 | |
434 /* | |
435 * first look for an outstanding offer | |
436 */ | |
437 b = idtooffer(rp->id, &rp->gii); | |
438 | |
439 /* | |
440 * rfc2131 says: | |
441 * If an address is available, the new address | |
442 * SHOULD be chosen as follows: | |
443 * | |
444 * o The client's current address as recorded in the client… | |
445 * binding, ELSE | |
446 * | |
447 * o The client's previous address as recorded in the clien… | |
448 * expired or released) binding, if that address is in th… | |
449 * pool of available addresses and not already allocated,… | |
450 * | |
451 * o The address requested in the 'Requested IP Address' op… | |
452 * address is valid and not already allocated, ELSE | |
453 * | |
454 * o A new address allocated from the server's pool of avai… | |
455 * addresses; the address is selected based on the subnet… | |
456 * the message was received (if 'giaddr' is 0) or on the … | |
457 * the relay agent that forwarded the message ('giaddr' w… | |
458 */ | |
459 if(b == nil){ | |
460 b = idtobinding(rp->id, &rp->gii, 1); | |
461 if(b && b->boundto && strcmp(b->boundto, rp->id) != 0) | |
462 if(validip(rp->ip) && samenet(rp->ip, &rp->gii)){ | |
463 nb = iptobinding(rp->ip, 0); | |
464 if(nb && nb->lease < now) | |
465 b = nb; | |
466 } | |
467 } | |
468 if(b == nil){ | |
469 warning(0, "!Discover(%s via %I): no binding %I", | |
470 rp->id, rp->gii.ipaddr, rp->ip); | |
471 return; | |
472 } | |
473 mkoffer(b, rp->id, rp->leasetime); | |
474 sendoffer(rp, b->ip, b->offer); | |
475 } | |
476 | |
477 void | |
478 rcvrequest(Req *rp) | |
479 { | |
480 Binding *b; | |
481 | |
482 if(validip(rp->server)){ | |
483 /* this is a reply to an offer - SELECTING */ | |
484 | |
485 /* check for hard assignment */ | |
486 if(rp->staticbinding){ | |
487 if(forme(rp->server)) | |
488 sendack(rp, rp->ii.ipaddr, StaticLease, … | |
489 else | |
490 warning(0, "!Request(%s via %I): for ser… | |
491 rp->id, rp->gii.ipaddr, rp->serv… | |
492 return; | |
493 } | |
494 | |
495 b = idtooffer(rp->id, &rp->gii); | |
496 | |
497 /* if we don't have an offer, nak */ | |
498 if(b == nil){ | |
499 warning(0, "!Request(%s via %I): no offer", | |
500 rp->id, rp->gii.ipaddr); | |
501 if(forme(rp->server)) | |
502 sendnak(rp, "no offer for you"); | |
503 return; | |
504 } | |
505 | |
506 /* if not for me, retract offer */ | |
507 if(!forme(rp->server)){ | |
508 b->expoffer = 0; | |
509 warning(0, "!Request(%s via %I): for server %I n… | |
510 rp->id, rp->gii.ipaddr, rp->server); | |
511 return; | |
512 } | |
513 | |
514 /* | |
515 * if the client is confused about what we offered, nak. | |
516 * client really shouldn't be specifying this when sele… | |
517 */ | |
518 if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){ | |
519 warning(0, "!Request(%s via %I): requests %I, no… | |
520 rp->id, rp->gii.ipaddr, rp->ip, b->ip); | |
521 sendnak(rp, "bad ip address option"); | |
522 return; | |
523 } | |
524 if(commitbinding(b) < 0){ | |
525 warning(0, "!Request(%s via %I): can't commit %I… | |
526 rp->id, rp->gii.ipaddr, b->ip); | |
527 sendnak(rp, "can't commit binding"); | |
528 return; | |
529 } | |
530 sendack(rp, b->ip, b->offer, 1); | |
531 } else if(validip(rp->ip)){ | |
532 /* | |
533 * checking address/net - INIT-REBOOT | |
534 * | |
535 * This is a rebooting client that remembers its old | |
536 * address. | |
537 */ | |
538 /* check for hard assignment */ | |
539 if(rp->staticbinding){ | |
540 if(memcmp(rp->ip, rp->ii.ipaddr, IPaddrlen) != 0… | |
541 warning(0, "!Request(%s via %I): %I not … | |
542 rp->id, rp->gii.ipaddr, rp->ip, … | |
543 sendnak(rp, "not valid"); | |
544 } | |
545 sendack(rp, rp->ii.ipaddr, StaticLease, 1); | |
546 return; | |
547 } | |
548 | |
549 /* make sure the network makes sense */ | |
550 if(!samenet(rp->ip, &rp->gii)){ | |
551 warning(0, "!Request(%s via %I): bad forward of … | |
552 rp->id, rp->gii.ipaddr, rp->ip); | |
553 sendnak(rp, "wrong network"); | |
554 return; | |
555 } | |
556 b = iptobinding(rp->ip, 0); | |
557 if(b == nil){ | |
558 warning(0, "!Request(%s via %I): no binding for … | |
559 rp->id, rp->gii.ipaddr, rp->ip); | |
560 return; | |
561 } | |
562 if(memcmp(rp->ip, b->ip, IPaddrlen) != 0 || now > b->lea… | |
563 warning(0, "!Request(%s via %I): %I not valid", | |
564 rp->id, rp->gii.ipaddr, rp->ip); | |
565 sendnak(rp, "not valid"); | |
566 return; | |
567 } | |
568 b->offer = b->lease - now; | |
569 sendack(rp, b->ip, b->offer, 1); | |
570 } else if(validip(rp->ciaddr)){ | |
571 /* | |
572 * checking address - RENEWING or REBINDING | |
573 * | |
574 * these states are indistinguishable in our action. T… | |
575 * difference is how close to lease expiration the clie… | |
576 * If it is really close, it broadcasts the request hop… | |
577 * some server will answer. | |
578 */ | |
579 | |
580 /* check for hard assignment */ | |
581 if(rp->staticbinding){ | |
582 if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){ | |
583 warning(0, "!Request(%s via %I): %I not … | |
584 rp->id, rp->gii.ipaddr, rp->ciad… | |
585 sendnak(rp, "not valid"); | |
586 } | |
587 sendack(rp, rp->ii.ipaddr, StaticLease, 1); | |
588 return; | |
589 } | |
590 | |
591 /* make sure the network makes sense */ | |
592 if(!samenet(rp->ciaddr, &rp->gii)){ | |
593 warning(0, "!Request(%s via %I): bad forward of … | |
594 rp->id, rp->gii.ipaddr, rp->ip); | |
595 sendnak(rp, "wrong network"); | |
596 return; | |
597 } | |
598 b = iptobinding(rp->ciaddr, 0); | |
599 if(b == nil){ | |
600 warning(0, "!Request(%s via %I): no binding for … | |
601 rp->id, rp->gii.ipaddr, rp->ciaddr); | |
602 return; | |
603 } | |
604 if(ipcmp(rp->ciaddr, b->ip) != 0){ | |
605 warning(0, "!Request(%I via %s): %I not valid", | |
606 rp->id, rp->gii.ipaddr, rp->ciaddr); | |
607 sendnak(rp, "invalid ip address"); | |
608 return; | |
609 } | |
610 mkoffer(b, rp->id, rp->leasetime); | |
611 if(commitbinding(b) < 0){ | |
612 warning(0, "!Request(%s via %I): can't commit %I… | |
613 rp->id, rp->gii.ipaddr, b->ip); | |
614 sendnak(rp, "can't commit binding"); | |
615 return; | |
616 } | |
617 sendack(rp, b->ip, b->offer, 1); | |
618 } | |
619 } | |
620 | |
621 void | |
622 rcvdecline(Req *rp) | |
623 { | |
624 Binding *b; | |
625 char buf[64]; | |
626 | |
627 if(rp->staticbinding) | |
628 return; | |
629 | |
630 b = idtooffer(rp->id, &rp->gii); | |
631 if(b == nil){ | |
632 warning(0, "!Decline(%s via %I): no binding", | |
633 rp->id, rp->gii.ipaddr); | |
634 return; | |
635 } | |
636 | |
637 /* mark ip address as in use */ | |
638 snprint(buf, sizeof(buf), "declined by %s", rp->id); | |
639 mkoffer(b, buf, 0x7fffffff); | |
640 commitbinding(b); | |
641 } | |
642 | |
643 void | |
644 rcvrelease(Req *rp) | |
645 { | |
646 Binding *b; | |
647 | |
648 if(rp->staticbinding) | |
649 return; | |
650 | |
651 b = idtobinding(rp->id, &rp->gii, 0); | |
652 if(b == nil){ | |
653 warning(0, "!Release(%s via %I): no binding", | |
654 rp->id, rp->gii.ipaddr); | |
655 return; | |
656 } | |
657 if(strcmp(rp->id, b->boundto) != 0){ | |
658 warning(0, "!Release(%s via %I): invalid release of %I", | |
659 rp->id, rp->gii.ipaddr, rp->ip); | |
660 return; | |
661 } | |
662 warning(0, "Release(%s via %I): releasing %I", b->boundto, rp->g… | |
663 if(releasebinding(b, rp->id) < 0) | |
664 warning(0, "release: couldn't release"); | |
665 } | |
666 | |
667 void | |
668 rcvinform(Req *rp) | |
669 { | |
670 Binding *b; | |
671 | |
672 if(rp->staticbinding){ | |
673 sendack(rp, rp->ii.ipaddr, 0, 0); | |
674 return; | |
675 } | |
676 | |
677 b = iptobinding(rp->ciaddr, 0); | |
678 if(b == nil){ | |
679 warning(0, "!Inform(%s via %I): no binding for %I", | |
680 rp->id, rp->gii.ipaddr, rp->ip); | |
681 return; | |
682 } | |
683 sendack(rp, b->ip, 0, 0); | |
684 } | |
685 | |
686 int | |
687 setsiaddr(uchar *siaddr, uchar *saddr, uchar *laddr) | |
688 { | |
689 if(ipcmp(saddr, IPnoaddr) != 0){ | |
690 v6tov4(siaddr, saddr); | |
691 return 0; | |
692 } else { | |
693 v6tov4(siaddr, laddr); | |
694 return 1; | |
695 } | |
696 } | |
697 | |
698 void | |
699 sendoffer(Req *rp, uchar *ip, int offer) | |
700 { | |
701 int n; | |
702 int fd; | |
703 ushort flags; | |
704 Bootp *bp; | |
705 Udphdr *up; | |
706 | |
707 bp = rp->bp; | |
708 up = rp->up; | |
709 | |
710 /* | |
711 * set destination | |
712 */ | |
713 flags = nhgets(bp->flags); | |
714 fd = wfd; | |
715 if(validip(rp->giaddr)){ | |
716 ipmove(up->raddr, rp->giaddr); | |
717 hnputs(up->rport, 67); | |
718 } else if(flags & Fbroadcast){ | |
719 fd = bwfd; | |
720 ipmove(up->raddr, IPv4bcast); | |
721 hnputs(up->rport, 68); | |
722 } else { | |
723 ipmove(up->raddr, ip); | |
724 if(bp->htype == 1) | |
725 arpenter(up->raddr, bp->chaddr); | |
726 hnputs(up->rport, 68); | |
727 } | |
728 | |
729 /* | |
730 * fill in standard bootp part | |
731 */ | |
732 bp->op = Bootreply; | |
733 bp->hops = 0; | |
734 hnputs(bp->secs, 0); | |
735 memset(bp->ciaddr, 0, sizeof(bp->ciaddr)); | |
736 v6tov4(bp->giaddr, rp->giaddr); | |
737 v6tov4(bp->yiaddr, ip); | |
738 setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr); | |
739 strncpy(bp->sname, mysysname, sizeof(bp->sname)); | |
740 strncpy(bp->file, rp->ii.bootf, sizeof(bp->file)); | |
741 | |
742 /* | |
743 * set options | |
744 */ | |
745 byteopt(rp, ODtype, Offer); | |
746 longopt(rp, ODlease, offer); | |
747 addropt(rp, ODserverid, up->laddr); | |
748 miscoptions(rp, ip); | |
749 termopt(rp); | |
750 | |
751 logdhcpout(rp, "Offer"); | |
752 | |
753 /* | |
754 * send | |
755 */ | |
756 n = rp->p - rp->buf; | |
757 if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) | |
758 warning(0, "offer: write failed: %r"); | |
759 } | |
760 | |
761 void | |
762 sendack(Req *rp, uchar *ip, int offer, int sendlease) | |
763 { | |
764 int n, fd; | |
765 ushort flags; | |
766 Bootp *bp; | |
767 Udphdr *up; | |
768 | |
769 bp = rp->bp; | |
770 up = rp->up; | |
771 | |
772 /* | |
773 * set destination | |
774 */ | |
775 fd = wfd; | |
776 flags = nhgets(bp->flags); | |
777 if(validip(rp->giaddr)){ | |
778 ipmove(up->raddr, rp->giaddr); | |
779 hnputs(up->rport, 67); | |
780 } else if(flags & Fbroadcast){ | |
781 fd = bwfd; | |
782 ipmove(up->raddr, IPv4bcast); | |
783 hnputs(up->rport, 68); | |
784 } else { | |
785 ipmove(up->raddr, ip); | |
786 if(bp->htype == 1) | |
787 arpenter(up->raddr, bp->chaddr); | |
788 hnputs(up->rport, 68); | |
789 } | |
790 | |
791 /* | |
792 * fill in standard bootp part | |
793 */ | |
794 bp->op = Bootreply; | |
795 bp->hops = 0; | |
796 hnputs(bp->secs, 0); | |
797 v6tov4(bp->giaddr, rp->giaddr); | |
798 v6tov4(bp->yiaddr, ip); | |
799 setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr); | |
800 strncpy(bp->sname, mysysname, sizeof(bp->sname)); | |
801 strncpy(bp->file, rp->ii.bootf, sizeof(bp->file)); | |
802 | |
803 /* | |
804 * set options | |
805 */ | |
806 byteopt(rp, ODtype, Ack); | |
807 if(sendlease){ | |
808 longopt(rp, ODlease, offer); | |
809 } | |
810 addropt(rp, ODserverid, up->laddr); | |
811 miscoptions(rp, ip); | |
812 termopt(rp); | |
813 | |
814 logdhcpout(rp, "Ack"); | |
815 | |
816 /* | |
817 * send | |
818 */ | |
819 n = rp->p - rp->buf; | |
820 if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) | |
821 warning(0, "ack: write failed: %r"); | |
822 } | |
823 | |
824 void | |
825 sendnak(Req *rp, char *msg) | |
826 { | |
827 int n, fd; | |
828 Bootp *bp; | |
829 Udphdr *up; | |
830 | |
831 bp = rp->bp; | |
832 up = rp->up; | |
833 | |
834 /* | |
835 * set destination (always broadcast) | |
836 */ | |
837 fd = wfd; | |
838 if(validip(rp->giaddr)){ | |
839 ipmove(up->raddr, rp->giaddr); | |
840 hnputs(up->rport, 67); | |
841 } else { | |
842 fd = bwfd; | |
843 ipmove(up->raddr, IPv4bcast); | |
844 hnputs(up->rport, 68); | |
845 } | |
846 | |
847 /* | |
848 * fill in standard bootp part | |
849 */ | |
850 bp->op = Bootreply; | |
851 bp->hops = 0; | |
852 hnputs(bp->secs, 0); | |
853 v6tov4(bp->giaddr, rp->giaddr); | |
854 memset(bp->ciaddr, 0, sizeof(bp->ciaddr)); | |
855 memset(bp->yiaddr, 0, sizeof(bp->yiaddr)); | |
856 memset(bp->siaddr, 0, sizeof(bp->siaddr)); | |
857 | |
858 /* | |
859 * set options | |
860 */ | |
861 byteopt(rp, ODtype, Nak); | |
862 addropt(rp, ODserverid, up->laddr); | |
863 if(msg) | |
864 stringopt(rp, ODmessage, msg); | |
865 if(strncmp(rp->id, "id", 2) == 0) | |
866 hexopt(rp, ODclientid, rp->id+2); | |
867 termopt(rp); | |
868 | |
869 logdhcpout(rp, "Nak"); | |
870 | |
871 /* | |
872 * send nak | |
873 */ | |
874 n = rp->p - rp->buf; | |
875 if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) | |
876 warning(0, "nak: write failed: %r"); | |
877 } | |
878 | |
879 void | |
880 bootp(Req *rp) | |
881 { | |
882 int n, fd; | |
883 Bootp *bp; | |
884 Udphdr *up; | |
885 ushort flags; | |
886 Iplifc *lifc; | |
887 Info *iip; | |
888 | |
889 warning(0, "bootp %s %I->%I from %s via %I, file %s", | |
890 rp->genrequest ? "generic" : (rp->p9request ? "p9" : ""), | |
891 rp->up->raddr, rp->up->laddr, | |
892 rp->id, rp->gii.ipaddr, | |
893 rp->bp->file); | |
894 | |
895 if(nobootp) | |
896 return; | |
897 | |
898 bp = rp->bp; | |
899 up = rp->up; | |
900 iip = &rp->ii; | |
901 | |
902 if(rp->staticbinding == 0){ | |
903 warning(0, "bootp from unknown %s via %I", rp->id, rp->g… | |
904 return; | |
905 } | |
906 | |
907 /* ignore if not for us */ | |
908 if(*bp->sname){ | |
909 if(strcmp(bp->sname, mysysname) != 0){ | |
910 bp->sname[20] = 0; | |
911 warning(0, "bootp for server %s", bp->sname); | |
912 return; | |
913 } | |
914 } else if(slow) | |
915 sleep(500); | |
916 | |
917 /* ignore if we don't know what file to load */ | |
918 if(*bp->file == 0){ | |
919 if(rp->genrequest && *iip->bootf2) /* if not plan… | |
920 strncpy(bp->file, iip->bootf2, sizeof(bp->file)); | |
921 else if(*iip->bootf) | |
922 strncpy(bp->file, iip->bootf, sizeof(bp->file)); | |
923 else if(*bp->sname) /* if we were asked, … | |
924 bp->file[0] = '\0'; | |
925 else { | |
926 warning(0, "no bootfile for %I", iip->ipaddr); | |
927 return; | |
928 } | |
929 } | |
930 | |
931 /* ignore if the file is unreadable */ | |
932 if((!rp->genrequest) && bp->file[0] && access(bp->file, 4) < 0){ | |
933 warning(0, "inaccessible bootfile1 %s", bp->file); | |
934 return; | |
935 } | |
936 | |
937 bp->op = Bootreply; | |
938 v6tov4(bp->yiaddr, iip->ipaddr); | |
939 if(rp->p9request){ | |
940 warning(0, "p9bootp: %I", iip->ipaddr); | |
941 memmove(bp->optmagic, plan9opt, 4); | |
942 if(iip->gwip == 0) | |
943 v4tov6(iip->gwip, bp->giaddr); | |
944 rp->p += sprint((char*)rp->p, "%V %I %I %I", iip->ipmask… | |
945 iip->auip, iip->gwip); | |
946 sprint(optbuf, "%s", (char*)(bp->optmagic)); | |
947 } else if(rp->genrequest){ | |
948 warning(0, "genericbootp: %I", iip->ipaddr); | |
949 memmove(bp->optmagic, genericopt, 4); | |
950 miscoptions(rp, iip->ipaddr); | |
951 termopt(rp); | |
952 } else if(iip->vendor[0] != 0) { | |
953 warning(0, "bootp vendor field: %s", iip->vendor); | |
954 memset(rp->p, 0, 128-4); | |
955 rp->p += sprint((char*)bp->optmagic, "%s", iip->vendor); | |
956 } else { | |
957 memset(rp->p, 0, 128-4); | |
958 rp->p += 128-4; | |
959 } | |
960 | |
961 /* | |
962 * set destination | |
963 */ | |
964 fd = wfd; | |
965 flags = nhgets(bp->flags); | |
966 if(validip(rp->giaddr)){ | |
967 ipmove(up->raddr, rp->giaddr); | |
968 hnputs(up->rport, 67); | |
969 } else if(flags & Fbroadcast){ | |
970 fd = bwfd; | |
971 ipmove(up->raddr, IPv4bcast); | |
972 hnputs(up->rport, 68); | |
973 } else { | |
974 v4tov6(up->raddr, bp->yiaddr); | |
975 if(bp->htype == 1) | |
976 arpenter(up->raddr, bp->chaddr); | |
977 hnputs(up->rport, 68); | |
978 } | |
979 | |
980 /* | |
981 * select best local address if destination is directly connect… | |
982 */ | |
983 lifc = findlifc(up->raddr); | |
984 if(lifc) | |
985 ipmove(up->laddr, lifc->ip); | |
986 | |
987 /* | |
988 * our identity | |
989 */ | |
990 strncpy(bp->sname, mysysname, sizeof(bp->sname)); | |
991 | |
992 /* | |
993 * set tftp server | |
994 */ | |
995 setsiaddr(bp->siaddr, iip->tftp, up->laddr); | |
996 if(rp->genrequest && *iip->bootf2) | |
997 setsiaddr(bp->siaddr, iip->tftp2, up->laddr); | |
998 | |
999 /* | |
1000 * RFC 1048 says that we must pad vendor field with | |
1001 * zeros until we have a 64 byte field. | |
1002 */ | |
1003 n = rp->p - rp->bp->optdata; | |
1004 if(n < 64-4) { | |
1005 memset(rp->p, 0, (64-4)-n); | |
1006 rp->p += (64-4)-n; | |
1007 } | |
1008 | |
1009 /* | |
1010 * send | |
1011 */ | |
1012 n = rp->p - rp->buf; | |
1013 if(!mute && udpwrite(fd, rp->up, rp->buf, n) != n) | |
1014 warning(0, "bootp: write failed: %r"); | |
1015 | |
1016 warning(0, "bootp via %I: file %s xid(%ux)flag(%ux)ci(%V)gi(%V)y… | |
1017 up->raddr, bp->file, nhgetl(bp->xid), nhgets(bp-… | |
1018 bp->ciaddr, bp->giaddr, bp->yiaddr, bp->siaddr, | |
1019 optbuf); | |
1020 } | |
1021 | |
1022 void | |
1023 parseoptions(Req *rp) | |
1024 { | |
1025 int n, c, code; | |
1026 uchar *o, *p; | |
1027 | |
1028 p = rp->p; | |
1029 | |
1030 while(p < rp->e){ | |
1031 code = *p++; | |
1032 if(code == 255) | |
1033 break; | |
1034 if(code == 0) | |
1035 continue; | |
1036 | |
1037 /* ignore anything that's too long */ | |
1038 n = *p++; | |
1039 o = p; | |
1040 p += n; | |
1041 if(p > rp->e) | |
1042 return; | |
1043 | |
1044 switch(code){ | |
1045 case ODipaddr: /* requested ip address */ | |
1046 if(n == IPv4addrlen) | |
1047 v4tov6(rp->ip, o); | |
1048 break; | |
1049 case ODlease: /* requested lease time */ | |
1050 rp->leasetime = nhgetl(o); | |
1051 if(rp->leasetime > MaxLease || rp->leasetime < 0) | |
1052 rp->leasetime = MaxLease; | |
1053 break; | |
1054 case ODtype: | |
1055 c = *o; | |
1056 if(c < 10 && c > 0) | |
1057 rp->dhcptype = c; | |
1058 break; | |
1059 case ODserverid: | |
1060 if(n == IPv4addrlen) | |
1061 v4tov6(rp->server, o); | |
1062 break; | |
1063 case ODmessage: | |
1064 if(n > sizeof rp->msg-1) | |
1065 n = sizeof rp->msg-1; | |
1066 memmove(rp->msg, o, n); | |
1067 rp->msg[n] = 0; | |
1068 break; | |
1069 case ODmaxmsg: | |
1070 c = nhgets(o); | |
1071 c -= 28; | |
1072 if(c > 0) | |
1073 rp->max = rp->buf + c; | |
1074 break; | |
1075 case ODclientid: | |
1076 if(n <= 1) | |
1077 break; | |
1078 rp->id = toid( o, n); | |
1079 break; | |
1080 case ODparams: | |
1081 if(n > sizeof(rp->requested)) | |
1082 n = sizeof(rp->requested); | |
1083 memmove(rp->requested, o, n); | |
1084 break; | |
1085 case ODvendorclass: | |
1086 if(n >= sizeof(rp->vendorclass)) | |
1087 n = sizeof(rp->vendorclass)-1; | |
1088 memmove(rp->vendorclass, o, n); | |
1089 rp->vendorclass[n] = 0; | |
1090 if(strncmp((char*)rp->vendorclass, "p9-", 3) == … | |
1091 strcpy(rp->cputype, (char*)rp->vendorcla… | |
1092 break; | |
1093 case OBend: | |
1094 return; | |
1095 } | |
1096 } | |
1097 } | |
1098 | |
1099 void | |
1100 remrequested(Req *rp, int opt) | |
1101 { | |
1102 uchar *p; | |
1103 | |
1104 p = memchr(rp->requested, opt, sizeof(rp->requested)); | |
1105 if(p != nil) | |
1106 *p = OBpad; | |
1107 } | |
1108 | |
1109 void | |
1110 miscoptions(Req *rp, uchar *ip) | |
1111 { | |
1112 char *p; | |
1113 int i, j; | |
1114 uchar *addrs[2]; | |
1115 uchar x[2*IPaddrlen]; | |
1116 uchar vopts[64]; | |
1117 uchar *op, *omax; | |
1118 char *attr[100], **a; | |
1119 int na; | |
1120 Ndbtuple *t; | |
1121 | |
1122 addrs[0] = x; | |
1123 addrs[1] = x+IPaddrlen; | |
1124 | |
1125 /* always supply these */ | |
1126 maskopt(rp, OBmask, rp->gii.ipmask); | |
1127 if(validip(rp->gii.gwip)){ | |
1128 remrequested(rp, OBrouter); | |
1129 addropt(rp, OBrouter, rp->gii.gwip); | |
1130 } else if(validip(rp->giaddr)){ | |
1131 remrequested(rp, OBrouter); | |
1132 addropt(rp, OBrouter, rp->giaddr); | |
1133 } | |
1134 | |
1135 /* OBhostname for the HP4000M switches */ | |
1136 /* (this causes NT to log infinite errors - tough shit ) */ | |
1137 if(*rp->ii.domain){ | |
1138 remrequested(rp, OBhostname); | |
1139 stringopt(rp, OBhostname, rp->ii.domain); | |
1140 } | |
1141 if(*rp->ii.rootpath) | |
1142 stringopt(rp, OBrootpath, rp->ii.rootpath); | |
1143 | |
1144 /* figure out what we need to lookup */ | |
1145 na = 0; | |
1146 a = attr; | |
1147 if(*rp->ii.domain == 0) | |
1148 a[na++] = "dom"; | |
1149 for(i = 0; i < sizeof(rp->requested); i++) | |
1150 switch(rp->requested[i]){ | |
1151 case OBrouter: | |
1152 a[na++] = "@ipgw"; | |
1153 break; | |
1154 case OBdnserver: | |
1155 a[na++] = "@dns"; | |
1156 break; | |
1157 case OBnetbiosns: | |
1158 a[na++] = "@wins"; | |
1159 break; | |
1160 case OBsmtpserver: | |
1161 a[na++] = "@smtp"; | |
1162 break; | |
1163 case OBpop3server: | |
1164 a[na++] = "@pop3"; | |
1165 break; | |
1166 case OBwwwserver: | |
1167 a[na++] = "@www"; | |
1168 break; | |
1169 case OBntpserver: | |
1170 a[na++] = "@ntp"; | |
1171 break; | |
1172 case OBtimeserver: | |
1173 a[na++] = "@time"; | |
1174 break; | |
1175 } | |
1176 if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0 | |
1177 || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){ | |
1178 a[na++] = "@fs"; | |
1179 a[na++] = "@auth"; | |
1180 } | |
1181 t = lookupinfo(ip, a, na); | |
1182 | |
1183 /* lookup anything we might be missing */ | |
1184 if(*rp->ii.domain == 0) | |
1185 lookupname(rp->ii.domain, t); | |
1186 | |
1187 /* add any requested ones that we know about */ | |
1188 for(i = 0; i < sizeof(rp->requested); i++) | |
1189 switch(rp->requested[i]){ | |
1190 case OBrouter: | |
1191 j = lookupserver("ipgw", addrs, t); | |
1192 addrsopt(rp, OBrouter, addrs, j); | |
1193 break; | |
1194 case OBdnserver: | |
1195 j = lookupserver("dns", addrs, t); | |
1196 addrsopt(rp, OBdnserver, addrs, j); | |
1197 break; | |
1198 case OBhostname: | |
1199 if(*rp->ii.domain) | |
1200 stringopt(rp, OBhostname, rp->ii.domain); | |
1201 break; | |
1202 case OBdomainname: | |
1203 p = strchr(rp->ii.domain, '.'); | |
1204 if(p) | |
1205 stringopt(rp, OBdomainname, p+1); | |
1206 break; | |
1207 case OBnetbiosns: | |
1208 j = lookupserver("wins", addrs, t); | |
1209 addrsopt(rp, OBnetbiosns, addrs, j); | |
1210 break; | |
1211 case OBnetbiostype: | |
1212 /* p-node: peer to peer WINS queries */ | |
1213 byteopt(rp, OBnetbiostype, 0x2); | |
1214 break; | |
1215 case OBsmtpserver: | |
1216 j = lookupserver("smtp", addrs, t); | |
1217 addrsopt(rp, OBsmtpserver, addrs, j); | |
1218 break; | |
1219 case OBpop3server: | |
1220 j = lookupserver("pop3", addrs, t); | |
1221 addrsopt(rp, OBpop3server, addrs, j); | |
1222 break; | |
1223 case OBwwwserver: | |
1224 j = lookupserver("www", addrs, t); | |
1225 addrsopt(rp, OBwwwserver, addrs, j); | |
1226 break; | |
1227 case OBntpserver: | |
1228 j = lookupserver("ntp", addrs, t); | |
1229 addrsopt(rp, OBntpserver, addrs, j); | |
1230 break; | |
1231 case OBtimeserver: | |
1232 j = lookupserver("time", addrs, t); | |
1233 addrsopt(rp, OBtimeserver, addrs, j); | |
1234 break; | |
1235 case OBttl: | |
1236 byteopt(rp, OBttl, 255); | |
1237 break; | |
1238 } | |
1239 | |
1240 /* add plan9 specific options */ | |
1241 if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0 | |
1242 || strncmp((char*)rp->vendorclass, "p9-", 3) == 0){ | |
1243 /* point to temporary area */ | |
1244 op = rp->p; | |
1245 omax = rp->max; | |
1246 rp->p = vopts; | |
1247 rp->max = vopts + sizeof(vopts) - 1; | |
1248 | |
1249 j = lookupserver("fs", addrs, t); | |
1250 addrsopt(rp, OP9fs, addrs, j); | |
1251 j = lookupserver("auth", addrs, t); | |
1252 addrsopt(rp, OP9auth, addrs, j); | |
1253 | |
1254 /* point back */ | |
1255 j = rp->p - vopts; | |
1256 rp->p = op; | |
1257 rp->max = omax; | |
1258 vectoropt(rp, OBvendorinfo, vopts, j); | |
1259 } | |
1260 | |
1261 ndbfree(t); | |
1262 } | |
1263 | |
1264 int | |
1265 openlisten(char *net) | |
1266 { | |
1267 int fd; | |
1268 char data[128]; | |
1269 char devdir[40]; | |
1270 int yes; | |
1271 | |
1272 sprint(data, "udp!*!bootps"); | |
1273 fd = announce(data, devdir); | |
1274 if(fd < 0) | |
1275 fatal(1, "can't announce"); | |
1276 yes = 1; | |
1277 if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes) < … | |
1278 fatal(1, "can't broadcast"); | |
1279 return fd; | |
1280 } | |
1281 | |
1282 void | |
1283 fatal(int syserr, char *fmt, ...) | |
1284 { | |
1285 char buf[ERRMAX]; | |
1286 va_list arg; | |
1287 | |
1288 va_start(arg, fmt); | |
1289 vseprint(buf, buf+sizeof(buf), fmt, arg); | |
1290 va_end(arg); | |
1291 if(syserr) | |
1292 syslog(1, blog, "%s: %r", buf); | |
1293 else | |
1294 syslog(1, blog, "%s", buf); | |
1295 exits(buf); | |
1296 } | |
1297 | |
1298 extern void | |
1299 warning(int syserr, char *fmt, ...) | |
1300 { | |
1301 char buf[256]; | |
1302 va_list arg; | |
1303 | |
1304 va_start(arg, fmt); | |
1305 vseprint(buf, buf+sizeof(buf), fmt, arg); | |
1306 va_end(arg); | |
1307 if(syserr){ | |
1308 syslog(0, blog, "%s: %r", buf); | |
1309 if(debug) | |
1310 fprint(2, "%s: %r\n", buf); | |
1311 } else { | |
1312 syslog(0, blog, "%s", buf); | |
1313 if(debug) | |
1314 fprint(2, "%s\n", buf); | |
1315 } | |
1316 } | |
1317 | |
1318 char* | |
1319 readsysname(void) | |
1320 { | |
1321 static char name[128]; | |
1322 char *p; | |
1323 int n, fd; | |
1324 | |
1325 fd = open("/dev/sysname", OREAD); | |
1326 if(fd >= 0){ | |
1327 n = read(fd, name, sizeof(name)-1); | |
1328 close(fd); | |
1329 if(n > 0){ | |
1330 name[n] = 0; | |
1331 return name; | |
1332 } | |
1333 } | |
1334 p = getenv("sysname"); | |
1335 if(p == nil || *p == 0) | |
1336 return "unknown"; | |
1337 return p; | |
1338 } | |
1339 | |
1340 extern int | |
1341 validip(uchar *ip) | |
1342 { | |
1343 if(ipcmp(ip, IPnoaddr) == 0) | |
1344 return 0; | |
1345 if(ipcmp(ip, v4prefix) == 0) | |
1346 return 0; | |
1347 return 1; | |
1348 } | |
1349 | |
1350 void | |
1351 longopt(Req *rp, int t, long v) | |
1352 { | |
1353 if(rp->p + 6 > rp->max) | |
1354 return; | |
1355 *rp->p++ = t; | |
1356 *rp->p++ = 4; | |
1357 hnputl(rp->p, v); | |
1358 rp->p += 4; | |
1359 | |
1360 op = seprint(op, oe, "%s(%ld)", optname[t], v); | |
1361 } | |
1362 | |
1363 void | |
1364 addropt(Req *rp, int t, uchar *ip) | |
1365 { | |
1366 if(rp->p + 6 > rp->max) | |
1367 return; | |
1368 *rp->p++ = t; | |
1369 *rp->p++ = 4; | |
1370 memmove(rp->p, ip+IPv4off, 4); | |
1371 rp->p += 4; | |
1372 | |
1373 op = seprint(op, oe, "%s(%I)", optname[t], ip); | |
1374 } | |
1375 | |
1376 void | |
1377 maskopt(Req *rp, int t, uchar *ip) | |
1378 { | |
1379 if(rp->p + 6 > rp->max) | |
1380 return; | |
1381 *rp->p++ = t; | |
1382 *rp->p++ = 4; | |
1383 memmove(rp->p, ip+IPv4off, 4); | |
1384 rp->p += 4; | |
1385 | |
1386 op = seprint(op, oe, "%s(%M)", optname[t], ip); | |
1387 } | |
1388 | |
1389 void | |
1390 addrsopt(Req *rp, int t, uchar **ip, int i) | |
1391 { | |
1392 if(i <= 0) | |
1393 return; | |
1394 if(rp->p + 2 + 4*i > rp->max) | |
1395 return; | |
1396 *rp->p++ = t; | |
1397 *rp->p++ = 4*i; | |
1398 op = seprint(op, oe, "%s(", optname[t]); | |
1399 while(i-- > 0){ | |
1400 v6tov4(rp->p, *ip); | |
1401 rp->p += 4; | |
1402 op = seprint(op, oe, "%I", *ip); | |
1403 ip++; | |
1404 if(i > 0) | |
1405 op = seprint(op, oe, " "); | |
1406 } | |
1407 op = seprint(op, oe, ")"); | |
1408 } | |
1409 | |
1410 void | |
1411 byteopt(Req *rp, int t, uchar v) | |
1412 { | |
1413 if(rp->p + 3 > rp->max) | |
1414 return; | |
1415 *rp->p++ = t; | |
1416 *rp->p++ = 1; | |
1417 *rp->p++ = v; | |
1418 | |
1419 op = seprint(op, oe, "%s(%d)", optname[t], v); | |
1420 } | |
1421 | |
1422 void | |
1423 termopt(Req *rp) | |
1424 { | |
1425 if(rp->p + 1 > rp->max) | |
1426 return; | |
1427 *rp->p++ = OBend; | |
1428 } | |
1429 | |
1430 void | |
1431 stringopt(Req *rp, int t, char *str) | |
1432 { | |
1433 int n; | |
1434 | |
1435 n = strlen(str); | |
1436 if(n > 255) | |
1437 n = 255; | |
1438 if(rp->p+n+2 > rp->max) | |
1439 return; | |
1440 *rp->p++ = t; | |
1441 *rp->p++ = n; | |
1442 memmove(rp->p, str, n); | |
1443 rp->p += n; | |
1444 | |
1445 op = seprint(op, oe, "%s(%s)", optname[t], str); | |
1446 } | |
1447 | |
1448 void | |
1449 vectoropt(Req *rp, int t, uchar *v, int n) | |
1450 { | |
1451 int i; | |
1452 | |
1453 if(n > 255) | |
1454 n = 255; | |
1455 if(rp->p+n+2 > rp->max) | |
1456 return; | |
1457 *rp->p++ = t; | |
1458 *rp->p++ = n; | |
1459 memmove(rp->p, v, n); | |
1460 rp->p += n; | |
1461 | |
1462 op = seprint(op, oe, "%s(", optname[t]); | |
1463 if(n > 0) | |
1464 op = seprint(op, oe, "%ud", 0); | |
1465 for(i = 1; i < n; i++) | |
1466 op = seprint(op, oe, " %ud", v[i]); | |
1467 } | |
1468 | |
1469 int | |
1470 fromhex(int x) | |
1471 { | |
1472 if(x >= '0' && x <= '9') | |
1473 return x - '0'; | |
1474 return x - 'a'; | |
1475 } | |
1476 | |
1477 void | |
1478 hexopt(Req *rp, int t, char *str) | |
1479 { | |
1480 int n; | |
1481 | |
1482 n = strlen(str); | |
1483 n /= 2; | |
1484 if(n > 255) | |
1485 n = 255; | |
1486 if(rp->p+n+2 > rp->max) | |
1487 return; | |
1488 *rp->p++ = t; | |
1489 *rp->p++ = n; | |
1490 while(n-- > 0){ | |
1491 *rp->p++ = (fromhex(str[0])<<4)|fromhex(str[1]); | |
1492 str += 2; | |
1493 } | |
1494 | |
1495 op = seprint(op, oe, "%s(%s)", optname[t], str); | |
1496 } | |
1497 | |
1498 /* | |
1499 * What a crock it is to do this for real. | |
1500 * A giant hairy mess of ioctls that differ from | |
1501 * system to system. Don't get sucked in. | |
1502 * This need not be fast. | |
1503 */ | |
1504 void | |
1505 arpenter(uchar *ip, uchar *ether) | |
1506 { | |
1507 int pid; | |
1508 char xip[100], xether[100]; | |
1509 | |
1510 switch(pid=fork()){ | |
1511 case -1: | |
1512 break; | |
1513 default: | |
1514 waitpid(); | |
1515 break; | |
1516 case 0: | |
1517 snprint(xip, sizeof xip, "%I", ip); | |
1518 snprint(xether, sizeof xether, "%#E", ether); | |
1519 execl("arp", "arp", "-s", xip, xether, "temp", nil); | |
1520 _exits("execl"); | |
1521 } | |
1522 /* | |
1523 for comfort - ah, the good old days | |
1524 | |
1525 int f; | |
1526 char buf[256]; | |
1527 | |
1528 sprint(buf, "%s/arp", net); | |
1529 f = open(buf, OWRITE); | |
1530 if(f < 0){ | |
1531 syslog(debug, blog, "open %s: %r", buf); | |
1532 return; | |
1533 } | |
1534 fprint(f, "add ether %I %E", ip, ether); | |
1535 close(f); | |
1536 */ | |
1537 } | |
1538 | |
1539 char *dhcpmsgname[] = | |
1540 { | |
1541 [Discover] "Discover", | |
1542 [Offer] "Offer", | |
1543 [Request] "Request", | |
1544 [Decline] "Decline", | |
1545 [Ack] "Ack", | |
1546 [Nak] "Nak", | |
1547 [Release] "Release", | |
1548 [Inform] "Inform" | |
1549 }; | |
1550 | |
1551 void | |
1552 logdhcp(Req *rp) | |
1553 { | |
1554 char buf[4096]; | |
1555 char *p, *e; | |
1556 int i; | |
1557 | |
1558 p = buf; | |
1559 e = buf + sizeof(buf); | |
1560 if(rp->dhcptype > 0 && rp->dhcptype <= Inform) | |
1561 p = seprint(p, e, "%s(", dhcpmsgname[rp->dhcptype]); | |
1562 else | |
1563 p = seprint(p, e, "%d(", rp->dhcptype); | |
1564 p = seprint(p, e, "%I->%I) xid(%ux)flag(%ux)", rp->up->raddr, rp… | |
1565 nhgetl(rp->bp->xid), nhgets(rp->bp->flags)); | |
1566 if(rp->bp->htype == 1) | |
1567 p = seprint(p, e, "ea(%E)", rp->bp->chaddr); | |
1568 if(validip(rp->ciaddr)) | |
1569 p = seprint(p, e, "ci(%I)", rp->ciaddr); | |
1570 if(validip(rp->giaddr)) | |
1571 p = seprint(p, e, "gi(%I)", rp->giaddr); | |
1572 if(validip(rp->ip)) | |
1573 p = seprint(p, e, "ip(%I)", rp->ip); | |
1574 if(rp->id != nil) | |
1575 p = seprint(p, e, "id(%s)", rp->id); | |
1576 if(rp->leasetime) | |
1577 p = seprint(p, e, "leas(%d)", rp->leasetime); | |
1578 if(validip(rp->server)) | |
1579 p = seprint(p, e, "sid(%I)", rp->server); | |
1580 p = seprint(p, e, "need("); | |
1581 for(i = 0; i < sizeof(rp->requested); i++) | |
1582 if(rp->requested[i] != 0) | |
1583 p = seprint(p, e, "%s ", optname[rp->requested[i… | |
1584 p = seprint(p, e, ")"); | |
1585 | |
1586 USED(p); | |
1587 syslog(0, blog, "%s", buf); | |
1588 } | |
1589 | |
1590 void | |
1591 logdhcpout(Req *rp, char *type) | |
1592 { | |
1593 syslog(0, blog, "%s(%I->%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s", | |
1594 type, rp->up->laddr, rp->up->raddr, rp->id, | |
1595 rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->… | |
1596 } | |
1597 | |
1598 /* | |
1599 * if we get behind, it's useless to try answering since the sender | |
1600 * will probably have retransmitted with a differnt sequence number. | |
1601 * So dump all the last message in the queue. | |
1602 */ | |
1603 void ding(void *x, char *msg) | |
1604 { | |
1605 USED(x); | |
1606 | |
1607 if(strstr(msg, "alarm")) | |
1608 noted(NCONT); | |
1609 else | |
1610 noted(NDFLT); | |
1611 } | |
1612 | |
1613 int | |
1614 readlast(int fd, Udphdr *hdr, uchar *buf, int len) | |
1615 { | |
1616 int lastn, n; | |
1617 | |
1618 notify(ding); | |
1619 | |
1620 lastn = 0; | |
1621 for(;;){ | |
1622 alarm(20); | |
1623 n = udpread(fd, hdr, buf, len); | |
1624 alarm(0); | |
1625 if(n < 0){ | |
1626 if(lastn > 0) | |
1627 return lastn; | |
1628 break; | |
1629 } | |
1630 lastn = n; | |
1631 } | |
1632 return udpread(fd, hdr, buf, len); | |
1633 } |