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