Coverage Report

Created: 2026-07-14 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/dhcp/pkt.h
Line
Count
Source
1
// Copyright (C) 2014-2025 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
#ifndef PKT_H
8
#define PKT_H
9
10
#include <asiolink/io_address.h>
11
#include <util/buffer.h>
12
#include <dhcp/option.h>
13
#include <dhcp/hwaddr.h>
14
#include <dhcp/classify.h>
15
#include <hooks/callout_handle_associate.h>
16
17
#include <boost/date_time/posix_time/posix_time.hpp>
18
#include <boost/shared_ptr.hpp>
19
20
#include <limits>
21
#include <utility>
22
23
namespace isc {
24
25
namespace dhcp {
26
27
/// @brief A value used to signal that the interface index was not set.
28
/// That means that more than UNSET_IFINDEX interfaces are not supported.
29
/// That's fine, since it would have overflowed with UNSET_IFINDEX + 1 anyway.
30
constexpr unsigned int UNSET_IFINDEX = std::numeric_limits<unsigned int>::max();
31
32
/// @brief RAII object enabling copying options retrieved from the
33
/// packet.
34
///
35
/// This object enables copying retrieved options from a packet within
36
/// a scope in which this object exists. When the object goes out of scope
37
/// copying options is disabled. This is applicable in cases when the
38
/// server is going to invoke a callout (hook library) where copying options
39
/// must be enabled by default. When the callouts return copying options
40
/// should be disabled. The use of RAII object eliminates the need for
41
/// explicitly re-disabling options copying and is safer in case of
42
/// exceptions thrown by callouts and a presence of multiple exit points.
43
///
44
/// @tparam PktType Type of the packet, e.g. Pkt4, Pkt6, Pkt4o6.
45
template<typename PktType>
46
class ScopedEnableOptionsCopy {
47
public:
48
49
    /// @brief Pointer to an encapsulated packet.
50
    typedef boost::shared_ptr<PktType> PktTypePtr;
51
52
    /// @brief Constructor.
53
    ///
54
    /// Enables options copying on a packet(s).
55
    ///
56
    /// @param pkt1 Pointer to first packet.
57
    /// @param pkt2 Optional pointer to the second packet.
58
    ScopedEnableOptionsCopy(const PktTypePtr& pkt1,
59
                            const PktTypePtr& pkt2 = PktTypePtr())
60
0
        : pkts_(pkt1, pkt2) {
61
0
        if (pkt1) {
62
0
            pkt1->setCopyRetrievedOptions(true);
63
0
        }
64
0
        if (pkt2) {
65
0
            pkt2->setCopyRetrievedOptions(true);
66
0
        }
67
0
    }
Unexecuted instantiation: isc::dhcp::ScopedEnableOptionsCopy<isc::dhcp::Pkt6>::ScopedEnableOptionsCopy(boost::shared_ptr<isc::dhcp::Pkt6> const&, boost::shared_ptr<isc::dhcp::Pkt6> const&)
Unexecuted instantiation: isc::dhcp::ScopedEnableOptionsCopy<isc::dhcp::Pkt4>::ScopedEnableOptionsCopy(boost::shared_ptr<isc::dhcp::Pkt4> const&, boost::shared_ptr<isc::dhcp::Pkt4> const&)
68
69
    /// @brief Destructor.
70
    ///
71
    /// Disables options copying on a packets.
72
0
    ~ScopedEnableOptionsCopy() {
73
0
        if (pkts_.first) {
74
0
            pkts_.first->setCopyRetrievedOptions(false);
75
0
        }
76
0
        if (pkts_.second) {
77
0
            pkts_.second->setCopyRetrievedOptions(false);
78
0
        }
79
0
    }
Unexecuted instantiation: isc::dhcp::ScopedEnableOptionsCopy<isc::dhcp::Pkt6>::~ScopedEnableOptionsCopy()
Unexecuted instantiation: isc::dhcp::ScopedEnableOptionsCopy<isc::dhcp::Pkt4>::~ScopedEnableOptionsCopy()
80
81
private:
82
83
    /// @brief Holds a pair of pointers of the packets.
84
    std::pair<PktTypePtr, PktTypePtr> pkts_;
85
};
86
87
88
/// @brief Describes an event during the life cycle of a packet.
89
class PktEvent {
90
public:
91
    /// @brief Event that marks when a packet is placed in the socket buffer
92
    /// by the kernel.
93
    static const std::string SOCKET_RECEIVED;
94
95
    /// @brief Event that marks when a packet is read from the socket buffer
96
    /// by application.
97
    static const std::string BUFFER_READ;
98
99
    /// @brief Event that marks when a packet is been written to the socket
100
    /// by application.
101
    static const std::string RESPONSE_SENT;
102
103
    /// @brief Constructor.
104
    ///
105
    /// @param label string identifying the event.
106
    /// @param timestamp time at which the event occurred.
107
    PktEvent(const std::string& label, boost::posix_time::ptime timestamp)
108
547
        : label_(label), timestamp_(timestamp) {
109
547
    }
110
111
    /// @brief Destructor.
112
1.09k
    ~PktEvent() = default;
113
114
    /// @brief Fetch the current UTC system time, microsecond precision.
115
    ///
116
    /// @return ptime containing the microsecond system time.
117
547
    static boost::posix_time::ptime now() {
118
547
        return (boost::posix_time::microsec_clock::universal_time());
119
547
    }
120
121
    /// @brief Fetch an empty timestamp, used for logic comparisons
122
    ///
123
    /// @return an unset ptime.
124
0
    static boost::posix_time::ptime& EMPTY_TIME() {
125
0
        static boost::posix_time::ptime empty_time;
126
0
        return (empty_time);
127
0
    }
128
129
    /// @brief Fetches the minimum timestamp
130
    ///
131
    /// @return the minimum timestamp
132
0
    static boost::posix_time::ptime& MIN_TIME() {
133
0
        static auto min_time = boost::posix_time::ptime(boost::posix_time::min_date_time);
134
0
        return (min_time);
135
0
    }
136
137
    /// @brief Fetches the maximum timestamp
138
    ///
139
    /// @return the maximum timestamp
140
0
    static boost::posix_time::ptime& MAX_TIME() {
141
0
        static auto max_time = boost::posix_time::ptime(boost::posix_time::max_date_time);
142
0
        return (max_time);
143
0
    }
144
145
    /// @brief Label identifying this event.
146
    std::string label_;
147
148
    /// @brief Timestamp at which the event occurred.
149
    boost::posix_time::ptime timestamp_;
150
};
151
152
/// @brief Base class for classes representing DHCP messages.
153
///
154
/// This is a base class that holds common information (e.g. source
155
/// and destination ports) and operations (e.g. add, get, delete options)
156
/// for derived classes representing both DHCPv4 and DHCPv6 messages.
157
/// The @c Pkt4 and @c Pkt6 classes derive from it.
158
///
159
/// @note This is abstract class. Please instantiate derived classes
160
/// such as @c Pkt4 or @c Pkt6.
161
class Pkt : public hooks::CalloutHandleAssociate {
162
protected:
163
164
    /// @brief Constructor.
165
    ///
166
    /// This constructor is typically used for transmitted messages as it
167
    /// creates an empty (no options) packet. The constructor is protected,
168
    /// so only derived classes can call it. Pkt class cannot be instantiated
169
    /// anyway, because it is an abstract class.
170
    ///
171
    /// @param transid transaction-id
172
    /// @param local_addr local IPv4 or IPv6 address
173
    /// @param remote_addr remote IPv4 or IPv6 address
174
    /// @param local_port local UDP (one day also TCP) port
175
    /// @param remote_port remote UDP (one day also TCP) port
176
    Pkt(uint32_t transid, const isc::asiolink::IOAddress& local_addr,
177
        const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
178
        uint16_t remote_port);
179
180
    /// @brief Constructor.
181
    ///
182
    /// This constructor is typically used for received messages as it takes
183
    /// a buffer that's going to be parsed as one of arguments. The constructor
184
    /// is protected, so only derived classes can call it. Pkt class cannot be
185
    /// instantiated anyway, because it is an abstract class.
186
    ///
187
    /// @param buf pointer to a buffer that contains on-wire data
188
    /// @param len length of the pointer specified in buf
189
    /// @param local_addr local IPv4 or IPv6 address
190
    /// @param remote_addr remote IPv4 or IPv6 address
191
    /// @param local_port local UDP (one day also TCP) port
192
    /// @param remote_port remote UDP (one day also TCP) port
193
    Pkt(const uint8_t* buf, uint32_t len,
194
        const isc::asiolink::IOAddress& local_addr,
195
        const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
196
        uint16_t remote_port);
197
198
public:
199
200
    /// @brief Prepares on-wire format of DHCP (either v4 or v6) packet.
201
    ///
202
    /// Prepares on-wire format of message and all its options.
203
    /// A caller must ensure that options are stored in options_ field
204
    /// prior to calling this method.
205
    ///
206
    /// Output buffer will be stored in buffer_out_.
207
    /// The buffer_out_ should be cleared before writing to the buffer
208
    /// in the derived classes.
209
    ///
210
    /// @note This is a pure virtual method and must be implemented in
211
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
212
    /// implementations of this method.
213
    ///
214
    /// @throw InvalidOperation if packing fails
215
    virtual void pack() = 0;
216
217
    /// @brief Parses on-wire form of DHCP (either v4 or v6) packet.
218
    ///
219
    /// Parses received packet, stored in on-wire format in data_.
220
    ///
221
    /// Will create a collection of option objects that will
222
    /// be stored in options_ container.
223
    ///
224
    /// @note This is a pure virtual method and must be implemented in
225
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
226
    /// implementations of this method.
227
    ///
228
    /// Method will throw exception if packet parsing fails.
229
    ///
230
    /// @throw tbd
231
    virtual void unpack() = 0;
232
233
    /// @brief Returns reference to output buffer.
234
    ///
235
    /// Returned buffer will contain reasonable data only for
236
    /// output (TX) packet and after pack() was called.
237
    ///
238
    /// RX packet or TX packet before pack() will return buffer with
239
    /// zero length. This buffer is returned as non-const, so hooks
240
    /// framework (and user's callouts) can modify them if needed
241
    ///
242
    /// @note This buffer is only valid till object that returned it exists.
243
    ///
244
    /// @return reference to output buffer
245
801
    isc::util::OutputBuffer& getBuffer() {
246
801
        return (buffer_out_);
247
801
    }
248
249
    /// @brief Adds an option to this packet.
250
    ///
251
    /// Derived classes may provide more specialized implementations.
252
    /// In particular @c Pkt4 provides one that checks if option is
253
    /// unique.
254
    ///
255
    /// @param opt option to be added.
256
    virtual void addOption(const OptionPtr& opt);
257
258
    /// @brief Attempts to delete first suboption of requested type.
259
    ///
260
    /// If there are several options of the same type present, only
261
    /// the first option will be deleted.
262
    ///
263
    /// @param type Type of option to be deleted.
264
    ///
265
    /// @return true if option was deleted, false if no such option existed
266
    bool delOption(uint16_t type);
267
268
    /// @brief Returns text representation primary packet identifiers
269
    ///
270
    /// This method is intended to be used to provide as a consistent way to
271
    /// identify packets within log statements.  Derivations should supply
272
    /// there own implementation.
273
    ///
274
    /// @return string with text representation
275
0
    virtual std::string getLabel() const {
276
0
        isc_throw(NotImplemented, "Pkt::getLabel()");
277
0
    }
Unexecuted instantiation: isc::dhcp::Pkt::getLabel() const
Unexecuted instantiation: isc::dhcp::Pkt::getLabel() const
278
279
    /// @brief Returns text representation of the packet.
280
    ///
281
    /// This function is useful mainly for debugging.
282
    ///
283
    /// @note This is a pure virtual method and must be implemented in
284
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
285
    /// implementations of this method.
286
    /// @param verbose output most if not all members.
287
    ///
288
    /// @return string with text representation
289
    virtual std::string toText(bool verbose = false) const = 0;
290
291
    /// @brief Returns packet size in binary format.
292
    ///
293
    /// Returns size of the packet in on-wire format or size needed to store
294
    /// it in on-wire format.
295
    ///
296
    /// @note This is a pure virtual method and must be implemented in
297
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
298
    /// implementations of this method.
299
    ///
300
    /// @return packet size in bytes
301
    virtual size_t len() = 0;
302
303
    /// @brief Returns message type (e.g. 1 = SOLICIT).
304
    ///
305
    /// @note This is a pure virtual method and must be implemented in
306
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
307
    /// implementations of this method.
308
    ///
309
    /// @return message type
310
    virtual uint8_t getType() const = 0;
311
312
    /// @brief Sets message type (e.g. 1 = SOLICIT).
313
    ///
314
    /// @note This is a pure virtual method and must be implemented in
315
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
316
    /// implementations of this method.
317
    ///
318
    /// @param type message type to be set
319
    virtual void setType(uint8_t type) = 0;
320
321
    /// @brief Returns name of the DHCP message.
322
    ///
323
    /// For all unsupported messages the derived classes must return
324
    /// "UNKNOWN".
325
    ///
326
    /// @return Pointer to "const" string containing DHCP message name.
327
    /// The implementations in the derived classes should statically
328
    /// allocate returned strings and the caller must not release the
329
    /// returned pointer.
330
    virtual const char* getName() const = 0;
331
332
    /// @brief Sets transaction-id value.
333
    ///
334
    /// @param transid transaction-id to be set.
335
0
    void setTransid(uint32_t transid) {
336
0
        transid_ = transid;
337
0
    }
338
339
    /// @brief Returns value of transaction-id field.
340
    ///
341
    /// @return transaction-id
342
24.9k
    uint32_t getTransid() const {
343
24.9k
        return (transid_);
344
24.9k
    }
345
346
    /// @brief Checks whether a client belongs to a given class.
347
    ///
348
    /// @param client_class name of the class
349
    /// @return true if belongs
350
    bool inClass(const isc::dhcp::ClientClass& client_class);
351
352
    /// @brief Adds a specified class to the packet.
353
    ///
354
    /// A class can be added to the same packet repeatedly. Any additional
355
    /// attempts to add to a packet the class already added, will be
356
    /// ignored silently.
357
    ///
358
    /// @param client_class name of the class to be added
359
    void addClass(const isc::dhcp::ClientClass& client_class);
360
361
    /// @brief Adds a specified class to the packet's additional class list.
362
    ///
363
    /// A class can be added to the same packet repeatedly. Any additional
364
    /// attempts to add to a packet the class already added, will be
365
    /// ignored silently.
366
    ///
367
    /// @param client_class name of the class to be added
368
    void addAdditionalClass(const isc::dhcp::ClientClass& client_class);
369
370
    /// @brief Adds a specified subclass to the packet.
371
    ///
372
    /// A subclass can be added to the same packet repeatedly. Any additional
373
    /// attempts to add to a packet the subclass already added, will be
374
    /// ignored silently.
375
    ///
376
    /// @param class_def name of the class definition to be added
377
    /// @param subclass name of the subclass to be added
378
    void addSubClass(const isc::dhcp::ClientClass& class_def,
379
                     const isc::dhcp::ClientClass& subclass);
380
381
    /// @brief Returns the class set
382
    ///
383
    /// @note This should be used only to iterate over the class set.
384
    /// @return Classes to which the packet belongs
385
2.79k
    const ClientClasses& getClasses() const {
386
2.79k
        return (classes_);
387
2.79k
    }
388
389
    /// @brief Returns the additional class list.
390
    ///
391
    /// @note This should be used only to iterate over the additional class set.
392
    /// @return The classes to be evaluated.
393
503
    const ClientClasses& getAdditionalClasses() const {
394
503
        return (additional_classes_);
395
503
    }
396
397
    /// @brief Returns the class set including template classes associated with
398
    /// subclasses
399
    ///
400
    /// @note This should be used only to iterate over the class set.
401
    /// @note SubClasses are always last.
402
    /// @return sub class relationships to which the packet belongs.
403
0
    const SubClassRelationContainer& getSubClassesRelations() const {
404
0
        return (subclasses_);
405
0
    }
406
407
    /// @brief Unparsed data (in received packets).
408
    ///
409
    /// @warning This public member is accessed by derived
410
    /// classes directly. One of such derived classes is
411
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
412
    /// behavior must be taken into consideration before making
413
    /// changes to this member such as access scope restriction or
414
    /// data format change etc.
415
    OptionBuffer data_;
416
417
protected:
418
419
    /// @brief Returns the first option of specified type without copying.
420
    ///
421
    /// This method is internally used by the @ref Pkt class and derived
422
    /// classes to retrieve a pointer to the specified option. This
423
    /// method doesn't copy the option before returning it to the
424
    /// caller.
425
    ///
426
    /// @param type Option type.
427
    ///
428
    /// @return Pointer to the option of specified type or NULL pointer
429
    /// if such option is not present.
430
    OptionPtr getNonCopiedOption(const uint16_t type) const;
431
432
    /// @brief Returns all option instances of specified type without
433
    /// copying.
434
    ///
435
    /// This is a variant of @ref getOptions method, which returns a collection
436
    /// of options without copying them. This method should be only used by
437
    /// the @ref Pkt6 class and derived classes. Any external callers should
438
    /// use @ref getOptions which copies option instances before returning them
439
    /// when the @ref Pkt::copy_retrieved_options_ flag is set to true.
440
    ///
441
    /// @param opt_type Option code.
442
    ///
443
    /// @return Collection of options found.
444
    OptionCollection getNonCopiedOptions(const uint16_t opt_type) const;
445
446
public:
447
448
    /// @brief Clones all options so that they can be safely modified.
449
    ///
450
    /// @return A container with option clones.
451
    OptionCollection cloneOptions();
452
453
    /// @brief Returns the first option of specified type.
454
    ///
455
    /// Returns the first option of specified type. Note that in DHCPv6 several
456
    /// instances of the same option are allowed (and frequently used).
457
    ///
458
    /// The options will be only returned after unpack() is called.
459
    ///
460
    /// @param type option type we are looking for
461
    ///
462
    /// @return pointer to found option (or NULL)
463
    OptionPtr getOption(const uint16_t type);
464
465
    /// @brief Returns all instances of specified type.
466
    ///
467
    /// Returns all instances of options of the specified type. DHCPv6 protocol
468
    /// allows (and uses frequently) multiple instances.
469
    ///
470
    /// @param type option type we are looking for
471
    /// @return instance of option collection with requested options
472
    isc::dhcp::OptionCollection getOptions(const uint16_t type);
473
474
    /// @brief Controls whether the option retrieved by the @ref Pkt::getOption
475
    /// should be copied before being returned.
476
    ///
477
    /// Setting this value to true enables the mechanism of copying options
478
    /// retrieved from the packet to prevent accidental modifications of
479
    /// options that shouldn't be modified. The typical use case for this
480
    /// mechanism is to prevent hook library from modifying instance of
481
    /// an option within the packet that would also affect the value for
482
    /// this option within the Kea configuration structures.
483
    ///
484
    /// Kea doesn't copy option instances which it stores in the packet.
485
    /// It merely copy pointers into the packets. Thus, any modification
486
    /// to an option would change the value of this option in the
487
    /// Kea configuration. To prevent this, option copying should be
488
    /// enabled prior to passing the pointer to a packet to a hook library.
489
    ///
490
    /// Not only does this method cause the server to copy
491
    /// an option, but the copied option also replaces the original
492
    /// option within the packet. The option can be then freely modified
493
    /// and the modifications will only affect the instance of this
494
    /// option within the packet but not within the server configuration.
495
    ///
496
    /// @param copy Indicates if the options should be copied when
497
    /// retrieved (if true), or not copied (if false).
498
0
    virtual void setCopyRetrievedOptions(const bool copy) {
499
0
        copy_retrieved_options_ = copy;
500
0
    }
501
502
    /// @brief Returns whether the copying of retrieved options is enabled.
503
    ///
504
    /// Also see @ref setCopyRetrievedOptions.
505
    ///
506
    /// @return true if retrieved options are copied.
507
0
    bool isCopyRetrievedOptions() const {
508
0
        return (copy_retrieved_options_);
509
0
    }
510
511
    /// @brief Update packet timestamp.
512
    ///
513
    /// Updates packet timestamp. This method is invoked
514
    /// by interface manager just before sending or
515
    /// just after receiving it.
516
    /// @throw isc::Unexpected if timestamp update failed
517
    void updateTimestamp();
518
519
    /// @brief Returns packet timestamp.
520
    ///
521
    /// Returns packet timestamp value updated when
522
    /// packet is received or send.
523
    ///
524
    /// @return packet timestamp.
525
0
    const boost::posix_time::ptime& getTimestamp() const {
526
0
        return timestamp_;
527
0
    }
528
529
    /// @brief Set socket receive timestamp.
530
    ///
531
    /// Sets the socket receive timestamp to an arbitrary value.
532
0
    void setTimestamp(boost::posix_time::ptime& timestamp) {
533
0
        timestamp_ = timestamp;
534
0
    }
535
536
    /// @brief Adds an event to the end of the event stack.
537
    ///
538
    /// @param label string identifying the event
539
    /// @param timestamp time at which the event occurred. It is expected
540
    /// to be in UTC/microseconds.  Defaults to the current time.
541
    void addPktEvent(const std::string& label,
542
                     const boost::posix_time::ptime& timestamp = PktEvent::now());
543
544
    /// @brief Adds an event to the end of the event stack with the timestamp
545
    /// specified as a struct timeval.
546
    ///
547
    /// @param label string identifying the event
548
    /// @param timestamp time at which the event occurred. It is expected
549
    /// to be in UTC/microseconds.
550
    void addPktEvent(const std::string& label, const struct timeval& timestamp);
551
552
    /// @brief Updates (or adds) an event in the event stack.
553
    ///
554
    /// Updates the timestamp of the event described by label if it exists in
555
    /// the stack, otherwise it adds the event to the end of the stack.  This
556
    /// is intended to be used for testing.
557
    ///
558
    /// @param label string identifying the event
559
    /// @param timestamp time at which the event occurred. It is expected
560
    /// to be in UTC/microseconds.
561
    void setPktEvent(const std::string& label,
562
                     const boost::posix_time::ptime& timestamp = PktEvent::now());
563
564
    /// @brief Discards contents of the packet event stack.
565
    ///
566
    /// This is provided primarily for test purposes.
567
    void clearPktEvents();
568
569
    /// @brief Fetches the timestamp for a given event in the stack.
570
    ///
571
    /// @param label string identifying the event
572
    /// @return timestamp of the event (UTC/microseconds)
573
    boost::posix_time::ptime getPktEventTime(const std::string& label) const;
574
575
    /// @brief Fetches the current event stack contents.
576
    ///
577
    /// @return reference to the list of events.
578
0
    const std::list<PktEvent>& getPktEvents() {
579
0
        return (events_);
580
0
    }
581
582
    /// @brief Creates a dump of the stack contents to a string for logging.
583
    ///
584
    /// @param verbose when true the dump is more verbose, includes durations
585
    /// between events and spans multiple lines.  Defaults to false.
586
    std::string dumpPktEvents(bool verbose = false) const;
587
588
    /// @brief Copies content of input buffer to output buffer.
589
    ///
590
    /// This is mostly a diagnostic function. It is being used for sending
591
    /// received packet. Received packet is stored in data_, but
592
    /// transmitted data is stored in buffer_out_. If we want to send packet
593
    /// that we just received, a copy between those two buffers is necessary.
594
    void repack();
595
596
    /// @brief Sets remote IP address.
597
    ///
598
    /// @param remote specifies remote address
599
2.10k
    void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
600
2.10k
        remote_addr_ = remote;
601
2.10k
    }
602
603
    /// @brief Returns remote IP address.
604
    ///
605
    /// @return remote address
606
15.0k
    const isc::asiolink::IOAddress& getRemoteAddr() const {
607
15.0k
        return (remote_addr_);
608
15.0k
    }
609
610
    /// @brief Sets local IP address.
611
    ///
612
    /// @param local specifies local address
613
2.10k
    void setLocalAddr(const isc::asiolink::IOAddress& local) {
614
2.10k
        local_addr_ = local;
615
2.10k
    }
616
617
    /// @brief Returns local IP address.
618
    ///
619
    /// @return local address
620
11.4k
    const isc::asiolink::IOAddress& getLocalAddr() const {
621
11.4k
        return (local_addr_);
622
11.4k
    }
623
624
    /// @brief Sets local UDP (and soon TCP) port.
625
    ///
626
    /// This sets a local port, i.e. destination port for recently received
627
    /// packet or a source port for to be transmitted packet.
628
    ///
629
    /// @param local specifies local port
630
2.10k
    void setLocalPort(uint16_t local) {
631
2.10k
        local_port_ = local;
632
2.10k
    }
633
634
    /// @brief Returns local UDP (and soon TCP) port.
635
    ///
636
    /// This sets a local port, i.e. destination port for recently received
637
    /// packet or a source port for to be transmitted packet.
638
    ///
639
    /// @return local port
640
7.59k
    uint16_t getLocalPort() const {
641
7.59k
        return (local_port_);
642
7.59k
    }
643
644
    /// @brief Sets remote UDP (and soon TCP) port.
645
    ///
646
    /// This sets a remote port, i.e. source port for recently received
647
    /// packet or a destination port for to be transmitted packet.
648
    ///
649
    /// @param remote specifies remote port
650
2.10k
    void setRemotePort(uint16_t remote) {
651
2.10k
        remote_port_ = remote;
652
2.10k
    }
653
654
    /// @brief Returns remote port.
655
    ///
656
    /// @return remote port
657
7.59k
    uint16_t getRemotePort() const {
658
7.59k
        return (remote_port_);
659
7.59k
    }
660
661
    /// @brief Sets interface index.
662
    ///
663
    /// @param ifindex specifies interface index.
664
891
    void setIndex(const unsigned int ifindex) {
665
891
        ifindex_ = ifindex;
666
891
    }
667
668
    /// @brief Resets interface index to negative value.
669
0
    void resetIndex() {
670
0
        ifindex_ = UNSET_IFINDEX;
671
0
    }
672
673
    /// @brief Returns interface index.
674
    ///
675
    /// @return interface index
676
8.48k
    int getIndex() const {
677
8.48k
        return (ifindex_);
678
8.48k
    }
679
680
    /// @brief Checks if interface index has been set.
681
    ///
682
    /// @return true if interface index set, false otherwise.
683
0
    bool indexSet() const {
684
0
        return (ifindex_ != UNSET_IFINDEX);
685
0
    }
686
687
    /// @brief Returns interface name.
688
    ///
689
    /// Returns interface name over which packet was received or is
690
    /// going to be transmitted.
691
    ///
692
    /// @return interface name
693
14.2k
    std::string getIface() const {
694
14.2k
        return (iface_);
695
14.2k
    }
696
697
    /// @brief Sets interface name.
698
    ///
699
    /// Sets interface name over which packet was received or is
700
    /// going to be transmitted.
701
    ///
702
    /// @param iface The interface name
703
891
    void setIface(const std::string& iface) {
704
891
        iface_ = iface;
705
891
    }
706
707
    /// @brief Sets remote hardware address.
708
    ///
709
    /// Sets hardware address (MAC) from an existing HWAddr structure.
710
    /// The remote address is a destination address for outgoing
711
    /// packet and source address for incoming packet. When this
712
    /// is an outgoing packet, this address will be used to
713
    /// construct the link layer header.
714
    ///
715
    /// @param hw_addr structure representing HW address.
716
    ///
717
    /// @throw BadValue if addr is null
718
    void setRemoteHWAddr(const HWAddrPtr& hw_addr);
719
720
    /// @brief Sets remote hardware address.
721
    ///
722
    /// Sets the destination hardware (MAC) address for the outgoing packet
723
    /// or source HW address for the incoming packet. When this
724
    /// is an outgoing packet this address will be used to construct
725
    /// the link layer header.
726
    ///
727
    /// @note mac_addr must be a buffer of at least hlen bytes.
728
    ///
729
    /// In a typical case, hlen field would be redundant, as it could
730
    /// be extracted from mac_addr.size(). However, the difference is
731
    /// when running on exotic hardware, like Infiniband, that had
732
    /// MAC addresses 20 bytes long. In that case, hlen is set to zero
733
    /// in DHCPv4.
734
    ///
735
    /// @param htype hardware type (will be sent in htype field)
736
    /// @param hlen hardware length (will be sent in hlen field)
737
    /// @param hw_addr pointer to hardware address
738
    void setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
739
                         const std::vector<uint8_t>& hw_addr);
740
741
    /// @brief Returns the remote HW address obtained from raw sockets.
742
    ///
743
    /// @return remote HW address.
744
15.2k
    HWAddrPtr getRemoteHWAddr() const {
745
15.2k
        return (remote_hwaddr_);
746
15.2k
    }
747
748
    /// @brief Returns MAC address.
749
    ///
750
    /// The difference between this method and getRemoteHWAddr() is that
751
    /// getRemoteHWAddr() returns only what was obtained from raw sockets.
752
    /// This method is more generic and can attempt to obtain MAC from
753
    /// varied sources: raw sockets, client-id, link-local IPv6 address,
754
    /// and various relay options.
755
    ///
756
    /// @note Technically the proper term for this information is a link layer
757
    /// address, but it is frequently referred to MAC or hardware address.
758
    /// Since we're calling the feature "MAC addresses in DHCPv6", we decided
759
    /// to keep the name of getMAC().
760
    ///
761
    /// hw_addr_src takes a combination of bit values specified in
762
    /// HWADDR_SOURCE_* constants.
763
    ///
764
    /// @param hw_addr_src a bitmask that specifies hardware address source
765
    HWAddrPtr getMAC(uint32_t hw_addr_src);
766
767
    /// @brief Virtual destructor.
768
    ///
769
    /// There is nothing to clean up here, but since there are virtual methods,
770
    /// we define virtual destructor to ensure that derived classes will have
771
    /// a virtual one, too.
772
86.8k
    virtual ~Pkt() {
773
86.8k
    }
774
775
    /// @brief Classes this packet belongs to.
776
    ///
777
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
778
    /// iterate over existing classes. Having it public also solves the problem
779
    /// of returned reference lifetime. It is preferred to use @ref inClass and
780
    /// @ref addClass to operate on this field.
781
    ClientClasses classes_;
782
783
    /// @brief Classes to be evaluated during additional class evaluation
784
    ///
785
    /// This list allows hook libraries a way to add classes to the list of classes
786
    /// which will be evaluated during evaluate-additional-classes evaluation.
787
    ///
788
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
789
    /// iterate over additional classes. Having it public also solves the problem
790
    /// of returned reference lifetime. It is preferred to use @ref addAdditionalClass
791
    /// operate on this field.
792
    ///
793
    /// Before output option processing these classes will be evaluated
794
    /// and if evaluation status is true added to the classes_ collection.
795
    ClientClasses additional_classes_;
796
797
    /// @brief SubClasses this packet belongs to.
798
    ///
799
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
800
    /// iterate over existing classes. Having it public also solves the problem
801
    /// of returned reference lifetime. It is preferred to use @ref inClass and
802
    /// @ref addSubClass to operate on this field.
803
    SubClassRelationContainer subclasses_;
804
805
    /// @brief Collection of options present in this message.
806
    ///
807
    /// @warning This public member is accessed by derived
808
    /// classes directly. One of such derived classes is
809
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
810
    /// behavior must be taken into consideration before making
811
    /// changes to this member such as access scope restriction or
812
    /// data format change etc.
813
    isc::dhcp::OptionCollection options_;
814
815
protected:
816
817
    /// @brief Attempts to obtain MAC address from source link-local
818
    /// IPv6 address
819
    ///
820
    /// This method is called from getMAC(HWADDR_SOURCE_IPV6_LINK_LOCAL)
821
    /// and should not be called directly. It is not 100% reliable.
822
    /// The source IPv6 address does not necessarily have to be link-local
823
    /// (may be global or ULA) and even if it's link-local, it doesn't
824
    /// necessarily be based on EUI-64. For example, Windows supports
825
    /// RFC4941, which randomized IID part of the link-local address.
826
    /// If this method fails, it will return NULL.
827
    ///
828
    /// For direct message, it attempts to use remote_addr_ field. For relayed
829
    /// message, it uses peer-addr of the first relay.
830
    ///
831
    /// @note This is a pure virtual method and must be implemented in
832
    /// the derived classes. The @c Pkt6 class have respective implementation.
833
    /// This method is not applicable to DHCPv4.
834
    ///
835
    /// @return hardware address (or NULL)
836
    virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;
837
838
    /// @brief Attempts to obtain MAC address from relay option
839
    /// client-linklayer-addr
840
    ///
841
    /// This method is called from getMAC(HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)
842
    /// and should not be called directly. It will extract the client's
843
    /// MAC/Hardware address from option client_linklayer_addr (RFC6939)
844
    /// inserted by the relay agent closest to the client.
845
    /// If this method fails, it will return NULL.
846
    ///
847
    /// @note This is a pure virtual method and must be implemented in
848
    /// the derived classes. The @c Pkt6 class have respective implementation.
849
    /// This method is not applicable to DHCPv4.
850
    ///
851
    /// @return hardware address (or NULL)
852
    virtual HWAddrPtr getMACFromIPv6RelayOpt() = 0;
853
854
    /// @brief Attempts to obtain MAC address from DUID-LL or DUID-LLT.
855
    ///
856
    /// This method is called from getMAC(HWADDR_SOURCE_DUID) and should not be
857
    /// called directly. It will attempt to extract MAC address information
858
    /// from DUID if its type is LLT or LL. If this method fails, it will
859
    /// return NULL.
860
    ///
861
    /// @note This is a pure virtual method and must be implemented in
862
    /// the derived classes. The @c Pkt6 class have respective implementation.
863
    /// This method is not applicable to DHCPv4.
864
    ///
865
    /// @return hardware address (or NULL)
866
    virtual HWAddrPtr getMACFromDUID() = 0;
867
868
    /// @brief Attempts to obtain MAC address from remote-id relay option.
869
    ///
870
    /// This method is called from getMAC(HWADDR_SOURCE_REMOTE_ID) and should not be
871
    /// called directly. It will attempt to extract MAC address information
872
    /// from remote-id option inserted by a relay agent closest to the client.
873
    /// If this method fails, it will return NULL.
874
    ///
875
    /// @note This is a pure virtual method and must be implemented in
876
    /// the derived classes. The @c Pkt6 class have respective implementation.
877
    /// This method is not applicable to DHCPv4.
878
    ///
879
    /// @return hardware address (or NULL)
880
    virtual HWAddrPtr getMACFromRemoteIdRelayOption() = 0;
881
882
    /// @brief Attempts to convert IPv6 address into MAC.
883
    ///
884
    /// Utility method that attempts to convert link-local IPv6 address to the
885
    /// MAC address. That works only for link-local IPv6 addresses that are
886
    /// based on EUI-64.
887
    ///
888
    /// @note This method uses hardware type of the interface the packet was
889
    /// received on. If you have multiple access technologies in your network
890
    /// (e.g. client connected to WiFi that relayed the traffic to the server
891
    /// over Ethernet), hardware type may be invalid.
892
    ///
893
    /// @param addr IPv6 address to be converted
894
    /// @return hardware address (or NULL)
895
    HWAddrPtr
896
    getMACFromIPv6(const isc::asiolink::IOAddress& addr);
897
898
    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
899
    ///        inserted by the modem itself.
900
    ///
901
    /// This is a generic mechanism for extracting hardware address from the
902
    /// DOCSIS options.
903
    ///
904
    /// @note This is a pure virtual method and must be implemented in
905
    /// the derived classes. The @c Pkt6 class have respective implementation.
906
    /// This method is currently not implemented in DHCPv4.
907
    ///
908
    /// @return hardware address (if necessary DOCSIS suboptions are present)
909
    virtual HWAddrPtr getMACFromDocsisModem() = 0;
910
911
    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
912
    ///        inserted by the CMTS (the relay agent)
913
    ///
914
    /// This is a generic mechanism for extracting hardware address from the
915
    /// DOCSIS options.
916
    ///
917
    /// @note This is a pure virtual method and must be implemented in
918
    /// the derived classes. The @c Pkt6 class have respective implementation.
919
    /// This method is currently not implemented in DHCPv4.
920
    ///
921
    /// @return hardware address (if necessary DOCSIS suboptions are present)
922
    virtual HWAddrPtr getMACFromDocsisCMTS() = 0;
923
924
    /// Transaction-id (32 bits for v4, 24 bits for v6)
925
    uint32_t transid_;
926
927
    /// Name of the network interface the packet was received/to be sent over.
928
    std::string iface_;
929
930
    /// @brief Interface index.
931
    ///
932
    /// Each network interface has assigned an unique ifindex.
933
    /// It is a functional equivalent of a name, but sometimes more useful, e.g.
934
    /// when using odd systems that allow spaces in interface names.
935
    unsigned int ifindex_;
936
937
    /// @brief Local IP (v4 or v6) address.
938
    ///
939
    /// Specifies local IPv4 or IPv6 address. It is a destination address for
940
    /// received packet, and a source address if it packet is being transmitted.
941
    isc::asiolink::IOAddress local_addr_;
942
943
    /// @brief Remote IP address.
944
    ///
945
    /// Specifies local IPv4 or IPv6 address. It is source address for received
946
    /// packet and a destination address for packet being transmitted.
947
    isc::asiolink::IOAddress remote_addr_;
948
949
    /// local TDP or UDP port
950
    uint16_t local_port_;
951
952
    /// remote TCP or UDP port
953
    uint16_t remote_port_;
954
955
    /// Output buffer (used during message transmission)
956
    ///
957
    /// @warning This protected member is accessed by derived
958
    /// classes directly. One of such derived classes is
959
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
960
    /// behavior must be taken into consideration before making
961
    /// changes to this member such as access scope restriction or
962
    /// data format change etc.
963
    isc::util::OutputBuffer buffer_out_;
964
965
    /// @brief Indicates if a copy of the retrieved option should be
966
    /// returned when @ref Pkt::getOption is called.
967
    ///
968
    /// @see the documentation for @ref Pkt::setCopyRetrievedOptions.
969
    bool copy_retrieved_options_;
970
971
    /// packet timestamp
972
    boost::posix_time::ptime timestamp_;
973
974
    // remote HW address (src if receiving packet, dst if sending packet)
975
    HWAddrPtr remote_hwaddr_;
976
977
private:
978
979
    /// @brief Generic method that validates and sets HW address.
980
    ///
981
    /// This is a generic method used by all modifiers of this class
982
    /// which set class members representing HW address.
983
    ///
984
    /// @param htype hardware type.
985
    /// @param hlen hardware length.
986
    /// @param hw_addr pointer to actual hardware address.
987
    /// @param [out] storage pointer to a class member to be modified.
988
    ///
989
    /// @throw isc::OutOfRange if invalid HW address specified.
990
    virtual void setHWAddrMember(const uint8_t htype, const uint8_t hlen,
991
                                 const std::vector<uint8_t>& hw_addr,
992
                                 HWAddrPtr& storage);
993
994
    /// @brief List of timestamped packet events.
995
    std::list<PktEvent> events_;
996
};
997
998
/// @brief A pointer to either Pkt4 or Pkt6 packet
999
typedef boost::shared_ptr<isc::dhcp::Pkt> PktPtr;
1000
1001
/// @brief RAII object enabling duplication of the stored options and restoring
1002
/// the original options on destructor.
1003
///
1004
/// This object enables duplication of the stored options and restoring the
1005
/// original options on destructor. When the object goes out of scope, the
1006
/// initial options are restored. This is applicable in cases when the server is
1007
/// going to invoke a callout (hook library) where the list of options in the
1008
/// packet will be modified. This can also be used to restore the initial
1009
/// suboptions of an option when the suboptions are changed (e.g. when splitting
1010
/// long options and suboptions). The use of RAII object eliminates the need for
1011
/// explicitly copying and restoring the list of options and is safer in case of
1012
/// exceptions thrown by callouts and a presence of multiple exit points.
1013
class ScopedSubOptionsCopy {
1014
public:
1015
1016
    /// @brief Constructor.
1017
    ///
1018
    /// Creates a copy of the initial options on an option.
1019
    ///
1020
    /// @param opt Pointer to the option.
1021
1.33M
    ScopedSubOptionsCopy(const OptionPtr& opt) : option_(opt) {
1022
1.33M
        if (opt) {
1023
1.33M
            options_ = opt->getMutableOptions();
1024
1.33M
        }
1025
1.33M
    }
1026
1027
    /// @brief Destructor.
1028
    ///
1029
    /// Restores the initial options on a packet.
1030
1.33M
    ~ScopedSubOptionsCopy() {
1031
1.33M
        if (option_) {
1032
1.33M
            option_->getMutableOptions() = options_;
1033
1.33M
        }
1034
1.33M
    }
1035
1036
private:
1037
1038
    /// @brief Holds a pointer to the option.
1039
    OptionPtr option_;
1040
1041
    /// @brief Holds the initial options.
1042
    OptionCollection options_;
1043
};
1044
1045
/// @brief RAII object enabling duplication of the stored options and restoring
1046
/// the original options on destructor.
1047
///
1048
/// This object enables duplication of the stored options and restoring the
1049
/// original options on destructor. When the object goes out of scope, the
1050
/// initial options are restored. This is applicable in cases when the server is
1051
/// going to invoke a callout (hook library) where the list of options in the
1052
/// packet will be modified. The use of RAII object eliminates the need for
1053
/// explicitly copying and restoring the list of options and is safer in case of
1054
/// exceptions thrown by callouts and a presence of multiple exit points.
1055
///
1056
/// @tparam PktType Type of the packet, e.g. Pkt4, Pkt6, Pkt4o6.
1057
template<typename PktType>
1058
class ScopedPktOptionsCopy {
1059
public:
1060
1061
    /// @brief Constructor.
1062
    ///
1063
    /// Creates a copy of the initial options on a packet.
1064
    ///
1065
    /// @param pkt Pointer to the packet.
1066
2.00k
    ScopedPktOptionsCopy(PktType& pkt) : pkt_(pkt), options_(pkt.options_) {
1067
2.00k
        pkt_.options_ = pkt_.cloneOptions();
1068
2.00k
    }
1069
1070
    /// @brief Destructor.
1071
    ///
1072
    /// Restores the initial options on a packet.
1073
2.00k
    ~ScopedPktOptionsCopy() {
1074
2.00k
        pkt_.options_ = options_;
1075
2.00k
    }
1076
1077
private:
1078
1079
    /// @brief Holds a reference to the packet.
1080
    PktType& pkt_;
1081
1082
    /// @brief Holds the initial options.
1083
    OptionCollection options_;
1084
};
1085
1086
} // end of namespace isc::dhcp
1087
} // end of namespace isc
1088
1089
#endif