]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-dhcp-server.c
networkd: dhcp server Support Vendor specific 43
[thirdparty/systemd.git] / src / network / networkd-dhcp-server.c
index f4c2178b7b2e42f12a72485289abec4d4b074410..77eb46341cc286fd1e83cd291776773a5667cf28 100644 (file)
@@ -2,11 +2,15 @@
 
 #include "sd-dhcp-server.h"
 
+#include "escape.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "parse-util.h"
 #include "strv.h"
+#include "string-table.h"
+#include "string-util.h"
 
 static Address* link_find_dhcp_server_address(Link *link) {
         Address *address;
@@ -137,10 +141,61 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
 }
 
+static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+        _cleanup_free_ struct in_addr *addresses = NULL;
+        size_t n_addresses = 0, n_allocated = 0;
+        char **a;
+
+        if (!link->network)
+                return 0;
+
+        log_debug("Copying SIP server information from %s", link->ifname);
+
+        STRV_FOREACH(a, link->network->sip) {
+                union in_addr_union ia;
+
+                /* Only look for IPv4 addresses */
+                if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
+                        continue;
+
+                /* Never propagate obviously borked data */
+                if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
+                        continue;
+
+                if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+                        return log_oom();
+
+                addresses[n_addresses++] = ia.in;
+        }
+
+        if (link->network->dhcp_use_sip && link->dhcp_lease) {
+                const struct in_addr *da = NULL;
+                int j, n;
+
+                n = sd_dhcp_lease_get_sip(link->dhcp_lease, &da);
+                if (n > 0) {
+
+                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+                                return log_oom();
+
+                        for (j = 0; j < n; j++)
+                                if (in4_addr_is_non_local(&da[j]))
+                                        addresses[n_addresses++] = da[j];
+                }
+        }
+
+        if (n_addresses <= 0)
+                return 0;
+
+        return sd_dhcp_server_set_sip(s, addresses, n_addresses);
+}
+
 int dhcp4_server_configure(Link *link) {
-        Address *address;
-        Link *uplink = NULL;
         bool acquired_uplink = false;
+        sd_dhcp_raw_option *p;
+        Link *uplink = NULL;
+        Address *address;
+        Iterator i;
         int r;
 
         address = link_find_dhcp_server_address(link);
@@ -209,6 +264,24 @@ int dhcp4_server_configure(Link *link) {
                         log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
         }
 
+        if (link->network->dhcp_server_emit_sip) {
+                if (link->network->n_dhcp_server_sip > 0)
+                        r = sd_dhcp_server_set_sip(link->dhcp_server, link->network->dhcp_server_sip, link->network->n_dhcp_server_sip);
+                else {
+                        if (!acquired_uplink)
+                                uplink = manager_find_uplink(link->manager, link);
+
+                        if (!uplink) {
+                                log_link_debug(link, "Not emitting sip server information on link, couldn't find suitable uplink.");
+                                r = 0;
+                        } else
+                                r = link_push_uplink_sip_to_dhcp_server(uplink, link->dhcp_server);
+
+                }
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Failed to set SIP server for DHCP server, ignoring: %m");
+        }
+
         r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
@@ -231,6 +304,15 @@ int dhcp4_server_configure(Link *link) {
                 if (r < 0)
                         return r;
         }
+
+        ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_raw_options, i) {
+                r = sd_dhcp_server_add_raw_option(link->dhcp_server, p);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return r;
+        }
+
         if (!sd_dhcp_server_is_running(link->dhcp_server)) {
                 r = sd_dhcp_server_start(link->dhcp_server);
                 if (r < 0)
@@ -345,3 +427,230 @@ int config_parse_dhcp_server_ntp(
                 n->dhcp_server_ntp = m;
         }
 }
