Skip to content

Commit 521f10c

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 fa8e65d commit 521f10c

File tree

3 files changed

+245
-8
lines changed

3 files changed

+245
-8
lines changed

src/mctp-control-spec.h

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

232232
#define MCTP_CTRL_CC_GET_MCTP_VER_SUPPORT_UNSUPPORTED_TYPE 0x80
233233

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

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

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

src/mctpd.c

Lines changed: 158 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ static int emit_interface_added(struct link *link);
224224
static int emit_interface_removed(struct link *link);
225225
static int emit_net_added(struct ctx *ctx, struct net *net);
226226
static int emit_net_removed(struct ctx *ctx, struct net *net);
227+
static int add_peer(struct ctx *ctx, const dest_phys *dest, mctp_eid_t eid,
228+
uint32_t net, struct peer **ret_peer);
229+
static int add_peer_from_addr(struct ctx *ctx,
230+
const struct sockaddr_mctp_ext *addr,
231+
struct peer **ret_peer);
232+
static int remove_peer(struct peer *peer);
227233
static int query_peer_properties(struct peer *peer);
228234
static int setup_added_peer(struct peer *peer);
229235
static void add_peer_route(struct peer *peer);
@@ -624,32 +630,163 @@ static int reply_message(struct ctx *ctx, int sd, const void *resp,
624630
return 0;
625631
}
626632

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

637703
if (buf_size < sizeof(*req)) {
638-
warnx("short Set Endpoint ID message");
704+
bug_warn("short Set Endpoint ID message");
639705
return -ENOMSG;
640706
}
641707
req = (void *)buf;
642708

709+
link_data = mctp_nl_get_link_userdata(ctx->nl, addr->smctp_ifindex);
710+
if (!link_data) {
711+
bug_warn("unconfigured interface %d", addr->smctp_ifindex);
712+
return -ENOENT;
713+
}
714+
643715
mctp_ctrl_msg_hdr_init_resp(&respi.ctrl_hdr, req->ctrl_hdr);
644716
resp->completion_code = MCTP_CTRL_CC_SUCCESS;
645-
resp->status = 0x01 << 4; // Already assigned, TODO
646-
resp->eid_set = local_addr(ctx, addr->smctp_ifindex);
647-
resp->eid_pool_size = 0;
648717
resp_len = sizeof(struct mctp_ctrl_resp_set_eid);
649718

