]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #12172 from LabNConsulting/working/lb/api-zero-len-del-flag
authorRuss White <russ@riw.us>
Sat, 29 Oct 2022 14:38:27 +0000 (10:38 -0400)
committerGitHub <noreply@github.com>
Sat, 29 Oct 2022 14:38:27 +0000 (10:38 -0400)
OSPF opaques LSA API - fix bug with LL API delete, add option for zero data length withdrawals

ospfclient/ospf_apiclient.c
ospfclient/ospf_apiclient.h
ospfclient/ospfclient.c
ospfclient/ospfclient.py
ospfd/ospf_api.c
ospfd/ospf_api.h
ospfd/ospf_apiserver.c
ospfd/ospf_lsa.c
ospfd/ospf_lsa.h
ospfd/ospf_opaque.c
tests/topotests/ospfapi/test_ospf_clientapi.py

index b5e6389d4c9b0cee3690537ed72bafe56753a36e..05c5e7789d3ab3d5793fb562ab1283c3270ed2c0 100644 (file)
@@ -481,8 +481,9 @@ int ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient,
 }
 
 int ospf_apiclient_lsa_delete(struct ospf_apiclient *oclient,
-                             struct in_addr area_id, uint8_t lsa_type,
-                             uint8_t opaque_type, uint32_t opaque_id)
+                             struct in_addr addr, uint8_t lsa_type,
+                             uint8_t opaque_type, uint32_t opaque_id,
+                             uint8_t flags)
 {
        struct msg *msg;
        int rc;
@@ -496,8 +497,8 @@ int ospf_apiclient_lsa_delete(struct ospf_apiclient *oclient,
 
        /* opaque_id is in host byte order and will be converted
         * to network byte order by new_msg_delete_request */
-       msg = new_msg_delete_request(ospf_apiclient_get_seqnr(), area_id,
-                                    lsa_type, opaque_type, opaque_id);
+       msg = new_msg_delete_request(ospf_apiclient_get_seqnr(), addr, lsa_type,
+                                    opaque_type, opaque_id, flags);
 
        rc = ospf_apiclient_send_request(oclient, msg);
        return rc;
index 6d1eb7f64f45ca3cb4ad87c51570da689ff8bce5..b904937c2ab0da2e0c521b4dc0bdae09c69dae65 100644 (file)
@@ -94,8 +94,9 @@ int ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient,
 /* Synchronous request to delete opaque LSA. Parameter opaque_id is in
    host byte order */
 int ospf_apiclient_lsa_delete(struct ospf_apiclient *oclient,
-                             struct in_addr area_id, uint8_t lsa_type,
-                             uint8_t opaque_type, uint32_t opaque_id);
+                             struct in_addr addr, uint8_t lsa_type,
+                             uint8_t opaque_type, uint32_t opaque_id,
+                             uint8_t flags);
 
 /* Fetch async message and handle it  */
 int ospf_apiclient_handle_async(struct ospf_apiclient *oclient);
index 3cfee7d573a98182d210603ea9c8a5b6d56cb64c..edf814184ac2b9ca581faccb8f2259549de2b865 100644 (file)
@@ -98,9 +98,10 @@ static void lsa_delete(struct thread *t)
 
        printf("Deleting LSA... ");
        rc = ospf_apiclient_lsa_delete(oclient, area_id,
-                                      atoi(args[2]),  /* lsa type */
-                                      atoi(args[3]),  /* opaque type */
-                                      atoi(args[4])); /* opaque ID */
+                                      atoi(args[2]), /* lsa type */
+                                      atoi(args[3]), /* opaque type */
+                                      atoi(args[4]), /* opaque ID */
+                                      0); /* send data in withdrawals */
        printf("done, return code is = %d\n", rc);
 }
 
