diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 700defeda6b..55ca597c97a 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -946,6 +946,21 @@
+
+ MDI=
+
+ Specifies the medium dependent interface (MDI) mode for the interface. A MDI describes
+ the interface from a physical layer implementation to the physical medium used to carry the
+ transmission. Takes one of the following words: straight (or equivalently:
+ mdi), crossover (or equivalently:
+ mdi-x, mdix), and auto. When
+ straight, the MDI straight through mode will be used. When
+ crossover, the MDI crossover (MDI-X) mode will be used. When
+ auto, the MDI status is automatically detected. Defaults to unset, and the
+ kernel's default will be used.
+
+
+
SR-IOVVirtualFunctions=
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index 489a29fa2e8..6c9aeb1c6c6 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -74,6 +74,15 @@ static const char* const port_table[] = {
DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
+static const char* const mdi_table[] = {
+ [ETH_TP_MDI_INVALID] = "unknown",
+ [ETH_TP_MDI] = "mdi",
+ [ETH_TP_MDI_X] = "mdi-x",
+ [ETH_TP_MDI_AUTO] = "auto",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int);
+
static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
[NET_DEV_FEAT_SG] = "tx-scatter-gather",
[NET_DEV_FEAT_IP_CSUM] = "tx-checksum-ipv4",
@@ -835,6 +844,8 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r
.base.phy_address = ecmd.phy_address,
.base.autoneg = ecmd.autoneg,
.base.mdio_support = ecmd.mdio_support,
+ .base.eth_tp_mdix = ecmd.eth_tp_mdix,
+ .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
.link_modes.supported[0] = ecmd.supported,
.link_modes.advertising[0] = ecmd.advertising,
@@ -914,7 +925,8 @@ int ethtool_set_glinksettings(
const uint32_t advertise[static N_ADVERTISE],
uint64_t speed,
Duplex duplex,
- NetDevPort port) {
+ NetDevPort port,
+ uint8_t mdi) {
_cleanup_free_ struct ethtool_link_usettings *u = NULL;
struct ifreq ifr = {};
@@ -926,7 +938,7 @@ int ethtool_set_glinksettings(
assert(advertise);
if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
- speed == 0 && duplex < 0 && port < 0)
+ speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
return 0;
/* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
@@ -957,7 +969,7 @@ int ethtool_set_glinksettings(
if (r < 0) {
r = get_gset(*fd, &ifr, &u);
if (r < 0)
- return log_debug_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname);
+ return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
}
if (speed > 0)
@@ -984,6 +996,13 @@ int ethtool_set_glinksettings(
ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
}
+ if (mdi != ETH_TP_MDI_INVALID) {
+ if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
+ log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
+ else
+ UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
+ }
+
if (!changed)
return 0;
@@ -1261,6 +1280,48 @@ int config_parse_advertise(
}
}
+int config_parse_mdi(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint8_t *mdi = ASSERT_PTR(data);
+
+ assert(filename);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *mdi = ETH_TP_MDI_INVALID;
+ return 0;
+ }
+
+ if (STR_IN_SET(rvalue, "mdi", "straight")) {
+ *mdi = ETH_TP_MDI;
+ return 0;
+ }
+
+ if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
+ *mdi = ETH_TP_MDI_X;
+ return 0;
+ }
+
+ if (streq(rvalue, "auto")) {
+ *mdi = ETH_TP_MDI_AUTO;
+ return 0;
+ }
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+}
+
int config_parse_ring_buffer_or_channel(
const char *unit,
const char *filename,
diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h
index 8ca221bc090..d07cfaefb26 100644
--- a/src/shared/ethtool-util.h
+++ b/src/shared/ethtool-util.h
@@ -168,9 +168,15 @@ int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts, const uint8_t password[SOPASS_MAX]);
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]);
-int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
- int autonegotiation, const uint32_t advertise[static N_ADVERTISE],
- uint64_t speed, Duplex duplex, NetDevPort port);
+int ethtool_set_glinksettings(
+ int *fd,
+ const char *ifname,
+ int autonegotiation,
+ const uint32_t advertise[static N_ADVERTISE],
+ uint64_t speed,
+ Duplex duplex,
+ NetDevPort port,
+ uint8_t mdi);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels);
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce);
@@ -183,12 +189,15 @@ int wol_options_to_string_alloc(uint32_t opts, char **ret);
const char *port_to_string(NetDevPort port) _const_;
NetDevPort port_from_string(const char *port) _pure_;
+const char *mdi_to_string(int mdi) _const_;
+
const char *ethtool_link_mode_bit_to_string(enum ethtool_link_mode_bit_indices val) _const_;
enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char *str) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_duplex);
CONFIG_PARSER_PROTOTYPE(config_parse_wol);
CONFIG_PARSER_PROTOTYPE(config_parse_port);
+CONFIG_PARSER_PROTOTYPE(config_parse_mdi);
CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel);
CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32);
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 7dde4ed59f5..fe33507694d 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -102,6 +102,7 @@ Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32,
Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval)
+Link.MDI, config_parse_mdi, 0, offsetof(LinkConfig, mdi)
Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs)
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 7e2da6088c4..29e960acc04 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -250,6 +250,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
.txqueuelen = UINT32_MAX,
.coalesce.use_adaptive_rx_coalesce = -1,
.coalesce.use_adaptive_tx_coalesce = -1,
+ .mdi = ETH_TP_MDI_INVALID,
.sr_iov_num_vfs = UINT32_MAX,
};
@@ -475,7 +476,7 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
r = ethtool_set_glinksettings(ethtool_fd, name,
config->autonegotiation, config->advertise,
- config->speed, config->duplex, config->port);
+ config->speed, config->duplex, config->port, config->mdi);
if (r < 0) {
if (config->autonegotiation >= 0)
log_link_warning_errno(link, r, "Could not %s auto negotiation, ignoring: %m",
@@ -495,6 +496,10 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) {
if (config->port >= 0)
log_link_warning_errno(link, r, "Could not set port to '%s', ignoring: %m",
port_to_string(config->port));
+
+ if (config->mdi != ETH_TP_MDI_INVALID)
+ log_link_warning_errno(link, r, "Could not set MDI-X to '%s', ignoring: %m",
+ mdi_to_string(config->mdi));
}
r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password);
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 0d1d117f2e1..90cb438e4b6 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -76,6 +76,7 @@ struct LinkConfig {
int tx_flow_control;
int autoneg_flow_control;
netdev_coalesce_param coalesce;
+ uint8_t mdi;
uint32_t sr_iov_num_vfs;
OrderedHashmap *sr_iov_by_section;
diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link
index a5773826c58..bffdce0ec95 100644
--- a/test/fuzz/fuzz-link-parser/directives.link
+++ b/test/fuzz/fuzz-link-parser/directives.link
@@ -80,6 +80,7 @@ RxMaxCoalescedHighFrames=
TxCoalesceHighSec=
TxMaxCoalescedHighFrames=
CoalescePacketRateSampleIntervalSec=
+MDI=
SR-IOVVirtualFunctions=
[SR-IOV]
VirtualFunction=