+
+int config_parse_dhcp_server_sip(
+                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 *n = data;
+        const char *p = rvalue;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        for (;;) {
+                _cleanup_free_ char *w = NULL;
+                union in_addr_union a;
+                struct in_addr *m;
+
+                r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to extract word, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = in_addr_from_string(AF_INET, w, &a);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse SIP server address '%s', ignoring: %m", w);
+                        continue;
+                }
+
+                m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr));
+                if (!m)
+                        return log_oom();
+
+                m[n->n_dhcp_server_sip++] = a.in;
+                n->dhcp_server_sip = m;
+        }
+}
+
+static const char * const dhcp_raw_option_data_type_table[_DHCP_RAW_OPTION_DATA_MAX] = {
+        [DHCP_RAW_OPTION_DATA_UINT8]      = "uint8",
+        [DHCP_RAW_OPTION_DATA_UINT16]     = "uint16",
+        [DHCP_RAW_OPTION_DATA_UINT32]     = "uint32",
+        [DHCP_RAW_OPTION_DATA_STRING]     = "string",
+        [DHCP_RAW_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp_raw_option_data_type, DHCPRawOption);
+
+int config_parse_dhcp_server_raw_option_data(
+                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) {
+
+        _cleanup_(sd_dhcp_raw_option_unrefp) sd_dhcp_raw_option *opt = NULL, *old = NULL;
+        _cleanup_free_ char *word = NULL, *q = NULL;
+        union in_addr_union addr;
+        Network *network = data;
+        uint16_t uint16_data;
+        uint32_t uint32_data;
+        uint8_t uint8_data;
+        DHCPRawOption type;
+        const char *p;
+        void *udata;
+        ssize_t sz;
+        uint8_t u;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                network->dhcp_server_raw_options = ordered_hashmap_free(network->dhcp_server_raw_options);
+                return 0;
+        }
+
+        p = rvalue;
+        r = extract_first_word(&p, &word, ":", 0);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r <= 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid DHCP server send raw option, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        r = safe_atou8(word, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse  DHCP server send raw option type, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+        if (u < 1 || u >= 255) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Invalid DHCP server send raw option, valid range is 1-254, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        free(word);
+
+        r = extract_first_word(&p, &word, ":", 0);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r <= 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid DHCP server send raw option, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        r = dhcp_raw_option_data_type_from_string(word);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid DHCP server send data type, ignoring assignment: %s", p);
+                return 0;
+        }
+
+        type = r;
+        switch(type) {
+        case DHCP_RAW_OPTION_DATA_UINT8:{
+                r = safe_atou8(p, &uint8_data);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse DHCPv4 vendor specific uint8 data, ignoring assignment: %s", p);
+                        return 0;
+                }
+
+                udata = &uint8_data;
+                sz = sizeof(uint8_t);
+                break;
+        }
+        case DHCP_RAW_OPTION_DATA_UINT16:{
+                r = safe_atou16(p, &uint16_data);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse DHCPv4 vendor specific uint16 data, ignoring assignment: %s", p);
+                        return 0;
+                }
+
+                udata = &uint16_data;
+                sz = sizeof(uint16_t);
+                break;
+        }
+        case DHCP_RAW_OPTION_DATA_UINT32: {
+                r = safe_atou32(p, &uint32_data);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse DHCPv4 vendor specific uint32 data, ignoring assignment: %s", p);
+                        return 0;
+                }
+
+                udata = &uint32_data;
+                sz = sizeof(uint32_t);
+
+                break;
+        }
+        case DHCP_RAW_OPTION_DATA_IPV4ADDRESS: {
+                r = in_addr_from_string(AF_INET, p, &addr);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse DHCPv4 vendor specific ipv4address data, ignoring assignment: %s", p);
+                        return 0;
+                }
+
+                udata = &addr.in;
+                sz = sizeof(addr.in.s_addr);
+                break;
+        }
+        case DHCP_RAW_OPTION_DATA_STRING:
+                sz = cunescape(p, 0, &q);
+                if (sz < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, sz,
+                                   "Failed to decode option data, ignoring assignment: %s", p);
+                }
+
+                udata = q;
+                break;
+        default:
+                return -EINVAL;
+        }
+
+        r = sd_dhcp_raw_option_new(u, udata, sz, &opt);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to store DHCP send raw option '%s', ignoring assignment: %m", rvalue);
+                return 0;
+        }
+
+        r = ordered_hashmap_ensure_allocated(&network->dhcp_server_raw_options, &dhcp_raw_options_hash_ops);
+        if (r < 0)
+                return log_oom();
+
+        /* Overwrite existing option */
+        old = ordered_hashmap_remove(network->dhcp_server_raw_options, UINT_TO_PTR(u));
+        r = ordered_hashmap_put(network->dhcp_server_raw_options, UINT_TO_PTR(u), opt);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to store DHCP server send raw option '%s'", rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(opt);
+
+        return 0;
+}