#include <u.h>#include <libc.h>#include <bio.h>#include <ip.h>#include <ndb.h>#include "/sys/src/9/port/arp.h"#include "bootp.h" int dbg;int bootreq;int bootctl;char *sysname;Ipinfo myiip;Ndb *db;char blog[] = "ipboot"; typedef struct Boottab Boottab;struct Boottab{ Ipinfo; uchar reply[4]; /* reply address */}; void openlisten(void);void fatal(int, char*, ...);void warning(int, char*, ...);void doreply(Bootp*, Boottab*);Boottab* lookup(Bootp*); voidmain(int argc, char **argv){ Bootp *boot; Boottab *f; char buf[1024]; uchar myip[4]; ARGBEGIN{ case 'd': dbg++; break; default: fprint(2, "usage: bootp [-d]\n"); exits("usage"); }ARGEND USED(argc); USED(argv); fmtinstall('E', eipconv); fmtinstall('I', eipconv); /* who am i? */ sysname = getenv("sysname"); if(sysname == 0) fatal(0, "I must know my name!"); if(myipaddr(myip, "/net/udp") < 0) fatal(0, "I must know my ip addr!"); /* get local info from the database */ db = ndbopen(0); if(db == 0) fatal(0, "can't open database\n"); sprint(buf, "%I", myip); if(ipinfo(db, 0, buf, 0, &myiip) < 0) fatal(0, "I'm not in network database!"); syslog(dbg, blog, "started"); switch(rfork(RFNOTEG|RFPROC|RFFDG)) { case -1: fatal(1, "fork"); case 0: break; default: exits(0); } openlisten(); for(;;) { read(bootreq, buf, sizeof(buf)); boot = (Bootp*)buf; if(boot->op != Bootrequest) continue; if(boot->htype != 1){ syslog(dbg, blog, "not ethernet %E\n", boot->chaddr); continue; } /* * Answer the request if the client didn't specify another * server and either we know the bootfile or the client * specified one. */ if(boot->sname[0]==0 || strcmp(boot->sname, sysname)==0){ f = lookup(boot); if(f && (f->bootf[0] || boot->file[0]!=0)){ doreply(boot, f); } else { if(f) syslog(dbg, blog, "no bootfile for %I %E", f->ipaddr, f->etheraddr); else syslog(dbg, blog, "no entry for %I %E", boot->ciaddr, boot->chaddr); } } }} /* * lookup info about a client in the database. The client may have supplied * an hardware address or an ip adress and a hardware address. */Boottab*lookup(Bootp *b){ static Boottab bt; char ether[32]; char ip[32]; memset(&bt, 0, sizeof(Boottab)); sprint(ether, "%E", b->chaddr); sprint(ip, "%I", b->ciaddr); if(ipinfo(db, ether , *ip ? ip : 0, 0, &bt) < 0) return 0; /* it knows who it it, reply directly */ memmove(bt.reply, bt.ipaddr, sizeof(bt.reply)); return &bt;} voidarpenter(uchar *ip, uchar *ether){ Arpentry a; int f; memmove(a.etaddr, ether, sizeof(a.etaddr)); memmove(a.ipaddr, ip, sizeof(a.ipaddr)); f = open("#a/arp/data", OWRITE); if(f < 0) { syslog(dbg, blog, "open arp: %r\n"); return; } if(write(f, &a, sizeof(a)) < 0) syslog(dbg, blog, "write arp: %r\n"); close(f);} voiddoreply(Bootp *boot, Boottab *tab){ Bootp reply; char buf[128]; int n; boot->sname[sizeof(boot->sname)-1] = '\0'; memmove(&reply, boot, sizeof(reply)); reply.op = Bootreply; /* the client always best know his ether address */ if(tab) arpenter(tab->ipaddr, boot->chaddr); else arpenter(boot->ciaddr, boot->chaddr); /* but we best know the ip addresses */ if(tab) memmove(reply.yiaddr, tab->ipaddr, sizeof(tab->ipaddr)); else memmove(reply.yiaddr, boot->ciaddr, sizeof(reply.yiaddr)); memmove(reply.siaddr, myiip.ipaddr, sizeof(myiip.ipaddr)); if(tab) memmove(reply.giaddr, tab->gwip, sizeof(reply.giaddr)); else memset(reply.giaddr, 0, sizeof(reply.giaddr)); strcpy(reply.sname, sysname); if(boot->file[0] != '\0') { if(boot->file[0] != '/') sprint(reply.file, "/lib/tftpd/%s", boot->file); } else if(tab) strcpy(reply.file, tab->bootf); else strcpy(reply.file, "no file"); if(tab && strncmp(reply.vend, "p9 ", 4) == 0) sprint(reply.vend, "p9 %I %I %I %I", tab->ipmask, tab->fsip, tab->auip, tab->gwip); syslog(dbg, blog, "reply to %I %E %s %s", reply.yiaddr, boot->chaddr, reply.file, reply.vend); /* * get a connection (sort of) back to the client */ if(tab) sprint(buf, "connect %I!68", tab->reply); else sprint(buf, "connect %I!68", reply.yiaddr); if(write(bootctl, buf, strlen(buf)) < 0){ warning(1, "can't %s", buf); return; } /* * send the reply */ n = write(bootreq, (void*)&reply, sizeof(reply)); if(n < 0) warning(1, "can't reply to %s", buf); /* * clear */ sprint(buf, "connect 0.0.0.0!0"); if(write(bootctl, buf, strlen(buf)) < 0){ warning(1, "can't %s", buf); return; }} voidopenlisten(void){ char data[128]; char devdir[40]; bootctl = announce("udp!*!bootp", devdir); if(bootctl < 0) fatal(1, "can't announce"); sprint(data, "%s/data", devdir); bootreq = open(data, ORDWR); if(bootreq < 0) fatal(1, "open udp data");} voidfatal(int syserr, char *fmt, ...){ char buf[ERRLEN], sysbuf[ERRLEN]; doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)); if(syserr) { errstr(sysbuf); fprint(2, "bootp: %s: %s\n", buf, sysbuf); } else fprint(2, "bootp: %s\n", buf); exits(buf);} voidwarning(int syserr, char *fmt, ...){ char buf[ERRLEN], sysbuf[ERRLEN]; doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)); if(syserr) { errstr(sysbuf); fprint(2, "bootp: %s: %s\n", buf, sysbuf); } else fprint(2, "bootp: %s\n", buf);}
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ip.h>
#include <ndb.h>
#include "/sys/src/9/port/arp.h"
#include "bootp.h"
int dbg;
int bootreq;
int bootctl;
char *sysname;
Ipinfo myiip;
Ndb *db;
char blog[] = "ipboot";
typedef struct Boottab Boottab;
struct Boottab
{
Ipinfo;
uchar reply[4]; /* reply address */
};
void openlisten(void);
void fatal(int, char*, ...);
void warning(int, char*, ...);
void doreply(Bootp*, Boottab*);
Boottab* lookup(Bootp*);
void
main(int argc, char **argv)
{
Bootp *boot;
Boottab *f;
char buf[1024];
uchar myip[4];
ARGBEGIN{
case 'd':
dbg++;
break;
default:
fprint(2, "usage: bootp [-d]\n");
exits("usage");
}ARGEND
USED(argc); USED(argv);
fmtinstall('E', eipconv);
fmtinstall('I', eipconv);
/* who am i? */
sysname = getenv("sysname");
if(sysname == 0)
fatal(0, "I must know my name!");
if(myipaddr(myip, "/net/udp") < 0)
fatal(0, "I must know my ip addr!");
/* get local info from the database */
db = ndbopen(0);
if(db == 0)
fatal(0, "can't open database\n");
sprint(buf, "%I", myip);
if(ipinfo(db, 0, buf, 0, &myiip) < 0)
fatal(0, "I'm not in network database!");
syslog(dbg, blog, "started");
switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
case -1:
fatal(1, "fork");
case 0:
break;
default:
exits(0);
}
openlisten();
for(;;) {
read(bootreq, buf, sizeof(buf));
boot = (Bootp*)buf;
if(boot->op != Bootrequest)
continue;
if(boot->htype != 1){
syslog(dbg, blog, "not ethernet %E\n", boot->chaddr);
continue;
}
/*
* Answer the request if the client didn't specify another
* server and either we know the bootfile or the client
* specified one.
*/
if(boot->sname[0]==0 || strcmp(boot->sname, sysname)==0){
f = lookup(boot);
if(f && (f->bootf[0] || boot->file[0]!=0)){
doreply(boot, f);
} else {
if(f)
syslog(dbg, blog, "no bootfile for %I %E",
f->ipaddr, f->etheraddr);
else
syslog(dbg, blog, "no entry for %I %E",
boot->ciaddr, boot->chaddr);
}
}
}
}
/*
* lookup info about a client in the database. The client may have supplied
* an hardware address or an ip adress and a hardware address.
*/
Boottab*
lookup(Bootp *b)
{
static Boottab bt;
char ether[32];
char ip[32];
memset(&bt, 0, sizeof(Boottab));
sprint(ether, "%E", b->chaddr);
sprint(ip, "%I", b->ciaddr);
if(ipinfo(db, ether , *ip ? ip : 0, 0, &bt) < 0)
return 0;
/* it knows who it it, reply directly */
memmove(bt.reply, bt.ipaddr, sizeof(bt.reply));
return &bt;
}
void
arpenter(uchar *ip, uchar *ether)
{
Arpentry a;
int f;
memmove(a.etaddr, ether, sizeof(a.etaddr));
memmove(a.ipaddr, ip, sizeof(a.ipaddr));
f = open("#a/arp/data", OWRITE);
if(f < 0) {
syslog(dbg, blog, "open arp: %r\n");
return;
}
if(write(f, &a, sizeof(a)) < 0)
syslog(dbg, blog, "write arp: %r\n");
close(f);
}
void
doreply(Bootp *boot, Boottab *tab)
{
Bootp reply;
char buf[128];
int n;
boot->sname[sizeof(boot->sname)-1] = '\0';
memmove(&reply, boot, sizeof(reply));
reply.op = Bootreply;
/* the client always best know his ether address */
if(tab)
arpenter(tab->ipaddr, boot->chaddr);
else
arpenter(boot->ciaddr, boot->chaddr);
/* but we best know the ip addresses */
if(tab)
memmove(reply.yiaddr, tab->ipaddr, sizeof(tab->ipaddr));
else
memmove(reply.yiaddr, boot->ciaddr, sizeof(reply.yiaddr));
memmove(reply.siaddr, myiip.ipaddr, sizeof(myiip.ipaddr));
if(tab)
memmove(reply.giaddr, tab->gwip, sizeof(reply.giaddr));
else
memset(reply.giaddr, 0, sizeof(reply.giaddr));
strcpy(reply.sname, sysname);
if(boot->file[0] != '\0') {
if(boot->file[0] != '/')
sprint(reply.file, "/lib/tftpd/%s", boot->file);
}
else if(tab)
strcpy(reply.file, tab->bootf);
else
strcpy(reply.file, "no file");
if(tab && strncmp(reply.vend, "p9 ", 4) == 0)
sprint(reply.vend, "p9 %I %I %I %I", tab->ipmask, tab->fsip,
tab->auip, tab->gwip);
syslog(dbg, blog, "reply to %I %E %s %s", reply.yiaddr, boot->chaddr,
reply.file, reply.vend);
/*
* get a connection (sort of) back to the client
*/
if(tab)
sprint(buf, "connect %I!68", tab->reply);
else
sprint(buf, "connect %I!68", reply.yiaddr);
if(write(bootctl, buf, strlen(buf)) < 0){
warning(1, "can't %s", buf);
return;
}
/*
* send the reply
*/
n = write(bootreq, (void*)&reply, sizeof(reply));
if(n < 0)
warning(1, "can't reply to %s", buf);
/*
* clear
*/
sprint(buf, "connect 0.0.0.0!0");
if(write(bootctl, buf, strlen(buf)) < 0){
warning(1, "can't %s", buf);
return;
}
}
void
openlisten(void)
{
char data[128];
char devdir[40];
bootctl = announce("udp!*!bootp", devdir);
if(bootctl < 0)
fatal(1, "can't announce");
sprint(data, "%s/data", devdir);
bootreq = open(data, ORDWR);
if(bootreq < 0)
fatal(1, "open udp data");
}
void
fatal(int syserr, char *fmt, ...)
{
char buf[ERRLEN], sysbuf[ERRLEN];
doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
if(syserr) {
errstr(sysbuf);
fprint(2, "bootp: %s: %s\n", buf, sysbuf);
}
else
fprint(2, "bootp: %s\n", buf);
exits(buf);
}
void
warning(int syserr, char *fmt, ...)
{
char buf[ERRLEN], sysbuf[ERRLEN];
doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
if(syserr) {
errstr(sysbuf);
fprint(2, "bootp: %s: %s\n", buf, sysbuf);
}
else
fprint(2, "bootp: %s\n", buf);
}