[PATCH 2/2] hw/ipmi/ipmi_bmc_sim: Support setting fake LAN channel config

Yunpeng Yang posted 2 patches 2 weeks, 3 days ago
Maintainers: Corey Minyard <minyard@acm.org>, Fabiano Rosas <farosas@suse.de>, Laurent Vivier <lvivier@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>
[PATCH 2/2] hw/ipmi/ipmi_bmc_sim: Support setting fake LAN channel config
Posted by Yunpeng Yang 2 weeks, 3 days ago
The "Set LAN Configuration Parameters" IPMI command is added to the
`ipmi_bmc_sim` device to support dynamically setting fake LAN channel
configurations. With the fake LAN channel enabled, inside the guest OS,
tools such as `ipmitool` can be used to modify the configurations.

Signed-off-by: Yunpeng Yang <yunpeng.yang@nutanix.com>
---
 hw/ipmi/ipmi_bmc_sim.c      | 110 ++++++++++++++++++++++++++++++++++++
 tests/qtest/ipmi-kcs-test.c |  83 +++++++++++++++++++++++++++
 2 files changed, 193 insertions(+)

diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c
index 2ead46ee55..f4cea91fde 100644
--- a/hw/ipmi/ipmi_bmc_sim.c
+++ b/hw/ipmi/ipmi_bmc_sim.c
@@ -106,6 +106,7 @@
 
 #define IPMI_NETFN_TRANSPORT          0x0c
 
+#define IPMI_CMD_SET_LAN_CONFIG           0x01
 #define IPMI_CMD_GET_LAN_CONFIG           0x02
 
 
@@ -300,6 +301,7 @@ struct IPMIBmcSim {
     ((ibs)->lan.channel != 0 && (ibs)->lan.channel == (c))
 
 #define IPMI_BMC_LAN_CFG_CC_PARAM_NOT_SUPPORTED    0x80
+#define IPMI_BMC_LAN_CFG_CC_PARAM_READONLY         0x82
 
 #define IPMI_BMC_LAN_CFG_PARAM_SET_IN_PROGRESS        0x00
 #define IPMI_BMC_LAN_CFG_PARAM_AUTH_TYPE_SUPPORT      0x01
@@ -2131,6 +2133,113 @@ static inline bool is_valid_netmask(const uint8_t *netmask)
     return mask != 0 && (inverted & (inverted + 1)) == 0;
 }
 
