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