]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/opensm/opensm/osm_multicast.c
Merge OpenSSL 1.1.1f.
[FreeBSD/FreeBSD.git] / contrib / ofed / opensm / opensm / osm_multicast.c
1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2015 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 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
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
71 void mgrp_box_delete(osm_mgrp_box_t *mbox)
72 {
73         osm_mtree_destroy(mbox->root);
74         free(mbox);
75 }
76
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
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
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
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
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(&notice, 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(&notice.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, &notice)))
212                 OSM_LOG(log, OSM_LOG_ERROR, "ERR 7601: "
213                         "Error sending trap reports (%s)\n",
214                         ib_get_err_str(status));
215 }
216
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
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 }
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
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
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
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
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
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 }