Skip to content

Commit 8b906b4

Browse files
mctpd: accept set endpoint ID as endpoint
Currently, mctpd handles Set EID message as a bus owner, which means it assumes it has at least one local EID and rejects all Set Endpoint ID requests. This commit handles the case where mctpd runs on an endpoint and it has no EID set yet. Signed-off-by: Khang D Nguyen <[email protected]>
1 parent 19e2104 commit 8b906b4

File tree

3 files changed

+246
-8
lines changed

3 files changed

+246
-8
lines changed

src/mctp-control-spec.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,23 @@ struct mctp_ctrl_resp_resolve_endpoint_id {
232232

233233
#define MCTP_CTRL_CC_GET_MCTP_VER_SUPPORT_UNSUPPORTED_TYPE 0x80
234234

235-
/* MCTP Set Endpoint ID response fields
235+
/* MCTP Set Endpoint ID request and response fields
236236
* See DSP0236 v1.3.0 Table 14.
237237
*/
238238

239+
#define MCTP_SET_EID_OPERATION_SHIFT 0x0
240+
#define MCTP_SET_EID_OPERATION_MASK 0x3
241+
#define GET_MCTP_SET_EID_OPERATION(field) \
242+
(((field) >> MCTP_SET_EID_OPERATION_SHIFT) & \
243+
MCTP_SET_EID_OPERATION_MASK)
244+
#define SET_MCTP_SET_EID_OPERATION(status) \
245+
(((status) & MCTP_SET_EID_OPERATION_MASK) \
246+
<< MCTP_SET_EID_OPERATION_SHIFT)
247+
#define MCTP_SET_EID_SET 0x0
248+
#define MCTP_SET_EID_FORCE 0x1
249+
#define MCTP_SET_EID_RESET 0x2
250+
#define MCTP_SET_EID_DISCOVERED 0x3
251+
239252
#define MCTP_EID_ASSIGNMENT_STATUS_SHIFT 0x4
240253
#define MCTP_EID_ASSIGNMENT_STATUS_MASK 0x3
241254
#define SET_MCTP_EID_ASSIGNMENT_STATUS(status) \
@@ -244,6 +257,15 @@ struct mctp_ctrl_resp_resolve_endpoint_id {
244257
#define MCTP_SET_EID_ACCEPTED 0x0
245258
#define MCTP_SET_EID_REJECTED 0x1
246259

260+
#define MCTP_EID_ALLOCATION_STATUS_SHIFT 0x0
261+
#define MCTP_EID_ALLOCATION_STATUS_MASK 0x3
262+
#define SET_MCTP_EID_ALLOCATION_STATUS(status) \
263+
(((status) & MCTP_EID_ALLOCATION_STATUS_MASK) \
264+
<< MCTP_EID_ALLOCATION_STATUS_SHIFT)
265+
#define MCTP_SET_EID_POOL_NONE 0x0
266+
#define MCTP_SET_EID_POOL_REQUIRED 0x1
267+
#define MCTP_SET_EID_POOL_RECEIVED 0x2
268+
247269
/* MCTP Physical Transport Binding identifiers
248270
* See DSP0239 v1.7.0 Table 3.
249271
*/

src/mctpd.c

Lines changed: 157 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ static int emit_interface_added(struct link *link);
231231
static int emit_interface_removed(struct link *link);
232232
static int emit_net_added(struct ctx *ctx, struct net *net);
233233
static int emit_net_removed(struct ctx *ctx, struct net *net);
234+
static int add_peer(struct ctx *ctx, const dest_phys *dest, mctp_eid_t eid,
235+
uint32_t net, struct peer **ret_peer);
236+
static int add_peer_from_addr(struct ctx *ctx,
237+
const struct sockaddr_mctp_ext *addr,
238+
struct peer **ret_peer);
239+
static int remove_peer(struct peer *peer);
234240
static int query_peer_properties(struct peer *peer);
235241
static int setup_added_peer(struct peer *peer);
236242
static void add_peer_route(struct peer *peer);
@@ -631,32 +637,162 @@ static int reply_message(struct ctx *ctx, int sd, const void *resp,
631637
return 0;
632638
}
633639

634-
// Handles new Incoming Set Endpoint ID request
640+
/// Clear interface local addresses and remote cached peers
641+
static void clear_interface_addrs(struct ctx *ctx, int ifindex)
642+
{
643+
mctp_eid_t *addrs;
644+
size_t addrs_num;
645+
size_t i;
646+
int rc;
647+
648+
// Remove all addresses on this interface
649+
addrs = mctp_nl_addrs_byindex(ctx->nl, ifindex, &addrs_num);
650+
if (addrs) {
651+
for (i = 0; i < addrs_num; i++) {
652+
rc = mctp_nl_addr_del(ctx->nl, addrs[i], ifindex);
653+
if (rc < 0) {
654+
errx(rc,
655+
"ERR: cannot remove local eid %d ifindex %d",
656+
addrs[i], ifindex);
657+
}
658+
}
659+
free(addrs);
660+
}
661+
662+
// Remove all peers on this interface
663+
for (i = 0; i < ctx->num_peers; i++) {
664+
struct peer *p = ctx->peers[i];
665+
if (p->state == REMOTE && p->phys.ifindex == ifindex) {
666+
remove_peer(p);
667+
}
668+
}
669+
}
670+
671+
/// Handles new Incoming Set Endpoint ID request
672+
///
673+
/// This currently handles two cases: Top-most bus owner and Endpoint. No bridge
674+
/// support yet.
675+
///
676+
///
677+
/// # References
678+
///
679+
/// The DSP0236 1.3.3 specification describes Set Endpoint ID in the following
680+
/// sections:
681+
///
682+
/// - 8.18 Endpoint ID assignment and endpoint ID pools
683+
///
684+
/// > A non-bridge device that is connected to multiple different buses
685+
/// > will have one EID for each bus it is attached to.
686+
///
687+
/// - 9.1.3 EID options for MCTP bridge
688+
///
689+
/// > There are three general options:
690+
/// > - The bridge uses a single MCTP endpoint
691+
/// > - The bridge uses an MCTP endpoint for each bus that connects to a bus owner
692+
/// > - The bridge uses an MCTP endpoint for every bus to which it connects
693+
///
694+
/// - 12.4 Set Endpoint ID
695+
///
696+
/// [the whole section]
697+
///
635698
static int handle_control_set_endpoint_id(struct ctx *ctx, int sd,
636699
struct sockaddr_mctp_ext *addr,
637700
const uint8_t *buf,
638701
const size_t buf_size)
639702
{
640703
struct mctp_ctrl_cmd_set_eid *req = NULL;
641704
struct mctp_ctrl_resp_set_eid respi = { 0 }, *resp = &respi;
705+
struct link *link_data;
706+
struct peer *peer;
642707
size_t resp_len;
708+
int rc;
643709

644710
if (buf_size < sizeof(*req)) {
645-
warnx("short Set Endpoint ID message");
711+
bug_warn("short Set Endpoint ID message");
646712
return -ENOMSG;
647713
}
648714
req = (void *)buf;
649715

716+
link_data = mctp_nl_get_link_userdata(ctx->nl, addr->smctp_ifindex);
717+
if (!link_data) {
718+
bug_warn("unconfigured interface %d", addr->smctp_ifindex);
719+
return -ENOENT;
720+
}
721+
650722
mctp_ctrl_msg_hdr_init_resp(&respi.ctrl_hdr, req->ctrl_hdr);
651723
resp->completion_code = MCTP_CTRL_CC_SUCCESS;
652-
resp->status = 0x01 << 4; // Already assigned, TODO
653-
resp->eid_set = local_addr(ctx, addr->smctp_ifindex);
654-
resp->eid_pool_size = 0;
655724
resp_len = sizeof(struct mctp_ctrl_resp_set_eid);
656725

657-
// TODO: learn busowner route and neigh
726+
// reject if we are bus owner
727+
if (link_data->role == ENDPOINT_ROLE_BUS_OWNER) {
728+
warnx("Rejected set EID %d request from (%s) because we are the bus owner",
729+
req->eid, ext_addr_tostr(addr));
730+
resp->completion_code = MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD;
731+
resp_len = sizeof(struct mctp_ctrl_resp);
732+
return reply_message(ctx, sd, resp, resp_len, addr);
733+
}
658734

659-
return reply_message(ctx, sd, resp, resp_len, addr);
735+
// error if EID is invalid
736+
if (req->eid < 0x08 || req->eid == 0xFF) {
737+
warnx("Rejected invalid EID %d", req->eid);
738+
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
739+
resp_len = sizeof(struct mctp_ctrl_resp);
740+
return reply_message(ctx, sd, resp, resp_len, addr);
741+
}
742+
743+
switch (GET_MCTP_SET_EID_OPERATION(req->operation)) {
744+
case MCTP_SET_EID_SET:
745+
// TODO: for bridges, only accept EIDs from originator bus
746+
//
747+
// We currently only support endpoints, which require separate EIDs on
748+
// interfaces (see function comment). For bridges, we might need to support
749+
// sharing a single EID for multiple interfaces. We will need to:
750+
// - track the first bus assigned the EID.
751+
// - policy for propagating EID to other interfaces (see bridge EID options in
752+
// function comment above)
753+
754+
// fallthrough
755+
case MCTP_SET_EID_FORCE:
756+
757+
fprintf(stderr, "setting EID to %d\n", req->eid);
758+
759+
// When we are assigned a new EID, assume our world view of the network
760+
// reachable from this interface has been stale. Reset everything.
761+
clear_interface_addrs(ctx, addr->smctp_ifindex);
762+
763+
rc = mctp_nl_addr_add(ctx->nl, req->eid, addr->smctp_ifindex);
764+
if (rc < 0) {
765+
warnx("ERR: cannot add local eid %d to ifindex %d",
766+
req->eid, addr->smctp_ifindex);
767+
resp->completion_code = MCTP_CTRL_CC_ERROR_NOT_READY;
768+
}
769+
770+
rc = add_peer_from_addr(ctx, addr, &peer);
771+
if (rc == 0) {
772+
rc = setup_added_peer(peer);
773+
}
774+
if (rc < 0) {
775+
warnx("ERR: cannot add bus owner to object lists");
776+
}
777+
778+
resp->status =
779+
SET_MCTP_EID_ASSIGNMENT_STATUS(MCTP_SET_EID_ACCEPTED) |
780+
SET_MCTP_EID_ALLOCATION_STATUS(MCTP_SET_EID_POOL_NONE);
781+
resp->eid_set = req->eid;
782+
resp->eid_pool_size = 0;
783+
fprintf(stderr, "Accepted set eid %d\n", req->eid);
784+
return reply_message(ctx, sd, resp, resp_len, addr);
785+
786+
case MCTP_SET_EID_DISCOVERED:
787+
case MCTP_SET_EID_RESET:
788+
// unsupported
789+
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
790+
return reply_message(ctx, sd, resp, resp_len, addr);
791+
792+
default:
793+
bug_warn("unreachable Set EID operation code");
794+
return -EINVAL;
795+
}
660796
}
661797

662798
static int
@@ -1478,6 +1614,20 @@ static int add_peer(struct ctx *ctx, const dest_phys *dest, mctp_eid_t eid,
14781614
return 0;
14791615
}
14801616

1617+
static int add_peer_from_addr(struct ctx *ctx,
1618+
const struct sockaddr_mctp_ext *addr,
1619+
struct peer **ret_peer)
1620+
{
1621+
struct dest_phys phys;
1622+
1623+
phys.ifindex = addr->smctp_ifindex;
1624+
memcpy(phys.hwaddr, addr->smctp_haddr, addr->smctp_halen);
1625+
phys.hwaddr_len = addr->smctp_halen;
1626+
1627+
return add_peer(ctx, &phys, addr->smctp_base.smctp_addr.s_addr,
1628+
addr->smctp_base.smctp_network, ret_peer);
1629+
}
1630+
14811631
static int check_peer_struct(const struct peer *peer, const struct net *n)
14821632
{
14831633
if (n->net != peer->net) {

tests/test_mctpd_endpoint.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import pytest
2+
import asyncdbus
23
from mctp_test_utils import *
34
from mctpenv import *
45

6+
"""Simple endpoint setup.
7+
8+
Contains one interface (lladdr 0x1d), and one bus-owner (lladdr 0x1d, eid 8),
9+
that reports support for MCTP control and PLDM.
10+
"""
11+
512
@pytest.fixture
613
def config():
714
return """
@@ -35,3 +42,62 @@ async def test_respond_get_eid_with_no_eid(dbus, mctpd):
3542
cmd = MCTPControlCommand(True, 0, 0x02)
3643
rsp = await bo.send_control(mctpd.network.mctp_socket, cmd)
3744
assert rsp.hex(' ') == '00 02 00 00 02 00'
45+
46+
47+
""" Test if mctpd accepts Set EID when no EID """
48+
async def test_accept_set_eid(dbus, mctpd):
49+
bo = mctpd.network.endpoints[0]
50+
51+
assert len(mctpd.system.addresses) == 0
52+
53+
# no EID yet
54+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
55+
assert rsp.hex(' ') == '00 02 00 00 02 00'
56+
57+
# set EID = 42
58+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, 0x42])))
59+
assert rsp.hex(' ') == '00 01 00 00 42 00'
60+
61+
# get EID, expect receive 42 back
62+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
63+
assert rsp.hex(' ') == '00 02 00 42 02 00'
64+
65+
66+
async def test_accept_multiple_set_eids_for_single_interface(dbus, mctpd):
67+
bo = mctpd.network.endpoints[0]
68+
69+
assert len(mctpd.system.addresses) == 0
70+
71+
# if we are only reachable through one interfaces,
72+
# accept all Set EIDs
73+
assert len(mctpd.system.interfaces) == 1
74+
75+
# no EID yet
76+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
77+
assert rsp.hex(' ') == '00 02 00 00 02 00'
78+
79+
# set EID = 42
80+
first_eid = 42
81+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, first_eid])))
82+
assert rsp.hex(' ') == f'00 01 00 00 {first_eid:02x} 00'
83+
84+
# get EID, expect receive 42 back
85+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
86+
assert rsp.hex(' ') == f'00 02 00 {first_eid:02x} 02 00'
87+
88+
# set EID = 66
89+
second_eid = 66
90+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, second_eid])))
91+
assert rsp.hex(' ') == f'00 01 00 00 {second_eid:02x} 00'
92+
93+
# get EID, expect receive 66 back
94+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
95+
assert rsp.hex(' ') == f'00 02 00 {second_eid:02x} 02 00'
96+
97+
# expect previous EID removed on D-Bus
98+
with pytest.raises(asyncdbus.errors.DBusError) as ex:
99+
await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{first_eid}")
100+
assert str(ex.value) == f"Unknown object '/au/com/codeconstruct/mctp1/networks/1/endpoints/{first_eid}'."
101+
102+
# expect new EID on D-Bus
103+
assert await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{second_eid}")

0 commit comments

Comments
 (0)