index 19561145a09484fe45afdcb1dfc0fbb802397306..8e3c68445f018c13db619ca875e8d4e7bdd0df72 100755 (executable)
@@ -62,13 +62,16 @@ smsg_info = {
     MSG_REGISTER_EVENT: ("REGISTER_EVENT", FMT_LSA_FILTER),
     MSG_SYNC_LSDB: ("SYNC_LSDB", FMT_LSA_FILTER),
     MSG_ORIGINATE_REQUEST: ("ORIGINATE_REQUEST", ">II" + FMT_LSA_HEADER[1:]),
-    MSG_DELETE_REQUEST: ("DELETE_REQUEST", ">IBBxxL"),
+    MSG_DELETE_REQUEST: ("DELETE_REQUEST", ">IBBxBL"),
     MSG_SYNC_REACHABLE: ("MSG_SYNC_REACHABLE", ""),
     MSG_SYNC_ISM: ("MSG_SYNC_ISM", ""),
     MSG_SYNC_NSM: ("MSG_SYNC_NSM", ""),
     MSG_SYNC_ROUTER_ID: ("MSG_SYNC_ROUTER_ID", ""),
 }
 
+# OSPF API MSG Delete Flag.
+OSPF_API_DEL_ZERO_LEN_LSA = 0x01  # send withdrawal with no LSA data
+
 # --------------------------
 # Messages from OSPF daemon.
 # --------------------------
@@ -842,7 +845,7 @@ class OspfOpaqueClient(OspfApiClient):
         await self._assure_opaque_ready(lsa_type, otype)
         await self.msg_send_raises(mt, msg)
 
-    async def delete_opaque_data(self, addr, lsa_type, otype, oid):
+    async def delete_opaque_data(self, addr, lsa_type, otype, oid, flags=0):
         """Delete an instance of opaque data.
 
         Delete an instance of opaque data. This call will register for the given
@@ -854,6 +857,7 @@ class OspfOpaqueClient(OspfApiClient):
             otype: (octet) opaque type. Note: the type will be registered if the user
                 has not explicity done that yet with `register_opaque_data`.
             oid: (3 octets) ID of this opaque data
+            flags: (octet) optional flags (e.g., OSPF_API_DEL_ZERO_LEN_LSA, defaults to no flags)
         Raises:
             See `msg_send_raises`
         """
@@ -862,7 +866,7 @@ class OspfOpaqueClient(OspfApiClient):
 
         mt = MSG_DELETE_REQUEST
         await self._assure_opaque_ready(lsa_type, otype)
-        mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, oid)
+        mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, flags, oid)
         await self.msg_send_raises(mt, mp)
 
     async def register_opaque_data(self, lsa_type, otype, callback=None):
@@ -1115,23 +1119,28 @@ async def async_main(args):
                     except ValueError:
                         addr = ip(aval)
                 oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))]
-                assert len(_s) <= 1, "Bad format for action argument"
-                try:
-                    b = bytes.fromhex(_s.pop(False))
-                except IndexError:
-                    b = b""
-                logging.info("opaque data is %s octets", len(b))
-                # Needs to be multiple of 4 in length
-                mod = len(b) % 4
-                if mod:
-                    b += b"\x00" * (4 - mod)
-                    logging.info("opaque padding to %s octets", len(b))
-
                 if what.casefold() == "add":
+                    try:
+                        b = bytes.fromhex(_s.pop(False))
+                    except IndexError:
+                        b = b""
+                    logging.info("opaque data is %s octets", len(b))
+                    # Needs to be multiple of 4 in length
+                    mod = len(b) % 4
+                    if mod:
+                        b += b"\x00" * (4 - mod)
+                        logging.info("opaque padding to %s octets", len(b))
+
                     await c.add_opaque_data(*oargs, b)
                 else:
                     assert what.casefold().startswith("del")
-                    await c.delete_opaque_data(*oargs)
+                    f = 0
+                    if len(_s) >= 1:
+                        try:
+                            f = int(_s.pop(False))
+                        except IndexError:
+                            f = 0
+                    await c.delete_opaque_data(*oargs, f)
             if args.exit:
                 return 0
     except Exception as error:
@@ -1153,7 +1162,9 @@ def main(*args):
     ap.add_argument("--server", default="localhost", help="OSPF API server")
     ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
     ap.add_argument(
-        "actions", nargs="*", help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA]"
+        "actions",
+        nargs="*",
+        help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA|DEL_FLAG]",
     )
     args = ap.parse_args()
 
index 8636db450b402fc1e8a35d9e7f5886e085cd425a..9a134d033c339ddbead85c6c6f950a14a86fce81 100644 (file)
@@ -532,16 +532,17 @@ struct msg *new_msg_originate_request(uint32_t seqnum, struct in_addr ifaddr,
        return msg_new(MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen);
 }
 
-struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr area_id,
+struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr addr,
                                   uint8_t lsa_type, uint8_t opaque_type,
-                                  uint32_t opaque_id)
+                                  uint32_t opaque_id, uint8_t flags)
 {
        struct msg_delete_request dmsg;
-       dmsg.area_id = area_id;
+       dmsg.addr = addr;
        dmsg.lsa_type = lsa_type;
        dmsg.opaque_type = opaque_type;
        dmsg.opaque_id = htonl(opaque_id);
        memset(&dmsg.pad, 0, sizeof(dmsg.pad));
+       dmsg.flags = flags;
 
        return msg_new(MSG_DELETE_REQUEST, &dmsg, seqnum,
                       sizeof(struct msg_delete_request));
index 51c8c52ce542c57883d7336cb300af4dac6c9b78..6f569e962d93ee3c1f6e71265edfd5e9003fb62c 100644 (file)
@@ -185,11 +185,19 @@ struct msg_originate_request {
        struct lsa_header data;
 };
 
+
+/* OSPF API MSG Delete Flag. */
+#define OSPF_API_DEL_ZERO_LEN_LSA 0x01 /* send withdrawal with no LSA data */
+
+#define IS_DEL_ZERO_LEN_LSA(x) ((x)->flags & OSPF_API_DEL_ZERO_LEN_LSA)
+
 struct msg_delete_request {
-       struct in_addr area_id; /* "0.0.0.0" for AS-external opaque LSAs */
+       struct in_addr addr; /* intf IP for link local, area for type 10,
+                               "0.0.0.0" for AS-external */
        uint8_t lsa_type;
        uint8_t opaque_type;
-       uint8_t pad[2]; /* padding */
+       uint8_t pad;   /* padding */
+       uint8_t flags; /* delete flags */
        uint32_t opaque_id;
 };
 
@@ -311,10 +319,9 @@ extern struct msg *new_msg_originate_request(uint32_t seqnum,
                                             struct in_addr ifaddr,
                                             struct in_addr area_id,
                                             struct lsa_header *data);
-extern struct msg *new_msg_delete_request(uint32_t seqnum,
-                                         struct in_addr area_id,
+extern struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr addr,
                                          uint8_t lsa_type, uint8_t opaque_type,
-                                         uint32_t opaque_id);
+                                         uint32_t opaque_id, uint8_t flags);
 
 /* Messages sent by OSPF daemon */
 extern struct msg *new_msg_reply(uint32_t seqnum, uint8_t rc);
index f5ed77dab59099ae18d51a920d495c0374375535..0c2ee0c4f8a934f8303dfef7211c3c9aef672207 100644 (file)
@@ -1924,6 +1924,7 @@ int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
        struct msg_delete_request *dmsg;
        struct ospf_lsa *old;
        struct ospf_area *area = NULL;
+       struct ospf_interface *oi = NULL;
        struct in_addr id;
        int lsa_type, opaque_type;
        int rc = 0;
@@ -1938,11 +1939,20 @@ int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
        /* Lookup area for link-local and area-local opaque LSAs */
        switch (dmsg->lsa_type) {
        case OSPF_OPAQUE_LINK_LSA:
+               oi = ospf_apiserver_if_lookup_by_addr(dmsg->addr);
+               if (!oi) {
+                       zlog_warn("%s: unknown interface %pI4", __func__,
+                                 &dmsg->addr);
+                       rc = OSPF_API_NOSUCHINTERFACE;
+                       goto out;
+               }
+               area = oi->area;
+               break;
        case OSPF_OPAQUE_AREA_LSA:
-               area = ospf_area_lookup_by_area_id(ospf, dmsg->area_id);
+               area = ospf_area_lookup_by_area_id(ospf, dmsg->addr);
                if (!area) {
                        zlog_warn("%s: unknown area %pI4", __func__,
-                                 &dmsg->area_id);
+                                 &dmsg->addr);
                        rc = OSPF_API_NOSUCHAREA;
                        goto out;
                }
@@ -1987,6 +1997,11 @@ int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
                goto out;
        }
 
