1/*
2 * Copyright (c) 2010 Lawrence Livermore National Lab.  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#define _GNU_SOURCE
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <getopt.h>
43#include <inttypes.h>
44
45#include <infiniband/mad.h>
46#include <infiniband/ibnetdisc.h>
47
48#include "ibdiag_common.h"
49
50uint64_t switchguid_before = 0;
51uint64_t switchguid_after = 0;
52int switchguid_flag = 0;
53
54uint64_t caguid_before = 0;
55uint64_t caguid_after = 0;
56int caguid_flag = 0;
57
58uint64_t sysimgguid_before = 0;
59uint64_t sysimgguid_after = 0;
60int sysimgguid_flag = 0;
61
62uint64_t portguid_nodeguid = 0;
63uint64_t portguid_before = 0;
64uint64_t portguid_after = 0;
65int portguid_flag = 0;
66
67struct guids {
68	uint64_t searchguid;
69	int searchguid_found;
70	uint64_t before;
71	uint64_t after;
72	int found;
73};
74
75static int parse_beforeafter(char *arg, uint64_t *before, uint64_t *after)
76{
77	char *ptr;
78	char *before_str;
79	char *after_str;
80
81	ptr = strchr(optarg, ':');
82	if (!ptr || !(*(ptr + 1))) {
83		fprintf(stderr, "invalid input '%s'\n", arg);
84		return -1;
85	}
86	(*ptr) = '\0';
87	before_str = arg;
88	after_str = ptr + 1;
89
90	(*before) = strtoull(before_str, 0, 0);
91	(*after) = strtoull(after_str, 0, 0);
92	return 0;
93}
94
95static int parse_guidbeforeafter(char *arg,
96				 uint64_t *guid,
97				 uint64_t *before,
98				 uint64_t *after)
99{
100	char *ptr1;
101	char *ptr2;
102	char *guid_str;
103	char *before_str;
104	char *after_str;
105
106	ptr1 = strchr(optarg, ':');
107	if (!ptr1 || !(*(ptr1 + 1))) {
108		fprintf(stderr, "invalid input '%s'\n", arg);
109		return -1;
110	}
111	guid_str = arg;
112	before_str = ptr1 + 1;
113
114	ptr2 = strchr(before_str, ':');
115	if (!ptr2 || !(*(ptr2 + 1))) {
116		fprintf(stderr, "invalid input '%s'\n", arg);
117		return -1;
118	}
119	(*ptr1) = '\0';
120	(*ptr2) = '\0';
121	after_str = ptr2 + 1;
122
123	(*guid) = strtoull(guid_str, 0, 0);
124	(*before) = strtoull(before_str, 0, 0);
125	(*after) = strtoull(after_str, 0, 0);
126	return 0;
127}
128
129static int process_opt(void *context, int ch, char *optarg)
130{
131	switch (ch) {
132	case 1:
133		if (parse_beforeafter(optarg,
134				      &switchguid_before,
135				      &switchguid_after) < 0)
136			return -1;
137		switchguid_flag++;
138		break;
139	case 2:
140		if (parse_beforeafter(optarg,
141				      &caguid_before,
142				      &caguid_after) < 0)
143			return -1;
144		caguid_flag++;
145		break;
146	case 3:
147		if (parse_beforeafter(optarg,
148				      &sysimgguid_before,
149				      &sysimgguid_after) < 0)
150			return -1;
151		sysimgguid_flag++;
152		break;
153	case 4:
154		if (parse_guidbeforeafter(optarg,
155					  &portguid_nodeguid,
156					  &portguid_before,
157					  &portguid_after) < 0)
158			return -1;
159		portguid_flag++;
160		break;
161	default:
162		return -1;
163	}
164
165	return 0;
166}
167
168static void update_switchportguids(ibnd_node_t *node)
169{
170	ibnd_port_t *port;
171	int p;
172
173	for (p = 0; p <= node->numports; p++) {
174		port = node->ports[p];
175		if (port)
176			port->guid = node->guid;
177	}
178}
179
180static void replace_node_guid(ibnd_node_t *node, void *user_data)
181{
182	struct guids *guids;
183
184	guids = (struct guids *)user_data;
185
186	if (node->guid == guids->before) {
187
188		node->guid = guids->after;
189
190		/* port guids are identical to switch guids on
191		 * switches, so update port guids too
192		 */
193		if (node->type == IB_NODE_SWITCH)
194			update_switchportguids(node);
195
196		guids->found++;
197	}
198}
199
200static void replace_sysimgguid(ibnd_node_t *node, void *user_data)
201{
202	struct guids *guids;
203	uint64_t sysimgguid;
204
205	guids = (struct guids *)user_data;
206
207	sysimgguid = mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F);
208	if (sysimgguid == guids->before) {
209		mad_set_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F,
210				guids->after);
211		guids->found++;
212	}
213}
214
215static void replace_portguid(ibnd_node_t *node, void *user_data)
216{
217	struct guids *guids;
218
219	guids = (struct guids *)user_data;
220
221	if (node->guid != guids->searchguid)
222		return;
223
224	guids->searchguid_found++;
225
226	if (node->type == IB_NODE_SWITCH) {
227		/* port guids are identical to switch guids on
228		 * switches, so update switch guid too
229		 */
230		if (node->guid == guids->before) {
231			node->guid = guids->after;
232			update_switchportguids(node);
233			guids->found++;
234		}
235	}
236	else {
237		ibnd_port_t *port;
238		int p;
239
240		for (p = 1; p <= node->numports; p++) {
241			port = node->ports[p];
242			if (port
243			    && port->guid == guids->before) {
244				port->guid = guids->after;
245				guids->found++;
246				break;
247			}
248		}
249	}
250}
251
252int main(int argc, char **argv)
253{
254	ibnd_fabric_t *fabric = NULL;
255	char *orig_cache_file = NULL;
256	char *new_cache_file = NULL;
257	struct guids guids;
258
259	const struct ibdiag_opt opts[] = {
260		{"switchguid", 1, 1, "BEFOREGUID:AFTERGUID",
261		 "Specify before and after switchguid to edit"},
262		{"caguid", 2, 1, "BEFOREGUID:AFTERGUID",
263		 "Specify before and after caguid to edit"},
264		{"sysimgguid", 3, 1, "BEFOREGUID:AFTERGUID",
265		 "Specify before and after sysimgguid to edit"},
266		{"portguid", 4, 1, "NODEGUID:BEFOREGUID:AFTERGUID",
267		 "Specify before and after port guid to edit"},
268		{0}
269	};
270	char *usage_args = "<orig.cache> <new.cache>";
271
272	ibdiag_process_opts(argc, argv, NULL, "CDdeGKLPstvy",
273			    opts, process_opt, usage_args,
274			    NULL);
275
276	argc -= optind;
277	argv += optind;
278
279	orig_cache_file = argv[0];
280	new_cache_file = argv[1];
281
282	if (!orig_cache_file)
283		IBEXIT("original cache file not specified");
284
285	if (!new_cache_file)
286		IBEXIT("new cache file not specified");
287
288	if ((fabric = ibnd_load_fabric(orig_cache_file, 0)) == NULL)
289		IBEXIT("loading original cached fabric failed");
290
291	if (switchguid_flag) {
292		guids.before = switchguid_before;
293		guids.after = switchguid_after;
294		guids.found = 0;
295		ibnd_iter_nodes_type(fabric,
296				     replace_node_guid,
297				     IB_NODE_SWITCH,
298				     &guids);
299
300		if (!guids.found)
301			IBEXIT("switchguid = %" PRIx64 " not found",
302				switchguid_before);
303	}
304
305	if (caguid_flag) {
306		guids.before = caguid_before;
307		guids.after = caguid_after;
308		guids.found = 0;
309		ibnd_iter_nodes_type(fabric,
310				     replace_node_guid,
311				     IB_NODE_CA,
312				     &guids);
313
314		if (!guids.found)
315			IBEXIT("caguid = %" PRIx64 " not found",
316				caguid_before);
317	}
318
319	if (sysimgguid_flag) {
320		guids.before = sysimgguid_before;
321		guids.after = sysimgguid_after;
322		guids.found = 0;
323		ibnd_iter_nodes(fabric,
324				replace_sysimgguid,
325				&guids);
326
327		if (!guids.found)
328			IBEXIT("sysimgguid = %" PRIx64 " not found",
329				sysimgguid_before);
330	}
331
332	if (portguid_flag) {
333		guids.searchguid = portguid_nodeguid;
334		guids.searchguid_found = 0;
335		guids.before = portguid_before;
336		guids.after = portguid_after;
337		guids.found = 0;
338		ibnd_iter_nodes(fabric,
339				replace_portguid,
340				&guids);
341
342		if (!guids.searchguid_found)
343			IBEXIT("nodeguid = %" PRIx64 " not found",
344				portguid_nodeguid);
345
346		if (!guids.found)
347			IBEXIT("portguid = %" PRIx64 " not found",
348				portguid_before);
349	}
350
351	if (ibnd_cache_fabric(fabric, new_cache_file, 0) < 0)
352		IBEXIT("caching new cache data failed");
353
354	ibnd_destroy_fabric(fabric);
355	exit(0);
356}
357