GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dvmrpd/igmp.c Lines: 0 89 0.0 %
Date: 2017-11-07 Branches: 0 50 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: igmp.c,v 1.4 2015/12/07 19:14:49 mmcc Exp $ */
2
3
/*
4
 * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/socket.h>
21
#include <netinet/in.h>
22
#include <arpa/inet.h>
23
#include <sys/time.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <event.h>
27
28
#include "igmp.h"
29
#include "dvmrpd.h"
30
#include "dvmrp.h"
31
#include "log.h"
32
#include "dvmrpe.h"
33
34
int	 igmp_chksum(struct igmp_hdr *);
35
36
/* IGMP packet handling */
37
int
38
send_igmp_query(struct iface *iface, struct group *group)
39
{
40
	struct igmp_hdr		 igmp_hdr;
41
	struct sockaddr_in	 dst;
42
	struct ibuf		*buf;
43
	int			 ret = 0;
44
45
	log_debug("send_igmp_query: interface %s", iface->name);
46
47
	if (iface->passive)
48
		return (0);
49
50
	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
51
		fatal("send_igmp_query");
52
53
	/* IGMP header */
54
	memset(&igmp_hdr, 0, sizeof(igmp_hdr));
55
	igmp_hdr.type = PKT_TYPE_MEMBER_QUERY;
56
57
	if (group == NULL) {
58
		/* general query - version is configured */
59
		igmp_hdr.grp_addr = 0;
60
61
		switch (iface->igmp_version) {
62
		case 1:
63
			break;
64
		case 2:
65
			igmp_hdr.max_resp_time = iface->query_resp_interval;
66
			break;
67
		default:
68
			fatal("send_igmp_query: invalid igmp version");
69
		}
70
	} else {
71
		/* group specific query - only version 2 */
72
		igmp_hdr.grp_addr = group->addr.s_addr;
73
		igmp_hdr.max_resp_time = iface->last_member_query_interval;
74
	}
75
76
	/* update chksum */
77
	igmp_hdr.chksum = in_cksum(&igmp_hdr, sizeof(igmp_hdr));
78
79
	ibuf_add(buf, &igmp_hdr, sizeof(igmp_hdr));
80
81
	/* set destination address */
82
	dst.sin_family = AF_INET;
83
	dst.sin_len = sizeof(struct sockaddr_in);
84
	inet_aton(AllSystems, &dst.sin_addr);
85
86
	ret = send_packet(iface, buf->buf, buf->wpos, &dst);
87
	ibuf_free(buf);
88
	return (ret);
89
}
90
91
void
92
recv_igmp_query(struct iface *iface, struct in_addr src, char *buf,
93
    u_int16_t len)
94
{
95
	struct igmp_hdr	 igmp_hdr;
96
	struct group	*group;
97
98
	log_debug("recv_igmp_query: interface %s", iface->name);
99
100
	if (len < sizeof(igmp_hdr)) {
101
		log_debug("recv_igmp_query: invalid IGMP report, interface %s",
102
		    iface->name);
103
		return;
104
	}
105
106
	memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
107
	iface->recv_query_resp_interval = igmp_hdr.max_resp_time;
108
109
	/* verify chksum */
110
	if (igmp_chksum(&igmp_hdr) == -1) {
111
		log_debug("recv_igmp_query: invalid chksum, interface %s",
112
		    iface->name);
113
		return;
114
	}
115
116
	if (src.s_addr < iface->addr.s_addr && igmp_hdr.grp_addr == 0) {
117
		/* we received a general query and we lost the election */
118
		if_fsm(iface, IF_EVT_QRECVD);
119
		/* remember who is querier */
120
		iface->querier = src;
121
		return;
122
	}
123
124
	if (iface->state == IF_STA_NONQUERIER && igmp_hdr.grp_addr != 0) {
125
		/* validate group id */
126
		if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
127
			log_debug("recv_igmp_query: invalid group, "
128
			    "interface %s", iface->name);
129
			return;
130
		}
131
132
		if ((group = group_list_add(iface, igmp_hdr.grp_addr))
133
		    != NULL)
134
			group_fsm(group, GRP_EVT_QUERY_RCVD);
135
	}
136
}
137
138
void
139
recv_igmp_report(struct iface *iface, struct in_addr src, char *buf,
140
    u_int16_t len, u_int8_t type)
141
{
142
	struct igmp_hdr	 igmp_hdr;
143
	struct group	*group;
144
145
	log_debug("recv_igmp_report: interface %s", iface->name);
146
147
	if (len < sizeof(igmp_hdr)) {
148
		log_debug("recv_igmp_report: invalid IGMP report, interface %s",
149
		    iface->name);
150
		return;
151
	}
152
153
	memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
154
155
	/* verify chksum */
156
	if (igmp_chksum(&igmp_hdr) == -1) {
157
		log_debug("recv_igmp_report: invalid chksum, interface %s",
158
		    iface->name);
159
		return;
160
	}
161
162
	/* validate group id */
163
	if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
164
		log_debug("recv_igmp_report: invalid group, interface %s",
165
		    iface->name);
166
		return;
167
	}
168
169
	if ((group = group_list_add(iface, igmp_hdr.grp_addr)) == NULL)
170
		return;
171
172
	if (iface->state == IF_STA_QUERIER) {
173
		/* querier */
174
		switch (type) {
175
		case PKT_TYPE_MEMBER_REPORTv1:
176
			group_fsm(group, GRP_EVT_V1_REPORT_RCVD);
177
			break;
178
		case PKT_TYPE_MEMBER_REPORTv2:
179
			group_fsm(group, GRP_EVT_V2_REPORT_RCVD);
180
			break;
181
		default:
182
			fatalx("recv_igmp_report: unknown IGMP report type");
183
		}
184
	} else {
185
		/* non querier */
186
		group_fsm(group, GRP_EVT_REPORT_RCVD);
187
	}
188
}
189
190
void
191
recv_igmp_leave(struct iface *iface, struct in_addr src, char *buf,
192
    u_int16_t len)
193
{
194
	struct igmp_hdr	 igmp_hdr;
195
	struct group	*group;
196
197
	log_debug("recv_igmp_leave: interface %s", iface->name);
198
199
	if (iface->state != IF_STA_QUERIER)
200
		return;
201
202
	if (len < sizeof(igmp_hdr)) {
203
		log_debug("recv_igmp_leave: invalid IGMP leave, interface %s",
204
		    iface->name);
205
		return;
206
	}
207
208
	memcpy(&igmp_hdr, buf, sizeof(igmp_hdr));
209
210
	/* verify chksum */
211
	if (igmp_chksum(&igmp_hdr) == -1) {
212
		log_debug("recv_igmp_leave: invalid chksum, interface %s",
213
		    iface->name);
214
		return;
215
	}
216
217
	/* validate group id */
218
	if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) {
219
		log_debug("recv_igmp_leave: invalid group, interface %s",
220
		    iface->name);
221
		return;
222
	}
223
224
	if ((group = group_list_find(iface, igmp_hdr.grp_addr)) != NULL) {
225
		group_fsm(group, GRP_EVT_LEAVE_RCVD);
226
	}
227
}
228
229
int
230
igmp_chksum(struct igmp_hdr *igmp_hdr)
231
{
232
	u_int16_t	chksum;
233
234
	chksum = igmp_hdr->chksum;
235
	igmp_hdr->chksum = 0;
236
237
	if (chksum != in_cksum(igmp_hdr, sizeof(*igmp_hdr)))
238
		return (-1);
239
240
	return (0);
241
}