|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2010 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: #include <stdint.h> ! 22: #include <string.h> ! 23: #include <stdio.h> ! 24: #include <errno.h> ! 25: #include <byteswap.h> ! 26: #include <ipxe/features.h> ! 27: #include <ipxe/if_ether.h> ! 28: #include <ipxe/ethernet.h> ! 29: #include <ipxe/netdevice.h> ! 30: #include <ipxe/iobuf.h> ! 31: #include <ipxe/vlan.h> ! 32: ! 33: /** @file ! 34: * ! 35: * Virtual LANs ! 36: * ! 37: */ ! 38: ! 39: FEATURE ( FEATURE_PROTOCOL, "VLAN", DHCP_EB_FEATURE_VLAN, 1 ); ! 40: ! 41: struct net_protocol vlan_protocol __net_protocol; ! 42: ! 43: /** VLAN device private data */ ! 44: struct vlan_device { ! 45: /** Trunk network device */ ! 46: struct net_device *trunk; ! 47: /** VLAN tag */ ! 48: unsigned int tag; ! 49: /** Default priority */ ! 50: unsigned int priority; ! 51: }; ! 52: ! 53: /** ! 54: * Open VLAN device ! 55: * ! 56: * @v netdev Network device ! 57: * @ret rc Return status code ! 58: */ ! 59: static int vlan_open ( struct net_device *netdev ) { ! 60: struct vlan_device *vlan = netdev->priv; ! 61: ! 62: return netdev_open ( vlan->trunk ); ! 63: } ! 64: ! 65: /** ! 66: * Close VLAN device ! 67: * ! 68: * @v netdev Network device ! 69: */ ! 70: static void vlan_close ( struct net_device *netdev ) { ! 71: struct vlan_device *vlan = netdev->priv; ! 72: ! 73: netdev_close ( vlan->trunk ); ! 74: } ! 75: ! 76: /** ! 77: * Transmit packet on VLAN device ! 78: * ! 79: * @v netdev Network device ! 80: * @v iobuf I/O buffer ! 81: * @ret rc Return status code ! 82: */ ! 83: static int vlan_transmit ( struct net_device *netdev, ! 84: struct io_buffer *iobuf ) { ! 85: struct vlan_device *vlan = netdev->priv; ! 86: struct net_device *trunk = vlan->trunk; ! 87: struct ll_protocol *ll_protocol; ! 88: struct vlan_header *vlanhdr; ! 89: uint8_t ll_dest_copy[ETH_ALEN]; ! 90: uint8_t ll_source_copy[ETH_ALEN]; ! 91: const void *ll_dest; ! 92: const void *ll_source; ! 93: uint16_t net_proto; ! 94: int rc; ! 95: ! 96: /* Strip link-layer header and preserve link-layer header fields */ ! 97: ll_protocol = netdev->ll_protocol; ! 98: if ( ( rc = ll_protocol->pull ( netdev, iobuf, &ll_dest, &ll_source, ! 99: &net_proto ) ) != 0 ) { ! 100: DBGC ( netdev, "VLAN %s could not parse link-layer header: " ! 101: "%s\n", netdev->name, strerror ( rc ) ); ! 102: return rc; ! 103: } ! 104: memcpy ( ll_dest_copy, ll_dest, ETH_ALEN ); ! 105: memcpy ( ll_source_copy, ll_source, ETH_ALEN ); ! 106: ! 107: /* Construct VLAN header */ ! 108: vlanhdr = iob_push ( iobuf, sizeof ( *vlanhdr ) ); ! 109: vlanhdr->tci = htons ( VLAN_TCI ( vlan->tag, vlan->priority ) ); ! 110: vlanhdr->net_proto = net_proto; ! 111: ! 112: /* Reclaim I/O buffer from VLAN device's TX queue */ ! 113: list_del ( &iobuf->list ); ! 114: ! 115: /* Transmit packet on trunk device */ ! 116: if ( ( rc = net_tx ( iob_disown ( iobuf ), trunk, &vlan_protocol, ! 117: ll_dest_copy, ll_source_copy ) ) != 0 ) { ! 118: DBGC ( netdev, "VLAN %s could not transmit: %s\n", ! 119: netdev->name, strerror ( rc ) ); ! 120: /* Cannot return an error status, since that would ! 121: * cause the I/O buffer to be double-freed. ! 122: */ ! 123: return 0; ! 124: } ! 125: ! 126: return 0; ! 127: } ! 128: ! 129: /** ! 130: * Poll VLAN device ! 131: * ! 132: * @v netdev Network device ! 133: */ ! 134: static void vlan_poll ( struct net_device *netdev ) { ! 135: struct vlan_device *vlan = netdev->priv; ! 136: ! 137: /* Poll trunk device */ ! 138: netdev_poll ( vlan->trunk ); ! 139: } ! 140: ! 141: /** ! 142: * Enable/disable interrupts on VLAN device ! 143: * ! 144: * @v netdev Network device ! 145: * @v enable Interrupts should be enabled ! 146: */ ! 147: static void vlan_irq ( struct net_device *netdev, int enable ) { ! 148: struct vlan_device *vlan = netdev->priv; ! 149: ! 150: /* Enable/disable interrupts on trunk device. This is not at ! 151: * all robust, but there is no sensible course of action ! 152: * available. ! 153: */ ! 154: netdev_irq ( vlan->trunk, enable ); ! 155: } ! 156: ! 157: /** VLAN device operations */ ! 158: static struct net_device_operations vlan_operations = { ! 159: .open = vlan_open, ! 160: .close = vlan_close, ! 161: .transmit = vlan_transmit, ! 162: .poll = vlan_poll, ! 163: .irq = vlan_irq, ! 164: }; ! 165: ! 166: /** ! 167: * Synchronise VLAN device ! 168: * ! 169: * @v netdev Network device ! 170: */ ! 171: static void vlan_sync ( struct net_device *netdev ) { ! 172: struct vlan_device *vlan = netdev->priv; ! 173: struct net_device *trunk = vlan->trunk; ! 174: ! 175: /* Synchronise link status */ ! 176: if ( netdev->link_rc != trunk->link_rc ) ! 177: netdev_link_err ( netdev, trunk->link_rc ); ! 178: ! 179: /* Synchronise open/closed status */ ! 180: if ( netdev_is_open ( trunk ) ) { ! 181: if ( ! netdev_is_open ( netdev ) ) ! 182: netdev_open ( netdev ); ! 183: } else { ! 184: if ( netdev_is_open ( netdev ) ) ! 185: netdev_close ( netdev ); ! 186: } ! 187: } ! 188: ! 189: /** ! 190: * Identify VLAN device ! 191: * ! 192: * @v trunk Trunk network device ! 193: * @v tag VLAN tag ! 194: * @ret netdev VLAN device, if any ! 195: */ ! 196: struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) { ! 197: struct net_device *netdev; ! 198: struct vlan_device *vlan; ! 199: ! 200: for_each_netdev ( netdev ) { ! 201: if ( netdev->op != &vlan_operations ) ! 202: continue; ! 203: vlan = netdev->priv; ! 204: if ( ( vlan->trunk == trunk ) && ( vlan->tag == tag ) ) ! 205: return netdev; ! 206: } ! 207: return NULL; ! 208: } ! 209: ! 210: /** ! 211: * Process incoming VLAN packet ! 212: * ! 213: * @v iobuf I/O buffer ! 214: * @v trunk Trunk network device ! 215: * @v ll_dest Link-layer destination address ! 216: * @v ll_source Link-layer source address ! 217: * @ret rc Return status code ! 218: */ ! 219: static int vlan_rx ( struct io_buffer *iobuf, struct net_device *trunk, ! 220: const void *ll_dest, const void *ll_source ) { ! 221: struct vlan_header *vlanhdr = iobuf->data; ! 222: struct net_device *netdev; ! 223: struct ll_protocol *ll_protocol; ! 224: uint8_t ll_dest_copy[ETH_ALEN]; ! 225: uint8_t ll_source_copy[ETH_ALEN]; ! 226: uint16_t tag; ! 227: int rc; ! 228: ! 229: /* Sanity check */ ! 230: if ( iob_len ( iobuf ) < sizeof ( *vlanhdr ) ) { ! 231: DBGC ( trunk, "VLAN %s received underlength packet (%zd " ! 232: "bytes)\n", trunk->name, iob_len ( iobuf ) ); ! 233: rc = -EINVAL; ! 234: goto err_sanity; ! 235: } ! 236: ! 237: /* Identify VLAN device */ ! 238: tag = VLAN_TAG ( ntohs ( vlanhdr->tci ) ); ! 239: netdev = vlan_find ( trunk, tag ); ! 240: if ( ! netdev ) { ! 241: DBGC2 ( trunk, "VLAN %s received packet for unknown VLAN " ! 242: "%d\n", trunk->name, tag ); ! 243: rc = -EPIPE; ! 244: goto err_no_vlan; ! 245: } ! 246: ! 247: /* Strip VLAN header and preserve original link-layer header fields */ ! 248: iob_pull ( iobuf, sizeof ( *vlanhdr ) ); ! 249: ll_protocol = trunk->ll_protocol; ! 250: memcpy ( ll_dest_copy, ll_dest, ETH_ALEN ); ! 251: memcpy ( ll_source_copy, ll_source, ETH_ALEN ); ! 252: ! 253: /* Reconstruct link-layer header for VLAN device */ ! 254: ll_protocol = netdev->ll_protocol; ! 255: if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest_copy, ! 256: ll_source_copy, ! 257: vlanhdr->net_proto ) ) != 0 ) { ! 258: DBGC ( netdev, "VLAN %s could not reconstruct link-layer " ! 259: "header: %s\n", netdev->name, strerror ( rc ) ); ! 260: goto err_ll_push; ! 261: } ! 262: ! 263: /* Enqueue packet on VLAN device */ ! 264: netdev_rx ( netdev, iob_disown ( iobuf ) ); ! 265: return 0; ! 266: ! 267: err_ll_push: ! 268: err_no_vlan: ! 269: err_sanity: ! 270: free_iob ( iobuf ); ! 271: return rc; ! 272: } ! 273: ! 274: /** VLAN protocol */ ! 275: struct net_protocol vlan_protocol __net_protocol = { ! 276: .name = "VLAN", ! 277: .net_proto = htons ( ETH_P_8021Q ), ! 278: .rx = vlan_rx, ! 279: }; ! 280: ! 281: /** ! 282: * Check if network device can be used as a VLAN trunk device ! 283: * ! 284: * @v trunk Trunk network device ! 285: * @ret is_ok Trunk network device is usable ! 286: * ! 287: * VLAN devices will be created as Ethernet devices. (We cannot ! 288: * simply clone the link layer of the trunk network device, because ! 289: * this link layer may expect the network device structure to contain ! 290: * some link-layer-private data.) The trunk network device must ! 291: * therefore have a link layer that is in some sense 'compatible' with ! 292: * Ethernet; specifically, it must have link-layer addresses that are ! 293: * the same length as Ethernet link-layer addresses. ! 294: * ! 295: * As an additional check, and primarily to assist with the sanity of ! 296: * the FCoE code, we refuse to allow nested VLANs. ! 297: */ ! 298: int vlan_can_be_trunk ( struct net_device *trunk ) { ! 299: ! 300: return ( ( trunk->ll_protocol->ll_addr_len == ETH_ALEN ) && ! 301: ( trunk->op != &vlan_operations ) ); ! 302: } ! 303: ! 304: /** ! 305: * Create VLAN device ! 306: * ! 307: * @v trunk Trunk network device ! 308: * @v tag VLAN tag ! 309: * @v priority Default VLAN priority ! 310: * @ret rc Return status code ! 311: */ ! 312: int vlan_create ( struct net_device *trunk, unsigned int tag, ! 313: unsigned int priority ) { ! 314: struct net_device *netdev; ! 315: struct vlan_device *vlan; ! 316: int rc; ! 317: ! 318: /* If VLAN already exists, just update the priority */ ! 319: if ( ( netdev = vlan_find ( trunk, tag ) ) != NULL ) { ! 320: vlan = netdev->priv; ! 321: if ( priority != vlan->priority ) { ! 322: DBGC ( netdev, "VLAN %s priority changed from %d to " ! 323: "%d\n", netdev->name, vlan->priority, priority ); ! 324: } ! 325: vlan->priority = priority; ! 326: return 0; ! 327: } ! 328: ! 329: /* Sanity checks */ ! 330: if ( ! vlan_can_be_trunk ( trunk ) ) { ! 331: DBGC ( trunk, "VLAN %s cannot create VLAN on non-trunk " ! 332: "device\n", trunk->name ); ! 333: rc = -ENOTTY; ! 334: goto err_sanity; ! 335: } ! 336: if ( ! VLAN_TAG_IS_VALID ( tag ) ) { ! 337: DBGC ( trunk, "VLAN %s cannot create VLAN with invalid tag " ! 338: "%d\n", trunk->name, tag ); ! 339: rc = -EINVAL; ! 340: goto err_sanity; ! 341: } ! 342: if ( ! VLAN_PRIORITY_IS_VALID ( priority ) ) { ! 343: DBGC ( trunk, "VLAN %s cannot create VLAN with invalid " ! 344: "priority %d\n", trunk->name, priority ); ! 345: rc = -EINVAL; ! 346: goto err_sanity; ! 347: } ! 348: ! 349: /* Allocate and initialise structure */ ! 350: netdev = alloc_etherdev ( sizeof ( *vlan ) ); ! 351: if ( ! netdev ) { ! 352: rc = -ENOMEM; ! 353: goto err_alloc_etherdev; ! 354: } ! 355: netdev_init ( netdev, &vlan_operations ); ! 356: netdev->dev = trunk->dev; ! 357: memcpy ( netdev->hw_addr, trunk->ll_addr, ETH_ALEN ); ! 358: vlan = netdev->priv; ! 359: vlan->trunk = netdev_get ( trunk ); ! 360: vlan->tag = tag; ! 361: vlan->priority = priority; ! 362: ! 363: /* Construct VLAN device name */ ! 364: snprintf ( netdev->name, sizeof ( netdev->name ), "%s-%d", ! 365: trunk->name, vlan->tag ); ! 366: ! 367: /* Register VLAN device */ ! 368: if ( ( rc = register_netdev ( netdev ) ) != 0 ) { ! 369: DBGC ( netdev, "VLAN %s could not register: %s\n", ! 370: netdev->name, strerror ( rc ) ); ! 371: goto err_register; ! 372: } ! 373: ! 374: /* Synchronise with trunk device */ ! 375: vlan_sync ( netdev ); ! 376: ! 377: DBGC ( netdev, "VLAN %s created with tag %d and priority %d\n", ! 378: netdev->name, vlan->tag, vlan->priority ); ! 379: ! 380: return 0; ! 381: ! 382: unregister_netdev ( netdev ); ! 383: err_register: ! 384: netdev_nullify ( netdev ); ! 385: netdev_put ( netdev ); ! 386: netdev_put ( trunk ); ! 387: err_alloc_etherdev: ! 388: err_sanity: ! 389: return rc; ! 390: } ! 391: ! 392: /** ! 393: * Destroy VLAN device ! 394: * ! 395: * @v netdev Network device ! 396: * @ret rc Return status code ! 397: */ ! 398: int vlan_destroy ( struct net_device *netdev ) { ! 399: struct vlan_device *vlan = netdev->priv; ! 400: struct net_device *trunk; ! 401: ! 402: /* Sanity check */ ! 403: if ( netdev->op != &vlan_operations ) { ! 404: DBGC ( netdev, "VLAN %s cannot destroy non-VLAN device\n", ! 405: netdev->name ); ! 406: return -ENOTTY; ! 407: } ! 408: ! 409: DBGC ( netdev, "VLAN %s destroyed\n", netdev->name ); ! 410: ! 411: /* Remove VLAN device */ ! 412: unregister_netdev ( netdev ); ! 413: trunk = vlan->trunk; ! 414: netdev_nullify ( netdev ); ! 415: netdev_put ( netdev ); ! 416: netdev_put ( trunk ); ! 417: ! 418: return 0; ! 419: } ! 420: ! 421: /** ! 422: * Do nothing ! 423: * ! 424: * @v trunk Trunk network device ! 425: * @ret rc Return status code ! 426: */ ! 427: static int vlan_probe ( struct net_device *trunk __unused ) { ! 428: return 0; ! 429: } ! 430: ! 431: /** ! 432: * Handle trunk network device link state change ! 433: * ! 434: * @v trunk Trunk network device ! 435: */ ! 436: static void vlan_notify ( struct net_device *trunk ) { ! 437: struct net_device *netdev; ! 438: struct vlan_device *vlan; ! 439: ! 440: for_each_netdev ( netdev ) { ! 441: if ( netdev->op != &vlan_operations ) ! 442: continue; ! 443: vlan = netdev->priv; ! 444: if ( vlan->trunk == trunk ) ! 445: vlan_sync ( netdev ); ! 446: } ! 447: } ! 448: ! 449: /** ! 450: * Destroy first VLAN device for a given trunk ! 451: * ! 452: * @v trunk Trunk network device ! 453: * @ret found A VLAN device was found ! 454: */ ! 455: static int vlan_remove_first ( struct net_device *trunk ) { ! 456: struct net_device *netdev; ! 457: struct vlan_device *vlan; ! 458: ! 459: for_each_netdev ( netdev ) { ! 460: if ( netdev->op != &vlan_operations ) ! 461: continue; ! 462: vlan = netdev->priv; ! 463: if ( vlan->trunk == trunk ) { ! 464: vlan_destroy ( netdev ); ! 465: return 1; ! 466: } ! 467: } ! 468: return 0; ! 469: } ! 470: ! 471: /** ! 472: * Destroy all VLAN devices for a given trunk ! 473: * ! 474: * @v trunk Trunk network device ! 475: */ ! 476: static void vlan_remove ( struct net_device *trunk ) { ! 477: ! 478: /* Remove all VLAN devices attached to this trunk, safe ! 479: * against arbitrary net device removal. ! 480: */ ! 481: while ( vlan_remove_first ( trunk ) ) {} ! 482: } ! 483: ! 484: /** VLAN driver */ ! 485: struct net_driver vlan_driver __net_driver = { ! 486: .name = "VLAN", ! 487: .probe = vlan_probe, ! 488: .notify = vlan_notify, ! 489: .remove = vlan_remove, ! 490: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.