odhcpd: Support for Option NTP and SNTP
authorAvinash Tekumalla <avinash.tekumalla@technicolor.com>
Wed, 8 Dec 2021 13:14:36 +0000 (18:44 +0530)
committerHans Dedecker <dedeckeh@gmail.com>
Wed, 16 Mar 2022 19:34:56 +0000 (20:34 +0100)
Support for DHCPv6 Option NTP (Option-56) and SNTP (Option-31),
DHCP Option NTP(Option-42) is implemented.
ntp list is supported for IPv4, IPv6 and FQDN.

Signed-off-by: Avinash Tekumalla <avinash.tekumalla@technicolor.com>
Signed-off-by: Alin Nastac <alin.nastac@technicolor.com>
Signed-off-by: Ashutosh Shandilya <ashutosh.shandilya@technicolor.com>
Signed-off-by: Vidya Rajagopal <vidya.rajagopal@technicolor.com>
README
src/config.c
src/dhcpv4.c
src/dhcpv6.c
src/dhcpv6.h
src/odhcpd.h

diff --git a/README b/README
index 6c79b95bb8edb4348d3ecc30172dc4971cc5a07f..ef8758e86cbecb28330b46889ec5757264c7a71b 100644 (file)
--- a/README
+++ b/README
@@ -147,6 +147,8 @@ ndproxy_slave               bool    0                       NDProxy external slave
 prefix_filter          string  ::/0                    Only advertise on-link prefixes within
                        [IPv6 prefix]                   the provided IPv6 prefix; others are
                                                        filtered out.
+ntp                    list    <local address>         NTP servers to announce
+                                                       accepts IPv4 and IPv6
 
 
 Sections of type host (static leases)
index 31893d1a2b6471490552b9b08548d2bf8e9c27cd..4e3db86596b82548b8f3ac48dd73164a5cb260c1 100644 (file)
@@ -88,6 +88,7 @@ enum {
        IFACE_ATTR_NDPROXY_SLAVE,
        IFACE_ATTR_PREFIX_FILTER,
        IFACE_ATTR_PREFERRED_LIFETIME,
+       IFACE_ATTR_NTP,
        IFACE_ATTR_MAX
 };
 
@@ -138,6 +139,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
        [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_PREFERRED_LIFETIME] = { .name = "preferred_lifetime", .type = BLOBMSG_TYPE_STRING },
+       [IFACE_ATTR_NTP] = { .name = "ntp", .type = BLOBMSG_TYPE_ARRAY },
 };
 
 static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
@@ -230,6 +232,9 @@ static void clean_interface(struct interface *iface)
        free(iface->dhcpv4_dns);
        free(iface->dhcpv6_raw);
        free(iface->filter_class);
+       free(iface->dhcpv4_ntp);
+       free(iface->dhcpv6_ntp);
+       free(iface->dhcpv6_sntp);
        memset(&iface->ra, 0, sizeof(*iface) - offsetof(struct interface, ra));
        set_interface_defaults(iface);
 }
@@ -443,6 +448,74 @@ static int set_lease_from_uci(struct uci_section *s)
        return set_lease_from_blobmsg(b.head);
 }
 
