Browse Source

initial commit, need to figure out why setLinkUp/Down returns NLE_PERM even though it works

testing
Brad Parker 10 years ago
commit
83843743bd
  1. 5
      .gitignore
  2. 2331
      Doxyfile
  3. 2
      EXAMPLES.md
  4. 10
      LICENSE
  5. 32
      README.md
  6. 553
      interface.cpp
  7. 31
      interface.h
  8. 335
      neighbor.cpp
  9. 26
      neighbor.h
  10. 17
      qiproute.pro
  11. 18
      qiproute_global.h
  12. 98
      utils.cpp
  13. 21
      utils.h

5
.gitignore

@ -0,0 +1,5 @@
moc_*
*.o
*.swp
*.so*
Makefile

2331
Doxyfile

File diff suppressed because it is too large

2
EXAMPLES.md

@ -0,0 +1,2 @@
# QIPRoute Examples

10
LICENSE

@ -0,0 +1,10 @@
Copyright (c) 2014, Brad Parker
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

32
README.md

@ -0,0 +1,32 @@
# QIPRoute
This library is designed to make it extremely easy to work with ethernet interfaces of a Linux system.
Currently supported functions are:
> add/delete/list IP addresses
> get physical link state
> set admin link state up/down
> search/list ARP entries
## Compiling
Dependencies are Qt and libnl3.
Tested with Qt versions 4.8.5 and 5.2.0, and libnl 3.2.23.
To compile, simply run qmake followed by make. The resulting shared library can then be included in your project by linking it with -lQIPRoute.
## API Reference
Online documentation is available at: http://fiveforty.net/QIPRoute/doc/
Or you can generate the documentation with the included Doxyfile by running "doxygen" in the source directory, which will output HTML files under a new doc/ directory.
## Examples
Please see the EXAMPLES file.
## LICENSE
This project is licensed under the 2-clause BSD license.

553
interface.cpp

