diff options
Diffstat (limited to 'usr.sbin/rtadvd/control.c')
-rw-r--r-- | usr.sbin/rtadvd/control.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/usr.sbin/rtadvd/control.c b/usr.sbin/rtadvd/control.c new file mode 100644 index 0000000..709fae3 --- /dev/null +++ b/usr.sbin/rtadvd/control.c @@ -0,0 +1,456 @@ +/*- + * 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 "rtadvd.h" +#include "if.h" +#include "pathnames.h" +#include "control.h" + +int +cmsg_recv(int fd, char *buf) +{ + int n; + struct ctrl_msg_hdr *cm; + char *msg; + + syslog(LOG_DEBUG, "<%s> enter, fd=%d", __func__, fd); + + memset(buf, 0, CM_MSG_MAXLEN); + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + for (;;) { + n = read(fd, cm, sizeof(*cm)); + if (n < 0 && errno == EAGAIN) { + syslog(LOG_DEBUG, + "<%s> waiting...", __func__); + continue; + } + break; + } + + if (n != sizeof(*cm)) { + syslog(LOG_WARNING, + "<%s> received a too small message.", __func__); + goto cmsg_recv_err; + } + if (cm->cm_len > CM_MSG_MAXLEN) { + syslog(LOG_WARNING, + "<%s> received a too large message.", __func__); + goto cmsg_recv_err; + } + if (cm->cm_version != CM_VERSION) { + syslog(LOG_WARNING, + "<%s> version mismatch", __func__); + goto cmsg_recv_err; + } + if (cm->cm_type >= CM_TYPE_MAX) { + syslog(LOG_WARNING, + "<%s> invalid msg type.", __func__); + goto cmsg_recv_err; + } + + syslog(LOG_DEBUG, + "<%s> ctrl msg received: type=%d", __func__, + cm->cm_type); + + if (cm->cm_len > sizeof(cm)) { + int msglen = cm->cm_len - sizeof(*cm); + + syslog(LOG_DEBUG, + "<%s> ctrl msg has payload (len=%d)", __func__, + msglen); + + for (;;) { + n = read(fd, msg, msglen); + if (n < 0 && errno == EAGAIN) { + syslog(LOG_DEBUG, + "<%s> waiting...", __func__); + continue; + } + break; + } + if (n != msglen) { + syslog(LOG_WARNING, + "<%s> payload size mismatch.", __func__); + goto cmsg_recv_err; + } + buf[CM_MSG_MAXLEN - 1] = '\0'; + } + + return (0); + +cmsg_recv_err: + close(fd); + return (-1); +} + +int +cmsg_send(int fd, char *buf) +{ + struct iovec iov[2]; + int iovcnt; + ssize_t len; + ssize_t iov_len_total; + struct ctrl_msg_hdr *cm; + char *msg; + + cm = (struct ctrl_msg_hdr *)buf; + msg = (char *)buf + sizeof(*cm); + + iovcnt = 1; + iov[0].iov_base = cm; + iov[0].iov_len = sizeof(*cm); + iov_len_total = iov[0].iov_len; + if (cm->cm_len > sizeof(*cm)) { + iovcnt++; + iov[1].iov_base = msg; + iov[1].iov_len = cm->cm_len - iov[0].iov_len; + iov_len_total += iov[1].iov_len; + } + + syslog(LOG_DEBUG, + "<%s> ctrl msg send: type=%d, count=%d, total_len=%zd", __func__, + cm->cm_type, iovcnt, iov_len_total); + + len = writev(fd, iov, iovcnt); + syslog(LOG_DEBUG, + "<%s> ctrl msg send: length=%zd", __func__, len); + + if (len == -1) { + syslog(LOG_DEBUG, + "<%s> write failed: (%d)%s", __func__, errno, + strerror(errno)); + close(fd); + return (-1); + } + + syslog(LOG_DEBUG, + "<%s> write length = %zd (actual)", __func__, len); + syslog(LOG_DEBUG, + "<%s> write length = %zd (expected)", __func__, iov_len_total); + + if (len != iov_len_total) { + close(fd); + return (-1); + } + + return (0); +} + +int +csock_accept(struct sockinfo *s) +{ + struct sockaddr_un sun; + int flags; + int fd; + + sun.sun_len = sizeof(sun); + if ((fd = accept(s->si_fd, (struct sockaddr *)&sun, + (socklen_t *)&sun.sun_len)) == -1) { + if (errno != EWOULDBLOCK && errno != EINTR) + syslog(LOG_WARNING, "<%s> accept ", __func__); + syslog(LOG_WARNING, "<%s> Xaccept: %s", __func__, strerror(errno)); + return (-1); + } + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) { + syslog(LOG_WARNING, "<%s> fcntl F_GETFL", __func__); + close(s->si_fd); + return (-1); + } + if ((flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) { + syslog(LOG_WARNING, "<%s> fcntl F_SETFL", __func__); + return (-1); + } + syslog(LOG_DEBUG, "<%s> accept connfd=%d, listenfd=%d", __func__, + fd, s->si_fd); + + return (fd); +} + +int +csock_close(struct sockinfo *s) +{ + close(s->si_fd); + unlink(s->si_name); + syslog(LOG_DEBUG, "<%s> remove %s", __func__, s->si_name); + return (0); +} + +int +csock_listen(struct sockinfo *s) +{ + if (s->si_fd == -1) { + syslog(LOG_ERR, "<%s> listen failed", __func__); + return (-1); + } + if (listen(s->si_fd, SOCK_BACKLOG) == -1) { + syslog(LOG_ERR, "<%s> listen failed", __func__); + return (-1); + } + + return (0); +} + +int +csock_open(struct sockinfo *s, mode_t mode) +{ + int flags; + struct sockaddr_un sun; + mode_t old_umask; + + if (s == NULL) { + syslog(LOG_ERR, "<%s> internal error.", __func__); + exit(1); + } + if (s->si_name == NULL) + s->si_name = _PATH_CTRL_SOCK; + + if ((s->si_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { + syslog(LOG_ERR, + "<%s> cannot open control socket", __func__); + return (-1); + } + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, s->si_name, sizeof(sun.sun_path)); + + if (unlink(s->si_name) == -1) + if (errno != ENOENT) { + syslog(LOG_ERR, + "<%s> unlink %s", __func__, s->si_name); + close(s->si_fd); + return (-1); + } + old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); + if (bind(s->si_fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + syslog(LOG_ERR, + "<%s> bind failed: %s", __func__, s->si_name); + close(s->si_fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + if (chmod(s->si_name, mode) == -1) { + syslog(LOG_ERR, + "<%s> chmod failed: %s", __func__, s->si_name); + goto csock_open_err; + } + if ((flags = fcntl(s->si_fd, F_GETFL, 0)) == -1) { + syslog(LOG_ERR, + "<%s> fcntl F_GETFL failed: %s", __func__, s->si_name); + goto csock_open_err; + } + if ((flags = fcntl(s->si_fd, F_SETFL, flags | O_NONBLOCK)) == -1) { + syslog(LOG_ERR, + "<%s> fcntl F_SETFL failed: %s", __func__, s->si_name); + goto csock_open_err; + } + + return (s->si_fd); + +csock_open_err: + close(s->si_fd); + unlink(s->si_name); + return (-1); +} + +struct ctrl_msg_pl * +cmsg_bin2pl(char *str, struct ctrl_msg_pl *cp) +{ + size_t len; + size_t *lenp; + char *p; + + memset(cp, 0, sizeof(*cp)); + + p = str; + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(ifname) = %zu", __func__, len); + if (len > 0) { + cp->cp_ifname = malloc(len + 1); + if (cp->cp_ifname == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_ifname, p, len); + cp->cp_ifname[len] = '\0'; + p += len; + } + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(key) = %zu", __func__, len); + if (len > 0) { + cp->cp_key = malloc(len + 1); + if (cp->cp_key == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_key, p, len); + cp->cp_key[len] = '\0'; + p += len; + } + + lenp = (size_t *)p; + len = *lenp++; + p = (char *)lenp; + syslog(LOG_DEBUG, "<%s> len(val) = %zu", __func__, len); + if (len > 0) { + cp->cp_val = malloc(len + 1); + if (cp->cp_val == NULL) { + syslog(LOG_ERR, "<%s> malloc", __func__); + exit(1); + } + memcpy(cp->cp_val, p, len); + cp->cp_val[len] = '\0'; + cp->cp_val_len = len; + } else + cp->cp_val_len = 0; + + return (cp); +} + +size_t +cmsg_pl2bin(char *str, struct ctrl_msg_pl *cp) +{ + size_t len; + size_t *lenp; + char *p; + struct ctrl_msg_hdr *cm; + + len = sizeof(size_t); + if (cp->cp_ifname != NULL) + len += strlen(cp->cp_ifname); + len += sizeof(size_t); + if (cp->cp_key != NULL) + len += strlen(cp->cp_key); + len += sizeof(size_t); + if (cp->cp_val != NULL && cp->cp_val_len > 0) + len += cp->cp_val_len; + + if (len > CM_MSG_MAXLEN - sizeof(*cm)) { + syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)", + __func__, len); + return (0); + } + syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len); + memset(str, 0, len); + p = str; + lenp = (size_t *)p; + + if (cp->cp_ifname != NULL) { + *lenp++ = strlen(cp->cp_ifname); + p = (char *)lenp; + memcpy(p, cp->cp_ifname, strlen(cp->cp_ifname)); + p += strlen(cp->cp_ifname); + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + lenp = (size_t *)p; + if (cp->cp_key != NULL) { + *lenp++ = strlen(cp->cp_key); + p = (char *)lenp; + memcpy(p, cp->cp_key, strlen(cp->cp_key)); + p += strlen(cp->cp_key); + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + lenp = (size_t *)p; + if (cp->cp_val != NULL && cp->cp_val_len > 0) { + *lenp++ = cp->cp_val_len; + p = (char *)lenp; + memcpy(p, cp->cp_val, cp->cp_val_len); + p += cp->cp_val_len; + } else { + *lenp++ = '\0'; + p = (char *)lenp; + } + + return (len); +} + +size_t +cmsg_str2bin(char *bin, void *str, size_t len) +{ + struct ctrl_msg_hdr *cm; + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + if (len > CM_MSG_MAXLEN - sizeof(*cm)) { + syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)", + __func__, len); + return (0); + } + syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len); + memcpy(bin, (char *)str, len); + + return (len); +} + +void * +cmsg_bin2str(char *bin, void *str, size_t len) +{ + + syslog(LOG_DEBUG, "<%s> enter", __func__); + + memcpy((char *)str, bin, len); + + return (str); +} |