diff options
Diffstat (limited to 'usr.sbin/dvmrpd/rde_mfc.c')
-rw-r--r-- | usr.sbin/dvmrpd/rde_mfc.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/usr.sbin/dvmrpd/rde_mfc.c b/usr.sbin/dvmrpd/rde_mfc.c new file mode 100644 index 00000000000..b985360fa56 --- /dev/null +++ b/usr.sbin/dvmrpd/rde_mfc.c @@ -0,0 +1,218 @@ +/* $OpenBSD: rde_mfc.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2006 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/socket.h> +#include <sys/tree.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <stdlib.h> + +#include "igmp.h" +#include "dvmrp.h" +#include "dvmrpd.h" +#include "rde.h" +#include "log.h" +#include "dvmrpe.h" + +/* multicast forwarding cache */ + +void mfc_invalidate(void); +void mfc_expire_timer(int, short, void *); +int mfc_start_expire_timer(struct mfc_node *); +int mfc_compare(struct mfc_node *, struct mfc_node *); + +RB_HEAD(mfc_tree, mfc_node) mfc; +RB_PROTOTYPE(mfc_tree, mfc_node, entry, mfc_compare) +RB_GENERATE(mfc_tree, mfc_node, entry, mfc_compare) + +extern struct dvmrpd_conf *rdeconf; + +/* timers */ +void +mfc_expire_timer(int fd, short event, void *arg) +{ + struct mfc_node *mn = arg; + struct mfc nmfc; + + log_debug("mfc_expire_timer: group %s", inet_ntoa(mn->group)); + + /* remove route entry */ + nmfc.origin = mn->origin; + nmfc.group = mn->group; + rde_imsg_compose_parent(IMSG_MFC_DEL, 0, &nmfc, sizeof(nmfc)); + + event_del(&mn->expiration_timer); + mfc_remove(mn); +} + +int +mfc_start_expire_timer(struct mfc_node *mn) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = ROUTE_EXPIRATION_TIME; + return (evtimer_add(&mn->expiration_timer, &tv)); +} + +/* route table */ +void +mfc_init(void) +{ + RB_INIT(&mfc); +} + +int +mfc_compare(struct mfc_node *a, struct mfc_node *b) +{ + if (ntohl(a->origin.s_addr) < ntohl(b->origin.s_addr)) + return (-1); + if (ntohl(a->origin.s_addr) > ntohl(b->origin.s_addr)) + return (1); + if (ntohl(a->group.s_addr) < ntohl(b->group.s_addr)) + return (-1); + if (ntohl(a->group.s_addr) > ntohl(b->group.s_addr)) + return (1); + return (0); +} + +struct mfc_node * +mfc_find(in_addr_t origin, in_addr_t group) +{ + struct mfc_node s; + + s.origin.s_addr = origin; + s.group.s_addr = group; + + return (RB_FIND(mfc_tree, &mfc, &s)); +} + +int +mfc_insert(struct mfc_node *m) +{ + if (RB_INSERT(mfc_tree, &mfc, m) != NULL) { + log_warnx("mfc_insert failed for group %s", + inet_ntoa(m->group)); + free(m); + return (-1); + } + + return (0); +} + +int +mfc_remove(struct mfc_node *m) +{ + if (RB_REMOVE(mfc_tree, &mfc, m) == NULL) { + log_warnx("mfc_remove failed for group %s", + inet_ntoa(m->group)); + return (-1); + } + + free(m); + return (0); +} + +void +mfc_clear(void) +{ + struct mfc_node *m; + + while ((m = RB_MIN(mfc_tree, &mfc)) != NULL) + mfc_remove(m); +} + +void +mfc_dump(pid_t pid) +{ + static struct ctl_mfc mfcctl; + struct timespec now; + struct timeval tv, now2, res; + struct mfc_node *mn; + int i; + + clock_gettime(CLOCK_MONOTONIC, &now); + + RB_FOREACH(mn, mfc_tree, &mfc) { + mfcctl.origin.s_addr = mn->origin.s_addr; + mfcctl.group.s_addr = mn->group.s_addr; + mfcctl.uptime = now.tv_sec - mn->uptime; + mfcctl.ifindex = mn->ifindex; + + for (i = 0; i < MAXVIFS; i ++) { + mfcctl.ttls[i] = mn->ttls[i]; + } + + gettimeofday(&now2, NULL); + if (evtimer_pending(&mn->expiration_timer, &tv)) { + timersub(&tv, &now2, &res); + mfcctl.expire = res.tv_sec; + } else + mfcctl.expire = -1; + + rde_imsg_compose_dvmrpe(IMSG_CTL_SHOW_MFC, 0, pid, &mfcctl, + sizeof(mfcctl)); + } +} + +void +mfc_update(struct mfc *nmfc) +{ + struct timespec now; + struct mfc_node *mn; + int i; + + clock_gettime(CLOCK_MONOTONIC, &now); + + if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL) { + if ((mn = calloc(1, sizeof(struct mfc_node))) == NULL) + fatalx("mfc_update"); + + mn->origin.s_addr = nmfc->origin.s_addr; + mn->group.s_addr = nmfc->group.s_addr; + mn->ifindex = nmfc->ifindex; + mn->uptime = now.tv_sec; + for (i = 0; i < MAXVIFS; i++) + mn->ttls[i] = nmfc->ttls[i]; + + if (mfc_insert(mn) == 0) { + if (nmfc->origin.s_addr != 0) + rde_imsg_compose_parent(IMSG_MFC_ADD, 0, nmfc, + sizeof(*nmfc)); + } + + evtimer_set(&mn->expiration_timer, mfc_expire_timer, mn); + mfc_start_expire_timer(mn); + } +} + +void +mfc_delete(struct mfc *nmfc) +{ + struct mfc_node *mn; + + if ((mn = mfc_find(nmfc->origin.s_addr, nmfc->group.s_addr)) == NULL) + return; + + /* XXX decide if it should really be removed */ + mfc_remove(mn); + + /* XXX notify parent */ +} |