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