Kea 3.1.8
protocol_util.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
9#include <dhcp/dhcp6.h>
10#include <dhcp/protocol_util.h>
11// in_systm.h is required on some some BSD systems
12// complaining that n_time is undefined but used
13// in ip.h.
14#include <netinet/in_systm.h>
15#include <netinet/ip.h>
16
17using namespace isc::asiolink;
18using namespace isc::util;
19
20namespace isc {
21namespace dhcp {
22
23void
25 // The size of the buffer to be parsed must not be lower
26 // then the size of the Ethernet frame header.
27 if (buf.getLength() - buf.getPosition() < ETHERNET_HEADER_LEN) {
28 isc_throw(InvalidPacketHeader, "size of ethernet header in received "
29 << "packet is invalid, expected at least "
30 << ETHERNET_HEADER_LEN << " bytes, received "
31 << buf.getLength() - buf.getPosition() << " bytes");
32 }
33 // Packet object must not be NULL. We want to output some values
34 // to this object.
35 if (!pkt) {
36 isc_throw(BadValue, "NULL packet object provided when parsing ethernet"
37 " frame header");
38 }
39
40 // The size of the single address is always lower then the size of
41 // the header that holds this address. Otherwise, it is a programming
42 // error that we want to detect in the compilation time.
43 static_assert(ETHERNET_HEADER_LEN > HWAddr::ETHERNET_HWADDR_LEN,
44 "ETHERNET_HEADER_LEN > HWAddr::ETHERNET_HWADDR_LEN");
45
46 // Remember initial position.
47 size_t start_pos = buf.getPosition();
48
49 // Read the destination HW address.
50 std::vector<uint8_t> dest_addr;
52 pkt->setLocalHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, dest_addr);
53 // Read the source HW address.
54 std::vector<uint8_t> src_addr;
56 pkt->setRemoteHWAddr(HWTYPE_ETHERNET, HWAddr::ETHERNET_HWADDR_LEN, src_addr);
57 // Move the buffer read pointer to the end of the Ethernet frame header.
58 buf.setPosition(start_pos + ETHERNET_HEADER_LEN);
59}
60
61void
63 // The size of the buffer must be at least equal to the minimal size of
64 // the IPv4 packet header plus UDP header length.
65 if (buf.getLength() - buf.getPosition() < MIN_IP_HEADER_LEN + UDP_HEADER_LEN) {
66 isc_throw(InvalidPacketHeader, "the total size of the IP and UDP headers in "
67 << "received packet is invalid, expected at least "
68 << MIN_IP_HEADER_LEN + UDP_HEADER_LEN
69 << " bytes, received " << buf.getLength() - buf.getPosition()
70 << " bytes");
71 }
72
73 // Packet object must not be NULL.
74 if (!pkt) {
75 isc_throw(BadValue, "NULL packet object provided when parsing IP and UDP"
76 " packet headers");
77 }
78
79 static_assert(IP_SRC_ADDR_OFFSET < MIN_IP_HEADER_LEN,
80 "IP_SRC_ADDR_OFFSET < MIN_IP_HEADER_LEN");
81
82 // Remember initial position of the read pointer.
83 size_t start_pos = buf.getPosition();
84
85 // Read IP header length (mask most significant bits as they indicate IP version).
86 uint8_t ip_len = buf.readUint8() & 0xF;
87 // IP length is the number of 4 byte chunks that construct IPv4 header.
88 // It must not be lower than 5 because first 20 bytes are fixed.
89 if (ip_len < 5) {
90 isc_throw(InvalidPacketHeader, "Value of the length of the IP header must not be"
91 << " lower than 5 words. The length of the received header is "
92 << static_cast<unsigned>(ip_len) << ".");
93 }
94
95 // Seek to the position of source IP address.
96 buf.setPosition(start_pos + IP_SRC_ADDR_OFFSET);
97 // Read source address.
98 pkt->setRemoteAddr(IOAddress(buf.readUint32()));
99 // Read destination address.
100 pkt->setLocalAddr(IOAddress(buf.readUint32()));
101
102 // Skip IP header options (if any) to start of the
103 // UDP header.
104 buf.setPosition(start_pos + ip_len * 4);
105
106 // Read source port from UDP header.
107 pkt->setRemotePort(buf.readUint16());
108 // Read destination port from UDP header.
109 pkt->setLocalPort(buf.readUint16());
110
111 // Set the pointer position to the first byte o the
112 // UDP payload (DHCP packet).
113 buf.setPosition(start_pos + ip_len * 4 + UDP_HEADER_LEN);
114}
115
116void
118 // Set destination HW address.
119 HWAddrPtr remote_addr = pkt->getRemoteHWAddr();
120 if (remote_addr) {
121 if (remote_addr->hwaddr_.size() != HWAddr::ETHERNET_HWADDR_LEN) {
122 isc_throw(BadValue, "invalid size of the remote HW address "
123 << remote_addr->hwaddr_.size() << " when constructing"
124 << " an ethernet frame header; expected size is"
126 } else if (!pkt->isRelayed() &&
127 (pkt->getFlags() & Pkt4::FLAG_BROADCAST_MASK)) {
128 out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN,255)[0],
130 } else {
131 out_buf.writeData(&remote_addr->hwaddr_[0],
133 }
134 } else {
135 // HW address has not been specified. This is possible when receiving
136 // packet through a logical interface (e.g. lo). In such cases, we
137 // don't want to fail but rather provide a default HW address, which
138 // consists of zeros.
139 out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
141 }
142
143 // Set source HW address.
144 HWAddrPtr local_addr = pkt->getLocalHWAddr();
145 if (local_addr) {
146 if (local_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
147 out_buf.writeData(&local_addr->hwaddr_[0],
149 } else {
150 isc_throw(BadValue, "invalid size of the local HW address "
151 << local_addr->hwaddr_.size() << " when constructing"
152 << " an ethernet frame header; expected size is"
154 }
155 } else {
156 // Provide default HW address.
157 out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
159 }
160
161 // Type IP.
162 out_buf.writeUint16(ETHERNET_TYPE_IP);
163}
164
165void
167
168 out_buf.writeUint8(0x45); // IP version 4, IP header length 5
169 out_buf.writeUint8(IPTOS_LOWDELAY); // DSCP and ECN
170 out_buf.writeUint16(28 + pkt->getBuffer().getLength()); // Total length.
171 out_buf.writeUint16(0); // Identification
172 out_buf.writeUint16(0x4000); // Disable fragmentation.
173 out_buf.writeUint8(128); // TTL
174 out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
175 out_buf.writeUint16(0); // Temporarily set checksum to 0.
176 out_buf.writeUint32(pkt->getLocalAddr().toUint32()); // Source address.
177 out_buf.writeUint32(pkt->getRemoteAddr().toUint32()); // Destination address.
178
179 // Calculate pseudo header checksum. It will be necessary to compute
180 // UDP checksum.
181 // Get the UDP length. This includes udp header's and data length.
182 uint32_t udp_len = 8 + pkt->getBuffer().getLength();
183 // The magic number "8" indicates the offset where the source address
184 // is stored in the buffer. This offset is counted here from the
185 // current tail of the buffer. Starting from this offset we calculate
186 // the checksum using 8 following bytes of data. This will include
187 // 4 bytes of source address and 4 bytes of destination address.
188 // The IPPROTO_UDP and udp_len are also added up to the checksum.
189 uint16_t pseudo_hdr_checksum =
190 calcChecksum(out_buf.getData() + out_buf.getLength() - 8,
191 8, IPPROTO_UDP + udp_len);
192
193 // Calculate IP header checksum.
194 uint16_t ip_checksum = ~calcChecksum(out_buf.getData()
195 + out_buf.getLength() - 20, 20);
196 // Write checksum in the IP header. The offset of the checksum is 10 bytes
197 // back from the tail of the current buffer.
198 out_buf.writeUint16At(ip_checksum, out_buf.getLength() - 10);
199
200 // Start UDP header.
201 out_buf.writeUint16(pkt->getLocalPort()); // Source port.
202 out_buf.writeUint16(pkt->getRemotePort()); // Destination port.
203 out_buf.writeUint16(udp_len); // Length of the header and data.
204
205 // Checksum is calculated from the contents of UDP header, data and pseudo ip header.
206 // The magic number "6" indicates that the UDP header starts at offset 6 from the
207 // tail of the current buffer. These 6 bytes contain source and destination port
208 // as well as the length of the header.
209 uint16_t udp_checksum =
210 ~calcChecksum(out_buf.getData() + out_buf.getLength() - 6, 6,
211 calcChecksum(pkt->getBuffer().getData(),
212 pkt->getBuffer().getLength(),
213 pseudo_hdr_checksum));
214 // Write UDP checksum.
215 out_buf.writeUint16(udp_checksum);
216}
217
218uint16_t
219calcChecksum(const uint8_t* buf, const uint32_t buf_size, uint32_t sum) {
220 uint32_t i;
221 for (i = 0; i < (buf_size & ~1U); i += 2) {
222 uint16_t chunk = buf[i] << 8 | buf[i + 1];
223 sum += chunk;
224 if (sum > 0xFFFF) {
225 sum -= 0xFFFF;
226 }
227 }
228 // If one byte has left, we also need to add it to the checksum.
229 if (i < buf_size) {
230 sum += buf[i] << 8;
231 if (sum > 0xFFFF) {
232 sum -= 0xFFFF;
233 }
234 }
235
236 return (sum);
237
238}
239
240}
241}
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Exception thrown when error occurred during parsing packet's headers.
static const uint16_t FLAG_BROADCAST_MASK
Mask for the value of flags field in the DHCPv4 message to check whether client requested broadcast r...
Definition pkt4.h:54
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition buffer.h:81
void readVector(std::vector< uint8_t > &data, size_t len)
Read specified number of bytes as a vector.
Definition buffer.h:262
uint32_t readUint32()
Read an unsigned 32-bit integer in network byte order from the buffer, and return it.
Definition buffer.h:196
void setPosition(size_t position)
Set the read position of the buffer to the given value.
Definition buffer.h:112
size_t getPosition() const
Return the current read position.
Definition buffer.h:101
uint8_t readUint8()
Read an unsigned 8-bit integer from the buffer and return it.
Definition buffer.h:138
size_t getLength() const
Return the length of the data stored in the buffer.
Definition buffer.h:96
uint16_t readUint16()
Read an unsigned 16-bit integer in network byte order from the buffer, and return it.
Definition buffer.h:166
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:346
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition buffer.h:476
void writeUint16(uint16_t data)
Write an unsigned 16-bit integer in host byte order into the buffer in network byte order.
Definition buffer.h:501
void writeUint16At(uint16_t data, size_t position)
Write an unsigned 16-bit integer in host byte order at the specified position of the buffer in networ...
Definition buffer.h:517
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition buffer.h:559
void writeUint32(uint32_t data)
Write an unsigned 32-bit integer in host byte order into the buffer in network byte order.
Definition buffer.h:531
const uint8_t * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition buffer.h:398
size_t getLength() const
Return the length of data written in the buffer.
Definition buffer.h:412
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
void decodeEthernetHeader(InputBuffer &buf, Pkt4Ptr &pkt)
Decode the Ethernet header.
void writeEthernetHeader(const Pkt4Ptr &pkt, OutputBuffer &out_buf)
Writes ethernet frame header into a buffer.
void decodeIpUdpHeader(InputBuffer &buf, Pkt4Ptr &pkt)
Decode IP and UDP header.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
uint16_t calcChecksum(const uint8_t *buf, const uint32_t buf_size, uint32_t sum)
Calculates checksum for provided buffer.
void writeIpUdpHeader(const Pkt4Ptr &pkt, util::OutputBuffer &out_buf)
Writes both IP and UDP header into output buffer.
Defines the logger used by the top-level component of kea-lfc.
static const size_t ETHERNET_HWADDR_LEN
Size of an ethernet hardware address.
Definition hwaddr.h:24