diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/config.c | 34 | ||||
-rw-r--r-- | src/dhcpv4.c | 4 | ||||
-rw-r--r-- | src/dhcpv6-ia.c | 27 | ||||
-rw-r--r-- | src/dnsupdate.c | 217 | ||||
-rw-r--r-- | src/odhcpd.c | 1 | ||||
-rw-r--r-- | src/odhcpd.h | 9 |
7 files changed, 288 insertions, 6 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 828fa71..2843db9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ if(${DHCPV4_SUPPORT}) set(EXT_SRC ${EXT_SRC} src/dhcpv4.c) endif(${DHCPV4_SUPPORT}) -add_executable(odhcpd src/odhcpd.c src/config.c src/router.c src/dhcpv6.c src/ndp.c src/dhcpv6-ia.c src/netlink.c ${EXT_SRC}) +add_executable(odhcpd src/odhcpd.c src/config.c src/router.c src/dhcpv6.c src/ndp.c src/dhcpv6-ia.c src/netlink.c src/dnsupdate.c ${EXT_SRC}) target_link_libraries(odhcpd resolv ubox uci ${libnl} ${EXT_LINK}) # Installation diff --git a/src/config.c b/src/config.c index 71b786c..dc0722e 100644 --- a/src/config.c +++ b/src/config.c @@ -172,6 +172,10 @@ enum { ODHCPD_ATTR_LEASEFILE, ODHCPD_ATTR_LEASETRIGGER, ODHCPD_ATTR_LOGLEVEL, + ODHCPD_ATTR_NSUPDATE_DOMAIN, + ODHCPD_ATTR_NSUPDATE_KEY, + ODHCPD_ATTR_NSUPDATE_SERVER, + ODHCPD_ATTR_NSUPDATE_ZONE, ODHCPD_ATTR_MAX }; @@ -181,6 +185,10 @@ static const struct blobmsg_policy odhcpd_attrs[ODHCPD_ATTR_MAX] = { [ODHCPD_ATTR_LEASEFILE] = { .name = "leasefile", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_LEASETRIGGER] = { .name = "leasetrigger", .type = BLOBMSG_TYPE_STRING }, [ODHCPD_ATTR_LOGLEVEL] = { .name = "loglevel", .type = BLOBMSG_TYPE_INT32 }, + [ODHCPD_ATTR_NSUPDATE_DOMAIN] = { .name = "nsupdate_domain", .type = BLOBMSG_TYPE_STRING }, + [ODHCPD_ATTR_NSUPDATE_KEY] = { .name = "nsupdate_key", .type = BLOBMSG_TYPE_STRING }, + [ODHCPD_ATTR_NSUPDATE_SERVER] = { .name = "nsupdate_server", .type = BLOBMSG_TYPE_STRING }, + [ODHCPD_ATTR_NSUPDATE_ZONE] = { .name = "nsupdate_zone", .type = BLOBMSG_TYPE_STRING }, }; const struct uci_blob_param_list odhcpd_attr_list = { @@ -326,6 +334,26 @@ static void set_config(struct uci_section *s) setlogmask(LOG_UPTO(config.log_level)); } } + + if ((c = tb[ODHCPD_ATTR_NSUPDATE_DOMAIN])) { + free(config.nsupdate_domain); + config.nsupdate_domain = strdup(blobmsg_get_string(c)); + } + + if ((c = tb[ODHCPD_ATTR_NSUPDATE_KEY])) { + free(config.nsupdate_key); + config.nsupdate_key = strdup(blobmsg_get_string(c)); + } + + if ((c = tb[ODHCPD_ATTR_NSUPDATE_SERVER])) { + free(config.nsupdate_server); + config.nsupdate_server = strdup(blobmsg_get_string(c)); + } + + if ((c = tb[ODHCPD_ATTR_NSUPDATE_ZONE])) { + free(config.nsupdate_zone); + config.nsupdate_zone = strdup(blobmsg_get_string(c)); + } } static double parse_leasetime(struct blob_attr *c) { @@ -1210,6 +1238,8 @@ void odhcpd_reload(void) uci_unload(uci, dhcp); uci_free_context(uci); + + dns_reload(); } static void handle_signal(int signal) @@ -1234,6 +1264,7 @@ static struct uloop_fd reload_fd = { .fd = -1, .cb = reload_cb }; void odhcpd_run(void) { + syslog(LOG_DEBUG, "odhcpd_run"); if (pipe2(reload_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {} reload_fd.fd = reload_pipe[0]; @@ -1248,6 +1279,9 @@ void odhcpd_run(void) sleep(1); #endif + syslog(LOG_DEBUG, "pre dns_init"); + dns_init(); + syslog(LOG_DEBUG, "after dns_init"); odhcpd_reload(); uloop_run(); } diff --git a/src/dhcpv4.c b/src/dhcpv4.c index a3b0e33..4bab081 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -917,6 +917,10 @@ void dhcpv4_handle_msg(void *addr, void *data, size_t len, ubus_bcast_dhcp_event("dhcp.ack", req->chaddr, req->hlen, &reply.yiaddr, a ? a->hostname : NULL, iface->ifname); #endif + if (msg == DHCPV4_MSG_ACK) { + dns_update_rr(a ? a->hostname : NULL, AF_INET, &reply.yiaddr); + dns_update_finish(); + } } static bool dhcpv4_insert_assignment(struct list_head *list, struct dhcp_assignment *a, diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index e8255b5..af0b367 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -453,12 +453,26 @@ void dhcpv6_ia_write_statefile(void) } } -static void __apply_lease(struct dhcp_assignment *a, +static void __apply_lease_na(struct dhcp_assignment *a, struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add) { - if (a->flags & OAF_DHCPV6_NA) - return; + for (ssize_t i = 0; i < addr_len; ++i) { + struct in6_addr addr; + /* if (ADDR_MATCH_PIO_FILTER(&addrs[i], a->iface)) */ + /* continue; */ + + addr = addrs[i].addr.in6; + addr.s6_addr32[2] = htonl(a->assigned_host_id >> 32); + addr.s6_addr32[3] = htonl(a->assigned_host_id & UINT32_MAX); + dns_update_rr(a->hostname, AF_INET6, &addr); + } + dns_update_finish(); +} + +static void __apply_lease_pd(struct dhcp_assignment *a, + struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add) +{ for (ssize_t i = 0; i < addr_len; ++i) { struct in6_addr prefix; @@ -479,7 +493,10 @@ static void apply_lease(struct dhcp_assignment *a, bool add) struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6; ssize_t addrlen = (a->managed) ? a->managed_size : (ssize_t)iface->addr6_len; - __apply_lease(a, addrs, addrlen, add); + if (a->flags & OAF_DHCPV6_NA) + __apply_lease_na(a, addrs, addrlen, add); + else + __apply_lease_pd(a, addrs, addrlen, add); } /* Set border assignment size based on the IPv6 address prefixes */ @@ -774,7 +791,7 @@ static void handle_addrlist_change(struct netevent_handler_info *info) list_for_each_entry(c, &iface->ia_assignments, head) { if ((c->flags & OAF_DHCPV6_PD) && !(iface->ra_flags & ND_RA_FLAG_MANAGED) && (c->flags & OAF_BOUND)) - __apply_lease(c, info->addrs_old.addrs, + __apply_lease_pd(c, info->addrs_old.addrs, info->addrs_old.len, false); } diff --git a/src/dnsupdate.c b/src/dnsupdate.c new file mode 100644 index 0000000..e8550a6 --- /dev/null +++ b/src/dnsupdate.c @@ -0,0 +1,217 @@ +#include <arpa/inet.h> +#include <fcntl.h> +#include <syslog.h> +#include <unistd.h> +#include <libubox/uloop.h> +#include "odhcpd.h" + +#define READ_END 0 +#define WRITE_END 1 + +static void update_cb(struct uloop_fd *u, unsigned int events); +static void update_proc_cb(struct uloop_process *c, int ret); +static void update_time_cb(struct uloop_timeout *t); + +static int dns_start(); +static int dns_update_init(); +static ssize_t dns_write(const char *buf, size_t count); + +static struct uloop_fd update_in = { .fd = -1, .cb = update_cb }; +static struct uloop_fd update_err = { .fd = -1, .cb = update_cb }; +static struct uloop_process update_proc = { .cb = update_proc_cb }; +static int update_pipes[3][2] = {{-1, -1}, {-1, -1}, {-1, -1}}; +static struct uloop_timeout update_time = { .cb = update_time_cb }; + +static void update_time_cb(struct uloop_timeout *t) +{ + const char CMD_QUIT[] = "quit\n"; + syslog(LOG_ERR, "update_time"); + if (update_pipes[1][WRITE_END] < 0) { + return; + } + dns_write(CMD_QUIT, sizeof(CMD_QUIT)); +} + +static void update_cb(struct uloop_fd *u, unsigned int events) +{ + syslog(LOG_ERR, "update_cb: %d %d", u->fd, events); + +// if (events & ULOOP_READ) { + char buf[512]; + int len = 0; + if ((len = read(u->fd, buf, sizeof(buf)-1)) > 0) { + buf[len] = 0; + syslog(LOG_ERR, "update_cb: read '%s'", buf); + } +// } + syslog(LOG_ERR, "update_cb: read done"); +} + + +static void update_proc_cb(struct uloop_process *c, int ret) +{ + syslog(LOG_ERR, "update_cb: process exit: %d", ret); + uloop_timeout_cancel(&update_time); + c->pid = 0; + uloop_fd_delete(&update_in); + update_in.fd = -1; + uloop_fd_delete(&update_err); + update_err.fd = -1; + close(update_pipes[0][READ_END]); + close(update_pipes[1][WRITE_END]); + close(update_pipes[2][READ_END]); + update_pipes[0][READ_END] = -1; + update_pipes[1][WRITE_END] = -1; + update_pipes[2][READ_END] = -1; +} + +static ssize_t dns_write(const char *buf, size_t count) +{ + if (update_pipes[1][WRITE_END] < 0) { + if (dns_start() < 0) + return -1; + } + + uloop_timeout_set(&update_time, 10 * 1000); + return write(update_pipes[1][WRITE_END], buf, count); +} + +int dns_update_rr(const char *hostname, int family, const void *addr) +{ + if (!config.nsupdate_domain) { + return 0; + } + + if (!hostname) { + syslog(LOG_ERR, "update_cb: no hostname"); + return 0; + } + + dns_update_init(); + + char tmp_addr[INET6_ADDRSTRLEN]; + inet_ntop(family, addr, tmp_addr, sizeof(tmp_addr)); + + char buf[512]=""; + int res = snprintf(buf, sizeof(buf), "add %s.%s. 3600 %s %s\n", hostname, config.nsupdate_domain, family==AF_INET6?"AAAA":"A", tmp_addr); + // build_nsupdate(name, 'dhcid', build_dhcid(TYPE_HWID, mac)) + // build_nsupdate(name, 'dhcid', build_dhcid(TYPE_DUID, duid)) + + syslog(LOG_ERR, "dns_update_rr: buf=%s", buf); + + if (res > 0 && res < (int)sizeof(buf)) { + res = dns_write(buf, strlen(buf)); + syslog(LOG_ERR, "dns_update_rr: write %d %zd", res, strlen(buf)); + syslog(LOG_ERR, "dns_update_rr: hostname=%s addr=%s", hostname, tmp_addr); + } + return 0; +} + +int dns_update_finish() +{ + const char CMD[] = "send\n"; + int res = dns_write(CMD, strlen(CMD)); + syslog(LOG_ERR, "dns_update_finish: write %d %zd", res, strlen(CMD)); + return res; +} + +int dns_reload() +{ + return 0; +} + +static int dns_update_init() +{ + struct { + const char *name; + const char *value; + } params[] = { + { "key", config.nsupdate_key }, + { "server", config.nsupdate_server }, + { "zone", config.nsupdate_zone }, + { NULL, NULL }, + }; + + for (int i = 0; params[i].name; i++) { + + if (!params[i].value) + continue; + + char buf[512]; + int len = snprintf(buf, sizeof(buf), "%s %s\n", + params[i].name, params[i].value); + + if (len < 0 || len > (int)sizeof(buf)) + continue; + + syslog(LOG_ERR, "dns_reload: %s=%s", params[i].name, params[i].value); + int res = dns_write(buf, len); + syslog(LOG_ERR, "dns_reload: write %d %d", res, len); + } + + return 0; +} + +int dns_init() +{ + syslog(LOG_ERR, "dns_init"); + return -1; +} + +static int dns_start() +{ + /* if (update_pipes[0] > -1) */ + /* close(update_pipes[0]); */ + + /* if (update_pipes[1] > -1) */ + /* close(update_pipes[1]); */ + + for (int i = 0; i < 3; i++) { + if (pipe2(update_pipes[i], 0) < 0) { + syslog(LOG_ERR, "dns_init: pipe2 failed"); + return -1; + } + } + + int pid = fork(); + + switch (pid) { + case 0: + /* child */ + syslog(LOG_ERR, "dns_init: child"); + dup2(update_pipes[0][WRITE_END], STDOUT_FILENO); + dup2(update_pipes[1][READ_END], STDIN_FILENO); + dup2(update_pipes[2][WRITE_END], STDERR_FILENO); + close(update_pipes[0][READ_END]); + close(update_pipes[1][WRITE_END]); + close(update_pipes[2][READ_END]); + + char *argv[2] = {"/root/knsupdate.sh", NULL}; + execv(argv[0], argv); + syslog(LOG_ERR, "dns_init: exec error?"); + return 0; + case -1: + /* error returned in parent. */ + syslog(LOG_ERR, "dns_init: failed"); + return -1; + default: + /* parent */ + break; + } + + syslog(LOG_ERR, "dns_init: parent '%d'", pid); + update_in.fd = update_pipes[0][READ_END]; + update_err.fd = update_pipes[2][READ_END]; + close(update_pipes[0][WRITE_END]); + close(update_pipes[1][READ_END]); + close(update_pipes[2][WRITE_END]); + update_pipes[0][WRITE_END] = -1; + update_pipes[1][READ_END] = -1; + update_pipes[2][WRITE_END] = -1; + uloop_fd_add(&update_in, ULOOP_READ | ULOOP_BLOCKING); + uloop_fd_add(&update_err, ULOOP_READ | ULOOP_BLOCKING); + update_proc.pid = pid; + uloop_process_add(&update_proc); + //dns_reload(); + return 0; +} diff --git a/src/odhcpd.c b/src/odhcpd.c index 9797507..3ab172f 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -130,6 +130,7 @@ int main(int argc, char **argv) return 4; #endif + syslog(LOG_ERR, "pre odhcpd_run"); odhcpd_run(); return 0; } diff --git a/src/odhcpd.h b/src/odhcpd.h index ff7e105..4cdf5a4 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -149,6 +149,10 @@ struct config { char *dhcp_cb; char *dhcp_statefile; int log_level; + char *nsupdate_domain; + char *nsupdate_key; + char *nsupdate_server; + char *nsupdate_zone; }; @@ -446,4 +450,9 @@ int router_setup_interface(struct interface *iface, bool enable); int dhcpv6_setup_interface(struct interface *iface, bool enable); int ndp_setup_interface(struct interface *iface, bool enable); +int dns_init(); +int dns_update_rr(const char *hostname, int family, const void *addr); +int dns_update_finish(); +int dns_reload(); + void odhcpd_reload(void); |