/* * ipconfig/dhcp_proto.c * * DHCP RFC 2131 and 2132 */ #include #include /* for __u8 */ #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 __u8 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 __u8 dhcp_discover_hdr[] = { 99, 130, 83, 99, /* bootp cookie */ 53, 1, DHCPDISCOVER, /* dhcp message type */ 55, sizeof(dhcp_params),/* parameter list */ }; static __u8 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 __u8 dhcp_end[] = { 255, }; 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_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_end, sizeof(dhcp_end) } }; /* * Parse a DHCP response packet */ static int dhcp_parse(struct netdev *dev, struct bootp_hdr *hdr, __u8 *exts, int extlen) { __u8 type = 0; __u32 serverid = INADDR_NONE; int ret = 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++; 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; __u8 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, int len) { 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); DEBUG(("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs))); return packet_send(dev, vec, 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, 5); } /* * 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, 5); } /* * Receive a DHCP ack packet */ int dhcp_recv_ack(struct netdev *dev) { return dhcp_recv(dev); }