First of all , Actually i am java developer and this code maybe unclear for C developers , sorry ....
I try to get network interface informations with native C Programming.
This is my example code :
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <linux/if_link.h>
#include <linux/types.h>
#include <sys/socket.h>
#include <list>
using namespace std ;
class Network {
private:
struct ifaddrs *ifaddrs ;
char host[NI_MAXHOST];
public:
Network() {
if (getifaddrs(&ifaddrs) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}
}
~Network() {
freeifaddrs(ifaddrs);
}
list<string> interfaces() {
list<string> names ;
for (struct ifaddrs *ifa = ifaddrs ; ifa != NULL ; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL) continue ;
if (ifa->ifa_addr->sa_family == AF_PACKET) {
names.push_back(ifa->ifa_name);
}
}
return names ;
}
int mac_addr(const char *name,char *mac_addr) {
int s;
struct ifreq buffer;
s = socket(PF_INET, SOCK_DGRAM, 0);
memset(&buffer, 0x00, sizeof(buffer));
strcpy(buffer.ifr_name, name);
ioctl(s, SIOCGIFHWADDR, &buffer);
close(s);
char *data = buffer.ifr_hwaddr.sa_data;
sprintf(mac_addr,
"x:x:x:x:x:x",
(unsigned char)data[0],
(unsigned char)data[1],
(unsigned char)data[2],
(unsigned char)data[3],
(unsigned char)data[4],
(unsigned char)data[5]);
return 0 ;
}
int ipaddr(const char *name, bool ipv4,bool netmask,char *ip) {
void * tmpAddrPtr;
for (struct ifaddrs *ifa = ifaddrs ; ifa != NULL ; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL) continue ;
int family = ifa->ifa_addr->sa_family ;
if (strcmp(ifa->ifa_name,name) == 0 && family == AF_INET && ipv4) {
tmpAddrPtr = netmask ?
&((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr :
&((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr ;
inet_ntop(AF_INET, tmpAddrPtr, ip, INET_ADDRSTRLEN);
return 0 ;
} else if (strcmp(ifa->ifa_name,name) == 0 && family == AF_INET6 && !ipv4) {
tmpAddrPtr = netmask ?
&((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr :
&((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr ;
inet_ntop(AF_INET6, tmpAddrPtr, ip, INET6_ADDRSTRLEN);
return 0 ;
}
}
return 1 ;
}
int link_stats(const char *name,struct rtnl_link_stats *stats) {
for (struct ifaddrs *ifa = ifaddrs ; ifa != NULL ; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL) continue ;
int family = ifa->ifa_addr->sa_family ;
if (strcmp(ifa->ifa_name,name) == 0 && family == AF_PACKET && ifa->ifa_data != NULL) {
stats = (struct rtnl_link_stats *) ifa->ifa_data;
return 0 ;
}
}
return 1 ;
}
} ;
int main() {
Network network ;
list<string> interface_list = network.interfaces() ;
list<string>::iterator it ;
const char* ethernet = "wlan0" ;
char *ip = new char[16] ;
struct rtnl_link_stats *stats ;
network.ipaddr(ethernet,true,false,ip) ;
network.link_stats(ethernet,stats) ;
cout << "Ethernet Name: " << ethernet << endl ;
cout << "Ethernet IP: " << ip << endl ;
printf("\t\ttx_packets = u; rx_packets = u\n"
"\t\ttx_bytes = u; rx_bytes = u\n",
stats->tx_packets, stats->rx_packets,
stats->tx_bytes, stats->rx_bytes);
free(ip);
return 0;
}
First Question :
Why this code some times result Segmentation fault ?!
> make
mkdir -p build
c -g -o build/main main.cpp
strip --strip-all build/main
--------------------------------------------------------
> ./build/main
Segmentation fault (core dumped)
> ./build/main
Segmentation fault (core dumped)
> ./build/main
Ethernet Name: wlan0
Ethernet IP: 192.168.1.123
tx_packets = 0; rx_packets = 4294967295
tx_bytes = 32615; rx_bytes = 495411392
> ./build/main
Ethernet Name: wlan0
Ethernet IP: 192.168.1.123
tx_packets = 3077881989; rx_packets = 818185032
tx_bytes = 609520456; rx_bytes = 5081928
> ./build/main
Segmentation fault (core dumped)
Second Quesion :
Why result is unstable ?!
for example tx_bytes some times return 32615
or 609520456
or ... .
CodePudding user response:
Why the segfault?
int ipaddr(const char *name, bool ipv4,bool netmask,char *ip) {
...
inet_ntop(AF_INET, tmpAddrPtr, ip, INET_ADDRSTRLEN);
...
int main() {
char *ip;
network.ipaddr(ethernet,true,false,ip) ;
You are using an uninitialised pointer ip
right there.
I would fix it like this
Change ip
to be a string
string ip;
Change ipaddr
to take that parameter by reference
int ipaddr(const char *name, bool ipv4,bool netmask,string& ip) {
Resize the string to the correct size immediately prior to calling inet_ntop
and then use string::data
to get the pointer inet_top
requires.
ip.resize(INET_ADDRSTRLEN);
inet_ntop(AF_INET, tmpAddrPtr, ip.data(), INET_ADDRSTRLEN)
Similar change for the other call to inet_top
Good rule of thumb for C programs which have to interact with C API is to always hold your data in C objects (string, vectors etc) and convert to the pointers that the C API requires only at the point where you call the C API.