xref: /illumos-gate/usr/src/uts/common/io/neti_impl.c (revision 4a9b8375b74044a5b0fc0191eb42b835e72b65c0)
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 int
266 net_getlifzone(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata,
267     zoneid_t *zoneid)
268 {
269 	ASSERT(info != NULL);
270 
271 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
272 		return (-1);
273 
274 	return (info->netd_info.neti_getlifzone(info, phy_ifdata, ifdata,
275 	    zoneid));
276 }
277 
278 int
279 net_getlifflags(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata,
280     uint64_t *flags)
281 {
282 	ASSERT(info != NULL);
283 
284 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
285 		return (-1);
286 
287 	return (info->netd_info.neti_getlifflags(info, phy_ifdata, ifdata,
288 	    flags));
289 }
290 
291 phy_if_t
292 net_phygetnext(net_handle_t info, phy_if_t nic)
293 {
294 
295 	ASSERT(info != NULL);
296 
297 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
298 		return ((phy_if_t)-1);
299 
300 	return (info->netd_info.netp_phygetnext(info, nic));
301 }
302 
303 phy_if_t
304 net_phylookup(net_handle_t info, const char *name)
305 {
306 
307 	ASSERT(info != NULL);
308 
309 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
310 		return ((phy_if_t)-1);
311 
312 	return (info->netd_info.netp_phylookup(info, name));
313 }
314 
315 lif_if_t
316 net_lifgetnext(net_handle_t info, phy_if_t ifidx, lif_if_t ifdata)
317 {
318 
319 	ASSERT(info != NULL);
320 
321 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
322 		return ((lif_if_t)-1);
323 
324 	return (info->netd_info.netp_lifgetnext(info, ifidx, ifdata));
325 }
326 
327 int
328 net_inject(net_handle_t info, inject_t style, net_inject_t *packet)
329 {
330 
331 	ASSERT(info != NULL);
332 
333 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
334 		return (-1);
335 
336 	return (info->netd_info.netp_inject(info, style, packet));
337 }
338 
339 phy_if_t
340 net_routeto(net_handle_t info, struct sockaddr *address, struct sockaddr *next)
341 {
342 
343 	ASSERT(info != NULL);
344 
345 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
346 		return ((phy_if_t)-1);
347 
348 	return (info->netd_info.netp_routeto(info, address, next));
349 }
350 
351 int
352 net_ispartialchecksum(net_handle_t info, mblk_t *mp)
353 {
354 
355 	ASSERT(info != NULL);
356 	ASSERT(mp != NULL);
357 
358 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
359 		return (-1);
360 
361 	return (info->netd_info.netp_ispartialchecksum(info, mp));
362 }
363 
364 int
365 net_isvalidchecksum(net_handle_t info, mblk_t *mp)
366 {
367 
368 	ASSERT(info != NULL);
369 	ASSERT(mp != NULL);
370 
371 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
372 		return (-1);
373 
374 	return (info->netd_info.netp_isvalidchecksum(info, mp));
375 }
376 
377 /*
378  * Hooks related functions
379  */
380 
381 /*
382  * Function:	net_family_register
383  * Returns:	int - 0 = Succ, Else = Fail
384  * Parameters:	info(I) - protocol
385  *		hf(I) - family pointer
386  *
387  * Call hook_family_add to register family
388  *
389  * There is no need to bump netd_refcnt in the two functions
390  * net_family_register and net_family_unregister because the caller of these
391  * two functions is assumed to "own" a reference on 'info' via an earlier
392  * call to net_protocol_register().  Thus the owner is expected to do a
393  * call to net_protocol_unregister() after having done a
394  * net_family_unregister() to make sure things are properly cleaned up.
395  * Passing a pointer to info->netd_hooks into hook_family_add is required
396  * so that this can be set before the notify functions are called. If this
397  * does not happen, the notify function may do something that seems fine,
398  * like add a notify function to the family but cause a panic because
399  * netd_hooks is NULL when we get to hook_family_notify_register.
400  */
401 int
402 net_family_register(net_handle_t info, hook_family_t *hf)
403 {
404 	hook_family_int_t *hfi;
405 	netstack_t *ns;
406 
407 	ASSERT(info != NULL);
408 	ASSERT(hf != NULL);
409 
410 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
411 		return (ESHUTDOWN);
412 
413 	if (info->netd_hooks != NULL)
414 		return (EEXIST);
415 
416 	ns = info->netd_stack->nts_netstack;
417 	ASSERT(ns != NULL);
418 	if (hook_family_add(hf, ns->netstack_hook,
419 	    (void **)&info->netd_hooks) == NULL)
420 		return (EEXIST);
421 
422 	return (0);
423 }
424 
425 /*
426  * Function:	net_family_unregister
427  * Returns:	int - transparent value, explained by caller
428  * Parameters:	info(I) - protocol
429  *		hf(I) - family pointer
430  *
431  * Call hook_family_remove to unregister family
432  */
433 int
434 net_family_unregister(net_handle_t info, hook_family_t *hf)
435 {
436 	int ret;
437 
438 	ASSERT(info != NULL);
439 	ASSERT(hf != NULL);
440 
441 	if (info->netd_hooks == NULL)
442 		return (ENXIO);
443 
444 	if (strcmp(info->netd_hooks->hfi_family.hf_name,
445 	    hf->hf_name) != 0)
446 		return (EINVAL);
447 
448 	ret = hook_family_remove(info->netd_hooks);
449 	if (ret == 0)
450 		info->netd_hooks = NULL;
451 
452 	return (ret);
453 }
454 
455 int
456 net_family_shutdown(net_handle_t info, hook_family_t *hf)
457 {
458 
459 	ASSERT(info != NULL);
460 	ASSERT(hf != NULL);
461 
462 	if (info->netd_hooks == NULL)
463 		return (ENXIO);
464 
465 	if (strcmp(info->netd_hooks->hfi_family.hf_name,
466 	    hf->hf_name) != 0)
467 		return (EINVAL);
468 
469 	return (hook_family_shutdown(info->netd_hooks));
470 }
471 
472 /*
473  * Function:	net_event_register
474  * Returns:	internal event pointer - NULL = Fail
475  * Parameters:	info(I) - protocol
476  *		he(I) - event pointer
477  *
478  * Call hook_event_add to register event on specific family
479  * 	Internal event pointer is returned so caller can get
480  * 	handle to run hooks
481  */
482 hook_event_token_t
483 net_event_register(net_handle_t info, hook_event_t *he)
484 {
485 	hook_event_int_t *hei;
486 
487 	ASSERT(info != NULL);
488 	ASSERT(he != NULL);
489 
490 	if (info->netd_hooks == NULL || info->netd_condemned != 0 ||
491 	    info->netd_stack == NULL)
492 		return (NULL);
493 
494 	hei = hook_event_add(info->netd_hooks, he);
495 	return ((hook_event_token_t)hei);
496 }
497 
498 /*
499  * Function:	net_event_unregister
500  * Returns:	int - transparent value, explained by caller
501  * Parameters:	info(I) - protocol
502  *		he(I) - event pointer
503  *
504  * Call hook_event_remove to unregister event on specific family
505  */
506 int
507 net_event_unregister(net_handle_t info, hook_event_t *he)
508 {
509 
510 	ASSERT(info != NULL);
511 	ASSERT(he != NULL);
512 
513 	if (info->netd_hooks == NULL)
514 		return (ENXIO);
515 
516 	return (hook_event_remove(info->netd_hooks, he));
517 }
518 
519 int
520 net_event_shutdown(net_handle_t info, hook_event_t *he)
521 {
522 
523 	ASSERT(info != NULL);
524 	ASSERT(he != NULL);
525 
526 	if (info->netd_hooks == NULL)
527 		return (ENXIO);
528 
529 	return (hook_event_shutdown(info->netd_hooks, he));
530 }
531 
532 /*
533  * Function:	net_hook_register
534  * Returns:	int - transparent value, explained by caller
535  * Parameters:	info(I) - protocol
536  *		event(I) - event name
537  *		h(I) - hook pointer
538  *
539  * Call hook_register to add hook on specific family/event
540  */
541 int
542 net_hook_register(net_handle_t info, char *event, hook_t *h)
543 {
544 
545 	ASSERT(info != NULL);
546 	ASSERT(event != NULL);
547 	ASSERT(h != NULL);
548 
549 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
550 		return (ESHUTDOWN);
551 
552 	if (info->netd_hooks == NULL)
553 		return (ENXIO);
554 
555 	return (hook_register(info->netd_hooks, event, h));
556 }
557 
558 /*
559  * Function:	net_hook_unregister
560  * Returns:	int - transparent value, explained by caller
561  * Parameters:	info(I) - protocol
562  *		event(I) - event name
563  *		h(I) - hook pointer
564  *
565  * Call hook_unregister to remove hook on specific family/event
566  */
567 int
568 net_hook_unregister(net_handle_t info, char *event, hook_t *h)
569 {
570 
571 	ASSERT(info != NULL);
572 	ASSERT(event != NULL);
573 	ASSERT(h != NULL);
574 
575 	if (info->netd_hooks == NULL)
576 		return (ENXIO);
577 
578 	return (hook_unregister(info->netd_hooks, event, h));
579 }
580 
581 netid_t
582 net_getnetid(net_handle_t netd)
583 {
584 
585 	if (netd->netd_stack == NULL)
586 		return (-1);
587 	return (netd->netd_stack->nts_id);
588 }
589 
590 net_inject_t *
591 net_inject_alloc(const int version)
592 {
593 	net_inject_t *ni;
594 
595 	ni = kmem_zalloc(sizeof (*ni), KM_NOSLEEP);
596 	if (ni == NULL)
597 		return (NULL);
598 
599 	ni->ni_version = version;
600 	return (ni);
601 }
602 
603 void
604 net_inject_free(net_inject_t *ni)
605 {
606 	kmem_free(ni, sizeof (*ni));
607 }
608 
609 kstat_t *
610 net_kstat_create(netid_t netid, char *module, int instance, char *name,
611     char *class, uchar_t type, ulong_t ndata, uchar_t ks_flag)
612 {
613 	netstackid_t stackid = net_getnetstackidbynetid(netid);
614 
615 	if (stackid == -1)
616 		return (NULL);
617 
618 	return (kstat_create_netstack(module, instance, name, class, type,
619 	    ndata, ks_flag, stackid));
620 }
621 
622 void
623 net_kstat_delete(netid_t netid, kstat_t *ks)
624 {
625 	netstackid_t stackid = net_getnetstackidbynetid(netid);
626 
627 	if (stackid != -1)
628 		kstat_delete_netstack(ks, stackid);
629 }
630 
631 int
632 net_event_notify_register(net_handle_t family, char *event,
633     hook_notify_fn_t callback, void *arg)
634 {
635 	int error;
636 
637 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
638 		return (ESHUTDOWN);
639 
640 	error = hook_event_notify_register(family->netd_hooks, event,
641 	    callback, arg);
642 
643 	return (error);
644 }
645 
646 int
647 net_event_notify_unregister(net_handle_t family, char *event,
648     hook_notify_fn_t callback)
649 {
650 	int error;
651 
652 	error = hook_event_notify_unregister(family->netd_hooks, event,
653 	    callback);
654 
655 	return (error);
656 }
657 
658 int
659 net_protocol_notify_register(net_handle_t family, hook_notify_fn_t callback,
660     void *arg)
661 {
662 	int error;
663 
664 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
665 		return (ESHUTDOWN);
666 
667 	error = hook_family_notify_register(family->netd_hooks, callback,
668 	    arg);
669 
670 	return (error);
671 }
672 
673 int
674 net_protocol_notify_unregister(net_handle_t family, hook_notify_fn_t callback)
675 {
676 	int error;
677 
678 	error = hook_family_notify_unregister(family->netd_hooks, callback);
679 
680 	return (error);
681 }
682