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