1 // Copyright (C) 2014-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 <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
Pkt(uint32_t transid,const isc::asiolink::IOAddress & local_addr,const isc::asiolink::IOAddress & remote_addr,uint16_t local_port,uint16_t remote_port)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),
21 iface_(""),
22 ifindex_(-1),
23 local_addr_(local_addr),
24 remote_addr_(remote_addr),
25 local_port_(local_port),
26 remote_port_(remote_port),
27 buffer_out_(0),
28 copy_retrieved_options_(false)
29 {
30 }
31
Pkt(const uint8_t * buf,uint32_t len,const isc::asiolink::IOAddress & local_addr,const isc::asiolink::IOAddress & remote_addr,uint16_t local_port,uint16_t remote_port)32 Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local_addr,
33 const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
34 uint16_t remote_port)
35 :transid_(0),
36 iface_(""),
37 ifindex_(-1),
38 local_addr_(local_addr),
39 remote_addr_(remote_addr),
40 local_port_(local_port),
41 remote_port_(remote_port),
42 buffer_out_(0),
43 copy_retrieved_options_(false)
44 {
45
46 if (len != 0) {
47 if (buf == NULL) {
48 isc_throw(InvalidParameter, "data buffer passed to Pkt is NULL");
49 }
50 data_.resize(len);
51 memcpy(&data_[0], buf, len);
52 }
53 }
54
55 void
addOption(const OptionPtr & opt)56 Pkt::addOption(const OptionPtr& opt) {
57 options_.insert(std::pair<int, OptionPtr>(opt->getType(), opt));
58 }
59
60 OptionPtr
getNonCopiedOption(const uint16_t type) const61 Pkt::getNonCopiedOption(const uint16_t type) const {
62 OptionCollection::const_iterator x = options_.find(type);
63 if (x != options_.end()) {
64 return (x->second);
65 }
66 return (OptionPtr());
67 }
68
69 OptionPtr
getOption(const uint16_t type)70 Pkt::getOption(const uint16_t type) {
71 OptionCollection::iterator x = options_.find(type);
72 if (x != options_.end()) {
73 if (copy_retrieved_options_) {
74 OptionPtr option_copy = x->second->clone();
75 x->second = option_copy;
76 }
77 return (x->second);
78 }
79 return (OptionPtr()); // NULL
80 }
81
82 bool
delOption(uint16_t type)83 Pkt::delOption(uint16_t type) {
84
85 isc::dhcp::OptionCollection::iterator x = options_.find(type);
86 if (x!=options_.end()) {
87 options_.erase(x);
88 return (true); // delete successful
89 } else {
90 return (false); // can't find option to be deleted
91 }
92 }
93
94 bool
inClass(const std::string & client_class)95 Pkt::inClass(const std::string& client_class) {
96 return (classes_.contains(client_class));
97 }
98
99 void
addClass(const std::string & client_class,bool required)100 Pkt::addClass(const std::string& client_class, bool required) {
101 // Always have ALL first.
102 if (classes_.empty()) {
103 classes_.insert("ALL");
104 }
105 ClientClasses& classes = !required ? classes_ : required_classes_;
106 if (!classes.contains(client_class)) {
107 classes.insert(client_class);
108 }
109 }
110
111 void
updateTimestamp()112 Pkt::updateTimestamp() {
113 timestamp_ = boost::posix_time::microsec_clock::universal_time();
114 }
115
repack()116 void Pkt::repack() {
117 if (!data_.empty()) {
118 buffer_out_.writeData(&data_[0], data_.size());
119 }
120 }
121
122 void
setRemoteHWAddr(const uint8_t htype,const uint8_t hlen,const std::vector<uint8_t> & hw_addr)123 Pkt::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
124 const std::vector<uint8_t>& hw_addr) {
125 setHWAddrMember(htype, hlen, hw_addr, remote_hwaddr_);
126 }
127
128 void
setRemoteHWAddr(const HWAddrPtr & hw_addr)129 Pkt::setRemoteHWAddr(const HWAddrPtr& hw_addr) {
130 if (!hw_addr) {
131 isc_throw(BadValue, "Setting remote HW address to NULL is"
132 << " forbidden.");
133 }
134 remote_hwaddr_ = hw_addr;
135 }
136
137 void
setHWAddrMember(const uint8_t htype,const uint8_t,const std::vector<uint8_t> & hw_addr,HWAddrPtr & storage)138 Pkt::setHWAddrMember(const uint8_t htype, const uint8_t,
139 const std::vector<uint8_t>& hw_addr,
140 HWAddrPtr& storage) {
141
142 storage.reset(new HWAddr(hw_addr, htype));
143 }
144
145 HWAddrPtr
getMAC(uint32_t hw_addr_src)146 Pkt::getMAC(uint32_t hw_addr_src) {
147 HWAddrPtr mac;
148
149 /// @todo: Implement an array of method pointers instead of set of ifs
150
151 // Method 1: from raw sockets.
152 if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) {
153 mac = getRemoteHWAddr();
154 if (mac) {
155 mac->source_ = HWAddr::HWADDR_SOURCE_RAW;
156 return (mac);
157 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_RAW) {
158 // If we're interested only in RAW sockets as source of that info,
159 // there's no point in trying other options.
160 return (HWAddrPtr());
161 }
162 }
163
164 // Method 2: From client link-layer address option inserted by a relay
165 if (hw_addr_src & HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
166 mac = getMACFromIPv6RelayOpt();
167 if (mac) {
168 return (mac);
169 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
170 // If we're interested only in RFC6939 link layer address as source
171 // of that info, there's no point in trying other options.
172 return (HWAddrPtr());
173 }
174 }
175
176 // Method 3: Extracted from DUID-LLT or DUID-LL
177 if(hw_addr_src & HWAddr::HWADDR_SOURCE_DUID) {
178 mac = getMACFromDUID();
179 if (mac) {
180 return (mac);
181 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DUID) {
182 // If the only source allowed is DUID then we can skip the other
183 // methods.
184 return (HWAddrPtr());
185 }
186 }
187
188 // Method 4: Extracted from source IPv6 link-local address
189 if (hw_addr_src & HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
190 mac = getMACFromSrcLinkLocalAddr();
191 if (mac) {
192 return (mac);
193 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
194 // If we're interested only in link-local addr as source of that
195 // info, there's no point in trying other options.
196 return (HWAddrPtr());
197 }
198 }
199
200 // Method 5: From remote-id option inserted by a relay
201 if(hw_addr_src & HWAddr::HWADDR_SOURCE_REMOTE_ID) {
202 mac = getMACFromRemoteIdRelayOption();
203 if (mac) {
204 return (mac);
205 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_REMOTE_ID) {
206 // If the only source allowed is remote-id option then we can skip
207 // the other methods.
208 return (HWAddrPtr());
209 }
210 }
211
212 // Method 6: From subscriber-id option inserted by a relay
213
214 // Method 7: From docsis options
215 if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
216 mac = getMACFromDocsisCMTS();
217 if (mac) {
218 return (mac);
219 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_CMTS) {
220 // If we're interested only in CMTS options as a source of that
221 // info, there's no point in trying other options.
222 return (HWAddrPtr());
223 }
224 }
225
226 // Method 8: From docsis options
227 if (hw_addr_src & HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
228 mac = getMACFromDocsisModem();
229 if (mac) {
230 return (mac);
231 } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_DOCSIS_MODEM) {
232 // If we're interested only in CMTS options as a source of that
233 // info, there's no point in trying other options.
234 return (HWAddrPtr());
235 }
236 }
237
238 // Ok, none of the methods were suitable. Return NULL.
239 return (HWAddrPtr());
240 }
241
242 HWAddrPtr
getMACFromIPv6(const isc::asiolink::IOAddress & addr)243 Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) {
244 HWAddrPtr mac;
245
246 if (addr.isV6LinkLocal()) {
247 std::vector<uint8_t> bin = addr.toBytes();
248
249 // Double check that it's of appropriate size
250 if ((bin.size() == isc::asiolink::V6ADDRESS_LEN) &&
251 // Check that it's link-local (starts with fe80).
252 (bin[0] == 0xfe) && (bin[1] == 0x80) &&
253 // Check that u bit is set and g is clear.
254 // See Section 2.5.1 of RFC2373 for details.
255 ((bin[8] & 3) == 2) &&
256 // And that the IID is of EUI-64 type.
257 (bin[11] == 0xff) && (bin[12] == 0xfe)) {
258
259 // Remove 8 most significant bytes
260 bin.erase(bin.begin(), bin.begin() + 8);
261
262 // Ok, we're down to EUI-64 only now: XX:XX:XX:ff:fe:XX:XX:XX
263 bin.erase(bin.begin() + 3, bin.begin() + 5);
264
265 // MAC-48 to EUI-64 involves inverting u bit (see explanation
266 // in Section 2.5.1 of RFC2373). We need to revert that.
267 bin[0] = bin[0] ^ 2;
268
269 // Let's get the interface this packet was received on.
270 // We need it to get hardware type
271 IfacePtr iface = IfaceMgr::instance().getIface(iface_);
272 uint16_t hwtype = 0; // not specified
273 if (iface) {
274 hwtype = iface->getHWType();
275 }
276
277 mac.reset(new HWAddr(bin, hwtype));
278 mac->source_ = HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL;
279 }
280 }
281
282 return (mac);
283 }
284
285 };
286 };
287