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

22 篇文章 6 订阅
16 篇文章 1 订阅

差不多一个月没写文章了,这期间,主要是搞一些比较复杂的问题,一直被搞,没有搞其它的东西,也就没写出什么东西来。

在找问题过程中,上网了解到ethtool这个工具十分强大,以为这个代码很复杂,而恰好领导要求我提供设置网卡信息的接口,于是下了代码,研究了一下,参考了一下,整理了一下。当然文中写的是第一个版本,要是这样的接口提供出去,其它部门的人肯定会有意见的。

Linux内核很早就已经加入ethtool相关的控制命令了(不是内核fans,不了解是哪个版本加入的),在用户空间调用ioctl函数即可。有空的话,就专门写篇关于ethtool的内核跟踪的文章。现在只需知道,在本文提到的功能中,使用ethtool的ETHTOOL_GSET可以获取网卡信息,而ETHTOOL_SSET是设置网卡信息,其它的可以查询ethtool.h这个头文件。当中最重要的结构体是ethtool_cmd,其定义如下:

/* 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设置成所有支持的模式,即把十兆百兆千兆全都加上。

下面是代码:

/*
指定网速时,需要关闭自动协商吗?需要吗?不需要吗?
千兆有半双工吗?需要吗?
-->测试发现,设置百兆、千兆时,同时开启自动协商,则会断网再连接一次。
如果不开自动协商,则不会断网,从千兆切换到百兆时会无效,故默认自动协商
*/
#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;
}

本文目的是使用ethtool接口为应用程序服务器,没有深入研究其原理,也不涉及ifreq结构体。

李迟,2015.3.28,周六早上睡不着起来写的



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值