您的位置:首页 > 运维架构 > Linux

参考ethtool写了个Linux设置、获取网卡模式的接口

2017-11-11 17:37 351 查看
差不多一个月没写文章了,这期间,主要是搞一些比较复杂的问题,一直被搞,没有搞其它的东西,也就没写出什么东西来。
在找问题过程中,上网了解到ethtool这个工具十分强大,以为这个代码很复杂,而恰好领导要求我提供设置网卡信息的接口,于是下了代码,研究了一下,参考了一下,整理了一下。当然文中写的是第一个版本,要是这样的接口提供出去,其它部门的人肯定会有意见的。
Linux内核很早就已经加入ethtool相关的控制命令了(不是内核fans,不了解是哪个版本加入的),在用户空间调用ioctl函数即可。有空的话,就专门写篇关于ethtool的内核跟踪的文章。现在只需知道,在本文提到的功能中,使用ethtool的ETHTOOL_GSET可以获取网卡信息,而ETHTOOL_SSET是设置网卡信息,其它的可以查询ethtool.h这个头文件。当中最重要的结构体是ethtool_cmd,其定义如下:

[cpp]
view plain
copy

/* This should work for both 32 and 64 bit userland. */  
struct ethtool_cmd {  
    __u32   cmd;  
    __u32   supported;  /* Features this interface supports */  
    __u32   advertising;    /* Features this interface advertises */  
    __u16   speed;      /* The forced speed, 10Mb, 100Mb, gigabit */  
    __u8    duplex;     /* Duplex, half or full */  
    __u8    port;       /* Which connector port */  
    __u8    phy_address;  
    __u8    transceiver;    /* Which transceiver to use */  
    __u8    autoneg;    /* Enable or disable autonegotiation */  
    __u8    mdio_support;  
    __u32   maxtxpkt;   /* Tx pkts before generating tx int */  
    __u32   maxrxpkt;   /* Rx pkts before generating rx int */  
    __u16   speed_hi;  
    __u8    eth_tp_mdix;  
    __u8    reserved2;  
    __u32   lp_advertising; /* Features the link partner advertises */  
    __u32   reserved[2];  
};  

从上可以看到,我们最关心的如网卡速率、双工模式、自动协商等,都在此结构体中。于是,读取、设置这些信息,就不困难了。
由于涉及到网卡,ioctl用到的设备描述符是socket产生的描述符。读取网卡信息比较简单,赋值相关参数,调用ioctl,返回正确后即可读取ethtool_cmd中的对应字段,从而得到结果。
对于设置网卡,需要注意的是当使用自动协商——即不指定速率情况下,要将advertising设置成所有支持的模式,即把十兆百兆千兆全都加上。

下面是代码:

[cpp]
view plain
copy

/* 
指定网速时,需要关闭自动协商吗?需要吗?不需要吗? 
千兆有半双工吗?需要吗? 
-->测试发现,设置百兆、千兆时,同时开启自动协商,则会断网再连接一次。 
如果不开自动协商,则不会断网,从千兆切换到百兆时会无效,故默认自动协商 
*/  
#include <string.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <string.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/ioctl.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
#include <net/if.h>  
  
#include <linux/ethtool.h>  
#include <linux/sockios.h>  
  
int ethtool_mygset(const char* devname, int* speed, int* duplex, int* autoneg, int* link)  
{  
    struct ifreq ifr;  
    int fd = 0;  
    int err = -1;  
  
    struct ethtool_cmd ecmd;  
    struct ethtool_value edata;  
  
    if (devname == NULL) return -2;  
  
    memset(&ifr, 0, sizeof(ifr));  
    strcpy(ifr.ifr_name, devname);  
  
    fd = socket(AF_INET, SOCK_DGRAM, 0);  
    printf("socket fd: %d\n", fd);  
    if (fd < 0)  
    {  
        perror("ethtool_gset Cannot get control socket");  
        return -1;  
    }  
  
    ecmd.cmd = ETHTOOL_GSET;  
    ifr.ifr_data = (caddr_t)&ecmd;  
    err = ioctl(fd, SIOCETHTOOL, &ifr);  
  
    if (err < 0)  
    {  
        perror("Cannot get device settings");  
        return -1;  
    }  
      
    printf("PHY xx - %d/%s ", ecmd.speed, (ecmd.duplex == DUPLEX_FULL) ? "Full" : "Half");  
    printf(" Auto-negotiation: %s ", (ecmd.autoneg == AUTONEG_DISABLE) ? "off" : "on");  
  
    switch (ecmd.speed) {  
    case SPEED_10:  
    case SPEED_100:  
    case SPEED_1000:  
    case SPEED_2500:  
    case SPEED_10000:  
        *speed = ecmd.speed;  
        break;  
    default:  
        fprintf(stdout, "Unknown! (%i)\n", ecmd.speed);  
        break;  
    };  
      
    switch (ecmd.duplex) {  
    case DUPLEX_HALF:  
    case DUPLEX_FULL:  
        *duplex = ecmd.duplex;  
        break;  
    default:  
        fprintf(stdout, "Unknown! (%i)\n", ecmd.duplex);  
        break;  
    };  
    *autoneg = ecmd.autoneg;  
  
    edata.cmd = ETHTOOL_GLINK;  
    ifr.ifr_data = (caddr_t)&edata;  
    err = ioctl(fd, SIOCETHTOOL, &ifr);  
    if (err == 0)  
    {  
        *link = edata.data ? 1: 0;  
  
        printf(" %s\n", edata.data ? "Up" : "Down");  
    }  
    else if (errno != EOPNOTSUPP)  
    {  
        perror("Cannot get link status");  
    }  
  
    close(fd);  
  
    return 0;  
}  
  
