1 /* $NetBSD: igmp.c,v 1.13 2006/05/11 21:17:02 mrg Exp $ */
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.
8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9 * Leland Stanford Junior University.
19 char *recv_buf
; /* input packet buffer */
20 char *send_buf
; /* output packet buffer */
21 size_t send_buflen
; /* output packet buffer */
22 int igmp_socket
; /* socket for all network I/O */
23 u_int32_t allhosts_group
; /* All hosts addr in net order */
24 u_int32_t allrtrs_group
; /* All-Routers " in net order */
25 u_int32_t dvmrp_group
; /* DVMRP grp addr in net order */
26 u_int32_t dvmrp_genid
; /* IGMP generation id */
29 * Local function definitions.
31 /* u_char promoted to u_int */
32 static const char * packet_kind(u_int type
, u_int code
);
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
);
46 send_buflen
= RECV_BUF_SIZE
;
48 if ((igmp_socket
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
)) < 0)
49 logit(LOG_ERR
, errno
, "IGMP socket");
51 k_hdr_include(TRUE
); /* include IP header when sending */
52 k_set_rcvbuf(48*1024); /* lots of input buffering */
53 k_set_ttl(1); /* restrict multicasts to one hop */
54 k_set_loop(FALSE
); /* disable multicast loopback */
56 ip
= (struct ip
*)send_buf
;
57 ip
->ip_hl
= sizeof(struct ip
) >> 2;
61 ip
->ip_p
= IPPROTO_IGMP
;
62 ip
->ip_ttl
= MAXTTL
; /* applies to unicasts only */
64 allhosts_group
= htonl(INADDR_ALLHOSTS_GROUP
);
65 dvmrp_group
= htonl(INADDR_DVMRP_GROUP
);
66 allrtrs_group
= htonl(INADDR_ALLRTRS_GROUP
);
70 #define PIM_REGISTER 1
71 #define PIM_REGISTER_STOP 2
72 #define PIM_JOIN_PRUNE 3
73 #define PIM_RP_REACHABLE 4
76 #define PIM_GRAFT_ACK 7
79 packet_kind(u_int type
, u_int code
)
82 case IGMP_HOST_MEMBERSHIP_QUERY
: return "membership query ";
83 case IGMP_v1_HOST_MEMBERSHIP_REPORT
: return "v1 member report ";
84 case IGMP_v2_HOST_MEMBERSHIP_REPORT
: return "v2 member report ";
85 case IGMP_HOST_LEAVE_MESSAGE
: return "leave message ";
88 case DVMRP_PROBE
: return "neighbor probe ";
89 case DVMRP_REPORT
: return "route report ";
90 case DVMRP_ASK_NEIGHBORS
: return "neighbor request ";
91 case DVMRP_NEIGHBORS
: return "neighbor list ";
92 case DVMRP_ASK_NEIGHBORS2
: return "neighbor request 2";
93 case DVMRP_NEIGHBORS2
: return "neighbor list 2 ";
94 case DVMRP_PRUNE
: return "prune message ";
95 case DVMRP_GRAFT
: return "graft message ";
96 case DVMRP_GRAFT_ACK
: return "graft message ack ";
97 case DVMRP_INFO_REQUEST
: return "info request ";
98 case DVMRP_INFO_REPLY
: return "info reply ";
99 default: return "unknown DVMRP msg ";
103 case PIM_QUERY
: return "PIM Router-Query ";
104 case PIM_REGISTER
: return "PIM Register ";
105 case PIM_REGISTER_STOP
: return "PIM Register-Stop ";
106 case PIM_JOIN_PRUNE
: return "PIM Join/Prune ";
107 case PIM_RP_REACHABLE
: return "PIM RP-Reachable ";
108 case PIM_ASSERT
: return "PIM Assert ";
109 case PIM_GRAFT
: return "PIM Graft ";
110 case PIM_GRAFT_ACK
: return "PIM Graft-Ack ";
111 default: return "unknown PIM msg ";
113 case IGMP_MTRACE_QUERY
: return "IGMP trace query ";
114 case IGMP_MTRACE_REPLY
: return "IGMP trace reply ";
115 default: return "unknown IGMP msg ";
120 * Process a newly received IGMP packet that is sitting in the input
124 accept_igmp(int recvlen
)
126 u_int32_t src
, dst
, group
;
129 int ipdatalen
, iphdrlen
, igmpdatalen
;
131 if (recvlen
< (int)sizeof(struct ip
)) {
132 logit(LOG_WARNING
, 0,
133 "received packet too short (%u bytes) for IP header", recvlen
);
137 ip
= (struct ip
*)recv_buf
;
138 src
= ip
->ip_src
.s_addr
;
139 dst
= ip
->ip_dst
.s_addr
;
142 * this is most likely a message from the kernel indicating that
143 * a new src grp pair message has arrived and so, it would be
144 * necessary to install a route into the kernel for this.
147 if (src
== 0 || dst
== 0)
148 logit(LOG_WARNING
, 0, "kernel request not accurate");
150 add_table_entry(src
, dst
);
154 iphdrlen
= ip
->ip_hl
<< 2;
155 ipdatalen
= ip
->ip_len
;
156 if (iphdrlen
+ ipdatalen
!= recvlen
) {
157 logit(LOG_WARNING
, 0,
158 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
159 inet_fmt(src
), recvlen
, iphdrlen
, ipdatalen
);
163 igmp
= (struct igmp
*)(recv_buf
+ iphdrlen
);
164 group
= igmp
->igmp_group
.s_addr
;
165 igmpdatalen
= ipdatalen
- IGMP_MINLEN
;
166 if (igmpdatalen
< 0) {
167 logit(LOG_WARNING
, 0,
168 "received IP data field too short (%u bytes) for IGMP, from %s",
169 ipdatalen
, inet_fmt(src
));
173 logit(LOG_DEBUG
, 0, "RECV %s from %-15s to %s",
174 packet_kind(igmp
->igmp_type
, igmp
->igmp_code
),
175 inet_fmt(src
), inet_fmt(dst
));
177 switch (igmp
->igmp_type
) {
179 case IGMP_HOST_MEMBERSHIP_QUERY
:
180 accept_membership_query(src
, dst
, group
, igmp
->igmp_code
);
183 case IGMP_v1_HOST_MEMBERSHIP_REPORT
:
184 case IGMP_v2_HOST_MEMBERSHIP_REPORT
:
185 accept_group_report(src
, dst
, group
, igmp
->igmp_type
);
188 case IGMP_HOST_LEAVE_MESSAGE
:
189 accept_leave_message(src
, dst
, group
);
193 group
= ntohl(group
);
195 switch (igmp
->igmp_code
) {
197 accept_probe(src
, dst
,
198 (char *)(igmp
+1), igmpdatalen
, group
);
202 accept_report(src
, dst
,
203 (char *)(igmp
+1), igmpdatalen
, group
);
206 case DVMRP_ASK_NEIGHBORS
:
207 accept_neighbor_request(src
, dst
);
210 case DVMRP_ASK_NEIGHBORS2
:
211 accept_neighbor_request2(src
, dst
);
214 case DVMRP_NEIGHBORS
:
215 accept_neighbors(src
, dst
, (u_char
*)(igmp
+1), igmpdatalen
,
219 case DVMRP_NEIGHBORS2
:
220 accept_neighbors2(src
, dst
, (u_char
*)(igmp
+1), igmpdatalen
,
225 accept_prune(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
229 accept_graft(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
232 case DVMRP_GRAFT_ACK
:
233 accept_g_ack(src
, dst
, (char *)(igmp
+1), igmpdatalen
);
236 case DVMRP_INFO_REQUEST
:
237 accept_info_request(src
, dst
, (u_char
*)(igmp
+1),
241 case DVMRP_INFO_REPLY
:
242 accept_info_reply(src
, dst
, (u_char
*)(igmp
+1), igmpdatalen
);
247 "ignoring unknown DVMRP message code %u from %s to %s",
248 igmp
->igmp_code
, inet_fmt(src
),
256 case IGMP_MTRACE_REPLY
:
259 case IGMP_MTRACE_QUERY
:
260 accept_mtrace(src
, dst
, group
, (char *)(igmp
+1),
261 igmp
->igmp_code
, igmpdatalen
);
266 "ignoring unknown IGMP message type %x from %s to %s",
267 igmp
->igmp_type
, inet_fmt(src
),
274 * Some IGMP messages are more important than others. This routine
275 * determines the logging level at which to log a send error (often
276 * "No route to host"). This is important when there is asymmetric
277 * reachability and someone is trying to, i.e., mrinfo me periodically.
280 igmp_log_level(u_int type
, u_int code
)
283 case IGMP_MTRACE_REPLY
:
288 case DVMRP_NEIGHBORS
:
289 case DVMRP_NEIGHBORS2
:
297 * Construct an IGMP message in the output packet buffer. The caller may
298 * have already placed data in that buffer, of length 'datalen'. Then send
299 * the message from the interface with IP address 'src' to destination 'dst'.
302 send_igmp(u_int32_t src
, u_int32_t dst
, int type
, int code
, u_int32_t group
,
305 struct sockaddr_in sdst
;
311 ip
= (struct ip
*)send_buf
;
312 ip
->ip_src
.s_addr
= src
;
313 ip
->ip_dst
.s_addr
= dst
;
314 ip
->ip_len
= MIN_IP_HEADER_LEN
+ IGMP_MINLEN
+ datalen
;
316 igmp
= (struct igmp
*)(send_buf
+ MIN_IP_HEADER_LEN
);
317 igmp
->igmp_type
= type
;
318 igmp
->igmp_code
= code
;
319 igmp
->igmp_group
.s_addr
= group
;
320 igmp
->igmp_cksum
= 0;
321 igmp
->igmp_cksum
= inet_cksum((u_int16_t
*)igmp
,
322 IGMP_MINLEN
+ datalen
);
324 if (IN_MULTICAST(ntohl(dst
))) {
326 if (type
!= IGMP_DVMRP
) {
332 bzero(&sdst
, sizeof(sdst
));
333 sdst
.sin_family
= AF_INET
;
334 #if (defined(BSD) && (BSD >= 199103))
335 sdst
.sin_len
= sizeof(sdst
);
337 sdst
.sin_addr
.s_addr
= dst
;
338 if (sendto(igmp_socket
, send_buf
, ip
->ip_len
, 0,
339 (struct sockaddr
*)&sdst
, sizeof(sdst
)) < 0) {
340 if (errno
== ENETDOWN
)
343 logit(igmp_log_level(type
, code
), errno
,
344 "sendto to %s on %s",
345 inet_fmt(dst
), inet_fmt(src
));
351 logit(LOG_DEBUG
, 0, "SENT %s from %-15s to %s",
352 packet_kind(type
, code
), inet_fmt(src
),