1 /*
2  * Copyright 2017, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <linux/if_ether.h>
19 #include <netinet/in.h>
20 #include <stddef.h>
21 #include <string.h>
22 
23 #include <initializer_list>
24 
25 class Message {
26 public:
27     Message();
28     Message(const uint8_t* data, size_t size);
29     static Message discover(const uint8_t (&sourceMac)[ETH_ALEN]);
30     static Message request(const uint8_t (&sourceMac)[ETH_ALEN],
31                            in_addr_t requestAddress,
32                            in_addr_t serverAddress);
33     static Message offer(const Message& sourceMessage,
34                          in_addr_t serverAddress,
35                          in_addr_t offeredAddress,
36                          in_addr_t offeredNetmask,
37                          in_addr_t offeredGateway,
38                          const in_addr_t* offeredDnsServers,
39                          size_t numOfferedDnsServers);
40     static Message ack(const Message& sourceMessage,
41                        in_addr_t serverAddress,
42                        in_addr_t offeredAddress,
43                        in_addr_t offeredNetmask,
44                        in_addr_t offeredGateway,
45                        const in_addr_t* offeredDnsServers,
46                        size_t numOfferedDnsServers);
47     static Message nack(const Message& sourceMessage, in_addr_t serverAddress);
48 
49     // Ensure that the data in the message represent a valid DHCP message
50     bool isValidDhcpMessage(uint8_t expectedOp) const;
51     // Ensure that the data in the message represent a valid DHCP message and
52     // has a xid (transaction ID) that matches |expectedXid|.
53     bool isValidDhcpMessage(uint8_t expectedOp, uint32_t expectedXid) const;
54 
data()55     const uint8_t* data() const {
56         return reinterpret_cast<const uint8_t*>(&dhcpData);
57     }
data()58     uint8_t* data() {
59         return reinterpret_cast<uint8_t*>(&dhcpData);
60     }
end()61     const uint8_t* end() const { return data() + mSize; }
62 
63     size_t optionsSize() const;
size()64     size_t size() const { return mSize; }
setSize(size_t size)65     void setSize(size_t size) { mSize = size; }
capacity()66     size_t capacity() const { return sizeof(dhcpData); }
67 
68     // Get the DHCP message type
69     uint8_t type() const;
70     // Get the DHCP server ID
71     in_addr_t serverId() const;
72     // Get the requested IP
73     in_addr_t requestedIp() const;
74 
75     struct Dhcp {
76         uint8_t op;           /* BOOTREQUEST / BOOTREPLY    */
77         uint8_t htype;        /* hw addr type               */
78         uint8_t hlen;         /* hw addr len                */
79         uint8_t hops;         /* client set to 0            */
80 
81         uint32_t xid;         /* transaction id             */
82 
83         uint16_t secs;        /* seconds since start of acq */
84         uint16_t flags;
85 
86         uint32_t ciaddr;      /* client IP addr             */
87         uint32_t yiaddr;      /* your (client) IP addr      */
88         uint32_t siaddr;      /* ip addr of next server     */
89                               /* (DHCPOFFER and DHCPACK)    */
90         uint32_t giaddr;      /* relay agent IP addr        */
91 
92         uint8_t chaddr[16];  /* client hw addr             */
93         char sname[64];      /* asciiz server hostname     */
94         char file[128];      /* asciiz boot file name      */
95 
96         uint8_t options[1024];  /* optional parameters        */
97     }  dhcpData;
98 private:
99     Message(uint8_t operation,
100             const uint8_t (&macAddress)[ETH_ALEN],
101             uint8_t type);
102 
103     void addOption(uint8_t type, const void* data, uint8_t size);
104     template<typename T>
addOption(uint8_t type,T data)105     void addOption(uint8_t type, T data) {
106         static_assert(sizeof(T) <= 255, "The size of data is too large");
107         addOption(type, &data, sizeof(data));
108     }
109     template<typename T, size_t N>
addOption(uint8_t type,T (& items)[N])110     void addOption(uint8_t type, T (&items)[N]) {
111         static_assert(sizeof(T) * N <= 255,
112                       "The size of data is too large");
113         uint8_t* opts = nextOption();
114         *opts++ = type;
115         *opts++ = sizeof(T) * N;
116         for (const T& item : items) {
117             memcpy(opts, &item, sizeof(item));
118             opts += sizeof(item);
119         }
120         updateSize(opts);
121     }
122     void endOptions();
123 
124     const uint8_t* getOption(uint8_t optCode, uint8_t* length) const;
125     uint8_t* nextOption();
126     void updateSize(uint8_t* optionsEnd);
127     size_t mSize;
128 };
129 
130 static_assert(offsetof(Message::Dhcp, htype) == sizeof(Message::Dhcp::op),
131               "Invalid packing for DHCP message struct");
132