/*
* ipconfig - configure parameters of an ip stack
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <ndb.h>
#include "ipconfig.h"
#include "../dhcp.h"
#include <libsec.h> /* genrandom() */
Conf conf;
int myifc = -1;
int beprimary = -1;
int noconfig;
Ipifc *ifc;
Ctl *firstctl, **ctll = &firstctl;
int debug;
int dolog;
int plan9 = 1;
int Oflag;
int rflag;
int dodhcp;
int nodhcpwatch;
int sendhostname;
char *ndboptions;
int ipv6auto;
int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
int dondbconfig;
char *dbfile;
static char logfile[] = "ipconfig";
static void binddevice(void);
static void controldevice(void);
extern void pppbinddev(void);
static void doadd(void);
static void doremove(void);
static void dounbind(void);
static void ndbconfig(void);
static int Ufmt(Fmt*);
#pragma varargck type "U" char*
void
usage(void)
{
fprint(2, "usage: %s [-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw]"
"[-h host][-m mtu]\n"
"\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
"[raddr [fs [auth]]]]]\n", argv0);
exits("usage");
}
static void
init(void)
{
srand(truerand());
fmtinstall('H', encodefmt);
fmtinstall('E', eipfmt);
fmtinstall('I', eipfmt);
fmtinstall('M', eipfmt);
fmtinstall('V', eipfmt);
fmtinstall('U', Ufmt);
nsec(); /* make sure time file is open before forking */
conf.cfd = -1;
conf.rfd = -1;
setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
conf.cputype = getenv("cputype");
if(conf.cputype == nil)
conf.cputype = "386";
v6paraminit(&conf);
dhcpinit();
}
void
warning(char *fmt, ...)
{
char buf[1024];
va_list arg;
va_start(arg, fmt);
vseprint(buf, buf + sizeof buf, fmt, arg);
va_end(arg);
if (dolog)
syslog(0, logfile, "%s", buf);
else
fprint(2, "%s: %s\n", argv0, buf);
}
static 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:
if (strcmp(argv[1], "0") != 0){
if (parseipandmask(conf.laddr, conf.mask, argv[0], argv[1]) == -1)
usage();
break;
}
/* fall through */
case 1:
if (parseip(conf.laddr, argv[0]) == -1)
usage();
/* fall through */
case 0:
break;
default:
usage();
}
}
static char*
finddev(char *dir, char *name, char *dev)
{
int fd, i, nd;
Dir *d;
fd = open(dir, OREAD);
if(fd >= 0){
d = nil;
nd = dirreadall(fd, &d);
close(fd);
for(i=0; i<nd; i++){
if(strncmp(d[i].name, name, strlen(name)))
continue;
if(strstr(d[i].name, "ctl") != nil)
continue; /* ignore ctl files */
dev = smprint("%s/%s", dir, d[i].name);
break;
}
free(d);
}
return dev;
}
/* look for an action */
static int
parseverb(char *name)
{
static char *verbs[] = {
[Vadd] "add",
[Vremove] "remove",
[Vunbind] "unbind",
[Vether] "ether",
[Vgbe] "gbe",
[Vppp] "ppp",
[Vloopback] "loopback",
[Vaddpref6] "add6",
[Vra6] "ra6",
[Vtorus] "torus",
[Vtree] "tree",
[Vpkt] "pkt",
};
int i;
for(i = 0; i < nelem(verbs); i++)
if(verbs[i] != nil && strcmp(name, verbs[i]) == 0)
return i;
return -1;
}
static 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 == nil || *p == 0)
p = sysname();
if(p != nil)
utf2idn(p, conf.hostname, sizeof(conf.hostname));
}
/* defaults */
conf.type = "ether";
conf.dev = nil;
action = Vadd;
/* get optional medium and device */
if (argc > 0){
verb = parseverb(*argv);
switch(verb){
case Vether:
case Vgbe:
case Vloopback:
case Vpkt:
case Vppp:
case Vtorus:
case Vtree:
conf.type = *argv++;
argc--;
if(argc > 0){
conf.dev = *argv++;
argc--;
} else if(verb == Vppp)
conf.dev = finddev("/dev", "eia", "/dev/eia0");
break;
}
}
if(conf.dev == nil)
conf.dev = finddev(conf.mpoint, "ether", "/net/ether0");
/* get optional verb */
if (argc > 0){
verb = parseverb(*argv);
switch(verb){
case Vether:
case Vgbe:
case Vppp:
case Vloopback:
case Vtorus:
case Vtree:
case Vpkt:
sysfatal("medium %s already specified", conf.type);
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;
}
static int
findifc(char *net, char *dev)
{
Ipifc *nifc;
ifc = readipifc(net, ifc, -1);
for(nifc = ifc; nifc != nil; nifc = nifc->next)
if(strcmp(nifc->dev, dev) == 0)
return nifc->index;
return -1;
}
int
isether(void)
{
switch(parseverb(conf.type)){
case Vether:
case Vgbe:
return 1;
}
return 0;
}
/* create a client id */
static void
mkclientid(void)
{
if(isether() && 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;
snprint((char*)conf.cid, sizeof conf.cid,
"plan9_%ld.%d", lrand(), getpid());
conf.cidlen = strlen((char*)conf.cid);
genrandom(conf.hwa, sizeof(conf.hwa));
}
ea2lla(conf.lladdr, conf.hwa);
}
void
main(int argc, char **argv)
{
int action;
Ctl *cp;
init();
ARGBEGIN {
case '6': /* IPv6 auto config */
ipv6auto = 1;
break;
case 'b':
conf.baud = EARGF(usage());
break;
case 'c':
cp = malloc(sizeof *cp);
if(cp == nil)
sysfatal("%r");
*ctll = cp;
ctll = &cp->next;
cp->next = nil;
cp->ctl = EARGF(usage());
break;
case 'd':
dodhcp = 1;
break;
case 'D':
debug = 1;
break;
case 'f':
dbfile = EARGF(usage());
break;
case 'g':
if (parseip(conf.gaddr, EARGF(usage())) == -1)
usage();
break;
case 'G':
plan9 = 0;
break;
case 'h':
if(utf2idn(EARGF(usage()), conf.hostname, sizeof(conf.hostname)) <= 0)
sysfatal("bad hostname");
sendhostname = 1;
break;
case 'm':
conf.mtu = atoi(EARGF(usage()));
break;
case 'n':
noconfig = 1;
break;
case 'N':
dondbconfig = 1;
break;
case 'o':
if(addoption(EARGF(usage())) < 0)
usage();
break;
case 'O':
Oflag = 1;
break;
case 'p':
beprimary = 1;
break;
case 'P':
beprimary = 0;
break;
case 'r':
rflag = 1;
break;
case 'u': /* IPv6: duplicate neighbour disc. off */
dupl_disc = 0;
break;
case 'x':
setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
break;
case 'X':
nodhcpwatch = 1;
break;
default:
usage();
} ARGEND;
argv0 = "ipconfig"; /* boot invokes us as tcp? */
action = parseargs(argc, argv);
if(beprimary == -1 && (ipv6auto || parseverb(conf.type) == Vloopback))
beprimary = 0;
myifc = findifc(conf.mpoint, conf.dev);
if(myifc < 0) {
switch(action){
default:
if(noconfig)
break;
/* bind new interface */
controldevice();
binddevice();
myifc = findifc(conf.mpoint, conf.dev);
case Vremove:
case Vunbind:
break;
}
if(myifc < 0)
sysfatal("interface not found for: %s", conf.dev);
} else if(!noconfig) {
/* open old interface */
binddevice();
}
switch(action){
case Vadd:
mkclientid();
if(dondbconfig){
dodhcp = 0;
beprimary = 0;
ndbconfig();
break;
}
doadd();
break;
case Vra6:
case Vaddpref6:
mkclientid();
doipv6(action);
break;
case Vremove:
doremove();
break;
case Vunbind:
dounbind();
break;
}
exits(nil);
}
static void
doadd(void)
{
if(!validip(conf.laddr)){
if(ipv6auto){
ipmove(conf.laddr, conf.lladdr);
dodhcp = 0;
} else
dodhcp = 1;
}
/* run dhcp if we need something */
if(dodhcp){
fprint(conf.rfd, "tag dhcp");
dhcpquery(!noconfig, Sselecting);
}
if(!validip(conf.laddr)){
if(rflag && dodhcp && !noconfig){
warning("couldn't determine ip address, retrying");
dhcpwatch(1);
return;
} else
sysfatal("no success with DHCP");
}
DEBUG("adding address %I %M on %s", conf.laddr, conf.mask, conf.dev);
if(noconfig)
return;
if(!isv4(conf.laddr)){
if(ip6cfg() < 0)
sysfatal("can't start IPv6 on %s, address %I", conf.dev, conf.laddr);
} else {
if(ip4cfg() < 0)
sysfatal("can't start IPv4 on %s, address %I", conf.dev, conf.laddr);
else if(dodhcp && conf.lease != Lforever)
dhcpwatch(0);
}
/* leave everything we've learned somewhere other procs can find it */
putndb();
refresh();
}
static void
doremove(void)
{
if(!validip(conf.laddr))
sysfatal("remove requires an address");
DEBUG("removing address %I %M on %s", conf.laddr, conf.mask, conf.dev);
if(conf.cfd < 0)
return;
if(fprint(conf.cfd, "remove %I %M", conf.laddr, conf.mask) < 0)
warning("can't remove %I %M: %r", conf.laddr, conf.mask);
}
static void
dounbind(void)
{
if(conf.cfd < 0)
return;
if(fprint(conf.cfd, "unbind") < 0)
warning("can't unbind %s: %r", conf.dev);
}
/* send some ctls to a device */
static void
controldevice(void)
{
char ctlfile[256];
int fd;
Ctl *cp;
if (firstctl == nil || !isether())
return;
snprint(ctlfile, sizeof ctlfile, "%s/clone", conf.dev);
fd = open(ctlfile, ORDWR);
if(fd < 0)
sysfatal("can't open %s", ctlfile);
for(cp = firstctl; cp != nil; cp = cp->next){
if(write(fd, cp->ctl, strlen(cp->ctl)) < 0)
sysfatal("ctl message %s: %r", cp->ctl);
seek(fd, 0, 0);
}
// close(fd); /* or does it need to be left hanging? */
}
/* bind an ip stack to a device, leave the control channel open */
static void
binddevice(void)
{
char buf[256];
if(myifc >= 0){
/* open the old interface */
snprint(buf, sizeof buf, "%s/ipifc/%d/ctl", conf.mpoint, myifc);
conf.cfd = open(buf, ORDWR);
if(conf.cfd < 0)
sysfatal("open %s: %r", buf);
} else if(strcmp(conf.type, "ppp") == 0)
pppbinddev();
else {
/* get a new ip interface */
snprint(buf, sizeof buf, "%s/ipifc/clone", conf.mpoint);
conf.cfd = open(buf, ORDWR);
if(conf.cfd < 0)
sysfatal("opening %s/ipifc/clone: %r", conf.mpoint);
/* specify medium as ethernet, bind the interface to it */
if(fprint(conf.cfd, "bind %s %s", conf.type, conf.dev) < 0)
sysfatal("%s: bind %s %s: %r", buf, conf.type, conf.dev);
}
snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
conf.rfd = open(buf, OWRITE);
}
/* add a logical interface to the ip stack */
int
ip4cfg(void)
{
char buf[256];
int n;
if(!validip(conf.laddr) || !isv4(conf.laddr))
return -1;
n = sprint(buf, "add");
n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
if(!validip(conf.mask))
ipmove(conf.mask, defmask(conf.laddr));
n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
if(validip(conf.raddr)){
n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
if(conf.mtu != 0)
n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
}
if(write(conf.cfd, buf, n) < 0){
warning("write(%s): %r", buf);
return -1;
}
if(validip(conf.gaddr) && isv4(conf.gaddr)
&& ipcmp(conf.gaddr, conf.laddr) != 0)
adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
return 0;
}
/* remove a logical interface from the ip stack */
void
ipunconfig(void)
{
if(!validip(conf.laddr))
return;
if(!validip(conf.mask))
ipmove(conf.mask, defmask(conf.laddr));
if(validip(conf.gaddr))
removedefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
doremove();
ipmove(conf.laddr, IPnoaddr);
ipmove(conf.raddr, IPnoaddr);
ipmove(conf.mask, IPnoaddr);
}
/* return true if this is not a null address */
int
validip(uchar *addr)
{
return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
}
/* put server ip addresses into the ndb entry */
static char*
putaddrs(char *p, char *e, char *attr, uchar *a, int len)
{
int i;
for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
p = seprint(p, e, "%s=%I\n", attr, a);
return p;
}
/* put space separated names into ndb entry */
static char*
putnames(char *p, char *e, char *attr, char *s)
{
char *x;
for(; *s != 0; s = x+1){
if((x = strchr(s, ' ')) != nil)
*x = 0;
p = seprint(p, e, "%s=%U\n", attr, s);
if(x == nil)
break;
*x = ' ';
}
return p;
}
/* make an ndb entry and put it into /net/ndb for the servers to see */
void
putndb(void)
{
static char buf[16*1024];
char file[64], *p, *e, *np;
Ndbtuple *t, *nt;
Ndb *db;
int fd;
if(beprimary == 0)
return;
p = buf;
e = buf + sizeof buf;
p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
conf.laddr, conf.mask, conf.gaddr);
if(np = strchr(conf.hostname, '.')){
if(*conf.domainname == 0)
strcpy(conf.domainname, np+1);
*np = 0;
}
if(*conf.hostname)
p = seprint(p, e, "\tsys=%U\n", conf.hostname);
if(*conf.domainname)
p = seprint(p, e, "\tdom=%U.%U\n",
conf.hostname, conf.domainname);
if(*conf.dnsdomain)
p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
if(validip(conf.dns))
p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
if(validip(conf.fs))
p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
if(validip(conf.auth))
p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
if(validip(conf.ntp))
p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
if(ndboptions)
p = seprint(p, e, "%s\n", ndboptions);
/* append preexisting entries not matching our ip */
snprint(file, sizeof file, "%s/ndb", conf.mpoint);
db = ndbopen(file);
if(db != nil ){
while((t = ndbparse(db)) != nil){
uchar ip[IPaddrlen];
if((nt = ndbfindattr(t, t, "ip")) == nil
|| parseip(ip, nt->val) == -1
|| ipcmp(ip, conf.laddr) != 0){
p = seprint(p, e, "\n");
for(nt = t; nt != nil; nt = nt->entry)
p = seprint(p, e, "%s=%s%s", nt->attr, nt->val,
nt->entry==nil? "\n": nt->line!=nt->entry? "\n\t": " ");
}
ndbfree(t);
}
ndbclose(db);
}
if((fd = open(file, OWRITE|OTRUNC)) < 0)
return;
write(fd, buf, p-buf);
close(fd);
}
static int
issrcspec(uchar *src, uchar *smask)
{
return isv4(src)? memcmp(smask+IPv4off, IPnoaddr+IPv4off, 4): ipcmp(smask, IPnoaddr);
}
static void
routectl(char *cmd, uchar *dst, uchar *mask, uchar *gate, uchar *ia, uchar *src, uchar *smask)
{
char *ctl;
if(issrcspec(src, smask))
ctl = "%s %I %M %I %I %I %M";
else
ctl = "%s %I %M %I %I";
DEBUG(ctl, cmd, dst, mask, gate, ia, src, smask);
if(conf.rfd < 0)
return;
fprint(conf.rfd, ctl, cmd, dst, mask, gate, ia, src, smask);
}
static void
defroutectl(char *cmd, uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
{
uchar dst[IPaddrlen], mask[IPaddrlen];
if(isv4(gaddr)){
parseipandmask(dst, mask, "0.0.0.0", "0.0.0.0");
if(src == nil)
src = dst;
if(smask == nil)
smask = mask;
} else {
parseipandmask(dst, mask, "2000::", "/3");
if(src == nil)
src = IPnoaddr;
if(smask == nil)
smask = IPnoaddr;
}
routectl(cmd, dst, mask, gaddr, ia, src, smask);
/* also add a source specific route */
if(ipcmp(src, IPnoaddr) != 0 && ipcmp(src, v4prefix) != 0)
routectl(cmd, dst, mask, gaddr, ia, src, IPallbits);
}
void
adddefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
{
defroutectl("add", gaddr, ia, src, smask);
}
void
removedefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
{
defroutectl("remove", gaddr, ia, src, smask);
}
void
refresh(void)
{
char file[64];
int fd;
snprint(file, sizeof file, "%s/cs", conf.mpoint);
if((fd = open(file, OWRITE)) >= 0){
write(fd, "refresh", 7);
close(fd);
}
snprint(file, sizeof file, "%s/dns", conf.mpoint);
if((fd = open(file, OWRITE)) >= 0){
write(fd, "refresh", 7);
close(fd);
}
}
void
catch(void*, char *msg)
{
if(strstr(msg, "alarm"))
noted(NCONT);
exits(msg);
}
/* return pseudo-random integer in range low...(hi-1) */
ulong
randint(ulong low, ulong hi)
{
if (hi < low)
return low;
return low + nrand(hi - low);
}
long
jitter(void) /* compute small pseudo-random delay in ms */
{
return randint(0, 10*1000);
}
int
countaddrs(uchar *a, int len)
{
int i;
for(i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen)
;
return i / IPaddrlen;
}
void
addaddrs(uchar *to, int nto, uchar *from, int nfrom)
{
int i, j;
for(i = 0; i < nfrom; i += IPaddrlen, from += IPaddrlen){
if(!validip(from))
continue;
for(j = 0; j < nto && validip(to+j); j += IPaddrlen){
if(ipcmp(to+j, from) == 0)
return;
}
if(j == nto)
return;
ipmove(to+j, from);
}
}
void
addnames(char *d, char *s, int len)
{
char *p, *e, *f;
int n;
for(;;s++){
if((e = strchr(s, ' ')) == nil)
e = strchr(s, 0);
n = e - s;
if(n == 0)
goto next;
for(p = d;;p++){
if((f = strchr(p, ' ')) == nil)
f = strchr(p, 0);
if(f - p == n && memcmp(s, p, n) == 0)
goto next;
p = f;
if(*p == 0)
break;
}
if(1 + n + p - d >= len)
break;
if(p > d)
*p++ = ' ';
p[n] = 0;
memmove(p, s, n);
next:
s = e;
if(*s == 0)
break;
}
}
int
pnames(uchar *d, int nd, char *s)
{
uchar *de = d + nd;
int l;
if(nd < 1)
return -1;
for(; *s != 0; s++){
for(l = 0; *s != 0 && *s != '.' && *s != ' '; l++)
s++;
d += l+1;
if(d >= de || l > 077)
return -1;
d[-l-1] = l;
memmove(d-l, s-l, l);
if(*s != '.')
*d++ = 0;
}
return d - (de - nd);
}
int
gnames(char *d, int nd, uchar *s, int ns)
{
char *de = d + nd;
uchar *se = s + ns;
uchar *c = nil;
int l, p = 0;
if(ns < 1 || nd < 1)
return -1;
while(s < se){
l = *s++;
if((l & 0300) == 0300){
if(++p > 100 || s >= se)
break;
l = (l & 077)<<8 | *s++;
if(c == nil)
c = s;
s = (se - ns) + l;
continue;
}
l &= 077;
if(l == 0){
if(d <= de - nd)
break;
d[-1] = ' ';
if(c != nil){
s = c;
c = nil;
p = 0;
}
continue;
}
if(s+l >= se || d+l >= de)
break;
memmove(d, s, l);
s += l;
d += l;
*d++ = '.';
}
if(p != 0 || s != se || d <= de - nd || d[-1] != ' ')
return -1;
*(--d) = 0;
return d - (de - nd);
}
static int
Ufmt(Fmt *f)
{
char d[256], *s;
s = va_arg(f->args, char*);
if(idn2utf(s, d, sizeof(d)) >= 0)
s = d;
fmtprint(f, "%s", s);
return 0;
}
static Ndbtuple*
uniquent(Ndbtuple *t)
{
Ndbtuple **l, *x;
l = &t->entry;
while((x = *l) != nil){
if(strcmp(t->attr, x->attr) != 0){
l = &x->entry;
continue;
}
*l = x->entry;
x->entry = nil;
ndbfree(x);
}
return t;
}
/* my ips from ndb, read by ndbconfig() below */
static uchar dbips[128*IPaddrlen];
static int
ipindb(uchar *ip)
{
uchar *a;
for(a = dbips; a < &dbips[sizeof(dbips)]; a += IPaddrlen){
if(!validip(a))
break;
if(ipcmp(ip, a) == 0)
return 1;
}
return 0;
}
/* read configuration (except laddr) for myip from ndb */
void
ndb2conf(Ndb *db, uchar *myip)
{
int nattr;
char *attrs[10], val[256];
uchar ip[IPaddrlen];
Ndbtuple *t, *nt;
ipmove(conf.mask, defmask(conf.laddr));
memset(conf.gaddr, 0, sizeof(conf.gaddr));
memset(conf.dns, 0, sizeof(conf.dns));
memset(conf.ntp, 0, sizeof(conf.ntp));
memset(conf.fs, 0, sizeof(conf.fs));
memset(conf.auth, 0, sizeof(conf.auth));
memset(conf.dnsdomain, 0, sizeof(conf.dnsdomain));
if(db == nil)
return;
nattr = 0;
attrs[nattr++] = "ipmask";
attrs[nattr++] = "@ipgw";
attrs[nattr++] = "@dns";
attrs[nattr++] = "@ntp";
attrs[nattr++] = "@fs";
attrs[nattr++] = "@auth";
attrs[nattr++] = "dnsdomain";
snprint(val, sizeof(val), "%I", myip);
t = ndbipinfo(db, "ip", val, attrs, nattr);
for(nt = t; nt != nil; nt = nt->entry) {
if(strcmp(nt->attr, "dnsdomain") == 0) {
if(utf2idn(nt->val, val, sizeof(val)) <= 0)
continue;
addnames(conf.dnsdomain, val, sizeof(conf.dnsdomain));
continue;
}
if(strcmp(nt->attr, "ipmask") == 0) {
nt = uniquent(nt);
if(parseipmask(conf.mask, nt->val, isv4(myip)) == -1)
goto Badip;
continue;
}
if(parseip(ip, nt->val) == -1) {
Badip:
fprint(2, "%s: bad %s address in ndb: %s\n", argv0, nt->attr, nt->val);
continue;
}
if(strcmp(nt->attr, "ipgw") == 0) {
/* ignore in case we are the gateway */
if(ipindb(ip))
continue;
ipmove(conf.gaddr, ip);
nt = uniquent(nt);
} else if(strcmp(nt->attr, "dns") == 0) {
addaddrs(conf.dns, sizeof(conf.dns), ip, IPaddrlen);
} else if(strcmp(nt->attr, "ntp") == 0) {
addaddrs(conf.ntp, sizeof(conf.ntp), ip, IPaddrlen);
} else if(strcmp(nt->attr, "fs") == 0) {
addaddrs(conf.fs, sizeof(conf.fs), ip, IPaddrlen);
} else if(strcmp(nt->attr, "auth") == 0) {
addaddrs(conf.auth, sizeof(conf.auth), ip, IPaddrlen);
}
}
ndbfree(t);
}
Ndb*
opendatabase(void)
{
static Ndb *db;
if(db != nil)
ndbclose(db);
db = ndbopen(dbfile);
return db;
}
/* add addresses for my ethernet address from ndb */
static void
ndbconfig(void)
{
char etheraddr[32], *attr;
Ndbtuple *t, *nt;
Ndb *db;
int n, i;
db = opendatabase();
if(db == nil)
sysfatal("can't open ndb: %r");
if(validip(conf.laddr)){
ndb2conf(db, conf.laddr);
doadd();
return;
}
memset(dbips, 0, sizeof(dbips));
if(conf.hwatype != 1)
sysfatal("can't read hardware address");
snprint(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
attr = "ip";
t = ndbipinfo(db, "ether", etheraddr, &attr, 1);
for(nt = t; nt != nil; nt = nt->entry) {
if(parseip(conf.laddr, nt->val) == -1){
fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
nt->attr, nt->val);
continue;
}
addaddrs(dbips, sizeof(dbips), conf.laddr, IPaddrlen);
}
ndbfree(t);
n = countaddrs(dbips, sizeof(dbips));
if(n == 0)
sysfatal("no ip addresses found in ndb");
/* add link local address first, if not already done */
if(!findllip(conf.lladdr, ifc)){
for(i = 0; i < n; i++){
ipmove(conf.laddr, dbips+i*IPaddrlen);
if(ISIPV6LINKLOCAL(conf.laddr)){
ipv6auto = 0;
ipmove(conf.lladdr, conf.laddr);
ndb2conf(db, conf.laddr);
doadd();
break;
}
}
if(ipv6auto){
ipmove(conf.laddr, IPnoaddr);
doadd();
}
}
/* add v4 addresses and v6 if link local address is available */
for(i = 0; i < n; i++){
ipmove(conf.laddr, dbips+i*IPaddrlen);
if(isv4(conf.laddr) || ipcmp(conf.laddr, conf.lladdr) != 0){
ndb2conf(db, conf.laddr);
doadd();
}
}
}