1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff#if HAVE_CONFIG_H
35219820Sjeff#  include <config.h>
36219820Sjeff#endif /* HAVE_CONFIG_H */
37219820Sjeff
38219820Sjeff#define _GNU_SOURCE
39219820Sjeff#include <stdio.h>
40219820Sjeff#include <stdlib.h>
41219820Sjeff#include <unistd.h>
42219820Sjeff#include <stdarg.h>
43219820Sjeff#include <ctype.h>
44219820Sjeff#include <getopt.h>
45219820Sjeff#include <netinet/in.h>
46219820Sjeff#include <inttypes.h>
47219820Sjeff#include <errno.h>
48219820Sjeff
49219820Sjeff#include <infiniband/common.h>
50219820Sjeff#include <infiniband/umad.h>
51219820Sjeff#include <infiniband/mad.h>
52219820Sjeff#include <infiniband/complib/cl_nodenamemap.h>
53219820Sjeff
54219820Sjeff#include "ibdiag_common.h"
55219820Sjeff
56219820Sjeff#define MAXHOPS	63
57219820Sjeff
58219820Sjeffstatic char *node_type_str[] = {
59219820Sjeff	"???",
60219820Sjeff	"ca",
61219820Sjeff	"switch",
62219820Sjeff	"router",
63219820Sjeff	"iwarp rnic"
64219820Sjeff};
65219820Sjeff
66219820Sjeffstatic int timeout = 0;		/* ms */
67219820Sjeffstatic int verbose;
68219820Sjeffstatic int force;
69219820Sjeffstatic FILE *f;
70219820Sjeff
71219820Sjeffchar *argv0 = "ibtracert";
72219820Sjeff
73219820Sjeffstatic char *node_name_map_file = NULL;
74219820Sjeffstatic nn_map_t *node_name_map = NULL;
75219820Sjeff
76219820Sjefftypedef struct Port Port;
77219820Sjefftypedef struct Switch Switch;
78219820Sjefftypedef struct Node Node;
79219820Sjeff
80219820Sjeffstruct Port {
81219820Sjeff	Port *next;
82219820Sjeff	Port *remoteport;
83219820Sjeff	uint64_t portguid;
84219820Sjeff	int portnum;
85219820Sjeff	int lid;
86219820Sjeff	int lmc;
87219820Sjeff	int state;
88219820Sjeff	int physstate;
89219820Sjeff	char portinfo[64];
90219820Sjeff};
91219820Sjeff
92219820Sjeffstruct Switch {
93219820Sjeff	int linearcap;
94219820Sjeff	int mccap;
95219820Sjeff	int linearFDBtop;
96219820Sjeff	int fdb_base;
97219820Sjeff	int8_t fdb[64];
98219820Sjeff	char switchinfo[64];
99219820Sjeff};
100219820Sjeff
101219820Sjeffstruct Node {
102219820Sjeff	Node *htnext;
103219820Sjeff	Node *dnext;
104219820Sjeff	Port *ports;
105219820Sjeff	ib_portid_t path;
106219820Sjeff	int type;
107219820Sjeff	int dist;
108219820Sjeff	int numports;
109219820Sjeff	int upport;
110219820Sjeff	Node *upnode;
111219820Sjeff	uint64_t nodeguid;	/* also portguid */
112219820Sjeff	char nodedesc[64];
113219820Sjeff	char nodeinfo[64];
114219820Sjeff};
115219820Sjeff
116219820SjeffNode *nodesdist[MAXHOPS];
117219820Sjeffuint64_t target_portguid;
118219820Sjeff
119219820Sjeffstatic int
120219820Sjeffget_node(Node *node, Port *port, ib_portid_t *portid)
121219820Sjeff{
122219820Sjeff	void *pi = port->portinfo, *ni = node->nodeinfo, *nd = node->nodedesc;
123219820Sjeff	char *s, *e;
124219820Sjeff
125219820Sjeff	if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, timeout))
126219820Sjeff		return -1;
127219820Sjeff
128219820Sjeff	if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, timeout))
129219820Sjeff		return -1;
130219820Sjeff
131219820Sjeff	for (s = nd, e = s + 64; s < e; s++) {
132219820Sjeff		if (!*s)
133219820Sjeff			break;
134219820Sjeff		if (!isprint(*s))
135219820Sjeff			*s = ' ';
136219820Sjeff	}
137219820Sjeff
138219820Sjeff	if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, 0, timeout))
139219820Sjeff		return -1;
140219820Sjeff
141219820Sjeff	mad_decode_field(ni, IB_NODE_GUID_F, &node->nodeguid);
142219820Sjeff	mad_decode_field(ni, IB_NODE_TYPE_F, &node->type);
143219820Sjeff	mad_decode_field(ni, IB_NODE_NPORTS_F, &node->numports);
144219820Sjeff
145219820Sjeff	mad_decode_field(ni, IB_NODE_PORT_GUID_F, &port->portguid);
146219820Sjeff	mad_decode_field(ni, IB_NODE_LOCAL_PORT_F, &port->portnum);
147219820Sjeff	mad_decode_field(pi, IB_PORT_LID_F, &port->lid);
148219820Sjeff	mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc);
149219820Sjeff	mad_decode_field(pi, IB_PORT_STATE_F, &port->state);
150219820Sjeff
151219820Sjeff	DEBUG("portid %s: got node %" PRIx64 " '%s'", portid2str(portid), node->nodeguid, node->nodedesc);
152219820Sjeff	return 0;
153219820Sjeff}
154219820Sjeff
155219820Sjeffstatic int
156219820Sjeffswitch_lookup(Switch *sw, ib_portid_t *portid, int lid)
157219820Sjeff{
158219820Sjeff	void *si = sw->switchinfo, *fdb = sw->fdb;
159219820Sjeff
160219820Sjeff	if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout))
161219820Sjeff		return -1;
162219820Sjeff
163219820Sjeff	mad_decode_field(si, IB_SW_LINEAR_FDB_CAP_F, &sw->linearcap);
164219820Sjeff	mad_decode_field(si, IB_SW_LINEAR_FDB_TOP_F, &sw->linearFDBtop);
165219820Sjeff
166219820Sjeff	if (lid > sw->linearcap && lid > sw->linearFDBtop)
167219820Sjeff		return -1;
168219820Sjeff
169219820Sjeff	if (!smp_query(fdb, portid, IB_ATTR_LINEARFORWTBL, lid / 64, timeout))
170219820Sjeff		return -1;
171219820Sjeff
172219820Sjeff	DEBUG("portid %s: forward lid %d to port %d",
173219820Sjeff		portid2str(portid), lid, sw->fdb[lid % 64]);
174219820Sjeff	return sw->fdb[lid % 64];
175219820Sjeff}
176219820Sjeff
177219820Sjeffstatic int
178219820Sjeffsameport(Port *a, Port *b)
179219820Sjeff{
180219820Sjeff	return a->portguid == b->portguid || (force && a->lid == b->lid);
181219820Sjeff}
182219820Sjeff
183219820Sjeffstatic int
184219820Sjeffextend_dpath(ib_dr_path_t *path, int nextport)
185219820Sjeff{
186219820Sjeff	if (path->cnt+2 >= sizeof(path->p))
187219820Sjeff		return -1;
188219820Sjeff	++path->cnt;
189219820Sjeff	path->p[path->cnt] = nextport;
190219820Sjeff	return path->cnt;
191219820Sjeff}
192219820Sjeff
193219820Sjeffstatic void
194219820Sjeffdump_endnode(int dump, char *prompt, Node *node, Port *port)
195219820Sjeff{
196219820Sjeff	char *nodename = NULL;
197219820Sjeff
198219820Sjeff	if (!dump)
199219820Sjeff		return;
200219820Sjeff	if (dump == 1) {
201219820Sjeff		fprintf(f, "%s {0x%016" PRIx64 "}[%d]\n",
202219820Sjeff			prompt, node->nodeguid,
203219820Sjeff			node->type == IB_NODE_SWITCH ? 0 : port->portnum);
204219820Sjeff		return;
205219820Sjeff	}
206219820Sjeff
207219820Sjeff	nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
208219820Sjeff
209219820Sjeff	fprintf(f, "%s %s {0x%016" PRIx64 "} portnum %d lid %u-%u \"%s\"\n",
210219820Sjeff		prompt,
211219820Sjeff		(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
212219820Sjeff		node->nodeguid, node->type == IB_NODE_SWITCH ? 0 : port->portnum,
213219820Sjeff		port->lid, port->lid + (1 << port->lmc) - 1,
214219820Sjeff		nodename);
215219820Sjeff
216219820Sjeff	free(nodename);
217219820Sjeff}
218219820Sjeff
219219820Sjeffstatic void
220219820Sjeffdump_route(int dump, Node *node, int outport, Port *port)
221219820Sjeff{
222219820Sjeff	char *nodename = NULL;
223219820Sjeff
224219820Sjeff	if (!dump && !verbose)
225219820Sjeff		return;
226219820Sjeff
227219820Sjeff	nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
228219820Sjeff
229219820Sjeff	if (dump == 1)
230219820Sjeff		fprintf(f, "[%d] -> {0x%016" PRIx64 "}[%d]\n",
231219820Sjeff			outport, port->portguid, port->portnum);
232219820Sjeff	else
233219820Sjeff		fprintf(f, "[%d] -> %s port {0x%016" PRIx64 "}[%d] lid %u-%u \"%s\"\n",
234219820Sjeff			outport,
235219820Sjeff			(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
236219820Sjeff			port->portguid, port->portnum,
237219820Sjeff			port->lid, port->lid + (1 << port->lmc) - 1,
238219820Sjeff			nodename);
239219820Sjeff
240219820Sjeff	free(nodename);
241219820Sjeff}
242219820Sjeff
243219820Sjeffstatic int
244219820Sjefffind_route(ib_portid_t *from, ib_portid_t *to, int dump)
245219820Sjeff{
246219820Sjeff	Node *node, fromnode, tonode, nextnode;
247219820Sjeff	Port *port, fromport, toport, nextport;
248219820Sjeff	Switch sw;
249219820Sjeff	int maxhops = MAXHOPS;
250219820Sjeff	int portnum, outport;
251219820Sjeff
252219820Sjeff	DEBUG("from %s", portid2str(from));
253219820Sjeff
254219820Sjeff	if (get_node(&fromnode, &fromport, from) < 0 ||
255219820Sjeff	    get_node(&tonode, &toport, to) < 0) {
256219820Sjeff		IBWARN("can't reach to/from ports");
257219820Sjeff		if (!force)
258219820Sjeff			return -1;
259219820Sjeff		if (to->lid > 0)
260219820Sjeff			toport.lid = to->lid;
261219820Sjeff		IBWARN("Force: look for lid %d", to->lid);
262219820Sjeff	}
263219820Sjeff
264219820Sjeff	node = &fromnode;
265219820Sjeff	port = &fromport;
266219820Sjeff	portnum = port->portnum;
267219820Sjeff
268219820Sjeff	dump_endnode(dump, "From", node, port);
269219820Sjeff
270219820Sjeff	while (maxhops--) {
271219820Sjeff		if (port->state != 4)
272219820Sjeff			goto badport;
273219820Sjeff
274219820Sjeff		if (sameport(port, &toport))
275219820Sjeff			break;	/* found */
276219820Sjeff
277219820Sjeff		outport = portnum;
278219820Sjeff		if (node->type == IB_NODE_SWITCH) {
279219820Sjeff			DEBUG("switch node");
280219820Sjeff			if ((outport = switch_lookup(&sw, from, to->lid)) < 0 ||
281219820Sjeff			    outport > node->numports)
282219820Sjeff				goto badtbl;
283219820Sjeff
284219820Sjeff			if (extend_dpath(&from->drpath, outport) < 0)
285219820Sjeff				goto badpath;
286219820Sjeff
287219820Sjeff			if (get_node(&nextnode, &nextport, from) < 0) {
288219820Sjeff				IBWARN("can't reach port at %s", portid2str(from));
289219820Sjeff				return -1;
290219820Sjeff			}
291219820Sjeff			if (outport == 0) {
292219820Sjeff				if (!sameport(&nextport, &toport))
293219820Sjeff					goto badtbl;
294219820Sjeff				else
295219820Sjeff					break;	/* found SMA port */
296219820Sjeff			}
297219820Sjeff		} else if ((node->type == IB_NODE_CA) ||
298219820Sjeff			   (node->type == IB_NODE_ROUTER)) {
299219820Sjeff			int ca_src = 0;
300219820Sjeff
301219820Sjeff			DEBUG("ca or router node");
302219820Sjeff			if (!sameport(port, &fromport)) {
303219820Sjeff				IBWARN("can't continue: reached CA or router port %" PRIx64 ", lid %d",
304219820Sjeff					port->portguid, port->lid);
305219820Sjeff				return -1;
306219820Sjeff			}
307219820Sjeff			/* we are at CA or router "from" - go one hop back to (hopefully) a switch */
308219820Sjeff			if (from->drpath.cnt > 0) {
309219820Sjeff				DEBUG("ca or router node - return back 1 hop");
310219820Sjeff				from->drpath.cnt--;
311219820Sjeff			} else {
312219820Sjeff				ca_src = 1;
313219820Sjeff				if (portnum && extend_dpath(&from->drpath, portnum) < 0)
314219820Sjeff					goto badpath;
315219820Sjeff			}
316219820Sjeff			if (get_node(&nextnode, &nextport, from) < 0) {
317219820Sjeff				IBWARN("can't reach port at %s", portid2str(from));
318219820Sjeff				return -1;
319219820Sjeff			}
320219820Sjeff			/* fix port num to be seen from the CA or router side */
321219820Sjeff			if (!ca_src)
322219820Sjeff				nextport.portnum = from->drpath.p[from->drpath.cnt+1];
323219820Sjeff		}
324219820Sjeff		port = &nextport;
325219820Sjeff		if (port->state != 4)
326219820Sjeff			goto badoutport;
327219820Sjeff		node = &nextnode;
328219820Sjeff		portnum = port->portnum;
329219820Sjeff		dump_route(dump, node, outport, port);
330219820Sjeff	}
331219820Sjeff
332219820Sjeff	if (maxhops <= 0) {
333219820Sjeff		IBWARN("no route found after %d hops", MAXHOPS);
334219820Sjeff		return -1;
335219820Sjeff	}
336219820Sjeff	dump_endnode(dump, "To", node, port);
337219820Sjeff	return 0;
338219820Sjeff
339219820Sjeffbadport:
340219820Sjeff	IBWARN("Bad port state found: node \"%s\" port %d state %d",
341219820Sjeff		clean_nodedesc(node->nodedesc), portnum, port->state);
342219820Sjeff	return -1;
343219820Sjeffbadoutport:
344219820Sjeff	IBWARN("Bad out port state found: node \"%s\" outport %d state %d",
345219820Sjeff		clean_nodedesc(node->nodedesc), outport, port->state);
346219820Sjeff	return -1;
347219820Sjeffbadtbl:
348219820Sjeff	IBWARN("Bad forwarding table entry found at: node \"%s\" lid entry %d is %d (top %d)",
349219820Sjeff		clean_nodedesc(node->nodedesc), to->lid, outport, sw.linearFDBtop);
350219820Sjeff	return -1;
351219820Sjeffbadpath:
352219820Sjeff	IBWARN("Direct path too long!");
353219820Sjeff	return -1;
354219820Sjeff}
355219820Sjeff
356219820Sjeff
357219820Sjeff/**************************
358219820Sjeff * MC span part
359219820Sjeff */
360219820Sjeff
361219820Sjeff#define HASHGUID(guid)		((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103)))
362219820Sjeff#define HTSZ 137
363219820Sjeff
364219820Sjeffstatic int
365219820Sjeffinsert_node(Node *new)
366219820Sjeff{
367219820Sjeff	static Node *nodestbl[HTSZ];
368219820Sjeff	int hash = HASHGUID(new->nodeguid) % HTSZ;
369219820Sjeff	Node *node ;
370219820Sjeff
371219820Sjeff	for (node = nodestbl[hash]; node; node = node->htnext)
372219820Sjeff		if (node->nodeguid == new->nodeguid) {
373219820Sjeff			DEBUG("node %" PRIx64 " already exists", new->nodeguid);
374219820Sjeff			return -1;
375219820Sjeff		}
376219820Sjeff
377219820Sjeff	new->htnext = nodestbl[hash];
378219820Sjeff	nodestbl[hash] = new;
379219820Sjeff
380219820Sjeff	return 0;
381219820Sjeff}
382219820Sjeff
383219820Sjeffstatic int
384219820Sjeffget_port(Port *port, int portnum, ib_portid_t *portid)
385219820Sjeff{
386219820Sjeff	char portinfo[64];
387219820Sjeff	void *pi = portinfo;
388219820Sjeff
389219820Sjeff	port->portnum = portnum;
390219820Sjeff
391219820Sjeff	if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout))
392219820Sjeff		return -1;
393219820Sjeff
394219820Sjeff	mad_decode_field(pi, IB_PORT_LID_F, &port->lid);
395219820Sjeff	mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc);
396219820Sjeff	mad_decode_field(pi, IB_PORT_STATE_F, &port->state);
397219820Sjeff	mad_decode_field(pi, IB_PORT_PHYS_STATE_F, &port->physstate);
398219820Sjeff
399219820Sjeff	VERBOSE("portid %s portnum %d: lid %d state %d physstate %d",
400219820Sjeff		portid2str(portid), portnum, port->lid, port->state, port->physstate);
401219820Sjeff	return 1;
402219820Sjeff}
403219820Sjeff
404219820Sjeffstatic void
405219820Sjefflink_port(Port *port, Node *node)
406219820Sjeff{
407219820Sjeff	port->next = node->ports;
408219820Sjeff	node->ports = port;
409219820Sjeff}
410219820Sjeff
411219820Sjeffstatic int
412219820Sjeffnew_node(Node *node, Port *port, ib_portid_t *path, int dist)
413219820Sjeff{
414219820Sjeff	if (port->portguid == target_portguid) {
415219820Sjeff		node->dist = -1;		/* tag as target */
416219820Sjeff		link_port(port, node);
417219820Sjeff		dump_endnode(verbose, "found target", node, port);
418219820Sjeff		return 1;	/* found; */
419219820Sjeff	}
420219820Sjeff
421219820Sjeff	/* BFS search start with my self */
422219820Sjeff	if (insert_node(node) < 0)
423219820Sjeff		return -1;	/* known switch */
424219820Sjeff
425219820Sjeff	VERBOSE("insert dist %d node %p port %d lid %d", dist, node, port->portnum, port->lid);
426219820Sjeff
427219820Sjeff	link_port(port, node);
428219820Sjeff
429219820Sjeff	node->dist = dist;
430219820Sjeff	node->path = *path;
431219820Sjeff	node->dnext = nodesdist[dist];
432219820Sjeff	nodesdist[dist] = node;
433219820Sjeff
434219820Sjeff	return 0;
435219820Sjeff}
436219820Sjeff
437219820Sjeffstatic int
438219820Sjeffswitch_mclookup(Node *node, ib_portid_t *portid, int mlid, char *map)
439219820Sjeff{
440219820Sjeff	Switch sw;
441219820Sjeff	char mdb[64];
442219820Sjeff	void *si = sw.switchinfo;
443219820Sjeff	uint16_t *msets = (uint16_t *)mdb;
444219820Sjeff	int maxsets, block, i, set;
445219820Sjeff
446219820Sjeff	memset(map, 0, 256);
447219820Sjeff
448219820Sjeff	if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout))
449219820Sjeff		return -1;
450219820Sjeff
451219820Sjeff	mlid -= 0xc000;
452219820Sjeff
453219820Sjeff	mad_decode_field(si, IB_SW_MCAST_FDB_CAP_F, &sw.mccap);
454219820Sjeff
455219820Sjeff	if (mlid > sw.mccap)
456219820Sjeff		return -1;
457219820Sjeff
458219820Sjeff	block = mlid / 32;
459219820Sjeff	maxsets = (node->numports + 15) / 16;		/* round up */
460219820Sjeff
461219820Sjeff	for (set = 0; set < maxsets; set++) {
462219820Sjeff		if (!smp_query(mdb, portid, IB_ATTR_MULTICASTFORWTBL,
463219820Sjeff		    block | (set << 28), timeout))
464219820Sjeff			return -1;
465219820Sjeff
466219820Sjeff		for (i = 0; i < 16; i++, map++) {
467219820Sjeff			uint16_t mask = ntohs(msets[mlid % 32]);
468219820Sjeff			if (mask & (1 << i))
469219820Sjeff				*map = 1;
470219820Sjeff			else
471219820Sjeff				continue;
472219820Sjeff			VERBOSE("Switch guid 0x%" PRIx64 ": mlid 0x%x is forwarded to port %d",
473219820Sjeff				node->nodeguid, mlid + 0xc000, i + set * 16);
474219820Sjeff		}
475219820Sjeff	}
476219820Sjeff
477219820Sjeff	return 0;
478219820Sjeff}
479219820Sjeff
480219820Sjeff/*
481219820Sjeff * Return 1 if found, 0 if not, -1 on errors.
482219820Sjeff */
483219820Sjeffstatic Node *
484219820Sjefffind_mcpath(ib_portid_t *from, int mlid)
485219820Sjeff{
486219820Sjeff	Node *node, *remotenode;
487219820Sjeff	Port *port, *remoteport;
488219820Sjeff	char map[256];
489219820Sjeff	int r, i;
490219820Sjeff	int dist = 0, leafport = 0;
491219820Sjeff	ib_portid_t *path;
492219820Sjeff
493219820Sjeff	DEBUG("from %s", portid2str(from));
494219820Sjeff
495219820Sjeff	if (!(node = calloc(1, sizeof(Node))))
496219820Sjeff		IBERROR("out of memory");
497219820Sjeff
498219820Sjeff	if (!(port = calloc(1, sizeof(Port))))
499219820Sjeff		IBERROR("out of memory");
500219820Sjeff
501219820Sjeff	if (get_node(node, port, from) < 0) {
502219820Sjeff		IBWARN("can't reach node %s", portid2str(from));
503219820Sjeff		return 0;
504219820Sjeff	}
505219820Sjeff
506219820Sjeff	node->upnode = 0;		/* root */
507219820Sjeff	if ((r = new_node(node, port, from, 0)) > 0) {
508219820Sjeff		if (node->type != IB_NODE_SWITCH) {
509219820Sjeff			IBWARN("ibtracert from CA to CA is unsupported");
510219820Sjeff			return 0;	/* ibtracert from host to itself is unsupported */
511219820Sjeff		}
512219820Sjeff
513219820Sjeff		if (switch_mclookup(node, from, mlid, map) < 0 ||
514219820Sjeff		    !map[0])
515219820Sjeff			return 0;
516219820Sjeff		return node;
517219820Sjeff	}
518219820Sjeff
519219820Sjeff	for (dist = 0; dist < MAXHOPS; dist++) {
520219820Sjeff
521219820Sjeff		for (node = nodesdist[dist]; node; node = node->dnext) {
522219820Sjeff
523219820Sjeff			path = &node->path;
524219820Sjeff
525219820Sjeff			VERBOSE("dist %d node %p", dist, node);
526219820Sjeff			dump_endnode(verbose, "processing", node, node->ports);
527219820Sjeff
528219820Sjeff			memset(map, 0, sizeof(map));
529219820Sjeff
530219820Sjeff			if (node->type != IB_NODE_SWITCH) {
531219820Sjeff				if (dist)
532219820Sjeff					continue;
533219820Sjeff				leafport = path->drpath.p[path->drpath.cnt];
534219820Sjeff				map[port->portnum] = 1;
535219820Sjeff				node->upport = 0;	/* starting here */
536219820Sjeff				DEBUG("Starting from CA 0x%" PRIx64 " lid %d port %d (leafport %d)",
537219820Sjeff					node->nodeguid, port->lid, port->portnum, leafport);
538219820Sjeff			} else {	/* switch */
539219820Sjeff
540219820Sjeff				/* if starting from a leaf port fix up port (up port) */
541219820Sjeff				if (dist == 1 && leafport)
542219820Sjeff					node->upport = leafport;
543219820Sjeff
544219820Sjeff				if (switch_mclookup(node, path, mlid, map) < 0) {
545219820Sjeff					IBWARN("skipping bad Switch 0x%" PRIx64 "",
546219820Sjeff						node->nodeguid);
547219820Sjeff					continue;
548219820Sjeff				}
549219820Sjeff			}
550219820Sjeff
551219820Sjeff			for (i = 1; i <= node->numports; i++) {
552219820Sjeff				if (!map[i] || i == node->upport)
553219820Sjeff					continue;
554219820Sjeff
555219820Sjeff				if (dist == 0 && leafport) {
556219820Sjeff					if (from->drpath.cnt > 0)
557219820Sjeff						path->drpath.cnt--;
558219820Sjeff				} else {
559219820Sjeff					if (!(port = calloc(1, sizeof(Port))))
560219820Sjeff						IBERROR("out of memory");
561219820Sjeff
562219820Sjeff					if (get_port(port, i, path) < 0) {
563219820Sjeff						IBWARN("can't reach node %s port %d", portid2str(path), i);
564219820Sjeff						return 0;
565219820Sjeff					}
566219820Sjeff
567219820Sjeff					if (port->physstate != 5) {	/* LinkUP */
568219820Sjeff						free(port);
569219820Sjeff						continue;
570219820Sjeff					}
571219820Sjeff
572219820Sjeff#if 0
573219820Sjeff					link_port(port, node);
574219820Sjeff#endif
575219820Sjeff
576219820Sjeff					if (extend_dpath(&path->drpath, i) < 0)
577219820Sjeff						return 0;
578219820Sjeff				}
579219820Sjeff
580219820Sjeff				if (!(remotenode = calloc(1, sizeof(Node))))
581219820Sjeff					IBERROR("out of memory");
582219820Sjeff
583219820Sjeff				if (!(remoteport = calloc(1, sizeof(Port))))
584219820Sjeff					IBERROR("out of memory");
585219820Sjeff
586219820Sjeff				if (get_node(remotenode, remoteport, path) < 0) {
587219820Sjeff					IBWARN("NodeInfo on %s port %d failed, skipping port",
588219820Sjeff						portid2str(path), i);
589219820Sjeff					path->drpath.cnt--;	/* restore path */
590219820Sjeff					free(remotenode);
591219820Sjeff					free(remoteport);
592219820Sjeff					continue;
593219820Sjeff				}
594219820Sjeff
595219820Sjeff				remotenode->upnode = node;
596219820Sjeff				remotenode->upport = remoteport->portnum;
597219820Sjeff				remoteport->remoteport = port;
598219820Sjeff
599219820Sjeff				if ((r = new_node(remotenode, remoteport, path, dist+1)) > 0)
600219820Sjeff					return remotenode;
601219820Sjeff
602219820Sjeff				if (r == 0)
603219820Sjeff					dump_endnode(verbose, "new remote",
604219820Sjeff						remotenode, remoteport);
605219820Sjeff				else if (remotenode->type == IB_NODE_SWITCH)
606219820Sjeff					dump_endnode(2, "ERR: circle discovered at",
607219820Sjeff						remotenode, remoteport);
608219820Sjeff
609219820Sjeff				path->drpath.cnt--;	/* restore path */
610219820Sjeff			}
611219820Sjeff		}
612219820Sjeff	}
613219820Sjeff
614219820Sjeff	return 0;		/* not found */
615219820Sjeff}
616219820Sjeff
617219820Sjeffstatic uint64_t
618219820Sjefffind_target_portguid(ib_portid_t *to)
619219820Sjeff{
620219820Sjeff	Node tonode;
621219820Sjeff	Port toport;
622219820Sjeff
623219820Sjeff	if (get_node(&tonode, &toport, to) < 0) {
624219820Sjeff		IBWARN("can't find to port\n");
625219820Sjeff		return -1;
626219820Sjeff	}
627219820Sjeff
628219820Sjeff	return toport.portguid;
629219820Sjeff}
630219820Sjeff
631219820Sjeffstatic void
632219820Sjeffdump_mcpath(Node *node, int dumplevel)
633219820Sjeff{
634219820Sjeff	char *nodename = NULL;
635219820Sjeff
636219820Sjeff	if (node->upnode)
637219820Sjeff		dump_mcpath(node->upnode, dumplevel);
638219820Sjeff
639219820Sjeff	nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc);
640219820Sjeff
641219820Sjeff	if (!node->dist) {
642219820Sjeff		printf("From %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n",
643219820Sjeff			(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
644219820Sjeff			node->nodeguid, node->ports->portnum, node->ports->lid,
645219820Sjeff			node->ports->lid + (1 << node->ports->lmc) - 1,
646219820Sjeff			nodename);
647219820Sjeff		goto free_name;
648219820Sjeff	}
649219820Sjeff
650219820Sjeff	if (node->dist) {
651219820Sjeff		if (dumplevel == 1)
652219820Sjeff			printf("[%d] -> %s {0x%016" PRIx64 "}[%d]\n",
653219820Sjeff				node->ports->remoteport->portnum,
654219820Sjeff				(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
655219820Sjeff				node->nodeguid, node->upport);
656219820Sjeff		else
657219820Sjeff			printf("[%d] -> %s 0x%" PRIx64 "[%d] lid %u \"%s\"\n",
658219820Sjeff				node->ports->remoteport->portnum,
659219820Sjeff				(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
660219820Sjeff				node->nodeguid, node->upport,
661219820Sjeff				node->ports->lid, nodename);
662219820Sjeff	}
663219820Sjeff
664219820Sjeff	if (node->dist < 0)
665219820Sjeff	/* target node */
666219820Sjeff		printf("To %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n",
667219820Sjeff			(node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"),
668219820Sjeff			node->nodeguid, node->ports->portnum, node->ports->lid,
669219820Sjeff			node->ports->lid + (1 << node->ports->lmc) - 1,
670219820Sjeff			nodename);
671219820Sjeff
672219820Sjefffree_name:
673219820Sjeff	free(nodename);
674219820Sjeff}
675219820Sjeff
676219820Sjeffstatic int resolve_lid(ib_portid_t  *portid, const void *srcport)
677219820Sjeff{
678219820Sjeff	uint8_t portinfo[64];
679219820Sjeff	uint16_t lid;
680219820Sjeff
681219820Sjeff	if (!smp_query_via(portinfo, portid, IB_ATTR_PORT_INFO, 0, 0, srcport))
682219820Sjeff		return -1;
683219820Sjeff	mad_decode_field(portinfo, IB_PORT_LID_F, &lid);
684219820Sjeff
685219820Sjeff	ib_portid_set(portid, lid, 0, 0);
686219820Sjeff
687219820Sjeff	return 0;
688219820Sjeff}
689219820Sjeff
690219820Sjeffstatic void
691219820Sjeffusage(void)
692219820Sjeff{
693219820Sjeff	char *basename;
694219820Sjeff
695219820Sjeff	if (!(basename = strrchr(argv0, '/')))
696219820Sjeff		basename = argv0;
697219820Sjeff	else
698219820Sjeff		basename++;
699219820Sjeff
700219820Sjeff	fprintf(stderr, "Usage: %s [-d(ebug) -v(erbose) -D(irect) -G(uids) -n(o_info) -C ca_name -P ca_port "
701219820Sjeff			"-s smlid -t(imeout) timeout_ms -m mlid --node-name-map node-name-map ] <src-addr> <dest-addr>\n",
702219820Sjeff			basename);
703219820Sjeff	fprintf(stderr, "\n\tUnicast examples:\n");
704219820Sjeff	fprintf(stderr, "\t\t%s 4 16\t\t\t# show path between lids 4 and 16\n", basename);
705219820Sjeff	fprintf(stderr, "\t\t%s -n 4 16\t\t# same, but using simple output format\n", basename);
706219820Sjeff	fprintf(stderr, "\t\t%s -G 0x8f1040396522d 0x002c9000100d051\t# use guid addresses\n", basename);
707219820Sjeff
708219820Sjeff	fprintf(stderr, "\n\tMulticast example:\n");
709219820Sjeff	fprintf(stderr, "\t\t%s -m 0xc000 4 16\t# show multicast path of mlid 0xc000 between lids 4 and 16\n", basename);
710219820Sjeff	exit(-1);
711219820Sjeff}
712219820Sjeff
713219820Sjeffint
714219820Sjeffmain(int argc, char **argv)
715219820Sjeff{
716219820Sjeff	int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS};
717219820Sjeff	ib_portid_t my_portid = {0};
718219820Sjeff	ib_portid_t src_portid = {0};
719219820Sjeff	ib_portid_t dest_portid = {0};
720219820Sjeff	ib_portid_t *sm_id = 0, sm_portid = {0};
721219820Sjeff	int dumplevel = 2, dest_type = IB_DEST_LID, multicast = 0, mlid = 0;
722219820Sjeff	Node *endnode;
723219820Sjeff	int udebug = 0;
724219820Sjeff	char *ca = 0;
725219820Sjeff	int ca_port = 0;
726219820Sjeff
727219820Sjeff	static char const str_opts[] = "C:P:t:s:m:dvfDGnVhu";
728219820Sjeff	static const struct option long_opts[] = {
729219820Sjeff		{ "C", 1, 0, 'C'},
730219820Sjeff		{ "P", 1, 0, 'P'},
731219820Sjeff		{ "debug", 0, 0, 'd'},
732219820Sjeff		{ "verbose", 0, 0, 'v'},
733219820Sjeff		{ "force", 0, 0, 'f'},
734219820Sjeff		{ "Direct", 0, 0, 'D'},
735219820Sjeff		{ "Guids", 0, 0, 'G'},
736219820Sjeff		{ "no_info", 0, 0, 'n'},
737219820Sjeff		{ "timeout", 1, 0, 't'},
738219820Sjeff		{ "s", 1, 0, 's'},
739219820Sjeff		{ "m", 1, 0, 'm'},
740219820Sjeff		{ "Version", 0, 0, 'V'},
741219820Sjeff		{ "help", 0, 0, 'h'},
742219820Sjeff		{ "usage", 0, 0, 'u'},
743219820Sjeff		{ "node-name-map", 1, 0, 1},
744219820Sjeff		{ }
745219820Sjeff	};
746219820Sjeff
747219820Sjeff	argv0 = argv[0];
748219820Sjeff
749219820Sjeff	f = stdout;
750219820Sjeff
751219820Sjeff	while (1) {
752219820Sjeff		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
753219820Sjeff		if ( ch == -1 )
754219820Sjeff			break;
755219820Sjeff		switch(ch) {
756219820Sjeff		case 1:
757219820Sjeff			node_name_map_file = strdup(optarg);
758219820Sjeff			break;
759219820Sjeff		case 'C':
760219820Sjeff			ca = optarg;
761219820Sjeff			break;
762219820Sjeff		case 'P':
763219820Sjeff			ca_port = strtoul(optarg, 0, 0);
764219820Sjeff			break;
765219820Sjeff		case 'd':
766219820Sjeff			ibdebug++;
767219820Sjeff			madrpc_show_errors(1);
768219820Sjeff			umad_debug(udebug);
769219820Sjeff			udebug++;
770219820Sjeff			break;
771219820Sjeff		case 'D':
772219820Sjeff			dest_type = IB_DEST_DRPATH;
773219820Sjeff			break;
774219820Sjeff		case 'G':
775219820Sjeff			dest_type = IB_DEST_GUID;
776219820Sjeff			break;
777219820Sjeff		case 'm':
778219820Sjeff			multicast++;
779219820Sjeff			mlid = strtoul(optarg, 0, 0);
780219820Sjeff			break;
781219820Sjeff		case 'f':
782219820Sjeff			force++;
783219820Sjeff			break;
784219820Sjeff		case 'n':
785219820Sjeff			dumplevel = 1;
786219820Sjeff			break;
787219820Sjeff		case 's':
788219820Sjeff			if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0)
789219820Sjeff				IBERROR("can't resolve SM destination port %s", optarg);
790219820Sjeff			sm_id = &sm_portid;
791219820Sjeff			break;
792219820Sjeff		case 't':
793219820Sjeff			timeout = strtoul(optarg, 0, 0);
794219820Sjeff			madrpc_set_timeout(timeout);
795219820Sjeff			break;
796219820Sjeff		case 'v':
797219820Sjeff			madrpc_show_errors(1);
798219820Sjeff			verbose++;
799219820Sjeff			break;
800219820Sjeff		case 'V':
801219820Sjeff			fprintf(stderr, "%s %s\n", argv0, get_build_version() );
802219820Sjeff			exit(-1);
803219820Sjeff		default:
804219820Sjeff			usage();
805219820Sjeff			break;
806219820Sjeff		}
807219820Sjeff	}
808219820Sjeff	argc -= optind;
809219820Sjeff	argv += optind;
810219820Sjeff
811219820Sjeff	if (argc < 2)
812219820Sjeff		usage();
813219820Sjeff
814219820Sjeff	madrpc_init(ca, ca_port, mgmt_classes, 3);
815219820Sjeff	node_name_map = open_node_name_map(node_name_map_file);
816219820Sjeff
817219820Sjeff	if (ib_resolve_portid_str(&src_portid, argv[0], dest_type, sm_id) < 0)
818219820Sjeff		IBERROR("can't resolve source port %s", argv[0]);
819219820Sjeff
820219820Sjeff	if (ib_resolve_portid_str(&dest_portid, argv[1], dest_type, sm_id) < 0)
821219820Sjeff		IBERROR("can't resolve destination port %s", argv[1]);
822219820Sjeff
823219820Sjeff	if (dest_type == IB_DEST_DRPATH) {
824219820Sjeff		if (resolve_lid(&src_portid, NULL) < 0)
825219820Sjeff			IBERROR("cannot resolve lid for port \'%s\'",
826219820Sjeff				portid2str(&src_portid));
827219820Sjeff		if (resolve_lid(&dest_portid, NULL) < 0)
828219820Sjeff			IBERROR("cannot resolve lid for port \'%s\'",
829219820Sjeff				portid2str(&dest_portid));
830219820Sjeff	}
831219820Sjeff
832219820Sjeff	if (dest_portid.lid == 0 || src_portid.lid == 0) {
833219820Sjeff		IBWARN("bad src/dest lid");
834219820Sjeff		usage();
835219820Sjeff	}
836219820Sjeff
837219820Sjeff	if (dest_type != IB_DEST_DRPATH) {
838219820Sjeff		/* first find a direct path to the src port */
839219820Sjeff		if (find_route(&my_portid, &src_portid, 0) < 0)
840219820Sjeff			IBERROR("can't find a route to the src port");
841219820Sjeff
842219820Sjeff		src_portid = my_portid;
843219820Sjeff	}
844219820Sjeff
845219820Sjeff	if (!multicast) {
846219820Sjeff		if (find_route(&src_portid, &dest_portid, dumplevel) < 0)
847219820Sjeff			IBERROR("can't find a route from src to dest");
848219820Sjeff		exit(0);
849219820Sjeff	} else {
850219820Sjeff		if (mlid < 0xc000)
851219820Sjeff			IBWARN("invalid MLID; must be 0xc000 or larger");
852219820Sjeff	}
853219820Sjeff
854219820Sjeff	if (!(target_portguid = find_target_portguid(&dest_portid)))
855219820Sjeff		IBERROR("can't reach target lid %d", dest_portid.lid);
856219820Sjeff
857219820Sjeff	if (!(endnode = find_mcpath(&src_portid, mlid)))
858219820Sjeff		IBERROR("can't find a multicast route from src to dest");
859219820Sjeff
860219820Sjeff	/* dump multicast path */
861219820Sjeff	dump_mcpath(endnode, dumplevel);
862219820Sjeff
863219820Sjeff	close_node_name_map(node_name_map);
864219820Sjeff	exit(0);
865219820Sjeff}
866