Import Upstream version 1.2.2
[quagga-debian.git] / pimd / pim_join.c
1 /*
2   PIM for Quagga
3   Copyright (C) 2008  Everton da Silva Marques
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14   
15   You should have received a copy of the GNU General Public License
16   along with this program; see the file COPYING; if not, write to the
17   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18   MA 02110-1301 USA
19   
20   $QuaggaId: $Format:%an, %ai, %h$ $
21 */
22
23 #include <zebra.h>
24
25 #include "log.h"
26 #include "prefix.h"
27
28 #include "pimd.h"
29 #include "pim_str.h"
30 #include "pim_tlv.h"
31 #include "pim_msg.h"
32 #include "pim_pim.h"
33 #include "pim_join.h"
34 #include "pim_iface.h"
35 #include "pim_hello.h"
36 #include "pim_ifchannel.h"
37
38 static void on_trace(const char *label,
39                      struct interface *ifp, struct in_addr src)
40 {
41   if (PIM_DEBUG_PIM_TRACE) {
42     char src_str[100];
43     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
44     zlog_debug("%s: from %s on %s",
45                label, src_str, ifp->name);
46   }
47 }
48
49 static void recv_join(struct interface *ifp,
50                       struct pim_neighbor *neigh,
51                       uint16_t holdtime,
52                       struct in_addr upstream,
53                       struct in_addr group,
54                       struct in_addr source,
55                       uint8_t source_flags)
56 {
57   if (PIM_DEBUG_PIM_TRACE) {
58     char up_str[100];
59     char src_str[100];
60     char grp_str[100];
61     char neigh_str[100];
62     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
63     pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
64     pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
65     pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
66     zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
67               __PRETTY_FUNCTION__,
68               src_str, grp_str,
69               source_flags & PIM_RPT_BIT_MASK,
70               source_flags & PIM_WILDCARD_BIT_MASK,
71               up_str, holdtime, neigh_str, ifp->name);
72   }
73   
74   /* Restart join expiry timer */
75   pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
76                          source, group, source_flags, holdtime);
77 }
78
79 static void recv_prune(struct interface *ifp,
80                        struct pim_neighbor *neigh,
81                        uint16_t holdtime,
82                        struct in_addr upstream,
83                        struct in_addr group,
84                        struct in_addr source,
85                        uint8_t source_flags)
86 {
87   if (PIM_DEBUG_PIM_TRACE) {
88     char up_str[100];
89     char src_str[100];
90     char grp_str[100];
91     char neigh_str[100];
92     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
93     pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
94     pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
95     pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
96     zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
97               __PRETTY_FUNCTION__,
98               src_str, grp_str,
99               source_flags & PIM_RPT_BIT_MASK,
100               source_flags & PIM_WILDCARD_BIT_MASK,
101               up_str, holdtime, neigh_str, ifp->name);
102   }
103   
104   pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
105 }
106
107 int pim_joinprune_recv(struct interface *ifp,
108                        struct pim_neighbor *neigh,
109                        struct in_addr src_addr,
110                        uint8_t *tlv_buf, int tlv_buf_size)
111 {
112   struct prefix   msg_upstream_addr;
113   uint8_t         msg_num_groups;
114   uint16_t        msg_holdtime;
115   int             addr_offset;
116   uint8_t        *buf;
117   uint8_t        *pastend;
118   int             remain;
119   int             group;
120
121   on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
122
123   buf     = tlv_buf;
124   pastend = tlv_buf + tlv_buf_size;
125
126   /*
127     Parse ucast addr
128   */
129   addr_offset = pim_parse_addr_ucast(ifp->name, src_addr,
130                                      &msg_upstream_addr,
131                                      buf, pastend - buf);
132 #if 0
133   zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d",
134             __PRETTY_FUNCTION__,
135             addr_offset);
136 #endif
137   if (addr_offset < 1) {
138     char src_str[100];
139     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
140     zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
141               __PRETTY_FUNCTION__,
142               src_str, ifp->name);
143     return -1;
144   }
145   buf += addr_offset;
146
147   /*
148     Check upstream address family
149    */
150   if (msg_upstream_addr.family != AF_INET) {
151     if (PIM_DEBUG_PIM_TRACE) {
152       char src_str[100];
153       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
154       zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
155                 __PRETTY_FUNCTION__,
156                 msg_upstream_addr.family, src_str, ifp->name);
157     }
158     return -2;
159   }
160
161   remain = pastend - buf;
162   if (remain < 4) {
163     char src_str[100];
164     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
165     zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
166               __PRETTY_FUNCTION__,
167               remain, 4, src_str, ifp->name);
168     return -4;
169   }
170
171   ++buf; /* skip reserved byte */
172   msg_num_groups = *(const uint8_t *) buf;
173   ++buf;
174   msg_holdtime = ntohs(*(const uint16_t *) buf);
175   ++buf;
176   ++buf;
177
178   if (PIM_DEBUG_PIM_TRACE) {
179     char src_str[100];
180     char upstream_str[100];
181     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
182     pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
183                    upstream_str, sizeof(upstream_str));
184     zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
185               __PRETTY_FUNCTION__,
186               upstream_str, msg_num_groups, msg_holdtime,
187               src_str, ifp->name);
188   }
189
190   /* Scan groups */
191   for (group = 0; group < msg_num_groups; ++group) {
192     struct prefix msg_group_addr;
193     struct prefix msg_source_addr;
194     uint8_t       msg_source_flags;
195     uint16_t      msg_num_joined_sources;
196     uint16_t      msg_num_pruned_sources;
197     int           source;
198
199     addr_offset = pim_parse_addr_group(ifp->name, src_addr,
200                                        &msg_group_addr,
201                                        buf, pastend - buf);
202 #if 0
203     zlog_warn("%s: pim_parse_addr_group addr_offset=%d",
204               __PRETTY_FUNCTION__,
205               addr_offset);
206 #endif
207     if (addr_offset < 1) {
208       return -5;
209     }
210     buf += addr_offset;
211
212     remain = pastend - buf;
213     if (remain < 4) {
214       char src_str[100];
215       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
216       zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
217                 __PRETTY_FUNCTION__,
218                 remain, 4, src_str, ifp->name);
219       return -6;
220     }
221
222     msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
223     buf += 2;
224     msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
225     buf += 2;
226
227     if (PIM_DEBUG_PIM_TRACE) {
228       char src_str[100];
229       char upstream_str[100];
230       char group_str[100];
231       pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
232       pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
233                      upstream_str, sizeof(upstream_str));
234       pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
235                      group_str, sizeof(group_str));
236       zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
237                 __PRETTY_FUNCTION__,
238                 upstream_str, group_str, msg_group_addr.prefixlen,
239                 msg_num_joined_sources, msg_num_pruned_sources,
240                 src_str, ifp->name);
241     }
242
243     /* Scan joined sources */
244     for (source = 0; source < msg_num_joined_sources; ++source) {
245       addr_offset = pim_parse_addr_source(ifp->name, src_addr,
246                                           &msg_source_addr,
247                                           &msg_source_flags,
248                                           buf, pastend - buf);
249 #if 0
250       zlog_warn("%s: pim_parse_addr_source addr_offset=%d",
251                 __PRETTY_FUNCTION__,
252                 addr_offset);
253 #endif
254       if (addr_offset < 1) {
255         return -7;
256       }
257
258       buf += addr_offset;
259
260       recv_join(ifp, neigh, msg_holdtime,
261                 msg_upstream_addr.u.prefix4,
262                 msg_group_addr.u.prefix4,
263                 msg_source_addr.u.prefix4,
264                 msg_source_flags);
265     }
266
267     /* Scan pruned sources */
268     for (source = 0; source < msg_num_pruned_sources; ++source) {
269       addr_offset = pim_parse_addr_source(ifp->name, src_addr,
270                                           &msg_source_addr,
271                                           &msg_source_flags,
272                                           buf, pastend - buf);
273       if (addr_offset < 1) {
274         return -8;
275       }
276
277       buf += addr_offset;
278
279       recv_prune(ifp, neigh, msg_holdtime,
280                  msg_upstream_addr.u.prefix4,
281                  msg_group_addr.u.prefix4,
282                  msg_source_addr.u.prefix4,
283                  msg_source_flags);
284     }
285
286   } /* scan groups */
287
288   return 0;
289 }
290
291 int pim_joinprune_send(struct interface *ifp,
292                        struct in_addr upstream_addr,
293                        struct in_addr source_addr,
294                        struct in_addr group_addr,
295                        int send_join)
296 {
297   struct pim_interface *pim_ifp;
298   uint8_t pim_msg[1000];
299   const uint8_t *pastend = pim_msg + sizeof(pim_msg);
300   uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
301   int pim_msg_size;
302   int remain;
303
304   zassert(ifp);
305
306   pim_ifp = ifp->info;
307
308   if (!pim_ifp) {
309     zlog_warn("%s: multicast not enabled on interface %s",
310               __PRETTY_FUNCTION__,
311               ifp->name);
312     return -1;
313   }
314
315   if (PIM_DEBUG_PIM_TRACE) {
316     char source_str[100];
317     char group_str[100];
318     char dst_str[100];
319     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
320     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
321     pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
322     zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
323                __PRETTY_FUNCTION__,
324                send_join ? "Join" : "Prune",
325                source_str, group_str, dst_str, ifp->name);
326   }
327
328   if (PIM_INADDR_IS_ANY(upstream_addr)) {
329     if (PIM_DEBUG_PIM_TRACE) {
330       char source_str[100];
331       char group_str[100];
332       char dst_str[100];
333       pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
334       pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
335       pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
336       zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
337                  __PRETTY_FUNCTION__,
338                  send_join ? "Join" : "Prune",
339                  source_str, group_str, dst_str, ifp->name);
340     }
341     return 0;
342   }
343
344   /*
345     RFC 4601: 4.3.1.  Sending Hello Messages
346
347     Thus, if a router needs to send a Join/Prune or Assert message on
348     an interface on which it has not yet sent a Hello message with the
349     currently configured IP address, then it MUST immediately send the
350     relevant Hello message without waiting for the Hello Timer to
351     expire, followed by the Join/Prune or Assert message.
352   */
353   pim_hello_require(ifp);
354
355   /*
356     Build PIM message
357   */
358
359   remain = pastend - pim_msg_curr;
360   pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
361                                                 remain,
362                                                 upstream_addr);
363   if (!pim_msg_curr) {
364     char dst_str[100];
365     pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
366     zlog_warn("%s: failure encoding destination address %s: space left=%d",
367               __PRETTY_FUNCTION__, dst_str, remain);
368     return -3;
369   }
370
371   remain = pastend - pim_msg_curr;
372   if (remain < 4) {
373     zlog_warn("%s: group will not fit: space left=%d",
374             __PRETTY_FUNCTION__, remain);
375     return -4;
376   }
377
378   *pim_msg_curr = 0; /* reserved */
379   ++pim_msg_curr;
380   *pim_msg_curr = 1; /* number of groups */
381   ++pim_msg_curr;
382   *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
383   ++pim_msg_curr;
384   ++pim_msg_curr;
385
386   remain = pastend - pim_msg_curr;
387   pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
388                                                 remain,
389                                                 group_addr);
390   if (!pim_msg_curr) {
391     char group_str[100];
392     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
393     zlog_warn("%s: failure encoding group address %s: space left=%d",
394               __PRETTY_FUNCTION__, group_str, remain);
395     return -5;
396   }
397
398   remain = pastend - pim_msg_curr;
399   if (remain < 4) {
400     zlog_warn("%s: sources will not fit: space left=%d",
401               __PRETTY_FUNCTION__, remain);
402     return -6;
403   }
404
405   /* number of joined sources */
406   *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
407   ++pim_msg_curr;
408   ++pim_msg_curr;
409
410   /* number of pruned sources */
411   *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
412   ++pim_msg_curr;
413   ++pim_msg_curr;
414
415   remain = pastend - pim_msg_curr;
416   pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
417                                                  remain,
418                                                  source_addr);
419   if (!pim_msg_curr) {
420     char source_str[100];
421     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
422     zlog_warn("%s: failure encoding source address %s: space left=%d",
423               __PRETTY_FUNCTION__, source_str, remain);
424     return -7;
425   }
426
427   /* Add PIM header */
428
429   pim_msg_size = pim_msg_curr - pim_msg;
430
431   pim_msg_build_header(pim_msg, pim_msg_size,
432                        PIM_MSG_TYPE_JOIN_PRUNE);
433
434   if (pim_msg_send(pim_ifp->pim_sock_fd,
435                    qpim_all_pim_routers_addr,
436                    pim_msg,
437                    pim_msg_size,
438                    ifp->name)) {
439     zlog_warn("%s: could not send PIM message on interface %s",
440               __PRETTY_FUNCTION__, ifp->name);
441     return -8;
442   }
443
444   return 0;
445 }