这一段主要是来自于:
Suppose that you wish to display the IPv4 address of a network interface. The variable if_name points to a null-terminated string containing the name of the interface (for example, eth0).
On Linux-based systems, one way to obtain the IPv4 address of an interface is to use the ioctl command SIOCGIFADDR. The method described here has four steps:
Create an ifreq structure for passing data in and out of ioctl.
Provide an open socket descriptor with the address family AF_INET.
Invoke ioctl.
Extract the IP address from the ifreq structure.
The following header files are needed when using this method:
-
#include <sys/ioctl.h>
-
#include <net/if.h>
-
#include <netinet/in.h>
In addition, this particular implementation makes use of:
-
#include <errno.h>
-
#include <string.h>
-
#include <stdio.h>
-
#include <arpa/inet.h>
Please note that whilst this method can be used with some network protocols other than IPv4, the Linux implementation does not support IPv6. Furthermore it is only able to return a single result for any given network protocol, so will only return one of the addresses of an interface that has several. It is not necessarily portable to other POSIX-compatible systems, and is no longer the preferred method on Linux.
Create an ifreq structure for passing data in and out of ioctl
The ifreq structure should initially contain the name of the interface to be queried, which should be copied into the ifr_name field. Since this is a fixed-length buffer you should take care to ensure that the name does not cause an overrun:
-
struct ifreq ifr;
-
size_t if_name_len=strlen(if_name);
-
if (if_name_len<sizeof(ifr.ifr_name)) {
-
memcpy(ifr.ifr_name,if_name,if_name_len);
-
ifr.ifr_name[if_name_len]=0;
-
} else {
-
die("interface name is too long");
-
}
Provide an open socket descriptor with the address family AF_INET
All ioctl calls need a file descriptor to act on. In the case of SIOCGIFADDR this must refer to a socket (as opposed to, for example, a regular file) and must be of the address family that you wish to obtain (AF_INET in this instance). Otherwise any type of socket would suffice, but it should preferably not be one that requires any obscure kernel modules to be loaded. For this example a UDP socket will be used:
-
int fd=socket(AF_INET,SOCK_DGRAM,0);
-
if (fd==-1) {
-
die("%s",strerror(errno));
-
}
Invoke ioctl
Once you have the ifreq structure and socket descriptor then you are ready to invoke ioctl:
-
if (ioctl(fd,SIOCGIFADDR,&ifr)==-1) {
-
int temp_errno=errno;
-
close(fd);
-
die("%s",strerror(temp_errno));
-
}
-
close(fd)
If this completes without error then the hardware address of the interface should have been returned in ifr.ifr_addr in the form of a struct sockaddr_in.
Extract the IP address from the ifreq structure
If an address was returned at all then it ought to be an IPv4 address, because that was the address family of the socket. To obtain the numerical value of the address you should:
Cast the returned address to a struct sockaddr_in.
Extract the sin_addr field of this structure to obtain a struct in_addr.
Extract the s_addr field of the in_addr structure to obtain an in_addr_t (equivalent to a uint32_t).
Finally, convert the s_addr field (which is in network byte order) into whatever representation you require.
-
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr.ifr_addr;
-
printf("IP address: %s\n",inet_ntoa(ipaddr->sin_addr))
连在一起就是下面整个代码:
注: err_sys是APUE里面的error.c提供的函数。
-
#include <stdio.h>
-
#include <sys/ioctl.h>
-
#include <arpa/inet.h>
-
#include <net/if.h>
-
#include <netinet/in.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <limits.h>
-
#include <errno.h>
-
#include <error.c>
-
-
int main(){
-
struct ifreq ifr;
-
char * if_name= "eth0";
-
size_t if_name_len=strlen(if_name);
-
if(if_name_len < sizeof(ifr.ifr_name)){
-
memcpy(ifr.ifr_name, if_name, if_name_len);
-
ifr.ifr_name[if_name_len]=0;
-
}else{
-
err_sys("interface name is too long");
-
}
-
int fd=socket(AF_INET, SOCK_DGRAM,0);
-
if (fd==-1){
-
err_sys("socket error");
-
}
-
-
if(ioctl(fd, SIOCGIFADDR, &ifr)==-1){
-
close(fd);
-
err_sys("ioctl error");
-
}
-
close(fd);
-
-
struct sockaddr_in* ipaddr=(struct sockaddr_in*)&ifr.ifr_addr;
-
printf("IP address: %s\n",inet_ntoa(ipaddr->sin_addr))
当然我后来发现有很多更简单的代码:
如这个:
还有
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <limits.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;
-
char* ifname="wlan0";
-
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, ifname, 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;
-
}
后面是python 3的代码
-
import socket
-
import fcntl
-
import struct
-
-
def get_ip_address(ifname):
-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-
return socket.inet_ntoa(fcntl.ioctl(
-
s.fileno(),0x8915, # SIOCGIFADDR
-
struct.pack('256s', ifname.encode('utf-8')[:15])
-
#struct.pack('256s', ifname.encode('ascii')[:15])
-
)[20:24])
-
-
print(get_ip_address('lo'))
-
print(get_ip_address('eth0'))
-
print(get_ip_address('wlan0'))
-
-
def get_mac_address(ifname):
-
s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-
info=fcntl.ioctl(s.fileno(),0x8927, struct.pack('256s',ifname.encode('ascii')[:15]))
-
mac_address=''.join(['%02x:' % char for char in info[18:24]])
-
return mac_address
-
-
-
print(get_mac_address('lo'))
-
print(get_mac_address('eth0'))
-
print(get_mac_address('wlan0'))
阅读(1797) | 评论(0) | 转发(0) |