650-
// TODO: learn busowner route and neigh
719+
// reject if we are bus owner
720+
if (link_data->role == ENDPOINT_ROLE_BUS_OWNER) {
721+
warnx("Rejected set EID %d because we are the bus owner",
722+
req->eid);
723+
resp->completion_code = MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD;
724+
resp_len =
725+
sizeof(resp->ctrl_hdr) + sizeof(resp->completion_code);
726+
return reply_message(ctx, sd, resp, resp_len, addr);
727+
}
728+
729+
// error if EID is invalid
730+
if (req->eid < 0x08 || req->eid == 0xFF) {
731+
warnx("Rejected invalid EID %d", req->eid);
732+
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
733+
resp_len =
734+
sizeof(resp->ctrl_hdr) + sizeof(resp->completion_code);
735+
return reply_message(ctx, sd, resp, resp_len, addr);
736+
}
737+
738+
switch (GET_MCTP_SET_EID_OPERATION(req->operation)) {
739+
case MCTP_SET_EID_SET:
740+
// TODO: for bridges, only accept EIDs from originator bus
741+
//
742+
// We currently only support endpoints, which require separate EIDs on
743+
// interfaces (see function comment). For bridges, we might need to support
744+
// sharing a single EID for multiple interfaces. We will need to:
745+
// - track the first bus assigned the EID.
746+
// - policy for propagating EID to other interfaces (see bridge EID options in
747+
// function comment above)
748+
case MCTP_SET_EID_FORCE:
749+
750+
warnx("setting EID to %d", req->eid);
751+
752+
// When we are assigned a new EID, assume our world view of the network
753+
// reachable from this interface has been stale. Reset everything.
754+
clear_interface_addrs(ctx, addr->smctp_ifindex);
755+
756+
rc = mctp_nl_addr_add(ctx->nl, req->eid, addr->smctp_ifindex);
757+
if (rc < 0) {
758+
warnx("ERR: cannot add local eid %d to ifindex %d",
759+
req->eid, addr->smctp_ifindex);
760+
resp->completion_code = MCTP_CTRL_CC_ERROR_NOT_READY;
761+
}
762+
763+
rc = add_peer_from_addr(ctx, addr, &peer);
764+
if (rc == 0) {
765+
rc = setup_added_peer(peer);
766+
}
767+
if (rc < 0) {
768+
warnx("ERR: cannot add bus owner to object lists");
769+
}
651770

652-
return reply_message(ctx, sd, resp, resp_len, addr);
771+
SET_MCTP_EID_ASSIGNMENT_STATUS(resp->status,
772+
MCTP_SET_EID_ACCEPTED);
773+
SET_MCTP_EID_ALLOCATION_STATUS(resp->status,
774+
MCTP_SET_EID_POOL_NONE);
775+
resp->eid_set = req->eid;
776+
resp->eid_pool_size = 0;
777+
warnx("Accepted set eid %d\n", req->eid);
778+
return reply_message(ctx, sd, resp, resp_len, addr);
779+
780+
case MCTP_SET_EID_DISCOVERED:
781+
case MCTP_SET_EID_RESET:
782+
// unsupported
783+
resp->completion_code = MCTP_CTRL_CC_ERROR_INVALID_DATA;
784+
return reply_message(ctx, sd, resp, resp_len, addr);
785+
786+
default:
787+
bug_warn("unreachable Set EID operation code");
788+
return -EINVAL;
789+
}
653790
}
654791

655792
static int
@@ -1469,6 +1606,20 @@ static int add_peer(struct ctx *ctx, const dest_phys *dest, mctp_eid_t eid,
14691606
return 0;
14701607
}
14711608

1609+
static int add_peer_from_addr(struct ctx *ctx,
1610+
const struct sockaddr_mctp_ext *addr,
1611+
struct peer **ret_peer)
1612+
{
1613+
struct dest_phys phys;
1614+
1615+
phys.ifindex = addr->smctp_ifindex;
1616+
memcpy(phys.hwaddr, addr->smctp_haddr, addr->smctp_halen);
1617+
phys.hwaddr_len = addr->smctp_halen;
1618+
1619+
return add_peer(ctx, &phys, addr->smctp_base.smctp_addr.s_addr,
1620+
addr->smctp_base.smctp_network, ret_peer);
1621+
}
1622+
14721623
static int check_peer_struct(const struct peer *peer, const struct net *n)
14731624
{
14741625
if (n->net != peer->net) {

tests/test_mctpd_endpoint.py

Lines changed: 64 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,60 @@ 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+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, 0x42])))
81+
assert rsp.hex(' ') == '00 01 00 00 42 00'
82+
83+
# get EID, expect receive 42 back
84+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
85+
assert rsp.hex(' ') == '00 02 00 42 02 00'
86+
87+
# set EID = 66
88+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x01, bytes([0x00, 0x66])))
89+
assert rsp.hex(' ') == '00 01 00 00 66 00'
90+
91+
# get EID, expect receive 66 back
92+
rsp = await bo.send_control(mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x02))
93+
assert rsp.hex(' ') == '00 02 00 66 02 00'
94+
95+
# expect previous EID removed on D-Bus
96+
with pytest.raises(asyncdbus.errors.DBusError) as ex:
97+
await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{0x42}")
98+
assert str(ex.value) == f"Unknown object '/au/com/codeconstruct/mctp1/networks/1/endpoints/{0x42}'."
99+
100+
# expect new EID on D-Bus
101+
assert await mctpd_mctp_endpoint_control_obj(dbus, f"/au/com/codeconstruct/mctp1/networks/1/endpoints/{0x66}")

0 commit comments

Comments
 (0)