From 9e7e440835c43d81ffdbc299d2c07daaa641ed50 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Thu, 4 Dec 2014 15:52:21 +0100 Subject: [PATCH] networkd: add support for source routing --- man/systemd.network.xml | 7 ++++ src/libsystemd/sd-rtnl/rtnl-message.c | 18 +++++++++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-route.c | 49 +++++++++++++++++++----- src/network/networkd.h | 2 + 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 c9c946c00..a913fdfc5 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -415,6 +415,13 @@ prefixlength. If omitted, a full-length host route is assumed. + + Source= + + The source prefix of the route. Possibly followed by a slash and the + prefixlength. If omitted, a full-length host route is assumed. + + Metric= diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index a2f75471b..4491a00f4 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 1aef09007..4f0e05aeb 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 25db676f0..4e389db5e 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 1297ef98a..07917f0ef 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 bf1dde400..4754d2915 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); -- 2.30.2