标准网卡控制接口
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