mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 12:16:46 +00:00
Merge pull request #11289 from LabNConsulting/chopps/ospfapi-update
ospfapi improvements and fixes
This commit is contained in:
commit
a775175bfd
1
debian/frr-pythontools.install
vendored
1
debian/frr-pythontools.install
vendored
@ -1,3 +1,4 @@
|
||||
usr/lib/frr/frr-reload.py
|
||||
usr/lib/frr/generate_support_bundle.py
|
||||
usr/lib/frr/frr_babeltrace.py
|
||||
usr/lib/frr/ospfclient.py
|
||||
|
@ -212,7 +212,8 @@ options from the list below.
|
||||
|
||||
.. option:: --disable-ospfclient
|
||||
|
||||
Disable building of the example OSPF-API client.
|
||||
Disable installation of the python ospfclient and building of the example
|
||||
OSPF-API client.
|
||||
|
||||
.. option:: --disable-isisd
|
||||
|
||||
|
@ -867,6 +867,12 @@ Opaque LSA
|
||||
|
||||
Show Opaque LSA from the database.
|
||||
|
||||
.. clicmd:: show ip ospf (1-65535) reachable-routers
|
||||
|
||||
.. clicmd:: show ip ospf [vrf <NAME|all>] reachable-routers
|
||||
|
||||
Show routing table of reachable routers.
|
||||
|
||||
.. _ospf-traffic-engineering:
|
||||
|
||||
Traffic Engineering
|
||||
@ -1064,6 +1070,10 @@ Debugging OSPF
|
||||
library messages and OSPF BFD integration messages that are mostly state
|
||||
transitions and validation problems.
|
||||
|
||||
.. clicmd:: debug ospf client-api
|
||||
|
||||
Show debug information for the OSPF opaque data client API.
|
||||
|
||||
.. clicmd:: debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]
|
||||
|
||||
|
||||
|
1133
ospfclient/ospfclient.py
Executable file
1133
ospfclient/ospfclient.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,10 @@ if OSPFCLIENT
|
||||
lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la
|
||||
noinst_PROGRAMS += ospfclient/ospfclient
|
||||
#man8 += $(MANBUILD)/frr-ospfclient.8
|
||||
|
||||
sbin_SCRIPTS += \
|
||||
ospfclient/ospfclient.py \
|
||||
# end
|
||||
endif
|
||||
|
||||
ospfclient_libfrrospfapiclient_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
|
||||
@ -41,3 +45,7 @@ endif
|
||||
ospfclient_ospfclient_SOURCES = \
|
||||
ospfclient/ospfclient.c \
|
||||
# end
|
||||
|
||||
EXTRA_DIST += \
|
||||
ospfclient/ospfclient.py \
|
||||
# end
|
||||
|
@ -177,6 +177,10 @@ const char *ospf_api_typename(int msgtype)
|
||||
{
|
||||
MSG_NSM_CHANGE, "NSM change",
|
||||
},
|
||||
{
|
||||
MSG_REACHABLE_CHANGE,
|
||||
"Reachable change",
|
||||
},
|
||||
};
|
||||
|
||||
int i, n = array_size(NameTab);
|
||||
@ -651,4 +655,31 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
|
||||
return msg_new(msgtype, nmsg, seqnum, len);
|
||||
}
|
||||
|
||||
struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
|
||||
struct in_addr *add, uint16_t nremove,
|
||||
struct in_addr *remove)
|
||||
{
|
||||
uint8_t buf[OSPF_API_MAX_MSG_SIZE];
|
||||
struct msg_reachable_change *nmsg = (void *)buf;
|
||||
const uint insz = sizeof(*nmsg->router_ids);
|
||||
const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz;
|
||||
uint len;
|
||||
|
||||
if (nadd > nmax)
|
||||
nadd = nmax;
|
||||
if (nremove > (nmax - nadd))
|
||||
nremove = (nmax - nadd);
|
||||
|
||||
if (nadd)
|
||||
memcpy(nmsg->router_ids, add, nadd * insz);
|
||||
if (nremove)
|
||||
memcpy(&nmsg->router_ids[nadd], remove, nremove * insz);
|
||||
|
||||
nmsg->nadd = htons(nadd);
|
||||
nmsg->nremove = htons(nremove);
|
||||
len = sizeof(*nmsg) + insz * (nadd + nremove);
|
||||
|
||||
return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len);
|
||||
}
|
||||
|
||||
#endif /* SUPPORT_OSPF_API */
|
||||
|
@ -26,6 +26,9 @@
|
||||
#ifndef _OSPF_API_H
|
||||
#define _OSPF_API_H
|
||||
|
||||
#include <zebra.h>
|
||||
#include "ospf_lsa.h"
|
||||
|
||||
#define OSPF_API_VERSION 1
|
||||
|
||||
/* MTYPE definition is not reflected to "memory.h". */
|
||||
@ -112,6 +115,9 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
|
||||
#define MSG_SYNC_LSDB 4
|
||||
#define MSG_ORIGINATE_REQUEST 5
|
||||
#define MSG_DELETE_REQUEST 6
|
||||
#define MSG_SYNC_REACHABLE 7
|
||||
#define MSG_SYNC_ISM 8
|
||||
#define MSG_SYNC_NSM 9
|
||||
|
||||
/* Messages from OSPF daemon. */
|
||||
#define MSG_REPLY 10
|
||||
@ -122,6 +128,7 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
|
||||
#define MSG_DEL_IF 15
|
||||
#define MSG_ISM_CHANGE 16
|
||||
#define MSG_NSM_CHANGE 17
|
||||
#define MSG_REACHABLE_CHANGE 18
|
||||
|
||||
struct msg_register_opaque_type {
|
||||
uint8_t lsatype;
|
||||
@ -247,6 +254,12 @@ struct msg_nsm_change {
|
||||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
struct msg_reachable_change {
|
||||
uint16_t nadd;
|
||||
uint16_t nremove;
|
||||
struct in_addr router_ids[]; /* add followed by remove */
|
||||
};
|
||||
|
||||
/* We make use of a union to define a structure that covers all
|
||||
possible API messages. This allows us to find out how much memory
|
||||
needs to be reserved for the largest API message. */
|
||||
@ -265,6 +278,7 @@ struct apimsg {
|
||||
struct msg_ism_change ism_change;
|
||||
struct msg_nsm_change nsm_change;
|
||||
struct msg_lsa_change_notify lsa_change_notify;
|
||||
struct msg_reachable_change reachable_change;
|
||||
} u;
|
||||
};
|
||||
|
||||
@ -320,6 +334,10 @@ extern struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
|
||||
uint8_t is_self_originated,
|
||||
struct lsa_header *data);
|
||||
|
||||
extern struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
|
||||
struct in_addr *add,
|
||||
uint16_t nremove,
|
||||
struct in_addr *remove);
|
||||
/* string printing functions */
|
||||
extern const char *ospf_api_errname(int errcode);
|
||||
extern const char *ospf_api_typename(int msgtype);
|
||||
|
@ -726,6 +726,7 @@ static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv,
|
||||
case MSG_DEL_IF:
|
||||
case MSG_ISM_CHANGE:
|
||||
case MSG_NSM_CHANGE:
|
||||
case MSG_REACHABLE_CHANGE:
|
||||
fifo = apiserv->out_async_fifo;
|
||||
fd = apiserv->fd_async;
|
||||
event = OSPF_APISERVER_ASYNC_WRITE;
|
||||
@ -799,6 +800,15 @@ int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg)
|
||||
case MSG_DELETE_REQUEST:
|
||||
rc = ospf_apiserver_handle_delete_request(apiserv, msg);
|
||||
break;
|
||||
case MSG_SYNC_REACHABLE:
|
||||
rc = ospf_apiserver_handle_sync_reachable(apiserv, msg);
|
||||
break;
|
||||
case MSG_SYNC_ISM:
|
||||
rc = ospf_apiserver_handle_sync_ism(apiserv, msg);
|
||||
break;
|
||||
case MSG_SYNC_NSM:
|
||||
rc = ospf_apiserver_handle_sync_nsm(apiserv, msg);
|
||||
break;
|
||||
default:
|
||||
zlog_warn("ospf_apiserver_handle_msg: Unknown message type: %d",
|
||||
msg->hdr.msgtype);
|
||||
@ -1343,6 +1353,131 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------
|
||||
* Followings are functions for synchronization.
|
||||
* -----------------------------------------------------------
|
||||
*/
|
||||
|
||||
int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg)
|
||||
{
|
||||
struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
struct route_table *rt = ospf->all_rtrs;
|
||||
uint32_t seqnum = msg_get_seq(msg);
|
||||
struct in_addr *a, *abuf;
|
||||
struct msg_reachable_change *areach;
|
||||
struct msg *amsg;
|
||||
uint mcount, count;
|
||||
int _rc, rc = 0;
|
||||
|
||||
if (!rt)
|
||||
goto out;
|
||||
|
||||
/* send all adds based on current reachable routers */
|
||||
a = abuf = XCALLOC(MTYPE_OSPF_APISERVER,
|
||||
sizeof(struct in_addr) * rt->count);
|
||||
for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn))
|
||||
if (listhead((struct list *)rn->info))
|
||||
*a++ = rn->p.u.prefix4;
|
||||
|
||||
assert((a - abuf) <= (long)rt->count);
|
||||
count = (a - abuf);
|
||||
|
||||
a = abuf;
|
||||
while (count && !rc) {
|
||||
amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL);
|
||||
areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s);
|
||||
mcount = ntohs(areach->nadd) + ntohs(areach->nremove);
|
||||
assert(mcount <= count);
|
||||
a = a + mcount;
|
||||
count -= mcount;
|
||||
rc = ospf_apiserver_send_msg(apiserv, amsg);
|
||||
msg_free(amsg);
|
||||
}
|
||||
XFREE(MTYPE_OSPF_APISERVER, abuf);
|
||||
|
||||
out:
|
||||
/* Send a reply back to client with return code */
|
||||
_rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
|
||||
rc = rc ? rc : _rc;
|
||||
apiserv->reachable_sync = !rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg)
|
||||
{
|
||||
struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
struct listnode *anode, *inode;
|
||||
struct ospf_area *area;
|
||||
struct ospf_interface *oi;
|
||||
struct msg *m;
|
||||
uint32_t seqnum = msg_get_seq(msg);
|
||||
int _rc, rc = 0;
|
||||
|
||||
/* walk all areas */
|
||||
for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
|
||||
/* walk all interfaces */
|
||||
for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
|
||||
m = new_msg_ism_change(seqnum, oi->address->u.prefix4,
|
||||
area->area_id, oi->state);
|
||||
rc = ospf_apiserver_send_msg(apiserv, m);
|
||||
msg_free(m);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
/* Send a reply back to client with return code */
|
||||
_rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
|
||||
return rc ? rc : _rc;
|
||||
}
|
||||
|
||||
|
||||
int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg)
|
||||
{
|
||||
struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
struct listnode *anode, *inode;
|
||||
struct ospf_area *area;
|
||||
struct ospf_interface *oi;
|
||||
struct ospf_neighbor *nbr;
|
||||
struct route_node *rn;
|
||||
struct msg *m;
|
||||
uint32_t seqnum = msg_get_seq(msg);
|
||||
int _rc, rc = 0;
|
||||
|
||||
/* walk all areas */
|
||||
for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
|
||||
/* walk all interfaces */
|
||||
for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
|
||||
/* walk all neighbors */
|
||||
for (rn = route_top(oi->nbrs); rn;
|
||||
rn = route_next(rn)) {
|
||||
nbr = rn->info;
|
||||
if (!nbr)
|
||||
continue;
|
||||
m = new_msg_nsm_change(
|
||||
seqnum, oi->address->u.prefix4,
|
||||
nbr->src, nbr->router_id, nbr->state);
|
||||
rc = ospf_apiserver_send_msg(apiserv, m);
|
||||
msg_free(m);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
/* Send a reply back to client with return code */
|
||||
_rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
|
||||
return rc ? rc : _rc;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
* Following are functions to originate or update LSA
|
||||
@ -1427,45 +1562,20 @@ struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area,
|
||||
|
||||
int ospf_apiserver_is_ready_type9(struct ospf_interface *oi)
|
||||
{
|
||||
/* Type 9 opaque LSA can be originated if there is at least one
|
||||
active opaque-capable neighbor attached to the outgoing
|
||||
interface. */
|
||||
|
||||
return (ospf_nbr_count_opaque_capable(oi) > 0);
|
||||
/* We can always handle getting opaque's even if we can't flood them */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ospf_apiserver_is_ready_type10(struct ospf_area *area)
|
||||
{
|
||||
/* Type 10 opaque LSA can be originated if there is at least one
|
||||
interface belonging to the area that has an active opaque-capable
|
||||
neighbor. */
|
||||
struct listnode *node, *nnode;
|
||||
struct ospf_interface *oi;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi))
|
||||
/* Is there an active neighbor attached to this interface? */
|
||||
if (ospf_apiserver_is_ready_type9(oi))
|
||||
return 1;
|
||||
|
||||
/* No active neighbor in area */
|
||||
return 0;
|
||||
/* We can always handle getting opaque's even if we can't flood them */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ospf_apiserver_is_ready_type11(struct ospf *ospf)
|
||||
{
|
||||
/* Type 11 opaque LSA can be originated if there is at least one
|
||||
interface
|
||||
that has an active opaque-capable neighbor. */
|
||||
struct listnode *node, *nnode;
|
||||
struct ospf_interface *oi;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi))
|
||||
/* Is there an active neighbor attached to this interface? */
|
||||
if (ospf_apiserver_is_ready_type9(oi))
|
||||
return 1;
|
||||
|
||||
/* No active neighbor at all */
|
||||
return 0;
|
||||
/* We can always handle getting opaque's even if we can't flood them */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -1570,9 +1680,9 @@ int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv,
|
||||
/* Determine if LSA is new or an update for an existing one. */
|
||||
old = ospf_lsdb_lookup(lsdb, new);
|
||||
|
||||
if (!old) {
|
||||
if (!old || !ospf_opaque_is_owned(old)) {
|
||||
/* New LSA install in LSDB. */
|
||||
rc = ospf_apiserver_originate1(new);
|
||||
rc = ospf_apiserver_originate1(new, old);
|
||||
} else {
|
||||
/*
|
||||
* Keep the new LSA instance in the "waiting place" until the
|
||||
@ -1639,17 +1749,33 @@ void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa)
|
||||
}
|
||||
}
|
||||
|
||||
int ospf_apiserver_originate1(struct ospf_lsa *lsa)
|
||||
int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old)
|
||||
{
|
||||
struct ospf *ospf;
|
||||
|
||||
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
assert(ospf);
|
||||
|
||||
if (old) {
|
||||
/*
|
||||
* An old LSA exists that we didn't originate it in this
|
||||
* session. Dump it, but increment past it's seqnum.
|
||||
*/
|
||||
assert(!ospf_opaque_is_owned(old));
|
||||
if (IS_LSA_MAX_SEQ(old)) {
|
||||
flog_warn(
|
||||
EC_OSPF_LSA_INSTALL_FAILURE,
|
||||
"ospf_apiserver_originate1: old LSA at maxseq");
|
||||
return -1;
|
||||
}
|
||||
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
|
||||
ospf_discard_from_db(ospf, old->lsdb, old);
|
||||
}
|
||||
|
||||
/* Install this LSA into LSDB. */
|
||||
if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) {
|
||||
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
|
||||
"ospf_apiserver_originate1: ospf_lsa_install failed");
|
||||
"%s: ospf_lsa_install failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2015,6 +2141,7 @@ int ospf_apiserver_del_if(struct interface *ifp)
|
||||
if (!oi) {
|
||||
/* This interface is known to Zebra but not to OSPF daemon
|
||||
anymore. No need to tell clients about it */
|
||||
zlog_warn("ifp name=%s not known to OSPFd", ifp->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2061,9 +2188,6 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
|
||||
struct opaque_lsa *olsa;
|
||||
int opaquelen;
|
||||
|
||||
if (json)
|
||||
return;
|
||||
|
||||
olsa = (struct opaque_lsa *)lsa->data;
|
||||
|
||||
if (VALID_OPAQUE_INFO_LEN(lsa->data))
|
||||
@ -2072,7 +2196,10 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
|
||||
opaquelen = 0;
|
||||
|
||||
/* Output information about opaque LSAs */
|
||||
if (vty != NULL) {
|
||||
if (json)
|
||||
json_object_string_addf(json, "opaqueData", "%*pHXn",
|
||||
(int)opaquelen, olsa->data);
|
||||
else if (vty != NULL) {
|
||||
int i;
|
||||
vty_out(vty,
|
||||
" Added using OSPF API: %u octets of opaque data %s\n",
|
||||
@ -2333,8 +2460,8 @@ void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr)
|
||||
msg_free(msg);
|
||||
}
|
||||
|
||||
static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
struct ospf_lsa *lsa)
|
||||
static int apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
struct ospf_lsa *lsa)
|
||||
{
|
||||
struct msg *msg;
|
||||
struct listnode *node, *nnode;
|
||||
@ -2362,7 +2489,7 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
if (!msg) {
|
||||
zlog_warn(
|
||||
"apiserver_clients_lsa_change_notify: msg_new failed");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now send message to all clients with a matching filter */
|
||||
@ -2413,6 +2540,8 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
}
|
||||
/* Free message since it is not used anymore */
|
||||
msg_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -2422,53 +2551,132 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
*/
|
||||
|
||||
|
||||
static int apiserver_notify_clients_lsa(uint8_t msgtype, struct ospf_lsa *lsa)
|
||||
int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
|
||||
{
|
||||
struct msg *msg;
|
||||
/* default area for AS-External and Opaque11 LSAs */
|
||||
struct in_addr area_id = {.s_addr = 0L};
|
||||
|
||||
/* default interface for non Opaque9 LSAs */
|
||||
struct in_addr ifaddr = {.s_addr = 0L};
|
||||
|
||||
/* Only notify this update if the LSA's age is smaller than
|
||||
MAXAGE. Otherwise clients would see LSA updates with max age just
|
||||
before they are deleted from the LSDB. LSA delete messages have
|
||||
MAXAGE too but should not be filtered. */
|
||||
if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) {
|
||||
if (IS_LSA_MAXAGE(lsa))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lsa->area) {
|
||||
area_id = lsa->area->area_id;
|
||||
}
|
||||
if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) {
|
||||
ifaddr = lsa->oi->address->u.prefix4;
|
||||
}
|
||||
msg = new_msg_lsa_change_notify(msgtype, 0L, /* no sequence number */
|
||||
ifaddr, area_id,
|
||||
lsa->flags & OSPF_LSA_SELF, lsa->data);
|
||||
if (!msg) {
|
||||
zlog_warn("notify_clients_lsa: msg_new failed");
|
||||
return -1;
|
||||
}
|
||||
/* Notify all clients that new LSA is added/updated */
|
||||
apiserver_clients_lsa_change_notify(msgtype, lsa);
|
||||
|
||||
/* Clients made their own copies of msg so we can free msg here */
|
||||
msg_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
|
||||
{
|
||||
return apiserver_notify_clients_lsa(MSG_LSA_UPDATE_NOTIFY, lsa);
|
||||
return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa);
|
||||
}
|
||||
|
||||
int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
|
||||
{
|
||||
return apiserver_notify_clients_lsa(MSG_LSA_DELETE_NOTIFY, lsa);
|
||||
return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------
|
||||
* Reachable functions
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static inline int cmp_route_nodes(struct route_node *orn,
|
||||
struct route_node *nrn)
|
||||
{
|
||||
if (!orn)
|
||||
return 1;
|
||||
else if (!nrn)
|
||||
return -1;
|
||||
else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr)
|
||||
return -1;
|
||||
else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ospf_apiserver_notify_reachable(struct route_table *ort,
|
||||
struct route_table *nrt)
|
||||
{
|
||||
struct msg *msg;
|
||||
struct msg_reachable_change *areach;
|
||||
struct route_node *orn, *nrn;
|
||||
const uint insz = sizeof(struct in_addr);
|
||||
struct in_addr *abuf = NULL, *dbuf = NULL;
|
||||
struct in_addr *a = NULL, *d = NULL;
|
||||
uint nadd, nremove;
|
||||
int cmp;
|
||||
|
||||
if (!ort && !nrt) {
|
||||
if (IS_DEBUG_OSPF_CLIENT_API)
|
||||
zlog_debug("%s: no routing tables", __func__);
|
||||
return;
|
||||
}
|
||||
if (nrt && nrt->count)
|
||||
a = abuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * nrt->count);
|
||||
if (ort && ort->count)
|
||||
d = dbuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * ort->count);
|
||||
|
||||
/* walk both tables */
|
||||
orn = ort ? route_top(ort) : NULL;
|
||||
nrn = nrt ? route_top(nrt) : NULL;
|
||||
while (orn || nrn) {
|
||||
if (orn && !listhead((struct list *)orn->info)) {
|
||||
orn = route_next(orn);
|
||||
continue;
|
||||
}
|
||||
if (nrn && !listhead((struct list *)nrn->info)) {
|
||||
nrn = route_next(nrn);
|
||||
continue;
|
||||
}
|
||||
cmp = cmp_route_nodes(orn, nrn);
|
||||
if (!cmp) {
|
||||
/* if old == new advance old and new */
|
||||
if (IS_DEBUG_OSPF_CLIENT_API)
|
||||
zlog_debug("keeping router id: %pI4",
|
||||
&orn->p.u.prefix4);
|
||||
orn = route_next(orn);
|
||||
nrn = route_next(nrn);
|
||||
} else if (cmp < 0) {
|
||||
assert(d != NULL); /* Silence SA warning */
|
||||
|
||||
/* if old < new, delete old, advance old */
|
||||
*d++ = orn->p.u.prefix4;
|
||||
if (IS_DEBUG_OSPF_CLIENT_API)
|
||||
zlog_debug("removing router id: %pI4",
|
||||
&orn->p.u.prefix4);
|
||||
orn = route_next(orn);
|
||||
} else {
|
||||
assert(a != NULL); /* Silence SA warning */
|
||||
|
||||
/* if new < old, add new, advance new */
|
||||
*a++ = nrn->p.u.prefix4;
|
||||
if (IS_DEBUG_OSPF_CLIENT_API)
|
||||
zlog_debug("adding router id: %pI4",
|
||||
&nrn->p.u.prefix4);
|
||||
nrn = route_next(nrn);
|
||||
}
|
||||
}
|
||||
|
||||
nadd = abuf ? (a - abuf) : 0;
|
||||
nremove = dbuf ? (d - dbuf) : 0;
|
||||
a = abuf;
|
||||
d = dbuf;
|
||||
|
||||
while (nadd + nremove) {
|
||||
msg = new_msg_reachable_change(0, nadd, a, nremove, d);
|
||||
areach = (struct msg_reachable_change *)STREAM_DATA(msg->s);
|
||||
|
||||
a += ntohs(areach->nadd);
|
||||
nadd = nadd - ntohs(areach->nadd);
|
||||
|
||||
d += ntohs(areach->nremove);
|
||||
nremove = nremove - ntohs(areach->nremove);
|
||||
|
||||
if (IS_DEBUG_OSPF_CLIENT_API)
|
||||
zlog_debug("%s: adding %d removing %d", __func__,
|
||||
ntohs(areach->nadd), ntohs(areach->nremove));
|
||||
ospf_apiserver_clients_notify_all(msg);
|
||||
msg_free(msg);
|
||||
}
|
||||
if (abuf)
|
||||
XFREE(MTYPE_OSPF_APISERVER, abuf);
|
||||
if (dbuf)
|
||||
XFREE(MTYPE_OSPF_APISERVER, dbuf);
|
||||
}
|
||||
|
||||
|
||||
#endif /* SUPPORT_OSPF_API */
|
||||
|
@ -22,6 +22,10 @@
|
||||
#ifndef _OSPF_APISERVER_H
|
||||
#define _OSPF_APISERVER_H
|
||||
|
||||
#include <zebra.h>
|
||||
#include "ospf_api.h"
|
||||
#include "ospf_lsdb.h"
|
||||
|
||||
/* MTYPE definition is not reflected to "memory.h". */
|
||||
#define MTYPE_OSPF_APISERVER MTYPE_TMP
|
||||
#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP
|
||||
@ -52,6 +56,9 @@ struct ospf_apiserver {
|
||||
/* Temporary storage for LSA instances to be refreshed. */
|
||||
struct ospf_lsdb reserve;
|
||||
|
||||
/* Sync reachable routers */
|
||||
bool reachable_sync;
|
||||
|
||||
/* filter for LSA update/delete notifies */
|
||||
struct lsa_filter_type *filter;
|
||||
|
||||
@ -144,7 +151,15 @@ extern int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg);
|
||||
extern int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg);
|
||||
extern int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg);
|
||||
extern int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg);
|
||||
extern int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
|
||||
struct msg *msg);
|
||||
|
||||
extern void ospf_apiserver_notify_reachable(struct route_table *ort,
|
||||
struct route_table *nrt);
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
* Following are functions for LSA origination/deletion
|
||||
@ -164,7 +179,8 @@ extern struct ospf_interface *
|
||||
ospf_apiserver_if_lookup_by_addr(struct in_addr address);
|
||||
extern struct ospf_interface *
|
||||
ospf_apiserver_if_lookup_by_ifp(struct interface *ifp);
|
||||
extern int ospf_apiserver_originate1(struct ospf_lsa *lsa);
|
||||
extern int ospf_apiserver_originate1(struct ospf_lsa *lsa,
|
||||
struct ospf_lsa *old);
|
||||
extern void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa);
|
||||
|
||||
|
||||
@ -201,7 +217,4 @@ extern void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv,
|
||||
extern int ospf_apiserver_lsa_update(struct ospf_lsa *lsa);
|
||||
extern int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa);
|
||||
|
||||
extern void ospf_apiserver_clients_lsa_change_notify(uint8_t msgtype,
|
||||
struct ospf_lsa *lsa);
|
||||
|
||||
#endif /* _OSPF_APISERVER_H */
|
||||
|
@ -62,6 +62,7 @@ unsigned long conf_debug_ospf_defaultinfo = 0;
|
||||
unsigned long conf_debug_ospf_ldp_sync = 0;
|
||||
unsigned long conf_debug_ospf_gr = 0;
|
||||
unsigned long conf_debug_ospf_bfd;
|
||||
unsigned long conf_debug_ospf_client_api;
|
||||
|
||||
/* Enable debug option variables -- valid only session. */
|
||||
unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
|
||||
@ -79,6 +80,7 @@ unsigned long term_debug_ospf_defaultinfo;
|
||||
unsigned long term_debug_ospf_ldp_sync;
|
||||
unsigned long term_debug_ospf_gr = 0;
|
||||
unsigned long term_debug_ospf_bfd;
|
||||
unsigned long term_debug_ospf_client_api;
|
||||
|
||||
const char *ospf_redist_string(unsigned int route_type)
|
||||
{
|
||||
@ -1620,6 +1622,33 @@ DEFPY(debug_ospf_bfd, debug_ospf_bfd_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(debug_ospf_client_api,
|
||||
debug_ospf_client_api_cmd,
|
||||
"debug ospf client-api",
|
||||
DEBUG_STR OSPF_STR
|
||||
"OSPF client API information\n")
|
||||
{
|
||||
if (vty->node == CONFIG_NODE)
|
||||
CONF_DEBUG_ON(client_api, CLIENT_API);
|
||||
TERM_DEBUG_ON(client_api, CLIENT_API);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(no_debug_ospf_client_api,
|
||||
no_debug_ospf_client_api_cmd,
|
||||
"no debug ospf client-api",
|
||||
NO_STR
|
||||
DEBUG_STR
|
||||
OSPF_STR
|
||||
"OSPF client API information\n")
|
||||
{
|
||||
if (vty->node == CONFIG_NODE)
|
||||
CONF_DEBUG_OFF(client_api, CLIENT_API);
|
||||
TERM_DEBUG_OFF(client_api, CLIENT_API);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN (no_debug_ospf,
|
||||
no_debug_ospf_cmd,
|
||||
"no debug ospf",
|
||||
@ -1654,6 +1683,7 @@ DEFUN (no_debug_ospf,
|
||||
DEBUG_OFF(te, TE);
|
||||
DEBUG_OFF(sr, SR);
|
||||
DEBUG_OFF(ti_lfa, TI_LFA);
|
||||
DEBUG_OFF(client_api, CLIENT_API);
|
||||
|
||||
/* BFD debugging is two parts: OSPF and library. */
|
||||
DEBUG_OFF(bfd, BFD_LIB);
|
||||
@ -1690,6 +1720,7 @@ DEFUN (no_debug_ospf,
|
||||
TERM_DEBUG_OFF(sr, SR);
|
||||
TERM_DEBUG_OFF(ti_lfa, TI_LFA);
|
||||
TERM_DEBUG_OFF(bfd, BFD_LIB);
|
||||
TERM_DEBUG_OFF(client_api, CLIENT_API);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@ -1815,6 +1846,10 @@ static int show_debugging_ospf_common(struct vty *vty)
|
||||
vty_out(vty,
|
||||
" OSPF BFD integration library debugging is on\n");
|
||||
|
||||
/* Show debug status for LDP-SYNC. */
|
||||
if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API)
|
||||
vty_out(vty, " OSPF client-api debugging is on\n");
|
||||
|
||||
vty_out(vty, "\n");
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@ -2007,6 +2042,13 @@ static int config_write_debug(struct vty *vty)
|
||||
write = 1;
|
||||
}
|
||||
|
||||
/* debug ospf client-api */
|
||||
if (IS_CONF_DEBUG_OSPF(client_api, CLIENT_API) ==
|
||||
OSPF_DEBUG_CLIENT_API) {
|
||||
vty_out(vty, "debug ospf%s client-api\n", str);
|
||||
write = 1;
|
||||
}
|
||||
|
||||
return write;
|
||||
}
|
||||
|
||||
@ -2027,6 +2069,7 @@ void ospf_debug_init(void)
|
||||
install_element(ENABLE_NODE, &debug_ospf_ti_lfa_cmd);
|
||||
install_element(ENABLE_NODE, &debug_ospf_default_info_cmd);
|
||||
install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd);
|
||||
install_element(ENABLE_NODE, &debug_ospf_client_api_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
|
||||
@ -2038,6 +2081,7 @@ void ospf_debug_init(void)
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_ti_lfa_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd);
|
||||
install_element(ENABLE_NODE, &no_debug_ospf_client_api_cmd);
|
||||
install_element(ENABLE_NODE, &debug_ospf_gr_cmd);
|
||||
install_element(ENABLE_NODE, &debug_ospf_bfd_cmd);
|
||||
|
||||
@ -2072,6 +2116,7 @@ void ospf_debug_init(void)
|
||||
install_element(CONFIG_NODE, &debug_ospf_ti_lfa_cmd);
|
||||
install_element(CONFIG_NODE, &debug_ospf_default_info_cmd);
|
||||
install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd);
|
||||
install_element(CONFIG_NODE, &debug_ospf_client_api_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
|
||||
@ -2082,6 +2127,7 @@ void ospf_debug_init(void)
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_ti_lfa_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd);
|
||||
install_element(CONFIG_NODE, &no_debug_ospf_client_api_cmd);
|
||||
install_element(CONFIG_NODE, &debug_ospf_gr_cmd);
|
||||
install_element(CONFIG_NODE, &debug_ospf_bfd_cmd);
|
||||
|
||||
|
@ -68,6 +68,8 @@
|
||||
|
||||
#define OSPF_DEBUG_BFD_LIB 0x01
|
||||
|
||||
#define OSPF_DEBUG_CLIENT_API 0x01
|
||||
|
||||
/* Macro for setting debug option. */
|
||||
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
|
||||
#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b)
|
||||
@ -118,6 +120,7 @@
|
||||
|
||||
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
|
||||
#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
|
||||
#define IS_DEBUG_OSPF_CLIENT_API IS_DEBUG_OSPF(client_api, CLIENT_API)
|
||||
|
||||
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
|
||||
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
|
||||
@ -142,6 +145,7 @@ extern unsigned long term_debug_ospf_defaultinfo;
|
||||
extern unsigned long term_debug_ospf_ldp_sync;
|
||||
extern unsigned long term_debug_ospf_gr;
|
||||
extern unsigned long term_debug_ospf_bfd;
|
||||
extern unsigned long term_debug_ospf_client_api;
|
||||
|
||||
/* Message Strings. */
|
||||
extern char *ospf_lsa_type_str[];
|
||||
|
@ -74,9 +74,8 @@ int ospf_apiserver_enable;
|
||||
static void ospf_opaque_register_vty(void);
|
||||
static void ospf_opaque_funclist_init(void);
|
||||
static void ospf_opaque_funclist_term(void);
|
||||
static void free_opaque_info_per_type(void *val);
|
||||
static void free_opaque_info_per_type_del(void *val);
|
||||
static void free_opaque_info_per_id(void *val);
|
||||
static void free_opaque_info_owner(void *val);
|
||||
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
|
||||
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
|
||||
|
||||
@ -141,7 +140,7 @@ int ospf_opaque_type9_lsa_init(struct ospf_interface *oi)
|
||||
list_delete(&oi->opaque_lsa_self);
|
||||
|
||||
oi->opaque_lsa_self = list_new();
|
||||
oi->opaque_lsa_self->del = free_opaque_info_per_type;
|
||||
oi->opaque_lsa_self->del = free_opaque_info_per_type_del;
|
||||
oi->t_opaque_lsa_self = NULL;
|
||||
return 0;
|
||||
}
|
||||
@ -161,7 +160,7 @@ int ospf_opaque_type10_lsa_init(struct ospf_area *area)
|
||||
list_delete(&area->opaque_lsa_self);
|
||||
|
||||
area->opaque_lsa_self = list_new();
|
||||
area->opaque_lsa_self->del = free_opaque_info_per_type;
|
||||
area->opaque_lsa_self->del = free_opaque_info_per_type_del;
|
||||
area->t_opaque_lsa_self = NULL;
|
||||
|
||||
#ifdef MONITOR_LSDB_CHANGE
|
||||
@ -189,7 +188,7 @@ int ospf_opaque_type11_lsa_init(struct ospf *top)
|
||||
list_delete(&top->opaque_lsa_self);
|
||||
|
||||
top->opaque_lsa_self = list_new();
|
||||
top->opaque_lsa_self->del = free_opaque_info_per_type;
|
||||
top->opaque_lsa_self->del = free_opaque_info_per_type_del;
|
||||
top->t_opaque_lsa_self = NULL;
|
||||
|
||||
#ifdef MONITOR_LSDB_CHANGE
|
||||
@ -263,6 +262,9 @@ static const char *ospf_opaque_type_name(uint8_t opaque_type)
|
||||
|
||||
struct opaque_info_per_type; /* Forward declaration. */
|
||||
|
||||
static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
|
||||
bool cleanup_owner);
|
||||
|
||||
struct ospf_opaque_functab {
|
||||
uint8_t opaque_type;
|
||||
struct opaque_info_per_type *oipt;
|
||||
@ -433,12 +435,9 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type)
|
||||
if (functab->opaque_type == opaque_type) {
|
||||
/* Cleanup internal control information, if it
|
||||
* still remains. */
|
||||
if (functab->oipt != NULL) {
|
||||
free_opaque_info_owner(functab->oipt);
|
||||
free_opaque_info_per_type(
|
||||
functab->oipt);
|
||||
}
|
||||
|
||||
if (functab->oipt != NULL)
|
||||
free_opaque_info_per_type(functab->oipt,
|
||||
true);
|
||||
/* Dequeue listnode entry from the list. */
|
||||
listnode_delete(funclist, functab);
|
||||
|
||||
@ -558,8 +557,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
|
||||
case OSPF_OPAQUE_AS_LSA:
|
||||
top = ospf_lookup_by_vrf_id(new->vrf_id);
|
||||
if (new->area != NULL && (top = new->area->ospf) == NULL) {
|
||||
free_opaque_info_owner(oipt);
|
||||
free_opaque_info_per_type(oipt);
|
||||
free_opaque_info_per_type(oipt, true);
|
||||
oipt = NULL;
|
||||
goto out; /* This case may not exist. */
|
||||
}
|
||||
@ -571,8 +569,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
|
||||
EC_OSPF_LSA_UNEXPECTED,
|
||||
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
|
||||
new->data->type);
|
||||
free_opaque_info_owner(oipt);
|
||||
free_opaque_info_per_type(oipt);
|
||||
free_opaque_info_per_type(oipt, true);
|
||||
oipt = NULL;
|
||||
goto out; /* This case may not exist. */
|
||||
}
|
||||
@ -589,42 +586,13 @@ out:
|
||||
return oipt;
|
||||
}
|
||||
|
||||
/* Remove "oipt" from its owner's self-originated LSA list. */
|
||||
static void free_opaque_info_owner(void *val)
|
||||
static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
|
||||
bool cleanup_owner)
|
||||
{
|
||||
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
|
||||
|
||||
switch (oipt->lsa_type) {
|
||||
case OSPF_OPAQUE_LINK_LSA: {
|
||||
struct ospf_interface *oi =
|
||||
(struct ospf_interface *)(oipt->owner);
|
||||
listnode_delete(oi->opaque_lsa_self, oipt);
|
||||
break;
|
||||
}
|
||||
case OSPF_OPAQUE_AREA_LSA: {
|
||||
struct ospf_area *area = (struct ospf_area *)(oipt->owner);
|
||||
listnode_delete(area->opaque_lsa_self, oipt);
|
||||
break;
|
||||
}
|
||||
case OSPF_OPAQUE_AS_LSA: {
|
||||
struct ospf *top = (struct ospf *)(oipt->owner);
|
||||
listnode_delete(top->opaque_lsa_self, oipt);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
flog_warn(EC_OSPF_LSA_UNEXPECTED,
|
||||
"free_opaque_info_owner: Unexpected LSA-type(%u)",
|
||||
oipt->lsa_type);
|
||||
break; /* This case may not exist. */
|
||||
}
|
||||
}
|
||||
|
||||
static void free_opaque_info_per_type(void *val)
|
||||
{
|
||||
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
|
||||
struct opaque_info_per_id *oipi;
|
||||
struct ospf_lsa *lsa;
|
||||
struct listnode *node, *nnode;
|
||||
struct list *l;
|
||||
|
||||
/* Control information per opaque-id may still exist. */
|
||||
for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) {
|
||||
@ -637,10 +605,37 @@ static void free_opaque_info_per_type(void *val)
|
||||
|
||||
OSPF_TIMER_OFF(oipt->t_opaque_lsa_self);
|
||||
list_delete(&oipt->id_list);
|
||||
if (cleanup_owner) {
|
||||
/* Remove from its owner's self-originated LSA list. */
|
||||
switch (oipt->lsa_type) {
|
||||
case OSPF_OPAQUE_LINK_LSA:
|
||||
l = ((struct ospf_interface *)oipt->owner)
|
||||
->opaque_lsa_self;
|
||||
break;
|
||||
case OSPF_OPAQUE_AREA_LSA:
|
||||
l = ((struct ospf_area *)oipt->owner)->opaque_lsa_self;
|
||||
break;
|
||||
case OSPF_OPAQUE_AS_LSA:
|
||||
l = ((struct ospf *)oipt->owner)->opaque_lsa_self;
|
||||
break;
|
||||
default:
|
||||
flog_warn(
|
||||
EC_OSPF_LSA_UNEXPECTED,
|
||||
"free_opaque_info_owner: Unexpected LSA-type(%u)",
|
||||
oipt->lsa_type);
|
||||
return;
|
||||
}
|
||||
listnode_delete(l, oipt);
|
||||
}
|
||||
XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
|
||||
return;
|
||||
}
|
||||
|
||||
static void free_opaque_info_per_type_del(void *val)
|
||||
{
|
||||
free_opaque_info_per_type((struct opaque_info_per_type *)val, false);
|
||||
}
|
||||
|
||||
static struct opaque_info_per_type *
|
||||
lookup_opaque_info_by_type(struct ospf_lsa *lsa)
|
||||
{
|
||||
@ -758,6 +753,13 @@ out:
|
||||
return oipi;
|
||||
}
|
||||
|
||||
int ospf_opaque_is_owned(struct ospf_lsa *lsa)
|
||||
{
|
||||
struct opaque_info_per_type *oipt = lookup_opaque_info_by_type(lsa);
|
||||
|
||||
return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* Following are (vty) configuration functions for Opaque-LSAs handling.
|
||||
*------------------------------------------------------------------------*/
|
||||
|
@ -173,4 +173,6 @@ extern void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
|
||||
struct ospf_lsa *lsa);
|
||||
extern struct ospf *oi_to_top(struct ospf_interface *oi);
|
||||
|
||||
extern int ospf_opaque_is_owned(struct ospf_lsa *lsa);
|
||||
|
||||
#endif /* _ZEBRA_OSPF_OPAQUE_H */
|
||||
|
@ -363,7 +363,7 @@ void ospf_route_install(struct ospf *ospf, struct route_table *rt)
|
||||
|
||||
/* RFC2328 16.1. (4). For "router". */
|
||||
void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
|
||||
struct ospf_area *area)
|
||||
struct ospf_area *area, bool add_all)
|
||||
{
|
||||
struct route_node *rn;
|
||||
struct ospf_route * or ;
|
||||
@ -388,7 +388,8 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
|
||||
/* If the newly added vertex is an area border router or AS boundary
|
||||
router, a routing table entry is added whose destination type is
|
||||
"router". */
|
||||
if (!IS_ROUTER_LSA_BORDER(lsa) && !IS_ROUTER_LSA_EXTERNAL(lsa)) {
|
||||
if (!add_all && !IS_ROUTER_LSA_BORDER(lsa) &&
|
||||
!IS_ROUTER_LSA_EXTERNAL(lsa)) {
|
||||
if (IS_DEBUG_OSPF_EVENT)
|
||||
zlog_debug(
|
||||
"ospf_intra_add_router: this router is neither ASBR nor ABR, skipping it");
|
||||
@ -733,6 +734,24 @@ void ospf_route_table_dump(struct route_table *rt)
|
||||
zlog_debug("========================================");
|
||||
}
|
||||
|
||||
void ospf_router_route_table_dump(struct route_table *rt)
|
||||
{
|
||||
struct route_node *rn;
|
||||
struct ospf_route *or;
|
||||
struct listnode *node;
|
||||
|
||||
zlog_debug("========== OSPF routing table ==========");
|
||||
for (rn = route_top(rt); rn; rn = route_next(rn)) {
|
||||
for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) {
|
||||
assert(or->type == OSPF_DESTINATION_ROUTER);
|
||||
zlog_debug("R %-18pI4 %-15pI4 %s %d", &rn->p.u.prefix4,
|
||||
&or->u.std.area_id,
|
||||
ospf_path_type_str[or->path_type], or->cost);
|
||||
}
|
||||
}
|
||||
zlog_debug("========================================");
|
||||
}
|
||||
|
||||
/* This is 16.4.1 implementation.
|
||||
o Intra-area paths using non-backbone areas are always the most preferred.
|
||||
o The other paths, intra-area backbone paths and inter-area paths,
|
||||
|
@ -139,9 +139,10 @@ extern void ospf_route_table_free(struct route_table *);
|
||||
|
||||
extern void ospf_route_install(struct ospf *, struct route_table *);
|
||||
extern void ospf_route_table_dump(struct route_table *);
|
||||
extern void ospf_router_route_table_dump(struct route_table *rt);
|
||||
|
||||
extern void ospf_intra_add_router(struct route_table *, struct vertex *,
|
||||
struct ospf_area *);
|
||||
extern void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
|
||||
struct ospf_area *area, bool add_all);
|
||||
|
||||
extern void ospf_intra_add_transit(struct route_table *, struct vertex *,
|
||||
struct ospf_area *);
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "ospfd/ospf_sr.h"
|
||||
#include "ospfd/ospf_ti_lfa.h"
|
||||
#include "ospfd/ospf_errors.h"
|
||||
#include "ospfd/ospf_apiserver.h"
|
||||
|
||||
/* Variables to ensure a SPF scheduled log message is printed only once */
|
||||
|
||||
@ -1669,6 +1670,7 @@ void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list)
|
||||
/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */
|
||||
void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
|
||||
struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs, bool is_dry_run,
|
||||
bool is_root_node)
|
||||
{
|
||||
@ -1737,10 +1739,13 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
|
||||
ospf_vertex_add_parent(v);
|
||||
|
||||
/* RFC2328 16.1. (4). */
|
||||
if (v->type == OSPF_VERTEX_ROUTER)
|
||||
ospf_intra_add_router(new_rtrs, v, area);
|
||||
else
|
||||
if (v->type != OSPF_VERTEX_ROUTER)
|
||||
ospf_intra_add_transit(new_table, v, area);
|
||||
else {
|
||||
ospf_intra_add_router(new_rtrs, v, area, false);
|
||||
if (all_rtrs)
|
||||
ospf_intra_add_router(all_rtrs, v, area, true);
|
||||
}
|
||||
|
||||
/* Iterate back to (2), see RFC2328 16.1. (5). */
|
||||
}
|
||||
@ -1748,6 +1753,8 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
|
||||
if (IS_DEBUG_OSPF_EVENT) {
|
||||
ospf_spf_dump(area->spf, 0);
|
||||
ospf_route_table_dump(new_table);
|
||||
if (all_rtrs)
|
||||
ospf_router_route_table_dump(all_rtrs);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1771,10 +1778,11 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
|
||||
|
||||
void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
|
||||
struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs)
|
||||
{
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
|
||||
false, true);
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
|
||||
new_rtrs, false, true);
|
||||
|
||||
if (ospf->ti_lfa_enabled)
|
||||
ospf_ti_lfa_compute(area, new_table,
|
||||
@ -1787,6 +1795,7 @@ void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
|
||||
}
|
||||
|
||||
void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs)
|
||||
{
|
||||
struct ospf_area *area;
|
||||
@ -1799,13 +1808,14 @@ void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
|
||||
if (ospf->backbone && ospf->backbone == area)
|
||||
continue;
|
||||
|
||||
ospf_spf_calculate_area(ospf, area, new_table, new_rtrs);
|
||||
ospf_spf_calculate_area(ospf, area, new_table, all_rtrs,
|
||||
new_rtrs);
|
||||
}
|
||||
|
||||
/* SPF for backbone, if required */
|
||||
if (ospf->backbone)
|
||||
ospf_spf_calculate_area(ospf, ospf->backbone, new_table,
|
||||
new_rtrs);
|
||||
all_rtrs, new_rtrs);
|
||||
}
|
||||
|
||||
/* Worker for SPF calculation scheduler. */
|
||||
@ -1813,6 +1823,7 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
|
||||
{
|
||||
struct ospf *ospf = THREAD_ARG(thread);
|
||||
struct route_table *new_table, *new_rtrs;
|
||||
struct route_table *all_rtrs = NULL;
|
||||
struct timeval start_time, spf_start_time;
|
||||
unsigned long ia_time, prune_time, rt_time;
|
||||
unsigned long abr_time, total_spf_time, spf_time;
|
||||
@ -1829,7 +1840,12 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
|
||||
monotime(&spf_start_time);
|
||||
new_table = route_table_init(); /* routing table */
|
||||
new_rtrs = route_table_init(); /* ABR/ASBR routing table */
|
||||
ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
|
||||
|
||||
/* If we have opaque enabled then track all router reachability */
|
||||
if (CHECK_FLAG(ospf->opaque, OPAQUE_OPERATION_READY_BIT))
|
||||
all_rtrs = route_table_init();
|
||||
|
||||
ospf_spf_calculate_areas(ospf, new_table, all_rtrs, new_rtrs);
|
||||
spf_time = monotime_since(&spf_start_time, NULL);
|
||||
|
||||
ospf_vl_shut_unapproved(ospf);
|
||||
@ -1842,6 +1858,8 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
|
||||
/* Get rid of transit networks and routers we cannot reach anyway. */
|
||||
monotime(&start_time);
|
||||
ospf_prune_unreachable_networks(new_table);
|
||||
if (all_rtrs)
|
||||
ospf_prune_unreachable_routers(all_rtrs);
|
||||
ospf_prune_unreachable_routers(new_rtrs);
|
||||
prune_time = monotime_since(&start_time, NULL);
|
||||
|
||||
@ -1866,6 +1884,16 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
|
||||
ospf_route_install(ospf, new_table);
|
||||
rt_time = monotime_since(&start_time, NULL);
|
||||
|
||||
/* Free old all routers routing table */
|
||||
if (ospf->oall_rtrs)
|
||||
/* ospf_route_delete (ospf->old_rtrs); */
|
||||
ospf_rtrs_free(ospf->oall_rtrs);
|
||||
|
||||
/* Update all routers routing table */
|
||||
ospf->oall_rtrs = ospf->all_rtrs;
|
||||
ospf->all_rtrs = all_rtrs;
|
||||
ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs);
|
||||
|
||||
/* Free old ABR/ASBR routing table */
|
||||
if (ospf->old_rtrs)
|
||||
/* ospf_route_delete (ospf->old_rtrs); */
|
||||
|
@ -76,13 +76,16 @@ extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t);
|
||||
extern void ospf_spf_calculate(struct ospf_area *area,
|
||||
struct ospf_lsa *root_lsa,
|
||||
struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs, bool is_dry_run,
|
||||
bool is_root_node);
|
||||
extern void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
|
||||
struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs);
|
||||
extern void ospf_spf_calculate_areas(struct ospf *ospf,
|
||||
struct route_table *new_table,
|
||||
struct route_table *all_rtrs,
|
||||
struct route_table *new_rtrs);
|
||||
extern void ospf_rtrs_free(struct route_table *);
|
||||
extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list);
|
||||
|
@ -326,8 +326,8 @@ static void ospf_ti_lfa_generate_inner_label_stack(
|
||||
XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head));
|
||||
|
||||
/* dry run true, root node false */
|
||||
ospf_spf_calculate(area, start_vertex->lsa_p, new_table, new_rtrs, true,
|
||||
false);
|
||||
ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, new_rtrs,
|
||||
true, false);
|
||||
|
||||
q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list);
|
||||
|
||||
@ -676,6 +676,7 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
|
||||
sizeof(struct ospf_ti_lfa_node_info));
|
||||
|
||||
new_table = route_table_init();
|
||||
/* XXX do these get freed?? */
|
||||
new_rtrs = route_table_init();
|
||||
|
||||
/*
|
||||
@ -683,7 +684,8 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
|
||||
* dry run true, root node false
|
||||
*/
|
||||
area->spf_reversed = true;
|
||||
ospf_spf_calculate(area, dest->lsa_p, new_table, new_rtrs, true, false);
|
||||
ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, new_rtrs, true,
|
||||
false);
|
||||
|
||||
/* Reset the flag for reverse SPF */
|
||||
area->spf_reversed = false;
|
||||
@ -750,6 +752,7 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
|
||||
struct route_table *new_table, *new_rtrs;
|
||||
|
||||
new_table = route_table_init();
|
||||
/* XXX do these get freed?? */
|
||||
new_rtrs = route_table_init();
|
||||
|
||||
area->spf_protected_resource = p_space->protected_resource;
|
||||
@ -769,8 +772,8 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
|
||||
* endeavour (because LSAs are stored as a 'raw' stream), so we go with
|
||||
* this rather hacky way for now.
|
||||
*/
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
|
||||
true, false);
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL,
|
||||
new_rtrs, true, false);
|
||||
|
||||
p_space->pc_spf = area->spf;
|
||||
p_space->pc_vertex_list = area->spf_vertex_list;
|
||||
|
120
ospfd/ospf_vty.c
120
ospfd/ospf_vty.c
@ -10741,8 +10741,9 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf,
|
||||
*json_nexthop = NULL;
|
||||
|
||||
if (!json)
|
||||
vty_out(vty,
|
||||
"============ OSPF router routing table =============\n");
|
||||
vty_out(vty, "============ OSPF %s table =============\n",
|
||||
ospf->all_rtrs == rtrs ? "reachable routers"
|
||||
: "router routing");
|
||||
|
||||
for (rn = route_top(rtrs); rn; rn = route_next(rn)) {
|
||||
if (rn->info == NULL)
|
||||
@ -11004,6 +11005,114 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf,
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
|
||||
static int show_ip_ospf_reachable_routers_common(struct vty *vty,
|
||||
struct ospf *ospf,
|
||||
uint8_t use_vrf)
|
||||
{
|
||||
if (ospf->instance)
|
||||
vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance);
|
||||
|
||||
ospf_show_vrf_name(ospf, vty, NULL, use_vrf);
|
||||
|
||||
if (ospf->all_rtrs == NULL) {
|
||||
vty_out(vty, "No OSPF reachable router information exist\n");
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Show Router routes. */
|
||||
show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, NULL);
|
||||
|
||||
vty_out(vty, "\n");
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN (show_ip_ospf_reachable_routers,
|
||||
show_ip_ospf_reachable_routers_cmd,
|
||||
"show ip ospf [vrf <NAME|all>] reachable-routers",
|
||||
SHOW_STR
|
||||
IP_STR
|
||||
"OSPF information\n"
|
||||
VRF_CMD_HELP_STR
|
||||
"All VRFs\n"
|
||||
"Show all the reachable OSPF routers\n")
|
||||
{
|
||||
struct ospf *ospf = NULL;
|
||||
struct listnode *node = NULL;
|
||||
char *vrf_name = NULL;
|
||||
bool all_vrf = false;
|
||||
int ret = CMD_SUCCESS;
|
||||
int inst = 0;
|
||||
int idx_vrf = 0;
|
||||
uint8_t use_vrf = 0;
|
||||
|
||||
OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
|
||||
|
||||
if (vrf_name) {
|
||||
bool ospf_output = false;
|
||||
|
||||
use_vrf = 1;
|
||||
|
||||
if (all_vrf) {
|
||||
for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
|
||||
if (!ospf->oi_running)
|
||||
continue;
|
||||
|
||||
ospf_output = true;
|
||||
ret = show_ip_ospf_reachable_routers_common(
|
||||
vty, ospf, use_vrf);
|
||||
}
|
||||
|
||||
if (!ospf_output)
|
||||
vty_out(vty, "%% OSPF instance not found\n");
|
||||
} else {
|
||||
ospf = ospf_lookup_by_inst_name(inst, vrf_name);
|
||||
if (ospf == NULL || !ospf->oi_running) {
|
||||
vty_out(vty, "%% OSPF instance not found\n");
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
ret = show_ip_ospf_reachable_routers_common(vty, ospf,
|
||||
use_vrf);
|
||||
}
|
||||
} else {
|
||||
/* Display default ospf (instance 0) info */
|
||||
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
if (ospf == NULL || !ospf->oi_running) {
|
||||
vty_out(vty, "%% OSPF instance not found\n");
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
ret = show_ip_ospf_reachable_routers_common(vty, ospf, use_vrf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFUN (show_ip_ospf_instance_reachable_routers,
|
||||
show_ip_ospf_instance_reachable_routers_cmd,
|
||||
"show ip ospf (1-65535) reachable-routers",
|
||||
SHOW_STR
|
||||
IP_STR
|
||||
"OSPF information\n"
|
||||
"Instance ID\n"
|
||||
"Show all the reachable OSPF routers\n")
|
||||
{
|
||||
int idx_number = 3;
|
||||
struct ospf *ospf;
|
||||
unsigned short instance = 0;
|
||||
|
||||
instance = strtoul(argv[idx_number]->arg, NULL, 10);
|
||||
if (instance != ospf_instance)
|
||||
return CMD_NOT_MY_INSTANCE;
|
||||
|
||||
ospf = ospf_lookup_instance(instance);
|
||||
if (!ospf || !ospf->oi_running)
|
||||
return CMD_SUCCESS;
|
||||
|
||||
return show_ip_ospf_reachable_routers_common(vty, ospf, 0);
|
||||
}
|
||||
|
||||
static int show_ip_ospf_border_routers_common(struct vty *vty,
|
||||
struct ospf *ospf,
|
||||
uint8_t use_vrf)
|
||||
@ -11146,6 +11255,10 @@ static int show_ip_ospf_route_common(struct vty *vty, struct ospf *ospf,
|
||||
/* Show Router routes. */
|
||||
show_ip_ospf_route_router(vty, ospf, ospf->new_rtrs, json_vrf);
|
||||
|
||||
/* Show Router routes. */
|
||||
if (ospf->all_rtrs)
|
||||
show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, json_vrf);
|
||||
|
||||
/* Show AS External routes. */
|
||||
show_ip_ospf_route_external(vty, ospf, ospf->old_external_route,
|
||||
json_vrf);
|
||||
@ -12603,9 +12716,12 @@ void ospf_vty_show_init(void)
|
||||
/* "show ip ospf route" commands. */
|
||||
install_element(VIEW_NODE, &show_ip_ospf_route_cmd);
|
||||
install_element(VIEW_NODE, &show_ip_ospf_border_routers_cmd);
|
||||
install_element(VIEW_NODE, &show_ip_ospf_reachable_routers_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_ip_ospf_instance_route_cmd);
|
||||
install_element(VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd);
|
||||
install_element(VIEW_NODE,
|
||||
&show_ip_ospf_instance_reachable_routers_cmd);
|
||||
|
||||
/* "show ip ospf vrfs" commands. */
|
||||
install_element(VIEW_NODE, &show_ip_ospf_vrfs_cmd);
|
||||
|
@ -234,6 +234,9 @@ struct ospf {
|
||||
struct route_table *old_table; /* Old routing table. */
|
||||
struct route_table *new_table; /* Current routing table. */
|
||||
|
||||
struct route_table *oall_rtrs; /* Old router RT. */
|
||||
struct route_table *all_rtrs; /* New routers RT. */
|
||||
|
||||
struct route_table *old_rtrs; /* Old ABR/ASBR RT. */
|
||||
struct route_table *new_rtrs; /* New ABR/ASBR RT. */
|
||||
|
||||
|
@ -466,6 +466,9 @@ install -d -m750 %{buildroot}%{rundir}
|
||||
# avoid `ERROR: ambiguous python shebang in` errors
|
||||
pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}/usr/lib/frr/*.py
|
||||
%py_byte_compile %{__python3} %{buildroot}/usr/lib/frr/*.py
|
||||
%else
|
||||
# remove ospfclient.py (if present) as it requires > python36
|
||||
rm -f %{buildroot}%{_sbindir}/ospfclient.py
|
||||
%endif
|
||||
|
||||
%pre
|
||||
@ -719,11 +722,13 @@ fi
|
||||
%files contrib
|
||||
%doc tools
|
||||
|
||||
|
||||
%files pythontools
|
||||
%{_sbindir}/generate_support_bundle.py
|
||||
%{_sbindir}/frr-reload.py
|
||||
%{_sbindir}/frr_babeltrace.py
|
||||
%if %{with_ospfclient} && (0%{?rhel} > 7 || 0%{?fedora} > 29)
|
||||
%{_sbindir}/ospfclient.py
|
||||
%endif
|
||||
%if 0%{?rhel} > 7 || 0%{?fedora} > 29
|
||||
%{_sbindir}/__pycache__/*
|
||||
%else
|
||||
@ -774,6 +779,10 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
|
||||
|
||||
|
||||
%changelog
|
||||
* Sun May 29 2022 Christian Hopps <chopps@labn.net> - %{version}
|
||||
- ospfclient:
|
||||
- Add OSPF API python client ospfclient.py
|
||||
|
||||
* Tue Mar 1 2022 Martin Winter <mwinter@opensourcerouting.org> - %{version}
|
||||
|
||||
* Tue Mar 1 2022 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.2
|
||||
|
@ -52,6 +52,7 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
|
||||
enum protection_type protection_type, bool verbose)
|
||||
{
|
||||
struct route_table *new_table, *new_rtrs;
|
||||
struct route_table *all_rtrs = NULL;
|
||||
struct ospf_area *area;
|
||||
struct p_space *p_space;
|
||||
struct q_space *q_space;
|
||||
@ -63,10 +64,11 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
|
||||
|
||||
new_table = route_table_init();
|
||||
new_rtrs = route_table_init();
|
||||
all_rtrs = route_table_init();
|
||||
|
||||
/* dryrun true, root_node false */
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
|
||||
true, false);
|
||||
ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
|
||||
new_rtrs, true, false);
|
||||
|
||||
if (verbose) {
|
||||
vty_out(vty, "SPF Tree without TI-LFA backup paths:\n\n");
|
||||
|
137
tests/topotests/ospfapi/ctester.py
Executable file
137
tests/topotests/ospfapi/ctester.py
Executable file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
|
||||
#
|
||||
# January 17 2022, Christian Hopps <chopps@labn.net>
|
||||
#
|
||||
# Copyright 2022, LabN Consulting, L.L.C.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
|
||||
if not os.path.exists(CLIENTDIR):
|
||||
CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
|
||||
assert os.path.exists(
|
||||
os.path.join(CLIENTDIR, "ospfclient.py")
|
||||
), "can't locate ospfclient.py"
|
||||
|
||||
sys.path[0:0] = [CLIENTDIR]
|
||||
|
||||
import ospfclient as api # pylint: disable=E0401 # noqa: E402
|
||||
|
||||
|
||||
async def do_wait(c, args):
|
||||
cv = asyncio.Condition()
|
||||
|
||||
async def cb(added, removed):
|
||||
logging.debug("callback: added: %s removed: %s", added, removed)
|
||||
sys.stdout.flush()
|
||||
async with cv:
|
||||
cv.notify_all()
|
||||
|
||||
logging.debug("API using callback")
|
||||
await c.monitor_reachable(callback=cb)
|
||||
|
||||
for w in args.wait:
|
||||
check = ",".join(sorted(list(w.split(","))))
|
||||
logging.info("Waiting for %s", check)
|
||||
|
||||
while True:
|
||||
async with cv:
|
||||
got = ",".join(sorted([str(x) for x in c.reachable_routers]))
|
||||
if check == got:
|
||||
break
|
||||
logging.debug("expected '%s' != '%s'\nwaiting on notify", check, got)
|
||||
await cv.wait()
|
||||
|
||||
logging.info("SUCCESS: %s", check)
|
||||
print("SUCCESS: {}".format(check))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
async def async_main(args):
|
||||
c = api.OspfOpaqueClient(args.server)
|
||||
await c.connect()
|
||||
if sys.version_info[1] > 6:
|
||||
asyncio.create_task(c._handle_msg_loop()) # pylint: disable=W0212
|
||||
else:
|
||||
asyncio.get_event_loop().create_task(
|
||||
c._handle_msg_loop() # pylint: disable=W0212
|
||||
)
|
||||
|
||||
if args.wait:
|
||||
await do_wait(c, args)
|
||||
return 0
|
||||
|
||||
|
||||
def main(*args):
|
||||
ap = argparse.ArgumentParser(args)
|
||||
ap.add_argument("--server", default="localhost", help="OSPF API server")
|
||||
ap.add_argument(
|
||||
"--wait", action="append", help="wait for comma-sep set of reachable routers"
|
||||
)
|
||||
ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
|
||||
args = ap.parse_args()
|
||||
|
||||
level = logging.DEBUG if args.verbose else logging.INFO
|
||||
logging.basicConfig(
|
||||
level=level, format="%(asctime)s %(levelname)s: TESTER: %(name)s: %(message)s"
|
||||
)
|
||||
|
||||
# We need to flush this output to stdout right away
|
||||
h = logging.StreamHandler(sys.stdout)
|
||||
h.flush = sys.stdout.flush
|
||||
f = logging.Formatter("%(asctime)s %(name)s: %(levelname)s: %(message)s")
|
||||
h.setFormatter(f)
|
||||
logger = logging.getLogger("ospfclient")
|
||||
logger.addHandler(h)
|
||||
logger.propagate = False
|
||||
|
||||
logging.info("ctester: starting")
|
||||
sys.stdout.flush()
|
||||
|
||||
status = 3
|
||||
try:
|
||||
if sys.version_info[1] > 6:
|
||||
status = asyncio.run(async_main(args))
|
||||
else:
|
||||
loop = asyncio.get_event_loop()
|
||||
try:
|
||||
status = loop.run_until_complete(async_main(args))
|
||||
finally:
|
||||
loop.close()
|
||||
except KeyboardInterrupt:
|
||||
logging.info("Exiting, received KeyboardInterrupt in main")
|
||||
except Exception as error:
|
||||
logging.info("Exiting, unexpected exception %s", error, exc_info=True)
|
||||
else:
|
||||
logging.info("api: clean exit")
|
||||
|
||||
return status
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit_status = main()
|
||||
sys.exit(exit_status)
|
1
tests/topotests/ospfapi/lib
Symbolic link
1
tests/topotests/ospfapi/lib
Symbolic link
@ -0,0 +1 @@
|
||||
../lib
|
10
tests/topotests/ospfapi/r1/ospfd.conf
Normal file
10
tests/topotests/ospfapi/r1/ospfd.conf
Normal file
@ -0,0 +1,10 @@
|
||||
!
|
||||
interface r1-eth0
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 192.168.0.1
|
||||
capability opaque
|
||||
!
|
4
tests/topotests/ospfapi/r1/zebra.conf
Normal file
4
tests/topotests/ospfapi/r1/zebra.conf
Normal file
@ -0,0 +1,4 @@
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 10.0.1.1/24
|
||||
!
|
15
tests/topotests/ospfapi/r2/ospfd.conf
Normal file
15
tests/topotests/ospfapi/r2/ospfd.conf
Normal file
@ -0,0 +1,15 @@
|
||||
!
|
||||
interface r2-eth0
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
interface r2-eth1
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 192.168.0.2
|
||||
capability opaque
|
||||
!
|
7
tests/topotests/ospfapi/r2/zebra.conf
Normal file
7
tests/topotests/ospfapi/r2/zebra.conf
Normal file
@ -0,0 +1,7 @@
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 10.0.1.2/24
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 10.0.2.2/24
|
||||
!
|
15
tests/topotests/ospfapi/r3/ospfd.conf
Normal file
15
tests/topotests/ospfapi/r3/ospfd.conf
Normal file
@ -0,0 +1,15 @@
|
||||
!
|
||||
interface r3-eth0
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
interface r3-eth1
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 192.168.0.3
|
||||
capability opaque
|
||||
!
|
7
tests/topotests/ospfapi/r3/zebra.conf
Normal file
7
tests/topotests/ospfapi/r3/zebra.conf
Normal file
@ -0,0 +1,7 @@
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 10.0.2.3/24
|
||||
!
|
||||
interface r3-eth1
|
||||
ip address 10.0.3.3/24
|
||||
!
|
10
tests/topotests/ospfapi/r4/ospfd.conf
Normal file
10
tests/topotests/ospfapi/r4/ospfd.conf
Normal file
@ -0,0 +1,10 @@
|
||||
!
|
||||
interface r4-eth0
|
||||
ip ospf hello-interval 2
|
||||
ip ospf dead-interval 10
|
||||
ip ospf area 1.2.3.4
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 192.168.0.4
|
||||
capability opaque
|
||||
!
|
4
tests/topotests/ospfapi/r4/zebra.conf
Normal file
4
tests/topotests/ospfapi/r4/zebra.conf
Normal file
@ -0,0 +1,4 @@
|
||||
!
|
||||
interface r4-eth0
|
||||
ip address 10.0.3.4/24
|
||||
!
|
470
tests/topotests/ospfapi/test_ospf_clientapi.py
Normal file
470
tests/topotests/ospfapi/test_ospf_clientapi.py
Normal file
@ -0,0 +1,470 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
|
||||
#
|
||||
# Copyright (c) 2021, LabN Consulting, L.L.C.
|
||||
#
|
||||
# This program 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
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program; see the file COPYING; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
"""
|
||||
test_ospf_clientapi.py: Test the OSPF client API.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from lib.common_config import retry, run_frr_cmd, step
|
||||
from lib.micronet import comm_error
|
||||
from lib.topogen import Topogen, TopoRouter
|
||||
from lib.topotest import interface_set_status, json_cmp
|
||||
|
||||
pytestmark = [pytest.mark.ospfd]
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
TESTDIR = os.path.abspath(CWD)
|
||||
|
||||
CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
|
||||
if not os.path.exists(CLIENTDIR):
|
||||
CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
|
||||
|
||||
assert os.path.exists(
|
||||
os.path.join(CLIENTDIR, "ospfclient.py")
|
||||
), "can't locate ospfclient.py"
|
||||
|
||||
|
||||
# ----------
|
||||
# Test Setup
|
||||
# ----------
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", name="tgen")
|
||||
def _tgen(request):
|
||||
"Setup/Teardown the environment and provide tgen argument to tests"
|
||||
nrouters = request.param
|
||||
topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
|
||||
|
||||
tgen = Topogen(topodef, request.module.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
router_list = tgen.routers()
|
||||
for _, router in router_list.items():
|
||||
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
|
||||
router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
|
||||
router.net.daemons_options["ospfd"] = "--apiserver"
|
||||
|
||||
tgen.start_router()
|
||||
|
||||
yield tgen
|
||||
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
# Fixture that executes before each test
|
||||
@pytest.fixture(autouse=True)
|
||||
def skip_on_failure(tgen):
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip("skipped because of previous test failure")
|
||||
|
||||
|
||||
# ------------
|
||||
# Test Utility
|
||||
# ------------
|
||||
|
||||
|
||||
@retry(retry_timeout=45)
|
||||
def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
|
||||
del tgen
|
||||
show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
|
||||
if not bool(show_ospf_json):
|
||||
return "ospf is not running"
|
||||
result = json_cmp(show_ospf_json, input_dict)
|
||||
return str(result) if result else None
|
||||
|
||||
|
||||
def myreadline(f):
|
||||
buf = b""
|
||||
while True:
|
||||
# logging.info("READING 1 CHAR")
|
||||
c = f.read(1)
|
||||
if not c:
|
||||
return buf if buf else None
|
||||
buf += c
|
||||
# logging.info("READ CHAR: '%s'", c)
|
||||
if c == b"\n":
|
||||
return buf
|
||||
|
||||
|
||||
def _wait_output(p, regex, timeout=120):
|
||||
retry_until = datetime.now() + timedelta(seconds=timeout)
|
||||
while datetime.now() < retry_until:
|
||||
# line = p.stdout.readline()
|
||||
line = myreadline(p.stdout)
|
||||
if not line:
|
||||
assert None, "Timeout waiting for '{}'".format(regex)
|
||||
line = line.decode("utf-8")
|
||||
line = line.rstrip()
|
||||
if line:
|
||||
logging.debug("GOT LINE: '%s'", line)
|
||||
m = re.search(regex, line)
|
||||
if m:
|
||||
return m
|
||||
assert None, "Failed to get output withint {}s".format(timeout)
|
||||
|
||||
|
||||
# -----
|
||||
# Tests
|
||||
# -----
|
||||
|
||||
|
||||
def _test_reachability(tgen, testbin):
|
||||
waitlist = [
|
||||
"192.168.0.1,192.168.0.2,192.168.0.4",
|
||||
"192.168.0.2,192.168.0.4",
|
||||
"192.168.0.1,192.168.0.2,192.168.0.4",
|
||||
]
|
||||
r2 = tgen.gears["r2"]
|
||||
r3 = tgen.gears["r3"]
|
||||
|
||||
wait_args = [f"--wait={x}" for x in waitlist]
|
||||
|
||||
p = None
|
||||
try:
|
||||
step("reachable: check for initial reachability")
|
||||
p = r3.popen(
|
||||
["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
|
||||
encoding=None, # don't buffer
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
_wait_output(p, "SUCCESS: {}".format(waitlist[0]))
|
||||
|
||||
step("reachable: check for modified reachability")
|
||||
interface_set_status(r2, "r2-eth0", False)
|
||||
_wait_output(p, "SUCCESS: {}".format(waitlist[1]))
|
||||
|
||||
step("reachable: check for restored reachability")
|
||||
interface_set_status(r2, "r2-eth0", True)
|
||||
_wait_output(p, "SUCCESS: {}".format(waitlist[2]))
|
||||
except Exception as error:
|
||||
logging.error("ERROR: %s", error)
|
||||
raise
|
||||
finally:
|
||||
if p:
|
||||
p.terminate()
|
||||
p.wait()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tgen", [4], indirect=True)
|
||||
def test_ospf_reachability(tgen):
|
||||
testbin = os.path.join(TESTDIR, "ctester.py")
|
||||
rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
|
||||
logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
|
||||
_test_reachability(tgen, testbin)
|
||||
|
||||
|
||||
def _test_add_data(tgen, apibin):
|
||||
"Test adding opaque data to domain"
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
step("add opaque: add opaque link local")
|
||||
|
||||
p = None
|
||||
try:
|
||||
p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
|
||||
input_dict = {
|
||||
"routerId": "192.168.0.1",
|
||||
"areas": {
|
||||
"1.2.3.4": {
|
||||
"linkLocalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "230.0.0.2",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
# Wait for it to show up
|
||||
assert verify_ospf_database(tgen, r1, input_dict) is None
|
||||
|
||||
input_dict = {
|
||||
"linkLocalOpaqueLsa": {
|
||||
"areas": {
|
||||
"1.2.3.4": [
|
||||
{
|
||||
"linkStateId": "230.0.0.2",
|
||||
"advertisingRouter": "192.168.0.1",
|
||||
"lsaSeqNumber": "80000001",
|
||||
"opaqueData": "00000202",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
# verify content
|
||||
json_cmd = "show ip ospf da opaque-link json"
|
||||
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
|
||||
|
||||
step("reset client, add opaque area, verify link local flushing")
|
||||
|
||||
p.send_signal(signal.SIGINT)
|
||||
time.sleep(2)
|
||||
p.wait()
|
||||
p = None
|
||||
p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
|
||||
input_dict = {
|
||||
"routerId": "192.168.0.1",
|
||||
"areas": {
|
||||
"1.2.3.4": {
|
||||
"linkLocalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "230.0.0.2",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
"lsaAge": 3600,
|
||||
}
|
||||
],
|
||||
"areaLocalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "231.0.0.1",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
# Wait for it to show up
|
||||
assert verify_ospf_database(tgen, r1, input_dict) is None
|
||||
|
||||
input_dict = {
|
||||
"areaLocalOpaqueLsa": {
|
||||
"areas": {
|
||||
"1.2.3.4": [
|
||||
{
|
||||
"linkStateId": "231.0.0.1",
|
||||
"advertisingRouter": "192.168.0.1",
|
||||
"lsaSeqNumber": "80000001",
|
||||
"opaqueData": "00010101",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
# verify content
|
||||
json_cmd = "show ip ospf da opaque-area json"
|
||||
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
|
||||
|
||||
step("reset client, add opaque AS, verify area flushing")
|
||||
|
||||
p.send_signal(signal.SIGINT)
|
||||
time.sleep(2)
|
||||
p.wait()
|
||||
p = None
|
||||
|
||||
p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
|
||||
input_dict = {
|
||||
"routerId": "192.168.0.1",
|
||||
"areas": {
|
||||
"1.2.3.4": {
|
||||
"areaLocalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "231.0.0.1",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
"lsaAge": 3600,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
"asExternalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "232.0.0.3",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
},
|
||||
],
|
||||
}
|
||||
# Wait for it to show up
|
||||
assert verify_ospf_database(tgen, r1, input_dict) is None
|
||||
|
||||
input_dict = {
|
||||
"asExternalOpaqueLsa": [
|
||||
{
|
||||
"linkStateId": "232.0.0.3",
|
||||
"advertisingRouter": "192.168.0.1",
|
||||
"lsaSeqNumber": "80000001",
|
||||
"opaqueData": "deadbeaf01234567",
|
||||
},
|
||||
]
|
||||
}
|
||||
# verify content
|
||||
json_cmd = "show ip ospf da opaque-as json"
|
||||
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
|
||||
|
||||
step("stop client, verify AS flushing")
|
||||
|
||||
p.send_signal(signal.SIGINT)
|
||||
time.sleep(2)
|
||||
p.wait()
|
||||
p = None
|
||||
|
||||
input_dict = {
|
||||
"routerId": "192.168.0.1",
|
||||
"asExternalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "232.0.0.3",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000001",
|
||||
"lsaAge": 3600,
|
||||
},
|
||||
],
|
||||
}
|
||||
# Wait for it to be flushed
|
||||
assert verify_ospf_database(tgen, r1, input_dict) is None
|
||||
|
||||
step("start client adding opaque domain, verify new sequence number and data")
|
||||
|
||||
# Originate it again
|
||||
p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
|
||||
input_dict = {
|
||||
"routerId": "192.168.0.1",
|
||||
"asExternalOpaqueLsa": [
|
||||
{
|
||||
"lsId": "232.0.0.3",
|
||||
"advertisedRouter": "192.168.0.1",
|
||||
"sequenceNumber": "80000002",
|
||||
},
|
||||
],
|
||||
}
|
||||
assert verify_ospf_database(tgen, r1, input_dict) is None
|
||||
|
||||
input_dict = {
|
||||
"asExternalOpaqueLsa": [
|
||||
{
|
||||
"linkStateId": "232.0.0.3",
|
||||
"advertisingRouter": "192.168.0.1",
|
||||
"lsaSeqNumber": "80000002",
|
||||
"opaqueData": "ebadf00d",
|
||||
},
|
||||
]
|
||||
}
|
||||
# verify content
|
||||
json_cmd = "show ip ospf da opaque-as json"
|
||||
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
|
||||
|
||||
p.send_signal(signal.SIGINT)
|
||||
time.sleep(2)
|
||||
p.wait()
|
||||
p = None
|
||||
|
||||
except Exception:
|
||||
if p:
|
||||
p.terminate()
|
||||
if p.wait():
|
||||
comm_error(p)
|
||||
p = None
|
||||
raise
|
||||
finally:
|
||||
if p:
|
||||
p.terminate()
|
||||
p.wait()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tgen", [2], indirect=True)
|
||||
def test_ospf_opaque_add_data3(tgen):
|
||||
apibin = os.path.join(CLIENTDIR, "ospfclient.py")
|
||||
rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
|
||||
logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
|
||||
_test_add_data(tgen, apibin)
|
||||
|
||||
|
||||
def _test_opaque_add_del(tgen, apibin):
|
||||
"Test adding opaque data to domain"
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
r2 = tgen.gears["r2"]
|
||||
|
||||
p = None
|
||||
pread = None
|
||||
try:
|
||||
step("reachable: check for add notification")
|
||||
pread = r2.popen(
|
||||
["/usr/bin/timeout", "120", apibin, "-v"],
|
||||
encoding=None, # don't buffer
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
|
||||
|
||||
# Wait for add notification
|
||||
# RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
|
||||
|
||||
ls_id = "232.0.0.3"
|
||||
waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
|
||||
_ = _wait_output(pread, waitfor)
|
||||
|
||||
p.terminate()
|
||||
if p.wait():
|
||||
comm_error(p)
|
||||
|
||||
# step("reachable: check for flush/age out")
|
||||
# # Wait for max age notification
|
||||
# waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
|
||||
# _wait_output(pread, waitfor)
|
||||
|
||||
step("reachable: check for delete")
|
||||
# Wait for delete notification
|
||||
waitfor = "RECV:.*delete msg.*LSA {}.*".format(ls_id)
|
||||
_wait_output(pread, waitfor)
|
||||
except Exception:
|
||||
if p:
|
||||
p.terminate()
|
||||
if p.wait():
|
||||
comm_error(p)
|
||||
p = None
|
||||
raise
|
||||
finally:
|
||||
if pread:
|
||||
pread.terminate()
|
||||
pread.wait()
|
||||
if p:
|
||||
p.terminate()
|
||||
p.wait()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tgen", [2], indirect=True)
|
||||
def test_ospf_opaque_delete_data3(tgen):
|
||||
apibin = os.path.join(CLIENTDIR, "ospfclient.py")
|
||||
rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
|
||||
logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
|
||||
_test_opaque_add_del(tgen, apibin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user