+/* Parse NTP Options for DHCPv6 Address */
+static int parse_ntp_options(uint16_t *dhcpv6_ntp_len, struct in6_addr addr6, uint8_t **dhcpv6_ntp)
+{
+       uint16_t sub_opt = 0, sub_len = htons(IPV6_ADDR_LEN);
+       uint16_t ntp_len = IPV6_ADDR_LEN + 4;
+       uint8_t *ntp = *dhcpv6_ntp;
+       size_t pos = *dhcpv6_ntp_len;
+
+       ntp = realloc(ntp, pos + ntp_len);
+       if (!ntp)
+               return -1;
+
+       *dhcpv6_ntp = ntp;
+
+       if (IN6_IS_ADDR_MULTICAST(&addr6))
+               sub_opt = htons(NTP_SUBOPTION_MC_ADDR);
+       else
+               sub_opt = htons(NTP_SUBOPTION_SRV_ADDR);
+
+       memcpy(ntp + pos, &sub_opt, sizeof(sub_opt));
+       pos += sizeof(sub_opt);
+       memcpy(ntp + pos, &sub_len, sizeof(sub_len));
+       pos += sizeof(sub_len);
+       memcpy(ntp + pos, &addr6, IPV6_ADDR_LEN);
+
+       *dhcpv6_ntp_len += ntp_len;
+
+       return 0;
+}
+
+/* Parse NTP Options for FQDN */
+static int parse_ntp_fqdn(uint16_t *dhcpv6_ntp_len, char *fqdn, uint8_t **dhcpv6_ntp)
+{
+       size_t fqdn_len = strlen(fqdn);
+       uint16_t sub_opt = 0, sub_len = 0, ntp_len = 0;
+       uint8_t *ntp = *dhcpv6_ntp;
+       size_t pos = *dhcpv6_ntp_len;
+       uint8_t buf[256] = {0};
+
+       if (fqdn_len > 0 && fqdn[fqdn_len - 1] == '.')
+               fqdn[fqdn_len - 1] = 0;
+
+       int len = dn_comp(fqdn, buf, sizeof(buf), NULL, NULL);
+       if (len <= 0)
+               return -1;
+
+       ntp_len = len + 4;
+
+       ntp = realloc(ntp, pos + ntp_len);
+       if (!ntp)
+               return -1;
+
+       *dhcpv6_ntp = ntp;
+
+       sub_opt = htons(NTP_SUBOPTION_SRV_FQDN);
+       sub_len = htons(len);
+
+       memcpy(ntp + pos, &sub_opt, sizeof(sub_opt));
+       pos += sizeof(sub_opt);
+       memcpy(ntp + pos, &sub_len, sizeof(sub_len));
+       pos += sizeof(sub_len);
+       memcpy(ntp + pos, buf, len);
+
+       *dhcpv6_ntp_len += ntp_len;
+
+       return 0;
+}
+
 int config_parse_interface(void *data, size_t len, const char *name, bool overwrite)
 {
        struct interface *iface;
@@ -915,6 +988,48 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
                        free(astr);
        }
 
+       if (overwrite && (c = tb[IFACE_ATTR_NTP])) {
+               struct blob_attr *cur;
+               unsigned rem;
+
+               blobmsg_for_each_attr(cur, c, rem) {
+                       if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, false))
+                               continue;
+
+                       char *str = blobmsg_get_string(cur);
+                       struct in_addr addr4;
+                       struct in6_addr addr6;
+
+                       if (inet_pton(AF_INET, str, &addr4) == 1) {
+                               if (addr4.s_addr == INADDR_ANY)
+                                       goto err;
+
+                               iface->dhcpv4_ntp = realloc(iface->dhcpv4_ntp,
+                                               (++iface->dhcpv4_ntp_cnt) * sizeof(*iface->dhcpv4_ntp));
+                               if (!iface->dhcpv4_ntp)
+                                       goto err;
+
+                               iface->dhcpv4_ntp[iface->dhcpv4_ntp_cnt - 1] = addr4;
+                       } else if (inet_pton(AF_INET6, str, &addr6) == 1) {
+                               if (IN6_IS_ADDR_UNSPECIFIED(&addr6))
+                                       goto err;
+
+                               iface->dhcpv6_sntp = realloc(iface->dhcpv6_sntp,
+                                               (++iface->dhcpv6_sntp_cnt) * sizeof(*iface->dhcpv6_sntp));
+                               if (!iface->dhcpv6_sntp)
+                                       goto err;
+
+                               iface->dhcpv6_sntp[iface->dhcpv6_sntp_cnt - 1] = addr6;
+
+                               if (!parse_ntp_options(&iface->dhcpv6_ntp_len, addr6, &iface->dhcpv6_ntp))
+                                       iface->dhcpv6_ntp_cnt++;
+                       } else {
+                               if (!parse_ntp_fqdn(&iface->dhcpv6_ntp_len, str, &iface->dhcpv6_ntp))
+                                       iface->dhcpv6_ntp_cnt++;
+                       }
+               }
+       }
+
        return 0;
 
 err:
