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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/types.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <sys/mac.h>
29 #include <sys/dls_mgmt.h>
30 #include <sys/dlpi.h>
31 #include <net/simnet.h>
32 #include <errno.h>
33 #include <unistd.h>
34 
35 #include <libdladm_impl.h>
36 #include <libdllink.h>
37 #include <libdlaggr.h>
38 #include <libdlsim.h>
39 
40 static dladm_status_t dladm_simnet_persist_conf(dladm_handle_t, const char *,
41     dladm_simnet_attr_t *);
42 
43 /* New simnet instance creation */
44 static dladm_status_t
i_dladm_create_simnet(dladm_handle_t handle,dladm_simnet_attr_t * attrp)45 i_dladm_create_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
46 {
47 	int rc;
48 	dladm_status_t status = DLADM_STATUS_OK;
49 	simnet_ioc_create_t ioc;
50 
51 	bzero(&ioc, sizeof (ioc));
52 	ioc.sic_link_id = attrp->sna_link_id;
53 	ioc.sic_type = attrp->sna_type;
54 	if (attrp->sna_mac_len > 0 && attrp->sna_mac_len <= MAXMACADDRLEN) {
55 		ioc.sic_mac_len = attrp->sna_mac_len;
56 		bcopy(attrp->sna_mac_addr, ioc.sic_mac_addr, ioc.sic_mac_len);
57 	}
58 
59 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_CREATE, &ioc);
60 	if (rc < 0)
61 		status = dladm_errno2status(errno);
62 
63 	if (status != DLADM_STATUS_OK)
64 		return (status);
65 
66 	bcopy(ioc.sic_mac_addr, attrp->sna_mac_addr, MAXMACADDRLEN);
67 	attrp->sna_mac_len = ioc.sic_mac_len;
68 	return (status);
69 }
70 
71 /* Modify existing simnet instance */
72 static dladm_status_t
i_dladm_modify_simnet(dladm_handle_t handle,dladm_simnet_attr_t * attrp)73 i_dladm_modify_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
74 {
75 	int rc;
76 	dladm_status_t status = DLADM_STATUS_OK;
77 	simnet_ioc_modify_t ioc;
78 
79 	bzero(&ioc, sizeof (ioc));
80 	ioc.sim_link_id = attrp->sna_link_id;
81 	ioc.sim_peer_link_id = attrp->sna_peer_link_id;
82 
83 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_MODIFY, &ioc);
84 	if (rc < 0)
85 		status = dladm_errno2status(errno);
86 
87 	return (status);
88 }
89 
90 /* Delete simnet instance */
91 static dladm_status_t
i_dladm_delete_simnet(dladm_handle_t handle,dladm_simnet_attr_t * attrp)92 i_dladm_delete_simnet(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
93 {
94 	int rc;
95 	dladm_status_t status = DLADM_STATUS_OK;
96 	simnet_ioc_delete_t ioc;
97 
98 	bzero(&ioc, sizeof (ioc));
99 	ioc.sid_link_id = attrp->sna_link_id;
100 
101 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_DELETE, &ioc);
102 	if (rc < 0)
103 		status = dladm_errno2status(errno);
104 
105 	return (status);
106 }
107 
108 /* Retrieve simnet instance information */
109 static dladm_status_t
i_dladm_get_simnet_info(dladm_handle_t handle,dladm_simnet_attr_t * attrp)110 i_dladm_get_simnet_info(dladm_handle_t handle, dladm_simnet_attr_t *attrp)
111 {
112 	int rc;
113 	dladm_status_t status = DLADM_STATUS_OK;
114 	simnet_ioc_info_t ioc;
115 
116 	bzero(&ioc, sizeof (ioc));
117 	ioc.sii_link_id = attrp->sna_link_id;
118 
119 	rc = ioctl(dladm_dld_fd(handle), SIMNET_IOC_INFO, &ioc);
120 	if (rc < 0) {
121 		status = dladm_errno2status(errno);
122 		return (status);
123 	}
124 
125 	bcopy(ioc.sii_mac_addr, attrp->sna_mac_addr, MAXMACADDRLEN);
126 	attrp->sna_mac_len = ioc.sii_mac_len;
127 	attrp->sna_peer_link_id = ioc.sii_peer_link_id;
128 	attrp->sna_type = ioc.sii_type;
129 	return (status);
130 }
131 
132 /* Retrieve simnet configuratin */
133 static dladm_status_t
i_dladm_get_simnet_info_persist(dladm_handle_t handle,dladm_simnet_attr_t * attrp)134 i_dladm_get_simnet_info_persist(dladm_handle_t handle,
135     dladm_simnet_attr_t *attrp)
136 {
137 	dladm_conf_t conf;
138 	dladm_status_t status;
139 	char macstr[ETHERADDRL * 3];
140 	char simnetpeer[MAXLINKNAMELEN];
141 	uint64_t u64;
142 	boolean_t mac_fixed;
143 
144 	if ((status = dladm_getsnap_conf(handle, attrp->sna_link_id,
145 	    &conf)) != DLADM_STATUS_OK)
146 		return (status);
147 
148 	status = dladm_get_conf_field(handle, conf, FSIMNETTYPE, &u64,
149 	    sizeof (u64));
150 	if (status != DLADM_STATUS_OK)
151 		goto done;
152 	attrp->sna_type = (uint_t)u64;
153 
154 	status = dladm_get_conf_field(handle, conf, FMADDRLEN, &u64,
155 	    sizeof (u64));
156 	if (status != DLADM_STATUS_OK)
157 		goto done;
158 	attrp->sna_mac_len = (uint_t)u64;
159 
160 	status = dladm_get_conf_field(handle, conf, FMACADDR, macstr,
161 	    sizeof (macstr));
162 	if (status != DLADM_STATUS_OK)
163 		goto done;
164 	(void) dladm_aggr_str2macaddr(macstr, &mac_fixed, attrp->sna_mac_addr);
165 
166 	/* Peer field is optional and only set when peer is attached */
167 	if (dladm_get_conf_field(handle, conf, FSIMNETPEER, simnetpeer,
168 	    sizeof (simnetpeer)) == DLADM_STATUS_OK) {
169 		status = dladm_name2info(handle, simnetpeer,
170 		    &attrp->sna_peer_link_id, NULL, NULL, NULL);
171 	} else {
172 		attrp->sna_peer_link_id = DATALINK_INVALID_LINKID;
173 	}
174 done:
175 	dladm_destroy_conf(handle, conf);
176 	return (status);
177 }
178 
179 dladm_status_t
dladm_simnet_create(dladm_handle_t handle,const char * simnetname,uint_t media,uint32_t flags)180 dladm_simnet_create(dladm_handle_t handle, const char *simnetname,
181     uint_t media, uint32_t flags)
182 {
183 	datalink_id_t simnet_id;
184 	dladm_status_t status;
185 	dladm_simnet_attr_t attr;
186 
187 	if (!(flags & DLADM_OPT_ACTIVE))
188 		return (DLADM_STATUS_NOTSUP);
189 
190 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
191 	if ((status = dladm_create_datalink_id(handle, simnetname,
192 	    DATALINK_CLASS_SIMNET, media, flags,
193 	    &simnet_id)) != DLADM_STATUS_OK)
194 		return (status);
195 
196 	bzero(&attr, sizeof (attr));
197 	attr.sna_link_id = simnet_id;
198 	attr.sna_type = media;
199 	status = i_dladm_create_simnet(handle, &attr);
200 	if (status != DLADM_STATUS_OK)
201 		goto done;
202 
203 	if (!(flags & DLADM_OPT_PERSIST))
204 		goto done;
205 
206 	status = dladm_simnet_persist_conf(handle, simnetname, &attr);
207 	if (status != DLADM_STATUS_OK) {
208 		(void) i_dladm_delete_simnet(handle, &attr);
209 		goto done;
210 	}
211 
212 	(void) dladm_set_linkprop(handle, simnet_id, NULL, NULL, 0, flags);
213 
214 done:
215 	if (status != DLADM_STATUS_OK) {
216 		(void) dladm_destroy_datalink_id(handle, simnet_id, flags);
217 	}
218 	return (status);
219 }
220 
221 /* Update existing simnet configuration */
222 static dladm_status_t
i_dladm_simnet_update_conf(dladm_handle_t handle,datalink_id_t simnet_id,datalink_id_t peer_simnet_id)223 i_dladm_simnet_update_conf(dladm_handle_t handle, datalink_id_t simnet_id,
224     datalink_id_t peer_simnet_id)
225 {
226 	dladm_status_t status;
227 	dladm_conf_t conf;
228 	char simnetpeer[MAXLINKNAMELEN];
229 
230 	status = dladm_open_conf(handle, simnet_id, &conf);
231 	if (status != DLADM_STATUS_OK)
232 		return (status);
233 
234 	/* First clear previous peer if any in configuration */
235 	(void) dladm_unset_conf_field(handle, conf, FSIMNETPEER);
236 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
237 		if ((status = dladm_datalink_id2info(handle,
238 		    peer_simnet_id, NULL, NULL, NULL, simnetpeer,
239 		    sizeof (simnetpeer))) == DLADM_STATUS_OK) {
240 			status = dladm_set_conf_field(handle, conf,
241 			    FSIMNETPEER, DLADM_TYPE_STR, simnetpeer);
242 		}
243 		if (status != DLADM_STATUS_OK)
244 			goto fail;
245 	}
246 
247 	status = dladm_write_conf(handle, conf);
248 fail:
249 	dladm_destroy_conf(handle, conf);
250 	return (status);
251 }
252 
253 /* Modify attached simnet peer */
254 dladm_status_t
dladm_simnet_modify(dladm_handle_t handle,datalink_id_t simnet_id,datalink_id_t peer_simnet_id,uint32_t flags)255 dladm_simnet_modify(dladm_handle_t handle, datalink_id_t simnet_id,
256     datalink_id_t peer_simnet_id, uint32_t flags)
257 {
258 	dladm_simnet_attr_t attr;
259 	dladm_simnet_attr_t prevattr;
260 	dladm_status_t status;
261 	datalink_class_t class;
262 	uint32_t linkflags;
263 	uint32_t peerlinkflags;
264 
265 	if (!(flags & DLADM_OPT_ACTIVE))
266 		return (DLADM_STATUS_NOTSUP);
267 
268 	if ((dladm_datalink_id2info(handle, simnet_id, &linkflags, &class,
269 	    NULL, NULL, 0) != DLADM_STATUS_OK))
270 		return (DLADM_STATUS_BADARG);
271 	if (class != DATALINK_CLASS_SIMNET)
272 		return (DLADM_STATUS_BADARG);
273 
274 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
275 		if (dladm_datalink_id2info(handle, peer_simnet_id,
276 		    &peerlinkflags, &class, NULL, NULL, 0) != DLADM_STATUS_OK)
277 			return (DLADM_STATUS_BADARG);
278 		if (class != DATALINK_CLASS_SIMNET)
279 			return (DLADM_STATUS_BADARG);
280 		/* Check to ensure the peer link has identical flags */
281 		if (peerlinkflags != linkflags)
282 			return (DLADM_STATUS_BADARG);
283 	}
284 
285 	/* Retrieve previous attrs before modification */
286 	bzero(&prevattr, sizeof (prevattr));
287 	if ((status = dladm_simnet_info(handle, simnet_id, &prevattr,
288 	    flags)) != DLADM_STATUS_OK)
289 		return (status);
290 
291 	bzero(&attr, sizeof (attr));
292 	attr.sna_link_id = simnet_id;
293 	attr.sna_peer_link_id = peer_simnet_id;
294 	status = i_dladm_modify_simnet(handle, &attr);
295 	if ((status != DLADM_STATUS_OK) || !(flags & DLADM_OPT_PERSIST))
296 		return (status);
297 
298 	/* First we clear link's existing peer field in config */
299 	status = i_dladm_simnet_update_conf(handle, simnet_id,
300 	    DATALINK_INVALID_LINKID);
301 	if (status != DLADM_STATUS_OK)
302 		return (status);
303 
304 	/* Clear the previous peer link's existing peer field in config */
305 	if (prevattr.sna_peer_link_id != DATALINK_INVALID_LINKID) {
306 		status = i_dladm_simnet_update_conf(handle,
307 		    prevattr.sna_peer_link_id, DATALINK_INVALID_LINKID);
308 		if (status != DLADM_STATUS_OK)
309 			return (status);
310 	}
311 
312 	/* Update the configuration in both simnets with any new peer link */
313 	if (peer_simnet_id != DATALINK_INVALID_LINKID) {
314 		status = i_dladm_simnet_update_conf(handle, simnet_id,
315 		    peer_simnet_id);
316 		if (status == DLADM_STATUS_OK)
317 			status = i_dladm_simnet_update_conf(handle,
318 			    peer_simnet_id, simnet_id);
319 	}
320 
321 	return (status);
322 }
323 
324 dladm_status_t
dladm_simnet_delete(dladm_handle_t handle,datalink_id_t simnet_id,uint32_t flags)325 dladm_simnet_delete(dladm_handle_t handle, datalink_id_t simnet_id,
326     uint32_t flags)
327 {
328 	dladm_simnet_attr_t attr;
329 	dladm_simnet_attr_t prevattr;
330 	dladm_status_t status;
331 	datalink_class_t class;
332 
333 	if ((dladm_datalink_id2info(handle, simnet_id, NULL, &class,
334 	    NULL, NULL, 0) != DLADM_STATUS_OK))
335 		return (DLADM_STATUS_BADARG);
336 
337 	if (class != DATALINK_CLASS_SIMNET)
338 		return (DLADM_STATUS_BADARG);
339 
340 	/* Check current simnet attributes before deletion */
341 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
342 	bzero(&prevattr, sizeof (prevattr));
343 	if ((status = dladm_simnet_info(handle, simnet_id, &prevattr,
344 	    flags)) != DLADM_STATUS_OK)
345 		return (status);
346 
347 	bzero(&attr, sizeof (attr));
348 	attr.sna_link_id = simnet_id;
349 	if (flags & DLADM_OPT_ACTIVE) {
350 		status = i_dladm_delete_simnet(handle, &attr);
351 		if (status == DLADM_STATUS_OK) {
352 			(void) dladm_set_linkprop(handle, simnet_id, NULL,
353 			    NULL, 0, DLADM_OPT_ACTIVE);
354 			(void) dladm_destroy_datalink_id(handle, simnet_id,
355 			    DLADM_OPT_ACTIVE);
356 		} else if (status != DLADM_STATUS_NOTFOUND) {
357 			return (status);
358 		}
359 	}
360 
361 	if (flags & DLADM_OPT_PERSIST) {
362 		(void) dladm_remove_conf(handle, simnet_id);
363 		(void) dladm_destroy_datalink_id(handle, simnet_id,
364 		    DLADM_OPT_PERSIST);
365 
366 		/* Update any attached peer configuration */
367 		if (prevattr.sna_peer_link_id != DATALINK_INVALID_LINKID)
368 			status = i_dladm_simnet_update_conf(handle,
369 			    prevattr.sna_peer_link_id, DATALINK_INVALID_LINKID);
370 	}
371 	return (status);
372 }
373 
374 /* Retrieve simnet information either active or from configuration */
375 dladm_status_t
dladm_simnet_info(dladm_handle_t handle,datalink_id_t simnet_id,dladm_simnet_attr_t * attrp,uint32_t flags)376 dladm_simnet_info(dladm_handle_t handle, datalink_id_t simnet_id,
377     dladm_simnet_attr_t *attrp, uint32_t flags)
378 {
379 	datalink_class_t class;
380 	dladm_status_t status;
381 
382 	if ((dladm_datalink_id2info(handle, simnet_id, NULL, &class,
383 	    NULL, NULL, 0) != DLADM_STATUS_OK))
384 		return (DLADM_STATUS_BADARG);
385 
386 	if (class != DATALINK_CLASS_SIMNET)
387 		return (DLADM_STATUS_BADARG);
388 
389 	bzero(attrp, sizeof (*attrp));
390 	attrp->sna_link_id = simnet_id;
391 
392 	if (flags & DLADM_OPT_ACTIVE) {
393 		status = i_dladm_get_simnet_info(handle, attrp);
394 		/*
395 		 * If no active simnet found then return any simnet
396 		 * from stored config if requested.
397 		 */
398 		if (status == DLADM_STATUS_NOTFOUND &&
399 		    (flags & DLADM_OPT_PERSIST))
400 			return (i_dladm_get_simnet_info_persist(handle, attrp));
401 		return (status);
402 	} else if (flags & DLADM_OPT_PERSIST) {
403 		return (i_dladm_get_simnet_info_persist(handle, attrp));
404 	} else {
405 		return (DLADM_STATUS_BADARG);
406 	}
407 }
408 
409 /* Bring up simnet from stored configuration */
410 static int
i_dladm_simnet_up(dladm_handle_t handle,datalink_id_t simnet_id,void * arg)411 i_dladm_simnet_up(dladm_handle_t handle, datalink_id_t simnet_id, void *arg)
412 {
413 	dladm_status_t *statusp = arg;
414 	dladm_status_t status;
415 	dladm_simnet_attr_t attr;
416 	dladm_simnet_attr_t peer_attr;
417 
418 	bzero(&attr, sizeof (attr));
419 	attr.sna_link_id = simnet_id;
420 	status = dladm_simnet_info(handle, simnet_id, &attr,
421 	    DLADM_OPT_PERSIST);
422 	if (status != DLADM_STATUS_OK)
423 		goto done;
424 
425 	status = i_dladm_create_simnet(handle, &attr);
426 	if (status != DLADM_STATUS_OK)
427 		goto done;
428 
429 	/*
430 	 * When bringing up check if the peer link is available, if it
431 	 * is then modify the simnet and attach the peer link.
432 	 */
433 	if ((attr.sna_peer_link_id != DATALINK_INVALID_LINKID) &&
434 	    (dladm_simnet_info(handle, attr.sna_peer_link_id, &peer_attr,
435 	    DLADM_OPT_ACTIVE) == DLADM_STATUS_OK)) {
436 		status = i_dladm_modify_simnet(handle, &attr);
437 		if (status != DLADM_STATUS_OK)
438 			goto done;
439 	}
440 
441 	if ((status = dladm_up_datalink_id(handle, simnet_id)) !=
442 	    DLADM_STATUS_OK) {
443 		(void) dladm_simnet_delete(handle, simnet_id,
444 		    DLADM_OPT_PERSIST);
445 		goto done;
446 	}
447 done:
448 	*statusp = status;
449 	return (DLADM_WALK_CONTINUE);
450 }
451 
452 /* Bring up simnet instance(s) from configuration */
453 dladm_status_t
dladm_simnet_up(dladm_handle_t handle,datalink_id_t simnet_id,uint32_t flags __unused)454 dladm_simnet_up(dladm_handle_t handle, datalink_id_t simnet_id,
455     uint32_t flags __unused)
456 {
457 	dladm_status_t status;
458 
459 	if (simnet_id == DATALINK_ALL_LINKID) {
460 		(void) dladm_walk_datalink_id(i_dladm_simnet_up, handle,
461 		    &status, DATALINK_CLASS_SIMNET, DATALINK_ANY_MEDIATYPE,
462 		    DLADM_OPT_PERSIST);
463 		return (DLADM_STATUS_OK);
464 	} else {
465 		(void) i_dladm_simnet_up(handle, simnet_id, &status);
466 		return (status);
467 	}
468 }
469 
470 /* Store simnet configuration */
471 static dladm_status_t
dladm_simnet_persist_conf(dladm_handle_t handle,const char * name,dladm_simnet_attr_t * attrp)472 dladm_simnet_persist_conf(dladm_handle_t handle, const char *name,
473     dladm_simnet_attr_t *attrp)
474 {
475 	dladm_conf_t conf;
476 	dladm_status_t status;
477 	char mstr[ETHERADDRL * 3];
478 	uint64_t u64;
479 
480 	if ((status = dladm_create_conf(handle, name, attrp->sna_link_id,
481 	    DATALINK_CLASS_SIMNET, attrp->sna_type, &conf)) != DLADM_STATUS_OK)
482 		return (status);
483 
484 	status = dladm_set_conf_field(handle, conf, FMACADDR,
485 	    DLADM_TYPE_STR, dladm_aggr_macaddr2str(attrp->sna_mac_addr, mstr));
486 	if (status != DLADM_STATUS_OK)
487 		goto done;
488 
489 	u64 = attrp->sna_type;
490 	status = dladm_set_conf_field(handle, conf, FSIMNETTYPE,
491 	    DLADM_TYPE_UINT64, &u64);
492 	if (status != DLADM_STATUS_OK)
493 		goto done;
494 
495 	u64 = attrp->sna_mac_len;
496 	status = dladm_set_conf_field(handle, conf, FMADDRLEN,
497 	    DLADM_TYPE_UINT64, &u64);
498 	if (status != DLADM_STATUS_OK)
499 		goto done;
500 
501 	status = dladm_write_conf(handle, conf);
502 done:
503 	dladm_destroy_conf(handle, conf);
504 	return (status);
505 }
506