xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_ctl.c (revision f6f4cb8ada400367a1921f6b93fb9e02f53ac5e6)
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 /*
27  * Virtual Network Interface Card (VNIC)
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/modctl.h>
32 #include <sys/vnic.h>
33 #include <sys/vnic_impl.h>
34 #include <inet/common.h>
35 
36 /* module description */
37 #define	VNIC_LINKINFO		"VNIC MAC"
38 
39 /* device info ptr, only one for instance 0 */
40 static dev_info_t *vnic_dip = NULL;
41 static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
42 static int vnic_attach(dev_info_t *, ddi_attach_cmd_t);
43 static int vnic_detach(dev_info_t *, ddi_detach_cmd_t);
44 static dld_ioc_func_t vnic_ioc_create, vnic_ioc_modify, vnic_ioc_delete,
45     vnic_ioc_info;
46 
47 static dld_ioc_info_t vnic_ioc_list[] = {
48 	{VNIC_IOC_CREATE, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_create_t),
49 	    vnic_ioc_create},
50 	{VNIC_IOC_DELETE, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_delete_t),
51 	    vnic_ioc_delete},
52 	{VNIC_IOC_INFO, DLDCOPYINOUT, sizeof (vnic_ioc_info_t),
53 	    vnic_ioc_info},
54 	{VNIC_IOC_MODIFY, DLDCOPYIN | DLDDLCONFIG, sizeof (vnic_ioc_modify_t),
55 	    vnic_ioc_modify}
56 };
57 
58 static struct cb_ops vnic_cb_ops = {
59 	nulldev,		/* open */
60 	nulldev,		/* close */
61 	nulldev,		/* strategy */
62 	nulldev,		/* print */
63 	nodev,			/* dump */
64 	nodev,			/* read */
65 	nodev,			/* write */
66 	nodev,			/* ioctl */
67 	nodev,			/* devmap */
68 	nodev,			/* mmap */
69 	nodev,			/* segmap */
70 	nochpoll,		/* poll */
71 	ddi_prop_op,		/* cb_prop_op */
72 	0,			/* streamtab  */
73 	D_MP			/* Driver compatibility flag */
74 };
75 
76 static struct dev_ops vnic_dev_ops = {
77 	DEVO_REV,		/* devo_rev */
78 	0,			/* refcnt */
79 	vnic_getinfo,		/* get_dev_info */
80 	nulldev,		/* identify */
81 	nulldev,		/* probe */
82 	vnic_attach,		/* attach */
83 	vnic_detach,		/* detach */
84 	nodev,			/* reset */
85 	&vnic_cb_ops,		/* driver operations */
86 	NULL,			/* bus operations */
87 	nodev			/* dev power */
88 };
89 
90 static struct modldrv vnic_modldrv = {
91 	&mod_driverops,		/* Type of module.  This one is a driver */
92 	VNIC_LINKINFO,		/* short description */
93 	&vnic_dev_ops		/* driver specific ops */
94 };
95 
96 static struct modlinkage modlinkage = {
97 	MODREV_1,
98 	&vnic_modldrv,
99 	NULL
100 };
101 
102 int
103 _init(void)
104 {
105 	int	err;
106 
107 	mac_init_ops(&vnic_dev_ops, "vnic");
108 	if ((err = mod_install(&modlinkage)) != 0)
109 		mac_fini_ops(&vnic_dev_ops);
110 	return (err);
111 }
112 
113 int
114 _fini(void)
115 {
116 	int	err;
117 
118 	if ((err = mod_remove(&modlinkage)) == 0)
119 		mac_fini_ops(&vnic_dev_ops);
120 	return (err);
121 }
122 
123 int
124 _info(struct modinfo *modinfop)
125 {
126 	return (mod_info(&modlinkage, modinfop));
127 }
128 
129 static void
130 vnic_init(void)
131 {
132 	vnic_dev_init();
133 	vnic_bcast_init();
134 	vnic_classifier_init();
135 }
136 
137 static void
138 vnic_fini(void)
139 {
140 	vnic_dev_fini();
141 	vnic_bcast_fini();
142 	vnic_classifier_fini();
143 }
144 
145 dev_info_t *
146 vnic_get_dip(void)
147 {
148 	return (vnic_dip);
149 }
150 
151 /*ARGSUSED*/
152 static int
153 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
154     void **result)
155 {
156 	switch (infocmd) {
157 	case DDI_INFO_DEVT2DEVINFO:
158 		*result = vnic_dip;
159 		return (DDI_SUCCESS);
160 	case DDI_INFO_DEVT2INSTANCE:
161 		*result = 0;
162 		return (DDI_SUCCESS);
163 	}
164 	return (DDI_FAILURE);
165 }
166 
167 static int
168 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
169 {
170 	switch (cmd) {
171 	case DDI_ATTACH:
172 		if (ddi_get_instance(dip) != 0) {
173 			/* we only allow instance 0 to attach */
174 			return (DDI_FAILURE);
175 		}
176 
177 		if (dld_ioc_register(VNIC_IOC, vnic_ioc_list,
178 		    DLDIOCCNT(vnic_ioc_list)) != 0)
179 			return (DDI_FAILURE);
180 
181 		vnic_dip = dip;
182 		vnic_init();
183 
184 		return (DDI_SUCCESS);
185 
186 	case DDI_RESUME:
187 		return (DDI_SUCCESS);
188 
189 	default:
190 		return (DDI_FAILURE);
191 	}
192 }
193 
194 /*ARGSUSED*/
195 static int
196 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
197 {
198 	switch (cmd) {
199 	case DDI_DETACH:
200 		/*
201 		 * Allow the VNIC instance to be detached only if there
202 		 * are not VNICs configured.
203 		 */
204 		if (vnic_dev_count() > 0)
205 			return (DDI_FAILURE);
206 
207 		vnic_dip = NULL;
208 		vnic_fini();
209 		dld_ioc_unregister(VNIC_IOC);
210 
211 		return (DDI_SUCCESS);
212 
213 	case DDI_SUSPEND:
214 		return (DDI_SUCCESS);
215 
216 	default:
217 		return (DDI_FAILURE);
218 	}
219 }
220 
221 /*
222  * Process a VNIC_IOC_CREATE request.
223  */
224 /* ARGSUSED */
225 static int
226 vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred)
227 {
228 	vnic_ioc_create_t *create_arg = karg;
229 	int mac_len;
230 	uchar_t mac_addr[MAXMACADDRLEN];
231 	datalink_id_t vnic_id, linkid;
232 	vnic_mac_addr_type_t mac_addr_type;
233 
234 	/*
235 	 * VNIC link id
236 	 */
237 	vnic_id = create_arg->vc_vnic_id;
238 
239 	/*
240 	 * Linkid of the link the VNIC is defined on top of.
241 	 */
242 	linkid = create_arg->vc_link_id;
243 
244 	/* MAC address */
245 	mac_addr_type = create_arg->vc_mac_addr_type;
246 	mac_len = create_arg->vc_mac_len;
247 
248 	switch (mac_addr_type) {
249 	case VNIC_MAC_ADDR_TYPE_FIXED:
250 		bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
251 		break;
252 	default:
253 		return (ENOTSUP);
254 	}
255 
256 	return (vnic_dev_create(vnic_id, linkid, mac_len, mac_addr));
257 }
258 
259 /* ARGSUSED */
260 static int
261 vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred)
262 {
263 	vnic_ioc_modify_t *modify_arg = karg;
264 	datalink_id_t vnic_id;
265 	uint_t modify_mask;
266 	vnic_mac_addr_type_t mac_addr_type;
267 	uint_t mac_len;
268 	uchar_t mac_addr[MAXMACADDRLEN];
269 
270 	vnic_id = modify_arg->vm_vnic_id;
271 	modify_mask = modify_arg->vm_modify_mask;
272 
273 	if (modify_mask & VNIC_IOC_MODIFY_ADDR) {
274 		mac_addr_type = modify_arg->vm_mac_addr_type;
275 		mac_len = modify_arg->vm_mac_len;
276 		bcopy(modify_arg->vm_mac_addr, mac_addr, MAXMACADDRLEN);
277 	}
278 
279 	return (vnic_dev_modify(vnic_id, modify_mask, mac_addr_type,
280 	    mac_len, mac_addr));
281 }
282 
283 /* ARGSUSED */
284 static int
285 vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred)
286 {
287 	vnic_ioc_delete_t *delete_arg = karg;
288 
289 	return (vnic_dev_delete(delete_arg->vd_vnic_id));
290 }
291 
292 typedef struct vnic_ioc_info_state {
293 	uint32_t	bytes_left;
294 	uchar_t		*where;
295 	int		mode;
296 } vnic_ioc_info_state_t;
297 
298 static int
299 vnic_ioc_info_new_vnic(void *arg, datalink_id_t id,
300     vnic_mac_addr_type_t addr_type, uint_t mac_len, uint8_t *mac_addr,
301     datalink_id_t linkid)
302 {
303 	vnic_ioc_info_state_t *state = arg;
304 	/*LINTED*/
305 	vnic_ioc_info_vnic_t *vn = (vnic_ioc_info_vnic_t *)state->where;
306 
307 	if (state->bytes_left < sizeof (*vn))
308 		return (ENOSPC);
309 
310 	vn->vn_vnic_id = id;
311 	vn->vn_link_id = linkid;
312 	vn->vn_mac_addr_type = addr_type;
313 	vn->vn_mac_len = mac_len;
314 	if (ddi_copyout(mac_addr, &(vn->vn_mac_addr), mac_len,
315 	    state->mode) != 0)
316 		return (EFAULT);
317 
318 	state->where += sizeof (*vn);
319 	state->bytes_left -= sizeof (*vn);
320 
321 	return (0);
322 }
323 
324 /* ARGSUSED */
325 static int
326 vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred)
327 {
328 	vnic_ioc_info_t *info_argp = karg;
329 	uint32_t nvnics;
330 	datalink_id_t vnic_id, linkid;
331 	vnic_ioc_info_state_t state;
332 
333 	/*
334 	 * ID of the vnic to return or vnic device.
335 	 * If zero, the call returns information
336 	 * regarding all vnics currently defined.
337 	 */
338 	vnic_id = info_argp->vi_vnic_id;
339 	linkid = info_argp->vi_linkid;
340 
341 	state.bytes_left = info_argp->vi_size;
342 	state.where = (uchar_t *)(arg + sizeof (vnic_ioc_info_t));
343 	state.mode = mode;
344 
345 	return (vnic_info(&nvnics, vnic_id, linkid, &state,
346 	    vnic_ioc_info_new_vnic));
347 }
348