[RFC PATCH] util: Add a tool to generate network override requests

Samuel Mendoza-Jonas sam at mendozajonas.com
Tue Mar 22 16:37:40 AEDT 2016


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



More information about the Petitboot mailing list