
the arp table is per interface, so it is possible to have the same netwrok on multiple physical interfaces, tho with different source ip address. one example would be a ethernet and a wlan interface. the mac addresses on these mediums can differ (arp proxying taking place). so provide our source address on the interface we received the request on. the previous change used the ifcaddr; which is correct; but due to a oversight in the kernel, had to match the ip of the arp entry. source address will always work.
1688 lines
36 KiB
C
1688 lines
36 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <ip.h>
|
|
#include <bio.h>
|
|
#include <ndb.h>
|
|
#include "dat.h"
|
|
|
|
/*
|
|
* ala rfc2131
|
|
*/
|
|
|
|
enum {
|
|
Maxloglen = 1024,
|
|
};
|
|
|
|
typedef struct Req Req;
|
|
struct Req
|
|
{
|
|
int fd; /* for reply */
|
|
Bootp *bp;
|
|
Udphdr *up;
|
|
Ipifc *ifc;
|
|
uchar *e; /* end of received message */
|
|
uchar *p; /* options pointer */
|
|
uchar *max; /* max end of reply */
|
|
|
|
/* expanded to v6 */
|
|
uchar ciaddr[IPaddrlen];
|
|
uchar giaddr[IPaddrlen];
|
|
|
|
/* parsed options */
|
|
int p9request; /* flag: this is a bootp with plan9 options */
|
|
int genrequest; /* flag: this is a bootp with generic options */
|
|
int dhcptype; /* dhcp message type */
|
|
int leasetime; /* dhcp lease */
|
|
uchar ip[IPaddrlen]; /* requested address */
|
|
uchar server[IPaddrlen]; /* server address */
|
|
char msg[ERRMAX]; /* error message */
|
|
char vci[32]; /* vendor class id */
|
|
char *id; /* client id */
|
|
uchar requested[32]; /* requested params */
|
|
uchar vendorclass[32];
|
|
char cputype[32-3];
|
|
|
|
Info gii; /* about target network */
|
|
Info ii; /* about target system */
|
|
int staticbinding;
|
|
|
|
uchar buf[2*1024]; /* message buffer */
|
|
};
|
|
|
|
#define TFTP "/lib/tftpd"
|
|
|
|
char *blog = "ipboot";
|
|
char *mysysname;
|
|
Ipifc *ipifcs;
|
|
int debug;
|
|
int nobootp;
|
|
long now;
|
|
int slowstat, slowdyn;
|
|
char net[256];
|
|
|
|
int pptponly; /* only answer request that came from the pptp server */
|
|
int mute, mutestat;
|
|
int minlease = MinLease;
|
|
int staticlease = StaticLease;
|
|
|
|
uvlong start;
|
|
|
|
static int v6opts;
|
|
|
|
/* option magic */
|
|
char plan9opt[4] = { 'p', '9', ' ', ' ' };
|
|
char genericopt[4] = { 0x63, 0x82, 0x53, 0x63 };
|
|
|
|
/* well known addresses */
|
|
uchar zeros[Maxhwlen];
|
|
|
|
/* option debug buffer */
|
|
char optbuf[1024];
|
|
char *op;
|
|
char *oe = optbuf + sizeof(optbuf);
|
|
|
|
char *optname[256] =
|
|
{
|
|
[OBend] "end",
|
|
[OBpad] "pad",
|
|
[OBmask] "mask",
|
|
[OBtimeoff] "timeoff",
|
|
[OBrouter] "router",
|
|
[OBtimeserver] "time",
|
|
[OBnameserver] "name",
|
|
[OBdnserver] "dns",
|
|
[OBlogserver] "log",
|
|
[OBcookieserver] "cookie",
|
|
[OBlprserver] "lpr",
|
|
[OBimpressserver] "impress",
|
|
[OBrlserver] "rl",
|
|
[OBhostname] "host",
|
|
[OBbflen] "bflen",
|
|
[OBdumpfile] "dumpfile",
|
|
[OBdomainname] "dom",
|
|
[OBswapserver] "swap",
|
|
[OBrootpath] "rootpath",
|
|
[OBextpath] "extpath",
|
|
[OBipforward] "ipforward",
|
|
[OBnonlocal] "nonlocal",
|
|
[OBpolicyfilter] "policyfilter",
|
|
[OBmaxdatagram] "maxdatagram",
|
|
[OBttl] "ttl",
|
|
[OBpathtimeout] "pathtimeout",
|
|
[OBpathplateau] "pathplateau",
|
|
[OBmtu] "mtu",
|
|
[OBsubnetslocal] "subnetslocal",
|
|
[OBbaddr] "baddr",
|
|
[OBdiscovermask] "discovermask",
|
|
[OBsupplymask] "supplymask",
|
|
[OBdiscoverrouter] "discoverrouter",
|
|
[OBrsserver] "rsserver",
|
|
[OBstaticroutes] "staticroutes",
|
|
[OBtrailerencap] "trailerencap",
|
|
[OBarptimeout] "arptimeout",
|
|
[OBetherencap] "etherencap",
|
|
[OBtcpttl] "tcpttl",
|
|
[OBtcpka] "tcpka",
|
|
[OBtcpkag] "tcpkag",
|
|
[OBnisdomain] "nisdomain",
|
|
[OBniserver] "niserver",
|
|
[OBntpserver] "ntpserver",
|
|
[OBvendorinfo] "vendorinfo",
|
|
[OBnetbiosns] "NBns",
|
|
[OBnetbiosdds] "NBdds",
|
|
[OBnetbiostype] "NBtype",
|
|
[OBnetbiosscope] "NBscope",
|
|
[OBxfontserver] "xfont",
|
|
[OBxdispmanager] "xdisp",
|
|
[OBnisplusdomain] "NPdomain",
|
|
[OBnisplusserver] "NP",
|
|
[OBhomeagent] "homeagent",
|
|
[OBsmtpserver] "smtp",
|
|
[OBpop3server] "pop3",
|
|
[OBnntpserver] "nntp",
|
|
[OBwwwserver] "www",
|
|
[OBfingerserver] "finger",
|
|
[OBircserver] "ircserver",
|
|
[OBstserver] "stserver",
|
|
[OBstdaserver] "stdaserver",
|
|
|
|
/* dhcp options */
|
|
[ODipaddr] "ip",
|
|
[ODlease] "leas",
|
|
[ODoverload] "overload",
|
|
[ODtype] "typ",
|
|
[ODserverid] "sid",
|
|
[ODparams] "params",
|
|
[ODmessage] "message",
|
|
[ODmaxmsg] "maxmsg",
|
|
[ODrenewaltime] "renewaltime",
|
|
[ODrebindingtime] "rebindingtime",
|
|
[ODvendorclass] "vendorclass",
|
|
[ODclientid] "cid",
|
|
[ODtftpserver] "tftpserver",
|
|
[ODbootfile] "bf",
|
|
};
|
|
|
|
void addropt(Req*, int, uchar*);
|
|
void addrsopt(Req*, int, uchar**, int);
|
|
void arpenter(uchar*, uchar*, uchar*);
|
|
void bootp(Req*);
|
|
void byteopt(Req*, int, uchar);
|
|
void dhcp(Req*);
|
|
void hexopt(Req*, int, char*);
|
|
void logdhcp(Req*);
|
|
void logdhcpout(Req *, char *);
|
|
void longopt(Req*, int, long);
|
|
void maskopt(Req*, int, uchar*);
|
|
void miscoptions(Req*, uchar*);
|
|
int openlisten(char *net);
|
|
void p9addrsopt(Req *rp, int t, uchar **ip, int i);
|
|
void parseoptions(Req*);
|
|
void proto(Req*, int);
|
|
void rcvdecline(Req*);
|
|
void rcvdiscover(Req*);
|
|
void rcvinform(Req*);
|
|
void rcvrelease(Req*);
|
|
void rcvrequest(Req*);
|
|
int readlast(int, uchar*, int);
|
|
char* readsysname(void);
|
|
void remrequested(Req*, int);
|
|
void sendack(Req*, uchar*, int, int);
|
|
void sendnak(Req*, uchar*, char*);
|
|
void sendoffer(Req*, uchar*, int);
|
|
void stringopt(Req*, int, char*);
|
|
void termopt(Req*);
|
|
int validip(uchar*);
|
|
void vectoropt(Req*, int, uchar*, int);
|
|
|
|
void
|
|
timestamp(char *tag)
|
|
{
|
|
uvlong t;
|
|
|
|
t = nsec()/1000;
|
|
syslog(0, blog, "%s %lludµs", tag, t - start);
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: dhcp [-dmnprsSZ] [-f directory] [-M minlease] "
|
|
"[-x netmtpt] [-Z staticlease] addr n [addr n] ...\n");
|
|
exits("usage");
|
|
}
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
int i, n, fd;
|
|
uchar ip[IPaddrlen];
|
|
Req r;
|
|
|
|
setnetmtpt(net, sizeof net, nil);
|
|
|
|
fmtinstall('E', eipfmt);
|
|
fmtinstall('I', eipfmt);
|
|
fmtinstall('V', eipfmt);
|
|
fmtinstall('M', eipfmt);
|
|
fmtinstall('H', encodefmt);
|
|
|
|
ARGBEGIN {
|
|
case '6':
|
|
v6opts = 1;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'f':
|
|
ndbfile = EARGF(usage());
|
|
break;
|
|
case 'm':
|
|
mute = 1;
|
|
break;
|
|
case 'M':
|
|
minlease = atoi(EARGF(usage()));
|
|
if(minlease <= 0)
|
|
minlease = MinLease;
|
|
break;
|
|
case 'n':
|
|
nobootp = 1;
|
|
break;
|
|
case 'p':
|
|
pptponly = 1;
|
|
break;
|
|
case 'r':
|
|
mutestat = 1;
|
|
break;
|
|
case 's':
|
|
slowstat = 1;
|
|
break;
|
|
case 'S':
|
|
slowdyn = 1;
|
|
break;
|
|
case 'x':
|
|
setnetmtpt(net, sizeof net, EARGF(usage()));
|
|
break;
|
|
case 'Z':
|
|
staticlease = atoi(EARGF(usage()));
|
|
if(staticlease <= 0)
|
|
staticlease = StaticLease;
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
} ARGEND;
|
|
|
|
while(argc > 1){
|
|
if(parseip(ip, argv[0]) == -1 || !validip(ip))
|
|
usage();
|
|
n = atoi(argv[1]);
|
|
if(n <= 0)
|
|
usage();
|
|
initbinding(ip, n);
|
|
argc -= 2;
|
|
argv += 2;
|
|
}
|
|
|
|
/* for debugging */
|
|
for(i = 0; i < 256; i++)
|
|
if(optname[i] == 0)
|
|
optname[i] = smprint("%d", i);
|
|
|
|
/* what is my name? */
|
|
mysysname = readsysname();
|
|
|
|
/* put process in background */
|
|
if(!debug)
|
|
switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
|
|
case -1:
|
|
fatal("fork: %r");
|
|
case 0:
|
|
break;
|
|
default:
|
|
exits(0);
|
|
}
|
|
|
|
if (chdir(TFTP) < 0)
|
|
warning("can't change directory to %s: %r", TFTP);
|
|
fd = openlisten(net);
|
|
|
|
for(;;){
|
|
memset(&r, 0, sizeof(r));
|
|
r.fd = fd;
|
|
n = readlast(r.fd, r.buf, sizeof(r.buf));
|
|
if(n < Udphdrsize)
|
|
fatal("error reading requests: %r");
|
|
start = nsec()/1000;
|
|
op = optbuf;
|
|
*op = 0;
|
|
proto(&r, n);
|
|
}
|
|
}
|
|
|
|
void
|
|
proto(Req *rp, int n)
|
|
{
|
|
now = time(0);
|
|
|
|
rp->e = rp->buf + n;
|
|
rp->bp = (Bootp*)rp->buf;
|
|
rp->up = (Udphdr*)rp->buf;
|
|
rp->max = rp->buf + Udphdrsize + MINSUPPORTED - IPUDPHDRSIZE;
|
|
rp->p = rp->bp->optdata;
|
|
v4tov6(rp->giaddr, rp->bp->giaddr);
|
|
v4tov6(rp->ciaddr, rp->bp->ciaddr);
|
|
|
|
if(pptponly && rp->bp->htype != 0)
|
|
return;
|
|
|
|
if(rp->bp->op != Bootrequest){
|
|
warning("not bootrequest");
|
|
return;
|
|
}
|
|
|
|
if(!isv4(rp->up->laddr))
|
|
return;
|
|
|
|
ipifcs = readipifc(net, ipifcs, -1);
|
|
if((rp->ifc = findifc(rp->up->ifcaddr)) == nil){
|
|
warning("no interface");
|
|
return;
|
|
}
|
|
if(validip(rp->giaddr)){
|
|
/* info about gateway */
|
|
if(lookupip(rp->giaddr, nil, nil, &rp->gii, 1) < 0){
|
|
warning("unknown gateway %I", rp->giaddr);
|
|
return;
|
|
}
|
|
rp->gii.ifc = nil;
|
|
} else {
|
|
/* no gateway, directly connected */
|
|
if(ipcmp(rp->up->laddr, IPv4bcast) != 0 && localonifc(rp->up->laddr, rp->ifc) == nil){
|
|
warning("wrong network %I->%I on %s",
|
|
rp->up->raddr, rp->up->laddr, rp->ifc->dev);
|
|
return;
|
|
}
|
|
memset(&rp->gii, 0, sizeof(rp->gii));
|
|
rp->gii.ifc = rp->ifc;
|
|
}
|
|
|
|
if(rp->e < (uchar*)rp->bp->sname){
|
|
warning("packet too short");
|
|
return;
|
|
}
|
|
if(rp->e >= rp->bp->optdata){
|
|
if(memcmp(rp->bp->optmagic, plan9opt, sizeof(rp->bp->optmagic)) == 0)
|
|
rp->p9request = 1;
|
|
if(memcmp(rp->bp->optmagic, genericopt, sizeof(rp->bp->optmagic)) == 0) {
|
|
rp->genrequest = 1;
|
|
parseoptions(rp);
|
|
}
|
|
}
|
|
rp->p = rp->bp->optdata;
|
|
|
|
/* If no id is specified, make one from the hardware address
|
|
* of the target. We assume all zeros is not a hardware address
|
|
* which could be a mistake.
|
|
*/
|
|
if(rp->id == nil){
|
|
static char hwaid[Maxstr];
|
|
|
|
if(rp->bp->hlen > Maxhwlen || memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){
|
|
warning("no chaddr");
|
|
return;
|
|
}
|
|
snprint(hwaid, sizeof(hwaid), "hwa%2.2ux_%.*lH", rp->bp->htype, rp->bp->hlen, rp->bp->chaddr);
|
|
rp->id = hwaid;
|
|
}
|
|
|
|
/* info about target system */
|
|
if(lookup(rp->bp, &rp->ii, &rp->gii) == 0)
|
|
if(rp->ii.indb && rp->ii.dhcpgroup[0] == 0)
|
|
rp->staticbinding = 1;
|
|
|
|
if(rp->dhcptype)
|
|
dhcp(rp);
|
|
else
|
|
bootp(rp);
|
|
timestamp("done");
|
|
}
|
|
|
|
static void
|
|
slowdelay(Req *rp)
|
|
{
|
|
if(slowstat && rp->staticbinding || slowdyn && !rp->staticbinding)
|
|
sleep(2000);
|
|
}
|
|
|
|
void
|
|
dhcp(Req *rp)
|
|
{
|
|
logdhcp(rp);
|
|
|
|
switch(rp->dhcptype){
|
|
case Discover:
|
|
slowdelay(rp);
|
|
rcvdiscover(rp);
|
|
break;
|
|
case Request:
|
|
rcvrequest(rp);
|
|
break;
|
|
case Decline:
|
|
rcvdecline(rp);
|
|
break;
|
|
case Release:
|
|
rcvrelease(rp);
|
|
break;
|
|
case Inform:
|
|
rcvinform(rp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
rcvdiscover(Req *rp)
|
|
{
|
|
Binding *b, *nb;
|
|
|
|
if(rp->staticbinding){
|
|
sendoffer(rp, rp->ii.ipaddr, (staticlease > minlease? staticlease: minlease));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* first look for an outstanding offer
|
|
*/
|
|
b = idtooffer(rp->id, &rp->gii);
|
|
|
|
/*
|
|
* rfc2131 says:
|
|
* If an address is available, the new address
|
|
* SHOULD be chosen as follows:
|
|
*
|
|
* o The client's current address as recorded in the client's current
|
|
* binding, ELSE
|
|
*
|
|
* o The client's previous address as recorded in the client's (now
|
|
* expired or released) binding, if that address is in the server's
|
|
* pool of available addresses and not already allocated, ELSE
|
|
*
|
|
* o The address requested in the 'Requested IP Address' option, if that
|
|
* address is valid and not already allocated, ELSE
|
|
*
|
|
* o A new address allocated from the server's pool of available
|
|
* addresses; the address is selected based on the subnet from which
|
|
* the message was received (if 'giaddr' is 0) or on the address of
|
|
* the relay agent that forwarded the message ('giaddr' when not 0).
|
|
*/
|
|
if(b == nil){
|
|
b = idtobinding(rp->id, &rp->gii, 1);
|
|
if(b && b->boundto && strcmp(b->boundto, rp->id) != 0)
|
|
if(validip(rp->ip) && samenet(rp->ip, &rp->gii)){
|
|
nb = iptobinding(rp->ip, 0);
|
|
if(nb && nb->lease < now)
|
|
b = nb;
|
|
}
|
|
}
|
|
if(b == nil){
|
|
warning("!Discover(%s via %I on %s): no binding %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
return;
|
|
}
|
|
mkoffer(b, rp->id, rp->leasetime);
|
|
sendoffer(rp, b->ip, b->offer);
|
|
}
|
|
|
|
void
|
|
rcvrequest(Req *rp)
|
|
{
|
|
Binding *b;
|
|
|
|
if(validip(rp->server)){
|
|
/* this is a reply to an offer - SELECTING */
|
|
|
|
/* check for hard assignment */
|
|
if(rp->staticbinding){
|
|
if(findifc(rp->server) != rp->ifc) {
|
|
warning("!Request(%s via %I on %s): for server %I not me",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->server);
|
|
} else
|
|
sendack(rp, rp->ii.ipaddr,
|
|
(staticlease > minlease? staticlease:
|
|
minlease), 1);
|
|
return;
|
|
}
|
|
|
|
b = idtooffer(rp->id, &rp->gii);
|
|
|
|
/* if we don't have an offer, nak */
|
|
if(b == nil){
|
|
warning("!Request(%s via %I on %s): no offer",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev);
|
|
if(findifc(rp->server) == rp->ifc)
|
|
sendnak(rp, rp->server, "no offer for you");
|
|
return;
|
|
}
|
|
|
|
/* if not for me, retract offer */
|
|
if(findifc(rp->server) != rp->ifc){
|
|
b->expoffer = 0;
|
|
warning("!Request(%s via %I on %s): for server %I not me",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->server);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* if the client is confused about what we offered, nak.
|
|
* client really shouldn't be specifying this when selecting
|
|
*/
|
|
if(validip(rp->ip) && ipcmp(rp->ip, b->ip) != 0){
|
|
warning("!Request(%s via %I on %s): requests %I, not %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip, b->ip);
|
|
sendnak(rp, rp->ip, "bad ip address option");
|
|
return;
|
|
}
|
|
if(commitbinding(b) < 0){
|
|
warning("!Request(%s via %I on %s): can't commit %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, b->ip);
|
|
sendnak(rp, b->ip, "can't commit binding");
|
|
return;
|
|
}
|
|
sendack(rp, b->ip, b->offer, 1);
|
|
} else if(validip(rp->ip)){
|
|
/*
|
|
* checking address/net - INIT-REBOOT
|
|
*
|
|
* This is a rebooting client that remembers its old
|
|
* address.
|
|
*/
|
|
/* check for hard assignment */
|
|
if(rp->staticbinding){
|
|
if(ipcmp(rp->ip, rp->ii.ipaddr) != 0){
|
|
warning("!Request(%s via %I on %s): %I not valid for %E",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip, rp->bp->chaddr);
|
|
sendnak(rp, rp->ip, "not valid");
|
|
} else
|
|
sendack(rp, rp->ip, (staticlease > minlease?
|
|
staticlease: minlease), 1);
|
|
return;
|
|
}
|
|
|
|
/* make sure the network makes sense */
|
|
if(!samenet(rp->ip, &rp->gii)){
|
|
warning("!Request(%s via %I on %s): bad forward of %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
return;
|
|
}
|
|
b = iptobinding(rp->ip, 0);
|
|
if(b == nil){
|
|
warning("!Request(%s via %I on %s): no binding for %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
return;
|
|
}
|
|
if(ipcmp(rp->ip, b->ip) != 0 || now > b->lease){
|
|
warning("!Request(%s via %I on %s): %I not valid",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
sendnak(rp, rp->ip, "not valid");
|
|
return;
|
|
}
|
|
b->offer = b->lease - now;
|
|
sendack(rp, b->ip, b->offer, 1);
|
|
} else if(validip(rp->ciaddr)){
|
|
/*
|
|
* checking address - RENEWING or REBINDING
|
|
*
|
|
* these states are indistinguishable in our action. The only
|
|
* difference is how close to lease expiration the client is.
|
|
* If it is really close, it broadcasts the request hoping that
|
|
* some server will answer.
|
|
*/
|
|
|
|
/* check for hard assignment */
|
|
if(rp->staticbinding){
|
|
if(ipcmp(rp->ciaddr, rp->ii.ipaddr) != 0){
|
|
warning("!Request(%s via %I on %s): %I not valid",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ciaddr);
|
|
sendnak(rp, rp->ciaddr, "not valid");
|
|
} else
|
|
sendack(rp, rp->ciaddr, (staticlease > minlease?
|
|
staticlease: minlease), 1);
|
|
return;
|
|
}
|
|
|
|
/* make sure the network makes sense */
|
|
if(!samenet(rp->ciaddr, &rp->gii)){
|
|
warning("!Request(%s via %I on %s): bad forward of %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ciaddr);
|
|
return;
|
|
}
|
|
b = iptobinding(rp->ciaddr, 0);
|
|
if(b == nil){
|
|
warning("!Request(%s via %I on %s): no binding for %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ciaddr);
|
|
return;
|
|
}
|
|
if(ipcmp(rp->ciaddr, b->ip) != 0){
|
|
warning("!Request(%s via %I on %s): %I not valid",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ciaddr);
|
|
sendnak(rp, rp->ciaddr, "invalid ip address");
|
|
return;
|
|
}
|
|
mkoffer(b, rp->id, rp->leasetime);
|
|
if(commitbinding(b) < 0){
|
|
warning("!Request(%s via %I on %s): can't commit %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, b->ip);
|
|
sendnak(rp, b->ip, "can't commit binding");
|
|
return;
|
|
}
|
|
sendack(rp, b->ip, b->offer, 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
rcvdecline(Req *rp)
|
|
{
|
|
Binding *b;
|
|
char buf[64];
|
|
|
|
if(rp->staticbinding)
|
|
return;
|
|
|
|
b = idtooffer(rp->id, &rp->gii);
|
|
if(b == nil){
|
|
warning("!Decline(%s via %I on %s): no binding",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev);
|
|
return;
|
|
}
|
|
|
|
/* mark ip address as in use */
|
|
snprint(buf, sizeof(buf), "declined by %s", rp->id);
|
|
mkoffer(b, buf, 0x7fffffff);
|
|
commitbinding(b);
|
|
}
|
|
|
|
void
|
|
rcvrelease(Req *rp)
|
|
{
|
|
Binding *b;
|
|
|
|
if(rp->staticbinding)
|
|
return;
|
|
|
|
b = idtobinding(rp->id, &rp->gii, 0);
|
|
if(b == nil){
|
|
warning("!Release(%s via %I on %s): no binding",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev);
|
|
return;
|
|
}
|
|
if(strcmp(rp->id, b->boundto) != 0){
|
|
warning("!Release(%s via %I on %s): invalid release of %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
return;
|
|
}
|
|
warning("Release(%s via %I on %s): releasing %I",
|
|
b->boundto, rp->gii.ipaddr, rp->ifc->dev, b->ip);
|
|
if(releasebinding(b, rp->id) < 0)
|
|
warning("release: couldn't release");
|
|
}
|
|
|
|
void
|
|
rcvinform(Req *rp)
|
|
{
|
|
Binding *b;
|
|
|
|
if(rp->staticbinding){
|
|
sendack(rp, rp->ii.ipaddr, 0, 0);
|
|
return;
|
|
}
|
|
|
|
b = iptobinding(rp->ciaddr, 0);
|
|
if(b == nil){
|
|
warning("!Inform(%s via %I on %s): no binding for %I",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev, rp->ip);
|
|
return;
|
|
}
|
|
sendack(rp, b->ip, 0, 0);
|
|
}
|
|
|
|
int
|
|
setsiaddr(uchar *siaddr, uchar *saddr, uchar *laddr)
|
|
{
|
|
if(ipcmp(saddr, IPnoaddr) != 0){
|
|
v6tov4(siaddr, saddr);
|
|
return 0;
|
|
} else {
|
|
v6tov4(siaddr, laddr);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int
|
|
ismuted(Req *rp)
|
|
{
|
|
return mute || (mutestat && rp->staticbinding);
|
|
}
|
|
|
|
void
|
|
sendoffer(Req *rp, uchar *ip, int offer)
|
|
{
|
|
int n;
|
|
ushort flags;
|
|
Bootp *bp;
|
|
Udphdr *up;
|
|
|
|
bp = rp->bp;
|
|
up = rp->up;
|
|
|
|
localip(up->laddr, ip, rp->ifc);
|
|
|
|
flags = nhgets(bp->flags);
|
|
if(validip(rp->giaddr)){
|
|
ipmove(up->raddr, rp->giaddr);
|
|
hnputs(up->rport, 67);
|
|
} else if(flags & Fbroadcast){
|
|
ipmove(up->raddr, IPv4bcast);
|
|
hnputs(up->rport, 68);
|
|
} else {
|
|
ipmove(up->raddr, ip);
|
|
if(bp->htype == 1)
|
|
arpenter(up->raddr, bp->chaddr, up->laddr);
|
|
hnputs(up->rport, 68);
|
|
}
|
|
|
|
/*
|
|
* fill in standard bootp part
|
|
*/
|
|
bp->op = Bootreply;
|
|
bp->hops = 0;
|
|
hnputs(bp->secs, 0);
|
|
memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
|
|
v6tov4(bp->giaddr, rp->giaddr);
|
|
v6tov4(bp->yiaddr, ip);
|
|
setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
|
|
strncpy(bp->sname, mysysname, sizeof(bp->sname));
|
|
strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
|
|
|
|
/*
|
|
* set options
|
|
*/
|
|
byteopt(rp, ODtype, Offer);
|
|
longopt(rp, ODlease, offer);
|
|
addropt(rp, ODserverid, up->laddr);
|
|
miscoptions(rp, ip);
|
|
termopt(rp);
|
|
|
|
logdhcpout(rp, "Offer");
|
|
|
|
/*
|
|
* send
|
|
*/
|
|
n = rp->p - rp->buf;
|
|
if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
|
|
warning("offer: write failed: %r");
|
|
}
|
|
|
|
void
|
|
sendack(Req *rp, uchar *ip, int offer, int sendlease)
|
|
{
|
|
int n;
|
|
ushort flags;
|
|
Bootp *bp;
|
|
Udphdr *up;
|
|
|
|
bp = rp->bp;
|
|
up = rp->up;
|
|
|
|
localip(up->laddr, ip, rp->ifc);
|
|
|
|
flags = nhgets(bp->flags);
|
|
if(validip(rp->giaddr)){
|
|
ipmove(up->raddr, rp->giaddr);
|
|
hnputs(up->rport, 67);
|
|
} else if(flags & Fbroadcast){
|
|
ipmove(up->raddr, IPv4bcast);
|
|
hnputs(up->rport, 68);
|
|
} else {
|
|
ipmove(up->raddr, ip);
|
|
if(bp->htype == 1)
|
|
arpenter(up->raddr, bp->chaddr, up->laddr);
|
|
hnputs(up->rport, 68);
|
|
}
|
|
|
|
/*
|
|
* fill in standard bootp part
|
|
*/
|
|
bp->op = Bootreply;
|
|
bp->hops = 0;
|
|
hnputs(bp->secs, 0);
|
|
v6tov4(bp->giaddr, rp->giaddr);
|
|
v6tov4(bp->yiaddr, ip);
|
|
setsiaddr(bp->siaddr, rp->ii.tftp, up->laddr);
|
|
strncpy(bp->sname, mysysname, sizeof(bp->sname));
|
|
strncpy(bp->file, rp->ii.bootf, sizeof(bp->file));
|
|
|
|
/*
|
|
* set options
|
|
*/
|
|
byteopt(rp, ODtype, Ack);
|
|
if(sendlease){
|
|
longopt(rp, ODlease, offer);
|
|
}
|
|
addropt(rp, ODserverid, up->laddr);
|
|
miscoptions(rp, ip);
|
|
termopt(rp);
|
|
|
|
logdhcpout(rp, "Ack");
|
|
|
|
/*
|
|
* send
|
|
*/
|
|
n = rp->p - rp->buf;
|
|
if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
|
|
warning("ack: write failed: %r");
|
|
}
|
|
|
|
void
|
|
sendnak(Req *rp, uchar *ip, char *msg)
|
|
{
|
|
int n;
|
|
Bootp *bp;
|
|
Udphdr *up;
|
|
|
|
bp = rp->bp;
|
|
up = rp->up;
|
|
|
|
localip(up->laddr, ip, rp->ifc);
|
|
|
|
/*
|
|
* set destination (always broadcast)
|
|
*/
|
|
if(validip(rp->giaddr)){
|
|
ipmove(up->raddr, rp->giaddr);
|
|
hnputs(up->rport, 67);
|
|
} else {
|
|
ipmove(up->raddr, IPv4bcast);
|
|
hnputs(up->rport, 68);
|
|
}
|
|
|
|
/*
|
|
* fill in standard bootp part
|
|
*/
|
|
bp->op = Bootreply;
|
|
bp->hops = 0;
|
|
hnputs(bp->secs, 0);
|
|
v6tov4(bp->giaddr, rp->giaddr);
|
|
memset(bp->ciaddr, 0, sizeof(bp->ciaddr));
|
|
memset(bp->yiaddr, 0, sizeof(bp->yiaddr));
|
|
memset(bp->siaddr, 0, sizeof(bp->siaddr));
|
|
|
|
/*
|
|
* set options
|
|
*/
|
|
byteopt(rp, ODtype, Nak);
|
|
addropt(rp, ODserverid, up->laddr);
|
|
if(msg)
|
|
stringopt(rp, ODmessage, msg);
|
|
if(strncmp(rp->id, "id", 2) == 0)
|
|
hexopt(rp, ODclientid, rp->id+2);
|
|
termopt(rp);
|
|
|
|
logdhcpout(rp, "Nak");
|
|
|
|
/*
|
|
* send nak
|
|
*/
|
|
n = rp->p - rp->buf;
|
|
if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
|
|
warning("nak: write failed: %r");
|
|
}
|
|
|
|
void
|
|
bootp(Req *rp)
|
|
{
|
|
int n;
|
|
Bootp *bp;
|
|
Udphdr *up;
|
|
ushort flags;
|
|
Info *iip;
|
|
|
|
warning("bootp %s %I->%I from %s via %I on %s, file %s",
|
|
rp->genrequest? "generic": (rp->p9request? "p9": ""),
|
|
rp->up->raddr, rp->up->laddr,
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev,
|
|
rp->bp->file);
|
|
|
|
if(nobootp)
|
|
return;
|
|
|
|
bp = rp->bp;
|
|
up = rp->up;
|
|
iip = &rp->ii;
|
|
|
|
if(rp->staticbinding == 0){
|
|
warning("bootp from unknown %s via %I on %s",
|
|
rp->id, rp->gii.ipaddr, rp->ifc->dev);
|
|
return;
|
|
}
|
|
|
|
/* ignore if not for us */
|
|
if(*bp->sname){
|
|
if(strcmp(bp->sname, mysysname) != 0){
|
|
bp->sname[20] = 0;
|
|
warning("bootp for server %s", bp->sname);
|
|
return;
|
|
}
|
|
} else
|
|
slowdelay(rp);
|
|
|
|
/* ignore file for sgi arcs command bootp(); */
|
|
if(strcmp(bp->file, ";") == 0)
|
|
*bp->file = 0;
|
|
|
|
/* ignore if we don't know what file to load */
|
|
if(*bp->file == 0){
|
|
if(rp->genrequest && *iip->bootf2) /* if not plan 9 & have alternate file... */
|
|
strncpy(bp->file, iip->bootf2, sizeof(bp->file));
|
|
else if(*iip->bootf)
|
|
strncpy(bp->file, iip->bootf, sizeof(bp->file));
|
|
else if(*bp->sname) /* if we were asked, respond no matter what */
|
|
bp->file[0] = '\0';
|
|
else {
|
|
warning("no bootfile for %I", iip->ipaddr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* ignore if the file is unreadable */
|
|
if((!rp->genrequest) && bp->file[0] && access(bp->file, 4) < 0){
|
|
warning("inaccessible bootfile1 %s", bp->file);
|
|
return;
|
|
}
|
|
|
|
bp->op = Bootreply;
|
|
v6tov4(bp->yiaddr, iip->ipaddr);
|
|
if(rp->p9request){
|
|
warning("p9bootp: %I", iip->ipaddr);
|
|
memmove(bp->optmagic, plan9opt, 4);
|
|
rp->p += sprint((char*)rp->p, "%V %I %I %I",
|
|
iip->ipmask+IPv4off, iip->fsip, iip->auip, iip->gwip);
|
|
sprint(optbuf, "%s", (char*)(bp->optmagic));
|
|
} else if(rp->genrequest){
|
|
warning("genericbootp: %I", iip->ipaddr);
|
|
memmove(bp->optmagic, genericopt, 4);
|
|
miscoptions(rp, iip->ipaddr);
|
|
termopt(rp);
|
|
} else if(iip->vendor[0] != 0) {
|
|
warning("bootp vendor field: %s", iip->vendor);
|
|
memset(rp->p, 0, 128-4);
|
|
rp->p += sprint((char*)bp->optmagic, "%s", iip->vendor);
|
|
} else {
|
|
memset(rp->p, 0, 128-4);
|
|
rp->p += 128-4;
|
|
}
|
|
|
|
localip(up->laddr, iip->ipaddr, iip->ifc);
|
|
|
|
flags = nhgets(bp->flags);
|
|
if(validip(rp->giaddr)){
|
|
ipmove(up->raddr, rp->giaddr);
|
|
hnputs(up->rport, 67);
|
|
} else if(flags & Fbroadcast){
|
|
ipmove(up->raddr, IPv4bcast);
|
|
hnputs(up->rport, 68);
|
|
} else {
|
|
v4tov6(up->raddr, bp->yiaddr);
|
|
if(bp->htype == 1)
|
|
arpenter(up->raddr, bp->chaddr, up->laddr);
|
|
hnputs(up->rport, 68);
|
|
}
|
|
|
|
/*
|
|
* our identity
|
|
*/
|
|
strncpy(bp->sname, mysysname, sizeof(bp->sname));
|
|
|
|
/*
|
|
* set tftp server
|
|
*/
|
|
setsiaddr(bp->siaddr, iip->tftp, up->laddr);
|
|
if(rp->genrequest && *iip->bootf2)
|
|
setsiaddr(bp->siaddr, iip->tftp2, up->laddr);
|
|
|
|
/*
|
|
* RFC 1048 says that we must pad vendor field with
|
|
* zeros until we have a 64 byte field.
|
|
*/
|
|
n = rp->p - rp->bp->optdata;
|
|
if(n < 64-4) {
|
|
memset(rp->p, 0, (64-4)-n);
|
|
rp->p += (64-4)-n;
|
|
}
|
|
|
|
/*
|
|
* send
|
|
*/
|
|
n = rp->p - rp->buf;
|
|
if(!ismuted(rp) && write(rp->fd, rp->buf, n) != n)
|
|
warning("bootp: write failed: %r");
|
|
|
|
warning("bootp via %I: file %s xid(%ux)flag(%ux)ci(%V)gi(%V)yi(%V)si(%V) %s",
|
|
up->raddr, bp->file, nhgetl(bp->xid), nhgets(bp->flags),
|
|
bp->ciaddr, bp->giaddr, bp->yiaddr, bp->siaddr,
|
|
optbuf);
|
|
}
|
|
|
|
void
|
|
parseoptions(Req *rp)
|
|
{
|
|
int n, c, code;
|
|
uchar *o, *p;
|
|
|
|
p = rp->p;
|
|
|
|
while(p < rp->e){
|
|
code = *p++;
|
|
if(code == 255)
|
|
break;
|
|
if(code == 0)
|
|
continue;
|
|
|
|
/* ignore anything that's too long */
|
|
n = *p++;
|
|
o = p;
|
|
p += n;
|
|
if(p > rp->e)
|
|
return;
|
|
|
|
switch(code){
|
|
case ODipaddr: /* requested ip address */
|
|
if(n == IPv4addrlen)
|
|
v4tov6(rp->ip, o);
|
|
break;
|
|
case ODlease: /* requested lease time */
|
|
rp->leasetime = nhgetl(o);
|
|
if(rp->leasetime > MaxLease || rp->leasetime < 0)
|
|
rp->leasetime = MaxLease;
|
|
break;
|
|
case ODtype:
|
|
c = *o;
|
|
if(c < 10 && c > 0)
|
|
rp->dhcptype = c;
|
|
break;
|
|
case ODserverid:
|
|
if(n == IPv4addrlen)
|
|
v4tov6(rp->server, o);
|
|
break;
|
|
case ODmessage:
|
|
if(n > sizeof rp->msg-1)
|
|
n = sizeof rp->msg-1;
|
|
memmove(rp->msg, o, n);
|
|
rp->msg[n] = 0;
|
|
break;
|
|
case ODmaxmsg:
|
|
c = nhgets(o);
|
|
c -= 28;
|
|
c += Udphdrsize;
|
|
if(c > 0)
|
|
rp->max = rp->buf + c;
|
|
break;
|
|
case ODclientid:
|
|
if(n <= 1)
|
|
break;
|
|
rp->id = toid(o, n);
|
|
break;
|
|
case ODparams:
|
|
if(n > sizeof(rp->requested))
|
|
n = sizeof(rp->requested);
|
|
memmove(rp->requested, o, n);
|
|
break;
|
|
case ODvendorclass:
|
|
if(n >= sizeof(rp->vendorclass))
|
|
n = sizeof(rp->vendorclass)-1;
|
|
memmove(rp->vendorclass, o, n);
|
|
rp->vendorclass[n] = 0;
|
|
if(strncmp((char*)rp->vendorclass, "p9-", 3) == 0)
|
|
strncpy(rp->cputype, (char*)rp->vendorclass+3, sizeof(rp->cputype));
|
|
break;
|
|
case OBend:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
remrequested(Req *rp, int opt)
|
|
{
|
|
uchar *p;
|
|
|
|
p = memchr(rp->requested, opt, sizeof(rp->requested));
|
|
if(p != nil)
|
|
*p = OBpad;
|
|
}
|
|
|
|
void
|
|
miscoptions(Req *rp, uchar *ip)
|
|
{
|
|
int i, j, na;
|
|
uchar *addrs[8];
|
|
uchar *op, *omax;
|
|
uchar x[nelem(addrs)*IPaddrlen], vopts[Maxoptlen];
|
|
char *p;
|
|
char *attr[100], **a;
|
|
Ndbtuple *t;
|
|
Iplifc *lifc;
|
|
|
|
for(i=0; i<nelem(addrs); i++)
|
|
addrs[i] = &x[i*IPaddrlen];
|
|
|
|
/* always supply these */
|
|
if(validip(rp->ii.ipmask))
|
|
maskopt(rp, OBmask, rp->ii.ipmask);
|
|
else if(validip(rp->gii.ipmask))
|
|
maskopt(rp, OBmask, rp->gii.ipmask);
|
|
else if((lifc = localonifc(ip, rp->ifc)) != nil)
|
|
maskopt(rp, OBmask, lifc->mask);
|
|
|
|
if(validip(rp->ii.gwip)){
|
|
remrequested(rp, OBrouter);
|
|
addropt(rp, OBrouter, rp->ii.gwip);
|
|
} else if(validip(rp->gii.gwip)){
|
|
remrequested(rp, OBrouter);
|
|
addropt(rp, OBrouter, rp->gii.gwip);
|
|
} else if(validip(rp->giaddr)){
|
|
remrequested(rp, OBrouter);
|
|
addropt(rp, OBrouter, rp->giaddr);
|
|
}
|
|
|
|
/*
|
|
* OBhostname for the HP4000M switches
|
|
* (this causes NT to log infinite errors - tough shit)
|
|
*/
|
|
if(*rp->ii.domain){
|
|
remrequested(rp, OBhostname);
|
|
stringopt(rp, OBhostname, rp->ii.domain);
|
|
}
|
|
if(*rp->ii.rootpath)
|
|
stringopt(rp, OBrootpath, rp->ii.rootpath);
|
|
|
|
/* figure out what we need to lookup */
|
|
na = 0;
|
|
a = attr;
|
|
if(*rp->ii.domain == 0)
|
|
a[na++] = "dom";
|
|
for(i = 0; i < sizeof(rp->requested); i++)
|
|
switch(rp->requested[i]){
|
|
case OBrouter:
|
|
a[na++] = "@ipgw";
|
|
break;
|
|
case OBdnserver:
|
|
a[na++] = "@dns";
|
|
break;
|
|
case OBnetbiosns:
|
|
a[na++] = "@wins";
|
|
break;
|
|
case OBsmtpserver:
|
|
a[na++] = "@smtp";
|
|
break;
|
|
case OBpop3server:
|
|
a[na++] = "@pop3";
|
|
break;
|
|
case OBwwwserver:
|
|
a[na++] = "@www";
|
|
break;
|
|
case OBntpserver:
|
|
a[na++] = "@ntp";
|
|
break;
|
|
case OBtimeserver:
|
|
a[na++] = "@time";
|
|
break;
|
|
}
|
|
if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
|
|
|| strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
|
|
a[na++] = "@fs";
|
|
a[na++] = "@auth";
|
|
}
|
|
t = lookupinfo(ip, a, na);
|
|
|
|
/* lookup anything we might be missing */
|
|
if(*rp->ii.domain == 0)
|
|
lookupname(rp->ii.domain, sizeof(rp->ii.domain), t);
|
|
|
|
/* add any requested ones that we know about */
|
|
for(i = 0; i < sizeof(rp->requested); i++)
|
|
switch(rp->requested[i]){
|
|
case OBrouter:
|
|
j = lookupserver("ipgw", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBrouter, addrs, j);
|
|
break;
|
|
case OBdnserver:
|
|
j = lookupserver("dns", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBdnserver, addrs, j);
|
|
break;
|
|
case OBhostname:
|
|
if(*rp->ii.domain)
|
|
stringopt(rp, OBhostname, rp->ii.domain);
|
|
break;
|
|
case OBdomainname:
|
|
p = strchr(rp->ii.domain, '.');
|
|
if(p != nil)
|
|
stringopt(rp, OBdomainname, p+1);
|
|
break;
|
|
case OBnetbiosns:
|
|
j = lookupserver("wins", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBnetbiosns, addrs, j);
|
|
break;
|
|
case OBnetbiostype:
|
|
/* p-node: peer to peer WINS queries */
|
|
byteopt(rp, OBnetbiostype, 0x2);
|
|
break;
|
|
case OBsmtpserver:
|
|
j = lookupserver("smtp", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBsmtpserver, addrs, j);
|
|
break;
|
|
case OBpop3server:
|
|
j = lookupserver("pop3", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBpop3server, addrs, j);
|
|
break;
|
|
case OBwwwserver:
|
|
j = lookupserver("www", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBwwwserver, addrs, j);
|
|
break;
|
|
case OBntpserver:
|
|
j = lookupserver("ntp", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBntpserver, addrs, j);
|
|
break;
|
|
case OBtimeserver:
|
|
j = lookupserver("time", addrs, nelem(addrs), t);
|
|
addrsopt(rp, OBtimeserver, addrs, j);
|
|
break;
|
|
case OBttl:
|
|
byteopt(rp, OBttl, 255);
|
|
break;
|
|
}
|
|
|
|
/* add plan9 specific options */
|
|
if(strncmp((char*)rp->vendorclass, "plan9_", 6) == 0
|
|
|| strncmp((char*)rp->vendorclass, "p9-", 3) == 0){
|
|
/* point to temporary area */
|
|
op = rp->p;
|
|
omax = rp->max;
|
|
/* stash encoded options in vopts */
|
|
rp->p = vopts;
|
|
rp->max = vopts + sizeof(vopts) - 1;
|
|
|
|
/* emit old v4 addresses first to make sure that they fit */
|
|
addrsopt(rp, OP9fsv4, addrs, lookupserver("fs", addrs, nelem(addrs), t));
|
|
addrsopt(rp, OP9authv4, addrs, lookupserver("auth", addrs, nelem(addrs), t));
|
|
|
|
p9addrsopt(rp, OP9fs, addrs, lookupserver("fs", addrs, nelem(addrs), t));
|
|
p9addrsopt(rp, OP9auth, addrs, lookupserver("auth", addrs, nelem(addrs), t));
|
|
p9addrsopt(rp, OP9ipaddr, addrs, lookupserver("ip", addrs, nelem(addrs), t));
|
|
p9addrsopt(rp, OP9ipmask, addrs, lookupserver("ipmask", addrs, nelem(addrs), t));
|
|
p9addrsopt(rp, OP9ipgw, addrs, lookupserver("ipgw", addrs, nelem(addrs), t));
|
|
|
|
/* point back to packet, encapsulate vopts into packet */
|
|
j = rp->p - vopts;
|
|
rp->p = op;
|
|
rp->max = omax;
|
|
vectoropt(rp, OBvendorinfo, vopts, j);
|
|
}
|
|
|
|
ndbfree(t);
|
|
}
|
|
|
|
int
|
|
openlisten(char *net)
|
|
{
|
|
int fd, cfd;
|
|
char data[128], devdir[40];
|
|
|
|
sprint(data, "%s/udp!*!bootp", net);
|
|
cfd = announce(data, devdir);
|
|
if(cfd < 0)
|
|
fatal("can't announce: %r");
|
|
if(fprint(cfd, "headers") < 0)
|
|
fatal("can't set header mode: %r");
|
|
|
|
sprint(data, "%s/data", devdir);
|
|
fd = open(data, ORDWR);
|
|
if(fd < 0)
|
|
fatal("open udp data: %r");
|
|
return fd;
|
|
}
|
|
|
|
void
|
|
fatal(char *fmt, ...)
|
|
{
|
|
char buf[Maxloglen];
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
va_end(arg);
|
|
syslog(1, blog, "%s", buf);
|
|
exits(buf);
|
|
}
|
|
|
|
void
|
|
warning(char *fmt, ...)
|
|
{
|
|
char buf[Maxloglen];
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
vseprint(buf, buf+sizeof(buf), fmt, arg);
|
|
va_end(arg);
|
|
syslog(0, blog, "%s", buf);
|
|
if(debug)
|
|
fprint(2, "%s\n", buf);
|
|
}
|
|
|
|
char*
|
|
readsysname(void)
|
|
{
|
|
static char name[128];
|
|
char *p;
|
|
int n, fd;
|
|
|
|
fd = open("/dev/sysname", OREAD);
|
|
if(fd >= 0){
|
|
n = read(fd, name, sizeof(name)-1);
|
|
close(fd);
|
|
if(n > 0){
|
|
name[n] = 0;
|
|
return name;
|
|
}
|
|
}
|
|
p = getenv("sysname");
|
|
if(p == nil || *p == 0)
|
|
return "unknown";
|
|
return p;
|
|
}
|
|
|
|
extern int
|
|
validip(uchar *ip)
|
|
{
|
|
if(ipcmp(ip, IPnoaddr) == 0)
|
|
return 0;
|
|
if(ipcmp(ip, v4prefix) == 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
longopt(Req *rp, int t, long v)
|
|
{
|
|
if(rp->p + 6 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = 4;
|
|
hnputl(rp->p, v);
|
|
rp->p += 4;
|
|
|
|
op = seprint(op, oe, "%s(%ld)", optname[t], v);
|
|
}
|
|
|
|
void
|
|
addropt(Req *rp, int t, uchar *ip)
|
|
{
|
|
if(rp->p + 6 > rp->max)
|
|
return;
|
|
if (!isv4(ip)) {
|
|
if (debug)
|
|
warning("not a v4 %s server: %I", optname[t], ip);
|
|
return;
|
|
}
|
|
*rp->p++ = t;
|
|
*rp->p++ = 4;
|
|
memmove(rp->p, ip+IPv4off, 4);
|
|
rp->p += 4;
|
|
|
|
op = seprint(op, oe, "%s(%I)", optname[t], ip);
|
|
}
|
|
|
|
void
|
|
maskopt(Req *rp, int t, uchar *ip)
|
|
{
|
|
if(rp->p + 6 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = 4;
|
|
memmove(rp->p, ip+IPv4off, 4);
|
|
rp->p += 4;
|
|
|
|
op = seprint(op, oe, "%s(%M)", optname[t], ip);
|
|
}
|
|
|
|
void
|
|
addrsopt(Req *rp, int t, uchar **ip, int i)
|
|
{
|
|
int v4s, n;
|
|
|
|
if(i <= 0)
|
|
return;
|
|
if(rp->p + 2 + 4*i > rp->max)
|
|
return;
|
|
v4s = 0;
|
|
for(n = i; n-- > 0; )
|
|
if (isv4(ip[n]))
|
|
v4s++;
|
|
if (v4s <= 0) {
|
|
if (debug)
|
|
warning("no v4 %s servers", optname[t]);
|
|
return;
|
|
}
|
|
*rp->p++ = t;
|
|
*rp->p++ = 4*v4s;
|
|
op = seprint(op, oe, " %s(", optname[t]);
|
|
while(i-- > 0){
|
|
if (!isv4(*ip)) {
|
|
op = seprint(op, oe, " skipping %I ", *ip);
|
|
continue;
|
|
}
|
|
v6tov4(rp->p, *ip);
|
|
rp->p += 4;
|
|
op = seprint(op, oe, "%I", *ip);
|
|
ip++;
|
|
if(i > 0)
|
|
op = seprint(op, oe, " ");
|
|
}
|
|
op = seprint(op, oe, ")");
|
|
}
|
|
|
|
void
|
|
p9addrsopt(Req *rp, int t, uchar **ip, int i)
|
|
{
|
|
char *pkt, *payload;
|
|
|
|
if(i <= 0 || !v6opts)
|
|
return;
|
|
pkt = (char *)rp->p;
|
|
*pkt++ = t; /* option */
|
|
pkt++; /* fill in payload length below */
|
|
payload = pkt;
|
|
*pkt++ = i; /* plan 9 address count */
|
|
op = seprint(op, oe, " %s(", optname[t]);
|
|
while(i-- > 0){
|
|
pkt = seprint(pkt, (char *)rp->max, "%I", *ip);
|
|
if ((uchar *)pkt+1 >= rp->max) {
|
|
op = seprint(op, oe, "<out of mem1>)");
|
|
return;
|
|
}
|
|
pkt++; /* leave NUL as terminator */
|
|
op = seprint(op, oe, "%I", *ip);
|
|
ip++;
|
|
if(i > 0)
|
|
op = seprint(op, oe, " ");
|
|
}
|
|
if ((uchar *)pkt - rp->p > 0377) {
|
|
op = seprint(op, oe, "<out of mem2>)");
|
|
return;
|
|
}
|
|
op = seprint(op, oe, ")");
|
|
rp->p[1] = pkt - payload; /* payload length */
|
|
rp->p = (uchar *)pkt;
|
|
}
|
|
|
|
void
|
|
byteopt(Req *rp, int t, uchar v)
|
|
{
|
|
if(rp->p + 3 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = 1;
|
|
*rp->p++ = v;
|
|
|
|
op = seprint(op, oe, "%s(%d)", optname[t], v);
|
|
}
|
|
|
|
void
|
|
termopt(Req *rp)
|
|
{
|
|
if(rp->p + 1 > rp->max)
|
|
return;
|
|
*rp->p++ = OBend;
|
|
}
|
|
|
|
void
|
|
stringopt(Req *rp, int t, char *str)
|
|
{
|
|
int n;
|
|
|
|
n = strlen(str);
|
|
if(n > 255)
|
|
n = 255;
|
|
if(rp->p+n+2 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = n;
|
|
memmove(rp->p, str, n);
|
|
rp->p += n;
|
|
|
|
op = seprint(op, oe, "%s(%s)", optname[t], str);
|
|
}
|
|
|
|
void
|
|
vectoropt(Req *rp, int t, uchar *v, int n)
|
|
{
|
|
int i;
|
|
|
|
if(n > 255) {
|
|
n = 255;
|
|
op = seprint(op, oe, "vectoropt len %d > 255 ", n);
|
|
}
|
|
if(rp->p+n+2 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = n;
|
|
memmove(rp->p, v, n);
|
|
rp->p += n;
|
|
|
|
op = seprint(op, oe, "%s(", optname[t]);
|
|
if(n > 0)
|
|
op = seprint(op, oe, "%ud", v[0]);
|
|
for(i = 1; i < n; i++)
|
|
op = seprint(op, oe, " %ud", v[i]);
|
|
op = seprint(op, oe, ")");
|
|
}
|
|
|
|
int
|
|
fromhex(int x)
|
|
{
|
|
if(x >= '0' && x <= '9')
|
|
return x - '0';
|
|
return x - 'a';
|
|
}
|
|
|
|
void
|
|
hexopt(Req *rp, int t, char *str)
|
|
{
|
|
int n;
|
|
|
|
n = strlen(str);
|
|
n /= 2;
|
|
if(n > 255)
|
|
n = 255;
|
|
if(rp->p+n+2 > rp->max)
|
|
return;
|
|
*rp->p++ = t;
|
|
*rp->p++ = n;
|
|
while(n-- > 0){
|
|
*rp->p++ = (fromhex(str[0])<<4)|fromhex(str[1]);
|
|
str += 2;
|
|
}
|
|
|
|
op = seprint(op, oe, "%s(%s)", optname[t], str);
|
|
}
|
|
|
|
void
|
|
arpenter(uchar *ip, uchar *mac, uchar *src)
|
|
{
|
|
char buf[256];
|
|
int fd, n;
|
|
|
|
snprint(buf, sizeof buf, "%s/arp", net);
|
|
if((fd = open(buf, OWRITE)) < 0){
|
|
warning("couldn't open %s: %r", buf);
|
|
return;
|
|
}
|
|
n = snprint(buf, sizeof buf, "add ether %I %E %I\n", ip, mac, src);
|
|
if(write(fd, buf, n) != n)
|
|
warning("arpenter: %s: %r", buf);
|
|
close(fd);
|
|
}
|
|
|
|
char *dhcpmsgname[] =
|
|
{
|
|
[Discover] "Discover",
|
|
[Offer] "Offer",
|
|
[Request] "Request",
|
|
[Decline] "Decline",
|
|
[Ack] "Ack",
|
|
[Nak] "Nak",
|
|
[Release] "Release",
|
|
[Inform] "Inform",
|
|
};
|
|
|
|
void
|
|
logdhcp(Req *rp)
|
|
{
|
|
char buf[4096];
|
|
char *p, *e;
|
|
int i;
|
|
|
|
p = buf;
|
|
e = buf + sizeof(buf);
|
|
if(rp->dhcptype > 0 && rp->dhcptype <= Inform)
|
|
p = seprint(p, e, "%s(", dhcpmsgname[rp->dhcptype]);
|
|
else
|
|
p = seprint(p, e, "%d(", rp->dhcptype);
|
|
p = seprint(p, e, "%I->%I) xid(%ux)flag(%ux)", rp->up->raddr, rp->up->laddr,
|
|
nhgetl(rp->bp->xid), nhgets(rp->bp->flags));
|
|
if(rp->bp->htype == 1)
|
|
p = seprint(p, e, "ea(%E)", rp->bp->chaddr);
|
|
if(validip(rp->ciaddr))
|
|
p = seprint(p, e, "ci(%I)", rp->ciaddr);
|
|
if(validip(rp->giaddr))
|
|
p = seprint(p, e, "gi(%I)", rp->giaddr);
|
|
if(validip(rp->ip))
|
|
p = seprint(p, e, "ip(%I)", rp->ip);
|
|
if(rp->id != nil)
|
|
p = seprint(p, e, "id(%s)", rp->id);
|
|
if(rp->leasetime)
|
|
p = seprint(p, e, "leas(%d)", rp->leasetime);
|
|
if(validip(rp->server))
|
|
p = seprint(p, e, "sid(%I)", rp->server);
|
|
p = seprint(p, e, "need(");
|
|
for(i = 0; i < sizeof(rp->requested); i++)
|
|
if(rp->requested[i] != 0)
|
|
p = seprint(p, e, "%s ", optname[rp->requested[i]]);
|
|
p = seprint(p, e, ")");
|
|
|
|
USED(p);
|
|
syslog(0, blog, "%s", buf);
|
|
}
|
|
|
|
void
|
|
logdhcpout(Req *rp, char *type)
|
|
{
|
|
syslog(0, blog, "%s(%I->%I)id(%s)ci(%V)gi(%V)yi(%V)si(%V) %s",
|
|
type, rp->up->laddr, rp->up->raddr, rp->id,
|
|
rp->bp->ciaddr, rp->bp->giaddr, rp->bp->yiaddr, rp->bp->siaddr, optbuf);
|
|
}
|
|
|
|
/*
|
|
* if we get behind, it's useless to try answering since the sender
|
|
* will probably have retransmitted with a differnt sequence number.
|
|
* So dump all but the last message in the queue.
|
|
*/
|
|
void
|
|
ding(void*, char *msg)
|
|
{
|
|
if(strstr(msg, "alarm"))
|
|
noted(NCONT);
|
|
else
|
|
noted(NDFLT);
|
|
}
|
|
|
|
int
|
|
readlast(int fd, uchar *buf, int len)
|
|
{
|
|
int lastn, n;
|
|
|
|
notify(ding);
|
|
|
|
lastn = 0;
|
|
for(;;){
|
|
alarm(20);
|
|
n = read(fd, buf, len);
|
|
alarm(0);
|
|
if(n < 0){
|
|
if(lastn > 0)
|
|
return lastn;
|
|
break;
|
|
}
|
|
lastn = n;
|
|
}
|
|
return read(fd, buf, len);
|
|
}
|