summaryrefslogtreecommitdiff
path: root/contrib/bmc-snmp-proxy
blob: fcb2ab8256d3096c70b60ca04c68950fa24de5e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#!/bin/sh
#############################################################################
#
# bmc-snmp-proxy:	Set SNMP proxy to BMC (Baseboard Management Controller)
#
# version:	0.62
#
# Authors:	Charles Rose <charles_rose@dell.com>
#		Jordan Hargrave <jordan_hargrave@dell.com>
#
# 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="/var/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
#############################################################################