xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_ctl.c (revision b6c3f786)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Virtual Network Interface Card (VNIC)
30  */
31 
32 #include <sys/ddi.h>
33 #include <sys/strsun.h>
34 #include <sys/dld.h>
35 #include <sys/dld_impl.h>
36 #include <sys/conf.h>
37 #include <sys/modctl.h>
38 #include <sys/stat.h>
39 #include <sys/vnic.h>
40 #include <sys/vnic_impl.h>
41 #include <inet/common.h>
42 
43 /* module description */
44 #define	VNIC_LINKINFO		"VNIC MAC"
45 #define	VNIC_DRIVER_NAME	"vnic"
46 
47 /* device info ptr, only one for instance 0 */
48 static dev_info_t *vnic_dip = NULL;
49 
50 /* for control interface */
51 static int vnic_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
52 static int vnic_attach(dev_info_t *, ddi_attach_cmd_t);
53 static int vnic_detach(dev_info_t *, ddi_detach_cmd_t);
54 static int vnic_open(queue_t *, dev_t *, int, int, cred_t *);
55 static int vnic_close(queue_t *);
56 static void vnic_wput(queue_t *, mblk_t *);
57 static void vnic_ioctl(queue_t *, mblk_t *);
58 
59 static int vnic_ioc_create(mblk_t *, int);
60 static int vnic_ioc_modify(mblk_t *, int);
61 static int vnic_ioc_delete(mblk_t *, int);
62 static int vnic_ioc_info(mblk_t *, int);
63 
64 typedef struct ioc_cmd_s {
65 	int ic_cmd;
66 	int (*ic_func)(mblk_t *, int);
67 } ioc_cmd_t;
68 
69 static ioc_cmd_t ioc_cmd[] = {
70 	{VNIC_IOC_CREATE, vnic_ioc_create},
71 	{VNIC_IOC_DELETE, vnic_ioc_delete},
72 	{VNIC_IOC_INFO, vnic_ioc_info},
73 	{VNIC_IOC_MODIFY, vnic_ioc_modify}
74 };
75 
76 #define	IOC_CMD_SZ (sizeof (ioc_cmd) / sizeof (ioc_cmd_t))
77 
78 /*
79  * mi_hiwat is set to 1 because of the flow control mechanism implemented
80  * in dld. refer to the comments in dld_str.c for details.
81  */
82 static struct module_info vnic_module_info = {
83 	0,
84 	VNIC_DRIVER_NAME,
85 	0,
86 	INFPSZ,
87 	1,
88 	0
89 };
90 
91 static struct qinit vnic_r_qinit = {	/* read queues */
92 	NULL,
93 	NULL,
94 	vnic_open,
95 	vnic_close,
96 	NULL,
97 	&vnic_module_info
98 };
99 
100 static struct qinit vnic_w_qinit = {	/* write queues */
101 	(pfi_t)dld_wput,
102 	(pfi_t)dld_wsrv,
103 	NULL,
104 	NULL,
105 	NULL,
106 	&vnic_module_info
107 };
108 
109 /*
110  * Entry points for vnic control node
111  */
112 static struct qinit vnic_w_ctl_qinit = {
113 	(pfi_t)vnic_wput,
114 	NULL,
115 	NULL,
116 	NULL,
117 	NULL,
118 	&vnic_module_info
119 };
120 
121 static struct streamtab vnic_streamtab = {
122 	&vnic_r_qinit,
123 	&vnic_w_qinit
124 };
125 
126 DDI_DEFINE_STREAM_OPS(vnic_dev_ops, nulldev, nulldev, vnic_attach, vnic_detach,
127     nodev, vnic_getinfo, D_MP, &vnic_streamtab);
128 
129 static struct modldrv vnic_modldrv = {
130 	&mod_driverops,		/* Type of module.  This one is a driver */
131 	VNIC_LINKINFO,		/* short description */
132 	&vnic_dev_ops		/* driver specific ops */
133 };
134 
135 static struct modlinkage modlinkage = {
136 	MODREV_1,
137 	&vnic_modldrv,
138 	NULL
139 };
140 
141 int
142 _init(void)
143 {
144 	return (mod_install(&modlinkage));
145 }
146 
147 int
148 _fini(void)
149 {
150 	return (mod_remove(&modlinkage));
151 }
152 
153 int
154 _info(struct modinfo *modinfop)
155 {
156 	return (mod_info(&modlinkage, modinfop));
157 }
158 
159 static int
160 vnic_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
161 {
162 	if (q->q_ptr != NULL)
163 		return (EBUSY);
164 
165 	if (getminor(*devp) == VNIC_CTL_NODE_MINOR) {
166 		dld_str_t	*dsp;
167 
168 		dsp = dld_str_create(q, DLD_CONTROL, getmajor(*devp),
169 		    DL_STYLE1);
170 		if (dsp == NULL)
171 			return (ENOSR);
172 
173 		/*
174 		 * The ioctl handling callback to process control ioctl
175 		 * messages; see comments above dld_ioctl() for details.
176 		 */
177 		dsp->ds_ioctl = vnic_ioctl;
178 
179 		/*
180 		 * The VNIC control node uses its own set of entry points.
181 		 */
182 		WR(q)->q_qinfo = &vnic_w_ctl_qinit;
183 		*devp = makedevice(getmajor(*devp), dsp->ds_minor);
184 		qprocson(q);
185 		return (0);
186 	}
187 	return (dld_open(q, devp, flag, sflag, credp));
188 }
189 
190 static int
191 vnic_close(queue_t *q)
192 {
193 	dld_str_t	*dsp = q->q_ptr;
194 
195 	if (dsp->ds_type == DLD_CONTROL) {
196 		qprocsoff(q);
197 		dld_finish_pending_task(dsp);
198 		dsp->ds_ioctl = NULL;
199 		dld_str_destroy(dsp);
200 		return (0);
201 	}
202 	return (dld_close(q));
203 }
204 
205 static void
206 vnic_ioctl(queue_t *wq, mblk_t *mp)
207 {
208 	/* LINTED alignment */
209 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
210 	int i, err = EINVAL;
211 	mblk_t *nmp;
212 
213 	if (mp->b_cont == NULL) {
214 		err = EINVAL;
215 		goto done;
216 	}
217 
218 	/*
219 	 * Construct contiguous message
220 	 */
221 	if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) {
222 		freemsg(mp->b_cont);
223 		err = ENOMEM;
224 		goto done;
225 	}
226 
227 	freemsg(mp->b_cont);
228 	mp->b_cont = nmp;
229 
230 	for (i = 0; i < IOC_CMD_SZ; i++) {
231 		if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) {
232 			err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag);
233 			break;
234 		}
235 	}
236 
237 	if (err == 0) {
238 		int len = 0;
239 
240 		if (mp->b_cont != NULL)
241 			len = MBLKL(mp->b_cont);
242 		miocack(wq, mp, len, 0);
243 		return;
244 	}
245 
246 done:
247 	miocnak(wq, mp, 0, err);
248 }
249 
250 static void
251 vnic_wput(queue_t *q, mblk_t *mp)
252 {
253 	if (DB_TYPE(mp) == M_IOCTL)
254 		dld_ioctl(q, mp);
255 	else
256 		freemsg(mp);
257 }
258 
259 static void
260 vnic_init(void)
261 {
262 	vnic_dev_init();
263 	vnic_bcast_init();
264 	vnic_classifier_init();
265 }
266 
267 static void
268 vnic_fini(void)
269 {
270 	vnic_dev_fini();
271 	vnic_bcast_fini();
272 	vnic_classifier_fini();
273 }
274 
275 dev_info_t *
276 vnic_get_dip(void)
277 {
278 	return (vnic_dip);
279 }
280 
281 /*ARGSUSED*/
282 static int
283 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
284     void **result)
285 {
286 	switch (infocmd) {
287 	case DDI_INFO_DEVT2DEVINFO:
288 		*result = vnic_dip;
289 		return (DDI_SUCCESS);
290 	case DDI_INFO_DEVT2INSTANCE:
291 		*result = NULL;
292 		return (DDI_SUCCESS);
293 	}
294 	return (DDI_FAILURE);
295 }
296 
297 static int
298 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
299 {
300 	switch (cmd) {
301 	case DDI_ATTACH:
302 		if (ddi_get_instance(dip) != 0) {
303 			/* we only allow instance 0 to attach */
304 			return (DDI_FAILURE);
305 		}
306 
307 		/* create minor node for control interface */
308 		if (ddi_create_minor_node(dip, VNIC_CTL_NODE_NAME, S_IFCHR,
309 		    VNIC_CTL_NODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
310 			return (DDI_FAILURE);
311 		}
312 
313 		vnic_dip = dip;
314 		vnic_init();
315 
316 		return (DDI_SUCCESS);
317 
318 	case DDI_RESUME:
319 		return (DDI_SUCCESS);
320 
321 	default:
322 		return (DDI_FAILURE);
323 	}
324 }
325 
326 /*ARGSUSED*/
327 static int
328 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
329 {
330 	switch (cmd) {
331 	case DDI_DETACH:
332 		/*
333 		 * Allow the VNIC instance to be detached only if there
334 		 * are not VNICs configured.
335 		 */
336 		if (vnic_dev_count() > 0)
337 			return (DDI_FAILURE);
338 
339 		vnic_dip = NULL;
340 		vnic_fini();
341 		ddi_remove_minor_node(dip, VNIC_CTL_NODE_NAME);
342 
343 		return (DDI_SUCCESS);
344 
345 	case DDI_SUSPEND:
346 		return (DDI_SUCCESS);
347 
348 	default:
349 		return (DDI_FAILURE);
350 	}
351 }
352 
353 /*
354  * Process a VNICIOC_CREATE request.
355  */
356 static int
357 vnic_ioc_create(mblk_t *mp, int mode)
358 {
359 	STRUCT_HANDLE(vnic_ioc_create, create_arg);
360 	int rc = 0;
361 	int mac_len;
362 	uchar_t mac_addr[MAXMACADDRLEN];
363 	datalink_id_t vnic_id, linkid;
364 	vnic_mac_addr_type_t mac_addr_type;
365 
366 	STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr);
367 	if (MBLKL(mp->b_cont) < STRUCT_SIZE(create_arg))
368 		return (EINVAL);
369 
370 	/*
371 	 * VNIC link id
372 	 */
373 	vnic_id = STRUCT_FGET(create_arg, vc_vnic_id);
374 
375 	/*
376 	 * Linkid of the link the VNIC is defined on top of.
377 	 */
378 	linkid = STRUCT_FGET(create_arg, vc_link_id);
379 
380 	/* MAC address */
381 	mac_addr_type = STRUCT_FGET(create_arg, vc_mac_addr_type);
382 	mac_len = STRUCT_FGET(create_arg, vc_mac_len);
383 
384 	switch (mac_addr_type) {
385 	case VNIC_MAC_ADDR_TYPE_FIXED:
386 		bcopy(STRUCT_FGET(create_arg, vc_mac_addr), mac_addr,
387 		    MAXMACADDRLEN);
388 		break;
389 	default:
390 		return (ENOTSUP);
391 	}
392 
393 	rc = vnic_dev_create(vnic_id, linkid, mac_len, mac_addr);
394 	return (rc);
395 }
396 
397 static int
398 vnic_ioc_modify(mblk_t *mp, int mode)
399 {
400 	STRUCT_HANDLE(vnic_ioc_modify, modify_arg);
401 	int err = 0;
402 	datalink_id_t vnic_id;
403 	uint_t modify_mask;
404 	vnic_mac_addr_type_t mac_addr_type;
405 	uint_t mac_len;
406 	uchar_t mac_addr[MAXMACADDRLEN];
407 
408 	STRUCT_SET_HANDLE(modify_arg, mode, (void *)mp->b_cont->b_rptr);
409 	if (MBLKL(mp->b_cont) < STRUCT_SIZE(modify_arg))
410 		return (EINVAL);
411 
412 	vnic_id = STRUCT_FGET(modify_arg, vm_vnic_id);
413 	modify_mask = STRUCT_FGET(modify_arg, vm_modify_mask);
414 
415 	if (modify_mask & VNIC_IOC_MODIFY_ADDR) {
416 		mac_addr_type = STRUCT_FGET(modify_arg, vm_mac_addr_type);
417 		mac_len = STRUCT_FGET(modify_arg, vm_mac_len);
418 		bcopy(STRUCT_FGET(modify_arg, vm_mac_addr), mac_addr,
419 		    MAXMACADDRLEN);
420 	}
421 
422 	err = vnic_dev_modify(vnic_id, modify_mask, mac_addr_type,
423 	    mac_len, mac_addr);
424 	return (err);
425 }
426 
427 static int
428 vnic_ioc_delete(mblk_t *mp, int mode)
429 {
430 	STRUCT_HANDLE(vnic_ioc_delete, delete_arg);
431 	datalink_id_t vnic_id;
432 	int err = 0;
433 
434 	STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr);
435 	if (STRUCT_SIZE(delete_arg) > MBLKL(mp))
436 		return (EINVAL);
437 
438 	vnic_id = STRUCT_FGET(delete_arg, vd_vnic_id);
439 	err = vnic_dev_delete(vnic_id);
440 	return (err);
441 }
442 
443 typedef struct vnic_ioc_info_state {
444 	uint32_t bytes_left;
445 	uchar_t *where;
446 } vnic_ioc_info_state_t;
447 
448 static int
449 vnic_ioc_info_new_vnic(void *arg, datalink_id_t id,
450     vnic_mac_addr_type_t addr_type, uint_t mac_len, uint8_t *mac_addr,
451     datalink_id_t linkid)
452 {
453 	vnic_ioc_info_state_t *state = arg;
454 	/*LINTED*/
455 	vnic_ioc_info_vnic_t *vn = (vnic_ioc_info_vnic_t *)state->where;
456 
457 	if (state->bytes_left < sizeof (*vn))
458 		return (ENOSPC);
459 
460 	vn->vn_vnic_id = id;
461 	vn->vn_link_id = linkid;
462 	vn->vn_mac_addr_type = addr_type;
463 	vn->vn_mac_len = mac_len;
464 	bcopy(mac_addr, &(vn->vn_mac_addr), mac_len);
465 
466 	state->where += sizeof (*vn);
467 	state->bytes_left -= sizeof (*vn);
468 
469 	return (0);
470 }
471 
472 /* ARGSUSED */
473 static int
474 vnic_ioc_info(mblk_t *mp, int mode)
475 {
476 	vnic_ioc_info_t *info_argp;
477 	int rc, len;
478 	uint32_t nvnics;
479 	datalink_id_t vnic_id, linkid;
480 	vnic_ioc_info_state_t state;
481 
482 	if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp))
483 		return (EINVAL);
484 
485 	/* LINTED alignment */
486 	info_argp = (vnic_ioc_info_t *)mp->b_cont->b_rptr;
487 
488 	/*
489 	 * ID of the vnic to return or vnic device.
490 	 * If zero, the call returns information
491 	 * regarding all vnics currently defined.
492 	 */
493 	vnic_id = info_argp->vi_vnic_id;
494 	linkid = info_argp->vi_linkid;
495 
496 	state.bytes_left = len - sizeof (vnic_ioc_info_t);
497 	state.where = (uchar_t *)(info_argp +1);
498 
499 	rc = vnic_info(&nvnics, vnic_id, linkid, &state,
500 	    vnic_ioc_info_new_vnic);
501 	return (rc);
502 }
503