/* * Center for Information Technology Integration * The University of Michigan * Ann Arbor * * Dedicated to the public domain. * Send questions to info@citi.umich.edu * * BOOTP is documented in RFC 951 and RFC 1048 * Delinted, ANSIfied and reformatted - 5/30/91 P. Karn */ /* * BOOTP (bootstrap protocol) server daemon. * */ #include #include #include #include #include #include "global.h" #include "config.h" #include "bootp.h" #include "cmdparse.h" #include "iface.h" #include "mbuf.h" #include "proc.h" #include "socket.h" #include "bootpd.h" #include "udp.h" #include "ip.h" #include "arp.h" #include "netuser.h" void bootpd __ARGS((struct iface *iface, struct udp_cb *sock, int cnt)); struct udp_cb *Bootpd_cb = NULLUDP; char homedir[64] = ""; /* bootfile homedirectory */ char defaultboot[64] = ""; /* default file to boot */ struct host hosts[MHOSTS+1]; int Nhosts = 0; /* current number of hosts */ int32 bp_DefaultDomainNS[BP_MAXDNS] = {0}; struct bootpd_stat Bootpd_stat; char *ArpNames[] = { "Netrom", "Ethernet", "AX25", "Pronet", "Chaos", "IEEE 802", "ArcNet", "AppleTalk" }; /* Routine declarations */ static int bootpd_recv __ARGS((struct udp_cb *sock,struct bootp *bootp)); static void sendreply __ARGS((struct bootp *bp,struct iface *iface)); static void vend_fill __ARGS((char *vend,struct iface *iface,struct host *hp)); static void vend_print __ARGS((char *vend)); static void bootpd_request __ARGS((struct bootp *rq,struct iface *iface)); /* The bootp server. */ void bootpd(iface,sock,cnt) struct iface *iface; struct udp_cb *sock; int cnt; { struct bootp bp_packet; while(bootpd_recv(sock, &bp_packet) != -1) { if(readtab() == -1) /* maybe re-read bootptab */ return; switch(uchar(bp_packet.op)) { case BOOTREQUEST: bootpd_request(&bp_packet, iface); break; case BOOTREPLY: /* Replies are not forwarded, left to the gateway */ break; } } } /* A packet has been received, read it into a bootp structure. */ static int bootpd_recv(sock, bootp) struct udp_cb *sock; struct bootp *bootp; { struct socket fsock; struct mbuf *bp; int len; /* increment the rcvd cnt */ Bootpd_stat.rcvd++; /* receive the packet */ len = recv_udp(sock,&fsock,&bp); if(len == -1) return -1; /* check length of packet */ if(len < sizeof(struct bootp)) { Bootpd_stat.bad_size++; bootp->op = 0; free_p(bp); return -1; } /* parse the packet */ pullup (&bp, (char *)bootp, sizeof (struct bootp)); free_p(bp); if(bootp->op != BOOTREPLY && bootp->op != BOOTREQUEST) { Bootpd_stat.bad_op++; bootp->op = 0; return -1; } bootp->ciaddr.s_addr = (unsigned long) get32 ((char *)&(bootp->ciaddr)); bootp->giaddr.s_addr = (unsigned long) get32 ((char *)&(bootp->giaddr)); return 0; }; /* * Process BOOTREQUEST packet. * * (Note, this version of the bootp.c server never forwards * the request to another server. In our environment the * stand-alone gateways perform that function.) * * (Also this version does not interpret the hostname field of * the request packet; it COULD do a name->address lookup and * forward the request there.) */ static void bootpd_request (rq, iface) struct bootp *rq; struct iface *iface; { struct bootp *rp; char path[64], file[64]; struct host *hp; int n; time_t tloc; int32 ipaddr; struct arp_type *at; time(&tloc); bp_log ("\nBootpd request packet received %s", ctime(&tloc)); /* Forwarding not done here. */ if(rq->giaddr.s_addr) { bp_log (" Dropped, giaddr specifies to be forwarded;\n"); return; } /* Is a specific host requested? */ if((strlen(rq->sname) != 0) && (strcmp (Hostname, rq->sname) != 0)) { bp_log (" Dropped, sname specifies server '%s'\n", rq->sname); return; } /* allocate the reply */ rp = (struct bootp *) calloc (1, sizeof (struct bootp)); /* copy for construction */ memcpy (rp, rq, sizeof (struct bootp) - sizeof (rp->vend)); rp->op = BOOTREPLY; hp = NULLHOST; /* If the client doesn't know it's ip address, find one. */ if(rq->ciaddr.s_addr == 0) { /* * client doesn't know his IP address, * search by hardware address. */ at = &Arp_type[rq->htype]; bp_log (" Resolved by %s addr %s\n", ArpNames[rq->htype], (*at->format)(bp_ascii, rq->chaddr)); for(hp = &hosts[0], n = 0 ; n < Nhosts ; n++,hp++) if((rq->htype == hp->htype) && (strncmp(rq->chaddr, hp->haddr, rq->hlen) == 0)) break; /* If the client wasn't found, assign an IP address */ if(n == Nhosts) { hp = NULLHOST; if(da_assign(iface, rq->chaddr, &ipaddr) != 0) { free (rp); bp_log (" No dynamic addresses available.\n"); return; } else { put32 ((char *) &(rp->yiaddr), ipaddr); bp_log (" Dynamic address assigned: %s\n", inet_ntoa (ipaddr)); } } else { bp_log (" Static address assigned: %s\n", inet_ntoa (hp->iaddr.s_addr)); put32 ((char *) &(rp->yiaddr), hp->iaddr.s_addr); } } else { /* search by IP address */ bp_log (" Resolve by IP addr %s\n", inet_ntoa (rq->ciaddr.s_addr)); for(hp = &hosts[0], n = 0 ; n < Nhosts ; n++,hp++) if(rq->ciaddr.s_addr == hp->iaddr.s_addr) break; if(n == Nhosts) { hp = NULLHOST; bp_log (" Host not found, default values used.\n"); } else bp_log (" Lookup successful.\n"); put32 ((char *) &(rp->ciaddr), rq->ciaddr.s_addr); } put32 ((char *) &(rp->siaddr), iface->addr); /* Determine the bootp file */ file[0] = 0; if(rq->file[0] == 0) { /* if client didn't specify file */ /* Use the host record file, else the default file */ if((hp == NULLHOST) || (hp->bootfile[0] == 0)) strcpy(file, defaultboot); else strcpy(file, hp->bootfile); } else { /* use client specified file */ strcpy(file, rq->file); } /* If a file is specified, specify the path to the bootp file */ path[0] = 0; if((*homedir != 0) && (*file != 0)) { strcpy(path, homedir); strcat(path, "/"); } if(file[0] == '/') /* if absolute pathname */ strcpy(path, file); else strcat(path, file); /* No files are provided here, just return a path. */ strcpy(rp->file, path); /* Fill in the vendor information */ vend_fill (rp->vend, iface, hp); sendreply (rp, iface); free (rp); }; /* Print the bootp structure. */ void bootp_print_packet (bp) struct bootp *bp; { bp_log ("Packet op code........................%d\n", bp->op); bp_log ("hardware address type.................%d\n", bp->htype); bp_log ("hardware address length...............%d\n", bp->hlen); bp_log ("client sets to zero...................%d\n", bp->hops); bp_log ("transaction ID........................%ld\n", bp->xid); bp_log ("seconds elapsed since client booted...%d\n", bp->secs); bp_log ("unused................................%d\n", bp->unused); bp_log ("Client IP address, if known...........%s\n", inet_ntoa ((int32)bp->ciaddr.s_addr)); bp_log ("Server supplied IP address............%s\n", inet_ntoa ((int32)bp->yiaddr.s_addr)); bp_log ("Server IP address.....................%s\n", inet_ntoa ((int32)bp->siaddr.s_addr)); bp_log ("Gateway IP address....................%s\n", inet_ntoa ((int32)bp->giaddr.s_addr)); bp_log ("Client hardware address...............%x:%x:%x:%x:%x:%x\n", bp->chaddr[0], bp->chaddr[1], bp->chaddr[2], bp->chaddr[3], bp->chaddr[4], bp->chaddr[5]); bp_log ("Server host name......................'%s'\n", bp->sname); bp_log ("Boot file name........................'%s'\n", bp->file); vend_print (bp->vend); } static void vend_print (vend) char *vend; { unsigned char ch; int size; char *start; int32 *ipaddr; int i; start = vend; bp_log ("Magic Cookie..........................x%02x%02x%02x%02x\n", (int) vend[0], (int) vend[1], (int) vend[2], (int) vend[3]); vend = vend + 4; while(((ch = *vend++) != BOOTP_END) && (vend - start <= 64)) switch(ch) { case BOOTP_PAD: /* They're just padding */ continue; case BOOTP_SUBNET: /* fixed length, 4 octets */ size = (int) *vend++; ipaddr = (int32 *) vend; bp_log ("Vend Subnet...........................%s\n", inet_ntoa (*ipaddr)); vend += size; break; case BOOTP_HOSTNAME: size = (int) *vend++; bp_log ("Vend Hostname.........................%s\n", vend); vend += size; break; case BOOTP_DNS: size = (int) *vend++; for(i = 0; i < (size / 4); i++) { ipaddr = (int32 *) vend; bp_log ("Vend DomainNS.........................%s\n", inet_ntoa (*ipaddr)); vend += 4; } break; case BOOTP_GATEWAY: size = (int) *vend++; for(i = 0; i < (size / 4); i++) { ipaddr = (int32 *) vend; bp_log ("Vend Gateway..........................%s\n", inet_ntoa (*ipaddr)); vend += 4; } break; default: /* variable field we don't know about */ size = *vend++; vend += size; break; } } static void vend_fill (vend, iface, hp) char *vend; struct iface *iface; struct host *hp; { char cookie[5] = {99, 130, 83, 99, 0}; int len; int mod; int i; char *sizep; /* Magic cookie */ strcpy (vend, cookie); vend += 4; /* Send the iface subnet */ /* Pad so number falls on word boundry */ vend++; vend++; *vend = BOOTP_SUBNET; vend++; *vend = 4; vend++; put32 (vend, iface->netmask); vend += 4; /* Send the DNS */ if(bp_DefaultDomainNS[0] != 0) { /* Pad for allignment */ vend++; vend++; *vend = BOOTP_DNS; vend++; sizep = vend; vend++; for(i = 0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++) { put32 (vend, bp_DefaultDomainNS[i]); *sizep = *sizep + 4; vend += 4; } } /* Send the default gateway */ if(R_default.iface == iface) { vend++; vend++; *vend = BOOTP_GATEWAY; vend++; *vend = 4; vend++; put32 (vend, R_default.gateway); vend += 4; } /* Send the hostname */ if(hp != NULLHOST) { /* Pad so name begins on a word boundry */ vend++; vend++; *vend = BOOTP_HOSTNAME; vend++; *vend = len = strlen (hp->name) + 1; vend++; strcpy (vend, hp->name); vend += len; /* Pad to a word. */ mod = 4 - (len % 4); for(i = 0; i < mod; i++) { *vend = BOOTP_PAD; vend++; } } /* Mark the end of the data */ *vend = BOOTP_END; } /* * Send a reply packet to the client. 'forward' flag is set if we are * not the originator of this reply packet. */ static void sendreply(bp, iface) struct bootp *bp; struct iface *iface; { struct mbuf *buf; int32 faddr; int16 length; char *cp; /* * If the client IP address is specified, use that * else if gateway IP address is specified, use that * else make a temporary arp cache entry for the client's NEW * IP/hardware address and use that. */ if(bp->ciaddr.s_addr) { faddr = get32 ((char *) &(bp->ciaddr)); } else { faddr = get32 ((char *) &(bp->yiaddr)); arp_add (faddr, (int16) bp->htype, bp->chaddr, 0); } if((buf = qdata ((char *)bp, sizeof (struct bootp))) == NULLBUF) return; buf = pushdown (buf, UDPHDR); length = sizeof (struct bootp) + UDPHDR; cp = buf->data; cp = put16 (cp, IPPORT_BOOTPS); /* Source */ cp = put16 (cp, IPPORT_BOOTPC); /* Dest */ cp = put16 (cp, length); *cp++ = 0; *cp = 0; ip_send (iface->addr, faddr, UDP_PTCL, 0, 0, buf, length, 0, 0); };