summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/systemd.network.xml7
-rw-r--r--src/libsystemd/sd-rtnl/rtnl-message.c18
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-route.c49
-rw-r--r--src/network/networkd.h2
-rw-r--r--src/systemd/sd-rtnl.h1
6 files changed, 69 insertions, 9 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index c9c946c00a..a913fdfc5e 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -416,6 +416,13 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><varname>Source=</varname></term>
+ <listitem>
+ <para>The source prefix of the route. Possibly followed by a slash and the
+ prefixlength. If omitted, a full-length host route is assumed.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>Metric=</varname></term>
<listitem>
<para>The metric of the route. An unsigned integer</para>
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
index a2f75471b4..4491a00f4f 100644
--- a/src/libsystemd/sd-rtnl/rtnl-message.c
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -114,6 +114,24 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char pr
return 0;
}
+int sd_rtnl_message_route_set_src_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ if ((rtm->rtm_family == AF_INET && prefixlen > 32) ||
+ (rtm->rtm_family == AF_INET6 && prefixlen > 128))
+ return -ERANGE;
+
+ rtm->rtm_src_len = prefixlen;
+
+ return 0;
+}
+
int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) {
struct rtmsg *rtm;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 1aef09007a..4f0e05aeb6 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -47,6 +47,7 @@ Address.Broadcast, config_parse_broadcast, 0,
Address.Label, config_parse_label, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
+Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 25db676f05..4e389db5ef 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -138,6 +138,19 @@ int route_drop(Route *route, Link *link,
return log_error_errno(r, "Could not set destination prefix length: %m");
}
+ if (route->src_prefixlen) {
+ if (route->family == AF_INET)
+ r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ else if (route->family == AF_INET6)
+ r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTA_DST attribute: %m");
+
+ r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
+ if (r < 0)
+ return log_error_errno(r, "Could not set source prefix length: %m");
+ }
+
if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
if (route->family == AF_INET)
r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
@@ -207,6 +220,19 @@ int route_configure(Route *route, Link *link,
return log_error_errno(r, "Could not set destination prefix length: %m");
}
+ if (route->src_prefixlen) {
+ if (route->family == AF_INET)
+ r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+ else if (route->family == AF_INET6)
+ r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
+
+ r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
+ if (r < 0)
+ return log_error_errno(r, "Could not set source prefix length: %m");
+ }
+
if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
if (route->family == AF_INET)
r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
@@ -298,6 +324,7 @@ int config_parse_destination(const char *unit,
_cleanup_route_free_ Route *n = NULL;
const char *address, *e;
union in_addr_union buffer;
+ unsigned char prefixlen;
int r, f;
assert(filename);
@@ -310,7 +337,7 @@ int config_parse_destination(const char *unit,
if (r < 0)
return r;
- /* Destination=address/prefixlen */
+ /* Destination|Source=address/prefixlen */
/* address */
e = strchr(rvalue, '/');
@@ -328,29 +355,33 @@ int config_parse_destination(const char *unit,
/* prefixlen */
if (e) {
- unsigned i;
-
- r = safe_atou(e + 1, &i);
+ r = safe_atou8(e + 1, &prefixlen);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
return 0;
}
-
- n->dst_prefixlen = (unsigned char) i;
} else {
switch (n->family) {
case AF_INET:
- n->dst_prefixlen = 32;
+ prefixlen = 32;
break;
case AF_INET6:
- n->dst_prefixlen = 128;
+ prefixlen = 128;
break;
}
}
n->family = f;
- n->dst_addr = buffer;
+ if (streq(lvalue, "Destination")) {
+ n->dst_addr = buffer;
+ n->dst_prefixlen = prefixlen;
+ } else if (streq(lvalue, "Source")) {
+ n->src_addr = buffer;
+ n->src_prefixlen = prefixlen;
+ } else
+ assert_not_reached(lvalue);
+
n = NULL;
return 0;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 1297ef98a7..07917f0ef4 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -147,12 +147,14 @@ struct Route {
int family;
unsigned char dst_prefixlen;
+ unsigned char src_prefixlen;
unsigned char scope;
uint32_t metrics;
unsigned char protocol; /* RTPROT_* */
union in_addr_union in_addr;
union in_addr_union dst_addr;
+ union in_addr_union src_addr;
union in_addr_union prefsrc_addr;
LIST_FIELDS(Route, routes);
diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h
index bf1dde4008..4754d29157 100644
--- a/src/systemd/sd-rtnl.h
+++ b/src/systemd/sd-rtnl.h
@@ -102,6 +102,7 @@ int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags);
int sd_rtnl_message_link_get_type(sd_rtnl_message *m, unsigned *type);
int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen);
+int sd_rtnl_message_route_set_src_prefixlen(sd_rtnl_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope);
int sd_rtnl_message_route_get_family(sd_rtnl_message *m, int *family);
int sd_rtnl_message_route_get_dst_len(sd_rtnl_message *m, unsigned char *dst_len);