blob: 6b46759a6b21b9aeb4ba233fd7e8d4f593e9e694 [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,
110 char *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 char *buf;
117 char *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 (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
243 /*
244 RFC 4601: 4.9.1 Encoded Source and Group Address Formats
245
246 Encoded-Source Address
247 (...)
248 The mask length MUST be equal to the mask length in bits for the
249 given Address Family and Encoding Type (32 for IPv4 native and
250 128 for IPv6 native). A router SHOULD ignore any messages
251 received with any other mask length.
252 */
253 if (msg_source_addr.prefixlen!=32) return;
254
Everton Marques871dbcf2009-08-11 15:43:05 -0300255 buf += addr_offset;
256
257 recv_join(ifp, neigh, msg_holdtime,
258 msg_upstream_addr.u.prefix4,
259 msg_group_addr.u.prefix4,
260 msg_source_addr.u.prefix4,
261 msg_source_flags);
262 }
263
264 /* Scan pruned sources */
265 for (source = 0; source < msg_num_pruned_sources; ++source) {
266 addr_offset = pim_parse_addr_source(ifp->name, src_addr,
267 &msg_source_addr,
268 &msg_source_flags,
269 buf, pastend - buf);
270 if (addr_offset < 1) {
271 return -8;
272 }
Leonard Herve236b0152009-08-11 15:51:52 -0300273
274 /*
275 RFC 4601: 4.9.1 Encoded Source and Group Address Formats
276
277 Encoded-Source Address
278 (...)
279 The mask length MUST be equal to the mask length in bits for the
280 given Address Family and Encoding Type (32 for IPv4 native and
281 128 for IPv6 native). A router SHOULD ignore any messages
282 received with any other mask length.
283 */
284 if (msg_source_addr.prefixlen!=32) return;
285
Everton Marques871dbcf2009-08-11 15:43:05 -0300286 buf += addr_offset;
287
288 recv_prune(ifp, neigh, msg_holdtime,
289 msg_upstream_addr.u.prefix4,
290 msg_group_addr.u.prefix4,
291 msg_source_addr.u.prefix4,
292 msg_source_flags);
293 }
294
295 } /* scan groups */
296
297 return 0;
298}
299
300int pim_joinprune_send(struct interface *ifp,
301 struct in_addr upstream_addr,
302 struct in_addr source_addr,
303 struct in_addr group_addr,
304 int send_join)
305{
306 struct pim_interface *pim_ifp;
307 char pim_msg[1000];
308 const char *pastend = pim_msg + sizeof(pim_msg);
309 char *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
310 int pim_msg_size;
311 int remain;
312
313 zassert(ifp);
314
315 pim_ifp = ifp->info;
316
317 if (!pim_ifp) {
318 zlog_warn("%s: multicast not enabled on interface %s",
319 __PRETTY_FUNCTION__,
320 ifp->name);
321 return -1;
322 }
323
324 if (PIM_DEBUG_PIM_TRACE) {
325 char source_str[100];
326 char group_str[100];
327 char dst_str[100];
328 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
329 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
330 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
331 zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
332 __PRETTY_FUNCTION__,
333 send_join ? "Join" : "Prune",
334 source_str, group_str, dst_str, ifp->name);
335 }
336
337 if (PIM_INADDR_IS_ANY(upstream_addr)) {
338 if (PIM_DEBUG_PIM_TRACE) {
339 char source_str[100];
340 char group_str[100];
341 char dst_str[100];
342 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
343 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
344 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
345 zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
346 __PRETTY_FUNCTION__,
347 send_join ? "Join" : "Prune",
348 source_str, group_str, dst_str, ifp->name);
349 }
350 return 0;
351 }
352
353 /*
354 RFC 4601: 4.3.1. Sending Hello Messages
355
356 Thus, if a router needs to send a Join/Prune or Assert message on
357 an interface on which it has not yet sent a Hello message with the
358 currently configured IP address, then it MUST immediately send the
359 relevant Hello message without waiting for the Hello Timer to
360 expire, followed by the Join/Prune or Assert message.
361 */
362 pim_hello_require(ifp);
363
364 /*
365 Build PIM message
366 */
367
368 remain = pastend - pim_msg_curr;
369 pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
370 remain,
371 upstream_addr);
372 if (!pim_msg_curr) {
373 char dst_str[100];
374 pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
375 zlog_warn("%s: failure encoding destination address %s: space left=%d",
376 __PRETTY_FUNCTION__, dst_str, remain);
377 return -3;
378 }
379
380 remain = pastend - pim_msg_curr;
381 if (remain < 4) {
382 zlog_warn("%s: group will not fit: space left=%d",
383 __PRETTY_FUNCTION__, remain);
384 return -4;
385 }
386
387 *pim_msg_curr = 0; /* reserved */
388 ++pim_msg_curr;
389 *pim_msg_curr = 1; /* number of groups */
390 ++pim_msg_curr;
391 *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
392 ++pim_msg_curr;
393 ++pim_msg_curr;
394
395 remain = pastend - pim_msg_curr;
396 pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
397 remain,
398 group_addr);
399 if (!pim_msg_curr) {
400 char group_str[100];
401 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
402 zlog_warn("%s: failure encoding group address %s: space left=%d",
403 __PRETTY_FUNCTION__, group_str, remain);
404 return -5;
405 }
406
407 remain = pastend - pim_msg_curr;
408 if (remain < 4) {
409 zlog_warn("%s: sources will not fit: space left=%d",
410 __PRETTY_FUNCTION__, remain);
411 return -6;
412 }
413
414 /* number of joined sources */
415 *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
416 ++pim_msg_curr;
417 ++pim_msg_curr;
418
419 /* number of pruned sources */
420 *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
421 ++pim_msg_curr;
422 ++pim_msg_curr;
423
424 remain = pastend - pim_msg_curr;
425 pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
426 remain,
427 source_addr);
428 if (!pim_msg_curr) {
429 char source_str[100];
430 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
431 zlog_warn("%s: failure encoding source address %s: space left=%d",
432 __PRETTY_FUNCTION__, source_str, remain);
433 return -7;
434 }
435
436 /* Add PIM header */
437
438 pim_msg_size = pim_msg_curr - pim_msg;
439
440 pim_msg_build_header(pim_msg, pim_msg_size,
441 PIM_MSG_TYPE_JOIN_PRUNE);
442
443 if (pim_msg_send(pim_ifp->pim_sock_fd,
444 qpim_all_pim_routers_addr,
445 pim_msg,
446 pim_msg_size,
447 ifp->name)) {
448 zlog_warn("%s: could not send PIM message on interface %s",
449 __PRETTY_FUNCTION__, ifp->name);
450 return -8;
451 }
452
453 return 0;
454}