/* * BOOTP packet protocol handling. */ #include #include #include #include #include #include #include #include #include "ipconfig.h" #include "netdev.h" #include "bootp_packet.h" #include "bootp_proto.h" #include "packet.h" static uint8_t bootp_options[312] = { [ 0] = 99, 130, 83, 99,/* RFC1048 magic cookie */ [ 4] = 1, 4, /* 4- 9 subnet mask */ [ 10] = 3, 4, /* 10- 15 default gateway */ [ 16] = 5, 8, /* 16- 25 nameserver */ [ 26] = 12, 32, /* 26- 59 host name */ [ 60] = 40, 32, /* 60- 95 nis domain name */ [ 96] = 17, 40, /* 96-137 boot path */ [138] = 57, 2, 1, 150, /* 138-141 extension buffer */ [142] = 255, /* end of list */ }; /* * Send a plain bootp request packet with options */ int bootp_send_request(struct netdev *dev) { struct bootp_hdr bootp; struct iovec iov[] = { /* [0] = ip + udp headers */ [1] = {&bootp, sizeof(bootp)}, [2] = {bootp_options, 312} }; memset(&bootp, 0, sizeof(struct bootp_hdr)); bootp.op = BOOTP_REQUEST, bootp.htype = dev->hwtype; bootp.hlen = dev->hwlen; bootp.xid = dev->bootp.xid; bootp.ciaddr = dev->ip_addr; bootp.secs = htons(time(NULL) - dev->open_time); memcpy(bootp.chaddr, dev->hwaddr, 16); dprintf("-> bootp xid 0x%08x secs 0x%08x ", bootp.xid, ntohs(bootp.secs)); return packet_send(dev, iov, 2); } /* * Parse a bootp reply packet */ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr, uint8_t *exts, int extlen) { dev->bootp.gateway = hdr->giaddr; dev->ip_addr = hdr->yiaddr; dev->ip_server = hdr->siaddr; dev->ip_netmask = INADDR_ANY; dev->ip_broadcast = INADDR_ANY; dev->ip_gateway = hdr->giaddr; dev->ip_nameserver[0] = INADDR_ANY; dev->ip_nameserver[1] = INADDR_ANY; dev->hostname[0] = '\0'; dev->nisdomainname[0] = '\0'; dev->bootpath[0] = '\0'; memcpy(&dev->filename, &hdr->boot_file, FNLEN); if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 && exts[2] == 83 && exts[3] == 99) { uint8_t *ext; for (ext = exts + 4; ext - exts < extlen;) { int len; uint8_t opt = *ext++; if (opt == 0) continue; else if (opt == 255) break; len = *ext++; switch (opt) { case 1: /* subnet mask */ if (len == 4) memcpy(&dev->ip_netmask, ext, 4); break; case 3: /* default gateway */ if (len >= 4) memcpy(&dev->ip_gateway, ext, 4); break; case 6: /* DNS server */ if (len >= 4) memcpy(&dev->ip_nameserver, ext, len >= 8 ? 8 : 4); break; case 12: /* host name */ if (len > sizeof(dev->hostname) - 1) len = sizeof(dev->hostname) - 1; memcpy(&dev->hostname, ext, len); dev->hostname[len] = '\0'; break; case 15: /* domain name */ if (len > sizeof(dev->dnsdomainname) - 1) len = sizeof(dev->dnsdomainname) - 1; memcpy(&dev->dnsdomainname, ext, len); dev->dnsdomainname[len] = '\0'; break; case 17: /* root path */ if (len > sizeof(dev->bootpath) - 1) len = sizeof(dev->bootpath) - 1; memcpy(&dev->bootpath, ext, len); dev->bootpath[len] = '\0'; break; case 26: /* interface MTU */ if (len == 2) dev->mtu = (ext[0] << 8) + ext[1]; break; case 28: /* broadcast addr */ if (len == 4) memcpy(&dev->ip_broadcast, ext, 4); break; case 40: /* NIS domain name */ if (len > sizeof(dev->nisdomainname) - 1) len = sizeof(dev->nisdomainname) - 1; memcpy(&dev->nisdomainname, ext, len); dev->nisdomainname[len] = '\0'; break; case 54: /* server identifier */ if (len == 4 && !dev->ip_server) memcpy(&dev->ip_server, ext, 4); break; } ext += len; } } /* * Got packet. */ return 1; } /* * Receive a bootp reply and parse packet * Returns: *-1 = Error in packet_recv, try again later * 0 = Unexpected packet, discarded * 1 = Correctly received and parsed packet */ int bootp_recv_reply(struct netdev *dev) { struct bootp_hdr bootp; uint8_t bootp_options[312]; struct iovec iov[] = { /* [0] = ip + udp headers */ [1] = {&bootp, sizeof(struct bootp_hdr)}, [2] = {bootp_options, 312} }; int ret; ret = packet_recv(iov, 3); if (ret <= 0) return -1; if (ret < sizeof(struct bootp_hdr) || bootp.op != BOOTP_REPLY || /* RFC951 7.5 */ bootp.xid != dev->bootp.xid || memcmp(bootp.chaddr, dev->hwaddr, 16)) return 0; ret -= sizeof(struct bootp_hdr); return bootp_parse(dev, &bootp, bootp_options, ret); } /* * Initialise interface for bootp. */ int bootp_init_if(struct netdev *dev) { short flags; /* * Get the device flags */ if (netdev_getflags(dev, &flags)) return -1; /* * We can't do DHCP nor BOOTP if this device * doesn't support broadcast. */ if (dev->mtu < 364 || (flags & IFF_BROADCAST) == 0) { dev->caps &= ~(CAP_BOOTP | CAP_DHCP); return 0; } /* * Get a random XID */ dev->bootp.xid = (uint32_t) lrand48(); dev->open_time = time(NULL); return 0; }