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: $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:
38: static 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:
49: static 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:
79: static 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:
107: int pim_joinprune_recv(struct interface *ifp,
108: struct pim_neighbor *neigh,
109: struct in_addr src_addr,
110: uint8_t *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: uint8_t *buf;
117: uint8_t *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 0
133: zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d",
134: __PRETTY_FUNCTION__,
135: addr_offset);
136: #endif
137: 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);
202: #if 0
203: zlog_warn("%s: pim_parse_addr_group addr_offset=%d",
204: __PRETTY_FUNCTION__,
205: addr_offset);
206: #endif
207: 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);
249: #if 0
250: zlog_warn("%s: pim_parse_addr_source addr_offset=%d",
251: __PRETTY_FUNCTION__,
252: addr_offset);
253: #endif
254: if (addr_offset < 1) {
255: return -7;
256: }
257:
258: 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: }
276:
277: 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:
291: int 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;
298: 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 */
301: 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>