BIND 10 trac1528, updated. cc72ac90f8858d6e24da1816d5e70d38b0db8e9c [1528] Gentoo interface detection fix.
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Feb 21 19:28:38 UTC 2012
The branch, trac1528 has been updated
via cc72ac90f8858d6e24da1816d5e70d38b0db8e9c (commit)
via d53ab0d663c6ead51bdde2d2b16365ff6adab424 (commit)
via 6207354d2f7830511008d49e36aac658ec378348 (commit)
via 44b275d27c8089167cff36195d1102f54c99047c (commit)
via 7f9ddb4963264b1ac022e7d550930dfbc825d71c (commit)
from 6ab057e9726a30b87909a09026c797e99cd935d7 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit cc72ac90f8858d6e24da1816d5e70d38b0db8e9c
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Feb 21 20:27:49 2012 +0100
[1528] Gentoo interface detection fix.
commit d53ab0d663c6ead51bdde2d2b16365ff6adab424
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Feb 21 20:13:59 2012 +0100
[1528] Linux-specific interface detection improved.
- converted C-style table of rtattr message to boost
- added doxygen comments
- renamed several variables to something more meaningful
commit 6207354d2f7830511008d49e36aac658ec378348
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Feb 21 17:34:20 2012 +0100
[1528] Minor improvements:
- Linux-specific methods are now in private namespace
- using new/delete rather than malloc/free
commit 44b275d27c8089167cff36195d1102f54c99047c
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Feb 21 17:26:15 2012 +0100
[1528] Memory leaks fixed, more reasonable names, socket is now closed.
commit 7f9ddb4963264b1ac022e7d550930dfbc825d71c
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Feb 21 16:56:58 2012 +0100
[1528] nlmsg_list converted to C++ style.
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp/iface_mgr_linux.cc | 264 +++++++++++++++++-------------
src/lib/dhcp/tests/iface_mgr_unittest.cc | 31 +++-
src/lib/dhcp/tests/pkt4_unittest.cc | 2 +
3 files changed, 182 insertions(+), 115 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 8b19c27..38b2134 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -12,7 +12,6 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-
#include <config.h>
#if defined(OS_LINUX)
@@ -22,33 +21,46 @@
#include <net/if.h>
#include <linux/rtnetlink.h>
+#include <boost/array.hpp>
using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
-/// This is a structure that defines context for netlink connection.
+namespace {
+/// @brief Holds pointers to netlink messages.
+///
+/// netlink (a Linux interface for getting information about network
+/// interfaces) uses memory aliasing. There are many nlmsg structures
+/// with varying size that all have the same nlmsghdr. The only
+/// reasonable way to represent this in C++ is to use vector of
+/// pointers to nlmsghdr (the common structure).
+typedef vector<struct nlmsghdr*> NetlinkMessages;
+
+/// @brief Holds information about interface or address attributes.
+///
+/// Note that to get addres info, a shorter (IFA_MAX rather than IFLA_MAX)
+/// table could be used, but we will use the bigger one anyway to
+/// make any code reuse
+typedef boost::array<struct rtattr*, IFLA_MAX+1> RTattribs;
+
+/// @brief This structure defines context for netlink connection.
struct rtnl_handle
{
- int fd;
+ rtnl_handle() :fd(0), seq(0), dump(0) {
+ memset(&local, 0, sizeof(struct sockaddr_nl));
+ memset(&peer, 0, sizeof(struct sockaddr_nl));
+ }
+ int fd; // netlink file descriptor
struct sockaddr_nl local;
struct sockaddr_nl peer;
- __u32 seq;
- __u32 dump;
-};
-
-struct nlmsg_list
-{
- struct nlmsg_list *next;
- struct nlmsghdr h;
+ __u32 seq;
+ __u32 dump;
};
-const int sndbuf = 32768;
-const int rcvbuf = 32768;
-
-namespace isc {
-
+const size_t sndbuf = 32768;
+const size_t rcvbuf = 32768;
/// @brief Opens netlink socket and initializes handle structure.
///
@@ -97,8 +109,8 @@ void rtnl_open_socket(struct rtnl_handle& handle) {
/// @param type request type (RTM_GETLINK or RTM_GETADDR)
void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
struct {
- struct nlmsghdr nlh;
- struct rtgenmsg g;
+ struct nlmsghdr netlink_header;
+ struct rtgenmsg generic;
} req;
struct sockaddr_nl nladdr;
@@ -106,12 +118,12 @@ void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
nladdr.nl_family = AF_NETLINK;
memset(&req, 0, sizeof(req));
- req.nlh.nlmsg_len = sizeof(req);
- req.nlh.nlmsg_type = type;
- req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
- req.nlh.nlmsg_pid = 0;
- req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
- req.g.rtgen_family = family;
+ req.netlink_header.nlmsg_len = sizeof(req);
+ req.netlink_header.nlmsg_type = type;
+ req.netlink_header.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.netlink_header.nlmsg_pid = 0;
+ req.netlink_header.nlmsg_seq = handle.dump = ++handle.seq;
+ req.generic.rtgen_family = family;
int status = sendto(handle.fd, (void*)&req, sizeof(req), 0,
(struct sockaddr*)&nladdr, sizeof(nladdr));
@@ -121,34 +133,40 @@ void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
}
}
-/// @brief Appends nlmsg to a list
+/// @brief Appends nlmsg to a storage.
///
-/// @param n a message to be added
-/// @param link_info a list
-void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
+/// @param storage a vector that holds netlink messages
+/// @param msg a netlink message to be added
+void rtnl_store_reply(NetlinkMessages& storage, const struct nlmsghdr *msg)
{
- struct nlmsg_list *h;
- struct nlmsg_list **lp;
-
- h = (nlmsg_list*)malloc(n->nlmsg_len+sizeof(void*));
- if (h == NULL) {
- isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
+ // we need to make a copy of this message. We really can't allocate
+ // nlmsghdr directly as it is only part of the structure. There are
+ // many message types with varying lengths and a common header.
+ struct nlmsghdr* copy = reinterpret_cast<struct nlmsghdr*>(new char[(msg->nlmsg_len)]);
+ memcpy(copy, msg, msg->nlmsg_len);
+ if (copy == NULL) {
+ isc_throw(Unexpected, "Failed to allocate " << msg->nlmsg_len
<< " bytes.");
}
- memcpy(&h->h, n, n->nlmsg_len);
- h->next = NULL;
-
- for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
- *lp = h;
+ // push_back copies only pointer content, not the pointed object
+ storage.push_back(copy);
}
-void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+/// @brief Parses rtattr message.
+///
+/// Netlink can return a concatenated list of rtattr structures. This function iterates
+/// over that list and stores pointers to those messages in flat array (table).
+///
+/// @param table rtattr messages will be stored here
+/// @param rta pointer to first rtattr object
+/// @param len length (in bytes) of concatenated rtattr list.
+void parse_rtattr(RTattribs& table, struct rtattr * rta, int len)
{
- memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ std::fill(table.begin(), table.end(), static_cast<struct rtattr*>(NULL));
while (RTA_OK(rta, len)) {
- if (rta->rta_type <= max)
- tb[rta->rta_type] = rta;
+ if (rta->rta_type <= table.size()-1)
+ table[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len) {
@@ -156,13 +174,17 @@ void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
}
}
-void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
+/// @brief Parses addr_info and appends appropriate addresses to Iface object.
+///
+/// @param iface interface representation (addresses will be added here)
+/// @param addr_info collection of parsed netlink messages
+void ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info) {
uint8_t addr[16];
- struct rtattr * rta_tb[IFA_MAX+1];
+ RTattribs rta_tb;
- for ( ;addr_info ; addr_info = addr_info->next) {
- struct nlmsghdr *n = &addr_info->h;
- struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
+ for (NetlinkMessages::const_iterator msg = addr_info.begin();
+ msg != addr_info.end(); ++msg) {
+ struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(*msg);
// these are not the addresses you are looking for
if ( ifa->ifa_index != iface.getIndex()) {
@@ -170,8 +192,8 @@ void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
}
if ( ifa->ifa_family == AF_INET6 ) {
- memset(rta_tb, 0, sizeof(rta_tb));
- parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+ std::fill(rta_tb.begin(), rta_tb.end(), static_cast<struct rtattr*>(NULL));
+ parse_rtattr(rta_tb, IFA_RTA(ifa), (*msg)->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
@@ -185,8 +207,8 @@ void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
}
if ( ifa->ifa_family == AF_INET ) {
- memset(rta_tb, 0, sizeof(rta_tb));
- parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+ std::fill(rta_tb.begin(), rta_tb.end(), static_cast<struct rtattr*>(NULL));
+ parse_rtattr(rta_tb, IFA_RTA(ifa), (*msg)->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
@@ -199,8 +221,11 @@ void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
}
}
-
-void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
+/// @brief Processes reply received over netlink socket.
+///
+/// @param rth netlink parameters
+/// @param info received netlink messages will be stored here
+void rtnl_process_reply(const struct rtnl_handle& rth, NetlinkMessages& info) {
struct sockaddr_nl nladdr;
struct iovec iov;
@@ -214,9 +239,9 @@ void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
char buf[rcvbuf];
iov.iov_base = buf;
- while (1) {
+ while (true) {
int status;
- struct nlmsghdr *h;
+ struct nlmsghdr* header;
iov.iov_len = sizeof(buf);
status = recvmsg(rth.fd, &msg, 0);
@@ -231,25 +256,25 @@ void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
isc_throw(Unexpected, "EOF while reading netlink socket.");
}
- h = (struct nlmsghdr*)buf;
- while (NLMSG_OK(h, status)) {
+ header = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(header, status)) {
// why we received this anyway?
if (nladdr.nl_pid != 0 ||
- h->nlmsg_pid != rth.local.nl_pid ||
- h->nlmsg_seq != rth.dump) {
- h = NLMSG_NEXT(h, status);
+ header->nlmsg_pid != rth.local.nl_pid ||
+ header->nlmsg_seq != rth.dump) {
+ header = NLMSG_NEXT(header, status);
continue;
}
- if (h->nlmsg_type == NLMSG_DONE) {
+ if (header->nlmsg_type == NLMSG_DONE) {
// end of message
return;
}
- if (h->nlmsg_type == NLMSG_ERROR) {
- struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
- if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ if (header->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(header);
+ if (header->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
// we are really out of luck here. We can't even say what is
// wrong as error message is truncated. D'oh.
isc_throw(Unexpected, "Netlink reply read failed.");
@@ -261,9 +286,9 @@ void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
}
// store the data
- rtnl_store_reply(h, &info);
+ rtnl_store_reply(info, header);
- h = NLMSG_NEXT(h, status);
+ header = NLMSG_NEXT(header, status);
}
if (msg.msg_flags & MSG_TRUNC) {
isc_throw(Unexpected, "Message received over netlink truncated.");
@@ -274,89 +299,102 @@ void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
}
}
-/// @brief releases nlmsg list
+/// @brief releases nlmsg structure
///
-/// @param head first element of the list to be released
-void release_list(struct nlmsg_list *head) {
- struct nlmsg_list *tmp;
- while (head) {
- tmp = head->next;
- free(head);
- head = tmp;
+/// @param messages first element of the list to be released
+void release_list(NetlinkMessages& messages) {
+ // let's free local copies of stored messages
+ for (NetlinkMessages::iterator msg = messages.begin(); msg != messages.end(); ++msg) {
+ delete (*msg);
}
+
+ // ang get rid of the message pointers as well
+ messages.clear();
}
+} // end of anonymous namespace
+
+namespace isc {
+
+namespace dhcp {
+
void IfaceMgr::detectIfaces() {
cout << "Linux: detecting interfaces." << endl;
- struct nlmsg_list* link_info = NULL; // link info list
- struct nlmsg_list* addr_info = NULL; // address info list
- struct nlmsg_list* l = NULL;
- struct rtnl_handle rth;
+ NetlinkMessages link_info; // link info
+ NetlinkMessages addr_info; // address info
+ struct rtnl_handle handle; // socket descriptors other rtnl-related parameters
// required to display information about interface
- struct ifinfomsg* ifi = NULL;
- struct rtattr* tb[IFLA_MAX+1];
+ struct ifinfomsg* interface_info = NULL;
+ RTattribs attribs_table; // table with address attributes
int len = 0;
- memset(tb, 0, sizeof(tb));
- memset(&rth,0, sizeof(rth));
+ std::fill(attribs_table.begin(), attribs_table.end(), static_cast<struct rtattr*>(NULL));
// open socket
- rtnl_open_socket(rth);
+ rtnl_open_socket(handle);
// now we have open functional socket, let's use it!
// ask for list of interface...
- rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
+ rtnl_send_request(handle, AF_PACKET, RTM_GETLINK);
// Get reply and store it in link_info list.
- rtnl_process_reply(rth, link_info);
+ rtnl_process_reply(handle, link_info);
// Now ask for list of addresses (AF_UNSPEC = of any family)
- rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
+ rtnl_send_request(handle, AF_UNSPEC, RTM_GETADDR);
// Get reply and store it in addr_info list.
- rtnl_process_reply(rth, addr_info);
+ rtnl_process_reply(handle, addr_info);
// Now build list with interface names
- for (l=link_info; l; l = l->next) {
- ifi = (ifinfomsg*)NLMSG_DATA(&l->h);
- len = (&l->h)->nlmsg_len;
- len -= NLMSG_LENGTH(sizeof(*ifi));
- parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
- Iface* iface = new Iface(string( (char*)RTA_DATA(tb[IFLA_IFNAME])), ifi->ifi_index);
-
- iface->hardware_type_ = ifi->ifi_type;
- iface->setFlags(ifi->ifi_flags);
-
- iface->mac_len_ = 0;
- memset(iface->mac_, 0, IfaceMgr::MAX_MAC_LEN);
+ for (NetlinkMessages::iterator msg = link_info.begin(); msg != link_info.end(); ++msg) {
+ interface_info = static_cast<ifinfomsg*>(NLMSG_DATA(*msg));
+ len = (*msg)->nlmsg_len;
+ len -= NLMSG_LENGTH(sizeof(*interface_info));
+ parse_rtattr(attribs_table, IFLA_RTA(interface_info), len);
+
+ // valgrind reports *possible* memory leak in the line below, but I do believe that this
+ // report is bogus. Nevertheless, I've split the whole interface definition into
+ // three separate steps for easier debugging.
+ const char* tmp = static_cast<const char*>(RTA_DATA(attribs_table[IFLA_IFNAME]));
+ string iface_name(tmp); // <--- (probably bogus) valgrind warning here
+ Iface iface = Iface(iface_name, interface_info->ifi_index);
+
+ iface.hardware_type_ = interface_info->ifi_type;
+ iface.setFlags(interface_info->ifi_flags);
+
+ iface.mac_len_ = 0;
+ memset(iface.mac_, 0, IfaceMgr::MAX_MAC_LEN);
// Does inetface has LL_ADDR?
- if (tb[IFLA_ADDRESS]) {
- iface->mac_len_ = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
- if (iface->mac_len_ > IfaceMgr::MAX_MAC_LEN) {
- iface->mac_len_ = 0;
- isc_throw(Unexpected, "Interface " << iface->getFullName()
- << " was detected to have link address of length " << RTA_PAYLOAD(tb[IFLA_ADDRESS])
+ if (attribs_table[IFLA_ADDRESS]) {
+ iface.mac_len_ = RTA_PAYLOAD(attribs_table[IFLA_ADDRESS]);
+ if (iface.mac_len_ > IfaceMgr::MAX_MAC_LEN) {
+ iface.mac_len_ = 0;
+ isc_throw(Unexpected, "Interface " << iface.getFullName()
+ << " was detected to have link address of length "
+ << RTA_PAYLOAD(attribs_table[IFLA_ADDRESS])
<< ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
}
- memcpy(iface->mac_, RTA_DATA(tb[IFLA_ADDRESS]), iface->mac_len_);
+ memcpy(iface.mac_, RTA_DATA(attribs_table[IFLA_ADDRESS]), iface.mac_len_);
}
else {
// Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
// dereference it in this manner
// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
- iface->mac_len_ = 0;
+ iface.mac_len_ = 0;
}
- ipaddrs_get(*iface, addr_info);
- ifaces_.push_back(*iface);
+ ipaddrs_get(iface, addr_info);
+ ifaces_.push_back(iface);
}
release_list(link_info);
release_list(addr_info);
printIfaces();
+
+ close(handle.fd);
}
/// @brief sets flag_*_ fields.
@@ -375,6 +413,8 @@ void IfaceMgr::Iface::setFlags(uint32_t flags) {
flag_broadcast_ = flags & IFF_BROADCAST;
}
-}
+} // end of isc::dhcp namespace
+
+} // end of isc namespace
#endif // if defined(LINUX)
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 5d666b3..8175a19 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -708,6 +708,11 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
if (offset == string::npos) {
isc_throw(BadValue, "Malformed output of ifconfig");
}
+
+ // ifconfig in Gentoo prints out eth0: instead of eth0
+ if (line[offset-1] == ':') {
+ offset--;
+ }
string name = line.substr(0, offset);
// sadly, ifconfig does not return ifindex
@@ -729,13 +734,33 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
if (line.find("inet6") != string::npos) {
// IPv6 address
- string addr = line.substr(line.find("inet6")+12, string::npos);
+ string addr;
+ if (line.find("addr:", line.find("inet6")) != string::npos) {
+ // Ubuntu style format: inet6 addr: ::1/128 Scope:Host
+ addr = line.substr(line.find("addr:")+6, string::npos);
+ } else {
+ // Gentoo style format: inet6 fe80::6ef0:49ff:fe96:ba17 prefixlen 64 scopeid 0x20<link>
+ addr = line.substr(line.find("inet6")+6, string::npos);
+ }
+
+ // handle Ubuntu format: inet6 addr: fe80::f66d:4ff:fe96:58f2/64 Scope:Link
addr = addr.substr(0, addr.find("/"));
+
+ // handle inet6 fe80::ca3a:35ff:fed4:8f1d prefixlen 64 scopeid 0x20<link>
+ addr = addr.substr(0, addr.find(" "));
IOAddress a(addr);
iface->addAddress(a);
} else if(line.find("inet") != string::npos) {
// IPv4 address
- string addr = line.substr(line.find("inet")+10, string::npos);
+ string addr;
+ if (line.find("addr:", line.find("inet")) != string::npos) {
+ // Ubuntu style format: inet addr:127.0.0.1 Mask:255.0.0.0
+ addr = line.substr(line.find("addr:")+5, string::npos);
+ } else {
+ // Gentoo style format: inet 10.53.0.4 netmask 255.255.255.0
+ addr = line.substr(line.find("inet")+5, string::npos);
+ }
+
addr = addr.substr(0, addr.find_first_of(" "));
IOAddress a(addr);
iface->addAddress(a);
@@ -786,7 +811,7 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
// list of interfaces parsed from ifconfig
IfaceMgr::IfaceCollection parsedIfaces;
- EXPECT_NO_THROW(
+ ASSERT_NO_THROW(
parse_ifconfig(textFile, parsedIfaces);
);
unlink(textFile.c_str());
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 9936ca4..bed8c2f 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -594,6 +594,8 @@ TEST(Pkt4Test, metaFields) {
EXPECT_EQ(42, pkt->getIndex());
EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
+
+ delete pkt;
}
} // end of anonymous namespace
More information about the bind10-changes
mailing list