1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff * Copyright (c) 2008 Xsigo Systems Inc.  All rights reserved.
6219820Sjeff *
7219820Sjeff * This software is available to you under a choice of one of two
8219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
9219820Sjeff * General Public License (GPL) Version 2, available from the file
10219820Sjeff * COPYING in the main directory of this source tree, or the
11219820Sjeff * OpenIB.org BSD license below:
12219820Sjeff *
13219820Sjeff *     Redistribution and use in source and binary forms, with or
14219820Sjeff *     without modification, are permitted provided that the following
15219820Sjeff *     conditions are met:
16219820Sjeff *
17219820Sjeff *      - Redistributions of source code must retain the above
18219820Sjeff *        copyright notice, this list of conditions and the following
19219820Sjeff *        disclaimer.
20219820Sjeff *
21219820Sjeff *      - Redistributions in binary form must reproduce the above
22219820Sjeff *        copyright notice, this list of conditions and the following
23219820Sjeff *        disclaimer in the documentation and/or other materials
24219820Sjeff *        provided with the distribution.
25219820Sjeff *
26219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33219820Sjeff * SOFTWARE.
34219820Sjeff *
35219820Sjeff */
36219820Sjeff
37219820Sjeff/*
38219820Sjeff * Abstract:
39219820Sjeff *    Implementation of osm_mcmr_recv_t.
40219820Sjeff * This object represents the MCMemberRecord Receiver object.
41219820Sjeff * This object is part of the opensm family of objects.
42219820Sjeff */
43219820Sjeff
44219820Sjeff#if HAVE_CONFIG_H
45219820Sjeff#  include <config.h>
46219820Sjeff#endif				/* HAVE_CONFIG_H */
47219820Sjeff
48219820Sjeff#include <stdlib.h>
49219820Sjeff#include <string.h>
50219820Sjeff#include <arpa/inet.h>
51219820Sjeff#include <iba/ib_types.h>
52219820Sjeff#include <complib/cl_qmap.h>
53219820Sjeff#include <complib/cl_passivelock.h>
54219820Sjeff#include <complib/cl_debug.h>
55219820Sjeff#include <complib/cl_qlist.h>
56219820Sjeff#include <vendor/osm_vendor_api.h>
57219820Sjeff#include <opensm/osm_madw.h>
58219820Sjeff#include <opensm/osm_log.h>
59219820Sjeff#include <opensm/osm_subnet.h>
60219820Sjeff#include <opensm/osm_mad_pool.h>
61219820Sjeff#include <opensm/osm_helper.h>
62219820Sjeff#include <opensm/osm_msgdef.h>
63219820Sjeff#include <opensm/osm_pkey.h>
64219820Sjeff#include <opensm/osm_inform.h>
65219820Sjeff#include <opensm/osm_sa.h>
66219820Sjeff
67219820Sjeff#include <sys/socket.h>
68219820Sjeff
69219820Sjeff#define JOIN_MC_COMP_MASK (IB_MCR_COMPMASK_MGID | \
70219820Sjeff				IB_MCR_COMPMASK_PORT_GID | \
71219820Sjeff				IB_MCR_COMPMASK_JOIN_STATE)
72219820Sjeff
73219820Sjeff#define REQUIRED_MC_CREATE_COMP_MASK (IB_MCR_COMPMASK_MGID | \
74219820Sjeff					IB_MCR_COMPMASK_PORT_GID | \
75219820Sjeff					IB_MCR_COMPMASK_JOIN_STATE | \
76219820Sjeff					IB_MCR_COMPMASK_QKEY | \
77219820Sjeff					IB_MCR_COMPMASK_TCLASS | \
78219820Sjeff					IB_MCR_COMPMASK_PKEY | \
79219820Sjeff					IB_MCR_COMPMASK_FLOW | \
80219820Sjeff					IB_MCR_COMPMASK_SL)
81219820Sjeff
82219820Sjefftypedef struct osm_mcmr_item {
83219820Sjeff	cl_list_item_t list_item;
84219820Sjeff	ib_member_rec_t rec;
85219820Sjeff} osm_mcmr_item_t;
86219820Sjeff
87219820Sjeff/*********************************************************************
88219820Sjeff Copy certain fields between two mcmember records
89219820Sjeff used during the process of join request to copy data from the mgrp
90219820Sjeff to the port record.
91219820Sjeff**********************************************************************/
92219820Sjeffstatic inline void
93219820Sjeff__copy_from_create_mc_rec(IN ib_member_rec_t * const dest,
94219820Sjeff			  IN const ib_member_rec_t * const src)
95219820Sjeff{
96219820Sjeff	dest->qkey = src->qkey;
97219820Sjeff	dest->mlid = src->mlid;
98219820Sjeff	dest->tclass = src->tclass;
99219820Sjeff	dest->pkey = src->pkey;
100219820Sjeff	dest->sl_flow_hop = src->sl_flow_hop;
101219820Sjeff	dest->mtu = src->mtu;
102219820Sjeff	dest->rate = src->rate;
103219820Sjeff	dest->pkt_life = src->pkt_life;
104219820Sjeff}
105219820Sjeff
106219820Sjeff/*********************************************************************
107219820Sjeff Return mlid to the pool of free mlids.
108219820Sjeff But this implementation is not a pool - it simply scans through
109219820Sjeff the MGRP database for unused mlids...
110219820Sjeff*********************************************************************/
111219820Sjeffstatic void __free_mlid(IN osm_sa_t * sa, IN uint16_t mlid)
112219820Sjeff{
113219820Sjeff	UNUSED_PARAM(sa);
114219820Sjeff	UNUSED_PARAM(mlid);
115219820Sjeff}
116219820Sjeff
117219820Sjeff/*********************************************************************
118219820Sjeff Get a new unused mlid by scanning all the used ones in the subnet.
119219820Sjeff**********************************************************************/
120219820Sjeffstatic ib_net16_t __get_new_mlid(osm_sa_t *sa, ib_net16_t requested_mlid)
121219820Sjeff{
122219820Sjeff	osm_subn_t *p_subn = sa->p_subn;
123219820Sjeff	unsigned i, max;
124219820Sjeff
125219820Sjeff	if (requested_mlid && cl_ntoh16(requested_mlid) >= IB_LID_MCAST_START_HO
126219820Sjeff	    && cl_ntoh16(requested_mlid) <= p_subn->max_mcast_lid_ho
127219820Sjeff	    && !osm_get_mgrp_by_mlid(p_subn, requested_mlid))
128219820Sjeff		return requested_mlid;
129219820Sjeff
130219820Sjeff	max = p_subn->max_mcast_lid_ho - IB_LID_MCAST_START_HO + 1;
131219820Sjeff	for (i = 0; i < max; i++) {
132219820Sjeff		osm_mgrp_t *p_mgrp = sa->p_subn->mgroups[i];
133219820Sjeff		if (!p_mgrp || p_mgrp->to_be_deleted)
134219820Sjeff			return cl_hton16(i + IB_LID_MCAST_START_HO);
135219820Sjeff	}
136219820Sjeff
137219820Sjeff	return 0;
138219820Sjeff}
139219820Sjeff
140219820Sjeff/*********************************************************************
141219820Sjeff This procedure is only invoked to cleanup an INTERMEDIATE mgrp.
142219820Sjeff If there is only one port on the mgrp it means that the current
143219820Sjeff request was the only member and the group is not really needed. So
144219820Sjeff we silently drop it. Since it was an intermediate group no need to
145219820Sjeff re-route it.
146219820Sjeff**********************************************************************/
147219820Sjeffstatic void __cleanup_mgrp(IN osm_sa_t * sa, osm_mgrp_t *mgrp)
148219820Sjeff{
149219820Sjeff	/* Remove MGRP only if osm_mcm_port_t count is 0 and
150219820Sjeff	   not a well known group */
151219820Sjeff	if (cl_is_qmap_empty(&mgrp->mcm_port_tbl) && !mgrp->well_known) {
152219820Sjeff		sa->p_subn->mgroups[cl_ntoh16(mgrp->mlid) - IB_LID_MCAST_START_HO] = NULL;
153219820Sjeff		osm_mgrp_delete(mgrp);
154219820Sjeff	}
155219820Sjeff}
156219820Sjeff
157219820Sjeff/*********************************************************************
158219820Sjeff Add a port to the group. Calculating its PROXY_JOIN by the Port and
159219820Sjeff requester gids.
160219820Sjeff**********************************************************************/
161219820Sjeffstatic ib_api_status_t
162219820Sjeff__add_new_mgrp_port(IN osm_sa_t * sa,
163219820Sjeff		    IN osm_mgrp_t * p_mgrp,
164219820Sjeff		    IN ib_member_rec_t * p_recvd_mcmember_rec,
165219820Sjeff		    IN osm_mad_addr_t * p_mad_addr,
166219820Sjeff		    OUT osm_mcm_port_t ** pp_mcmr_port)
167219820Sjeff{
168219820Sjeff	boolean_t proxy_join;
169219820Sjeff	ib_gid_t requester_gid;
170219820Sjeff	ib_api_status_t res;
171219820Sjeff
172219820Sjeff	/* set the proxy_join if the requester gid is not identical to the
173219820Sjeff	   joined gid */
174219820Sjeff	res = osm_get_gid_by_mad_addr(sa->p_log, sa->p_subn,
175219820Sjeff				      p_mad_addr, &requester_gid);
176219820Sjeff	if (res != IB_SUCCESS) {
177219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B29: "
178219820Sjeff			"Could not find GID for requester\n");
179219820Sjeff
180219820Sjeff		return IB_INVALID_PARAMETER;
181219820Sjeff	}
182219820Sjeff
183219820Sjeff	if (!memcmp(&p_recvd_mcmember_rec->port_gid, &requester_gid,
184219820Sjeff		    sizeof(ib_gid_t))) {
185219820Sjeff		proxy_join = FALSE;
186219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
187219820Sjeff			"Create new port with proxy_join FALSE\n");
188219820Sjeff	} else {
189219820Sjeff		/* The port is not the one specified in PortGID.
190219820Sjeff		   The check that the requester is in the same partition as
191219820Sjeff		   the PortGID is done before - just need to update
192219820Sjeff		   the proxy_join. */
193219820Sjeff		proxy_join = TRUE;
194219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
195219820Sjeff			"Create new port with proxy_join TRUE\n");
196219820Sjeff	}
197219820Sjeff
198219820Sjeff	*pp_mcmr_port = osm_mgrp_add_port(sa->p_subn, sa->p_log, p_mgrp,
199219820Sjeff					  &p_recvd_mcmember_rec->port_gid,
200219820Sjeff					  p_recvd_mcmember_rec->scope_state,
201219820Sjeff					  proxy_join);
202219820Sjeff	if (*pp_mcmr_port == NULL) {
203219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B06: "
204219820Sjeff			"osm_mgrp_add_port failed\n");
205219820Sjeff
206219820Sjeff		return IB_INSUFFICIENT_MEMORY;
207219820Sjeff	}
208219820Sjeff
209219820Sjeff	return IB_SUCCESS;
210219820Sjeff}
211219820Sjeff
212219820Sjeff/**********************************************************************
213219820Sjeff **********************************************************************/
214219820Sjeffstatic inline boolean_t __check_join_comp_mask(ib_net64_t comp_mask)
215219820Sjeff{
216219820Sjeff	return ((comp_mask & JOIN_MC_COMP_MASK) == JOIN_MC_COMP_MASK);
217219820Sjeff}
218219820Sjeff
219219820Sjeff/**********************************************************************
220219820Sjeff **********************************************************************/
221219820Sjeffstatic inline boolean_t
222219820Sjeff__check_create_comp_mask(ib_net64_t comp_mask,
223219820Sjeff			 ib_member_rec_t * p_recvd_mcmember_rec)
224219820Sjeff{
225219820Sjeff	return ((comp_mask & REQUIRED_MC_CREATE_COMP_MASK) ==
226219820Sjeff		 REQUIRED_MC_CREATE_COMP_MASK);
227219820Sjeff}
228219820Sjeff
229219820Sjeff/**********************************************************************
230219820Sjeff Generate the response MAD
231219820Sjeff**********************************************************************/
232219820Sjeffstatic void
233219820Sjeff__osm_mcmr_rcv_respond(IN osm_sa_t * sa,
234219820Sjeff		       IN osm_madw_t * const p_madw,
235219820Sjeff		       IN ib_member_rec_t * p_mcmember_rec)
236219820Sjeff{
237219820Sjeff	cl_qlist_t rec_list;
238219820Sjeff	osm_mcmr_item_t *item;
239219820Sjeff
240219820Sjeff	OSM_LOG_ENTER(sa->p_log);
241219820Sjeff
242219820Sjeff	item = malloc(sizeof(*item));
243219820Sjeff	if (!item) {
244219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B16: "
245219820Sjeff			"rec_item alloc failed\n");
246219820Sjeff		goto Exit;
247219820Sjeff	}
248219820Sjeff
249219820Sjeff	item->rec = *p_mcmember_rec;
250219820Sjeff
251219820Sjeff	/* Fill in the mtu, rate, and packet lifetime selectors */
252219820Sjeff	item->rec.mtu &= 0x3f;
253219820Sjeff	item->rec.mtu |= 2 << 6;	/* exactly */
254219820Sjeff	item->rec.rate &= 0x3f;
255219820Sjeff	item->rec.rate |= 2 << 6;	/* exactly */
256219820Sjeff	item->rec.pkt_life &= 0x3f;
257219820Sjeff	item->rec.pkt_life |= 2 << 6;	/* exactly */
258219820Sjeff
259219820Sjeff	cl_qlist_init(&rec_list);
260219820Sjeff	cl_qlist_insert_tail(&rec_list, &item->list_item);
261219820Sjeff
262219820Sjeff	osm_sa_respond(sa, p_madw, sizeof(ib_member_rec_t), &rec_list);
263219820Sjeff
264219820SjeffExit:
265219820Sjeff	OSM_LOG_EXIT(sa->p_log);
266219820Sjeff}
267219820Sjeff
268219820Sjeff/*********************************************************************
269219820Sjeff In joining an existing group, or when querying the mc groups,
270219820Sjeff we make sure the following components provided match: MTU and RATE
271219820Sjeff HACK: Currently we ignore the PKT_LIFETIME field.
272219820Sjeff**********************************************************************/
273219820Sjeffstatic boolean_t
274219820Sjeff__validate_more_comp_fields(osm_log_t * p_log,
275219820Sjeff			    const osm_mgrp_t * p_mgrp,
276219820Sjeff			    const ib_member_rec_t * p_recvd_mcmember_rec,
277219820Sjeff			    ib_net64_t comp_mask)
278219820Sjeff{
279219820Sjeff	uint8_t mtu_sel;
280219820Sjeff	uint8_t mtu_required;
281219820Sjeff	uint8_t mtu_mgrp;
282219820Sjeff	uint8_t rate_sel;
283219820Sjeff	uint8_t rate_required;
284219820Sjeff	uint8_t rate_mgrp;
285219820Sjeff
286219820Sjeff	if (comp_mask & IB_MCR_COMPMASK_MTU_SEL) {
287219820Sjeff		mtu_sel = (uint8_t) (p_recvd_mcmember_rec->mtu >> 6);
288219820Sjeff		/* Clearing last 2 bits */
289219820Sjeff		mtu_required = (uint8_t) (p_recvd_mcmember_rec->mtu & 0x3F);
290219820Sjeff		mtu_mgrp = (uint8_t) (p_mgrp->mcmember_rec.mtu & 0x3F);
291219820Sjeff		switch (mtu_sel) {
292219820Sjeff		case 0:	/* Greater than MTU specified */
293219820Sjeff			if (mtu_mgrp <= mtu_required) {
294219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
295219820Sjeff					"Requested mcast group has MTU %x, "
296219820Sjeff					"which is not greater than %x\n",
297219820Sjeff					mtu_mgrp, mtu_required);
298219820Sjeff				return FALSE;
299219820Sjeff			}
300219820Sjeff			break;
301219820Sjeff		case 1:	/* Less than MTU specified */
302219820Sjeff			if (mtu_mgrp >= mtu_required) {
303219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
304219820Sjeff					"Requested mcast group has MTU %x, "
305219820Sjeff					"which is not less than %x\n",
306219820Sjeff					mtu_mgrp, mtu_required);
307219820Sjeff				return FALSE;
308219820Sjeff			}
309219820Sjeff			break;
310219820Sjeff		case 2:	/* Exactly MTU specified */
311219820Sjeff			if (mtu_mgrp != mtu_required) {
312219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
313219820Sjeff					"Requested mcast group has MTU %x, "
314219820Sjeff					"which is not equal to %x\n",
315219820Sjeff					mtu_mgrp, mtu_required);
316219820Sjeff				return FALSE;
317219820Sjeff			}
318219820Sjeff			break;
319219820Sjeff		default:
320219820Sjeff			break;
321219820Sjeff		}
322219820Sjeff	}
323219820Sjeff
324219820Sjeff	/* what about rate ? */
325219820Sjeff	if (comp_mask & IB_MCR_COMPMASK_RATE_SEL) {
326219820Sjeff		rate_sel = (uint8_t) (p_recvd_mcmember_rec->rate >> 6);
327219820Sjeff		/* Clearing last 2 bits */
328219820Sjeff		rate_required = (uint8_t) (p_recvd_mcmember_rec->rate & 0x3F);
329219820Sjeff		rate_mgrp = (uint8_t) (p_mgrp->mcmember_rec.rate & 0x3F);
330219820Sjeff		switch (rate_sel) {
331219820Sjeff		case 0:	/* Greater than RATE specified */
332219820Sjeff			if (rate_mgrp <= rate_required) {
333219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
334219820Sjeff					"Requested mcast group has RATE %x, "
335219820Sjeff					"which is not greater than %x\n",
336219820Sjeff					rate_mgrp, rate_required);
337219820Sjeff				return FALSE;
338219820Sjeff			}
339219820Sjeff			break;
340219820Sjeff		case 1:	/* Less than RATE specified */
341219820Sjeff			if (rate_mgrp >= rate_required) {
342219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
343219820Sjeff					"Requested mcast group has RATE %x, "
344219820Sjeff					"which is not less than %x\n",
345219820Sjeff					rate_mgrp, rate_required);
346219820Sjeff				return FALSE;
347219820Sjeff			}
348219820Sjeff			break;
349219820Sjeff		case 2:	/* Exactly RATE specified */
350219820Sjeff			if (rate_mgrp != rate_required) {
351219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
352219820Sjeff					"Requested mcast group has RATE %x, "
353219820Sjeff					"which is not equal to %x\n",
354219820Sjeff					rate_mgrp, rate_required);
355219820Sjeff				return FALSE;
356219820Sjeff			}
357219820Sjeff			break;
358219820Sjeff		default:
359219820Sjeff			break;
360219820Sjeff		}
361219820Sjeff	}
362219820Sjeff
363219820Sjeff	return TRUE;
364219820Sjeff}
365219820Sjeff
366219820Sjeff/*********************************************************************
367219820Sjeff In joining an existing group, we make sure the following components
368219820Sjeff are physically realizable: MTU and RATE
369219820Sjeff**********************************************************************/
370219820Sjeffstatic boolean_t
371219820Sjeff__validate_port_caps(osm_log_t * const p_log,
372219820Sjeff		     const osm_mgrp_t * p_mgrp, const osm_physp_t * p_physp)
373219820Sjeff{
374219820Sjeff	uint8_t mtu_required;
375219820Sjeff	uint8_t mtu_mgrp;
376219820Sjeff	uint8_t rate_required;
377219820Sjeff	uint8_t rate_mgrp;
378219820Sjeff
379219820Sjeff	mtu_required = ib_port_info_get_mtu_cap(&p_physp->port_info);
380219820Sjeff	mtu_mgrp = (uint8_t) (p_mgrp->mcmember_rec.mtu & 0x3F);
381219820Sjeff	if (mtu_required < mtu_mgrp) {
382219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG,
383219820Sjeff			"Port's MTU %x is less than %x\n",
384219820Sjeff			mtu_required, mtu_mgrp);
385219820Sjeff		return FALSE;
386219820Sjeff	}
387219820Sjeff
388219820Sjeff	rate_required = ib_port_info_compute_rate(&p_physp->port_info);
389219820Sjeff	rate_mgrp = (uint8_t) (p_mgrp->mcmember_rec.rate & 0x3F);
390219820Sjeff	if (rate_required < rate_mgrp) {
391219820Sjeff		OSM_LOG(p_log, OSM_LOG_DEBUG,
392219820Sjeff			"Port's RATE %x is less than %x\n",
393219820Sjeff			rate_required, rate_mgrp);
394219820Sjeff		return FALSE;
395219820Sjeff	}
396219820Sjeff
397219820Sjeff	return TRUE;
398219820Sjeff}
399219820Sjeff
400219820Sjeff/**********************************************************************
401219820Sjeff * o15-0.2.1: If SA supports UD multicast, then if SA receives a SubnAdmSet()
402219820Sjeff * or SubnAdmDelete() method that would modify an existing
403219820Sjeff * MCMemberRecord, SA shall not modify that MCMemberRecord and shall
404219820Sjeff * return an error status of ERR_REQ_INVALID in response in the
405219820Sjeff * following cases:
406219820Sjeff * 1. Saved MCMemberRecord.ProxyJoin is not set and the request is
407219820Sjeff * issued by a requester with a GID other than the Port-GID.
408219820Sjeff * 2. Saved MCMemberRecord.ProxyJoin is set and the requester is not
409219820Sjeff * part of the partition for that MCMemberRecord.
410219820Sjeff **********************************************************************/
411219820Sjeffstatic boolean_t
412219820Sjeff__validate_modify(IN osm_sa_t * sa,
413219820Sjeff		  IN osm_mgrp_t * p_mgrp,
414219820Sjeff		  IN osm_mad_addr_t * p_mad_addr,
415219820Sjeff		  IN ib_member_rec_t * p_recvd_mcmember_rec,
416219820Sjeff		  OUT osm_mcm_port_t ** pp_mcm_port)
417219820Sjeff{
418219820Sjeff	ib_net64_t portguid;
419219820Sjeff	ib_gid_t request_gid;
420219820Sjeff	osm_physp_t *p_request_physp;
421219820Sjeff	ib_api_status_t res;
422219820Sjeff
423219820Sjeff	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
424219820Sjeff
425219820Sjeff	*pp_mcm_port = NULL;
426219820Sjeff
427219820Sjeff	/* o15-0.2.1: If this is a new port being added - nothing to check */
428219820Sjeff	if (!osm_mgrp_is_port_present(p_mgrp, portguid, pp_mcm_port)) {
429219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
430219820Sjeff			"This is a new port in the MC group\n");
431219820Sjeff		return TRUE;
432219820Sjeff	}
433219820Sjeff
434219820Sjeff	/* We validate the request according the the proxy_join.
435219820Sjeff	   Check if the proxy_join is set or not */
436219820Sjeff	if ((*pp_mcm_port)->proxy_join == FALSE) {
437219820Sjeff		/* The proxy_join is not set. Modifying can by done only
438219820Sjeff		   if the requester GID == PortGID */
439219820Sjeff		res = osm_get_gid_by_mad_addr(sa->p_log,
440219820Sjeff					      sa->p_subn,
441219820Sjeff					      p_mad_addr, &request_gid);
442219820Sjeff
443219820Sjeff		if (res != IB_SUCCESS) {
444219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
445219820Sjeff				"Could not find port for requested address\n");
446219820Sjeff			return FALSE;
447219820Sjeff		}
448219820Sjeff
449219820Sjeff		if (memcmp(&((*pp_mcm_port)->port_gid), &request_gid,
450219820Sjeff			   sizeof(ib_gid_t))) {
451219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
452219820Sjeff				"No ProxyJoin but different ports: stored:"
453219820Sjeff				"0x%016" PRIx64 " request:0x%016" PRIx64 "\n",
454219820Sjeff				cl_ntoh64((*pp_mcm_port)->port_gid.unicast.
455219820Sjeff					  interface_id),
456219820Sjeff				cl_ntoh64(p_mad_addr->addr_type.gsi.grh_info.
457219820Sjeff					  src_gid.unicast.interface_id));
458219820Sjeff			return FALSE;
459219820Sjeff		}
460219820Sjeff	} else {
461219820Sjeff		/* The proxy_join is set. Modification allowed only if the
462219820Sjeff		   requester is part of the partition for this MCMemberRecord */
463219820Sjeff		p_request_physp = osm_get_physp_by_mad_addr(sa->p_log,
464219820Sjeff							    sa->p_subn,
465219820Sjeff							    p_mad_addr);
466219820Sjeff		if (p_request_physp == NULL)
467219820Sjeff			return FALSE;
468219820Sjeff
469219820Sjeff		if (!osm_physp_has_pkey(sa->p_log, p_mgrp->mcmember_rec.pkey,
470219820Sjeff					p_request_physp)) {
471219820Sjeff			/* the request port is not part of the partition for this mgrp */
472219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
473219820Sjeff				"ProxyJoin but port not in partition. stored:"
474219820Sjeff				"0x%016" PRIx64 " request:0x%016" PRIx64 "\n",
475219820Sjeff				cl_ntoh64((*pp_mcm_port)->port_gid.unicast.
476219820Sjeff					  interface_id),
477219820Sjeff				cl_ntoh64(p_mad_addr->addr_type.gsi.grh_info.
478219820Sjeff					  src_gid.unicast.interface_id));
479219820Sjeff			return FALSE;
480219820Sjeff		}
481219820Sjeff	}
482219820Sjeff	return TRUE;
483219820Sjeff}
484219820Sjeff
485219820Sjeff/**********************************************************************
486219820Sjeff **********************************************************************/
487219820Sjeff/*
488219820Sjeff * Check legality of the requested MGID DELETE
489219820Sjeff * o15-0.1.14 = VALID DELETE:
490219820Sjeff * To be a valid delete MAD needs to:
491219820Sjeff * 1 the MADs PortGID and MGID components match the PortGID and
492219820Sjeff *   MGID of a stored MCMemberRecord;
493219820Sjeff * 2 the MADs JoinState component contains at least one bit set to 1
494219820Sjeff *   in the same position as that stored MCMemberRecords JoinState
495219820Sjeff *   has a bit set to 1,
496219820Sjeff *   i.e., the logical AND of the two JoinState components
497219820Sjeff *   is not all zeros;
498219820Sjeff * 3 the MADs JoinState component does not have some bits set
499219820Sjeff *   which are not set in the stored MCMemberRecords JoinState component;
500219820Sjeff * 4 either the stored MCMemberRecord:ProxyJoin is reset (0), and the
501219820Sjeff *   MADs source is the stored PortGID;
502219820Sjeff *   OR
503219820Sjeff *   the stored MCMemberRecord:ProxyJoin is set (1), (see o15-
504219820Sjeff *   0.1.2:); and the MADs source is a member of the partition indicated
505219820Sjeff *   by the stored MCMemberRecord:P_Key.
506219820Sjeff */
507219820Sjeffstatic boolean_t
508219820Sjeff__validate_delete(IN osm_sa_t * sa,
509219820Sjeff		  IN osm_mgrp_t * p_mgrp,
510219820Sjeff		  IN osm_mad_addr_t * p_mad_addr,
511219820Sjeff		  IN ib_member_rec_t * p_recvd_mcmember_rec,
512219820Sjeff		  OUT osm_mcm_port_t ** pp_mcm_port)
513219820Sjeff{
514219820Sjeff	ib_net64_t portguid;
515219820Sjeff
516219820Sjeff	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
517219820Sjeff
518219820Sjeff	*pp_mcm_port = NULL;
519219820Sjeff
520219820Sjeff	/* 1 */
521219820Sjeff	if (!osm_mgrp_is_port_present(p_mgrp, portguid, pp_mcm_port)) {
522219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
523219820Sjeff			"Failed to find the port in the MC group\n");
524219820Sjeff		return FALSE;
525219820Sjeff	}
526219820Sjeff
527219820Sjeff	/* 2 */
528219820Sjeff	if (!(p_recvd_mcmember_rec->scope_state & 0x0F &
529219820Sjeff	      (*pp_mcm_port)->scope_state)) {
530219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
531219820Sjeff			"Could not find any matching bits in the stored "
532219820Sjeff			"and requested JoinStates\n");
533219820Sjeff		return FALSE;
534219820Sjeff	}
535219820Sjeff
536219820Sjeff	/* 3 */
537219820Sjeff	if (((p_recvd_mcmember_rec->scope_state & 0x0F) |
538219820Sjeff	     (0x0F & (*pp_mcm_port)->scope_state)) !=
539219820Sjeff	    (0x0F & (*pp_mcm_port)->scope_state)) {
540219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
541219820Sjeff			"Some bits in the request JoinState (0x%X) are not "
542219820Sjeff			"set in the stored port (0x%X)\n",
543219820Sjeff			(p_recvd_mcmember_rec->scope_state & 0x0F),
544219820Sjeff			(0x0F & (*pp_mcm_port)->scope_state));
545219820Sjeff		return FALSE;
546219820Sjeff	}
547219820Sjeff
548219820Sjeff	/* 4 */
549219820Sjeff	/* Validate according the the proxy_join (o15-0.1.2) */
550219820Sjeff	if (__validate_modify(sa, p_mgrp, p_mad_addr, p_recvd_mcmember_rec,
551219820Sjeff			      pp_mcm_port) == FALSE) {
552219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
553219820Sjeff			"proxy_join validation failure\n");
554219820Sjeff		return FALSE;
555219820Sjeff	}
556219820Sjeff	return TRUE;
557219820Sjeff}
558219820Sjeff
559219820Sjeff/**********************************************************************
560219820Sjeff **********************************************************************/
561219820Sjeff/*
562219820Sjeff * Check legality of the requested MGID (note this does not hold for SA
563219820Sjeff * created MGIDs)
564219820Sjeff *
565219820Sjeff * Implementing o15-0.1.5:
566219820Sjeff * A multicast GID is considered to be invalid if:
567219820Sjeff * 1. It does not comply with the rules as specified in 4.1.1 "GID Usage and
568219820Sjeff *    Properties" on page 145:
569219820Sjeff *
570219820Sjeff * 14) The multicast GID format is (bytes are comma sep):
571219820Sjeff *     0xff,<Fl><Sc>,<Si>,<Si>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<P>,<Id>,<Id>,<Id>,<Id>
572219820Sjeff *     Fl  4bit = Flags (b)
573219820Sjeff *     Sc  4bit = Scope (c)
574219820Sjeff *     Si 16bit = Signature (2)
575219820Sjeff *     P  64bit = GID Prefix (should be a subnet unique ID - normally Subnet Prefix)
576219820Sjeff *     Id 32bit = Unique ID in the Subnet (might be MLID or Pkey ?)
577219820Sjeff *
578219820Sjeff *  a) 8-bits of 11111111 at the start of the GID identifies this as being a
579219820Sjeff *     multicast GID.
580219820Sjeff *  b) Flags is a set of four 1-bit flags: 000T with three flags reserved
581219820Sjeff *     and defined as zero (0). The T flag is defined as follows:
582219820Sjeff *     i) T = 0 indicates this is a permanently assigned (i.e. wellknown)
583219820Sjeff *        multicast GID. See RFC 2373 and RFC 2375 as reference
584219820Sjeff *        for these permanently assigned GIDs.
585219820Sjeff *     ii) T = 1 indicates this is a non-permanently assigned (i.e. transient)
586219820Sjeff *        multicast GID.
587219820Sjeff *  c) Scope is a 4-bit multicast scope value used to limit the scope of
588219820Sjeff *     the multicast group. The following table defines scope value and
589219820Sjeff *     interpretation.
590219820Sjeff *
591219820Sjeff *     Multicast Address Scope Values:
592219820Sjeff *     0x2 Link-local
593219820Sjeff *     0x5 Site-local
594219820Sjeff *     0x8 Organization-local
595219820Sjeff *     0xE Global
596219820Sjeff *
597219820Sjeff * 2. It contains the SA-specific signature of 0xA01B and has the link-local
598219820Sjeff *    scope bits set. (EZ: the idea here is that SA created MGIDs are the
599219820Sjeff *    only source for this signature with link-local scope)
600219820Sjeff */
601219820Sjeffstatic ib_api_status_t
602219820Sjeff__validate_requested_mgid(IN osm_sa_t * sa,
603219820Sjeff			  IN const ib_member_rec_t * p_mcm_rec)
604219820Sjeff{
605219820Sjeff	uint16_t signature;
606219820Sjeff	boolean_t valid = TRUE;
607219820Sjeff
608219820Sjeff	OSM_LOG_ENTER(sa->p_log);
609219820Sjeff
610219820Sjeff	/* 14-a: mcast GID must start with 0xFF */
611219820Sjeff	if (p_mcm_rec->mgid.multicast.header[0] != 0xFF) {
612219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B01: "
613219820Sjeff			"Wrong MGID Prefix 0x%02X must be 0xFF\n",
614219820Sjeff			cl_ntoh16(p_mcm_rec->mgid.multicast.header[0]));
615219820Sjeff		valid = FALSE;
616219820Sjeff		goto Exit;
617219820Sjeff	}
618219820Sjeff
619219820Sjeff	/* the MGID signature can mark IPoIB or SA assigned MGIDs */
620219820Sjeff	memcpy(&signature, &(p_mcm_rec->mgid.multicast.raw_group_id),
621219820Sjeff	       sizeof(signature));
622219820Sjeff	signature = cl_ntoh16(signature);
623219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
624219820Sjeff		"MGID Signed as 0x%04X\n", signature);
625219820Sjeff
626219820Sjeff	/*
627219820Sjeff	 * We skip any checks for MGIDs that follow IPoIB
628219820Sjeff	 * GID structure as defined by the IETF ipoib-link-multicast.
629219820Sjeff	 *
630219820Sjeff	 * For IPv4 over IB, the signature will be "0x401B".
631219820Sjeff	 *
632219820Sjeff	 * |   8    |  4 |  4 |     16 bits     | 16 bits | 48 bits  | 32 bits |
633219820Sjeff	 * +--------+----+----+-----------------+---------+----------+---------+
634219820Sjeff	 * |11111111|0001|scop|<IPoIB signature>|< P_Key >|00.......0|<all 1's>|
635219820Sjeff	 * +--------+----+----+-----------------+---------+----------+---------+
636219820Sjeff	 *
637219820Sjeff	 * For IPv6 over IB, the signature will be "0x601B".
638219820Sjeff	 *
639219820Sjeff	 * |   8    |  4 |  4 |     16 bits     | 16 bits |       80 bits      |
640219820Sjeff	 * +--------+----+----+-----------------+---------+--------------------+
641219820Sjeff	 * |11111111|0001|scop|<IPoIB signature>|< P_Key >|000.............0001|
642219820Sjeff	 * +--------+----+----+-----------------+---------+--------------------+
643219820Sjeff	 *
644219820Sjeff	 */
645219820Sjeff	if (signature == 0x401B || signature == 0x601B) {
646219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
647219820Sjeff			"Skipping MGID Validation for IPoIB Signed (0x%04X) MGIDs\n",
648219820Sjeff			signature);
649219820Sjeff		goto Exit;
650219820Sjeff	}
651219820Sjeff
652219820Sjeff	/* 14-b: the 3 upper bits in the "flags" should be zero: */
653219820Sjeff	if (p_mcm_rec->mgid.multicast.header[1] & 0xE0) {
654219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B28: "
655219820Sjeff			"MGID uses Reserved Flags: flags=0x%X\n",
656219820Sjeff			(p_mcm_rec->mgid.multicast.header[1] & 0xE0) >> 4);
657219820Sjeff		valid = FALSE;
658219820Sjeff		goto Exit;
659219820Sjeff	}
660219820Sjeff
661219820Sjeff	/* 2 - now what if the link local format 0xA01B is used -
662219820Sjeff	   the scope should not be link local */
663219820Sjeff	if (signature == 0xA01B &&
664219820Sjeff	    (p_mcm_rec->mgid.multicast.header[1] & 0x0F) ==
665219820Sjeff	     IB_MC_SCOPE_LINK_LOCAL) {
666219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B24: "
667219820Sjeff			"MGID uses 0xA01B signature but with link-local scope\n");
668219820Sjeff		valid = FALSE;
669219820Sjeff		goto Exit;
670219820Sjeff	}
671219820Sjeff
672219820Sjeff	/*
673219820Sjeff	 * For SA assigned MGIDs (signature 0xA01B):
674219820Sjeff	 * There is no real way to make sure the Unique MGID Prefix is really unique.
675219820Sjeff	 * If we could enforce using the Subnet Prefix for that purpose it would
676219820Sjeff	 * have been nice. But the spec does not require it.
677219820Sjeff	 */
678219820Sjeff
679219820SjeffExit:
680219820Sjeff	OSM_LOG_EXIT(sa->p_log);
681219820Sjeff	return (valid);
682219820Sjeff}
683219820Sjeff
684219820Sjeff/**********************************************************************
685219820Sjeff Check if the requested new MC group parameters are realizable.
686219820Sjeff Also set the default MTU and Rate if not provided by the user.
687219820Sjeff**********************************************************************/
688219820Sjeffstatic boolean_t
689219820Sjeff__mgrp_request_is_realizable(IN osm_sa_t * sa,
690219820Sjeff			     IN ib_net64_t comp_mask,
691219820Sjeff			     IN ib_member_rec_t * p_mcm_rec,
692219820Sjeff			     IN const osm_physp_t * const p_physp)
693219820Sjeff{
694219820Sjeff	uint8_t mtu_sel = 2;	/* exactly */
695219820Sjeff	uint8_t mtu_required, mtu, port_mtu;
696219820Sjeff	uint8_t rate_sel = 2;	/* exactly */
697219820Sjeff	uint8_t rate_required, rate, port_rate;
698219820Sjeff	osm_log_t *p_log = sa->p_log;
699219820Sjeff
700219820Sjeff	OSM_LOG_ENTER(sa->p_log);
701219820Sjeff
702219820Sjeff	/*
703219820Sjeff	 * End of o15-0.2.3 specifies:
704219820Sjeff	 * ....
705219820Sjeff	 * The entity may also supply the other components such as HopLimit,
706219820Sjeff	 * MTU, etc. during group creation time. If these components are not
707219820Sjeff	 * provided during group creation time, SA will provide them for the
708219820Sjeff	 * group. The values chosen are vendor-dependent and beyond the scope
709219820Sjeff	 * of the specification.
710219820Sjeff	 *
711219820Sjeff	 * so we might also need to assign RATE/MTU if they are not comp
712219820Sjeff	 * masked in.
713219820Sjeff	 */
714219820Sjeff
715219820Sjeff	port_mtu = p_physp ? ib_port_info_get_mtu_cap(&p_physp->port_info) : 0;
716219820Sjeff	if (!(comp_mask & IB_MCR_COMPMASK_MTU) ||
717219820Sjeff	    !(comp_mask & IB_MCR_COMPMASK_MTU_SEL) ||
718219820Sjeff	    (mtu_sel = (p_mcm_rec->mtu >> 6)) == 3)
719219820Sjeff		mtu = port_mtu ? port_mtu : sa->p_subn->min_ca_mtu;
720219820Sjeff	else {
721219820Sjeff		mtu_required = (uint8_t) (p_mcm_rec->mtu & 0x3F);
722219820Sjeff		mtu = mtu_required;
723219820Sjeff		switch (mtu_sel) {
724219820Sjeff		case 0:	/* Greater than MTU specified */
725219820Sjeff			if (port_mtu && mtu_required >= port_mtu) {
726219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
727219820Sjeff					"Requested MTU %x >= the port\'s mtu:%x\n",
728219820Sjeff					mtu_required, port_mtu);
729219820Sjeff				return FALSE;
730219820Sjeff			}
731219820Sjeff			/* we provide the largest MTU possible if we can */
732219820Sjeff			if (port_mtu)
733219820Sjeff				mtu = port_mtu;
734219820Sjeff			else if (mtu_required < sa->p_subn->min_ca_mtu)
735219820Sjeff				mtu = sa->p_subn->min_ca_mtu;
736219820Sjeff			else
737219820Sjeff				mtu++;
738219820Sjeff			break;
739219820Sjeff		case 1:	/* Less than MTU specified */
740219820Sjeff			/* use the smaller of the two:
741219820Sjeff			   a. one lower then the required
742219820Sjeff			   b. the mtu of the requesting port (if exists) */
743219820Sjeff			if (port_mtu && mtu_required > port_mtu)
744219820Sjeff				mtu = port_mtu;
745219820Sjeff			else
746219820Sjeff				mtu--;
747219820Sjeff			break;
748219820Sjeff		case 2:	/* Exactly MTU specified */
749219820Sjeff		default:
750219820Sjeff			break;
751219820Sjeff		}
752219820Sjeff		/* make sure it still be in the range */
753219820Sjeff		if (mtu < IB_MIN_MTU || mtu > IB_MAX_MTU) {
754219820Sjeff			OSM_LOG(p_log, OSM_LOG_DEBUG,
755219820Sjeff				"Calculated MTU %x is out of range\n", mtu);
756219820Sjeff			return FALSE;
757219820Sjeff		}
758219820Sjeff	}
759219820Sjeff	p_mcm_rec->mtu = (mtu_sel << 6) | mtu;
760219820Sjeff
761219820Sjeff	port_rate =
762219820Sjeff	    p_physp ? ib_port_info_compute_rate(&p_physp->port_info) : 0;
763219820Sjeff	if (!(comp_mask & IB_MCR_COMPMASK_RATE)
764219820Sjeff	    || !(comp_mask & IB_MCR_COMPMASK_RATE_SEL)
765219820Sjeff	    || (rate_sel = (p_mcm_rec->rate >> 6)) == 3)
766219820Sjeff		rate = port_rate ? port_rate : sa->p_subn->min_ca_rate;
767219820Sjeff	else {
768219820Sjeff		rate_required = (uint8_t) (p_mcm_rec->rate & 0x3F);
769219820Sjeff		rate = rate_required;
770219820Sjeff		switch (rate_sel) {
771219820Sjeff		case 0:	/* Greater than RATE specified */
772219820Sjeff			if (port_rate && rate_required >= port_rate) {
773219820Sjeff				OSM_LOG(p_log, OSM_LOG_DEBUG,
774219820Sjeff					"Requested RATE %x >= the port\'s rate:%x\n",
775219820Sjeff					rate_required, port_rate);
776219820Sjeff				return FALSE;
777219820Sjeff			}
778219820Sjeff			/* we provide the largest RATE possible if we can */
779219820Sjeff			if (port_rate)
780219820Sjeff				rate = port_rate;
781219820Sjeff			else if (rate_required < sa->p_subn->min_ca_rate)
782219820Sjeff				rate = sa->p_subn->min_ca_rate;
783219820Sjeff			else
784219820Sjeff				rate++;
785219820Sjeff			break;
786219820Sjeff		case 1:	/* Less than RATE specified */
787219820Sjeff			/* use the smaller of the two:
788219820Sjeff			   a. one lower then the required
789219820Sjeff			   b. the rate of the requesting port (if exists) */
790219820Sjeff			if (port_rate && rate_required > port_rate)
791219820Sjeff				rate = port_rate;
792219820Sjeff			else
793219820Sjeff				rate--;
794219820Sjeff			break;
795219820Sjeff		case 2:	/* Exactly RATE specified */
796219820Sjeff		default:
797219820Sjeff			break;
798219820Sjeff		}
799219820Sjeff		/* make sure it still is in the range */
800219820Sjeff		if (rate < IB_MIN_RATE || rate > IB_MAX_RATE) {
801219820Sjeff			OSM_LOG(p_log, OSM_LOG_DEBUG,
802219820Sjeff				"Calculated RATE %x is out of range\n", rate);
803219820Sjeff			return FALSE;
804219820Sjeff		}
805219820Sjeff	}
806219820Sjeff	p_mcm_rec->rate = (rate_sel << 6) | rate;
807219820Sjeff
808219820Sjeff	OSM_LOG_EXIT(sa->p_log);
809219820Sjeff	return TRUE;
810219820Sjeff}
811219820Sjeff
812219820Sjeff/**********************************************************************
813219820Sjeff Call this function to create a new mgrp.
814219820Sjeff**********************************************************************/
815219820Sjeffib_api_status_t
816219820Sjeffosm_mcmr_rcv_create_new_mgrp(IN osm_sa_t * sa,
817219820Sjeff			     IN ib_net64_t comp_mask,
818219820Sjeff			     IN const ib_member_rec_t *
819219820Sjeff			     const p_recvd_mcmember_rec,
820219820Sjeff			     IN const osm_physp_t * const p_physp,
821219820Sjeff			     OUT osm_mgrp_t ** pp_mgrp)
822219820Sjeff{
823219820Sjeff	ib_net16_t mlid;
824219820Sjeff	unsigned zero_mgid, i;
825219820Sjeff	uint8_t scope;
826219820Sjeff	ib_gid_t *p_mgid;
827219820Sjeff	osm_mgrp_t *p_prev_mgrp;
828219820Sjeff	ib_api_status_t status = IB_SUCCESS;
829219820Sjeff	ib_member_rec_t mcm_rec = *p_recvd_mcmember_rec;	/* copy for modifications */
830219820Sjeff
831219820Sjeff	OSM_LOG_ENTER(sa->p_log);
832219820Sjeff
833219820Sjeff	/* but what if the given MGID was not 0 ? */
834219820Sjeff	zero_mgid = 1;
835219820Sjeff	for (i = 0; i < sizeof(p_recvd_mcmember_rec->mgid); i++)
836219820Sjeff		if (p_recvd_mcmember_rec->mgid.raw[i] != 0) {
837219820Sjeff			zero_mgid = 0;
838219820Sjeff			break;
839219820Sjeff		}
840219820Sjeff
841219820Sjeff	/*
842219820Sjeff	   we allocate a new mlid number before we might use it
843219820Sjeff	   for MGID ...
844219820Sjeff	 */
845219820Sjeff	mlid = __get_new_mlid(sa, mcm_rec.mlid);
846219820Sjeff	if (mlid == 0) {
847219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B19: "
848219820Sjeff			"__get_new_mlid failed request mlid 0x%04x\n", cl_ntoh16(mcm_rec.mlid));
849219820Sjeff		status = IB_SA_MAD_STATUS_NO_RESOURCES;
850219820Sjeff		goto Exit;
851219820Sjeff	}
852219820Sjeff
853219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
854219820Sjeff		"Obtained new mlid 0x%X\n", cl_ntoh16(mlid));
855219820Sjeff
856219820Sjeff	/* we need to create the new MGID if it was not defined */
857219820Sjeff	if (zero_mgid) {
858219820Sjeff		/* create a new MGID */
859219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
860219820Sjeff
861219820Sjeff		/* use the given scope state only if requested! */
862219820Sjeff		if (comp_mask & IB_MCR_COMPMASK_SCOPE)
863219820Sjeff			ib_member_get_scope_state(p_recvd_mcmember_rec->
864219820Sjeff						  scope_state, &scope, NULL);
865219820Sjeff		else
866219820Sjeff			/* to guarantee no collision with other subnets use local scope! */
867219820Sjeff			scope = IB_MC_SCOPE_LINK_LOCAL;
868219820Sjeff
869219820Sjeff		p_mgid = &(mcm_rec.mgid);
870219820Sjeff		p_mgid->raw[0] = 0xFF;
871219820Sjeff		p_mgid->raw[1] = 0x10 | scope;
872219820Sjeff		p_mgid->raw[2] = 0xA0;
873219820Sjeff		p_mgid->raw[3] = 0x1B;
874219820Sjeff
875219820Sjeff		/* HACK: use the SA port gid to make it globally unique */
876219820Sjeff		memcpy((&p_mgid->raw[4]),
877219820Sjeff		       &sa->p_subn->opt.subnet_prefix, sizeof(uint64_t));
878219820Sjeff
879219820Sjeff		/* HACK: how do we get a unique number - use the mlid twice */
880219820Sjeff		memcpy(&p_mgid->raw[10], &mlid, sizeof(uint16_t));
881219820Sjeff		memcpy(&p_mgid->raw[12], &mlid, sizeof(uint16_t));
882219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Allocated new MGID:%s\n",
883219820Sjeff			inet_ntop(AF_INET6, p_mgid->raw, gid_str,
884219820Sjeff				  sizeof gid_str));
885219820Sjeff	} else if (!__validate_requested_mgid(sa, &mcm_rec)) {
886219820Sjeff		/* a specific MGID was requested so validate the resulting MGID */
887219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B22: "
888219820Sjeff			"Invalid requested MGID\n");
889219820Sjeff		__free_mlid(sa, mlid);
890219820Sjeff		status = IB_SA_MAD_STATUS_REQ_INVALID;
891219820Sjeff		goto Exit;
892219820Sjeff	}
893219820Sjeff
894219820Sjeff	/* check the requested parameters are realizable */
895219820Sjeff	if (__mgrp_request_is_realizable(sa, comp_mask, &mcm_rec, p_physp) ==
896219820Sjeff	    FALSE) {
897219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B26: "
898219820Sjeff			"Requested MGRP parameters are not realizable\n");
899219820Sjeff		__free_mlid(sa, mlid);
900219820Sjeff		status = IB_SA_MAD_STATUS_REQ_INVALID;
901219820Sjeff		goto Exit;
902219820Sjeff	}
903219820Sjeff
904219820Sjeff	/* create a new MC Group */
905219820Sjeff	*pp_mgrp = osm_mgrp_new(mlid);
906219820Sjeff	if (*pp_mgrp == NULL) {
907219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B08: "
908219820Sjeff			"osm_mgrp_new failed\n");
909219820Sjeff		__free_mlid(sa, mlid);
910219820Sjeff		status = IB_SA_MAD_STATUS_NO_RESOURCES;
911219820Sjeff		goto Exit;
912219820Sjeff	}
913219820Sjeff
914219820Sjeff	/* Initialize the mgrp */
915219820Sjeff	(*pp_mgrp)->mcmember_rec = mcm_rec;
916219820Sjeff	(*pp_mgrp)->mcmember_rec.mlid = mlid;
917219820Sjeff
918219820Sjeff	/* the mcmember_record should have mtu_sel, rate_sel, and pkt_lifetime_sel = 2 */
919219820Sjeff	(*pp_mgrp)->mcmember_rec.mtu &= 0x3f;
920219820Sjeff	(*pp_mgrp)->mcmember_rec.mtu |= 2 << 6;	/* exactly */
921219820Sjeff	(*pp_mgrp)->mcmember_rec.rate &= 0x3f;
922219820Sjeff	(*pp_mgrp)->mcmember_rec.rate |= 2 << 6;	/* exactly */
923219820Sjeff	(*pp_mgrp)->mcmember_rec.pkt_life &= 0x3f;
924219820Sjeff	(*pp_mgrp)->mcmember_rec.pkt_life |= 2 << 6;	/* exactly */
925219820Sjeff
926219820Sjeff	/* Insert the new group in the data base */
927219820Sjeff
928219820Sjeff	/* since we might have an old group by that mlid
929219820Sjeff	   one whose deletion was delayed for an idle time
930219820Sjeff	   we need to deallocate it first */
931219820Sjeff	p_prev_mgrp = osm_get_mgrp_by_mlid(sa->p_subn, mlid);
932219820Sjeff	if (p_prev_mgrp) {
933219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
934219820Sjeff			"Found previous group for mlid:0x%04x - "
935219820Sjeff			"Destroying it first\n",
936219820Sjeff			cl_ntoh16(mlid));
937219820Sjeff		sa->p_subn->mgroups[cl_ntoh16(mlid) - IB_LID_MCAST_START_HO] = NULL;
938219820Sjeff		osm_mgrp_delete(p_prev_mgrp);
939219820Sjeff	}
940219820Sjeff
941219820Sjeff	sa->p_subn->mgroups[cl_ntoh16(mlid) - IB_LID_MCAST_START_HO] = *pp_mgrp;
942219820Sjeff
943219820SjeffExit:
944219820Sjeff	OSM_LOG_EXIT(sa->p_log);
945219820Sjeff	return status;
946219820Sjeff}
947219820Sjeff
948219820Sjeff/**********************************************************************
949219820Sjeff *********************************************************************/
950219820Sjeffstatic unsigned match_mgrp_by_mgid(IN osm_mgrp_t * const p_mgrp, ib_gid_t *mgid)
951219820Sjeff{
952219820Sjeff	/* ignore groups marked for deletion */
953219820Sjeff	if (p_mgrp->to_be_deleted ||
954219820Sjeff	    memcmp(&p_mgrp->mcmember_rec.mgid, mgid, sizeof(ib_gid_t)))
955219820Sjeff		return 0;
956219820Sjeff	else
957219820Sjeff		return 1;
958219820Sjeff}
959219820Sjeff
960219820Sjeff/**********************************************************************
961219820Sjeff **********************************************************************/
962219820Sjeff#define PREFIX_MASK CL_HTON64(0xff10ffff0000ffffULL)
963219820Sjeff#define PREFIX_SIGNATURE CL_HTON64(0xff10601b00000000ULL)
964219820Sjeff#define INT_ID_MASK CL_HTON64(0xfffffff1ff000000ULL)
965219820Sjeff#define INT_ID_SIGNATURE CL_HTON64(0x00000001ff000000ULL)
966219820Sjeff
967219820Sjeff/* Special Case IPv6 Solicited Node Multicast (SNM) addresses */
968219820Sjeff/* 0xff1Z601bXXXX0000 : 0x00000001ffYYYYYY */
969219820Sjeff/* Where Z is the scope, XXXX is the P_Key, and
970219820Sjeff * YYYYYY is the last 24 bits of the port guid */
971219820Sjeffstatic unsigned match_and_update_ipv6_snm_mgid(ib_gid_t *mgid)
972219820Sjeff{
973219820Sjeff	if ((mgid->unicast.prefix & PREFIX_MASK) == PREFIX_SIGNATURE &&
974219820Sjeff	    (mgid->unicast.interface_id & INT_ID_MASK) == INT_ID_SIGNATURE) {
975219820Sjeff		mgid->unicast.prefix &= PREFIX_MASK;
976219820Sjeff		mgid->unicast.interface_id &= INT_ID_MASK;
977219820Sjeff		return 1;
978219820Sjeff	}
979219820Sjeff	return 0;
980219820Sjeff}
981219820Sjeff
982219820Sjeffosm_mgrp_t *osm_get_mgrp_by_mgid(IN osm_sa_t *sa, IN ib_gid_t *p_mgid)
983219820Sjeff{
984219820Sjeff	int i;
985219820Sjeff
986219820Sjeff	if (sa->p_subn->opt.consolidate_ipv6_snm_req &&
987219820Sjeff	    match_and_update_ipv6_snm_mgid(p_mgid)) {
988219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
989219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
990219820Sjeff			"Special Case Solicited Node Mcast Join for MGID %s\n",
991219820Sjeff			inet_ntop(AF_INET6, p_mgid->raw, gid_str,
992219820Sjeff				  sizeof gid_str));
993219820Sjeff	}
994219820Sjeff
995219820Sjeff	for (i = 0; i <= sa->p_subn->max_mcast_lid_ho - IB_LID_MCAST_START_HO;
996219820Sjeff	     i++)
997219820Sjeff		if (sa->p_subn->mgroups[i] &&
998219820Sjeff		    match_mgrp_by_mgid(sa->p_subn->mgroups[i], p_mgid))
999219820Sjeff			return sa->p_subn->mgroups[i];
1000219820Sjeff
1001219820Sjeff	return NULL;
1002219820Sjeff}
1003219820Sjeff
1004219820Sjeff/**********************************************************************
1005219820Sjeff Call this function to find or create a new mgrp.
1006219820Sjeff**********************************************************************/
1007219820Sjeffib_api_status_t
1008219820Sjeffosm_mcmr_rcv_find_or_create_new_mgrp(IN osm_sa_t * sa,
1009219820Sjeff				     IN ib_net64_t comp_mask,
1010219820Sjeff				     IN ib_member_rec_t *
1011219820Sjeff				     const p_recvd_mcmember_rec,
1012219820Sjeff				     OUT osm_mgrp_t ** pp_mgrp)
1013219820Sjeff{
1014219820Sjeff	osm_mgrp_t *mgrp;
1015219820Sjeff
1016219820Sjeff	if ((mgrp = osm_get_mgrp_by_mgid(sa, &p_recvd_mcmember_rec->mgid))) {
1017219820Sjeff		*pp_mgrp = mgrp;
1018219820Sjeff		return IB_SUCCESS;
1019219820Sjeff	}
1020219820Sjeff	return osm_mcmr_rcv_create_new_mgrp(sa, comp_mask,
1021219820Sjeff					    p_recvd_mcmember_rec, NULL,
1022219820Sjeff					    pp_mgrp);
1023219820Sjeff}
1024219820Sjeff
1025219820Sjeff/*********************************************************************
1026219820SjeffProcess a request for leaving the group
1027219820Sjeff**********************************************************************/
1028219820Sjeffstatic void
1029219820Sjeff__osm_mcmr_rcv_leave_mgrp(IN osm_sa_t * sa,
1030219820Sjeff			  IN osm_madw_t * const p_madw)
1031219820Sjeff{
1032219820Sjeff	osm_mgrp_t *p_mgrp;
1033219820Sjeff	ib_sa_mad_t *p_sa_mad;
1034219820Sjeff	ib_member_rec_t *p_recvd_mcmember_rec;
1035219820Sjeff	ib_member_rec_t mcmember_rec;
1036219820Sjeff	ib_net16_t mlid;
1037219820Sjeff	ib_net64_t portguid;
1038219820Sjeff	osm_mcm_port_t *p_mcm_port;
1039219820Sjeff	int removed;
1040219820Sjeff
1041219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1042219820Sjeff
1043219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1044219820Sjeff	p_recvd_mcmember_rec =
1045219820Sjeff	    (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1046219820Sjeff
1047219820Sjeff	mcmember_rec = *p_recvd_mcmember_rec;
1048219820Sjeff
1049219820Sjeff	if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG)) {
1050219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Dump of record\n");
1051219820Sjeff		osm_dump_mc_record(sa->p_log, &mcmember_rec, OSM_LOG_DEBUG);
1052219820Sjeff	}
1053219820Sjeff
1054219820Sjeff	CL_PLOCK_EXCL_ACQUIRE(sa->p_lock);
1055219820Sjeff	p_mgrp = osm_get_mgrp_by_mgid(sa, &p_recvd_mcmember_rec->mgid);
1056219820Sjeff	if (!p_mgrp) {
1057219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
1058219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1059219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1060219820Sjeff			"Failed since multicast group %s not present\n",
1061219820Sjeff			inet_ntop(AF_INET6, p_recvd_mcmember_rec->mgid.raw,
1062219820Sjeff				gid_str, sizeof gid_str));
1063219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1064219820Sjeff		goto Exit;
1065219820Sjeff	}
1066219820Sjeff
1067219820Sjeff	mlid = p_mgrp->mlid;
1068219820Sjeff	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
1069219820Sjeff
1070219820Sjeff	/* check validity of the delete request o15-0.1.14 */
1071219820Sjeff	if (!__validate_delete(sa, p_mgrp, osm_madw_get_mad_addr_ptr(p_madw),
1072219820Sjeff			       p_recvd_mcmember_rec, &p_mcm_port)) {
1073219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
1074219820Sjeff		char gid_str2[INET6_ADDRSTRLEN];
1075219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1076219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B25: "
1077219820Sjeff			"Received an invalid delete request for "
1078219820Sjeff			"MGID: %s for PortGID: %s\n",
1079219820Sjeff			inet_ntop(AF_INET6, p_recvd_mcmember_rec->mgid.raw,
1080219820Sjeff				  gid_str, sizeof gid_str),
1081219820Sjeff			inet_ntop(AF_INET6, p_recvd_mcmember_rec->port_gid.raw,
1082219820Sjeff				  gid_str2, sizeof gid_str2));
1083219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1084219820Sjeff		goto Exit;
1085219820Sjeff	}
1086219820Sjeff
1087219820Sjeff	/* store state - we'll need it if the port is removed */
1088219820Sjeff	mcmember_rec.scope_state = p_mcm_port->scope_state;
1089219820Sjeff
1090219820Sjeff	/* remove port or update join state */
1091219820Sjeff	removed = osm_mgrp_remove_port(sa->p_subn, sa->p_log, p_mgrp, p_mcm_port,
1092219820Sjeff				       p_recvd_mcmember_rec->scope_state&0x0F);
1093219820Sjeff	if (!removed)
1094219820Sjeff		mcmember_rec.scope_state = p_mcm_port->scope_state;
1095219820Sjeff
1096219820Sjeff	CL_PLOCK_RELEASE(sa->p_lock);
1097219820Sjeff
1098219820Sjeff	/* we can leave if port was deleted from MCG */
1099219820Sjeff	if (removed && osm_sm_mcgrp_leave(sa->sm, mlid, portguid))
1100219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B09: "
1101219820Sjeff			"osm_sm_mcgrp_leave failed\n");
1102219820Sjeff
1103219820Sjeff	/* Send an SA response */
1104219820Sjeff	__osm_mcmr_rcv_respond(sa, p_madw, &mcmember_rec);
1105219820Sjeff
1106219820SjeffExit:
1107219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1108219820Sjeff}
1109219820Sjeff
1110219820Sjeff/**********************************************************************
1111219820Sjeff Handle a join (or create) request
1112219820Sjeff**********************************************************************/
1113219820Sjeffstatic void
1114219820Sjeff__osm_mcmr_rcv_join_mgrp(IN osm_sa_t * sa, IN osm_madw_t * const p_madw)
1115219820Sjeff{
1116219820Sjeff	osm_mgrp_t *p_mgrp = NULL;
1117219820Sjeff	ib_api_status_t status;
1118219820Sjeff	ib_sa_mad_t *p_sa_mad;
1119219820Sjeff	ib_member_rec_t *p_recvd_mcmember_rec;
1120219820Sjeff	ib_member_rec_t mcmember_rec;
1121219820Sjeff	ib_net16_t mlid;
1122219820Sjeff	osm_mcm_port_t *p_mcmr_port;
1123219820Sjeff	ib_net64_t portguid;
1124219820Sjeff	osm_port_t *p_port;
1125219820Sjeff	osm_physp_t *p_physp;
1126219820Sjeff	osm_physp_t *p_request_physp;
1127219820Sjeff	uint8_t is_new_group;	/* TRUE = there is a need to create a group */
1128219820Sjeff	osm_mcast_req_type_t req_type;
1129219820Sjeff	uint8_t join_state;
1130219820Sjeff
1131219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1132219820Sjeff
1133219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1134219820Sjeff	p_recvd_mcmember_rec = ib_sa_mad_get_payload_ptr(p_sa_mad);
1135219820Sjeff
1136219820Sjeff	portguid = p_recvd_mcmember_rec->port_gid.unicast.interface_id;
1137219820Sjeff
1138219820Sjeff	mcmember_rec = *p_recvd_mcmember_rec;
1139219820Sjeff
1140219820Sjeff	if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG)) {
1141219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Dump of incoming record\n");
1142219820Sjeff		osm_dump_mc_record(sa->p_log, &mcmember_rec, OSM_LOG_DEBUG);
1143219820Sjeff	}
1144219820Sjeff
1145219820Sjeff	CL_PLOCK_EXCL_ACQUIRE(sa->p_lock);
1146219820Sjeff
1147219820Sjeff	/* make sure the requested port guid is known to the SM */
1148219820Sjeff	p_port = osm_get_port_by_guid(sa->p_subn, portguid);
1149219820Sjeff	if (!p_port) {
1150219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1151219820Sjeff
1152219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1153219820Sjeff			"Unknown port GUID 0x%016" PRIx64 "\n",
1154219820Sjeff			cl_ntoh64(portguid));
1155219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1156219820Sjeff		goto Exit;
1157219820Sjeff	}
1158219820Sjeff
1159219820Sjeff	p_physp = p_port->p_physp;
1160219820Sjeff	/* Check that the p_physp and the requester physp are in the same
1161219820Sjeff	   partition. */
1162219820Sjeff	p_request_physp =
1163219820Sjeff	    osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
1164219820Sjeff				      osm_madw_get_mad_addr_ptr(p_madw));
1165219820Sjeff	if (p_request_physp == NULL) {
1166219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1167219820Sjeff		goto Exit;
1168219820Sjeff	}
1169219820Sjeff
1170219820Sjeff	if (!osm_physp_share_pkey(sa->p_log, p_physp, p_request_physp)) {
1171219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1172219820Sjeff
1173219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1174219820Sjeff			"Port and requester don't share pkey\n");
1175219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1176219820Sjeff		goto Exit;
1177219820Sjeff	}
1178219820Sjeff
1179219820Sjeff	ib_member_get_scope_state(p_recvd_mcmember_rec->scope_state, NULL,
1180219820Sjeff				  &join_state);
1181219820Sjeff
1182219820Sjeff	/* do we need to create a new group? */
1183219820Sjeff	p_mgrp = osm_get_mgrp_by_mgid(sa, &p_recvd_mcmember_rec->mgid);
1184219820Sjeff	if (!p_mgrp || p_mgrp->to_be_deleted) {
1185219820Sjeff		/* check for JoinState.FullMember = 1 o15.0.1.9 */
1186219820Sjeff		if ((join_state & 0x01) != 0x01) {
1187219820Sjeff			char gid_str[INET6_ADDRSTRLEN];
1188219820Sjeff			CL_PLOCK_RELEASE(sa->p_lock);
1189219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B10: "
1190219820Sjeff				"Provided Join State != FullMember - "
1191219820Sjeff				"required for create, "
1192219820Sjeff				"MGID: %s from port 0x%016" PRIx64 " (%s)\n",
1193219820Sjeff				inet_ntop(AF_INET6,
1194219820Sjeff					p_recvd_mcmember_rec->mgid.raw,
1195219820Sjeff					gid_str, sizeof gid_str),
1196219820Sjeff				cl_ntoh64(portguid),
1197219820Sjeff				p_port->p_node->print_desc);
1198219820Sjeff			osm_sa_send_error(sa, p_madw,
1199219820Sjeff					  IB_SA_MAD_STATUS_REQ_INVALID);
1200219820Sjeff			goto Exit;
1201219820Sjeff		}
1202219820Sjeff
1203219820Sjeff		/* check the comp_mask */
1204219820Sjeff		if (!__check_create_comp_mask(p_sa_mad->comp_mask,
1205219820Sjeff					      p_recvd_mcmember_rec)) {
1206219820Sjeff			char gid_str[INET6_ADDRSTRLEN];
1207219820Sjeff			CL_PLOCK_RELEASE(sa->p_lock);
1208219820Sjeff
1209219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B11: "
1210219820Sjeff				"method = %s, scope_state = 0x%x, "
1211219820Sjeff				"component mask = 0x%016" PRIx64 ", "
1212219820Sjeff				"expected comp mask = 0x%016" PRIx64 ", "
1213219820Sjeff				"MGID: %s from port 0x%016" PRIx64 " (%s)\n",
1214219820Sjeff				ib_get_sa_method_str(p_sa_mad->method),
1215219820Sjeff				p_recvd_mcmember_rec->scope_state,
1216219820Sjeff				cl_ntoh64(p_sa_mad->comp_mask),
1217219820Sjeff				CL_NTOH64(REQUIRED_MC_CREATE_COMP_MASK),
1218219820Sjeff				inet_ntop(AF_INET6,
1219219820Sjeff					p_recvd_mcmember_rec->mgid.raw,
1220219820Sjeff					gid_str, sizeof gid_str),
1221219820Sjeff				cl_ntoh64(portguid),
1222219820Sjeff				p_port->p_node->print_desc);
1223219820Sjeff
1224219820Sjeff			osm_sa_send_error(sa, p_madw,
1225219820Sjeff					  IB_SA_MAD_STATUS_INSUF_COMPS);
1226219820Sjeff			goto Exit;
1227219820Sjeff		}
1228219820Sjeff
1229219820Sjeff		status = osm_mcmr_rcv_create_new_mgrp(sa, p_sa_mad->comp_mask,
1230219820Sjeff						      p_recvd_mcmember_rec,
1231219820Sjeff						      p_physp, &p_mgrp);
1232219820Sjeff		if (status != IB_SUCCESS) {
1233219820Sjeff			CL_PLOCK_RELEASE(sa->p_lock);
1234219820Sjeff			osm_sa_send_error(sa, p_madw, status);
1235219820Sjeff			goto Exit;
1236219820Sjeff		}
1237219820Sjeff		/* copy the MGID to the result */
1238219820Sjeff		mcmember_rec.mgid = p_mgrp->mcmember_rec.mgid;
1239219820Sjeff		is_new_group = 1;
1240219820Sjeff		req_type = OSM_MCAST_REQ_TYPE_CREATE;
1241219820Sjeff	} else {
1242219820Sjeff		/* no need for a new group */
1243219820Sjeff		is_new_group = 0;
1244219820Sjeff		req_type = OSM_MCAST_REQ_TYPE_JOIN;
1245219820Sjeff	}
1246219820Sjeff
1247219820Sjeff	CL_ASSERT(p_mgrp);
1248219820Sjeff	mlid = p_mgrp->mlid;
1249219820Sjeff
1250219820Sjeff	/*
1251219820Sjeff	 * o15-0.2.4: If SA supports UD multicast, then SA shall cause an
1252219820Sjeff	 * endport to join an existing multicast group if:
1253219820Sjeff	 * 1. It receives a SubnAdmSet() method for a MCMemberRecord, and
1254219820Sjeff	 *    - WE KNOW THAT ALREADY
1255219820Sjeff	 * 2. The MGID is specified and matches an existing multicast
1256219820Sjeff	 *    group, and
1257219820Sjeff	 *    - WE KNOW THAT ALREADY
1258219820Sjeff	 * 3. The MCMemberRecord:JoinState is not all 0s, and
1259219820Sjeff	 * 4. PortGID is specified and
1260219820Sjeff	 *    - WE KNOW THAT ALREADY (as it matched a real one)
1261219820Sjeff	 * 5. All other components match that existing group, either by
1262219820Sjeff	 *    being wildcarded or by having values identical to those specified
1263219820Sjeff	 *    by the component mask and in use by the group with the exception
1264219820Sjeff	 *    of components such as ProxyJoin and Reserved, which are ignored
1265219820Sjeff	 *    by SA.
1266219820Sjeff	 *
1267219820Sjeff	 * We need to check #3 and #5 here:
1268219820Sjeff	 */
1269219820Sjeff	if (!__validate_more_comp_fields(sa->p_log, p_mgrp,
1270219820Sjeff					 p_recvd_mcmember_rec,
1271219820Sjeff					 p_sa_mad->comp_mask)
1272219820Sjeff	    || !__validate_port_caps(sa->p_log, p_mgrp, p_physp)
1273219820Sjeff	    || !(join_state != 0)) {
1274219820Sjeff		/* since we might have created the new group we need to cleanup */
1275219820Sjeff		__cleanup_mgrp(sa, p_mgrp);
1276219820Sjeff
1277219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1278219820Sjeff
1279219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B12: "
1280219820Sjeff			"__validate_more_comp_fields, __validate_port_caps, "
1281219820Sjeff			"or JoinState = 0 failed from port 0x%016" PRIx64
1282219820Sjeff			" (%s), " "sending IB_SA_MAD_STATUS_REQ_INVALID\n",
1283219820Sjeff			cl_ntoh64(portguid), p_port->p_node->print_desc);
1284219820Sjeff
1285219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1286219820Sjeff		goto Exit;
1287219820Sjeff	}
1288219820Sjeff
1289219820Sjeff	/*
1290219820Sjeff	 * o15-0.2.1 requires validation of the requesting port
1291219820Sjeff	 * in the case of modification:
1292219820Sjeff	 */
1293219820Sjeff	if (!is_new_group &&
1294219820Sjeff	    !__validate_modify(sa, p_mgrp, osm_madw_get_mad_addr_ptr(p_madw),
1295219820Sjeff			       p_recvd_mcmember_rec, &p_mcmr_port)) {
1296219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1297219820Sjeff
1298219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B13: "
1299219820Sjeff			"__validate_modify failed from port 0x%016" PRIx64
1300219820Sjeff			" (%s), sending IB_SA_MAD_STATUS_REQ_INVALID\n",
1301219820Sjeff			cl_ntoh64(portguid), p_port->p_node->print_desc);
1302219820Sjeff
1303219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
1304219820Sjeff		goto Exit;
1305219820Sjeff	}
1306219820Sjeff
1307219820Sjeff	/* create or update existing port (join-state will be updated) */
1308219820Sjeff	status = __add_new_mgrp_port(sa, p_mgrp, p_recvd_mcmember_rec,
1309219820Sjeff				     osm_madw_get_mad_addr_ptr(p_madw),
1310219820Sjeff				     &p_mcmr_port);
1311219820Sjeff
1312219820Sjeff	if (status != IB_SUCCESS) {
1313219820Sjeff		/* we fail to add the port so we might need to delete the group */
1314219820Sjeff		__cleanup_mgrp(sa, p_mgrp);
1315219820Sjeff
1316219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1317219820Sjeff
1318219820Sjeff		osm_sa_send_error(sa, p_madw, status == IB_INVALID_PARAMETER ?
1319219820Sjeff				  IB_SA_MAD_STATUS_REQ_INVALID :
1320219820Sjeff				  IB_SA_MAD_STATUS_NO_RESOURCES);
1321219820Sjeff		goto Exit;
1322219820Sjeff	}
1323219820Sjeff
1324219820Sjeff	/* o15.0.1.11: copy the join state */
1325219820Sjeff	mcmember_rec.scope_state = p_mcmr_port->scope_state;
1326219820Sjeff
1327219820Sjeff	/* copy qkey mlid tclass pkey sl_flow_hop mtu rate pkt_life sl_flow_hop */
1328219820Sjeff	__copy_from_create_mc_rec(&mcmember_rec, &p_mgrp->mcmember_rec);
1329219820Sjeff
1330219820Sjeff	/* Release the lock as we don't need it. */
1331219820Sjeff	CL_PLOCK_RELEASE(sa->p_lock);
1332219820Sjeff
1333219820Sjeff	/* do the actual routing (actually schedule the update) */
1334219820Sjeff	status = osm_sm_mcgrp_join(sa->sm, mlid,
1335219820Sjeff				   p_recvd_mcmember_rec->port_gid.unicast.
1336219820Sjeff				   interface_id, req_type);
1337219820Sjeff
1338219820Sjeff	if (status != IB_SUCCESS) {
1339219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B14: "
1340219820Sjeff			"osm_sm_mcgrp_join failed from port 0x%016" PRIx64
1341219820Sjeff			" (%s), " "sending IB_SA_MAD_STATUS_NO_RESOURCES\n",
1342219820Sjeff			cl_ntoh64(portguid), p_port->p_node->print_desc);
1343219820Sjeff
1344219820Sjeff		CL_PLOCK_EXCL_ACQUIRE(sa->p_lock);
1345219820Sjeff
1346219820Sjeff		/* the request for routing failed so we need to remove the port */
1347219820Sjeff		osm_mgrp_delete_port(sa->p_subn, sa->p_log, p_mgrp,
1348219820Sjeff				     p_recvd_mcmember_rec->port_gid.
1349219820Sjeff				     unicast.interface_id);
1350219820Sjeff		__cleanup_mgrp(sa, p_mgrp);
1351219820Sjeff		CL_PLOCK_RELEASE(sa->p_lock);
1352219820Sjeff		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_NO_RESOURCES);
1353219820Sjeff		goto Exit;
1354219820Sjeff
1355219820Sjeff	}
1356219820Sjeff	/* failed to route */
1357219820Sjeff	if (osm_log_is_active(sa->p_log, OSM_LOG_DEBUG))
1358219820Sjeff		osm_dump_mc_record(sa->p_log, &mcmember_rec, OSM_LOG_DEBUG);
1359219820Sjeff
1360219820Sjeff	__osm_mcmr_rcv_respond(sa, p_madw, &mcmember_rec);
1361219820Sjeff
1362219820SjeffExit:
1363219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1364219820Sjeff}
1365219820Sjeff
1366219820Sjeff/**********************************************************************
1367219820Sjeff Add a patched multicast group to the results list
1368219820Sjeff**********************************************************************/
1369219820Sjeffstatic ib_api_status_t
1370219820Sjeff__osm_mcmr_rcv_new_mcmr(IN osm_sa_t * sa,
1371219820Sjeff			IN const ib_member_rec_t * p_rcvd_rec,
1372219820Sjeff			IN cl_qlist_t * const p_list)
1373219820Sjeff{
1374219820Sjeff	osm_mcmr_item_t *p_rec_item;
1375219820Sjeff	ib_api_status_t status = IB_SUCCESS;
1376219820Sjeff
1377219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1378219820Sjeff
1379219820Sjeff	p_rec_item = malloc(sizeof(*p_rec_item));
1380219820Sjeff	if (p_rec_item == NULL) {
1381219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B15: "
1382219820Sjeff			"rec_item alloc failed\n");
1383219820Sjeff		status = IB_INSUFFICIENT_RESOURCES;
1384219820Sjeff		goto Exit;
1385219820Sjeff	}
1386219820Sjeff
1387219820Sjeff	memset(p_rec_item, 0, sizeof(*p_rec_item));
1388219820Sjeff
1389219820Sjeff	/* HACK: Untrusted requesters should result with 0 Join
1390219820Sjeff	   State, Port Guid, and Proxy */
1391219820Sjeff	p_rec_item->rec = *p_rcvd_rec;
1392219820Sjeff	cl_qlist_insert_tail(p_list, &p_rec_item->list_item);
1393219820Sjeff
1394219820SjeffExit:
1395219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1396219820Sjeff	return (status);
1397219820Sjeff}
1398219820Sjeff
1399219820Sjeff/**********************************************************************
1400219820Sjeff Match the given mgrp to the requested mcmr
1401219820Sjeff**********************************************************************/
1402219820Sjeffstatic void mcmr_by_comp_mask(osm_sa_t *sa, const ib_member_rec_t *p_rcvd_rec,
1403219820Sjeff			      ib_net64_t comp_mask, osm_mgrp_t *p_mgrp,
1404219820Sjeff			      const osm_physp_t *p_req_physp,
1405219820Sjeff			      boolean_t trusted_req, cl_qlist_t *list)
1406219820Sjeff{
1407219820Sjeff	/* since we might change scope_state */
1408219820Sjeff	ib_member_rec_t match_rec;
1409219820Sjeff	osm_mcm_port_t *p_mcm_port;
1410219820Sjeff	ib_net64_t portguid = p_rcvd_rec->port_gid.unicast.interface_id;
1411219820Sjeff	/* will be used for group or port info */
1412219820Sjeff	uint8_t scope_state;
1413219820Sjeff	uint8_t scope_state_mask = 0;
1414219820Sjeff	cl_map_item_t *p_item;
1415219820Sjeff	ib_gid_t port_gid;
1416219820Sjeff	boolean_t proxy_join = FALSE;
1417219820Sjeff
1418219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1419219820Sjeff
1420219820Sjeff	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1421219820Sjeff		"Checking mlid:0x%X\n", cl_ntoh16(p_mgrp->mlid));
1422219820Sjeff
1423219820Sjeff	/* the group might be marked for deletion */
1424219820Sjeff	if (p_mgrp->to_be_deleted) {
1425219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1426219820Sjeff			"Group mlid:0x%X is marked to be deleted\n",
1427219820Sjeff			cl_ntoh16(p_mgrp->mlid));
1428219820Sjeff		goto Exit;
1429219820Sjeff	}
1430219820Sjeff
1431219820Sjeff	/* first try to eliminate the group by MGID, MLID, or P_Key */
1432219820Sjeff	if ((IB_MCR_COMPMASK_MGID & comp_mask) &&
1433219820Sjeff	    memcmp(&p_rcvd_rec->mgid, &p_mgrp->mcmember_rec.mgid,
1434219820Sjeff		   sizeof(ib_gid_t)))
1435219820Sjeff		goto Exit;
1436219820Sjeff
1437219820Sjeff	if ((IB_MCR_COMPMASK_MLID & comp_mask) &&
1438219820Sjeff	    memcmp(&p_rcvd_rec->mlid, &p_mgrp->mcmember_rec.mlid,
1439219820Sjeff		   sizeof(uint16_t)))
1440219820Sjeff		goto Exit;
1441219820Sjeff
1442219820Sjeff	/* if the requester physical port doesn't have the pkey that is defined
1443219820Sjeff	   for the group - exit. */
1444219820Sjeff	if (!osm_physp_has_pkey(sa->p_log, p_mgrp->mcmember_rec.pkey,
1445219820Sjeff				p_req_physp))
1446219820Sjeff		goto Exit;
1447219820Sjeff
1448219820Sjeff	/* now do the rest of the match */
1449219820Sjeff	if ((IB_MCR_COMPMASK_QKEY & comp_mask) &&
1450219820Sjeff	    p_rcvd_rec->qkey != p_mgrp->mcmember_rec.qkey)
1451219820Sjeff		goto Exit;
1452219820Sjeff
1453219820Sjeff	if ((IB_MCR_COMPMASK_PKEY & comp_mask) &&
1454219820Sjeff	    p_rcvd_rec->pkey != p_mgrp->mcmember_rec.pkey)
1455219820Sjeff		goto Exit;
1456219820Sjeff
1457219820Sjeff	if ((IB_MCR_COMPMASK_TCLASS & comp_mask) &&
1458219820Sjeff	    p_rcvd_rec->tclass != p_mgrp->mcmember_rec.tclass)
1459219820Sjeff		goto Exit;
1460219820Sjeff
1461219820Sjeff	/* check SL, Flow, and Hop limit */
1462219820Sjeff	{
1463219820Sjeff		uint8_t mgrp_sl, query_sl;
1464219820Sjeff		uint32_t mgrp_flow, query_flow;
1465219820Sjeff		uint8_t mgrp_hop, query_hop;
1466219820Sjeff
1467219820Sjeff		ib_member_get_sl_flow_hop(p_rcvd_rec->sl_flow_hop,
1468219820Sjeff					  &query_sl, &query_flow, &query_hop);
1469219820Sjeff
1470219820Sjeff		ib_member_get_sl_flow_hop(p_mgrp->mcmember_rec.sl_flow_hop,
1471219820Sjeff					  &mgrp_sl, &mgrp_flow, &mgrp_hop);
1472219820Sjeff
1473219820Sjeff		if ((IB_MCR_COMPMASK_SL & comp_mask) && query_sl != mgrp_sl)
1474219820Sjeff			goto Exit;
1475219820Sjeff
1476219820Sjeff		if ((IB_MCR_COMPMASK_FLOW & comp_mask) &&
1477219820Sjeff		     query_flow != mgrp_flow)
1478219820Sjeff			goto Exit;
1479219820Sjeff
1480219820Sjeff		if ((IB_MCR_COMPMASK_HOP & comp_mask) && query_hop != mgrp_hop)
1481219820Sjeff			goto Exit;
1482219820Sjeff	}
1483219820Sjeff
1484219820Sjeff	if ((IB_MCR_COMPMASK_PROXY & comp_mask) &&
1485219820Sjeff	    p_rcvd_rec->proxy_join != p_mgrp->mcmember_rec.proxy_join)
1486219820Sjeff		goto Exit;
1487219820Sjeff
1488219820Sjeff	/* need to validate mtu, rate, and pkt_lifetime fields */
1489219820Sjeff	if (__validate_more_comp_fields(sa->p_log, p_mgrp, p_rcvd_rec,
1490219820Sjeff					comp_mask) == FALSE)
1491219820Sjeff		goto Exit;
1492219820Sjeff
1493219820Sjeff	/* Port specific fields */
1494219820Sjeff	/* so did we get the PortGUID mask */
1495219820Sjeff	if (IB_MCR_COMPMASK_PORT_GID & comp_mask) {
1496219820Sjeff		/* try to find this port */
1497219820Sjeff		if (osm_mgrp_is_port_present(p_mgrp, portguid, &p_mcm_port)) {
1498219820Sjeff			scope_state = p_mcm_port->scope_state;
1499219820Sjeff			memcpy(&port_gid, &(p_mcm_port->port_gid),
1500219820Sjeff			       sizeof(ib_gid_t));
1501219820Sjeff			proxy_join = p_mcm_port->proxy_join;
1502219820Sjeff		} else {
1503219820Sjeff			/* port not in group */
1504219820Sjeff			goto Exit;
1505219820Sjeff		}
1506219820Sjeff	} else {
1507219820Sjeff		/* point to the group information */
1508219820Sjeff		scope_state = p_mgrp->mcmember_rec.scope_state;
1509219820Sjeff	}
1510219820Sjeff
1511219820Sjeff	if (IB_MCR_COMPMASK_SCOPE & comp_mask)
1512219820Sjeff		scope_state_mask = 0xF0;
1513219820Sjeff
1514219820Sjeff	if (IB_MCR_COMPMASK_JOIN_STATE & comp_mask)
1515219820Sjeff		scope_state_mask = scope_state_mask | 0x0F;
1516219820Sjeff
1517219820Sjeff	/* Many MC records returned */
1518219820Sjeff	if (trusted_req == TRUE
1519219820Sjeff	    && !(IB_MCR_COMPMASK_PORT_GID & comp_mask)) {
1520219820Sjeff		char gid_str[INET6_ADDRSTRLEN];
1521219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1522219820Sjeff			"Trusted req is TRUE and no specific port defined\n");
1523219820Sjeff
1524219820Sjeff		/* return all the ports that match in this MC group */
1525219820Sjeff		p_item = cl_qmap_head(&(p_mgrp->mcm_port_tbl));
1526219820Sjeff		while (p_item != cl_qmap_end(&(p_mgrp->mcm_port_tbl))) {
1527219820Sjeff			p_mcm_port = (osm_mcm_port_t *) p_item;
1528219820Sjeff
1529219820Sjeff			if ((scope_state_mask & p_rcvd_rec->scope_state) ==
1530219820Sjeff			    (scope_state_mask & p_mcm_port->scope_state)) {
1531219820Sjeff				/* add to the list */
1532219820Sjeff				match_rec = p_mgrp->mcmember_rec;
1533219820Sjeff				match_rec.scope_state = p_mcm_port->scope_state;
1534219820Sjeff				memcpy(&(match_rec.port_gid),
1535219820Sjeff				       &(p_mcm_port->port_gid),
1536219820Sjeff				       sizeof(ib_gid_t));
1537219820Sjeff				OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
1538219820Sjeff					"Record of port_gid: %s"
1539219820Sjeff					" in multicast_lid: 0x%X is returned\n",
1540219820Sjeff					inet_ntop(AF_INET6, match_rec.port_gid.raw,
1541219820Sjeff						  gid_str, sizeof gid_str),
1542219820Sjeff					cl_ntoh16(p_mgrp->mlid));
1543219820Sjeff
1544219820Sjeff				match_rec.proxy_join =
1545219820Sjeff				    (uint8_t) (p_mcm_port->proxy_join);
1546219820Sjeff
1547219820Sjeff				__osm_mcmr_rcv_new_mcmr(sa, &match_rec, list);
1548219820Sjeff			}
1549219820Sjeff			p_item = cl_qmap_next(p_item);
1550219820Sjeff		}
1551219820Sjeff	}
1552219820Sjeff	/* One MC record returned */
1553219820Sjeff	else {
1554219820Sjeff		if ((scope_state_mask & p_rcvd_rec->scope_state) !=
1555219820Sjeff		    (scope_state_mask & scope_state))
1556219820Sjeff			goto Exit;
1557219820Sjeff
1558219820Sjeff		/* add to the list */
1559219820Sjeff		match_rec = p_mgrp->mcmember_rec;
1560219820Sjeff		match_rec.scope_state = scope_state;
1561219820Sjeff		memcpy(&(match_rec.port_gid), &port_gid, sizeof(ib_gid_t));
1562219820Sjeff		match_rec.proxy_join = (uint8_t) proxy_join;
1563219820Sjeff
1564219820Sjeff		__osm_mcmr_rcv_new_mcmr(sa, &match_rec, list);
1565219820Sjeff	}
1566219820Sjeff
1567219820SjeffExit:
1568219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1569219820Sjeff}
1570219820Sjeff
1571219820Sjeff/**********************************************************************
1572219820Sjeff Handle a query request
1573219820Sjeff**********************************************************************/
1574219820Sjeffstatic void
1575219820Sjeff__osm_mcmr_query_mgrp(IN osm_sa_t * sa,
1576219820Sjeff		      IN osm_madw_t * const p_madw)
1577219820Sjeff{
1578219820Sjeff	const ib_sa_mad_t *p_rcvd_mad;
1579219820Sjeff	const ib_member_rec_t *p_rcvd_rec;
1580219820Sjeff	cl_qlist_t rec_list;
1581219820Sjeff	ib_net64_t comp_mask;
1582219820Sjeff	osm_physp_t *p_req_physp;
1583219820Sjeff	boolean_t trusted_req;
1584219820Sjeff	osm_mgrp_t *p_mgrp;
1585219820Sjeff	int i;
1586219820Sjeff
1587219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1588219820Sjeff
1589219820Sjeff	p_rcvd_mad = osm_madw_get_sa_mad_ptr(p_madw);
1590219820Sjeff	p_rcvd_rec = (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_rcvd_mad);
1591219820Sjeff	comp_mask = p_rcvd_mad->comp_mask;
1592219820Sjeff
1593219820Sjeff	/*
1594219820Sjeff	   if sm_key is not zero and does not match we never get here
1595219820Sjeff	   see main SA receiver
1596219820Sjeff	 */
1597219820Sjeff	trusted_req = (p_rcvd_mad->sm_key != 0);
1598219820Sjeff
1599219820Sjeff	/* update the requester physical port. */
1600219820Sjeff	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
1601219820Sjeff						osm_madw_get_mad_addr_ptr
1602219820Sjeff						(p_madw));
1603219820Sjeff	if (p_req_physp == NULL) {
1604219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B04: "
1605219820Sjeff			"Cannot find requester physical port\n");
1606219820Sjeff		goto Exit;
1607219820Sjeff	}
1608219820Sjeff
1609219820Sjeff	cl_qlist_init(&rec_list);
1610219820Sjeff
1611219820Sjeff	CL_PLOCK_ACQUIRE(sa->p_lock);
1612219820Sjeff
1613219820Sjeff	/* simply go over all MCGs and match */
1614219820Sjeff	for (i = 0; i <= sa->p_subn->max_mcast_lid_ho - IB_LID_MCAST_START_HO;
1615219820Sjeff	     i++) {
1616219820Sjeff		p_mgrp = sa->p_subn->mgroups[i];
1617219820Sjeff		if (p_mgrp)
1618219820Sjeff			mcmr_by_comp_mask(sa, p_rcvd_rec, comp_mask, p_mgrp,
1619219820Sjeff					  p_req_physp, trusted_req, &rec_list);
1620219820Sjeff	}
1621219820Sjeff
1622219820Sjeff	CL_PLOCK_RELEASE(sa->p_lock);
1623219820Sjeff
1624219820Sjeff	/*
1625219820Sjeff	   p923 - The PortGID, JoinState and ProxyJoin shall be zero,
1626219820Sjeff	   except in the case of a trusted request.
1627219820Sjeff	   Note: In the mad controller we check that the SM_Key received on
1628219820Sjeff	   the mad is valid. Meaning - is either zero or equal to the local
1629219820Sjeff	   sm_key.
1630219820Sjeff	 */
1631219820Sjeff
1632219820Sjeff	if (!p_rcvd_mad->sm_key) {
1633219820Sjeff		osm_mcmr_item_t *item;
1634219820Sjeff		for (item = (osm_mcmr_item_t *) cl_qlist_head(&rec_list);
1635219820Sjeff		     item != (osm_mcmr_item_t *) cl_qlist_end(&rec_list);
1636219820Sjeff		     item = (osm_mcmr_item_t *)cl_qlist_next(&item->list_item)) {
1637219820Sjeff			memset(&item->rec.port_gid, 0, sizeof(ib_gid_t));
1638219820Sjeff			ib_member_set_join_state(&item->rec, 0);
1639219820Sjeff			item->rec.proxy_join = 0;
1640219820Sjeff		}
1641219820Sjeff	}
1642219820Sjeff
1643219820Sjeff	osm_sa_respond(sa, p_madw, sizeof(ib_member_rec_t), &rec_list);
1644219820Sjeff
1645219820SjeffExit:
1646219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1647219820Sjeff}
1648219820Sjeff
1649219820Sjeff/**********************************************************************
1650219820Sjeff **********************************************************************/
1651219820Sjeffvoid osm_mcmr_rcv_process(IN void *context, IN void *data)
1652219820Sjeff{
1653219820Sjeff	osm_sa_t *sa = context;
1654219820Sjeff	osm_madw_t *p_madw = data;
1655219820Sjeff	ib_sa_mad_t *p_sa_mad;
1656219820Sjeff	ib_member_rec_t *p_recvd_mcmember_rec;
1657219820Sjeff
1658219820Sjeff	CL_ASSERT(sa);
1659219820Sjeff
1660219820Sjeff	OSM_LOG_ENTER(sa->p_log);
1661219820Sjeff
1662219820Sjeff	CL_ASSERT(p_madw);
1663219820Sjeff
1664219820Sjeff	p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
1665219820Sjeff	p_recvd_mcmember_rec =
1666219820Sjeff	    (ib_member_rec_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
1667219820Sjeff
1668219820Sjeff	CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_MCMEMBER_RECORD);
1669219820Sjeff
1670219820Sjeff	switch (p_sa_mad->method) {
1671219820Sjeff	case IB_MAD_METHOD_SET:
1672219820Sjeff		if (!__check_join_comp_mask(p_sa_mad->comp_mask)) {
1673219820Sjeff			char gid_str[INET6_ADDRSTRLEN];
1674219820Sjeff			char gid_str2[INET6_ADDRSTRLEN];
1675219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B18: "
1676219820Sjeff				"component mask = 0x%016" PRIx64 ", "
1677219820Sjeff				"expected comp mask = 0x%016" PRIx64 ", "
1678219820Sjeff				"MGID: %s for PortGID: %s\n",
1679219820Sjeff				cl_ntoh64(p_sa_mad->comp_mask),
1680219820Sjeff				CL_NTOH64(JOIN_MC_COMP_MASK),
1681219820Sjeff				inet_ntop(AF_INET6,
1682219820Sjeff					p_recvd_mcmember_rec->mgid.raw,
1683219820Sjeff					gid_str, sizeof gid_str),
1684219820Sjeff				inet_ntop(AF_INET6,
1685219820Sjeff					p_recvd_mcmember_rec->port_gid.raw,
1686219820Sjeff					gid_str2, sizeof gid_str2));
1687219820Sjeff
1688219820Sjeff			osm_sa_send_error(sa, p_madw,
1689219820Sjeff					  IB_SA_MAD_STATUS_REQ_INVALID);
1690219820Sjeff			goto Exit;
1691219820Sjeff		}
1692219820Sjeff
1693219820Sjeff		/*
1694219820Sjeff		 * Join or Create Multicast Group
1695219820Sjeff		 */
1696219820Sjeff		__osm_mcmr_rcv_join_mgrp(sa, p_madw);
1697219820Sjeff		break;
1698219820Sjeff	case IB_MAD_METHOD_DELETE:
1699219820Sjeff		if (!__check_join_comp_mask(p_sa_mad->comp_mask)) {
1700219820Sjeff			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B20: "
1701219820Sjeff				"component mask = 0x%016" PRIx64 ", "
1702219820Sjeff				"expected comp mask = 0x%016" PRIx64 "\n",
1703219820Sjeff				cl_ntoh64(p_sa_mad->comp_mask),
1704219820Sjeff				CL_NTOH64(JOIN_MC_COMP_MASK));
1705219820Sjeff
1706219820Sjeff			osm_sa_send_error(sa, p_madw,
1707219820Sjeff					  IB_SA_MAD_STATUS_REQ_INVALID);
1708219820Sjeff			goto Exit;
1709219820Sjeff		}
1710219820Sjeff
1711219820Sjeff		/*
1712219820Sjeff		 * Leave Multicast Group
1713219820Sjeff		 */
1714219820Sjeff		__osm_mcmr_rcv_leave_mgrp(sa, p_madw);
1715219820Sjeff		break;
1716219820Sjeff	case IB_MAD_METHOD_GET:
1717219820Sjeff	case IB_MAD_METHOD_GETTABLE:
1718219820Sjeff		/*
1719219820Sjeff		 * Querying a Multicast Group
1720219820Sjeff		 */
1721219820Sjeff		__osm_mcmr_query_mgrp(sa, p_madw);
1722219820Sjeff		break;
1723219820Sjeff	default:
1724219820Sjeff		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1B21: "
1725219820Sjeff			"Unsupported Method (%s)\n",
1726219820Sjeff			ib_get_sa_method_str(p_sa_mad->method));
1727219820Sjeff		osm_sa_send_error(sa, p_madw,
1728219820Sjeff				  IB_MAD_STATUS_UNSUP_METHOD_ATTR);
1729219820Sjeff		break;
1730219820Sjeff	}
1731219820Sjeff
1732219820SjeffExit:
1733219820Sjeff	OSM_LOG_EXIT(sa->p_log);
1734219820Sjeff	return;
1735219820Sjeff}
1736