Hiroki Sato 3724189620 - Improve interface list handling. The rtadvd(8) now supports dynamically-
added/removed interfaces in a more consistent manner and reloading the
  configuration file.

- Implement burst unsolicited RA sending into the internal RA timer framework
  when AdvSendAdvertisements and/or configuration entries are changed as
  described in RFC 4861 6.2.4.  This fixes issues that make termination of the
  rtadvd(8) daemon take very long time.

  An interface now has three internal states, UNCONFIGURED, TRANSITIVE, or
  CONFIGURED, and the burst unsolicited sending happens in TRANSITIVE.
  See rtadvd.h for the details.

- rtadvd(8) now accepts non-existent interfaces as well in the command line.

- Add control socket support and rtadvctl(8) utility to show the RA information
  in rtadvd(8).  Dumping by SIGUSR1 has been removed in favor of it.
2011-07-17 19:24:54 +00:00

457 lines
10 KiB
C

/*-
* 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);
}