mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 05:27:16 +02:00

a) EIGRP was having issues with the prefix created as part of the topology destination. Make this just a part of the topology data structure instead of allocating it. b) EIGRP was not freeing up any memory associated with the network table. Free it. c) EIGRP was confusing zebra shutdown as part of the deletion of the last eigrp data structure. This was inappropriate it should be part of the `I'm just shutting down`. d) The QOBJ was not being properly freed, free it. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
1033 lines
27 KiB
C
1033 lines
27 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* EIGRP Sending and Receiving EIGRP Update Packets.
|
|
* Copyright (C) 2013-2016
|
|
* Authors:
|
|
* Donnie Savage
|
|
* Jan Janovic
|
|
* Matej Perina
|
|
* Peter Orsag
|
|
* Peter Paluch
|
|
* Frantisek Gazo
|
|
* Tomas Hvorkovy
|
|
* Martin Kontsek
|
|
* Lukas Koribsky
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
|
|
#include "frrevent.h"
|
|
#include "memory.h"
|
|
#include "linklist.h"
|
|
#include "prefix.h"
|
|
#include "if.h"
|
|
#include "table.h"
|
|
#include "sockunion.h"
|
|
#include "stream.h"
|
|
#include "log.h"
|
|
#include "sockopt.h"
|
|
#include "checksum.h"
|
|
#include "md5.h"
|
|
#include "vty.h"
|
|
#include "plist.h"
|
|
#include "plist_int.h"
|
|
#include "routemap.h"
|
|
#include "vty.h"
|
|
|
|
#include "eigrpd/eigrp_types.h"
|
|
#include "eigrpd/eigrp_structs.h"
|
|
#include "eigrpd/eigrpd.h"
|
|
#include "eigrpd/eigrp_interface.h"
|
|
#include "eigrpd/eigrp_neighbor.h"
|
|
#include "eigrpd/eigrp_packet.h"
|
|
#include "eigrpd/eigrp_zebra.h"
|
|
#include "eigrpd/eigrp_vty.h"
|
|
#include "eigrpd/eigrp_dump.h"
|
|
#include "eigrpd/eigrp_macros.h"
|
|
#include "eigrpd/eigrp_topology.h"
|
|
#include "eigrpd/eigrp_fsm.h"
|
|
#include "eigrpd/eigrp_network.h"
|
|
#include "eigrpd/eigrp_metric.h"
|
|
|
|
bool eigrp_update_prefix_apply(struct eigrp *eigrp, struct eigrp_interface *ei,
|
|
int in, struct prefix *prefix)
|
|
{
|
|
struct access_list *alist;
|
|
struct prefix_list *plist;
|
|
|
|
alist = eigrp->list[in];
|
|
if (alist && access_list_apply(alist, prefix) == FILTER_DENY)
|
|
return true;
|
|
|
|
plist = eigrp->prefix[in];
|
|
if (plist && prefix_list_apply(plist, prefix) == PREFIX_DENY)
|
|
return true;
|
|
|
|
alist = ei->list[in];
|
|
if (alist && access_list_apply(alist, prefix) == FILTER_DENY)
|
|
return true;
|
|
|
|
plist = ei->prefix[in];
|
|
if (plist && prefix_list_apply(plist, prefix) == PREFIX_DENY)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @fn remove_received_prefix_gr
|
|
*
|
|
* @param[in] nbr_prefixes List of neighbor prefixes
|
|
* @param[in] recv_prefix Prefix which needs to be removed from
|
|
* list
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function is used for removing received prefix
|
|
* from list of neighbor prefixes
|
|
*/
|
|
static void
|
|
remove_received_prefix_gr(struct list *nbr_prefixes,
|
|
struct eigrp_prefix_descriptor *recv_prefix)
|
|
{
|
|
struct listnode *node1, *node11;
|
|
struct eigrp_prefix_descriptor *prefix = NULL;
|
|
|
|
/* iterate over all prefixes in list */
|
|
for (ALL_LIST_ELEMENTS(nbr_prefixes, node1, node11, prefix)) {
|
|
/* remove prefix from list if found */
|
|
if (prefix == recv_prefix) {
|
|
listnode_delete(nbr_prefixes, prefix);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_receive_GR_ask
|
|
*
|
|
* @param[in] eigrp EIGRP process
|
|
* @param[in] nbr Neighbor update of who we
|
|
* received
|
|
* @param[in] nbr_prefixes Prefixes which weren't advertised
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function is used for notifying FSM about prefixes which
|
|
* weren't advertised by neighbor:
|
|
* We will send message to FSM with prefix delay set to infinity.
|
|
*/
|
|
static void eigrp_update_receive_GR_ask(struct eigrp *eigrp,
|
|
struct eigrp_neighbor *nbr,
|
|
struct list *nbr_prefixes)
|
|
{
|
|
struct listnode *node1;
|
|
struct eigrp_prefix_descriptor *prefix;
|
|
struct eigrp_fsm_action_message fsm_msg;
|
|
|
|
/* iterate over all prefixes which weren't advertised by neighbor */
|
|
for (ALL_LIST_ELEMENTS_RO(nbr_prefixes, node1, prefix)) {
|
|
zlog_debug("GR receive: Neighbor not advertised %pFX", &prefix->destination);
|
|
|
|
fsm_msg.metrics = prefix->reported_metric;
|
|
/* set delay to MAX */
|
|
fsm_msg.metrics.delay = EIGRP_MAX_METRIC;
|
|
|
|
struct eigrp_route_descriptor *entry =
|
|
eigrp_route_descriptor_lookup(prefix->entries, nbr);
|
|
|
|
fsm_msg.packet_type = EIGRP_OPC_UPDATE;
|
|
fsm_msg.eigrp = eigrp;
|
|
fsm_msg.data_type = EIGRP_INT;
|
|
fsm_msg.adv_router = nbr;
|
|
fsm_msg.entry = entry;
|
|
fsm_msg.prefix = prefix;
|
|
|
|
/* send message to FSM */
|
|
eigrp_fsm_event(&fsm_msg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EIGRP UPDATE read function
|
|
*/
|
|
void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,
|
|
struct eigrp_header *eigrph, struct stream *s,
|
|
struct eigrp_interface *ei, int size)
|
|
{
|
|
struct eigrp_neighbor *nbr;
|
|
struct TLV_IPv4_Internal_type *tlv;
|
|
struct eigrp_prefix_descriptor *pe;
|
|
struct eigrp_route_descriptor *ne;
|
|
uint32_t flags;
|
|
uint16_t type;
|
|
uint16_t length;
|
|
uint8_t same;
|
|
struct prefix dest_addr;
|
|
uint8_t graceful_restart;
|
|
uint8_t graceful_restart_final;
|
|
struct list *nbr_prefixes = NULL;
|
|
|
|
/* increment statistics. */
|
|
ei->update_in++;
|
|
|
|
/* get neighbor struct */
|
|
nbr = eigrp_nbr_get(ei, eigrph, iph);
|
|
|
|
/* neighbor must be valid, eigrp_nbr_get creates if none existed */
|
|
assert(nbr);
|
|
|
|
flags = ntohl(eigrph->flags);
|
|
|
|
if (flags & EIGRP_CR_FLAG) {
|
|
return;
|
|
}
|
|
|
|
same = 0;
|
|
graceful_restart = 0;
|
|
graceful_restart_final = 0;
|
|
if ((nbr->recv_sequence_number) == (ntohl(eigrph->sequence)))
|
|
same = 1;
|
|
|
|
nbr->recv_sequence_number = ntohl(eigrph->sequence);
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug(
|
|
"Processing Update size[%u] int(%s) nbr(%pI4) seq [%u] flags [%0x]",
|
|
size,
|
|
ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id),
|
|
&nbr->src, nbr->recv_sequence_number, flags);
|
|
|
|
|
|
if ((flags == (EIGRP_INIT_FLAG + EIGRP_RS_FLAG + EIGRP_EOT_FLAG))
|
|
&& (!same)) {
|
|
/* Graceful restart Update received with all routes */
|
|
|
|
zlog_info("Neighbor %pI4 (%s) is resync: peer graceful-restart",
|
|
&nbr->src,
|
|
ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));
|
|
|
|
/* get all prefixes from neighbor from topology table */
|
|
nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr);
|
|
graceful_restart = 1;
|
|
graceful_restart_final = 1;
|
|
} else if ((flags == (EIGRP_INIT_FLAG + EIGRP_RS_FLAG)) && (!same)) {
|
|
/* Graceful restart Update received, routes also in next packet
|
|
*/
|
|
|
|
zlog_info("Neighbor %pI4 (%s) is resync: peer graceful-restart",
|
|
&nbr->src,
|
|
ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));
|
|
|
|
/* get all prefixes from neighbor from topology table */
|
|
nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr);
|
|
/* save prefixes to neighbor for later use */
|
|
nbr->nbr_gr_prefixes = nbr_prefixes;
|
|
graceful_restart = 1;
|
|
graceful_restart_final = 0;
|
|
} else if ((flags == (EIGRP_EOT_FLAG)) && (!same)) {
|
|
/* If there was INIT+RS Update packet before,
|
|
* consider this as GR EOT */
|
|
|
|
if (nbr->nbr_gr_prefixes != NULL) {
|
|
/* this is final packet of GR */
|
|
nbr_prefixes = nbr->nbr_gr_prefixes;
|
|
nbr->nbr_gr_prefixes = NULL;
|
|
|
|
graceful_restart = 1;
|
|
graceful_restart_final = 1;
|
|
}
|
|
|
|
} else if ((flags == (0)) && (!same)) {
|
|
/* If there was INIT+RS Update packet before,
|
|
* consider this as GR not final packet */
|
|
|
|
if (nbr->nbr_gr_prefixes != NULL) {
|
|
/* this is GR not final route packet */
|
|
nbr_prefixes = nbr->nbr_gr_prefixes;
|
|
|
|
graceful_restart = 1;
|
|
graceful_restart_final = 0;
|
|
}
|
|
|
|
} else if ((flags & EIGRP_INIT_FLAG)
|
|
&& (!same)) { /* When in pending state, send INIT update only
|
|
if it wasn't
|
|
already sent before (only if init_sequence
|
|
is 0) */
|
|
if ((nbr->state == EIGRP_NEIGHBOR_PENDING)
|
|
&& (nbr->init_sequence_number == 0))
|
|
eigrp_update_send_init(nbr);
|
|
|
|
if (nbr->state == EIGRP_NEIGHBOR_UP) {
|
|
eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN);
|
|
eigrp_topology_neighbor_down(nbr->ei->eigrp, nbr);
|
|
nbr->recv_sequence_number = ntohl(eigrph->sequence);
|
|
zlog_info("Neighbor %pI4 (%s) is down: peer restarted",
|
|
&nbr->src,
|
|
ifindex2ifname(nbr->ei->ifp->ifindex,
|
|
eigrp->vrf_id));
|
|
eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING);
|
|
zlog_info(
|
|
"Neighbor %pI4 (%s) is pending: new adjacency",
|
|
&nbr->src,
|
|
ifindex2ifname(nbr->ei->ifp->ifindex,
|
|
eigrp->vrf_id));
|
|
eigrp_update_send_init(nbr);
|
|
}
|
|
}
|
|
|
|
/*If there is topology information*/
|
|
while (s->endp > s->getp) {
|
|
type = stream_getw(s);
|
|
switch (type) {
|
|
case EIGRP_TLV_IPv4_INT:
|
|
stream_set_getp(s, s->getp - sizeof(uint16_t));
|
|
|
|
tlv = eigrp_read_ipv4_tlv(s);
|
|
|
|
/*searching if destination exists */
|
|
dest_addr.family = AF_INET;
|
|
dest_addr.u.prefix4 = tlv->destination;
|
|
dest_addr.prefixlen = tlv->prefix_length;
|
|
struct eigrp_prefix_descriptor *dest =
|
|
eigrp_topology_table_lookup_ipv4(
|
|
eigrp->topology_table, &dest_addr);
|
|
|
|
/*if exists it comes to DUAL*/
|
|
if (dest != NULL) {
|
|
/* remove received prefix from neighbor prefix
|
|
* list if in GR */
|
|
if (graceful_restart)
|
|
remove_received_prefix_gr(nbr_prefixes,
|
|
dest);
|
|
|
|
struct eigrp_fsm_action_message msg;
|
|
struct eigrp_route_descriptor *entry =
|
|
eigrp_route_descriptor_lookup(
|
|
dest->entries, nbr);
|
|
|
|
msg.packet_type = EIGRP_OPC_UPDATE;
|
|
msg.eigrp = eigrp;
|
|
msg.data_type = EIGRP_INT;
|
|
msg.adv_router = nbr;
|
|
msg.metrics = tlv->metric;
|
|
msg.entry = entry;
|
|
msg.prefix = dest;
|
|
eigrp_fsm_event(&msg);
|
|
} else {
|
|
/*Here comes topology information save*/
|
|
pe = eigrp_prefix_descriptor_new();
|
|
pe->serno = eigrp->serno;
|
|
prefix_copy(&pe->destination, &dest_addr);
|
|
pe->af = AF_INET;
|
|
pe->state = EIGRP_FSM_STATE_PASSIVE;
|
|
pe->nt = EIGRP_TOPOLOGY_TYPE_REMOTE;
|
|
|
|
ne = eigrp_route_descriptor_new();
|
|
ne->ei = ei;
|
|
ne->adv_router = nbr;
|
|
ne->reported_metric = tlv->metric;
|
|
ne->reported_distance = eigrp_calculate_metrics(
|
|
eigrp, tlv->metric);
|
|
/*
|
|
* Filtering
|
|
*/
|
|
if (eigrp_update_prefix_apply(eigrp, ei,
|
|
EIGRP_FILTER_IN,
|
|
&dest_addr))
|
|
ne->reported_metric.delay =
|
|
EIGRP_MAX_METRIC;
|
|
|
|
ne->distance = eigrp_calculate_total_metrics(
|
|
eigrp, ne);
|
|
|
|
pe->fdistance = pe->distance = pe->rdistance =
|
|
ne->distance;
|
|
ne->prefix = pe;
|
|
ne->flags =
|
|
EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
|
|
|
|
eigrp_prefix_descriptor_add(
|
|
eigrp->topology_table, pe);
|
|
eigrp_route_descriptor_add(eigrp, pe, ne);
|
|
pe->distance = pe->fdistance = pe->rdistance =
|
|
ne->distance;
|
|
pe->reported_metric = ne->total_metric;
|
|
eigrp_topology_update_node_flags(eigrp, pe);
|
|
|
|
pe->req_action |= EIGRP_FSM_NEED_UPDATE;
|
|
listnode_add(
|
|
eigrp->topology_changes_internalIPV4,
|
|
pe);
|
|
}
|
|
eigrp_IPv4_InternalTLV_free(tlv);
|
|
break;
|
|
|
|
case EIGRP_TLV_IPv4_EXT:
|
|
/* DVS: processing of external routes needs packet and fsm work.
|
|
* for now, lets just not creash the box
|
|
*/
|
|
default:
|
|
length = stream_getw(s);
|
|
// -2 for type, -2 for len
|
|
for (length -= 4; length; length--) {
|
|
(void)stream_getc(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ask about prefixes not present in GR update,
|
|
* if this is final GR packet */
|
|
if (graceful_restart_final) {
|
|
eigrp_update_receive_GR_ask(eigrp, nbr, nbr_prefixes);
|
|
}
|
|
|
|
/*
|
|
* We don't need to send separate Ack for INIT Update. INIT will be
|
|
* acked in EOT Update.
|
|
*/
|
|
if ((nbr->state == EIGRP_NEIGHBOR_UP) && !(flags == EIGRP_INIT_FLAG)) {
|
|
eigrp_hello_send_ack(nbr);
|
|
}
|
|
|
|
eigrp_query_send_all(eigrp);
|
|
eigrp_update_send_all(eigrp, ei);
|
|
|
|
if (nbr_prefixes)
|
|
list_delete(&nbr_prefixes);
|
|
}
|
|
|
|
/*send EIGRP Update packet*/
|
|
void eigrp_update_send_init(struct eigrp_neighbor *nbr)
|
|
{
|
|
struct eigrp_packet *ep;
|
|
uint16_t length = EIGRP_HEADER_LEN;
|
|
|
|
ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr);
|
|
|
|
/* Prepare EIGRP INIT UPDATE header */
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug("Enqueuing Update Init Seq [%u] Ack [%u]",
|
|
nbr->ei->eigrp->sequence_number,
|
|
nbr->recv_sequence_number);
|
|
|
|
eigrp_packet_header_init(
|
|
EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s, EIGRP_INIT_FLAG,
|
|
nbr->ei->eigrp->sequence_number, nbr->recv_sequence_number);
|
|
|
|
// encode Authentication TLV, if needed
|
|
if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (nbr->ei->params.auth_keychain != NULL)) {
|
|
length += eigrp_add_authTLV_MD5_to_stream(ep->s, nbr->ei);
|
|
eigrp_make_md5_digest(nbr->ei, ep->s,
|
|
EIGRP_AUTH_UPDATE_INIT_FLAG);
|
|
}
|
|
|
|
/* EIGRP Checksum */
|
|
eigrp_packet_checksum(nbr->ei, ep->s, length);
|
|
|
|
ep->length = length;
|
|
ep->dst.s_addr = nbr->src.s_addr;
|
|
|
|
/*This ack number we await from neighbor*/
|
|
nbr->init_sequence_number = nbr->ei->eigrp->sequence_number;
|
|
ep->sequence_number = nbr->ei->eigrp->sequence_number;
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug(
|
|
"Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]",
|
|
ep->length, ep->sequence_number, &ep->dst);
|
|
|
|
/*Put packet to retransmission queue*/
|
|
eigrp_fifo_push(nbr->retrans_queue, ep);
|
|
|
|
if (nbr->retrans_queue->count == 1) {
|
|
eigrp_send_packet_reliably(nbr);
|
|
}
|
|
}
|
|
|
|
static void eigrp_update_place_on_nbr_queue(struct eigrp_neighbor *nbr,
|
|
struct eigrp_packet *ep,
|
|
uint32_t seq_no, int length)
|
|
{
|
|
if ((nbr->ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (nbr->ei->params.auth_keychain != NULL)) {
|
|
eigrp_make_md5_digest(nbr->ei, ep->s, EIGRP_AUTH_UPDATE_FLAG);
|
|
}
|
|
|
|
/* EIGRP Checksum */
|
|
eigrp_packet_checksum(nbr->ei, ep->s, length);
|
|
|
|
ep->length = length;
|
|
ep->dst.s_addr = nbr->src.s_addr;
|
|
|
|
/*This ack number we await from neighbor*/
|
|
ep->sequence_number = seq_no;
|
|
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug(
|
|
"Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]",
|
|
ep->length, ep->sequence_number, &ep->dst);
|
|
|
|
/*Put packet to retransmission queue*/
|
|
eigrp_fifo_push(nbr->retrans_queue, ep);
|
|
|
|
if (nbr->retrans_queue->count == 1)
|
|
eigrp_send_packet_reliably(nbr);
|
|
}
|
|
|
|
static void eigrp_update_send_to_all_nbrs(struct eigrp_interface *ei,
|
|
struct eigrp_packet *ep)
|
|
{
|
|
struct eigrp_neighbor *nbr;
|
|
bool packet_sent = false;
|
|
|
|
frr_each (eigrp_nbr_hash, &ei->nbr_hash_head, nbr) {
|
|
struct eigrp_packet *ep_dup;
|
|
|
|
if (nbr->state != EIGRP_NEIGHBOR_UP)
|
|
continue;
|
|
|
|
if (packet_sent)
|
|
ep_dup = eigrp_packet_duplicate(ep, NULL);
|
|
else
|
|
ep_dup = ep;
|
|
|
|
ep_dup->nbr = nbr;
|
|
packet_sent = true;
|
|
/*Put packet to retransmission queue*/
|
|
eigrp_fifo_push(nbr->retrans_queue, ep_dup);
|
|
|
|
if (nbr->retrans_queue->count == 1) {
|
|
eigrp_send_packet_reliably(nbr);
|
|
}
|
|
}
|
|
|
|
if (!packet_sent)
|
|
eigrp_packet_free(ep);
|
|
}
|
|
|
|
void eigrp_update_send_EOT(struct eigrp_neighbor *nbr)
|
|
{
|
|
struct eigrp_packet *ep;
|
|
uint16_t length = EIGRP_HEADER_LEN;
|
|
struct eigrp_route_descriptor *te;
|
|
struct eigrp_prefix_descriptor *pe;
|
|
struct listnode *node2, *nnode2;
|
|
struct eigrp_interface *ei = nbr->ei;
|
|
struct eigrp *eigrp = ei->eigrp;
|
|
struct prefix *dest_addr;
|
|
uint32_t seq_no = eigrp->sequence_number;
|
|
uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
|
|
struct route_node *rn;
|
|
|
|
ep = eigrp_packet_new(eigrp_mtu, nbr);
|
|
|
|
/* Prepare EIGRP EOT UPDATE header */
|
|
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, EIGRP_EOT_FLAG,
|
|
seq_no, nbr->recv_sequence_number);
|
|
|
|
// encode Authentication TLV, if needed
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei);
|
|
}
|
|
|
|
for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
|
|
if (!rn->info)
|
|
continue;
|
|
|
|
pe = rn->info;
|
|
for (ALL_LIST_ELEMENTS(pe->entries, node2, nnode2, te)) {
|
|
if (eigrp_nbr_split_horizon_check(te, ei))
|
|
continue;
|
|
|
|
if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) {
|
|
eigrp_update_place_on_nbr_queue(nbr, ep, seq_no,
|
|
length);
|
|
seq_no++;
|
|
|
|
length = EIGRP_HEADER_LEN;
|
|
ep = eigrp_packet_new(eigrp_mtu, nbr);
|
|
eigrp_packet_header_init(
|
|
EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s,
|
|
EIGRP_EOT_FLAG, seq_no,
|
|
nbr->recv_sequence_number);
|
|
|
|
if ((ei->params.auth_type
|
|
== EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
length +=
|
|
eigrp_add_authTLV_MD5_to_stream(
|
|
ep->s, ei);
|
|
}
|
|
}
|
|
/* Get destination address from prefix */
|
|
dest_addr = &pe->destination;
|
|
|
|
/* Check if any list fits */
|
|
if (eigrp_update_prefix_apply(
|
|
eigrp, ei, EIGRP_FILTER_OUT, dest_addr))
|
|
continue;
|
|
else {
|
|
length += eigrp_add_internalTLV_to_stream(ep->s,
|
|
pe);
|
|
}
|
|
}
|
|
}
|
|
|
|
eigrp_update_place_on_nbr_queue(nbr, ep, seq_no, length);
|
|
eigrp->sequence_number = seq_no++;
|
|
}
|
|
|
|
void eigrp_update_send(struct eigrp_interface *ei)
|
|
{
|
|
struct eigrp_packet *ep;
|
|
struct listnode *node, *nnode;
|
|
struct eigrp_prefix_descriptor *pe;
|
|
uint8_t has_tlv;
|
|
struct eigrp *eigrp = ei->eigrp;
|
|
struct prefix *dest_addr;
|
|
uint32_t seq_no = eigrp->sequence_number;
|
|
uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
|
|
|
|
if (eigrp_nbr_hash_count(&ei->nbr_hash_head) == 0)
|
|
return;
|
|
|
|
uint16_t length = EIGRP_HEADER_LEN;
|
|
|
|
ep = eigrp_packet_new(eigrp_mtu, NULL);
|
|
|
|
/* Prepare EIGRP INIT UPDATE header */
|
|
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, 0, seq_no, 0);
|
|
|
|
// encode Authentication TLV, if needed
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei);
|
|
}
|
|
|
|
has_tlv = 0;
|
|
for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node,
|
|
nnode, pe)) {
|
|
struct eigrp_route_descriptor *ne;
|
|
|
|
if (!(pe->req_action & EIGRP_FSM_NEED_UPDATE))
|
|
continue;
|
|
|
|
ne = listnode_head(pe->entries);
|
|
if (eigrp_nbr_split_horizon_check(ne, ei))
|
|
continue;
|
|
|
|
if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) {
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
eigrp_make_md5_digest(ei, ep->s,
|
|
EIGRP_AUTH_UPDATE_FLAG);
|
|
}
|
|
|
|
eigrp_packet_checksum(ei, ep->s, length);
|
|
ep->length = length;
|
|
|
|
ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS);
|
|
|
|
ep->sequence_number = seq_no;
|
|
seq_no++;
|
|
eigrp_update_send_to_all_nbrs(ei, ep);
|
|
|
|
length = EIGRP_HEADER_LEN;
|
|
ep = eigrp_packet_new(eigrp_mtu, NULL);
|
|
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s,
|
|
0, seq_no, 0);
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
length += eigrp_add_authTLV_MD5_to_stream(ep->s,
|
|
ei);
|
|
}
|
|
has_tlv = 0;
|
|
}
|
|
/* Get destination address from prefix */
|
|
dest_addr = &pe->destination;
|
|
|
|
if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT,
|
|
dest_addr)) {
|
|
// pe->reported_metric.delay = EIGRP_MAX_METRIC;
|
|
continue;
|
|
} else {
|
|
length += eigrp_add_internalTLV_to_stream(ep->s, pe);
|
|
has_tlv = 1;
|
|
}
|
|
}
|
|
|
|
if (!has_tlv) {
|
|
eigrp_packet_free(ep);
|
|
return;
|
|
}
|
|
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG);
|
|
}
|
|
|
|
/* EIGRP Checksum */
|
|
eigrp_packet_checksum(ei, ep->s, length);
|
|
ep->length = length;
|
|
|
|
ep->dst.s_addr = htonl(EIGRP_MULTICAST_ADDRESS);
|
|
|
|
/*This ack number we await from neighbor*/
|
|
ep->sequence_number = eigrp->sequence_number;
|
|
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug("Enqueuing Update length[%u] Seq [%u]", length,
|
|
ep->sequence_number);
|
|
|
|
eigrp_update_send_to_all_nbrs(ei, ep);
|
|
ei->eigrp->sequence_number = seq_no++;
|
|
}
|
|
|
|
void eigrp_update_send_all(struct eigrp *eigrp,
|
|
struct eigrp_interface *exception)
|
|
{
|
|
struct eigrp_interface *iface;
|
|
struct listnode *node2, *nnode2;
|
|
struct eigrp_prefix_descriptor *pe;
|
|
|
|
frr_each (eigrp_interface_hash, &eigrp->eifs, iface) {
|
|
if (iface != exception) {
|
|
eigrp_update_send(iface);
|
|
}
|
|
}
|
|
|
|
for (ALL_LIST_ELEMENTS(eigrp->topology_changes_internalIPV4, node2,
|
|
nnode2, pe)) {
|
|
if (pe->req_action & EIGRP_FSM_NEED_UPDATE) {
|
|
pe->req_action &= ~EIGRP_FSM_NEED_UPDATE;
|
|
listnode_delete(eigrp->topology_changes_internalIPV4,
|
|
pe);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_send_GR_part
|
|
*
|
|
* @param[in] nbr contains neighbor who would receive
|
|
* Graceful
|
|
* restart
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function used for sending Graceful restart Update packet
|
|
* and if there are multiple chunks, send only one of them.
|
|
* It is called from thread. Do not call it directly.
|
|
*
|
|
* Uses nbr_gr_packet_type from neighbor.
|
|
*/
|
|
static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr)
|
|
{
|
|
struct eigrp_packet *ep;
|
|
uint16_t length = EIGRP_HEADER_LEN;
|
|
struct eigrp_prefix_descriptor *pe;
|
|
struct prefix *dest_addr;
|
|
struct eigrp_interface *ei = nbr->ei;
|
|
struct eigrp *eigrp = ei->eigrp;
|
|
struct list *prefixes;
|
|
uint32_t flags;
|
|
unsigned int send_prefixes;
|
|
struct route_node *rn;
|
|
|
|
/* get prefixes to send to neighbor */
|
|
prefixes = nbr->nbr_gr_prefixes_send;
|
|
|
|
send_prefixes = 0;
|
|
|
|
/* if there already were last packet chunk, we won't continue */
|
|
if (nbr->nbr_gr_packet_type == EIGRP_PACKET_PART_LAST)
|
|
return;
|
|
|
|
/* if this is first packet chunk, we need to decide,
|
|
* if there will be one or more chunks */
|
|
if (nbr->nbr_gr_packet_type == EIGRP_PACKET_PART_FIRST) {
|
|
if (prefixes->count <= EIGRP_TLV_MAX_IPv4) {
|
|
/* there will be only one chunk */
|
|
flags = EIGRP_INIT_FLAG + EIGRP_RS_FLAG
|
|
+ EIGRP_EOT_FLAG;
|
|
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_LAST;
|
|
} else {
|
|
/* there will be more chunks */
|
|
flags = EIGRP_INIT_FLAG + EIGRP_RS_FLAG;
|
|
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_NA;
|
|
}
|
|
} else {
|
|
/* this is not first chunk, and we need to decide,
|
|
* if there will be more chunks */
|
|
if (prefixes->count <= EIGRP_TLV_MAX_IPv4) {
|
|
/* this is last chunk */
|
|
flags = EIGRP_EOT_FLAG;
|
|
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_LAST;
|
|
} else {
|
|
/* there will be more chunks */
|
|
flags = 0;
|
|
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_NA;
|
|
}
|
|
}
|
|
|
|
ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr);
|
|
|
|
/* Prepare EIGRP Graceful restart UPDATE header */
|
|
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, flags,
|
|
eigrp->sequence_number,
|
|
nbr->recv_sequence_number);
|
|
|
|
// encode Authentication TLV, if needed
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei);
|
|
}
|
|
|
|
for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
|
|
if (!rn->info)
|
|
continue;
|
|
|
|
pe = rn->info;
|
|
/*
|
|
* Filtering
|
|
*/
|
|
dest_addr = &pe->destination;
|
|
|
|
if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT,
|
|
dest_addr)) {
|
|
/* do not send filtered route */
|
|
zlog_info("Filtered prefix %pI4 won't be sent out.",
|
|
&dest_addr->u.prefix4);
|
|
} else {
|
|
/* sending route which wasn't filtered */
|
|
length += eigrp_add_internalTLV_to_stream(ep->s, pe);
|
|
send_prefixes++;
|
|
}
|
|
|
|
/*
|
|
* This makes no sense, Filter out then filter in???
|
|
* Look into this more - DBS
|
|
*/
|
|
if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_IN,
|
|
dest_addr)) {
|
|
/* do not send filtered route */
|
|
zlog_info("Filtered prefix %pI4 will be removed.",
|
|
&dest_addr->u.prefix4);
|
|
|
|
/* prepare message for FSM */
|
|
struct eigrp_fsm_action_message fsm_msg;
|
|
|
|
struct eigrp_route_descriptor *entry =
|
|
eigrp_route_descriptor_lookup(pe->entries, nbr);
|
|
|
|
fsm_msg.packet_type = EIGRP_OPC_UPDATE;
|
|
fsm_msg.eigrp = eigrp;
|
|
fsm_msg.data_type = EIGRP_INT;
|
|
fsm_msg.adv_router = nbr;
|
|
fsm_msg.metrics = pe->reported_metric;
|
|
/* Set delay to MAX */
|
|
fsm_msg.metrics.delay = EIGRP_MAX_METRIC;
|
|
fsm_msg.entry = entry;
|
|
fsm_msg.prefix = pe;
|
|
|
|
/* send message to FSM */
|
|
eigrp_fsm_event(&fsm_msg);
|
|
}
|
|
|
|
/* delete processed prefix from list */
|
|
listnode_delete(prefixes, pe);
|
|
|
|
/* if there are enough prefixes, send packet */
|
|
if (send_prefixes >= EIGRP_TLV_MAX_IPv4)
|
|
break;
|
|
}
|
|
|
|
/* compute Auth digest */
|
|
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
|
|
&& (ei->params.auth_keychain != NULL)) {
|
|
eigrp_make_md5_digest(ei, ep->s, EIGRP_AUTH_UPDATE_FLAG);
|
|
}
|
|
|
|
/* EIGRP Checksum */
|
|
eigrp_packet_checksum(ei, ep->s, length);
|
|
|
|
ep->length = length;
|
|
ep->dst.s_addr = nbr->src.s_addr;
|
|
|
|
/*This ack number we await from neighbor*/
|
|
ep->sequence_number = eigrp->sequence_number;
|
|
|
|
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
|
|
zlog_debug(
|
|
"Enqueuing Update Init Len [%u] Seq [%u] Dest [%pI4]",
|
|
ep->length, ep->sequence_number, &ep->dst);
|
|
|
|
/*Put packet to retransmission queue*/
|
|
eigrp_fifo_push(nbr->retrans_queue, ep);
|
|
|
|
if (nbr->retrans_queue->count == 1) {
|
|
eigrp_send_packet_reliably(nbr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_send_GR_thread
|
|
*
|
|
* @param[in] thread contains neighbor who would receive
|
|
* Graceful restart
|
|
*
|
|
* @return int always 0
|
|
*
|
|
* @par
|
|
* Function used for sending Graceful restart Update packet
|
|
* in thread, it is prepared for multiple chunks of packet.
|
|
*
|
|
* Uses nbr_gr_packet_type and t_nbr_send_gr from neighbor.
|
|
*/
|
|
void eigrp_update_send_GR_thread(struct event *thread)
|
|
{
|
|
struct eigrp_neighbor *nbr;
|
|
|
|
/* get argument from thread */
|
|
nbr = EVENT_ARG(thread);
|
|
/* remove this thread pointer */
|
|
|
|
/* if there is packet waiting in queue,
|
|
* schedule this thread again with small delay */
|
|
if (nbr->retrans_queue->count > 0) {
|
|
event_add_timer_msec(master, eigrp_update_send_GR_thread, nbr,
|
|
10, &nbr->t_nbr_send_gr);
|
|
return;
|
|
}
|
|
|
|
/* send GR EIGRP packet chunk */
|
|
eigrp_update_send_GR_part(nbr);
|
|
|
|
/* if it wasn't last chunk, schedule this thread again */
|
|
if (nbr->nbr_gr_packet_type != EIGRP_PACKET_PART_LAST) {
|
|
event_execute(master, eigrp_update_send_GR_thread, nbr, 0, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_send_GR
|
|
*
|
|
* @param[in] nbr Neighbor who would receive
|
|
* Graceful
|
|
* restart
|
|
* @param[in] gr_type Who executed Graceful restart
|
|
* @param[in] vty Virtual terminal for log output
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function used for sending Graceful restart Update packet:
|
|
* Creates Update packet with INIT, RS, EOT flags and include
|
|
* all route except those filtered
|
|
*/
|
|
void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type,
|
|
struct vty *vty)
|
|
{
|
|
struct eigrp_prefix_descriptor *pe2;
|
|
struct list *prefixes;
|
|
struct route_node *rn;
|
|
struct eigrp_interface *ei = nbr->ei;
|
|
struct eigrp *eigrp = ei->eigrp;
|
|
|
|
if (gr_type == EIGRP_GR_FILTER) {
|
|
/* function was called after applying filtration */
|
|
zlog_info(
|
|
"Neighbor %pI4 (%s) is resync: route configuration changed",
|
|
&nbr->src,
|
|
ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id));
|
|
} else if (gr_type == EIGRP_GR_MANUAL) {
|
|
/* Graceful restart was called manually */
|
|
zlog_info("Neighbor %pI4 (%s) is resync: manually cleared",
|
|
&nbr->src,
|
|
ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id));
|
|
|
|
if (vty != NULL) {
|
|
vty_time_print(vty, 0);
|
|
vty_out(vty,
|
|
"Neighbor %pI4 (%s) is resync: manually cleared\n",
|
|
&nbr->src,
|
|
ifindex2ifname(ei->ifp->ifindex,
|
|
eigrp->vrf_id));
|
|
}
|
|
}
|
|
|
|
prefixes = list_new();
|
|
/* add all prefixes from topology table to list */
|
|
for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
|
|
if (!rn->info)
|
|
continue;
|
|
|
|
pe2 = rn->info;
|
|
listnode_add(prefixes, pe2);
|
|
}
|
|
|
|
/* save prefixes to neighbor */
|
|
nbr->nbr_gr_prefixes_send = prefixes;
|
|
/* indicate, that this is first GR Update packet chunk */
|
|
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_FIRST;
|
|
/* execute packet sending in thread */
|
|
event_execute(master, eigrp_update_send_GR_thread, nbr, 0, NULL);
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_send_interface_GR
|
|
*
|
|
* @param[in] ei Interface to neighbors of which
|
|
* the
|
|
* GR
|
|
* is sent
|
|
* @param[in] gr_type Who executed Graceful restart
|
|
* @param[in] vty Virtual terminal for log output
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function used for sending Graceful restart Update packet
|
|
* to all neighbors on specified interface.
|
|
*/
|
|
void eigrp_update_send_interface_GR(struct eigrp_interface *ei,
|
|
enum GR_type gr_type, struct vty *vty)
|
|
{
|
|
struct eigrp_neighbor *nbr;
|
|
|
|
/* iterate over all neighbors on eigrp interface */
|
|
frr_each (eigrp_nbr_hash, &ei->nbr_hash_head, nbr) {
|
|
/* send GR to neighbor */
|
|
eigrp_update_send_GR(nbr, gr_type, vty);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn eigrp_update_send_process_GR
|
|
*
|
|
* @param[in] eigrp EIGRP process
|
|
* @param[in] gr_type Who executed Graceful restart
|
|
* @param[in] vty Virtual terminal for log output
|
|
*
|
|
* @return void
|
|
*
|
|
* @par
|
|
* Function used for sending Graceful restart Update packet
|
|
* to all neighbors in eigrp process.
|
|
*/
|
|
void eigrp_update_send_process_GR(struct eigrp *eigrp, enum GR_type gr_type,
|
|
struct vty *vty)
|
|
{
|
|
struct eigrp_interface *ei;
|
|
|
|
/* iterate over all eigrp interfaces */
|
|
frr_each (eigrp_interface_hash, &eigrp->eifs, ei) {
|
|
/* send GR to all neighbors on interface */
|
|
eigrp_update_send_interface_GR(ei, gr_type, vty);
|
|
}
|
|
}
|