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
40static dladm_status_t dladm_simnet_persist_conf(dladm_handle_t, const char *,
41    dladm_simnet_attr_t *);
42
43/* New simnet instance creation */
44static dladm_status_t
45i_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 */
72static dladm_status_t
73i_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 */
91static dladm_status_t
92i_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 */
109static dladm_status_t
110i_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 */
133static dladm_status_t
134i_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	}
174done:
175	dladm_destroy_conf(handle, conf);
176	return (status);
177}
178
179dladm_status_t
180dladm_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
214done:
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 */
222static dladm_status_t
223i_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);
248fail:
249	dladm_destroy_conf(handle, conf);
250	return (status);
251}
252
253/* Modify attached simnet peer */
254dladm_status_t
255dladm_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
324dladm_status_t
325dladm_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 */
375dladm_status_t
376dladm_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 */
410static int
411i_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	}
447done:
448	*statusp = status;
449	return (DLADM_WALK_CONTINUE);
450}
451
452/* Bring up simnet instance(s) from configuration */
453/* ARGSUSED */
454dladm_status_t
455dladm_simnet_up(dladm_handle_t handle, datalink_id_t simnet_id,
456    uint32_t flags)
457{
458	dladm_status_t status;
459
460	if (simnet_id == DATALINK_ALL_LINKID) {
461		(void) dladm_walk_datalink_id(i_dladm_simnet_up, handle,
462		    &status, DATALINK_CLASS_SIMNET, DATALINK_ANY_MEDIATYPE,
463		    DLADM_OPT_PERSIST);
464		return (DLADM_STATUS_OK);
465	} else {
466		(void) i_dladm_simnet_up(handle, simnet_id, &status);
467		return (status);
468	}
469}
470
471/* Store simnet configuration */
472static dladm_status_t
473dladm_simnet_persist_conf(dladm_handle_t handle, const char *name,
474    dladm_simnet_attr_t *attrp)
475{
476	dladm_conf_t conf;
477	dladm_status_t status;
478	char mstr[ETHERADDRL * 3];
479	uint64_t u64;
480
481	if ((status = dladm_create_conf(handle, name, attrp->sna_link_id,
482	    DATALINK_CLASS_SIMNET, attrp->sna_type, &conf)) != DLADM_STATUS_OK)
483		return (status);
484
485	status = dladm_set_conf_field(handle, conf, FMACADDR,
486	    DLADM_TYPE_STR, dladm_aggr_macaddr2str(attrp->sna_mac_addr, mstr));
487	if (status != DLADM_STATUS_OK)
488		goto done;
489
490	u64 = attrp->sna_type;
491	status = dladm_set_conf_field(handle, conf, FSIMNETTYPE,
492	    DLADM_TYPE_UINT64, &u64);
493	if (status != DLADM_STATUS_OK)
494		goto done;
495
496	u64 = attrp->sna_mac_len;
497	status = dladm_set_conf_field(handle, conf, FMADDRLEN,
498	    DLADM_TYPE_UINT64, &u64);
499	if (status != DLADM_STATUS_OK)
500		goto done;
501
502	status = dladm_write_conf(handle, conf);
503done:
504	dladm_destroy_conf(handle, conf);
505	return (status);
506}
507