参考ethtool写了个Linux设置、获取网卡模式的接口 | 迟思堂工作室
A-A+

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

2015-03-30 09:02 代码生活, 嵌入式Linux 暂无评论 阅读 3,622 次

差不多一个月没写文章了,这期间,主要是搞一些比较复杂的问题,一直被搞,没有搞其它的东西,也就没写出什么东西来。
在找问题过程中,上网了解到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 #include #include #include #include #include #include #include #include #include #include #include #include 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,周六早上睡不着起来写的





如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate




给我留言