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