feature:add ipv6 conflict check function

This commit is contained in:
renpeijia 2021-08-20 14:09:23 +08:00
parent c2a1efa633
commit bcad10d46f
4 changed files with 511 additions and 6 deletions

View File

@ -9,6 +9,7 @@ HEADERS += \
$$PWD/kylin-dbus-interface.h \
$$PWD/kylin-network-interface.h \
$$PWD/kylinipv4arping.h \
$$PWD/kylinipv6arping.h \
$$PWD/sysdbusregister.h \
$$PWD/utils.h \
$$PWD/wifi-auth-thread.h
@ -20,6 +21,7 @@ SOURCES += \
$$PWD/kylin-dbus-interface.cpp \
$$PWD/kylin-network-interface.c \
$$PWD/kylinipv4arping.cpp \
$$PWD/kylinipv6arping.cpp \
$$PWD/sysdbusregister.cpp \
$$PWD/utils.cpp \
$$PWD/wifi-auth-thread.cpp

View File

@ -91,7 +91,7 @@ int KyIpv4Arping::sendIpv4Packet()
memcpy(p, &p_me->sll_addr, ah->ar_hln);
p += p_me->sll_halen;
qWarning()<<"m_src address:" << inet_ntoa(m_srcAddress);
qWarning()<<"[KyIpv4Arping]" <<"m_src address:" << inet_ntoa(m_srcAddress);
memcpy(p, &m_srcAddress, 4);
p += 4;
@ -237,7 +237,7 @@ int KyIpv4Arping::ipv4EventLoop()
case POLLFD_TIMER:
bytes = read(tfd, &exp, sizeof(uint64_t));
if (bytes != sizeof(uint64_t)) {
qWarning() << "could not read timerfd";
qWarning() <<"[KyIpv4Arping]" << "could not read timerfd";
continue;
}
@ -254,7 +254,7 @@ int KyIpv4Arping::ipv4EventLoop()
bytes = recvfrom(m_ipv4Socket, packet, sizeof(packet), 0,
(struct sockaddr *)&from, &addr_len);
if (bytes < 0) {
qWarning() << "recvfrom function failed, errno" << errno;
qWarning()<<"[KyIpv4Arping]" << "recvfrom function failed, errno" << errno;
continue;
}
if (ipv4PacketProcess(packet, bytes, (struct sockaddr_ll *)&from) == FINAL_PACKS) {
@ -263,7 +263,7 @@ int KyIpv4Arping::ipv4EventLoop()
}
break;
default:
qWarning()<<"the fd index is undefine" << index;
qWarning()<<"[KyIpv4Arping]" <<"the fd index is undefine" << index;
break;
}
}
@ -278,12 +278,12 @@ int KyIpv4Arping::ipv4EventLoop()
int KyIpv4Arping::checkIfflags(unsigned int ifflags)
{
if (!(ifflags & IFF_UP)) {
qWarning()<<"the iface" << m_ifaceName <<" is down.";
qWarning()<<"[KyIpv4Arping]" <<"the iface" << m_ifaceName <<" is down.";
return -1;
}
if (ifflags & (IFF_NOARP | IFF_LOOPBACK)) {
qWarning()<< "Interface" << m_ifaceName << "is not ARPable.";
qWarning()<<"[KyIpv4Arping]" << "Interface" << m_ifaceName << "is not ARPable.";
return -1;
}

View File

@ -0,0 +1,394 @@
#include "kylinipv6arping.h"
#include <sys/times.h>
#include <sys/ioctl.h>
#include <net/if.h>
KyIpv6Arping::KyIpv6Arping(QString ifaceName, QString ipAddress, int retryCount, int timeout, QObject *parent) : QObject(parent)
{
m_ifaceName = ifaceName;
m_ipv6Address = ipAddress;
m_retryCount = retryCount;
m_timeoutMs = timeout;
m_ipv6Conflict = false;
}
KyIpv6Arping::~KyIpv6Arping()
{
}
void KyIpv6Arping::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);
}
}
int KyIpv6Arping::getLocalMacAddress(const char *ifname, unsigned char *addr)
{
# ifdef SIOCGIFHWADDR
struct ifreq req;
memset (&req, 0, sizeof (req));
if (((unsigned)strlen (ifname)) >= (unsigned)IFNAMSIZ) {
return -1; /* buffer overflow = local root */
}
strcpy (req.ifr_name, ifname);
int fd = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd == -1) {
return -1;
}
if (ioctl (fd, SIOCGIFHWADDR, &req)) {
qWarning()<<"[KyIpv6Arping]"<<"get local mac address failed, errno" << errno;
close (fd);
return -1;
}
close (fd);
memcpy (addr, req.ifr_hwaddr.sa_data, 6);
return 0;
# else
/* No SIOCGIFHWADDR, which seems Linux specific. */
struct ifaddrs *ifa = NULL;
struct ifaddrs *ifp = NULL;
getifaddrs(&ifa);
ifp = ifa; /* preserve the address of ifa to free memory later */
while (ifp != NULL) {
if (ifp->ifa_addr->sa_family == AF_LINK && strcmp(ifp->ifa_name, ifname) == 0) {
const struct sockaddr_dl* sdl = (const struct sockaddr_dl*) ifp->ifa_addr;
memcpy(addr, sdl->sdl_data + sdl->sdl_nlen, 6);
freeifaddrs(ifa);
return 0;
}
ifp = ifp->ifa_next;
}
freeifaddrs(ifa);
return -1;
# endif
}
int KyIpv6Arping::getIpv6ByName(struct sockaddr_in6 *addr)
{
struct addrinfo hints, *res;
memset (&hints, 0, sizeof (hints));
hints.ai_family = PF_INET6;
hints.ai_socktype = SOCK_DGRAM; /* dummy */
hints.ai_flags = 0;
int val = getaddrinfo (m_ipv6Address.toUtf8(), NULL, &hints, &res);
if (val) {
qWarning()<<"[KyIpv6Arping]" << m_ipv6Address <<"get addrinfo failed, errno" << val;
return -1;
}
memcpy (addr, res->ai_addr, sizeof (struct sockaddr_in6));
freeaddrinfo (res);
val = if_nametoindex (m_ifaceName.toUtf8());
if (val == 0) {
qWarning()<<"[KyIpv6Arping]" <<"if_nametoindex failed, errno" << errno;
return -1;
}
addr->sin6_scope_id = val;
return 0;
}
int KyIpv6Arping::buildSolicitationPacket(solicit_packet *ns, struct sockaddr_in6 *tgt, const char *ifname)
{
/* builds ICMPv6 Neighbor Solicitation packet */
ns->hdr.nd_ns_type = ND_NEIGHBOR_SOLICIT;
ns->hdr.nd_ns_code = 0;
ns->hdr.nd_ns_cksum = 0; /* computed by the kernel */
ns->hdr.nd_ns_reserved = 0;
memcpy (&ns->hdr.nd_ns_target, &tgt->sin6_addr, 16);
/* determines actual multicast destination address */
memcpy (&tgt->sin6_addr.s6_addr, "\xff\x02\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x01\xff", 13);
/* gets our own interface's link-layer address (MAC) */
if (getLocalMacAddress (m_ifaceName.toUtf8().constData(), ns->hw_addr)) {
return sizeof (ns->hdr);
}
ns->opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
ns->opt.nd_opt_len = 1; /* 8 bytes */
return sizeof (*ns);
}
void KyIpv6Arping::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_conflictMacAddress = QString(macAddress);
return ;
}
int KyIpv6Arping::parseIpv6Packet(const uint8_t *buf, size_t len, const struct sockaddr_in6 *tgt)
{
const struct nd_neighbor_advert *na =
(const struct nd_neighbor_advert *)buf;
const uint8_t *ptr;
/* checks if the packet is a Neighbor Advertisement, and
* if the target IPv6 address is the right one */
if ((len < sizeof (struct nd_neighbor_advert))
|| (na->nd_na_type != ND_NEIGHBOR_ADVERT)
|| (na->nd_na_code != 0)
|| memcmp (&na->nd_na_target, &tgt->sin6_addr, 16)) {
return -1;
}
len -= sizeof (struct nd_neighbor_advert);
/* looks for Target Link-layer address option */
ptr = buf + sizeof (struct nd_neighbor_advert);
while (len >= 8)
{
uint16_t optlen;
optlen = ((uint16_t)(ptr[1])) << 3;
if (optlen == 0)
break; /* invalid length */
if (len < optlen) /* length > remaining bytes */
break;
len -= optlen;
/* skips unrecognized option */
if (ptr[0] != ND_OPT_TARGET_LINKADDR)
{
ptr += optlen;
continue;
}
/* Found! displays link-layer address */
ptr += 2;
optlen -= 2;
saveMacAddress (ptr, optlen);
setIpv6ConflictFlag(true);
return 0;
}
return -1;
}
int KyIpv6Arping::readIpv6Packet(void *buf,
size_t len,
int flags,
struct sockaddr_in6 *addr)
{
char cbuf[CMSG_SPACE (sizeof (int))];
struct iovec iov = {
.iov_base = buf,
.iov_len = len
};
struct msghdr hdr = {
.msg_name = addr,
.msg_namelen = sizeof (*addr),
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cbuf,
.msg_controllen = sizeof (cbuf)
};
ssize_t val = recvmsg (m_ipv6Socket, &hdr, flags);
if (val == -1) {
return val;
}
/* ensures the hop limit is 255 */
for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&hdr);
cmsg != NULL; cmsg = CMSG_NXTHDR (&hdr, cmsg)) {
if ((cmsg->cmsg_level == IPPROTO_IPV6)
&& (cmsg->cmsg_type == IPV6_HOPLIMIT)) {
if (255 != *(int *)CMSG_DATA (cmsg)) {
// pretend to be a spurious wake-up
errno = EAGAIN;
return -1;
}
}
}
return val;
}
int KyIpv6Arping::reciveAndProcessIpv6Packet(const struct sockaddr_in6 *tgt, unsigned wait_ms)
{
struct timespec end;
struct pollfd fds;
/* computes deadline time */
monoGetTime (&end);
div_t d;
d = div (wait_ms, 1000);
end.tv_sec = end.tv_sec + d.quot;
end.tv_nsec = end.tv_nsec + (d.rem * 1000000);
/* receive loop */
for (;;)
{
/* waits for reply until deadline */
struct timespec now;
int val = 0;
monoGetTime (&now);
if (end.tv_sec >= now.tv_sec) {
val = (end.tv_sec - now.tv_sec) * 1000
+ (int)((end.tv_nsec - now.tv_nsec) / 1000000);
if (val < 0) {
val = 0;
}
}
fds.fd = m_ipv6Socket;
fds.events = POLLIN;
val = poll (&fds, 1, val);
if (val < 0)
break;
if (val == 0)
return 0;
/* receives an ICMPv6 packet */
// TODO: use interface MTU as buffer size
uint8_t buf[1460];
struct sockaddr_in6 addr;
val = readIpv6Packet(buf, sizeof (buf), MSG_DONTWAIT, &addr);
if (val < 0) {
if (errno != EAGAIN)
qWarning()<<"[KyIpv6Arping]"<<"Receiving ICMPv6 packet failed";
continue;
}
/* ensures the response came through the right interface */
if (addr.sin6_scope_id
&& (addr.sin6_scope_id != tgt->sin6_scope_id))
continue;
if (parseIpv6Packet(buf, val, tgt) == 0) {
return 1 /* = responses */;
}
}
return -1; /* error */
}
int KyIpv6Arping::ipv6ConflictCheck()
{
struct sockaddr_in6 tgt;
struct icmp6_filter filter;
int retry = 0;
m_ipv6Socket = socket (PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (m_ipv6Socket < 0) {
qDebug()<<"[KyIpv6Arping]" <<"create ipv6 socket failed:";
return -1;
}
fcntl (m_ipv6Socket, F_SETFD, FD_CLOEXEC);
/* set ICMPv6 filter */
ICMP6_FILTER_SETBLOCKALL (&filter);
ICMP6_FILTER_SETPASS (ND_NEIGHBOR_ADVERT, &filter);
setsockopt (m_ipv6Socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof (filter));
int soDontRoute = 1;
setsockopt (m_ipv6Socket, SOL_SOCKET, SO_DONTROUTE, &soDontRoute, sizeof(soDontRoute));
/* sets Hop-by-hop limit to 255 */
int multicastHopLimit = 255;
setsockopt (m_ipv6Socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&multicastHopLimit, sizeof (multicastHopLimit));
int unicastHops = 255;
setsockopt (m_ipv6Socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&unicastHops, sizeof (unicastHops));
int recvHopLimit = 1;
setsockopt(m_ipv6Socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
&recvHopLimit, sizeof (recvHopLimit));
/* resolves target's IPv6 address */
int ret = getIpv6ByName(&tgt);
if (ret < 0) {
qWarning()<<"[KyIpv6Arping]"<<"get ipv6 by name failed.";
close (m_ipv6Socket);
return ret;
}
solicit_packet packet;
struct sockaddr_in6 dst;
ssize_t plen;
memcpy (&dst, &tgt, sizeof (dst));
plen = buildSolicitationPacket(&packet, &dst, m_ifaceName.toUtf8().constData());
if (plen == -1) {
qWarning()<<"[KyIpv6Arping]"<<"build solicit packet failed.";
close (m_ipv6Socket);
return -1;
}
while (retry < m_retryCount) {
/* sends a Solitication */
if (sendto (m_ipv6Socket, &packet, plen, MSG_DONTROUTE,
(const struct sockaddr *)&dst,
sizeof (dst)) != plen) {
qWarning()<<"[KyIpv6Arping]"<<"Sending ICMPv6 packet failed.";
close (m_ipv6Socket);
return -1;
}
retry++;
/* receives an Advertisement */
ssize_t val = reciveAndProcessIpv6Packet(&tgt, m_timeoutMs);
if (val > 0) {
close (m_ipv6Socket);
return 0;
} else if (val == 0) {
continue;
} else {
close (m_ipv6Socket);
return -1;
}
}
close(m_ipv6Socket);
if (retry == m_retryCount) {
return 0;
} else {
return -2;
}
}

View File

@ -0,0 +1,109 @@
#ifndef KYLINIPV6ARPING_H
#define KYLINIPV6ARPING_H
//#include <gettext.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* div() */
#include <inttypes.h> /* uint8_t */
#include <limits.h> /* UINT_MAX */
#include <locale.h>
#include <stdbool.h>
#include <errno.h> /* EMFILE */
#include <sys/types.h>
#include <unistd.h> /* close() */
#include <time.h> /* clock_gettime() */
#include <poll.h> /* poll() */
#include <sys/socket.h>
#include <sys/uio.h>
#include <fcntl.h>
//#include "gettime.h"
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#include <netdb.h> /* getaddrinfo() */
#include <arpa/inet.h> /* inet_ntop() */
#include <net/if.h> /* if_nametoindex() */
#include <netinet/in.h>
#include <netinet/icmp6.h>
#ifndef IPV6_RECVHOPLIMIT
/* Using obsolete RFC 2292 instead of RFC 3542 */
# define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
#endif
/* BSD-like systems define ND_RA_FLAG_HA instead of ND_RA_FLAG_HOME_AGENT */
#ifndef ND_RA_FLAG_HOME_AGENT
# ifdef ND_RA_FLAG_HA
# define ND_RA_FLAG_HOME_AGENT ND_RA_FLAG_HA
# endif
#endif
#ifndef AI_IDN
# define AI_IDN 0
#endif
#include <QString>
#include <QDebug>
#include <QObject>
typedef struct
{
struct nd_neighbor_solicit hdr;
struct nd_opt_hdr opt;
uint8_t hw_addr[6];
} solicit_packet;
class KyIpv6Arping: public QObject
{
Q_OBJECT
public:
explicit KyIpv6Arping(QString ifaceName, QString ipAddress, int retryCount=3, int timeout=1000, QObject *parent = nullptr);
~KyIpv6Arping();
public:
bool ipv6IsConflict() {
return m_ipv6Conflict;
}
void setIpv6ConflictFlag(bool conflict) {
m_ipv6Conflict = conflict;
}
QString getConflictMacAddress() {
return m_conflictMacAddress;
}
int ipv6ConflictCheck();
private:
void monoGetTime (struct timespec *ts);
void saveMacAddress (const uint8_t *ptr, size_t len);
int getLocalMacAddress(const char *ifname, unsigned char *addr);
int getIpv6ByName(struct sockaddr_in6 *addr);
int buildSolicitationPacket(solicit_packet *ns, struct sockaddr_in6 *tgt, const char *ifname);
int parseIpv6Packet(const uint8_t *buf, size_t len, const struct sockaddr_in6 *tgt);
int readIpv6Packet(void *buf, size_t len, int flags, struct sockaddr_in6 *addr);
int reciveAndProcessIpv6Packet(const struct sockaddr_in6 *tgt, unsigned wait_ms);
private:
int m_ipv6Socket = 0;
QString m_ifaceName;
QString m_ipv6Address;
int m_retryCount;
int m_timeoutMs;
bool m_ipv6Conflict = false;
QString m_conflictMacAddress = nullptr;
};
#endif // KYLINIPV6ARPING_H