1 /*
2  * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2006,2008-2009 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_file_ids.h>
53 #define FILE_ID OSM_FILE_UCAST_FILE_C
54 #include <opensm/osm_opensm.h>
55 #include <opensm/osm_switch.h>
56 #include <opensm/osm_log.h>
57 
58 static uint16_t remap_lid(osm_opensm_t * p_osm, uint16_t lid, ib_net64_t guid)
59 {
60 	osm_port_t *p_port;
61 	uint16_t min_lid, max_lid;
62 	uint8_t lmc;
63 
64 	p_port = osm_get_port_by_guid(&p_osm->subn, guid);
65 	if (!p_port) {
66 		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
67 			"cannot find port guid 0x%016" PRIx64
68 			" , will use the same lid\n", cl_ntoh64(guid));
69 		return lid;
70 	}
71 
72 	osm_port_get_lid_range_ho(p_port, &min_lid, &max_lid);
73 	if (min_lid <= lid && lid <= max_lid)
74 		return lid;
75 
76 	lmc = osm_port_get_lmc(p_port);
77 	return min_lid + (lid & ((1 << lmc) - 1));
78 }
79 
80 static void add_path(osm_opensm_t * p_osm,
81 		     osm_switch_t * p_sw, uint16_t lid, uint8_t port_num,
82 		     ib_net64_t port_guid)
83 {
84 	uint16_t new_lid;
85 	uint8_t old_port;
86 
87 	new_lid = port_guid ? remap_lid(p_osm, lid, port_guid) : lid;
88 	old_port = osm_switch_get_port_by_lid(p_sw, new_lid, OSM_LFT);
89 	if (old_port != OSM_NO_PATH && old_port != port_num) {
90 		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
91 			"LID collision is detected on switch "
92 			"0x016%" PRIx64 ", will overwrite LID %u entry\n",
93 			cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)),
94 			new_lid);
95 	}
96 
97 	p_sw->new_lft[new_lid] = port_num;
98 	if (!(p_osm->subn.opt.port_profile_switch_nodes && port_guid &&
99 	      osm_get_switch_by_guid(&p_osm->subn, port_guid)))
100 		osm_switch_count_path(p_sw, port_num);
101 
102 	OSM_LOG(&p_osm->log, OSM_LOG_DEBUG,
103 		"route 0x%04x(was 0x%04x) %u 0x%016" PRIx64
104 		" is added to switch 0x%016" PRIx64 "\n",
105 		new_lid, lid, port_num, cl_ntoh64(port_guid),
106 		cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)));
107 }
108 
109 static void add_lid_hops(osm_opensm_t * p_osm, osm_switch_t * p_sw,
110 			 uint16_t lid, ib_net64_t guid,
111 			 uint8_t hops[], unsigned len)
112 {
113 	uint8_t i;
114 
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 
122 static 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 	int status = -1;
134 
135 	file_name = p_osm->subn.opt.lfts_file;
136 	if (!file_name) {
137 		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
138 			"LFTs file name is not given; "
139 			"using default routing algorithm\n");
140 		return 1;
141 	}
142 
143 	file = fopen(file_name, "r");
144 	if (!file) {
145 		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6302: "
146 			"Can't open ucast dump file \'%s\': %m\n", file_name);
147 		goto Exit;
148 	}
149 
150 	lineno = 0;
151 	p_sw = NULL;
152 
153 	while (fgets(line, sizeof(line) - 1, file) != NULL) {
154 		char *p, *q;
155 		lineno++;
156 
157 		p = line;
158 		while (isspace(*p))
159 			p++;
160 
161 		if (*p == '#')
162 			continue;
163 
164 		if (!strncmp(p, "Multicast mlids", 15)) {
165 			OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS,
166 				"ERR 6303: "
167 				"Multicast dump file detected; "
168 				"skipping parsing. Using default "
169 				"routing algorithm\n");
170 		} else if (!strncmp(p, "Unicast lids", 12)) {
171 			q = strstr(p, " guid 0x");
172 			if (!q) {
173 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
174 					"PARSE ERROR: %s:%u: "
175 					"cannot parse switch definition\n",
176 					file_name, lineno);
177 				goto Exit;
178 			}
179 			p = q + 8;
180 			sw_guid = strtoull(p, &q, 16);
181 			if (q == p || !isspace(*q)) {
182 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
183 					"PARSE ERROR: %s:%u: "
184 					"cannot parse switch guid: \'%s\'\n",
185 					file_name, lineno, p);
186 				goto Exit;
187 			}
188 			sw_guid = cl_hton64(sw_guid);
189 
190 			p_sw = osm_get_switch_by_guid(&p_osm->subn, sw_guid);
191 			if (!p_sw) {
192 				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
193 					"cannot find switch %016" PRIx64 "\n",
194 					cl_ntoh64(sw_guid));
195 				continue;
196 			}
197 			memset(p_sw->new_lft, OSM_NO_PATH, p_sw->lft_size);
198 		} else if (p_sw && !strncmp(p, "0x", 2)) {
199 			p += 2;
200 			lid = (uint16_t) strtoul(p, &q, 16);
201 			if (q == p || !isspace(*q)) {
202 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
203 					"PARSE ERROR: %s:%u: "
204 					"cannot parse lid: \'%s\'\n",
205 					file_name, lineno, p);
206 				goto Exit;
207 			}
208 			p = q;
209 			while (isspace(*p))
210 				p++;
211 			port_num = (uint8_t) strtoul(p, &q, 10);
212 			if (q == p || !isspace(*q)) {
213 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
214 					"PARSE ERROR: %s:%u: "
215 					"cannot parse port: \'%s\'\n",
216 					file_name, lineno, p);
217 				goto Exit;
218 			}
219 			if (port_num >=
220 				osm_node_get_num_physp(p_sw->p_node)) {
221 					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
222 						"Invalid port %d found "
223 						"for switch %016" PRIx64 "\n",
224 						port_num,
225 						cl_ntoh64(osm_node_get_node_guid
226 							(p_sw->p_node)));
227 					goto Exit;
228 			}
229 
230 			p = q;
231 			/* additionally try to extract guid */
232 			q = strstr(p, " portguid 0x");
233 			if (!q) {
234 				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
235 					"PARSE WARNING: %s:%u: "
236 					"cannot find port guid "
237 					"(maybe broken dump): \'%s\'\n",
238 					file_name, lineno, p);
239 				port_guid = 0;
240 			} else {
241 				p = q + 12;
242 				port_guid = strtoull(p, &q, 16);
243 				if (q == p || (!isspace(*q) && *q != ':')) {
244 					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
245 						"PARSE WARNING: %s:%u: "
246 						"cannot parse port guid "
247 						"(maybe broken dump): \'%s\'\n",
248 						file_name, lineno, p);
249 					port_guid = 0;
250 				}
251 			}
252 			port_guid = cl_hton64(port_guid);
253 			add_path(p_osm, p_sw, lid, port_num, port_guid);
254 		}
255 	}
256 	status = 0;
257 Exit:
258 	if (file)
259 		fclose(file);
260 	return status;
261 }
262 
263 static int do_lid_matrix_file_load(void *context)
264 {
265 	char line[1024];
266 	uint8_t hops[256];
267 	char *file_name;
268 	FILE *file;
269 	ib_net64_t guid;
270 	osm_opensm_t *p_osm = context;
271 	osm_switch_t *p_sw;
272 	unsigned lineno;
273 	uint16_t lid;
274 	int status = -1;
275 
276 	file_name = p_osm->subn.opt.lid_matrix_dump_file;
277 	if (!file_name) {
278 		OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
279 			"lid matrix file name is not given; "
280 			"using default lid matrix generation algorithm\n");
281 		return 1;
282 	}
283 
284 	file = fopen(file_name, "r");
285 	if (!file) {
286 		OSM_LOG(&p_osm->log, OSM_LOG_ERROR | OSM_LOG_SYS, "ERR 6305: "
287 			"Can't open lid matrix file \'%s\': %m\n", file_name);
288 		goto Exit;
289 	}
290 
291 	lineno = 0;
292 	p_sw = NULL;
293 
294 	while (fgets(line, sizeof(line) - 1, file) != NULL) {
295 		char *p, *q;
296 		lineno++;
297 
298 		p = line;
299 		while (isspace(*p))
300 			p++;
301 
302 		if (*p == '#')
303 			continue;
304 
305 		if (!strncmp(p, "Switch", 6)) {
306 			q = strstr(p, " guid 0x");
307 			if (!q) {
308 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
309 					"PARSE ERROR: %s:%u: "
310 					"cannot parse switch definition\n",
311 					file_name, lineno);
312 				goto Exit;
313 			}
314 			p = q + 8;
315 			guid = strtoull(p, &q, 16);
316 			if (q == p || !isspace(*q)) {
317 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
318 					"PARSE ERROR: %s:%u: "
319 					"cannot parse switch guid: \'%s\'\n",
320 					file_name, lineno, p);
321 				goto Exit;
322 			}
323 			guid = cl_hton64(guid);
324 
325 			p_sw = osm_get_switch_by_guid(&p_osm->subn, guid);
326 			if (!p_sw) {
327 				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
328 					"cannot find switch %016" PRIx64 "\n",
329 					cl_ntoh64(guid));
330 				continue;
331 			}
332 		} else if (p_sw && !strncmp(p, "0x", 2)) {
333 			unsigned long num;
334 			unsigned len = 0;
335 
336 			memset(hops, 0xff, sizeof(hops));
337 
338 			p += 2;
339 			num = strtoul(p, &q, 16);
340 			if (num > 0xffff || q == p ||
341 			    (*q != ':' && !isspace(*q))) {
342 				OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
343 					"PARSE ERROR: %s:%u: "
344 					"cannot parse lid: \'%s\'\n",
345 					file_name, lineno, p);
346 				goto Exit;
347 			}
348 			/* Just checked the range, so casting is safe */
349 			lid = (uint16_t) num;
350 			p = q;
351 			while (isspace(*p) || *p == ':')
352 				p++;
353 			while (len < 256 && *p && *p != '#') {
354 				num = strtoul(p, &q, 16);
355 				if (num > 0xff || q == p) {
356 					OSM_LOG(&p_osm->log, OSM_LOG_ERROR,
357 						"PARSE ERROR: %s:%u: "
358 						"cannot parse hops number: \'%s\'\n",
359 						file_name, lineno, p);
360 					goto Exit;
361 				}
362 				/* Just checked the range, so casting is safe */
363 				hops[len++] = (uint8_t) num;
364 				p = q;
365 				while (isspace(*p))
366 					p++;
367 			}
368 			/* additionally try to extract guid */
369 			q = strstr(p, " portguid 0x");
370 			if (!q) {
371 				OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
372 					"PARSE WARNING: %s:%u: "
373 					"cannot find port guid "
374 					"(maybe broken dump): \'%s\'\n",
375 					file_name, lineno, p);
376 				guid = 0;
377 			} else {
378 				p = q + 12;
379 				guid = strtoull(p, &q, 16);
380 				if (q == p || !isspace(*q)) {
381 					OSM_LOG(&p_osm->log, OSM_LOG_VERBOSE,
382 						"PARSE WARNING: %s:%u: "
383 						"cannot parse port guid "
384 						"(maybe broken dump): \'%s\'\n",
385 						file_name, lineno, p);
386 					guid = 0;
387 				}
388 			}
389 			guid = cl_hton64(guid);
390 			add_lid_hops(p_osm, p_sw, lid, guid, hops, len);
391 		}
392 	}
393 	status = 0;
394 Exit:
395 	if (file)
396 		fclose(file);
397 	return status;
398 }
399 
400 int osm_ucast_file_setup(struct osm_routing_engine *r, osm_opensm_t *osm)
401 {
402 	r->context = osm;
403 	r->build_lid_matrices = do_lid_matrix_file_load;
404 	r->ucast_build_fwd_tables = do_ucast_file_load;
405 	return 0;
406 }
407