get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.0/patches/2218850/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2218850,
    "url": "http://patchwork.ozlabs.org/api/1.0/patches/2218850/?format=api",
    "project": {
        "id": 22,
        "url": "http://patchwork.ozlabs.org/api/1.0/projects/22/?format=api",
        "name": "HostAP Development",
        "link_name": "hostap",
        "list_id": "hostap.lists.infradead.org",
        "list_email": "hostap@lists.infradead.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260401220220.4418-59-andrei.otcheretianski@intel.com>",
    "date": "2026-04-01T22:02:07",
    "name": "[RFC,58/71] tests: Add NAN data path tests",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "e42cd8ba3e5ff59e84b7f450db2b4e3815b23446",
    "submitter": {
        "id": 62065,
        "url": "http://patchwork.ozlabs.org/api/1.0/people/62065/?format=api",
        "name": "Andrei Otcheretianski",
        "email": "andrei.otcheretianski@intel.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/hostap/patch/20260401220220.4418-59-andrei.otcheretianski@intel.com/mbox/",
    "series": [
        {
            "id": 498402,
            "url": "http://patchwork.ozlabs.org/api/1.0/series/498402/?format=api",
            "date": "2026-04-01T22:01:09",
            "name": "NAN Data Path and Bootstrapping support",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/498402/mbox/"
        }
    ],
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2218850/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n secure) header.d=lists.infradead.org header.i=@lists.infradead.org\n header.a=rsa-sha256 header.s=bombadil.20210309 header.b=bf/s8eB2;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256\n header.s=Intel header.b=F1/BZ0cF;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=none (no SPF record) smtp.mailfrom=lists.infradead.org\n (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org;\n envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from bombadil.infradead.org (bombadil.infradead.org\n [IPv6:2607:7c80:54:3::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fmK1B2wV8z1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 02 Apr 2026 09:10:22 +1100 (AEDT)",
            "from localhost ([::1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1w83kx-0000000GFVq-29Qm;\n\tWed, 01 Apr 2026 22:09:47 +0000",
            "from mgamail.intel.com ([198.175.65.20])\n\tby bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1w83ht-0000000G9xK-2tWi\n\tfor hostap@lists.infradead.org;\n\tWed, 01 Apr 2026 22:06:46 +0000",
            "from fmviesa003.fm.intel.com ([10.60.135.143])\n  by orvoesa112.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 01 Apr 2026 15:06:12 -0700",
            "from iapp347.iil.intel.com (HELO 87c02287900a.iil.intel.com)\n ([10.167.28.6])\n  by fmviesa003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 01 Apr 2026 15:06:11 -0700"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20210309; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:\n\tList-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:\n\tMessage-ID:Date:Subject:To:From:Reply-To:Cc:Content-ID:Content-Description:\n\tResent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:\n\tList-Owner; bh=o99P/W0suaC5dOI+Q2Dg8WTMwGJjYUsmJ+/JaRH54CE=; b=bf/s8eB2OH38ci\n\tfgWq5pPBL7FUqmc+OMSzftDCluWU4aYf1ygECcYetUPqxb4by064r1MvGjBdvANUDDpCrNfhDdcYK\n\tRQOCtv5/WS6G3G/MMZH2BQv5HyA7VqdqyBLdV365z6b+rZMnamBaMpbijI52egjbLcwUnAAqpz3RH\n\t0YvewfPOmJdBy2+xfkf3hEN8pCqJAQkuYBE+8Xzuk6RVlA+83KkYgyw+YtTWotgGGjxT3BLqLgF0+\n\t/4ZYwadgbcmLLxPEGyqqeSuUbdWvNcP9hPVKh4JXUHD6MT4J0VycKFzavPr3o0ZQMQmplnsVnWyLf\n\tjK/VyR6XxWvfXwmWpiQA==;",
            "v=1; a=rsa-sha256; c=relaxed/simple;\n  d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n  t=1775081197; x=1806617197;\n  h=from:to:subject:date:message-id:in-reply-to:references:\n   mime-version:content-transfer-encoding;\n  bh=bVhtMJYfDfmzgIBz7Ayyb+FumBe2e+C5lv9Lmio9id8=;\n  b=F1/BZ0cFuVlb7nr7F2lgQ+3LTktdEI1ndtSuFcd9EyiptMAF3uW2gtLk\n   7f7BPVjAenumMPBv+LCEv2ZWZUU5dclcnvg/qkyePBf/EeA0pwVMYdz/T\n   e5WWFaNlG7V6rXKdTn0vAINK7IWYhJc7PwmnWKWyz07r9sYj0eAccbIGK\n   lWu1f/J3plndQBUYFyvv0SDWpyBqLEL9hjnLH57MqOa195QVz7nvRGGY8\n   nQTiID3/r7loooxg+jnq+ECWPCKqA2HcUxGdDpWE+/jUyyW29Uxat5ZbS\n   lgWvThXyVQzXHjgrfucLsrGrR4fw5Z6qKuVTTQgoJalJFF+r0GNGwuoMW\n   Q==;"
        ],
        "X-CSE-ConnectionGUID": [
            "rIC6YWGBSO6K0gsOZVpZ9w==",
            "a8WEMghjT2SHCjbNxV5kmg=="
        ],
        "X-CSE-MsgGUID": [
            "vSzqBAFCT1q1h6CE43qYjQ==",
            "nXREOUx0TBalb9iqOmLPEw=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6800,10657,11746\"; a=\"75851657\"",
            "E=Sophos;i=\"6.23,153,1770624000\";\n   d=\"scan'208\";a=\"75851657\""
        ],
        "X-ExtLoop1": "1",
        "From": "Andrei Otcheretianski <andrei.otcheretianski@intel.com>",
        "To": "hostap@lists.infradead.org",
        "Subject": "[RFC 58/71] tests: Add NAN data path tests",
        "Date": "Thu,  2 Apr 2026 01:02:07 +0300",
        "Message-ID": "<20260401220220.4418-59-andrei.otcheretianski@intel.com>",
        "X-Mailer": "git-send-email 2.53.0",
        "In-Reply-To": "<20260401220220.4418-1-andrei.otcheretianski@intel.com>",
        "References": "<20260401220220.4418-1-andrei.otcheretianski@intel.com>",
        "MIME-Version": "1.0",
        "X-CRM114-Version": "20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ",
        "X-CRM114-CacheID": "sfid-20260401_150637_870514_56E3A060 ",
        "X-CRM114-Status": "GOOD (  13.56  )",
        "X-Spam-Score": "-1.9 (-)",
        "X-Spam-Report": "Spam detection software,\n running on the system \"bombadil.infradead.org\",\n has NOT identified this incoming email as spam.  The original\n message has been attached to this so you can view it or label\n similar future email.  If you have any questions, see\n the administrator of that system for details.\n Content preview:  Add several NDP establishment tests for open and encrypted\n    connections,\n including counter NDL proposal. In addition add local schedule\n    configuration test. Signed-off-by: Andrei Otcheretianski\n <andrei.otcheretianski@intel.com>\n    --- tests/hwsim/test_nan.py | 320 +++++++++++++++++++++++++++++++++++++++-\n    1 file changed, 319 insertions(+), 1 deletion(-)\n Content analysis details:   (-1.9 points, 5.0 required)\n  pts rule name              description\n ---- ----------------------\n --------------------------------------------------\n -2.3 RCVD_IN_DNSWL_MED      RBL: Sender listed at https://www.dnswl.org/,\n                             medium trust\n                             [198.175.65.20 listed in list.dnswl.org]\n  1.0 RCVD_IN_VALIDITY_RPBL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to\n                              Validity was blocked.  See\n                             https://knowledge.validity.com/hc/en-us/articles/20961730681243\n                              for more information.\n                             [198.175.65.20 listed in\n bl.score.senderscore.com]\n  1.0 RCVD_IN_VALIDITY_CERTIFIED_BLOCKED RBL: ADMINISTRATOR NOTICE: The\n                             query to Validity was blocked.  See\n                             https://knowledge.validity.com/hc/en-us/articles/20961730681243\n                              for more information.\n                          [198.175.65.20 listed in\n sa-trusted.bondedsender.org]\n  1.0 RCVD_IN_VALIDITY_SAFE_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to\n                              Validity was blocked.  See\n                             https://knowledge.validity.com/hc/en-us/articles/20961730681243\n                              for more information.\n                             [198.175.65.20 listed in sa-accredit.habeas.com]\n  0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record\n -0.0 SPF_PASS               SPF: sender matches SPF record\n  0.1 DKIM_SIGNED            Message has a DKIM or DK signature,\n not necessarily valid\n -0.1 DKIM_VALID_EF          Message has a valid DKIM or DK signature from\n                             envelope-from domain\n -0.1 DKIM_VALID_AU          Message has a valid DKIM or DK signature from\n author's\n                             domain\n -0.1 DKIM_VALID             Message has at least one valid DKIM or DK\n signature\n -1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%\n                             [score: 0.0000]\n -0.5 DKIMWL_WL_HIGH         DKIMwl.org - High trust sender",
        "X-BeenThere": "hostap@lists.infradead.org",
        "X-Mailman-Version": "2.1.34",
        "Precedence": "list",
        "List-Id": "<hostap.lists.infradead.org>",
        "List-Unsubscribe": "<http://lists.infradead.org/mailman/options/hostap>,\n <mailto:hostap-request@lists.infradead.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.infradead.org/pipermail/hostap/>",
        "List-Post": "<mailto:hostap@lists.infradead.org>",
        "List-Help": "<mailto:hostap-request@lists.infradead.org?subject=help>",
        "List-Subscribe": "<http://lists.infradead.org/mailman/listinfo/hostap>,\n <mailto:hostap-request@lists.infradead.org?subject=subscribe>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "\"Hostap\" <hostap-bounces@lists.infradead.org>",
        "Errors-To": "hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org"
    },
    "content": "Add several NDP establishment tests for open and encrypted connections,\nincluding counter NDL proposal.\nIn addition add local schedule configuration test.\n\nSigned-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>\n---\n tests/hwsim/test_nan.py | 320 +++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 319 insertions(+), 1 deletion(-)",
    "diff": "diff --git a/tests/hwsim/test_nan.py b/tests/hwsim/test_nan.py\nindex f7a212f7f8..24e10608ab 100644\n--- a/tests/hwsim/test_nan.py\n+++ b/tests/hwsim/test_nan.py\n@@ -9,8 +9,10 @@ import logging\n logger = logging.getLogger()\n from utils import *\n import string\n+from hwsim_utils import test_connectivity\n from hwsim import HWSimRadio\n from contextlib import contextmanager, ExitStack\n+from test_p2p_channel import set_country\n \n @contextmanager\n def hwsim_nan_radios(count=2, n_channels=3):\n@@ -41,10 +43,11 @@ def check_nan_capab(dev):\n         raise HwsimSkip(f\"NAN not supported: {capa}\")\n \n class NanDevice:\n-    def __init__(self, dev, ifname):\n+    def __init__(self, dev, ifname, ndi_name=None):\n         self.dev = dev\n         self.ifname = ifname\n         self.wpas = None\n+        self.ndi_name = ndi_name\n \n     def __enter__(self):\n         self.start()\n@@ -71,12 +74,19 @@ class NanDevice:\n \n         logger.info(f\"NAN device started on {self.ifname}\")\n \n+        # Add NDI\n+        if self.ndi_name is not None:\n+            self.dev.interface_add(self.ndi_name, if_type=\"nan_data\", create=True)\n+\n     def stop(self):\n         logger.info(f\"NAN device stopping on {self.ifname}\")\n \n         if \"OK\" not in self.wpas.request(\"NAN_STOP\"):\n             raise Exception(f\"Failed to stop NAN functionality on {self.ifname}\")\n \n+        if self.ndi_name is not None:\n+            self.dev.interface_remove(self.ndi_name)\n+\n         self.dev.global_request(f\"INTERFACE_REMOVE {self.ifname}\")\n         self.wpas.remove_ifname()\n \n@@ -105,6 +115,68 @@ class NanDevice:\n \n         return self.wpas.request(cmd)\n \n+    def schedule_config(self, *chans, map_id=1):\n+        cmd = f\"NAN_SCHED_CONFIG_MAP map_id={map_id} \"\n+        cmd += \" \".join([f\"{freq}:{bitmap}\" for freq, bitmap in chans])\n+        return self.wpas.request(cmd)\n+\n+    def remove_shedule(self, map_id=1):\n+        cmd = f\"NAN_SCHED_CONFIG_MAP map_id={map_id}\"\n+        return self.wpas.request(cmd)\n+\n+    def ndp_request(self, ndi, handle, peer_nmi, peer_id, ssi=None,\n+                    qos_slots=0, qos_latency=0xffff, csid=None, password=None,\n+                    pmk=None):\n+        cmd = f\"NAN_NDP_REQUEST handle={handle} ndi={ndi} peer_nmi={peer_nmi} peer_id={peer_id}\"\n+\n+        params = [\n+            (\"ssi\", ssi),\n+            (\"csid\", csid),\n+            (\"password\", password),\n+            (\"pmk\", pmk),\n+        ]\n+\n+        cmd += \"\".join(f\" {name}={value}\" for name, value in params if value is not None)\n+\n+        if qos_slots > 0 or qos_latency != 0xffff:\n+            cmd += f\" qos={qos_slots}:{qos_latency}\"\n+\n+        return self.wpas.request(cmd)\n+\n+    def ndp_response(self, action, peer_nmi, ndi=None, peer_ndi=None,\n+                     ndp_id=None, init_ndi=None, reason_code=None, ssi=None,\n+                     qos_slots=0, qos_latency=0xffff, handle=None, csid=None,\n+                     password=None, pmk=None):\n+        if action not in [\"accept\", \"reject\"]:\n+            raise Exception(f\"Invalid action: {action}. Must be 'accept' or 'reject'\")\n+\n+        cmd = f\"NAN_NDP_RESPONSE {action} peer_nmi={peer_nmi}\"\n+\n+        params = [\n+            (\"reason_code\", reason_code),\n+            (\"ndi\", ndi),\n+            (\"peer_ndi\", peer_ndi),\n+            (\"ndp_id\", ndp_id),\n+            (\"init_ndi\", init_ndi),\n+            (\"handle\", handle),\n+            (\"ssi\", ssi),\n+            (\"csid\", csid),\n+            (\"password\", password),\n+            (\"pmk\", pmk),\n+        ]\n+\n+        cmd += \"\".join(f\" {name}={value}\" for name, value in params if value is not None)\n+\n+        if qos_slots > 0 or qos_latency != 0xffff:\n+            cmd += f\" qos={qos_slots}:{qos_latency}\"\n+\n+        return self.wpas.request(cmd)\n+\n+\n+    def ndp_terminate(self, peer_nmi, init_ndi, ndp_id):\n+        cmd = f\"NAN_NDP_TERMINATE peer_nmi={peer_nmi} init_ndi={init_ndi} ndp_id={ndp_id}\"\n+        return self.wpas.request(cmd)\n+\n     def subscribe(self, service_name, ssi=None, active=1,\n                   sync=1, match_filter_rx=None, match_filter_tx=None,\n                   srf_include=0, srf_mac_list=None, srf_bf_len=0,\n@@ -197,6 +269,40 @@ def nan_sync_verify_event(ev, addr, pid, sid, ssi):\n     if data['address'] != addr:\n         raise Exception(\"Unexpected peer_addr: \" + ev)\n \n+def nan_ndp_verify_event(ev, peer_nmi, publish_inst_id=None, init_ndi=None,\n+                         ssi=None, csid=None):\n+    \"\"\"Verify NAN-NDP-REQUEST event format and content\"\"\"\n+    data = split_nan_event(ev)\n+\n+    if 'peer_nmi' not in data:\n+        raise Exception(f\"Missing peer_nmi in NDP event: {ev}\")\n+\n+    if 'init_ndi' not in data:\n+        raise Exception(f\"Missing init_ndi in NDP event: {ev}\")\n+\n+    if 'ndp_id' not in data:\n+        raise Exception(f\"Missing ndp_id in NDP event: {ev}\")\n+\n+    if publish_inst_id is not None and 'publish_inst_id' not in data:\n+        raise Exception(f\"Missing publish_inst_id in NDP event: {ev}\")\n+\n+    if data['peer_nmi'] != peer_nmi:\n+        raise Exception(f\"Unexpected peer_nmi: got {data['peer_nmi']}, expected {peer_nmi} in event: {ev}\")\n+\n+    if init_ndi is not None and data['init_ndi'] != init_ndi:\n+        raise Exception(f\"Unexpected init_ndi: got {data['init_ndi']}, expected {init_ndi} in event: {ev}\")\n+\n+    if (publish_inst_id is not None and data['publish_inst_id'] != publish_inst_id):\n+        raise Exception(f\"Unexpected publish_inst_id: got {data['publish_inst_id']}, expected {publish_inst_id} in event: {ev}\")\n+\n+    if ssi is not None and 'ssi' in data:\n+        if data['ssi'] != ssi:\n+            raise Exception(f\"Unexpected ssi: got {data['ssi']}, expected {ssi} in event: {ev}\")\n+\n+    if csid is not None and 'csid' in data:\n+        if data['csid'] != str(csid):\n+            raise Exception(f\"Unexpected csid: got {data['csid']}, expected {str(csid)} in event: {ev}\")\n+\n def nan_sync_discovery(pub, sub, service_name, pssi, sssi,\n                        unsolicited=1, solicited=1, active=1,\n                        expect_discovery=True,\n@@ -834,3 +940,215 @@ def test_nan_config(dev, apdev, params):\n         # and finally update the configuration\n         logger.info(\"Updating NAN configuration\")\n         nan.update_config()\n+\n+def test_nan_sched(dev, apdev, params):\n+    \"\"\"NAN configure schedule\"\"\"\n+    set_country(\"US\")\n+    try:\n+        with hwsim_nan_radios() as (wpas1, wpas2), NanDevice(wpas1, \"nan0\") as pub:\n+            if \"OK\" not in pub.schedule_config((2437, \"03000000\"), (5180, \"0000ff00\"), (5825, \"000000ff\")):\n+                raise Exception(\"Failed to configure schedule\")\n+            # Remove\n+            if \"OK\" not in pub.remove_shedule():\n+                raise Exception(\"Failed to remove schedule\")\n+            # Overlapping maps\n+            if \"FAIL\" not in pub.schedule_config((2437, \"03000000\"), (5180, \"0000ff00\"), (5825, \"050000ff\")):\n+                raise Exception(\"A schedule with overlapping time bitmaps was unexpectedly accepted\")\n+            # Same channel\n+            if \"FAIL\" not in pub.schedule_config((2437, \"03000000\"), (2437, \"0000ff00\")):\n+                raise Exception(\"A schedule with duplicate channel entries was unexpectedly accepted\")\n+            # Bad length\n+            if \"FAIL\" not in pub.schedule_config((2437, \"0300\")):\n+                raise Exception(\"Too short schedule bitmap accepted\")\n+    finally:\n+        set_country(\"00\")\n+\n+def _nan_discover_service(pub, sub, service_name, pssi, sssi):\n+    paddr = pub.wpas.own_addr()\n+    saddr = sub.wpas.own_addr()\n+\n+    pid = pub.publish(service_name, ssi=pssi)\n+    sid = sub.subscribe(service_name, ssi=sssi, active=0)\n+\n+    logger.info(f\"Publish ID: {pid}, Subscribe ID: {sid}\")\n+\n+    ev = sub.wpas.wait_event([\"NAN-DISCOVERY-RESULT\"], timeout=2)\n+    if ev is None:\n+        raise Exception(f\"NAN-DISCOVERY-RESULT event not seen for {service_name}\")\n+\n+    nan_sync_verify_event(ev, paddr, pid, sid, pssi)\n+\n+    return pid, sid, paddr, saddr\n+\n+def _nan_ndp_request_and_accept(pub, sub, pid, sid, paddr, saddr, req_ssi, resp_ssi, csid=None,\n+                                password=None, pmk=None, counter=False, wrong_pwd=False, configure_schedule=True):\n+    \"\"\"\n+    Request NDP from subscriber and accept on publisher.\n+\n+    Returns: (ndp_id, init_ndi) or None if wrong_pwd test completed\n+    \"\"\"\n+    # Configure schedule on subscriber if needed\n+    if configure_schedule:\n+        if \"OK\" not in sub.schedule_config((2437, \"0e000000\"), (5180, \"f0ffffff\")):\n+            raise Exception(\"Failed to configure schedule (sub)\")\n+\n+    # NDP request\n+    if \"OK\" not in sub.ndp_request(sub.ndi_name, sid, paddr, pid, req_ssi,\n+                                   csid=csid, password=password, pmk=pmk):\n+        raise Exception(\"NDP request failed\")\n+\n+    ev = pub.wpas.wait_event([\"NAN-NDP-REQUEST\"], timeout=5)\n+    if ev is None:\n+        raise Exception(\"NAN-NDP-REQUEST event not seen\")\n+\n+    ndi_sub = sub.dev.get_iface_addr(sub.ndi_name)\n+    nan_ndp_verify_event(ev, saddr, pid, ndi_sub, req_ssi)\n+\n+    data = split_nan_event(ev)\n+    ndp_id = data['ndp_id']\n+    init_ndi = data['init_ndi']\n+\n+    # Configure schedule on publisher\n+    if configure_schedule:\n+        if counter:\n+            if \"OK\" not in pub.schedule_config((5745, \"feffffff\")):\n+                raise Exception(\"Failed to configure schedule (pub)\")\n+        else:\n+            if \"OK\" not in pub.schedule_config((2437, \"0e000000\"), (5180, \"f0ffffff\")):\n+                raise Exception(\"Failed to configure schedule (pub)\")\n+\n+    # Accept NDP request\n+    accept_pwd = \"WRONG_PWD\" if wrong_pwd else password\n+    if \"OK\" not in pub.ndp_response(\"accept\", saddr, ndi=pub.ndi_name, ndp_id=ndp_id, init_ndi=init_ndi,\n+                                    handle=pid, ssi=resp_ssi, csid=csid, password=accept_pwd, pmk=pmk):\n+        raise Exception(\"NDP response (accept) failed\")\n+\n+    # Verify disconnection on wrong password\n+    if wrong_pwd:\n+        ev = sub.wpas.wait_event([\"NAN-NDP-DISCONNECTED\"], timeout=5)\n+        if ev is None or \"reason=3\" not in ev:\n+            raise Exception(\"NAN-NDP-DISCONNECTED event not seen on subscriber\")\n+        ev = pub.wpas.wait_event([\"NAN-NDP-DISCONNECTED\"], timeout=5)\n+        if ev is None or \"reason=3\" not in ev:\n+            raise Exception(\"NAN-NDP-DISCONNECTED event not seen on publisher\")\n+        return None\n+\n+    # Handle counter proposal\n+    if counter:\n+        ev = sub.wpas.wait_event([\"NAN-NDP-COUNTER-REQUEST\"], timeout=5)\n+        if ev is None:\n+            raise Exception(\"NAN-NDP-COUNTER-REQUEST event not seen\")\n+\n+        nan_ndp_verify_event(ev, paddr, init_ndi=ndi_sub, ssi=resp_ssi)\n+\n+        data = split_nan_event(ev)\n+        ndp_id = data['ndp_id']\n+        init_ndi = data['init_ndi']\n+\n+        if \"OK\" not in sub.schedule_config((5745, \"feffffff\")):\n+            raise Exception(\"Failed to configure schedule (sub)\")\n+\n+        if \"OK\" not in sub.ndp_response(\"accept\", paddr, ndi=sub.ndi_name, ndp_id=ndp_id, handle=sid,\n+                                        init_ndi=init_ndi, ssi=\"11223344\", csid=csid, password=password, pmk=pmk):\n+            raise Exception(\"NDP response (confirm) failed\")\n+\n+    # Wait for NDP connected events\n+    ev = pub.wpas.wait_event([\"NAN-NDP-CONNECTED\"], timeout=5)\n+    if ev is None:\n+        raise Exception(\"NAN-NDP-CONNECTED event not seen on publisher\")\n+\n+    ev = sub.wpas.wait_event([\"NAN-NDP-CONNECTED\"], timeout=5)\n+    if ev is None:\n+        raise Exception(\"NAN-NDP-CONNECTED event not seen on subscriber\")\n+\n+    logger.info(\"NDP connection established successfully\")\n+\n+    return ndp_id, init_ndi\n+\n+def _nan_ndp_terminate(pub, sub, paddr, init_ndi, ndp_id):\n+    \"\"\"Terminate an NDP and wait for disconnect events on both sides.\"\"\"\n+\n+    sub.ndp_terminate(paddr, init_ndi, ndp_id)\n+\n+    ev = pub.wpas.wait_event([\"NAN-NDP-DISCONNECTED\"], timeout=5)\n+    if ev is None:\n+        raise Exception(f\"NAN-NDP-DISCONNECTED event not seen on publisher\")\n+\n+    ev = sub.wpas.wait_event([\"NAN-NDP-DISCONNECTED\"], timeout=5)\n+    if ev is None:\n+        raise Exception(f\"NAN-NDP-DISCONNECTED event not seen on subscriber\")\n+\n+def _nan_test_connectivity(pub, sub):\n+    \"\"\"Test IP connectivity between publisher and subscriber NDI interfaces.\"\"\"\n+    wpas_ndi_pub = WpaSupplicant(ifname=pub.ndi_name)\n+    wpas_ndi_sub = WpaSupplicant(ifname=sub.ndi_name)\n+    test_connectivity(wpas_ndi_pub, wpas_ndi_sub, tos=0, ifname1=pub.ndi_name, ifname2=sub.ndi_name,\n+                      max_tries=3, timeout=5, broadcast=False)\n+\n+def _run_nan_dp(counter=False, csid=None, wrong_pwd=False, use_pmk=False):\n+    if use_pmk:\n+        pmk = \"00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff\"\n+        pwd = None\n+    else:\n+        pwd = \"NAN\" if csid is not None else None\n+        pmk = None\n+\n+    with hwsim_nan_radios() as (wpas1, wpas2), \\\n+        NanDevice(wpas1, \"nan0\", \"ndi0\") as pub, NanDevice(wpas2, \"nan1\", \"ndi1\") as sub:\n+\n+        pssi = \"aabbccdd001122334455667788\"\n+        sssi = \"ddbbccaa001122334455667788\"\n+\n+        pid, sid, paddr, saddr= _nan_discover_service(pub, sub, \"test_service\", pssi, sssi)\n+\n+        # Log peer info (specific to this test)\n+        peer_schedule = pub.wpas.request(\"NAN_PEER_INFO \" + saddr + \" schedule\")\n+        logger.info(\"\\n\" + peer_schedule)\n+        potential = pub.wpas.request(\"NAN_PEER_INFO \" + saddr + \" potential\")\n+        logger.info(\"\\n\" + potential)\n+        capa = pub.wpas.request(\"NAN_PEER_INFO \" + saddr + \" capa\")\n+        logger.info(\"\\n\" + capa)\n+\n+        result = _nan_ndp_request_and_accept(pub, sub, pid, sid, paddr, saddr, req_ssi=\"aabbcc\",\n+                                             resp_ssi=\"ddeeff\", csid=csid, password=pwd, pmk=pmk,\n+                                             counter=counter, wrong_pwd=wrong_pwd)\n+\n+        if result is None:\n+            # wrong_pwd test completed\n+            return\n+\n+        ndp_id, init_ndi = result\n+\n+        _nan_test_connectivity(pub, sub)\n+        _nan_ndp_terminate(pub, sub, paddr, init_ndi, ndp_id)\n+\n+def run_nan_dp(country=\"US\", counter=False, csid=None, wrong_pwd=False, use_pmk=False):\n+    set_country(country)\n+    try:\n+        _run_nan_dp(counter=counter, csid=csid, wrong_pwd=wrong_pwd, use_pmk=use_pmk)\n+    finally:\n+        set_country(\"00\")\n+\n+def test_nan_dp_open(dev, apdev, params):\n+    \"\"\"NAN DP open\"\"\"\n+    run_nan_dp()\n+\n+def test_nan_dp_open_counter(dev, apdev, params):\n+    \"\"\"NAN DP open with counter proposal\"\"\"\n+    run_nan_dp(counter=True)\n+\n+def test_nan_dp_sk_ccmp128(dev, apdev, params):\n+    \"\"\"NAN DP - 2way NDL + SK CCMP security\"\"\"\n+    run_nan_dp(csid=1)\n+\n+def test_nan_dp_sk_gcmp256(dev, apdev, params):\n+    \"\"\"NAN DP - 3way NDL + SK GCMP-256 security\"\"\"\n+    run_nan_dp(counter=True, csid=2)\n+\n+def test_nan_dp_wrong_pwd(dev, apdev, params):\n+    \"\"\"NAN DP - Wrong password\"\"\"\n+    run_nan_dp(csid=1, wrong_pwd=True)\n+\n+def test_nan_dp_pmk(dev, apdev, params):\n+    \"\"\"NAN DP - 3way NDL + SK CCMP security with PMK\"\"\"\n+    run_nan_dp(counter=True, csid=1, use_pmk=True)\n",
    "prefixes": [
        "RFC",
        "58/71"
    ]
}