diff options
Diffstat (limited to 'eigrpd/eigrp_packet.c')
| -rw-r--r-- | eigrpd/eigrp_packet.c | 1441 |
1 files changed, 1441 insertions, 0 deletions
diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c new file mode 100644 index 0000000000..9a0a9897f0 --- /dev/null +++ b/eigrpd/eigrp_packet.c @@ -0,0 +1,1441 @@ +/* + * EIGRP General Sending and Receiving of EIGRP Packets. + * Copyright (C) 2013-2014 + * Authors: + * Donnie Savage + * Jan Janovic + * Matej Perina + * Peter Orsag + * Peter Paluch + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "thread.h" +#include "memory.h" +#include "linklist.h" +#include "vty.h" +#include "keychain.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 "sha256.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_network.h" +#include "eigrpd/eigrp_topology.h" +#include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_memory.h" + +/* Packet Type String. */ +const struct message eigrp_packet_type_str[] = +{ + { EIGRP_OPC_UPDATE, "Update" }, + { EIGRP_OPC_REQUEST, "Request" }, + { EIGRP_OPC_QUERY, "Query" }, + { EIGRP_OPC_REPLY, "Reply" }, + { EIGRP_OPC_HELLO, "Hello" }, + { EIGRP_OPC_IPXSAP, "IPX-SAP" }, + { EIGRP_OPC_PROBE, "Probe" }, + { EIGRP_OPC_ACK, "Ack" }, + { EIGRP_OPC_SIAQUERY, "SIAQuery" }, + { EIGRP_OPC_SIAREPLY, "SIAReply" }, +}; +const size_t eigrp_packet_type_str_max = sizeof(eigrp_packet_type_str) / + sizeof(eigrp_packet_type_str[0]); + +static unsigned char zeropad[16] = {0}; + +/* Forward function reference*/ +static struct stream * eigrp_recv_packet (int, struct interface **, struct stream *); +static int eigrp_verify_header (struct stream *, struct eigrp_interface *, struct ip *, + struct eigrp_header *); +static int eigrp_check_network_mask (struct eigrp_interface *, struct in_addr); + + +static int eigrp_retrans_count_exceeded(struct eigrp_packet *ep, struct eigrp_neighbor *nbr) +{ + return 1; +} + +int +eigrp_make_md5_digest (struct eigrp_interface *ei, struct stream *s, u_char flags) +{ + struct key *key = NULL; + struct keychain *keychain; + + unsigned char digest[EIGRP_AUTH_TYPE_MD5_LEN]; + MD5_CTX ctx; + u_char *ibuf; + size_t backup_get, backup_end; + struct TLV_MD5_Authentication_Type *auth_TLV; + + ibuf = s->data; + backup_end = s->endp; + backup_get = s->getp; + + auth_TLV = eigrp_authTLV_MD5_new(); + + stream_set_getp(s,EIGRP_HEADER_LEN); + stream_get(auth_TLV,s,EIGRP_AUTH_MD5_TLV_SIZE); + stream_set_getp(s, backup_get); + + keychain = keychain_lookup(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + if(keychain) + key = key_lookup_for_send(keychain); + else + return EIGRP_AUTH_TYPE_NONE; + + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + + /* Generate a digest. Each situation needs different handling */ + if(flags & EIGRP_AUTH_BASIC_HELLO_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if(strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + } + else if(flags & EIGRP_AUTH_UPDATE_INIT_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_UPDATE_INIT_COMPUTE); + } + else if(flags & EIGRP_AUTH_UPDATE_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if(strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + if(backup_end > (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)) + { + MD5Update(&ctx, ibuf + (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE), + backup_end - 20 - (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)); + } + } + + MD5Final(digest, &ctx); + + + /* Append md5 digest to the end of the stream. */ + memcpy(auth_TLV->digest,digest,EIGRP_AUTH_TYPE_MD5_LEN); + + stream_set_endp(s,EIGRP_HEADER_LEN); + stream_put(s,auth_TLV,EIGRP_AUTH_MD5_TLV_SIZE); + stream_set_endp(s, backup_end); + + eigrp_authTLV_MD5_free(auth_TLV); + return EIGRP_AUTH_TYPE_MD5_LEN; +} + +int +eigrp_check_md5_digest (struct stream *s, struct TLV_MD5_Authentication_Type *authTLV,struct eigrp_neighbor *nbr, u_char flags) +{ + MD5_CTX ctx; + unsigned char digest[EIGRP_AUTH_TYPE_MD5_LEN]; + struct key *key = NULL; + struct keychain *keychain; + u_char *ibuf; + size_t backup_end; + struct TLV_MD5_Authentication_Type *auth_TLV; + struct eigrp_header *eigrph; + + + if (nbr && ntohl(nbr->crypt_seqnum) > ntohl(authTLV->key_sequence)) + { + zlog_warn ("interface %s: eigrp_check_md5 bad sequence %d (expect %d)", + IF_NAME (nbr->ei), + ntohl(authTLV->key_sequence), + ntohl(nbr->crypt_seqnum)); + return 0; + } + + eigrph = (struct eigrp_header *) s->data; + eigrph->checksum = 0; + + auth_TLV =(struct TLV_MD5_Authentication_Type *) (s->data + EIGRP_HEADER_LEN); + memcpy(auth_TLV->digest, "0", sizeof(auth_TLV->digest)); + + ibuf = s->data; + backup_end = s->endp; + + keychain = keychain_lookup(IF_DEF_PARAMS (nbr->ei->ifp)->auth_keychain); + if(keychain) + key = key_lookup_for_send(keychain); + + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + + /* Generate a digest. Each situation needs different handling */ + if(flags & EIGRP_AUTH_BASIC_HELLO_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if(strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + } + else if(flags & EIGRP_AUTH_UPDATE_INIT_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_UPDATE_INIT_COMPUTE); + } + else if(flags & EIGRP_AUTH_UPDATE_FLAG) + { + MD5Update(&ctx, ibuf, EIGRP_MD5_BASIC_COMPUTE); + MD5Update(&ctx, key->string, strlen(key->string)); + if(strlen(key->string) < 16) + MD5Update(&ctx, zeropad, 16 - strlen(key->string)); + if(backup_end > (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)) + { + MD5Update(&ctx, ibuf + (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE), + backup_end - 20 - (EIGRP_HEADER_LEN + EIGRP_AUTH_MD5_TLV_SIZE)); + } + } + + MD5Final(digest, &ctx); + + /* compare the two */ + if (memcmp (authTLV->digest, digest, EIGRP_AUTH_TYPE_MD5_LEN) == 0) + { + zlog_debug("VSETKO OK"); + } + else + { + zlog_warn ("interface %s: eigrp_check_md5 checksum mismatch", + IF_NAME (nbr->ei)); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (nbr) + nbr->crypt_seqnum = authTLV->key_sequence; + + return 1; +} + +int +eigrp_make_sha256_digest (struct eigrp_interface *ei, struct stream *s, u_char flags) +{ + struct key *key = NULL; + struct keychain *keychain; + char *source_ip; + + unsigned char digest[EIGRP_AUTH_TYPE_SHA256_LEN]; + unsigned char buffer[1 + PLAINTEXT_LENGTH + 45 + 1] = { 0 }; + HMAC_SHA256_CTX ctx; + void *ibuf; + size_t backup_get, backup_end; + struct TLV_SHA256_Authentication_Type *auth_TLV; + + ibuf = s->data; + backup_end = s->endp; + backup_get = s->getp; + + auth_TLV = eigrp_authTLV_SHA256_new (); + + stream_set_getp(s,EIGRP_HEADER_LEN); + stream_get(auth_TLV,s,EIGRP_AUTH_SHA256_TLV_SIZE); + stream_set_getp(s, backup_get); + + keychain = keychain_lookup(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + if(keychain) + key = key_lookup_for_send(keychain); + +// saved_len[index] = strnzcpyn(saved_key[index], key, +// PLAINTEXT_LENGTH + 1); + + source_ip = calloc(16, sizeof(char)); + inet_ntop(AF_INET, &ei->address->u.prefix4, source_ip, 16); + + memset(&ctx, 0, sizeof(ctx)); + buffer[0] = '\n'; + memcpy(buffer + 1, key, strlen (key->string)); + memcpy(buffer + 1 + strlen(key->string), source_ip, strlen(source_ip)); + HMAC__SHA256_Init(&ctx, buffer, 1 + strlen (key->string) + strlen(source_ip)); + HMAC__SHA256_Update(&ctx, ibuf, strlen(ibuf)); + HMAC__SHA256_Final(digest, &ctx); + + + /* Put hmac-sha256 digest to it's place */ + memcpy(auth_TLV->digest,digest,EIGRP_AUTH_TYPE_SHA256_LEN); + + stream_set_endp(s,EIGRP_HEADER_LEN); + stream_put(s,auth_TLV,EIGRP_AUTH_SHA256_TLV_SIZE); + stream_set_endp(s, backup_end); + + eigrp_authTLV_SHA256_free(auth_TLV); + free(source_ip); + + return EIGRP_AUTH_TYPE_SHA256_LEN; +} + +int +eigrp_check_sha256_digest (struct stream *s, struct TLV_SHA256_Authentication_Type *authTLV,struct eigrp_neighbor *nbr, u_char flags) +{ + + return 1; +} +/* + * eigrp_packet_dump + * + * This routing dumps the contents of the IP packet either received or + * built by EIGRP. + */ +static void +eigrp_packet_dump (struct stream *s) +{ + // not yet... + return; +} + +/* + * Converts a 24-bit integer represented as an unsigned char[3] *value + * in network byte order into uint32_t in host byte order + */ +//static uint32_t u24_32 (const unsigned char *value) +//{ +// return (value[0] << 16) + (value[1] << 8) + value[2]; +//} +// +///* +// * Converts an uint32_t value in host byte order into a 24-bit integer +// * in network byte order represented by unsigned char[3] *result +// */ +//static unsigned char * u32_24 (uint32_t value, unsigned char *result) +//{ +// value = htonl(value & 0x00FFFFFF); +// memcpy (result, (unsigned char *) &value + 1, 3); +// +// return result; +//} + +int +eigrp_write (struct thread *thread) +{ + struct eigrp *eigrp = THREAD_ARG(thread); + struct eigrp_header *eigrph; + struct eigrp_interface *ei; + struct eigrp_packet *ep; + struct sockaddr_in sa_dst; + struct ip iph; + struct msghdr msg; + struct iovec iov[2]; + u_int16_t opcode = 0; + + int ret; + int flags = 0; + struct listnode *node; +#ifdef WANT_EIGRP_WRITE_FRAGMENT + static u_int16_t ipid = 0; +#endif /* WANT_EIGRP_WRITE_FRAGMENT */ +#define EIGRP_WRITE_IPHL_SHIFT 2 + + eigrp->t_write = NULL; + + node = listhead(eigrp->oi_write_q); + assert(node); + ei = listgetdata(node); + assert(ei); + +#ifdef WANT_EIGRP_WRITE_FRAGMENT + /* seed ipid static with low order bits of time */ + if (ipid == 0) + ipid = (time(NULL) & 0xffff); +#endif /* WANT_EIGRP_WRITE_FRAGMENT */ + + /* Get one packet from queue. */ + ep = eigrp_fifo_head(ei->obuf); + assert(ep); + assert(ep->length >= EIGRP_HEADER_LEN); + + if (ep->dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS)) + eigrp_if_ipmulticast(eigrp, ei->address, ei->ifp->ifindex); + + memset(&iph, 0, sizeof(struct ip)); + memset(&sa_dst, 0, sizeof(sa_dst)); + + sa_dst.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_dst.sin_len = sizeof(sa_dst); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + sa_dst.sin_addr = ep->dst; + sa_dst.sin_port = htons(0); + + /* Set DONTROUTE flag if dst is unicast. */ + if (!IN_MULTICAST(htonl(ep->dst.s_addr))) + flags = MSG_DONTROUTE; + + iph.ip_hl = sizeof(struct ip) >> EIGRP_WRITE_IPHL_SHIFT; + /* it'd be very strange for header to not be 4byte-word aligned but.. */ + if (sizeof(struct ip) > (unsigned int)(iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT)) + iph.ip_hl++; /* we presume sizeof struct ip cant overflow ip_hl.. */ + + iph.ip_v = IPVERSION; + iph.ip_tos = IPTOS_PREC_INTERNETCONTROL; + iph.ip_len = (iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT) + ep->length; + +#if defined (__DragonFly__) + /* + * DragonFly's raw socket expects ip_len/ip_off in network byte order. + */ + iph.ip_len = htons(iph.ip_len); +#endif + + iph.ip_off = 0; + iph.ip_ttl = EIGRP_IP_TTL; + iph.ip_p = IPPROTO_EIGRPIGP; + iph.ip_sum = 0; + iph.ip_src.s_addr = ei->address->u.prefix4.s_addr; + iph.ip_dst.s_addr = ep->dst.s_addr; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (caddr_t) &sa_dst; + msg.msg_namelen = sizeof(sa_dst); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + iov[0].iov_base = (char*)&iph; + iov[0].iov_len = iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT; + iov[1].iov_base = STREAM_PNT(ep->s); + iov[1].iov_len = ep->length; + + /* send final fragment (could be first) */ + sockopt_iphdrincl_swab_htosys(&iph); + ret = sendmsg(eigrp->fd, &msg, flags); + sockopt_iphdrincl_swab_systoh(&iph); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, SEND)) + { + eigrph = (struct eigrp_header *) STREAM_DATA(ep->s); + opcode = eigrph->opcode; + zlog_debug("Sending [%s] to [%s] via [%s] ret [%d].", + LOOKUP(eigrp_packet_type_str, opcode), inet_ntoa(ep->dst), + IF_NAME(ei), ret); + } + + if (ret < 0) + zlog_warn("*** sendmsg in eigrp_write failed to %s, " + "id %d, off %d, len %d, interface %s, mtu %u: %s", + inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, ei->ifp->name, + ei->ifp->mtu, safe_strerror(errno)); + + /* Show debug sending packet. */ + if (IS_DEBUG_EIGRP_TRANSMIT(0, SEND) && (IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL))) + { + zlog_debug("-----------------------------------------------------"); + eigrp_ip_header_dump(&iph); + stream_set_getp(ep->s, 0); + eigrp_packet_dump(ep->s); + zlog_debug("-----------------------------------------------------"); + } + + /* Now delete packet from queue. */ + eigrp_packet_delete(ei); + + if (eigrp_fifo_head(ei->obuf) == NULL) + { + ei->on_write_q = 0; + list_delete_node(eigrp->oi_write_q, node); + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(eigrp->oi_write_q)) + eigrp->t_write = thread_add_write(master, eigrp_write, eigrp, eigrp->fd); + + return 0; +} + +/* Starting point of packet process function. */ +int +eigrp_read (struct thread *thread) +{ + int ret; + struct stream *ibuf; + struct eigrp *eigrp; + struct eigrp_interface *ei; + struct ip *iph; + struct eigrp_header *eigrph; + struct interface *ifp; + struct eigrp_neighbor *nbr; + + u_int16_t opcode = 0; + u_int16_t length = 0; + + /* first of all get interface pointer. */ + eigrp = THREAD_ARG(thread); + + /* prepare for next packet. */ + eigrp->t_read = thread_add_read(master, eigrp_read, eigrp, eigrp->fd); + + stream_reset(eigrp->ibuf); + if (!(ibuf = eigrp_recv_packet(eigrp->fd, &ifp, eigrp->ibuf))) + { + /* This raw packet is known to be at least as big as its IP header. */ + return -1; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *)STREAM_DATA(ibuf); + + //Substract IPv4 header size from EIGRP Packet itself + if(iph->ip_v == 4) + length = (iph->ip_len) - 20U; + + + /* IP Header dump. */ + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) && IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) + eigrp_ip_header_dump(iph); + + /* Note that sockopt_iphdrincl_swab_systoh was called in eigrp_recv_packet. */ + if (ifp == NULL) + { + /* Handle cases where the platform does not support retrieving the ifindex, + and also platforms (such as Solaris 8) that claim to support ifindex + retrieval but do not. */ + ifp = if_lookup_address((void *)&iph->ip_src, VRF_DEFAULT); + + if (ifp == NULL) + return 0; + } + + /* associate packet with eigrp interface */ + ei = eigrp_if_lookup_recv_if(eigrp, iph->ip_src, ifp); + + /* eigrp_verify_header() relies on a valid "ei" and thus can be called only + after the checks below are passed. These checks in turn access the + fields of unverified "eigrph" structure for their own purposes and + must remain very accurate in doing this. + */ + if (!ei) + return 0; + + /* Self-originated packet should be discarded silently. */ + if (eigrp_if_lookup_by_local_addr(eigrp, NULL, iph->ip_src) || + (IPV4_ADDR_SAME(&iph->ip_src.s_addr, &ei->address->u.prefix4))) + { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug("eigrp_read[%s]: Dropping self-originated packet", + inet_ntoa(iph->ip_src)); + return 0; + } + + /* Advance from IP header to EIGRP header (iph->ip_hl has been verified + by eigrp_recv_packet() to be correct). */ + + stream_forward_getp(ibuf, (iph->ip_hl * 4)); + eigrph = (struct eigrp_header *) STREAM_PNT(ibuf); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) && IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) + eigrp_header_dump(eigrph); + +// if (MSG_OK != eigrp_packet_examin(eigrph, stream_get_endp(ibuf) - stream_get_getp(ibuf))) +// return -1; + + /* Now it is safe to access all fields of EIGRP packet header. */ + /* associate packet with eigrp interface */ + ei = eigrp_if_lookup_recv_if(eigrp, iph->ip_src, ifp); + + /* eigrp_verify_header() relies on a valid "ei" and thus can be called only + after the checks below are passed. These checks in turn access the + fields of unverified "eigrph" structure for their own purposes and + must remain very accurate in doing this. + */ + if (!ei) + return 0; + + /* If incoming interface is passive one, ignore it. */ + if (ei && EIGRP_IF_PASSIVE_STATUS(ei) == EIGRP_IF_PASSIVE) + { + char buf[3][INET_ADDRSTRLEN]; + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug("ignoring packet from router %s sent to %s, " + "received on a passive interface, %s", + inet_ntop(AF_INET, &eigrph->vrid, buf[0], sizeof(buf[0])), + inet_ntop(AF_INET, &iph->ip_dst, buf[1], sizeof(buf[1])), + inet_ntop(AF_INET, &ei->address->u.prefix4, + buf[2], sizeof(buf[2]))); + + if (iph->ip_dst.s_addr == htonl(EIGRP_MULTICAST_ADDRESS)) + { + /* Try to fix multicast membership. + * Some OS:es may have problems in this area, + * make sure it is removed. + */ + EI_MEMBER_JOINED(ei, MEMBER_ALLROUTERS); + eigrp_if_set_multicast(ei); + } + return 0; + } + + /* else it must be a local eigrp interface, check it was received on + * correct link + */ + else if (ei->ifp != ifp) + { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_warn("Packet from [%s] received on wrong link %s", + inet_ntoa(iph->ip_src), ifp->name); + return 0; + } + + /* Verify more EIGRP header fields. */ + ret = eigrp_verify_header(ibuf, ei, iph, eigrph); + if (ret < 0) + { + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug("eigrp_read[%s]: Header check failed, dropping.", + inet_ntoa(iph->ip_src)); + return ret; + } + + /* calcualte the eigrp packet length, and move the pounter to the + start of the eigrp TLVs */ + opcode = eigrph->opcode; + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug("Received [%s] length [%u] via [%s] src [%s] dst [%s]", + LOOKUP(eigrp_packet_type_str, opcode), length, + IF_NAME(ei), inet_ntoa(iph->ip_src), inet_ntoa(iph->ip_dst)); + + /* Read rest of the packet and call each sort of packet routine. */ + stream_forward_getp(ibuf, EIGRP_HEADER_LEN); + + + /* New testing block of code for handling Acks */ + if (ntohl(eigrph->ack) != 0) + { + nbr = eigrp_nbr_get(ei, eigrph, iph); + + /* neighbor must be valid, eigrp_nbr_get creates if none existed */ + assert(nbr); + + struct eigrp_packet *ep; + + ep = eigrp_fifo_tail(nbr->retrans_queue); + if (ep != NULL) + { + if (ntohl(eigrph->ack) == ep->sequence_number) + { + if((nbr->state == EIGRP_NEIGHBOR_PENDING) && (ntohl(eigrph->ack) == nbr->init_sequence_number)) + { + eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_UP); + zlog_info("Neighbor adjacency became full"); + nbr->init_sequence_number = 0; + nbr->recv_sequence_number = ntohl(eigrph->sequence); + eigrp_update_send_EOT(nbr); + } + ep = eigrp_fifo_pop_tail(nbr->retrans_queue); + /*eigrp_packet_free(ep);*/ + if (nbr->retrans_queue->count > 0) + { + eigrp_send_packet_reliably(nbr); + } + } + } + ep = eigrp_fifo_tail(nbr->multicast_queue); + if (ep != NULL) + { + if (ntohl(eigrph->ack) == ep->sequence_number) + { + ep = eigrp_fifo_pop_tail(nbr->multicast_queue); + eigrp_packet_free(ep); + if (nbr->multicast_queue->count > 0) + { + eigrp_send_packet_reliably(nbr); + } + } + } + } + + + switch (opcode) + { + case EIGRP_OPC_HELLO: + eigrp_hello_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_PROBE: + // eigrp_probe_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_QUERY: + eigrp_query_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_REPLY: + eigrp_reply_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_REQUEST: + // eigrp_request_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_SIAQUERY: + eigrp_query_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_SIAREPLY: + eigrp_reply_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + case EIGRP_OPC_UPDATE: + eigrp_update_receive(eigrp, iph, eigrph, ibuf, ei, length); + break; + default: + zlog(NULL, LOG_WARNING, + "interface %s: EIGRP packet header type %d unsupported", + IF_NAME(ei), opcode); + break; + } + + return 0; +} + +static struct stream * +eigrp_recv_packet (int fd, struct interface **ifp, struct stream *ibuf) +{ + int ret; + struct ip *iph; + u_int16_t ip_len; + unsigned int ifindex = 0; + struct iovec iov; + /* Header and data both require alignment. */ + char buff[CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())]; + struct msghdr msgh; + + memset(&msgh, 0, sizeof(struct msghdr)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = (caddr_t) buff; + msgh.msg_controllen = sizeof(buff); + + ret = stream_recvmsg(ibuf, fd, &msgh, 0, (EIGRP_PACKET_MAX_LEN + 1)); + if (ret < 0) + { + zlog_warn("stream_recvmsg failed: %s", safe_strerror(errno)); + return NULL; + } + if ((unsigned int) ret < sizeof(iph)) /* ret must be > 0 now */ + { + zlog_warn("eigrp_recv_packet: discarding runt packet of length %d " + "(ip header size is %u)", ret, (u_int) sizeof(iph)); + return NULL; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *) STREAM_DATA(ibuf); + sockopt_iphdrincl_swab_systoh(iph); + + ip_len = iph->ip_len; + +#if !defined (GNU_LINUX) && (OpenBSD < 200311) && (__FreeBSD_version < 1000000) + /* + * Kernel network code touches incoming IP header parameters, + * before protocol specific processing. + * + * 1) Convert byteorder to host representation. + * --> ip_len, ip_id, ip_off + * + * 2) Adjust ip_len to strip IP header size! + * --> If user process receives entire IP packet via RAW + * socket, it must consider adding IP header size to + * the "ip_len" field of "ip" structure. + * + * For more details, see <netinet/ip_input.c>. + */ + ip_len = ip_len + (iph->ip_hl << 2); +#endif + +#if defined (__DragonFly__) + /* + * in DragonFly's raw socket, ip_len/ip_off are read + * in network byte order. + * As OpenBSD < 200311 adjust ip_len to strip IP header size! + */ + ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2); +#endif + + ifindex = getsockopt_ifindex(AF_INET, &msgh); + + *ifp = if_lookup_by_index(ifindex); + + if (ret != ip_len) + { + zlog_warn("eigrp_recv_packet read length mismatch: ip_len is %d, " + "but recvmsg returned %d", ip_len, ret); + return NULL; + } + + return ibuf; +} + +struct eigrp_fifo * +eigrp_fifo_new (void) +{ + struct eigrp_fifo *new; + + new = XCALLOC(MTYPE_EIGRP_FIFO, sizeof(struct eigrp_fifo)); + return new; +} + +/* Free eigrp packet fifo. */ +void +eigrp_fifo_free (struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + struct eigrp_packet *next; + + for (ep = fifo->head; ep; ep = next) + { + next = ep->next; + eigrp_packet_free(ep); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; + + XFREE(MTYPE_EIGRP_FIFO, fifo); +} + +/* Free eigrp fifo entries without destroying fifo itself*/ +void +eigrp_fifo_reset (struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + struct eigrp_packet *next; + + for (ep = fifo->head; ep; ep = next) + { + next = ep->next; + eigrp_packet_free(ep); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +struct eigrp_packet * +eigrp_packet_new (size_t size) +{ + struct eigrp_packet *new; + + new = XCALLOC(MTYPE_EIGRP_PACKET, sizeof(struct eigrp_packet)); + new->s = stream_new(size); + new->retrans_counter = 0; + + return new; +} + +void +eigrp_send_packet_reliably (struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *ep; + + ep = eigrp_fifo_tail(nbr->retrans_queue); + + if (ep) + { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push_head(nbr->ei->obuf, duplicate); + + /*Start retransmission timer*/ + THREAD_TIMER_ON(master, ep->t_retrans_timer, eigrp_unack_packet_retrans, + nbr, EIGRP_PACKET_RETRANS_TIME); + + /*Increment sequence number counter*/ + nbr->ei->eigrp->sequence_number++; + + /* 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; + } + if (nbr->ei->eigrp->t_write == NULL) + nbr->ei->eigrp->t_write = + thread_add_write(master, eigrp_write, nbr->ei->eigrp, nbr->ei->eigrp->fd); + } +} + +/* Calculate EIGRP checksum */ +void +eigrp_packet_checksum (struct eigrp_interface *ei, struct stream *s, + u_int16_t length) +{ + struct eigrp_header *eigrph; + + eigrph = (struct eigrp_header *) STREAM_DATA(s); + + /* Calculate checksum. */ + eigrph->checksum = in_cksum(eigrph, length); +} + +/* Make EIGRP header. */ +void +eigrp_packet_header_init (int type, struct eigrp_interface *ei, struct stream *s, + u_int32_t flags, u_int32_t sequence, u_int32_t ack) +{ + struct eigrp_header *eigrph; + + eigrph = (struct eigrp_header *) STREAM_DATA(s); + + eigrph->version = (u_char) EIGRP_HEADER_VERSION; + eigrph->opcode = (u_char) type; + eigrph->checksum = 0; + + eigrph->vrid = htons(ei->eigrp->vrid); + eigrph->ASNumber = htons(ei->eigrp->AS); + eigrph->ack = htonl(ack); + eigrph->sequence = htonl(sequence); +// if(flags == EIGRP_INIT_FLAG) +// eigrph->sequence = htonl(3); + eigrph->flags = htonl(flags); + + if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) + zlog_debug("Packet Header Init Seq [%u] Ack [%u]", + htonl(eigrph->sequence), htonl(eigrph->ack)); + + stream_forward_endp(s, EIGRP_HEADER_LEN); +} + +/* Add new packet to head of fifo. */ +void +eigrp_fifo_push_head (struct eigrp_fifo *fifo, struct eigrp_packet *ep) +{ + ep->next = fifo->head; + ep->previous = NULL; + + if (fifo->tail == NULL) + fifo->tail = ep; + + if (fifo->count != 0) + fifo->head->previous = ep; + + fifo->head = ep; + + fifo->count++; +} + +/* Return first fifo entry. */ +struct eigrp_packet * +eigrp_fifo_head (struct eigrp_fifo *fifo) +{ + return fifo->head; +} + +/* Return last fifo entry. */ +struct eigrp_packet * +eigrp_fifo_tail (struct eigrp_fifo *fifo) +{ + return fifo->tail; +} + +void +eigrp_packet_delete (struct eigrp_interface *ei) +{ + struct eigrp_packet *ep; + + ep = eigrp_fifo_pop (ei->obuf); + + if (ep) + eigrp_packet_free(ep); +} + +/* Delete first packet from fifo. */ +struct eigrp_packet * +eigrp_fifo_pop (struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + + ep = fifo->head; + + if (ep) + { + fifo->head = ep->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + else + fifo->head->previous = NULL; + + fifo->count--; + } + + return ep; +} + +void +eigrp_packet_free (struct eigrp_packet *ep) +{ + if (ep->s) + stream_free(ep->s); + + THREAD_OFF(ep->t_retrans_timer); + + XFREE(MTYPE_EIGRP_PACKET, ep); + + ep = NULL; +} + +/* EIGRP Header verification. */ +static int +eigrp_verify_header (struct stream *ibuf, struct eigrp_interface *ei, + struct ip *iph, struct eigrp_header *eigrph) +{ + + /* Check network mask, Silently discarded. */ + if (!eigrp_check_network_mask(ei, iph->ip_src)) + { + zlog_warn("interface %s: eigrp_read network address is not same [%s]", + IF_NAME(ei), inet_ntoa(iph->ip_src)); + return -1; + } +// +// /* Check authentication. The function handles logging actions, where required. */ +// if (! eigrp_check_auth(ei, eigrph)) +// return -1; + + return 0; +} + +/* Unbound socket will accept any Raw IP packets if proto is matched. + To prevent it, compare src IP address and i/f address with masking + i/f network mask. */ +static int +eigrp_check_network_mask (struct eigrp_interface *ei, struct in_addr ip_src) +{ + struct in_addr mask, me, him; + + if (ei->type == EIGRP_IFTYPE_POINTOPOINT) + return 1; + + masklen2ip(ei->address->prefixlen, &mask); + + me.s_addr = ei->address->u.prefix4.s_addr & mask.s_addr; + him.s_addr = ip_src.s_addr & mask.s_addr; + + if (IPV4_ADDR_SAME(&me, &him)) + return 1; + + return 0; +} + +int +eigrp_unack_packet_retrans (struct thread *thread) +{ + struct eigrp_neighbor *nbr; + nbr = (struct eigrp_neighbor *) THREAD_ARG(thread); + + struct eigrp_packet *ep; + ep = eigrp_fifo_tail(nbr->retrans_queue); + + if (ep) + { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push_head(nbr->ei->obuf, duplicate); + + ep->retrans_counter++; + if(ep->retrans_counter == EIGRP_PACKET_RETRANS_MAX) + return eigrp_retrans_count_exceeded(ep, nbr); + + /*Start retransmission timer*/ + ep->t_retrans_timer = + thread_add_timer(master, eigrp_unack_packet_retrans, nbr,EIGRP_PACKET_RETRANS_TIME); + + /* 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; + } + if (nbr->ei->eigrp->t_write == NULL) + nbr->ei->eigrp->t_write = + thread_add_write(master, eigrp_write, nbr->ei->eigrp, nbr->ei->eigrp->fd); + } + + return 0; +} + +int +eigrp_unack_multicast_packet_retrans (struct thread *thread) +{ + struct eigrp_neighbor *nbr; + nbr = (struct eigrp_neighbor *) THREAD_ARG(thread); + + struct eigrp_packet *ep; + ep = eigrp_fifo_tail(nbr->multicast_queue); + + if (ep) + { + struct eigrp_packet *duplicate; + duplicate = eigrp_packet_duplicate(ep, nbr); + /* Add packet to the top of the interface output queue*/ + eigrp_fifo_push_head(nbr->ei->obuf, duplicate); + + ep->retrans_counter++; + if(ep->retrans_counter == EIGRP_PACKET_RETRANS_MAX) + return eigrp_retrans_count_exceeded(ep, nbr); + + /*Start retransmission timer*/ + ep->t_retrans_timer = + thread_add_timer(master, eigrp_unack_multicast_packet_retrans, nbr,EIGRP_PACKET_RETRANS_TIME); + + /* 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; + } + if (nbr->ei->eigrp->t_write == NULL) + nbr->ei->eigrp->t_write = + thread_add_write(master, eigrp_write, nbr->ei->eigrp, nbr->ei->eigrp->fd); + } + + return 0; +} + +/* Get packet from tail of fifo. */ +struct eigrp_packet * +eigrp_fifo_pop_tail (struct eigrp_fifo *fifo) +{ + struct eigrp_packet *ep; + + ep = fifo->tail; + + if (ep) + { + fifo->tail = ep->previous; + + if (fifo->tail == NULL) + fifo->head = NULL; + else + fifo->tail->next = NULL; + + fifo->count--; + } + + return ep; +} + +struct eigrp_packet * +eigrp_packet_duplicate (struct eigrp_packet *old, struct eigrp_neighbor *nbr) +{ + struct eigrp_packet *new; + + new = eigrp_packet_new(nbr->ei->ifp->mtu); + new->length = old->length; + new->retrans_counter = old->retrans_counter; + new->dst = old->dst; + new->sequence_number = old->sequence_number; + stream_copy(new->s, old->s); + + return new; +} + +struct TLV_IPv4_Internal_type * +eigrp_read_ipv4_tlv (struct stream *s) +{ + struct TLV_IPv4_Internal_type *tlv; + + tlv = eigrp_IPv4_InternalTLV_new (); + + tlv->type = stream_getw(s); + tlv->length = stream_getw(s); + tlv->forward.s_addr = stream_getl(s); + tlv->metric.delay = stream_getl(s); + tlv->metric.bandwith = stream_getl(s); + tlv->metric.mtu[0] = stream_getc(s); + tlv->metric.mtu[1] = stream_getc(s); + tlv->metric.mtu[2] = stream_getc(s); + tlv->metric.hop_count = stream_getc(s); + tlv->metric.reliability = stream_getc(s); + tlv->metric.load = stream_getc(s); + tlv->metric.tag = stream_getc(s); + tlv->metric.flags = stream_getc(s); + + tlv->prefix_length = stream_getc(s); + + if (tlv->prefix_length <= 8) + { + tlv->destination_part[0] = stream_getc(s); + tlv->destination.s_addr = (tlv->destination_part[0]); + + } + else if (tlv->prefix_length > 8 && tlv->prefix_length <= 16) + { + tlv->destination_part[0] = stream_getc(s); + tlv->destination_part[1] = stream_getc(s); + tlv->destination.s_addr = ((tlv->destination_part[1] << 8) + + tlv->destination_part[0]); + } + else if (tlv->prefix_length > 16 && tlv->prefix_length <= 24) + { + tlv->destination_part[0] = stream_getc(s); + tlv->destination_part[1] = stream_getc(s); + tlv->destination_part[2] = stream_getc(s); + tlv->destination.s_addr = ((tlv->destination_part[2] << 16) + + (tlv->destination_part[1] << 8) + tlv->destination_part[0]); + } + else if (tlv->prefix_length > 24 && tlv->prefix_length <= 32) + { + tlv->destination_part[0] = stream_getc(s); + tlv->destination_part[1] = stream_getc(s); + tlv->destination_part[2] = stream_getc(s); + tlv->destination_part[3] = stream_getc(s); + tlv->destination.s_addr = ((tlv->destination_part[3] << 24) + + (tlv->destination_part[2] << 16) + (tlv->destination_part[1] << 8) + + tlv->destination_part[0]); + } + return tlv; +} + +u_int16_t +eigrp_add_internalTLV_to_stream (struct stream *s, + struct eigrp_prefix_entry *pe) +{ + u_int16_t length; + + stream_putw(s, EIGRP_TLV_IPv4_INT); + if (pe->destination_ipv4->prefixlen <= 8) + { + stream_putw(s, 0x001A); + length = 0x001A; + } + if ((pe->destination_ipv4->prefixlen > 8) + && (pe->destination_ipv4->prefixlen <= 16)) + { + stream_putw(s, 0x001B); + length = 0x001B; + } + if ((pe->destination_ipv4->prefixlen > 16) + && (pe->destination_ipv4->prefixlen <= 24)) + { + stream_putw(s, 0x001C); + length = 0x001C; + } + if (pe->destination_ipv4->prefixlen > 24) + { + stream_putw(s, 0x001D); + length = 0x001D; + } + + stream_putl(s, 0x00000000); + + /*Metric*/ + stream_putl(s, pe->reported_metric.delay); + stream_putl(s, pe->reported_metric.bandwith); + stream_putc(s, pe->reported_metric.mtu[2]); + stream_putc(s, pe->reported_metric.mtu[1]); + stream_putc(s, pe->reported_metric.mtu[0]); + stream_putc(s, pe->reported_metric.hop_count); + stream_putc(s, pe->reported_metric.reliability); + stream_putc(s, pe->reported_metric.load); + stream_putc(s, pe->reported_metric.tag); + stream_putc(s, pe->reported_metric.flags); + + stream_putc(s, pe->destination_ipv4->prefixlen); + + if (pe->destination_ipv4->prefixlen <= 8) + { + stream_putc(s, pe->destination_ipv4->prefix.s_addr & 0xFF); + } + if ((pe->destination_ipv4->prefixlen > 8) + && (pe->destination_ipv4->prefixlen <= 16)) + { + stream_putc(s, pe->destination_ipv4->prefix.s_addr & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 8) & 0xFF); + } + if ((pe->destination_ipv4->prefixlen > 16) + && (pe->destination_ipv4->prefixlen <= 24)) + { + stream_putc(s, pe->destination_ipv4->prefix.s_addr & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 8) & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 16) & 0xFF); + } + if (pe->destination_ipv4->prefixlen > 24) + { + stream_putc(s, pe->destination_ipv4->prefix.s_addr & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 8) & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 16) & 0xFF); + stream_putc(s, (pe->destination_ipv4->prefix.s_addr >> 24) & 0xFF); + } + + return length; +} + +u_int16_t +eigrp_add_authTLV_MD5_to_stream (struct stream *s, + struct eigrp_interface *ei) +{ + struct key *key; + struct keychain *keychain; + struct TLV_MD5_Authentication_Type *authTLV; + + authTLV = eigrp_authTLV_MD5_new(); + + authTLV->type = htons(EIGRP_TLV_AUTH); + authTLV->length = htons(EIGRP_AUTH_MD5_TLV_SIZE); + authTLV->auth_type = htons(EIGRP_AUTH_TYPE_MD5); + authTLV->auth_length = htons(EIGRP_AUTH_TYPE_MD5_LEN); + authTLV->key_sequence = 0; + memset(authTLV->Nullpad,0,sizeof(authTLV->Nullpad)); + + + keychain = keychain_lookup(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + if(keychain) + key = key_lookup_for_send(keychain); + else + { + free(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + IF_DEF_PARAMS (ei->ifp)->auth_keychain = NULL; + eigrp_authTLV_MD5_free(authTLV); + return 0; + } + + if(key) + { + authTLV->key_id = htonl(key->index); + memset(authTLV->digest,0,EIGRP_AUTH_TYPE_MD5_LEN); + stream_put(s,authTLV, sizeof(struct TLV_MD5_Authentication_Type)); + eigrp_authTLV_MD5_free(authTLV); + return EIGRP_AUTH_MD5_TLV_SIZE; + } + + eigrp_authTLV_MD5_free(authTLV); + + return 0; +} + +u_int16_t +eigrp_add_authTLV_SHA256_to_stream (struct stream *s, + struct eigrp_interface *ei) +{ + struct key *key; + struct keychain *keychain; + struct TLV_SHA256_Authentication_Type *authTLV; + + authTLV = eigrp_authTLV_SHA256_new(); + + authTLV->type = htons(EIGRP_TLV_AUTH); + authTLV->length = htons(EIGRP_AUTH_SHA256_TLV_SIZE); + authTLV->auth_type = htons(EIGRP_AUTH_TYPE_SHA256); + authTLV->auth_length = htons(EIGRP_AUTH_TYPE_SHA256_LEN); + authTLV->key_sequence = 0; + memset(authTLV->Nullpad,0,sizeof(authTLV->Nullpad)); + + + keychain = keychain_lookup(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + if(keychain) + key = key_lookup_for_send(keychain); + else + { + free(IF_DEF_PARAMS (ei->ifp)->auth_keychain); + IF_DEF_PARAMS (ei->ifp)->auth_keychain = NULL; + eigrp_authTLV_SHA256_free(authTLV); + return 0; + } + + if(key) + { + authTLV->key_id = 0; + memset(authTLV->digest,0,EIGRP_AUTH_TYPE_SHA256_LEN); + stream_put(s,authTLV, sizeof(struct TLV_SHA256_Authentication_Type)); + eigrp_authTLV_SHA256_free(authTLV); + return EIGRP_AUTH_SHA256_TLV_SIZE; + } + + eigrp_authTLV_SHA256_free(authTLV); + + return 0; + +} + +struct TLV_MD5_Authentication_Type * +eigrp_authTLV_MD5_new () +{ + struct TLV_MD5_Authentication_Type *new; + + new = XCALLOC(MTYPE_EIGRP_AUTH_TLV, sizeof(struct TLV_MD5_Authentication_Type)); + + return new; +} + +void +eigrp_authTLV_MD5_free (struct TLV_MD5_Authentication_Type *authTLV) +{ + + XFREE(MTYPE_EIGRP_AUTH_TLV, authTLV); +} + +struct TLV_SHA256_Authentication_Type * +eigrp_authTLV_SHA256_new () +{ + struct TLV_SHA256_Authentication_Type *new; + + new = XCALLOC(MTYPE_EIGRP_AUTH_SHA256_TLV, sizeof(struct TLV_SHA256_Authentication_Type)); + + return new; +} + +void +eigrp_authTLV_SHA256_free (struct TLV_SHA256_Authentication_Type *authTLV) +{ + + XFREE(MTYPE_EIGRP_AUTH_SHA256_TLV, authTLV); +} + + +struct TLV_IPv4_Internal_type * +eigrp_IPv4_InternalTLV_new () +{ + struct TLV_IPv4_Internal_type *new; + + new = XCALLOC(MTYPE_EIGRP_IPV4_INT_TLV,sizeof(struct TLV_IPv4_Internal_type)); + + return new; +} + +void +eigrp_IPv4_InternalTLV_free (struct TLV_IPv4_Internal_type *IPv4_InternalTLV) +{ + + XFREE(MTYPE_EIGRP_IPV4_INT_TLV, IPv4_InternalTLV); +} + +struct TLV_Sequence_Type * +eigrp_SequenceTLV_new () +{ + struct TLV_Sequence_Type *new; + + new = XCALLOC(MTYPE_EIGRP_SEQ_TLV,sizeof(struct TLV_Sequence_Type)); + + return new; +} |
