From 860ca900e41c5d0f98cc85e67b39977f6f2cb355 Mon Sep 17 00:00:00 2001 From: Avinash Tekumalla Date: Wed, 8 Dec 2021 18:44:36 +0530 Subject: odhcpd: Support for Option NTP and SNTP 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 Signed-off-by: Alin Nastac Signed-off-by: Ashutosh Shandilya Signed-off-by: Vidya Rajagopal --- README | 2 ++ src/config.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dhcpv4.c | 8 +++++ src/dhcpv6.c | 57 +++++++++++++++++++++++++++-- src/dhcpv6.h | 2 ++ src/odhcpd.h | 16 +++++++++ 6 files changed, 197 insertions(+), 3 deletions(-) diff --git a/README b/README index 6c79b95..ef8758e 100644 --- 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 NTP servers to announce + accepts IPv4 and IPv6 Sections of type host (static leases) diff --git a/src/config.c b/src/config.c index 31893d1..4e3db86 100644 --- a/src/config.c +++ b/src/config.c @@ -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: diff --git a/src/dhcpv4.c b/src/dhcpv4.c index a3b0e33..f078087 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -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); diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 225f961..b086fd8 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -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); diff --git a/src/dhcpv6.h b/src/dhcpv6.h index c96dc0d..f344969 100644 --- a/src/dhcpv6.h +++ b/src/dhcpv6.h @@ -55,8 +55,10 @@ #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 diff --git a/src/odhcpd.h b/src/odhcpd.h index 88c8c79..5ba6854 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -42,6 +42,11 @@ #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; -- cgit v1.2.3