/* * Copyright 2014 Cloudius Systems */ #include #include #include #include #include "dhcp.hh" #include "ip.hh" #include "udp.hh" using namespace std::literals::chrono_literals; class net::dhcp::impl : public ip_packet_filter { public: decltype(std::cout) & log() { return std::cout << "DHCP "; } enum class state { NONE, DISCOVER, REQUEST, DONE, FAIL, }; enum class m_type : uint8_t { BOOTREQUEST = 1, BOOTREPLY = 2 }; enum class htype : uint8_t { ETHERNET = 1 }; enum class opt_type : uint8_t { PAD = 0, SUBNET_MASK = 1, ROUTER = 3, DOMAIN_NAME_SERVERS = 6, INTERFACE_MTU = 26, BROADCAST_ADDRESS = 28, REQUESTED_ADDRESS = 50, LEASE_TIME = 51, MESSAGE_TYPE = 53, DHCP_SERVER = 54, PARAMETER_REQUEST_LIST = 55, RENEWAL_TIME = 58, REBINDING_TIME = 59, CLASSLESS_ROUTE = 121, END = 255 }; enum class msg_type : uint8_t { DISCOVER = 1, OFFER = 2, REQUEST = 3, DECLINE = 4, ACK = 5, NAK = 6, RELEASE = 7, INFORM = 8, LEASEQUERY = 10, LEASEUNASSIGNED = 11, LEASEUNKNOWN = 12, LEASEACTIVE = 13, INVALID = 255 }; struct dhcp_header { m_type op = m_type::BOOTREQUEST; // Message op code / message type. htype type = htype::ETHERNET; // Hardware address type uint8_t hlen = 6; // Hardware address length uint8_t hops = 0; // Client sets to zero, used by relay agents packed xid = 0; // Client sets Transaction ID, a random number packed secs = 0; // Client sets seconds elapsed since op start packed flags = 0; // Flags ipv4_address ciaddr; // Client IP address ipv4_address yiaddr; // 'your' (client) IP address. ipv4_address siaddr; // IP address of next server to use in bootstrap ipv4_address giaddr; // Relay agent IP address uint8_t chaddr[16] = { 0, }; // Client hardware address. char sname[64] = { 0, }; // unused char file[128] = { 0, }; // unused template auto adjust_endianness(Adjuster a) { return a(xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr); } } __attribute__((packed)); typedef std::array req_opt_type; static const req_opt_type requested_options; struct option_mark { option_mark(opt_type t = opt_type::END) : type(t) {}; opt_type type; } __attribute__((packed)); struct option : public option_mark { option(opt_type t, uint8_t l = 1) : option_mark(t), len(l) {}; uint8_t len; } __attribute__((packed)); struct type_option : public option { type_option(msg_type t) : option(opt_type::MESSAGE_TYPE), type(t) {} msg_type type; } __attribute__((packed)); struct mtu_option : public option { mtu_option(uint16_t v) : option(opt_type::INTERFACE_MTU, 2), mtu((::htons)(v)) {} packed mtu; } __attribute__((packed)); struct ip_option : public option { ip_option(opt_type t = opt_type::BROADCAST_ADDRESS, const ipv4_address & ip = ipv4_address()) : option(t, sizeof(uint32_t)), ip(::htonl(ip.ip)) {} packed ip; } __attribute__((packed)); struct time_option : public option { time_option(opt_type t, uint32_t v) : option(t, sizeof(uint32_t)), time(::htonl(v)) {} packed time; } __attribute__((packed)); struct requested_option: public option { requested_option() : option(opt_type::PARAMETER_REQUEST_LIST, uint8_t(requested_options.size())), req( requested_options) { } req_opt_type req; }__attribute__((packed)); static const uint16_t client_port = 68; static const uint16_t server_port = 67; typedef std::array magic_tag; static const magic_tag options_magic; struct dhcp_payload { dhcp_header bootp; magic_tag magic = options_magic; template auto adjust_endianness(Adjuster a) { return a(bootp); } } __attribute__((packed)); struct dhcp_packet_base { ip_hdr ip; udp_hdr udp; dhcp_payload dhp; template auto adjust_endianness(Adjuster a) { return a(ip, udp, dhp); } } __attribute__((packed)); struct ip_info : public lease { msg_type type = msg_type(); void set(opt_type type, const ipv4_address & ip) { switch (type) { case opt_type::SUBNET_MASK: netmask = ip; break; case opt_type::ROUTER: gateway = ip; break; case opt_type::BROADCAST_ADDRESS: broadcast = ip; break; case opt_type::DHCP_SERVER: dhcp_server = ip; break; case opt_type::DOMAIN_NAME_SERVERS: name_servers.emplace_back(ip); break; default: break; } } void set(opt_type type, std::chrono::seconds s) { switch (type) { case opt_type::LEASE_TIME: lease_time = s; break; case opt_type::RENEWAL_TIME: renew_time = s; break; case opt_type::REBINDING_TIME: rebind_time = s; break; default: break; } } void parse_options(packet & p, size_t off) { for (;;) { auto * m = p.get_header(off); if (m == nullptr || m->type == opt_type::END) { break; } auto * o = p.get_header