How to get MAC address of your machine using a C program? - c

I am working on Ubuntu. How can I get MAC address of my machine or an interface say eth0 using C program.

Much nicer than all this socket or shell madness is simply using sysfs for this:
the file /sys/class/net/eth0/address carries your mac adress as simple string you can read with fopen()/fscanf()/fclose(). Nothing easier than that.
And if you want to support other network interfaces than eth0 (and you probably want), then simply use opendir()/readdir()/closedir() on /sys/class/net/.

You need to iterate over all the available interfaces on your machine, and use ioctl with SIOCGIFHWADDR flag to get the mac address. The mac address will be obtained as a 6-octet binary array. You also want to skip the loopback interface.
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) { /* handle error*/ };
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }
struct ifreq* it = ifc.ifc_req;
const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
}
else { /* handle error */ }
}
unsigned char mac_address[6];
if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
}

You want to take a look at the getifaddrs(3) manual page. There is an example in C in the manpage itself that you can use. You want to get the address with the type AF_LINK.

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
int main()
{
struct ifreq s;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strcpy(s.ifr_name, "eth0");
if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
int i;
for (i = 0; i < 6; ++i)
printf(" %02x", (unsigned char) s.ifr_addr.sa_data[i]);
puts("\n");
return 0;
}
return 1;
}

Using getifaddrs you can get MAC address from the family AF_PACKET.
In order to display the MAC address to each interface, you can proceed like this:
#include <stdio.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>
int main (int argc, const char * argv[])
{
struct ifaddrs *ifaddr=NULL;
struct ifaddrs *ifa = NULL;
int i = 0;
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
}
else
{
for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) )
{
struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
printf("%-8s ", ifa->ifa_name);
for (i=0; i <s->sll_halen; i++)
{
printf("%02x%c", (s->sll_addr[i]), (i+1!=s->sll_halen)?':':'\n');
}
}
}
freeifaddrs(ifaddr);
}
return 0;
}
Ideone

