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