1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 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 #if HAVE_CONFIG_H
38 #  include <config.h>
39 #endif				/* HAVE_CONFIG_H */
40 
41 #include <string.h>
42 #include <iba/ib_types.h>
43 #include <complib/cl_qmap.h>
44 #include <complib/cl_passivelock.h>
45 #include <complib/cl_debug.h>
46 #include <complib/cl_qlist.h>
47 #include <opensm/osm_file_ids.h>
48 #define FILE_ID OSM_FILE_SA_PKEY_RECORD_C
49 #include <vendor/osm_vendor_api.h>
50 #include <opensm/osm_port.h>
51 #include <opensm/osm_node.h>
52 #include <opensm/osm_helper.h>
53 #include <opensm/osm_pkey.h>
54 #include <opensm/osm_sa.h>
55 
56 #define SA_PKEY_RESP_SIZE SA_ITEM_RESP_SIZE(pkey_rec)
57 
58 typedef struct osm_pkey_search_ctxt {
59 	const ib_pkey_table_record_t *p_rcvd_rec;
60 	ib_net64_t comp_mask;
61 	uint16_t block_num;
62 	cl_qlist_t *p_list;
63 	osm_sa_t *sa;
64 	const osm_physp_t *p_req_physp;
65 } osm_pkey_search_ctxt_t;
66 
67 static void sa_pkey_create(IN osm_sa_t * sa, IN osm_physp_t * p_physp,
68 			   IN osm_pkey_search_ctxt_t * p_ctxt,
69 			   IN uint16_t block)
70 {
71 	osm_sa_item_t *p_rec_item;
72 	uint16_t lid;
73 	ib_pkey_table_t *tbl;
74 
75 	OSM_LOG_ENTER(sa->p_log);
76 
77 	p_rec_item = malloc(SA_PKEY_RESP_SIZE);
78 	if (p_rec_item == NULL) {
79 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4602: "
80 			"rec_item alloc failed\n");
81 		goto Exit;
82 	}
83 
84 	if (p_physp->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH)
85 		lid = p_physp->port_info.base_lid;
86 	else
87 		lid = osm_node_get_base_lid(p_physp->p_node, 0);
88 
89 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
90 		"New P_Key table for: port 0x%016" PRIx64
91 		", lid %u, port %u Block:%u\n",
92 		cl_ntoh64(osm_physp_get_port_guid(p_physp)),
93 		cl_ntoh16(lid), osm_physp_get_port_num(p_physp), block);
94 
95 	memset(p_rec_item, 0, SA_PKEY_RESP_SIZE);
96 
97 	p_rec_item->resp.pkey_rec.lid = lid;
98 	p_rec_item->resp.pkey_rec.block_num = block;
99 	p_rec_item->resp.pkey_rec.port_num = osm_physp_get_port_num(p_physp);
100 	/* FIXME: There are ninf.PartitionCap or swinf.PartitionEnforcementCap
101 	   pkey entries so everything in that range is a valid block number
102 	   even if opensm is not using it. Return 0. However things outside
103 	   that range should return no entries. Not sure how to figure that
104 	   here? The range of pkey_tbl can be less than the cap, so
105 	   this falsely triggers. */
106 	tbl = osm_pkey_tbl_block_get(osm_physp_get_pkey_tbl(p_physp), block);
107 	if (tbl)
108 		p_rec_item->resp.pkey_rec.pkey_tbl = *tbl;
109 
110 	cl_qlist_insert_tail(p_ctxt->p_list, &p_rec_item->list_item);
111 
112 Exit:
113 	OSM_LOG_EXIT(sa->p_log);
114 }
115 
116 static void sa_pkey_check_physp(IN osm_sa_t * sa, IN osm_physp_t * p_physp,
117 				osm_pkey_search_ctxt_t * p_ctxt)
118 {
119 	ib_net64_t comp_mask = p_ctxt->comp_mask;
120 	uint16_t block, num_blocks;
121 
122 	OSM_LOG_ENTER(sa->p_log);
123 
124 	/* we got here with the phys port - all is left is to get the right block */
125 	if (comp_mask & IB_PKEY_COMPMASK_BLOCK) {
126 		sa_pkey_create(sa, p_physp, p_ctxt, p_ctxt->block_num);
127 	} else {
128 		num_blocks =
129 		    osm_pkey_tbl_get_num_blocks(osm_physp_get_pkey_tbl
130 						(p_physp));
131 		for (block = 0; block < num_blocks; block++)
132 			sa_pkey_create(sa, p_physp, p_ctxt, block);
133 	}
134 
135 	OSM_LOG_EXIT(sa->p_log);
136 }
137 
138 static void sa_pkey_by_comp_mask(IN osm_sa_t * sa, IN const osm_port_t * p_port,
139 				 osm_pkey_search_ctxt_t * p_ctxt)
140 {
141 	const ib_pkey_table_record_t *p_rcvd_rec;
142 	ib_net64_t comp_mask;
143 	osm_physp_t *p_physp;
144 	uint8_t port_num;
145 	uint8_t num_ports;
146 	const osm_physp_t *p_req_physp;
147 
148 	OSM_LOG_ENTER(sa->p_log);
149 
150 	p_rcvd_rec = p_ctxt->p_rcvd_rec;
151 	comp_mask = p_ctxt->comp_mask;
152 	port_num = p_rcvd_rec->port_num;
153 	p_req_physp = p_ctxt->p_req_physp;
154 
155 	/* if this is a switch port we can search all ports
156 	   otherwise we must be looking on port 0 */
157 	if (p_port->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH) {
158 		/* we put it in the comp mask and port num */
159 		port_num = p_port->p_physp->port_num;
160 		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
161 			"Using Physical Default Port Number: 0x%X (for End Node)\n",
162 			port_num);
163 		comp_mask |= IB_PKEY_COMPMASK_PORT;
164 	}
165 
166 	if (comp_mask & IB_PKEY_COMPMASK_PORT) {
167 		if (port_num < osm_node_get_num_physp(p_port->p_node)) {
168 			p_physp =
169 			    osm_node_get_physp_ptr(p_port->p_node, port_num);
170 			/* Check that the p_physp is valid, and that is shares a pkey
171 			   with the p_req_physp. */
172 			if (p_physp &&
173 			    osm_physp_share_pkey(sa->p_log, p_req_physp,
174 						 p_physp, sa->p_subn->opt.allow_both_pkeys))
175 				sa_pkey_check_physp(sa, p_physp, p_ctxt);
176 		} else {
177 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4603: "
178 				"Given Physical Port Number: 0x%X is out of range should be < 0x%X\n",
179 				port_num,
180 				osm_node_get_num_physp(p_port->p_node));
181 			goto Exit;
182 		}
183 	} else {
184 		num_ports = osm_node_get_num_physp(p_port->p_node);
185 		for (port_num = 0; port_num < num_ports; port_num++) {
186 			p_physp =
187 			    osm_node_get_physp_ptr(p_port->p_node, port_num);
188 			if (!p_physp)
189 				continue;
190 
191 			/* if the requester and the p_physp don't share a pkey -
192 			   continue */
193 			if (!osm_physp_share_pkey
194 			    (sa->p_log, p_req_physp, p_physp, sa->p_subn->opt.allow_both_pkeys))
195 				continue;
196 
197 			sa_pkey_check_physp(sa, p_physp, p_ctxt);
198 		}
199 	}
200 Exit:
201 	OSM_LOG_EXIT(sa->p_log);
202 }
203 
204 static void sa_pkey_by_comp_mask_cb(IN cl_map_item_t * p_map_item, IN void *cxt)
205 {
206 	const osm_port_t *p_port = (osm_port_t *) p_map_item;
207 	osm_pkey_search_ctxt_t *p_ctxt = cxt;
208 
209 	sa_pkey_by_comp_mask(p_ctxt->sa, p_port, p_ctxt);
210 }
211 
212 void osm_pkey_rec_rcv_process(IN void *ctx, IN void *data)
213 {
214 	osm_sa_t *sa = ctx;
215 	osm_madw_t *p_madw = data;
216 	const ib_sa_mad_t *p_rcvd_mad;
217 	const ib_pkey_table_record_t *p_rcvd_rec;
218 	const osm_port_t *p_port = NULL;
219 	cl_qlist_t rec_list;
220 	osm_pkey_search_ctxt_t context;
221 	ib_net64_t comp_mask;
222 	osm_physp_t *p_req_physp;
223 
224 	CL_ASSERT(sa);
225 
226 	OSM_LOG_ENTER(sa->p_log);
227 
228 	CL_ASSERT(p_madw);
229 
230 	p_rcvd_mad = osm_madw_get_sa_mad_ptr(p_madw);
231 	p_rcvd_rec =
232 	    (ib_pkey_table_record_t *) ib_sa_mad_get_payload_ptr(p_rcvd_mad);
233 	comp_mask = p_rcvd_mad->comp_mask;
234 
235 	CL_ASSERT(p_rcvd_mad->attr_id == IB_MAD_ATTR_PKEY_TBL_RECORD);
236 
237 	/* we only support SubnAdmGet and SubnAdmGetTable methods */
238 	if (p_rcvd_mad->method != IB_MAD_METHOD_GET &&
239 	    p_rcvd_mad->method != IB_MAD_METHOD_GETTABLE) {
240 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4605: "
241 			"Unsupported Method (%s) for PKeyRecord request\n",
242 			ib_get_sa_method_str(p_rcvd_mad->method));
243 		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
244 		goto Exit;
245 	}
246 
247 	/*
248 	   p922 - P_KeyTableRecords shall only be provided in response
249 	   to trusted requests.
250 	   Check that the requester is a trusted one.
251 	 */
252 	if (p_rcvd_mad->sm_key != sa->p_subn->opt.sa_key) {
253 		/* This is not a trusted requester! */
254 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4608: "
255 			"Ignoring PKeyRecord request from non-trusted requester"
256 			" with SM_Key 0x%016" PRIx64 "\n",
257 			cl_ntoh64(p_rcvd_mad->sm_key));
258 		osm_sa_send_error(sa, p_madw, IB_SA_MAD_STATUS_REQ_INVALID);
259 		goto Exit;
260 	}
261 
262 	cl_plock_acquire(sa->p_lock);
263 
264 	/* update the requester physical port */
265 	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
266 						osm_madw_get_mad_addr_ptr
267 						(p_madw));
268 	if (p_req_physp == NULL) {
269 		cl_plock_release(sa->p_lock);
270 		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4604: "
271 			"Cannot find requester physical port\n");
272 		goto Exit;
273 	}
274 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
275 		"Requester port GUID 0x%" PRIx64 "\n",
276 		cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
277 
278 	cl_qlist_init(&rec_list);
279 
280 	context.p_rcvd_rec = p_rcvd_rec;
281 	context.p_list = &rec_list;
282 	context.comp_mask = p_rcvd_mad->comp_mask;
283 	context.sa = sa;
284 	context.block_num = cl_ntoh16(p_rcvd_rec->block_num);
285 	context.p_req_physp = p_req_physp;
286 
287 	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
288 		"Got Query Lid:%u(%02X), Block:0x%02X(%02X), Port:0x%02X(%02X)\n",
289 		cl_ntoh16(p_rcvd_rec->lid),
290 		(comp_mask & IB_PKEY_COMPMASK_LID) != 0, p_rcvd_rec->port_num,
291 		(comp_mask & IB_PKEY_COMPMASK_PORT) != 0, context.block_num,
292 		(comp_mask & IB_PKEY_COMPMASK_BLOCK) != 0);
293 
294 	/*
295 	   If the user specified a LID, it obviously narrows our
296 	   work load, since we don't have to search every port
297 	 */
298 	if (comp_mask & IB_PKEY_COMPMASK_LID) {
299 		p_port = osm_get_port_by_lid(sa->p_subn, p_rcvd_rec->lid);
300 		if (!p_port)
301 			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 460B: "
302 				"No port found with LID %u\n",
303 				cl_ntoh16(p_rcvd_rec->lid));
304 		else
305 			sa_pkey_by_comp_mask(sa, p_port, &context);
306 	} else
307 		cl_qmap_apply_func(&sa->p_subn->port_guid_tbl,
308 				   sa_pkey_by_comp_mask_cb, &context);
309 
310 	cl_plock_release(sa->p_lock);
311 
312 	osm_sa_respond(sa, p_madw, sizeof(ib_pkey_table_record_t), &rec_list);
313 
314 Exit:
315 	OSM_LOG_EXIT(sa->p_log);
316 }
317