📄 dhcpd.c
字号:
#include <u.h>#include <libc.h>#include <ip.h>#include <bio.h>#include <ndb.h>#include "dat.h"//// ala rfc2131//typedef struct Req Req;struct Req{ int fd; /* for reply */ Bootp *bp; OUdphdr *up; 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; /* true if this is a bootp with plan9 options */ int genrequest; /* true if 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[64];Ipifc *ipifcs;int debug;int nobootp;long now;int slowstat, slowdyn;char net[256];int pptponly; // only answer request that came from the pptp serverint mute, mutestat;int minlease = MinLease;ulong start;/* 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*);void bootp(Req*);void byteopt(Req*, int, uchar);void dhcp(Req*);void fatal(int, char*, ...);void hexopt(Req*, int, char*);void longopt(Req*, int, long);void maskopt(Req*, int, uchar*);void miscoptions(Req*, uchar*);int openlisten(char *net);void parseoptions(Req*);void proto(Req*, int);void rcvdecline(Req*);void rcvdiscover(Req*);void rcvinform(Req*);void rcvrelease(Req*);void rcvrequest(Req*);char* readsysname(void);void remrequested(Req*, int);void sendack(Req*, uchar*, int, int);void sendnak(Req*, char*);void sendoffer(Req*, uchar*, int);void stringopt(Req*, int, char*);void termopt(Req*);int validip(uchar*);void vectoropt(Req*, int, uchar*, int);void warning(int, char*, ...);void logdhcp(Req*);void logdhcpout(Req *, char *);int readlast(int, uchar*, int);voidtimestamp(char *tag){ ulong t; t = nsec()/1000; syslog(0, blog, "%s %lud", tag, t - start);}voidusage(void){ fprint(2, "usage: dhcp [-dmsnp] [-f directory] [-x netmtpt] [-M minlease] addr n [addr n ...]\n"); exits("usage");}voidmain(int argc, char **argv){ int i, n, fd; char *p; uchar ip[IPaddrlen]; Req r; setnetmtpt(net, sizeof(net), nil); fmtinstall('E', eipfmt); fmtinstall('I', eipfmt); fmtinstall('V', eipfmt); fmtinstall('M', eipfmt); ARGBEGIN { case 'm': mute = 1; break; case 'd': debug = 1; break; case 'f': p = ARGF(); if(p == nil) usage(); ndbfile = p; break; case 's': slowstat = 1; break; case 'S': slowdyn = 1; break; case 'n': nobootp = 1; break; case 'p': pptponly = 1; break; case 'r': mutestat = 1; break; case 'x': p = ARGF(); if(p == nil) usage(); setnetmtpt(net, sizeof(net), p); break; case 'M': p = ARGF(); if(p == nil) usage(); minlease = atoi(p); if(minlease <= 0) minlease = MinLease; break; } ARGEND; while(argc > 1){ parseip(ip, argv[0]); if(!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? */ p = readsysname(); strcpy(mysysname, p); /* put process in background */ if(!debug) switch(rfork(RFNOTEG|RFPROC|RFFDG)) { case -1: fatal(1, "fork"); case 0: break; default: exits(0); } chdir(TFTP); fd = openlisten(net); for(;;){ memset(&r, 0, sizeof(r)); r.fd = fd; n = readlast(r.fd, r.buf, sizeof(r.buf)); if(n < OUdphdrsize) fatal(1, "error reading requests"); start = nsec()/1000; op = optbuf; *op = 0; proto(&r, n); if(r.id != nil) free(r.id); }}voidproto(Req *rp, int n){ uchar relip[IPaddrlen]; char buf[64]; now = time(0); rp->e = rp->buf + n; rp->bp = (Bootp*)rp->buf; rp->up = (OUdphdr*)rp->buf; rp->max = rp->buf + OUdphdrsize + 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; ipifcs = readipifc(net, ipifcs, -1); if(validip(rp->giaddr)) ipmove(relip, rp->giaddr); else if(validip(rp->up->raddr)) ipmove(relip, rp->up->raddr); else ipmove(relip, rp->up->laddr); if(rp->e < (uchar*)rp->bp->sname){ warning(0, "packet too short"); return; } if(rp->bp->op != Bootrequest){ warning(0, "not bootrequest"); 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){ if(rp->bp->hlen > Maxhwlen){ warning(0, "hlen %d", rp->bp->hlen); return; } if(memcmp(zeros, rp->bp->chaddr, rp->bp->hlen) == 0){ warning(0, "no chaddr"); return; } sprint(buf, "hwa%2.2ux_", rp->bp->htype); rp->id = tohex(buf, rp->bp->chaddr, rp->bp->hlen); } /* info about gateway */ if(lookupip(relip, &rp->gii, 1) < 0){ warning(0, "lookupip failed"); return; } /* 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 voidslowdelay(Req *rp){ if(slowstat && rp->staticbinding || slowdyn && !rp->staticbinding) sleep(2000);}voiddhcp(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; }}voidrcvdiscover(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(0, "!Discover(%s via %I): no binding %I", rp->id, rp->gii.ipaddr, rp->ip); return; } mkoffer(b, rp->id, rp->leasetime); sendoffer(rp, b->ip, b->offer);}voidrcvrequest(Req *rp){ Binding *b; if(validip(rp->server)){ /* this is a reply to an offer - SELECTING */ /* check for hard assignment */ if(rp->staticbinding){ if(forme(rp->server)) sendack(rp, rp->ii.ipaddr, (StaticLease > minlease ? StaticLease : minlease), 1); else warning(0, "!Request(%s via %I): for server %I not me", rp->id, rp->gii.ipaddr, rp->server); return; } b = idtooffer(rp->id, &rp->gii); /* if we don't have an offer, nak */ if(b == nil){ warning(0, "!Request(%s via %I): no offer", rp->id, rp->gii.ipaddr); if(forme(rp->server)) sendnak(rp, "no offer for you"); return; } /* if not for me, retract offer */ if(!forme(rp->server)){ b->expoffer = 0; warning(0, "!Request(%s via %I): for server %I not me", rp->id, rp->gii.ipaddr, 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(0, "!Request(%s via %I): requests %I, not %I", rp->id, rp->gii.ipaddr, rp->ip, b->ip); sendnak(rp, "bad ip address option"); return; } if(commitbinding(b) < 0){ warning(0, "!Request(%s via %I): can't commit %I", rp->id, rp->gii.ipaddr, b->ip); sendnak(rp, "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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -