예제 #1
0
static void update_dr_priority(struct pim_neighbor *neigh,
			       pim_hello_options hello_options,
			       uint32_t dr_priority)
{
  pim_hello_options will_set_pri; /* boolean */
  pim_hello_options bit_flip;     /* boolean */
  pim_hello_options pri_change;   /* boolean */

  will_set_pri = PIM_OPTION_IS_SET(hello_options,
				   PIM_OPTION_MASK_DR_PRIORITY);

  bit_flip =
    (
     will_set_pri !=
     PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)
     );

  if (bit_flip) {
    struct pim_interface *pim_ifp = neigh->interface->info;

    /* update num. of neighbors without dr_pri */

    if (will_set_pri) {
      --pim_ifp->pim_dr_num_nondrpri_neighbors; 
    }
    else {
      ++pim_ifp->pim_dr_num_nondrpri_neighbors; 
    }
  }

  pri_change = 
    (
     bit_flip
     ||
     (neigh->dr_priority != dr_priority)
     );

  if (will_set_pri) {
    neigh->dr_priority = dr_priority;
  }
  else {
    neigh->dr_priority = 0; /* cosmetic unset */
  }

  if (pri_change) {
    /*
      RFC 4601: 4.3.2.  DR Election
      
      A router's idea of the current DR on an interface can change when a
      PIM Hello message is received, when a neighbor times out, or when a
      router's own DR Priority changes.
    */
    pim_if_dr_election(neigh->interface); // router's own DR Priority changes
  }
}
예제 #2
0
void pim_neighbor_update(struct pim_neighbor *neigh,
			 pim_hello_options hello_options,
			 uint16_t holdtime,
			 uint32_t dr_priority,
			 struct list *addr_list)
{
  struct pim_interface *pim_ifp = neigh->interface->info;

  /* Received holdtime ? */
  if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
    pim_neighbor_timer_reset(neigh, holdtime);
  }
  else {
    pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
  }

#ifdef DUMP_PREFIX_LIST
  zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d",
	     __PRETTY_FUNCTION__,
	     (unsigned) neigh->prefix_list,
	     neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1,
	     (unsigned) addr_list,
	     addr_list ? (int) listcount(addr_list) : -1);
#endif

  if (neigh->prefix_list == addr_list) {
    if (addr_list) {
      zlog_err("%s: internal error: trying to replace same prefix list=%p",
	       __PRETTY_FUNCTION__, (void *) addr_list);
    }
  }
  else {
    /* Delete existing secondary address list */
    delete_prefix_list(neigh);
  }

  if (addr_list) {
    delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr);
  }

  /* Replace secondary address list */
  neigh->prefix_list = addr_list;

  update_dr_priority(neigh,
		     hello_options,
		     dr_priority);
  /*
    Copy flags
   */
  neigh->hello_options = hello_options;
}
예제 #3
0
void pim_neighbor_delete(struct interface *ifp,
			 struct pim_neighbor *neigh,
			 const char *delete_message)
{
  struct pim_interface *pim_ifp;
  char src_str[100];

  pim_ifp = ifp->info;
  zassert(pim_ifp);

  pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
  zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
	    src_str, ifp->name, delete_message);

  neighbor_timer_off(neigh);

  pim_if_assert_on_neighbor_down(ifp, neigh->source_addr);

  if (!PIM_OPTION_IS_SET(neigh->hello_options,
                         PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
    /* update num. of neighbors without hello option lan_delay */

    --pim_ifp->pim_number_of_nonlandelay_neighbors;
  }

  if (!PIM_OPTION_IS_SET(neigh->hello_options,
			 PIM_OPTION_MASK_DR_PRIORITY)) {
    /* update num. of neighbors without dr_pri */

    --pim_ifp->pim_dr_num_nondrpri_neighbors; 
  }

  zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec);
  zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec);

  if (pim_if_lan_delay_enabled(ifp)) {

    /* will delete a neighbor with highest propagation delay? */
    if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
      /* then find the next highest propagation delay */
      pim_ifp->pim_neighbors_highest_propagation_delay_msec =
	find_neighbors_next_highest_propagation_delay_msec(ifp, neigh);
    }

    /* will delete a neighbor with highest override interval? */
    if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) {
      /* then find the next highest propagation delay */
      pim_ifp->pim_neighbors_highest_override_interval_msec =
	find_neighbors_next_highest_override_interval_msec(ifp, neigh);
    }
  }

  if (PIM_DEBUG_PIM_TRACE) {
    zlog_debug("%s: deleting PIM neighbor %s on interface %s",
	       __PRETTY_FUNCTION__,
	       src_str, ifp->name);
  }

  listnode_delete(pim_ifp->pim_neighbor_list, neigh);

  pim_neighbor_free(neigh);
}
예제 #4
0
static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
					     struct in_addr source_addr,
					     pim_hello_options hello_options,
					     uint16_t holdtime,
					     uint16_t propagation_delay,
					     uint16_t override_interval,
					     uint32_t dr_priority,
					     uint32_t generation_id,
					     struct list *addr_list)
{
  struct pim_interface *pim_ifp;
  struct pim_neighbor *neigh;
  char src_str[100];

  zassert(ifp);
  pim_ifp = ifp->info;
  zassert(pim_ifp);

  neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh));
  if (!neigh) {
    zlog_err("%s: PIM XMALLOC(%zu) failure",
	     __PRETTY_FUNCTION__, sizeof(*neigh));
    return 0;
  }

  neigh->creation               = pim_time_monotonic_sec();
  neigh->source_addr            = source_addr;
  neigh->hello_options          = hello_options;
  neigh->propagation_delay_msec = propagation_delay;
  neigh->override_interval_msec = override_interval;
  neigh->dr_priority            = dr_priority;
  neigh->generation_id          = generation_id;
  neigh->prefix_list            = addr_list;
  neigh->t_expire_timer         = 0;
  neigh->interface              = ifp;

  pim_neighbor_timer_reset(neigh, holdtime);

  pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));

  if (PIM_DEBUG_PIM_EVENTS) {
    zlog_debug("%s: creating PIM neighbor %s on interface %s",
	       __PRETTY_FUNCTION__,
	       src_str, ifp->name);
  }

  zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s",
	    src_str, ifp->name);

  if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
    pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec;
  }
  if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) {
    pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec;
  }

  if (!PIM_OPTION_IS_SET(neigh->hello_options,
			 PIM_OPTION_MASK_LAN_PRUNE_DELAY)) {
    /* update num. of neighbors without hello option lan_delay */
    ++pim_ifp->pim_number_of_nonlandelay_neighbors; 
  }

  if (!PIM_OPTION_IS_SET(neigh->hello_options,
			 PIM_OPTION_MASK_DR_PRIORITY)) {
    /* update num. of neighbors without hello option dr_pri */
    ++pim_ifp->pim_dr_num_nondrpri_neighbors; 
  }

  return neigh;
}
예제 #5
0
int pim_hello_recv(struct interface *ifp, struct in_addr src_addr,
		   uint8_t *tlv_buf, int tlv_buf_size)
{
	struct pim_interface *pim_ifp;
	struct pim_neighbor *neigh;
	uint8_t *tlv_curr;
	uint8_t *tlv_pastend;
	pim_hello_options hello_options =
		0; /* bit array recording options found */
	uint16_t hello_option_holdtime = 0;
	uint16_t hello_option_propagation_delay = 0;
	uint16_t hello_option_override_interval = 0;
	uint32_t hello_option_dr_priority = 0;
	uint32_t hello_option_generation_id = 0;
	struct list *hello_option_addr_list = 0;

	if (PIM_DEBUG_PIM_HELLO)
		on_trace(__PRETTY_FUNCTION__, ifp, src_addr);

	pim_ifp = ifp->info;
	zassert(pim_ifp);

	++pim_ifp->pim_ifstat_hello_recv;

	/*
	  Parse PIM hello TLVs
	 */
	zassert(tlv_buf_size >= 0);
	tlv_curr = tlv_buf;
	tlv_pastend = tlv_buf + tlv_buf_size;

	while (tlv_curr < tlv_pastend) {
		uint16_t option_type;
		uint16_t option_len;
		int remain = tlv_pastend - tlv_curr;

		if (remain < PIM_TLV_MIN_SIZE) {
			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_debug(
					"%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
					__PRETTY_FUNCTION__, remain,
					PIM_TLV_MIN_SIZE, src_str, ifp->name);
			}
			FREE_ADDR_LIST_THEN_RETURN(-1);
		}

		option_type = PIM_TLV_GET_TYPE(tlv_curr);
		tlv_curr += PIM_TLV_TYPE_SIZE;
		option_len = PIM_TLV_GET_LENGTH(tlv_curr);
		tlv_curr += PIM_TLV_LENGTH_SIZE;

		if ((tlv_curr + option_len) > tlv_pastend) {
			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_debug(
					"%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
					__PRETTY_FUNCTION__, option_type,
					option_len, tlv_pastend - tlv_curr,
					src_str, ifp->name);
			}
			FREE_ADDR_LIST_THEN_RETURN(-2);
		}

		if (PIM_DEBUG_PIM_HELLO) {
			char src_str[INET_ADDRSTRLEN];
			pim_inet4_dump("<src?>", src_addr, src_str,
				       sizeof(src_str));
			zlog_debug(
				"%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
				__PRETTY_FUNCTION__, remain, option_type,
				option_len, src_str, ifp->name);
		}

		switch (option_type) {
		case PIM_MSG_OPTION_TYPE_HOLDTIME:
			if (pim_tlv_parse_holdtime(ifp->name, src_addr,
						   &hello_options,
						   &hello_option_holdtime,
						   option_len, tlv_curr)) {
				FREE_ADDR_LIST_THEN_RETURN(-3);
			}
			break;
		case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
			if (pim_tlv_parse_lan_prune_delay(
				    ifp->name, src_addr, &hello_options,
				    &hello_option_propagation_delay,
				    &hello_option_override_interval, option_len,
				    tlv_curr)) {
				FREE_ADDR_LIST_THEN_RETURN(-4);
			}
			break;
		case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
			if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
						      &hello_options,
						      &hello_option_dr_priority,
						      option_len, tlv_curr)) {
				FREE_ADDR_LIST_THEN_RETURN(-5);
			}
			break;
		case PIM_MSG_OPTION_TYPE_GENERATION_ID:
			if (pim_tlv_parse_generation_id(
				    ifp->name, src_addr, &hello_options,
				    &hello_option_generation_id, option_len,
				    tlv_curr)) {
				FREE_ADDR_LIST_THEN_RETURN(-6);
			}
			break;
		case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
			if (pim_tlv_parse_addr_list(ifp->name, src_addr,
						    &hello_options,
						    &hello_option_addr_list,
						    option_len, tlv_curr)) {
				return -7;
			}
			break;
		case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_debug(
					"%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
					__PRETTY_FUNCTION__, option_type,
					option_len, src_str, ifp->name);
			}
			break;
		default:
			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_debug(
					"%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
					__PRETTY_FUNCTION__, option_type,
					option_len, src_str, ifp->name);
			}
		}

		tlv_curr += option_len;
	}

	/*
	  Check received PIM hello options
	*/

	if (PIM_DEBUG_PIM_HELLO) {
		tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime", ifp->name,
				 src_addr,
				 PIM_OPTION_IS_SET(hello_options,
						   PIM_OPTION_MASK_HOLDTIME),
				 hello_option_holdtime);
		tlv_trace_uint16(
			__PRETTY_FUNCTION__, "propagation_delay", ifp->name,
			src_addr,
			PIM_OPTION_IS_SET(hello_options,
					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
			hello_option_propagation_delay);
		tlv_trace_uint16(
			__PRETTY_FUNCTION__, "override_interval", ifp->name,
			src_addr,
			PIM_OPTION_IS_SET(hello_options,
					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
			hello_option_override_interval);
		tlv_trace_bool(
			__PRETTY_FUNCTION__, "can_disable_join_suppression",
			ifp->name, src_addr,
			PIM_OPTION_IS_SET(hello_options,
					  PIM_OPTION_MASK_LAN_PRUNE_DELAY),
			PIM_OPTION_IS_SET(
				hello_options,
				PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
		tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority", ifp->name,
				 src_addr,
				 PIM_OPTION_IS_SET(hello_options,
						   PIM_OPTION_MASK_DR_PRIORITY),
				 hello_option_dr_priority);
		tlv_trace_uint32_hex(
			__PRETTY_FUNCTION__, "generation_id", ifp->name,
			src_addr,
			PIM_OPTION_IS_SET(hello_options,
					  PIM_OPTION_MASK_GENERATION_ID),
			hello_option_generation_id);
		tlv_trace_list(__PRETTY_FUNCTION__, "address_list", ifp->name,
			       src_addr,
			       PIM_OPTION_IS_SET(hello_options,
						 PIM_OPTION_MASK_ADDRESS_LIST),
			       hello_option_addr_list);
	}

	if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
		if (PIM_DEBUG_PIM_HELLO) {
			char src_str[INET_ADDRSTRLEN];
			pim_inet4_dump("<src?>", src_addr, src_str,
				       sizeof(src_str));
			zlog_debug(
				"%s: PIM hello missing holdtime from %s on interface %s",
				__PRETTY_FUNCTION__, src_str, ifp->name);
		}
	}

	/*
	  New neighbor?
	*/

	neigh = pim_neighbor_find(ifp, src_addr);
	if (!neigh) {
		/* Add as new neighbor */

		neigh = pim_neighbor_add(
			ifp, src_addr, hello_options, hello_option_holdtime,
			hello_option_propagation_delay,
			hello_option_override_interval,
			hello_option_dr_priority, hello_option_generation_id,
			hello_option_addr_list, PIM_NEIGHBOR_SEND_DELAY);
		if (!neigh) {
			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_warn(
					"%s: failure creating PIM neighbor %s on interface %s",
					__PRETTY_FUNCTION__, src_str,
					ifp->name);
			}
			FREE_ADDR_LIST_THEN_RETURN(-8);
		}

		/* actual addr list has been saved under neighbor */
		return 0;
	}

	/*
	  Received generation ID ?
	*/

	if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
		/* GenID mismatch ? */
		if (!PIM_OPTION_IS_SET(neigh->hello_options,
				       PIM_OPTION_MASK_GENERATION_ID)
		    || (hello_option_generation_id != neigh->generation_id)) {
			/* GenID mismatch, then replace neighbor */

			if (PIM_DEBUG_PIM_HELLO) {
				char src_str[INET_ADDRSTRLEN];
				pim_inet4_dump("<src?>", src_addr, src_str,
					       sizeof(src_str));
				zlog_debug(
					"%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
					__PRETTY_FUNCTION__,
					hello_option_generation_id,
					neigh->generation_id, src_str,
					ifp->name);
			}

			pim_upstream_rpf_genid_changed(pim_ifp->pim,
						       neigh->source_addr);

			pim_neighbor_delete(ifp, neigh, "GenID mismatch");
			neigh = pim_neighbor_add(ifp, src_addr, hello_options,
						 hello_option_holdtime,
						 hello_option_propagation_delay,
						 hello_option_override_interval,
						 hello_option_dr_priority,
						 hello_option_generation_id,
						 hello_option_addr_list,
						 PIM_NEIGHBOR_SEND_NOW);
			if (!neigh) {
				if (PIM_DEBUG_PIM_HELLO) {
					char src_str[INET_ADDRSTRLEN];
					pim_inet4_dump("<src?>", src_addr,
						       src_str,
						       sizeof(src_str));
					zlog_debug(
						"%s: failure re-creating PIM neighbor %s on interface %s",
						__PRETTY_FUNCTION__, src_str,
						ifp->name);
				}
				FREE_ADDR_LIST_THEN_RETURN(-9);
			}
			/* actual addr list is saved under neighbor */
			return 0;

		} /* GenId mismatch: replace neighbor */

	} /* GenId received */

	/*
	  Update existing neighbor
	*/

	pim_neighbor_update(neigh, hello_options, hello_option_holdtime,
			    hello_option_dr_priority, hello_option_addr_list);
	/* actual addr list is saved under neighbor */
	return 0;
}