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