From: Tom Gundersen Date: Tue, 19 Nov 2013 15:54:42 +0000 (+0100) Subject: networkd: add support for [Address] sections X-Git-Tag: v209~1304 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=6ae115c1fe95611b39d2f20cfcea3d385429f59e networkd: add support for [Address] sections This will allow specifying more options per address than the simple Address= entry in the [Network] section. Preliminary support for the same functionality for [Route] sections are added, but not yet hooked up, as more testing is needed. --- diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml index f3239bbf9..8bf2d928b 100644 --- a/man/systemd-networkd.service.xml +++ b/man/systemd-networkd.service.xml @@ -162,6 +162,23 @@ . + + + The [Address] section accepts the following keys: + + + + Address + + As in the [Network] section. + + + + Label + + An address label. + + diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 0e582d626..4c321dc45 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -28,9 +28,20 @@ #include "conf-parser.h" #include "net-util.h" -int address_new(Network *network, Address **ret) { +int address_new(Network *network, unsigned section, Address **ret) { _cleanup_address_free_ Address *address = NULL; + if (section) { + uint64_t key = section; + address = hashmap_get(network->addresses_by_section, &key); + if (address) { + *ret = address; + address = NULL; + + return 0; + } + } + address = new0(Address, 1); if (!address) return -ENOMEM; @@ -39,6 +50,11 @@ int address_new(Network *network, Address **ret) { LIST_PREPEND(addresses, network->addresses, address); + if (section) { + address->section = section; + hashmap_put(network->addresses_by_section, &address->section, address); + } + *ret = address; address = NULL; @@ -51,7 +67,10 @@ void address_free(Address *address) { LIST_REMOVE(addresses, address->network->addresses, address); - free(address->label); + if (address->section) + hashmap_remove(address->network->addresses_by_section, + &address->section); + free(address); } @@ -121,17 +140,19 @@ int config_parse_address(const char *unit, const char *rvalue, void *data, void *userdata) { + Network *network = userdata; _cleanup_address_free_ Address *n = NULL; _cleanup_free_ char *address = NULL; const char *e; int r; assert(filename); + assert(section); assert(lvalue); assert(rvalue); assert(data); - r = address_new(userdata, &n); + r = address_new(network, section_line, &n); if (r < 0) return r; @@ -172,3 +193,54 @@ int config_parse_address(const char *unit, return 0; } + +int config_parse_label(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; + _cleanup_free_ char *address = NULL; + char *label; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new(network, section_line, &n); + if (r < 0) + return r; + + label = strdup(rvalue); + if (!label) + return log_oom(); + + if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Interface label is not ASCII clean or is too" + " long, ignoring assignment: %s", rvalue); + free(label); + return 0; + } + + free(n->label); + if (*label) + n->label = label; + else { + free(label); + n->label = NULL; + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index a0ca6aa7a..7fbe4669a 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -23,3 +23,5 @@ Match.Name, config_parse_ifname, 0, offsetof(Networ Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 +Address.Address, config_parse_address, 0, 0 +Address.Label, config_parse_label, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 7be9645d5..dc2af9dd2 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -45,8 +45,21 @@ static int network_load_one(Manager *manager, const char *filename) { network->manager = manager; LIST_HEAD_INIT(network->addresses); + LIST_HEAD_INIT(network->routes); - r = config_parse(NULL, filename, file, "Match\0Network\0", config_item_perf_lookup, + network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!network->addresses_by_section) + return log_oom(); + + network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!network->routes_by_section) + return log_oom(); + + network->filename = strdup(filename); + if (!network->filename) + return log_oom(); + + r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0", config_item_perf_lookup, (void*) network_gperf_lookup, false, false, network); if (r < 0) { log_warning("Could not parse config file %s: %s", filename, strerror(-r)); @@ -54,10 +67,6 @@ static int network_load_one(Manager *manager, const char *filename) { } else log_debug("Parsed configuration file %s", filename); - network->filename = strdup(filename); - if (!network->filename) - return log_oom(); - LIST_PREPEND(networks, manager->networks, network); network = NULL; @@ -121,6 +130,9 @@ void network_free(Network *network) { while ((address = network->addresses)) address_free(address); + hashmap_free(network->addresses_by_section); + hashmap_free(network->routes_by_section); + LIST_REMOVE(networks, network->manager->networks, network); free(network); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 3eca3cc54..b88c62261 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -28,9 +28,21 @@ #include "conf-parser.h" #include "net-util.h" -int route_new(Network *network, Route **ret) { +int route_new(Network *network, unsigned section, Route **ret) { _cleanup_route_free_ Route *route = NULL; + if (section) { + uint64_t key = section; + + route = hashmap_get(network->routes_by_section, &key); + if (route) { + *ret = route; + route = NULL; + + return 0; + } + } + route = new0(Route, 1); if (!route) return -ENOMEM; @@ -39,6 +51,11 @@ int route_new(Network *network, Route **ret) { LIST_PREPEND(routes, network->routes, route); + if (section) { + route->section = section; + hashmap_put(network->routes_by_section, &route->section, route); + } + *ret = route; route = NULL; @@ -51,6 +68,10 @@ void route_free(Route *route) { LIST_REMOVE(routes, route->network->routes, route); + if (route->section) + hashmap_remove(route->network->routes_by_section, + &route->section); + free(route); } @@ -65,9 +86,9 @@ int route_configure(Route *route, Link *link, assert(link->ifindex > 0); assert(route->family == AF_INET || route->family == AF_INET6); - r = sd_rtnl_message_route_new(RTM_NEWROUTE, route->family, 0, 0, 0, - RT_TABLE_MAIN, RT_SCOPE_UNIVERSE, RTPROT_BOOT, - RTN_UNICAST, 0, &req); + r = sd_rtnl_message_route_new(RTM_NEWROUTE, route->family, route->dst_prefixlen, + 0, 0, RT_TABLE_MAIN, RT_SCOPE_UNIVERSE, + RTPROT_BOOT, RTN_UNICAST, 0, &req); if (r < 0) { log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); return r; @@ -79,6 +100,12 @@ int route_configure(Route *route, Link *link, return r; } + r = sd_rtnl_message_append(req, RTA_DST, &route->dst_addr); + if (r < 0) { + log_error("Could not append RTA_DST attribute: %s", strerror(-r)); + return r; + } + r = sd_rtnl_message_append(req, RTA_OIF, &link->ifindex); if (r < 0) { log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); @@ -106,16 +133,18 @@ int config_parse_gateway(const char *unit, const char *rvalue, void *data, void *userdata) { + Network *network = userdata; _cleanup_route_free_ Route *n = NULL; _cleanup_free_ char *route = NULL; int r; assert(filename); + assert(section); assert(lvalue); assert(rvalue); assert(data); - r = route_new(userdata, &n); + r = route_new(network, section_line, &n); if (r < 0) return r; @@ -130,3 +159,66 @@ int config_parse_gateway(const char *unit, return 0; } + +int config_parse_destination(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_route_free_ Route *n = NULL; + _cleanup_free_ char *address = NULL; + const char *e; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new(network, section_line, &n); + if (r < 0) + return r; + + /* Destination=address/prefixlen */ + + /* prefixlen */ + e = strchr(rvalue, '/'); + if (e) { + unsigned i; + r = safe_atou(e + 1, &i); + 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; + + address = strndup(rvalue, e - rvalue); + if (!address) + return log_oom(); + } else { + address = strdup(rvalue); + if (!address) + return log_oom(); + } + + r = net_parse_inaddr(address, &n->family, &n->dst_addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Destination is invalid, ignoring assignment: %s", address); + return 0; + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd.h b/src/network/networkd.h index 11012f555..04a56ea8c 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -54,11 +54,15 @@ struct Network { LIST_HEAD(Address, addresses); LIST_HEAD(Route, routes); + Hashmap *addresses_by_section; + Hashmap *routes_by_section; + LIST_FIELDS(Network, networks); }; struct Address { Network *network; + uint64_t section; unsigned char family; unsigned char prefixlen; @@ -76,14 +80,21 @@ struct Address { struct Route { Network *network; + uint64_t section; unsigned char family; + unsigned char dst_prefixlen; union { struct in_addr in; struct in6_addr in6; } in_addr; + union { + struct in_addr in; + struct in6_addr in6; + } dst_addr; + LIST_FIELDS(Route, routes); }; @@ -156,7 +167,7 @@ int network_apply(Manager *manager, Network *network, Link *link); const struct ConfigPerfItem* network_gperf_lookup(const char *key, unsigned length); /* Route */ -int route_new(Network *network, Route **ret); +int route_new(Network *network, unsigned section, Route **ret); void route_free(Route *route); int route_configure(Route *route, Link *link, sd_rtnl_message_handler_t callback); @@ -167,8 +178,12 @@ int config_parse_gateway(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_destination(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + /* Address */ -int address_new(Network *network, Address **ret); +int address_new(Network *network, unsigned section, Address **ret); void address_free(Address *address); int address_configure(Address *address, Link *link, sd_rtnl_message_handler_t callback); @@ -179,6 +194,10 @@ int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_label(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + /* Link */ int link_new(Manager *manager, struct udev_device *device, Link **ret);