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