/* * DHCP RFC 2131 and 2132 */ #include #include #include #include #include #include #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 6 static struct iovec dhcp_discover_iov[] = { /* [0] = ip + udp header */ /* [1] = bootp header */ [2] = {dhcp_discover_hdr, sizeof(dhcp_discover_hdr)}, [3] = {dhcp_params, sizeof(dhcp_params)}, /* [4] = DHCP vendor class */ [5] = {dhcp_end, sizeof(dhcp_end)} }; static struct iovec dhcp_request_iov[] = { /* [0] = ip + udp header */ /* [1] = bootp header */ [2] = {dhcp_request_hdr, sizeof(dhcp_request_hdr)}, [3] = {dhcp_params, sizeof(dhcp_params)}, /* [4] = DHCP vendor class */ [5] = {dhcp_end, sizeof(dhcp_end)} }; /* * Parse a DHCP response packet */ 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; 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;) { uint8_t len, *opt = ext++; if (*opt == 0) continue; len = *ext++; ext += len; if (*opt == 53) type = opt[2]; if (*opt == 54) memcpy(&serverid, opt + 2, 4); } } switch (type) { case DHCPOFFER: ret = bootp_parse(dev, hdr, exts, extlen); if (ret == 1 && serverid != INADDR_NONE) dev->serverid = serverid; DEBUG(("\n dhcp offer\n")); break; case DHCPACK: ret = bootp_parse(dev, hdr, exts, extlen); DEBUG(("\n dhcp ack\n")); break; case DHCPNAK: ret = 2; DEBUG(("\n dhcp nak\n")); break; } return ret; } /* * Receive and parse a DHCP packet */ 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(iov, 3); if (ret <= 0) return ret; DEBUG(("\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; 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.giaddr = dev->bootp.gateway; 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); vec[4].iov_base = vendor_class_identifier; vec[4].iov_len = vendor_class_identifier_len; DEBUG(("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs))); return packet_send(dev, vec, DHCP_IOV_LEN); } /* * Send a DHCP discover packet */ int dhcp_send_discover(struct netdev *dev) { dev->ip_addr = INADDR_ANY; dev->ip_gateway = INADDR_ANY; DEBUG(("-> 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); DEBUG(("-> 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); }