1/*
2 * Copyright (c) 2004-2008 Voltaire Inc.  All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34#define _GNU_SOURCE
35
36#if HAVE_CONFIG_H
37#  include <config.h>
38#endif /* HAVE_CONFIG_H */
39
40#include <inttypes.h>
41#include <string.h>
42#include <errno.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <unistd.h>
46#include <stdarg.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <fcntl.h>
50#include <sys/ioctl.h>
51#include <unistd.h>
52#include <getopt.h>
53#include <endian.h>
54#include <byteswap.h>
55#include <sys/poll.h>
56#include <syslog.h>
57#include <netinet/in.h>
58
59#include <infiniband/common.h>
60#include <infiniband/mad.h>
61#include <infiniband/umad.h>
62
63#include <ibdiag_common.h>
64
65static const uint8_t  CLASS_SUBN_DIRECTED_ROUTE = 0x81;
66static const uint8_t  CLASS_SUBN_LID_ROUTE = 0x1;
67
68#define  ATTR_NODE_DESC ((uint16_t)(htons(0x10)))
69#define  ATTR_NODE_INFO ((uint16_t)(htons(0x11)))
70#define  ATTR_PORT_INFO ((uint16_t)(htons(0x15)))
71
72static int mad_agent;
73static int drmad_tid = 0x123;
74
75static int debug, verbose;
76
77char *argv0 = "smpdump";
78
79typedef struct {
80	char path[64];
81	int hop_cnt;
82} DRPath;
83
84struct drsmp {
85	uint8_t		base_version;
86	uint8_t		mgmt_class;
87	uint8_t		class_version;
88	uint8_t		method;
89	uint16_t	status;
90	uint8_t		hop_ptr;
91	uint8_t		hop_cnt;
92	uint64_t	tid;
93	uint16_t	attr_id;
94	uint16_t	resv;
95	uint32_t	attr_mod;
96	uint64_t	mkey;
97	uint16_t	dr_slid;
98	uint16_t	dr_dlid;
99	uint8_t		reserved[28];
100	uint8_t		data[64];
101	uint8_t		initial_path[64];
102	uint8_t		return_path[64];
103};
104
105void
106drsmp_get_init(void *umad, DRPath *path, int attr, int mod)
107{
108	struct drsmp *smp = (struct drsmp *)(umad_get_mad(umad));
109
110	memset(smp, 0, sizeof (*smp));
111
112	smp->base_version  = 1;
113	smp->mgmt_class    = CLASS_SUBN_DIRECTED_ROUTE;
114	smp->class_version = 1;
115
116	smp->method        = 1;
117	smp->attr_id	   = (uint16_t)htons((uint16_t)attr);
118	smp->attr_mod	   = htonl(mod);
119	smp->tid           = htonll(drmad_tid++);
120	smp->dr_slid       = 0xffff;
121	smp->dr_dlid       = 0xffff;
122
123	umad_set_addr(umad, 0xffff, 0, 0, 0);
124
125	if (path)
126		memcpy(smp->initial_path, path->path, path->hop_cnt+1);
127
128	smp->hop_cnt = path->hop_cnt;
129}
130
131void
132smp_get_init(void *umad, int lid, int attr, int mod)
133{
134	struct drsmp *smp = (struct drsmp *)(umad_get_mad(umad));
135
136	memset(smp, 0, sizeof (*smp));
137
138	smp->base_version  = 1;
139	smp->mgmt_class    = CLASS_SUBN_LID_ROUTE;
140	smp->class_version = 1;
141
142	smp->method        = 1;
143	smp->attr_id	   = (uint16_t)htons((uint16_t)attr);
144	smp->attr_mod	   = htonl(mod);
145	smp->tid           = htonll(drmad_tid++);
146
147	umad_set_addr(umad, lid, 0, 0xffff, 0);
148}
149
150void
151drsmp_set_init(void *umad, DRPath *path, int attr, int mod, void *data)
152{
153	struct drsmp *smp = (struct drsmp *)(umad_get_mad(umad));
154
155	memset(smp, 0, sizeof (*smp));
156
157	smp->method        = 2;		/* SET */
158	smp->attr_id	   = (uint16_t)htons((uint16_t)attr);
159	smp->attr_mod	   = htonl(mod);
160	smp->tid           = htonll(drmad_tid++);
161	smp->dr_slid       = 0xffff;
162	smp->dr_dlid       = 0xffff;
163
164	umad_set_addr(umad, 0xffff, 0, 0, 0);
165
166	if (path)
167		memcpy(smp->initial_path, path->path, path->hop_cnt+1);
168
169	if (data)
170		memcpy(smp->data, data, sizeof smp->data);
171
172	smp->hop_cnt = path->hop_cnt;
173}
174
175char *
176drmad_status_str(struct drsmp *drsmp)
177{
178	switch (drsmp->status) {
179	case 0:
180		return "success";
181	case ETIMEDOUT:
182		return "timeout";
183	}
184	return "unknown error";
185}
186
187int
188str2DRPath(char *str, DRPath *path)
189{
190	char *s;
191
192	path->hop_cnt = -1;
193
194	DEBUG("DR str: %s", str);
195	while (str && *str) {
196		if ((s = strchr(str, ',')))
197			*s = 0;
198		path->path[++path->hop_cnt] = atoi(str);
199		if (!s)
200			break;
201		str = s+1;
202	}
203
204#if 0
205	if (path->path[0] != 0 ||
206	   (path->hop_cnt > 0 && dev_port && path->path[1] != dev_port)) {
207		DEBUG("hop 0 != 0 or hop 1 != dev_port");
208		return -1;
209	}
210#endif
211
212	return path->hop_cnt;
213}
214
215void
216usage(void)
217{
218	fprintf(stderr, "Usage: %s [-s(ring) -D(irect) -V(ersion) -C ca_name -P ca_port -t(imeout) timeout_ms] <dlid|dr_path> <attr> [mod]\n", argv0);
219	fprintf(stderr, "\tDR examples:\n");
220	fprintf(stderr, "\t\t%s -D 0,1,2,3,5 16	# NODE DESC\n", argv0);
221	fprintf(stderr, "\t\t%s -D 0,1,2 0x15 2	# PORT INFO, port 2\n", argv0);
222	fprintf(stderr, "\n\tLID routed examples:\n");
223	fprintf(stderr, "\t\t%s 3 0x15 2	# PORT INFO, lid 3 port 2\n", argv0);
224	fprintf(stderr, "\t\t%s 0xa0 0x11	# NODE INFO, lid 0xa0\n", argv0);
225	fprintf(stderr, "\n");
226	exit(-1);
227}
228
229int
230main(int argc, char *argv[])
231{
232	int dump_char = 0, timeout_ms = 1000;
233	int dev_port = 0, mgmt_class = CLASS_SUBN_LID_ROUTE, dlid = 0;
234	char *dev_name = 0;
235	void *umad;
236	struct drsmp *smp;
237	int i, portid, mod = 0, attr;
238	DRPath path;
239	uint8_t *desc;
240	int length;
241
242	static char const str_opts[] = "C:P:t:dsDVhu";
243	static const struct option long_opts[] = {
244		{ "C", 1, 0, 'C'},
245		{ "P", 1, 0, 'P'},
246		{ "debug", 0, 0, 'd'},
247		{ "sring", 0, 0, 's'},
248		{ "Direct", 0, 0, 'D'},
249		{ "timeout", 1, 0, 't'},
250		{ "Version", 0, 0, 'V'},
251		{ "help", 0, 0, 'h'},
252		{ "usage", 0, 0, 'u'},
253		{ }
254	};
255
256	argv0 = argv[0];
257
258	while (1) {
259		int ch = getopt_long(argc, argv, str_opts, long_opts, NULL);
260		if ( ch == -1 )
261			break;
262		switch(ch) {
263		case 's':
264			dump_char++;
265			break;
266		case 'd':
267			debug++;
268			if (debug > 1)
269				umad_debug(debug-1);
270			break;
271		case 'D':
272			mgmt_class = CLASS_SUBN_DIRECTED_ROUTE;
273			break;
274		case 'C':
275			dev_name = optarg;
276			break;
277		case 'P':
278			dev_port = atoi(optarg);
279			break;
280		case 't':
281			timeout_ms = strtoul(optarg, 0, 0);
282			break;
283		case 'V':
284			fprintf(stderr, "%s %s\n", argv0, get_build_version() );
285			exit(-1);
286		default:
287			usage();
288			break;
289		}
290	}
291	argc -= optind;
292	argv += optind;
293
294	if (argc < 2)
295		usage();
296
297	if (mgmt_class == CLASS_SUBN_DIRECTED_ROUTE &&
298	    str2DRPath(strdup(argv[0]), &path) < 0)
299		IBPANIC("bad path str '%s'", argv[0]);
300
301	if (mgmt_class == CLASS_SUBN_LID_ROUTE)
302		dlid = strtoul(argv[0], 0, 0);
303
304	attr = strtoul(argv[1], 0, 0);
305	if (argc > 2)
306		mod = strtoul(argv[2], 0, 0);
307
308	if (umad_init() < 0)
309		IBPANIC("can't init UMAD library");
310
311	if ((portid = umad_open_port(dev_name, dev_port)) < 0)
312		IBPANIC("can't open UMAD port (%s:%d)", dev_name, dev_port);
313
314	if ((mad_agent = umad_register(portid, mgmt_class, 1, 0, 0)) < 0)
315		IBPANIC("Couldn't register agent for SMPs");
316
317	if (!(umad = umad_alloc(1, umad_size() + IB_MAD_SIZE)))
318		IBPANIC("can't alloc MAD");
319
320	smp = umad_get_mad(umad);
321
322	if (mgmt_class == CLASS_SUBN_DIRECTED_ROUTE)
323		drsmp_get_init(umad, &path, attr, mod);
324	else
325		smp_get_init(umad, dlid, attr, mod);
326
327	if (debug > 1)
328		xdump(stderr, "before send:\n", smp, 256);
329
330	length = IB_MAD_SIZE;
331	if (umad_send(portid, mad_agent, umad, length, timeout_ms, 0) < 0)
332		IBPANIC("send failed");
333
334	if (umad_recv(portid, umad, &length, -1) != mad_agent)
335		IBPANIC("recv error: %s", drmad_status_str(smp));
336
337	if (!dump_char) {
338		xdump(stdout, 0, smp->data, 64);
339		if (smp->status)
340			fprintf(stdout, "SMP status: 0x%x\n", ntohs(smp->status));
341		return 0;
342	}
343
344	desc = smp->data;
345	for (i = 0; i < 64; ++i) {
346		if (!desc[i])
347			break;
348		putchar(desc[i]);
349	}
350	putchar('\n');
351	if (smp->status)
352		fprintf(stdout, "SMP status: 0x%x\n", ntohs(smp->status));
353	return 0;
354}
355