标准网卡控制接口

更新时间:
2024-03-14
下载文档

标准网卡控制接口

SylixOS 支持 POSIX 标准网卡控制函数,通过这些函数可以使应用程序能够方便地修改网络接口的行为,如使网卡激活、使网卡关闭、启用 DHCP 等。

另外,通过网络 ioctl 命令可以获得或者设置网卡的 IP 地址、网卡标志(如启用混杂模式)等。

网络接口 API

#include <net/if.h>
int  if_down(const char *ifname);

函数 if_down 原型分析:

  • 此函数成功返回 0,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。

调用 if_down 函数可以使指定的网络设备关闭,如果网络接口启用了 DHCP 租约,将同时停用 DHCP 租约。

#include <net/if.h>
int     if_up(const char *ifname);

函数 if_up 原型分析:

  • 此函数成功返回 0,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。

调用 if_up 函数可以使指定的网络设备激活,如果 DHCP 标志被设置,将同时启用 DHCP 租约。

#include <net/if.h>
int   if_isup(const char *ifname);

函数 if_isup 原型分析:

  • 此函数成功返回 0 代表网卡未使能,1 代表网卡使能,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。

调用 if_isup 函数可以检测指定的网络接口是否处于激活状态。

#include <net/if.h>
int   if_islink(const char *ifname);

函数 if_islink 原型分析:

  • 此函数成功返回 0 代表网卡未连接,1 代表网卡连接,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。

调用 if_islink 函数可以检测指定的网络接口是否已经连接,需要注意的是,激活的网络接口连接成功后才可以发送网络数据包。

#include <net/if.h>
int  if_set_dhcp(const char *ifname, int en);
int  if_get_dhcp(const char *ifname);

函数 if_set_dhcp 原型分析:

  • 此函数成功返回 0,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。
  • 参数 en 分两种值:0 代表停用 DHCP 租约,1 代表启用 DHCP 租约。

函数 if_get_dhcp 原型分析:

  • 此函数成功获得 DHCP 状态,0 代表停用,1 代表启用,失败返回-1 并设置错误号。
  • 参数 ifname 是网络接口名。

调用 if_set_dhcp 函数可以使指定的网络接口启用或者停用 DHCP 租约,调用 if_get_dhcp 函数将获得 DHCP 状态。

#include <net/if.h>
unsigned  if_nametoindex(const char *ifname);
char     *if_indextoname(unsigned  ifindex, char *ifname);

函数 if_nametoindex 原型分析:

  • 此函数返回网络接口索引号。
  • 参数 ifname 是网络接口名。

函数 if_indextoname 原型分析:

  • 此函数成功返回网络接口名(如 en1),失败返回 LW_NULL。
  • 参数 ifindex 是网络接口索引。
  • 输出参数 ifname 返回网络接口名。

调用 if_nametoindex 函数将返回网络接口 ifname 对应的索引号,调用 if_indextoname 函数将获得网络接口名,网络接口名存储在参数 ifname 所指的缓冲区中,此缓冲区大小至少应该为 IF_NAMESIZE(<net/if.h>)。

#include <net/if.h>
struct if_nameindex  *if_nameindex(void);
void   if_freenameindex(struct if_nameindex *ptr);

函数 if_nameindex 原型分析:

  • 此函数成功返回网络接口结构体数组指针,失败返回 NULL 并设置错误号。

函数 if_freenameindex 原型分析:

  • 参数 ptr 是 if_nameindex 函数返回的指针。

调用 if_nameindex 函数将返回 if_nameindex 结构的数组,此数组包含了所有本地的网络接口,此数组被动态分配,可以通过调用 if_freenameindex 函数来释放这段内存。if_nameindex 结构体,如下所示:

struct if_nameindex {
    unsigned         if_index;            /* Numeric index of interface            */
    char            *if_name;             /* Null-terminated name of the           */
                                          /* interface.                            */
    ……
};

下面是 if_nameindex 结构成员含义:

  • if_index:接口索引。
  • if_name:以 null 字符结尾的网络接口名,如 en1。

下面程序打印本地所有网络接口:

#include <net/if.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
    struct if_nameindex     *if_ni, *p;
    if_ni = if_nameindex();
    if (if_ni == NULL) {
       fprintf(stderr, "if_nameindex error.\n");
       return  (-1);
    }
    for (p = if_ni; !(p->if_index == 0 && p->if_name[0] == '\0'); p++) {
       fprintf(stdout, "%u: %s\n", p->if_index, p->if_name);
    }
    if_freenameindex(if_ni);
    return  (0);
}