index a3b0e33c7540df34af8a0b4c2c6e5ff54366df0d..f0780876ebbe47c71a71a6d460fa9084508c2e44 100644 (file)
@@ -849,6 +849,14 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len,
                dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER,
                                4 * iface->dhcpv4_dns_cnt, iface->dhcpv4_dns);
 
+       if (a->reqopts && iface->dhcpv4_ntp_cnt != 0) {
+               for(size_t opts = 0; a->reqopts[opts]; opts++) {
+                       if (a->reqopts[opts] == DHCPV4_OPT_NTPSERVER) {
+                               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_NTPSERVER,
+                                               4 * iface->dhcpv4_ntp_cnt, iface->dhcpv4_ntp);
+                       }
+               }
+       }
 
        dhcpv4_put(&reply, &cookie, DHCPV4_OPT_END, 0, NULL);
 
index 225f9616f029b4c95d46a5947d04095e74f1c4d6..b086fd848b714e84e6cf7132113a5d22cc84c0ed 100644 (file)
@@ -176,6 +176,10 @@ enum {
 #define        IOV_REFRESH IOV_PDBUF
        IOV_CERID,
        IOV_DHCPV6_RAW,
+       IOV_NTP,
+       IOV_NTP_ADDR,
+       IOV_SNTP,
+       IOV_SNTP_ADDR,
        IOV_RELAY_MSG,
        IOV_DHCPV4O6_SERVER,
        IOV_TOTAL
@@ -376,7 +380,50 @@ static void handle_client_request(void *addr, void *data, size_t len,
                uint16_t len;
        } dns = {htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_cnt * sizeof(*dns_addr_ptr))};
 
+       /* SNTP */
+       struct in6_addr *sntp_addr_ptr = iface->dhcpv6_sntp;
+       size_t sntp_cnt = 0;
 
+       struct {
+               uint16_t type;
+               uint16_t len;
+       } dhcpv6_sntp;
+
+       /* NTP */
+       uint8_t *ntp_ptr = iface->dhcpv6_ntp;
+       uint16_t ntp_len = iface->dhcpv6_ntp_len;
+       size_t ntp_cnt = 0;
+
+       struct {
+               uint16_t type;
+               uint16_t len;
+       } ntp;
+
+       uint16_t otype, olen;
+       uint16_t *reqopts = NULL;
+       uint8_t *odata;
+       size_t reqopts_len = 0;
+
+       dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
+               if (otype == DHCPV6_OPT_ORO) {
+                       reqopts_len = olen;
+                       reqopts = (uint16_t *)odata;
+               }
+       }
+
+       for(size_t opt = 0; opt < reqopts_len/2; opt++) {
+               if (iface->dhcpv6_sntp_cnt != 0 &&
+                       DHCPV6_OPT_SNTP_SERVERS == ntohs(reqopts[opt])) {
+                       sntp_cnt = iface->dhcpv6_sntp_cnt;
+                       dhcpv6_sntp.type = htons(DHCPV6_OPT_SNTP_SERVERS);
+                       dhcpv6_sntp.len = htons(sntp_cnt * sizeof(*sntp_addr_ptr));
+               } else if (iface->dhcpv6_ntp_cnt != 0 &&
+                       DHCPV6_OPT_NTP_SERVERS == ntohs(reqopts[opt])) {
+                       ntp_cnt = iface->dhcpv6_ntp_cnt;
+                       ntp.type = htons(DHCPV6_OPT_NTP_SERVERS);
+                       ntp.len = htons(ntp_len);
+               }
+       }
 
        /* DNS Search options */
        uint8_t search_buf[256], *search_domain = iface->search;