+       if (IS_DEL_ZERO_LEN_LSA(dmsg)) {
+               /* minimize the size of the withdrawal: */
+               old->opaque_zero_len_delete = 1;
+       }
+
        /* Schedule flushing of LSA from LSDB */
        /* NB: Multiple scheduling will produce a warning message, but harmless.
         */
index c67181cba607c657a7bc4f1097dd9d9e5173825c..da5e15ae5f6328ae710d9c5b15710487520da6e0 100644 (file)
@@ -190,6 +190,7 @@ struct ospf_lsa *ospf_lsa_new(void)
        new->refresh_list = -1;
        new->vrf_id = VRF_DEFAULT;
        new->to_be_acknowledged = 0;
+       new->opaque_zero_len_delete = 0;
 
        return new;
 }
index a2a2393c90d53beaf541e8c4752c682c18720688..4e884fa89bda07e960d8b41132a846b9fee611c1 100644 (file)
@@ -124,6 +124,9 @@ struct ospf_lsa {
 
        /*For topo chg detection in HELPER role*/
        bool to_be_acknowledged;
+
+       /* send maxage with no data */
+       bool opaque_zero_len_delete;
 };
 
 /* OSPF LSA Link Type. */
index 261d69df338d5bd24a3853f6ef72a746eaa02149..ab647625fe93e0fdc38ffe23868618d22c5a57bb 100644 (file)
@@ -2050,6 +2050,17 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0)
                goto out;
        }
 
+       if (lsa->opaque_zero_len_delete &&
+           lsa->data->length != htons(sizeof(struct lsa_header))) {
+               /* minimize the size of the withdrawal: */
+               /*     increment the sequence number and make len just header */
+               /*     and update checksum */
+               lsa->data->ls_seqnum = lsa_seqnum_increment(lsa);
+               lsa->data->length = htons(sizeof(struct lsa_header));
+               lsa->data->checksum = 0;
+               lsa->data->checksum = ospf_lsa_checksum(lsa->data);
+       }
+
        /* Delete this lsa from neighbor retransmit-list. */
        switch (lsa->data->type) {
        case OSPF_OPAQUE_LINK_LSA:
index f6928d7cd017855b5faebf48041aa53348d12916..94c8c5c732faa6f2b7bb9da57e31d70e4dc30b77 100644 (file)
@@ -472,52 +472,70 @@ def _test_opaque_add_del(tgen, apibin):
             [
                 apibin,
                 "-v",
+                "add,9,10.0.1.1,230,1",
                 "add,9,10.0.1.1,230,2,00000202",
-                "add,10,1.2.3.4,231,1,00010101",
                 "wait,1",
-                "add,10,1.2.3.4,231,2",
-                "add,11,232,3,ebadf00d",
+                "add,10,1.2.3.4,231,1",
+                "add,10,1.2.3.4,231,2,0102030405060708",
+                "wait,1",
+                "add,11,232,1",
+                "add,11,232,2,ebadf00d",
                 "wait,20",
-                "del,10,1.2.3.4,231,1",
-                "del,10,1.2.3.4,231,2",
+                "del,9,10.0.1.1,230,2,0",
+                "del,10,1.2.3.4,231,2,1",
+                "del,11,232,1,1",
             ]
         )
-
         add_input_dict = {
             "areas": {
                 "1.2.3.4": {
                     "linkLocalOpaqueLsa": [
+                        {
+                            "lsId": "230.0.0.1",
+                            "advertisedRouter": "192.168.0.1",
+                            "sequenceNumber": "80000001",
+                            "checksum": "6d5f",
+                        },
                         {
                             "lsId": "230.0.0.2",
                             "advertisedRouter": "192.168.0.1",
                             "sequenceNumber": "80000001",
                             "checksum": "8142",
-                        }
+                        },
                     ],
+                    "linkLocalOpaqueLsaCount": 2,
                     "areaLocalOpaqueLsa": [
                         {
                             "lsId": "231.0.0.1",
                             "advertisedRouter": "192.168.0.1",
                             "sequenceNumber": "80000001",
-                            "checksum": "695a",
+                            "checksum": "5278",
                         },
                         {
                             "lsId": "231.0.0.2",
                             "advertisedRouter": "192.168.0.1",
                             "sequenceNumber": "80000001",
-                            "checksum": "4881",
+                            "checksum": "6d30",
                         },
                     ],
-                }
+                    "areaLocalOpaqueLsaCount": 2,
+                },
             },
             "asExternalOpaqueLsa": [
                 {
-                    "lsId": "232.0.0.3",
+                    "lsId": "232.0.0.1",
                     "advertisedRouter": "192.168.0.1",
                     "sequenceNumber": "80000001",
-                    "checksum": "c666",
-                }
+                    "checksum": "5575",
+                },
+                {
+                    "lsId": "232.0.0.2",
+                    "advertisedRouter": "192.168.0.1",
+                    "sequenceNumber": "80000001",
+                    "checksum": "d05d",
+                },
             ],
