xref: /freebsd/contrib/ofed/infiniband-diags/src/ibtracert.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*
2  * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3  * Copyright (c) 2009 HNR Consulting.  All rights reserved.
4  * Copyright (c) 2010,2011 Mellanox Technologies LTD.  All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35 
36 #if HAVE_CONFIG_H
37 #  include <config.h>
38 #endif				/* HAVE_CONFIG_H */
39 
40 #define _GNU_SOURCE
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 #include <getopt.h>
46 #include <netinet/in.h>
47 #include <inttypes.h>
48 
49 #include <infiniband/umad.h>
50 #include <infiniband/mad.h>
51 #include <complib/cl_nodenamemap.h>
52 
53 #include "ibdiag_common.h"
54 
55 struct ibmad_port *srcport;
56 
57 #define MAXHOPS	63
58 
59 static char *node_type_str[] = {
60 	"???",
61 	"ca",
62 	"switch",
63 	"router",
64 	"iwarp rnic"
65 };
66 
67 static int timeout = 0;		/* ms */
68 static int force;
69 static FILE *f;
70 
71 static char *node_name_map_file = NULL;
72 static nn_map_t *node_name_map = NULL;
73 
74 typedef struct Port Port;
75 typedef struct Switch Switch;
76 typedef struct Node Node;
77 
78 struct Port {
79 	Port *next;
80 	Port *remoteport;
81 	uint64_t portguid;
82 	int portnum;
83 	int lid;
84 	int lmc;
85 	int state;
86 	int physstate;
87 	char portinfo[64];
88 };
89 
90 struct Switch {
91 	int linearcap;
92 	int mccap;
93 	int linearFDBtop;
94 	int fdb_base;
95 	int enhsp0;
96 	int8_t fdb[64];
97 	char switchinfo[64];
98 };
99 
100 struct Node {
101 	Node *htnext;
102 	Node *dnext;
103 	Port *ports;
104 	ib_portid_t path;
105 	int type;
106 	int dist;
107 	int numports;
108 	int upport;
109 	Node *upnode;
110 	uint64_t nodeguid;	/* also portguid */
111 	char nodedesc[64];
112 	char nodeinfo[64];
113 };
114 
115 Node *nodesdist[MAXHOPS];
116 uint64_t target_portguid;
117 
118 /*
119  * is_port_inactive
120  * Checks whether or not the port state is other than active.
121  * The "sw" argument is only relevant when the port is on a
122  * switch; for HCAs and routers, this argument is ignored.
123  * Returns 1 when port is not active and 0 when active.
124  * Base switch port 0 is considered always active.
125  */
126 static int is_port_inactive(Node * node, Port * port, Switch * sw)
127 {
128 	int res = 0;
129 	if (port->state != 4 &&
130 	    (node->type != IB_NODE_SWITCH ||
131 	     (node->type == IB_NODE_SWITCH && sw->enhsp0)))
132 		res = 1;
133 	return res;
134 }
135 
136 static int get_node(Node * node, Port * port, ib_portid_t * portid)
137 {
138 	void *pi = port->portinfo, *ni = node->nodeinfo, *nd = node->nodedesc;
139 	char *s, *e;
140 
141 	memset(ni, 0, sizeof(node->nodeinfo));
142 	if (!smp_query_via(ni, portid, IB_ATTR_NODE_INFO, 0, timeout, srcport))
143 		return -1;
144 
145 	memset(nd, 0, sizeof(node->nodedesc));
146 	if (!smp_query_via(nd, portid, IB_ATTR_NODE_DESC, 0, timeout, srcport))
147 		return -1;
148 
149 	for (s = nd, e = s + 64; s < e; s++) {
150 		if (!*s)
151 			break;
152 		if (!isprint(*s))
153 			*s = ' ';
154 	}
155 
156 	memset(pi, 0, sizeof(port->portinfo));
157 	if (!smp_query_via(pi, portid, IB_ATTR_PORT_INFO, 0, timeout, srcport))
158 		return -1;
159 
160 	mad_decode_field(ni, IB_NODE_GUID_F, &node->nodeguid);
161 	mad_decode_field(ni, IB_NODE_TYPE_F, &node->type);
162 	mad_decode_field(ni, IB_NODE_NPORTS_F, &node->numports);
163 
164 	mad_decode_field(ni, IB_NODE_PORT_GUID_F, &port->portguid);
165 	mad_decode_field(ni, IB_NODE_LOCAL_PORT_F, &port->portnum);
166 	mad_decode_field(pi, IB_PORT_LID_F, &port->lid);
167 	mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc);
168 	mad_decode_field(pi, IB_PORT_STATE_F, &port->state);
169 
170 	DEBUG("portid %s: got node %" PRIx64 " '%s'", portid2str(portid),
171 	      node->nodeguid, node->nodedesc);
172 	return 0;
173 }
174 
175 static int switch_lookup(Switch * sw, ib_portid_t * portid, int lid)
176 {
177 	void *si = sw->switchinfo, *fdb = sw->fdb;
178 
179 	memset(si, 0, sizeof(sw->switchinfo));
180 	if (!smp_query_via(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout,
181 			   srcport))
182 		return -1;
183 
184 	mad_decode_field(si, IB_SW_LINEAR_FDB_CAP_F, &sw->linearcap);
185 	mad_decode_field(si, IB_SW_LINEAR_FDB_TOP_F, &sw->linearFDBtop);
186 	mad_decode_field(si, IB_SW_ENHANCED_PORT0_F, &sw->enhsp0);
187 
188 	if (lid >= sw->linearcap && lid > sw->linearFDBtop)
189 		return -1;
190 
191 	memset(fdb, 0, sizeof(sw->fdb));
192 	if (!smp_query_via(fdb, portid, IB_ATTR_LINEARFORWTBL, lid / 64,
193 			   timeout, srcport))
194 		return -1;
195 
196 	DEBUG("portid %s: forward lid %d to port %d",
197 	      portid2str(portid), lid, sw->fdb[lid % 64]);
198 	return sw->fdb[lid % 64];
199 }
200 
201 static int sameport(Port * a, Port * b)
202 {
203 	return a->portguid == b->portguid || (force && a->lid == b->lid);
204 }
205 
206 static int extend_dpath(ib_dr_path_t * path, int nextport)
207 {
208 	if (path->cnt + 2 >= sizeof(path->p))
209 		return -1;
210 	++path->cnt;
211 	path->p[path->cnt] = (uint8_t) nextport;
212 	return path->cnt;
213 }
214 
215 static void dump_endnode(int dump, char *prompt, Node * node, Port * port)
216 {
217 	char *nodename = NULL;
218 
219 	if (!dump)
220 		return;
221 	if (dump == 1) {
222 		fprintf(f, "%s {0x%016" PRIx64 "}[%d]\n",
223 			prompt, node->nodeguid,
224 			node->type == IB_NODE_SWITCH ? 0 : port->portnum);
225 		return;
226 	}
227 
228 	nodename =
229 	    remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
230 
231 	fprintf(f, "%s %s {0x%016" PRIx64 "} portnum %d lid %u-%u \"%s\"\n",
232 		prompt,
233 		(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
234 		node->nodeguid,
235 		node->type == IB_NODE_SWITCH ? 0 : port->portnum, port->lid,
236 		port->lid + (1 << port->lmc) - 1, nodename);
237 
238 	free(nodename);
239 }
240 
241 static void dump_route(int dump, Node * node, int outport, Port * port)
242 {
243 	char *nodename = NULL;
244 
245 	if (!dump && !ibverbose)
246 		return;
247 
248 	nodename =
249 	    remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
250 
251 	if (dump == 1)
252 		fprintf(f, "[%d] -> {0x%016" PRIx64 "}[%d]\n",
253 			outport, port->portguid, port->portnum);
254 	else
255 		fprintf(f, "[%d] -> %s port {0x%016" PRIx64
256 			"}[%d] lid %u-%u \"%s\"\n", outport,
257 			(node->type <=
258 			 IB_NODE_MAX ? node_type_str[node->type] : "???"),
259 			port->portguid, port->portnum, port->lid,
260 			port->lid + (1 << port->lmc) - 1, nodename);
261 
262 	free(nodename);
263 }
264 
265 static int find_route(ib_portid_t * from, ib_portid_t * to, int dump)
266 {
267 	Node *node, fromnode, tonode, nextnode;
268 	Port *port, fromport, toport, nextport;
269 	Switch sw;
270 	int maxhops = MAXHOPS;
271 	int portnum, outport = 255, next_sw_outport = 255;
272 
273 	memset(&fromnode,0,sizeof(Node));
274 	memset(&tonode,0,sizeof(Node));
275 	memset(&nextnode,0,sizeof(Node));
276 	memset(&fromport,0,sizeof(Port));
277 	memset(&toport,0,sizeof(Port));
278 	memset(&nextport,0,sizeof(Port));
279 
280 	DEBUG("from %s", portid2str(from));
281 
282 	if (get_node(&fromnode, &fromport, from) < 0 ||
283 	    get_node(&tonode, &toport, to) < 0) {
284 		IBWARN("can't reach to/from ports");
285 		if (!force)
286 			return -1;
287 		if (to->lid > 0)
288 			toport.lid = to->lid;
289 		IBWARN("Force: look for lid %d", to->lid);
290 	}
291 
292 	node = &fromnode;
293 	port = &fromport;
294 	portnum = port->portnum;
295 
296 	dump_endnode(dump, "From", node, port);
297 	if (node->type == IB_NODE_SWITCH) {
298 		next_sw_outport = switch_lookup(&sw, from, to->lid);
299 		if (next_sw_outport < 0 || next_sw_outport > node->numports) {
300 			/* Need to print the port in badtbl */
301 			outport = next_sw_outport;
302 			goto badtbl;
303 		}
304 	}
305 
306 	while (maxhops--) {
307 		if (is_port_inactive(node, port, &sw))
308 			goto badport;
309 
310 		if (sameport(port, &toport))
311 			break;	/* found */
312 
313 		if (node->type == IB_NODE_SWITCH) {
314 			DEBUG("switch node");
315 			outport = next_sw_outport;
316 
317 			if (extend_dpath(&from->drpath, outport) < 0)
318 				goto badpath;
319 
320 			if (get_node(&nextnode, &nextport, from) < 0) {
321 				IBWARN("can't reach port at %s",
322 				       portid2str(from));
323 				return -1;
324 			}
325 			if (outport == 0) {
326 				if (!sameport(&nextport, &toport))
327 					goto badtbl;
328 				else
329 					break;	/* found SMA port */
330 			}
331 		} else if ((node->type == IB_NODE_CA) ||
332 			   (node->type == IB_NODE_ROUTER)) {
333 			int ca_src = 0;
334 
335 			outport = portnum;
336 			DEBUG("ca or router node");
337 			if (!sameport(port, &fromport)) {
338 				IBWARN
339 				    ("can't continue: reached CA or router port %"
340 				     PRIx64 ", lid %d", port->portguid,
341 				     port->lid);
342 				return -1;
343 			}
344 			/* we are at CA or router "from" - go one hop back to (hopefully) a switch */
345 			if (from->drpath.cnt > 0) {
346 				DEBUG("ca or router node - return back 1 hop");
347 				from->drpath.cnt--;
348 			} else {
349 				ca_src = 1;
350 				if (portnum
351 				    && extend_dpath(&from->drpath, portnum) < 0)
352 					goto badpath;
353 			}
354 			if (get_node(&nextnode, &nextport, from) < 0) {
355 				IBWARN("can't reach port at %s",
356 				       portid2str(from));
357 				return -1;
358 			}
359 			/* fix port num to be seen from the CA or router side */
360 			if (!ca_src)
361 				nextport.portnum =
362 				    from->drpath.p[from->drpath.cnt + 1];
363 		}
364 		/* only if the next node is a switch, get switch info */
365 		if (nextnode.type == IB_NODE_SWITCH) {
366 			next_sw_outport = switch_lookup(&sw, from, to->lid);
367 			if (next_sw_outport < 0 ||
368 			    next_sw_outport > nextnode.numports) {
369 				/* needed to print the port in badtbl */
370 				outport = next_sw_outport;
371 				goto badtbl;
372 			}
373 		}
374 
375 		port = &nextport;
376 		if (is_port_inactive(&nextnode, port, &sw))
377 			goto badoutport;
378 		node = &nextnode;
379 		portnum = port->portnum;
380 		dump_route(dump, node, outport, port);
381 	}
382 
383 	if (maxhops <= 0) {
384 		IBWARN("no route found after %d hops", MAXHOPS);
385 		return -1;
386 	}
387 	dump_endnode(dump, "To", node, port);
388 	return 0;
389 
390 badport:
391 	IBWARN("Bad port state found: node \"%s\" port %d state %d",
392 	       clean_nodedesc(node->nodedesc), portnum, port->state);
393 	return -1;
394 badoutport:
395 	IBWARN("Bad out port state found: node \"%s\" outport %d state %d",
396 	       clean_nodedesc(node->nodedesc), outport, port->state);
397 	return -1;
398 badtbl:
399 	IBWARN
400 	    ("Bad forwarding table entry found at: node \"%s\" lid entry %d is %d (top %d)",
401 	     clean_nodedesc(node->nodedesc), to->lid, outport, sw.linearFDBtop);
402 	return -1;
403 badpath:
404 	IBWARN("Direct path too long!");
405 	return -1;
406 }
407 
408 /**************************
409  * MC span part
410  */
411 
412 #define HASHGUID(guid)		((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103)))
413 #define HTSZ 137
414 
415 static int insert_node(Node * new)
416 {
417 	static Node *nodestbl[HTSZ];
418 	int hash = HASHGUID(new->nodeguid) % HTSZ;
419 	Node *node;
420 
421 	for (node = nodestbl[hash]; node; node = node->htnext)
422 		if (node->nodeguid == new->nodeguid) {
423 			DEBUG("node %" PRIx64 " already exists", new->nodeguid);
424 			return -1;
425 		}
426 
427 	new->htnext = nodestbl[hash];
428 	nodestbl[hash] = new;
429 
430 	return 0;
431 }
432 
433 static int get_port(Port * port, int portnum, ib_portid_t * portid)
434 {
435 	char portinfo[64] = { 0 };
436 	void *pi = portinfo;
437 
438 	port->portnum = portnum;
439 
440 	if (!smp_query_via(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout,
441 			   srcport))
442 		return -1;
443 
444 	mad_decode_field(pi, IB_PORT_LID_F, &port->lid);
445 	mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc);
446 	mad_decode_field(pi, IB_PORT_STATE_F, &port->state);
447 	mad_decode_field(pi, IB_PORT_PHYS_STATE_F, &port->physstate);
448 
449 	VERBOSE("portid %s portnum %d: lid %d state %d physstate %d",
450 		portid2str(portid), portnum, port->lid, port->state,
451 		port->physstate);
452 	return 1;
453 }
454 
455 static void link_port(Port * port, Node * node)
456 {
457 	port->next = node->ports;
458 	node->ports = port;
459 }
460 
461 static int new_node(Node * node, Port * port, ib_portid_t * path, int dist)
462 {
463 	if (port->portguid == target_portguid) {
464 		node->dist = -1;	/* tag as target */
465 		link_port(port, node);
466 		dump_endnode(ibverbose, "found target", node, port);
467 		return 1;	/* found; */
468 	}
469 
470 	/* BFS search start with my self */
471 	if (insert_node(node) < 0)
472 		return -1;	/* known switch */
473 
474 	VERBOSE("insert dist %d node %p port %d lid %d", dist, node,
475 		port->portnum, port->lid);
476 
477 	link_port(port, node);
478 
479 	node->dist = dist;
480 	node->path = *path;
481 	node->dnext = nodesdist[dist];
482 	nodesdist[dist] = node;
483 
484 	return 0;
485 }
486 
487 static int switch_mclookup(Node * node, ib_portid_t * portid, int mlid,
488 			   char *map)
489 {
490 	Switch sw;
491 	char mdb[64];
492 	void *si = sw.switchinfo;
493 	uint16_t *msets = (uint16_t *) mdb;
494 	int maxsets, block, i, set;
495 
496 	memset(map, 0, 256);
497 
498 	memset(si, 0, sizeof(sw.switchinfo));
499 	if (!smp_query_via(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout,
500 			   srcport))
501 		return -1;
502 
503 	mlid -= 0xc000;
504 
505 	mad_decode_field(si, IB_SW_MCAST_FDB_CAP_F, &sw.mccap);
506 
507 	if (mlid >= sw.mccap)
508 		return -1;
509 
510 	block = mlid / 32;
511 	maxsets = (node->numports + 15) / 16;	/* round up */
512 
513 	for (set = 0; set < maxsets; set++) {
514 		memset(mdb, 0, sizeof(mdb));
515 		if (!smp_query_via(mdb, portid, IB_ATTR_MULTICASTFORWTBL,
516 				   block | (set << 28), timeout, srcport))
517 			return -1;
518 
519 		for (i = 0; i < 16; i++, map++) {
520 			uint16_t mask = ntohs(msets[mlid % 32]);
521 			if (mask & (1 << i))
522 				*map = 1;
523 			else
524 				continue;
525 			VERBOSE("Switch guid 0x%" PRIx64
526 				": mlid 0x%x is forwarded to port %d",
527 				node->nodeguid, mlid + 0xc000, i + set * 16);
528 		}
529 	}
530 
531 	return 0;
532 }
533 
534 /*
535  * Return 1 if found, 0 if not, -1 on errors.
536  */
537 static Node *find_mcpath(ib_portid_t * from, int mlid)
538 {
539 	Node *node, *remotenode;
540 	Port *port, *remoteport;
541 	char map[256];
542 	int r, i;
543 	int dist = 0, leafport = 0;
544 	ib_portid_t *path;
545 
546 	DEBUG("from %s", portid2str(from));
547 
548 	if (!(node = calloc(1, sizeof(Node))))
549 		IBEXIT("out of memory");
550 
551 	if (!(port = calloc(1, sizeof(Port))))
552 		IBEXIT("out of memory");
553 
554 	if (get_node(node, port, from) < 0) {
555 		IBWARN("can't reach node %s", portid2str(from));
556 		return 0;
557 	}
558 
559 	node->upnode = 0;	/* root */
560 	if ((r = new_node(node, port, from, 0)) > 0) {
561 		if (node->type != IB_NODE_SWITCH) {
562 			IBWARN("ibtracert from CA to CA is unsupported");
563 			return 0;	/* ibtracert from host to itself is unsupported */
564 		}
565 
566 		if (switch_mclookup(node, from, mlid, map) < 0 || !map[0])
567 			return 0;
568 		return node;
569 	}
570 
571 	for (dist = 0; dist < MAXHOPS; dist++) {
572 
573 		for (node = nodesdist[dist]; node; node = node->dnext) {
574 
575 			path = &node->path;
576 
577 			VERBOSE("dist %d node %p", dist, node);
578 			dump_endnode(ibverbose, "processing", node,
579 				     node->ports);
580 
581 			memset(map, 0, sizeof(map));
582 
583 			if (node->type != IB_NODE_SWITCH) {
584 				if (dist)
585 					continue;
586 				leafport = path->drpath.p[path->drpath.cnt];
587 				map[port->portnum] = 1;
588 				node->upport = 0;	/* starting here */
589 				DEBUG("Starting from CA 0x%" PRIx64
590 				      " lid %d port %d (leafport %d)",
591 				      node->nodeguid, port->lid, port->portnum,
592 				      leafport);
593 			} else {	/* switch */
594 
595 				/* if starting from a leaf port fix up port (up port) */
596 				if (dist == 1 && leafport)
597 					node->upport = leafport;
598 
599 				if (switch_mclookup(node, path, mlid, map) < 0) {
600 					IBWARN("skipping bad Switch 0x%" PRIx64
601 					       "", node->nodeguid);
602 					continue;
603 				}
604 			}
605 
606 			for (i = 1; i <= node->numports; i++) {
607 				if (!map[i] || i == node->upport)
608 					continue;
609 
610 				if (dist == 0 && leafport) {
611 					if (from->drpath.cnt > 0)
612 						path->drpath.cnt--;
613 				} else {
614 					if (!(port = calloc(1, sizeof(Port))))
615 						IBEXIT("out of memory");
616 
617 					if (get_port(port, i, path) < 0) {
618 						IBWARN
619 						    ("can't reach node %s port %d",
620 						     portid2str(path), i);
621 						free(port);
622 						return 0;
623 					}
624 
625 					if (port->physstate != 5) {	/* LinkUP */
626 						free(port);
627 						continue;
628 					}
629 #if 0
630 					link_port(port, node);
631 #endif
632 
633 					if (extend_dpath(&path->drpath, i) < 0) {
634 						free(port);
635 						return 0;
636 					}
637 				}
638 
639 				if (!(remotenode = calloc(1, sizeof(Node))))
640 					IBEXIT("out of memory");
641 
642 				if (!(remoteport = calloc(1, sizeof(Port))))
643 					IBEXIT("out of memory");
644 
645 				if (get_node(remotenode, remoteport, path) < 0) {
646 					IBWARN
647 					    ("NodeInfo on %s port %d failed, skipping port",
648 					     portid2str(path), i);
649 					path->drpath.cnt--;	/* restore path */
650 					free(remotenode);
651 					free(remoteport);
652 					continue;
653 				}
654 
655 				remotenode->upnode = node;
656 				remotenode->upport = remoteport->portnum;
657 				remoteport->remoteport = port;
658 
659 				if ((r = new_node(remotenode, remoteport, path,
660 						  dist + 1)) > 0)
661 					return remotenode;
662 
663 				if (r == 0)
664 					dump_endnode(ibverbose, "new remote",
665 						     remotenode, remoteport);
666 				else if (remotenode->type == IB_NODE_SWITCH)
667 					dump_endnode(2,
668 						     "ERR: circle discovered at",
669 						     remotenode, remoteport);
670 
671 				path->drpath.cnt--;	/* restore path */
672 			}
673 		}
674 	}
675 
676 	return 0;		/* not found */
677 }
678 
679 static uint64_t find_target_portguid(ib_portid_t * to)
680 {
681 	Node tonode;
682 	Port toport;
683 
684 	if (get_node(&tonode, &toport, to) < 0) {
685 		IBWARN("can't find to port\n");
686 		return -1;
687 	}
688 
689 	return toport.portguid;
690 }
691 
692 static void dump_mcpath(Node * node, int dumplevel)
693 {
694 	char *nodename = NULL;
695 
696 	if (node->upnode)
697 		dump_mcpath(node->upnode, dumplevel);
698 
699 	nodename =
700 	    remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
701 
702 	if (!node->dist) {
703 		printf("From %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n",
704 		       (node->type <=
705 			IB_NODE_MAX ? node_type_str[node->type] : "???"),
706 		       node->nodeguid, node->ports->portnum, node->ports->lid,
707 		       node->ports->lid + (1 << node->ports->lmc) - 1,
708 		       nodename);
709 		goto free_name;
710 	}
711 
712 	if (node->dist) {
713 		if (dumplevel == 1)
714 			printf("[%d] -> %s {0x%016" PRIx64 "}[%d]\n",
715 			       node->ports->remoteport->portnum,
716 			       (node->type <=
717 				IB_NODE_MAX ? node_type_str[node->type] :
718 				"???"), node->nodeguid, node->upport);
719 		else
720 			printf("[%d] -> %s 0x%" PRIx64 "[%d] lid %u \"%s\"\n",
721 			       node->ports->remoteport->portnum,
722 			       (node->type <=
723 				IB_NODE_MAX ? node_type_str[node->type] :
724 				"???"), node->nodeguid, node->upport,
725 			       node->ports->lid, nodename);
726 	}
727 
728 	if (node->dist < 0)
729 		/* target node */
730 		printf("To %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n",
731 		       (node->type <=
732 			IB_NODE_MAX ? node_type_str[node->type] : "???"),
733 		       node->nodeguid, node->ports->portnum, node->ports->lid,
734 		       node->ports->lid + (1 << node->ports->lmc) - 1,
735 		       nodename);
736 
737 free_name:
738 	free(nodename);
739 }
740 
741 static int resolve_lid(ib_portid_t * portid, const void *srcport)
742 {
743 	uint8_t portinfo[64] = { 0 };
744 	uint16_t lid;
745 
746 	if (!smp_query_via(portinfo, portid, IB_ATTR_PORT_INFO, 0, 0, srcport))
747 		return -1;
748 	mad_decode_field(portinfo, IB_PORT_LID_F, &lid);
749 
750 	ib_portid_set(portid, lid, 0, 0);
751 
752 	return 0;
753 }
754 
755 static int dumplevel = 2, multicast, mlid;
756 
757 static int process_opt(void *context, int ch, char *optarg)
758 {
759 	switch (ch) {
760 	case 1:
761 		node_name_map_file = strdup(optarg);
762 		break;
763 	case 'm':
764 		multicast++;
765 		mlid = strtoul(optarg, 0, 0);
766 		break;
767 	case 'f':
768 		force++;
769 		break;
770 	case 'n':
771 		dumplevel = 1;
772 		break;
773 	default:
774 		return -1;
775 	}
776 	return 0;
777 }
778 
779 int main(int argc, char **argv)
780 {
781 	int mgmt_classes[3] =
782 	    { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS };
783 	ib_portid_t my_portid = { 0 };
784 	ib_portid_t src_portid = { 0 };
785 	ib_portid_t dest_portid = { 0 };
786 	Node *endnode;
787 
788 	const struct ibdiag_opt opts[] = {
789 		{"force", 'f', 0, NULL, "force"},
790 		{"no_info", 'n', 0, NULL, "simple format"},
791 		{"mlid", 'm', 1, "<mlid>", "multicast trace of the mlid"},
792 		{"node-name-map", 1, 1, "<file>", "node name map file"},
793 		{0}
794 	};
795 	char usage_args[] = "<src-addr> <dest-addr>";
796 	const char *usage_examples[] = {
797 		"- Unicast examples:",
798 		"4 16\t\t\t# show path between lids 4 and 16",
799 		"-n 4 16\t\t# same, but using simple output format",
800 		"-G 0x8f1040396522d 0x002c9000100d051\t# use guid addresses",
801 
802 		" - Multicast examples:",
803 		"-m 0xc000 4 16\t# show multicast path of mlid 0xc000 between lids 4 and 16",
804 		NULL,
805 	};
806 
807 	ibdiag_process_opts(argc, argv, NULL, "DK", opts, process_opt,
808 			    usage_args, usage_examples);
809 
810 	f = stdout;
811 	argc -= optind;
812 	argv += optind;
813 
814 	if (argc < 2)
815 		ibdiag_show_usage();
816 
817 	if (ibd_timeout)
818 		timeout = ibd_timeout;
819 
820 	srcport = mad_rpc_open_port(ibd_ca, ibd_ca_port, mgmt_classes, 3);
821 	if (!srcport)
822 		IBEXIT("Failed to open '%s' port '%d'", ibd_ca, ibd_ca_port);
823 
824 	smp_mkey_set(srcport, ibd_mkey);
825 
826 	node_name_map = open_node_name_map(node_name_map_file);
827 
828 	if (resolve_portid_str(ibd_ca, ibd_ca_port, &src_portid, argv[0],
829 			       ibd_dest_type, ibd_sm_id, srcport) < 0)
830 		IBEXIT("can't resolve source port %s", argv[0]);
831 
832 	if (resolve_portid_str(ibd_ca, ibd_ca_port, &dest_portid, argv[1],
833 			       ibd_dest_type, ibd_sm_id, srcport) < 0)
834 		IBEXIT("can't resolve destination port %s", argv[1]);
835 
836 	if (ibd_dest_type == IB_DEST_DRPATH) {
837 		if (resolve_lid(&src_portid, NULL) < 0)
838 			IBEXIT("cannot resolve lid for port \'%s\'",
839 				portid2str(&src_portid));
840 		if (resolve_lid(&dest_portid, NULL) < 0)
841 			IBEXIT("cannot resolve lid for port \'%s\'",
842 				portid2str(&dest_portid));
843 	}
844 
845 	if (dest_portid.lid == 0 || src_portid.lid == 0) {
846 		IBWARN("bad src/dest lid");
847 		ibdiag_show_usage();
848 	}
849 
850 	if (ibd_dest_type != IB_DEST_DRPATH) {
851 		/* first find a direct path to the src port */
852 		if (find_route(&my_portid, &src_portid, 0) < 0)
853 			IBEXIT("can't find a route to the src port");
854 
855 		src_portid = my_portid;
856 	}
857 
858 	if (!multicast) {
859 		if (find_route(&src_portid, &dest_portid, dumplevel) < 0)
860 			IBEXIT("can't find a route from src to dest");
861 		exit(0);
862 	} else {
863 		if (mlid < 0xc000)
864 			IBWARN("invalid MLID; must be 0xc000 or larger");
865 	}
866 
867 	if (!(target_portguid = find_target_portguid(&dest_portid)))
868 		IBEXIT("can't reach target lid %d", dest_portid.lid);
869 
870 	if (!(endnode = find_mcpath(&src_portid, mlid)))
871 		IBEXIT("can't find a multicast route from src to dest");
872 
873 	/* dump multicast path */
874 	dump_mcpath(endnode, dumplevel);
875 
876 	close_node_name_map(node_name_map);
877 
878 	mad_rpc_close_port(srcport);
879 
880 	exit(0);
881 }
882