@@ -426,6 +473,10 @@ static void handle_client_request(void *addr, void *data, size_t len,
                [IOV_PDBUF] = {pdbuf, 0},
                [IOV_CERID] = {&cerid, 0},
                [IOV_DHCPV6_RAW] = {iface->dhcpv6_raw, iface->dhcpv6_raw_len},
+               [IOV_NTP] = {&ntp, (ntp_cnt) ? sizeof(ntp) : 0},
+               [IOV_NTP_ADDR] = {ntp_ptr, (ntp_cnt) ? ntp_len : 0},
+               [IOV_SNTP] = {&dhcpv6_sntp, (sntp_cnt) ? sizeof(dhcpv6_sntp) : 0},
+               [IOV_SNTP_ADDR] = {sntp_addr_ptr, sntp_cnt * sizeof(*sntp_addr_ptr)},
                [IOV_RELAY_MSG] = {NULL, 0},
                [IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0},
        };
@@ -467,8 +518,6 @@ static void handle_client_request(void *addr, void *data, size_t len,
        memcpy(dest.tr_id, hdr->transaction_id, sizeof(dest.tr_id));
 
        /* Go through options and find what we need */
-       uint16_t otype, olen;
-       uint8_t *odata;
        dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
                if (otype == DHCPV6_OPT_CLIENTID && olen <= 130) {
                        dest.clientid_length = htons(olen);
@@ -600,7 +649,9 @@ static void handle_client_request(void *addr, void *data, size_t len,
                                      iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
                                      iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
                                      iov[IOV_DHCPV4O6_SERVER].iov_len +
-                                     iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len -
+                                     iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len +
+                                     iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len +
+                                     iov[IOV_SNTP].iov_len + iov[IOV_SNTP_ADDR].iov_len -
                                      (4 + opts_end - opts));
 
        syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);
index c96dc0d042ae56af2c64b952806c1b4cadf3e087..f344969f19380b336ef77709d18f0971d36a0e72 100644 (file)
 #define DHCPV6_OPT_DNS_DOMAIN 24
 #define DHCPV6_OPT_IA_PD 25
 #define DHCPV6_OPT_IA_PREFIX 26
+#define DHCPV6_OPT_SNTP_SERVERS 31
 #define DHCPV6_OPT_INFO_REFRESH 32
 #define DHCPV6_OPT_FQDN 39
+#define DHCPV6_OPT_NTP_SERVERS 56
 #define DHCPV6_OPT_SOL_MAX_RT 82
 #define DHCPV6_OPT_INF_MAX_RT 83
 #define DHCPV6_OPT_DHCPV4_MSG 87
index 88c8c792fe6e00d506140ab38c65ffe222ab1f2b..5ba68547db9efb070ccec3cf05d138c43806611b 100644 (file)
 #define ALL_IPV6_NODES "ff02::1"
 #define ALL_IPV6_ROUTERS "ff02::2"
 
+#define NTP_SUBOPTION_SRV_ADDR 1
+#define NTP_SUBOPTION_MC_ADDR 2
+#define NTP_SUBOPTION_SRV_FQDN 3
+#define IPV6_ADDR_LEN 16
+
 #define IN6_IS_ADDR_ULA(a) (((a)->s6_addr32[0] & htonl(0xfe000000)) == htonl(0xfc000000))
 
 #define ADDR_MATCH_PIO_FILTER(_addr, iface) (odhcpd_bmemcmp(&(_addr)->addr, \
@@ -342,6 +347,17 @@ struct interface {
        size_t upstream_len;
 
        char *filter_class;
+
+       // NTP
+       struct in_addr *dhcpv4_ntp;
+       size_t dhcpv4_ntp_cnt;
+       uint8_t *dhcpv6_ntp;
+       uint16_t dhcpv6_ntp_len;
+       size_t dhcpv6_ntp_cnt;
+
+       // SNTP
+       struct in6_addr *dhcpv6_sntp;
+       size_t dhcpv6_sntp_cnt;
 };
 
 extern struct avl_tree interfaces;