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) 2013 Oracle and/or its affiliates. All rights reserved.
6 *
7 * This software is available to you under a choice of one of two
8 * licenses. You may choose to be licensed under the terms of the GNU
9 * General Public License (GPL) Version 2, available from the file
10 * COPYING in the main directory of this source tree, or the
11 * OpenIB.org BSD license below:
12 *
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
16 *
17 * - Redistributions of source code must retain the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer.
20 *
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials
24 * provided with the distribution.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 * SOFTWARE.
34 *
35 */
36
37 /*
38 * Abstract:
39 * Implementation of multicast functions.
40 */
41
42 #if HAVE_CONFIG_H
43 # include <config.h>
44 #endif /* HAVE_CONFIG_H */
45
46 #include <stdlib.h>
47 #include <string.h>
48 #include <arpa/inet.h>
49 #include <sys/socket.h>
50 #include <opensm/osm_file_ids.h>
51 #define FILE_ID OSM_FILE_MULTICAST_C
52 #include <opensm/osm_multicast.h>
53 #include <opensm/osm_mcm_port.h>
54 #include <opensm/osm_mtree.h>
55 #include <opensm/osm_inform.h>
56 #include <opensm/osm_opensm.h>
57
mgrp_box_new(uint16_t mlid)58 static osm_mgrp_box_t *mgrp_box_new(uint16_t mlid)
59 {
60 osm_mgrp_box_t *mbox = malloc(sizeof(*mbox));
61 if (!mbox)
62 return NULL;
63
64 memset(mbox, 0, sizeof(*mbox));
65 mbox->mlid = mlid;
66 cl_qlist_init(&mbox->mgrp_list);
67
68 return mbox;
69 }
70
mgrp_box_delete(osm_mgrp_box_t * mbox)71 void mgrp_box_delete(osm_mgrp_box_t *mbox)
72 {
73 osm_mtree_destroy(mbox->root);
74 free(mbox);
75 }
76
mgrp_delete(IN osm_mgrp_t * p_mgrp)77 void mgrp_delete(IN osm_mgrp_t * p_mgrp)
78 {
79 osm_mcm_alias_guid_t *p_mcm_alias_guid, *p_next_mcm_alias_guid;
80 osm_mcm_port_t *p_mcm_port, *p_next_mcm_port;
81
82 CL_ASSERT(p_mgrp);
83
84 p_next_mcm_alias_guid =
85 (osm_mcm_alias_guid_t *) cl_qmap_head(&p_mgrp->mcm_alias_port_tbl);
86 while (p_next_mcm_alias_guid !=
87 (osm_mcm_alias_guid_t *) cl_qmap_end(&p_mgrp->mcm_alias_port_tbl)) {
88 p_mcm_alias_guid = p_next_mcm_alias_guid;
89 p_next_mcm_alias_guid =
90 (osm_mcm_alias_guid_t *) cl_qmap_next(&p_mcm_alias_guid->map_item);
91 osm_mcm_alias_guid_delete(&p_mcm_alias_guid);
92 }
93
94 p_next_mcm_port =
95 (osm_mcm_port_t *) cl_qmap_head(&p_mgrp->mcm_port_tbl);
96 while (p_next_mcm_port !=
97 (osm_mcm_port_t *) cl_qmap_end(&p_mgrp->mcm_port_tbl)) {
98 p_mcm_port = p_next_mcm_port;
99 p_next_mcm_port =
100 (osm_mcm_port_t *) cl_qmap_next(&p_mcm_port->map_item);
101 osm_mcm_port_delete(p_mcm_port);
102 }
103
104 free(p_mgrp);
105 }
106
osm_mgrp_box_delete(osm_mgrp_box_t * mbox)107 void osm_mgrp_box_delete(osm_mgrp_box_t *mbox)
108 {
109 osm_mgrp_t *mgrp;
110
111 while (cl_qlist_count(&mbox->mgrp_list)) {
112 mgrp = cl_item_obj(cl_qlist_remove_head(&mbox->mgrp_list),
113 mgrp, list_item);
114 mgrp_delete(mgrp);
115 }
116 mgrp_box_delete(mbox);
117 }
118
osm_mgrp_new(IN osm_subn_t * subn,IN ib_net16_t mlid,IN ib_member_rec_t * mcmr)119 osm_mgrp_t *osm_mgrp_new(IN osm_subn_t * subn, IN ib_net16_t mlid,
120 IN ib_member_rec_t * mcmr)
121 {
122 osm_mgrp_t *p_mgrp;
123 osm_mgrp_box_t *mbox;
124
125 p_mgrp = (osm_mgrp_t *) malloc(sizeof(*p_mgrp));
126 if (!p_mgrp)
127 return NULL;
128
129 memset(p_mgrp, 0, sizeof(*p_mgrp));
130 cl_qmap_init(&p_mgrp->mcm_port_tbl);
131 cl_qmap_init(&p_mgrp->mcm_alias_port_tbl);
132 p_mgrp->mlid = mlid;
133 p_mgrp->mcmember_rec = *mcmr;
134
135 mbox = osm_get_mbox_by_mlid(subn, p_mgrp->mlid);
136 if (!mbox && !(mbox = mgrp_box_new(cl_ntoh16(p_mgrp->mlid)))) {
137 free(p_mgrp);
138 return NULL;
139 }
140
141 cl_qlist_insert_tail(&mbox->mgrp_list, &p_mgrp->list_item);
142 subn->mboxes[mbox->mlid - IB_LID_MCAST_START_HO] = mbox;
143
144 cl_fmap_insert(&subn->mgrp_mgid_tbl, &p_mgrp->mcmember_rec.mgid,
145 &p_mgrp->map_item);
146
147 subn->p_osm->sa.dirty = TRUE;
148 return p_mgrp;
149 }
150
osm_mgrp_cleanup(osm_subn_t * subn,osm_mgrp_t * mgrp)151 void osm_mgrp_cleanup(osm_subn_t * subn, osm_mgrp_t * mgrp)
152 {
153 osm_mgrp_box_t *mbox;
154 osm_mcm_alias_guid_t *mcm_alias_guid;
155 osm_mcm_port_t *mcm_port;
156
157 if (mgrp->full_members)
158 return;
159
160 while (cl_qmap_count(&mgrp->mcm_alias_port_tbl)) {
161 mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_head(&mgrp->mcm_alias_port_tbl);
162 cl_qmap_remove_item(&mgrp->mcm_alias_port_tbl, &mcm_alias_guid->map_item);
163 osm_mcm_alias_guid_delete(&mcm_alias_guid);
164 }
165
166 while (cl_qmap_count(&mgrp->mcm_port_tbl)) {
167 mcm_port = (osm_mcm_port_t *) cl_qmap_head(&mgrp->mcm_port_tbl);
168 cl_qmap_remove_item(&mgrp->mcm_port_tbl, &mcm_port->map_item);
169 cl_qlist_remove_item(&mcm_port->port->mcm_list,
170 &mcm_port->list_item);
171 osm_mcm_port_delete(mcm_port);
172 }
173
174 if (mgrp->well_known)
175 return;
176
177 cl_fmap_remove_item(&subn->mgrp_mgid_tbl, &mgrp->map_item);
178
179 mbox = osm_get_mbox_by_mlid(subn, mgrp->mlid);
180 cl_qlist_remove_item(&mbox->mgrp_list, &mgrp->list_item);
181 if (cl_is_qlist_empty(&mbox->mgrp_list)) {
182 subn->mboxes[cl_ntoh16(mgrp->mlid) - IB_LID_MCAST_START_HO] = NULL;
183 mgrp_box_delete(mbox);
184 }
185 free(mgrp);
186
187 subn->p_osm->sa.dirty = TRUE;
188 }
189
mgrp_send_notice(osm_subn_t * subn,osm_log_t * log,osm_mgrp_t * mgrp,unsigned num)190 static void mgrp_send_notice(osm_subn_t * subn, osm_log_t * log,
191 osm_mgrp_t * mgrp, unsigned num)
192 {
193 ib_mad_notice_attr_t notice;
194 ib_api_status_t status;
195
196 notice.generic_type = 0x80 | IB_NOTICE_TYPE_SUBN_MGMT; /* is generic subn mgt type */
197 ib_notice_set_prod_type_ho(¬ice, 4); /* A Class Manager generator */
198 notice.g_or_v.generic.trap_num = CL_HTON16(num);
199 /* The sm_base_lid is saved in network order already. */
200 notice.issuer_lid = subn->sm_base_lid;
201 /* following o14-12.1.11 and table 120 p726 */
202 /* we need to provide the MGID */
203 memcpy(¬ice.data_details.ntc_64_67.gid,
204 &mgrp->mcmember_rec.mgid, sizeof(ib_gid_t));
205
206 /* According to page 653 - the issuer gid in this case of trap
207 is the SM gid, since the SM is the initiator of this trap. */
208 notice.issuer_gid.unicast.prefix = subn->opt.subnet_prefix;
209 notice.issuer_gid.unicast.interface_id = subn->sm_port_guid;
210
211 if ((status = osm_report_notice(log, subn, ¬ice)))
212 OSM_LOG(log, OSM_LOG_ERROR, "ERR 7601: "
213 "Error sending trap reports (%s)\n",
214 ib_get_err_str(status));
215 }
216
is_qmap_empty_for_port(IN const cl_qmap_t * const p_map,IN const osm_port_t * port)217 static boolean_t is_qmap_empty_for_port(IN const cl_qmap_t * const p_map,
218 IN const osm_port_t *port)
219 {
220 size_t count = 0;
221 cl_map_item_t *item;
222 osm_mcm_alias_guid_t *mcm_alias_guid;
223
224 for (item = cl_qmap_head(p_map); item != cl_qmap_end(p_map);
225 item = cl_qmap_next(item)) {
226 mcm_alias_guid = (osm_mcm_alias_guid_t *) item;
227 if (mcm_alias_guid->p_base_mcm_port->port == port) {
228 count++;
229 break;
230 }
231 }
232
233 return (count == 0);
234 }
235
is_qmap_empty_for_mcm_port(IN const cl_qmap_t * const p_map,IN const osm_mcm_port_t * mcm_port)236 static boolean_t is_qmap_empty_for_mcm_port(IN const cl_qmap_t * const p_map,
237 IN const osm_mcm_port_t *mcm_port)
238 {
239 size_t count = 0;
240 cl_map_item_t *item;
241 osm_mcm_alias_guid_t *mcm_alias_guid;
242
243 for (item = cl_qmap_head(p_map); item != cl_qmap_end(p_map);
244 item = cl_qmap_next(item)) {
245 mcm_alias_guid = (osm_mcm_alias_guid_t *) item;
246 if (mcm_alias_guid->p_base_mcm_port == mcm_port) {
247 count++;
248 break;
249 }
250 }
251
252 return (count == 0);
253 }
insert_alias_guid(IN osm_mgrp_t * mgrp,IN osm_mcm_alias_guid_t * p_mcm_alias_guid)254 static osm_mcm_alias_guid_t *insert_alias_guid(IN osm_mgrp_t * mgrp,
255 IN osm_mcm_alias_guid_t * p_mcm_alias_guid)
256 {
257 osm_mcm_alias_guid_t *p_mcm_alias_guid_check;
258
259 /* insert into mcm alias guid table */
260 p_mcm_alias_guid_check =
261 (osm_mcm_alias_guid_t *) cl_qmap_insert(&mgrp->mcm_alias_port_tbl,
262 p_mcm_alias_guid->alias_guid,
263 &p_mcm_alias_guid->map_item);
264 if (p_mcm_alias_guid_check != (osm_mcm_alias_guid_t *) &p_mcm_alias_guid->map_item) {
265 /* alias GUID is a duplicate */
266 osm_mcm_alias_guid_delete(&p_mcm_alias_guid);
267 return p_mcm_alias_guid_check;
268 }
269 return NULL;
270 }
271
osm_mgrp_add_port(IN osm_subn_t * subn,osm_log_t * log,IN osm_mgrp_t * mgrp,osm_port_t * port,IN ib_member_rec_t * mcmr,IN boolean_t proxy)272 osm_mcm_port_t *osm_mgrp_add_port(IN osm_subn_t * subn, osm_log_t * log,
273 IN osm_mgrp_t * mgrp, osm_port_t *port,
274 IN ib_member_rec_t *mcmr, IN boolean_t proxy)
275 {
276 osm_mcm_port_t *mcm_port;
277 osm_mcm_alias_guid_t *p_mcm_alias_guid, *p_mcm_alias_guid_check;
278 cl_map_item_t *prev_item;
279 uint8_t prev_join_state = 0, join_state = mcmr->scope_state;
280 uint8_t prev_scope;
281
282 if (OSM_LOG_IS_ACTIVE_V2(log, OSM_LOG_VERBOSE)) {
283 char gid_str[INET6_ADDRSTRLEN];
284 OSM_LOG(log, OSM_LOG_VERBOSE, "GUID 0x%016" PRIx64
285 " Port 0x%016" PRIx64 " joining "
286 "MC group %s (mlid 0x%x)\n",
287 cl_ntoh64(mcmr->port_gid.unicast.interface_id),
288 cl_ntoh64(port->guid),
289 inet_ntop(AF_INET6, mgrp->mcmember_rec.mgid.raw,
290 gid_str, sizeof(gid_str)),
291 cl_ntoh16(mgrp->mlid));
292 }
293
294 mcm_port = osm_mcm_port_new(port, mgrp);
295 if (!mcm_port)
296 return NULL;
297
298 p_mcm_alias_guid = osm_mcm_alias_guid_new(mcm_port, mcmr, proxy);
299 if (!p_mcm_alias_guid) {
300 osm_mcm_port_delete(mcm_port);
301 return NULL;
302 }
303
304 /*
305 prev_item = cl_qmap_insert(...)
306 Pointer to the item in the map with the specified key. If insertion
307 was successful, this is the pointer to the item. If an item with the
308 specified key already exists in the map, the pointer to that item is
309 returned.
310 */
311 prev_item = cl_qmap_insert(&mgrp->mcm_port_tbl, port->guid,
312 &mcm_port->map_item);
313
314 if (prev_item != &mcm_port->map_item) { /* mcm port already exists */
315 osm_mcm_port_delete(mcm_port);
316 mcm_port = (osm_mcm_port_t *) prev_item;
317
318 p_mcm_alias_guid->p_base_mcm_port = (osm_mcm_port_t *) prev_item;
319 p_mcm_alias_guid_check = insert_alias_guid(mgrp, p_mcm_alias_guid);
320 if (p_mcm_alias_guid_check) { /* alias GUID already exists */
321 p_mcm_alias_guid = p_mcm_alias_guid_check;
322 ib_member_get_scope_state(p_mcm_alias_guid->scope_state,
323 &prev_scope, &prev_join_state);
324 p_mcm_alias_guid->scope_state =
325 ib_member_set_scope_state(prev_scope,
326 prev_join_state | join_state);
327 }
328 } else {
329 insert_alias_guid(mgrp, p_mcm_alias_guid);
330 cl_qlist_insert_tail(&port->mcm_list, &mcm_port->list_item);
331 osm_sm_reroute_mlid(&subn->p_osm->sm, mgrp->mlid);
332 }
333
334 /* o15.0.1.11: copy the join state */
335 mcmr->scope_state = p_mcm_alias_guid->scope_state;
336
337 if ((join_state & IB_JOIN_STATE_FULL) &&
338 !(prev_join_state & IB_JOIN_STATE_FULL) &&
339 ++mgrp->full_members == 1)
340 mgrp_send_notice(subn, log, mgrp, SM_MGID_CREATED_TRAP); /* 66 */
341
342 subn->p_osm->sa.dirty = TRUE;
343 return mcm_port;
344 }
345
osm_mgrp_remove_port(osm_subn_t * subn,osm_log_t * log,osm_mgrp_t * mgrp,osm_mcm_alias_guid_t * mcm_alias_guid,ib_member_rec_t * mcmr)346 boolean_t osm_mgrp_remove_port(osm_subn_t * subn, osm_log_t * log, osm_mgrp_t * mgrp,
347 osm_mcm_alias_guid_t * mcm_alias_guid,
348 ib_member_rec_t *mcmr)
349 {
350 uint8_t join_state = mcmr->scope_state & 0xf;
351 uint8_t port_join_state, new_join_state;
352 boolean_t mgrp_deleted = FALSE;
353
354 /*
355 * according to the same o15-0.1.14 we get the stored
356 * JoinState and the request JoinState and they must be
357 * opposite to leave - otherwise just update it
358 */
359 port_join_state = mcm_alias_guid->scope_state & 0x0F;
360 new_join_state = port_join_state & ~join_state;
361
362 if (OSM_LOG_IS_ACTIVE_V2(log, OSM_LOG_VERBOSE)) {
363 char gid_str[INET6_ADDRSTRLEN];
364 OSM_LOG(log, OSM_LOG_VERBOSE,
365 "GUID 0x%" PRIx64 " Port 0x%" PRIx64
366 " leaving MC group %s (mlid 0x%x)\n",
367 cl_ntoh64(mcm_alias_guid->alias_guid),
368 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid),
369 inet_ntop(AF_INET6, mgrp->mcmember_rec.mgid.raw,
370 gid_str, sizeof(gid_str)),
371 cl_ntoh16(mgrp->mlid));
372 }
373
374 if (new_join_state & IB_JOIN_STATE_FULL ||
375 (new_join_state &&
376 (mgrp->full_members > (port_join_state & IB_JOIN_STATE_FULL) ? 1 : 0))) {
377 mcm_alias_guid->scope_state =
378 new_join_state | (mcm_alias_guid->scope_state & 0xf0);
379 OSM_LOG(log, OSM_LOG_DEBUG,
380 "updating GUID 0x%" PRIx64 " port 0x%" PRIx64
381 " JoinState 0x%x -> 0x%x\n",
382 cl_ntoh64(mcm_alias_guid->alias_guid),
383 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid),
384 port_join_state, new_join_state);
385 mcmr->scope_state = mcm_alias_guid->scope_state;
386 } else {
387 mcmr->scope_state = mcm_alias_guid->scope_state & 0xf0;
388 OSM_LOG(log, OSM_LOG_DEBUG, "removing alias GUID 0x%" PRIx64 "\n",
389 cl_ntoh64(mcm_alias_guid->alias_guid));
390 cl_qmap_remove_item(&mgrp->mcm_alias_port_tbl,
391 &mcm_alias_guid->map_item);
392 if (is_qmap_empty_for_port(&mgrp->mcm_alias_port_tbl,
393 mcm_alias_guid->p_base_mcm_port->port)) { /* last alias in mcast group for this port */
394 OSM_LOG(log, OSM_LOG_DEBUG, "removing port 0x%" PRIx64 "\n",
395 cl_ntoh64(mcm_alias_guid->p_base_mcm_port->port->guid));
396 cl_qmap_remove_item(&mgrp->mcm_port_tbl,
397 &mcm_alias_guid->p_base_mcm_port->map_item);
398 cl_qlist_remove_item(&mcm_alias_guid->p_base_mcm_port->port->mcm_list,
399 &mcm_alias_guid->p_base_mcm_port->list_item);
400 if (is_qmap_empty_for_mcm_port(&mgrp->mcm_alias_port_tbl,
401 mcm_alias_guid->p_base_mcm_port)) /* last alias in mcast group for this mcm port */
402 osm_mcm_port_delete(mcm_alias_guid->p_base_mcm_port);
403 osm_sm_reroute_mlid(&subn->p_osm->sm, mgrp->mlid);
404 }
405 osm_mcm_alias_guid_delete(&mcm_alias_guid);
406 }
407
408 /* no more full members so the group will be deleted after re-route
409 but only if it is not a well known group */
410 if ((port_join_state & IB_JOIN_STATE_FULL) &&
411 !(new_join_state & IB_JOIN_STATE_FULL) &&
412 --mgrp->full_members == 0) {
413 mgrp_send_notice(subn, log, mgrp, SM_MGID_DESTROYED_TRAP); /* 67 */
414 osm_mgrp_cleanup(subn, mgrp);
415 mgrp_deleted = TRUE;
416 }
417
418 subn->p_osm->sa.dirty = TRUE;
419
420 return (mgrp_deleted);
421 }
422
osm_mgrp_delete_port(osm_subn_t * subn,osm_log_t * log,osm_mgrp_t * mgrp,osm_port_t * port)423 void osm_mgrp_delete_port(osm_subn_t * subn, osm_log_t * log, osm_mgrp_t * mgrp,
424 osm_port_t * port)
425 {
426 osm_mcm_alias_guid_t *mcm_alias_guid, *next_mcm_alias_guid;
427 ib_member_rec_t mcmrec;
428 boolean_t mgrp_deleted = FALSE;
429
430 next_mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_head(&mgrp->mcm_alias_port_tbl);
431 while (next_mcm_alias_guid != (osm_mcm_alias_guid_t *) cl_qmap_end(&mgrp->mcm_alias_port_tbl) &&
432 !mgrp_deleted) {
433 mcm_alias_guid = next_mcm_alias_guid;
434 next_mcm_alias_guid = (osm_mcm_alias_guid_t *) cl_qmap_next(&next_mcm_alias_guid->map_item);
435 if (mcm_alias_guid->p_base_mcm_port->port == port) {
436 mcmrec.scope_state = 0xf;
437 mgrp_deleted = osm_mgrp_remove_port(subn, log, mgrp, mcm_alias_guid,
438 &mcmrec);
439 }
440 }
441 }
442
osm_mgrp_get_mcm_port(IN const osm_mgrp_t * p_mgrp,IN ib_net64_t port_guid)443 osm_mcm_port_t *osm_mgrp_get_mcm_port(IN const osm_mgrp_t * p_mgrp,
444 IN ib_net64_t port_guid)
445 {
446 cl_map_item_t *item = cl_qmap_get(&p_mgrp->mcm_port_tbl, port_guid);
447 if (item != cl_qmap_end(&p_mgrp->mcm_port_tbl))
448 return (osm_mcm_port_t *) item;
449 return NULL;
450 }
451
osm_mgrp_get_mcm_alias_guid(IN const osm_mgrp_t * p_mgrp,IN ib_net64_t port_guid)452 osm_mcm_alias_guid_t *osm_mgrp_get_mcm_alias_guid(IN const osm_mgrp_t * p_mgrp,
453 IN ib_net64_t port_guid)
454 {
455 cl_map_item_t *item = cl_qmap_get(&p_mgrp->mcm_alias_port_tbl,
456 port_guid);
457 if (item != cl_qmap_end(&p_mgrp->mcm_alias_port_tbl))
458 return (osm_mcm_alias_guid_t *) item;
459 return NULL;
460 }
461