差不多一个月没写文章了,这期间,主要是搞一些比较复杂的问题,一直被搞,没有搞其它的东西,也就没写出什么东西来。
在找问题过程中,上网了解到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;
- }
原文地址:http://blog.csdn.net/subfate/article/details/44746007