網絡link狀態檢測

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這一功能的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章