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