[pim] Initial pim 0.155
diff --git a/pimd/pim_join.c b/pimd/pim_join.c
new file mode 100644
index 0000000..ce4ec4e
--- /dev/null
+++ b/pimd/pim_join.c
@@ -0,0 +1,428 @@
+/*
+ PIM for Quagga
+ Copyright (C) 2008 Everton da Silva Marques
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301 USA
+
+ $QuaggaId: $Format:%an, %ai, %h$ $
+*/
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+
+#include "pimd.h"
+#include "pim_str.h"
+#include "pim_tlv.h"
+#include "pim_msg.h"
+#include "pim_pim.h"
+#include "pim_join.h"
+#include "pim_iface.h"
+#include "pim_hello.h"
+#include "pim_ifchannel.h"
+
+static void on_trace(const char *label,
+ struct interface *ifp, struct in_addr src)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
+ zlog_debug("%s: from %s on %s",
+ label, src_str, ifp->name);
+ }
+}
+
+static void recv_join(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ uint16_t holdtime,
+ struct in_addr upstream,
+ struct in_addr group,
+ struct in_addr source,
+ uint8_t source_flags)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char up_str[100];
+ char src_str[100];
+ char grp_str[100];
+ char neigh_str[100];
+ pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+ zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ source_flags & PIM_RPT_BIT_MASK,
+ source_flags & PIM_WILDCARD_BIT_MASK,
+ up_str, holdtime, neigh_str, ifp->name);
+ }
+
+ /* Restart join expiry timer */
+ pim_ifchannel_join_add(ifp, neigh->source_addr, upstream,
+ source, group, source_flags, holdtime);
+}
+
+static void recv_prune(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ uint16_t holdtime,
+ struct in_addr upstream,
+ struct in_addr group,
+ struct in_addr source,
+ uint8_t source_flags)
+{
+ if (PIM_DEBUG_PIM_TRACE) {
+ char up_str[100];
+ char src_str[100];
+ char grp_str[100];
+ char neigh_str[100];
+ pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
+ pim_inet4_dump("<src?>", source, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", group, grp_str, sizeof(grp_str));
+ pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str, sizeof(neigh_str));
+ zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, grp_str,
+ source_flags & PIM_RPT_BIT_MASK,
+ source_flags & PIM_WILDCARD_BIT_MASK,
+ up_str, holdtime, neigh_str, ifp->name);
+ }
+
+ pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime);
+}
+
+int pim_joinprune_recv(struct interface *ifp,
+ struct pim_neighbor *neigh,
+ struct in_addr src_addr,
+ char *tlv_buf, int tlv_buf_size)
+{
+ struct prefix msg_upstream_addr;
+ uint8_t msg_num_groups;
+ uint16_t msg_holdtime;
+ int addr_offset;
+ char *buf;
+ char *pastend;
+ int remain;
+ int group;
+
+ on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
+
+ buf = tlv_buf;
+ pastend = tlv_buf + tlv_buf_size;
+
+ /*
+ Parse ucast addr
+ */
+ addr_offset = pim_parse_addr_ucast(ifp->name, src_addr,
+ &msg_upstream_addr,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
+ __PRETTY_FUNCTION__,
+ src_str, ifp->name);
+ return -1;
+ }
+ buf += addr_offset;
+
+ /*
+ Check upstream address family
+ */
+ if (msg_upstream_addr.family != AF_INET) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ msg_upstream_addr.family, src_str, ifp->name);
+ }
+ return -2;
+ }
+
+ remain = pastend - buf;
+ if (remain < 4) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ remain, 4, src_str, ifp->name);
+ return -4;
+ }
+
+ ++buf; /* skip reserved byte */
+ msg_num_groups = *(const uint8_t *) buf;
+ ++buf;
+ msg_holdtime = ntohs(*(const uint16_t *) buf);
+ ++buf;
+ ++buf;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char upstream_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+ upstream_str, sizeof(upstream_str));
+ zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ upstream_str, msg_num_groups, msg_holdtime,
+ src_str, ifp->name);
+ }
+
+ /* Scan groups */
+ for (group = 0; group < msg_num_groups; ++group) {
+ struct prefix msg_group_addr;
+ struct prefix msg_source_addr;
+ uint8_t msg_source_flags;
+ uint16_t msg_num_joined_sources;
+ uint16_t msg_num_pruned_sources;
+ int source;
+
+ addr_offset = pim_parse_addr_group(ifp->name, src_addr,
+ &msg_group_addr,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -5;
+ }
+ buf += addr_offset;
+
+ remain = pastend - buf;
+ if (remain < 4) {
+ char src_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ remain, 4, src_str, ifp->name);
+ return -6;
+ }
+
+ msg_num_joined_sources = ntohs(*(const uint16_t *) buf);
+ buf += 2;
+ msg_num_pruned_sources = ntohs(*(const uint16_t *) buf);
+ buf += 2;
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char src_str[100];
+ char upstream_str[100];
+ char group_str[100];
+ pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
+ pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
+ upstream_str, sizeof(upstream_str));
+ pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4,
+ group_str, sizeof(group_str));
+ zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s",
+ __PRETTY_FUNCTION__,
+ upstream_str, group_str, msg_group_addr.prefixlen,
+ msg_num_joined_sources, msg_num_pruned_sources,
+ src_str, ifp->name);
+ }
+
+ /* Scan joined sources */
+ for (source = 0; source < msg_num_joined_sources; ++source) {
+ addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+ &msg_source_addr,
+ &msg_source_flags,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -7;
+ }
+ buf += addr_offset;
+
+ recv_join(ifp, neigh, msg_holdtime,
+ msg_upstream_addr.u.prefix4,
+ msg_group_addr.u.prefix4,
+ msg_source_addr.u.prefix4,
+ msg_source_flags);
+ }
+
+ /* Scan pruned sources */
+ for (source = 0; source < msg_num_pruned_sources; ++source) {
+ addr_offset = pim_parse_addr_source(ifp->name, src_addr,
+ &msg_source_addr,
+ &msg_source_flags,
+ buf, pastend - buf);
+ if (addr_offset < 1) {
+ return -8;
+ }
+ buf += addr_offset;
+
+ recv_prune(ifp, neigh, msg_holdtime,
+ msg_upstream_addr.u.prefix4,
+ msg_group_addr.u.prefix4,
+ msg_source_addr.u.prefix4,
+ msg_source_flags);
+ }
+
+ } /* scan groups */
+
+ return 0;
+}
+
+int pim_joinprune_send(struct interface *ifp,
+ struct in_addr upstream_addr,
+ struct in_addr source_addr,
+ struct in_addr group_addr,
+ int send_join)
+{
+ struct pim_interface *pim_ifp;
+ char pim_msg[1000];
+ const char *pastend = pim_msg + sizeof(pim_msg);
+ char *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */
+ int pim_msg_size;
+ int remain;
+
+ zassert(ifp);
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ zlog_warn("%s: multicast not enabled on interface %s",
+ __PRETTY_FUNCTION__,
+ ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ char dst_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s",
+ __PRETTY_FUNCTION__,
+ send_join ? "Join" : "Prune",
+ source_str, group_str, dst_str, ifp->name);
+ }
+
+ if (PIM_INADDR_IS_ANY(upstream_addr)) {
+ if (PIM_DEBUG_PIM_TRACE) {
+ char source_str[100];
+ char group_str[100];
+ char dst_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s",
+ __PRETTY_FUNCTION__,
+ send_join ? "Join" : "Prune",
+ source_str, group_str, dst_str, ifp->name);
+ }
+ return 0;
+ }
+
+ /*
+ RFC 4601: 4.3.1. Sending Hello Messages
+
+ Thus, if a router needs to send a Join/Prune or Assert message on
+ an interface on which it has not yet sent a Hello message with the
+ currently configured IP address, then it MUST immediately send the
+ relevant Hello message without waiting for the Hello Timer to
+ expire, followed by the Join/Prune or Assert message.
+ */
+ pim_hello_require(ifp);
+
+ /*
+ Build PIM message
+ */
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
+ remain,
+ upstream_addr);
+ if (!pim_msg_curr) {
+ char dst_str[100];
+ pim_inet4_dump("<dst?>", upstream_addr, dst_str, sizeof(dst_str));
+ zlog_warn("%s: failure encoding destination address %s: space left=%d",
+ __PRETTY_FUNCTION__, dst_str, remain);
+ return -3;
+ }
+
+ remain = pastend - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: group will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -4;
+ }
+
+ *pim_msg_curr = 0; /* reserved */
+ ++pim_msg_curr;
+ *pim_msg_curr = 1; /* number of groups */
+ ++pim_msg_curr;
+ *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
+ remain,
+ group_addr);
+ if (!pim_msg_curr) {
+ char group_str[100];
+ pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("%s: failure encoding group address %s: space left=%d",
+ __PRETTY_FUNCTION__, group_str, remain);
+ return -5;
+ }
+
+ remain = pastend - pim_msg_curr;
+ if (remain < 4) {
+ zlog_warn("%s: sources will not fit: space left=%d",
+ __PRETTY_FUNCTION__, remain);
+ return -6;
+ }
+
+ /* number of joined sources */
+ *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ /* number of pruned sources */
+ *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1);
+ ++pim_msg_curr;
+ ++pim_msg_curr;
+
+ remain = pastend - pim_msg_curr;
+ pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr,
+ remain,
+ source_addr);
+ if (!pim_msg_curr) {
+ char source_str[100];
+ pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
+ zlog_warn("%s: failure encoding source address %s: space left=%d",
+ __PRETTY_FUNCTION__, source_str, remain);
+ return -7;
+ }
+
+ /* Add PIM header */
+
+ pim_msg_size = pim_msg_curr - pim_msg;
+
+ pim_msg_build_header(pim_msg, pim_msg_size,
+ PIM_MSG_TYPE_JOIN_PRUNE);
+
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ pim_msg_size,
+ ifp->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, ifp->name);
+ return -8;
+ }
+
+ return 0;
+}