4.4BSD snapshot (revision 8.1)
[unix-history] / usr / src / sys / netinet / igmp.c
CommitLineData
0720a479
KS
1/*
2 * Copyright (c) 1988 Stephen Deering.
3 * Copyright (c) 1992 Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Stephen Deering of Stanford University.
8 *
9 * %sccs.include.redist.c%
10 *
e7a3707f 11 * @(#)igmp.c 8.1 (Berkeley) %G%
0720a479
KS
12 */
13
14/* Internet Group Management Protocol (IGMP) routines. */
15
0720a479 16
5548a02f
KB
17#include <sys/param.h>
18#include <sys/mbuf.h>
19#include <sys/socket.h>
20#include <sys/protosw.h>
21
22#include <net/if.h>
23#include <net/route.h>
24
25#include <netinet/in.h>
26#include <netinet/in_var.h>
27#include <netinet/in_systm.h>
28#include <netinet/ip.h>
29#include <netinet/ip_var.h>
30#include <netinet/igmp.h>
31#include <netinet/igmp_var.h>
0720a479
KS
32
33extern struct ifnet loif;
34
35static int igmp_timers_are_running = 0;
36static u_long igmp_all_hosts_group;
37
38static void igmp_sendreport __P((struct in_multi *));
39
40void
41igmp_init()
42{
43 /*
44 * To avoid byte-swapping the same value over and over again.
45 */
46 igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
47}
48
49void
50igmp_input(m, ifp)
51 register struct mbuf *m;
52 register struct ifnet *ifp;
53{
54 register struct igmp *igmp;
55 register struct ip *ip;
56 register int igmplen;
57 register int iphlen;
58 register int minlen;
59 register struct in_multi *inm;
60 register struct in_ifaddr *ia;
61 struct in_multistep step;
62
63 ++igmpstat.igps_rcv_total;
64
65 ip = mtod(m, struct ip *);
66 iphlen = ip->ip_hl << 2;
67 igmplen = ip->ip_len;
68
69 /*
70 * Validate lengths
71 */
72 if (igmplen < IGMP_MINLEN) {
73 ++igmpstat.igps_rcv_tooshort;
74 m_freem(m);
75 return;
76 }
77 minlen = iphlen + IGMP_MINLEN;
78 if ((m->m_flags & M_EXT || m->m_len < minlen) &&
79 (m = m_pullup(m, minlen)) == 0) {
80 ++igmpstat.igps_rcv_tooshort;
81 return;
82 }
83
84 /*
85 * Validate checksum
86 */
87 m->m_data += iphlen;
88 m->m_len -= iphlen;
89 igmp = mtod(m, struct igmp *);
90 if (in_cksum(m, igmplen)) {
91 ++igmpstat.igps_rcv_badsum;
92 m_freem(m);
93 return;
94 }
95 m->m_data -= iphlen;
96 m->m_len += iphlen;
97 ip = mtod(m, struct ip *);
98
99 switch (igmp->igmp_type) {
100
101 case IGMP_HOST_MEMBERSHIP_QUERY:
102 ++igmpstat.igps_rcv_queries;
103
104 if (ifp == &loif)
105 break;
106
107 if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
108 ++igmpstat.igps_rcv_badqueries;
109 m_freem(m);
110 return;
111 }
112
113 /*
114 * Start the timers in all of our membership records for
115 * the interface on which the query arrived, except those
116 * that are already running and those that belong to the
117 * "all-hosts" group.
118 */
119 IN_FIRST_MULTI(step, inm);
120 while (inm != NULL) {
121 if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
122 inm->inm_addr.s_addr != igmp_all_hosts_group) {
123 inm->inm_timer =
124 IGMP_RANDOM_DELAY(inm->inm_addr);
125 igmp_timers_are_running = 1;
126 }
127 IN_NEXT_MULTI(step, inm);
128 }
129
130 break;
131
132 case IGMP_HOST_MEMBERSHIP_REPORT:
133 ++igmpstat.igps_rcv_reports;
134
135 if (ifp == &loif)
136 break;
137
138 if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
139 igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
140 ++igmpstat.igps_rcv_badreports;
141 m_freem(m);
142 return;
143 }
144
145 /*
146 * KLUDGE: if the IP source address of the report has an
147 * unspecified (i.e., zero) subnet number, as is allowed for
148 * a booting host, replace it with the correct subnet number
149 * so that a process-level multicast routing demon can
150 * determine which subnet it arrived from. This is necessary
151 * to compensate for the lack of any way for a process to
152 * determine the arrival interface of an incoming packet.
153 */
154 if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
155 IFP_TO_IA(ifp, ia);
156 if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
157 }
158
159 /*
160 * If we belong to the group being reported, stop
161 * our timer for that group.
162 */
163 IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
164 if (inm != NULL) {
165 inm->inm_timer = 0;
166 ++igmpstat.igps_rcv_ourreports;
167 }
168
169 break;
170 }
171
172 /*
173 * Pass all valid IGMP packets up to any process(es) listening
174 * on a raw IGMP socket.
175 */
176 rip_input(m);
177}
178
179void
180igmp_joingroup(inm)
181 struct in_multi *inm;
182{
183 register int s = splnet();
184
185 if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
186 inm->inm_ifp == &loif)
187 inm->inm_timer = 0;
188 else {
189 igmp_sendreport(inm);
190 inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
191 igmp_timers_are_running = 1;
192 }
193 splx(s);
194}
195
196void
197igmp_leavegroup(inm)
198 struct in_multi *inm;
199{
200 /*
201 * No action required on leaving a group.
202 */
203}
204
205void
206igmp_fasttimo()
207{
208 register struct in_multi *inm;
209 register int s;
210 struct in_multistep step;
211
212 /*
213 * Quick check to see if any work needs to be done, in order
214 * to minimize the overhead of fasttimo processing.
215 */
216 if (!igmp_timers_are_running)
217 return;
218
219 s = splnet();
220 igmp_timers_are_running = 0;
221 IN_FIRST_MULTI(step, inm);
222 while (inm != NULL) {
223 if (inm->inm_timer == 0) {
224 /* do nothing */
225 } else if (--inm->inm_timer == 0) {
226 igmp_sendreport(inm);
227 } else {
228 igmp_timers_are_running = 1;
229 }
230 IN_NEXT_MULTI(step, inm);
231 }
232 splx(s);
233}
234
235static void
236igmp_sendreport(inm)
237 register struct in_multi *inm;
238{
239 register struct mbuf *m;
240 register struct igmp *igmp;
241 register struct ip *ip;
242 register struct ip_moptions *imo;
243 struct ip_moptions simo;
0720a479
KS
244
245 MGETHDR(m, M_DONTWAIT, MT_HEADER);
246 if (m == NULL)
247 return;
248 m->m_data += max_linkhdr;
249 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
250
251 ip = mtod(m, struct ip *);
252 ip->ip_tos = 0;
253 ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
254 ip->ip_off = 0;
255 ip->ip_p = IPPROTO_IGMP;
256 ip->ip_src.s_addr = INADDR_ANY;
257 ip->ip_dst = inm->inm_addr;
258
259 igmp = (struct igmp *)(ip + 1);
260 igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
261 igmp->igmp_code = 0;
262 igmp->igmp_group = inm->inm_addr;
263 igmp->igmp_cksum = 0;
264 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
265
266 imo = &simo;
267 bzero((caddr_t)imo, sizeof(*imo));
268 imo->imo_multicast_ifp = inm->inm_ifp;
269 imo->imo_multicast_ttl = 1;
270 /*
271 * Request loopback of the report if we are acting as a multicast
272 * router, so that the process-level routing demon can hear it.
273 */
80fb1f7c
KS
274#ifdef MROUTING
275 {
276 extern struct socket *ip_mrouter;
0720a479 277 imo->imo_multicast_loop = (ip_mrouter != NULL);
80fb1f7c
KS
278 }
279#endif
0720a479
KS
280 ip_output(m, NULL, NULL, 0, imo);
281
282 ++igmpstat.igps_snd_reports;
283}