mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-13 17:40:04 +00:00
Merge pull request #8725 from pjdruddy/ospfv3_fair_socket
OSPFv3 socket rework
This commit is contained in:
commit
b79f1e068e
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user