diff options
-rw-r--r-- | man/systemd.network.xml | 7 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-message.c | 18 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-route.c | 49 | ||||
-rw-r--r-- | src/network/networkd.h | 2 | ||||
-rw-r--r-- | src/systemd/sd-rtnl.h | 1 |
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); |