blob: 5230bb6c9eb755916609f686fc234138d6d45ace [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);
132 if (addr_offset < 1) {
133 char src_str[100];
134 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
135 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
136 __PRETTY_FUNCTION__,
137 src_str, ifp->name);
138 return -1;
139 }
140 buf += addr_offset;
141
142 /*
143 Check upstream address family
144 */
145 if (msg_upstream_addr.family != AF_INET) {
146 if (PIM_DEBUG_PIM_TRACE) {
147 char src_str[100];
148 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
149 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
150 __PRETTY_FUNCTION__,
151 msg_upstream_addr.family, src_str, ifp->name);
152 }
153 return -2;
154 }
155
156 remain = pastend - buf;
157 if (remain < 4) {
158 char src_str[100];
159 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
160 zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
161 __PRETTY_FUNCTION__,
162 remain, 4, src_str, ifp->name);
163 return -4;
164 }
165
166 ++buf; /* skip reserved byte */
167 msg_num_groups = *(const uint8_t *) buf;
168 ++buf;
169 msg_holdtime = ntohs(*(const uint16_t *) buf);
170 ++buf;
171 ++buf;
172
173 if (PIM_DEBUG_PIM_TRACE) {
174 char src_str[100];
175 char upstream_str[100];
176 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
177 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
178 upstream_str, sizeof(upstream_str));
179 zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
180 __PRETTY_FUNCTION__,
181 upstream_str, msg_num_groups, msg_holdtime,
182 src_str, ifp->name);
183 }
184
185 /* Scan groups */
186 for (group = 0; group < msg_num_groups; ++group) {
187 struct prefix msg_group_addr;
188 struct prefix msg_source_addr;
189 uint8_t msg_source_flags;
190 uint16_t msg_num_joined_sources;
191 uint16_t msg_num_pruned_sources;
192 int source;
193
194 addr_offset = pim_parse_addr_group(ifp->name, src_addr,
195 &msg_group_addr,
196 buf, pastend - buf);
197 if (addr_offset < 1) {
198 return -5;
199 }
200 buf += addr_offset;
201
202 remain = pastend - buf;
203 if (remain < 4) {
204 char src_str[100];
205 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
206 zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
207 __PRETTY_FUNCTION__,
208 remain, 4, src_str, ifp->name);
209 return -6;
210 }
211
212 msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
213 buf += 2;
214 msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
215 buf += 2;
216
217 if (PIM_DEBUG_PIM_TRACE) {
218 char src_str[100];
219 char upstream_str[100];
220 char group_str[100];
221 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
222 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
223 upstream_str, sizeof(upstream_str));
224 pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
225 group_str, sizeof(group_str));
226 zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
227 __PRETTY_FUNCTION__,
228 upstream_str, group_str, msg_group_addr.prefixlen,
229 msg_num_joined_sources, msg_num_pruned_sources,
230 src_str, ifp->name);
231 }
232
233 /* Scan joined sources */
234 for (source = 0; source < msg_num_joined_sources; ++source) {
235 addr_offset = pim_parse_addr_source(ifp->name, src_addr,
236 &msg_source_addr,
237 &msg_source_flags,
238 buf, pastend - buf);
239 if (addr_offset < 1) {
240 return -7;
241 }
Leonard Herve236b0152009-08-11 15:51:52 -0300242
Everton Marques871dbcf2009-08-11 15:43:05 -0300243 buf += addr_offset;
244
245 recv_join(ifp, neigh, msg_holdtime,
246 msg_upstream_addr.u.prefix4,
247 msg_group_addr.u.prefix4,
248 msg_source_addr.u.prefix4,
249 msg_source_flags);
250 }
251
252 /* Scan pruned sources */
253 for (source = 0; source < msg_num_pruned_sources; ++source) {
254 addr_offset = pim_parse_addr_source(ifp->name, src_addr,
255 &msg_source_addr,
256 &msg_source_flags,
257 buf, pastend - buf);
258 if (addr_offset < 1) {
259 return -8;
260 }
Leonard Herve236b0152009-08-11 15:51:52 -0300261
Everton Marques871dbcf2009-08-11 15:43:05 -0300262 buf += addr_offset;
263
264 recv_prune(ifp, neigh, msg_holdtime,
265 msg_upstream_addr.u.prefix4,
266 msg_group_addr.u.prefix4,
267 msg_source_addr.u.prefix4,
268 msg_source_flags);
269 }
270
271 } /* scan groups */
272
273 return 0;
274}
275
276int pim_joinprune_send(struct interface *ifp,
277 struct in_addr upstream_addr,
278 struct in_addr source_addr,
279 struct in_addr group_addr,
280 int send_join)
281{
282 struct pim_interface *pim_ifp;
David Lamparterf8cfeb22012-02-16 04:31:08 +0000283 uint8_t pim_msg[1000];
284 const uint8_t *pastend = pim_msg + sizeof(pim_msg);
285 uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
Everton Marques871dbcf2009-08-11 15:43:05 -0300286 int pim_msg_size;
287 int remain;
288
289 zassert(ifp);
290
291 pim_ifp = ifp->info;
292
293 if (!pim_ifp) {
294 zlog_warn("%s: multicast not enabled on interface %s",
295 __PRETTY_FUNCTION__,
296 ifp->name);
297 return -1;
298 }
299
300 if (PIM_DEBUG_PIM_TRACE) {
301 char source_str[100];
302 char group_str[100];
303 char dst_str[100];
304 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
305 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
306 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
307 zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
308 __PRETTY_FUNCTION__,
309 send_join ? "Join" : "Prune",
310 source_str, group_str, dst_str, ifp->name);
311 }
312
313 if (PIM_INADDR_IS_ANY(upstream_addr)) {
314 if (PIM_DEBUG_PIM_TRACE) {
315 char source_str[100];
316 char group_str[100];
317 char dst_str[100];
318 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
319 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
320 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
321 zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
322 __PRETTY_FUNCTION__,
323 send_join ? "Join" : "Prune",
324 source_str, group_str, dst_str, ifp->name);
325 }
326 return 0;
327 }
328
329 /*
330 RFC 4601: 4.3.1. Sending Hello Messages
331
332 Thus, if a router needs to send a Join/Prune or Assert message on
333 an interface on which it has not yet sent a Hello message with the
334 currently configured IP address, then it MUST immediately send the
335 relevant Hello message without waiting for the Hello Timer to
336 expire, followed by the Join/Prune or Assert message.
337 */
338 pim_hello_require(ifp);
339
340 /*
341 Build PIM message
342 */
343
344 remain = pastend - pim_msg_curr;
345 pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
346 remain,
347 upstream_addr);
348 if (!pim_msg_curr) {
349 char dst_str[100];
350 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
351 zlog_warn("%s: failure encoding destination address %s: space left=%d",
352 __PRETTY_FUNCTION__, dst_str, remain);
353 return -3;
354 }
355
356 remain = pastend - pim_msg_curr;
357 if (remain < 4) {
358 zlog_warn("%s: group will not fit: space left=%d",
359 __PRETTY_FUNCTION__, remain);
360 return -4;
361 }
362
363 *pim_msg_curr = 0; /* reserved */
364 ++pim_msg_curr;
365 *pim_msg_curr = 1; /* number of groups */
366 ++pim_msg_curr;
367 *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
368 ++pim_msg_curr;
369 ++pim_msg_curr;
370
371 remain = pastend - pim_msg_curr;
372 pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
373 remain,
374 group_addr);
375 if (!pim_msg_curr) {
376 char group_str[100];
377 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
378 zlog_warn("%s: failure encoding group address %s: space left=%d",
379 __PRETTY_FUNCTION__, group_str, remain);
380 return -5;
381 }
382
383 remain = pastend - pim_msg_curr;
384 if (remain < 4) {
385 zlog_warn("%s: sources will not fit: space left=%d",
386 __PRETTY_FUNCTION__, remain);
387 return -6;
388 }
389
390 /* number of joined sources */
391 *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
392 ++pim_msg_curr;
393 ++pim_msg_curr;
394
395 /* number of pruned sources */
396 *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
397 ++pim_msg_curr;
398 ++pim_msg_curr;
399
400 remain = pastend - pim_msg_curr;
401 pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
402 remain,
403 source_addr);
404 if (!pim_msg_curr) {
405 char source_str[100];
406 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
407 zlog_warn("%s: failure encoding source address %s: space left=%d",
408 __PRETTY_FUNCTION__, source_str, remain);
409 return -7;
410 }
411
412 /* Add PIM header */
413
414 pim_msg_size = pim_msg_curr - pim_msg;
415
416 pim_msg_build_header(pim_msg, pim_msg_size,
417 PIM_MSG_TYPE_JOIN_PRUNE);
418
419 if (pim_msg_send(pim_ifp->pim_sock_fd,
420 qpim_all_pim_routers_addr,
421 pim_msg,
422 pim_msg_size,
423 ifp->name)) {
424 zlog_warn("%s: could not send PIM message on interface %s",
425 __PRETTY_FUNCTION__, ifp->name);
426 return -8;
427 }
428
429 return 0;
430}