dhcpd.c

Go to the documentation of this file.
00001 /*      dhcpd 1.15 - Dynamic Host Configuration Protocol daemon.
00002  *                                                      Author: Kees J. Bot
00003  *                                                              11 Jun 1999
00004  */
00005 #include <sys/types.h>
00006 #include <stdio.h>
00007 #include <stddef.h>
00008 #include <stdlib.h>
00009 #include <unistd.h>
00010 #include <fcntl.h>
00011 #include <errno.h>
00012 #include <signal.h>
00013 #include <string.h>
00014 #include <time.h>
00015 #include <limits.h>
00016 #include <configfile.h>
00017 #include <sys/ioctl.h>
00018 #include <sys/asynchio.h>
00019 #include <net/hton.h>
00020 #include <net/gen/socket.h>
00021 #include <net/gen/netdb.h>
00022 #include <net/gen/in.h>
00023 #include <net/gen/inet.h>
00024 #include <net/gen/ether.h>
00025 #include <net/gen/if_ether.h>
00026 #include <net/gen/eth_hdr.h>
00027 #include <net/gen/ip_hdr.h>
00028 #include <net/gen/udp.h>
00029 #include <net/gen/udp_hdr.h>
00030 #include <net/gen/udp_io.h>
00031 #include <net/gen/dhcp.h>
00032 #include "arp.h"
00033 #define EXTERN
00034 #include "dhcpd.h"
00035 
00036 char *configfile= PATH_DHCPCONF;
00037 char *poolfile= PATH_DHCPPOOL;
00038 static char *cachefile= PATH_DHCPCACHE;
00039 static int qflag;               /* True if printing cached DHCP data. */
00040 static int aflag, rflag;        /* True if adding or deleting pool addresses. */
00041 
00042 #define BCAST_IP        HTONL(0xFFFFFFFFUL)
00043 
00044 /* We try to play with up to this many networks. */
00045 #define N_NETS          32
00046 static unsigned n_nets;         /* Actual number of networks. */
00047 
00048 void report(const char *label)
00049 {
00050     static FILE *logfp;
00051     if(!logfp)
00052         logfp = fopen("/usr/log/dhcp.log", "w");
00053     if(logfp)
00054         fprintf(logfp, "%s: %s: %s\n", program, label, strerror(errno));
00055 }
00056 
00057 void fatal(const char *label)
00058 {
00059     report(label);
00060     exit(1);
00061 }
00062 
00063 void *allocate(size_t size)
00064 {
00065     void *mem;
00066 
00067     if ((mem= malloc(size)) == nil) fatal("Can't allocate memory");
00068     return mem;
00069 }
00070 
00071 /* Choose a DHCP xid based on the start time and network number.  Not really
00072  * random, but we don't have anything more random than the clock anyway.
00073  */
00074 #define XID(np)         htonl(((u32_t) (np)->start << 8) | (np)->n)
00075 
00076 static network_t *network[N_NETS];
00077 
00078 int ifname2if(const char *name)
00079 {
00080     /* Translate an interface name to a number, -1 if bad. */
00081     char *end;
00082     unsigned long n;
00083 
00084     if (*name++ != 'i' || *name++ != 'p') return -1;
00085     n= strtoul(name, &end, 10);
00086     if (end == name || *end != 0) return -1;
00087     if (n >= N_NETS) return -1;
00088     return n;
00089 }
00090 
00091 network_t *if2net(int n)
00092 {
00093     /* Translate an interface number to a network struct. */
00094     int i;
00095 
00096     for (i= 0; i < n_nets; i++) {
00097         if (network[i]->n == n) return network[i];
00098     }
00099     return nil;
00100 }
00101 
00102 static ipaddr_t defaultmask(ipaddr_t ip)
00103 {
00104     /* Compute netmask by the oldfashioned Class rules. */
00105     if (B(&ip)[0] < 0x80) return HTONL(0xFF000000UL);   /* Class A. */
00106     if (B(&ip)[0] < 0xC0) return HTONL(0xFFFF0000UL);   /* Class B. */
00107     if (B(&ip)[0] < 0xE0) return HTONL(0xFFFFFF00UL);   /* Class C. */
00108     return HTONL(0xFFFFFFFFUL);  /* Multicast?  Shouldn't happen... */
00109 }
00110 
00111 #define POOL_MAGIC      HTONL(0x81F85D00UL)
00112 
00113 typedef struct pool {           /* Dynamic pool entry. */
00114         u32_t           magic;          /* Pool file magic number. */
00115         ipaddr_t        ip;             /* IP address. */
00116         u32_t           expire;         /* When does/did the lease expire? */
00117         u8_t            len;            /* Client ID length. */
00118         u8_t            unused[19];     /* Space for extensions. */
00119         u8_t            clid[CLID_MAX]; /* Client ID of current/last user. */
00120 } pool_t;
00121 
00122 static int openpool(int mode)
00123 {
00124     /* Open the dynamic pool and lock it, return fd on success or -1. */
00125     int fd;
00126     struct flock lck;
00127 
00128     if ((fd= open(poolfile, mode, 0644)) < 0) {
00129         if (errno != ENOENT) fatal(poolfile);
00130         return -1;
00131     }
00132     if (mode != O_RDONLY) {
00133         lck.l_type= F_WRLCK;
00134         lck.l_whence= SEEK_SET;
00135         lck.l_start= 0;
00136         lck.l_len= 0;
00137         if (fcntl(fd, F_SETLKW, &lck) < 0) fatal(poolfile);
00138     }
00139     return fd;
00140 }
00141 
00142 static int readpool(int fd, pool_t *entry)
00143 {
00144     /* Read one pool table entry, return true unless EOF. */
00145     ssize_t r;
00146 
00147     if ((r= read(fd, entry, sizeof(*entry))) < 0) fatal(poolfile);
00148     if (r == 0) return 0;
00149 
00150     if (r != sizeof(*entry) || entry->magic != POOL_MAGIC) {
00151         fprintf(stderr, "%s: %s: Pool table is corrupt\n",
00152             program, poolfile);
00153         close(fd);
00154         return 0;
00155     }
00156     return 1;
00157 }
00158 
00159 #if !__minix_vmd        /* No fsync() for Minix. */
00160 #define fsync(fd)       sync()
00161 #endif
00162 
00163 static void writepool(int fd, pool_t *entry)
00164 {
00165     /* (Over)write a pool table entry. */
00166     if (write(fd, entry, sizeof(*entry)) < 0
00167         || (entry->expire > now && fsync(fd) < 0)
00168     ) {
00169         fatal(poolfile);
00170     }
00171 }
00172 
00173 static ipaddr_t findpool(u8_t *client, size_t len, ipaddr_t ifip)
00174 {
00175     /* Look for a client ID in the dynamic address pool within the same network
00176      * as 'ifip'.  Select an unused one for a new client if necessary.  Return
00177      * 0 if nothing is available, otherwise the IP address we can offer.
00178      */
00179     int fd, found;
00180     pool_t entry, oldest;
00181     dhcp_t dhcp;
00182     u8_t *pmask;
00183     ipaddr_t mask;
00184 
00185     /* Any information available on the network the client is at? */
00186     if (!makedhcp(&dhcp, nil, 0, nil, 0, ifip, ifip, nil)) return 0;
00187 
00188     if ((fd= openpool(O_RDWR)) < 0) return 0;
00189 
00190     (void) gettag(&dhcp, DHCP_TAG_NETMASK, &pmask, nil);
00191     memcpy(&mask, pmask, sizeof(mask));
00192 
00193     oldest.expire= NEVER;
00194     while ((found= readpool(fd, &entry))) {
00195         /* Deleted entry? */
00196         if (entry.ip == 0) continue;
00197 
00198         /* Correct network? */
00199         if (((entry.ip ^ ifip) & mask) != 0) continue;
00200 
00201         /* Client present? */
00202         if (entry.len == len && memcmp(entry.clid, client, len) == 0) break;
00203 
00204         /* Oldest candidate for a new lease? */
00205         entry.expire= ntohl(entry.expire);
00206         if (entry.expire < oldest.expire) oldest= entry;
00207     }
00208     close(fd);
00209 
00210     if (found) return entry.ip;
00211     if (oldest.expire <= now) return oldest.ip;
00212     return 0;
00213 }
00214 
00215 static int commitpool(ipaddr_t ip, u8_t *client, size_t len, time_t expire)
00216 {
00217     /* Commit a new binding to stable storage, return true on success. */
00218     int fd;
00219     pool_t entry;
00220 
00221     if ((fd= openpool(O_RDWR)) < 0) return 0;
00222 
00223     do {
00224         if (!readpool(fd, &entry)) {
00225             close(fd);
00226             return 0;
00227         }
00228     } while (entry.ip != ip);
00229 
00230     entry.expire= htonl(expire);
00231     entry.len= len;
00232     memcpy(entry.clid, client, len);
00233     if (lseek(fd, -(off_t)sizeof(entry), SEEK_CUR) == -1) fatal(poolfile);
00234     writepool(fd, &entry);
00235     close(fd);
00236     return 1;
00237 }
00238 
00239 static void updatepool(int add, const char *name)
00240 {
00241     /* Add a new IP address to the dynamic pool. */
00242     ipaddr_t ip;
00243     int fd, i;
00244     pool_t entry;
00245     struct hostent *he;
00246     off_t off, off0;
00247 
00248     if ((he= gethostbyname(name)) == nil || he->h_addrtype != AF_INET) {
00249         fprintf(stderr, "%s: %s: Unknown host\n", program, name);
00250         exit(1);
00251     }
00252     for (i= 0; he->h_addr_list[i] != nil; i++) {}
00253     if (i != 1) {
00254         fprintf(stderr, "%s: %s has %d addresses\n", program, name, i);
00255         exit(1);
00256     }
00257     memcpy(&ip, he->h_addr_list[0], sizeof(ip));
00258 
00259     if ((fd= openpool(O_RDWR|O_CREAT)) < 0) fatal(poolfile);
00260 
00261     off= 0;
00262     off0= -1;
00263     while (readpool(fd, &entry)) {
00264         if (add) {
00265             if (entry.ip == ip) {
00266                 fprintf(stderr, "%s: %s: %s is already present\n",
00267                     program, poolfile, name);
00268                 exit(1);
00269             }
00270             if (entry.ip == 0 && off0 == -1) off0= off;
00271         } else {
00272             if (entry.ip == ip) {
00273                 memset(&entry, 0, sizeof(entry));
00274                 entry.magic= POOL_MAGIC;
00275                 entry.ip= 0;
00276                 if (lseek(fd, off, SEEK_SET) == -1) fatal(poolfile);
00277                 writepool(fd, &entry);
00278             }
00279         }
00280         off+= sizeof(entry);
00281     }
00282 
00283     if (add) {
00284         if (off0 != -1 && lseek(fd, off0, SEEK_SET) == -1) fatal(poolfile);
00285         memset(&entry, 0, sizeof(entry));
00286         entry.magic= POOL_MAGIC;
00287         entry.ip= ip;
00288         writepool(fd, &entry);
00289     }
00290     close(fd);
00291 }
00292 
00293 static void cachedhcp(int n, dhcp_t *dp)
00294 {
00295     /* Store a DHCP packet in a cache where those who care can find it. */
00296     static int inited;
00297     FILE *fp;
00298     int fd;
00299     int mode;
00300 
00301     if (test > 0) return;
00302 
00303     if (!inited) {
00304         /* First time, clear store and also save my pid. */
00305         if ((fp= fopen(PATH_DHCPPID, "w")) != nil) {
00306             if (fprintf(fp, "%d\n", getpid()) == EOF || fclose(fp) == EOF) {
00307                 fatal(PATH_DHCPPID);
00308             }
00309         }
00310         inited= 1;
00311         mode= O_WRONLY | O_CREAT | O_TRUNC;
00312     } else {
00313         mode= O_WRONLY;
00314     }
00315 
00316     dp->xid= htonl(now);        /* To tell how old this data is. */
00317 
00318     if ((fd= open(cachefile, mode, 0666)) < 0
00319         || lseek(fd, (off_t) n * sizeof(*dp), SEEK_SET) == -1
00320         || write(fd, dp, sizeof(*dp)) < 0
00321         || close(fd) < 0
00322     ) {
00323         if (errno != ENOENT) fatal(cachefile);
00324     }
00325 }
00326 
00327 static void printdata(void)
00328 {
00329     /* Show the contents of the cache and the dynamic pool. */
00330     int fd;
00331     dhcp_t d;
00332     ssize_t r;
00333     int i;
00334     pool_t entry;
00335     unsigned long expire;
00336     char delta[3*sizeof(u32_t)];
00337 
00338     initdhcpconf();
00339 
00340     if ((fd= open(cachefile, O_RDONLY)) < 0) fatal(cachefile);
00341     i= 0;
00342     while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) {
00343         if (d.yiaddr != 0) {
00344             printf("DHCP data for network %d:\n", i);
00345             printdhcp(&d);
00346         }
00347         i++;
00348     }
00349     if (r < 0) fatal(cachefile);
00350     close(fd);
00351 
00352     if ((fd= openpool(O_RDONLY)) >= 0) {
00353         printf("Dynamic address pool since %ld:\n", (long) now);
00354         while (readpool(fd, &entry)) {
00355             if (entry.ip == 0) continue;
00356             expire= ntohl(entry.expire);
00357             if (expire == 0) {
00358                 strcpy(delta, "unused");
00359             } else
00360             if (expire == 0xFFFFFFFFUL) {
00361                 strcpy(delta, "infinite");
00362             } else
00363             if (expire < now) {
00364                 sprintf(delta, "-%lu", now - expire);
00365             } else {
00366                 sprintf(delta, "+%lu", expire - now);
00367             }
00368             printf("\t%-15s %8s  ", inet_ntoa(entry.ip), delta);
00369             for (i= 0; i < entry.len; i++) {
00370                 printf("%02X", entry.clid[i]);
00371             }
00372             fputc('\n', stdout);
00373         }
00374         close(fd);
00375     }
00376 }
00377 
00378 static udpport_t portbyname(const char *name)
00379 {
00380     struct servent *se;
00381 
00382     if ((se= getservbyname(name, "udp")) == nil) {
00383         fprintf(stderr, "%s: Unknown port \"%s\"\n", program, name);
00384         exit(1);
00385     }
00386     return se->s_port;
00387 }
00388 
00389 static int send(network_t *np, void *data, size_t len)
00390 {
00391     /* Send out a packet using a filedescriptor that is probably in async mode,
00392      * so first dup() a sync version, then write.  Return true on success.
00393      */
00394     int fd;
00395     ssize_t r;
00396 
00397     if ((fd= dup(np->fdp->fd)) < 0) fatal("Can't dup()");
00398     if ((r= write(fd, data, len)) < 0) {
00399         report(np->fdp->device);
00400         sleep(10);
00401     }
00402     close(fd);
00403     return r >= 0;
00404 }
00405 
00406 static size_t servdhcp(network_t *np, buf_t *bp, size_t dlen)
00407 {
00408     buf_t *abp= nil;
00409     ipaddr_t cip, ifip;
00410     u8_t defclid[1+sizeof(bp->dhcp->chaddr)];
00411     u8_t *pdata, *client, *class, *server, *reqip, *lease;
00412     u32_t expire;
00413     size_t len, cilen, calen;
00414     int type, dyn;
00415     u8_t atype;
00416     static char NAKMESS[] = "IP address requested isn't yours";
00417 
00418     if (test > 0) return 0;
00419 
00420     /* The IP address of the interface close to the client. */
00421     ifip= bp->dhcp->giaddr != 0 ? bp->dhcp->giaddr : np->ip;
00422 
00423     /* All kinds of DHCP tags. */
00424     if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
00425         type= *pdata;
00426     } else {
00427         type= -1;               /* BOOTP? */
00428     }
00429 
00430     if (!gettag(bp->dhcp, DHCP_TAG_CLIENTID, &client, &cilen)) {
00431         defclid[0]= bp->dhcp->htype;
00432         memcpy(defclid+1, bp->dhcp->chaddr, bp->dhcp->hlen);
00433         client= defclid;
00434         cilen= 1+bp->dhcp->hlen;
00435     }
00436 
00437     if (!gettag(bp->dhcp, DHCP_TAG_CLASSID, &class, &calen)) {
00438         calen= 0;
00439     }
00440 
00441     if (!gettag(bp->dhcp, DHCP_TAG_SERVERID, &server, nil)) {
00442         server= B(&np->ip);
00443     }
00444 
00445     if (!gettag(bp->dhcp, DHCP_TAG_REQIP, &reqip, nil)) {
00446         reqip= nil;
00447     }
00448 
00449     /* I'm a server?  Then see if I know this client. */
00450     if ((np->flags & NF_SERVING)
00451         && bp->dhcp->op == DHCP_BOOTREQUEST
00452         && between(1, bp->dhcp->hlen, sizeof(bp->dhcp->chaddr))
00453         && (server == nil || memcmp(server, &np->ip, sizeof(np->ip)) == 0)
00454     ) {
00455         get_buf(&abp);
00456 
00457         /* Is the client in my tables? */
00458         (void) makedhcp(abp->dhcp, class, calen, client, cilen, 0, ifip, nil);
00459         cip= abp->dhcp->yiaddr;
00460 
00461         dyn= 0;
00462         /* If not, do we have a dynamic address? */
00463         if (cip == 0 && (cip= findpool(client, cilen, ifip)) != 0) dyn= 1;
00464 
00465         if (type == DHCP_INFORM) {
00466             /* The client already has an address, it just wants information.
00467              * We only answer if we could answer a normal request (cip != 0),
00468              * unless configured to answer anyone.
00469              */
00470             if (cip != 0 || (np->flags & NF_INFORM)) cip= bp->dhcp->ciaddr;
00471         }
00472 
00473         if (cip == 0 || !makedhcp(abp->dhcp, class, calen,
00474                                         client, cilen, cip, ifip, nil)) {
00475             put_buf(&abp);
00476         }
00477 
00478         if (abp != nil) {
00479             if (gettag(abp->dhcp, DHCP_TAG_LEASE, &lease, nil)) {
00480                 memcpy(&expire, lease, sizeof(expire));
00481                 expire= now + ntohl(expire);
00482                 if (expire < now) expire= 0xFFFFFFFFUL;
00483             } else {
00484                 if (dyn) {
00485                     /* A dynamic address must have a lease. */
00486                     fprintf(stderr, "%s: No lease set for address %s\n",
00487                         program, inet_ntoa(cip));
00488                     exit(1);
00489                 }
00490                 lease= nil;
00491                 expire= 0xFFFFFFFFUL;
00492             }
00493 
00494             /* What does our client want, and what do we say? */
00495             switch (type) {
00496             case DHCP_DISCOVER:
00497                 atype= DHCP_OFFER;
00498 
00499                 /* Assign this address for a short moment. */
00500                 if (dyn && !commitpool(cip, client, cilen, now + DELTA_FAST)) {
00501                     put_buf(&abp);
00502                 }
00503                 break;
00504 
00505             case -1:/* BOOTP */
00506             case DHCP_REQUEST:
00507             case DHCP_INFORM:
00508                 atype= DHCP_ACK;
00509                 /* The address wanted must be the address we offer. */
00510                 if ((reqip != nil && memcmp(reqip, &cip, sizeof(cip)) != 0)
00511                     || (bp->dhcp->ciaddr != 0 && bp->dhcp->ciaddr != cip)
00512                 ) {
00513                     atype= DHCP_NAK;
00514                 } else
00515                 if (dyn && type == DHCP_REQUEST) {
00516                     /* Assign this address for the duration of the lease. */
00517                     if (!commitpool(cip, client, cilen, expire)) put_buf(&abp);
00518                 }
00519                 break;
00520 
00521             case DHCP_DECLINE:
00522                 /* Our client doesn't want the offered address! */
00523                 if (dyn
00524                     && reqip != nil
00525                     && memcmp(reqip, &cip, sizeof(cip)) == 0
00526                 ) {
00527                     int i;
00528 
00529                     fprintf(stderr, "%s: ", program);
00530                     for (i= 0; i < cilen; i++) {
00531                         fprintf(stderr, "%02X", client[i]);
00532                     }
00533                     fprintf(stderr, " declines %s", inet_ntoa(cip));
00534                     if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
00535                         fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
00536                     }
00537                     fputc('\n', stderr);
00538 
00539                     /* Disable address for the duration of the lease. */
00540                     (void) commitpool(cip, nil, 0, expire);
00541                 }
00542                 put_buf(&abp);
00543                 break;
00544 
00545             case DHCP_RELEASE:
00546                 /* Our client is nice enough to return its address. */
00547                 if (dyn) (void) commitpool(cip, client, cilen, now);
00548                 put_buf(&abp);
00549                 break;
00550 
00551             default:    /* Anything else is ignored. */
00552                 put_buf(&abp);
00553             }
00554         }
00555 
00556         if (abp != nil) {
00557             /* Finish the return packet. */
00558             abp->dhcp->htype= bp->dhcp->htype;
00559             abp->dhcp->hlen= bp->dhcp->hlen;
00560             abp->dhcp->hops= 0;
00561             abp->dhcp->xid= bp->dhcp->xid;
00562             abp->dhcp->secs= 0;
00563             abp->dhcp->flags= bp->dhcp->flags;
00564             abp->dhcp->ciaddr= 0;
00565             abp->dhcp->yiaddr= atype == DHCP_NAK ? 0 : cip;
00566             if (atype == DHCP_NAK) abp->dhcp->siaddr= 0;
00567             abp->dhcp->giaddr= bp->dhcp->giaddr;
00568             memcpy(abp->dhcp->chaddr,bp->dhcp->chaddr,sizeof(bp->dhcp->chaddr));
00569 
00570             settag(abp->dhcp, DHCP_TAG_SERVERID, &np->ip, sizeof(np->ip));
00571 
00572             if (lease == nil) {
00573                 /* No lease specified?  Then give an infinite lease. */
00574                 settag(abp->dhcp, DHCP_TAG_LEASE, &expire, sizeof(expire));
00575             }
00576 
00577             if (type == DHCP_INFORM) {
00578                 /* Oops, this one has a fixed address, so no lease business. */
00579                 abp->dhcp->yiaddr= 0;
00580                 settag(abp->dhcp, DHCP_TAG_LEASE, nil, 0);
00581                 settag(abp->dhcp, DHCP_TAG_RENEWAL, nil, 0);
00582                 settag(abp->dhcp, DHCP_TAG_REBINDING, nil, 0);
00583             }
00584 
00585             if (atype == DHCP_NAK) {
00586                 /* A NAK doesn't need much. */
00587                 memset(abp->dhcp->sname, 0, sizeof(abp->dhcp->sname));
00588                 memset(abp->dhcp->file, 0, sizeof(abp->dhcp->file));
00589                 memset(abp->dhcp->options, 255, sizeof(abp->dhcp->options));
00590                 settag(abp->dhcp, DHCP_TAG_MESSAGE, NAKMESS, sizeof(NAKMESS));
00591             }
00592 
00593             settag(abp->dhcp, DHCP_TAG_TYPE, &atype, sizeof(atype));
00594 
00595             /* Figure out where to send this to. */
00596             abp->udpio->uih_src_addr= np->ip;
00597             abp->udpio->uih_src_port= port_server;
00598             if (bp->dhcp->giaddr != 0) {
00599                 abp->udpio->uih_dst_addr= bp->dhcp->giaddr;
00600                 abp->udpio->uih_dst_port= port_server;
00601             } else
00602             if (bp->dhcp->flags & DHCP_FLAGS_BCAST) {
00603                 abp->udpio->uih_dst_addr= BCAST_IP;
00604                 abp->udpio->uih_dst_port= port_client;
00605             } else
00606             if (bp->udpio->uih_src_addr != 0
00607                 && bp->udpio->uih_dst_addr == np->ip
00608             ) {
00609                 abp->udpio->uih_dst_addr= bp->udpio->uih_src_addr;
00610                 abp->udpio->uih_dst_port= port_client;
00611             } else {
00612                 abp->udpio->uih_dst_addr= BCAST_IP;
00613                 abp->udpio->uih_dst_port= port_client;
00614             }
00615             abp->udpio->uih_ip_opt_len= 0;
00616             abp->udpio->uih_data_len= sizeof(dhcp_t);
00617 
00618             /* Copy the packet to the input buffer, and return the new size. */
00619             memcpy(bp->buf, abp->buf, sizeof(bp->buf));
00620             put_buf(&abp);
00621             return sizeof(udp_io_hdr_t) + sizeof(dhcp_t);
00622         }
00623     }
00624 
00625     /* I'm a relay?  If it is a not already a relayed request then relay. */
00626     if ((np->flags & NF_RELAYING)
00627         && bp->dhcp->op == DHCP_BOOTREQUEST
00628         && bp->dhcp->giaddr == 0
00629     ) {
00630         bp->dhcp->giaddr= np->ip;
00631         bp->udpio->uih_src_addr= np->ip;
00632         bp->udpio->uih_src_port= port_server;
00633         bp->udpio->uih_dst_addr= np->server;
00634         bp->udpio->uih_dst_port= port_server;
00635         return dlen;
00636     }
00637 
00638     /* I'm a relay?  If the server sends a reply to me then relay back. */
00639     if ((np->flags & NF_RELAYING)
00640         && bp->dhcp->op == DHCP_BOOTREPLY
00641         && bp->dhcp->giaddr == np->ip
00642     ) {
00643         bp->dhcp->giaddr= 0;
00644         bp->udpio->uih_src_addr= np->ip;
00645         bp->udpio->uih_src_port= port_server;
00646         bp->udpio->uih_dst_addr= BCAST_IP;
00647         bp->udpio->uih_dst_port= port_client;
00648         return dlen;
00649     }
00650 
00651     /* Don't know what to do otherwise, so doing nothing seems wise. */
00652     return 0;
00653 }
00654 
00655 static void onsig(int sig)
00656 {
00657     switch (sig) {
00658     case SIGUSR1:       debug++;        break;
00659     case SIGUSR2:       debug= 0;       break;
00660     }
00661 }
00662 
00663 static void usage(void)
00664 {
00665     fprintf(stderr,
00666 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
00667         program);
00668     exit(1);
00669 }
00670 
00671 int main(int argc, char **argv)
00672 {
00673     int i;
00674     network_t *np;
00675     struct sigaction sa;
00676     ssize_t r= -1;
00677     buf_t *bp= nil;
00678     static struct timeval eventtv;
00679 
00680     program= argv[0];
00681     start= now= time(nil);
00682 
00683     debug= 0;
00684     i= 1;
00685     while (i < argc && argv[i][0] == '-') {
00686         char *opt= argv[i++]+1;
00687 
00688         if (opt[0] == '-' && opt[1] == 0) break;        /* -- */
00689 
00690         while (*opt != 0) switch (*opt++) {
00691         case 'f':
00692             if (*opt == 0) {
00693                 if (i == argc) usage();
00694                 opt= argv[i++];
00695             }
00696             configfile= opt;
00697             opt= "";
00698             break;
00699         case 'c':
00700             if (*opt == 0) {
00701                 if (i == argc) usage();
00702                 opt= argv[i++];
00703             }
00704             cachefile= opt;
00705             opt= "";
00706             break;
00707         case 'p':
00708             if (*opt == 0) {
00709                 if (i == argc) usage();
00710                 opt= argv[i++];
00711             }
00712             poolfile= opt;
00713             opt= "";
00714             break;
00715         case 't':
00716             test= 1;
00717             if (between('0', *opt, '9')) test= strtoul(opt, &opt, 10);
00718             break;
00719         case 'd':
00720             debug= 1;
00721             if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
00722             break;
00723         case 'q':
00724             qflag= 1;
00725             break;
00726         case 'a':
00727             aflag= 1;
00728             break;
00729         case 'r':
00730             rflag= 1;
00731             break;
00732         default:
00733             usage();
00734         }
00735     }
00736     if (aflag + rflag + qflag > 1) usage();
00737 
00738     if (aflag || rflag) {
00739         /* Add or remove addresses from the dynamic pool. */
00740         while (i < argc) updatepool(aflag, argv[i++]);
00741         exit(0);
00742     }
00743 
00744     if (i != argc) usage();
00745 
00746     if (qflag) {
00747         /* Only show the contents of the cache and dynamic pool to the user. */
00748         printdata();
00749         exit(0);
00750     }
00751 
00752     /* BOOTP ports. */
00753     port_server= portbyname("bootps");
00754     port_client= portbyname("bootpc");
00755 
00756     sa.sa_handler= onsig;
00757     sigemptyset(&sa.sa_mask);
00758     sa.sa_flags= 0;
00759     sigaction(SIGUSR1, &sa, nil);
00760     sigaction(SIGUSR2, &sa, nil);
00761 
00762     /* Initial configuration. */
00763     for (i= 0; i < N_NETS; i++) {
00764         int fd;
00765         ipaddr_t ip, mask;
00766 
00767         /* Is there something there? */
00768         if ((fd= open(ipdev(i), O_RDWR|O_NONBLOCK)) < 0) {
00769             if (errno != ENOENT && errno != ENODEV && errno != ENXIO) {
00770                 fatal(ipdev(i));
00771             }
00772             continue;
00773         }
00774         close(fd);
00775 
00776         network[n_nets++]= np= newnetwork();
00777         np->n= i;
00778 
00779         /* Ethernet? */
00780         if (opendev(np, FT_ETHERNET, 1)) {
00781             np->type= B(&np->eth)[0] != 'Z' ? NT_ETHERNET : NT_SINK;
00782             if (debug >= 1) {
00783                 printf("%s: Ethernet address is %s%s\n",
00784                     np->fdp->device, ether_ntoa(&np->eth),
00785                     np->type == NT_SINK ? " (sink)" : "");
00786             }
00787             closedev(np, FT_ETHERNET);
00788         }
00789 
00790         /* Only true Ethernets worry about DHCP. */
00791         if (np->type != NT_ETHERNET) np->renew= np->rebind= np->lease= NEVER;
00792     }
00793 
00794     /* Try to find my interfaces in the DHCP table. */
00795     for (i= 0; i < n_nets; i++) {
00796         ipaddr_t cip;
00797         u8_t clid[1+DHCP_HLEN_ETH];
00798         size_t cilen;
00799 
00800         np= network[i];
00801         if (np->flags & NF_BOUND) continue;
00802 
00803         if (np->type == NT_IP) {
00804             cilen= 0;
00805         } else {
00806             ether2clid(clid, &np->eth);
00807             cilen= 1+DHCP_HLEN_ETH;
00808         }
00809 
00810         /* Try to find an Ethernet address, or the IP address of an already
00811          * configured network.  If we have data we get an IP address back.
00812          */
00813         get_buf(&bp);
00814         (void) makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
00815                                         clid, cilen, np->ip, 0, np);
00816         cip= bp->dhcp->yiaddr;
00817 
00818         /* Gather information on the interface. */
00819         if (cip != 0
00820             && makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
00821                                         clid, cilen, cip, cip, np)
00822             && test < 2
00823         ) {
00824             u8_t *pdata;
00825             u16_t mtu;
00826 
00827             cachedhcp(np->n, bp->dhcp);
00828             np->ip= cip;
00829             (void) gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil);
00830             memcpy(&np->mask, pdata, sizeof(np->mask));
00831             if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
00832                 memcpy(&np->gateway, pdata, sizeof(np->gateway));
00833             } else {
00834                 np->gateway= 0;
00835             }
00836             if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
00837                 memcpy(&mtu, pdata, sizeof(mtu));
00838                 mtu= ntohs(mtu);
00839             } else {
00840                 mtu= 0;
00841             }
00842             set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
00843             if (debug >= 1) {
00844                 printf("%s: IP address is %s\n",
00845                     ipdev(np->n), cidr_ntoa(np->ip, np->mask));
00846             }
00847             np->flags |= NF_BOUND;
00848             np->renew= np->rebind= np->lease= NEVER;
00849             np->sol_ct= N_SOLICITS;
00850             np->solicit= 0;
00851 
00852             /* Other (previous) interfaces may have been defined. */
00853             i= 0;
00854         }
00855         put_buf(&bp);
00856     }
00857 
00858     for (;;) {
00859         now= time(nil);
00860         event= NEVER;
00861 
00862         /* Is it time to request/renew a lease? */
00863         for (i= 0; i < n_nets; i++) {
00864             np= network[i];
00865 
00866             if (np->renew <= now) {
00867                 u8_t type;
00868                 static u8_t taglist[] = {
00869                     DHCP_TAG_NETMASK, DHCP_TAG_GATEWAY, DHCP_TAG_DNS,
00870                         DHCP_TAG_HOSTNAME
00871                 };
00872                 u8_t ethclid[1+DHCP_HLEN_ETH];
00873 
00874                 /* We may have lost our binding or even our lease. */
00875                 if (np->rebind <= now) np->server= BCAST_IP;
00876 
00877                 if (np->lease <= now) {
00878                     if (np->flags & NF_BOUND) closedev(np, FT_ALL);
00879 
00880                     if ((np->flags & (NF_BOUND | NF_POSSESSIVE)) == NF_BOUND) {
00881                         set_ipconf(ipdev(np->n), np->ip= 0, np->mask= 0, 0);
00882                         if (debug >= 1) {
00883                             printf("%s: Interface disabled (lease expired)\n",
00884                                 ipdev(np->n));
00885                         }
00886                     }
00887                     np->flags &= ~NF_BOUND;
00888                 }
00889 
00890                 /* See if we can open the network we need to send on. */
00891                 if (!(np->flags & NF_BOUND)) {
00892                     if (!opendev(np, FT_ETHERNET, 1)) continue;
00893                 } else {
00894                     if (!opendev(np, FT_BOOTPC, 1)) continue;
00895                 }
00896 
00897                 if (!(np->flags & NF_NEGOTIATING)) {
00898                     /* We need to start querying a DHCP server. */
00899                     np->start= now;
00900                     np->delta= DELTA_FIRST;
00901                     np->flags |= NF_NEGOTIATING;
00902                 }
00903 
00904                 /* Fill in a DHCP query packet. */
00905                 get_buf(&bp);
00906                 dhcp_init(bp->dhcp);
00907                 bp->dhcp->op= DHCP_BOOTREQUEST;
00908                 bp->dhcp->htype= DHCP_HTYPE_ETH;
00909                 bp->dhcp->hlen= DHCP_HLEN_ETH;
00910                 bp->dhcp->xid= XID(np);
00911                 bp->dhcp->secs= htons(now - np->start > 0xFFFF
00912                                         ? 0xFFFF : now - np->start);
00913                 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
00914 
00915                 if (np->lease <= now) {
00916                     /* First time, or my old server is unresponsive. */
00917                     type= DHCP_DISCOVER;
00918                 } else {
00919                     /* Request an offered address or renew an address. */
00920                     type= DHCP_REQUEST;
00921                     if (np->flags & NF_BOUND) {
00922                         /* A renewal, I claim my current address. */
00923                         bp->dhcp->ciaddr= np->ip;
00924                     } else {
00925                         /* Nicely ask for the address just offered. */
00926                         settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip,
00927                                                         sizeof(np->ip));
00928                         settag(bp->dhcp, DHCP_TAG_SERVERID, &np->server,
00929                                                         sizeof(np->server));
00930                     }
00931                 }
00932                 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
00933 
00934                 /* My client ID.  Simply use the default. */
00935                 ether2clid(ethclid, &np->eth);
00936                 settag(bp->dhcp, DHCP_TAG_CLIENTID, ethclid, sizeof(ethclid));
00937 
00938                 /* The Class ID may serve to recognize Minix hosts. */
00939                 settag(bp->dhcp, DHCP_TAG_CLASSID, "Minix", 5);
00940 
00941                 /* The few tags that Minix can make good use of. */
00942                 settag(bp->dhcp, DHCP_TAG_REQPAR, taglist, sizeof(taglist));
00943 
00944                 /* Some weird sites use a hostname, not a client ID. */
00945                 if (np->hostname != nil) {
00946                     settag(bp->dhcp, DHCP_TAG_HOSTNAME,
00947                                         np->hostname, strlen(np->hostname));
00948                 }
00949 
00950                 bp->udpio->uih_src_addr= np->ip;
00951                 bp->udpio->uih_dst_addr= np->server;
00952                 bp->udpio->uih_src_port= port_client;
00953                 bp->udpio->uih_dst_port= port_server;
00954                 bp->udpio->uih_ip_opt_len= 0;
00955                 bp->udpio->uih_data_len= sizeof(dhcp_t);
00956 
00957                 if (!(np->flags & NF_BOUND)) {
00958                     /* Rebind over Ethernet. */
00959                     udp2ether(bp, np);
00960                     if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
00961                                         + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
00962                         if (debug >= 1) {
00963                             printf("%s: Broadcast DHCP %s\n",
00964                                 np->fdp->device, dhcptypename(type));
00965                             if (debug >= 2) printdhcp(bp->dhcp);
00966                         }
00967                     }
00968                 } else {
00969                     /* Renew over UDP. */
00970                     if (send(np, bp->udpio, sizeof(udp_io_hdr_t)
00971                                                         + sizeof(dhcp_t))) {
00972                         if (debug >= 1) {
00973                             printf("%s: Sent DHCP %s to %s\n",
00974                                 np->fdp->device,
00975                                 dhcptypename(type),
00976                                 inet_ntoa(np->server));
00977                             if (debug >= 2) printdhcp(bp->dhcp);
00978                         }
00979                     }
00980                 }
00981                 put_buf(&bp);
00982 
00983                 /* When to continue querying a DHCP server? */
00984                 if (np->flags & NF_BOUND) {
00985                     /* Still bound, keep halving time till next event. */
00986                     time_t e, d;
00987 
00988                     e= now < np->rebind ? np->rebind : np->lease;
00989                     d= (e - now) / 2;
00990                     if (d < DELTA_SLOW) d= DELTA_SLOW;
00991                     np->renew= now + d;
00992                     if (np->renew > e) np->renew= e;
00993                 } else {
00994                     /* Not bound, be desparate. */
00995                     np->renew= now + np->delta;
00996                     if ((np->delta *= 2) > DELTA_FAST) np->delta= DELTA_FAST;
00997                 }
00998             }
00999             if (np->renew < event) event= np->renew;
01000         }
01001 
01002         /* Read DHCP responses. */
01003         for (i= 0; i < n_nets; i++) {
01004             np= network[i];
01005             if (!(np->flags & NF_NEGOTIATING)) continue;
01006 
01007             if (!(np->flags & NF_BOUND)) {
01008                 if (!opendev(np, FT_ETHERNET, 0)) continue;
01009                 get_buf(&np->fdp->bp);
01010                 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->eth,
01011                                                         BUF_ETH_SIZE);
01012             } else {
01013                 if (!opendev(np, FT_BOOTPC, 0)) continue;
01014                 get_buf(&np->fdp->bp);
01015                 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio,
01016                                                         BUF_UDP_SIZE);
01017             }
01018             if (r != -1) break;
01019             if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
01020                 report(np->fdp->device);
01021                 sleep(10);
01022             }
01023         }
01024 
01025         /* Is there a response? */
01026         if (i < n_nets) {
01027             give_buf(&bp, &np->fdp->bp);
01028             if (((!(np->flags & NF_BOUND)
01029                     && r >= (sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
01030                                 + sizeof(udp_hdr_t) + offsetof(dhcp_t, options))
01031                     && ether2udp(bp)
01032                     && bp->udpio->uih_dst_port == port_client)
01033                   ||
01034                   ((np->flags & NF_BOUND)
01035                     && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)))
01036                 && bp->dhcp->op == DHCP_BOOTREPLY
01037                 && bp->dhcp->htype == DHCP_HTYPE_ETH
01038                 && bp->dhcp->hlen == DHCP_HLEN_ETH
01039                 && bp->dhcp->xid == XID(np)
01040                 && memcmp(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)) == 0
01041             ) {
01042                 /* Pfew!  We got a DHCP reply! */
01043                 u8_t *pdata;
01044                 size_t len;
01045                 int type;
01046                 ipaddr_t mask, gateway, relay, server;
01047                 u16_t mtu;
01048                 u32_t lease, renew, rebind, t;
01049 
01050                 relay= bp->udpio->uih_src_addr;
01051                 if (gettag(bp->dhcp, DHCP_TAG_SERVERID, &pdata, nil)) {
01052                     memcpy(&server, pdata, sizeof(server));
01053                 } else {
01054                     server= relay;
01055                 }
01056 
01057                 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
01058                     type= pdata[0];
01059                 } else {
01060                     type= DHCP_ACK;     /* BOOTP? */
01061                 }
01062 
01063                 if (debug >= 1) {
01064                     printf("%s: Got a DHCP %s from %s",
01065                         np->fdp->device, dhcptypename(type), inet_ntoa(server));
01066                     printf(relay != server ? " through %s\n" : "\n",
01067                         inet_ntoa(relay));
01068                     if (debug >= 2) printdhcp(bp->dhcp);
01069                 }
01070 
01071                 if (gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil)) {
01072                     memcpy(&mask, pdata, sizeof(mask));
01073                 } else {
01074                     mask= defaultmask(bp->dhcp->ciaddr);
01075                 }
01076 
01077                 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
01078                     memcpy(&mtu, pdata, sizeof(mtu));
01079                     mtu= ntohs(mtu);
01080                 } else {
01081                     mtu= 0;
01082                 }
01083 
01084                 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
01085                     memcpy(&gateway, pdata, sizeof(gateway));
01086                 } else {
01087                     gateway= 0;
01088                 }
01089 
01090                 lease= NEVER;
01091                 if (gettag(bp->dhcp, DHCP_TAG_LEASE, &pdata, nil)) {
01092                     memcpy(&lease, pdata, sizeof(lease));
01093                     lease= ntohl(lease);
01094                 }
01095 
01096                 rebind= lease - lease / 8;
01097                 if (gettag(bp->dhcp, DHCP_TAG_REBINDING, &pdata, nil)) {
01098                     memcpy(&t, pdata, sizeof(t));
01099                     t= ntohl(t);
01100                     if (t < rebind) rebind= t;
01101                 }
01102 
01103                 renew= lease / 2;
01104                 if (gettag(bp->dhcp, DHCP_TAG_RENEWAL, &pdata, nil)) {
01105                     memcpy(&t, pdata, sizeof(t));
01106                     t= ntohl(t);
01107                     if (t < renew) renew= t;
01108                 }
01109 
01110                 if (type == DHCP_OFFER && np->rebind <= np->renew) {
01111                     /* It's an offer for an address and we haven't taken one
01112                      * yet.  It's all the same to us, so take this one.
01113                      */
01114                     np->ip= bp->dhcp->yiaddr;
01115                     np->mask= mask;
01116                     np->server= server;
01117                     np->gateway= gateway;
01118                     np->delta= DELTA_FIRST;
01119                     np->renew= now;
01120                     np->rebind= np->lease= now + DELTA_FAST;
01121 
01122                     /* Send out an ARP request to see if the offered address
01123                      * is in use already.
01124                      */
01125                     make_arp(bp, np);
01126                     if (send(np, bp->eth, sizeof(arp46_t))) {
01127                         if (debug >= 2) {
01128                             printf("Sent ARP for %s\n", inet_ntoa(np->ip));
01129                         }
01130                     }
01131                     np->flags &= ~NF_CONFLICT;
01132                 }
01133 
01134                 if (type == DHCP_ACK && !(np->flags & NF_CONFLICT)) {
01135                     /* An acknowledgment.  The address is all mine. */
01136                     cachedhcp(np->n, bp->dhcp);
01137                     np->ip= bp->dhcp->yiaddr;
01138                     np->mask= mask;
01139                     np->server= server;
01140                     set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
01141                     if (debug >= 1) {
01142                         printf("%s: Address set to %s\n",
01143                             ipdev(np->n), cidr_ntoa(np->ip, np->mask));
01144                     }
01145                     if (lease >= NEVER - now) {
01146                         /* The lease is infinite! */
01147                         np->renew= np->rebind= np->lease= NEVER;
01148                     } else {
01149                         np->lease= now + lease;
01150                         np->renew= now + renew;
01151                         np->rebind= now + rebind;
01152                     }
01153                     if (test >= 3) {
01154                         np->renew= now + 60;
01155                         np->rebind= test >= 4 ? np->renew : np->renew + 60;
01156                         np->lease= test >= 5 ? np->rebind : np->rebind + 60;
01157                     }
01158                     if (!(np->flags & NF_IRDP)) {
01159                         np->sol_ct= (np->flags & NF_BOUND) ? 1 : N_SOLICITS;
01160                         np->solicit= 0;
01161                     }
01162                     np->flags &= ~NF_NEGOTIATING;
01163                     np->flags |= NF_BOUND;
01164                     closedev(np, FT_ETHERNET);
01165                     closedev(np, FT_BOOTPC);
01166                 }
01167 
01168                 if (type == DHCP_ACK && (np->flags & NF_CONFLICT)) {
01169                     /* Alas there is a conflict.  Decline to use the address. */
01170                     u8_t ethclid[1+DHCP_HLEN_ETH];
01171                     static char USED[]= "Address in use by 00:00:00:00:00:00";
01172 
01173                     type= DHCP_DECLINE;
01174                     dhcp_init(bp->dhcp);
01175                     bp->dhcp->op= DHCP_BOOTREQUEST;
01176                     bp->dhcp->htype= DHCP_HTYPE_ETH;
01177                     bp->dhcp->hlen= DHCP_HLEN_ETH;
01178                     bp->dhcp->xid= XID(np);
01179                     bp->dhcp->secs= 0;
01180                     memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
01181                     settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, sizeof(np->ip));
01182                     settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
01183                     ether2clid(ethclid, &np->eth);
01184                     settag(bp->dhcp, DHCP_TAG_CLIENTID,ethclid,sizeof(ethclid));
01185                     strcpy(USED+18, ether_ntoa(&np->conflict));
01186                     settag(bp->dhcp, DHCP_TAG_MESSAGE, USED, strlen(USED));
01187 
01188                     bp->udpio->uih_src_port= port_client;
01189                     bp->udpio->uih_dst_port= port_server;
01190                     bp->udpio->uih_ip_opt_len= 0;
01191                     bp->udpio->uih_data_len= sizeof(dhcp_t);
01192                     udp2ether(bp, np);
01193 
01194                     if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
01195                                         + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
01196                         if (debug >= 1) {
01197                             printf("%s: Broadcast DHCP %s\n",
01198                                 np->fdp->device, dhcptypename(type));
01199                             if (debug >= 2) printdhcp(bp->dhcp);
01200                         }
01201                     }
01202                     
01203                     np->renew= np->rebind= np->lease= now + DELTA_FAST;
01204                     np->delta= DELTA_FIRST;
01205                 }
01206 
01207                 if (type == DHCP_NAK) {
01208                     /* Oops, a DHCP server doesn't like me, start over! */
01209                     np->renew= np->rebind= np->lease= now + DELTA_FAST;
01210                     np->delta= DELTA_FIRST;
01211 
01212                     fprintf(stderr, "%s: Got a NAK from %s",
01213                         program, inet_ntoa(server));
01214                     if (relay != server) {
01215                         fprintf(stderr, " through %s", inet_ntoa(relay));
01216                     }
01217                     if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
01218                         fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
01219                     }
01220                     fputc('\n', stderr);
01221                 }
01222             } else
01223             if (!(np->flags & NF_BOUND)
01224                 && np->rebind > now
01225                 && r >= sizeof(arp46_t)
01226                 && is_arp_me(bp, np)
01227             ) {
01228                 /* Oh no, someone else is using the address offered to me! */
01229                 np->flags |= NF_CONFLICT;
01230 
01231                 fprintf(stderr, "%s: %s: %s offered by ",
01232                         program,
01233                         np->fdp->device,
01234                         inet_ntoa(np->ip));
01235                 fprintf(stderr, "%s is already in use by %s\n",
01236                         inet_ntoa(np->server),
01237                         ether_ntoa(&np->conflict));
01238             }
01239             put_buf(&bp);
01240             if (np->renew < event) event= np->renew;
01241         }
01242 
01243         /* Perform router solicitations. */
01244         for (i= 0; i < n_nets; i++) {
01245             np= network[i];
01246             if (!(np->flags & NF_BOUND)) continue;
01247 
01248             if (np->solicit <= now) {
01249                 if (!opendev(np, FT_ICMP, 1)) continue;
01250                 np->solicit= NEVER;
01251 
01252                 get_buf(&bp);
01253                 if (np->gateway != 0) {
01254                     /* No IRDP response seen yet, advertise the router given
01255                      * by DHCP to my own interface.
01256                      */
01257                     icmp_advert(bp, np);
01258                     if (send(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
01259                         if (debug >= 2) {
01260                             printf("%s: Sent advert for %s to self\n",
01261                                 np->fdp->device, inet_ntoa(np->gateway));
01262                         }
01263                     }
01264                     np->solicit= now + DELTA_ADV/2;
01265                 }
01266 
01267                 if (np->sol_ct >= 0 && --np->sol_ct >= 0) {
01268                     /* Send a router solicitation. */
01269                     icmp_solicit(bp);
01270                     if (send(np, bp->ip, sizeof(*bp->ip) + 8)) {
01271                         if (debug >= 2) {
01272                             printf("%s: Broadcast router solicitation\n",
01273                                 np->fdp->device);
01274                         }
01275                     }
01276                     np->solicit= now + DELTA_SOL;
01277                 } else {
01278                     /* No response, or not soliciting right now. */
01279                     closedev(np, FT_ICMP);
01280                 }
01281 
01282                 put_buf(&bp);
01283             }
01284             if (np->solicit < event) event= np->solicit;
01285         }
01286 
01287         /* Read router adverts. */
01288         for (i= 0; i < n_nets; i++) {
01289             np= network[i];
01290             if (!(np->flags & NF_BOUND)) continue;
01291             if (np->sol_ct < 0) continue;
01292 
01293             if (!opendev(np, FT_ICMP, 0)) continue;
01294             get_buf(&np->fdp->bp);
01295             r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->ip, BUF_IP_SIZE);
01296             if (r != -1) break;
01297             if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
01298                 report(np->fdp->device);
01299                 sleep(10);
01300             }
01301         }
01302 
01303         /* Is there an advert? */
01304         if (i < n_nets && r >= sizeof(ip_hdr_t) + 8) {
01305             ipaddr_t router;
01306 
01307             give_buf(&bp, &np->fdp->bp);
01308             if ((router= icmp_is_advert(bp)) != 0) {
01309                 if (debug >= 2) {
01310                     printf("%s: Router advert received from %s\n",
01311                         np->fdp->device, inet_ntoa(router));
01312                 }
01313                 np->solicit= NEVER;
01314                 np->sol_ct= -1;
01315                 np->flags |= NF_IRDP;
01316                 closedev(np, FT_ICMP);
01317             }
01318             put_buf(&bp);
01319         }
01320 
01321         /* We start serving if all the interfaces so marked are configured. */
01322         for (i= 0; i < n_nets; i++) {
01323             np= network[i];
01324             if ((np->flags & NF_RELAYING) && (np->flags & NF_BOUND)) {
01325                 if (((np->ip ^ np->server) & np->mask) == 0) {
01326                     /* Don't relay to a server that is on this same net. */
01327                     np->flags &= ~NF_RELAYING;
01328                 }
01329             }
01330             if (!(np->flags & (NF_SERVING|NF_RELAYING))) continue;
01331             if (!(np->flags & NF_BOUND)) { serving= 0; break; }
01332             serving= 1;
01333         }
01334 
01335         /* Read DHCP requests. */
01336         for (i= 0; i < n_nets; i++) {
01337             np= network[i];
01338             if (!(np->flags & NF_BOUND)) continue;
01339             if (!(np->flags & (NF_SERVING|NF_RELAYING)) || !serving) continue;
01340 
01341             if (!opendev(np, FT_BOOTPS, 0)) continue;
01342             get_buf(&np->fdp->bp);
01343             r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, BUF_UDP_SIZE);
01344 
01345             if (r != -1) break;
01346             if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
01347                 report(np->fdp->device);
01348                 sleep(10);
01349             }
01350         }
01351 
01352         /* Is there a request? */
01353         if (i < n_nets
01354             && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)
01355         ) {
01356             give_buf(&bp, &np->fdp->bp);
01357 
01358             if (debug >= 1) {
01359                 printf("%s: Got DHCP packet from %s to ",
01360                     np->fdp->device, inet_ntoa(bp->udpio->uih_src_addr));
01361                 printf("%s\n", inet_ntoa(bp->udpio->uih_dst_addr));
01362                 if (debug >= 2) printdhcp(bp->dhcp);
01363             }
01364 
01365             /* Can we do something with this DHCP packet? */
01366             if ((r= servdhcp(np, bp, r)) > 0) {
01367                 /* Yes, we have something to send somewhere. */
01368                 if (send(np, bp->udpio, r)) {
01369                     if (debug >= 1) {
01370                         printf("%s: Sent DHCP packet to %s\n",
01371                             np->fdp->device,
01372                             inet_ntoa(bp->udpio->uih_dst_addr));
01373                         if (debug >= 2) printdhcp(bp->dhcp);
01374                     }
01375                 }
01376             }
01377             put_buf(&bp);
01378         }
01379 
01380         if (debug >= 1) {
01381             static char *lastbrk;
01382             extern char _end;
01383 
01384             if (sbrk(0) != lastbrk) {
01385                 lastbrk= sbrk(0);
01386                 printf("Memory use = %lu\n",
01387                     (unsigned long) (lastbrk - &_end));
01388             }
01389             fflush(stdout);
01390         }
01391 
01392         /* Bail out if not a server, and there is nothing else to do ever. */
01393         if (!serving && event == NEVER) break;
01394 
01395         /* Wait for something to do. */
01396         eventtv.tv_sec= event;
01397         if (asyn_wait(&asyn, 0, event == NEVER ? nil : &eventtv) < 0) {
01398             if (errno != EINTR) {
01399                 report("asyn_wait()");
01400                 sleep(10);
01401             }
01402         }
01403     }
01404     if (debug >= 1) printf("Nothing more to do! Bailing out...\n");
01405     return 0;
01406 }

Generated on Fri Apr 14 22:56:47 2006 for minix by  doxygen 1.4.6