diff --git a/CHANGELOG.md b/CHANGELOG.md index 07fdc1e..5e6f5ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 3. mctpd: Allow configuring .Connectivity as writable for development 4. mctpd: Add AssignEndpointStatic for static EID allocations 5. mctpd: Add a configuration file facility, defaulting to /etc/mctpd.conf. +6. mctpd: Add mctp/interfaces/ D-Bus object ### Changed diff --git a/conf/mctpd.conf b/conf/mctpd.conf index 82b5546..02fe6c8 100644 --- a/conf/mctpd.conf +++ b/conf/mctpd.conf @@ -1,7 +1,7 @@ -# Mode: either bus-owner or endpoint -mode = "bus-owner" +# Mode: either BusOwner or endpoint +mode = "BusOwner" -# MCTP protocol configuration. Used for both endpoint and bus-owner modes. +# MCTP protocol configuration. Used for both endpoint and BusOwner modes. [mctp] message_timeout_ms = 30 diff --git a/docs/mctpd.md b/docs/mctpd.md index 3cc65da..a82372a 100644 --- a/docs/mctpd.md +++ b/docs/mctpd.md @@ -1,6 +1,6 @@ # `mctpd` -## D-Bus +## D-Bus /xyz/openbmc_project/mctp1//endpoints/ `mctpd` provides a D-Bus service named `au.com.codeconstruct.MCTP1`, and a base object path of `/au/com/codeconstruct/mctp1`. For each known MCTP endpoint, @@ -104,3 +104,11 @@ busctl call au.com.codeconstruct.MCTP1 \ Removes the MCTP endpoint from `mctpd`, and deletes routes and neighbour entries. +## D-Bus /xyz/openbmc_project/mctp1/interfaces/ + +`mctpd` provides a D-Bus path of `/xyz/openbmc_project/mctp1/interfaces`. +For each known MCTP interfaces, `mctpd` will populate an object `/xyz/openbmc_project/mctp1/interfaces/`. The D-Bus objects have interface `au.com.CodeConstruct.MCTP.Link1`. The D-Bus interface includes the `Role` property which reports +BMC roles in the link. The possible value of `Role` are `BusOwner`, `Endpoint` and `Unknown`. +Because the BMC `Role` in the MCTP link is fixed. The `Role` property is changeable value but it can only be changed +when the current configured value is `Unknown`. +The D-Bus `mctp1/interfaces/` objects also includes an au.com.codeconstruct.MCTP.BusOwner1 which exposes bus-owner level functions. \ No newline at end of file diff --git a/src/mctp-netlink.c b/src/mctp-netlink.c index 56f620d..8211e24 100644 --- a/src/mctp-netlink.c +++ b/src/mctp-netlink.c @@ -20,33 +20,6 @@ #include "mctp-util.h" #include "mctp-ops.h" -struct linkmap_entry { - int ifindex; - char ifname[IFNAMSIZ+1]; - int net; - bool up; - - mctp_eid_t *local_eids; - size_t num_local; -}; - -struct mctp_nl { - // socket for queries - int sd; - // socket for monitor - int sd_monitor; - - struct linkmap_entry *linkmap; - size_t linkmap_count; - size_t linkmap_alloc; - bool verbose; - - // allows callers to silence printf of EEXIST returns. - // TODO: this is a workaround, if more are required we should - // rework how error messages are returned to callers. - bool quiet_eexist; -}; - #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) diff --git a/src/mctp-netlink.h b/src/mctp-netlink.h index 31ee6b6..6006582 100644 --- a/src/mctp-netlink.h +++ b/src/mctp-netlink.h @@ -3,12 +3,54 @@ #include #include #include +#include #include #include "mctp.h" -struct mctp_nl; +struct linkmap_entry { + int ifindex; + char ifname[IFNAMSIZ+1]; + int net; + bool up; + bool published; + enum { + MCTP_ENTITY_ROLE_BUS_OWNER, + MCTP_ENTITY_ROLE_ENDPOINT, + MCTP_ENTITY_ROLE_UNKNOWN + } role; + + mctp_eid_t *local_eids; + size_t num_local; +}; + +#define MCTP_ENTITY_ROLE_COUNT 3 +static const char * const role_name[] = { + [MCTP_ENTITY_ROLE_BUS_OWNER] = "BusOwner", + [MCTP_ENTITY_ROLE_ENDPOINT] = "Endpoint", + [MCTP_ENTITY_ROLE_UNKNOWN] = "Unknown" +}; + +struct mctp_nl { + // socket for queries + int sd; + // socket for monitor + int sd_monitor; + + struct linkmap_entry *linkmap; + size_t linkmap_count; + size_t linkmap_alloc; + bool verbose; + bool publish_links; + + // allows callers to silence printf of EEXIST returns. + // TODO: this is a workaround, if more are required we should + // rework how error messages are returned to callers. + bool quiet_eexist; +}; + typedef struct mctp_nl mctp_nl; +typedef struct linkmap_entry linkmap_entry; struct mctp_nl_change { #define MCTP_NL_OP_COUNT 6 diff --git a/src/mctpd.c b/src/mctpd.c index 12b9c3c..add7bcf 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -47,6 +47,9 @@ #define MCTP_DBUS_NAME "au.com.codeconstruct.MCTP1" #define MCTP_DBUS_IFACE_ENDPOINT "xyz.openbmc_project.MCTP.Endpoint" #define OPENBMC_IFACE_COMMON_UUID "xyz.openbmc_project.Common.UUID" +#define CC_MCTP_DBUS_IFACE_LINK "au.com.CodeConstruct.MCTP.Link1" +#define MCTP_DBUS_PATH_LINKS "/au/com/codeconstruct/mctp1/interfaces" +#define MCTP_DBUS_PATH_NETWORKS "/au/com/codeconstruct/mctp1/networks" // an arbitrary constant for use with sd_id128_get_machine_app_specific() static const char* mctpd_appid = "67369c05-4b97-4b7e-be72-65cfd8639f10"; @@ -153,6 +156,9 @@ struct ctx { // Whether we are running as the bus owner bool bus_owner; + // default BMC role in all active MCTP interfaces + size_t default_bmc_role; + // An allocated array of peers, changes address (reallocated) during runtime peer *peers; size_t size_peers; @@ -160,6 +166,9 @@ struct ctx { struct net_det *nets; size_t num_nets; + struct linkmap_entry *linkmap; + size_t linkmap_count; + // Timeout in usecs for a MCTP response uint64_t mctp_timeout; @@ -173,6 +182,8 @@ typedef struct ctx ctx; static int emit_endpoint_added(const peer *peer); static int emit_endpoint_removed(const peer *peer); +static int emit_interface_added(ctx *ctx, int ifindex); +static int emit_interface_removed(ctx *ctx, int ifindex); static int emit_net_added(ctx *ctx, int net); static int emit_net_removed(ctx *ctx, int net); static int query_peer_properties(peer *peer); @@ -189,6 +200,7 @@ static int change_net_interface(ctx *ctx, int ifindex, int old_net); static int add_local_eid(ctx *ctx, int net, int eid); static int del_local_eid(ctx *ctx, int net, int eid); static int add_net(ctx *ctx, int net); +static int add_interface(ctx *ctx, int ifindex); mctp_eid_t local_addr(const ctx *ctx, int ifindex) { mctp_eid_t *eids, ret = 0; @@ -212,6 +224,24 @@ static net_det *lookup_net(ctx *ctx, int net) return NULL; } +static linkmap_entry * find_link_by_ifindex(ctx *ctx, int ifindex) +{ + for (size_t i = 0; i < ctx->linkmap_count; i++) { + if (ctx->linkmap[i].ifindex == ifindex) + return &ctx->linkmap[i]; + } + return NULL; +} + +static linkmap_entry * find_link_by_name(ctx *ctx, const char* ifname) +{ + for (size_t i = 0; i < ctx->linkmap_count; i++) { + if (strcmp(ctx->linkmap[i].ifname, ifname) == 0) + return &ctx->linkmap[i]; + } + return NULL; +} + static bool match_phys(const dest_phys *d1, const dest_phys *d2) { return d1->ifindex == d2->ifindex && d1->hwaddr_len == d2->hwaddr_len && @@ -2159,6 +2189,7 @@ static int publish_peer(peer *peer, bool add_route) /* removes route, neigh, dbus entry for the peer */ static int unpublish_peer(peer *peer) { int rc; + if (peer->have_neigh) { if (peer->ctx->verbose) { fprintf(stderr, "Deleting neigh to %s\n", peer_tostr(peer)); @@ -2707,6 +2738,55 @@ static const sd_bus_vtable testing_vtable[] = { SD_BUS_VTABLE_END }; +static bool is_endpoint_path(const char *path) +{ + char *netstr = NULL, *eidstr = NULL; + uint32_t tmp, net; + int rc; + + rc = sd_bus_path_decode_many(path, + MCTP_DBUS_PATH "/networks/%/endpoints/%", + &netstr, &eidstr); + + if (rc == 0) + return false; + if (rc < 0) + return false; + + dfree(netstr); + dfree(eidstr); + + if (parse_uint32(eidstr, &tmp) < 0 || tmp > 0xff) + return false; + + if (parse_uint32(netstr, &net) < 0) + return false; + + return true; +} + +static bool is_interfaces_path(const char *path) +{ + char *intfName = NULL; + uint32_t tmp, net; + mctp_eid_t eid; + int rc; + + rc = sd_bus_path_decode_many(path, + MCTP_DBUS_PATH "/interfaces/%", + &intfName); + + if (rc == 0) + return false; + + if (rc < 0) + return false; + + dfree(intfName); + + return true; +} + static int bus_endpoint_get_prop(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *berr) @@ -2714,6 +2794,10 @@ static int bus_endpoint_get_prop(sd_bus *bus, peer *peer = userdata; int rc; + if (!is_endpoint_path(path)) { + return -ENOENT; + } + if (strcmp(property, "NetworkId") == 0) { rc = sd_bus_message_append(reply, "u", peer->net); } else if (strcmp(property, "EID") == 0) { @@ -2734,19 +2818,150 @@ static int bus_endpoint_get_prop(sd_bus *bus, return rc; } +static int bus_link_get_prop(sd_bus *bus, + const char *path, const char *interface, const char *property, + sd_bus_message *reply, void *userdata, sd_bus_error *berr) +{ + ctx *ctx = userdata; + char *tmpstr = NULL; + char *link_name = NULL; + linkmap_entry *lm = NULL; + int rc = 0; + + if (!is_interfaces_path(path)) { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Invalid Object Path"); + goto out; + } + + rc = sd_bus_path_decode_many(path, MCTP_DBUS_PATH "/%/%", &tmpstr, + &link_name); + if (rc <= 0) { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Invalid Object Path"); + goto out; + } + + lm = find_link_by_name(ctx, link_name); + if (!lm) { + rc = -ENOENT; + goto out; + } + dfree(tmpstr); + dfree(link_name); + + if (lm->published && strcmp(property, "Role") == 0) { + rc = sd_bus_message_append(reply, "s", role_name[lm->role]); + } else { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Unknown property."); + rc = -ENOENT; + } + +out: + set_berr(ctx, rc, berr); + return rc; +} + +static int bus_link_set_prop(sd_bus *bus, + const char *path, const char *interface, const char *property, + sd_bus_message *value, void *userdata, sd_bus_error *berr) +{ + ctx *ctx = userdata; + const char *state; + char *tmpstr = NULL; + char *link_name = NULL; + linkmap_entry *lm; + bool found = false; + size_t i; + int rc; + + if (!is_interfaces_path(path)) { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Invalid Object Path"); + goto out; + } + + rc = sd_bus_path_decode_many(path, MCTP_DBUS_PATH "/%/%", &tmpstr, + &link_name); + if (rc <= 0) { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Invalid Object Path"); + goto out; + } + + lm = find_link_by_name(ctx, link_name); + if (!lm) { + rc = -ENOENT; + goto out; + } + + dfree(tmpstr); + dfree(link_name); + + + if (strcmp(property, "Role") == 0) { + if (lm->role != MCTP_ENTITY_ROLE_UNKNOWN) + { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Role is already set."); + rc = -ENOENT; + goto out; + } + + rc = sd_bus_message_read(value, "s", &state); + if (rc < 0) { + sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS, + "Unknown Role. Only Support BusOwner/EndPoint."); + goto out; + } + for (i = 0; i < MCTP_ENTITY_ROLE_COUNT; i++) + { + if (strcmp(state, role_name[i]) == 0) + { + found = true; + break; + } + } + if (found) + { + lm->role = i; + } + else + { + printf("Invalid property value '%s' for property '%s' from interface '%s' on object '%s'\n", + state, property, interface, path); + rc = -EINVAL; + goto out; + } + } else { + printf("Unknown property '%s' for %s iface %s\n", property, path, interface); + rc = -ENOENT; + } + +out: + set_berr(ctx, rc, berr); + return rc; +} + __attribute__((unused)) static int bus_endpoint_set_prop(sd_bus *bus, const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *ret_error) + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *ret_error) { const char *connectivity; peer *peer = userdata; ctx *ctx = peer->ctx; int rc; + if (!is_endpoint_path(path)) { + rc = -ENOENT; + goto out; + } + if (strcmp(property, "Connectivity") == 0) { bool previously = peer->degraded; rc = sd_bus_message_read(value, "s", &connectivity); @@ -2807,6 +3022,17 @@ static const sd_bus_vtable bus_endpoint_uuid_vtable[] = { SD_BUS_VTABLE_END }; +static const sd_bus_vtable bus_endpoint_link_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_WRITABLE_PROPERTY("Role", + "s", + bus_link_get_prop, + bus_link_set_prop, + 0, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_VTABLE_END +}; + static const sd_bus_vtable bus_endpoint_cc_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD_WITH_ARGS("SetMTU", @@ -2848,6 +3074,9 @@ static int bus_endpoint_find(sd_bus *bus, const char *path, ctx *ctx = userdata; peer *peer = NULL; int rc; + if (!is_endpoint_path(path)) { + return 0; + } rc = peer_from_path(ctx, path, &peer); if (rc >= 0 && peer->published) { @@ -2865,6 +3094,9 @@ static int bus_endpoint_find_uuid(sd_bus *bus, const char *path, ctx *ctx = userdata; peer *peer = NULL; int rc; + if (!is_endpoint_path(path)) { + return 0; + } rc = peer_from_path(ctx, path, &peer); if (rc >= 0 && peer->published) { @@ -2876,6 +3108,45 @@ static int bus_endpoint_find_uuid(sd_bus *bus, const char *path, return 0; } +/* au.com.CodeConstruct.MCTP.Link1 interface */ +static int bus_mctp_link_find(sd_bus *bus, const char *path, + const char *interface, void *userdata, void **ret_found, + sd_bus_error *ret_error) +{ + ctx *ctx = userdata; + char *tmpstr = NULL; + char *link_name = NULL; + linkmap_entry *lm = NULL; + int rc = 0; + + if (!is_interfaces_path(path)) { + return 0; + } + + rc = sd_bus_path_decode_many(path, MCTP_DBUS_PATH "/%/%", &tmpstr, + &link_name); + if (rc == 0) + return -ENOENT; + if (rc < 0) + return rc; + + lm = find_link_by_name(ctx, link_name); + if (!lm) { + warnx("Linkmap entry of link %s is not existing\n", link_name); + return -ENOMEM; + } + + dfree(tmpstr); + dfree(link_name); + + if (lm->published) { + *ret_found = ctx; + return 1; + } + + return 0; +} + static char* net_path(int net) { size_t l; @@ -2892,6 +3163,35 @@ static char* net_path(int net) return buf; } +static char* root_endpoints_path(int net) +{ + size_t l; + char *buf = NULL; + + l = strlen(MCTP_DBUS_PATH) + 30; + buf = malloc(l); + if (!buf) { + return NULL; + } + snprintf(buf, l, "%s/networks/%d/endpoints", MCTP_DBUS_PATH, net); + return buf; +} + +static char* interface_path(const char* link_name) +{ + size_t l; + char *buf = NULL; + + l = strlen(MCTP_DBUS_PATH) + 30; + buf = malloc(l); + if (!buf) { + return NULL; + } + + snprintf(buf, l, "%s/interfaces/%s", MCTP_DBUS_PATH, link_name); + return buf; +} + static int emit_endpoint_added(const peer *peer) { char *path = NULL; int rc; @@ -2937,6 +3237,30 @@ static int emit_net_added(ctx *ctx, int net) { return rc; } +static int emit_interface_added(ctx *ctx, int ifindex) { + const char* ifname = NULL; + char *path = NULL, *links = NULL; + size_t l; + int rc; + + ifname = mctp_nl_if_byindex(ctx->nl, ifindex); + if (!ifname) { + warnx("BUG %s: no interface for ifindex %d", __func__, ifindex); + return -EPROTO; + } + + path = interface_path(ifname); + if (path == NULL) { + warnx("%s: out of memory", __func__); + return -ENOMEM; + } + rc = sd_bus_emit_object_added(ctx->bus, dfree(path)); + if (rc < 0) + warnx("%s: error emitting, %s", __func__, strerror(-rc)); + + return rc; +} + static int emit_net_removed(ctx *ctx, int net) { char *path = NULL; int rc; @@ -2952,6 +3276,31 @@ static int emit_net_removed(ctx *ctx, int net) { return rc; } +static int emit_interface_removed(ctx *ctx, int ifindex) { + const char* ifname = NULL; + char *path = NULL, *links = NULL; + size_t l; + int rc; + + ifname = mctp_nl_if_byindex(ctx->nl_query, ifindex); + if (!ifname) { + warnx("BUG %s: no interface for ifindex %d", __func__, ifindex); + return -EPROTO; + } + + path = interface_path(ifname); + if (path == NULL) { + warnx("%s: out of memory", __func__); + return -ENOMEM; + } + + rc = sd_bus_emit_object_removed(ctx->bus, dfree(path)); + if (rc < 0) + warnx("%s: error emitting, %s", __func__, strerror(-rc)); + + return rc; +} + static int bus_mctpd_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error) @@ -2973,14 +3322,39 @@ static int mctpd_dbus_enumerate(sd_bus *bus, const char* path, // NULL terminator num_nodes = 1; - // .../mctp object + // .../mctp1 object + num_nodes++; + // .../mctp1/networks object num_nodes++; + // .../mctp1/networks/ + for (i = 0; i < ctx->num_nets; i++) { + num_nodes++; + for (size_t t = 0; t < 256; t++) { + if (ctx->nets[i].peeridx[t] != -1) + { + // .../mctp1/networks//endpoints object + num_nodes++; + break; + } + } + } + + // .../mctp1/networks//endpoints/ object for (i = 0; i < ctx->size_peers; i++) if (ctx->peers[i].published) num_nodes++; - num_nodes += ctx->num_nets; + // .../mctp1/interfaces object + num_nodes++; + + // .../mctp1/interface/ + for (i = 0; i < ctx->linkmap_count; i++) { + linkmap_entry *entry = &ctx->linkmap[i]; + if (entry->published) { + num_nodes++; + } + } nodes = malloc(sizeof(*nodes) * num_nodes); if (!nodes) { @@ -2989,6 +3363,7 @@ static int mctpd_dbus_enumerate(sd_bus *bus, const char* path, } j = 0; + // .../mctp1 nodes[j] = strdup(MCTP_DBUS_PATH); if (!nodes[j]) { rc = -ENOMEM; @@ -2996,6 +3371,38 @@ static int mctpd_dbus_enumerate(sd_bus *bus, const char* path, } j++; + // .../mctp1/networks + nodes[j] = strdup(MCTP_DBUS_PATH_NETWORKS); + if (!nodes[j]) { + rc = -ENOMEM; + goto out; + } + j++; + + for (i = 0; i < ctx->num_nets; i++) { + // .../mctp1/networks/ + nodes[j] = net_path(ctx->nets[i].net); + if (nodes[j] == NULL) { + rc = -ENOMEM; + goto out; + } + j++; + + for (size_t t = 0; t < 256; t++) { + if (ctx->nets[i].peeridx[t] != -1) + { + // .../mctp1/networks//endpoints object + nodes[j] = root_endpoints_path(ctx->nets[i].net); + if (nodes[j] == NULL) { + rc = -ENOMEM; + goto out; + } + j++; + break; + } + } + } + // Peers for (i = 0; i < ctx->size_peers; i++) { peer *peer = &ctx->peers[i]; @@ -3009,9 +3416,21 @@ static int mctpd_dbus_enumerate(sd_bus *bus, const char* path, j++; } - // Nets - for (i = 0; i < ctx->num_nets; i++) { - nodes[j] = net_path(ctx->nets[i].net); + // .../mctp1/interfaces object + nodes[j] = strdup(MCTP_DBUS_PATH_LINKS); + if (!nodes[j]) { + rc = -ENOMEM; + goto out; + } + j++; + + // interfaces + for (i = 0; i < ctx->linkmap_count; i++) { + linkmap_entry *entry = &ctx->linkmap[i]; + if (!entry->published) + continue; + + nodes[j] = interface_path(entry->ifname); if (nodes[j] == NULL) { rc = -ENOMEM; goto out; @@ -3124,6 +3543,17 @@ static int setup_bus(ctx *ctx) goto out; } + rc = sd_bus_add_fallback_vtable(ctx->bus, NULL, + MCTP_DBUS_PATH, + CC_MCTP_DBUS_IFACE_LINK, + bus_endpoint_link_vtable, + bus_mctp_link_find, + ctx); + if (rc < 0) { + warnx("Failed adding link D-Bus interface: %s", strerror(-rc)); + goto out; + } + rc = sd_bus_add_object_manager(ctx->bus, NULL, MCTP_DBUS_PATH); if (rc < 0) { warnx("Adding object manager failed: %s", strerror(-rc)); @@ -3226,6 +3656,32 @@ static int prune_old_nets(ctx *ctx) return 0; } +// Remove nets that have no interfaces +static int del_dbus_link(ctx *ctx) +{ + size_t i, j; + int ifindex; + + // iterate and discard unused nets + for (i = 0, j = 0; i < ctx->linkmap_count; i++) { + bool found = false; + for (size_t n = 0; n < ctx->nl->linkmap_count && !found; n++) + if (ctx->nl->linkmap[n].ifindex == ctx->linkmap[i].ifindex) + found = true; + if (found) { + // isn't stale + if (j != i) + memmove(&ctx->linkmap[j], &ctx->linkmap[i], + sizeof(*ctx->linkmap)); + j++; + } else { + emit_interface_removed(ctx, ctx->linkmap[i].ifindex); + } + } + ctx->linkmap_count = j; + return 0; +} + // Removes remote peers associated with an old interface. // Note that this old_ifindex has already been removed from ctx->nl */ static int del_interface(ctx *ctx, int old_ifindex) @@ -3239,6 +3695,7 @@ static int del_interface(ctx *ctx, int old_ifindex) remove_peer(p); } } + del_dbus_link(ctx); prune_old_nets(ctx); return 0; } @@ -3376,6 +3833,7 @@ static int add_local_eid(ctx *ctx, int net, int eid) static int add_interface_local(ctx *ctx, int ifindex) { mctp_eid_t *eids = NULL; + linkmap_entry *lm = NULL; size_t num; int net; int rc; @@ -3389,6 +3847,13 @@ static int add_interface_local(ctx *ctx, int ifindex) warnx("Warning, interface %s is down", mctp_nl_if_byindex(ctx->nl, ifindex)); + // Add new link if required + lm = find_link_by_ifindex(ctx, ifindex); + if (!lm) { + rc = add_interface(ctx, ifindex); + if (rc < 0) + return rc; + } net = mctp_nl_net_byindex(ctx->nl, ifindex); if (net == 0) { warnx("No net for ifindex %d", ifindex); @@ -3413,6 +3878,7 @@ static int add_interface_local(ctx *ctx, int ifindex) static int add_net(ctx *ctx, int net) { net_det *n, *tmp; + if (lookup_net(ctx, net) != NULL) { warnx("BUG: add_net for existing net %d", net); return -EEXIST; @@ -3436,6 +3902,41 @@ static int add_net(ctx *ctx, int net) return 0; } +static int add_interface(ctx *ctx, int ifindex) +{ + linkmap_entry *m = NULL, *tmp = NULL; + int rc; + + tmp = realloc(ctx->linkmap, + sizeof(linkmap_entry) * (ctx->linkmap_count + 1)); + if (!tmp) { + warnx("Out of memory"); + return -ENOMEM; + } + ctx->linkmap = tmp; + ctx->linkmap_count ++; + + // Initialise the new entry + memcpy(&ctx->linkmap[ctx->linkmap_count-1], + &ctx->nl->linkmap[ctx->linkmap_count-1], sizeof(linkmap_entry)); + + m = find_link_by_ifindex(ctx, ifindex); + if (!m) { + warnx("Can't find link %d\n", ifindex); + return -ENOMEM; + } + /* Use the `mode` setting in conf/mctp.conf */ + m->role = ctx->default_bmc_role; + + rc = emit_interface_added(ctx, ifindex); + if (rc) + { + m->published = true; + } + + return rc; +} + static int setup_nets(ctx *ctx) { size_t num_ifs; @@ -3589,10 +4090,16 @@ static int parse_args(ctx *ctx, int argc, char **argv) static int parse_config_mode(ctx *ctx, const char *mode) { - if (!strcmp(mode, "bus-owner")) { + if (!strcmp(mode, role_name[MCTP_ENTITY_ROLE_BUS_OWNER])) { ctx->bus_owner = true; - } else if (!strcmp(mode, "endpoint")) { + ctx->default_bmc_role = MCTP_ENTITY_ROLE_BUS_OWNER; + } else if (!strcmp(mode, role_name[MCTP_ENTITY_ROLE_ENDPOINT]) || + !strcmp(mode, role_name[MCTP_ENTITY_ROLE_UNKNOWN])) { ctx->bus_owner = false; + if (!strcmp(mode, role_name[MCTP_ENTITY_ROLE_ENDPOINT])) + ctx->default_bmc_role = MCTP_ENTITY_ROLE_ENDPOINT; + else + ctx->default_bmc_role = MCTP_ENTITY_ROLE_UNKNOWN; } else { warnx("invalid value '%s' for mode configuration", mode); return -1;