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