Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / mrouted / igmp.c
1 /*
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.
5  *
6  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7  * Leland Stanford Junior University.
8  *
9  *
10  * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp
11  */
12
13 #ifndef lint
14 static const char rcsid[] =
15   "$FreeBSD: src/usr.sbin/mrouted/igmp.c,v 1.16 1999/08/28 01:17:04 peter Exp $";
16 #endif /* not lint */
17
18 #include "defs.h"
19
20 /*
21  * Exported variables.
22  */
23 char            *recv_buf;                   /* input packet buffer         */
24 char            *send_buf;                   /* output packet buffer        */
25 int             igmp_socket;                 /* socket for all network I/O  */
26 u_int32         allhosts_group;              /* All hosts addr in net order */
27 u_int32         allrtrs_group;               /* All-Routers "  in net order */
28 u_int32         dvmrp_group;                 /* DVMRP grp addr in net order */
29 u_int32         dvmrp_genid;                 /* IGMP generation id          */
30
31 /*
32  * Local function definitions.
33  */
34 /* u_char promoted to u_int */
35 static int      igmp_log_level __P((u_int type, u_int code));
36
37 /*
38  * Open and initialize the igmp socket, and fill in the non-changing
39  * IP header fields in the output packet buffer.
40  */
41 void
42 init_igmp()
43 {
44     struct ip *ip;
45
46     recv_buf = malloc(RECV_BUF_SIZE);
47     send_buf = malloc(RECV_BUF_SIZE);
48
49     if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 
50         log(LOG_ERR, errno, "IGMP socket");
51
52     k_hdr_include(TRUE);        /* include IP header when sending */
53     k_set_rcvbuf(256*1024,48*1024);     /* lots of input buffering        */
54     k_set_ttl(1);               /* restrict multicasts to one hop */
55     k_set_loop(FALSE);          /* disable multicast loopback     */
56
57     ip         = (struct ip *)send_buf;
58     bzero(ip, sizeof(struct ip));
59     /*
60      * Fields zeroed that aren't filled in later:
61      * - IP ID (let the kernel fill it in)
62      * - Offset (we don't send fragments)
63      * - Checksum (let the kernel fill it in)
64      */
65     ip->ip_v   = IPVERSION;
66     ip->ip_hl  = sizeof(struct ip) >> 2;
67     ip->ip_tos = 0xc0;          /* Internet Control */
68     ip->ip_ttl = MAXTTL;        /* applies to unicasts only */
69     ip->ip_p   = IPPROTO_IGMP;
70
71     allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
72     dvmrp_group    = htonl(INADDR_DVMRP_GROUP);
73     allrtrs_group  = htonl(INADDR_ALLRTRS_GROUP);
74 }
75
76 #define PIM_QUERY        0
77 #define PIM_REGISTER     1
78 #define PIM_REGISTER_STOP       2
79 #define PIM_JOIN_PRUNE   3
80 #define PIM_RP_REACHABLE 4
81 #define PIM_ASSERT       5
82 #define PIM_GRAFT        6
83 #define PIM_GRAFT_ACK    7
84
85 char *
86 igmp_packet_kind(type, code)
87      u_int type, code;
88 {
89     static char unknown[20];
90
91     switch (type) {
92         case IGMP_MEMBERSHIP_QUERY:             return "membership query  ";
93         case IGMP_V1_MEMBERSHIP_REPORT:         return "V1 member report  ";
94         case IGMP_V2_MEMBERSHIP_REPORT:         return "V2 member report  ";
95         case IGMP_V2_LEAVE_GROUP:               return "leave message     ";
96         case IGMP_DVMRP:
97           switch (code) {
98             case DVMRP_PROBE:                   return "neighbor probe    ";
99             case DVMRP_REPORT:                  return "route report      ";
100             case DVMRP_ASK_NEIGHBORS:           return "neighbor request  ";
101             case DVMRP_NEIGHBORS:               return "neighbor list     ";
102             case DVMRP_ASK_NEIGHBORS2:          return "neighbor request 2";
103             case DVMRP_NEIGHBORS2:              return "neighbor list 2   ";
104             case DVMRP_PRUNE:                   return "prune message     ";
105             case DVMRP_GRAFT:                   return "graft message     ";
106             case DVMRP_GRAFT_ACK:               return "graft message ack ";
107             case DVMRP_INFO_REQUEST:            return "info request      ";
108             case DVMRP_INFO_REPLY:              return "info reply        ";
109             default:
110                     sprintf(unknown, "unknown DVMRP %3d ", code);
111                     return unknown;
112           }
113         case IGMP_PIM:
114           switch (code) {
115             case PIM_QUERY:                     return "PIM Router-Query  ";
116             case PIM_REGISTER:                  return "PIM Register      ";
117             case PIM_REGISTER_STOP:             return "PIM Register-Stop ";
118             case PIM_JOIN_PRUNE:                return "PIM Join/Prune    ";
119             case PIM_RP_REACHABLE:              return "PIM RP-Reachable  ";
120             case PIM_ASSERT:                    return "PIM Assert        ";
121             case PIM_GRAFT:                     return "PIM Graft         ";
122             case PIM_GRAFT_ACK:                 return "PIM Graft-Ack     ";
123             default:
124                     sprintf(unknown, "unknown PIM msg%3d", code);
125                     return unknown;
126           }
127         case IGMP_MTRACE:                       return "IGMP trace query  ";
128         case IGMP_MTRACE_RESP:                  return "IGMP trace reply  ";
129         default:
130                 sprintf(unknown, "unk: 0x%02x/0x%02x    ", type, code);
131                 return unknown;
132     }
133 }
134
135 int
136 igmp_debug_kind(type, code)
137      u_int type, code;
138 {
139     switch (type) {
140         case IGMP_MEMBERSHIP_QUERY:             return DEBUG_IGMP;
141         case IGMP_V1_MEMBERSHIP_REPORT:         return DEBUG_IGMP;
142         case IGMP_V2_MEMBERSHIP_REPORT:         return DEBUG_IGMP;
143         case IGMP_V2_LEAVE_GROUP:               return DEBUG_IGMP;
144         case IGMP_DVMRP:
145           switch (code) {
146             case DVMRP_PROBE:                   return DEBUG_PEER;
147             case DVMRP_REPORT:                  return DEBUG_ROUTE;
148             case DVMRP_ASK_NEIGHBORS:           return 0;
149             case DVMRP_NEIGHBORS:               return 0;
150             case DVMRP_ASK_NEIGHBORS2:          return 0;
151             case DVMRP_NEIGHBORS2:              return 0;
152             case DVMRP_PRUNE:                   return DEBUG_PRUNE;
153             case DVMRP_GRAFT:                   return DEBUG_PRUNE;
154             case DVMRP_GRAFT_ACK:               return DEBUG_PRUNE;
155             case DVMRP_INFO_REQUEST:            return 0;
156             case DVMRP_INFO_REPLY:              return 0;
157             default:                            return 0;
158           }
159         case IGMP_PIM:
160           switch (code) {
161             case PIM_QUERY:                     return 0;
162             case PIM_REGISTER:                  return 0;
163             case PIM_REGISTER_STOP:             return 0;
164             case PIM_JOIN_PRUNE:                return 0;
165             case PIM_RP_REACHABLE:              return 0;
166             case PIM_ASSERT:                    return 0;
167             case PIM_GRAFT:                     return 0;
168             case PIM_GRAFT_ACK:                 return 0;
169             default:                            return 0;
170           }
171         case IGMP_MTRACE:                       return DEBUG_TRACE;
172         case IGMP_MTRACE_RESP:                  return DEBUG_TRACE;
173         default:                                return DEBUG_IGMP;
174     }
175 }
176
177 /*
178  * Process a newly received IGMP packet that is sitting in the input
179  * packet buffer.
180  */
181 void
182 accept_igmp(recvlen)
183     int recvlen;
184 {
185     register u_int32 src, dst, group;
186     struct ip *ip;
187     struct igmp *igmp;
188     int ipdatalen, iphdrlen, igmpdatalen;
189
190     if (recvlen < sizeof(struct ip)) {
191         log(LOG_WARNING, 0,
192             "received packet too short (%u bytes) for IP header", recvlen);
193         return;
194     }
195
196     ip        = (struct ip *)recv_buf;
197     src       = ip->ip_src.s_addr;
198     dst       = ip->ip_dst.s_addr;
199
200     /* 
201      * this is most likely a message from the kernel indicating that
202      * a new src grp pair message has arrived and so, it would be 
203      * necessary to install a route into the kernel for this.
204      */
205     if (ip->ip_p == 0) {
206         if (src == 0 || dst == 0)
207             log(LOG_WARNING, 0, "kernel request not accurate");
208         else
209             add_table_entry(src, dst);
210         return;
211     }
212
213     iphdrlen  = ip->ip_hl << 2;
214 #ifdef RAW_INPUT_IS_RAW
215     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
216 #else
217     ipdatalen = ip->ip_len;
218 #endif
219     if (iphdrlen + ipdatalen != recvlen) {
220         log(LOG_WARNING, 0,
221             "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
222             inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen);
223         return;
224     }
225
226     igmp        = (struct igmp *)(recv_buf + iphdrlen);
227     group       = igmp->igmp_group.s_addr;
228     igmpdatalen = ipdatalen - IGMP_MINLEN;
229     if (igmpdatalen < 0) {
230         log(LOG_WARNING, 0,
231             "received IP data field too short (%u bytes) for IGMP, from %s",
232             ipdatalen, inet_fmt(src, s1));
233         return;
234     }
235
236     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
237     log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
238         igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
239         inet_fmt(src, s1), inet_fmt(dst, s2));
240
241     switch (igmp->igmp_type) {
242
243         case IGMP_MEMBERSHIP_QUERY:
244             accept_membership_query(src, dst, group, igmp->igmp_code);
245             return;
246
247         case IGMP_V1_MEMBERSHIP_REPORT:
248         case IGMP_V2_MEMBERSHIP_REPORT:
249             accept_group_report(src, dst, group, igmp->igmp_type);
250             return;
251             
252         case IGMP_V2_LEAVE_GROUP:
253             accept_leave_message(src, dst, group);
254             return;
255
256         case IGMP_DVMRP:
257             group = ntohl(group);
258
259             switch (igmp->igmp_code) {
260                 case DVMRP_PROBE:
261                     accept_probe(src, dst,
262                                  (char *)(igmp+1), igmpdatalen, group);
263                     return;
264
265                 case DVMRP_REPORT:
266                     accept_report(src, dst,
267                                   (char *)(igmp+1), igmpdatalen, group);
268                     return;
269
270                 case DVMRP_ASK_NEIGHBORS:
271                     accept_neighbor_request(src, dst);
272                     return;
273
274                 case DVMRP_ASK_NEIGHBORS2:
275                     accept_neighbor_request2(src, dst);
276                     return;
277
278                 case DVMRP_NEIGHBORS:
279                     accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
280                                              group);
281                     return;
282
283                 case DVMRP_NEIGHBORS2:
284                     accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
285                                              group);
286                     return;
287
288                 case DVMRP_PRUNE:
289                     accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
290                     return;
291
292                 case DVMRP_GRAFT:
293                     accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
294                     return;
295
296                 case DVMRP_GRAFT_ACK:
297                     accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
298                     return;
299
300                 case DVMRP_INFO_REQUEST:
301                     accept_info_request(src, dst, (char *)(igmp+1),
302                                 igmpdatalen);
303                     return;
304
305                 case DVMRP_INFO_REPLY:
306                     accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen);
307                     return;
308
309                 default:
310                     log(LOG_INFO, 0,
311                      "ignoring unknown DVMRP message code %u from %s to %s",
312                      igmp->igmp_code, inet_fmt(src, s1),
313                      inet_fmt(dst, s2));
314                     return;
315             }
316
317         case IGMP_PIM:
318             return;
319
320         case IGMP_MTRACE_RESP:
321             return;
322
323         case IGMP_MTRACE:
324             accept_mtrace(src, dst, group, (char *)(igmp+1),
325                    igmp->igmp_code, igmpdatalen);
326             return;
327
328         default:
329             log(LOG_INFO, 0,
330                 "ignoring unknown IGMP message type %x from %s to %s",
331                 igmp->igmp_type, inet_fmt(src, s1),
332                 inet_fmt(dst, s2));
333             return;
334     }
335 }
336
337 /*
338  * Some IGMP messages are more important than others.  This routine
339  * determines the logging level at which to log a send error (often
340  * "No route to host").  This is important when there is asymmetric
341  * reachability and someone is trying to, i.e., mrinfo me periodically.
342  */
343 static int
344 igmp_log_level(type, code)
345     u_int type, code;
346 {
347     switch (type) {
348         case IGMP_MTRACE_RESP:
349             return LOG_INFO;
350
351         case IGMP_DVMRP:
352           switch (code) {
353             case DVMRP_NEIGHBORS:
354             case DVMRP_NEIGHBORS2:
355                 return LOG_INFO;
356           }
357     }
358     return LOG_WARNING;
359 }
360
361 /*
362  * Construct an IGMP message in the output packet buffer.  The caller may
363  * have already placed data in that buffer, of length 'datalen'.
364  */
365 void
366 build_igmp(src, dst, type, code, group, datalen)
367     u_int32 src, dst;
368     int type, code;
369     u_int32 group;
370     int datalen;
371 {
372     struct ip *ip;
373     struct igmp *igmp;
374     extern int curttl;
375
376     ip                      = (struct ip *)send_buf;
377     ip->ip_src.s_addr       = src;
378     ip->ip_dst.s_addr       = dst;
379     ip->ip_len              = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
380 #ifdef RAW_OUTPUT_IS_RAW
381     ip->ip_len              = htons(ip->ip_len);
382 #endif
383     if (IN_MULTICAST(ntohl(dst))) {
384         ip->ip_ttl = curttl;
385     } else {
386         ip->ip_ttl = MAXTTL;
387     }
388
389     igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
390     igmp->igmp_type         = type;
391     igmp->igmp_code         = code;
392     igmp->igmp_group.s_addr = group;
393     igmp->igmp_cksum        = 0;
394     igmp->igmp_cksum        = inet_cksum((u_short *)igmp,
395                                          IGMP_MINLEN + datalen);
396 }
397
398 /* 
399  * Call build_igmp() to build an IGMP message in the output packet buffer.
400  * Then send the message from the interface with IP address 'src' to
401  * destination 'dst'.
402  */
403 void
404 send_igmp(src, dst, type, code, group, datalen)
405     u_int32 src, dst;
406     int type, code;
407     u_int32 group;
408     int datalen;
409 {
410     struct sockaddr_in sdst;
411     int setloop = 0;
412
413     build_igmp(src, dst, type, code, group, datalen);
414
415     if (IN_MULTICAST(ntohl(dst))) {
416         k_set_if(src);
417         if (type != IGMP_DVMRP || dst == allhosts_group) {
418             setloop = 1;
419             k_set_loop(TRUE);
420         }
421     }
422
423     bzero(&sdst, sizeof(sdst));
424     sdst.sin_family = AF_INET;
425 #ifdef HAVE_SA_LEN
426     sdst.sin_len = sizeof(sdst);
427 #endif
428     sdst.sin_addr.s_addr = dst;
429     if (sendto(igmp_socket, send_buf,
430                         MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen, 0,
431                         (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
432         if (errno == ENETDOWN)
433             check_vif_state();
434         else
435             log(igmp_log_level(type, code), errno,
436                 "sendto to %s on %s",
437                 inet_fmt(dst, s1), inet_fmt(src, s2));
438     }
439
440     if (setloop)
441             k_set_loop(FALSE);
442
443     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
444     log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
445         igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
446                                  inet_fmt(src, s1), inet_fmt(dst, s2));
447 }