summaryrefslogtreecommitdiff
path: root/src/plugins/lanplus
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/lanplus')
-rw-r--r--src/plugins/lanplus/Makefile.am45
-rw-r--r--src/plugins/lanplus/Makefile.in550
-rw-r--r--src/plugins/lanplus/asf.h75
-rw-r--r--src/plugins/lanplus/lanplus.c3680
-rw-r--r--src/plugins/lanplus/lanplus.h126
-rw-r--r--src/plugins/lanplus/lanplus_crypt.c934
-rw-r--r--src/plugins/lanplus/lanplus_crypt.h75
-rw-r--r--src/plugins/lanplus/lanplus_crypt_impl.c293
-rw-r--r--src/plugins/lanplus/lanplus_crypt_impl.h66
-rw-r--r--src/plugins/lanplus/lanplus_dump.c192
-rw-r--r--src/plugins/lanplus/lanplus_dump.h45
-rw-r--r--src/plugins/lanplus/lanplus_strings.c39
-rw-r--r--src/plugins/lanplus/rmcp.h82
13 files changed, 6202 insertions, 0 deletions
diff --git a/src/plugins/lanplus/Makefile.am b/src/plugins/lanplus/Makefile.am
new file mode 100644
index 0000000..428eb04
--- /dev/null
+++ b/src/plugins/lanplus/Makefile.am
@@ -0,0 +1,45 @@
+# Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = -I$(top_srcdir)/include
+
+EXTRA_LTLIBRARIES = libintf_lanplus.la
+noinst_LTLIBRARIES = @INTF_LANPLUS_LIB@
+libintf_lanplus_la_LIBADD = $(top_builddir)/lib/libipmitool.la
+libintf_lanplus_la_SOURCES = \
+ rmcp.h asf.h \
+ lanplus.c lanplus.h \
+ lanplus_strings.c \
+ lanplus_crypt.c lanplus_crypt.h \
+ lanplus_dump.h lanplus_dump.c \
+ lanplus_crypt_impl.h lanplus_crypt_impl.c
+
diff --git a/src/plugins/lanplus/Makefile.in b/src/plugins/lanplus/Makefile.in
new file mode 100644
index 0000000..6860f1b
--- /dev/null
+++ b/src/plugins/lanplus/Makefile.in
@@ -0,0 +1,550 @@
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+# ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+# INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+# PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+# SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+# FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+# OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+# SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+# OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+# PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+# LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+# EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = src/plugins/lanplus
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libintf_lanplus_la_DEPENDENCIES = $(top_builddir)/lib/libipmitool.la
+am_libintf_lanplus_la_OBJECTS = lanplus.lo lanplus_strings.lo \
+ lanplus_crypt.lo lanplus_dump.lo lanplus_crypt_impl.lo
+libintf_lanplus_la_OBJECTS = $(am_libintf_lanplus_la_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libintf_lanplus_la_SOURCES)
+DIST_SOURCES = $(libintf_lanplus_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+ARCH = @ARCH@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BASEDIR = @BASEDIR@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTRO = @DISTRO@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTF_BMC = @INTF_BMC@
+INTF_BMC_LIB = @INTF_BMC_LIB@
+INTF_DUMMY = @INTF_DUMMY@
+INTF_DUMMY_LIB = @INTF_DUMMY_LIB@
+INTF_FREE = @INTF_FREE@
+INTF_FREE_LIB = @INTF_FREE_LIB@
+INTF_IMB = @INTF_IMB@
+INTF_IMB_LIB = @INTF_IMB_LIB@
+INTF_LAN = @INTF_LAN@
+INTF_LANPLUS = @INTF_LANPLUS@
+INTF_LANPLUS_LIB = @INTF_LANPLUS_LIB@
+INTF_LAN_LIB = @INTF_LAN_LIB@
+INTF_LIPMI = @INTF_LIPMI@
+INTF_LIPMI_LIB = @INTF_LIPMI_LIB@
+INTF_OPEN = @INTF_OPEN@
+INTF_OPEN_LIB = @INTF_OPEN_LIB@
+INTF_SERIAL = @INTF_SERIAL@
+INTF_SERIAL_LIB = @INTF_SERIAL_LIB@
+IPMITOOL_INTF_LIB = @IPMITOOL_INTF_LIB@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OS = @OS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POW_LIB = @POW_LIB@
+PSTAMP = @PSTAMP@
+RANLIB = @RANLIB@
+RPMBUILD = @RPMBUILD@
+RPM_RELEASE = @RPM_RELEASE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_configure_args = @ac_configure_args@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+MAINTAINERCLEANFILES = Makefile.in
+INCLUDES = -I$(top_srcdir)/include
+EXTRA_LTLIBRARIES = libintf_lanplus.la
+noinst_LTLIBRARIES = @INTF_LANPLUS_LIB@
+libintf_lanplus_la_LIBADD = $(top_builddir)/lib/libipmitool.la
+libintf_lanplus_la_SOURCES = \
+ rmcp.h asf.h \
+ lanplus.c lanplus.h \
+ lanplus_strings.c \
+ lanplus_crypt.c lanplus_crypt.h \
+ lanplus_dump.h lanplus_dump.c \
+ lanplus_crypt_impl.h lanplus_crypt_impl.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/lanplus/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/plugins/lanplus/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libintf_lanplus.la: $(libintf_lanplus_la_OBJECTS) $(libintf_lanplus_la_DEPENDENCIES) $(EXTRA_libintf_lanplus_la_DEPENDENCIES)
+ $(LINK) $(libintf_lanplus_la_OBJECTS) $(libintf_lanplus_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_crypt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_crypt_impl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_dump.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanplus_strings.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/plugins/lanplus/asf.h b/src/plugins/lanplus/asf.h
new file mode 100644
index 0000000..7a30418
--- /dev/null
+++ b/src/plugins/lanplus/asf.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef IPMI_ASF_H
+#define IPMI_ASF_H
+
+#include <ipmitool/helper.h>
+#include "lanplus.h"
+
+#define ASF_RMCP_IANA 0x000011be
+
+#define ASF_TYPE_PING 0x80
+#define ASF_TYPE_PONG 0x40
+
+static const struct valstr asf_type_vals[] __attribute__((unused)) = {
+ { 0x10, "Reset" },
+ { 0x11, "Power-up" },
+ { 0x12, "Unconditional Power-down" },
+ { 0x13, "Power Cycle" },
+ { 0x40, "Presence Pong" },
+ { 0x41, "Capabilities Response" },
+ { 0x42, "System State Response" },
+ { 0x80, "Presence Ping" },
+ { 0x81, "Capabilities Request" },
+ { 0x82, "System State Request" },
+ { 0x00, NULL }
+};
+
+/* ASF message header */
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(1)
+#endif
+struct asf_hdr {
+ uint32_t iana;
+ uint8_t type;
+ uint8_t tag;
+ uint8_t __reserved;
+ uint8_t len;
+} ATTRIBUTE_PACKING;
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(0)
+#endif
+
+int handle_asf(struct ipmi_intf * intf, uint8_t * data, int data_len);
+
+#endif /* IPMI_ASF_H */
diff --git a/src/plugins/lanplus/lanplus.c b/src/plugins/lanplus/lanplus.c
new file mode 100644
index 0000000..27b9610
--- /dev/null
+++ b/src/plugins/lanplus/lanplus.c
@@ -0,0 +1,3680 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <time.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <ipmitool/helper.h>
+#include <ipmitool/log.h>
+#include <ipmitool/ipmi.h>
+#include <ipmitool/ipmi_lanp.h>
+#include <ipmitool/ipmi_channel.h>
+#include <ipmitool/ipmi_intf.h>
+#include <ipmitool/ipmi_strings.h>
+#include <ipmitool/hpm2.h>
+#include <ipmitool/bswap.h>
+#include <openssl/rand.h>
+
+#include "lanplus.h"
+#include "lanplus_crypt.h"
+#include "lanplus_crypt_impl.h"
+#include "lanplus_dump.h"
+#include "rmcp.h"
+#include "asf.h"
+
+/*
+ * LAN interface is required to support 45 byte request transactions and
+ * 42 byte response transactions.
+ */
+#define IPMI_LAN_MAX_REQUEST_SIZE 38 /* 45 - 7 */
+#define IPMI_LAN_MAX_RESPONSE_SIZE 34 /* 42 - 8 */
+
+extern const struct valstr ipmi_rakp_return_codes[];
+extern const struct valstr ipmi_priv_levels[];
+extern const struct valstr ipmi_auth_algorithms[];
+extern const struct valstr ipmi_integrity_algorithms[];
+extern const struct valstr ipmi_encryption_algorithms[];
+
+static struct ipmi_rq_entry * ipmi_req_entries;
+static struct ipmi_rq_entry * ipmi_req_entries_tail;
+
+
+static int ipmi_lanplus_setup(struct ipmi_intf * intf);
+static int ipmi_lanplus_keepalive(struct ipmi_intf * intf);
+static int ipmi_lan_send_packet(struct ipmi_intf * intf, uint8_t * data, int data_len);
+static struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf);
+static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf);
+static struct ipmi_rs * ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req);
+static struct ipmi_rs * ipmi_lanplus_send_payload(struct ipmi_intf * intf,
+ struct ipmi_v2_payload * payload);
+static void getIpmiPayloadWireRep(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * payload, /* in */
+ uint8_t * out,
+ struct ipmi_rq * req,
+ uint8_t rq_seq,
+ uint8_t curr_seq);
+static void getSolPayloadWireRep(
+ struct ipmi_intf * intf,
+ uint8_t * msg,
+ struct ipmi_v2_payload * payload);
+static void read_open_session_response(struct ipmi_rs * rsp, int offset);
+static void read_rakp2_message(struct ipmi_rs * rsp, int offset, uint8_t alg);
+static void read_rakp4_message(struct ipmi_rs * rsp, int offset, uint8_t alg);
+static void read_session_data(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s);
+static void read_session_data_v15(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s);
+static void read_session_data_v2x(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s);
+static void read_ipmi_response(struct ipmi_rs * rsp, int * offset);
+static void read_sol_packet(struct ipmi_rs * rsp, int * offset);
+static struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf);
+static struct ipmi_rs * ipmi_lanplus_send_sol(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * payload);
+static int check_sol_packet_for_new_data(
+ struct ipmi_intf * intf,
+ struct ipmi_rs *rsp);
+static void ack_sol_packet(
+ struct ipmi_intf * intf,
+ struct ipmi_rs * rsp);
+static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size);
+static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size);
+
+static uint8_t bridgePossible = 0;
+
+struct ipmi_intf ipmi_lanplus_intf = {
+ name: "lanplus",
+ desc: "IPMI v2.0 RMCP+ LAN Interface",
+ setup: ipmi_lanplus_setup,
+ open: ipmi_lanplus_open,
+ close: ipmi_lanplus_close,
+ sendrecv: ipmi_lanplus_send_ipmi_cmd,
+ recv_sol: ipmi_lanplus_recv_sol,
+ send_sol: ipmi_lanplus_send_sol,
+ keepalive: ipmi_lanplus_keepalive,
+ set_max_request_data_size: ipmi_lanp_set_max_rq_data_size,
+ set_max_response_data_size: ipmi_lanp_set_max_rp_data_size,
+ target_addr: IPMI_BMC_SLAVE_ADDR,
+};
+
+
+extern int verbose;
+
+
+
+/*
+ * lanplus_get_requested_ciphers
+ *
+ * Set the authentication, integrity and encryption algorithms based
+ * on the cipher suite ID. See table 22-19 in the IPMIv2 spec for the
+ * source of this information.
+ *
+ * param cipher_suite_id [in]
+ * param auth_alg [out]
+ * param integrity_alg [out]
+ * param crypt_alg [out]
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int lanplus_get_requested_ciphers(int cipher_suite_id,
+ uint8_t * auth_alg,
+ uint8_t * integrity_alg,
+ uint8_t * crypt_alg)
+{
+ if ((cipher_suite_id < 0) || (cipher_suite_id > 14))
+ return 1;
+
+ /* See table 22-19 for the source of the statement */
+ switch (cipher_suite_id)
+ {
+ case 0:
+ *auth_alg = IPMI_AUTH_RAKP_NONE;
+ *integrity_alg = IPMI_INTEGRITY_NONE;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 1:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
+ *integrity_alg = IPMI_INTEGRITY_NONE;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 2:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 3:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
+ *crypt_alg = IPMI_CRYPT_AES_CBC_128;
+ break;
+ case 4:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
+ *crypt_alg = IPMI_CRYPT_XRC4_128;
+ break;
+ case 5:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_SHA1;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96;
+ *crypt_alg = IPMI_CRYPT_XRC4_40;
+ break;
+ case 6:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_NONE;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 7:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 8:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
+ *crypt_alg = IPMI_CRYPT_AES_CBC_128;
+ break;
+ case 9:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
+ *crypt_alg = IPMI_CRYPT_XRC4_128;
+ break;
+ case 10:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128;
+ *crypt_alg = IPMI_CRYPT_XRC4_40;
+ break;
+ case 11:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_MD5_128;
+ *crypt_alg = IPMI_CRYPT_NONE;
+ break;
+ case 12:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_MD5_128;
+ *crypt_alg = IPMI_CRYPT_AES_CBC_128;
+ break;
+ case 13:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_MD5_128;
+ *crypt_alg = IPMI_CRYPT_XRC4_128;
+ break;
+ case 14:
+ *auth_alg = IPMI_AUTH_RAKP_HMAC_MD5;
+ *integrity_alg = IPMI_INTEGRITY_MD5_128;
+ *crypt_alg = IPMI_CRYPT_XRC4_40;
+ break;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Reverse the order of arbitrarily long strings of bytes
+ */
+void lanplus_swap(
+ uint8_t * buffer,
+ int length)
+{
+ int i;
+ uint8_t temp;
+
+ for (i =0; i < length/2; ++i)
+ {
+ temp = buffer[i];
+ buffer[i] = buffer[length - 1 - i];
+ buffer[length - 1 - i] = temp;
+ }
+}
+
+
+
+static const struct valstr plus_payload_types_vals[] = {
+ { IPMI_PAYLOAD_TYPE_IPMI, "IPMI (0)" }, // IPMI Message
+ { IPMI_PAYLOAD_TYPE_SOL, "SOL (1)" }, // SOL (Serial over LAN)
+ { IPMI_PAYLOAD_TYPE_OEM, "OEM (2)" }, // OEM Explicid
+
+ { IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST, "OpenSession Req (0x10)" },
+ { IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE,"OpenSession Resp (0x11)" },
+ { IPMI_PAYLOAD_TYPE_RAKP_1, "RAKP1 (0x12)" },
+ { IPMI_PAYLOAD_TYPE_RAKP_2, "RAKP2 (0x13)" },
+ { IPMI_PAYLOAD_TYPE_RAKP_3, "RAKP3 (0x14)" },
+ { IPMI_PAYLOAD_TYPE_RAKP_4, "RAKP4 (0x15)" },
+ { 0x00, NULL },
+};
+
+
+static struct ipmi_rq_entry *
+ipmi_req_add_entry(struct ipmi_intf * intf, struct ipmi_rq * req, uint8_t req_seq)
+{
+ struct ipmi_rq_entry * e;
+
+ e = malloc(sizeof(struct ipmi_rq_entry));
+ if (e == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return NULL;
+ }
+
+ memset(e, 0, sizeof(struct ipmi_rq_entry));
+ memcpy(&e->req, req, sizeof(struct ipmi_rq));
+
+ e->intf = intf;
+ e->rq_seq = req_seq;
+
+ if (ipmi_req_entries == NULL)
+ ipmi_req_entries = e;
+ else
+ ipmi_req_entries_tail->next = e;
+
+ ipmi_req_entries_tail = e;
+ lprintf(LOG_DEBUG+3, "added list entry seq=0x%02x cmd=0x%02x",
+ e->rq_seq, e->req.msg.cmd);
+ return e;
+}
+
+
+static struct ipmi_rq_entry *
+ipmi_req_lookup_entry(uint8_t seq, uint8_t cmd)
+{
+ struct ipmi_rq_entry * e = ipmi_req_entries;
+
+ while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) {
+ if (e == e->next)
+ return NULL;
+ e = e->next;
+ }
+ return e;
+}
+
+static void
+ipmi_req_remove_entry(uint8_t seq, uint8_t cmd)
+{
+ struct ipmi_rq_entry * p, * e, * saved_next_entry;
+
+ e = p = ipmi_req_entries;
+
+ while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) {
+ p = e;
+ e = e->next;
+ }
+ if (e) {
+ lprintf(LOG_DEBUG+3, "removed list entry seq=0x%02x cmd=0x%02x",
+ seq, cmd);
+ saved_next_entry = e->next;
+ p->next = (p->next == e->next) ? NULL : e->next;
+ /* If entry being removed is first in list, fix up list head */
+ if (ipmi_req_entries == e) {
+ if (ipmi_req_entries != p)
+ ipmi_req_entries = p;
+ else
+ ipmi_req_entries = saved_next_entry;
+ }
+ /* If entry being removed is last in list, fix up list tail */
+ if (ipmi_req_entries_tail == e) {
+ if (ipmi_req_entries_tail != p)
+ ipmi_req_entries_tail = p;
+ else
+ ipmi_req_entries_tail = NULL;
+ }
+
+ if (e->msg_data) {
+ free(e->msg_data);
+ e->msg_data = NULL;
+ }
+ free(e);
+ e = NULL;
+ }
+}
+
+static void
+ipmi_req_clear_entries(void)
+{
+ struct ipmi_rq_entry * p, * e;
+
+ e = ipmi_req_entries;
+ while (e) {
+ lprintf(LOG_DEBUG+3, "cleared list entry seq=0x%02x cmd=0x%02x",
+ e->rq_seq, e->req.msg.cmd);
+ p = e->next;
+ free(e);
+ e = p;
+ }
+}
+
+
+int
+ipmi_lan_send_packet(
+ struct ipmi_intf * intf,
+ uint8_t * data, int
+ data_len)
+{
+ if (verbose >= 5)
+ printbuf(data, data_len, ">> sending packet");
+
+ return send(intf->fd, data, data_len, 0);
+}
+
+
+
+struct ipmi_rs *
+ipmi_lan_recv_packet(struct ipmi_intf * intf)
+{
+ static struct ipmi_rs rsp;
+ fd_set read_set, err_set;
+ struct timeval tmout;
+ int ret;
+
+ FD_ZERO(&read_set);
+ FD_SET(intf->fd, &read_set);
+
+ FD_ZERO(&err_set);
+ FD_SET(intf->fd, &err_set);
+
+ tmout.tv_sec = intf->session->timeout;
+ tmout.tv_usec = 0;
+
+ ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout);
+ if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set))
+ return NULL;
+
+ /* the first read may return ECONNREFUSED because the rmcp ping
+ * packet--sent to UDP port 623--will be processed by both the
+ * BMC and the OS.
+ *
+ * The problem with this is that the ECONNREFUSED takes
+ * priority over any other received datagram; that means that
+ * the Connection Refused shows up _before_ the response packet,
+ * regardless of the order they were sent out. (unless the
+ * response is read before the connection refused is returned)
+ */
+ ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0);
+
+ if (ret < 0) {
+ FD_ZERO(&read_set);
+ FD_SET(intf->fd, &read_set);
+
+ FD_ZERO(&err_set);
+ FD_SET(intf->fd, &err_set);
+
+ tmout.tv_sec = intf->session->timeout;
+ tmout.tv_usec = 0;
+
+ ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout);
+ if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set))
+ return NULL;
+
+ ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0);
+ if (ret < 0)
+ return NULL;
+ }
+
+ if (ret == 0)
+ return NULL;
+
+ rsp.data[ret] = '\0';
+ rsp.data_len = ret;
+
+ if (verbose >= 5)
+ printbuf(rsp.data, rsp.data_len, "<< received packet");
+
+ return &rsp;
+}
+
+
+
+/*
+ * parse response RMCP "pong" packet
+ *
+ * return -1 if ping response not received
+ * returns 0 if IPMI is NOT supported
+ * returns 1 if IPMI is supported
+ *
+ * udp.source = 0x026f // RMCP_UDP_PORT
+ * udp.dest = ? // udp.source from rmcp-ping
+ * udp.len = ?
+ * udp.check = ?
+ * rmcp.ver = 0x06 // RMCP Version 1.0
+ * rmcp.__res = 0x00 // RESERVED
+ * rmcp.seq = 0xff // no RMCP ACK
+ * rmcp.class = 0x06 // RMCP_CLASS_ASF
+ * asf.iana = 0x000011be // ASF_RMCP_IANA
+ * asf.type = 0x40 // ASF_TYPE_PONG
+ * asf.tag = ? // asf.tag from rmcp-ping
+ * asf.__res = 0x00 // RESERVED
+ * asf.len = 0x10 // 16 bytes
+ * asf.data[3:0]= 0x000011be // IANA# = RMCP_ASF_IANA if no OEM
+ * asf.data[7:4]= 0x00000000 // OEM-defined (not for IPMI)
+ * asf.data[8] = 0x81 // supported entities
+ * // [7]=IPMI [6:4]=RES [3:0]=ASF_1.0
+ * asf.data[9] = 0x00 // supported interactions (reserved)
+ * asf.data[f:a]= 0x000000000000
+ */
+static int
+ipmi_handle_pong(struct ipmi_intf * intf, struct ipmi_rs * rsp)
+{
+ struct rmcp_pong {
+ struct rmcp_hdr rmcp;
+ struct asf_hdr asf;
+ uint32_t iana;
+ uint32_t oem;
+ uint8_t sup_entities;
+ uint8_t sup_interact;
+ uint8_t reserved[6];
+ } * pong;
+
+ if (!rsp)
+ return -1;
+
+ pong = (struct rmcp_pong *)rsp->data;
+
+ if (verbose)
+ printf("Received IPMI/RMCP response packet: "
+ "IPMI%s Supported\n",
+ (pong->sup_entities & 0x80) ? "" : " NOT");
+
+ if (verbose > 1)
+ printf(" ASF Version %s\n"
+ " RMCP Version %s\n"
+ " RMCP Sequence %d\n"
+ " IANA Enterprise %lu\n\n",
+ (pong->sup_entities & 0x01) ? "1.0" : "unknown",
+ (pong->rmcp.ver == 6) ? "1.0" : "unknown",
+ pong->rmcp.seq,
+ (unsigned long)ntohl(pong->iana));
+
+ return (pong->sup_entities & 0x80) ? 1 : 0;
+}
+
+
+/* build and send RMCP presence ping packet
+ *
+ * RMCP ping
+ *
+ * udp.source = ?
+ * udp.dest = 0x026f // RMCP_UDP_PORT
+ * udp.len = ?
+ * udp.check = ?
+ * rmcp.ver = 0x06 // RMCP Version 1.0
+ * rmcp.__res = 0x00 // RESERVED
+ * rmcp.seq = 0xff // no RMCP ACK
+ * rmcp.class = 0x06 // RMCP_CLASS_ASF
+ * asf.iana = 0x000011be // ASF_RMCP_IANA
+ * asf.type = 0x80 // ASF_TYPE_PING
+ * asf.tag = ? // ASF sequence number
+ * asf.__res = 0x00 // RESERVED
+ * asf.len = 0x00
+ *
+ */
+int
+ipmiv2_lan_ping(struct ipmi_intf * intf)
+{
+ struct asf_hdr asf_ping = {
+ .iana = htonl(ASF_RMCP_IANA),
+ .type = ASF_TYPE_PING,
+ };
+ struct rmcp_hdr rmcp_ping = {
+ .ver = RMCP_VERSION_1,
+ .class = RMCP_CLASS_ASF,
+ .seq = 0xff,
+ };
+ uint8_t * data;
+ int len = sizeof(rmcp_ping) + sizeof(asf_ping);
+ int rv;
+
+ data = malloc(len);
+ if (data == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return -1;
+ }
+ memset(data, 0, len);
+ memcpy(data, &rmcp_ping, sizeof(rmcp_ping));
+ memcpy(data+sizeof(rmcp_ping), &asf_ping, sizeof(asf_ping));
+
+ lprintf(LOG_DEBUG, "Sending IPMI/RMCP presence ping packet");
+
+ rv = ipmi_lan_send_packet(intf, data, len);
+
+ free(data);
+ data = NULL;
+
+ if (rv < 0) {
+ lprintf(LOG_ERR, "Unable to send IPMI presence ping packet");
+ return -1;
+ }
+
+ if (ipmi_lan_poll_recv(intf) == 0)
+ return 0;
+
+ return 1;
+}
+
+
+/**
+ *
+ * ipmi_lan_poll_recv
+ *
+ * Receive whatever comes back. Ignore received packets that don't correspond
+ * to a request we've sent.
+ *
+ * Returns: the ipmi_rs packet describing the/a reponse we expect.
+ */
+static struct ipmi_rs *
+ipmi_lan_poll_recv(struct ipmi_intf * intf)
+{
+ struct rmcp_hdr rmcp_rsp;
+ struct ipmi_rs * rsp;
+ struct ipmi_session * session = intf->session;
+ int offset, rv;
+ uint16_t payload_size;
+ uint8_t ourAddress = intf->my_addr;
+
+ if (ourAddress == 0) {
+ ourAddress = IPMI_BMC_SLAVE_ADDR;
+ }
+
+ rsp = ipmi_lan_recv_packet(intf);
+
+ /*
+ * Not positive why we're looping. Do we sometimes get stuff we don't
+ * expect?
+ */
+ while (rsp != NULL) {
+
+ /* parse response headers */
+ memcpy(&rmcp_rsp, rsp->data, 4);
+
+ if (rmcp_rsp.class == RMCP_CLASS_ASF) {
+ /* might be ping response packet */
+ rv = ipmi_handle_pong(intf, rsp);
+ return (rv <= 0) ? NULL : rsp;
+ }
+
+ if (rmcp_rsp.class != RMCP_CLASS_IPMI) {
+ lprintf(LOG_DEBUG, "Invalid RMCP class: %x",
+ rmcp_rsp.class);
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+
+ /*
+ * The authtype / payload type determines what we are receiving
+ */
+ offset = 4;
+
+
+ /*--------------------------------------------------------------------
+ *
+ * The current packet could be one of several things:
+ *
+ * 1) An IPMI 1.5 packet (the response to our GET CHANNEL
+ * AUTHENTICATION CAPABILITIES request)
+ * 2) An RMCP+ message with an IPMI reponse payload
+ * 3) AN RMCP+ open session response
+ * 4) An RAKP-2 message (response to an RAKP 1 message)
+ * 5) An RAKP-4 message (response to an RAKP 3 message)
+ * 6) A Serial Over LAN packet
+ * 7) An Invalid packet (one that doesn't match a request)
+ * -------------------------------------------------------------------
+ */
+
+ read_session_data(rsp, &offset, intf->session);
+
+ if (lanplus_has_valid_auth_code(rsp, intf->session) == 0)
+ {
+ lprintf(LOG_ERR, "ERROR: Received message with invalid authcode!");
+ rsp = ipmi_lan_recv_packet(intf);
+ assert(0);
+ //continue;
+ }
+
+ if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) &&
+ (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) &&
+ (rsp->session.bEncrypted))
+
+ {
+ lanplus_decrypt_payload(session->v2_data.crypt_alg,
+ session->v2_data.k2,
+ rsp->data + offset,
+ rsp->session.msglen,
+ rsp->data + offset,
+ &payload_size);
+ }
+ else
+ payload_size = rsp->session.msglen;
+
+
+ /*
+ * Handle IPMI responses (case #1 and #2) -- all IPMI reponses
+ */
+ if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI)
+ {
+ struct ipmi_rq_entry * entry;
+ int payload_start = offset;
+ int extra_data_length;
+ read_ipmi_response(rsp, &offset);
+
+ lprintf(LOG_DEBUG+1, "<< IPMI Response Session Header");
+ lprintf(LOG_DEBUG+1, "<< Authtype : %s",
+ val2str(rsp->session.authtype, ipmi_authtype_session_vals));
+ lprintf(LOG_DEBUG+1, "<< Payload type : %s",
+ val2str(rsp->session.payloadtype, plus_payload_types_vals));
+ lprintf(LOG_DEBUG+1, "<< Session ID : 0x%08lx",
+ (long)rsp->session.id);
+ lprintf(LOG_DEBUG+1, "<< Sequence : 0x%08lx",
+ (long)rsp->session.seq);
+ lprintf(LOG_DEBUG+1, "<< IPMI Msg/Payload Length : %d",
+ rsp->session.msglen);
+ lprintf(LOG_DEBUG+1, "<< IPMI Response Message Header");
+ lprintf(LOG_DEBUG+1, "<< Rq Addr : %02x",
+ rsp->payload.ipmi_response.rq_addr);
+ lprintf(LOG_DEBUG+1, "<< NetFn : %02x",
+ rsp->payload.ipmi_response.netfn);
+ lprintf(LOG_DEBUG+1, "<< Rq LUN : %01x",
+ rsp->payload.ipmi_response.rq_lun);
+ lprintf(LOG_DEBUG+1, "<< Rs Addr : %02x",
+ rsp->payload.ipmi_response.rs_addr);
+ lprintf(LOG_DEBUG+1, "<< Rq Seq : %02x",
+ rsp->payload.ipmi_response.rq_seq);
+ lprintf(LOG_DEBUG+1, "<< Rs Lun : %01x",
+ rsp->payload.ipmi_response.rs_lun);
+ lprintf(LOG_DEBUG+1, "<< Command : %02x",
+ rsp->payload.ipmi_response.cmd);
+ lprintf(LOG_DEBUG+1, "<< Compl Code : 0x%02x",
+ rsp->ccode);
+
+ /* Are we expecting this packet? */
+ entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq,
+ rsp->payload.ipmi_response.cmd);
+
+ if (entry != NULL) {
+ lprintf(LOG_DEBUG+2, "IPMI Request Match found");
+ if ( intf->target_addr != intf->my_addr &&
+ bridgePossible &&
+ rsp->data_len &&
+ rsp->payload.ipmi_response.cmd == 0x34 &&
+ (rsp->payload.ipmi_response.netfn == 0x06 ||
+ rsp->payload.ipmi_response.netfn == 0x07) &&
+ rsp->payload.ipmi_response.rs_lun == 0 )
+ {
+ /* Check completion code */
+ if (rsp->data[offset-1] == 0)
+ {
+ lprintf(LOG_DEBUG, "Bridged command answer,"
+ " waiting for next answer... ");
+ ipmi_req_remove_entry(
+ rsp->payload.ipmi_response.rq_seq,
+ rsp->payload.ipmi_response.cmd);
+ return ipmi_lan_poll_recv(intf);
+ }
+ else
+ {
+ lprintf(LOG_DEBUG, "WARNING: Bridged "
+ "cmd ccode = 0x%02x",
+ rsp->data[offset-1]);
+ }
+
+ if (rsp->data_len &&
+ rsp->payload.ipmi_response.cmd == 0x34) {
+ memcpy(rsp->data, &rsp->data[offset],
+ (rsp->data_len-offset));
+ if (verbose > 2)
+ printbuf( &rsp->data[offset],
+ (rsp->data_len-offset),
+ "bridge command response");
+ }
+ }
+
+ ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq,
+ rsp->payload.ipmi_response.cmd);
+ } else {
+ lprintf(LOG_INFO, "IPMI Request Match NOT FOUND");
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+ /*
+ * Good packet. Shift response data to start of array.
+ * rsp->data becomes the variable length IPMI response data
+ * rsp->data_len becomes the length of that data
+ */
+ extra_data_length = payload_size - (offset - payload_start) - 1;
+ if (rsp != NULL && extra_data_length)
+ {
+ rsp->data_len = extra_data_length;
+ memmove(rsp->data, rsp->data + offset, extra_data_length);
+ }
+ else
+ rsp->data_len = 0;
+
+ break;
+ }
+
+
+ /*
+ * Open Response
+ */
+ else if (rsp->session.payloadtype ==
+ IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE)
+ {
+ if (session->v2_data.session_state !=
+ LANPLUS_STATE_OPEN_SESSION_SENT)
+ {
+ lprintf(LOG_ERR, "Error: Received an Unexpected Open Session "
+ "Response");
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+ read_open_session_response(rsp, offset);
+ break;
+ }
+
+
+ /*
+ * RAKP 2
+ */
+ else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2)
+ {
+ if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT)
+ {
+ lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 2 message");
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+ read_rakp2_message(rsp, offset, session->v2_data.auth_alg);
+ break;
+ }
+
+
+ /*
+ * RAKP 4
+ */
+ else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4)
+ {
+ if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT)
+ {
+ lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 4 message");
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+ read_rakp4_message(rsp, offset, session->v2_data.auth_alg);
+ break;
+ }
+
+
+ /*
+ * SOL
+ */
+ else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)
+ {
+ int payload_start = offset;
+ int extra_data_length;
+
+ if (session->v2_data.session_state != LANPLUS_STATE_ACTIVE)
+ {
+ lprintf(LOG_ERR, "Error: Received an Unexpected SOL packet");
+ rsp = ipmi_lan_recv_packet(intf);
+ continue;
+ }
+
+ read_sol_packet(rsp, &offset);
+ extra_data_length = payload_size - (offset - payload_start);
+ if (rsp && extra_data_length)
+ {
+ rsp->data_len = extra_data_length;
+ memmove(rsp->data, rsp->data + offset, extra_data_length);
+ }
+ else
+ rsp->data_len = 0;
+
+ break;
+ }
+
+ else
+ {
+ lprintf(LOG_ERR, "Invalid RMCP+ payload type : 0x%x",
+ rsp->session.payloadtype);
+ assert(0);
+ }
+ }
+
+ return rsp;
+}
+
+
+
+/*
+ * read_open_session_reponse
+ *
+ * Initialize the ipmi_rs from the IPMI 2.x open session response data.
+ *
+ * The offset should point to the first byte of the the Open Session Response
+ * payload when this function is called.
+ *
+ * param rsp [in/out] reading from the data and writing to the open_session_response
+ * section
+ * param offset [in] tells us where the Open Session Response payload starts
+ *
+ * returns 0 on success, 1 on error
+ */
+void
+read_open_session_response(struct ipmi_rs * rsp, int offset)
+{
+ memset(&rsp->payload.open_session_response, 0,
+ sizeof(rsp->payload.open_session_response));
+
+ /* Message tag */
+ rsp->payload.open_session_response.message_tag = rsp->data[offset];
+
+ /* RAKP reponse code */
+ rsp->payload.open_session_response.rakp_return_code = rsp->data[offset + 1];
+
+ /* Maximum privilege level */
+ rsp->payload.open_session_response.max_priv_level = rsp->data[offset + 2];
+
+ /*** offset + 3 is reserved ***/
+
+ /* Remote console session ID */
+ memcpy(&(rsp->payload.open_session_response.console_id),
+ rsp->data + offset + 4,
+ 4);
+ #if WORDS_BIGENDIAN
+ rsp->payload.open_session_response.console_id =
+ BSWAP_32(rsp->payload.open_session_response.console_id);
+ #endif
+
+ /* only tag, status, privlvl, and console id are returned if error */
+ if (rsp->payload.open_session_response.rakp_return_code !=
+ IPMI_RAKP_STATUS_NO_ERRORS)
+ return;
+
+ /* BMC session ID */
+ memcpy(&(rsp->payload.open_session_response.bmc_id),
+ rsp->data + offset + 8,
+ 4);
+ #if WORDS_BIGENDIAN
+ rsp->payload.open_session_response.bmc_id =
+ BSWAP_32(rsp->payload.open_session_response.bmc_id);
+ #endif
+
+ /* And of course, our negotiated algorithms */
+ rsp->payload.open_session_response.auth_alg = rsp->data[offset + 16];
+ rsp->payload.open_session_response.integrity_alg = rsp->data[offset + 24];
+ rsp->payload.open_session_response.crypt_alg = rsp->data[offset + 32];
+}
+
+
+
+/*
+ * read_rakp2_message
+ *
+ * Initialize the ipmi_rs from the IPMI 2.x RAKP 2 message
+ *
+ * The offset should point the first byte of the the RAKP 2 payload when this
+ * function is called.
+ *
+ * param rsp [in/out] reading from the data variable and writing to the rakp 2
+ * section
+ * param offset [in] tells us where hte rakp2 payload starts
+ * param auth_alg [in] describes the authentication algorithm was agreed upon in
+ * the open session request/response phase. We need to know that here so
+ * that we know how many bytes (if any) to read fromt the packet.
+ *
+ * returns 0 on success, 1 on error
+ */
+void
+read_rakp2_message(
+ struct ipmi_rs * rsp,
+ int offset,
+ uint8_t auth_alg)
+{
+ int i;
+
+ /* Message tag */
+ rsp->payload.rakp2_message.message_tag = rsp->data[offset];
+
+ /* RAKP reponse code */
+ rsp->payload.rakp2_message.rakp_return_code = rsp->data[offset + 1];
+
+ /* Console session ID */
+ memcpy(&(rsp->payload.rakp2_message.console_id),
+ rsp->data + offset + 4,
+ 4);
+ #if WORDS_BIGENDIAN
+ rsp->payload.rakp2_message.console_id =
+ BSWAP_32(rsp->payload.rakp2_message.console_id);
+ #endif
+
+ /* BMC random number */
+ memcpy(&(rsp->payload.rakp2_message.bmc_rand),
+ rsp->data + offset + 8,
+ 16);
+ #if WORDS_BIGENDIAN
+ lanplus_swap(rsp->payload.rakp2_message.bmc_rand, 16);
+ #endif
+
+ /* BMC GUID */
+ memcpy(&(rsp->payload.rakp2_message.bmc_guid),
+ rsp->data + offset + 24,
+ 16);
+ #if WORDS_BIGENDIAN
+ lanplus_swap(rsp->payload.rakp2_message.bmc_guid, 16);
+ #endif
+
+ /* Key exchange authentication code */
+ switch (auth_alg)
+ {
+ case IPMI_AUTH_RAKP_NONE:
+ /* Nothing to do here */
+ break;
+
+ case IPMI_AUTH_RAKP_HMAC_SHA1:
+ /* We need to copy 20 bytes */
+ for (i = 0; i < 20; ++i)
+ rsp->payload.rakp2_message.key_exchange_auth_code[i] =
+ rsp->data[offset + 40 + i];
+ break;
+
+ case IPMI_AUTH_RAKP_HMAC_MD5:
+ lprintf(LOG_ERR, "read_rakp2_message: no support for "
+ "IPMI_AUTH_RAKP_HMAC_MD5");
+ assert(0);
+ break;
+ }
+}
+
+
+
+/*
+ * read_rakp4_message
+ *
+ * Initialize the ipmi_rs from the IPMI 2.x RAKP 4 message
+ *
+ * The offset should point the first byte of the the RAKP 4 payload when this
+ * function is called.
+ *
+ * param rsp [in/out] reading from the data variable and writing to the rakp
+ * 4 section
+ * param offset [in] tells us where hte rakp4 payload starts
+ * param integrity_alg [in] describes the authentication algorithm was
+ * agreed upon in the open session request/response phase. We need
+ * to know that here so that we know how many bytes (if any) to read
+ * from the packet.
+ *
+ * returns 0 on success, 1 on error
+ */
+void
+read_rakp4_message(
+ struct ipmi_rs * rsp,
+ int offset,
+ uint8_t auth_alg)
+{
+ int i;
+
+ /* Message tag */
+ rsp->payload.rakp4_message.message_tag = rsp->data[offset];
+
+ /* RAKP reponse code */
+ rsp->payload.rakp4_message.rakp_return_code = rsp->data[offset + 1];
+
+ /* Console session ID */
+ memcpy(&(rsp->payload.rakp4_message.console_id),
+ rsp->data + offset + 4,
+ 4);
+ #if WORDS_BIGENDIAN
+ rsp->payload.rakp4_message.console_id =
+ BSWAP_32(rsp->payload.rakp4_message.console_id);
+ #endif
+
+
+ /* Integrity check value */
+ switch (auth_alg)
+ {
+ case IPMI_AUTH_RAKP_NONE:
+ /* Nothing to do here */
+ break;
+
+ case IPMI_AUTH_RAKP_HMAC_SHA1:
+ /* We need to copy 12 bytes */
+ for (i = 0; i < 12; ++i)
+ rsp->payload.rakp4_message.integrity_check_value[i] =
+ rsp->data[offset + 8 + i];
+ break;
+
+ case IPMI_AUTH_RAKP_HMAC_MD5:
+ lprintf(LOG_ERR, "read_rakp4_message: no support "
+ "for authentication algorithm 0x%x", auth_alg);
+ assert(0);
+ break;
+ }
+}
+
+
+
+
+/*
+ * read_session_data
+ *
+ * Initialize the ipmi_rsp from the session data in the packet
+ *
+ * The offset should point the first byte of the the IPMI session when this
+ * function is called.
+ *
+ * param rsp [in/out] we read from the data buffer and populate the session
+ * specific fields.
+ * param offset [in/out] should point to the beginning of the session when
+ * this function is called. The offset will be adjusted to
+ * point to the end of the session when this function exits.
+ * param session holds our session state
+ */
+void
+read_session_data(
+ struct ipmi_rs * rsp,
+ int * offset,
+ struct ipmi_session * s)
+{
+ /* We expect to read different stuff depending on the authtype */
+ rsp->session.authtype = rsp->data[*offset];
+
+ if (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS)
+ read_session_data_v2x(rsp, offset, s);
+ else
+ read_session_data_v15(rsp, offset, s);
+}
+
+
+
+/*
+ * read_session_data_v2x
+ *
+ * Initialize the ipmi_rsp from the v2.x session header of the packet.
+ *
+ * The offset should point to the first byte of the the IPMI session when this
+ * function is called. When this function exits, offset will point to the
+ * start of payload.
+ *
+ * Should decrypt and perform integrity checking here?
+ *
+ * param rsp [in/out] we read from the data buffer and populate the session
+ * specific fields.
+ * param offset [in/out] should point to the beginning of the session when this
+ * function is called. The offset will be adjusted to point to
+ * the end of the session when this function exits.
+ * param s holds our session state
+ */
+void
+read_session_data_v2x(
+ struct ipmi_rs * rsp,
+ int * offset,
+ struct ipmi_session * s)
+{
+ rsp->session.authtype = rsp->data[(*offset)++];
+
+ rsp->session.bEncrypted = (rsp->data[*offset] & 0x80 ? 1 : 0);
+ rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0);
+
+
+ /* Payload type */
+ rsp->session.payloadtype = rsp->data[(*offset)++] & 0x3F;
+
+ /* Session ID */
+ memcpy(&rsp->session.id, rsp->data + *offset, 4);
+ *offset += 4;
+ #if WORDS_BIGENDIAN
+ rsp->session.id = BSWAP_32(rsp->session.id);
+ #endif
+
+
+ /*
+ * Verify that the session ID is what we think it should be
+ */
+ if ((s->v2_data.session_state == LANPLUS_STATE_ACTIVE) &&
+ (rsp->session.id != s->v2_data.console_id))
+ {
+ lprintf(LOG_ERR, "packet session id 0x%x does not "
+ "match active session 0x%0x",
+ rsp->session.id, s->v2_data.console_id);
+ assert(0);
+ }
+
+
+ /* Ignored, so far */
+ memcpy(&rsp->session.seq, rsp->data + *offset, 4);
+ *offset += 4;
+ #if WORDS_BIGENDIAN
+ rsp->session.seq = BSWAP_32(rsp->session.seq);
+ #endif
+
+ memcpy(&rsp->session.msglen, rsp->data + *offset, 2);
+ *offset += 2;
+ #if WORDS_BIGENDIAN
+ rsp->session.msglen = BSWAP_16(rsp->session.msglen);
+ #endif
+}
+
+
+
+/*
+ * read_session_data_v15
+ *
+ * Initialize the ipmi_rsp from the session header of the packet.
+ *
+ * The offset should point the first byte of the the IPMI session when this
+ * function is called. When this function exits, the offset will point to
+ * the start of the IPMI message.
+ *
+ * param rsp [in/out] we read from the data buffer and populate the session
+ * specific fields.
+ * param offset [in/out] should point to the beginning of the session when this
+ * function is called. The offset will be adjusted to point to the
+ * end of the session when this function exits.
+ * param s holds our session state
+ */
+void read_session_data_v15(
+ struct ipmi_rs * rsp,
+ int * offset,
+ struct ipmi_session * s)
+{
+ /* All v15 messages are IPMI messages */
+ rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI;
+
+ rsp->session.authtype = rsp->data[(*offset)++];
+
+ /* All v15 messages that we will receive are unencrypted/unauthenticated */
+ rsp->session.bEncrypted = 0;
+ rsp->session.bAuthenticated = 0;
+
+ /* skip the session id and sequence number fields */
+ *offset += 8;
+
+ /* This is the size of the whole payload */
+ rsp->session.msglen = rsp->data[(*offset)++];
+}
+
+
+
+/*
+ * read_ipmi_response
+ *
+ * Initialize the ipmi_rs from with the IPMI response specific data
+ *
+ * The offset should point the first byte of the the IPMI payload when this
+ * function is called.
+ *
+ * param rsp [in/out] we read from the data buffer and populate the IPMI
+ * specific fields.
+ * param offset [in/out] should point to the beginning of the IPMI payload when
+ * this function is called.
+ */
+void read_ipmi_response(struct ipmi_rs * rsp, int * offset)
+{
+ /*
+ * The data here should be decrypted by now.
+ */
+ rsp->payload.ipmi_response.rq_addr = rsp->data[(*offset)++];
+ rsp->payload.ipmi_response.netfn = rsp->data[*offset] >> 2;
+ rsp->payload.ipmi_response.rq_lun = rsp->data[(*offset)++] & 0x3;
+ (*offset)++; /* checksum */
+ rsp->payload.ipmi_response.rs_addr = rsp->data[(*offset)++];
+ rsp->payload.ipmi_response.rq_seq = rsp->data[*offset] >> 2;
+ rsp->payload.ipmi_response.rs_lun = rsp->data[(*offset)++] & 0x3;
+ rsp->payload.ipmi_response.cmd = rsp->data[(*offset)++];
+ rsp->ccode = rsp->data[(*offset)++];
+
+}
+
+
+
+/*
+ * read_sol_packet
+ *
+ * Initialize the ipmi_rs with the SOL response data
+ *
+ * The offset should point the first byte of the the SOL payload when this
+ * function is called.
+ *
+ * param rsp [in/out] we read from the data buffer and populate the
+ * SOL specific fields.
+ * param offset [in/out] should point to the beginning of the SOL payload
+ * when this function is called.
+ */
+void read_sol_packet(struct ipmi_rs * rsp, int * offset)
+{
+
+ /*
+ * The data here should be decrypted by now.
+ */
+ rsp->payload.sol_packet.packet_sequence_number =
+ rsp->data[(*offset)++] & 0x0F;
+
+ rsp->payload.sol_packet.acked_packet_number =
+ rsp->data[(*offset)++] & 0x0F;
+
+ rsp->payload.sol_packet.accepted_character_count =
+ rsp->data[(*offset)++];
+
+ rsp->payload.sol_packet.is_nack =
+ rsp->data[*offset] & 0x40;
+
+ rsp->payload.sol_packet.transfer_unavailable =
+ rsp->data[*offset] & 0x20;
+
+ rsp->payload.sol_packet.sol_inactive =
+ rsp->data[*offset] & 0x10;
+
+ rsp->payload.sol_packet.transmit_overrun =
+ rsp->data[*offset] & 0x08;
+
+ rsp->payload.sol_packet.break_detected =
+ rsp->data[(*offset)++] & 0x04;
+
+ lprintf(LOG_DEBUG, "<<<<<<<<<< RECV FROM BMC <<<<<<<<<<<");
+ lprintf(LOG_DEBUG, "< SOL sequence number : 0x%02x",
+ rsp->payload.sol_packet.packet_sequence_number);
+ lprintf(LOG_DEBUG, "< SOL acked packet : 0x%02x",
+ rsp->payload.sol_packet.acked_packet_number);
+ lprintf(LOG_DEBUG, "< SOL accepted char count : 0x%02x",
+ rsp->payload.sol_packet.accepted_character_count);
+ lprintf(LOG_DEBUG, "< SOL is nack : %s",
+ rsp->payload.sol_packet.is_nack? "true" : "false");
+ lprintf(LOG_DEBUG, "< SOL xfer unavailable : %s",
+ rsp->payload.sol_packet.transfer_unavailable? "true" : "false");
+ lprintf(LOG_DEBUG, "< SOL inactive : %s",
+ rsp->payload.sol_packet.sol_inactive? "true" : "false");
+ lprintf(LOG_DEBUG, "< SOL transmit overrun : %s",
+ rsp->payload.sol_packet.transmit_overrun? "true" : "false");
+ lprintf(LOG_DEBUG, "< SOL break detected : %s",
+ rsp->payload.sol_packet.break_detected? "true" : "false");
+ lprintf(LOG_DEBUG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
+
+ if (verbose >= 5)
+ printbuf(rsp->data + *offset - 4, 4, "SOL MSG FROM BMC");
+}
+
+
+
+/*
+ * getIpmiPayloadWireRep
+ *
+ * param out [out] will contain our wire representation
+ * param req [in] is the IPMI request to be written
+ * param crypt_alg [in] specifies the encryption to use
+ * param rq_seq [in] is the IPMI command sequence number.
+ */
+void getIpmiPayloadWireRep(
+ struct ipmi_intf * intf, /* in out */
+ struct ipmi_v2_payload * payload, /* in */
+ uint8_t * msg,
+ struct ipmi_rq * req,
+ uint8_t rq_seq,
+ uint8_t curr_seq)
+{
+ int cs, tmp, len;
+ int cs2 = 0;
+ int cs3 = 0;
+ uint8_t ourAddress = intf->my_addr;
+ uint8_t bridgedRequest = 0;
+
+ if (ourAddress == 0)
+ ourAddress = IPMI_BMC_SLAVE_ADDR;
+
+ len = 0;
+
+ /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */
+ if ((intf->target_addr == ourAddress) || (!bridgePossible)) {
+ cs = len;
+ } else {
+ bridgedRequest = 1;
+
+ if(intf->transit_addr != ourAddress && intf->transit_addr != 0)
+ {
+ bridgedRequest++;
+ }
+ /* bridged request: encapsulate w/in Send Message */
+ cs = len;
+ msg[len++] = IPMI_BMC_SLAVE_ADDR;
+ msg[len++] = IPMI_NETFN_APP << 2;
+ tmp = len - cs;
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+ cs2 = len;
+ msg[len++] = IPMI_REMOTE_SWID;
+ msg[len++] = curr_seq << 2;
+
+
+ msg[len++] = 0x34; /* Send Message rqst */
+ if(bridgedRequest == 2)
+ msg[len++] = (0x40|intf->transit_channel); /* Track request*/
+ else
+ msg[len++] = (0x40|intf->target_channel); /* Track request*/
+
+ payload->payload_length += 7;
+ cs = len;
+
+ if(bridgedRequest == 2)
+ {
+ /* bridged request: encapsulate w/in Send Message */
+ cs = len;
+ msg[len++] = intf->transit_addr;
+ msg[len++] = IPMI_NETFN_APP << 2;
+ tmp = len - cs;
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+ cs3 = len;
+ msg[len++] = intf->my_addr;
+ msg[len++] = curr_seq << 2;
+ msg[len++] = 0x34; /* Send Message rqst */
+ #if 0 /* From lan.c example */
+ entry->req.msg.target_cmd = entry->req.msg.cmd; /* Save target command */
+ entry->req.msg.cmd = 0x34; /* (fixup request entry) */
+ #endif
+ msg[len++] = (0x40|intf->target_channel); /* Track request*/
+
+ payload->payload_length += 7;
+
+ cs = len;
+ }
+ }
+
+ lprintf(LOG_DEBUG,"%s RqAddr %#x transit %#x:%#x target %#x:%#x "
+ "bridgePossible %d",
+ bridgedRequest ? "Bridging" : "Local",
+ intf->my_addr, intf->transit_addr, intf->transit_channel,
+ intf->target_addr, intf->target_channel,
+ bridgePossible);
+
+ /* rsAddr */
+ msg[len++] = intf->target_addr; /* IPMI_BMC_SLAVE_ADDR; */
+
+ /* net Fn */
+ msg[len++] = req->msg.netfn << 2 | (req->msg.lun & 3);
+ tmp = len - cs;
+
+ /* checkSum */
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+ cs = len;
+
+ /* rqAddr */
+ if (!bridgedRequest)
+ msg[len++] = IPMI_REMOTE_SWID;
+ else /* Bridged message */
+ msg[len++] = intf->my_addr;
+
+ /* rqSeq / rqLUN */
+ msg[len++] = rq_seq << 2;
+
+ /* cmd */
+ msg[len++] = req->msg.cmd;
+
+ /* message data */
+ if (req->msg.data_len) {
+ memcpy(msg + len, req->msg.data, req->msg.data_len);
+ len += req->msg.data_len;
+ }
+
+ /* second checksum */
+ tmp = len - cs;
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+
+ /* Dual bridged request: 2nd checksum */
+ if (bridgedRequest == 2) {
+ tmp = len - cs3;
+ msg[len++] = ipmi_csum(msg+cs3, tmp);
+ payload->payload_length += 1;
+ }
+
+ /* bridged request: 2nd checksum */
+ if (bridgedRequest) {
+ tmp = len - cs2;
+ msg[len++] = ipmi_csum(msg+cs2, tmp);
+ payload->payload_length += 1;
+ }
+}
+
+
+
+/*
+ * getSolPayloadWireRep
+ *
+ * param msg [out] will contain our wire representation
+ * param payload [in] holds the v2 payload with our SOL data
+ */
+void getSolPayloadWireRep(
+ struct ipmi_intf * intf, /* in out */
+ uint8_t * msg, /* output */
+ struct ipmi_v2_payload * payload) /* input */
+{
+ int i = 0;
+
+ lprintf(LOG_DEBUG, ">>>>>>>>>> SENDING TO BMC >>>>>>>>>>");
+ lprintf(LOG_DEBUG, "> SOL sequence number : 0x%02x",
+ payload->payload.sol_packet.packet_sequence_number);
+ lprintf(LOG_DEBUG, "> SOL acked packet : 0x%02x",
+ payload->payload.sol_packet.acked_packet_number);
+ lprintf(LOG_DEBUG, "> SOL accepted char count : 0x%02x",
+ payload->payload.sol_packet.accepted_character_count);
+ lprintf(LOG_DEBUG, "> SOL is nack : %s",
+ payload->payload.sol_packet.is_nack ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL assert ring wor : %s",
+ payload->payload.sol_packet.assert_ring_wor ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL generate break : %s",
+ payload->payload.sol_packet.generate_break ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL deassert cts : %s",
+ payload->payload.sol_packet.deassert_cts ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL deassert dcd dsr : %s",
+ payload->payload.sol_packet.deassert_dcd_dsr ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL flush inbound : %s",
+ payload->payload.sol_packet.flush_inbound ? "true" : "false");
+ lprintf(LOG_DEBUG, "> SOL flush outbound : %s",
+ payload->payload.sol_packet.flush_outbound ? "true" : "false");
+
+ msg[i++] = payload->payload.sol_packet.packet_sequence_number;
+ msg[i++] = payload->payload.sol_packet.acked_packet_number;
+ msg[i++] = payload->payload.sol_packet.accepted_character_count;
+
+ msg[i] = payload->payload.sol_packet.is_nack ? 0x40 : 0;
+ msg[i] |= payload->payload.sol_packet.assert_ring_wor ? 0x20 : 0;
+ msg[i] |= payload->payload.sol_packet.generate_break ? 0x10 : 0;
+ msg[i] |= payload->payload.sol_packet.deassert_cts ? 0x08 : 0;
+ msg[i] |= payload->payload.sol_packet.deassert_dcd_dsr ? 0x04 : 0;
+ msg[i] |= payload->payload.sol_packet.flush_inbound ? 0x02 : 0;
+ msg[i++] |= payload->payload.sol_packet.flush_outbound ? 0x01 : 0;
+
+ /* We may have data to add */
+ memcpy(msg + i,
+ payload->payload.sol_packet.data,
+ payload->payload.sol_packet.character_count);
+
+ lprintf(LOG_DEBUG, "> SOL character count : %d",
+ payload->payload.sol_packet.character_count);
+ lprintf(LOG_DEBUG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+
+ if (verbose >= 5 && payload->payload.sol_packet.character_count)
+ printbuf(payload->payload.sol_packet.data, payload->payload.sol_packet.character_count, "SOL SEND DATA");
+
+ /*
+ * At this point, the payload length becomes the whole payload
+ * length, including the 4 bytes at the beginning of the SOL
+ * packet
+ */
+ payload->payload_length = payload->payload.sol_packet.character_count + 4;
+}
+
+
+
+/*
+ * ipmi_lanplus_build_v2x_msg
+ *
+ * Encapsulates the payload data to create the IPMI v2.0 / RMCP+ packet.
+ *
+ *
+ * IPMI v2.0 LAN Request Message Format
+ * +----------------------+
+ * | rmcp.ver | 4 bytes
+ * | rmcp.__reserved |
+ * | rmcp.seq |
+ * | rmcp.class |
+ * +----------------------+
+ * | session.authtype | 10 bytes
+ * | session.payloadtype |
+ * | session.id |
+ * | session.seq |
+ * +----------------------+
+ * | message length | 2 bytes
+ * +----------------------+
+ * | Confidentiality Hdr | var (possibly absent)
+ * +----------------------+
+ * | Paylod | var Payload
+ * +----------------------+
+ * | Confidentiality Trlr | var (possibly absent)
+ * +----------------------+
+ * | Integrity pad | var (possibly absent)
+ * +----------------------+
+ * | Pad length | 1 byte (WTF?)
+ * +----------------------+
+ * | Next Header | 1 byte (WTF?)
+ * +----------------------+
+ * | Authcode | var (possibly absent)
+ * +----------------------+
+ */
+void
+ipmi_lanplus_build_v2x_msg(
+ struct ipmi_intf * intf, /* in */
+ struct ipmi_v2_payload * payload, /* in */
+ int * msg_len, /* out */
+ uint8_t ** msg_data, /* out */
+ uint8_t curr_seq)
+{
+ uint32_t session_trailer_length = 0;
+ struct ipmi_session * session = intf->session;
+ struct rmcp_hdr rmcp = {
+ .ver = RMCP_VERSION_1,
+ .class = RMCP_CLASS_IPMI,
+ .seq = 0xff,
+ };
+
+ /* msg will hold the entire message to be sent */
+ uint8_t * msg;
+ int len = 0;
+
+
+ len =
+ sizeof(rmcp) + // RMCP Header (4)
+ 10 + // IPMI Session Header
+ 2 + // Message length
+ payload->payload_length + // The actual payload
+ IPMI_MAX_INTEGRITY_PAD_SIZE + // Integrity Pad
+ 1 + // Pad Length
+ 1 + // Next Header
+ IPMI_MAX_AUTH_CODE_SIZE; // Authcode
+
+
+ msg = malloc(len);
+ if (msg == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return;
+ }
+ memset(msg, 0, len);
+
+ /*
+ *------------------------------------------
+ * RMCP HEADER
+ *------------------------------------------
+ */
+ memcpy(msg, &rmcp, sizeof(rmcp));
+ len = sizeof(rmcp);
+
+
+ /*
+ *------------------------------------------
+ * IPMI SESSION HEADER
+ *------------------------------------------
+ */
+ /* ipmi session Auth Type / Format is always 0x06 for IPMI v2 */
+ msg[IPMI_LANPLUS_OFFSET_AUTHTYPE] = 0x06;
+
+ /* Payload Type -- also specifies whether were authenticated/encyrpted */
+ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] = payload->payload_type;
+
+ if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE)
+ {
+ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |=
+ ((session->v2_data.crypt_alg != IPMI_CRYPT_NONE )? 0x80 : 0x00);
+ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |=
+ ((session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)? 0x40 : 0x00);
+ }
+
+ if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE)
+ {
+ /* Session ID -- making it LSB */
+ msg[IPMI_LANPLUS_OFFSET_SESSION_ID ] = session->v2_data.bmc_id & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 1] = (session->v2_data.bmc_id >> 8) & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 2] = (session->v2_data.bmc_id >> 16) & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 3] = (session->v2_data.bmc_id >> 24) & 0xff;
+
+ /* Sequence Number -- making it LSB */
+ msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM ] = session->out_seq & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 1] = (session->out_seq >> 8) & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 2] = (session->out_seq >> 16) & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 3] = (session->out_seq >> 24) & 0xff;
+ }
+
+ /*
+ * Payload Length is set below (we don't know how big the payload is until after
+ * encryption).
+ */
+
+ /*
+ * Payload
+ *
+ * At this point we are ready to slam the payload in.
+ * This includes:
+ * 1) The confidentiality header
+ * 2) The payload proper (possibly encrypted)
+ * 3) The confidentiality trailer
+ *
+ */
+ switch (payload->payload_type)
+ {
+ case IPMI_PAYLOAD_TYPE_IPMI:
+ getIpmiPayloadWireRep(intf,
+ payload, /* in */
+ msg + IPMI_LANPLUS_OFFSET_PAYLOAD,
+ payload->payload.ipmi_request.request,
+ payload->payload.ipmi_request.rq_seq,
+ curr_seq);
+ break;
+
+ case IPMI_PAYLOAD_TYPE_SOL:
+ getSolPayloadWireRep(intf,
+ msg + IPMI_LANPLUS_OFFSET_PAYLOAD,
+ payload);
+
+ if (verbose >= 5)
+ printbuf(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, 4, "SOL MSG TO BMC");
+
+ len += payload->payload_length;
+
+ break;
+
+ case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST:
+ /* never encrypted, so our job is easy */
+ memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD,
+ payload->payload.open_session_request.request,
+ payload->payload_length);
+ len += payload->payload_length;
+ break;
+
+ case IPMI_PAYLOAD_TYPE_RAKP_1:
+ /* never encrypted, so our job is easy */
+ memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD,
+ payload->payload.rakp_1_message.message,
+ payload->payload_length);
+ len += payload->payload_length;
+ break;
+
+ case IPMI_PAYLOAD_TYPE_RAKP_3:
+ /* never encrypted, so our job is easy */
+ memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD,
+ payload->payload.rakp_3_message.message,
+ payload->payload_length);
+ len += payload->payload_length;
+ break;
+
+ default:
+ lprintf(LOG_ERR, "unsupported payload type 0x%x",
+ payload->payload_type);
+ free(msg);
+ msg = NULL;
+ assert(0);
+ break;
+ }
+
+
+ /*
+ *------------------------------------------
+ * ENCRYPT THE PAYLOAD IF NECESSARY
+ *------------------------------------------
+ */
+ if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE)
+ {
+ /* Payload len is adjusted as necessary by lanplus_encrypt_payload */
+ lanplus_encrypt_payload(session->v2_data.crypt_alg, /* input */
+ session->v2_data.k2, /* input */
+ msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* input */
+ payload->payload_length, /* input */
+ msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* output */
+ &(payload->payload_length)); /* output */
+
+ }
+
+ /* Now we know the payload length */
+ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE ] =
+ payload->payload_length & 0xff;
+ msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE + 1] =
+ (payload->payload_length >> 8) & 0xff;
+
+
+ /*
+ *------------------------------------------
+ * SESSION TRAILER
+ *------------------------------------------
+ */
+ if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) &&
+ (session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE))
+ {
+ uint32_t i, hmac_length, integrity_pad_size = 0, hmac_input_size;
+ uint8_t * hmac_output;
+ uint32_t start_of_session_trailer =
+ IPMI_LANPLUS_OFFSET_PAYLOAD +
+ payload->payload_length;
+
+
+ /*
+ * Determine the required integrity pad length. We have to make the
+ * data range covered by the authcode a multiple of 4.
+ */
+ uint32_t length_before_authcode;
+
+ if (ipmi_oem_active(intf, "icts")) {
+ length_before_authcode =
+ 12 + /* the stuff before the payload */
+ payload->payload_length;
+ } else {
+ length_before_authcode =
+ 12 + /* the stuff before the payload */
+ payload->payload_length +
+ 1 + /* pad length field */
+ 1; /* next header field */
+ }
+
+ if (length_before_authcode % 4)
+ integrity_pad_size = 4 - (length_before_authcode % 4);
+
+ for (i = 0; i < integrity_pad_size; ++i)
+ msg[start_of_session_trailer + i] = 0xFF;
+
+ /* Pad length */
+ msg[start_of_session_trailer + integrity_pad_size] = integrity_pad_size;
+
+ /* Next Header */
+ msg[start_of_session_trailer + integrity_pad_size + 1] =
+ 0x07; /* Hardcoded per the spec, table 13-8 */
+
+ hmac_input_size =
+ 12 +
+ payload->payload_length +
+ integrity_pad_size +
+ 2;
+
+ hmac_output =
+ msg +
+ IPMI_LANPLUS_OFFSET_PAYLOAD +
+ payload->payload_length +
+ integrity_pad_size +
+ 2;
+
+ if (verbose > 2)
+ printbuf(msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, hmac_input_size, "authcode input");
+
+
+ /* Auth Code */
+ lanplus_HMAC(session->v2_data.integrity_alg,
+ session->v2_data.k1, /* key */
+ 20, /* key length */
+ msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, /* hmac input */
+ hmac_input_size,
+ hmac_output,
+ &hmac_length);
+
+ assert(hmac_length == 20);
+
+ if (verbose > 2)
+ printbuf(hmac_output, 12, "authcode output");
+
+ /* Set session_trailer_length appropriately */
+ session_trailer_length =
+ integrity_pad_size +
+ 2 + /* pad length + next header */
+ 12; /* Size of the authcode (we only use the first 12 bytes) */
+ }
+
+
+ ++(session->out_seq);
+ if (!session->out_seq)
+ ++(session->out_seq);
+
+ *msg_len =
+ IPMI_LANPLUS_OFFSET_PAYLOAD +
+ payload->payload_length +
+ session_trailer_length;
+ *msg_data = msg;
+}
+
+
+
+/*
+ * ipmi_lanplus_build_v2x_ipmi_cmd
+ *
+ * Wraps ipmi_lanplus_build_v2x_msg and returns a new entry object for the
+ * command
+ *
+ */
+static struct ipmi_rq_entry *
+ipmi_lanplus_build_v2x_ipmi_cmd(
+ struct ipmi_intf * intf,
+ struct ipmi_rq * req,
+ int isRetry)
+{
+ struct ipmi_v2_payload v2_payload;
+ struct ipmi_rq_entry * entry;
+
+ /*
+ * We have a problem. we need to know the sequence number here,
+ * because we use it in our stored entry. But we also need to
+ * know the sequence number when we generate our IPMI
+ * representation far below.
+ */
+ static uint8_t curr_seq = 0;
+
+ if( isRetry == 0 )
+ curr_seq += 1;
+
+ if (curr_seq >= 64)
+ curr_seq = 0;
+
+
+ /* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */
+ if ((intf->target_addr == intf->my_addr) || (!bridgePossible))
+ {
+ entry = ipmi_req_add_entry(intf, req, curr_seq);
+ }
+ else /* it's a bridge command */
+ {
+ unsigned char backup_cmd;
+
+ /* Add entry for cmd */
+ entry = ipmi_req_add_entry(intf, req, curr_seq);
+
+ if(entry)
+ {
+ /* Add entry for bridge cmd */
+ backup_cmd = req->msg.cmd;
+ req->msg.cmd = 0x34;
+ entry = ipmi_req_add_entry(intf, req, curr_seq);
+ req->msg.cmd = backup_cmd;
+ }
+ }
+
+ if (entry == NULL)
+ return NULL;
+
+ // Build our payload
+ v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI;
+ v2_payload.payload_length = req->msg.data_len + 7;
+ v2_payload.payload.ipmi_request.request = req;
+ v2_payload.payload.ipmi_request.rq_seq = curr_seq;
+
+ ipmi_lanplus_build_v2x_msg(intf, // in
+ &v2_payload, // in
+ &(entry->msg_len), // out
+ &(entry->msg_data), // out
+ curr_seq); // in
+
+ return entry;
+}
+
+
+
+
+
+/*
+ * IPMI LAN Request Message Format
+ * +--------------------+
+ * | rmcp.ver | 4 bytes
+ * | rmcp.__reserved |
+ * | rmcp.seq |
+ * | rmcp.class |
+ * +--------------------+
+ * | session.authtype | 9 bytes
+ * | session.seq |
+ * | session.id |
+ * +--------------------+
+ * | [session.authcode] | 16 bytes (AUTHTYPE != none)
+ * +--------------------+
+ * | message length | 1 byte
+ * +--------------------+
+ * | message.rs_addr | 6 bytes
+ * | message.netfn_lun |
+ * | message.checksum |
+ * | message.rq_addr |
+ * | message.rq_seq |
+ * | message.cmd |
+ * +--------------------+
+ * | [request data] | data_len bytes
+ * +--------------------+
+ * | checksum | 1 byte
+ * +--------------------+
+ */
+static struct ipmi_rq_entry *
+ipmi_lanplus_build_v15_ipmi_cmd(
+ struct ipmi_intf * intf,
+ struct ipmi_rq * req)
+{
+ struct rmcp_hdr rmcp = {
+ .ver = RMCP_VERSION_1,
+ .class = RMCP_CLASS_IPMI,
+ .seq = 0xff,
+ };
+ uint8_t * msg;
+ int cs, mp, len = 0, tmp;
+ struct ipmi_session * session = intf->session;
+ struct ipmi_rq_entry * entry;
+
+ entry = ipmi_req_add_entry(intf, req, 0);
+ if (entry == NULL)
+ return NULL;
+
+ len = req->msg.data_len + 21;
+
+ msg = malloc(len);
+ if (msg == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return NULL;
+ }
+ memset(msg, 0, len);
+
+ /* rmcp header */
+ memcpy(msg, &rmcp, sizeof(rmcp));
+ len = sizeof(rmcp);
+
+ /*
+ * ipmi session header
+ */
+ /* Authtype should always be none for 1.5 packets sent from this
+ * interface
+ */
+ msg[len++] = IPMI_SESSION_AUTHTYPE_NONE;
+
+ msg[len++] = session->out_seq & 0xff;
+ msg[len++] = (session->out_seq >> 8) & 0xff;
+ msg[len++] = (session->out_seq >> 16) & 0xff;
+ msg[len++] = (session->out_seq >> 24) & 0xff;
+
+ /*
+ * The session ID should be all zeroes for pre-session commands. We
+ * should only be using the 1.5 interface for the pre-session Get
+ * Channel Authentication Capabilities command
+ */
+ msg[len++] = 0;
+ msg[len++] = 0;
+ msg[len++] = 0;
+ msg[len++] = 0;
+
+ /* message length */
+ msg[len++] = req->msg.data_len + 7;
+
+ /* ipmi message header */
+ cs = mp = len;
+ msg[len++] = IPMI_BMC_SLAVE_ADDR;
+ msg[len++] = req->msg.netfn << 2;
+ tmp = len - cs;
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+ cs = len;
+ msg[len++] = IPMI_REMOTE_SWID;
+
+ entry->rq_seq = 0;
+
+ msg[len++] = entry->rq_seq << 2;
+ msg[len++] = req->msg.cmd;
+
+ lprintf(LOG_DEBUG+1, ">> IPMI Request Session Header");
+ lprintf(LOG_DEBUG+1, ">> Authtype : %s",
+ val2str(IPMI_SESSION_AUTHTYPE_NONE, ipmi_authtype_session_vals));
+ lprintf(LOG_DEBUG+1, ">> Sequence : 0x%08lx",
+ (long)session->out_seq);
+ lprintf(LOG_DEBUG+1, ">> Session ID : 0x%08lx",
+ (long)0);
+
+ lprintf(LOG_DEBUG+1, ">> IPMI Request Message Header");
+ lprintf(LOG_DEBUG+1, ">> Rs Addr : %02x", IPMI_BMC_SLAVE_ADDR);
+ lprintf(LOG_DEBUG+1, ">> NetFn : %02x", req->msg.netfn);
+ lprintf(LOG_DEBUG+1, ">> Rs LUN : %01x", 0);
+ lprintf(LOG_DEBUG+1, ">> Rq Addr : %02x", IPMI_REMOTE_SWID);
+ lprintf(LOG_DEBUG+1, ">> Rq Seq : %02x", entry->rq_seq);
+ lprintf(LOG_DEBUG+1, ">> Rq Lun : %01x", 0);
+ lprintf(LOG_DEBUG+1, ">> Command : %02x", req->msg.cmd);
+
+ /* message data */
+ if (req->msg.data_len) {
+ memcpy(msg+len, req->msg.data, req->msg.data_len);
+ len += req->msg.data_len;
+ }
+
+ /* second checksum */
+ tmp = len - cs;
+ msg[len++] = ipmi_csum(msg+cs, tmp);
+
+ entry->msg_len = len;
+ entry->msg_data = msg;
+
+ return entry;
+}
+
+
+
+/*
+ * is_sol_packet
+ */
+static int
+is_sol_packet(struct ipmi_rs * rsp)
+{
+ return (rsp &&
+ (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) &&
+ (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL));
+}
+
+
+
+/*
+ * sol_response_acks_packet
+ */
+static int
+sol_response_acks_packet(
+ struct ipmi_rs * rsp,
+ struct ipmi_v2_payload * payload)
+{
+ return (is_sol_packet(rsp) &&
+ payload &&
+ (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) &&
+ (rsp->payload.sol_packet.acked_packet_number ==
+ payload->payload.sol_packet.packet_sequence_number));
+}
+
+
+
+/*
+ * ipmi_lanplus_send_payload
+ *
+ */
+struct ipmi_rs *
+ipmi_lanplus_send_payload(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * payload)
+{
+ struct ipmi_rs * rsp = NULL;
+ uint8_t * msg_data;
+ int msg_length;
+ struct ipmi_session * session = intf->session;
+ struct ipmi_rq_entry * entry = NULL;
+ int try = 0;
+ int xmit = 1;
+ time_t ltime;
+ uint32_t saved_timeout;
+
+ if (!intf->opened && intf->open && intf->open(intf) < 0)
+ return NULL;
+
+ /*
+ * The session timeout is initialized in the above interface open,
+ * so it will only be valid after the open completes.
+ */
+ saved_timeout = session->timeout;
+ while (try < session->retry) {
+ //ltime = time(NULL);
+
+ if (xmit) {
+ ltime = time(NULL);
+
+ if (payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI)
+ {
+ /*
+ * Build an IPMI v1.5 or v2 command
+ */
+ struct ipmi_rq * ipmi_request = payload->payload.ipmi_request.request;
+
+ lprintf(LOG_DEBUG, "");
+ lprintf(LOG_DEBUG, ">> Sending IPMI command payload");
+ lprintf(LOG_DEBUG, ">> netfn : 0x%02x", ipmi_request->msg.netfn);
+ lprintf(LOG_DEBUG, ">> command : 0x%02x", ipmi_request->msg.cmd);
+
+ if (verbose > 1)
+ {
+ uint16_t i;
+ fprintf(stderr, ">> data : ");
+ for (i = 0; i < ipmi_request->msg.data_len; ++i)
+ fprintf(stderr, "0x%02x ", ipmi_request->msg.data[i]);
+ fprintf(stderr, "\n\n");
+ }
+
+
+ /*
+ * If we are presession, and the command is GET CHANNEL AUTHENTICATION
+ * CAPABILITIES, we will build the command in v1.5 format. This is so
+ * that we can ask any server whether it supports IPMI v2 / RMCP+
+ * before we attempt to open a v2.x session.
+ */
+ if ((ipmi_request->msg.netfn == IPMI_NETFN_APP) &&
+ (ipmi_request->msg.cmd == IPMI_GET_CHANNEL_AUTH_CAP) &&
+ (session->v2_data.bmc_id == 0)) // jme - check
+ {
+ lprintf(LOG_DEBUG+1, "BUILDING A v1.5 COMMAND");
+ entry = ipmi_lanplus_build_v15_ipmi_cmd(intf, ipmi_request);
+ }
+ else
+ {
+ int isRetry = ( try > 0 ? 1 : 0 );
+
+ lprintf(LOG_DEBUG+1, "BUILDING A v2 COMMAND");
+ entry = ipmi_lanplus_build_v2x_ipmi_cmd(intf, ipmi_request, isRetry);
+ }
+
+ if (entry == NULL) {
+ lprintf(LOG_ERR, "Aborting send command, unable to build");
+ return NULL;
+ }
+
+ msg_data = entry->msg_data;
+ msg_length = entry->msg_len;
+ }
+
+ else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST)
+ {
+ lprintf(LOG_DEBUG, ">> SENDING AN OPEN SESSION REQUEST\n");
+ assert(session->v2_data.session_state == LANPLUS_STATE_PRESESSION
+ || session->v2_data.session_state == LANPLUS_STATE_OPEN_SESSION_SENT);
+
+ ipmi_lanplus_build_v2x_msg(intf, /* in */
+ payload, /* in */
+ &msg_length, /* out */
+ &msg_data, /* out */
+ 0); /* irrelevant for this msg*/
+
+ }
+
+ else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_1)
+ {
+ lprintf(LOG_DEBUG, ">> SENDING A RAKP 1 MESSAGE\n");
+ assert(session->v2_data.session_state ==
+ LANPLUS_STATE_OPEN_SESSION_RECEIEVED);
+
+ ipmi_lanplus_build_v2x_msg(intf, /* in */
+ payload, /* in */
+ &msg_length, /* out */
+ &msg_data, /* out */
+ 0); /* irrelevant for this msg*/
+
+ }
+
+ else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_3)
+ {
+ lprintf(LOG_DEBUG, ">> SENDING A RAKP 3 MESSAGE\n");
+ assert(session->v2_data.session_state ==
+ LANPLUS_STATE_RAKP_2_RECEIVED);
+
+ ipmi_lanplus_build_v2x_msg(intf, /* in */
+ payload, /* in */
+ &msg_length, /* out */
+ &msg_data, /* out */
+ 0); /* irrelevant for this msg*/
+
+ }
+
+ else if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL)
+ {
+ lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n");
+ assert(session->v2_data.session_state == LANPLUS_STATE_ACTIVE);
+
+ ipmi_lanplus_build_v2x_msg(intf, /* in */
+ payload, /* in */
+ &msg_length, /* out */
+ &msg_data, /* out */
+ 0); /* irrelevant for this msg*/
+ }
+
+ else
+ {
+ lprintf(LOG_ERR, "Payload type 0x%0x is unsupported!",
+ payload->payload_type);
+ assert(0);
+ }
+
+
+ if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) {
+ lprintf(LOG_ERR, "IPMI LAN send command failed");
+ return NULL;
+ }
+ }
+
+ /* if we are set to noanswer we do not expect response */
+ if (intf->noanswer)
+ break;
+
+ usleep(100); /* Not sure what this is for */
+
+ /* Remember our connection state */
+ switch (payload->payload_type)
+ {
+ case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST:
+ session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_SENT;
+ /* not retryable for timeouts, force no retry */
+ try = session->retry;
+ break;
+ case IPMI_PAYLOAD_TYPE_RAKP_1:
+ session->v2_data.session_state = LANPLUS_STATE_RAKP_1_SENT;
+ /* not retryable for timeouts, force no retry */
+ try = session->retry;
+ break;
+ case IPMI_PAYLOAD_TYPE_RAKP_3:
+ /* not retryable for timeouts, force no retry */
+ try = session->retry;
+ session->v2_data.session_state = LANPLUS_STATE_RAKP_3_SENT;
+ break;
+ }
+
+
+ /*
+ * Special case for SOL outbound packets.
+ */
+ if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL)
+ {
+ if (! payload->payload.sol_packet.packet_sequence_number)
+ {
+ /* We're just sending an ACK. No need to retry. */
+ break;
+ }
+
+
+ rsp = ipmi_lanplus_recv_sol(intf); /* Grab the next packet */
+
+ if (sol_response_acks_packet(rsp, payload))
+ break;
+
+ else if (is_sol_packet(rsp) && rsp->data_len)
+ {
+ /*
+ * We're still waiting for our ACK, but we more data from
+ * the BMC
+ */
+ intf->session->sol_data.sol_input_handler(rsp);
+ /* In order to avoid duplicate output, just set data_len to 0 */
+ rsp->data_len = 0;
+ }
+ }
+
+
+ /* Non-SOL processing */
+ else
+ {
+ rsp = ipmi_lan_poll_recv(intf);
+
+ /* Duplicate Request ccode most likely indicates a response to
+ a previous retry. Ignore and keep polling. */
+ while ((rsp != NULL) && (rsp->ccode == 0xcf))
+ {
+ rsp = NULL;
+ rsp = ipmi_lan_poll_recv(intf);
+ }
+
+ if (rsp)
+ break;
+ /* This payload type is retryable for timeouts. */
+ if ((payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) && entry) {
+ ipmi_req_remove_entry( entry->rq_seq, entry->req.msg.cmd);
+ }
+ }
+
+ /* only timeout if time exceeds the timeout value */
+ xmit = ((time(NULL) - ltime) >= session->timeout);
+
+ usleep(5000);
+
+ if (xmit) {
+ /* increment session timeout by 1 second each retry */
+ session->timeout++;
+ }
+
+ try++;
+ }
+ session->timeout = saved_timeout;
+
+ /* IPMI messages are deleted under ipmi_lan_poll_recv() */
+ switch (payload->payload_type) {
+ case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST:
+ case IPMI_PAYLOAD_TYPE_RAKP_1:
+ case IPMI_PAYLOAD_TYPE_RAKP_3:
+ free(msg_data);
+ msg_data = NULL;
+ break;
+ }
+
+ return rsp;
+}
+
+
+
+/*
+ * is_sol_partial_ack
+ *
+ * Determine if the response is a partial ACK/NACK that indicates
+ * we need to resend part of our packet.
+ *
+ * returns the number of characters we need to resend, or
+ * 0 if this isn't an ACK or we don't need to resend anything
+ */
+int is_sol_partial_ack(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * v2_payload,
+ struct ipmi_rs * rs)
+{
+ int chars_to_resend = 0;
+
+ if (v2_payload &&
+ rs &&
+ is_sol_packet(rs) &&
+ sol_response_acks_packet(rs, v2_payload) &&
+ (rs->payload.sol_packet.accepted_character_count <
+ v2_payload->payload.sol_packet.character_count))
+ {
+ if (ipmi_oem_active(intf, "intelplus") &&
+ rs->payload.sol_packet.accepted_character_count == 0)
+ return 0;
+
+ chars_to_resend =
+ v2_payload->payload.sol_packet.character_count -
+ rs->payload.sol_packet.accepted_character_count;
+ }
+
+ return chars_to_resend;
+}
+
+
+
+/*
+ * set_sol_packet_sequence_number
+ */
+static void set_sol_packet_sequence_number(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * v2_payload)
+{
+ /* Keep our sequence number sane */
+ if (intf->session->sol_data.sequence_number > 0x0F)
+ intf->session->sol_data.sequence_number = 1;
+
+ v2_payload->payload.sol_packet.packet_sequence_number =
+ intf->session->sol_data.sequence_number++;
+}
+
+
+
+/*
+ * ipmi_lanplus_send_sol
+ *
+ * Sends a SOL packet.. We handle partial ACK/NACKs from the BMC here.
+ *
+ * Returns a pointer to the SOL ACK we received, or
+ * 0 on failure
+ *
+ */
+struct ipmi_rs *
+ipmi_lanplus_send_sol(
+ struct ipmi_intf * intf,
+ struct ipmi_v2_payload * v2_payload)
+{
+ struct ipmi_rs * rs;
+
+ /*
+ * chars_to_resend indicates either that we got a NACK telling us
+ * that we need to resend some part of our data.
+ */
+ int chars_to_resend = 0;
+
+ v2_payload->payload_type = IPMI_PAYLOAD_TYPE_SOL;
+
+ /*
+ * Payload length is just the length of the character
+ * data here.
+ */
+ v2_payload->payload_length = v2_payload->payload.sol_packet.character_count;
+
+ v2_payload->payload.sol_packet.acked_packet_number = 0; /* NA */
+
+ set_sol_packet_sequence_number(intf, v2_payload);
+
+ v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */
+
+ rs = ipmi_lanplus_send_payload(intf, v2_payload);
+
+ /* Determine if we need to resend some of our data */
+ chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs);
+
+ while (rs && !rs->payload.sol_packet.transfer_unavailable &&
+ !rs->payload.sol_packet.is_nack &&
+ chars_to_resend)
+ {
+ /*
+ * We first need to handle any new data we might have
+ * received in our NACK
+ */
+ if (rs->data_len)
+ intf->session->sol_data.sol_input_handler(rs);
+
+ set_sol_packet_sequence_number(intf, v2_payload);
+
+ /* Just send the required data */
+ memmove(v2_payload->payload.sol_packet.data,
+ v2_payload->payload.sol_packet.data +
+ rs->payload.sol_packet.accepted_character_count,
+ chars_to_resend);
+
+ v2_payload->payload.sol_packet.character_count = chars_to_resend;
+
+ v2_payload->payload_length = v2_payload->payload.sol_packet.character_count;
+
+ rs = ipmi_lanplus_send_payload(intf, v2_payload);
+
+ chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs);
+ }
+
+ return rs;
+}
+
+
+
+/*
+ * check_sol_packet_for_new_data
+ *
+ * Determine whether the SOL packet has already been seen
+ * and whether the packet has new data for us.
+ *
+ * This function has the side effect of removing an previously
+ * seen data, and moving new data to the front.
+ *
+ * It also "Remembers" the data so we don't get repeats.
+ *
+ * returns the number of new bytes in the SOL packet
+ */
+static int
+check_sol_packet_for_new_data(
+ struct ipmi_intf * intf,
+ struct ipmi_rs *rsp)
+{
+ static uint8_t last_received_sequence_number = 0;
+ static uint8_t last_received_byte_count = 0;
+ int new_data_size = 0;
+
+
+ if (rsp &&
+ (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) &&
+ (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL))
+ {
+ /* Store the data length before we mod it */
+ uint8_t unaltered_data_len = rsp->data_len;
+
+ if (rsp->payload.sol_packet.packet_sequence_number ==
+ last_received_sequence_number)
+ {
+
+ /*
+ * This is the same as the last packet, but may include
+ * extra data
+ */
+ new_data_size = rsp->data_len - last_received_byte_count;
+
+ if (new_data_size > 0)
+ {
+ /* We have more data to process */
+ memmove(rsp->data,
+ rsp->data +
+ rsp->data_len - new_data_size,
+ new_data_size);
+ }
+
+ rsp->data_len = new_data_size;
+ }
+
+
+ /*
+ *Rember the data for next round
+ */
+ if (rsp->payload.sol_packet.packet_sequence_number)
+ {
+ last_received_sequence_number =
+ rsp->payload.sol_packet.packet_sequence_number;
+
+ last_received_byte_count = unaltered_data_len;
+ }
+ }
+
+
+ return new_data_size;
+}
+
+
+
+/*
+ * ack_sol_packet
+ *
+ * Provided the specified packet looks reasonable, ACK it.
+ */
+static void
+ack_sol_packet(
+ struct ipmi_intf * intf,
+ struct ipmi_rs * rsp)
+{
+ if (rsp &&
+ (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) &&
+ (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) &&
+ (rsp->payload.sol_packet.packet_sequence_number))
+ {
+ struct ipmi_v2_payload ack;
+
+ bzero(&ack, sizeof(struct ipmi_v2_payload));
+
+ ack.payload_type = IPMI_PAYLOAD_TYPE_SOL;
+
+ /*
+ * Payload length is just the length of the character
+ * data here.
+ */
+ ack.payload_length = 0;
+
+ /* ACK packets have sequence numbers of 0 */
+ ack.payload.sol_packet.packet_sequence_number = 0;
+
+ ack.payload.sol_packet.acked_packet_number =
+ rsp->payload.sol_packet.packet_sequence_number;
+
+ ack.payload.sol_packet.accepted_character_count = rsp->data_len;
+
+ ipmi_lanplus_send_payload(intf, &ack);
+ }
+}
+
+
+
+/*
+ * ipmi_lanplus_recv_sol
+ *
+ * Receive a SOL packet and send an ACK in response.
+ *
+ */
+struct ipmi_rs *
+ipmi_lanplus_recv_sol(struct ipmi_intf * intf)
+{
+ struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf);
+
+ if (rsp && rsp->session.authtype != 0)
+ {
+ ack_sol_packet(intf, rsp);
+
+ /*
+ * Remembers the data sent, and alters the data to just
+ * include the new stuff.
+ */
+ check_sol_packet_for_new_data(intf, rsp);
+ }
+ return rsp;
+}
+
+
+
+/**
+ * ipmi_lanplus_send_ipmi_cmd
+ *
+ * Build a payload request and dispatch it.
+ */
+struct ipmi_rs *
+ipmi_lanplus_send_ipmi_cmd(
+ struct ipmi_intf * intf,
+ struct ipmi_rq * req)
+{
+ struct ipmi_v2_payload v2_payload;
+
+ v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI;
+ v2_payload.payload.ipmi_request.request = req;
+
+ return ipmi_lanplus_send_payload(intf, &v2_payload);
+}
+
+
+/*
+ * ipmi_get_auth_capabilities_cmd
+ *
+ * This command may have to be sent twice. We first ask for the
+ * authentication capabilities with the "request IPMI v2 data bit"
+ * set. If this fails, we send the same command without that bit
+ * set.
+ *
+ * param intf is the initialized (but possibly) pre-session interface
+ * on which we will send the command
+ * param auth_cap [out] will be initialized to hold the Get Channel
+ * Authentication Capabilities return data on success. Its
+ * contents will be undefined on error.
+ *
+ * returns 0 on success
+ * non-zero if we were unable to contact the BMC, or we cannot
+ * get a successful response
+ *
+ */
+static int
+ipmi_get_auth_capabilities_cmd(
+ struct ipmi_intf * intf,
+ struct get_channel_auth_cap_rsp * auth_cap)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[2];
+ uint8_t backupBridgePossible;
+
+ backupBridgePossible = bridgePossible;
+
+ bridgePossible = 0;
+
+ msg_data[0] = IPMI_LAN_CHANNEL_E | 0x80; // Ask for IPMI v2 data as well
+ msg_data[1] = intf->session->privlvl;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_APP; // 0x06
+ req.msg.cmd = IPMI_GET_CHANNEL_AUTH_CAP; // 0x38
+ req.msg.data = msg_data;
+ req.msg.data_len = 2;
+
+ rsp = intf->sendrecv(intf, &req);
+
+ if (rsp == NULL || rsp->ccode > 0) {
+ /*
+ * It's very possible that this failed because we asked for IPMI
+ * v2 data. Ask again, without requesting IPMI v2 data.
+ */
+ msg_data[0] &= 0x7F;
+
+ rsp = intf->sendrecv(intf, &req);
+
+ if (rsp == NULL) {
+ lprintf(LOG_INFO, "Get Auth Capabilities error");
+ return 1;
+ }
+ if (rsp->ccode > 0) {
+ lprintf(LOG_INFO, "Get Auth Capabilities error: %s",
+ val2str(rsp->ccode, completion_code_vals));
+ return 1;
+ }
+ }
+
+
+ memcpy(auth_cap,
+ rsp->data,
+ sizeof(struct get_channel_auth_cap_rsp));
+
+ bridgePossible = backupBridgePossible;
+
+ return 0;
+}
+
+
+
+static int
+ipmi_close_session_cmd(struct ipmi_intf * intf)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t msg_data[4];
+ uint32_t bmc_session_lsbf;
+ uint8_t backupBridgePossible;
+
+ if (intf->session->v2_data.session_state != LANPLUS_STATE_ACTIVE)
+ return -1;
+
+ backupBridgePossible = bridgePossible;
+
+ intf->target_addr = IPMI_BMC_SLAVE_ADDR;
+ bridgePossible = 0;
+
+ bmc_session_lsbf = intf->session->v2_data.bmc_id;
+#if WORDS_BIGENDIAN
+ bmc_session_lsbf = BSWAP_32(bmc_session_lsbf);
+#endif
+
+ memcpy(&msg_data, &bmc_session_lsbf, 4);
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_APP;
+ req.msg.cmd = 0x3c;
+ req.msg.data = msg_data;
+ req.msg.data_len = 4;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ /* Looks like the session was closed */
+ lprintf(LOG_ERR, "Close Session command failed");
+ return -1;
+ }
+ if (verbose > 2)
+ printbuf(rsp->data, rsp->data_len, "close_session");
+
+ if (rsp->ccode == 0x87) {
+ lprintf(LOG_ERR, "Failed to Close Session: invalid "
+ "session ID %08lx",
+ (long)intf->session->v2_data.bmc_id);
+ return -1;
+ }
+ if (rsp->ccode > 0) {
+ lprintf(LOG_ERR, "Close Session command failed: %s",
+ val2str(rsp->ccode, completion_code_vals));
+ return -1;
+ }
+
+ lprintf(LOG_DEBUG, "Closed Session %08lx\n",
+ (long)intf->session->v2_data.bmc_id);
+
+ bridgePossible = backupBridgePossible;
+
+ return 0;
+}
+
+
+
+/*
+ * ipmi_lanplus_open_session
+ *
+ * Build and send the open session command. See section 13.17 of the IPMI
+ * v2 specification for details.
+ */
+static int
+ipmi_lanplus_open_session(struct ipmi_intf * intf)
+{
+ struct ipmi_v2_payload v2_payload;
+ struct ipmi_session * session = intf->session;
+ uint8_t * msg;
+ struct ipmi_rs * rsp;
+ /* 0 = success, 1 = error, 2 = timeout */
+ int rc = 0;
+
+
+ /*
+ * Build an Open Session Request Payload
+ */
+ msg = (uint8_t*)malloc(IPMI_OPEN_SESSION_REQUEST_SIZE);
+ if (msg == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+ memset(msg, 0, IPMI_OPEN_SESSION_REQUEST_SIZE);
+
+ msg[0] = 0; /* Message tag */
+ if (ipmi_oem_active(intf, "intelplus") || session->privlvl != IPMI_SESSION_PRIV_ADMIN)
+ msg[1] = session->privlvl;
+ else
+ msg[1] = 0; /* Give us highest privlg level based on supported algorithms */
+ msg[2] = 0; /* reserved */
+ msg[3] = 0; /* reserved */
+
+ /* Choose our session ID for easy recognition in the packet dump */
+ session->v2_data.console_id = 0xA0A2A3A4;
+ msg[4] = session->v2_data.console_id & 0xff;
+ msg[5] = (session->v2_data.console_id >> 8) & 0xff;
+ msg[6] = (session->v2_data.console_id >> 16) & 0xff;
+ msg[7] = (session->v2_data.console_id >> 24) & 0xff;
+
+
+ if (lanplus_get_requested_ciphers(session->cipher_suite_id,
+ &(session->v2_data.requested_auth_alg),
+ &(session->v2_data.requested_integrity_alg),
+ &(session->v2_data.requested_crypt_alg)))
+ {
+ lprintf(LOG_WARNING, "Unsupported cipher suite ID : %d\n",
+ session->cipher_suite_id);
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+
+
+ /*
+ * Authentication payload
+ */
+ msg[8] = 0; /* specifies authentication payload */
+ msg[9] = 0; /* reserved */
+ msg[10] = 0; /* reserved */
+ msg[11] = 8; /* payload length */
+ msg[12] = session->v2_data.requested_auth_alg;
+ msg[13] = 0; /* reserved */
+ msg[14] = 0; /* reserved */
+ msg[15] = 0; /* reserved */
+
+ /*
+ * Integrity payload
+ */
+ msg[16] = 1; /* specifies integrity payload */
+ msg[17] = 0; /* reserved */
+ msg[18] = 0; /* reserved */
+ msg[19] = 8; /* payload length */
+ msg[20] = session->v2_data.requested_integrity_alg;
+ msg[21] = 0; /* reserved */
+ msg[22] = 0; /* reserved */
+ msg[23] = 0; /* reserved */
+
+ /*
+ * Confidentiality/Encryption payload
+ */
+ msg[24] = 2; /* specifies confidentiality payload */
+ msg[25] = 0; /* reserved */
+ msg[26] = 0; /* reserved */
+ msg[27] = 8; /* payload length */
+ msg[28] = session->v2_data.requested_crypt_alg;
+ msg[29] = 0; /* reserved */
+ msg[30] = 0; /* reserved */
+ msg[31] = 0; /* reserved */
+
+
+ v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST;
+ v2_payload.payload_length = IPMI_OPEN_SESSION_REQUEST_SIZE;
+ v2_payload.payload.open_session_request.request = msg;
+
+ rsp = ipmi_lanplus_send_payload(intf, &v2_payload);
+
+ free(msg);
+ msg = NULL;
+ if (rsp == NULL ) {
+ lprintf(LOG_DEBUG, "Timeout in open session response message.");
+ return 2;
+ }
+ if (verbose)
+ lanplus_dump_open_session_response(rsp);
+
+ if (rsp->payload.open_session_response.rakp_return_code !=
+ IPMI_RAKP_STATUS_NO_ERRORS)
+ {
+ lprintf(LOG_WARNING, "Error in open session response message : %s\n",
+ val2str(rsp->payload.open_session_response.rakp_return_code,
+ ipmi_rakp_return_codes));
+ return 1;
+ }
+ else
+ {
+ if (rsp->payload.open_session_response.console_id !=
+ session->v2_data.console_id) {
+ lprintf(LOG_WARNING, "Warning: Console session ID is not "
+ "what we requested");
+ }
+
+ session->v2_data.max_priv_level =
+ rsp->payload.open_session_response.max_priv_level;
+ session->v2_data.bmc_id =
+ rsp->payload.open_session_response.bmc_id;
+ session->v2_data.auth_alg =
+ rsp->payload.open_session_response.auth_alg;
+ session->v2_data.integrity_alg =
+ rsp->payload.open_session_response.integrity_alg;
+ session->v2_data.crypt_alg =
+ rsp->payload.open_session_response.crypt_alg;
+ session->v2_data.session_state =
+ LANPLUS_STATE_OPEN_SESSION_RECEIEVED;
+
+
+ /*
+ * Verify that we have agreed on a cipher suite
+ */
+ if (rsp->payload.open_session_response.auth_alg !=
+ session->v2_data.requested_auth_alg)
+ {
+ lprintf(LOG_WARNING, "Authentication algorithm 0x%02x is "
+ "not what we requested 0x%02x\n",
+ rsp->payload.open_session_response.auth_alg,
+ session->v2_data.requested_auth_alg);
+ rc = 1;
+ }
+ else if (rsp->payload.open_session_response.integrity_alg !=
+ session->v2_data.requested_integrity_alg)
+ {
+ lprintf(LOG_WARNING, "Integrity algorithm 0x%02x is "
+ "not what we requested 0x%02x\n",
+ rsp->payload.open_session_response.integrity_alg,
+ session->v2_data.requested_integrity_alg);
+ rc = 1;
+ }
+ else if (rsp->payload.open_session_response.crypt_alg !=
+ session->v2_data.requested_crypt_alg)
+ {
+ lprintf(LOG_WARNING, "Encryption algorithm 0x%02x is "
+ "not what we requested 0x%02x\n",
+ rsp->payload.open_session_response.crypt_alg,
+ session->v2_data.requested_crypt_alg);
+ rc = 1;
+ }
+
+ }
+
+ return rc;
+}
+
+
+
+/*
+ * ipmi_lanplus_rakp1
+ *
+ * Build and send the RAKP 1 message as part of the IPMI v2 / RMCP+ session
+ * negotiation protocol. We also read and validate the RAKP 2 message received
+ * from the BMC, here. See section 13.20 of the IPMI v2 specification for
+ * details.
+ *
+ * returns 0 on success
+ * 1 on failure
+ *
+ * Note that failure is only indicated if we have an internal error of
+ * some kind. If we actually get a RAKP 2 message in response to our
+ * RAKP 1 message, any errors will be stored in
+ * session->v2_data.rakp2_return_code and sent to the BMC in the RAKP
+ * 3 message.
+ */
+static int
+ipmi_lanplus_rakp1(struct ipmi_intf * intf)
+{
+ struct ipmi_v2_payload v2_payload;
+ struct ipmi_session * session = intf->session;
+ uint8_t * msg;
+ struct ipmi_rs * rsp;
+ int rc = 0; /* 0 = success, 1 = error, 2 = timeout */
+
+ /*
+ * Build a RAKP 1 message
+ */
+ msg = (uint8_t*)malloc(IPMI_RAKP1_MESSAGE_SIZE);
+ if (msg == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+ memset(msg, 0, IPMI_RAKP1_MESSAGE_SIZE);
+
+
+ msg[0] = 0; /* Message tag */
+
+ msg[1] = 0; /* reserved */
+ msg[2] = 0; /* reserved */
+ msg[3] = 0; /* reserved */
+
+ /* BMC session ID */
+ msg[4] = session->v2_data.bmc_id & 0xff;
+ msg[5] = (session->v2_data.bmc_id >> 8) & 0xff;
+ msg[6] = (session->v2_data.bmc_id >> 16) & 0xff;
+ msg[7] = (session->v2_data.bmc_id >> 24) & 0xff;
+
+
+ /* We need a 16 byte random number */
+ if (lanplus_rand(session->v2_data.console_rand, 16))
+ {
+ // ERROR;
+ lprintf(LOG_ERR, "ERROR generating random number "
+ "in ipmi_lanplus_rakp1");
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ memcpy(msg + 8, session->v2_data.console_rand, 16);
+ #if WORDS_BIGENDIAN
+ lanplus_swap(msg + 8, 16);
+ #endif
+
+ if (verbose > 1)
+ printbuf(session->v2_data.console_rand, 16,
+ ">> Console generated random number");
+
+
+ /*
+ * Requested maximum privilege level.
+ */
+ msg[24] = session->privlvl | session->v2_data.lookupbit;
+ session->v2_data.requested_role = msg[24];
+ msg[25] = 0; /* reserved */
+ msg[26] = 0; /* reserved */
+
+
+ /* Username specification */
+ msg[27] = strlen((const char *)session->username);
+ if (msg[27] > IPMI_MAX_USER_NAME_LENGTH)
+ {
+ lprintf(LOG_ERR, "ERROR: user name too long. "
+ "(Exceeds %d characters)",
+ IPMI_MAX_USER_NAME_LENGTH);
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ memcpy(msg + 28, session->username, msg[27]);
+
+ v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_1;
+ v2_payload.payload_length =
+ IPMI_RAKP1_MESSAGE_SIZE - (16 - msg[27]);
+ v2_payload.payload.rakp_1_message.message = msg;
+
+ rsp = ipmi_lanplus_send_payload(intf, &v2_payload);
+
+ free(msg);
+ msg = NULL;
+
+ if (rsp == NULL)
+ {
+ lprintf(LOG_WARNING, "> Error: no response from RAKP 1 message");
+ return 2;
+ }
+
+ session->v2_data.session_state = LANPLUS_STATE_RAKP_2_RECEIVED;
+
+ if (verbose)
+ lanplus_dump_rakp2_message(rsp, session->v2_data.auth_alg);
+
+
+
+ if (rsp->payload.rakp2_message.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS)
+ {
+ lprintf(LOG_INFO, "RAKP 2 message indicates an error : %s",
+ val2str(rsp->payload.rakp2_message.rakp_return_code,
+ ipmi_rakp_return_codes));
+ rc = 1;
+ }
+
+ else
+ {
+ memcpy(session->v2_data.bmc_rand, rsp->payload.rakp2_message.bmc_rand, 16);
+ memcpy(session->v2_data.bmc_guid, rsp->payload.rakp2_message.bmc_guid, 16);
+
+ if (verbose > 2)
+ printbuf(session->v2_data.bmc_rand, 16, "bmc_rand");
+
+ /*
+ * It is at this point that we have to decode the random number and determine
+ * whether the BMC has authenticated.
+ */
+ if (! lanplus_rakp2_hmac_matches(session,
+ rsp->payload.rakp2_message.key_exchange_auth_code,
+ intf))
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> RAKP 2 HMAC is invalid");
+ session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE;
+ rc = 1;
+ }
+ else
+ {
+ /* Success */
+ session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_NO_ERRORS;
+ }
+ }
+
+ return rc;
+}
+
+
+
+/*
+ * ipmi_lanplus_rakp3
+ *
+ * Build and send the RAKP 3 message as part of the IPMI v2 / RMCP+ session
+ * negotiation protocol. We also read and validate the RAKP 4 message received
+ * from the BMC, here. See section 13.20 of the IPMI v2 specification for
+ * details.
+ *
+ * If the RAKP 2 return code is not IPMI_RAKP_STATUS_NO_ERRORS, we will
+ * exit with an error code immediately after sendint the RAKP 3 message.
+ *
+ * param intf is the intf that holds all the state we are concerned with
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+static int
+ipmi_lanplus_rakp3(struct ipmi_intf * intf)
+{
+ struct ipmi_v2_payload v2_payload;
+ struct ipmi_session * session = intf->session;
+ uint8_t * msg;
+ struct ipmi_rs * rsp;
+
+ assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED);
+
+ /*
+ * Build a RAKP 3 message
+ */
+ msg = (uint8_t*)malloc(IPMI_RAKP3_MESSAGE_MAX_SIZE);
+ if (msg == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+ memset(msg, 0, IPMI_RAKP3_MESSAGE_MAX_SIZE);
+
+
+ msg[0] = 0; /* Message tag */
+ msg[1] = session->v2_data.rakp2_return_code;
+
+ msg[2] = 0; /* reserved */
+ msg[3] = 0; /* reserved */
+
+ /* BMC session ID */
+ msg[4] = session->v2_data.bmc_id & 0xff;
+ msg[5] = (session->v2_data.bmc_id >> 8) & 0xff;
+ msg[6] = (session->v2_data.bmc_id >> 16) & 0xff;
+ msg[7] = (session->v2_data.bmc_id >> 24) & 0xff;
+
+ v2_payload.payload_type = IPMI_PAYLOAD_TYPE_RAKP_3;
+ v2_payload.payload_length = 8;
+ v2_payload.payload.rakp_3_message.message = msg;
+
+ /*
+ * If the rakp2 return code indicates and error, we don't have to
+ * generate an authcode or session integrity key. In that case, we
+ * are simply sending a RAKP 3 message to indicate to the BMC that the
+ * RAKP 2 message caused an error.
+ */
+ if (session->v2_data.rakp2_return_code == IPMI_RAKP_STATUS_NO_ERRORS)
+ {
+ uint32_t auth_length;
+
+ if (lanplus_generate_rakp3_authcode(msg + 8, session, &auth_length, intf))
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> Error generating RAKP 3 authcode");
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ else
+ {
+ /* Success */
+ v2_payload.payload_length += auth_length;
+ }
+
+ /* Generate our Session Integrity Key, K1, and K2 */
+ if (lanplus_generate_sik(session, intf))
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> Error generating session integrity key");
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ else if (lanplus_generate_k1(session))
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> Error generating K1 key");
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ else if (lanplus_generate_k2(session))
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> Error generating K1 key");
+ free(msg);
+ msg = NULL;
+ return 1;
+ }
+ }
+
+
+ rsp = ipmi_lanplus_send_payload(intf, &v2_payload);
+
+ free(msg);
+ msg = NULL;
+
+ if (session->v2_data.rakp2_return_code != IPMI_RAKP_STATUS_NO_ERRORS)
+ {
+ /*
+ * If the previous RAKP 2 message received was deemed erroneous,
+ * we have nothing else to do here. We only sent the RAKP 3 message
+ * to indicate to the BMC that the RAKP 2 message failed.
+ */
+ return 1;
+ }
+ else if (rsp == NULL)
+ {
+ lprintf(LOG_WARNING, "> Error: no response from RAKP 3 message");
+ return 2;
+ }
+
+
+ /*
+ * We have a RAKP 4 message to chew on.
+ */
+ if (verbose)
+ lanplus_dump_rakp4_message(rsp, session->v2_data.auth_alg);
+
+
+ if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS)
+ {
+ lprintf(LOG_INFO, "RAKP 4 message indicates an error : %s",
+ val2str(rsp->payload.rakp4_message.rakp_return_code,
+ ipmi_rakp_return_codes));
+ return 1;
+ }
+
+ else
+ {
+ /* Validate the authcode */
+ if (lanplus_rakp4_hmac_matches(session,
+ rsp->payload.rakp4_message.integrity_check_value,
+ intf))
+ {
+ /* Success */
+ session->v2_data.session_state = LANPLUS_STATE_ACTIVE;
+ }
+ else
+ {
+ /* Error */
+ lprintf(LOG_INFO, "> RAKP 4 message has invalid integrity check value");
+ return 1;
+ }
+ }
+
+ intf->abort = 0;
+ return 0;
+}
+
+
+
+/**
+ * ipmi_lan_close
+ */
+void
+ipmi_lanplus_close(struct ipmi_intf * intf)
+{
+ if (!intf->abort)
+ ipmi_close_session_cmd(intf);
+
+ if (intf->fd >= 0)
+ close(intf->fd);
+
+ ipmi_req_clear_entries();
+
+ if (intf->session) {
+ free(intf->session);
+ intf->session = NULL;
+ }
+
+ intf->session = NULL;
+ intf->opened = 0;
+ intf->manufacturer_id = IPMI_OEM_UNKNOWN;
+ intf = NULL;
+}
+
+
+
+static int
+ipmi_set_session_privlvl_cmd(struct ipmi_intf * intf)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req;
+ uint8_t backupBridgePossible;
+ uint8_t privlvl = intf->session->privlvl;
+
+ if (privlvl <= IPMI_SESSION_PRIV_USER)
+ return 0; /* no need to set higher */
+
+ backupBridgePossible = bridgePossible;
+
+ bridgePossible = 0;
+
+ memset(&req, 0, sizeof(req));
+ req.msg.netfn = IPMI_NETFN_APP;
+ req.msg.cmd = 0x3b;
+ req.msg.data = &privlvl;
+ req.msg.data_len = 1;
+
+ rsp = intf->sendrecv(intf, &req);
+ if (rsp == NULL) {
+ lprintf(LOG_ERR, "Set Session Privilege Level to %s failed",
+ val2str(privlvl, ipmi_privlvl_vals));
+ bridgePossible = backupBridgePossible;
+ return -1;
+ }
+ if (verbose > 2)
+ printbuf(rsp->data, rsp->data_len, "set_session_privlvl");
+
+ if (rsp->ccode > 0) {
+ lprintf(LOG_ERR, "Set Session Privilege Level to %s failed: %s",
+ val2str(privlvl, ipmi_privlvl_vals),
+ val2str(rsp->ccode, completion_code_vals));
+ bridgePossible = backupBridgePossible;
+ return -1;
+ }
+
+ lprintf(LOG_DEBUG, "Set Session Privilege Level to %s\n",
+ val2str(rsp->data[0], ipmi_privlvl_vals));
+
+ bridgePossible = backupBridgePossible;
+
+ return 0;
+}
+
+/**
+ * ipmi_lanplus_open
+ */
+int
+ipmi_lanplus_open(struct ipmi_intf * intf)
+{
+ int rc;
+ int retry;
+ struct get_channel_auth_cap_rsp auth_cap;
+ struct ipmi_session *session;
+
+ if (!intf || !intf->session)
+ return -1;
+ session = intf->session;
+
+
+ if (!session->port)
+ session->port = IPMI_LANPLUS_PORT;
+ if (!session->privlvl)
+ session->privlvl = IPMI_SESSION_PRIV_ADMIN;
+ if (!session->timeout)
+ session->timeout = IPMI_LAN_TIMEOUT;
+ if (!session->retry)
+ session->retry = IPMI_LAN_RETRY;
+
+ if (session->hostname == NULL || strlen((const char *)session->hostname) == 0) {
+ lprintf(LOG_ERR, "No hostname specified!");
+ return -1;
+ }
+
+ intf->abort = 1;
+
+
+ /* Setup our lanplus session state */
+ session->v2_data.auth_alg = IPMI_AUTH_RAKP_NONE;
+ session->v2_data.crypt_alg = IPMI_CRYPT_NONE;
+ session->v2_data.console_id = 0x00;
+ session->v2_data.bmc_id = 0x00;
+ session->sol_data.sequence_number = 1;
+ //session->sol_data.last_received_sequence_number = 0;
+ //session->sol_data.last_received_byte_count = 0;
+ memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE);
+
+ /* Kg is set in ipmi_intf */
+ //memset(session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE);
+
+ if (ipmi_intf_socket_connect (intf) == -1) {
+ lprintf(LOG_ERR, "Could not open socket!");
+ return -1;
+ }
+
+ if (intf->fd < 0) {
+ lperror(LOG_ERR, "Connect to %s failed",
+ session->hostname);
+ intf->close(intf);
+ return -1;
+ }
+
+ intf->opened = 1;
+
+ /*
+ *
+ * Make sure the BMC supports IPMI v2 / RMCP+
+ */
+ if (!ipmi_oem_active(intf, "i82571spt") &&
+ ipmi_get_auth_capabilities_cmd(intf, &auth_cap)) {
+ lprintf(LOG_INFO, "Error issuing Get Channel "
+ "Authentication Capabilies request");
+ goto fail;
+ }
+
+ if (!ipmi_oem_active(intf, "i82571spt") && ! auth_cap.v20_data_available)
+ {
+ lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+");
+ goto fail;
+ }
+
+ /*
+ * If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence
+ * needs to restart. The individual messages are not individually retryable,
+ * as the session state is advancing.
+ */
+ for (retry = 0; retry < IPMI_LAN_RETRY; retry++) {
+ session->v2_data.session_state = LANPLUS_STATE_PRESESSION;
+ /*
+ * Open session
+ */
+ if ((rc = ipmi_lanplus_open_session(intf)) == 1) {
+ intf->close(intf);
+ goto fail;
+ }
+ if (rc == 2) {
+ lprintf(LOG_DEBUG, "Retry lanplus open session, %d", retry);
+ continue;
+ }
+ /*
+ * RAKP 1
+ */
+ if ((rc = ipmi_lanplus_rakp1(intf)) == 1) {
+ intf->close(intf);
+ goto fail;
+ }
+ if (rc == 2) {
+ lprintf(LOG_DEBUG, "Retry lanplus rakp1, %d", retry);
+ continue;
+ }
+ /*
+ * RAKP 3
+ */
+ if ((rc = ipmi_lanplus_rakp3(intf)) == 1) {
+ intf->close(intf);
+ goto fail;
+ }
+ if (rc == 0) break;
+ lprintf(LOG_DEBUG,"Retry lanplus rakp3, %d", retry);
+ }
+
+ lprintf(LOG_DEBUG, "IPMIv2 / RMCP+ SESSION OPENED SUCCESSFULLY\n");
+
+ if (!ipmi_oem_active(intf, "i82571spt")) {
+ rc = ipmi_set_session_privlvl_cmd(intf);
+ if (rc < 0) {
+ intf->close(intf);
+ goto fail;
+ }
+ }
+ intf->manufacturer_id = ipmi_get_oem(intf);
+ bridgePossible = 1;
+
+ /* automatically detect interface request and response sizes */
+ hpm2_detect_max_payload_size(intf);
+
+ return intf->fd;
+
+ fail:
+ lprintf(LOG_ERR, "Error: Unable to establish IPMI v2 / RMCP+ session");
+ intf->opened = 0;
+ return -1;
+}
+
+
+
+void test_crypt1(void)
+{
+ uint8_t key[] =
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14};
+
+ uint16_t bytes_encrypted;
+ uint16_t bytes_decrypted;
+ uint8_t decrypt_buffer[1000];
+ uint8_t encrypt_buffer[1000];
+
+ uint8_t data[] =
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
+ 0x11, 0x12};
+
+ printbuf(data, sizeof(data), "original data");
+
+ if (lanplus_encrypt_payload(IPMI_CRYPT_AES_CBC_128,
+ key,
+ data,
+ sizeof(data),
+ encrypt_buffer,
+ &bytes_encrypted))
+ {
+ lprintf(LOG_ERR, "Encrypt test failed");
+ assert(0);
+ }
+ printbuf(encrypt_buffer, bytes_encrypted, "encrypted payload");
+
+
+ if (lanplus_decrypt_payload(IPMI_CRYPT_AES_CBC_128,
+ key,
+ encrypt_buffer,
+ bytes_encrypted,
+ decrypt_buffer,
+ &bytes_decrypted))
+ {
+ lprintf(LOG_ERR, "Decrypt test failed\n");
+ assert(0);
+ }
+ printbuf(decrypt_buffer, bytes_decrypted, "decrypted payload");
+
+ lprintf(LOG_DEBUG, "\nDone testing the encrypt/decyrpt methods!\n");
+ exit(0);
+}
+
+
+
+void test_crypt2(void)
+{
+ uint8_t key[] =
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14};
+ uint8_t iv[] =
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14};
+ uint8_t data[8] = "12345678";
+
+ uint8_t encrypt_buffer[1000];
+ uint8_t decrypt_buffer[1000];
+ uint32_t bytes_encrypted;
+ uint32_t bytes_decrypted;
+
+ printbuf((const uint8_t *)data, strlen((const char *)data), "input data");
+
+ lanplus_encrypt_aes_cbc_128(iv,
+ key,
+ data,
+ strlen((const char *)data),
+ encrypt_buffer,
+ &bytes_encrypted);
+ printbuf((const uint8_t *)encrypt_buffer, bytes_encrypted, "encrypt_buffer");
+
+ lanplus_decrypt_aes_cbc_128(iv,
+ key,
+ encrypt_buffer,
+ bytes_encrypted,
+ decrypt_buffer,
+ &bytes_decrypted);
+ printbuf((const uint8_t *)decrypt_buffer, bytes_decrypted, "decrypt_buffer");
+
+ lprintf(LOG_INFO, "\nDone testing the encrypt/decyrpt methods!\n");
+ exit(0);
+}
+
+
+/**
+ * send a get device id command to keep session active
+ */
+static int
+ipmi_lanplus_keepalive(struct ipmi_intf * intf)
+{
+ struct ipmi_rs * rsp;
+ struct ipmi_rq req = { msg: {
+ netfn: IPMI_NETFN_APP,
+ cmd: 1,
+ }};
+
+ if (!intf->opened)
+ return 0;
+
+ rsp = intf->sendrecv(intf, &req);
+ while (rsp != NULL && is_sol_packet(rsp)) {
+ /* rsp was SOL data instead of our answer */
+ /* since it didn't go through the sol recv, do sol recv stuff here */
+ ack_sol_packet(intf, rsp);
+ check_sol_packet_for_new_data(intf, rsp);
+ if (rsp->data_len)
+ intf->session->sol_data.sol_input_handler(rsp);
+ rsp = ipmi_lan_poll_recv(intf);
+ if (rsp == NULL) /* the get device id answer never got back, but retry mechanism was bypassed by SOL data */
+ return 0; /* so get device id command never returned, the connection is still alive */
+ }
+
+ if (rsp == NULL)
+ return -1;
+ if (rsp->ccode > 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * ipmi_lanplus_setup
+ */
+static int ipmi_lanplus_setup(struct ipmi_intf * intf)
+{
+ //test_crypt1();
+ assert("ipmi_lanplus_setup");
+
+ if (lanplus_seed_prng(16))
+ return -1;
+
+ intf->session = malloc(sizeof(struct ipmi_session));
+ if (intf->session == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return -1;
+ }
+ memset(intf->session, 0, sizeof(struct ipmi_session));
+
+ /* setup default LAN maximum request and response sizes */
+ intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE;
+ intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE;
+
+ return 0;
+}
+
+static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size)
+{
+ if (intf->session->cipher_suite_id == 3) {
+ /*
+ * encrypted payload can only be multiple of 16 bytes
+ */
+ size &= ~15;
+
+ /*
+ * decrement payload size on confidentiality header size
+ * plus minimal confidentiality trailer size
+ */
+ size -= (16 + 1);
+ }
+
+ intf->max_request_data_size = size;
+}
+
+static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size)
+{
+ if (intf->session->cipher_suite_id == 3) {
+ /*
+ * encrypted payload can only be multiple of 16 bytes
+ */
+ size &= ~15;
+
+ /*
+ * decrement payload size on confidentiality header size
+ * plus minimal confidentiality trailer size
+ */
+ size -= (16 + 1);
+ }
+
+ intf->max_response_data_size = size;
+}
diff --git a/src/plugins/lanplus/lanplus.h b/src/plugins/lanplus/lanplus.h
new file mode 100644
index 0000000..4b6ae1e
--- /dev/null
+++ b/src/plugins/lanplus/lanplus.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef IPMI_LANPLUS_H
+#define IPMI_LANPLUS_H
+
+#include <ipmitool/ipmi.h>
+
+#define IPMI_LANPLUS_PORT 0x26f
+
+/*
+ * RAKP return codes. These values come from table 13-15 of the IPMI v2
+ * specification.
+ */
+#define IPMI_RAKP_STATUS_NO_ERRORS 0x00
+#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION 0x01
+#define IPMI_RAKP_STATUS_INVALID_SESSION_ID 0x02
+#define IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE 0x03
+#define IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM 0x04
+#define IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM 0x05
+#define IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD 0x06
+#define IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD 0x07
+#define IPMI_RAKP_STATUS_INACTIVE_SESSION_ID 0x08
+#define IPMI_RAKP_STATUS_INVALID_ROLE 0x09
+#define IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED 0x0A
+#define IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE 0x0B
+#define IPMI_RAKP_STATUS_INVALID_NAME_LENGTH 0x0C
+#define IPMI_RAKP_STATUS_UNAUTHORIZED_NAME 0x0D
+#define IPMI_RAKP_STATUS_UNAUTHORIZED_GUID 0x0E
+#define IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE 0x0F
+#define IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM 0x10
+#define IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH 0x11
+#define IPMI_RAKP_STATUS_ILLEGAL_PARAMTER 0x12
+
+
+#define IPMI_LAN_CHANNEL_1 0x07
+#define IPMI_LAN_CHANNEL_2 0x06
+#define IPMI_LAN_CHANNEL_E 0x0e
+
+#define IPMI_LAN_TIMEOUT 1
+#define IPMI_LAN_RETRY 4
+
+#define IPMI_PRIV_CALLBACK 1
+#define IPMI_PRIV_USER 2
+#define IPMI_PRIV_OPERATOR 3
+#define IPMI_PRIV_ADMIN 4
+#define IPMI_PRIV_OEM 5
+
+
+#define IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE 0x10
+
+
+/* Session message offsets, from table 13-8 of the v2 specification */
+#define IPMI_LANPLUS_OFFSET_AUTHTYPE 0x04
+#define IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE 0x05
+#define IPMI_LANPLUS_OFFSET_SESSION_ID 0x06
+#define IPMI_LANPLUS_OFFSET_SEQUENCE_NUM 0x0A
+#define IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE 0x0E
+#define IPMI_LANPLUS_OFFSET_PAYLOAD 0x10
+
+
+#define IPMI_GET_CHANNEL_AUTH_CAP 0x38
+
+/*
+ * TODO: these are wild guesses and should be checked
+ */
+#define IPMI_MAX_CONF_HEADER_SIZE 0x20
+#define IPMI_MAX_PAYLOAD_SIZE 0xFFFF /* Includes confidentiality header/trailer */
+#define IPMI_MAX_CONF_TRAILER_SIZE 0x20
+#define IPMI_MAX_INTEGRITY_PAD_SIZE 0x20
+#define IPMI_MAX_AUTH_CODE_SIZE 0x20
+
+#define IPMI_REQUEST_MESSAGE_SIZE 0x07
+#define IPMI_MAX_MAC_SIZE 0x14 /* The largest mac we ever expect to generate */
+#define IPMI_SHA1_AUTHCODE_SIZE 0x0C
+
+/*
+ *This is accurate, as long as we're only passing 1 auth algorithm,
+ * one integrity algorithm, and 1 encyrption alogrithm
+ */
+#define IPMI_OPEN_SESSION_REQUEST_SIZE 32
+#define IPMI_RAKP1_MESSAGE_SIZE 44
+#define IPMI_RAKP3_MESSAGE_MAX_SIZE 28
+
+#define IPMI_MAX_USER_NAME_LENGTH 16
+
+extern const struct valstr ipmi_privlvl_vals[];
+extern const struct valstr ipmi_authtype_vals[];
+
+extern struct ipmi_intf ipmi_lanplus_intf;
+
+struct ipmi_rs * ipmi_lan_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req);
+int ipmi_lanplus_open(struct ipmi_intf * intf);
+void ipmi_lanplus_close(struct ipmi_intf * intf);
+int ipmiv2_lan_ping(struct ipmi_intf * intf);
+
+#endif /*IPMI_LAN_H*/
diff --git a/src/plugins/lanplus/lanplus_crypt.c b/src/plugins/lanplus/lanplus_crypt.c
new file mode 100644
index 0000000..54fd5cb
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_crypt.c
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <assert.h>
+#include <string.h>
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <ipmitool/bswap.h>
+#include <ipmitool/log.h>
+#include "lanplus.h"
+#include "lanplus_crypt.h"
+#include "lanplus_crypt_impl.h"
+
+
+
+/*
+ * lanplus_rakp2_hmac_matches
+ *
+ * param session holds all the state data that we need to generate the hmac
+ * param hmac is the HMAC sent by the BMC in the RAKP 2 message
+ *
+ * The HMAC was generated [per RFC2404] from :
+ *
+ * SIDm - Remote console session ID
+ * SIDc - BMC session ID
+ * Rm - Remote console random number
+ * Rc - BMC random number
+ * GUIDc - BMC guid
+ * ROLEm - Requested privilege level (entire byte)
+ * ULENGTHm - Username length
+ * <UNAMEm> - Username (absent for null user names)
+ *
+ * generated by using Kuid. I am aware that the subscripts on the values
+ * look backwards, but that's the way they are written in the specification.
+ *
+ * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
+ *
+ * return 0 on success (the authcode matches)
+ * 1 on failure (the authcode does not match)
+ */
+int
+lanplus_rakp2_hmac_matches(const struct ipmi_session * session,
+ const uint8_t * bmc_mac, struct ipmi_intf * intf)
+{
+ uint8_t * buffer;
+ int bufferLength, i;
+ uint8_t mac[20];
+ uint32_t macLength;
+
+ uint32_t SIDm_lsbf, SIDc_lsbf;
+
+
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ return 1;
+
+ /* We don't yet support other algorithms */
+ assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
+
+
+ bufferLength =
+ 4 + /* SIDm */
+ 4 + /* SIDc */
+ 16 + /* Rm */
+ 16 + /* Rc */
+ 16 + /* GUIDc */
+ 1 + /* ROLEm */
+ 1 + /* ULENGTHm */
+ strlen((const char *)session->username); /* optional */
+
+ buffer = malloc(bufferLength);
+ if (buffer == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+ /*
+ * Fill the buffer. I'm assuming that we're using the LSBF representation of the
+ * multibyte numbers in use.
+ */
+
+ /* SIDm */
+ SIDm_lsbf = session->v2_data.console_id;
+ #if WORDS_BIGENDIAN
+ SIDm_lsbf = BSWAP_32(SIDm_lsbf);
+ #endif
+
+ memcpy(buffer, &SIDm_lsbf, 4);
+
+ /* SIDc */
+ SIDc_lsbf = session->v2_data.bmc_id;
+ #if WORDS_BIGENDIAN
+ SIDc_lsbf = BSWAP_32(SIDc_lsbf);
+ #endif
+ memcpy(buffer + 4, &SIDc_lsbf, 4);
+
+ /* Rm */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ buffer[8 + i] = session->v2_data.console_rand[i];
+ #endif
+
+ /* Rc */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ buffer[24 + i] = session->v2_data.bmc_rand[i];
+ #endif
+
+ /* GUIDc */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ buffer[40 + i] = session->v2_data.bmc_guid[i];
+ #endif
+
+ /* ROLEm */
+ buffer[56] = session->v2_data.requested_role;
+
+ if (ipmi_oem_active(intf, "i82571spt")) {
+ /*
+ * The HMAC calculation code in the Intel 82571 GbE
+ * skips this bit! Looks like a GbE bug, but we need
+ * to work around it here anyway...
+ */
+ buffer[56] &= ~0x10;
+ }
+
+ /* ULENGTHm */
+ buffer[57] = strlen((const char *)session->username);
+
+ /* UserName [optional] */
+ for (i = 0; i < buffer[57]; ++i)
+ buffer[58 + i] = session->username[i];
+
+ if (verbose > 2)
+ {
+ printbuf((const uint8_t *)buffer, bufferLength, ">> rakp2 mac input buffer");
+ printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key");
+ }
+
+ /*
+ * The buffer is complete. Let's hash.
+ */
+ lanplus_HMAC(session->v2_data.auth_alg,
+ session->authcode,
+ IPMI_AUTHCODE_BUFFER_SIZE,
+ buffer,
+ bufferLength,
+ mac,
+ &macLength);
+
+ free(buffer);
+ buffer = NULL;
+
+
+ if (verbose > 2)
+ {
+ printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console");
+ }
+
+ return (memcmp(bmc_mac, mac, macLength) == 0);
+}
+
+
+
+/*
+ * lanplus_rakp4_hmac_matches
+ *
+ * param session holds all the state data that we need to generate the hmac
+ * param hmac is the HMAC sent by the BMC in the RAKP 4 message
+ *
+ * The HMAC was generated [per RFC2404] from :
+ *
+ * Rm - Remote console random number
+ * SIDc - BMC session ID
+ * GUIDc - BMC guid
+ *
+ * generated by using SIK (the session integrity key). I am aware that the
+ * subscripts on the values look backwards, but that's the way they are
+ * written in the specification.
+ *
+ * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
+ *
+ * return 1 on success (the authcode matches)
+ * 0 on failure (the authcode does not match)
+ *
+ */
+int
+lanplus_rakp4_hmac_matches(const struct ipmi_session * session,
+ const uint8_t * bmc_mac, struct ipmi_intf * intf)
+{
+ uint8_t * buffer;
+ int bufferLength, i;
+ uint8_t mac[20];
+ uint32_t macLength;
+ uint32_t SIDc_lsbf;
+
+ if (ipmi_oem_active(intf, "intelplus")){
+ /* Intel BMC responds with the integrity Algorithm in RAKP4 */
+ if (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)
+ return 1;
+
+ /* We don't yet support other algorithms */
+ assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
+ } else {
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ return 1;
+
+ /* We don't yet support other algorithms */
+ assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
+ }
+
+ bufferLength =
+ 16 + /* Rm */
+ 4 + /* SIDc */
+ 16; /* GUIDc */
+
+ buffer = (uint8_t *)malloc(bufferLength);
+ if (buffer == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+ /*
+ * Fill the buffer. I'm assuming that we're using the LSBF representation of the
+ * multibyte numbers in use.
+ */
+
+ /* Rm */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ buffer[i] = session->v2_data.console_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ buffer[i] = session->v2_data.console_rand[i];
+ #endif
+
+
+ /* SIDc */
+ SIDc_lsbf = session->v2_data.bmc_id;
+ #if WORDS_BIGENDIAN
+ SIDc_lsbf = BSWAP_32(SIDc_lsbf);
+ #endif
+ memcpy(buffer + 16, &SIDc_lsbf, 4);
+
+
+ /* GUIDc */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ buffer[i + 20] = session->v2_data.bmc_guid[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ buffer[i + 20] = session->v2_data.bmc_guid[i];
+ #endif
+
+
+ if (verbose > 2)
+ {
+ printbuf((const uint8_t *)buffer, bufferLength, ">> rakp4 mac input buffer");
+ printbuf(session->v2_data.sik, 20l, ">> rakp4 mac key (sik)");
+ }
+
+
+ /*
+ * The buffer is complete. Let's hash.
+ */
+ lanplus_HMAC((ipmi_oem_active(intf, "intelplus"))
+ ? session->v2_data.integrity_alg
+ : session->v2_data.auth_alg ,
+ session->v2_data.sik,
+ IPMI_SIK_BUFFER_SIZE,
+ buffer,
+ bufferLength,
+ mac,
+ &macLength);
+
+ if (verbose > 2)
+ {
+ printbuf(bmc_mac, macLength, ">> rakp4 mac as computed by the BMC");
+ printbuf(mac, macLength, ">> rakp4 mac as computed by the remote console");
+ }
+
+
+
+ free(buffer);
+ buffer = NULL;
+ assert(macLength == 20);
+ return (memcmp(bmc_mac, mac, 12) == 0);
+}
+
+
+
+/*
+ * lanplus_generate_rakp3_auth_code
+ *
+ * This auth code is an HMAC generated with :
+ *
+ * Rc - BMC random number
+ * SIDm - Console session ID
+ * ROLEm - Requested privilege level (entire byte)
+ * ULENGTHm - Username length
+ * <USERNAME> - Usename (absent for null usernames)
+ *
+ * The key used to generated the MAC is Kuid
+ *
+ * I am aware that the subscripts look backwards, but that is the way they are
+ * written in the spec.
+ *
+ * param output_buffer [out] will hold the generated MAC
+ * param session [in] holds all the state data we need to generate the auth code
+ * param mac_length [out] will be set to the length of the auth code
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int
+lanplus_generate_rakp3_authcode(uint8_t * output_buffer,
+ const struct ipmi_session * session,
+ uint32_t * mac_length, struct ipmi_intf * intf)
+{
+ int ret = 0;
+ int input_buffer_length, i;
+ uint8_t * input_buffer;
+ uint32_t SIDm_lsbf;
+
+
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ {
+ *mac_length = 0;
+ return 0;
+ }
+
+ /* We don't yet support other algorithms */
+ assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
+
+ input_buffer_length =
+ 16 + /* Rc */
+ 4 + /* SIDm */
+ 1 + /* ROLEm */
+ 1 + /* ULENGTHm */
+ strlen((const char *)session->username);
+
+ input_buffer = malloc(input_buffer_length);
+ if (input_buffer == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+ /*
+ * Fill the buffer. I'm assuming that we're using the LSBF representation of the
+ * multibyte numbers in use.
+ */
+
+ /* Rc */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ input_buffer[i] = session->v2_data.bmc_rand[i];
+ #endif
+
+ /* SIDm */
+ SIDm_lsbf = session->v2_data.console_id;
+ #if WORDS_BIGENDIAN
+ SIDm_lsbf = BSWAP_32(SIDm_lsbf);
+ #endif
+ memcpy(input_buffer + 16, &SIDm_lsbf, 4);
+
+ /* ROLEm */
+ if (ipmi_oem_active(intf, "intelplus") || ipmi_oem_active(intf, "i82571spt"))
+ input_buffer[20] = session->privlvl;
+ else
+ input_buffer[20] = session->v2_data.requested_role;
+
+ /* ULENGTHm */
+ input_buffer[21] = strlen((const char *)session->username);
+
+ /* USERNAME */
+ for (i = 0; i < input_buffer[21]; ++i)
+ input_buffer[22 + i] = session->username[i];
+
+ if (verbose > 2)
+ {
+ printbuf((const uint8_t *)input_buffer, input_buffer_length, ">> rakp3 mac input buffer");
+ printbuf((const uint8_t *)session->authcode, IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp3 mac key");
+ }
+
+ lanplus_HMAC(session->v2_data.auth_alg,
+ session->authcode,
+ IPMI_AUTHCODE_BUFFER_SIZE,
+ input_buffer,
+ input_buffer_length,
+ output_buffer,
+ mac_length);
+
+ if (verbose > 2)
+ printbuf((const uint8_t *)output_buffer, *mac_length, "generated rakp3 mac");
+
+
+ free(input_buffer);
+ input_buffer = NULL;
+
+ return ret;
+}
+
+
+
+/*
+ * lanplus_generate_sik
+ *
+ * Generate the session integrity key (SIK) used for integrity checking
+ * during the IPMI v2 / RMCP+ session
+ *
+ * This session integrity key is a HMAC generated with :
+ *
+ * Rm - Console generated random number
+ * Rc - BMC generated random number
+ * ROLEm - Requested privilege level (entire byte)
+ * ULENGTHm - Username length
+ * <USERNAME> - Usename (absent for null usernames)
+ *
+ * The key used to generated the SIK is Kg if Kg is not null (two-key logins are
+ * enabled). Otherwise Kuid (the user authcode) is used as the key to genereate
+ * the SIK.
+ *
+ * I am aware that the subscripts look backwards, but that is the way they are
+ * written in the spec.
+ *
+ * param session [in/out] contains our input and output fields.
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int
+lanplus_generate_sik(struct ipmi_session * session, struct ipmi_intf * intf)
+{
+ uint8_t * input_buffer;
+ int input_buffer_length, i;
+ uint8_t * input_key;
+ uint32_t mac_length;
+
+
+ memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE);
+
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ return 0;
+
+ /* We don't yet support other algorithms */
+ assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
+
+ input_buffer_length =
+ 16 + /* Rm */
+ 16 + /* Rc */
+ 1 + /* ROLEm */
+ 1 + /* ULENGTHm */
+ strlen((const char *)session->username);
+
+ input_buffer = malloc(input_buffer_length);
+ if (input_buffer == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+ /*
+ * Fill the buffer. I'm assuming that we're using the LSBF representation of the
+ * multibyte numbers in use.
+ */
+
+ /* Rm */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ input_buffer[i] = session->v2_data.console_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ input_buffer[i] = session->v2_data.console_rand[i];
+ #endif
+
+
+ /* Rc */
+ #if WORDS_BIGENDIAN
+ for (i = 0; i < 16; ++i)
+ input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i];
+ #else
+ for (i = 0; i < 16; ++i)
+ input_buffer[16 + i] = session->v2_data.bmc_rand[i];
+ #endif
+
+ /* ROLEm */
+ input_buffer[32] = session->v2_data.requested_role;
+
+ if (ipmi_oem_active(intf, "i82571spt")) {
+ /*
+ * The HMAC calculation code in the Intel 82571 GbE
+ * skips this bit! Looks like a GbE bug, but we need
+ * to work around it here anyway...
+ */
+ input_buffer[32] &= ~0x10;
+ }
+
+ /* ULENGTHm */
+ input_buffer[33] = strlen((const char *)session->username);
+
+ /* USERNAME */
+ for (i = 0; i < input_buffer[33]; ++i)
+ input_buffer[34 + i] = session->username[i];
+
+ if (session->v2_data.kg[0])
+ {
+ /* We will be hashing with Kg */
+ /*
+ * Section 13.31 of the IPMI v2 spec describes the SIK creation
+ * using Kg. It specifies that Kg should not be truncated.
+ * Kg is set in ipmi_intf.
+ */
+ input_key = session->v2_data.kg;
+ }
+ else
+ {
+ /* We will be hashing with Kuid */
+ input_key = session->authcode;
+ }
+
+
+ if (verbose >= 2)
+ printbuf((const uint8_t *)input_buffer, input_buffer_length, "session integrity key input");
+
+ lanplus_HMAC(session->v2_data.auth_alg,
+ input_key,
+ IPMI_AUTHCODE_BUFFER_SIZE,
+ input_buffer,
+ input_buffer_length,
+ session->v2_data.sik,
+ &mac_length);
+
+ free(input_buffer);
+ input_buffer = NULL;
+ assert(mac_length == 20);
+
+ /*
+ * The key MAC generated is 20 bytes, but we will only be using the first
+ * 12 for SHA1 96
+ */
+ if (verbose >= 2)
+ printbuf(session->v2_data.sik, 20, "Generated session integrity key");
+
+ return 0;
+}
+
+
+
+/*
+ * lanplus_generate_k1
+ *
+ * Generate K1, the key presumably used to generate integrity authcodes
+ *
+ * We use the authentication algorithm to generated the HMAC, using
+ * the session integrity key (SIK) as our key.
+ *
+ * param session [in/out].
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int
+lanplus_generate_k1(struct ipmi_session * session)
+{
+ uint32_t mac_length;
+
+ uint8_t CONST_1[] =
+ {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ memcpy(session->v2_data.k1, CONST_1, 20);
+ else
+ {
+ lanplus_HMAC(session->v2_data.auth_alg,
+ session->v2_data.sik,
+ IPMI_SIK_BUFFER_SIZE, /* SIK length */
+ CONST_1,
+ 20,
+ session->v2_data.k1,
+ &mac_length);
+ assert(mac_length == 20);
+ }
+
+ if (verbose >= 2)
+ printbuf(session->v2_data.k1, 20, "Generated K1");
+
+ return 0;
+}
+
+
+
+/*
+ * lanplus_generate_k2
+ *
+ * Generate K2, the key used for RMCP+ AES encryption.
+ *
+ * We use the authentication algorithm to generated the HMAC, using
+ * the session integrity key (SIK) as our key.
+ *
+ * param session [in/out].
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int
+lanplus_generate_k2(struct ipmi_session * session)
+{
+ uint32_t mac_length;
+
+ uint8_t CONST_2[] =
+ {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
+
+ if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
+ memcpy(session->v2_data.k2, CONST_2, 20);
+ else
+ {
+ lanplus_HMAC(session->v2_data.auth_alg,
+ session->v2_data.sik,
+ IPMI_SIK_BUFFER_SIZE, /* SIK length */
+ CONST_2,
+ 20,
+ session->v2_data.k2,
+ &mac_length);
+ assert(mac_length == 20);
+ }
+
+ if (verbose >= 2)
+ printbuf(session->v2_data.k2, 20, "Generated K2");
+
+ return 0;
+}
+
+
+
+/*
+ * lanplus_encrypt_payload
+ *
+ * Perform the appropriate encryption on the input data. Output the encrypted
+ * data to output, including the required confidentiality header and trailer.
+ * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and
+ * set bytes_written to input_length.
+ *
+ * param crypt_alg specifies the encryption algorithm (from table 13-19 of the
+ * IPMI v2 spec)
+ * param key is the used as input to the encryption algorithmf
+ * param input is the input data to be encrypted
+ * param input_length is the length of the input data to be encrypted
+ * param output is the cipher text generated by the encryption process
+ * param bytes_written is the number of bytes written during the encryption
+ * process
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int
+lanplus_encrypt_payload(uint8_t crypt_alg,
+ const uint8_t * key, const uint8_t * input,
+ uint32_t input_length, uint8_t * output,
+ uint16_t * bytes_written)
+{
+ uint8_t * padded_input;
+ uint32_t mod, i, bytes_encrypted;
+ uint8_t pad_length = 0;
+
+ if (crypt_alg == IPMI_CRYPT_NONE)
+ {
+ /* Just copy the input to the output */
+ *bytes_written = input_length;
+ return 0;
+ }
+
+ /* Currently, we only support AES */
+ assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
+ assert(input_length <= IPMI_MAX_PAYLOAD_SIZE);
+
+
+ /*
+ * The input to the AES encryption algorithm has to be a multiple of the
+ * block size (16 bytes). The extra byte we are adding is the pad length
+ * byte.
+ */
+ mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE;
+ if (mod)
+ pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod;
+
+ padded_input = (uint8_t*)malloc(input_length + pad_length + 1);
+ if (padded_input == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+ memcpy(padded_input, input, input_length);
+
+ /* add the pad */
+ for (i = 0; i < pad_length; ++i)
+ padded_input[input_length + i] = i + 1;
+
+ /* add the pad length */
+ padded_input[input_length + pad_length] = pad_length;
+
+ /* Generate an initialization vector, IV, for the encryption process */
+ if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE))
+ {
+ lprintf(LOG_ERR, "lanplus_encrypt_payload: Error generating IV");
+ if (padded_input != NULL) {
+ free(padded_input);
+ padded_input = NULL;
+ }
+ return 1;
+ }
+
+ if (verbose > 2)
+ printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector");
+
+
+
+ lanplus_encrypt_aes_cbc_128(output, /* IV */
+ key, /* K2 */
+ padded_input, /* Data to encrypt */
+ input_length + pad_length + 1, /* Input length */
+ output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output */
+ &bytes_encrypted); /* bytes written */
+
+ *bytes_written =
+ IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */
+ bytes_encrypted;
+
+ free(padded_input);
+ padded_input = NULL;
+
+ return 0;
+}
+
+
+
+/*
+ * lanplus_has_valid_auth_code
+ *
+ * Determine whether the packets authcode field is valid for packet.
+ *
+ * We always return success if any of the following are true.
+ * - this is not an IPMIv2 packet
+ * - the session is not yet active
+ * - the packet specifies that it is not authenticated
+ * - the integrity algorithm agreed upon during session creation is "none"
+ *
+ * The authcode is computed using the specified integrity algorithm starting
+ * with the AuthType / Format field, and ending with the field immediately
+ * preceeding the authcode itself.
+ *
+ * The key key used to generate the authcode MAC is K1.
+ *
+ * param rs holds the response structure.
+ * param session holds our session state, including our chosen algorithm, key, etc.
+ *
+ * returns 1 on success (authcode is valid)
+ * 0 on failure (autchode integrity check failed)
+ */
+int
+lanplus_has_valid_auth_code(struct ipmi_rs * rs, struct ipmi_session * session)
+{
+ uint8_t * bmc_authcode;
+ uint8_t generated_authcode[IPMI_MAX_MAC_SIZE];
+ uint32_t generated_authcode_length;
+
+
+ if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) ||
+ (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) ||
+ (! rs->session.bAuthenticated) ||
+ (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE))
+ return 1;
+
+ /* We only support SHA1-96 now */
+ assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
+
+ /*
+ * For SHA1-96, the authcode will be the last 12 bytes in the packet
+ */
+ bmc_authcode = rs->data + (rs->data_len - IPMI_SHA1_AUTHCODE_SIZE);
+
+ lanplus_HMAC(session->v2_data.integrity_alg,
+ session->v2_data.k1,
+ IPMI_AUTHCODE_BUFFER_SIZE,
+ rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE,
+ rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
+ generated_authcode,
+ &generated_authcode_length);
+
+ if (verbose > 3)
+ {
+ lprintf(LOG_DEBUG+2, "Validating authcode");
+ printbuf(session->v2_data.k1, 20, "K1");
+ printbuf(rs->data + IPMI_LANPLUS_OFFSET_AUTHTYPE,
+ rs->data_len - IPMI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
+ "Authcode Input Data");
+ printbuf(generated_authcode, 12, "Generated authcode");
+ printbuf(bmc_authcode, 12, "Expected authcode");
+ }
+
+
+ assert(generated_authcode_length == 20);
+ return (memcmp(bmc_authcode, generated_authcode, 12) == 0);
+}
+
+
+
+/*
+ * lanplus_decrypt_payload
+ *
+ *
+ * param input points to the beginning of the payload (which will be the IV if
+ * we are using AES)
+ * param payload_size [out] will be set to the size of the payload EXCLUDING
+ * padding
+ *
+ * returns 0 on success (we were able to successfully decrypt the packet)
+ * 1 on failure (we were unable to successfully decrypt the packet)
+ */
+int
+lanplus_decrypt_payload(uint8_t crypt_alg, const uint8_t * key,
+ const uint8_t * input, uint32_t input_length,
+ uint8_t * output, uint16_t * payload_size)
+{
+ uint8_t * decrypted_payload;
+ uint32_t bytes_decrypted;
+
+ if (crypt_alg == IPMI_CRYPT_NONE)
+ {
+ /* We are not encrypted. The paylaod size is is everything. */
+ *payload_size = input_length;
+ memmove(output, input, input_length);
+ return 0;
+ }
+
+ /* We only support AES */
+ assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
+
+ decrypted_payload = (uint8_t*)malloc(input_length);
+ if (decrypted_payload == NULL) {
+ lprintf(LOG_ERR, "ipmitool: malloc failure");
+ return 1;
+ }
+
+
+ lanplus_decrypt_aes_cbc_128(input, /* IV */
+ key, /* Key */
+ input +
+ IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Data to decrypt */
+ input_length -
+ IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* Input length */
+ decrypted_payload, /* output */
+ &bytes_decrypted); /* bytes written */
+
+ if (bytes_decrypted != 0)
+ {
+ /* Success */
+ uint8_t conf_pad_length;
+ int i;
+
+ memmove(output,
+ decrypted_payload,
+ bytes_decrypted);
+
+ /*
+ * We have to determine the payload size, by substracting the padding, etc.
+ * The last byte of the decrypted payload is the confidentiality pad length.
+ */
+ conf_pad_length = decrypted_payload[bytes_decrypted - 1];
+ *payload_size = bytes_decrypted - conf_pad_length - 1;
+
+ /*
+ * Extra test to make sure that the padding looks like it should (should start
+ * with 0x01, 0x02, 0x03, etc...
+ */
+ for (i = 0; i < conf_pad_length; ++i)
+ {
+ if (decrypted_payload[*payload_size + i] != (i + 1))
+ {
+ lprintf(LOG_ERR, "Malformed payload padding");
+ assert(0);
+ }
+ }
+ }
+ else
+ {
+ lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes");
+ assert(0);
+ }
+
+ free(decrypted_payload);
+ decrypted_payload = NULL;
+ return (bytes_decrypted == 0);
+}
diff --git a/src/plugins/lanplus/lanplus_crypt.h b/src/plugins/lanplus/lanplus_crypt.h
new file mode 100644
index 0000000..d69cc9b
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_crypt.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef IPMI_LANPLUS_CRYPT_H
+#define IPMI_LANPLUS_CRYPT_H
+
+#include <ipmitool/ipmi_intf.h>
+
+/*
+ * See the implementation file for documentation
+ * ipmi_intf can be used for oem specific implementations
+ * e.g. if (ipmi_oem_active(intf, "OEM_XYZ"))
+ */
+
+int lanplus_rakp2_hmac_matches(const struct ipmi_session * session,
+ const uint8_t * hmac,
+ struct ipmi_intf * intf);
+int lanplus_rakp4_hmac_matches(const struct ipmi_session * session,
+ const uint8_t * hmac,
+ struct ipmi_intf * intf);
+int lanplus_generate_rakp3_authcode(uint8_t * buffer,
+ const struct ipmi_session * session,
+ uint32_t * auth_length,
+ struct ipmi_intf * intf);
+int lanplus_generate_sik(struct ipmi_session * session, struct ipmi_intf * intf);
+int lanplus_generate_k1(struct ipmi_session * session);
+int lanplus_generate_k2(struct ipmi_session * session);
+int lanplus_encrypt_payload(uint8_t crypt_alg,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint16_t * bytesWritten);
+int lanplus_decrypt_payload(uint8_t crypt_alg,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint16_t * payload_size);
+int lanplus_has_valid_auth_code(struct ipmi_rs * rs,
+ struct ipmi_session * session);
+
+
+
+
+#endif /* IPMI_LANPLUS_CRYPT_H */
diff --git a/src/plugins/lanplus/lanplus_crypt_impl.c b/src/plugins/lanplus/lanplus_crypt_impl.c
new file mode 100644
index 0000000..cde6c54
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_crypt_impl.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "ipmitool/log.h"
+#include "ipmitool/ipmi_constants.h"
+#include "lanplus.h"
+#include "lanplus_crypt_impl.h"
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <assert.h>
+
+
+
+/*
+ * lanplus_seed_prng
+ *
+ * Seed our PRNG with the specified number of bytes from /dev/random
+ *
+ * param bytes specifies the number of bytes to read from /dev/random
+ *
+ * returns 0 on success
+ * 1 on failure
+ */
+int lanplus_seed_prng(uint32_t bytes)
+{
+ if (! RAND_load_file("/dev/urandom", bytes))
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ * lanplus_rand
+ *
+ * Generate a random number of the specified size
+ *
+ * param num_bytes [in] is the size of the random number to be
+ * generated
+ * param buffer [out] is where we will place our random number
+ *
+ * return 0 on success
+ * 1 on failure
+ */
+int
+lanplus_rand(uint8_t * buffer, uint32_t num_bytes)
+{
+#undef IPMI_LANPLUS_FAKE_RAND
+#ifdef IPMI_LANPLUS_FAKE_RAND
+
+ /*
+ * This code exists so that we can easily find the generated random number
+ * in the hex dumps.
+ */
+ int i;
+ for (i = 0; i < num_bytes; ++i)
+ buffer[i] = 0x70 | i;
+
+ return 0;
+#else
+ return (! RAND_bytes(buffer, num_bytes));
+#endif
+}
+
+
+
+/*
+ * lanplus_HMAC
+ *
+ * param mac specifies the algorithm to be used, currently only SHA1 is supported
+ * param key is the key used for HMAC generation
+ * param key_len is the lenght of key
+ * param d is the data to be MAC'd
+ * param n is the length of the data at d
+ * param md is the result of the HMAC algorithm
+ * param md_len is the length of md
+ *
+ * returns a pointer to md
+ */
+uint8_t *
+lanplus_HMAC(uint8_t mac,
+ const void *key,
+ int key_len,
+ const uint8_t *d,
+ int n,
+ uint8_t *md,
+ uint32_t *md_len)
+{
+ const EVP_MD *evp_md = NULL;
+
+ if ((mac == IPMI_AUTH_RAKP_HMAC_SHA1) ||
+ (mac == IPMI_INTEGRITY_HMAC_SHA1_96))
+ evp_md = EVP_sha1();
+ else
+ {
+ lprintf(LOG_DEBUG, "Invalid mac type 0x%x in lanplus_HMAC\n", mac);
+ assert(0);
+ }
+
+ return HMAC(evp_md, key, key_len, d, n, md, (unsigned int *)md_len);
+}
+
+
+/*
+ * lanplus_encrypt_aes_cbc_128
+ *
+ * Encrypt with the AES CBC 128 algorithm
+ *
+ * param iv is the 16 byte initialization vector
+ * param key is the 16 byte key used by the AES algorithm
+ * param input is the data to be encrypted
+ * param input_length is the number of bytes to be encrypted. This MUST
+ * be a multiple of the block size, 16.
+ * param output is the encrypted output
+ * param bytes_written is the number of bytes written. This param is set
+ * to 0 on failure, or if 0 bytes were input.
+ */
+void
+lanplus_encrypt_aes_cbc_128(const uint8_t * iv,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint32_t * bytes_written)
+{
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
+ EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+
+ *bytes_written = 0;
+
+ if (input_length == 0)
+ return;
+
+ if (verbose >= 5)
+ {
+ printbuf(iv, 16, "encrypting with this IV");
+ printbuf(key, 16, "encrypting with this key");
+ printbuf(input, input_length, "encrypting this data");
+ }
+
+
+ /*
+ * The default implementation adds a whole block of padding if the input
+ * data is perfectly aligned. We would like to keep that from happening.
+ * We have made a point to have our input perfectly padded.
+ */
+ assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0);
+
+
+ if(!EVP_EncryptUpdate(&ctx, output, (int *)bytes_written, input, input_length))
+ {
+ /* Error */
+ *bytes_written = 0;
+ return;
+ }
+ else
+ {
+ uint32_t tmplen;
+
+ if(!EVP_EncryptFinal_ex(&ctx, output + *bytes_written, (int *)&tmplen))
+ {
+ *bytes_written = 0;
+ return; /* Error */
+ }
+ else
+ {
+ /* Success */
+ *bytes_written += tmplen;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+ }
+}
+
+
+
+/*
+ * lanplus_decrypt_aes_cbc_128
+ *
+ * Decrypt with the AES CBC 128 algorithm
+ *
+ * param iv is the 16 byte initialization vector
+ * param key is the 16 byte key used by the AES algorithm
+ * param input is the data to be decrypted
+ * param input_length is the number of bytes to be decrypted. This MUST
+ * be a multiple of the block size, 16.
+ * param output is the decrypted output
+ * param bytes_written is the number of bytes written. This param is set
+ * to 0 on failure, or if 0 bytes were input.
+ */
+void
+lanplus_decrypt_aes_cbc_128(const uint8_t * iv,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint32_t * bytes_written)
+{
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
+ EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+
+ if (verbose >= 5)
+ {
+ printbuf(iv, 16, "decrypting with this IV");
+ printbuf(key, 16, "decrypting with this key");
+ printbuf(input, input_length, "decrypting this data");
+ }
+
+
+ *bytes_written = 0;
+
+ if (input_length == 0)
+ return;
+
+ /*
+ * The default implementation adds a whole block of padding if the input
+ * data is perfectly aligned. We would like to keep that from happening.
+ * We have made a point to have our input perfectly padded.
+ */
+ assert((input_length % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE) == 0);
+
+
+ if (!EVP_DecryptUpdate(&ctx, output, (int *)bytes_written, input, input_length))
+ {
+ /* Error */
+ lprintf(LOG_DEBUG, "ERROR: decrypt update failed");
+ *bytes_written = 0;
+ return;
+ }
+ else
+ {
+ uint32_t tmplen;
+
+ if (!EVP_DecryptFinal_ex(&ctx, output + *bytes_written, (int *)&tmplen))
+ {
+ char buffer[1000];
+ ERR_error_string(ERR_get_error(), buffer);
+ lprintf(LOG_DEBUG, "the ERR error %s", buffer);
+ lprintf(LOG_DEBUG, "ERROR: decrypt final failed");
+ *bytes_written = 0;
+ return; /* Error */
+ }
+ else
+ {
+ /* Success */
+ *bytes_written += tmplen;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+ }
+
+ if (verbose >= 5)
+ {
+ lprintf(LOG_DEBUG, "Decrypted %d encrypted bytes", input_length);
+ printbuf(output, *bytes_written, "Decrypted this data");
+ }
+}
diff --git a/src/plugins/lanplus/lanplus_crypt_impl.h b/src/plugins/lanplus/lanplus_crypt_impl.h
new file mode 100644
index 0000000..ff534bc
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_crypt_impl.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef IPMI_LANPLUS_CRYPT_IMPL_H
+#define IPMI_LANPLUS_CRYPT_IMPL_H
+
+
+int
+lanplus_seed_prng(uint32_t bytes);
+
+int
+lanplus_rand(uint8_t * buffer, uint32_t num_bytes);
+
+uint8_t *
+lanplus_HMAC(uint8_t mac, const void *key, int key_len,
+ const uint8_t *d, int n, uint8_t *md,
+ uint32_t *md_len);
+
+void
+lanplus_encrypt_aes_cbc_128(const uint8_t * iv,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint32_t * bytes_written);
+
+
+void
+lanplus_decrypt_aes_cbc_128(const uint8_t * iv,
+ const uint8_t * key,
+ const uint8_t * input,
+ uint32_t input_length,
+ uint8_t * output,
+ uint32_t * bytes_written);
+
+
+#endif /* IPMI_LANPLUS_CRYPT_IMPL_H */
diff --git a/src/plugins/lanplus/lanplus_dump.c b/src/plugins/lanplus/lanplus_dump.c
new file mode 100644
index 0000000..8d52fab
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_dump.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "lanplus.h"
+#include "lanplus_dump.h"
+
+extern const struct valstr ipmi_rakp_return_codes[];
+extern const struct valstr ipmi_priv_levels[];
+extern const struct valstr ipmi_auth_algorithms[];
+extern const struct valstr ipmi_integrity_algorithms[];
+extern const struct valstr ipmi_encryption_algorithms[];
+
+#define DUMP_PREFIX_INCOMING "<<"
+
+void lanplus_dump_open_session_response(const struct ipmi_rs * rsp)
+{
+ if (verbose < 2)
+ return;
+
+ printf("%sOPEN SESSION RESPONSE\n", DUMP_PREFIX_INCOMING);
+
+ printf("%s Message tag : 0x%02x\n",
+ DUMP_PREFIX_INCOMING,
+ rsp->payload.open_session_response.message_tag);
+ printf("%s RMCP+ status : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.open_session_response.rakp_return_code,
+ ipmi_rakp_return_codes));
+ printf("%s Maximum privilege level : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.open_session_response.max_priv_level,
+ ipmi_priv_levels));
+ printf("%s Console Session ID : 0x%08lx\n",
+ DUMP_PREFIX_INCOMING,
+ (long)rsp->payload.open_session_response.console_id);
+
+ /* only tag, status, privlvl, and console id are returned if error */
+ if (rsp->payload.open_session_response.rakp_return_code !=
+ IPMI_RAKP_STATUS_NO_ERRORS)
+ return;
+
+ printf("%s BMC Session ID : 0x%08lx\n",
+ DUMP_PREFIX_INCOMING,
+ (long)rsp->payload.open_session_response.bmc_id);
+ printf("%s Negotiated authenticatin algorithm : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.open_session_response.auth_alg,
+ ipmi_auth_algorithms));
+ printf("%s Negotiated integrity algorithm : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.open_session_response.integrity_alg,
+ ipmi_integrity_algorithms));
+ printf("%s Negotiated encryption algorithm : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.open_session_response.crypt_alg,
+ ipmi_encryption_algorithms));
+ printf("\n");
+}
+
+
+
+void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, uint8_t auth_alg)
+{
+ int i;
+
+ if (verbose < 2)
+ return;
+
+ printf("%sRAKP 2 MESSAGE\n", DUMP_PREFIX_INCOMING);
+
+ printf("%s Message tag : 0x%02x\n",
+ DUMP_PREFIX_INCOMING,
+ rsp->payload.rakp2_message.message_tag);
+
+ printf("%s RMCP+ status : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.rakp2_message.rakp_return_code,
+ ipmi_rakp_return_codes));
+
+ printf("%s Console Session ID : 0x%08lx\n",
+ DUMP_PREFIX_INCOMING,
+ (long)rsp->payload.rakp2_message.console_id);
+
+ printf("%s BMC random number : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 16; ++i)
+ printf("%02x", rsp->payload.rakp2_message.bmc_rand[i]);
+ printf("\n");
+
+ printf("%s BMC GUID : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 16; ++i)
+ printf("%02x", rsp->payload.rakp2_message.bmc_guid[i]);
+ printf("\n");
+
+ switch(auth_alg)
+ {
+ case IPMI_AUTH_RAKP_NONE:
+ printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING);
+ break;
+ case IPMI_AUTH_RAKP_HMAC_SHA1:
+ printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 20; ++i)
+ printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]);
+ printf("\n");
+ break;
+ case IPMI_AUTH_RAKP_HMAC_MD5:
+ printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 16; ++i)
+ printf("%02x", rsp->payload.rakp2_message.key_exchange_auth_code[i]);
+ printf("\n");
+ break;
+ default:
+ printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING);
+ }
+ printf("\n");
+}
+
+
+
+void lanplus_dump_rakp4_message(const struct ipmi_rs * rsp, uint8_t auth_alg)
+{
+ int i;
+
+ if (verbose < 2)
+ return;
+
+ printf("%sRAKP 4 MESSAGE\n", DUMP_PREFIX_INCOMING);
+
+ printf("%s Message tag : 0x%02x\n",
+ DUMP_PREFIX_INCOMING,
+ rsp->payload.rakp4_message.message_tag);
+
+ printf("%s RMCP+ status : %s\n",
+ DUMP_PREFIX_INCOMING,
+ val2str(rsp->payload.rakp4_message.rakp_return_code,
+ ipmi_rakp_return_codes));
+
+ printf("%s Console Session ID : 0x%08lx\n",
+ DUMP_PREFIX_INCOMING,
+ (long)rsp->payload.rakp4_message.console_id);
+
+ switch(auth_alg)
+ {
+ case IPMI_AUTH_RAKP_NONE:
+ printf("%s Key exchange auth code : none\n", DUMP_PREFIX_INCOMING);
+ break;
+ case IPMI_AUTH_RAKP_HMAC_SHA1:
+ printf("%s Key exchange auth code [sha1] : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 12; ++i)
+ printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]);
+ printf("\n");
+ break;
+ case IPMI_AUTH_RAKP_HMAC_MD5:
+ printf("%s Key exchange auth code [md5] : 0x", DUMP_PREFIX_INCOMING);
+ for (i = 0; i < 12; ++i)
+ printf("%02x", rsp->payload.rakp4_message.integrity_check_value[i]);
+ printf("\n");
+ break;
+ default:
+ printf("%s Key exchange auth code : invalid", DUMP_PREFIX_INCOMING);
+ }
+ printf("\n");
+}
+
diff --git a/src/plugins/lanplus/lanplus_dump.h b/src/plugins/lanplus/lanplus_dump.h
new file mode 100644
index 0000000..4e29ebb
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_dump.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+
+#ifndef IPMI_LANPLUS_DUMP_H
+#define IPMI_LANPLUS_DUMP_H
+
+#include <ipmitool/ipmi_intf.h>
+
+/* See the implementation file for documentation */
+void lanplus_dump_open_session_response(const struct ipmi_rs * rsp);
+void lanplus_dump_rakp2_message(const struct ipmi_rs * rsp, uint8_t auth_alg);
+void lanplus_dump_rakp4_message(const struct ipmi_rs * rsp, uint8_t auth_alg);
+
+
+#endif /* IPMI_LANPLUS_DUMP_H */
diff --git a/src/plugins/lanplus/lanplus_strings.c b/src/plugins/lanplus/lanplus_strings.c
new file mode 100644
index 0000000..074f898
--- /dev/null
+++ b/src/plugins/lanplus/lanplus_strings.c
@@ -0,0 +1,39 @@
+#include "lanplus.h"
+#include "ipmitool/ipmi_constants.h"
+
+const struct valstr ipmi_rakp_return_codes[] = {
+
+ { IPMI_RAKP_STATUS_NO_ERRORS, "no errors" },
+ { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_SESSION, "insufficient resources for session" },
+ { IPMI_RAKP_STATUS_INVALID_SESSION_ID, "invalid session ID" },
+ { IPMI_RAKP_STATUS_INVALID_PAYLOAD_TYPE, "invalid payload type" },
+ { IPMI_RAKP_STATUS_INVALID_AUTHENTICATION_ALGORITHM, "invalid authentication algorithm" },
+ { IPMI_RAKP_STATUS_INVALID_INTEGRITTY_ALGORITHM, "invalid integrity algorithm" },
+ { IPMI_RAKP_STATUS_NO_MATCHING_AUTHENTICATION_PAYLOAD, "no matching authentication algorithm"},
+ { IPMI_RAKP_STATUS_NO_MATCHING_INTEGRITY_PAYLOAD, "no matching integrity payload" },
+ { IPMI_RAKP_STATUS_INACTIVE_SESSION_ID, "inactive session ID" },
+ { IPMI_RAKP_STATUS_INVALID_ROLE, "invalid role" },
+ { IPMI_RAKP_STATUS_UNAUTHORIZED_ROLE_REQUESTED, "unauthorized role requested" },
+ { IPMI_RAKP_STATUS_INSUFFICIENT_RESOURCES_FOR_ROLE, "insufficient resources for role" },
+ { IPMI_RAKP_STATUS_INVALID_NAME_LENGTH, "invalid name length" },
+ { IPMI_RAKP_STATUS_UNAUTHORIZED_NAME, "unauthorized name" },
+ { IPMI_RAKP_STATUS_UNAUTHORIZED_GUID, "unauthorized GUID" },
+ { IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE, "invalid integrity check value" },
+ { IPMI_RAKP_STATUS_INVALID_CONFIDENTIALITY_ALGORITHM, "invalid confidentiality algorithm" },
+ { IPMI_RAKP_STATUS_NO_CIPHER_SUITE_MATCH, "no matching cipher suite" },
+ { IPMI_RAKP_STATUS_ILLEGAL_PARAMTER, "illegal parameter" },
+ { 0, 0 },
+};
+
+
+const struct valstr ipmi_priv_levels[] = {
+ { IPMI_PRIV_CALLBACK, "callback" },
+ { IPMI_PRIV_USER, "user" },
+ { IPMI_PRIV_OPERATOR, "operator" },
+ { IPMI_PRIV_ADMIN, "admin" },
+ { IPMI_PRIV_OEM, "oem" },
+ { 0, 0 },
+};
+
+
+
diff --git a/src/plugins/lanplus/rmcp.h b/src/plugins/lanplus/rmcp.h
new file mode 100644
index 0000000..51dc44d
--- /dev/null
+++ b/src/plugins/lanplus/rmcp.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights 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 Sun Microsystems, Inc. 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 "AS IS," without a warranty of any kind.
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef IPMI_RMCP_H
+#define IPMI_RMCP_H
+
+#include <ipmitool/helper.h>
+#include "lanplus.h"
+
+#define RMCP_VERSION_1 0x06
+
+#define RMCP_UDP_PORT 0x26f /* port 623 */
+#define RMCP_UDP_SECURE_PORT 0x298 /* port 664 */
+
+#define RMCP_TYPE_MASK 0x80
+#define RMCP_TYPE_NORM 0x00
+#define RMCP_TYPE_ACK 0x01
+
+static const struct valstr rmcp_type_vals[] __attribute__((unused)) = {
+ { RMCP_TYPE_NORM, "Normal RMCP" },
+ { RMCP_TYPE_ACK, "RMCP ACK" },
+ { 0, NULL }
+};
+
+#define RMCP_CLASS_MASK 0x1f
+#define RMCP_CLASS_ASF 0x06
+#define RMCP_CLASS_IPMI 0x07
+#define RMCP_CLASS_OEM 0x08
+
+static const struct valstr rmcp_class_vals[] __attribute__((unused)) = {
+ { RMCP_CLASS_ASF, "ASF" },
+ { RMCP_CLASS_IPMI, "IPMI" },
+ { RMCP_CLASS_OEM, "OEM" },
+ { 0, NULL }
+};
+
+/* RMCP message header */
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(1)
+#endif
+struct rmcp_hdr {
+ uint8_t ver;
+ uint8_t __reserved;
+ uint8_t seq;
+ uint8_t class;
+} ATTRIBUTE_PACKING;
+#ifdef HAVE_PRAGMA_PACK
+#pragma pack(0)
+#endif
+
+int handle_rmcp(struct ipmi_intf * intf, uint8_t * data, int data_len);
+
+#endif /* IPMI_RMCP_H */