2 * ipconfig - configure parameters of an ip stack
23 typedef struct Option Option;
31 * I was too lazy to look up the types for each of these
32 * options. If someone feels like it, please mail me a
33 * corrected array -- presotto
35 static Option option[256] =
37 [OBmask] { "ipmask", Taddr },
38 [OBtimeoff] { "timeoff", Tulong },
39 [OBrouter] { "ipgw", Taddrs },
40 [OBtimeserver] { "time", Taddrs },
41 [OBnameserver] { "name", Taddrs },
42 [OBdnserver] { "dns", Taddrs },
43 [OBlogserver] { "log", Taddrs },
44 [OBcookieserver] { "cookie", Taddrs },
45 [OBlprserver] { "lpr", Taddrs },
46 [OBimpressserver] { "impress", Taddrs },
47 [OBrlserver] { "rl", Taddrs },
48 [OBhostname] { "sys", Tstr },
49 [OBbflen] { "bflen", Tulong },
50 [OBdumpfile] { "dumpfile", Tstr },
51 [OBdomainname] { "dom", Tstr },
52 [OBrootserver] { "rootserver", Taddrs },
53 [OBrootpath] { "rootpath", Tstr },
54 [OBextpath] { "extpath", Tstr },
55 [OBipforward] { "ipforward", Taddrs },
56 [OBnonlocal] { "nonlocal", Taddrs },
57 [OBpolicyfilter] { "policyfilter", Taddrs },
58 [OBmaxdatagram] { "maxdatagram", Tulong },
59 [OBttl] { "ttl", Tulong },
60 [OBpathtimeout] { "pathtimeout", Taddrs },
61 [OBpathplateau] { "pathplateau", Taddrs },
62 [OBmtu] { "mtu", Tulong },
63 [OBsubnetslocal] { "subnetslocal", Taddrs },
64 [OBbaddr] { "baddr", Taddrs },
65 [OBdiscovermask] { "discovermask", Taddrs },
66 [OBsupplymask] { "supplymask", Taddrs },
67 [OBdiscoverrouter] { "discoverrouter", Taddrs },
68 [OBrsserver] { "rs", Taddrs },
69 [OBstaticroutes] { "staticroutes", Taddrs },
70 [OBtrailerencap] { "trailerencap", Taddrs },
71 [OBarptimeout] { "arptimeout", Tulong },
72 [OBetherencap] { "etherencap", Taddrs },
73 [OBtcpttl] { "tcpttl", Tulong },
74 [OBtcpka] { "tcpka", Tulong },
75 [OBtcpkag] { "tcpkag", Tulong },
76 [OBnisdomain] { "nisdomain", Tstr },
77 [OBniserver] { "ni", Taddrs },
78 [OBntpserver] { "ntp", Taddrs },
79 [OBnetbiosns] { "netbiosns", Taddrs },
80 [OBnetbiosdds] { "netbiosdds", Taddrs },
81 [OBnetbiostype] { "netbiostype", Taddrs },
82 [OBnetbiosscope] { "netbiosscope", Taddrs },
83 [OBxfontserver] { "xfont", Taddrs },
84 [OBxdispmanager] { "xdispmanager", Taddrs },
85 [OBnisplusdomain] { "nisplusdomain", Tstr },
86 [OBnisplusserver] { "nisplus", Taddrs },
87 [OBhomeagent] { "homeagent", Taddrs },
88 [OBsmtpserver] { "smtp", Taddrs },
89 [OBpop3server] { "pop3", Taddrs },
90 [OBnntpserver] { "nntp", Taddrs },
91 [OBwwwserver] { "www", Taddrs },
92 [OBfingerserver] { "finger", Taddrs },
93 [OBircserver] { "irc", Taddrs },
94 [OBstserver] { "st", Taddrs },
95 [OBstdaserver] { "stdar", Taddrs },
97 [ODipaddr] { "ipaddr", Taddr },
98 [ODlease] { "lease", Tulong },
99 [ODoverload] { "overload", Taddr },
100 [ODtype] { "type", Tbyte },
101 [ODserverid] { "serverid", Taddr },
102 [ODparams] { "params", Tvec },
103 [ODmessage] { "message", Tstr },
104 [ODmaxmsg] { "maxmsg", Tulong },
105 [ODrenewaltime] { "renewaltime", Tulong },
106 [ODrebindingtime] { "rebindingtime", Tulong },
107 [ODvendorclass] { "vendorclass", Tvec },
108 [ODclientid] { "clientid", Tvec },
109 [ODtftpserver] { "tftp", Tstr },
110 [ODbootfile] { "bootfile", Tstr },
111 [ODdnsdomain] { "dnsdomain", Tnames },
114 static uchar defrequested[] = {
115 OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, ODdnsdomain, OBntpserver,
118 static uchar requested[256];
119 static int nrequested;
121 static char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
123 static int openlisten(void);
125 static void dhcprecv(void);
126 static void dhcpsend(int);
127 static void dhcptimer(void);
129 static uchar* optaddaddr(uchar*, int, uchar*);
130 static uchar* optaddbyte(uchar*, int, int);
131 static uchar* optaddstr(uchar*, int, char*);
132 static uchar* optadd(uchar*, int, void*, int);
133 static uchar* optaddulong(uchar*, int, ulong);
134 static uchar* optaddvec(uchar*, int, uchar*, int);
135 static int optgetaddrs(uchar*, int, uchar*, int);
136 static int optgetp9addrs(uchar*, int, uchar*, int);
137 static int optgetaddr(uchar*, int, uchar*);
138 static int optgetbyte(uchar*, int);
139 static int optgetstr(uchar*, int, char*, int);
140 static uchar* optget(uchar*, int, int*);
141 static ulong optgetulong(uchar*, int);
142 static int optgetvec(uchar*, int, uchar*, int);
143 static char* optgetx(uchar*, uchar);
144 static int optgetnames(uchar*, int, char*, int);
146 static void getoptions(uchar*);
147 static int parseoptions(uchar *p, int n);
148 static Bootp* parsebootp(uchar*, int);
153 /* init set of requested dhcp parameters with the default */
154 nrequested = sizeof defrequested;
155 memcpy(requested, defrequested, nrequested);
161 fprint(conf.cfd, "remove %I %M", IPnoaddr, IPnoaddr);
162 atexitdont(removenulladdr);
168 atexit(removenulladdr);
169 fprint(conf.cfd, "add %I %M", IPnoaddr, IPnoaddr);
173 dhcpquery(int needconfig, int startstate)
178 conf.fd = openlisten();
186 conf.starttime = time(0);
187 conf.state = startstate;
197 sysfatal("internal error 0");
200 conf.timeout = time(0) + 4;
202 while(conf.state != Sbound && conf.state != Sinit){
214 * was an hour, needs to be less for the ARM/GS1 until the timer
215 * code has been cleaned up (pb).
221 dhcpwatch(int needconfig)
228 switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
235 dolog = 1; /* log, don't print */
236 procsetname("dhcpwatch on %s", conf.dev);
237 /* keep trying to renew the lease */
243 /* avoid overflows */
244 for(s = secs; s > 0; s -= t){
254 * during boot, the starttime can be bogus so avoid
255 * spurious ipunconfig's
257 t = time(0) - conf.starttime;
262 DEBUG("couldn't renew IP lease");
270 dhcpquery(needconfig, needconfig? Sselecting: Srenewing);
272 if(needconfig && conf.state == Sbound){
274 sysfatal("can't start ip: %r");
277 * leave everything we've learned somewhere that
278 * other procs can find it.
292 if(now < conf.timeout)
297 sysfatal("dhcptimer: unknown state %d", conf.state);
304 dhcpsend(conf.state == Sselecting? Discover: Request);
305 conf.timeout = now + 4;
306 if(++conf.resend > 5)
311 conf.timeout = now + 1;
312 if(++conf.resend > 3) {
313 conf.state = Srebinding;
327 Udphdr *up = (Udphdr*)bp.udphdr;
329 memset(&bp, 0, sizeof bp);
331 hnputs(up->rport, 67);
333 hnputl(bp.xid, conf.xid);
334 hnputs(bp.secs, time(0)-conf.starttime);
336 memmove(bp.optmagic, optmagic, 4);
337 if(conf.hwatype >= 0 && conf.hwalen < sizeof bp.chaddr){
338 memmove(bp.chaddr, conf.hwa, conf.hwalen);
339 bp.hlen = conf.hwalen;
340 bp.htype = conf.hwatype;
343 p = optaddbyte(p, ODtype, type);
344 p = optadd(p, ODclientid, conf.cid, conf.cidlen);
347 sysfatal("dhcpsend: unknown message type: %d", type);
349 ipmove(up->raddr, IPv4bcast); /* broadcast */
350 if(*conf.hostname && sendhostname)
351 p = optaddstr(p, OBhostname, conf.hostname);
353 n = snprint((char*)vendor, sizeof vendor,
354 "plan9_%s", conf.cputype);
355 p = optaddvec(p, ODvendorclass, vendor, n);
357 p = optaddvec(p, ODparams, requested, nrequested);
358 if(validip(conf.laddr))
359 p = optaddaddr(p, ODipaddr, conf.laddr);
364 ipmove(up->raddr, conf.server);
365 v6tov4(bp.ciaddr, conf.laddr);
368 ipmove(up->raddr, IPv4bcast); /* broadcast */
369 v6tov4(bp.ciaddr, conf.laddr);
372 ipmove(up->raddr, IPv4bcast); /* broadcast */
373 p = optaddaddr(p, ODipaddr, conf.laddr);
374 p = optaddaddr(p, ODserverid, conf.server);
377 p = optaddulong(p, ODlease, conf.offered);
379 n = snprint((char*)vendor, sizeof vendor,
380 "plan9_%s", conf.cputype);
381 p = optaddvec(p, ODvendorclass, vendor, n);
383 p = optaddvec(p, ODparams, requested, nrequested);
384 if(*conf.hostname && sendhostname)
385 p = optaddstr(p, OBhostname, conf.hostname);
388 ipmove(up->raddr, conf.server);
389 v6tov4(bp.ciaddr, conf.laddr);
390 p = optaddaddr(p, ODipaddr, conf.laddr);
391 p = optaddaddr(p, ODserverid, conf.server);
401 * We use a maximum size DHCP packet to survive the
402 * All_Aboard NAT package from Internet Share. It
403 * always replies to DHCP requests with a packet of the
404 * same size, so if the request is too short the reply
407 if(write(conf.fd, &bp, sizeof bp) != sizeof bp)
408 warning("dhcpsend: write failed: %r");
417 uchar buf[8000], vopts[256], taddr[IPaddrlen];
420 memset(buf, 0, sizeof buf);
422 n = read(conf.fd, buf, sizeof buf);
426 rerrstr(err, sizeof err);
427 if(strstr(err, "interrupt") == nil)
428 warning("dhcprecv: bad read: %s", err);
430 DEBUG("dhcprecv: read timed out");
434 bp = parsebootp(buf, n);
436 DEBUG("parsebootp failed: dropping packet");
440 type = optgetbyte(bp->optdata, ODtype);
443 warning("dhcprecv: unknown type: %d", type);
446 DEBUG("got offer from %V ", bp->siaddr);
447 if(conf.state != Sselecting)
449 lease = optgetulong(bp->optdata, ODlease);
452 * The All_Aboard NAT package from Internet Share
453 * doesn't give a lease time, so we have to assume one.
455 warning("Offer with %lud lease, using %d", lease, MinLease);
458 DEBUG("lease=%lud ", lease);
459 if(!optgetaddr(bp->optdata, ODserverid, conf.server)) {
460 warning("Offer from server with invalid serverid");
464 v4tov6(conf.laddr, bp->yiaddr);
465 memmove(conf.sname, bp->sname, sizeof conf.sname);
466 conf.sname[sizeof conf.sname-1] = 0;
467 DEBUG("server=%I sname=%s", conf.server, conf.sname);
468 conf.offered = lease;
469 conf.state = Srequesting;
472 conf.timeout = time(0) + 4;
475 DEBUG("got ack from %V ", bp->siaddr);
476 if (conf.state != Srequesting && conf.state != Srenewing &&
477 conf.state != Srebinding)
480 /* ignore a bad lease */
481 lease = optgetulong(bp->optdata, ODlease);
484 * The All_Aboard NAT package from Internet Share
485 * doesn't give a lease time, so we have to assume one.
487 warning("Ack with %lud lease, using %d", lease, MinLease);
490 DEBUG("lease=%lud ", lease);
492 /* address and mask */
493 if(!validip(conf.laddr) || !Oflag)
494 v4tov6(conf.laddr, bp->yiaddr);
495 if(!validip(conf.mask) || !Oflag){
496 if(!optgetaddr(bp->optdata, OBmask, conf.mask))
497 ipmove(conf.mask, IPnoaddr);
498 if(ipcmp(conf.mask, IPv4bcast) == 0)
499 ipmove(conf.mask, IPnoaddr);
501 DEBUG("ipaddr=%I ipmask=%M ", conf.laddr, conf.mask);
504 * get a router address either from the router option
505 * or from the router that forwarded the dhcp packet
507 if(validip(conf.gaddr) && Oflag) {
508 DEBUG("ipgw=%I ", conf.gaddr);
509 } else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
510 DEBUG("ipgw=%I ", conf.gaddr);
511 } else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
512 v4tov6(conf.gaddr, bp->giaddr);
513 DEBUG("giaddr=%I ", conf.gaddr);
516 /* get dns servers */
517 memset(conf.dns, 0, sizeof conf.dns);
518 n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
519 sizeof conf.dns/IPaddrlen);
520 for(i = 0; i < n; i++)
521 DEBUG("dns=%I ", conf.dns + i*IPaddrlen);
523 /* get ntp servers */
524 memset(conf.ntp, 0, sizeof conf.ntp);
525 n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
526 sizeof conf.ntp/IPaddrlen);
527 for(i = 0; i < n; i++)
528 DEBUG("ntp=%I ", conf.ntp + i*IPaddrlen);
531 if(optgetstr(bp->optdata, OBhostname,
532 conf.hostname, sizeof conf.hostname))
533 DEBUG("hostname=%s ", conf.hostname);
534 if(optgetstr(bp->optdata, OBdomainname,
535 conf.domainname, sizeof conf.domainname))
536 DEBUG("domainname=%s ", conf.domainname);
537 if(optgetnames(bp->optdata, ODdnsdomain,
538 conf.dnsdomain, sizeof conf.dnsdomain))
539 DEBUG("dnsdomain=%s ", conf.dnsdomain);
541 /* get anything else we asked for */
542 getoptions(bp->optdata);
544 /* get plan9-specific options */
545 n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof vopts-1);
546 if(n > 0 && parseoptions(vopts, n) == 0){
547 if(validip(conf.fs) && Oflag)
550 n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
552 n = optgetaddrs(vopts, OP9fsv4,
555 for(i = 0; i < n; i++)
556 DEBUG("fs=%I ", conf.fs + i*IPaddrlen);
558 if(validip(conf.auth) && Oflag)
561 n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
563 n = optgetaddrs(vopts, OP9authv4,
566 for(i = 0; i < n; i++)
567 DEBUG("auth=%I ", conf.auth + i*IPaddrlen);
569 n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
571 ipmove(conf.laddr, taddr);
572 n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
574 ipmove(conf.mask, taddr);
575 n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
577 ipmove(conf.gaddr, taddr);
578 DEBUG("new ipaddr=%I new ipmask=%M new ipgw=%I",
579 conf.laddr, conf.mask, conf.gaddr);
583 DEBUG("server=%I sname=%s", conf.server, conf.sname);
587 warning("recved dhcpnak on %s", conf.mpoint);
596 char data[128], devdir[40];
598 if (validip(conf.laddr) &&
599 (conf.state == Srenewing || conf.state == Srebinding))
600 sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
602 sprint(data, "%s/udp!*!68", conf.mpoint);
603 for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
605 sysfatal("can't announce for dhcp: %r");
607 /* might be another client - wait and try again */
608 warning("can't announce %s: %r", data);
614 if(fprint(cfd, "headers") < 0)
615 sysfatal("can't set header mode: %r");
617 sprint(data, "%s/data", devdir);
618 fd = open(data, ORDWR);
620 sysfatal("open %s: %r", data);
626 optadd(uchar *p, int op, void *d, int n)
635 optaddbyte(uchar *p, int op, int b)
644 optaddulong(uchar *p, int op, ulong x)
653 optaddaddr(uchar *p, int op, uchar *ip)
661 /* add dhcp option op with value v of length n to dhcp option array p */
663 optaddvec(uchar *p, int op, uchar *v, int n)
672 optaddstr(uchar *p, int op, char *v)
684 * parse p, looking for option `op'. if non-nil, np points to minimum length.
685 * return nil if option is too small, else ptr to opt, and
686 * store actual length via np if non-nil.
689 optget(uchar *p, int op, int *np)
693 while ((code = *p++) != OBend) {
712 optgetbyte(uchar *p, int op)
717 p = optget(p, op, &len);
724 optgetulong(uchar *p, int op)
729 p = optget(p, op, &len);
736 optgetaddr(uchar *p, int op, uchar *ip)
741 p = optget(p, op, &len);
748 /* expect at most n addresses; ip[] only has room for that many */
750 optgetaddrs(uchar *p, int op, uchar *ip, int n)
755 p = optget(p, op, &len);
761 for(i = 0; i < len; i++)
762 v4tov6(&ip[i*IPaddrlen], &p[i*IPv4addrlen]);
766 /* expect at most n addresses; ip[] only has room for that many */
768 optgetp9addrs(uchar *ap, int op, uchar *ip, int n)
770 int len, i, slen, addrs;
773 len = 1; /* minimum bytes needed */
774 p = (char *)optget(ap, op, &len);
777 addrs = *p++; /* first byte is address count */
778 for (i = 0; i < n && i < addrs && len > 0; i++) {
779 slen = strlen(p) + 1;
780 if (parseip(&ip[i*IPaddrlen], p) == -1)
781 fprint(2, "%s: bad address %s\n", argv0, p);
782 DEBUG("got plan 9 option %d addr %I (%s)",
783 op, &ip[i*IPaddrlen], p);
791 optgetvec(uchar *p, int op, uchar *v, int n)
796 p = optget(p, op, &len);
806 optgetstr(uchar *p, int op, char *s, int n)
811 p = optget(p, op, &len);
822 optgetnames(uchar *p, int op, char *s, int n)
827 for(nbuf=0;;p+=len,nbuf+=len){
829 p = optget(p, op, &len);
832 if(nbuf+len > sizeof(buf))
834 memmove(buf+nbuf, p, len);
836 if((len = gnames(s, n, buf, nbuf)) < 0){
851 for(o = option; o < &option[nelem(option)]; o++)
852 if(o->name && strcmp(opt, o->name) == 0){
854 if(memchr(requested, i, nrequested) == 0 &&
855 nrequested < nelem(requested))
856 requested[nrequested++] = i;
863 optgetx(uchar *p, uchar opt)
869 uchar ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
879 if(optgetaddr(p, opt, ip))
880 s = smprint("%s=%I", o->name, ip);
883 n = optgetaddrs(p, opt, ips, 16);
885 s = smprint("%s=%I", o->name, ips);
886 for(i = 1; i < n; i++){
887 ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
893 x = optgetulong(p, opt);
895 s = smprint("%s=%lud", o->name, x);
898 x = optgetbyte(p, opt);
900 s = smprint("%s=%lud", o->name, x);
903 if(optgetstr(p, opt, str, sizeof str))
904 s = smprint("%s=%s", o->name, str);
907 n = optgetvec(p, opt, vec, sizeof vec);
909 s = smprint("%s=%.*H", o->name, n, vec);
921 for(i = nelem(defrequested); i < nrequested; i++){
922 s = optgetx(p, requested[i]);
925 if(ndboptions == nil)
926 ndboptions = smprint("\t%s", s);
929 ndboptions = smprint("\t%s%s", s, ndboptions);
937 * sanity check options area
938 * - options don't overflow packet
939 * - options end with an OBend
942 parseoptions(uchar *p, int n)
944 int code, len, nin = n;
954 warning("parseoptions: bad option: 0x%ux: truncated: "
955 "opt length = %d", code, nin);
961 DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
962 option[code].name, code, len, n);
964 warning("parseoptions: bad option: 0x%ux: %d > %d: "
965 "opt length = %d", code, len, n, nin);
972 /* make sure packet ends with an OBend after all the optget code */
978 * sanity check received packet:
979 * - magic is dhcp magic
980 * - options don't overflow packet
983 parsebootp(uchar *p, int n)
988 if(n < bp->optmagic - p) {
989 warning("parsebootp: short bootp packet");
993 if(conf.xid != nhgetl(bp->xid)) /* not meant for us */
996 if(bp->op != Bootreply) {
997 warning("parsebootp: bad op %d", bp->op);
1001 n -= bp->optmagic - p;
1005 warning("parsebootp: no option data");
1008 if(memcmp(optmagic, p, 4) != 0) {
1009 warning("parsebootp: bad opt magic %ux %ux %ux %ux",
1010 p[0], p[1], p[2], p[3]);
1015 DEBUG("parsebootp: new packet");
1016 if(parseoptions(p, n) < 0)