// SPDX-License-Identifier: GPL-2.0-or-later /* * EIGRP Neighbor Handling. * Copyright (C) 2013-2016 * Authors: * Donnie Savage * Jan Janovic * Matej Perina * Peter Orsag * Peter Paluch * Frantisek Gazo * Tomas Hvorkovy * Martin Kontsek * Lukas Koribsky */ #include #include "linklist.h" #include "prefix.h" #include "memory.h" #include "command.h" #include "frrevent.h" #include "stream.h" #include "table.h" #include "log.h" #include "keychain.h" #include "vty.h" #include "eigrpd/eigrp_structs.h" #include "eigrpd/eigrpd.h" #include "eigrpd/eigrp_interface.h" #include "eigrpd/eigrp_neighbor.h" #include "eigrpd/eigrp_dump.h" #include "eigrpd/eigrp_packet.h" #include "eigrpd/eigrp_zebra.h" #include "eigrpd/eigrp_vty.h" #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_errors.h" DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_NEIGHBOR, "EIGRP neighbor"); struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei) { struct eigrp_neighbor *nbr; /* Allcate new neighbor. */ nbr = XCALLOC(MTYPE_EIGRP_NEIGHBOR, sizeof(struct eigrp_neighbor)); /* Relate neighbor to the interface. */ nbr->ei = ei; /* Set default values. */ eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); return nbr; } /** *@fn void dissect_eigrp_sw_version (tvbuff_t *tvb, proto_tree *tree, * proto_item *ti) * * @par * Create a new neighbor structure and initalize it. */ static struct eigrp_neighbor *eigrp_nbr_add(struct eigrp_interface *ei, struct eigrp_header *eigrph, struct ip *iph) { struct eigrp_neighbor *nbr; nbr = eigrp_nbr_new(ei); nbr->src = iph->ip_src; return nbr; } struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, struct eigrp_header *eigrph, struct ip *iph) { struct eigrp_neighbor *nbr; struct listnode *node, *nnode; for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { if (iph->ip_src.s_addr == nbr->src.s_addr) { return nbr; } } nbr = eigrp_nbr_add(ei, eigrph, iph); listnode_add(ei->nbrs, nbr); return nbr; } /** * @fn eigrp_nbr_lookup_by_addr * * @param[in] ei EIGRP interface * @param[in] nbr_addr Address of neighbor * * @return void * * @par * Function is used for neighbor lookup by address * in specified interface. */ struct eigrp_neighbor *eigrp_nbr_lookup_by_addr(struct eigrp_interface *ei, struct in_addr *addr) { struct eigrp_neighbor *nbr; struct listnode *node, *nnode; for (ALL_LIST_ELEMENTS(ei->nbrs, node, nnode, nbr)) { if (addr->s_addr == nbr->src.s_addr) { return nbr; } } return NULL; } /** * @fn eigrp_nbr_lookup_by_addr_process * * @param[in] eigrp EIGRP process * @param[in] nbr_addr Address of neighbor * * @return void * * @par * Function is used for neighbor lookup by address * in whole EIGRP process. */ struct eigrp_neighbor *eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, struct in_addr nbr_addr) { struct eigrp_interface *ei; struct listnode *node, *node2, *nnode2; struct eigrp_neighbor *nbr; /* iterate over all eigrp interfaces */ for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { /* iterate over all neighbors on eigrp interface */ for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { /* compare if neighbor address is same as arg address */ if (nbr->src.s_addr == nbr_addr.s_addr) { return nbr; } } } return NULL; } /* Delete specified EIGRP neighbor from interface. */ void eigrp_nbr_delete(struct eigrp_neighbor *nbr) { eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); if (nbr->ei) eigrp_topology_neighbor_down(nbr->ei->eigrp, nbr); /* Cancel all events. */ /* Thread lookup cost would be negligible. */ event_cancel_event(master, nbr); eigrp_fifo_free(nbr->multicast_queue); eigrp_fifo_free(nbr->retrans_queue); EVENT_OFF(nbr->t_holddown); if (nbr->ei) listnode_delete(nbr->ei->nbrs, nbr); XFREE(MTYPE_EIGRP_NEIGHBOR, nbr); } void holddown_timer_expired(struct event *thread) { struct eigrp_neighbor *nbr = EVENT_ARG(thread); struct eigrp *eigrp = nbr->ei->eigrp; zlog_info("Neighbor %pI4 (%s) is down: holding time expired", &nbr->src, ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); nbr->state = EIGRP_NEIGHBOR_DOWN; eigrp_nbr_delete(nbr); } uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *nbr) { return (nbr->state); } void eigrp_nbr_state_set(struct eigrp_neighbor *nbr, uint8_t state) { nbr->state = state; if (eigrp_nbr_state_get(nbr) == EIGRP_NEIGHBOR_DOWN) { // reset all the seq/ack counters nbr->recv_sequence_number = 0; nbr->init_sequence_number = 0; nbr->retrans_counter = 0; // Kvalues nbr->K1 = EIGRP_K1_DEFAULT; nbr->K2 = EIGRP_K2_DEFAULT; nbr->K3 = EIGRP_K3_DEFAULT; nbr->K4 = EIGRP_K4_DEFAULT; nbr->K5 = EIGRP_K5_DEFAULT; nbr->K6 = EIGRP_K6_DEFAULT; // hold time.. nbr->v_holddown = EIGRP_HOLD_INTERVAL_DEFAULT; EVENT_OFF(nbr->t_holddown); /* out with the old */ if (nbr->multicast_queue) eigrp_fifo_free(nbr->multicast_queue); if (nbr->retrans_queue) eigrp_fifo_free(nbr->retrans_queue); /* in with the new */ nbr->retrans_queue = eigrp_fifo_new(); nbr->multicast_queue = eigrp_fifo_new(); nbr->crypt_seqnum = 0; } } const char *eigrp_nbr_state_str(struct eigrp_neighbor *nbr) { const char *state; switch (nbr->state) { case EIGRP_NEIGHBOR_DOWN: state = "Down"; break; case EIGRP_NEIGHBOR_PENDING: state = "Waiting for Init"; break; case EIGRP_NEIGHBOR_UP: state = "Up"; break; default: state = "Unknown"; break; } return (state); } void eigrp_nbr_state_update(struct eigrp_neighbor *nbr) { switch (nbr->state) { case EIGRP_NEIGHBOR_DOWN: { /*Start Hold Down Timer for neighbor*/ // EVENT_OFF(nbr->t_holddown); // EVENT_TIMER_ON(master, nbr->t_holddown, // holddown_timer_expired, // nbr, nbr->v_holddown); break; } case EIGRP_NEIGHBOR_PENDING: { /*Reset Hold Down Timer for neighbor*/ EVENT_OFF(nbr->t_holddown); event_add_timer(master, holddown_timer_expired, nbr, nbr->v_holddown, &nbr->t_holddown); break; } case EIGRP_NEIGHBOR_UP: { /*Reset Hold Down Timer for neighbor*/ EVENT_OFF(nbr->t_holddown); event_add_timer(master, holddown_timer_expired, nbr, nbr->v_holddown, &nbr->t_holddown); break; } } } int eigrp_nbr_count_get(struct eigrp *eigrp) { struct eigrp_interface *iface; struct listnode *node, *node2, *nnode2; struct eigrp_neighbor *nbr; uint32_t counter; counter = 0; for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) { for (ALL_LIST_ELEMENTS(iface->nbrs, node2, nnode2, nbr)) { if (nbr->state == EIGRP_NEIGHBOR_UP) { counter++; } } } return counter; } /** * @fn eigrp_nbr_hard_restart * * @param[in] nbr Neighbor who would receive hard restart * @param[in] vty Virtual terminal for log output * @return void * * @par * Function used for executing hard restart for neighbor: * Send Hello packet with Peer Termination TLV with * neighbor's address, set it's state to DOWN and delete the neighbor */ void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty) { struct eigrp *eigrp = nbr->ei->eigrp; zlog_debug("Neighbor %pI4 (%s) is down: manually cleared", &nbr->src, ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); if (vty != NULL) { vty_time_print(vty, 0); vty_out(vty, "Neighbor %pI4 (%s) is down: manually cleared\n", &nbr->src, ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); } /* send Hello with Peer Termination TLV */ eigrp_hello_send(nbr->ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR, &(nbr->src)); /* set neighbor to DOWN */ nbr->state = EIGRP_NEIGHBOR_DOWN; /* delete neighbor */ eigrp_nbr_delete(nbr); } int eigrp_nbr_split_horizon_check(struct eigrp_route_descriptor *ne, struct eigrp_interface *ei) { if (ne->distance == EIGRP_MAX_METRIC) return 0; return (ne->ei == ei); }