在 SylixOS Shell 下运行结果如下:

# ./Print_network_Interface
2: en1
1: lo0

网络接口 ioctl

传统的 ioctl 函数用于那些普通的但不合适归入其他类别的任何特性的系统接口,但是 POSIX 去掉了 ioctl,POSIX 通过一些标准化的包裹函数来代替 ioctl,例如:操作串口的函数:tcgetattr 函数、tcflush 函数等,尽管如此,网络编程还是保留了 ioctl 操作,以适应具体的操作,例如获得网络接口信息,设置网卡标志等。

通常网络接口程序的第一步是从内核获取系统中配置的所有网络接口,这是通过 SIOCGIFCONF 命令请求实现的,此命令使用了 ifconf 结构,ifconf 结构又使用了 ifreq 结构,这两种结构,如下所示(两种结构体定义在 <net/if.h>):

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_ifindex;
        int                ifru_mtu;
        int                ifru_metric;
        int                ifru_type;
        void              *ifru_data;
    } ifr_ifru;
};
#define ifr_name           ifr_ifrn.ifrn_name
#define ifr_addr           ifr_ifru.ifru_addr
#define ifr_dstaddr        ifr_ifru.ifru_dstaddr
#define ifr_netmask        ifr_ifru.ifru_netmask
#define ifr_broadaddr      ifr_ifru.ifru_broadaddr
#define ifr_hwaddr         ifr_ifru.ifru_hwaddr
#define ifr_flags          ifr_ifru.ifru_flags
#define ifr_ifindex        ifr_ifru.ifru_ifindex
#define ifr_mtu            ifr_ifru.ifru_mtu
#define ifr_metric         ifr_ifru.ifru_metric
#define ifr_type           ifr_ifru.ifru_type
#define ifr_data           ifr_ifru.ifru_data

对于应用程序需要了解以下成员含义:

  • ifr_name:网络接口名(例如:en1)。
  • ifr_addr:网络接口地址。
  • ifr_dstaddr:目的地址。
  • ifr_netmask:子网掩码。
  • ifr_broadaddr:广播地址。
  • ifr_hwaddr:硬件地址。
  • ifr_flags:网络标志,如下表所示。
  • ifr_ifindex:网络接口索引。
  • ifr_mtu:网络 MTU。
  • ifr_metric:网络计量。
  • ifr_type:网络类型。
  • ifr_data:请求数据。
接口标志说明
IFF_UP网络设备激活
IFF_BROADCAST网络设备广播地址有效
IFF_POINTOPOINT网络设备是点对点模式
IFF_RUNNING网络设备已连接
IFF_MULTICAST网络设备支持IGMP
IFF_LOOPBACK回环设备
IFF_NOARP网络接口不支持ARP协议
IFF_PROMISC网络设置支持混杂模式
struct ifconf {
    int                    ifc_len;             /* size of buffer in bytes     */
    union {
        char              *ifcu_buf;
        struct ifreq      *ifcu_req;
    } ifc_ifcu;
};
#define ifc_buf            ifc_ifcu.ifcu_buf    /* buffer address              */
#define ifc_req            ifc_ifcu.ifcu_req    /* array of structures         */
  • ifc_len:缓冲区长度。
  • ifc_buf:缓冲区指针。
  • ifc_req:ifreq 结构体指针。

在调用 ioctl 函数之前分配一个缓冲区和一个 ifconf 结构,然后初始化 ifconf 结构,下图显示了 ifconf 的内存布局,ioctl 函数的第三个参数指向 ifconf 结构。内核返回的 ifreq 结构将被存放在 ifc_buf 所指向的缓冲区中如下图所示并且 ifc_len 被更新以反映缓冲区中存在的实际字节数量。

网络 ioctl 命令

