</varlistentry>
<varlistentry>
- <term><varname>MultiPathRoute=<replaceable>address</replaceable>[@<replaceable>name</replaceable>] [<replaceable>weight</replaceable>]</varname></term>
+ <term><varname>MultiPathRoute=</varname></term>
<listitem>
<para>Configures multipath route. Multipath routing is the technique of using multiple
- alternative paths through a network. Takes gateway address. Optionally, takes a network
- interface name or index separated with <literal>@</literal>, and a weight in 1..256 for this
- multipath route separated with whitespace. This setting can be specified multiple times. If
- an empty string is assigned, then the all previous assignments are cleared.</para>
+ alternative paths through a network. Takes a gateway address and/or a network interface
+ name or index (prefixed with <literal>@</literal>). At least one of these must be specified.
+ Optionally, a weight in 1..256 can be specified, separated with whitespace. This setting
+ can be specified multiple times. If an empty string is assigned, then all previous
+ assignments are cleared.</para>
+
+ <para>Examples:</para>
+ <programlisting>MultiPathRoute=10.0.0.1@eth0 20
+MultiPathRoute=192.168.1.1 50
+MultiPathRoute=@wg0 15
+MultiPathRoute=2001:db8::1@eth0</programlisting>
<xi:include href="version-info.xml" xpointer="v245"/>
</listitem>
/* See nh_comp() in net/ipv4/fib_semantics.c of the kernel. */
siphash24_compress_typesafe(nh->family, state);
- if (!IN_SET(nh->family, AF_INET, AF_INET6))
- return;
- in_addr_hash_func(&nh->gw, nh->family, state);
+ /* For device-only nexthops parsed from config, family is AF_UNSPEC until verification.
+ * We still need to hash weight/ifindex/ifname to distinguish different device-only entries. */
+ if (IN_SET(nh->family, AF_INET, AF_INET6))
+ in_addr_hash_func(&nh->gw, nh->family, state);
+
if (with_weight)
siphash24_compress_typesafe(nh->weight, state);
siphash24_compress_typesafe(nh->ifindex, state);
if (r != 0)
return r;
- if (!IN_SET(a->family, AF_INET, AF_INET6))
- return 0;
-
- r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
- if (r != 0)
- return r;
+ /* For device-only nexthops parsed from config, family is AF_UNSPEC until verification.
+ * We still need to compare weight/ifindex/ifname to distinguish different device-only entries. */
+ if (IN_SET(a->family, AF_INET, AF_INET6)) {
+ r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+ }
if (with_weight) {
r = CMP(a->weight, b->weight);
(*rta)->rta_len += sizeof(struct rtnexthop);
- if (nh->family == route->family) {
- r = rtattr_append_attribute(rta, RTA_GATEWAY, &nh->gw, FAMILY_ADDRESS_SIZE(nh->family));
- if (r < 0)
- goto clear;
+ /* For device-only nexthops, skip RTA_GATEWAY entirely. The kernel will use the
+ * interface specified in rtnh_ifindex without requiring a gateway address. */
+ if (in_addr_is_set(nh->family, &nh->gw)) {
+ if (nh->family == route->family) {
+ r = rtattr_append_attribute(rta, RTA_GATEWAY, &nh->gw, FAMILY_ADDRESS_SIZE(nh->family));
+ if (r < 0)
+ goto clear;
- rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
- rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(nh->family));
+ rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+ rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(nh->family));
- } else if (nh->family == AF_INET6) {
- assert(route->family == AF_INET);
+ } else if (nh->family == AF_INET6) {
+ assert(route->family == AF_INET);
- r = rtattr_append_attribute(rta, RTA_VIA,
- &(RouteVia) {
- .family = nh->family,
- .address = nh->gw,
- }, sizeof(RouteVia));
- if (r < 0)
- goto clear;
+ r = rtattr_append_attribute(rta, RTA_VIA,
+ &(RouteVia) {
+ .family = nh->family,
+ .address = nh->gw,
+ }, sizeof(RouteVia));
+ if (r < 0)
+ goto clear;
- rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
- rtnh->rtnh_len += RTA_SPACE(sizeof(RouteVia));
+ rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
+ rtnh->rtnh_len += RTA_SPACE(sizeof(RouteVia));
- } else if (nh->family == AF_INET)
- assert_not_reached();
+ } else if (nh->family == AF_INET)
+ assert_not_reached();
+ }
return 0;
}
}
- r = in_addr_from_string_auto(word, &nh->family, &nh->gw);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
- return 0;
+ if (isempty(word)) {
+ if (!dev)
+ return log_syntax_parse_error(unit, filename, line, SYNTHETIC_ERRNO(EINVAL), lvalue, rvalue);
+ } else {
+ r = in_addr_from_string_auto(word, &nh->family, &nh->gw);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
}
if (!isempty(p)) {