summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrenato <renato@openbsd.org>2015-10-02 04:26:47 +0000
committerrenato <renato@openbsd.org>2015-10-02 04:26:47 +0000
commit43509a120f2aeb0a26f1a8ca43fb60460ab80687 (patch)
tree4f83eebf70586b5a95973b8457bf1121d5d81d62
parentmention these pathname calls are checked in namei (diff)
downloadwireguard-openbsd-43509a120f2aeb0a26f1a8ca43fb60460ab80687.tar.xz
wireguard-openbsd-43509a120f2aeb0a26f1a8ca43fb60460ab80687.zip
Welcome eigrpd
The eigrpd daemon will support the Enhanced Interior Gateway Routing Protocol. Built using the imsg/three process framework and heavily based on ospfd(8), ospf6d(8) and ldpd(8). The current status of eigrpd(8) is as follows: * Almost full compliance with the specification: DUAL FSM, RTP, CR mode, SIA, etc * Support for both IPv4 and IPv6 * Support for multiple instances (different ASes/AFs) within the same process * Support for rdomains (one process per rdomain) * RIB/FIB synchronization * Basic redistribution support Not implemented features (yet): * Configuration reload support (partially implemented) * Route summarization * Advanced route redistribution/filtering * Carp integration * Authentication (draft is missing information) * Stub (not released by Cisco) Not yet connected to the builds. ok deraadt@ claudio@
-rw-r--r--usr.sbin/eigrpd/Makefile19
-rw-r--r--usr.sbin/eigrpd/control.c315
-rw-r--r--usr.sbin/eigrpd/control.h44
-rw-r--r--usr.sbin/eigrpd/eigrp.h210
-rw-r--r--usr.sbin/eigrpd/eigrpd.8109
-rw-r--r--usr.sbin/eigrpd/eigrpd.c657
-rw-r--r--usr.sbin/eigrpd/eigrpd.conf.50
-rw-r--r--usr.sbin/eigrpd/eigrpd.h447
-rw-r--r--usr.sbin/eigrpd/eigrpe.c678
-rw-r--r--usr.sbin/eigrpd/eigrpe.h200
-rw-r--r--usr.sbin/eigrpd/hello.c102
-rw-r--r--usr.sbin/eigrpd/in_cksum.c82
-rw-r--r--usr.sbin/eigrpd/interface.c753
-rw-r--r--usr.sbin/eigrpd/kroute.c1588
-rw-r--r--usr.sbin/eigrpd/log.c353
-rw-r--r--usr.sbin/eigrpd/log.h46
-rw-r--r--usr.sbin/eigrpd/neighbor.c269
-rw-r--r--usr.sbin/eigrpd/packet.c729
-rw-r--r--usr.sbin/eigrpd/parse.y1165
-rw-r--r--usr.sbin/eigrpd/printconf.c162
-rw-r--r--usr.sbin/eigrpd/query.c114
-rw-r--r--usr.sbin/eigrpd/rde.c764
-rw-r--r--usr.sbin/eigrpd/rde.h196
-rw-r--r--usr.sbin/eigrpd/rde_dual.c1245
-rw-r--r--usr.sbin/eigrpd/reply.c120
-rw-r--r--usr.sbin/eigrpd/rtp.c309
-rw-r--r--usr.sbin/eigrpd/tlv.c480
-rw-r--r--usr.sbin/eigrpd/update.c145
-rw-r--r--usr.sbin/eigrpd/util.c238
29 files changed, 11539 insertions, 0 deletions
diff --git a/usr.sbin/eigrpd/Makefile b/usr.sbin/eigrpd/Makefile
new file mode 100644
index 00000000000..9768067adae
--- /dev/null
+++ b/usr.sbin/eigrpd/Makefile
@@ -0,0 +1,19 @@
+# $OpenBSD: Makefile,v 1.1 2015/10/02 04:26:47 renato Exp $
+
+PROG= eigrpd
+SRCS= control.c eigrpd.c eigrpe.c hello.c in_cksum.c interface.c \
+ kroute.c log.c neighbor.c packet.c parse.y printconf.c query.c \
+ rde.c rde_dual.c reply.c rtp.c tlv.c update.c util.c
+
+MAN= eigrpd.8 eigrpd.conf.5
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+YFLAGS=
+LDADD+= -levent -lutil
+DPADD+= ${LIBEVENT} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/eigrpd/control.c b/usr.sbin/eigrpd/control.c
new file mode 100644
index 00000000000..a62a7d8518c
--- /dev/null
+++ b/usr.sbin/eigrpd/control.c
@@ -0,0 +1,315 @@
+/* $OpenBSD: control.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "eigrpe.h"
+#include "log.h"
+#include "control.h"
+
+#define CONTROL_BACKLOG 5
+
+struct ctl_conn *control_connbyfd(int);
+struct ctl_conn *control_connbypid(pid_t);
+void control_close(int);
+
+int
+control_init(char *path)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+
+ if (unlink(path) == -1)
+ if (errno != ENOENT) {
+ log_warn("%s: unlink %s", __func__, path);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("%s: bind: %s", __func__, path);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("%s: chmod", __func__);
+ close(fd);
+ (void)unlink(path);
+ return (-1);
+ }
+
+ control_state.fd = fd;
+
+ return (0);
+}
+
+int
+control_listen(void)
+{
+
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+ log_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ event_set(&control_state.ev, control_state.fd, EV_READ,
+ control_accept, NULL);
+ event_add(&control_state.ev, NULL);
+ evtimer_set(&control_state.evt, control_accept, NULL);
+
+ return (0);
+}
+
+void
+control_cleanup(char *path)
+{
+ if (path == NULL)
+ return;
+ event_del(&control_state.ev);
+ event_del(&control_state.evt);
+ unlink(path);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *bula)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct ctl_conn *c;
+
+ event_add(&control_state.ev, NULL);
+ if ((event & EV_TIMEOUT))
+ return;
+
+ len = sizeof(sun);
+ if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len,
+ SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) {
+ /*
+ * Pause accept if we are out of file descriptors, or
+ * libevent will haunt us here too.
+ */
+ if (errno == ENFILE || errno == EMFILE) {
+ struct timeval evtpause = { 1, 0 };
+
+ event_del(&control_state.ev);
+ evtimer_add(&control_state.evt, &evtpause);
+ } else if (errno != EWOULDBLOCK && errno != EINTR &&
+ errno != ECONNABORTED)
+ log_warn("%s: accept4", __func__);
+ return;
+ }
+
+ if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+ log_warn("%s: calloc", __func__);
+ close(connfd);
+ return;
+ }
+
+ imsg_init(&c->iev.ibuf, connfd);
+ c->iev.handler = control_dispatch_imsg;
+ c->iev.events = EV_READ;
+ event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
+ c->iev.handler, &c->iev);
+ event_add(&c->iev.ev, NULL);
+
+ TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+}
+
+struct ctl_conn *
+control_connbyfd(int fd)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+struct ctl_conn *
+control_connbypid(pid_t pid)
+{
+ struct ctl_conn *c;
+
+ for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.pid != pid;
+ c = TAILQ_NEXT(c, entry))
+ ; /* nothing */
+
+ return (c);
+}
+
+void
+control_close(int fd)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ msgbuf_clear(&c->iev.ibuf.w);
+ TAILQ_REMOVE(&ctl_conns, c, entry);
+
+ event_del(&c->iev.ev);
+ close(c->iev.ibuf.fd);
+
+ /* Some file descriptors are available again. */
+ if (evtimer_pending(&control_state.evt, NULL)) {
+ evtimer_del(&control_state.evt);
+ event_add(&control_state.ev, NULL);
+ }
+
+ free(c);
+}
+
+/* ARGSUSED */
+void
+control_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct ctl_conn *c;
+ struct imsg imsg;
+ ssize_t n;
+ unsigned int ifidx;
+ int verbose;
+
+ if ((c = control_connbyfd(fd)) == NULL) {
+ log_warnx("%s: fd %d: not found", __func__, fd);
+ return;
+ }
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
+ control_close(fd);
+ return;
+ }
+ }
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
+ control_close(fd);
+ return;
+ }
+ }
+
+ for (;;) {
+ if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
+ control_close(fd);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_FIB_COUPLE:
+ case IMSG_CTL_FIB_DECOUPLE:
+ case IMSG_CTL_RELOAD:
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ eigrpe_imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_IFINFO:
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ eigrpe_imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_INTERFACE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(ifidx))
+ break;
+
+ memcpy(&ifidx, imsg.data, sizeof(ifidx));
+ eigrpe_iface_ctl(c, ifidx);
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0,
+ 0, -1, NULL, 0);
+ break;
+ case IMSG_CTL_SHOW_TOPOLOGY:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ctl_show_topology_req))
+ break;
+
+ c->iev.ibuf.pid = imsg.hdr.pid;
+ eigrpe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_CTL_SHOW_NBR:
+ eigrpe_nbr_ctl(c);
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(verbose))
+ break;
+
+ /* forward to other processes */
+ eigrpe_imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ eigrpe_imsg_compose_rde(imsg.hdr.type, 0, imsg.hdr.pid,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+
+ imsg_event_add(&c->iev);
+}
+
+int
+control_imsg_relay(struct imsg *imsg)
+{
+ struct ctl_conn *c;
+
+ if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
+ return (0);
+
+ return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
+ -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
+}
diff --git a/usr.sbin/eigrpd/control.h b/usr.sbin/eigrpd/control.h
new file mode 100644
index 00000000000..608aedc0845
--- /dev/null
+++ b/usr.sbin/eigrpd/control.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: control.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _CONTROL_H_
+#define _CONTROL_H_
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <event.h>
+
+struct {
+ struct event ev;
+ struct event evt;
+ int fd;
+} control_state;
+
+struct ctl_conn {
+ TAILQ_ENTRY(ctl_conn) entry;
+ struct imsgev iev;
+};
+
+int control_init(char *);
+int control_listen(void);
+void control_accept(int, short, void *);
+void control_dispatch_imsg(int, short, void *);
+int control_imsg_relay(struct imsg *);
+void control_cleanup(char *);
+
+#endif /* _CONTROL_H_ */
diff --git a/usr.sbin/eigrpd/eigrp.h b/usr.sbin/eigrpd/eigrp.h
new file mode 100644
index 00000000000..b6bd3cfd06e
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrp.h
@@ -0,0 +1,210 @@
+/* $OpenBSD: eigrp.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* EIGRP protocol definitions */
+
+#ifndef _EIGRP_H_
+#define _EIGRP_H_
+
+#include <netinet/in.h>
+#include <stddef.h>
+
+/* misc */
+#define EIGRP_VERSION 2
+#define IPPROTO_EIGRP 88
+#define AllEIGRPRouters_v4 0xa0000e0 /* network byte order */
+#define AllEIGRPRouters_v6 "ff02::a"
+#define EIGRP_IP_TTL 2
+
+#define EIGRP_INFINITE_METRIC ((uint32_t )(~0))
+
+#define RTP_RTRNS_INTERVAL 5
+#define RTP_RTRNS_MAX_ATTEMPTS 16
+
+#define RTP_ACK_TIMEOUT 100000
+
+#define EIGRP_ACTIVE_TIMEOUT 180
+
+#define EIGRP_VERSION_MAJOR 1
+#define EIGRP_VERSION_MINOR 2
+
+#define EIGRP_MIN_AS 1
+#define EIGRP_MAX_AS 65535
+
+#define DEFAULT_HELLO_INTERVAL 5
+#define MIN_HELLO_INTERVAL 1
+#define MAX_HELLO_INTERVAL 65535
+
+#define DEFAULT_HELLO_HOLDTIME 15
+#define MIN_HELLO_HOLDTIME 1
+#define MAX_HELLO_HOLDTIME 65535
+
+#define EIGRP_SCALING_FACTOR 256
+
+#define DEFAULT_DELAY 10
+#define MIN_DELAY 1
+#define MAX_DELAY 16777215
+
+#define DEFAULT_BANDWIDTH 100000
+#define MIN_BANDWIDTH 1
+#define MAX_BANDWIDTH 10000000
+
+#define DEFAULT_RELIABILITY 255
+#define MIN_RELIABILITY 1
+#define MAX_RELIABILITY 255
+
+#define DEFAULT_LOAD 1
+#define MIN_LOAD 1
+#define MAX_LOAD 255
+
+#define MIN_MTU 1
+#define MAX_MTU 65535
+
+#define MIN_KVALUE 0
+#define MAX_KVALUE 254
+
+#define DEFAULT_MAXIMUM_HOPS 100
+#define MIN_MAXIMUM_HOPS 1
+#define MAX_MAXIMUM_HOPS 255
+
+#define DEFAULT_MAXIMUM_PATHS 4
+#define MIN_MAXIMUM_PATHS 1
+#define MAX_MAXIMUM_PATHS 32
+
+#define DEFAULT_VARIANCE 1
+#define MIN_VARIANCE 1
+#define MAX_VARIANCE 128
+
+#define EIGRP_HEADER_VERSION 2
+
+#define EIGRP_VRID_UNICAST_AF 0x0000
+#define EIGRP_VRID_MULTICAST_AF 0x0001
+#define EIGRP_VRID_UNICAST_SF 0x8000
+
+/* EIGRP packet types */
+#define EIGRP_OPC_UPDATE 1
+#define EIGRP_OPC_REQUEST 2
+#define EIGRP_OPC_QUERY 3
+#define EIGRP_OPC_REPLY 4
+#define EIGRP_OPC_HELLO 5
+#define EIGRP_OPC_PROBE 7
+#define EIGRP_OPC_SIAQUERY 10
+#define EIGRP_OPC_SIAREPLY 11
+
+struct eigrp_hdr {
+ uint8_t version;
+ uint8_t opcode;
+ uint16_t chksum;
+ uint32_t flags;
+ uint32_t seq_num;
+ uint32_t ack_num;
+ uint16_t vrid;
+ uint16_t as;
+};
+/* EIGRP header flags */
+#define EIGRP_HDR_FLAG_INIT 0x01
+#define EIGRP_HDR_FLAG_CR 0x02
+#define EIGRP_HDR_FLAG_RS 0x04
+#define EIGRP_HDR_FLAG_EOT 0x08
+
+/* TLV record */
+struct tlv {
+ uint16_t type;
+ uint16_t length;
+};
+#define TLV_HDR_LEN 4
+
+struct tlv_parameter {
+ uint16_t type;
+ uint16_t length;
+ uint8_t kvalues[6];
+ uint16_t holdtime;
+};
+
+struct tlv_sw_version {
+ uint16_t type;
+ uint16_t length;
+ uint8_t vendor_os_major;
+ uint8_t vendor_os_minor;
+ uint8_t eigrp_major;
+ uint8_t eigrp_minor;
+};
+
+struct tlv_mcast_seq {
+ uint16_t type;
+ uint16_t length;
+ uint32_t seq;
+};
+
+struct classic_metric {
+ uint32_t delay;
+ uint32_t bandwidth;
+ uint8_t mtu[3]; /* 3 bytes, yeah... */
+ uint8_t hop_count;
+ uint8_t reliability;
+ uint8_t load;
+ uint8_t tag;
+ uint8_t flags;
+};
+#define F_METRIC_SRC_WITHDRAW 0x01
+#define F_METRIC_C_DEFAULT 0x02
+#define F_METRIC_ACTIVE 0x04
+
+struct classic_emetric {
+ uint32_t routerid;
+ uint32_t as;
+ uint32_t tag;
+ uint32_t metric;
+ uint16_t reserved;
+ uint8_t protocol;
+ uint8_t flags;
+};
+
+#define EIGRP_EXT_PROTO_IGRP 1
+#define EIGRP_EXT_PROTO_EIGRP 2
+#define EIGRP_EXT_PROTO_STATIC 3
+#define EIGRP_EXT_PROTO_RIP 4
+#define EIGRP_EXT_PROTO_HELLO 5
+#define EIGRP_EXT_PROTO_OSPF 6
+#define EIGRP_EXT_PROTO_ISIS 7
+#define EIGRP_EXT_PROTO_EGP 8
+#define EIGRP_EXT_PROTO_BGP 9
+#define EIGRP_EXT_PROTO_IDRP 10
+#define EIGRP_EXT_PROTO_CONN 11
+
+/* EIGRP TLV types */
+#define TLV_TYPE_PARAMETER 0x0001
+#define TLV_TYPE_AUTH 0x0002
+#define TLV_TYPE_SEQ 0x0003
+#define TLV_TYPE_SW_VERSION 0x0004
+#define TLV_TYPE_MCAST_SEQ 0x0005
+#define TLV_TYPE_PEER_TERM 0x0007
+#define TLV_TYPE_IPV4_INTERNAL 0x0102
+#define TLV_TYPE_IPV4_EXTERNAL 0x0103
+#define TLV_TYPE_IPV4_COMMUNITY 0x0104
+#define TLV_TYPE_IPV6_INTERNAL 0x0402
+#define TLV_TYPE_IPV6_EXTERNAL 0x0403
+#define TLV_TYPE_IPV6_COMMUNITY 0x0404
+
+#define TLV_TYPE_PARAMETER_LEN 0x000C
+#define TLV_TYPE_SW_VERSION_LEN 0x0008
+#define TLV_TYPE_MCAST_SEQ_LEN 0x0008
+#define TLV_TYPE_IPV4_INT_MIN_LEN 0x0019
+#define TLV_TYPE_IPV6_INT_MIN_LEN 0x0025
+
+#endif /* _EIGRP_H_ */
diff --git a/usr.sbin/eigrpd/eigrpd.8 b/usr.sbin/eigrpd/eigrpd.8
new file mode 100644
index 00000000000..f157edd118d
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpd.8
@@ -0,0 +1,109 @@
+.\" $OpenBSD: eigrpd.8,v 1.1 2015/10/02 04:26:47 renato Exp $
+.\"
+.\" Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+.\" Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: October 2 2015 $
+.Dt EIGRPD 8
+.Os
+.Sh NAME
+.Nm eigrpd
+.Nd Enhanced Interior Gateway Routing Protocol daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnv
+.Op Fl D Ar macro Ns = Ns Ar value
+.Op Fl f Ar file
+.Op Fl s Ar socket
+.Sh DESCRIPTION
+.Nm
+is an Enhanced Interior Gateway Routing Protocol
+.Pq EIGRP
+daemon which manages routing tables.
+EIGRP is a routing protocol based on Distance Vector technology.
+.Pp
+.Nm
+is usually started at boot time, and can be enabled by
+setting the following in
+.Pa /etc/rc.conf.local :
+.Pp
+.Dl eigrpd_flags=\&"\&"
+.Pp
+See
+.Xr rc 8
+and
+.Xr rc.conf 8
+for more information on the boot process
+and enabling daemons.
+.Pp
+A running
+.Nm
+can be controlled with the
+.Xr eigrpctl 8
+utility.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the configuration file.
+.It Fl d
+Do not daemonize.
+If this option is specified,
+.Nm
+will run in the foreground and log to
+.Em stderr .
+.It Fl f Ar file
+Specify an alternative configuration file.
+.It Fl n
+Configtest mode.
+Only check the configuration file for validity.
+.It Fl s Ar socket
+Use an alternate location for the default control socket.
+.It Fl v
+Produce more verbose output.
+.El
+.Sh FILES
+.Bl -tag -width "/var/run/eigrpd.sockXX" -compact
+.It Pa /etc/eigrpd.conf
+Default
+.Nm
+configuration file.
+.It Pa /var/run/eigrpd.sock
+.Ux Ns -domain
+socket used for communication with
+.Xr eigrpctl 8 .
+.El
+.Sh SEE ALSO
+.Xr eigrpd.conf 5 ,
+.Xr eigrpctl 8
+.Sh STANDARDS
+.Rs
+.%A Savage, et al.
+.%D April, 2014
+.%R draft-savage-eigrp-02
+.%T Enhanced Interior Gateway Routing Protocol
+.Re
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Ox 5.8 .
diff --git a/usr.sbin/eigrpd/eigrpd.c b/usr.sbin/eigrpd/eigrpd.c
new file mode 100644
index 00000000000..6d4191bb003
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpd.c
@@ -0,0 +1,657 @@
+/* $OpenBSD: eigrpd.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "eigrpe.h"
+#include "control.h"
+#include "log.h"
+#include "rde.h"
+
+void main_sig_handler(int, short, void *);
+__dead void usage(void);
+void eigrpd_shutdown(void);
+int check_child(pid_t, const char *);
+
+void main_dispatch_eigrpe(int, short, void *);
+void main_dispatch_rde(int, short, void *);
+
+int eigrp_reload(void);
+int eigrp_sendboth(enum imsg_type, void *, uint16_t);
+void merge_instances(struct eigrpd_conf *, struct eigrp *, struct eigrp *);
+
+int pipe_parent2eigrpe[2];
+int pipe_parent2rde[2];
+int pipe_eigrpe2rde[2];
+
+struct eigrpd_conf *eigrpd_conf = NULL;
+struct imsgev *iev_eigrpe;
+struct imsgev *iev_rde;
+char *conffile;
+
+pid_t eigrpe_pid = 0;
+pid_t rde_pid = 0;
+
+/* ARGSUSED */
+void
+main_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ int die = 0;
+
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ die = 1;
+ /* FALLTHROUGH */
+ case SIGCHLD:
+ if (check_child(eigrpe_pid, "eigrp engine")) {
+ eigrpe_pid = 0;
+ die = 1;
+ }
+ if (check_child(rde_pid, "route decision engine")) {
+ rde_pid = 0;
+ die = 1;
+ }
+ if (die)
+ eigrpd_shutdown();
+ break;
+ case SIGHUP:
+ if (eigrp_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ default:
+ fatalx("unexpected signal");
+ /* NOTREACHED */
+ }
+}
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-dnv] [-D macro=value]"
+ " [-f file] [-s socket]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int ch, opts = 0;
+ int debug = 0;
+ int ipforwarding;
+ int mib[4];
+ size_t len;
+ char *sockname;
+
+ conffile = CONF_FILE;
+ eigrpd_process = PROC_MAIN;
+ sockname = EIGRPD_SOCKET;
+
+ log_init(1); /* log to stderr until daemonized */
+ log_verbose(1);
+
+ while ((ch = getopt(argc, argv, "dD:f:ns:v")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'D':
+ if (cmdline_symset(optarg) < 0)
+ log_warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'n':
+ opts |= EIGRPD_OPT_NOACTION;
+ break;
+ case 's':
+ sockname = optarg;
+ break;
+ case 'v':
+ if (opts & EIGRPD_OPT_VERBOSE)
+ opts |= EIGRPD_OPT_VERBOSE2;
+ opts |= EIGRPD_OPT_VERBOSE;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc > 0)
+ usage();
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET;
+ mib[2] = IPPROTO_IP;
+ mib[3] = IPCTL_FORWARDING;
+ len = sizeof(ipforwarding);
+ if (sysctl(mib, 4, &ipforwarding, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+
+ if (ipforwarding != 1)
+ log_warnx("WARNING: IP forwarding NOT enabled");
+
+ /* fetch interfaces early */
+ kif_init();
+
+ /* parse config file */
+ if ((eigrpd_conf = parse_config(conffile, opts)) == NULL) {
+ kif_clear();
+ exit(1);
+ }
+ eigrpd_conf->csock = sockname;
+
+ if (eigrpd_conf->opts & EIGRPD_OPT_NOACTION) {
+ if (eigrpd_conf->opts & EIGRPD_OPT_VERBOSE)
+ print_config(eigrpd_conf);
+ else
+ fprintf(stderr, "configuration OK\n");
+ kif_clear();
+ exit(0);
+ }
+
+ /* check for root privileges */
+ if (geteuid())
+ errx(1, "need root privileges");
+
+ /* check for eigrpd user */
+ if (getpwnam(EIGRPD_USER) == NULL)
+ errx(1, "unknown user %s", EIGRPD_USER);
+
+ log_init(debug);
+ log_verbose(eigrpd_conf->opts & EIGRPD_OPT_VERBOSE);
+
+ if (!debug)
+ daemon(1, 0);
+
+ log_info("startup");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, pipe_parent2eigrpe) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, pipe_parent2rde) == -1)
+ fatal("socketpair");
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ PF_UNSPEC, pipe_eigrpe2rde) == -1)
+ fatal("socketpair");
+
+ /* start children */
+ rde_pid = rde(eigrpd_conf, pipe_parent2rde, pipe_eigrpe2rde,
+ pipe_parent2eigrpe);
+ eigrpe_pid = eigrpe(eigrpd_conf, pipe_parent2eigrpe, pipe_eigrpe2rde,
+ pipe_parent2rde);
+
+ /* show who we are */
+ setproctitle("parent");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* setup pipes to children */
+ close(pipe_parent2eigrpe[1]);
+ close(pipe_parent2rde[1]);
+ close(pipe_eigrpe2rde[0]);
+ close(pipe_eigrpe2rde[1]);
+
+ if ((iev_eigrpe = malloc(sizeof(struct imsgev))) == NULL ||
+ (iev_rde = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_eigrpe->ibuf, pipe_parent2eigrpe[0]);
+ iev_eigrpe->handler = main_dispatch_eigrpe;
+ imsg_init(&iev_rde->ibuf, pipe_parent2rde[0]);
+ iev_rde->handler = main_dispatch_rde;
+
+ /* setup event handler */
+ iev_eigrpe->events = EV_READ;
+ event_set(&iev_eigrpe->ev, iev_eigrpe->ibuf.fd, iev_eigrpe->events,
+ iev_eigrpe->handler, iev_eigrpe);
+ event_add(&iev_eigrpe->ev, NULL);
+
+ iev_rde->events = EV_READ;
+ event_set(&iev_rde->ev, iev_rde->ibuf.fd, iev_rde->events,
+ iev_rde->handler, iev_rde);
+ event_add(&iev_rde->ev, NULL);
+
+ /* notify eigrpe about existing interfaces and addresses */
+ kif_redistribute();
+
+ if (kr_init(!(eigrpd_conf->flags & EIGRPD_FLAG_NO_FIB_UPDATE),
+ eigrpd_conf->rdomain) == -1)
+ fatalx("kr_init failed");
+
+ event_dispatch();
+
+ eigrpd_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+eigrpd_shutdown(void)
+{
+ pid_t pid;
+
+ if (eigrpe_pid)
+ kill(eigrpe_pid, SIGTERM);
+
+ if (rde_pid)
+ kill(rde_pid, SIGTERM);
+
+ kr_shutdown();
+
+ do {
+ if ((pid = wait(NULL)) == -1 &&
+ errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ config_clear(eigrpd_conf);
+
+ msgbuf_clear(&iev_eigrpe->ibuf.w);
+ free(iev_eigrpe);
+ msgbuf_clear(&iev_rde->ibuf.w);
+ free(iev_rde);
+
+ log_info("terminating");
+ exit(0);
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ log_warnx("lost child: %s terminated; signal %d",
+ pname, WTERMSIG(status));
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/* imsg handling */
+/* ARGSUSED */
+void
+main_dispatch_eigrpe(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0, verbose;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_CTL_RELOAD:
+ if (eigrp_reload() == -1)
+ log_warnx("configuration reload failed");
+ else
+ log_debug("configuration reloaded");
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ kr_fib_couple();
+ break;
+ case IMSG_CTL_FIB_DECOUPLE:
+ kr_fib_decouple();
+ break;
+ case IMSG_CTL_KROUTE:
+ kr_show_route(&imsg);
+ break;
+ case IMSG_CTL_IFINFO:
+ if (imsg.hdr.len == IMSG_HEADER_SIZE)
+ kr_ifinfo(NULL, imsg.hdr.pid);
+ else if (imsg.hdr.len == IMSG_HEADER_SIZE + IFNAMSIZ)
+ kr_ifinfo(imsg.data, imsg.hdr.pid);
+ else
+ log_warnx("IFINFO request with wrong len");
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ /* already checked by eigrpe */
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+main_dispatch_rde(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+ int shut = 0;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("imsg_get");
+
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_KROUTE_CHANGE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kroute))
+ fatalx("invalid size of IMSG_KROUTE_CHANGE");
+ if (kr_change(imsg.data))
+ log_warnx("%s: error changing route", __func__);
+ break;
+ case IMSG_KROUTE_DELETE:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct kroute))
+ fatalx("invalid size of IMSG_KROUTE_DELETE");
+ if (kr_delete(imsg.data))
+ log_warnx("%s: error deleting route", __func__);
+ break;
+
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+main_imsg_compose_eigrpe(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ if (iev_eigrpe == NULL)
+ return;
+ imsg_compose_event(iev_eigrpe, type, 0, pid, -1, data, datalen);
+}
+
+void
+main_imsg_compose_rde(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ imsg_compose_event(iev_rde, type, 0, pid, -1, data, datalen);
+}
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+ iev->events = EV_READ;
+ if (iev->ibuf.w.queued)
+ iev->events |= EV_WRITE;
+
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
+ pid_t pid, int fd, void *data, uint16_t datalen)
+{
+ int ret;
+
+ if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+ pid, fd, data, datalen)) != -1)
+ imsg_event_add(iev);
+ return (ret);
+}
+
+uint32_t
+eigrp_router_id(struct eigrpd_conf *xconf)
+{
+ return (xconf->rtr_id.s_addr);
+}
+
+struct eigrp *
+eigrp_find(struct eigrpd_conf *xconf, int af, uint16_t as)
+{
+ struct eigrp *eigrp;
+
+ TAILQ_FOREACH(eigrp, &xconf->instances, entry)
+ if (eigrp->af == af && eigrp->as == as)
+ return (eigrp);
+
+ return (NULL);
+}
+
+int
+eigrp_reload(void)
+{
+ struct eigrp *eigrp;
+ struct eigrp_iface *ei;
+ struct eigrpd_conf *xconf;
+
+ if ((xconf = parse_config(conffile, eigrpd_conf->opts)) == NULL)
+ return (-1);
+
+ if (eigrp_sendboth(IMSG_RECONF_CONF, xconf, sizeof(*xconf)) == -1)
+ return (-1);
+
+ TAILQ_FOREACH(eigrp, &xconf->instances, entry) {
+ if (eigrp_sendboth(IMSG_RECONF_INSTANCE, eigrp,
+ sizeof(*eigrp)) == -1)
+ return (-1);
+
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
+ if (eigrp_sendboth(IMSG_RECONF_IFACE, ei->iface,
+ sizeof(struct iface)) == -1)
+ return (-1);
+
+ if (eigrp_sendboth(IMSG_RECONF_EIGRP_IFACE, ei,
+ sizeof(*ei)) == -1)
+ return (-1);
+ }
+ }
+
+ if (eigrp_sendboth(IMSG_RECONF_END, NULL, 0) == -1)
+ return (-1);
+
+ merge_config(eigrpd_conf, xconf);
+
+ return (0);
+}
+
+int
+eigrp_sendboth(enum imsg_type type, void *buf, uint16_t len)
+{
+ if (imsg_compose_event(iev_eigrpe, type, 0, 0, -1, buf, len) == -1)
+ return (-1);
+ if (imsg_compose_event(iev_rde, type, 0, 0, -1, buf, len) == -1)
+ return (-1);
+ return (0);
+}
+
+void
+merge_config(struct eigrpd_conf *conf, struct eigrpd_conf *xconf)
+{
+ struct eigrp *eigrp, *etmp, *xe;
+
+ /* change of rtr_id needs a restart */
+ conf->flags = xconf->flags;
+ conf->rdomain= xconf->rdomain;
+ conf->fib_priority_internal = xconf->fib_priority_internal;
+ conf->fib_priority_external = xconf->fib_priority_external;
+
+ /* merge instances */
+ TAILQ_FOREACH_SAFE(eigrp, &conf->instances, entry, etmp) {
+ /* find deleted instances */
+ if ((xe = eigrp_find(xconf, eigrp->af, eigrp->as)) == NULL) {
+ TAILQ_REMOVE(&conf->instances, eigrp, entry);
+
+ switch (eigrpd_process) {
+ case PROC_RDE_ENGINE:
+ rde_instance_del(eigrp);
+ break;
+ case PROC_EIGRP_ENGINE:
+ eigrpe_instance_del(eigrp);
+ break;
+ case PROC_MAIN:
+ free(eigrp);
+ break;
+ }
+ }
+ }
+ TAILQ_FOREACH_SAFE(xe, &xconf->instances, entry, etmp) {
+ /* find new instances */
+ if ((eigrp = eigrp_find(conf, xe->af, xe->as)) == NULL) {
+ TAILQ_REMOVE(&xconf->instances, xe, entry);
+ TAILQ_INSERT_TAIL(&conf->instances, xe, entry);
+
+ switch (eigrpd_process) {
+ case PROC_RDE_ENGINE:
+ rde_instance_init(xe);
+ break;
+ case PROC_EIGRP_ENGINE:
+ eigrpe_instance_init(xe);
+ break;
+ case PROC_MAIN:
+ break;
+ }
+ continue;
+ }
+
+ /* update existing instances */
+ merge_instances(conf, eigrp, xe);
+ }
+ /* resend addresses to activate new interfaces */
+ if (eigrpd_process == PROC_MAIN)
+ kif_redistribute();
+
+ free(xconf);
+}
+
+void
+merge_instances(struct eigrpd_conf *xconf, struct eigrp *eigrp, struct eigrp *xe)
+{
+ /* TODO */
+}
+
+void
+config_clear(struct eigrpd_conf *conf)
+{
+ struct eigrpd_conf *xconf;
+
+ /* merge current config with an empty config */
+ xconf = malloc(sizeof(*xconf));
+ memcpy(xconf, conf, sizeof(*xconf));
+ TAILQ_INIT(&xconf->instances);
+ merge_config(conf, xconf);
+
+ free(conf);
+}
diff --git a/usr.sbin/eigrpd/eigrpd.conf.5 b/usr.sbin/eigrpd/eigrpd.conf.5
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpd.conf.5
diff --git a/usr.sbin/eigrpd/eigrpd.h b/usr.sbin/eigrpd/eigrpd.h
new file mode 100644
index 00000000000..8581d4cfd60
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpd.h
@@ -0,0 +1,447 @@
+/* $OpenBSD: eigrpd.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _EIGRPD_H_
+#define _EIGRPD_H_
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <event.h>
+
+#include <imsg.h>
+#include "eigrp.h"
+
+#define CONF_FILE "/etc/eigrpd.conf"
+#define EIGRPD_SOCKET "/var/run/eigrpd.sock"
+#define EIGRPD_USER "_eigrpd"
+
+#define NBR_IDSELF 1
+#define NBR_CNTSTART (NBR_IDSELF + 1)
+
+#define READ_BUF_SIZE 65535
+#define PKG_DEF_SIZE 512 /* compromise */
+#define RT_BUF_SIZE 16384
+#define MAX_RTSOCK_BUF 128 * 1024
+
+#define RTP_EIGRP 28 /* XXX move to sys/net/route.h */
+
+#define F_EIGRPD_INSERTED 0x0001
+#define F_KERNEL 0x0002
+#define F_CONNECTED 0x0004
+#define F_STATIC 0x0008
+#define F_DYNAMIC 0x0010
+#define F_DOWN 0x0020
+#define F_REJECT 0x0040
+#define F_BLACKHOLE 0x0080
+#define F_REDISTRIBUTED 0x0100
+#define F_CTL_EXTERNAL 0x0200 /* only used by eigrpctl */
+#define F_CTL_ACTIVE 0x0400
+#define F_CTL_ALLLINKS 0x0800
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ void *data;
+ short events;
+};
+
+enum imsg_type {
+ IMSG_CTL_RELOAD,
+ IMSG_CTL_SHOW_INTERFACE,
+ IMSG_CTL_SHOW_NBR,
+ IMSG_CTL_SHOW_TOPOLOGY,
+ IMSG_CTL_FIB_COUPLE,
+ IMSG_CTL_FIB_DECOUPLE,
+ IMSG_CTL_IFACE,
+ IMSG_CTL_KROUTE,
+ IMSG_CTL_IFINFO,
+ IMSG_CTL_END,
+ IMSG_CTL_LOG_VERBOSE,
+ IMSG_KROUTE_CHANGE,
+ IMSG_KROUTE_DELETE,
+ IMSG_NONE,
+ IMSG_IFINFO,
+ IMSG_IFDOWN,
+ IMSG_NEWADDR,
+ IMSG_DELADDR,
+ IMSG_NETWORK_ADD,
+ IMSG_NETWORK_DEL,
+ IMSG_NEIGHBOR_UP,
+ IMSG_NEIGHBOR_DOWN,
+ IMSG_RECV_UPDATE_INIT,
+ IMSG_RECV_UPDATE,
+ IMSG_RECV_QUERY,
+ IMSG_RECV_REPLY,
+ IMSG_RECV_SIAQUERY,
+ IMSG_RECV_SIAREPLY,
+ IMSG_SEND_UPDATE,
+ IMSG_SEND_QUERY,
+ IMSG_SEND_REPLY,
+ IMSG_SEND_MUPDATE,
+ IMSG_SEND_MQUERY,
+ IMSG_SEND_UPDATE_END,
+ IMSG_SEND_REPLY_END,
+ IMSG_SEND_SIAQUERY_END,
+ IMSG_SEND_SIAREPLY_END,
+ IMSG_SEND_MUPDATE_END,
+ IMSG_SEND_MQUERY_END,
+ IMSG_RECONF_CONF,
+ IMSG_RECONF_IFACE,
+ IMSG_RECONF_INSTANCE,
+ IMSG_RECONF_EIGRP_IFACE,
+ IMSG_RECONF_END
+};
+
+union eigrpd_addr {
+ struct in_addr v4;
+ struct in6_addr v6;
+};
+
+/* interface types */
+enum iface_type {
+ IF_TYPE_POINTOPOINT,
+ IF_TYPE_BROADCAST,
+};
+
+struct if_addr {
+ TAILQ_ENTRY(if_addr) entry;
+ int af;
+ union eigrpd_addr addr;
+ uint8_t prefixlen;
+ union eigrpd_addr dstbrd;
+};
+TAILQ_HEAD(if_addr_head, if_addr);
+
+struct iface {
+ TAILQ_ENTRY(iface) entry;
+ TAILQ_HEAD(, eigrp_iface) ei_list;
+ unsigned int ifindex;
+ char name[IF_NAMESIZE];
+ struct if_addr_head addr_list;
+ struct in6_addr linklocal;
+ int mtu;
+ enum iface_type type;
+ uint8_t if_type;
+ uint64_t baudrate;
+ uint16_t flags;
+ uint8_t linkstate;
+ uint8_t group_count_v4;
+ uint8_t group_count_v6;
+};
+
+enum route_type {
+ EIGRP_ROUTE_INTERNAL,
+ EIGRP_ROUTE_EXTERNAL
+};
+
+/* routing information advertised by update/query/reply messages */
+struct rinfo {
+ int af;
+ enum route_type type;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ union eigrpd_addr nexthop;
+ struct classic_metric metric;
+ struct classic_emetric emetric;
+};
+
+struct rinfo_entry {
+ TAILQ_ENTRY(rinfo_entry) entry;
+ struct rinfo rinfo;
+};
+TAILQ_HEAD(rinfo_head, rinfo_entry);
+
+/* interface states */
+#define IF_STA_DOWN 0x01
+#define IF_STA_ACTIVE 0x02
+
+struct eigrp_iface {
+ RB_ENTRY(eigrp_iface) id_tree;
+ TAILQ_ENTRY(eigrp_iface) e_entry;
+ TAILQ_ENTRY(eigrp_iface) i_entry;
+ struct eigrp *eigrp;
+ struct iface *iface;
+ int state;
+ uint32_t ifaceid;
+ struct event hello_timer;
+ uint32_t delay;
+ uint32_t bandwidth;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+ uint8_t splithorizon;
+ uint8_t passive;
+ time_t uptime;
+ TAILQ_HEAD(, nbr) nbr_list;
+ struct nbr *self;
+ struct rinfo_head update_list; /* multicast updates */
+ struct rinfo_head query_list; /* multicast queries */
+};
+
+#define INADDRSZ 4
+#define IN6ADDRSZ 16
+
+struct seq_addr_entry {
+ TAILQ_ENTRY(seq_addr_entry) entry;
+ int af;
+ union eigrpd_addr addr;
+};
+TAILQ_HEAD(seq_addr_head, seq_addr_entry);
+
+struct nbr;
+RB_HEAD(nbr_addr_head, nbr);
+RB_HEAD(nbr_pid_head, nbr);
+struct rt_node;
+RB_HEAD(rt_tree, rt_node);
+
+#define REDIST_STATIC 0x01
+#define REDIST_RIP 0x02
+#define REDIST_OSPF 0x04
+#define REDIST_CONNECTED 0x08
+#define REDIST_DEFAULT 0x10
+#define REDIST_ADDR 0x20
+#define REDIST_NO 0x40
+
+struct redist_metric {
+ uint32_t bandwidth;
+ uint32_t delay;
+ uint8_t reliability;
+ uint8_t load;
+ uint16_t mtu;
+};
+
+struct redistribute {
+ SIMPLEQ_ENTRY(redistribute) entry;
+ uint8_t type;
+ int af;
+ union eigrpd_addr addr;
+ uint8_t prefixlen;
+ struct redist_metric *metric;
+ struct {
+ uint32_t as;
+ uint32_t metric;
+ uint32_t tag;
+ } emetric;
+};
+SIMPLEQ_HEAD(redist_list, redistribute);
+
+/* eigrp instance */
+struct eigrp {
+ TAILQ_ENTRY(eigrp) entry;
+ int af;
+ uint16_t as;
+ uint8_t kvalues[6];
+ uint8_t maximum_hops;
+ uint8_t maximum_paths;
+ uint8_t variance;
+ struct redist_metric *dflt_metric;
+ struct redist_list redist_list;
+ TAILQ_HEAD(, eigrp_iface) ei_list;
+ struct nbr_addr_head nbrs;
+ struct rde_nbr *rnbr_redist;
+ struct rde_nbr *rnbr_summary;
+ struct rt_tree topology;
+ uint32_t seq_num;
+};
+
+/* eigrp_conf */
+enum {
+ PROC_MAIN,
+ PROC_EIGRP_ENGINE,
+ PROC_RDE_ENGINE
+} eigrpd_process;
+
+struct eigrpd_conf {
+ struct in_addr rtr_id;
+
+ uint32_t opts;
+#define EIGRPD_OPT_VERBOSE 0x00000001
+#define EIGRPD_OPT_VERBOSE2 0x00000002
+#define EIGRPD_OPT_NOACTION 0x00000004
+
+ int flags;
+#define EIGRPD_FLAG_NO_FIB_UPDATE 0x0001
+
+ time_t uptime;
+ int eigrp_socket_v4;
+ int eigrp_socket_v6;
+ unsigned int rdomain;
+ uint8_t fib_priority_internal;
+ uint8_t fib_priority_external;
+ TAILQ_HEAD(, iface) iface_list;
+ TAILQ_HEAD(, eigrp) instances;
+ char *csock;
+};
+
+/* kroute */
+struct kroute {
+ int af;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ union eigrpd_addr nexthop;
+ unsigned short ifindex;
+ uint8_t priority;
+ uint16_t flags;
+};
+
+struct kaddr {
+ unsigned short ifindex;
+ int af;
+ union eigrpd_addr addr;
+ uint8_t prefixlen;
+ union eigrpd_addr dstbrd;
+};
+
+struct kif {
+ char ifname[IF_NAMESIZE];
+ unsigned short ifindex;
+ int flags;
+ uint8_t link_state;
+ int mtu;
+ uint8_t if_type;
+ uint64_t baudrate;
+ uint8_t nh_reachable; /* for nexthop verification */
+};
+
+/* control data structures */
+struct ctl_iface {
+ int af;
+ uint16_t as;
+ char name[IF_NAMESIZE];
+ unsigned int ifindex;
+ union eigrpd_addr addr;
+ uint8_t prefixlen;
+ uint16_t flags;
+ uint8_t linkstate;
+ int mtu;
+ enum iface_type type;
+ uint8_t if_type;
+ uint64_t baudrate;
+ uint32_t delay;
+ uint32_t bandwidth;
+ uint16_t hello_holdtime;
+ uint16_t hello_interval;
+ uint8_t splithorizon;
+ uint8_t passive;
+ time_t uptime;
+ int nbr_cnt;
+};
+
+struct ctl_nbr {
+ int af;
+ uint16_t as;
+ char ifname[IF_NAMESIZE];
+ union eigrpd_addr addr;
+ uint16_t hello_holdtime;
+ time_t uptime;
+};
+
+struct ctl_rt {
+ int af;
+ uint16_t as;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ enum route_type type;
+ union eigrpd_addr nexthop;
+ char ifname[IF_NAMESIZE];
+ uint32_t distance;
+ uint32_t rdistance;
+ uint32_t fdistance;
+ int state;
+ uint8_t flags;
+ struct {
+ uint32_t delay;
+ uint32_t bandwidth;
+ uint32_t mtu;
+ uint8_t hop_count;
+ uint8_t reliability;
+ uint8_t load;
+ } metric;
+ struct classic_emetric emetric;
+};
+#define F_CTL_RT_FIRST 0x01
+#define F_CTL_RT_SUCCESSOR 0x02
+#define F_CTL_RT_FSUCCESSOR 0x04
+
+struct ctl_show_topology_req {
+ int af;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ uint16_t flags;
+};
+
+/* parse.y */
+struct eigrpd_conf *parse_config(char *, int);
+int cmdline_symset(char *);
+
+/* in_cksum.c */
+uint16_t in_cksum(void *, size_t);
+
+/* kroute.c */
+int kif_init(void);
+void kif_redistribute(void);
+int kr_init(int, unsigned int);
+int kr_change(struct kroute *);
+int kr_delete(struct kroute *);
+void kif_clear(void);
+void kr_shutdown(void);
+void kr_fib_couple(void);
+void kr_fib_decouple(void);
+void kr_fib_reload(void);
+void kr_dispatch_msg(int, short, void *);
+void kr_show_route(struct imsg *);
+void kr_ifinfo(char *, pid_t);
+struct kif *kif_findname(char *);
+void kr_reload(void);
+
+/* util.c */
+uint8_t mask2prefixlen(in_addr_t);
+uint8_t mask2prefixlen6(struct sockaddr_in6 *);
+in_addr_t prefixlen2mask(uint8_t);
+struct in6_addr *prefixlen2mask6(uint8_t);
+void eigrp_applymask(int, union eigrpd_addr *,
+ const union eigrpd_addr *, int);
+int eigrp_addrcmp(int, union eigrpd_addr *, union eigrpd_addr *);
+int eigrp_addrisset(int, union eigrpd_addr *);
+void embedscope(struct sockaddr_in6 *);
+void recoverscope(struct sockaddr_in6 *);
+void addscope(struct sockaddr_in6 *, uint32_t);
+void clearscope(struct in6_addr *);
+
+/* eigrpd.c */
+void main_imsg_compose_eigrpe(int, pid_t, void *, uint16_t);
+void main_imsg_compose_rde(int, pid_t, void *, uint16_t);
+void merge_config(struct eigrpd_conf *, struct eigrpd_conf *);
+void config_clear(struct eigrpd_conf *);
+void imsg_event_add(struct imsgev *);
+int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
+ pid_t, int, void *, uint16_t);
+uint32_t eigrp_router_id(struct eigrpd_conf *);
+struct eigrp *eigrp_find(struct eigrpd_conf *, int, uint16_t);
+
+/* printconf.c */
+void print_config(struct eigrpd_conf *);
+
+#endif /* _EIGRPD_H_ */
diff --git a/usr.sbin/eigrpd/eigrpe.c b/usr.sbin/eigrpd/eigrpe.c
new file mode 100644
index 00000000000..a141eaefc43
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpe.c
@@ -0,0 +1,678 @@
+/* $OpenBSD: eigrpe.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "eigrp.h"
+#include "eigrpd.h"
+#include "eigrpe.h"
+#include "rde.h"
+#include "control.h"
+#include "log.h"
+
+void eigrpe_sig_handler(int, short, void *);
+void eigrpe_shutdown(void);
+
+static struct event ev4;
+static struct event ev6;
+struct eigrpd_conf *econf = NULL, *nconf;
+struct imsgev *iev_main;
+struct imsgev *iev_rde;
+
+extern struct iface_id_head ifaces_by_id;
+RB_PROTOTYPE(iface_id_head, eigrp_iface, id_tree, iface_id_compare)
+
+extern struct nbr_addr_head nbrs_by_addr;
+RB_PROTOTYPE(nbr_addr_head, nbr, addr_tree, nbr_compare)
+
+/* ARGSUSED */
+void
+eigrpe_sig_handler(int sig, short event, void *bula)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ eigrpe_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* eigrp engine */
+pid_t
+eigrpe(struct eigrpd_conf *xconf, int pipe_parent2eigrpe[2], int pipe_eigrpe2rde[2],
+ int pipe_parent2rde[2])
+{
+ struct passwd *pw;
+ struct event ev_sigint, ev_sigterm;
+ pid_t pid;
+ struct iface *iface;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ /* create eigrpd control socket outside chroot */
+ if (control_init(xconf->csock) == -1)
+ fatalx("control socket setup failed");
+
+ /* create the raw ipv4 socket */
+ if ((xconf->eigrp_socket_v4 = socket(AF_INET,
+ SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_EIGRP)) == -1)
+ fatal("error creating raw ipv4 socket");
+
+ /* set some defaults */
+ if (if_set_ipv4_mcast_ttl(xconf->eigrp_socket_v4, EIGRP_IP_TTL) == -1)
+ fatal("if_set_ipv4_mcast_ttl");
+ if (if_set_ipv4_mcast_loop(xconf->eigrp_socket_v4) == -1)
+ fatal("if_set_ipv4_mcast_loop");
+ if (if_set_ipv4_recvif(xconf->eigrp_socket_v4, 1) == -1)
+ fatal("if_set_ipv4_recvif");
+ if (if_set_ipv4_hdrincl(xconf->eigrp_socket_v4) == -1)
+ fatal("if_set_ipv4_hdrincl");
+ if_set_sockbuf(xconf->eigrp_socket_v4);
+
+ /* create the raw ipv6 socket */
+ if ((xconf->eigrp_socket_v6 = socket(AF_INET6,
+ SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_EIGRP)) == -1)
+ fatal("error creating raw ipv6 socket");
+
+ /* set some defaults */
+ if (if_set_ipv6_mcast_loop(xconf->eigrp_socket_v6) == -1)
+ fatal("if_set_ipv6_mcast_loop");
+ if (if_set_ipv6_pktinfo(xconf->eigrp_socket_v6, 1) == -1)
+ fatal("if_set_ipv6_pktinfo");
+ if (if_set_ipv6_dscp(xconf->eigrp_socket_v6,
+ IPTOS_PREC_NETCONTROL) == -1)
+ fatal("if_set_ipv6_dscp");
+ if_set_sockbuf(xconf->eigrp_socket_v6);
+
+ econf = xconf;
+
+ if ((pw = getpwnam(EIGRPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("eigrp engine");
+ eigrpd_process = PROC_EIGRP_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, eigrpe_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, eigrpe_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_parent2eigrpe[0]);
+ close(pipe_eigrpe2rde[1]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2rde[1]);
+
+ if ((iev_rde = malloc(sizeof(struct imsgev))) == NULL ||
+ (iev_main = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_rde->ibuf, pipe_eigrpe2rde[0]);
+ iev_rde->handler = eigrpe_dispatch_rde;
+ imsg_init(&iev_main->ibuf, pipe_parent2eigrpe[1]);
+ iev_main->handler = eigrpe_dispatch_main;
+
+ /* setup event handler */
+ iev_rde->events = EV_READ;
+ event_set(&iev_rde->ev, iev_rde->ibuf.fd, iev_rde->events,
+ iev_rde->handler, iev_rde);
+ event_add(&iev_rde->ev, NULL);
+
+ iev_main->events = EV_READ;
+ event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+ iev_main->handler, iev_main);
+ event_add(&iev_main->ev, NULL);
+
+ event_set(&ev4, econf->eigrp_socket_v4, EV_READ|EV_PERSIST,
+ recv_packet_v4, econf);
+ event_add(&ev4, NULL);
+
+ event_set(&ev6, econf->eigrp_socket_v6, EV_READ|EV_PERSIST,
+ recv_packet_v6, econf);
+ event_add(&ev6, NULL);
+
+ /* listen on eigrpd control socket */
+ TAILQ_INIT(&ctl_conns);
+ control_listen();
+
+ if ((pkt_ptr = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("eigrpe");
+
+ /* initialize interfaces */
+ TAILQ_FOREACH(iface, &econf->iface_list, entry)
+ if_init(xconf, iface);
+
+ event_dispatch();
+
+ eigrpe_shutdown();
+ /* NOTREACHED */
+ return (0);
+}
+
+void
+eigrpe_shutdown(void)
+{
+ control_cleanup(econf->csock);
+
+ config_clear(econf);
+
+ event_del(&ev4);
+ event_del(&ev6);
+ close(econf->eigrp_socket_v4);
+ close(econf->eigrp_socket_v6);
+
+ /* clean up */
+ msgbuf_write(&iev_rde->ibuf.w);
+ msgbuf_clear(&iev_rde->ibuf.w);
+ free(iev_rde);
+ msgbuf_write(&iev_main->ibuf.w);
+ msgbuf_clear(&iev_main->ibuf.w);
+ free(iev_main);
+ free(pkt_ptr);
+
+ log_info("eigrp engine exiting");
+ _exit(0);
+}
+
+/* imesg */
+int
+eigrpe_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ return (imsg_compose_event(iev_main, type, 0, pid, -1, data, datalen));
+}
+
+int
+eigrpe_imsg_compose_rde(int type, uint32_t peerid, pid_t pid,
+ void *data, uint16_t datalen)
+{
+ return (imsg_compose_event(iev_rde, type, peerid, pid, -1,
+ data, datalen));
+}
+
+/* ARGSUSED */
+void
+eigrpe_dispatch_main(int fd, short event, void *bula)
+{
+ struct iface *niface;
+ static struct eigrp *neigrp;
+ struct eigrp_iface *nei;
+ struct imsg imsg;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct iface *iface = NULL;
+ struct kif *kif;
+ struct kaddr *ka;
+ int n, shut = 0;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("eigrpe_dispatch_main: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFINFO:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kif))
+ fatalx("IFSTATUS imsg with wrong len");
+ kif = imsg.data;
+
+ iface = if_lookup(econf, kif->ifindex);
+ if (!iface)
+ break;
+
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ if_update(iface, AF_UNSPEC);
+ break;
+ case IMSG_NEWADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kaddr))
+ fatalx("NEWADDR imsg with wrong len");
+ ka = imsg.data;
+
+ iface = if_lookup(econf, ka->ifindex);
+ if (iface == NULL)
+ break;
+
+ if (ka->af == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6)) {
+ memcpy(&iface->linklocal, &ka->addr.v6,
+ sizeof(iface->linklocal));
+ break;
+ }
+
+ if_addr_new(iface, ka);
+ break;
+ case IMSG_DELADDR:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kaddr))
+ fatalx("DELADDR imsg with wrong len");
+ ka = imsg.data;
+
+ iface = if_lookup(econf, ka->ifindex);
+ if (iface == NULL)
+ break;
+
+ if (ka->af == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&iface->linklocal,
+ &ka->addr.v6)) {
+ memset(&iface->linklocal, 0,
+ sizeof(iface->linklocal));
+ break;
+ }
+
+ if_addr_del(iface, ka);
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct eigrpd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct eigrpd_conf));
+
+ TAILQ_INIT(&nconf->iface_list);
+ TAILQ_INIT(&nconf->instances);
+ break;
+ case IMSG_RECONF_INSTANCE:
+ if ((neigrp = malloc(sizeof(struct eigrp))) == NULL)
+ fatal(NULL);
+ memcpy(neigrp, imsg.data, sizeof(struct eigrp));
+
+ SIMPLEQ_INIT(&neigrp->redist_list);
+ TAILQ_INIT(&neigrp->ei_list);
+ RB_INIT(&neigrp->nbrs);
+ RB_INIT(&neigrp->topology);
+ TAILQ_INSERT_TAIL(&nconf->instances, neigrp, entry);
+ break;
+ case IMSG_RECONF_IFACE:
+ niface = imsg.data;
+ niface = if_lookup(nconf, niface->ifindex);
+ if (niface)
+ break;
+
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ TAILQ_INIT(&niface->addr_list);
+ TAILQ_INSERT_TAIL(&nconf->iface_list, niface, entry);
+ break;
+ case IMSG_RECONF_EIGRP_IFACE:
+ if ((nei = malloc(sizeof(struct eigrp_iface))) == NULL)
+ fatal(NULL);
+ memcpy(nei, imsg.data, sizeof(struct eigrp_iface));
+
+ nei->iface = niface;
+ nei->eigrp = neigrp;
+ TAILQ_INIT(&nei->nbr_list);
+ TAILQ_INIT(&nei->update_list);
+ TAILQ_INIT(&nei->query_list);
+ TAILQ_INSERT_TAIL(&niface->ei_list, nei, i_entry);
+ TAILQ_INSERT_TAIL(&neigrp->ei_list, nei, e_entry);
+ if (RB_INSERT(iface_id_head, &ifaces_by_id, nei) !=
+ NULL)
+ fatalx("rde_dispatch_parent: "
+ "RB_INSERT(ifaces_by_id) failed");
+ break;
+ case IMSG_RECONF_END:
+ merge_config(econf, nconf);
+ nconf = NULL;
+ break;
+ case IMSG_CTL_KROUTE:
+ case IMSG_CTL_IFINFO:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+eigrpe_dispatch_rde(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ struct nbr *nbr;
+ struct eigrp_iface *ei;
+ struct rinfo rinfo;
+ int n, shut = 0;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("eigrpe_dispatch_rde: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_SEND_UPDATE:
+ case IMSG_SEND_QUERY:
+ case IMSG_SEND_REPLY:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rinfo))
+ fatalx("invalid size of rinfo");
+ memcpy(&rinfo, imsg.data, sizeof(rinfo));
+
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find rde neighbor",
+ __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_SEND_UPDATE:
+ message_add(&nbr->update_list, &rinfo);
+ break;
+ case IMSG_SEND_QUERY:
+ message_add(&nbr->query_list, &rinfo);
+ break;
+ case IMSG_SEND_REPLY:
+ message_add(&nbr->reply_list, &rinfo);
+ break;
+ }
+ break;
+ case IMSG_SEND_MUPDATE:
+ case IMSG_SEND_MQUERY:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rinfo))
+ fatalx("invalid size of rinfo");
+ memcpy(&rinfo, imsg.data, sizeof(rinfo));
+
+ ei = eigrp_iface_find_id(imsg.hdr.peerid);
+ if (ei == NULL) {
+ log_debug("%s: cannot find interface",
+ __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_SEND_MUPDATE:
+ message_add(&ei->update_list, &rinfo);
+ break;
+ case IMSG_SEND_MQUERY:
+ message_add(&ei->query_list, &rinfo);
+ break;
+ }
+ break;
+ case IMSG_SEND_UPDATE_END:
+ case IMSG_SEND_REPLY_END:
+ case IMSG_SEND_SIAQUERY_END:
+ case IMSG_SEND_SIAREPLY_END:
+ nbr = nbr_find_peerid(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find rde neighbor",
+ __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_SEND_UPDATE_END:
+ send_update(nbr->ei, nbr, 0, 1,
+ &nbr->update_list);
+ message_list_clr(&nbr->update_list);
+ break;
+ case IMSG_SEND_REPLY_END:
+ send_reply(nbr, &nbr->reply_list, 0);
+ message_list_clr(&nbr->reply_list);
+ break;
+ case IMSG_SEND_SIAQUERY_END:
+ send_query(nbr->ei, nbr, &nbr->query_list, 1);
+ message_list_clr(&nbr->query_list);
+ break;
+ case IMSG_SEND_SIAREPLY_END:
+ send_reply(nbr, &nbr->reply_list, 1);
+ message_list_clr(&nbr->reply_list);
+ break;
+ }
+ break;
+ case IMSG_SEND_MUPDATE_END:
+ case IMSG_SEND_MQUERY_END:
+ ei = eigrp_iface_find_id(imsg.hdr.peerid);
+ if (ei == NULL) {
+ log_debug("%s: cannot find interface",
+ __func__);
+ break;
+ }
+
+ switch (imsg.hdr.type) {
+ case IMSG_SEND_MUPDATE_END:
+ send_update(ei, NULL, 0, 0, &ei->update_list);
+ message_list_clr(&ei->update_list);
+ break;
+ case IMSG_SEND_MQUERY_END:
+ send_query(ei, NULL, &ei->query_list, 0);
+ message_list_clr(&ei->query_list);
+ break;
+ }
+ break;
+ case IMSG_CTL_SHOW_TOPOLOGY:
+ case IMSG_CTL_END:
+ control_imsg_relay(&imsg);
+ break;
+ default:
+ log_debug("%s: error handling imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+eigrpe_instance_init(struct eigrp *eigrp)
+{
+ struct eigrp_iface *ei;
+
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry)
+ if_init(econf, ei->iface);
+}
+
+void
+eigrpe_instance_del(struct eigrp *eigrp)
+{
+ struct eigrp_iface *ei;
+
+ while ((ei = TAILQ_FIRST(&eigrp->ei_list)) != NULL)
+ eigrp_if_del(ei);
+
+ free(eigrp);
+}
+
+void
+message_add(struct rinfo_head *rinfo_list, struct rinfo *rinfo)
+{
+ struct rinfo_entry *re;
+
+ re = calloc(1, sizeof(*re));
+ if (re == NULL)
+ fatal("message_add");
+ memcpy(&re->rinfo, rinfo, sizeof(re->rinfo));;
+
+ TAILQ_INSERT_TAIL(rinfo_list, re, entry);
+}
+
+void
+message_list_clr(struct rinfo_head *rinfo_list)
+{
+ struct rinfo_entry *re;
+
+ while ((re = TAILQ_FIRST(rinfo_list)) != NULL) {
+ TAILQ_REMOVE(rinfo_list, re, entry);
+ free(re);
+ }
+}
+
+void
+seq_addr_list_clr(struct seq_addr_head *seq_addr_list)
+{
+ struct seq_addr_entry *sa;
+
+ while ((sa = TAILQ_FIRST(seq_addr_list)) != NULL) {
+ TAILQ_REMOVE(seq_addr_list, sa, entry);
+ free(sa);
+ }
+}
+
+void
+eigrpe_orig_local_route(struct eigrp_iface *ei, struct if_addr *if_addr,
+ int withdraw)
+{
+ struct rinfo rinfo;
+
+ memset(&rinfo, 0, sizeof(rinfo));
+ rinfo.af = if_addr->af;
+ rinfo.type = EIGRP_ROUTE_INTERNAL;
+ memcpy(&rinfo.prefix, &if_addr->addr, sizeof(rinfo.prefix));
+ rinfo.prefixlen = if_addr->prefixlen;
+
+ eigrp_applymask(rinfo.af, &rinfo.prefix, &rinfo.prefix,
+ rinfo.prefixlen);
+
+ if (withdraw)
+ rinfo.metric.delay = EIGRP_INFINITE_METRIC;
+ else
+ rinfo.metric.delay = eigrp_composite_delay(ei->delay);
+ rinfo.metric.bandwidth = eigrp_composite_bandwidth(ei->bandwidth);
+ metric_encode_mtu(rinfo.metric.mtu, ei->iface->mtu);
+ rinfo.metric.hop_count = 0;
+ rinfo.metric.reliability = DEFAULT_RELIABILITY;
+ rinfo.metric.load = DEFAULT_LOAD;
+ rinfo.metric.tag = 0;
+ rinfo.metric.flags = 0;
+
+ eigrpe_imsg_compose_rde(IMSG_RECV_UPDATE, ei->self->peerid, 0,
+ &rinfo, sizeof(rinfo));
+}
+
+void
+eigrpe_iface_ctl(struct ctl_conn *c, unsigned int idx)
+{
+ struct eigrp *eigrp;
+ struct eigrp_iface *ei;
+ struct ctl_iface *ictl;
+
+ TAILQ_FOREACH(eigrp, &econf->instances, entry) {
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
+ if (idx == 0 || idx == ei->iface->ifindex) {
+ ictl = if_to_ctl(ei);
+ imsg_compose_event(&c->iev,
+ IMSG_CTL_SHOW_INTERFACE, 0, 0, -1,
+ ictl, sizeof(struct ctl_iface));
+ }
+ }
+ }
+}
+
+void
+eigrpe_nbr_ctl(struct ctl_conn *c)
+{
+ struct eigrp *eigrp;
+ struct nbr *nbr;
+ struct ctl_nbr *nctl;
+
+ TAILQ_FOREACH(eigrp, &econf->instances, entry) {
+ RB_FOREACH(nbr, nbr_addr_head, &eigrp->nbrs) {
+ if (nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF))
+ continue;
+
+ nctl = nbr_to_ctl(nbr);
+ imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0,
+ 0, -1, nctl, sizeof(struct ctl_nbr));
+ }
+ }
+
+ imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
+}
diff --git a/usr.sbin/eigrpd/eigrpe.h b/usr.sbin/eigrpd/eigrpe.h
new file mode 100644
index 00000000000..b7e43547a73
--- /dev/null
+++ b/usr.sbin/eigrpd/eigrpe.h
@@ -0,0 +1,200 @@
+/* $OpenBSD: eigrpe.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _EIGRPE_H_
+#define _EIGRPE_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns;
+
+struct pbuf {
+ struct ibuf *buf;
+ int refcnt;
+};
+
+struct packet {
+ TAILQ_ENTRY(packet) entry;
+ struct nbr *nbr;
+ uint32_t seq_num;
+ struct pbuf *pbuf;
+ struct event ev_timeout;
+ int attempts;
+};
+
+struct nbr {
+ RB_ENTRY(nbr) addr_tree, pid_tree;
+ TAILQ_ENTRY(nbr) entry;
+ struct event ev_ack;
+ struct event ev_hello_timeout;
+ struct eigrp_iface *ei;
+ union eigrpd_addr addr;
+ uint32_t peerid;
+ time_t uptime;
+ uint16_t hello_holdtime;
+ uint8_t flags;
+
+ struct rinfo_head update_list; /* unicast updates */
+ struct rinfo_head query_list; /* unicast queries */
+ struct rinfo_head reply_list; /* unicast replies */
+
+ /* RTP */
+ uint32_t recv_seq;
+ uint32_t next_mcast_seq;
+ TAILQ_HEAD(, packet) retrans_list;
+};
+#define F_EIGRP_NBR_SELF 0x01
+#define F_EIGRP_NBR_PENDING 0x02
+#define F_EIGRP_NBR_CR_MODE 0x04
+
+#define PREFIX_SIZE4(x) (((x - 1) / 8) + 1)
+#define PREFIX_SIZE6(x) ((x == 128) ? 16 : ((x / 8) + 1))
+
+/* eigrpe.c */
+pid_t eigrpe(struct eigrpd_conf *, int[2], int[2], int[2]);
+void eigrpe_dispatch_main(int, short, void *);
+void eigrpe_dispatch_rde(int, short, void *);
+int eigrpe_imsg_compose_parent(int, pid_t, void *, uint16_t);
+int eigrpe_imsg_compose_rde(int, uint32_t, pid_t, void *,
+ uint16_t);
+void eigrpe_instance_init(struct eigrp *);
+void eigrpe_instance_del(struct eigrp *);
+void message_add(struct rinfo_head *, struct rinfo *);
+void message_list_clr(struct rinfo_head *);
+void seq_addr_list_clr(struct seq_addr_head *);
+void eigrpe_orig_local_route(struct eigrp_iface *,
+ struct if_addr *, int);
+void eigrpe_iface_ctl(struct ctl_conn *, unsigned int);
+void eigrpe_nbr_ctl(struct ctl_conn *);
+
+/* interface.c */
+struct iface *if_new(struct eigrpd_conf *, struct kif *);
+void if_del(struct iface *);
+void if_init(struct eigrpd_conf *, struct iface *);
+struct iface *if_lookup(struct eigrpd_conf *, unsigned int);
+struct if_addr *if_addr_new(struct iface *, struct kaddr *);
+void if_addr_del(struct iface *, struct kaddr *);
+struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *);
+in_addr_t if_primary_addr(struct iface *);
+uint8_t if_primary_addr_prefixlen(struct iface *);
+void if_update(struct iface *, int);
+struct eigrp_iface *eigrp_if_new(struct eigrpd_conf *, struct eigrp *,
+ struct kif *);
+void eigrp_if_del(struct eigrp_iface *);
+void eigrp_if_start(struct eigrp_iface *);
+void eigrp_if_reset(struct eigrp_iface *);
+struct eigrp_iface *eigrp_if_lookup(struct iface *, int, uint16_t);
+struct eigrp_iface *eigrp_iface_find_id(uint32_t);
+struct ctl_iface *if_to_ctl(struct eigrp_iface *);
+void if_set_sockbuf(int);
+int if_join_ipv4_group(struct iface *, struct in_addr *);
+int if_leave_ipv4_group(struct iface *, struct in_addr *);
+int if_set_ipv4_mcast_ttl(int, uint8_t);
+int if_set_ipv4_mcast(struct iface *);
+int if_set_ipv4_mcast_loop(int);
+int if_set_ipv4_recvif(int, int);
+int if_set_ipv4_hdrincl(int);
+int if_join_ipv6_group(struct iface *, struct in6_addr *);
+int if_leave_ipv6_group(struct iface *, struct in6_addr *);
+int if_set_ipv6_mcast(struct iface *);
+int if_set_ipv6_mcast_loop(int);
+int if_set_ipv6_pktinfo(int, int);
+int if_set_ipv6_dscp(int, int);
+
+/* neighbor.c */
+struct nbr *nbr_new(struct eigrp_iface *, union eigrpd_addr *,
+ uint16_t, int);
+void nbr_init(struct nbr *);
+void nbr_del(struct nbr *);
+void nbr_update_peerid(struct nbr *);
+struct nbr *nbr_find(struct eigrp_iface *, union eigrpd_addr *);
+struct nbr *nbr_find_peerid(uint32_t);
+struct ctl_nbr *nbr_to_ctl(struct nbr *);
+void nbr_timeout(int, short, void *);
+void nbr_start_timeout(struct nbr *);
+void nbr_stop_timeout(struct nbr *);
+
+/* rtp.c */
+struct pbuf *rtp_buf_new(struct ibuf *);
+struct pbuf *rtp_buf_hold(struct pbuf *);
+void rtp_buf_release(struct pbuf *);
+struct packet *rtp_packet_new(struct nbr *, uint32_t, struct pbuf *);
+void rtp_packet_del(struct packet *);
+void rtp_process_ack(struct nbr *, uint32_t);
+void rtp_send_packet(struct packet *);
+void rtp_enqueue_packet(struct packet *);
+void rtp_send_ucast(struct nbr *, struct ibuf *);
+void rtp_send_mcast(struct eigrp_iface *, struct ibuf *);
+void rtp_send(struct eigrp_iface *, struct nbr *, struct ibuf *);
+void rtp_send_ack(struct nbr *);
+void rtp_ack_timer(int, short, void *);
+void rtp_ack_start_timer(struct nbr *);
+void rtp_ack_stop_timer(struct nbr *);
+
+/* packet.c */
+int gen_eigrp_hdr(struct ibuf *, uint16_t, uint8_t, uint32_t,
+ uint16_t);
+int send_packet(struct eigrp_iface *, struct nbr *, uint32_t,
+ struct ibuf *);
+void recv_packet_v4(int, short, void *);
+void recv_packet_v6(int, short, void *);
+
+/* tlv.c */
+int gen_parameter_tlv(struct ibuf *, struct eigrp_iface *);
+int gen_sequence_tlv(struct ibuf *,
+ struct seq_addr_head *);
+int gen_sw_version_tlv(struct ibuf *);
+int gen_mcast_seq_tlv(struct ibuf *, uint32_t);
+uint16_t len_route_tlv(struct rinfo *);
+int gen_route_tlv(struct ibuf *, struct rinfo *);
+struct tlv_parameter *tlv_decode_parameter(struct tlv *, char *);
+int tlv_decode_seq(struct tlv *, char *,
+ struct seq_addr_head *);
+struct tlv_sw_version *tlv_decode_sw_version(struct tlv *, char *);
+struct tlv_mcast_seq *tlv_decode_mcast_seq(struct tlv *, char *);
+int tlv_decode_route(int, enum route_type, struct tlv *,
+ char *, struct rinfo *);
+void metric_encode_mtu(uint8_t *, int);
+int metric_decode_mtu(uint8_t *);
+
+/* hello.c */
+void send_hello(struct eigrp_iface *, struct seq_addr_head *, uint32_t);
+void recv_hello(struct eigrp_iface *, union eigrpd_addr *, struct nbr *,
+ struct tlv_parameter *);
+
+/* update.c */
+void send_update(struct eigrp_iface *, struct nbr *, uint32_t, int,
+ struct rinfo_head *);
+void recv_update(struct nbr *, struct rinfo_head *, uint32_t);
+
+/* query.c */
+void send_query(struct eigrp_iface *, struct nbr *, struct rinfo_head *,
+ int);
+void recv_query(struct nbr *, struct rinfo_head *, int);
+
+/* reply.c */
+void send_reply(struct nbr *, struct rinfo_head *, int);
+void recv_reply(struct nbr *, struct rinfo_head *, int);
+
+char *pkt_ptr; /* packet buffer */
+
+#endif /* _EIGRPE_H_ */
diff --git a/usr.sbin/eigrpd/hello.c b/usr.sbin/eigrpd/hello.c
new file mode 100644
index 00000000000..401e5b4f6b2
--- /dev/null
+++ b/usr.sbin/eigrpd/hello.c
@@ -0,0 +1,102 @@
+/* $OpenBSD: hello.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+/* hello packet handling */
+
+void
+send_hello(struct eigrp_iface *ei, struct seq_addr_head *seq_addr_list,
+ uint32_t mcast_seq)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct ibuf *buf;
+ uint8_t flags = 0;
+
+ if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
+ IP_MAXPACKET - sizeof(struct ip))) == NULL)
+ fatal("send_hello");
+
+ /* EIGRP header */
+ if (gen_eigrp_hdr(buf, EIGRP_OPC_HELLO, flags, 0, eigrp->as))
+ goto fail;
+
+ if (gen_parameter_tlv(buf, ei))
+ goto fail;
+
+ if (gen_sw_version_tlv(buf))
+ goto fail;
+
+ if (seq_addr_list && !TAILQ_EMPTY(seq_addr_list) &&
+ gen_sequence_tlv(buf, seq_addr_list))
+ goto fail;
+
+ if (mcast_seq && gen_mcast_seq_tlv(buf, mcast_seq))
+ goto fail;
+
+ /* send unreliably */
+ send_packet(ei, NULL, 0, buf);
+ ibuf_free(buf);
+ return;
+fail:
+ log_warnx("%s: failed to send message", __func__);
+ ibuf_free(buf);
+}
+
+void
+recv_hello(struct eigrp_iface *ei, union eigrpd_addr *src, struct nbr *nbr,
+ struct tlv_parameter *tp)
+{
+ /*
+ * Some old Cisco routers seems to use the "parameters tlv" with all
+ * K-values set to 255 (except k6) to inform that the neighbor has been
+ * reset. The "peer termination" tlv described in the draft for the same
+ * purpose is probably something introduced recently. Let's check for
+ * this case to maintain backward compatibility.
+ */
+ if (tp->kvalues[0] == 255 && tp->kvalues[1] == 255 &&
+ tp->kvalues[2] == 255 && tp->kvalues[3] == 255 &&
+ tp->kvalues[4] == 255 && tp->kvalues[5] == 0) {
+ if (nbr) {
+ log_debug("%s: peer temination", __func__);
+ nbr_del(nbr);
+ }
+ return;
+ }
+
+ if (nbr == NULL) {
+ if (memcmp(ei->eigrp->kvalues, tp->kvalues, 6) != 0) {
+ log_debug("%s: k-values don't match (nbr %s)",
+ __func__, log_addr(ei->eigrp->af, src));
+ return;
+ }
+
+ nbr = nbr_new(ei, src, ntohs(tp->holdtime), 0);
+
+ /* send an expedited hello */
+ send_hello(ei, NULL, 0);
+
+ send_update(nbr->ei, nbr, EIGRP_HDR_FLAG_INIT, 0, NULL);
+ }
+}
diff --git a/usr.sbin/eigrpd/in_cksum.c b/usr.sbin/eigrpd/in_cksum.c
new file mode 100644
index 00000000000..daeb3f92ea0
--- /dev/null
+++ b/usr.sbin/eigrpd/in_cksum.c
@@ -0,0 +1,82 @@
+/* $OpenBSD: in_cksum.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+/* $NetBSD: in_cksum.c,v 1.3 1995/04/22 13:53:48 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#) Header: in_cksum.c,v 1.1 92/09/11 01:15:55 leres Exp (LBL)
+ */
+
+#include <sys/types.h>
+
+#include "eigrpd.h"
+#include "log.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * In particular, it should not be this one.
+ */
+uint16_t
+in_cksum(void *p, size_t l)
+{
+ unsigned int sum = 0;
+ int len;
+ unsigned char *cp = p;
+
+ /* ensure that < 2^16 bytes being summed */
+ if (l >= (1 << 16))
+ fatalx("in_cksum: packet too big");
+ len = (int)l;
+
+ if (((long)cp & 1) == 0) {
+ while (len > 1) {
+ sum += htons(*(unsigned short *)cp);
+ cp += 2;
+ len -= 2;
+ }
+ } else {
+ while (len > 1) {
+ sum += *cp++ << 8;
+ sum += *cp++;
+ len -= 2;
+ }
+ }
+ if (len == 1)
+ sum += *cp << 8;
+
+ sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */
+ sum += sum >> 16; /* add potential last carry */
+ sum = ntohs(sum);
+ return (0xffff & ~sum);
+}
diff --git a/usr.sbin/eigrpd/interface.c b/usr.sbin/eigrpd/interface.c
new file mode 100644
index 00000000000..087dc764ba6
--- /dev/null
+++ b/usr.sbin/eigrpd/interface.c
@@ -0,0 +1,753 @@
+/* $OpenBSD: interface.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <string.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+extern struct eigrpd_conf *econf;
+
+void eigrp_if_hello_timer(int, short, void *);
+void eigrp_if_start_hello_timer(struct eigrp_iface *);
+void eigrp_if_stop_hello_timer(struct eigrp_iface *);
+
+static __inline int iface_id_compare(struct eigrp_iface *,
+ struct eigrp_iface *);
+
+RB_HEAD(iface_id_head, eigrp_iface);
+RB_PROTOTYPE(iface_id_head, eigrp_iface, id_tree, iface_id_compare)
+RB_GENERATE(iface_id_head, eigrp_iface, id_tree, iface_id_compare)
+
+static __inline int
+iface_id_compare(struct eigrp_iface *a, struct eigrp_iface *b)
+{
+ return (a->ifaceid - b->ifaceid);
+}
+
+struct iface_id_head ifaces_by_id = RB_INITIALIZER(&ifaces_by_id);
+
+uint32_t ifacecnt = 1;
+
+struct iface *
+if_new(struct eigrpd_conf *xconf, struct kif *kif)
+{
+ struct iface *iface;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ err(1, "if_new: calloc");
+
+ TAILQ_INIT(&iface->ei_list);
+ TAILQ_INIT(&iface->addr_list);
+
+ strlcpy(iface->name, kif->ifname, sizeof(iface->name));
+
+ /* get type */
+ if (kif->flags & IFF_POINTOPOINT)
+ iface->type = IF_TYPE_POINTOPOINT;
+ if (kif->flags & IFF_BROADCAST &&
+ kif->flags & IFF_MULTICAST)
+ iface->type = IF_TYPE_BROADCAST;
+ if (kif->flags & IFF_LOOPBACK)
+ iface->type = IF_TYPE_POINTOPOINT;
+
+ /* get index and flags */
+ iface->mtu = kif->mtu;
+ iface->ifindex = kif->ifindex;
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ iface->if_type = kif->if_type;
+ iface->baudrate = kif->baudrate;
+
+ TAILQ_INSERT_TAIL(&xconf->iface_list, iface, entry);
+
+ return (iface);
+}
+
+void
+if_del(struct iface *iface)
+{
+ struct eigrp_iface *ei;
+ struct if_addr *if_addr;
+
+ log_debug("%s: interface %s", __func__, iface->name);
+
+ while ((ei = TAILQ_FIRST(&iface->ei_list)) != NULL)
+ eigrp_if_del(ei);
+
+ while ((if_addr = TAILQ_FIRST(&iface->addr_list)) != NULL) {
+ TAILQ_REMOVE(&iface->addr_list, if_addr, entry);
+ free(if_addr);
+ }
+
+ TAILQ_REMOVE(&econf->iface_list, iface, entry);
+ free(iface);
+}
+
+void
+if_init(struct eigrpd_conf *xconf, struct iface *iface)
+{
+ struct ifreq ifr;
+ unsigned int rdomain;
+ struct eigrp_iface *ei;
+ union eigrpd_addr addr;
+
+ memset(&addr, 0, sizeof(addr));
+ TAILQ_FOREACH(ei, &iface->ei_list, i_entry) {
+ /* init the dummy self neighbor */
+ ei->self = nbr_new(ei, &addr, 0, 1);
+ nbr_init(ei->self);
+
+ /* set event handlers for interface */
+ evtimer_set(&ei->hello_timer, eigrp_if_hello_timer, ei);
+ }
+
+ /* set rdomain */
+ strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
+ if (ioctl(econf->eigrp_socket_v4, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1)
+ rdomain = 0;
+ else {
+ rdomain = ifr.ifr_rdomainid;
+ if (setsockopt(econf->eigrp_socket_v4, SOL_SOCKET, SO_RTABLE,
+ &rdomain, sizeof(rdomain)) == -1)
+ fatal("failed to set rdomain");
+ }
+ if (rdomain != xconf->rdomain)
+ fatalx("interface rdomain mismatch");
+}
+
+struct iface *
+if_lookup(struct eigrpd_conf *xconf, unsigned int ifindex)
+{
+ struct iface *iface;
+
+ TAILQ_FOREACH(iface, &xconf->iface_list, entry)
+ if (iface->ifindex == ifindex)
+ return (iface);
+
+ return (NULL);
+}
+
+struct if_addr *
+if_addr_new(struct iface *iface, struct kaddr *kaddr)
+{
+ struct if_addr *if_addr;
+ struct eigrp_iface *ei;
+
+ if (if_addr_lookup(&iface->addr_list, kaddr) != NULL)
+ return (NULL);
+
+ if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
+ fatal("if_addr_new");
+
+ if_addr->af = kaddr->af;
+ memcpy(&if_addr->addr, &kaddr->addr, sizeof(if_addr->addr));
+ if_addr->prefixlen = kaddr->prefixlen;
+ memcpy(&if_addr->dstbrd, &kaddr->dstbrd, sizeof(if_addr->dstbrd));
+
+ TAILQ_INSERT_TAIL(&iface->addr_list, if_addr, entry);
+
+ TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
+ if (ei->state == IF_STA_ACTIVE && ei->eigrp->af == if_addr->af)
+ eigrpe_orig_local_route(ei, if_addr, 0);
+
+ if_update(iface, if_addr->af);
+
+ return (if_addr);
+}
+
+void
+if_addr_del(struct iface *iface, struct kaddr *kaddr)
+{
+ struct if_addr *if_addr;
+ struct eigrp_iface *ei;
+
+ if_addr = if_addr_lookup(&iface->addr_list, kaddr);
+ if (if_addr == NULL)
+ return;
+
+ TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
+ if (ei->state == IF_STA_ACTIVE && ei->eigrp->af == if_addr->af)
+ eigrpe_orig_local_route(ei, if_addr, 1);
+
+ TAILQ_REMOVE(&iface->addr_list, if_addr, entry);
+ if_update(iface, if_addr->af);
+ free(if_addr);
+}
+
+struct if_addr *
+if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *kaddr)
+{
+ struct if_addr *if_addr;
+ int af = kaddr->af;
+
+ TAILQ_FOREACH(if_addr, addr_list, entry)
+ if (!eigrp_addrcmp(af, &if_addr->addr, &kaddr->addr) &&
+ if_addr->prefixlen == kaddr->prefixlen &&
+ !eigrp_addrcmp(af, &if_addr->dstbrd, &kaddr->dstbrd))
+ return (if_addr);
+
+ return (NULL);
+}
+
+in_addr_t
+if_primary_addr(struct iface *iface)
+{
+ struct if_addr *if_addr;
+
+ TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
+ if (if_addr->af == AF_INET)
+ return (if_addr->addr.v4.s_addr);
+
+ return (INADDR_ANY);
+}
+
+uint8_t
+if_primary_addr_prefixlen(struct iface *iface)
+{
+ struct if_addr *if_addr;
+
+ TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
+ if (if_addr->af == AF_INET)
+ return (if_addr->prefixlen);
+
+ return (0);
+}
+
+/* up/down events */
+void
+if_update(struct iface *iface, int af)
+{
+ struct eigrp_iface *ei;
+ int link_ok;
+
+ link_ok = (iface->flags & IFF_UP) &&
+ LINK_STATE_IS_UP(iface->linkstate);
+
+ TAILQ_FOREACH(ei, &iface->ei_list, i_entry) {
+ if (af != AF_UNSPEC && ei->eigrp->af != af)
+ continue;
+
+ if (ei->state == IF_STA_DOWN) {
+ if (!link_ok)
+ continue;
+ if (af == AF_INET && TAILQ_EMPTY(&iface->addr_list))
+ continue;
+ ei->state = IF_STA_ACTIVE;
+ eigrp_if_start(ei);
+ } else {
+ if (link_ok)
+ continue;
+ if (!(af == AF_INET && TAILQ_EMPTY(&iface->addr_list)))
+ continue;
+ ei->state = IF_STA_DOWN;
+ eigrp_if_reset(ei);
+ }
+ }
+}
+
+struct eigrp_iface *
+eigrp_if_new(struct eigrpd_conf *xconf, struct eigrp *eigrp, struct kif *kif)
+{
+ struct iface *iface;
+ struct eigrp_iface *ei;
+ struct timeval now;
+
+ iface = if_lookup(xconf, kif->ifindex);
+ if (iface == NULL)
+ iface = if_new(xconf, kif);
+
+ if ((ei = calloc(1, sizeof(*ei))) == NULL)
+ err(1, "eigrp_if_new: calloc");
+
+ ei->state = IF_STA_DOWN;
+ /* get next unused ifaceid */
+ while (eigrp_iface_find_id(ifacecnt++))
+ ;
+ ei->ifaceid = ifacecnt;
+ ei->eigrp = eigrp;
+ ei->iface = iface;
+ if (ei->iface->flags & IFF_LOOPBACK)
+ ei->passive = 1;
+
+ gettimeofday(&now, NULL);
+ ei->uptime = now.tv_sec;
+
+ TAILQ_INIT(&ei->nbr_list);
+ TAILQ_INIT(&ei->update_list);
+ TAILQ_INIT(&ei->query_list);
+ TAILQ_INSERT_TAIL(&iface->ei_list, ei, i_entry);
+ TAILQ_INSERT_TAIL(&eigrp->ei_list, ei, e_entry);
+ if (RB_INSERT(iface_id_head, &ifaces_by_id, ei) != NULL)
+ fatalx("eigrp_if_new: RB_INSERT(ifaces_by_id) failed");
+
+ return (ei);
+}
+
+void
+eigrp_if_del(struct eigrp_iface *ei)
+{
+ struct nbr *nbr;
+
+ RB_REMOVE(iface_id_head, &ifaces_by_id, ei);
+ TAILQ_REMOVE(&ei->eigrp->ei_list, ei, e_entry);
+ TAILQ_REMOVE(&ei->iface->ei_list, ei, i_entry);
+
+ if (ei->state == IF_STA_ACTIVE)
+ eigrp_if_reset(ei);
+
+ while ((nbr = TAILQ_FIRST(&ei->nbr_list)) != NULL)
+ nbr_del(nbr);
+
+ if (TAILQ_EMPTY(&ei->iface->ei_list))
+ if_del(ei->iface);
+
+ free(ei);
+}
+
+void
+eigrp_if_start(struct eigrp_iface *ei)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct if_addr *if_addr;
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ log_debug("%s: %s as %u family %s", __func__, ei->iface->name,
+ eigrp->as, af_name(eigrp->af));
+
+ TAILQ_FOREACH(if_addr, &ei->iface->addr_list, entry) {
+ if (if_addr->af != eigrp->af)
+ continue;
+
+ eigrpe_orig_local_route(ei, if_addr, 0);
+ }
+
+ if (ei->passive)
+ return;
+
+ switch (eigrp->af) {
+ case AF_INET:
+ addr4.s_addr = AllEIGRPRouters_v4;
+ if (if_join_ipv4_group(ei->iface, &addr4))
+ return;
+ break;
+ case AF_INET6:
+ inet_pton(AF_INET6, AllEIGRPRouters_v6, &addr6);
+ if (if_join_ipv6_group(ei->iface, &addr6))
+ return;
+ break;
+ default:
+ break;
+ }
+
+ eigrp_if_start_hello_timer(ei);
+}
+
+void
+eigrp_if_reset(struct eigrp_iface *ei)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ log_debug("%s: %s as %u family %s", __func__, ei->iface->name,
+ eigrp->as, af_name(eigrp->af));
+
+ /* the rde will withdraw the connected route for us */
+
+ if (ei->passive)
+ return;
+
+ /* try to cleanup */
+ switch (eigrp->af) {
+ case AF_INET:
+ addr4.s_addr = AllEIGRPRouters_v4;
+ if_leave_ipv4_group(ei->iface, &addr4);
+ break;
+ case AF_INET6:
+ inet_pton(AF_INET6, AllEIGRPRouters_v6, &addr6);
+ if_leave_ipv6_group(ei->iface, &addr6);
+ break;
+ default:
+ break;
+ }
+
+ eigrp_if_stop_hello_timer(ei);
+}
+
+struct eigrp_iface *
+eigrp_iface_find_id(uint32_t ifaceid)
+{
+ struct eigrp_iface e;
+ e.ifaceid = ifaceid;
+ return (RB_FIND(iface_id_head, &ifaces_by_id, &e));
+}
+
+struct eigrp_iface *
+eigrp_if_lookup(struct iface *iface, int af, uint16_t as)
+{
+ struct eigrp_iface *ei;
+
+ TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
+ if (ei->eigrp->af == af &&
+ ei->eigrp->as == as)
+ return (ei);
+
+ return (NULL);
+}
+
+/* timers */
+/* ARGSUSED */
+void
+eigrp_if_hello_timer(int fd, short event, void *arg)
+{
+ struct eigrp_iface *ei = arg;
+ struct timeval tv;
+
+ send_hello(ei, NULL, 0);
+
+ /* reschedule hello_timer */
+ timerclear(&tv);
+ tv.tv_sec = ei->hello_interval;
+ if (evtimer_add(&ei->hello_timer, &tv) == -1)
+ fatal("eigrp_if_hello_timer");
+}
+
+void
+eigrp_if_start_hello_timer(struct eigrp_iface *ei)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = ei->hello_interval;
+ if (evtimer_add(&ei->hello_timer, &tv) == -1)
+ fatal("eigrp_if_start_hello_timer");
+}
+
+void
+eigrp_if_stop_hello_timer(struct eigrp_iface *ei)
+{
+ if (evtimer_pending(&ei->hello_timer, NULL) &&
+ evtimer_del(&ei->hello_timer) == -1)
+ fatal("eigrp_if_stop_hello_timer");
+}
+
+struct ctl_iface *
+if_to_ctl(struct eigrp_iface *ei)
+{
+ static struct ctl_iface ictl;
+ struct timeval now;
+ struct nbr *nbr;
+
+ ictl.af = ei->eigrp->af;
+ ictl.as = ei->eigrp->as;
+ memcpy(ictl.name, ei->iface->name, sizeof(ictl.name));
+ ictl.ifindex = ei->iface->ifindex;
+ switch (ei->eigrp->af) {
+ case AF_INET:
+ ictl.addr.v4.s_addr = if_primary_addr(ei->iface);
+ ictl.prefixlen = if_primary_addr_prefixlen(ei->iface);
+ break;
+ case AF_INET6:
+ memcpy(&ictl.addr.v6, &ei->iface->linklocal,
+ sizeof(ictl.addr.v6));
+ ictl.prefixlen = 64;
+ break;
+ default:
+ break;
+ }
+ ictl.flags = ei->iface->flags;
+ ictl.linkstate = ei->iface->linkstate;
+ ictl.mtu = ei->iface->mtu;
+ ictl.type = ei->iface->type;
+ ictl.if_type = ei->iface->if_type;
+ ictl.baudrate = ei->iface->baudrate;
+ ictl.delay = ei->delay;
+ ictl.bandwidth = ei->bandwidth;
+ ictl.hello_holdtime = ei->hello_holdtime;
+ ictl.hello_interval = ei->hello_interval;
+ ictl.splithorizon = ei->splithorizon;
+ ictl.passive = ei->passive;
+ ictl.nbr_cnt = 0;
+
+ gettimeofday(&now, NULL);
+ if (ei->state != IF_STA_DOWN && ei->uptime != 0)
+ ictl.uptime = now.tv_sec - ei->uptime;
+ else
+ ictl.uptime = 0;
+
+ TAILQ_FOREACH(nbr, &ei->nbr_list, entry)
+ if (!(nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF)))
+ ictl.nbr_cnt++;
+
+ return (&ictl);
+}
+
+/* misc */
+void
+if_set_sockbuf(int fd)
+{
+ int bsize;
+
+ bsize = 65535;
+ while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
+ sizeof(bsize)) == -1)
+ bsize /= 2;
+
+ if (bsize != 65535)
+ log_warnx("%s: recvbuf size only %d", __func__, bsize);
+
+ bsize = 65535;
+ while (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bsize,
+ sizeof(bsize)) == -1)
+ bsize /= 2;
+
+ if (bsize != 65535)
+ log_warnx("%s: sendbuf size only %d", __func__, bsize);
+}
+
+int
+if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ if (iface->group_count_v4++ != 0)
+ /* already joined */
+ return (0);
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ inet_ntoa(*addr));
+
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = if_primary_addr(iface);
+
+ if (setsockopt(econf->eigrp_socket_v4, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
+ __func__, iface->name, inet_ntoa(*addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+
+ if (--iface->group_count_v4 != 0)
+ /* others still joined */
+ return (0);
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ inet_ntoa(*addr));
+
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = if_primary_addr(iface);
+
+ if (setsockopt(econf->eigrp_socket_v4, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
+ "address %s", iface->name, __func__, inet_ntoa(*addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_TTL to %d",
+ __func__, ttl);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv4_mcast(struct iface *iface)
+{
+ in_addr_t addr;
+
+ addr = if_primary_addr(iface);
+
+ if (setsockopt(econf->eigrp_socket_v4, IPPROTO_IP, IP_MULTICAST_IF,
+ &addr, sizeof(addr)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_IF, interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv4_mcast_loop(int fd)
+{
+ uint8_t loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0) {
+ log_warn("%s: error setting IP_MULTICAST_LOOP", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv4_recvif(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("%s: error setting IP_RECVIF", __func__);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+if_set_ipv4_hdrincl(int fd)
+{
+ int hincl = 1;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
+ log_warn("%s: error setting IP_HDRINCL", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ if (iface->group_count_v6++ != 0)
+ /* already joined */
+ return (0);
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ log_in6addr(addr));
+
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(econf->eigrp_socket_v6, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
+ __func__, iface->name, log_in6addr(addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ if (--iface->group_count_v6 != 0)
+ /* others still joined */
+ return (0);
+
+ log_debug("%s: interface %s addr %s", __func__, iface->name,
+ log_in6addr(addr));
+
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(econf->eigrp_socket_v6, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
+ __func__, iface->name, log_in6addr(addr));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv6_mcast(struct iface *iface)
+{
+ if (setsockopt(econf->eigrp_socket_v6, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &iface->ifindex, sizeof(iface->ifindex)) < 0) {
+ log_debug("%s: error setting IPV6_MULTICAST_IF, interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv6_mcast_loop(int fd)
+{
+ unsigned int loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (unsigned int *)&loop, sizeof(loop)) < 0) {
+ log_warn("%s: error setting IPV6_MULTICAST_LOOP", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv6_pktinfo(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("%s: error setting IPV6_PKTINFO", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ipv6_dscp(int fd, int dscp)
+{
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp,
+ sizeof(dscp)) < 0) {
+ log_warn("%s: error setting IPV6_TCLASS", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/eigrpd/kroute.c b/usr.sbin/eigrpd/kroute.c
new file mode 100644
index 00000000000..7d0c081b1eb
--- /dev/null
+++ b/usr.sbin/eigrpd/kroute.c
@@ -0,0 +1,1588 @@
+/* $OpenBSD: kroute.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/tree.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "eigrpd.h"
+#include "log.h"
+
+extern struct eigrpd_conf *eigrpd_conf;
+
+struct {
+ uint32_t rtseq;
+ pid_t pid;
+ int fib_sync;
+ int fd;
+ struct event ev;
+ unsigned int rdomain;
+} kr_state;
+
+struct kroute_node {
+ TAILQ_ENTRY(kroute_node) entry;
+ struct kroute_priority *kprio; /* back pointer */
+ struct kroute r;
+};
+
+struct kroute_priority {
+ TAILQ_ENTRY(kroute_priority) entry;
+ struct kroute_prefix *kp; /* back pointer */
+ uint8_t priority;
+ TAILQ_HEAD(, kroute_node) nexthops;
+};
+
+struct kroute_prefix {
+ RB_ENTRY(kroute_prefix) entry;
+ int af;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ TAILQ_HEAD(plist, kroute_priority) priorities;
+};
+
+struct kif_addr {
+ TAILQ_ENTRY(kif_addr) entry;
+ struct kaddr a;
+};
+
+struct kif_node {
+ RB_ENTRY(kif_node) entry;
+ TAILQ_HEAD(, kif_addr) addrs;
+ struct kif k;
+};
+
+void kr_redist_remove(struct kroute *);
+int kr_redist_eval(struct kroute *);
+void kr_redistribute(struct kroute_prefix *);
+int kroute_compare(struct kroute_prefix *,
+ struct kroute_prefix *);
+struct kroute_prefix *kroute_find_prefix(int, union eigrpd_addr *, uint8_t);
+struct kroute_priority *kroute_find_prio(struct kroute_prefix *, uint8_t);
+struct kroute_node *kroute_find_gw(struct kroute_priority *,
+ union eigrpd_addr *);
+struct kroute_node *kroute_insert(struct kroute *);
+int kroute_remove(struct kroute *);
+void kroute_clear(void);
+
+int kif_compare(struct kif_node *, struct kif_node *);
+struct kif_node *kif_find(unsigned short);
+struct kif_node *kif_insert(unsigned short);
+int kif_remove(struct kif_node *);
+struct kif *kif_update(unsigned short, int, struct if_data *,
+ struct sockaddr_dl *);
+int kif_validate(unsigned short);
+
+void protect_lo(void);
+uint8_t prefixlen_classful(in_addr_t);
+void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+void if_change(unsigned short, int, struct if_data *,
+ struct sockaddr_dl *);
+void if_newaddr(unsigned short, struct sockaddr *, struct sockaddr *,
+ struct sockaddr *);
+void if_deladdr(unsigned short, struct sockaddr *, struct sockaddr *,
+ struct sockaddr *);
+void if_announce(void *);
+
+int send_rtmsg(int, int, struct kroute *);
+int dispatch_rtmsg(void);
+int fetchtable(void);
+int fetchifs(void);
+int rtmsg_process(char *, size_t);
+int rtmsg_process_route(struct rt_msghdr *,
+ struct sockaddr *[RTAX_MAX]);
+
+RB_HEAD(kroute_tree, kroute_prefix) krt;
+RB_PROTOTYPE(kroute_tree, kroute_prefix, entry, kroute_compare)
+RB_GENERATE(kroute_tree, kroute_prefix, entry, kroute_compare)
+
+RB_HEAD(kif_tree, kif_node) kit;
+RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
+RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
+
+int
+kif_init(void)
+{
+ RB_INIT(&kit);
+ kr_state.fib_sync = 0; /* decoupled */
+
+ if (fetchifs() == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+kr_init(int fs, unsigned int rdomain)
+{
+ int opt = 0, rcvbuf, default_rcvbuf;
+ socklen_t optlen;
+
+ kr_state.fib_sync = fs;
+ kr_state.rdomain = rdomain;
+
+ if ((kr_state.fd = socket(AF_ROUTE,
+ SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1) {
+ log_warn("%s: socket", __func__);
+ return (-1);
+ }
+
+ /* not interested in my own messages */
+ if (setsockopt(kr_state.fd, SOL_SOCKET, SO_USELOOPBACK,
+ &opt, sizeof(opt)) == -1)
+ log_warn("%s: setsockopt", __func__); /* not fatal */
+
+ /* grow receive buffer, don't wanna miss messages */
+ optlen = sizeof(default_rcvbuf);
+ if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
+ &default_rcvbuf, &optlen) == -1)
+ log_warn("%s: getsockopt SOL_SOCKET SO_RCVBUF", __func__);
+ else
+ for (rcvbuf = MAX_RTSOCK_BUF;
+ rcvbuf > default_rcvbuf &&
+ setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
+ rcvbuf /= 2)
+ ; /* nothing */
+
+ kr_state.pid = getpid();
+ kr_state.rtseq = 1;
+
+ if (fetchtable() == -1)
+ return (-1);
+
+ protect_lo();
+
+ event_set(&kr_state.ev, kr_state.fd, EV_READ | EV_PERSIST,
+ kr_dispatch_msg, NULL);
+ event_add(&kr_state.ev, NULL);
+
+ return (0);
+}
+
+void
+kif_redistribute(void)
+{
+ struct kif_node *kif;
+ struct kif_addr *ka;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ TAILQ_FOREACH(ka, &kif->addrs, entry)
+ main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a,
+ sizeof(struct kaddr));
+}
+
+int
+kr_change(struct kroute *kr)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+ int action = RTM_ADD;
+
+ kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
+ if (kp == NULL)
+ kn = kroute_insert(kr);
+ else {
+ kprio = kroute_find_prio(kp, kr->priority);
+ if (kprio == NULL)
+ kn = kroute_insert(kr);
+ else {
+ kn = kroute_find_gw(kprio, &kr->nexthop);
+ if (kn == NULL)
+ kn = kroute_insert(kr);
+ else
+ action = RTM_CHANGE;
+ }
+ }
+
+ /* send update */
+ if (send_rtmsg(kr_state.fd, action, kr) == -1)
+ return (-1);
+
+ kn->r.flags |= F_EIGRPD_INSERTED;
+
+ return (0);
+}
+
+int
+kr_delete(struct kroute *kr)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
+ if (kp == NULL)
+ return (0);
+ kprio = kroute_find_prio(kp, kr->priority);
+ if (kprio == NULL)
+ return (0);
+ kn = kroute_find_gw(kprio, &kr->nexthop);
+ if (kn == NULL)
+ return (0);
+
+ if (!(kn->r.flags & F_EIGRPD_INSERTED))
+ return (0);
+
+ if (send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r) == -1)
+ return (-1);
+
+ if (kroute_remove(kr) == -1)
+ return (-1);
+
+ return (0);
+}
+
+void
+kr_shutdown(void)
+{
+ kr_fib_decouple();
+ kroute_clear();
+ kif_clear();
+}
+
+void
+kr_fib_couple(void)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ if (kr_state.fib_sync == 1) /* already coupled */
+ return;
+
+ kr_state.fib_sync = 1;
+
+ RB_FOREACH(kp, kroute_tree, &krt)
+ TAILQ_FOREACH(kprio, &kp->priorities, entry)
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
+ if (!(kn->r.flags & F_EIGRPD_INSERTED))
+ continue;
+ send_rtmsg(kr_state.fd, RTM_ADD, &kn->r);
+ }
+
+ log_info("kernel routing table coupled");
+}
+
+void
+kr_fib_decouple(void)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ if (kr_state.fib_sync == 0) /* already decoupled */
+ return;
+
+ RB_FOREACH(kp, kroute_tree, &krt)
+ TAILQ_FOREACH(kprio, &kp->priorities, entry)
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
+ if (!(kn->r.flags & F_EIGRPD_INSERTED))
+ continue;
+
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r);
+ }
+
+ kr_state.fib_sync = 0;
+
+ log_info("kernel routing table decoupled");
+}
+
+/* ARGSUSED */
+void
+kr_dispatch_msg(int fd, short event, void *bula)
+{
+ if (dispatch_rtmsg() == -1)
+ event_loopexit(NULL);
+}
+
+void
+kr_show_route(struct imsg *imsg)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+ struct kroute kr;
+ int flags;
+
+ if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ return;
+ }
+ memcpy(&flags, imsg->data, sizeof(flags));
+ RB_FOREACH(kp, kroute_tree, &krt)
+ TAILQ_FOREACH(kprio, &kp->priorities, entry)
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
+ if (flags && !(kn->r.flags & flags))
+ continue;
+
+ memcpy(&kr, &kn->r, sizeof(kr));
+ if (kr.priority ==
+ eigrpd_conf->fib_priority_external)
+ kr.flags |= F_CTL_EXTERNAL;
+ main_imsg_compose_eigrpe(IMSG_CTL_KROUTE,
+ imsg->hdr.pid, &kr, sizeof(kr));
+ }
+
+ main_imsg_compose_eigrpe(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
+}
+
+void
+kr_ifinfo(char *ifname, pid_t pid)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (ifname == NULL || !strcmp(ifname, kif->k.ifname)) {
+ main_imsg_compose_eigrpe(IMSG_CTL_IFINFO,
+ pid, &kif->k, sizeof(kif->k));
+ }
+
+ main_imsg_compose_eigrpe(IMSG_CTL_END, pid, NULL, 0);
+}
+
+void
+kr_redist_remove(struct kroute *kr)
+{
+ /* was the route redistributed? */
+ if (!(kr->flags & F_REDISTRIBUTED))
+ return;
+
+ /* remove redistributed flag */
+ kr->flags &= ~F_REDISTRIBUTED;
+ main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, kr, sizeof(*kr));
+}
+
+int
+kr_redist_eval(struct kroute *kr)
+{
+ in_addr_t a;
+
+ /* Only non-eigrpd routes are considered for redistribution. */
+ if (!(kr->flags & F_KERNEL))
+ goto dont_redistribute;
+
+ /* Dynamic routes are not redistributable. */
+ if (kr->flags & F_DYNAMIC)
+ goto dont_redistribute;
+
+ /* interface is not up and running so don't announce */
+ if (kr->flags & F_DOWN)
+ goto dont_redistribute;
+
+ /* filter-out non redistributable addresses */
+ switch (kr->af) {
+ case AF_INET:
+ a = ntohl(kr->prefix.v4.s_addr);
+ if (IN_MULTICAST(a) || IN_BADCLASS(a) ||
+ (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
+ goto dont_redistribute;
+
+ if (kr->nexthop.v4.s_addr == htonl(INADDR_LOOPBACK) &&
+ !(kr->flags & (F_BLACKHOLE|F_REJECT)))
+ goto dont_redistribute;
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_LOOPBACK(&kr->prefix.v6) ||
+ IN6_IS_ADDR_MULTICAST(&kr->prefix.v6) ||
+ IN6_IS_ADDR_LINKLOCAL(&kr->prefix.v6) ||
+ IN6_IS_ADDR_SITELOCAL(&kr->prefix.v6) ||
+ IN6_IS_ADDR_V4MAPPED(&kr->prefix.v6) ||
+ IN6_IS_ADDR_V4COMPAT(&kr->prefix.v6))
+ goto dont_redistribute;
+
+ if (IN6_IS_ADDR_LOOPBACK(&kr->nexthop.v6) &&
+ !(kr->flags & (F_BLACKHOLE|F_REJECT)))
+ goto dont_redistribute;
+ break;
+ default:
+ log_debug("%s: unexpected address-family", __func__);
+ break;
+ }
+
+ /* prefix should be redistributed */
+ kr->flags |= F_REDISTRIBUTED;
+ main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, kr, sizeof(*kr));
+ return (1);
+
+dont_redistribute:
+ kr_redist_remove(kr);
+ return (0);
+}
+
+void
+kr_redistribute(struct kroute_prefix *kp)
+{
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ /* only the highest prio route can be redistributed */
+ TAILQ_FOREACH_REVERSE(kprio, &kp->priorities, plist, entry) {
+ if (kprio == TAILQ_FIRST(&kp->priorities)) {
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry)
+ /* pick just one entry in case of multipath */
+ if (kr_redist_eval(&kn->r))
+ break;
+ } else {
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry)
+ kr_redist_remove(&kn->r);
+ }
+ }
+}
+
+int
+kroute_compare(struct kroute_prefix *a, struct kroute_prefix *b)
+{
+ if (a->af < b->af)
+ return (-1);
+ if (a->af > b->af)
+ return (1);
+
+ switch (a->af) {
+ case AF_INET:
+ if (ntohl(a->prefix.v4.s_addr) <
+ ntohl(b->prefix.v4.s_addr))
+ return (-1);
+ if (ntohl(a->prefix.v4.s_addr) >
+ ntohl(b->prefix.v4.s_addr))
+ return (1);
+ break;
+ case AF_INET6:
+ if (memcmp(a->prefix.v6.s6_addr,
+ b->prefix.v6.s6_addr, 16) < 0)
+ return (-1);
+ if (memcmp(a->prefix.v6.s6_addr,
+ b->prefix.v6.s6_addr, 16) > 0)
+ return (1);
+ break;
+ default:
+ log_debug("%s: unexpected address-family", __func__);
+ break;
+ }
+
+ if (a->prefixlen < b->prefixlen)
+ return (-1);
+ if (a->prefixlen > b->prefixlen)
+ return (1);
+
+ return (0);
+}
+
+/* tree management */
+struct kroute_prefix *
+kroute_find_prefix(int af, union eigrpd_addr *prefix, uint8_t prefixlen)
+{
+ struct kroute_prefix s;
+
+ s.af = af;
+ memcpy(&s.prefix, prefix, sizeof(s.prefix));
+ s.prefixlen = prefixlen;
+
+ return (RB_FIND(kroute_tree, &krt, &s));
+}
+
+struct kroute_priority *
+kroute_find_prio(struct kroute_prefix *kp, uint8_t prio)
+{
+ struct kroute_priority *kprio;
+
+ /* RTP_ANY here picks the lowest priority node */
+ if (prio == RTP_ANY)
+ return (TAILQ_FIRST(&kp->priorities));
+
+ TAILQ_FOREACH(kprio, &kp->priorities, entry)
+ if (kprio->priority == prio)
+ return (kprio);
+
+ return (NULL);
+}
+
+struct kroute_node *
+kroute_find_gw(struct kroute_priority *kprio, union eigrpd_addr *nh)
+{
+ struct kroute_node *kn;
+
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry)
+ if (eigrp_addrcmp(kprio->kp->af, &kn->r.nexthop, nh) == 0)
+ return (kn);
+
+ return (NULL);
+}
+
+struct kroute_node *
+kroute_insert(struct kroute *kr)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio, *tmp = NULL;
+ struct kroute_node *kn;
+
+ kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
+ if (kp == NULL) {
+ kp = calloc(1, sizeof((*kp)));
+ if (kp == NULL)
+ fatal("kroute_insert");
+ kp->af = kr->af;
+ memcpy(&kp->prefix, &kr->prefix, sizeof(kp->prefix));
+ kp->prefixlen = kr->prefixlen;
+ TAILQ_INIT(&kp->priorities);
+ RB_INSERT(kroute_tree, &krt, kp);
+ }
+
+ kprio = kroute_find_prio(kp, kr->priority);
+ if (kprio == NULL) {
+ kprio = calloc(1, sizeof(*kprio));
+ if (kprio == NULL)
+ fatal("kroute_insert");
+ kprio->kp = kp;
+ kprio->priority = kr->priority;
+ TAILQ_INIT(&kprio->nexthops);
+
+ /* lower priorities first */
+ TAILQ_FOREACH(tmp, &kp->priorities, entry)
+ if (tmp->priority > kprio->priority)
+ break;
+ if (tmp)
+ TAILQ_INSERT_BEFORE(tmp, kprio, entry);
+ else
+ TAILQ_INSERT_TAIL(&kp->priorities, kprio, entry);
+ }
+
+ kn = kroute_find_gw(kprio, &kr->nexthop);
+ if (kn == NULL) {
+ kn = calloc(1, sizeof(*kn));
+ if (kn == NULL)
+ fatal("kroute_insert");
+ kn->kprio = kprio;
+ memcpy(&kn->r, kr, sizeof(kn->r));
+ TAILQ_INSERT_TAIL(&kprio->nexthops, kn, entry);
+ }
+
+ if (!(kr->flags & F_KERNEL)) {
+ /* don't validate or redistribute eigrp route */
+ kr->flags &= ~F_DOWN;
+ return (kn);
+ }
+
+ if (kif_validate(kr->ifindex))
+ kr->flags &= ~F_DOWN;
+ else
+ kr->flags |= F_DOWN;
+
+ kr_redistribute(kp);
+ return (kn);
+}
+
+int
+kroute_remove(struct kroute *kr)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ kp = kroute_find_prefix(kr->af, &kr->prefix, kr->prefixlen);
+ if (kp == NULL)
+ goto notfound;
+ kprio = kroute_find_prio(kp, kr->priority);
+ if (kprio == NULL)
+ goto notfound;
+ kn = kroute_find_gw(kprio, &kr->nexthop);
+ if (kn == NULL)
+ goto notfound;
+
+ kr_redist_remove(&kn->r);
+
+ TAILQ_REMOVE(&kprio->nexthops, kn, entry);
+ free(kn);
+
+ if (TAILQ_EMPTY(&kprio->nexthops)) {
+ TAILQ_REMOVE(&kp->priorities, kprio, entry);
+ free(kprio);
+ }
+
+ if (TAILQ_EMPTY(&kp->priorities)) {
+ if (RB_REMOVE(kroute_tree, &krt, kp) == NULL)
+ log_warnx("%s failed for %s/%u", __func__,
+ log_addr(kr->af, &kr->prefix), kp->prefixlen);
+ free(kp);
+ } else
+ kr_redistribute(kp);
+
+ return (0);
+
+notfound:
+ log_warnx("%s failed to find %s/%u", __func__,
+ log_addr(kr->af, &kr->prefix), kr->prefixlen);
+ return (-1);
+}
+
+void
+kroute_clear(void)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ while ((kp = RB_MIN(kroute_tree, &krt)) != NULL) {
+ while ((kprio = TAILQ_FIRST(&kp->priorities)) != NULL) {
+ while ((kn = TAILQ_FIRST(&kprio->nexthops)) != NULL) {
+ TAILQ_REMOVE(&kprio->nexthops, kn, entry);
+ free(kn);
+ }
+ TAILQ_REMOVE(&kp->priorities, kprio, entry);
+ free(kprio);
+ }
+ RB_REMOVE(kroute_tree, &krt, kp);
+ free(kp);
+ }
+}
+
+int
+kif_compare(struct kif_node *a, struct kif_node *b)
+{
+ return (b->k.ifindex - a->k.ifindex);
+}
+
+/* tree management */
+struct kif_node *
+kif_find(unsigned short ifindex)
+{
+ struct kif_node s;
+
+ memset(&s, 0, sizeof(s));
+ s.k.ifindex = ifindex;
+
+ return (RB_FIND(kif_tree, &kit, &s));
+}
+
+struct kif *
+kif_findname(char *ifname)
+{
+ struct kif_node *kif;
+
+ RB_FOREACH(kif, kif_tree, &kit)
+ if (!strcmp(ifname, kif->k.ifname))
+ return (&kif->k);
+
+ return (NULL);
+}
+
+struct kif_node *
+kif_insert(unsigned short ifindex)
+{
+ struct kif_node *kif;
+
+ if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
+ return (NULL);
+
+ kif->k.ifindex = ifindex;
+ TAILQ_INIT(&kif->addrs);
+
+ if (RB_INSERT(kif_tree, &kit, kif) != NULL)
+ fatalx("kif_insert: RB_INSERT");
+
+ return (kif);
+}
+
+int
+kif_remove(struct kif_node *kif)
+{
+ struct kif_addr *ka;
+
+ if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
+ log_warnx("%s failed for inteface %s", __func__, kif->k.ifname);
+ return (-1);
+ }
+
+ while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
+ TAILQ_REMOVE(&kif->addrs, ka, entry);
+ free(ka);
+ }
+ free(kif);
+ return (0);
+}
+
+void
+kif_clear(void)
+{
+ struct kif_node *kif;
+
+ while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
+ kif_remove(kif);
+}
+
+struct kif *
+kif_update(unsigned short ifindex, int flags, struct if_data *ifd,
+ struct sockaddr_dl *sdl)
+{
+ struct kif_node *kif;
+
+ if ((kif = kif_find(ifindex)) == NULL) {
+ if ((kif = kif_insert(ifindex)) == NULL)
+ return (NULL);
+ kif->k.nh_reachable = (flags & IFF_UP) &&
+ LINK_STATE_IS_UP(ifd->ifi_link_state);
+ }
+
+ kif->k.flags = flags;
+ kif->k.link_state = ifd->ifi_link_state;
+ kif->k.if_type = ifd->ifi_type;
+ kif->k.baudrate = ifd->ifi_baudrate;
+ kif->k.mtu = ifd->ifi_mtu;
+
+ if (sdl && sdl->sdl_family == AF_LINK) {
+ if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
+ memcpy(kif->k.ifname, sdl->sdl_data,
+ sizeof(kif->k.ifname) - 1);
+ else if (sdl->sdl_nlen > 0)
+ memcpy(kif->k.ifname, sdl->sdl_data,
+ sdl->sdl_nlen);
+ /* string already terminated via calloc() */
+ }
+
+ return (&kif->k);
+}
+
+int
+kif_validate(unsigned short ifindex)
+{
+ struct kif_node *kif;
+
+ if ((kif = kif_find(ifindex)) == NULL)
+ return (0);
+
+ return (kif->k.nh_reachable);
+}
+
+/* misc */
+void
+protect_lo(void)
+{
+ struct kroute kr4, kr6;
+
+ /* special protection for 127/8 */
+ memset(&kr4, 0, sizeof(kr4));
+ kr4.af = AF_INET;
+ kr4.prefix.v4.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
+ kr4.prefixlen = 8;
+ kr4.flags = F_KERNEL|F_CONNECTED;
+ kroute_insert(&kr4);
+
+ /* special protection for ::1 */
+ memset(&kr6, 0, sizeof(kr6));
+ kr6.af = AF_INET6;
+ memcpy(&kr6.prefix.v6, &in6addr_loopback, sizeof(kr6.prefix.v6));
+ kr6.prefixlen = 128;
+ kr6.flags = F_KERNEL|F_CONNECTED;
+ kroute_insert(&kr6);
+}
+
+uint8_t
+prefixlen_classful(in_addr_t ina)
+{
+ /* it hurt to write this. */
+
+ if (ina >= 0xf0000000U) /* class E */
+ return (32);
+ else if (ina >= 0xe0000000U) /* class D */
+ return (4);
+ else if (ina >= 0xc0000000U) /* class C */
+ return (24);
+ else if (ina >= 0x80000000U) /* class B */
+ return (16);
+ else /* class A */
+ return (8);
+}
+
+#define ROUNDUP(a) \
+ (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
+
+void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len));
+ } else
+ rti_info[i] = NULL;
+ }
+}
+
+void
+if_change(unsigned short ifindex, int flags, struct if_data *ifd,
+ struct sockaddr_dl *sdl)
+{
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+ struct kif *kif;
+ uint8_t reachable;
+
+ if ((kif = kif_update(ifindex, flags, ifd, sdl)) == NULL) {
+ log_warn("%s: kif_update(%u)", __func__, ifindex);
+ return;
+ }
+
+ reachable = (kif->flags & IFF_UP) &&
+ LINK_STATE_IS_UP(kif->link_state);
+
+ if (reachable == kif->nh_reachable)
+ return; /* nothing changed wrt nexthop validity */
+
+ kif->nh_reachable = reachable;
+
+ /* notify eigrpe about link state */
+ main_imsg_compose_eigrpe(IMSG_IFINFO, 0, kif, sizeof(struct kif));
+
+ /* notify rde about link going down */
+ if (!kif->nh_reachable)
+ main_imsg_compose_rde(IMSG_IFDOWN, 0, kif, sizeof(struct kif));
+
+ /* update redistribute list */
+ RB_FOREACH(kp, kroute_tree, &krt) {
+ TAILQ_FOREACH(kprio, &kp->priorities, entry) {
+ TAILQ_FOREACH(kn, &kprio->nexthops, entry) {
+ if (kn->r.ifindex != ifindex)
+ continue;
+
+ if (reachable)
+ kn->r.flags &= ~F_DOWN;
+ else
+ kn->r.flags |= F_DOWN;
+ }
+ }
+ kr_redistribute(kp);
+ }
+}
+
+void
+if_newaddr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask,
+ struct sockaddr *brd)
+{
+ struct kif_node *kif;
+ struct sockaddr_in *ifa4, *mask4, *brd4;
+ struct sockaddr_in6 *ifa6, *mask6, *brd6;
+ struct kif_addr *ka;
+ uint32_t a;
+
+ if (ifa == NULL)
+ return;
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("%s: corresponding if %d not found", __func__,
+ ifindex);
+ return;
+ }
+
+ switch (ifa->sa_family) {
+ case AF_INET:
+ ifa4 = (struct sockaddr_in *) ifa;
+ mask4 = (struct sockaddr_in *) mask;
+ brd4 = (struct sockaddr_in *) brd;
+
+ /* filter out unwanted addresses */
+ a = ntohl(ifa4->sin_addr.s_addr);
+ if (IN_MULTICAST(a) || IN_BADCLASS(a) ||
+ (a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
+ return;
+
+ if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
+ fatal("if_newaddr");
+ ka->a.addr.v4.s_addr = ifa4->sin_addr.s_addr;
+ if (mask4)
+ ka->a.prefixlen =
+ mask2prefixlen(mask4->sin_addr.s_addr);
+ if (brd4)
+ ka->a.dstbrd.v4.s_addr = brd4->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ ifa6 = (struct sockaddr_in6 *) ifa;
+ mask6 = (struct sockaddr_in6 *) mask;
+ brd6 = (struct sockaddr_in6 *) brd;
+
+ /* We only care about link-local and global-scope. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_LOOPBACK(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_V4MAPPED(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_V4COMPAT(&ifa6->sin6_addr))
+ return;
+
+ clearscope(&ifa6->sin6_addr);
+
+ if ((ka = calloc(1, sizeof(struct kif_addr))) == NULL)
+ fatal("if_newaddr");
+ ka->a.addr.v6 = ifa6->sin6_addr;
+ if (mask6)
+ ka->a.prefixlen = mask2prefixlen6(mask6);
+ if (brd6)
+ ka->a.dstbrd.v6 = brd6->sin6_addr;
+ break;
+ default:
+ return;
+ }
+
+ ka->a.ifindex = ifindex;
+ ka->a.af = ifa->sa_family;
+ TAILQ_INSERT_TAIL(&kif->addrs, ka, entry);
+
+ /* notify eigrpe about new address */
+ main_imsg_compose_eigrpe(IMSG_NEWADDR, 0, &ka->a,
+ sizeof(struct kaddr));
+}
+
+void
+if_deladdr(unsigned short ifindex, struct sockaddr *ifa, struct sockaddr *mask,
+ struct sockaddr *brd)
+{
+ struct kif_node *kif;
+ struct sockaddr_in *ifa4, *mask4, *brd4;
+ struct sockaddr_in6 *ifa6, *mask6, *brd6;
+ struct kaddr k;
+ struct kif_addr *ka, *nka;
+
+ if (ifa == NULL)
+ return;
+ if ((kif = kif_find(ifindex)) == NULL) {
+ log_warnx("%s: corresponding if %d not found", __func__,
+ ifindex);
+ return;
+ }
+
+ memset(&k, 0, sizeof(k));
+ k.af = ifa->sa_family;
+ switch (ifa->sa_family) {
+ case AF_INET:
+ ifa4 = (struct sockaddr_in *) ifa;
+ mask4 = (struct sockaddr_in *) mask;
+ brd4 = (struct sockaddr_in *) brd;
+
+ k.addr.v4.s_addr = ifa4->sin_addr.s_addr;
+ if (mask4)
+ k.prefixlen = mask2prefixlen(mask4->sin_addr.s_addr);
+ if (brd4)
+ k.dstbrd.v4.s_addr = brd4->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ ifa6 = (struct sockaddr_in6 *) ifa;
+ mask6 = (struct sockaddr_in6 *) mask;
+ brd6 = (struct sockaddr_in6 *) brd;
+
+ /* We only care about link-local and global-scope. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_LOOPBACK(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_V4MAPPED(&ifa6->sin6_addr) ||
+ IN6_IS_ADDR_V4COMPAT(&ifa6->sin6_addr))
+ return;
+
+ clearscope(&ifa6->sin6_addr);
+
+ k.addr.v6 = ifa6->sin6_addr;
+ if (mask6)
+ k.prefixlen = mask2prefixlen6(mask6);
+ if (brd6)
+ k.dstbrd.v6 = brd6->sin6_addr;
+ break;
+ default:
+ return;
+ }
+
+ for (ka = TAILQ_FIRST(&kif->addrs); ka != NULL; ka = nka) {
+ nka = TAILQ_NEXT(ka, entry);
+
+ if (ka->a.af != k.af ||
+ ka->a.prefixlen != k.prefixlen)
+ continue;
+
+ switch (ifa->sa_family) {
+ case AF_INET:
+ if (ka->a.addr.v4.s_addr != k.addr.v4.s_addr ||
+ ka->a.dstbrd.v4.s_addr != k.dstbrd.v4.s_addr)
+ continue;
+ break;
+ case AF_INET6:
+ if (!IN6_ARE_ADDR_EQUAL(&ka->a.addr.v6, &k.addr.v6) ||
+ !IN6_ARE_ADDR_EQUAL(&ka->a.dstbrd.v6, &k.dstbrd.v6))
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ /* notify eigrpe about removed address */
+ main_imsg_compose_eigrpe(IMSG_DELADDR, 0, &ka->a,
+ sizeof(struct kaddr));
+ TAILQ_REMOVE(&kif->addrs, ka, entry);
+ free(ka);
+ return;
+ }
+}
+
+void
+if_announce(void *msg)
+{
+ struct if_announcemsghdr *ifan;
+ struct kif_node *kif;
+
+ ifan = msg;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ kif = kif_insert(ifan->ifan_index);
+ if (kif)
+ strlcpy(kif->k.ifname, ifan->ifan_name,
+ sizeof(kif->k.ifname));
+ break;
+ case IFAN_DEPARTURE:
+ kif = kif_find(ifan->ifan_index);
+ if (kif)
+ kif_remove(kif);
+ break;
+ }
+}
+
+/* rtsock */
+static int
+send_rtmsg_v4(int fd, int action, struct kroute *kr)
+{
+ struct iovec iov[5];
+ struct rt_msghdr hdr;
+ struct sockaddr_in prefix;
+ struct sockaddr_in nexthop;
+ struct sockaddr_in mask;
+ int iovcnt = 0;
+
+ if (kr_state.fib_sync == 0)
+ return (0);
+
+ /* initialize header */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.rtm_version = RTM_VERSION;
+ hdr.rtm_type = action;
+ hdr.rtm_priority = kr->priority;
+ hdr.rtm_tableid = kr_state.rdomain; /* rtableid */
+ if (action == RTM_CHANGE)
+ hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
+ else
+ hdr.rtm_flags = RTF_MPATH;
+ hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
+ hdr.rtm_msglen = sizeof(hdr);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt++].iov_len = sizeof(hdr);
+
+ memset(&prefix, 0, sizeof(prefix));
+ prefix.sin_len = sizeof(prefix);
+ prefix.sin_family = AF_INET;
+ prefix.sin_addr.s_addr = kr->prefix.v4.s_addr;
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_DST;
+ hdr.rtm_msglen += sizeof(prefix);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &prefix;
+ iov[iovcnt++].iov_len = sizeof(prefix);
+
+ if (kr->nexthop.v4.s_addr != 0) {
+ memset(&nexthop, 0, sizeof(nexthop));
+ nexthop.sin_len = sizeof(nexthop);
+ nexthop.sin_family = AF_INET;
+ nexthop.sin_addr.s_addr = kr->nexthop.v4.s_addr;
+ /* adjust header */
+ hdr.rtm_flags |= RTF_GATEWAY;
+ hdr.rtm_addrs |= RTA_GATEWAY;
+ hdr.rtm_msglen += sizeof(nexthop);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &nexthop;
+ iov[iovcnt++].iov_len = sizeof(nexthop);
+ }
+
+ memset(&mask, 0, sizeof(mask));
+ mask.sin_len = sizeof(mask);
+ mask.sin_family = AF_INET;
+ mask.sin_addr.s_addr = prefixlen2mask(kr->prefixlen);
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_NETMASK;
+ hdr.rtm_msglen += sizeof(mask);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &mask;
+ iov[iovcnt++].iov_len = sizeof(mask);
+
+retry:
+ if (writev(fd, iov, iovcnt) == -1) {
+ if (errno == ESRCH) {
+ if (hdr.rtm_type == RTM_CHANGE) {
+ hdr.rtm_type = RTM_ADD;
+ goto retry;
+ } else if (hdr.rtm_type == RTM_DELETE) {
+ log_info("route %s/%u vanished before delete",
+ inet_ntoa(kr->prefix.v4),
+ kr->prefixlen);
+ return (0);
+ }
+ }
+ log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type,
+ inet_ntoa(kr->prefix.v4), kr->prefixlen);
+ return (0);
+ }
+
+ return (0);
+}
+
+static int
+send_rtmsg_v6(int fd, int action, struct kroute *kr)
+{
+ struct iovec iov[5];
+ struct rt_msghdr hdr;
+ struct pad {
+ struct sockaddr_in6 addr;
+ char pad[sizeof(long)]; /* thank you IPv6 */
+ } prefix, nexthop, mask;
+ int iovcnt = 0;
+
+ if (kr_state.fib_sync == 0)
+ return (0);
+
+ /* initialize header */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.rtm_version = RTM_VERSION;
+ hdr.rtm_type = action;
+ hdr.rtm_priority = kr->priority;
+ hdr.rtm_tableid = kr_state.rdomain; /* rtableid */
+ if (action == RTM_CHANGE)
+ hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
+ else
+ hdr.rtm_flags = RTF_MPATH;
+ hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */
+ hdr.rtm_msglen = sizeof(hdr);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt++].iov_len = sizeof(hdr);
+
+ memset(&prefix, 0, sizeof(prefix));
+ prefix.addr.sin6_len = sizeof(struct sockaddr_in6);
+ prefix.addr.sin6_family = AF_INET6;
+ prefix.addr.sin6_addr = kr->prefix.v6;
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_DST;
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &prefix;
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&kr->nexthop.v6)) {
+ memset(&nexthop, 0, sizeof(nexthop));
+ nexthop.addr.sin6_len = sizeof(struct sockaddr_in6);
+ nexthop.addr.sin6_family = AF_INET6;
+ nexthop.addr.sin6_addr = kr->nexthop.v6;
+ nexthop.addr.sin6_scope_id = kr->ifindex;
+ embedscope(&nexthop.addr);
+
+ /* adjust header */
+ hdr.rtm_flags |= RTF_GATEWAY;
+ hdr.rtm_addrs |= RTA_GATEWAY;
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &nexthop;
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
+ }
+
+ memset(&mask, 0, sizeof(mask));
+ mask.addr.sin6_len = sizeof(struct sockaddr_in6);
+ mask.addr.sin6_family = AF_INET6;
+ mask.addr.sin6_addr = *prefixlen2mask6(kr->prefixlen);
+ /* adjust header */
+ if (kr->prefixlen == 128)
+ hdr.rtm_flags |= RTF_HOST;
+ hdr.rtm_addrs |= RTA_NETMASK;
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &mask;
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
+
+retry:
+ if (writev(fd, iov, iovcnt) == -1) {
+ if (errno == ESRCH) {
+ if (hdr.rtm_type == RTM_CHANGE) {
+ hdr.rtm_type = RTM_ADD;
+ goto retry;
+ } else if (hdr.rtm_type == RTM_DELETE) {
+ log_info("route %s/%u vanished before delete",
+ log_in6addr(&kr->prefix.v6),
+ kr->prefixlen);
+ return (0);
+ }
+ }
+ log_warn("%s: action %u, prefix %s/%u", __func__, hdr.rtm_type,
+ log_in6addr(&kr->prefix.v6), kr->prefixlen);
+ return (0);
+ }
+
+ return (0);
+}
+
+int
+send_rtmsg(int fd, int action, struct kroute *kr)
+{
+ switch (kr->af) {
+ case AF_INET:
+ return (send_rtmsg_v4(fd, action, kr));
+ case AF_INET6:
+ return (send_rtmsg_v6(fd, action, kr));
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+int
+fetchtable(void)
+{
+ size_t len;
+ int mib[7];
+ char *buf;
+ int rv;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ mib[6] = kr_state.rdomain; /* rtableid */
+
+ if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ return (-1);
+ }
+ if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ rv = rtmsg_process(buf, len);
+ free(buf);
+
+ return (rv);
+}
+
+int
+fetchifs(void)
+{
+ size_t len;
+ int mib[6];
+ char *buf;
+ int rv;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0; /* wildcard */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchifs");
+ return (-1);
+ }
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+ log_warn("sysctl");
+ free(buf);
+ return (-1);
+ }
+
+ rv = rtmsg_process(buf, len);
+ free(buf);
+
+ return (rv);
+}
+
+int
+dispatch_rtmsg(void)
+{
+ char buf[RT_BUF_SIZE];
+ ssize_t n;
+
+ if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (0);
+ log_warn("%s: read error", __func__);
+ return (-1);
+ }
+
+ if (n == 0) {
+ log_warnx("routing socket closed");
+ return (-1);
+ }
+
+ return (rtmsg_process(buf, n));
+}
+
+int
+rtmsg_process(char *buf, size_t len)
+{
+ struct rt_msghdr *rtm;
+ struct if_msghdr ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ size_t offset;
+ char *next;
+
+ for (offset = 0; offset < len; offset += rtm->rtm_msglen) {
+ next = buf + offset;
+ rtm = (struct rt_msghdr *)next;
+ if (len < offset + sizeof(unsigned short) ||
+ len < offset + rtm->rtm_msglen)
+ fatalx("rtmsg_process: partial rtm in buffer");
+ if (rtm->rtm_version != RTM_VERSION)
+ continue;
+
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ switch (rtm->rtm_type) {
+ case RTM_ADD:
+ case RTM_GET:
+ case RTM_CHANGE:
+ case RTM_DELETE:
+ if (rtm->rtm_pid == kr_state.pid)
+ continue;
+
+ if (rtm->rtm_errno) /* failed attempts... */
+ continue;
+
+ if (rtm->rtm_tableid != kr_state.rdomain)
+ continue;
+
+ /* Skip ARP/ND cache and broadcast routes. */
+ if (rtm->rtm_flags & (RTF_LLINFO|RTF_BROADCAST))
+ continue;
+
+ if (rtmsg_process_route(rtm, rti_info) == -1)
+ return (-1);
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ memcpy(&ifm, next, sizeof(ifm));
+ if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data,
+ (struct sockaddr_dl *)rti_info[RTAX_IFP]);
+ break;
+ case RTM_NEWADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+
+ if_newaddr(ifam->ifam_index,
+ (struct sockaddr *)rti_info[RTAX_IFA],
+ (struct sockaddr *)rti_info[RTAX_NETMASK],
+ (struct sockaddr *)rti_info[RTAX_BRD]);
+ break;
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+ if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA |
+ RTA_BRD)) == 0)
+ break;
+
+ if_deladdr(ifam->ifam_index,
+ (struct sockaddr *)rti_info[RTAX_IFA],
+ (struct sockaddr *)rti_info[RTAX_NETMASK],
+ (struct sockaddr *)rti_info[RTAX_BRD]);
+ break;
+ case RTM_IFANNOUNCE:
+ if_announce(next);
+ break;
+ default:
+ /* ignore for now */
+ break;
+ }
+ }
+
+ return (offset);
+}
+
+int
+rtmsg_process_route(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX])
+{
+ struct sockaddr *sa;
+ struct sockaddr_in *sa_in;
+ struct sockaddr_in6 *sa_in6;
+ struct kroute kr;
+ struct kroute_prefix *kp;
+ struct kroute_priority *kprio;
+ struct kroute_node *kn;
+
+ if ((sa = rti_info[RTAX_DST]) == NULL)
+ return (-1);
+
+ memset(&kr, 0, sizeof(kr));
+ kr.af = sa->sa_family;
+ switch (kr.af) {
+ case AF_INET:
+ kr.prefix.v4.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa_in = (struct sockaddr_in *) rti_info[RTAX_NETMASK];
+ if (sa_in != NULL && sa_in->sin_len != 0)
+ kr.prefixlen = mask2prefixlen(sa_in->sin_addr.s_addr);
+ else if (rtm->rtm_flags & RTF_HOST)
+ kr.prefixlen = 32;
+ else if (kr.prefix.v4.s_addr == INADDR_ANY)
+ kr.prefixlen = 0;
+ else
+ kr.prefixlen = prefixlen_classful(kr.prefix.v4.s_addr);
+ break;
+ case AF_INET6:
+ memcpy(&kr.prefix.v6, &((struct sockaddr_in6 *)sa)->sin6_addr,
+ sizeof(kr.prefix.v6));
+ sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK];
+ if (sa_in6 != NULL && sa_in6->sin6_len != 0)
+ kr.prefixlen = mask2prefixlen6(sa_in6);
+ else if (rtm->rtm_flags & RTF_HOST)
+ kr.prefixlen = 128;
+ else if (IN6_IS_ADDR_UNSPECIFIED(&kr.prefix.v6))
+ kr.prefixlen = 0;
+ else
+ fatalx("in6 net addr without netmask");
+ break;
+ default:
+ return (0);
+ }
+ kr.ifindex = rtm->rtm_index;
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL) {
+ switch (sa->sa_family) {
+ case AF_INET:
+ kr.nexthop.v4.s_addr =
+ ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ sa_in6 = (struct sockaddr_in6 *)sa;
+ recoverscope(sa_in6);
+ kr.nexthop.v6 = sa_in6->sin6_addr;
+ if (sa_in6->sin6_scope_id)
+ kr.ifindex = sa_in6->sin6_scope_id;
+ break;
+ case AF_LINK:
+ kr.flags |= F_CONNECTED;
+ break;
+ }
+ }
+ kr.flags |= F_KERNEL;
+ if (rtm->rtm_flags & RTF_STATIC)
+ kr.flags |= F_STATIC;
+ if (rtm->rtm_flags & RTF_BLACKHOLE)
+ kr.flags |= F_BLACKHOLE;
+ if (rtm->rtm_flags & RTF_REJECT)
+ kr.flags |= F_REJECT;
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ kr.flags |= F_DYNAMIC;
+ kr.priority = rtm->rtm_priority;
+
+
+ if (rtm->rtm_type == RTM_CHANGE) {
+ /*
+ * The kernel doesn't allow RTM_CHANGE for multipath routes.
+ * If we got this message we know that the route has only one
+ * nexthop and we should remove it before installing the same
+ * route with the new nexthop.
+ */
+ kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen);
+ if (kp) {
+ kprio = kroute_find_prio(kp, kr.priority);
+ if (kprio) {
+ kn = TAILQ_FIRST(&kprio->nexthops);
+ if (kn && (kn->r.flags & F_KERNEL))
+ kroute_remove(&kr);
+ }
+ }
+ }
+
+ kn = NULL;
+ kp = kroute_find_prefix(kr.af, &kr.prefix, kr.prefixlen);
+ if (kp) {
+ kprio = kroute_find_prio(kp, kr.priority);
+ if (kprio)
+ kn = kroute_find_gw(kprio, &kr.nexthop);
+ }
+
+ if (rtm->rtm_type == RTM_DELETE) {
+ if (kn == NULL || !(kn->r.flags & F_KERNEL))
+ return (0);
+ return (kroute_remove(&kr));
+ }
+
+ if (!eigrp_addrisset(kr.af, &kr.nexthop) && !(kr.flags & F_CONNECTED)) {
+ log_warnx("%s: no nexthop for %s/%u", __func__,
+ log_addr(kr.af, &kr.prefix), kr.prefixlen);
+ return (-1);
+ }
+
+ if (kr.priority == eigrpd_conf->fib_priority_internal ||
+ kr.priority == eigrpd_conf->fib_priority_external) {
+ log_warnx("alien EIGRP route %s/%d", log_addr(kr.af, &kr.prefix),
+ kr.prefixlen);
+ return (send_rtmsg(kr_state.fd, RTM_DELETE, &kr));
+ }
+
+ if (kn != NULL) {
+ /* update route */
+ memcpy(&kn->r, &kr, sizeof(kn->r));
+
+ if (kif_validate(kn->r.ifindex))
+ kn->r.flags &= ~F_DOWN;
+ else
+ kn->r.flags |= F_DOWN;
+
+ kr_redistribute(kp);
+ } else
+ kroute_insert(&kr);
+
+ return (0);
+}
diff --git a/usr.sbin/eigrpd/log.c b/usr.sbin/eigrpd/log.c
new file mode 100644
index 00000000000..ce8ad3998fc
--- /dev/null
+++ b/usr.sbin/eigrpd/log.c
@@ -0,0 +1,353 @@
+/* $OpenBSD: log.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "eigrpd.h"
+#include "rde.h"
+#include "log.h"
+
+static const char * const procnames[] = {
+ "parent",
+ "eigrpe",
+ "rde"
+};
+
+int debug;
+int verbose;
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+log_verbose(int v)
+{
+ verbose = v;
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (verbose) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal in %s: %s", procnames[eigrpd_process],
+ strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal in %s: %s: %s",
+ procnames[eigrpd_process], emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal in %s: %s",
+ procnames[eigrpd_process], emsg);
+
+ if (eigrpd_process == PROC_MAIN)
+ exit(1);
+ else /* parent copes via SIGCHLD */
+ _exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
+
+#define NUM_LOGS 4
+const char *
+log_sockaddr(void *vp)
+{
+ static char buf[NUM_LOGS][NI_MAXHOST];
+ static int round = 0;
+ struct sockaddr *sa = vp;
+
+ round = (round + 1) % NUM_LOGS;
+
+ if (getnameinfo(sa, sa->sa_len, buf[round], NI_MAXHOST, NULL, 0,
+ NI_NUMERICHOST))
+ return ("(unknown)");
+ else
+ return (buf[round]);
+}
+
+const char *
+log_in6addr(const struct in6_addr *addr)
+{
+ struct sockaddr_in6 sa_in6;
+
+ memset(&sa_in6, 0, sizeof(sa_in6));
+ sa_in6.sin6_len = sizeof(sa_in6);
+ sa_in6.sin6_family = AF_INET6;
+ memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
+
+ recoverscope(&sa_in6);
+
+ return (log_sockaddr(&sa_in6));
+}
+
+const char *
+log_in6addr_scope(const struct in6_addr *addr, unsigned int ifindex)
+{
+ struct sockaddr_in6 sa_in6;
+
+ memset(&sa_in6, 0, sizeof(sa_in6));
+ sa_in6.sin6_len = sizeof(sa_in6);
+ sa_in6.sin6_family = AF_INET6;
+ memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
+
+ addscope(&sa_in6, ifindex);
+
+ return (log_sockaddr(&sa_in6));
+}
+
+const char *
+log_addr(int af, union eigrpd_addr *addr)
+{
+ static char buf[NUM_LOGS][INET6_ADDRSTRLEN];
+ static int round = 0;
+
+ switch (af) {
+ case AF_INET:
+ round = (round + 1) % NUM_LOGS;
+ if (inet_ntop(AF_INET, &addr->v4, buf[round],
+ sizeof(buf[round])) == NULL)
+ return ("???");
+ return (buf[round]);
+ case AF_INET6:
+ return (log_in6addr(&addr->v6));
+ default:
+ break;
+ }
+
+ return ("???");
+}
+
+const char *
+log_prefix(struct rt_node *rn)
+{
+ static char buf[64];
+
+ if (snprintf(buf, sizeof(buf), "%s/%u", log_addr(rn->eigrp->af,
+ &rn->prefix), rn->prefixlen) == -1)
+ return ("???");
+
+ return (buf);
+}
+
+const char *
+opcode_name(uint8_t opcode)
+{
+ switch (opcode) {
+ case EIGRP_OPC_UPDATE:
+ return ("UPDATE");
+ case EIGRP_OPC_REQUEST:
+ return ("REQUEST");
+ case EIGRP_OPC_QUERY:
+ return ("QUERY");
+ case EIGRP_OPC_REPLY:
+ return ("REPLY");
+ case EIGRP_OPC_HELLO:
+ return ("HELLO");
+ case EIGRP_OPC_PROBE:
+ return ("PROBE");
+ case EIGRP_OPC_SIAQUERY:
+ return ("SIAQUERY");
+ case EIGRP_OPC_SIAREPLY:
+ return ("SIAREPLY");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+af_name(int af)
+{
+ switch (af) {
+ case AF_INET:
+ return ("ipv4");
+ case AF_INET6:
+ return ("ipv6");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+if_type_name(enum iface_type type)
+{
+ switch (type) {
+ case IF_TYPE_POINTOPOINT:
+ return ("POINTOPOINT");
+ case IF_TYPE_BROADCAST:
+ return ("BROADCAST");
+ }
+ /* NOTREACHED */
+ return ("UNKNOWN");
+}
+
+const char *
+dual_state_name(int state)
+{
+ switch (state) {
+ case DUAL_STA_PASSIVE:
+ return ("PASSIVE");
+ case DUAL_STA_ACTIVE0:
+ return ("ACTIVE(Oij=0)");
+ case DUAL_STA_ACTIVE1:
+ return ("ACTIVE(Oij=1)");
+ case DUAL_STA_ACTIVE2:
+ return ("ACTIVE(Oij=2)");
+ case DUAL_STA_ACTIVE3:
+ return ("ACTIVE(Oij=3)");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+ext_proto_name(int proto)
+{
+ switch (proto) {
+ case EIGRP_EXT_PROTO_IGRP:
+ return ("IGRP");
+ case EIGRP_EXT_PROTO_EIGRP:
+ return ("EIGRP");
+ case EIGRP_EXT_PROTO_STATIC:
+ return ("Static");
+ case EIGRP_EXT_PROTO_RIP:
+ return ("RIP");
+ case EIGRP_EXT_PROTO_HELLO:
+ return ("HELLO");
+ case EIGRP_EXT_PROTO_OSPF:
+ return ("OSPF");
+ case EIGRP_EXT_PROTO_ISIS:
+ return ("ISIS");
+ case EIGRP_EXT_PROTO_EGP:
+ return ("EGP");
+ case EIGRP_EXT_PROTO_BGP:
+ return ("BGP");
+ case EIGRP_EXT_PROTO_IDRP:
+ return ("IDRP");
+ case EIGRP_EXT_PROTO_CONN:
+ return ("Connected");
+ default:
+ return ("UNKNOWN");
+ }
+}
diff --git a/usr.sbin/eigrpd/log.h b/usr.sbin/eigrpd/log.h
new file mode 100644
index 00000000000..b50cc7f33a4
--- /dev/null
+++ b/usr.sbin/eigrpd/log.h
@@ -0,0 +1,46 @@
+/* $OpenBSD: log.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include <stdarg.h>
+
+void log_init(int);
+void log_verbose(int);
+void logit(int, const char *, ...);
+void vlog(int, const char *, va_list);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void fatal(const char *) __dead;
+void fatalx(const char *) __dead;
+
+const char *log_in6addr(const struct in6_addr *);
+const char *log_in6addr_scope(const struct in6_addr *, unsigned int);
+const char *log_sockaddr(void *);
+const char *log_addr(int, union eigrpd_addr *);
+const char *log_prefix(struct rt_node *);
+const char *opcode_name(uint8_t);
+const char *af_name(int);
+const char *if_type_name(enum iface_type);
+const char *dual_state_name(int);
+const char *ext_proto_name(int);
+
+#endif /* _LOG_H_ */
diff --git a/usr.sbin/eigrpd/neighbor.c b/usr.sbin/eigrpd/neighbor.c
new file mode 100644
index 00000000000..bc61672105f
--- /dev/null
+++ b/usr.sbin/eigrpd/neighbor.c
@@ -0,0 +1,269 @@
+/* $OpenBSD: neighbor.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "eigrpe.h"
+#include "rde.h"
+#include "log.h"
+
+static __inline int nbr_compare(struct nbr *, struct nbr *);
+static __inline int nbr_pid_compare(struct nbr *, struct nbr *);
+
+RB_PROTOTYPE(nbr_addr_head, nbr, addr_tree, nbr_compare)
+RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_compare)
+RB_PROTOTYPE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
+RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
+
+static __inline int
+nbr_compare(struct nbr *a, struct nbr *b)
+{
+ int i;
+
+ if (a->ei->eigrp->af < b->ei->eigrp->af)
+ return (-1);
+ if (a->ei->eigrp->af > b->ei->eigrp->af)
+ return (1);
+ if (a->ei->iface->ifindex < b->ei->iface->ifindex)
+ return (-1);
+ if (a->ei->iface->ifindex > b->ei->iface->ifindex)
+ return (1);
+ if (a->ei->eigrp->as < b->ei->eigrp->as)
+ return (-1);
+ if (a->ei->eigrp->as > b->ei->eigrp->as)
+ return (1);
+
+ switch (a->ei->eigrp->af) {
+ case AF_INET:
+ if (ntohl(a->addr.v4.s_addr) < ntohl(b->addr.v4.s_addr))
+ return (-1);
+ if (ntohl(a->addr.v4.s_addr) > ntohl(b->addr.v4.s_addr))
+ return (1);
+ break;
+ case AF_INET6:
+ i = memcmp(&a->addr.v6, &b->addr.v6, sizeof(struct in6_addr));
+ if (i > 0)
+ return (1);
+ if (i < 0)
+ return (-1);
+ break;
+ default:
+ log_debug("%s: unexpected address-family", __func__);
+ break;
+ }
+
+ return (0);
+}
+
+static __inline int
+nbr_pid_compare(struct nbr *a, struct nbr *b)
+{
+ return (a->peerid - b->peerid);
+}
+
+struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid);
+
+uint32_t peercnt = NBR_CNTSTART;
+
+extern struct eigrpd_conf *econf;
+
+struct nbr *
+nbr_new(struct eigrp_iface *ei, union eigrpd_addr *addr, uint16_t holdtime,
+ int self)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct nbr *nbr;
+
+ if (!self)
+ log_debug("%s: interface %s addr %s as %u", __func__,
+ ei->iface->name, log_addr(eigrp->af, addr), eigrp->as);
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("nbr_new");
+
+ nbr->ei = ei;
+ TAILQ_INSERT_TAIL(&ei->nbr_list, nbr, entry);
+ memcpy(&nbr->addr, addr, sizeof(nbr->addr));
+ nbr->peerid = 0;
+ nbr->hello_holdtime = holdtime;
+ nbr->flags = F_EIGRP_NBR_PENDING;
+ if (self)
+ nbr->flags |= F_EIGRP_NBR_SELF;
+ TAILQ_INIT(&nbr->update_list);
+ TAILQ_INIT(&nbr->query_list);
+ TAILQ_INIT(&nbr->reply_list);
+ TAILQ_INIT(&nbr->retrans_list);
+
+ if (RB_INSERT(nbr_addr_head, &eigrp->nbrs, nbr) != NULL)
+ fatalx("nbr_new: RB_INSERT(eigrp->nbrs) failed");
+
+ /* timeout handling */
+ if (!self) {
+ evtimer_set(&nbr->ev_ack, rtp_ack_timer, nbr);
+ evtimer_set(&nbr->ev_hello_timeout, nbr_timeout, nbr);
+ nbr_start_timeout(nbr);
+ }
+
+ return (nbr);
+}
+
+void
+nbr_init(struct nbr *nbr)
+{
+ struct timeval now;
+ struct rde_nbr rnbr;
+
+ nbr->flags &= ~F_EIGRP_NBR_PENDING;
+
+ gettimeofday(&now, NULL);
+ nbr->uptime = now.tv_sec;
+
+ nbr_update_peerid(nbr);
+
+ memset(&rnbr, 0, sizeof(rnbr));
+ memcpy(&rnbr.addr, &nbr->addr, sizeof(rnbr.addr));
+ rnbr.ifaceid = nbr->ei->ifaceid;
+ if (nbr->flags & F_EIGRP_NBR_SELF)
+ rnbr.flags = F_RDE_NBR_SELF;
+
+ /* rde is not aware of pending nbrs */
+ eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rnbr,
+ sizeof(rnbr));
+}
+
+void
+nbr_del(struct nbr *nbr)
+{
+ struct eigrp *eigrp = nbr->ei->eigrp;
+ struct packet *pkt;
+
+ if (!(nbr->flags & F_EIGRP_NBR_SELF))
+ log_debug("%s: addr %s", __func__,
+ log_addr(eigrp->af, &nbr->addr));
+
+ eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
+
+ nbr_stop_timeout(nbr);
+
+ /* clear retransmission list */
+ while ((pkt = TAILQ_FIRST(&nbr->retrans_list)) != NULL)
+ rtp_packet_del(pkt);
+
+ if (nbr->peerid)
+ RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
+ RB_REMOVE(nbr_addr_head, &eigrp->nbrs, nbr);
+ TAILQ_REMOVE(&nbr->ei->nbr_list, nbr, entry);
+
+ free(nbr);
+}
+
+void
+nbr_update_peerid(struct nbr *nbr)
+{
+ if (nbr->peerid)
+ RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
+
+ /* get next unused peerid */
+ while (nbr_find_peerid(++peercnt))
+ ;
+ nbr->peerid = peercnt;
+
+ if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL)
+ fatalx("nbr_new: RB_INSERT(nbrs_by_pid) failed");
+}
+
+struct nbr *
+nbr_find(struct eigrp_iface *ei, union eigrpd_addr *addr)
+{
+ struct nbr n;
+ struct eigrp_iface i;
+ struct eigrp e;
+
+ e.af = ei->eigrp->af;
+ e.as = ei->eigrp->as;
+ i.eigrp = &e;
+ i.iface = ei->iface;
+ n.ei = &i;
+ memcpy(&n.addr, addr, sizeof(n.addr));
+
+ return (RB_FIND(nbr_addr_head, &ei->eigrp->nbrs, &n));
+}
+
+struct nbr *
+nbr_find_peerid(uint32_t peerid)
+{
+ struct nbr n;
+ n.peerid = peerid;
+ return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n));
+}
+
+struct ctl_nbr *
+nbr_to_ctl(struct nbr *nbr)
+{
+ static struct ctl_nbr nctl;
+ struct timeval now;
+
+ nctl.af = nbr->ei->eigrp->af;
+ nctl.as = nbr->ei->eigrp->as;
+ memcpy(nctl.ifname, nbr->ei->iface->name, sizeof(nctl.ifname));
+ memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
+ nctl.hello_holdtime = nbr->hello_holdtime;
+ gettimeofday(&now, NULL);
+ nctl.uptime = now.tv_sec - nbr->uptime;
+
+ return (&nctl);
+}
+
+/* timers */
+
+/* ARGSUSED */
+void
+nbr_timeout(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct eigrp *eigrp = nbr->ei->eigrp;
+
+ log_debug("%s: neighbor %s", __func__, log_addr(eigrp->af, &nbr->addr));
+
+ nbr_del(nbr);
+}
+
+void
+nbr_start_timeout(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = nbr->hello_holdtime;
+
+ if (evtimer_add(&nbr->ev_hello_timeout, &tv) == -1)
+ fatal("nbr_start_timeout");
+}
+
+void
+nbr_stop_timeout(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->ev_hello_timeout, NULL) &&
+ evtimer_del(&nbr->ev_hello_timeout) == -1)
+ fatal("nbr_stop_timeout");
+}
diff --git a/usr.sbin/eigrpd/packet.c b/usr.sbin/eigrpd/packet.c
new file mode 100644
index 00000000000..4b2670b13a5
--- /dev/null
+++ b/usr.sbin/eigrpd/packet.c
@@ -0,0 +1,729 @@
+/* $OpenBSD: packet.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+extern struct eigrpd_conf *econf;
+
+int ip_hdr_sanity_check(const struct ip *, uint16_t);
+int eigrp_hdr_sanity_check(int, union eigrpd_addr *,
+ struct eigrp_hdr *, uint16_t, const struct iface *);
+struct iface *find_iface(unsigned int, int, union eigrpd_addr *);
+
+int
+gen_eigrp_hdr(struct ibuf *buf, uint16_t opcode, uint8_t flags,
+ uint32_t seq_num, uint16_t as)
+{
+ struct eigrp_hdr eigrp_hdr;
+
+ memset(&eigrp_hdr, 0, sizeof(eigrp_hdr));
+ eigrp_hdr.version = EIGRP_VERSION;
+ eigrp_hdr.opcode = opcode;
+ /* chksum will be set later */
+ eigrp_hdr.flags = htonl(flags);
+ eigrp_hdr.seq_num = htonl(seq_num);
+ /* ack_num will be set later */
+ eigrp_hdr.vrid = htons(EIGRP_VRID_UNICAST_AF);
+ eigrp_hdr.as = htons(as);
+
+ return (ibuf_add(buf, &eigrp_hdr, sizeof(eigrp_hdr)));
+}
+
+/* send and receive packets */
+static int
+send_packet_v4(struct iface *iface, struct nbr *nbr, struct ibuf *buf)
+{
+ struct sockaddr_in dst;
+ struct msghdr msg;
+ struct iovec iov[2];
+ struct ip ip_hdr;
+
+ /* setup sockaddr */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+ if (nbr)
+ dst.sin_addr.s_addr = nbr->addr.v4.s_addr;
+ else
+ dst.sin_addr.s_addr = AllEIGRPRouters_v4;
+
+ /* setup IP hdr */
+ memset(&ip_hdr, 0, sizeof(ip_hdr));
+ ip_hdr.ip_v = IPVERSION;
+ ip_hdr.ip_hl = sizeof(ip_hdr) >> 2;
+ ip_hdr.ip_tos = IPTOS_PREC_INTERNETCONTROL;
+ ip_hdr.ip_len = htons(ibuf_size(buf) + sizeof(ip_hdr));
+ ip_hdr.ip_id = 0; /* 0 means kernel set appropriate value */
+ ip_hdr.ip_off = 0;
+ ip_hdr.ip_ttl = EIGRP_IP_TTL;
+ ip_hdr.ip_p = IPPROTO_EIGRP;
+ ip_hdr.ip_sum = 0;
+ ip_hdr.ip_src.s_addr = if_primary_addr(iface);
+ ip_hdr.ip_dst = dst.sin_addr;
+
+ /* setup buffer */
+ memset(&msg, 0, sizeof(msg));
+ iov[0].iov_base = &ip_hdr;
+ iov[0].iov_len = sizeof(ip_hdr);
+ iov[1].iov_base = buf->buf;
+ iov[1].iov_len = ibuf_size(buf);
+ msg.msg_name = &dst;
+ msg.msg_namelen = sizeof(dst);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ /* set outgoing interface for multicast traffic */
+ if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr)))
+ if (if_set_ipv4_mcast(iface) == -1) {
+ log_warn("%s: error setting multicast interface, %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ if (sendmsg(econf->eigrp_socket_v4, &msg, 0) == -1) {
+ log_warn("%s: error sending packet on interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+send_packet_v6(struct iface *iface, struct nbr *nbr, struct ibuf *buf)
+{
+ struct sockaddr_in6 sa6;
+
+ /* setup sockaddr */
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_len = sizeof(struct sockaddr_in6);
+ if (nbr)
+ sa6.sin6_addr = nbr->addr.v6;
+ else
+ inet_pton(AF_INET6, AllEIGRPRouters_v6, &sa6.sin6_addr);
+ addscope(&sa6, iface->ifindex);
+
+ /* set outgoing interface for multicast traffic */
+ if (IN6_IS_ADDR_MULTICAST(&sa6.sin6_addr))
+ if (if_set_ipv6_mcast(iface) == -1) {
+ log_warn("%s: error setting multicast interface, %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ if (sendto(econf->eigrp_socket_v6, buf->buf, buf->wpos, 0,
+ (struct sockaddr *)&sa6, sizeof(sa6)) == -1) {
+ log_warn("%s: error sending packet on interface %s",
+ __func__, iface->name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+send_packet(struct eigrp_iface *ei, struct nbr *nbr, uint32_t flags,
+ struct ibuf *buf)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct iface *iface = ei->iface;
+ struct eigrp_hdr *eigrp_hdr;
+
+ if (!(iface->flags & IFF_UP) || !LINK_STATE_IS_UP(iface->linkstate))
+ return (-1);
+
+ /* update ack number, flags and checksum */
+ if ((eigrp_hdr = ibuf_seek(buf, 0, sizeof(*eigrp_hdr))) == NULL)
+ fatalx("send_packet: buf_seek failed");
+ if (nbr) {
+ eigrp_hdr->ack_num = htonl(nbr->recv_seq);
+ rtp_ack_stop_timer(nbr);
+ }
+ if (flags) {
+ eigrp_hdr->flags = ntohl(eigrp_hdr->flags) | flags;
+ eigrp_hdr->flags = htonl(eigrp_hdr->flags);
+ }
+ eigrp_hdr->chksum = 0;
+ eigrp_hdr->chksum = in_cksum(buf->buf, ibuf_size(buf));
+
+ /* log packet being sent */
+ if (eigrp_hdr->opcode != EIGRP_OPC_HELLO) {
+ char buffer[64];
+
+ if (nbr)
+ snprintf(buffer, sizeof(buffer), "nbr %s",
+ log_addr(eigrp->af, &nbr->addr));
+ else
+ snprintf(buffer, sizeof(buffer), "(multicast)");
+
+ log_debug("%s: type %s iface %s %s AS %u seq %u ack %u",
+ __func__, opcode_name(eigrp_hdr->opcode), iface->name,
+ buffer, ntohs(eigrp_hdr->as), ntohl(eigrp_hdr->seq_num),
+ ntohl(eigrp_hdr->ack_num));
+ }
+
+ switch (eigrp->af) {
+ case AF_INET:
+ send_packet_v4(iface, nbr, buf);
+ break;
+ case AF_INET6:
+ send_packet_v6(iface, nbr, buf);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+static int
+recv_packet_nbr(struct nbr *nbr, struct eigrp_hdr *eigrp_hdr,
+ struct seq_addr_head *seq_addr_list, struct tlv_mcast_seq *tm)
+{
+ uint32_t seq, ack;
+ struct seq_addr_entry *sa;
+
+ seq = ntohl(eigrp_hdr->seq_num);
+ ack = ntohl(eigrp_hdr->ack_num);
+
+ /*
+ * draft-savage-eigrp-04 - Section 5.3.1:
+ * "In addition to the HELLO packet, if any packet is received within
+ * the hold time period, then the Hold Time period will be reset."
+ */
+ nbr_start_timeout(nbr);
+
+ /* ack processing */
+ if (ack != 0)
+ rtp_process_ack(nbr, ack);
+ if (seq != 0) {
+ /* check for sequence wraparound */
+ if (nbr->recv_seq >= seq &&
+ !(nbr->recv_seq == UINT32_MAX && seq == 1)) {
+ log_debug("%s: duplicate packet", __func__);
+ rtp_send_ack(nbr);
+ return (-1);
+ }
+ nbr->recv_seq = seq;
+ }
+
+ /* handle the sequence tlv */
+ if (eigrp_hdr->opcode == EIGRP_OPC_HELLO &&
+ !TAILQ_EMPTY(seq_addr_list)) {
+ nbr->flags |= F_EIGRP_NBR_CR_MODE;
+
+ TAILQ_FOREACH(sa, seq_addr_list, entry) {
+ switch (sa->af) {
+ case AF_INET:
+ if (sa->addr.v4.s_addr ==
+ if_primary_addr(nbr->ei->iface)) {
+ nbr->flags &= ~F_EIGRP_NBR_CR_MODE;
+ break;
+ }
+ break;
+ case AF_INET6:
+ if (IN6_ARE_ADDR_EQUAL(&sa->addr.v6,
+ &nbr->ei->iface->linklocal)) {
+ nbr->flags &= ~F_EIGRP_NBR_CR_MODE;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (tm)
+ nbr->next_mcast_seq = tm->seq;
+ }
+
+ if ((ntohl(eigrp_hdr->flags) & EIGRP_HDR_FLAG_CR)) {
+ if (!(nbr->flags & F_EIGRP_NBR_CR_MODE))
+ return (-1);
+ if (ntohl(eigrp_hdr->seq_num) != nbr->next_mcast_seq)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+recv_packet(int af, union eigrpd_addr *src, union eigrpd_addr *dest,
+ struct iface *iface, struct eigrp_hdr *eigrp_hdr, char *buf, uint16_t len)
+{
+ struct eigrp_iface *ei;
+ struct nbr *nbr;
+ struct tlv_parameter *tp;
+ struct tlv_sw_version *tv;
+ struct tlv_mcast_seq *tm;
+ struct rinfo ri;
+ struct rinfo_entry *re;
+ int route_af;
+ enum route_type route_type;
+ struct seq_addr_head seq_addr_list;
+ struct rinfo_head rinfo_list;
+
+ /* EIGRP header sanity checks */
+ if (eigrp_hdr_sanity_check(af, dest, eigrp_hdr, len, iface) == -1)
+ return;
+
+ buf += sizeof(*eigrp_hdr);
+ len -= sizeof(*eigrp_hdr);
+
+ TAILQ_INIT(&seq_addr_list);
+ TAILQ_INIT(&rinfo_list);
+ while (len > 0) {
+ struct tlv tlv;
+
+ if (len < sizeof(tlv)) {
+ log_debug("%s: malformed packet (bad length)",
+ __func__);
+ goto error;
+ }
+
+ memcpy(&tlv, buf, sizeof(tlv));
+ if (ntohs(tlv.length) > len) {
+ log_debug("%s: malformed packet (bad length)",
+ __func__);
+ goto error;
+ }
+
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_PARAMETER:
+ if ((tp = tlv_decode_parameter(&tlv, buf)) == NULL)
+ goto error;
+ break;
+ case TLV_TYPE_SEQ:
+ if (tlv_decode_seq(&tlv, buf, &seq_addr_list) < 0)
+ goto error;
+ break;
+ case TLV_TYPE_SW_VERSION:
+ if ((tv = tlv_decode_sw_version(&tlv, buf)) == NULL)
+ goto error;
+ break;
+ case TLV_TYPE_MCAST_SEQ:
+ if ((tm = tlv_decode_mcast_seq(&tlv, buf)) == NULL)
+ goto error;
+ break;
+ case TLV_TYPE_IPV4_INTERNAL:
+ case TLV_TYPE_IPV4_EXTERNAL:
+ case TLV_TYPE_IPV6_INTERNAL:
+ case TLV_TYPE_IPV6_EXTERNAL:
+ switch (ntohs(tlv.type)) {
+ case TLV_TYPE_IPV4_INTERNAL:
+ route_af = AF_INET;
+ route_type = EIGRP_ROUTE_INTERNAL;
+ break;
+ case TLV_TYPE_IPV4_EXTERNAL:
+ route_af = AF_INET;
+ route_type = EIGRP_ROUTE_EXTERNAL;
+ break;
+ case TLV_TYPE_IPV6_INTERNAL:
+ route_af = AF_INET6;
+ route_type = EIGRP_ROUTE_INTERNAL;
+ break;
+ case TLV_TYPE_IPV6_EXTERNAL:
+ route_af = AF_INET6;
+ route_type = EIGRP_ROUTE_EXTERNAL;
+ break;
+ }
+
+ if (tlv_decode_route(route_af, route_type, &tlv, buf,
+ &ri) < 0)
+ goto error;
+ if ((re = calloc(1, sizeof(*re))) == NULL)
+ fatal("recv_packet");
+ memcpy(&re->rinfo, &ri, sizeof(re->rinfo));
+ TAILQ_INSERT_TAIL(&rinfo_list, re, entry);
+ break;
+ case TLV_TYPE_AUTH:
+ case TLV_TYPE_PEER_TERM:
+ /*
+ * XXX There is no enough information in the draft
+ * to implement these TLVs properly.
+ */
+ case TLV_TYPE_IPV4_COMMUNITY:
+ case TLV_TYPE_IPV6_COMMUNITY:
+ /* TODO */
+ default:
+ /* ignore unknown tlv */
+ break;
+ }
+ buf += ntohs(tlv.length);
+ len -= ntohs(tlv.length);
+ }
+
+ ei = eigrp_if_lookup(iface, af, ntohs(eigrp_hdr->as));
+ if (ei == NULL || ei->passive)
+ goto error;
+
+ nbr = nbr_find(ei, src);
+ if (nbr == NULL && (eigrp_hdr->opcode != EIGRP_OPC_HELLO ||
+ ntohl(eigrp_hdr->ack_num) != 0)) {
+ log_debug("%s: unknown neighbor", __func__);
+ goto error;
+ } else if (nbr && recv_packet_nbr(nbr, eigrp_hdr, &seq_addr_list,
+ tm) < 0)
+ goto error;
+
+ /* log packet being received */
+ if (eigrp_hdr->opcode != EIGRP_OPC_HELLO)
+ log_debug("%s: type %s nbr %s AS %u seq %u ack %u", __func__,
+ opcode_name(eigrp_hdr->opcode), log_addr(af, &nbr->addr),
+ ei->eigrp->as, ntohl(eigrp_hdr->seq_num),
+ ntohl(eigrp_hdr->ack_num));
+
+ /* switch EIGRP packet type */
+ switch (eigrp_hdr->opcode) {
+ case EIGRP_OPC_HELLO:
+ if (ntohl(eigrp_hdr->ack_num) == 0)
+ recv_hello(ei, src, nbr, tp);
+ break;
+ case EIGRP_OPC_UPDATE:
+ recv_update(nbr, &rinfo_list, ntohl(eigrp_hdr->flags));
+ break;
+ case EIGRP_OPC_QUERY:
+ recv_query(nbr, &rinfo_list, 0);
+ break;
+ case EIGRP_OPC_REPLY:
+ recv_reply(nbr, &rinfo_list, 0);
+ break;
+ case EIGRP_OPC_SIAQUERY:
+ recv_query(nbr, &rinfo_list, 1);
+ break;
+ case EIGRP_OPC_SIAREPLY:
+ recv_reply(nbr, &rinfo_list, 1);
+ break;
+ default:
+ log_debug("%s: unknown EIGRP packet type, interface %s",
+ __func__, iface->name);
+ }
+
+error:
+ /* free rinfo tlvs */
+ message_list_clr(&rinfo_list);
+ /* free seq addresses tlvs */
+ seq_addr_list_clr(&seq_addr_list);
+}
+
+void
+recv_packet_v4(int fd, short event, void *bula)
+{
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+ } cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ struct ip ip_hdr;
+ struct eigrp_hdr *eigrp_hdr;
+ struct iface *iface;
+ char *buf;
+ struct cmsghdr *cmsg;
+ ssize_t r;
+ uint16_t len;
+ int l;
+ unsigned int ifindex = 0;
+ union eigrpd_addr src, dest;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = buf = pkt_ptr;
+ iov.iov_len = READ_BUF_SIZE;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((r = recvmsg(fd, &msg, 0)) == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ log_debug("%s: read error: %s", __func__,
+ strerror(errno));
+ return;
+ }
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVIF) {
+ ifindex = ((struct sockaddr_dl *)
+ CMSG_DATA(cmsg))->sdl_index;
+ break;
+ }
+ }
+
+ len = (uint16_t)r;
+
+ /* IP header sanity checks */
+ if (len < sizeof(ip_hdr)) {
+ log_debug("%s: bad packet size", __func__);
+ return;
+ }
+ memcpy(&ip_hdr, buf, sizeof(ip_hdr));
+ if ((l = ip_hdr_sanity_check(&ip_hdr, len)) == -1)
+ return;
+ src.v4.s_addr = ip_hdr.ip_src.s_addr;
+ dest.v4.s_addr = ip_hdr.ip_dst.s_addr;
+
+ buf += l;
+ len -= l;
+
+ /* find a matching interface */
+ if ((iface = find_iface(ifindex, AF_INET, &src)) == NULL)
+ return;
+
+ /*
+ * Packet needs to be sent to AllEIGRPRouters_v4 or to one
+ * of the interface addresses.
+ */
+ if (ip_hdr.ip_dst.s_addr != AllEIGRPRouters_v4) {
+ struct if_addr *if_addr;
+ int found = 0;
+
+ TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
+ if (if_addr->af == AF_INET &&
+ ip_hdr.ip_dst.s_addr == if_addr->addr.v4.s_addr) {
+ found = 1;
+ break;
+ }
+ if (found == 0) {
+ log_debug("%s: packet sent to wrong address %s, "
+ "interface %s", __func__, inet_ntoa(ip_hdr.ip_dst),
+ iface->name);
+ return;
+ }
+ }
+
+ if (len < sizeof(*eigrp_hdr)) {
+ log_debug("%s: bad packet size", __func__);
+ return;
+ }
+ eigrp_hdr = (struct eigrp_hdr *)buf;
+
+ recv_packet(AF_INET, &src, &dest, iface, eigrp_hdr, buf, len);
+}
+
+void
+recv_packet_v6(int fd, short event, void *bula)
+{
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ } cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov;
+ struct in6_addr maddr;
+ struct sockaddr_in6 sin6;
+ struct eigrp_hdr *eigrp_hdr;
+ struct iface *iface;
+ char *buf;
+ struct cmsghdr *cmsg;
+ ssize_t r;
+ uint16_t len;
+ unsigned int ifindex = 0;
+ union eigrpd_addr src, dest;
+
+ if (event != EV_READ)
+ return;
+
+ /* setup buffer */
+ memset(&msg, 0, sizeof(msg));
+ iov.iov_base = buf = pkt_ptr;
+ iov.iov_len = READ_BUF_SIZE;
+ msg.msg_name = &sin6;
+ msg.msg_namelen = sizeof(sin6);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((r = recvmsg(fd, &msg, 0)) == -1) {
+ if (errno != EAGAIN && errno != EINTR)
+ log_debug("%s: read error: %s", __func__,
+ strerror(errno));
+ return;
+ }
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ ifindex = ((struct in6_pktinfo *)
+ CMSG_DATA(cmsg))->ipi6_ifindex;
+ dest.v6 = ((struct in6_pktinfo *)
+ CMSG_DATA(cmsg))->ipi6_addr;
+ break;
+ }
+ }
+ src.v6 = sin6.sin6_addr;
+
+ /* find a matching interface */
+ if ((iface = find_iface(ifindex, AF_INET6, &src)) == NULL)
+ return;
+
+ /*
+ * Packet needs to be sent to AllEIGRPRouters_v6 or to the
+ * link local address of the interface.
+ */
+ inet_pton(AF_INET6, AllEIGRPRouters_v6, &maddr);
+
+ if (!IN6_ARE_ADDR_EQUAL(&dest.v6, &maddr) &&
+ !IN6_ARE_ADDR_EQUAL(&dest.v6, &iface->linklocal)) {
+ log_debug("%s: packet sent to wrong address %s, interface %s",
+ __func__, log_in6addr(&dest.v6), iface->name);
+ return;
+ }
+
+ len = (uint16_t)r;
+ if (len < sizeof(*eigrp_hdr)) {
+ log_debug("%s: bad packet size", __func__);
+ return;
+ }
+ eigrp_hdr = (struct eigrp_hdr *)buf;
+
+ recv_packet(AF_INET6, &src, &dest, iface, eigrp_hdr, buf, len);
+}
+
+int
+ip_hdr_sanity_check(const struct ip *ip_hdr, uint16_t len)
+{
+ in_addr_t ipv4;
+
+ if (ntohs(ip_hdr->ip_len) != len) {
+ log_debug("%s: invalid IP packet length %u", __func__,
+ ntohs(ip_hdr->ip_len));
+ return (-1);
+ }
+
+ ipv4 = ntohl(ip_hdr->ip_src.s_addr);
+ if (((ipv4 >> IN_CLASSA_NSHIFT) == 0)
+ || ((ipv4 >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
+ || IN_MULTICAST(ipv4) || IN_BADCLASS(ipv4)) {
+ log_debug("%s: invalid IP source address %s", __func__,
+ inet_ntoa(ip_hdr->ip_src));
+ return (-1);
+ }
+
+ if (ip_hdr->ip_p != IPPROTO_EIGRP)
+ /* this is enforced by the socket itself */
+ fatalx("ip_hdr_sanity_check: invalid IP proto");
+
+ return (ip_hdr->ip_hl << 2);
+}
+
+int
+eigrp_hdr_sanity_check(int af, union eigrpd_addr *addr,
+ struct eigrp_hdr *eigrp_hdr, uint16_t len, const struct iface *iface)
+{
+ if (in_cksum(eigrp_hdr, len)) {
+ log_debug("%s: invalid checksum, interface %s", __func__,
+ iface->name);
+ return (-1);
+ }
+
+ if (eigrp_hdr->version != EIGRP_HEADER_VERSION) {
+ log_debug("%s: invalid EIGRP version %d, interface %s",
+ __func__, eigrp_hdr->version, iface->name);
+ return (-1);
+ }
+
+ if (ntohs(eigrp_hdr->vrid) != EIGRP_VRID_UNICAST_AF) {
+ log_debug("%s: unknown or unsupported vrid %u, interface %s",
+ __func__, ntohs(eigrp_hdr->vrid), iface->name);
+ return (-1);
+ }
+
+ if (eigrp_hdr->opcode == EIGRP_OPC_HELLO &&
+ eigrp_hdr->ack_num != 0) {
+ switch (af) {
+ case AF_INET:
+ if (IN_MULTICAST(addr->v4.s_addr)) {
+ log_debug("%s: multicast ack (ipv4), "
+ "interface %s", __func__, iface->name);
+ return (-1);
+ }
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_MULTICAST(&addr->v6)) {
+ log_debug("%s: multicast ack (ipv6), "
+ "interface %s", __func__, iface->name);
+ return (-1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
+
+struct iface *
+find_iface(unsigned int ifindex, int af, union eigrpd_addr *src)
+{
+ struct iface *iface;
+ struct if_addr *if_addr;
+ uint32_t mask;
+
+ iface = if_lookup(econf, ifindex);
+ if (iface == NULL)
+ return (NULL);
+
+ switch (af) {
+ case AF_INET:
+ /*
+ * From CCNP ROUTE 642-902 OCG:
+ * "EIGRP's rules about neighbor IP addresses being in the same
+ * subnet are less exact than OSPF. OSPF requires matching
+ * subnet numbers and masks. EIGRP just asks the question of
+ * whether the neighbor's IP address is in the range of
+ * addresses for the subnet as known to the local router."
+ */
+ TAILQ_FOREACH(if_addr, &iface->addr_list, entry) {
+ mask = prefixlen2mask(if_addr->prefixlen);
+ if ((if_addr->addr.v4.s_addr & mask) ==
+ (src->v4.s_addr & mask))
+ return (iface);
+ }
+ break;
+ case AF_INET6:
+ /*
+ * draft-savage-eigrp-04 - Section 10.1:
+ * "EIGRP IPv6 will check that a received HELLO contains a valid
+ * IPv6 link-local source address."
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&src->v6))
+ return (iface);
+ break;
+ default:
+ break;
+ }
+
+ return (NULL);
+}
diff --git a/usr.sbin/eigrpd/parse.y b/usr.sbin/eigrpd/parse.y
new file mode 100644
index 00000000000..1228399f2e7
--- /dev/null
+++ b/usr.sbin/eigrpd/parse.y
@@ -0,0 +1,1165 @@
+/* $OpenBSD: parse.y,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "eigrp.h"
+#include "eigrpd.h"
+#include "eigrpe.h"
+#include "log.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+void clear_config(struct eigrpd_conf *xconf);
+uint32_t get_rtr_id(void);
+int host(const char *, union eigrpd_addr *, uint8_t *);
+
+static struct eigrpd_conf *conf;
+static int errors = 0;
+
+int af = AF_UNSPEC;
+struct eigrp *eigrp = NULL;
+struct eigrp_iface *ei = NULL;
+
+struct config_defaults {
+ uint8_t kvalues[6];
+ uint8_t maximum_hops;
+ uint8_t maximum_paths;
+ uint8_t variance;
+ struct redist_metric *dflt_metric;
+ uint16_t hello_interval;
+ uint16_t hello_holdtime;
+ uint32_t delay;
+ uint32_t bandwidth;
+ uint8_t splithorizon;
+};
+
+struct config_defaults globaldefs;
+struct config_defaults afdefs;
+struct config_defaults asdefs;
+struct config_defaults ifacedefs;
+struct config_defaults *defs;
+
+struct eigrp *conf_get_instance(uint16_t);
+struct eigrp_iface *conf_get_if(struct kif *);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ struct redistribute *redist;
+ struct redist_metric *redist_metric;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token ROUTERID AS FIBUPDATE RDOMAIN REDISTRIBUTE METRIC DFLTMETRIC
+%token MAXHOPS MAXPATHS VARIANCE FIBPRIORITY_INT FIBPRIORITY_EXT
+%token AF IPV4 IPV6 HELLOINTERVAL HOLDTIME KVALUES
+%token INTERFACE PASSIVE DELAY BANDWIDTH SPLITHORIZON
+%token YES NO
+%token INCLUDE
+%token ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.number> yesno no
+%type <v.string> string
+%type <v.number> eigrp_af
+%type <v.redist> redistribute
+%type <v.redist_metric> redist_metric opt_red_metric
+
+%%
+
+grammar : /* empty */
+ | grammar include '\n'
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar af '\n'
+ | grammar error '\n' { file->errors++; }
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 1)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : YES { $$ = 1; }
+ | NO { $$ = 0; }
+ ;
+
+no : /* empty */ { $$ = 0; }
+ | NO { $$ = 1; }
+ ;
+
+eigrp_af : IPV4 { $$ = AF_INET; }
+ | IPV6 { $$ = AF_INET6; }
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & EIGRPD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+conf_main : ROUTERID STRING {
+ if (!inet_aton($2, &conf->rtr_id)) {
+ yyerror("error parsing router-id");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= EIGRPD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~EIGRPD_FLAG_NO_FIB_UPDATE;
+ }
+ | RDOMAIN NUMBER {
+ if ($2 < 0 || $2 > RT_TABLEID_MAX) {
+ yyerror("invalid rdomain");
+ YYERROR;
+ }
+ conf->rdomain = $2;
+ }
+ | FIBPRIORITY_INT NUMBER {
+ if ($2 <= RTP_NONE || $2 > RTP_MAX) {
+ yyerror("invalid fib-priority");
+ YYERROR;
+ }
+ conf->fib_priority_internal = $2;
+ }
+ | FIBPRIORITY_EXT NUMBER {
+ if ($2 <= RTP_NONE || $2 > RTP_MAX) {
+ yyerror("invalid fib-priority");
+ YYERROR;
+ }
+ conf->fib_priority_external = $2;
+ }
+ | defaults
+ ;
+
+redistribute : no REDISTRIBUTE STRING opt_red_metric {
+ struct redistribute *r;
+
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ fatal(NULL);
+ if (!strcmp($3, "default"))
+ r->type = REDIST_DEFAULT;
+ else if (!strcmp($3, "static"))
+ r->type = REDIST_STATIC;
+ else if (!strcmp($3, "rip"))
+ r->type = REDIST_RIP;
+ else if (!strcmp($3, "ospf"))
+ r->type = REDIST_OSPF;
+ else if (!strcmp($3, "connected"))
+ r->type = REDIST_CONNECTED;
+ else if (host($3, &r->addr, &r->prefixlen) >= 0)
+ r->type = REDIST_ADDR;
+ else {
+ yyerror("invalid redistribute");
+ free($3);
+ free(r);
+ YYERROR;
+ }
+
+ r->af = af;
+ if ($1)
+ r->type |= REDIST_NO;
+ r->metric = $4;
+ free($3);
+ $$ = r;
+ }
+ ;
+
+redist_metric : NUMBER NUMBER NUMBER NUMBER NUMBER {
+ struct redist_metric *m;
+
+ if ($1 < MIN_BANDWIDTH || $1 > MAX_BANDWIDTH) {
+ yyerror("bandwidth out of range (%d-%d)",
+ MIN_BANDWIDTH, MAX_BANDWIDTH);
+ YYERROR;
+ }
+ if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
+ yyerror("delay out of range (%d-%d)",
+ MIN_DELAY, MAX_DELAY);
+ YYERROR;
+ }
+ if ($3 < MIN_RELIABILITY || $3 > MAX_RELIABILITY) {
+ yyerror("reliability out of range (%d-%d)",
+ MIN_RELIABILITY, MAX_RELIABILITY);
+ YYERROR;
+ }
+ if ($4 < MIN_LOAD || $4 > MAX_LOAD) {
+ yyerror("load out of range (%d-%d)",
+ MIN_LOAD, MAX_LOAD);
+ YYERROR;
+ }
+ if ($5 < MIN_MTU || $5 > MAX_MTU) {
+ yyerror("mtu out of range (%d-%d)",
+ MIN_MTU, MAX_MTU);
+ YYERROR;
+ }
+
+ if ((m = calloc(1, sizeof(*m))) == NULL)
+ fatal(NULL);
+ m->bandwidth = $1;
+ m->delay = $2;
+ m->reliability = $3;
+ m->load = $4;
+ m->mtu = $5;
+
+ $$ = m;
+ }
+ ;
+
+opt_red_metric : /* empty */ { $$ = NULL; }
+ | METRIC redist_metric { $$ = $2; }
+ ;
+
+defaults : KVALUES NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER {
+ if ($2 < MIN_KVALUE || $2 > MAX_KVALUE ||
+ $3 < MIN_KVALUE || $3 > MAX_KVALUE ||
+ $4 < MIN_KVALUE || $4 > MAX_KVALUE ||
+ $5 < MIN_KVALUE || $5 > MAX_KVALUE ||
+ $6 < MIN_KVALUE || $6 > MAX_KVALUE ||
+ $7 < MIN_KVALUE || $7 > MAX_KVALUE) {
+ yyerror("k-value out of range (%d-%d)",
+ MIN_KVALUE, MAX_KVALUE);
+ YYERROR;
+ }
+ defs->kvalues[0] = $2;
+ defs->kvalues[1] = $3;
+ defs->kvalues[2] = $4;
+ defs->kvalues[3] = $5;
+ defs->kvalues[4] = $6;
+ defs->kvalues[5] = $7;
+ }
+ | MAXHOPS NUMBER {
+ if ($2 < MIN_MAXIMUM_HOPS ||
+ $2 > MAX_MAXIMUM_HOPS) {
+ yyerror("maximum-hops out of range (%d-%d)",
+ MIN_MAXIMUM_HOPS, MAX_MAXIMUM_HOPS);
+ YYERROR;
+ }
+ defs->maximum_hops = $2;
+ }
+ | MAXPATHS NUMBER {
+ if ($2 < MIN_MAXIMUM_PATHS ||
+ $2 > MAX_MAXIMUM_PATHS) {
+ yyerror("maximum-paths out of range (%d-%d)",
+ MIN_MAXIMUM_PATHS, MAX_MAXIMUM_PATHS);
+ YYERROR;
+ }
+ defs->maximum_paths = $2;
+ }
+ | VARIANCE NUMBER {
+ if ($2 < MIN_VARIANCE ||
+ $2 > MAX_VARIANCE) {
+ yyerror("variance out of range (%d-%d)",
+ MIN_VARIANCE, MAX_VARIANCE);
+ YYERROR;
+ }
+ defs->variance = $2;
+ }
+ | DFLTMETRIC redist_metric {
+ defs->dflt_metric = $2;
+ }
+ | iface_defaults
+ ;
+
+iface_defaults : HELLOINTERVAL NUMBER {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ defs->hello_interval = $2;
+ }
+ | HOLDTIME NUMBER {
+ if ($2 < MIN_HELLO_HOLDTIME ||
+ $2 > MAX_HELLO_HOLDTIME) {
+ yyerror("hold-timel out of range (%d-%d)",
+ MIN_HELLO_HOLDTIME,
+ MAX_HELLO_HOLDTIME);
+ YYERROR;
+ }
+ defs->hello_holdtime = $2;
+ }
+ | DELAY NUMBER {
+ if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
+ yyerror("delay out of range (%d-%d)",
+ MIN_DELAY, MAX_DELAY);
+ YYERROR;
+ }
+ defs->delay = $2;
+ }
+ | BANDWIDTH NUMBER {
+ if ($2 < MIN_BANDWIDTH || $2 > MAX_BANDWIDTH) {
+ yyerror("bandwidth out of range (%d-%d)",
+ MIN_BANDWIDTH, MAX_BANDWIDTH);
+ YYERROR;
+ }
+ defs->bandwidth = $2;
+ }
+ | SPLITHORIZON yesno {
+ defs->splithorizon = $2;
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+af : AF eigrp_af {
+ af = $2;
+ memcpy(&afdefs, defs, sizeof(afdefs));
+ defs = &afdefs;
+ } af_block {
+ af = AF_UNSPEC;
+ defs = &globaldefs;
+ }
+ ;
+
+af_block : '{' optnl afopts_l '}'
+ | '{' optnl '}'
+ |
+ ;
+
+afopts_l : afopts_l afoptsl nl
+ | afoptsl optnl
+ ;
+
+afoptsl : as
+ | defaults
+ ;
+
+as : AS NUMBER {
+ if ($2 < EIGRP_MIN_AS || $2 > EIGRP_MAX_AS) {
+ yyerror("invalid autonomous-system");
+ YYERROR;
+ }
+ eigrp = conf_get_instance($2);
+ if (eigrp == NULL)
+ YYERROR;
+ TAILQ_INSERT_TAIL(&conf->instances, eigrp, entry);
+
+ memcpy(&asdefs, defs, sizeof(asdefs));
+ defs = &asdefs;
+ } as_block {
+ memcpy(eigrp->kvalues, defs->kvalues,
+ sizeof(eigrp->kvalues));
+ eigrp->maximum_hops = defs->maximum_hops;
+ eigrp->maximum_paths = defs->maximum_paths;
+ eigrp->variance = defs->variance;
+ eigrp->dflt_metric = defs->dflt_metric;
+ eigrp = NULL;
+ defs = &afdefs;
+ }
+ ;
+
+as_block : '{' optnl asopts_l '}'
+ | '{' optnl '}'
+ |
+ ;
+
+asopts_l : asopts_l asoptsl nl
+ | asoptsl optnl
+ ;
+
+asoptsl : interface
+ | redistribute {
+ SIMPLEQ_INSERT_TAIL(&eigrp->redist_list, $1, entry);
+ }
+ | defaults
+ ;
+
+interface : INTERFACE STRING {
+ struct kif *kif;
+
+ if ((kif = kif_findname($2)) == NULL) {
+ yyerror("unknown interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ ei = conf_get_if(kif);
+ if (ei == NULL)
+ YYERROR;
+
+ memcpy(&ifacedefs, defs, sizeof(ifacedefs));
+ defs = &ifacedefs;
+ } interface_block {
+ ei->hello_holdtime = defs->hello_holdtime;
+ ei->hello_interval = defs->hello_interval;
+ ei->delay = defs->delay;
+ ei->bandwidth = defs->bandwidth;
+ ei->splithorizon = defs->splithorizon;
+ ei = NULL;
+ defs = &asdefs;
+ }
+ ;
+
+interface_block : '{' optnl interfaceopts_l '}'
+ | '{' optnl '}'
+ |
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl nl
+ | interfaceoptsl optnl
+ ;
+
+interfaceoptsl : PASSIVE { ei->passive = 1; }
+ | iface_defaults
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) == -1)
+ fatalx("yyerror vasprintf");
+ va_end(ap);
+ logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
+ free(msg);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ {"address-family", AF},
+ {"autonomous-system", AS},
+ {"bandwidth", BANDWIDTH},
+ {"default-metric", DFLTMETRIC},
+ {"delay", DELAY},
+ {"fib-priority-external", FIBPRIORITY_EXT},
+ {"fib-priority-internal", FIBPRIORITY_INT},
+ {"fib-update", FIBUPDATE},
+ {"hello-interval", HELLOINTERVAL},
+ {"holdtime", HOLDTIME},
+ {"include", INCLUDE},
+ {"interface", INTERFACE},
+ {"ipv4", IPV4},
+ {"ipv6", IPV6},
+ {"k-values", KVALUES},
+ {"maximum-hops", MAXHOPS},
+ {"maximum-paths", MAXPATHS},
+ {"metric", METRIC},
+ {"no", NO},
+ {"passive", PASSIVE},
+ {"rdomain", RDOMAIN},
+ {"redistribute", REDISTRIBUTE},
+ {"router-id", ROUTERID},
+ {"split-horizon", SPLITHORIZON},
+ {"variance", VARIANCE},
+ {"yes", YES}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+unsigned char *parsebuf;
+int parseindex;
+unsigned char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ unsigned char buf[8096];
+ unsigned char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return (findeol());
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ log_warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ log_warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
+ log_warnx("%s: group writable or world read/writable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ log_warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ free(nfile);
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+struct eigrpd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(struct eigrpd_conf))) == NULL)
+ fatal("parse_config");
+ conf->opts = opts;
+ conf->rdomain = 0;
+ conf->fib_priority_internal = RTP_EIGRP;
+ conf->fib_priority_external = RTP_EIGRP;
+
+ memset(&globaldefs, 0, sizeof(globaldefs));
+ defs = &globaldefs;
+ defs->kvalues[0] = defs->kvalues[2] = 1;
+ defs->maximum_hops = DEFAULT_MAXIMUM_HOPS;
+ defs->maximum_paths = DEFAULT_MAXIMUM_PATHS;
+ defs->variance = DEFAULT_VARIANCE;
+ defs->hello_holdtime = DEFAULT_HELLO_HOLDTIME;
+ defs->hello_interval = DEFAULT_HELLO_INTERVAL;
+ defs->delay = DEFAULT_DELAY;
+ defs->bandwidth = DEFAULT_BANDWIDTH;
+ defs->splithorizon = 1;
+
+ if ((file = pushfile(filename, !(conf->opts & EIGRPD_OPT_NOACTION))) == NULL) {
+ free(conf);
+ return (NULL);
+ }
+ topfile = file;
+
+ TAILQ_INIT(&conf->iface_list);
+ TAILQ_INIT(&conf->instances);
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entry);
+ if ((conf->opts & EIGRPD_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ clear_config(conf);
+ return (NULL);
+ }
+
+ if (conf->rtr_id.s_addr == 0)
+ conf->rtr_id.s_addr = get_rtr_id();
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+struct eigrp *
+conf_get_instance(uint16_t as)
+{
+ struct eigrp *e;
+
+ if (eigrp_find(conf, af, as)) {
+ yyerror("autonomous-system %u already configured"
+ "for address-family %s", as, af_name(af));
+ return (NULL);
+ }
+
+ e = calloc(1, sizeof(struct eigrp));
+ e->af = af;
+ e->as = as;
+ SIMPLEQ_INIT(&e->redist_list);
+ TAILQ_INIT(&e->ei_list);
+ RB_INIT(&e->nbrs);
+ RB_INIT(&e->topology);
+
+ /* start local sequence number used by RTP */
+ e->seq_num = 1;
+
+ return (e);
+}
+
+struct eigrp_iface *
+conf_get_if(struct kif *kif)
+{
+ struct eigrp_iface *e;
+
+ TAILQ_FOREACH(e, &eigrp->ei_list, e_entry)
+ if (e->iface->ifindex == kif->ifindex) {
+ yyerror("interface %s already configured "
+ "for address-family %s and "
+ "autonomous-system %u", kif->ifname,
+ af_name(af), eigrp->as);
+ return (NULL);
+ }
+
+ e = eigrp_if_new(conf, eigrp, kif);
+
+ return (e);
+}
+
+void
+clear_config(struct eigrpd_conf *xconf)
+{
+ free(xconf);
+}
+
+uint32_t
+get_rtr_id(void)
+{
+ struct ifaddrs *ifap, *ifa;
+ uint32_t ip = 0, cur, localnet;
+
+ localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
+
+ if (getifaddrs(&ifap) == -1)
+ fatal("getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strncmp(ifa->ifa_name, "carp", 4) == 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+ if ((cur & localnet) == localnet) /* skip 127/8 */
+ continue;
+ if (ntohl(cur) < ntohl(ip) || ip == 0)
+ ip = cur;
+ }
+ freeifaddrs(ifap);
+
+ if (ip == 0)
+ fatal("router-id is 0.0.0.0");
+
+ return (ip);
+}
+
+int
+host(const char *s, union eigrpd_addr *addr, uint8_t *plen)
+{
+ char *p, *ps;
+ const char *errstr;
+ int maxplen;
+
+ switch (af) {
+ case AF_INET:
+ maxplen = 32;
+ break;
+ case AF_INET6:
+ maxplen = 128;
+ break;
+ default:
+ return (-1);
+ }
+
+ if ((p = strrchr(s, '/')) != NULL) {
+ *plen = strtonum(p + 1, 0, maxplen, &errstr);
+ if (errstr) {
+ log_warnx("prefixlen is %s: %s", errstr, p + 1);
+ return (-1);
+ }
+ if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
+ fatal("host: malloc");
+ strlcpy(ps, s, strlen(s) - strlen(p) + 1);
+ } else {
+ if ((ps = strdup(s)) == NULL)
+ fatal("host: strdup");
+ *plen = maxplen;
+ }
+
+ memset(addr, 0, sizeof(union eigrpd_addr));
+ switch (af) {
+ case AF_INET:
+ if (inet_pton(AF_INET, ps, &addr->v4) != 1)
+ return (-1);
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, ps, &addr->v6) != 1)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ eigrp_applymask(af, addr, addr, *plen);
+ free(ps);
+
+ return (0);
+}
diff --git a/usr.sbin/eigrpd/printconf.c b/usr.sbin/eigrpd/printconf.c
new file mode 100644
index 00000000000..acdbeba07f6
--- /dev/null
+++ b/usr.sbin/eigrpd/printconf.c
@@ -0,0 +1,162 @@
+/* $OpenBSD: printconf.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "eigrp.h"
+#include "eigrpd.h"
+#include "eigrpe.h"
+#include "log.h"
+
+void print_mainconf(struct eigrpd_conf *);
+const char *print_no(uint16_t);
+void print_redist_metric(struct redist_metric *);
+void print_redistribute(struct eigrp *);
+void print_iface(struct eigrp_iface *);
+void print_as(struct eigrp *);
+void print_af(struct eigrpd_conf *, int);
+
+void
+print_mainconf(struct eigrpd_conf *conf)
+{
+ printf("router-id %s\n", inet_ntoa(conf->rtr_id));
+
+ if (conf->flags & EIGRPD_FLAG_NO_FIB_UPDATE)
+ printf("fib-update no\n");
+ else
+ printf("fib-update yes\n");
+
+ printf("rdomain %u\n", conf->rdomain);
+ printf("fib-priority-internal %u\n", conf->fib_priority_internal);
+ printf("fib-priority-external %u\n", conf->fib_priority_external);
+}
+
+const char *
+print_no(uint16_t type)
+{
+ if (type & REDIST_NO)
+ return ("no ");
+ else
+ return ("");
+}
+
+void
+print_redist_metric(struct redist_metric *metric)
+{
+ printf(" %u %u %u %u %u", metric->bandwidth, metric->delay,
+ metric->reliability, metric->load, metric->mtu);
+}
+
+void
+print_redistribute(struct eigrp *eigrp)
+{
+ struct redistribute *r;
+
+ if (eigrp->dflt_metric) {
+ printf("\t\tdefault-metric");
+ print_redist_metric(eigrp->dflt_metric);
+ printf("\n");
+ }
+
+ SIMPLEQ_FOREACH(r, &eigrp->redist_list, entry) {
+ switch (r->type & ~REDIST_NO) {
+ case REDIST_STATIC:
+ printf("\t\t%sredistribute static", print_no(r->type));
+ break;
+ case REDIST_RIP:
+ printf("\t\t%sredistribute rip", print_no(r->type));
+ break;
+ case REDIST_OSPF:
+ printf("\t\t%sredistribute ospf", print_no(r->type));
+ break;
+ case REDIST_CONNECTED:
+ printf("\t\t%sredistribute connected",
+ print_no(r->type));
+ break;
+ case REDIST_DEFAULT:
+ printf("\t\t%sredistribute default", print_no(r->type));
+ break;
+ case REDIST_ADDR:
+ printf("\t\t%sredistribute %s/%u",
+ print_no(r->type), log_addr(r->af, &r->addr),
+ r->prefixlen);
+ break;
+ }
+
+ if (r->metric)
+ print_redist_metric(r->metric);
+ printf("\n");
+ }
+}
+
+void
+print_iface(struct eigrp_iface *ei)
+{
+ printf("\t\tinterface %s {\n", ei->iface->name);
+ printf("\t\t\thello-interval %u\n", ei->hello_interval);
+ printf("\t\t\tholdtime %u\n", ei->hello_holdtime);
+ printf("\t\t\tdelay %u\n", ei->delay);
+ printf("\t\t\tbandwidth %u\n", ei->bandwidth);
+ printf("\t\t\tsplit-horizon %s\n", (ei->splithorizon) ? "yes" : "no");
+ if (ei->passive)
+ printf("\t\t\tpassive\n");
+ printf("\t\t}\n");
+}
+
+void
+print_as(struct eigrp *eigrp)
+{
+ struct eigrp_iface *ei;
+
+ printf("\tautonomous-system %u {\n", eigrp->as);
+ printf("\t\tk-values %u %u %u %u %u %u\n", eigrp->kvalues[0],
+ eigrp->kvalues[1], eigrp->kvalues[2], eigrp->kvalues[3],
+ eigrp->kvalues[4], eigrp->kvalues[5]);
+ printf("\t\tmaximum-hops %u\n", eigrp->maximum_hops);
+ printf("\t\tmaximum-paths %u\n", eigrp->maximum_paths);
+ printf("\t\tvariance %u\n", eigrp->variance);
+ print_redistribute(eigrp);
+ printf("\n");
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry)
+ print_iface(ei);
+ printf("\t}\n");
+}
+
+void
+print_af(struct eigrpd_conf *conf, int af)
+{
+ struct eigrp *eigrp;
+
+ printf("address-family %s {\n", af_name(af));
+ TAILQ_FOREACH(eigrp, &conf->instances, entry)
+ if (eigrp->af == af)
+ print_as(eigrp);
+ printf("}\n\n");
+}
+
+void
+print_config(struct eigrpd_conf *conf)
+{
+ printf("\n");
+ print_mainconf(conf);
+ printf("\n");
+
+ print_af(conf, AF_INET);
+ print_af(conf, AF_INET6);
+}
diff --git a/usr.sbin/eigrpd/query.c b/usr.sbin/eigrpd/query.c
new file mode 100644
index 00000000000..c6b06c65c97
--- /dev/null
+++ b/usr.sbin/eigrpd/query.c
@@ -0,0 +1,114 @@
+/* $OpenBSD: query.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+extern struct eigrpd_conf *econf;
+
+/* query packet handling */
+
+void
+send_query(struct eigrp_iface *ei, struct nbr *nbr,
+ struct rinfo_head *rinfo_list, int siaquery)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct ibuf *buf;
+ uint16_t opcode;
+ struct rinfo_entry *re;
+ int size;
+ int route_len;
+
+ if (rinfo_list == NULL || TAILQ_EMPTY(rinfo_list))
+ return;
+
+ /* don't exceed the interface's mtu */
+ do {
+ if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
+ IP_MAXPACKET - sizeof(struct ip))) == NULL)
+ fatal("send_query");
+
+ if (!siaquery)
+ opcode = EIGRP_OPC_QUERY;
+ else
+ opcode = EIGRP_OPC_SIAQUERY;
+
+ /* EIGRP header */
+ if (gen_eigrp_hdr(buf, opcode, 0, eigrp->seq_num, eigrp->as))
+ goto fail;
+
+ switch (eigrp->af) {
+ case AF_INET:
+ size = sizeof(struct ip);
+ break;
+ case AF_INET6:
+ size = sizeof(struct ip6_hdr);
+ break;
+ default:
+ break;
+ }
+ size += sizeof(struct eigrp_hdr);
+
+ while ((re = TAILQ_FIRST(rinfo_list)) != NULL) {
+ route_len = len_route_tlv(&re->rinfo);
+ if (size + route_len > ei->iface->mtu) {
+ rtp_send(ei, nbr, buf);
+ break;
+ }
+ size += route_len;
+
+ if (gen_route_tlv(buf, &re->rinfo))
+ goto fail;
+ TAILQ_REMOVE(rinfo_list, re, entry);
+ free(re);
+ }
+ } while (!TAILQ_EMPTY(rinfo_list));
+
+ rtp_send(ei, nbr, buf);
+ return;
+fail:
+ log_warnx("%s: failed to send message", __func__);
+ if (rinfo_list)
+ message_list_clr(rinfo_list);
+ ibuf_free(buf);
+ return;
+}
+
+void
+recv_query(struct nbr *nbr, struct rinfo_head *rinfo_list, int siaquery)
+{
+ int type;
+ struct rinfo_entry *re;
+
+ rtp_ack_start_timer(nbr);
+
+ if (!siaquery)
+ type = IMSG_RECV_QUERY;
+ else
+ type = IMSG_RECV_SIAQUERY;
+
+ TAILQ_FOREACH(re, rinfo_list, entry)
+ eigrpe_imsg_compose_rde(type, nbr->peerid, 0, &re->rinfo,
+ sizeof(re->rinfo));
+}
diff --git a/usr.sbin/eigrpd/rde.c b/usr.sbin/eigrpd/rde.c
new file mode 100644
index 00000000000..3b543648f35
--- /dev/null
+++ b/usr.sbin/eigrpd/rde.c
@@ -0,0 +1,764 @@
+/* $OpenBSD: rde.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <pwd.h>
+
+#include "eigrp.h"
+#include "eigrpd.h"
+#include "eigrpe.h"
+#include "log.h"
+#include "rde.h"
+
+void rde_sig_handler(int sig, short, void *);
+void rde_shutdown(void);
+void rde_dispatch_imsg(int, short, void *);
+void rde_dispatch_parent(int, short, void *);
+
+struct eigrpd_conf *rdeconf = NULL, *nconf;
+struct imsgev *iev_eigrpe;
+struct imsgev *iev_main;
+
+extern struct iface_id_head ifaces_by_id;
+RB_PROTOTYPE(iface_id_head, eigrp_iface, id_tree, iface_id_compare)
+
+RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
+
+extern struct rde_nbr_head rde_nbrs;
+RB_PROTOTYPE(rde_nbr_head, rde_nbr, entry, rde_nbr_compare)
+
+/* ARGSUSED */
+void
+rde_sig_handler(int sig, short event, void *arg)
+{
+ /*
+ * signal handler rules don't apply, libevent decouples for us
+ */
+
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ rde_shutdown();
+ /* NOTREACHED */
+ default:
+ fatalx("unexpected signal");
+ }
+}
+
+/* route decision engine */
+pid_t
+rde(struct eigrpd_conf *xconf, int pipe_parent2rde[2], int pipe_eigrpe2rde[2],
+ int pipe_parent2eigrpe[2])
+{
+ struct event ev_sigint, ev_sigterm;
+ struct timeval now;
+ struct passwd *pw;
+ pid_t pid;
+ struct eigrp *eigrp;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ /* NOTREACHED */
+ case 0:
+ break;
+ default:
+ return (pid);
+ }
+
+ rdeconf = xconf;
+
+ if ((pw = getpwnam(EIGRPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("route decision engine");
+ eigrpd_process = PROC_RDE_ENGINE;
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ event_init();
+
+ /* setup signal handler */
+ signal_set(&ev_sigint, SIGINT, rde_sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, rde_sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ /* setup pipes */
+ close(pipe_eigrpe2rde[0]);
+ close(pipe_parent2rde[0]);
+ close(pipe_parent2eigrpe[0]);
+ close(pipe_parent2eigrpe[1]);
+
+ if ((iev_eigrpe = malloc(sizeof(struct imsgev))) == NULL ||
+ (iev_main = malloc(sizeof(struct imsgev))) == NULL)
+ fatal(NULL);
+ imsg_init(&iev_eigrpe->ibuf, pipe_eigrpe2rde[1]);
+ iev_eigrpe->handler = rde_dispatch_imsg;
+ imsg_init(&iev_main->ibuf, pipe_parent2rde[1]);
+ iev_main->handler = rde_dispatch_parent;
+
+ /* setup event handler */
+ iev_eigrpe->events = EV_READ;
+ event_set(&iev_eigrpe->ev, iev_eigrpe->ibuf.fd, iev_eigrpe->events,
+ iev_eigrpe->handler, iev_eigrpe);
+ event_add(&iev_eigrpe->ev, NULL);
+
+ iev_main->events = EV_READ;
+ event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
+ iev_main->handler, iev_main);
+ event_add(&iev_main->ev, NULL);
+
+ gettimeofday(&now, NULL);
+ rdeconf->uptime = now.tv_sec;
+
+ TAILQ_FOREACH(eigrp, &rdeconf->instances, entry)
+ rde_instance_init(eigrp);
+
+ event_dispatch();
+
+ rde_shutdown();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+void
+rde_shutdown(void)
+{
+ config_clear(rdeconf);
+
+ msgbuf_clear(&iev_eigrpe->ibuf.w);
+ free(iev_eigrpe);
+ msgbuf_clear(&iev_main->ibuf.w);
+ free(iev_main);
+
+ log_info("route decision engine exiting");
+ _exit(0);
+}
+
+int
+rde_imsg_compose_parent(int type, pid_t pid, void *data, uint16_t datalen)
+{
+ return (imsg_compose_event(iev_main, type, 0, pid, -1,
+ data, datalen));
+}
+
+int
+rde_imsg_compose_eigrpe(int type, uint32_t peerid, pid_t pid, void *data,
+ uint16_t datalen)
+{
+ return (imsg_compose_event(iev_eigrpe, type, peerid, pid, -1,
+ data, datalen));
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_imsg(int fd, short event, void *bula)
+{
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ struct rde_nbr *nbr;
+ struct rde_nbr new;
+ struct rinfo rinfo;
+ ssize_t n;
+ int shut = 0, verbose;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("rde_dispatch_imsg: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_NEIGHBOR_UP:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct rde_nbr))
+ fatalx("invalid size of neighbor request");
+ memcpy(&new, imsg.data, sizeof(new));
+
+ if (rde_nbr_find(imsg.hdr.peerid))
+ fatalx("rde_dispatch_imsg: "
+ "neighbor already exists");
+ rde_nbr_new(imsg.hdr.peerid, &new);
+ break;
+ case IMSG_NEIGHBOR_DOWN:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find rde neighbor",
+ __func__);
+ break;
+ }
+
+ rde_check_link_down_nbr(nbr);
+ rde_flush_queries();
+ rde_nbr_del(rde_nbr_find(imsg.hdr.peerid));
+ break;
+ case IMSG_RECV_UPDATE_INIT:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find rde neighbor",
+ __func__);
+ break;
+ }
+
+ rt_snap(nbr);
+ break;
+ case IMSG_RECV_UPDATE:
+ case IMSG_RECV_QUERY:
+ case IMSG_RECV_REPLY:
+ case IMSG_RECV_SIAQUERY:
+ case IMSG_RECV_SIAREPLY:
+ nbr = rde_nbr_find(imsg.hdr.peerid);
+ if (nbr == NULL) {
+ log_debug("%s: cannot find rde neighbor",
+ __func__);
+ break;
+ }
+
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rinfo))
+ fatalx("invalid size of rinfo");
+ memcpy(&rinfo, imsg.data, sizeof(rinfo));
+
+ switch (imsg.hdr.type) {
+ case IMSG_RECV_UPDATE:
+ rde_check_update(nbr, &rinfo);
+ break;
+ case IMSG_RECV_QUERY:
+ rde_check_query(nbr, &rinfo, 0);
+ break;
+ case IMSG_RECV_REPLY:
+ rde_check_reply(nbr, &rinfo, 0);
+ break;
+ case IMSG_RECV_SIAQUERY:
+ rde_check_query(nbr, &rinfo, 1);
+ break;
+ case IMSG_RECV_SIAREPLY:
+ rde_check_reply(nbr, &rinfo, 1);
+ break;
+ }
+ break;
+ case IMSG_CTL_SHOW_TOPOLOGY:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ctl_show_topology_req)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+
+ rt_dump(imsg.data, imsg.hdr.pid);
+ rde_imsg_compose_eigrpe(IMSG_CTL_END, 0, imsg.hdr.pid,
+ NULL, 0);
+ break;
+ case IMSG_CTL_LOG_VERBOSE:
+ /* already checked by eigrpe */
+ memcpy(&verbose, imsg.data, sizeof(verbose));
+ log_verbose(verbose);
+ break;
+ default:
+ log_debug("rde_dispatch_imsg: unexpected imsg %d",
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+rde_dispatch_parent(int fd, short event, void *bula)
+{
+ struct iface *niface;
+ static struct eigrp *neigrp;
+ struct eigrp_iface *nei;
+ struct imsg imsg;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf;
+ struct kif *kif;
+ ssize_t n;
+ int shut = 0;
+
+ ibuf = &iev->ibuf;
+
+ if (event & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read error");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+ if (event & EV_WRITE) {
+ if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
+ fatal("msgbuf_write");
+ if (n == 0) /* connection closed */
+ shut = 1;
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("rde_dispatch_parent: imsg_read error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_IFDOWN:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kif))
+ fatalx("IFDOWN imsg with wrong len");
+ kif = imsg.data;
+ rde_check_link_down(kif->ifindex);
+ break;
+ case IMSG_NETWORK_ADD:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kroute))
+ fatalx("IMSG_NETWORK_ADD imsg with wrong len");
+ rt_redist_set(imsg.data, 0);
+ break;
+ case IMSG_NETWORK_DEL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct kroute))
+ fatalx("IMSG_NETWORK_DEL imsg with wrong len");
+ rt_redist_set(imsg.data, 1);
+ break;
+ case IMSG_RECONF_CONF:
+ if ((nconf = malloc(sizeof(struct eigrpd_conf))) ==
+ NULL)
+ fatal(NULL);
+ memcpy(nconf, imsg.data, sizeof(struct eigrpd_conf));
+
+ TAILQ_INIT(&nconf->iface_list);
+ TAILQ_INIT(&nconf->instances);
+ break;
+ case IMSG_RECONF_INSTANCE:
+ if ((neigrp = malloc(sizeof(struct eigrp))) == NULL)
+ fatal(NULL);
+ memcpy(neigrp, imsg.data, sizeof(struct eigrp));
+
+ SIMPLEQ_INIT(&neigrp->redist_list);
+ TAILQ_INIT(&neigrp->ei_list);
+ RB_INIT(&neigrp->nbrs);
+ RB_INIT(&neigrp->topology);
+ TAILQ_INSERT_TAIL(&nconf->instances, neigrp, entry);
+ break;
+ case IMSG_RECONF_IFACE:
+ niface = imsg.data;
+ niface = if_lookup(nconf, niface->ifindex);
+ if (niface)
+ break;
+
+ if ((niface = malloc(sizeof(struct iface))) == NULL)
+ fatal(NULL);
+ memcpy(niface, imsg.data, sizeof(struct iface));
+
+ TAILQ_INIT(&niface->addr_list);
+ TAILQ_INSERT_TAIL(&nconf->iface_list, niface, entry);
+ break;
+ case IMSG_RECONF_EIGRP_IFACE:
+ if ((nei = malloc(sizeof(struct eigrp_iface))) == NULL)
+ fatal(NULL);
+ memcpy(nei, imsg.data, sizeof(struct eigrp_iface));
+
+ nei->iface = niface;
+ nei->eigrp = neigrp;
+ TAILQ_INIT(&nei->nbr_list);
+ TAILQ_INIT(&nei->update_list);
+ TAILQ_INIT(&nei->query_list);
+ TAILQ_INSERT_TAIL(&niface->ei_list, nei, i_entry);
+ TAILQ_INSERT_TAIL(&neigrp->ei_list, nei, e_entry);
+ if (RB_INSERT(iface_id_head, &ifaces_by_id, nei) !=
+ NULL)
+ fatalx("rde_dispatch_parent: "
+ "RB_INSERT(ifaces_by_id) failed");
+ break;
+ case IMSG_RECONF_END:
+ merge_config(rdeconf, nconf);
+ nconf = NULL;
+ break;
+ default:
+ log_debug("%s: unexpected imsg %d", __func__,
+ imsg.hdr.type);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+ if (!shut)
+ imsg_event_add(iev);
+ else {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&iev->ev);
+ event_loopexit(NULL);
+ }
+}
+
+void
+rde_instance_init(struct eigrp *eigrp)
+{
+ struct rde_nbr nbr;
+
+ memset(&nbr, 0, sizeof(nbr));
+ nbr.flags = F_RDE_NBR_SELF | F_RDE_NBR_REDIST;
+ eigrp->rnbr_redist = rde_nbr_new(NBR_IDSELF, &nbr);
+ eigrp->rnbr_redist->eigrp = eigrp;
+ nbr.flags = F_RDE_NBR_SELF | F_RDE_NBR_SUMMARY;
+ eigrp->rnbr_summary = rde_nbr_new(NBR_IDSELF, &nbr);
+ eigrp->rnbr_summary->eigrp = eigrp;
+}
+
+void
+rde_instance_del(struct eigrp *eigrp)
+{
+ struct rde_nbr *nbr, *safe;
+ struct rt_node *rn;
+
+ /* clear topology */
+ while((rn = RB_MIN(rt_tree, &eigrp->topology)) != NULL)
+ rt_del(rn);
+
+ /* clear nbrs */
+ RB_FOREACH_SAFE(nbr, rde_nbr_head, &rde_nbrs, safe)
+ if (nbr->eigrp == eigrp)
+ rde_nbr_del(nbr);
+ rde_nbr_del(eigrp->rnbr_redist);
+ rde_nbr_del(eigrp->rnbr_summary);
+
+ free(eigrp);
+}
+
+void
+rde_send_change_kroute(struct rt_node *rn, struct eigrp_route *route)
+{
+ struct eigrp *eigrp = route->nbr->eigrp;
+ struct kroute kr;
+
+ log_debug("%s: %s nbr %s", __func__, log_prefix(rn),
+ log_addr(eigrp->af, &route->nbr->addr));
+
+ memset(&kr, 0, sizeof(kr));
+ kr.af = eigrp->af;
+ memcpy(&kr.prefix, &rn->prefix, sizeof(kr.prefix));
+ kr.prefixlen = rn->prefixlen;
+ if (eigrp_addrisset(eigrp->af, &route->nexthop))
+ memcpy(&kr.nexthop, &route->nexthop, sizeof(kr.nexthop));
+ else
+ memcpy(&kr.nexthop, &route->nbr->addr, sizeof(kr.nexthop));
+ kr.ifindex = route->nbr->ei->iface->ifindex;
+ if (route->type == EIGRP_ROUTE_EXTERNAL)
+ kr.priority = rdeconf->fib_priority_external;
+ else
+ kr.priority = rdeconf->fib_priority_internal;
+
+ rde_imsg_compose_parent(IMSG_KROUTE_CHANGE, 0, &kr, sizeof(kr));
+
+ route->flags |= F_EIGRP_ROUTE_INSTALLED;
+}
+
+void
+rde_send_delete_kroute(struct rt_node *rn, struct eigrp_route *route)
+{
+ struct eigrp *eigrp = route->nbr->eigrp;
+ struct kroute kr;
+
+ log_debug("%s: %s nbr %s", __func__, log_prefix(rn),
+ log_addr(eigrp->af, &route->nbr->addr));
+
+ memset(&kr, 0, sizeof(kr));
+ kr.af = eigrp->af;
+ memcpy(&kr.prefix, &rn->prefix, sizeof(kr.prefix));
+ kr.prefixlen = rn->prefixlen;
+ if (eigrp_addrisset(eigrp->af, &route->nexthop))
+ memcpy(&kr.nexthop, &route->nexthop, sizeof(kr.nexthop));
+ else
+ memcpy(&kr.nexthop, &route->nbr->addr, sizeof(kr.nexthop));
+ kr.ifindex = route->nbr->ei->iface->ifindex;
+ if (route->type == EIGRP_ROUTE_EXTERNAL)
+ kr.priority = rdeconf->fib_priority_external;
+ else
+ kr.priority = rdeconf->fib_priority_internal;
+
+ rde_imsg_compose_parent(IMSG_KROUTE_DELETE, 0, &kr, sizeof(kr));
+
+ route->flags &= ~F_EIGRP_ROUTE_INSTALLED;
+}
+
+static struct redistribute *
+eigrp_redistribute(struct eigrp *eigrp, struct kroute *kr)
+{
+ struct redistribute *r;
+ uint8_t is_default = 0;
+ union eigrpd_addr addr;
+
+ /* only allow the default route via REDIST_DEFAULT */
+ if (!eigrp_addrisset(kr->af, &kr->prefix) && kr->prefixlen == 0)
+ is_default = 1;
+
+ SIMPLEQ_FOREACH(r, &eigrp->redist_list, entry) {
+ switch (r->type & ~REDIST_NO) {
+ case REDIST_STATIC:
+ if (is_default)
+ continue;
+ if (kr->flags & F_STATIC)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ case REDIST_RIP:
+ if (is_default)
+ continue;
+ if (kr->priority == RTP_RIP)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ case REDIST_OSPF:
+ if (is_default)
+ continue;
+ if (kr->priority == RTP_OSPF)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ case REDIST_CONNECTED:
+ if (is_default)
+ continue;
+ if (kr->flags & F_CONNECTED)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ case REDIST_ADDR:
+ if (eigrp_addrisset(r->af, &r->addr) &&
+ r->prefixlen == 0) {
+ if (is_default)
+ return (r->type & REDIST_NO ? NULL : r);
+ else
+ return (0);
+ }
+
+ eigrp_applymask(kr->af, &addr, &kr->prefix,
+ r->prefixlen);
+ if (eigrp_addrcmp(kr->af, &addr, &r->addr) == 0 &&
+ kr->prefixlen >= r->prefixlen)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ case REDIST_DEFAULT:
+ if (is_default)
+ return (r->type & REDIST_NO ? NULL : r);
+ break;
+ }
+ }
+
+ return (NULL);
+}
+
+void
+rt_redist_set(struct kroute *kr, int withdraw)
+{
+ struct eigrp *eigrp;
+ struct redistribute *r;
+ struct redist_metric *rmetric;
+ struct rinfo ri;
+
+ TAILQ_FOREACH(eigrp, &rdeconf->instances, entry) {
+ if (eigrp->af != kr->af)
+ continue;
+
+ r = eigrp_redistribute(eigrp, kr);
+ if (r == NULL)
+ continue;
+
+ if (r->metric)
+ rmetric = r->metric;
+ else if (eigrp->dflt_metric)
+ rmetric = eigrp->dflt_metric;
+ else
+ continue;
+
+ memset(&ri, 0, sizeof(ri));
+ ri.af = kr->af;
+ ri.type = EIGRP_ROUTE_EXTERNAL;
+ memcpy(&ri.prefix, &kr->prefix, sizeof(ri.prefix));
+ ri.prefixlen = kr->prefixlen;
+
+ /* metric */
+ if (withdraw)
+ ri.metric.delay = EIGRP_INFINITE_METRIC;
+ else
+ ri.metric.delay = eigrp_composite_delay(rmetric->delay);
+ ri.metric.bandwidth =
+ eigrp_composite_bandwidth(rmetric->bandwidth);
+ metric_encode_mtu(ri.metric.mtu, rmetric->mtu);
+ ri.metric.hop_count = 0;
+ ri.metric.reliability = rmetric->reliability;
+ ri.metric.load = rmetric->load;
+ ri.metric.tag = 0;
+ ri.metric.flags = 0;
+
+ /* external metric */
+ ri.emetric.routerid = htonl(eigrp_router_id(rdeconf));
+ ri.emetric.as = r->emetric.as;
+ ri.emetric.tag = r->emetric.tag;
+ ri.emetric.metric = r->emetric.metric;
+ if (kr->priority == rdeconf->fib_priority_internal)
+ ri.emetric.protocol = EIGRP_EXT_PROTO_EIGRP;
+ else if (kr->priority == RTP_STATIC)
+ ri.emetric.protocol = EIGRP_EXT_PROTO_STATIC;
+ else if (kr->priority == RTP_RIP)
+ ri.emetric.protocol = EIGRP_EXT_PROTO_RIP;
+ else if (kr->priority == RTP_OSPF)
+ ri.emetric.protocol = EIGRP_EXT_PROTO_OSPF;
+ else
+ ri.emetric.protocol = EIGRP_EXT_PROTO_CONN;
+ ri.emetric.flags = 0;
+
+ rde_check_update(eigrp->rnbr_redist, &ri);
+ }
+}
+
+/* send all known routing information to new neighbor */
+void
+rt_snap(struct rde_nbr *nbr)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct rt_node *rn;
+ struct rinfo ri;
+
+ RB_FOREACH(rn, rt_tree, &eigrp->topology)
+ if (rn->state == DUAL_STA_PASSIVE) {
+ rinfo_fill_successor(rn, &ri);
+ rde_imsg_compose_eigrpe(IMSG_SEND_UPDATE,
+ nbr->peerid, 0, &ri, sizeof(ri));
+ }
+
+ rde_imsg_compose_eigrpe(IMSG_SEND_UPDATE_END, nbr->peerid, 0,
+ NULL, 0);
+}
+
+struct ctl_rt *
+rt_to_ctl(struct rt_node *rn, struct eigrp_route *route)
+{
+ static struct ctl_rt rtctl;
+
+ memset(&rtctl, 0, sizeof(rtctl));
+ rtctl.af = route->nbr->eigrp->af;
+ rtctl.as = route->nbr->eigrp->as;
+ memcpy(&rtctl.prefix, &rn->prefix, sizeof(rtctl.prefix));
+ rtctl.prefixlen = rn->prefixlen;
+ rtctl.type = route->type;
+ memcpy(&rtctl.nexthop, &route->nbr->addr, sizeof(rtctl.nexthop));
+ if (route->nbr->flags & F_RDE_NBR_REDIST)
+ strlcpy(rtctl.ifname, "redistribute", sizeof(rtctl.ifname));
+ else if (route->nbr->flags & F_RDE_NBR_SUMMARY)
+ strlcpy(rtctl.ifname, "summary", sizeof(rtctl.ifname));
+ else
+ memcpy(rtctl.ifname, route->nbr->ei->iface->name,
+ sizeof(rtctl.ifname));
+ rtctl.distance = route->distance;
+ rtctl.rdistance = route->rdistance;
+ rtctl.fdistance = rn->successor.fdistance;
+ rtctl.state = rn->state;
+ /* metric */
+ rtctl.metric.delay = eigrp_real_delay(route->metric.delay);
+ /* translate to microseconds */
+ rtctl.metric.delay *= 10;
+ rtctl.metric.bandwidth = eigrp_real_bandwidth(route->metric.bandwidth);
+ rtctl.metric.mtu = metric_decode_mtu(route->metric.mtu);
+ rtctl.metric.hop_count = route->metric.hop_count;
+ rtctl.metric.reliability = route->metric.reliability;
+ rtctl.metric.load = route->metric.load;
+ /* external metric */
+ memcpy(&rtctl.emetric, &route->emetric, sizeof(rtctl.emetric));
+
+ if (route->nbr == rn->successor.nbr)
+ rtctl.flags |= F_CTL_RT_SUCCESSOR;
+ else if (route->rdistance < rn->successor.fdistance)
+ rtctl.flags |= F_CTL_RT_FSUCCESSOR;
+
+ return (&rtctl);
+}
+
+void
+rt_dump(struct ctl_show_topology_req *treq, pid_t pid)
+{
+ struct eigrp *eigrp;
+ struct rt_node *rn;
+ struct eigrp_route *route;
+ struct ctl_rt *rtctl;
+ int first = 1;
+
+ TAILQ_FOREACH(eigrp, &rdeconf->instances, entry) {
+ RB_FOREACH(rn, rt_tree, &eigrp->topology) {
+ if (eigrp_addrisset(treq->af, &treq->prefix) &&
+ eigrp_addrcmp(treq->af, &treq->prefix,
+ &rn->prefix))
+ continue;
+
+ if (treq->prefixlen &&
+ (treq->prefixlen != rn->prefixlen))
+ continue;
+
+ first = 1;
+ TAILQ_FOREACH(route, &rn->routes, entry) {
+ if (treq->flags & F_CTL_ACTIVE &&
+ !(rn->state & DUAL_STA_ACTIVE_ALL))
+ continue;
+ if (!(treq->flags & F_CTL_ALLLINKS) &&
+ route->rdistance >= rn->successor.fdistance)
+ continue;
+
+ rtctl = rt_to_ctl(rn, route);
+ if (first) {
+ rtctl->flags |= F_CTL_RT_FIRST;
+ first = 0;
+ }
+ rde_imsg_compose_eigrpe(IMSG_CTL_SHOW_TOPOLOGY,
+ 0, pid, rtctl, sizeof(*rtctl));
+ }
+ }
+ }
+}
diff --git a/usr.sbin/eigrpd/rde.h b/usr.sbin/eigrpd/rde.h
new file mode 100644
index 00000000000..ba491a53142
--- /dev/null
+++ b/usr.sbin/eigrpd/rde.h
@@ -0,0 +1,196 @@
+/* $OpenBSD: rde.h,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RDE_H_
+#define _RDE_H_
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#include <event.h>
+#include <limits.h>
+
+#define min(x,y) ((x) <= (y) ? (x) : (y))
+
+/* just the info RDE needs */
+struct rde_nbr {
+ RB_ENTRY(rde_nbr) entry;
+ uint32_t peerid;
+ uint32_t ifaceid;
+ union eigrpd_addr addr;
+ struct eigrp_iface *ei;
+ struct eigrp *eigrp;
+ TAILQ_HEAD(,reply_node) rijk; /* outstanding replies */
+ uint8_t flags;
+};
+/*
+ * We have one "self" neighbor for each interface on which EIGRP is configured.
+ * This way we can inject local routes into the DUAL FSM just like any other
+ * route received from a remote neighbor. For each instance, we also have two
+ * additional special neighbors used to inject redistributed and summarized
+ * routes.
+ */
+#define F_RDE_NBR_SELF 0x01
+#define F_RDE_NBR_REDIST 0x02
+#define F_RDE_NBR_SUMMARY 0x04
+
+struct reply_node {
+ TAILQ_ENTRY(reply_node) rn_entry;
+ TAILQ_ENTRY(reply_node) nbr_entry;
+ struct event ev_active_timeout;
+ struct event ev_sia_timeout;
+ int siaquery_sent;
+ int siareply_recv;
+ struct rt_node *rn;
+ struct rde_nbr *nbr;
+};
+
+struct eigrp_route {
+ TAILQ_ENTRY(eigrp_route) entry;
+ struct rde_nbr *nbr; /* advertising nbr */
+ enum route_type type;
+ union eigrpd_addr nexthop;
+ uint32_t distance; /* local distance */
+ uint32_t rdistance; /* reported distance */
+ struct classic_metric metric; /* metric */
+ struct classic_emetric emetric; /* external metric */
+ uint8_t flags;
+};
+#define F_EIGRP_ROUTE_INSTALLED 0x01
+#define F_EIGRP_ROUTE_M_CHANGED 0x02
+
+struct rt_node {
+ RB_ENTRY(rt_node) entry;
+ struct eigrp *eigrp;
+ union eigrpd_addr prefix;
+ uint8_t prefixlen;
+ int state;
+ TAILQ_HEAD(,eigrp_route) routes;
+ TAILQ_HEAD(,reply_node) rijk; /* outstanding replies */
+
+ struct {
+ struct rde_nbr *nbr;
+ enum route_type type;
+ uint32_t fdistance;
+ uint32_t rdistance;
+ struct classic_metric metric;
+ struct classic_emetric emetric;
+ } successor;
+};
+
+/* DUAL states */
+#define DUAL_STA_PASSIVE 0x0001
+#define DUAL_STA_ACTIVE0 0x0002
+#define DUAL_STA_ACTIVE1 0x0004
+#define DUAL_STA_ACTIVE2 0x0008
+#define DUAL_STA_ACTIVE3 0x0010
+#define DUAL_STA_ACTIVE_ALL (DUAL_STA_ACTIVE0 | DUAL_STA_ACTIVE1 | \
+ DUAL_STA_ACTIVE2 | DUAL_STA_ACTIVE3)
+
+enum dual_event {
+ DUAL_EVT_1,
+ DUAL_EVT_2,
+ DUAL_EVT_3,
+ DUAL_EVT_4,
+ DUAL_EVT_5,
+ DUAL_EVT_6,
+ DUAL_EVT_7,
+ DUAL_EVT_8,
+ DUAL_EVT_9,
+ DUAL_EVT_10,
+ DUAL_EVT_11,
+ DUAL_EVT_12,
+ DUAL_EVT_13,
+ DUAL_EVT_14,
+ DUAL_EVT_15,
+ DUAL_EVT_16
+};
+
+/* rde.c */
+pid_t rde(struct eigrpd_conf *, int [2], int [2], int [2]);
+int rde_imsg_compose_parent(int, pid_t, void *, uint16_t);
+int rde_imsg_compose_eigrpe(int, uint32_t, pid_t, void *,
+ uint16_t);
+
+void rde_instance_init(struct eigrp *);
+void rde_instance_del(struct eigrp *);
+void rde_send_change_kroute(struct rt_node *, struct eigrp_route *);
+void rde_send_delete_kroute(struct rt_node *, struct eigrp_route *);
+void rt_redist_set(struct kroute *, int);
+void rt_snap(struct rde_nbr *);
+struct ctl_rt *rt_to_ctl(struct rt_node *, struct eigrp_route *);
+void rt_dump(struct ctl_show_topology_req *, pid_t);
+
+/* rde_nbr.c */
+struct rde_nbr *rde_nbr_find(uint32_t);
+struct rde_nbr *rde_nbr_new(uint32_t, struct rde_nbr *);
+void rde_nbr_del(struct rde_nbr *);
+
+/* rde_dual.c */
+int dual_fsm(struct rt_node *, enum dual_event);
+struct rt_node *rt_find(struct eigrp *, struct rinfo *);
+struct rt_node *rt_new(struct eigrp *, struct rinfo *);
+void rt_del(struct rt_node *);
+struct eigrp_route *route_find(struct rde_nbr *, struct rt_node *);
+struct eigrp_route *route_new(struct rt_node *, struct rde_nbr *,
+ struct rinfo *);
+void route_del(struct rt_node *, struct eigrp_route *);
+uint32_t safe_sum_uint32(uint32_t, uint32_t);
+uint32_t eigrp_composite_delay(uint32_t);
+uint32_t eigrp_real_delay(uint32_t);
+uint32_t eigrp_composite_bandwidth(uint32_t);
+uint32_t eigrp_real_bandwidth(uint32_t);
+void route_update_metrics(struct eigrp_route *,
+ struct rinfo *);
+void reply_outstanding_add(struct rt_node *,
+ struct rde_nbr *);
+struct reply_node *reply_outstanding_find(struct rt_node *,
+ struct rde_nbr *);
+void reply_outstanding_remove(struct reply_node *);
+void rinfo_fill_successor(struct rt_node *, struct rinfo *);
+void rinfo_fill_infinite(struct rt_node *, enum route_type,
+ struct rinfo *);
+void rt_update_fib(struct rt_node *);
+void rt_set_successor(struct rt_node *,
+ struct eigrp_route *);
+struct eigrp_route *rt_get_successor_fc(struct rt_node *);
+
+void rde_send_ack(struct rde_nbr *);
+void rde_send_update(struct eigrp_iface *, struct rinfo *);
+void rde_send_update_all(struct rt_node *, struct rinfo *);
+void rde_send_query(struct eigrp_iface *, struct rinfo *,
+ int);
+void rde_send_siaquery(struct rde_nbr *, struct rinfo *);
+void rde_send_query_all(struct eigrp *, struct rt_node *,
+ int);
+void rde_flush_queries(void);
+void rde_send_reply(struct rde_nbr *, struct rinfo *, int);
+void rde_check_update(struct rde_nbr *, struct rinfo *);
+void rde_check_query(struct rde_nbr *, struct rinfo *, int);
+void rde_last_reply(struct rt_node *);
+void rde_check_reply(struct rde_nbr *, struct rinfo *, int);
+void rde_check_link_down_rn(struct rde_nbr *,
+ struct rt_node *, struct eigrp_route *);
+void rde_check_link_down_nbr(struct rde_nbr *);
+void rde_check_link_down(unsigned int);
+struct eigrp_interface;
+void rde_check_link_cost_change(struct rde_nbr *,
+ struct eigrp_interface *);
+
+#endif /* _RDE_H_ */
diff --git a/usr.sbin/eigrpd/rde_dual.c b/usr.sbin/eigrpd/rde_dual.c
new file mode 100644
index 00000000000..6af2dc2e90b
--- /dev/null
+++ b/usr.sbin/eigrpd/rde_dual.c
@@ -0,0 +1,1245 @@
+/* $OpenBSD: rde_dual.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "eigrp.h"
+#include "eigrpd.h"
+#include "eigrpe.h"
+#include "log.h"
+#include "rde.h"
+
+void reply_active_timer(int, short, void *);
+void reply_active_start_timer(struct reply_node *);
+void reply_active_stop_timer(struct reply_node *);
+void reply_sia_timer(int, short, void *);
+void reply_sia_start_timer(struct reply_node *);
+void reply_sia_stop_timer(struct reply_node *);
+
+extern struct eigrpd_conf *rdeconf;
+
+static int rt_compare(struct rt_node *, struct rt_node *);
+RB_PROTOTYPE(rt_tree, rt_node, entry, rt_compare)
+RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
+
+static __inline int rde_nbr_compare(struct rde_nbr *, struct rde_nbr *);
+RB_HEAD(rde_nbr_head, rde_nbr);
+RB_PROTOTYPE(rde_nbr_head, rde_nbr, entry, rde_nbr_compare)
+RB_GENERATE(rde_nbr_head, rde_nbr, entry, rde_nbr_compare)
+
+struct rde_nbr_head rde_nbrs = RB_INITIALIZER(&rde_nbrs);
+
+/*
+ * NOTE: events that don't cause a state transition aren't triggered to avoid
+ * too much verbosity and are here mostly for illustration purposes.
+ */
+struct {
+ int state;
+ enum dual_event event;
+ int new_state;
+} dual_fsm_tbl[] = {
+ /* current state event resulting state */
+/* Passive */
+ {DUAL_STA_PASSIVE, DUAL_EVT_1, 0},
+ {DUAL_STA_PASSIVE, DUAL_EVT_2, 0},
+ {DUAL_STA_PASSIVE, DUAL_EVT_3, DUAL_STA_ACTIVE3},
+ {DUAL_STA_PASSIVE, DUAL_EVT_4, DUAL_STA_ACTIVE1},
+/* Active Oij=0 */
+ {DUAL_STA_ACTIVE0, DUAL_EVT_5, DUAL_STA_ACTIVE2},
+ {DUAL_STA_ACTIVE0, DUAL_EVT_11, DUAL_STA_ACTIVE1},
+ {DUAL_STA_ACTIVE0, DUAL_EVT_14, DUAL_STA_PASSIVE},
+/* Active Oij=1 */
+ {DUAL_STA_ACTIVE1, DUAL_EVT_5, DUAL_STA_ACTIVE2},
+ {DUAL_STA_ACTIVE1, DUAL_EVT_9, DUAL_STA_ACTIVE0},
+ {DUAL_STA_ACTIVE1, DUAL_EVT_15, DUAL_STA_PASSIVE},
+/* Active Oij=2 */
+ {DUAL_STA_ACTIVE2, DUAL_EVT_12, DUAL_STA_ACTIVE3},
+ {DUAL_STA_ACTIVE2, DUAL_EVT_16, DUAL_STA_PASSIVE},
+/* Active Oij=3 */
+ {DUAL_STA_ACTIVE3, DUAL_EVT_10, DUAL_STA_ACTIVE2},
+ {DUAL_STA_ACTIVE3, DUAL_EVT_13, DUAL_STA_PASSIVE},
+/* Active (all) */
+ {DUAL_STA_ACTIVE_ALL, DUAL_EVT_6, 0},
+ {DUAL_STA_ACTIVE_ALL, DUAL_EVT_7, 0},
+ {DUAL_STA_ACTIVE_ALL, DUAL_EVT_8, 0},
+/* sentinel */
+ {-1, 0, 0},
+};
+
+const char * const dual_event_names[] = {
+ "DUAL_EVT_1",
+ "DUAL_EVT_2",
+ "DUAL_EVT_3",
+ "DUAL_EVT_4",
+ "DUAL_EVT_5",
+ "DUAL_EVT_6",
+ "DUAL_EVT_7",
+ "DUAL_EVT_8",
+ "DUAL_EVT_9",
+ "DUAL_EVT_10",
+ "DUAL_EVT_11",
+ "DUAL_EVT_12",
+ "DUAL_EVT_13",
+ "DUAL_EVT_14",
+ "DUAL_EVT_15",
+ "DUAL_EVT_16"
+};
+
+int
+dual_fsm(struct rt_node *rn, enum dual_event event)
+{
+ int old_state;
+ int new_state = 0;
+ int i;
+
+ old_state = rn->state;
+ for (i = 0; dual_fsm_tbl[i].state != -1; i++)
+ if ((dual_fsm_tbl[i].state & old_state) &&
+ (dual_fsm_tbl[i].event == event)) {
+ new_state = dual_fsm_tbl[i].new_state;
+ break;
+ }
+
+ if (dual_fsm_tbl[i].state == -1) {
+ /* event outside of the defined fsm, ignore it. */
+ log_warnx("%s: route %s, event %s not expected in state %s",
+ __func__, log_prefix(rn), dual_event_names[event],
+ dual_state_name(old_state));
+ return (0);
+ }
+
+ if (new_state != 0)
+ rn->state = new_state;
+
+ if (old_state != rn->state) {
+ log_debug("%s: event %s changing state for prefix %s "
+ "from %s to %s", __func__, dual_event_names[event],
+ log_prefix(rn), dual_state_name(old_state),
+ dual_state_name(rn->state));
+
+ if (old_state == DUAL_STA_PASSIVE ||
+ new_state == DUAL_STA_PASSIVE)
+ rt_update_fib(rn);
+ }
+
+ return (0);
+}
+
+static int
+rt_compare(struct rt_node *a, struct rt_node *b)
+{
+ if (a->eigrp->af < b->eigrp->af)
+ return (-1);
+ if (a->eigrp->af > b->eigrp->af)
+ return (1);
+
+ switch (a->eigrp->af) {
+ case AF_INET:
+ if (ntohl(a->prefix.v4.s_addr) <
+ ntohl(b->prefix.v4.s_addr))
+ return (-1);
+ if (ntohl(a->prefix.v4.s_addr) >
+ ntohl(b->prefix.v4.s_addr))
+ return (1);
+ break;
+ case AF_INET6:
+ if (memcmp(a->prefix.v6.s6_addr,
+ b->prefix.v6.s6_addr, 16) < 0)
+ return (-1);
+ if (memcmp(a->prefix.v6.s6_addr,
+ b->prefix.v6.s6_addr, 16) > 0)
+ return (1);
+ break;
+ default:
+ log_debug("%s: unexpected address-family", __func__);
+ break;
+ }
+
+ if (a->prefixlen < b->prefixlen)
+ return (-1);
+ if (a->prefixlen > b->prefixlen)
+ return (1);
+
+ return (0);
+}
+
+struct rt_node *
+rt_find(struct eigrp *eigrp, struct rinfo *ri)
+{
+ struct rt_node rn;
+
+ rn.eigrp = eigrp;
+ memcpy(&rn.prefix, &ri->prefix, sizeof(rn.prefix));
+ rn.prefixlen = ri->prefixlen;
+
+ return (RB_FIND(rt_tree, &eigrp->topology, &rn));
+}
+
+struct rt_node *
+rt_new(struct eigrp *eigrp, struct rinfo *ri)
+{
+ struct rt_node *rn;
+
+ if ((rn = calloc(1, sizeof(*rn))) == NULL)
+ fatal("rt_new");
+
+ rn->eigrp = eigrp;
+ memcpy(&rn->prefix, &ri->prefix, sizeof(rn->prefix));
+ rn->prefixlen = ri->prefixlen;
+ rn->state = DUAL_STA_PASSIVE;
+ TAILQ_INIT(&rn->routes);
+ TAILQ_INIT(&rn->rijk);
+ rt_set_successor(rn, NULL);
+
+ if (RB_INSERT(rt_tree, &eigrp->topology, rn) != NULL) {
+ log_warnx("%s failed for %s", __func__, log_prefix(rn));
+ free(rn);
+ return (NULL);
+ }
+
+ log_debug("%s: prefix %s", __func__, log_prefix(rn));
+
+ return (rn);
+}
+
+void
+rt_del(struct rt_node *rn)
+{
+ struct eigrp_route *route;
+ struct reply_node *reply;
+
+ log_debug("%s: prefix %s", __func__, log_prefix(rn));
+
+ while ((reply = TAILQ_FIRST(&rn->rijk)) != NULL)
+ reply_outstanding_remove(reply);
+ while ((route = TAILQ_FIRST(&rn->routes)) != NULL)
+ route_del(rn, route);
+ RB_REMOVE(rt_tree, &rn->eigrp->topology, rn);
+ free(rn);
+}
+
+struct eigrp_route *
+route_find(struct rde_nbr *nbr, struct rt_node *rn)
+{
+ struct eigrp_route *route;
+
+ TAILQ_FOREACH(route, &rn->routes, entry)
+ if (route->nbr == nbr)
+ return (route);
+
+ return (NULL);
+}
+
+static const char *
+route_print_origin(int af, struct rde_nbr *nbr)
+{
+ if (nbr->flags & F_RDE_NBR_SELF) {
+ if (nbr->flags & F_RDE_NBR_REDIST)
+ return ("redistribute");
+ if (nbr->flags & F_RDE_NBR_SUMMARY)
+ return ("summary");
+ else
+ return ("connected");
+ }
+
+ return (log_addr(af, &nbr->addr));
+}
+
+struct eigrp_route *
+route_new(struct rt_node *rn, struct rde_nbr *nbr, struct rinfo *ri)
+{
+ struct eigrp *eigrp = rn->eigrp;
+ struct eigrp_route *route;
+
+ if ((route = calloc(1, sizeof(*route))) == NULL)
+ fatal("route_new");
+
+ route->nbr = nbr;
+ route->type = ri->type;
+ memcpy(&route->nexthop, &ri->nexthop, sizeof(route->nexthop));
+ route_update_metrics(route, ri);
+ TAILQ_INSERT_TAIL(&rn->routes, route, entry);
+
+ log_debug("%s: prefix %s via %s distance (%u/%u)", __func__,
+ log_prefix(rn), route_print_origin(eigrp->af, route->nbr),
+ route->distance, route->rdistance);
+
+ return (route);
+}
+
+void
+route_del(struct rt_node *rn, struct eigrp_route *route)
+{
+ struct eigrp *eigrp = rn->eigrp;
+
+ log_debug("%s: prefix %s via %s", __func__, log_prefix(rn),
+ route_print_origin(eigrp->af, route->nbr));
+
+ if (route->flags & F_EIGRP_ROUTE_INSTALLED)
+ rde_send_delete_kroute(rn, route);
+
+ TAILQ_REMOVE(&rn->routes, route, entry);
+ free(route);
+}
+
+uint32_t
+safe_sum_uint32(uint32_t a, uint32_t b)
+{
+ uint64_t total;
+
+ total = (uint64_t) a + (uint64_t) b;
+
+ if (total >> 32)
+ return ((uint32_t )(~0));
+
+ return (total & 0xFFFFFFFF);
+}
+
+uint32_t
+eigrp_composite_delay(uint32_t delay)
+{
+ /*
+ * NOTE: the multiplication below has no risk of overflow
+ * because of the maximum configurable delay.
+ */
+ return (delay * EIGRP_SCALING_FACTOR);
+}
+
+uint32_t
+eigrp_real_delay(uint32_t delay)
+{
+ return (delay / EIGRP_SCALING_FACTOR);
+}
+
+uint32_t
+eigrp_composite_bandwidth(uint32_t bandwidth)
+{
+ return ((EIGRP_SCALING_FACTOR * (uint32_t)10000000) / bandwidth);
+}
+
+/* the formula is the same but let's focus on keeping the code readable */
+uint32_t
+eigrp_real_bandwidth(uint32_t bandwidth)
+{
+ return ((EIGRP_SCALING_FACTOR * (uint32_t)10000000) / bandwidth);
+}
+
+void
+route_update_metrics(struct eigrp_route *route, struct rinfo *ri)
+{
+ uint32_t bandwidth;
+ int mtu;
+
+ if (route->nbr->flags & F_RDE_NBR_SELF) {
+ memcpy(&route->metric, &ri->metric, sizeof(route->metric));
+ memcpy(&route->emetric, &ri->emetric, sizeof(route->emetric));
+ route->rdistance = 0;
+
+ /* no need to update the local metric */
+ } else {
+ memcpy(&route->metric, &ri->metric, sizeof(route->metric));
+ memcpy(&route->emetric, &ri->emetric, sizeof(route->emetric));
+ route->rdistance = safe_sum_uint32(ri->metric.delay,
+ ri->metric.bandwidth);
+
+ /* update delay. */
+ route->metric.delay = safe_sum_uint32(route->metric.delay,
+ eigrp_composite_delay(route->nbr->ei->delay));
+
+ /* update bandwidth */
+ bandwidth = min(route->nbr->ei->bandwidth,
+ eigrp_real_bandwidth(route->metric.bandwidth));
+ route->metric.bandwidth = eigrp_composite_bandwidth(bandwidth);
+
+ /* update mtu */
+ mtu = min(metric_decode_mtu(route->metric.mtu),
+ route->nbr->ei->iface->mtu);
+ metric_encode_mtu(route->metric.mtu, mtu);
+
+ /* update hop count */
+ if (route->metric.hop_count < UINT8_MAX)
+ route->metric.hop_count++;
+ }
+
+ route->distance = route->metric.delay + route->metric.bandwidth;
+ route->flags |= F_EIGRP_ROUTE_M_CHANGED;
+}
+
+void
+reply_outstanding_add(struct rt_node *rn, struct rde_nbr *nbr)
+{
+ struct reply_node *reply;
+
+ if ((reply = calloc(1, sizeof(*reply))) == NULL)
+ fatal("reply_outstanding_add");
+
+ evtimer_set(&reply->ev_active_timeout, reply_active_timer, reply);
+ evtimer_set(&reply->ev_sia_timeout, reply_sia_timer, reply);
+ reply->siaquery_sent = 0;
+ reply->siareply_recv = 0;
+ reply->rn = rn;
+ reply->nbr = nbr;
+ TAILQ_INSERT_TAIL(&rn->rijk, reply, rn_entry);
+ TAILQ_INSERT_TAIL(&nbr->rijk, reply, nbr_entry);
+
+ reply_active_start_timer(reply);
+ reply_sia_start_timer(reply);
+}
+
+struct reply_node *
+reply_outstanding_find(struct rt_node *rn, struct rde_nbr *nbr)
+{
+ struct reply_node *reply;
+
+ TAILQ_FOREACH(reply, &rn->rijk, rn_entry)
+ if (reply->nbr == nbr)
+ return (reply);
+
+ return (NULL);
+}
+
+void
+reply_outstanding_remove(struct reply_node *reply)
+{
+ reply_active_stop_timer(reply);
+ reply_sia_stop_timer(reply);
+ TAILQ_REMOVE(&reply->rn->rijk, reply, rn_entry);
+ TAILQ_REMOVE(&reply->nbr->rijk, reply, nbr_entry);
+ free(reply);
+}
+
+/* ARGSUSED */
+void
+reply_active_timer(int fd, short event, void *arg)
+{
+ struct reply_node *reply = arg;
+ struct rde_nbr *nbr = reply->nbr;
+
+ log_debug("%s: neighbor %s is stuck in active",
+ log_addr(nbr->eigrp->af, &nbr->addr));
+
+ rde_nbr_del(reply->nbr);
+}
+
+void
+reply_active_start_timer(struct reply_node *reply)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = EIGRP_ACTIVE_TIMEOUT;
+ if (evtimer_add(&reply->ev_active_timeout, &tv) == -1)
+ fatal("reply_active_start_timer");
+}
+
+void
+reply_active_stop_timer(struct reply_node *reply)
+{
+ if (evtimer_pending(&reply->ev_active_timeout, NULL) &&
+ evtimer_del(&reply->ev_active_timeout) == -1)
+ fatal("reply_active_stop_timer");
+}
+
+/* ARGSUSED */
+void
+reply_sia_timer(int fd, short event, void *arg)
+{
+ struct reply_node *reply = arg;
+ struct rde_nbr *nbr = reply->nbr;
+ struct rt_node *rn = reply->rn;
+ struct rinfo ri;
+
+ log_debug("%s: nbr %s prefix %s", __func__, log_addr(nbr->eigrp->af,
+ &nbr->addr), log_prefix(rn));
+
+ if (reply->siaquery_sent > 0 && reply->siareply_recv == 0) {
+ log_debug("%s: neighbor %s is stuck in active",
+ log_addr(nbr->eigrp->af, &nbr->addr));
+ rde_nbr_del(nbr);
+ return;
+ }
+
+ /* restart active timeout */
+ reply_active_start_timer(reply);
+
+ /* send an sia-query */
+ rinfo_fill_successor(rn, &ri);
+ ri.metric.flags |= F_METRIC_ACTIVE;
+ rde_send_siaquery(nbr, &ri);
+
+ /*
+ * draft-savage-eigrp-04 - Section 4.4.1.1:
+ * "Up to three SIA-QUERY packets for a specific destination may
+ * be sent, each at a value of one-half the ACTIVE time, so long
+ * as each are successfully acknowledged and met with an SIA-REPLY".
+ */
+ if (reply->siaquery_sent < 3) {
+ reply->siaquery_sent++;
+ reply->siareply_recv = 0;
+ reply_sia_start_timer(reply);
+ }
+}
+
+void
+reply_sia_start_timer(struct reply_node *reply)
+{
+ struct timeval tv;
+
+ /*
+ * draft-savage-eigrp-04 - Section 4.4.1.1:
+ * "The SIA-QUERY packet SHOULD be sent on a per-destination basis
+ * at one-half of the ACTIVE timeout period."
+ */
+ timerclear(&tv);
+ tv.tv_sec = EIGRP_ACTIVE_TIMEOUT / 2;
+ if (evtimer_add(&reply->ev_sia_timeout, &tv) == -1)
+ fatal("reply_sia_start_timer");
+}
+
+void
+reply_sia_stop_timer(struct reply_node *reply)
+{
+ if (evtimer_pending(&reply->ev_sia_timeout, NULL) &&
+ evtimer_del(&reply->ev_sia_timeout) == -1)
+ fatal("reply_sia_stop_timer");
+}
+
+void
+rinfo_fill_successor(struct rt_node *rn, struct rinfo *ri)
+{
+ if (rn->successor.nbr == NULL) {
+ rinfo_fill_infinite(rn, EIGRP_ROUTE_INTERNAL, ri);
+ return;
+ }
+
+ memset(ri, 0, sizeof(*ri));
+ ri->af = rn->eigrp->af;
+ ri->type = rn->successor.type;
+ memcpy(&ri->prefix, &rn->prefix, sizeof(ri->prefix));
+ ri->prefixlen = rn->prefixlen;
+ memcpy(&ri->metric, &rn->successor.metric, sizeof(ri->metric));
+ if (ri->type == EIGRP_ROUTE_EXTERNAL)
+ memcpy(&ri->emetric, &rn->successor.emetric,
+ sizeof(ri->emetric));
+}
+
+void
+rinfo_fill_infinite(struct rt_node *rn, enum route_type type, struct rinfo *ri)
+{
+ memset(ri, 0, sizeof(*ri));
+ ri->af = rn->eigrp->af;
+ ri->type = type;
+ memcpy(&ri->prefix, &rn->prefix, sizeof(ri->prefix));
+ ri->prefixlen = rn->prefixlen;
+ ri->metric.delay = EIGRP_INFINITE_METRIC;
+ ri->metric.bandwidth = 0;
+ ri->metric.mtu[0] = 0;
+ ri->metric.mtu[1] = 0;
+ ri->metric.mtu[2] = 0;
+ ri->metric.hop_count = 0;
+ ri->metric.reliability = 0;
+ ri->metric.load = 0;
+ ri->metric.tag = 0;
+ ri->metric.flags = 0;
+ if (ri->type == EIGRP_ROUTE_EXTERNAL) {
+ ri->emetric.routerid = 0;
+ ri->emetric.as = 0;
+ ri->emetric.tag = 0;
+ ri->emetric.metric = 0;
+ ri->emetric.protocol = 0;
+ ri->emetric.flags = 0;
+ }
+}
+
+void
+rt_update_fib(struct rt_node *rn)
+{
+ uint8_t maximum_paths = rn->eigrp->maximum_paths;
+ uint8_t variance = rn->eigrp->variance;
+ int installed = 0;
+ struct eigrp_route *route;
+
+ if (rn->state == DUAL_STA_PASSIVE) {
+ TAILQ_FOREACH(route, &rn->routes, entry) {
+ if (route->nbr->flags & F_RDE_NBR_SELF)
+ continue;
+
+ /*
+ * only feasible successors and the successor itself
+ * are elegible to be installed.
+ */
+ if (route->rdistance > rn->successor.fdistance)
+ goto uninstall;
+
+ /* no multipath for attached networks. */
+ if (rn->successor.rdistance == 0 &&
+ route->distance > 0)
+ goto uninstall;
+
+ if (route->distance >
+ (rn->successor.fdistance * variance))
+ goto uninstall;
+
+ if (installed >= maximum_paths)
+ goto uninstall;
+
+ installed++;
+
+ if (route->flags & (F_EIGRP_ROUTE_INSTALLED |
+ !F_EIGRP_ROUTE_M_CHANGED))
+ continue;
+
+ rde_send_change_kroute(rn, route);
+ continue;
+
+uninstall:
+ if (route->flags & F_EIGRP_ROUTE_INSTALLED)
+ rde_send_delete_kroute(rn, route);
+ }
+ } else {
+ TAILQ_FOREACH(route, &rn->routes, entry)
+ if (route->flags & F_EIGRP_ROUTE_INSTALLED)
+ rde_send_delete_kroute(rn, route);
+ }
+}
+
+void
+rt_set_successor(struct rt_node *rn, struct eigrp_route *successor)
+{
+ if (successor == NULL) {
+ rn->successor.nbr = NULL;
+ rn->successor.type = 0;
+ rn->successor.fdistance = EIGRP_INFINITE_METRIC;
+ rn->successor.rdistance = EIGRP_INFINITE_METRIC;
+ memset(&rn->successor.metric, 0,
+ sizeof(rn->successor.metric));
+ memset(&rn->successor.emetric, 0,
+ sizeof(rn->successor.emetric));
+ return;
+ }
+
+ rn->successor.nbr = successor->nbr;
+ rn->successor.type = successor->type;
+ rn->successor.fdistance = successor->distance;
+ rn->successor.rdistance = successor->rdistance;
+ memcpy(&rn->successor.metric, &successor->metric,
+ sizeof(rn->successor.metric));
+ memcpy(&rn->successor.emetric, &successor->emetric,
+ sizeof(rn->successor.emetric));
+}
+
+struct eigrp_route *
+rt_get_successor_fc(struct rt_node *rn)
+{
+ struct eigrp_route *route, *successor = NULL;
+ uint32_t distance = EIGRP_INFINITE_METRIC;
+ int external_only = 1;
+
+ TAILQ_FOREACH(route, &rn->routes, entry)
+ if (route->type == EIGRP_ROUTE_INTERNAL) {
+ /*
+ * connected routes should always be prefered over
+ * received routes independent of the metric.
+ */
+ if (route->rdistance == 0)
+ return (route);
+
+ external_only = 0;
+ }
+
+ TAILQ_FOREACH(route, &rn->routes, entry) {
+ /*
+ * draft-savage-eigrp-04 - Section 5.4.7:
+ * "Internal routes MUST be prefered over external routes
+ * independent of the metric."
+ */
+ if (route->type == EIGRP_ROUTE_EXTERNAL && !external_only)
+ continue;
+
+ /* pick the best route that meets the feasibility condition */
+ if (route->rdistance < rn->successor.fdistance &&
+ route->distance < distance) {
+ distance = route->distance;
+ successor = route;
+ }
+ }
+
+ return (successor);
+}
+
+void
+rde_send_update(struct eigrp_iface *ei, struct rinfo *ri)
+{
+ if (ri->metric.hop_count >= ei->eigrp->maximum_hops)
+ ri->metric.delay = EIGRP_INFINITE_METRIC;
+
+ rde_imsg_compose_eigrpe(IMSG_SEND_MUPDATE, ei->ifaceid, 0,
+ ri, sizeof(*ri));
+ rde_imsg_compose_eigrpe(IMSG_SEND_MUPDATE_END, ei->ifaceid, 0,
+ NULL, 0);
+}
+
+void
+rde_send_update_all(struct rt_node *rn, struct rinfo *ri)
+{
+ struct eigrp *eigrp = rn->eigrp;
+ struct eigrp_iface *ei;
+
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
+ /* respect split-horizon configuration */
+ if (rn->successor.nbr && rn->successor.nbr->ei == ei &&
+ ei->splithorizon)
+ continue;
+ rde_send_update(ei, ri);
+ }
+}
+
+void
+rde_send_query(struct eigrp_iface *ei, struct rinfo *ri, int push)
+{
+ rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY, ei->ifaceid, 0,
+ ri, sizeof(*ri));
+ if (push)
+ rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY_END, ei->ifaceid,
+ 0, NULL, 0);
+}
+
+void
+rde_send_siaquery(struct rde_nbr *nbr, struct rinfo *ri)
+{
+ rde_imsg_compose_eigrpe(IMSG_SEND_QUERY, nbr->peerid, 0,
+ ri, sizeof(*ri));
+ rde_imsg_compose_eigrpe(IMSG_SEND_SIAQUERY_END, nbr->peerid, 0,
+ NULL, 0);
+}
+
+void
+rde_send_query_all(struct eigrp *eigrp, struct rt_node *rn, int push)
+{
+ struct eigrp_iface *ei;
+ struct rde_nbr *nbr;
+ struct rinfo ri;
+
+ rinfo_fill_successor(rn, &ri);
+ ri.metric.flags |= F_METRIC_ACTIVE;
+
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
+ /* respect split-horizon configuration */
+ if (rn->successor.nbr && rn->successor.nbr->ei == ei &&
+ ei->splithorizon)
+ continue;
+
+ rde_send_query(ei, &ri, push);
+ }
+
+ RB_FOREACH(nbr, rde_nbr_head, &rde_nbrs)
+ if (nbr->ei->eigrp == eigrp && !(nbr->flags & F_RDE_NBR_SELF)) {
+ /* respect split-horizon configuration */
+ if (rn->successor.nbr &&
+ rn->successor.nbr->ei == nbr->ei &&
+ nbr->ei->splithorizon)
+ continue;
+
+ reply_outstanding_add(rn, nbr);
+ }
+}
+
+void
+rde_flush_queries(void)
+{
+ struct eigrp *eigrp;
+ struct eigrp_iface *ei;
+
+ TAILQ_FOREACH(eigrp, &rdeconf->instances, entry)
+ TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry)
+ rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY_END,
+ ei->ifaceid, 0, NULL, 0);
+}
+
+void
+rde_send_reply(struct rde_nbr *nbr, struct rinfo *ri, int siareply)
+{
+ int type;
+
+ if (ri->metric.hop_count >= nbr->eigrp->maximum_hops)
+ ri->metric.delay = EIGRP_INFINITE_METRIC;
+
+ if (!siareply)
+ type = IMSG_SEND_REPLY_END;
+ else
+ type = IMSG_SEND_SIAREPLY_END;
+
+ rde_imsg_compose_eigrpe(IMSG_SEND_REPLY, nbr->peerid, 0,
+ ri, sizeof(*ri));
+ rde_imsg_compose_eigrpe(type, nbr->peerid, 0, NULL, 0);
+}
+
+void
+rde_check_update(struct rde_nbr *nbr, struct rinfo *ri)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct rt_node *rn;
+ struct eigrp_route *route, *successor;
+ uint32_t old_fdistance;
+ struct rinfo sri;
+
+ rn = rt_find(eigrp, ri);
+ if (rn == NULL) {
+ if (ri->metric.delay == EIGRP_INFINITE_METRIC)
+ return;
+
+ rn = rt_new(eigrp, ri);
+ route = route_new(rn, nbr, ri);
+
+ old_fdistance = EIGRP_INFINITE_METRIC;
+ } else {
+ old_fdistance = rn->successor.fdistance;
+
+ if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
+ route = route_find(nbr, rn);
+ if (route) {
+ route_del(rn, route);
+ }
+ } else {
+ route = route_find(nbr, rn);
+ if (route == NULL)
+ route = route_new(rn, nbr, ri);
+ else
+ route_update_metrics(route, ri);
+ }
+ }
+
+ switch (rn->state) {
+ case DUAL_STA_PASSIVE:
+ successor = rt_get_successor_fc(rn);
+
+ /*
+ * go active if the successor was affected and no feasible
+ * successor exist.
+ */
+ if (successor == NULL) {
+ rde_send_query_all(eigrp, rn, 1);
+
+ dual_fsm(rn, DUAL_EVT_4);
+ } else {
+ rt_set_successor(rn, successor);
+ rt_update_fib(rn);
+
+ /* send update with new metric if necessary */
+ rinfo_fill_successor(rn, &sri);
+ if (rn->successor.fdistance != old_fdistance)
+ rde_send_update_all(rn, &sri);
+ }
+ break;
+ case DUAL_STA_ACTIVE1:
+ /* XXX event 9 if cost increase? */
+ break;
+ case DUAL_STA_ACTIVE3:
+ /* XXX event 10 if cost increase? */
+ break;
+ }
+
+ if ((rn->state & DUAL_STA_ACTIVE_ALL) && TAILQ_EMPTY(&rn->rijk))
+ rde_last_reply(rn);
+}
+
+void
+rde_check_query(struct rde_nbr *nbr, struct rinfo *ri, int siaquery)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct rt_node *rn;
+ struct eigrp_route *route, *successor;
+ uint32_t old_fdistance;
+ struct rinfo sri;
+ int reply_sent = 0;
+
+ /*
+ * draft-savage-eigrp-02 - Section 4.3:
+ * "When a query is received for a route that doesn't exist in our
+ * topology table, a reply with infinite metric is sent and an entry
+ * in the topology table is added with the metric in the QUERY if
+ * the metric is not an infinite value".
+ */
+ rn = rt_find(eigrp, ri);
+ if (rn == NULL) {
+ memcpy(&sri, ri, sizeof(sri));
+ sri.metric.delay = EIGRP_INFINITE_METRIC;
+ rde_send_reply(nbr, &sri, 0);
+
+ if (ri->metric.delay == EIGRP_INFINITE_METRIC)
+ return;
+
+ rn = rt_new(eigrp, ri);
+ route = route_new(rn, nbr, ri);
+ rt_set_successor(rn, route);
+ return;
+ }
+
+ old_fdistance = rn->successor.fdistance;
+
+ if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
+ route = route_find(nbr, rn);
+ if (route)
+ route_del(rn, route);
+ } else {
+ route = route_find(nbr, rn);
+ if (route == NULL)
+ route = route_new(rn, nbr, ri);
+ else
+ route_update_metrics(route, ri);
+ }
+
+ switch (rn->state) {
+ case DUAL_STA_PASSIVE:
+ successor = rt_get_successor_fc(rn);
+
+ /*
+ * go active if the successor was affected and no feasible
+ * successor exist.
+ */
+ if (successor == NULL) {
+ rde_send_query_all(eigrp, rn, 1);
+ dual_fsm(rn, DUAL_EVT_3);
+ } else {
+ rt_set_successor(rn, successor);
+ rt_update_fib(rn);
+
+ /* send reply */
+ rinfo_fill_successor(rn, &sri);
+ rde_send_reply(nbr, &sri, 0);
+ reply_sent = 1;
+
+ /* send update with new metric if necessary */
+ if (rn->successor.fdistance != old_fdistance)
+ rde_send_update_all(rn, &sri);
+ }
+ break;
+ case DUAL_STA_ACTIVE0:
+ case DUAL_STA_ACTIVE1:
+ if (nbr == rn->successor.nbr)
+ dual_fsm(rn, DUAL_EVT_5);
+ else {
+ dual_fsm(rn, DUAL_EVT_6);
+ rinfo_fill_successor(rn, &sri);
+ sri.metric.flags |= F_METRIC_ACTIVE;
+ rde_send_reply(nbr, &sri, 0);
+ reply_sent = 1;
+ }
+ break;
+ case DUAL_STA_ACTIVE2:
+ case DUAL_STA_ACTIVE3:
+ if (nbr == rn->successor.nbr) {
+ /* XXX not defined in the spec, do nothing? */
+ } else {
+ dual_fsm(rn, DUAL_EVT_6);
+ rinfo_fill_successor(rn, &sri);
+ sri.metric.flags |= F_METRIC_ACTIVE;
+ rde_send_reply(nbr, &sri, 0);
+ reply_sent = 1;
+ }
+ break;
+ }
+
+ if ((rn->state & DUAL_STA_ACTIVE_ALL) && TAILQ_EMPTY(&rn->rijk))
+ rde_last_reply(rn);
+
+ if (siaquery && !reply_sent) {
+ rinfo_fill_successor(rn, &sri);
+ sri.metric.flags |= F_METRIC_ACTIVE;
+ rde_send_reply(nbr, &sri, 1);
+ }
+}
+
+void
+rde_last_reply(struct rt_node *rn)
+{
+ struct eigrp *eigrp = rn->eigrp;
+ struct eigrp_route *successor;
+ struct rde_nbr *old_successor;
+ uint32_t old_fdistance;
+ struct rinfo ri;
+
+ old_successor = rn->successor.nbr;
+ old_fdistance = rn->successor.fdistance;
+
+ switch (rn->state) {
+ case DUAL_STA_ACTIVE0:
+ successor = rt_get_successor_fc(rn);
+ if (successor == NULL) {
+ /* feasibility condition is not met */
+ rde_send_query_all(eigrp, rn, 1);
+ dual_fsm(rn, DUAL_EVT_11);
+ break;
+ }
+
+ /* update successor - feasibility condition is met */
+ rt_set_successor(rn, successor);
+
+ /* advertise new successor to neighbors */
+ rinfo_fill_successor(rn, &ri);
+ rde_send_update_all(rn, &ri);
+
+ dual_fsm(rn, DUAL_EVT_14);
+ break;
+ case DUAL_STA_ACTIVE1:
+ /* update successor */
+ rn->successor.fdistance = EIGRP_INFINITE_METRIC;
+ successor = rt_get_successor_fc(rn);
+ rt_set_successor(rn, successor);
+
+ /* advertise new successor to neighbors */
+ rinfo_fill_successor(rn, &ri);
+ rde_send_update_all(rn, &ri);
+
+ dual_fsm(rn, DUAL_EVT_15);
+ break;
+ case DUAL_STA_ACTIVE2:
+ successor = rt_get_successor_fc(rn);
+ if (successor == NULL) {
+ /* feasibility condition is not met */
+ rde_send_query_all(eigrp, rn, 1);
+ dual_fsm(rn, DUAL_EVT_12);
+ break;
+ }
+
+ /* update successor - feasibility condition is met */
+ rt_set_successor(rn, successor);
+
+ /* send a reply to the old successor */
+ rinfo_fill_successor(rn, &ri);
+ ri.metric.flags |= F_METRIC_ACTIVE;
+ if (old_successor)
+ rde_send_reply(old_successor, &ri, 0);
+
+ /* advertise new successor to neighbors */
+ rde_send_update_all(rn, &ri);
+
+ dual_fsm(rn, DUAL_EVT_16);
+ break;
+ case DUAL_STA_ACTIVE3:
+ /* update successor */
+ rn->successor.fdistance = EIGRP_INFINITE_METRIC;
+ successor = rt_get_successor_fc(rn);
+ rt_set_successor(rn, successor);
+
+ /* send a reply to the old successor */
+ rinfo_fill_successor(rn, &ri);
+ ri.metric.flags |= F_METRIC_ACTIVE;
+ if (old_successor)
+ rde_send_reply(old_successor, &ri, 0);
+
+ /* advertise new successor to neighbors */
+ rde_send_update_all(rn, &ri);
+
+ dual_fsm(rn, DUAL_EVT_13);
+ break;
+ }
+
+ if (rn->state == DUAL_STA_PASSIVE && rn->successor.nbr == NULL)
+ rt_del(rn);
+}
+
+void
+rde_check_reply(struct rde_nbr *nbr, struct rinfo *ri, int siareply)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct rt_node *rn;
+ struct reply_node *reply;
+ struct eigrp_route *route;
+
+ rn = rt_find(eigrp, ri);
+ if (rn == NULL)
+ return;
+
+ /* XXX ignore reply when the state is passive? */
+ if (rn->state == DUAL_STA_PASSIVE)
+ return;
+
+ reply = reply_outstanding_find(rn, nbr);
+ if (reply == NULL)
+ return;
+
+ if (siareply) {
+ reply->siareply_recv = 1;
+ reply_active_start_timer(reply);
+ return;
+ }
+
+ if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
+ route = route_find(nbr, rn);
+ if (route)
+ route_del(rn, route);
+ } else {
+ route = route_find(nbr, rn);
+ if (route == NULL)
+ route = route_new(rn, nbr, ri);
+ else
+ route_update_metrics(route, ri);
+ }
+
+ reply_outstanding_remove(reply);
+ if (!TAILQ_EMPTY(&rn->rijk))
+ /* not last reply */
+ return;
+
+ rde_last_reply(rn);
+}
+
+void
+rde_check_link_down_rn(struct rde_nbr *nbr, struct rt_node *rn,
+ struct eigrp_route *route)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct reply_node *reply;
+ struct eigrp_route *successor;
+ uint32_t old_fdistance;
+ struct rinfo ri;
+
+ old_fdistance = rn->successor.fdistance;
+
+ route_del(rn, route);
+
+ switch (rn->state) {
+ case DUAL_STA_PASSIVE:
+ successor = rt_get_successor_fc(rn);
+
+ /*
+ * go active if the successor was affected and no feasible
+ * successor exist.
+ */
+ if (successor == NULL) {
+ rde_send_query_all(eigrp, rn, 0);
+
+ dual_fsm(rn, DUAL_EVT_4);
+ } else {
+ rt_set_successor(rn, successor);
+ rt_update_fib(rn);
+
+ /* send update with new metric if necessary */
+ rinfo_fill_successor(rn, &ri);
+ if (rn->successor.fdistance != old_fdistance)
+ rde_send_update_all(rn, &ri);
+ }
+ break;
+ case DUAL_STA_ACTIVE1:
+ if (nbr == rn->successor.nbr)
+ dual_fsm(rn, DUAL_EVT_9);
+ break;
+ case DUAL_STA_ACTIVE3:
+ if (nbr == rn->successor.nbr)
+ dual_fsm(rn, DUAL_EVT_10);
+ break;
+ }
+
+ if (rn->state & DUAL_STA_ACTIVE_ALL) {
+ reply = reply_outstanding_find(rn, nbr);
+ if (reply) {
+ rinfo_fill_infinite(rn, route->type, &ri);
+ rde_check_reply(nbr, &ri, 0);
+ }
+ }
+}
+
+void
+rde_check_link_down_nbr(struct rde_nbr *nbr)
+{
+ struct eigrp *eigrp = nbr->eigrp;
+ struct rt_node *rn, *safe;
+ struct eigrp_route *route;
+
+ RB_FOREACH_SAFE(rn, rt_tree, &eigrp->topology, safe) {
+ route = route_find(nbr, rn);
+ if (route) {
+ rde_check_link_down_rn(nbr, rn, route);
+ if (rn->successor.nbr == nbr)
+ rn->successor.nbr = NULL;
+ }
+ }
+}
+
+void
+rde_check_link_down(unsigned int ifindex)
+{
+ struct rde_nbr *nbr;
+
+ RB_FOREACH(nbr, rde_nbr_head, &rde_nbrs)
+ if (nbr->ei->iface->ifindex == ifindex)
+ rde_check_link_down_nbr(nbr);
+
+ rde_flush_queries();
+}
+
+void
+rde_check_link_cost_change(struct rde_nbr *nbr, struct eigrp_interface *ei)
+{
+}
+
+static __inline int
+rde_nbr_compare(struct rde_nbr *a, struct rde_nbr *b)
+{
+ return (a->peerid - b->peerid);
+}
+
+struct rde_nbr *
+rde_nbr_find(uint32_t peerid)
+{
+ struct rde_nbr n;
+
+ n.peerid = peerid;
+
+ return (RB_FIND(rde_nbr_head, &rde_nbrs, &n));
+}
+
+struct rde_nbr *
+rde_nbr_new(uint32_t peerid, struct rde_nbr *new)
+{
+ struct rde_nbr *nbr;
+
+ if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
+ fatal("rde_nbr_new");
+
+ nbr->peerid = peerid;
+ nbr->ifaceid = new->ifaceid;
+ memcpy(&nbr->addr, &new->addr, sizeof(nbr->addr));
+ nbr->ei = eigrp_iface_find_id(nbr->ifaceid);
+ if (nbr->ei)
+ nbr->eigrp = nbr->ei->eigrp;
+ TAILQ_INIT(&nbr->rijk);
+ nbr->flags = new->flags;
+
+ if (nbr->peerid != NBR_IDSELF &&
+ RB_INSERT(rde_nbr_head, &rde_nbrs, nbr) != NULL)
+ fatalx("rde_nbr_new: RB_INSERT failed");
+
+ return (nbr);
+}
+
+void
+rde_nbr_del(struct rde_nbr *nbr)
+{
+ struct reply_node *reply;
+
+ while((reply = TAILQ_FIRST(&nbr->rijk)) != NULL)
+ reply_outstanding_remove(reply);
+
+ if (nbr->peerid != NBR_IDSELF)
+ RB_REMOVE(rde_nbr_head, &rde_nbrs, nbr);
+ free(nbr);
+}
diff --git a/usr.sbin/eigrpd/reply.c b/usr.sbin/eigrpd/reply.c
new file mode 100644
index 00000000000..98dab8d54cf
--- /dev/null
+++ b/usr.sbin/eigrpd/reply.c
@@ -0,0 +1,120 @@
+/* $OpenBSD: reply.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+extern struct eigrpd_conf *econf;
+
+/* reply packet handling */
+
+void
+send_reply(struct nbr *nbr, struct rinfo_head *rinfo_list, int siareply)
+{
+ struct eigrp *eigrp = nbr->ei->eigrp;
+ struct ibuf *buf;
+ uint16_t opcode;
+ struct rinfo_entry *re;
+ int size;
+ int route_len;
+
+ if (rinfo_list == NULL || TAILQ_EMPTY(rinfo_list))
+ return;
+
+ /* don't exceed the interface's mtu */
+ do {
+ if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
+ IP_MAXPACKET - sizeof(struct ip))) == NULL)
+ fatal("send_reply");
+
+ if (!siareply)
+ opcode = EIGRP_OPC_REPLY;
+ else
+ opcode = EIGRP_OPC_SIAREPLY;
+
+ /* EIGRP header */
+ if (gen_eigrp_hdr(buf, opcode, 0, eigrp->seq_num, eigrp->as))
+ goto fail;
+
+ switch (eigrp->af) {
+ case AF_INET:
+ size = sizeof(struct ip);
+ break;
+ case AF_INET6:
+ size = sizeof(struct ip6_hdr);
+ break;
+ default:
+ break;
+ }
+ size += sizeof(struct eigrp_hdr);
+
+ while ((re = TAILQ_FIRST(rinfo_list)) != NULL) {
+ route_len = len_route_tlv(&re->rinfo);
+ if (size + route_len > nbr->ei->iface->mtu) {
+ rtp_send_ucast(nbr, buf);
+ break;
+ }
+ size += route_len;
+
+ if (gen_route_tlv(buf, &re->rinfo))
+ goto fail;
+ TAILQ_REMOVE(rinfo_list, re, entry);
+ free(re);
+ }
+ } while (!TAILQ_EMPTY(rinfo_list));
+
+ /* reply packets are always unicast */
+ rtp_send_ucast(nbr, buf);
+ return;
+fail:
+ log_warnx("%s: failed to send message", __func__);
+ if (rinfo_list)
+ message_list_clr(rinfo_list);
+ ibuf_free(buf);
+ return;
+}
+
+void
+recv_reply(struct nbr *nbr, struct rinfo_head *rinfo_list, int siareply)
+{
+ int type;
+ struct rinfo_entry *re;
+
+ /*
+ * draft-savage-eigrp-02 - Section 4.3:
+ * "When a REPLY packet is received, there is no reason to process
+ * the packet before an acknowledgment is sent. Therefore, an Ack
+ * packet is sent immediately and then the packet is processed."
+ */
+ rtp_send_ack(nbr);
+
+ if (!siareply)
+ type = IMSG_RECV_REPLY;
+ else
+ type = IMSG_RECV_SIAREPLY;
+
+ TAILQ_FOREACH(re, rinfo_list, entry)
+ eigrpe_imsg_compose_rde(type, nbr->peerid, 0, &re->rinfo,
+ sizeof(re->rinfo));
+}
diff --git a/usr.sbin/eigrpd/rtp.c b/usr.sbin/eigrpd/rtp.c
new file mode 100644
index 00000000000..556da6bb6f9
--- /dev/null
+++ b/usr.sbin/eigrpd/rtp.c
@@ -0,0 +1,309 @@
+/* $OpenBSD: rtp.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "eigrpe.h"
+#include "rde.h"
+#include "log.h"
+
+void rtp_retrans_timer(int, short, void *);
+void rtp_retrans_start_timer(struct packet *);
+void rtp_retrans_stop_timer(struct packet *);
+
+struct pbuf *
+rtp_buf_new(struct ibuf *buf)
+{
+ struct pbuf *pbuf;
+
+ if ((pbuf = calloc(1, sizeof(*pbuf))) == NULL)
+ fatal("rtp_buf_new");
+ pbuf->buf = buf;
+
+ return (pbuf);
+}
+
+struct pbuf *
+rtp_buf_hold(struct pbuf *pbuf)
+{
+ pbuf->refcnt++;
+ return (pbuf);
+}
+
+void
+rtp_buf_release(struct pbuf *pbuf)
+{
+ if (--pbuf->refcnt == 0) {
+ ibuf_free(pbuf->buf);
+ free(pbuf);
+ }
+}
+
+struct packet *
+rtp_packet_new(struct nbr *nbr, uint32_t seq_num, struct pbuf *pbuf)
+{
+ struct packet *pkt;
+
+ if ((pkt = calloc(1, sizeof(struct packet))) == NULL)
+ fatal("rtp_packet_new");
+
+ pkt->nbr = nbr;
+ pkt->seq_num = seq_num;
+ pkt->pbuf = rtp_buf_hold(pbuf);
+ pkt->attempts = 1;
+ evtimer_set(&pkt->ev_timeout, rtp_retrans_timer, pkt);
+
+ return (pkt);
+}
+
+void
+rtp_packet_del(struct packet *pkt)
+{
+ TAILQ_REMOVE(&pkt->nbr->retrans_list, pkt, entry);
+ rtp_retrans_stop_timer(pkt);
+ rtp_buf_release(pkt->pbuf);
+ free(pkt);
+}
+
+void
+rtp_process_ack(struct nbr *nbr, uint32_t ack_num)
+{
+ struct eigrp *eigrp = nbr->ei->eigrp;
+ struct packet *pkt;
+
+ /* window size is one */
+ pkt = TAILQ_FIRST(&nbr->retrans_list);
+ if (pkt && pkt->seq_num == ack_num) {
+ log_debug("%s: nbr %s ack %u", __func__,
+ log_addr(eigrp->af, &nbr->addr), ack_num);
+
+ /* dequeue packet from retransmission queue */
+ rtp_packet_del(pkt);
+
+ /* enqueue next packet from retransmission queue */
+ pkt = TAILQ_FIRST(&nbr->retrans_list);
+ if (pkt)
+ rtp_send_packet(pkt);
+ }
+}
+
+void
+rtp_send_packet(struct packet *pkt)
+{
+ rtp_retrans_start_timer(pkt);
+ send_packet(pkt->nbr->ei, pkt->nbr, 0, pkt->pbuf->buf);
+}
+
+void
+rtp_enqueue_packet(struct packet *pkt)
+{
+ /* only send packet if transmission queue is empty */
+ if (TAILQ_EMPTY(&pkt->nbr->retrans_list))
+ rtp_send_packet(pkt);
+
+ TAILQ_INSERT_TAIL(&pkt->nbr->retrans_list, pkt, entry);
+}
+
+static void
+rtp_seq_inc(struct eigrp *eigrp)
+{
+ /* automatic wraparound with unsigned arithmetic */
+ eigrp->seq_num++;
+
+ /* sequence number 0 is reserved for unreliably transmission */
+ if (eigrp->seq_num == 0)
+ eigrp->seq_num = 1;
+}
+
+void
+rtp_send_ucast(struct nbr *nbr, struct ibuf *buf)
+{
+ struct eigrp *eigrp = nbr->ei->eigrp;
+ struct packet *pkt;
+ struct pbuf *pbuf;
+
+ pbuf = rtp_buf_new(buf);
+ pkt = rtp_packet_new(nbr, eigrp->seq_num, pbuf);
+ rtp_enqueue_packet(pkt);
+ rtp_seq_inc(eigrp);
+}
+
+void
+rtp_send_mcast(struct eigrp_iface *ei, struct ibuf *buf)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct nbr *nbr;
+ int total = 0, pending = 0;
+ struct packet *pkt;
+ struct pbuf *pbuf;
+ uint32_t flags = 0;
+ struct seq_addr_entry *sa;
+ struct seq_addr_head seq_addr_list;
+
+ TAILQ_FOREACH(nbr, &ei->nbr_list, entry) {
+ if (nbr->flags & F_EIGRP_NBR_SELF)
+ continue;
+ if (!TAILQ_EMPTY(&nbr->retrans_list))
+ pending++;
+ total++;
+ }
+ if (total == 0)
+ return;
+
+ /*
+ * send a multicast if there's at least one neighbor with an empty
+ * queue on the interface.
+ */
+ if (pending < total) {
+ /*
+ * build a hello packet with a seq tlv indicating all the
+ * neighbors that have full queues.
+ */
+ if (pending > 0) {
+ flags |= EIGRP_HDR_FLAG_CR;
+ TAILQ_INIT(&seq_addr_list);
+
+ TAILQ_FOREACH(nbr, &ei->nbr_list, entry) {
+ if (TAILQ_EMPTY(&nbr->retrans_list))
+ continue;
+ if ((sa = calloc(1, sizeof(*sa))) == NULL)
+ fatal("rtp_send_mcast");
+ sa->af = eigrp->af;
+ memcpy(&sa->addr, &nbr->addr, sizeof(sa->addr));
+ TAILQ_INSERT_TAIL(&seq_addr_list, sa, entry);
+ }
+
+ send_hello(ei, &seq_addr_list, eigrp->seq_num);
+ seq_addr_list_clr(&seq_addr_list);
+ }
+ send_packet(ei, NULL, flags, buf);
+ }
+
+ /* schedule an unicast retransmission for each neighbor */
+ pbuf = rtp_buf_new(buf);
+ TAILQ_FOREACH(nbr, &ei->nbr_list, entry) {
+ pkt = rtp_packet_new(nbr, eigrp->seq_num, pbuf);
+ TAILQ_INSERT_TAIL(&nbr->retrans_list, pkt, entry);
+ }
+
+ rtp_seq_inc(eigrp);
+}
+
+void
+rtp_send(struct eigrp_iface *ei, struct nbr *nbr, struct ibuf *buf)
+{
+ if (nbr)
+ rtp_send_ucast(nbr, buf);
+ else
+ rtp_send_mcast(ei, buf);
+}
+
+void
+rtp_send_ack(struct nbr *nbr)
+{
+ struct eigrp *eigrp = nbr->ei->eigrp;
+ struct ibuf *buf;
+
+ if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
+ IP_MAXPACKET - sizeof(struct ip))) == NULL)
+ fatal("rtp_send_ack");
+
+ /* EIGRP header */
+ if (gen_eigrp_hdr(buf, EIGRP_OPC_HELLO, 0, 0, eigrp->as))
+ goto fail;
+
+ /* send unreliably */
+ send_packet(nbr->ei, nbr, 0, buf);
+ ibuf_free(buf);
+ return;
+fail:
+ log_warnx("%s: failed to send message", __func__);
+ ibuf_free(buf);
+}
+
+/* timers */
+
+/* ARGSUSED */
+void
+rtp_retrans_timer(int fd, short event, void *arg)
+{
+ struct packet *pkt = arg;
+ struct eigrp *eigrp = pkt->nbr->ei->eigrp;
+
+ pkt->attempts++;
+
+ if (pkt->attempts > RTP_RTRNS_MAX_ATTEMPTS) {
+ log_warnx("%s: retry limit exceeded, nbr %s", __func__,
+ log_addr(eigrp->af, &pkt->nbr->addr));
+ nbr_del(pkt->nbr);
+ return;
+ }
+
+ rtp_send_packet(pkt);
+}
+
+void
+rtp_retrans_start_timer(struct packet *pkt)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = RTP_RTRNS_INTERVAL;
+ if (evtimer_add(&pkt->ev_timeout, &tv) == -1)
+ fatal("rtp_retrans_start_timer");
+}
+
+void
+rtp_retrans_stop_timer(struct packet *pkt)
+{
+ if (evtimer_pending(&pkt->ev_timeout, NULL) &&
+ evtimer_del(&pkt->ev_timeout) == -1)
+ fatal("rtp_retrans_stop_timer");
+}
+
+/* ARGSUSED */
+void
+rtp_ack_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+
+ rtp_send_ack(nbr);
+}
+
+void
+rtp_ack_start_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_usec = RTP_ACK_TIMEOUT;
+ if (evtimer_add(&nbr->ev_ack, &tv) == -1)
+ fatal("rtp_ack_start_timer");
+}
+
+void
+rtp_ack_stop_timer(struct nbr *nbr)
+{
+ if (evtimer_pending(&nbr->ev_ack, NULL) &&
+ evtimer_del(&nbr->ev_ack) == -1)
+ fatal("rtp_ack_stop_timer");
+}
diff --git a/usr.sbin/eigrpd/tlv.c b/usr.sbin/eigrpd/tlv.c
new file mode 100644
index 00000000000..a871fcdd583
--- /dev/null
+++ b/usr.sbin/eigrpd/tlv.c
@@ -0,0 +1,480 @@
+/* $OpenBSD: tlv.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+int
+gen_parameter_tlv(struct ibuf *buf, struct eigrp_iface *ei)
+{
+ struct tlv_parameter tp;
+
+ tp.type = htons(TLV_TYPE_PARAMETER);
+ tp.length = htons(TLV_TYPE_PARAMETER_LEN);
+ memcpy(tp.kvalues, ei->eigrp->kvalues, 6);
+ tp.holdtime = htons(ei->hello_holdtime);
+
+ return (ibuf_add(buf, &tp, sizeof(tp)));
+}
+
+int
+gen_sequence_tlv(struct ibuf *buf, struct seq_addr_head *seq_addr_list)
+{
+ struct tlv tlv, *tlvp;
+ struct seq_addr_entry *sa;
+ uint8_t alen;
+ uint16_t len = TLV_HDR_LEN;
+ size_t original_size = ibuf_size(buf);
+
+ tlv.type = htons(TLV_TYPE_SEQ);
+ if (ibuf_add(buf, &tlv, sizeof(tlv))) {
+ log_warn("%s: ibuf_add failed", __func__);
+ return (-1);
+ }
+
+ TAILQ_FOREACH(sa, seq_addr_list, entry) {
+ switch (sa->af) {
+ case AF_INET:
+ alen = INADDRSZ;
+ if (ibuf_add(buf, &alen, sizeof(alen)))
+ return (-1);
+ if (ibuf_add(buf, &sa->addr.v4, sizeof(sa->addr.v4))) {
+ log_warn("%s: ibuf_add failed", __func__);
+ return (-1);
+ }
+ break;
+ case AF_INET6:
+ alen = IN6ADDRSZ;
+ if (ibuf_add(buf, &alen, sizeof(alen)))
+ return (-1);
+ if (ibuf_add(buf, &sa->addr.v6, sizeof(sa->addr.v6))) {
+ log_warn("%s: ibuf_add failed", __func__);
+ return (-1);
+ }
+ break;
+ default:
+ log_debug("%s: unkown address family", __func__);
+ return (-1);
+ }
+ len += (sizeof(alen) + alen);
+ }
+
+ /* adjust tlv length */
+ if ((tlvp = ibuf_seek(buf, original_size, sizeof(*tlvp))) == NULL)
+ fatalx("gen_sequence_tlv: buf_seek failed");
+ tlvp->length = htons(len);
+
+ return (0);
+}
+
+int
+gen_sw_version_tlv(struct ibuf *buf)
+{
+ struct tlv_sw_version ts;
+ struct utsname u;
+ unsigned int vendor_os_major;
+ unsigned int vendor_os_minor;
+
+ memset(&ts, 0, sizeof(ts));
+ ts.type = htons(TLV_TYPE_SW_VERSION);
+ ts.length = htons(TLV_TYPE_SW_VERSION_LEN);
+ if (uname(&u) == 0) {
+ if (sscanf(u.release, "%u.%u", &vendor_os_major,
+ &vendor_os_minor) == 2) {
+ ts.vendor_os_major = (uint8_t) vendor_os_major;
+ ts.vendor_os_minor = (uint8_t) vendor_os_minor;
+ }
+ }
+ ts.eigrp_major = EIGRP_VERSION_MAJOR;
+ ts.eigrp_minor = EIGRP_VERSION_MINOR;
+
+ return (ibuf_add(buf, &ts, sizeof(ts)));
+}
+
+int
+gen_mcast_seq_tlv(struct ibuf *buf, uint32_t seq)
+{
+ struct tlv_mcast_seq tm;
+
+ tm.type = htons(TLV_TYPE_MCAST_SEQ);
+ tm.length = htons(TLV_TYPE_MCAST_SEQ_LEN);
+ tm.seq = htonl(seq);
+
+ return (ibuf_add(buf, &tm, sizeof(tm)));
+}
+
+uint16_t
+len_route_tlv(struct rinfo *ri)
+{
+ uint16_t len = TLV_HDR_LEN;
+
+ switch (ri->af) {
+ case AF_INET:
+ len += sizeof(ri->nexthop.v4);
+ len += PREFIX_SIZE4(ri->prefixlen);
+ break;
+ case AF_INET6:
+ len += sizeof(ri->nexthop.v6);
+ len += PREFIX_SIZE6(ri->prefixlen);
+ break;
+ default:
+ break;
+ }
+
+ len += sizeof(ri->metric);
+ if (ri->type == EIGRP_ROUTE_EXTERNAL)
+ len += sizeof(ri->emetric);
+
+ len += sizeof(ri->prefixlen);
+
+ return (len);
+}
+
+int
+gen_route_tlv(struct ibuf *buf, struct rinfo *ri)
+{
+ struct tlv tlv, *tlvp;
+ struct in_addr addr;
+ struct classic_metric metric;
+ struct classic_emetric emetric;
+ uint16_t tlvlen;
+ uint8_t pflen;
+ size_t original_size = ibuf_size(buf);
+
+ switch (ri->af) {
+ case AF_INET:
+ switch (ri->type) {
+ case EIGRP_ROUTE_INTERNAL:
+ tlv.type = htons(TLV_TYPE_IPV4_INTERNAL);
+ break;
+ case EIGRP_ROUTE_EXTERNAL:
+ tlv.type = htons(TLV_TYPE_IPV4_EXTERNAL);
+ break;
+ }
+ break;
+ case AF_INET6:
+ switch (ri->type) {
+ case EIGRP_ROUTE_INTERNAL:
+ tlv.type = htons(TLV_TYPE_IPV6_INTERNAL);
+ break;
+ case EIGRP_ROUTE_EXTERNAL:
+ tlv.type = htons(TLV_TYPE_IPV6_EXTERNAL);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (ibuf_add(buf, &tlv, sizeof(tlv)))
+ return (-1);
+ tlvlen = TLV_HDR_LEN;
+
+ /* nexthop */
+ switch (ri->af) {
+ case AF_INET:
+ addr.s_addr = htonl(ri->nexthop.v4.s_addr);
+ if (ibuf_add(buf, &addr, sizeof(addr)))
+ return (-1);
+ tlvlen += sizeof(ri->nexthop.v4);
+ break;
+ case AF_INET6:
+ if (ibuf_add(buf, &ri->nexthop.v6, sizeof(ri->nexthop.v6)))
+ return (-1);
+ tlvlen += sizeof(ri->nexthop.v6);
+ break;
+ default:
+ break;
+ }
+
+ /* exterior metric */
+ if (ri->type == EIGRP_ROUTE_EXTERNAL) {
+ memcpy(&emetric, &ri->emetric, sizeof(emetric));
+ emetric.routerid = htonl(emetric.routerid);
+ emetric.as = htonl(emetric.as);
+ emetric.tag = htonl(emetric.tag);
+ emetric.metric = htonl(emetric.metric);
+ emetric.reserved = htons(emetric.reserved);
+ if (ibuf_add(buf, &emetric, sizeof(emetric)))
+ return (-1);
+ tlvlen += sizeof(emetric);
+ }
+
+ /* metric */
+ memcpy(&metric, &ri->metric, sizeof(metric));
+ metric.delay = htonl(metric.delay);
+ metric.bandwidth = htonl(metric.bandwidth);
+ if (ibuf_add(buf, &metric, sizeof(metric)))
+ return (-1);
+ tlvlen += sizeof(metric);
+
+ /* destination */
+ if (ibuf_add(buf, &ri->prefixlen, sizeof(ri->prefixlen)))
+ return (-1);
+ switch (ri->af) {
+ case AF_INET:
+ pflen = PREFIX_SIZE4(ri->prefixlen);
+ if (ibuf_add(buf, &ri->prefix.v4, pflen))
+ return (-1);
+ break;
+ case AF_INET6:
+ pflen = PREFIX_SIZE6(ri->prefixlen);
+ if (ibuf_add(buf, &ri->prefix.v6, pflen))
+ return (-1);
+ break;
+ default:
+ break;
+ }
+ tlvlen += sizeof(pflen) + pflen;
+
+ /* adjust tlv length */
+ if ((tlvp = ibuf_seek(buf, original_size, sizeof(*tlvp))) == NULL)
+ fatalx("gen_ipv4_internal_tlv: buf_seek failed");
+ tlvp->length = htons(tlvlen);
+
+ return (0);
+}
+
+struct tlv_parameter *
+tlv_decode_parameter(struct tlv *tlv, char *buf)
+{
+ struct tlv_parameter *tp;
+
+ if (ntohs(tlv->length) != TLV_TYPE_PARAMETER_LEN) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (NULL);
+ }
+ tp = (struct tlv_parameter *)buf;
+ return (tp);
+}
+
+int
+tlv_decode_seq(struct tlv *tlv, char *buf,
+ struct seq_addr_head *seq_addr_list)
+{
+ uint16_t len;
+ uint8_t alen;
+ struct seq_addr_entry *sa;
+
+ len = ntohs(tlv->length);
+ if (len < TLV_HDR_LEN) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (-1);
+ }
+ buf += TLV_HDR_LEN;
+ len -= TLV_HDR_LEN;
+
+ while (len > 0) {
+ memcpy(&alen, buf, sizeof(alen));
+ buf += sizeof(alen);
+ len -= sizeof(alen);
+ if (alen > len) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (-1);
+ }
+
+ if ((sa = calloc(1, sizeof(*sa))) == NULL)
+ fatal("tlv_decode_seq");
+ switch (alen) {
+ case INADDRSZ:
+ sa->af = AF_INET;
+ memcpy(&sa->addr.v4, buf, sizeof(struct in_addr));
+ break;
+ case IN6ADDRSZ:
+ sa->af = AF_INET6;
+ memcpy(&sa->addr.v6, buf, sizeof(struct in6_addr));
+ break;
+ default:
+ log_debug("%s: unknown address length", __func__);
+ free(sa);
+ return (-1);
+ }
+ buf += alen;
+ len -= alen;
+ TAILQ_INSERT_TAIL(seq_addr_list, sa, entry);
+ }
+
+ return (0);
+}
+
+struct tlv_sw_version *
+tlv_decode_sw_version(struct tlv *tlv, char *buf)
+{
+ struct tlv_sw_version *tv;
+
+ if (ntohs(tlv->length) != TLV_TYPE_SW_VERSION_LEN) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (NULL);
+ }
+ tv = (struct tlv_sw_version *)buf;
+ return (tv);
+}
+
+struct tlv_mcast_seq *
+tlv_decode_mcast_seq(struct tlv *tlv, char *buf)
+{
+ struct tlv_mcast_seq *tm;
+
+ if (ntohs(tlv->length) != TLV_TYPE_MCAST_SEQ_LEN) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (NULL);
+ }
+ tm = (struct tlv_mcast_seq *)buf;
+ return (tm);
+}
+
+int
+tlv_decode_route(int af, enum route_type type, struct tlv *tlv, char *buf,
+ struct rinfo *ri)
+{
+ int tlv_len, min_len, plen, offset;
+ in_addr_t ipv4;
+
+ tlv_len = ntohs(tlv->length);
+ switch (af) {
+ case AF_INET:
+ min_len = TLV_TYPE_IPV4_INT_MIN_LEN;
+ break;
+ case AF_INET6:
+ min_len = TLV_TYPE_IPV6_INT_MIN_LEN;
+ break;
+ default:
+ break;
+ }
+ if (type == EIGRP_ROUTE_EXTERNAL)
+ min_len += sizeof(struct classic_emetric);
+
+ if (tlv_len < min_len) {
+ log_debug("%s: malformed tlv (bad length)", __func__);
+ return (-1);
+ }
+
+ ri->af = af;
+ ri->type = type;
+
+ /* nexthop */
+ offset = TLV_HDR_LEN;
+ switch (af) {
+ case AF_INET:
+ memcpy(&ri->nexthop.v4, buf + offset, sizeof(ri->nexthop.v4));
+ offset += sizeof(ri->nexthop.v4);
+ break;
+ case AF_INET6:
+ memcpy(&ri->nexthop.v6, buf + offset, sizeof(ri->nexthop.v6));
+ offset += sizeof(ri->nexthop.v6);
+ default:
+ break;
+ }
+
+ /* exterior metric */
+ if (type == EIGRP_ROUTE_EXTERNAL) {
+ memcpy(&ri->emetric, buf + offset, sizeof(ri->emetric));
+ ri->emetric.routerid = ntohl(ri->emetric.routerid);
+ ri->emetric.as = ntohl(ri->emetric.as);
+ ri->emetric.tag = ntohl(ri->emetric.tag);
+ ri->emetric.metric = ntohl(ri->emetric.metric);
+ ri->emetric.reserved = ntohs(ri->emetric.reserved);
+ offset += sizeof(ri->emetric);
+ }
+
+ /* metric */
+ memcpy(&ri->metric, buf + offset, sizeof(ri->metric));
+ ri->metric.delay = ntohl(ri->metric.delay);
+ ri->metric.bandwidth = ntohl(ri->metric.bandwidth);
+ offset += sizeof(ri->metric);
+
+ /* prefixlen */
+ memcpy(&ri->prefixlen, buf + offset, sizeof(ri->prefixlen));
+ offset += sizeof(ri->prefixlen);
+
+ switch (af) {
+ case AF_INET:
+ plen = PREFIX_SIZE4(ri->prefixlen);
+ break;
+ case AF_INET6:
+ plen = PREFIX_SIZE6(ri->prefixlen);
+ break;
+ default:
+ break;
+ }
+
+ /* safety check */
+ if (plen != (tlv_len - min_len)) {
+ log_debug("%s: malformed tlv (invalid prefix length)",
+ __func__);
+ return (-1);
+ }
+
+ /* destination */
+ switch (af) {
+ case AF_INET:
+ memset(&ri->prefix.v4, 0, sizeof(ri->prefix.v4));
+ memcpy(&ri->prefix.v4, buf + offset, plen);
+
+ /* check if the network is valid */
+ ipv4 = ntohl(ri->prefix.v4.s_addr);
+ if (((ipv4 >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) ||
+ IN_MULTICAST(ipv4) || IN_BADCLASS(ipv4)) {
+ log_debug("%s: malformed tlv (invalid ipv4 prefix)",
+ __func__);
+ return (-1);
+ }
+ break;
+ case AF_INET6:
+ memset(&ri->prefix.v6, 0, sizeof(ri->prefix.v6));
+ memcpy(&ri->prefix.v6, buf + offset, plen);
+
+ /* check if the network is valid */
+ if (IN6_IS_ADDR_LOOPBACK(&ri->prefix.v6) ||
+ IN6_IS_ADDR_MULTICAST(&ri->prefix.v6)) {
+ log_debug("%s: malformed tlv (invalid ipv6 prefix)",
+ __func__);
+ return (-1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* just in case... */
+ eigrp_applymask(af, &ri->prefix, &ri->prefix, ri->prefixlen);
+
+ return (0);
+}
+
+void
+metric_encode_mtu(uint8_t *dst, int mtu)
+{
+ dst[0] = (mtu & 0x00FF0000) >> 16;
+ dst[1] = (mtu & 0x0000FF00) >> 8;
+ dst[2] = (mtu & 0x000000FF);
+}
+
+int
+metric_decode_mtu(uint8_t *mtu)
+{
+ return ((mtu[0] << 16) + (mtu[1] << 8) + mtu[2]);
+}
diff --git a/usr.sbin/eigrpd/update.c b/usr.sbin/eigrpd/update.c
new file mode 100644
index 00000000000..1b7b8b5d4a2
--- /dev/null
+++ b/usr.sbin/eigrpd/update.c
@@ -0,0 +1,145 @@
+/* $OpenBSD: update.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include "eigrpd.h"
+#include "eigrp.h"
+#include "log.h"
+#include "eigrpe.h"
+
+extern struct eigrpd_conf *econf;
+
+/* update packet handling */
+
+void
+send_update(struct eigrp_iface *ei, struct nbr *nbr, uint32_t flags,
+ int startup, struct rinfo_head *rinfo_list)
+{
+ struct eigrp *eigrp = ei->eigrp;
+ struct ibuf *buf;
+ struct rinfo_entry *re;
+ int size;
+ int route_len;
+ struct eigrp_hdr *eigrp_hdr;
+
+ /* don't exceed the interface's mtu */
+ do {
+ if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
+ IP_MAXPACKET - sizeof(struct ip))) == NULL)
+ fatal("send_update");
+
+ /* EIGRP header */
+ if (gen_eigrp_hdr(buf, EIGRP_OPC_UPDATE, flags,
+ eigrp->seq_num, eigrp->as))
+ goto fail;
+
+ if (rinfo_list == NULL)
+ break;
+
+ switch (eigrp->af) {
+ case AF_INET:
+ size = sizeof(struct ip);
+ break;
+ case AF_INET6:
+ size = sizeof(struct ip6_hdr);
+ break;
+ default:
+ break;
+ }
+ size += sizeof(struct eigrp_hdr);
+
+ while ((re = TAILQ_FIRST(rinfo_list)) != NULL) {
+ route_len = len_route_tlv(&re->rinfo);
+ if (size + route_len > ei->iface->mtu) {
+ rtp_send(ei, nbr, buf);
+ break;
+ }
+ size += route_len;
+
+ if (gen_route_tlv(buf, &re->rinfo))
+ goto fail;
+ TAILQ_REMOVE(rinfo_list, re, entry);
+ free(re);
+ }
+ } while (!TAILQ_EMPTY(rinfo_list));
+
+ /* set the EOT flag in the last startup update */
+ if (startup) {
+ if ((eigrp_hdr = ibuf_seek(buf, 0, sizeof(*eigrp_hdr))) == NULL)
+ fatalx("send_update: buf_seek failed");
+ eigrp_hdr->flags = ntohl(eigrp_hdr->flags) | EIGRP_HDR_FLAG_EOT;
+ eigrp_hdr->flags = htonl(eigrp_hdr->flags);
+ }
+
+ rtp_send(ei, nbr, buf);
+ return;
+fail:
+ log_warnx("%s: failed to send message", __func__);
+ if (rinfo_list)
+ message_list_clr(rinfo_list);
+ ibuf_free(buf);
+ return;
+}
+
+void
+recv_update(struct nbr *nbr, struct rinfo_head *rinfo_list, uint32_t flags)
+{
+ struct rinfo_entry *re;
+
+ rtp_ack_start_timer(nbr);
+
+ if (flags & EIGRP_HDR_FLAG_INIT) {
+ log_debug("%s: INIT flag is set", __func__);
+
+ if (nbr->flags & F_EIGRP_NBR_PENDING)
+ nbr_init(nbr);
+ else if (!(flags & EIGRP_HDR_FLAG_RS))
+ /*
+ * This is not in the draft, but apparently if a Cisco
+ * device sends an INIT Update it expects to receive
+ * an INIT Update as well, otherwise it triggers the
+ * "stuck in INIT state" error and discards subsequent
+ * packets. However, there is an exception: when the
+ * "clear ip eigrp neighbors soft" command is issued
+ * on a Cisco device, the "Restart Flag" is also set
+ * in the EIGRP header. In this case the Cisco device
+ * doesn't expect to receive an INIT Update otherwise
+ * the adjacency will flap. Unfortunately it looks
+ * like that there is some kind of initialization
+ * FSM implemented in the Cisco devices that is not
+ * documented in the draft.
+ */
+ send_update(nbr->ei, nbr, EIGRP_HDR_FLAG_INIT,
+ 0, NULL);
+
+ /*
+ * The INIT flag instructs us to advertise all of our routes,
+ * even if the neighbor is not pending.
+ */
+ eigrpe_imsg_compose_rde(IMSG_RECV_UPDATE_INIT, nbr->peerid,
+ 0, NULL, 0);
+ return;
+ }
+
+ TAILQ_FOREACH(re, rinfo_list, entry)
+ eigrpe_imsg_compose_rde(IMSG_RECV_UPDATE, nbr->peerid,
+ 0, &re->rinfo, sizeof(re->rinfo));
+}
diff --git a/usr.sbin/eigrpd/util.c b/usr.sbin/eigrpd/util.c
new file mode 100644
index 00000000000..90099c558b2
--- /dev/null
+++ b/usr.sbin/eigrpd/util.c
@@ -0,0 +1,238 @@
+/* $OpenBSD: util.c,v 1.1 2015/10/02 04:26:47 renato Exp $ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <netinet/in.h>
+#include <string.h>
+
+#include "eigrpd.h"
+#include "log.h"
+
+uint8_t
+mask2prefixlen(in_addr_t ina)
+{
+ if (ina == 0)
+ return (0);
+ else
+ return (33 - ffs(ntohl(ina)));
+}
+
+uint8_t
+mask2prefixlen6(struct sockaddr_in6 *sa_in6)
+{
+ uint8_t l = 0, *ap, *ep;
+
+ /*
+ * sin6_len is the size of the sockaddr so substract the offset of
+ * the possibly truncated sin6_addr struct.
+ */
+ ap = (uint8_t *)&sa_in6->sin6_addr;
+ ep = (uint8_t *)sa_in6 + sa_in6->sin6_len;
+ for (; ap < ep; ap++) {
+ /* this "beauty" is adopted from sbin/route/show.c ... */
+ switch (*ap) {
+ case 0xff:
+ l += 8;
+ break;
+ case 0xfe:
+ l += 7;
+ return (l);
+ case 0xfc:
+ l += 6;
+ return (l);
+ case 0xf8:
+ l += 5;
+ return (l);
+ case 0xf0:
+ l += 4;
+ return (l);
+ case 0xe0:
+ l += 3;
+ return (l);
+ case 0xc0:
+ l += 2;
+ return (l);
+ case 0x80:
+ l += 1;
+ return (l);
+ case 0x00:
+ return (l);
+ default:
+ fatalx("non contiguous inet6 netmask");
+ }
+ }
+
+ return (l);
+}
+
+in_addr_t
+prefixlen2mask(uint8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+struct in6_addr *
+prefixlen2mask6(uint8_t prefixlen)
+{
+ static struct in6_addr mask;
+ int i;
+
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ return (&mask);
+}
+
+void
+eigrp_applymask(int af, union eigrpd_addr *dest, const union eigrpd_addr *src,
+ int prefixlen)
+{
+ struct in6_addr mask;
+ int i;
+
+ switch (af) {
+ case AF_INET:
+ dest->v4.s_addr = src->v4.s_addr & prefixlen2mask(prefixlen);
+ break;
+ case AF_INET6:
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ for (i = 0; i < 16; i++)
+ dest->v6.s6_addr[i] = src->v6.s6_addr[i] &
+ mask.s6_addr[i];
+ break;
+ default:
+ break;
+ }
+}
+
+int
+eigrp_addrcmp(int af, union eigrpd_addr *a, union eigrpd_addr *b)
+{
+ switch (af) {
+ case AF_INET:
+ if (a->v4.s_addr != b->v4.s_addr)
+ return (1);
+ break;
+ case AF_INET6:
+ if (!IN6_ARE_ADDR_EQUAL(&a->v6, &b->v6))
+ return (1);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+int
+eigrp_addrisset(int af, union eigrpd_addr *addr)
+{
+ switch (af) {
+ case AF_INET:
+ if (addr->v4.s_addr != INADDR_ANY)
+ return (1);
+ break;
+ case AF_INET6:
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
+ return (1);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+#define IN6_IS_SCOPE_EMBED(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_INTFACELOCAL(a)))
+
+void
+embedscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ if (tmp16 != 0) {
+ log_warnx("%s: address %s already has embeded scope %u",
+ __func__, log_sockaddr(sin6), ntohs(tmp16));
+ }
+ tmp16 = htons(sin6->sin6_scope_id);
+ memcpy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
+ sin6->sin6_scope_id = 0;
+ }
+}
+
+void
+recoverscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (sin6->sin6_scope_id != 0) {
+ log_warnx("%s: address %s already has scope id %u",
+ __func__, log_sockaddr(sin6), sin6->sin6_scope_id);
+ }
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ sin6->sin6_scope_id = ntohs(tmp16);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+}
+
+void
+addscope(struct sockaddr_in6 *sin6, uint32_t id)
+{
+ if (sin6->sin6_scope_id != 0) {
+ log_warnx("%s: address %s already has scope id %u", __func__,
+ log_sockaddr(sin6), sin6->sin6_scope_id);
+ }
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id = id;
+ }
+}
+
+void
+clearscope(struct in6_addr *in6)
+{
+ if (IN6_IS_SCOPE_EMBED(in6)) {
+ in6->s6_addr[2] = 0;
+ in6->s6_addr[3] = 0;
+ }
+}
+
+#undef IN6_IS_SCOPE_EMBED