前面介绍了网络编程中需要通过 ioctl 命令请求的方式来执行不同的操作,在 SylixOS 中包括了以下命令:

  • SIOCGIFCONF 命令可以获得本地网络接口列表,需要注意,这些网络接口是 AF_INET 地址族(IPv4),系统将以 ifconf 结构返回网络接口列表,如果 ifc_len 返回的长度等于原始传入的长度,则需要增加 ifc_buf 缓冲大小。
  • SIOCGSIZIFCONF 命令可以获得网络接口列表所需要的缓冲区大小。
  • SIOCSIFFLAGS 命令可以设置网络标志,如下表所示。网卡标志通过 ifreq 结构的 ifr_flags 成员进行设置。
  • SIOCGIFFLAGS 命令可以获得网络标志如下表所示。网卡标志通过 ifreq 结构的 ifr_flags 成员获得。
  • SIOCGIFTYPE 命令可以获得网络接口类型,这些类型通常在 <net/if_type.h> 中以 IFT_*类型定义,例如 IFT_PPP、IFT_LOOP。这些网络类型通过 ifreq 结构的 ifr_type 成员获得。
  • SIOCGIFINDEX 命令可以获得网络接口索引值。网络接口索引通过 ifreq 结构的 ifr_ifindex 成员获得。
  • SIOCGIFMTU 命令可以获得网络接口 MTU 值。网络 MTU 通过 ifreq 结构的 ifr_mtu 成员获得。
  • SIOCSIFMTU 命令可以设置网络接口 MTU 值。网络 MTU 通过 ifreq 结构的 ifr_mtu 成员设置。
  • SIOCGIFHWADDR 命令可以获得网卡的硬件地址。硬件地址通过 ifreq 结构的 ifr_hwaddr 成员获得,该成员是 sockaddr 结构体类型,该结构体中的 sa_data 成员存放了硬件地址,sa_family 为 ARPHRD_ETHER 类型。
  • SIOCSIFHWADDR 命令可以设置网卡的硬件地址,此命令通常需要硬件驱动的支持。
  • SIOCSIFADDR 命令可以设置网卡 IP 地址(此命令对 IPv4 有效)。
  • SIOCSIFNETMASK 命令可以设置网络掩码(此命令对 IPv4 有效)。
  • SIOCSIFDSTADDR 命令可以设置目的 IP 地址(此命令对 IPv4 有效)。
  • SIOCSIFBRDADDR 命令可以设置网络广播地址(此命令对 IPv4 有效)。
  • SIOCGIFADDR 命令可以获得网卡 IP 地址(此命令对 IPv4 有效)。
  • SIOCGIFNETMASK 命令可以获得网络掩码(此命令对 IPv4 有效)。
  • SIOCGIFDSTADDR 命令可以获得目的 IP 地址(此命令对 IPv4 有效)。
  • SIOCGIFBRDADDR 命令可以获得网络广播地址(此命令对 IPv4 有效)。
  • SIOCGIFNAME 命令可以获得网络接口名。
接口标志说明
IFF_UP网络设备激活
IFF_BROADCAST网络设备广播地址有效
IFF_POINTOPOINT网络设备是点对点模式
IFF_RUNNING网络设备已连接
IFF_MULTICAST网络设备支持IGMP
IFF_LOOPBACK回环设备
IFF_NOARP网络接口不支持ARP协议
IFF_PROMISC网络设置支持混杂模式

下面实例展示了通过网络 ioctl 获取本地网络接口信息。程序调用 ioctl 函数使用 SIOCGIFCONF 命令来获得本地的所有网络接口并打印接口名和 IP 地址。

#include <stdio.h>
#include <sys/socket.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <netdb.h>
void  show_netif_msg (struct ifreq  *ifreq)
{
    struct  sockaddr_in    *ip4;
    char                       ipbuf[20];
    fprintf(stdout, "--------------------------------------\n");
    fprintf(stdout, "netif name: %s\n", ifreq->ifr_name);
    ip4 = (struct sockaddr_in *)&ifreq->ifr_addr;
    fprintf(stdout, "source addr: %s\n", 
    inet_ntop(AF_INET, (void *)&(ip4->sin_addr), ipbuf, 20));
}
int main (int argc, char *argv[])
{
    int               fd;
    struct  ifconf    iconf;
    char              buf[512];
    int               ret, i;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        fprintf(stderr, "socket error.\n");
        return  (-1);
    }
    iconf.ifc_len = 512;
    iconf.ifc_buf = (char *)buf;
    ret = ioctl(fd, SIOCGIFCONF, (void *)&iconf);
    if (ret < 0) {
        fprintf(stderr, "ioctl error.\n");
        return  (-1);
    }
    if (iconf.ifc_len >= 512) {
        fprintf(stderr, "ifconf buffer overflow.\n");
        return  (-1);
    }
    for (i = 0; i < iconf.ifc_len / sizeof(struct ifreq); i++) {
        show_netif_msg(&iconf.ifc_req[i]);
    }
    return (0);
}

在 SylixOS Shell 下运行程序,结果如下:

# ./Network_Interface_Information
--------------------------------------
netif name: en1
source addr: 10.4.120.30
--------------------------------------
netif name: lo0
source addr: 127.0.0.1
文档内容是否对您有所帮助?
有帮助
没帮助