File: [local] / src / usr.sbin / dvmrpd / group.c (download)
Revision 1.4, Tue Nov 18 20:54:28 2014 UTC (9 years, 6 months ago) by krw
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, OPENBSD_6_1_BASE, OPENBSD_6_1, OPENBSD_6_0_BASE, OPENBSD_6_0, OPENBSD_5_9_BASE, OPENBSD_5_9, OPENBSD_5_8_BASE, OPENBSD_5_8, OPENBSD_5_7_BASE, OPENBSD_5_7, HEAD Changes since 1.3: +1 -2 lines
Nuke more obvious #include duplications.
ok deraadt@ millert@ tedu@
|
/* $OpenBSD: group.c,v 1.4 2014/11/18 20:54:28 krw 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 <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <event.h>
#include "igmp.h"
#include "dvmrpd.h"
#include "dvmrp.h"
#include "dvmrpe.h"
#include "log.h"
#include "control.h"
void dead_timer(int, short, void *);
int start_dead_timer(struct group *);
int start_dead_timer_all(struct group *);
int stop_dead_timer(struct group *);
void v1_host_timer(int, short, void *);
int start_v1_host_timer(struct group *);
int stop_v1_host_timer(struct group *);
void retrans_timer(int, short, void *);
int start_retrans_timer(struct group *);
int stop_retrans_timer(struct group *);
extern struct dvmrpd_conf *deconf;
#define MAX_ACTIONS 4
struct {
int state;
enum group_event event;
enum group_action action[MAX_ACTIONS];
int new_state;
} grp_fsm[] = {
/* current state event that happened action(s) to take resulting state */
/* querier fsm */
{GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_ADD_GROUP,
GRP_ACT_START_TMR,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{GRP_STA_NO_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_ADD_GROUP,
GRP_ACT_START_TMR,
GRP_ACT_START_V1_HOST_TMR,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{GRP_STA_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_GROUP,
GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
{GRP_STA_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_END}, 0},
{GRP_STA_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_START_V1_HOST_TMR,
GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT},
{GRP_STA_MEMB_PRSNT, GRP_EVT_LEAVE_RCVD, {GRP_ACT_START_TMR_ALL,
GRP_ACT_START_RETRANS_TMR,
GRP_ACT_SEND_GRP_QUERY,
GRP_ACT_END}, GRP_STA_CHECK_MEMB},
{GRP_STA_CHECK_MEMB, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{GRP_STA_CHECK_MEMB, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_GROUP,
GRP_ACT_CLR_RETRANS_TMR,
GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
{GRP_STA_CHECK_MEMB, GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY,
GRP_ACT_START_RETRANS_TMR,
GRP_ACT_END}, 0},
{GRP_STA_CHECK_MEMB, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_START_V1_HOST_TMR,
GRP_ACT_END}, GRP_STA_V1_MEMB_PRSNT},
{GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V1_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_START_V1_HOST_TMR,
GRP_ACT_END}, 0},
{GRP_STA_V1_MEMB_PRSNT, GRP_EVT_V2_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_END}, 0},
{GRP_STA_V1_MEMB_PRSNT, GRP_EVT_TMR_EXPIRED, {GRP_ACT_DEL_GROUP,
GRP_ACT_END}, GRP_STA_NO_MEMB_PRSNT},
/* non querier fsm */
{GRP_STA_NO_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_ADD_GROUP,
GRP_ACT_START_TMR,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{GRP_STA_MEMB_PRSNT, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_END}, 0},
{GRP_STA_MEMB_PRSNT, GRP_EVT_QUERY_RCVD, {GRP_ACT_START_TMR_ALL,
GRP_ACT_END}, GRP_STA_CHECK_MEMB},
{GRP_STA_CHECK_MEMB, GRP_EVT_REPORT_RCVD, {GRP_ACT_START_TMR,
GRP_ACT_END}, GRP_STA_MEMB_PRSNT},
{-1, GRP_EVT_NOTHING, {GRP_ACT_NOTHING,
GRP_ACT_END}, 0},
};
const char * const group_action_names[] = {
"END MARKER",
"START TIMER",
"START ALL TIMER",
"START RETRANSMISSION TIMER",
"START V1 HOST TIMER",
"SEND GROUP QUERY",
"ADD GROUP",
"DEL GROUP",
"CLEAR RETRANSMISSION TIMER",
"NOTHING"
};
static const char * const group_event_names[] = {
"V2 REPORT RCVD",
"V1 REPORT RCVD",
"LEAVE RCVD",
"TIMER EXPIRED",
"RETRANS TIMER EXPIRED",
"V1 HOST TIMER EXPIRED",
"REPORT RCVD",
"QUERY RCVD",
"NOTHING"
};
int
group_fsm(struct group *group, enum group_event event)
{
struct mfc mfc;
int old_state;
int new_state = 0;
int i, j, ret = 0;
old_state = group->state;
for (i = 0; grp_fsm[i].state != -1; i++)
if ((grp_fsm[i].state & old_state) &&
(grp_fsm[i].event == event)) {
new_state = grp_fsm[i].new_state;
break;
}
if (grp_fsm[i].state == -1) {
/* XXX event outside of the defined fsm, ignore it. */
log_debug("group_fsm: group %s, event '%s' not expected in "
"state '%s'", inet_ntoa(group->addr),
group_event_name(event), group_state_name(old_state));
return (0);
}
for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) {
switch (grp_fsm[i].action[j]) {
case GRP_ACT_START_TMR:
ret = start_dead_timer(group);
break;
case GRP_ACT_START_TMR_ALL:
ret = start_dead_timer_all(group);
break;
case GRP_ACT_START_RETRANS_TMR:
ret = start_retrans_timer(group);
break;
case GRP_ACT_START_V1_HOST_TMR:
ret = start_v1_host_timer(group);
break;
case GRP_ACT_SEND_GRP_QUERY:
ret = send_igmp_query(group->iface, group);
break;
case GRP_ACT_ADD_GROUP:
mfc.origin.s_addr = 0;
mfc.group = group->addr;
mfc.ifindex = group->iface->ifindex;
dvmrpe_imsg_compose_rde(IMSG_GROUP_ADD, 0, 0, &mfc,
sizeof(mfc));
break;
case GRP_ACT_DEL_GROUP:
mfc.origin.s_addr = 0;
mfc.group = group->addr;
mfc.ifindex = group->iface->ifindex;
dvmrpe_imsg_compose_rde(IMSG_GROUP_DEL, 0, 0, &mfc,
sizeof(mfc));
break;
case GRP_ACT_CLR_RETRANS_TMR:
ret = stop_retrans_timer(group);
break;
case GRP_ACT_NOTHING:
case GRP_ACT_END:
/* do nothing */
break;
}
if (ret) {
log_debug("group_fsm: error changing state for "
"group %s, event '%s', state '%s'",
inet_ntoa(group->addr), group_event_name(event),
group_state_name(old_state));
return (-1);
}
}
if (new_state != 0)
group->state = new_state;
for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++)
log_debug("group_fsm: event '%s' resulted in action '%s' and "
"changing state for group %s from '%s' to '%s'",
group_event_name(event),
group_action_name(grp_fsm[i].action[j]),
inet_ntoa(group->addr), group_state_name(old_state),
group_state_name(group->state));
return (ret);
}
/* timers */
void
dead_timer(int fd, short event, void *arg)
{
struct group *group = arg;
log_debug("dead_timer: %s", inet_ntoa(group->addr));
group_fsm(group, GRP_EVT_TMR_EXPIRED);
}
int
start_dead_timer(struct group *group)
{
struct timeval tv;
log_debug("start_dead_timer: %s", inet_ntoa(group->addr));
/* Group Membership Interval */
timerclear(&tv);
tv.tv_sec = group->iface->robustness * group->iface->query_interval +
(group->iface->query_resp_interval / 2);
return (evtimer_add(&group->dead_timer, &tv));
}
int
start_dead_timer_all(struct group *group)
{
struct timeval tv;
log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr));
timerclear(&tv);
if (group->iface->state == IF_STA_QUERIER) {
/* querier */
tv.tv_sec = group->iface->last_member_query_interval *
group->iface->last_member_query_cnt;
} else {
/* non querier */
/* XXX max response time received in packet */
tv.tv_sec = group->iface->recv_query_resp_interval *
group->iface->last_member_query_cnt;
}
return (evtimer_add(&group->dead_timer, &tv));
}
int
stop_dead_timer(struct group *group)
{
log_debug("stop_dead_timer: %s", inet_ntoa(group->addr));
return (evtimer_del(&group->dead_timer));
}
void
v1_host_timer(int fd, short event, void *arg)
{
struct group *group = arg;
log_debug("v1_host_timer: %s", inet_ntoa(group->addr));
group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP);
}
int
start_v1_host_timer(struct group *group)
{
struct timeval tv;
log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr));
/* Group Membership Interval */
timerclear(&tv);
tv.tv_sec = group->iface->robustness * group->iface->query_interval +
(group->iface->query_resp_interval / 2);
return (evtimer_add(&group->v1_host_timer, &tv));
}
int
stop_v1_host_timer(struct group *group)
{
log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr));
return (evtimer_del(&group->v1_host_timer));
}
void
retrans_timer(int fd, short event, void *arg)
{
struct group *group = arg;
struct timeval tv;
log_debug("retrans_timer: %s", inet_ntoa(group->addr));
send_igmp_query(group->iface, group);
/* reschedule retrans_timer */
if (group->state == GRP_STA_CHECK_MEMB) {
timerclear(&tv);
tv.tv_sec = group->iface->last_member_query_interval;
evtimer_add(&group->retrans_timer, &tv);
}
}
int
start_retrans_timer(struct group *group)
{
struct timeval tv;
log_debug("start_retrans_timer: %s", inet_ntoa(group->addr));
timerclear(&tv);
tv.tv_sec = group->iface->last_member_query_interval;
return (evtimer_add(&group->retrans_timer, &tv));
}
int
stop_retrans_timer(struct group *group)
{
log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr));
return (evtimer_del(&group->retrans_timer));
}
/* group list */
struct group *
group_list_add(struct iface *iface, u_int32_t group)
{
struct group *ge;
struct timeval now;
/* validate group id */
if (!IN_MULTICAST(htonl(group)))
fatalx("group_list_add: invalid group");
if ((ge = group_list_find(iface, group)) != NULL) {
return (ge);
}
if ((ge = calloc(1, sizeof(*ge))) == NULL)
fatal("group_list_add");
ge->addr.s_addr = group;
ge->state = GRP_STA_NO_MEMB_PRSNT;
evtimer_set(&ge->dead_timer, dead_timer, ge);
evtimer_set(&ge->v1_host_timer, v1_host_timer, ge);
evtimer_set(&ge->retrans_timer, retrans_timer, ge);
gettimeofday(&now, NULL);
ge->uptime = now.tv_sec;
TAILQ_INSERT_TAIL(&iface->group_list, ge, entry);
iface->group_cnt++;
ge->iface = iface;
log_debug("group_list_add: interface %s, group %s", iface->name,
inet_ntoa(ge->addr));
return (ge);
}
void
group_list_remove(struct iface *iface, struct group *group)
{
log_debug("group_list_remove: interface %s, group %s", iface->name,
inet_ntoa(group->addr));
/* stop timers */
stop_dead_timer(group);
start_v1_host_timer(group);
stop_retrans_timer(group);
TAILQ_REMOVE(&iface->group_list, group, entry);
free(group);
iface->group_cnt--;
}
struct group *
group_list_find(struct iface *iface, u_int32_t group)
{
struct group *ge = NULL;
/* validate group id */
if (!IN_MULTICAST(htonl(group)))
fatalx("group_list_find: invalid group");
TAILQ_FOREACH(ge, &iface->group_list, entry) {
if (ge->addr.s_addr == group)
return (ge);
}
return (ge);
}
void
group_list_clr(struct iface *iface)
{
struct group *ge;
while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) {
TAILQ_REMOVE(&iface->group_list, ge, entry);
free(ge);
}
iface->group_cnt = 0;
}
int
group_list_empty(struct iface *iface)
{
return (TAILQ_EMPTY(&iface->group_list));
}
void
group_list_dump(struct iface *iface, struct ctl_conn *c)
{
struct group *ge;
struct ctl_group *gctl;
TAILQ_FOREACH(ge, &iface->group_list, entry) {
gctl = group_to_ctl(ge);
imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IGMP, 0, 0,
-1, gctl, sizeof(struct ctl_group));
}
}
/* names */
const char *
group_event_name(int event)
{
return (group_event_names[event]);
}
const char *
group_action_name(int action)
{
return (group_action_names[action]);
}
struct ctl_group *
group_to_ctl(struct group *group)
{
static struct ctl_group gctl;
struct timeval tv, now, res;
memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr));
gctl.state = group->state;
gettimeofday(&now, NULL);
if (evtimer_pending(&group->dead_timer, &tv)) {
timersub(&tv, &now, &res);
gctl.dead_timer = res.tv_sec;
} else
gctl.dead_timer = 0;
if (group->state != GRP_STA_NO_MEMB_PRSNT) {
gctl.uptime = now.tv_sec - group->uptime;
} else
gctl.uptime = 0;
return (&gctl);
}