Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

354 lines
9.3 KiB
C++

// Network.cpp
// Libunistd Copyright 2016 Robin.Rowe@CinePaint.org
// License open source MIT
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include "Network.h"
#include "StdFile.h"
#include "StdBlob.h"
#ifdef WIN32
#define SUPPRESS_SECURITY_WARNING __pragma(warning(suppress:4996))
#else
#define SUPPRESS_SECURITY_WARNING
#endif
#define CHECK(var) if(var!=ifstat->var){ ifstat->var=var; isChanged = true;}
namespace portable
{
const char* output_proc_net_route =
"Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT\n"
"eth0 00000000 0202000A 0003 0 0 100 00000000 0 0 1\n"
"eth1 0002000A 00000000 0001 0 0 100 00FFFFFF 0 0 2\n";
const char* output_proc_net_dev =
"Inter-| Receive | Transmit\n"
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"
" lo: 13380 87 0 0 0 0 0 0 13380 87 0 0 0 0 0 0\n"
" eth0: 279068630 1507895 0 0 0 0 0 0 567397982 852533 0 0 0 0 0 0\n"
" eth1: 1090810015 4017054 42 0 0 0 0 0 13975469 134398 0 0 0 0 0 0\n";
Network::Network(unsigned interfaceCount)
: isChanged(false)
, interfaces(0)
{ ifStats.resize(interfaceCount);
route_filename = "/proc/net/route";
dev_filename = "/proc/net/dev";
}
/* (size = 450 +4 = 77 + 123 + 2*125 + 4 LF)
root@vm-ubuntu:/opt/toolchains# cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
enth0: 616352817 446732 0 0 0 0 0 0 13516902 165770 0 0 0 0 0 0
enth1: 485454 4814 0 0 0 0 0 0 485454 502 0 0 0 0 0 0
*/
bool Network::UpdateIfStats()
{
#ifdef WIN32
StdBlob proc_net_dev(output_proc_net_dev);
#else
StdFile proc_net_dev;
if(!proc_net_dev.Open(dev_filename, "r"))
{ return false;
}
#endif
proc_net_dev.SkipLine();
proc_net_dev.SkipLine();
unsigned lineCount=3;
interfaces = 0;
while(!proc_net_dev.IsFeof())
{ if(interfaces>=ifStats.size())
{ proc_net_dev.SkipLine();
interfaces++;
}
IfStat* ifstat=&ifStats[interfaces];
ifstat->Reset();
SUPPRESS_SECURITY_WARNING
const int items = proc_net_dev.fscanf(
" %20[^:]:%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
ifstat->ifname,
&ifstat->in.bytes,
&ifstat->in.packets,
&ifstat->in.errors,
&ifstat->in.drops,
&ifstat->in.overruns,
&ifstat->in.in_frame,
&ifstat->in.in_compress,
&ifstat->in.multicast,
&ifstat->out.bytes,
&ifstat->out.packets,
&ifstat->out.errors,
&ifstat->out.drops,
&ifstat->out.overruns,
&ifstat->out.out_colls,
&ifstat->out.out_carrier,
&ifstat->out.out_carrier
);
interfaces++;
if(-1==items)
{ break;
}
if(items != 17)
{ printf("Invalid data read Network::UpdateIfStats() %u:%i\n",lineCount,items);
break;
}
UpdateIoctls(ifstat);
lineCount++;
}
UpdateRoute();
return true;
}
struct ProcNetRoute
{ char ifname[10];
unsigned destination;
unsigned gateway;
unsigned flags;
unsigned refcnt;
unsigned use;
unsigned metric;
unsigned mask;
unsigned mtu;
unsigned window;
unsigned irtt;
void Reset()
{ ifname[0]=0;
destination=0;
gateway=0;
flags=0;
refcnt=0;
use=0;
metric=0;
mask=0;
mtu=0;
window=0;
irtt=0;
}
};
/*
$ cat /proc/net/route
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 0202000A 0003 0 0 100 00000000 0 0 0
eth1 0002000A 00000000 0001 0 0 100 00FFFFFF 0 0 0
*/
void Network::UpdateRoute()
{
#ifdef WIN32
StdBlob proc_net_route(output_proc_net_route);
#else
StdFile proc_net_route;
if(!proc_net_route.Open(route_filename, "r"))
{ return;
}
#endif
proc_net_route.SkipLine();
ProcNetRoute pnr;
unsigned lineCount = 2;
while(!proc_net_route.IsFeof())
{ pnr.Reset();
SUPPRESS_SECURITY_WARNING
const int items = proc_net_route.fscanf(
"%s %x %x %x %x %x %x %x %x %x %x",
pnr.ifname,
&pnr.destination,
&pnr.gateway,
&pnr.flags,
&pnr.refcnt,
&pnr.use,
&pnr.metric,
&pnr.mask,
&pnr.mtu,
&pnr.window,
&pnr.irtt
);
// printf("Network::ifname=%s gateway=%u\n",pnr.ifname,pnr.gateway);
if(pnr.gateway)
{ IfStat* ifstat=GetIfStat(pnr.ifname);
if(ifstat)
{ const unsigned gateway = pnr.gateway;
CHECK(gateway)
} }
if(-1==items)
{ break;
}
if(items != 11)
{ printf("Invalid data read Network::UpdateRoute() %u:%i\n",lineCount,items);
break;
}
lineCount++;
} }
IfStat* Network::GetIfStat(const char* ifname)
{ for(unsigned i=0;i<ifStats.size();i++)
{ if(!strcmp(ifStats[i].ifname,ifname))
{ return &ifStats[i];
} }
return nullptr;
}
inline
uint32_t GetQuad(sockaddr *addr,bool isGood=true)
{ if(!isGood || AF_INET !=addr->sa_family)
{ return 0;
}
return ((struct sockaddr_in*)addr)->sin_addr.s_addr;
}
inline
bool IsGood(int retval)
{ return -1!=retval || EADDRNOTAVAIL==errno;
}
bool Network::UpdateIoctls(IfStat* ifstat)
{
#ifdef WIN32
ifstat->address= 0x0100007f;
ifstat->broadcast= 0x0000007f;
ifstat->netmask= 0x00ffffff;
ifstat->gateway= 0x04030201;
ifstat->hw_address=0x0102030405060708ULL;
ifstat->mtu=32;
return true;
#else
const int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if(-1 == sock)
{ return false;
}
ifreq req;
strncpy(req.ifr_name, ifstat->ifname, IFNAMSIZ);
req.ifr_name[IFNAMSIZ - 1] = 0;
int retval = ioctl(sock, SIOCGIFADDR, &req);
const uint32_t address=GetQuad(&req.ifr_addr,IsGood(retval));
CHECK(address)
retval += ioctl(sock, SIOCGIFNETMASK, &req);
const uint32_t netmask=GetQuad(&req.ifr_addr,IsGood(retval));;
CHECK(netmask)
retval += ioctl(sock, SIOCGIFBRDADDR, &req);
const uint32_t broadcast=GetQuad(&req.ifr_addr,IsGood(retval));
CHECK(broadcast)
retval += ioctl(sock, SIOCGIFMTU, &req);
const int mtu = -1!=retval ? req.ifr_mtu:0;
CHECK(mtu)
retval += ioctl(sock, SIOCGIFHWADDR, &req);
uint64_t hw_address = 0;
unsigned char* hwaddr = (unsigned char *) &req.ifr_hwaddr.sa_data;
memcpy(&hw_address,hwaddr,6);
CHECK(hw_address)
close(sock);
//printf("Network::ioctl=%i address=%u netmask=%u broadcast=%u mtu=%i hw=%llu\n",retval,address,netmask,broadcast,mtu,hw_address);
return !retval;
#endif
}
void Network::PrintStats()
{ printf("Network::PrintStats() interfaces=%u isChanged=%i\n",interfaces,isChanged);
for(unsigned i=0;i<ifStats.size();i++)
{ PrintIfStat(&ifStats[i]);
} }
void Network::PrintStats(const char* ifname)
{ printf("Network::PrintStats() interfaces=%u isChanged=%i\n",interfaces,isChanged);
IfStat* ifstat = GetIfStat(ifname);
if(ifstat)
{ PrintIfStat(ifstat);
}
else
{ printf("Interface %s not found\n",ifname);
} }
class Ip4Address
{ char data[4*4];
public:
Ip4Address(uint32_t quad,char sep)
{ unsigned char* p = reinterpret_cast<unsigned char*>(&quad);
SUPPRESS_SECURITY_WARNING
sprintf(data,"%u%c%u%c%u%c%u",unsigned(p[0]),sep,unsigned(p[1]),sep,unsigned(p[2]),sep,unsigned(p[3]));
}
const char* Get() const
{ return data;
}
};
class HwAddress
{ char data[4*4];
public:
HwAddress(uint64_t quad2,char sep)
{ unsigned char* p = reinterpret_cast<unsigned char*>(&quad2);
SUPPRESS_SECURITY_WARNING
sprintf(data,"%u%c%u%c%u%c%u%c%u%c%u",// only has 48 bits, ignore p[0] and p[1]
unsigned(p[2]),sep,unsigned(p[3]),sep,unsigned(p[4]),sep,unsigned(p[5]),sep,unsigned(p[6]),sep,unsigned(p[7]));
}
const char* Get() const
{ return data;
}
};
void Network::PrintIfStat(IfStat* ifstat)
{ if(!ifstat || !ifstat->address)
{ return;
}
Ip4Address address(ifstat->address,'.');
Ip4Address netmask(ifstat->netmask,':');
Ip4Address broadcast(ifstat->broadcast,'.');
Ip4Address gateway(ifstat->gateway,'.');
HwAddress hw_address(ifstat->hw_address,':');
printf("Interface: %s ip=%s mask=%s gateway=%s\n broadcast=%s hw=%s mtu=%i\n",ifstat->ifname,address.Get(),netmask.Get(),gateway.Get(),broadcast.Get(),hw_address.Get(),ifstat->mtu);
ifstat->in.Print(true);
ifstat->out.Print(false);
}
}
#if 0
uint32_t GetInet4(SOCKET sock)
{ struct sockaddr_storage addr;
char ipstr[INET_ADDRSTRLEN];
socklen_t len = sizeof addr;
getpeername(sock, (struct sockaddr*)&addr, &len);
if (addr.ss_family != AF_INET)
{ return 0;
}
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
unsigned ip[4];
SUPPRESS_SECURITY_WARNING
scanf("%x.%x.%x.%x",ip,ip+1,ip+2,ip+3);
uint32_t r;
unsigned char* p = reinterpret_cast<unsigned char*>(&r);
p[0] = (unsigned char) ip[3];
p[1] = (unsigned char) ip[2];
p[2] = (unsigned char) ip[1];
p[3] = (unsigned char) ip[0];
return r;
}
f()
{
int s;
struct sockaddr_in sa;
int sa_len;
sa_len = sizeof(sa);
if (getsockname(s, &sa, &sa_len) == -1) {
perror("getsockname() failed");
return -1;
}
printf("Local IP address is: %s\n", inet_ntoa(sa.sin_add r));
printf("Local port is: %d\n", (int) ntohs(sa.sin_port));
}
#endif