Commit | Line | Data |
---|---|---|
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 | |
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 | |
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 | |
50 | igmp_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 | ||
179 | void | |
180 | igmp_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 | ||
196 | void | |
197 | igmp_leavegroup(inm) | |
198 | struct in_multi *inm; | |
199 | { | |
200 | /* | |
201 | * No action required on leaving a group. | |
202 | */ | |
203 | } | |
204 | ||
205 | void | |
206 | igmp_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 | ||
235 | static void | |
236 | igmp_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 | } |