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 /* ARGSUSED */
454 dladm_status_t
dladm_simnet_up(dladm_handle_t handle,datalink_id_t simnet_id,uint32_t flags)455 dladm_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 */
472 static dladm_status_t
dladm_simnet_persist_conf(dladm_handle_t handle,const char * name,dladm_simnet_attr_t * attrp)473 dladm_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);
503 done:
504 dladm_destroy_conf(handle, conf);
505 return (status);
506 }
507