本文使用的命令是:
| Request | 说明 | 数据类型 | 对应的union成员 |
| SIOCSIFADDR | 设置接口地址 | struct ifreq | ifru_addr.sa_data[2-5] |
| SIOCSIFNETMASK | 设置子网掩码 | struct ifreq | ifru_netmask.sa_data[2-5] |
| SIOCSIFBRDADDR | 设置广播地址 | struct ifreq | ifru_broadaddr.sa_data[2-5] |
| SIOCSIFHWADDR | 设置mac地址 | struct ifreq | ifru_hwaddr.sa_data[0-5] |
struct ifreq结构体定义:
- #define IFNAMSIZ 16
- struct ifreq {
- #define IFHWADDRLEN 6
- union
- {
- char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
- } ifr_ifrn;
-
- union {
- struct sockaddr ifru_addr;
- struct sockaddr ifru_dstaddr;
- struct sockaddr ifru_broadaddr;
- struct sockaddr ifru_netmask;
- struct sockaddr ifru_hwaddr;
- short ifru_flags;
- int ifru_ivalue;
- int ifru_mtu;
- struct ifmap ifru_map;
- char ifru_slave[IFNAMSIZ]; /* Just fits the size */
- char ifru_newname[IFNAMSIZ];
- void __user * ifru_data;
- struct if_settings ifru_settings;
- } ifr_ifru;
- }
设置mac地址,首先要构造一个struct ifreq类型。例如 ,初始化网络接口的名字,例如ens33或者eth0,还有设置新的mac地址值。通过ifru_hwaddr成员设置,如下所示:
- int i = 0;
- uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x15};
- struct ifreq ifr;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 6;i++){
- ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
- }
这里介绍一系列宏:如下所示
- #define ifr_name ifr_ifrn.ifrn_name /* interface name */
- #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
- #define ifr_addr ifr_ifru.ifru_addr /* address */
- #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
- #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
- #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
- #define ifr_flags ifr_ifru.ifru_flags /* flags */
- #define ifr_metric ifr_ifru.ifru_ivalue /* metric */
- #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
- #define ifr_map ifr_ifru.ifru_map /* device map */
- #define ifr_slave ifr_ifru.ifru_slave /* slave device */
- #define ifr_data ifr_ifru.ifru_data /* for use by interface */
- #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
- #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
- #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
- #define ifr_newname ifr_ifru.ifru_newname /* New name */
- #define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/
看到这里有没有解决你的一个困惑?我们在书本上看到的是ifr_hwaddr 而不是ifr_ifru.ifru_hwaddr,实际上ifr_hwaddr只是一个宏,厉害吧。
然后就是通过ioctl函数设置该新的MAC地址:
ioctl(fd,SIOCSIFHWADDR,&ifr);
完整实例:ioctl.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int get_socket(void)
- {
- int fd = socket(AF_INET,SOCK_DGRAM,0);
- if(fd < 0){
- perror("socket");
- exit(-1);
- }
- return fd;
- }
-
- int set_mac_by_name(int fd,char *name)
- {
- struct ifreq ifr;
- int i = 0;
- uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x16};
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 6;i++){
- ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
- }
- if(i = ioctl(fd,SIOCSIFHWADDR,&ifr)){
- perror("ioctl");
- close(fd);
- exit(-1);
- }
- }
-
- int main(int argc,char *argv)
- {
- int fd = 0;
- fd = get_socket();
- set_mac_by_name(fd,"ens33");
- return 0;
- }
编译gcc ioctl.c -o app,运行./app,结果如下所示:
- root@ubuntu:/home/csdn# ./app
- ioctl: Invalid argument
- root@ubuntu:/home/csdn#
报错了,后上网查询得知,需要设置sa_family成员为ARPHRD_ETHER,并添加宏ARPHRD_ETHER的头文件:
- #include
- ... ...
- ifr.ifr_ifru.ifru_addr.sa_family = ARPHRD_ETHER;
- ... ...
修改函数set_mac_by_name:
- #include
- int set_mac_by_name(int fd,char *name)
- {
- struct ifreq ifr;
- int i = 0;
- uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x16};
- ifr.ifr_ifru.ifru_addr.sa_family = ARPHRD_ETHER;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 6;i++){
- ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
- }
- if(i = ioctl(fd,SIOCSIFHWADDR,&ifr)){
- perror("ioctl");
- close(fd);
- exit(-1);
- }
- }
编译gcc ioctl.c -o app,运行./app,结果如下所示:设置之前,mac地址最后一个数是16,设置完毕后,最后一个数变成了15。
- root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/p'
- ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:16
- root@ubuntu:/home/csdn# ./app
- root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/p'
- ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
- root@ubuntu:/home/csdn#
根据设置mac地址的经验,首先设置名字ens33,然后ifru_addr.sa_data[2-5]四个值,设置新的IP地址是192.168.0.234.还要设置ifru_addr.sa_family = AF_INET,否则设置会失败。
- int i = 0;
- uint8_t ip[6] = {192,168,0,234};
- struct ifreq ifr;
- ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 4;i++){
- ifr.ifr_ifru.ifru_addr.sa_data[2 + i] = ip[i];
- }
实例代码:ioctl.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int get_socket(void)
- {
- int fd = socket(AF_INET,SOCK_DGRAM,0);
- if(fd < 0){
- perror("socket");
- exit(-1);
- }
- return fd;
- }
-
- int set_ip_by_name(int fd,char *name)
- {
- struct ifreq ifr;
- int i = 0;
- uint8_t ip[6] = {192,168,0,234};
- ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 4;i++){
- ifr.ifr_ifru.ifru_addr.sa_data[2 + i] = ip[i];
- }
- if(i = ioctl(fd,SIOCSIFADDR,&ifr)){
- perror("ioctl");
- close(fd);
- exit(-1);
- }
- }
- int main(int argc,char *argv)
- {
- int fd = 0;
- fd = get_socket();
- set_ip_by_name(fd,"ens33");
- return 0;
- }
编译gcc ioctl.c -o app,运行./app,结果如下图所示:设置之前,ip地址是192.168.0.11,设置完毕后,IP地址变成了192.168.0.234。

