You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

335 lines
7.9 KiB

// 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;
}