summaryrefslogtreecommitdiff
path: root/usr.sbin/ldpd/interface.c
diff options
context:
space:
mode:
authorMichele Marchetto <michele@cvs.openbsd.org>2009-06-01 20:59:46 +0000
committerMichele Marchetto <michele@cvs.openbsd.org>2009-06-01 20:59:46 +0000
commit5de1d23aeb3294b99aea577259871a4a7686e9b4 (patch)
treecc434b947408b0844b7b33a08358359751c9a7e4 /usr.sbin/ldpd/interface.c
parent19268a0235511684a7ee9e71b0c3dd8170ff3762 (diff)
Welcome ldpd, the Label Distribution Protocol daemon.
Built using the imsg/three process framework, its main aim is to redistribute MPLS labels between peers. Right now it has some really basic functionalities, the basic protocol works and peers are able to exchange labels and insert them in the kernel. It still does not react to changes of topology. Not yet connected to the builds. ok claudio@ deraadt@
Diffstat (limited to 'usr.sbin/ldpd/interface.c')
-rw-r--r--usr.sbin/ldpd/interface.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/usr.sbin/ldpd/interface.c b/usr.sbin/ldpd/interface.c
new file mode 100644
index 00000000000..b921a5d675a
--- /dev/null
+++ b/usr.sbin/ldpd/interface.c
@@ -0,0 +1,603 @@
+/* $OpenBSD: interface.c,v 1.1 2009/06/01 20:59:45 michele Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005, 2008 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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "ldpd.h"
+#include "ldp.h"
+#include "log.h"
+#include "ldpe.h"
+
+void if_hello_timer(int, short, void *);
+void if_start_hello_timer(struct iface *);
+void if_stop_hello_timer(struct iface *);
+struct nbr *if_elect(struct nbr *, struct nbr *);
+
+struct {
+ int state;
+ enum iface_event event;
+ enum iface_action action;
+ int new_state;
+} iface_fsm[] = {
+ /* current state event that happened action to take resulting state */
+ {IF_STA_DOWN, IF_EVT_UP, IF_ACT_STRT, IF_STA_ACTIVE},
+ {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN},
+ {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0},
+};
+
+static int vlink_cnt = 0;
+
+const char * const if_event_names[] = {
+ "NOTHING",
+ "UP",
+ "DOWN"
+};
+
+const char * const if_action_names[] = {
+ "NOTHING",
+ "START",
+ "RESET"
+};
+
+int
+if_fsm(struct iface *iface, enum iface_event event)
+{
+ int old_state;
+ int new_state = 0;
+ int i, ret = 0;
+
+ old_state = iface->state;
+
+ for (i = 0; iface_fsm[i].state != -1; i++)
+ if ((iface_fsm[i].state & old_state) &&
+ (iface_fsm[i].event == event)) {
+ new_state = iface_fsm[i].new_state;
+ break;
+ }
+
+ if (iface_fsm[i].state == -1) {
+ /* event outside of the defined fsm, ignore it. */
+ log_debug("if_fsm: interface %s, "
+ "event %s not expected in state %s", iface->name,
+ if_event_names[event], if_state_name(old_state));
+ return (0);
+ }
+
+ switch (iface_fsm[i].action) {
+ case IF_ACT_STRT:
+ ret = if_act_start(iface);
+ break;
+ case IF_ACT_RST:
+ ret = if_act_reset(iface);
+ break;
+ case IF_ACT_NOTHING:
+ /* do nothing */
+ break;
+ }
+
+ if (ret) {
+ log_debug("if_fsm: error changing state for interface %s, "
+ "event %s, state %s", iface->name, if_event_names[event],
+ if_state_name(old_state));
+ return (-1);
+ }
+
+ if (new_state != 0)
+ iface->state = new_state;
+
+ log_debug("if_fsm: event %s resulted in action %s and changing "
+ "state for interface %s from %s to %s",
+ if_event_names[event], if_action_names[iface_fsm[i].action],
+ iface->name, if_state_name(old_state), if_state_name(iface->state));
+
+ return (ret);
+}
+
+struct iface *
+if_new(struct kif *kif, struct kif_addr *ka)
+{
+ struct iface *iface;
+
+ if ((iface = calloc(1, sizeof(*iface))) == NULL)
+ err(1, "if_new: calloc");
+
+ iface->state = IF_STA_DOWN;
+
+ LIST_INIT(&iface->nbr_list);
+ LIST_INIT(&iface->lde_nbr_list);
+
+ if (kif == NULL) {
+ iface->type = IF_TYPE_VIRTUALLINK;
+ snprintf(iface->name, sizeof(iface->name), "vlink%d",
+ vlink_cnt++);
+ iface->flags |= IFF_UP;
+ iface->mtu = IP_MSS;
+ return (iface);
+ }
+
+ 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;
+ iface->state = IF_STA_LOOPBACK;
+ }
+
+ /* get mtu, index and flags */
+ iface->mtu = kif->mtu;
+ iface->ifindex = kif->ifindex;
+ iface->flags = kif->flags;
+ iface->linkstate = kif->link_state;
+ iface->media_type = kif->media_type;
+ iface->baudrate = kif->baudrate;
+
+ /* set address, mask and p2p addr */
+ iface->addr = ka->addr;
+ iface->mask = ka->mask;
+ if (kif->flags & IFF_POINTOPOINT) {
+ iface->dst = ka->dstbrd;
+ }
+
+ return (iface);
+}
+
+void
+if_del(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+
+ log_debug("if_del: interface %s", iface->name);
+
+ /* clear lists etc */
+ while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL)
+ nbr_del(nbr);
+
+ if (evtimer_pending(&iface->hello_timer, NULL))
+ evtimer_del(&iface->hello_timer);
+
+ free(iface);
+}
+
+void
+if_init(struct ldpd_conf *xconf, struct iface *iface)
+{
+ /* init the dummy local neighbor */
+ iface->self = nbr_new(ldpe_router_id(), iface->ifindex, iface, 1);
+
+ /* set event handlers for interface */
+ evtimer_set(&iface->hello_timer, if_hello_timer, iface);
+
+ iface->discovery_fd = xconf->ldp_discovery_socket;
+}
+
+/* timers */
+/* ARGSUSED */
+void
+if_hello_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+ struct timeval tv;
+
+ send_hello(iface);
+
+ /* reschedule hello_timer */
+ timerclear(&tv);
+ tv.tv_sec = iface->hello_interval;
+ if (evtimer_add(&iface->hello_timer, &tv) == -1)
+ fatal("if_hello_timer");
+}
+
+void
+if_start_hello_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = iface->hello_interval;
+ if (evtimer_add(&iface->hello_timer, &tv) == -1)
+ fatal("if_start_hello_timer");
+}
+
+void
+if_stop_hello_timer(struct iface *iface)
+{
+ if (evtimer_del(&iface->hello_timer) == -1)
+ fatal("if_stop_hello_timer");
+}
+
+/* actions */
+int
+if_act_start(struct iface *iface)
+{
+ struct in_addr addr;
+ struct timeval now;
+
+ if (!((iface->flags & IFF_UP) &&
+ (LINK_STATE_IS_UP(iface->linkstate) ||
+ (iface->linkstate == LINK_STATE_UNKNOWN &&
+ iface->media_type != IFT_CARP)))) {
+ log_debug("if_act_start: interface %s link down",
+ iface->name);
+ return (0);
+ }
+
+ if (iface->media_type == IFT_CARP && iface->passive == 0) {
+ /* force passive mode on carp interfaces */
+ log_warnx("if_act_start: forcing interface %s to passive",
+ iface->name);
+ iface->passive = 1;
+ }
+
+ gettimeofday(&now, NULL);
+ iface->uptime = now.tv_sec;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllRouters, &addr);
+ if (if_join_group(iface, &addr))
+ return (-1);
+ iface->state = IF_STA_POINTTOPOINT;
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ iface->state = IF_STA_POINTTOPOINT;
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_NBMA:
+ log_debug("if_act_start: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllRouters, &addr);
+ if (if_join_group(iface, &addr))
+ return (-1);
+ iface->state = IF_STA_DOWN;
+ break;
+ default:
+ fatalx("if_act_start: unknown interface type");
+ }
+
+ /* hello timer needs to be started in any case */
+ if_start_hello_timer(iface);
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+/* struct nbr *nbr = NULL; */
+ struct in_addr addr;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_aton(AllRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", inet_ntoa(addr), iface->name);
+ }
+ break;
+ case IF_TYPE_VIRTUALLINK:
+ /* nothing */
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ log_debug("if_act_reset: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_act_reset: unknown interface type");
+ }
+/*
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
+ log_debug("if_act_reset: error killing neighbor %s",
+ inet_ntoa(nbr->id));
+ }
+ }
+*/
+ return (0);
+}
+
+struct ctl_iface *
+if_to_ctl(struct iface *iface)
+{
+ static struct ctl_iface ictl;
+ struct timeval tv, now, res;
+ struct nbr *nbr;
+
+ memcpy(ictl.name, iface->name, sizeof(ictl.name));
+ memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
+ memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
+ ictl.rtr_id.s_addr = ldpe_router_id();
+ ictl.ifindex = iface->ifindex;
+ ictl.state = iface->state;
+ ictl.mtu = iface->mtu;
+ ictl.nbr_cnt = 0;
+ ictl.adj_cnt = 0;
+ ictl.baudrate = iface->baudrate;
+ ictl.holdtime = iface->holdtime;
+ ictl.hello_interval = iface->hello_interval;
+ ictl.flags = iface->flags;
+ ictl.type = iface->type;
+ ictl.linkstate = iface->linkstate;
+ ictl.mediatype = iface->media_type;
+ ictl.priority = iface->priority;
+ ictl.passive = iface->passive;
+
+ gettimeofday(&now, NULL);
+ if (evtimer_pending(&iface->hello_timer, &tv)) {
+ timersub(&tv, &now, &res);
+ ictl.hello_timer = res.tv_sec;
+ } else
+ ictl.hello_timer = -1;
+
+ if (iface->state != IF_STA_DOWN) {
+ ictl.uptime = now.tv_sec - iface->uptime;
+ } else
+ ictl.uptime = 0;
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr == iface->self)
+ continue;
+ ictl.nbr_cnt++;
+ }
+
+ return (&ictl);
+}
+
+/* misc */
+int
+if_set_mcast_ttl(int fd, u_int8_t ttl)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0) {
+ log_warn("if_set_mcast_ttl: error setting "
+ "IP_MULTICAST_TTL to %d", ttl);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_tos(int fd, int tos)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) {
+ log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_recvif(int fd, int enable)
+{
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("if_set_recvif: error setting IP_RECVIF");
+ return (-1);
+ }
+ return (0);
+}
+
+void
+if_set_recvbuf(int fd)
+{
+ int bsize;
+
+ bsize = 65535;
+ while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
+ sizeof(bsize)) == -1)
+ bsize /= 2;
+}
+
+int
+if_set_reuse(int fd, int enable)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable,
+ sizeof(int)) < 0) {
+ log_warn("if_set_reuse: error setting SO_REUSEPORT");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_nonblock(int fd)
+{
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ log_warn("if_set_nonblock: error setting O_NONBLOCK");
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * only one JOIN or DROP per interface and address is allowed so we need
+ * to keep track of what is added and removed.
+ */
+struct if_group_count {
+ LIST_ENTRY(if_group_count) entry;
+ struct in_addr addr;
+ unsigned int ifindex;
+ int count;
+};
+
+LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
+
+int
+if_join_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+ struct if_group_count *ifg;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ LIST_FOREACH(ifg, &ifglist, entry)
+ if (iface->ifindex == ifg->ifindex &&
+ addr->s_addr == ifg->addr.s_addr)
+ break;
+ if (ifg == NULL) {
+ if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
+ fatal("if_join_group");
+ ifg->addr.s_addr = addr->s_addr;
+ ifg->ifindex = iface->ifindex;
+ LIST_INSERT_HEAD(&ifglist, ifg, entry);
+ }
+
+ if (ifg->count++ != 0)
+ /* already joined */
+ return (0);
+
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->discovery_fd, IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
+ "interface %s address %s", iface->name,
+ inet_ntoa(*addr));
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_join_group: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_join_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_leave_group(struct iface *iface, struct in_addr *addr)
+{
+ struct ip_mreq mreq;
+ struct if_group_count *ifg;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ LIST_FOREACH(ifg, &ifglist, entry)
+ if (iface->ifindex == ifg->ifindex &&
+ addr->s_addr == ifg->addr.s_addr)
+ break;
+
+ /* if interface is not found just try to drop membership */
+ if (ifg && --ifg->count != 0)
+ /* others still joined */
+ return (0);
+
+ mreq.imr_multiaddr.s_addr = addr->s_addr;
+ mreq.imr_interface.s_addr = iface->addr.s_addr;
+
+ if (setsockopt(iface->discovery_fd, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
+ "interface %s address %s", iface->name,
+ inet_ntoa(*addr));
+ return (-1);
+ }
+
+ if (ifg) {
+ LIST_REMOVE(ifg, entry);
+ free(ifg);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_leave_group: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_leave_group: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_set_mcast(struct iface *iface)
+{
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ if (setsockopt(iface->discovery_fd, IPPROTO_IP, IP_MULTICAST_IF,
+ &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) {
+ log_debug("if_set_mcast: error setting "
+ "IP_MULTICAST_IF, interface %s", iface->name);
+ return (-1);
+ }
+ break;
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ case IF_TYPE_NBMA:
+ log_debug("if_set_mcast: type %s not supported, interface %s",
+ if_type_name(iface->type), iface->name);
+ return (-1);
+ default:
+ fatalx("if_set_mcast: unknown interface type");
+ }
+
+ return (0);
+}
+
+int
+if_set_mcast_loop(int fd)
+{
+ u_int8_t loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0) {
+ log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
+ return (-1);
+ }
+
+ return (0);
+}