I have just write one and test it on gentoo in virtualbox.
// get_mac.c
#include <stdio.h> //printf
#include <string.h> //strncpy
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h> //ifreq
#include <unistd.h> //close
int main()
{
int fd;
struct ifreq ifr;
char *iface = "enp0s3";
unsigned char *mac = NULL;
memset(&ifr, 0, sizeof(ifr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);
if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) {
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
//display mac address
printf("Mac : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
close(fd);
return 0;
}

Assuming that c++ code (c++11) is okay as well and the interface is known.
#include <cstdint>
#include <fstream>
#include <streambuf>
#include <regex>
using namespace std;
uint64_t getIFMAC(const string &ifname) {
ifstream iface("/sys/class/net/" + ifname + "/address");
string str((istreambuf_iterator<char>(iface)), istreambuf_iterator<char>());
if (str.length() > 0) {
string hex = regex_replace(str, std::regex(":"), "");
return stoull(hex, 0, 16);
} else {
return 0;
}
}
int main()
{
string iface = "eth0";
printf("%s: mac=%016llX\n", iface.c_str(), getIFMAC(iface));
}

On Linux, use the service of "Network Manager" over the DBus.
There is also good'ol shell program which can be invoke and the result grabbed (use an exec function under C):
$ /sbin/ifconfig | grep HWaddr

A very portable way is to parse the output of this command.
ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'
Provided ifconfig can be run as the current user (usually can) and awk is installed (it often is). This will give you the mac address of the machine.

Expanding on the answer given by #user175104 ...
std::vector<std::string> GetAllFiles(const std::string& folder, bool recursive = false)
{
// uses opendir, readdir, and struct dirent.
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
bool ReadFileContents(const std::string& folder, const std::string& fname, std::string& contents)
{
// uses ifstream to read entire contents
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
std::vector<std::string> GetAllMacAddresses()
{
std::vector<std::string> macs;
std::string address;
// from: https://stackoverflow.com/questions/9034575/c-c-linux-mac-address-of-all-interfaces
// ... just read /sys/class/net/eth0/address
// NOTE: there may be more than one: /sys/class/net/*/address
// (1) so walk /sys/class/net/* to find the names to read the address of.
std::vector<std::string> nets = GetAllFiles("/sys/class/net/", false);
for (auto it = nets.begin(); it != nets.end(); ++it)
{
// we don't care about the local loopback interface
if (0 == strcmp((*it).substr(-3).c_str(), "/lo"))
continue;
address.clear();
if (ReadFileContents(*it, "address", address))
{
if (!address.empty())
{
macs.push_back(address);
}
}
}
return macs;
}

netlink socket is possible
man netlink(7) netlink(3) rtnetlink(7) rtnetlink(3)
#include <assert.h>
#include <stdio.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <unistd.h>
#define SZ 8192
int main(){
// Send
typedef struct {
struct nlmsghdr nh;
struct ifinfomsg ifi;
} Req_getlink;
assert(NLMSG_LENGTH(sizeof(struct ifinfomsg))==sizeof(Req_getlink));
int fd=-1;
fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
assert(0==bind(fd,(struct sockaddr*)(&(struct sockaddr_nl){
.nl_family=AF_NETLINK,
.nl_pad=0,
.nl_pid=getpid(),
.nl_groups=0
}),sizeof(struct sockaddr_nl)));
assert(sizeof(Req_getlink)==send(fd,&(Req_getlink){
.nh={
.nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlmsg_type=RTM_GETLINK,
.nlmsg_flags=NLM_F_REQUEST|NLM_F_ROOT,
.nlmsg_seq=0,
.nlmsg_pid=0
},
.ifi={
.ifi_family=AF_UNSPEC,
// .ifi_family=AF_INET,
.ifi_type=0,
.ifi_index=0,
.ifi_flags=0,
.ifi_change=0,
}
},sizeof(Req_getlink),0));
// Receive
char recvbuf[SZ]={};
int len=0;
for(char *p=recvbuf;;){
const int seglen=recv(fd,p,sizeof(recvbuf)-len,0);
assert(seglen>=1);
len += seglen;
if(((struct nlmsghdr*)p)->nlmsg_type==NLMSG_DONE||((struct nlmsghdr*)p)->nlmsg_type==NLMSG_ERROR)
break;
p += seglen;
}
struct nlmsghdr *nh=(struct nlmsghdr*)recvbuf;
for(;NLMSG_OK(nh,len);nh=NLMSG_NEXT(nh,len)){
if(nh->nlmsg_type==NLMSG_DONE)
break;
struct ifinfomsg *ifm=(struct ifinfomsg*)NLMSG_DATA(nh);
printf("#%d ",ifm->ifi_index);
#ifdef _NET_IF_H
#pragma GCC error "include <linux/if.h> instead of <net/if.h>"
#endif
// Part 3 rtattr
struct rtattr *rta=IFLA_RTA(ifm); // /usr/include/linux/if_link.h
int rtl=RTM_PAYLOAD(nh);
for(;RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl))switch(rta->rta_type){
case IFLA_IFNAME:printf("%s ",(const char*)RTA_DATA(rta));break;
case IFLA_ADDRESS:
printf("hwaddr ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_BROADCAST:
printf("bcast ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_PERM_ADDRESS:
printf("perm ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
}
printf("\n");
}
close(fd);
fd=-1;
return 0;
}
Example
#1 lo hwaddr 00:00:00:00:00:00 bcast 00:00:00:00:00:00
#2 eth0 hwaddr 57:da:52:45:5b:1a bcast ff:ff:ff:ff:ff:ff perm 57:da:52:45:5b:1a
#3 wlan0 hwaddr 3c:7f:46:47:58:c2 bcast ff:ff:ff:ff:ff:ff perm 3c:7f:46:47:58:c2

This is a Bash line that prints all available mac addresses, except the loopback:
for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done
Can be executed from a C program.

Related

Reading and writing EEPROM via I2C with Linux

I trying to read and write an Atmel 24C256 EEPROM with a Raspberry Pi B+ over I2C, but I'm having trouble getting it all to work right.
Here is the code I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <linux/i2c.h>
#define DEVICE_PATH "/dev/i2c-1"
#define PAGE_SIZE 64
#define DEVICE_ADDR 0x50 // 0b1010xxxx
int file_desc;
char buffer[PAGE_SIZE + 2]; // 64 bytes + 2 for the address
void teardownI2C()
{
int result = close(file_desc);
}
void setupI2C()
{
file_desc = open(DEVICE_PATH, O_RDWR);
if(file_desc < 0)
{
printf("%s\n", strerror(errno));
exit(1);
}
if(ioctl(file_desc, I2C_SLAVE, DEVICE_ADDR) < 0)
{
printf("%s\n", strerror(errno));
teardownI2C();
exit(1);
}
}
int write_to_device(char addr_hi, char addr_lo, char * buf, int len)
{
struct i2c_rdwr_ioctl_data msg_rdwr;
struct i2c_msg i2cmsg;
char my_buf[PAGE_SIZE + 2];
if(len > PAGE_SIZE + 2)
{
printf("Can't write more than %d bytes at a time.\n", PAGE_SIZE);
return -1;
}
int i;
my_buf[0] = addr_hi;
my_buf[1] = addr_lo;
for(i= 0; i < len; i++)
{
my_buf[2+i] = buf[i];
}
msg_rdwr.msgs = &i2cmsg;
msg_rdwr.nmsgs = 1;
i2cmsg.addr = DEVICE_ADDR;
i2cmsg.flags = 0;
i2cmsg.len = 2+len;
i2cmsg.buf = my_buf;
if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
{
printf("write_to_device(): %s\n", strerror(errno));
return -1;
}
return 0;
}
int read_from_device(char addr_hi, char addr_lo, char * buf, int len)
{
struct i2c_rdwr_ioctl_data msg_rdwr;
struct i2c_msg i2cmsg;
if(write_to_device(addr_hi, addr_lo ,NULL,0)<0)
{
printf("read_from_device(): address reset did not work\n");
return -1;
}
msg_rdwr.msgs = &i2cmsg;
msg_rdwr.nmsgs = 1;
i2cmsg.addr = DEVICE_ADDR;
i2cmsg.flags = I2C_M_RD;
i2cmsg.len = len;
i2cmsg.buf = buf;
if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
{
printf("read_from_device(): %s\n", strerror(errno));
return -1;
}
return 0;
}
void fill_buffer(char *buf)
{
int i = 0;
while(i < PAGE_SIZE && *buf)
{
buffer[i+2] = *buf++;
}
while(i++ < PAGE_SIZE-1)
{
buffer[i+2] = '*'; // fill the buffer with something
}
}
int main()
{
setupI2C(); //setup
fill_buffer("Here are some words.");
write_to_device(0x01, 0x00, buffer, PAGE_SIZE);
char newbuf[PAGE_SIZE];
if(read_from_device(0x01, 0x00, newbuf, PAGE_SIZE)>0)
{
printf("%s\n", newbuf);
}
teardownI2C(); //cleanup
return EXIT_SUCCESS;
}
Writing to the device like in the line write_to_device(0x01, 0x00, buffer, PAGE_SIZE); doesn't generate any errors but when I try to read from the device, I have to write a "dummy" byte according to the spec sheet and then try to read from the device but for some reason writing the dummy byte results in an error "Input/output error". I can't figure out how this works. I am using two resources to guide me, the Linux I2C-Dev documentation and an example from a similar EEPROM device. I'm sort of stuck here and don't know what to try. Any suggestions or pointers are greatly appreciated!
Alternatively, you could access it via the kernel at24.c driver, if you're able to compile and install a different kernel device tree for your Raspberry Pi.
The kernel device tree needs to specify the EEPROM's type and address, and which I²C bus it's connected to. I'm not sure about Raspberry Pi, but for the BeagleBone Black EEPROM it goes like this:
&i2c0 {
eeprom: eeprom#50 {
compatible = "at,24c32";
reg = <0x50>;
};
};
For your device you'd specify compatible = "at,24c256";
Ensure the kernel config specifies CONFIG_EEPROM_AT24=y (or =m).
Then you should be able to access the EEPROM memory from userspace at something like /sys/bus/i2c/devices/0-0050/eeprom or /sys/bus/i2c/drivers/at24/0-0050/eeprom.
maybe this here might help. http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program since it handles apparently the device you are trying to program and also explains some caveats of addressing 24c256
Craig McQueen's answer got me on the right track, but it is not easy to figure the whole thing out on your own.
Here is a AT24C256 device tree overlay that works for me on the Raspberry Pi:
/dts-v1/;
/plugin/;
/ {
fragment#0 {
target = <&i2c1>;
overlay {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
status = "okay";
at24#50 {
compatible = "atmel,24c256","at24";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x50>;
pagesize = <64>;
size = <32768>;
address-width = <16>;
};
};
};
};
Save it to "at24c256.dts", compile (might need to install the device tree compiler) it using:
dtc -O dtb -o at24c256.dtbo -b 0 -# at24c256.dts
and save it in "/boot/overlays". Then activate the overlay by adding:
dtparam=i2c_arm=on
dtoverlay=at24c256
to "/boot/config.txt" and reboot. You should now have a device file "/sys/class/i2c-dev/i2c-1/device/1-0050/eeprom" (if your I2C bus number is 1) which you can write to like a normal file.
Write to it using e.g.:
echo 'Hello World' | sudo tee /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom
Read from it using e.g.:
sudo more /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom
Not sure how you can get around the su-rights for accessing the device though. Adding the user to the i2c-group does not help...
Small and simple program to understand the easy management of an eeprom
/*
Simple program to write / read the eeprom AT24C32.
Developed and tested on the Raspberry pi3B jessie
To create the executable use the following command:
gcc -Wall -o thisprogram.exe thisprogram.c
*/
#include <stdio.h>
#include <sys/ioctl.h> // ioctl
#include <fcntl.h> // open
#include <unistd.h> // read/write usleep
#include <time.h>
#include <netinet/in.h> // htons
#include <linux/i2c-dev.h>
#pragma pack(1)
#define PAGESIZE 32
#define NPAGES 128
#define NBYTES (NPAGES*PAGESIZE)
#define ADDRESS 0x57 // AT24C32's address on I2C bus
typedef struct {
ushort AW;
char buf[PAGESIZE+2];
}WRITE;
static WRITE AT = {0};
int main() {
int fd;
char bufIN[180] = {0};
time_t clock=time(NULL);
snprintf(AT.buf, PAGESIZE+1, "%s: my first attempt to write", ctime(&clock)); // the buffer to write, cut to 32 bytes
if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) { printf("Couldn't open device! %d\n", fd); return 1; }
if (ioctl(fd, I2C_SLAVE, ADDRESS) < 0) { printf("Couldn't find device on address!\n"); return 1; }
AT.AW = htons(32); // I will write to start from byte 0 of page 1 ( 32nd byte of eeprom )
if (write(fd, &AT, PAGESIZE+2) != (PAGESIZE+2)) { perror("Write error !"); return 1; }
while (1) { char ap[4]; if (read(fd,&ap,1) != 1) usleep(500); else break; } // wait on write's end
if (write(fd, &AT, 2) != 2) { perror("Error in sending the reading address"); return 1; }
if (read(fd,bufIN,PAGESIZE) != PAGESIZE) { perror("reading error\n"); return 1;}
printf ("\n%s\n", bufIN);
close(fd);
return 0;
}
My code:
enter code here
__s32 write_eeprom(__s32 fd,__u32 offset,__u32 len,__u8 *buf)
{
__s32 ret;
struct i2c_rdwr_ioctl_data msg_set;
struct i2c_msg iomsgs;
__u32 sended, sending;
__u8 temp[ONE_PAGE + 1];
if((offset + len) > BYTES_MAX || len == 0)
{
printf("write too long than BYTES_MAX\n");
return -1;
}
sended = 0;
iomsgs.addr = DEVICE_ADDR;
iomsgs.flags = 0; //write
iomsgs.buf = temp;
msg_set.msgs = &iomsgs;
msg_set.nmsgs = 1;
while(len > sended)
{
if(len - sended > ONE_PAGE)
sending = ONE_PAGE;
else
sending = len - sended;
iomsgs.len = sending + 1;
temp[0] = offset + sended;
memcpy(&temp[1], buf + sended, sending);
//printf("sending:%d sended:%d len:%d offset:%d \n", sending, sended, len, offset);
ret = ioctl(fd, I2C_RDWR, (unsigned long)&msg_set);
if(ret < 0)
{
printf("Error dring I2C_RDWR ioctl with error code: %d\n", ret);
return ret;
}
sended += sending;
usleep(5000);
}
return sended;
}

Polling interface names via SIOCGIFCONF in Linux

I'm attempting to poll networking device names. I've pieced this together from various snippets,
http://unixhelp.ed.ac.uk/CGI/man-cgi?netdevice+7
http://lists.apple.com/archives/Unix-porting/2002/Apr/msg00134.html
http://ubuntuforums.org/showthread.php?t=1421487
But my output is just gibberish.
#include <stdio.h>
#include <stdlib.h>
#include <net/route.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define BUFLEN 1024
#define SEQ 9999
int main (int argc, const char* argv[])
{
// File descriptor for socket
int socketfd;
struct ifconf conf;
struct ifreq req[10];
struct ifreq *ifr;
printf("Opening socket...");
socketfd = socket(AF_ROUTE, SOCK_RAW, 0);
if (socketfd >= 0) {
printf(" OK\n");
conf.ifc_len = sizeof(req);
conf.ifc_buf = (__caddr_t) req;
ioctl(socketfd,SIOCGIFCONF,&conf);
printf("Discovering interfaces...\n");
int i;
for (i=0; i<conf.ifc_len/sizeof(req[0]); i++) {
ifr = &conf.ifc_req[i];
printf("%d. %s\n", i+1, req[i].ifr_name);
}
}
else {
printf("Failed!\n");
}
return 0;
}
Output:
Opening socket... OK
Discovering interfaces...
?u???}??Gh???
2. p?9}?
3.
4. v?=?n??u?`?y??]g?<?~?v??
5.
6.
7.
8. ?v?T?
9. ?|?mw??j??v??h??|??v?T00~??v?$?|??|?#
10. T00~??v?$?|??|?#
I tried outputting each char of the ifr_name array one-by-one to see if they were null terminated but that didn't change much. Each iteration of my program outputs something different so this leads me to think I'm referencing something wrong. Can someone provide me some insight as to what I may be doing wrong?
Here's some code I put together for Mac OS X:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/* This is defined on Mac OS X */
#ifndef _SIZEOF_ADDR_IFREQ
#define _SIZEOF_ADDR_IFREQ sizeof
#endif
int main (int argc, const char* argv[])
{
// File descriptor for socket
int socketfd;
struct ifconf conf;
char data[4096];
struct ifreq *ifr;
char addrbuf[1024];
int i;
printf("Opening socket...");
socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (socketfd >= 0) {
printf(" OK\n");
conf.ifc_len = sizeof(data);
conf.ifc_buf = (caddr_t) data;
if (ioctl(socketfd,SIOCGIFCONF,&conf) < 0) {
perror("ioctl");
}
printf("Discovering interfaces...\n");
i = 0;
ifr = (struct ifreq*)data;
while ((char*)ifr < data+conf.ifc_len) {
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
++i;
printf("%d. %s : %s\n", i, ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr, addrbuf, sizeof(addrbuf)));
break;
#if 0
case AF_INET6:
++i;
printf("%d. %s : %s\n", i, ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in6*)&ifr->ifr_addr)->sin6_addr, addrbuf, sizeof(addrbuf)));
break;
#endif
}
ifr = (struct ifreq*)((char*)ifr +_SIZEOF_ADDR_IFREQ(*ifr));
}
close(socketfd);
}
else {
printf(" Failed!\n");
}
return 0;
}
Poll as in you want to be notified if an interface is added or removed? Or polled as in you just want to find out the interface names once from the system? If the latter, take a look at getifaddrs().
Please see http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=blob;f=examples/rtnl/rtnl-link-dump.c;hb=HEAD on how to get the list of interfaces on Linux. AF_ROUTE is some BSD thing and the use of ioctl is discouraged on Linux for its apparent limitations (such as to convey multiple addresses on a single interface).

libpcap get MAC from AF_LINK sockaddr_dl (OSX)

I am trying to obtain the MAC addresses of all of my interface on OSX using C. The common ways to obtain it Linux dont work on BSD - from everything I have seen, you must obtain the interfaces and look for the ones that are of type AF_LINK. My problem is that the LLADDR(sockaddr_dl) gives me a whole bunch of data (which includes my MAC) and I dont know what format the data is in. For example; the following code will output:
Device: en1
link sdl_alen: 101 mac:
31:f8:1e:df:d6:22:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:b0:06:10:00:01:00:00:00:c0:02:10:00:01:00:00:00:00:00:00:00:00:00:00:00:40
:03:10:00:01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:03:00:6c:6f:30:00:00:00:00:00:00:00:00:00:00:00:00:00:70:03:10:00:01:00:00:00:e0:
02:10:00:01:00:00:
My MAC is bolded. It seems that this is the format all of the time, but I would be a lot more comfortable if I could cast LLADDR(sockaddr_dl) to something. In the net/if_dl.h, LLADDR is defied as:
#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
which, as far as I can tell, is saying that the results are of type (void *) - no help.
Other posts like:
Having a problem figuring out how to get Ethernet interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?
seem to think they have it figured out, but if you look through the code, you can see it will not work due to sdl_alen not being 6.
int main() {
pcap_if_t *alldevs;
pcap_if_t *d;
pcap_addr_t *alladdrs;
pcap_addr_t *a;
struct sockaddr_dl* link;
char eb[PCAP_ERRBUF_SIZE];
char *addr_buf[40];
if (pcap_findalldevs(&alldevs, eb) == -1) {
printf("no devs found\n");
return(-1);
}
for (d = alldevs; d != NULL; d = d->next) {
printf("Device: %s\n", d->name);
alladdrs = d->addresses;
for (a = alladdrs; a != NULL; a = a->next) {
if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
// MAC ADDRESS
//struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
link = (struct sockaddr_dl*)a->addr->sa_data;
char mac[link->sdl_alen];
caddr_t macaddr = LLADDR(link);
memcpy(mac, LLADDR(link), link->sdl_alen);
printf("link sdl_alen: %i\n", link->sdl_alen);
int i;
printf("mac: ");
for(i = 0; i<link->sdl_alen; i++){
printf("%02x:", (unsigned char)mac[i]);
}
printf("\n");
}
}
}
}
The problem is that you are casting the sockaddr->sa_data to sockaddr_dl instead of casting the sockaddr itself to sockaddr_dl. Keep in mind that sockaddr_dl is an OS X/BSD thing, so #ifdef that part for portability.
Don't do:
link = (struct sockaddr_dl*)a->addr->sa_data;
Do:
link = (struct sockaddr_dl*)a->addr;
Then you will get the correct sdl_alen and things will work with out any hacks. And if you want to really easily get the name of addresses that may be either AF_INET, AF_INET6, or AF_LINK use getnameinfo():
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if_dl.h>
int get_sock_len(struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_LINK:
return sizeof(struct sockaddr_dl);
default:
return -1;
}
}
int get_numeric_address(struct sockaddr *sa, char *outbuf, size_t buflen) {
socklen_t len;
if ((len = get_sock_len(sa)) < 0) {
return -1;
}
if (getnameinfo(sa, len, outbuf, buflen, NULL, 0, NI_NUMERICHOST)) {
return -1;
}
return 0;
}
...
char buf[NI_MAXHOST];
if (!get_numeric_address(sa, buf, sizeof(buf))) { /* For some struct sockaddr *sa */
printf("address: %s\n", buf);
} else {
printf("doh!\n");
}
Here is what I ended up doing:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_dl.h>
#include <pcap.h>
#include "mac.h"
int main() {
printf("en1: %s\n", lookupDeviceMac("vnic0"));
}
unsigned char *lookupDeviceMac(char *dev){
pcap_if_t *alldevs;
pcap_if_t *d;
pcap_addr_t *alladdrs;
pcap_addr_t *a;
struct sockaddr_dl* link;
char eb[PCAP_ERRBUF_SIZE];
char *ret = malloc(6);
if (pcap_findalldevs(&alldevs, eb) == -1) {
printf("%s\n", eb);
return(ret);
}
for (d = alldevs; d != NULL; d = d->next) {
if(strcmp(d->name, dev) == 0){
printf("Device: %s\n", d->name);
alladdrs = d->addresses;
for (a = alladdrs; a != NULL; a = a->next) {
if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
// MAC ADDRESS
//struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
link = (struct sockaddr_dl*)a->addr->sa_data;
char mac[link->sdl_alen];
caddr_t macaddr = LLADDR(link);
memcpy(mac, LLADDR(link), link->sdl_alen);
if(link->sdl_alen == 6){
// Seen in some sample code
sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[0],
(unsigned char)mac[1],
(unsigned char)mac[2],
(unsigned char)mac[3],
(unsigned char)mac[4],
(unsigned char)mac[5]);
} else if(link->sdl_alen > 6) {
// This is what happens in OSX 10.6.5
sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[1],
(unsigned char)mac[2],
(unsigned char)mac[3],
(unsigned char)mac[4],
(unsigned char)mac[5],
(unsigned char)mac[6]);
}
return(ret);
}
}
}
}
}
I was trying to see all devices reported by pcap_findalldevs and ended up here looking for information on interpreting AF_LINK addresses on MAC OS.
I'm used to seeing the struct sockaddr standing in for a interface family and being immediately cast to the appropriate type and not writing code to access *sa_data*.
For what I wanted it was sufficient to use link_ntoa to convert the address to a human readable form.
#import <Foundation/Foundation.h>
#include <pcap.h>
#include <netinet/in.h>
#include <net/if_dl.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
pcap_if_t* allDevs = NULL;
char errbuff[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&allDevs, errbuff) <0) {
NSLog(#"Failed with error '%s'", errbuff);
}
else {
for (pcap_if_t* device = allDevs; device != NULL; device = device->next) {
for (pcap_addr_t* address = device->addresses; address != NULL; address = address->next) {
struct sockaddr* sa_addr = address->addr;
if (sa_addr->sa_family == AF_LINK) {
struct sockaddr_dl* link_addr = (struct sockaddr_dl*) sa_addr;
char* linkAddress = link_ntoa(link_addr);
NSLog(#"ntoa %s", linkAddress);
}
}
}
}
pcap_freealldevs(allDevs);
[pool drain];
return 0;
}
Running on my machine I get the following devices with AF_LINK entries.
2011-08-14 02:22:43.024 HomePlugToolHelper[12473:903] ntoa en0:0.16.cb.xx.x.xx
2011-08-14 02:22:43.027 HomePlugToolHelper[12473:903] ntoa fw0:0.16.cb.xx.xx.xx.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa en1:0.16.cb.x.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa lo0

Get IP address of an interface on Linux

How can I get the IPv4 address of an interface on Linux from C code?
For example, I'd like to get the IP address (if any) assigned to eth0.
Try this:
#include <stdio.h>
#include <unistd.h>
#include <string.h> /* for strncpy */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
int
main()
{
int fd;
struct ifreq ifr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
/* I want to get an IPv4 IP address */
ifr.ifr_addr.sa_family = AF_INET;
/* I want IP address attached to "eth0" */
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ-1);
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
/* display result */
printf("%s\n", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
return 0;
}
The code sample is taken from here.
In addition to the ioctl() method Filip demonstrated you can use getifaddrs(). There is an example program at the bottom of the man page.
If you're looking for an address (IPv4) of the specific interface say wlan0 then
try this code which uses getifaddrs():
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
exit(EXIT_FAILURE);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if((strcmp(ifa->ifa_name,"wlan0")==0)&&(ifa->ifa_addr->sa_family==AF_INET))
{
if (s != 0)
{
printf("getnameinfo() failed: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("\tInterface : <%s>\n",ifa->ifa_name );
printf("\t Address : <%s>\n", host);
}
}
freeifaddrs(ifaddr);
exit(EXIT_SUCCESS);
}
You can replace wlan0 with eth0 for ethernet and lo for local loopback.
The structure and detailed explanations of the data structures
used could be found here.
To know more about linked list in C this page will be a good starting point.
My 2 cents: the same code works even if iOS:
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
showIP();
}
void showIP()
{
struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
exit(EXIT_FAILURE);
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if( /*(strcmp(ifa->ifa_name,"wlan0")==0)&&( */ ifa->ifa_addr->sa_family==AF_INET) // )
{
if (s != 0)
{
printf("getnameinfo() failed: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("\tInterface : <%s>\n",ifa->ifa_name );
printf("\t Address : <%s>\n", host);
}
}
freeifaddrs(ifaddr);
}
#end
I simply removed the test against wlan0 to see data.
ps You can remove "family"
I have been in the same issue recently, and this is the code I made up and it works. Make sure to use the name of the network interface, exactly as you have it (could be "eth0" or else).
gotta check if ifconfigcommand beforehand to get the interface name and use it in C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <linux/if.h>
#include <errno.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void extract_ipaddress()
{
//create an ifreq struct for passing data in and out of ioctl
struct ifreq my_struct;
//declare and define the variable containing the name of the interface
char *interface_name="enp0s3"; //a very frequent interface name is "eth0";
//the ifreq structure should initially contains the name of the interface to be queried. Which should be copied into the ifr_name field.
//Since this is a fixed length buffer, one should ensure that the name does not cause an overrun
size_t interface_name_len=strlen(interface_name);
if(interface_name_len<sizeof(my_struct.ifr_name))
{
memcpy(my_struct.ifr_name,interface_name,interface_name_len);
my_struct.ifr_name[interface_name_len]=0;
}
else
{
perror("Copy name of interface to ifreq struct");
printf("The name you provided for the interface is too long...\n");
}
//provide an open socket descriptor with the address family AF_INET
/* ***************************************************************
* All ioctl call needs a file descriptor to act on. In the case of SIOCGIFADDR this must refer to a socket file descriptor. This socket must be in the address family that you wish to obtain (AF_INET for IPv4)
* ***************************************************************
*/
int file_descriptor=socket(AF_INET, SOCK_DGRAM,0);
if(file_descriptor==-1)
{
perror("Socket file descriptor");
printf("The construction of the socket file descriptor was unsuccessful.\n");
return -1;
}
//invoke ioctl() because the socket file descriptor exists and also the struct 'ifreq' exists
int myioctl_call=ioctl(file_descriptor,SIOCGIFADDR,&my_struct);
if (myioctl_call==-1)
{
perror("ioctl");
printf("Ooops, error when invoking ioctl() system call.\n");
close(file_descriptor);
return -1;
}
close(file_descriptor);
/* **********************************************************************
* If this completes without error , then the hardware address of the interface should have been returned in the 'my_struct.ifr_addr' which is types as struct sockaddr_in.
* ***********************************************************************/
//extract the IP Address (IPv4) from the my_struct.ifr_addr which has the type 'ifreq'
/* *** Cast the returned address to a struct 'sockaddr_in' *** */
struct sockaddr_in * ipaddress= (struct sockaddr_in *)&my_struct.ifr_addr;
/* *** Extract the 'sin_addr' field from the data type (struct) to obtain a struct 'in_addr' *** */
printf("IP Address is %s.\n", inet_ntoa(ipaddress->sin_addr));
}
If you don't mind the binary size, you can use iproute2 as library.
iproute2-as-lib
Pros:
No need to write the socket layer code.
More or even more information about network interfaces can be got. Same functionality with the iproute2 tools.
Simple API interface.
Cons:
iproute2-as-lib library size is big. ~500kb.
I found a quite easy way to get ip, by take advantage of using bash command:
hostname -I
but use "hostname -I" natively will print the result on screen, we need to use "popen()" to read result out and save it into a string, here is c code:
#include <stdio.h> // popen
#include "ip_common_def.h"
const char * get_ip()
{
// Read out "hostname -I" command output
FILE *fd = popen("hostname -I", "r");
if(fd == NULL) {
fprintf(stderr, "Could not open pipe.\n");
return NULL;
}
// Put output into a string (static memory)
static char buffer[IP_BUFFER_LEN];
fgets(buffer, IP_BUFFER_LEN, fd);
// Only keep the first ip.
for (int i = 0; i < IP_BUFFER_LEN; ++i)
{
if (buffer[i] == ' ')
{
buffer[i] = '\0';
break;
}
}
char *ret = malloc(strlen(buffer) + 1);
memcpy(ret, buffer, strlen(buffer));
ret[strlen(buffer)] = '\0';
printf("%s\n", ret);
return ret;
}

How do I output my host’s IP addresses from a C program?

I need to display all the IP addresses from my local computer, using the C language. How can this be done?
#include <stdio.h>
#include <stropts.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netdevice.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int print_addresses(const int domain)
{
int s;
struct ifconf ifconf;
struct ifreq ifr[50];
int ifs;
int i;
s = socket(domain, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
return 0;
}
ifconf.ifc_buf = (char *) ifr;
ifconf.ifc_len = sizeof ifr;
if (ioctl(s, SIOCGIFCONF, &ifconf) == -1) {
perror("ioctl");
return 0;
}
ifs = ifconf.ifc_len / sizeof(ifr[0]);
printf("interfaces = %d:\n", ifs);
for (i = 0; i < ifs; i++) {
char ip[INET_ADDRSTRLEN];
struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;
if (!inet_ntop(domain, &s_in->sin_addr, ip, sizeof(ip))) {
perror("inet_ntop");
return 0;
}
printf("%s - %s\n", ifr[i].ifr_name, ip);
}
close(s);
return 1;
}
int main(int argc, char *argv[])
{
int domains[] = { AF_INET, AF_INET6 };
int i;
for (i = 0; i < sizeof(domains) / sizeof(domains[0]); i++)
if (!print_addresses(domains[i]))
return 1;
return 0;
}
Your question might be imprecise but I am not sure why everyone is breaking your chops.
I think you are asking the basics in which case you probably want is getifaddrs. The man page has a little example program.
You can also get similar info using the SIOCGIFCONF option with ioctl(). There is some sample code on here and the web.
If you search around for these and similar terms you will find this question has been asked in various forms before. You have to dig around a bit.
Also note, these will not give you the public facing IP of your network if you are behind NAT.
Another way to do it in C. I do have to say though.... there are so many ways to do it from the shell, what's the point?
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
void show_address_info( struct ifaddrs *ifa ){
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
/* ipv6 addresses have to fit in this buffer */
char buf[64];
if (AF_INET == ifa->ifa_addr->sa_family){
s4 = (struct sockaddr_in *)(ifa->ifa_addr);
if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s4->sin_addr), buf, sizeof(buf))){
printf("%s: inet_ntop failed!\n", ifa->ifa_name);
} else {
printf("IPv4 addr %s: %s\n", ifa->ifa_name, buf);
}
}
else if (AF_INET6 == ifa->ifa_addr->sa_family) {
s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))) {
printf("%s: inet_ntop failed!\n", ifa->ifa_name);
} else {
printf("IPv6 addr %s: %s\n", ifa->ifa_name, buf);
}
}
}
int main(int argc, char **argv){
struct ifaddrs *myaddrs, *ifa;
int status;
status = getifaddrs(&myaddrs);
if (status != 0){
perror("getifaddrs failed!");
exit(1);
}
for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next){
if (NULL == ifa->ifa_addr){
continue;
}
if ((ifa->ifa_flags & IFF_UP) == 0) {
continue;
}
show_address_info(ifa);
}
freeifaddrs(myaddrs);
return 0;
}
How about you simply cheat and look at the source of /sbin/ifconfig/ ? Nothing wrong with standing on the shoulders of other giants...
Not a complete solution yet, but take a look in /proc/net!
dev lists the available interface devices by name,
route lists some routes, as does ipv6_route,
arp lists devices in the actual routing table (does not include localhost).
Not as high-tech as the other solution, but it can be done with simple file reading. Linux specific, though.
You need POSIX function getaddrinfo() - it returns linked list of all IP addresses.
See man getaddrinfo for details and examples.
$ sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1
213.xx.xxx.xx
192.168.xx.x
127.0.0.1
And you can put that into popen():
/* not tested */
ph = popen("sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1", "r");
while (fgets(buf, sizeof buf, ph)) {
/* ip address, in nul-terminated string format, is in `buf` */
}
pclose(ph);
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
/*
* Who sez?
* http://blog.stackoverflow.com/2010/01/stack-overflow-where-we-hate-fun/
*/
int main(int argc, char *argv[])
{
int status;
const char * const cmd = /* die from END is too chatty */
"/sbin/ifconfig -a | \
perl -lne \
'print $1 if /inet6? addr:\\s*(\\S+)/; \
END { $. > 0 or \
warn(\"no output from ifconfig\\n\"), \
exit 1; }'";
status = system(cmd);
if (status < 0) {
perror("system");
return 1;
}
else if (status != 0) {
const char *extra;
status = WEXITSTATUS(status);
extra = status == 127 ? " (is /bin/sh ok?)" : "";
fprintf(stderr, "%s: command failed with status %d%s\n",
argv[0], status, extra);
}
return 0;
}

Resources