linux socket ifreq 设置IP地址


原文链接: linux socket ifreq 设置IP地址

Linux网络接口操作之ioctl-1_接口参数 - 反刍动物 - SegmentFault 思否

strace ./ifconfig

socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
ioctl(5, SIOCSIFADDR, {ifr_name="eth0:8", ifr_addr={AF_INET, inet_addr("192.168.1.202")}}) = 0
ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
ioctl(5, SIOCSIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
close(5)                                = 0
Complete source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>             /* offsetof */
#include <net/if.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_ether.h>
#endif

#define IFNAMSIZ    16
#define IFNAME "eth0:2"
#define HOST "192.168.1.204"
#define ifreq_offsetof(x)  offsetof(struct ifreq, x)

int main(int argc, char **argv) {

        struct ifreq ifr;
        struct sockaddr_in sai;
        int sockfd;                     /* socket fd we use to manipulate stuff with */
        int selector;
        unsigned char mask;

        char *p;


        /* Create a channel to the NET kernel. */
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);

        /* get interface name */
        strncpy(ifr.ifr_name, IFNAME, IFNAMSIZ);

        memset(&sai, 0, sizeof(struct sockaddr));
        sai.sin_family = AF_INET;
        sai.sin_addr.s_addr = inet_addr(HOST);
        sai.sin_port = 0;

        p = (char *) &sai;
        memcpy( (((char *)&ifr + ifreq_offsetof(ifr_addr) )),
                        p, sizeof(struct sockaddr));

        ioctl(sockfd, SIOCSIFADDR, &ifr);


        // set the interface to up
        ioctl(sockfd, SIOCGIFFLAGS, &ifr);

        ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
        // ifr.ifr_flags &= ~selector;  // unset something

        ioctl(sockfd, SIOCSIFFLAGS, &ifr);
        close(sockfd);
        return 0;
}



Perhaps you forgot to set the interface to up?

ioctl(sockfd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
ioctl(sockfd, SIOCSIFFLAGS, &ifr);

Linux提供了一系列网络接口操作相关的命令集,其中,一些传统的工具,如net-tools软件包中的ifconfig(8),arp(8),route(8)等都是通过ioctl(2)系统调用实现
本篇介绍使用ioctl(2)进行网络接口参数的获取与设置

函数原型
#include
int ioctl(int d, int request, ...);
第一个参数fd指定一个由open(2)/socket(2)创建的文件描述符
第二个参数request指定操作的类型,即对该文件描述符执行何种操作
第三个参数为一块内存区域,通常依赖于request指定的操作类型

接口参数与操作的相关定义
内核版本:2.6.32.5

ioctl(2)使用struct ifreq与/或struct ifconf结构执行网络接口相关的操作,这两个结构的地址作为ioctl(2)的第三个参数

/* include/linux/if.h */

#define IFNAMSIZ 16
#define IFALIASZ 256

struct ifreq
{
#define IFHWADDRLEN 6

union
{
    char    ifrn_name[IFNAMSIZ];
} 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];
    char    ifru_newname[IFNAMSIZ];
    void __user *    ifru_data;
    struct    if_settings ifru_settings;
} ifr_ifru;

};

#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*/

struct ifconf
{

int    ifc_len;
union 
{
    char __user *ifcu_buf;
    struct ifreq __user *ifcu_req;
} ifc_ifcu;

};

#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address /
#define ifc_req ifc_ifcu.ifcu_req /
array of structures */
操作类型,ioctl(2)的第二个参数

/* include/linux/sockios.h */

