/* * Copyright (C) 1999 Yasuhiro Ohara * * 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 "ospf6d.h" /* check validity and put lsa in reqestlist if needed. */ /* returns -1 if SeqNumMismatch required. */ int ospf6_dbex_check_dbdesc_lsa_header (struct ospf6_lsa_header *lsa_header, struct ospf6_neighbor *from) { struct ospf6_lsa *received = NULL; struct ospf6_lsa *have = NULL; received = ospf6_lsa_summary_create ((struct ospf6_lsa_header__ *) lsa_header); /* case when received is AS-External though neighbor belongs stub area */ if (lsa_header->type == htons (OSPF6_LSA_TYPE_AS_EXTERNAL) && ospf6_area_is_stub (from->ospf6_interface->area)) { zlog_err ("DbDesc %s receive from %s", from->str, received->str); zlog_err (" E-bit mismatch: %s", received->str); ospf6_lsa_delete (received); return -1; } /* if already have newer database copy, check next LSA */ have = ospf6_lsdb_lookup (lsa_header->type, lsa_header->ls_id, lsa_header->advrtr, ospf6_lsa_get_scope (lsa_header->type, from->ospf6_interface)); if (! have) { /* if we don't have database copy, add request */ if (IS_OSPF6_DUMP_DBEX) zlog_info ("Have no database copy, Request"); ospf6_neighbor_request_add (received, from); } else if (have) { /* if database copy is less recent, add request */ if (ospf6_lsa_check_recent (received, have) < 0) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("Database copy less recent, Request"); ospf6_neighbor_request_add (received, from); } } return 0; } /* Direct acknowledgement */ static void ospf6_dbex_acknowledge_direct (struct ospf6_lsa *lsa, struct ospf6_neighbor *o6n) { struct iovec directack[MAXIOVLIST]; assert (lsa); if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: [%s:%s] direct ack %s ", o6n->str, o6n->ospf6_interface->interface->name, lsa->str); /* clear pointers to fragments of packet for direct acknowledgement */ iov_clear (directack, MAXIOVLIST); /* set pointer of LSA to send */ OSPF6_MESSAGE_ATTACH (directack, lsa->header, sizeof (struct ospf6_lsa_header)); /* age update and add InfTransDelay */ ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay); /* send unicast packet to neighbor's ipaddress */ ospf6_message_send (OSPF6_MESSAGE_TYPE_LSACK, directack, &o6n->hisaddr, o6n->ospf6_interface->if_id); } /* Delayed acknowledgement */ void ospf6_dbex_acknowledge_delayed (struct ospf6_lsa *lsa, struct ospf6_interface *o6i) { assert (o6i); if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: [%s] delayed ack %s", o6i->interface->name, lsa->str); /* attach delayed acknowledge list */ ospf6_lsa_age_current (lsa); ospf6_interface_delayed_ack_add (lsa, o6i); /* if not yet, schedule delayed acknowledge RxmtInterval later. timers should be *less than* RxmtInterval or needless retrans will ensue */ if (o6i->thread_send_lsack_delayed == NULL) o6i->thread_send_lsack_delayed = thread_add_timer (master, ospf6_send_lsack_delayed, o6i, o6i->rxmt_interval - 1); return; } /* RFC2328 section 13 (4): if MaxAge LSA and if we have no instance, and no neighbor is in states Exchange or Loading */ /* returns 1 if match this case, else returns 0 */ static int ospf6_dbex_is_maxage_to_be_dropped (struct ospf6_lsa *received, struct ospf6_neighbor *from) { int count; if (! IS_LSA_MAXAGE (received)) return 0; if (ospf6_lsdb_lookup (received->header->type, received->header->id, received->header->adv_router, ospf6_lsa_get_scope (received->header->type, from->ospf6_interface))) return 0; if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (received->header->type))) { count = 0; (*from->ospf6_interface->foreach_nei) (from->ospf6_interface, &count, NBS_EXCHANGE, ospf6_count_state); (*from->ospf6_interface->foreach_nei) (from->ospf6_interface, &count, NBS_LOADING, ospf6_count_state); if (count) return 0; } else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (received->header->type))) { count = 0; (*from->ospf6_interface->area->foreach_nei) (from->ospf6_interface->area, &count, NBS_EXCHANGE, ospf6_count_state); (*from->ospf6_interface->area->foreach_nei) (from->ospf6_interface->area, &count, NBS_LOADING, ospf6_count_state); if (count) return 0; } else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (received->header->type))) { count = 0; (*from->ospf6_interface->area->ospf6->foreach_nei) (from->ospf6_interface->area->ospf6, &count, NBS_EXCHANGE, ospf6_count_state); (*from->ospf6_interface->area->ospf6->foreach_nei) (from->ospf6_interface->area->ospf6, &count, NBS_LOADING, ospf6_count_state); if (count) return 0; } return 1; } static void ospf6_dbex_remove_retrans (void *arg, int val, void *obj) { struct ospf6_lsa *rem; struct ospf6_neighbor *nei = (struct ospf6_neighbor *) obj; struct ospf6_lsa *lsa = (struct ospf6_lsa *) arg; rem = ospf6_lsdb_lookup_lsdb (lsa->header->type, lsa->header->id, lsa->header->adv_router, nei->retrans_list); if (rem) { ospf6_neighbor_retrans_remove (rem, nei); ospf6_maxage_remover (); } } void ospf6_dbex_remove_from_all_retrans_list (struct ospf6_lsa *lsa) { struct ospf6_interface *o6i; struct ospf6_area *o6a; if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (htons (lsa->header->type))) { o6i = lsa->scope; (*o6i->foreach_nei) (o6i, lsa, 0, ospf6_dbex_remove_retrans); } else if (OSPF6_LSA_IS_SCOPE_AREA (htons (lsa->header->type))) { o6a = lsa->scope; (*o6a->foreach_nei) (o6a, lsa, 0, ospf6_dbex_remove_retrans); } else if (OSPF6_LSA_IS_SCOPE_AS (htons (lsa->header->type))) { (*ospf6->foreach_nei) (ospf6, lsa, 0, ospf6_dbex_remove_retrans); } } /* RFC2328 section 13 */ void ospf6_dbex_receive_lsa (struct ospf6_lsa_header *lsa_header, struct ospf6_neighbor *from) { struct ospf6_lsa *received, *have, *rem; struct timeval now; int ismore_recent, acktype; unsigned short cksum; struct ospf6_lsa_slot *slot; received = have = (struct ospf6_lsa *)NULL; ismore_recent = -1; recent_reason = "no instance"; zlog_info ("Receive LSA (header -> %p)", lsa_header); /* make lsa structure for received lsa */ received = ospf6_lsa_create (lsa_header); /* set LSA scope */ if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (htons (lsa_header->type))) received->scope = from->ospf6_interface; else if (OSPF6_LSA_IS_SCOPE_AREA (htons (lsa_header->type))) received->scope = from->ospf6_interface->area; else if (OSPF6_LSA_IS_SCOPE_AS (htons (lsa_header->type))) received->scope = from->ospf6_interface->area->ospf6; /* (1) LSA Checksum */ cksum = ntohs (lsa_header->checksum); if (ntohs (ospf6_lsa_checksum (lsa_header)) != cksum) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: received %s from %s%%%s" ": wrong checksum, drop", received->str, from->str, from->ospf6_interface->interface->name); ospf6_lsa_delete (received); return; } /* (3) Ebit Missmatch: AS-External-LSA */ if (lsa_header->type == htons (OSPF6_LSA_TYPE_AS_EXTERNAL) && ospf6_area_is_stub (from->ospf6_interface->area)) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: received %s from %s%%%s" ": E-bit mismatch, drop", received->str, from->str, from->ospf6_interface->interface->name); ospf6_lsa_delete (received); return; } /* (4) if MaxAge LSA and if we have no instance, and no neighbor is in states Exchange or Loading */ if (ospf6_dbex_is_maxage_to_be_dropped (received, from)) { /* log */ if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: received %s from %s%%%s" ": MaxAge, no instance, no neighbor exchange, drop", received->str, from->str, from->ospf6_interface->interface->name); /* a) Acknowledge back to neighbor (13.5) */ /* Direct Acknowledgement */ ospf6_dbex_acknowledge_direct (received, from); /* b) Discard */ ospf6_lsa_delete (received); return; } /* (5) */ /* lookup the same database copy in lsdb */ have = ospf6_lsdb_lookup (lsa_header->type, lsa_header->ls_id, lsa_header->advrtr, ospf6_lsa_get_scope (lsa_header->type, from->ospf6_interface)); if (have) { ismore_recent = ospf6_lsa_check_recent (received, have); if (ntohl (received->header->seqnum) == ntohl (have->header->seqnum)) SET_FLAG (received->flag, OSPF6_LSA_FLAG_DUPLICATE); } /* if no database copy or received is more recent */ if (!have || ismore_recent < 0) { /* in case we have no database copy */ ismore_recent = -1; /* (a) MinLSArrival check */ gettimeofday (&now, (struct timezone *)NULL); if (have && SEC_TVDIFF (&now, &have->installed) < OSPF6_MIN_LS_ARRIVAL) { //if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Receive new LSA from %s: %s seq: %#x age: %d " "within MinLSArrival, drop: %ld.%06ld", from->str, received->str, ntohl (received->header->seqnum), ntohs (received->header->age), now.tv_sec, now.tv_usec); /* this will do free this lsa */ ospf6_lsa_delete (received); return; /* examin next lsa */ } //if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Receive new LSA from %s: %s seq: %#x age: %d: " "%ld.%06ld", from->str, received->str, ntohl (received->header->seqnum), ntohs (received->header->age), now.tv_sec, now.tv_usec); /* (b) immediately flood */ ospf6_dbex_flood (received, from); #if 0 /* Because New LSDB do not permit two LSA having the same identifier exist in a LSDB list, above ospf6_dbex_flood() will remove the old instance automatically. thus bellow is not needed. */ /* (c) remove database copy from all neighbor's retranslist */ if (have) ospf6_dbex_remove_from_all_retrans_list (have); #endif /* (d), installing lsdb, which may cause routing table calculation (replacing database copy) */ ospf6_lsdb_install (received); /* (e) possibly acknowledge */ acktype = ack_type (received, ismore_recent, from); if (acktype == DIRECT_ACK) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Direct Ack to %s", from->str); ospf6_dbex_acknowledge_direct (received, from); } else if (acktype == DELAYED_ACK) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Delayed Ack to %s", from->str); ospf6_dbex_acknowledge_delayed (received, from->ospf6_interface); } else { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: No Ack to %s", from->str); } /* (f) */ /* Self Originated LSA, section 13.4 */ if (received->lsa_hdr->lsh_advrtr == ospf6->router_id && (! have || ismore_recent < 0)) { /* we're going to make new lsa or to flush this LSA. */ if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Self-originated LSA %s from %s:%s", received->str, from->str, from->ospf6_interface->interface->name); if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: %s: Make new one/Flush", received->str); SET_FLAG (received->flag, OSPF6_LSA_FLAG_REFRESH); slot = ospf6_lsa_slot_get (received->header->type); if (slot && slot->func_refresh) { (*slot->func_refresh) (received); return; } zlog_warn ("Can't Refresh LSA: Unknown type: %#x, Flush", ntohs (received->header->type)); ospf6_lsa_premature_aging (received); return; } } else if (ospf6_lsdb_lookup_lsdb (received->header->type, received->header->id, received->header->adv_router, from->request_list)) /* (6) if there is instance on sending neighbor's request list */ { /* if no database copy, should go above state (5) */ assert (have); zlog_warn ("DBEX: [%s:%s] received LSA %s is not newer," " and is on his requestlist: Generate BadLSReq", from->str, from->ospf6_interface->interface->name, received->str); /* BadLSReq */ thread_add_event (master, bad_lsreq, from, 0); ospf6_lsa_delete (received); } else if (ismore_recent == 0) /* (7) if neither is more recent */ { /* (a) if on retranslist, Treat this LSA as an Ack: Implied Ack */ rem = ospf6_lsdb_lookup_lsdb (received->header->type, received->header->id, received->header->adv_router, from->retrans_list); if (rem) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Implied Ack from %s, (remove retrans)", from->str); SET_FLAG (received->flag, OSPF6_LSA_FLAG_IMPLIEDACK); ospf6_neighbor_retrans_remove (rem, from); } /* (b) possibly acknowledge */ acktype = ack_type (received, ismore_recent, from); if (acktype == DIRECT_ACK) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Direct Ack to %s", from->str); ospf6_dbex_acknowledge_direct (received, from); } else if (acktype == DELAYED_ACK) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: Delayed Ack to %s", from->str); ospf6_dbex_acknowledge_delayed (received, from->ospf6_interface); } else { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: No Ack to %s", from->str); } ospf6_lsa_delete (received); } else /* (8) previous database copy is more recent */ { /* If Seqnumber Wrapping, simply discard Otherwise, Send database copy of this LSA to this neighbor */ if (! IS_LSA_MAXAGE (received) || received->lsa_hdr->lsh_seqnum != MAX_SEQUENCE_NUMBER) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: database is more recent: send back to %s", from->str); ospf6_send_lsupdate_direct (have, from); } ospf6_lsa_delete (received); } } /* RFC2328: Table 19: Sending link state acknowledgements. */ int ack_type (struct ospf6_lsa *newp, int ismore_recent, struct ospf6_neighbor *from) { struct ospf6_interface *ospf6_interface; struct ospf6_lsa *have; int count; assert (from && from->ospf6_interface); ospf6_interface = from->ospf6_interface; if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_FLOODBACK)) return NO_ACK; if (ismore_recent < 0) { if (ospf6_interface->state != IFS_BDR) return DELAYED_ACK; if (ospf6_interface->dr == from->router_id) return DELAYED_ACK; return NO_ACK; } if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_DUPLICATE) && CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_IMPLIEDACK)) { if (ospf6_interface->state != IFS_BDR) return NO_ACK; if (ospf6_interface->dr == from->router_id) return DELAYED_ACK; return NO_ACK; } if (CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_DUPLICATE) && ! CHECK_FLAG (newp->flag, OSPF6_LSA_FLAG_IMPLIEDACK)) return DIRECT_ACK; have = ospf6_lsdb_lookup (newp->header->type, newp->header->id, newp->header->adv_router, ospf6_lsa_get_scope (newp->header->type, from->ospf6_interface)); count = 0; ospf6->foreach_nei (ospf6, &count, NBS_EXCHANGE, ospf6_count_state); ospf6->foreach_nei (ospf6, &count, NBS_LOADING, ospf6_count_state); if (IS_LSA_MAXAGE (newp) && have == NULL && count == 0) return DIRECT_ACK; return NO_ACK; } static void ospf6_dbex_flood_linklocal (struct ospf6_lsa *lsa, struct ospf6_interface *o6i, struct ospf6_neighbor *from) { struct ospf6_neighbor *o6n = (struct ospf6_neighbor *) NULL; int ismore_recent, addretrans = 0; listnode n; struct ospf6_lsa *req; /* (1) for each neighbor */ for (n = listhead (o6i->neighbor_list); n; nextnode (n)) { o6n = (struct ospf6_neighbor *) getdata (n); /* (a) */ if (o6n->state < NBS_EXCHANGE) continue; /* examin next neighbor */ /* (b) */ if (o6n->state == NBS_EXCHANGE || o6n->state == NBS_LOADING) { req = ospf6_lsdb_lookup_lsdb (lsa->header->type, lsa->header->id, lsa->header->adv_router, o6n->request_list); if (req) { ismore_recent = ospf6_lsa_check_recent (lsa, req); if (ismore_recent > 0) { continue; /* examin next neighbor */ } else if (ismore_recent == 0) { ospf6_neighbor_request_remove (req, o6n); continue; /* examin next neighbor */ } else /* ismore_recent < 0 (the new LSA is more recent) */ { ospf6_neighbor_request_remove (req, o6n); } } } /* (c) */ if (from && from->router_id == o6n->router_id) continue; /* examin next neighbor */ /* (d) add retranslist */ if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: schedule flooding [%s:%s]: %s", o6n->str, o6n->ospf6_interface->interface->name, lsa->str); ospf6_neighbor_retrans_add (lsa, o6n); addretrans++; if (o6n->send_update == (struct thread *) NULL) o6n->send_update = thread_add_timer (master, ospf6_send_lsupdate_rxmt, o6n, o6n->ospf6_interface->rxmt_interval); } /* (2) */ if (addretrans == 0) return; /* examin next interface */ if (from && from->ospf6_interface == o6i) { if (IS_OSPF6_DUMP_DBEX) zlog_info ("DBEX: flood back %s to %s", lsa->str, o6i->interface->name); /* note occurence of floodback */ SET_FLAG (lsa->flag, OSPF6_LSA_FLAG_FLOODBACK); } /* (3) */ if (from && from->ospf6_interface == o6i) { /* if from DR or BDR, don't need to flood this interface */ if (from->router_id == from->ospf6_interface->dr || from->router_id == from->ospf6_interface->bdr) return; /* examin next interface */ } /* (4) if I'm BDR, DR will flood this interface */ if (from && from->ospf6_interface == o6i && o6i->state == IFS_BDR) return; /* examin next interface */ if (IS_OSPF6_DUMP_DBEX) zlog_info ("Flood to interface %s", o6i->interface->name); /* (5) send LinkState Update */ ospf6_send_lsupdate_flood (lsa, o6i); return; } /* RFC2328 section 13.3 */ static void ospf6_dbex_flood_area (struct ospf6_lsa *lsa, struct ospf6_area *area, struct ospf6_neighbor *from) { listnode n; struct ospf6_interface *ospf6_interface; assert (lsa && lsa->lsa_hdr && area); /* for each eligible ospf_ifs */ for (n = listhead (area->if_list); n; nextnode (n)) { ospf6_interface = (struct ospf6_interface *) getdata (n); ospf6_dbex_flood_linklocal (lsa, ospf6_interface, from); } } static void ospf6_dbex_flood_as (struct ospf6_lsa *lsa, struct ospf6 *ospf6, struct ospf6_neighbor *from) { listnode n; struct ospf6_area *o6a; assert (lsa && lsa->lsa_hdr && ospf6); /* for each attached area */ for (n = listhead (ospf6->area_list); n; nextnode (n)) { o6a = (struct ospf6_area *) getdata (n); ospf6_dbex_flood_area (lsa, o6a, from); } } /* flood ospf6_lsa within appropriate scope */ void ospf6_dbex_flood (struct ospf6_lsa *lsa, struct ospf6_neighbor *from) { struct ospf6_area *o6a; struct ospf6_interface *o6i; struct ospf6 *o6; struct ospf6_lsa_header *lsa_header; lsa_header = (struct ospf6_lsa_header *) lsa->lsa_hdr; if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (lsa_header->type))) { o6i = (struct ospf6_interface *) lsa->scope; assert (o6i); if (IS_OSPF6_DUMP_DBEX) zlog_info ("Flood Linklocal: %s", o6i->interface->name); ospf6_dbex_flood_linklocal (lsa, o6i, from); } else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (lsa_header->type))) { o6a = (struct ospf6_area *) lsa->scope; assert (o6a); if (IS_OSPF6_DUMP_DBEX) zlog_info ("Flood Area: %s", o6a->str); ospf6_dbex_flood_area (lsa, o6a, from); } else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (lsa_header->type))) { o6 = (struct ospf6 *) lsa->scope; assert (o6); if (IS_OSPF6_DUMP_DBEX) zlog_info ("Flood AS"); ospf6_dbex_flood_as (lsa, o6, from); } else { zlog_warn ("Can't Flood %s: scope unknown", lsa->str); } }