2 * EIGRP Sending and Receiving EIGRP Hello Packets.
3 * Copyright (C) 2013-2016
15 * This file is part of GNU Zebra.
17 * GNU Zebra is free software; you can redistribute it and/or modify it
18 * under the terms of the GNU General Public License as published by the
19 * Free Software Foundation; either version 2, or (at your option) any
22 * GNU Zebra is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; see the file COPYING; if not, write to the Free Software
29 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 #include "sockunion.h"
48 #include "eigrpd/eigrp_structs.h"
49 #include "eigrpd/eigrpd.h"
50 #include "eigrpd/eigrp_interface.h"
51 #include "eigrpd/eigrp_neighbor.h"
52 #include "eigrpd/eigrp_packet.h"
53 #include "eigrpd/eigrp_zebra.h"
54 #include "eigrpd/eigrp_vty.h"
55 #include "eigrpd/eigrp_dump.h"
56 #include "eigrpd/eigrp_macros.h"
58 /* Packet Type String. */
59 static const struct message eigrp_general_tlv_type_str
[] = {
60 {EIGRP_TLV_PARAMETER
, "PARAMETER"},
61 {EIGRP_TLV_AUTH
, "AUTH"},
62 {EIGRP_TLV_SEQ
, "SEQ"},
63 {EIGRP_TLV_SW_VERSION
, "SW_VERSION"},
64 {EIGRP_TLV_NEXT_MCAST_SEQ
, "NEXT_MCAST_SEQ"},
65 {EIGRP_TLV_PEER_TERMINATION
, "PEER_TERMINATION"},
66 {EIGRP_TLV_PEER_MTRLIST
, "PEER_MTRLIST"},
67 {EIGRP_TLV_PEER_TIDLIST
, "PEER_TIDLIST"},
72 * @fn eigrp_hello_timer
74 * @param[in] thread current execution thread timer is associated with
76 * @return int always returns 0
79 * Called once per "hello" time interval, default 5 seconds
80 * Sends hello packet via multicast for all interfaces eigrp
83 int eigrp_hello_timer(struct thread
*thread
)
85 struct eigrp_interface
*ei
;
87 ei
= THREAD_ARG(thread
);
90 if (IS_DEBUG_EIGRP(0, TIMERS
))
91 zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei
),
92 EIGRP_IF_PARAM(ei
, v_hello
));
94 /* Sending hello packet. */
95 eigrp_hello_send(ei
, EIGRP_HELLO_NORMAL
, NULL
);
97 /* Hello timer set. */
99 thread_add_timer(master
, eigrp_hello_timer
, ei
,
100 EIGRP_IF_PARAM(ei
, v_hello
), &ei
->t_hello
);
106 * @fn eigrp_hello_parameter_decode
108 * @param[in] nbr neighbor the ACK should be sent to
109 * @param[in] param pointer packet TLV is stored to
111 * @return u_int16_t number of bytes added to packet stream
114 * Encode Parameter TLV, used to convey metric weights and the hold time.
117 * Note the addition of K6 for the new extended metrics, and does not apply to
118 * older TLV packet formats.
120 static struct eigrp_neighbor
*
121 eigrp_hello_parameter_decode(struct eigrp_neighbor
*nbr
,
122 struct eigrp_tlv_hdr_type
*tlv
)
124 struct eigrp
*eigrp
= nbr
->ei
->eigrp
;
125 struct TLV_Parameter_Type
*param
= (struct TLV_Parameter_Type
*)tlv
;
127 /* copy over the values passed in by the neighbor */
134 nbr
->v_holddown
= ntohs(param
->hold_time
);
137 * Check K1-K5 have the correct values to be able to become neighbors
138 * K6 does not have to match
140 if ((eigrp
->k_values
[0] == nbr
->K1
) && (eigrp
->k_values
[1] == nbr
->K2
)
141 && (eigrp
->k_values
[2] == nbr
->K3
)
142 && (eigrp
->k_values
[3] == nbr
->K4
)
143 && (eigrp
->k_values
[4] == nbr
->K5
)) {
145 if (eigrp_nbr_state_get(nbr
) == EIGRP_NEIGHBOR_DOWN
) {
146 zlog_info("Neighbor %s (%s) is pending: new adjacency",
148 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
151 /* Expedited hello sent */
152 eigrp_hello_send(nbr
->ei
, EIGRP_HELLO_NORMAL
, NULL
);
154 // if(ntohl(nbr->ei->address->u.prefix4.s_addr) >
155 // ntohl(nbr->src.s_addr))
156 eigrp_update_send_init(nbr
);
158 eigrp_nbr_state_set(nbr
, EIGRP_NEIGHBOR_PENDING
);
161 if (eigrp_nbr_state_get(nbr
) != EIGRP_NEIGHBOR_DOWN
) {
162 if ((param
->K1
& param
->K2
& param
->K3
& param
->K4
166 "Neighbor %s (%s) is down: Interface PEER-TERMINATION received",
168 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
170 eigrp_nbr_delete(nbr
);
174 "Neighbor %s (%s) going down: Kvalue mismatch",
176 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
178 eigrp_nbr_state_set(nbr
, EIGRP_NEIGHBOR_DOWN
);
187 eigrp_hello_authentication_decode(struct stream
*s
,
188 struct eigrp_tlv_hdr_type
*tlv_header
,
189 struct eigrp_neighbor
*nbr
)
191 struct TLV_MD5_Authentication_Type
*md5
;
193 md5
= (struct TLV_MD5_Authentication_Type
*)tlv_header
;
195 if (md5
->auth_type
== EIGRP_AUTH_TYPE_MD5
)
196 return eigrp_check_md5_digest(s
, md5
, nbr
,
197 EIGRP_AUTH_BASIC_HELLO_FLAG
);
198 else if (md5
->auth_type
== EIGRP_AUTH_TYPE_SHA256
)
199 return eigrp_check_sha256_digest(
200 s
, (struct TLV_SHA256_Authentication_Type
*)tlv_header
,
201 nbr
, EIGRP_AUTH_BASIC_HELLO_FLAG
);
207 * @fn eigrp_sw_version_decode
209 * @param[in] nbr neighbor the ACK shoudl be sent to
210 * @param[in] param pointer to TLV software version information
215 * Read the software version in the specified location.
216 * This consists of two bytes of OS version, and two bytes of EIGRP
219 static void eigrp_sw_version_decode(struct eigrp_neighbor
*nbr
,
220 struct eigrp_tlv_hdr_type
*tlv
)
222 struct TLV_Software_Type
*version
= (struct TLV_Software_Type
*)tlv
;
224 nbr
->os_rel_major
= version
->vender_major
;
225 nbr
->os_rel_minor
= version
->vender_minor
;
226 nbr
->tlv_rel_major
= version
->eigrp_major
;
227 nbr
->tlv_rel_minor
= version
->eigrp_minor
;
232 * @fn eigrp_peer_termination_decode
234 * @param[in] nbr neighbor the ACK shoudl be sent to
235 * @param[in] tlv pointer to TLV software version information
240 * Read the address in the TLV and match to out address. If
241 * a match is found, move the sending neighbor to the down state. If
242 * out address is not in the TLV, then ignore the peer termination
244 static void eigrp_peer_termination_decode(struct eigrp_neighbor
*nbr
,
245 struct eigrp_tlv_hdr_type
*tlv
)
247 struct TLV_Peer_Termination_type
*param
=
248 (struct TLV_Peer_Termination_type
*)tlv
;
250 uint32_t my_ip
= nbr
->ei
->address
->u
.prefix4
.s_addr
;
251 uint32_t received_ip
= param
->neighbor_ip
;
253 if (my_ip
== received_ip
) {
254 zlog_info("Neighbor %s (%s) is down: Peer Termination received",
256 ifindex2ifname(nbr
->ei
->ifp
->ifindex
, VRF_DEFAULT
));
257 /* set neighbor to DOWN */
258 nbr
->state
= EIGRP_NEIGHBOR_DOWN
;
259 /* delete neighbor */
260 eigrp_nbr_delete(nbr
);
265 * @fn eigrp_peer_termination_encode
267 * @param[in,out] s packet stream TLV is stored to
268 * @param[in] nbr_addr pointer to neighbor address for Peer
271 * @return u_int16_t number of bytes added to packet stream
274 * Function used to encode Peer Termination TLV to Hello packet.
276 static u_int16_t
eigrp_peer_termination_encode(struct stream
*s
,
277 struct in_addr
*nbr_addr
)
279 u_int16_t length
= EIGRP_TLV_PEER_TERMINATION_LEN
;
281 /* fill in type and length */
282 stream_putw(s
, EIGRP_TLV_PEER_TERMINATION
);
283 stream_putw(s
, length
);
285 /* fill in unknown field 0x04 */
286 stream_putc(s
, 0x04);
288 /* finally neighbor IP address */
289 stream_put_ipv4(s
, nbr_addr
->s_addr
);
295 * @fn eigrp_hello_receive
297 * @param[in] eigrp eigrp routing process
298 * @param[in] iph pointer to ip header
299 * @param[in] eigrph pointer to eigrp header
300 * @param[in] s input ip stream
301 * @param[in] ei eigrp interface packet arrived on
302 * @param[in] size size of eigrp packet
307 * This is the main worker function for processing hello packets. It
308 * will validate the peer associated with the src ip address of the ip
309 * header, and then decode each of the general TLVs which the packet
313 * Not all TLVs are current decoder. This is a work in progress..
315 void eigrp_hello_receive(struct eigrp
*eigrp
, struct ip
*iph
,
316 struct eigrp_header
*eigrph
, struct stream
*s
,
317 struct eigrp_interface
*ei
, int size
)
319 struct eigrp_tlv_hdr_type
*tlv_header
;
320 struct eigrp_neighbor
*nbr
;
324 /* get neighbor struct */
325 nbr
= eigrp_nbr_get(ei
, eigrph
, iph
);
327 /* neighbor must be valid, eigrp_nbr_get creates if none existed */
330 if (IS_DEBUG_EIGRP_PACKET(eigrph
->opcode
- 1, RECV
))
331 zlog_debug("Processing Hello size[%u] int(%s) nbr(%s)", size
,
332 ifindex2ifname(nbr
->ei
->ifp
->ifindex
, VRF_DEFAULT
),
333 inet_ntoa(nbr
->src
));
335 size
-= EIGRP_HEADER_LEN
;
339 tlv_header
= (struct eigrp_tlv_hdr_type
*)eigrph
->tlv
;
342 type
= ntohs(tlv_header
->type
);
343 length
= ntohs(tlv_header
->length
);
345 if ((length
> 0) && (length
<= size
)) {
346 if (IS_DEBUG_EIGRP_PACKET(0, RECV
))
349 lookup_msg(eigrp_general_tlv_type_str
,
352 // determine what General TLV is being processed
354 case EIGRP_TLV_PARAMETER
:
355 nbr
= eigrp_hello_parameter_decode(nbr
,
360 case EIGRP_TLV_AUTH
: {
361 if (eigrp_hello_authentication_decode(
371 case EIGRP_TLV_SW_VERSION
:
372 eigrp_sw_version_decode(nbr
, tlv_header
);
374 case EIGRP_TLV_NEXT_MCAST_SEQ
:
376 case EIGRP_TLV_PEER_TERMINATION
:
377 eigrp_peer_termination_decode(nbr
, tlv_header
);
380 case EIGRP_TLV_PEER_MTRLIST
:
381 case EIGRP_TLV_PEER_TIDLIST
:
388 tlv_header
= (struct eigrp_tlv_hdr_type
*)(((char *)tlv_header
)
395 /*If received packet is hello with Parameter TLV*/
396 if (ntohl(eigrph
->ack
) == 0) {
397 /* increment statistics. */
400 eigrp_nbr_state_update(nbr
);
403 if (IS_DEBUG_EIGRP_PACKET(0, RECV
))
404 zlog_debug("Hello Packet received from %s",
405 inet_ntoa(nbr
->src
));
409 * @fn eigrp_sw_version_encode
411 * @param[in,out] s packet stream TLV is stored to
413 * @return u_int16_t number of bytes added to packet stream
416 * Store the software version in the specified location.
417 * This consists of two bytes of OS version, and two bytes of EIGRP
420 static u_int16_t
eigrp_sw_version_encode(struct stream
*s
)
422 u_int16_t length
= EIGRP_TLV_SW_VERSION_LEN
;
424 // setup the tlv fields
425 stream_putw(s
, EIGRP_TLV_SW_VERSION
);
426 stream_putw(s
, length
);
428 // encode the version of quagga we're running
429 // DVS: need to figure out a cleaner way to do this
430 stream_putc(s
, 0); //!< major os version
431 stream_putc(s
, 99); //!< minor os version
433 /* and the core eigrp version */
434 stream_putc(s
, EIGRP_MAJOR_VERSION
);
435 stream_putc(s
, EIGRP_MINOR_VERSION
);
441 * @fn eigrp_tidlist_encode
443 * @param[in,out] s packet stream TLV is stored to
448 * If doing mutli-topology, then store the supported TID list.
449 * This is currently a place holder function
451 static u_int16_t
eigrp_tidlist_encode(struct stream
*s
)
453 // u_int16_t length = EIGRP_TLV_SW_VERSION_LEN;
458 * @fn eigrp_sequence_encode
460 * @param[in,out] s packet stream TLV is stored to
462 * @return u_int16_t number of bytes added to packet stream
465 * Part of conditional receive process
468 static u_int16_t
eigrp_sequence_encode(struct stream
*s
)
470 u_int16_t length
= EIGRP_TLV_SEQ_BASE_LEN
;
472 struct eigrp_interface
*ei
;
473 struct listnode
*node
, *node2
, *nnode2
;
474 struct eigrp_neighbor
*nbr
;
475 size_t backup_end
, size_end
;
478 eigrp
= eigrp_lookup();
483 // add in the parameters TLV
484 backup_end
= stream_get_endp(s
);
485 stream_putw(s
, EIGRP_TLV_SEQ
);
487 stream_putw(s
, 0x0000);
488 stream_putc(s
, IPV4_MAX_BYTELEN
);
491 for (ALL_LIST_ELEMENTS_RO(eigrp
->eiflist
, node
, ei
)) {
492 for (ALL_LIST_ELEMENTS(ei
->nbrs
, node2
, nnode2
, nbr
)) {
493 if (nbr
->multicast_queue
->count
> 0) {
494 length
+= (u_int16_t
)stream_put_ipv4(
502 stream_set_endp(s
, backup_end
);
506 backup_end
= stream_get_endp(s
);
507 stream_set_endp(s
, size_end
);
508 stream_putw(s
, length
);
509 stream_set_endp(s
, backup_end
);
515 * @fn eigrp_sequence_encode
517 * @param[in,out] s packet stream TLV is stored to
519 * @return u_int16_t number of bytes added to packet stream
522 * Part of conditional receive process
525 static u_int16_t
eigrp_next_sequence_encode(struct stream
*s
)
527 u_int16_t length
= EIGRP_NEXT_SEQUENCE_TLV_SIZE
;
530 eigrp
= eigrp_lookup();
535 // add in the parameters TLV
536 stream_putw(s
, EIGRP_TLV_NEXT_MCAST_SEQ
);
537 stream_putw(s
, EIGRP_NEXT_SEQUENCE_TLV_SIZE
);
538 stream_putl(s
, eigrp
->sequence_number
+ 1);
544 * @fn eigrp_hello_parameter_encode
546 * @param[in] ei pointer to interface hello packet came in on
547 * @param[in,out] s packet stream TLV is stored to
549 * @return u_int16_t number of bytes added to packet stream
552 * Encode Parameter TLV, used to convey metric weights and the hold time.
555 * Note the addition of K6 for the new extended metrics, and does not apply to
556 * older TLV packet formats.
558 static u_int16_t
eigrp_hello_parameter_encode(struct eigrp_interface
*ei
,
559 struct stream
*s
, u_char flags
)
561 u_int16_t length
= EIGRP_TLV_PARAMETER_LEN
;
563 // add in the parameters TLV
564 stream_putw(s
, EIGRP_TLV_PARAMETER
);
565 stream_putw(s
, EIGRP_TLV_PARAMETER_LEN
);
567 // if graceful shutdown is needed to be announced, send all 255 in K
569 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
) {
570 stream_putc(s
, 0xff); /* K1 */
571 stream_putc(s
, 0xff); /* K2 */
572 stream_putc(s
, 0xff); /* K3 */
573 stream_putc(s
, 0xff); /* K4 */
574 stream_putc(s
, 0xff); /* K5 */
575 stream_putc(s
, 0xff); /* K6 */
576 } else // set k values
578 stream_putc(s
, ei
->eigrp
->k_values
[0]); /* K1 */
579 stream_putc(s
, ei
->eigrp
->k_values
[1]); /* K2 */
580 stream_putc(s
, ei
->eigrp
->k_values
[2]); /* K3 */
581 stream_putc(s
, ei
->eigrp
->k_values
[3]); /* K4 */
582 stream_putc(s
, ei
->eigrp
->k_values
[4]); /* K5 */
583 stream_putc(s
, ei
->eigrp
->k_values
[5]); /* K6 */
586 // and set hold time value..
587 stream_putw(s
, IF_DEF_PARAMS(ei
->ifp
)->v_wait
);
593 * @fn eigrp_hello_encode
595 * @param[in] ei pointer to interface hello packet came in on
596 * @param[in] s packet stream TLV is stored to
597 * @param[in] ack if non-zero, neigbors sequence packet to ack
598 * @param[in] flags type of hello packet
599 * @param[in] nbr_addr pointer to neighbor address for Peer
602 * @return eigrp_packet pointer initialize hello packet
605 * Allocate an EIGRP hello packet, and add in the the approperate TLVs
608 static struct eigrp_packet
*eigrp_hello_encode(struct eigrp_interface
*ei
,
609 in_addr_t addr
, u_int32_t ack
,
611 struct in_addr
*nbr_addr
)
613 struct eigrp_packet
*ep
;
614 u_int16_t length
= EIGRP_HEADER_LEN
;
616 // allocate a new packet to be sent
617 ep
= eigrp_packet_new(ei
->ifp
->mtu
, NULL
);
620 // encode common header feilds
621 eigrp_packet_header_init(EIGRP_OPC_HELLO
, ei
, ep
->s
, 0, 0, ack
);
623 // encode Authentication TLV
624 if ((IF_DEF_PARAMS(ei
->ifp
)->auth_type
== EIGRP_AUTH_TYPE_MD5
)
625 && (IF_DEF_PARAMS(ei
->ifp
)->auth_keychain
!= NULL
)) {
626 length
+= eigrp_add_authTLV_MD5_to_stream(ep
->s
, ei
);
627 } else if ((IF_DEF_PARAMS(ei
->ifp
)->auth_type
628 == EIGRP_AUTH_TYPE_SHA256
)
629 && (IF_DEF_PARAMS(ei
->ifp
)->auth_keychain
!= NULL
)) {
630 length
+= eigrp_add_authTLV_SHA256_to_stream(ep
->s
, ei
);
633 /* encode appropriate parameters to Hello packet */
634 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
)
635 length
+= eigrp_hello_parameter_encode(
636 ei
, ep
->s
, EIGRP_HELLO_GRACEFUL_SHUTDOWN
);
638 length
+= eigrp_hello_parameter_encode(
639 ei
, ep
->s
, EIGRP_HELLO_NORMAL
);
641 // figure out the version of code we're running
642 length
+= eigrp_sw_version_encode(ep
->s
);
644 if (flags
& EIGRP_HELLO_ADD_SEQUENCE
) {
645 length
+= eigrp_sequence_encode(ep
->s
);
646 length
+= eigrp_next_sequence_encode(ep
->s
);
649 // add in the TID list if doing multi-topology
650 length
+= eigrp_tidlist_encode(ep
->s
);
652 /* encode Peer Termination TLV if needed */
653 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR
)
655 eigrp_peer_termination_encode(ep
->s
, nbr_addr
);
660 // set soruce address for the hello packet
661 ep
->dst
.s_addr
= addr
;
663 if ((IF_DEF_PARAMS(ei
->ifp
)->auth_type
== EIGRP_AUTH_TYPE_MD5
)
664 && (IF_DEF_PARAMS(ei
->ifp
)->auth_keychain
!= NULL
)) {
665 eigrp_make_md5_digest(ei
, ep
->s
,
666 EIGRP_AUTH_BASIC_HELLO_FLAG
);
667 } else if ((IF_DEF_PARAMS(ei
->ifp
)->auth_type
668 == EIGRP_AUTH_TYPE_SHA256
)
669 && (IF_DEF_PARAMS(ei
->ifp
)->auth_keychain
!= NULL
)) {
670 eigrp_make_sha256_digest(ei
, ep
->s
,
671 EIGRP_AUTH_BASIC_HELLO_FLAG
);
675 eigrp_packet_checksum(ei
, ep
->s
, length
);
682 * @fn eigrp_hello_send
684 * @param[in] nbr neighbor the ACK should be sent to
689 * Send (unicast) a hello packet with the destination address
690 * associated with the neighbor. The eigrp header ACK feild will be
691 * updated to the neighbor's sequence number to acknolodge any
692 * outstanding packets
694 void eigrp_hello_send_ack(struct eigrp_neighbor
*nbr
)
696 struct eigrp_packet
*ep
;
698 /* if packet succesfully created, add it to the interface queue */
699 ep
= eigrp_hello_encode(nbr
->ei
, nbr
->src
.s_addr
,
700 nbr
->recv_sequence_number
, EIGRP_HELLO_NORMAL
,
704 if (IS_DEBUG_EIGRP_PACKET(0, SEND
))
705 zlog_debug("Queueing [Hello] Ack Seq [%u] nbr [%s]",
706 nbr
->recv_sequence_number
,
707 inet_ntoa(nbr
->src
));
709 /* Add packet to the top of the interface output queue*/
710 eigrp_fifo_push(nbr
->ei
->obuf
, ep
);
712 /* Hook thread to write packet. */
713 if (nbr
->ei
->on_write_q
== 0) {
714 listnode_add(nbr
->ei
->eigrp
->oi_write_q
, nbr
->ei
);
715 nbr
->ei
->on_write_q
= 1;
717 thread_add_write(master
, eigrp_write
, nbr
->ei
->eigrp
,
718 nbr
->ei
->eigrp
->fd
, &nbr
->ei
->eigrp
->t_write
);
723 * @fn eigrp_hello_send
725 * @param[in] ei pointer to interface hello should be sent
726 * @param[in] flags type of hello packet
727 * @param[in] nbr_addr pointer to neighbor address for Peer
733 * Build and enqueue a generic (multicast) periodic hello packet for
734 * sending. If no packets are currently queues, the packet will be
737 void eigrp_hello_send(struct eigrp_interface
*ei
, u_char flags
,
738 struct in_addr
*nbr_addr
)
740 struct eigrp_packet
*ep
= NULL
;
742 /* If this is passive interface, do not send EIGRP Hello.
743 if ((EIGRP_IF_PASSIVE_STATUS (ei) == EIGRP_IF_PASSIVE) ||
744 (ei->type != EIGRP_IFTYPE_NBMA))
748 if (IS_DEBUG_EIGRP_PACKET(0, SEND
))
749 zlog_debug("Queueing [Hello] Interface(%s)", IF_NAME(ei
));
751 /* if packet was succesfully created, then add it to the interface queue
753 ep
= eigrp_hello_encode(ei
, htonl(EIGRP_MULTICAST_ADDRESS
), 0, flags
,
757 // Add packet to the top of the interface output queue
758 eigrp_fifo_push(ei
->obuf
, ep
);
760 /* Hook thread to write packet. */
761 if (ei
->on_write_q
== 0) {
762 listnode_add(ei
->eigrp
->oi_write_q
, ei
);
766 if (ei
->eigrp
->t_write
== NULL
) {
767 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
) {
768 thread_execute(master
, eigrp_write
, ei
->eigrp
,
771 thread_add_write(master
, eigrp_write
, ei
->eigrp
,
773 &ei
->eigrp
->t_write
);