/* Socket configuration controls. /
#define SIOCGIFNAME 0x8910 /
get iface name /
#define SIOCSIFLINK 0x8911 /
set iface channel /
#define SIOCGIFCONF 0x8912 /
get iface list /
#define SIOCGIFFLAGS 0x8913 /
get flags /
#define SIOCSIFFLAGS 0x8914 /
set flags /
#define SIOCGIFADDR 0x8915 /
get PA address /
#define SIOCSIFADDR 0x8916 /
set PA address /
#define SIOCGIFDSTADDR 0x8917 /
get remote PA address /
#define SIOCSIFDSTADDR 0x8918 /
set remote PA address /
#define SIOCGIFBRDADDR 0x8919 /
get broadcast PA address /
#define SIOCSIFBRDADDR 0x891a /
set broadcast PA address /
#define SIOCGIFNETMASK 0x891b /
get network PA mask /
#define SIOCSIFNETMASK 0x891c /
set network PA mask /
#define SIOCGIFMETRIC 0x891d /
get metric /
#define SIOCSIFMETRIC 0x891e /
set metric /
#define SIOCGIFMEM 0x891f /
get memory address (BSD) /
#define SIOCSIFMEM 0x8920 /
set memory address (BSD) /
#define SIOCGIFMTU 0x8921 /
get MTU size /
#define SIOCSIFMTU 0x8922 /
set MTU size /
#define SIOCSIFNAME 0x8923 /
set interface name /
#define SIOCSIFHWADDR 0x8924 /
set hardware address /
#define SIOCGIFENCAP 0x8925 /
get/set encapsulations /
#define SIOCSIFENCAP 0x8926
#define SIOCGIFHWADDR 0x8927 /
Get hardware address /
#define SIOCGIFSLAVE 0x8929 /
Driver slaving support /
#define SIOCSIFSLAVE 0x8930
#define SIOCADDMULTI 0x8931 /
Multicast address lists /
#define SIOCDELMULTI 0x8932
#define SIOCGIFINDEX 0x8933 /
name -> if_index mapping /
#define SIOGIFINDEX SIOCGIFINDEX /
misprint compatibility :-) /
#define SIOCSIFPFLAGS 0x8934 /
set/get extended flags set /
#define SIOCGIFPFLAGS 0x8935
#define SIOCDIFADDR 0x8936 /
delete PA address /
#define SIOCSIFHWBROADCAST 0x8937 /
set hardware broadcast addr /
#define SIOCGIFCOUNT 0x8938 /
get number of devices /
...
#define SIOCETHTOOL 0x8946 /
Ethtool interface */
结构字段与操作类型的含义在大都在注释中已注明

一般步骤
通过ioctl(2)执行网络接口参数的获取/设置的一般步骤为:

通过socket(2)创建IP套接字;由于ioctl(2)此时是与内核通信,因此对套接字的通信域与类型没有强制要求,通信域可以为AF_INET/AF_LOCAL,类型可以为SOCK_DGRAM/SOCK_STREAM/SOCK_RAW等

初始化struct ifconf与/或struct ifreq结构

对套接字描述符调用ioctl(2),执行相应类型的SIO操作

获取返回至truct ifconf与/或struct ifreq结构中的相关信息

示例程序
本地网络接口信息:eth0网线已连接且已配置IPv4地址,eth1网线未连接且未配置IPv4地址

ip l

1: lo: mtu 65536 qdisc noqueue state UNKNOWN

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000

link/ether 00:0c:29:ed:9d:28 brd ff:ff:ff:ff:ff:ff

3: eth1: mtu 1500 qdisc pfifo_fast state DOWN qlen 1000

link/ether 00:0c:29:ed:9d:32 brd ff:ff:ff:ff:ff:ff

ip a

1: lo: mtu 65536 qdisc noqueue state UNKNOWN

link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host 
   valid_lft forever preferred_lft forever

2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000

link/ether 00:0c:29:ed:9d:28 brd ff:ff:ff:ff:ff:ff
inet 192.168.56.139/24 brd 192.168.56.255 scope global eth0
inet6 fe80::20c:29ff:feed:9d28/64 scope link 
   valid_lft forever preferred_lft forever

3: eth1: mtu 1500 qdisc pfifo_fast state DOWN qlen 1000

link/ether 00:0c:29:ed:9d:32 brd ff:ff:ff:ff:ff:ff

1 . 通过SIOCGIFCONF操作获取系统中所有的网络接口

/* list_network_interfaces_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include

#define BUFSIZE 1024

int main(int argc, char *argv[])
{

int sfd, if_count, i;
struct ifconf ifc;
struct ifreq ifr[10];
char ipaddr[INET_ADDRSTRLEN] = {'\0'};

memset(&ifc, 0, sizeof(struct ifconf));

sfd = socket(AF_INET, SOCK_DGRAM, 0);

ifc.ifc_len = 10 * sizeof(struct ifreq);
ifc.ifc_buf = (char *)ifr;

/* SIOCGIFCONF is IP specific. see netdevice(7) */
ioctl(sfd, SIOCGIFCONF, (char *)&ifc);

