blob: 33c5a5e6f0dd8225c9034eaab2a62f048145d6a7 [file] [log] [blame]
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* ipconfig - configure parameters of an ip stack
*/
#include <parlib/alarm.h>
#include <iplib/iplib.h>
#include <ndblib/ndb.h>
#include <parlib/common.h>
#include <parlib/printf-ext.h>
#include <parlib/uthread.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "dhcp.h"
#include "ipconfig.h"
#define DEBUG(...) do { if (debug) warning(__VA_ARGS__); } while (0)
/* possible verbs */
enum {
/* commands */
Vadd,
Vremove,
Vunbind,
Vaddpref6,
Vra6,
/* media */
Vether,
Vgbe,
Vloopback,
Vtorus,
Vtree,
Vpkt,
};
enum {
Taddr,
Taddrs,
Tstr,
Tbyte,
Tulong,
Tvec,
};
struct option {
char *name;
int type;
};
/*
* I was too lazy to look up the types for each of these
* options. If someone feels like it, please mail me a
* corrected array -- presotto
*/
struct option option[256] = {
[OBmask] { "ipmask", Taddr },
[OBtimeoff] { "timeoff", Tulong },
[OBrouter] { "ipgw", Taddrs },
[OBtimeserver] { "time", Taddrs },
[OBnameserver] { "name", Taddrs },
[OBdnserver] { "dns", Taddrs },
[OBlogserver] { "log", Taddrs },
[OBcookieserver] { "cookie", Taddrs },
[OBlprserver] { "lpr", Taddrs },
[OBimpressserver] { "impress", Taddrs },
[OBrlserver] { "rl", Taddrs },
[OBhostname] { "sys", Tstr },
[OBbflen] { "bflen", Tulong },
[OBdumpfile] { "dumpfile", Tstr },
[OBdomainname] { "dom", Tstr },
[OBswapserver] { "swap", Taddrs },
[OBrootpath] { "rootpath", Tstr },
[OBextpath] { "extpath", Tstr },
[OBipforward] { "ipforward", Taddrs },
[OBnonlocal] { "nonlocal", Taddrs },
[OBpolicyfilter] { "policyfilter", Taddrs },
[OBmaxdatagram] { "maxdatagram", Tulong },
[OBttl] { "ttl", Tulong },
[OBpathtimeout] { "pathtimeout", Taddrs },
[OBpathplateau] { "pathplateau", Taddrs },
[OBmtu] { "mtu", Tulong },
[OBsubnetslocal] { "subnetslocal", Taddrs },
[OBbaddr] { "baddr", Taddrs },
[OBdiscovermask] { "discovermask", Taddrs },
[OBsupplymask] { "supplymask", Taddrs },
[OBdiscoverrouter] { "discoverrouter", Taddrs },
[OBrsserver] { "rs", Taddrs },
[OBstaticroutes] { "staticroutes", Taddrs },
[OBtrailerencap] { "trailerencap", Taddrs },
[OBarptimeout] { "arptimeout", Tulong },
[OBetherencap] { "etherencap", Taddrs },
[OBtcpttl] { "tcpttl", Tulong },
[OBtcpka] { "tcpka", Tulong },
[OBtcpkag] { "tcpkag", Tulong },
[OBnisdomain] { "nisdomain", Tstr },
[OBniserver] { "ni", Taddrs },
[OBntpserver] { "ntp", Taddrs },
[OBnetbiosns] { "netbiosns", Taddrs },
[OBnetbiosdds] { "netbiosdds", Taddrs },
[OBnetbiostype] { "netbiostype", Taddrs },
[OBnetbiosscope] { "netbiosscope", Taddrs },
[OBxfontserver] { "xfont", Taddrs },
[OBxdispmanager] { "xdispmanager", Taddrs },
[OBnisplusdomain] { "nisplusdomain", Tstr },
[OBnisplusserver] { "nisplus", Taddrs },
[OBhomeagent] { "homeagent", Taddrs },
[OBsmtpserver] { "smtp", Taddrs },
[OBpop3server] { "pop3", Taddrs },
[OBnntpserver] { "nntp", Taddrs },
[OBwwwserver] { "www", Taddrs },
[OBfingerserver] { "finger", Taddrs },
[OBircserver] { "irc", Taddrs },
[OBstserver] { "st", Taddrs },
[OBstdaserver] { "stdar", Taddrs },
[ODipaddr] { "ipaddr", Taddr },
[ODlease] { "lease", Tulong },
[ODoverload] { "overload", Taddr },
[ODtype] { "type", Tbyte },
[ODserverid] { "serverid", Taddr },
[ODparams] { "params", Tvec },
[ODmessage] { "message", Tstr },
[ODmaxmsg] { "maxmsg", Tulong },
[ODrenewaltime] { "renewaltime", Tulong },
[ODrebindingtime] { "rebindingtime", Tulong },
[ODvendorclass] { "vendorclass", Tvec },
[ODclientid] { "clientid", Tvec },
[ODtftpserver] { "tftp", Taddr },
[ODbootfile] { "bootfile", Tstr },
};
uint8_t defrequested[] = {
OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
};
uint8_t requested[256];
int nrequested;
char *argv0;
int Oflag;
int beprimary = -1;
struct conf conf;
int debug;
int dodhcp;
int dondbconfig;
int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
struct ctl *firstctl, **ctll;
struct ipifc *ifc;
int ipv6auto;
int myifc = -1;
char *ndboptions;
int nip;
int noconfig;
int nodhcpwatch;
char optmagic[4] = {0x63, 0x82, 0x53, 0x63};
int plan9 = 1;
int sendhostname;
int server;
char *verbs[] = {
[Vadd] "add",
[Vremove] "remove",
[Vunbind] "unbind",
[Vether] "ether",
[Vgbe] "gbe",
[Vloopback] "loopback",
[Vaddpref6] "add6",
[Vra6] "ra6",
[Vtorus] "torus",
[Vtree] "tree",
[Vpkt] "pkt",
};
void evnotify(int rc)
{
struct event_msg msg = { 0 };
msg.ev_type = EV_USER_IPI;
msg.ev_arg1 = rc;
sys_notify(getppid(), EV_USER_IPI, &msg);
}
void evexit(int rc)
{
if (server)
evnotify(rc);
exit(rc);
}
void usage(void)
{
fprintf(stderr,
"usage: %s %s\n\t%s\n",
argv0,
"[-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw] [-h host][-m mtu]",
"[-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask [raddr [fs [auth]]]]]");
evexit(1);
}
void warning(char *fmt, ...)
{
char buf[1024];
va_list arg;
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
va_end(arg);
fprintf(stderr, "%s: %s\n", argv0, buf);
}
char *sysname(void)
{
static char sname[256];
gethostname(sname, sizeof(sname));
return sname;
}
void parsenorm(int argc, char **argv)
{
switch (argc) {
case 5:
if (parseip(conf.auth, argv[4]) == -1)
usage();
/* fall through */
case 4:
if (parseip(conf.fs, argv[3]) == -1)
usage();
/* fall through */
case 3:
if (parseip(conf.raddr, argv[2]) == -1)
usage();
/* fall through */
case 2:
/*
* can't test for parseipmask()==-1 cuz 255.255.255.255
* looks like that.
*/
if (strcmp(argv[1], "0") != 0)
parseipmask(conf.mask, argv[1]);
/* fall through */
case 1:
if (parseip(conf.laddr, argv[0]) == -1)
usage();
/* fall through */
case 0:
break;
default:
usage();
}
}
void parse6pref(int argc, char **argv)
{
switch (argc) {
case 6:
conf.preflt = strtoul(argv[5], 0, 10);
/* fall through */
case 5:
conf.validlt = strtoul(argv[4], 0, 10);
/* fall through */
case 4:
conf.autoflag = (atoi(argv[3]) != 0);
/* fall through */
case 3:
conf.onlink = (atoi(argv[2]) != 0);
/* fall through */
case 2:
conf.prefixlen = atoi(argv[1]);
/* fall through */
case 1:
if (parseip(conf.v6pref, argv[0]) == -1) {
fprintf(stderr, "bad address %s\n", argv[0]);
evexit(-1);
}
break;
}
DEBUG("parse6pref: pref %R len %d", conf.v6pref, conf.prefixlen);
}
/* parse router advertisement (keyword, value) pairs */
void parse6ra(int argc, char *argv[])
{
int i, argsleft;
char *kw, *val;
if (argc % 2 != 0)
usage();
i = 0;
for (argsleft = argc; argsleft > 1; argsleft -= 2) {
kw = argv[i];
val = argv[i + 1];
if (strcmp(kw, "recvra") == 0)
conf.recvra = (atoi(val) != 0);
else if (strcmp(kw, "sendra") == 0)
conf.sendra = (atoi(val) != 0);
else if (strcmp(kw, "mflag") == 0)
conf.mflag = (atoi(val) != 0);
else if (strcmp(kw, "oflag") == 0)
conf.oflag = (atoi(val) != 0);
else if (strcmp(kw, "maxraint") == 0)
conf.maxraint = atoi(val);
else if (strcmp(kw, "minraint") == 0)
conf.minraint = atoi(val);
else if (strcmp(kw, "linkmtu") == 0)
conf.linkmtu = atoi(val);
else if (strcmp(kw, "reachtime") == 0)
conf.reachtime = atoi(val);
else if (strcmp(kw, "rxmitra") == 0)
conf.rxmitra = atoi(val);
else if (strcmp(kw, "ttl") == 0)
conf.ttl = atoi(val);
else if (strcmp(kw, "routerlt") == 0)
conf.routerlt = atoi(val);
else {
warning("bad ra6 keyword %s", kw);
usage();
}
i += 2;
}
/* consistency check */
if (conf.maxraint < conf.minraint) {
fprintf(stderr, "maxraint %d < minraint %d\n",
conf.maxraint, conf.minraint);
evexit(-1);
}
}
void init(void)
{
parlib_wants_to_be_mcp = FALSE;
srand(lrand48());
if (register_printf_specifier('E', printf_ethaddr,
printf_ethaddr_info) != 0)
fprintf(stderr, "Installing 'E' failed\n");
if (register_printf_specifier('R', printf_ipaddr, printf_ipaddr_info) !=
0)
fprintf(stderr, "Installing 'R' failed\n");
if (register_printf_specifier('M', printf_ipmask, printf_ipmask_info) !=
0)
fprintf(stderr, "Installing 'M' failed\n");
setnetmtpt(conf.mpoint, sizeof(conf).mpoint, NULL);
conf.cputype = getenv("cputype");
if (conf.cputype == NULL)
conf.cputype = "386";
ctll = &firstctl;
v6paraminit(&conf);
/* init set of requested dhcp parameters with the default */
nrequested = sizeof(defrequested);
memcpy(requested, defrequested, nrequested);
}
int parseargs(int argc, char *argv[])
{
char *p;
int action, verb;
/* default to any host name we already have */
if (*conf.hostname == 0) {
p = getenv("sysname");
if (p == NULL || *p == 0)
p = sysname();
if (p != NULL)
strncpy(conf.hostname, p, sizeof(conf).hostname - 1);
}
/* defaults */
conf.type = "ether";
conf.dev = "/net/ether0";
action = Vadd;
/* get optional medium and device */
if (argc > 0) {
verb = parseverb(*argv);
switch (verb) {
case Vether:
case Vgbe:
case Vloopback:
case Vtorus:
case Vtree:
case Vpkt:
conf.type = *argv++;
argc--;
if (argc > 0) {
conf.dev = *argv++;
argc--;
}
break;
}
}
/* get optional verb */
if (argc > 0) {
verb = parseverb(*argv);
switch (verb) {
case Vether:
case Vgbe:
case Vloopback:
case Vtorus:
case Vtree:
case Vpkt:
fprintf(stderr, "medium %s already specified\n",
conf.type);
evexit(-1);
case Vadd:
case Vremove:
case Vunbind:
case Vaddpref6:
case Vra6:
argv++;
argc--;
action = verb;
break;
}
}
/* get verb-dependent arguments */
switch (action) {
case Vadd:
case Vremove:
case Vunbind:
parsenorm(argc, argv);
break;
case Vaddpref6:
parse6pref(argc, argv);
break;
case Vra6:
parse6ra(argc, argv);
break;
}
return action;
}
int main(int argc, char *argv[])
{
int retry, action, ch;
struct ctl *cp;
init();
retry = 0;
while ((ch = getopt(argc, argv, "6b:c:dDg:h:m:nNo:OpPrSux:X")) != -1) {
switch (ch) {
case '6': /* IPv6 auto config */
ipv6auto = 1;
break;
case 'b':
conf.baud = optarg;
break;
case 'c':
cp = malloc(sizeof(*cp));
if (cp == NULL) {
fprintf(stderr, "%r\n");
evexit(1);
}
*ctll = cp;
ctll = &cp->next;
cp->next = NULL;
cp->ctl = optarg;
break;
case 'd':
dodhcp = 1;
break;
case 'D':
debug = 1;
break;
case 'g':
if (parseip(conf.gaddr, optarg) == -1)
usage();
break;
case 'G':
plan9 = 0;
break;
case 'h':
snprintf(conf.hostname, sizeof(conf).hostname,
"%s", optarg);
sendhostname = 1;
break;
case 'm':
conf.mtu = atoi(optarg);
break;
case 'n':
noconfig = 1;
break;
case 'N':
dondbconfig = 1;
break;
case 'o':
if (addoption(optarg) < 0)
usage();
break;
case 'O':
Oflag = 1;
break;
case 'p':
beprimary = 1;
break;
case 'P':
beprimary = 0;
break;
case 'r':
retry = 1;
break;
case 'S':
server = 1;
break;
case 'u': /* IPv6: duplicate neighbour disc. off */
dupl_disc = 0;
break;
case 'x':
setnetmtpt(conf.mpoint, sizeof(conf).mpoint, optarg);
break;
case 'X':
nodhcpwatch = 1;
break;
default:
usage();
}
}
argv0 = "ipconfig"; /* boot invokes us as tcp? */
argc -= optind;
argv += optind;
action = parseargs(argc, argv);
switch (action) {
case Vadd:
doadd(retry);
break;
case Vremove:
doremove();
break;
case Vunbind:
dounbind();
break;
case Vaddpref6:
case Vra6:
doipv6(action);
break;
}
if (server) {
evnotify(0);
uthread_sleep_forever();
}
evexit(0);
}
int havendb(char *net)
{
struct stat s;
char buf[128];
snprintf(buf, sizeof(buf), "%s/ndb", net);
if (stat(buf, &s) < 0)
return 0;
if (s.st_size == 0)
return 0;
return 1;
}
void doadd(int retry)
{
/* get number of preexisting interfaces */
nip = nipifcs(conf.mpoint);
if (beprimary == -1 && (nip == 0 || !havendb(conf.mpoint)))
beprimary = 1;
/* get ipifc into name space and condition device for ip */
if (!noconfig) {
lookforip(conf.mpoint);
controldevice();
binddevice();
}
if (ipv6auto) {
if (ip6cfg(ipv6auto) < 0) {
fprintf(stderr, "can't automatically start IPv6 on %s\n",
conf.dev);
evexit(-1);
}
} else if (validip(conf.laddr) && !isv4(conf.laddr)) {
if (ip6cfg(0) < 0)
fprintf(stderr, "can't start IPv6 on %s, address %R\n",
conf.dev, conf.laddr);
evexit(-1);
}
if (!validip(conf.laddr)) {
if (dondbconfig)
ndbconfig();
else
dodhcp = 1;
}
/* run dhcp if we need something */
if (dodhcp) {
mkclientid();
dhcpquery(!noconfig, Sselecting);
}
if (!validip(conf.laddr)) {
if (retry && dodhcp && !noconfig) {
warning("couldn't determine ip address, retrying");
dhcpwatch(1);
return;
}
fprintf(stderr, "no success with DHCP\n");
evexit(-1);
}
if (!noconfig) {
if (ip4cfg() < 0) {
fprintf(stderr, "can't start ip\n");
evexit(-1);
}
if (dodhcp && conf.lease != Lforever)
dhcpwatch(0);
}
/* leave everything we've learned somewhere other procs can find it */
if (beprimary == 1) {
putndb();
tweakservers();
}
}
void doremove(void)
{
char file[128];
char buf[256];
int cfd;
struct ipifc *nifc;
struct iplifc *lifc;
if (!validip(conf.laddr)) {
fprintf(stderr, "remove requires an address\n");
evexit(-1);
}
ifc = readipifc(conf.mpoint, ifc, -1);
for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
if (strcmp(nifc->dev, conf.dev) != 0)
continue;
for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next) {
if (ipcmp(conf.laddr, lifc->ip) != 0)
continue;
if (validip(conf.mask) && ipcmp(conf.mask, lifc->mask)
!= 0)
continue;
if (validip(conf.raddr) && ipcmp(conf.raddr, lifc->net)
!= 0)
continue;
snprintf(file, sizeof(file), "%s/ipifc/%d/ctl",
conf.mpoint, nifc->index);
cfd = open(file, O_RDWR);
if (cfd < 0) {
warning("can't open %s: %r", conf.mpoint);
continue;
}
snprintf(buf, sizeof(buf), "remove %R %M", lifc->ip,
lifc->mask);
if (write(cfd, buf, strlen(buf)) != strlen(buf))
warning("can't remove %R %M from %s: %r",
lifc->ip, lifc->mask, file);
close(cfd);
}
}
}
void dounbind(void)
{
struct ipifc *nifc;
char file[128];
int cfd;
ifc = readipifc(conf.mpoint, ifc, -1);
for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
if (strcmp(nifc->dev, conf.dev) == 0) {
snprintf(file, sizeof(file), "%s/ipifc/%d/ctl",
conf.mpoint, nifc->index);
cfd = open(file, O_RDWR);
if (cfd < 0) {
warning("can't open %s: %r", conf.mpoint);
break;
}
if (write(cfd, "unbind", strlen("unbind")) < 0)
warning("can't unbind from %s: %r", file);
close(cfd);
break;
}
}
}
/* set the default route */
void adddefroute(char *mpoint, uint8_t *gaddr)
{
char buf[256];
int cfd;
sprintf(buf, "%s/iproute", mpoint);
cfd = open(buf, O_RDWR);
if (cfd < 0)
return;
if (isv4(gaddr))
snprintf(buf, sizeof(buf), "add 0 0 %R", gaddr);
else
snprintf(buf, sizeof(buf), "add :: /0 %R", gaddr);
write(cfd, buf, strlen(buf));
close(cfd);
}
/* create a client id */
void mkclientid(void)
{
if ((strcmp(conf.type, "ether") == 0) ||
(strcmp(conf.type, "gbe") == 0)) {
if (myetheraddr(conf.hwa, conf.dev) == 0) {
conf.hwalen = 6;
conf.hwatype = 1;
conf.cid[0] = conf.hwatype;
memmove(&conf.cid[1], conf.hwa, conf.hwalen);
conf.cidlen = conf.hwalen + 1;
} else {
conf.hwatype = -1;
snprintf((char *)conf.cid, sizeof(conf).cid,
"plan9_%ld.%d", lrand48(), getpid());
conf.cidlen = strlen((char *)conf.cid);
}
}
}
/* bind ip into the namespace */
void lookforip(char *net)
{
struct stat s;
char proto[64];
snprintf(proto, sizeof(proto), "%s/ipifc", net);
if (stat(proto, &s) < 0) {
fprintf(stderr, "no ip stack bound onto %s\n", net);
evexit(-1);
}
}
/* send some ctls to a device */
void controldevice(void)
{
char ctlfile[256];
int fd;
struct ctl *cp;
if (firstctl == NULL ||
(strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0))
return;
snprintf(ctlfile, sizeof(ctlfile), "%s/clone", conf.dev);
fd = open(ctlfile, O_RDWR);
if (fd < 0) {
fprintf(stderr, "can't open %s\n", ctlfile);
evexit(-1);
}
for (cp = firstctl; cp != NULL; cp = cp->next) {
if (write(fd, cp->ctl, strlen(cp->ctl)) < 0) {
fprintf(stderr, "ctl message %s: %r\n", cp->ctl);
evexit(-1);
}
lseek(fd, 0, 0);
}
}
/* bind an ip stack to a device, leave the control channel open */
void binddevice(void)
{
char buf[256];
if (myifc < 0) {
/* get a new ip interface */
snprintf(buf, sizeof(buf), "%s/ipifc/clone", conf.mpoint);
conf.cfd = open(buf, O_RDWR);
if (conf.cfd < 0) {
fprintf(stderr, "opening %s/ipifc/clone: %r\n",
conf.mpoint);
evexit(-1);
}
/* specify medium as ethernet, bind the interface to it */
snprintf(buf, sizeof(buf), "bind %s %s", conf.type, conf.dev);
if (write(conf.cfd, buf, strlen(buf)) != strlen(buf)) {
fprintf(stderr, "%s: bind %s %s: %r\n", buf, conf.type,
conf.dev);
evexit(-1);
}
} else {
/* open the old interface */
snprintf(buf, sizeof(buf), "%s/ipifc/%d/ctl", conf.mpoint,
myifc);
conf.cfd = open(buf, O_RDWR);
if (conf.cfd < 0) {
fprintf(stderr, "open %s: %r\n", buf);
evexit(-1);
}
}
}
/* add a logical interface to the ip stack */
int ip4cfg(void)
{
char buf[256];
int n;
if (!validip(conf.laddr))
return -1;
n = snprintf(buf, sizeof(buf), "add");
n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr);
if (!validip(conf.mask))
ipmove(conf.mask, defmask(conf.laddr));
n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask);
if (validip(conf.raddr)) {
n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.raddr);
if (conf.mtu != 0)
n += snprintf(buf + n, sizeof(buf) - n, " %d",
conf.mtu);
}
if (write(conf.cfd, buf, n) < 0) {
warning("write(%s): %r", buf);
return -1;
}
if (beprimary == 1 && validip(conf.gaddr))
adddefroute(conf.mpoint, conf.gaddr);
return 0;
}
/* remove a logical interface to the ip stack */
void ipunconfig(void)
{
char buf[256];
int n;
if (!validip(conf.laddr))
return;
DEBUG("couldn't renew IP lease, releasing %R", conf.laddr);
n = sprintf(buf, "remove");
n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr);
if (!validip(conf.mask))
ipmove(conf.mask, defmask(conf.laddr));
n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask);
write(conf.cfd, buf, n);
ipmove(conf.laddr, IPnoaddr);
ipmove(conf.raddr, IPnoaddr);
ipmove(conf.mask, IPnoaddr);
/* forget configuration info */
if (beprimary == 1)
writendb("", 0, 0);
}
void dhcpquery(int needconfig, int startstate)
{
char buf[256];
if (needconfig) {
snprintf(buf, sizeof(buf), "add %R %R", IPnoaddr, IPnoaddr);
write(conf.cfd, buf, strlen(buf));
}
conf.fd = openlisten();
if (conf.fd < 0) {
conf.state = Sinit;
return;
}
/* try dhcp for 10 seconds */
conf.xid = lrand48();
conf.starttime = time(0);
conf.state = startstate;
switch (startstate) {
case Sselecting:
conf.offered = 0;
dhcpsend(Discover);
break;
case Srenewing:
dhcpsend(Request);
break;
default:
fprintf(stderr, "internal error 0\n");
evexit(-1);
}
conf.resend = 0;
conf.timeout = time(0) + 4;
while (conf.state != Sbound) {
dhcprecv();
if (dhcptimer() < 0)
break;
if (time(0) - conf.starttime > 10)
break;
}
close(conf.fd);
if (needconfig) {
snprintf(buf, sizeof(buf), "remove %R %R", IPnoaddr, IPnoaddr);
write(conf.cfd, buf, strlen(buf));
}
}
enum {
// This was an hour, but needs to be less for the ARM/GS1 until the
// timer code has been cleaned up (pb).
Maxsleep = 450,
};
static void *dhcpwatchthr(void *arg);
void dhcpwatch(int needconfig)
{
pthread_t tid;
intptr_t nc = needconfig;
if (nodhcpwatch)
return;
pthread_create(&tid, NULL, dhcpwatchthr, (void *)nc);
}
static void *dhcpwatchthr(void *arg)
{
int secs, s;
uint32_t t;
int needconfig = (arg == NULL);
/* keep trying to renew the lease */
for (;;) {
secs = conf.lease / 2;
if (secs == 0)
secs = 5;
/* avoid overflows */
for (s = secs; s > 0; s -= t) {
t = s;
if (t > Maxsleep)
t = Maxsleep;
usleep(t * 1000 * 1000);
}
if (conf.lease > 0) {
/*
* during boot, the starttime can be bogus so avoid
* spurious ipunconfig's
*/
t = time(0) - conf.starttime;
if (t > (3 * secs) / 2)
t = secs;
if (t >= conf.lease) {
conf.lease = 0;
if (!noconfig) {
ipunconfig();
needconfig = 1;
}
} else
conf.lease -= t;
}
dhcpquery(needconfig, needconfig ? Sselecting : Srenewing);
if (needconfig && conf.state == Sbound) {
if (ip4cfg() < 0) {
fprintf(stderr, "can't start ip: %r\n");
evexit(-1);
}
needconfig = 0;
/*
* leave everything we've learned somewhere that
* other procs can find it.
*/
if (beprimary == 1) {
putndb();
tweakservers();
}
}
}
return NULL;
}
int dhcptimer(void)
{
uint32_t now;
now = time(0);
if (now < conf.timeout)
return 0;
switch (conf.state) {
default:
fprintf(stderr, "dhcptimer: unknown state %d\n", conf.state);
evexit(-1);
case Sinit:
case Sbound:
break;
case Sselecting:
case Srequesting:
case Srebinding:
dhcpsend(conf.state == Sselecting ? Discover : Request);
conf.timeout = now + 4;
if (++conf.resend > 5) {
conf.state = Sinit;
return -1;
}
break;
case Srenewing:
dhcpsend(Request);
conf.timeout = now + 1;
if (++conf.resend > 3) {
conf.state = Srebinding;
conf.resend = 0;
}
break;
}
return 0;
}
void dhcpsend(int type)
{
struct bootp bp;
uint8_t *p;
int n;
uint8_t vendor[64];
struct udphdr *up = (struct udphdr *)bp.udphdr;
memset(&bp, 0, sizeof(bp));
hnputs(up->rport, 67);
bp.op = Bootrequest;
hnputl(bp.xid, conf.xid);
hnputs(bp.secs, time(0) - conf.starttime);
hnputs(bp.flags, 0);
memmove(bp.optmagic, optmagic, 4);
if (conf.hwatype >= 0 && conf.hwalen < sizeof(bp).chaddr) {
memmove(bp.chaddr, conf.hwa, conf.hwalen);
bp.hlen = conf.hwalen;
bp.htype = conf.hwatype;
}
p = bp.optdata;
p = optaddbyte(p, ODtype, type);
p = optadd(p, ODclientid, conf.cid, conf.cidlen);
switch (type) {
default:
fprintf(stderr, "dhcpsend: unknown message type: %d\n", type);
evexit(-1);
case Discover:
ipmove(up->raddr, IPv4bcast); /* broadcast */
if (*conf.hostname && sendhostname)
p = optaddstr(p, OBhostname, conf.hostname);
if (plan9) {
n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s",
conf.cputype);
p = optaddvec(p, ODvendorclass, vendor, n);
}
p = optaddvec(p, ODparams, requested, nrequested);
if (validip(conf.laddr))
p = optaddaddr(p, ODipaddr, conf.laddr);
break;
case Request:
switch (conf.state) {
case Srenewing:
ipmove(up->raddr, conf.server);
v6tov4(bp.ciaddr, conf.laddr);
break;
case Srebinding:
ipmove(up->raddr, IPv4bcast); /* broadcast */
v6tov4(bp.ciaddr, conf.laddr);
break;
case Srequesting:
ipmove(up->raddr, IPv4bcast); /* broadcast */
p = optaddaddr(p, ODipaddr, conf.laddr);
p = optaddaddr(p, ODserverid, conf.server);
break;
}
p = optaddulong(p, ODlease, conf.offered);
if (plan9) {
n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s",
conf.cputype);
p = optaddvec(p, ODvendorclass, vendor, n);
}
p = optaddvec(p, ODparams, requested, nrequested);
if (*conf.hostname && sendhostname)
p = optaddstr(p, OBhostname, conf.hostname);
break;
case Release:
ipmove(up->raddr, conf.server);
v6tov4(bp.ciaddr, conf.laddr);
p = optaddaddr(p, ODipaddr, conf.laddr);
p = optaddaddr(p, ODserverid, conf.server);
break;
}
*p++ = OBend;
n = p - (uint8_t *)&bp;
/*
* We use a maximum size DHCP packet to survive the
* All_Aboard NAT package from Internet Share. It
* always replies to DHCP requests with a packet of the
* same size, so if the request is too short the reply
* is truncated.
*/
if (write(conf.fd, &bp, sizeof(bp)) != sizeof(bp))
warning("dhcpsend: write failed: %r");
}
void rerrstr(char *buf, size_t buflen)
{
snprintf(buf, buflen, "%s", errstr());
}
void dhcprecv(void)
{
int i, n, type;
uint32_t lease;
char err[256];
uint8_t buf[8000], vopts[256], taddr[IPaddrlen];
struct bootp *bp;
struct alarm_waiter waiter;
init_awaiter(&waiter, alarm_abort_sysc);
waiter.data = current_uthread;
memset(buf, 0, sizeof(buf));
set_awaiter_rel(&waiter, 1000 * 1000);
set_alarm(&waiter);
n = read(conf.fd, buf, sizeof(buf));
unset_alarm(&waiter);
if (n < 0) {
rerrstr(err, sizeof(err));
if (strstr(err, "syscall aborted") == NULL)
warning("dhcprecv: bad read: %s", err);
else
DEBUG("dhcprecv: read timed out");
return;
}
if (n == 0) {
warning("dhcprecv: zero-length packet read");
return;
}
bp = parsebootp(buf, n);
if (bp == 0) {
DEBUG("parsebootp failed: dropping packet");
return;
}
type = optgetbyte(bp->optdata, ODtype);
switch (type) {
default:
warning("dhcprecv: unknown type: %d", type);
break;
case Offer:
DEBUG("got offer from %R ", bp->siaddr);
if (conf.state != Sselecting) {
DEBUG("");
break;
}
lease = optgetulong(bp->optdata, ODlease);
if (lease == 0) {
/*
* The All_Aboard NAT package from Internet Share
* doesn't give a lease time, so we have to assume one.
*/
warning("Offer with %lud lease, using %d", lease,
MinLease);
lease = MinLease;
}
DEBUG("lease=%lud ", lease);
if (!optgetaddr(bp->optdata, ODserverid, conf.server)) {
warning("Offer from server with invalid serverid");
break;
}
v4tov6(conf.laddr, bp->yiaddr);
memmove(conf.sname, bp->sname, sizeof(conf).sname);
conf.sname[sizeof(conf).sname - 1] = 0;
DEBUG("server=%R sname=%s", conf.server, conf.sname);
conf.offered = lease;
conf.state = Srequesting;
dhcpsend(Request);
conf.resend = 0;
conf.timeout = time(0) + 4;
break;
case Ack:
DEBUG("got ack from %R ", bp->siaddr);
if (conf.state != Srequesting && conf.state != Srenewing &&
conf.state != Srebinding)
break;
/* ignore a bad lease */
lease = optgetulong(bp->optdata, ODlease);
if (lease == 0) {
/*
* The All_Aboard NAT package from Internet Share
* doesn't give a lease time, so we have to assume one.
*/
warning("Ack with %lud lease, using %d", lease,
MinLease);
lease = MinLease;
}
DEBUG("lease=%lud ", lease);
/* address and mask */
if (!validip(conf.laddr) || !Oflag)
v4tov6(conf.laddr, bp->yiaddr);
if (!validip(conf.mask) || !Oflag) {
if (!optgetaddr(bp->optdata, OBmask, conf.mask))
ipmove(conf.mask, IPnoaddr);
}
DEBUG("ipaddr=%R ipmask=%M ", conf.laddr, conf.mask);
/*
* get a router address either from the router option
* or from the router that forwarded the dhcp packet
*/
if (validip(conf.gaddr) && Oflag) {
DEBUG("ipgw=%R ", conf.gaddr);
} else if (optgetaddr(bp->optdata, OBrouter, conf.gaddr)) {
DEBUG("ipgw=%R ", conf.gaddr);
} else if (memcmp(bp->giaddr, IPnoaddr + IPv4off, IPv4addrlen)
!= 0) {
v4tov6(conf.gaddr, bp->giaddr);
DEBUG("giaddr=%R ", conf.gaddr);
}
/* get dns servers */
memset(conf.dns, 0, sizeof(conf).dns);
n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
sizeof(conf).dns / IPaddrlen);
for (i = 0; i < n; i++)
DEBUG("dns=%R ", conf.dns + i * IPaddrlen);
/* get ntp servers */
memset(conf.ntp, 0, sizeof(conf).ntp);
n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
sizeof(conf).ntp / IPaddrlen);
for (i = 0; i < n; i++)
DEBUG("ntp=%R ", conf.ntp + i * IPaddrlen);
/* get names */
optgetstr(bp->optdata, OBhostname,
conf.hostname, sizeof(conf).hostname);
optgetstr(bp->optdata, OBdomainname,
conf.domainname, sizeof(conf).domainname);
/* get anything else we asked for */
getoptions(bp->optdata);
/* get plan9-specific options */
n = optgetvec(bp->optdata, OBvendorinfo, vopts,
sizeof(vopts) - 1);
if (n > 0 && parseoptions(vopts, n) == 0) {
n = 1;
if (!validip(conf.fs) || !Oflag) {
n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
if (n == 0)
n = optgetaddrs(vopts, OP9fsv4, conf.fs,
2);
}
for (i = 0; i < n; i++)
DEBUG("fs=%R ", conf.fs + i * IPaddrlen);
n = 1;
if (!validip(conf.auth) || !Oflag) {
n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
if (n == 0)
n = optgetaddrs(vopts, OP9authv4,
conf.auth, 2);
}
for (i = 0; i < n; i++)
DEBUG("auth=%R ", conf.auth + i * IPaddrlen);
n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
if (n > 0)
memmove(conf.laddr, taddr, IPaddrlen);
n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
if (n > 0)
memmove(conf.mask, taddr, IPaddrlen);
n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
if (n > 0)
memmove(conf.gaddr, taddr, IPaddrlen);
DEBUG("new ipaddr=%R new ipmask=%M new ipgw=%R",
conf.laddr, conf.mask, conf.gaddr);
}
conf.lease = lease;
conf.state = Sbound;
DEBUG("server=%R sname=%s", conf.server, conf.sname);
break;
case Nak:
conf.state = Sinit;
warning("recved dhcpnak on %s", conf.mpoint);
break;
}
}
/* return pseudo-random integer in range low...(hi-1) */
uint32_t randint(uint32_t low, uint32_t hi)
{
if (hi < low)
return low;
return low + (lrand48() % hi);
}
// compute small pseudo-random delay in ms
long jitter(void)
{
return randint(0, 10 * 1000);
}
int openlisten(void)
{
int n, fd, cfd;
char data[128], devdir[40];
if (validip(conf.laddr) &&
(conf.state == Srenewing || conf.state == Srebinding))
sprintf(data, "%s/udp!%R!68", conf.mpoint, conf.laddr);
else
sprintf(data, "%s/udp!*!68", conf.mpoint);
for (n = 0; (cfd = announce9(data, devdir, 0)) < 0; n++) {
if (!noconfig) {
fprintf(stderr, "can't announce for dhcp: %r\n");
evexit(-1);
}
/* might be another client - wait and try again */
warning("can't announce %s: %r", data);
usleep(jitter() * 1000);
if (n > 10)
return -1;
}
if (write(cfd, "headers", strlen("headers")) < 0) {
fprintf(stderr, "can't set header mode: %r\n");
evexit(-1);
}
sprintf(data, "%s/data", devdir);
fd = open(data, O_RDWR);
if (fd < 0) {
fprintf(stderr, "open %s: %r\n", data);
evexit(-1);
}
close(cfd);
return fd;
}
uint8_t *optadd(uint8_t *p, int op, void *d, int n)
{
p[0] = op;
p[1] = n;
memmove(p + 2, d, n);
return p + n + 2;
}
uint8_t *optaddbyte(uint8_t *p, int op, int b)
{
p[0] = op;
p[1] = 1;
p[2] = b;
return p + 3;
}
uint8_t *optaddulong(uint8_t *p, int op, uint32_t x)
{
p[0] = op;
p[1] = 4;
hnputl(p + 2, x);
return p + 6;
}
uint8_t *optaddaddr(uint8_t *p, int op, uint8_t *ip)
{
p[0] = op;
p[1] = 4;
v6tov4(p + 2, ip);
return p + 6;
}
/* add dhcp option op with value v of length n to dhcp option array p */
uint8_t *optaddvec(uint8_t *p, int op, uint8_t *v, int n)
{
p[0] = op;
p[1] = n;
memmove(p + 2, v, n);
return p + 2 + n;
}
uint8_t *optaddstr(uint8_t *p, int op, char *v)
{
int n;
n = strlen(v) + 1; /* microsoft leaves on the NUL, so we do too */
p[0] = op;
p[1] = n;
memmove(p + 2, v, n);
return p + 2 + n;
}
/*
* parse p, looking for option `op'. if non-nil, np points to minimum length.
* return NULL if option is too small, else ptr to opt, and
* store actual length via np if non-nil.
*/
uint8_t *optget(uint8_t *p, int op, int *np)
{
int len, code;
while ((code = *p++) != OBend) {
if (code == OBpad)
continue;
len = *p++;
if (code != op) {
p += len;
continue;
}
if (np != NULL) {
if (*np > len)
return 0;
*np = len;
}
return p;
}
return 0;
}
int optgetbyte(uint8_t *p, int op)
{
int len;
len = 1;
p = optget(p, op, &len);
if (p == NULL)
return 0;
return *p;
}
uint32_t optgetulong(uint8_t *p, int op)
{
int len;
len = 4;
p = optget(p, op, &len);
if (p == NULL)
return 0;
return nhgetl(p);
}
int optgetaddr(uint8_t *p, int op, uint8_t *ip)
{
int len;
len = 4;
p = optget(p, op, &len);
if (p == NULL)
return 0;
v4tov6(ip, p);
return 1;
}
/* expect at most n addresses; ip[] only has room for that many */
int optgetaddrs(uint8_t *p, int op, uint8_t *ip, int n)
{
int len, i;
len = 4;
p = optget(p, op, &len);
if (p == NULL)
return 0;
len /= IPv4addrlen;
if (len > n)
len = n;
for (i = 0; i < len; i++)
v4tov6(&ip[i * IPaddrlen], &p[i * IPv4addrlen]);
return i;
}
/* expect at most n addresses; ip[] only has room for that many */
int optgetp9addrs(uint8_t *ap, int op, uint8_t *ip, int n)
{
int len, i, slen, addrs;
char *p;
len = 1; /* minimum bytes needed */
p = (char *)optget(ap, op, &len);
if (p == NULL)
return 0;
addrs = *p++; /* first byte is address count */
for (i = 0; i < n && i < addrs && len > 0; i++) {
slen = strlen(p) + 1;
if (parseip(&ip[i * IPaddrlen], p) == -1)
fprintf(stderr, "%s: bad address %s\n", argv0, p);
DEBUG("got plan 9 option %d addr %R (%s)", op, &ip[i * IPaddrlen], p);
p += slen;
len -= slen;
}
return addrs;
}
int optgetvec(uint8_t *p, int op, uint8_t *v, int n)
{
int len;
len = 1;
p = optget(p, op, &len);
if (p == NULL)
return 0;
if (len > n)
len = n;
memmove(v, p, len);
return len;
}
int optgetstr(uint8_t *p, int op, char *s, int n)
{
int len;
len = 1;
p = optget(p, op, &len);
if (p == NULL)
return 0;
if (len >= n)
len = n - 1;
memmove(s, p, len);
s[len] = 0;
return len;
}
/*
* sanity check options area
* - options don't overflow packet
* - options end with an OBend
*/
int parseoptions(uint8_t *p, int n)
{
int code, len, nin = n;
while (n > 0) {
code = *p++;
n--;
if (code == OBend)
return 0;
if (code == OBpad)
continue;
if (n == 0) {
warning(
"parseoptions: bad option: 0x%x: truncated: opt length = %d",
code, nin);
return -1;
}
len = *p++;
n--;
DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
option[code].name, code, len, n);
if (len > n) {
warning(
"parseoptions: bad option: 0x%x: %d > %d: opt length = %d",
code, len, n, nin);
return -1;
}
p += len;
n -= len;
}
/* make sure packet ends with an OBend after all the optget code */
*p = OBend;
return 0;
}
/*
* sanity check received packet:
* - magic is dhcp magic
* - options don't overflow packet
*/
struct bootp *parsebootp(uint8_t *p, int n)
{
struct bootp *bp;
bp = (struct bootp *)p;
if (n < bp->optmagic - p) {
warning(
"parsebootp: short bootp packet; with options, need %d bytes, got %d",
bp->optmagic - p, n);
return NULL;
}
if (conf.xid != nhgetl(bp->xid)) /* not meant for us */
return NULL;
if (bp->op != Bootreply) {
warning("parsebootp: bad op %d", bp->op);
return NULL;
}
n -= bp->optmagic - p;
p = bp->optmagic;
if (n < 4) {
warning("parsebootp: no option data");
return NULL;
}
if (memcmp(optmagic, p, 4) != 0) {
warning("parsebootp: bad opt magic %x %x %x %x",
p[0], p[1], p[2], p[3]);
return NULL;
}
p += 4;
n -= 4;
DEBUG("parsebootp: new packet");
if (parseoptions(p, n) < 0)
return NULL;
return bp;
}
/* write out an ndb entry */
void writendb(char *s, int n, int append)
{
char file[64];
int fd;
snprintf(file, sizeof(file), "%s/ndb", conf.mpoint);
if (append) {
fd = open(file, O_WRITE);
lseek(fd, 0, 2);
} else
fd = open(file, O_WRITE | O_TRUNC);
write(fd, s, n);
close(fd);
}
/* put server addresses into the ndb entry */
size_t putaddrs(char *buf, size_t size, char *attr, uint8_t *a, int len)
{
int i;
size_t n;
char *p;
n = 0;
p = "";
for (i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen) {
n += snprintf(buf + n, size - n, "%s%s=%R\n", p, attr, a);
p = " ";
}
return n;
}
/* make an ndb entry and put it into /net/ndb for the servers to see */
void putndb(void)
{
int append;
char buf[1024];
char *np;
size_t n;
buf[0] = '\0';
n = 0;
if (getndb() == 0)
append = 1;
else {
append = 0;
n += snprintf(buf + n, sizeof(buf) - n,
"ip=%R ipmask=%M ipgw=%R\n",
conf.laddr, conf.mask, conf.gaddr);
}
np = strchr(conf.hostname, '.');
if (np != NULL) {
if (*conf.domainname == 0)
snprintf(conf.domainname, sizeof(conf).domainname, "%s",
np + 1);
*np = 0;
}
if (*conf.hostname)
n += snprintf(buf + n, sizeof(buf) - n, "\tsys=%s\n",
conf.hostname);
if (*conf.domainname)
n += snprintf(buf + n, sizeof(buf) - n, "\tdom=%s.%s\n",
conf.hostname, conf.domainname);
if (validip(conf.fs))
n += putaddrs(buf + n, sizeof(buf) - n, "\tfs",
conf.fs, sizeof(conf).fs);
if (validip(conf.auth))
n += putaddrs(buf + n, sizeof(buf) - n, "\tauth",
conf.auth, sizeof(conf).auth);
if (validip(conf.dns))
n += putaddrs(buf + n, sizeof(buf) - n, "\tdns",
conf.dns, sizeof(conf).dns);
if (validip(conf.ntp))
n += putaddrs(buf + n, sizeof(buf) - n, "\tntp",
conf.ntp, sizeof(conf).ntp);
if (ndboptions)
n += snprintf(buf + n, sizeof(buf) - n, "%s\n", ndboptions);
if (n > 0)
writendb(buf, n, append);
}
/* get an ndb entry someone else wrote */
int getndb(void)
{
char buf[1024];
int fd, n;
char *p;
snprintf(buf, sizeof(buf), "%s/ndb", conf.mpoint);
fd = open(buf, O_RDONLY);
n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n <= 0)
return -1;
buf[n] = 0;
p = strstr(buf, "ip=");
if (p == NULL)
return -1;
if (parseip(conf.laddr, p + 3) == -1)
fprintf(stderr, "%s: bad address %s\n", argv0, p + 3);
return 0;
}
/* tell a server to refresh */
void tweakserver(char *server)
{
int fd;
char file[64];
snprintf(file, sizeof(file), "%s/%s", conf.mpoint, server);
fd = open(file, O_RDWR);
if (fd < 0)
return;
write(fd, "refresh", strlen("refresh"));
close(fd);
}
/* tell all servers to refresh their information */
void tweakservers(void)
{
tweakserver("dns");
tweakserver("cs");
}
/* return number of networks */
int nipifcs(char *net)
{
int n;
struct ipifc *nifc;
struct iplifc *lifc;
n = 0;
ifc = readipifc(net, ifc, -1);
for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
/*
* ignore loopback devices when trying to
* figure out if we're the primary interface.
*/
if (strcmp(nifc->dev, "/dev/null") != 0)
for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next)
if (validip(lifc->ip)) {
n++;
break;
}
if (strcmp(nifc->dev, conf.dev) == 0)
myifc = nifc->index;
}
return n;
}
/* return true if this is a valid v4 address */
int validip(uint8_t *addr)
{
return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
}
/* look for an action */
int parseverb(char *name)
{
int i;
for (i = 0; i < COUNT_OF(verbs); i++)
if (verbs[i] != NULL && strcmp(name, verbs[i]) == 0)
return i;
return -1;
}
/* get everything out of ndb */
void ndbconfig(void)
{
int nattr, nauth = 0, ndns = 0, nfs = 0, ok;
char etheraddr[32];
char *attrs[10];
struct ndb *db;
struct ndbtuple *t, *nt;
db = ndbopen(0);
if (db == NULL) {
fprintf(stderr, "can't open ndb: %r\n");
evexit(-1);
}
if ((strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0)
|| myetheraddr(conf.hwa, conf.dev) != 0) {
fprintf(stderr, "can't read hardware address\n");
evexit(-1);
}
snprintf(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
nattr = 0;
attrs[nattr++] = "ip";
attrs[nattr++] = "ipmask";
attrs[nattr++] = "ipgw";
/* the @ triggers resolution to an IP address; see ndb(2) */
attrs[nattr++] = "@dns";
attrs[nattr++] = "@ntp";
attrs[nattr++] = "@fs";
attrs[nattr++] = "@auth";
attrs[nattr] = NULL;
t = ndbipinfo(db, "ether", etheraddr, attrs, nattr);
for (nt = t; nt != NULL; nt = nt->entry) {
ok = 1;
if (strcmp(nt->attr, "ip") == 0)
ok = parseip(conf.laddr, nt->val);
else if (strcmp(nt->attr, "ipmask") == 0)
parseipmask(conf.mask, nt->val); /* could be -1 */
else if (strcmp(nt->attr, "ipgw") == 0)
ok = parseip(conf.gaddr, nt->val);
else if (ndns < 2 && strcmp(nt->attr, "dns") == 0)
ok = parseip(conf.dns + IPaddrlen * ndns, nt->val);
else if (strcmp(nt->attr, "ntp") == 0)
ok = parseip(conf.ntp, nt->val);
else if (nfs < 2 && strcmp(nt->attr, "fs") == 0)
ok = parseip(conf.fs + IPaddrlen * nfs, nt->val);
else if (nauth < 2 && strcmp(nt->attr, "auth") == 0)
ok = parseip(conf.auth + IPaddrlen * nauth, nt->val);
if (!ok)
fprintf(stderr, "%s: bad %s address in ndb: %s\n",
argv0, nt->attr, nt->val);
}
ndbfree(t);
if (!validip(conf.laddr)) {
fprintf(stderr, "address not found in ndb\n");
evexit(-1);
}
}
int addoption(char *opt)
{
int i;
struct option *o;
if (opt == NULL)
return -1;
for (o = option; o < &option[COUNT_OF(option)]; o++)
if (o->name && strcmp(opt, o->name) == 0) {
i = o - option;
if (memchr(requested, i, nrequested) == 0 &&
nrequested < COUNT_OF(requested))
requested[nrequested++] = i;
return 0;
}
return -1;
}
char *optgetx(uint8_t *p, uint8_t opt)
{
int i, n;
uint32_t x;
char str[256], buf[1024];
uint8_t ip[IPaddrlen], ips[16 * IPaddrlen], vec[256];
size_t l;
struct option *o;
o = &option[opt];
if (o->name == NULL)
return NULL;
memset(buf, '\0', sizeof(buf));
switch (o->type) {
case Taddr:
if (optgetaddr(p, opt, ip))
snprintf(buf, sizeof(buf), "%s=%R", o->name, ip);
break;
case Taddrs:
n = optgetaddrs(p, opt, ips, 16);
if (n > 0)
l = snprintf(buf, sizeof(buf), "%s=%R", o->name, ips);
for (i = 1; i < n; i++) {
l += snprintf(buf + l, sizeof(buf) - l, " %s=%R",
o->name, &ips[i * IPaddrlen]);
}
break;
case Tulong:
x = optgetulong(p, opt);
if (x != 0)
snprintf(buf, sizeof(buf), "%s=%lud", o->name, x);
break;
case Tbyte:
x = optgetbyte(p, opt);
if (x != 0)
snprintf(buf, sizeof(buf), "%s=%lud", o->name, x);
break;
case Tstr:
if (optgetstr(p, opt, str, sizeof(str)))
snprintf(buf, sizeof(buf), "%s=%s", o->name, str);
break;
case Tvec:
n = optgetvec(p, opt, vec, sizeof(vec));
if (n > 0) /* what's %H? it's not installed */
snprintf(buf, sizeof(buf), "%s=%.*H", o->name, n, vec);
break;
}
return strdup(buf);
}
void getoptions(uint8_t *p)
{
int i;
char buf[1024];
char *s;
for (i = COUNT_OF(defrequested); i < nrequested; i++) {
s = optgetx(p, requested[i]);
if (s == NULL)
continue;
DEBUG("%s ", s);
snprintf(buf, sizeof(buf),
(ndboptions == NULL) ? "\t%s" : "\t%s%s",
s, ndboptions);
free(ndboptions);
ndboptions = strdup(buf);
free(s);
}
}