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 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <errno.h> 33 #include <assert.h> 34 #include <sys/dld.h> 35 #include <libdladm_impl.h> 36 #include <libdllink.h> 37 #include <libdlvlan.h> 38 39 /* 40 * VLAN Administration Library. 41 * 42 * This library is used by administration tools such as dladm(1M) to 43 * configure VLANs. 44 */ 45 46 /* 47 * Returns the current attributes of the specified VLAN. 48 */ 49 static dladm_status_t 50 i_dladm_vlan_info_active(datalink_id_t vlanid, dladm_vlan_attr_t *dvap) 51 { 52 int fd; 53 dld_ioc_vlan_attr_t div; 54 dladm_status_t status = DLADM_STATUS_OK; 55 56 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 57 return (dladm_errno2status(errno)); 58 59 div.div_vlanid = vlanid; 60 61 if (i_dladm_ioctl(fd, DLDIOC_VLAN_ATTR, &div, sizeof (div)) < 0) 62 status = dladm_errno2status(errno); 63 64 dvap->dv_vid = div.div_vid; 65 dvap->dv_linkid = div.div_linkid; 66 dvap->dv_force = div.div_force; 67 dvap->dv_implicit = div.div_implicit; 68 done: 69 (void) close(fd); 70 return (status); 71 } 72 73 /* 74 * Returns the persistent attributes of the specified VLAN. 75 */ 76 static dladm_status_t 77 i_dladm_vlan_info_persist(datalink_id_t vlanid, dladm_vlan_attr_t *dvap) 78 { 79 dladm_conf_t conf = DLADM_INVALID_CONF; 80 dladm_status_t status; 81 uint64_t u64; 82 83 if ((status = dladm_read_conf(vlanid, &conf)) != DLADM_STATUS_OK) 84 return (status); 85 86 status = dladm_get_conf_field(conf, FLINKOVER, &u64, sizeof (u64)); 87 if (status != DLADM_STATUS_OK) 88 goto done; 89 dvap->dv_linkid = (datalink_id_t)u64; 90 91 status = dladm_get_conf_field(conf, FFORCE, &dvap->dv_force, 92 sizeof (boolean_t)); 93 if (status != DLADM_STATUS_OK) 94 goto done; 95 96 dvap->dv_implicit = B_FALSE; 97 98 status = dladm_get_conf_field(conf, FVLANID, &u64, sizeof (u64)); 99 if (status != DLADM_STATUS_OK) 100 goto done; 101 dvap->dv_vid = (uint16_t)u64; 102 103 done: 104 dladm_destroy_conf(conf); 105 return (status); 106 } 107 108 dladm_status_t 109 dladm_vlan_info(datalink_id_t vlanid, dladm_vlan_attr_t *dvap, uint32_t flags) 110 { 111 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 112 if (flags == DLADM_OPT_ACTIVE) 113 return (i_dladm_vlan_info_active(vlanid, dvap)); 114 else 115 return (i_dladm_vlan_info_persist(vlanid, dvap)); 116 } 117 118 static dladm_status_t 119 dladm_persist_vlan_conf(const char *vlan, datalink_id_t vlanid, 120 boolean_t force, datalink_id_t linkid, uint16_t vid) 121 { 122 dladm_conf_t conf = DLADM_INVALID_CONF; 123 dladm_status_t status; 124 uint64_t u64; 125 126 if ((status = dladm_create_conf(vlan, vlanid, DATALINK_CLASS_VLAN, 127 DL_ETHER, &conf)) != DLADM_STATUS_OK) { 128 return (status); 129 } 130 131 u64 = linkid; 132 status = dladm_set_conf_field(conf, FLINKOVER, DLADM_TYPE_UINT64, &u64); 133 if (status != DLADM_STATUS_OK) 134 goto done; 135 136 status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force); 137 if (status != DLADM_STATUS_OK) 138 goto done; 139 140 u64 = vid; 141 status = dladm_set_conf_field(conf, FVLANID, DLADM_TYPE_UINT64, &u64); 142 if (status != DLADM_STATUS_OK) 143 goto done; 144 145 status = dladm_write_conf(conf); 146 147 done: 148 dladm_destroy_conf(conf); 149 return (status); 150 } 151 152 /* 153 * Create a VLAN on given link. 154 */ 155 dladm_status_t 156 dladm_vlan_create(const char *vlan, datalink_id_t linkid, uint16_t vid, 157 uint32_t flags) 158 { 159 dld_ioc_create_vlan_t dic; 160 int fd; 161 datalink_id_t vlanid = DATALINK_INVALID_LINKID; 162 uint_t media; 163 datalink_class_t class; 164 dladm_status_t status; 165 166 if (vid < 1 || vid > 4094) 167 return (DLADM_STATUS_VIDINVAL); 168 169 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 170 return (dladm_errno2status(errno)); 171 172 status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0); 173 if (status != DLADM_STATUS_OK || media != DL_ETHER || 174 class == DATALINK_CLASS_VLAN) { 175 return (DLADM_STATUS_BADARG); 176 } 177 178 status = dladm_create_datalink_id(vlan, DATALINK_CLASS_VLAN, DL_ETHER, 179 flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST), &vlanid); 180 if (status != DLADM_STATUS_OK) 181 goto fail; 182 183 if (flags & DLADM_OPT_PERSIST) { 184 status = dladm_persist_vlan_conf(vlan, vlanid, 185 (flags & DLADM_OPT_FORCE) != 0, linkid, vid); 186 if (status != DLADM_STATUS_OK) 187 goto fail; 188 } 189 190 if (flags & DLADM_OPT_ACTIVE) { 191 dic.dic_vlanid = vlanid; 192 dic.dic_linkid = linkid; 193 dic.dic_vid = vid; 194 dic.dic_force = (flags & DLADM_OPT_FORCE) != 0; 195 196 if (i_dladm_ioctl(fd, DLDIOC_CREATE_VLAN, &dic, 197 sizeof (dic)) < 0) { 198 status = dladm_errno2status(errno); 199 if (flags & DLADM_OPT_PERSIST) 200 (void) dladm_remove_conf(vlanid); 201 goto fail; 202 } 203 } 204 205 (void) close(fd); 206 return (DLADM_STATUS_OK); 207 208 fail: 209 if (vlanid != DATALINK_INVALID_LINKID) { 210 (void) dladm_destroy_datalink_id(vlanid, 211 flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST)); 212 } 213 (void) close(fd); 214 return (status); 215 } 216 217 /* 218 * Delete a given VLAN. 219 */ 220 dladm_status_t 221 dladm_vlan_delete(datalink_id_t vlanid, uint32_t flags) 222 { 223 dld_ioc_delete_vlan_t did; 224 int fd; 225 datalink_class_t class; 226 dladm_status_t status = DLADM_STATUS_OK; 227 228 if ((dladm_datalink_id2info(vlanid, NULL, &class, NULL, NULL, 0) != 229 DLADM_STATUS_OK) || (class != DATALINK_CLASS_VLAN)) { 230 return (DLADM_STATUS_BADARG); 231 } 232 233 if (flags & DLADM_OPT_ACTIVE) { 234 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 235 return (dladm_errno2status(errno)); 236 237 did.did_linkid = vlanid; 238 if ((i_dladm_ioctl(fd, DLDIOC_DELETE_VLAN, &did, 239 sizeof (did)) < 0) && 240 ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) { 241 (void) close(fd); 242 return (dladm_errno2status(errno)); 243 } 244 (void) close(fd); 245 246 /* 247 * Delete active linkprop before this active link is deleted. 248 */ 249 (void) dladm_set_linkprop(vlanid, NULL, NULL, 0, 250 DLADM_OPT_ACTIVE); 251 } 252 253 (void) dladm_destroy_datalink_id(vlanid, 254 flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST)); 255 256 if (flags & DLADM_OPT_PERSIST) 257 (void) dladm_remove_conf(vlanid); 258 259 return (status); 260 } 261 262 /* 263 * Callback used by dladm_vlan_up() 264 */ 265 static int 266 i_dladm_vlan_up(datalink_id_t vlanid, void *arg) 267 { 268 dladm_vlan_attr_t dva; 269 dld_ioc_create_vlan_t dic; 270 dladm_status_t *statusp = arg; 271 uint32_t flags; 272 int fd; 273 dladm_status_t status; 274 275 status = dladm_vlan_info(vlanid, &dva, DLADM_OPT_PERSIST); 276 if (status != DLADM_STATUS_OK) 277 goto done; 278 279 /* 280 * Validate (and delete) the link associated with this VLAN, see if 281 * the specific hardware has been removed during system shutdown. 282 */ 283 if ((status = dladm_datalink_id2info(dva.dv_linkid, &flags, NULL, 284 NULL, NULL, 0)) != DLADM_STATUS_OK) { 285 goto done; 286 } 287 288 if (!(flags & DLADM_OPT_ACTIVE)) { 289 status = DLADM_STATUS_BADARG; 290 goto done; 291 } 292 293 dic.dic_linkid = dva.dv_linkid; 294 dic.dic_force = dva.dv_force; 295 dic.dic_vid = dva.dv_vid; 296 297 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 298 status = dladm_errno2status(errno); 299 goto done; 300 } 301 302 dic.dic_vlanid = vlanid; 303 if (i_dladm_ioctl(fd, DLDIOC_CREATE_VLAN, &dic, sizeof (dic)) < 0) { 304 status = dladm_errno2status(errno); 305 goto done; 306 } 307 308 if ((status = dladm_up_datalink_id(vlanid)) != DLADM_STATUS_OK) { 309 dld_ioc_delete_vlan_t did; 310 311 did.did_linkid = vlanid; 312 (void) i_dladm_ioctl(fd, DLDIOC_DELETE_VLAN, &did, 313 sizeof (did)); 314 } else { 315 /* 316 * Reset the active linkprop of this specific link. 317 */ 318 (void) dladm_init_linkprop(vlanid, B_FALSE); 319 } 320 321 (void) close(fd); 322 done: 323 *statusp = status; 324 return (DLADM_WALK_CONTINUE); 325 } 326 327 /* 328 * Bring up one VLAN, or all persistent VLANs. In the latter case, the 329 * walk may terminate early if bringup of a VLAN fails. 330 */ 331 dladm_status_t 332 dladm_vlan_up(datalink_id_t linkid) 333 { 334 dladm_status_t status; 335 336 if (linkid == DATALINK_ALL_LINKID) { 337 (void) dladm_walk_datalink_id(i_dladm_vlan_up, &status, 338 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 339 DLADM_OPT_PERSIST); 340 return (DLADM_STATUS_OK); 341 } else { 342 (void) i_dladm_vlan_up(linkid, &status); 343 return (status); 344 } 345 } 346