Commit 5ec569cd authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[1237] OS-detection cleanup, Linux-specific IfaceMgr cleanup.

parent aaea8925
......@@ -373,7 +373,7 @@ AC_MSG_CHECKING(OS family)
system=`uname -s`
case $system in
Linux)
OS_TYPE="LINUX"
OS_TYPE="Linux"
CPPFLAGS="$CPPFLAGS -DOS_LINUX"
;;
Darwin | FreeBSD | NetBSD | OpenBSD)
......@@ -390,10 +390,13 @@ case $system in
;;
esac
AC_MSG_RESULT($OS_TYPE)
AM_CONDITIONAL(OS_LINUX, test $OS_TYPE = LINUX)
AM_CONDITIONAL(OS_LINUX, test $OS_TYPE = Linux)
AM_COND_IF([OS_LINUX], [AC_DEFINE([OS_LINUX], [1], [Running on Linux?])])
AM_CONDITIONAL(OS_BSD, test $OS_TYPE = BSD)
AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
AC_MSG_CHECKING(for sa_len in struct sockaddr)
AC_TRY_COMPILE([
......
......@@ -18,14 +18,8 @@ libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
libdhcp_la_SOURCES += dhcp6.h dhcp4.h
libdhcp_la_SOURCES += pkt6.cc pkt6.h
libdhcp_la_SOURCES += pkt4.cc pkt4.h
if OS_LINUX
libdhcp_la_SOURCES += iface_mgr_linux.cc
endif
if OS_BSD
libdhcp_la_SOURCES += iface_mgr_bsd.cc
endif
EXTRA_DIST = README
......
......@@ -12,6 +12,10 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#if defined(OS_BSD)
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
......@@ -20,17 +24,6 @@ using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
#if !defined(OS_BSD)
/// There are several detectIfaces() implementations for specific
/// Operating Systems. This one is specific to BSD. It works on
/// BSD only, so it does not make sense to try to compile it somewhere
/// else. In particular, Makefile.am includes only one iface_mgr_{OS_TYPE}.cc
/// file.
#error "Attempt to compile iface_mgr_bsd.cc on non-BSD system!"
#endif
namespace isc {
void
......@@ -41,3 +34,5 @@ IfaceMgr::detectIfaces() {
}
}
#endif
......@@ -12,6 +12,11 @@
// 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)
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
......@@ -24,29 +29,20 @@ using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
#if !defined(OS_LINUX)
/// There are several detectIfaces() implementations for specific
/// Operating Systems. This one is specific to Linux. It works on
/// Linux only, so it does not make sense to try to compile it somewhere
/// else. In particular, Makefile.am includes only one iface_mgr_{OS_TYPE}.cc
/// file.
#error "Attempt to compile iface_mgr_linux.cc on non-Linux system!"
#endif
/// This is a structure that defines context for netlink connection.
struct rtnl_handle
{
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
struct nlmsg_list
{
struct nlmsg_list *next;
struct nlmsghdr h;
struct nlmsg_list *next;
struct nlmsghdr h;
};
const int sndbuf = 32768;
......@@ -55,53 +51,61 @@ const int rcvbuf = 32768;
namespace isc {
/// @brief Opens netlink socket and initializes handle structure.
///
/// @exception Unexpected Thrown if socket configuration fails.
///
/// @param handle Context will be stored in this structure.
void rtnl_open_socket(struct rtnl_handle& handle) {
// equivalent of rtnl_open
handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (handle.fd < 0) {
isc_throw(Unexpected, "Failed to create NETLINK socket.");
isc_throw(Unexpected, "Failed to create NETLINK socket.");
}
if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
}
if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
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;
if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
isc_throw(Unexpected, "Failed to bind netlink socket.");
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) {
isc_throw(Unexpected, "Getsockname for netlink socket failed.");
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) ) {
isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
(handle.local.nl_family != AF_NETLINK) ) {
isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
}
}
// sends request over NETLINK socket
void rtnl_send_request(struct rtnl_handle& handle,
int family, int type) {
/// @brief Sends request over NETLINK socket.
///
/// @param handle context structure
/// @param family requested information family
/// @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 nlh;
struct rtgenmsg g;
} req;
struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = type;
......@@ -109,98 +113,95 @@ void rtnl_send_request(struct rtnl_handle& handle,
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
req.g.rtgen_family = family;
int status = sendto(handle.fd, (void*)&req, sizeof(req), 0,
(struct sockaddr*)&nladdr, sizeof(nladdr));
(struct sockaddr*)&nladdr, sizeof(nladdr));
if (status<0) {
isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
}
}
void rtnl_store_reply(/*const struct sockaddr_nl *who,*/ struct nlmsghdr *n, struct nlmsg_list** linfo)
/// @brief Appends nlmsg to a list
///
/// @param n a message to be added
/// @param link_info a list
void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
{
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*)
<< " bytes.");
isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
<< " bytes.");
}
memcpy(&h->h, n, n->nlmsg_len);
h->next = NULL;
for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
*lp = h;
// ll_remember_index(who, n, NULL);
// return 0;
}
void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max)
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
if (rta->rta_type <= max)
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len) {
isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
}
}
void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *ainfo) {
void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
uint8_t addr[16];
struct rtattr * rta_tb[IFA_MAX+1];
for ( ;ainfo ; ainfo = ainfo->next) {
struct nlmsghdr *n = &ainfo->h;
struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
// these are not the addresses you are looking for
if ( ifa->ifa_index != iface.ifindex_) {
continue;
}
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)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
iface.addrs_.push_back(a);
/// TODO: Read lifetimes of configured addresses
}
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)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
IOAddress a = IOAddress::from_bytes(AF_INET, addr);
iface.addrs_.push_back(a);
}
for ( ;addr_info ; addr_info = addr_info->next) {
struct nlmsghdr *n = &addr_info->h;
struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
// these are not the addresses you are looking for
if ( ifa->ifa_index != iface.ifindex_) {
continue;
}
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)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
iface.addrs_.push_back(a);
/// TODO: Read lifetimes of configured addresses
}
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)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
IOAddress a = IOAddress::from_bytes(AF_INET, addr);
iface.addrs_.push_back(a);
}
}
}
void rtnl_process_reply(struct rtnl_handle &rth,
struct nlmsg_list *&info) {
void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
struct sockaddr_nl nladdr;
struct iovec iov;
......@@ -212,91 +213,91 @@ void rtnl_process_reply(struct rtnl_handle &rth,
msg.msg_iovlen = 1;
char buf[rcvbuf];
iov.iov_base = buf;
while (1) {
int status;
struct nlmsghdr *h;
iov.iov_len = sizeof(buf);
status = recvmsg(rth.fd, &msg, 0);
if (status < 0) {
if (errno == EINTR)
continue;
isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
}
if (status == 0) {
isc_throw(Unexpected, "EOF while reading netlink socket.");
}
h = (struct nlmsghdr*)buf;
while (NLMSG_OK(h, 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);
continue;
}
if (h->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))) {
// 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.");
} else {
isc_throw(Unexpected, "Netlink reply read error " << -err->error);
}
// never happens we throw before we reach here
return;
}
// store the data
rtnl_store_reply(/*&nladdr,*/ h, &info);
h = NLMSG_NEXT(h, status);
}
if (msg.msg_flags & MSG_TRUNC) {
isc_throw(Unexpected, "Message received over netlink truncated.");
}
if (status) {
isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
}
int status;
struct nlmsghdr *h;
iov.iov_len = sizeof(buf);
status = recvmsg(rth.fd, &msg, 0);
if (status < 0) {
if (errno == EINTR)
continue;
isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
}
if (status == 0) {
isc_throw(Unexpected, "EOF while reading netlink socket.");
}
h = (struct nlmsghdr*)buf;
while (NLMSG_OK(h, 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);
continue;
}
if (h->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))) {
// 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.");
} else {
isc_throw(Unexpected, "Netlink reply read error " << -err->error);
}
// never happens we throw before we reach here
return;
}
// store the data
rtnl_store_reply(h, &info);
h = NLMSG_NEXT(h, status);
}
if (msg.msg_flags & MSG_TRUNC) {
isc_throw(Unexpected, "Message received over netlink truncated.");
}
if (status) {
isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
}
}
}
void release_list(struct nlmsg_list *n) {
/// @brief releases nlmsg list
///
/// @param head first element of the list to be released
void release_list(struct nlmsg_list *head) {
struct nlmsg_list *tmp;
while (n) {
tmp = n->next;
free(n);
n = tmp;
while (head) {
tmp = head->next;
free(head);
head = tmp;
}
}
void
IfaceMgr::detectIfaces() {
void IfaceMgr::detectIfaces() {
cout << "Linux: detecting interfaces." << endl;
struct nlmsg_list *linfo = NULL;
struct nlmsg_list *ainfo = NULL;
struct nlmsg_list *l = NULL;
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;
/* required to display information about interface */
struct ifinfomsg *ifi = NULL;
struct rtattr * tb[IFLA_MAX+1];
// required to display information about interface
struct ifinfomsg* ifi = NULL;
struct rtattr* tb[IFLA_MAX+1];
int len = 0;
memset(tb, 0, sizeof(tb));
memset(&rth,0, sizeof(rth));
......@@ -305,60 +306,66 @@ IfaceMgr::detectIfaces() {
rtnl_open_socket(rth);
// now we have open functional socket, let's use it!
// ask for list of interface...
rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
// process reply
rtnl_process_reply(rth, linfo);
//rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL);
/* 2nd attribute: AF_UNSPEC, AF_INET, AF_INET6 */
// Get reply and store it in link_info list.
rtnl_process_reply(rth, link_info);
// Now ask for list of addresses (AF_UNSPEC = of any family)
rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
rtnl_process_reply(rth, ainfo);
/* build list with interface names */
for (l=linfo; 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);
/* 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])
<< ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
}
memcpy(iface->mac_, RTA_DATA(tb[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;
}
ipaddrs_get(*iface, ainfo);
ifaces_.push_back(*iface);
}
// Get reply and store it in addr_info list.
rtnl_process_reply(rth, 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);
// 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])
<< ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
}
memcpy(iface->mac_, RTA_DATA(tb[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;