1 |
|
|
/* $NetBSD: igmp.c,v 1.5 1995/12/10 10:07:01 mycroft Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* The mrouted program is covered by the license in the accompanying file |
5 |
|
|
* named "LICENSE". Use of the mrouted program represents acceptance of |
6 |
|
|
* the terms and conditions listed in that file. |
7 |
|
|
* |
8 |
|
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of |
9 |
|
|
* Leland Stanford Junior University. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
|
13 |
|
|
#include "defs.h" |
14 |
|
|
|
15 |
|
|
|
16 |
|
|
/* |
17 |
|
|
* Exported variables. |
18 |
|
|
*/ |
19 |
|
|
char *recv_buf; /* input packet buffer */ |
20 |
|
|
char *send_buf; /* output packet buffer */ |
21 |
|
|
int igmp_socket; /* socket for all network I/O */ |
22 |
|
|
u_int32_t allhosts_group; /* All hosts addr in net order */ |
23 |
|
|
u_int32_t allrtrs_group; /* All-Routers " in net order */ |
24 |
|
|
u_int32_t dvmrp_group; /* DVMRP grp addr in net order */ |
25 |
|
|
u_int32_t dvmrp_genid; /* IGMP generation id */ |
26 |
|
|
|
27 |
|
|
/* |
28 |
|
|
* Local function definitions. |
29 |
|
|
*/ |
30 |
|
|
/* u_char promoted to u_int */ |
31 |
|
|
static char * packet_kind(u_int type, u_int code); |
32 |
|
|
static int igmp_log_level(u_int type, u_int code); |
33 |
|
|
|
34 |
|
|
/* |
35 |
|
|
* Open and initialize the igmp socket, and fill in the non-changing |
36 |
|
|
* IP header fields in the output packet buffer. |
37 |
|
|
*/ |
38 |
|
|
void |
39 |
|
|
init_igmp(void) |
40 |
|
|
{ |
41 |
|
|
struct ip *ip; |
42 |
|
|
|
43 |
|
|
recv_buf = malloc(RECV_BUF_SIZE); |
44 |
|
|
send_buf = malloc(RECV_BUF_SIZE); |
45 |
|
|
|
46 |
|
|
if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) |
47 |
|
|
logit(LOG_ERR, errno, "IGMP socket"); |
48 |
|
|
|
49 |
|
|
k_hdr_include(TRUE); /* include IP header when sending */ |
50 |
|
|
k_set_rcvbuf(48*1024); /* lots of input buffering */ |
51 |
|
|
k_set_ttl(1); /* restrict multicasts to one hop */ |
52 |
|
|
k_set_loop(FALSE); /* disable multicast loopback */ |
53 |
|
|
|
54 |
|
|
ip = (struct ip *)send_buf; |
55 |
|
|
ip->ip_hl = sizeof(struct ip) >> 2; |
56 |
|
|
ip->ip_v = IPVERSION; |
57 |
|
|
ip->ip_tos = 0; |
58 |
|
|
ip->ip_off = htons(0); |
59 |
|
|
ip->ip_p = IPPROTO_IGMP; |
60 |
|
|
ip->ip_ttl = MAXTTL; /* applies to unicasts only */ |
61 |
|
|
|
62 |
|
|
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); |
63 |
|
|
dvmrp_group = htonl(INADDR_DVMRP_GROUP); |
64 |
|
|
allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); |
65 |
|
|
} |
66 |
|
|
|
67 |
|
|
#define PIM_QUERY 0 |
68 |
|
|
#define PIM_REGISTER 1 |
69 |
|
|
#define PIM_REGISTER_STOP 2 |
70 |
|
|
#define PIM_JOIN_PRUNE 3 |
71 |
|
|
#define PIM_RP_REACHABLE 4 |
72 |
|
|
#define PIM_ASSERT 5 |
73 |
|
|
#define PIM_GRAFT 6 |
74 |
|
|
#define PIM_GRAFT_ACK 7 |
75 |
|
|
|
76 |
|
|
static char * |
77 |
|
|
packet_kind(u_int type, u_int code) |
78 |
|
|
{ |
79 |
|
|
switch (type) { |
80 |
|
|
case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; |
81 |
|
|
case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "v1 member report "; |
82 |
|
|
case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "v2 member report "; |
83 |
|
|
case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; |
84 |
|
|
case IGMP_DVMRP: |
85 |
|
|
switch (code) { |
86 |
|
|
case DVMRP_PROBE: return "neighbor probe "; |
87 |
|
|
case DVMRP_REPORT: return "route report "; |
88 |
|
|
case DVMRP_ASK_NEIGHBORS: return "neighbor request "; |
89 |
|
|
case DVMRP_NEIGHBORS: return "neighbor list "; |
90 |
|
|
case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; |
91 |
|
|
case DVMRP_NEIGHBORS2: return "neighbor list 2 "; |
92 |
|
|
case DVMRP_PRUNE: return "prune message "; |
93 |
|
|
case DVMRP_GRAFT: return "graft message "; |
94 |
|
|
case DVMRP_GRAFT_ACK: return "graft message ack "; |
95 |
|
|
case DVMRP_INFO_REQUEST: return "info request "; |
96 |
|
|
case DVMRP_INFO_REPLY: return "info reply "; |
97 |
|
|
default: return "unknown DVMRP msg "; |
98 |
|
|
} |
99 |
|
|
case IGMP_PIM: |
100 |
|
|
switch (code) { |
101 |
|
|
case PIM_QUERY: return "PIM Router-Query "; |
102 |
|
|
case PIM_REGISTER: return "PIM Register "; |
103 |
|
|
case PIM_REGISTER_STOP: return "PIM Register-Stop "; |
104 |
|
|
case PIM_JOIN_PRUNE: return "PIM Join/Prune "; |
105 |
|
|
case PIM_RP_REACHABLE: return "PIM RP-Reachable "; |
106 |
|
|
case PIM_ASSERT: return "PIM Assert "; |
107 |
|
|
case PIM_GRAFT: return "PIM Graft "; |
108 |
|
|
case PIM_GRAFT_ACK: return "PIM Graft-Ack "; |
109 |
|
|
default: return "unknown PIM msg "; |
110 |
|
|
} |
111 |
|
|
case IGMP_MTRACE_QUERY: return "IGMP trace query "; |
112 |
|
|
case IGMP_MTRACE_REPLY: return "IGMP trace reply "; |
113 |
|
|
default: return "unknown IGMP msg "; |
114 |
|
|
} |
115 |
|
|
} |
116 |
|
|
|
117 |
|
|
/* |
118 |
|
|
* Process a newly received IGMP packet that is sitting in the input |
119 |
|
|
* packet buffer. |
120 |
|
|
*/ |
121 |
|
|
void |
122 |
|
|
accept_igmp(int recvlen) |
123 |
|
|
{ |
124 |
|
|
u_int32_t src, dst, group; |
125 |
|
|
struct ip *ip; |
126 |
|
|
struct igmp *igmp; |
127 |
|
|
int ipdatalen, iphdrlen, igmpdatalen; |
128 |
|
|
|
129 |
|
|
if (recvlen < sizeof(struct ip)) { |
130 |
|
|
logit(LOG_WARNING, 0, |
131 |
|
|
"received packet too short (%u bytes) for IP header", recvlen); |
132 |
|
|
return; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
ip = (struct ip *)recv_buf; |
136 |
|
|
src = ip->ip_src.s_addr; |
137 |
|
|
dst = ip->ip_dst.s_addr; |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* this is most likely a message from the kernel indicating that |
141 |
|
|
* a new src grp pair message has arrived and so, it would be |
142 |
|
|
* necessary to install a route into the kernel for this. |
143 |
|
|
*/ |
144 |
|
|
if (ip->ip_p == 0) { |
145 |
|
|
if (src == 0 || dst == 0) |
146 |
|
|
logit(LOG_WARNING, 0, "kernel request not accurate"); |
147 |
|
|
else |
148 |
|
|
add_table_entry(src, dst); |
149 |
|
|
return; |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
iphdrlen = ip->ip_hl << 2; |
153 |
|
|
ipdatalen = ntohs(ip->ip_len) - iphdrlen; |
154 |
|
|
if (iphdrlen + ipdatalen != recvlen) { |
155 |
|
|
logit(LOG_WARNING, 0, |
156 |
|
|
"received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", |
157 |
|
|
inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen); |
158 |
|
|
return; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
igmp = (struct igmp *)(recv_buf + iphdrlen); |
162 |
|
|
group = igmp->igmp_group.s_addr; |
163 |
|
|
igmpdatalen = ipdatalen - IGMP_MINLEN; |
164 |
|
|
if (igmpdatalen < 0) { |
165 |
|
|
logit(LOG_WARNING, 0, |
166 |
|
|
"received IP data field too short (%u bytes) for IGMP, from %s", |
167 |
|
|
ipdatalen, inet_fmt(src, s1)); |
168 |
|
|
return; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s", |
172 |
|
|
packet_kind(igmp->igmp_type, igmp->igmp_code), |
173 |
|
|
inet_fmt(src, s1), inet_fmt(dst, s2)); |
174 |
|
|
|
175 |
|
|
switch (igmp->igmp_type) { |
176 |
|
|
|
177 |
|
|
case IGMP_HOST_MEMBERSHIP_QUERY: |
178 |
|
|
accept_membership_query(src, dst, group, igmp->igmp_code); |
179 |
|
|
return; |
180 |
|
|
|
181 |
|
|
case IGMP_v1_HOST_MEMBERSHIP_REPORT: |
182 |
|
|
case IGMP_v2_HOST_MEMBERSHIP_REPORT: |
183 |
|
|
accept_group_report(src, dst, group, igmp->igmp_type); |
184 |
|
|
return; |
185 |
|
|
|
186 |
|
|
case IGMP_HOST_LEAVE_MESSAGE: |
187 |
|
|
accept_leave_message(src, dst, group); |
188 |
|
|
return; |
189 |
|
|
|
190 |
|
|
case IGMP_DVMRP: |
191 |
|
|
group = ntohl(group); |
192 |
|
|
|
193 |
|
|
switch (igmp->igmp_code) { |
194 |
|
|
case DVMRP_PROBE: |
195 |
|
|
accept_probe(src, dst, |
196 |
|
|
(char *)(igmp+1), igmpdatalen, group); |
197 |
|
|
return; |
198 |
|
|
|
199 |
|
|
case DVMRP_REPORT: |
200 |
|
|
accept_report(src, dst, |
201 |
|
|
(char *)(igmp+1), igmpdatalen, group); |
202 |
|
|
return; |
203 |
|
|
|
204 |
|
|
case DVMRP_ASK_NEIGHBORS: |
205 |
|
|
accept_neighbor_request(src, dst); |
206 |
|
|
return; |
207 |
|
|
|
208 |
|
|
case DVMRP_ASK_NEIGHBORS2: |
209 |
|
|
accept_neighbor_request2(src, dst); |
210 |
|
|
return; |
211 |
|
|
|
212 |
|
|
case DVMRP_NEIGHBORS: |
213 |
|
|
accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, |
214 |
|
|
group); |
215 |
|
|
return; |
216 |
|
|
|
217 |
|
|
case DVMRP_NEIGHBORS2: |
218 |
|
|
accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, |
219 |
|
|
group); |
220 |
|
|
return; |
221 |
|
|
|
222 |
|
|
case DVMRP_PRUNE: |
223 |
|
|
accept_prune(src, dst, (char *)(igmp+1), igmpdatalen); |
224 |
|
|
return; |
225 |
|
|
|
226 |
|
|
case DVMRP_GRAFT: |
227 |
|
|
accept_graft(src, dst, (char *)(igmp+1), igmpdatalen); |
228 |
|
|
return; |
229 |
|
|
|
230 |
|
|
case DVMRP_GRAFT_ACK: |
231 |
|
|
accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen); |
232 |
|
|
return; |
233 |
|
|
|
234 |
|
|
case DVMRP_INFO_REQUEST: |
235 |
|
|
accept_info_request(src, dst, (char *)(igmp+1), |
236 |
|
|
igmpdatalen); |
237 |
|
|
return; |
238 |
|
|
|
239 |
|
|
case DVMRP_INFO_REPLY: |
240 |
|
|
accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen); |
241 |
|
|
return; |
242 |
|
|
|
243 |
|
|
default: |
244 |
|
|
logit(LOG_INFO, 0, |
245 |
|
|
"ignoring unknown DVMRP message code %u from %s to %s", |
246 |
|
|
igmp->igmp_code, inet_fmt(src, s1), |
247 |
|
|
inet_fmt(dst, s2)); |
248 |
|
|
return; |
249 |
|
|
} |
250 |
|
|
|
251 |
|
|
case IGMP_PIM: |
252 |
|
|
return; |
253 |
|
|
|
254 |
|
|
case IGMP_MTRACE_REPLY: |
255 |
|
|
return; |
256 |
|
|
|
257 |
|
|
case IGMP_MTRACE_QUERY: |
258 |
|
|
accept_mtrace(src, dst, group, (char *)(igmp+1), |
259 |
|
|
igmp->igmp_code, igmpdatalen); |
260 |
|
|
return; |
261 |
|
|
|
262 |
|
|
default: |
263 |
|
|
logit(LOG_INFO, 0, |
264 |
|
|
"ignoring unknown IGMP message type %x from %s to %s", |
265 |
|
|
igmp->igmp_type, inet_fmt(src, s1), |
266 |
|
|
inet_fmt(dst, s2)); |
267 |
|
|
return; |
268 |
|
|
} |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
/* |
272 |
|
|
* Some IGMP messages are more important than others. This routine |
273 |
|
|
* determines the logging level at which to log a send error (often |
274 |
|
|
* "No route to host"). This is important when there is asymmetric |
275 |
|
|
* reachability and someone is trying to, i.e., mrinfo me periodically. |
276 |
|
|
*/ |
277 |
|
|
static int |
278 |
|
|
igmp_log_level(u_int type, u_int code) |
279 |
|
|
{ |
280 |
|
|
switch (type) { |
281 |
|
|
case IGMP_MTRACE_REPLY: |
282 |
|
|
return LOG_INFO; |
283 |
|
|
|
284 |
|
|
case IGMP_DVMRP: |
285 |
|
|
switch (code) { |
286 |
|
|
case DVMRP_NEIGHBORS: |
287 |
|
|
case DVMRP_NEIGHBORS2: |
288 |
|
|
return LOG_INFO; |
289 |
|
|
} |
290 |
|
|
} |
291 |
|
|
return LOG_WARNING; |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
/* |
295 |
|
|
* Construct an IGMP message in the output packet buffer. The caller may |
296 |
|
|
* have already placed data in that buffer, of length 'datalen'. Then send |
297 |
|
|
* the message from the interface with IP address 'src' to destination 'dst'. |
298 |
|
|
*/ |
299 |
|
|
void |
300 |
|
|
send_igmp(u_int32_t src, u_int32_t dst, int type, int code, |
301 |
|
|
u_int32_t group, int datalen) |
302 |
|
|
{ |
303 |
|
|
struct sockaddr_in sdst; |
304 |
|
|
struct ip *ip; |
305 |
|
|
struct igmp *igmp; |
306 |
|
|
int setloop = 0; |
307 |
|
|
|
308 |
|
|
ip = (struct ip *)send_buf; |
309 |
|
|
ip->ip_src.s_addr = src; |
310 |
|
|
ip->ip_dst.s_addr = dst; |
311 |
|
|
ip->ip_len = htons(MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen); |
312 |
|
|
|
313 |
|
|
igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); |
314 |
|
|
igmp->igmp_type = type; |
315 |
|
|
igmp->igmp_code = code; |
316 |
|
|
igmp->igmp_group.s_addr = group; |
317 |
|
|
igmp->igmp_cksum = 0; |
318 |
|
|
igmp->igmp_cksum = inet_cksum((u_int16_t *)igmp, |
319 |
|
|
IGMP_MINLEN + datalen); |
320 |
|
|
|
321 |
|
|
if (IN_MULTICAST(ntohl(dst))) { |
322 |
|
|
k_set_if(src); |
323 |
|
|
if (type != IGMP_DVMRP) { |
324 |
|
|
setloop = 1; |
325 |
|
|
k_set_loop(TRUE); |
326 |
|
|
} |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
bzero(&sdst, sizeof(sdst)); |
330 |
|
|
sdst.sin_family = AF_INET; |
331 |
|
|
sdst.sin_len = sizeof(sdst); |
332 |
|
|
sdst.sin_addr.s_addr = dst; |
333 |
|
|
if (sendto(igmp_socket, send_buf, ntohs(ip->ip_len), 0, |
334 |
|
|
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) { |
335 |
|
|
if (errno == ENETDOWN) |
336 |
|
|
check_vif_state(); |
337 |
|
|
else |
338 |
|
|
logit(igmp_log_level(type, code), errno, |
339 |
|
|
"sendto to %s on %s", |
340 |
|
|
inet_fmt(dst, s1), inet_fmt(src, s2)); |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
if (setloop) |
344 |
|
|
k_set_loop(FALSE); |
345 |
|
|
|
346 |
|
|
logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s", |
347 |
|
|
packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); |
348 |
|
|
} |