[RFC PATCH] util: Add a tool to generate network override requests
Suraj Jitindar Singh
sjitindarsingh at gmail.com
Mon Apr 4 13:23:23 AEST 2016
On 22/03/16 16:39, Sam Mendoza-Jonas wrote:
> Missed a send-email option, CC'ing Suraj.
>
> On Tue, Mar 22, 2016 at 04:37:40PM +1100, Samuel Mendoza-Jonas wrote:
>> From: Suraj Jitindar Singh <sjitindarsingh at gmail.com>
>>
>> On OpenPOWER machines with an AMI BMC an interface config override
>> function is available, but requires a long raw hex string to use.
>> Add a helper utility to generate this hex string based off more readable
>> inputs.
>>
>> This is based largely off work done by Suraj Jitindar Singh, reworked to
>> match existing code a little better and simplify some of the logic flow.
>>
>> Signed-off-by: Samuel Mendoza-Jonas <sam at mendozajonas.com>
>> ---
>> utils/Makefile.am | 3 +-
>> utils/pb-net-override.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 350 insertions(+), 1 deletion(-)
>> create mode 100644 utils/pb-net-override.c
>>
>> diff --git a/utils/Makefile.am b/utils/Makefile.am
>> index 403f46d..877e38e 100644
>> --- a/utils/Makefile.am
>> +++ b/utils/Makefile.am
>> @@ -14,7 +14,8 @@
>>
>> dist_sbin_SCRIPTS += utils/pb-udhcpc utils/pb-plugin
>> dist_pkglibexec_SCRIPTS = utils/pb-console
>> -sbin_PROGRAMS += utils/pb-event utils/pb-config
>> +#TODO: Not needed as an install, will probably need an extra configure option
>> +sbin_PROGRAMS += utils/pb-event utils/pb-config utils/pb-net-override
>>
>> utils_pb_config_LDADD = $(top_builddir)/lib/libpbcore.la \
>> $(top_builddir)/discover/platform.ro
>> diff --git a/utils/pb-net-override.c b/utils/pb-net-override.c
>> new file mode 100644
>> index 0000000..9e2bf1d
>> --- /dev/null
>> +++ b/utils/pb-net-override.c
>> @@ -0,0 +1,348 @@
>> +/*
>> + * Copyright (C) 2015 IBM Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +/*
>> + * On OpenPOWER machines with an AMI BMC, extra IPMI functionality is available
>> + * to specify network overrides.
>> + * Generate the raw request required to override Petitboot network configuration
>> + * over IPMI
>> + */
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdbool.h>
>> +#include <string.h>
>> +#include <arpa/inet.h>
>> +#include <netinet/in.h>
>> +#include <getopt.h>
>> +#include <errno.h>
>> +
>> +#define REQUEST_BYTE_SIZE 5
>> +#define OFFSET_COOKIE 4
>> +#define OFFSET_VERSION 8
>> +#define OFFSET_HW_ADDR_SIZE 10
>> +#define OFFSET_IP_ADDR_SIZE 11
>> +#define OFFSET_HW_ADDR 12
>> +#define MAX_REQUEST_SIZE (REQUEST_BYTE_SIZE * 53)
>> +#define COMMAND_SET "0x00 0x08 0x61 0x80 "
>> +#define COMMAND_GET "0x00 0x09 0x61 0x80 "
>> +#define COOKIE_VALUE "0x21 0x70 0x62 0x21 "
>> +#define VERSION_VALUE "0x00 0x01 "
>> +#define IPV4_ADDR_SIZE 4
>> +#define IPV6_ADDR_SIZE 16
>> +
>> +#define USAGE_DEFAULT \
>> + "usage:\n" \
>> + "pb-net-override --get\n" \
>> + "pb-net-override --clear\n" \
>> + "pb-net-override -d -h addr -i type -a addr\n" \
>> + "pb-net-override -s -h addr -i type -a addr -g addr -m mask\n" \
>> + "pb-net-override --help for further details\n"
>> +
>> +#define USAGE_DETAIL \
>> + "--get: Command to read any existing override\n" \
>> + "--clear: Command to clear any existing override\n" \
>> + "-d | --dhcp: Set a DHCP override. Requires a MAC address and " \
>> + "ip type\n" \
>> + "-s | --static: Set a static override. Requires a MAC address, " \
>> + "ip type\n\t IP address, gateway address, and subnet mask.\n\n" \
>> + "DHCP/Static override parameters:\n" \
>> + "-h | --hwaddr arg: MAC address, " \
>> + "eg. 11:22:33:44:55:66\n" \
>> + "-i | --ip-version ver: IP type (4 or 6)\n" \
>> + "-a | --ipaddr addr: IP address\n" \
>> + "-g | --gateway addr: Gateway IP address\n" \
>> + "-m | --subnet-mask mask: 2-digit mask (eg. 22)\n"
>> +
>> +enum method {
>> + METHOD_NONE = 0,
>> + METHOD_DHCP,
>> + METHOD_STATIC,
>> +};
>> +
>> +static int format_hw(int *hw_addr_size, char *hw_formatted,
>> + char *hw_addr_raw)
>> +{
>> + int i = 0;
>> + char *token;
>> + token = strtok(hw_addr_raw, ":");
>> + while (token) {
>> + if (strlen(token) != 2)
>> + return -1;
>> + snprintf(&hw_formatted[i * REQUEST_BYTE_SIZE],
>> + 1 + REQUEST_BYTE_SIZE, "0x%s ", token);
>> + i++;
>> + token = strtok(NULL, ":");
>> + }
>> + if (i != 6) {
>> + return -1;
>> + }
>> + *hw_addr_size = i;
>> + return 0;
>> +}
>> +
>> +static int format_ip(int *ip_addr_size, char *ip_formatted, const char *ip_raw)
>> +{
>> + int i, ret = -1;
>> + struct in_addr ip4;
>> + struct in6_addr ip6;
>> +
>> + if (strchr(ip_raw, '.') && !strchr(ip_raw, ':')) {
>> + *ip_addr_size = IPV4_ADDR_SIZE;
>> + if (inet_pton(AF_INET, ip_raw, &ip4) == 1) {
>> + for (i = 0; i < 4; i++) {
>> + snprintf(&ip_formatted[i * REQUEST_BYTE_SIZE],
>> + 1 + REQUEST_BYTE_SIZE, "0x%2.2x ",
>> + 0xff & (ip4.s_addr >> (8 * i)));
>> + }
>> + ret = 0;
>> + }
>> + } else if (strchr(ip_raw, ':') && !strchr(ip_raw, '.')) {
>> + *ip_addr_size = IPV6_ADDR_SIZE;
>> + if (inet_pton(AF_INET6, ip_raw, &ip6) == 1) {
>> + for (i = 0; i < 16; i++) {
>> + snprintf(&ip_formatted[i * REQUEST_BYTE_SIZE],
>> + 1 + REQUEST_BYTE_SIZE, "0x%2.2x ",
>> + ip6.s6_addr[i]);
>> + }
>> + ret = 0;
>> + }
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static int format_subnet(char *subnet_formatted, const char *subnet_raw)
>> +{
>> + char *ptr;
>> + long subnet_mask = strtol(subnet_raw, &ptr, 10);
>> + if ((ptr == NULL) || (*ptr != '\0') || (subnet_mask < 0) ||
>> + (subnet_mask > 255))
>> + return -1;
>> + snprintf(subnet_formatted, 1 + REQUEST_BYTE_SIZE, "0x%2.2x ",
>> + (uint) subnet_mask);
>> + return 0;
>> +}
>> +
>> +static int format_gateway(int *gateway_size, char *gateway_formatted,
>> + const char *gateway_raw)
>> +{
>> + return format_ip(gateway_size, gateway_formatted, gateway_raw);
>> +}
>> +
>> +/* Set the header parameters and the address sizes */
>> +static int format_request_header(char *request, int hw_size, int ip_type)
>> +{
>> + snprintf(request, 1 + strlen(COMMAND_SET), "%s", COMMAND_SET);
>> + snprintf(&request[REQUEST_BYTE_SIZE * OFFSET_COOKIE],
>> + 1 + strlen(COOKIE_VALUE), "%s", COOKIE_VALUE);
>> + snprintf(&request[REQUEST_BYTE_SIZE * OFFSET_VERSION],
>> + 1 + strlen(VERSION_VALUE), "%s", VERSION_VALUE);
>> +
>> + snprintf(&request[REQUEST_BYTE_SIZE * OFFSET_HW_ADDR_SIZE],
>> + 1 + strlen("0x06 "), "0x0%u ", hw_size);
>> + snprintf(&request[REQUEST_BYTE_SIZE * OFFSET_IP_ADDR_SIZE],
>> + 1 + strlen("0x04 "), "0x0%u ", ip_type == AF_INET ? 4 : 6);
>> +
>> + return 0;
>> +}
>> +
>> +static int format_request_static(char *pos, const char *ip_addr, int ip_type,
>> + const char *gateway, const char *subnet_mask)
>> +{
>> + int rc, size, n, expected_size;
>> +
>> + expected_size = ip_type == AF_INET ? 4 : 6;
>> +
>> + rc = format_ip(&size, pos, ip_addr);
>> + if (rc || (size != expected_size)) {
>> + fprintf(stderr, "Error parsing IP address (%d/%d)\n", rc, size);
>> + return -1;
>> + }
>> + n = REQUEST_BYTE_SIZE * expected_size;
>> + rc = format_subnet(pos + n, subnet_mask);
>> + if (rc) {
>> + fprintf(stderr, "Error parsing subnet mask\n");
>> + return -1;
>> + }
>> +
>> + n += REQUEST_BYTE_SIZE;
>> + rc = format_gateway(&size, pos + n, gateway);
>> + if (rc || (size != expected_size)) {
>> + fprintf(stderr, "Error parsing gateway address\n");
>> + return -1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int request_clear(char *request)
>> +{
>> + snprintf(request, 1 + (8 * REQUEST_BYTE_SIZE),
>> + "%s0x00 0x00 0x00 0x00\n", COMMAND_SET);
>> + printf(request);
>> + return 0;
>> +}
>> +
>> +static int request_get(char *request)
>> +{
>> + snprintf(request, 1 + (5 * REQUEST_BYTE_SIZE),
>> + "%s0x00\n", COMMAND_GET);
>> + printf(request);
>> + return 0;
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> + char *hw_addr, *ip_addr, *gateway, *subnet_mask, *pos;
>> + char request[MAX_REQUEST_SIZE];
>> + int rc, hw_size, ip_type = -1;
>> + enum method method = 0;
>> + int option_index = 0;
>> +
>> + hw_addr = ip_addr = gateway = subnet_mask = NULL;
>> +
>> + static const struct option long_options[] = {
>> + {"dhcp", no_argument, NULL, 'd'},
>> + {"static", no_argument, NULL, 's'},
>> + {"hwaddr", required_argument, NULL, 'h'},
>> + {"ip-version", required_argument, NULL, 'i'},
>> + {"ipaddr", required_argument, NULL, 'a'},
>> + {"gateway", required_argument, NULL, 'g'},
>> + {"subnet-mask", required_argument, NULL, 'm'},
>> + {"get", no_argument, NULL, 0},
>> + {"clear", no_argument, NULL, 0},
>> + {"help", no_argument, NULL, 0},
>> + { NULL, 0, NULL, 0},
>> + };
>> + static const char short_options[] = "dsph:i:a:g:m:";
>> +
>> + while (1) {
>> + int c = getopt_long(argc, argv, short_options, long_options,
>> + &option_index);
>> +
>> + if (c == EOF)
>> + break;
>> +
>> + switch (c) {
>> + case 0:
>> + /* These requests require no parsing */
>> + if (strncmp(long_options[option_index].name,
>> + "get", strlen("get")) == 0)
>> + return request_get(request);
>> + if (strncmp(long_options[option_index].name,
>> + "clear", strlen("clear")) == 0)
>> + return request_clear(request);
>> + if (strncmp(long_options[option_index].name,
>> + "help", strlen("help")) == 0) {
>> + printf("%s\n%s", USAGE_DEFAULT, USAGE_DETAIL);
>> + return 0;
>> + }
>> + break;
>> + case 'd':
>> + if (method != METHOD_NONE) {
>> + fprintf(stderr, "Options conflict\n");
>> + return -1;
>> + }
>> + method = METHOD_DHCP;
>> + break;
>> + case 's':
>> + if (method != METHOD_NONE) {
>> + fprintf(stderr, "Options conflict\n");
>> + return -1;
>> + }
>> + method = METHOD_STATIC;
>> + break;
>> + case 'h':
>> + hw_addr = optarg;
>> + break;
>> + case 'i':
>> + if (strncmp(optarg, "4", 1) == 0)
>> + ip_type = AF_INET;
>> + else if (strncmp(optarg, "6", 1) == 0)
>> + ip_type = AF_INET6;
>> + else {
>> + fprintf(stderr, "IP scheme unrecognised\n");
>> + return -1;
>> + }
>> + break;
>> + case 'a':
>> + ip_addr = optarg;
>> + break;
>> + case 'g':
>> + gateway = optarg;
>> + break;
>> + case 'm':
>> + subnet_mask = optarg;
>> + break;
>> + default:
>> + fprintf(stderr, USAGE_DEFAULT);
>> + return -1;
>> + }
>> + }
>> +
>> + if (optind != argc) {
>> + fprintf(stderr, USAGE_DEFAULT);
>> + return -1;
>> + }
>> +
>> + if (!method || !hw_addr) {
>> + fprintf(stderr, "Must specify a method and hardware address\n");
>> + fprintf(stderr, USAGE_DETAIL);
>> + return -1;
>> + }
>> +
>> + if (ip_type != AF_INET && ip_type != AF_INET6) {
>> + fprintf(stderr, "Must specify IP address scheme (IPv4/IPv6)\n");
>> + return -1;
>> + }
>> +
>> + if (method == METHOD_STATIC && (!ip_addr || !gateway || !subnet_mask)) {
>> + fprintf(stderr, "Static config missing parameters\n");
>> + return -1;
>> + }
>> +
>> + memset(request, '\0', MAX_REQUEST_SIZE);
>> +
>> + /* We only support 6-byte MAC addresses at the moment */
>> + pos = request;
>> + format_request_header(pos, 6, ip_type);
>> +
>> + pos += REQUEST_BYTE_SIZE * OFFSET_HW_ADDR;
>> + rc = format_hw(&hw_size, pos, hw_addr);
>> + if (rc || hw_size != 6) {
>> + fprintf(stderr, "Error parsing MAC address\n");
>> + return -1;
>> + }
>> +
>> + pos += REQUEST_BYTE_SIZE * hw_size;
>> + rc = snprintf(pos, 1 + strlen("0x00 0x00 "), "0x00 0x0%u ",
>> + method == METHOD_DHCP ? 0 : 1);
>> +
>> + if (method == METHOD_STATIC) {
>> + /* Set static parameters */
>> + pos += rc;
>> + rc = format_request_static(pos, ip_addr, ip_type, gateway,
>> + subnet_mask);
>> + if (rc) {
>> + fprintf(stderr, "Error formatting static section\n");
>> + return -1;
>> + }
>> + }
>> +
>> + /* Done! */
>> + printf(request);
>> + printf("\n");
>> + return 0;
>> +}
>> --
>> 2.7.4
>>
>> _______________________________________________
>> Petitboot <sammj> ruscur is playing correctly list
>> Petitboot at lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/petitboot
Looks good, no comments to add.
Reviewed-by: Suraj Jitindar Singh <sjitindarsingh at gmail.com>
More information about the Petitboot
mailing list