/* $OpenBSD: rde_mfc.c,v 1.3 2009/02/03 16:21:19 michele Exp $ */ /* * Copyright (c) 2006 Esben Norby * * 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 #include #include #include #include #include #include #include "igmp.h" #include "dvmrp.h" #include "dvmrpd.h" #include "log.h" #include "dvmrpe.h" #include "rde.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_reset_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_reset_expire_timer(struct mfc_node *mn) { struct timeval tv; timerclear(&tv); tv.tv_sec = ROUTE_EXPIRATION_TIME; return (evtimer_add(&mn->expiration_timer, &tv)); } int mfc_start_expire_timer(struct mfc_node *mn) { struct timeval tv; log_debug("mfc_start_expire_timer: group %s", inet_ntoa(mn->group)); 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_source(struct rt_node *rn) { struct mfc_node *mn; struct mfc m; int i; RB_FOREACH(mn, mfc_tree, &mfc) { if (rn->prefix.s_addr == mn->origin.s_addr) { mn->ifindex = rn->ifindex; for (i = 0; i < MAXVIFS; i++) mn->ttls[i] = rn->ttls[i]; m.origin.s_addr = mn->origin.s_addr; m.group.s_addr = mn->group.s_addr; m.ifindex = mn->ifindex; for (i = 0; i < MAXVIFS; i++) m.ttls[i] = mn->ttls[i]; if (mn->origin.s_addr != 0) rde_imsg_compose_parent(IMSG_MFC_ADD, 0, &m, sizeof(m)); mfc_reset_expire_timer(mn); } } } 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 */ }