Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[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.4 2004/03/15 18:10:28 dillon 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(void)
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         dolog(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(u_int type, u_int code)
85 {
86     static char unknown[20];
87
88     switch (type) {
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     ";
93         case IGMP_DVMRP:
94           switch (code) {
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        ";
106             default:
107                     sprintf(unknown, "unknown DVMRP %3d ", code);
108                     return unknown;
109           }
110         case IGMP_PIM:
111           switch (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     ";
120             default:
121                     sprintf(unknown, "unknown PIM msg%3d", code);
122                     return unknown;
123           }
124         case IGMP_MTRACE:                       return "IGMP trace query  ";
125         case IGMP_MTRACE_RESP:                  return "IGMP trace reply  ";
126         default:
127                 sprintf(unknown, "unk: 0x%02x/0x%02x    ", type, code);
128                 return unknown;
129     }
130 }
131
132 int
133 igmp_debug_kind(u_int type, u_int code)
134 {
135     switch (type) {
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;
140         case IGMP_DVMRP:
141           switch (code) {
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;
153             default:                            return 0;
154           }
155         case IGMP_PIM:
156           switch (code) {
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;
165             default:                            return 0;
166           }
167         case IGMP_MTRACE:                       return DEBUG_TRACE;
168         case IGMP_MTRACE_RESP:                  return DEBUG_TRACE;
169         default:                                return DEBUG_IGMP;
170     }
171 }
172
173 /*
174  * Process a newly received IGMP packet that is sitting in the input
175  * packet buffer.
176  */
177 void
178 accept_igmp(int recvlen)
179 {
180     u_int32 src, dst, group;
181     struct ip *ip;
182     struct igmp *igmp;
183     int ipdatalen, iphdrlen, igmpdatalen;
184
185     if (recvlen < sizeof(struct ip)) {
186         dolog(LOG_WARNING, 0,
187             "received packet too short (%u bytes) for IP header", recvlen);
188         return;
189     }
190
191     ip        = (struct ip *)recv_buf;
192     src       = ip->ip_src.s_addr;
193     dst       = ip->ip_dst.s_addr;
194
195     /* 
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.
199      */
200     if (ip->ip_p == 0) {
201         if (src == 0 || dst == 0)
202             dolog(LOG_WARNING, 0, "kernel request not accurate");
203         else
204             add_table_entry(src, dst);
205         return;
206     }
207
208     iphdrlen  = ip->ip_hl << 2;
209 #ifdef RAW_INPUT_IS_RAW
210     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
211 #else
212     ipdatalen = ip->ip_len;
213 #endif
214     if (iphdrlen + ipdatalen != recvlen) {
215         dolog(LOG_WARNING, 0,
216             "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
217             inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen);
218         return;
219     }
220
221     igmp        = (struct igmp *)(recv_buf + iphdrlen);
222     group       = igmp->igmp_group.s_addr;
223     igmpdatalen = ipdatalen - IGMP_MINLEN;
224     if (igmpdatalen < 0) {
225         dolog(LOG_WARNING, 0,
226             "received IP data field too short (%u bytes) for IGMP, from %s",
227             ipdatalen, inet_fmt(src, s1));
228         return;
229     }
230
231     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
232     dolog(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));
235
236     switch (igmp->igmp_type) {
237
238         case IGMP_MEMBERSHIP_QUERY:
239             accept_membership_query(src, dst, group, igmp->igmp_code);
240             return;
241
242         case IGMP_V1_MEMBERSHIP_REPORT:
243         case IGMP_V2_MEMBERSHIP_REPORT:
244             accept_group_report(src, dst, group, igmp->igmp_type);
245             return;
246             
247         case IGMP_V2_LEAVE_GROUP:
248             accept_leave_message(src, dst, group);
249             return;
250
251         case IGMP_DVMRP:
252             group = ntohl(group);
253
254             switch (igmp->igmp_code) {
255                 case DVMRP_PROBE:
256                     accept_probe(src, dst,
257                                  (char *)(igmp+1), igmpdatalen, group);
258                     return;
259
260                 case DVMRP_REPORT:
261                     accept_report(src, dst,
262                                   (char *)(igmp+1), igmpdatalen, group);
263                     return;
264
265                 case DVMRP_ASK_NEIGHBORS:
266                     accept_neighbor_request(src, dst);
267                     return;
268
269                 case DVMRP_ASK_NEIGHBORS2:
270                     accept_neighbor_request2(src, dst);
271                     return;
272
273                 case DVMRP_NEIGHBORS:
274                     accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
275                                              group);
276                     return;
277
278                 case DVMRP_NEIGHBORS2:
279                     accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
280                                              group);
281                     return;
282
283                 case DVMRP_PRUNE:
284                     accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
285                     return;
286
287                 case DVMRP_GRAFT:
288                     accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
289                     return;
290
291                 case DVMRP_GRAFT_ACK:
292                     accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
293                     return;
294
295                 case DVMRP_INFO_REQUEST:
296                     accept_info_request(src, dst, (char *)(igmp+1),
297                                 igmpdatalen);
298                     return;
299
300                 case DVMRP_INFO_REPLY:
301                     accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen);
302                     return;
303
304                 default:
305                     dolog(LOG_INFO, 0,
306                      "ignoring unknown DVMRP message code %u from %s to %s",
307                      igmp->igmp_code, inet_fmt(src, s1),
308                      inet_fmt(dst, s2));
309                     return;
310             }
311
312         case IGMP_PIM:
313             return;
314
315         case IGMP_MTRACE_RESP:
316             return;
317
318         case IGMP_MTRACE:
319             accept_mtrace(src, dst, group, (char *)(igmp+1),
320                    igmp->igmp_code, igmpdatalen);
321             return;
322
323         default:
324             dolog(LOG_INFO, 0,
325                 "ignoring unknown IGMP message type %x from %s to %s",
326                 igmp->igmp_type, inet_fmt(src, s1),
327                 inet_fmt(dst, s2));
328             return;
329     }
330 }
331
332 /*
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.
337  */
338 static int
339 igmp_log_level(u_int type, u_int code)
340 {
341     switch (type) {
342         case IGMP_MTRACE_RESP:
343             return LOG_INFO;
344
345         case IGMP_DVMRP:
346           switch (code) {
347             case DVMRP_NEIGHBORS:
348             case DVMRP_NEIGHBORS2:
349                 return LOG_INFO;
350           }
351     }
352     return LOG_WARNING;
353 }
354
355 /*
356  * Construct an IGMP message in the output packet buffer.  The caller may
357  * have already placed data in that buffer, of length 'datalen'.
358  */
359 void
360 build_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group,
361            int datalen)
362 {
363     struct ip *ip;
364     struct igmp *igmp;
365     extern int curttl;
366
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);
373 #endif
374     if (IN_MULTICAST(ntohl(dst))) {
375         ip->ip_ttl = curttl;
376     } else {
377         ip->ip_ttl = MAXTTL;
378     }
379
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);
387 }
388
389 /* 
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
392  * destination 'dst'.
393  */
394 void
395 send_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group,
396           int datalen)
397 {
398     struct sockaddr_in sdst;
399     int setloop = 0;
400
401     build_igmp(src, dst, type, code, group, datalen);
402
403     if (IN_MULTICAST(ntohl(dst))) {
404         k_set_if(src);
405         if (type != IGMP_DVMRP || dst == allhosts_group) {
406             setloop = 1;
407             k_set_loop(TRUE);
408         }
409     }
410
411     bzero(&sdst, sizeof(sdst));
412     sdst.sin_family = AF_INET;
413 #ifdef HAVE_SA_LEN
414     sdst.sin_len = sizeof(sdst);
415 #endif
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)
421             check_vif_state();
422         else
423             dolog(igmp_log_level(type, code), errno,
424                 "sendto to %s on %s",
425                 inet_fmt(dst, s1), inet_fmt(src, s2));
426     }
427
428     if (setloop)
429             k_set_loop(FALSE);
430
431     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
432     dolog(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));
435 }