+/*
+ * Request data (from cmd[2] on):
+ * bytes   meaning
+ *    1    [bits 3:0] channel number
+ *    2    parameter selector
+ * [3:N]   configuration parameter data (from cmd[4] on)
+ */
+static void set_lan_config(IPMIBmcSim *ibs,
+                           uint8_t *cmd, unsigned int cmd_len,
+                           RspBuffer *rsp)
+{
+    uint8_t channel;
+    uint8_t *param;  /* pointer to configuration parameter data */
+    unsigned int param_len;
+
+    if (ibs->lan.channel == 0) {
+        /* LAN channel disabled. Fail as if this command were not defined. */
+        rsp_buffer_set_error(rsp, IPMI_CC_INVALID_CMD);
+        return;
+    }
+    if (cmd_len < 5) {
+        rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+        return;
+    }
+    channel = cmd[2] & 0xf;
+    param = cmd + 4;
+    param_len = cmd_len - 4;
+
+    if (!IPMI_BMC_CHANNEL_IS_LAN(ibs, channel)) {
+        rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+        return;
+    }
+
+    switch (cmd[3]) {
+    case IPMI_BMC_LAN_CFG_PARAM_IP_ADDR:
+        if (param_len < NBYTES_IP) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        memcpy(ibs->lan.ipaddr, param, NBYTES_IP);
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_IP_ADDR_SOURCE:
+        if (param_len < 1) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        if (!IPMI_BMC_LAN_CFG_IS_VALID_IP_SOURCE(*param)) {
+            rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+            return;
+        }
+        ibs->lan.ipsrc = *param;
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_MAC_ADDR:
+        if (param_len < NBYTES_MAC) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        memcpy(ibs->lan.macaddr.a, param, NBYTES_MAC);
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_SUBNET_MASK:
+        if (param_len < NBYTES_IP) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        if (!is_valid_netmask(param)) {
+            rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+            return;
+        }
+        memcpy(ibs->lan.netmask, param, NBYTES_IP);
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_DEFAULT_GW_IP_ADDR:
+        if (param_len < NBYTES_IP) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        memcpy(ibs->lan.defgw_ipaddr, param, NBYTES_IP);
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_DEFAULT_GW_MAC_ADDR:
+        if (param_len < NBYTES_MAC) {
+            rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_LENGTH_INVALID);
+            return;
+        }
+        memcpy(ibs->lan.defgw_macaddr.a, param, NBYTES_MAC);
+        break;
+
+    case IPMI_BMC_LAN_CFG_PARAM_SET_IN_PROGRESS:
+    case IPMI_BMC_LAN_CFG_PARAM_AUTH_TYPE_SUPPORT:
+    case IPMI_BMC_LAN_CFG_PARAM_AUTH_TYPE_ENABLES:
+    case IPMI_BMC_LAN_CFG_PARAM_IPV4_HDR_PARAMS:
+    case IPMI_BMC_LAN_CFG_PARAM_BACKUP_GW_ADDR:
+    case IPMI_BMC_LAN_CFG_PARAM_BACKUP_GW_MAC_ADDR:
+    case IPMI_BMC_LAN_CFG_PARAM_COMMUNITY_STRING:
+    case IPMI_BMC_LAN_CFG_PARAM_NUM_DESTINATIONS:
+        rsp_buffer_set_error(rsp, IPMI_BMC_LAN_CFG_CC_PARAM_READONLY);
+        return;
+
+    default:
+        rsp_buffer_set_error(rsp, IPMI_BMC_LAN_CFG_CC_PARAM_NOT_SUPPORTED);
+        return;
+    }
+}
+
 /*
  * Request data (from cmd[2] to cmd[5] inclusive):
  * bytes   meaning
@@ -2329,6 +2438,7 @@ static const IPMINetfn storage_netfn = {
 };
 
 static const IPMICmdHandler transport_cmds[] = {
+    [IPMI_CMD_SET_LAN_CONFIG] = { set_lan_config },
     [IPMI_CMD_GET_LAN_CONFIG] = { get_lan_config },
 };
 static const IPMINetfn transport_netfn = {
diff --git a/tests/qtest/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c
index d0a207477e..9bab0d84ad 100644
--- a/tests/qtest/ipmi-kcs-test.c
+++ b/tests/qtest/ipmi-kcs-test.c
@@ -318,6 +318,88 @@ static void test_kcs_lan_get(void)
 }
 
 
+/* set/get ip address: 192.0.2.2 */
+static uint8_t lan_set_ipaddr_cmd[] = { 0x30, 0x01, 0x01, 0x03,
+                                        0xc0, 0x00, 0x02, 0x02 };
+static uint8_t lan_set_ipaddr_rsp[] = { 0x34, 0x01, 0x00 };
+static uint8_t lan_get_ipaddr_cmd[] = { 0x30, 0x02, 0x01, 0x03, 0x00, 0x00 };
+static uint8_t lan_get_ipaddr_rsp[] = { 0x34, 0x02, 0x00, 0x11,
+                                        0xc0, 0x00, 0x02, 0x02 };
+/* set ip address source: static */
+static uint8_t lan_set_ipsrc_cmd[] = { 0x30, 0x01, 0x01, 0x04, 0x01 };
+static uint8_t lan_set_ipsrc_rsp[] = { 0x34, 0x01, 0x00 };
+
+/* set/get subnet mask: 255.255.255.0 */
+static uint8_t lan_set_netmask_cmd[] = { 0x30, 0x01, 0x01, 0x06,
+                                         0xff, 0xff, 0xff, 0x00 };
+static uint8_t lan_set_netmask_rsp[] = { 0x34, 0x01, 0x00 };
+static uint8_t lan_get_netmask_cmd[] = { 0x30, 0x02, 0x01, 0x06, 0x00, 0x00 };
+static uint8_t lan_get_netmask_rsp[] = { 0x34, 0x02, 0x00, 0x11,
+                                         0xff, 0xff, 0xff, 0x00 };
+
+/* set/get default gateway ip address: 192.0.2.1 */
+static uint8_t lan_set_defgw_ipaddr_cmd[] = { 0x30, 0x01, 0x01, 0x0c,
+                                              0xc0, 0x00, 0x02, 0x01 };
+static uint8_t lan_set_defgw_ipaddr_rsp[] = { 0x34, 0x01, 0x00 };
+static uint8_t lan_get_defgw_ipaddr_cmd[] = { 0x30, 0x02, 0x01, 0x0c,
+                                              0x00, 0x00 };
+static uint8_t lan_get_defgw_ipaddr_rsp[] = { 0x34, 0x02, 0x00, 0x11,
+                                              0xc0, 0x00, 0x02, 0x01 };
+
+/*
+ * Set and then get LAN configurations
+ */
+static void test_kcs_lan_set_get(void)
+{
+    uint8_t rsp[20];
+    unsigned int rsplen = 0;
+
+    /* set ip address */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_set_ipaddr_cmd, sizeof(lan_set_ipaddr_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_set_ipaddr_rsp));
+    g_assert(memcmp(lan_set_ipaddr_rsp, rsp, rsplen) == 0);
+
+    /* get ip address */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_get_ipaddr_cmd, sizeof(lan_get_ipaddr_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_get_ipaddr_rsp));
+    g_assert(memcmp(lan_get_ipaddr_rsp, rsp, rsplen) == 0);
+
+    /* set ip address source */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_set_ipsrc_cmd, sizeof(lan_set_ipsrc_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_set_ipsrc_rsp));
+    g_assert(memcmp(lan_set_ipsrc_rsp, rsp, rsplen) == 0);
+
+    /* set subnet mask */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_set_netmask_cmd, sizeof(lan_set_netmask_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_set_netmask_rsp));
+    g_assert(memcmp(lan_set_netmask_rsp, rsp, rsplen) == 0);
+
+    /* get subnet mask */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_get_netmask_cmd, sizeof(lan_get_netmask_cmd), rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_get_netmask_rsp));
+    g_assert(memcmp(lan_get_netmask_rsp, rsp, rsplen) == 0);
+
+    /* set default gateway ip address */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_set_defgw_ipaddr_cmd, sizeof(lan_set_defgw_ipaddr_cmd),
+            rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_set_defgw_ipaddr_rsp));
+    g_assert(memcmp(lan_set_defgw_ipaddr_rsp, rsp, rsplen) == 0);
+
+    /* get default gateway ip address */
+    rsplen = sizeof(rsp);
+    kcs_cmd(lan_get_defgw_ipaddr_cmd, sizeof(lan_get_defgw_ipaddr_cmd),
+            rsp, &rsplen);
+    g_assert(rsplen == sizeof(lan_get_defgw_ipaddr_rsp));
+    g_assert(memcmp(lan_get_defgw_ipaddr_rsp, rsp, rsplen) == 0);
+}
+
+
 int main(int argc, char **argv)
 {
     char *cmdline;
@@ -340,6 +422,7 @@ int main(int argc, char **argv)
     qtest_add_func("/ipmi/local/kcs_channel_access", test_kcs_channel_access);
     qtest_add_func("/ipmi/local/kcs_channel_info", test_kcs_channel_info);
     qtest_add_func("/ipmi/local/kcs_lan_get", test_kcs_lan_get);
+    qtest_add_func("/ipmi/local/kcs_lan_set_get", test_kcs_lan_set_get);
     ret = g_test_run();
     qtest_quit(global_qtest);
 
-- 
2.43.7