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