diff --git a/src/mctpd.c b/src/mctpd.c index e74f118..afd4ce1 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -1330,6 +1330,61 @@ handle_control_endpoint_discovery(struct ctx *ctx, int sd, return reply_message_phys(ctx, sd, resp, sizeof(*resp), addr); } +/* Handles an incoming Discovery Notify (DSP0236 command 0x0D): a device + * telling us, as bus owner, that it wants (re)discovery. The message + * carries no payload beyond the header - all we learn is the sender's + * physical address. Acks the message and relays it as a D-Bus signal on + * the sender's interface object; deciding what to do about it is left to + * whatever is listening for that signal, since only the bus-owner policy + * layer knows how to reconcile or debounce it. + */ +static int handle_control_discovery_notify(struct ctx *ctx, int sd, + const struct sockaddr_mctp_ext *addr, + const uint8_t *buf, + const size_t buf_size) +{ + struct mctp_ctrl_cmd_discovery_notify *req = NULL; + struct mctp_ctrl_resp_discovery_notify respi = { 0 }, *resp = &respi; + const char *ifname; + char *path = NULL; + int rc; + + if (buf_size < sizeof(*req)) { + warnx("short Discovery Notify message"); + return -ENOMSG; + } + req = (void *)buf; + + /* Acknowledge immediately using physical addressing, since the + * sender may not have a valid EID yet. */ + mctp_ctrl_msg_hdr_init_resp(&respi.ctrl_hdr, req->ctrl_hdr); + resp->completion_code = MCTP_CTRL_CC_SUCCESS; + rc = reply_message_phys(ctx, sd, resp, sizeof(respi), addr); + if (rc < 0) + warnx("Failed replying to Discovery Notify from %s: %s", + ext_addr_tostr(addr), strerror(-rc)); + + ifname = mctp_nl_if_byindex(ctx->nl, addr->smctp_ifindex); + if (!ifname) { + warnx("No interface found for Discovery Notify from %s", + ext_addr_tostr(addr)); + return 0; + } + + rc = asprintf(&path, "%s/%s", MCTP_DBUS_PATH_LINKS, ifname); + if (rc < 0) + return 0; + + rc = sd_bus_emit_signal(ctx->bus, path, CC_MCTP_DBUS_IFACE_BUSOWNER, + "DiscoveryNotify", NULL); + if (rc < 0) + warnx("Failed to emit DiscoveryNotify signal for %s: %s", + ifname, strerror(-rc)); + free(path); + + return 0; +} + static int handle_control_unsupported(struct ctx *ctx, int sd, const struct sockaddr_mctp_ext *addr, const uint8_t *buf, const size_t buf_size) @@ -1424,6 +1479,10 @@ static int cb_listen_control_msg(sd_event_source *s, int sd, uint32_t revents, rc = handle_control_endpoint_discovery(ctx, sd, &addr, buf, buf_size); break; + case MCTP_CTRL_CMD_DISCOVERY_NOTIFY: + rc = handle_control_discovery_notify(ctx, sd, &addr, buf, + buf_size); + break; default: if (ctx->verbose) { warnx("Ignoring unsupported command code 0x%02x", @@ -4330,6 +4389,8 @@ static const sd_bus_vtable bus_link_owner_vtable[] = { SD_BUS_PARAM(found), method_learn_endpoint, 0), + + SD_BUS_SIGNAL("DiscoveryNotify", "", 0), SD_BUS_VTABLE_END, }; diff --git a/tests/test_mctpd.py b/tests/test_mctpd.py index 4f75954..50747a0 100644 --- a/tests/test_mctpd.py +++ b/tests/test_mctpd.py @@ -641,6 +641,48 @@ async def test_get_endpoint_id(dbus, mctpd, routed_ep): assert rsp[3] == mctpd.system.addresses[0].eid +async def test_discovery_notify(dbus, mctpd, routed_ep): + """mctpd acks Discovery Notify and relays it as a DiscoveryNotify + signal on the sending interface's BusOwner1 object + """ + ep = routed_ep + iface = mctpd.system.interfaces[0] + + mctp = await mctpd_mctp_iface_obj(dbus, iface) + + notified = trio.Semaphore(initial_value=0) + + def discovery_notify(): + notified.release() + + await mctp.on_discovery_notify(discovery_notify) + + cmd = MCTPControlCommand(True, 0, 0x0D) + rsp = await ep.send_control(mctpd.network.mctp_socket, cmd) + + # command code + assert rsp[1] == 0x0D + # completion code indicates success + assert rsp[2] == 0x00 + + with trio.move_on_after(2) as expected: + await notified.acquire() + assert not expected.cancelled_caught + + +async def test_discovery_notify_no_eid(mctpd): + """Discovery Notify is acked using physical addressing, even from a + peer with no assigned EID yet + """ + peer = mctpd.network.endpoints[0] + + cmd = MCTPControlCommand(True, 0, 0x0D) + rsp = await peer.send_control(mctpd.network.mctp_socket, cmd) + + assert rsp[1] == 0x0D + assert rsp[2] == 0x00 + + async def test_response_iid(mctpd): """Test that instance ID is populated correctly on control protocol responses