2015-10-15 18:02:50 +02:00
|
|
|
/*
|
|
|
|
* PIM for Quagga
|
|
|
|
* Copyright (C) 2015 Cumulus Networks, Inc.
|
|
|
|
* Donald Sharp
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#include "log.h"
|
|
|
|
#include "if.h"
|
|
|
|
#include "thread.h"
|
2016-07-14 16:12:25 +02:00
|
|
|
#include "prefix.h"
|
2015-10-15 18:02:50 +02:00
|
|
|
|
|
|
|
#include "pimd.h"
|
2015-10-29 19:27:39 +01:00
|
|
|
#include "pim_mroute.h"
|
|
|
|
#include "pim_iface.h"
|
|
|
|
#include "pim_msg.h"
|
|
|
|
#include "pim_pim.h"
|
2015-10-15 18:02:50 +02:00
|
|
|
#include "pim_str.h"
|
|
|
|
#include "pim_rp.h"
|
|
|
|
#include "pim_register.h"
|
2015-10-20 17:42:16 +02:00
|
|
|
#include "pim_upstream.h"
|
2015-10-15 18:02:50 +02:00
|
|
|
#include "pim_br.h"
|
2016-06-27 20:51:04 +02:00
|
|
|
#include "pim_rpf.h"
|
|
|
|
#include "pim_oil.h"
|
|
|
|
#include "pim_zebra.h"
|
|
|
|
#include "pim_join.h"
|
2016-07-14 16:12:25 +02:00
|
|
|
#include "pim_util.h"
|
2015-10-15 18:02:50 +02:00
|
|
|
|
|
|
|
struct thread *send_test_packet_timer = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This seems stupidly expensive. A list lookup. Why is this
|
|
|
|
* not a hash?
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pim_check_is_my_ip_address (struct in_addr dest_addr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* See if we can short-cut some?
|
|
|
|
* This might not make sense if we ever leave a static RP
|
|
|
|
* type of configuration.
|
|
|
|
* Note - Premature optimization might bite our patooeys' here.
|
|
|
|
*/
|
2015-10-27 21:13:23 +01:00
|
|
|
if (I_am_RP(dest_addr) && (dest_addr.s_addr == qpim_rp.rpf_addr.s_addr))
|
2015-10-15 18:02:50 +02:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (if_lookup_exact_address (&dest_addr, AF_INET))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2016-07-14 16:12:25 +02:00
|
|
|
pim_register_stop_send (struct interface *ifp, struct in_addr source,
|
|
|
|
struct in_addr group, struct in_addr originator)
|
2015-10-15 18:02:50 +02:00
|
|
|
{
|
2016-07-14 16:12:25 +02:00
|
|
|
struct pim_interface *pinfo;
|
|
|
|
unsigned char buffer[3000];
|
|
|
|
unsigned int b1length = 0;
|
|
|
|
unsigned int length;
|
|
|
|
uint8_t *b1;
|
|
|
|
struct prefix p;
|
|
|
|
|
|
|
|
memset (buffer, 0, 3000);
|
|
|
|
b1 = (uint8_t *)buffer + PIM_MSG_REGISTER_STOP_LEN;
|
|
|
|
|
|
|
|
length = pim_encode_addr_group (b1, AFI_IP, 0, 0, group);
|
|
|
|
b1length += length;
|
|
|
|
b1 += length;
|
|
|
|
|
|
|
|
p.family = AF_INET;
|
|
|
|
p.u.prefix4 = source;
|
|
|
|
p.prefixlen = 32;
|
|
|
|
length = pim_encode_addr_ucast (b1, &p);
|
|
|
|
b1length += length;
|
|
|
|
|
|
|
|
pim_msg_build_header (buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, PIM_MSG_TYPE_REG_STOP);
|
|
|
|
|
|
|
|
pinfo = (struct pim_interface *)ifp->info;
|
|
|
|
if (!pinfo)
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_TRACE)
|
|
|
|
zlog_debug ("%s: No pinfo!\n", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pim_msg_send (pinfo->pim_sock_fd, originator,
|
|
|
|
buffer, b1length + PIM_MSG_REGISTER_STOP_LEN,
|
|
|
|
ifp->name))
|
|
|
|
{
|
|
|
|
if (PIM_DEBUG_PIM_TRACE)
|
|
|
|
{
|
|
|
|
zlog_debug ("%s: could not send PIM register stop message on interface %s",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name);
|
|
|
|
}
|
|
|
|
}
|
2015-10-15 18:02:50 +02:00
|
|
|
}
|
|
|
|
|
2016-07-14 16:30:37 +02:00
|
|
|
int
|
2016-07-15 21:42:09 +02:00
|
|
|
pim_register_stop_recv (uint8_t *buf, int buf_size)
|
2016-07-14 16:30:37 +02:00
|
|
|
{
|
2016-07-15 21:42:09 +02:00
|
|
|
struct pim_upstream *upstream = NULL;
|
|
|
|
struct prefix source;
|
|
|
|
struct prefix group;
|
2016-07-23 05:12:06 +02:00
|
|
|
struct prefix sg;
|
2016-07-15 21:42:09 +02:00
|
|
|
int l;
|
|
|
|
|
2016-07-15 21:47:13 +02:00
|
|
|
if (PIM_DEBUG_PIM_PACKETDUMP_RECV)
|
2016-07-15 21:42:09 +02:00
|
|
|
pim_pkt_dump ("Received Register Stop", buf, buf_size);
|
|
|
|
|
|
|
|
l = pim_parse_addr_group (&group, buf, buf_size);
|
|
|
|
buf += l;
|
|
|
|
buf_size -= l;
|
|
|
|
l = pim_parse_addr_ucast (&source, buf, buf_size);
|
2016-07-23 05:12:06 +02:00
|
|
|
memset (&sg, 0, sizeof (struct prefix));
|
|
|
|
sg.u.sg.src = source.u.prefix4;
|
|
|
|
sg.u.sg.grp = group.u.prefix4;
|
|
|
|
upstream = pim_upstream_find (&sg);
|
2016-07-15 21:42:09 +02:00
|
|
|
if (!upstream)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (upstream->join_state)
|
|
|
|
{
|
|
|
|
case PIM_UPSTREAM_NOTJOINED:
|
|
|
|
case PIM_UPSTREAM_PRUNE:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case PIM_UPSTREAM_JOINED:
|
|
|
|
case PIM_UPSTREAM_JOIN_PENDING:
|
|
|
|
upstream->join_state = PIM_UPSTREAM_PRUNE;
|
|
|
|
pim_upstream_start_register_stop_timer (upstream, 0);
|
|
|
|
pim_channel_del_oif (upstream->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-14 16:30:37 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-29 19:27:39 +01:00
|
|
|
void
|
2016-07-18 02:18:33 +02:00
|
|
|
pim_register_send (const uint8_t *buf, int buf_size, struct pim_rpf *rpg, int null_register)
|
2015-10-29 19:27:39 +01:00
|
|
|
{
|
|
|
|
unsigned char buffer[3000];
|
|
|
|
unsigned char *b1;
|
|
|
|
struct pim_interface *pinfo;
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
ifp = rpg->source_nexthop.interface;
|
|
|
|
pinfo = (struct pim_interface *)ifp->info;
|
|
|
|
if (!pinfo) {
|
|
|
|
zlog_debug("%s: No pinfo!\n", __PRETTY_FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(buffer, 0, 3000);
|
2016-07-18 02:18:33 +02:00
|
|
|
b1 = buffer + PIM_MSG_HEADER_LEN;
|
|
|
|
*b1 |= null_register << 31;
|
2015-10-29 19:27:39 +01:00
|
|
|
b1 = buffer + PIM_MSG_REGISTER_LEN;
|
|
|
|
|
2016-07-18 02:18:33 +02:00
|
|
|
memcpy(b1, (const unsigned char *)buf, buf_size);
|
2015-10-29 19:27:39 +01:00
|
|
|
|
2016-07-18 02:18:33 +02:00
|
|
|
pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER);
|
2015-10-29 19:27:39 +01:00
|
|
|
|
|
|
|
if (pim_msg_send(pinfo->pim_sock_fd,
|
|
|
|
rpg->rpf_addr,
|
|
|
|
buffer,
|
2016-07-18 02:18:33 +02:00
|
|
|
buf_size + PIM_MSG_REGISTER_LEN,
|
2015-10-29 19:27:39 +01:00
|
|
|
ifp->name)) {
|
|
|
|
if (PIM_DEBUG_PIM_TRACE) {
|
|
|
|
zlog_debug("%s: could not send PIM register message on interface %s",
|
|
|
|
__PRETTY_FUNCTION__, ifp->name);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-15 18:02:50 +02:00
|
|
|
/*
|
|
|
|
* 4.4.2 Receiving Register Messages at the RP
|
|
|
|
*
|
|
|
|
* When an RP receives a Register message, the course of action is
|
|
|
|
* decided according to the following pseudocode:
|
|
|
|
*
|
|
|
|
* packet_arrives_on_rp_tunnel( pkt ) {
|
|
|
|
* if( outer.dst is not one of my addresses ) {
|
|
|
|
* drop the packet silently.
|
|
|
|
* # Note: this may be a spoofing attempt
|
|
|
|
* }
|
|
|
|
* if( I_am_RP(G) AND outer.dst == RP(G) ) {
|
|
|
|
* sentRegisterStop = FALSE;
|
|
|
|
* if ( register.borderbit == TRUE ) {
|
|
|
|
* if ( PMBR(S,G) == unknown ) {
|
|
|
|
* PMBR(S,G) = outer.src
|
|
|
|
* } else if ( outer.src != PMBR(S,G) ) {
|
|
|
|
* send Register-Stop(S,G) to outer.src
|
|
|
|
* drop the packet silently.
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* if ( SPTbit(S,G) OR
|
|
|
|
* ( SwitchToSptDesired(S,G) AND
|
|
|
|
* ( inherited_olist(S,G) == NULL ))) {
|
|
|
|
* send Register-Stop(S,G) to outer.src
|
|
|
|
* sentRegisterStop = TRUE;
|
|
|
|
* }
|
|
|
|
* if ( SPTbit(S,G) OR SwitchToSptDesired(S,G) ) {
|
|
|
|
* if ( sentRegisterStop == TRUE ) {
|
|
|
|
* set KeepaliveTimer(S,G) to RP_Keepalive_Period;
|
|
|
|
* } else {
|
|
|
|
* set KeepaliveTimer(S,G) to Keepalive_Period;
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* if( !SPTbit(S,G) AND ! pkt.NullRegisterBit ) {
|
|
|
|
* decapsulate and forward the inner packet to
|
|
|
|
* inherited_olist(S,G,rpt) # Note (+)
|
|
|
|
* }
|
|
|
|
* } else {
|
|
|
|
* send Register-Stop(S,G) to outer.src
|
|
|
|
* # Note (*)
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pim_register_recv (struct interface *ifp,
|
|
|
|
struct in_addr dest_addr,
|
|
|
|
struct in_addr src_addr,
|
|
|
|
uint8_t *tlv_buf, int tlv_buf_size)
|
|
|
|
{
|
2015-10-20 17:36:37 +02:00
|
|
|
int sentRegisterStop = 0;
|
|
|
|
struct ip *ip_hdr;
|
2015-10-15 18:02:50 +02:00
|
|
|
struct in_addr group = { .s_addr = 0 };
|
|
|
|
struct in_addr source = { .s_addr = 0 };
|
2016-07-23 05:12:06 +02:00
|
|
|
struct prefix sg;
|
2015-10-20 17:36:37 +02:00
|
|
|
uint32_t *bits;
|
2015-10-15 18:02:50 +02:00
|
|
|
|
|
|
|
if (!pim_check_is_my_ip_address (dest_addr)) {
|
|
|
|
if (PIM_DEBUG_PIM_PACKETS) {
|
|
|
|
char dest[100];
|
|
|
|
|
|
|
|
pim_inet4_dump ("<dst?>", dest_addr, dest, sizeof(dest));
|
|
|
|
zlog_debug ("%s: Received Register message for %s that I do not own", __func__,
|
|
|
|
dest);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-20 17:42:16 +02:00
|
|
|
#define inherited_olist(S,G) NULL
|
2015-10-20 17:36:37 +02:00
|
|
|
/*
|
|
|
|
* Please note this is not drawn to get the correct bit/data size
|
|
|
|
*
|
|
|
|
* The entirety of the REGISTER packet looks like this:
|
|
|
|
* -------------------------------------------------------------
|
|
|
|
* | Ver | Type | Reserved | Checksum |
|
|
|
|
* |-----------------------------------------------------------|
|
|
|
|
* |B|N| Reserved 2 |
|
|
|
|
* |-----------------------------------------------------------|
|
|
|
|
* | Encap | IP HDR |
|
|
|
|
* | Mcast | |
|
|
|
|
* | Packet |--------------------------------------------------|
|
|
|
|
* | | Mcast Data |
|
|
|
|
* | | |
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* tlv_buf when received from the caller points at the B bit
|
|
|
|
* We need to know the inner source and dest
|
|
|
|
*/
|
|
|
|
bits = (uint32_t *)tlv_buf;
|
2016-06-24 02:42:19 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* tlv_buf points to the start of the |B|N|... Reserved
|
|
|
|
* Line above. So we need to add 4 bytes to get to the
|
|
|
|
* start of the actual Encapsulated data.
|
|
|
|
*/
|
|
|
|
#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4
|
|
|
|
ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN);
|
|
|
|
source = ip_hdr->ip_src;
|
|
|
|
group = ip_hdr->ip_dst;
|
2015-10-20 17:36:37 +02:00
|
|
|
|
2015-10-28 15:00:31 +01:00
|
|
|
if (I_am_RP (group) && (dest_addr.s_addr == ((RP (group))->rpf_addr.s_addr))) {
|
2015-10-20 17:36:37 +02:00
|
|
|
sentRegisterStop = 0;
|
2015-10-15 18:02:50 +02:00
|
|
|
|
2016-05-26 02:36:10 +02:00
|
|
|
if (*bits & PIM_REGISTER_BORDER_BIT) {
|
2015-10-15 18:02:50 +02:00
|
|
|
struct in_addr pimbr = pim_br_get_pmbr (source, group);
|
|
|
|
if (PIM_DEBUG_PIM_PACKETS)
|
|
|
|
zlog_debug("%s: Received Register message with Border bit set", __func__);
|
|
|
|
|
|
|
|
if (pimbr.s_addr == pim_br_unknown.s_addr)
|
2015-10-20 17:42:16 +02:00
|
|
|
pim_br_set_pmbr(source, group, src_addr);
|
|
|
|
else if (src_addr.s_addr != pimbr.s_addr) {
|
2016-07-14 16:12:25 +02:00
|
|
|
pim_register_stop_send (ifp, source, group, src_addr);
|
2015-10-15 18:02:50 +02:00
|
|
|
if (PIM_DEBUG_PIM_PACKETS)
|
|
|
|
zlog_debug("%s: Sending register-Stop to %s and dropping mr. packet",
|
|
|
|
__func__, "Sender");
|
2015-10-20 17:36:37 +02:00
|
|
|
/* Drop Packet Silently */
|
|
|
|
return 1;
|
2015-10-15 18:02:50 +02:00
|
|
|
}
|
|
|
|
}
|
2015-10-20 17:36:37 +02:00
|
|
|
|
2016-07-23 05:12:06 +02:00
|
|
|
memset (&sg, 0, sizeof (struct prefix));
|
|
|
|
sg.u.sg.src = source;
|
|
|
|
sg.u.sg.grp = group;
|
|
|
|
struct pim_upstream *upstream = pim_upstream_find (&sg);
|
2015-10-20 17:36:37 +02:00
|
|
|
/*
|
|
|
|
* If we don't have a place to send ignore the packet
|
|
|
|
*/
|
|
|
|
if (!upstream)
|
2016-07-22 15:26:30 +02:00
|
|
|
{
|
2016-07-23 05:12:06 +02:00
|
|
|
upstream = pim_upstream_add (&sg, ifp);
|
2016-07-22 15:26:30 +02:00
|
|
|
pim_upstream_switch (upstream, PIM_UPSTREAM_PRUNE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (upstream->join_state == PIM_UPSTREAM_PRUNE)
|
2016-07-14 15:09:39 +02:00
|
|
|
{
|
2016-07-14 16:12:25 +02:00
|
|
|
pim_register_stop_send (ifp, source, group, src_addr);
|
2016-07-14 15:09:39 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2015-10-20 17:36:37 +02:00
|
|
|
|
|
|
|
if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) ||
|
2016-07-23 06:06:55 +02:00
|
|
|
((SwitchToSptDesired(&sg)) &&
|
2015-10-20 17:42:16 +02:00
|
|
|
(inherited_olist(source, group) == NULL))) {
|
2016-07-14 16:12:25 +02:00
|
|
|
pim_register_stop_send (ifp, source, group, src_addr);
|
2015-10-20 17:36:37 +02:00
|
|
|
sentRegisterStop = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) ||
|
2016-07-23 06:06:55 +02:00
|
|
|
(SwitchToSptDesired(&sg))) {
|
2015-10-20 17:42:16 +02:00
|
|
|
if (sentRegisterStop) {
|
|
|
|
pim_upstream_keep_alive_timer_start (upstream, PIM_RP_KEEPALIVE_PERIOD);
|
2015-10-20 17:36:37 +02:00
|
|
|
} else {
|
2015-10-20 17:42:16 +02:00
|
|
|
pim_upstream_keep_alive_timer_start (upstream, PIM_KEEPALIVE_PERIOD);
|
2015-10-20 17:36:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) &&
|
2016-06-27 20:51:04 +02:00
|
|
|
!(*bits & PIM_REGISTER_NR_BIT))
|
|
|
|
{
|
|
|
|
pim_rp_set_upstream_addr (&upstream->upstream_addr, source);
|
|
|
|
pim_nexthop_lookup (&upstream->rpf.source_nexthop,
|
|
|
|
upstream->upstream_addr, NULL);
|
|
|
|
upstream->rpf.source_nexthop.interface = ifp;
|
2016-07-22 14:57:20 +02:00
|
|
|
upstream->sg.u.sg.src.s_addr = source.s_addr;
|
2016-07-22 16:19:32 +02:00
|
|
|
upstream->rpf.rpf_addr = upstream->rpf.source_nexthop.mrib_nexthop_addr;
|
2016-06-27 20:51:04 +02:00
|
|
|
upstream->channel_oil->oil.mfcc_origin = source;
|
|
|
|
pim_scan_individual_oil (upstream->channel_oil);
|
2016-07-15 21:42:09 +02:00
|
|
|
pim_upstream_send_join (upstream);
|
2016-06-27 20:51:04 +02:00
|
|
|
|
|
|
|
//decapsulate and forward the iner packet to
|
|
|
|
//inherited_olist(S,G,rpt)
|
|
|
|
}
|
2015-10-20 17:36:37 +02:00
|
|
|
} else {
|
2016-07-14 16:12:25 +02:00
|
|
|
pim_register_stop_send (ifp, source, group, src_addr);
|
2015-10-20 17:36:37 +02:00
|
|
|
}
|
2015-10-15 18:02:50 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
pim_register_send_test_packet (struct thread *t)
|
|
|
|
{
|
|
|
|
uint8_t *packet;
|
|
|
|
|
|
|
|
packet = THREAD_ARG(t);
|
|
|
|
|
|
|
|
*packet = 4;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pim_register_send_test_packet
|
|
|
|
*
|
|
|
|
* Send a test packet to the RP from source, in group and pps packets per second
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pim_register_send_test_packet_start (struct in_addr source,
|
|
|
|
struct in_addr group,
|
|
|
|
uint32_t pps)
|
|
|
|
{
|
|
|
|
uint8_t *packet = NULL;
|
|
|
|
|
|
|
|
THREAD_TIMER_MSEC_ON(master, send_test_packet_timer,
|
|
|
|
pim_register_send_test_packet, packet, 1000/pps);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|