Merge pull request #8725 from pjdruddy/ospfv3_fair_socket

OSPFv3 socket rework
This commit is contained in:
Russ White 2021-06-22 10:58:33 -04:00 committed by GitHub
commit b79f1e068e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1007 additions and 509 deletions

View File

@ -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:

View File

@ -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

View File

@ -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 */

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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",

View File

@ -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;

View File

@ -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()

View File

@ -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()