summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac3
-rw-r--r--man/systemd.link.xml14
-rw-r--r--man/systemd.network.xml7
-rw-r--r--man/systemd.service.xml6
-rw-r--r--src/basic/missing.h27
-rw-r--r--src/core/namespace.c32
-rw-r--r--src/libsystemd-network/dhcp-internal.h5
-rw-r--r--src/libsystemd-network/dhcp-network.c10
-rw-r--r--src/libsystemd-network/dhcp-packet.c6
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c25
-rw-r--r--src/libsystemd-network/test-dhcp-client.c2
-rw-r--r--src/libsystemd/sd-bus/busctl-introspect.c86
-rw-r--r--src/network/networkd-dhcp4.c6
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/resolve/resolved-resolv-conf.c2
-rw-r--r--src/resolve/resolved.c4
-rw-r--r--src/systemd/sd-dhcp-client.h3
-rw-r--r--src/udev/net/ethtool-util.c209
-rw-r--r--src/udev/net/ethtool-util.h18
-rw-r--r--src/udev/net/link-config-gperf.gperf1
-rw-r--r--src/udev/net/link-config.c28
-rw-r--r--src/udev/net/link-config.h1
23 files changed, 409 insertions, 88 deletions
diff --git a/configure.ac b/configure.ac
index 7f6b3b937c..6423204178 100644
--- a/configure.ac
+++ b/configure.ac
@@ -319,9 +319,10 @@ AC_CHECK_DECLS([
#include <linux/random.h>
]])
-AC_CHECK_TYPES([char16_t, char32_t, key_serial_t],
+AC_CHECK_TYPES([char16_t, char32_t, key_serial_t, struct ethtool_link_settings],
[], [], [[
#include <uchar.h>
+#include <linux/ethtool.h>
]])
AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE,
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 8edbe758d9..023e24eeb3 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -359,6 +359,20 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>AutoNegotiation=</varname></term>
+ <listitem>
+ <para>Enables or disables automatic negotiation of transmission parameters.
+ Autonegotiation is a procedure by which two connected ethernet devices choose
+ common transmission parameters, such as speed, duplex mode, and flow control.
+ Takes a boolean value. Unset by default, which means that the kernel default
+ will be used.</para>
+
+ <para>Note that if autonegotiation is enabled, speed and duplex settings are
+ read-only. If autonegotation is disabled, speed and duplex settings are writable
+ if the driver supports multiple link modes.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>WakeOnLan=</varname></term>
<listitem>
<para>The Wake-on-LAN policy to set for the device. The
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 2fb4907634..99283813fd 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -984,6 +984,13 @@
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>ListenPort=</varname></term>
+ <listitem>
+ <para>Allow setting custom port for the DHCP client to listen on.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 5c65957bda..3ba6ab34db 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -663,6 +663,12 @@
or signal is specified in
<varname>RestartForceExitStatus=</varname> (see below).</para>
+ <para>Note that service restart is subject to unit start rate
+ limiting configured with <varname>StartLimitIntervalSec=</varname>
+ and <varname>StartLimitBurst=</varname>, see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details.</para>
+
<para>Setting this to <option>on-failure</option> is the
recommended choice for long-running services, in order to
increase reliability by attempting automatic recovery from
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 4c013be608..a5ae5d9e79 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1076,6 +1076,33 @@ typedef int32_t key_serial_t;
#define IFA_F_MCAUTOJOIN 0x400
#endif
+#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS
+
+#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
+#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
+
+struct ethtool_link_settings {
+ __u32 cmd;
+ __u32 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __s8 link_mode_masks_nwords;
+ __u32 reserved[8];
+ __u32 link_mode_masks[0];
+ /* layout of link_mode_masks fields:
+ * __u32 map_supported[link_mode_masks_nwords];
+ * __u32 map_advertising[link_mode_masks_nwords];
+ * __u32 map_lp_advertising[link_mode_masks_nwords];
+ */
+};
+
+#endif
+
#endif
#include "missing_syscall.h"
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 308e4d768e..67b203ba76 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -331,7 +331,7 @@ static void drop_duplicates(BindMount *m, unsigned *n) {
/* Drops duplicate entries. Expects that the array is properly ordered already. */
- for (f = m, t = m, previous = NULL; f < m+*n; f++) {
+ for (f = m, t = m, previous = NULL; f < m + *n; f++) {
/* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
* above. */
@@ -359,7 +359,7 @@ static void drop_inaccessible(BindMount *m, unsigned *n) {
/* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
* ordered already. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
/* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
* it, as inaccessible paths really should drop the entire subtree. */
@@ -387,7 +387,7 @@ static void drop_nop(BindMount *m, unsigned *n) {
/* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
* list is ordered by prefixes. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
/* Only suppress such subtrees for READONLY and READWRITE entries */
if (IN_SET(f->mode, READONLY, READWRITE)) {
@@ -423,12 +423,13 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned
assert(m);
assert(n);
+ /* Nothing to do */
if (!root_directory)
return;
/* Drops all mounts that are outside of the root directory. */
- for (f = m, t = m; f < m+*n; f++) {
+ for (f = m, t = m; f < m + *n; f++) {
if (!path_startswith(f->path, root_directory)) {
log_debug("%s is outside of root directory.", f->path);
@@ -671,9 +672,10 @@ static int make_read_only(BindMount *m, char **blacklist) {
return r;
}
+/* Chase symlinks and remove failed paths from mounts */
static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) {
BindMount *f, *t;
- int r;
+ int r = 0;
assert(m);
assert(n);
@@ -683,22 +685,24 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
* can't resolve the path, and which have been marked for such removal. */
for (f = m, t = m; f < m + *n; f++) {
+ int k;
_cleanup_free_ char *chased = NULL;
- r = chase_symlinks(f->path, root_directory, &chased);
- if (r == -ENOENT && f->ignore) {
- /* Doesn't exist? Then remove it! */
+ k = chase_symlinks(f->path, root_directory, &chased);
+ if (k < 0) {
+ /* Get only real errors */
+ if (r >= 0 && (k != -ENOENT || !f->ignore))
+ r = k;
+
+ log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path);
+ /* Doesn't exist or failed? Then remove it and continue! */
f->path = mfree(f->path);
continue;
}
- if (r < 0)
- return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path);
if (!path_equal(f->path, chased)) {
log_debug("Chased %s → %s", f->path, chased);
- r = free_and_replace(f->path, chased);
- if (r < 0)
- return r;
+ free_and_replace(f->path, chased);
}
*t = *f;
@@ -706,7 +710,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned
}
*n = t - m;
- return 0;
+ return r;
}
static unsigned namespace_calculate_mounts(
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index 99f690897d..5aa8aca426 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -32,7 +32,8 @@
int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
- size_t mac_addr_len, uint16_t arp_type);
+ size_t mac_addr_len, uint16_t arp_type,
+ uint16_t port);
int dhcp_network_bind_udp_socket(be32_t address, uint16_t port);
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len);
@@ -57,7 +58,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
uint16_t source, be32_t destination_addr,
uint16_t destination, uint16_t len);
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum);
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
* client from the callback doesn't destroy the object we are working
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index a9f5a0a5de..3c85bb0b54 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -36,7 +36,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
size_t mac_addr_len,
const uint8_t *bcast_addr,
const struct ether_addr *eth_mac,
- uint16_t arp_type, uint8_t dhcp_hlen) {
+ uint16_t arp_type, uint8_t dhcp_hlen,
+ uint16_t port) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
@@ -53,7 +54,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */
@@ -125,7 +126,8 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
uint32_t xid, const uint8_t *mac_addr,
- size_t mac_addr_len, uint16_t arp_type) {
+ size_t mac_addr_len, uint16_t arp_type,
+ uint16_t port) {
static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* Default broadcast address for IPoIB */
static const uint8_t ib_bcast[] = {
@@ -151,7 +153,7 @@ int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
return -EINVAL;
return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len,
- bcast_addr, &eth_mac, arp_type, dhcp_hlen);
+ bcast_addr, &eth_mac, arp_type, dhcp_hlen, port);
}
int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) {
diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c
index 8be774061d..40442b3636 100644
--- a/src/libsystemd-network/dhcp-packet.c
+++ b/src/libsystemd-network/dhcp-packet.c
@@ -114,7 +114,7 @@ void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE);
}
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
+int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port) {
size_t hdrlen;
assert(packet);
@@ -160,10 +160,10 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) {
return -EINVAL;
}
- if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) {
+ if (be16toh(packet->udp.dest) != port) {
log_debug("ignoring packet: to port %u, which "
"is not the DHCP client port (%u)",
- be16toh(packet->udp.dest), DHCP_PORT_CLIENT);
+ be16toh(packet->udp.dest), port);
return -EINVAL;
}
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 5ccb23922c..6475da2c2a 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -55,6 +55,7 @@ struct sd_dhcp_client {
sd_event_source *timeout_resend;
int ifindex;
int fd;
+ uint16_t port;
union sockaddr_union link;
sd_event_source *receive_message;
bool request_broadcast;
@@ -426,6 +427,17 @@ int sd_dhcp_client_set_vendor_class_identifier(
return 0;
}
+int sd_dhcp_client_set_client_port(
+ sd_dhcp_client *client,
+ uint16_t port) {
+
+ assert_return(client, -EINVAL);
+
+ client->port = port;
+
+ return 0;
+}
+
int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
assert_return(client, -EINVAL);
assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
@@ -668,7 +680,7 @@ static int dhcp_client_send_raw(
DHCPPacket *packet,
size_t len) {
- dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
+ dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
INADDR_BROADCAST, DHCP_PORT_SERVER, len);
return dhcp_network_send_raw_socket(client->fd, &client->link,
@@ -1120,7 +1132,7 @@ static int client_start_delayed(sd_dhcp_client *client) {
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
- client->mac_addr_len, client->arp_type);
+ client->mac_addr_len, client->arp_type, client->port);
if (r < 0) {
client_stop(client, r);
return r;
@@ -1170,7 +1182,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
client->xid, client->mac_addr,
- client->mac_addr_len, client->arp_type);
+ client->mac_addr_len, client->arp_type,
+ client->port);
if (r < 0) {
client_stop(client, r);
return 0;
@@ -1555,8 +1568,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
goto error;
}
- r = dhcp_network_bind_udp_socket(client->lease->address,
- DHCP_PORT_CLIENT);
+ r = dhcp_network_bind_udp_socket(client->lease->address, client->port);
if (r < 0) {
log_dhcp_client(client, "could not bind UDP socket");
goto error;
@@ -1766,7 +1778,7 @@ static int client_receive_message_raw(
}
}
- r = dhcp_packet_verify_headers(packet, len, checksum);
+ r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
if (r < 0)
return 0;
@@ -1891,6 +1903,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
client->fd = -1;
client->attempt = 1;
client->mtu = DHCP_DEFAULT_MIN_SIZE;
+ client->port = DHCP_PORT_CLIENT;
client->req_opts_size = ELEMENTSOF(default_req_opts);
client->req_opts = memdup(default_req_opts, client->req_opts_size);
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 2a101cb1fe..c10ca74b86 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -195,7 +195,7 @@ int dhcp_network_bind_raw_socket(
union sockaddr_union *link,
uint32_t id,
const uint8_t *addr, size_t addr_len,
- uint16_t arp_type) {
+ uint16_t arp_type, uint16_t port) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
return -errno;
diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c
index 09cbd9ab44..a05794941f 100644
--- a/src/libsystemd/sd-bus/busctl-introspect.c
+++ b/src/libsystemd/sd-bus/busctl-introspect.c
@@ -143,9 +143,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
case STATE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
- free(field);
- field = name;
- name = NULL;
+ free_and_replace(field, name);
state = STATE_ANNOTATION;
} else {
@@ -158,9 +156,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
case STATE_VALUE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(value);
- value = name;
- name = NULL;
+ free_and_replace(value, name);
state = STATE_ANNOTATION;
} else {
@@ -194,6 +190,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
STATE_SIGNAL_ARG,
STATE_SIGNAL_ARG_NAME,
STATE_SIGNAL_ARG_TYPE,
+ STATE_SIGNAL_ARG_DIRECTION,
STATE_PROPERTY,
STATE_PROPERTY_NAME,
STATE_PROPERTY_TYPE,
@@ -350,11 +347,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_INTERFACE_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
- if (n_depth == 0) {
- free(context->interface_name);
- context->interface_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->interface_name, name);
state = STATE_INTERFACE;
} else {
@@ -409,12 +403,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
state = STATE_METHOD;
} else {
@@ -432,7 +422,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
else if (streq_ptr(name, "type"))
state = STATE_METHOD_ARG_TYPE;
else if (streq_ptr(name, "direction"))
- state = STATE_METHOD_ARG_DIRECTION;
+ state = STATE_METHOD_ARG_DIRECTION;
else {
log_error("Unexpected method <arg> attribute %s.", name);
return -EBADMSG;
@@ -458,7 +448,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
} else if (streq(argument_direction, "out")) {
if (!strextend(&context->member_result, argument_type, NULL))
return log_oom();
- }
+ } else
+ log_error("Unexpected method <arg> direction value '%s'.", argument_direction);
}
argument_type = mfree(argument_type);
@@ -487,9 +478,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_type);
- argument_type = name;
- name = NULL;
+ free_and_replace(argument_type, name);
state = STATE_METHOD_ARG;
} else {
@@ -502,9 +491,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_METHOD_ARG_DIRECTION:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_direction);
- argument_direction = name;
- name = NULL;
+ free_and_replace(argument_direction, name);
state = STATE_METHOD_ARG;
} else {
@@ -559,12 +546,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_SIGNAL_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
state = STATE_SIGNAL;
} else {
@@ -582,6 +565,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
state = STATE_SIGNAL_ARG_NAME;
else if (streq_ptr(name, "type"))
state = STATE_SIGNAL_ARG_TYPE;
+ else if (streq_ptr(name, "direction"))
+ state = STATE_SIGNAL_ARG_DIRECTION;
else {
log_error("Unexpected signal <arg> attribute %s.", name);
return -EBADMSG;
@@ -599,8 +584,11 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
(t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) {
if (argument_type) {
- if (!strextend(&context->member_signature, argument_type, NULL))
- return log_oom();
+ if (!argument_direction || streq(argument_direction, "out")) {
+ if (!strextend(&context->member_signature, argument_type, NULL))
+ return log_oom();
+ } else
+ log_error("Unexpected signal <arg> direction value '%s'.", argument_direction);
argument_type = mfree(argument_type);
}
@@ -627,9 +615,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_SIGNAL_ARG_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
- free(argument_type);
- argument_type = name;
- name = NULL;
+ free_and_replace(argument_type, name);
state = STATE_SIGNAL_ARG;
} else {
@@ -639,6 +625,19 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
break;
+ case STATE_SIGNAL_ARG_DIRECTION:
+
+ if (t == XML_ATTRIBUTE_VALUE) {
+ free_and_replace(argument_direction, name);
+
+ state = STATE_SIGNAL_ARG;
+ } else {
+ log_error("Unexpected token in signal <arg>. (4)");
+ return -EINVAL;
+ }
+
+ break;
+
case STATE_PROPERTY:
if (t == XML_ATTRIBUTE_NAME) {
@@ -688,12 +687,9 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_PROPERTY_NAME:
if (t == XML_ATTRIBUTE_VALUE) {
+ if (n_depth == 0)
+ free_and_replace(context->member_name, name);
- if (n_depth == 0) {
- free(context->member_name);
- context->member_name = name;
- name = NULL;
- }
state = STATE_PROPERTY;
} else {
log_error("Unexpected token in <property>. (2)");
@@ -705,12 +701,8 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
case STATE_PROPERTY_TYPE:
if (t == XML_ATTRIBUTE_VALUE) {
-
- if (n_depth == 0) {
- free(context->member_signature);
- context->member_signature = name;
- name = NULL;
- }
+ if (n_depth == 0)
+ free_and_replace(context->member_signature, name);
state = STATE_PROPERTY;
} else {
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 76d3d132ea..3feb158642 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -630,6 +630,12 @@ int dhcp4_configure(Link *link) {
return r;
}
+ if (link->network->dhcp_client_port) {
+ r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
+ if (r < 0)
+ return r;
+ }
+
switch (link->network->dhcp_client_identifier) {
case DHCP_CLIENT_ID_DUID: {
/* If configured, apply user specified DUID and/or IAID */
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index bcf8186c33..814f435fc1 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -100,6 +100,7 @@ DHCP.RouteMetric, config_parse_unsigned,
DHCP.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, dhcp_route_table)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
+DHCP.ListenPort, config_parse_uint32, 0, offsetof(Network, dhcp_client_port)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_dhcp_route_table, 0, offsetof(Network, ipv6_accept_ra_route_table)
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 42fc82d392..4df8c37979 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -124,6 +124,7 @@ struct Network {
bool dhcp_use_timezone;
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
+ uint32_t dhcp_client_port;
/* DHCP Server Support */
bool dhcp_server;
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 801014caf5..13f08f8a6c 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -60,7 +60,7 @@ int manager_read_resolv_conf(Manager *m) {
return 0;
/* Is it symlinked to our own file? */
- if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
+ if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 &&
st.st_dev == own.st_dev &&
st.st_ino == own.st_ino)
return 0;
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index deb75f9ae5..8d5a5c6b79 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -112,6 +112,10 @@ int main(int argc, char *argv[]) {
sd_event_get_exit_code(m->event, &r);
finish:
+ /* systemd-nspawn checks for private resov.conf to decide whether
+ or not to mount it into the container. So just delete it. */
+ (void) unlink(PRIVATE_RESOLV_CONF);
+
sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 9a90c2ed42..ffe7f836de 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -126,6 +126,9 @@ int sd_dhcp_client_get_client_id(
int sd_dhcp_client_set_mtu(
sd_dhcp_client *client,
uint32_t mtu);
+int sd_dhcp_client_set_client_port(
+ sd_dhcp_client *client,
+ uint16_t port);
int sd_dhcp_client_set_hostname(
sd_dhcp_client *client,
const char *hostname);
diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c
index 708a665576..d7edbb396b 100644
--- a/src/udev/net/ethtool-util.c
+++ b/src/udev/net/ethtool-util.c
@@ -29,6 +29,7 @@
#include "string-table.h"
#include "strxcpyx.h"
#include "util.h"
+#include "missing.h"
static const char* const duplex_table[_DUP_MAX] = {
[DUP_FULL] = "full",
@@ -323,3 +324,211 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) {
return 0;
}
+
+static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) {
+ struct ecmd {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd = {
+ .req.cmd = ETHTOOL_GLINKSETTINGS,
+ };
+ struct ethtool_link_usettings *u;
+ unsigned offset;
+ int r;
+
+ /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
+ handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
+ agree with user, it returns the bitmap length it is expecting from user as a negative
+ length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
+ all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
+ https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
+ */
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return -ENOTSUP;
+
+ ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return -ENOTSUP;
+
+ u = new0(struct ethtool_link_usettings , 1);
+ if (!u)
+ return -ENOMEM;
+
+ memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings));
+
+ offset = 0;
+ memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
+
+ *g = u;
+
+ return 0;
+}
+
+static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) {
+ struct ethtool_link_usettings *e;
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_GSET,
+ };
+ int r;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ e = new0(struct ethtool_link_usettings, 1);
+ if (!e)
+ return -ENOMEM;
+
+ e->base.cmd = ETHTOOL_GSET;
+
+ e->base.link_mode_masks_nwords = 1;
+ e->base.speed = ethtool_cmd_speed(&ecmd);
+ e->base.duplex = ecmd.duplex;
+ e->base.port = ecmd.port;
+ e->base.phy_address = ecmd.phy_address;
+ e->base.autoneg = ecmd.autoneg;
+ e->base.mdio_support = ecmd.mdio_support;
+
+ e->link_modes.supported[0] = ecmd.supported;
+ e->link_modes.advertising[0] = ecmd.advertising;
+ e->link_modes.lp_advertising[0] = ecmd.lp_advertising;
+
+ *u = e;
+
+ return 0;
+}
+
+static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+ struct {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd = {
+ .req.cmd = ETHTOOL_SLINKSETTINGS,
+ };
+ unsigned int offset;
+ int r;
+
+ if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
+ return -EINVAL;
+
+ offset = 0;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
+
+ offset += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_SSET,
+ };
+ int r;
+
+ if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
+ return -EINVAL;
+
+ ecmd.supported = u->link_modes.supported[0];
+ ecmd.advertising = u->link_modes.advertising[0];
+ ecmd.lp_advertising = u->link_modes.lp_advertising[0];
+
+ ethtool_cmd_speed_set(&ecmd, u->base.speed);
+
+ ecmd.duplex = u->base.duplex;
+ ecmd.port = u->base.port;
+ ecmd.phy_address = u->base.phy_address;
+ ecmd.autoneg = u->base.autoneg;
+ ecmd.mdio_support = u->base.mdio_support;
+
+ ifr->ifr_data = (void *) &ecmd;
+
+ r = ioctl(*fd, SIOCETHTOOL, ifr);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+/* If autonegotiation is disabled, the speed and duplex represent the fixed link
+ * mode and are writable if the driver supports multiple link modes. If it is
+ * enabled then they are read-only. If the link is up they represent the negotiated
+ * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
+ * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
+ */
+
+int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autonegotiation) {
+ _cleanup_free_ struct ethtool_link_usettings *u = NULL;
+ struct ifreq ifr = {};
+ int r;
+
+ if (autonegotiation != 0) {
+ log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.");
+ return 0;
+ }
+
+ if (*fd < 0) {
+ r = ethtool_connect(fd);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ }
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = get_glinksettings(fd, &ifr, &u);
+ if (r < 0) {
+
+ r = get_gset(fd, &ifr, &u);
+ if (r < 0)
+ return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname);
+ }
+
+ if (speed)
+ u->base.speed = speed;
+
+ if (duplex != _DUP_INVALID)
+ u->base.duplex = duplex;
+
+ u->base.autoneg = autonegotiation;
+
+ if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
+ r = set_slinksettings(fd, &ifr, u);
+ else
+ r = set_sset(fd, &ifr, u);
+
+ if (r < 0)
+ return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname);
+
+ return r;
+}
diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h
index 0744164653..75d6af396b 100644
--- a/src/udev/net/ethtool-util.h
+++ b/src/udev/net/ethtool-util.h
@@ -20,6 +20,9 @@
***/
#include <macro.h>
+#include <linux/ethtool.h>
+
+#include "missing.h"
/* we can't use DUPLEX_ prefix, as it
* clashes with <linux/ethtool.h> */
@@ -48,12 +51,27 @@ typedef enum NetDevFeature {
_NET_DEV_FEAT_INVALID = -1
} NetDevFeature;
+
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
+
+/* layout of the struct passed from/to userland */
+struct ethtool_link_usettings {
+ struct ethtool_link_settings base;
+
+ struct {
+ uint32_t supported[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ uint32_t lp_advertising[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } link_modes;
+};
+
int ethtool_connect(int *ret);
int ethtool_get_driver(int *fd, const char *ifname, char **ret);
int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex);
int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features);
+int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autoneg);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index f8b85cbd13..78e551df22 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -34,6 +34,7 @@ Link.Alias, config_parse_ifalias, 0,
Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu)
Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
+Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation)
Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO])
Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO])
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index ece9248c2a..4578d0d9b2 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -167,6 +167,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
link->mac_policy = _MACPOLICY_INVALID;
link->wol = _WOL_INVALID;
link->duplex = _DUP_INVALID;
+ link->autonegotiation = -1;
memset(&link->features, -1, _NET_DEV_FEAT_MAX);
@@ -202,9 +203,9 @@ static bool enable_name_policy(void) {
}
int link_config_load(link_config_ctx *ctx) {
- int r;
_cleanup_strv_free_ char **files;
char **f;
+ int r;
link_configs_free(ctx);
@@ -364,11 +365,12 @@ static int get_mac(struct udev_device *device, bool want_random,
int link_config_apply(link_config_ctx *ctx, link_config *config,
struct udev_device *device, const char **name) {
- const char *old_name;
- const char *new_name = NULL;
+ bool respect_predictable = false;
struct ether_addr generated_mac;
struct ether_addr *mac = NULL;
- bool respect_predictable = false;
+ const char *new_name = NULL;
+ const char *old_name;
+ unsigned speed;
int r, ifindex;
assert(ctx);
@@ -380,11 +382,19 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (!old_name)
return -EINVAL;
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
- if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m",
- old_name, config->speed / 1024,
- duplex_to_string(config->duplex));
+
+ speed = DIV_ROUND_UP(config->speed, 1000000);
+
+ r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, speed, config->duplex, config->autonegotiation);
+ if (r < 0) {
+
+ if (r == -EOPNOTSUPP)
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex);
+
+ if (r < 0)
+ log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
+ old_name, speed, duplex_to_string(config->duplex));
+ }
r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol);
if (r < 0)
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 91cc0357c4..a99060d943 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -69,6 +69,7 @@ struct link_config {
size_t mtu;
size_t speed;
Duplex duplex;
+ int autonegotiation;
WakeOnLan wol;
NetDevFeature features[_NET_DEV_FEAT_MAX];