if_count = ifc.ifc_len / (sizeof(struct ifreq));
for (i = 0; i < if_count; i++) {
    printf("Interface %s : ", ifr[i].ifr_name);    
    inet_ntop(AF_INET, 
    &(((struct sockaddr_in *)&(ifr[i].ifr_addr))->sin_addr),
    ipaddr, INET_ADDRSTRLEN);
    printf("%s\n", ipaddr);
}

close(sfd);

exit(EXIT_SUCCESS);

}
编译并运行

gcc list_network_interfaces_ioctl.c -g -o list_network_interfaces_ioctl

./list_network_interfaces_ioctl

Interface lo : 127.0.0.1
Interface eth0 : 192.168.56.139
SIOCGIFCONF操作需要同时使用struct ifconf与struct ifreq
初始化struct ifconf时,ifc_ifcu指定一个缓冲区的首地址,缓冲区中保存若干个连续的struct ifreq,ifc_len指定该缓冲区的长度;ioctl(2)返回后,内核将为每一个配置了IPv4地址的本地网络接口分配一个struct ifreq并保存到初始化的缓冲区中,struct ifreq中保存单个网络接口的名称及IPv4地址,struct ifconf的ifc_len被更新为初始化缓冲区中实际返回的数据长度

2 . 通过SIOCGIFADDR操作获取指定网络接口的IPv4地址