原理和设置ip地址一样,不同的是命令改为SIOCSIFNETMASK和SIOCSIFBRDADDR ,成员赋值改为ifru_netmask和ifru_broadaddr。
测试代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int get_socket(void)
- {
- int fd = socket(AF_INET,SOCK_DGRAM,0);
- if(fd < 0){
- perror("socket");
- exit(-1);
- }
- return fd;
- }
-
- int set_netmask_by_name(int fd,char *name)
- {
- struct ifreq ifr;
- int i = 0;
- uint8_t ip[6] = {255,255,255,0};
- ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 4;i++){
- ifr.ifr_ifru.ifru_netmask.sa_data[2 + i] = ip[i];
- }
- if(i = ioctl(fd,SIOCSIFNETMASK,&ifr)){
- perror("ioctl");
- close(fd);
- exit(-1);
- }
- return 0;
- }
- int set_broadcast_by_name(int fd,char *name)
- {
- struct ifreq ifr;
- int i = 0;
- uint8_t ip[6] = {192,168,0,255};
- ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
- snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
- for(i = 0;i < 4;i++){
- ifr.ifr_ifru.ifru_broadaddr.sa_data[2 + i] = ip[i];
- }
- if(i = ioctl(fd,SIOCSIFBRDADDR,&ifr)){
- perror("ioctl");
- close(fd);
- exit(-1);
- }
- return 0;
- }
- int main(int argc,char *argv)
- {
- int fd = 0;
- fd = get_socket();
- set_netmask_by_name(fd,"ens33");
- set_broadcast_by_name(fd,"ens33");
- return 0;
- }
-
编译gcc ioctl.c -o app,运行./app,结果如下图所示:设置之前,广播地址是255.255.255.255,设置完毕后,广播地址变成了192.168.0.255。子网掩码设置前后分别是255.255.0.0和255.255.255.0。测试很成功
- root@ubuntu:/home/csdn# gcc ioctl.c -o app
- root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/,+1p'
- ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
- inet 地址:192.168.0.11 广播:255.255.255.255 掩码:255.255.0.0
- root@ubuntu:/home/csdn# ./app
- root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/,+1p'
- ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
- inet 地址:192.168.0.11 广播:192.168.0.255 掩码:255.255.255.0
- root@ubuntu:/home/csdn#