2016-11-25 17:18:40 +01:00
|
|
|
/*
|
|
|
|
* ipconfig - configure parameters of an ip stack
|
|
|
|
*/
|
|
|
|
#include <u.h>
|
2017-04-19 23:33:14 +02:00
|
|
|
#include <lib9.h>
|
2017-10-20 00:55:05 +02:00
|
|
|
#include <envvars.h>
|
2016-11-25 17:18:40 +01:00
|
|
|
#include <ip.h>
|
|
|
|
#include <bio.h>
|
|
|
|
#include <ndb.h>
|
|
|
|
#include "../dhcp.h"
|
|
|
|
#include "ipconfig.h"
|
|
|
|
|
|
|
|
#define DEBUG if(debug)warning
|
|
|
|
|
|
|
|
/* possible verbs */
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
/* commands */
|
|
|
|
Vadd,
|
|
|
|
Vremove,
|
|
|
|
Vunbind,
|
|
|
|
Vaddpref6,
|
|
|
|
Vra6,
|
|
|
|
/* media */
|
|
|
|
Vether,
|
|
|
|
Vgbe,
|
|
|
|
Vppp,
|
|
|
|
Vloopback,
|
|
|
|
Vtorus,
|
|
|
|
Vtree,
|
|
|
|
Vpkt,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
Taddr,
|
|
|
|
Taddrs,
|
|
|
|
Tstr,
|
|
|
|
Tbyte,
|
|
|
|
Tuint32_t,
|
|
|
|
Tvec,
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Option Option;
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
Option option[256] =
|
|
|
|
{
|
|
|
|
[OBmask] { "ipmask", Taddr },
|
|
|
|
[OBtimeoff] { "timeoff", Tuint32_t },
|
|
|
|
[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", Tuint32_t },
|
|
|
|
[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", Tuint32_t },
|
|
|
|
[OBttl] { "ttl", Tuint32_t },
|
|
|
|
[OBpathtimeout] { "pathtimeout", Taddrs },
|
|
|
|
[OBpathplateau] { "pathplateau", Taddrs },
|
|
|
|
[OBmtu] { "mtu", Tuint32_t },
|
|
|
|
[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", Tuint32_t },
|
|
|
|
[OBetherencap] { "etherencap", Taddrs },
|
|
|
|
[OBtcpttl] { "tcpttl", Tuint32_t },
|
|
|
|
[OBtcpka] { "tcpka", Tuint32_t },
|
|
|
|
[OBtcpkag] { "tcpkag", Tuint32_t },
|
|
|
|
[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", Tuint32_t },
|
|
|
|
[ODoverload] { "overload", Taddr },
|
|
|
|
[ODtype] { "type", Tbyte },
|
|
|
|
[ODserverid] { "serverid", Taddr },
|
|
|
|
[ODparams] { "params", Tvec },
|
|
|
|
[ODmessage] { "message", Tstr },
|
|
|
|
[ODmaxmsg] { "maxmsg", Tuint32_t },
|
|
|
|
[ODrenewaltime] { "renewaltime", Tuint32_t },
|
|
|
|
[ODrebindingtime] { "rebindingtime", Tuint32_t },
|
|
|
|
[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;
|
|
|
|
|
|
|
|
int Oflag;
|
|
|
|
int beprimary = -1;
|
|
|
|
Conf conf;
|
|
|
|
int debug;
|
|
|
|
int dodhcp;
|
|
|
|
int dolog;
|
|
|
|
int dondbconfig = 0;
|
|
|
|
int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
|
|
|
|
Ctl *firstctl, **ctll;
|
|
|
|
Ipifc *ifc;
|
|
|
|
int ipv6auto = 0;
|
|
|
|
int myifc = -1;
|
|
|
|
char *dbfile;
|
|
|
|
char *ndboptions;
|
|
|
|
int nip;
|
|
|
|
int noconfig;
|
|
|
|
int nodhcpwatch;
|
|
|
|
char optmagic[4] = { 0x63, 0x82, 0x53, 0x63 };
|
|
|
|
int plan9 = 1;
|
|
|
|
int sendhostname;
|
|
|
|
|
|
|
|
static char logfile[] = "ipconfig";
|
|
|
|
|
|
|
|
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",
|
|
|
|
};
|
|
|
|
|
|
|
|
void adddefroute(char*, uint8_t*);
|
|
|
|
int addoption(char*);
|
|
|
|
void binddevice(void);
|
|
|
|
void bootprequest(void);
|
|
|
|
void controldevice(void);
|
|
|
|
void dhcpquery(int, int);
|
|
|
|
void dhcprecv(void);
|
|
|
|
void dhcpsend(int);
|
|
|
|
void dhcptimer(void);
|
|
|
|
void dhcpwatch(int);
|
|
|
|
void doadd(int);
|
|
|
|
void doremove(void);
|
|
|
|
void dounbind(void);
|
|
|
|
void getoptions(uint8_t*);
|
|
|
|
int ip4cfg(void);
|
|
|
|
int ip6cfg(int a);
|
|
|
|
void lookforip(char*);
|
|
|
|
void mkclientid(void);
|
|
|
|
void ndbconfig(void);
|
|
|
|
int nipifcs(char*);
|
|
|
|
int openlisten(void);
|
|
|
|
uint8_t* optaddaddr(uint8_t*, int, uint8_t*);
|
|
|
|
uint8_t* optaddbyte(uint8_t*, int, int);
|
|
|
|
uint8_t* optaddstr(uint8_t*, int, char*);
|
|
|
|
uint8_t* optadd(uint8_t*, int, void*, int);
|
|
|
|
uint8_t* optadduint32_t(uint8_t*, int, uint32_t);
|
|
|
|
uint8_t* optaddvec(uint8_t*, int, uint8_t*, int);
|
|
|
|
int optgetaddrs(uint8_t*, int, uint8_t*, int);
|
|
|
|
int optgetp9addrs(uint8_t*, int, uint8_t*, int);
|
|
|
|
int optgetaddr(uint8_t*, int, uint8_t*);
|
|
|
|
int optgetbyte(uint8_t*, int);
|
|
|
|
int optgetstr(uint8_t*, int, char*, int);
|
|
|
|
uint8_t* optget(uint8_t*, int, int*);
|
|
|
|
uint32_t optgetuint32_t(uint8_t*, int);
|
|
|
|
int optgetvec(uint8_t*, int, uint8_t*, int);
|
|
|
|
char* optgetx(uint8_t*, uint8_t);
|
|
|
|
Bootp* parsebootp(uint8_t*, int);
|
|
|
|
int parseoptions(uint8_t *p, int n);
|
|
|
|
int parseverb(char*);
|
|
|
|
void pppbinddev(void);
|
|
|
|
void putndb(void);
|
|
|
|
void usage(void);
|
|
|
|
int validip(uint8_t*);
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
/*
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static 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)
|
|
|
|
sysfatal("bad address %s", argv[0]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse router advertisement (keyword, value) pairs */
|
|
|
|
static 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)
|
|
|
|
sysfatal("maxraint %d < minraint %d",
|
|
|
|
conf.maxraint, conf.minraint);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
init(void)
|
|
|
|
{
|
|
|
|
srand(truerand());
|
|
|
|
fmtinstall('E', eipfmt);
|
|
|
|
fmtinstall('I', eipfmt);
|
|
|
|
fmtinstall('M', eipfmt);
|
|
|
|
fmtinstall('V', eipfmt);
|
|
|
|
nsec(); /* make sure time file is open before forking */
|
|
|
|
|
|
|
|
setnetmtpt(conf.mpoint, sizeof conf.mpoint, nil);
|
2017-10-20 00:55:05 +02:00
|
|
|
conf.cputype = getenv(ENV_CPUTYPE);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(conf.cputype == nil)
|
|
|
|
conf.cputype = "386";
|
|
|
|
|
|
|
|
ctll = &firstctl;
|
|
|
|
v6paraminit(&conf);
|
|
|
|
|
|
|
|
/* init set of requested dhcp parameters with the default */
|
|
|
|
nrequested = sizeof defrequested;
|
|
|
|
memcpy(requested, defrequested, nrequested);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parseargs(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int action, verb;
|
|
|
|
|
|
|
|
/* default to any host name we already have */
|
|
|
|
if(*conf.hostname == 0){
|
2017-10-20 00:55:05 +02:00
|
|
|
p = getenv(ENV_SYSNAME);
|
2016-11-25 17:18:40 +01:00
|
|
|
if(p == nil || *p == 0)
|
|
|
|
p = sysname();
|
|
|
|
if(p != nil)
|
|
|
|
strncpy(conf.hostname, p, sizeof conf.hostname-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 Vppp:
|
|
|
|
case Vloopback:
|
|
|
|
case Vtorus:
|
|
|
|
case Vtree:
|
|
|
|
case Vpkt:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int retry, action;
|
|
|
|
Ctl *cp;
|
|
|
|
|
|
|
|
init();
|
|
|
|
retry = 0;
|
|
|
|
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':
|
|
|
|
snprint(conf.hostname, sizeof conf.hostname, "%s",
|
|
|
|
EARGF(usage()));
|
|
|
|
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':
|
|
|
|
retry = 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);
|
|
|
|
switch(action){
|
|
|
|
case Vadd:
|
|
|
|
doadd(retry);
|
|
|
|
break;
|
|
|
|
case Vremove:
|
|
|
|
doremove();
|
|
|
|
break;
|
|
|
|
case Vunbind:
|
|
|
|
dounbind();
|
|
|
|
break;
|
|
|
|
case Vaddpref6:
|
|
|
|
case Vra6:
|
|
|
|
doipv6(action);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
exits(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
doadd(int retry)
|
|
|
|
{
|
|
|
|
int ppp;
|
|
|
|
|
|
|
|
ppp = strcmp(conf.type, "ppp") == 0;
|
|
|
|
|
|
|
|
/* get number of preexisting interfaces */
|
|
|
|
nip = nipifcs(conf.mpoint);
|
|
|
|
if(beprimary == -1 && nip == 0)
|
|
|
|
beprimary = 1;
|
|
|
|
|
|
|
|
/* get ipifc into name space and condition device for ip */
|
|
|
|
if(!noconfig){
|
|
|
|
lookforip(conf.mpoint);
|
|
|
|
controldevice();
|
|
|
|
binddevice();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv6auto && !ppp) {
|
|
|
|
if (ip6cfg(ipv6auto) < 0)
|
|
|
|
sysfatal("can't automatically start IPv6 on %s",
|
|
|
|
conf.dev);
|
|
|
|
} else if (validip(conf.laddr) && !isv4(conf.laddr)) {
|
|
|
|
if (ip6cfg(0) < 0)
|
|
|
|
sysfatal("can't start IPv6 on %s, address %I",
|
|
|
|
conf.dev, conf.laddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!validip(conf.laddr) && !ppp)
|
|
|
|
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;
|
|
|
|
} else
|
|
|
|
sysfatal("no success with DHCP");
|
|
|
|
|
|
|
|
if(noconfig)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(ip4cfg() < 0)
|
|
|
|
sysfatal("can't start ip");
|
|
|
|
else if(dodhcp && conf.lease != Lforever)
|
|
|
|
dhcpwatch(0);
|
|
|
|
|
|
|
|
/* leave everything we've learned somewhere other procs can find it */
|
|
|
|
if(beprimary)
|
|
|
|
putndb();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
doremove(void)
|
|
|
|
{
|
|
|
|
char file[128];
|
|
|
|
int cfd;
|
|
|
|
Ipifc *nifc;
|
|
|
|
Iplifc *lifc;
|
|
|
|
|
|
|
|
if(!validip(conf.laddr))
|
|
|
|
sysfatal("remove requires an address");
|
|
|
|
ifc = readipifc(conf.mpoint, ifc, -1);
|
|
|
|
for(nifc = ifc; nifc != nil; nifc = nifc->next){
|
|
|
|
if(strcmp(nifc->dev, conf.dev) != 0)
|
|
|
|
continue;
|
|
|
|
for(lifc = nifc->lifc; lifc != nil; 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;
|
|
|
|
|
|
|
|
snprint(file, sizeof file, "%s/ipifc/%d/ctl",
|
|
|
|
conf.mpoint, nifc->index);
|
|
|
|
cfd = open(file, ORDWR);
|
|
|
|
if(cfd < 0){
|
|
|
|
warning("can't open %s: %r", conf.mpoint);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(fprint(cfd, "remove %I %M", lifc->ip, lifc->mask) < 0)
|
|
|
|
warning("can't remove %I %M from %s: %r",
|
|
|
|
lifc->ip, lifc->mask, file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dounbind(void)
|
|
|
|
{
|
|
|
|
Ipifc *nifc;
|
|
|
|
char file[128];
|
|
|
|
int cfd;
|
|
|
|
|
|
|
|
ifc = readipifc(conf.mpoint, ifc, -1);
|
|
|
|
for(nifc = ifc; nifc != nil; nifc = nifc->next){
|
|
|
|
if(strcmp(nifc->dev, conf.dev) == 0){
|
|
|
|
snprint(file, sizeof file, "%s/ipifc/%d/ctl",
|
|
|
|
conf.mpoint, nifc->index);
|
|
|
|
cfd = open(file, ORDWR);
|
|
|
|
if(cfd < 0){
|
|
|
|
warning("can't open %s: %r", conf.mpoint);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(fprint(cfd, "unbind") < 0)
|
|
|
|
warning("can't unbind from %s: %r", file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the default route */
|
|
|
|
void
|
|
|
|
adddefroute(char *mpoint, uint8_t *gaddr)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
int cfd;
|
|
|
|
|
|
|
|
sprint(buf, "%s/iproute", mpoint);
|
|
|
|
cfd = open(buf, ORDWR);
|
|
|
|
if(cfd < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(isv4(gaddr))
|
|
|
|
fprint(cfd, "add 0 0 %I", gaddr);
|
|
|
|
else
|
|
|
|
fprint(cfd, "add :: /0 %I", gaddr);
|
|
|
|
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;
|
|
|
|
snprint((char*)conf.cid, sizeof conf.cid,
|
|
|
|
"plan9_%ld.%d", lrand(), getpid());
|
|
|
|
conf.cidlen = strlen((char*)conf.cid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bind ip into the namespace */
|
|
|
|
void
|
|
|
|
lookforip(char *net)
|
|
|
|
{
|
|
|
|
char proto[64];
|
|
|
|
|
|
|
|
snprint(proto, sizeof proto, "%s/ipifc", net);
|
2016-12-01 00:09:42 +01:00
|
|
|
if(access(proto, AEXIST) == 0)
|
2016-11-25 17:18:40 +01:00
|
|
|
return;
|
|
|
|
sysfatal("no ip stack bound onto %s", net);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send some ctls to a device */
|
|
|
|
void
|
|
|
|
controldevice(void)
|
|
|
|
{
|
|
|
|
char ctlfile[256];
|
|
|
|
int fd;
|
|
|
|
Ctl *cp;
|
|
|
|
|
|
|
|
if (firstctl == nil ||
|
|
|
|
strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0)
|
|
|
|
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 */
|
|
|
|
void
|
|
|
|
binddevice(void)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
if(strcmp(conf.type, "ppp") == 0)
|
|
|
|
pppbinddev();
|
|
|
|
else if(myifc < 0){
|
|
|
|
/* 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);
|
|
|
|
} else {
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add a logical interface to the ip stack */
|
|
|
|
int
|
|
|
|
ip4cfg(void)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if(!validip(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, " %I", 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(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 %I", conf.laddr);
|
|
|
|
n = sprint(buf, "remove");
|
|
|
|
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, " %I", conf.mask);
|
|
|
|
|
|
|
|
write(conf.cfd, buf, n);
|
|
|
|
|
|
|
|
ipmove(conf.laddr, IPnoaddr);
|
|
|
|
ipmove(conf.raddr, IPnoaddr);
|
|
|
|
ipmove(conf.mask, IPnoaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ding(void* _, char *msg)
|
|
|
|
{
|
|
|
|
if(strstr(msg, "alarm"))
|
|
|
|
noted(NCONT);
|
|
|
|
noted(NDFLT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcpquery(int needconfig, int startstate)
|
|
|
|
{
|
|
|
|
if(needconfig)
|
|
|
|
fprint(conf.cfd, "add %I %I", IPnoaddr, IPnoaddr);
|
|
|
|
|
|
|
|
conf.fd = openlisten();
|
|
|
|
if(conf.fd < 0){
|
|
|
|
conf.state = Sinit;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
notify(ding);
|
|
|
|
|
|
|
|
conf.xid = lrand();
|
|
|
|
conf.starttime = time(0);
|
|
|
|
conf.state = startstate;
|
|
|
|
switch(startstate){
|
|
|
|
case Sselecting:
|
|
|
|
conf.offered = 0;
|
|
|
|
dhcpsend(Discover);
|
|
|
|
break;
|
|
|
|
case Srenewing:
|
|
|
|
dhcpsend(Request);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sysfatal("internal error 0");
|
|
|
|
}
|
|
|
|
conf.resend = 0;
|
|
|
|
conf.timeout = time(0) + 4;
|
|
|
|
|
|
|
|
while(conf.state != Sbound && conf.state != Sinit){
|
|
|
|
dhcprecv();
|
|
|
|
dhcptimer();
|
|
|
|
}
|
|
|
|
close(conf.fd);
|
|
|
|
|
|
|
|
if(needconfig)
|
|
|
|
fprint(conf.cfd, "remove %I %I", IPnoaddr, IPnoaddr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
|
|
|
/*
|
|
|
|
* was an hour, needs to be less for the ARM/GS1 until the timer
|
|
|
|
* code has been cleaned up (pb).
|
|
|
|
*/
|
|
|
|
Maxsleep = 450,
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcpwatch(int needconfig)
|
|
|
|
{
|
|
|
|
uint32_t secs, s, t;
|
|
|
|
|
|
|
|
if(nodhcpwatch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(rfork(RFPROC|RFFDG|RFNOWAIT|RFNOTEG)){
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dolog = 1; /* log, don't print */
|
|
|
|
procsetname("dhcpwatch");
|
|
|
|
/* keep trying to renew the lease */
|
|
|
|
for(;;){
|
|
|
|
secs = conf.lease/2;
|
|
|
|
if(secs < 5)
|
|
|
|
secs = 5;
|
|
|
|
|
|
|
|
/* avoid overflows */
|
|
|
|
for(s = secs; s > 0; s -= t){
|
|
|
|
if(s > Maxsleep)
|
|
|
|
t = Maxsleep;
|
|
|
|
else
|
|
|
|
t = s;
|
|
|
|
sleep(t*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)
|
|
|
|
sysfatal("can't start ip: %r");
|
|
|
|
needconfig = 0;
|
|
|
|
/*
|
|
|
|
* leave everything we've learned somewhere that
|
|
|
|
* other procs can find it.
|
|
|
|
*/
|
|
|
|
if(beprimary)
|
|
|
|
putndb();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcptimer(void)
|
|
|
|
{
|
|
|
|
uint32_t now;
|
|
|
|
|
|
|
|
now = time(0);
|
|
|
|
if(now < conf.timeout)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch(conf.state) {
|
|
|
|
default:
|
|
|
|
sysfatal("dhcptimer: unknown state %d", conf.state);
|
|
|
|
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;
|
|
|
|
break;
|
|
|
|
case Srenewing:
|
|
|
|
dhcpsend(Request);
|
|
|
|
conf.timeout = now + 1;
|
|
|
|
if(++conf.resend > 3) {
|
|
|
|
conf.state = Srebinding;
|
|
|
|
conf.resend = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcpsend(int type)
|
|
|
|
{
|
|
|
|
Bootp bp;
|
|
|
|
uint8_t *p;
|
|
|
|
int n;
|
|
|
|
uint8_t vendor[64];
|
|
|
|
Udphdr *up = (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:
|
|
|
|
sysfatal("dhcpsend: unknown message type: %d", type);
|
|
|
|
case Discover:
|
|
|
|
ipmove(up->raddr, IPv4bcast); /* broadcast */
|
|
|
|
if(*conf.hostname && sendhostname)
|
|
|
|
p = optaddstr(p, OBhostname, conf.hostname);
|
|
|
|
if(plan9){
|
|
|
|
n = snprint((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 = optadduint32_t(p, ODlease, conf.offered);
|
|
|
|
if(plan9){
|
|
|
|
n = snprint((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;
|
|
|
|
USED(n);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
dhcprecv(void)
|
|
|
|
{
|
|
|
|
int i, n, type;
|
|
|
|
uint32_t lease;
|
|
|
|
char err[ERRMAX];
|
|
|
|
uint8_t buf[8000], vopts[256], taddr[IPaddrlen];
|
|
|
|
Bootp *bp;
|
|
|
|
|
|
|
|
memset(buf, 0, sizeof buf);
|
|
|
|
alarm(1000);
|
|
|
|
n = read(conf.fd, buf, sizeof buf);
|
|
|
|
alarm(0);
|
|
|
|
|
|
|
|
if(n < 0){
|
|
|
|
rerrstr(err, sizeof err);
|
|
|
|
if(strstr(err, "interrupt") == nil)
|
|
|
|
warning("dhcprecv: bad read: %s", err);
|
|
|
|
else
|
|
|
|
DEBUG("dhcprecv: read timed out");
|
|
|
|
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 %V ", bp->siaddr);
|
|
|
|
if(conf.state != Sselecting){
|
|
|
|
// DEBUG("");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lease = optgetuint32_t(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=%I 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 %V ", bp->siaddr);
|
|
|
|
if (conf.state != Srequesting && conf.state != Srenewing &&
|
|
|
|
conf.state != Srebinding)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ignore a bad lease */
|
|
|
|
lease = optgetuint32_t(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);
|
|
|
|
if(ipcmp(conf.mask, IPv4bcast) == 0)
|
|
|
|
ipmove(conf.mask, IPnoaddr);
|
|
|
|
}
|
|
|
|
DEBUG("ipaddr=%I 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=%I ", conf.gaddr);
|
|
|
|
} else if(optgetaddr(bp->optdata, OBrouter, conf.gaddr)){
|
|
|
|
DEBUG("ipgw=%I ", conf.gaddr);
|
|
|
|
} else if(memcmp(bp->giaddr, IPnoaddr+IPv4off, IPv4addrlen)!=0){
|
|
|
|
v4tov6(conf.gaddr, bp->giaddr);
|
|
|
|
DEBUG("giaddr=%I ", 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=%I ", 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=%I ", 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){
|
|
|
|
if(validip(conf.fs) && Oflag)
|
|
|
|
n = 1;
|
|
|
|
else {
|
|
|
|
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=%I ", conf.fs + i*IPaddrlen);
|
|
|
|
|
|
|
|
if(validip(conf.auth) && Oflag)
|
|
|
|
n = 1;
|
|
|
|
else {
|
|
|
|
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=%I ", 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=%I new ipmask=%M new ipgw=%I",
|
|
|
|
conf.laddr, conf.mask, conf.gaddr);
|
|
|
|
}
|
|
|
|
conf.lease = lease;
|
|
|
|
conf.state = Sbound;
|
|
|
|
DEBUG("server=%I 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 + nrand(hi - low);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
jitter(void) /* compute small pseudo-random delay in ms */
|
|
|
|
{
|
|
|
|
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))
|
|
|
|
sprint(data, "%s/udp!%I!68", conf.mpoint, conf.laddr);
|
|
|
|
else
|
|
|
|
sprint(data, "%s/udp!*!68", conf.mpoint);
|
|
|
|
for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
|
|
|
|
if(!noconfig)
|
|
|
|
sysfatal("can't announce for dhcp: %r");
|
|
|
|
|
|
|
|
/* might be another client - wait and try again */
|
|
|
|
warning("can't announce %s: %r", data);
|
|
|
|
sleep(jitter());
|
|
|
|
if(n > 10)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fprint(cfd, "headers") < 0)
|
|
|
|
sysfatal("can't set header mode: %r");
|
|
|
|
|
|
|
|
sprint(data, "%s/data", devdir);
|
|
|
|
fd = open(data, ORDWR);
|
|
|
|
if(fd < 0)
|
|
|
|
sysfatal("open %s: %r", data);
|
|
|
|
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*
|
|
|
|
optadduint32_t(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 nil 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 != nil){
|
|
|
|
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 == nil)
|
|
|
|
return 0;
|
|
|
|
return *p;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
optgetuint32_t(uint8_t *p, int op)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = 4;
|
|
|
|
p = optget(p, op, &len);
|
|
|
|
if(p == nil)
|
|
|
|
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 == nil)
|
|
|
|
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 == nil)
|
|
|
|
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 == nil)
|
|
|
|
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)
|
|
|
|
fprint(2, "%s: bad address %s\n", argv0, p);
|
|
|
|
DEBUG("got plan 9 option %d addr %I (%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 == nil)
|
|
|
|
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 == nil)
|
|
|
|
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%ux: 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%ux: %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
|
|
|
|
*/
|
|
|
|
Bootp *
|
|
|
|
parsebootp(uint8_t *p, int n)
|
|
|
|
{
|
|
|
|
Bootp *bp;
|
|
|
|
|
|
|
|
bp = (Bootp*)p;
|
|
|
|
if(n < bp->optmagic - p) {
|
|
|
|
warning("parsebootp: short bootp packet");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(conf.xid != nhgetl(bp->xid)) /* not meant for us */
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if(bp->op != Bootreply) {
|
|
|
|
warning("parsebootp: bad op %d", bp->op);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
n -= bp->optmagic - p;
|
|
|
|
p = bp->optmagic;
|
|
|
|
|
|
|
|
if(n < 4) {
|
|
|
|
warning("parsebootp: no option data");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if(memcmp(optmagic, p, 4) != 0) {
|
|
|
|
warning("parsebootp: bad opt magic %ux %ux %ux %ux",
|
|
|
|
p[0], p[1], p[2], p[3]);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
p += 4;
|
|
|
|
n -= 4;
|
|
|
|
DEBUG("parsebootp: new packet");
|
|
|
|
if(parseoptions(p, n) < 0)
|
|
|
|
return nil;
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* put server addresses into the ndb entry */
|
|
|
|
char*
|
|
|
|
putaddrs(char *p, char *e, char *attr, uint8_t *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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
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=%s\n", conf.hostname);
|
|
|
|
if(*conf.domainname)
|
|
|
|
p = seprint(p, e, "\tdom=%s.%s\n",
|
|
|
|
conf.hostname, conf.domainname);
|
|
|
|
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.dns))
|
|
|
|
p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
|
|
|
|
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){
|
|
|
|
uint8_t ip[IPaddrlen];
|
|
|
|
|
|
|
|
if((nt = ndbfindattr(t, t, "ip")) == nil
|
|
|
|
|| parseip(ip, nt->val) < 0 || 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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return number of networks */
|
|
|
|
int
|
|
|
|
nipifcs(char *net)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
Ipifc *nifc;
|
|
|
|
Iplifc *lifc;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
ifc = readipifc(net, ifc, -1);
|
|
|
|
for(nifc = ifc; nifc != nil; 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 != nil; 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 < nelem(verbs); i++)
|
|
|
|
if(verbs[i] != nil && 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];
|
|
|
|
Ndb *db;
|
|
|
|
Ndbtuple *t, *nt;
|
|
|
|
|
|
|
|
db = ndbopen(dbfile);
|
|
|
|
if(db == nil)
|
|
|
|
sysfatal("can't open ndb: %r");
|
|
|
|
if (strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0 ||
|
|
|
|
myetheraddr(conf.hwa, conf.dev) != 0)
|
|
|
|
sysfatal("can't read hardware address");
|
|
|
|
sprint(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] = nil;
|
|
|
|
t = ndbipinfo(db, "ether", etheraddr, attrs, nattr);
|
|
|
|
for(nt = t; nt != nil; 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)
|
|
|
|
fprint(2, "%s: bad %s address in ndb: %s\n", argv0,
|
|
|
|
nt->attr, nt->val);
|
|
|
|
}
|
|
|
|
ndbfree(t);
|
|
|
|
if(!validip(conf.laddr))
|
|
|
|
sysfatal("address not found in ndb");
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
addoption(char *opt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Option *o;
|
|
|
|
|
|
|
|
if(opt == nil)
|
|
|
|
return -1;
|
|
|
|
for(o = option; o < &option[nelem(option)]; o++)
|
|
|
|
if(o->name && strcmp(opt, o->name) == 0){
|
|
|
|
i = o - option;
|
|
|
|
if(memchr(requested, i, nrequested) == 0 &&
|
|
|
|
nrequested < nelem(requested))
|
|
|
|
requested[nrequested++] = i;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
optgetx(uint8_t *p, uint8_t opt)
|
|
|
|
{
|
|
|
|
int i, n;
|
|
|
|
uint32_t x;
|
|
|
|
char *s, *ns;
|
|
|
|
char str[256];
|
|
|
|
uint8_t ip[IPaddrlen], ips[16*IPaddrlen], vec[256];
|
|
|
|
Option *o;
|
|
|
|
|
|
|
|
o = &option[opt];
|
|
|
|
if(o->name == nil)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
s = nil;
|
|
|
|
switch(o->type){
|
|
|
|
case Taddr:
|
|
|
|
if(optgetaddr(p, opt, ip))
|
|
|
|
s = smprint("%s=%I", o->name, ip);
|
|
|
|
break;
|
|
|
|
case Taddrs:
|
|
|
|
n = optgetaddrs(p, opt, ips, 16);
|
|
|
|
if(n > 0)
|
|
|
|
s = smprint("%s=%I", o->name, ips);
|
|
|
|
for(i = 1; i < n; i++){
|
|
|
|
ns = smprint("%s %s=%I", s, o->name, &ips[i*IPaddrlen]);
|
|
|
|
free(s);
|
|
|
|
s = ns;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Tuint32_t:
|
|
|
|
x = optgetuint32_t(p, opt);
|
|
|
|
if(x != 0)
|
|
|
|
s = smprint("%s=%lud", o->name, x);
|
|
|
|
break;
|
|
|
|
case Tbyte:
|
|
|
|
x = optgetbyte(p, opt);
|
|
|
|
if(x != 0)
|
|
|
|
s = smprint("%s=%lud", o->name, x);
|
|
|
|
break;
|
|
|
|
case Tstr:
|
|
|
|
if(optgetstr(p, opt, str, sizeof str))
|
|
|
|
s = smprint("%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 */
|
|
|
|
s = smprint("%s=%.*H", o->name, n, vec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
getoptions(uint8_t *p)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *s, *t;
|
|
|
|
|
|
|
|
for(i = nelem(defrequested); i < nrequested; i++){
|
|
|
|
s = optgetx(p, requested[i]);
|
|
|
|
if(s != nil)
|
|
|
|
DEBUG("%s ", s);
|
|
|
|
if(ndboptions == nil)
|
|
|
|
ndboptions = smprint("\t%s", s);
|
|
|
|
else{
|
|
|
|
t = ndboptions;
|
|
|
|
ndboptions = smprint("\t%s%s", s, ndboptions);
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
}
|