Kea  2.3.9
pkt.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2023 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 <utility>
9 #include <dhcp/pkt.h>
10 #include <dhcp/iface_mgr.h>
11 #include <dhcp/hwaddr.h>
12 #include <vector>
13 
14 namespace isc {
15 namespace dhcp {
16 
17 Pkt::Pkt(uint32_t transid, const isc::asiolink::IOAddress& local_addr,
18  const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
19  uint16_t remote_port)
20  : transid_(transid), iface_(""), ifindex_(UNSET_IFINDEX), local_addr_(local_addr),
21  remote_addr_(remote_addr), local_port_(local_port),
22  remote_port_(remote_port), buffer_out_(0), copy_retrieved_options_(false) {
23 }
24 
25 Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local_addr,
26  const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
27  uint16_t remote_port)
28  : transid_(0), iface_(""), ifindex_(UNSET_IFINDEX), local_addr_(local_addr),
29  remote_addr_(remote_addr), local_port_(local_port),
30  remote_port_(remote_port), buffer_out_(0), copy_retrieved_options_(false) {
31  if (len != 0) {
32  if (buf == NULL) {
33  isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL");
34  }
35  data_.resize(len);
36  memcpy(&data_[0], buf, len);
37  }
38 }
39 
40 void
42  options_.insert(std::pair<int, OptionPtr>(opt->getType(), opt));
43 }
44 
46 Pkt::getNonCopiedOption(const uint16_t type) const {
47  const auto& x = options_.find(type);
48  if (x != options_.end()) {
49  return (x->second);
50  }
51  return (OptionPtr());
52 }
53 
55 Pkt::getOption(const uint16_t type) {
56  const auto& x = options_.find(type);
57  if (x != options_.end()) {
59  OptionPtr option_copy = x->second->clone();
60  x->second = option_copy;
61  }
62  return (x->second);
63  }
64  return (OptionPtr()); // NULL
65 }
66 
68 Pkt::getNonCopiedOptions(const uint16_t opt_type) const {
69  std::pair<OptionCollection::const_iterator,
70  OptionCollection::const_iterator> range = options_.equal_range(opt_type);
71  return (OptionCollection(range.first, range.second));
72 }
73 
75 Pkt::getOptions(const uint16_t opt_type) {
76  OptionCollection options_copy;
77 
78  std::pair<OptionCollection::iterator,
79  OptionCollection::iterator> range = options_.equal_range(opt_type);
80  // If options should be copied on retrieval, we should now iterate over
81  // matching options, copy them and replace the original ones with new
82  // instances.
84  for (OptionCollection::iterator opt_it = range.first;
85  opt_it != range.second; ++opt_it) {
86  OptionPtr option_copy = opt_it->second->clone();
87  opt_it->second = option_copy;
88  }
89  }
90  // Finally, return updated options. This can also be empty in some cases.
91  return (OptionCollection(range.first, range.second));
92 }
93 
94 bool
95 Pkt::delOption(uint16_t type) {
96  const auto& x = options_.find(type);
97  if (x != options_.end()) {
98  options_.erase(x);
99  return (true); // delete successful
100  } else {
101  return (false); // can't find option to be deleted
102  }
103 }
104 
105 bool
106 Pkt::inClass(const ClientClass& client_class) {
107  return (classes_.contains(client_class));
108 }
109 
110 void
111 Pkt::addClass(const ClientClass& client_class, bool required) {
112  ClientClasses& classes = !required ? classes_ : required_classes_;
113  if (!classes.contains(client_class)) {
114  classes.insert(client_class);
115  static_cast<void>(subclasses_.push_back(SubClassRelation(client_class, client_class)));
116  }
117 }
118 
119 void
120 Pkt::addSubClass(const ClientClass& class_def, const ClientClass& subclass) {
121  if (!classes_.contains(class_def)) {
122  classes_.insert(class_def);
123  static_cast<void>(subclasses_.push_back(SubClassRelation(class_def, subclass)));
124  }
125  if (!classes_.contains(subclass)) {
126  classes_.insert(subclass);
127  static_cast<void>(subclasses_.push_back(SubClassRelation(subclass, subclass)));
128  }
129 }
130 
131 void
133  timestamp_ = boost::posix_time::microsec_clock::universal_time();
134 }
135 
136 void Pkt::repack() {
137  if (!data_.empty()) {
138  buffer_out_.writeData(&data_[0], data_.size());
139  }
140 }
141 
142 void
143 Pkt::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
144  const std::vector<uint8_t>& hw_addr) {
145  setHWAddrMember(htype, hlen, hw_addr, remote_hwaddr_);
146 }
147 
148 void
150  if (!hw_addr) {
151  isc_throw(BadValue, "Setting remote HW address to NULL is"
152  << " forbidden.");
153  }
154  remote_hwaddr_ = hw_addr;
155 }
156 
157 void
158 Pkt::setHWAddrMember(const uint8_t htype, const uint8_t,
159  const std::vector<uint8_t>& hw_addr,
160  HWAddrPtr& storage) {
161  storage.reset(new HWAddr(hw_addr, htype));
162 }
163 
164 HWAddrPtr
165 Pkt::getMAC(uint32_t hw_addr_src) {
166  HWAddrPtr mac;
167 
169 
170  // Method 1: from raw sockets.
171  if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) {
172  mac = getRemoteHWAddr();
173  if (mac) {
174  mac->source_ = HWAddr::HWADDR_SOURCE_RAW;
175  return (mac);
176  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_RAW) {
177  // If we're interested only in RAW sockets as source of that info,
178  // there's no point in trying other options.
179  return (HWAddrPtr());
180  }
181  }
182 
183  // Method 2: From client link-layer address option inserted by a relay
185  mac = getMACFromIPv6RelayOpt();
186  if (mac) {
187  return (mac);
188  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
189  // If we're interested only in RFC6939 link layer address as source
190  // of that info, there's no point in trying other options.
191  return (HWAddrPtr());
192  }
193  }
194 
195  // Method 3: Extracted from DUID-LLT or DUID-LL
196  if(hw_addr_src & HWAddr::HWADDR_SOURCE_DUID) {
197  mac = getMACFromDUID();
198  if (mac) {
199  return (mac);
200  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DUID) {
201  // If the only source allowed is DUID then we can skip the other
202  // methods.
203  return (HWAddrPtr());
204  }
205  }
206 
207  // Method 4: Extracted from source IPv6 link-local address
208  if (hw_addr_src & HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
210  if (mac) {
211  return (mac);
212  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
213  // If we're interested only in link-local addr as source of that
214  // info, there's no point in trying other options.
215  return (HWAddrPtr());
216  }
217  }
218 
219  // Method 5: From remote-id option inserted by a relay
220  if(hw_addr_src & HWAddr::HWADDR_SOURCE_REMOTE_ID) {
222  if (mac) {
223  return (mac);
224  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_REMOTE_ID) {
225  // If the only source allowed is remote-id option then we can skip
226  // the other methods.
227  return (HWAddrPtr());
228  }
229  }
230 
231  // Method 6: From subscriber-id option inserted by a relay
232 
233  // Method 7: From docsis options
234  if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
235  mac = getMACFromDocsisCMTS();
236  if (mac) {
237  return (mac);
238  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
239  // If we're interested only in CMTS options as a source of that
240  // info, there's no point in trying other options.
241  return (HWAddrPtr());
242  }
243  }
244 
245  // Method 8: From docsis options
246  if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
247  mac = getMACFromDocsisModem();
248  if (mac) {
249  return (mac);
250  } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
251  // If we're interested only in CMTS options as a source of that
252  // info, there's no point in trying other options.
253  return (HWAddrPtr());
254  }
255  }
256 
257  // Ok, none of the methods were suitable. Return NULL.
258  return (HWAddrPtr());
259 }
260 
261 HWAddrPtr
263  HWAddrPtr mac;
264 
265  if (addr.isV6LinkLocal()) {
266  std::vector<uint8_t> bin = addr.toBytes();
267 
268  // Double check that it's of appropriate size
269  if ((bin.size() == isc::asiolink::V6ADDRESS_LEN) &&
270  // Check that it's link-local (starts with fe80).
271  (bin[0] == 0xfe) && (bin[1] == 0x80) &&
272  // Check that u bit is set and g is clear.
273  // See Section 2.5.1 of RFC2373 for details.
274  ((bin[8] & 3) == 2) &&
275  // And that the IID is of EUI-64 type.
276  (bin[11] == 0xff) && (bin[12] == 0xfe)) {
277 
278  // Remove 8 most significant bytes
279  bin.erase(bin.begin(), bin.begin() + 8);
280 
281  // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX
282  bin.erase(bin.begin() + 3, bin.begin() + 5);
283 
284  // MAC-48 to EUI-64 involves inverting u bit (see explanation
285  // in Section 2.5.1 of RFC2373). We need to revert that.
286  bin[0] = bin[0] ^ 2;
287 
288  // Let's get the interface this packet was received on.
289  // We need it to get hardware type
291  uint16_t hwtype = 0; // not specified
292  if (iface) {
293  hwtype = iface->getHWType();
294  }
295 
296  mac.reset(new HWAddr(bin, hwtype));
298  }
299  }
300 
301  return (mac);
302 }
303 
304 } // end of namespace isc::dhcp
305 } // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
Container for storing client class names.
Definition: classify.h:108
bool contains(const ClientClass &x) const
returns if class x belongs to the defined classes
Definition: classify.cc:49
void insert(const ClientClass &class_name)
Insert an element.
Definition: classify.h:128
IfacePtr getIface(const unsigned int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:903
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
bool delOption(uint16_t type)
Attempts to delete first suboption of requested type.
Definition: pkt.cc:95
virtual HWAddrPtr getMACFromDocsisModem()=0
Attempts to extract MAC/Hardware address from DOCSIS options inserted by the modem itself.
isc::dhcp::OptionCollection getOptions(const uint16_t type)
Returns all instances of specified type.
Definition: pkt.cc:75
virtual HWAddrPtr getMACFromDocsisCMTS()=0
Attempts to extract MAC/Hardware address from DOCSIS options inserted by the CMTS (the relay agent)
ClientClasses required_classes_
Classes which are required to be evaluated.
Definition: pkt.h:658
void repack()
Copies content of input buffer to output buffer.
Definition: pkt.cc:136
virtual HWAddrPtr getMACFromRemoteIdRelayOption()=0
Attempts to obtain MAC address from remote-id relay option.
OptionBuffer data_
Unparsed data (in received packets).
Definition: pkt.h:340
HWAddrPtr getRemoteHWAddr() const
Returns the remote HW address obtained from raw sockets.
Definition: pkt.h:613
virtual HWAddrPtr getMACFromSrcLinkLocalAddr()=0
Attempts to obtain MAC address from source link-local IPv6 address.
ClientClasses classes_
Classes this packet belongs to.
Definition: pkt.h:650
virtual size_t len()=0
Returns packet size in binary format.
HWAddrPtr remote_hwaddr_
Definition: pkt.h:838
isc::dhcp::OptionCollection options_
Collection of options present in this message.
Definition: pkt.h:676
isc::util::OutputBuffer buffer_out_
Output buffer (used during message transmission)
Definition: pkt.h:826
virtual HWAddrPtr getMACFromDUID()=0
Attempts to obtain MAC address from DUID-LL or DUID-LLT.
SubClassRelationContainer subclasses_
SubClasses this packet belongs to.
Definition: pkt.h:666
OptionCollection getNonCopiedOptions(const uint16_t opt_type) const
Returns all option instances of specified type without copying.
Definition: pkt.cc:68
Pkt(uint32_t transid, const isc::asiolink::IOAddress &local_addr, const isc::asiolink::IOAddress &remote_addr, uint16_t local_port, uint16_t remote_port)
Constructor.
Definition: pkt.cc:17
OptionPtr getOption(const uint16_t type)
Returns the first option of specified type.
Definition: pkt.cc:55
void setRemoteHWAddr(const HWAddrPtr &hw_addr)
Sets remote hardware address.
Definition: pkt.cc:149
void addSubClass(const isc::dhcp::ClientClass &class_def, const isc::dhcp::ClientClass &subclass)
Adds a specified subclass to the packet.
Definition: pkt.cc:120
bool inClass(const isc::dhcp::ClientClass &client_class)
Checks whether a client belongs to a given class.
Definition: pkt.cc:106
virtual HWAddrPtr getMACFromIPv6RelayOpt()=0
Attempts to obtain MAC address from relay option client-linklayer-addr.
boost::posix_time::ptime timestamp_
packet timestamp
Definition: pkt.h:835
HWAddrPtr getMAC(uint32_t hw_addr_src)
Returns MAC address.
Definition: pkt.cc:165
void updateTimestamp()
Update packet timestamp.
Definition: pkt.cc:132
bool copy_retrieved_options_
Indicates if a copy of the retrieved option should be returned when Pkt::getOption is called.
Definition: pkt.h:832
std::string iface_
Name of the network interface the packet was received/to be sent over.
Definition: pkt.h:791
void addClass(const isc::dhcp::ClientClass &client_class, bool required=false)
Adds a specified class to the packet.
Definition: pkt.cc:111
OptionPtr getNonCopiedOption(const uint16_t type) const
Returns the first option of specified type without copying.
Definition: pkt.cc:46
HWAddrPtr getMACFromIPv6(const isc::asiolink::IOAddress &addr)
Attempts to convert IPv6 address into MAC.
Definition: pkt.cc:262
virtual void addOption(const OptionPtr &opt)
Adds an option to this packet.
Definition: pkt.cc:41
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.
static const uint32_t HWADDR_SOURCE_RAW
Obtained first hand from raw socket (100% reliable).
Definition: hwaddr.h:44
static const uint32_t HWADDR_SOURCE_REMOTE_ID
A relay can insert remote-id.
Definition: hwaddr.h:63
static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION
Get it from RFC6939 option.
Definition: hwaddr.h:59
static const uint32_t HWADDR_SOURCE_IPV6_LINK_LOCAL
Extracted from IPv6 link-local address.
Definition: hwaddr.h:53
static const uint32_t HWADDR_SOURCE_DOCSIS_MODEM
A cable modem (acting as DHCP client) that supports DOCSIS standard can insert DOCSIS options that co...
Definition: hwaddr.h:79
static const uint32_t HWADDR_SOURCE_DUID
Extracted from DUID-LL or DUID-LLT (not 100% reliable as the client can send fake DUID).
Definition: hwaddr.h:48
static const uint32_t HWADDR_SOURCE_DOCSIS_CMTS
A CMTS (acting as DHCP relay agent) that supports DOCSIS standard can insert DOCSIS options that cont...
Definition: hwaddr.h:73
std::string ClientClass
Defines a single class name.
Definition: classify.h:42
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition: iface_mgr.h:487
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
constexpr unsigned int UNSET_IFINDEX
A value used to signal that the interface index was not set.
Definition: pkt.h:30
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
Defines the logger used by the top-level component of kea-lfc.
Hardware type that represents information from DHCPv4 packet.
Definition: hwaddr.h:20
Defines a subclass to template class relation.
Definition: classify.h:67