Commit abee3e73 authored by Marcin Siodelski's avatar Marcin Siodelski

[master] Merge branch 'trac4300'

parents 12320a72 7741ab8c
...@@ -75,14 +75,15 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len, ...@@ -75,14 +75,15 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
const std::string& hostname, const std::string& hostname,
const std::string& dhcp4_client_classes, const std::string& dhcp4_client_classes,
const std::string& dhcp6_client_classes) const std::string& dhcp6_client_classes)
: hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id), : identifier_type_(identifier_type),
identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
ipv6_subnet_id_(ipv6_subnet_id), ipv6_subnet_id_(ipv6_subnet_id),
ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes), hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
dhcp6_client_classes_(dhcp6_client_classes), host_id_(0), dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
cfg_option4_(), cfg_option6_() { cfg_option4_(), cfg_option6_() {
// Initialize HWAddr or DUID // Initialize host identifier.
setIdentifier(identifier, identifier_len, identifier_type); setIdentifier(identifier, identifier_len, identifier_type);
if (!ipv4_reservation.isV4Zero()) { if (!ipv4_reservation.isV4Zero()) {
...@@ -97,14 +98,15 @@ Host::Host(const std::string& identifier, const std::string& identifier_name, ...@@ -97,14 +98,15 @@ Host::Host(const std::string& identifier, const std::string& identifier_name,
const std::string& hostname, const std::string& hostname,
const std::string& dhcp4_client_classes, const std::string& dhcp4_client_classes,
const std::string& dhcp6_client_classes) const std::string& dhcp6_client_classes)
: hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id), : identifier_type_(IDENT_HWADDR),
identifier_value_(), ipv4_subnet_id_(ipv4_subnet_id),
ipv6_subnet_id_(ipv6_subnet_id), ipv6_subnet_id_(ipv6_subnet_id),
ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()), ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes), hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
dhcp6_client_classes_(dhcp6_client_classes), host_id_(0), dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) { cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
// Initialize HWAddr or DUID // Initialize host identifier.
setIdentifier(identifier, identifier_name); setIdentifier(identifier, identifier_name);
if (!ipv4_reservation.isV4Zero()) { if (!ipv4_reservation.isV4Zero()) {
...@@ -115,51 +117,36 @@ Host::Host(const std::string& identifier, const std::string& identifier_name, ...@@ -115,51 +117,36 @@ Host::Host(const std::string& identifier, const std::string& identifier_name,
const std::vector<uint8_t>& const std::vector<uint8_t>&
Host::getIdentifier() const { Host::getIdentifier() const {
if (hw_address_) { return (identifier_value_);
return (hw_address_->hwaddr_);
} else if (duid_) {
return (duid_->getDuid());
}
static std::vector<uint8_t> empty_vector;
return (empty_vector);
} }
Host::IdentifierType Host::IdentifierType
Host::getIdentifierType() const { Host::getIdentifierType() const {
if (hw_address_) { return (identifier_type_);
return (IDENT_HWADDR);
}
return (IDENT_DUID);
} }
std::string HWAddrPtr
Host::getIdentifierAsText() const { Host::getHWAddress() const {
std::string txt; return ((identifier_type_ == IDENT_HWADDR) ?
if (hw_address_) { HWAddrPtr(new HWAddr(identifier_value_, HTYPE_ETHER)) : HWAddrPtr());
txt = getIdentifierAsText(IDENT_HWADDR, &hw_address_->hwaddr_[0], }
hw_address_->hwaddr_.size());
} else if (duid_) { DuidPtr
txt = getIdentifierAsText(IDENT_DUID, &duid_->getDuid()[0], Host::getDuid() const {
duid_->getDuid().size()); return ((identifier_type_ == IDENT_DUID) ?
} else { DuidPtr(new DUID(identifier_value_)) : DuidPtr());
txt = "(none)"; }
}
return (txt);
std::string
Host::getIdentifierAsText() const {
return (getIdentifierAsText(identifier_type_, &identifier_value_[0],
identifier_value_.size()));
} }
std::string std::string
Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value, Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
const size_t length) { const size_t length) {
// Length 0 doesn't make sense.
if (length == 0) {
isc_throw(BadValue, "invalid length 0 of the host identifier while"
" converting the identifier to a textual form");
}
// Convert identifier into <type>=<value> form. // Convert identifier into <type>=<value> form.
std::ostringstream s; std::ostringstream s;
switch (type) { switch (type) {
...@@ -169,46 +156,98 @@ Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value, ...@@ -169,46 +156,98 @@ Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
case IDENT_DUID: case IDENT_DUID:
s << "duid"; s << "duid";
break; break;
case IDENT_CIRCUIT_ID:
s << "circuit-id";
break;
default: default:
isc_throw(BadValue, "requested conversion of the unsupported" // This should never happen actually, unless we add new identifier
" identifier into textual form"); // and forget to add a case for it above.
s << "(invalid-type)";
} }
std::vector<uint8_t> vec(value, value + length); std::vector<uint8_t> vec(value, value + length);
s << "=" << util::encode::encodeHex(vec); s << "=" << (length > 0 ? util::encode::encodeHex(vec) : "(null)");
return (s.str()); return (s.str());
} }
std::string
Host::getIdentifierName(const IdentifierType& type) {
switch (type) {
case Host::IDENT_HWADDR:
return ("hw-address");
case Host::IDENT_DUID:
return ("duid");
case Host::IDENT_CIRCUIT_ID:
return ("circuit-id");
default:
;
}
return ("(unknown)");
}
void void
Host::setIdentifier(const uint8_t* identifier, const size_t len, Host::setIdentifier(const uint8_t* identifier, const size_t len,
const IdentifierType& type) { const IdentifierType& type) {
switch (type) { if (len < 1) {
case IDENT_HWADDR: isc_throw(BadValue, "invalid client identifier length 0");
hw_address_ = HWAddrPtr(new HWAddr(identifier, len, HTYPE_ETHER));
duid_.reset();
break;
case IDENT_DUID:
duid_ = DuidPtr(new DUID(identifier, len));
hw_address_.reset();
break;
default:
isc_throw(isc::BadValue, "invalid client identifier type '"
<< static_cast<int>(type) << "' when creating host "
" instance");
} }
identifier_type_ = type;
identifier_value_.assign(identifier, identifier + len);
} }
void void
Host::setIdentifier(const std::string& identifier, const std::string& name) { Host::setIdentifier(const std::string& identifier, const std::string& name) {
// HW address and DUID are special cases because they are typically
// specified as values with colons between consecutive octets. Thus,
// we use the HWAddr and DUID classes to validate them and to
// convert them into binary format.
if (name == "hw-address") { if (name == "hw-address") {
hw_address_ = HWAddrPtr(new HWAddr(HWAddr::fromText(identifier))); HWAddr hwaddr(HWAddr::fromText(identifier));
duid_.reset(); identifier_type_= IDENT_HWADDR;
identifier_value_ = hwaddr.hwaddr_;
} else if (name == "duid") { } else if (name == "duid") {
duid_ = DuidPtr(new DUID(DUID::fromText(identifier))); identifier_type_ = IDENT_DUID;
hw_address_.reset(); DUID duid(DUID::fromText(identifier));
identifier_value_ = duid.getDuid();
} else { } else {
isc_throw(isc::BadValue, "invalid client identifier type '" if (name == "circuit-id") {
<< name << "' when creating host instance"); identifier_type_ = IDENT_CIRCUIT_ID;
} else {
isc_throw(isc::BadValue, "invalid client identifier type '"
<< name << "' when creating host instance");
}
// Here we're converting values other than DUID and HW address. These
// values can either be specified as strings of hexadecimal digits or
// strings in quotes. The latter are copied to a vector excluding quote
// characters.
// Try to convert the values in quotes into a vector of ASCII codes.
// If the identifier lacks opening and closing quote, this will return
// an empty value, in which case we'll try to decode it as a string of
// hexadecimal digits.
std::vector<uint8_t> binary = util::str::quotedStringToBinary(identifier);
if (binary.empty()) {
try {
util::encode::decodeHex(identifier, binary);
} catch (...) {
// The string doesn't match any known pattern, so we have to
// report an error at this point.
isc_throw(isc::BadValue, "invalid host identifier value '"
<< identifier << "'");
}
}
// Successfully decoded the identifier, so let's use it.
identifier_value_.swap(binary);
} }
} }
......
...@@ -179,7 +179,8 @@ public: ...@@ -179,7 +179,8 @@ public:
/// DHCPv6 client's DUID are supported. /// DHCPv6 client's DUID are supported.
enum IdentifierType { enum IdentifierType {
IDENT_HWADDR, IDENT_HWADDR,
IDENT_DUID IDENT_DUID,
IDENT_CIRCUIT_ID
}; };
/// @brief Constructor. /// @brief Constructor.
...@@ -222,11 +223,19 @@ public: ...@@ -222,11 +223,19 @@ public:
/// is useful in cases when the reservation is specified in the server /// is useful in cases when the reservation is specified in the server
/// configuration file, where: /// configuration file, where:
/// - MAC address is specified as: "01:02:03:04:05:06" /// - MAC address is specified as: "01:02:03:04:05:06"
/// - DUID is specified as: "010203040506abcd" /// - DUID can be specified as: "01:02:03:04:05:06:ab:cd" or "010203040506abcd".
/// - Other identifiers are specified as: "010203040506abcd" or as
/// "'some identfier'".
///
/// In case of identifiers other than HW address and DUID it is possible to use
/// textual representation, e.g. 'some identifier', which is converted to a
/// vector of ASCII codes representing characters in a given string, excluding
/// quotes. This is useful in cases when specific identifiers, e.g. circuit-id
/// are manually assigned user friendly values.
/// ///
/// @param identifier Identifier in the textual format. The expected formats /// @param identifier Identifier in the textual format. The expected formats
/// for the hardware address and DUID have been shown above. /// for the hardware address and other identifiers are provided above.
/// @param identifier_name One of "hw-address" or "duid" /// @param identifier_name One of "hw-address", "duid", "circuit-id".
/// @param ipv4_subnet_id Identifier of the IPv4 subnet to which the host /// @param ipv4_subnet_id Identifier of the IPv4 subnet to which the host
/// is connected. /// is connected.
/// @param ipv6_subnet_id Identifier of the IPv6 subnet to which the host /// @param ipv6_subnet_id Identifier of the IPv6 subnet to which the host
...@@ -251,13 +260,10 @@ public: ...@@ -251,13 +260,10 @@ public:
/// @brief Replaces currently used identifier with a new identifier. /// @brief Replaces currently used identifier with a new identifier.
/// ///
/// This method initializes hardware address or DUID (@c hw_address_ or /// This method sets a new identifier type and value for a host.
/// @c duid_ respectively). The other (not initialized member) is
/// deallocated.
///
/// This method is called by the @c Host constructor. /// This method is called by the @c Host constructor.
/// ///
/// @param identifier Pointer to the new identifier in the textual format. /// @param identifier Pointer to a buffer holding an identifier.
/// @param len Length of the identifier that the @c identifier points to. /// @param len Length of the identifier that the @c identifier points to.
/// @param type Identifier type. /// @param type Identifier type.
/// ///
...@@ -267,14 +273,11 @@ public: ...@@ -267,14 +273,11 @@ public:
/// @brief Replaces currently used identifier with a new identifier. /// @brief Replaces currently used identifier with a new identifier.
/// ///
/// This method initializes hardware address or DUID (@c hw_address_ or /// This method sets a new identifier type and value for a host.
/// @c duid_ respectively). The other (not initialized member) is
/// deallocated.
///
/// This method is called by the @c Host constructor. /// This method is called by the @c Host constructor.
/// ///
/// @param identifier Pointer to the new identifier in the textual format. /// @param identifier Reference to a new identifier in the textual format.
/// @param name One of "hw-address" or "duid". /// @param name One of "hw-address", "duid", "circuit-id".
/// ///
/// @throw BadValue if the identifier is invalid. /// @throw BadValue if the identifier is invalid.
void setIdentifier(const std::string& identifier, const std::string& name); void setIdentifier(const std::string& identifier, const std::string& name);
...@@ -283,30 +286,32 @@ public: ...@@ -283,30 +286,32 @@ public:
/// ///
/// @return Pointer to the @c HWAddr structure or null if the reservation /// @return Pointer to the @c HWAddr structure or null if the reservation
/// is not associated with a hardware address. /// is not associated with a hardware address.
HWAddrPtr getHWAddress() const { HWAddrPtr getHWAddress() const;
return (hw_address_);
}
/// @brief Returns DUID for which the reservations are made. /// @brief Returns DUID for which the reservations are made.
/// ///
/// @return Pointer to the @c DUID structure or null if the reservation /// @return Pointer to the @c DUID structure or null if the reservation
/// is not associated with a DUID. /// is not associated with a DUID.
DuidPtr getDuid() const { DuidPtr getDuid() const;
return (duid_);
}
/// @brief Returns the identifier (MAC or DUID) in binary form. /// @brief Returns the identifier in a binary form.
/// @return const reference to MAC or DUID in vector<uint8_t> form ///
/// @return const reference to a vector<uint8_t> holding an identifier
/// value.
const std::vector<uint8_t>& getIdentifier() const; const std::vector<uint8_t>& getIdentifier() const;
/// @brief Returns the identifier type. /// @brief Returns the identifier type.
/// @return the identifier type ///
IdentifierType getIdentifierType() const; IdentifierType getIdentifierType() const;
/// @brief Returns host identifier (mac or DUID) in printer friendly form. /// @brief Returns host identifier in a textual form.
/// @return text form of the identifier, including (duid= or mac=). ///
/// @return Identifier in the form of <type>=<value>.
std::string getIdentifierAsText() const; std::string getIdentifierAsText() const;
/// @brief Returns name of the identifier of a specified type.
static std::string getIdentifierName(const IdentifierType& type);
/// @brief Returns host identifier in textual form. /// @brief Returns host identifier in textual form.
/// ///
/// @param type Identifier type. /// @param type Identifier type.
...@@ -487,12 +492,10 @@ private: ...@@ -487,12 +492,10 @@ private:
void addClientClassInternal(ClientClasses& classes, void addClientClassInternal(ClientClasses& classes,
const std::string& class_name); const std::string& class_name);
/// @brief Pointer to the hardware address associated with the reservations /// @brief Identifier type.
/// for the host. IdentifierType identifier_type_;
HWAddrPtr hw_address_; /// @brief Vector holding identifier value.
/// @brief Pointer to the DUID associated with the reservations for the std::vector<uint8_t> identifier_value_;
/// host.
DuidPtr duid_;
/// @brief Subnet identifier for the DHCPv4 client. /// @brief Subnet identifier for the DHCPv4 client.
SubnetID ipv4_subnet_id_; SubnetID ipv4_subnet_id_;
/// @brief Subnet identifier for the DHCPv6 client. /// @brief Subnet identifier for the DHCPv6 client.
......
...@@ -42,6 +42,11 @@ const size_t CLIENT_CLASSES_MAX_LEN = 255; ...@@ -42,6 +42,11 @@ const size_t CLIENT_CLASSES_MAX_LEN = 255;
/// in the Client FQDN %Option (see RFC4702 and RFC4704). /// in the Client FQDN %Option (see RFC4702 and RFC4704).
const size_t HOSTNAME_MAX_LEN = 255; const size_t HOSTNAME_MAX_LEN = 255;
/// @brief Numeric value representing last supported identifier.
///
/// This value is used to validate whether the identifier type stored in
/// a database is within bounds. of supported identifiers.
const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::IDENT_CIRCUIT_ID);
/// @brief Prepared MySQL statements used by the backend to insert and /// @brief Prepared MySQL statements used by the backend to insert and
/// retrieve hosts from the database. /// retrieve hosts from the database.
...@@ -299,42 +304,21 @@ public: ...@@ -299,42 +304,21 @@ public:
bind_[0].is_unsigned = MLM_TRUE; bind_[0].is_unsigned = MLM_TRUE;
// dhcp_identifier : VARBINARY(128) NOT NULL // dhcp_identifier : VARBINARY(128) NOT NULL
// Check which of the identifiers is used and set values accordingly dhcp_identifier_length_ = host->getIdentifier().size();
if (host->getDuid()) { memcpy(static_cast<void*>(dhcp_identifier_buffer_),
dhcp_identifier_length_ = host->getDuid()->getDuid().size(); &(host->getIdentifier())[0],
bind_[1].buffer_type = MYSQL_TYPE_BLOB; host->getIdentifier().size());
bind_[1].buffer = reinterpret_cast<char*>
(const_cast<uint8_t*>(&(host->getDuid()->getDuid()[0]))); bind_[1].buffer_type = MYSQL_TYPE_BLOB;
bind_[1].buffer_length = dhcp_identifier_length_; bind_[1].buffer = dhcp_identifier_buffer_;
bind_[1].length = &dhcp_identifier_length_; bind_[1].buffer_length = dhcp_identifier_length_;
bind_[1].length = &dhcp_identifier_length_;
} else if (host->getHWAddress()){
dhcp_identifier_length_ = host->getHWAddress()->hwaddr_.size();
bind_[1].buffer_type = MYSQL_TYPE_BLOB;
bind_[1].buffer = reinterpret_cast<char*>
(&(host->getHWAddress()->hwaddr_[0]));
bind_[1].buffer_length = dhcp_identifier_length_;
bind_[1].length = &dhcp_identifier_length_;
} else {
isc_throw(DbOperationError, "Host object doesn't contain any"
" identifier which can be used to retrieve information"
" from the database about its static reservations");
}
// dhcp_identifier_type : TINYINT NOT NULL // dhcp_identifier_type : TINYINT NOT NULL
// Check which of the identifier types is used and set values accordingly dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
if (host->getHWAddress()) { bind_[2].buffer_type = MYSQL_TYPE_TINY;
dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
bind_[2].buffer_type = MYSQL_TYPE_TINY; bind_[2].is_unsigned = MLM_TRUE;
bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
bind_[2].is_unsigned = MLM_TRUE;
} else if (host->getDuid()) {
dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
bind_[2].buffer_type = MYSQL_TYPE_TINY;
bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
bind_[2].is_unsigned = MLM_TRUE;
}
// dhcp4_subnet_id : INT UNSIGNED NULL // dhcp4_subnet_id : INT UNSIGNED NULL
// Can't take an address of intermediate object, so let's store it // Can't take an address of intermediate object, so let's store it
...@@ -495,25 +479,14 @@ public: ...@@ -495,25 +479,14 @@ public:
/// @return Host Pointer to a @ref HostPtr object holding a pointer to the /// @return Host Pointer to a @ref HostPtr object holding a pointer to the
/// @ref Host object returned. /// @ref Host object returned.
HostPtr retrieveHost() { HostPtr retrieveHost() {
// Check if the identifier stored in the database is correct.
// Set the dhcp identifier type in a variable of the appropriate data type, if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
// which has been initialized with an arbitrary (but valid) value.
Host::IdentifierType type = Host::IDENT_HWADDR;
switch (dhcp_identifier_type_) {
case 0:
type = Host::IDENT_HWADDR;
break;
case 1:
type = Host::IDENT_DUID;
break;
default:
isc_throw(BadValue, "invalid dhcp identifier type returned: " isc_throw(BadValue, "invalid dhcp identifier type returned: "
<< static_cast<int>(dhcp_identifier_type_) << static_cast<int>(dhcp_identifier_type_));
<< ". Only 0 or 1 are supported.");
} }
// Set the dhcp identifier type in a variable of the appropriate data type.
Host::IdentifierType type =
static_cast<Host::IdentifierType>(dhcp_identifier_type_);
// Set DHCPv4 subnet ID to the value returned. If NULL returned, set to 0. // Set DHCPv4 subnet ID to the value returned. If NULL returned, set to 0.
SubnetID ipv4_subnet_id(0); SubnetID ipv4_subnet_id(0);
......
...@@ -26,7 +26,7 @@ GenericHostDataSourceTest::GenericHostDataSourceTest() ...@@ -26,7 +26,7 @@ GenericHostDataSourceTest::GenericHostDataSourceTest()
GenericHostDataSourceTest::~GenericHostDataSourceTest() { GenericHostDataSourceTest::~GenericHostDataSourceTest() {
} }
std::string std::vector<uint8_t>
GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) { GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) {
/// @todo: Consider moving this somewhere to lib/testutils. /// @todo: Consider moving this somewhere to lib/testutils.
...@@ -34,15 +34,6 @@ GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) { ...@@ -34,15 +34,6 @@ GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) {
// if you need to enter MySQL queries by hand. // if you need to enter MySQL queries by hand.
static uint8_t hwaddr[] = {65, 66, 67, 68, 69, 70}; static uint8_t hwaddr[] = {65, 66, 67, 68, 69, 70};
stringstream tmp;
for (int i = 0; i < sizeof(hwaddr); ++i) {
if (i) {
tmp << ":";
}
tmp << std::setw(2) << std::hex << std::setfill('0')
<< static_cast<unsigned int>(hwaddr[i]);
}
if (new_identifier) { if (new_identifier) {
// Increase the address for the next time we use it. // Increase the address for the next time we use it.
// This is primitive, but will work for 65k unique // This is primitive, but will work for 65k unique
...@@ -52,46 +43,38 @@ GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) { ...@@ -52,46 +43,38 @@ GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) {
hwaddr[sizeof(hwaddr) - 2]++; hwaddr[sizeof(hwaddr) - 2]++;
} }
} }
return (tmp.str()); return (std::vector<uint8_t>(hwaddr, hwaddr + sizeof(hwaddr)));
} }
std::string std::vector<uint8_t>
GenericHostDataSourceTest::generateDuid(const bool new_identifier) { GenericHostDataSourceTest::generateIdentifier(const bool new_identifier) {
/// @todo: Consider moving this somewhere to lib/testutils. /// @todo: Consider moving this somewhere to lib/testutils.
// Let's use something that is easily printable. That's convenient // Let's use something that is easily printable. That's convenient
// if you need to enter MySQL queries by hand. // if you need to enter MySQL queries by hand.
static uint8_t duid[] = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74 }; static uint8_t ident[] = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74 };
stringstream tmp; // Increase the identifier for the next time we use it.
for (int i = 0; i < sizeof(duid); ++i) {
tmp << std::setw(2) << std::hex << std::setfill('0')
<< static_cast<unsigned int>(duid[i]);
}
// Increase the DUID for the next time we use it.
// This is primitive, but will work for 65k unique // This is primitive, but will work for 65k unique
// DUIDs. // identifiers.
if (new_identifier) { if (new_identifier) {
duid[sizeof(duid) - 1]++; ident[sizeof(ident) - 1]++;
if (duid[sizeof(duid) - 1] == 0) { if (ident[sizeof(ident) - 1] == 0) {
duid[sizeof(duid) - 2]++; ident[sizeof(ident) - 2]++;
} }
} }