diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index d26062e2b9..7c01b6f136 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -70,6 +70,13 @@ OSPF6 router Use this command to control the maximum number of parallel routes that OSPFv3 can support. The default is 64. +.. clicmd:: write-multiplier (1-100) + + Use this command to tune the amount of work done in the packet read and + write threads before relinquishing control. The parameter is the number + of packets to process before returning. The default value of this parameter + is 20. + .. _ospf6-area: diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 80ab279596..692ce8c1b2 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -299,6 +299,13 @@ To start OSPF process you have to specify the OSPF router. a specific destination. The upper limit may differ if you change the value of MULTIPATH_NUM during compilation. The default is MULTIPATH_NUM (64). +.. clicmd:: write-multiplier (1-100) + + Use this command to tune the amount of work done in the packet read and + write threads before relinquishing control. The parameter is the number + of packets to process before returning. The defult value of this parameter + is 20. + .. _ospf-area: Areas diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 6763e7457f..edf9490119 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -185,6 +185,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface)); + oi->obuf = ospf6_fifo_new(); + oi->area = (struct ospf6_area *)NULL; oi->neighbor_list = list_new(); oi->neighbor_list->cmp = ospf6_neighbor_cmp; @@ -243,6 +245,8 @@ void ospf6_interface_delete(struct ospf6_interface *oi) QOBJ_UNREG(oi); + ospf6_fifo_free(oi->obuf); + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) ospf6_neighbor_delete(on); @@ -885,6 +889,15 @@ int interface_down(struct thread *thread) ospf6_sso(oi->interface->ifindex, &allspfrouters6, IPV6_LEAVE_GROUP, ospf6->fd); + /* deal with write fifo */ + ospf6_fifo_flush(oi->obuf); + if (oi->on_write_q) { + listnode_delete(ospf6->oi_write_q, oi); + if (list_isempty(ospf6->oi_write_q)) + thread_cancel(&ospf6->t_write); + oi->on_write_q = 0; + } + ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi); return 0; @@ -1969,6 +1982,38 @@ DEFUN (no_auto_cost_reference_bandwidth, } +DEFUN (ospf6_write_multiplier, + ospf6_write_multiplier_cmd, + "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + uint32_t write_oi_count; + + write_oi_count = strtol(argv[1]->arg, NULL, 10); + if (write_oi_count < 1 || write_oi_count > 100) { + vty_out(vty, "write-multiplier value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + o->write_oi_count = write_oi_count; + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_write_multiplier, + no_ospf6_write_multiplier_cmd, + "no write-multiplier (1-100)", + NO_STR + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_hellointerval, ipv6_ospf6_hellointerval_cmd, "ipv6 ospf6 hello-interval (1-65535)", @@ -2641,6 +2686,9 @@ void ospf6_interface_init(void) /* reference bandwidth commands */ install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); + /* write-multiplier commands */ + install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd); + install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd); } /* Clear the specified interface structure */ diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 796d75e897..530efc3bd2 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -56,6 +56,9 @@ struct ospf6_interface { /* I/F transmission delay */ uint32_t transdelay; + /* Packet send buffer. */ + struct ospf6_fifo *obuf; /* Output queue */ + /* Network Type */ uint8_t type; bool type_cfg; @@ -118,6 +121,9 @@ struct ospf6_interface { struct ospf6_route_table *route_connected; + /* last hello sent */ + struct timeval last_hello; + /* prefix-list name to filter connected prefix */ char *plist_name; @@ -130,6 +136,8 @@ struct ospf6_interface { char *profile; } bfd_config; + int on_write_q; + /* Statistics Fields */ uint32_t hello_in; uint32_t hello_out; diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 1cf4585c95..5f23aab80a 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -50,6 +50,8 @@ #include DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue"); unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; static const struct message ospf6_message_type_str[] = { @@ -252,6 +254,137 @@ void ospf6_lsack_print(struct ospf6_header *oh, int action) } } +static struct ospf6_packet *ospf6_packet_new(size_t size) +{ + struct ospf6_packet *new; + + new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet)); + new->s = stream_new(size); + + return new; +} + +static void ospf6_packet_free(struct ospf6_packet *op) +{ + if (op->s) + stream_free(op->s); + + XFREE(MTYPE_OSPF6_PACKET, op); +} + +struct ospf6_fifo *ospf6_fifo_new(void) +{ + struct ospf6_fifo *new; + + new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo)); + return new; +} + +/* Add new packet to fifo. */ +static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Add new packet to head of fifo. */ +static void ospf6_fifo_push_head(struct ospf6_fifo *fifo, + struct ospf6_packet *op) +{ + op->next = fifo->head; + + if (fifo->tail == NULL) + fifo->tail = op; + + fifo->head = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + + op = fifo->head; + + if (op) { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void ospf6_fifo_flush(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + struct ospf6_packet *next; + + for (op = fifo->head; op; op = next) { + next = op->next; + ospf6_packet_free(op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void ospf6_fifo_free(struct ospf6_fifo *fifo) +{ + ospf6_fifo_flush(fifo); + + XFREE(MTYPE_OSPF6_FIFO, fifo); +} + +static void ospf6_packet_add(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to end of queue. */ + ospf6_fifo_push(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_add_top(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to head of queue. */ + ospf6_fifo_push_head(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_delete(struct ospf6_interface *oi) +{ + struct ospf6_packet *op; + + op = ospf6_fifo_pop(oi->obuf); + + if (op) + ospf6_packet_free(op); +} + + static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, struct ospf6_interface *oi, struct ospf6_header *oh) @@ -263,7 +396,10 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, int neighborchange = 0; int neighbor_ifindex_change = 0; int backupseen = 0; + int64_t latency = 0; + struct timeval timestamp; + monotime(×tamp); hello = (struct ospf6_hello *)((caddr_t)oh + sizeof(struct ospf6_header)); @@ -305,6 +441,17 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, on->priority = hello->priority; } + /* check latency against hello period */ + if (on->hello_in) + latency = monotime_since(&on->last_hello, NULL) + - (oi->hello_interval * 1000000); + /* log if latency exceeds the hello period */ + if (latency > (oi->hello_interval * 1000000)) + zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__, + &on->router_id, latency); + on->last_hello = timestamp; + on->hello_in++; + /* Always override neighbor's source address */ memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr)); @@ -1512,23 +1659,19 @@ void ospf6_message_terminate(void) iobuflen = 0; } -int ospf6_receive(struct thread *thread) +enum ospf6_read_return_enum { + OSPF6_READ_ERROR, + OSPF6_READ_CONTINUE, +}; + +static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) { - int sockfd; - unsigned int len; + int len; struct in6_addr src, dst; ifindex_t ifindex; struct iovec iovector[2]; struct ospf6_interface *oi; struct ospf6_header *oh; - struct ospf6 *ospf6; - - /* add next read thread */ - ospf6 = THREAD_ARG(thread); - sockfd = THREAD_FD(thread); - - thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, - &ospf6->t_ospf6_receive); /* initialize */ memset(&src, 0, sizeof(src)); @@ -1542,9 +1685,12 @@ int ospf6_receive(struct thread *thread) /* receive message */ len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd); - if (len > iobuflen) { + if (len < 0) + return OSPF6_READ_ERROR; + + if ((uint)len > iobuflen) { flog_err(EC_LIB_DEVELOPMENT, "Excess message read"); - return 0; + return OSPF6_READ_ERROR; } oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); @@ -1553,19 +1699,19 @@ int ospf6_receive(struct thread *thread) if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR)) zlog_debug("Message received on disabled interface"); - return 0; + return OSPF6_READ_CONTINUE; } if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR)) zlog_debug("%s: Ignore message on passive interface %s", __func__, oi->interface->name); - return 0; + return OSPF6_READ_CONTINUE; } oh = (struct ospf6_header *)recvbuf; if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK) - return 0; + return OSPF6_READ_CONTINUE; /* Being here means, that no sizing/alignment issues were detected in the input packet. This renders the additional checks performed below @@ -1626,75 +1772,72 @@ int ospf6_receive(struct thread *thread) assert(0); } - return 0; + return OSPF6_READ_CONTINUE; } -static void ospf6_send(struct in6_addr *src, struct in6_addr *dst, - struct ospf6_interface *oi, struct ospf6_header *oh) +int ospf6_receive(struct thread *thread) { - unsigned int len; - char srcname[64]; - struct iovec iovector[2]; + int sockfd; + struct ospf6 *ospf6; + int count = 0; - /* initialize */ - iovector[0].iov_base = (caddr_t)oh; - iovector[0].iov_len = ntohs(oh->length); - iovector[1].iov_base = NULL; - iovector[1].iov_len = 0; + /* add next read thread */ + ospf6 = THREAD_ARG(thread); + sockfd = THREAD_FD(thread); - /* fill OSPF header */ - oh->version = OSPFV3_VERSION; - /* message type must be set before */ - /* message length must be set before */ - oh->router_id = oi->area->ospf6->router_id; - oh->area_id = oi->area->area_id; - /* checksum is calculated by kernel */ - oh->instance_id = oi->instance_id; - oh->reserved = 0; + thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); - /* Log */ - if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) { - if (src) - inet_ntop(AF_INET6, src, srcname, sizeof(srcname)); - else - memset(srcname, 0, sizeof(srcname)); - zlog_debug("%s send on %s", - lookup_msg(ospf6_message_type_str, oh->type, NULL), - oi->interface->name); - zlog_debug(" src: %s", srcname); - zlog_debug(" dst: %pI6", dst); - - switch (oh->type) { - case OSPF6_MESSAGE_TYPE_HELLO: - ospf6_hello_print(oh, OSPF6_ACTION_RECV); - break; - case OSPF6_MESSAGE_TYPE_DBDESC: - ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV); - break; - case OSPF6_MESSAGE_TYPE_LSREQ: - ospf6_lsreq_print(oh, OSPF6_ACTION_RECV); - break; - case OSPF6_MESSAGE_TYPE_LSUPDATE: - ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV); - break; - case OSPF6_MESSAGE_TYPE_LSACK: - ospf6_lsack_print(oh, OSPF6_ACTION_RECV); - break; - default: - zlog_debug("Unknown message"); - assert(0); + while (count < ospf6->write_oi_count) { + count++; + switch (ospf6_read_helper(sockfd, ospf6)) { + case OSPF6_READ_ERROR: + return 0; + case OSPF6_READ_CONTINUE: break; } } - /* send message */ - if (oi->area->ospf6->fd != -1) { - len = ospf6_sendmsg(src, dst, oi->interface->ifindex, iovector, - oi->area->ospf6->fd); - if (len != ntohs(oh->length)) - flog_err(EC_LIB_DEVELOPMENT, - "Could not send entire message"); - } + return 0; +} + +static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi, + struct stream *s) +{ + struct ospf6_header *oh; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->version = (uint8_t)OSPFV3_VERSION; + oh->type = type; + + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + stream_forward_endp(s, OSPF6_HEADER_SIZE); +} + +static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s, + uint16_t length) +{ + struct ospf6_header *oh; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->length = htons(length); +} + +static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num) +{ + struct ospf6_header *oh; + struct ospf6_lsupdate *lsu; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + lsu = (struct ospf6_lsupdate *)((caddr_t)oh + + sizeof(struct ospf6_header)); + lsu->lsa_number = htonl(lsa_num); } static uint32_t ospf6_packet_max(struct ospf6_interface *oi) @@ -1703,14 +1846,169 @@ static uint32_t ospf6_packet_max(struct ospf6_interface *oi) return oi->ifmtu - (sizeof(struct ip6_hdr)); } +static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + uint16_t length = OSPF6_HELLO_MIN_SIZE; + + stream_putl(s, oi->interface->ifindex); + stream_putc(s, oi->priority); + stream_putc(s, oi->area->options[0]); + stream_putc(s, oi->area->options[1]); + stream_putc(s, oi->area->options[2]); + stream_putw(s, oi->hello_interval); + stream_putw(s, oi->dead_interval); + stream_put_ipv4(s, oi->drouter); + stream_put_ipv4(s, oi->bdrouter); + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + if (on->state < OSPF6_NEIGHBOR_INIT) + continue; + + if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, + SEND)) + zlog_debug( + "sending Hello message: exceeds I/F MTU"); + break; + } + + stream_put_ipv4(s, on->router_id); + length += sizeof(uint32_t); + } + + return length; +} + +static int ospf6_write(struct thread *thread) +{ + struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6_interface *oi; + struct ospf6_interface *last_serviced_oi = NULL; + struct ospf6_header *oh; + struct ospf6_packet *op; + struct listnode *node; + char srcname[64], dstname[64]; + struct iovec iovector[2]; + int pkt_count = 0; + int len; + int64_t latency = 0; + struct timeval timestamp; + + if (ospf6->fd < 0) { + zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd); + return -1; + } + + node = listhead(ospf6->oi_write_q); + assert(node); + oi = listgetdata(node); + + while ((pkt_count < ospf6->write_oi_count) && oi + && (last_serviced_oi != oi)) { + + op = ospf6_fifo_head(oi->obuf); + assert(op); + assert(op->length >= OSPF6_HEADER_SIZE); + + iovector[0].iov_base = (caddr_t)stream_pnt(op->s); + iovector[0].iov_len = op->length; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + oh = (struct ospf6_header *)STREAM_DATA(op->s); + + len = ospf6_sendmsg(oi->linklocal_addr, &op->dst, + oi->interface->ifindex, iovector, + ospf6->fd); + if (len != op->length) + flog_err(EC_LIB_DEVELOPMENT, + "Could not send entire message"); + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) { + inet_ntop(AF_INET6, &op->dst, dstname, sizeof(dstname)); + inet_ntop(AF_INET6, oi->linklocal_addr, srcname, + sizeof(srcname)); + zlog_debug("%s send on %s", + lookup_msg(ospf6_message_type_str, oh->type, + NULL), + oi->interface->name); + zlog_debug(" src: %s", srcname); + zlog_debug(" dst: %s", dstname); + } + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + monotime(×tamp); + if (oi->hello_out) + latency = monotime_since(&oi->last_hello, NULL) + - (oi->hello_interval * 1000000); + + /* log if latency exceeds the hello period */ + if (latency > (oi->hello_interval * 1000000)) + zlog_warn("%s hello TX high latency %" PRId64 + "us.", + __func__, latency); + oi->last_hello = timestamp; + oi->hello_out++; + ospf6_hello_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + oi->db_desc_out++; + ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + oi->ls_req_out++; + ospf6_lsreq_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + oi->ls_upd_out++; + ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + oi->ls_ack_out++; + ospf6_lsack_print(oh, OSPF6_ACTION_SEND); + break; + default: + zlog_debug("Unknown message"); + assert(0); + break; + } + /* Now delete packet from queue. */ + ospf6_packet_delete(oi); + + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + list_delete_node(ospf6->oi_write_q, node); + if (ospf6_fifo_head(oi->obuf) == NULL) { + oi->on_write_q = 0; + last_serviced_oi = NULL; + oi = NULL; + } else { + listnode_add(ospf6->oi_write_q, oi); + } + + /* Setup to service from the head of the queue again */ + if (!list_isempty(ospf6->oi_write_q)) { + node = listhead(ospf6->oi_write_q); + oi = listgetdata(node); + } + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(ospf6->oi_write_q)) + thread_add_write(master, ospf6_write, ospf6, ospf6->fd, + &ospf6->t_write); + + return 0; +} + int ospf6_hello_send(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - struct ospf6_hello *hello; - uint8_t *p; - struct listnode *node, *nnode; - struct ospf6_neighbor *on; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_hello = (struct thread *)NULL; @@ -1722,72 +2020,95 @@ int ospf6_hello_send(struct thread *thread) return 0; } - if (iobuflen == 0) { - zlog_debug("Unable to send Hello on interface %s iobuflen is 0", - oi->interface->name); + op = ospf6_packet_new(oi->ifmtu); + + ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s); + + /* Prepare OSPF Hello body */ + length += ospf6_make_hello(oi, op->s); + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); return 0; } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + op->dst = allspfrouters6; + + /* Add packet to the top of the interface output queue, so that they + * can't get delayed by things like long queues of LS Update packets + */ + ospf6_packet_add_top(oi, op); + /* set next thread */ thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, &oi->thread_send_hello); - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - hello = (struct ospf6_hello *)((caddr_t)oh - + sizeof(struct ospf6_header)); + OSPF6_MESSAGE_WRITE_ON(oi); - hello->interface_id = htonl(oi->interface->ifindex); - hello->priority = oi->priority; - hello->options[0] = oi->area->options[0]; - hello->options[1] = oi->area->options[1]; - hello->options[2] = oi->area->options[2]; - hello->hello_interval = htons(oi->hello_interval); - hello->dead_interval = htons(oi->dead_interval); - hello->drouter = oi->drouter; - hello->bdrouter = oi->bdrouter; + return 0; +} - p = (uint8_t *)((caddr_t)hello + sizeof(struct ospf6_hello)); +static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) +{ + uint16_t length = OSPF6_DB_DESC_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; - for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - if (on->state < OSPF6_NEIGHBOR_INIT) - continue; - - if (p - sendbuf + sizeof(uint32_t) > ospf6_packet_max(oi)) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, - SEND_HDR)) - zlog_debug( - "sending Hello message: exceeds I/F MTU"); - break; - } - - memcpy(p, &on->router_id, sizeof(uint32_t)); - p += sizeof(uint32_t); + /* if this is initial one, initialize sequence number for DbDesc */ + if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + && (on->dbdesc_seqnum == 0)) { + on->dbdesc_seqnum = monotime(NULL); } - oh->type = OSPF6_MESSAGE_TYPE_HELLO; - oh->length = htons(p - sendbuf); + /* reserved */ + stream_putc(s, 0); /* reserved 1 */ + stream_putc(s, on->ospf6_if->area->options[0]); + stream_putc(s, on->ospf6_if->area->options[1]); + stream_putc(s, on->ospf6_if->area->options[2]); + stream_putw(s, on->ospf6_if->ifmtu); + stream_putc(s, 0); /* reserved 2 */ + stream_putc(s, on->dbdesc_bits); + stream_putl(s, on->dbdesc_seqnum); - oi->hello_out++; + /* if this is not initial one, set LSA headers in dbdesc */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) { + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) { + ospf6_lsa_age_update_to_send(lsa, + on->ospf6_if->transdelay); - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); - return 0; + /* MTU check */ + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + break; + } + stream_put(s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + } + } + return length; } int ospf6_dbdesc_send(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_dbdesc *dbdesc; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext; - struct in6_addr *dst; + uint16_t length = OSPF6_HEADER_SIZE; + struct ospf6_packet *op; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_dbdesc = (struct thread *)NULL; if (on->state < OSPF6_NEIGHBOR_EXSTART) { - if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND_HDR)) + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND)) zlog_debug( "Quit to send DbDesc to neighbor %s state %s", on->name, ospf6_neighbor_state_str[on->state]); @@ -1800,56 +2121,23 @@ int ospf6_dbdesc_send(struct thread *thread) on->ospf6_if->rxmt_interval, &on->thread_send_dbdesc); - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh - + sizeof(struct ospf6_header)); + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s); - /* if this is initial one, initialize sequence number for DbDesc */ - if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) - && (on->dbdesc_seqnum == 0)) { - on->dbdesc_seqnum = monotime(NULL); - } - - dbdesc->options[0] = on->ospf6_if->area->options[0]; - dbdesc->options[1] = on->ospf6_if->area->options[1]; - dbdesc->options[2] = on->ospf6_if->area->options[2]; - dbdesc->ifmtu = htons(on->ospf6_if->ifmtu); - dbdesc->bits = on->dbdesc_bits; - dbdesc->seqnum = htonl(on->dbdesc_seqnum); - - /* if this is not initial one, set LSA headers in dbdesc */ - p = (uint8_t *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc)); - if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) { - for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) { - ospf6_lsa_age_update_to_send(lsa, - on->ospf6_if->transdelay); - - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) - > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); - if (lsanext) - ospf6_lsa_unlock(lsanext); - break; - } - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); - } - } - - oh->type = OSPF6_MESSAGE_TYPE_DBDESC; - oh->length = htons(p - sendbuf); + length += ospf6_make_dbdesc(on, op->s); + ospf6_fill_header(on->ospf6_if, op->s, length); + /* Set packet length. */ + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - dst = &allspfrouters6; + op->dst = allspfrouters6; else - dst = &on->linklocal_addr; + op->dst = on->linklocal_addr; - on->ospf6_if->db_desc_out++; + ospf6_packet_add(on->ospf6_if, op); - ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); return 0; } @@ -1902,13 +2190,84 @@ int ospf6_dbdesc_send_newone(struct thread *thread) return 0; } +static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext, *last_req = NULL; + + for (ALL_LSDB(on->request_list, lsa, lsanext)) { + if ((length + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + break; + } + stream_putw(s, 0); /* reserved */ + stream_putw(s, ntohs(lsa->header->type)); + stream_putl(s, ntohl(lsa->header->id)); + stream_putl(s, ntohl(lsa->header->adv_router)); + length += sizeof(struct ospf6_lsreq_entry); + last_req = lsa; + } + + if (last_req != NULL) { + if (on->last_ls_req != NULL) + on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + + ospf6_lsa_lock(last_req); + on->last_ls_req = last_req; + } + + return length; +} + +static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on, + struct ospf6_packet **op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + int lsa_cnt = 0; + + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + /* if we run out of packet size/space here, + better to try again soon. */ + if (lsa_cnt) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + + (*op)->length = length + OSPF6_HEADER_SIZE; + (*op)->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + /* new packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, + on->ospf6_if, (*op)->s); + length = 0; + lsa_cnt = 0; + } + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsack_list); + lsa_cnt++; + } + return length; +} + int ospf6_lsreq_send(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_lsreq_entry *e; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext, *last_req; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsreq = (struct thread *)NULL; @@ -1929,49 +2288,31 @@ int ospf6_lsreq_send(struct thread *thread) return 0; } - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - last_req = NULL; + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s); - /* set Request entries in lsreq */ - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); - for (ALL_LSDB(on->request_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsreq_entry) - > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); - if (lsanext) - ospf6_lsa_unlock(lsanext); - break; - } + length += ospf6_make_lsreq(on, op->s); - e = (struct ospf6_lsreq_entry *)p; - e->type = lsa->header->type; - e->id = lsa->header->id; - e->adv_router = lsa->header->adv_router; - p += sizeof(struct ospf6_lsreq_entry); - last_req = lsa; + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); + return 0; } - if (last_req != NULL) { - if (on->last_ls_req != NULL) - on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); - ospf6_lsa_lock(last_req); - on->last_ls_req = last_req; - } - - oh->type = OSPF6_MESSAGE_TYPE_LSREQ; - oh->length = htons(p - sendbuf); - - on->ospf6_if->ls_req_out++; + /* Set packet length */ + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6, - on->ospf6_if, oh); + op->dst = allspfrouters6; else - ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); + op->dst = on->linklocal_addr; + + ospf6_packet_add(on->ospf6_if, op); + + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); /* set next thread */ if (on->request_list->count != 0) { @@ -1986,43 +2327,117 @@ int ospf6_lsreq_send(struct thread *thread) static void ospf6_send_lsupdate(struct ospf6_neighbor *on, struct ospf6_interface *oi, - struct ospf6_header *oh) + struct ospf6_packet *op) { if (on) { - on->ospf6_if->ls_upd_out++; if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || (on->ospf6_if->state == OSPF6_INTERFACE_DR) - || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) { - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, on->ospf6_if, oh); - } else { - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, oh); - } + || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + oi = on->ospf6_if; } else if (oi) { - - oi->ls_upd_out++; - if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || (oi->state == OSPF6_INTERFACE_DR) - || (oi->state == OSPF6_INTERFACE_BDR)) { - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); - } else { - ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh); - } + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; } + if (oi) { + ospf6_packet_add(oi, op); + OSPF6_MESSAGE_WRITE_ON(oi); + } +} + +static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) { + if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(on, NULL, *op); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsupdate_list); + } + return length; +} + +static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { + if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + (*op)->dst = allspfrouters6; + else + (*op)->dst = on->linklocal_addr; + + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + } + return length; } int ospf6_lsupdate_send_neighbor(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt; - struct ospf6_lsa *lsa, *lsanext; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsupdate = (struct thread *)NULL; @@ -2038,119 +2453,41 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread) return 0; } - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - /* lsupdate_list lists those LSA which doesn't need to be - retransmitted. remove those from the list */ - for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header)) - > ospf6_packet_max(on->ospf6_if)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(on, NULL, oh); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, on->lsupdate_list); - } - + /* first do lsupdate_list */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - ospf6_send_lsupdate(on, NULL, oh); - } - - /* The addresses used for retransmissions are different from those sent - the - first time and so we need to separate them here. - */ - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header)) - > ospf6_packet_max(on->ospf6_if)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - if (on->ospf6_if->state - == OSPF6_INTERFACE_POINTTOPOINT) { - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, - on->ospf6_if, oh); - } else { - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, - on->ospf6_if, oh); - } - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(on, NULL, op); + + /* prepare new packet */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_HEADER_SIZE; + lsa_cnt = 0; + } else { + stream_reset(op->s); + length = OSPF6_HEADER_SIZE; } + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + /* now do retransmit list */ + length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) - ospf6_send(on->ospf6_if->linklocal_addr, - &allspfrouters6, on->ospf6_if, oh); + op->dst = allspfrouters6; else - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, oh); - } + op->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + } else + ospf6_packet_free(op); if (on->lsupdate_list->count != 0) { on->thread_send_lsupdate = NULL; @@ -2168,44 +2505,78 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread) int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, struct ospf6_lsa *lsa) { - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); + /* skip over fixed header */ + stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE); ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); + stream_put(op->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE + + OSPF6_LSA_SIZE(lsa->header); + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, 1); + op->length = length; if (IS_OSPF6_DEBUG_FLOODING || IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR)) zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__, lsa->name, ntohs(lsa->header->age)); - ospf6_send_lsupdate(on, NULL, oh); + ospf6_send_lsupdate(on, NULL, op); return 0; } +static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) { + if (length + (unsigned int)OSPF6_LSA_SIZE(lsa->header) + + OSPF6_HEADER_SIZE + > ospf6_packet_max(oi)) { + ospf6_fill_header(oi, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(NULL, oi, *op); + + /* refresh packet */ + *op = ospf6_packet_new(oi->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, + (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsupdate_list); + } + return length; +} + int ospf6_lsupdate_send_interface(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - struct ospf6_lsupdate *lsupdate; - uint8_t *p; - int lsa_cnt; - struct ospf6_lsa *lsa, *lsanext; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_lsupdate = (struct thread *)NULL; @@ -2224,59 +2595,17 @@ int ospf6_lsupdate_send_interface(struct thread *thread) if (oi->lsupdate_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh - + sizeof(struct ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); - lsa_cnt = 0; - - for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) { - /* MTU check */ - if ((p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE(lsa->header))) - > ospf6_packet_max(oi)) { - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(NULL, oi, oh); - if (IS_OSPF6_DEBUG_MESSAGE( - OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) - zlog_debug("%s: LSUpdate length %d", - __func__, ntohs(oh->length)); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - lsupdate = (struct ospf6_lsupdate - *)((caddr_t)oh - + sizeof(struct - ospf6_header)); - - p = (uint8_t *)((caddr_t)lsupdate - + sizeof(struct - ospf6_lsupdate)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, oi->transdelay); - memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header)); - p += OSPF6_LSA_SIZE(lsa->header); - lsa_cnt++; - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, oi->lsupdate_list); - } - + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s); + length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt); if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons(p - sendbuf); - lsupdate->lsa_number = htonl(lsa_cnt); - - ospf6_send_lsupdate(NULL, oi, oh); - } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(NULL, oi, op); + } else + ospf6_packet_free(op); if (oi->lsupdate_list->count > 0) { oi->thread_send_lsupdate = NULL; @@ -2290,10 +2619,8 @@ int ospf6_lsupdate_send_interface(struct thread *thread) int ospf6_lsack_send_neighbor(struct thread *thread) { struct ospf6_neighbor *on; - struct ospf6_header *oh; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; on = (struct ospf6_neighbor *)THREAD_ARG(thread); on->thread_send_lsack = (struct thread *)NULL; @@ -2310,53 +2637,24 @@ int ospf6_lsack_send_neighbor(struct thread *thread) if (on->lsack_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s); - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); + length += ospf6_make_lsack_neighbor(on, &op); - for (ALL_LSDB(on->lsack_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) - > ospf6_packet_max(on->ospf6_if)) { - /* if we run out of packet size/space here, - better to try again soon. */ - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); - - on->ospf6_if->ls_ack_out++; - - ospf6_send(on->ospf6_if->linklocal_addr, - &on->linklocal_addr, on->ospf6_if, - oh); - - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; - p = (uint8_t *)((caddr_t)oh - + sizeof(struct ospf6_header)); - lsa_cnt = 0; - } - } - - ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, on->lsack_list); - lsa_cnt++; + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return 0; } - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); - on->ospf6_if->ls_ack_out++; - - ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); - } + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + op->dst = on->linklocal_addr; + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); if (on->lsack_list->count > 0) thread_add_event(master, ospf6_lsack_send_neighbor, on, 0, @@ -2365,13 +2663,42 @@ int ospf6_lsack_send_neighbor(struct thread *thread) return 0; } +static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF(oi->thread_send_lsack); + thread_add_event(master, ospf6_lsack_send_interface, oi, + 0, &oi->thread_send_lsack); + + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); + break; + } + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsack_list); + } + return length; +} + int ospf6_lsack_send_interface(struct thread *thread) { struct ospf6_interface *oi; - struct ospf6_header *oh; - uint8_t *p; - struct ospf6_lsa *lsa, *lsanext; - int lsa_cnt = 0; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; oi = (struct ospf6_interface *)THREAD_ARG(thread); oi->thread_send_lsack = (struct thread *)NULL; @@ -2389,47 +2716,29 @@ int ospf6_lsack_send_interface(struct thread *thread) if (oi->lsack_list->count == 0) return 0; - memset(sendbuf, 0, iobuflen); - oh = (struct ospf6_header *)sendbuf; + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s); - p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header)); + length += ospf6_make_lsack_interface(oi, op); - for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) { - /* MTU check */ - if (p - sendbuf + sizeof(struct ospf6_lsa_header) - > ospf6_packet_max(oi)) { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF(oi->thread_send_lsack); - thread_add_event(master, ospf6_lsack_send_interface, oi, - 0, &oi->thread_send_lsack); - - ospf6_lsa_unlock(lsa); - if (lsanext) - ospf6_lsa_unlock(lsanext); - break; - } - - ospf6_lsa_age_update_to_send(lsa, oi->transdelay); - memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); - p += sizeof(struct ospf6_lsa_header); - - assert(lsa->lock == 2); - ospf6_lsdb_remove(lsa, oi->lsack_list); - lsa_cnt++; + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return 0; } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); - if (lsa_cnt) { - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons(p - sendbuf); + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) + || (oi->state == OSPF6_INTERFACE_DR) + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; - if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) - || (oi->state == OSPF6_INTERFACE_DR) - || (oi->state == OSPF6_INTERFACE_BDR)) - ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh); - } + ospf6_packet_add(oi, op); + OSPF6_MESSAGE_WRITE_ON(oi); if (oi->lsack_list->count > 0) thread_add_event(master, ospf6_lsack_send_interface, oi, 0, @@ -2438,7 +2747,6 @@ int ospf6_lsack_send_interface(struct thread *thread) return 0; } - /* Commands */ DEFUN(debug_ospf6_message, debug_ospf6_message_cmd, "debug ospf6 message []", diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 7c108bd452..0cd10ef825 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -63,6 +63,27 @@ extern unsigned char conf_debug_ospf6_message[]; #define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */ #define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ +struct ospf6_packet { + struct ospf6_packet *next; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in6_addr dst; + + /* OSPF6 packet length. */ + uint16_t length; +}; + +/* OSPF packet queue structure. */ +struct ospf6_fifo { + unsigned long count; + + struct ospf6_packet *head; + struct ospf6_packet *tail; +}; + /* OSPFv3 packet header */ #define OSPF6_HEADER_SIZE 16U struct ospf6_header { @@ -136,6 +157,10 @@ extern void ospf6_lsreq_print(struct ospf6_header *, int action); extern void ospf6_lsupdate_print(struct ospf6_header *, int action); extern void ospf6_lsack_print(struct ospf6_header *, int action); +extern struct ospf6_fifo *ospf6_fifo_new(void); +extern void ospf6_fifo_flush(struct ospf6_fifo *fifo); +extern void ospf6_fifo_free(struct ospf6_fifo *fifo); + extern int ospf6_iobuf_size(unsigned int size); extern void ospf6_message_terminate(void); extern int ospf6_receive(struct thread *thread); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 85b8e4b8ae..729b1d2e85 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -47,6 +47,10 @@ struct ospf6_neighbor { uint32_t state_change; struct timeval last_changed; + /* last received hello */ + struct timeval last_hello; + uint32_t hello_in; + /* Neighbor Router ID */ in_addr_t router_id; diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 76f98fecdd..5961cfe66a 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -257,10 +257,13 @@ int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, rmsghdr.msg_control = (caddr_t)cmsgbuf; rmsghdr.msg_controllen = sizeof(cmsgbuf); - retval = recvmsg(ospf6_sock, &rmsghdr, 0); - if (retval < 0) - zlog_warn("recvmsg failed: %s", safe_strerror(errno)); - else if (retval == iov_totallen(message)) + retval = recvmsg(ospf6_sock, &rmsghdr, MSG_DONTWAIT); + if (retval < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + zlog_warn("stream_recvmsg failed: %s", + safe_strerror(errno)); + return retval; + } else if (retval == iov_totallen(message)) zlog_warn("recvmsg read full buffer size: %d", retval); /* source address */ diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 08d8be4445..3886a0d263 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -36,4 +36,19 @@ extern int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, ifindex_t *ifindex, struct iovec *message, int ospf6_sock); +#define OSPF6_MESSAGE_WRITE_ON(oi) \ + do { \ + bool list_was_empty = \ + list_isempty(oi->area->ospf6->oi_write_q); \ + if ((oi)->on_write_q == 0) { \ + listnode_add(oi->area->ospf6->oi_write_q, (oi)); \ + (oi)->on_write_q = 1; \ + } \ + if (list_was_empty \ + && !list_isempty(oi->area->ospf6->oi_write_q)) \ + thread_add_write(master, ospf6_write, oi->area->ospf6, \ + oi->area->ospf6->fd, \ + &oi->area->ospf6->t_write); \ + } while (0) + #endif /* OSPF6_NETWORK_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 7e4604e987..6cea5032bd 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -408,6 +408,7 @@ static struct ospf6 *ospf6_create(const char *name) o->external_id_table = route_table_init(); + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; o->distance_table = route_table_init(); @@ -415,6 +416,8 @@ static struct ospf6 *ospf6_create(const char *name) o->max_multipath = MULTIPATH_NUM; + o->oi_write_q = list_new(); + QOBJ_REG(o, ospf6); /* Make ospf protocol socket. */ @@ -484,6 +487,7 @@ void ospf6_delete(struct ospf6 *o) ospf6_distance_reset(o); route_table_finish(o->distance_table); + list_delete(&o->oi_write_q); if (o->vrf_id != VRF_UNKNOWN) { vrf = vrf_lookup_by_id(o->vrf_id); @@ -1667,6 +1671,11 @@ static int config_write_ospf6(struct vty *vty) vty_out(vty, " auto-cost reference-bandwidth %d\n", ospf6->ref_bandwidth); + if (ospf6->write_oi_count + != OSPF6_WRITE_INTERFACE_COUNT_DEFAULT) + vty_out(vty, " write-multiplier %d\n", + ospf6->write_oi_count); + /* LSA timers print. */ if (ospf6->lsa_minarrival != OSPF_MIN_LS_ARRIVAL) vty_out(vty, " timers lsa min-arrival %d\n", diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index e4dfebe1de..5b739ac76b 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -128,7 +128,10 @@ struct ospf6 { struct thread *maxage_remover; struct thread *t_distribute_update; /* Distirbute update timer. */ struct thread *t_ospf6_receive; /* OSPF6 receive timer */ +#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20 + struct thread *t_write; + int write_oi_count; /* Num of packets sent per thread invocation */ uint32_t ref_bandwidth; /* Distance parameters */ @@ -150,6 +153,7 @@ struct ospf6 { /* Count of NSSA areas */ uint8_t anyNSSA; struct thread *t_abr_task; /* ABR task timer. */ + struct list *oi_write_q; uint32_t redist_count; QOBJ_FIELDS; diff --git a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py index f8c3476e18..bbd18a57ff 100644 --- a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py @@ -360,6 +360,36 @@ def test_linux_ipv6_kernel_routingTable(): ) +def test_ospfv3_routingTable_write_multiplier(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + + # Modify R1 write muliplier and reset the interfaces + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100") + r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet") + r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5") + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().items(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + def test_shutdown_check_stderr(): tgen = get_topogen() diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py index e1857abc4f..b158099d9a 100755 --- a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py +++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py @@ -5,7 +5,7 @@ # Part of NetDEF Topology Tests # # Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks") -# Used Copyright (c) 2016 by Network Device Education Foundation, +# Used Copyright (c) 2016 by Network Device Education Foundation, # Inc. ("NetDEF") in this file. # # Permission to use, copy, modify, and/or distribute this software @@ -179,13 +179,9 @@ def setup_module(mod): "ip link set {0}-stubnet master {0}-cust1", ] - cmds1 = [ - "ip link set {0}-sw5 master {0}-cust1", - ] + cmds1 = ["ip link set {0}-sw5 master {0}-cust1"] - cmds2 = [ - "ip link set {0}-sw6 master {0}-cust1", - ] + cmds2 = ["ip link set {0}-sw6 master {0}-cust1"] # For all registered routers, load the zebra configuration file for rname, router in tgen.routers().items(): @@ -219,6 +215,7 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_wait_protocol_convergence(): "Wait for OSPFv3 to converge" tgen = get_topogen() @@ -261,7 +258,7 @@ def compare_show_ipv6_vrf(rname, expected): # Use the vtysh output, with some masking to make comparison easy vrf_name = "{0}-cust1".format(rname) current = topotest.ip6_route_zebra(tgen.gears[rname], vrf_name) - + # Use just the 'O'spf lines of the output linearr = [] for line in current.splitlines(): @@ -331,7 +328,11 @@ def test_linux_ipv6_kernel_routingTable(): for i in range(1, 5): # Actual output from router - actual = tgen.gears["r{}".format(i)].run("ip -6 route show vrf r{}-cust1".format(i)).rstrip() + actual = ( + tgen.gears["r{}".format(i)] + .run("ip -6 route show vrf r{}-cust1".format(i)) + .rstrip() + ) if "nhid" in actual: refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i)) else: @@ -362,9 +363,9 @@ def test_linux_ipv6_kernel_routingTable(): "unreachable fe80::/64 " ): continue - if 'anycast' in line: + if "anycast" in line: continue - if 'multicast' in line: + if "multicast" in line: continue filtered_lines.append(line) actual = "\n".join(filtered_lines).splitlines(1) @@ -398,6 +399,35 @@ def test_linux_ipv6_kernel_routingTable(): ) +def test_ospfv3_routingTable_write_multiplier(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + # Modify R1 write muliplier and reset the interfaces + r1 = tgen.gears["r1"] + + r1.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100") + r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet") + r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5") + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6_vrf, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) + + def test_shutdown_check_stderr(): tgen = get_topogen()