using namespace isc::asiolink;
using namespace isc::dhcp;
+BOOST_STATIC_ASSERT(IFLA_MAX>=IFA_MAX);
+
namespace {
+
+/// @brief This class offers utility methods for netlink connection.
+///
+/// See IfaceMgr::detectIfaces() (Linux implementation) for example
+/// usage.
+class Netlink
+{
+public:
/// @brief Holds pointers to netlink messages.
///
/// netlink (a Linux interface for getting information about network
/// as nlmsghdr with followed variable number of bytes that are
/// message-specific. The only reasonable way to represent this in
/// C++ is to use vector of pointers to nlmsghdr (the common structure).
-typedef vector<nlmsghdr*> NetlinkMessages;
+ typedef vector<nlmsghdr*> NetlinkMessages;
/// @brief Holds pointers to interface or address attributes.
///
/// unsigned short<>rta_type;
/// };
///
-typedef boost::array<struct rtattr*, IFLA_MAX+1> RTattribPtrs;
-
-BOOST_STATIC_ASSERT(IFLA_MAX>=IFA_MAX);
+ typedef boost::array<struct rtattr*, IFLA_MAX+1> RTattribPtrs;
-/// @brief This structure defines context for netlink connection.
-struct rtnl_handle
-{
- rtnl_handle() :fd(-1), seq(0), dump(0) {
- memset(&local, 0, sizeof(struct sockaddr_nl));
- memset(&peer, 0, sizeof(struct sockaddr_nl));
+ Netlink() :fd_(-1), seq_(0), dump_(0) {
+ memset(&local_, 0, sizeof(struct sockaddr_nl));
+ memset(&peer_, 0, sizeof(struct sockaddr_nl));
}
- ~rtnl_handle() {
- if (fd != -1) {
- close(fd);
- }
+ ~Netlink() {
+ rtnl_close_socket();
}
- int fd; // netlink file descriptor
- sockaddr_nl local; // local and remote addresses
- sockaddr_nl peer;
- __u32 seq; // counter used for generating unique sequence numbers
- __u32 dump; // number of expected message response
+ void rtnl_open_socket();
+ void rtnl_send_request(int family, int type);
+ void rtnl_store_reply(NetlinkMessages& storage, const nlmsghdr* msg);
+ void parse_rtattr(RTattribPtrs& table, rtattr* rta, int len);
+ void ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info);
+ void rtnl_process_reply(NetlinkMessages& info);
+ void release_list(NetlinkMessages& messages);
+ void rtnl_close_socket();
+
+private:
+ int fd_; // netlink file descriptor
+ sockaddr_nl local_; // local and remote addresses
+ sockaddr_nl peer_;
+ uint32_t seq_; // counter used for generating unique sequence numbers
+ uint32_t dump_; // number of expected message response
};
const size_t sndbuf = 32768;
/// @exception Unexpected Thrown if socket configuration fails.
///
/// @param handle Context will be stored in this structure.
-void rtnl_open_socket(struct rtnl_handle& handle) {
+void Netlink::rtnl_open_socket() {
// equivalent of rtnl_open
- handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (handle.fd < 0) {
+ fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (fd_ < 0) {
isc_throw(Unexpected, "Failed to create NETLINK socket.");
}
- if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
+ if (setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
}
- if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
+ if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
}
- memset(&handle.local, 0, sizeof(handle.local));
- handle.local.nl_family = AF_NETLINK;
- handle.local.nl_groups = 0;
+ local_.nl_family = AF_NETLINK;
+ local_.nl_groups = 0;
- if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
+ if (bind(fd_, (struct sockaddr*)&local_, sizeof(local_)) < 0) {
isc_throw(Unexpected, "Failed to bind netlink socket.");
}
- socklen_t addr_len = sizeof(handle.local);
- if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
+ socklen_t addr_len = sizeof(local_);
+ if (getsockname(fd_, (struct sockaddr*)&local_, &addr_len) < 0) {
isc_throw(Unexpected, "Getsockname for netlink socket failed.");
}
// just 2 sanity checks and we are done
- if ( (addr_len != sizeof(handle.local)) ||
- (handle.local.nl_family != AF_NETLINK) ) {
+ if ( (addr_len != sizeof(local_)) ||
+ (local_.nl_family != AF_NETLINK) ) {
isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
}
}
+void Netlink::rtnl_close_socket() {
+ if (fd_ != -1) {
+ close(fd_);
+ }
+ fd_ = -1;
+}
+
/// @brief Sends request over NETLINK socket.
///
/// @param handle structure that contains necessary information about netlink
/// @param family requested information family
/// @param type request type (RTM_GETLINK or RTM_GETADDR)
-void rtnl_send_request(rtnl_handle& handle, int family, int type) {
+void Netlink::rtnl_send_request(int family, int type) {
struct {
nlmsghdr netlink_header;
rtgenmsg generic;
// not really useful, as we send a single request and get a single
// response at a time, but still it better to obey man page suggestion
// and just set this to monotonically increasing numbers.
- handle.seq++;
+ seq_++;
// this will be used to finding correct response (responses
// sent by kernel are supposed to have the same sequence number
// as the request we sent)
- handle.dump = handle.seq;
+ dump_ = seq_;
memset(&req, 0, sizeof(req));
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.seq;
+ req.netlink_header.nlmsg_seq = seq_;
req.generic.rtgen_family = family;
- int status = sendto(handle.fd, static_cast<void*>(&req), sizeof(req), 0,
+ int status = sendto(fd_, static_cast<void*>(&req), sizeof(req), 0,
static_cast<struct sockaddr*>(static_cast<void*>(&nladdr)),
sizeof(nladdr));
///
/// @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)
+void Netlink::rtnl_store_reply(NetlinkMessages& storage, const struct nlmsghdr *msg)
{
// 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
/// @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(RTattribPtrs& table, struct rtattr * rta, int len)
+void Netlink::parse_rtattr(RTattribPtrs& table, struct rtattr * rta, int len)
{
std::fill(table.begin(), table.end(), static_cast<struct rtattr*>(NULL));
// RTA_OK and RTA_NEXT() are macros defined in linux/rtnetlink.h
///
/// @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) {
+void Netlink::ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info) {
uint8_t addr[V6ADDRESS_LEN];
RTattribPtrs rta_tb;
///
/// Make sure to release this memory, e.g. using release_info() function.
///
-/// @param context netlink parameters
/// @param info received netlink messages will be stored here
-void rtnl_process_reply(const rtnl_handle& handle, NetlinkMessages& info) {
-
+void Netlink::rtnl_process_reply(NetlinkMessages& info) {
sockaddr_nl nladdr;
iovec iov;
msghdr msg;
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
while (true) {
- int status = recvmsg(handle.fd, &msg, 0);
+ int status = recvmsg(fd_, &msg, 0);
if (status < 0) {
if (errno == EINTR) {
// with a sequence number we are expecting. Ignore, and
// look at the next one.
if (nladdr.nl_pid != 0 ||
- header->nlmsg_pid != handle.local.nl_pid ||
- header->nlmsg_seq != handle.dump) {
+ header->nlmsg_pid != local_.nl_pid ||
+ header->nlmsg_seq != dump_) {
header = NLMSG_NEXT(header, status);
continue;
}
/// @brief releases nlmsg structure
///
/// @param messages first element of the list to be released
-void release_list(NetlinkMessages& messages) {
+void Netlink::release_list(NetlinkMessages& messages) {
// let's free local copies of stored messages
for (NetlinkMessages::iterator msg = messages.begin(); msg != messages.end(); ++msg) {
delete (*msg);
cout << "Linux: detecting interfaces." << endl;
// Copies of netlink messages about links will be stored here.
- NetlinkMessages link_info;
+ Netlink::NetlinkMessages link_info;
// Copies of netlink messages about addresses will be stored here.
- NetlinkMessages addr_info;
+ Netlink::NetlinkMessages addr_info;
// Socket descriptors and other rtnl-related parameters.
- struct rtnl_handle handle;
+ Netlink nl;
- RTattribPtrs attribs_table; // table with pointers to address attributes
+ // table with pointers to address attributes
+ Netlink::RTattribPtrs attribs_table;
std::fill(attribs_table.begin(), attribs_table.end(),
static_cast<struct rtattr*>(NULL));
// open socket
- rtnl_open_socket(handle);
+ nl.rtnl_open_socket();
// now we have open functional socket, let's use it!
// ask for list of network interfaces...
- rtnl_send_request(handle, AF_PACKET, RTM_GETLINK);
+ nl.rtnl_send_request(AF_PACKET, RTM_GETLINK);
// Get reply and store it in link_info list:
// response is received as with any other socket - just a series
// buffer, copy each message to a newly allocated memory and
// store pointers to it in link_info. This allocated memory will
// be released later. See release_info(link_info) below.
- rtnl_process_reply(handle, link_info);
+ nl.rtnl_process_reply(link_info);
// Now ask for list of addresses (AF_UNSPEC = of any family)
// Let's repeat, but this time ask for any addresses.
// That includes IPv4, IPv6 and any other address families that
// are happen to be supported by this system.
- rtnl_send_request(handle, AF_UNSPEC, RTM_GETADDR);
+ nl.rtnl_send_request(AF_UNSPEC, RTM_GETADDR);
// Get reply and store it in addr_info list.
// Again, we will allocate new memory and store messages in
// addr_info. It will be released later using release_info(addr_info).
- rtnl_process_reply(handle, addr_info);
+ nl.rtnl_process_reply(addr_info);
// Now build list with interface names
- for (NetlinkMessages::iterator msg = link_info.begin(); msg != link_info.end(); ++msg) {
+ for (Netlink::NetlinkMessages::iterator msg = link_info.begin();
+ msg != link_info.end(); ++msg) {
// required to display information about interface
struct ifinfomsg* interface_info = static_cast<ifinfomsg*>(NLMSG_DATA(*msg));
int len = (*msg)->nlmsg_len;
len -= NLMSG_LENGTH(sizeof(*interface_info));
- parse_rtattr(attribs_table, IFLA_RTA(interface_info), len);
+ nl.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.
+ // but it 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
+ string iface_name(tmp); // <--- bogus valgrind warning here
Iface iface = Iface(iface_name, interface_info->ifi_index);
iface.setHWType(interface_info->ifi_type);
// dereference it in this manner
}
- ipaddrs_get(iface, addr_info);
+ nl.ipaddrs_get(iface, addr_info);
ifaces_.push_back(iface);
}
- release_list(link_info);
- release_list(addr_info);
+ nl.release_list(link_info);
+ nl.release_list(addr_info);
printIfaces();
}
);
}
+TEST_F(IfaceMgrTest, iface_methods) {
+ IfaceMgr::Iface iface("foo", 1234);
+
+ iface.setHWType(42);
+ EXPECT_EQ(42, iface.getHWType());
+
+ uint8_t mac[IfaceMgr::MAX_MAC_LEN+10];
+ for (int i = 0; i < IfaceMgr::MAX_MAC_LEN + 10; i++)
+ mac[i] = 255 - i;
+
+ EXPECT_EQ("foo", iface.getName());
+ EXPECT_EQ(1234, iface.getIndex());
+
+ // MAC is too long. Exception should be thrown and
+ // MAC length should not be set.
+ EXPECT_THROW(
+ iface.setMac(mac, IfaceMgr::MAX_MAC_LEN + 1),
+ OutOfRange
+ );
+
+ // MAC length should stay not set as excep
+ EXPECT_EQ(0, iface.getMacLen());
+
+ // Setting maximum length MAC should be ok.
+ iface.setMac(mac, IfaceMgr::MAX_MAC_LEN);
+
+ // For some reason constants cannot be used directly in EXPECT_EQ
+ // as this produces linking error.
+ size_t len = IfaceMgr::MAX_MAC_LEN;
+ EXPECT_EQ(len, iface.getMacLen());
+ EXPECT_EQ(0, memcmp(mac, iface.getMac(), iface.getMacLen()));
+}
+
TEST_F(IfaceMgrTest, socketInfo) {
// check that socketinfo for IPv4 socket is functional
/// of text that ifconfig prints and robustness to handle slight differences
/// in ifconfig output.
///
+/// @todo: Consider using isc::util::str::tokens here.
+///
/// @param textFile name of a text file that holds output of ifconfig -a
/// @param ifaces empty list of interfaces to be filled
void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifaces) {
}
// ifconfig in Gentoo prints out eth0: instead of eth0
- if (line[offset-1] == ':') {
+ if (line[offset - 1] == ':') {
offset--;
}
string name = line.substr(0, offset);
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);
+ 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);
+ addr = line.substr(line.find("inet6") + 6, string::npos);
}
// handle Ubuntu format: inet6 addr: fe80::f66d:4ff:fe96:58f2/64 Scope:Link
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);
+ 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 = line.substr(line.find("inet") + 5, string::npos);
}
addr = addr.substr(0, addr.find_first_of(" "));