• 网络ioctl实践3:设置网卡的mac、ip、子网掩码、广播地址


    前言

            网络ioctl实践1:获取网卡的MAC和IP

            网络ioctl实践2:获取网卡的广播地址和子网掩码

    本文使用的命令是:

    Request说明数据类型对应的union成员
    SIOCSIFADDR设置接口地址struct ifreqifru_addr.sa_data[2-5]
    SIOCSIFNETMASK设置子网掩码struct ifreqifru_netmask.sa_data[2-5]
    SIOCSIFBRDADDR设置广播地址struct ifreqifru_broadaddr.sa_data[2-5]
    SIOCSIFHWADDR设置mac地址struct ifreqifru_hwaddr.sa_data[0-5]

    struct ifreq结构体定义:

    1. #define IFNAMSIZ 16
    2. struct ifreq {
    3. #define IFHWADDRLEN 6
    4. union
    5. {
    6. char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
    7. } ifr_ifrn;
    8. union {
    9. struct sockaddr ifru_addr;
    10. struct sockaddr ifru_dstaddr;
    11. struct sockaddr ifru_broadaddr;
    12. struct sockaddr ifru_netmask;
    13. struct sockaddr ifru_hwaddr;
    14. short ifru_flags;
    15. int ifru_ivalue;
    16. int ifru_mtu;
    17. struct ifmap ifru_map;
    18. char ifru_slave[IFNAMSIZ]; /* Just fits the size */
    19. char ifru_newname[IFNAMSIZ];
    20. void __user * ifru_data;
    21. struct if_settings ifru_settings;
    22. } ifr_ifru;
    23. }

     一 设置MAC地址:SIOCSIFHWADDR命令

            设置mac地址,首先要构造一个struct ifreq类型。例如 ,初始化网络接口的名字,例如ens33或者eth0,还有设置新的mac地址值。通过ifru_hwaddr成员设置,如下所示:

    1. int i = 0;
    2. uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x15};
    3. struct ifreq ifr;
    4. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    5. for(i = 0;i < 6;i++){
    6. ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
    7. }

            这里介绍一系列宏:如下所示

    1. #define ifr_name ifr_ifrn.ifrn_name /* interface name */
    2. #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
    3. #define ifr_addr ifr_ifru.ifru_addr /* address */
    4. #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
    5. #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
    6. #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
    7. #define ifr_flags ifr_ifru.ifru_flags /* flags */
    8. #define ifr_metric ifr_ifru.ifru_ivalue /* metric */
    9. #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
    10. #define ifr_map ifr_ifru.ifru_map /* device map */
    11. #define ifr_slave ifr_ifru.ifru_slave /* slave device */
    12. #define ifr_data ifr_ifru.ifru_data /* for use by interface */
    13. #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
    14. #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
    15. #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
    16. #define ifr_newname ifr_ifru.ifru_newname /* New name */
    17. #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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. int get_socket(void)
    9. {
    10. int fd = socket(AF_INET,SOCK_DGRAM,0);
    11. if(fd < 0){
    12. perror("socket");
    13. exit(-1);
    14. }
    15. return fd;
    16. }
    17. int set_mac_by_name(int fd,char *name)
    18. {
    19. struct ifreq ifr;
    20. int i = 0;
    21. uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x16};
    22. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    23. for(i = 0;i < 6;i++){
    24. ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
    25. }
    26. if(i = ioctl(fd,SIOCSIFHWADDR,&ifr)){
    27. perror("ioctl");
    28. close(fd);
    29. exit(-1);
    30. }
    31. }
    32. int main(int argc,char *argv)
    33. {
    34. int fd = 0;
    35. fd = get_socket();
    36. set_mac_by_name(fd,"ens33");
    37. return 0;
    38. }

    编译gcc ioctl.c -o app,运行./app,结果如下所示:

    1. root@ubuntu:/home/csdn# ./app
    2. ioctl: Invalid argument
    3. root@ubuntu:/home/csdn#

    报错了,后上网查询得知,需要设置sa_family成员为ARPHRD_ETHER,并添加宏ARPHRD_ETHER的头文件:

    1. #include 
    2. ... ...
    3. ifr.ifr_ifru.ifru_addr.sa_family = ARPHRD_ETHER;
    4. ... ...

    修改函数set_mac_by_name:

    1. #include 
    2. int set_mac_by_name(int fd,char *name)
    3. {
    4. struct ifreq ifr;
    5. int i = 0;
    6. uint8_t mac[6] = {0x00,0x0c,0x29,0x8c,0x05,0x16};
    7. ifr.ifr_ifru.ifru_addr.sa_family = ARPHRD_ETHER;
    8. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    9. for(i = 0;i < 6;i++){
    10. ifr.ifr_ifru.ifru_hwaddr.sa_data[i] = mac[i];
    11. }
    12. if(i = ioctl(fd,SIOCSIFHWADDR,&ifr)){
    13. perror("ioctl");
    14. close(fd);
    15. exit(-1);
    16. }
    17. }

            编译gcc ioctl.c -o app,运行./app,结果如下所示:设置之前,mac地址最后一个数是16,设置完毕后,最后一个数变成了15。

    1. root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/p'
    2. ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:16
    3. root@ubuntu:/home/csdn# ./app
    4. root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/p'
    5. ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
    6. root@ubuntu:/home/csdn#

    二  设置IP地址:SIOCSIFADDR命令

            根据设置mac地址的经验,首先设置名字ens33,然后ifru_addr.sa_data[2-5]四个值,设置新的IP地址是192.168.0.234.还要设置ifru_addr.sa_family = AF_INET,否则设置会失败。

    1. int i = 0;
    2. uint8_t ip[6] = {192,168,0,234};
    3. struct ifreq ifr;
    4. ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
    5. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    6. for(i = 0;i < 4;i++){
    7. ifr.ifr_ifru.ifru_addr.sa_data[2 + i] = ip[i];
    8. }

    实例代码:ioctl.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. int get_socket(void)
    9. {
    10. int fd = socket(AF_INET,SOCK_DGRAM,0);
    11. if(fd < 0){
    12. perror("socket");
    13. exit(-1);
    14. }
    15. return fd;
    16. }
    17. int set_ip_by_name(int fd,char *name)
    18. {
    19. struct ifreq ifr;
    20. int i = 0;
    21. uint8_t ip[6] = {192,168,0,234};
    22. ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
    23. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    24. for(i = 0;i < 4;i++){
    25. ifr.ifr_ifru.ifru_addr.sa_data[2 + i] = ip[i];
    26. }
    27. if(i = ioctl(fd,SIOCSIFADDR,&ifr)){
    28. perror("ioctl");
    29. close(fd);
    30. exit(-1);
    31. }
    32. }
    33. int main(int argc,char *argv)
    34. {
    35. int fd = 0;
    36. fd = get_socket();
    37. set_ip_by_name(fd,"ens33");
    38. return 0;
    39. }

            编译gcc ioctl.c -o app,运行./app,结果如下图所示:设置之前,ip地址是192.168.0.11,设置完毕后,IP地址变成了192.168.0.234。

     三 设置子网掩码和广播地址

            原理和设置ip地址一样,不同的是命令改为SIOCSIFNETMASK和SIOCSIFBRDADDR ,成员赋值改为ifru_netmask和ifru_broadaddr。

    测试代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int get_socket(void)
    10. {
    11. int fd = socket(AF_INET,SOCK_DGRAM,0);
    12. if(fd < 0){
    13. perror("socket");
    14. exit(-1);
    15. }
    16. return fd;
    17. }
    18. int set_netmask_by_name(int fd,char *name)
    19. {
    20. struct ifreq ifr;
    21. int i = 0;
    22. uint8_t ip[6] = {255,255,255,0};
    23. ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
    24. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    25. for(i = 0;i < 4;i++){
    26. ifr.ifr_ifru.ifru_netmask.sa_data[2 + i] = ip[i];
    27. }
    28. if(i = ioctl(fd,SIOCSIFNETMASK,&ifr)){
    29. perror("ioctl");
    30. close(fd);
    31. exit(-1);
    32. }
    33. return 0;
    34. }
    35. int set_broadcast_by_name(int fd,char *name)
    36. {
    37. struct ifreq ifr;
    38. int i = 0;
    39. uint8_t ip[6] = {192,168,0,255};
    40. ifr.ifr_ifru.ifru_addr.sa_family = AF_INET;
    41. snprintf(ifr.ifr_ifrn.ifrn_name,sizeof(ifr.ifr_ifrn.ifrn_name),"%s",name);
    42. for(i = 0;i < 4;i++){
    43. ifr.ifr_ifru.ifru_broadaddr.sa_data[2 + i] = ip[i];
    44. }
    45. if(i = ioctl(fd,SIOCSIFBRDADDR,&ifr)){
    46. perror("ioctl");
    47. close(fd);
    48. exit(-1);
    49. }
    50. return 0;
    51. }
    52. int main(int argc,char *argv)
    53. {
    54. int fd = 0;
    55. fd = get_socket();
    56. set_netmask_by_name(fd,"ens33");
    57. set_broadcast_by_name(fd,"ens33");
    58. return 0;
    59. }

    编译gcc ioctl.c -o app,运行./app,结果如下图所示:设置之前,广播地址是255.255.255.255,设置完毕后,广播地址变成了192.168.0.255。子网掩码设置前后分别是255.255.0.0和255.255.255.0。测试很成功

    1. root@ubuntu:/home/csdn# gcc ioctl.c -o app
    2. root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/,+1p'
    3. ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
    4. inet 地址:192.168.0.11 广播:255.255.255.255 掩码:255.255.0.0
    5. root@ubuntu:/home/csdn# ./app
    6. root@ubuntu:/home/csdn# ifconfig | sed -n '/ens33/,+1p'
    7. ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
    8. inet 地址:192.168.0.11 广播:192.168.0.255 掩码:255.255.255.0
    9. root@ubuntu:/home/csdn#

    小结       

  • 相关阅读:
    LVGL学习笔记
    供应商管理
    聚醚羰基铑功能化离子液体{[CH3O(CH2CH2O)nmim][Rhx(CO)y]}
    Android 如何添加自定义字体
    uniapp一键生成iOS通用链接
    不同类型的球幕影院对观影体验有何影响?
    面试字节,过关斩将直接干到 3 面,结果被吊打了?
    第十六章总结:反射和注解
    翻译一组文本 API 返回值说明
    [车联网安全自学篇] 五十六. Android安全之APK应用程序是否签名异常的检测方法
  • 原文地址:https://blog.csdn.net/yueni_zhao/article/details/127818787