/* get_interface_ip_address_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

static char *get_ipaddr(const char *);

int main(int argc, char *argv[])
{

if (argc != 2) {
    fprintf(stderr, "Usage: %s [network interface name]\n", argv[0]);
    exit(EXIT_FAILURE);
}

char ifname[IFNAMSIZ] = {'\0'};
strncpy(ifname, argv[1], IFNAMSIZ-1);

char *ip = get_ipaddr(ifname);

printf("Interface %s : %s\n", ifname, ip);

return 0;

}

static char *get_ipaddr(const char *dev)
{

int sfd, saved_errno, ret;
struct ifreq ifr;
char *ipaddr;

ipaddr = (char *)malloc(INET_ADDRSTRLEN);
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

sfd = socket(AF_INET, SOCK_DGRAM, 0);

errno = saved_errno;
ret = ioctl(sfd, SIOCGIFADDR, &ifr);
if (ret == -1) {
    if (errno == 19) {
        fprintf(stderr, "Interface %s : No such device.\n", dev);
        exit(EXIT_FAILURE);
    }
    if (errno == 99) {
        fprintf(stderr, "Interface %s : No IPv4 address assigned.\n", dev);
        exit(EXIT_FAILURE);
    }
}
saved_errno = errno;

inet_ntop(AF_INET, &(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), ipaddr, INET_ADDRSTRLEN);

close(sfd);
return ipaddr;

}
编译并运行

gcc get_interface_ip_address_ioctl.c -g -o get_interface_ip_address_ioctl

#

./get_interface_ip_address_ioctl eth0

Interface eth0 : 192.168.56.139
#

./get_interface_ip_address_ioctl eth1

Interface eth1 : No IPv4 address assigned.
#

./get_interface_ip_address_ioctl eth2

Interface eth2 : No such device.
SIOCGIFADDR操作使用struct ifreq中的ifr_ifru.ifru_addr字段;ifr_ifrn.ifrn_name指定为网络接口名称并调用ioctl(SIOCGIFADDR),返回后将ifr_ifru.ifru_addr转换为IPv4套接字地址结构,IPv4地址保存在该结构中的sin_addr字段中

SIOCGIFCONF与SIOCGIFADDR属于IPv4特定的操作,对于未配置IPv4地址的网络接口,ioctl(SIOCGIFCONF)返回时不会分配struct ifreq结构,因而不会返回该接口的名称,而ioctl(SIOCGIFADDR)将以errno值99(Cannot assign requested address)而调用失败
若指定了系统中不存在的网络接口,则errno的值为19(No such device)

SIOCGIFCONF与SIOCGIFADDR无法获取网络接口的IPv6地址,ioctl的内核源码中通过读取/proc/net/if_inet6获取

3 . 通过SIOCGIFHWADDR操作获取指定网络接口的mac地址

/* get_interface_mac_address_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include

static unsigned char *get_if_mac(const char *);

int main(int argc, char *argv[])
{

if (argc != 2) {
    fprintf(stderr, "Usage: %s [network interface name]\n", argv[0]);
    exit(EXIT_FAILURE);
}

char ifname[IFNAMSIZ] = {'\0'};
strncpy(ifname, argv[1], IFNAMSIZ-1);

unsigned char *mac = get_if_mac(ifname);

printf("Interface %s : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
ifname, *mac, *(mac+1), *(mac+2), *(mac+3), *(mac+4), *(mac+5));

return 0;

}

static unsigned char *get_if_mac(const char *dev)
{

int sfd, ret, saved_errno, i;
unsigned char *mac_addr;
struct ifreq ifr;

mac_addr = (unsigned char *)malloc(ETH_ALEN);

sfd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

saved_errno = errno;
ret = ioctl(sfd, SIOCGIFHWADDR, &ifr);
if (ret == -1 && errno == 19) {
    fprintf(stderr, "Interface %s : No such device.\n", dev);
    exit(EXIT_FAILURE);
}
errno = saved_errno;

if (ifr.ifr_addr.sa_family == ARPHRD_LOOPBACK) {
    printf("Interface %s : A Loopback device.\n", dev);
    printf("MAC address is always 00:00:00:00:00:00\n");
    exit(EXIT_SUCCESS);
}

if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
    fprintf(stderr, "Interface %s : Not an Ethernet device.\n", dev);
    exit(EXIT_FAILURE);
}

memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

return (unsigned char *)mac_addr;

}
编译并运行

gcc get_interface_mac_address_ioctl.c -g -o get_interface_mac_address_ioctl

#

./get_interface_mac_address_ioctl lo

Interface lo : A Loopback device.
MAC address is always 00:00:00:00:00:00
#

./get_interface_mac_address_ioctl eth0

Interface eth0 : 00:0c:29:ed:9d:28
#

./get_interface_mac_address_ioctl eth1

Interface eth1 : 00:0c:29:ed:9d:32
#

./get_interface_mac_address_ioctl eth2

Interface eth2 : No such device.
SIOCGIFHWADDR操作使用struct ifreq中的ifr_ifru.ifru_hwaddr字段,在ifr_ifrn.ifrn_name中填充指定的网络接口名称后,该接口的mac地址按顺序返回到ifr_ifru.ifru_hwaddr.sa_data数组的前6个字节中

4 . 通过SIOCGIFFLAGS操作获取指定网络接口的标志

/* get_interface_flags_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

static short get_if_flags(int, char *);

int main(int argc, char *argv[])
{

if (argc != 2) {
    fprintf(stderr, "Usage: %s [network interface name]\n", argv[0]);
    exit(EXIT_FAILURE);
}

int sfd;
short flags;
char ifname[IFNAMSIZ] = {'\0'};
strncpy(ifname, argv[1], IFNAMSIZ-1);

sfd = socket(AF_INET, SOCK_DGRAM, 0);
flags = get_if_flags(sfd, ifname);

printf("Interface %s : ", ifname);
if (flags & IFF_UP)
    printf("UP ");

if (flags & IFF_RUNNING)
    printf("RUNNING ");

if (flags & IFF_LOOPBACK)
    printf("LOOPBACK ");

if (flags & IFF_BROADCAST)
    printf("BROADCAST ");

if (flags & IFF_MULTICAST)
    printf("MULTICAST ");

if (flags & IFF_PROMISC)
    printf("PROMISC");

#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000

if (flags & IFF_LOWER_UP)
    printf("LOWER_UP");

#endif

printf("\n");

close(sfd);
exit(EXIT_SUCCESS);

}

static short get_if_flags(int s, char *dev)
{

int saved_errno, ret;
short if_flags;
struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

saved_errno = errno;
ret = ioctl(s, SIOCGIFFLAGS, &ifr);
if (ret == -1 && errno == 19) {
    fprintf(stderr, "Interface %s : No such device.\n", dev);
    exit(EXIT_FAILURE);
}
errno = saved_errno;
if_flags = ifr.ifr_flags;

return if_flags;

}
编译并运行

gcc get_interface_flags_ioctl.c -g -o get_interface_flags_ioctl

#

./get_interface_flags_ioctl lo

Interface lo : UP RUNNING LOOPBACK
#

./get_interface_flags_ioctl eth0

Interface eth0 : UP RUNNING BROADCAST MULTICAST
#

./get_interface_flags_ioctl eth1

Interface eth1 : UP BROADCAST MULTICAST
#

./get_interface_flags_ioctl eth2

Interface eth2 : No such device.
ifr_ifrn.ifrn_name指定为网络接口名称后,ioctl(SIOCGIFFLAGS)调用将标志返回到ifr_ifru.ifru_flags字段

IFF_RUNNING表示该接口已被激活,且可以正常传输数据

IFF_UP表示giant接口已被激活,但可能无法正常传输数据,如网线未连接的情况

IFF_LOWER_UP表示网络的物理连接已就绪,即网线连接正常;由于struct ifreq的ifr_ifru.ifru_flags类型为short,用16进制表示仅为4位,因而无法获取与设置5位16进制的IFF_LOWER_UP标志(0x10000)

5 .通过SIOCSIFADDR操作设置指定网络接口的IPv4地址

/* set_interface_ip_address_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

static void set_ipaddr(const char *, const char *);

int main(int argc, char *argv[])
{

if (argc != 3) {
    fprintf(stderr, "Usage: %s [network interface name] [ip address]\n",
    argv[0]);
    exit(EXIT_FAILURE);
}

char ifname[IFNAMSIZ] = {'\0'};
strncpy(ifname, argv[1], IFNAMSIZ-1);
char ipaddr[INET_ADDRSTRLEN] = {'\0'};
strncpy(ipaddr, argv[2], INET_ADDRSTRLEN);

set_ipaddr(ifname, ipaddr);

printf("Interface %s : ip address is set to %s\n", ifname, ipaddr);

return 0;

}

static void set_ipaddr(const char *dev, const char *ip)
{

int sfd, saved_errno, ret;
struct ifreq ifr;
struct sockaddr_in sin;

sfd = socket(AF_INET, SOCK_DGRAM, 0);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
inet_pton(AF_INET, ip, &(sin.sin_addr));

memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));

errno = saved_errno;
ret = ioctl(sfd, SIOCSIFADDR, &ifr);
if (ret == -1) {
    if (errno == 19) {
        fprintf(stderr, "Interface %s : No such device.\n", dev);
        exit(EXIT_FAILURE);
    }
    if (errno == 99) {
        fprintf(stderr, "Interface %s : No IPv4 address assigned.\n", dev);
        exit(EXIT_FAILURE);
    }
}
saved_errno = errno;

close(sfd);

}
编译并运行

gcc set_interface_ip_address_ioctl.c -g -o set_interface_ip_address_ioctl

#

./set_interface_ip_address_ioctl eth1 10.0.0.1

Interface eth1 : ip address is set to 10.0.0.1
#

./get_interface_ip_address_ioctl eth1

Interface eth1 : 10.0.0.1
#

./set_interface_ip_address_ioctl eth1 10.0.0.2

Interface eth1 : ip address is set to 10.0.0.2
#

./get_interface_ip_address_ioctl eth1

Interface eth1 : 10.0.0.2
与ifconfig(8)相同,多次指定同一网络接口名称设置IP地址时,最后的设置将覆盖先前的设置而生效

6 . 通过SIOCGIFFLAGS操作设置指定网络接口的标志
使用ifconfig(8)将eth1设置为混杂模式,并关闭该接口,然后在程序中关闭混杂模式,并开启该接口

ifconfig eth1 promisc

#

ifconfig eth1 down

#

./get_interface_flags_ioctl eth1

Interface eth1 : BROADCAST MULTICAST PROMISC
/* set_interface_flags_ioctl.c */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

