feature: add ipv4 conflict check function

This commit is contained in:
renpeijia 2021-08-19 14:57:28 +08:00
parent 1033455505
commit 2ca22019a8
3 changed files with 518 additions and 0 deletions

View File

@ -8,6 +8,7 @@ HEADERS += \
$$PWD/ksimplenm.h \
$$PWD/kylin-dbus-interface.h \
$$PWD/kylin-network-interface.h \
$$PWD/kylinipv4arping.h \
$$PWD/sysdbusregister.h \
$$PWD/utils.h \
$$PWD/wifi-auth-thread.h
@ -18,6 +19,9 @@ SOURCES += \
$$PWD/ksimplenm.cpp \
$$PWD/kylin-dbus-interface.cpp \
$$PWD/kylin-network-interface.c \
$$PWD/kylinipv4arping.cpp \
$$PWD/sysdbusregister.cpp \
$$PWD/utils.cpp \
$$PWD/wifi-auth-thread.cpp
DISTFILES +=

View File

@ -0,0 +1,426 @@
#include "kylinipv4arping.h"
#include <sys/times.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define FINAL_PACKS 2
__attribute__((const)) static inline size_t sll_len(const size_t halen)
{
struct sockaddr_ll unused;
const size_t len = offsetof(struct sockaddr_ll, sll_addr) + halen;
if (len < sizeof(unused))
return sizeof(unused);
return len;
}
KyIpv4Arping::KyIpv4Arping(QString ifaceName, QString ipAddress, int retryCount, int timeout, QObject *parent) : QObject(parent)
{
m_ifaceName = ifaceName;
m_ipv4Address = ipAddress;
m_retryCount = retryCount;
m_timeout = timeout;
memset(&m_srcAddress, 0, sizeof(m_srcAddress));
}
KyIpv4Arping::~KyIpv4Arping()
{
}
void KyIpv4Arping::monoGetTime (struct timespec *ts)
{
#ifdef CLOCK_MONOTONIC
if (clock_gettime (CLOCK_MONOTONIC, ts))
#endif
{
static long freq = 0;
if (freq == 0)
freq = sysconf (_SC_CLK_TCK);
struct tms dummy;
clock_t t = times (&dummy);
ts->tv_sec = t / freq;
ts->tv_nsec = (t % freq) * (1000000000 / freq);
}
}
void KyIpv4Arping::saveMacAddress(const uint8_t *ptr, size_t len)
{
int index;
char macAddress[64] = {0};
for (index = 0; index < len; index++) {
snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%02X", ptr[index]);
if (index != len - 1) {
snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%s", ":");
}
}
m_conflictMac = QString(macAddress);
return ;
}
void KyIpv4Arping::setConflictFlag(bool isConflict)
{
m_ipConflict = isConflict;
}
int KyIpv4Arping::sendIpv4Packet()
{
int err;
unsigned char buf[256];
struct arphdr *ah = (struct arphdr *)buf;
unsigned char *p = (unsigned char *)(ah + 1);
struct sockaddr_ll *p_me = (struct sockaddr_ll *)&(m_me);
struct sockaddr_ll *p_he = (struct sockaddr_ll *)&(m_he);
ah->ar_hrd = htons(p_me->sll_hatype);
if (ah->ar_hrd == htons(ARPHRD_FDDI))
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP);
ah->ar_hln = p_me->sll_halen;
ah->ar_pln = 4;
ah->ar_op = htons(ARPOP_REQUEST);
memcpy(p, &p_me->sll_addr, ah->ar_hln);
p += p_me->sll_halen;
qWarning()<<"m_src address:" << inet_ntoa(m_srcAddress);
memcpy(p, &m_srcAddress, 4);
p += 4;
memcpy(p, &p_he->sll_addr, ah->ar_hln);
p += ah->ar_hln;
memcpy(p, &m_destAddress, 4);
p += 4;
err = sendto(m_ipv4Socket, buf, p - buf, 0, (struct sockaddr *)p_he, sll_len(ah->ar_hln));
return err;
}
int KyIpv4Arping::ipv4PacketProcess(unsigned char *buf, ssize_t len, struct sockaddr_ll *from)
{
struct arphdr *ah = (struct arphdr *)buf;
unsigned char *p = (unsigned char *)(ah + 1);
struct in_addr src_ip, dst_ip;
/* Filter out wild packets */
if (from->sll_pkttype != PACKET_HOST &&
from->sll_pkttype != PACKET_BROADCAST &&
from->sll_pkttype != PACKET_MULTICAST)
return 0;
/* Only these types are recognised */
if (ah->ar_op != htons(ARPOP_REQUEST) &&
ah->ar_op != htons(ARPOP_REPLY))
return 0;
/* ARPHRD check and this darned FDDI hack here :-( */
if (ah->ar_hrd != htons(from->sll_hatype) &&
(from->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
return 0;
/* Protocol must be IP. */
if (ah->ar_pro != htons(ETH_P_IP))
return 0;
if (ah->ar_pln != 4)
return 0;
if (ah->ar_hln != ((struct sockaddr_ll *)&m_me)->sll_halen)
return 0;
if (len < (ssize_t) sizeof(*ah) + 2 * (4 + ah->ar_hln))
return 0;
memcpy(&src_ip, p + ah->ar_hln, 4);
memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
/*
* DAD packet was:
* src_ip = 0 (or some src)
* src_hw = ME
* dst_ip = tested address
* dst_hw = <unspec>
*
* We fail, if receive request/reply with:
* src_ip = tested_address
* src_hw != ME
* if src_ip in request was not zero, check
* also that it matches to dst_ip, otherwise
* dst_ip/dst_hw do not matter.
*/
if (src_ip.s_addr != m_destAddress.s_addr)
return 0;
if (memcmp(p, ((struct sockaddr_ll *)&m_me)->sll_addr,
((struct sockaddr_ll *)&m_me)->sll_halen) == 0)
return 0;
saveMacAddress(p, ah->ar_hln);
setConflictFlag(true);
return FINAL_PACKS;
}
int KyIpv4Arping::ipv4EventLoop()
{
int rc = -1;
int ret;
int index;
int exit_loop = 0;
ssize_t bytes;
int tfd;
struct pollfd pfds[POLLFD_COUNT];
struct itimerspec timerfd_vals;
timerfd_vals.it_interval.tv_sec = m_timeout,
timerfd_vals.it_interval.tv_nsec = 0,
timerfd_vals.it_value.tv_sec = m_timeout,
timerfd_vals.it_value.tv_nsec = 0;
uint64_t exp;
uint64_t total_expires = 1;
unsigned char packet[4096];
struct sockaddr_storage from = { 0 };
socklen_t addr_len = sizeof(from);
/* timerfd */
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (tfd == -1) {
qWarning()<<"[KyIpv4Arping]" << "timerfd_create failed, errno" << errno;
return -1;
}
if (timerfd_settime(tfd, 0, &timerfd_vals, NULL)) {
close(tfd);
qWarning()<<"[KyIpv4Arping]" << "timerfd_settime failed, errno" << errno;
return -1;
}
pfds[POLLFD_TIMER].fd = tfd;
pfds[POLLFD_TIMER].events = POLLIN | POLLERR | POLLHUP;
/* socket */
pfds[POLLFD_SOCKET].fd = m_ipv4Socket;
pfds[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP;
sendIpv4Packet();
while (!exit_loop) {
ret = poll(pfds, POLLFD_COUNT, -1);
if (ret <= 0) {
if (errno == EAGAIN) {
continue;
}
if (errno) {
qWarning()<<"[KyIpv4Arping]" <<"poll failed, errno" << errno;
}
exit_loop = 1;
continue;
}
for (index = 0; index < POLLFD_COUNT; index++) {
if (pfds[index].revents == 0) {
continue;
}
switch (index) {
case POLLFD_TIMER:
bytes = read(tfd, &exp, sizeof(uint64_t));
if (bytes != sizeof(uint64_t)) {
qWarning() << "could not read timerfd";
continue;
}
total_expires += exp;
if (0 < m_retryCount && (uint64_t)m_retryCount < total_expires) {
exit_loop = 1;
rc = 0;
continue;
}
sendIpv4Packet();
break;
case POLLFD_SOCKET:
bytes = recvfrom(m_ipv4Socket, packet, sizeof(packet), 0,
(struct sockaddr *)&from, &addr_len);
if (bytes < 0) {
qWarning() << "recvfrom function failed, errno" << errno;
continue;
}
if (ipv4PacketProcess(packet, bytes, (struct sockaddr_ll *)&from) == FINAL_PACKS) {
exit_loop = 1;
rc = 0;
}
break;
default:
qWarning()<<"the fd index is undefine" << index;
break;
}
}
}
close(tfd);
freeifaddrs(m_ifa0);
return rc;
}
int KyIpv4Arping::checkIfflags(unsigned int ifflags)
{
if (!(ifflags & IFF_UP)) {
qWarning()<<"the iface" << m_ifaceName <<" is down.";
return -1;
}
if (ifflags & (IFF_NOARP | IFF_LOOPBACK)) {
qWarning()<< "Interface" << m_ifaceName << "is not ARPable.";
return -1;
}
return 0;
}
int KyIpv4Arping::checkDevice()
{
int rc;
struct ifaddrs *ifa;
int n = 0;
rc = getifaddrs(&m_ifa0);
if (rc < 0) {
qWarning()<<"[KyIpv4Arping]" <<"get iface address failed, erron"<< errno;
return -errno;
}
for (ifa = m_ifa0; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family != AF_PACKET) {
continue;
}
if (!m_ifaceName.isEmpty()
&& (NULL != ifa->ifa_name)
&& strcmp(ifa->ifa_name, m_ifaceName.toUtf8())) {
continue;
}
if (checkIfflags(ifa->ifa_flags) < 0) {
continue;
}
if (!((struct sockaddr_ll *)ifa->ifa_addr)->sll_halen) {
continue;
}
if (!ifa->ifa_broadaddr) {
continue;
}
m_ifa = ifa;
if (n++)
break;
}
if (n == 1 && m_ifa) {
m_ifindex = if_nametoindex(m_ifa->ifa_name);
if (!m_ifindex) {
qWarning()<<"[KyIpv4Arping]" <<"if_nametoindex is invalid";
freeifaddrs(m_ifa0);
return -1;
}
m_ifaceName = m_ifa->ifa_name;
return 0;
}
freeifaddrs(m_ifa0);
return -1;
}
void KyIpv4Arping::findBroadcastAddress()
{
struct sockaddr_ll *he = (struct sockaddr_ll *)&(m_he);
if (m_ifa) {
struct sockaddr_ll *sll =
(struct sockaddr_ll *)m_ifa->ifa_broadaddr;
if (sll->sll_halen == he->sll_halen) {
memcpy(he->sll_addr, sll->sll_addr, he->sll_halen);
return;
}
}
qWarning()<<"[KyIpv4Arping]" <<"using default broadcast address.";
memset(he->sll_addr, -1, he->sll_halen);
return;
}
int KyIpv4Arping::ipv4ConflictCheck()
{
int ret = checkDevice();
if (ret < 0) {
qWarning()<<"[KyIpv4Arping]"<<"the device is invalid" << m_ifaceName;
return -1;
}
m_ipv4Socket = socket(PF_PACKET, SOCK_DGRAM, 0);
if (m_ipv4Socket < 0) {
qWarning()<<"[KyIpv4Arping]" << "create ipv4 socket failed, errno" << errno;
return -1;
}
inet_aton(m_ipv4Address.toUtf8(), &m_destAddress);
m_destAddressFamily = AF_INET;
((struct sockaddr_ll *)&m_me)->sll_family = AF_PACKET;
((struct sockaddr_ll *)&m_me)->sll_ifindex = m_ifindex;
((struct sockaddr_ll *)&m_me)->sll_protocol = htons(ETH_P_ARP);
ret = bind(m_ipv4Socket, (struct sockaddr *)&m_me, sizeof(m_me));
if (ret < 0) {
qWarning()<<"[KyIpv4Arping]" <<"ipv4 socket bind failed, errno"<< errno;
close(m_ipv4Socket);
return -1;
}
socklen_t alen = sizeof(m_me);
ret = getsockname(m_ipv4Socket, (struct sockaddr *)&m_me, &alen);
if (ret < 0) {
qWarning()<<"[KyIpv4Arping]" <<"ipv4 get socket name failed, errno" << errno;
close(m_ipv4Socket);
return -1;
}
if (((struct sockaddr_ll *)&m_me)->sll_halen == 0) {
qWarning()<<"[KyIpv4Arping]" <<"the iface"<< m_ifaceName <<" is not suitable for arp";
close(m_ipv4Socket);
return -1;
}
m_he = m_me;
findBroadcastAddress();
ret = ipv4EventLoop();
close(m_ipv4Socket);
return ret;
}

View File

@ -0,0 +1,88 @@
#ifndef KYLIN_IPV4_ARPING
#define KYLIN_IPV4_ARPING
#include <arpa/inet.h>
#include <errno.h>
#include <ifaddrs.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/rtnetlink.h>
#include <netdb.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <poll.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <QString>
#include <QDebug>
#include <QObject>
enum {
POLLFD_TIMER = 0,
POLLFD_SOCKET,
POLLFD_COUNT
};
class KyIpv4Arping : public QObject
{
Q_OBJECT
public:
explicit KyIpv4Arping(QString ifaceName, QString ipAddress, int retryCount=3, int timeout=1, QObject *parent = nullptr);
~KyIpv4Arping();
public:
bool ipv4IsConflict() {
return m_ipConflict;
}
QString getMacAddress() {
return m_conflictMac;
}
int ipv4ConflictCheck();
private:
void monoGetTime(struct timespec *ts);
void saveMacAddress(const uint8_t *ptr, size_t len);
void findBroadcastAddress();
int checkDevice();
int checkIfflags(unsigned int ifflags);
int sendIpv4Packet();
int ipv4EventLoop();
int ipv4PacketProcess(unsigned char *buf, ssize_t len, struct sockaddr_ll *from);
void setConflictFlag(bool isConflict);
private:
struct in_addr m_srcAddress;
struct in_addr m_destAddress;
int m_destAddressFamily;
struct sockaddr_storage m_me;
struct sockaddr_storage m_he;
int m_ipv4Socket = 0;
QString m_ifaceName;
int m_ifindex;
struct ifaddrs *m_ifa;
struct ifaddrs *m_ifa0;
QString m_ipv4Address;
int m_retryCount;
int m_timeout;
bool m_ipConflict = false;
QString m_conflictMac = nullptr;
};
#endif