int ethtool_mysset(const char* devname, int speed, int duplex, int autoneg)  
{  
    int speed_wanted = -1;  
    int duplex_wanted = -1;  
    int autoneg_wanted = AUTONEG_ENABLE;  
    int advertising_wanted = -1;  
      
    struct ethtool_cmd ecmd;  
    struct ifreq ifr;  
    int fd = 0;  
    int err = -1;  
  
    // pass args  
    if (devname == NULL)  
    {  
        printf("devname emtpy...\n");  
        return -2;  
    }  
    speed_wanted = speed;  
    duplex_wanted = duplex;  
    autoneg_wanted = autoneg;  
  
    strcpy(ifr.ifr_name, devname);  
  
    fd = socket(AF_INET, SOCK_DGRAM, 0);  
    if (fd < 0) {  
        perror("ethtool_sset Cannot get control socket");  
        return -1;  
    }  
  
    ecmd.cmd = ETHTOOL_GSET;  
    ifr.ifr_data = (caddr_t)&ecmd;  
    err = ioctl(fd, SIOCETHTOOL, &ifr);  
    if (err < 0)  
    {  
        perror("Cannot get current device settings");  
        return -1;  
    }  
  
    if (speed_wanted != -1)  
        ecmd.speed = speed_wanted;  
    if (duplex_wanted != -1)  
        ecmd.duplex = duplex_wanted;  
    if (autoneg_wanted != -1)  
        ecmd.autoneg = autoneg_wanted;  
  
    if ((autoneg_wanted == AUTONEG_ENABLE) && (advertising_wanted < 0))  
    {  
        if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)  
            advertising_wanted = ADVERTISED_10baseT_Half;  
        else if (speed_wanted == SPEED_10 &&  
             duplex_wanted == DUPLEX_FULL)  
            advertising_wanted = ADVERTISED_10baseT_Full;  
        else if (speed_wanted == SPEED_100 &&  
             duplex_wanted == DUPLEX_HALF)  
            advertising_wanted = ADVERTISED_100baseT_Half;  
        else if (speed_wanted == SPEED_100 &&  
             duplex_wanted == DUPLEX_FULL)  
            advertising_wanted = ADVERTISED_100baseT_Full;  
        else if (speed_wanted == SPEED_1000 &&  
             duplex_wanted == DUPLEX_HALF)  
            advertising_wanted = ADVERTISED_1000baseT_Half;  
        else if (speed_wanted == SPEED_1000 &&  
             duplex_wanted == DUPLEX_FULL)  
            advertising_wanted = ADVERTISED_1000baseT_Full;  
        else if (speed_wanted == SPEED_2500 &&  
             duplex_wanted == DUPLEX_FULL)  
            advertising_wanted = ADVERTISED_2500baseX_Full;  
        else if (speed_wanted == SPEED_10000 &&  
             duplex_wanted == DUPLEX_FULL)  
            advertising_wanted = ADVERTISED_10000baseT_Full;  
        else  
            advertising_wanted = 0;  
    }  
  
    if (advertising_wanted != -1)  
    {  
        if (advertising_wanted == 0)  
            ecmd.advertising = ecmd.supported &  
                (ADVERTISED_10baseT_Half |  
                 ADVERTISED_10baseT_Full |  
                 ADVERTISED_100baseT_Half |  
                 ADVERTISED_100baseT_Full |  
                 ADVERTISED_1000baseT_Half |  
                 ADVERTISED_1000baseT_Full |  
                 ADVERTISED_2500baseX_Full |  
                 ADVERTISED_10000baseT_Full);  
        else  
            ecmd.advertising = advertising_wanted;  
    }  
  
    ecmd.cmd = ETHTOOL_SSET;  
    ifr.ifr_data = (caddr_t)&ecmd;  
    err = ioctl(fd, SIOCETHTOOL, &ifr);  
    if (err < 0)  
        perror("Cannot set new settings");  
  
    if (err < 0) {  
        if (speed_wanted != -1)  
            fprintf(stderr, "  not setting speed\n");  
        if (duplex_wanted != -1)  
            fprintf(stderr, "  not setting duplex\n");  
        if (autoneg_wanted != -1)  
            fprintf(stderr, "  not setting autoneg\n");  
    }  
  
    close(fd);  
  
    return 0;  


原文地址:http://blog.csdn.net/subfate/article/details/44746007


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: