xref: /openbmc/ipmitool/contrib/bmc-snmp-proxy (revision c18ec02f)
1#!/bin/sh
2#############################################################################
3#
4# bmc-snmp-proxy:	Set SNMP proxy to BMC (Baseboard Management Controller)
5#
6# version:	0.6
7#
8# Authors:	Charles Rose <charles_rose@dell.com>
9#		Jordan Hargrave <jordan_hargrave@dell.com>
10#
11# Description:  Script to set snmp proxy to the BMC for certain OID
12#		See here for details:
13#		https://fedoraproject.org/wiki/Features/AgentFreeManagement
14#
15# Assumptions:  This script will work only when /etc/snmp/ is writable.
16#
17#############################################################################
18# GLOBALS
19#############################################################################
20SYSCONF_DIR="/etc/sysconfig"
21CONFIG="${SYSCONF_DIR}/bmc-snmp-proxy"
22
23SNMPD_LOCAL_CONF_DIR="/etc/snmp/bmc"
24SNMPD_LOCAL_CONF="${SNMPD_LOCAL_CONF_DIR}/snmpd.local.conf"
25TRAPD_LOCAL_CONF="${SNMPD_LOCAL_CONF_DIR}/snmptrapd.local.conf"
26
27TRAPD_CONF="/etc/snmp/snmptrapd.conf"
28
29LOCKFILE="/var/lock/subsys/bmc-snmp-proxy"
30BMC_INFO="/var/run/bmc-info"
31
32IPMITOOL=`which ipmitool`
33
34#Default config
35BMC_COMMUNITY="public"
36BMC_OID=".1.3.6.1.4.1.674.10892.2"  # Dell iDRAC
37TRAP_FORWARD="no"
38RELOAD_SERVICES="yes"
39
40#############################################################################
41
42#TODO: Use inotify and daemonize when $BMC_INFO changes
43
44# source config
45[ -r ${CONFIG} ] && . ${CONFIG}
46
47. gettext.sh
48
49SCRIPT_NAME=$(basename $0)
50RETVAL=0
51
52# Check if bmc-info created by exchange-bmc-os-info
53bmc_info_exists()
54{
55	if [ -r "${BMC_INFO}" ]; then
56		. ${BMC_INFO}
57	else
58		RETVAL=2
59	fi
60	return $RETVAL
61}
62
63check_snmp()
64{
65	if [ ! -d /etc/snmp ] && [ ! -x /usr/sbin/snmpd ]; then
66		RETVAL=12
67	fi
68	return $RETVAL
69}
70
71#############################################################################
72# configure SNMP proxy
73#############################################################################
74write_snmp_conf()
75{
76	# SNMPv3 security: bmcview, bmc_ctx, bmc_sec, bmc_grp, bmc_cmty
77	printf "###############################################\n"
78	printf "# Automatically created by %s #\n" "${SCRIPT_NAME}"
79	printf "###############################################\n"
80	printf "view bmcview included %s 80\n" "${BMC_OID}"
81	printf "com2sec -Cn bmc_ctx bmc_sec default bmc_cmty\n"
82	printf "group bmc_grp v1 bmc_sec\n"
83	printf "access bmc_grp bmc_ctx any noauth exact bmcview none none\n"
84	printf "proxy -Cn bmc_ctx -v 1 %s\n" "${PROXY_TOKEN}"
85	printf "###############################################\n"
86}
87
88valid_ip()
89{
90        #Thanks to mkyong.com
91        octet="([01]?[[:digit:]][[:digit:]]?|2[0-4][[:digit:]]|25[0-5])"
92
93        printf -- "%s" "${1}"| grep -Eq \
94		"^${octet}\\.${octet}\\.${octet}\\.${octet}$"
95        return $?
96}
97
98check_vars()
99{
100	[ -z ${BMC_COMMUNITY} ] && BMC_COMMUNITY="public"
101	[ -z ${BMC_OID} ] && return 1
102
103	if [ -n "${BMC_IPv4}" ] && valid_ip ${BMC_IPv4}; then
104		return 0
105	else
106		return 1
107	fi
108}
109
110set_snmp_proxy()
111{
112	if check_vars; then
113		PROXY_TOKEN="-c ${BMC_COMMUNITY} ${BMC_IPv4} ${BMC_OID}"
114
115		if [ ! -d ${SNMPD_LOCAL_CONF_DIR} ] && \
116			 	mkdir ${SNMPD_LOCAL_CONF_DIR}; then
117			write_snmp_conf > ${SNMPD_LOCAL_CONF}
118			[ $? -ne 0 ] && RETVAL=4
119		fi
120	else
121		RETVAL=3
122	fi
123}
124
125
126set_snmpd_conf_path()
127{
128	for SYSCONF in ${SYSCONF_DIR}/snmp*d;
129	do
130		if grep -q "${SNMPD_LOCAL_CONF_DIR}" "${SYSCONF}" > \
131				 /dev/null 2>&1; then
132			continue
133		else
134			printf "SNMPCONFPATH=%s\n" "${SNMPD_LOCAL_CONF_DIR}" \
135				>> ${SYSCONF} || RETVAL=7
136		fi
137	done
138	return $RETVAL
139}
140
141disable_snmp_proxy()
142{
143	if [ -f ${SNMPD_LOCAL_CONF} ]; then
144		rm -f ${SNMPD_LOCAL_CONF}
145		[ $? -ne 0 ] && RETVAL=5
146	fi
147}
148#############################################################################
149# Trap Forwarding
150#############################################################################
151
152pick_alert_dest()
153{
154	test_ip="$1"
155	for ALERT_DEST in `seq 1 4`
156	do
157		temp_ip=$(${IPMITOOL} lan alert print ${CHANNEL} ${ALERT_DEST}\
158			2>/dev/null| sed -n "s#^Alert IP Address.*: ##p")
159
160		[ "${temp_ip}" = "${test_ip}" ] && return 0
161	done
162	return 1
163}
164
165set_alert_dest_ip()
166{
167	${IPMITOOL} lan alert set ${CHANNEL} ${ALERT_DEST} ipaddr ${1} \
168		retry 4 type pet >/dev/null 2>&1
169	[ $? -ne 0 ] && RETVAL=8
170}
171
172bmc_alert_dest()
173{
174	# Pick the first active LAN channel
175        for CHANNEL in `seq 1 14`
176        do
177                [ $(${IPMITOOL} -I open channel info ${CHANNEL} 2>/dev/null \
178                        | grep -q "802\.3") ] || break
179        done
180
181	# If TRAPD_IP is already set as an alert dest,
182	if pick_alert_dest "${TRAPD_IP}"; then
183		# reset: reset it if we are called with reset
184		[ "${1}" = "reset" ] && \
185			set_alert_dest_ip "0.0.0.0"
186	# else, find the next free alert dest,
187	elif pick_alert_dest "0.0.0.0"; then
188		[ "${1}" = "reset" ] && \
189			return $RETVAL
190		# set: the TRAPD_IP
191		set_alert_dest_ip "${TRAPD_IP}"
192	else
193		# No free alert destinations
194		RETVAL=9
195	fi
196	return $RETVAL
197}
198
199set_ipmi_alert()
200{
201	${IPMITOOL} lan set ${CHANNEL} alert "${1}" >/dev/null 2>&1
202	[ $? -ne 0 ] && RETVAL=10
203}
204
205get_host_ip()
206{
207	# Get host's IP that the BMC can reach.
208	IFACE=$(/usr/sbin/ip -o -f inet address |awk '!/: lo/ {print $2}')
209	for dev in ${IFACE}
210	do
211		ping -c 1 -I ${dev} ${BMC_IPv4} > /dev/null 2>&1
212	done
213}
214
215config_bmc_alert()
216{
217	# Get Host's IP that the BMC can send traps to
218	TRAPD_IP=$(get_host_ip)
219
220	# Set Host's IP as the alert destination in the BMC
221	valid_ip ${TRAPD_IP} && bmc_alert_dest "${ACTION}"
222
223	# Enable alerting on the LAN channel
224	[ $RETVAL -eq 0 ] && set_ipmi_alert "${ACTION}"
225}
226
227write_trapd_conf()
228{
229	printf "###############################################\n"
230	printf "# Automatically created by %s #\n" "${SCRIPT_NAME}"
231	printf "forward %s %s\n" "${BMC_OID}*" "${FORWARD_HOST}"
232	printf "###############################################\n"
233}
234
235config_trapd()
236{
237	# Proceed only if snmptrapd is available on the system
238	if [ -f ${TRAPD_CONF} ]; then
239		write_trapd_conf > ${TRAPD_LOCAL_CONF}
240		[ $? -ne 0 ] && RETVAL=11
241	else
242		return 1
243	fi
244}
245
246trap_sink_exists()
247{
248	# TODO: We only set the first match. We should be able to set
249	# multiple
250	FORWARD_HOST=$(awk '/^trap.*sink/{print $2}; /^informsink/{print $2}' \
251			/etc/snmp/snmpd*conf | head -1)
252	if [ -z "${FORWARD_HOST}" ]; then
253		# there is no trapsink setup.
254		return 1
255	else
256		return 0
257	fi
258}
259
260# Forward SNMP traps from the BMC to trapsink.
261trap_forward()
262{
263	NO_TRAP=0
264	ACTION=${1} # set or reset
265
266	if [ "${ACTION}" = "set" ]; then
267		# Get trapd config,
268		if trap_sink_exists; then
269			config_trapd && config_bmc_alert
270		else
271			# exit silently if there is no sink
272			NO_TRAP=1
273		fi
274	else
275		if [ -f ${TRAPD_LOCAL_CONF} ]; then
276			rm -f ${TRAPD_LOCAL_CONF} >/dev/null 2>&1
277		else
278			NO_TRAP=1
279		fi
280	fi
281}
282
283#############################################################################
284service_reload()
285{
286	#TODO: do this in systemd
287	if [ ${RETVAL} -eq 0 ] && [ "${RELOAD_SERVICES}" = "yes" ]; then
288		service $1 reload
289		[ $? -ne 0 ] && RETVAL=6
290	fi
291	return
292}
293
294#############################################################################
295start()
296{
297	if bmc_info_exists && check_snmp; then
298		touch ${LOCKFILE}
299		set_snmpd_conf_path && set_snmp_proxy
300		[ $RETVAL -eq 0 ] && service_reload snmpd
301
302		if [ "${TRAP_FORWARD}" = "yes" ]; then
303			trap_forward "set"
304			[ $RETVAL -eq 0 ] && [ $NO_TRAP -eq 0 ] && \
305				service_reload snmptrapd
306		fi
307	fi
308}
309
310#############################################################################
311stop()
312{
313	[ ! -f ${LOCKFILE} ] && return
314	if bmc_info_exists && check_snmp; then
315		disable_snmp_proxy
316		[ $RETVAL -eq 0 ] && service_reload snmpd
317
318		if [ "${TRAP_FORWARD}" = "yes" ]; then
319			trap_forward "reset"
320			[ $RETVAL -eq 0 ] && [ $NO_TRAP -eq 0 ] && \
321				service_reload snmptrapd
322		fi
323		rm -f ${LOCKFILE}
324	fi
325}
326
327#############################################################################
328status()
329{
330	eval_gettext "${SCRIPT_NAME}: snmp proxy to BMC is "
331	# Checking for lockfile is better.
332	#if grep -q "^proxy" "${SNMPD_LOCAL_CONF}" > /dev/null 2>&1 ; then
333	if [ -f ${LOCKFILE} ]; then
334		eval_gettext "set"
335	else
336		eval_gettext "not set"
337	fi
338	echo
339	RETVAL=0
340}
341
342#############################################################################
343usage()
344{
345	eval_gettext "Usage: $0 {start|stop|status}"; echo 1>&2
346	RETVAL=1
347}
348
349#############################################################################
350# MAIN
351#############################################################################
352case "$1" in
353	start) start ;;
354	stop)  stop ;;
355	status)	status ;;
356	*) usage ;;
357esac
358
359case "$RETVAL" in
360	0|1) ;;
361	2) eval_gettext "${SCRIPT_NAME}: failed to read ${BMC_INFO} " 1>&2 ;;
362	3) eval_gettext "${SCRIPT_NAME}: failed to get proxy config." 1>&2 ;;
363	4) eval_gettext "${SCRIPT_NAME}: failed to set ${SNMPD_LOCAL_CONF}." 1>&2 ;;
364	5) eval_gettext "${SCRIPT_NAME}: failed to disable snmp proxy." 1>&2 ;;
365	6) eval_gettext "${SCRIPT_NAME}: failed to reload snmpd." 1>&2 ;;
366	7) eval_gettext "${SCRIPT_NAME}: failed to update ${SYSCONF}." 1>&2 ;;
367	8) eval_gettext "${SCRIPT_NAME}: failed to set IPMI alert dest." 1>&2 ;;
368	9) eval_gettext "${SCRIPT_NAME}: no free IPMI alert dest." 1>&2 ;;
369	10) eval_gettext "${SCRIPT_NAME}: failed to set IPMI PEF." 1>&2 ;;
370	11) eval_gettext "${SCRIPT_NAME}: failed to write snmptrapd.conf." 1>&2 ;;
371	12) eval_gettext "${SCRIPT_NAME}: snmpd not found." 1>&2 ;;
372	*) eval_gettext "${SCRIPT_NAME}: unknown error." 1>&2 ;;
373esac
374
375if [ ${RETVAL} -gt 1 ]; then
376        eval_gettext " Return code: ${RETVAL}"; echo
377fi
378exit ${RETVAL}
379#############################################################################
380# end of file
381#############################################################################
382