summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ospf6d/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ospf6d/interface.c')
-rw-r--r--usr.sbin/ospf6d/interface.c803
1 files changed, 803 insertions, 0 deletions
diff --git a/usr.sbin/ospf6d/interface.c b/usr.sbin/ospf6d/interface.c
new file mode 100644
index 00000000000..be415114742
--- /dev/null
+++ b/usr.sbin/ospf6d/interface.c
@@ -0,0 +1,803 @@
+/* $OpenBSD: interface.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@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.
+ */
+
+#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 <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <event.h>
+
+#include "ospf6d.h"
+#include "ospf6.h"
+#include "log.h"
+#include "ospfe.h"
+
+void if_hello_timer(int, short, void *);
+void if_start_hello_timer(struct iface *);
+void if_stop_hello_timer(struct iface *);
+void if_stop_wait_timer(struct iface *);
+void if_wait_timer(int, short, void *);
+void if_start_wait_timer(struct iface *);
+void if_stop_wait_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, 0},
+ {IF_STA_WAITING, IF_EVT_BACKUP_SEEN, IF_ACT_ELECT, 0},
+ {IF_STA_WAITING, IF_EVT_WTIMER, IF_ACT_ELECT, 0},
+ {IF_STA_ANY, IF_EVT_WTIMER, IF_ACT_NOTHING, 0},
+ {IF_STA_WAITING, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0},
+ {IF_STA_MULTI, IF_EVT_NBR_CHNG, IF_ACT_ELECT, 0},
+ {IF_STA_ANY, IF_EVT_NBR_CHNG, IF_ACT_NOTHING, 0},
+ {IF_STA_ANY, IF_EVT_DOWN, IF_ACT_RST, IF_STA_DOWN},
+ {IF_STA_ANY, IF_EVT_LOOP, IF_ACT_RST, IF_STA_LOOPBACK},
+ {IF_STA_LOOPBACK, IF_EVT_UNLOOP, IF_ACT_NOTHING, IF_STA_DOWN},
+ {-1, IF_EVT_NOTHING, IF_ACT_NOTHING, 0},
+};
+
+static int vlink_cnt = 0;
+
+const char * const if_event_names[] = {
+ "NOTHING",
+ "UP",
+ "WAITTIMER",
+ "BACKUPSEEN",
+ "NEIGHBORCHANGE",
+ "LOOP",
+ "UNLOOP",
+ "DOWN"
+};
+
+const char * const if_action_names[] = {
+ "NOTHING",
+ "START",
+ "ELECT",
+ "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_ELECT:
+ ret = if_act_elect(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;
+
+ if (iface->state != old_state)
+ orig_rtr_lsa(iface->area);
+
+ if (old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT) &&
+ (iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
+ ospfe_demote_iface(iface, 0);
+ if ((old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0 &&
+ iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT))
+ ospfe_demote_iface(iface, 1);
+
+ 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);
+ TAILQ_INIT(&iface->ls_ack_list);
+
+ iface->crypt_seq_num = arc4random() & 0x0fffffff;
+
+ 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);
+
+ /* revert the demotion when the interface is deleted */
+ if ((iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
+ ospfe_demote_iface(iface, 1);
+
+ /* 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);
+ if (evtimer_pending(&iface->wait_timer, NULL))
+ evtimer_del(&iface->wait_timer);
+ if (evtimer_pending(&iface->lsack_tx_timer, NULL))
+ evtimer_del(&iface->lsack_tx_timer);
+
+ ls_ack_list_clr(iface);
+ free(iface);
+}
+
+void
+if_init(struct ospfd_conf *xconf, struct iface *iface)
+{
+ /* init the dummy local neighbor */
+ iface->self = nbr_new(ospfe_router_id(), iface, 1);
+
+ /* set event handlers for interface */
+ evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
+ evtimer_set(&iface->hello_timer, if_hello_timer, iface);
+ evtimer_set(&iface->wait_timer, if_wait_timer, iface);
+
+ iface->fd = xconf->ospf_socket;
+
+ ospfe_demote_iface(iface, 0);
+}
+
+/* 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);
+ 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");
+}
+
+/* ARGSUSED */
+void
+if_wait_timer(int fd, short event, void *arg)
+{
+ struct iface *iface = arg;
+
+ if_fsm(iface, IF_EVT_WTIMER);
+}
+
+void
+if_start_wait_timer(struct iface *iface)
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = iface->dead_interval;
+ if (evtimer_add(&iface->wait_timer, &tv) == -1)
+ fatal("if_start_wait_timer");
+}
+
+void
+if_stop_wait_timer(struct iface *iface)
+{
+ if (evtimer_del(&iface->wait_timer) == -1)
+ fatal("if_stop_wait_timer");
+}
+
+/* actions */
+int
+if_act_start(struct iface *iface)
+{
+ struct in6_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;
+ }
+
+ if (iface->passive) {
+ /* for an update of stub network entries */
+ orig_rtr_lsa(iface->area);
+ return (0);
+ }
+
+ gettimeofday(&now, NULL);
+ iface->uptime = now.tv_sec;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_pton(AF_INET6, AllSPFRouters, &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_pton(AF_INET6, AllSPFRouters, &addr);
+
+ if (if_join_group(iface, &addr))
+ return (-1);
+ if (iface->priority == 0) {
+ iface->state = IF_STA_DROTHER;
+ } else {
+ iface->state = IF_STA_WAITING;
+ if_start_wait_timer(iface);
+ }
+ 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);
+}
+
+struct nbr *
+if_elect(struct nbr *a, struct nbr *b)
+{
+ if (a->priority > b->priority)
+ return (a);
+ if (a->priority < b->priority)
+ return (b);
+ if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr))
+ return (a);
+ return (b);
+}
+
+int
+if_act_elect(struct iface *iface)
+{
+ struct in6_addr addr;
+ struct nbr *nbr, *bdr = NULL, *dr = NULL;
+ int round = 0;
+ int changed = 0;
+ int old_state;
+ char b1[16], b2[16], b3[16], b4[16];
+
+start:
+ /* elect backup designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || nbr == dr || /* not electable */
+ nbr->state & NBR_STA_PRELIM || /* not available */
+ nbr->dr.s_addr == nbr->id.s_addr) /* don't elect DR */
+ continue;
+ if (bdr != NULL) {
+ /*
+ * routers announcing themselves as BDR have higher
+ * precedence over those routers announcing a
+ * different BDR.
+ */
+ if (nbr->bdr.s_addr == nbr->id.s_addr) {
+ if (bdr->bdr.s_addr == bdr->id.s_addr)
+ bdr = if_elect(bdr, nbr);
+ else
+ bdr = nbr;
+ } else if (bdr->bdr.s_addr != bdr->id.s_addr)
+ bdr = if_elect(bdr, nbr);
+ } else
+ bdr = nbr;
+ }
+
+ /* elect designated router */
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM ||
+ (nbr != dr && nbr->dr.s_addr != nbr->id.s_addr))
+ /* only DR may be elected check priority too */
+ continue;
+ if (dr == NULL)
+ dr = nbr;
+ else
+ dr = if_elect(dr, nbr);
+ }
+
+ if (dr == NULL) {
+ /* no designate router found use backup DR */
+ dr = bdr;
+ bdr = NULL;
+ }
+
+ /*
+ * if we are involved in the election (e.g. new DR or no
+ * longer BDR) redo the election
+ */
+ if (round == 0 &&
+ ((iface->self == dr && iface->self != iface->dr) ||
+ (iface->self != dr && iface->self == iface->dr) ||
+ (iface->self == bdr && iface->self != iface->bdr) ||
+ (iface->self != bdr && iface->self == iface->bdr))) {
+ /*
+ * Reset announced DR/BDR to calculated one, so
+ * that we may get elected in the second round.
+ * This is needed to drop from a DR to a BDR.
+ */
+ iface->self->dr.s_addr = dr->id.s_addr;
+ if (bdr)
+ iface->self->bdr.s_addr = bdr->id.s_addr;
+ round = 1;
+ goto start;
+ }
+
+ log_debug("if_act_elect: interface %s old dr %s new dr %s, "
+ "old bdr %s new bdr %s", iface->name,
+ iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) :
+ "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none",
+ iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) :
+ "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) :
+ "none");
+
+ /*
+ * After the second round still DR or BDR change state to DR or BDR,
+ * etc.
+ */
+ old_state = iface->state;
+ if (dr == iface->self)
+ iface->state = IF_STA_DR;
+ else if (bdr == iface->self)
+ iface->state = IF_STA_BACKUP;
+ else
+ iface->state = IF_STA_DROTHER;
+
+ /* TODO if iface is NBMA send all non eligible neighbors event Start */
+
+ /*
+ * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way
+ */
+ if (iface->dr != dr || iface->bdr != bdr)
+ changed = 1;
+
+ iface->dr = dr;
+ iface->bdr = bdr;
+
+ if (changed) {
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (old_state & IF_STA_DRORBDR &&
+ (iface->state & IF_STA_DRORBDR) == 0) {
+ if (if_leave_group(iface, &addr))
+ return (-1);
+ } else if ((old_state & IF_STA_DRORBDR) == 0 &&
+ iface->state & IF_STA_DRORBDR) {
+ if (if_join_group(iface, &addr))
+ return (-1);
+ }
+
+ LIST_FOREACH(nbr, &iface->nbr_list, entry) {
+ if (nbr->state & NBR_STA_BIDIR)
+ nbr_fsm(nbr, NBR_EVT_ADJ_OK);
+ }
+
+ orig_rtr_lsa(iface->area);
+ if (iface->state & IF_STA_DR || old_state & IF_STA_DR)
+ orig_net_lsa(iface);
+ }
+
+ if_start_hello_timer(iface);
+ return (0);
+}
+
+int
+if_act_reset(struct iface *iface)
+{
+ struct nbr *nbr = NULL;
+ struct in6_addr addr;
+
+ if (iface->passive) {
+ /* for an update of stub network entries */
+ orig_rtr_lsa(iface->area);
+ return (0);
+ }
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ inet_pton(AF_INET6, AllSPFRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: error leaving group %s, "
+ "interface %s", log_in6addr(&addr), iface->name);
+ }
+ if (iface->state & IF_STA_DRORBDR) {
+ inet_pton(AF_INET6, AllDRouters, &addr);
+ if (if_leave_group(iface, &addr)) {
+ log_warnx("if_act_reset: "
+ "error leaving group %s, interface %s",
+ log_in6addr(&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));
+ }
+ }
+
+ iface->dr = NULL;
+ iface->bdr = NULL;
+
+ ls_ack_list_clr(iface);
+ stop_ls_ack_tx_timer(iface);
+ if_stop_hello_timer(iface);
+ if_stop_wait_timer(iface);
+
+ /* send empty hello to tell everybody that we are going down */
+ send_hello(iface);
+
+ 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 = ospfe_router_id();
+ memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area));
+ if (iface->dr) {
+ memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id));
+ memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr));
+ } else {
+ bzero(&ictl.dr_id, sizeof(ictl.dr_id));
+ bzero(&ictl.dr_addr, sizeof(ictl.dr_addr));
+ }
+ if (iface->bdr) {
+ memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id));
+ memcpy(&ictl.bdr_addr, &iface->bdr->addr,
+ sizeof(ictl.bdr_addr));
+ } else {
+ bzero(&ictl.bdr_id, sizeof(ictl.bdr_id));
+ bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr));
+ }
+ 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.dead_interval = iface->dead_interval;
+ ictl.transmit_delay = iface->transmit_delay;
+ ictl.hello_interval = iface->hello_interval;
+ ictl.flags = iface->flags;
+ ictl.metric = iface->metric;
+ ictl.rxmt_interval = iface->rxmt_interval;
+ 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++;
+ if (nbr->state & NBR_STA_ADJFORM)
+ ictl.adj_cnt++;
+ }
+
+ return (&ictl);
+}
+
+/* misc */
+int
+if_set_recvif(int fd, int enable)
+{
+#if 0
+ if (setsockopt(fd, IPPROTO_IPV6, IP_RECVIF, &enable,
+ sizeof(enable)) < 0) {
+ log_warn("if_set_recvif: error setting IP_RECVIF");
+ return (-1);
+ }
+#endif
+ 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_join_group(struct iface *iface, struct in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ log_debug("if_join_group: interface %s addr %s",
+ iface->name, log_in6addr(addr));
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ log_warn("if_join_group: error IPV6_JOIN_GROUP, "
+ "interface %s address %s", iface->name,
+ log_in6addr(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 in6_addr *addr)
+{
+ struct ipv6_mreq mreq;
+
+ switch (iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ case IF_TYPE_BROADCAST:
+ log_debug("if_leave_group: interface %s addr %s",
+ iface->name, log_in6addr(addr));
+ mreq.ipv6mr_multiaddr = *addr;
+ mreq.ipv6mr_interface = iface->ifindex;
+
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (void *)&mreq, sizeof(mreq)) < 0) {
+ log_warn("if_leave_group: error IPV6_LEAVE_GROUP, "
+ "interface %s address %s", iface->name,
+ log_in6addr(addr));
+ return (-1);
+ }
+ 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:
+ log_debug("if_set_mcast: iface %s", iface->name);
+ if (setsockopt(iface->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &iface->ifindex, sizeof(iface->ifindex)) < 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_int loop = 0;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ (u_int *)&loop, sizeof(loop)) < 0) {
+ log_warn("if_set_mcast_loop: error setting "
+ "IPV6_MULTICAST_LOOP");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+if_set_ip_hdrincl(int fd)
+{
+#if 0
+ int hincl = 1;
+
+ if (setsockopt(fd, IPPROTO_IPV6, IP_HDRINCL, &hincl,
+ sizeof(hincl)) < 0) {
+ log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL");
+ return (-1);
+ }
+#endif
+ return (0);
+}