+            "asExternalOpaqueLsaCount": 2,
         }
 
         step("reachable: check for add LSAs")
@@ -536,6 +554,14 @@ def _test_opaque_add_del(tgen, apibin):
                 "linkLocalOpaqueLsa": {
                     "areas": {
                         "1.2.3.4": [
+                            {
+                                "linkStateId": "230.0.0.1",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "6d5f",
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
                             {
                                 "linkStateId": "230.0.0.2",
                                 "advertisingRouter": "192.168.0.1",
@@ -544,7 +570,7 @@ def _test_opaque_add_del(tgen, apibin):
                                 "length": 24,
                                 "opaqueId": 2,
                                 "opaqueDataLength": 4,
-                            }
+                            },
                         ]
                     }
                 }
@@ -557,33 +583,41 @@ def _test_opaque_add_del(tgen, apibin):
                                 "linkStateId": "231.0.0.1",
                                 "advertisingRouter": "192.168.0.1",
                                 "lsaSeqNumber": "80000001",
-                                "checksum": "695a",
-                                "length": 24,
-                                "opaqueDataLength": 4,
+                                "checksum": "5278",
+                                "length": 20,
+                                "opaqueDataLength": 0,
                             },
                             {
                                 "linkStateId": "231.0.0.2",
                                 "advertisingRouter": "192.168.0.1",
                                 "lsaSeqNumber": "80000001",
-                                "checksum": "4881",
-                                "length": 20,
-                                "opaqueDataLength": 0,
+                                "checksum": "6d30",
+                                "length": 28,
+                                "opaqueDataLength": 8,
                             },
-                        ]
-                    }
-                }
+                        ],
+                    },
+                },
             },
             {
                 "asExternalOpaqueLsa": [
                     {
-                        "linkStateId": "232.0.0.3",
+                        "linkStateId": "232.0.0.1",
+                        "advertisingRouter": "192.168.0.1",
+                        "lsaSeqNumber": "80000001",
+                        "checksum": "5575",
+                        "length": 20,
+                        "opaqueDataLength": 0,
+                    },
+                    {
+                        "linkStateId": "232.0.0.2",
                         "advertisingRouter": "192.168.0.1",
                         "lsaSeqNumber": "80000001",
-                        "checksum": "c666",
+                        "checksum": "d05d",
                         "length": 24,
                         "opaqueDataLength": 4,
-                    }
-                ]
+                    },
+                ],
             },
         ]
         i = 0
@@ -602,9 +636,16 @@ def _test_opaque_add_del(tgen, apibin):
         # 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
 
-        step("reachable: check for API add notifications")
-        ls_ids = ["230.0.0.2", "231.0.0.1", "231.0.0.2", "232.0.0.3"]
+        ls_ids = [
+            "230.0.0.1",
+            "230.0.0.2",
+            "231.0.0.1",
+            "231.0.0.2",
+            "232.0.0.1",
+            "232.0.0.2",
+        ]
         for ls_id in ls_ids:
+            step("reachable: check for API add notification: %s" % ls_id)
             waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
             _ = _wait_output(pread, waitfor)
 
