diff options
Diffstat (limited to 'ospfd/ospf_packet.c')
-rw-r--r-- | ospfd/ospf_packet.c | 3243 |
1 files changed, 3243 insertions, 0 deletions
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c new file mode 100644 index 00000000..2156ce33 --- /dev/null +++ b/ospfd/ospf_packet.c @@ -0,0 +1,3243 @@ +/* + * OSPF Sending and Receiving OSPF Packets. + * Copyright (C) 1999, 2000 Toshiaki Takada + * + * 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 "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "md5-gnu.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" + +static void ospf_ls_ack_send_list (struct ospf_interface *, list, + struct in_addr); + +/* Packet Type String. */ +char *ospf_packet_type_str[] = +{ + "unknown", + "Hello", + "Database Description", + "Link State Request", + "Link State Update", + "Link State Acknowledgment", +}; + +extern int in_cksum (void *ptr, int nbytes); + +/* OSPF authentication checking function */ +int +ospf_auth_type (struct ospf_interface *oi) +{ + int auth_type; + + if (OSPF_IF_PARAM (oi, auth_type) == OSPF_AUTH_NOTSET) + auth_type = oi->area->auth_type; + else + auth_type = OSPF_IF_PARAM (oi, auth_type); + + /* Handle case where MD5 key list is not configured aka Cisco */ + if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC && + list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + return OSPF_AUTH_NULL; + + return auth_type; + +} + +/* forward output pointer. */ +void +ospf_output_forward (struct stream *s, int size) +{ + s->putp += size; +} + +struct ospf_packet * +ospf_packet_new (size_t size) +{ + struct ospf_packet *new; + + new = XCALLOC (MTYPE_OSPF_PACKET, sizeof (struct ospf_packet)); + new->s = stream_new (size); + + return new; +} + +void +ospf_packet_free (struct ospf_packet *op) +{ + if (op->s) + stream_free (op->s); + + XFREE (MTYPE_OSPF_PACKET, op); + + op = NULL; +} + +struct ospf_fifo * +ospf_fifo_new () +{ + struct ospf_fifo *new; + + new = XCALLOC (MTYPE_OSPF_FIFO, sizeof (struct ospf_fifo)); + return new; +} + +/* Add new packet to fifo. */ +void +ospf_fifo_push (struct ospf_fifo *fifo, struct ospf_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +struct ospf_packet * +ospf_fifo_pop (struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + + op = fifo->head; + + if (op) + { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +struct ospf_packet * +ospf_fifo_head (struct ospf_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void +ospf_fifo_flush (struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + struct ospf_packet *next; + + for (op = fifo->head; op; op = next) + { + next = op->next; + ospf_packet_free (op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void +ospf_fifo_free (struct ospf_fifo *fifo) +{ + ospf_fifo_flush (fifo); + + XFREE (MTYPE_OSPF_FIFO, fifo); +} + +void +ospf_packet_add (struct ospf_interface *oi, struct ospf_packet *op) +{ + /* Add packet to end of queue. */ + ospf_fifo_push (oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +void +ospf_packet_delete (struct ospf_interface *oi) +{ + struct ospf_packet *op; + + op = ospf_fifo_pop (oi->obuf); + + if (op) + ospf_packet_free (op); +} + +struct stream * +ospf_stream_copy (struct stream *new, struct stream *s) +{ + new->endp = s->endp; + new->putp = s->putp; + new->getp = s->getp; + + memcpy (new->data, s->data, stream_get_endp (s)); + + return new; +} + +struct ospf_packet * +ospf_packet_dup (struct ospf_packet *op) +{ + struct ospf_packet *new; + + new = ospf_packet_new (op->length); + ospf_stream_copy (new->s, op->s); + + new->dst = op->dst; + new->length = op->length; + + return new; +} + +int +ospf_packet_max (struct ospf_interface *oi) +{ + int max; + + if ( ospf_auth_type (oi) == OSPF_AUTH_CRYPTOGRAPHIC) + max = oi->ifp->mtu - OSPF_AUTH_MD5_SIZE - 88; + else + max = oi->ifp->mtu - 88; + + return max; +} + + +int +ospf_check_md5_digest (struct ospf_interface *oi, struct stream *s, + u_int16_t length) +{ + void *ibuf; + struct md5_ctx ctx; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + unsigned char *pdigest; + struct crypt_key *ck; + struct ospf_header *ospfh; + struct ospf_neighbor *nbr; + + + ibuf = STREAM_PNT (s); + ospfh = (struct ospf_header *) ibuf; + + /* Get pointer to the end of the packet. */ + pdigest = ibuf + length; + + /* Get secret key. */ + ck = ospf_crypt_key_lookup (OSPF_IF_PARAM (oi, auth_crypt), + ospfh->u.crypt.key_id); + if (ck == NULL) + { + zlog_warn ("interface %s: ospf_check_md5 no key %d", + IF_NAME (oi), ospfh->u.crypt.key_id); + return 0; + } + + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id); + + if (nbr && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) + { + zlog_warn ("interface %s: ospf_check_md5 bad sequence %d (expect %d)", + IF_NAME (oi), + ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum)); + return 0; + } + + /* Generate a digest for the ospf packet - their digest + our digest. */ + md5_init_ctx (&ctx); + md5_process_bytes (ibuf, length, &ctx); + md5_process_bytes (ck->auth_key, OSPF_AUTH_MD5_SIZE, &ctx); + md5_finish_ctx (&ctx, digest); + + /* compare the two */ + if (memcmp (pdigest, digest, OSPF_AUTH_MD5_SIZE)) + { + zlog_warn ("interface %s: ospf_check_md5 checksum mismatch", + IF_NAME (oi)); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +/* This function is called from ospf_write(), it will detect the + authentication scheme and if it is MD5, it will change the sequence + and update the MD5 digest. */ +int +ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) +{ + struct ospf_header *ospfh; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + struct md5_ctx ctx; + void *ibuf; + unsigned long oldputp; + struct crypt_key *ck; + char *auth_key; + + ibuf = STREAM_DATA (op->s); + ospfh = (struct ospf_header *) ibuf; + + if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + return 0; + + /* We do this here so when we dup a packet, we don't have to + waste CPU rewriting other headers. */ + ospfh->u.crypt.crypt_seqnum = htonl (oi->crypt_seqnum++); + + /* Get MD5 Authentication key from auth_key list. */ + if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + auth_key = ""; + else + { + ck = getdata (OSPF_IF_PARAM (oi, auth_crypt)->tail); + auth_key = ck->auth_key; + } + + /* Generate a digest for the entire packet + our secret key. */ + md5_init_ctx (&ctx); + md5_process_bytes (ibuf, ntohs (ospfh->length), &ctx); + md5_process_bytes (auth_key, OSPF_AUTH_MD5_SIZE, &ctx); + md5_finish_ctx (&ctx, digest); + + /* Append md5 digest to the end of the stream. */ + oldputp = stream_get_putp (op->s); + stream_set_putp (op->s, ntohs (ospfh->length)); + stream_put (op->s, digest, OSPF_AUTH_MD5_SIZE); + stream_set_putp (op->s, oldputp); + + /* We do *NOT* increment the OSPF header length. */ + op->length += OSPF_AUTH_MD5_SIZE; + + return OSPF_AUTH_MD5_SIZE; +} + + +int +ospf_ls_req_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_ls_req = NULL; + + /* Send Link State Request. */ + if (ospf_ls_request_count (nbr)) + ospf_ls_req_send (nbr); + + /* Set Link State Request retransmission timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); + + return 0; +} + +void +ospf_ls_req_event (struct ospf_neighbor *nbr) +{ + if (nbr->t_ls_req) + { + thread_cancel (nbr->t_ls_req); + nbr->t_ls_req = NULL; + } + nbr->t_ls_req = thread_add_event (master, ospf_ls_req_timer, nbr, 0); +} + +/* Cyclic timer function. Fist registered in ospf_nbr_new () in + ospf_neighbor.c */ +int +ospf_ls_upd_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_ls_upd = NULL; + + /* Send Link State Update. */ + if (ospf_ls_retransmit_count (nbr) > 0) + { + list update; + struct ospf_lsdb *lsdb; + int i; + struct timeval now; + int retransmit_interval; + + gettimeofday (&now, NULL); + retransmit_interval = OSPF_IF_PARAM (nbr->oi, retransmit_interval); + + lsdb = &nbr->ls_rxmt; + update = list_new (); + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top (table); rn; rn = route_next (rn)) + { + struct ospf_lsa *lsa; + + if ((lsa = rn->info) != NULL) + /* Don't retransmit an LSA if we received it within + the last RxmtInterval seconds - this is to allow the + neighbour a chance to acknowledge the LSA as it may + have ben just received before the retransmit timer + fired. This is a small tweak to what is in the RFC, + but it will cut out out a lot of retransmit traffic + - MAG */ + if (tv_cmp (tv_sub (now, lsa->tv_recv), + int2tv (retransmit_interval)) >= 0) + listnode_add (update, rn->info); + } + } + + if (listcount (update) > 0) + ospf_ls_upd_send (nbr, update, OSPF_SEND_PACKET_DIRECT); + list_delete (update); + } + + /* Set LS Update retransmission timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); + + return 0; +} + +int +ospf_ls_ack_timer (struct thread *thread) +{ + struct ospf_interface *oi; + + oi = THREAD_ARG (thread); + oi->t_ls_ack = NULL; + + /* Send Link State Acknowledgment. */ + if (listcount (oi->ls_ack) > 0) + ospf_ls_ack_send_delayed (oi); + + /* Set LS Ack timer. */ + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + + return 0; +} + +int +ospf_write (struct thread *thread) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + struct sockaddr_in sa_dst; + u_char type; + int ret; + int flags = 0; + struct ip iph; + struct msghdr msg; + struct iovec iov[2]; + struct ospf *top; + listnode node; + + top = THREAD_ARG (thread); + top->t_write = NULL; + + node = listhead (top->oi_write_q); + assert (node); + oi = getdata (node); + assert (oi); + + /* Get one packet from queue. */ + op = ospf_fifo_head (oi->obuf); + assert (op); + assert (op->length >= OSPF_HEADER_SIZE); + + if (op->dst.s_addr == htonl (OSPF_ALLSPFROUTERS) || + op->dst.s_addr == htonl (OSPF_ALLDROUTERS)) + ospf_if_ipmulticast (top, oi->address, oi->ifp->ifindex); + + /* Rewrite the md5 signature & update the seq */ + ospf_make_md5_digest (oi, op); + + memset (&sa_dst, 0, sizeof (sa_dst)); + sa_dst.sin_family = AF_INET; +#ifdef HAVE_SIN_LEN + sa_dst.sin_len = sizeof(sa_dst); +#endif /* HAVE_SIN_LEN */ + sa_dst.sin_addr = op->dst; + sa_dst.sin_port = htons (0); + + /* Set DONTROUTE flag if dst is unicast. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (!IN_MULTICAST (htonl (op->dst.s_addr))) + flags = MSG_DONTROUTE; + + iph.ip_hl = sizeof (struct ip) >> 2; + iph.ip_v = IPVERSION; + iph.ip_tos = 0; +#if defined(__NetBSD__) || defined(__FreeBSD__) + iph.ip_len = iph.ip_hl*4 + op->length; +#else + iph.ip_len = htons (iph.ip_hl*4 + op->length); +#endif + iph.ip_id = 0; + iph.ip_off = 0; + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + iph.ip_ttl = OSPF_VL_IP_TTL; + else + iph.ip_ttl = OSPF_IP_TTL; + iph.ip_p = IPPROTO_OSPFIGP; + iph.ip_sum = 0; + iph.ip_src.s_addr = oi->address->u.prefix4.s_addr; + iph.ip_dst.s_addr = op->dst.s_addr; + + memset (&msg, 0, sizeof (msg)); + msg.msg_name = &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*4; + iov[1].iov_base = STREAM_DATA (op->s); + iov[1].iov_len = op->length; + + ret = sendmsg (top->fd, &msg, flags); + + if (ret < 0) + zlog_warn ("*** sendmsg in ospf_write failed with %s", strerror (errno)); + + /* Retrieve OSPF packet type. */ + stream_set_getp (op->s, 1); + type = stream_getc (op->s); + + /* Show debug sending packet. */ + if (IS_DEBUG_OSPF_PACKET (type - 1, SEND)) + { + if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL)) + { + zlog_info ("-----------------------------------------------------"); + stream_set_getp (op->s, 0); + ospf_packet_dump (op->s); + } + + zlog_info ("%s sent to [%s] via [%s].", + ospf_packet_type_str[type], inet_ntoa (op->dst), + IF_NAME (oi)); + + if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL)) + zlog_info ("-----------------------------------------------------"); + } + + /* Now delete packet from queue. */ + ospf_packet_delete (oi); + + if (ospf_fifo_head (oi->obuf) == NULL) + { + oi->on_write_q = 0; + list_delete_node (top->oi_write_q, node); + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty (top->oi_write_q)) + ospf_top->t_write = + thread_add_write (master, ospf_write, top, top->fd); + + return 0; +} + +/* OSPF Hello message read -- RFC2328 Section 10.5. */ +void +ospf_hello (struct ip *iph, struct ospf_header *ospfh, + struct stream * s, struct ospf_interface *oi, int size) +{ + struct ospf_hello *hello; + struct ospf_neighbor *nbr; + struct route_node *rn; + struct prefix p, key; + int old_state; + + /* increment statistics. */ + oi->hello_in++; + + hello = (struct ospf_hello *) STREAM_PNT (s); + + /* If Hello is myself, silently discard. */ + if (IPV4_ADDR_SAME (&ospfh->router_id, &ospf_top->router_id)) + return; + + /* If incoming interface is passive one, ignore Hello. */ + if (OSPF_IF_PARAM (oi, passive_interface) == OSPF_IF_PASSIVE) + return; + + /* get neighbor prefix. */ + p.family = AF_INET; + p.prefixlen = ip_masklen (hello->network_mask); + p.u.prefix4 = iph->ip_src; + + /* Compare network mask. */ + /* Checking is ignored for Point-to-Point and Virtual link. */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT + && oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (oi->address->prefixlen != p.prefixlen) + { + zlog_warn ("Packet %s [Hello:RECV]: NetworkMask mismatch.", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Compare Hello Interval. */ + if (OSPF_IF_PARAM (oi, v_hello) != ntohs (hello->hello_interval)) + { + zlog_warn ("Packet %s [Hello:RECV]: HelloInterval mismatch.", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Compare Router Dead Interval. */ + if (OSPF_IF_PARAM (oi, v_wait) != ntohl (hello->dead_interval)) + { + zlog_warn ("Packet %s [Hello:RECV]: RouterDeadInterval mismatch.", + inet_ntoa (ospfh->router_id)); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Packet %s [Hello:RECV]: Options %s", + inet_ntoa (ospfh->router_id), + ospf_options_dump (hello->options)); + + /* Compare options. */ +#define REJECT_IF_TBIT_ON 1 /* XXX */ +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG (hello->options, OSPF_OPTION_T)) + { + /* + * This router does not support non-zero TOS. + * Drop this Hello packet not to establish neighbor relationship. + */ + zlog_warn ("Packet %s [Hello:RECV]: T-bit on, drop it.", + inet_ntoa (ospfh->router_id)); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + +#ifdef HAVE_OPAQUE_LSA + if (CHECK_FLAG (ospf_top->config, OSPF_OPAQUE_CAPABLE) + && CHECK_FLAG (hello->options, OSPF_OPTION_O)) + { + /* + * This router does know the correct usage of O-bit + * the bit should be set in DD packet only. + */ + zlog_warn ("Packet %s [Hello:RECV]: O-bit abuse?", + inet_ntoa (ospfh->router_id)); +#ifdef STRICT_OBIT_USAGE_CHECK + return; /* Reject this packet. */ +#else /* STRICT_OBIT_USAGE_CHECK */ + UNSET_FLAG (hello->options, OSPF_OPTION_O); /* Ignore O-bit. */ +#endif /* STRICT_OBIT_USAGE_CHECK */ + } +#endif /* HAVE_OPAQUE_LSA */ + + /* new for NSSA is to ensure that NP is on and E is off */ + +#ifdef HAVE_NSSA + if (oi->area->external_routing == OSPF_AREA_NSSA) + { + if (! (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_NP) + && CHECK_FLAG (hello->options, OSPF_OPTION_NP) + && ! CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E) + && ! CHECK_FLAG (hello->options, OSPF_OPTION_E))) + { + zlog_warn ("NSSA-Packet-%s[Hello:RECV]: my options: %x, his options %x", inet_ntoa (ospfh->router_id), OPTIONS (oi), hello->options); + return; + } + if (IS_DEBUG_OSPF_NSSA) + zlog_info ("NSSA-Hello:RECV:Packet from %s:", inet_ntoa(ospfh->router_id)); + } + else +#endif /* HAVE_NSSA */ + /* The setting of the E-bit found in the Hello Packet's Options + field must match this area's ExternalRoutingCapability A + mismatch causes processing to stop and the packet to be + dropped. The setting of the rest of the bits in the Hello + Packet's Options field should be ignored. */ + if (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E) != + CHECK_FLAG (hello->options, OSPF_OPTION_E)) + { + zlog_warn ("Packet[Hello:RECV]: my options: %x, his options %x", + OPTIONS (oi), hello->options); + return; + } + + + /* Get neighbor information from table. */ + key.family = AF_INET; + key.prefixlen = IPV4_MAX_BITLEN; + key.u.prefix4 = iph->ip_src; + + rn = route_node_get (oi->nbrs, &key); + if (rn->info) + { + route_unlock_node (rn); + nbr = rn->info; + + if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt) + { + nbr->src = iph->ip_src; + nbr->address = p; + } + } + else + { + /* Create new OSPF Neighbor structure. */ + nbr = ospf_nbr_new (oi); + nbr->state = NSM_Down; + nbr->src = iph->ip_src; + nbr->address = p; + + rn->info = nbr; + + nbr->nbr_nbma = NULL; + + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_nbr_nbma *nbr_nbma; + listnode node; + + for (node = listhead (oi->nbr_nbma); node; nextnode (node)) + { + nbr_nbma = getdata (node); + assert (nbr_nbma); + + if (IPV4_ADDR_SAME(&nbr_nbma->addr, &iph->ip_src)) + { + nbr_nbma->nbr = nbr; + nbr->nbr_nbma = nbr_nbma; + + if (nbr_nbma->t_poll) + OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll); + + nbr->state_change = nbr_nbma->state_change + 1; + } + } + } + + /* New nbr, save the crypto sequence number if necessary */ + if (ntohs (ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("NSM[%s:%s]: start", IF_NAME (nbr->oi), + inet_ntoa (nbr->router_id)); + } + + nbr->router_id = ospfh->router_id; + + old_state = nbr->state; + + /* Add event to thread. */ + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_HelloReceived); + + /* RFC2328 Section 9.5.1 + If the router is not eligible to become Designated Router, + (snip) It must also send an Hello Packet in reply to an + Hello Packet received from any eligible neighbor (other than + the current Designated Router and Backup Designated Router). */ + if (oi->type == OSPF_IFTYPE_NBMA) + if (PRIORITY(oi) == 0 && hello->priority > 0 + && IPV4_ADDR_CMP(&DR(oi), &iph->ip_src) + && IPV4_ADDR_CMP(&BDR(oi), &iph->ip_src)) + OSPF_NSM_TIMER_ON (nbr->t_hello_reply, ospf_hello_reply_timer, + OSPF_HELLO_REPLY_DELAY); + + /* on NBMA network type, it happens to receive bidirectional Hello packet + without advance 1-Way Received event. + To avoid incorrect DR-seletion, raise 1-Way Received event.*/ + if (oi->type == OSPF_IFTYPE_NBMA && + (old_state == NSM_Down || old_state == NSM_Attempt)) + { + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_OneWayReceived); + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + if (ospf_nbr_bidirectional (&ospf_top->router_id, hello->neighbors, + size - OSPF_HELLO_MIN_SIZE)) + { + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_TwoWayReceived); + nbr->options |= hello->options; + } + else + { + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_OneWayReceived); + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + /* If neighbor itself declares DR and no BDR exists, + cause event BackupSeen */ + if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router)) + if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen); + + /* neighbor itself declares BDR. */ + if (oi->state == ISM_Waiting && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router)) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen); + + /* had not previously. */ + if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router) && + IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->d_router)) || + (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->d_router) && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->d_router))) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* had not previously. */ + if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router) && + IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->bd_router)) || + (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->bd_router) && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->bd_router))) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* Neighbor priority check. */ + if (nbr->priority >= 0 && nbr->priority != hello->priority) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; +} + +/* Save DD flags/options/Seqnum received. */ +void +ospf_db_desc_save_current (struct ospf_neighbor *nbr, + struct ospf_db_desc *dd) +{ + nbr->last_recv.flags = dd->flags; + nbr->last_recv.options = dd->options; + nbr->last_recv.dd_seqnum = ntohl (dd->dd_seqnum); +} + +/* Process rest of DD packet. */ +static void +ospf_db_desc_proc (struct stream *s, struct ospf_interface *oi, + struct ospf_neighbor *nbr, struct ospf_db_desc *dd, + u_int16_t size) +{ + struct ospf_lsa *new, *find; + struct lsa_header *lsah; + + stream_forward (s, OSPF_DB_DESC_MIN_SIZE); + for (size -= OSPF_DB_DESC_MIN_SIZE; + size >= OSPF_LSA_HEADER_SIZE; size -= OSPF_LSA_HEADER_SIZE) + { + lsah = (struct lsa_header *) STREAM_PNT (s); + stream_forward (s, OSPF_LSA_HEADER_SIZE); + + /* Unknown LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) + { + zlog_warn ("Pakcet [DD:RECV]: Unknown LS type %d.", lsah->type); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } + +#ifdef HAVE_OPAQUE_LSA + if (IS_OPAQUE_LSA (lsah->type) + && ! CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { + zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } +#endif /* HAVE_OPAQUE_LSA */ + + switch (lsah->type) + { + case OSPF_AS_EXTERNAL_LSA: +#ifdef HAVE_OPAQUE_LSA + case OSPF_OPAQUE_AS_LSA: +#endif /* HAVE_OPAQUE_LSA */ +#ifdef HAVE_NSSA + /* Check for stub area. Reject if AS-External from stub but + allow if from NSSA. */ + if (oi->area->external_routing == OSPF_AREA_STUB) +#else /* ! HAVE_NSSA */ + if (oi->area->external_routing != OSPF_AREA_DEFAULT) +#endif /* HAVE_NSSA */ + { + zlog_warn ("Packet [DD:RECV]: LSA[Type%d:%s] from %s area.", + lsah->type, inet_ntoa (lsah->id), + (oi->area->external_routing == OSPF_AREA_STUB) ?\ + "STUB" : "NSSA"); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } + break; + default: + break; + } + + /* Create LS-request object. */ + new = ospf_ls_request_new (lsah); + + /* Lookup received LSA, then add LS request list. */ + find = ospf_lsa_lookup_by_header (oi->area, lsah); + if (!find || ospf_lsa_more_recent (find, new) < 0) + { + ospf_ls_request_add (nbr, new); + ospf_lsa_discard (new); + } + else + { + /* Received LSA is not recent. */ + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Packet [DD:RECV]: LSA received Type %d, " + "ID %s is not recent.", lsah->type, inet_ntoa (lsah->id)); + ospf_lsa_discard (new); + continue; + } + } + + /* Master */ + if (IS_SET_DD_MS (nbr->dd_flags)) + { + nbr->dd_seqnum++; + /* Entire DD packet sent. */ + if (!IS_SET_DD_M (dd->flags) && !IS_SET_DD_M (nbr->dd_flags)) + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone); + else + /* Send new DD packet. */ + ospf_db_desc_send (nbr); + } + /* Slave */ + else + { + nbr->dd_seqnum = ntohl (dd->dd_seqnum); + + /* When master's more flags is not set. */ + if (!IS_SET_DD_M (dd->flags) && ospf_db_summary_isempty (nbr)) + { + nbr->dd_flags &= ~(OSPF_DD_FLAG_M); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone); + } + + /* Send DD pakcet in reply. */ + ospf_db_desc_send (nbr); + } + + /* Save received neighbor values from DD. */ + ospf_db_desc_save_current (nbr, dd); +} + +int +ospf_db_desc_is_dup (struct ospf_db_desc *dd, struct ospf_neighbor *nbr) +{ + /* Is DD duplicated? */ + if (dd->options == nbr->last_recv.options && + dd->flags == nbr->last_recv.flags && + dd->dd_seqnum == htonl (nbr->last_recv.dd_seqnum)) + return 1; + + return 0; +} + +/* OSPF Database Description message read -- RFC2328 Section 10.6. */ +void +ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_db_desc *dd; + struct ospf_neighbor *nbr; + + /* Increment statistics. */ + oi->db_desc_in++; + + dd = (struct ospf_db_desc *) STREAM_PNT (s); + + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src); + if (nbr == NULL) + { + zlog_warn ("Packet[DD]: Unknown Neighbor %s", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Check MTU. */ + if (ntohs (dd->mtu) > oi->ifp->mtu) + { + zlog_warn ("Packet[DD]: MTU is larger than [%s]'s MTU", IF_NAME (oi)); + return; + } + +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG (dd->options, OSPF_OPTION_T)) + { + /* + * In Hello protocol, optional capability must have checked + * to prevent this T-bit enabled router be my neighbor. + */ + zlog_warn ("Packet[DD]: Neighbor %s: T-bit on?", inet_ntoa (nbr->router_id)); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + +#ifdef HAVE_OPAQUE_LSA + if (CHECK_FLAG (dd->options, OSPF_OPTION_O) + && !CHECK_FLAG (ospf_top->config, OSPF_OPAQUE_CAPABLE)) + { + /* + * This node is not configured to handle O-bit, for now. + * Clear it to ignore unsupported capability proposed by neighbor. + */ + UNSET_FLAG (dd->options, OSPF_OPTION_O); + } +#endif /* HAVE_OPAQUE_LSA */ + + /* Process DD packet by neighbor status. */ + switch (nbr->state) + { + case NSM_Down: + case NSM_Attempt: + case NSM_TwoWay: + zlog_warn ("Packet[DD]: Neighbor state is %s, packet discarded.", + LOOKUP (ospf_nsm_state_msg, nbr->state)); + break; + case NSM_Init: + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_TwoWayReceived); + /* If the new state is ExStart, the processing of the current + packet should then continue in this new state by falling + through to case ExStart below. */ + if (nbr->state != NSM_ExStart) + break; + case NSM_ExStart: + /* Initial DBD */ + if ((IS_SET_DD_ALL (dd->flags) == OSPF_DD_FLAG_ALL) && + (size == OSPF_DB_DESC_MIN_SIZE)) + { + if (IPV4_ADDR_CMP (&nbr->router_id, &ospf_top->router_id) > 0) + { + /* We're Slave---obey */ + zlog_warn ("Packet[DD]: Negotiation done (Slave)."); + nbr->dd_seqnum = ntohl (dd->dd_seqnum); + nbr->dd_flags &= ~(OSPF_DD_FLAG_MS|OSPF_DD_FLAG_I); /* Reset I/MS */ + } + else + { + /* We're Master, ignore the initial DBD from Slave */ + zlog_warn ("Packet[DD]: Initial DBD from Slave, ignoring."); + break; + } + } + /* Ack from the Slave */ + else if (!IS_SET_DD_MS (dd->flags) && !IS_SET_DD_I (dd->flags) && + ntohl (dd->dd_seqnum) == nbr->dd_seqnum && + IPV4_ADDR_CMP (&nbr->router_id, &ospf_top->router_id) < 0) + { + zlog_warn ("Packet[DD]: Negotiation done (Master)."); + nbr->dd_flags &= ~OSPF_DD_FLAG_I; + } + else + { + zlog_warn ("Packet[DD]: Negotiation fails."); + break; + } + + /* This is where the real Options are saved */ + nbr->options = dd->options; + +#ifdef HAVE_OPAQUE_LSA + if (CHECK_FLAG (ospf_top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Neighbor[%s] is %sOpaque-capable.", + inet_ntoa (nbr->router_id), + CHECK_FLAG (nbr->options, OSPF_OPTION_O) ? "" : "NOT "); + + if (! CHECK_FLAG (nbr->options, OSPF_OPTION_O) + && IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4)) + { + zlog_warn ("DR-neighbor[%s] is NOT opaque-capable; Opaque-LSAs cannot be reliably advertised in this network.", inet_ntoa (nbr->router_id)); + /* This situation is undesirable, but not a real error. */ + } + } +#endif /* HAVE_OPAQUE_LSA */ + + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_NegotiationDone); + + /* continue processing rest of packet. */ + ospf_db_desc_proc (s, oi, nbr, dd, size); + break; + case NSM_Exchange: + if (ospf_db_desc_is_dup (dd, nbr)) + { + if (IS_SET_DD_MS (nbr->dd_flags)) + /* Master: discard duplicated DD packet. */ + zlog_warn ("Packet[DD] (Master): packet duplicated."); + else + /* Slave: cause to retransmit the last Database Description. */ + { + zlog_warn ("Packet[DD] [Slave]: packet duplicated."); + ospf_db_desc_resend (nbr); + } + break; + } + + /* Otherwise DD packet should be checked. */ + /* Check Master/Slave bit mismatch */ + if (IS_SET_DD_MS (dd->flags) != IS_SET_DD_MS (nbr->last_recv.flags)) + { + zlog_warn ("Packet[DD]: MS-bit mismatch."); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("Packet[DD]: dd->flags=%d, nbr->dd_flags=%d", + dd->flags, nbr->dd_flags); + break; + } + + /* Check initialize bit is set. */ + if (IS_SET_DD_I (dd->flags)) + { + zlog_warn ("Packet[DD]: I-bit set."); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD Options. */ + if (dd->options != nbr->options) + { +#ifdef ORIGINAL_CODING + /* Save the new options for debugging */ + nbr->options = dd->options; +#endif /* ORIGINAL_CODING */ + zlog_warn ("Packet[DD]: options mismatch."); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD sequence number. */ + if ((IS_SET_DD_MS (nbr->dd_flags) && + ntohl (dd->dd_seqnum) != nbr->dd_seqnum) || + (!IS_SET_DD_MS (nbr->dd_flags) && + ntohl (dd->dd_seqnum) != nbr->dd_seqnum + 1)) + { + zlog_warn ("Pakcet[DD]: sequence number mismatch."); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Continue processing rest of packet. */ + ospf_db_desc_proc (s, oi, nbr, dd, size); + break; + case NSM_Loading: + case NSM_Full: + if (ospf_db_desc_is_dup (dd, nbr)) + { + if (IS_SET_DD_MS (nbr->dd_flags)) + { + /* Master should discard duplicate DD packet. */ + zlog_warn ("Pakcet[DD]: duplicated, packet discarded."); + break; + } + else + { + struct timeval t, now; + gettimeofday (&now, NULL); + t = tv_sub (now, nbr->last_send_ts); + if (tv_cmp (t, int2tv (nbr->v_inactivity)) < 0) + { + /* In states Loading and Full the slave must resend + its last Database Description packet in response to + duplicate Database Description packets received + from the master. For this reason the slave must + wait RouterDeadInterval seconds before freeing the + last Database Description packet. Reception of a + Database Description packet from the master after + this interval will generate a SeqNumberMismatch + neighbor event. RFC2328 Section 10.8 */ + ospf_db_desc_resend (nbr); + break; + } + } + } + + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + default: + zlog_warn ("Packet[DD]: NSM illegal status."); + break; + } +} + +#define OSPF_LSA_KEY_SIZE 12 /* type(4) + id(4) + ar(4) */ + +/* OSPF Link State Request Read -- RFC2328 Section 10.7. */ +void +ospf_ls_req (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_neighbor *nbr; + u_int32_t ls_type; + struct in_addr ls_id; + struct in_addr adv_router; + struct ospf_lsa *find; + list ls_upd; + int length; + + /* Increment statistics. */ + oi->ls_req_in++; + + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src); + if (nbr == NULL) + { + zlog_warn ("Link State Request: Unknown Neighbor %s.", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Neighbor State should be Exchange or later. */ + if (nbr->state != NSM_Exchange && + nbr->state != NSM_Loading && + nbr->state != NSM_Full) + { + zlog_warn ("Link State Request: Neighbor state is %s, packet discarded.", + LOOKUP (ospf_nsm_state_msg, nbr->state)); + return; + } + + /* Send Link State Update for ALL requested LSAs. */ + ls_upd = list_new (); + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + + while (size >= OSPF_LSA_KEY_SIZE) + { + /* Get one slice of Link State Request. */ + ls_type = stream_getl (s); + ls_id.s_addr = stream_get_ipv4 (s); + adv_router.s_addr = stream_get_ipv4 (s); + + /* Verify LSA type. */ + if (ls_type < OSPF_MIN_LSA || ls_type >= OSPF_MAX_LSA) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + list_delete (ls_upd); + return; + } + + /* Search proper LSA in LSDB. */ + find = ospf_lsa_lookup (oi->area, ls_type, ls_id, adv_router); + if (find == NULL) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + list_delete (ls_upd); + return; + } + + /* Packet overflows MTU size, send immediatly. */ + if (length + ntohs (find->data->length) > OSPF_PACKET_MAX (oi)) + { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT); + else + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT); + + /* Only remove list contents. Keep ls_upd. */ + list_delete_all_node (ls_upd); + + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + } + + /* Append LSA to update list. */ + listnode_add (ls_upd, find); + length += ntohs (find->data->length); + + size -= OSPF_LSA_KEY_SIZE; + } + + /* Send rest of Link State Update. */ + if (listcount (ls_upd) > 0) + { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT); + else + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT); + + list_delete (ls_upd); + } + else + list_free (ls_upd); +} + +/* Get the list of LSAs from Link State Update packet. + And process some validation -- RFC2328 Section 13. (1)-(2). */ +static list +ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, + struct ospf_interface *oi, size_t size) +{ + u_int16_t count, sum; + u_int32_t length; + struct lsa_header *lsah; + struct ospf_lsa *lsa; + list lsas; + + lsas = list_new (); + + count = stream_getl (s); + size -= OSPF_LS_UPD_MIN_SIZE; /* # LSAs */ + + for (; size >= OSPF_LSA_HEADER_SIZE && count > 0; + size -= length, stream_forward (s, length), count--) + { + lsah = (struct lsa_header *) STREAM_PNT (s); + length = ntohs (lsah->length); + + if (length > size) + { + zlog_warn ("Link State Update: LSA length exceeds packet size."); + break; + } + + /* Validate the LSA's LS checksum. */ + sum = lsah->checksum; + if (sum != ospf_lsa_checksum (lsah)) + { + zlog_warn ("Link State Update: LSA checksum error %x, %x.", + sum, lsah->checksum); + continue; + } + + /* Examine the LSA's LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) + { + zlog_warn ("Link State Update: Unknown LS type %d", lsah->type); + continue; + } + + /* + * What if the received LSA's age is greater than MaxAge? + * Treat it as a MaxAge case -- endo. + */ + if (ntohs (lsah->ls_age) > OSPF_LSA_MAXAGE) + lsah->ls_age = htons (OSPF_LSA_MAXAGE); + +#ifdef HAVE_OPAQUE_LSA + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { +#ifdef STRICT_OBIT_USAGE_CHECK + if ((IS_OPAQUE_LSA(lsah->type) && + ! CHECK_FLAG (lsah->options, OSPF_OPTION_O)) + || (! IS_OPAQUE_LSA(lsah->type) && + CHECK_FLAG (lsah->options, OSPF_OPTION_O))) + { + /* + * This neighbor must know the exact usage of O-bit; + * the bit will be set in Type-9,10,11 LSAs only. + */ + zlog_warn ("LSA[Type%d:%s]: O-bit abuse?", lsah->type, inet_ntoa (lsah->id)); + continue; + } +#endif /* STRICT_OBIT_USAGE_CHECK */ + + /* Do not take in AS External Opaque-LSAs if we are a stub. */ + if (lsah->type == OSPF_OPAQUE_AS_LSA + && nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("LSA[Type%d:%s]: We are a stub, don't take this LSA.", lsah->type, inet_ntoa (lsah->id)); + continue; + } + } + else if (IS_OPAQUE_LSA(lsah->type)) + { + zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id)); + continue; + } +#endif /* HAVE_OPAQUE_LSA */ + + /* Create OSPF LSA instance. */ + lsa = ospf_lsa_new (); + + /* We may wish to put some error checking if type NSSA comes in + and area not in NSSA mode */ + switch (lsah->type) + { + case OSPF_AS_EXTERNAL_LSA: +#ifdef HAVE_OPAQUE_LSA + case OSPF_OPAQUE_AS_LSA: + lsa->area = NULL; + break; + case OSPF_OPAQUE_LINK_LSA: + lsa->oi = oi; /* Remember incoming interface for flooding control. */ + /* Fallthrough */ +#endif /* HAVE_OPAQUE_LSA */ + default: + lsa->area = oi->area; + break; + } + + lsa->data = ospf_lsa_data_new (length); + memcpy (lsa->data, lsah, length); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info("LSA[Type%d:%s]: %p new LSA created with Link State Update", + lsa->data->type, inet_ntoa (lsa->data->id), lsa); + listnode_add (lsas, lsa); + } + + return lsas; +} + +/* Cleanup Update list. */ +void +ospf_upd_list_clean (list lsas) +{ + listnode node; + struct ospf_lsa *lsa; + + for (node = listhead (lsas); node; nextnode (node)) + if ((lsa = getdata (node)) != NULL) + ospf_lsa_discard (lsa); + + list_delete (lsas); +} + +/* OSPF Link State Update message read -- RFC2328 Section 13. */ +void +ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_neighbor *nbr; + list lsas; +#ifdef HAVE_OPAQUE_LSA + list mylsa_acks, mylsa_upds; +#endif /* HAVE_OPAQUE_LSA */ + listnode node, next; + struct ospf_lsa *lsa = NULL; + /* unsigned long ls_req_found = 0; */ + + /* Dis-assemble the stream, update each entry, re-encapsulate for flooding */ + + /* Increment statistics. */ + oi->ls_upd_in++; + + /* Check neighbor. */ + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src); + if (nbr == NULL) + { + zlog_warn ("Link State Update: Unknown Neighbor %s on int: %s", + inet_ntoa (ospfh->router_id), IF_NAME (oi)); + return; + } + + /* Check neighbor state. */ + if (nbr->state < NSM_Exchange) + { + zlog_warn ("Link State Update: Neighbor[%s] state is less than Exchange", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Get list of LSAs from Link State Update packet. - Also perorms Stages + * 1 (validate LSA checksum) and 2 (check for LSA consistent type) + * of section 13. + */ + lsas = ospf_ls_upd_list_lsa (nbr, s, oi, size); + +#ifdef HAVE_OPAQUE_LSA + /* + * Prepare two kinds of lists to clean up unwanted self-originated + * Opaque-LSAs from the routing domain as soon as possible. + */ + mylsa_acks = list_new (); /* Let the sender cease retransmission. */ + mylsa_upds = list_new (); /* Flush target LSAs if necessary. */ + + /* + * If self-originated Opaque-LSAs that have flooded before restart + * are contained in the received LSUpd message, corresponding LSReq + * messages to be sent may have to be modified. + * To eliminate possible race conditions such that flushing and normal + * updating for the same LSA would take place alternately, this trick + * must be done before entering to the loop below. + */ + ospf_opaque_adjust_lsreq (nbr, lsas); +#endif /* HAVE_OPAQUE_LSA */ + +#define DISCARD_LSA(L,N) {\ + if (IS_DEBUG_OSPF_EVENT) \ + zlog_info ("ospf_lsa_discard() in ospf_ls_upd() point %d: lsa %p Type-%d", N, lsa, (int) lsa->data->type); \ + ospf_lsa_discard (L); \ + continue; } + + /* Process each LSA received in the one packet. */ + for (node = listhead (lsas); node; node = next) + { + struct ospf_lsa *ls_ret, *current; + int ret = 1; + + next = node->next; + + lsa = getdata (node); + +#ifdef HAVE_NSSA + if (IS_DEBUG_OSPF_NSSA) + { + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + char buf3[INET_ADDRSTRLEN]; + + zlog_info("LSA Type-%d from %s, ID: %s, ADV: %s", + lsa->data->type, + inet_ntop (AF_INET, &ospfh->router_id, + buf1, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->id, + buf2, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->adv_router, + buf3, INET_ADDRSTRLEN)); + } +#endif /* HAVE_NSSA */ + + listnode_delete (lsas, lsa); /* We don't need it in list anymore */ + + /* Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ + + /* LSA Type - Done above by ospf_ls_upd_list_lsa() */ + + /* Do not take in AS External LSAs if we are a stub or NSSA. */ + + /* Do not take in AS NSSA if this neighbor and we are not NSSA */ + + /* Do take in Type-7's if we are an NSSA */ + + /* If we are also an ABR, later translate them to a Type-5 packet */ + + /* Later, an NSSA Re-fresh can Re-fresh Type-7's and an ABR will + translate them to a separate Type-5 packet. */ + + if (lsa->data->type == OSPF_AS_EXTERNAL_LSA) + /* Reject from STUB or NSSA */ + if (nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) + { + DISCARD_LSA (lsa, 1); +#ifdef HAVE_NSSA + if (IS_DEBUG_OSPF_NSSA) + zlog_info("Incoming External LSA Discarded: We are NSSA/STUB Area"); +#endif /* HAVE_NSSA */ + } + +#ifdef HAVE_NSSA + if (lsa->data->type == OSPF_AS_NSSA_LSA) + if (nbr->oi->area->external_routing != OSPF_AREA_NSSA) + { + DISCARD_LSA (lsa,2); + if (IS_DEBUG_OSPF_NSSA) + zlog_info("Incoming NSSA LSA Discarded: Not NSSA Area"); + } +#endif /* HAVE_NSSA */ + + /* Find the LSA in the current database. */ + + current = ospf_lsa_lookup_by_header (oi->area, lsa->data); + + /* If the LSA's LS age is equal to MaxAge, and there is currently + no instance of the LSA in the router's link state database, + and none of router's neighbors are in states Exchange or Loading, + then take the following actions. */ + + if (IS_LSA_MAXAGE (lsa) && !current && + (ospf_nbr_count (oi->nbrs, NSM_Exchange) + + ospf_nbr_count (oi->nbrs, NSM_Loading)) == 0) + { + /* Response Link State Acknowledgment. */ + ospf_ls_ack_send (nbr, lsa); + + /* Discard LSA. */ + zlog_warn ("Link State Update: LS age is equal to MaxAge."); + DISCARD_LSA (lsa, 3); + } + +#ifdef HAVE_OPAQUE_LSA + if (IS_OPAQUE_LSA (lsa->data->type) + && IPV4_ADDR_SAME (&lsa->data->adv_router, &ospf_top->router_id)) + { + /* + * Even if initial flushing seems to be completed, there might + * be a case that self-originated LSA with MaxAge still remain + * in the routing domain. + * Just send an LSAck message to cease retransmission. + */ + if (IS_LSA_MAXAGE (lsa)) + { + zlog_warn ("LSA[%s]: Boomerang effect?", dump_lsa_key (lsa)); + ospf_ls_ack_send (nbr, lsa); + ospf_lsa_discard (lsa); + + if (current != NULL && ! IS_LSA_MAXAGE (current)) + ospf_opaque_lsa_refresh_schedule (current); + continue; + } + + /* + * If an instance of self-originated Opaque-LSA is not found + * in the LSDB, there are some possible cases here. + * + * 1) This node lost opaque-capability after restart. + * 2) Else, a part of opaque-type is no more supported. + * 3) Else, a part of opaque-id is no more supported. + * + * Anyway, it is still this node's responsibility to flush it. + * Otherwise, the LSA instance remains in the routing domain + * until its age reaches to MaxAge. + */ + if (current == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("LSA[%s]: Previously originated Opaque-LSA, not found in the LSDB.", dump_lsa_key (lsa)); + + SET_FLAG (lsa->flags, OSPF_LSA_SELF); + listnode_add (mylsa_upds, ospf_lsa_dup (lsa)); + listnode_add (mylsa_acks, ospf_lsa_lock (lsa)); + continue; + } + } +#endif /* HAVE_OPAQUE_LSA */ + + /* (5) Find the instance of this LSA that is currently contained + in the router's link state database. If there is no + database copy, or the received LSA is more recent than + the database copy the following steps must be performed. */ + + if (current == NULL || + (ret = ospf_lsa_more_recent (current, lsa)) < 0) + { + /* Actual flooding procedure. */ + if (ospf_flood (nbr, current, lsa) < 0) /* Trap NSSA later. */ + DISCARD_LSA (lsa, 4); + continue; + } + + /* (6) Else, If there is an instance of the LSA on the sending + neighbor's Link state request list, an error has occurred in + the Database Exchange process. In this case, restart the + Database Exchange process by generating the neighbor event + BadLSReq for the sending neighbor and stop processing the + Link State Update packet. */ + + if (ospf_ls_request_lookup (nbr, lsa)) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + zlog_warn ("LSA instance exists on Link state request list"); + + /* Clean list of LSAs. */ + ospf_upd_list_clean (lsas); + /* this lsa is not on lsas list already. */ + ospf_lsa_discard (lsa); +#ifdef HAVE_OPAQUE_LSA + list_delete (mylsa_acks); + list_delete (mylsa_upds); +#endif /* HAVE_OPAQUE_LSA */ + return; + } + + /* If the received LSA is the same instance as the database copy + (i.e., neither one is more recent) the following two steps + should be performed: */ + + if (ret == 0) + { + /* If the LSA is listed in the Link state retransmission list + for the receiving adjacency, the router itself is expecting + an acknowledgment for this LSA. The router should treat the + received LSA as an acknowledgment by removing the LSA from + the Link state retransmission list. This is termed an + "implied acknowledgment". */ + + ls_ret = ospf_ls_retransmit_lookup (nbr, lsa); + + if (ls_ret != NULL) + { + ospf_ls_retransmit_delete (nbr, ls_ret); + + /* Delayed acknowledgment sent if advertisement received + from Designated Router, otherwise do nothing. */ + if (oi->state == ISM_Backup) + if (NBR_IS_DR (nbr)) + listnode_add (oi->ls_ack, ospf_lsa_lock (lsa)); + + DISCARD_LSA (lsa, 5); + } + else + /* Acknowledge the receipt of the LSA by sending a + Link State Acknowledgment packet back out the receiving + interface. */ + { + ospf_ls_ack_send (nbr, lsa); + DISCARD_LSA (lsa, 6); + } + } + + /* The database copy is more recent. If the database copy + has LS age equal to MaxAge and LS sequence number equal to + MaxSequenceNumber, simply discard the received LSA without + acknowledging it. (In this case, the LSA's LS sequence number is + wrapping, and the MaxSequenceNumber LSA must be completely + flushed before any new LSA instance can be introduced). */ + + else if (ret > 0) /* Database copy is more recent */ + { + if (IS_LSA_MAXAGE (current) && + current->data->ls_seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER)) + { + DISCARD_LSA (lsa, 7); + } + /* Otherwise, as long as the database copy has not been sent in a + Link State Update within the last MinLSArrival seconds, send the + database copy back to the sending neighbor, encapsulated within + a Link State Update Packet. The Link State Update Packet should + be sent directly to the neighbor. In so doing, do not put the + database copy of the LSA on the neighbor's link state + retransmission list, and do not acknowledge the received (less + recent) LSA instance. */ + else + { + struct timeval now; + + gettimeofday (&now, NULL); + + if (tv_cmp (tv_sub (now, current->tv_orig), + int2tv (OSPF_MIN_LS_ARRIVAL)) > 0) + /* Trap NSSA type later.*/ + ospf_ls_upd_send_lsa (nbr, current, OSPF_SEND_PACKET_DIRECT); + DISCARD_LSA (lsa, 8); + } + } + } + +#ifdef HAVE_OPAQUE_LSA + /* + * Now that previously originated Opaque-LSAs those which not yet + * installed into LSDB are captured, take several steps to clear + * them completely from the routing domain, before proceeding to + * origination for the current target Opaque-LSAs. + */ + while (listcount (mylsa_acks) > 0) + ospf_ls_ack_send_list (oi, mylsa_acks, nbr->address.u.prefix4); + + if (listcount (mylsa_upds) > 0) + ospf_opaque_self_originated_lsa_received (nbr, mylsa_upds); + + list_delete (mylsa_upds); +#endif /* HAVE_OPAQUE_LSA */ + + assert (listcount (lsas) == 0); + list_delete (lsas); +} + +/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */ +void +ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_neighbor *nbr; +#ifdef HAVE_OPAQUE_LSA + list opaque_acks; +#endif /* HAVE_OPAQUE_LSA */ + + /* increment statistics. */ + oi->ls_ack_in++; + + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src); + if (nbr == NULL) + { + zlog_warn ("Link State Acknowledgment: Unknown Neighbor %s.", + inet_ntoa (ospfh->router_id)); + return; + } + + if (nbr->state < NSM_Exchange) + { + zlog_warn ("Link State Acknowledgment: State is less than Exchange."); + return; + } + +#ifdef HAVE_OPAQUE_LSA + opaque_acks = list_new (); +#endif /* HAVE_OPAQUE_LSA */ + + while (size >= OSPF_LSA_HEADER_SIZE) + { + struct ospf_lsa *lsa, *lsr; + + lsa = ospf_lsa_new (); + lsa->data = (struct lsa_header *) STREAM_PNT (s); + + /* lsah = (struct lsa_header *) STREAM_PNT (s); */ + size -= OSPF_LSA_HEADER_SIZE; + stream_forward (s, OSPF_LSA_HEADER_SIZE); + + if (lsa->data->type < OSPF_MIN_LSA || lsa->data->type >= OSPF_MAX_LSA) + { + lsa->data = NULL; + ospf_lsa_discard (lsa); + continue; + } + + lsr = ospf_ls_retransmit_lookup (nbr, lsa); + + if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum) + { +#ifdef HAVE_OPAQUE_LSA + /* Keep this LSA entry for later reference. */ + if (IS_OPAQUE_LSA (lsr->data->type)) + listnode_add (opaque_acks, ospf_lsa_dup (lsr)); +#endif /* HAVE_OPAQUE_LSA */ + + ospf_ls_retransmit_delete (nbr, lsr); + } + + lsa->data = NULL; + ospf_lsa_discard (lsa); + } + +#ifdef HAVE_OPAQUE_LSA + if (listcount (opaque_acks) > 0) + ospf_opaque_ls_ack_received (nbr, opaque_acks); + + list_delete (opaque_acks); + return; +#endif /* HAVE_OPAQUE_LSA */ +} + +struct stream * +ospf_recv_packet (int fd, struct interface **ifp) +{ + int ret; + struct ip iph; + u_int16_t ip_len; + struct stream *ibuf; + unsigned int ifindex = 0; + struct iovec iov; + struct cmsghdr *cmsg; +#if defined (IP_PKTINFO) + struct in_pktinfo *pktinfo; +#elif defined (IP_RECVIF) + struct sockaddr_dl *pktinfo; +#else + char *pktinfo; /* dummy */ +#endif + char buff [sizeof (*cmsg) + sizeof (*pktinfo)]; + struct msghdr msgh = {NULL, 0, &iov, 1, buff, + sizeof (*cmsg) + sizeof (*pktinfo), 0}; + + ret = recvfrom (fd, (void *)&iph, sizeof (iph), MSG_PEEK, NULL, 0); + + if (ret != sizeof (iph)) + { + zlog_warn ("ospf_recv_packet packet smaller than ip header"); + return NULL; + } + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + ip_len = iph.ip_len; +#else + ip_len = ntohs (iph.ip_len); +#endif + +#if !defined(GNU_LINUX) + /* + * 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 + + ibuf = stream_new (ip_len); + iov.iov_base = STREAM_DATA (ibuf); + iov.iov_len = ip_len; + ret = recvmsg (fd, &msgh, 0); + + cmsg = CMSG_FIRSTHDR (&msgh); + + if (cmsg != NULL && //cmsg->cmsg_len == sizeof (*pktinfo) && + cmsg->cmsg_level == IPPROTO_IP && +#if defined (IP_PKTINFO) + cmsg->cmsg_type == IP_PKTINFO +#elif defined (IP_RECVIF) + cmsg->cmsg_type == IP_RECVIF +#else + 0 +#endif + ) + { +#if defined (IP_PKTINFO) + pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + ifindex = pktinfo->ipi_ifindex; +#elif defined (IP_RECVIF) + pktinfo = (struct sockaddr_dl *)CMSG_DATA(cmsg); + ifindex = pktinfo->sdl_index; +#else + ifindex = 0; +#endif + } + + *ifp = if_lookup_by_index (ifindex); + + if (ret != ip_len) + { + zlog_warn ("ospf_recv_packet short read. " + "ip_len %d bytes read %d", ip_len, ret); + stream_free (ibuf); + return NULL; + } + + return ibuf; +} + +struct ospf_interface * +ospf_associate_packet_vl (struct interface *ifp, struct ospf_interface *oi, + struct ip *iph, struct ospf_header *ospfh) +{ + struct ospf_interface *rcv_oi; + listnode node; + struct ospf_vl_data *vl_data; + struct ospf_area *vl_area; + + if (IN_MULTICAST (ntohl (iph->ip_dst.s_addr)) || + !OSPF_IS_AREA_BACKBONE (ospfh)) + return oi; + + if ((rcv_oi = oi) == NULL) + { + if ((rcv_oi = ospf_if_lookup_by_local_addr (ifp, iph->ip_dst)) == NULL) + return NULL; + } + + for (node = listhead (ospf_top->vlinks); node; nextnode (node)) + { + if ((vl_data = getdata (node)) == NULL) + continue; + + vl_area = ospf_area_lookup_by_area_id (vl_data->vl_area_id); + if (!vl_area) + continue; + + if (OSPF_AREA_SAME (&vl_area, &rcv_oi->area) && + IPV4_ADDR_SAME (&vl_data->vl_peer, &ospfh->router_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("associating packet with %s", + IF_NAME (vl_data->vl_oi)); + if (! CHECK_FLAG (vl_data->vl_oi->ifp->flags, IFF_UP)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("This VL is not up yet, sorry"); + return NULL; + } + + return vl_data->vl_oi; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("couldn't find any VL to associate the packet with"); + + return oi; +} + +int +ospf_check_area_id (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + /* Check match the Area ID of the receiving interface. */ + if (OSPF_AREA_SAME (&oi->area, &ospfh)) + 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. */ +int +ospf_check_network_mask (struct ospf_interface *oi, struct in_addr ip_src) +{ + struct in_addr mask, me, him; + + if (oi->type == OSPF_IFTYPE_POINTOPOINT || + oi->type == OSPF_IFTYPE_VIRTUALLINK) + return 1; + + masklen2ip (oi->address->prefixlen, &mask); + + me.s_addr = oi->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 +ospf_check_auth (struct ospf_interface *oi, struct stream *ibuf, + struct ospf_header *ospfh) +{ + int ret = 0; + struct crypt_key *ck; + + switch (ntohs (ospfh->auth_type)) + { + case OSPF_AUTH_NULL: + ret = 1; + break; + case OSPF_AUTH_SIMPLE: + if (!memcmp (OSPF_IF_PARAM (oi, auth_simple), ospfh->u.auth_data, OSPF_AUTH_SIMPLE_SIZE)) + ret = 1; + else + ret = 0; + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + if ((ck = getdata (OSPF_IF_PARAM (oi,auth_crypt)->tail)) == NULL) + { + ret = 0; + break; + } + + /* This is very basic, the digest processing is elsewhere */ + if (ospfh->u.crypt.auth_data_len == OSPF_AUTH_MD5_SIZE && + ospfh->u.crypt.key_id == ck->key_id && + ntohs (ospfh->length) + OSPF_AUTH_SIMPLE_SIZE <= stream_get_size (ibuf)) + ret = 1; + else + ret = 0; + break; + default: + ret = 0; + break; + } + + return ret; +} + +int +ospf_check_sum (struct ospf_header *ospfh) +{ + u_int32_t ret; + u_int16_t sum; + int in_cksum (void *ptr, int nbytes); + + /* clear auth_data for checksum. */ + memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + /* keep checksum and clear. */ + sum = ospfh->checksum; + memset (&ospfh->checksum, 0, sizeof (u_int16_t)); + + /* calculate checksum. */ + ret = in_cksum (ospfh, ntohs (ospfh->length)); + + if (ret != sum) + { + zlog_info ("ospf_check_sum(): checksum mismatch, my %X, his %X", + ret, sum); + return 0; + } + + return 1; +} + +/* OSPF Header verification. */ +int +ospf_verify_header (struct stream *ibuf, struct ospf_interface *oi, + struct ip *iph, struct ospf_header *ospfh) +{ + /* check version. */ + if (ospfh->version != OSPF_VERSION) + { + zlog_warn ("interface %s: ospf_read version number mismatch.", + IF_NAME (oi)); + return -1; + } + + /* Check Area ID. */ + if (!ospf_check_area_id (oi, ospfh)) + { + zlog_warn ("interface %s: ospf_read invalid Area ID %s.", + IF_NAME (oi), inet_ntoa (ospfh->area_id)); + return -1; + } + + /* Check network mask, Silently discarded. */ + if (! ospf_check_network_mask (oi, iph->ip_src)) + { + zlog_warn ("interface %s: ospf_read network address is not same [%s]", + IF_NAME (oi), inet_ntoa (iph->ip_src)); + return -1; + } + + /* Check authentication. */ + if (ospf_auth_type (oi) != ntohs (ospfh->auth_type)) + { + zlog_warn ("interface %s: ospf_read authentication type mismatch.", + IF_NAME (oi)); + return -1; + } + + if (! ospf_check_auth (oi, ibuf, ospfh)) + { + zlog_warn ("interface %s: ospf_read authentication failed.", + IF_NAME (oi)); + return -1; + } + + /* if check sum is invalid, packet is discarded. */ + if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + { + if (! ospf_check_sum (ospfh)) + { + zlog_warn ("interface %s: ospf_read packet checksum error %s", + IF_NAME (oi), inet_ntoa (ospfh->router_id)); + return -1; + } + } + else + { + if (ospfh->checksum != 0) + return -1; + if (ospf_check_md5_digest (oi, ibuf, ntohs (ospfh->length)) == 0) + { + zlog_warn ("interface %s: ospf_read md5 authentication failed.", + IF_NAME (oi)); + return -1; + } + } + + return 0; +} + +/* Starting point of packet process function. */ +int +ospf_read (struct thread *thread) +{ + int ret; + struct stream *ibuf; + struct ospf *top; + struct ospf_interface *oi; + struct ip *iph; + struct ospf_header *ospfh; + u_int16_t length; + struct interface *ifp; + + /* first of all get interface pointer. */ + top = THREAD_ARG (thread); + top->t_read = NULL; + + /* read OSPF packet. */ + ibuf = ospf_recv_packet (top->fd, &ifp); + if (ibuf == NULL) + return -1; + + iph = (struct ip *) STREAM_DATA (ibuf); + + /* prepare for next packet. */ + top->t_read = thread_add_read (master, ospf_read, top, top->fd); + + /* IP Header dump. */ + /* + if (ospf_debug_packet & OSPF_DEBUG_RECV) + ospf_ip_header_dump (ibuf); + */ + /* Self-originated packet should be discarded silently. */ + if (ospf_if_lookup_by_local_addr (NULL, iph->ip_src)) + { + stream_free (ibuf); + return 0; + } + + /* Adjust size to message length. */ + stream_forward (ibuf, iph->ip_hl * 4); + + /* Get ospf packet header. */ + ospfh = (struct ospf_header *) STREAM_PNT (ibuf); + + /* associate packet with ospf interface */ + oi = ospf_if_lookup_recv_interface (iph->ip_src); + if (ifp && oi && oi->ifp != ifp) + { + zlog_warn ("Packet from [%s] received on wrong link %s", + inet_ntoa (iph->ip_src), ifp->name); + stream_free (ibuf); + return 0; + } + + if ((oi = ospf_associate_packet_vl (ifp, oi, iph, ospfh)) == NULL) + { + stream_free (ibuf); + return 0; + } + + /* + * If the received packet is destined for AllDRouters, the packet + * should be accepted only if the received ospf interface state is + * either DR or Backup -- endo. + */ + if (iph->ip_dst.s_addr == htonl (OSPF_ALLDROUTERS) + && (oi->state != ISM_DR && oi->state != ISM_Backup)) + { + zlog_info ("Packet for AllDRouters from [%s] via [%s] (ISM: %s)", + inet_ntoa (iph->ip_src), IF_NAME (oi), + LOOKUP (ospf_ism_state_msg, oi->state)); + stream_free (ibuf); + return 0; + } + + /* Show debug receiving packet. */ + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL)) + { + zlog_info ("-----------------------------------------------------"); + ospf_packet_dump (ibuf); + } + + zlog_info ("%s received from [%s] via [%s]", + ospf_packet_type_str[ospfh->type], + inet_ntoa (ospfh->router_id), IF_NAME (oi)); + zlog_info (" src [%s],", inet_ntoa (iph->ip_src)); + zlog_info (" dst [%s]", inet_ntoa (iph->ip_dst)); + + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL)) + zlog_info ("-----------------------------------------------------"); + } + + /* Some header verification. */ + ret = ospf_verify_header (ibuf, oi, iph, ospfh); + if (ret < 0) + { + stream_free (ibuf); + return ret; + } + + stream_forward (ibuf, OSPF_HEADER_SIZE); + + /* Adjust size to message length. */ + length = ntohs (ospfh->length) - OSPF_HEADER_SIZE; + + /* Read rest of the packet and call each sort of packet routine. */ + switch (ospfh->type) + { + case OSPF_MSG_HELLO: + ospf_hello (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_DB_DESC: + ospf_db_desc (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_REQ: + ospf_ls_req (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_UPD: + ospf_ls_upd (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_ACK: + ospf_ls_ack (iph, ospfh, ibuf, oi, length); + break; + default: + zlog (NULL, LOG_WARNING, + "interface %s: OSPF packet header type %d is illegal", + IF_NAME (oi), ospfh->type); + break; + } + + stream_free (ibuf); + return 0; +} + +/* Make OSPF header. */ +void +ospf_make_header (int type, struct ospf_interface *oi, struct stream *s) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *) STREAM_DATA (s); + + ospfh->version = (u_char) OSPF_VERSION; + ospfh->type = (u_char) type; + + ospfh->router_id = ospf_top->router_id; + + ospfh->checksum = 0; + ospfh->area_id = oi->area->area_id; + ospfh->auth_type = htons (ospf_auth_type (oi)); + + memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + ospf_output_forward (s, OSPF_HEADER_SIZE); +} + +/* Make Authentication Data. */ +int +ospf_make_auth (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + struct crypt_key *ck; + + switch (ospf_auth_type (oi)) + { + case OSPF_AUTH_NULL: + /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */ + break; + case OSPF_AUTH_SIMPLE: + memcpy (ospfh->u.auth_data, OSPF_IF_PARAM (oi, auth_simple), + OSPF_AUTH_SIMPLE_SIZE); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + /* If key is not set, then set 0. */ + if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + else + { + ck = getdata (OSPF_IF_PARAM (oi, auth_crypt)->tail); + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = ck->key_id; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + /* note: the seq is done in ospf_make_md5_digest() */ + break; + default: + /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */ + break; + } + + return 0; +} + +/* Fill rest of OSPF header. */ +void +ospf_fill_header (struct ospf_interface *oi, + struct stream *s, u_int16_t length) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *) STREAM_DATA (s); + + /* Fill length. */ + ospfh->length = htons (length); + + /* Calculate checksum. */ + if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + ospfh->checksum = in_cksum (ospfh, length); + else + ospfh->checksum = 0; + + /* Add Authentication Data. */ + ospf_make_auth (oi, ospfh); +} + +int +ospf_make_hello (struct ospf_interface *oi, struct stream *s) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + u_int16_t length = OSPF_HELLO_MIN_SIZE; + struct in_addr mask; + unsigned long p; + int flag = 0; + + /* Set netmask of interface. */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT && + oi->type != OSPF_IFTYPE_VIRTUALLINK) + masklen2ip (oi->address->prefixlen, &mask); + else + memset ((char *) &mask, 0, sizeof (struct in_addr)); + stream_put_ipv4 (s, mask.s_addr); + + /* Set Hello Interval. */ + stream_putw (s, OSPF_IF_PARAM (oi, v_hello)); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("make_hello: options: %x, int: %s", + OPTIONS(oi), IF_NAME (oi)); + + /* Set Options. */ + stream_putc (s, OPTIONS (oi)); + + /* Set Router Priority. */ + stream_putc (s, PRIORITY (oi)); + + /* Set Router Dead Interval. */ + stream_putl (s, OSPF_IF_PARAM (oi, v_wait)); + + /* Set Designated Router. */ + stream_put_ipv4 (s, DR (oi).s_addr); + + p = s->putp; + + /* Set Backup Designated Router. */ + stream_put_ipv4 (s, BDR (oi).s_addr); + + /* Add neighbor seen. */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + /* ignore 0.0.0.0 node. */ + if (nbr->router_id.s_addr != 0) + if (nbr->state != NSM_Attempt) + /* ignore Down neighbor. */ + if (nbr->state != NSM_Down) + /* this is myself for DR election. */ + if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf_top->router_id)) + { + /* Check neighbor is sane? */ + if (nbr->d_router.s_addr != 0 && + IPV4_ADDR_SAME (&nbr->d_router, &oi->address->u.prefix4) && + IPV4_ADDR_SAME (&nbr->bd_router, &oi->address->u.prefix4)) + flag = 1; + + stream_put_ipv4 (s, nbr->router_id.s_addr); + length += 4; + } + + /* Let neighbor generate BackupSeen. */ + if (flag == 1) + { + stream_set_putp (s, p); + stream_put_ipv4 (s, 0); + } + + return length; +} + +int +ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, + struct stream *s) +{ + struct ospf_lsa *lsa; + u_int16_t length = OSPF_DB_DESC_MIN_SIZE; + u_char options; + unsigned long pp; + int i; + struct ospf_lsdb *lsdb; + + /* Set Interface MTU. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + stream_putw (s, 0); + else + stream_putw (s, oi->ifp->mtu); + + /* Set Options. */ + options = OPTIONS (oi); +#ifdef HAVE_OPAQUE_LSA + if (CHECK_FLAG (ospf_top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_SET_DD_I (nbr->dd_flags) + || CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + /* + * Set O-bit in the outgoing DD packet for capablity negotiation, + * if one of following case is applicable. + * + * 1) WaitTimer expiration event triggered the neighbor state to + * change to Exstart, but no (valid) DD packet has received + * from the neighbor yet. + * + * 2) At least one DD packet with O-bit on has received from the + * neighbor. + */ + SET_FLAG (options, OSPF_OPTION_O); + } +#endif /* HAVE_OPAQUE_LSA */ + stream_putc (s, options); + + /* Keep pointer to flags. */ + pp = stream_get_putp (s); + stream_putc (s, nbr->dd_flags); + + /* Set DD Sequence Number. */ + stream_putl (s, nbr->dd_seqnum); + + if (ospf_db_summary_isempty (nbr)) + { + if (nbr->state >= NSM_Exchange) + { + nbr->dd_flags &= ~OSPF_DD_FLAG_M; + /* Set DD flags again */ + stream_set_putp (s, pp); + stream_putc (s, nbr->dd_flags); + } + return length; + } + + /* Describe LSA Header from Database Summary List. */ + lsdb = &nbr->db_sum; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = rn->info) != NULL) + { +#ifdef HAVE_OPAQUE_LSA + if (IS_OPAQUE_LSA (lsa->data->type) + && (! CHECK_FLAG (options, OSPF_OPTION_O))) + { + /* Suppress advertising opaque-informations. */ + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete (lsdb, lsa); + continue; + } +#endif /* HAVE_OPAQUE_LSA */ + + if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD)) + { + struct lsa_header *lsah; + u_int16_t ls_age; + + /* DD packet overflows interface MTU. */ + if (length + OSPF_LSA_HEADER_SIZE > OSPF_PACKET_MAX (oi)) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header *) (STREAM_DATA (s) + + stream_get_putp (s)); + + /* Proceed stream pointer. */ + stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + /* Set LS age. */ + ls_age = LS_AGE (lsa); + lsah->ls_age = htons (ls_age); + + } + + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete (lsdb, lsa); + } + } + + return length; +} + +int +ospf_make_ls_req_func (struct stream *s, u_int16_t *length, + unsigned long delta, struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* LS Request packet overflows interface MTU. */ + if (*length + delta > OSPF_PACKET_MAX(oi)) + return 0; + + stream_putl (s, lsa->data->type); + stream_put_ipv4 (s, lsa->data->id.s_addr); + stream_put_ipv4 (s, lsa->data->adv_router.s_addr); + + ospf_lsa_unlock (nbr->ls_req_last); + nbr->ls_req_last = ospf_lsa_lock (lsa); + + *length += 12; + return 1; +} + +int +ospf_make_ls_req (struct ospf_neighbor *nbr, struct stream *s) +{ + struct ospf_lsa *lsa; + u_int16_t length = OSPF_LS_REQ_MIN_SIZE; + unsigned long delta = stream_get_putp(s)+12; + struct route_table *table; + struct route_node *rn; + int i; + struct ospf_lsdb *lsdb; + + lsdb = &nbr->ls_req; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + table = lsdb->type[i].db; + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = (rn->info)) != NULL) + if (ospf_make_ls_req_func (s, &length, delta, nbr, lsa) == 0) + { + route_unlock_node (rn); + break; + } + } + return length; +} + +int +ls_age_increment (struct ospf_lsa *lsa, int delay) +{ + int age; + + age = IS_LSA_MAXAGE (lsa) ? OSPF_LSA_MAXAGE : LS_AGE (lsa) + delay; + + return (age > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : age); +} + +int +ospf_make_ls_upd (struct ospf_interface *oi, list update, struct stream *s) +{ + struct ospf_lsa *lsa; + listnode node; + u_int16_t length = OSPF_LS_UPD_MIN_SIZE; + unsigned long delta = stream_get_putp (s); + unsigned long pp; + int count = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info("ospf_make_ls_upd: Start"); + + pp = stream_get_putp (s); + ospf_output_forward (s, 4); + + while ((node = listhead (update)) != NULL) + { + struct lsa_header *lsah; + u_int16_t ls_age; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info("ospf_make_ls_upd: List Iteration"); + + lsa = getdata (node); + assert (lsa); + assert (lsa->data); + + /* Check packet size. */ + if (length + delta + ntohs (lsa->data->length) > OSPF_PACKET_MAX (oi)) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header *) (STREAM_DATA (s) + stream_get_putp (s)); + + /* Put LSA to Link State Request. */ + stream_put (s, lsa->data, ntohs (lsa->data->length)); + + /* Set LS age. */ + /* each hop must increment an lsa_age by transmit_delay + of OSPF interface */ + ls_age = ls_age_increment (lsa, OSPF_IF_PARAM (oi, transmit_delay)); + lsah->ls_age = htons (ls_age); + + length += ntohs (lsa->data->length); + count++; + + list_delete_node (update, node); + ospf_lsa_unlock (lsa); + } + + /* Now set #LSAs. */ + stream_set_putp (s, pp); + stream_putl (s, count); + + stream_set_putp (s, s->endp); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info("ospf_make_ls_upd: Stop"); + return length; +} + +int +ospf_make_ls_ack (struct ospf_interface *oi, list ack, struct stream *s) +{ + list rm_list; + listnode node; + u_int16_t length = OSPF_LS_ACK_MIN_SIZE; + unsigned long delta = stream_get_putp(s) + 24; + struct ospf_lsa *lsa; + + rm_list = list_new (); + + for (node = listhead (ack); node; nextnode (node)) + { + lsa = getdata (node); + assert (lsa); + + if (length + delta > OSPF_PACKET_MAX (oi)) + break; + + stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + listnode_add (rm_list, lsa); + } + + /* Remove LSA from LS-Ack list. */ + for (node = listhead (rm_list); node; nextnode (node)) + { + lsa = (struct ospf_lsa *) getdata (node); + + listnode_delete (ack, lsa); + ospf_lsa_unlock (lsa); + } + + list_delete (rm_list); + + return length; +} + +void +ospf_hello_send_sub (struct ospf_interface *oi, struct in_addr *addr) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_HELLO, oi, op->s); + + /* Prepare OSPF Hello body. */ + length += ospf_make_hello (oi, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + op->dst.s_addr = addr->s_addr; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); +} + +void +ospf_poll_send (struct ospf_nbr_nbma *nbr_nbma) +{ + struct ospf_interface *oi; + + oi = nbr_nbma->oi; + assert(oi); + + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PARAM (oi, passive_interface) == OSPF_IF_PASSIVE) + return; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down) + return; + + if (PRIORITY(oi) == 0) + return; + + if (nbr_nbma->priority == 0 + && oi->state != ISM_DR && oi->state != ISM_Backup) + return; + + ospf_hello_send_sub (oi, &nbr_nbma->addr); +} + +int +ospf_poll_timer (struct thread *thread) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = THREAD_ARG (thread); + nbr_nbma->t_poll = NULL; + + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + zlog (NULL, LOG_INFO, "NSM[%s:%s]: Timer (Poll timer expire)", + IF_NAME (nbr_nbma->oi), inet_ntoa (nbr_nbma->addr)); + + ospf_poll_send (nbr_nbma); + + if (nbr_nbma->v_poll > 0) + OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + + return 0; +} + + +int +ospf_hello_reply_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_hello_reply = NULL; + + assert (nbr->oi); + + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + zlog (NULL, LOG_INFO, "NSM[%s:%s]: Timer (hello-reply timer expire)", + IF_NAME (nbr->oi), inet_ntoa (nbr->router_id)); + + ospf_hello_send_sub (nbr->oi, &nbr->address.u.prefix4); + + return 0; +} + +/* Send OSPF Hello. */ +void +ospf_hello_send (struct ospf_interface *oi) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PARAM (oi, passive_interface) == OSPF_IF_PASSIVE) + return; + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_HELLO, oi, op->s); + + /* Prepare OSPF Hello body. */ + length += ospf_make_hello (oi, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (nbr != oi->nbr_self) + if (nbr->state != NSM_Down) + { + /* RFC 2328 Section 9.5.1 + If the router is not eligible to become Designated Router, + it must periodically send Hello Packets to both the + Designated Router and the Backup Designated Router (if they + exist). */ + if (PRIORITY(oi) == 0 && + IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) && + IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4)) + continue; + + /* If the router is eligible to become Designated Router, it + must periodically send Hello Packets to all neighbors that + are also eligible. In addition, if the router is itself the + Designated Router or Backup Designated Router, it must also + send periodic Hello Packets to all other neighbors. */ + + if (nbr->priority == 0 && oi->state == ISM_DROther) + continue; + /* if oi->state == Waiting, send hello to all neighbors */ + { + struct ospf_packet *op_dup; + + op_dup = ospf_packet_dup(op); + op_dup->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op_dup); + + OSPF_ISM_WRITE_ON (); + } + + } + ospf_packet_free (op); + } + else + { + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + op->dst.s_addr = oi->vl_data->peer_addr.s_addr; + else + op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); + } +} + +/* Send OSPF Database Description. */ +void +ospf_db_desc_send (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_DB_DESC, oi, op->s); + + /* Prepare OSPF Database Description body. */ + length += ospf_make_db_desc (oi, nbr, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); + + /* Remove old DD packet, then copy new one and keep in neighbor structure. */ + if (nbr->last_send) + ospf_packet_free (nbr->last_send); + nbr->last_send = ospf_packet_dup (op); + gettimeofday (&nbr->last_send_ts, NULL); +} + +/* Re-send Database Description. */ +void +ospf_db_desc_resend (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, ospf_packet_dup (nbr->last_send)); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); +} + +/* Send Link State Request. */ +void +ospf_ls_req_send (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_REQ, oi, op->s); + + /* Prepare OSPF Link State Request body. */ + length += ospf_make_ls_req (nbr, op->s); + if (length == OSPF_HEADER_SIZE) + { + ospf_packet_free (op); + return; + } + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); + + /* Add Link State Request Retransmission Timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); +} + +/* Send Link State Update with an LSA. */ +void +ospf_ls_upd_send_lsa (struct ospf_neighbor *nbr, struct ospf_lsa *lsa, + int flag) +{ + list update; + + update = list_new (); + + listnode_add (update, lsa); + ospf_ls_upd_send (nbr, update, flag); + + list_delete (update); +} + +static void +ospf_ls_upd_queue_send (struct ospf_interface *oi, list update, + struct in_addr addr) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("listcount = %d, dst %s", listcount (update), inet_ntoa(addr)); + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_UPD, oi, op->s); + + /* Prepare OSPF Link State Update body. */ + /* Includes Type-7 translation. */ + length += ospf_make_ls_upd (oi, update, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + op->dst.s_addr = addr.s_addr; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); +} + +static int +ospf_ls_upd_send_queue_event (struct thread *thread) +{ + struct ospf_interface *oi = THREAD_ARG(thread); + struct route_node *rn; + + oi->t_ls_upd_event = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_ls_upd_send_queue start"); + + for (rn = route_top (oi->ls_upd_queue); rn; rn = route_next (rn)) + { + if (rn->info == NULL) + continue; + + while (!list_isempty ((list)rn->info)) + ospf_ls_upd_queue_send (oi, rn->info, rn->p.u.prefix4); + + list_delete (rn->info); + rn->info = NULL; + + route_unlock_node (rn); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("ospf_ls_upd_send_queue stop"); + return 0; +} + +void +ospf_ls_upd_send (struct ospf_neighbor *nbr, list update, int flag) +{ + struct ospf_interface *oi; + struct prefix_ipv4 p; + struct route_node *rn; + listnode n; + + oi = nbr->oi; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + p.prefix = oi->vl_data->peer_addr; + else if (flag == OSPF_SEND_PACKET_DIRECT) + p.prefix = nbr->address.u.prefix4; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if ((oi->type == OSPF_IFTYPE_POINTOPOINT) + && (flag == OSPF_SEND_PACKET_INDIRECT)) + p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + p.prefix.s_addr = htonl (OSPF_ALLDROUTERS); + + if (oi->type == OSPF_IFTYPE_NBMA) + { + if (flag == OSPF_SEND_PACKET_INDIRECT) + zlog_warn ("* LS-Update is directly sent on NBMA network."); + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix.s_addr)) + zlog_warn ("* LS-Update is sent to myself."); + } + + rn = route_node_get (oi->ls_upd_queue, (struct prefix *) &p); + + if (rn->info == NULL) + rn->info = list_new (); + + for (n = listhead (update); n; nextnode (n)) + listnode_add (rn->info, ospf_lsa_lock (getdata (n))); + + if (oi->t_ls_upd_event == NULL) + oi->t_ls_upd_event = + thread_add_event (master, ospf_ls_upd_send_queue_event, oi, 0); +} + +static void +ospf_ls_ack_send_list (struct ospf_interface *oi, list ack, struct in_addr dst) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_ACK, oi, op->s); + + /* Prepare OSPF Link State Acknowledgment body. */ + length += ospf_make_ls_ack (oi, ack, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Set destination IP address. */ + op->dst = dst; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (); +} + +static int +ospf_ls_ack_send_event (struct thread *thread) +{ + struct ospf_interface *oi = THREAD_ARG (thread); + + oi->t_ls_ack_direct = NULL; + + while (listcount (oi->ls_ack_direct.ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack_direct.ls_ack, + oi->ls_ack_direct.dst); + + return 0; +} + +void +ospf_ls_ack_send (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + struct ospf_interface *oi = nbr->oi; + + if (listcount (oi->ls_ack_direct.ls_ack) == 0) + oi->ls_ack_direct.dst = nbr->address.u.prefix4; + + listnode_add (oi->ls_ack_direct.ls_ack, ospf_lsa_lock (lsa)); + + if (oi->t_ls_ack_direct == NULL) + oi->t_ls_ack_direct = + thread_add_event (master, ospf_ls_ack_send_event, oi, 0); +} + +/* Send Link State Acknowledgment delayed. */ +void +ospf_ls_ack_send_delayed (struct ospf_interface *oi) +{ + struct in_addr dst; + + /* Decide destination address. */ + /* RFC2328 Section 13.5 On non-broadcast + networks, delayed Link State Acknowledgment packets must be + unicast separately over each adjacency (i.e., neighbor whose + state is >= Exchange). */ + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange) + while (listcount (oi->ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack, nbr->address.u.prefix4); + return; + } + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + dst.s_addr = oi->vl_data->peer_addr.s_addr; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + dst.s_addr = htonl (OSPF_ALLDROUTERS); + + while (listcount (oi->ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack, dst); +} |