blob: 9d8e0012e4e262d67acc4437f92a5b681f7de6bd [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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
38static 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
49static 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
79static 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
107int pim_joinprune_recv(struct interface *ifp,
108 struct pim_neighbor *neigh,
109 struct in_addr src_addr,
David Lamparterf8cfeb22012-02-16 04:31:08 +0000110 uint8_t *tlv_buf, int tlv_buf_size)
Everton Marques871dbcf2009-08-11 15:43:05 -0300111{
112 struct prefix msg_upstream_addr;
113 uint8_t msg_num_groups;
114 uint16_t msg_holdtime;
115 int addr_offset;
David Lamparterf8cfeb22012-02-16 04:31:08 +0000116 uint8_t *buf;
117 uint8_t *pastend;
Everton Marques871dbcf2009-08-11 15:43:05 -0300118 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);
Everton Marquesd4595862014-02-13 19:50:30 -0200132#if 0
133 zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d",
134 __PRETTY_FUNCTION__,
135 addr_offset);
136#endif
Everton Marques871dbcf2009-08-11 15:43:05 -0300137 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);
Everton Marquesd4595862014-02-13 19:50:30 -0200202#if 0
203 zlog_warn("%s: pim_parse_addr_group addr_offset=%d",
204 __PRETTY_FUNCTION__,
205 addr_offset);
206#endif
Everton Marques871dbcf2009-08-11 15:43:05 -0300207 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);
Everton Marquesd4595862014-02-13 19:50:30 -0200249#if 0
250 zlog_warn("%s: pim_parse_addr_source addr_offset=%d",
251 __PRETTY_FUNCTION__,
252 addr_offset);
253#endif
Everton Marques871dbcf2009-08-11 15:43:05 -0300254 if (addr_offset < 1) {
255 return -7;
256 }
Leonard Herve236b0152009-08-11 15:51:52 -0300257
Everton Marques871dbcf2009-08-11 15:43:05 -0300258 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 }
Leonard Herve236b0152009-08-11 15:51:52 -0300276
Everton Marques871dbcf2009-08-11 15:43:05 -0300277 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
291int 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;
David Lamparterf8cfeb22012-02-16 04:31:08 +0000298 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 */
Everton Marques871dbcf2009-08-11 15:43:05 -0300301 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}