Commit 0549709f authored by David Johnson's avatar David Johnson

A simple utility to reset remote nodes over (secure) RMCP/ASF from

Linux/FreeBSD, by talking directly to an ASF-enabled NIC.

The rmcp lib in rmcp.c is quick and dirty.  Since we didn't need IPMI, I
didn't make any real attempt to unentangle RMCP and ASF.  However, if we
do need it, we could easily generalize the code.
parent 57eb3828
#
# EMULAB-COPYRIGHT
# Copyright (c) 2006 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ../..
SUBDIR = tools/rmanage
include $(OBJDIR)/Makeconf
all: rmanage
include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS = -Wall
LDFLAGS = -lssl
rmanage: GNUmakefile rmanage.o
$(CC) $(CFLAGS) $(LDFLAGS) rmanage.o -o rmanage
cp rmanage rmanage.debug
strip rmanage
rmanage.o: rmanage.c rmcp.c
$(CC) -c -o rmanage.o $(CFLAGS) $<
rmcp.o: rmcp.c rmcp.h
$(CC) -c -o rmcp.o $(CFLAGS) $<
install boss-install: $(INSTALL_SBINDIR)/rmanage
clean:
rm -f *.o core rmanage rmanage.debug
This directory contains a utility that can use ASF (Alert Standards Format)
over secure (insecure also supported) RMCP (Remote Management and Control
Protocol) to send power control commands and queries to nodes with ASF-enabled
NICs. It does not provide full functionality of a "management console" (i.e.,
does not listen for autonomous heartbeats/alerts from the managed node), but
should still be useful for those wanting to do ASF control from Linux/UNIX.
* Requirements:
- motherboard/chipset support for ASF
- NIC support for ASF (including a utility that can enable ASF on your
NIC and customize the security settings for remote power control)
- Should compile fine under FreeBSD/Linux. Haven't tested on other
unixes, but there shouldn't be (much of) a problem. Will probably not
compile on cygwin/windows.
Files:
* rmanage.c: A simple RMCP client that supports sending ASF commands over
RMCP (also secure RMCP if desired). Note that power cycle/reset/off/on
commands are only supported over secure RMCP. You can also extract
capabilities, supported protocols, and current state, in addition to
sending power control commands (the output of rmanage for these commands is
pretty straightforward). The usage is pasted in below:
Usage: ./rmanage [-hHds] [-tmrkgu <arg>] -c <clientname> <command>
-h Print this message
-d Turn on debugging (more d's mean more debug info)
-s Use secure RMCP
-H Interpret keys as hex strings instead of char strings
-c host The hostname of the managed client
-t timeout Timeout (in seconds) for individual RMCP messages
(default: 3)
-m retries Retry N times for unacknowledged RMCP sends
(default: 3)
-r role Use this role (either 'operator' or 'administrator')
-k key Use this key with the role specified by '-r'
-g key Use this generation key
-u uid Send the specified username
command This argument performs an operation on the managed
client. The available commands are:
ping [Send an RMCP ping and display supported modes.]
capabilities [Get and display RMCP capabilities.]
state [Get and display current node power state.]
nop [Open a session if in secure mode; else nothing.]
reset [Send a warm reset command.]
* rmcp.h: Contains the "API" functions for talking RMCP to a NIC. This is a
pretty quick hack, but it attempts to use a context-based approach, kind of
like some of the Openssl libraries. Kind of. There's a bunch of very
simple high-level functions, but users can get into the guts if they want.
I don't recommend this; the code has not been tested beyond what is
necessary to get rmanage to work on Dell Optiplex 745s with Broadcom
gigabit NICs. It's a decently complete implementation of RMCP/ASF at the
protocol level, anyway.
* rmcp.c: A quick 'n dirty implementation of RMCP/RSP/RAKP/ASF. ASF is
pretty entangled with RMCP at the moment, but it wouldn't be hard to make
it nice and layered so that the library could support IPMI over RMCP, or
whatever...
David Johnson
<johnsond@cs.utah.edu>
December 5, 2006
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2006 University of Utah and the Flux Group.
* All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "rmcp.h"
extern char *optarg;
extern int optopt;
extern int opterr;
extern int optind;
#define COMMAND_PING "ping"
#define COMMAND_CAPABILITIES "capabilities"
#define COMMAND_STATE "state"
#define COMMAND_NOP "nop"
#define COMMAND_RESET "reset"
#define COMMAND_POWER_CYCLE "cycle"
#define COMMAND_POWER_DOWN "off"
#define COMMAND_POWER_UP "on"
#define COMMAND_COUNT 0x08
static char *commands[COMMAND_COUNT][2] =
{
{ COMMAND_PING, "Send an RMCP ping and display supported modes." },
{ COMMAND_CAPABILITIES, "Get and display RMCP capabilities." },
{ COMMAND_STATE, "Get and display current node power state." },
{ COMMAND_NOP, "Open a session if in secure mode; else nothing." },
{ COMMAND_RESET, "Send a warm reset command." },
{ COMMAND_POWER_CYCLE, "Send a full power cycle command." },
{ COMMAND_POWER_DOWN, "Send a power down command." },
{ COMMAND_POWER_UP, "Send a power up command." }
};
#define DEFAULT_TIMEOUT 3
#define DEFAULT_RETRIES 3
void usage(char *prog) {
int i;
fprintf(stderr,
"Usage: %s [-hHds] [-tmrkgu <arg>] -c <clientname> <command>\n"
"\n"
" -h Print this message\n"
" -d Turn on debugging (more d's mean more debug info)\n"
" -s Use secure RMCP\n"
" -H Interpret keys as hex strings instead of char strings\n"
" -c host The hostname of the managed client\n"
" -t timeout Timeout (in seconds) for individual RMCP messages\n"
" (default: %d)\n"
" -m retries Retry N times for unacknowledged RMCP sends\n"
" (default: %d)\n"
" -r role Use this role (either 'operator' or 'administrator')\n"
" -k key Use this key with the role specified by '-r'\n"
" -g key Use this generation key\n"
" -u uid Send the specified username\n"
"\n"
" command This argument performs an operation on the managed\n"
" client. The available commands are:\n",
prog,DEFAULT_TIMEOUT,DEFAULT_RETRIES);
for (i = 0; i < COMMAND_COUNT; ++i) {
fprintf(stderr,
" %s\t[%s]\n",
commands[i][0],commands[i][1]);
}
fprintf(stderr,"\n");
return;
}
int main(int argc,char **argv) {
rmcp_error_t retval;
rmcp_ctx_t *ctx;
rmcp_asf_supported_t *supported;
rmcp_asf_capabilities_t *capabilities;
rmcp_asf_sysstate_t *sysstate;
int c;
int debug = 0;
int secure = 0;
int hex_mode = 0;
char *client = NULL;
u_int8_t *rkey;
int rkey_len;
u_int8_t *gkey;
int gkey_len;
int timeout = DEFAULT_TIMEOUT;
int retries = DEFAULT_RETRIES;
char *role = NULL;
int roleno;
char *uid = NULL;
char *command = NULL;
int i;
while ((c = getopt(argc,argv,"c:t:m:r:k:g:u:hdsH")) != -1) {
switch (c) {
case 'h':
usage(argv[0]);
exit(0);
break;
case 'd':
++debug;
break;
case 's':
secure = 1;
break;
case 'H':
hex_mode = 1;
break;
case 'c':
client = optarg;
break;
case 't':
timeout = atoi(optarg);
break;
case 'm':
retries = atoi(optarg);
break;
case 'r':
role = optarg;
if (strcmp("operator",role) && strcmp("administrator",role)) {
fprintf(stderr,"ERROR: unknown role '%s'!\n",role);
exit(-1);
}
break;
case 'k':
/* rkey = (u_int8_t *)malloc((strlen(optarg)-1)*sizeof(u_int8_t)); */
/* memcpy(rkey,optarg,strlen(optarg)-1); */
/* rkey_len = strlen(optarg)-1; */
rkey = optarg;
rkey_len = strlen(optarg);
break;
case 'g':
gkey = optarg;
gkey_len = strlen(optarg);
/* gkey = (u_int8_t *)malloc((strlen(optarg)-1)*sizeof(u_int8_t)); */
/* memcpy(gkey,optarg,strlen(optarg)-1); */
/* gkey_len = strlen(optarg)-1; */
break;
case 'u':
uid = optarg;
break;
default:
fprintf(stderr,"ERROR: unknown option '%c'!\n",c);
usage(argv[0]);
exit(-1);
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
fprintf(stderr,"ERROR: required command argument is missing!\n");
exit(-1);
}
else {
command = argv[0];
}
if (!secure && (!strcmp(command,"reset") || !strcmp(command,"cycle")
|| !strcmp(command,"on") || !strcmp(command,"off"))) {
fprintf(stderr,"Command '%s' requires secure RMCP.\n",command);
exit(-3);
}
rmcp_set_debug_level(debug);
ctx = rmcp_ctx_init(timeout,retries);
if (secure) {
if (strcmp(role,"administrator") == 0) {
roleno = RMCP_ROLE_ADM;
}
else if (strcmp(role,"operator") == 0) {
roleno = RMCP_ROLE_OP;
}
else {
fprintf(stderr,"Unknown role '%s'!\n",role);
exit(-4);
}
/* rkey_len = gkey_len = 4; */
rmcp_ctx_setsecure(ctx,
roleno,
//rkey,rkey_len, //"1234",4, //rkey_len,
//gkey,gkey_len); //"1234",4); // gkey_len);
"1234",rkey_len,
"1234",gkey_len);
if (uid) {
rmcp_ctx_setuid(ctx,uid,strlen(uid)-1);
}
}
retval = rmcp_open(ctx,client);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Could not open connection to client %s: error 0x%x.\n",
client,retval);
exit(-5);
}
if (secure) {
retval = rmcp_start_secure_session(ctx);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Could not start secure session: error 0x%x\n",
retval);
exit(-6);
}
}
if (!strcmp(command,COMMAND_PING)) {
/* send a ping and print the supported protocols */
retval = rmcp_asf_ping(ctx,&supported);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Command '%s' failed: 0x%x\n",
command,
retval);
exit(-16);
}
rmcp_print_asf_supported(supported,NULL);
}
else if (!strcmp(command,COMMAND_CAPABILITIES)) {
/* get capabilities and print them */
retval = rmcp_asf_get_capabilities(ctx,&capabilities);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Command '%s' failed: 0x%x\n",
command,
retval);
exit(-16);
}
rmcp_print_asf_capabilities(capabilities,NULL);
}
else if (!strcmp(command,COMMAND_STATE)) {
/* get state and print it */
retval = rmcp_asf_get_sysstate(ctx,&sysstate);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Command '%s' failed: 0x%x\n",
command,
retval);
exit(-16);
}
rmcp_print_asf_sysstate(sysstate,NULL);
}
else if (!strcmp(command,COMMAND_NOP)) {
/* do nothing */
fprintf(stdout,
"As requested, nothing done%s.\n",
(secure)?" (except secure session establishment)":"");
}
else if (!strcmp(command,COMMAND_RESET)) {
/* reset */
retval = rmcp_asf_reset(ctx);
if (retval == RMCP_SUCCESS) {
fprintf(stdout,"Reset sent successfully.\n");
}
else {
fprintf(stderr,"Reset unsuccessful: 0x%x\n",retval);
exit(-12);
}
}
else if (!strcmp(command,COMMAND_POWER_CYCLE)) {
/* power cycle */
retval = rmcp_asf_power_cycle(ctx);
if (retval == RMCP_SUCCESS) {
fprintf(stdout,"Power cycle sent successfully.\n");
}
else {
fprintf(stderr,"Power cycle unsuccessful: 0x%x\n",retval);
exit(-12);
}
}
else if (!strcmp(command,COMMAND_POWER_UP)) {
/* power up */
retval = rmcp_asf_power_up(ctx);
if (retval == RMCP_SUCCESS) {
fprintf(stdout,"Power up sent successfully.\n");
}
else {
fprintf(stderr,"Power up unsuccessful: 0x%x\n",retval);
exit(-12);
}
}
else if (!strcmp(command,COMMAND_POWER_DOWN)) {
/* power down */
retval = rmcp_asf_power_down(ctx);
if (retval == RMCP_SUCCESS) {
fprintf(stdout,"Power down sent successfully.\n");
}
else {
fprintf(stderr,"Power down unsuccessful: 0x%x\n",retval);
exit(-12);
}
}
retval = rmcp_finalize(ctx);
if (retval != RMCP_SUCCESS) {
fprintf(stderr,
"Could not close session/connection: 0x%x\n",
retval);
exit(-10);
}
exit(0);
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment