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