2 * ipconfig - configure parameters of an ip stack
22 typedef struct Option Option;
30 * I was too lazy to look up the types for each of these
31 * options. If someone feels like it, please mail me a
32 * corrected array -- presotto
34 static Option option[256] =
36 [OBmask] { "ipmask", Taddr },
37 [OBtimeoff] { "timeoff", Tulong },
38 [OBrouter] { "ipgw", Taddrs },
39 [OBtimeserver] { "time", Taddrs },
40 [OBnameserver] { "name", Taddrs },
41 [OBdnserver] { "dns", Taddrs },
42 [OBlogserver] { "log", Taddrs },
43 [OBcookieserver] { "cookie", Taddrs },
44 [OBlprserver] { "lpr", Taddrs },
45 [OBimpressserver] { "impress", Taddrs },
46 [OBrlserver] { "rl", Taddrs },
47 [OBhostname] { "sys", Tstr },
48 [OBbflen] { "bflen", Tulong },
49 [OBdumpfile] { "dumpfile", Tstr },
50 [OBdomainname] { "dom", Tstr },
51 [OBswapserver] { "swap", Taddrs },
52 [OBrootpath] { "rootpath", Tstr },
53 [OBextpath] { "extpath", Tstr },
54 [OBipforward] { "ipforward", Taddrs },
55 [OBnonlocal] { "nonlocal", Taddrs },
56 [OBpolicyfilter] { "policyfilter", Taddrs },
57 [OBmaxdatagram] { "maxdatagram", Tulong },
58 [OBttl] { "ttl", Tulong },
59 [OBpathtimeout] { "pathtimeout", Taddrs },
60 [OBpathplateau] { "pathplateau", Taddrs },
61 [OBmtu] { "mtu", Tulong },
62 [OBsubnetslocal] { "subnetslocal", Taddrs },
63 [OBbaddr] { "baddr", Taddrs },
64 [OBdiscovermask] { "discovermask", Taddrs },
65 [OBsupplymask] { "supplymask", Taddrs },
66 [OBdiscoverrouter] { "discoverrouter", Taddrs },
67 [OBrsserver] { "rs", Taddrs },
68 [OBstaticroutes] { "staticroutes", Taddrs },
69 [OBtrailerencap] { "trailerencap", Taddrs },
70 [OBarptimeout] { "arptimeout", Tulong },
71 [OBetherencap] { "etherencap", Taddrs },
72 [OBtcpttl] { "tcpttl", Tulong },
73 [OBtcpka] { "tcpka", Tulong },
74 [OBtcpkag] { "tcpkag", Tulong },
75 [OBnisdomain] { "nisdomain", Tstr },
76 [OBniserver] { "ni", Taddrs },
77 [OBntpserver] { "ntp", Taddrs },
78 [OBnetbiosns] { "netbiosns", Taddrs },
79 [OBnetbiosdds] { "netbiosdds", Taddrs },
80 [OBnetbiostype] { "netbiostype", Taddrs },
81 [OBnetbiosscope] { "netbiosscope", Taddrs },
82 [OBxfontserver] { "xfont", Taddrs },
83 [OBxdispmanager] { "xdispmanager", Taddrs },
84 [OBnisplusdomain] { "nisplusdomain", Tstr },
85 [OBnisplusserver] { "nisplus", Taddrs },
86 [OBhomeagent] { "homeagent", Taddrs },
87 [OBsmtpserver] { "smtp", Taddrs },
88 [OBpop3server] { "pop3", Taddrs },
89 [OBnntpserver] { "nntp", Taddrs },
90 [OBwwwserver] { "www", Taddrs },
91 [OBfingerserver] { "finger", Taddrs },
92 [OBircserver] { "irc", Taddrs },
93 [OBstserver] { "st", Taddrs },
94 [OBstdaserver] { "stdar", Taddrs },
96 [ODipaddr] { "ipaddr", Taddr },
97 [ODlease] { "lease", Tulong },
98 [ODoverload] { "overload", Taddr },
99 [ODtype] { "type", Tbyte },
100 [ODserverid] { "serverid", Taddr },
101 [ODparams] { "params", Tvec },
102 [ODmessage] { "message", Tstr },
103 [ODmaxmsg] { "maxmsg", Tulong },
104 [ODrenewaltime] { "renewaltime", Tulong },
105 [ODrebindingtime] { "rebindingtime", Tulong },
106 [ODvendorclass] { "vendorclass", Tvec },
107 [ODclientid] { "clientid", Tvec },
108 [ODtftpserver] { "tftp", Taddr },
109 [ODbootfile] { "bootfile", Tstr },
112 static uchar defrequested[] = {
113 OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
116 static uchar requested[256];
117 static int nrequested;
119 static char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
121 static int openlisten(void);
123 static void dhcprecv(void);
124 static void dhcpsend(int);
125 static void dhcptimer(void);
127 static uchar* optaddaddr(uchar*, int, uchar*);
128 static uchar* optaddbyte(uchar*, int, int);
129 static uchar* optaddstr(uchar*, int, char*);
130 static uchar* optadd(uchar*, int, void*, int);
131 static uchar* optaddulong(uchar*, int, ulong);
132 static uchar* optaddvec(uchar*, int, uchar*, int);
133 static int optgetaddrs(uchar*, int, uchar*, int);
134 static int optgetp9addrs(uchar*, int, uchar*, int);
135 static int optgetaddr(uchar*, int, uchar*);
136 static int optgetbyte(uchar*, int);
137 static int optgetstr(uchar*, int, char*, int);
138 static uchar* optget(uchar*, int, int*);
139 static ulong optgetulong(uchar*, int);
140 static int optgetvec(uchar*, int, uchar*, int);
141 static char* optgetx(uchar*, uchar);
143 static void getoptions(uchar*);
144 static int parseoptions(uchar *p, int n);
145 static Bootp* parsebootp(uchar*, int);
150 /* init set of requested dhcp parameters with the default */
151 nrequested = sizeof defrequested;
152 memcpy(requested, defrequested, nrequested);
156 dhcpquery(int needconfig, int startstate)
159 fprint(conf.cfd, "add %I %I", IPnoaddr, IPnoaddr);
161 conf.fd = openlisten();
169 conf.starttime = time(0);
170 conf.state = startstate;
180 sysfatal("internal error 0");
183 conf.timeout = time(0) + 4;
185 while(conf.state != Sbound && conf.state != Sinit){
192 fprint(conf.cfd, "remove %I %I", IPnoaddr, IPnoaddr);
198 * was an hour, needs to be less for the ARM/GS1 until the timer
199 * code has been cleaned up (pb).
205 dhcpwatch(int needconfig)
212 switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
219 dolog = 1; /* log, don't print */
220 procsetname("dhcpwatch on %s", conf.dev);
221 /* keep trying to renew the lease */
227 /* avoid overflows */
228 for(s = secs; s > 0; s -= t){
238 * during boot, the starttime can be bogus so avoid
239 * spurious ipunconfig's
241 t = time(0) - conf.starttime;
253 dhcpquery(needconfig, needconfig? Sselecting: Srenewing);
255 if(needconfig && conf.state == Sbound){
257 sysfatal("can't start ip: %r");
260 * leave everything we've learned somewhere that
261 * other procs can find it.
276 if(now < conf.timeout)
281 sysfatal("dhcptimer: unknown state %d", conf.state);
288 dhcpsend(conf.state == Sselecting? Discover: Request);
289 conf.timeout = now + 4;
290 if(++conf.resend > 5)
295 conf.timeout = now + 1;
296 if(++conf.resend > 3) {
297 conf.state = Srebinding;
311 Udphdr *up = (Udphdr*)bp.udphdr;
313 memset(&bp, 0, sizeof bp);
315 hnputs(up->rport, 67);
317 hnputl(bp.xid, conf.xid);
318 hnputs(bp.secs, time(0)-conf.starttime);
320 memmove(bp.optmagic, optmagic, 4);
321 if(conf.hwatype >= 0 && conf.hwalen < sizeof bp.chaddr){
322 memmove(bp.chaddr, conf.hwa, conf.hwalen);
323 bp.hlen = conf.hwalen;
324 bp.htype = conf.hwatype;
327 p = optaddbyte(p, ODtype, type);
328 p = optadd(p, ODclientid, conf.cid, conf.cidlen);
331 sysfatal("dhcpsend: unknown message type: %d", type);
333 ipmove(up->raddr, IPv4bcast); /* broadcast */
334 if(*conf.hostname && sendhostname)
335 p = optaddstr(p, OBhostname, conf.hostname);
337 n = snprint((char*)vendor, sizeof vendor,
338 "plan9_%s", conf.cputype);
339 p = optaddvec(p, ODvendorclass, vendor, n);
341 p = optaddvec(p, ODparams, requested, nrequested);
342 if(validip(conf.laddr))
343 p = optaddaddr(p, ODipaddr, conf.laddr);
348 ipmove(up->raddr, conf.server);
349 v6tov4(bp.ciaddr, conf.laddr);
352 ipmove(up->raddr, IPv4bcast); /* broadcast */
353 v6tov4(bp.ciaddr, conf.laddr);
356 ipmove(up->raddr, IPv4bcast); /* broadcast */
357 p = optaddaddr(p, ODipaddr, conf.laddr);
358 p = optaddaddr(p, ODserverid, conf.server);
361 p = optaddulong(p, ODlease, conf.offered);
363 n = snprint((char*)vendor, sizeof vendor,
364 "plan9_%s", conf.cputype);
365 p = optaddvec(p, ODvendorclass, vendor, n);
367 p = optaddvec(p, ODparams, requested, nrequested);
368 if(*conf.hostname && sendhostname)
369 p = optaddstr(p, OBhostname, conf.hostname);
372 ipmove(up->raddr, conf.server);
373 v6tov4(bp.ciaddr, conf.laddr);
374 p = optaddaddr(p, ODipaddr, conf.laddr);
375 p = optaddaddr(p, ODserverid, conf.server);
385 * We use a maximum size DHCP packet to survive the
386 * All_Aboard NAT package from Internet Share. It
387 * always replies to DHCP requests with a packet of the
388 * same size, so if the request is too short the reply
391 if(write(conf.fd, &bp, sizeof bp) != sizeof bp)
392 warning("dhcpsend: write failed: %r");
401 uchar buf[8000], vopts[256], taddr[IPaddrlen];
404 memset(buf, 0, sizeof buf);
406 n = read(conf.fd, buf, sizeof buf);
410 rerrstr(err, sizeof err);
411 if(strstr(err, "interrupt") == nil)
412 warning("dhcprecv: bad read: %s", err);
414 DEBUG("dhcprecv: read timed out");
418 bp = parsebootp(buf, n);
420 DEBUG("parsebootp failed: dropping packet");
424 type = optgetbyte(bp->optdata, ODtype);
427 warning("dhcprecv: unknown type: %d", type);
430 DEBUG("got offer from %V ", bp->siaddr);
431 if(conf.state != Sselecting)
433 lease = optgetulong(bp->optdata, ODlease);
436 * The All_Aboard NAT package from Internet Share
437 * doesn't give a lease time, so we have to assume one.
439 warning("Offer with %lud lease, using %d", lease, MinLease);
442 DEBUG("lease=%lud ", lease);
443 if(!optgetaddr(bp->optdata, ODserverid, conf.server)) {
444 warning("Offer from server with invalid serverid");
448 v4tov6(conf.laddr, bp->yiaddr);
449 memmove(conf.sname, bp->sname, sizeof conf.sname);
450 conf.sname[sizeof conf.sname-1] = 0;
451 DEBUG("server=%I sname=%s", conf.server, conf.sname);
452 conf.offered = lease;
453 conf.state = Srequesting;
456 conf.timeout = time(0) + 4;
459 DEBUG("got ack from %V ", bp->siaddr);
460 if (conf.state != Srequesting && conf.state != Srenewing &&
461 conf.state != Srebinding)
464 /* ignore a bad lease */
465 lease = optgetulong(bp->optdata, ODlease);
468 * The All_Aboard NAT package from Internet Share
469 * doesn't give a lease time, so we have to assume one.
471 warning("Ack with %lud lease, using %d", lease, MinLease);
474 DEBUG("lease=%lud ", lease);
476 /* address and mask */
477 if(!validip(conf.laddr) || !Oflag)
478 v4tov6(conf.laddr, bp->yiaddr);
479 if(!validip(conf.mask) || !Oflag){
480 if(!optgetaddr(bp->optdata, OBmask, conf.mask))
481 ipmove(conf.mask, IPnoaddr);
482 if(ipcmp(conf.mask, IPv4bcast) == 0)
483 ipmove(conf.mask, IPnoaddr);
485 DEBUG("ipaddr=%I ipmask=%M ", conf.laddr, conf.mask);
488 * get a router address either from the router option
489 * or from the router that forwarded the dhcp packet
491 if(validip(conf.gaddr) && Oflag) {
492 DEBUG("ipgw=%I ", conf.gaddr);
493 } else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
494 DEBUG("ipgw=%I ", conf.gaddr);
495 } else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
496 v4tov6(conf.gaddr, bp->giaddr);
497 DEBUG("giaddr=%I ", conf.gaddr);
500 /* get dns servers */
501 memset(conf.dns, 0, sizeof conf.dns);
502 n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
503 sizeof conf.dns/IPaddrlen);
504 for(i = 0; i < n; i++)
505 DEBUG("dns=%I ", conf.dns + i*IPaddrlen);
507 /* get ntp servers */
508 memset(conf.ntp, 0, sizeof conf.ntp);
509 n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
510 sizeof conf.ntp/IPaddrlen);
511 for(i = 0; i < n; i++)
512 DEBUG("ntp=%I ", conf.ntp + i*IPaddrlen);
515 optgetstr(bp->optdata, OBhostname,
516 conf.hostname, sizeof conf.hostname);
517 optgetstr(bp->optdata, OBdomainname,
518 conf.domainname, sizeof conf.domainname);
520 /* get anything else we asked for */
521 getoptions(bp->optdata);
523 /* get plan9-specific options */
524 n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof vopts-1);
525 if(n > 0 && parseoptions(vopts, n) == 0){
526 if(validip(conf.fs) && Oflag)
529 n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
531 n = optgetaddrs(vopts, OP9fsv4,
534 for(i = 0; i < n; i++)
535 DEBUG("fs=%I ", conf.fs + i*IPaddrlen);
537 if(validip(conf.auth) && Oflag)
540 n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
542 n = optgetaddrs(vopts, OP9authv4,
545 for(i = 0; i < n; i++)
546 DEBUG("auth=%I ", conf.auth + i*IPaddrlen);
548 n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
550 memmove(conf.laddr, taddr, IPaddrlen);
551 n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
553 memmove(conf.mask, taddr, IPaddrlen);
554 n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
556 memmove(conf.gaddr, taddr, IPaddrlen);
557 DEBUG("new ipaddr=%I new ipmask=%M new ipgw=%I",
558 conf.laddr, conf.mask, conf.gaddr);
562 DEBUG("server=%I sname=%s", conf.server, conf.sname);
566 warning("recved dhcpnak on %s", conf.mpoint);
575 char data[128], devdir[40];
577 if (validip(conf.laddr) &&
578 (conf.state == Srenewing || conf.state == Srebinding))
579 sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
581 sprint(data, "%s/udp!*!68", conf.mpoint);
582 for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
584 sysfatal("can't announce for dhcp: %r");
586 /* might be another client - wait and try again */
587 warning("can't announce %s: %r", data);
593 if(fprint(cfd, "headers") < 0)
594 sysfatal("can't set header mode: %r");
596 sprint(data, "%s/data", devdir);
597 fd = open(data, ORDWR);
599 sysfatal("open %s: %r", data);
605 optadd(uchar *p, int op, void *d, int n)
614 optaddbyte(uchar *p, int op, int b)
623 optaddulong(uchar *p, int op, ulong x)
632 optaddaddr(uchar *p, int op, uchar *ip)
640 /* add dhcp option op with value v of length n to dhcp option array p */
642 optaddvec(uchar *p, int op, uchar *v, int n)
651 optaddstr(uchar *p, int op, char *v)
663 * parse p, looking for option `op'. if non-nil, np points to minimum length.
664 * return nil if option is too small, else ptr to opt, and
665 * store actual length via np if non-nil.
668 optget(uchar *p, int op, int *np)
672 while ((code = *p++) != OBend) {
692 optgetbyte(uchar *p, int op)
697 p = optget(p, op, &len);
704 optgetulong(uchar *p, int op)
709 p = optget(p, op, &len);
716 optgetaddr(uchar *p, int op, uchar *ip)
721 p = optget(p, op, &len);
728 /* expect at most n addresses; ip[] only has room for that many */
730 optgetaddrs(uchar *p, int op, uchar *ip, int n)
735 p = optget(p, op, &len);
741 for(i = 0; i < len; i++)
742 v4tov6(&ip[i*IPaddrlen], &p[i*IPv4addrlen]);
746 /* expect at most n addresses; ip[] only has room for that many */
748 optgetp9addrs(uchar *ap, int op, uchar *ip, int n)
750 int len, i, slen, addrs;
753 len = 1; /* minimum bytes needed */
754 p = (char *)optget(ap, op, &len);
757 addrs = *p++; /* first byte is address count */
758 for (i = 0; i < n && i < addrs && len > 0; i++) {
759 slen = strlen(p) + 1;
760 if (parseip(&ip[i*IPaddrlen], p) == -1)
761 fprint(2, "%s: bad address %s\n", argv0, p);
762 DEBUG("got plan 9 option %d addr %I (%s)",
763 op, &ip[i*IPaddrlen], p);
771 optgetvec(uchar *p, int op, uchar *v, int n)
776 p = optget(p, op, &len);
786 optgetstr(uchar *p, int op, char *s, int n)
791 p = optget(p, op, &len);
809 for(o = option; o < &option[nelem(option)]; o++)
810 if(o->name && strcmp(opt, o->name) == 0){
812 if(memchr(requested, i, nrequested) == 0 &&
813 nrequested < nelem(requested))
814 requested[nrequested++] = i;
821 optgetx(uchar *p, uchar opt)
827 uchar ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
837 if(optgetaddr(p, opt, ip))
838 s = smprint("%s=%I", o->name, ip);
841 n = optgetaddrs(p, opt, ips, 16);
843 s = smprint("%s=%I", o->name, ips);
844 for(i = 1; i < n; i++){
845 ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
851 x = optgetulong(p, opt);
853 s = smprint("%s=%lud", o->name, x);
856 x = optgetbyte(p, opt);
858 s = smprint("%s=%lud", o->name, x);
861 if(optgetstr(p, opt, str, sizeof str))
862 s = smprint("%s=%s", o->name, str);
865 n = optgetvec(p, opt, vec, sizeof vec);
867 /* what's %H? it's not installed */
868 s = smprint("%s=%.*H", o->name, n, vec);
880 for(i = nelem(defrequested); i < nrequested; i++){
881 s = optgetx(p, requested[i]);
884 if(ndboptions == nil)
885 ndboptions = smprint("\t%s", s);
888 ndboptions = smprint("\t%s%s", s, ndboptions);
896 * sanity check options area
897 * - options don't overflow packet
898 * - options end with an OBend
901 parseoptions(uchar *p, int n)
903 int code, len, nin = n;
913 warning("parseoptions: bad option: 0x%ux: truncated: "
914 "opt length = %d", code, nin);
920 DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
921 option[code].name, code, len, n);
923 warning("parseoptions: bad option: 0x%ux: %d > %d: "
924 "opt length = %d", code, len, n, nin);
931 /* make sure packet ends with an OBend after all the optget code */
937 * sanity check received packet:
938 * - magic is dhcp magic
939 * - options don't overflow packet
942 parsebootp(uchar *p, int n)
947 if(n < bp->optmagic - p) {
948 warning("parsebootp: short bootp packet");
952 if(conf.xid != nhgetl(bp->xid)) /* not meant for us */
955 if(bp->op != Bootreply) {
956 warning("parsebootp: bad op %d", bp->op);
960 n -= bp->optmagic - p;
964 warning("parsebootp: no option data");
967 if(memcmp(optmagic, p, 4) != 0) {
968 warning("parsebootp: bad opt magic %ux %ux %ux %ux",
969 p[0], p[1], p[2], p[3]);
974 DEBUG("parsebootp: new packet");
975 if(parseoptions(p, n) < 0)