#!/bin/sh ############################################################################# # # bmc-snmp-proxy: Set SNMP proxy to BMC (Baseboard Management Controller) # # version: 0.62 # # Authors: Charles Rose # Jordan Hargrave # # Description: Script to set snmp proxy to the BMC for certain OID # See here for details: # https://fedoraproject.org/wiki/Features/AgentFreeManagement # # Assumptions: This script will work only when /etc/snmp/ is writable. # ############################################################################# # GLOBALS ############################################################################# SYSCONF_DIR="/etc/sysconfig" CONFIG="${SYSCONF_DIR}/bmc-snmp-proxy" SNMPD_BMC_CONF_DIR="/etc/snmp/bmc" SNMPD_BMC_CONF="${SNMPD_BMC_CONF_DIR}/snmpd.local.conf" TRAPD_BMC_CONF="${SNMPD_BMC_CONF_DIR}/snmptrapd.local.conf" TRAPD_CONF="/etc/snmp/snmptrapd.conf" LOCKFILE="/var/lock/subsys/bmc-snmp-proxy" BMC_INFO="/run/bmc-info" IPMITOOL=$(which ipmitool) #Default config BMC_COMMUNITY="public" BMC_OID=".1.3.6.1.4.1.674.10892.2" # Dell iDRAC TRAP_FORWARD="no" RELOAD_SERVICES="yes" ############################################################################# #TODO: Use inotify and daemonize when $BMC_INFO changes # source config [ -r ${CONFIG} ] && . ${CONFIG} . gettext.sh SCRIPT_NAME=$(basename $0) RETVAL=0 # Check if bmc-info created by exchange-bmc-os-info bmc_info_exists() { if [ -r "${BMC_INFO}" ]; then . ${BMC_INFO} else RETVAL=2 fi return $RETVAL } check_snmp() { if [ ! -d /etc/snmp ] || [ ! -x /usr/sbin/snmpd ]; then RETVAL=12 fi return $RETVAL } ############################################################################# # configure SNMP proxy ############################################################################# write_snmp_conf() { # SNMPv3 security: bmcview, bmc_ctx, bmc_sec, bmc_grp, bmc_cmty printf "###############################################\n" printf "# Automatically created by %s #\n" "${SCRIPT_NAME}" printf "###############################################\n" printf "#view bmcview included %s 80\n" "${BMC_OID}" printf "#com2sec -Cn bmc_ctx bmc_sec default bmc_cmty\n" printf "#group bmc_grp v1 bmc_sec\n" printf "#access bmc_grp bmc_ctx any noauth exact bmcview none none\n" printf "#proxy -Cn bmc_ctx -v 1 %s\n" "${PROXY_TOKEN}" printf "proxy -v 1 %s\n" "${PROXY_TOKEN}" printf "###############################################\n" } valid_ip() { #Thanks to mkyong.com octet="([01]?[[:digit:]][[:digit:]]?|2[0-4][[:digit:]]|25[0-5])" printf -- "%s" "${1}"| grep -Eq \ "^${octet}\\.${octet}\\.${octet}\\.${octet}$" return $? } check_vars() { [ -z ${BMC_COMMUNITY} ] && BMC_COMMUNITY="public" [ -z ${BMC_OID} ] && return 1 if [ -n "${BMC_IPv4}" ] && valid_ip ${BMC_IPv4}; then return 0 else return 1 fi } set_snmp_proxy() { if check_vars; then PROXY_TOKEN="-c ${BMC_COMMUNITY} ${BMC_IPv4} ${BMC_OID}" if [ -d ${SNMPD_BMC_CONF_DIR} ]; then write_snmp_conf > ${SNMPD_BMC_CONF} || RETVAL=4 fi else RETVAL=3 fi } set_snmpd_conf_path() { if [ ! -d ${SNMPD_BMC_CONF_DIR} ]; then mkdir ${SNMPD_BMC_CONF_DIR} || RETVAL=7 fi # We need SNMPCONFPATH set for both snmpd and snmptrapd for sysconf in ${SYSCONF_DIR}/snmp*d; do if ! grep -q "^SNMPCONFPATH.*${SNMPD_BMC_CONF_DIR}" \ "${sysconf}" > /dev/null 2>&1; then printf "SNMPCONFPATH=/etc/snmp:%s\n" \ "${SNMPD_BMC_CONF_DIR}" >> ${sysconf} || \ RETVAL=7 fi done return $RETVAL } disable_snmp_proxy() { if [ -f ${SNMPD_BMC_CONF} ]; then rm -f ${SNMPD_BMC_CONF} || RETVAL=5 fi } ############################################################################# # Trap Forwarding ############################################################################# pick_alert_dest() { test_ip="$1" # We have 4 IPv4 and 4 IPv6 alert dest. We will set IPv4 for now. for ALERT_DEST in $(seq 1 4) do temp_ip=$(${IPMITOOL} lan alert print ${CHANNEL} ${ALERT_DEST}\ 2>/dev/null| sed -n "s#^Alert IP Address.*: ##p") [ "${temp_ip}" = "${test_ip}" ] && return 0 done return 1 } set_alert_dest_ip() { ${IPMITOOL} lan alert set ${CHANNEL} ${ALERT_DEST} ipaddr ${1} \ retry 4 type pet >/dev/null 2>&1 || RETVAL=8 } config_bmc_alert_dest() { # call with enable|disable # Pick the first active LAN channel for CHANNEL in $(seq 1 14) do [ $(${IPMITOOL} -I open channel info ${CHANNEL} 2>/dev/null \ | grep -q "802\.3") ] || break done # If TRAPD_IP is already set as an alert dest, if pick_alert_dest "${TRAPD_IP}"; then # disable: reset it if we are called with disable [ "${1}" = "disable" ] && \ set_alert_dest_ip "0.0.0.0" # else, find the next free alert dest, elif pick_alert_dest "0.0.0.0"; then [ "${1}" = "disable" ] && \ return $RETVAL # set: the TRAPD_IP set_alert_dest_ip "${TRAPD_IP}" else # No free alert destinations RETVAL=9 fi return $RETVAL } set_ipmi_pef() { # Needs ipmitool-1.8.13 + patches ${IPMITOOL} pef policy set ${ALERT_DEST} "${1}" >/dev/null 2>&1 || \ RETVAL=10 } get_host_ip() { # Get host's IP that the BMC can reach. This is at best a hack. IFACE=$(/usr/sbin/ip -o -f inet address |awk '!/: lo/ {print $2}') for dev in ${IFACE} do temp_ping=$(ping -c 1 -I ${dev} ${BMC_IPv4}) [ $? -ne 0 ] && continue printf -- "%s" "$temp_ping"| awk 'NR==1{print $5}' && break done } config_bmc_alert() { # Do two things # Set/Reset TRAP IP in BMC # Enable/Disable PEF alerting in BMC for TRAP # Get Host's IP that the BMC can send traps to TRAPD_IP=$(get_host_ip) # Set Host's IP as the alert destination in the BMC valid_ip ${TRAPD_IP} && config_bmc_alert_dest "${ACTION}" # Enable/Disable alerting on the LAN channel [ $RETVAL -eq 0 ] && set_ipmi_pef "${ACTION}" return $RETVAL } write_trapd_conf() { printf "###############################################\n" printf "# Automatically created by %s #\n" "${SCRIPT_NAME}" printf "forward default %s\n" "${FORWARD_HOST}" printf "###############################################\n" } config_trapd() { # Proceed only if snmptrapd is available on the system if [ -f ${TRAPD_CONF} ]; then write_trapd_conf > ${TRAPD_BMC_CONF} || RETVAL=11 else RETVAL=11 fi } trap_sink_exists() { # TODO: We only set the first match. We should be able to set # multiple FORWARD_HOST=$(awk '/^trap.*sink/{print $2}; /^informsink/{print $2}' \ /etc/snmp/snmpd*conf | head -1) if [ -z "${FORWARD_HOST}" ]; then # there is no trapsink setup. return 1 else return 0 fi } # Forward SNMP traps from the BMC to trapsink. trap_forward() { NO_TRAP=0 ACTION=${1} # enable or disable if [ "${ACTION}" = "enable" ]; then # Get trapd config, if trap_sink_exists; then config_bmc_alert && config_trapd else # exit silently if there is no sink NO_TRAP=1 fi else if [ -f ${TRAPD_BMC_CONF} ]; then rm -f ${TRAPD_BMC_CONF} >/dev/null 2>&1 config_bmc_alert else NO_TRAP=1 fi fi } ############################################################################# service_reload() { #TODO: do this in systemd if [ ${RETVAL} -eq 0 ] && [ "${RELOAD_SERVICES}" = "yes" ]; then service $1 reload [ $? -ne 0 ] && RETVAL=6 fi } ############################################################################# start() { if bmc_info_exists && check_snmp; then touch ${LOCKFILE} set_snmpd_conf_path && set_snmp_proxy [ $RETVAL -eq 0 ] && service_reload snmpd if [ "${TRAP_FORWARD}" = "yes" ]; then trap_forward "enable" [ $RETVAL -eq 0 ] && [ $NO_TRAP -eq 0 ] && \ service_reload snmptrapd fi fi } ############################################################################# stop() { [ ! -f ${LOCKFILE} ] && return if bmc_info_exists && check_snmp; then disable_snmp_proxy [ $RETVAL -eq 0 ] && service_reload snmpd if [ "${TRAP_FORWARD}" = "yes" ]; then trap_forward "disable" [ $RETVAL -eq 0 ] && [ $NO_TRAP -eq 0 ] && \ service_reload snmptrapd fi rm -f ${LOCKFILE} fi } ############################################################################# status() { eval_gettext "${SCRIPT_NAME}: snmp proxy to BMC is " # Checking for lockfile is better. #if grep -q "^proxy" "${SNMPD_BMC_CONF}" > /dev/null 2>&1 ; then if [ -f ${LOCKFILE} ]; then eval_gettext "set" else eval_gettext "not set" fi echo RETVAL=0 } ############################################################################# usage() { eval_gettext "Usage: $0 {start|stop|status}"; echo 1>&2 RETVAL=1 } ############################################################################# # MAIN ############################################################################# case "$1" in start) start ;; stop) stop ;; status) status ;; *) usage ;; esac case "$RETVAL" in 0|1) ;; 2) eval_gettext "${SCRIPT_NAME}: failed to read ${BMC_INFO} " 1>&2 ;; 3) eval_gettext "${SCRIPT_NAME}: failed to get proxy config." 1>&2 ;; 4) eval_gettext "${SCRIPT_NAME}: failed to set ${SNMPD_BMC_CONF}." 1>&2 ;; 5) eval_gettext "${SCRIPT_NAME}: failed to disable snmp proxy." 1>&2 ;; 6) eval_gettext "${SCRIPT_NAME}: failed to reload snmpd." 1>&2 ;; 7) eval_gettext "${SCRIPT_NAME}: failed to set snmpd config." 1>&2 ;; 8) eval_gettext "${SCRIPT_NAME}: failed to set IPMI alert dest." 1>&2 ;; 9) eval_gettext "${SCRIPT_NAME}: no free IPMI alert dest." 1>&2 ;; 10) eval_gettext "${SCRIPT_NAME}: failed to set IPMI PEF." 1>&2 ;; 11) eval_gettext "${SCRIPT_NAME}: failed to write snmptrapd.conf." 1>&2 ;; 12) eval_gettext "${SCRIPT_NAME}: snmpd not found." 1>&2 ;; *) eval_gettext "${SCRIPT_NAME}: unknown error." 1>&2 ;; esac if [ ${RETVAL} -gt 1 ]; then eval_gettext " Return code: ${RETVAL}"; echo fi exit ${RETVAL} ############################################################################# # end of file #############################################################################