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