Browse Source

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 <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>
Avinash Tekumalla 2 years ago
parent
commit
860ca900e4
6 changed files with 197 additions and 3 deletions
  1. 2 0
      README
  2. 115 0
      src/config.c
  3. 8 0
      src/dhcpv4.c
  4. 54 3
      src/dhcpv6.c
  5. 2 0
      src/dhcpv6.h
  6. 16 0
      src/odhcpd.h

+ 2 - 0
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)

+ 115 - 0
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:

+ 8 - 0
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);
 

+ 54 - 3
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);

+ 2 - 0
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

+ 16 - 0
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;