xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_ctl.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 	ddi_quiesce_not_supported,	/* dev quiesce */
89 };
90 
91 static struct modldrv vnic_modldrv = {
92 	&mod_driverops,		/* Type of module.  This one is a driver */
93 	VNIC_LINKINFO,		/* short description */
94 	&vnic_dev_ops		/* driver specific ops */
95 };
96 
97 static struct modlinkage modlinkage = {
98 	MODREV_1,
99 	&vnic_modldrv,
100 	NULL
101 };
102 
103 int
104 _init(void)
105 {
106 	int	err;
107 
108 	mac_init_ops(&vnic_dev_ops, "vnic");
109 	if ((err = mod_install(&modlinkage)) != 0)
110 		mac_fini_ops(&vnic_dev_ops);
111 	return (err);
112 }
113 
114 int
115 _fini(void)
116 {
117 	int	err;
118 
119 	if ((err = mod_remove(&modlinkage)) == 0)
120 		mac_fini_ops(&vnic_dev_ops);
121 	return (err);
122 }
123 
124 int
125 _info(struct modinfo *modinfop)
126 {
127 	return (mod_info(&modlinkage, modinfop));
128 }
129 
130 static void
131 vnic_init(void)
132 {
133 	vnic_dev_init();
134 	vnic_bcast_init();
135 	vnic_classifier_init();
136 }
137 
138 static void
139 vnic_fini(void)
140 {
141 	vnic_dev_fini();
142 	vnic_bcast_fini();
143 	vnic_classifier_fini();
144 }
145 
146 dev_info_t *
147 vnic_get_dip(void)
148 {
149 	return (vnic_dip);
150 }
151 
152 /*ARGSUSED*/
153 static int
154 vnic_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
155     void **result)
156 {
157 	switch (infocmd) {
158 	case DDI_INFO_DEVT2DEVINFO:
159 		*result = vnic_dip;
160 		return (DDI_SUCCESS);
161 	case DDI_INFO_DEVT2INSTANCE:
162 		*result = 0;
163 		return (DDI_SUCCESS);
164 	}
165 	return (DDI_FAILURE);
166 }
167 
168 static int
169 vnic_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
170 {
171 	switch (cmd) {
172 	case DDI_ATTACH:
173 		if (ddi_get_instance(dip) != 0) {
174 			/* we only allow instance 0 to attach */
175 			return (DDI_FAILURE);
176 		}
177 
178 		if (dld_ioc_register(VNIC_IOC, vnic_ioc_list,
179 		    DLDIOCCNT(vnic_ioc_list)) != 0)
180 			return (DDI_FAILURE);
181 
182 		vnic_dip = dip;
183 		vnic_init();
184 
185 		return (DDI_SUCCESS);
186 
187 	case DDI_RESUME:
188 		return (DDI_SUCCESS);
189 
190 	default:
191 		return (DDI_FAILURE);
192 	}
193 }
194 
195 /*ARGSUSED*/
196 static int
197 vnic_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
198 {
199 	switch (cmd) {
200 	case DDI_DETACH:
201 		/*
202 		 * Allow the VNIC instance to be detached only if there
203 		 * are not VNICs configured.
204 		 */
205 		if (vnic_dev_count() > 0)
206 			return (DDI_FAILURE);
207 
208 		vnic_dip = NULL;
209 		vnic_fini();
210 		dld_ioc_unregister(VNIC_IOC);
211 
212 		return (DDI_SUCCESS);
213 
214 	case DDI_SUSPEND:
215 		return (DDI_SUCCESS);
216 
217 	default:
218 		return (DDI_FAILURE);
219 	}
220 }
221 
222 /*
223  * Process a VNIC_IOC_CREATE request.
224  */
225 /* ARGSUSED */
226 static int
227 vnic_ioc_create(void *karg, intptr_t arg, int mode, cred_t *cred)
228 {
229 	vnic_ioc_create_t *create_arg = karg;
230 	int mac_len;
231 	uchar_t mac_addr[MAXMACADDRLEN];
232 	datalink_id_t vnic_id, linkid;
233 	vnic_mac_addr_type_t mac_addr_type;
234 
235 	/*
236 	 * VNIC link id
237 	 */
238 	vnic_id = create_arg->vc_vnic_id;
239 
240 	/*
241 	 * Linkid of the link the VNIC is defined on top of.
242 	 */
243 	linkid = create_arg->vc_link_id;
244 
245 	/* MAC address */
246 	mac_addr_type = create_arg->vc_mac_addr_type;
247 	mac_len = create_arg->vc_mac_len;
248 
249 	switch (mac_addr_type) {
250 	case VNIC_MAC_ADDR_TYPE_FIXED:
251 		bcopy(create_arg->vc_mac_addr, mac_addr, MAXMACADDRLEN);
252 		break;
253 	default:
254 		return (ENOTSUP);
255 	}
256 
257 	return (vnic_dev_create(vnic_id, linkid, mac_len, mac_addr));
258 }
259 
260 /* ARGSUSED */
261 static int
262 vnic_ioc_modify(void *karg, intptr_t arg, int mode, cred_t *cred)
263 {
264 	vnic_ioc_modify_t *modify_arg = karg;
265 	datalink_id_t vnic_id;
266 	uint_t modify_mask;
267 	vnic_mac_addr_type_t mac_addr_type;
268 	uint_t mac_len;
269 	uchar_t mac_addr[MAXMACADDRLEN];
270 
271 	vnic_id = modify_arg->vm_vnic_id;
272 	modify_mask = modify_arg->vm_modify_mask;
273 
274 	if (modify_mask & VNIC_IOC_MODIFY_ADDR) {
275 		mac_addr_type = modify_arg->vm_mac_addr_type;
276 		mac_len = modify_arg->vm_mac_len;
277 		bcopy(modify_arg->vm_mac_addr, mac_addr, MAXMACADDRLEN);
278 	}
279 
280 	return (vnic_dev_modify(vnic_id, modify_mask, mac_addr_type,
281 	    mac_len, mac_addr));
282 }
283 
284 /* ARGSUSED */
285 static int
286 vnic_ioc_delete(void *karg, intptr_t arg, int mode, cred_t *cred)
287 {
288 	vnic_ioc_delete_t *delete_arg = karg;
289 
290 	return (vnic_dev_delete(delete_arg->vd_vnic_id));
291 }
292 
293 typedef struct vnic_ioc_info_state {
294 	uint32_t	bytes_left;
295 	uchar_t		*where;
296 	int		mode;
297 } vnic_ioc_info_state_t;
298 
299 static int
300 vnic_ioc_info_new_vnic(void *arg, datalink_id_t id,
301     vnic_mac_addr_type_t addr_type, uint_t mac_len, uint8_t *mac_addr,
302     datalink_id_t linkid)
303 {
304 	vnic_ioc_info_state_t *state = arg;
305 	/*LINTED*/
306 	vnic_ioc_info_vnic_t *vn = (vnic_ioc_info_vnic_t *)state->where;
307 
308 	if (state->bytes_left < sizeof (*vn))
309 		return (ENOSPC);
310 
311 	vn->vn_vnic_id = id;
312 	vn->vn_link_id = linkid;
313 	vn->vn_mac_addr_type = addr_type;
314 	vn->vn_mac_len = mac_len;
315 	if (ddi_copyout(mac_addr, &(vn->vn_mac_addr), mac_len,
316 	    state->mode) != 0)
317 		return (EFAULT);
318 
319 	state->where += sizeof (*vn);
320 	state->bytes_left -= sizeof (*vn);
321 
322 	return (0);
323 }
324 
325 /* ARGSUSED */
326 static int
327 vnic_ioc_info(void *karg, intptr_t arg, int mode, cred_t *cred)
328 {
329 	vnic_ioc_info_t *info_argp = karg;
330 	uint32_t nvnics;
331 	datalink_id_t vnic_id, linkid;
332 	vnic_ioc_info_state_t state;
333 
334 	/*
335 	 * ID of the vnic to return or vnic device.
336 	 * If zero, the call returns information
337 	 * regarding all vnics currently defined.
338 	 */
339 	vnic_id = info_argp->vi_vnic_id;
340 	linkid = info_argp->vi_linkid;
341 
342 	state.bytes_left = info_argp->vi_size;
343 	state.where = (uchar_t *)(arg + sizeof (vnic_ioc_info_t));
344 	state.mode = mode;
345 
346 	return (vnic_info(&nvnics, vnic_id, linkid, &state,
347 	    vnic_ioc_info_new_vnic));
348 }
349