| /* |
| * ipconfig/bootp_proto.c |
| * |
| * BOOTP packet protocol handling. |
| */ |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <netinet/in.h> |
| |
| #include "ipconfig.h" |
| #include "netdev.h" |
| #include "bootp_packet.h" |
| #include "bootp_proto.h" |
| #include "packet.h" |
| |
| static __u8 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); |
| |
| DEBUG(("-> 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, __u8 *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'; |
| |
| if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 && |
| exts[2] == 83 && exts[3] == 99) { |
| __u8 *ext; |
| |
| for (ext = exts + 4; ext - exts < extlen; ) { |
| __u8 len, opt = *ext++; |
| if (opt == 0) |
| continue; |
| |
| len = *ext++; |
| |
| switch (opt) { |
| case 1: /* subnet mask */ |
| if (len > 4) |
| len = 4; |
| memcpy(&dev->ip_netmask, ext, len); |
| break; |
| case 3: /* default gateway */ |
| if (len > 4) |
| len = 4; |
| memcpy(&dev->ip_gateway, ext, len); |
| break; |
| case 6: /* DNS server */ |
| if (len > 8) |
| len = 8; |
| memcpy(&dev->ip_nameserver, ext, len); |
| 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 28: /* broadcast addr */ |
| if (len > 4) |
| len = 4; |
| memcpy(&dev->ip_broadcast, ext, len); |
| 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; |
| } |
| |
| ext += len; |
| } |
| } |
| |
| /* |
| * Got packet. |
| */ |
| return 1; |
| } |
| |
| /* |
| * Receive a bootp reply and parse packet |
| */ |
| int bootp_recv_reply(struct netdev *dev) |
| { |
| struct bootp_hdr bootp; |
| __u8 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 ret; |
| |
| 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 = (__u32)lrand48(); |
| dev->open_time = time(NULL); |
| |
| return 0; |
| } |