| /* |
| * DHCP RFC 2131 and 2132 |
| */ |
| #include <sys/types.h> |
| #include <sys/uio.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include "ipconfig.h" |
| #include "netdev.h" |
| #include "bootp_packet.h" |
| #include "bootp_proto.h" |
| #include "dhcp_proto.h" |
| #include "packet.h" |
| |
| static uint8_t dhcp_params[] = { |
| 1, /* subnet mask */ |
| 3, /* default gateway */ |
| 6, /* DNS server */ |
| 12, /* host name */ |
| 15, /* domain name */ |
| 17, /* root path */ |
| 26, /* interface mtu */ |
| 28, /* broadcast addr */ |
| 40, /* NIS domain name (why?) */ |
| }; |
| |
| static uint8_t dhcp_discover_hdr[] = { |
| 99, 130, 83, 99, /* bootp cookie */ |
| 53, 1, DHCPDISCOVER, /* dhcp message type */ |
| 55, sizeof(dhcp_params), /* parameter list */ |
| }; |
| |
| static uint8_t dhcp_request_hdr[] = { |
| 99, 130, 83, 99, /* boot cookie */ |
| 53, 1, DHCPREQUEST, /* dhcp message type */ |
| #define SERVER_IP_OFF 9 |
| 54, 4, 0, 0, 0, 0, /* server IP */ |
| #define REQ_IP_OFF 15 |
| 50, 4, 0, 0, 0, 0, /* requested IP address */ |
| 55, sizeof(dhcp_params), /* parameter list */ |
| }; |
| |
| static uint8_t dhcp_end[] = { |
| 255, |
| }; |
| |
| /* Both iovecs below have to have the same structure, since dhcp_send() |
| pokes at the internals */ |
| #define DHCP_IOV_LEN 7 |
| |
| static struct iovec dhcp_discover_iov[DHCP_IOV_LEN] = { |
| /* [0] = ip + udp header */ |
| /* [1] = bootp header */ |
| [2] = {dhcp_discover_hdr, sizeof(dhcp_discover_hdr)}, |
| [3] = {dhcp_params, sizeof(dhcp_params)}, |
| /* [4] = optional vendor class */ |
| /* [5] = optional hostname */ |
| /* [6] = {dhcp_end, sizeof(dhcp_end)} */ |
| }; |
| |
| static struct iovec dhcp_request_iov[DHCP_IOV_LEN] = { |
| /* [0] = ip + udp header */ |
| /* [1] = bootp header */ |
| [2] = {dhcp_request_hdr, sizeof(dhcp_request_hdr)}, |
| [3] = {dhcp_params, sizeof(dhcp_params)}, |
| /* [4] = optional vendor class */ |
| /* [5] = optional hostname */ |
| /* [6] = {dhcp_end, sizeof(dhcp_end)} */ |
| }; |
| |
| /* |
| * Parse a DHCP response packet |
| * Returns: |
| * 0 = Unexpected packet, not parsed |
| * 2 = DHCPOFFER (from dhcp_proto.h) |
| * 5 = DHCPACK |
| * 6 = DHCPNACK |
| */ |
| static int dhcp_parse(struct netdev *dev, struct bootp_hdr *hdr, |
| uint8_t *exts, int extlen) |
| { |
| uint8_t type = 0; |
| uint32_t serverid = INADDR_NONE; |
| uint32_t leasetime = 0; |
| int ret = 0; |
| |
| 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; |
| |
| if (ext - exts >= extlen) |
| break; |
| len = *ext++; |
| |
| if (ext - exts + len > extlen) |
| break; |
| switch (opt) { |
| case 51: /* IP Address Lease Time */ |
| if (len == 4) |
| leasetime = ntohl(*(uint32_t *)ext); |
| break; |
| case 53: /* DHCP Message Type */ |
| if (len == 1) |
| type = *ext; |
| break; |
| case 54: /* Server Identifier */ |
| if (len == 4) |
| memcpy(&serverid, ext, 4); |
| break; |
| } |
| ext += len; |
| } |
| } |
| |
| switch (type) { |
| case DHCPOFFER: |
| ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPOFFER : 0; |
| if (ret == DHCPOFFER && serverid != INADDR_NONE) |
| dev->serverid = serverid; |
| dprintf("\n dhcp offer\n"); |
| break; |
| |
| case DHCPACK: |
| dev->dhcpleasetime = leasetime; |
| ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPACK : 0; |
| dprintf("\n dhcp ack\n"); |
| break; |
| |
| case DHCPNAK: |
| ret = DHCPNAK; |
| dprintf("\n dhcp nak\n"); |
| break; |
| } |
| return ret; |
| } |
| |
| /* |
| * Receive and parse a DHCP packet |
| * Returns: |
| *-1 = Error in packet_recv, try again later |
| * 0 = Unexpected packet, discarded |
| * 2 = DHCPOFFER (from dhcp_proto.h) |
| * 5 = DHCPACK |
| * 6 = DHCPNACK |
| */ |
| static int dhcp_recv(struct netdev *dev) |
| { |
| struct bootp_hdr bootp; |
| uint8_t dhcp_options[1500]; |
| struct iovec iov[] = { |
| /* [0] = ip + udp header */ |
| [1] = {&bootp, sizeof(struct bootp_hdr)}, |
| [2] = {dhcp_options, sizeof(dhcp_options)} |
| }; |
| int ret; |
| |
| ret = packet_recv(dev, iov, 3); |
| if (ret <= 0) |
| return ret; |
| |
| dprintf("\n dhcp xid %08x ", dev->bootp.xid); |
| |
| 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 dhcp_parse(dev, &bootp, dhcp_options, ret); |
| } |
| |
| static int dhcp_send(struct netdev *dev, struct iovec *vec) |
| { |
| struct bootp_hdr bootp; |
| char dhcp_hostname[SYS_NMLN+2]; |
| int i = 4; |
| |
| 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 = INADDR_ANY; |
| bootp.yiaddr = dev->ip_addr; |
| bootp.giaddr = INADDR_ANY; |
| bootp.secs = htons(time(NULL) - dev->open_time); |
| memcpy(bootp.chaddr, dev->hwaddr, 16); |
| |
| vec[1].iov_base = &bootp; |
| vec[1].iov_len = sizeof(struct bootp_hdr); |
| |
| dprintf("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs)); |
| |
| if (vendor_class_identifier_len > 2) { |
| vec[i].iov_base = vendor_class_identifier; |
| vec[i].iov_len = vendor_class_identifier_len; |
| i++; |
| |
| dprintf("vendor_class_identifier \"%.*s\" ", |
| vendor_class_identifier_len-2, |
| vendor_class_identifier+2); |
| } |
| |
| if (dev->reqhostname[0] != '\0') { |
| int len = strlen(dev->reqhostname); |
| dhcp_hostname[0] = 12; |
| dhcp_hostname[1] = len; |
| memcpy(dhcp_hostname+2, dev->reqhostname, len); |
| |
| vec[i].iov_base = dhcp_hostname; |
| vec[i].iov_len = len+2; |
| i++; |
| |
| printf("hostname %.*s ", len, dhcp_hostname+2); |
| } |
| |
| vec[i].iov_base = dhcp_end; |
| vec[i].iov_len = sizeof(dhcp_end); |
| |
| return packet_send(dev, vec, i + 1); |
| } |
| |
| /* |
| * Send a DHCP discover packet |
| */ |
| int dhcp_send_discover(struct netdev *dev) |
| { |
| dev->ip_addr = INADDR_ANY; |
| dev->ip_gateway = INADDR_ANY; |
| |
| dprintf("-> dhcp discover "); |
| |
| return dhcp_send(dev, dhcp_discover_iov); |
| } |
| |
| /* |
| * Receive a DHCP offer packet |
| */ |
| int dhcp_recv_offer(struct netdev *dev) |
| { |
| return dhcp_recv(dev); |
| } |
| |
| /* |
| * Send a DHCP request packet |
| */ |
| int dhcp_send_request(struct netdev *dev) |
| { |
| memcpy(&dhcp_request_hdr[SERVER_IP_OFF], &dev->serverid, 4); |
| memcpy(&dhcp_request_hdr[REQ_IP_OFF], &dev->ip_addr, 4); |
| |
| dprintf("-> dhcp request "); |
| |
| return dhcp_send(dev, dhcp_request_iov); |
| } |
| |
| /* |
| * Receive a DHCP ack packet |
| */ |
| int dhcp_recv_ack(struct netdev *dev) |
| { |
| return dhcp_recv(dev); |
| } |