xref: /illumos-gate/usr/src/uts/common/io/neti_impl.c (revision 0bb073995ac5a95bd35f2dd790df1ea3d8c2d507)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/atomic.h>
28 #include <sys/kmem.h>
29 #include <sys/rwlock.h>
30 #include <sys/errno.h>
31 #include <sys/queue.h>
32 #include <inet/common.h>
33 #include <inet/led.h>
34 #include <inet/ip.h>
35 #include <sys/neti.h>
36 #include <sys/zone.h>
37 
38 static net_handle_t net_find(const char *protocol, neti_stack_t *ns);
39 
40 static net_handle_t
41 net_find(const char *protocol, neti_stack_t *nts)
42 {
43 	struct net_data *n;
44 
45 	ASSERT(protocol != NULL);
46 	ASSERT(nts != NULL);
47 
48 	LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
49 		ASSERT(n->netd_info.netp_name != NULL);
50 		/*
51 		 * If they're trying to find a protocol that is being
52 		 * shutdown, just ignore it..
53 		 */
54 		if (n->netd_condemned != 0)
55 			continue;
56 		if (strcmp(n->netd_info.netp_name, protocol) == 0) {
57 			break;
58 		}
59 	}
60 
61 	return (n);
62 }
63 
64 net_handle_t
65 net_protocol_register(netid_t id, const net_protocol_t *info)
66 {
67 	struct net_data *n, *new;
68 	neti_stack_t *nts;
69 
70 	ASSERT(info != NULL);
71 
72 	nts = net_getnetistackbyid(id);
73 	if (nts == NULL)
74 		return (NULL);
75 
76 	new = kmem_alloc(sizeof (*new), KM_SLEEP);
77 	new->netd_refcnt = 1;
78 	new->netd_hooks = NULL;
79 	new->netd_info = *info;
80 	new->netd_stack = nts;
81 	new->netd_condemned = 0;
82 
83 	mutex_enter(&nts->nts_lock);
84 	n = net_find(info->netp_name, nts);
85 	if (n != NULL) {
86 		mutex_exit(&nts->nts_lock);
87 		kmem_free(new, sizeof (*new));
88 		return (NULL);
89 	}
90 
91 	if (LIST_EMPTY(&nts->nts_netd_head)) {
92 		LIST_INSERT_HEAD(&nts->nts_netd_head, new, netd_list);
93 	} else {
94 		LIST_INSERT_AFTER(LIST_FIRST(&nts->nts_netd_head),
95 		    new, netd_list);
96 	}
97 	mutex_exit(&nts->nts_lock);
98 
99 	return (new);
100 }
101 
102 int
103 net_protocol_unregister(net_handle_t info)
104 {
105 	neti_stack_t *nts;
106 
107 	ASSERT(info != NULL);
108 
109 	nts = info->netd_stack;
110 	ASSERT(nts != NULL);
111 
112 	mutex_enter(&nts->nts_lock);
113 	LIST_REMOVE(info, netd_list);
114 	info->netd_stack = NULL;
115 	mutex_exit(&nts->nts_lock);
116 
117 	(void) net_protocol_release(info);
118 
119 	return (0);
120 }
121 
122 net_handle_t
123 net_protocol_lookup(netid_t netid, const char *protocol)
124 {
125 	neti_stack_t *nts;
126 	net_handle_t nd;
127 
128 	ASSERT(protocol != NULL);
129 
130 	nts = net_getnetistackbyid(netid);
131 	if (nts == NULL)
132 		return (NULL);
133 
134 	mutex_enter(&nts->nts_lock);
135 	nd = net_find(protocol, nts);
136 	if (nd != NULL)
137 		atomic_add_32((uint_t *)&nd->netd_refcnt, 1);
138 	mutex_exit(&nts->nts_lock);
139 	return (nd);
140 }
141 
142 /*
143  * Note: the man page specifies "returns -1 if the value passed in is unknown
144  * to this framework".  We are not doing a lookup in this function, just a
145  * simply add to the netd_refcnt of the net_handle_t passed in, so -1 is never a
146  * return value.
147  */
148 int
149 net_protocol_release(net_handle_t info)
150 {
151 
152 	ASSERT(info->netd_refcnt > 0);
153 	/*
154 	 * Is this safe? No hold on nts_lock? Consider that if the caller
155 	 * of net_protocol_release() is going to free this structure then
156 	 * it is now the only owner (refcnt==1) and it will have been
157 	 * removed from the nts_netd_head list on the neti_stack_t from a
158 	 * call to net_protocol_unregister already, so it is thus an orphan.
159 	 */
160 	if (atomic_add_32_nv((uint_t *)&info->netd_refcnt, -1) == 0) {
161 		ASSERT(info->netd_hooks == NULL);
162 		ASSERT(info->netd_stack == NULL);
163 		kmem_free(info, sizeof (struct net_data));
164 	}
165 
166 	return (0);
167 }
168 
169 net_handle_t
170 net_protocol_walk(netid_t netid, net_handle_t info)
171 {
172 	struct net_data *n = NULL;
173 	boolean_t found = B_FALSE;
174 	neti_stack_t *nts;
175 
176 	nts = net_getnetistackbyid(netid);
177 	ASSERT(nts != NULL);
178 
179 	if (info == NULL)
180 		found = B_TRUE;
181 
182 	mutex_enter(&nts->nts_lock);
183 	LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
184 		if (found) {
185 			/*
186 			 * We are only interested in finding protocols that
187 			 * are not in some sort of shutdown state.  There is
188 			 * no need to check for netd_stack==NULL because
189 			 * that implies it is no longer on this list.
190 			 */
191 			if (n->netd_condemned == 0)
192 				continue;
193 			break;
194 		}
195 
196 		if (n == info)
197 			found = B_TRUE;
198 	}
199 
200 	if (info != NULL)
201 		(void) net_protocol_release(info);
202 
203 	if (n != NULL)
204 		atomic_add_32((uint_t *)&n->netd_refcnt, 1);
205 
206 	mutex_exit(&nts->nts_lock);
207 
208 	return (n);
209 }
210 
211 /*
212  * Public accessor functions
213  */
214 int
215 net_getifname(net_handle_t info, phy_if_t nic, char *buffer,
216     const size_t buflen)
217 {
218 
219 	ASSERT(info != NULL);
220 
221 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
222 		return (-1);
223 
224 	return (info->netd_info.netp_getifname(info, nic, buffer, buflen));
225 }
226 
227 int
228 net_getmtu(net_handle_t info, phy_if_t nic, lif_if_t ifdata)
229 {
230 
231 	ASSERT(info != NULL);
232 
233 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
234 		return (-1);
235 
236 	return (info->netd_info.netp_getmtu(info, nic, ifdata));
237 }
238 
239 int
240 net_getpmtuenabled(net_handle_t info)
241 {
242 
243 	ASSERT(info != NULL);
244 
245 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
246 		return (-1);
247 
248 	return (info->netd_info.netp_getpmtuenabled(info));
249 }
250 
251 int
252 net_getlifaddr(net_handle_t info, phy_if_t nic, lif_if_t ifdata,
253     int nelem, net_ifaddr_t type[], void *storage)
254 {
255 
256 	ASSERT(info != NULL);
257 
258 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
259 		return (-1);
260 
261 	return (info->netd_info.netp_getlifaddr(info, nic, ifdata,
262 	    nelem, type, storage));
263 }
264 
265 phy_if_t
266 net_phygetnext(net_handle_t info, phy_if_t nic)
267 {
268 
269 	ASSERT(info != NULL);
270 
271 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
272 		return ((phy_if_t)-1);
273 
274 	return (info->netd_info.netp_phygetnext(info, nic));
275 }
276 
277 phy_if_t
278 net_phylookup(net_handle_t info, const char *name)
279 {
280 
281 	ASSERT(info != NULL);
282 
283 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
284 		return ((phy_if_t)-1);
285 
286 	return (info->netd_info.netp_phylookup(info, name));
287 }
288 
289 lif_if_t
290 net_lifgetnext(net_handle_t info, phy_if_t ifidx, lif_if_t ifdata)
291 {
292 
293 	ASSERT(info != NULL);
294 
295 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
296 		return ((lif_if_t)-1);
297 
298 	return (info->netd_info.netp_lifgetnext(info, ifidx, ifdata));
299 }
300 
301 int
302 net_inject(net_handle_t info, inject_t style, net_inject_t *packet)
303 {
304 
305 	ASSERT(info != NULL);
306 
307 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
308 		return (-1);
309 
310 	return (info->netd_info.netp_inject(info, style, packet));
311 }
312 
313 phy_if_t
314 net_routeto(net_handle_t info, struct sockaddr *address, struct sockaddr *next)
315 {
316 
317 	ASSERT(info != NULL);
318 
319 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
320 		return ((phy_if_t)-1);
321 
322 	return (info->netd_info.netp_routeto(info, address, next));
323 }
324 
325 int
326 net_ispartialchecksum(net_handle_t info, mblk_t *mp)
327 {
328 
329 	ASSERT(info != NULL);
330 	ASSERT(mp != NULL);
331 
332 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
333 		return (-1);
334 
335 	return (info->netd_info.netp_ispartialchecksum(info, mp));
336 }
337 
338 int
339 net_isvalidchecksum(net_handle_t info, mblk_t *mp)
340 {
341 
342 	ASSERT(info != NULL);
343 	ASSERT(mp != NULL);
344 
345 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
346 		return (-1);
347 
348 	return (info->netd_info.netp_isvalidchecksum(info, mp));
349 }
350 
351 /*
352  * Hooks related functions
353  */
354 
355 /*
356  * Function:	net_family_register
357  * Returns:	int - 0 = Succ, Else = Fail
358  * Parameters:	info(I) - protocol
359  *		hf(I) - family pointer
360  *
361  * Call hook_family_add to register family
362  *
363  * There is no need to bump netd_refcnt in the two functions
364  * net_family_register and net_family_unregister because the caller of these
365  * two functions is assumed to "own" a reference on 'info' via an earlier
366  * call to net_protocol_register().  Thus the owner is expected to do a
367  * call to net_protocol_unregister() after having done a
368  * net_family_unregister() to make sure things are properly cleaned up.
369  */
370 int
371 net_family_register(net_handle_t info, hook_family_t *hf)
372 {
373 	hook_family_int_t *hfi;
374 	netstack_t *ns;
375 
376 	ASSERT(info != NULL);
377 	ASSERT(hf != NULL);
378 
379 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
380 		return (ESHUTDOWN);
381 
382 	if (info->netd_hooks != NULL)
383 		return (EEXIST);
384 
385 	ns = info->netd_stack->nts_netstack;
386 	ASSERT(ns != NULL);
387 	hfi = hook_family_add(hf, ns->netstack_hook);
388 	if (hfi == NULL)
389 		return (EEXIST);
390 
391 	info->netd_hooks = hfi;
392 	return (0);
393 }
394 
395 /*
396  * Function:	net_family_unregister
397  * Returns:	int - transparent value, explained by caller
398  * Parameters:	info(I) - protocol
399  *		hf(I) - family pointer
400  *
401  * Call hook_family_remove to unregister family
402  */
403 int
404 net_family_unregister(net_handle_t info, hook_family_t *hf)
405 {
406 	int ret;
407 
408 	ASSERT(info != NULL);
409 	ASSERT(hf != NULL);
410 
411 	if (info->netd_hooks == NULL)
412 		return (ENXIO);
413 
414 	if (strcmp(info->netd_hooks->hfi_family.hf_name,
415 	    hf->hf_name) != 0)
416 		return (EINVAL);
417 
418 	ret = hook_family_remove(info->netd_hooks);
419 	if (ret == 0)
420 		info->netd_hooks = NULL;
421 
422 	return (ret);
423 }
424 
425 /*
426  * Function:	net_event_register
427  * Returns:	internal event pointer - NULL = Fail
428  * Parameters:	info(I) - protocol
429  *		he(I) - event pointer
430  *
431  * Call hook_event_add to register event on specific family
432  * 	Internal event pointer is returned so caller can get
433  * 	handle to run hooks
434  */
435 hook_event_token_t
436 net_event_register(net_handle_t info, hook_event_t *he)
437 {
438 	hook_event_int_t *hei;
439 
440 	ASSERT(info != NULL);
441 	ASSERT(he != NULL);
442 
443 	if (info->netd_hooks == NULL || info->netd_condemned != 0 ||
444 	    info->netd_stack == NULL)
445 		return (NULL);
446 
447 	hei = hook_event_add(info->netd_hooks, he);
448 	return ((hook_event_token_t)hei);
449 }
450 
451 /*
452  * Function:	net_event_unregister
453  * Returns:	int - transparent value, explained by caller
454  * Parameters:	info(I) - protocol
455  *		he(I) - event pointer
456  *
457  * Call hook_event_remove to unregister event on specific family
458  */
459 int
460 net_event_unregister(net_handle_t info, hook_event_t *he)
461 {
462 
463 	ASSERT(info != NULL);
464 	ASSERT(he != NULL);
465 
466 	if (info->netd_hooks == NULL)
467 		return (ENXIO);
468 
469 	return (hook_event_remove(info->netd_hooks, he));
470 }
471 
472 /*
473  * Function:	net_hook_register
474  * Returns:	int - transparent value, explained by caller
475  * Parameters:	info(I) - protocol
476  *		event(I) - event name
477  *		h(I) - hook pointer
478  *
479  * Call hook_register to add hook on specific family/event
480  */
481 int
482 net_hook_register(net_handle_t info, char *event, hook_t *h)
483 {
484 
485 	ASSERT(info != NULL);
486 	ASSERT(event != NULL);
487 	ASSERT(h != NULL);
488 
489 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
490 		return (ESHUTDOWN);
491 
492 	if (info->netd_hooks == NULL)
493 		return (ENXIO);
494 
495 	return (hook_register(info->netd_hooks, event, h));
496 }
497 
498 /*
499  * Function:	net_hook_unregister
500  * Returns:	int - transparent value, explained by caller
501  * Parameters:	info(I) - protocol
502  *		event(I) - event name
503  *		h(I) - hook pointer
504  *
505  * Call hook_unregister to remove hook on specific family/event
506  */
507 int
508 net_hook_unregister(net_handle_t info, char *event, hook_t *h)
509 {
510 
511 	ASSERT(info != NULL);
512 	ASSERT(event != NULL);
513 	ASSERT(h != NULL);
514 
515 	if (info->netd_hooks == NULL)
516 		return (ENXIO);
517 
518 	return (hook_unregister(info->netd_hooks, event, h));
519 }
520 
521 netid_t
522 net_getnetid(net_handle_t netd)
523 {
524 
525 	if (netd->netd_stack == NULL)
526 		return (-1);
527 	return (netd->netd_stack->nts_id);
528 }
529 
530 net_inject_t *
531 net_inject_alloc(const int version)
532 {
533 	net_inject_t *ni;
534 
535 	ni = kmem_zalloc(sizeof (*ni), KM_NOSLEEP);
536 	if (ni == NULL)
537 		return (NULL);
538 
539 	ni->ni_version = version;
540 	return (ni);
541 }
542 
543 void
544 net_inject_free(net_inject_t *ni)
545 {
546 	kmem_free(ni, sizeof (*ni));
547 }
548 
549 kstat_t *
550 net_kstat_create(netid_t netid, char *module, int instance, char *name,
551     char *class, uchar_t type, ulong_t ndata, uchar_t ks_flag)
552 {
553 	netstackid_t stackid = net_getnetstackidbynetid(netid);
554 
555 	if (stackid == -1)
556 		return (NULL);
557 
558 	return (kstat_create_netstack(module, instance, name, class, type,
559 	    ndata, ks_flag, stackid));
560 }
561 
562 void
563 net_kstat_delete(netid_t netid, kstat_t *ks)
564 {
565 	netstackid_t stackid = net_getnetstackidbynetid(netid);
566 
567 	if (stackid != -1)
568 		kstat_delete_netstack(ks, stackid);
569 }
570 
571 int
572 net_event_notify_register(net_handle_t family, char *event,
573     hook_notify_fn_t callback, void *arg)
574 {
575 	int error;
576 
577 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
578 		return (ESHUTDOWN);
579 
580 	error = hook_event_notify_register(family->netd_hooks, event,
581 	    callback, arg);
582 
583 	return (error);
584 }
585 
586 int
587 net_event_notify_unregister(net_handle_t family, char *event,
588     hook_notify_fn_t callback)
589 {
590 	int error;
591 
592 	error = hook_event_notify_unregister(family->netd_hooks, event,
593 	    callback);
594 
595 	return (error);
596 }
597 
598 int
599 net_protocol_notify_register(net_handle_t family, hook_notify_fn_t callback,
600     void *arg)
601 {
602 	int error;
603 
604 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
605 		return (ESHUTDOWN);
606 
607 	error = hook_family_notify_register(family->netd_hooks, callback,
608 	    arg);
609 
610 	return (error);
611 }
612 
613 int
614 net_protocol_notify_unregister(net_handle_t family, hook_notify_fn_t callback)
615 {
616 	int error;
617 
618 	error = hook_family_notify_unregister(family->netd_hooks, callback);
619 
620 	return (error);
621 }
622