xref: /titanic_41/usr/src/uts/common/inet/iptun/iptun_dev.c (revision 8f588c8376a0fa02470fc4554ec83442b305020f)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * IP Tunneling Driver
28  *
29  * As viewed from the top, this module is a GLDv3 driver that consumes the
30  * mac driver interfaces.  It implements the logic for various forms of IP
31  * (IPv4 or IPv6) encapsulation within IP (IPv4 or IPv6).
32  */
33 
34 #include <sys/file.h>
35 #include <sys/list.h>
36 #include "iptun_impl.h"
37 
38 #define	IPTUN_LINKINFO		"IP tunneling driver"
39 #define	IPTUN_HASHSZ		67
40 
41 dev_info_t	*iptun_dip;
42 ldi_ident_t	iptun_ldi_ident;
43 
44 static int	iptun_attach(dev_info_t *, ddi_attach_cmd_t);
45 static int	iptun_detach(dev_info_t *, ddi_detach_cmd_t);
46 static int	iptun_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
47 static int	iptun_constructor(void *, void *, int);
48 static void	iptun_destructor(void *, void *);
49 
50 DDI_DEFINE_STREAM_OPS(iptun_dev_ops, nulldev, nulldev, iptun_attach,
51     iptun_detach, nodev, iptun_getinfo, D_MP, NULL, ddi_quiesce_not_supported);
52 
53 static struct modldrv iptun_modldrv = {
54 	&mod_driverops,
55 	IPTUN_LINKINFO,
56 	&iptun_dev_ops
57 };
58 
59 static struct modlinkage iptun_modlinkage = {
60 	MODREV_1,
61 	&iptun_modldrv,
62 	NULL
63 };
64 
65 /*
66  * Initialize the tunnel stack instance.
67  */
68 /* ARGSUSED */
69 static void *
70 iptun_stack_init(netstackid_t stackid, netstack_t *ns)
71 {
72 	iptun_stack_t	*iptuns;
73 
74 	iptuns = kmem_zalloc(sizeof (*iptuns), KM_SLEEP);
75 	iptuns->iptuns_netstack = ns;
76 	mutex_init(&iptuns->iptuns_lock, NULL, MUTEX_DEFAULT, NULL);
77 	list_create(&iptuns->iptuns_iptunlist, sizeof (iptun_t),
78 	    offsetof(iptun_t, iptun_link));
79 
80 	return (iptuns);
81 }
82 
83 /* ARGSUSED */
84 static void
85 iptun_stack_shutdown(netstackid_t stackid, void *arg)
86 {
87 	iptun_stack_t	*iptuns = arg;
88 	iptun_t		*iptun;
89 	datalink_id_t	linkid;
90 
91 	/* note that iptun_delete() removes iptun from the list */
92 	while ((iptun = list_head(&iptuns->iptuns_iptunlist)) != NULL) {
93 		linkid = iptun->iptun_linkid;
94 		(void) iptun_delete(linkid, iptun->iptun_cred);
95 		(void) dls_mgmt_destroy(linkid, B_FALSE);
96 	}
97 	if (iptuns->iptuns_g_q != NULL)
98 		(void) ldi_close(iptuns->iptuns_g_q_lh, FWRITE|FREAD, CRED());
99 }
100 
101 /*
102  * Free the tunnel stack instance.
103  */
104 /* ARGSUSED */
105 static void
106 iptun_stack_fini(netstackid_t stackid, void *arg)
107 {
108 	iptun_stack_t *iptuns = arg;
109 
110 	list_destroy(&iptuns->iptuns_iptunlist);
111 	mutex_destroy(&iptuns->iptuns_lock);
112 	kmem_free(iptuns, sizeof (*iptuns));
113 }
114 
115 static void
116 iptun_fini(void)
117 {
118 	ddi_taskq_destroy(iptun_taskq);
119 	mac_fini_ops(&iptun_dev_ops);
120 	ldi_ident_release(iptun_ldi_ident);
121 	mod_hash_destroy_idhash(iptun_hash);
122 	kmem_cache_destroy(iptun_cache);
123 }
124 
125 int
126 _init(void)
127 {
128 	int rc;
129 
130 	rc = ldi_ident_from_mod(&iptun_modlinkage, &iptun_ldi_ident);
131 	if (rc != 0)
132 		return (rc);
133 
134 	iptun_cache = kmem_cache_create("iptun_cache", sizeof (iptun_t), 0,
135 	    iptun_constructor, iptun_destructor, NULL, NULL, NULL, 0);
136 	if (iptun_cache == NULL) {
137 		ldi_ident_release(iptun_ldi_ident);
138 		return (ENOMEM);
139 	}
140 
141 	iptun_taskq = ddi_taskq_create(NULL, "iptun_taskq", 1,
142 	    TASKQ_DEFAULTPRI, 0);
143 	if (iptun_taskq == NULL) {
144 		ldi_ident_release(iptun_ldi_ident);
145 		kmem_cache_destroy(iptun_cache);
146 		return (ENOMEM);
147 	}
148 
149 	iptun_hash = mod_hash_create_idhash("iptun_hash", IPTUN_HASHSZ,
150 	    mod_hash_null_valdtor);
151 
152 	mac_init_ops(&iptun_dev_ops, IPTUN_DRIVER_NAME);
153 
154 	if ((rc = mod_install(&iptun_modlinkage)) != 0)
155 		iptun_fini();
156 	return (rc);
157 }
158 
159 int
160 _fini(void)
161 {
162 	int rc;
163 
164 	if ((rc = mod_remove(&iptun_modlinkage)) == 0)
165 		iptun_fini();
166 	return (rc);
167 }
168 
169 int
170 _info(struct modinfo *modinfop)
171 {
172 	return (mod_info(&iptun_modlinkage, modinfop));
173 }
174 
175 static int
176 iptun_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
177 {
178 	switch (cmd) {
179 	case DDI_ATTACH:
180 		if (ddi_get_instance(dip) != 0 || iptun_ioc_init() != 0)
181 			return (DDI_FAILURE);
182 		iptun_dip = dip;
183 		netstack_register(NS_IPTUN, iptun_stack_init,
184 		    iptun_stack_shutdown, iptun_stack_fini);
185 		return (DDI_SUCCESS);
186 
187 	default:
188 		return (DDI_FAILURE);
189 	}
190 }
191 
192 /* ARGSUSED */
193 static int
194 iptun_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
195 {
196 	switch (cmd) {
197 	case DDI_DETACH:
198 		/*
199 		 * We prevent the pseudo device from detaching (and thus the
200 		 * driver from unloading) when there are tunnels configured by
201 		 * consulting iptun_count().  We don't need to hold a lock
202 		 * here because the tunnel count is only changed when a tunnel
203 		 * is created or deleted, which can't happen while the detach
204 		 * routine is running (the ioctl path calls
205 		 * ddi_hold_devi_by_instance() in dld's drv_ioctl(), and the
206 		 * /dev/net implicit path has the device open).
207 		 */
208 		if (iptun_count() > 0)
209 			return (DDI_FAILURE);
210 		netstack_unregister(NS_IPTUN);
211 		iptun_dip = NULL;
212 		iptun_ioc_fini();
213 		return (DDI_SUCCESS);
214 
215 	default:
216 		return (DDI_FAILURE);
217 	}
218 }
219 
220 /* ARGSUSED */
221 static int
222 iptun_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
223 {
224 	switch (infocmd) {
225 	case DDI_INFO_DEVT2DEVINFO:
226 		*result = iptun_dip;
227 		return (DDI_SUCCESS);
228 	case DDI_INFO_DEVT2INSTANCE:
229 		*result = NULL;
230 		return (DDI_SUCCESS);
231 	}
232 	return (DDI_FAILURE);
233 }
234 
235 /* ARGSUSED */
236 static int
237 iptun_constructor(void *buf, void *cdrarg, int kmflags)
238 {
239 	iptun_t	*iptun = buf;
240 
241 	bzero(iptun, sizeof (*iptun));
242 	mutex_init(&iptun->iptun_lock, NULL, MUTEX_DEFAULT, NULL);
243 	cv_init(&iptun->iptun_upcall_cv, NULL, CV_DRIVER, NULL);
244 	cv_init(&iptun->iptun_enter_cv, NULL, CV_DRIVER, NULL);
245 
246 	return (0);
247 }
248 
249 /* ARGSUSED */
250 static void
251 iptun_destructor(void *buf, void *cdrarg)
252 {
253 	iptun_t *iptun = buf;
254 
255 	/* This iptun_t must not still be in use. */
256 	ASSERT(!(iptun->iptun_flags & (IPTUN_BOUND|IPTUN_MAC_REGISTERED|
257 	    IPTUN_MAC_STARTED|IPTUN_HASH_INSERTED|IPTUN_UPCALL_PENDING)));
258 
259 	mutex_destroy(&iptun->iptun_lock);
260 	cv_destroy(&iptun->iptun_upcall_cv);
261 }
262