2 * The mrouted program is covered by the license in the accompanying file
3 * named "LICENSE". Use of the mrouted program represents acceptance of
4 * the terms and conditions listed in that file.
6 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7 * Leland Stanford Junior University.
10 * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp
12 * $FreeBSD: src/usr.sbin/mrouted/igmp.c,v 1.16 1999/08/28 01:17:04 peter Exp $
13 * $DragonFly: src/usr.sbin/mrouted/igmp.c,v 1.4 2004/03/15 18:10:28 dillon Exp $
21 char *recv_buf
; /* input packet buffer */
22 char *send_buf
; /* output packet buffer */
23 int igmp_socket
; /* socket for all network I/O */
24 u_int32 allhosts_group
; /* All hosts addr in net order */
25 u_int32 allrtrs_group
; /* All-Routers " in net order */
26 u_int32 dvmrp_group
; /* DVMRP grp addr in net order */
27 u_int32 dvmrp_genid
; /* IGMP generation id */
30 * Local function definitions.
32 /* u_char promoted to u_int */
33 static int igmp_log_level(u_int type
, u_int code
);
36 * Open and initialize the igmp socket, and fill in the non-changing
37 * IP header fields in the output packet buffer.
44 recv_buf
= malloc(RECV_BUF_SIZE
);
45 send_buf
= malloc(RECV_BUF_SIZE
);
47 if ((igmp_socket
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
)) < 0)
48 log(LOG_ERR
, errno
, "IGMP socket");
50 k_hdr_include(TRUE
); /* include IP header when sending */
51 k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
52 k_set_ttl(1); /* restrict multicasts to one hop */
53 k_set_loop(FALSE
); /* disable multicast loopback */
55 ip
= (struct ip
*)send_buf
;
56 bzero(ip
, sizeof(struct ip
));
58 * Fields zeroed that aren't filled in later:
59 * - IP ID (let the kernel fill it in)
60 * - Offset (we don't send fragments)
61 * - Checksum (let the kernel fill it in)
64 ip
->ip_hl
= sizeof(struct ip
) >> 2;
65 ip
->ip_tos
= 0xc0; /* Internet Control */
66 ip
->ip_ttl
= MAXTTL
; /* applies to unicasts only */
67 ip
->ip_p
= IPPROTO_IGMP
;
69 allhosts_group
= htonl(INADDR_ALLHOSTS_GROUP
);
70 dvmrp_group
= htonl(INADDR_DVMRP_GROUP
);
71 allrtrs_group
= htonl(INADDR_ALLRTRS_GROUP
);
75 #define PIM_REGISTER 1
76 #define PIM_REGISTER_STOP 2
77 #define PIM_JOIN_PRUNE 3
78 #define PIM_RP_REACHABLE 4
81 #define PIM_GRAFT_ACK 7
84 igmp_packet_kind(u_int type
, u_int code
)
86 static char unknown
[20];
89 case IGMP_MEMBERSHIP_QUERY
: return "membership query ";
90 case IGMP_V1_MEMBERSHIP_REPORT
: return "V1 member report ";
91 case IGMP_V2_MEMBERSHIP_REPORT
: return "V2 member report ";
92 case IGMP_V2_LEAVE_GROUP
: return "leave message ";
95 case DVMRP_PROBE
: return "neighbor probe ";
96 case DVMRP_REPORT
: return "route report ";
97 case DVMRP_ASK_NEIGHBORS
: return "neighbor request ";
98 case DVMRP_NEIGHBORS
: return "neighbor list ";
99 case DVMRP_ASK_NEIGHBORS2
: return "neighbor request 2";
100 case DVMRP_NEIGHBORS2
: return "neighbor list 2 ";
101 case DVMRP_PRUNE
: return "prune message ";
102 case DVMRP_GRAFT
: return "graft message ";
103 case DVMRP_GRAFT_ACK
: return "graft message ack ";
104 case DVMRP_INFO_REQUEST
: return "info request ";
105 case DVMRP_INFO_REPLY
: return "info reply ";
107 sprintf(unknown
, "unknown DVMRP %3d ", code
);
112 case PIM_QUERY
: return "PIM Router-Query ";
113 case PIM_REGISTER
: return "PIM Register ";
114 case PIM_REGISTER_STOP
: return "PIM Register-Stop ";
115 case PIM_JOIN_PRUNE
: return "PIM Join/Prune ";
116 case PIM_RP_REACHABLE
: return "PIM RP-Reachable ";
117 case PIM_ASSERT
: return "PIM Assert ";
118 case PIM_GRAFT
: return "PIM Graft ";
119 case PIM_GRAFT_ACK
: return "PIM Graft-Ack ";
121 sprintf(unknown
, "unknown PIM msg%3d", code
);
124 case IGMP_MTRACE
: return "IGMP trace query ";
125 case IGMP_MTRACE_RESP
: return "IGMP trace reply ";
127 sprintf(unknown
, "unk: 0x%02x/0x%02x ", type
, code
);
133 igmp_debug_kind(u_int type
, u_int code
)
136 case IGMP_MEMBERSHIP_QUERY
: return DEBUG_IGMP
;
137 case IGMP_V1_MEMBERSHIP_REPORT
: return DEBUG_IGMP
;
138 case IGMP_V2_MEMBERSHIP_REPORT
: return DEBUG_IGMP
;
139 case IGMP_V2_LEAVE_GROUP
: return DEBUG_IGMP
;
142 case DVMRP_PROBE
: return DEBUG_PEER
;
143 case DVMRP_REPORT
: return DEBUG_ROUTE
;
144 case DVMRP_ASK_NEIGHBORS
: return 0;
145 case DVMRP_NEIGHBORS
: return 0;
146 case DVMRP_ASK_NEIGHBORS2
: return 0;
147 case DVMRP_NEIGHBORS2
: return 0;
148 case DVMRP_PRUNE
: return DEBUG_PRUNE
;
149 case DVMRP_GRAFT
: return DEBUG_PRUNE
;
150 case DVMRP_GRAFT_ACK
: return DEBUG_PRUNE
;
151 case DVMRP_INFO_REQUEST
: return 0;
152 case DVMRP_INFO_REPLY
: return 0;
157 case PIM_QUERY
: return 0;
158 case PIM_REGISTER
: return 0;
159 case PIM_REGISTER_STOP
: return 0;
160 case PIM_JOIN_PRUNE
: return 0;
161 case PIM_RP_REACHABLE
: return 0;
162 case PIM_ASSERT
: return 0;
163 case PIM_GRAFT
: return 0;
164 case PIM_GRAFT_ACK
: return 0;
167 case IGMP_MTRACE
: return DEBUG_TRACE
;
168 case IGMP_MTRACE_RESP
: return DEBUG_TRACE
;
169 default: return DEBUG_IGMP
;
174 * Process a newly received IGMP packet that is sitting in the input
178 accept_igmp(int recvlen
)
180 u_int32 src
, dst
, group
;
183 int ipdatalen
, iphdrlen
, igmpdatalen
;
185 if (recvlen
< sizeof(struct ip
)) {
187 "received packet too short (%u bytes) for IP header", recvlen
);
191 ip
= (struct ip
*)recv_buf
;
192 src
= ip
->ip_src
.s_addr
;
193 dst
= ip
->ip_dst
.s_addr
;
196 * this is most likely a message from the kernel indicating that
197 * a new src grp pair message has arrived and so, it would be
198 * necessary to install a route into the kernel for this.
201 if (src
== 0 || dst
== 0)
202 log(LOG_WARNING
, 0, "kernel request not accurate");
204 add_table_entry(src
, dst
);
208 iphdrlen
= ip
->ip_hl
<< 2;
209 #ifdef RAW_INPUT_IS_RAW
210 ipdatalen
= ntohs(ip
->ip_len
) - iphdrlen
;
212 ipdatalen
= ip
->ip_len
;
214 if (iphdrlen
+ ipdatalen
!= recvlen
) {
216 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
217 inet_fmt(src
, s1
), recvlen
, iphdrlen
, ipdatalen
);
221 igmp
= (struct igmp
*)(recv_buf
+ iphdrlen
);
222 group
= igmp
->igmp_group
.s_addr
;
223 igmpdatalen
= ipdatalen
- IGMP_MINLEN
;
224 if (igmpdatalen
< 0) {
226 "received IP data field too short (%u bytes) for IGMP, from %s",
227 ipdatalen
, inet_fmt(src
, s1
));
231 IF_DEBUG(DEBUG_PKT
|igmp_debug_kind(igmp
->igmp_type
, igmp
->igmp_code
))
232 log(LOG_DEBUG
, 0, "RECV %s from %-15s to %s",
233 igmp_packet_kind(igmp
->igmp_type
, igmp
->igmp_code
),
234 inet_fmt(src
, s1
), inet_fmt(dst
, s2
));
236 switch (igmp
->igmp_type
) {
238 case IGMP_MEMBERSHIP_QUERY
:
239 accept_membership_query(src
, dst
, group
, igmp
->igmp_code
);
242 case IGMP_V1_MEMBERSHIP_REPORT
:
243 case IGMP_V2_MEMBERSHIP_REPORT
:
244 accept_group_report(src
, dst
, group
, igmp
->igmp_type
);
247 case IGMP_V2_LEAVE_GROUP
:
248 accept_leave_message(src
, dst
, group
);
252 group
= ntohl(group
);
254 switch (igmp
->igmp_code
) {
256 accept_probe(src
, dst
,
257 (char *)(igmp
+1), igmpdatalen
, group
);
261 accept_report(src
, dst
,
262 (char *)(igmp
+1), igmpdatalen
, group
);
265 case DVMRP_ASK_NEIGHBORS
:
266 accept_neighbor_request(src
, dst
);
269 case DVMRP_ASK_NEIGHBORS2
:
270 accept_neighbor_request2(src
, dst
);
273 case DVMRP_NEIGHBORS
:
274 accept_neighbors(src
, dst
, (u_char
*)(igmp
+1), igmpdatalen
,
278 case DVMRP_NEIGHBORS2
:
279 accept_neighbors2(src
, dst
, (u_char
*)(igmp
+1), igmpdatalen
,
284 accept_prune(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
288 accept_graft(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
291 case DVMRP_GRAFT_ACK
:
292 accept_g_ack(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
295 case DVMRP_INFO_REQUEST
:
296 accept_info_request(src
, dst
, (char *)(igmp
+1),
300 case DVMRP_INFO_REPLY
:
301 accept_info_reply(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
306 "ignoring unknown DVMRP message code %u from %s to %s",
307 igmp
->igmp_code
, inet_fmt(src
, s1
),
315 case IGMP_MTRACE_RESP
:
319 accept_mtrace(src
, dst
, group
, (char *)(igmp
+1),
320 igmp
->igmp_code
, igmpdatalen
);
325 "ignoring unknown IGMP message type %x from %s to %s",
326 igmp
->igmp_type
, inet_fmt(src
, s1
),
333 * Some IGMP messages are more important than others. This routine
334 * determines the logging level at which to log a send error (often
335 * "No route to host"). This is important when there is asymmetric
336 * reachability and someone is trying to, i.e., mrinfo me periodically.
339 igmp_log_level(u_int type
, u_int code
)
342 case IGMP_MTRACE_RESP
:
347 case DVMRP_NEIGHBORS
:
348 case DVMRP_NEIGHBORS2
:
356 * Construct an IGMP message in the output packet buffer. The caller may
357 * have already placed data in that buffer, of length 'datalen'.
360 build_igmp(u_int32 src
, u_int32 dst
, int type
, int code
, u_int32 group
,
367 ip
= (struct ip
*)send_buf
;
368 ip
->ip_src
.s_addr
= src
;
369 ip
->ip_dst
.s_addr
= dst
;
370 ip
->ip_len
= MIN_IP_HEADER_LEN
+ IGMP_MINLEN
+ datalen
;
371 #ifdef RAW_OUTPUT_IS_RAW
372 ip
->ip_len
= htons(ip
->ip_len
);
374 if (IN_MULTICAST(ntohl(dst
))) {
380 igmp
= (struct igmp
*)(send_buf
+ MIN_IP_HEADER_LEN
);
381 igmp
->igmp_type
= type
;
382 igmp
->igmp_code
= code
;
383 igmp
->igmp_group
.s_addr
= group
;
384 igmp
->igmp_cksum
= 0;
385 igmp
->igmp_cksum
= inet_cksum((u_short
*)igmp
,
386 IGMP_MINLEN
+ datalen
);
390 * Call build_igmp() to build an IGMP message in the output packet buffer.
391 * Then send the message from the interface with IP address 'src' to
395 send_igmp(u_int32 src
, u_int32 dst
, int type
, int code
, u_int32 group
,
398 struct sockaddr_in sdst
;
401 build_igmp(src
, dst
, type
, code
, group
, datalen
);
403 if (IN_MULTICAST(ntohl(dst
))) {
405 if (type
!= IGMP_DVMRP
|| dst
== allhosts_group
) {
411 bzero(&sdst
, sizeof(sdst
));
412 sdst
.sin_family
= AF_INET
;
414 sdst
.sin_len
= sizeof(sdst
);
416 sdst
.sin_addr
.s_addr
= dst
;
417 if (sendto(igmp_socket
, send_buf
,
418 MIN_IP_HEADER_LEN
+ IGMP_MINLEN
+ datalen
, 0,
419 (struct sockaddr
*)&sdst
, sizeof(sdst
)) < 0) {
420 if (errno
== ENETDOWN
)
423 log(igmp_log_level(type
, code
), errno
,
424 "sendto to %s on %s",
425 inet_fmt(dst
, s1
), inet_fmt(src
, s2
));
431 IF_DEBUG(DEBUG_PKT
|igmp_debug_kind(type
, code
))
432 log(LOG_DEBUG
, 0, "SENT %s from %-15s to %s",
433 igmp_packet_kind(type
, code
), src
== INADDR_ANY
? "INADDR_ANY" :
434 inet_fmt(src
, s1
), inet_fmt(dst
, s2
));