xref: /illumos-gate/usr/src/uts/common/io/neti_impl.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
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_inc_32((uint_t *)&nd->netd_refcnt);
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_dec_32_nv((uint_t *)&info->netd_refcnt) == 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_inc_32((uint_t *)&n->netd_refcnt);
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 	netstack_t *ns;
405 
406 	ASSERT(info != NULL);
407 	ASSERT(hf != NULL);
408 
409 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
410 		return (ESHUTDOWN);
411 
412 	if (info->netd_hooks != NULL)
413 		return (EEXIST);
414 
415 	ns = info->netd_stack->nts_netstack;
416 	ASSERT(ns != NULL);
417 	if (hook_family_add(hf, ns->netstack_hook,
418 	    (void **)&info->netd_hooks) == NULL)
419 		return (EEXIST);
420 
421 	return (0);
422 }
423 
424 /*
425  * Function:	net_family_unregister
426  * Returns:	int - transparent value, explained by caller
427  * Parameters:	info(I) - protocol
428  *		hf(I) - family pointer
429  *
430  * Call hook_family_remove to unregister family
431  */
432 int
433 net_family_unregister(net_handle_t info, hook_family_t *hf)
434 {
435 	int ret;
436 
437 	ASSERT(info != NULL);
438 	ASSERT(hf != NULL);
439 
440 	if (info->netd_hooks == NULL)
441 		return (ENXIO);
442 
443 	if (strcmp(info->netd_hooks->hfi_family.hf_name,
444 	    hf->hf_name) != 0)
445 		return (EINVAL);
446 
447 	ret = hook_family_remove(info->netd_hooks);
448 	if (ret == 0)
449 		info->netd_hooks = NULL;
450 
451 	return (ret);
452 }
453 
454 int
455 net_family_shutdown(net_handle_t info, hook_family_t *hf)
456 {
457 
458 	ASSERT(info != NULL);
459 	ASSERT(hf != NULL);
460 
461 	if (info->netd_hooks == NULL)
462 		return (ENXIO);
463 
464 	if (strcmp(info->netd_hooks->hfi_family.hf_name,
465 	    hf->hf_name) != 0)
466 		return (EINVAL);
467 
468 	return (hook_family_shutdown(info->netd_hooks));
469 }
470 
471 /*
472  * Function:	net_event_register
473  * Returns:	internal event pointer - NULL = Fail
474  * Parameters:	info(I) - protocol
475  *		he(I) - event pointer
476  *
477  * Call hook_event_add to register event on specific family
478  * 	Internal event pointer is returned so caller can get
479  * 	handle to run hooks
480  */
481 hook_event_token_t
482 net_event_register(net_handle_t info, hook_event_t *he)
483 {
484 	hook_event_int_t *hei;
485 
486 	ASSERT(info != NULL);
487 	ASSERT(he != NULL);
488 
489 	if (info->netd_hooks == NULL || info->netd_condemned != 0 ||
490 	    info->netd_stack == NULL)
491 		return (NULL);
492 
493 	hei = hook_event_add(info->netd_hooks, he);
494 	return ((hook_event_token_t)hei);
495 }
496 
497 /*
498  * Function:	net_event_unregister
499  * Returns:	int - transparent value, explained by caller
500  * Parameters:	info(I) - protocol
501  *		he(I) - event pointer
502  *
503  * Call hook_event_remove to unregister event on specific family
504  */
505 int
506 net_event_unregister(net_handle_t info, hook_event_t *he)
507 {
508 
509 	ASSERT(info != NULL);
510 	ASSERT(he != NULL);
511 
512 	if (info->netd_hooks == NULL)
513 		return (ENXIO);
514 
515 	return (hook_event_remove(info->netd_hooks, he));
516 }
517 
518 int
519 net_event_shutdown(net_handle_t info, hook_event_t *he)
520 {
521 
522 	ASSERT(info != NULL);
523 	ASSERT(he != NULL);
524 
525 	if (info->netd_hooks == NULL)
526 		return (ENXIO);
527 
528 	return (hook_event_shutdown(info->netd_hooks, he));
529 }
530 
531 /*
532  * Function:	net_hook_register
533  * Returns:	int - transparent value, explained by caller
534  * Parameters:	info(I) - protocol
535  *		event(I) - event name
536  *		h(I) - hook pointer
537  *
538  * Call hook_register to add hook on specific family/event
539  */
540 int
541 net_hook_register(net_handle_t info, char *event, hook_t *h)
542 {
543 
544 	ASSERT(info != NULL);
545 	ASSERT(event != NULL);
546 	ASSERT(h != NULL);
547 
548 	if (info->netd_condemned != 0 || info->netd_stack == NULL)
549 		return (ESHUTDOWN);
550 
551 	if (info->netd_hooks == NULL)
552 		return (ENXIO);
553 
554 	return (hook_register(info->netd_hooks, event, h));
555 }
556 
557 /*
558  * Function:	net_hook_unregister
559  * Returns:	int - transparent value, explained by caller
560  * Parameters:	info(I) - protocol
561  *		event(I) - event name
562  *		h(I) - hook pointer
563  *
564  * Call hook_unregister to remove hook on specific family/event
565  */
566 int
567 net_hook_unregister(net_handle_t info, char *event, hook_t *h)
568 {
569 
570 	ASSERT(info != NULL);
571 	ASSERT(event != NULL);
572 	ASSERT(h != NULL);
573 
574 	if (info->netd_hooks == NULL)
575 		return (ENXIO);
576 
577 	return (hook_unregister(info->netd_hooks, event, h));
578 }
579 
580 netid_t
581 net_getnetid(net_handle_t netd)
582 {
583 
584 	if (netd->netd_stack == NULL)
585 		return (-1);
586 	return (netd->netd_stack->nts_id);
587 }
588 
589 net_inject_t *
590 net_inject_alloc(const int version)
591 {
592 	net_inject_t *ni;
593 
594 	ni = kmem_zalloc(sizeof (*ni), KM_NOSLEEP);
595 	if (ni == NULL)
596 		return (NULL);
597 
598 	ni->ni_version = version;
599 	return (ni);
600 }
601 
602 void
603 net_inject_free(net_inject_t *ni)
604 {
605 	kmem_free(ni, sizeof (*ni));
606 }
607 
608 kstat_t *
609 net_kstat_create(netid_t netid, char *module, int instance, char *name,
610     char *class, uchar_t type, ulong_t ndata, uchar_t ks_flag)
611 {
612 	netstackid_t stackid = net_getnetstackidbynetid(netid);
613 
614 	if (stackid == -1)
615 		return (NULL);
616 
617 	return (kstat_create_netstack(module, instance, name, class, type,
618 	    ndata, ks_flag, stackid));
619 }
620 
621 void
622 net_kstat_delete(netid_t netid, kstat_t *ks)
623 {
624 	netstackid_t stackid = net_getnetstackidbynetid(netid);
625 
626 	if (stackid != -1)
627 		kstat_delete_netstack(ks, stackid);
628 }
629 
630 int
631 net_event_notify_register(net_handle_t family, char *event,
632     hook_notify_fn_t callback, void *arg)
633 {
634 	int error;
635 
636 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
637 		return (ESHUTDOWN);
638 
639 	error = hook_event_notify_register(family->netd_hooks, event,
640 	    callback, arg);
641 
642 	return (error);
643 }
644 
645 int
646 net_event_notify_unregister(net_handle_t family, char *event,
647     hook_notify_fn_t callback)
648 {
649 	int error;
650 
651 	error = hook_event_notify_unregister(family->netd_hooks, event,
652 	    callback);
653 
654 	return (error);
655 }
656 
657 int
658 net_protocol_notify_register(net_handle_t family, hook_notify_fn_t callback,
659     void *arg)
660 {
661 	int error;
662 
663 	if (family->netd_condemned != 0 || family->netd_stack == NULL)
664 		return (ESHUTDOWN);
665 
666 	error = hook_family_notify_register(family->netd_hooks, callback,
667 	    arg);
668 
669 	return (error);
670 }
671 
672 int
673 net_protocol_notify_unregister(net_handle_t family, hook_notify_fn_t callback)
674 {
675 	int error;
676 
677 	error = hook_family_notify_unregister(family->netd_hooks, callback);
678 
679 	return (error);
680 }
681