1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2015 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2008 Xsigo Systems Inc.  All rights reserved.
6  * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
7  *
8  * This software is available to you under a choice of one of two
9  * licenses.  You may choose to be licensed under the terms of the GNU
10  * General Public License (GPL) Version 2, available from the file
11  * COPYING in the main directory of this source tree, or the
12  * OpenIB.org BSD license below:
13  *
14  *     Redistribution and use in source and binary forms, with or
15  *     without modification, are permitted provided that the following
16  *     conditions are met:
17  *
18  *      - Redistributions of source code must retain the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer.
21  *
22  *      - Redistributions in binary form must reproduce the above
23  *        copyright notice, this list of conditions and the following
24  *        disclaimer in the documentation and/or other materials
25  *        provided with the distribution.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34  * SOFTWARE.
35  *
36  */
37 
38 /*
39  * Abstract:
40  *    Implementation of osm_mcmr_recv_t.
41  * This object represents the MCMemberRecord Receiver object.
42  * This object is part of the opensm family of objects.
43  */
44 
45 #if HAVE_CONFIG_H
46 #  include <config.h>
47 #endif				/* HAVE_CONFIG_H */
48 
49 #include <stdlib.h>
50 #include <string.h>
51 #include <arpa/inet.h>
52 #include <sys/socket.h>
53 #include <iba/ib_types.h>
54 #include <complib/cl_qmap.h>
55 #include <complib/cl_passivelock.h>
56 #include <complib/cl_debug.h>
57 #include <complib/cl_qlist.h>
58 #include <opensm/osm_file_ids.h>
59 #define FILE_ID OSM_FILE_SA_MCMEMBER_RECORD_C
60 #include <vendor/osm_vendor_api.h>
61 #include <opensm/osm_madw.h>
62 #include <opensm/osm_log.h>
63 #include <opensm/osm_subnet.h>
64 #include <opensm/osm_mad_pool.h>
65 #include <opensm/osm_helper.h>
66 #include <opensm/osm_msgdef.h>
67 #include <opensm/osm_pkey.h>
68 #include <opensm/osm_inform.h>
69 #include <opensm/osm_sa.h>
70 
71 #define SA_MCM_RESP_SIZE SA_ITEM_RESP_SIZE(mc_rec)
72 
73 #define JOIN_MC_COMP_MASK (IB_MCR_COMPMASK_MGID | \
74 				IB_MCR_COMPMASK_PORT_GID | \
75 				IB_MCR_COMPMASK_JOIN_STATE)
76 
77 #define REQUIRED_MC_CREATE_COMP_MASK (IB_MCR_COMPMASK_MGID | \
78 					IB_MCR_COMPMASK_PORT_GID | \
79 					IB_MCR_COMPMASK_JOIN_STATE | \
80 					IB_MCR_COMPMASK_QKEY | \
81 					IB_MCR_COMPMASK_TCLASS | \
82 					IB_MCR_COMPMASK_PKEY | \
83 					IB_MCR_COMPMASK_FLOW | \
84 					IB_MCR_COMPMASK_SL)
85 
86 #define IPV4_BCAST_MGID_PREFIX CL_HTON64(0xff10401b00000000ULL)
87 #define IPV4_BCAST_MGID_INT_ID CL_HTON64(0x00000000ffffffffULL)
88 
89 static int validate_other_comp_fields(osm_log_t * p_log, ib_net64_t comp_mask,
90 				      const ib_member_rec_t * p_mcmr,
91 				      osm_mgrp_t * p_mgrp,
92 				      osm_log_level_t log_level);
93 
94 /*********************************************************************
95  Copy certain fields between two mcmember records
96  used during the process of join request to copy data from the mgrp
97  to the port record.
98 **********************************************************************/
copy_from_create_mc_rec(IN ib_member_rec_t * dest,IN const ib_member_rec_t * src)99 static void copy_from_create_mc_rec(IN ib_member_rec_t * dest,
100 				    IN const ib_member_rec_t * src)
101 {
102 	dest->qkey = src->qkey;
103 	dest->mlid = src->mlid;
104 	dest->tclass = src->tclass;
105 	dest->pkey = src->pkey;
106 	dest->sl_flow_hop = src->sl_flow_hop;
107 	dest->scope_state = ib_member_set_scope_state(src->scope_state >> 4,
108 						      dest->scope_state & 0x0F);
109 	dest->mtu = src->mtu;
110 	dest->rate = src->rate;
111 	dest->pkt_life = src->pkt_life;
112 }
113 
114 /*********************************************************************
115  Return mlid to the pool of free mlids.
116  But this implementation is not a pool - it simply scans through
117  the MGRP database for unused mlids...
118 *********************************************************************/
free_mlid(IN osm_sa_t * sa,IN uint16_t mlid)119 static void free_mlid(IN osm_sa_t * sa, IN uint16_t mlid)
120 {
121 	UNUSED_PARAM(sa);
122 	UNUSED_PARAM(mlid);
123 }
124 
125 /*********************************************************************
126  Get a new unused mlid by scanning all the used ones in the subnet.
127 **********************************************************************/
128 /* Special Case IPv6 Solicited Node Multicast (SNM) addresses */
129 /* 0xff1Z601bXXXX0000 : 0x00000001ffYYYYYY */
130 /* Where Z is the scope, XXXX is the P_Key, and
131  * YYYYYY is the last 24 bits of the port guid */
132 #define PREFIX_MASK CL_HTON64(0xff10ffff0000ffffULL)
133 #define PREFIX_SIGNATURE CL_HTON64(0xff10601b00000000ULL)
134 #define INT_ID_MASK CL_HTON64(0xfffffff1ff000000ULL)
135 #define INT_ID_SIGNATURE CL_HTON64(0x00000001ff000000ULL)
136 
compare_ipv6_snm_mgids(const void * m1,const void * m2)137 static int compare_ipv6_snm_mgids(const void *m1, const void *m2)
138 {
139 	return memcmp(m1, m2, sizeof(ib_gid_t) - 3);
140 }
141 
find_ipv6_snm_mlid(osm_subn_t * subn,ib_gid_t * mgid)142 static ib_net16_t find_ipv6_snm_mlid(osm_subn_t *subn, ib_gid_t *mgid)
143 {
144 	osm_mgrp_t *m = (osm_mgrp_t *)cl_fmap_match(&subn->mgrp_mgid_tbl, mgid,
145 						    compare_ipv6_snm_mgids);
146 	if (m != (osm_mgrp_t *)cl_fmap_end(&subn->mgrp_mgid_tbl))
147 		return m->mlid;
148 	return 0;
149 }
150 
match_ipv6_snm_mgid(ib_gid_t * mgid)151 static unsigned match_ipv6_snm_mgid(ib_gid_t * mgid)
152 {
153 	return ((mgid->unicast.prefix & PREFIX_MASK) == PREFIX_SIGNATURE &&
154 		(mgid->unicast.interface_id & INT_ID_MASK) == INT_ID_SIGNATURE);
155 }
156 
get_new_mlid(osm_sa_t * sa,ib_member_rec_t * mcmr)157 static ib_net16_t get_new_mlid(osm_sa_t * sa, ib_member_rec_t * mcmr)
158 {
159 	osm_subn_t *p_subn = sa->p_subn;
160 	ib_net16_t requested_mlid = mcmr->mlid;
161 	unsigned i, max;
162 
163 	if (requested_mlid && cl_ntoh16(requested_mlid) >= IB_LID_MCAST_START_HO
164 	    && cl_ntoh16(requested_mlid) <= p_subn->max_mcast_lid_ho
165 	    && !osm_get_mbox_by_mlid(p_subn, requested_mlid))
166 		return requested_mlid;
167 
168 	if (sa->p_subn->opt.consolidate_ipv6_snm_req
169 	    && match_ipv6_snm_mgid(&mcmr->mgid)
170 	    && (requested_mlid = find_ipv6_snm_mlid(sa->p_subn, &mcmr->mgid))) {
171 		char str[INET6_ADDRSTRLEN];
172 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
173 			"Special Case Solicited Node Mcast Join for MGID %s\n",
174 			inet_ntop(AF_INET6, mcmr->mgid.raw, str, sizeof(str)));
175 		return requested_mlid;
176 	}
177 
178 	max = p_subn->max_mcast_lid_ho - IB_LID_MCAST_START_HO + 1;
179 	for (i = 0; i < max; i++)
180 		if (!sa->p_subn->mboxes[i])
181 			return cl_hton16(i + IB_LID_MCAST_START_HO);
182 
183 	return 0;
184 }
185 
check_join_comp_mask(ib_net64_t comp_mask)186 static inline boolean_t check_join_comp_mask(ib_net64_t comp_mask)
187 {
188 	return ((comp_mask & JOIN_MC_COMP_MASK) == JOIN_MC_COMP_MASK);
189 }
190 
check_create_comp_mask(ib_net64_t comp_mask,ib_member_rec_t * p_recvd_mcmember_rec)191 static boolean_t check_create_comp_mask(ib_net64_t comp_mask,
192 					ib_member_rec_t * p_recvd_mcmember_rec)
193 {
194 	return ((comp_mask & REQUIRED_MC_CREATE_COMP_MASK) ==
195 		REQUIRED_MC_CREATE_COMP_MASK);
196 }
197 
198 /**********************************************************************
199  Generate the response MAD
200 **********************************************************************/
mcmr_rcv_respond(IN osm_sa_t * sa,IN osm_madw_t * p_madw,IN ib_member_rec_t * p_mcmember_rec)201 static void mcmr_rcv_respond(IN osm_sa_t * sa, IN osm_madw_t * p_madw,
202 			     IN ib_member_rec_t * p_mcmember_rec)
203 {
204 	cl_qlist_t rec_list;
205 	osm_sa_item_t *item;
206 
207 	OSM_LOG_ENTER(sa->p_log);
208 
209 	item = malloc(SA_MCM_RESP_SIZE);
210 	if (!item) {
211 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B16: "
212 			"rec_item alloc failed\n");
213 		goto Exit;
214 	}
215 
216 	item->resp.mc_rec = *p_mcmember_rec;
217 
218 	/* Fill in the mtu, rate, and packet lifetime selectors */
219 	item->resp.mc_rec.mtu &= 0x3f;
220 	item->resp.mc_rec.mtu |= IB_PATH_SELECTOR_EXACTLY << 6;
221 	item->resp.mc_rec.rate &= 0x3f;
222 	item->resp.mc_rec.rate |= IB_PATH_SELECTOR_EXACTLY << 6;
223 	item->resp.mc_rec.pkt_life &= 0x3f;
224 	item->resp.mc_rec.pkt_life |= IB_PATH_SELECTOR_EXACTLY << 6;
225 
226 	cl_qlist_init(&rec_list);
227 	cl_qlist_insert_tail(&rec_list, &item->list_item);
228 
229 	osm_sa_respond(sa, p_madw, sizeof(ib_member_rec_t), &rec_list);
230 
231 Exit:
232 	OSM_LOG_EXIT(sa->p_log);
233 }
234 
235 /*********************************************************************
236  In joining an existing group, or when querying the mc groups,
237  we make sure the following components provided match: MTU and RATE
238  HACK: Currently we ignore the PKT_LIFETIME field.
239 **********************************************************************/
validate_more_comp_fields(osm_log_t * p_log,const osm_mgrp_t * p_mgrp,const ib_member_rec_t * p_recvd_mcmember_rec,ib_net64_t comp_mask)240 static boolean_t validate_more_comp_fields(osm_log_t * p_log,
241 					   const osm_mgrp_t * p_mgrp,
242 					   const ib_member_rec_t *
243 					   p_recvd_mcmember_rec,
244 					   ib_net64_t comp_mask)
245 {
246 	uint8_t mtu_sel;
247 	uint8_t mtu_required;
248 	uint8_t mtu_mgrp;
249 	uint8_t rate_sel;
250 	uint8_t rate_required;
251 	uint8_t rate_mgrp;
252 
253 	if (comp_mask & IB_MCR_COMPMASK_MTU_SEL) {
254 		mtu_sel = (uint8_t) (p_recvd_mcmember_rec->mtu >> 6);
255 		/* Clearing last 2 bits */
256 		mtu_required = (uint8_t) (p_recvd_mcmember_rec->mtu & 0x3F);
257 		mtu_mgrp = (uint8_t) (p_mgrp->mcmember_rec.mtu & 0x3F);
258 		switch (mtu_sel) {
259 		case 0:	/* Greater than MTU specified */
260 			if (mtu_mgrp <= mtu_required) {
261 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
262 					"Requested mcast group has MTU %x, "
263 					"which is not greater than %x\n",
264 					mtu_mgrp, mtu_required);
265 				return FALSE;
266 			}
267 			break;
268 		case 1:	/* Less than MTU specified */
269 			if (mtu_mgrp >= mtu_required) {
270 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
271 					"Requested mcast group has MTU %x, "
272 					"which is not less than %x\n",
273 					mtu_mgrp, mtu_required);
274 				return FALSE;
275 			}
276 			break;
277 		case 2:	/* Exactly MTU specified */
278 			if (mtu_mgrp != mtu_required) {
279 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
280 					"Requested mcast group has MTU %x, "
281 					"which is not equal to %x\n",
282 					mtu_mgrp, mtu_required);
283 				return FALSE;
284 			}
285 			break;
286 		default:
287 			break;
288 		}
289 	}
290 
291 	/* what about rate ? */
292 	if (comp_mask & IB_MCR_COMPMASK_RATE_SEL) {
293 		rate_sel = (uint8_t) (p_recvd_mcmember_rec->rate >> 6);
294 		/* Clearing last 2 bits */
295 		rate_required = (uint8_t) (p_recvd_mcmember_rec->rate & 0x3F);
296 		rate_mgrp = (uint8_t) (p_mgrp->mcmember_rec.rate & 0x3F);
297 		switch (rate_sel) {
298 		case 0:	/* Greater than RATE specified */
299 			if (ib_path_compare_rates(rate_mgrp, rate_required) <= 0) {
300 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
301 					"Requested mcast group has RATE %x, "
302 					"which is not greater than %x\n",
303 					rate_mgrp, rate_required);
304 				return FALSE;
305 			}
306 			break;
307 		case 1:	/* Less than RATE specified */
308 			if (ib_path_compare_rates(rate_mgrp, rate_required) >= 0) {
309 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
310 					"Requested mcast group has RATE %x, "
311 					"which is not less than %x\n",
312 					rate_mgrp, rate_required);
313 				return FALSE;
314 			}
315 			break;
316 		case 2:	/* Exactly RATE specified */
317 			if (ib_path_compare_rates(rate_mgrp, rate_required)) {
318 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
319 					"Requested mcast group has RATE %x, "
320 					"which is not equal to %x\n",
321 					rate_mgrp, rate_required);
322 				return FALSE;
323 			}
324 			break;
325 		default:
326 			break;
327 		}
328 	}
329 
330 	return TRUE;
331 }
332 
333 /*********************************************************************
334  In joining an existing group, we make sure the following components
335  are physically realizable: MTU and RATE
336 **********************************************************************/
validate_port_caps(osm_log_t * p_log,const osm_mgrp_t * p_mgrp,const osm_physp_t * p_physp)337 static boolean_t validate_port_caps(osm_log_t * p_log,
338 				    const osm_mgrp_t * p_mgrp,
339 				    const osm_physp_t * p_physp)
340 {
341 	const ib_port_info_t *p_pi;
342 	uint8_t mtu_required;
343 	uint8_t mtu_mgrp;
344 	uint8_t rate_required;
345 	uint8_t rate_mgrp;
346 	int extended;
347 
348 	mtu_required = ib_port_info_get_neighbor_mtu(&p_physp->port_info);
349 	mtu_mgrp = (uint8_t) (p_mgrp->mcmember_rec.mtu & 0x3F);
350 	if (mtu_required < mtu_mgrp) {
351 		OSM_LOG(p_log, OSM_LOG_VERBOSE,
352 			"Port's MTU %x is less than %x\n",
353 			mtu_required, mtu_mgrp);
354 		return FALSE;
355 	}
356 
357 	p_pi = &p_physp->port_info;
358 	extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
359 	rate_required = ib_port_info_compute_rate(p_pi, extended);
360 	rate_mgrp = (uint8_t) (p_mgrp->mcmember_rec.rate & 0x3F);
361 	if (ib_path_compare_rates(rate_required, rate_mgrp) < 0) {
362 		OSM_LOG(p_log, OSM_LOG_VERBOSE,
363 			"Port's RATE %x is less than %x\n",
364 			rate_required, rate_mgrp);
365 		return FALSE;
366 	}
367 
368 	return TRUE;
369 }
370 
371 /**********************************************************************
372  * o15-0.2.1: If SA supports UD multicast, then if SA receives a SubnAdmSet()
373  * or SubnAdmDelete() method that would modify an existing
374  * MCMemberRecord, SA shall not modify that MCMemberRecord and shall
375  * return an error status of ERR_REQ_INVALID in response in the
376  * following cases:
377  * 1. Saved MCMemberRecord.ProxyJoin is not set and the request is
378  * issued by a requester with a GID other than the Port-GID.
379  * 2. Saved MCMemberRecord.ProxyJoin is set and the requester is not
380  * part of the partition for that MCMemberRecord.
381  **********************************************************************/
validate_modify(IN osm_sa_t * sa,IN osm_mgrp_t * p_mgrp,IN osm_mad_addr_t * p_mad_addr,IN ib_member_rec_t * p_recvd_mcmember_rec,OUT osm_mcm_alias_guid_t ** pp_mcm_alias_guid)382 static boolean_t validate_modify(IN osm_sa_t * sa, IN osm_mgrp_t * p_mgrp,
383 				 IN osm_mad_addr_t * p_mad_addr,
384 				 IN ib_member_rec_t * p_recvd_mcmember_rec,
385 				 OUT osm_mcm_alias_guid_t ** pp_mcm_alias_guid)
386 {
387 	ib_net64_t portguid;
388 	ib_gid_t request_gid;
389 	osm_physp_t *p_request_physp;
390 	ib_api_status_t res;
391 
392 	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
393 
394 	*pp_mcm_alias_guid = osm_mgrp_get_mcm_alias_guid(p_mgrp, portguid);
395 
396 	/* o15-0.2.1: If this is a new port being added - nothing to check */
397 	if (!*pp_mcm_alias_guid) {
398 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
399 			"This is a new port in the MC group\n");
400 		return TRUE;
401 	}
402 
403 	/* We validate the request according the the proxy_join.
404 	   Check if the proxy_join is set or not */
405 	if ((*pp_mcm_alias_guid)->proxy_join == FALSE) {
406 		/* The proxy_join is not set. Modifying can by done only
407 		   if the requester GID == PortGID */
408 		res = osm_get_gid_by_mad_addr(sa->p_log, sa->p_subn, p_mad_addr,
409 					      &request_gid);
410 		if (res != IB_SUCCESS) {
411 			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
412 				"Could not find port for requested address\n");
413 			return FALSE;
414 		}
415 
416 		if ((*pp_mcm_alias_guid)->p_base_mcm_port->port->guid !=
417 		    request_gid.unicast.interface_id ||
418 		    (*pp_mcm_alias_guid)->port_gid.unicast.prefix !=
419 		    request_gid.unicast.prefix) {
420 			ib_gid_t base_port_gid;
421 			char gid_str[INET6_ADDRSTRLEN];
422 			char gid_str2[INET6_ADDRSTRLEN];
423 
424 			base_port_gid.unicast.prefix = (*pp_mcm_alias_guid)->port_gid.unicast.prefix;
425 			base_port_gid.unicast.interface_id = (*pp_mcm_alias_guid)->p_base_mcm_port->port->guid;
426 			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
427 				"No ProxyJoin but different ports: stored:"
428 				"%s request:%s\n",
429 				inet_ntop(AF_INET6, base_port_gid.raw, gid_str,
430 					  sizeof gid_str),
431 				inet_ntop(AF_INET6, request_gid.raw, gid_str2,
432 					  sizeof gid_str2));
433 			return FALSE;
434 		}
435 	} else {
436 		/* The proxy_join is set. Modification allowed only if the
437 		   requester is part of the partition for this MCMemberRecord */
438 		p_request_physp = osm_get_physp_by_mad_addr(sa->p_log,
439 							    sa->p_subn,
440 							    p_mad_addr);
441 		if (p_request_physp == NULL)
442 			return FALSE;
443 
444 		if (!osm_physp_has_pkey(sa->p_log, p_mgrp->mcmember_rec.pkey,
445 					p_request_physp)) {
446 			/* the request port is not part of the partition for this mgrp */
447 			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
448 				"Requesting port 0x%016" PRIx64 " has no PKey 0x%04x\n",
449 				cl_ntoh64(p_request_physp->port_guid),
450 				cl_ntoh16(p_mgrp->mcmember_rec.pkey));
451 			return FALSE;
452 		}
453 	}
454 	return TRUE;
455 }
456 
457 /*
458  * Check legality of the requested MGID DELETE
459  * o15-0.1.14 = VALID DELETE:
460  * To be a valid delete MAD needs to:
461  * 1 the MADs PortGID and MGID components match the PortGID and
462  *   MGID of a stored MCMemberRecord;
463  * 2 the MADs JoinState component contains at least one bit set to 1
464  *   in the same position as that stored MCMemberRecords JoinState
465  *   has a bit set to 1,
466  *   i.e., the logical AND of the two JoinState components
467  *   is not all zeros;
468  * 3 the MADs JoinState component does not have some bits set
469  *   which are not set in the stored MCMemberRecords JoinState component;
470  * 4 either the stored MCMemberRecord:ProxyJoin is reset (0), and the
471  *   MADs source is the stored PortGID;
472  *   OR
473  *   the stored MCMemberRecord:ProxyJoin is set (1), (see o15-
474  *   0.1.2:); and the MADs source is a member of the partition indicated
475  *   by the stored MCMemberRecord:P_Key.
476  */
validate_delete(IN osm_sa_t * sa,IN osm_mgrp_t * p_mgrp,IN osm_mad_addr_t * p_mad_addr,IN ib_member_rec_t * p_recvd_mcmember_rec,OUT osm_mcm_alias_guid_t ** pp_mcm_alias_guid)477 static boolean_t validate_delete(IN osm_sa_t * sa, IN osm_mgrp_t * p_mgrp,
478 				 IN osm_mad_addr_t * p_mad_addr,
479 				 IN ib_member_rec_t * p_recvd_mcmember_rec,
480 				 OUT osm_mcm_alias_guid_t ** pp_mcm_alias_guid)
481 {
482 	ib_net64_t portguid;
483 
484 	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
485 
486 	*pp_mcm_alias_guid = osm_mgrp_get_mcm_alias_guid(p_mgrp, portguid);
487 
488 	/* 1 */
489 	if (!*pp_mcm_alias_guid) {
490 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
491 			"Failed to find the port in the MC group\n");
492 		return FALSE;
493 	}
494 
495 	/* 2 */
496 	if (!(p_recvd_mcmember_rec->scope_state & 0x0F &
497 	      (*pp_mcm_alias_guid)->scope_state)) {
498 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
499 			"Could not find any matching bits in the stored "
500 			"and requested JoinStates\n");
501 		return FALSE;
502 	}
503 
504 	/* 3 */
505 	if (((p_recvd_mcmember_rec->scope_state & 0x0F) |
506 	     (0x0F & (*pp_mcm_alias_guid)->scope_state)) !=
507 	    (0x0F & (*pp_mcm_alias_guid)->scope_state)) {
508 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
509 			"Some bits in the request JoinState (0x%X) are not "
510 			"set in the stored port (0x%X)\n",
511 			(p_recvd_mcmember_rec->scope_state & 0x0F),
512 			(0x0F & (*pp_mcm_alias_guid)->scope_state));
513 		return FALSE;
514 	}
515 
516 	/* 4 */
517 	/* Validate according the the proxy_join (o15-0.1.2) */
518 	if (validate_modify(sa, p_mgrp, p_mad_addr, p_recvd_mcmember_rec,
519 			    pp_mcm_alias_guid) == FALSE) {
520 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
521 			"proxy_join validation failure\n");
522 		return FALSE;
523 	}
524 	return TRUE;
525 }
526 
527 /*
528  * Check legality of the requested MGID (note this does not hold for SA
529  * created MGIDs)
530  *
531  * Implementing o15-0.1.5:
532  * A multicast GID is considered to be invalid if:
533  * 1. It does not comply with the rules as specified in 4.1.1 "GID Usage and
534  *    Properties" on page 145:
535  *
536  * 14) The multicast GID format is (bytes are comma sep):
537  *     0xff,<Fl><Sc>,<Si>,<Si>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<Id>,<Id>,<Id>,<Id>
538  *     Fl  4bit = Flags (b)
539  *     Sc  4bit = Scope (c)
540  *     Si 16bit = Signature (2)
541  *     P  64bit = GID Prefix (should be a subnet unique ID - normally Subnet Prefix)
542  *     Id 32bit = Unique ID in the Subnet (might be MLID or P_Key ?)
543  *
544  *  a) 8-bits of 11111111 at the start of the GID identifies this as being a
545  *     multicast GID.
546  *  b) Flags is a set of four 1-bit flags: 000T with three flags reserved
547  *     and defined as zero (0). The T flag is defined as follows:
548  *     i) T = 0 indicates this is a permanently assigned (i.e. wellknown)
549  *        multicast GID. See RFC 2373 and RFC 2375 as reference
550  *        for these permanently assigned GIDs.
551  *     ii) T = 1 indicates this is a non-permanently assigned (i.e. transient)
552  *        multicast GID.
553  *  c) Scope is a 4-bit multicast scope value used to limit the scope of
554  *     the multicast group. The following table defines scope value and
555  *     interpretation.
556  *
557  *     Multicast Address Scope Values:
558  *     0x2 Link-local
559  *     0x5 Site-local
560  *     0x8 Organization-local
561  *     0xE Global
562  *
563  * 2. It contains the SA-specific signature of 0xA01B and has the link-local
564  *    scope bits set. (EZ: the idea here is that SA created MGIDs are the
565  *    only source for this signature with link-local scope)
566  */
validate_requested_mgid(IN osm_sa_t * sa,IN const ib_member_rec_t * p_mcm_rec)567 static boolean_t validate_requested_mgid(IN osm_sa_t * sa,
568 					 IN const ib_member_rec_t * p_mcm_rec)
569 {
570 	uint16_t signature;
571 	boolean_t valid = TRUE;
572 
573 	OSM_LOG_ENTER(sa->p_log);
574 
575 	/* 14-a: mcast GID must start with 0xFF */
576 	if (p_mcm_rec->mgid.multicast.header[0] != 0xFF) {
577 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B01: "
578 			"Invalid prefix 0x%02X in requested MGID, "
579 			"must be 0xFF\n",
580 			cl_ntoh16(p_mcm_rec->mgid.multicast.header[0]));
581 		valid = FALSE;
582 		goto Exit;
583 	}
584 
585 	/* the MGID signature can mark IPoIB or SA assigned MGIDs */
586 	memcpy(&signature, &(p_mcm_rec->mgid.multicast.raw_group_id),
587 	       sizeof(signature));
588 	signature = cl_ntoh16(signature);
589 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "MGID Signed as 0x%04X\n", signature);
590 
591 	/*
592 	 * We skip any checks for MGIDs that follow IPoIB
593 	 * GID structure as defined by the IETF ipoib-link-multicast.
594 	 *
595 	 * For IPv4 over IB, the signature will be "0x401B".
596 	 *
597 	 * |   8    |  4 |  4 |     16 bits     | 16 bits | 48 bits  | 32 bits |
598 	 * +--------+----+----+-----------------+---------+----------+---------+
599 	 * |11111111|0001|scop|<IPoIB signature>|< P_Key >|00.......0|<all 1's>|
600 	 * +--------+----+----+-----------------+---------+----------+---------+
601 	 *
602 	 * For IPv6 over IB, the signature will be "0x601B".
603 	 *
604 	 * |   8    |  4 |  4 |     16 bits     | 16 bits |       80 bits      |
605 	 * +--------+----+----+-----------------+---------+--------------------+
606 	 * |11111111|0001|scop|<IPoIB signature>|< P_Key >|000.............0001|
607 	 * +--------+----+----+-----------------+---------+--------------------+
608 	 *
609 	 */
610 	if (signature == 0x401B || signature == 0x601B) {
611 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
612 			"Skipping MGID Validation for IPoIB Signed (0x%04X) MGIDs\n",
613 			signature);
614 		goto Exit;
615 	}
616 
617 	/* 14-b: the 3 upper bits in the "flags" should be zero: */
618 	if (p_mcm_rec->mgid.multicast.header[1] & 0xE0) {
619 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B28: "
620 			"Requested MGID invalid, uses Reserved Flags: flags=0x%X\n",
621 			(p_mcm_rec->mgid.multicast.header[1] & 0xE0) >> 4);
622 		valid = FALSE;
623 		goto Exit;
624 	}
625 
626 	/* 2 - now what if the link local format 0xA01B is used -
627 	   the scope should not be link local */
628 	if (signature == 0xA01B &&
629 	    (p_mcm_rec->mgid.multicast.header[1] & 0x0F) ==
630 	    IB_MC_SCOPE_LINK_LOCAL) {
631 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B24: "
632 			"Requested MGID invalid, "
633 			"uses 0xA01B signature but with link-local scope\n");
634 		valid = FALSE;
635 		goto Exit;
636 	}
637 
638 	/*
639 	 * For SA assigned MGIDs (signature 0xA01B):
640 	 * There is no real way to make sure the GID Prefix is really unique.
641 	 * If we could enforce using the Subnet Prefix for that purpose it would
642 	 * have been nice. But the spec does not require it.
643 	 */
644 
645 Exit:
646 	OSM_LOG_EXIT(sa->p_log);
647 	return valid;
648 }
649 
650 /**********************************************************************
651  Check if the requested new MC group parameters are realizable.
652  Also set the default MTU and Rate if not provided by the user.
653 **********************************************************************/
mgrp_request_is_realizable(IN osm_sa_t * sa,IN ib_net64_t comp_mask,IN ib_member_rec_t * p_mcm_rec,IN const osm_physp_t * p_physp)654 static boolean_t mgrp_request_is_realizable(IN osm_sa_t * sa,
655 					    IN ib_net64_t comp_mask,
656 					    IN ib_member_rec_t * p_mcm_rec,
657 					    IN const osm_physp_t * p_physp)
658 {
659 	uint8_t mtu_sel = 2;	/* exactly */
660 	uint8_t mtu_required, mtu, port_mtu;
661 	uint8_t rate_sel = 2;	/* exactly */
662 	uint8_t rate_required, rate, port_rate;
663 	const ib_port_info_t *p_pi;
664 	osm_log_t *p_log = sa->p_log;
665 	int extended;
666 
667 	OSM_LOG_ENTER(sa->p_log);
668 
669 	/*
670 	 * End of o15-0.2.3 specifies:
671 	 * ....
672 	 * The entity may also supply the other components such as HopLimit,
673 	 * MTU, etc. during group creation time. If these components are not
674 	 * provided during group creation time, SA will provide them for the
675 	 * group. The values chosen are vendor-dependent and beyond the scope
676 	 * of the specification.
677 	 *
678 	 * so we might also need to assign RATE/MTU if they are not comp
679 	 * masked in.
680 	 */
681 
682 	p_pi = &p_physp->port_info;
683 	port_mtu = p_physp ? ib_port_info_get_mtu_cap(p_pi) : 0;
684 	if (!(comp_mask & IB_MCR_COMPMASK_MTU) ||
685 	    !(comp_mask & IB_MCR_COMPMASK_MTU_SEL) ||
686 	    (mtu_sel = (p_mcm_rec->mtu >> 6)) == 3)
687 		mtu = port_mtu ? port_mtu : sa->p_subn->min_ca_mtu;
688 	else {
689 		mtu_required = (uint8_t) (p_mcm_rec->mtu & 0x3F);
690 		mtu = mtu_required;
691 		switch (mtu_sel) {
692 		case 0:	/* Greater than MTU specified */
693 			if (port_mtu && mtu_required >= port_mtu) {
694 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
695 					"Requested MTU %x >= the port\'s mtu:%x\n",
696 					mtu_required, port_mtu);
697 				return FALSE;
698 			}
699 			/* we provide the largest MTU possible if we can */
700 			if (port_mtu)
701 				mtu = port_mtu;
702 			else if (mtu_required < sa->p_subn->min_ca_mtu)
703 				mtu = sa->p_subn->min_ca_mtu;
704 			else
705 				mtu++;
706 			break;
707 		case 1:	/* Less than MTU specified */
708 			/* use the smaller of the two:
709 			   a. one lower then the required
710 			   b. the mtu of the requesting port (if exists) */
711 			if (port_mtu && mtu_required > port_mtu)
712 				mtu = port_mtu;
713 			else
714 				mtu--;
715 			break;
716 		case 2:	/* Exactly MTU specified */
717 		default:
718 			break;
719 		}
720 		/* make sure it still is in the range */
721 		if (mtu < IB_MIN_MTU || mtu > IB_MAX_MTU) {
722 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
723 				"Calculated MTU %x is out of range\n", mtu);
724 			return FALSE;
725 		}
726 	}
727 	p_mcm_rec->mtu = (mtu_sel << 6) | mtu;
728 
729 	if (p_physp) {
730 		extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
731 		port_rate = ib_port_info_compute_rate(p_pi, extended);
732 	} else
733 		port_rate = 0;
734 
735 	if (!(comp_mask & IB_MCR_COMPMASK_RATE)
736 	    || !(comp_mask & IB_MCR_COMPMASK_RATE_SEL)
737 	    || (rate_sel = (p_mcm_rec->rate >> 6)) == 3)
738 		rate = port_rate ? port_rate : sa->p_subn->min_ca_rate;
739 	else {
740 		rate_required = (uint8_t) (p_mcm_rec->rate & 0x3F);
741 		rate = rate_required;
742 		switch (rate_sel) {
743 		case 0:	/* Greater than RATE specified */
744 			if (ib_path_compare_rates(rate_required, port_rate) >= 0) {
745 				OSM_LOG(p_log, OSM_LOG_VERBOSE,
746 					"Requested RATE %x >= the port\'s rate:%x\n",
747 					rate_required, port_rate);
748 				return FALSE;
749 			}
750 			/* we provide the largest RATE possible if we can */
751 			if (port_rate)
752 				rate = port_rate;
753 			else if (ib_path_compare_rates(rate_required,
754 						       sa->p_subn->min_ca_rate) < 0)
755 				rate = sa->p_subn->min_ca_rate;
756 			else
757 				rate = ib_path_rate_get_next(rate);
758 			break;
759 		case 1:	/* Less than RATE specified */
760 			/* use the smaller of the two:
761 			   a. one lower then the required
762 			   b. the rate of the requesting port (if exists) */
763 			if (ib_path_compare_rates(rate_required, port_rate) > 0)
764 				rate = port_rate;
765 			else
766 				rate = ib_path_rate_get_prev(rate);
767 			break;
768 		case 2:	/* Exactly RATE specified */
769 		default:
770 			break;
771 		}
772 		/* make sure it still is in the range */
773 		if (rate < IB_MIN_RATE || rate > IB_MAX_RATE) {
774 			OSM_LOG(p_log, OSM_LOG_VERBOSE,
775 				"Calculated RATE %x is out of range\n", rate);
776 			return FALSE;
777 		}
778 	}
779 	p_mcm_rec->rate = (rate_sel << 6) | rate;
780 
781 	OSM_LOG_EXIT(sa->p_log);
782 	return TRUE;
783 }
784 
build_new_mgid(osm_sa_t * sa,ib_net64_t comp_mask,ib_member_rec_t * mcmr)785 static unsigned build_new_mgid(osm_sa_t * sa, ib_net64_t comp_mask,
786 			       ib_member_rec_t * mcmr)
787 {
788 	static uint32_t uniq_count;
789 	ib_gid_t *mgid = &mcmr->mgid;
790 	uint8_t scope;
791 	unsigned i;
792 
793 	/* use the given scope state only if requested! */
794 	if (comp_mask & IB_MCR_COMPMASK_SCOPE)
795 		ib_member_get_scope_state(mcmr->scope_state, &scope, NULL);
796 	else
797 	/* to guarantee no collision with other subnets use local scope! */
798 		scope = IB_MC_SCOPE_LINK_LOCAL;
799 
800 	mgid->raw[0] = 0xff;
801 	mgid->raw[1] = 0x10 | scope;
802 	mgid->raw[2] = 0xa0;
803 	mgid->raw[3] = 0x1b;
804 
805 	memcpy(&mgid->raw[4], &sa->p_subn->opt.subnet_prefix, sizeof(uint64_t));
806 
807 	for (i = 0; i < 1000; i++) {
808 		memcpy(&mgid->raw[10], &uniq_count, 4);
809 		uniq_count++;
810 		if (!osm_get_mgrp_by_mgid(sa->p_subn, mgid))
811 			return 1;
812 	}
813 
814 	return 0;
815 }
816 
817 /**********************************************************************
818  Call this function to create a new mgrp.
819 **********************************************************************/
mcmr_rcv_create_new_mgrp(IN osm_sa_t * sa,IN ib_net64_t comp_mask,IN const ib_member_rec_t * p_recvd_mcmember_rec,IN const osm_physp_t * p_physp,OUT osm_mgrp_t ** pp_mgrp)820 static ib_api_status_t mcmr_rcv_create_new_mgrp(IN osm_sa_t * sa,
821 						IN ib_net64_t comp_mask,
822 						IN const ib_member_rec_t * p_recvd_mcmember_rec,
823 						IN const osm_physp_t * p_physp,
824 						OUT osm_mgrp_t ** pp_mgrp)
825 {
826 	ib_net16_t mlid;
827 	uint16_t signature;
828 	ib_api_status_t status = IB_SUCCESS;
829 	osm_mgrp_t *bcast_mgrp;
830 	ib_gid_t bcast_mgid;
831 	ib_member_rec_t mcm_rec = *p_recvd_mcmember_rec;	/* copy for modifications */
832 	char gid_str[INET6_ADDRSTRLEN];
833 
834 	OSM_LOG_ENTER(sa->p_log);
835 
836 	/* we need to create the new MGID if it was not defined */
837 	if (!ib_gid_is_notzero(&p_recvd_mcmember_rec->mgid)) {
838 		/* create a new MGID */
839 		if (!build_new_mgid(sa, comp_mask, &mcm_rec)) {
840 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B23: "
841 				"cannot allocate unique MGID value\n");
842 			status = IB_SA_MAD_STATUS_NO_RESOURCES;
843 			goto Exit;
844 		}
845 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Allocated new MGID:%s\n",
846 			inet_ntop(AF_INET6, mcm_rec.mgid.raw, gid_str,
847 				  sizeof gid_str));
848 	} else if (sa->p_subn->opt.ipoib_mcgroup_creation_validation) {
849 		/* a specific MGID was requested so validate the resulting MGID */
850 		if (validate_requested_mgid(sa, &mcm_rec)) {
851 			memcpy(&signature, &(mcm_rec.mgid.multicast.raw_group_id),
852 			       sizeof(signature));
853 			signature = cl_ntoh16(signature);
854 			/* Check for IPoIB signature in MGID */
855 			if (signature == 0x401B || signature == 0x601B) {
856 				/* Derive IPoIB broadcast MGID */
857 				bcast_mgid.unicast.prefix = IPV4_BCAST_MGID_PREFIX;
858 				bcast_mgid.unicast.interface_id = IPV4_BCAST_MGID_INT_ID;
859 				/* Set scope in IPoIB broadcast MGID */
860 				bcast_mgid.multicast.header[1] =
861 					(bcast_mgid.multicast.header[1] & 0xF0) |
862 					(mcm_rec.mgid.multicast.header[1] & 0x0F);
863 				/* Set P_Key in IPoIB broadcast MGID */
864 				bcast_mgid.multicast.raw_group_id[2] =
865 					mcm_rec.mgid.multicast.raw_group_id[2];
866 				bcast_mgid.multicast.raw_group_id[3] =
867 					mcm_rec.mgid.multicast.raw_group_id[3];
868 				/* Check MC group for the IPoIB broadcast group */
869 				if (signature != 0x401B ||
870 				    memcmp(&bcast_mgid, &(mcm_rec.mgid), sizeof(ib_gid_t))) {
871 					bcast_mgrp = osm_get_mgrp_by_mgid(sa->p_subn,
872 									  &bcast_mgid);
873 					if (!bcast_mgrp) {
874 						OSM_LOG(sa->p_log, OSM_LOG_ERROR,
875 							"ERR 1B1B: Broadcast group %s not found, sending IB_SA_MAD_STATUS_REQ_INVALID\n",
876 							inet_ntop(AF_INET6, bcast_mgid.raw, gid_str, sizeof gid_str));
877 						status = IB_SA_MAD_STATUS_REQ_INVALID;
878 						goto Exit;
879 					}
880 					if (!validate_other_comp_fields(sa->p_log, comp_mask, p_recvd_mcmember_rec, bcast_mgrp, OSM_LOG_ERROR)) {
881 						OSM_LOG(sa->p_log, OSM_LOG_ERROR,
882 							"ERR 1B1C: validate_other_comp_fields failed for MGID: %s, sending IB_SA_MAD_STATUS_REQ_INVALID\n",
883 							inet_ntop(AF_INET6, &p_recvd_mcmember_rec->mgid, gid_str, sizeof gid_str));
884 						status = IB_SA_MAD_STATUS_REQ_INVALID;
885 						goto Exit;
886 					}
887 				}
888 			}
889 		} else {
890 			status = IB_SA_MAD_STATUS_REQ_INVALID;
891 			goto Exit;
892 		}
893 	}
894 
895 	/* check the requested parameters are realizable */
896 	if (mgrp_request_is_realizable(sa, comp_mask, &mcm_rec, p_physp) ==
897 	    FALSE) {
898 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B26: "
899 			"Requested MGRP parameters are not realizable\n");
900 		status = IB_SA_MAD_STATUS_REQ_INVALID;
901 		goto Exit;
902 	}
903 
904 	mlid = get_new_mlid(sa, &mcm_rec);
905 	if (mlid == 0) {
906 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B19: "
907 			"get_new_mlid failed request mlid 0x%04x\n",
908 			cl_ntoh16(mcm_rec.mlid));
909 		status = IB_SA_MAD_STATUS_NO_RESOURCES;
910 		goto Exit;
911 	}
912 
913 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Obtained new mlid 0x%X\n",
914 		cl_ntoh16(mlid));
915 
916 	mcm_rec.mlid = mlid;
917 	/* create a new MC Group */
918 	*pp_mgrp = osm_mgrp_new(sa->p_subn, mlid, &mcm_rec);
919 	if (*pp_mgrp == NULL) {
920 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B08: "
921 			"osm_mgrp_new failed\n");
922 		free_mlid(sa, mlid);
923 		status = IB_SA_MAD_STATUS_NO_RESOURCES;
924 		goto Exit;
925 	}
926 
927 	/* the mcmember_record should have mtu_sel, rate_sel, and pkt_lifetime_sel = 2 */
928 	(*pp_mgrp)->mcmember_rec.mtu &= 0x3f;
929 	(*pp_mgrp)->mcmember_rec.mtu |= IB_PATH_SELECTOR_EXACTLY << 6;
930 	(*pp_mgrp)->mcmember_rec.rate &= 0x3f;
931 	(*pp_mgrp)->mcmember_rec.rate |= IB_PATH_SELECTOR_EXACTLY << 6;
932 	(*pp_mgrp)->mcmember_rec.pkt_life &= 0x3f;
933 	(*pp_mgrp)->mcmember_rec.pkt_life |= IB_PATH_SELECTOR_EXACTLY << 6;
934 
935 Exit:
936 	OSM_LOG_EXIT(sa->p_log);
937 	return status;
938 }
939 
940 /**********************************************************************
941  Call this function to find or create a new mgrp.
942 **********************************************************************/
osm_mcmr_rcv_find_or_create_new_mgrp(IN osm_sa_t * sa,IN ib_net64_t comp_mask,IN ib_member_rec_t * p_recvd_mcmember_rec)943 osm_mgrp_t *osm_mcmr_rcv_find_or_create_new_mgrp(IN osm_sa_t * sa,
944 						 IN ib_net64_t comp_mask,
945 						 IN ib_member_rec_t *
946 						 p_recvd_mcmember_rec)
947 {
948 	osm_mgrp_t *mgrp;
949 
950 	if ((mgrp = osm_get_mgrp_by_mgid(sa->p_subn,
951 					 &p_recvd_mcmember_rec->mgid)))
952 		return mgrp;
953 	if (mcmr_rcv_create_new_mgrp(sa, comp_mask, p_recvd_mcmember_rec, NULL,
954 				     &mgrp) == IB_SUCCESS)
955 		return mgrp;
956 	return NULL;
957 }
958 
959 /*********************************************************************
960 Process a request for leaving the group
961 **********************************************************************/
mcmr_rcv_leave_mgrp(IN osm_sa_t * sa,IN osm_madw_t * p_madw)962 static void mcmr_rcv_leave_mgrp(IN osm_sa_t * sa, IN osm_madw_t * p_madw)
963 {
964 	osm_mgrp_t *p_mgrp;
965 	ib_sa_mad_t *p_sa_mad;
966 	ib_member_rec_t *p_recvd_mcmember_rec;
967 	ib_member_rec_t mcmember_rec;
968 	osm_mcm_alias_guid_t *p_mcm_alias_guid;
969 
970 	OSM_LOG_ENTER(sa->p_log);
971 
972 	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
973 	p_recvd_mcmember_rec =
974 	    (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
975 
976 	mcmember_rec = *p_recvd_mcmember_rec;
977 
978 	/* Validate the subnet prefix in the PortGID */
979 	if (p_recvd_mcmember_rec->port_gid.unicast.prefix !=
980 	    sa->p_subn->opt.subnet_prefix) {
981 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
982 			"PortGID subnet prefix 0x%" PRIx64
983 			" does not match configured prefix 0x%" PRIx64 "\n",
984 			cl_ntoh64(p_recvd_mcmember_rec->port_gid.unicast.prefix),
985 			cl_ntoh64(sa->p_subn->opt.subnet_prefix));
986 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_INVALID_GID);
987 		goto Exit;
988 	}
989 
990 	CL_PLOCK_EXCL_ACQUIRE(sa->p_lock);
991 
992 	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
993 		osm_physp_t *p_req_physp;
994 
995 		p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
996 							osm_madw_get_mad_addr_ptr(p_madw));
997 		if (p_req_physp == NULL) {
998 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B02: "
999 				"Cannot find requester physical port\n");
1000 		} else {
1001 			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1002 				"Requester port GUID 0x%" PRIx64 "\n",
1003 				cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
1004 		}
1005 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Dump of record\n");
1006 		osm_dump_mc_record_v2(sa->p_log, &mcmember_rec, FILE_ID, OSM_LOG_DEBUG);
1007 	}
1008 
1009 	p_mgrp = osm_get_mgrp_by_mgid(sa->p_subn, &p_recvd_mcmember_rec->mgid);
1010 	if (!p_mgrp) {
1011 		char gid_str[INET6_ADDRSTRLEN];
1012 		CL_PLOCK_RELEASE(sa->p_lock);
1013 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1014 			"Failed since multicast group %s not present\n",
1015 			inet_ntop(AF_INET6, p_recvd_mcmember_rec->mgid.raw,
1016 				  gid_str, sizeof gid_str));
1017 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1018 		goto Exit;
1019 	}
1020 
1021 	/* check validity of the delete request o15-0.1.14 */
1022 	if (!validate_delete(sa, p_mgrp, osm_madw_get_mad_addr_ptr(p_madw),
1023 			     p_recvd_mcmember_rec, &p_mcm_alias_guid)) {
1024 		char gid_str[INET6_ADDRSTRLEN];
1025 		char gid_str2[INET6_ADDRSTRLEN];
1026 		CL_PLOCK_RELEASE(sa->p_lock);
1027 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B25: "
1028 			"Received an invalid delete request for "
1029 			"MGID: %s for PortGID: %s\n",
1030 			inet_ntop(AF_INET6, p_recvd_mcmember_rec->mgid.raw,
1031 				  gid_str, sizeof gid_str),
1032 			inet_ntop(AF_INET6, p_recvd_mcmember_rec->port_gid.raw,
1033 				  gid_str2, sizeof gid_str2));
1034 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1035 		goto Exit;
1036 	}
1037 
1038 	/* remove port and/or update join state */
1039 	osm_mgrp_remove_port(sa->p_subn, sa->p_log, p_mgrp, p_mcm_alias_guid,
1040 			     &mcmember_rec);
1041 	CL_PLOCK_RELEASE(sa->p_lock);
1042 
1043 	mcmr_rcv_respond(sa, p_madw, &mcmember_rec);
1044 
1045 Exit:
1046 	OSM_LOG_EXIT(sa->p_log);
1047 }
1048 
validate_other_comp_fields(osm_log_t * p_log,ib_net64_t comp_mask,const ib_member_rec_t * p_mcmr,osm_mgrp_t * p_mgrp,osm_log_level_t log_level)1049 static int validate_other_comp_fields(osm_log_t * p_log, ib_net64_t comp_mask,
1050 				      const ib_member_rec_t * p_mcmr,
1051 				      osm_mgrp_t * p_mgrp,
1052 				      osm_log_level_t log_level)
1053 {
1054 	int ret = 0;
1055 
1056 	if ((IB_MCR_COMPMASK_QKEY & comp_mask) &&
1057 	    p_mcmr->qkey != p_mgrp->mcmember_rec.qkey) {
1058 		OSM_LOG(p_log, log_level, "ERR 1B30: "
1059 			"Q_Key mismatch: query 0x%x group 0x%x\n",
1060 			cl_ntoh32(p_mcmr->qkey),
1061 			cl_ntoh32(p_mgrp->mcmember_rec.qkey));
1062 		goto Exit;
1063 	}
1064 
1065 	if (IB_MCR_COMPMASK_PKEY & comp_mask) {
1066 		if (!(ib_pkey_is_full_member(p_mcmr->pkey) ||
1067 		      ib_pkey_is_full_member(p_mgrp->mcmember_rec.pkey))) {
1068 			OSM_LOG(p_log, log_level, "ERR 1B31: "
1069 				"Both limited P_Keys: query 0x%x group 0x%x\n",
1070 				cl_ntoh16(p_mcmr->pkey),
1071 				cl_ntoh16(p_mgrp->mcmember_rec.pkey));
1072 			goto Exit;
1073 		}
1074 		if (ib_pkey_get_base(p_mcmr->pkey) !=
1075 		    ib_pkey_get_base(p_mgrp->mcmember_rec.pkey)) {
1076 			OSM_LOG(p_log, log_level, "ERR 1B32: "
1077 				"P_Key base mismatch: query 0x%x group 0x%x\n",
1078 				cl_ntoh16(p_mcmr->pkey),
1079 				cl_ntoh16(p_mgrp->mcmember_rec.pkey));
1080 			goto Exit;
1081 		}
1082 	}
1083 
1084 	if ((IB_MCR_COMPMASK_TCLASS & comp_mask) &&
1085 	    p_mcmr->tclass != p_mgrp->mcmember_rec.tclass) {
1086 		OSM_LOG(p_log, log_level, "ERR 1B33: "
1087 			"TClass mismatch: query %d group %d\n",
1088 			p_mcmr->tclass, p_mgrp->mcmember_rec.tclass);
1089 		goto Exit;
1090 	}
1091 
1092 	/* check SL, Flow, and Hop limit */
1093 	{
1094 		uint32_t mgrp_flow, query_flow;
1095 		uint8_t mgrp_sl, query_sl;
1096 		uint8_t mgrp_hop, query_hop;
1097 
1098 		ib_member_get_sl_flow_hop(p_mcmr->sl_flow_hop,
1099 					  &query_sl, &query_flow, &query_hop);
1100 
1101 		ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1102 					  &mgrp_sl, &mgrp_flow, &mgrp_hop);
1103 
1104 		if ((IB_MCR_COMPMASK_SL & comp_mask) && query_sl != mgrp_sl) {
1105 			OSM_LOG(p_log, log_level, "ERR 1B34: "
1106 				"SL mismatch: query %d group %d\n",
1107 				query_sl, mgrp_sl);
1108 			goto Exit;
1109 		}
1110 
1111 		if ((IB_MCR_COMPMASK_FLOW & comp_mask) &&
1112 		    query_flow != mgrp_flow) {
1113 			OSM_LOG(p_log, log_level, "ERR 1B35: "
1114 				"FlowLabel mismatch: query 0x%x group 0x%x\n",
1115 				query_flow, mgrp_flow);
1116 			goto Exit;
1117 		}
1118 
1119 		if ((IB_MCR_COMPMASK_HOP & comp_mask) && query_hop != mgrp_hop) {
1120 			OSM_LOG(p_log, log_level, "ERR 1B36: "
1121 				"Hop mismatch: query %d group %d\n",
1122 				query_hop, mgrp_hop);
1123 			goto Exit;
1124 		}
1125 	}
1126 
1127 	ret = 1;
1128 Exit:
1129 	return ret;
1130 }
1131 
1132 /**********************************************************************
1133  Handle a join (or create) request
1134 **********************************************************************/
mcmr_rcv_join_mgrp(IN osm_sa_t * sa,IN osm_madw_t * p_madw)1135 static void mcmr_rcv_join_mgrp(IN osm_sa_t * sa, IN osm_madw_t * p_madw)
1136 {
1137 	osm_mgrp_t *p_mgrp = NULL;
1138 	ib_api_status_t status;
1139 	ib_sa_mad_t *p_sa_mad;
1140 	ib_member_rec_t *p_recvd_mcmember_rec;
1141 	ib_member_rec_t mcmember_rec;
1142 	osm_mcm_port_t *p_mcmr_port;
1143 	osm_mcm_alias_guid_t *p_mcm_alias_guid;
1144 	ib_net64_t portguid;
1145 	osm_port_t *p_port;
1146 	osm_physp_t *p_physp;
1147 	osm_physp_t *p_request_physp;
1148 	uint8_t is_new_group;	/* TRUE = there is a need to create a group */
1149 	uint8_t join_state;
1150 	boolean_t proxy;
1151 
1152 	OSM_LOG_ENTER(sa->p_log);
1153 
1154 	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1155 	p_recvd_mcmember_rec = ib_sa_mad_get_payload_ptr(p_sa_mad);
1156 
1157 	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
1158 
1159 	mcmember_rec = *p_recvd_mcmember_rec;
1160 
1161 	/* Validate the subnet prefix in the PortGID */
1162 	if (p_recvd_mcmember_rec->port_gid.unicast.prefix !=
1163 	    sa->p_subn->opt.subnet_prefix) {
1164 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1165 			"PortGID subnet prefix 0x%" PRIx64
1166 			" does not match configured prefix 0x%" PRIx64 "\n",
1167 			cl_ntoh64(p_recvd_mcmember_rec->port_gid.unicast.prefix),
1168 			cl_ntoh64(sa->p_subn->opt.subnet_prefix));
1169 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_INVALID_GID);
1170 		goto Exit;
1171 	}
1172 
1173 	CL_PLOCK_EXCL_ACQUIRE(sa->p_lock);
1174 
1175 	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
1176 		osm_physp_t *p_req_physp;
1177 
1178 		p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
1179 							osm_madw_get_mad_addr_ptr(p_madw));
1180 		if (p_req_physp == NULL) {
1181 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B03: "
1182 				"Cannot find requester physical port\n");
1183 		} else {
1184 			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1185 				"Requester port GUID 0x%" PRIx64 "\n",
1186 				cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
1187 		}
1188 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Dump of incoming record\n");
1189 		osm_dump_mc_record_v2(sa->p_log, &mcmember_rec, FILE_ID, OSM_LOG_DEBUG);
1190 	}
1191 
1192 	/* make sure the requested port guid is known to the SM */
1193 	p_port = osm_get_port_by_alias_guid(sa->p_subn, portguid);
1194 	if (!p_port) {
1195 		CL_PLOCK_RELEASE(sa->p_lock);
1196 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1197 			"Unknown port GUID 0x%016" PRIx64 "\n",
1198 			cl_ntoh64(portguid));
1199 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1200 		goto Exit;
1201 	}
1202 
1203 	p_physp = p_port->p_physp;
1204 	/* Check that the p_physp and the requester physp are in the same
1205 	   partition. */
1206 	p_request_physp =
1207 	    osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
1208 				      osm_madw_get_mad_addr_ptr(p_madw));
1209 	if (p_request_physp == NULL) {
1210 		CL_PLOCK_RELEASE(sa->p_lock);
1211 		goto Exit;
1212 	}
1213 
1214 	proxy = (p_physp != p_request_physp);
1215 
1216 	if (proxy && !osm_physp_share_pkey(sa->p_log, p_physp, p_request_physp,
1217 					   sa->p_subn->opt.allow_both_pkeys)) {
1218 		CL_PLOCK_RELEASE(sa->p_lock);
1219 		OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1220 			"Port and requester don't share PKey\n");
1221 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1222 		goto Exit;
1223 	}
1224 
1225 	if ((p_sa_mad->comp_mask & IB_MCR_COMPMASK_PKEY) &&
1226 	    ib_pkey_is_invalid(p_recvd_mcmember_rec->pkey)) {
1227 		CL_PLOCK_RELEASE(sa->p_lock);
1228 		OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
1229 			"Invalid PKey supplied in request\n");
1230 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1231 		goto Exit;
1232 	}
1233 
1234 	ib_member_get_scope_state(p_recvd_mcmember_rec->scope_state, NULL,
1235 				  &join_state);
1236 
1237 	/* do we need to create a new group? */
1238 	p_mgrp = osm_get_mgrp_by_mgid(sa->p_subn, &p_recvd_mcmember_rec->mgid);
1239 	if (!p_mgrp) {
1240 		/* check for JoinState.FullMember = 1 o15.0.1.9 */
1241 		if ((join_state & 0x01) != 0x01) {
1242 			char gid_str[INET6_ADDRSTRLEN];
1243 			CL_PLOCK_RELEASE(sa->p_lock);
1244 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B10: "
1245 				"Failed to create multicast group "
1246 				"because Join State != FullMember, "
1247 				"MGID: %s from port 0x%016" PRIx64 " (%s)\n",
1248 				inet_ntop(AF_INET6,
1249 					  p_recvd_mcmember_rec->mgid.raw,
1250 					  gid_str, sizeof gid_str),
1251 				cl_ntoh64(portguid),
1252 				p_port->p_node->print_desc);
1253 			osm_sa_send_error(sa, p_madw,
1254 					  IB_SA_MAD_STATUS_REQ_INVALID);
1255 			goto Exit;
1256 		}
1257 
1258 		/* check the comp_mask */
1259 		if (!check_create_comp_mask(p_sa_mad->comp_mask,
1260 					    p_recvd_mcmember_rec)) {
1261 			char gid_str[INET6_ADDRSTRLEN];
1262 			CL_PLOCK_RELEASE(sa->p_lock);
1263 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B11: "
1264 				"Port 0x%016" PRIx64 " (%s) failed to join "
1265 				"non-existing multicast group with MGID %s, "
1266 				"insufficient components specified for "
1267 				"implicit create (comp_mask 0x%" PRIx64 ")\n",
1268 				cl_ntoh64(portguid), p_port->p_node->print_desc,
1269 				inet_ntop(AF_INET6,
1270 					  p_recvd_mcmember_rec->mgid.raw,
1271 					  gid_str, sizeof gid_str),
1272 				cl_ntoh64(p_sa_mad->comp_mask));
1273 			osm_sa_send_error(sa, p_madw,
1274 					  IB_SA_MAD_STATUS_INSUF_COMPS);
1275 			goto Exit;
1276 		}
1277 
1278 		status = mcmr_rcv_create_new_mgrp(sa, p_sa_mad->comp_mask,
1279 						  p_recvd_mcmember_rec,
1280 						  p_physp, &p_mgrp);
1281 		if (status != IB_SUCCESS) {
1282 			CL_PLOCK_RELEASE(sa->p_lock);
1283 			osm_sa_send_error(sa, p_madw, status);
1284 			goto Exit;
1285 		}
1286 		/* copy the MGID to the result */
1287 		mcmember_rec.mgid = p_mgrp->mcmember_rec.mgid;
1288 		is_new_group = 1;
1289 	} else {
1290 		/* no need for a new group */
1291 		is_new_group = 0;
1292 		if (sa->p_subn->opt.mcgroup_join_validation &&
1293 		    !validate_other_comp_fields(sa->p_log, p_sa_mad->comp_mask,
1294 						p_recvd_mcmember_rec, p_mgrp,
1295 						OSM_LOG_ERROR)) {
1296 			char gid_str[INET6_ADDRSTRLEN];
1297 			CL_PLOCK_RELEASE(sa->p_lock);
1298 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B1A: "
1299 				"validate_other_comp_fields failed for "
1300 				"MGID: %s port 0x%016" PRIx64
1301 				" (%s), sending IB_SA_MAD_STATUS_REQ_INVALID\n",
1302 				inet_ntop(AF_INET6,
1303 					  p_mgrp->mcmember_rec.mgid.raw,
1304 					  gid_str, sizeof gid_str),
1305 				cl_ntoh64(portguid),
1306 				p_port->p_node->print_desc);
1307 			osm_sa_send_error(sa, p_madw,
1308 					  IB_SA_MAD_STATUS_REQ_INVALID);
1309 			goto Exit;
1310 		}
1311 	}
1312 
1313 	CL_ASSERT(p_mgrp);
1314 
1315 	/*
1316 	 * o15-0.2.4: If SA supports UD multicast, then SA shall cause an
1317 	 * endport to join an existing multicast group if:
1318 	 * 1. It receives a SubnAdmSet() method for a MCMemberRecord, and
1319 	 *    - WE KNOW THAT ALREADY
1320 	 * 2. The MGID is specified and matches an existing multicast
1321 	 *    group, and
1322 	 *    - WE KNOW THAT ALREADY
1323 	 * 3. The MCMemberRecord:JoinState is not all 0s, and
1324 	 * 4. PortGID is specified and
1325 	 *    - WE KNOW THAT ALREADY (as it matched a real one)
1326 	 * 5. All other components match that existing group, either by
1327 	 *    being wildcarded or by having values identical to those specified
1328 	 *    by the component mask and in use by the group with the exception
1329 	 *    of components such as ProxyJoin and Reserved, which are ignored
1330 	 *    by SA.
1331 	 *
1332 	 * We need to check #3 and #5 here:
1333 	 */
1334 	if (!validate_more_comp_fields(sa->p_log, p_mgrp, p_recvd_mcmember_rec,
1335 				       p_sa_mad->comp_mask)
1336 	    || !validate_port_caps(sa->p_log, p_mgrp, p_physp)
1337 	    || !(join_state != 0)) {
1338 		char gid_str[INET6_ADDRSTRLEN];
1339 		/* since we might have created the new group we need to cleanup */
1340 		if (is_new_group)
1341 			osm_mgrp_cleanup(sa->p_subn, p_mgrp);
1342 		CL_PLOCK_RELEASE(sa->p_lock);
1343 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B12: "
1344 			"validate_more_comp_fields, validate_port_caps, "
1345 			"or JoinState = 0 failed for MGID: %s port 0x%016" PRIx64
1346 			" (%s), sending IB_SA_MAD_STATUS_REQ_INVALID\n",
1347 			   inet_ntop(AF_INET6, p_mgrp->mcmember_rec.mgid.raw,
1348 				     gid_str, sizeof gid_str),
1349 			cl_ntoh64(portguid), p_port->p_node->print_desc);
1350 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1351 		goto Exit;
1352 	}
1353 
1354 	/* verify that the joining port is in the partition of the group */
1355 	if (!osm_physp_has_pkey(sa->p_log, p_mgrp->mcmember_rec.pkey, p_physp)) {
1356 		char gid_str[INET6_ADDRSTRLEN];
1357 		if (is_new_group)
1358 			osm_mgrp_cleanup(sa->p_subn, p_mgrp);
1359 		CL_PLOCK_RELEASE(sa->p_lock);
1360 		memset(gid_str, 0, sizeof(gid_str));
1361 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B14: "
1362 			"Cannot join port 0x%016" PRIx64 " to MGID %s - "
1363 			"Port is not in partition of this MC group\n",
1364 			cl_ntoh64(portguid),
1365 			inet_ntop(AF_INET6,
1366 				  p_mgrp->mcmember_rec.mgid.raw,
1367 				  gid_str, sizeof(gid_str)));
1368 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1369 		goto Exit;
1370 	}
1371 
1372 	/*
1373 	 * o15-0.2.1 requires validation of the requesting port
1374 	 * in the case of modification:
1375 	 */
1376 	if (!is_new_group &&
1377 	    !validate_modify(sa, p_mgrp, osm_madw_get_mad_addr_ptr(p_madw),
1378 			     p_recvd_mcmember_rec, &p_mcm_alias_guid)) {
1379 		char gid_str[INET6_ADDRSTRLEN];
1380 		CL_PLOCK_RELEASE(sa->p_lock);
1381 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B13: "
1382 			"validate_modify failed from port 0x%016" PRIx64
1383 			" (%s) for MGID: %s, sending IB_SA_MAD_STATUS_REQ_INVALID\n",
1384 			cl_ntoh64(portguid), p_port->p_node->print_desc,
1385 			inet_ntop(AF_INET6,
1386 				  p_mgrp->mcmember_rec.mgid.raw,
1387 				  gid_str, sizeof(gid_str)));
1388 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1389 		goto Exit;
1390 	}
1391 
1392 	/* copy qkey mlid tclass pkey sl_flow_hop mtu rate pkt_life */
1393 	copy_from_create_mc_rec(&mcmember_rec, &p_mgrp->mcmember_rec);
1394 
1395 	/* create or update existing port (join-state will be updated) */
1396 	p_mcmr_port = osm_mgrp_add_port(sa->p_subn, sa->p_log, p_mgrp, p_port,
1397 					&mcmember_rec, proxy);
1398 	if (!p_mcmr_port) {
1399 		/* we fail to add the port so we might need to delete the group */
1400 		if (is_new_group)
1401 			osm_mgrp_cleanup(sa->p_subn, p_mgrp);
1402 		CL_PLOCK_RELEASE(sa->p_lock);
1403 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B06: "
1404 			"osm_mgrp_add_port failed\n");
1405 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_NO_RESOURCES);
1406 		goto Exit;
1407 	}
1408 
1409 	/* Release the lock as we don't need it. */
1410 	CL_PLOCK_RELEASE(sa->p_lock);
1411 
1412 	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG))
1413 		osm_dump_mc_record_v2(sa->p_log, &mcmember_rec, FILE_ID, OSM_LOG_DEBUG);
1414 
1415 	mcmr_rcv_respond(sa, p_madw, &mcmember_rec);
1416 
1417 Exit:
1418 	OSM_LOG_EXIT(sa->p_log);
1419 }
1420 
1421 /**********************************************************************
1422  Add a patched multicast group to the results list
1423 **********************************************************************/
mcmr_rcv_new_mcmr(IN osm_sa_t * sa,IN const ib_member_rec_t * p_rcvd_rec,IN cl_qlist_t * p_list)1424 static ib_api_status_t mcmr_rcv_new_mcmr(IN osm_sa_t * sa,
1425 					 IN const ib_member_rec_t * p_rcvd_rec,
1426 					 IN cl_qlist_t * p_list)
1427 {
1428 	osm_sa_item_t *p_rec_item;
1429 	ib_api_status_t status = IB_SUCCESS;
1430 
1431 	OSM_LOG_ENTER(sa->p_log);
1432 
1433 	p_rec_item = malloc(SA_MCM_RESP_SIZE);
1434 	if (p_rec_item == NULL) {
1435 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B15: "
1436 			"rec_item alloc failed\n");
1437 		status = IB_INSUFFICIENT_RESOURCES;
1438 		goto Exit;
1439 	}
1440 
1441 	memset(p_rec_item, 0, sizeof(cl_list_item_t));
1442 
1443 	/* HACK: Untrusted requesters should result with 0 Join
1444 	   State, Port Guid, and Proxy */
1445 	p_rec_item->resp.mc_rec = *p_rcvd_rec;
1446 	cl_qlist_insert_tail(p_list, &p_rec_item->list_item);
1447 
1448 Exit:
1449 	OSM_LOG_EXIT(sa->p_log);
1450 	return status;
1451 }
1452 
1453 /**********************************************************************
1454  Match the given mgrp to the requested mcmr
1455 **********************************************************************/
mcmr_by_comp_mask(osm_sa_t * sa,const ib_member_rec_t * p_rcvd_rec,ib_net64_t comp_mask,osm_mgrp_t * p_mgrp,const osm_physp_t * p_req_physp,boolean_t trusted_req,cl_qlist_t * list)1456 static void mcmr_by_comp_mask(osm_sa_t * sa, const ib_member_rec_t * p_rcvd_rec,
1457 			      ib_net64_t comp_mask, osm_mgrp_t * p_mgrp,
1458 			      const osm_physp_t * p_req_physp,
1459 			      boolean_t trusted_req, cl_qlist_t * list)
1460 {
1461 	/* since we might change scope_state */
1462 	ib_member_rec_t match_rec;
1463 	osm_mcm_alias_guid_t *p_mcm_alias_guid;
1464 	ib_net64_t portguid = p_rcvd_rec->port_gid.unicast.interface_id;
1465 	/* will be used for group or port info */
1466 	uint8_t scope_state;
1467 	uint8_t scope_state_mask = 0;
1468 	cl_map_item_t *p_item;
1469 	ib_gid_t port_gid;
1470 	boolean_t proxy_join = FALSE;
1471 
1472 	OSM_LOG_ENTER(sa->p_log);
1473 
1474 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1475 		"Checking mlid:0x%X\n", cl_ntoh16(p_mgrp->mlid));
1476 
1477 	/* first try to eliminate the group by MGID, MLID, or P_Key */
1478 	if ((IB_MCR_COMPMASK_MGID & comp_mask) &&
1479 	    memcmp(&p_rcvd_rec->mgid, &p_mgrp->mcmember_rec.mgid,
1480 		   sizeof(ib_gid_t)))
1481 		goto Exit;
1482 
1483 	if ((IB_MCR_COMPMASK_MLID & comp_mask) &&
1484 	    memcmp(&p_rcvd_rec->mlid, &p_mgrp->mcmember_rec.mlid,
1485 		   sizeof(uint16_t)))
1486 		goto Exit;
1487 
1488 	/* if the requester physical port doesn't have the pkey that is defined
1489 	   for the group - exit. */
1490 	if (!osm_physp_has_pkey(sa->p_log, p_mgrp->mcmember_rec.pkey,
1491 				p_req_physp))
1492 		goto Exit;
1493 
1494 	/* now do the rest of the match */
1495 	if (!validate_other_comp_fields(sa->p_log, comp_mask, p_rcvd_rec, p_mgrp,
1496 					OSM_LOG_NONE))
1497 		goto Exit;
1498 
1499 	if ((IB_MCR_COMPMASK_PROXY & comp_mask) &&
1500 	    p_rcvd_rec->proxy_join != p_mgrp->mcmember_rec.proxy_join)
1501 		goto Exit;
1502 
1503 	/* need to validate mtu, rate, and pkt_lifetime fields */
1504 	if (validate_more_comp_fields(sa->p_log, p_mgrp, p_rcvd_rec,
1505 				      comp_mask) == FALSE)
1506 		goto Exit;
1507 
1508 	/* Port specific fields */
1509 	/* so did we get the PortGUID mask */
1510 	if (IB_MCR_COMPMASK_PORT_GID & comp_mask) {
1511 		/* try to find this port */
1512 		p_mcm_alias_guid = osm_mgrp_get_mcm_alias_guid(p_mgrp, portguid);
1513 		if (!p_mcm_alias_guid) /* port not in group */
1514 			goto Exit;
1515 		scope_state = p_mcm_alias_guid->scope_state;
1516 		memcpy(&port_gid, &(p_mcm_alias_guid->port_gid),
1517 		       sizeof(ib_gid_t));
1518 		proxy_join = p_mcm_alias_guid->proxy_join;
1519 	} else /* point to the group information */
1520 		scope_state = p_mgrp->mcmember_rec.scope_state;
1521 
1522 	if (IB_MCR_COMPMASK_SCOPE & comp_mask)
1523 		scope_state_mask = 0xF0;
1524 
1525 	if (IB_MCR_COMPMASK_JOIN_STATE & comp_mask)
1526 		scope_state_mask = scope_state_mask | 0x0F;
1527 
1528 	/* Many MC records returned */
1529 	if (trusted_req == TRUE && !(IB_MCR_COMPMASK_PORT_GID & comp_mask)) {
1530 		char gid_str[INET6_ADDRSTRLEN];
1531 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1532 			"Trusted req is TRUE and no specific port defined\n");
1533 
1534 		/* return all the ports that match in this MC group */
1535 		p_item = cl_qmap_head(&(p_mgrp->mcm_alias_port_tbl));
1536 		while (p_item != cl_qmap_end(&(p_mgrp->mcm_alias_port_tbl))) {
1537 			p_mcm_alias_guid = (osm_mcm_alias_guid_t *) p_item;
1538 
1539 			if ((scope_state_mask & p_rcvd_rec->scope_state) ==
1540 			    (scope_state_mask & p_mcm_alias_guid->scope_state)) {
1541 				/* add to the list */
1542 				match_rec = p_mgrp->mcmember_rec;
1543 				match_rec.scope_state = p_mcm_alias_guid->scope_state;
1544 				memcpy(&match_rec.port_gid,
1545 				       &p_mcm_alias_guid->port_gid,
1546 				       sizeof(ib_gid_t));
1547 				OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1548 					"Record of port_gid: %s"
1549 					" in multicast_lid: 0x%X is returned\n",
1550 					inet_ntop(AF_INET6,
1551 						  match_rec.port_gid.raw,
1552 						  gid_str, sizeof gid_str),
1553 					cl_ntoh16(p_mgrp->mlid));
1554 
1555 				match_rec.proxy_join =
1556 				    (uint8_t) (p_mcm_alias_guid->proxy_join);
1557 
1558 				mcmr_rcv_new_mcmr(sa, &match_rec, list);
1559 			}
1560 			p_item = cl_qmap_next(p_item);
1561 		}
1562 	} else { /* One MC record returned */
1563 		if ((scope_state_mask & p_rcvd_rec->scope_state) !=
1564 		    (scope_state_mask & scope_state))
1565 			goto Exit;
1566 
1567 		/* add to the list */
1568 		match_rec = p_mgrp->mcmember_rec;
1569 		match_rec.scope_state = scope_state;
1570 		memcpy(&(match_rec.port_gid), &port_gid, sizeof(ib_gid_t));
1571 		match_rec.proxy_join = (uint8_t) proxy_join;
1572 
1573 		mcmr_rcv_new_mcmr(sa, &match_rec, list);
1574 	}
1575 
1576 Exit:
1577 	OSM_LOG_EXIT(sa->p_log);
1578 }
1579 
1580 /**********************************************************************
1581  Handle a query request
1582 **********************************************************************/
mcmr_query_mgrp(IN osm_sa_t * sa,IN osm_madw_t * p_madw)1583 static void mcmr_query_mgrp(IN osm_sa_t * sa, IN osm_madw_t * p_madw)
1584 {
1585 	const ib_sa_mad_t *p_rcvd_mad;
1586 	const ib_member_rec_t *p_rcvd_rec;
1587 	cl_qlist_t rec_list;
1588 	ib_net64_t comp_mask;
1589 	osm_physp_t *p_req_physp;
1590 	boolean_t trusted_req;
1591 	osm_mgrp_t *p_mgrp;
1592 
1593 	OSM_LOG_ENTER(sa->p_log);
1594 
1595 	p_rcvd_mad = osm_madw_get_sa_mad_ptr(p_madw);
1596 	p_rcvd_rec = (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_rcvd_mad);
1597 	comp_mask = p_rcvd_mad->comp_mask;
1598 
1599 	/*
1600 	   if sm_key is not zero and does not match we never get here
1601 	   see main SA receiver
1602 	 */
1603 	trusted_req = (p_rcvd_mad->sm_key != 0);
1604 
1605 	CL_PLOCK_ACQUIRE(sa->p_lock);
1606 
1607 	/* update the requester physical port */
1608 	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
1609 						osm_madw_get_mad_addr_ptr
1610 						(p_madw));
1611 	if (p_req_physp == NULL) {
1612 		CL_PLOCK_RELEASE(sa->p_lock);
1613 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B04: "
1614 			"Cannot find requester physical port\n");
1615 		goto Exit;
1616 	}
1617 
1618 	if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
1619 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1620 			"Requester port GUID 0x%" PRIx64 "\n",
1621 			cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
1622 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Dump of record\n");
1623 		osm_dump_mc_record(sa->p_log, p_rcvd_rec, OSM_LOG_DEBUG);
1624 	}
1625 
1626 	cl_qlist_init(&rec_list);
1627 
1628 	/* simply go over all MCGs and match */
1629 	for (p_mgrp = (osm_mgrp_t *) cl_fmap_head(&sa->p_subn->mgrp_mgid_tbl);
1630 	     p_mgrp != (osm_mgrp_t *) cl_fmap_end(&sa->p_subn->mgrp_mgid_tbl);
1631 	     p_mgrp = (osm_mgrp_t *) cl_fmap_next(&p_mgrp->map_item))
1632 		mcmr_by_comp_mask(sa, p_rcvd_rec, comp_mask, p_mgrp,
1633 				  p_req_physp, trusted_req, &rec_list);
1634 
1635 	CL_PLOCK_RELEASE(sa->p_lock);
1636 
1637 	/*
1638 	   p923 - The PortGID, JoinState and ProxyJoin shall be zero,
1639 	   except in the case of a trusted request.
1640 	   Note: In the mad controller we check that the SM_Key received on
1641 	   the mad is valid. Meaning - is either zero or equal to the local
1642 	   sm_key.
1643 	 */
1644 
1645 	if (!p_rcvd_mad->sm_key) {
1646 		osm_sa_item_t *item;
1647 		for (item = (osm_sa_item_t *) cl_qlist_head(&rec_list);
1648 		     item != (osm_sa_item_t *) cl_qlist_end(&rec_list);
1649 		     item =
1650 		     (osm_sa_item_t *) cl_qlist_next(&item->list_item)) {
1651 			memset(&item->resp.mc_rec.port_gid, 0, sizeof(ib_gid_t));
1652 			ib_member_set_join_state(&item->resp.mc_rec, 0);
1653 			item->resp.mc_rec.proxy_join = 0;
1654 		}
1655 	}
1656 
1657 	osm_sa_respond(sa, p_madw, sizeof(ib_member_rec_t), &rec_list);
1658 
1659 Exit:
1660 	OSM_LOG_EXIT(sa->p_log);
1661 }
1662 
rate_is_valid(IN const ib_sa_mad_t * p_sa_mad,IN const ib_member_rec_t * p_recvd_mcmember_rec)1663 static uint8_t rate_is_valid(IN const ib_sa_mad_t *p_sa_mad,
1664 			     IN const ib_member_rec_t *p_recvd_mcmember_rec)
1665 {
1666 	uint8_t rate;
1667 
1668 	/* Validate rate if supplied */
1669 	if ((p_sa_mad->comp_mask & IB_MCR_COMPMASK_RATE_SEL) &&
1670 	    (p_sa_mad->comp_mask & IB_MCR_COMPMASK_RATE)) {
1671 		rate = (uint8_t) (p_recvd_mcmember_rec->rate & 0x3F);
1672 		return ib_rate_is_valid(rate);
1673 	}
1674 	return 1;
1675 }
1676 
mtu_is_valid(IN const ib_sa_mad_t * p_sa_mad,IN const ib_member_rec_t * p_recvd_mcmember_rec)1677 static int mtu_is_valid(IN const ib_sa_mad_t *p_sa_mad,
1678 			IN const ib_member_rec_t *p_recvd_mcmember_rec)
1679 {
1680 	uint8_t mtu;
1681 
1682 	/* Validate MTU if supplied */
1683 	if ((p_sa_mad->comp_mask & IB_MCR_COMPMASK_MTU_SEL) &&
1684 	    (p_sa_mad->comp_mask & IB_MCR_COMPMASK_MTU)) {
1685 		mtu = (uint8_t) (p_recvd_mcmember_rec->mtu & 0x3F);
1686 		return ib_mtu_is_valid(mtu);
1687 	}
1688 	return 1;
1689 }
1690 
osm_mcmr_rcv_process(IN void * context,IN void * data)1691 void osm_mcmr_rcv_process(IN void *context, IN void *data)
1692 {
1693 	osm_sa_t *sa = context;
1694 	osm_madw_t *p_madw = data;
1695 	ib_sa_mad_t *p_sa_mad;
1696 	ib_member_rec_t *p_recvd_mcmember_rec;
1697 
1698 	CL_ASSERT(sa);
1699 
1700 	OSM_LOG_ENTER(sa->p_log);
1701 
1702 	CL_ASSERT(p_madw);
1703 
1704 	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1705 	p_recvd_mcmember_rec =
1706 	    (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1707 
1708 	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_MCMEMBER_RECORD);
1709 
1710 	switch (p_sa_mad->method) {
1711 	case IB_MAD_METHOD_SET:
1712 		if (!check_join_comp_mask(p_sa_mad->comp_mask)) {
1713 			char gid_str[INET6_ADDRSTRLEN];
1714 			char gid_str2[INET6_ADDRSTRLEN];
1715 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B18: "
1716 				"component mask = 0x%016" PRIx64 ", "
1717 				"expected comp mask = 0x%016" PRIx64 ", "
1718 				"MGID: %s for PortGID: %s\n",
1719 				cl_ntoh64(p_sa_mad->comp_mask),
1720 				CL_NTOH64(JOIN_MC_COMP_MASK),
1721 				inet_ntop(AF_INET6,
1722 					  p_recvd_mcmember_rec->mgid.raw,
1723 					  gid_str, sizeof gid_str),
1724 				inet_ntop(AF_INET6,
1725 					  p_recvd_mcmember_rec->port_gid.raw,
1726 					  gid_str2, sizeof gid_str2));
1727 			osm_sa_send_error(sa, p_madw,
1728 					  IB_SA_MAD_STATUS_INSUF_COMPS);
1729 			goto Exit;
1730 		}
1731 		if (!rate_is_valid(p_sa_mad, p_recvd_mcmember_rec) ||
1732 		    !mtu_is_valid(p_sa_mad, p_recvd_mcmember_rec)) {
1733 			osm_sa_send_error(sa, p_madw,
1734 					  IB_SA_MAD_STATUS_REQ_INVALID);
1735 			goto Exit;
1736 		}
1737 
1738 		/*
1739 		 * Join or Create Multicast Group
1740 		 */
1741 		mcmr_rcv_join_mgrp(sa, p_madw);
1742 		break;
1743 	case IB_MAD_METHOD_DELETE:
1744 		if (!check_join_comp_mask(p_sa_mad->comp_mask)) {
1745 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B20: "
1746 				"component mask = 0x%016" PRIx64 ", "
1747 				"expected comp mask = 0x%016" PRIx64 "\n",
1748 				cl_ntoh64(p_sa_mad->comp_mask),
1749 				CL_NTOH64(JOIN_MC_COMP_MASK));
1750 			osm_sa_send_error(sa, p_madw,
1751 					  IB_SA_MAD_STATUS_INSUF_COMPS);
1752 			goto Exit;
1753 		}
1754 		if (!rate_is_valid(p_sa_mad, p_recvd_mcmember_rec) ||
1755 		    !mtu_is_valid(p_sa_mad, p_recvd_mcmember_rec)) {
1756 			osm_sa_send_error(sa, p_madw,
1757 					  IB_SA_MAD_STATUS_REQ_INVALID);
1758 			goto Exit;
1759 		}
1760 
1761 		/*
1762 		 * Leave Multicast Group
1763 		 */
1764 		mcmr_rcv_leave_mgrp(sa, p_madw);
1765 		break;
1766 	case IB_MAD_METHOD_GET:
1767 	case IB_MAD_METHOD_GETTABLE:
1768 		if (!rate_is_valid(p_sa_mad, p_recvd_mcmember_rec) ||
1769 		    !mtu_is_valid(p_sa_mad, p_recvd_mcmember_rec)) {
1770 			osm_sa_send_error(sa, p_madw,
1771 					  IB_SA_MAD_STATUS_REQ_INVALID);
1772 			goto Exit;
1773 		}
1774 
1775 		/*
1776 		 * Querying a Multicast Group
1777 		 */
1778 		mcmr_query_mgrp(sa, p_madw);
1779 		break;
1780 	default:
1781 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B21: "
1782 			"Unsupported Method (%s) for MCMemberRecord request\n",
1783 			ib_get_sa_method_str(p_sa_mad->method));
1784 		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
1785 		break;
1786 	}
1787 
1788 Exit:
1789 	OSM_LOG_EXIT(sa->p_log);
1790 	return;
1791 }
1792