@ -0,0 +1,553 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
extern "C" {
#include <pthread.h>
#include <netlink/socket.h>
#include <linux/netlink.h>
#include <netlink/netlink.h>
#include <netlink/route/addr.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
}
#include "interface.h"
#include "utils.h"
#include <iostream>
#include <QStringList>
#include <QTimer>
#include <QtCore/QCoreApplication>
#include <QDir>
#include <QtDebug>
#define BUFLEN 65535
#define ADDRBUFLEN BUFLEN
/*!
\class Interface
\brief The Interface class is used to manage IP addresses associated with an interface.
*/
/*!
Constructs an Interface object. All operations will be limited to the specified \a interface.
*/
Interface::Interface(QString interface) :
QObject(0),
m_interface(interface),
m_index(-1)
{
m_index = Utils::interfaceIndex(interface);
}
Interface::~Interface() {
}
/*!
Set the interface's administrative link state to up. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error.
*/
int Interface::setLinkUp() {
struct rtnl_link *link, *link_orig = NULL;
struct nl_sock *sock = NULL;
QByteArray interfaceArray = m_interface.toLatin1();
const char *interface = interfaceArray.constData();
if((sock = Utils::connect())) {
if(rtnl_link_get_kernel(sock, 0, interface, &link_orig) < 0) {
std::cerr << "error looking up interface" << std::endl;
nl_socket_free(sock);
}else{
if(!link_orig) {
std::cerr << "can't get link" << std::endl;
nl_socket_free(sock);
return 1;
}
link = rtnl_link_alloc();
if(!link) {
std::cerr << "can't alloc link" << std::endl;
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
int err = 0;
rtnl_link_set_flags(link, IFF_UP);
if((err = rtnl_link_change(sock, link_orig, link, 0)) < 0) {
//error = nl_geterror(err);
rtnl_link_put(link);
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
rtnl_link_put(link);
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
}
return 0;
}
/*!
Set the interface's administrative link state to down. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error.
*/
int Interface::setLinkDown() {
struct rtnl_link *link, *link_orig = NULL;
struct nl_sock *sock = NULL;
QByteArray interfaceArray = m_interface.toLatin1();
const char *interface = interfaceArray.constData();
if((sock = Utils::connect())) {
if(rtnl_link_get_kernel(sock, 0, interface, &link_orig) < 0) {
std::cerr << "error looking up interface" << std::endl;
nl_socket_free(sock);
}else{
if(!link_orig) {
std::cerr << "can't get link" << std::endl;
nl_socket_free(sock);
return 1;
}
link = rtnl_link_alloc();
if(!link) {
std::cerr << "can't alloc link" << std::endl;
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
int err = 0;
rtnl_link_unset_flags(link, IFF_UP);
if((err = rtnl_link_change(sock, link_orig, link, 0)) < 0) {
//error = nl_geterror(err);
rtnl_link_put(link);
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
rtnl_link_put(link);
rtnl_link_put(link_orig);
nl_socket_free(sock);
return 1;
}
}
return 0;
}
/*!
Add an IP address to the interface using the CIDR notation, e.g. 192.168.1.1/24. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error.
*/
int Interface::addAddress(QString addressStr) const {
struct nl_sock *sock = NULL;
QByteArray addressArrayNoMask = addressStr.left(addressStr.indexOf('/')).toLatin1();
QByteArray addressArray = addressStr.toLatin1();
QString bitmask = addressStr.right(2);
const char *addressDataNoMask = addressArrayNoMask.constData();
const char *addressData = addressArray.constData();
in_addr_t brdInt = inet_addr(addressDataNoMask);
struct in_addr brd = { .s_addr = brdInt };
int bitlen = bitmask.toInt();
if(bitlen <= 30) {
for(int i = 31; i >= bitlen; i--) {
brd.s_addr |= htonl(1 << (31 - i));
}
}
if(!(sock = Utils::connect())) {
return 1;
}
struct rtnl_addr *addr = rtnl_addr_alloc();
if(!addr) {
nl_socket_free(sock);
return 1;
}
struct nl_addr *address = NULL;
struct nl_addr *bcast = NULL;
struct nl_msg *result = NULL;
int parseResult = nl_addr_parse(addressData, AF_UNSPEC, &address);
if(parseResult != 0) {
std::cerr << "could not parse address" << std::endl;
rtnl_addr_put(addr);
nl_socket_free(sock);
return 1;
}
int parseBroadcastResult = nl_addr_parse(inet_ntoa(brd), AF_INET, &bcast);
if(parseBroadcastResult != 0) {
std::cerr << "could not parse broadcast address" << std::endl;
rtnl_addr_put(addr);
nl_addr_put(address);
nl_socket_free(sock);
return 1;
}
rtnl_addr_set_ifindex(addr, m_index);
rtnl_addr_set_local(addr, address);
rtnl_addr_set_broadcast(addr, bcast);
int ret = rtnl_addr_build_add_request(addr, 0, &result);
if(ret != 0) {
std::cerr << "could not build add request" << std::endl;
rtnl_addr_put(addr);
nl_addr_put(address);
nl_addr_put(bcast);
nl_socket_free(sock);
return 1;
}
if(rtnl_addr_add(sock, addr, 0) != 0) {
std::cerr << "could not add address" << std::endl;
rtnl_addr_put(addr);
nl_addr_put(address);
nl_addr_put(bcast);
nlmsg_free(result);
nl_socket_free(sock);
return 1;
}
rtnl_addr_put(addr);
nl_addr_put(address);
nl_addr_put(bcast);
nlmsg_free(result);
nl_socket_free(sock);
return 0;
}
/*!
Delete an IP address from the interface using the CIDR notation, e.g. 192.168.1.1/24. Requires that the CAP_NET_ADMIN capability be set on the application binary, or run as root (\a not \a recommended). Returns 0 on success, 1 on error.
*/
int Interface::deleteAddress(QString addressStr) const {
struct nl_sock *sock = NULL;
QByteArray addressArrayNoMask = addressStr.left(addressStr.indexOf('/')).toLatin1();
QByteArray addressArray = addressStr.toLatin1();
QString bitmask = addressStr.right(2);
const char *addressDataNoMask = addressArrayNoMask.constData();
const char *addressData = addressArray.constData();
in_addr_t brdInt = inet_addr(addressDataNoMask);
struct in_addr brd = { .s_addr = brdInt };
int bitlen = bitmask.toInt();
if(bitlen <= 30) {
for(int i = 31; i >= bitlen; i--) {
brd.s_addr |= htonl(1 << (31 - i));
}
}
if(!(sock = Utils::connect())) {
return 1;
}
struct rtnl_addr *addr = rtnl_addr_alloc();
struct nl_addr *address = NULL;
struct nl_addr *bcast = NULL;
struct nl_msg *result = NULL;
if(!addr) {
std::cerr << "could not allocate address" << std::endl;
nl_socket_free(sock);
return 1;
}
int parseResult = nl_addr_parse(addressData, AF_UNSPEC, &address);
if(parseResult != 0) {
std::cerr << "could not parse address" << std::endl;
rtnl_addr_put(addr);
nl_socket_free(sock);
return 1;
}
int parseBroadcastResult = nl_addr_parse(inet_ntoa(brd), AF_INET, &bcast);
if(parseBroadcastResult != 0) {
std::cerr << "could not parse broadcast address" << std::endl;
rtnl_addr_put(addr);
nl_addr_put(address);
nl_socket_free(sock);
return 1;
}
rtnl_addr_set_ifindex(addr, m_index);
rtnl_addr_set_local(addr, address);
rtnl_addr_set_broadcast(addr, bcast);
int ret = rtnl_addr_build_delete_request(addr, 0, &result);
if(ret != 0) {
std::cerr << "could not build add request" << std::endl;
rtnl_addr_put(addr);
nl_addr_put(address);
nl_addr_put(bcast);
nl_socket_free(sock);
return 1;
}
if(rtnl_addr_delete(sock, addr, 0) != 0) {
std::cerr << "could not delete address" << std::endl;
rtnl_addr_put(addr);
nlmsg_free(result);
nl_addr_put(address);
nl_addr_put(bcast);
nl_socket_free(sock);
return 1;
}
rtnl_addr_put(addr);
nl_addr_put(address);
nl_addr_put(bcast);
nlmsg_free(result);
nl_socket_free(sock);
return 0;
}
/*!
Returns true if the interface is administratively up, otherwise false if it is administratively down or if there was an error retrieving the status information. If there was an error, *ok will be set to false, otherwise it will be true.
*/
bool Interface::hasAdminLink(bool *ok) {
struct rtnl_link *link = NULL;
struct nl_sock *sock = NULL;
QByteArray interfaceArray = m_interface.toLatin1();
const char *interface = interfaceArray.constData();
bool carrier = false;
if((sock = Utils::connect())) {
if(rtnl_link_get_kernel(sock, 0, interface, &link) < 0) {
std::cerr << "error looking up interface" << std::endl;
nl_socket_free(sock);
}else{
unsigned int flags = rtnl_link_get_flags(link);
carrier = (flags & IFF_UP) == IFF_UP;
rtnl_link_put(link);
nl_socket_free(sock);
if(ok != NULL) {
*ok = true;
}
return carrier;
}
}
if(ok != NULL) {
*ok = false;
}
return false;
}
/*!
Returns true if the interface is up and running (link-layer is up), and false if the link/interface is down, or if there was an error retrieving the status information. If there was an error, *ok will be set to false, otherwise it will be true.
*/
bool Interface::hasCarrier(bool *ok) {
struct rtnl_link *link = NULL;
struct nl_sock *sock = NULL;
QByteArray interfaceArray = m_interface.toLatin1();
const char *interface = interfaceArray.constData();
bool carrier = false;
if((sock = Utils::connect())) {
if(rtnl_link_get_kernel(sock, 0, interface, &link) < 0) {
std::cerr << "error looking up interface" << std::endl;
nl_socket_free(sock);
}else{
unsigned int flags = rtnl_link_get_flags(link);
carrier = flags & IFF_RUNNING;
rtnl_link_put(link);
nl_socket_free(sock);
if(ok != NULL) {
*ok = true;
}
return carrier;
}
}
if(ok != NULL) {
*ok = false;
}
return false;
}
/*!
Returns a list of assigned IP addresses on the interface. All addresses include the subnet mask as CIDR notation (e.g. 192.168.1.1/24).
*/
QStringList Interface::addresses() const {
struct nl_sock *sock = NULL;
struct rtnl_addr *addr = NULL;
struct nl_cache *addr_cache, *link_cache = NULL;
struct nl_dump_params params;
char buf[BUFLEN];
char addrs[ADDRBUFLEN];
QByteArray interfaceArray = m_interface.toLatin1();
const char *interface = interfaceArray.constData();
char *ipInterface = strndup(interface, strlen(interface));
memset(&params, 0, sizeof(struct nl_dump_params));
params.dp_type = NL_DUMP_LINE;
params.dp_fd = NULL;
params.dp_buf = buf;
params.dp_buflen = BUFLEN;
if(!(sock = Utils::connect())) {
free(ipInterface);
return QStringList();
}
if(Utils::alloc_addr_cache(sock, &addr_cache)) {
free(ipInterface);
nl_socket_free(sock);
return QStringList();
}
if(Utils::alloc_link_cache(sock, &link_cache)) {
free(ipInterface);
nl_cache_free(addr_cache);
nl_socket_free(sock);
return QStringList();
}
if(!(addr = rtnl_addr_alloc())) {
std::cerr << "could not allocate address" << std::endl;
free(ipInterface);
nl_cache_free(addr_cache);
nl_cache_free(link_cache);
nl_socket_free(sock);
return QStringList();
}
rtnl_addr_set_family(addr, AF_INET);
int ival = 0;
if(!(ival = rtnl_link_name2i(link_cache, ipInterface))) {
std::cerr << "Link " << ipInterface << " does not exist" << std::endl;
free(ipInterface);
rtnl_addr_put(addr);
nl_cache_free(addr_cache);
nl_cache_free(link_cache);
nl_socket_free(sock);
return QStringList();
}
rtnl_addr_set_ifindex(addr, ival);
memset(buf, 0, BUFLEN);
memset(addrs, 0, ADDRBUFLEN);
nl_cache_dump_filter(addr_cache, &params, OBJ_CAST(addr));
buf[BUFLEN - 1] = '\0';
QString addrsString = buf;
QStringList addrsLines = addrsString.split('\n');
QStringList addrsList;
foreach(QString addrString, addrsLines) {
QStringList fields = addrString.split(' ');
if(fields.count() > 0) {
QString address = fields.at(0);
if(!address.isEmpty()) {
addrsList << fields.at(0);
}
}
}
free(ipInterface);
rtnl_addr_put(addr);
nl_cache_free(addr_cache);
nl_cache_free(link_cache);
nl_socket_free(sock);
return addrsList;
}
/*!
Returns a list of all interfaces on the system.
*/
QStringList Interface::list() {
struct nl_sock *sock = NULL;
struct nl_cache *link_cache = NULL;
QStringList linkList;
if(!(sock = Utils::connect())) {
return QStringList();
}
if(Utils::alloc_link_cache(sock, &link_cache)) {
nl_socket_free(sock);
return QStringList();
}
int index = 1;
forever {
struct rtnl_link *link = rtnl_link_get(link_cache, index);
if(link != NULL) {
char *name = rtnl_link_get_name(link);
linkList << name;
rtnl_link_put(link);
}else{
break;
}
++index;
}
nl_cache_free(link_cache);
nl_socket_free(sock);
// workaround for libnl issue: https://github.com/tgraf/libnl/issues/49
// find any interfaces libnl doesn't know about (e.g. USB wireless adapters after they've been unplugged and replugged)
QDir dir("/sys/class/net");
QStringList interfaces = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
foreach(QString interface, interfaces) {
if(!linkList.contains(interface)) {
linkList << interface;
}
}
return linkList;
}
/*!
Returns the name of the interface used when the Interface object was constructed.
*/
const QString& Interface::name() const {
return m_interface;
}

31
interface.h

@ -0,0 +1,31 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
#ifndef QIPROUTE_INTERFACE_H
#define QIPROUTE_INTERFACE_H
#include <QObject>
#include "qiproute_global.h"
class QIPROUTE_EXPORT Interface : public QObject {
Q_OBJECT
public:
Interface(QString interface);
~Interface();
static QStringList list();
QStringList addresses() const;
bool hasCarrier(bool *ok = 0);
bool hasAdminLink(bool *ok = 0);
int setLinkUp();
int setLinkDown();
int addAddress(QString address) const;
int deleteAddress(QString address) const;
const QString& name() const;
private:
QString m_interface;
int m_index;
};
#endif // QIPROUTE_INTERFACE_H

335
neighbor.cpp

@ -0,0 +1,335 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
extern "C" {
#include <netlink/socket.h>
#include <linux/netlink.h>
#include <netlink/netlink.h>
#include <netlink/route/link.h>
#include <netlink/route/neighbour.h>
#include <pthread.h>
}
#include "neighbor.h"
#include "utils.h"
#include <iostream>
#include <QStringList>
#define BUFLEN 65535
#define MACBUFLEN 18
#define IPBUFLEN 64
#define EMPTY_MAC "00:00:00:00:00:00"
/*!
\class Neighbor
\brief The Neighbor class is used to manage ARP table information.
*/
/*!
Constructs a Neighbor object. If \a interface is specified, all operations will be limited to that interface.
*/
Neighbor::Neighbor(QString interface) :
QObject(0),
m_interface(interface)
{
}
Neighbor::~Neighbor() {
}
/*!
Returns the MAC address that has been learned for the IP address \a ip. If the IP address is not in the ARP table or an error retrieving the neighbor occurred, the string "00:00:00:00:00:00" will be returned. If there was an error, *ok will be set to false, otherwise it will be true.
*/
QString Neighbor::macOfIP(QString ip, bool *ok) const {
struct nl_sock *sock = NULL;
struct rtnl_neigh *neigh = NULL;
struct nl_cache *neigh_cache, *link_cache = NULL;
struct nl_dump_params params;
char buf[BUFLEN] = {0};
QByteArray ipArray = ip.toLatin1();
char *ipAddr = ipArray.data();
memset(&params, 0, sizeof(struct nl_dump_params));
params.dp_type = NL_DUMP_LINE;
params.dp_fd = NULL;
params.dp_buf = buf;
params.dp_buflen = BUFLEN;
if(!(sock = Utils::connect())) {
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
if(Utils::alloc_neigh_cache(sock, &neigh_cache)) {
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
if(Utils::alloc_link_cache(sock, &link_cache)) {
nl_cache_free(neigh_cache);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
if(!(neigh = rtnl_neigh_alloc())) {
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
if(!m_interface.isEmpty()) {
QByteArray interfaceArray = m_interface.toLatin1();
char *interface = interfaceArray.data();
int ival = 0;
if(!(ival = rtnl_link_name2i(link_cache, interface))) {
std::cerr << "Link " << interface << " does not exist" << std::endl;
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
rtnl_neigh_put(neigh);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
rtnl_neigh_set_ifindex(neigh, ival);
}
struct nl_addr *addr = NULL;
int err = 0;
if((err = nl_addr_parse(ipAddr, rtnl_neigh_get_family(neigh), &addr)) < 0) {
std::cerr << "Unable to parse address \"" << ipAddr << "\": " << nl_geterror(err) << std::endl;
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
rtnl_neigh_put(neigh);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
if((err = rtnl_neigh_set_dst(neigh, addr)) < 0) {
std::cerr << "Unable to set local address \"" << ipAddr << "\": " << nl_geterror(err) << std::endl;
nl_addr_put(addr);
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
rtnl_neigh_put(neigh);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return QString(EMPTY_MAC);
}
nl_addr_put(addr);
memset(buf, 0, BUFLEN);
char mac[MACBUFLEN] = {0};
memset(mac, 0, MACBUFLEN);
nl_cache_dump_filter(neigh_cache, &params, OBJ_CAST(neigh));
unsigned short count = 0;
char *rest = NULL;
char *field = strtok_r(buf, " ", &rest);
while(field != NULL) {
++count;
if(count == 5) {
if(strstr(field, ":") != NULL) {
memcpy(mac, field, MACBUFLEN - 1);
}
}
field = strtok_r(NULL, " ", &rest);
}
rtnl_neigh_put(neigh);
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
nl_socket_free(sock);
if(mac == NULL || count == 0) {
if(ok != NULL) {
*ok = true;
}
return QString(EMPTY_MAC);
}
QString macStr = mac;
if(ok != NULL) {
*ok = true;
}
return macStr;
}
/*!
Returns a string hash consisting of IP addresses (both IPv4 and IPv6) in the ARP cache as the keys, and their corresponding MAC addresses as the values. If there are no entries found or if there was an error obtaining the list, an empty hash with no keys are returned. If there was an error, *ok will be set to false, otherwise it will be true.
*/
QHash<QString, QString> Neighbor::list(bool *ok) {
return list(m_interface, ok);
}
/*!
Returns a string hash consisting of IP addresses (both IPv4 and IPv6) in the ARP cache as the keys, and their corresponding MAC addresses as the values. By specifying \a interfaceString the returned list is confined to addresses seen from that named interface. If there are no entries found or if there was an error obtaining the list, an empty hash with no keys are returned. If there was an error, *ok will be set to false, otherwise it will be true.
*/
QHash<QString, QString> Neighbor::list(QString interfaceString, bool *ok) {
struct nl_sock *sock = NULL;
struct rtnl_neigh *neigh = NULL;
struct nl_cache *neigh_cache, *link_cache = NULL;
struct nl_dump_params params;
char buf[BUFLEN] = {0};
QHash<QString, QString> neighborList;
memset(&params, 0, sizeof(struct nl_dump_params));
params.dp_type = NL_DUMP_LINE;
params.dp_fd = NULL;
params.dp_buf = buf;
params.dp_buflen = BUFLEN;
if(!(sock = Utils::connect())) {
if(ok != NULL) {
*ok = false;
}
return neighborList;
}
if(Utils::alloc_link_cache(sock, &link_cache)) {
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return neighborList;
}
if(Utils::alloc_neigh_cache(sock, &neigh_cache)) {
nl_cache_free(link_cache);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return neighborList;
}
if(!(neigh = rtnl_neigh_alloc())) {
std::cerr << "could not allocate neighbor" << std::endl;
nl_cache_free(neigh_cache);
nl_cache_free(link_cache);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return neighborList;
}
if(!interfaceString.isEmpty()) {
QByteArray interfaceArray = interfaceString.toLatin1();
char *interface = interfaceArray.data();
int ival = 0;
if(!(ival = rtnl_link_name2i(link_cache, interface))) {
std::cerr << "Link " << interface << " does not exist" << std::endl;
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
rtnl_neigh_put(neigh);
nl_socket_free(sock);
if(ok != NULL) {
*ok = false;
}
return neighborList;
}
rtnl_neigh_set_ifindex(neigh, ival);
}
nl_cache_dump_filter(neigh_cache, &params, OBJ_CAST(neigh));
QString bufString = buf;
QStringList list = bufString.split('\n');
foreach(QString neighbor, list) {
if(neighbor.isEmpty()) {
continue;
}
QStringList neighborFields = neighbor.split(' ');
if(neighborFields.count() > 3) {
if(neighborFields.count() > 4) {
if((neighborFields[0].contains('.') || neighborFields[0].contains(':')) && neighborFields[4].contains(':')) {
neighborList.insert(neighborFields[0], neighborFields[4]);
}
}else{
if(neighborFields[3] == "<failed>") {
neighborList.insert(neighborFields[0], "<incomplete>");
}
}
}
}
rtnl_neigh_put(neigh);
nl_cache_free(link_cache);
nl_cache_free(neigh_cache);
nl_socket_free(sock);
if(ok != NULL) {
*ok = true;
}
return neighborList;
}
/*!
Returns the name of the interface used when creating the Neighbor object. If a name was not provided, an empty string will be returned.
*/
const QString& Neighbor::name() const {
return m_interface;
}

26
neighbor.h

@ -0,0 +1,26 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
#ifndef QIPROUTE_NEIGHBOR_H
#define QIPROUTE_NEIGHBOR_H
#include <QObject>
#include <QHash>
#include "qiproute_global.h"
class QIPROUTE_EXPORT Neighbor : public QObject {
Q_OBJECT
public:
Neighbor(QString interface = QString());
~Neighbor();
QString macOfIP(QString ip, bool *ok = 0) const;
QHash<QString, QString> list(bool *ok = 0);
static QHash<QString, QString> list(QString interface = QString(), bool *ok = 0);
const QString& name() const;
private:
QString m_interface;
};
#endif // QIPROUTE_NEIGHBOR_H

17
qiproute.pro

@ -0,0 +1,17 @@
# Copyright (c) 2014, Brad Parker
# This file is licensed under the BSD 2-clause license.
QT = core
TEMPLATE = lib
TARGET = QIPRoute
INCLUDEPATH += /usr/include/libnl3
DEFINES += QIPROUTE_LIBRARY
QMAKE_CXXFLAGS += -Wall -Wextra
QMAKE_CFLAGS += -Wall -Wextra
LIBS += -lnl-route-3 -lnl-3 -lnl-genl-3
HEADERS += interface.h neighbor.h utils.h
SOURCES += interface.cpp neighbor.cpp utils.cpp

18
qiproute_global.h

@ -0,0 +1,18 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
#include <QtGlobal>
#if defined(QIPROUTE_LIBRARY)
# define QIPROUTE_EXPORT Q_DECL_EXPORT
#else
# define QIPROUTE_EXPORT Q_DECL_IMPORT
#endif
#ifdef WIN32
#define QIPROUTE_LOCAL
#define QIPROUTE_PUBLIC
#else
#define QIPROUTE_LOCAL __attribute__((visibility("hidden")))
#define QIPROUTE_PUBLIC __attribute__((visibility("default")))
#endif

98
utils.cpp

@ -0,0 +1,98 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
extern "C" {
#include <pthread.h>
#include <netlink/socket.h>
#include <linux/netlink.h>
#include <netlink/netlink.h>
#include <netlink/route/addr.h>
#include <netlink/route/neighbour.h>
}
#include "utils.h"
#include <iostream>
#include <QTimer>
#include <QtCore/QCoreApplication>
struct nl_sock* Utils::connect() {
struct nl_sock *sock = nl_socket_alloc();
int err = 0;
if(sock == NULL) {
std::cerr << "could not allocate netlink socket" << std::endl;
return NULL;
}
if((err = nl_connect(sock, NETLINK_ROUTE)) < 0) {
std::cerr << "could not connect to netlink socket: " << nl_geterror(err) << std::endl;
nl_socket_free(sock);
return NULL;
}
return sock;
}
int Utils::alloc_addr_cache(struct nl_sock *sock, struct nl_cache **cache) {
int err = 0;
if((err = rtnl_addr_alloc_cache(sock, cache)) < 0) {
std::cerr << "could not allocate address cache: " << nl_geterror(err) << std::endl;
return 1;
}
nl_cache_mngt_provide(*cache);
return 0;
}
int Utils::alloc_link_cache(struct nl_sock *sock, struct nl_cache **cache) {
int err = rtnl_link_alloc_cache(sock, AF_UNSPEC, cache);
if(err != 0) {
std::cerr << "could not allocate link cache: " << nl_geterror(err) << std::endl;
return 1;
}
nl_cache_mngt_provide(*cache);
return 0;
}
int Utils::alloc_neigh_cache(struct nl_sock *sock, struct nl_cache **cache) {
int err = 0;
if((err = rtnl_neigh_alloc_cache(sock, cache)) < 0) {
std::cerr << "could not allocate neighbor cache: " << nl_geterror(err) << std::endl;
return 1;
}
nl_cache_mngt_provide(*cache);
return 0;
}
int Utils::interfaceIndex(QString interface) {
QByteArray array = interface.toLatin1();
struct nl_sock *sock = Utils::connect();
if(!sock) {
std::cerr << "could not allocate socket" << std::endl;
return 0;
}
const char *interfaceData = array.constData();
struct nl_cache *link_cache = NULL;
if(Utils::alloc_link_cache(sock, &link_cache)) {
nl_socket_free(sock);
return 0;
}
int index = rtnl_link_name2i(link_cache, interfaceData);
nl_cache_free(link_cache);
nl_socket_free(sock);
return index;
}

21
utils.h

@ -0,0 +1,21 @@
// Copyright (c) 2014, Brad Parker
// This file is licensed under the BSD 2-clause license.
#ifndef QIPROUTE_UTILS_H
#define QIPROUTE_UTILS_H
#include <QObject>
#include "qiproute_global.h"
class QIPROUTE_LOCAL Utils : public QObject {
Q_OBJECT
public:
static int interfaceIndex(QString interface);
static struct nl_sock* connect();
static int alloc_addr_cache(struct nl_sock *sock, struct nl_cache **cache);
static int alloc_link_cache(struct nl_sock *sock, struct nl_cache **cache);
static int alloc_neigh_cache(struct nl_sock *sock, struct nl_cache **cache);
};
#endif // QIPROUTE_UTILS_H
Loading…
Cancel
Save