xref: /freebsd/contrib/ofed/opensm/opensm/osm_prtn_config.c (revision 1de7b4b805ddbf2429da511c053686ac4591ed89)
1 /*
2  * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2012-2015 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 partition management configuration
38  */
39 
40 #if HAVE_CONFIG_H
41 #  include <config.h>
42 #endif				/* HAVE_CONFIG_H */
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <ctype.h>
49 
50 #include <iba/ib_types.h>
51 #include <opensm/osm_file_ids.h>
52 #define FILE_ID OSM_FILE_PRTN_CONFIG_C
53 #include <opensm/osm_base.h>
54 #include <opensm/osm_partition.h>
55 #include <opensm/osm_subnet.h>
56 #include <opensm/osm_log.h>
57 #include <arpa/inet.h>
58 #include <sys/socket.h>
59 
60 typedef enum {
61 	LIMITED,
62 	FULL,
63 	BOTH
64 } membership_t;
65 
66 const ib_gid_t osm_ipoib_broadcast_mgid = {
67 	{
68 	 0xff,			/*  multicast field */
69 	 0x12,			/*  non-permanent bit, link local scope */
70 	 0x40, 0x1b,		/*  IPv4 signature */
71 	 0xff, 0xff,		/*  16 bits of P_Key (to be filled in) */
72 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*  48 bits of zeros */
73 	 0xff, 0xff, 0xff, 0xff,	/*  32 bit IPv4 broadcast address */
74 	 },
75 };
76 
77 struct group_flags {
78 	unsigned mtu, rate, sl, scope_mask;
79 	uint32_t Q_Key;
80 	uint8_t TClass;
81 	uint32_t FlowLabel;
82 };
83 
84 struct precreate_mgroup {
85 	ib_gid_t mgid;
86 	struct group_flags flags;
87 };
88 
89 struct part_conf {
90 	osm_log_t *p_log;
91 	osm_subn_t *p_subn;
92 	osm_prtn_t *p_prtn;
93 	unsigned is_ipoib;
94 	struct group_flags flags;
95 	membership_t membership;
96 	boolean_t indx0;
97 };
98 
99 extern osm_prtn_t *osm_prtn_make_new(osm_log_t * p_log, osm_subn_t * p_subn,
100 				     const char *name, uint16_t pkey);
101 extern ib_api_status_t osm_prtn_add_all(osm_log_t * p_log, osm_subn_t * p_subn,
102 					osm_prtn_t * p, unsigned type,
103 					boolean_t full, boolean_t indx0);
104 extern ib_api_status_t osm_prtn_add_port(osm_log_t * p_log,
105 					 osm_subn_t * p_subn, osm_prtn_t * p,
106 					 ib_net64_t guid, boolean_t full,
107 					 boolean_t indx0);
108 
109 ib_api_status_t osm_prtn_add_mcgroup(osm_log_t * p_log, osm_subn_t * p_subn,
110 				     osm_prtn_t * p, uint8_t rate, uint8_t mtu,
111 				     uint8_t sl, uint8_t scope, uint32_t Q_Key,
112 				     uint8_t TClass, uint32_t FlowLabel,
113 				     const ib_gid_t *mgid);
114 
115 
116 static inline boolean_t mgid_is_broadcast(const ib_gid_t *mgid)
117 {
118 	return (memcmp(mgid, &osm_ipoib_broadcast_mgid,
119 			sizeof(osm_ipoib_broadcast_mgid)) == 0);
120 }
121 
122 static inline boolean_t mgid_is_ip(const ib_gid_t *mgid)
123 {
124 	ib_net16_t ipsig = *(ib_net16_t *)&mgid->raw[2];
125 	return (ipsig == cl_hton16(0x401b) || ipsig == cl_hton16(0x601b));
126 }
127 
128 static inline boolean_t ip_mgroup_pkey_ok(struct part_conf *conf,
129 					  struct precreate_mgroup *group)
130 {
131 	ib_net16_t mpkey = *(ib_net16_t *)&group->mgid.raw[4];
132 	char gid_str[INET6_ADDRSTRLEN];
133 
134 	if (mgid_is_broadcast(&group->mgid)
135 	    /* user requested "wild card" of pkey */
136 	    || mpkey == 0x0000
137 	    /* user was smart enough to match */
138 	    || mpkey == (conf->p_prtn->pkey | cl_hton16(0x8000)))
139 		return (TRUE);
140 
141 	OSM_LOG(conf->p_log, OSM_LOG_ERROR,
142 		"IP MC group (%s) specified with invalid pkey 0x%04x "
143 		"for partition pkey = 0x%04x (%s)\n",
144 		inet_ntop(AF_INET6, group->mgid.raw, gid_str, sizeof gid_str),
145 		cl_ntoh16(mpkey), cl_ntoh16(conf->p_prtn->pkey), conf->p_prtn->name);
146 	return (FALSE);
147 }
148 
149 static inline boolean_t ip_mgroup_rate_ok(struct part_conf *conf,
150 				struct precreate_mgroup *group)
151 {
152 	char gid_str[INET6_ADDRSTRLEN];
153 
154 	if (group->flags.rate == conf->flags.rate)
155 		return (TRUE);
156 
157 	OSM_LOG(conf->p_log, OSM_LOG_ERROR,
158 		"IP MC group (%s) specified with invalid rate (%d): "
159 		"partition pkey = 0x%04x (%s) "
160 		"[Partition broadcast group rate = %d]\n",
161 		inet_ntop(AF_INET6, group->mgid.raw, gid_str, sizeof gid_str),
162 		group->flags.rate, cl_ntoh16(conf->p_prtn->pkey),
163 		conf->p_prtn->name, conf->flags.rate);
164 	return (FALSE);
165 }
166 
167 static inline boolean_t ip_mgroup_mtu_ok(struct part_conf *conf,
168 				struct precreate_mgroup *group)
169 {
170 	char gid_str[INET6_ADDRSTRLEN];
171 
172 	if (group->flags.mtu == conf->flags.mtu)
173 		return (TRUE);
174 
175 	OSM_LOG(conf->p_log, OSM_LOG_ERROR,
176 		"IP MC group (%s) specified with invalid mtu (%d): "
177 		"partition pkey = 0x%04x (%s) "
178 		"[Partition broadcast group mtu = %d]\n",
179 		inet_ntop(AF_INET6, group->mgid.raw, gid_str, sizeof gid_str),
180 		group->flags.mtu, cl_ntoh16(conf->p_prtn->pkey),
181 		conf->p_prtn->name, conf->flags.mtu);
182 	return (FALSE);
183 }
184 
185 static void __create_mgrp(struct part_conf *conf, struct precreate_mgroup *group)
186 {
187 	unsigned int scope;
188 
189 	if (!group->flags.scope_mask) {
190 		osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
191 				     (uint8_t) group->flags.rate,
192 				     (uint8_t) group->flags.mtu,
193 				     group->flags.sl,
194 				     0,
195 				     group->flags.Q_Key,
196 				     group->flags.TClass,
197 				     group->flags.FlowLabel,
198 				     &group->mgid);
199 	} else {
200 		for (scope = 0; scope < 16; scope++) {
201 			if (((1<<scope) & group->flags.scope_mask) == 0)
202 				continue;
203 
204 			osm_prtn_add_mcgroup(conf->p_log, conf->p_subn, conf->p_prtn,
205 					     (uint8_t)group->flags.rate,
206 					     (uint8_t)group->flags.mtu,
207 					     (uint8_t)group->flags.sl,
208 					     (uint8_t)scope,
209 					     group->flags.Q_Key,
210 					     group->flags.TClass,
211 					     group->flags.FlowLabel,
212 					     &group->mgid);
213 		}
214 	}
215 }
216 
217 static int partition_create(unsigned lineno, struct part_conf *conf,
218 			    char *name, char *id, char *flag, char *flag_val)
219 {
220 	ib_net16_t pkey;
221 
222 	if (!id && name && isdigit(*name)) {
223 		id = name;
224 		name = NULL;
225 	}
226 
227 	if (id) {
228 		char *end;
229 
230 		pkey = cl_hton16((uint16_t)strtoul(id, &end, 0));
231 		if (end == id || *end)
232 			return -1;
233 	} else
234 		pkey = 0;
235 
236 	conf->p_prtn = osm_prtn_make_new(conf->p_log, conf->p_subn,
237 					 name, pkey);
238 	if (!conf->p_prtn)
239 		return -1;
240 
241 	if (!conf->p_subn->opt.qos && conf->flags.sl != OSM_DEFAULT_SL) {
242 		OSM_LOG(conf->p_log, OSM_LOG_DEBUG, "Overriding SL %d"
243 			" to default SL %d on partition %s"
244 			" as QoS is not enabled.\n",
245 			conf->flags.sl, OSM_DEFAULT_SL, name);
246 		conf->flags.sl = OSM_DEFAULT_SL;
247 	}
248 	conf->p_prtn->sl = (uint8_t) conf->flags.sl;
249 
250 	if (conf->is_ipoib) {
251 		struct precreate_mgroup broadcast_mgroup;
252 		memset(&broadcast_mgroup, 0, sizeof(broadcast_mgroup));
253 		broadcast_mgroup.mgid = osm_ipoib_broadcast_mgid;
254 		pkey = CL_HTON16(0x8000) | conf->p_prtn->pkey;
255 		memcpy(&broadcast_mgroup.mgid.raw[4], &pkey , sizeof(pkey));
256 		broadcast_mgroup.flags.mtu = conf->flags.mtu;
257 		broadcast_mgroup.flags.rate = conf->flags.rate;
258 		broadcast_mgroup.flags.sl = conf->flags.sl;
259 		broadcast_mgroup.flags.Q_Key = conf->flags.Q_Key ?
260 						conf->flags.Q_Key :
261 						OSM_IPOIB_BROADCAST_MGRP_QKEY;
262 		broadcast_mgroup.flags.TClass = conf->flags.TClass;
263 		broadcast_mgroup.flags.FlowLabel = conf->flags.FlowLabel;
264 		__create_mgrp(conf, &broadcast_mgroup);
265 	}
266 
267 	return 0;
268 }
269 
270 /* returns 1 if processed 0 if _not_ */
271 static int parse_group_flag(unsigned lineno, osm_log_t * p_log,
272 			    struct group_flags *flags,
273 			    char *flag, char *val)
274 {
275 	int rc = 0;
276 	int len = strlen(flag);
277 	if (!strncmp(flag, "mtu", len)) {
278 		rc = 1;
279 		if (!val || (flags->mtu = strtoul(val, NULL, 0)) == 0)
280 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
281 				"PARSE WARN: line %d: "
282 				"flag \'mtu\' requires valid value"
283 				" - skipped\n", lineno);
284 	} else if (!strncmp(flag, "rate", len)) {
285 		rc = 1;
286 		if (!val || (flags->rate = strtoul(val, NULL, 0)) == 0)
287 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
288 				"PARSE WARN: line %d: "
289 				"flag \'rate\' requires valid value"
290 				" - skipped\n", lineno);
291 	} else if (!strncmp(flag, "scope", len)) {
292 		unsigned int scope;
293 		rc = 1;
294 		if (!val || (scope = strtoul(val, NULL, 0)) == 0 || scope > 0xF)
295 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
296 				"PARSE WARN: line %d: "
297 				"flag \'scope\' requires valid value"
298 				" - skipped\n", lineno);
299 		else
300 			flags->scope_mask |= (1<<scope);
301 	} else if (!strncmp(flag, "Q_Key", strlen(flag))) {
302 		rc = 1;
303 		if (!val || (flags->Q_Key = strtoul(val, NULL, 0)) == 0)
304 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
305 				"PARSE WARN: line %d: "
306 				"flag \'Q_Key\' requires valid value"
307 				" - using '0'\n", lineno);
308 	} else if (!strncmp(flag, "TClass", strlen(flag))) {
309 		rc =1;
310 		if (!val || (flags->TClass = strtoul(val, NULL, 0)) == 0)
311 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
312 				"PARSE WARN: line %d: "
313 				"flag \'TClass\' requires valid value"
314 				" - using '0'\n", lineno);
315 	} else if (!strncmp(flag, "sl", len)) {
316 		unsigned sl;
317 		char *end;
318 		rc = 1;
319 
320 		if (!val || !*val || (sl = strtoul(val, &end, 0)) > 15 ||
321 		    (*end && !isspace(*end)))
322 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
323 				"PARSE WARN: line %d: "
324 				"flag \'sl\' requires valid value"
325 				" - skipped\n", lineno);
326 		else
327 			flags->sl = sl;
328 	} else if (!strncmp(flag, "FlowLabel", len)) {
329 		uint32_t FlowLabel;
330 		char *end;
331 		rc = 1;
332 
333 		if (!val || !*val ||
334 		    (FlowLabel = strtoul(val, &end, 0)) > 0xFFFFF ||
335 		    (*end && !isspace(*end)))
336 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
337 				"PARSE WARN: line %d: "
338 				"flag \'FlowLabel\' requires valid value"
339 				" - skipped\n", lineno);
340 		else
341 			flags->FlowLabel = FlowLabel;
342 	}
343 
344 	return rc;
345 }
346 
347 static int partition_add_flag(unsigned lineno, struct part_conf *conf,
348 			      char *flag, char *val)
349 {
350 	int len = strlen(flag);
351 
352 	/* ipoib gc group flags are processed here. */
353 	if (parse_group_flag(lineno, conf->p_log, &conf->flags, flag, val))
354 		return 0;
355 
356 	/* partition flags go here. */
357 	if (!strncmp(flag, "ipoib", len)) {
358 		conf->is_ipoib = 1;
359 	} else if (!strncmp(flag, "defmember", len)) {
360 		if (!val || (strncmp(val, "limited", strlen(val))
361 			     && strncmp(val, "both", strlen(val))
362 			     && strncmp(val, "full", strlen(val))))
363 			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
364 				"PARSE WARN: line %d: "
365 				"flag \'defmember\' requires valid value (limited or full or both)"
366 				" - skipped\n", lineno);
367 		else {
368 			if (!strncmp(val, "full", strlen(val)))
369 				conf->membership = FULL;
370 			else if (!strncmp(val, "both", strlen(val)))
371 				conf->membership = BOTH;
372 			else
373 				conf->membership = LIMITED;
374 		}
375 	} else if (!strcmp(flag, "indx0"))
376 		conf->indx0 = TRUE;
377 	else {
378 		OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
379 			"PARSE WARN: line %d: "
380 			"unrecognized partition flag \'%s\'"
381 			" - ignored\n", lineno, flag);
382 	}
383 	return 0;
384 }
385 static void manage_membership_change(struct part_conf *conf, osm_prtn_t * p,
386 				     unsigned type, membership_t membership,
387 				     ib_net64_t guid)
388 {
389 	cl_map_t *p_tbl;
390 	cl_map_iterator_t p_next, p_item;
391 	osm_physp_t *p_physp;
392 
393 	/* In allow_both_pkeys mode */
394 	/* if membership of the PKEY is set to FULL */
395 	/* need to clean up the part_guid_tbl table entry for this guid */
396 	/* if membership of the PKEY is set to LIMITED */
397 	/* need to clean up the full_guid_tbl table entry for this guid */
398 	/* as it could be populated because of previous definitions */
399 
400 	if (!conf->p_subn->opt.allow_both_pkeys || membership == BOTH)
401 		return;
402 
403 	switch (type){
404 	/* ALL = 0 */
405 	case 0:
406 		cl_map_remove_all(membership == LIMITED ?
407 				  &p->full_guid_tbl : &p->part_guid_tbl);
408 		break;
409 	/* specific GUID */
410 	case 0xFF:
411 		cl_map_remove(membership == LIMITED ?
412 			      &p->full_guid_tbl : &p->part_guid_tbl,
413 			      cl_hton64(guid));
414 		break;
415 
416 	case IB_NODE_TYPE_CA:
417 	case IB_NODE_TYPE_SWITCH:
418 	case IB_NODE_TYPE_ROUTER:
419 		p_tbl = (membership == LIMITED) ?
420 			 &p->full_guid_tbl : &p->part_guid_tbl;
421 
422 		p_next = cl_map_head(p_tbl);
423 		while (p_next != cl_map_end(p_tbl)) {
424 			p_item = p_next;
425 			p_next = cl_map_next(p_item);
426 			p_physp = (osm_physp_t *) cl_map_obj(p_item);
427 			if (osm_node_get_type(p_physp->p_node) == type)
428 				cl_map_remove_item(p_tbl, p_item);
429 		}
430 		break;
431 	default:
432 		break;
433 
434 	}
435 }
436 static int partition_add_all(struct part_conf *conf, osm_prtn_t * p,
437 			     unsigned type, membership_t membership)
438 {
439 	manage_membership_change(conf, p, type, membership, 0);
440 
441 	if (membership != LIMITED &&
442 	    osm_prtn_add_all(conf->p_log, conf->p_subn, p, type, TRUE, conf->indx0) != IB_SUCCESS)
443 		return -1;
444 	if ((membership == LIMITED ||
445 	     (membership == BOTH && conf->p_subn->opt.allow_both_pkeys)) &&
446 	    osm_prtn_add_all(conf->p_log, conf->p_subn, p, type, FALSE, conf->indx0) != IB_SUCCESS)
447 		return -1;
448 	return 0;
449 }
450 
451 static int partition_add_port(unsigned lineno, struct part_conf *conf,
452 			      char *name, char *flag)
453 {
454 	osm_prtn_t *p = conf->p_prtn;
455 	ib_net64_t guid;
456 	membership_t membership = conf->membership;
457 
458 	if (!name || !*name || !strncmp(name, "NONE", strlen(name)))
459 		return 0;
460 
461 	if (flag) {
462 		/* reset default membership to limited */
463 		membership = LIMITED;
464 		if (!strncmp(flag, "full", strlen(flag)))
465 			membership = FULL;
466 		else if (!strncmp(flag, "both", strlen(flag)))
467 			membership = BOTH;
468 		else if (strncmp(flag, "limited", strlen(flag))) {
469 			OSM_LOG(conf->p_log, OSM_LOG_VERBOSE,
470 				"PARSE WARN: line %d: "
471 				"unrecognized port flag \'%s\'."
472 				" Assume \'limited\'\n", lineno, flag);
473 		}
474 	}
475 
476 	if (!strncmp(name, "ALL", strlen(name)))
477 		return partition_add_all(conf, p, 0, membership);
478 	else if (!strncmp(name, "ALL_CAS", strlen(name)))
479 		return partition_add_all(conf, p, IB_NODE_TYPE_CA, membership);
480 	else if (!strncmp(name, "ALL_SWITCHES", strlen(name)))
481 		return partition_add_all(conf, p, IB_NODE_TYPE_SWITCH,
482 					 membership);
483 	else if (!strncmp(name, "ALL_ROUTERS", strlen(name)))
484 		return partition_add_all(conf, p, IB_NODE_TYPE_ROUTER,
485 					 membership);
486 	else if (!strncmp(name, "SELF", strlen(name))) {
487 		guid = cl_ntoh64(conf->p_subn->sm_port_guid);
488 	} else {
489 		char *end;
490 		guid = strtoull(name, &end, 0);
491 		if (!guid || *end)
492 			return -1;
493 	}
494 
495 	manage_membership_change(conf, p, 0xFF, membership, guid);
496 	if (membership != LIMITED &&
497 	    osm_prtn_add_port(conf->p_log, conf->p_subn, p,
498 			      cl_hton64(guid), TRUE, conf->indx0) != IB_SUCCESS)
499 		return -1;
500 	if ((membership == LIMITED ||
501 	    (membership == BOTH && conf->p_subn->opt.allow_both_pkeys)) &&
502 	    osm_prtn_add_port(conf->p_log, conf->p_subn, p,
503 			      cl_hton64(guid), FALSE, conf->indx0) != IB_SUCCESS)
504 		return -1;
505 	return 0;
506 }
507 
508 /* conf file parser */
509 
510 #define STRIP_HEAD_SPACES(p) while (*(p) == ' ' || *(p) == '\t' || \
511 		*(p) == '\n') { (p)++; }
512 #define STRIP_TAIL_SPACES(p) { char *q = (p) + strlen(p); \
513 				while ( q != (p) && ( *q == '\0' || \
514 					*q == ' ' || *q == '\t' || \
515 					*q == '\n')) { *q-- = '\0'; }; }
516 
517 static int parse_name_token(char *str, char **name, char **val)
518 {
519 	int len = 0;
520 	char *p, *q;
521 
522 	*name = *val = NULL;
523 
524 	p = str;
525 
526 	while (*p == ' ' || *p == '\t' || *p == '\n')
527 		p++;
528 
529 	q = strchr(p, '=');
530 	if (q)
531 		*q++ = '\0';
532 
533 	len = strlen(str) + 1;
534 	str = q;
535 
536 	q = p + strlen(p);
537 	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
538 		*q-- = '\0';
539 
540 	*name = p;
541 
542 	p = str;
543 	if (!p)
544 		return len;
545 
546 	while (*p == ' ' || *p == '\t' || *p == '\n')
547 		p++;
548 
549 	q = p + strlen(p);
550 	len += (int)(q - str) + 1;
551 	while (q != p && (*q == '\0' || *q == ' ' || *q == '\t' || *q == '\n'))
552 		*q-- = '\0';
553 	*val = p;
554 
555 	return len;
556 }
557 
558 static int parse_mgroup_flags(osm_log_t * p_log,
559 				struct precreate_mgroup *mgroup,
560 				char *p, unsigned lineno)
561 {
562 	int ret, len = 0;
563 	char *flag, *val, *q;
564 	do {
565 		flag = val = NULL;
566 		q = strchr(p, ',');
567 		if (q)
568 			*q++ = '\0';
569 
570 		ret = parse_name_token(p, &flag, &val);
571 
572 		if (!parse_group_flag(lineno, p_log, &mgroup->flags,
573 				     flag, val)) {
574 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
575 				"PARSE WARN: line %d: "
576 				"unrecognized mgroup flag \'%s\'"
577 				" - ignored\n", lineno, flag);
578 		}
579 		p += ret;
580 		len += ret;
581 	} while (q);
582 
583 	return (len);
584 }
585 
586 static int mgroup_create(char *p, char *mgid, unsigned lineno, struct part_conf *conf)
587 {
588 	int ret = 0;
589 	struct precreate_mgroup mgroup;
590 
591 	memset(&mgroup, 0, sizeof(mgroup));
592 
593 	if (inet_pton(AF_INET6, mgid, &mgroup.mgid) != 1
594 	    || mgroup.mgid.raw[0] != 0xff) {
595 		OSM_LOG(conf->p_log, OSM_LOG_ERROR,
596 			"PARSE ERROR partition conf file line %d: "
597 			"mgid \"%s\": gid is not multicast\n", lineno, mgid);
598 		return 0;
599 	}
600 
601 	/* inherit partition flags */
602 	mgroup.flags.mtu = conf->flags.mtu;
603 	mgroup.flags.rate = conf->flags.rate;
604 	mgroup.flags.sl = conf->flags.sl;
605 	mgroup.flags.Q_Key = conf->flags.Q_Key;
606 	mgroup.flags.FlowLabel = conf->flags.FlowLabel;
607 	mgroup.flags.scope_mask = conf->flags.scope_mask;
608 
609 	/* override with user specified flags */
610 	ret = parse_mgroup_flags(conf->p_log, &mgroup, p, lineno);
611 
612 	/* check/verify special IP group parameters */
613 	if (mgid_is_ip(&mgroup.mgid)) {
614 		ib_net16_t pkey = conf->p_prtn->pkey | cl_hton16(0x8000);
615 
616 		if (!ip_mgroup_pkey_ok(conf, &mgroup)
617 		    || !ip_mgroup_rate_ok(conf, &mgroup)
618 		    || !ip_mgroup_mtu_ok(conf, &mgroup))
619 			goto error;
620 
621 		/* set special IP settings */
622 		memcpy(&mgroup.mgid.raw[4], &pkey, sizeof(pkey));
623 
624 		if (mgroup.flags.Q_Key == 0)
625 			mgroup.flags.Q_Key = OSM_IPOIB_BROADCAST_MGRP_QKEY;
626 	}
627 
628 	/* don't create multiple copies of the group */
629 	if (osm_get_mgrp_by_mgid(conf->p_subn, &mgroup.mgid))
630 		goto error;
631 
632 	/* create the group */
633 	__create_mgrp(conf, &mgroup);
634 
635 error:
636 	return ret;
637 }
638 
639 static struct part_conf *new_part_conf(osm_log_t * p_log, osm_subn_t * p_subn)
640 {
641 	static struct part_conf part;
642 	struct part_conf *conf = &part;
643 
644 	memset(conf, 0, sizeof(*conf));
645 	conf->p_log = p_log;
646 	conf->p_subn = p_subn;
647 	conf->p_prtn = NULL;
648 	conf->is_ipoib = 0;
649 	conf->flags.sl = OSM_DEFAULT_SL;
650 	conf->flags.rate = OSM_DEFAULT_MGRP_RATE;
651 	conf->flags.mtu = OSM_DEFAULT_MGRP_MTU;
652 	conf->membership = LIMITED;
653 	conf->indx0 = FALSE;
654 	return conf;
655 }
656 
657 static int flush_part_conf(struct part_conf *conf)
658 {
659 	memset(conf, 0, sizeof(*conf));
660 	return 0;
661 }
662 
663 static int parse_part_conf(struct part_conf *conf, char *str, int lineno)
664 {
665 	int ret, len = 0;
666 	char *name, *id, *flag, *flval;
667 	char *q, *p;
668 
669 	p = str;
670 	if (*p == '\t' || *p == '\0' || *p == '\n')
671 		p++;
672 
673 	len += (int)(p - str);
674 	str = p;
675 
676 	if (conf->p_prtn)
677 		goto skip_header;
678 
679 	q = strchr(p, ':');
680 	if (!q) {
681 		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
682 			"no partition definition found\n", lineno);
683 		fprintf(stderr, "\nPARSE ERROR: line %d: "
684 			"no partition definition found\n", lineno);
685 		return -1;
686 	}
687 
688 	*q++ = '\0';
689 	str = q;
690 
691 	name = id = flag = flval = NULL;
692 
693 	q = strchr(p, ',');
694 	if (q)
695 		*q = '\0';
696 
697 	ret = parse_name_token(p, &name, &id);
698 	p += ret;
699 	len += ret;
700 
701 	while (q) {
702 		flag = flval = NULL;
703 		q = strchr(p, ',');
704 		if (q)
705 			*q++ = '\0';
706 		ret = parse_name_token(p, &flag, &flval);
707 		if (!flag) {
708 			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
709 				"PARSE ERROR: line %d: "
710 				"bad partition flags\n", lineno);
711 			fprintf(stderr, "\nPARSE ERROR: line %d: "
712 				"bad partition flags\n", lineno);
713 			return -1;
714 		}
715 		p += ret;
716 		len += ret;
717 		partition_add_flag(lineno, conf, flag, flval);
718 	}
719 
720 	if (p != str || (partition_create(lineno, conf,
721 					  name, id, flag, flval) < 0)) {
722 		OSM_LOG(conf->p_log, OSM_LOG_ERROR, "PARSE ERROR: line %d: "
723 			"bad partition definition\n", lineno);
724 		fprintf(stderr, "\nPARSE ERROR: line %d: "
725 			"bad partition definition\n", lineno);
726 		return -1;
727 	}
728 
729 skip_header:
730 	do {
731 		name = flag = NULL;
732 		q = strchr(p, ',');
733 		if (q)
734 			*q++ = '\0';
735 		ret = parse_name_token(p, &name, &flag);
736 		len += ret;
737 
738 		if (strcmp(name, "mgid") == 0) {
739 			/* parse an mgid line if specified. */
740 			len += mgroup_create(p+ret, flag, lineno, conf);
741 			goto done; /* We're done: this consumes the line */
742 		}
743 		if (partition_add_port(lineno, conf, name, flag) < 0) {
744 			OSM_LOG(conf->p_log, OSM_LOG_ERROR,
745 				"PARSE ERROR: line %d: "
746 				"bad PortGUID\n", lineno);
747 			fprintf(stderr, "PARSE ERROR: line %d: "
748 				"bad PortGUID\n", lineno);
749 			return -1;
750 		}
751 		p += ret;
752 	} while (q);
753 
754 done:
755 	return len;
756 }
757 
758 /**
759  * @return 1 on error, 0 on success
760  */
761 int osm_prtn_config_parse_file(osm_log_t * p_log, osm_subn_t * p_subn,
762 			       const char *file_name)
763 {
764 	char line[4096];
765 	struct part_conf *conf = NULL;
766 	FILE *file;
767 	int lineno;
768 	int is_parse_success;
769 
770 	line[0] = '\0';
771 	file = fopen(file_name, "r");
772 	if (!file) {
773 		OSM_LOG(p_log, OSM_LOG_VERBOSE,
774 			"Cannot open config file \'%s\': %s\n",
775 			file_name, strerror(errno));
776 		return -1;
777 	}
778 
779 	lineno = 0;
780 
781 	is_parse_success = 0;
782 
783 	while (fgets(line, sizeof(line) - 1, file) != NULL) {
784 		char *q, *p = line;
785 
786 		lineno++;
787 
788 		p = line;
789 
790 		q = strchr(p, '#');
791 		if (q)
792 			*q = '\0';
793 
794 		do {
795 			int len;
796 			while (*p == ' ' || *p == '\t' || *p == '\n')
797 				p++;
798 			if (*p == '\0')
799 				break;
800 
801 			if (!conf && !(conf = new_part_conf(p_log, p_subn))) {
802 				OSM_LOG(p_log, OSM_LOG_ERROR,
803 					"PARSE ERROR: line %d: "
804 					"internal: cannot create config\n",
805 					lineno);
806 				fprintf(stderr,
807 					"PARSE ERROR: line %d: "
808 					"internal: cannot create config\n",
809 					lineno);
810 				is_parse_success = -1;
811 				break;
812 			}
813 
814 			q = strchr(p, ';');
815 			if (q)
816 				*q = '\0';
817 
818 			len = parse_part_conf(conf, p, lineno);
819 			if (len < 0) {
820 				is_parse_success = -1;
821 				break;
822 			}
823 
824 			is_parse_success = 1;
825 
826 			p += len;
827 
828 			if (q) {
829 				flush_part_conf(conf);
830 				conf = NULL;
831 			}
832 		} while (q);
833 
834 		if (is_parse_success == -1)
835 			break;
836 	}
837 
838 	fclose(file);
839 
840 	return (is_parse_success == 1) ? 0 : 1;
841 }
842