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
89static 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**********************************************************************/
99static 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*********************************************************************/
119static 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
137static int compare_ipv6_snm_mgids(const void *m1, const void *m2)
138{
139	return memcmp(m1, m2, sizeof(ib_gid_t) - 3);
140}
141
142static 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
151static 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
157static 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
186static 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
191static 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**********************************************************************/
201static 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
231Exit:
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**********************************************************************/
240static 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**********************************************************************/
337static 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 **********************************************************************/
382static 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 */
477static 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 */
567static 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
645Exit:
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**********************************************************************/
654static 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
785static 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**********************************************************************/
820static 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
935Exit:
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**********************************************************************/
943osm_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/*********************************************************************
960Process a request for leaving the group
961**********************************************************************/
962static 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
1045Exit:
1046	OSM_LOG_EXIT(sa->p_log);
1047}
1048
1049static 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;
1128Exit:
1129	return ret;
1130}
1131
1132/**********************************************************************
1133 Handle a join (or create) request
1134**********************************************************************/
1135static 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
1417Exit:
1418	OSM_LOG_EXIT(sa->p_log);
1419}
1420
1421/**********************************************************************
1422 Add a patched multicast group to the results list
1423**********************************************************************/
1424static 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
1448Exit:
1449	OSM_LOG_EXIT(sa->p_log);
1450	return status;
1451}
1452
1453/**********************************************************************
1454 Match the given mgrp to the requested mcmr
1455**********************************************************************/
1456static 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
1576Exit:
1577	OSM_LOG_EXIT(sa->p_log);
1578}
1579
1580/**********************************************************************
1581 Handle a query request
1582**********************************************************************/
1583static 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
1659Exit:
1660	OSM_LOG_EXIT(sa->p_log);
1661}
1662
1663static 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
1677static 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
1691void 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
1788Exit:
1789	OSM_LOG_EXIT(sa->p_log);
1790	return;
1791}
1792