diff options
Diffstat (limited to 'ospf6d/ospf6_message.c')
-rw-r--r-- | ospf6d/ospf6_message.c | 1972 |
1 files changed, 1972 insertions, 0 deletions
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c new file mode 100644 index 00000000..f6577a99 --- /dev/null +++ b/ospf6d/ospf6_message.c @@ -0,0 +1,1972 @@ +/* + * 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" + +int +is_ospf6_message_dump (u_char type) +{ + if (type > OSPF6_MESSAGE_TYPE_LSACK) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + + switch (type) + { + case OSPF6_MESSAGE_TYPE_UNKNOWN: + return 1; + break; + case OSPF6_MESSAGE_TYPE_HELLO: + if (IS_OSPF6_DUMP_HELLO) + return 1; + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + if (IS_OSPF6_DUMP_DBDESC) + return 1; + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + if (IS_OSPF6_DUMP_LSREQ) + return 1; + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + if (IS_OSPF6_DUMP_LSUPDATE) + return 1; + break; + case OSPF6_MESSAGE_TYPE_LSACK: + if (IS_OSPF6_DUMP_LSACK) + return 1; + break; + default: + break; + } + return 0; +} +#define IS_OSPF6_DUMP_MESSAGE(x) (is_ospf6_message_dump(x)) + +char *ospf6_message_type_string[] = +{ + "Unknown", "Hello", "DbDesc", "LSReq", "LSUpdate", "LSAck", NULL +}; + +void +ospf6_message_log_lsa_header (struct ospf6_lsa_header *lsa_header) +{ + char buf_id[16], buf_router[16], typebuf[32]; + + inet_ntop (AF_INET, &lsa_header->advrtr, buf_router, sizeof (buf_router)); + inet_ntop (AF_INET, &lsa_header->ls_id, buf_id, sizeof (buf_id)); + zlog_info (" [%s ID=%s Adv=%s]", + ospf6_lsa_type_string (lsa_header->type, typebuf, + sizeof (typebuf)), + buf_id, buf_router); + zlog_info (" Age=%hu SeqNum=%#lx Cksum=%#hx Len=%hu", + ntohs (lsa_header->age), (u_long)ntohl (lsa_header->seqnum), + ntohs (lsa_header->checksum), ntohs (lsa_header->length)); +} + +static void +ospf6_message_log_unknown (struct iovec *message) +{ + zlog_info ("Message: Unknown"); +} + +static void +ospf6_message_log_hello (struct iovec *message) +{ + struct ospf6_header *ospf6_header; + u_int16_t length_left; + struct ospf6_hello *hello; + char dr_str[16], bdr_str[16]; + char *start, *end, *current; + + /* calculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length_left = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length_left = (length_left < iov_totallen (message) - sizeof (struct ospf6_header) ? + length_left : iov_totallen (message) - sizeof (struct ospf6_header)); + + hello = (struct ospf6_hello *) message[1].iov_base; + + inet_ntop (AF_INET, &hello->dr, dr_str, sizeof (dr_str)); + inet_ntop (AF_INET, &hello->bdr, bdr_str, sizeof (bdr_str)); + + zlog_info (" IFID:%ld Priority:%d Option:%s", + (u_long)ntohl (hello->interface_id), hello->rtr_pri, "xxx"); + zlog_info (" HelloInterval:%hu Deadinterval:%hu", + ntohs (hello->hello_interval), + ntohs (hello->router_dead_interval)); + zlog_info (" DR:%s BDR:%s", dr_str, bdr_str); + + start = (char *) (hello + 1); + if (start >= (char *) message[1].iov_base + message[1].iov_len) + start = message[2].iov_base; + end = (char *) start + (length_left - sizeof (struct ospf6_hello)); + + for (current = start; current < end; current += sizeof (u_int32_t)) + { + char neighbor[16]; + inet_ntop (AF_INET, current, neighbor, sizeof (neighbor)); + zlog_info (" Neighbor: %s", neighbor); + } +} + +static void +ospf6_message_log_dbdesc (struct iovec *message) +{ + struct ospf6_header *ospf6_header; + u_int16_t length_left; + struct ospf6_dbdesc *dbdesc; + int i; + char buffer[16]; + struct ospf6_lsa_header *lsa_header; + + /* calculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length_left = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length_left = (length_left < iov_totallen (message) - sizeof (struct ospf6_header) ? + length_left : iov_totallen (message) - sizeof (struct ospf6_header)); + + dbdesc = (struct ospf6_dbdesc *) message[1].iov_base; + ospf6_options_string (dbdesc->options, buffer, sizeof (buffer)); + + zlog_info (" Option:%s IFMTU:%hu", buffer, ntohs (dbdesc->ifmtu)); + zlog_info (" Bits:%s%s%s SeqNum:%#lx", + (DD_IS_IBIT_SET (dbdesc->bits) ? "I" : "-"), + (DD_IS_MBIT_SET (dbdesc->bits) ? "M" : "-"), + (DD_IS_MSBIT_SET (dbdesc->bits) ? "m" : "s"), + (u_long)ntohl (dbdesc->seqnum)); + + for (lsa_header = (struct ospf6_lsa_header *) (dbdesc + 1); + (char *)(lsa_header + 1) <= (char *)(message[1].iov_base + message[1].iov_len) && + (char *)(lsa_header + 1) <= (char *)dbdesc + length_left; + lsa_header++) + ospf6_message_log_lsa_header (lsa_header); + + length_left -= message[1].iov_len; + for (i = 2; message[i].iov_base; i++) + { + for (lsa_header = (struct ospf6_lsa_header *) message[i].iov_base; + (char *)(lsa_header + 1) <= (char *) (message[i].iov_base + + message[i].iov_len) && + (char *)(lsa_header + 1) <= (char *) (message[i].iov_base + length_left); + lsa_header++) + ospf6_message_log_lsa_header (lsa_header); + length_left -= message[i].iov_len; + } +} + +static void +ospf6_message_log_lsreq (struct iovec *message) +{ + struct ospf6_header *ospf6_header; + u_int16_t length_left; + int i; + struct ospf6_lsreq *lsreq; + char buf_router[16], buf_id[16], buf_type[16]; + + /* calculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length_left = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length_left = (length_left < iov_totallen (message) - sizeof (struct ospf6_header) ? + length_left : iov_totallen (message) - sizeof (struct ospf6_header)); + + for (i = 1; message[i].iov_base; i++) + { + for (lsreq = (struct ospf6_lsreq *) message[i].iov_base; + (char *)(lsreq + 1) <= (char *) (message[i].iov_base + message[i].iov_len) && + (char *)(lsreq + 1) <= (char *) (message[i].iov_base + length_left); + lsreq++) + { + inet_ntop (AF_INET, &lsreq->adv_router, buf_router, sizeof (buf_router)); + inet_ntop (AF_INET, &lsreq->id, buf_id, sizeof (buf_id)); + zlog_info (" [%s ID=%s Adv=%s]", + ospf6_lsa_type_string (lsreq->type, buf_type, + sizeof (buf_type)), + buf_id, buf_router); + } + length_left -= message[i].iov_len; + } +} + +static void +ospf6_message_log_lsupdate (struct iovec *message) +{ + struct ospf6_header *ospf6_header; + u_int16_t length_left; + int i, lsanum; + struct ospf6_lsupdate *lsupdate; + struct ospf6_lsa_header *lsa_header; + + /* calculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length_left = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length_left = (length_left < iov_totallen (message) - sizeof (struct ospf6_header) ? + length_left : iov_totallen (message) - sizeof (struct ospf6_header)); + + lsupdate = (struct ospf6_lsupdate *) message[1].iov_base; + lsanum = ntohl (lsupdate->lsupdate_num); + + zlog_info (" Number of LSA: #%d", lsanum); + + for (lsa_header = (struct ospf6_lsa_header *) (lsupdate + 1); + (char *)lsa_header < (char *)(message[1].iov_base + message[1].iov_len) && + (char *)lsa_header < (char *)(message[1].iov_base + length_left); + lsa_header = OSPF6_LSA_NEXT (lsa_header)) + ospf6_message_log_lsa_header (lsa_header); + length_left -= message[1].iov_len; + + for (i = 2; message[i].iov_base; i++) + { + + for (lsa_header = (struct ospf6_lsa_header *) message[i].iov_base; + (char *)lsa_header < (char *) (message[i].iov_base + message[i].iov_len) && + (char *)lsa_header < (char *) (message[i].iov_base + length_left); + lsa_header = OSPF6_LSA_NEXT (lsa_header)) + ospf6_message_log_lsa_header (lsa_header); + length_left -= message[i].iov_len; + } +} + +static void +ospf6_message_log_lsack (struct iovec *message) +{ + struct ospf6_header *ospf6_header; + u_int16_t length_left; + struct ospf6_lsa_header *lsa_header; + int i; + + /* calculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length_left = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length_left = (length_left < iov_totallen (message) - sizeof (struct ospf6_header) ? + length_left : iov_totallen (message) - sizeof (struct ospf6_header)); + + for (i = 1; message[i].iov_base; i++) + { + for (lsa_header = (struct ospf6_lsa_header *) message[i].iov_base; + (char *)(lsa_header + 1) <= (char *) (message[i].iov_base + + message[i].iov_len) && + (char *)(lsa_header + 1) <= (char *) (message[i].iov_base + length_left); + lsa_header++) + ospf6_message_log_lsa_header (lsa_header); + length_left -= message[i].iov_len; + } +} + +struct { + void (*message_log) (struct iovec *); +} ospf6_message_log_body [] = +{ + {ospf6_message_log_unknown}, + {ospf6_message_log_hello}, + {ospf6_message_log_dbdesc}, + {ospf6_message_log_lsreq}, + {ospf6_message_log_lsupdate}, + {ospf6_message_log_lsack}, +}; + +static void +ospf6_message_log (struct iovec *message) +{ + struct ospf6_header *o6h; + char router_id[16], area_id[16]; + u_char type; + + assert (message[0].iov_len == sizeof (struct ospf6_header)); + o6h = (struct ospf6_header *) message[0].iov_base; + + inet_ntop (AF_INET, &o6h->router_id, router_id, sizeof (router_id)); + inet_ntop (AF_INET, &o6h->area_id, area_id, sizeof (area_id)); + + zlog_info (" OSPFv%d Type:%d Len:%hu RouterID:%s", + o6h->version, o6h->type, ntohs (o6h->len), router_id); + zlog_info (" AreaID:%s Cksum:%hx InstanceID:%d", + area_id, ntohs (o6h->cksum), o6h->instance_id); + + type = (OSPF6_MESSAGE_TYPE_UNKNOWN < o6h->type && + o6h->type <= OSPF6_MESSAGE_TYPE_LSACK ? + o6h->type : OSPF6_MESSAGE_TYPE_UNKNOWN); + (* ospf6_message_log_body[type].message_log) (&message[0]); +} + +int +ospf6_opt_is_mismatch (unsigned char opt, char *options1, char *options2) +{ + return (OSPF6_OPT_ISSET (options1, opt) ^ OSPF6_OPT_ISSET (options2, opt)); +} + + +void +ospf6_process_unknown (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + zlog_warn ("unknown message type, drop"); +} + +void +ospf6_process_hello (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + struct ospf6_header *ospf6_header; + u_int16_t length; + struct ospf6_hello *hello; + char changes = 0; +#define CHANGE_RTRPRI (1 << 0) +#define CHANGE_DR (1 << 1) +#define CHANGE_BDR (1 << 2) + int twoway = 0, backupseen = 0, nbchange = 0; + u_int32_t *router_id_ptr; + int i, seenrtrnum = 0, router_id_space = 0; + char strbuf[64]; + struct ospf6_neighbor *o6n = NULL; + + /* assert interface */ + assert (o6i); + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* set hello pointer */ + hello = (struct ospf6_hello *) message[1].iov_base; + + /* find neighbor. if cannot be found, create */ + o6n = ospf6_neighbor_lookup (router_id, o6i); + if (!o6n) + { + o6n = ospf6_neighbor_create (router_id, o6i); + o6n->ifid = ntohl (hello->interface_id); + o6n->prevdr = o6n->dr = hello->dr; + o6n->prevbdr = o6n->bdr = hello->bdr; + o6n->priority = hello->rtr_pri; + memcpy (&o6n->hisaddr, src, sizeof (struct in6_addr)); + } + + /* HelloInterval check */ + if (ntohs (hello->hello_interval) != o6i->hello_interval) + { + zlog_warn ("HelloInterval mismatch with %s", o6n->str); + return; + } + + /* RouterDeadInterval check */ + if (ntohs (hello->router_dead_interval) + != o6i->dead_interval) + { + zlog_warn ("RouterDeadInterval mismatch with %s", o6n->str); + return; + } + + /* check options */ + /* Ebit */ + if (ospf6_opt_is_mismatch (OSPF6_OPT_E, hello->options, o6i->area->options)) + { + zlog_warn ("Ebit mismatch with %s", o6n->str); + return; + } + + /* RouterPriority set */ + if (o6n->priority != hello->rtr_pri) + { + o6n->priority = hello->rtr_pri; + if (IS_OSPF6_DUMP_HELLO) + zlog_info ("%s: RouterPriority changed", o6n->str); + changes |= CHANGE_RTRPRI; + } + + /* DR set */ + if (o6n->dr != hello->dr) + { + /* save previous dr, set current */ + o6n->prevdr = o6n->dr; + o6n->dr = hello->dr; + inet_ntop (AF_INET, &o6n->dr, strbuf, sizeof (strbuf)); + if (IS_OSPF6_DUMP_HELLO) + zlog_info ("%s declare %s as DR", o6n->str, strbuf); + changes |= CHANGE_DR; + } + + /* BDR set */ + if (o6n->bdr != hello->bdr) + { + /* save previous bdr, set current */ + o6n->prevbdr = o6n->bdr; + o6n->bdr = hello->bdr; + inet_ntop (AF_INET, &o6n->bdr, strbuf, sizeof (strbuf)); + if (IS_OSPF6_DUMP_HELLO) + zlog_info ("%s declare %s as BDR", o6n->str, strbuf); + changes |= CHANGE_BDR; + } + + /* TwoWay check */ + router_id_space = length - sizeof (struct ospf6_hello); + seenrtrnum = router_id_space / sizeof (u_int32_t); + router_id_ptr = (u_int32_t *) (hello + 1); + for (i = 0; i < seenrtrnum; i++) + { + if (*router_id_ptr == o6i->area->ospf6->router_id) + twoway++; + router_id_ptr++; + } + + /* execute neighbor events */ + thread_execute (master, hello_received, o6n, 0); + if (twoway) + thread_execute (master, twoway_received, o6n, 0); + else + thread_execute (master, oneway_received, o6n, 0); + + /* BackupSeen check */ + if (o6i->state == IFS_WAITING) + { + if (hello->dr == hello->bdr && + hello->dr == o6n->router_id) + zlog_warn ("*** DR Election of %s is illegal", o6n->str); + + if (hello->bdr == o6n->router_id) + backupseen++; + else if (hello->dr == o6n->router_id && hello->bdr == 0) + backupseen++; + } + + /* NeighborChange check */ + if (changes & CHANGE_RTRPRI) + nbchange++; + if (changes & CHANGE_DR) + if (o6n->prevdr == o6n->router_id || o6n->dr == o6n->router_id) + nbchange++; + if (changes & CHANGE_BDR) + if (o6n->prevbdr == o6n->router_id || o6n->bdr == o6n->router_id) + nbchange++; + + /* schedule interface events */ + if (backupseen) + thread_add_event (master, backup_seen, o6i, 0); + if (nbchange) + thread_add_event (master, neighbor_change, o6i, 0); + + return; +} + +int +ospf6_dbdesc_is_master (struct ospf6_neighbor *o6n) +{ + char buf[128]; + + if (o6n->router_id == ospf6->router_id) + { + inet_ntop (AF_INET6, &o6n->hisaddr, buf, sizeof (buf)); + zlog_warn ("Message: Neighbor router-id conflicts: %s: %s", + o6n->str, buf); + return -1; + } + else if (ntohl (o6n->router_id) > ntohl (ospf6->router_id)) + return 0; + return 1; +} + +int +ospf6_dbdesc_is_duplicate (struct ospf6_dbdesc *received, + struct ospf6_dbdesc *last_received) +{ + if (memcmp (received->options, last_received->options, 3) != 0) + return 0; + if (received->ifmtu != last_received->ifmtu) + return 0; + if (received->bits != last_received->bits) + return 0; + if (received->seqnum != last_received->seqnum) + return 0; + return 1; +} + +void +ospf6_process_dbdesc_master (struct iovec *message, struct ospf6_neighbor *o6n) +{ + struct ospf6_header *ospf6_header; + u_int16_t length, lsa_count; + struct ospf6_dbdesc *dbdesc; + struct ospf6_lsa_header *lsa_header; + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* set database description pointer */ + dbdesc = (struct ospf6_dbdesc *) message[1].iov_base; + + switch (o6n->state) + { + case NBS_DOWN: + case NBS_ATTEMPT: + case NBS_TWOWAY: + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("DbDesc from %s Ignored: state less than Init", + o6n->str); + return; + + case NBS_INIT: + thread_execute (master, twoway_received, o6n, 0); + if (o6n->state != NBS_EXSTART) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("DbDesc from %s Ignored: state less than ExStart", + o6n->str); + return; + } + /* else fall through to ExStart */ + case NBS_EXSTART: + if (DDBIT_IS_SLAVE (dbdesc->bits) && + !DDBIT_IS_INITIAL (dbdesc->bits) && + ntohl (dbdesc->seqnum) == o6n->dbdesc_seqnum) + { + ospf6_neighbor_dbex_init (o6n); + + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = (struct thread *) NULL; + + thread_add_event (master, negotiation_done, o6n, 0); + } + else + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" negotiation failed with %s", o6n->str); + return; + } + break; + + case NBS_EXCHANGE: + /* duplicate dbdesc dropped by master */ + if (!memcmp (dbdesc, &o6n->last_dd, + sizeof (struct ospf6_dbdesc))) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" duplicate dbdesc, drop"); + return; + } + + /* check Initialize bit and Master/Slave bit */ + if (DDBIT_IS_INITIAL (dbdesc->bits)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("Initialize bit mismatch"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + if (DDBIT_IS_MASTER (dbdesc->bits)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("Master/Slave bit mismatch"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + + /* dbdesc option check */ + if (memcmp (dbdesc->options, o6n->last_dd.options, + sizeof (dbdesc->options))) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("dbdesc option field changed"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + + /* dbdesc sequence number check */ + if (ntohl (dbdesc->seqnum) != o6n->dbdesc_seqnum) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_warn ("*** dbdesc seqnumber mismatch: %d expected", + o6n->dbdesc_seqnum); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + break; + + case NBS_LOADING: + case NBS_FULL: + /* duplicate dbdesc dropped by master */ + if (ospf6_dbdesc_is_duplicate (dbdesc, &o6n->last_dd)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" duplicate dbdesc, drop"); + return; + } + else + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" not duplicate dbdesc in state %s", + ospf6_neighbor_state_string[o6n->state]); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + break; /* not reached */ + + default: + assert (0); + break; /* not reached */ + } + + /* process LSA headers */ + lsa_count = 0; + for (lsa_header = (struct ospf6_lsa_header *) (dbdesc + 1); + (char *)(lsa_header + 1) <= (char *)dbdesc + length; + lsa_header++) + { + if (ospf6_dbex_check_dbdesc_lsa_header (lsa_header, o6n) < 0) + { + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + lsa_count ++; + } + + /* increment dbdesc seqnum */ + o6n->dbdesc_seqnum++; + + /* cancel transmission/retransmission thread */ + if (o6n->thread_send_dbdesc) + thread_cancel (o6n->thread_send_dbdesc); + o6n->thread_send_dbdesc = (struct thread *) NULL; + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = (struct thread *) NULL; + + /* more bit check */ + if (!DD_IS_MBIT_SET (dbdesc->bits) && !DD_IS_MBIT_SET (o6n->dbdesc_bits)) + thread_add_event (master, exchange_done, o6n, 0); + else + o6n->thread_send_dbdesc = + thread_add_event (master, ospf6_send_dbdesc, o6n, 0); + + /* save last received dbdesc */ + memcpy (&o6n->last_dd, dbdesc, sizeof (struct ospf6_dbdesc)); + + /* statistics */ + o6n->lsa_receive[OSPF6_MESSAGE_TYPE_DBDESC] += lsa_count; + + return; +} + +void +ospf6_process_dbdesc_slave (struct iovec *message, struct ospf6_neighbor *o6n) +{ + struct ospf6_header *ospf6_header; + u_int16_t length, lsa_count; + struct ospf6_dbdesc *dbdesc; + struct ospf6_lsa_header *lsa_header; + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* set database description pointer */ + dbdesc = (struct ospf6_dbdesc *) message[1].iov_base; + + switch (o6n->state) + { + case NBS_DOWN: + case NBS_ATTEMPT: + case NBS_TWOWAY: + return; + case NBS_INIT: + thread_execute (master, twoway_received, o6n, 0); + if (o6n->state != NBS_EXSTART) + { + return; + } + /* else fall through to ExStart */ + case NBS_EXSTART: + if (DD_IS_IBIT_SET (dbdesc->bits) && + DD_IS_MBIT_SET (dbdesc->bits) && + DD_IS_MSBIT_SET (dbdesc->bits)) + { + /* Master/Slave bit set to slave */ + DD_MSBIT_CLEAR (o6n->dbdesc_bits); + /* Initialize bit clear */ + DD_IBIT_CLEAR (o6n->dbdesc_bits); + /* sequence number set to master's */ + o6n->dbdesc_seqnum = ntohl (dbdesc->seqnum); + ospf6_neighbor_dbex_init (o6n); + + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = (struct thread *) NULL; + + thread_add_event (master, negotiation_done, o6n, 0); + } + else + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("negotiation failed"); + return; + } + break; + + case NBS_EXCHANGE: + /* duplicate dbdesc dropped by master */ + if (!memcmp (dbdesc, &o6n->last_dd, + sizeof (struct ospf6_dbdesc))) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" duplicate dbdesc, retransmit dbdesc"); + + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = + thread_add_event (master, ospf6_send_dbdesc_rxmt, o6n, 0); + + return; + } + + /* check Initialize bit and Master/Slave bit */ + if (DDBIT_IS_INITIAL (dbdesc->bits)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("Initialize bit mismatch"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + if (DDBIT_IS_SLAVE (dbdesc->bits)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("Master/Slave bit mismatch"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + + /* dbdesc option check */ + if (memcmp (dbdesc->options, o6n->last_dd.options, + sizeof (dbdesc->options))) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("dbdesc option field changed"); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + + /* dbdesc sequence number check */ + if (ntohl (dbdesc->seqnum) != o6n->dbdesc_seqnum + 1) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_warn ("*** dbdesc seqnumber mismatch: %d expected", + o6n->dbdesc_seqnum + 1); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + break; + + case NBS_LOADING: + case NBS_FULL: + /* duplicate dbdesc cause slave to retransmit */ + if (ospf6_dbdesc_is_duplicate (dbdesc, &o6n->last_dd)) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" duplicate dbdesc, retransmit"); + + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = + thread_add_event (master, ospf6_send_dbdesc_rxmt, o6n, 0); + + return; + } + else + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info (" not duplicate dbdesc in state %s", + ospf6_neighbor_state_string[o6n->state]); + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + break; /* not reached */ + + default: + assert (0); + break; /* not reached */ + } + + /* process LSA headers */ + lsa_count = 0; + for (lsa_header = (struct ospf6_lsa_header *) (dbdesc + 1); + (char *)(lsa_header + 1) <= (char *)dbdesc + length; + lsa_header++) + { + if (ospf6_dbex_check_dbdesc_lsa_header (lsa_header, o6n) < 0) + { + thread_add_event (master, seqnumber_mismatch, o6n, 0); + return; + } + lsa_count ++; + } + + /* set dbdesc seqnum to master's */ + o6n->dbdesc_seqnum = ntohl (dbdesc->seqnum); + + if (o6n->thread_send_dbdesc) + thread_cancel (o6n->thread_send_dbdesc); + o6n->thread_send_dbdesc = + thread_add_event (master, ospf6_send_dbdesc, o6n, 0); + + /* save last received dbdesc */ + memcpy (&o6n->last_dd, dbdesc, sizeof (struct ospf6_dbdesc)); + + /* statistics */ + o6n->lsa_receive[OSPF6_MESSAGE_TYPE_DBDESC] += lsa_count; + + return; +} + +void +ospf6_process_dbdesc (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + struct ospf6_header *ospf6_header; + u_int16_t length; + struct ospf6_neighbor *o6n; + struct ospf6_dbdesc *dbdesc; + int Im_master = 0; + + /* assert interface */ + assert (o6i); + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* set database description pointer */ + dbdesc = (struct ospf6_dbdesc *) message[1].iov_base; + + /* find neighbor. if cannot be found, reject this message */ + o6n = ospf6_neighbor_lookup (router_id, o6i); + if (!o6n) + { + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("neighbor not found, reject"); + return; + } + + if (memcmp (src, &o6n->hisaddr, sizeof (struct in6_addr))) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("From Secondary I/F of the neighbor: ignore"); + return; + } + + /* interface mtu check */ + /* xxx */ + + /* check am I master */ + Im_master = ospf6_dbdesc_is_master (o6n); + if (Im_master < 0) + { + return; /* can't decide which is master, return */ + } + + if (Im_master) + ospf6_process_dbdesc_master (message, o6n); + else + ospf6_process_dbdesc_slave (message, o6n); + + return; +} + +void +ospf6_process_lsreq (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + struct ospf6_header *ospf6_header; + u_int16_t length; + struct ospf6_neighbor *o6n; + struct ospf6_lsreq *lsreq; + struct iovec response[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsa *lsa; + unsigned long lsanum = 0; + struct ospf6_lsupdate lsupdate; + char buf_id[16], buf_router[16], buf_type[16]; + + /* assert interface */ + assert (o6i); + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* find neighbor. if cannot be found, reject this message */ + o6n = ospf6_neighbor_lookup (router_id, o6i); + if (!o6n) + { + if (IS_OSPF6_DUMP_LSREQ) + zlog_info (" neighbor not found, reject"); + return; + } + + if (memcmp (src, &o6n->hisaddr, sizeof (struct in6_addr))) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("From Secondary I/F of the neighbor: ignore"); + return; + } + + /* In states other than ExChange, Loading, or Full, the packet + should be ignored. */ + if (o6n->state != NBS_EXCHANGE && o6n->state != NBS_LOADING + && o6n->state != NBS_FULL) + { + if (IS_OSPF6_DUMP_LSREQ) + zlog_info (" neighbor state less than Exchange, reject"); + return; + } + + /* Initialize response LSUpdate packet */ + OSPF6_MESSAGE_CLEAR (response); + memset (&lsupdate, 0, sizeof (struct ospf6_lsupdate)); + OSPF6_MESSAGE_ATTACH (response, &lsupdate, sizeof (struct ospf6_lsupdate)); + + /* process each request */ + lsanum = 0; + for (lsreq = (struct ospf6_lsreq *) message[1].iov_base; + (char *)(lsreq + 1) <= (char *)(message[1].iov_base + length); + lsreq++) + { + inet_ntop (AF_INET, &lsreq->adv_router, buf_router, sizeof (buf_router)); + inet_ntop (AF_INET, &lsreq->id, buf_id, sizeof (buf_id)); + + /* find instance of database copy */ + lsa = ospf6_lsdb_lookup (lsreq->type, lsreq->id, lsreq->adv_router, + ospf6_lsa_get_scope (lsreq->type, o6i)); + + if (!lsa) + { + if (IS_OSPF6_DUMP_LSREQ) + zlog_info ("BadLSReq: %s requests [%s ID=%s Adv=%s] not found", + o6n->str, ospf6_lsa_type_string (lsreq->type, buf_type, + sizeof (buf_type)), + buf_id, buf_router); + thread_add_event (master, bad_lsreq, o6n, 0); + return; + } + + /* I/F MTU check */ + if (sizeof (struct ospf6_header) + sizeof (struct ospf6_lsupdate) + + iov_totallen (response) + ntohs (lsa->header->length) + > o6i->ifmtu) + break; + + OSPF6_MESSAGE_ATTACH (response, lsa->header, ntohs (lsa->header->length)); + lsanum++; + } + + /* send response LSUpdate to this request */ + if (lsanum) + { + lsupdate.lsupdate_num = htonl (lsanum); + + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSUPDATE, response, + &o6n->hisaddr, o6i->if_id); + } + + /* statistics */ + o6n->lsa_receive[OSPF6_MESSAGE_TYPE_LSREQ] + += length / sizeof (struct ospf6_lsreq); +} + +void +ospf6_process_lsupdate (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + struct ospf6_header *ospf6_header; + u_int16_t length; + struct ospf6_lsupdate *lsupdate; + struct ospf6_neighbor *o6n; + unsigned long lsanum; + struct ospf6_lsa_header *lsa_header; + + /* assert interface */ + assert (o6i); + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* find neighbor. if cannot be found, reject this message */ + o6n = ospf6_neighbor_lookup (router_id, o6i); + if (! o6n) + { + if (IS_OSPF6_DUMP_LSUPDATE) + zlog_info (" neighbor not found, reject"); + return; + } + + if (memcmp (src, &o6n->hisaddr, sizeof (struct in6_addr))) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("From Secondary I/F of the neighbor: ignore"); + return; + } + + /* if neighbor state less than ExChange, reject this message */ + if (o6n->state < NBS_EXCHANGE) + { + if (IS_OSPF6_DUMP_LSUPDATE) + zlog_info (" neighbor state less than Exchange, reject"); + return; + } + + /* set linkstate update pointer */ + lsupdate = (struct ospf6_lsupdate *) message[1].iov_base; + + /* save linkstate update info */ + lsanum = ntohl (lsupdate->lsupdate_num); + + /* statistics */ + o6n->ospf6_stat_received_lsa += lsanum; + o6n->ospf6_stat_received_lsupdate++; + + /* RFC2328 Section 10.9: When the neighbor responds to these requests + with the proper Link State Update packet(s), the Link state request + list is truncated and a new Link State Request packet is sent. */ + + /* process LSAs */ + for (lsa_header = (struct ospf6_lsa_header *) (lsupdate + 1); + lsanum && (char *)lsa_header < (char *)lsupdate + length; + lsanum--) + { + ospf6_dbex_receive_lsa (lsa_header, o6n); + lsa_header = OSPF6_LSA_NEXT (lsa_header); + } + + /* send new Link State Request packet if this LS Update packet + can be recognized as a response to our previous LS request */ + if (! IN6_IS_ADDR_MULTICAST(dst) && + (o6n->state == NBS_EXCHANGE || o6n->state == NBS_LOADING)) + thread_add_event (master, ospf6_send_lsreq, o6n, 0); + + return; +} + +void +ospf6_process_lsack (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i, + u_int32_t router_id) +{ + struct ospf6_header *ospf6_header; + u_int16_t length; + struct ospf6_neighbor *o6n; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa, *copy, *rem; + + /* assert interface */ + assert (o6i); + + /* caluculate length */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + length = ntohs (ospf6_header->len) - sizeof (struct ospf6_header); + length = (length < message[1].iov_len ? length : message[1].iov_len); + + /* find neighbor. if cannot be found, reject this message */ + o6n = ospf6_neighbor_lookup (router_id, o6i); + if (!o6n) + { + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: neighbor not found, reject"); + return; + } + + if (memcmp (src, &o6n->hisaddr, sizeof (struct in6_addr))) + { + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: From Secondary I/F of the neighbor: ignore"); + return; + } + + /* if neighbor state less than ExChange, reject this message */ + if (o6n->state < NBS_EXCHANGE) + { + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: neighbor state less than Exchange, reject"); + return; + } + + /* process each LSA header */ + for (lsa_header = (struct ospf6_lsa_header *) message[1].iov_base; + (char *)(lsa_header + 1) <= (char *)(message[1].iov_base + length); + lsa_header++) + { + /* find database copy */ + copy = ospf6_lsdb_lookup (lsa_header->type, lsa_header->ls_id, + lsa_header->advrtr, + ospf6_lsa_get_scope (lsa_header->type, o6i)); + + /* if no database copy */ + if (!copy) + { + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: no database copy, ignore"); + continue; + } + + /* if not on his retrans list */ + rem = ospf6_lsdb_lookup_lsdb (copy->header->type, copy->header->id, + copy->header->adv_router, + o6n->retrans_list); + if (rem == NULL) + { + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: not on %s's retranslist, ignore", o6n->str); + continue; + } + + /* create temporary LSA from Ack message */ + lsa = ospf6_lsa_summary_create ((struct ospf6_lsa_header__ *) lsa_header); + + /* if the same instance, remove from retrans list. + else, log and ignore */ + if (ospf6_lsa_check_recent (lsa, copy) == 0) + ospf6_neighbor_retrans_remove (rem, o6n); + else + { + /* Log the questionable acknowledgement, + and examine the next one. */ + zlog_info ("LSACK: questionable acknowledge: %s", copy->str); + zlog_info ("LSACK: received: seq: %#x age: %hu", + ntohl (lsa->header->seqnum), + ntohs (lsa->header->age)); + zlog_info ("LSACK: instance: seq: %#x age: %hu", + ntohl (copy->header->seqnum), + ospf6_lsa_age_current (copy)); + } + + /* release temporary LSA from Ack message */ + ospf6_lsa_delete (lsa); + } + + ospf6_maxage_remover (); + return; +} + +struct { + void (*process) (struct iovec *, struct in6_addr *, struct in6_addr *, + struct ospf6_interface *, u_int32_t); +} ospf6_message_process_type [] = +{ + {ospf6_process_unknown}, + {ospf6_process_hello}, + {ospf6_process_dbdesc}, + {ospf6_process_lsreq}, + {ospf6_process_lsupdate}, + {ospf6_process_lsack} +}; + +/* process ospf6 protocol header. then, call next process function + for each message type */ +static void +ospf6_message_process (struct iovec *message, + struct in6_addr *src, + struct in6_addr *dst, + struct ospf6_interface *o6i) +{ + struct ospf6_header *ospf6_header = NULL; + u_char type; + u_int32_t router_id; + char srcname[64]; + + assert (o6i); + assert (src); + assert (dst); + + /* set ospf6_hdr pointer to head of buffer */ + ospf6_header = (struct ospf6_header *) message[0].iov_base; + + /* version check */ + if (ospf6_header->version != OSPF6_VERSION) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("version mismatch, drop"); + return; + } + + /* area id check */ + if (ospf6_header->area_id != o6i->area->area_id) + { + if (ospf6_header->area_id == 0) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("virtual link not yet, drop"); + return; + } + + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("area id mismatch, drop"); + return; + } + + /* instance id check */ + if (ospf6_header->instance_id != o6i->instance_id) + { + if (IS_OSPF6_DUMP_MESSAGE (ospf6_header->type)) + zlog_info ("instance id mismatch, drop"); + return; + } + + /* message type check */ + type = (ospf6_header->type >= OSPF6_MESSAGE_TYPE_MAX ? + OSPF6_MESSAGE_TYPE_UNKNOWN : ospf6_header->type); + + /* log */ + if (IS_OSPF6_DUMP_MESSAGE (type)) + { + char srcname[64], dstname[64]; + inet_ntop (AF_INET6, dst, dstname, sizeof (dstname)); + inet_ntop (AF_INET6, src, srcname, sizeof (srcname)); + zlog_info ("Receive %s on %s", + ospf6_message_type_string[type], o6i->interface->name); + zlog_info (" %s -> %s", srcname, dstname); + ospf6_message_log (message); + } + + /* router id check */ + router_id = ospf6_header->router_id; + if (ospf6_header->router_id == o6i->area->ospf6->router_id) + { + inet_ntop (AF_INET6, src, srcname, sizeof (srcname)); + zlog_warn ("*** Router-ID mismatch: from %s on %s", + srcname, o6i->interface->name); + return; + } + + /* octet statistics relies on some asumption: + on ethernet, no IPv6 Extention header, etc */ +#define OSPF6_IP6_HEADER_SIZE 40 +#define OSPF6_ETHER_HEADER_SIZE 14 + o6i->message_stat[type].recv++; + o6i->message_stat[type].recv_octet += ntohs (ospf6_header->len) + + OSPF6_IP6_HEADER_SIZE + OSPF6_ETHER_HEADER_SIZE; + + /* futher process */ + (*ospf6_message_process_type[type].process) (&message[0], src, dst, o6i, router_id); + + return; +} + +int +ospf6_receive (struct thread *thread) +{ + int sockfd; + struct in6_addr src, dst; + unsigned int ifindex; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_header ospf6_header; + char buffer[OSPF6_MESSAGE_RECEIVE_BUFSIZE]; + struct ospf6_interface *o6i; + unsigned char type; + + /* get socket */ + sockfd = THREAD_FD (thread); + + /* add next read thread */ + thread_add_read (master, ospf6_receive, NULL, sockfd); + + /* initialize */ + OSPF6_MESSAGE_CLEAR (message); + memset (&ospf6_header, 0, sizeof (struct ospf6_header)); + + OSPF6_MESSAGE_ATTACH (message, &ospf6_header, sizeof (struct ospf6_header)); + OSPF6_MESSAGE_ATTACH (message, buffer, OSPF6_MESSAGE_RECEIVE_BUFSIZE); + + /* receive message */ + ospf6_recvmsg (&src, &dst, &ifindex, message); + + type = (OSPF6_MESSAGE_TYPE_UNKNOWN < ospf6_header.type && + ospf6_header.type <= OSPF6_MESSAGE_TYPE_LSACK ? + ospf6_header.type : OSPF6_MESSAGE_TYPE_UNKNOWN); + o6i = ospf6_interface_lookup_by_index (ifindex); + if (!o6i || !o6i->area) + { + //zlog_warn ("*** received interface ospf6 disabled"); + return 0; + } + + /* if not passive, process message */ + if (! CHECK_FLAG (o6i->flag, OSPF6_INTERFACE_FLAG_PASSIVE)) + ospf6_message_process (message, &src, &dst, o6i); + else if (IS_OSPF6_DUMP_MESSAGE (type)) + zlog_info ("Ignore message on passive interface %s", + o6i->interface->name); + + return 0; +} + + +/* send section */ +int +ospf6_message_length (struct iovec *message) +{ + int i, length = 0; + for (i = 0; i < OSPF6_MESSAGE_IOVEC_SIZE; i++) + { + if (message[i].iov_base == NULL && message[i].iov_len == 0) + break; + length += message[i].iov_len; + } + return length; +} +#define OSPF6_MESSAGE_LENGTH(msg) \ +(ospf6_message_length (msg)) + +void +ospf6_message_send (unsigned char type, struct iovec *msg, + struct in6_addr *dst, u_int ifindex) +{ + struct ospf6_interface *o6i; + struct ospf6_header ospf6_header; + char dst_name[64], src_name[64]; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + int msg_len; + + /* ospf6 interface lookup */ + o6i = ospf6_interface_lookup_by_index (ifindex); + assert (o6i); + + msg_len = OSPF6_MESSAGE_LENGTH (msg); + + /* I/F MTU check */ +#if 0 + if (msg_len + sizeof (struct ospf6_header) >= o6i->interface->mtu) +#else + if (msg_len + sizeof (struct ospf6_header) >= o6i->ifmtu) +#endif + { + /* If Interface MTU is 0, save the case + since zebra had been failed to get MTU from Kernel */ + if (o6i->interface->mtu != 0) + { + zlog_warn ("Message: Send failed on %s: exceeds I/F MTU", + o6i->interface->name); + zlog_warn ("Message: while sending %s: Len:%d MTU:%d", + ospf6_message_type_string[type], + msg_len + sizeof (struct ospf6_header), + o6i->ifmtu); + return; + } + else + { + zlog_warn ("Message: I/F MTU check ignored on %s", + o6i->interface->name); + } + } + + /* Initialize */ + OSPF6_MESSAGE_CLEAR (message); + + /* set OSPF header */ + memset (&ospf6_header, 0, sizeof (ospf6_header)); + ospf6_header.version = OSPF6_VERSION; + ospf6_header.type = type; + ospf6_header.len = htons (msg_len + sizeof (struct ospf6_header)); + ospf6_header.router_id = ospf6->router_id; + ospf6_header.area_id = o6i->area->area_id; + /* checksum is calculated by kernel */ + ospf6_header.instance_id = o6i->instance_id; + ospf6_header.reserved = 0; + OSPF6_MESSAGE_ATTACH (message, &ospf6_header, sizeof (struct ospf6_header)); + + /* Attach rest to message */ + OSPF6_MESSAGE_JOIN (message, msg); + + /* statistics */ + if (type >= OSPF6_MESSAGE_TYPE_MAX) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + o6i->message_stat[type].send++; + o6i->message_stat[type].send_octet += ntohs (ospf6_header.len); + + /* log */ + if (IS_OSPF6_DUMP_MESSAGE (type)) + { + inet_ntop (AF_INET6, dst, dst_name, sizeof (dst_name)); + if (o6i->lladdr) + inet_ntop (AF_INET6, o6i->lladdr, src_name, sizeof (src_name)); + else + memcpy (src_name, "Unknown", sizeof (src_name)); + zlog_info ("Send %s on %s", + ospf6_message_type_string[type], o6i->interface->name); + zlog_info (" %s -> %s", src_name, dst_name); + ospf6_message_log (message); + } + + /* send message */ + ospf6_sendmsg (o6i->lladdr, dst, &ifindex, message); +} + + +int +ospf6_send_hello (struct thread *thread) +{ + listnode n; + struct ospf6_interface *o6i; + struct ospf6_neighbor *o6n; + struct in6_addr dst; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_hello hello; + char router_buffer[1024]; /* xxx */ + u_int router_size; + + /* which ospf6 interface to send */ + o6i = (struct ospf6_interface *) THREAD_ARG (thread); + o6i->thread_send_hello = (struct thread *) NULL; + + /* assure interface is up */ + if (o6i->state <= IFS_DOWN) + { + if (IS_OSPF6_DUMP_HELLO) + zlog_warn ("Send HELLO Failed: Interface not enabled: %s", + o6i->interface->name); + return 0; + } + + /* clear message buffer */ + OSPF6_MESSAGE_CLEAR (message); + + /* set Hello fields */ + hello.interface_id = htonl (o6i->if_id); + hello.rtr_pri = o6i->priority; + memcpy (hello.options, o6i->area->options, sizeof (hello.options)); + hello.hello_interval = htons (o6i->hello_interval); + hello.router_dead_interval = htons (o6i->dead_interval); + hello.dr = o6i->dr; + hello.bdr = o6i->bdr; + OSPF6_MESSAGE_ATTACH (message, &hello, sizeof (struct ospf6_hello)); + + /* set neighbor router id */ + router_size = 0; + for (n = listhead (o6i->neighbor_list); n; nextnode (n)) + { + o6n = (struct ospf6_neighbor *) getdata (n); + + if (o6n->state < NBS_INIT) + continue; + + if (router_size + sizeof (o6n->router_id) > sizeof (router_buffer)) + { + zlog_warn ("Send HELLO: Buffer shortage on %s", + o6i->interface->name); + break; + } + + /* Copy Router-ID to Buffer */ + memcpy (router_buffer + router_size, &o6n->router_id, + sizeof (o6n->router_id)); + router_size += sizeof (o6n->router_id); + } + OSPF6_MESSAGE_ATTACH (message, router_buffer, router_size); + + /* set destionation */ + inet_pton (AF_INET6, ALLSPFROUTERS6, &dst); + + /* send hello */ + ospf6_message_send (OSPF6_MESSAGE_TYPE_HELLO, message, &dst, + o6i->interface->ifindex); + + /* set next timer thread */ + o6i->thread_send_hello = thread_add_timer (master, ospf6_send_hello, + o6i, o6i->hello_interval); + + return 0; +} + +void +ospf6_dbdesc_seqnum_init (struct ospf6_neighbor *o6n) +{ + struct timeval tv; + + if (gettimeofday (&tv, (struct timezone *) NULL) < 0) + tv.tv_sec = 1; + + o6n->dbdesc_seqnum = tv.tv_sec; + + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("set dbdesc seqnum %d for %s", o6n->dbdesc_seqnum, o6n->str); +} + +int +ospf6_send_dbdesc_rxmt (struct thread *thread) +{ + struct ospf6_lsdb_node node; + struct ospf6_neighbor *o6n; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsa *lsa; + struct ospf6_lsa_header *lsa_header; + struct ospf6_dbdesc dbdesc; + + o6n = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (o6n); + + /* clear thread */ + o6n->thread_rxmt_dbdesc = (struct thread *) NULL; + + /* if state less than ExStart, do nothing */ + if (o6n->state < NBS_EXSTART) + return 0; + + OSPF6_MESSAGE_CLEAR (message); + + /* set dbdesc */ + memcpy (dbdesc.options, o6n->ospf6_interface->area->options, + sizeof (dbdesc.options)); + dbdesc.ifmtu = htons (o6n->ospf6_interface->interface->mtu); + dbdesc.bits = o6n->dbdesc_bits; + dbdesc.seqnum = htonl (o6n->dbdesc_seqnum); + OSPF6_MESSAGE_ATTACH (message, &dbdesc, sizeof (struct ospf6_dbdesc)); + + /* if this is not initial, set LSA summary to dbdesc */ + if (! DD_IS_IBIT_SET (o6n->dbdesc_bits)) + { + for (ospf6_lsdb_head (&node, o6n->dbdesc_list); + ! ospf6_lsdb_is_end (&node); ospf6_lsdb_next (&node)) + { + lsa = node.lsa; + + /* xxx, no MTU check: no support for Dynamic MTU change */ + + /* set age and add InfTransDelay */ + ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay); + + /* set LSA summary to send buffer */ + lsa_header = (struct ospf6_lsa_header *) lsa->lsa_hdr; + OSPF6_MESSAGE_ATTACH (message, lsa_header, + sizeof (struct ospf6_lsa_header)); + } + } + + /* send dbdesc */ + ospf6_message_send (OSPF6_MESSAGE_TYPE_DBDESC, message, &o6n->hisaddr, + o6n->ospf6_interface->interface->ifindex); + + /* if master, set futher retransmission */ + if (DD_IS_MSBIT_SET (o6n->dbdesc_bits)) + o6n->thread_rxmt_dbdesc = + thread_add_timer (master, ospf6_send_dbdesc_rxmt, + o6n, o6n->ospf6_interface->rxmt_interval); + + /* statistics */ + o6n->ospf6_stat_retrans_dbdesc++; + + return 0; +} + +int +ospf6_send_dbdesc (struct thread *thread) +{ + struct ospf6_neighbor *o6n; + struct ospf6_lsa *lsa; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_dbdesc dbdesc; + struct ospf6_lsdb_node node; + + o6n = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (o6n); + + /* clear thread */ + o6n->thread_send_dbdesc = (struct thread *) NULL; + if (o6n->thread_rxmt_dbdesc) + thread_cancel (o6n->thread_rxmt_dbdesc); + o6n->thread_rxmt_dbdesc = (struct thread *) NULL; + + /* if state less than ExStart, do nothing */ + if (o6n->state < NBS_EXSTART) + return 0; + + OSPF6_MESSAGE_CLEAR (message); + OSPF6_MESSAGE_ATTACH (message, &dbdesc, sizeof (struct ospf6_dbdesc)); + + /* clear previous LSA summary sent */ + ospf6_lsdb_remove_all (o6n->dbdesc_list); + assert (o6n->dbdesc_list->count == 0); + + /* if this is not initial, set LSA summary to dbdesc */ + if (! DD_IS_IBIT_SET (o6n->dbdesc_bits)) + { + for (ospf6_lsdb_head (&node, o6n->summary_list); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + { + lsa = node.lsa; + + /* MTU check */ + if (OSPF6_MESSAGE_LENGTH (message) + + sizeof (struct ospf6_lsa_header) + + sizeof (struct ospf6_header) + > o6n->ospf6_interface->ifmtu) + break; + + /* debug */ + if (IS_OSPF6_DUMP_DBDESC) + zlog_info ("Include DbDesc: %s", lsa->str); + + /* attach to dbdesclist */ + ospf6_neighbor_dbdesc_add (lsa, o6n); + /* detach from summarylist */ + ospf6_neighbor_summary_remove (lsa, o6n); + + /* set age and add InfTransDelay */ + ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay); + + /* set LSA summary to send buffer */ + OSPF6_MESSAGE_ATTACH (message, lsa->header, + sizeof (struct ospf6_lsa_header)); + } + + if (o6n->summary_list->count == 0) + { + /* Clear more bit */ + DD_MBIT_CLEAR (o6n->dbdesc_bits); + + /* slave must schedule ExchangeDone on sending, here */ + if (! DD_IS_MSBIT_SET (o6n->dbdesc_bits)) + { + if (! DD_IS_MBIT_SET (o6n->dbdesc_bits) && + ! DD_IS_MBIT_SET (o6n->last_dd.bits)) + thread_add_event (master, exchange_done, o6n, 0); + } + } + } + + /* if this is initial, set seqnum */ + if (DDBIT_IS_INITIAL (o6n->dbdesc_bits)) + ospf6_dbdesc_seqnum_init (o6n); + + /* set dbdesc */ + memcpy (dbdesc.options, o6n->ospf6_interface->area->options, + sizeof (dbdesc.options)); + dbdesc.ifmtu = htons (o6n->ospf6_interface->interface->mtu); + dbdesc.bits = o6n->dbdesc_bits; + dbdesc.seqnum = htonl (o6n->dbdesc_seqnum); + + /* send dbdesc */ + ospf6_message_send (OSPF6_MESSAGE_TYPE_DBDESC, message, &o6n->hisaddr, + o6n->ospf6_interface->interface->ifindex); + + /* if master, set retransmission */ + if (DD_IS_MSBIT_SET (o6n->dbdesc_bits)) + o6n->thread_rxmt_dbdesc = + thread_add_timer (master, ospf6_send_dbdesc_rxmt, + o6n, o6n->ospf6_interface->rxmt_interval); + + /* statistics */ + o6n->lsa_send[OSPF6_MESSAGE_TYPE_DBDESC] += o6n->dbdesc_list->count; + + return 0; +} + +int +ospf6_send_lsreq_rxmt (struct thread *thread) +{ + struct ospf6_neighbor *o6n; + + o6n = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (o6n); + + o6n->thread_rxmt_lsreq = (struct thread *) NULL; + o6n->thread_send_lsreq = thread_add_event (master, ospf6_send_lsreq, o6n, 0); + return 0; +} + +int +ospf6_send_lsreq (struct thread *thread) +{ + struct ospf6_neighbor *o6n; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsreq lsreq[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsa *lsa; + struct ospf6_lsdb_node node; + int i; + + o6n = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (o6n); + + /* LSReq will be send only in ExStart or Loading */ + if (o6n->state != NBS_EXCHANGE && o6n->state != NBS_LOADING) + return 0; + + /* clear thread */ + o6n->thread_send_lsreq = (struct thread *) NULL; + if (o6n->thread_rxmt_lsreq) + thread_cancel (o6n->thread_rxmt_lsreq); + o6n->thread_rxmt_lsreq = (struct thread *) NULL; + + /* schedule loading_done if request list is empty */ + if (o6n->request_list->count == 0) + { + thread_add_event (master, loading_done, o6n, 0); + return 0; + } + + /* clear message buffer */ + OSPF6_MESSAGE_CLEAR (message); + + i = 0; + for (ospf6_lsdb_head (&node, o6n->request_list); + ! ospf6_lsdb_is_end (&node); ospf6_lsdb_next (&node)) + { + lsa = node.lsa; + + /* Buffer Overflow */ + if (i >= OSPF6_MESSAGE_IOVEC_SIZE) + break; + + /* I/F MTU check */ + if (OSPF6_MESSAGE_LENGTH (message) + + sizeof (struct ospf6_lsreq) + + sizeof (struct ospf6_header) + > o6n->ospf6_interface->ifmtu) + break; + + lsreq[i].mbz = 0; + lsreq[i].type = lsa->header->type; + lsreq[i].id = lsa->header->id; + lsreq[i].adv_router = lsa->header->adv_router; + + OSPF6_MESSAGE_ATTACH (message, &lsreq[i], sizeof (struct ospf6_lsreq)); + i++; + } + + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSREQ, message, &o6n->hisaddr, + o6n->ospf6_interface->interface->ifindex); + + /* set retransmit thread */ + o6n->thread_rxmt_lsreq = + thread_add_timer (master, ospf6_send_lsreq_rxmt, + o6n, o6n->ospf6_interface->rxmt_interval); + + /* statistics */ + o6n->lsa_send[OSPF6_MESSAGE_TYPE_LSREQ] += i; + + return 0; +} + +/* Send LSUpdate directly to the neighbor, from his retransmission list */ +int +ospf6_send_lsupdate_rxmt (struct thread *thread) +{ + struct ospf6_neighbor *o6n; + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsupdate lsupdate; + struct ospf6_lsa *lsa; + struct ospf6_lsdb_node node; + + o6n = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (o6n); + + o6n->send_update = (struct thread *) NULL; + + if (o6n->ospf6_interface->state <= IFS_WAITING) + return -1; + + /* clear message buffer */ + OSPF6_MESSAGE_CLEAR (message); + + /* set lsupdate header */ + lsupdate.lsupdate_num = 0; /* set gradually */ + OSPF6_MESSAGE_ATTACH (message, &lsupdate, sizeof (struct ospf6_lsupdate)); + + /* for each LSA listed on retransmission-list */ + for (ospf6_lsdb_head (&node, o6n->retrans_list); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + { + lsa = node.lsa; + + /* I/F MTU check */ + if (OSPF6_MESSAGE_LENGTH (message) + + sizeof (struct ospf6_lsupdate) + + sizeof (struct ospf6_header) + + ntohs (lsa->header->length) + > o6n->ospf6_interface->ifmtu) + break; + + ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay); + OSPF6_MESSAGE_ATTACH (message, lsa->header, ntohs (lsa->header->length)); + lsupdate.lsupdate_num++; + } + + /* check and correct lsupdate */ + if (lsupdate.lsupdate_num == 0) + return 0; + lsupdate.lsupdate_num = htonl (lsupdate.lsupdate_num); + + if (IS_OSPF6_DUMP_LSUPDATE) + zlog_info ("MESSAGE: retrsnsmit LSUpdate to %s", o6n->str); + + /* statistics */ + o6n->ospf6_stat_retrans_lsupdate++; + + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSUPDATE, message, + &o6n->hisaddr, o6n->ospf6_interface->if_id); + + o6n->send_update = thread_add_timer (master, ospf6_send_lsupdate_rxmt, o6n, + o6n->ospf6_interface->rxmt_interval); + return 0; +} + +/* Send LSUpdate containing one LSA directly to the neighbor. + This is "implied acknowledgement" */ +void +ospf6_send_lsupdate_direct (struct ospf6_lsa *lsa, struct ospf6_neighbor *o6n) +{ + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsupdate lsupdate; + int lsa_len; + + /* clear message buffer */ + OSPF6_MESSAGE_CLEAR (message); + + /* set lsupdate header */ + lsupdate.lsupdate_num = ntohl (1); + OSPF6_MESSAGE_ATTACH (message, &lsupdate, sizeof (struct ospf6_lsupdate)); + + /* set one LSA */ + lsa_len = ntohs (lsa->lsa_hdr->lsh_len); + ospf6_lsa_age_update_to_send (lsa, o6n->ospf6_interface->transdelay); + OSPF6_MESSAGE_ATTACH (message, lsa->lsa_hdr, lsa_len); + + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSUPDATE, message, &o6n->hisaddr, + o6n->ospf6_interface->if_id); +} + +/* Send LSUpdate containing one LSA by multicast. + On non-broadcast link, send it to each neighbor by unicast. + This is ordinary flooding */ +void +ospf6_send_lsupdate_flood (struct ospf6_lsa *lsa, struct ospf6_interface *o6i) +{ + struct iovec message[OSPF6_MESSAGE_IOVEC_SIZE]; + struct ospf6_lsupdate lsupdate; + struct in6_addr dst; + int lsa_len; + + /* clear message buffer */ + OSPF6_MESSAGE_CLEAR (message); + + /* set lsupdate header */ + lsupdate.lsupdate_num = ntohl (1); + OSPF6_MESSAGE_ATTACH (message, &lsupdate, sizeof (struct ospf6_lsupdate)); + + /* set one LSA */ + lsa_len = ntohs (lsa->lsa_hdr->lsh_len); + ospf6_lsa_age_update_to_send (lsa, o6i->transdelay); + OSPF6_MESSAGE_ATTACH (message, lsa->lsa_hdr, lsa_len); + + if (if_is_broadcast (o6i->interface)) + { + /* set destination */ + if (o6i->state == IFS_DR || o6i->state == IFS_BDR) + inet_pton (AF_INET6, ALLSPFROUTERS6, &dst); + else + inet_pton (AF_INET6, ALLDROUTERS6, &dst); + } + else + { + /* IPv6 relies on link local multicast */ + inet_pton (AF_INET6, ALLSPFROUTERS6, &dst); + } + + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSUPDATE, message, &dst, + o6i->if_id); +} + +int +ospf6_send_lsack_delayed (struct thread *thread) +{ + struct ospf6_interface *o6i; + struct iovec message[MAXIOVLIST]; + struct ospf6_lsa *lsa; + struct ospf6_lsdb_node node; + + o6i = THREAD_ARG (thread); + assert (o6i); + + if (IS_OSPF6_DUMP_LSACK) + zlog_info ("LSACK: Delayed LSAck for %s\n", o6i->interface->name); + + o6i->thread_send_lsack_delayed = (struct thread *) NULL; + + if (o6i->state <= IFS_WAITING) + return 0; + + if (o6i->ack_list->count == 0) + return 0; + + iov_clear (message, MAXIOVLIST); + + for (ospf6_lsdb_head (&node, o6i->ack_list); + ! ospf6_lsdb_is_end (&node); + ospf6_lsdb_next (&node)) + { + lsa = node.lsa; + if (IS_OVER_MTU (message, o6i->ifmtu, sizeof (struct ospf6_lsa_hdr))) + break; + + OSPF6_MESSAGE_ATTACH (message, lsa->header, + sizeof (struct ospf6_lsa_header)); + ospf6_interface_delayed_ack_remove (lsa, o6i); + } + + /* statistics */ + o6i->ospf6_stat_delayed_lsack++; + + switch (o6i->state) + { + case IFS_DR: + case IFS_BDR: + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSACK, message, + &allspfrouters6.sin6_addr, o6i->if_id); + break; + default: + ospf6_message_send (OSPF6_MESSAGE_TYPE_LSACK, message, + &alldrouters6.sin6_addr, o6i->if_id); + break; + } + + iov_clear (message, MAXIOVLIST); + return 0; +} + |