static short get_if_flags(int, struct ifreq);
static void set_if_flags(int, struct ifreq
);

int main(int argc, char *argv[])
{

if (argc != 2) {
    fprintf(stderr, "Usage: %s [network interface name]\n", argv[0]);
    exit(EXIT_FAILURE);
}

int sfd;
short flags;
struct ifreq ifr;

char ifname[IFNAMSIZ] = {'\0'};
strncpy(ifname, argv[1], IFNAMSIZ-1);

sfd = socket(AF_INET, SOCK_DGRAM, 0);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
flags = get_if_flags(sfd, &ifr);

ifr.ifr_flags = flags;

/* set IFF_UP if cleared */
if (!(flags & IFF_UP)) {
    ifr.ifr_flags |= IFF_UP;
    set_if_flags(sfd, &ifr);
    printf("Interface %s : UP set.\n", ifname);
}

flags = ifr.ifr_flags;

/* clear IFF_PROMISC if set */
if (flags & IFF_PROMISC) {
    ifr.ifr_flags &= ~IFF_PROMISC;
    set_if_flags(sfd, &ifr);
    printf("Interface %s : PROMISC cleared.\n", ifname);
}

close(sfd);

exit(EXIT_SUCCESS);

}

static short get_if_flags(int s, struct ifreq *ifr)
{

int ret, saved_errno;
short if_flags;

saved_errno = errno;
ret = ioctl(s, SIOCGIFFLAGS, ifr);
if (ret == -1 && errno == 19) {
    fprintf(stderr, "Interface %s : No such device.\n", ifr->ifr_name);
    exit(EXIT_FAILURE);
}
errno = saved_errno;

if_flags = ifr->ifr_flags;
return if_flags;

}

