diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2022-05-28 20:08:30 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2022-05-28 20:08:30 +0000 |
commit | ff0694bbdd7222a9daf47ef45ffaad70e290463c (patch) | |
tree | 020234a1743c05524918ac7ec91cfd3171aba0e3 | |
parent | 2facdf288df59690b62037cb91df472b2215e975 (diff) | |
parent | ca73d965d73a930ab366a61e96e1af181ff45594 (diff) | |
download | slirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.zip slirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.tar.gz slirp-ff0694bbdd7222a9daf47ef45ffaad70e290463c.tar.bz2 |
Merge branch 'master' into 'master'
ncsi: Add Mellanox Get MAC Address handler
See merge request slirp/libslirp!125
-rw-r--r-- | meson.build | 8 | ||||
-rw-r--r-- | src/libslirp.h | 5 | ||||
-rw-r--r-- | src/ncsi-pkt.h | 50 | ||||
-rw-r--r-- | src/ncsi.c | 136 | ||||
-rw-r--r-- | src/slirp.c | 2 | ||||
-rw-r--r-- | src/slirp.h | 1 | ||||
-rw-r--r-- | test/ncsitest.c | 174 |
7 files changed, 367 insertions, 9 deletions
diff --git a/meson.build b/meson.build index 5fe33d3..435f331 100644 --- a/meson.build +++ b/meson.build @@ -151,6 +151,14 @@ pingtest = executable('pingtest', 'test/pingtest.c', test('ping', pingtest) +ncsitest = executable('ncsitest', 'test/ncsitest.c', + link_with: [lib], + include_directories: ['src'], + dependencies: [glib_dep, platform_deps] +) + +test('ncsi', ncsitest) + if install_devel install_headers(['src/libslirp.h'], subdir : 'slirp') diff --git a/src/libslirp.h b/src/libslirp.h index 35ddab6..3afad21 100644 --- a/src/libslirp.h +++ b/src/libslirp.h @@ -144,6 +144,11 @@ typedef struct SlirpConfig { * Fields introduced in SlirpConfig version 5 begin */ uint32_t mfr_id; /* Manufacturer ID (IANA Private Enterprise number) */ + /* + * MAC address allocated for an out-of-band management controller, to be + * retrieved through NC-SI. + */ + uint8_t oob_eth_addr[6]; } SlirpConfig; /* Create a new instance of a slirp stack */ diff --git a/src/ncsi-pkt.h b/src/ncsi-pkt.h index 39cf844..9dd167c 100644 --- a/src/ncsi-pkt.h +++ b/src/ncsi-pkt.h @@ -181,6 +181,29 @@ struct ncsi_cmd_snfc_pkt { unsigned char pad[22]; } SLIRP_PACKED; +/* OEM Request Command as per NCSI Specification */ +struct ncsi_cmd_oem_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* OEM Payload Data */ +} SLIRP_PACKED; + +/* OEM Response Packet as per NCSI Specification */ +struct ncsi_rsp_oem_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* Payload data */ +} SLIRP_PACKED; + +/* Mellanox Response Data */ +struct ncsi_rsp_oem_mlx_pkt { + unsigned char cmd_rev; /* Command Revision */ + unsigned char cmd; /* Command ID */ + unsigned char param; /* Parameter */ + unsigned char optional; /* Optional data */ + unsigned char data[]; /* Data */ +} SLIRP_PACKED; + /* Get Link Status */ struct ncsi_rsp_gls_pkt { struct ncsi_rsp_pkt_hdr rsp; /* Response header */ @@ -442,4 +465,31 @@ struct ncsi_aen_hncdsc_pkt { #define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ #define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ +/* OEM Vendor Manufacture ID */ +#define NCSI_OEM_MFR_MLX_ID 0x8119 +#define NCSI_OEM_MFR_BCM_ID 0x113d +#define NCSI_OEM_MFR_INTEL_ID 0x157 +/* Intel specific OEM command */ +#define NCSI_OEM_INTEL_CMD_GMA 0x06 /* CMD ID for Get MAC */ +#define NCSI_OEM_INTEL_CMD_KEEP_PHY 0x20 /* CMD ID for Keep PHY up */ +/* Broadcom specific OEM Command */ +#define NCSI_OEM_BCM_CMD_GMA 0x01 /* CMD ID for Get MAC */ +/* Mellanox specific OEM Command */ +#define NCSI_OEM_MLX_CMD_GMA 0x00 /* CMD ID for Get MAC */ +#define NCSI_OEM_MLX_CMD_GMA_PARAM 0x1b /* Parameter for GMA */ +#define NCSI_OEM_MLX_CMD_SMAF 0x01 /* CMD ID for Set MC Affinity */ +#define NCSI_OEM_MLX_CMD_SMAF_PARAM 0x07 /* Parameter for SMAF */ +/* Offset in OEM request */ +#define MLX_SMAF_MAC_ADDR_OFFSET 8 /* Offset for MAC in SMAF */ +#define MLX_SMAF_MED_SUPPORT_OFFSET 14 /* Offset for medium in SMAF */ +/* Mac address offset in OEM response */ +#define BCM_MAC_ADDR_OFFSET 28 +#define MLX_MAC_ADDR_OFFSET 8 +#define INTEL_MAC_ADDR_OFFSET 1 + +/* Status offset in OEM response */ +#define MLX_GMA_STATUS_OFFSET 0 +/* OEM Response payload length */ +#define MLX_GMA_PAYLOAD_LEN 24 + #endif /* NCSI_PKT_H */ @@ -55,8 +55,123 @@ static uint32_t ncsi_calculate_checksum(uint8_t *data, int len) return checksum; } +/* Response handler for Mellanox command Get Mac Address */ +static int ncsi_rsp_handler_oem_mlx_gma(Slirp *slirp, + const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + uint8_t oob_eth_addr_allocated = 0; + struct ncsi_rsp_oem_pkt *rsp; + int i; + + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + + /* Set the payload length */ + rsp->rsp.common.length = htons(MLX_GMA_PAYLOAD_LEN); + + for (i = 0; i < ETH_ALEN; i++) { + if (slirp->oob_eth_addr[i] != 0x00) { + oob_eth_addr_allocated = 1; + break; + } + } + rsp->data[MLX_GMA_STATUS_OFFSET] = oob_eth_addr_allocated; + + /* Set the allocated management address */ + memcpy(&rsp->data[MLX_MAC_ADDR_OFFSET], slirp->oob_eth_addr, ETH_ALEN); + + return 0; +} + +/* Response handler for Mellanox card */ +static int ncsi_rsp_handler_oem_mlx(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_cmd_oem_pkt *cmd; + const struct ncsi_rsp_oem_mlx_pkt *cmd_mlx; + struct ncsi_rsp_oem_pkt *rsp; + struct ncsi_rsp_oem_mlx_pkt *rsp_mlx; + + /* Get the command header */ + cmd = (const struct ncsi_cmd_oem_pkt *)nh; + cmd_mlx = (const struct ncsi_rsp_oem_mlx_pkt *)cmd->data; + + /* Get the response header */ + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + rsp_mlx = (struct ncsi_rsp_oem_mlx_pkt *)rsp->data; + + /* Ensure the OEM response header matches the command's */ + rsp_mlx->cmd_rev = cmd_mlx->cmd_rev; + rsp_mlx->cmd = cmd_mlx->cmd; + rsp_mlx->param = cmd_mlx->param; + rsp_mlx->optional = cmd_mlx->optional; + + if (cmd_mlx->cmd == NCSI_OEM_MLX_CMD_GMA && + cmd_mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM) + return ncsi_rsp_handler_oem_mlx_gma(slirp, nh, rnh); + + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + +static const struct ncsi_rsp_oem_handler { + unsigned int mfr_id; + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); +} ncsi_rsp_oem_handlers[] = { + { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx }, + { NCSI_OEM_MFR_BCM_ID, NULL }, + { NCSI_OEM_MFR_INTEL_ID, NULL }, +}; + +/* Response handler for OEM command */ +static int ncsi_rsp_handler_oem(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_rsp_oem_handler *nrh = NULL; + const struct ncsi_cmd_oem_pkt *cmd = (const struct ncsi_cmd_oem_pkt *)nh; + struct ncsi_rsp_oem_pkt *rsp = (struct ncsi_rsp_oem_pkt *)rnh; + uint32_t mfr_id = ntohl(cmd->mfr_id); + int i; + + rsp->mfr_id = cmd->mfr_id; + + if (mfr_id != slirp->mfr_id) { + goto error; + } + + /* Check for manufacturer id and Find the handler */ + for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_oem_handlers); i++) { + if (ncsi_rsp_oem_handlers[i].mfr_id == mfr_id) { + if (ncsi_rsp_oem_handlers[i].handler) + nrh = &ncsi_rsp_oem_handlers[i]; + else + nrh = NULL; + + break; + } + } + + if (!nrh) { + goto error; + } + + /* Process the packet */ + return nrh->handler(slirp, nh, rnh); + +error: + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + + /* Get Version ID */ -static int ncsi_rsp_handler_gvi(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gvi(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gvi_pkt *rsp = (struct ncsi_rsp_gvi_pkt *)rnh; @@ -67,7 +182,8 @@ static int ncsi_rsp_handler_gvi(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Capabilities */ -static int ncsi_rsp_handler_gc(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gc(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gc_pkt *rsp = (struct ncsi_rsp_gc_pkt *)rnh; @@ -82,7 +198,8 @@ static int ncsi_rsp_handler_gc(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Link status */ -static int ncsi_rsp_handler_gls(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gls(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gls_pkt *rsp = (struct ncsi_rsp_gls_pkt *)rnh; @@ -91,7 +208,8 @@ static int ncsi_rsp_handler_gls(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) } /* Get Parameters */ -static int ncsi_rsp_handler_gp(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) +static int ncsi_rsp_handler_gp(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) { struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *)rnh; @@ -107,7 +225,8 @@ static int ncsi_rsp_handler_gp(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh) static const struct ncsi_rsp_handler { unsigned char type; int payload; - int (*handler)(Slirp *slirp, struct ncsi_rsp_pkt_hdr *rnh); + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); } ncsi_rsp_handlers[] = { { NCSI_PKT_RSP_CIS, 4, NULL }, { NCSI_PKT_RSP_SP, 4, NULL }, { NCSI_PKT_RSP_DP, 4, NULL }, @@ -135,7 +254,7 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_GNS, 172, NULL }, { NCSI_PKT_RSP_GNPTS, 172, NULL }, { NCSI_PKT_RSP_GPS, 8, NULL }, - { NCSI_PKT_RSP_OEM, 0, NULL }, + { NCSI_PKT_RSP_OEM, 0, ncsi_rsp_handler_oem }, { NCSI_PKT_RSP_PLDM, 0, NULL }, { NCSI_PKT_RSP_GPUUID, 20, NULL } }; @@ -188,10 +307,9 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) rnh->reason = htons(NCSI_PKT_RSP_R_NO_ERROR); if (handler->handler) { - /* TODO: handle errors */ - handler->handler(slirp, rnh); + handler->handler(slirp, nh, rnh); } - ncsi_rsp_len += handler->payload; + ncsi_rsp_len += ntohs(rnh->common.length); } else { rnh->common.length = 0; rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); diff --git a/src/slirp.c b/src/slirp.c index 588cada..54056c9 100644 --- a/src/slirp.c +++ b/src/slirp.c @@ -650,8 +650,10 @@ Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque) if (cfg->version >= 5) { slirp->mfr_id = cfg->mfr_id; + memcpy(slirp->oob_eth_addr, cfg->oob_eth_addr, ETH_ALEN); } else { slirp->mfr_id = 0; + memset(slirp->oob_eth_addr, 0, ETH_ALEN); } ip6_post_init(slirp); diff --git a/src/slirp.h b/src/slirp.h index 4c956d9..e413867 100644 --- a/src/slirp.h +++ b/src/slirp.h @@ -152,6 +152,7 @@ struct Slirp { bool disable_host_loopback; uint32_t mfr_id; + uint8_t oob_eth_addr[ETH_ALEN]; /* mbuf states */ struct slirp_quehead m_freelist; diff --git a/test/ncsitest.c b/test/ncsitest.c new file mode 100644 index 0000000..c763af2 --- /dev/null +++ b/test/ncsitest.c @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + */ + +/* + * This test verifies slirp responses to NC-SI commands. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "slirp.h" +#include "ncsi-pkt.h" + +#define NCSI_RESPONSE_CAPACITY 1024 + +static void test_ncsi_get_version_id(Slirp *slirp) +{ + slirp->mfr_id = 0xabcdef01; + + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x15, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x00, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + }; + slirp_input(slirp, command, sizeof(command)); + + const struct ncsi_rsp_gvi_pkt *gvi = slirp->opaque + ETH_HLEN; + + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(gvi->mf_id) == slirp->mfr_id); + + slirp->mfr_id = 0; +} + +static void test_ncsi_oem_mlx_unsupported_command(Slirp *slirp) +{ + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0xff, /* Command Revision */ + 0xff, /* Command ID */ + 0x00, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = slirp->opaque + ETH_HLEN; + + slirp->mfr_id = 0x00000000; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); + + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); +} + +static void test_ncsi_oem_mlx_gma(Slirp *slirp) +{ + uint8_t oob_eth_addr[ETH_ALEN] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}; + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0x00, /* Command Revision */ + 0x00, /* Command ID */ + 0x1b, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = slirp->opaque + ETH_HLEN; + + memset(slirp->oob_eth_addr, 0, ETH_ALEN); + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(slirp->oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 0); + + memcpy(slirp->oob_eth_addr, oob_eth_addr, ETH_ALEN); + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 1); +} + +static ssize_t send_packet(const void *buf, size_t len, void *opaque) +{ + assert(len <= NCSI_RESPONSE_CAPACITY); + memcpy(opaque, buf, len); + return len; +} + +int main(int argc, char *argv[]) +{ + SlirpConfig config = {}; + SlirpCb callbacks = {}; + Slirp *slirp = NULL; + uint8_t ncsi_response[NCSI_RESPONSE_CAPACITY]; + + config.version = SLIRP_CONFIG_VERSION_MAX; + callbacks.send_packet = send_packet; + slirp = slirp_new(&config, &callbacks, ncsi_response); + + test_ncsi_get_version_id(slirp); + test_ncsi_oem_mlx_unsupported_command(slirp); + test_ncsi_oem_mlx_gma(slirp); + + slirp_cleanup(slirp); +} |