1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * EIGRP Sending and Receiving EIGRP Hello Packets.
4 * Copyright (C) 2013-2016
25 #include "sockunion.h"
33 #include "eigrpd/eigrp_structs.h"
34 #include "eigrpd/eigrpd.h"
35 #include "eigrpd/eigrp_interface.h"
36 #include "eigrpd/eigrp_neighbor.h"
37 #include "eigrpd/eigrp_packet.h"
38 #include "eigrpd/eigrp_zebra.h"
39 #include "eigrpd/eigrp_vty.h"
40 #include "eigrpd/eigrp_dump.h"
41 #include "eigrpd/eigrp_macros.h"
42 #include "eigrpd/eigrp_errors.h"
44 /* Packet Type String. */
45 static const struct message eigrp_general_tlv_type_str
[] = {
46 {EIGRP_TLV_PARAMETER
, "PARAMETER"},
47 {EIGRP_TLV_AUTH
, "AUTH"},
48 {EIGRP_TLV_SEQ
, "SEQ"},
49 {EIGRP_TLV_SW_VERSION
, "SW_VERSION"},
50 {EIGRP_TLV_NEXT_MCAST_SEQ
, "NEXT_MCAST_SEQ"},
51 {EIGRP_TLV_PEER_TERMINATION
, "PEER_TERMINATION"},
52 {EIGRP_TLV_PEER_MTRLIST
, "PEER_MTRLIST"},
53 {EIGRP_TLV_PEER_TIDLIST
, "PEER_TIDLIST"},
58 * @fn eigrp_hello_timer
60 * @param[in] thread current execution thread timer is associated with
65 * Called once per "hello" time interval, default 5 seconds
66 * Sends hello packet via multicast for all interfaces eigrp
69 void eigrp_hello_timer(struct event
*thread
)
71 struct eigrp_interface
*ei
;
73 ei
= EVENT_ARG(thread
);
75 if (IS_DEBUG_EIGRP(0, TIMERS
))
76 zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei
),
79 /* Sending hello packet. */
80 eigrp_hello_send(ei
, EIGRP_HELLO_NORMAL
, NULL
);
82 /* Hello timer set. */
83 event_add_timer(master
, eigrp_hello_timer
, ei
, ei
->params
.v_hello
,
88 * @fn eigrp_hello_parameter_decode
90 * @param[in] nbr neighbor the ACK should be sent to
91 * @param[in] param pointer packet TLV is stored to
93 * @return uint16_t number of bytes added to packet stream
96 * Encode Parameter TLV, used to convey metric weights and the hold time.
99 * Note the addition of K6 for the new extended metrics, and does not apply to
100 * older TLV packet formats.
102 static struct eigrp_neighbor
*
103 eigrp_hello_parameter_decode(struct eigrp_neighbor
*nbr
,
104 struct eigrp_tlv_hdr_type
*tlv
)
106 struct eigrp
*eigrp
= nbr
->ei
->eigrp
;
107 struct TLV_Parameter_Type
*param
= (struct TLV_Parameter_Type
*)tlv
;
109 /* First validate TLV length */
110 if (tlv
->length
< sizeof(struct TLV_Parameter_Type
))
113 /* copy over the values passed in by the neighbor */
120 nbr
->v_holddown
= ntohs(param
->hold_time
);
123 * Check K1-K5 have the correct values to be able to become neighbors
124 * K6 does not have to match
126 if ((eigrp
->k_values
[0] == nbr
->K1
) && (eigrp
->k_values
[1] == nbr
->K2
)
127 && (eigrp
->k_values
[2] == nbr
->K3
)
128 && (eigrp
->k_values
[3] == nbr
->K4
)
129 && (eigrp
->k_values
[4] == nbr
->K5
)) {
131 if (eigrp_nbr_state_get(nbr
) == EIGRP_NEIGHBOR_DOWN
) {
133 "Neighbor %pI4 (%s) is pending: new adjacency",
135 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
138 /* Expedited hello sent */
139 eigrp_hello_send(nbr
->ei
, EIGRP_HELLO_NORMAL
, NULL
);
141 // if(ntohl(nbr->ei->address->u.prefix4.s_addr) >
142 // ntohl(nbr->src.s_addr))
143 eigrp_update_send_init(nbr
);
145 eigrp_nbr_state_set(nbr
, EIGRP_NEIGHBOR_PENDING
);
148 if (eigrp_nbr_state_get(nbr
) != EIGRP_NEIGHBOR_DOWN
) {
149 if ((param
->K1
& param
->K2
& param
->K3
& param
->K4
153 "Neighbor %pI4 (%s) is down: Interface PEER-TERMINATION received",
155 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
157 eigrp_nbr_delete(nbr
);
161 "Neighbor %pI4 (%s) going down: Kvalue mismatch",
163 ifindex2ifname(nbr
->ei
->ifp
->ifindex
,
165 eigrp_nbr_state_set(nbr
, EIGRP_NEIGHBOR_DOWN
);
174 eigrp_hello_authentication_decode(struct stream
*s
,
175 struct eigrp_tlv_hdr_type
*tlv_header
,
176 struct eigrp_neighbor
*nbr
)
178 struct TLV_MD5_Authentication_Type
*md5
;
180 md5
= (struct TLV_MD5_Authentication_Type
*)tlv_header
;
182 if (md5
->auth_type
== EIGRP_AUTH_TYPE_MD5
) {
183 /* Validate tlv length */
184 if (md5
->length
< sizeof(struct TLV_MD5_Authentication_Type
))
187 return eigrp_check_md5_digest(s
, md5
, nbr
,
188 EIGRP_AUTH_BASIC_HELLO_FLAG
);
189 } else if (md5
->auth_type
== EIGRP_AUTH_TYPE_SHA256
) {
190 /* Validate tlv length */
191 if (md5
->length
< sizeof(struct TLV_SHA256_Authentication_Type
))
194 return eigrp_check_sha256_digest(
195 s
, (struct TLV_SHA256_Authentication_Type
*)tlv_header
,
196 nbr
, EIGRP_AUTH_BASIC_HELLO_FLAG
);
203 * @fn eigrp_sw_version_decode
205 * @param[in] nbr neighbor the ACK shoudl be sent to
206 * @param[in] param pointer to TLV software version information
211 * Read the software version in the specified location.
212 * This consists of two bytes of OS version, and two bytes of EIGRP
215 static void eigrp_sw_version_decode(struct eigrp_neighbor
*nbr
,
216 struct eigrp_tlv_hdr_type
*tlv
)
218 struct TLV_Software_Type
*version
= (struct TLV_Software_Type
*)tlv
;
220 /* Validate TLV length */
221 if (tlv
->length
< sizeof(struct TLV_Software_Type
))
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 eigrp
*eigrp
= nbr
->ei
->eigrp
;
248 struct TLV_Peer_Termination_type
*param
=
249 (struct TLV_Peer_Termination_type
*)tlv
;
251 /* Validate TLV length */
252 if (tlv
->length
< sizeof(struct TLV_Peer_Termination_type
))
255 uint32_t my_ip
= nbr
->ei
->address
.u
.prefix4
.s_addr
;
256 uint32_t received_ip
= param
->neighbor_ip
;
258 if (my_ip
== received_ip
) {
260 "Neighbor %pI4 (%s) is down: Peer Termination received",
262 ifindex2ifname(nbr
->ei
->ifp
->ifindex
, eigrp
->vrf_id
));
263 /* set neighbor to DOWN */
264 nbr
->state
= EIGRP_NEIGHBOR_DOWN
;
265 /* delete neighbor */
266 eigrp_nbr_delete(nbr
);
271 * @fn eigrp_peer_termination_encode
273 * @param[in,out] s packet stream TLV is stored to
274 * @param[in] nbr_addr pointer to neighbor address for Peer
277 * @return uint16_t number of bytes added to packet stream
280 * Function used to encode Peer Termination TLV to Hello packet.
282 static uint16_t eigrp_peer_termination_encode(struct stream
*s
,
283 struct in_addr
*nbr_addr
)
285 uint16_t length
= EIGRP_TLV_PEER_TERMINATION_LEN
;
287 /* fill in type and length */
288 stream_putw(s
, EIGRP_TLV_PEER_TERMINATION
);
289 stream_putw(s
, length
);
291 /* fill in unknown field 0x04 */
292 stream_putc(s
, 0x04);
294 /* finally neighbor IP address */
295 stream_put_ipv4(s
, nbr_addr
->s_addr
);
301 * @fn eigrp_hello_receive
303 * @param[in] eigrp eigrp routing process
304 * @param[in] iph pointer to ip header
305 * @param[in] eigrph pointer to eigrp header
306 * @param[in] s input ip stream
307 * @param[in] ei eigrp interface packet arrived on
308 * @param[in] size size of eigrp packet
313 * This is the main worker function for processing hello packets. It
314 * will validate the peer associated with the src ip address of the ip
315 * header, and then decode each of the general TLVs which the packet
319 * Not all TLVs are current decoder. This is a work in progress..
321 void eigrp_hello_receive(struct eigrp
*eigrp
, struct ip
*iph
,
322 struct eigrp_header
*eigrph
, struct stream
*s
,
323 struct eigrp_interface
*ei
, int size
)
325 struct eigrp_tlv_hdr_type
*tlv_header
;
326 struct eigrp_neighbor
*nbr
;
330 /* get neighbor struct */
331 nbr
= eigrp_nbr_get(ei
, eigrph
, iph
);
333 /* neighbor must be valid, eigrp_nbr_get creates if none existed */
336 if (IS_DEBUG_EIGRP_PACKET(eigrph
->opcode
- 1, RECV
))
337 zlog_debug("Processing Hello size[%u] int(%s) nbr(%pI4)", size
,
338 ifindex2ifname(nbr
->ei
->ifp
->ifindex
, eigrp
->vrf_id
),
341 size
-= EIGRP_HEADER_LEN
;
345 tlv_header
= (struct eigrp_tlv_hdr_type
*)eigrph
->tlv
;
348 type
= ntohs(tlv_header
->type
);
349 length
= ntohs(tlv_header
->length
);
351 /* Validate length against packet size */
355 if ((length
> 0) && (length
<= size
)) {
356 if (IS_DEBUG_EIGRP_PACKET(0, RECV
))
359 lookup_msg(eigrp_general_tlv_type_str
,
362 // determine what General TLV is being processed
364 case EIGRP_TLV_PARAMETER
:
365 nbr
= eigrp_hello_parameter_decode(nbr
,
370 case EIGRP_TLV_AUTH
: {
371 if (eigrp_hello_authentication_decode(
381 case EIGRP_TLV_SW_VERSION
:
382 eigrp_sw_version_decode(nbr
, tlv_header
);
384 case EIGRP_TLV_NEXT_MCAST_SEQ
:
386 case EIGRP_TLV_PEER_TERMINATION
:
387 eigrp_peer_termination_decode(nbr
, tlv_header
);
390 case EIGRP_TLV_PEER_MTRLIST
:
391 case EIGRP_TLV_PEER_TIDLIST
:
398 tlv_header
= (struct eigrp_tlv_hdr_type
*)(((char *)tlv_header
)
405 /*If received packet is hello with Parameter TLV*/
406 if (ntohl(eigrph
->ack
) == 0) {
407 /* increment statistics. */
410 eigrp_nbr_state_update(nbr
);
413 if (IS_DEBUG_EIGRP_PACKET(0, RECV
))
414 zlog_debug("Hello Packet received from %pI4", &nbr
->src
);
420 void eigrp_sw_version_initialize(void)
422 char ver_string
[] = VERSION
;
423 char *dash
= strstr(ver_string
, "-");
429 ret
= sscanf(ver_string
, "%" SCNu32
".%" SCNu32
, &FRR_MAJOR
,
432 flog_err(EC_EIGRP_PACKET
,
433 "Did not Properly parse %s, please fix VERSION string",
438 * @fn eigrp_sw_version_encode
440 * @param[in,out] s packet stream TLV is stored to
442 * @return uint16_t number of bytes added to packet stream
445 * Store the software version in the specified location.
446 * This consists of two bytes of OS version, and two bytes of EIGRP
449 static uint16_t eigrp_sw_version_encode(struct stream
*s
)
451 uint16_t length
= EIGRP_TLV_SW_VERSION_LEN
;
453 // setup the tlv fields
454 stream_putw(s
, EIGRP_TLV_SW_VERSION
);
455 stream_putw(s
, length
);
457 stream_putc(s
, FRR_MAJOR
); //!< major os version
458 stream_putc(s
, FRR_MINOR
); //!< minor os version
460 /* and the core eigrp version */
461 stream_putc(s
, EIGRP_MAJOR_VERSION
);
462 stream_putc(s
, EIGRP_MINOR_VERSION
);
468 * @fn eigrp_tidlist_encode
470 * @param[in,out] s packet stream TLV is stored to
475 * If doing mutli-topology, then store the supported TID list.
476 * This is currently a place holder function
478 static uint16_t eigrp_tidlist_encode(struct stream
*s
)
480 // uint16_t length = EIGRP_TLV_SW_VERSION_LEN;
485 * @fn eigrp_sequence_encode
487 * @param[in,out] s packet stream TLV is stored to
489 * @return uint16_t number of bytes added to packet stream
492 * Part of conditional receive process
495 static uint16_t eigrp_sequence_encode(struct eigrp
*eigrp
, struct stream
*s
)
497 uint16_t length
= EIGRP_TLV_SEQ_BASE_LEN
;
498 struct eigrp_interface
*ei
;
499 struct listnode
*node
, *node2
, *nnode2
;
500 struct eigrp_neighbor
*nbr
;
501 size_t backup_end
, size_end
;
504 // add in the parameters TLV
505 backup_end
= stream_get_endp(s
);
506 stream_putw(s
, EIGRP_TLV_SEQ
);
508 stream_putw(s
, 0x0000);
509 stream_putc(s
, IPV4_MAX_BYTELEN
);
512 for (ALL_LIST_ELEMENTS_RO(eigrp
->eiflist
, node
, ei
)) {
513 for (ALL_LIST_ELEMENTS(ei
->nbrs
, node2
, nnode2
, nbr
)) {
514 if (nbr
->multicast_queue
->count
> 0) {
515 length
+= (uint16_t)stream_put_ipv4(
523 stream_set_endp(s
, backup_end
);
527 backup_end
= stream_get_endp(s
);
528 stream_set_endp(s
, size_end
);
529 stream_putw(s
, length
);
530 stream_set_endp(s
, backup_end
);
536 * @fn eigrp_sequence_encode
538 * @param[in,out] s packet stream TLV is stored to
540 * @return uint16_t number of bytes added to packet stream
543 * Part of conditional receive process
546 static uint16_t eigrp_next_sequence_encode(struct eigrp
*eigrp
,
549 uint16_t length
= EIGRP_NEXT_SEQUENCE_TLV_SIZE
;
551 // add in the parameters TLV
552 stream_putw(s
, EIGRP_TLV_NEXT_MCAST_SEQ
);
553 stream_putw(s
, EIGRP_NEXT_SEQUENCE_TLV_SIZE
);
554 stream_putl(s
, eigrp
->sequence_number
+ 1);
560 * @fn eigrp_hello_parameter_encode
562 * @param[in] ei pointer to interface hello packet came in on
563 * @param[in,out] s packet stream TLV is stored to
565 * @return uint16_t number of bytes added to packet stream
568 * Encode Parameter TLV, used to convey metric weights and the hold time.
571 * Note the addition of K6 for the new extended metrics, and does not apply to
572 * older TLV packet formats.
574 static uint16_t eigrp_hello_parameter_encode(struct eigrp_interface
*ei
,
575 struct stream
*s
, uint8_t flags
)
577 // add in the parameters TLV
578 stream_putw(s
, EIGRP_TLV_PARAMETER
);
579 stream_putw(s
, EIGRP_TLV_PARAMETER_LEN
);
581 // if graceful shutdown is needed to be announced, send all 255 in K
583 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
) {
584 stream_putc(s
, 0xff); /* K1 */
585 stream_putc(s
, 0xff); /* K2 */
586 stream_putc(s
, 0xff); /* K3 */
587 stream_putc(s
, 0xff); /* K4 */
588 stream_putc(s
, 0xff); /* K5 */
589 stream_putc(s
, 0xff); /* K6 */
590 } else // set k values
592 stream_putc(s
, ei
->eigrp
->k_values
[0]); /* K1 */
593 stream_putc(s
, ei
->eigrp
->k_values
[1]); /* K2 */
594 stream_putc(s
, ei
->eigrp
->k_values
[2]); /* K3 */
595 stream_putc(s
, ei
->eigrp
->k_values
[3]); /* K4 */
596 stream_putc(s
, ei
->eigrp
->k_values
[4]); /* K5 */
597 stream_putc(s
, ei
->eigrp
->k_values
[5]); /* K6 */
600 // and set hold time value..
601 stream_putw(s
, ei
->params
.v_wait
);
603 return EIGRP_TLV_PARAMETER_LEN
;
607 * @fn eigrp_hello_encode
609 * @param[in] ei pointer to interface hello packet came in on
610 * @param[in] s packet stream TLV is stored to
611 * @param[in] ack if non-zero, neigbors sequence packet to ack
612 * @param[in] flags type of hello packet
613 * @param[in] nbr_addr pointer to neighbor address for Peer
616 * @return eigrp_packet pointer initialize hello packet
619 * Allocate an EIGRP hello packet, and add in the the approperate TLVs
622 static struct eigrp_packet
*eigrp_hello_encode(struct eigrp_interface
*ei
,
623 in_addr_t addr
, uint32_t ack
,
625 struct in_addr
*nbr_addr
)
627 struct eigrp_packet
*ep
;
628 uint16_t length
= EIGRP_HEADER_LEN
;
630 // allocate a new packet to be sent
631 ep
= eigrp_packet_new(EIGRP_PACKET_MTU(ei
->ifp
->mtu
), NULL
);
634 // encode common header feilds
635 eigrp_packet_header_init(EIGRP_OPC_HELLO
, ei
->eigrp
, ep
->s
, 0,
638 // encode Authentication TLV
639 if ((ei
->params
.auth_type
== EIGRP_AUTH_TYPE_MD5
)
640 && (ei
->params
.auth_keychain
!= NULL
)) {
641 length
+= eigrp_add_authTLV_MD5_to_stream(ep
->s
, ei
);
642 } else if ((ei
->params
.auth_type
== EIGRP_AUTH_TYPE_SHA256
)
643 && (ei
->params
.auth_keychain
!= NULL
)) {
644 length
+= eigrp_add_authTLV_SHA256_to_stream(ep
->s
, ei
);
647 /* encode appropriate parameters to Hello packet */
648 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
)
649 length
+= eigrp_hello_parameter_encode(
650 ei
, ep
->s
, EIGRP_HELLO_GRACEFUL_SHUTDOWN
);
652 length
+= eigrp_hello_parameter_encode(
653 ei
, ep
->s
, EIGRP_HELLO_NORMAL
);
655 // figure out the version of code we're running
656 length
+= eigrp_sw_version_encode(ep
->s
);
658 if (flags
& EIGRP_HELLO_ADD_SEQUENCE
) {
659 length
+= eigrp_sequence_encode(ei
->eigrp
, ep
->s
);
660 length
+= eigrp_next_sequence_encode(ei
->eigrp
, ep
->s
);
663 // add in the TID list if doing multi-topology
664 length
+= eigrp_tidlist_encode(ep
->s
);
666 /* encode Peer Termination TLV if needed */
667 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN_NBR
)
669 eigrp_peer_termination_encode(ep
->s
, nbr_addr
);
674 // set soruce address for the hello packet
675 ep
->dst
.s_addr
= addr
;
677 if ((ei
->params
.auth_type
== EIGRP_AUTH_TYPE_MD5
)
678 && (ei
->params
.auth_keychain
!= NULL
)) {
679 eigrp_make_md5_digest(ei
, ep
->s
,
680 EIGRP_AUTH_BASIC_HELLO_FLAG
);
681 } else if ((ei
->params
.auth_type
== EIGRP_AUTH_TYPE_SHA256
)
682 && (ei
->params
.auth_keychain
!= NULL
)) {
683 eigrp_make_sha256_digest(ei
, ep
->s
,
684 EIGRP_AUTH_BASIC_HELLO_FLAG
);
688 eigrp_packet_checksum(ei
, ep
->s
, length
);
695 * @fn eigrp_hello_send
697 * @param[in] nbr neighbor the ACK should be sent to
702 * Send (unicast) a hello packet with the destination address
703 * associated with the neighbor. The eigrp header ACK feild will be
704 * updated to the neighbor's sequence number to acknolodge any
705 * outstanding packets
707 void eigrp_hello_send_ack(struct eigrp_neighbor
*nbr
)
709 struct eigrp_packet
*ep
;
711 /* if packet succesfully created, add it to the interface queue */
712 ep
= eigrp_hello_encode(nbr
->ei
, nbr
->src
.s_addr
,
713 nbr
->recv_sequence_number
, EIGRP_HELLO_NORMAL
,
717 if (IS_DEBUG_EIGRP_PACKET(0, SEND
))
718 zlog_debug("Queueing [Hello] Ack Seq [%u] nbr [%pI4]",
719 nbr
->recv_sequence_number
, &nbr
->src
);
721 /* Add packet to the top of the interface output queue*/
722 eigrp_fifo_push(nbr
->ei
->obuf
, ep
);
724 /* Hook thread to write packet. */
725 if (nbr
->ei
->on_write_q
== 0) {
726 listnode_add(nbr
->ei
->eigrp
->oi_write_q
, nbr
->ei
);
727 nbr
->ei
->on_write_q
= 1;
729 event_add_write(master
, eigrp_write
, nbr
->ei
->eigrp
,
730 nbr
->ei
->eigrp
->fd
, &nbr
->ei
->eigrp
->t_write
);
735 * @fn eigrp_hello_send
737 * @param[in] ei pointer to interface hello should be sent
738 * @param[in] flags type of hello packet
739 * @param[in] nbr_addr pointer to neighbor address for Peer
745 * Build and enqueue a generic (multicast) periodic hello packet for
746 * sending. If no packets are currently queues, the packet will be
749 void eigrp_hello_send(struct eigrp_interface
*ei
, uint8_t flags
,
750 struct in_addr
*nbr_addr
)
752 struct eigrp_packet
*ep
= NULL
;
754 if (IS_DEBUG_EIGRP_PACKET(0, SEND
))
755 zlog_debug("Queueing [Hello] Interface(%s)", IF_NAME(ei
));
757 /* if packet was succesfully created, then add it to the interface queue
759 ep
= eigrp_hello_encode(ei
, htonl(EIGRP_MULTICAST_ADDRESS
), 0, flags
,
763 // Add packet to the top of the interface output queue
764 eigrp_fifo_push(ei
->obuf
, ep
);
766 /* Hook thread to write packet. */
767 if (ei
->on_write_q
== 0) {
768 listnode_add(ei
->eigrp
->oi_write_q
, ei
);
772 if (ei
->eigrp
->t_write
== NULL
) {
773 if (flags
& EIGRP_HELLO_GRACEFUL_SHUTDOWN
) {
774 event_execute(master
, eigrp_write
, ei
->eigrp
,
777 event_add_write(master
, eigrp_write
, ei
->eigrp
,
779 &ei
->eigrp
->t_write
);