Linux系统提供了两类ioctl系统调用SIOCETHTOOL和SIOCXMIIXXX,用于控制或者获取网卡PHY的状态。这两类系统调用的实现取决于PHY驱动中对应ioctl的实现,一般的PHY驱动都会实现至少其中的一类。下面以获取网卡的Link状态来说明这两类系统调用的使用。代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/mii.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <linux/types.h>
#include <netinet/in.h>
static int detect_ethtool(int skfd, char *ifname);
static int detect_mii(int skfd, char *ifname);
#define MII_BMSR 0x01 /* Basic mode status register */
#define BMSR_JCD 0x0002 /* Jabber detected */
#define BMSR_LSTATUS 0x0004 /* Link status */
#define BMSR_RFAULT 0x0010 /* Remote fault detected */
/*--------------------------------------------------------------------------*/
/**
@brief Detect eth link status from ETHTOOL API and MII reg.
@param ifname -- interface name.
@return Return true if link is up, return false if link is down,
or return -1 if errors occur.
This function will first try to get eth link status from ETHTOOL API.
If it fails, it will try to get eth link status from MII reg.
*/
/*--------------------------------------------------------------------------*/
static int get_netlink_status(char *ifname)
{
int skfd = -1;
int link_status;
if (ifname == NULL) {
ifname = "eth0";
}
if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
nl_err("socket error\n");
return -1;
}
link_status = detect_ethtool(skfd, ifname);
if (link_status < 0)
link_status = detect_mii(skfd, ifname);
close(skfd);
return link_status;
}
/*--------------------------------------------------------------------------*/
/**
@brief Detect eth link status from ETHTOOL API.
@param skfd -- socket handler.
@param ifname -- interface name.
@return Return true if link is up, return false if link is down,
or return -1 if errors occur.
*/
/*--------------------------------------------------------------------------*/
int detect_ethtool(int skfd, char *ifname)
{
struct ifreq ifr;
struct ethtool_value edata;
memset(&ifr, 0, sizeof(ifr));
edata.cmd = ETHTOOL_GLINK;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);
ifr.ifr_data = (char *) &edata;
if (ioctl(skfd, SIOCETHTOOL, &ifr) == -1) {
nl_err("ETHTOOL_GLINK failed: %s\n", strerror(errno));
return -1;
}
return (edata.data ? 1 : 0);
}
/*--------------------------------------------------------------------------*/
/**
@brief Detect eth link status from MII status reg.
@param skfd -- socket handler.
@param ifname -- interface name.
@return Return true if link is up, return false if link is down,
or return -1 if errors occur.
*/
/*--------------------------------------------------------------------------*/
static int detect_mii(int skfd, char *ifname)
{
struct ifreq ifr;
struct mii_ioctl_data *mii_val;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) {
fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", ifname,
strerror(errno));
(void) close(skfd);
return -1;
}
mii_val = (struct mii_ioctl_data *)(&ifr.ifr_data);
mii_val->reg_num = MII_BMSR;
if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) {
nl_err("SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,
strerror(errno));
return -1;
}
if ((!(mii_val->val_out & BMSR_RFAULT)) &&
(mii_val->val_out & BMSR_LSTATUS) &&
(!(mii_val->val_out & BMSR_JCD))) {
return 1;
} else {
return 0;
}
}
int main(int argc, char* argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: %s <ethname>", argv[0]);
return -1;
}
if(getuid() != 0)
{
fprintf(stderr, "Netlink Status Check Need Root Power.\n");
return 1;
}
printf("Net link status: %s\n", get_netlink_status(argv[1])==1?"up":"down");
return 0;
}
SIOCETHTOOL相当于是对SIOCGMIIPHY的一个封装吧,其实最终调用的方式都是读取状态寄存器Link Status的值,测试程序中用到的三个寄存器如下图所示:
Remote Fault:远端错误指示位。Bit4=1代表连接对端(Link Partner)出错,至于出错的具体类型以及错误检测机制在规范中并没有定义,由PHY的制造商自由发挥,一般的厂商都会在其他的寄存器(Register16-31由厂商自行定义)指示比较详细的错误类型。在与端口相关的故障查证中,Remote Fault是一个重要的指示信息,通过互联双方的Remote Fault信息(可能要加上其他的具体错误指示),可以帮助定位故障原因。
Link Status:Link状态指示位。Bit2=1代表端口Link up,0则代表端口Link down。实际应用中一般都是通过Bit2来判断端口的状态。而且,一般的MAC芯片也是通过轮询PHY的这个寄存器值来判断端口的Link状态的(这个过程可能有不同的名称,比如BCM叫做Link Scan,而Marvell叫做PHY Polling。)如前所述,在AN Enable的情况下,Link Status的信息只有在Auto-Negotiation Complete指示已经完成的情况下才是正确可靠的,否则有可能出错。
Jabber Detect:Jabber 检测指示位。IEEE802.3对Jabber的解释是"A condition wherein a station transmits for a period of time longer than the maximum permissible packet length, usually due to a fault condition"。这一位指示的是Link Partner发送的时间超过了规定的最大长度。值得注意的是,Jabber Detect只有在10BASE-T模式下才有意义,100和1000M模式是没有定义Jabber这一功能的。