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)
IFACE_ATTR_NDPROXY_SLAVE,
IFACE_ATTR_PREFIX_FILTER,
IFACE_ATTR_PREFERRED_LIFETIME,
+ IFACE_ATTR_NTP,
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] = {
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);
}
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;
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:
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);
#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
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;
[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},
};
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);
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);
#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
#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, \
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;