@@ -612,39 +653,55 @@ def _test_opaque_add_del(tgen, apibin):
             "areas": {
                 "1.2.3.4": {
                     "linkLocalOpaqueLsa": [
+                        {
+                            "lsId": "230.0.0.1",
+                            "advertisedRouter": "192.168.0.1",
+                            "sequenceNumber": "80000001",
+                            "checksum": "6d5f",
+                        },
                         {
                             "lsId": "230.0.0.2",
                             "advertisedRouter": "192.168.0.1",
+                            "lsaAge": 3600,
                             "sequenceNumber": "80000001",
                             "checksum": "8142",
-                        }
+                        },
                     ],
+                    "linkLocalOpaqueLsaCount": 2,
                     "areaLocalOpaqueLsa": [
                         {
-                            "lsaAge": 3600,
                             "lsId": "231.0.0.1",
                             "advertisedRouter": "192.168.0.1",
                             "sequenceNumber": "80000001",
-                            "checksum": "695a",
+                            "checksum": "5278",
                         },
                         {
-                            "lsaAge": 3600,
                             "lsId": "231.0.0.2",
                             "advertisedRouter": "192.168.0.1",
-                            "sequenceNumber": "80000001",
-                            "checksum": "4881",
+                            "lsaAge": 3600,
+                            "sequenceNumber": "80000002",
+                            "checksum": "4682",
                         },
                     ],
-                }
+                    "areaLocalOpaqueLsaCount": 2,
+                },
             },
             "asExternalOpaqueLsa": [
                 {
-                    "lsId": "232.0.0.3",
+                    "lsId": "232.0.0.1",
                     "advertisedRouter": "192.168.0.1",
+                    "lsaAge": 3600,
                     "sequenceNumber": "80000001",
-                    "checksum": "c666",
-                }
+                    "checksum": "5575",
+                },
+                {
+                    "lsId": "232.0.0.2",
+                    "advertisedRouter": "192.168.0.1",
+                    "sequenceNumber": "80000001",
+                    "checksum": "d05d",
+                },
             ],
+            "asExternalOpaqueLsaCount": 2,
         }
 
         step("reachable: check for explicit withdrawal LSAs")
@@ -652,64 +709,207 @@ def _test_opaque_add_del(tgen, apibin):
         assert verify_ospf_database(tgen, r1, del_input_dict, json_cmd) is None
         assert verify_ospf_database(tgen, r2, del_input_dict, json_cmd) is None
 