static void set_if_flags(int s, struct ifreq *ifr)
{

int ret, saved_errno;
saved_errno = errno;
ret = ioctl(s, SIOCSIFFLAGS, ifr);
if (ret == -1) {
    fprintf(stderr, "Interface %s : %s\n", ifr->ifr_name, strerror(errno));
    exit(EXIT_FAILURE);
}
errno = saved_errno;

}
编译并运行

gcc set_interface_flags_ioctl.c -g -o set_interface_flags_ioctl

#

./set_interface_flags_ioctl eth1

Interface eth1 : UP set.
Interface eth1 : PROMISC cleared.
#

./get_interface_flags_ioctl eth1

Interface eth1 : UP BROADCAST MULTICAST
7 .通过SIOCSIFNAME操作更改网络接口的名称

/* change_ifname_ioctl.c */
#include
#include
#include
#include
#include
#include
#include
#include

static void change_ifname(char *, char *);
static void shutdown_if_up(char *);

int main(int argc, char *argv[])
{

if (argc != 3) {
    fprintf(stderr, "%s [old ifname] [new ifname]\n", argv[0]);
    exit(EXIT_FAILURE);
}

char old_ifname[IFNAMSIZ] = {'\0'};
strncpy(old_ifname, argv[1], IFNAMSIZ);

char new_ifname[IFNAMSIZ] = {'\0'};
strncpy(new_ifname, argv[2], IFNAMSIZ);

change_ifname(old_ifname, new_ifname);
printf("Interface name %s has been changed to %s\n", old_ifname, new_ifname);

return 0;

}

void change_ifname(char *old_dev, char *new_dev)
{

int sfd, ret, saved_errno;
struct ifreq ifr;

shutdown_if_up(old_dev);

sfd = socket(AF_INET, SOCK_DGRAM, 0);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, old_dev, IFNAMSIZ);
strncpy(ifr.ifr_newname, new_dev, IFNAMSIZ);

saved_errno = errno;
ret = ioctl(sfd, SIOCSIFNAME, &ifr);
if (ret == -1) {
    fprintf(stderr, "Interface %s : %s\n", dev, strerror(errno));
    exit(EXIT_FAILURE);
}
errno = saved_errno;

}

static void shutdown_if_up(char *dev)
{

int sfd, ret, saved_errno;
short flags;
struct ifreq ifr;

sfd = socket(AF_INET, SOCK_DGRAM, 0);

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);

saved_errno = errno;
ret = ioctl(sfd, SIOCGIFFLAGS, &ifr);
if (ret == -1) {
    fprintf(stderr, "Interface %s : %s\n", dev, strerror(errno));
    exit(EXIT_FAILURE);
}
errno = saved_errno;

flags = ifr.ifr_flags;
if (flags & IFF_UP) {
    ifr.ifr_flags &= ~IFF_UP;
    saved_errno = errno;
    ret = ioctl(sfd, SIOCSIFFLAGS, &ifr);
    if (ret == -1) {
        fprintf(stderr, "Interface %s : %s\n",dev, strerror(errno));
        exit(EXIT_FAILURE);
    }
    errno = saved_errno;
}

}
将struct ifreq的ifr_ifrn.ifrn_name指定为网络接口名称后,ioctl(SIOCSIFNAME)将指定的新名称写入到ifr_ifru.ifru_newname中;该操作要求网络接口为关闭状态,即(~IFF_UP)

`