if <literal>RequiredForOnline=no</literal>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ActivationPolicy=</varname></term>
+ <listitem>
+ <para>Specifies the policy for <command>systemd-networkd</command> managing the link
+ administrative state. Specifically, this controls how <command>systemd-networkd</command>
+ changes the network device's <literal>IFF_UP</literal> flag, which is sometimes
+ controlled by system administrators by running e.g., <command>ip set dev eth0 up</command>
+ or <command>ip set dev eth0 down</command>, and can also be changed with
+ <command>networkctl up eth0</command> or <command>networkctl down eth0</command>.</para>
+
+ <para>Takes one of <literal>up</literal>, <literal>always-up</literal>,
+ <literal>manual</literal>, <literal>always-down</literal>, <literal>down</literal>,
+ or <literal>bound</literal>. When <literal>manual</literal>, <command>systemd-networkd</command>
+ will not change the link's admin state automatically; the system administrator must bring the
+ interface up or down manually, as desired. When <literal>up</literal> (the default) or
+ <literal>always-up</literal>, or <literal>down</literal> or <literal>always-down</literal>,
+ <command>systemd-networkd</command> will set the link up or down, respectively,
+ when the interface is (re)configured. When <literal>always-up</literal> or
+ <literal>always-down</literal>, <command>systemd-networkd</command> will set the link up
+ or down, respectively, any time <command>systemd-networkd</command> detects a change in
+ the administrative state. When <varname>BindCarrier=</varname> is also set, this is
+ automatically set to <literal>bound</literal> and any other value is ignored.</para>
+
+ <para>The administrative state is not the same as the carrier state, so using
+ <literal>always-up</literal> does not mean the link will never lose carrier. The link
+ carrier depends on both the administrative state as well as the network device's physical
+ connection. However, to avoid reconfiguration failures, when using <literal>always-up</literal>,
+ <varname>IgnoreCarrierLoss=</varname> is forced to true.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<listitem>
<para>A link name or a list of link names. When set, controls the behavior of the current
link. When all links in the list are in an operational down state, the current link is brought
- down. When at least one link has carrier, the current interface is brought up.
- </para>
+ down. When at least one link has carrier, the current interface is brought up.</para>
+
+ <para>This forces <varname>ActivationPolicy=</varname> to be set to <literal>bound</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
of the interface even if its carrier is lost. When unset, the value specified with
<option>ConfigureWithoutCarrier=</option> is used.
</para>
+
+ <para>When <varname>ActivationPolicy=</varname> is set to <literal>always-up</literal>, this
+ is forced to <literal>true</literal>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
assert(link);
assert(link->network);
- if (!hashmap_isempty(link->bound_to_links)) {
+ switch (link->network->activation_policy) {
+ case ACTIVATION_POLICY_BOUND:
r = link_handle_bound_to_list(link);
if (r < 0)
return r;
- } else if (!(link->flags & IFF_UP)) {
+ break;
+ case ACTIVATION_POLICY_UP:
+ if (link->activated)
+ break;
+ _fallthrough_;
+ case ACTIVATION_POLICY_ALWAYS_UP:
r = link_up(link);
if (r < 0) {
link_enter_failed(link);
return r;
}
+ break;
+ case ACTIVATION_POLICY_DOWN:
+ if (link->activated)
+ break;
+ _fallthrough_;
+ case ACTIVATION_POLICY_ALWAYS_DOWN:
+ r = link_down(link, NULL);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
+ }
+ break;
+ default:
+ break;
}
+ link->activated = true;
if (link->network->bridge) {
r = link_set_bridge(link);
return r;
link_set_state(link, LINK_STATE_INITIALIZED);
+ link->activated = false;
link_dirty(link);
/* link_configure_duid() returns 0 if it requests product UUID. In that case,
static int link_admin_state_up(Link *link) {
int r;
+ assert(link);
+
+ if (!link->network)
+ return 0;
+
+ if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) {
+ log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down");
+ return link_down(link, NULL);
+ }
+
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. The check for
* ipv6_mtu_set prevents this from trying to set it too early before
return 0;
}
+static int link_admin_state_down(Link *link) {
+
+ assert(link);
+
+ if (!link->network)
+ return 0;
+
+ if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
+ log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up");
+ return link_up(link);
+ }
+
+ return 0;
+}
+
int link_update(Link *link, sd_netlink_message *m) {
_cleanup_strv_free_ char **s = NULL;
hw_addr_data hw_addr;
r = link_admin_state_up(link);
if (r < 0)
return r;
- } else if (link_was_admin_up && !(link->flags & IFF_UP))
+ } else if (link_was_admin_up && !(link->flags & IFF_UP)) {
log_link_info(link, "Link DOWN");
+ r = link_admin_state_down(link);
+ if (r < 0)
+ return r;
+ }
+
r = link_update_lldp(link);
if (r < 0)
return r;
bool setting_genmode:1;
bool ipv6_mtu_set:1;
bool bridge_mdb_configured:1;
+ bool activated:1;
sd_dhcp_server *dhcp_server;
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Promiscuous, config_parse_tristate, 0, offsetof(Network, promiscuous)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
+Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0
if (network->dhcp_use_gateway < 0)
network->dhcp_use_gateway = network->dhcp_use_routes;
- if (network->ignore_carrier_loss < 0)
- network->ignore_carrier_loss = network->configure_without_carrier;
-
if (network->dhcp_critical >= 0) {
if (network->keep_configuration >= 0)
log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
network->keep_configuration = KEEP_CONFIGURATION_NO;
}
+ if (!strv_isempty(network->bind_carrier)) {
+ if (!IN_SET(network->activation_policy, _ACTIVATION_POLICY_INVALID, ACTIVATION_POLICY_BOUND))
+ log_warning("%s: ActivationPolicy=bound is required with BindCarrier=. "
+ "Setting ActivationPolicy=bound.", network->filename);
+ network->activation_policy = ACTIVATION_POLICY_BOUND;
+ } else if (network->activation_policy == ACTIVATION_POLICY_BOUND) {
+ log_warning("%s: ActivationPolicy=bound requires BindCarrier=. "
+ "Ignoring ActivationPolicy=bound.", network->filename);
+ network->activation_policy = ACTIVATION_POLICY_UP;
+ }
+
+ if (network->activation_policy == _ACTIVATION_POLICY_INVALID)
+ network->activation_policy = ACTIVATION_POLICY_UP;
+
+ if (network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
+ if (network->ignore_carrier_loss == false)
+ log_warning("%s: IgnoreCarrierLoss=false conflicts with ActivationPolicy=always-up. "
+ "Setting IgnoreCarrierLoss=true.", network->filename);
+ network->ignore_carrier_loss = true;
+ }
+
+ if (network->ignore_carrier_loss < 0)
+ network->ignore_carrier_loss = network->configure_without_carrier;
+
if (network->keep_configuration < 0)
network->keep_configuration = KEEP_CONFIGURATION_NO;
.required_for_online = true,
.required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
+ .activation_policy = _ACTIVATION_POLICY_INVALID,
.arp = -1,
.multicast = -1,
.allmulticast = -1,
DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");
+
+static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = {
+ [ACTIVATION_POLICY_UP] = "up",
+ [ACTIVATION_POLICY_ALWAYS_UP] = "always-up",
+ [ACTIVATION_POLICY_MANUAL] = "manual",
+ [ACTIVATION_POLICY_ALWAYS_DOWN] = "always-down",
+ [ACTIVATION_POLICY_DOWN] = "down",
+ [ACTIVATION_POLICY_BOUND] = "bound",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(activation_policy, ActivationPolicy);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_activation_policy, activation_policy, ActivationPolicy, "Failed to parse activation policy");
_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -1
} IPv6LinkLocalAddressGenMode;
+typedef enum ActivationPolicy {
+ ACTIVATION_POLICY_UP,
+ ACTIVATION_POLICY_ALWAYS_UP,
+ ACTIVATION_POLICY_MANUAL,
+ ACTIVATION_POLICY_ALWAYS_DOWN,
+ ACTIVATION_POLICY_DOWN,
+ ACTIVATION_POLICY_BOUND,
+ _ACTIVATION_POLICY_MAX,
+ _ACTIVATION_POLICY_INVALID = -1
+} ActivationPolicy;
+
typedef struct Manager Manager;
typedef struct NetworkDHCPServerEmitAddress {
bool unmanaged;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalStateRange required_operstate_for_online;
+ ActivationPolicy activation_policy;
/* misc settings */
bool configure_without_carrier;
CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues);
+CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_;
IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_;
+
+const char* activation_policy_to_string(ActivationPolicy i) _const_;
+ActivationPolicy activation_policy_from_string(const char *s) _pure_;
MACAddress=
PermanentMACAddress=
[Link]
+ActivationPolicy=
RequiredForOnline=
ARP=
AllMulticast=