xref: /openbmc/ipmitool/src/plugins/ipmi_intf.c (revision c18ec02f)
1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #if defined(HAVE_CONFIG_H)
37 # include <config.h>
38 #endif
39 
40 #if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <ifaddrs.h>
46 #include <unistd.h>
47 #include <netdb.h>
48 #endif
49 
50 
51 #include <ipmitool/ipmi_intf.h>
52 #include <ipmitool/ipmi.h>
53 #include <ipmitool/ipmi_sdr.h>
54 #include <ipmitool/log.h>
55 
56 #ifdef IPMI_INTF_OPEN
57 extern struct ipmi_intf ipmi_open_intf;
58 #endif
59 #ifdef IPMI_INTF_IMB
60 extern struct ipmi_intf ipmi_imb_intf;
61 #endif
62 #ifdef IPMI_INTF_LIPMI
63 extern struct ipmi_intf ipmi_lipmi_intf;
64 #endif
65 #ifdef IPMI_INTF_BMC
66 extern struct ipmi_intf ipmi_bmc_intf;
67 #endif
68 #ifdef IPMI_INTF_LAN
69 extern struct ipmi_intf ipmi_lan_intf;
70 #endif
71 #ifdef IPMI_INTF_LANPLUS
72 extern struct ipmi_intf ipmi_lanplus_intf;
73 #endif
74 #ifdef IPMI_INTF_FREE
75 extern struct ipmi_intf ipmi_free_intf;
76 #endif
77 #ifdef IPMI_INTF_SERIAL
78 extern struct ipmi_intf ipmi_serial_term_intf;
79 extern struct ipmi_intf ipmi_serial_bm_intf;
80 #endif
81 #ifdef IPMI_INTF_DUMMY
82 extern struct ipmi_intf ipmi_dummy_intf;
83 #endif
84 
85 struct ipmi_intf * ipmi_intf_table[] = {
86 #ifdef IPMI_INTF_OPEN
87 	&ipmi_open_intf,
88 #endif
89 #ifdef IPMI_INTF_IMB
90 	&ipmi_imb_intf,
91 #endif
92 #ifdef IPMI_INTF_LIPMI
93 	&ipmi_lipmi_intf,
94 #endif
95 #ifdef IPMI_INTF_BMC
96 	&ipmi_bmc_intf,
97 #endif
98 #ifdef IPMI_INTF_LAN
99 	&ipmi_lan_intf,
100 #endif
101 #ifdef IPMI_INTF_LANPLUS
102 	&ipmi_lanplus_intf,
103 #endif
104 #ifdef IPMI_INTF_FREE
105 	&ipmi_free_intf,
106 #endif
107 #ifdef IPMI_INTF_SERIAL
108 	&ipmi_serial_term_intf,
109 	&ipmi_serial_bm_intf,
110 #endif
111 #ifdef IPMI_INTF_DUMMY
112 	&ipmi_dummy_intf,
113 #endif
114 	NULL
115 };
116 
117 /* ipmi_intf_print  -  Print list of interfaces
118  *
119  * no meaningful return code
120  */
121 void ipmi_intf_print(struct ipmi_intf_support * intflist)
122 {
123 	struct ipmi_intf ** intf;
124 	struct ipmi_intf_support * sup;
125 	int def = 1;
126 	int found;
127 
128 	lprintf(LOG_NOTICE, "Interfaces:");
129 
130 	for (intf = ipmi_intf_table; intf && *intf; intf++) {
131 
132 		if (intflist != NULL) {
133 			found = 0;
134 			for (sup=intflist; sup->name != NULL; sup++) {
135 				if (strncmp(sup->name, (*intf)->name, strlen(sup->name)) == 0 &&
136 				    strncmp(sup->name, (*intf)->name, strlen((*intf)->name)) == 0 &&
137 				    sup->supported == 1)
138 					found = 1;
139 			}
140 			if (found == 0)
141 				continue;
142 		}
143 
144 		lprintf(LOG_NOTICE, "\t%-12s  %s %s",
145 			(*intf)->name, (*intf)->desc,
146 			def ? "[default]" : "");
147 		def = 0;
148 	}
149 	lprintf(LOG_NOTICE, "");
150 }
151 
152 /* ipmi_intf_load  -  Load an interface from the interface table above
153  *                    If no interface name is given return first entry
154  *
155  * @name:	interface name to try and load
156  *
157  * returns pointer to inteface structure if found
158  * returns NULL on error
159  */
160 struct ipmi_intf * ipmi_intf_load(char * name)
161 {
162 	struct ipmi_intf ** intf;
163 	struct ipmi_intf * i;
164 
165 	if (name == NULL) {
166 		i = ipmi_intf_table[0];
167 		if (i->setup != NULL && (i->setup(i) < 0)) {
168 			lprintf(LOG_ERR, "Unable to setup "
169 				"interface %s", name);
170 			return NULL;
171 		}
172 		return i;
173 	}
174 
175 	for (intf = ipmi_intf_table;
176 	     ((intf != NULL) && (*intf != NULL));
177 	     intf++) {
178 		i = *intf;
179 		if (strncmp(name, i->name, strlen(name)) == 0) {
180 			if (i->setup != NULL && (i->setup(i) < 0)) {
181 				lprintf(LOG_ERR, "Unable to setup "
182 					"interface %s", name);
183 				return NULL;
184 			}
185 			return i;
186 		}
187 	}
188 
189 	return NULL;
190 }
191 
192 void
193 ipmi_intf_session_set_hostname(struct ipmi_intf * intf, char * hostname)
194 {
195 	if (intf->session == NULL)
196 		return;
197 
198 	memset(intf->session->hostname, 0, 16);
199 
200 	if (hostname != NULL) {
201 		memcpy(intf->session->hostname, hostname,
202 		       __min(strlen(hostname), 64));
203 	}
204 }
205 
206 void
207 ipmi_intf_session_set_username(struct ipmi_intf * intf, char * username)
208 {
209 	if (intf->session == NULL)
210 		return;
211 
212 	memset(intf->session->username, 0, 17);
213 
214 	if (username == NULL)
215 		return;
216 
217 	memcpy(intf->session->username, username, __min(strlen(username), 16));
218 }
219 
220 void
221 ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password)
222 {
223 	if (intf->session == NULL)
224 		return;
225 
226 	memset(intf->session->authcode, 0, IPMI_AUTHCODE_BUFFER_SIZE);
227 
228 	if (password == NULL) {
229 		intf->session->password = 0;
230 		return;
231 	}
232 
233 	intf->session->password = 1;
234 	memcpy(intf->session->authcode, password,
235 	       __min(strlen(password), IPMI_AUTHCODE_BUFFER_SIZE));
236 }
237 
238 void
239 ipmi_intf_session_set_privlvl(struct ipmi_intf * intf, uint8_t level)
240 {
241 	if (intf->session == NULL)
242 		return;
243 
244 	intf->session->privlvl = level;
245 }
246 
247 void
248 ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit)
249 {
250 	if (intf->session == NULL)
251 		return;
252 
253 	intf->session->v2_data.lookupbit = lookupbit;
254 }
255 
256 void
257 ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, uint8_t cipher_suite_id)
258 {
259 	if (intf->session == NULL)
260 		return;
261 
262 	intf->session->cipher_suite_id = cipher_suite_id;
263 }
264 
265 void
266 ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char)
267 {
268 	if (intf->session == NULL)
269 		return;
270 
271 	intf->session->sol_escape_char = sol_escape_char;
272 }
273 
274 void
275 ipmi_intf_session_set_kgkey(struct ipmi_intf * intf, char * kgkey)
276 {
277 	if (intf->session == NULL)
278 		return;
279 
280 	memset(intf->session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE);
281 
282 	if (kgkey == NULL)
283 		return;
284 
285 	memcpy(intf->session->v2_data.kg, kgkey,
286 	       __min(strlen(kgkey), IPMI_KG_BUFFER_SIZE));
287 }
288 
289 void
290 ipmi_intf_session_set_port(struct ipmi_intf * intf, int port)
291 {
292 	if (intf->session == NULL)
293 		return;
294 
295 	intf->session->port = port;
296 }
297 
298 void
299 ipmi_intf_session_set_authtype(struct ipmi_intf * intf, uint8_t authtype)
300 {
301 	if (intf->session == NULL)
302 		return;
303 
304 	/* clear password field if authtype NONE specified */
305 	if (authtype == IPMI_SESSION_AUTHTYPE_NONE) {
306 		memset(intf->session->authcode, 0, IPMI_AUTHCODE_BUFFER_SIZE);
307 		intf->session->password = 0;
308 	}
309 
310 	intf->session->authtype_set = authtype;
311 }
312 
313 void
314 ipmi_intf_session_set_timeout(struct ipmi_intf * intf, uint32_t timeout)
315 {
316 	if (intf->session == NULL)
317 		return;
318 
319 	intf->session->timeout = timeout;
320 }
321 
322 void
323 ipmi_intf_session_set_retry(struct ipmi_intf * intf, int retry)
324 {
325 	if (intf->session == NULL)
326 		return;
327 
328 	intf->session->retry = retry;
329 }
330 
331 void
332 ipmi_cleanup(struct ipmi_intf * intf)
333 {
334 	ipmi_sdr_list_empty(intf);
335 }
336 
337 #if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
338 int
339 ipmi_intf_socket_connect(struct ipmi_intf * intf)
340 {
341 	struct ipmi_session *session;
342 
343 	struct sockaddr_storage addr;
344 	struct addrinfo hints;
345 	struct addrinfo *rp0 = NULL, *rp;
346 	char service[NI_MAXSERV];
347 	int rc;
348 
349 	if (!intf || intf->session == NULL) {
350 		return -1;
351 	}
352 
353 	session = intf->session;
354 
355 	if (session->hostname == NULL || strlen((const char *)session->hostname) == 0) {
356 		lprintf(LOG_ERR, "No hostname specified!");
357 		return -1;
358 	}
359 
360 	/* open port to BMC */
361 	memset(&addr, 0, sizeof(addr));
362 
363 	sprintf(service, "%d", session->port);
364 	/* Obtain address(es) matching host/port */
365 	memset(&hints, 0, sizeof(hints));
366 	hints.ai_family   = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
367 	hints.ai_socktype = SOCK_DGRAM;   /* Datagram socket */
368 	hints.ai_flags    = 0;            /* use AI_NUMERICSERV for no name resolution */
369 	hints.ai_protocol = IPPROTO_UDP; /*  */
370 
371 	if (getaddrinfo(session->hostname, service, &hints, &rp0) != 0) {
372 		lprintf(LOG_ERR, "Address lookup for %s failed",
373 			session->hostname);
374 		return -1;
375 	}
376 
377 	/* getaddrinfo() returns a list of address structures.
378 	 * Try each address until we successfully connect(2).
379 	 * If socket(2) (or connect(2)) fails, we (close the socket
380 	 * and) try the next address.
381 	 */
382 
383 	session->ai_family = AF_UNSPEC;
384 	for (rp = rp0; rp != NULL; rp = rp->ai_next) {
385 		/* We are only interested in IPv4 and IPv6 */
386 		if ((rp->ai_family != AF_INET6) && (rp->ai_family != AF_INET)) {
387 			continue;
388 		}
389 
390 		intf->fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
391 		if (intf->fd == -1) {
392 			continue;
393 		}
394 
395 		if (rp->ai_family == AF_INET) {
396 			if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
397 				memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
398 				session->addrlen = rp->ai_addrlen;
399 				session->ai_family = rp->ai_family;
400 				break;  /* Success */
401 			}
402 		}  else if (rp->ai_family == AF_INET6) {
403 			struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)rp->ai_addr;
404 			char hbuf[NI_MAXHOST];
405 			socklen_t len;
406 
407 			/* The scope was specified on the command line e.g. with -H FE80::219:99FF:FEA0:BD95%eth0 */
408 			if (addr6->sin6_scope_id != 0) {
409 				len = sizeof(struct sockaddr_in6);
410 				if (getnameinfo((struct sockaddr *)addr6, len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) == 0) {
411 					lprintf(LOG_DEBUG, "Trying address: %s scope=%d",
412 						hbuf,
413 						addr6->sin6_scope_id);
414 				}
415 				if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
416 					memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
417 					session->addrlen = rp->ai_addrlen;
418 					session->ai_family = rp->ai_family;
419 					break;  /* Success */
420 				}
421 			} else {
422 				/* No scope specified, try to get this from the list of interfaces */
423 				struct ifaddrs *ifaddrs = NULL;
424 				struct ifaddrs *ifa = NULL;
425 
426 				if (getifaddrs(&ifaddrs) < 0) {
427 					lprintf(LOG_ERR, "Interface address lookup for %s failed",
428 						session->hostname);
429 					break;
430 				}
431 
432 				for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
433 					if (ifa->ifa_addr == NULL) {
434 						continue;
435 					}
436 
437 					if (ifa->ifa_addr->sa_family == AF_INET6) {
438 						struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *)ifa->ifa_addr;
439 
440 						/* Skip unwanted addresses */
441 						if (IN6_IS_ADDR_MULTICAST(&tmp6->sin6_addr)) {
442 							continue;
443 						}
444 						if (IN6_IS_ADDR_LOOPBACK(&tmp6->sin6_addr)) {
445 							continue;
446 						}
447 						len = sizeof(struct sockaddr_in6);
448 						if ( getnameinfo((struct sockaddr *)tmp6, len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) == 0) {
449 							lprintf(LOG_DEBUG, "Testing %s interface address: %s scope=%d",
450 								ifa->ifa_name != NULL ? ifa->ifa_name : "???",
451 								hbuf,
452 								tmp6->sin6_scope_id);
453 						}
454 
455 						if (tmp6->sin6_scope_id != 0) {
456 							addr6->sin6_scope_id = tmp6->sin6_scope_id;
457 						} else {
458 							/*
459 							 * No scope information in interface address information
460 							 * On some OS'es, getifaddrs() is returning out the 'kernel' representation
461 							 * of scoped addresses which stores the scope in the 3rd and 4th
462 							 * byte. See also this page:
463 							 * http://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html
464 							 */
465 							if (IN6_IS_ADDR_LINKLOCAL(&tmp6->sin6_addr)
466 									&& (tmp6->sin6_addr.s6_addr16[1] != 0)) {
467 								addr6->sin6_scope_id = ntohs(tmp6->sin6_addr.s6_addr16[1]);
468 							}
469 						}
470 
471 						/* OK, now try to connect with the scope id from this interface address */
472 						if (addr6->sin6_scope_id != 0) {
473 							if (connect(intf->fd, rp->ai_addr, rp->ai_addrlen) != -1) {
474 								memcpy(&session->addr, rp->ai_addr, rp->ai_addrlen);
475 								session->addrlen = rp->ai_addrlen;
476 								session->ai_family = rp->ai_family;
477 								lprintf(LOG_DEBUG, "Successful connected on %s interface with scope id %d", ifa->ifa_name, tmp6->sin6_scope_id);
478 								break;  /* Success */
479 							}
480 						}
481 					}
482 				}
483 				freeifaddrs(ifaddrs);
484 			}
485 		}
486 		if (session->ai_family != AF_UNSPEC) {
487 			break;
488 		}
489 		close(intf->fd);
490 		intf->fd = -1;
491 	}
492 
493 	/* No longer needed */
494 	freeaddrinfo(rp0);
495 
496 	return ((intf->fd != -1) ? 0 : -1);
497 }
498 #endif
499 
500