From 2ca22019a87ec6c183ba3a5545aa8fc1a55ebf17 Mon Sep 17 00:00:00 2001 From: renpeijia Date: Thu, 19 Aug 2021 14:57:28 +0800 Subject: [PATCH] feature: add ipv4 conflict check function --- src/backend/backend.pri | 4 + src/backend/kylinipv4arping.cpp | 426 ++++++++++++++++++++++++++++++++ src/backend/kylinipv4arping.h | 88 +++++++ 3 files changed, 518 insertions(+) create mode 100644 src/backend/kylinipv4arping.cpp create mode 100644 src/backend/kylinipv4arping.h diff --git a/src/backend/backend.pri b/src/backend/backend.pri index 5cc3a775..0c597c0f 100644 --- a/src/backend/backend.pri +++ b/src/backend/backend.pri @@ -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 += diff --git a/src/backend/kylinipv4arping.cpp b/src/backend/kylinipv4arping.cpp new file mode 100644 index 00000000..9ad8a6f4 --- /dev/null +++ b/src/backend/kylinipv4arping.cpp @@ -0,0 +1,426 @@ +#include "kylinipv4arping.h" + +#include +#include +#include + +#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 = + * + * 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; +} diff --git a/src/backend/kylinipv4arping.h b/src/backend/kylinipv4arping.h new file mode 100644 index 00000000..995848e9 --- /dev/null +++ b/src/backend/kylinipv4arping.h @@ -0,0 +1,88 @@ +#ifndef KYLIN_IPV4_ARPING +#define KYLIN_IPV4_ARPING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +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