1/*
2 * Copyright (c) 2006,2007 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2008      Mellanox Technologies LTD. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 *
33 */
34
35/*
36 * Abstract:
37 *    Implementation of OpenSM unicast routing module which loads
38 *    routes from the dump file
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <string.h>
47#include <ctype.h>
48
49#include <iba/ib_types.h>
50#include <complib/cl_qmap.h>
51#include <complib/cl_debug.h>
52#include <opensm/osm_opensm.h>
53#include <opensm/osm_switch.h>
54#include <opensm/osm_log.h>
55
56static uint16_t remap_lid(osm_opensm_t * p_osm, uint16_t lid, ib_net64_t guid)
57{
58	osm_port_t *p_port;
59	uint16_t min_lid, max_lid;
60	uint8_t lmc;
61
62	p_port = osm_get_port_by_guid(&p_osm->subn, guid);
63	if (!p_port) {
64		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
65			"cannot find port guid 0x%016" PRIx64
66			" , will use the same lid\n", cl_ntoh64(guid));
67		return lid;
68	}
69
70	osm_port_get_lid_range_ho(p_port, &min_lid, &max_lid);
71	if (min_lid <= lid && lid <= max_lid)
72		return lid;
73
74	lmc = osm_port_get_lmc(p_port);
75	return min_lid + (lid & ((1 << lmc) - 1));
76}
77
78static void add_path(osm_opensm_t * p_osm,
79		     osm_switch_t * p_sw, uint16_t lid, uint8_t port_num,
80		     ib_net64_t port_guid)
81{
82	uint16_t new_lid;
83	uint8_t old_port;
84
85	new_lid = port_guid ? remap_lid(p_osm, lid, port_guid) : lid;
86	old_port = osm_switch_get_port_by_lid(p_sw, new_lid);
87	if (old_port != OSM_NO_PATH && old_port != port_num) {
88		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
89			"LID collision is detected on switch "
90			"0x016%" PRIx64 ", will overwrite LID %u entry\n",
91			cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)),
92			new_lid);
93	}
94
95	p_sw->new_lft[new_lid] = port_num;
96	if (!(p_osm->subn.opt.port_profile_switch_nodes && port_guid &&
97	      osm_get_switch_by_guid(&p_osm->subn, port_guid)))
98		osm_switch_count_path(p_sw, port_num);
99
100	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
101		"route 0x%04x(was 0x%04x) %u 0x%016" PRIx64
102		" is added to switch 0x%016" PRIx64 "\n",
103		new_lid, lid, port_num, cl_ntoh64(port_guid),
104		cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
105}
106
107static void add_lid_hops(osm_opensm_t * p_osm, osm_switch_t * p_sw,
108			 uint16_t lid, ib_net64_t guid,
109			 uint8_t hops[], unsigned len)
110{
111	uint16_t new_lid;
112	uint8_t i;
113
114	new_lid = guid ? remap_lid(p_osm, lid, guid) : lid;
115	if (len > p_sw->num_ports)
116		len = p_sw->num_ports;
117
118	for (i = 0; i < len; i++)
119		osm_switch_set_hops(p_sw, lid, i, hops[i]);
120}
121
122static int do_ucast_file_load(void *context)
123{
124	char line[1024];
125	char *file_name;
126	FILE *file;
127	ib_net64_t sw_guid, port_guid;
128	osm_opensm_t *p_osm = context;
129	osm_switch_t *p_sw;
130	uint16_t lid;
131	uint8_t port_num;
132	unsigned lineno;
133
134	file_name = p_osm->subn.opt.lfts_file;
135	if (!file_name) {
136		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
137			"LFTs file name is not given; "
138			"using default routing algorithm\n");
139		return 1;
140	}
141
142	file = fopen(file_name, "r");
143	if (!file) {
144		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6302: "
145			"cannot open ucast dump file \'%s\': %m\n", file_name);
146		return -1;
147	}
148
149	lineno = 0;
150	p_sw = NULL;
151
152	while (fgets(line, sizeof(line) - 1, file) != NULL) {
153		char *p, *q;
154		lineno++;
155
156		p = line;
157		while (isspace(*p))
158			p++;
159
160		if (*p == '#')
161			continue;
162
163		if (!strncmp(p, "Multicast mlids", 15)) {
164			OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS,
165				"ERR 6303: "
166				"Multicast dump file detected; "
167				"skipping parsing. Using default "
168				"routing algorithm\n");
169		} else if (!strncmp(p, "Unicast lids", 12)) {
170			if (p_sw)
171				osm_ucast_mgr_set_fwd_table(&p_osm->sm.
172							    ucast_mgr, p_sw);
173			q = strstr(p, " guid 0x");
174			if (!q) {
175				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
176					"PARSE ERROR: %s:%u: "
177					"cannot parse switch definition\n",
178					file_name, lineno);
179				return -1;
180			}
181			p = q + 8;
182			sw_guid = strtoull(p, &q, 16);
183			if (q == p || !isspace(*q)) {
184				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
185					"PARSE ERROR: %s:%u: "
186					"cannot parse switch guid: \'%s\'\n",
187					file_name, lineno, p);
188				return -1;
189			}
190			sw_guid = cl_hton64(sw_guid);
191
192			p_sw = osm_get_switch_by_guid(&p_osm->subn, sw_guid);
193			if (!p_sw) {
194				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
195					"cannot find switch %016" PRIx64 "\n",
196					cl_ntoh64(sw_guid));
197				continue;
198			}
199			memset(p_sw->new_lft, OSM_NO_PATH,
200			       IB_LID_UCAST_END_HO + 1);
201		} else if (p_sw && !strncmp(p, "0x", 2)) {
202			p += 2;
203			lid = (uint16_t) strtoul(p, &q, 16);
204			if (q == p || !isspace(*q)) {
205				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
206					"PARSE ERROR: %s:%u: "
207					"cannot parse lid: \'%s\'\n",
208					file_name, lineno, p);
209				return -1;
210			}
211			p = q;
212			while (isspace(*p))
213				p++;
214			port_num = (uint8_t) strtoul(p, &q, 10);
215			if (q == p || !isspace(*q)) {
216				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
217					"PARSE ERROR: %s:%u: "
218					"cannot parse port: \'%s\'\n",
219					file_name, lineno, p);
220				return -1;
221			}
222			p = q;
223			/* additionally try to exract guid */
224			q = strstr(p, " portguid 0x");
225			if (!q) {
226				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
227					"PARSE WARNING: %s:%u: "
228					"cannot find port guid "
229					"(maybe broken dump): \'%s\'\n",
230					file_name, lineno, p);
231				port_guid = 0;
232			} else {
233				p = q + 12;
234				port_guid = strtoull(p, &q, 16);
235				if (q == p || (!isspace(*q) && *q != ':')) {
236					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
237						"PARSE WARNING: %s:%u: "
238						"cannot parse port guid "
239						"(maybe broken dump): \'%s\'\n",
240						file_name, lineno, p);
241					port_guid = 0;
242				}
243			}
244			port_guid = cl_hton64(port_guid);
245			add_path(p_osm, p_sw, lid, port_num, port_guid);
246		}
247	}
248
249	if (p_sw)
250		osm_ucast_mgr_set_fwd_table(&p_osm->sm.ucast_mgr, p_sw);
251
252	fclose(file);
253	return 0;
254}
255
256static int do_lid_matrix_file_load(void *context)
257{
258	char line[1024];
259	uint8_t hops[256];
260	char *file_name;
261	FILE *file;
262	ib_net64_t guid;
263	osm_opensm_t *p_osm = context;
264	osm_switch_t *p_sw;
265	unsigned lineno;
266	uint16_t lid;
267
268	file_name = p_osm->subn.opt.lid_matrix_dump_file;
269	if (!file_name) {
270		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
271			"lid matrix file name is not given; "
272			"using default lid matrix generation algorithm\n");
273		return 1;
274	}
275
276	file = fopen(file_name, "r");
277	if (!file) {
278		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6305: "
279			"cannot open lid matrix file \'%s\': %m\n", file_name);
280		return -1;
281	}
282
283	lineno = 0;
284	p_sw = NULL;
285
286	while (fgets(line, sizeof(line) - 1, file) != NULL) {
287		char *p, *q;
288		lineno++;
289
290		p = line;
291		while (isspace(*p))
292			p++;
293
294		if (*p == '#')
295			continue;
296
297		if (!strncmp(p, "Switch", 6)) {
298			q = strstr(p, " guid 0x");
299			if (!q) {
300				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
301					"PARSE ERROR: %s:%u: "
302					"cannot parse switch definition\n",
303					file_name, lineno);
304				return -1;
305			}
306			p = q + 8;
307			guid = strtoull(p, &q, 16);
308			if (q == p || !isspace(*q)) {
309				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
310					"PARSE ERROR: %s:%u: "
311					"cannot parse switch guid: \'%s\'\n",
312					file_name, lineno, p);
313				return -1;
314			}
315			guid = cl_hton64(guid);
316
317			p_sw = osm_get_switch_by_guid(&p_osm->subn, guid);
318			if (!p_sw) {
319				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
320					"cannot find switch %016" PRIx64 "\n",
321					cl_ntoh64(guid));
322				continue;
323			}
324		} else if (p_sw && !strncmp(p, "0x", 2)) {
325			unsigned long num;
326			unsigned len = 0;
327
328			memset(hops, 0xff, sizeof(hops));
329
330			p += 2;
331			num = strtoul(p, &q, 16);
332			if (num > 0xffff || q == p ||
333			    (*q != ':' && !isspace(*q))) {
334				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
335					"PARSE ERROR: %s:%u: "
336					"cannot parse lid: \'%s\'\n",
337					file_name, lineno, p);
338				return -1;
339			}
340			/* Just checked the range, so casting is safe */
341			lid = (uint16_t) num;
342			p = q;
343			while (isspace(*p) || *p == ':')
344				p++;
345			while (len < 256 && *p && *p != '#') {
346				num = strtoul(p, &q, 16);
347				if (num > 0xff || q == p) {
348					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
349						"PARSE ERROR: %s:%u: "
350						"cannot parse hops number: \'%s\'\n",
351						file_name, lineno, p);
352					return -1;
353				}
354				/* Just checked the range, so casting is safe */
355				hops[len++] = (uint8_t) num;
356				p = q;
357				while (isspace(*p))
358					p++;
359			}
360			/* additionally try to extract guid */
361			q = strstr(p, " portguid 0x");
362			if (!q) {
363				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
364					"PARSE WARNING: %s:%u: "
365					"cannot find port guid "
366					"(maybe broken dump): \'%s\'\n",
367					file_name, lineno, p);
368				guid = 0;
369			} else {
370				p = q + 12;
371				guid = strtoull(p, &q, 16);
372				if (q == p || !isspace(*q)) {
373					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
374						"PARSE WARNING: %s:%u: "
375						"cannot parse port guid "
376						"(maybe broken dump): \'%s\'\n",
377						file_name, lineno, p);
378					guid = 0;
379				}
380			}
381			guid = cl_hton64(guid);
382			add_lid_hops(p_osm, p_sw, lid, guid, hops, len);
383		}
384	}
385
386	fclose(file);
387	return 0;
388}
389
390int osm_ucast_file_setup(struct osm_routing_engine *r, osm_opensm_t *osm)
391{
392	r->context = osm;
393	r->build_lid_matrices = do_lid_matrix_file_load;
394	r->ucast_build_fwd_tables = do_ucast_file_load;
395	return 0;
396}
397