1 /*
2  * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  *
32  */
33 
34 #if HAVE_CONFIG_H
35 #  include <config.h>
36 #endif				/* HAVE_CONFIG_H */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <getopt.h>
42 #include <netinet/in.h>
43 
44 #include <infiniband/umad.h>
45 #include <infiniband/mad.h>
46 
47 #include "ibdiag_common.h"
48 
49 #define IB_MLX_VENDOR_CLASS		10
50 
51 /* Vendor specific Attribute IDs */
52 #define IB_MLX_IS3_GENERAL_INFO		0x17
53 
54 #define MAX_SWITCH_PORTS         (36+1)
55 static char mtx_ports[MAX_SWITCH_PORTS] = {0};
56 static char mrx_ports[MAX_SWITCH_PORTS] = {0};
57 static char str[4096];
58 static uint8_t buf[256];
59 
60 #define ATTRID_PM_ROUTE   0xff30
61 #define ATTRID_PM_FILTER  0xff31
62 #define ATTRID_PM_PORTS   0xff32
63 #define ATTRID_LOSSY_CFG  0xff80
64 
65 enum mirror_type {
66 	MT_DISABLED        = 0,
67 	MT_MIRROR_NATIVE   = 2,
68 	MT_DROP            = 5,
69 	MT_MIRROR_ENCAP    = 6,
70 	MT_MIRROR_DROP     = 7
71 };
72 
73 enum mirror_port {
74 	MP_DISABLED          = 0,
75 	MP_MIRROR_FILTER     = 1,
76 	MP_MIRROR_ALWAYS     = 2,
77 	MP_MIRROR_FILTER_NOT = 3,
78 	MT_MIRROR_AS_RX      = 1
79 };
80 
81 #define PM_ENCAP_ETHERTYPE 0x1123
82 
83 struct ibmad_port *srcport;
84 
85 typedef struct {
86 	uint16_t hw_revision;
87 	uint16_t device_id;
88 	uint8_t reserved[24];
89 	uint32_t uptime;
90 } is3_hw_info_t;
91 
92 typedef struct {
93 	uint8_t resv1;
94 	uint8_t major;
95 	uint8_t minor;
96 	uint8_t sub_minor;
97 	uint32_t build_id;
98 	uint8_t month;
99 	uint8_t day;
100 	uint16_t year;
101 	uint16_t resv2;
102 	uint16_t hour;
103 	uint8_t psid[16];
104 	uint32_t ini_file_version;
105 } is3_fw_info_t;
106 
107 typedef struct {
108 	uint8_t resv1;
109 	uint8_t major;
110 	uint8_t minor;
111 	uint8_t sub_minor;
112 	uint8_t resv2[28];
113 } is3_sw_info_t;
114 
115 typedef struct {
116 	uint8_t reserved[8];
117 	is3_hw_info_t hw_info;
118 	is3_fw_info_t fw_info;
119 	is3_sw_info_t sw_info;
120 } is3_general_info_t;
121 
122 typedef struct {
123 	uint16_t ignore_buffer_mask;
124 	uint16_t ignore_credit_mask;
125 } lossy_config_t;
126 
127 static int mirror_query, mirror_dport, mirror_dlid, mirror_clear, mirror_sl, lossy_set;
128 static int set_mtx, set_mrx, packet_size = 0xfff;
129 
130 static int parse_ports(char *ports_str, char *ports_array)
131 {
132 	int num, i;
133 	char *str = strdup(ports_str);
134 	char *token = strtok(str, ",");
135 	for (i = 0; i < MAX_SWITCH_PORTS && token; i++) {
136 		num = strtoul(token, NULL, 0);
137 		if (num > 0 && num < MAX_SWITCH_PORTS)
138 			ports_array[num] = 1;
139 
140 		token = strtok(NULL, ",");
141 	}
142 	free(str);
143 	return 0;
144 }
145 
146 void port_mirror_route(ib_portid_t * portid, int query, int clear)
147 {
148 	int mirror_type;
149 
150 	memset(&buf, 0, sizeof(buf));
151 
152 	if (clear) {
153 		if (!smp_set_via(buf, portid, ATTRID_PM_ROUTE, 0, 0, srcport))
154 			IBEXIT("Clear port mirror route set failed");
155 		return;
156 	}
157 
158 	if (query) {
159 		if (!smp_query_via(buf, portid, ATTRID_PM_ROUTE, 0, 0, srcport))
160 			IBEXIT("Read port mirror route get failed");
161 		mad_decode_field(buf, IB_PMR_MT_F, &mirror_type);
162 		if (mirror_type == MT_MIRROR_ENCAP && mirror_dlid == 0)
163 			mad_decode_field(buf, IB_PMR_LRH_DLID_F, &mirror_dlid);
164 		if (mirror_type == MT_MIRROR_NATIVE && mirror_dport == 0)
165 			mad_decode_field(buf, IB_PMR_NM_PORT_F, &mirror_dport);
166 		goto Exit;
167 	}
168 
169 	/* Port Mirror Route */
170 	mad_set_field(buf, 0, IB_PMR_ENCAP_RAW_ETH_TYPE_F, PM_ENCAP_ETHERTYPE);
171 
172 	if (mirror_dlid == 0) {
173 		/* Can not truncate mirrored packets in local mode */
174 		mad_set_field(buf, 0, IB_PMR_MAX_MIRROR_LEN_F, 0xfff);
175 		mad_set_field(buf, 0, IB_PMR_MT_F, MT_MIRROR_NATIVE);
176 		mad_set_field(buf, 0, IB_PMR_NM_PORT_F, mirror_dport);
177 	}
178 	else { /* remote mirror */
179 		/* convert size to dwords */
180 		packet_size = packet_size / 4 + 1;
181 		mad_set_field(buf, 0, IB_PMR_MAX_MIRROR_LEN_F, packet_size);
182 		mad_set_field(buf, 0, IB_PMR_MT_F, MT_MIRROR_ENCAP);
183 		mad_set_field(buf, 0, IB_PMR_LRH_SL_F, mirror_sl);
184 		mad_set_field(buf, 0, IB_PMR_LRH_DLID_F, mirror_dlid);
185 		mad_set_field(buf, 0, IB_PMR_LRH_SLID_F, portid->lid);
186 	}
187 
188 	if (!smp_set_via(buf, portid, ATTRID_PM_ROUTE, 0, 0, srcport))
189 		IBEXIT("port mirror route set failed");
190 
191 Exit:
192 	mad_dump_portmirror_route(str, sizeof str, buf, sizeof buf);
193 	printf("Port Mirror Route\n%s", str);
194 }
195 
196 void port_mirror_ports(ib_portid_t * portid, int query, int clear)
197 {
198 	int p, rqf, tqf, rqv, tqv;
199 
200 	memset(&buf, 0, sizeof(buf));
201 
202 	if (clear) {
203 		if (!smp_set_via(buf, portid, ATTRID_PM_PORTS, 0, 0, srcport))
204 			IBEXIT("Clear port mirror ports set failed");
205 		return;
206 	}
207 
208 	if (query) {
209 		if (!smp_query_via(buf, portid, ATTRID_PM_PORTS, 0, 0, srcport))
210 			IBEXIT("Read port mirror ports get failed");
211 		goto Exit;
212 	}
213 
214 	/* Port Mirror Ports */
215 	rqf = IB_PMP_RQ_1_F;
216 	tqf = IB_PMP_TQ_1_F;
217 
218 	for (p = 1; p < MAX_SWITCH_PORTS; p++) {
219 		rqv = mrx_ports[p] ? MP_MIRROR_ALWAYS : MP_DISABLED;
220 		tqv = mtx_ports[p] ? MP_MIRROR_ALWAYS : MT_MIRROR_AS_RX;
221 		mad_set_field(buf, 0, rqf, rqv);
222 		mad_set_field(buf, 0, tqf, tqv);
223 		rqf += 2;
224 		tqf += 2;
225 	}
226 
227 	if (!smp_set_via(buf, portid, ATTRID_PM_PORTS, 0, 0, srcport))
228 		IBEXIT("port mirror ports set failed");
229 
230 Exit:
231 	mad_dump_portmirror_ports(str, sizeof str, buf, sizeof buf);
232 	printf("Port Mirror Ports\n%s", str);
233 }
234 
235 int get_out_port(ib_portid_t* portid)
236 {
237 	int block;
238 	int offset;
239 
240 	if (mirror_dlid) {
241 		block = mirror_dlid / IB_SMP_DATA_SIZE;
242 		offset = mirror_dlid - block * IB_SMP_DATA_SIZE;
243 		/* get out port from lft */
244 		if (!smp_query_via(buf, portid, IB_ATTR_LINEARFORWTBL, block, 0, srcport))
245 			IBEXIT("linear forwarding table get failed");
246 		block = mirror_dlid / IB_SMP_DATA_SIZE;
247 		offset = mirror_dlid - block * IB_SMP_DATA_SIZE;
248 		return buf[offset];
249 	}
250 	else
251 		return mirror_dport;
252 }
253 
254 int get_peer(ib_portid_t* portid, int outport, int* peerlid, int* peerport)
255 {
256 	ib_portid_t selfportid = { 0 };
257 	ib_portid_t peerportid = { 0 };
258 	int selfport = 0;
259 
260 	/* set peerportid for peer port */
261 	memcpy(&peerportid, portid, sizeof(peerportid));
262 	peerportid.drpath.cnt = 1;
263 	peerportid.drpath.p[1] = outport;
264 	if (ib_resolve_self_via(&selfportid, &selfport, 0, srcport) < 0)
265 		IBEXIT("failed to resolve self portid");
266 	peerportid.drpath.drslid = (uint16_t) selfportid.lid;
267 	peerportid.drpath.drdlid = 0xffff;
268 	if (!smp_query_via(buf, &peerportid, IB_ATTR_PORT_INFO, 0, 0, srcport))
269 		IBEXIT("get peer portinfo failed - unable to configure lossy\n");
270 
271 	mad_decode_field(buf, IB_PORT_LID_F, peerlid);
272 	mad_decode_field(buf, IB_PORT_LOCAL_PORT_F, peerport);
273 
274 	return 0;
275 }
276 
277 int get_mirror_vl(ib_portid_t* portid, int outport)
278 {
279 	ib_slvl_table_t * p_slvl_tbl;
280 	int portnum;
281 	int vl;
282 
283 	/* hack; assume all sl2vl mappings are the same for any in port and outport */
284 	portnum = (1 << 8) | outport;
285 
286 	/* get sl2vl mapping */
287 	if (!smp_query_via(buf, portid, IB_ATTR_SLVL_TABLE, portnum, 0, srcport))
288 		IBEXIT("slvl query failed");
289 
290 	p_slvl_tbl = (ib_slvl_table_t *) buf;
291 	vl = ib_slvl_table_get(p_slvl_tbl, mirror_sl);
292 	printf("mirror_sl %d, mirror_vl %d\n", mirror_sl, vl);
293 	return vl;
294 }
295 
296 int lossy_config(ib_portid_t* portid, int query, int clear)
297 {
298 	int outport;
299 	int peerport;
300 	int attr_mod;
301 	uint8_t mirror_vl;
302 	ib_portid_t peerportid = { 0 };
303 	ib_portid_t * p_portid;
304 	lossy_config_t local_lossy_cfg;
305 	lossy_config_t peer_lossy_cfg;
306 	lossy_config_t lossy_cfg;
307 
308 	outport = get_out_port(portid);
309 	if (outport == 0)
310 		IBEXIT("get_out_port failed, mirror_dlid and mirror_dport are 0");
311 
312 	get_peer(portid, outport, &peerportid.lid, &peerport);
313 
314 	printf("local lid %d / port %d\n", portid->lid, outport);
315 	printf("peer  lid %d / port %d\n", peerportid.lid, peerport);
316 
317 	mirror_vl = get_mirror_vl(portid, outport);
318 
319 	/* read local lossy configuration */
320 	if (!smp_query_via(buf, portid, ATTRID_LOSSY_CFG, outport, 0, srcport))
321 		IBEXIT("get lossy config from lid %d port %d failed - not supported\n",
322 			portid->lid, outport);
323 	memcpy(&local_lossy_cfg, buf, sizeof(local_lossy_cfg));
324 
325 	/* read peer lossy configuration */
326 	if (!smp_query_via(buf, &peerportid, ATTRID_LOSSY_CFG, peerport, 0, srcport))
327 		IBEXIT("get lossy config from lid %d port %d failed - not supported\n",
328 			peerportid.lid, peerport);
329 	memcpy(&peer_lossy_cfg, buf, sizeof(peer_lossy_cfg));
330 
331 	if (query) {
332 		printf("local port lid %d port %d ignore_buffer 0x%04x, ignore_credit 0x%04x\n",
333 			portid->lid, outport,
334 			ntohs(local_lossy_cfg.ignore_buffer_mask), ntohs(local_lossy_cfg.ignore_credit_mask));
335 		printf("peer  port lid %d port %d ignore_buffer 0x%04x, ignore_credit 0x%04x\n",
336 			peerportid.lid, peerport,
337 			ntohs(peer_lossy_cfg.ignore_buffer_mask), ntohs(peer_lossy_cfg.ignore_credit_mask));
338 		return 0;
339 	}
340 
341 	/* VLs 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1  15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 */
342 	/*                ignore Buf Overrun             ignore Credits                 */
343 	/* when mirror activated set ignore buffer overrun on peer port */
344 	/* when mirror is de-activated clear ignore credits on local port */
345 	memset(&buf, 0, sizeof(buf));
346 	if (clear) {
347 		p_portid = portid;
348 		attr_mod = outport;
349 	} else {
350 		/* set buffer overrun on peer port */
351 		p_portid = &peerportid;
352 		attr_mod = peerport;
353 		lossy_cfg.ignore_buffer_mask = htons(1<<mirror_vl);
354 		lossy_cfg.ignore_credit_mask = 0;
355 		memcpy(&buf, &lossy_cfg, sizeof(lossy_cfg));
356 	}
357 	if (!smp_set_via(buf, p_portid, ATTRID_LOSSY_CFG, attr_mod, 0, srcport))
358 		IBEXIT("%s lossy config on lid %d failed\n", clear?"clear":"set", p_portid->lid);
359 
360 	/* when mirror activated set ignore credit on local port */
361 	/* when mirror de-activated clear buffer overrun on peer */
362 	memset(&buf, 0, sizeof(buf));
363 	if (clear) {
364 		p_portid = &peerportid;
365 		attr_mod = peerport;
366 	} else {
367 		/* set ignore credit on local port */
368 		p_portid = portid;
369 		attr_mod = outport;
370 		lossy_cfg.ignore_credit_mask = htons(1<<mirror_vl);
371 		lossy_cfg.ignore_buffer_mask = 0;
372 		memcpy(&buf, &lossy_cfg, sizeof(lossy_cfg));
373 	}
374 	if (!smp_set_via(buf, p_portid, ATTRID_LOSSY_CFG, attr_mod, 0, srcport))
375 		IBEXIT("%s lossy config on lid %d failed\n", clear?"clear":"set", p_portid->lid);
376 
377 	return 0;
378 }
379 
380 int mirror_config(ib_portid_t* portid, int query, int clear)
381 {
382 	port_mirror_route(portid, query, clear);
383 	/* port_mirror_filter(portid, query, clear); */
384 	port_mirror_ports(portid, query, clear);
385 
386 	return 0;
387 }
388 
389 static int process_opt(void *context, int ch, char *optarg)
390 {
391 	switch (ch) {
392 	case 'p':
393 		mirror_dport = strtoul(optarg, NULL, 0);
394 		break;
395 	case 'S':
396 		packet_size = strtoul(optarg, NULL, 0);
397 		break;
398 	case 'l':
399 		mirror_sl = strtoul(optarg, NULL, 0);
400 		break;
401 	case 'L':
402 		mirror_dlid = strtoul(optarg, NULL, 0);
403 		break;
404 	case 'R':
405 		set_mrx = 1;
406 		if (-1 == parse_ports(optarg, mrx_ports))
407 			return -1;
408 		break;
409 	case 'T':
410 		set_mtx = 1;
411 		if (-1 == parse_ports(optarg, mtx_ports))
412 			return -1;
413 		break;
414 	case 'D':
415 		mirror_clear = 1;
416 		break;
417 	case 'Q':
418 		mirror_query = 1;
419 		break;
420 	case 'y':
421 		lossy_set = 1;
422 		break;
423 	default:
424 		return -1;
425 	}
426 	return 0;
427 }
428 
429 int main(int argc, char **argv)
430 {
431 	int mgmt_classes[4] = { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS,
432 		IB_MLX_VENDOR_CLASS
433 	};
434 	ib_portid_t portid = { 0 };
435 	int port = 0;
436 	ib_vendor_call_t call;
437 	is3_general_info_t *gi;
438 	uint32_t fw_ver;
439 	char op_str[32];
440 
441 	const struct ibdiag_opt opts[] = {
442 		{"dport", 'p', 1, "<port>", "set mirror destination port"},
443 		{"dlid", 'L', 1, "<dlid>", "set mirror destination LID"},
444 		{"sl", 'l', 1, "<sl>", "set mirror SL"},
445 		{"size", 'S', 1, "<size>", "set packet size"},
446 		{"rxports", 'R', 1, NULL, "mirror receive port list"},
447 		{"txports", 'T', 1, NULL, "mirror transmit port list"},
448 		{"clear", 'D', 0, NULL, "clear ports mirroring"},
449 		{"query", 'Q', 0, NULL, "read mirror configuration"},
450 		{"lossy", 'y', 0, NULL, "set lossy configuration on out port"},
451 		{0}
452 	};
453 
454 	char usage_args[] = "<lid>";
455 	const char *usage_examples[] = {
456 		"-R 1,2,3 -T 2,5 -l1 -L25 -S100 <lid>\t# configure mirror ports",
457 		"-D <lid> \t# clear mirror configuration",
458 		"-Q <lid>\t# read mirror configuration",
459 		NULL
460 	};
461 
462 	ibdiag_process_opts(argc, argv, NULL, "GDLs", opts, process_opt,
463 			    usage_args, usage_examples);
464 
465 	argc -= optind;
466 	argv += optind;
467 
468 	if (argc == 0)
469 		ibdiag_show_usage();
470 
471 	srcport = mad_rpc_open_port(ibd_ca, ibd_ca_port, mgmt_classes, 4);
472 	if (!srcport)
473 		IBEXIT("Failed to open '%s' port '%d'", ibd_ca, ibd_ca_port);
474 
475 	if (argc) {
476 		if (ib_resolve_portid_str_via(&portid, argv[0], ibd_dest_type,
477 					      ibd_sm_id, srcport) < 0)
478 			IBEXIT("can't resolve destination port %s", argv[0]);
479 	}
480 
481 
482 	memset(&buf, 0, sizeof(buf));
483 	memset(&call, 0, sizeof(call));
484 	call.mgmt_class = IB_MLX_VENDOR_CLASS;
485 	call.method = IB_MAD_METHOD_GET;
486 	call.timeout = ibd_timeout;
487 	call.attrid = IB_MLX_IS3_GENERAL_INFO;
488 	if (!ib_vendor_call_via(&buf, &portid, &call, srcport))
489 		IBEXIT("failed to read vendor info");
490 	gi = (is3_general_info_t *) & buf;
491 	if (ntohs(gi->hw_info.device_id) != 0x1b3)
492 		IBEXIT("device id 0x%x does not support mirroring", ntohs(gi->hw_info.device_id));
493 
494 	fw_ver = gi->fw_info.major * 100000 + gi->fw_info.minor * 1000 + gi->fw_info.sub_minor;
495 	printf("FW version %08d\n", fw_ver);
496 	if (lossy_set && fw_ver < 704000)
497 		IBEXIT("FW version %d.%d.%d does not support lossy config",
498 			gi->fw_info.major, gi->fw_info.minor, gi->fw_info.sub_minor);
499 
500 	if (ibdebug) {
501 		printf( "switch_lid = %d\n"
502 			"mirror_clear = %d\n"
503 			"mirror_dlid = %d\n"
504 			"mirror_sl = %d\n"
505 			"mirror_port = %d\n",
506 			portid.lid, mirror_clear, mirror_dlid,
507 			mirror_sl, mirror_dport);
508 
509 		for (port = 1; port < MAX_SWITCH_PORTS; port++) {
510 			if (mtx_ports[port])
511 				printf("TX:	%d\n",port);
512 			else if(mrx_ports[port])
513 				printf("RX:	%d\n",port);
514 		}
515 	}
516 
517 	if (mirror_clear)
518 		strcpy(op_str, "Clear");
519 	else if (mirror_query)
520 		strcpy(op_str, "Read");
521 	else if (!mirror_dport && !mirror_dlid)
522 		IBEXIT("Mirror remote LID and local port are zero");
523 	else if (!set_mtx && !set_mrx)
524 		IBEXIT("Mirror Rx and Tx ports not selected");
525 	else
526 		strcpy(op_str, "Set");
527 
528 	printf("\n%s Mirror Configuration\n", op_str);
529 	mirror_config(&portid, mirror_query, mirror_clear);
530 
531 	if (lossy_set) {
532 		printf("%s Lossy Configuration\n", op_str);
533 		lossy_config(&portid, mirror_query, mirror_clear);
534 	}
535 
536 	mad_rpc_close_port(srcport);
537 	exit(0);
538 }
539