Kea: protocol_util.cc Source File

| Kea 3.0.2-git

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