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