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
55struct ibmad_port *srcport;
56
57#define MAXHOPS	63
58
59static char *node_type_str[] = {
60	"???",
61	"ca",
62	"switch",
63	"router",
64	"iwarp rnic"
65};
66
67static int timeout = 0;		/* ms */
68static int force;
69static FILE *f;
70
71static char *node_name_map_file = NULL;
72static nn_map_t *node_name_map = NULL;
73
74typedef struct Port Port;
75typedef struct Switch Switch;
76typedef struct Node Node;
77
78struct 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
90struct 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
100struct 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
115Node *nodesdist[MAXHOPS];
116uint64_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 */
126static 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
136static 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
175static 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
201static int sameport(Port * a, Port * b)
202{
203	return a->portguid == b->portguid || (force && a->lid == b->lid);
204}
205
206static 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
215static 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
241static 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
265static 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
390badport:
391	IBWARN("Bad port state found: node \"%s\" port %d state %d",
392	       clean_nodedesc(node->nodedesc), portnum, port->state);
393	return -1;
394badoutport:
395	IBWARN("Bad out port state found: node \"%s\" outport %d state %d",
396	       clean_nodedesc(node->nodedesc), outport, port->state);
397	return -1;
398badtbl:
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;
403badpath:
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
415static 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
433static 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
455static void link_port(Port * port, Node * node)
456{
457	port->next = node->ports;
458	node->ports = port;
459}
460
461static 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
487static 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 */
537static 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
679static 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
692static 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
737free_name:
738	free(nodename);
739}
740
741static 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
755static int dumplevel = 2, multicast, mlid;
756
757static 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
779int 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