LCOV - code coverage report
Current view: top level - shared - ethtool-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 2 413 0.5 %
Date: 2019-08-22 15:41:25 Functions: 4 27 14.8 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <net/if.h>
       4             : #include <sys/ioctl.h>
       5             : #include <linux/ethtool.h>
       6             : #include <linux/sockios.h>
       7             : 
       8             : #include "conf-parser.h"
       9             : #include "ethtool-util.h"
      10             : #include "extract-word.h"
      11             : #include "log.h"
      12             : #include "memory-util.h"
      13             : #include "missing.h"
      14             : #include "socket-util.h"
      15             : #include "string-table.h"
      16             : #include "strxcpyx.h"
      17             : 
      18             : static const char* const duplex_table[_DUP_MAX] = {
      19             :         [DUP_FULL] = "full",
      20             :         [DUP_HALF] = "half"
      21             : };
      22             : 
      23           8 : DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
      24           0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
      25             : 
      26             : static const char* const wol_table[_WOL_MAX] = {
      27             :         [WOL_PHY]         = "phy",
      28             :         [WOL_UCAST]       = "unicast",
      29             :         [WOL_MCAST]       = "multicast",
      30             :         [WOL_BCAST]       = "broadcast",
      31             :         [WOL_ARP]         = "arp",
      32             :         [WOL_MAGIC]       = "magic",
      33             :         [WOL_MAGICSECURE] = "secureon",
      34             :         [WOL_OFF]         = "off",
      35             : };
      36             : 
      37          20 : DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
      38           0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
      39             : 
      40             : static const char* const port_table[] = {
      41             :         [NET_DEV_PORT_TP]     = "tp",
      42             :         [NET_DEV_PORT_AUI]    = "aui",
      43             :         [NET_DEV_PORT_MII]    = "mii",
      44             :         [NET_DEV_PORT_FIBRE]  = "fibre",
      45             :         [NET_DEV_PORT_BNC]    = "bnc",
      46             : };
      47             : 
      48           0 : DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
      49           0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
      50             : 
      51             : static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
      52             :         [NET_DEV_FEAT_GSO]  = "tx-generic-segmentation",
      53             :         [NET_DEV_FEAT_GRO]  = "rx-gro",
      54             :         [NET_DEV_FEAT_LRO]  = "rx-lro",
      55             :         [NET_DEV_FEAT_TSO]  = "tx-tcp-segmentation",
      56             :         [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
      57             : };
      58             : 
      59             : static const char* const ethtool_link_mode_bit_table[] = {
      60             :         [ETHTOOL_LINK_MODE_10baseT_Half_BIT]               = "10baset-half",
      61             :         [ETHTOOL_LINK_MODE_10baseT_Full_BIT]               = "10baset-full",
      62             :         [ETHTOOL_LINK_MODE_100baseT_Half_BIT]              = "100baset-half",
      63             :         [ETHTOOL_LINK_MODE_100baseT_Full_BIT]              = "100baset-full",
      64             :         [ETHTOOL_LINK_MODE_1000baseT_Half_BIT]             = "1000baset-half",
      65             :         [ETHTOOL_LINK_MODE_1000baseT_Full_BIT]             = "1000baset-full",
      66             :         [ETHTOOL_LINK_MODE_Autoneg_BIT]                    = "autonegotiation",
      67             :         [ETHTOOL_LINK_MODE_TP_BIT]                         = "tp",
      68             :         [ETHTOOL_LINK_MODE_AUI_BIT]                        = "aui",
      69             :         [ETHTOOL_LINK_MODE_MII_BIT]                        = "mii",
      70             :         [ETHTOOL_LINK_MODE_FIBRE_BIT]                      = "fibre",
      71             :         [ETHTOOL_LINK_MODE_BNC_BIT]                        = "bnc",
      72             :         [ETHTOOL_LINK_MODE_10000baseT_Full_BIT]            = "10000baset-full",
      73             :         [ETHTOOL_LINK_MODE_Pause_BIT]                      = "pause",
      74             :         [ETHTOOL_LINK_MODE_Asym_Pause_BIT]                 = "asym-pause",
      75             :         [ETHTOOL_LINK_MODE_2500baseX_Full_BIT]             = "2500basex-full",
      76             :         [ETHTOOL_LINK_MODE_Backplane_BIT]                  = "backplane",
      77             :         [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT]            = "1000basekx-full",
      78             :         [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT]          = "10000basekx4-full",
      79             :         [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT]           = "10000basekr-full",
      80             :         [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT]             = "10000baser-fec",
      81             :         [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT]         = "20000basemld2-full",
      82             :         [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT]          = "20000basekr2-full",
      83             :         [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT]          = "40000basekr4-full",
      84             :         [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT]          = "40000basecr4-full",
      85             :         [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT]          = "40000basesr4-full",
      86             :         [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT]          = "40000baselr4-full",
      87             :         [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT]          = "56000basekr4-full",
      88             :         [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT]          = "56000basecr4-full",
      89             :         [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT]          = "56000basesr4-full",
      90             :         [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT]          = "56000baselr4-full",
      91             :         [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT]           = "25000basecr-full",
      92             :         [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT]           = "25000basekr-full",
      93             :         [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT]           = "25000basesr-full",
      94             :         [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT]          = "50000basecr2-full",
      95             :         [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT]          = "50000basekr2-full",
      96             :         [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT]         = "100000basekr4-full",
      97             :         [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT]         = "100000basesr4-full",
      98             :         [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT]         = "100000basecr4-full",
      99             :         [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT]     = "100000baselr4-er4-full",
     100             :         [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT]          = "50000basesr2-full",
     101             :         [ETHTOOL_LINK_MODE_1000baseX_Full_BIT]             = "1000basex-full",
     102             :         [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT]           = "10000basecr-full",
     103             :         [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT]           = "10000basesr-full",
     104             :         [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT]           = "10000baselr-full",
     105             :         [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT]          = "10000baselrm-full",
     106             :         [ETHTOOL_LINK_MODE_10000baseER_Full_BIT]           = "10000baseer-full",
     107             :         [ETHTOOL_LINK_MODE_2500baseT_Full_BIT]             = "2500baset-full",
     108             :         [ETHTOOL_LINK_MODE_5000baseT_Full_BIT]             = "5000baset-full",
     109             :         [ETHTOOL_LINK_MODE_FEC_NONE_BIT]                   = "fec-none",
     110             :         [ETHTOOL_LINK_MODE_FEC_RS_BIT]                     = "fec-rs",
     111             :         [ETHTOOL_LINK_MODE_FEC_BASER_BIT]                  = "fec-baser",
     112             :         [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT]           = "50000basekr_full",
     113             :         [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT]           = "50000basesr_full",
     114             :         [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT]           = "50000basecr_full",
     115             :         [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT]     = "50000baselr_er_fr_full",
     116             :         [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT]           = "50000basedr_full",
     117             :         [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT]         = "100000basekr2_full",
     118             :         [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT]         = "100000basesr2_full",
     119             :         [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT]         = "100000basecr2_full",
     120             :         [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2_er2_fr2_full",
     121             :         [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT]         = "100000basedr2_full",
     122             :         [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT]         = "200000basekr4_full",
     123             :         [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT]         = "200000basesr4_full",
     124             :         [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT]         = "200000basecr4_full",
     125             :         [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4_er4_fr4_full",
     126             :         [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT]         = "200000basedr4_full",
     127             : };
     128             : /* Make sure the array is large enough to fit all bits */
     129             : assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
     130             : 
     131           0 : DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
     132             : 
     133           0 : static int ethtool_connect_or_warn(int *ret, bool warn) {
     134             :         int fd;
     135             : 
     136           0 :         assert_return(ret, -EINVAL);
     137             : 
     138           0 :         fd = socket_ioctl_fd();
     139           0 :         if (fd < 0)
     140           0 :                 return log_full_errno(warn ? LOG_WARNING: LOG_DEBUG, fd,
     141             :                                        "ethtool: could not create control socket: %m");
     142             : 
     143           0 :         *ret = fd;
     144             : 
     145           0 :         return 0;
     146             : }
     147             : 
     148           0 : int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
     149           0 :         struct ethtool_drvinfo ecmd = {
     150             :                 .cmd = ETHTOOL_GDRVINFO
     151             :         };
     152           0 :         struct ifreq ifr = {
     153             :                 .ifr_data = (void*) &ecmd
     154             :         };
     155             :         char *d;
     156             :         int r;
     157             : 
     158           0 :         if (*fd < 0) {
     159           0 :                 r = ethtool_connect_or_warn(fd, true);
     160           0 :                 if (r < 0)
     161           0 :                         return r;
     162             :         }
     163             : 
     164           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     165             : 
     166           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     167           0 :         if (r < 0)
     168           0 :                 return -errno;
     169             : 
     170           0 :         d = strdup(ecmd.driver);
     171           0 :         if (!d)
     172           0 :                 return -ENOMEM;
     173             : 
     174           0 :         *ret = d;
     175           0 :         return 0;
     176             : }
     177             : 
     178           0 : int ethtool_get_link_info(int *fd, const char *ifname,
     179             :                           int *ret_autonegotiation, size_t *ret_speed,
     180             :                           Duplex *ret_duplex, NetDevPort *ret_port) {
     181           0 :         struct ethtool_cmd ecmd = {
     182             :                 .cmd = ETHTOOL_GSET,
     183             :         };
     184           0 :         struct ifreq ifr = {
     185             :                 .ifr_data = (void*) &ecmd,
     186             :         };
     187             :         int r;
     188             : 
     189           0 :         if (*fd < 0) {
     190           0 :                 r = ethtool_connect_or_warn(fd, false);
     191           0 :                 if (r < 0)
     192           0 :                         return r;
     193             :         }
     194             : 
     195           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     196             : 
     197           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     198           0 :         if (r < 0)
     199           0 :                 return -errno;
     200             : 
     201           0 :         if (ret_autonegotiation)
     202           0 :                 *ret_autonegotiation = ecmd.autoneg;
     203             : 
     204           0 :         if (ret_speed) {
     205             :                 uint32_t speed;
     206             : 
     207           0 :                 speed = ethtool_cmd_speed(&ecmd);
     208           0 :                 *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
     209           0 :                         SIZE_MAX : (size_t) speed * 1000 * 1000;
     210             :         }
     211             : 
     212           0 :         if (ret_duplex)
     213           0 :                 *ret_duplex = ecmd.duplex;
     214             : 
     215           0 :         if (ret_port)
     216           0 :                 *ret_port = ecmd.port;
     217             : 
     218           0 :         return 0;
     219             : }
     220             : 
     221           0 : int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) {
     222           0 :         struct ethtool_cmd ecmd = {
     223             :                 .cmd = ETHTOOL_GSET
     224             :         };
     225           0 :         struct ifreq ifr = {
     226             :                 .ifr_data = (void*) &ecmd
     227             :         };
     228           0 :         bool need_update = false;
     229             :         int r;
     230             : 
     231           0 :         if (speed == 0 && duplex == _DUP_INVALID)
     232           0 :                 return 0;
     233             : 
     234           0 :         if (*fd < 0) {
     235           0 :                 r = ethtool_connect_or_warn(fd, true);
     236           0 :                 if (r < 0)
     237           0 :                         return r;
     238             :         }
     239             : 
     240           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     241             : 
     242           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     243           0 :         if (r < 0)
     244           0 :                 return -errno;
     245             : 
     246           0 :         if (ethtool_cmd_speed(&ecmd) != speed) {
     247           0 :                 ethtool_cmd_speed_set(&ecmd, speed);
     248           0 :                 need_update = true;
     249             :         }
     250             : 
     251           0 :         switch (duplex) {
     252           0 :                 case DUP_HALF:
     253           0 :                         if (ecmd.duplex != DUPLEX_HALF) {
     254           0 :                                 ecmd.duplex = DUPLEX_HALF;
     255           0 :                                 need_update = true;
     256             :                         }
     257           0 :                         break;
     258           0 :                 case DUP_FULL:
     259           0 :                         if (ecmd.duplex != DUPLEX_FULL) {
     260           0 :                                 ecmd.duplex = DUPLEX_FULL;
     261           0 :                                 need_update = true;
     262             :                         }
     263           0 :                         break;
     264           0 :                 default:
     265           0 :                         break;
     266             :         }
     267             : 
     268           0 :         if (need_update) {
     269           0 :                 ecmd.cmd = ETHTOOL_SSET;
     270             : 
     271           0 :                 r = ioctl(*fd, SIOCETHTOOL, &ifr);
     272           0 :                 if (r < 0)
     273           0 :                         return -errno;
     274             :         }
     275             : 
     276           0 :         return 0;
     277             : }
     278             : 
     279           0 : int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
     280           0 :         struct ethtool_wolinfo ecmd = {
     281             :                 .cmd = ETHTOOL_GWOL
     282             :         };
     283           0 :         struct ifreq ifr = {
     284             :                 .ifr_data = (void*) &ecmd
     285             :         };
     286           0 :         bool need_update = false;
     287             :         int r;
     288             : 
     289           0 :         if (wol == _WOL_INVALID)
     290           0 :                 return 0;
     291             : 
     292           0 :         if (*fd < 0) {
     293           0 :                 r = ethtool_connect_or_warn(fd, true);
     294           0 :                 if (r < 0)
     295           0 :                         return r;
     296             :         }
     297             : 
     298           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     299             : 
     300           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     301           0 :         if (r < 0)
     302           0 :                 return -errno;
     303             : 
     304           0 :         switch (wol) {
     305           0 :         case WOL_PHY:
     306           0 :                 if (ecmd.wolopts != WAKE_PHY) {
     307           0 :                         ecmd.wolopts = WAKE_PHY;
     308           0 :                         need_update = true;
     309             :                 }
     310           0 :                 break;
     311           0 :         case WOL_UCAST:
     312           0 :                 if (ecmd.wolopts != WAKE_UCAST) {
     313           0 :                         ecmd.wolopts = WAKE_UCAST;
     314           0 :                         need_update = true;
     315             :                 }
     316           0 :                 break;
     317           0 :         case WOL_MCAST:
     318           0 :                 if (ecmd.wolopts != WAKE_MCAST) {
     319           0 :                         ecmd.wolopts = WAKE_MCAST;
     320           0 :                         need_update = true;
     321             :                 }
     322           0 :                 break;
     323           0 :         case WOL_BCAST:
     324           0 :                 if (ecmd.wolopts != WAKE_BCAST) {
     325           0 :                         ecmd.wolopts = WAKE_BCAST;
     326           0 :                         need_update = true;
     327             :                 }
     328           0 :                 break;
     329           0 :         case WOL_ARP:
     330           0 :                 if (ecmd.wolopts != WAKE_ARP) {
     331           0 :                         ecmd.wolopts = WAKE_ARP;
     332           0 :                         need_update = true;
     333             :                 }
     334           0 :                 break;
     335           0 :         case WOL_MAGIC:
     336           0 :                 if (ecmd.wolopts != WAKE_MAGIC) {
     337           0 :                         ecmd.wolopts = WAKE_MAGIC;
     338           0 :                         need_update = true;
     339             :                 }
     340           0 :                 break;
     341           0 :         case WOL_MAGICSECURE:
     342           0 :                 if (ecmd.wolopts != WAKE_MAGICSECURE) {
     343           0 :                         ecmd.wolopts = WAKE_MAGICSECURE;
     344           0 :                         need_update = true;
     345             :                 }
     346           0 :                 break;
     347           0 :         case WOL_OFF:
     348           0 :                 if (ecmd.wolopts != 0) {
     349           0 :                         ecmd.wolopts = 0;
     350           0 :                         need_update = true;
     351             :                 }
     352           0 :                 break;
     353           0 :         default:
     354           0 :                 break;
     355             :         }
     356             : 
     357           0 :         if (need_update) {
     358           0 :                 ecmd.cmd = ETHTOOL_SWOL;
     359             : 
     360           0 :                 r = ioctl(*fd, SIOCETHTOOL, &ifr);
     361           0 :                 if (r < 0)
     362           0 :                         return -errno;
     363             :         }
     364             : 
     365           0 :         return 0;
     366             : }
     367             : 
     368           0 : static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) {
     369           0 :         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
     370             :         struct {
     371             :                 struct ethtool_sset_info info;
     372             :                 uint32_t space;
     373           0 :         } buffer = {
     374             :                 .info = {
     375             :                         .cmd = ETHTOOL_GSSET_INFO,
     376           0 :                         .sset_mask = UINT64_C(1) << stringset_id,
     377             :                 },
     378             :         };
     379             :         unsigned len;
     380             :         int r;
     381             : 
     382           0 :         ifr->ifr_data = (void *) &buffer.info;
     383             : 
     384           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     385           0 :         if (r < 0)
     386           0 :                 return -errno;
     387             : 
     388           0 :         if (!buffer.info.sset_mask)
     389           0 :                 return -EINVAL;
     390             : 
     391           0 :         len = buffer.info.data[0];
     392             : 
     393           0 :         strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
     394           0 :         if (!strings)
     395           0 :                 return -ENOMEM;
     396             : 
     397           0 :         strings->cmd = ETHTOOL_GSTRINGS;
     398           0 :         strings->string_set = stringset_id;
     399           0 :         strings->len = len;
     400             : 
     401           0 :         ifr->ifr_data = (void *) strings;
     402             : 
     403           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     404           0 :         if (r < 0)
     405           0 :                 return -errno;
     406             : 
     407           0 :         *gstrings = TAKE_PTR(strings);
     408             : 
     409           0 :         return 0;
     410             : }
     411             : 
     412           0 : static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
     413             :         unsigned i;
     414             : 
     415           0 :         for (i = 0; i < strings->len; i++) {
     416           0 :                 if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
     417           0 :                         return i;
     418             :         }
     419             : 
     420           0 :         return -ENODATA;
     421             : }
     422             : 
     423           0 : int ethtool_set_features(int *fd, const char *ifname, int *features) {
     424           0 :         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
     425             :         struct ethtool_sfeatures *sfeatures;
     426             :         int block, bit, i, r;
     427           0 :         struct ifreq ifr = {};
     428             : 
     429           0 :         if (*fd < 0) {
     430           0 :                 r = ethtool_connect_or_warn(fd, true);
     431           0 :                 if (r < 0)
     432           0 :                         return r;
     433             :         }
     434             : 
     435           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     436             : 
     437           0 :         r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings);
     438           0 :         if (r < 0)
     439           0 :                 return log_warning_errno(r, "ethtool: could not get ethtool features for %s", ifname);
     440             : 
     441           0 :         sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
     442           0 :         sfeatures->cmd = ETHTOOL_SFEATURES;
     443           0 :         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
     444             : 
     445           0 :         for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
     446             : 
     447           0 :                 if (features[i] != -1) {
     448             : 
     449           0 :                         r = find_feature_index(strings, netdev_feature_table[i]);
     450           0 :                         if (r < 0) {
     451           0 :                                 log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]);
     452           0 :                                 continue;
     453             :                         }
     454             : 
     455           0 :                         block = r / 32;
     456           0 :                         bit = r % 32;
     457             : 
     458           0 :                         sfeatures->features[block].valid |= 1 << bit;
     459             : 
     460           0 :                         if (features[i])
     461           0 :                                 sfeatures->features[block].requested |= 1 << bit;
     462             :                         else
     463           0 :                                 sfeatures->features[block].requested &= ~(1 << bit);
     464             :                 }
     465             :         }
     466             : 
     467           0 :         ifr.ifr_data = (void *) sfeatures;
     468             : 
     469           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     470           0 :         if (r < 0)
     471           0 :                 return log_warning_errno(r, "ethtool: could not set ethtool features for %s", ifname);
     472             : 
     473           0 :         return 0;
     474             : }
     475             : 
     476           0 : static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
     477             :         struct ecmd {
     478             :                 struct ethtool_link_settings req;
     479             :                 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
     480           0 :         } ecmd = {
     481             :                 .req.cmd = ETHTOOL_GLINKSETTINGS,
     482             :         };
     483             :         struct ethtool_link_usettings *u;
     484             :         unsigned offset;
     485             :         int r;
     486             : 
     487             :         /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
     488             :            handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
     489             :            agree with user, it returns the bitmap length it is expecting from user as a negative
     490             :            length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
     491             :            all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
     492             :            https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
     493             :         */
     494             : 
     495           0 :         ifr->ifr_data = (void *) &ecmd;
     496             : 
     497           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     498           0 :         if (r < 0)
     499           0 :                 return -errno;
     500             : 
     501           0 :         if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
     502           0 :                 return -EOPNOTSUPP;
     503             : 
     504           0 :         ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
     505             : 
     506           0 :         ifr->ifr_data = (void *) &ecmd;
     507             : 
     508           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     509           0 :         if (r < 0)
     510           0 :                 return -errno;
     511             : 
     512           0 :         if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
     513           0 :                 return -EOPNOTSUPP;
     514             : 
     515           0 :         u = new(struct ethtool_link_usettings, 1);
     516           0 :         if (!u)
     517           0 :                 return -ENOMEM;
     518             : 
     519           0 :         *u = (struct ethtool_link_usettings) {
     520             :                 .base = ecmd.req,
     521             :         };
     522             : 
     523           0 :         offset = 0;
     524           0 :         memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
     525             : 
     526           0 :         offset += ecmd.req.link_mode_masks_nwords;
     527           0 :         memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
     528             : 
     529           0 :         offset += ecmd.req.link_mode_masks_nwords;
     530           0 :         memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
     531             : 
     532           0 :         *g = u;
     533             : 
     534           0 :         return 0;
     535             : }
     536             : 
     537           0 : static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
     538             :         struct ethtool_link_usettings *e;
     539           0 :         struct ethtool_cmd ecmd = {
     540             :                 .cmd = ETHTOOL_GSET,
     541             :         };
     542             :         int r;
     543             : 
     544           0 :         ifr->ifr_data = (void *) &ecmd;
     545             : 
     546           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     547           0 :         if (r < 0)
     548           0 :                 return -errno;
     549             : 
     550           0 :         e = new(struct ethtool_link_usettings, 1);
     551           0 :         if (!e)
     552           0 :                 return -ENOMEM;
     553             : 
     554           0 :         *e = (struct ethtool_link_usettings) {
     555             :                 .base.cmd = ETHTOOL_GSET,
     556             :                 .base.link_mode_masks_nwords = 1,
     557           0 :                 .base.speed = ethtool_cmd_speed(&ecmd),
     558           0 :                 .base.duplex = ecmd.duplex,
     559           0 :                 .base.port = ecmd.port,
     560           0 :                 .base.phy_address = ecmd.phy_address,
     561           0 :                 .base.autoneg = ecmd.autoneg,
     562           0 :                 .base.mdio_support = ecmd.mdio_support,
     563             : 
     564           0 :                 .link_modes.supported[0] = ecmd.supported,
     565           0 :                 .link_modes.advertising[0] = ecmd.advertising,
     566           0 :                 .link_modes.lp_advertising[0] = ecmd.lp_advertising,
     567             :         };
     568             : 
     569           0 :         *u = e;
     570             : 
     571           0 :         return 0;
     572             : }
     573             : 
     574           0 : static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
     575             :         struct {
     576             :                 struct ethtool_link_settings req;
     577             :                 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
     578           0 :         } ecmd = {};
     579             :         unsigned offset;
     580             :         int r;
     581             : 
     582           0 :         if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
     583           0 :                 return -EINVAL;
     584             : 
     585           0 :         ecmd.req = u->base;
     586           0 :         ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
     587           0 :         offset = 0;
     588           0 :         memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
     589             : 
     590           0 :         offset += ecmd.req.link_mode_masks_nwords;
     591           0 :         memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
     592             : 
     593           0 :         offset += ecmd.req.link_mode_masks_nwords;
     594           0 :         memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
     595             : 
     596           0 :         ifr->ifr_data = (void *) &ecmd;
     597             : 
     598           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     599           0 :         if (r < 0)
     600           0 :                 return -errno;
     601             : 
     602           0 :         return 0;
     603             : }
     604             : 
     605           0 : static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
     606           0 :         struct ethtool_cmd ecmd = {
     607             :                 .cmd = ETHTOOL_SSET,
     608             :         };
     609             :         int r;
     610             : 
     611           0 :         if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
     612           0 :                 return -EINVAL;
     613             : 
     614           0 :         ecmd.supported = u->link_modes.supported[0];
     615           0 :         ecmd.advertising = u->link_modes.advertising[0];
     616           0 :         ecmd.lp_advertising = u->link_modes.lp_advertising[0];
     617             : 
     618           0 :         ethtool_cmd_speed_set(&ecmd, u->base.speed);
     619             : 
     620           0 :         ecmd.duplex = u->base.duplex;
     621           0 :         ecmd.port = u->base.port;
     622           0 :         ecmd.phy_address = u->base.phy_address;
     623           0 :         ecmd.autoneg = u->base.autoneg;
     624           0 :         ecmd.mdio_support = u->base.mdio_support;
     625           0 :         ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
     626           0 :         ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
     627             : 
     628           0 :         ifr->ifr_data = (void *) &ecmd;
     629             : 
     630           0 :         r = ioctl(fd, SIOCETHTOOL, ifr);
     631           0 :         if (r < 0)
     632           0 :                 return -errno;
     633             : 
     634           0 :         return 0;
     635             : }
     636             : 
     637             : /* If autonegotiation is disabled, the speed and duplex represent the fixed link
     638             :  * mode and are writable if the driver supports multiple link modes. If it is
     639             :  * enabled then they are read-only. If the link is up they represent the negotiated
     640             :  * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
     641             :  * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
     642             :  */
     643           0 : int ethtool_set_glinksettings(
     644             :                 int *fd,
     645             :                 const char *ifname,
     646             :                 int autonegotiation,
     647             :                 uint32_t advertise[static N_ADVERTISE],
     648             :                 size_t speed,
     649             :                 Duplex duplex,
     650             :                 NetDevPort port) {
     651           0 :         _cleanup_free_ struct ethtool_link_usettings *u = NULL;
     652           0 :         struct ifreq ifr = {};
     653             :         int r;
     654             : 
     655           0 :         assert(advertise);
     656             : 
     657           0 :         if (autonegotiation != AUTONEG_DISABLE && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
     658           0 :                 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
     659           0 :                 return 0;
     660             :         }
     661             : 
     662           0 :         if (*fd < 0) {
     663           0 :                 r = ethtool_connect_or_warn(fd, true);
     664           0 :                 if (r < 0)
     665           0 :                         return r;
     666             :         }
     667             : 
     668           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     669             : 
     670           0 :         r = get_glinksettings(*fd, &ifr, &u);
     671           0 :         if (r < 0) {
     672           0 :                 r = get_gset(*fd, &ifr, &u);
     673           0 :                 if (r < 0)
     674           0 :                         return log_warning_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname);
     675             :         }
     676             : 
     677           0 :         if (speed > 0)
     678           0 :                 u->base.speed = DIV_ROUND_UP(speed, 1000000);
     679             : 
     680           0 :         if (duplex != _DUP_INVALID)
     681           0 :                 u->base.duplex = duplex;
     682             : 
     683           0 :         if (port != _NET_DEV_PORT_INVALID)
     684           0 :                 u->base.port = port;
     685             : 
     686           0 :         if (autonegotiation >= 0)
     687           0 :                 u->base.autoneg = autonegotiation;
     688             : 
     689           0 :         if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
     690           0 :                 u->base.autoneg = AUTONEG_ENABLE;
     691           0 :                 memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
     692           0 :                 memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
     693             :                         ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
     694             :         }
     695             : 
     696           0 :         if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
     697           0 :                 r = set_slinksettings(*fd, &ifr, u);
     698             :         else
     699           0 :                 r = set_sset(*fd, &ifr, u);
     700           0 :         if (r < 0)
     701           0 :                 return log_warning_errno(r, "ethtool: Cannot set device settings for %s : %m", ifname);
     702             : 
     703           0 :         return r;
     704             : }
     705             : 
     706           0 : int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) {
     707           0 :         struct ethtool_channels ecmd = {
     708             :                 .cmd = ETHTOOL_GCHANNELS
     709             :         };
     710           0 :         struct ifreq ifr = {
     711             :                 .ifr_data = (void*) &ecmd
     712             :         };
     713             : 
     714           0 :         bool need_update = false;
     715             :         int r;
     716             : 
     717           0 :         if (*fd < 0) {
     718           0 :                 r = ethtool_connect_or_warn(fd, true);
     719           0 :                 if (r < 0)
     720           0 :                         return r;
     721             :         }
     722             : 
     723           0 :         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
     724             : 
     725           0 :         r = ioctl(*fd, SIOCETHTOOL, &ifr);
     726           0 :         if (r < 0)
     727           0 :                 return -errno;
     728             : 
     729           0 :         if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) {
     730           0 :                 ecmd.rx_count = channels->rx_count;
     731           0 :                 need_update = true;
     732             :         }
     733             : 
     734           0 :         if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) {
     735           0 :                 ecmd.tx_count = channels->tx_count;
     736           0 :                 need_update = true;
     737             :         }
     738             : 
     739           0 :         if (channels->other_count_set && ecmd.other_count != channels->other_count) {
     740           0 :                 ecmd.other_count = channels->other_count;
     741           0 :                 need_update = true;
     742             :         }
     743             : 
     744           0 :         if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) {
     745           0 :                 ecmd.combined_count = channels->combined_count;
     746           0 :                 need_update = true;
     747             :         }
     748             : 
     749           0 :         if (need_update) {
     750           0 :                 ecmd.cmd = ETHTOOL_SCHANNELS;
     751             : 
     752           0 :                 r = ioctl(*fd, SIOCETHTOOL, &ifr);
     753           0 :                 if (r < 0)
     754           0 :                         return -errno;
     755             :         }
     756             : 
     757           0 :         return 0;
     758             : }
     759             : 
     760           0 : int config_parse_channel(const char *unit,
     761             :                          const char *filename,
     762             :                          unsigned line,
     763             :                          const char *section,
     764             :                          unsigned section_line,
     765             :                          const char *lvalue,
     766             :                          int ltype,
     767             :                          const char *rvalue,
     768             :                          void *data,
     769             :                          void *userdata) {
     770           0 :         netdev_channels *channels = data;
     771             :         uint32_t k;
     772             :         int r;
     773             : 
     774           0 :         assert(filename);
     775           0 :         assert(section);
     776           0 :         assert(lvalue);
     777           0 :         assert(rvalue);
     778           0 :         assert(data);
     779             : 
     780           0 :         r = safe_atou32(rvalue, &k);
     781           0 :         if (r < 0) {
     782           0 :                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue);
     783           0 :                 return 0;
     784             :         }
     785             : 
     786           0 :         if (k < 1) {
     787           0 :                 log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue);
     788           0 :                 return 0;
     789             :         }
     790             : 
     791           0 :         if (streq(lvalue, "RxChannels")) {
     792           0 :                 channels->rx_count = k;
     793           0 :                 channels->rx_count_set = true;
     794           0 :         } else if (streq(lvalue, "TxChannels")) {
     795           0 :                 channels->tx_count = k;
     796           0 :                 channels->tx_count_set = true;
     797           0 :         } else if (streq(lvalue, "OtherChannels")) {
     798           0 :                 channels->other_count = k;
     799           0 :                 channels->other_count_set = true;
     800           0 :         } else if (streq(lvalue, "CombinedChannels")) {
     801           0 :                 channels->combined_count = k;
     802           0 :                 channels->combined_count_set = true;
     803             :         }
     804             : 
     805           0 :         return 0;
     806             : }
     807             : 
     808           0 : int config_parse_advertise(const char *unit,
     809             :                            const char *filename,
     810             :                            unsigned line,
     811             :                            const char *section,
     812             :                            unsigned section_line,
     813             :                            const char *lvalue,
     814             :                            int ltype,
     815             :                            const char *rvalue,
     816             :                            void *data,
     817             :                            void *userdata) {
     818           0 :         uint32_t *advertise = data;
     819             :         const char *p;
     820             :         int r;
     821             : 
     822           0 :         assert(filename);
     823           0 :         assert(section);
     824           0 :         assert(lvalue);
     825           0 :         assert(rvalue);
     826           0 :         assert(data);
     827             : 
     828           0 :         if (isempty(rvalue)) {
     829             :                 /* Empty string resets the value. */
     830           0 :                 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
     831           0 :                 return 0;
     832             :         }
     833             : 
     834           0 :         for (p = rvalue;;) {
     835           0 :                 _cleanup_free_ char *w = NULL;
     836             :                 enum ethtool_link_mode_bit_indices mode;
     837             : 
     838           0 :                 r = extract_first_word(&p, &w, NULL, 0);
     839           0 :                 if (r == -ENOMEM)
     840           0 :                         return log_oom();
     841           0 :                 if (r < 0) {
     842           0 :                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue);
     843           0 :                         break;
     844             :                 }
     845           0 :                 if (r == 0)
     846           0 :                         break;
     847             : 
     848           0 :                 mode = ethtool_link_mode_bit_from_string(w);
     849             :                 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
     850             :                  * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
     851           0 :                 if ((int) mode < 0) {
     852           0 :                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w);
     853           0 :                         continue;
     854             :                 }
     855             : 
     856           0 :                 advertise[mode / 32] |= 1UL << (mode % 32);
     857             :         }
     858             : 
     859           0 :         return 0;
     860             : }

Generated by: LCOV version 1.14