From e30c357db78b977017ae88a5953c8071142848a7 Mon Sep 17 00:00:00 2001 From: Dmitry Rakhchev Date: Mon, 4 Jul 2016 18:14:42 +0300 Subject: [PATCH] ID:375 - Add lan6 subcommand to handle IPv6 LAN parameters --- include/ipmitool/Makefile.am | 4 +- include/ipmitool/ipmi_cfgp.h | 197 ++++++ include/ipmitool/ipmi_lanp.h | 31 + include/ipmitool/ipmi_lanp6.h | 54 ++ lib/Makefile.am | 1 + lib/ipmi_cfgp.c | 542 ++++++++++++++ lib/ipmi_lanp6.c | 1240 +++++++++++++++++++++++++++++++++ src/ipmitool.c | 2 + 8 files changed, 2069 insertions(+), 2 deletions(-) create mode 100644 include/ipmitool/ipmi_cfgp.h create mode 100644 include/ipmitool/ipmi_lanp6.h create mode 100644 lib/ipmi_cfgp.c create mode 100644 lib/ipmi_lanp6.c diff --git a/include/ipmitool/Makefile.am b/include/ipmitool/Makefile.am index 160e354..9093a56 100644 --- a/include/ipmitool/Makefile.am +++ b/include/ipmitool/Makefile.am @@ -38,5 +38,5 @@ noinst_HEADERS = log.h bswap.h hpm2.h helper.h ipmi.h ipmi_cc.h ipmi_intf.h \ ipmi_oem.h ipmi_sdradd.h ipmi_isol.h ipmi_sunoem.h ipmi_picmg.h \ ipmi_fwum.h ipmi_main.h ipmi_tsol.h ipmi_firewall.h \ ipmi_kontronoem.h ipmi_ekanalyzer.h ipmi_gendev.h ipmi_ime.h \ - ipmi_delloem.h ipmi_dcmi.h ipmi_vita.h ipmi_sel_supermicro.h - + ipmi_delloem.h ipmi_dcmi.h ipmi_vita.h ipmi_sel_supermicro.h \ + ipmi_cfgp.h ipmi_lanp6.h diff --git a/include/ipmitool/ipmi_cfgp.h b/include/ipmitool/ipmi_cfgp.h new file mode 100644 index 0000000..ab8acb3 --- /dev/null +++ b/include/ipmitool/ipmi_cfgp.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2016 Pentair Technical Products. All right reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pentair Technical Products or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * PENTAIR TECHNICAL SOLUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IPMI_CFGP_H +#define IPMI_CFGP_H + +#include + +/* Forward declarations. */ +struct ipmi_cfgp; +struct ipmi_cfgp_ctx; + +/* + * Action types. + */ +enum { + /* parse dumped parameter data */ + CFGP_PARSE, + /* get parameter from BMC */ + CFGP_GET, + /* set parameter to BMC */ + CFGP_SET, + /* output parameter data in form that can be parsed back */ + CFGP_SAVE, + /* print parameter in user-friendly format */ + CFGP_PRINT +}; + +/* + * Action-specific information. + */ +struct ipmi_cfgp_action { + /* Action type. */ + int type; + + /* Set selector. */ + int set; + + /* Block selector. */ + int block; + + /* No error output needed. */ + int quiet; + + /* Number of command line arguments (only for parse action). */ + int argc; + + /* Command line arguments (only for parse action). */ + const char **argv; + + /* Output file (only for dump/print actions). */ + FILE *file; +}; + +/* + * Access types. + */ +enum { + CFGP_RDWR, + CFGP_RDONLY, + CFGP_WRONLY, + CFGP_RESERVED +}; + +/* + * Configuration parameter descriptor. + */ +struct ipmi_cfgp { + /* Parameter name. */ + const char *name; + + /* Parameter format description. */ + const char *format; + + /* Various parameter traits. */ + unsigned int size; /* block size */ + unsigned int access:2; /* read-write/read-only/write-only */ + unsigned int is_set:1; /* takes non-zero set selectors */ + unsigned int first_set:1; /* 1 = 1-based set selector */ + unsigned int has_blocks:1; /* takes non-zero block selectors */ + unsigned int first_block:1; /* 1 = 1-based block selector */ + + /* Parameter-specific data. */ + int specific; +}; + +/* Parameter callback. */ +typedef int (*ipmi_cfgp_handler_t)(void *priv, + const struct ipmi_cfgp *p, const struct ipmi_cfgp_action *action, + unsigned char *data); + +/* + * Parameter selector. + */ +struct ipmi_cfgp_sel { + int param; + int set; + int block; +}; + +/* + * Configuration parameter data. + */ +struct ipmi_cfgp_data { + struct ipmi_cfgp_data *next; + struct ipmi_cfgp_sel sel; + unsigned char data[]; +}; + +/* + * Configuration parameter operation context. + */ +struct ipmi_cfgp_ctx { + /* Set of parameters. */ + const struct ipmi_cfgp *set; + + /* Descriptor count. */ + int count; + + /* Parameter action handler. */ + ipmi_cfgp_handler_t handler; + + /* ipmitool cmd name */ + const char *cmdname; + + /* List of parameter values. */ + struct ipmi_cfgp_data *v; + + /* Private data. */ + void *priv; +}; + +/* Initialize configuration context. */ +extern int ipmi_cfgp_init(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp *set, unsigned int count, + const char *cmdname, + ipmi_cfgp_handler_t handler, void *priv); + +/* Uninitialize context, free allocated memory. */ +extern int ipmi_cfgp_uninit(struct ipmi_cfgp_ctx *ctx); + +/* Print parameter usage. */ +void ipmi_cfgp_usage(const struct ipmi_cfgp *set, int count, int write); + +/* Parse parameter selector from command line. */ +extern int ipmi_cfgp_parse_sel(struct ipmi_cfgp_ctx *ctx, + int argc, const char **argv, struct ipmi_cfgp_sel *sel); + +/* Parse parameter data from command line. */ +extern int ipmi_cfgp_parse_data(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, int argc, const char **argv); + +/* Get parameter data from BMC. */ +extern int ipmi_cfgp_get(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel); + +/* Set parameter data to BMC. */ +extern int ipmi_cfgp_set(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel); + +/* Write parameter data to file. */ +extern int ipmi_cfgp_save(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, FILE *file); + +/* Print parameter data in user-friendly format. */ +extern int ipmi_cfgp_print(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, FILE *file); + +#endif /* IPMI_CFGP_H */ diff --git a/include/ipmitool/ipmi_lanp.h b/include/ipmitool/ipmi_lanp.h index 0972544..0ef2d96 100644 --- a/include/ipmitool/ipmi_lanp.h +++ b/include/ipmitool/ipmi_lanp.h @@ -78,6 +78,37 @@ enum { IPMI_LANP_RMCP_PRIV_LEVELS, IPMI_LANP_VLAN_TAGS, IPMI_LANP_BAD_PASS_THRESH, + IPMI_LANP_IP6_SUPPORT=50, + IPMI_LANP_IP6_ENABLES, + IPMI_LANP_IP6_TRAFFIC_CLASS, + IPMI_LANP_IP6_STATIC_HOPS, + IPMI_LANP_IP6_FLOW_LABEL, + IPMI_LANP_IP6_STATUS, + IPMI_LANP_IP6_STATIC_ADDR, + IPMI_LANP_IP6_STATIC_DUID_STG, + IPMI_LANP_IP6_STATIC_DUID, + IPMI_LANP_IP6_DYNAMIC_ADDR, + IPMI_LANP_IP6_DYNAMIC_DUID_STG, + IPMI_LANP_IP6_DYNAMIC_DUID, + IPMI_LANP_IP6_DHCP6_CFG_SUP, + IPMI_LANP_IP6_DHCP6_CFG, + IPMI_LANP_IP6_ROUTER_CFG, + IPMI_LANP_IP6_STATIC_RTR1_ADDR, + IPMI_LANP_IP6_STATIC_RTR1_MAC, + IPMI_LANP_IP6_STATIC_RTR1_PFX_LEN, + IPMI_LANP_IP6_STATIC_RTR1_PFX, + IPMI_LANP_IP6_STATIC_RTR2_ADDR, + IPMI_LANP_IP6_STATIC_RTR2_MAC, + IPMI_LANP_IP6_STATIC_RTR2_PFX_LEN, + IPMI_LANP_IP6_STATIC_RTR2_PFX, + IPMI_LANP_IP6_NUM_DYNAMIC_RTRS, + IPMI_LANP_IP6_DYNAMIC_RTR_ADDR, + IPMI_LANP_IP6_DYNAMIC_RTR_MAC, + IPMI_LANP_IP6_DYNAMIC_RTR_PFX_LEN, + IPMI_LANP_IP6_DYNAMIC_RTR_PFX, + IPMI_LANP_IP6_DYNAMIC_HOPS, + IPMI_LANP_IP6_NDSLAAC_CFG_SUP, + IPMI_LANP_IP6_NDSLAAC_CFG, IPMI_LANP_OEM_ALERT_STRING=96, IPMI_LANP_ALERT_RETRY=97, IPMI_LANP_UTC_OFFSET=98, diff --git a/include/ipmitool/ipmi_lanp6.h b/include/ipmitool/ipmi_lanp6.h new file mode 100644 index 0000000..91799e5 --- /dev/null +++ b/include/ipmitool/ipmi_lanp6.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Pentair Technical Products. All right reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pentair Technical Products or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * PENTAIR TECHNICAL SOLUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IPMI_LANP6_H +#define IPMI_LANP6_H + +#include + +/* + * LAN configuration parameter. + */ +struct ipmi_lanp { + int selector; + const char *name; + int size; +}; + +/* + * Private data for LAN configuration. + */ +struct ipmi_lanp_priv { + struct ipmi_intf *intf; + int channel; +}; + +#endif /* IPMI_LANP6_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 359cb30..cc69a8f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -41,6 +41,7 @@ libipmitool_la_SOURCES = helper.c ipmi_sdr.c ipmi_sel.c ipmi_sol.c ipmi_pef.c \ ipmi_main.c ipmi_tsol.c ipmi_firewall.c ipmi_kontronoem.c \ ipmi_hpmfwupg.c ipmi_sdradd.c ipmi_ekanalyzer.c ipmi_gendev.c \ ipmi_ime.c ipmi_delloem.c ipmi_dcmi.c hpm2.c ipmi_vita.c \ + ipmi_lanp6.c ipmi_cfgp.c \ ../src/plugins/lan/md5.c ../src/plugins/lan/md5.h libipmitool_la_LDFLAGS = -export-dynamic diff --git a/lib/ipmi_cfgp.c b/lib/ipmi_cfgp.c new file mode 100644 index 0000000..b8af80d --- /dev/null +++ b/lib/ipmi_cfgp.c @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2016 Pentair Technical Products. All right reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pentair Technical Products or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * PENTAIR TECHNICAL SOLUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +/* ipmi_cfgp_init initialize configuration parameter context + * @param ctx context to initialize + * @param set array of parameter descriptors + * @param count amount of descriptors supplied + * @param handler function to do real job on parameters from the set + * @param priv private data for the handler + */ +int +ipmi_cfgp_init(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp *set, + unsigned int count, const char *cmdname, + ipmi_cfgp_handler_t handler, void *priv) +{ + if (ctx == NULL || set == NULL || handler == NULL || !cmdname) { + return -1; + } + + memset(ctx, 0, sizeof(struct ipmi_cfgp_ctx)); + + ctx->set = set; + ctx->count = count; + ctx->cmdname = cmdname; + ctx->handler = handler; + ctx->priv = priv; + + return 0; +} + +/* ipmi_cfgp_uninit destroy data list attached to context + * @param ctx parameter context to clear + * @returns 0 -- list destroyed + * -1 -- ctx is NULL + */ +int +ipmi_cfgp_uninit(struct ipmi_cfgp_ctx *ctx) +{ + struct ipmi_cfgp_data *d; + + if (ctx == NULL) { + return -1; + } + + while (ctx->v) { + d = ctx->v; + ctx->v = d->next; + free(d); + d = NULL; + } + + return 0; +} + +/* lookup_cfgp -- find a parameter in a set*/ +static const struct ipmi_cfgp * +lookup_cfgp(const struct ipmi_cfgp_ctx *ctx, const char *name) +{ + const struct ipmi_cfgp *p; + int i; + + for (i = 0; i < ctx->count; i++) { + p = &ctx->set[i]; + + if (p->name && !strcasecmp(p->name, name)) { + return p; + } + } + + return NULL; +} + +/* ipmi_cfgp_parse_sel parse parameter selector + * (parameter ID, set selector, block selector) from cmdline. + * + * @param ctx configuration parameter context to use + * @param argc elements left in argv + * @param argv array of arguments + * @param sel where to store parsed selector + * + * @returns >=0 number of argv elements used + * <0 error + */ +int +ipmi_cfgp_parse_sel(struct ipmi_cfgp_ctx *ctx, + int argc, const char **argv, struct ipmi_cfgp_sel *sel) +{ + const struct ipmi_cfgp *p; + + if (ctx == NULL || argv == NULL || sel == NULL) { + return -1; + } + + sel->param = -1; + sel->set = -1; + sel->block = -1; + + if (argc == 0) { + /* no parameter specified, good for print, save */ + return 0; + } + + p = lookup_cfgp(ctx, argv[0]); + if (p == NULL) { + lprintf(LOG_ERR, "invalid parameter"); + return -1; + } + + sel->param = p - ctx->set; + sel->set = p->is_set ? -1 : 0; + sel->block = p->has_blocks ? -1 : 0; + + if (argc == 1 || !p->is_set) { + /* No set and block selector applicable or specified */ + return 1; + } + + if (str2int(argv[1], &sel->set) + || sel->set < 0 + || (sel->set == 0 && p->first_set)) { + lprintf(LOG_ERR, "invalid set selector"); + return -1; + } + + if (argc == 2 || !p->has_blocks) { + /* No block selector applicable or specified */ + return 2; + } + + if (str2int(argv[2], &sel->block) + || sel->block < 0 + || (sel->block == 0 && p->first_block)) { + lprintf(LOG_ERR, "invalid block selector"); + return -1; + } + + return 3; +} + +/* cfgp_add_data adds block of data to list in the configuration + * parameter context + * + * @param ctx context to add data to + * @param data parameter data + */ +static void +cfgp_add_data(struct ipmi_cfgp_ctx *ctx, struct ipmi_cfgp_data *data) +{ + struct ipmi_cfgp_data **pprev = &ctx->v; + + data->next = NULL; + + while (*pprev) { + pprev = &(*pprev)->next; + } + + *pprev = data; +} + +/* cfgp_usage prints format for configuration parameter + * + * @param p configuration parameter descriptor + * @param write 0 if no value is expected, !=0 otherwise + */ +static void +cfgp_usage(const struct ipmi_cfgp *p, int write) +{ + if (p->name == NULL) { + return; + } + + if (write && p->format == NULL) { + return; + } + + printf(" %s%s%s %s\n", + p->name, p->is_set ? " " : "", + p->has_blocks ? " " : "", + write ? p->format : ""); +} + +/* ipmi_cfgp_usage prints format for configuration parameter set + * + * @param set configuration parameter descriptor array + * @param count number of elements in set + * @param write 0 if no value is expected, !=0 otherwise + */ +void +ipmi_cfgp_usage(const struct ipmi_cfgp *set, int count, int write) +{ + const struct ipmi_cfgp *p; + int i; + + if (set == NULL) { + return; + } + + for (i = 0; i < count; i++) { + p = &set[i]; + + if (write && p->access == CFGP_RDONLY) { + continue; + } + + if (!write && p->access == CFGP_WRONLY) { + continue; + } + + cfgp_usage(p, write); + } +} + +/* ipmi_cfgp_parse_data parse parameter data from command line into context + * @param ctx context to add data + * @param sel parameter selector + * @param argc number of elements in argv + * @param argv array of unparsed arguments + * + * @returns 0 on success + * <0 on error + */ +int +ipmi_cfgp_parse_data(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, int argc, const char **argv) +{ + const struct ipmi_cfgp *p; + struct ipmi_cfgp_data *data; + struct ipmi_cfgp_action action; + + if (ctx == NULL || sel == NULL || argv == NULL) { + return -1; + } + + if (sel->param == -1 || sel->param >= ctx->count) { + lprintf(LOG_ERR, "invalid parameter, must be one of:"); + ipmi_cfgp_usage(ctx->set, ctx->count, 1); + return -1; + } + + if (sel->set == -1) { + lprintf(LOG_ERR, "set selector is not specified"); + return -1; + } + + if (sel->block == -1) { + lprintf(LOG_ERR, "block selector is not specified"); + return -1; + } + + p = &ctx->set[sel->param]; + + if (p->size == 0) { + return -1; + } + + data = malloc(sizeof(struct ipmi_cfgp_data) + p->size); + if (data == NULL) { + return -1; + } + + memset(data, 0, sizeof(struct ipmi_cfgp_data) + p->size); + + action.type = CFGP_PARSE; + action.set = sel->set; + action.block = sel->block; + action.argc = argc; + action.argv = argv; + action.file = NULL; + + if (ctx->handler(ctx->priv, p, &action, data->data) != 0) { + ipmi_cfgp_usage(p, 1, 1); + free(data); + data = NULL; + return -1; + } + + data->sel = *sel; + + cfgp_add_data(ctx, data); + return 0; +} + +/* cfgp_get_param -- get parameter data from MC into data list within context + * + * @param ctx context + * @param p parameter descriptor + * @param set parameter set selector, can be -1 to scan all set selectors + * @param block parameter block selector, can be -1 to get all blocks + * @param quiet set to non-zero to continue on errors + * (required for -1 to work) + * @returns 0 on success, non-zero otherwise + */ +static int +cfgp_get_param(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp *p, + int set, int block, int quiet) +{ + struct ipmi_cfgp_data *data; + struct ipmi_cfgp_action action; + int cset; + int cblock; + int ret; + + if (p->size == 0) { + return -1; + } + + action.type = CFGP_GET; + action.argc = 0; + action.argv = NULL; + action.file = NULL; + + if (set == -1 && !p->is_set) { + set = 0; + } + + if (block == -1 && !p->has_blocks) { + block = 0; + } + + if (set == -1) { + cset = p->first_set; + } else { + cset = set; + } + + action.quiet = quiet; + + do { + if (block == -1) { + cblock = p->first_block; + } else { + cblock = block; + } + + do { + data = malloc(sizeof(struct ipmi_cfgp_data) + p->size); + if (data == NULL) { + return -1; + } + + memset(data, 0, sizeof(struct ipmi_cfgp_data) + p->size); + + action.set = cset; + action.block = cblock; + + ret = ctx->handler(ctx->priv, p, &action, data->data); + if (ret != 0) { + free(data); + data = NULL; + + if (!action.quiet) { + return ret; + } + break; + } + + data->sel.param = p - ctx->set; + data->sel.set = cset; + data->sel.block = cblock; + + cfgp_add_data(ctx, data); + + cblock++; + action.quiet = 1; + } while (block == -1); + + if (ret != 0 && cblock == p->first_block) { + break; + } + + cset++; + } while (set == -1); + + return 0; +} + +/* ipmi_cfgp_get -- get parameters data from MC into data list within context + * + * @param ctx context + * @param sel parameter selector + * @returns 0 on success, non-zero otherwise + */ +int +ipmi_cfgp_get(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp_sel *sel) +{ + int i; + int ret; + + if (ctx == NULL || sel == NULL) { + return -1; + } + + if (sel->param != -1) { + if (sel->param >= ctx->count) { + return -1; + } + + ret = cfgp_get_param(ctx, &ctx->set[sel->param], + sel->set, sel->block, 0); + if (ret) { + return -1; + } + return 0; + } + + for (i = 0; i < ctx->count; i++) { + if (ctx->set[i].access == CFGP_WRONLY) { + continue; + } + + if (cfgp_get_param(ctx, &ctx->set[i], sel->set, sel->block, 1)) { + return -1; + } + } + + return 0; +} + +static int +cfgp_do_action(struct ipmi_cfgp_ctx *ctx, int action_type, + const struct ipmi_cfgp_sel *sel, FILE *file, int filter) +{ + const struct ipmi_cfgp *p; + struct ipmi_cfgp_data *data; + struct ipmi_cfgp_action action; + int ret; + + if (ctx == NULL || sel == NULL) { + return -1; + } + + action.type = action_type; + action.argc = 0; + action.argv = NULL; + action.file = file; + + for (data = ctx->v; data != NULL; data = data->next) { + if (sel->param != -1 && sel->param != data->sel.param) { + continue; + } + if (sel->set != -1 && sel->set != data->sel.set) { + continue; + } + if (sel->block != -1 && sel->block != data->sel.block) { + continue; + } + if (ctx->set[data->sel.param].access == filter) { + continue; + } + + p = &ctx->set[data->sel.param]; + + action.set = data->sel.set; + action.block = data->sel.block; + + if (action_type == CFGP_SAVE) { + fprintf(file, "%s %s ", ctx->cmdname, p->name); + if (p->is_set) { + fprintf(file, "%d ", data->sel.set); + } + if (p->has_blocks) { + fprintf(file, "%d ", data->sel.block); + } + } + + ret = ctx->handler(ctx->priv, p, &action, data->data); + + if (action_type == CFGP_SAVE) { + fputc('\n', file); + } + + if (ret != 0) { + return -1; + } + } + + return 0; +} + +int +ipmi_cfgp_set(struct ipmi_cfgp_ctx *ctx, const struct ipmi_cfgp_sel *sel) +{ + return cfgp_do_action(ctx, CFGP_SET, sel, NULL, CFGP_RDONLY); +} + +int +ipmi_cfgp_save(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, FILE *file) +{ + if (file == NULL) { + return -1; + } + + return cfgp_do_action(ctx, CFGP_SAVE, sel, file, CFGP_RDONLY); +} + +int +ipmi_cfgp_print(struct ipmi_cfgp_ctx *ctx, + const struct ipmi_cfgp_sel *sel, FILE *file) +{ + if (file == NULL) { + return -1; + } + + return cfgp_do_action(ctx, CFGP_PRINT, sel, file, CFGP_RESERVED); +} diff --git a/lib/ipmi_lanp6.c b/lib/ipmi_lanp6.c new file mode 100644 index 0000000..bbffb89 --- /dev/null +++ b/lib/ipmi_lanp6.c @@ -0,0 +1,1240 @@ +/* + * Copyright (c) 2016 Pentair Technical Products. All right reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Pentair Technical Products or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * PENTAIR TECHNICAL SOLUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * LAN6 command values. + */ +enum { + LANP_CMD_SAVE, + LANP_CMD_SET, + LANP_CMD_PRINT, + LANP_CMD_LOCK, + LANP_CMD_COMMIT, + LANP_CMD_DISCARD, + LANP_CMD_HELP, + LANP_CMD_ANY = 0xFF +}; + +/* + * Generic LAN configuration parameters. + */ +const struct ipmi_lanp generic_lanp6[] = { + { 0, "Set In Progress", 1 }, + { 50, "IPv6/IPv4 Support", 1 }, + { 51, "IPv6/IPv4 Addressing Enables", 1 }, + { 52, "IPv6 Header Traffic Class", 1 }, + { 53, "IPv6 Header Static Hop Limit", 1 }, + { 54, "IPv6 Header Flow Label", 3 }, + { 55, "IPv6 Status", 3 }, + { 56, "IPv6 Static Address", 20 }, + { 57, "IPv6 DHCPv6 Static DUID Storage Length", 1 }, + { 58, "IPv6 DHCPv6 Static DUID", 18 }, + { 59, "IPv6 Dynamic Address", 20 }, + { 60, "IPv6 DHCPv6 Dynamic DUID Storage Length", 1 }, + { 61, "IPv6 DHCPv6 Dynamic DUID", 18 }, + { 62, "IPv6 DHCPv6 Timing Configuration Support", 1 }, + { 63, "IPv6 DHCPv6 Timing Configuration", 18 }, + { 64, "IPv6 Router Address Configuration Control", 1 }, + { 65, "IPv6 Static Router 1 IP Address", 16 }, + { 66, "IPv6 Static Router 1 MAC Address", 6 }, + { 67, "IPv6 Static Router 1 Prefix Length", 1 }, + { 68, "IPv6 Static Router 1 Prefix Value", 16 }, + { 69, "IPv6 Static Router 2 IP Address", 16 }, + { 70, "IPv6 Static Router 2 MAC Address", 6 }, + { 71, "IPv6 Static Router 2 Prefix Length", 1 }, + { 72, "IPv6 Static Router 2 Prefix Value", 16 }, + { 73, "IPv6 Number of Dynamic Router Info Sets", 1 }, + { 74, "IPv6 Dynamic Router Info IP Address", 17 }, + { 75, "IPv6 Dynamic Router Info MAC Address", 7 }, + { 76, "IPv6 Dynamic Router Info Prefix Length", 2 }, + { 77, "IPv6 Dynamic Router Info Prefix Value", 17 }, + { 78, "IPv6 Dynamic Router Received Hop Limit", 1 }, + { 79, "IPv6 ND/SLAAC Timing Configuration Support", 1 }, + { 80, "IPv6 ND/SLAAC Timing Configuration", 18 }, + { 0, NULL, 0 } +}; + +/* + * Set/Get LAN Configuration Parameters + * command-specific completion codes. + */ +const struct valstr lanp_cc_vals[] = { + { 0x80, "Parameter not supported" }, + { 0x81, "Set already in progress" }, + { 0x82, "Parameter is read-only" }, + { 0x83, "Write-only parameter" }, + { 0x00, NULL } +}; + +/* + * IPv6/IPv4 Addressing Enables. + */ +const struct valstr ip6_enable_vals[] = { + { 0, "ipv4" }, + { 1, "ipv6" }, + { 2, "both" }, + { 0xFF, NULL } +}; + +/* + * Enable/Disable a static address. + */ +const struct valstr ip6_addr_enable_vals[] = { + { 0x00, "disable" }, + { 0x80, "enable" }, + { 0xFF, NULL } +}; + +/* + * IPv6 address source values. + */ +const struct valstr ip6_addr_sources[] = { + { 0, "static" }, + { 1, "SLAAC" }, + { 2, "DHCPv6" }, + { 0, NULL } +}; + +/* + * IPv6 address status values. + */ +const struct valstr ip6_addr_statuses[] = { + { 0, "active" }, + { 1, "disabled" }, + { 2, "pending" }, + { 3, "failed" }, + { 4, "deprecated" }, + { 5, "invalid" }, + { 0xFF, NULL } +}; + +/* + * DHCPv6 DUID type values. + */ +const struct valstr ip6_duid_types[] = { + { 0, "unknown" }, + { 1, "DUID-LLT" }, + { 2, "DUID-EN" }, + { 3, "DUID-LL" }, + { 0xFF, NULL } +}; + +/* + * Timing Configuration support values. + */ +const struct valstr ip6_cfg_sup_vals[] = { + { 0, "not supported" }, + { 1, "global" }, + { 2, "per interface" }, + { 0xFF, NULL } +}; + +/* + * Router Address Configuration Control values. + */ +const struct valstr ip6_rtr_configs[] = { + { 1, "static" }, + { 2, "dynamic" }, + { 3, "both" }, + { 0xFF, NULL } +}; + + +const struct valstr ip6_command_vals[] = { + { LANP_CMD_SET, "set" }, + { LANP_CMD_SAVE, "save" }, + { LANP_CMD_PRINT, "print" }, + { LANP_CMD_LOCK, "lock" }, + { LANP_CMD_COMMIT, "commit" }, + { LANP_CMD_DISCARD, "discard" }, + { LANP_CMD_HELP, "help" }, + { LANP_CMD_ANY, NULL } +}; + +static const struct ipmi_cfgp lan_cfgp[] = { + { .name = "support", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_SUPPORT + }, + { .name = "enables", .format = "{ipv4|ipv6|both}", .size = 1, + .access = CFGP_RDWR, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_ENABLES + }, + { .name = "traffic_class", .format = "", .size = 1, + .access = CFGP_RDWR, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_TRAFFIC_CLASS + }, + { .name = "static_hops", .format = "", .size = 1, + .access = CFGP_RDWR, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_STATIC_HOPS + }, + { .name = "flow_label", .format = "", .size = 3, + .access = CFGP_RDWR, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_FLOW_LABEL + }, + { .name = "status", .format = NULL, .size = 3, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_STATUS + }, + { .name = "static_addr", + .format = "{enable|disable} ", .size = 20, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_STATIC_ADDR + }, + { .name = "static_duid_stg", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_STATIC_DUID_STG + }, + { .name = "static_duid", .format = "", .size = 18, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 0, .has_blocks = 1, .first_block = 0, + .specific = IPMI_LANP_IP6_STATIC_DUID + }, + { .name = "dynamic_addr", .format = NULL, .size = 20, + .access = CFGP_RDONLY, + .is_set = 1, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DYNAMIC_ADDR + }, + { .name = "dynamic_duid_stg", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DYNAMIC_DUID_STG + }, + { .name = "dynamic_duid", .format = "", .size = 18, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 0, .has_blocks = 1, .first_block = 0, + .specific = IPMI_LANP_IP6_DYNAMIC_DUID + }, + { .name = "dhcp6_cfg_sup", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DHCP6_CFG_SUP + }, + { .name = "dhcp6_cfg", .format = " ", .size = 36, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DHCP6_CFG + }, + { .name = "rtr_cfg", .format = "{static|dynamic|both}", .size = 1, + .access = CFGP_RDWR, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_ROUTER_CFG + }, + { .name = "static_rtr", + .format = " ", .size = 43, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 1, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_STATIC_RTR1_ADDR + }, + { .name = "num_dynamic_rtrs", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_NUM_DYNAMIC_RTRS + }, + { .name = "dynamic_rtr", .format = NULL, .size = 43, + .access = CFGP_RDONLY, + .is_set = 1, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DYNAMIC_RTR_ADDR + }, + { .name = "dynamic_hops", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_DYNAMIC_HOPS + }, + { .name = "ndslaac_cfg_sup", .format = NULL, .size = 1, + .access = CFGP_RDONLY, + .is_set = 0, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_NDSLAAC_CFG_SUP + }, + { .name = "ndslaac_cfg", .format = "", .size = 18, + .access = CFGP_RDWR, + .is_set = 1, .first_set = 0, .has_blocks = 0, .first_block = 0, + .specific = IPMI_LANP_IP6_NDSLAAC_CFG + } +}; + +/* + * Lookup LAN parameter descriptor by parameter selector. + */ +const struct ipmi_lanp * +lookup_lanp(int param) +{ + const struct ipmi_lanp *p = generic_lanp6; + + while (p->name) { + if (p->selector == param) { + return p; + } + + p++; + } + + return NULL; +} + +/* + * Print request error. + */ +static int +ipmi_lanp_err(const struct ipmi_rs *rsp, const struct ipmi_lanp *p, + const char *action, int quiet) +{ + const char *reason; + char cc_msg[10]; + int log_level = LOG_ERR; + int err; + + if (rsp == NULL) { + reason = "No response"; + err = -1; + } else { + err = rsp->ccode; + if (quiet == 1 + && (rsp->ccode == 0x80 + || rsp->ccode == IPMI_CC_PARAM_OUT_OF_RANGE + || rsp->ccode == IPMI_CC_INV_DATA_FIELD_IN_REQ)) { + /* be quiet */ + return err; + } + + if (rsp->ccode >= 0xC0) { + /* browse for generic completion codes */ + reason = val2str(rsp->ccode, completion_code_vals); + } else { + /* browse for command-specific completion codes first */ + reason = val2str(rsp->ccode, lanp_cc_vals); + } + + if (reason == NULL) { + /* print completion code value */ + snprintf(cc_msg, sizeof(cc_msg), "CC=%02x", rsp->ccode); + reason = cc_msg; + } + + if (rsp->ccode == IPMI_CC_OK) { + log_level = LOG_DEBUG; + } + } + + lprintf(log_level, "Failed to %s %s: %s", action, p->name, reason); + return err; +} + +/* + * Get dynamic OEM LAN configuration parameter from BMC. + * Dynamic in this context is when the base for OEM LAN parameters + * is not known apriori. + */ +int +ipmi_get_dynamic_oem_lanp(void *priv, const struct ipmi_lanp *param, + int oem_base, int set_selector, int block_selector, + void *data, int quiet) +{ + struct ipmi_lanp_priv *lp = priv; + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint8_t req_data[4]; + int length; + + if (!priv || !param || !data) { + return -1; + } + req_data[0] = lp->channel; + req_data[1] = param->selector + oem_base; + req_data[2] = set_selector; + req_data[3] = block_selector; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = 2; + req.msg.data = req_data; + req.msg.data_len = 4; + + lprintf(LOG_INFO, "Getting parameter '%s' set %d block %d", + param->name, set_selector, block_selector); + + rsp = lp->intf->sendrecv(lp->intf, &req); + if (rsp == NULL || rsp->ccode) { + return ipmi_lanp_err(rsp, param, "get", quiet); + } + + memset(data, 0, param->size); + + if (rsp->data_len - 1 < param->size) { + length = rsp->data_len - 1; + } else { + length = param->size; + } + + if (length) { + memcpy(data, rsp->data + 1, length); + } + + return 0; +} + +/* + * Get generic LAN configuration parameter. + */ +int +ipmi_get_lanp(void *priv, int param_selector, int set_selector, + int block_selector, void *data, int quiet) +{ + return ipmi_get_dynamic_oem_lanp(priv, lookup_lanp(param_selector), 0, + set_selector, block_selector, data, quiet); +} + +/* + * Set dynamic OEM LAN configuration parameter to BMC. + * Dynamic in this context is when the base for OEM LAN parameters + * is not known apriori. + */ +int +ipmi_set_dynamic_oem_lanp(void *priv, const struct ipmi_lanp *param, + int base, const void *data) +{ + struct ipmi_lanp_priv *lp = priv; + struct ipmi_rs *rsp; + struct ipmi_rq req; + uint8_t req_data[32]; + + if (!priv || !param || !data) { + return -1; + } + /* fill the first two bytes */ + req_data[0] = lp->channel; + req_data[1] = param->selector + base; + + /* fill the rest data */ + memcpy(&req_data[2], data, param->size); + + /* fill request */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_TRANSPORT; + req.msg.cmd = 1; + req.msg.data = req_data; + req.msg.data_len = param->size + 2; + + lprintf(LOG_INFO, "Setting parameter '%s'", param->name); + + rsp = lp->intf->sendrecv(lp->intf, &req); + if (rsp == NULL || rsp->ccode) { + return ipmi_lanp_err(rsp, param, "set", 0); + } + + return 0; +} + +/* + * Set generic LAN configuration parameter. + */ +int +ipmi_set_lanp(void *priv, int param_selector, const void *data) +{ + return ipmi_set_dynamic_oem_lanp(priv, lookup_lanp(param_selector), + 0, data); +} + +static int +lanp_parse_cfgp(const struct ipmi_cfgp *p, int set, int block, + int argc, const char *argv[], unsigned char *data) +{ + unsigned int v; + + if (argc == 0) { + return -1; + } + + switch(p->specific) { + case IPMI_LANP_IP6_ENABLES: + data[0] = str2val(argv[0], ip6_enable_vals); + if (data[0] == 0xFF) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + case IPMI_LANP_IP6_FLOW_LABEL: + if (str2uint(argv[0], &v)) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + + data[0] = (v >> 16) & 0x0F; + data[1] = (v >> 8) & 0xFF; + data[2] = v & 0xFF; + break; + + case IPMI_LANP_IP6_STATUS: + if (argc < 3) { + return -1; + } + + if (str2uchar(argv[0], &data[0]) + || str2uchar(argv[1], &data[1]) + || str2uchar(argv[2], &data[2])) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + case IPMI_LANP_IP6_STATIC_ADDR: + case IPMI_LANP_IP6_DYNAMIC_ADDR: + if (argc < 3) { + return -1; + } + + data[0] = set; + if (p->specific == IPMI_LANP_IP6_STATIC_ADDR) { + data[1] = str2val(argv[0], ip6_addr_enable_vals); + } else { + data[1] = str2val(argv[0], ip6_addr_sources); + } + if (data[1] == 0xFF) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + + if (inet_pton(AF_INET6, argv[1], &data[2]) != 1) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + + if (str2uchar(argv[2], &data[18])) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + + if (argc >= 4) { + data[19] = str2val(argv[3], ip6_addr_statuses); + } + break; + + case IPMI_LANP_IP6_STATIC_DUID: + case IPMI_LANP_IP6_DYNAMIC_DUID: + case IPMI_LANP_IP6_NDSLAAC_CFG: + data[0] = set; + data[1] = block; + if (ipmi_parse_hex(argv[0], &data[2], 16) < 0) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + case IPMI_LANP_IP6_DHCP6_CFG: + data[0] = set; + data[1] = 0; + data[18] = set; + data[19] = 1; + + if (ipmi_parse_hex(argv[0], &data[2], 16) < 0 + || (argc > 1 && + ipmi_parse_hex(argv[1], &data[20], 6) < 0)) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + case IPMI_LANP_IP6_ROUTER_CFG: + data[0] = str2val(argv[0], ip6_rtr_configs); + if (data[0] == 0xFF) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + case IPMI_LANP_IP6_STATIC_RTR1_ADDR: + if (set > 2) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + + case IPMI_LANP_IP6_DYNAMIC_RTR_ADDR: + if (argc < 4) { + return -1; + } + + /* + * Data is stored in the following way: + * 0: ... + * 17: ... + * 24: + * 26: ... + */ + data[0] = data[17] = data[24] = data[26] = set; + + if (inet_pton(AF_INET6, argv[0], &data[1]) != 1 + || str2mac(argv[1], &data[18]) + || inet_pton(AF_INET6, argv[2], &data[27]) != 1 + || str2uchar(argv[3], &data[25])) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + break; + + default: + if (str2uchar(argv[0], &data[0])) { + lprintf(LOG_ERR, "invalid value"); + return -1; + } + } + + return 0; +} + +static int +lanp_set_cfgp(void *priv, const struct ipmi_cfgp *p, const unsigned char *data) +{ + int ret; + int param = p->specific; + int off = 0; + + switch(param) { + case IPMI_LANP_IP6_DHCP6_CFG: + ret = ipmi_set_lanp(priv, param, &data[0]); + if (ret == 0) { + ret = ipmi_set_lanp(priv, param, &data[18]); + } + break; + + case IPMI_LANP_IP6_STATIC_RTR1_ADDR: + if (data[0] == 2) { + param = IPMI_LANP_IP6_STATIC_RTR2_ADDR; + } + off = 1; + + case IPMI_LANP_IP6_DYNAMIC_RTR_ADDR: + ret = ipmi_set_lanp(priv, param, &data[0 + off]); + if (ret == 0) { + ret = ipmi_set_lanp(priv, param + 1, &data[17 + off]); + } + if (ret == 0) { + ret = ipmi_set_lanp(priv, param + 2, &data[24 + off]); + } + if (ret == 0) { + ret = ipmi_set_lanp(priv, param + 3, &data[26 + off]); + } + break; + + + default: + ret = ipmi_set_lanp(priv, param, data); + } + + return ret; +} + +static int +lanp_get_cfgp(void *priv, const struct ipmi_cfgp *p, + int set, int block, unsigned char *data, int quiet) +{ + int ret; + int param = p->specific; + int off = 0; + + switch(param) { + case IPMI_LANP_IP6_DHCP6_CFG: + ret = ipmi_get_lanp(priv, param, set, 0, &data[0], quiet); + if (ret == 0) { + ret = ipmi_get_lanp(priv, param, set, + 1, &data[18], quiet); + } + break; + + case IPMI_LANP_IP6_STATIC_RTR1_ADDR: + if (set > 2) { + return -1; + } + + if (set == 2) { + param = IPMI_LANP_IP6_STATIC_RTR2_ADDR; + } + set = 0; + off = 1; + data[0] = data[17] = data[24] = data[26] = set; + + case IPMI_LANP_IP6_DYNAMIC_RTR_ADDR: + ret = ipmi_get_lanp(priv, param, set, block, + &data[0 + off], quiet); + if (ret == 0) { + ret = ipmi_get_lanp(priv, param + 1, set, block, + &data[17 + off], 0); + } + if (ret == 0) { + ret = ipmi_get_lanp(priv, param + 2, set, block, + &data[24 + off], 0); + } + if (ret == 0) { + ret = ipmi_get_lanp(priv, param + 3, set, block, + &data[26 + off], 0); + } + break; + + default: + ret = ipmi_get_lanp(priv, param, set, block, data, quiet); + } + + return ret; +} + +static int +lanp_save_cfgp(const struct ipmi_cfgp *p, const unsigned char *data, FILE *file) +{ + char addr[INET6_ADDRSTRLEN]; + char pfx[INET6_ADDRSTRLEN]; + const char *src; + + switch(p->specific) { + case IPMI_LANP_IP6_ENABLES: + fputs(val2str(data[0], ip6_enable_vals), file); + break; + + case IPMI_LANP_IP6_FLOW_LABEL: + fprintf(file, "0x%xd", (data[0] << 16 ) | (data[1] << 8) | data[2]); + break; + + case IPMI_LANP_IP6_STATUS: + fprintf(file, "%d %d %d", data[0], data[1], data[2]); + break; + + case IPMI_LANP_IP6_STATIC_ADDR: + case IPMI_LANP_IP6_DYNAMIC_ADDR: + if (p->specific == IPMI_LANP_IP6_STATIC_ADDR) { + src = val2str(data[1], ip6_addr_enable_vals); + } else { + src = val2str(data[1], ip6_addr_sources); + } + + fprintf(file, "%s %s %d %s", src, + inet_ntop(AF_INET6, &data[2], addr, sizeof(addr)), + data[18], val2str(data[19], ip6_addr_statuses)); + break; + + case IPMI_LANP_IP6_STATIC_DUID: + case IPMI_LANP_IP6_DYNAMIC_DUID: + case IPMI_LANP_IP6_NDSLAAC_CFG: + fprintf(file, "%s", buf2str(&data[2], 16)); + break; + + case IPMI_LANP_IP6_DHCP6_CFG: + fprintf(file, "%s", buf2str(&data[2], 16)); + fprintf(file, " %s", buf2str(&data[20], 6)); + break; + + case IPMI_LANP_IP6_ROUTER_CFG: + fputs(val2str(data[0], ip6_rtr_configs), file); + break; + + case IPMI_LANP_IP6_STATIC_RTR1_ADDR: + case IPMI_LANP_IP6_DYNAMIC_RTR_ADDR: + fprintf(file, "%s %s %s %d", + inet_ntop(AF_INET6, &data[1], addr, sizeof(addr)), + mac2str(&data[18]), + inet_ntop(AF_INET6, &data[27], pfx, sizeof(pfx)), data[25]); + break; + + default: + fprintf(file, "%d", data[0]); + } + + return 0; +} + + +static int +lanp_print_cfgp(const struct ipmi_cfgp *p, + int set, int block, const unsigned char *data, FILE *file) +{ + char addr[INET6_ADDRSTRLEN]; + char pfx[INET6_ADDRSTRLEN]; + const char *pname; + const struct ipmi_lanp *lanp = lookup_lanp(p->specific); + + if (!lanp || !p || !file || !data || !lanp->name) { + return -1; + } + pname = lanp->name; + + switch(p->specific) { + case IPMI_LANP_IP6_SUPPORT: + fprintf(file, "%s:\n" + " IPv6 only: %s\n" + " IPv4 and IPv6: %s\n" + " IPv6 Destination Addresses for LAN alerting: %s\n", + pname, + data[0] & 1 ? "yes" : "no", + data[0] & 2 ? "yes" : "no", + data[0] & 4 ? "yes" : "no"); + break; + + case IPMI_LANP_IP6_ENABLES: + fprintf(file, "%s: %s\n", + pname, val2str(data[0], ip6_enable_vals)); + break; + + case IPMI_LANP_IP6_FLOW_LABEL: + fprintf(file, "%s: %d\n", + pname, (data[0] << 16 ) | (data[1] << 8) | data[2]); + break; + + case IPMI_LANP_IP6_STATUS: + fprintf(file, "%s:\n" + " Static address max: %d\n" + " Dynamic address max: %d\n" + " DHCPv6 support: %s\n" + " SLAAC support: %s\n", + pname, + data[0], data[1], + (data[2] & 1) ? "yes" : "no", + (data[2] & 2) ? "yes" : "no"); + break; + + case IPMI_LANP_IP6_STATIC_ADDR: + fprintf(file, "%s %d:\n" + " Enabled: %s\n" + " Address: %s/%d\n" + " Status: %s\n", + pname, set, + (data[1] & 0x80) ? "yes" : "no", + inet_ntop(AF_INET6, &data[2], addr, sizeof(addr)), + data[18], val2str(data[19] & 0xF, ip6_addr_statuses)); + break; + + case IPMI_LANP_IP6_DYNAMIC_ADDR: + fprintf(file, "%s %d:\n" + " Source/Type: %s\n" + " Address: %s/%d\n" + " Status: %s\n", + pname, set, + val2str(data[1] & 0xF, ip6_addr_sources), + inet_ntop(AF_INET6, &data[2], addr, sizeof(addr)), + data[18], val2str(data[19] & 0xF, ip6_addr_statuses)); + break; + + case IPMI_LANP_IP6_STATIC_DUID: + case IPMI_LANP_IP6_DYNAMIC_DUID: + if (block == 0) { + fprintf(file, "%s %d:\n" + " Length: %d\n" + " Type: %s\n", + pname, set, data[2], + val2str((data[3] << 8) + data[4], ip6_duid_types)); + } + fprintf(file, " %s\n", buf2str(&data[2], 16)); + break; + + case IPMI_LANP_IP6_DHCP6_CFG_SUP: + case IPMI_LANP_IP6_NDSLAAC_CFG_SUP: + fprintf(file, "%s: %s\n", + pname, val2str(data[0], ip6_cfg_sup_vals)); + break; + + case IPMI_LANP_IP6_DHCP6_CFG: + fprintf(file, "%s %d:\n", pname, set); + + fprintf(file, + " SOL_MAX_DELAY: %d\n" + " SOL_TIMEOUT: %d\n" + " SOL_MAX_RT: %d\n" + " REQ_TIMEOUT: %d\n" + " REQ_MAX_RT: %d\n" + " REQ_MAX_RC: %d\n" + " CNF_MAX_DELAY: %d\n" + " CNF_TIMEOUT: %d\n" + " CNF_MAX_RT: %d\n" + " CNF_MAX_RD: %d\n" + " REN_TIMEOUT: %d\n" + " REN_MAX_RT: %d\n" + " REB_TIMEOUT: %d\n" + " REB_MAX_RT: %d\n" + " INF_MAX_DELAY: %d\n" + " INF_TIMEOUT: %d\n" + " INF_MAX_RT: %d\n" + " REL_TIMEOUT: %d\n" + " REL_MAX_RC: %d\n" + " DEC_TIMEOUT: %d\n" + " DEC_MAX_RC: %d\n" + " HOP_COUNT_LIMIT: %d\n", + data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], + data[10], data[11], data[12], data[13], + data[14], data[15], data[16], data[17], + data[20], data[21], data[22], data[23], + data[24], data[25]); + break; + + case IPMI_LANP_IP6_ROUTER_CFG: + fprintf(file, "%s:\n" + " Enable static router address: %s\n" + " Enable dynamic router address: %s\n", + pname, + (data[0] & 1) ? "yes" : "no", + (data[0] & 2) ? "yes" : "no"); + break; + + case IPMI_LANP_IP6_STATIC_RTR1_ADDR: + case IPMI_LANP_IP6_DYNAMIC_RTR_ADDR: + if (p->specific == IPMI_LANP_IP6_STATIC_RTR1_ADDR) { + pname = "IPv6 Static Router"; + } else { + pname = "IPv6 Dynamic Router"; + } + + fprintf(file, "%s %d:\n" + " Address: %s\n" + " MAC: %s\n" + " Prefix: %s/%d\n", + pname, set, + inet_ntop(AF_INET6, &data[1], addr, sizeof(addr)), + mac2str(&data[18]), + inet_ntop(AF_INET6, &data[27], pfx, sizeof(pfx)), data[25]); + break; + + case IPMI_LANP_IP6_NDSLAAC_CFG: + fprintf(file, "%s %d:\n" + " MAX_RTR_SOLICITATION_DELAY: %d\n" + " RTR_SOLICITATION_INTERVAL: %d\n" + " MAX_RTR_SOLICITATIONS: %d\n" + " DupAddrDetectTransmits: %d\n" + " MAX_MULTICAST_SOLICIT: %d\n" + " MAX_UNICAST_SOLICIT: %d\n" + " MAX_ANYCAST_DELAY_TIME: %d\n" + " MAX_NEIGHBOR_ADVERTISEMENT: %d\n" + " REACHABLE_TIME: %d\n" + " RETRANS_TIMER: %d\n" + " DELAY_FIRST_PROBE_TIME: %d\n" + " MAX_RANDOM_FACTOR: %d\n" + " MIN_RANDOM_FACTOR: %d\n", + pname, set, + data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], + data[10], data[11], data[12], data[13], + data[14]); + break; + + default: + fprintf(file, "%s: %d\n", pname, data[0]); + } + + return 0; +} + +static int +lanp_ip6_cfgp(void *priv, const struct ipmi_cfgp *p, + const struct ipmi_cfgp_action *action, unsigned char *data) +{ + switch (action->type) { + case CFGP_PARSE: + return lanp_parse_cfgp(p, action->set, action->block, + action->argc, action->argv, data); + + case CFGP_GET: + return lanp_get_cfgp(priv, p, action->set, action->block, + data, action->quiet); + + case CFGP_SET: + return lanp_set_cfgp(priv, p, data); + + case CFGP_SAVE: + return lanp_save_cfgp(p, data, action->file); + + case CFGP_PRINT: + return lanp_print_cfgp(p, action->set, action->block, + data, action->file); + + default: + return -1; + } + + return 0; +} + +static void lanp_print_usage(int cmd) +{ + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_HELP) { + printf(" help [command]\n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_SAVE) { + printf(" save [ [ []]]\n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_SET) { + printf(" set [nolock] [ []] \n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_PRINT) { + printf(" print [ [ []]]\n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_LOCK) { + printf(" lock \n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_COMMIT) { + printf(" commit \n"); + } + if (cmd == LANP_CMD_ANY || cmd == LANP_CMD_DISCARD) { + printf(" discard \n"); + } + if (cmd == LANP_CMD_SAVE + || cmd == LANP_CMD_PRINT + || cmd == LANP_CMD_SET) { + printf("\n available parameters:\n"); + /* 'save' shall use 'write' filter, since it outputs a block + * of 'set's */ + ipmi_cfgp_usage(lan_cfgp, + sizeof(lan_cfgp)/sizeof(lan_cfgp[0]), + cmd != LANP_CMD_PRINT); + } +} + +static int +lanp_lock(struct ipmi_lanp_priv *lp) +{ + unsigned char byte = 1; + + return ipmi_set_lanp(lp, 0, &byte); +} + +static int +lanp_discard(struct ipmi_lanp_priv *lp) +{ + unsigned char byte = 0; + + return ipmi_set_lanp(lp, 0, &byte); +} + +static int +lanp_commit(struct ipmi_lanp_priv *lp) +{ + unsigned char byte = 2; + int ret; + + ret = ipmi_set_lanp(lp, 0, &byte); + if (ret == 0) { + ret = lanp_discard(lp); + } + + return ret; +} + +int +ipmi_lan6_main(struct ipmi_intf *intf, int argc, char **argv) +{ + struct ipmi_cfgp_ctx ctx; + struct ipmi_cfgp_sel sel; + struct ipmi_lanp_priv lp; + int cmd; + int chan; + int nolock = 0; + int ret; + + if (argc == 0) { + lanp_print_usage(LANP_CMD_ANY); + return 0; + } + + cmd = str2val(argv[0], ip6_command_vals); + if (cmd == LANP_CMD_ANY) { + lanp_print_usage(cmd); + return -1; + } + + if (cmd == LANP_CMD_HELP) { + if (argc == 1) { + cmd = LANP_CMD_ANY; + } else { + cmd = str2val(argv[1], ip6_command_vals); + } + + lanp_print_usage(cmd); + return 0; + } + + /* + * the rest commands expect channel number + * with the exception of 'get' and 'print' + */ + if (argc == 1) { + if (cmd == LANP_CMD_SAVE || cmd == LANP_CMD_PRINT) { + chan = find_lan_channel(intf, 1); + if (chan == 0) { + lprintf(LOG_ERR, "No LAN channel found"); + return -1; + } + } else { + lanp_print_usage(cmd); + return -1; + } + + argc -= 1; + argv += 1; + } else { + if (str2int(argv[1], &chan) != 0) { + lprintf(LOG_ERR, "Invalid channel: %s", argv[1]); + return -1; + } + + argc -= 2; + argv += 2; + + if (cmd == LANP_CMD_SET) { + if (argc && !strcasecmp(argv[0], "nolock")) { + nolock = 1; + + argc -= 1; + argv += 1; + } + } + + } + + lp.intf = intf; + lp.channel = chan; + + /* + * lock/commit/discard commands do not require parsing + * of parameter selection + */ + + switch (cmd) { + case LANP_CMD_LOCK: + lprintf(LOG_NOTICE, "Lock parameter(s)..."); + return lanp_lock(&lp); + + case LANP_CMD_COMMIT: + lprintf(LOG_NOTICE, "Commit parameter(s)..."); + return lanp_commit(&lp); + + case LANP_CMD_DISCARD: + lprintf(LOG_NOTICE, "Discard parameter(s)..."); + return lanp_discard(&lp); + } + + /* + * initialize configuration context and parse parameter selection + */ + + ipmi_cfgp_init(&ctx, lan_cfgp, + sizeof(lan_cfgp)/sizeof(lan_cfgp[0]), "lan6 set nolock", + lanp_ip6_cfgp, &lp); + + ret = ipmi_cfgp_parse_sel(&ctx, argc, (const char **)argv, &sel); + if (ret == -1) { + lanp_print_usage(cmd); + ipmi_cfgp_uninit(&ctx); + return -1; + } + + argc -= ret; + argv += ret; + + /* + * handle the rest commands + */ + + switch (cmd) { + case LANP_CMD_SAVE: + case LANP_CMD_PRINT: + lprintf(LOG_NOTICE, "Getting parameter(s)..."); + + ret = ipmi_cfgp_get(&ctx, &sel); + if (ret != 0) { + break; + } + + if (cmd == LANP_CMD_SAVE) { + static char cmd[20]; + FILE *out = stdout; + snprintf(cmd, sizeof(cmd) - 1, "lan6 set %d nolock", + lp.channel); + cmd[sizeof(cmd) - 1] = '\0'; + ctx.cmdname = cmd; + fprintf(out, "lan6 lock %d\n", lp.channel); + ret = ipmi_cfgp_save(&ctx, &sel, out); + fprintf(out, "lan6 commit %d\nlan6 discard %d\nexit\n", + lp.channel, lp.channel); + } else { + ret = ipmi_cfgp_print(&ctx, &sel, stdout); + } + break; + + case LANP_CMD_SET: + ret = ipmi_cfgp_parse_data(&ctx, &sel, argc, + (const char **)argv); + if (ret != 0) { + break; + } + + lprintf(LOG_NOTICE, "Setting parameter(s)..."); + + if (!nolock) { + ret = lanp_lock(&lp); + if (ret != 0) { + break; + } + } + + ret = ipmi_cfgp_set(&ctx, &sel); + if (!nolock) { + if (ret == 0) { + ret = lanp_commit(&lp); + } else { + lanp_discard(&lp); + } + } + break; + } + + /* + * free allocated memory + */ + ipmi_cfgp_uninit(&ctx); + + return ret; +} diff --git a/src/ipmitool.c b/src/ipmitool.c index 164fd44..5e19c6e 100644 --- a/src/ipmitool.c +++ b/src/ipmitool.c @@ -77,6 +77,7 @@ extern int ipmi_shell_main(struct ipmi_intf * intf, int argc, char ** argv); extern int ipmi_echo_main(struct ipmi_intf * intf, int argc, char ** argv); extern int ipmi_set_main(struct ipmi_intf * intf, int argc, char ** argv); extern int ipmi_exec_main(struct ipmi_intf * intf, int argc, char ** argv); +extern int ipmi_lan6_main(struct ipmi_intf *intf, int argc, char **argv); int csv_output = 0; @@ -122,6 +123,7 @@ struct ipmi_cmd ipmitool_cmd_list[] = { { ipmi_ekanalyzer_main,"ekanalyzer", "run FRU-Ekeying analyzer using FRU files"}, { ipmi_ime_main, "ime", "Update Intel Manageability Engine Firmware"}, { ipmi_vita_main, "vita", "Run a VITA 46.11 extended cmd"}, + { ipmi_lan6_main, "lan6", "Configure IPv6 LAN Channels"}, { NULL }, };