diff options
Diffstat (limited to 'eigrpd/eigrp_hello.c')
-rw-r--r-- | eigrpd/eigrp_hello.c | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c new file mode 100644 index 0000000..ee0e245 --- /dev/null +++ b/eigrpd/eigrp_hello.c @@ -0,0 +1,783 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EIGRP Sending and Receiving EIGRP Hello 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 "vty.h" +#include "md5.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_errors.h" + +/* Packet Type String. */ +static const struct message eigrp_general_tlv_type_str[] = { + {EIGRP_TLV_PARAMETER, "PARAMETER"}, + {EIGRP_TLV_AUTH, "AUTH"}, + {EIGRP_TLV_SEQ, "SEQ"}, + {EIGRP_TLV_SW_VERSION, "SW_VERSION"}, + {EIGRP_TLV_NEXT_MCAST_SEQ, "NEXT_MCAST_SEQ"}, + {EIGRP_TLV_PEER_TERMINATION, "PEER_TERMINATION"}, + {EIGRP_TLV_PEER_MTRLIST, "PEER_MTRLIST"}, + {EIGRP_TLV_PEER_TIDLIST, "PEER_TIDLIST"}, + {0}}; + + +/* + * @fn eigrp_hello_timer + * + * @param[in] thread current execution thread timer is associated with + * + * @return void + * + * @par + * Called once per "hello" time interval, default 5 seconds + * Sends hello packet via multicast for all interfaces eigrp + * is configured for + */ +void eigrp_hello_timer(struct event *thread) +{ + struct eigrp_interface *ei; + + ei = EVENT_ARG(thread); + + if (IS_DEBUG_EIGRP(0, TIMERS)) + zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei), + ei->params.v_hello); + + /* Sending hello packet. */ + eigrp_hello_send(ei, EIGRP_HELLO_NORMAL, NULL); + + /* Hello timer set. */ + event_add_timer(master, eigrp_hello_timer, ei, ei->params.v_hello, + &ei->t_hello); +} + +/** + * @fn eigrp_hello_parameter_decode + * + * @param[in] nbr neighbor the ACK should be sent to + * @param[in] param pointer packet TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Encode Parameter TLV, used to convey metric weights and the hold time. + * + * @usage + * Note the addition of K6 for the new extended metrics, and does not apply to + * older TLV packet formats. + */ +static struct eigrp_neighbor * +eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct eigrp *eigrp = nbr->ei->eigrp; + struct TLV_Parameter_Type *param = (struct TLV_Parameter_Type *)tlv; + + /* First validate TLV length */ + if (tlv->length < sizeof(struct TLV_Parameter_Type)) + return NULL; + + /* copy over the values passed in by the neighbor */ + nbr->K1 = param->K1; + nbr->K2 = param->K2; + nbr->K3 = param->K3; + nbr->K4 = param->K4; + nbr->K5 = param->K5; + nbr->K6 = param->K6; + nbr->v_holddown = ntohs(param->hold_time); + + /* + * Check K1-K5 have the correct values to be able to become neighbors + * K6 does not have to match + */ + if ((eigrp->k_values[0] == nbr->K1) && (eigrp->k_values[1] == nbr->K2) + && (eigrp->k_values[2] == nbr->K3) + && (eigrp->k_values[3] == nbr->K4) + && (eigrp->k_values[4] == nbr->K5)) { + + if (eigrp_nbr_state_get(nbr) == EIGRP_NEIGHBOR_DOWN) { + zlog_info( + "Neighbor %pI4 (%s) is pending: new adjacency", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + + /* Expedited hello sent */ + eigrp_hello_send(nbr->ei, EIGRP_HELLO_NORMAL, NULL); + + // if(ntohl(nbr->ei->address->u.prefix4.s_addr) > + // ntohl(nbr->src.s_addr)) + eigrp_update_send_init(nbr); + + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING); + } + } else { + if (eigrp_nbr_state_get(nbr) != EIGRP_NEIGHBOR_DOWN) { + if ((param->K1 & param->K2 & param->K3 & param->K4 + & param->K5) + == 255) { + zlog_info( + "Neighbor %pI4 (%s) is down: Interface PEER-TERMINATION received", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_nbr_delete(nbr); + return NULL; + } else { + zlog_info( + "Neighbor %pI4 (%s) going down: Kvalue mismatch", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, + eigrp->vrf_id)); + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); + } + } + } + + return nbr; +} + +static uint8_t +eigrp_hello_authentication_decode(struct stream *s, + struct eigrp_tlv_hdr_type *tlv_header, + struct eigrp_neighbor *nbr) +{ + struct TLV_MD5_Authentication_Type *md5; + + md5 = (struct TLV_MD5_Authentication_Type *)tlv_header; + + if (md5->auth_type == EIGRP_AUTH_TYPE_MD5) { + /* Validate tlv length */ + if (md5->length < sizeof(struct TLV_MD5_Authentication_Type)) + return 0; + + return eigrp_check_md5_digest(s, md5, nbr, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } else if (md5->auth_type == EIGRP_AUTH_TYPE_SHA256) { + /* Validate tlv length */ + if (md5->length < sizeof(struct TLV_SHA256_Authentication_Type)) + return 0; + + return eigrp_check_sha256_digest( + s, (struct TLV_SHA256_Authentication_Type *)tlv_header, + nbr, EIGRP_AUTH_BASIC_HELLO_FLAG); + } + + return 0; +} + +/** + * @fn eigrp_sw_version_decode + * + * @param[in] nbr neighbor the ACK shoudl be sent to + * @param[in] param pointer to TLV software version information + * + * @return void + * + * @par + * Read the software version in the specified location. + * This consists of two bytes of OS version, and two bytes of EIGRP + * revision number. + */ +static void eigrp_sw_version_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct TLV_Software_Type *version = (struct TLV_Software_Type *)tlv; + + /* Validate TLV length */ + if (tlv->length < sizeof(struct TLV_Software_Type)) + return; + + nbr->os_rel_major = version->vender_major; + nbr->os_rel_minor = version->vender_minor; + nbr->tlv_rel_major = version->eigrp_major; + nbr->tlv_rel_minor = version->eigrp_minor; + return; +} + +/** + * @fn eigrp_peer_termination_decode + * + * @param[in] nbr neighbor the ACK shoudl be sent to + * @param[in] tlv pointer to TLV software version information + * + * @return void + * + * @par + * Read the address in the TLV and match to out address. If + * a match is found, move the sending neighbor to the down state. If + * out address is not in the TLV, then ignore the peer termination + */ +static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr, + struct eigrp_tlv_hdr_type *tlv) +{ + struct eigrp *eigrp = nbr->ei->eigrp; + struct TLV_Peer_Termination_type *param = + (struct TLV_Peer_Termination_type *)tlv; + + /* Validate TLV length */ + if (tlv->length < sizeof(struct TLV_Peer_Termination_type)) + return; + + uint32_t my_ip = nbr->ei->address.u.prefix4.s_addr; + uint32_t received_ip = param->neighbor_ip; + + if (my_ip == received_ip) { + zlog_info( + "Neighbor %pI4 (%s) is down: Peer Termination received", + &nbr->src, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); + /* set neighbor to DOWN */ + nbr->state = EIGRP_NEIGHBOR_DOWN; + /* delete neighbor */ + eigrp_nbr_delete(nbr); + } +} + +/** + * @fn eigrp_peer_termination_encode + * + * @param[in,out] s packet stream TLV is stored to + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Function used to encode Peer Termination TLV to Hello packet. + */ +static uint16_t eigrp_peer_termination_encode(struct stream *s, + struct in_addr *nbr_addr) +{ + uint16_t length = EIGRP_TLV_PEER_TERMINATION_LEN; + + /* fill in type and length */ + stream_putw(s, EIGRP_TLV_PEER_TERMINATION); + stream_putw(s, length); + + /* fill in unknown field 0x04 */ + stream_putc(s, 0x04); + + /* finally neighbor IP address */ + stream_put_ipv4(s, nbr_addr->s_addr); + + return (length); +} + +/* + * @fn eigrp_hello_receive + * + * @param[in] eigrp eigrp routing process + * @param[in] iph pointer to ip header + * @param[in] eigrph pointer to eigrp header + * @param[in] s input ip stream + * @param[in] ei eigrp interface packet arrived on + * @param[in] size size of eigrp packet + * + * @return void + * + * @par + * This is the main worker function for processing hello packets. It + * will validate the peer associated with the src ip address of the ip + * header, and then decode each of the general TLVs which the packet + * may contain. + * + * @usage + * Not all TLVs are current decoder. This is a work in progress.. + */ +void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph, + struct eigrp_header *eigrph, struct stream *s, + struct eigrp_interface *ei, int size) +{ + struct eigrp_tlv_hdr_type *tlv_header; + struct eigrp_neighbor *nbr; + uint16_t type; + uint16_t length; + + /* get neighbor struct */ + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + if (IS_DEBUG_EIGRP_PACKET(eigrph->opcode - 1, RECV)) + zlog_debug("Processing Hello size[%u] int(%s) nbr(%pI4)", size, + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id), + &nbr->src); + + size -= EIGRP_HEADER_LEN; + if (size < 0) + return; + + tlv_header = (struct eigrp_tlv_hdr_type *)eigrph->tlv; + + do { + type = ntohs(tlv_header->type); + length = ntohs(tlv_header->length); + + /* Validate length against packet size */ + if (length > size) + return; + + if ((length > 0) && (length <= size)) { + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug( + " General TLV(%s)", + lookup_msg(eigrp_general_tlv_type_str, + type, NULL)); + + // determine what General TLV is being processed + switch (type) { + case EIGRP_TLV_PARAMETER: + nbr = eigrp_hello_parameter_decode(nbr, + tlv_header); + if (!nbr) + return; + break; + case EIGRP_TLV_AUTH: { + if (eigrp_hello_authentication_decode( + s, tlv_header, nbr) + == 0) + return; + else + break; + break; + } + case EIGRP_TLV_SEQ: + break; + case EIGRP_TLV_SW_VERSION: + eigrp_sw_version_decode(nbr, tlv_header); + break; + case EIGRP_TLV_NEXT_MCAST_SEQ: + break; + case EIGRP_TLV_PEER_TERMINATION: + eigrp_peer_termination_decode(nbr, tlv_header); + return; + break; + case EIGRP_TLV_PEER_MTRLIST: + case EIGRP_TLV_PEER_TIDLIST: + break; + default: + break; + } + } + + tlv_header = (struct eigrp_tlv_hdr_type *)(((char *)tlv_header) + + length); + size -= length; + + } while (size > 0); + + + /*If received packet is hello with Parameter TLV*/ + if (ntohl(eigrph->ack) == 0) { + /* increment statistics. */ + ei->hello_in++; + if (nbr) + eigrp_nbr_state_update(nbr); + } + + if (IS_DEBUG_EIGRP_PACKET(0, RECV)) + zlog_debug("Hello Packet received from %pI4", &nbr->src); +} + +uint32_t FRR_MAJOR; +uint32_t FRR_MINOR; + +void eigrp_sw_version_initialize(void) +{ + char ver_string[] = VERSION; + char *dash = strstr(ver_string, "-"); + int ret; + + if (dash) + dash[0] = '\0'; + + ret = sscanf(ver_string, "%" SCNu32 ".%" SCNu32, &FRR_MAJOR, + &FRR_MINOR); + if (ret != 2) + flog_err(EC_EIGRP_PACKET, + "Did not Properly parse %s, please fix VERSION string", + VERSION); +} + +/** + * @fn eigrp_sw_version_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Store the software version in the specified location. + * This consists of two bytes of OS version, and two bytes of EIGRP + * revision number. + */ +static uint16_t eigrp_sw_version_encode(struct stream *s) +{ + uint16_t length = EIGRP_TLV_SW_VERSION_LEN; + + // setup the tlv fields + stream_putw(s, EIGRP_TLV_SW_VERSION); + stream_putw(s, length); + + stream_putc(s, FRR_MAJOR); //!< major os version + stream_putc(s, FRR_MINOR); //!< minor os version + + /* and the core eigrp version */ + stream_putc(s, EIGRP_MAJOR_VERSION); + stream_putc(s, EIGRP_MINOR_VERSION); + + return (length); +} + +/** + * @fn eigrp_tidlist_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return void + * + * @par + * If doing mutli-topology, then store the supported TID list. + * This is currently a place holder function + */ +static uint16_t eigrp_tidlist_encode(struct stream *s) +{ + // uint16_t length = EIGRP_TLV_SW_VERSION_LEN; + return 0; +} + +/** + * @fn eigrp_sequence_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Part of conditional receive process + * + */ +static uint16_t eigrp_sequence_encode(struct eigrp *eigrp, struct stream *s) +{ + uint16_t length = EIGRP_TLV_SEQ_BASE_LEN; + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + size_t backup_end, size_end; + int found; + + // add in the parameters TLV + backup_end = stream_get_endp(s); + stream_putw(s, EIGRP_TLV_SEQ); + size_end = s->endp; + stream_putw(s, 0x0000); + stream_putc(s, IPV4_MAX_BYTELEN); + + found = 0; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (nbr->multicast_queue->count > 0) { + length += (uint16_t)stream_put_ipv4( + s, nbr->src.s_addr); + found = 1; + } + } + } + + if (found == 0) { + stream_set_endp(s, backup_end); + return 0; + } + + backup_end = stream_get_endp(s); + stream_set_endp(s, size_end); + stream_putw(s, length); + stream_set_endp(s, backup_end); + + return length; +} + +/** + * @fn eigrp_sequence_encode + * + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Part of conditional receive process + * + */ +static uint16_t eigrp_next_sequence_encode(struct eigrp *eigrp, + struct stream *s) +{ + uint16_t length = EIGRP_NEXT_SEQUENCE_TLV_SIZE; + + // add in the parameters TLV + stream_putw(s, EIGRP_TLV_NEXT_MCAST_SEQ); + stream_putw(s, EIGRP_NEXT_SEQUENCE_TLV_SIZE); + stream_putl(s, eigrp->sequence_number + 1); + + return length; +} + +/** + * @fn eigrp_hello_parameter_encode + * + * @param[in] ei pointer to interface hello packet came in on + * @param[in,out] s packet stream TLV is stored to + * + * @return uint16_t number of bytes added to packet stream + * + * @par + * Encode Parameter TLV, used to convey metric weights and the hold time. + * + * @usage + * Note the addition of K6 for the new extended metrics, and does not apply to + * older TLV packet formats. + */ +static uint16_t eigrp_hello_parameter_encode(struct eigrp_interface *ei, + struct stream *s, uint8_t flags) +{ + // add in the parameters TLV + stream_putw(s, EIGRP_TLV_PARAMETER); + stream_putw(s, EIGRP_TLV_PARAMETER_LEN); + + // if graceful shutdown is needed to be announced, send all 255 in K + // values + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) { + stream_putc(s, 0xff); /* K1 */ + stream_putc(s, 0xff); /* K2 */ + stream_putc(s, 0xff); /* K3 */ + stream_putc(s, 0xff); /* K4 */ + stream_putc(s, 0xff); /* K5 */ + stream_putc(s, 0xff); /* K6 */ + } else // set k values + { + stream_putc(s, ei->eigrp->k_values[0]); /* K1 */ + stream_putc(s, ei->eigrp->k_values[1]); /* K2 */ + stream_putc(s, ei->eigrp->k_values[2]); /* K3 */ + stream_putc(s, ei->eigrp->k_values[3]); /* K4 */ + stream_putc(s, ei->eigrp->k_values[4]); /* K5 */ + stream_putc(s, ei->eigrp->k_values[5]); /* K6 */ + } + + // and set hold time value.. + stream_putw(s, ei->params.v_wait); + + return EIGRP_TLV_PARAMETER_LEN; +} + +/** + * @fn eigrp_hello_encode + * + * @param[in] ei pointer to interface hello packet came in on + * @param[in] s packet stream TLV is stored to + * @param[in] ack if non-zero, neigbors sequence packet to ack + * @param[in] flags type of hello packet + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return eigrp_packet pointer initialize hello packet + * + * @par + * Allocate an EIGRP hello packet, and add in the the approperate TLVs + * + */ +static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei, + in_addr_t addr, uint32_t ack, + uint8_t flags, + struct in_addr *nbr_addr) +{ + struct eigrp_packet *ep; + uint16_t length = EIGRP_HEADER_LEN; + + // allocate a new packet to be sent + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), NULL); + + if (ep) { + // encode common header feilds + eigrp_packet_header_init(EIGRP_OPC_HELLO, ei->eigrp, ep->s, 0, + 0, ack); + + // encode Authentication TLV + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_MD5_to_stream(ep->s, ei); + } else if ((ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) + && (ei->params.auth_keychain != NULL)) { + length += eigrp_add_authTLV_SHA256_to_stream(ep->s, ei); + } + + /* encode appropriate parameters to Hello packet */ + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) + length += eigrp_hello_parameter_encode( + ei, ep->s, EIGRP_HELLO_GRACEFUL_SHUTDOWN); + else + length += eigrp_hello_parameter_encode( + ei, ep->s, EIGRP_HELLO_NORMAL); + + // figure out the version of code we're running + length += eigrp_sw_version_encode(ep->s); + + if (flags & EIGRP_HELLO_ADD_SEQUENCE) { + length += eigrp_sequence_encode(ei->eigrp, ep->s); + length += eigrp_next_sequence_encode(ei->eigrp, ep->s); + } + + // add in the TID list if doing multi-topology + length += eigrp_tidlist_encode(ep->s); + + /* encode Peer Termination TLV if needed */ + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR) + length += + eigrp_peer_termination_encode(ep->s, nbr_addr); + + // Set packet length + ep->length = length; + + // set soruce address for the hello packet + ep->dst.s_addr = addr; + + if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_md5_digest(ei, ep->s, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } else if ((ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) + && (ei->params.auth_keychain != NULL)) { + eigrp_make_sha256_digest(ei, ep->s, + EIGRP_AUTH_BASIC_HELLO_FLAG); + } + + // EIGRP Checksum + eigrp_packet_checksum(ei, ep->s, length); + } + + return (ep); +} + +/** + * @fn eigrp_hello_send + * + * @param[in] nbr neighbor the ACK should be sent to + * + * @return void + * + * @par + * Send (unicast) a hello packet with the destination address + * associated with the neighbor. The eigrp header ACK feild will be + * updated to the neighbor's sequence number to acknolodge any + * outstanding packets + */ +void eigrp_hello_send_ack(struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + + /* if packet succesfully created, add it to the interface queue */ + ep = eigrp_hello_encode(nbr->ei, nbr->src.s_addr, + nbr->recv_sequence_number, EIGRP_HELLO_NORMAL, + NULL); + + if (ep) { + if (IS_DEBUG_EIGRP_PACKET(0, SEND)) + zlog_debug("Queueing [Hello] Ack Seq [%u] nbr [%pI4]", + nbr->recv_sequence_number, &nbr->src); + + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push(nbr->ei->obuf, ep); + + /* Hook thread to write packet. */ + if (nbr->ei->on_write_q == 0) { + listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); + nbr->ei->on_write_q = 1; + } + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + } +} + +/** + * @fn eigrp_hello_send + * + * @param[in] ei pointer to interface hello should be sent + * @param[in] flags type of hello packet + * @param[in] nbr_addr pointer to neighbor address for Peer + * Termination TLV + * + * @return void + * + * @par + * Build and enqueue a generic (multicast) periodic hello packet for + * sending. If no packets are currently queues, the packet will be + * sent immadiatly + */ +void eigrp_hello_send(struct eigrp_interface *ei, uint8_t flags, + struct in_addr *nbr_addr) +{ + struct eigrp_packet *ep = NULL; + + if (IS_DEBUG_EIGRP_PACKET(0, SEND)) + zlog_debug("Queueing [Hello] Interface(%s)", IF_NAME(ei)); + + /* if packet was succesfully created, then add it to the interface queue + */ + ep = eigrp_hello_encode(ei, htonl(EIGRP_MULTICAST_ADDRESS), 0, flags, + nbr_addr); + + if (ep) { + // Add packet to the top of the interface output queue + eigrp_fifo_push(ei->obuf, ep); + + /* Hook thread to write packet. */ + if (ei->on_write_q == 0) { + listnode_add(ei->eigrp->oi_write_q, ei); + ei->on_write_q = 1; + } + + if (ei->eigrp->t_write == NULL) { + if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) { + event_execute(master, eigrp_write, ei->eigrp, + ei->eigrp->fd, NULL); + } else { + event_add_write(master, eigrp_write, ei->eigrp, + ei->eigrp->fd, + &ei->eigrp->t_write); + } + } + } +} |