+        del_detail_input_dict = [
+            {
+                "linkLocalOpaqueLsa": {
+                    "areas": {
+                        "1.2.3.4": [
+                            {
+                                "linkStateId": "230.0.0.1",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "6d5f",
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                            {
+                                "linkStateId": "230.0.0.2",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaAge": 3600,
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "8142",
+                                "length": 24,
+                                "opaqueId": 2,
+                                "opaqueDataLength": 4,
+                            },
+                        ]
+                    }
+                }
+            },
+            {
+                "areaLocalOpaqueLsa": {
+                    "areas": {
+                        "1.2.3.4": [
+                            {
+                                "linkStateId": "231.0.0.1",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "5278",
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                            {
+                                "lsaAge": 3600,
+                                "linkStateId": "231.0.0.2",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000002",
+                                "checksum": "4682",
+                                # data removed
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                        ],
+                    },
+                },
+            },
+            {
+                "asExternalOpaqueLsa": [
+                    {
+                        "linkStateId": "232.0.0.1",
+                        "advertisingRouter": "192.168.0.1",
+                        "lsaAge": 3600,
+                        "lsaSeqNumber": "80000001",
+                        "checksum": "5575",
+                        "length": 20,
+                        "opaqueDataLength": 0,
+                    },
+                    {
+                        "linkStateId": "232.0.0.2",
+                        "advertisingRouter": "192.168.0.1",
+                        "lsaSeqNumber": "80000001",
+                        "checksum": "d05d",
+                        "length": 24,
+                        "opaqueDataLength": 4,
+                    },
+                ],
+            },
+        ]
+        i = 0
+        while i < numcs:
+            step("reachable: check for delete LSA details: %s" % json_cmds[i])
+            assert (
+                verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
+                is None
+            )
+            assert (
+                verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
+                is None
+            )
+            i += 1
+
         p.terminate()
         if p.wait():
             comm_error(p)
-        del_input_dict = {
-            "areas": {
-                "1.2.3.4": {
-                    "linkLocalOpaqueLsa": [
-                        {
-                            "lsaAge": 3600,
-                            "lsId": "230.0.0.2",
-                            "advertisedRouter": "192.168.0.1",
-                            "sequenceNumber": "80000001",
-                            "checksum": "8142",
-                        }
-                    ],
-                    "areaLocalOpaqueLsa": [
-                        {
-                            "lsaAge": 3600,
-                            "lsId": "231.0.0.1",
-                            "advertisedRouter": "192.168.0.1",
-                            "sequenceNumber": "80000001",
-                            "checksum": "695a",
-                        },
-                        {
-                            "lsaAge": 3600,
-                            "lsId": "231.0.0.2",
-                            "advertisedRouter": "192.168.0.1",
-                            "sequenceNumber": "80000001",
-                            "checksum": "4881",
-                        },
-                    ],
+
+        del_detail_input_dict = [
+            {
+                "linkLocalOpaqueLsa": {
+                    "areas": {
+                        "1.2.3.4": [
+                            {
+                                "linkStateId": "230.0.0.1",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaAge": 3600,
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "6d5f",
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                            {
+                                "linkStateId": "230.0.0.2",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaAge": 3600,
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "8142",
+                                "length": 24,
+                                "opaqueId": 2,
+                                "opaqueDataLength": 4,
+                            },
+                        ]
+                    }
                 }
             },
-            "asExternalOpaqueLsa": [
-                {
-                    "lsaAge": 3600,
-                    "lsId": "232.0.0.3",
-                    "advertisedRouter": "192.168.0.1",
-                    "sequenceNumber": "80000001",
-                    "checksum": "c666",
-                }
-            ],
-        }
-
-        step("reachable: check for implicit withdrawal LSAs")
-        json_cmd = "show ip ospf da json"
-        assert verify_ospf_database(tgen, r1, del_input_dict, json_cmd) is None
-        assert verify_ospf_database(tgen, r2, del_input_dict, json_cmd) is None
+            {
+                "areaLocalOpaqueLsa": {
+                    "areas": {
+                        "1.2.3.4": [
+                            {
+                                "lsaAge": 3600,
+                                "linkStateId": "231.0.0.1",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000001",
+                                "checksum": "5278",
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                            {
+                                "lsaAge": 3600,
+                                "linkStateId": "231.0.0.2",
+                                "advertisingRouter": "192.168.0.1",
+                                "lsaSeqNumber": "80000002",
+                                "checksum": "4682",
+                                # data removed
+                                "length": 20,
+                                "opaqueDataLength": 0,
+                            },
+                        ],
+                    },
+                },
+            },
+            {
+                "asExternalOpaqueLsa": [
+                    {
+                        "linkStateId": "232.0.0.1",
+                        "advertisingRouter": "192.168.0.1",
+                        "lsaAge": 3600,
+                        "lsaSeqNumber": "80000001",
+                        "checksum": "5575",
+                        "length": 20,
+                        "opaqueDataLength": 0,
+                    },
+                    {
+                        "linkStateId": "232.0.0.2",
+                        "advertisingRouter": "192.168.0.1",
+                        "lsaAge": 3600,
+                        "lsaSeqNumber": "80000001",
+                        "checksum": "d05d",
+                        "length": 24,
+                        "opaqueDataLength": 4,
+                    },
+                ],
+            },
+        ]
+        i = 0
+        while i < numcs:
+            step(
+                "reachable: check for post API shutdown delete LSA details: %s"
+                % json_cmds[i]
+            )
+            assert (
+                verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
+                is None
+            )
+            assert (
+                verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
+                is None
+            )
+            i += 1
 
         # 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 API delete notifications")
-        ls_ids = ["231.0.0.1", "231.0.0.2", "230.0.0.2", "232.0.0.3"]
+        ls_ids = [
+            "230.0.0.2",
+            "231.0.0.2",
+            "232.0.0.1",
+            "230.0.0.1",
+            "231.0.0.1",
+            "232.0.0.2",
+        ]
         for ls_id in ls_ids:
-            waitfor = "RECV:.*delete msg.*LSA {}.*".format(ls_id)
+            step("reachable: check for API delete notification: %s" % ls_id)
+            waitfor = "RECV:.*delete msg.*LSA {}.*age".format(ls_id)
             _ = _wait_output(pread, waitfor)
     except Exception:
         if p: