Kea  2.1.6-git
protocol_util.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2019 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 
18 using namespace isc::asiolink;
19 using namespace isc::util;
20 
21 namespace isc {
22 namespace dhcp {
23 
24 void
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 
61 void
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 
115 void
116 writeEthernetHeader(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  out_buf.writeData(&remote_addr->hwaddr_[0],
122  HWAddr::ETHERNET_HWADDR_LEN);
123  } else {
124  isc_throw(BadValue, "invalid size of the remote HW address "
125  << remote_addr->hwaddr_.size() << " when constructing"
126  << " an ethernet frame header; expected size is"
127  << " " << HWAddr::ETHERNET_HWADDR_LEN);
128  }
129  } else {
130  // HW address has not been specified. This is possible when receiving
131  // packet through a logical interface (e.g. lo). In such cases, we
132  // don't want to fail but rather provide a default HW address, which
133  // consists of zeros.
134  out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
135  HWAddr::ETHERNET_HWADDR_LEN);
136  }
137 
138  // Set source HW address.
139  HWAddrPtr local_addr = pkt->getLocalHWAddr();
140  if (local_addr) {
141  if (local_addr->hwaddr_.size() == HWAddr::ETHERNET_HWADDR_LEN) {
142  out_buf.writeData(&local_addr->hwaddr_[0],
143  HWAddr::ETHERNET_HWADDR_LEN);
144  } else {
145  isc_throw(BadValue, "invalid size of the local HW address "
146  << local_addr->hwaddr_.size() << " when constructing"
147  << " an ethernet frame header; expected size is"
148  << " " << HWAddr::ETHERNET_HWADDR_LEN);
149  }
150  } else {
151  // Provide default HW address.
152  out_buf.writeData(&std::vector<uint8_t>(HWAddr::ETHERNET_HWADDR_LEN)[0],
153  HWAddr::ETHERNET_HWADDR_LEN);
154  }
155 
156  // Type IP.
157  out_buf.writeUint16(ETHERNET_TYPE_IP);
158 }
159 
160 void
162 
163  out_buf.writeUint8(0x45); // IP version 4, IP header length 5
164  out_buf.writeUint8(IPTOS_LOWDELAY); // DSCP and ECN
165  out_buf.writeUint16(28 + pkt->getBuffer().getLength()); // Total length.
166  out_buf.writeUint16(0); // Identification
167  out_buf.writeUint16(0x4000); // Disable fragmentation.
168  out_buf.writeUint8(128); // TTL
169  out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
170  out_buf.writeUint16(0); // Temporarily set checksum to 0.
171  out_buf.writeUint32(pkt->getLocalAddr().toUint32()); // Source address.
172  out_buf.writeUint32(pkt->getRemoteAddr().toUint32()); // Destination address.
173 
174  // Calculate pseudo header checksum. It will be necessary to compute
175  // UDP checksum.
176  // Get the UDP length. This includes udp header's and data length.
177  uint32_t udp_len = 8 + pkt->getBuffer().getLength();
178  // The magic number "8" indicates the offset where the source address
179  // is stored in the buffer. This offset is counted here from the
180  // current tail of the buffer. Starting from this offset we calculate
181  // the checksum using 8 following bytes of data. This will include
182  // 4 bytes of source address and 4 bytes of destination address.
183  // The IPPROTO_UDP and udp_len are also added up to the checksum.
184  uint16_t pseudo_hdr_checksum =
185  calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 8,
186  8, IPPROTO_UDP + udp_len);
187 
188  // Calculate IP header checksum.
189  uint16_t ip_checksum = ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData())
190  + out_buf.getLength() - 20, 20);
191  // Write checksum in the IP header. The offset of the checksum is 10 bytes
192  // back from the tail of the current buffer.
193  out_buf.writeUint16At(ip_checksum, out_buf.getLength() - 10);
194 
195  // Start UDP header.
196  out_buf.writeUint16(pkt->getLocalPort()); // Source port.
197  out_buf.writeUint16(pkt->getRemotePort()); // Destination port.
198  out_buf.writeUint16(udp_len); // Length of the header and data.
199 
200  // Checksum is calculated from the contents of UDP header, data and pseudo ip header.
201  // The magic number "6" indicates that the UDP header starts at offset 6 from the
202  // tail of the current buffer. These 6 bytes contain source and destination port
203  // as well as the length of the header.
204  uint16_t udp_checksum =
205  ~calcChecksum(static_cast<const uint8_t*>(out_buf.getData()) + out_buf.getLength() - 6, 6,
206  calcChecksum(static_cast<const uint8_t*>(pkt->getBuffer().getData()),
207  pkt->getBuffer().getLength(),
208  pseudo_hdr_checksum));
209  // Write UDP checksum.
210  out_buf.writeUint16(udp_checksum);
211 }
212 
213 uint16_t
214 calcChecksum(const uint8_t* buf, const uint32_t buf_size, uint32_t sum) {
215  uint32_t i;
216  for (i = 0; i < (buf_size & ~1U); i += 2) {
217  uint16_t chunk = buf[i] << 8 | buf[i + 1];
218  sum += chunk;
219  if (sum > 0xFFFF) {
220  sum -= 0xFFFF;
221  }
222  }
223  // If one byte has left, we also need to add it to the checksum.
224  if (i < buf_size) {
225  sum += buf[i] << 8;
226  if (sum > 0xFFFF) {
227  sum -= 0xFFFF;
228  }
229  }
230 
231  return (sum);
232 
233 }
234 
235 }
236 }
void readVector(std::vector< uint8_t > &data, size_t len)
Read specified number of bytes as a vector.
Definition: buffer.h:204
void writeEthernetHeader(const Pkt4Ptr &pkt, OutputBuffer &out_buf)
Writes ethernet frame header into a buffer.
uint16_t calcChecksum(const uint8_t *buf, const uint32_t buf_size, uint32_t sum)
Calculates checksum for provided buffer.
void setPosition(size_t position)
Set the read position of the buffer to the given value.
Definition: buffer.h:115
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
size_t getPosition() const
Return the current read position.
Definition: buffer.h:102
size_t getLength() const
Return the length of the data stored in the buffer.
Definition: buffer.h:100
void decodeEthernetHeader(InputBuffer &buf, Pkt4Ptr &pkt)
Decode the Ethernet header.
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: edns.h:19
void writeUint16At(uint16_t data, size_t pos)
Write an unsigned 16-bit integer in host byte order at the specified position of the buffer in networ...
Definition: buffer.h:507
void writeIpUdpHeader(const Pkt4Ptr &pkt, util::OutputBuffer &out_buf)
Writes both IP and UDP header into output buffer.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
const void * getData() const
Return a pointer to the head of the data stored in the buffer.
Definition: buffer.h:401
Exception thrown when error occurred during parsing packet&#39;s headers.
Definition: protocol_util.h:22
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
size_t getLength() const
Return the length of data written in the buffer.
Definition: buffer.h:403
uint16_t readUint16()
Read an unsigned 16-bit integer in network byte order from the buffer, convert it to host byte order...
Definition: buffer.h:142
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:520
Defines the logger used by the top-level component of kea-lfc.
uint32_t readUint32()
Read an unsigned 32-bit integer in network byte order from the buffer, convert it to host byte order...
Definition: buffer.h:162
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:466
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:490
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
uint8_t readUint8()
Read an unsigned 8-bit integer from the buffer and return it.
Definition: buffer.h:130
void decodeIpUdpHeader(InputBuffer &buf, Pkt4Ptr &pkt)
Decode IP and UDP header.