/* * Copyright 2017, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <linux/if_ether.h> #include <netinet/in.h> #include <stddef.h> #include <string.h> #include <initializer_list> class Message { public: Message(); Message(const uint8_t* data, size_t size); static Message discover(const uint8_t (&sourceMac)[ETH_ALEN]); static Message request(const uint8_t (&sourceMac)[ETH_ALEN], in_addr_t requestAddress, in_addr_t serverAddress); static Message offer(const Message& sourceMessage, in_addr_t serverAddress, in_addr_t offeredAddress, in_addr_t offeredNetmask, in_addr_t offeredGateway, const in_addr_t* offeredDnsServers, size_t numOfferedDnsServers); static Message ack(const Message& sourceMessage, in_addr_t serverAddress, in_addr_t offeredAddress, in_addr_t offeredNetmask, in_addr_t offeredGateway, const in_addr_t* offeredDnsServers, size_t numOfferedDnsServers); static Message nack(const Message& sourceMessage, in_addr_t serverAddress); // Ensure that the data in the message represent a valid DHCP message bool isValidDhcpMessage(uint8_t expectedOp) const; // Ensure that the data in the message represent a valid DHCP message and // has a xid (transaction ID) that matches |expectedXid|. bool isValidDhcpMessage(uint8_t expectedOp, uint32_t expectedXid) const; const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(&dhcpData); } uint8_t* data() { return reinterpret_cast<uint8_t*>(&dhcpData); } const uint8_t* end() const { return data() + mSize; } size_t optionsSize() const; size_t size() const { return mSize; } void setSize(size_t size) { mSize = size; } size_t capacity() const { return sizeof(dhcpData); } // Get the DHCP message type uint8_t type() const; // Get the DHCP server ID in_addr_t serverId() const; // Get the requested IP in_addr_t requestedIp() const; struct Dhcp { uint8_t op; /* BOOTREQUEST / BOOTREPLY */ uint8_t htype; /* hw addr type */ uint8_t hlen; /* hw addr len */ uint8_t hops; /* client set to 0 */ uint32_t xid; /* transaction id */ uint16_t secs; /* seconds since start of acq */ uint16_t flags; uint32_t ciaddr; /* client IP addr */ uint32_t yiaddr; /* your (client) IP addr */ uint32_t siaddr; /* ip addr of next server */ /* (DHCPOFFER and DHCPACK) */ uint32_t giaddr; /* relay agent IP addr */ uint8_t chaddr[16]; /* client hw addr */ char sname[64]; /* asciiz server hostname */ char file[128]; /* asciiz boot file name */ uint8_t options[1024]; /* optional parameters */ } dhcpData; private: Message(uint8_t operation, const uint8_t (&macAddress)[ETH_ALEN], uint8_t type); void addOption(uint8_t type, const void* data, uint8_t size); template<typename T> void addOption(uint8_t type, T data) { static_assert(sizeof(T) <= 255, "The size of data is too large"); addOption(type, &data, sizeof(data)); } template<typename T, size_t N> void addOption(uint8_t type, T (&items)[N]) { static_assert(sizeof(T) * N <= 255, "The size of data is too large"); uint8_t* opts = nextOption(); *opts++ = type; *opts++ = sizeof(T) * N; for (const T& item : items) { memcpy(opts, &item, sizeof(item)); opts += sizeof(item); } updateSize(opts); } void endOptions(); const uint8_t* getOption(uint8_t optCode, uint8_t* length) const; uint8_t* nextOption(); void updateSize(uint8_t* optionsEnd); size_t mSize; }; static_assert(offsetof(Message::Dhcp, htype) == sizeof(Message::Dhcp::op), "Invalid packing for DHCP message struct");