diff options
Diffstat (limited to 'usr.sbin/rtadvd/control_server.c')
-rw-r--r-- | usr.sbin/rtadvd/control_server.c | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/usr.sbin/rtadvd/control_server.c b/usr.sbin/rtadvd/control_server.c new file mode 100644 index 0000000..76ca541 --- /dev/null +++ b/usr.sbin/rtadvd/control_server.c @@ -0,0 +1,752 @@ +/*- + * Copyright (C) 2011 Hiroki Sato <hrs@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + * + */ + +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/uio.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <netinet/in.h> +#include <netinet/icmp6.h> +#include <fcntl.h> +#include <errno.h> +#include <netdb.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> + +#include "pathnames.h" +#include "rtadvd.h" +#include "if.h" +#include "config.h" +#include "control.h" +#include "control_server.h" +#include "timer.h" + +static char *do_reload_ifname; +static int do_reload; +static int do_shutdown; + +void set_do_reload(int sig __unused) { do_reload = 1; } +void set_do_reload_ifname(char *ifname){ do_reload_ifname = ifname; } +void set_do_shutdown(int sig __unused) { do_shutdown = 1; } +void reset_do_reload(void) { do_reload = 0; do_reload_ifname = NULL; } +void reset_do_shutdown(void) { do_shutdown = 0; } +int is_do_reload(void) { return (do_reload); } +int is_do_shutdown(void) { return (do_shutdown); } +char *reload_ifname(void) { return (do_reload_ifname); } + +#define DEF_PL_HANDLER(key) { #key, cm_getprop_##key } + +static int cm_getprop_echo(struct ctrl_msg_pl *); +static int cm_getprop_version(struct ctrl_msg_pl *); +static int cm_getprop_ifilist(struct ctrl_msg_pl *); +static int cm_getprop_ifi(struct ctrl_msg_pl *); +static int cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *); +static int cm_getprop_rai(struct ctrl_msg_pl *); +static int cm_getprop_pfx(struct ctrl_msg_pl *); +static int cm_getprop_rdnss(struct ctrl_msg_pl *); +static int cm_getprop_dnssl(struct ctrl_msg_pl *); +static int cm_getprop_rti(struct ctrl_msg_pl *); + +static int cm_setprop_reload(struct ctrl_msg_pl *); +static int cm_setprop_enable(struct ctrl_msg_pl *); +static int cm_setprop_disable(struct ctrl_msg_pl *); + +static struct dispatch_table { + const char *dt_comm; + int (*dt_act)(struct ctrl_msg_pl *cp); +} getprop_dtable[] = { + { "", cm_getprop_echo }, + DEF_PL_HANDLER(echo), + DEF_PL_HANDLER(version), + DEF_PL_HANDLER(ifilist), + DEF_PL_HANDLER(ifi), + DEF_PL_HANDLER(ifi_ra_timer), + DEF_PL_HANDLER(rai), + DEF_PL_HANDLER(rti), + DEF_PL_HANDLER(pfx), + DEF_PL_HANDLER(rdnss), + DEF_PL_HANDLER(dnssl), +}; + +static int +cm_getprop_echo(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + cp->cp_val = strdup(""); + cp->cp_val_len = strlen(cp->cp_val) + 1; + + return (0); +} + +static int +cm_getprop_version(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + cp->cp_val = strdup(CM_VERSION_STR); + cp->cp_val_len = strlen(cp->cp_val) + 1; + + return (0); +} + +static int +cm_getprop_ifilist(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + len += strlen(ifi->ifi_ifname) + 1; + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + syslog(LOG_DEBUG, "<%s> add ifname=%s(%d)", + __func__, ifi->ifi_ifname, ifi->ifi_ifindex); + strcpy(p, ifi->ifi_ifname); + p += strlen(ifi->ifi_ifname) + 1; + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_ifi(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + p = malloc(sizeof(*ifi)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, ifi, sizeof(*ifi)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_rai(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if ((rai = ifi->ifi_rainfo) == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + + p = malloc(sizeof(*rai)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, rai, sizeof(*rai)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_ifi_ra_timer(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rtadvd_timer *rtimer; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if ((rai = ifi->ifi_rainfo) == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + if ((rtimer = ifi->ifi_ra_timer) == NULL) { + syslog(LOG_ERR, "<%s> %s has no ifi_ra_timer", __func__, + cp->cp_ifname); + return (1); + } + p = malloc(sizeof(*rtimer)); + if (p == NULL) + exit(1); + len = cm_str2bin(p, rtimer, sizeof(*rtimer)); + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + if (len == 0) + return (1); + + cp->cp_val = p; + cp->cp_val_len = len; + + return (0); +} + +static int +cm_getprop_rti(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rtinfo *rti; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + len += sizeof(*rti); + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(rti, &rai->rai_route, rti_next) { + memcpy(p, rti, sizeof(*rti)); + p += sizeof(*rti); + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_pfx(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct prefix *pfx; + char *p; + size_t len; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + len += sizeof(*pfx); + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + if (len > 0) + TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { + memcpy(p, pfx, sizeof(*pfx)); + p += sizeof(*pfx); + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_rdnss(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct rdnss *rdn; + struct rdnss_addr *rda; + char *p; + size_t len; + uint16_t *rdn_cnt; + uint16_t *rda_cnt; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + + len = sizeof(*rdn_cnt); + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + len += sizeof(*rdn); + len += sizeof(*rda_cnt); + TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) { + len += sizeof(*rda); + } + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + rdn_cnt = (uint16_t *)p; + p += sizeof(*rdn_cnt); + TAILQ_FOREACH(rdn, &rai->rai_rdnss, rd_next) { + *rdn_cnt += 1; + memcpy(p, rdn, sizeof(*rdn)); + p += sizeof(*rdn); + + rda_cnt = (uint16_t *)p; + p += sizeof(*rda_cnt); + TAILQ_FOREACH(rda, &rdn->rd_list, ra_next) { + *rda_cnt += 1; + memcpy(p, rda, sizeof(*rda)); + p += sizeof(*rda); + } + } + syslog(LOG_DEBUG, "<%s> rdn_cnt = %d", __func__, *rdn_cnt); + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +static int +cm_getprop_dnssl(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + struct rainfo *rai; + struct dnssl *dns; + struct dnssl_addr *dna; + char *p; + size_t len; + uint16_t *dns_cnt; + uint16_t *dna_cnt; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + len = 0; + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + if (ifi->ifi_rainfo == NULL) { + syslog(LOG_ERR, "<%s> %s has no rainfo", __func__, + cp->cp_ifname); + return (1); + } + rai = ifi->ifi_rainfo; + + len = sizeof(*dns_cnt); + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + len += sizeof(*dns); + len += sizeof(*dna_cnt); + TAILQ_FOREACH(dna, &dns->dn_list, da_next) { + len += sizeof(*dna); + } + } + + syslog(LOG_DEBUG, "<%s> len = %zu", __func__, len); + + p = malloc(len); + if (p == NULL) + exit(1); + memset(p, 0, len); + cp->cp_val = p; + + dns_cnt = (uint16_t *)cp->cp_val; + p += sizeof(*dns_cnt); + TAILQ_FOREACH(dns, &rai->rai_dnssl, dn_next) { + (*dns_cnt)++; + memcpy(p, dns, sizeof(*dns)); + p += sizeof(*dns); + + dna_cnt = (uint16_t *)p; + p += sizeof(*dna_cnt); + TAILQ_FOREACH(dna, &dns->dn_list, da_next) { + (*dna_cnt)++; + memcpy(p, dna, sizeof(*dna)); + p += sizeof(*dna); + } + } + cp->cp_val_len = p - cp->cp_val; + + return (0); +} + +int +cm_getprop(struct ctrl_msg_pl *cp) +{ + size_t i; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (cp == NULL) + return (1); + + for (i = 0; + i < sizeof(getprop_dtable) / sizeof(getprop_dtable[0]); + i++) { + if (strcmp(cp->cp_key, getprop_dtable[i].dt_comm) == 0) + return (getprop_dtable[i].dt_act(cp)); + } + return (1); +} + +int +cm_setprop(struct ctrl_msg_pl *cp) +{ + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (cp == NULL || cp->cp_key == NULL) + return (1); + + if (strncmp(cp->cp_key, "reload", sizeof("reload")) == 0) + cm_setprop_reload(cp); + else if (strncmp(cp->cp_key, "shutdown", sizeof("shutdown")) == 0) + set_do_shutdown(0); + else if (strncmp(cp->cp_key, "enable", sizeof("enable")) == 0) + cm_setprop_enable(cp); + else if (strncmp(cp->cp_key, "disable", sizeof("disable")) == 0) + cm_setprop_disable(cp); + else if (strncmp(cp->cp_key, "echo", 8) == 0) + ; /* do nothing */ + else + return (1); + + return (0); +} + +static int +cm_setprop_reload(struct ctrl_msg_pl *cp) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + set_do_reload_ifname(cp->cp_ifname); + set_do_reload(1); + + return (0); +} + +static int +cm_setprop_enable(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + ifi->ifi_persist = 1; + set_do_reload_ifname(ifi->ifi_ifname); + set_do_reload(0); + + return (0); +} + +static int +cm_setprop_disable(struct ctrl_msg_pl *cp) +{ + struct ifinfo *ifi; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + TAILQ_FOREACH(ifi, &ifilist, ifi_next) { + if (strcmp(cp->cp_ifname, ifi->ifi_ifname) == 0) + break; + } + if (ifi == NULL) { + syslog(LOG_ERR, "<%s> %s not found", __func__, + cp->cp_ifname); + return (1); + } + + if (ifi->ifi_persist == 1) { + ifi->ifi_persist = 0; + rm_ifinfo(ifi); + + /* MC leaving needed here */ + sock_mc_leave(&sock, ifi->ifi_ifindex); + + set_do_reload_ifname(ifi->ifi_ifname); + set_do_reload(0); + } + + return (0); +} + +int +cm_handler_server(int fd) +{ + int state; + char *msg; + struct ctrl_msg_hdr *cm; + struct ctrl_msg_pl cp; + char buf[CM_MSG_MAXLEN]; + char pbuf[CM_MSG_MAXLEN]; + int error; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + memset(buf, 0, sizeof(buf)); + memset(pbuf, 0, sizeof(pbuf)); + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + state = CM_STATE_INIT; + while (state != CM_STATE_EOM) { + syslog(LOG_DEBUG, "<%s> state = %d", __func__, state); + + switch (state) { + case CM_STATE_INIT: + state = CM_STATE_MSG_RECV; + break; + case CM_STATE_MSG_DISPATCH: + cm->cm_version = CM_VERSION; + error = cm_send(fd, buf); + if (error) + syslog(LOG_WARNING, + "<%s> cm_send()", __func__); + state = CM_STATE_EOM; + break; + case CM_STATE_ACK_WAIT: + error = cm_recv(fd, buf); + if (error) { + syslog(LOG_ERR, + "<%s> cm_recv()", __func__); + close(fd); + return (-1); + } + + switch (cm->cm_type) { + case CM_TYPE_ACK: + break; + case CM_TYPE_ERR: + syslog(LOG_DEBUG, + "<%s> CM_TYPE_ERR", __func__); + close(fd); + return (-1); + default: + syslog(LOG_DEBUG, + "<%s> unknown status", __func__); + close(fd); + return (-1); + } + state = CM_STATE_EOM; + break; + case CM_STATE_MSG_RECV: + error = cm_recv(fd, buf); + + if (error) { + syslog(LOG_ERR, + "<%s> cm_recv()", __func__); + close(fd); + return (-1); + } + memset(&cp, 0, sizeof(cp)); + + syslog(LOG_DEBUG, + "<%s> cm->cm_type = %d", __func__, cm->cm_type); + syslog(LOG_DEBUG, + "<%s> cm->cm_len = %zu", __func__, cm->cm_len); + + switch (cm->cm_type) { + case CM_TYPE_EOM: + state = CM_STATE_EOM; + case CM_TYPE_NUL: + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + break; + case CM_TYPE_REQ_GET_PROP: + cm_bin2pl(msg, &cp); + error = cm_getprop(&cp); + if (error) { + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } else { + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + cm->cm_len += cm_pl2bin(msg, &cp); + } + if (cp.cp_val != NULL) + free(cp.cp_val); + break; + case CM_TYPE_REQ_SET_PROP: + cm_bin2pl(msg, &cp); + error = cm_setprop(&cp); + if (error) { + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } else { + cm->cm_type = CM_TYPE_ACK; + cm->cm_len = sizeof(*cm); + } + break; + default: + cm->cm_type = CM_TYPE_ERR; + cm->cm_len = sizeof(*cm); + } + + switch (cm->cm_type) { + case CM_TYPE_ERR: + case CM_TYPE_ACK: + state = CM_STATE_MSG_DISPATCH; + break; + } + } + } + syslog(LOG_DEBUG, "<%s> leave", __func__); + + return (0); +} |