diff options
author | paul <paul> | 2002-12-13 20:15:29 +0000 |
---|---|---|
committer | paul <paul> | 2002-12-13 20:15:29 +0000 |
commit | 718e3744195351130f4ce7dbe0613f4b3e23df93 (patch) | |
tree | bac2ad39971cd43f31241ef123bd4e470f695ac9 /ospf6d/ospf6_lsa.c |
Initial revision
Diffstat (limited to 'ospf6d/ospf6_lsa.c')
-rw-r--r-- | ospf6d/ospf6_lsa.c | 1926 |
1 files changed, 1926 insertions, 0 deletions
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c new file mode 100644 index 00000000..b14979ff --- /dev/null +++ b/ospf6d/ospf6_lsa.c @@ -0,0 +1,1926 @@ +/* + * LSA function + * 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 <zebra.h> + +/* Include other stuffs */ +#include "version.h" +#include "log.h" +#include "getopt.h" +#include "linklist.h" +#include "thread.h" +#include "command.h" +#include "memory.h" +#include "sockunion.h" +#include "if.h" +#include "prefix.h" +#include "stream.h" +#include "thread.h" +#include "filter.h" +#include "zclient.h" +#include "table.h" +#include "plist.h" + +#include "ospf6_proto.h" +#include "ospf6_prefix.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_dump.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_ism.h" +#include "ospf6_nsm.h" +#include "ospf6_dbex.h" + +#define HEADER_DEPENDENCY +#include "ospf6d.h" +#undef HEADER_DEPENDENCY + +/* test LSAs identity */ +static int +ospf6_lsa_issame (struct ospf6_lsa_header__ *lsh1, + struct ospf6_lsa_header__ *lsh2) +{ + assert (lsh1 && lsh2); + + if (lsh1->adv_router != lsh2->adv_router) + return 0; + + if (lsh1->id != lsh2->id) + return 0; + + if (lsh1->type != lsh2->type) + return 0; + + return 1; +} + +/* RFC2328: Section 13.2 */ +int +ospf6_lsa_differ (struct ospf6_lsa *lsa1, + struct ospf6_lsa *lsa2) +{ + int diff, cmplen; + + if (! ospf6_lsa_issame (lsa1->header, lsa2->header)) + return 1; + + /* check Options field */ + /* xxx */ + + ospf6_lsa_age_current (lsa1); + ospf6_lsa_age_current (lsa2); + if (ntohs (lsa1->header->age) == MAXAGE && + ntohs (lsa2->header->age) != MAXAGE) + return 1; + if (ntohs (lsa1->header->age) != MAXAGE && + ntohs (lsa2->header->age) == MAXAGE) + return 1; + + /* compare body */ + if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length)) + return 1; + + cmplen = ntohs (lsa1->header->length) - sizeof (struct ospf6_lsa_header); + diff = memcmp (lsa1->header + 1, lsa2->header + 1, cmplen); + + return diff; +} + +int +ospf6_lsa_match (u_int16_t type, u_int32_t id, u_int32_t adv_router, + struct ospf6_lsa_header *lsh) +{ + if (lsh->advrtr != adv_router) + return 0; + + if (lsh->ls_id != id) + return 0; + + if (lsh->type != type) + return 0; + + return 1; +} + +/* ospf6 age functions */ +/* calculate birth and set expire timer */ +static void +ospf6_lsa_age_set (struct ospf6_lsa *lsa) +{ + struct timeval now; + + assert (lsa && lsa->header); + + if (gettimeofday (&now, (struct timezone *)NULL) < 0) + zlog_warn ("LSA: gettimeofday failed, may fail LSA AGEs: %s", + strerror (errno)); + + lsa->birth.tv_sec = now.tv_sec - ntohs (lsa->header->age); + lsa->birth.tv_usec = now.tv_usec; + if (ntohs (lsa->header->age) != MAXAGE) + lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa, + lsa->birth.tv_sec + MAXAGE - now.tv_sec); + else + lsa->expire = NULL; + return; +} + +/* this function calculates current age from its birth, + then update age field of LSA header. return value is current age */ +u_int16_t +ospf6_lsa_age_current (struct ospf6_lsa *lsa) +{ + struct timeval now; + u_int32_t ulage; + u_int16_t age; + + assert (lsa); + assert (lsa->header); + + /* current time */ + if (gettimeofday (&now, (struct timezone *)NULL) < 0) + zlog_warn ("LSA: gettimeofday failed, may fail ages: %s", + strerror (errno)); + + /* calculate age */ + ulage = now.tv_sec - lsa->birth.tv_sec; + + /* if over MAXAGE, set to it */ + if (ulage > MAXAGE) + age = MAXAGE; + else + age = ulage; + + lsa->header->age = htons (age); + return age; +} + +/* update age field of LSA header with adding InfTransDelay */ +void +ospf6_lsa_age_update_to_send (struct ospf6_lsa *lsa, u_int32_t transdelay) +{ + unsigned short age; + + age = ospf6_lsa_age_current (lsa) + transdelay; + if (age > MAXAGE) + age = MAXAGE; + lsa->header->age = htons (age); + return; +} + +void +ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) +{ + /* log */ + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Premature aging: %s", lsa->str); + + if (lsa->expire) + thread_cancel (lsa->expire); + lsa->expire = (struct thread *) NULL; + if (lsa->refresh) + thread_cancel (lsa->refresh); + lsa->refresh = (struct thread *) NULL; + + memset (&lsa->birth, 0, sizeof (struct timeval)); + thread_execute (master, ospf6_lsa_expire, lsa, 0); +} + +/* check which is more recent. if a is more recent, return -1; + if the same, return 0; otherwise(b is more recent), return 1 */ +int +ospf6_lsa_check_recent (struct ospf6_lsa *a, struct ospf6_lsa *b) +{ + signed long seqnuma, seqnumb; + u_int16_t cksuma, cksumb; + u_int16_t agea, ageb; + + assert (a && a->header); + assert (b && b->header); + assert (ospf6_lsa_issame (a->header, b->header)); + + seqnuma = ((signed long) ntohl (a->header->seqnum)) + - (signed long) INITIAL_SEQUENCE_NUMBER; + seqnumb = ((signed long) ntohl (b->header->seqnum)) + - (signed long) INITIAL_SEQUENCE_NUMBER; + + /* compare by sequence number */ + /* xxx, care about LS sequence number wrapping */ + recent_reason = "seqnum"; + if (seqnuma > seqnumb) + return -1; + else if (seqnuma < seqnumb) + return 1; + + /* Checksum */ + cksuma = ntohs (a->header->checksum); + cksumb = ntohs (b->header->checksum); + if (cksuma > cksumb) + return -1; + if (cksuma < cksumb) + return 0; + + /* Age check */ + agea = ospf6_lsa_age_current (a); + ageb = ospf6_lsa_age_current (b); + + /* MaxAge check */ + recent_reason = "max age"; + if (agea == OSPF6_LSA_MAXAGE && ageb != OSPF6_LSA_MAXAGE) + return -1; + else if (agea != OSPF6_LSA_MAXAGE && ageb == OSPF6_LSA_MAXAGE) + return 1; + + recent_reason = "age differ"; + if (agea > ageb && agea - ageb >= OSPF6_LSA_MAXAGEDIFF) + return 1; + else if (agea < ageb && ageb - agea >= OSPF6_LSA_MAXAGEDIFF) + return -1; + + /* neither recent */ + recent_reason = "the same instance"; + return 0; +} + +int +ospf6_lsa_lsd_num (struct ospf6_lsa_header *lsa_header) +{ + int ldnum = 0; + u_int16_t len; + + len = ntohs (lsa_header->length); + len -= sizeof (struct ospf6_lsa_header); + if (lsa_header->type == htons (OSPF6_LSA_TYPE_ROUTER)) + { + len -= sizeof (struct ospf6_router_lsa); + ldnum = len / sizeof (struct ospf6_router_lsd); + } + else /* (lsa_header->type == htons (OSPF6_LSA_TYPE_NETWORK)) */ + { + len -= sizeof (struct ospf6_network_lsa); + ldnum = len / sizeof (u_int32_t); + } + + return ldnum; +} + +void * +ospf6_lsa_lsd_get (int index, struct ospf6_lsa_header *lsa_header) +{ + void *p; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsd *router_lsd; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsd *network_lsd; + + if (lsa_header->type == htons (OSPF6_LSA_TYPE_ROUTER)) + { + router_lsa = (struct ospf6_router_lsa *) (lsa_header + 1); + router_lsd = (struct ospf6_router_lsd *) (router_lsa + 1); + router_lsd += index; + p = (void *) router_lsd; + } + else if (lsa_header->type == htons (OSPF6_LSA_TYPE_NETWORK)) + { + network_lsa = (struct ospf6_network_lsa *) (lsa_header + 1); + network_lsd = (struct ospf6_network_lsd *) (network_lsa + 1); + network_lsd += index; + p = (void *) network_lsd; + } + else + { + p = (void *) NULL; + } + + return p; +} + +/* network_lsd <-> router_lsd */ +static int +ospf6_lsa_lsd_network_reference_match (struct ospf6_network_lsd *network_lsd1, + struct ospf6_lsa_header *lsa_header1, + struct ospf6_router_lsd *router_lsd2, + struct ospf6_lsa_header *lsa_header2) +{ + if (network_lsd1->adv_router != lsa_header2->advrtr) + return 0; + if (router_lsd2->type != OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK) + return 0; + if (router_lsd2->neighbor_router_id != lsa_header1->advrtr) + return 0; + if (router_lsd2->neighbor_interface_id != lsa_header1->ls_id) + return 0; + return 1; +} + +/* router_lsd <-> router_lsd */ +static int +ospf6_lsa_lsd_router_reference_match (struct ospf6_router_lsd *router_lsd1, + struct ospf6_lsa_header *lsa_header1, + struct ospf6_router_lsd *router_lsd2, + struct ospf6_lsa_header *lsa_header2) +{ + if (router_lsd1->type != OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT) + return 0; + if (router_lsd2->type != OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT) + return 0; + if (router_lsd1->neighbor_router_id != lsa_header2->advrtr) + return 0; + if (router_lsd2->neighbor_router_id != lsa_header1->advrtr) + return 0; + if (router_lsd1->neighbor_interface_id != router_lsd2->interface_id) + return 0; + if (router_lsd2->neighbor_interface_id != router_lsd1->interface_id) + return 0; + return 1; +} + +int +ospf6_lsa_lsd_is_refer_ok (int index1, struct ospf6_lsa_header *lsa_header1, + int index2, struct ospf6_lsa_header *lsa_header2) +{ + struct ospf6_router_lsd *r1, *r2; + struct ospf6_network_lsd *n; + + r1 = (struct ospf6_router_lsd *) NULL; + r2 = (struct ospf6_router_lsd *) NULL; + n = (struct ospf6_network_lsd *) NULL; + if (lsa_header1->type == htons (OSPF6_LSA_TYPE_ROUTER)) + r1 = (struct ospf6_router_lsd *) ospf6_lsa_lsd_get (index1, lsa_header1); + else + n = (struct ospf6_network_lsd *) ospf6_lsa_lsd_get (index1, lsa_header1); + + if (lsa_header2->type == htons (OSPF6_LSA_TYPE_ROUTER)) + r2 = (struct ospf6_router_lsd *) ospf6_lsa_lsd_get (index2, lsa_header2); + else + n = (struct ospf6_network_lsd *) ospf6_lsa_lsd_get (index2, lsa_header2); + + if (r1 && r2) + return ospf6_lsa_lsd_router_reference_match (r1, lsa_header1, + r2, lsa_header2); + else if (r1 && n) + return ospf6_lsa_lsd_network_reference_match (n, lsa_header2, + r1, lsa_header1); + else if (n && r2) + return ospf6_lsa_lsd_network_reference_match (n, lsa_header1, + r2, lsa_header2); + return 0; +} + +void +ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + char adv_router[64], id[64], type[32]; + + assert (lsa); + assert (lsa->header); + + ospf6_lsa_type_string (lsa->header->type, type, sizeof (type)); + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + inet_ntop (AF_INET, &lsa->header->adv_router, + adv_router, sizeof (adv_router)); + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa), + type, VTY_NEWLINE); + vty_out (vty, "Link State ID: %s%s", id, VTY_NEWLINE); + vty_out (vty, "Advertising Router: %s%s", adv_router, VTY_NEWLINE); + vty_out (vty, "LS Sequence Number: %#lx%s", (u_long)ntohl (lsa->header->seqnum), + VTY_NEWLINE); + vty_out (vty, "CheckSum: %#hx Length: %hu%s", ntohs (lsa->header->checksum), + ntohs (lsa->header->length), VTY_NEWLINE); + + { + struct ospf6_lsa_slot *slot; + slot = ospf6_lsa_slot_get (lsa->header->type); + if (slot) + { + (*slot->func_show) (vty, lsa); + vty_out (vty, "%s", VTY_NEWLINE); + return; + } + } + + vty_out (vty, "%sUnknown LSA type ...%s", VTY_NEWLINE, VTY_NEWLINE); +} + +void +ospf6_lsa_show_summary_header (struct vty *vty) +{ + vty_out (vty, "%-12s %-15s %-15s %4s %8s %4s %4s %-8s%s", + "Type", "LSId", "AdvRouter", "Age", "SeqNum", + "Cksm", "Len", "Duration", VTY_NEWLINE); +} + +void +ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) +{ + char adv_router[16], id[16], type[16]; + struct timeval now, res; + char duration[16]; + + assert (lsa); + assert (lsa->header); + + memset (type, 0, sizeof (type)); + ospf6_lsa_type_string (lsa->header->type, type, 13); + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, + sizeof (adv_router)); + + gettimeofday (&now, NULL); + ospf6_timeval_sub (&now, &lsa->installed, &res); + ospf6_timeval_string_summary (&res, duration, sizeof (duration)); + + vty_out (vty, "%-12s %-15s %-15s %4hu %8lx %04hx %4hu %8s%s", + type, id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum), + ntohs (lsa->header->checksum), ntohs (lsa->header->length), + duration, VTY_NEWLINE); +} + +void +ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa) +{ + u_char *start, *end, *current; + char byte[4]; + + start = (char *) lsa->header; + end = (char *) lsa->header + ntohs (lsa->header->length); + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%s:%s", lsa->str, VTY_NEWLINE); + + for (current = start; current < end; current ++) + { + if ((current - start) % 16 == 0) + vty_out (vty, "%s ", VTY_NEWLINE); + else if ((current - start) % 4 == 0) + vty_out (vty, " "); + + snprintf (byte, sizeof (byte), "%02x", *current); + vty_out (vty, "%s", byte); + } + + vty_out (vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE); +} + +/* OSPFv3 LSA creation/deletion function */ + +/* calculate LS sequence number for my new LSA. + return value is network byte order */ +static signed long +ospf6_lsa_seqnum_new (u_int16_t type, u_int32_t id, u_int32_t adv_router, + void *scope) +{ + struct ospf6_lsa *lsa; + signed long seqnum; + + /* get current database copy */ + lsa = ospf6_lsdb_lookup (type, id, adv_router, scope); + + /* if current database copy not found, return InitialSequenceNumber */ + if (!lsa) + seqnum = INITIAL_SEQUENCE_NUMBER; + else + seqnum = (signed long) ntohl (lsa->header->seqnum) + 1; + + return (htonl (seqnum)); +} + +#if 0 +static void +ospf6_lsa_header_set (u_int16_t type, u_int32_t ls_id, u_int32_t advrtr, + struct ospf6_lsa_header *lsa_header, int bodysize) +{ + /* fill LSA header */ + lsa_header->age = 0; + lsa_header->type = type; + lsa_header->ls_id = ls_id; + lsa_header->advrtr = advrtr; + lsa_header->seqnum = + ospf6_lsa_seqnum_new (lsa_header->type, lsa_header->ls_id, + lsa_header->advrtr); + lsa_header->length = htons (sizeof (struct ospf6_lsa_header) + bodysize); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); +} +#endif /*0*/ + +struct ospf6_lsa * +ospf6_lsa_create (struct ospf6_lsa_header *source) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_lsa_header *lsa_header = NULL; + u_int16_t lsa_size = 0; + char buf_router[16], buf_id[16], typebuf[32]; + + /* whole length of this LSA */ + lsa_size = ntohs (source->length); + + /* allocate memory for this LSA */ + lsa_header = (struct ospf6_lsa_header *) + XMALLOC (MTYPE_OSPF6_LSA, lsa_size); + if (! lsa_header) + { + zlog_err ("Can't allocate memory for LSA Header"); + return (struct ospf6_lsa *) NULL; + } + memset (lsa_header, 0, lsa_size); + + /* copy LSA from source */ + memcpy (lsa_header, source, lsa_size); + + /* LSA information structure */ + /* allocate memory */ + lsa = (struct ospf6_lsa *) + XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa)); + memset (lsa, 0, sizeof (struct ospf6_lsa)); + + lsa->lsa_hdr = (struct ospf6_lsa_hdr *) lsa_header; + lsa->header = (struct ospf6_lsa_header__ *) lsa_header; + + lsa->summary = 0; /* this is not LSA summary */ + + /* dump string */ + inet_ntop (AF_INET, &lsa->header->id, buf_id, sizeof (buf_id)); + inet_ntop (AF_INET, &lsa->header->adv_router, buf_router, + sizeof (buf_router)); + snprintf (lsa->str, sizeof (lsa->str), "[%s ID=%s Adv=%s]", + ospf6_lsa_type_string (lsa_header->type, typebuf, + sizeof (typebuf)), + buf_id, buf_router); + + /* calculate birth, expire and refresh of this lsa */ + ospf6_lsa_age_set (lsa); + +#ifdef DEBUG + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Create: %s (%p/%p)", lsa->str, lsa, lsa->header); +#endif /*DEBUG*/ + + return lsa; +} + +struct ospf6_lsa * +ospf6_lsa_summary_create (struct ospf6_lsa_header__ *source) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_lsa_header *lsa_header = NULL; + u_int16_t lsa_size = 0; + char buf_router[16], buf_id[16], typebuf[16]; + + /* LSA summary contains LSA Header only */ + lsa_size = sizeof (struct ospf6_lsa_header); + + /* allocate memory for this LSA */ + lsa_header = (struct ospf6_lsa_header *) + XMALLOC (MTYPE_OSPF6_LSA_SUMMARY, lsa_size); + memset (lsa_header, 0, lsa_size); + + /* copy LSA from source */ + memcpy (lsa_header, source, lsa_size); + + /* LSA information structure */ + /* allocate memory */ + lsa = (struct ospf6_lsa *) + XMALLOC (MTYPE_OSPF6_LSA_SUMMARY, sizeof (struct ospf6_lsa)); + memset (lsa, 0, sizeof (struct ospf6_lsa)); + + lsa->lsa_hdr = (struct ospf6_lsa_hdr *) lsa_header; + lsa->header = (struct ospf6_lsa_header__ *) lsa_header; + lsa->summary = 1; /* this is LSA summary */ + + /* dump string */ + inet_ntop (AF_INET, &lsa->header->id, buf_id, sizeof (buf_id)); + inet_ntop (AF_INET, &lsa->header->adv_router, buf_router, + sizeof (buf_router)); + snprintf (lsa->str, sizeof (lsa->str), "[%s Summary ID=%s Adv=%s]", + ospf6_lsa_type_string (lsa->header->type, typebuf, + sizeof (typebuf)), + buf_id, buf_router); + + /* calculate birth, expire and refresh of this lsa */ + ospf6_lsa_age_set (lsa); + +#ifdef DEBUG + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Create: %s (%p/%p)", lsa->str, lsa, lsa->header); +#endif /*DEBUG*/ + + return lsa; +} + +void +ospf6_lsa_delete (struct ospf6_lsa *lsa) +{ + /* just to make sure */ + if (lsa->lock != 0) + { + zlog_err ("Can't delete %s: lock: %ld", lsa->str, lsa->lock); + return; + } + + /* cancel threads */ + if (lsa->expire) + thread_cancel (lsa->expire); + lsa->expire = (struct thread *) NULL; + if (lsa->refresh) + thread_cancel (lsa->refresh); + lsa->refresh = (struct thread *) NULL; + +#ifdef DEBUG + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Delete %s (%p/%p)", lsa->str, lsa, lsa->header); +#endif /*DEBUG*/ + + /* do free */ + if (lsa->summary) + XFREE (MTYPE_OSPF6_LSA_SUMMARY, lsa->header); + else + XFREE (MTYPE_OSPF6_LSA, lsa->header); + lsa->header = NULL; + + if (lsa->summary) + XFREE (MTYPE_OSPF6_LSA_SUMMARY, lsa); + else + XFREE (MTYPE_OSPF6_LSA, lsa); +} + +/* increment reference counter of struct ospf6_lsa */ +void +ospf6_lsa_lock (struct ospf6_lsa *lsa) +{ + lsa->lock++; + return; +} + +/* decrement reference counter of struct ospf6_lsa */ +void +ospf6_lsa_unlock (struct ospf6_lsa *lsa) +{ + /* decrement reference counter */ + if (lsa->lock > 0) + lsa->lock--; + else + zlog_warn ("Can't unlock %s: already no lock", lsa->str); + + if (lsa->lock == 0) + ospf6_lsa_delete (lsa); +} + +void +ospf6_lsa_originate (u_int16_t type, u_int32_t id, u_int32_t adv_router, + char *data, int data_len, void *scope) +{ + char buffer[MAXLSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + struct ospf6_lsa *old; + + assert (data_len <= sizeof (buffer) - sizeof (struct ospf6_lsa_header)); + + lsa_header = (struct ospf6_lsa_header *) buffer; + + /* Copy LSA Body */ + memcpy (buffer + sizeof (struct ospf6_lsa_header), data, data_len); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = type; + lsa_header->ls_id = id; + lsa_header->advrtr = adv_router; + lsa_header->seqnum = + ospf6_lsa_seqnum_new (lsa_header->type, lsa_header->ls_id, + lsa_header->advrtr, scope); + lsa_header->length = htons (sizeof (struct ospf6_lsa_header) + data_len); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create ((struct ospf6_lsa_header *) buffer); + lsa->scope = scope; + + /* find previous LSA */ + old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->scope); + if (old) + { + /* Check if this is neither different instance nor refresh, return */ + if (! CHECK_FLAG (old->flag, OSPF6_LSA_FLAG_REFRESH) && + ! ospf6_lsa_differ (lsa, old)) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Suppress updating %s", lsa->str); + ospf6_lsa_delete (lsa); + return; + } + } + + lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa, + OSPF6_LS_REFRESH_TIME); + gettimeofday (&lsa->originated, NULL); + + //if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: originate %s seq: %#x age: %hu %ld.%06ld", + lsa->str, ntohl (lsa->header->seqnum), + ospf6_lsa_age_current (lsa), + lsa->originated.tv_sec, lsa->originated.tv_usec); + + ospf6_dbex_remove_from_all_retrans_list (lsa); + ospf6_dbex_flood (lsa, NULL); + ospf6_lsdb_install (lsa); +} + + +/* ospf6_lsa expired */ +int +ospf6_lsa_expire (struct thread *thread) +{ + struct ospf6_lsa *lsa; + struct ospf6_lsdb *lsdb = NULL; + void (*hook) (struct ospf6_lsa *, struct ospf6_lsa *); + + lsa = (struct ospf6_lsa *) THREAD_ARG (thread); + assert (lsa && lsa->lsa_hdr); + + /* assertion */ + assert (IS_LSA_MAXAGE (lsa)); + assert (!lsa->refresh); + + lsa->expire = (struct thread *) NULL; + + /* log */ + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Expire: %s", lsa->str); + + if (!lsa->summary) + { + /* reflood lsa */ + ospf6_dbex_flood (lsa, NULL); + + /* get scoped lsdb, call remove hook */ + if (OSPF6_LSA_IS_SCOPE_LINKLOCAL (ntohs (lsa->header->type))) + lsdb = ((struct ospf6_interface *) lsa->scope)->lsdb; + else if (OSPF6_LSA_IS_SCOPE_AREA (ntohs (lsa->header->type))) + lsdb = ((struct ospf6_area *) lsa->scope)->lsdb; + else if (OSPF6_LSA_IS_SCOPE_AS (ntohs (lsa->header->type))) + lsdb = ((struct ospf6 *) lsa->scope)->lsdb; + else + assert (0); + + /* call LSDB hook to re-process LSA */ + hook = ospf6_lsdb_hook[ntohs (lsa->header->type) & + OSPF6_LSTYPE_CODE_MASK].hook; + if (hook) + (*hook) (NULL, lsa); + + /* do not free LSA, and do nothing about lslists. + wait event (ospf6_lsdb_check_maxage) */ + } + + return 0; +} + +int +ospf6_lsa_refresh (struct thread *thread) +{ + struct ospf6_lsa *lsa; + struct ospf6_lsa_slot *slot; + + assert (thread); + lsa = (struct ospf6_lsa *) THREAD_ARG (thread); + assert (lsa && lsa->lsa_hdr); + + /* this will be used later as flag to decide really originate */ + lsa->refresh = (struct thread *) NULL; + SET_FLAG (lsa->flag, OSPF6_LSA_FLAG_REFRESH); + + /* log */ + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA Refresh: %s", lsa->str); + + slot = ospf6_lsa_slot_get (lsa->header->type); + if (slot) + { + zlog_info ("LSA Refresh: %s", slot->name); + (*slot->func_refresh) (lsa); + return 0; + } + + zlog_warn ("Can't Refresh LSA: Unknown type: %#x", + ntohs (lsa->header->type)); + return 1; +} + + + +/* enhanced Fletcher checksum algorithm, RFC1008 7.2 */ +#define MODX 4102 +#define LSA_CHECKSUM_OFFSET 15 + +unsigned short +ospf6_lsa_checksum (struct ospf6_lsa_header *lsa_header) +{ + u_char *sp, *ep, *p, *q; + int c0 = 0, c1 = 0; + int x, y; + u_int16_t length; + + lsa_header->checksum = 0; + length = ntohs (lsa_header->length) - 2; + sp = (char *) &lsa_header->type; + + for (ep = sp + length; sp < ep; sp = q) + { + q = sp + MODX; + if (q > ep) + q = ep; + for (p = sp; p < q; p++) + { + c0 += *p; + c1 += c0; + } + c0 %= 255; + c1 %= 255; + } + + /* r = (c1 << 8) + c0; */ + x = ((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255; + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + lsa_header->checksum = htons ((x << 8) + y); + + return (lsa_header->checksum); +} + +int +ospf6_lsa_is_known_type (struct ospf6_lsa_header *lsa_header) +{ + struct ospf6_lsa_slot *slot; + + slot = ospf6_lsa_slot_get (lsa_header->type); + if (slot) + return 1; + return 0; +} + +struct ospf6_lsa_slot *slot_head = NULL; + +struct ospf6_lsa_slot * +ospf6_lsa_slot_get (u_int16_t type) +{ + struct ospf6_lsa_slot *slot; + + for (slot = slot_head; slot; slot = slot->next) + { + if (slot->type == type) + return slot; + } + + return NULL; +} + +int +ospf6_lsa_slot_register (struct ospf6_lsa_slot *src) +{ + struct ospf6_lsa_slot *new, *slot; + + slot = ospf6_lsa_slot_get (src->type); + if (slot) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Slot register: already exists: %#x %s", + slot->type, slot->name); + return -1; + } + + new = (struct ospf6_lsa_slot *) + XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa_slot)); + if (! new) + { + zlog_err ("Can't allocate memory for LSA slot: %s", strerror (errno)); + return -1; + } + memset (new, 0, sizeof (struct ospf6_lsa_slot)); + memcpy (new, src, sizeof (struct ospf6_lsa_slot)); + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Slot register: %#x %s", slot->type, slot->name); + + if (slot_head == NULL) + { + new->prev = NULL; + new->next = NULL; + slot_head = new; + return 0; + } + + slot = slot_head; + while (slot->next) + slot = slot->next; + + slot->next = new; + new->prev = slot; + + return 0; +} + +int +ospf6_lsa_slot_unregister (u_int16_t type) +{ + struct ospf6_lsa_slot *slot; + + slot = ospf6_lsa_slot_get (type); + if (slot == NULL) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Registering LSA slot: no such slot: %#x", type); + return -1; + } + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Unregistering LSA Slot: %#x %s", slot->type, slot->name); + + if (slot->prev) + slot->prev->next = slot->next; + if (slot->next) + slot->next->prev = slot->prev; + + if (slot_head == slot) + slot_head = slot->next; + + XFREE (MTYPE_OSPF6_LSA, slot); + return 0; +} + +char * +ospf6_lsa_type_string (u_int16_t type, char *buf, int bufsize) +{ + struct ospf6_lsa_slot *slot; + + slot = ospf6_lsa_slot_get (type); + if (slot) + snprintf (buf, bufsize, "%s", slot->name); + else + snprintf (buf, bufsize, "Type=0x%04x", ntohs (type)); + + return buf; +} + + +/*******************/ +/* LSA Origination */ +/*******************/ + +#define CONTINUE_IF_ADDRESS_LINKLOCAL(addr)\ + if (IN6_IS_ADDR_LINKLOCAL (&(addr)->u.prefix6))\ + {\ + char buf[64];\ + prefix2str (addr, buf, sizeof (buf));\ + if (IS_OSPF6_DUMP_LSA)\ + zlog_info (" Filter out Linklocal: %s", buf);\ + continue;\ + } + +#define CONTINUE_IF_ADDRESS_UNSPECIFIED(addr)\ + if (IN6_IS_ADDR_UNSPECIFIED (&(addr)->u.prefix6))\ + {\ + char buf[64];\ + prefix2str (addr, buf, sizeof (buf));\ + if (IS_OSPF6_DUMP_LSA)\ + zlog_info (" Filter out Unspecified: %s", buf);\ + continue;\ + } + +#define CONTINUE_IF_ADDRESS_LOOPBACK(addr)\ + if (IN6_IS_ADDR_LOOPBACK (&(addr)->u.prefix6))\ + {\ + char buf[64];\ + prefix2str (addr, buf, sizeof (buf));\ + if (IS_OSPF6_DUMP_LSA)\ + zlog_info (" Filter out Loopback: %s", buf);\ + continue;\ + } + +#define CONTINUE_IF_ADDRESS_V4COMPAT(addr)\ + if (IN6_IS_ADDR_V4COMPAT (&(addr)->u.prefix6))\ + {\ + char buf[64];\ + prefix2str (addr, buf, sizeof (buf));\ + if (IS_OSPF6_DUMP_LSA)\ + zlog_info (" Filter out V4Compat: %s", buf);\ + continue;\ + } + +#define CONTINUE_IF_ADDRESS_V4MAPPED(addr)\ + if (IN6_IS_ADDR_V4MAPPED (&(addr)->u.prefix6))\ + {\ + char buf[64];\ + prefix2str (addr, buf, sizeof (buf));\ + if (IS_OSPF6_DUMP_LSA)\ + zlog_info (" Filter out V4Mapped: %s", buf);\ + continue;\ + } + +/******************************/ +/* RFC2740 3.4.3.1 Router-LSA */ +/******************************/ + +char * +ospf6_lsa_router_bits_string (u_char router_bits, char *buf, int size) +{ + char w, v, e, b; + + w = (router_bits & OSPF6_ROUTER_LSA_BIT_W ? 'W' : '-'); + v = (router_bits & OSPF6_ROUTER_LSA_BIT_V ? 'V' : '-'); + e = (router_bits & OSPF6_ROUTER_LSA_BIT_E ? 'E' : '-'); + b = (router_bits & OSPF6_ROUTER_LSA_BIT_B ? 'B' : '-'); + snprintf (buf, size, "----%c%c%c%c", w, v, e, b); + return buf; +} + +int +ospf6_lsa_router_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + char *start, *end, *current; + char buf[32], name[32], bits[32], options[32]; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsd *lsdesc; + + assert (lsa->header); + + router_lsa = (struct ospf6_router_lsa *) + ((char *) lsa->header + sizeof (struct ospf6_lsa_header)); + + ospf6_lsa_router_bits_string (router_lsa->bits, bits, sizeof (bits)); + ospf6_options_string (router_lsa->options, options, sizeof (options)); + vty_out (vty, " Bits: %s Options: %s%s", bits, options, VTY_NEWLINE); + + start = (char *) router_lsa + sizeof (struct ospf6_router_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current + sizeof (struct ospf6_router_lsd) <= end; + current += sizeof (struct ospf6_router_lsd)) + { + lsdesc = (struct ospf6_router_lsd *) current; + + if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT) + snprintf (name, sizeof (name), "Point-To-Point"); + else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK) + snprintf (name, sizeof (name), "Transit-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_STUB_NETWORK) + snprintf (name, sizeof (name), "Stub-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSD_TYPE_VIRTUAL_LINK) + snprintf (name, sizeof (name), "Virtual-Link"); + else + snprintf (name, sizeof (name), "Unknown (%#x)", lsdesc->type); + + vty_out (vty, " Type: %s Metric: %d%s", + name, ntohs (lsdesc->metric), VTY_NEWLINE); + vty_out (vty, " Interface ID: %s%s", + inet_ntop (AF_INET, &lsdesc->interface_id, + buf, sizeof (buf)), VTY_NEWLINE); + vty_out (vty, " Neighbor Interface ID: %s%s", + inet_ntop (AF_INET, &lsdesc->neighbor_interface_id, + buf, sizeof (buf)), VTY_NEWLINE); + vty_out (vty, " Neighbor Router ID: %s%s", + inet_ntop (AF_INET, &lsdesc->neighbor_router_id, + buf, sizeof (buf)), VTY_NEWLINE); + } + return 0; +} + +u_long +ospf6_lsa_has_elasped (u_int16_t type, u_int32_t id, + u_int32_t adv_router, void *scope) +{ + struct ospf6_lsa *old; + struct timeval now; + + if (adv_router != ospf6->router_id) + zlog_info ("LSA: Router-ID changed ?"); + + old = ospf6_lsdb_lookup (type, id, adv_router, scope); + if (! old) + return OSPF6_LSA_MAXAGE; + + gettimeofday (&now, NULL); + return ((u_long) SEC_TVDIFF (&now, &old->originated)); +} + +int +ospf6_lsa_originate_router (struct thread *thread) +{ + char buffer [MAXLSASIZE]; + u_int16_t size; + struct ospf6_area *o6a; + int count; + u_int32_t area_id; + + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsd *router_lsd; + listnode i; + struct ospf6_interface *o6i; + struct ospf6_neighbor *o6n = NULL; + + area_id = (u_int32_t) THREAD_ARG (thread); + + o6a = ospf6_area_lookup (area_id, ospf6); + if (! o6a) + { + inet_ntop (AF_INET, &area_id, buffer, sizeof (buffer)); + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: Update Router-LSA: No such area: %s", buffer); + return 0; + } + + /* clear thread */ + o6a->thread_router_lsa = NULL; + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: originate Router-LSA for Area %s", o6a->str); + + size = sizeof (struct ospf6_router_lsa); + memset (buffer, 0, sizeof (buffer)); + router_lsa = (struct ospf6_router_lsa *) buffer; + + OSPF6_OPT_CLEAR_ALL (router_lsa->options); + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_E); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_MC); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_N); + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_R); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_DC); + + OSPF6_ROUTER_LSA_CLEAR_ALL_BITS (router_lsa); + OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_B); + + if (ospf6_is_asbr (o6a->ospf6)) + OSPF6_ROUTER_LSA_SET (router_lsa, OSPF6_ROUTER_LSA_BIT_E); + else + OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_E); + + OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_V); + OSPF6_ROUTER_LSA_CLEAR (router_lsa, OSPF6_ROUTER_LSA_BIT_W); + + /* describe links for each interfaces */ + router_lsd = (struct ospf6_router_lsd *) (router_lsa + 1); + for (i = listhead (o6a->if_list); i; nextnode (i)) + { + o6i = (struct ospf6_interface *) getdata (i); + assert (o6i); + + /* Interfaces in state Down or Loopback are not described */ + if (o6i->state == IFS_DOWN || o6i->state == IFS_LOOPBACK) + continue; + + /* Nor are interfaces without any full adjacencies described */ + count = 0; + o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state); + if (count == 0) + continue; + + /* Point-to-Point interfaces */ + if (if_is_pointopoint (o6i->interface)) + { + if (listcount (o6i->neighbor_list) == 0) + continue; + + if (listcount (o6i->neighbor_list) != 1) + zlog_warn ("LSA: Multiple neighbors on PoinToPoint: %s", + o6i->interface->name); + + o6n = (struct ospf6_neighbor *) + getdata (listhead (o6i->neighbor_list)); + assert (o6n); + + router_lsd->type = OSPF6_ROUTER_LSD_TYPE_POINTTOPOINT; + router_lsd->metric = htons (o6i->cost); + router_lsd->interface_id = htonl (o6i->if_id); + router_lsd->neighbor_interface_id = htonl (o6n->ifid); + router_lsd->neighbor_router_id = o6n->router_id; + + size += sizeof (struct ospf6_router_lsd); + router_lsd ++; + + continue; + } + + /* Broadcast and NBMA interfaces */ + if (if_is_broadcast (o6i->interface)) + { + /* If this router is not DR, + and If this router not fully adjacent with DR, + this interface is not transit yet: ignore. */ + if (o6i->state != IFS_DR) + { + o6n = ospf6_neighbor_lookup (o6i->dr, o6i); /* find DR */ + if (o6n == NULL || o6n->state != NBS_FULL) + continue; + } + else + { + count = 0; + o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state); + if (count == 0) + continue; + } + + router_lsd->type = OSPF6_ROUTER_LSD_TYPE_TRANSIT_NETWORK; + router_lsd->metric = htons (o6i->cost); + router_lsd->interface_id = htonl (o6i->if_id); + if (o6i->state != IFS_DR) + { + router_lsd->neighbor_interface_id = htonl (o6n->ifid); + router_lsd->neighbor_router_id = o6n->router_id; + } + else + { + router_lsd->neighbor_interface_id = htonl (o6i->if_id); + router_lsd->neighbor_router_id = o6i->area->ospf6->router_id; + } + + size += sizeof (struct ospf6_router_lsd); + router_lsd ++; + + continue; + } + + /* Virtual links */ + /* xxx */ + /* Point-to-Multipoint interfaces */ + /* xxx */ + } + + ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_ROUTER), + htonl (0), o6a->ospf6->router_id, + (char *) router_lsa, size, o6a); + return 0; +} + +void +ospf6_lsa_schedule_router (struct ospf6_area *area) +{ + u_long elasped_time, time = 0; + + if (area->thread_router_lsa) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: schedule: Router-LSA for Area %s: another thread", + area->str); + return; + } + + elasped_time = + ospf6_lsa_has_elasped (htons (OSPF6_LSA_TYPE_ROUTER), htonl (0), + area->ospf6->router_id, area); + if (elasped_time < OSPF6_MIN_LS_INTERVAL) + time = (u_long) (OSPF6_MIN_LS_INTERVAL - elasped_time); + else + time = 0; + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("LSA: schedule: Router-LSA for Area %s after %lu sec", + area->str, time); + + if (time) + area->thread_router_lsa = + thread_add_timer (master, ospf6_lsa_originate_router, + (void *) area->area_id, time); + else + area->thread_router_lsa = + thread_add_event (master, ospf6_lsa_originate_router, + (void *) area->area_id, 0); +} + +int +ospf6_lsa_router_hook_neighbor (void *neighbor) +{ + struct ospf6_neighbor *o6n = neighbor; + if (o6n->ospf6_interface->area) + ospf6_lsa_schedule_router (o6n->ospf6_interface->area); + return 0; +} + +int +ospf6_lsa_router_hook_interface (void *interface) +{ + struct ospf6_interface *o6i = interface; + if (o6i->area) + ospf6_lsa_schedule_router (o6i->area); + return 0; +} + +int +ospf6_lsa_router_hook_area (void *area) +{ + struct ospf6_area *o6a = area; + ospf6_lsa_schedule_router (o6a); + return 0; +} + +int +ospf6_lsa_router_hook_top (void *ospf6) +{ + struct ospf6 *o6 = ospf6; + struct ospf6_area *o6a; + listnode node; + + for (node = listhead (o6->area_list); node; nextnode (node)) + { + o6a = getdata (node); + ospf6_lsa_schedule_router (o6a); + } + return 0; +} + +int +ospf6_lsa_router_refresh (void *old) +{ + struct ospf6_lsa *lsa = old; + struct ospf6_area *o6a; + + o6a = lsa->scope; + ospf6_lsa_schedule_router (o6a); + return 0; +} + +void +ospf6_lsa_slot_register_router () +{ + struct ospf6_lsa_slot slot; + struct ospf6_hook hook; + + memset (&slot, 0, sizeof (struct ospf6_lsa_slot)); + slot.type = htons (OSPF6_LSA_TYPE_ROUTER); + slot.name = "Router"; + slot.func_show = ospf6_lsa_router_show; + slot.func_refresh = ospf6_lsa_router_refresh; + ospf6_lsa_slot_register (&slot); + + ospf6_lsdb_hook[OSPF6_LSA_TYPE_ROUTER & OSPF6_LSTYPE_CODE_MASK].hook = + ospf6_spf_database_hook; + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateRouter"; + hook.hook_change = ospf6_lsa_router_hook_neighbor; + ospf6_hook_register (&hook, &neighbor_hook); + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateRouter"; + hook.hook_change = ospf6_lsa_router_hook_interface; + ospf6_hook_register (&hook, &interface_hook); + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateRouter"; + hook.hook_change = ospf6_lsa_router_hook_area; + ospf6_hook_register (&hook, &area_hook); + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateRouter"; + hook.hook_change = ospf6_lsa_router_hook_top; + ospf6_hook_register (&hook, &top_hook); +} + +/*******************************/ +/* RFC2740 3.4.3.2 Network-LSA */ +/*******************************/ + +int +ospf6_lsa_network_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + u_int32_t *router_id; + char buf[128], options[32]; + + assert (lsa->header); + network_lsa = (struct ospf6_network_lsa *) (lsa->header + 1); + router_id = (u_int32_t *)(network_lsa + 1); + + ospf6_options_string (network_lsa->options, options, sizeof (options)); + vty_out (vty, " Options: %s%s", options, VTY_NEWLINE); + + start = (char *) network_lsa + sizeof (struct ospf6_network_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current + sizeof (u_int32_t) <= end; + current += sizeof (u_int32_t)) + { + router_id = (u_int32_t *) current; + inet_ntop (AF_INET, router_id, buf, sizeof (buf)); + vty_out (vty, " Attached Router: %s%s", buf, VTY_NEWLINE); + } + return 0; +} + +void +ospf6_lsa_network_update (char *ifname) +{ + char buffer [MAXLSASIZE]; + u_int16_t size; + struct ospf6_lsa *old; + struct interface *ifp; + struct ospf6_interface *o6i; + int count; + + struct ospf6_network_lsa *network_lsa; + struct ospf6_neighbor *o6n; + u_int32_t *router_id; + listnode node; + + ifp = if_lookup_by_name (ifname); + if (! ifp) + { + if (IS_OSPF6_DUMP_LSA) + zlog_warn ("Update Network: No such Interface: %s", ifname); + return; + } + + o6i = (struct ospf6_interface *) ifp->info; + if (! o6i || ! o6i->area) + { + if (IS_OSPF6_DUMP_LSA) + zlog_warn ("Update Network: Interface not enabled: %s", ifname); + return; + } + + /* find previous LSA */ + old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_NETWORK), + htonl (o6i->if_id), + o6i->area->ospf6->router_id, o6i->area); + + /* Don't originate Network-LSA if not DR */ + if (o6i->state != IFS_DR) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Network: Interface %s is not DR", + o6i->interface->name); + if (old) + ospf6_lsa_premature_aging (old); + return; + } + + /* If none of neighbor is adjacent to us */ + count = 0; + o6i->foreach_nei (o6i, &count, NBS_FULL, ospf6_count_state); + if (count == 0) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Network: Interface %s is Stub", + o6i->interface->name); + if (old) + ospf6_lsa_premature_aging (old); + return; + } + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Network: Interface %s", o6i->interface->name); + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + size = sizeof (struct ospf6_network_lsa); + network_lsa = (struct ospf6_network_lsa *) buffer; + router_id = (u_int32_t *)(network_lsa + 1); + + /* set fields of myself */ + *router_id++ = o6i->area->ospf6->router_id; + size += sizeof (u_int32_t); + network_lsa->options[0] |= o6i->area->options[0]; + network_lsa->options[1] |= o6i->area->options[1]; + network_lsa->options[2] |= o6i->area->options[2]; + + /* Walk through neighbors */ + for (node = listhead (o6i->neighbor_list); node; nextnode (node)) + { + o6n = (struct ospf6_neighbor *) getdata (node); + + if (o6n->state != NBS_FULL) + continue; + + /* set this neighbor's Router-ID to LSA */ + *router_id++ = o6n->router_id; + size += sizeof (u_int32_t); + + /* options field is logical OR */ + network_lsa->options[0] |= o6n->options[0]; + network_lsa->options[1] |= o6n->options[1]; + network_lsa->options[2] |= o6n->options[2]; + } + + ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_NETWORK), + htonl (o6i->if_id), o6i->area->ospf6->router_id, + (char *) network_lsa, size, o6i->area); +} + +int +ospf6_lsa_network_hook_neighbor (void *neighbor) +{ + struct ospf6_neighbor *o6n = neighbor; + ospf6_lsa_network_update (o6n->ospf6_interface->interface->name); + return 0; +} + +int +ospf6_lsa_network_hook_interface (void *interface) +{ + struct ospf6_interface *o6i = interface; + if (o6i->area) + ospf6_lsa_network_update (o6i->interface->name); + return 0; +} + +int +ospf6_lsa_network_refresh (void *old) +{ + struct ospf6_lsa *lsa = old; + struct interface *ifp; + + ifp = if_lookup_by_index (ntohl (lsa->header->id)); + if (! ifp) + ospf6_lsa_premature_aging (old); + else + ospf6_lsa_network_update (ifp->name); + + return 0; +} + +void +ospf6_lsa_slot_register_network () +{ + struct ospf6_lsa_slot slot; + struct ospf6_hook hook; + + memset (&slot, 0, sizeof (struct ospf6_lsa_slot)); + slot.type = htons (OSPF6_LSA_TYPE_NETWORK); + slot.name = "Network"; + slot.func_show = ospf6_lsa_network_show; + slot.func_refresh = ospf6_lsa_network_refresh; + ospf6_lsa_slot_register (&slot); + + ospf6_lsdb_hook[OSPF6_LSA_TYPE_NETWORK & OSPF6_LSTYPE_CODE_MASK].hook = + ospf6_spf_database_hook; + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateNetwork"; + hook.hook_change = ospf6_lsa_network_hook_neighbor; + ospf6_hook_register (&hook, &neighbor_hook); + + memset (&hook, 0, sizeof (hook)); + hook.name = "OriginateNetwork"; + hook.hook_change = ospf6_lsa_network_hook_interface; + ospf6_hook_register (&hook, &interface_hook); +} + +/****************************/ +/* RFC2740 3.4.3.6 Link-LSA */ +/****************************/ + +int +ospf6_lsa_link_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + int prefixnum; + struct ospf6_prefix *prefix; + char buf[128]; + struct in6_addr in6; + + assert (lsa->header); + + link_lsa = (struct ospf6_link_lsa *) (lsa->header + 1); + prefixnum = ntohl (link_lsa->llsa_prefix_num); + + inet_ntop (AF_INET6, (void *)&link_lsa->llsa_linklocal, buf, sizeof (buf)); + vty_out (vty, " LinkLocal Address: %s%s", buf, VTY_NEWLINE); + vty_out (vty, " Number of Prefix: %d%s", prefixnum, VTY_NEWLINE); + + start = (char *) link_lsa + sizeof (struct ospf6_link_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix)) + { + prefix = (struct ospf6_prefix *) current; + if (current + OSPF6_PREFIX_SIZE (prefix) > end) + { + vty_out (vty, " Trailing %d byte garbage ... Malformed%s", + end - current, VTY_NEWLINE); + return -1; + } + + ospf6_prefix_options_str (prefix->prefix_options, buf, sizeof (buf)); + vty_out (vty, " Prefix Options: %s%s", buf, VTY_NEWLINE); + ospf6_prefix_in6_addr (prefix, &in6); + inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); + vty_out (vty, " Prefix: %s/%d%s", + buf, prefix->prefix_length, VTY_NEWLINE); + } + + return 0; +} + + +void +ospf6_lsa_link_update (char *ifname) +{ + char *cp, buffer [MAXLSASIZE], buf[32]; + u_int16_t size; + struct ospf6_lsa *old; + struct interface *ifp; + struct ospf6_interface *o6i; + + struct ospf6_link_lsa *link_lsa; + struct ospf6_prefix *p; + list prefix_connected; + listnode node; + struct connected *c; + + ifp = if_lookup_by_name (ifname); + if (! ifp) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Link: No such Interface: %s", ifname); + return; + } + + o6i = (struct ospf6_interface *) ifp->info; + if (! o6i || ! o6i->area) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Link: Interface not enabled: %s", ifname); + return; + } + +#if 0 + /* Link-LSA is on Broadcast or NBMA */ + if (! if_is_broadcast (o6i->interface) /* && ! NBMA xxx */) + { + return; + } +#endif /*0*/ + + /* find previous LSA */ + old = ospf6_lsdb_lookup (htons (OSPF6_LSA_TYPE_LINK), htonl (o6i->if_id), + ospf6->router_id, o6i->area); + + /* can't make Link-LSA if linklocal address not set */ + if (! o6i->lladdr) + { + if (IS_OSPF6_DUMP_LSA) + zlog_warn ("Update Link: No Linklocal Address: %s", + o6i->interface->name); + if (old) + ospf6_lsa_premature_aging (old); + return; + } + + if (IS_OSPF6_DUMP_LSA) + zlog_info ("Update Link: Interface %s", o6i->interface->name); + + if (! ospf6_interface_is_enabled (o6i->interface->ifindex)) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info (" Interface %s not enabled", o6i->interface->name); + if (old) + ospf6_lsa_premature_aging (old); + return; + } + + /* check connected prefix */ + prefix_connected = list_new (); + for (node = listhead (o6i->interface->connected); node; nextnode (node)) + { + c = (struct connected *) getdata (node); + + /* filter prefix not IPv6 */ + if (c->address->family != AF_INET6) + continue; + + /* for log */ + prefix2str (c->address, buf, sizeof (buf)); + + CONTINUE_IF_ADDRESS_LINKLOCAL (c->address); + CONTINUE_IF_ADDRESS_UNSPECIFIED (c->address); + CONTINUE_IF_ADDRESS_LOOPBACK (c->address); + CONTINUE_IF_ADDRESS_V4COMPAT (c->address); + CONTINUE_IF_ADDRESS_V4MAPPED (c->address); + + /* filter prefix specified by configuration */ + if (o6i->plist_name) + { + struct prefix_list *plist; + enum prefix_list_type result = PREFIX_PERMIT; + + plist = prefix_list_lookup (AFI_IP6, o6i->plist_name); + if (plist) + result = prefix_list_apply (plist, c->address); + else if (IS_OSPF6_DUMP_LSA) + zlog_warn ("Update Intra-Prefix (Stub): " + "Prefix list \"%s\" not found", o6i->plist_name); + + if (result == PREFIX_DENY) + { + if (IS_OSPF6_DUMP_LSA) + zlog_info (" Filter out Prefix-list %s: %s", + o6i->plist_name, buf); + continue; + } + } + + if (IS_OSPF6_DUMP_LSA) + zlog_info (" Advertise %s", buf); + + /* hold prefix in list. duplicate is filtered in ospf6_prefix_add() */ + p = ospf6_prefix_create (0, 0, (struct prefix_ipv6 *) c->address); + ospf6_prefix_add (prefix_connected, p); + } + + /* Note: even if no prefix configured, still we have to create Link-LSA + for next-hop resolution */ + + memset (buffer, 0, sizeof (buffer)); + size = sizeof (struct ospf6_link_lsa); + link_lsa = (struct ospf6_link_lsa *) buffer; + + /* fill Link LSA and calculate size */ + link_lsa->llsa_rtr_pri = o6i->priority; + link_lsa->llsa_options[0] = o6i->area->options[0]; + link_lsa->llsa_options[1] = o6i->area->options[1]; + link_lsa->llsa_options[2] = o6i->area->options[2]; + + /* linklocal address */ + memcpy (&link_lsa->llsa_linklocal, o6i->lladdr, sizeof (struct in6_addr)); + +#ifdef KAME /* clear ifindex */ + if (link_lsa->llsa_linklocal.s6_addr[3] & 0x0f) + link_lsa->llsa_linklocal.s6_addr[3] &= ~((char)0x0f); +#endif /* KAME */ + + link_lsa->llsa_prefix_num = htonl (listcount (prefix_connected)); + cp = (char *)(link_lsa + 1); + for (node = listhead (prefix_connected); node; nextnode (node)) + { + p = (struct ospf6_prefix *) getdata (node); + size += OSPF6_PREFIX_SIZE (p); + memcpy (cp, p, OSPF6_PREFIX_SIZE (p)); + cp += OSPF6_PREFIX_SIZE (p); + } + + for (node = listhead (prefix_connected); node; nextnode (node)) + { + p = (struct ospf6_prefix *) getdata (node); + ospf6_prefix_delete (p); + } + list_delete (prefix_connected); + + ospf6_lsa_originate (htons (OSPF6_LSA_TYPE_LINK), + htonl (o6i->if_id), o6i->area->ospf6->router_id, + (char *) link_lsa, size, o6i); +} + +int +ospf6_lsa_link_hook_interface (void *interface) +{ + struct ospf6_interface *o6i = interface; + if (o6i->area) + ospf6_lsa_link_update (o6i->interface->name); + return 0; +} + +int +ospf6_lsa_link_refresh (void *old) +{ + struct ospf6_lsa *lsa = old; + struct interface *ifp; + + ifp = if_lookup_by_index (ntohl (lsa->header->id)); + if (! ifp) + ospf6_lsa_premature_aging (old); + else + ospf6_lsa_link_update (ifp->name); + + return 0; +} + +void +ospf6_lsa_slot_register_link () +{ + struct ospf6_lsa_slot slot; + + memset (&slot, 0, sizeof (struct ospf6_lsa_slot)); + slot.type = htons (OSPF6_LSA_TYPE_LINK); + slot.name = "Link"; + slot.func_show = ospf6_lsa_link_show; + slot.func_refresh = ospf6_lsa_link_refresh; + slot.hook_interface.name = "OriginateLink"; + slot.hook_interface.hook_change = ospf6_lsa_link_hook_interface; + ospf6_lsa_slot_register (&slot); + + /* + * Link LSA handling will be shift in ospf6_intra.c + * Currently, only database hook only moved to ospf6_intra.c + */ +#if 0 + ospf6_lsdb_hook[OSPF6_LSA_TYPE_LINK & OSPF6_LSTYPE_CODE_MASK].hook = + ospf6_spf_database_hook; +#endif /*0*/ +} + +int +ospf6_lsa_add_hook (void *data) +{ + struct ospf6_lsa *lsa = data; + struct ospf6_lsa_slot *sp; + + sp = ospf6_lsa_slot_get (lsa->header->type); + if (sp) + { + CALL_CHANGE_HOOK (&sp->database_hook, lsa); + } + else + zlog_warn ("Unknown LSA added to database: %s", lsa->str); + return 0; +} + +int +ospf6_lsa_change_hook (void *data) +{ + struct ospf6_lsa *lsa = data; + struct ospf6_lsa_slot *sp; + + sp = ospf6_lsa_slot_get (lsa->header->type); + if (sp) + { + CALL_CHANGE_HOOK (&sp->database_hook, lsa); + } + else + zlog_warn ("Unknown LSA changed in database: %s", lsa->str); + return 0; +} + +int +ospf6_lsa_remove_hook (void *data) +{ + struct ospf6_lsa *lsa = data; + struct ospf6_lsa_slot *sp; + + sp = ospf6_lsa_slot_get (lsa->header->type); + if (sp) + { + CALL_REMOVE_HOOK (&sp->database_hook, lsa); + } + else + zlog_warn ("Unknown LSA removed from database: %s", lsa->str); + return 0; +} + +/* Initialize LSA slots */ +void +ospf6_lsa_init () +{ + struct ospf6_hook hook; + + slot_head = NULL; + ospf6_lsa_slot_register_router (); + ospf6_lsa_slot_register_network (); + ospf6_lsa_slot_register_link (); +#if 0 + ospf6_lsa_slot_register_intra_prefix (); + ospf6_lsa_slot_register_as_external (); +#endif /*0*/ + + hook.name = "LSADatabaseHook"; + hook.hook_add = ospf6_lsa_add_hook; + hook.hook_change = ospf6_lsa_change_hook; + hook.hook_remove = ospf6_lsa_remove_hook; + ospf6_hook_register (&hook, &database_hook); +} + |