Hi, I've reworked the patch to be fully optional and less invasive.
The receiving path is from the raw device via 802.1Q network protocol into net_rx. The sending path is via linklayer of the proxy device which adds 802.1Q header and the forwards the iobuf to the link layer of the raw device. The transmit as well as all other commands to the proxy device (except pull) are forwarded to the raw device. Further, there is a vconfig command to add/remove vlan interfaces just like linux does. In order to have network device names like net0.VID, I increased the len of the netdevice name to 10 characters and let the driver decide upon the name. In order to achieve autobooting from the vlan device without trying to boot from the raw device first, I optionally added a netboot command. The code uses netdev_put and netdev_get commands to lock the raw device. The most tricky part is the irq enable/disable code as well as the open/close code, the patch just forwards these commands which just works though there should be open/close counters. Sincerely, M. Braun
diff --git a/src/config/config.c b/src/config/config.c
index a6e7622..497cd49 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -92,6 +92,14 @@ REQUIRE_OBJECT ( ipv4 );
#endif
/*
+ * Drag in 802.1Q support
+ */
+#ifdef NET_8021Q
+REQUIRE_OBJECT(vconfig_cmd);
+REQUIRE_OBJECT(ieee8021q);
+#endif
+
+/*
* Drag in all requested PXE support
*
*/
@@ -234,7 +242,10 @@ REQUIRE_OBJECT ( digest_cmd );
#ifdef PXE_CMD
REQUIRE_OBJECT ( pxe_cmd );
#endif
-
+#ifdef NETBOOT_CMD
+REQUIRE_OBJECT ( netboot_cmd );
+#endif
+/* VCONFIG_CMD is brought by 8021q if requested */
/*
* Drag in miscellaneous objects
*
diff --git a/src/config/general.h b/src/config/general.h
index 0a9e625..f5a16dd 100644
--- a/src/config/general.h
+++ b/src/config/general.h
@@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
#define NET_PROTO_IPV4 /* IPv4 protocol */
+//#undef NET_8021Q /* 802.1Q protocol */
/*
* PXE support
@@ -120,6 +121,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#undef TIME_CMD /* Time commands */
#undef DIGEST_CMD /* Image crypto digest commands */
//#undef PXE_CMD /* PXE commands */
+//#undef NETBOOT_CMD /* netboot commnds, like autoboot for a single device */
/*
* Error message tables to include
diff --git a/src/hci/commands/netboot_cmd.c b/src/hci/commands/netboot_cmd.c
new file mode 100644
index 0000000..74ffed3
--- /dev/null
+++ b/src/hci/commands/netboot_cmd.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <gpxe/command.h>
+#include <gpxe/netdevice.h>
+#include <usr/autoboot.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+void netboot_usage(char** argv, char* msg) {
+ printf ( "Usage:\n"
+ " %s <interface>\n"
+ "\n"
+ "Attempts to boot the system\n"
+ "%s\n",
+ argv[0], msg );
+}
+
+static int netboot_exec ( int argc, char **argv ) {
+
+ if (argc != 2) {
+ netboot_usage(argv, "Wrong number of arguments");
+ return 1;
+ }
+
+ struct net_device* netdev = find_netdev(argv[1]);
+ if (!netdev) {
+ netboot_usage(argv, "No such interface.");
+ return 1;
+ }
+
+ netboot(netdev);
+ return 0;
+}
+
+struct command netboot_command __command = {
+ .name = "netboot",
+ .exec = netboot_exec,
+};
diff --git a/src/hci/commands/vconfig_cmd.c b/src/hci/commands/vconfig_cmd.c
new file mode 100644
index 0000000..7edca01
--- /dev/null
+++ b/src/hci/commands/vconfig_cmd.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 Michael Brown <[email protected]>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <strings.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/ieee8021q.h>
+#include <gpxe/command.h>
+
+/** @file
+ *
+ * 801.Q interface management commands
+ *
+ */
+
+/**
+ * Print syntax of vconfig command
+ *
+ * @v argv Command arguments
+ * @v help Error message
+ */
+static void vconfig_syntax ( char **argv, char* help ) {
+ printf ( "Usage:\n"
+ " %s add <interface> vid\n"
+ " %s rem <interface>\n"
+ "\n"
+ "%s\n",
+ argv[0], argv[0], help );
+}
+
+static int vconfig_exec ( int argc, char **argv ) {
+ struct net_device* netdev = NULL;
+ int vid = 0;
+
+ if (argc < 3) {
+ vconfig_syntax ( argv, "Too few parameters." );
+ return 1;
+ }
+
+ netdev = find_netdev(argv[2]);
+ if (!netdev) {
+ vconfig_syntax ( argv, "No such device." );
+ return 1;
+ }
+
+ if (strcmp("add", argv[1]) == 0) {
+ if (argc < 4) {
+ vconfig_syntax ( argv, "Missing vid." );
+ }
+ vid = strtoul(argv[3],NULL,0);
+ if (vid <= 0 || vid >= 4095) {
+ vconfig_syntax ( argv, "VID must be in range [1,4094]." );
+ }
+ netdev = ieee8021q_create(netdev,vid);
+ if (!netdev) {
+ printf("Failed to create vlan device.\n");
+ return 1;
+ } else {
+ printf("Added device %s\n", netdev->name);
+ }
+ } else if (strcmp("rem", argv[1]) == 0) {
+ ieee8021q_remove(netdev);
+ } else {
+ vconfig_syntax ( argv, "Unknown command." );
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Interface management commands */
+struct command vconfig_commands[] __command = {
+ {
+ .name = "vconfig",
+ .exec = vconfig_exec,
+ },
+};
diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h
index 1a6b482..6d25e68 100644
--- a/src/include/gpxe/errfile.h
+++ b/src/include/gpxe/errfile.h
@@ -120,6 +120,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_vxge_main ( ERRFILE_DRIVER | 0x00550000 )
#define ERRFILE_vxge_config ( ERRFILE_DRIVER | 0x00560000 )
#define ERRFILE_vxge_traffic ( ERRFILE_DRIVER | 0x00570000 )
+#define ERRFILE_ieee8021q ( ERRFILE_DRIVER | 0x00580000 )
#define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 )
#define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 )
diff --git a/src/include/gpxe/ieee8021q.h b/src/include/gpxe/ieee8021q.h
new file mode 100644
index 0000000..bff0440
--- /dev/null
+++ b/src/include/gpxe/ieee8021q.h
@@ -0,0 +1,12 @@
+#ifndef _GPXE_8021Q_H
+#define _GPXE_8021Q_H
+
+#include <stdint.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+struct net_device * ieee8021q_create ( struct net_device* target, uint16_t vid );
+void ieee8021q_remove ( struct net_device* netdev);
+
+#endif
+
diff --git a/src/include/gpxe/if_ether_8021q.h b/src/include/gpxe/if_ether_8021q.h
new file mode 100644
index 0000000..71d54ca
--- /dev/null
+++ b/src/include/gpxe/if_ether_8021q.h
@@ -0,0 +1,26 @@
+#ifndef _GPXE_IF_ETHER_8021Q_H
+#define _GPXE_IF_ETHER_8021Q_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#define NET_8021Q_CFI_POS 12
+#define NET_8021Q_CFI_LEN 1
+#define NET_8021Q_CFI_VAL 0
+#define NET_8021Q_PRIO_POS 13
+#define NET_8021Q_PRIO_LEN 3
+#define NET_8021Q_VID_POS 0
+#define NET_8021Q_VID_LEN 12
+
+#define NET_8021Q_PROTO 0x8100
+
+/* 802.1Q header */
+struct vlanhdr {
+ /** PRIO (3bit) / CFI (1Bit) / VID (12 Bit) */
+ uint16_t h_8021q;
+ /** Protocol ID */
+ uint16_t h_protocol;
+} __attribute__ ((packed));
+
+
+#endif /* _GPXE_IF_ETHER_8021Q_H */
diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h
index 858d8e9..b02b78c 100644
--- a/src/include/gpxe/netdevice.h
+++ b/src/include/gpxe/netdevice.h
@@ -267,7 +267,7 @@ struct net_device {
/** List of open network devices */
struct list_head open_list;
/** Name of this network device */
- char name[8];
+ char name[10];
/** Underlying hardware device */
struct device *dev;
diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h
index a918020..242dd42 100644
--- a/src/include/usr/autoboot.h
+++ b/src/include/usr/autoboot.h
@@ -22,4 +22,6 @@ extern int boot_root_path ( const char *root_path );
extern int pxe_menu_boot ( struct net_device *netdev )
__attribute__ (( weak ));
+extern int netboot ( struct net_device *netdev );
+
#endif /* _USR_AUTOBOOT_H */
diff --git a/src/net/ieee8021q.c b/src/net/ieee8021q.c
new file mode 100644
index 0000000..904c3a6
--- /dev/null
+++ b/src/net/ieee8021q.c
@@ -0,0 +1,266 @@
+/* ieee8021q.c - etherboot driver for proxing 802.1Q to other devices
+ *
+ * This driver installs an networkl ayer receiving handler,
+ * which will catch 802.1Q packets from the raw device and instead inject them into a virtual vlan device.
+ * Further, all packets send through the virtual device and then proxied through the link layer of the raw device.
+ * All device actions apply instandly on the raw device.
+ *
+ * (c) Copyright 2010 M. Braun <[email protected]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <gpxe/if_ether_8021q.h>
+#include <gpxe/ieee8021q.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/malloc.h>
+#include <gpxe/list.h>
+#include <gpxe/iobuf.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+struct vlan_device {
+ struct net_device* netdev; /* the vlan device */
+ struct list_head list;
+};
+
+static LIST_HEAD ( ieee8021q_devices );
+
+/** The 802.1Q VLAN-ID to use */
+struct setting ethernet_802_1q_vid_setting __setting = {
+ .name = "vid",
+ .description = "vlan id",
+ .type = &setting_type_uint16,
+};
+
+/** The 802.1P priority to use */
+struct setting ethernet_802_1q_prio_setting __setting = {
+ .name = "prio",
+ .description = "packet priority",
+ .type = &setting_type_uint8,
+};
+
+/* proxy device operations */
+static void ieee8021q_close ( struct net_device* dev ) {
+ return netdev_close((struct net_device*)dev->priv);
+}
+
+static int ieee8021q_open ( struct net_device* dev ) {
+ return netdev_open((struct net_device*)dev->priv);
+}
+
+static int ieee8021q_transmit ( struct net_device* dev, struct io_buffer* iobuf ) {
+ /* cannot proxy iobuf directly because it includes a list reference! */
+ struct io_buffer* newiobuf = alloc_iob(iob_len(iobuf)); //+iob_headroom(iobuf)+iob_tailroom(iobuf));
+ if (!newiobuf) return -ENOMEM;
+ void* data = iob_put(newiobuf, iob_len(iobuf));
+ memcpy(data, iobuf->data, iob_len(iobuf));
+ int rc = netdev_tx((struct net_device*)dev->priv, newiobuf);
+ if (rc==0) {
+ netdev_tx_complete ( dev, iobuf );
+ } /* otherwise netdev_tx will do so */
+ return rc;
+}
+
+static void ieee8021q_poll ( struct net_device* dev __unused) {
+ netdev_poll((struct net_device*)dev->priv);
+}
+
+static void ieee8021q_irq ( struct net_device* dev, int enable ) {
+ /* a little bit unsure about side effects */
+ return netdev_irq((struct net_device*)dev->priv, enable);
+}
+
+static struct net_device_operations ieee8021q_operations = {
+ .open = ieee8021q_open,
+ .close = ieee8021q_close,
+ .transmit = ieee8021q_transmit,
+ .poll = ieee8021q_poll,
+ .irq = ieee8021q_irq,
+};
+
+/** Link-Layer Stack */
+
+#define FILTERNBIT(n) ((1 << n)-1)
+
+/**
+ * Add Ethernet link-layer header. This is called on the tagged device.
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @v ll_dest Link-layer destination address
+ * @v ll_source Source link-layer address
+ * @v net_proto Network-layer protocol, in network-byte order
+ * @ret rc Return status code
+ */
+static int ieee8021q_push ( struct net_device *netdev,
+ struct io_buffer *iobuf, const void *ll_dest,
+ const void *ll_source, uint16_t net_proto ) {
+ struct vlanhdr *vlanhdr = iob_push ( iobuf, sizeof ( *vlanhdr ) );
+
+ const unsigned int prio = fetch_uintz_setting( netdev_settings(netdev) , ðernet_802_1q_prio_setting);
+ const unsigned int vid = fetch_uintz_setting( netdev_settings(netdev) , ðernet_802_1q_vid_setting);
+
+ vlanhdr->h_8021q = 0;
+ vlanhdr->h_8021q |= (prio & FILTERNBIT(NET_8021Q_PRIO_LEN)) << NET_8021Q_PRIO_POS;
+ vlanhdr->h_8021q |= (vid & FILTERNBIT(NET_8021Q_VID_LEN)) << NET_8021Q_VID_POS;
+ vlanhdr->h_8021q |= (NET_8021Q_CFI_VAL & FILTERNBIT(NET_8021Q_CFI_LEN)) << NET_8021Q_CFI_POS;
+ vlanhdr->h_8021q = htons(vlanhdr->h_8021q);
+ vlanhdr->h_protocol = net_proto;
+
+ netdev = netdev->priv;
+ return netdev->ll_protocol->push(netdev, iobuf, ll_dest, ll_source, htons(NET_8021Q_PROTO));
+}
+
+/**
+ * Remove Ethernet link-layer header. This is called on raw device which can receive tagged and untagged packets.
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret ll_dest Link-layer destination address
+ * @ret ll_source Source link-layer address
+ * @ret net_proto Network-layer protocol, in network-byte order
+ * @ret rc Return status code
+ */
+static int ieee8021q_pull ( struct io_buffer *iobuf, struct net_device *netdev,
+ const void *ll_source) {
+ struct vlanhdr *vlanhdr = iobuf->data;
+
+ if ( iob_len ( iobuf ) < sizeof(*vlanhdr) ) {
+ DBG ( "VLAN packet too short (%zd bytes)\n",
+ iob_len ( iobuf ) );
+ goto err;
+ }
+
+ /* Strip off VLAN header */
+ iob_pull ( iobuf, sizeof(*vlanhdr) );
+
+ const unsigned int incomingvid = (htons(vlanhdr->h_8021q) >> NET_8021Q_VID_POS) & FILTERNBIT(NET_8021Q_VID_LEN);
+ if (incomingvid <= 0 || incomingvid >= 4095) {
+ DBG("Bad vlan id %d on wire\n", incomingvid);
+ goto err;
+ }
+
+ struct vlan_device* vdev;
+ list_for_each_entry(vdev, &ieee8021q_devices, list) {
+ const unsigned int vid = fetch_uintz_setting( netdev_settings(vdev->netdev) , ðernet_802_1q_vid_setting);
+
+ if (incomingvid != vid || vid <= 0 || vid >= 4095 || netdev != (struct net_device*) vdev->netdev->priv)
+ continue;
+
+ DBG("inject into %s with proto %X\n", vdev->netdev->name, vlanhdr->h_protocol);
+ if (!(vdev->netdev->state & NETDEV_OPEN)) goto err;
+
+ return net_rx ( iobuf, vdev->netdev, vlanhdr->h_protocol, ll_source);
+
+ }
+err:
+ free_iob(iobuf);
+ return -EINVAL;
+}
+
+static const char * vid_ntoa ( const void *net_addr ) {
+ static char buf[2]; /* "00" */
+ const uint8_t eth_addr = * (uint8_t*)net_addr;
+
+ sprintf ( buf, "%02x", eth_addr);
+ return buf;
+}
+
+/** 802.1Q protocol */
+struct net_protocol ieee8021q_protocol __net_protocol = {
+ .name = "802.1Q",
+ .net_proto = htons ( NET_8021Q_PROTO ),
+ .net_addr_len = 2,
+ .rx = ieee8021q_pull,
+ .ntoa = vid_ntoa,
+};
+
+/** Initialisation and Remove */
+
+struct net_device * ieee8021q_create ( struct net_device* target, uint16_t vid ) {
+ struct net_device* netdev = alloc_netdev( 0 );
+
+ if (!netdev)
+ return 0;
+
+ netdev_init ( netdev, &ieee8021q_operations );
+
+ netdev->ll_protocol = zalloc(sizeof(*target->ll_protocol));
+ if (!netdev->ll_protocol)
+ goto err_register_llprotocol;
+ memcpy(netdev->ll_protocol,target->ll_protocol, sizeof(*target->ll_protocol));
+ netdev->ll_protocol->push = ieee8021q_push;
+
+ netdev->ll_broadcast = target->ll_broadcast;
+ netdev->max_pkt_len = target->max_pkt_len;
+ memcpy(netdev->hw_addr,target->hw_addr,sizeof(netdev->hw_addr));
+ netdev->state = target->state;
+
+ char newname[sizeof(target->name)];
+ snprintf ( newname, sizeof ( newname ), "%s.%d", target->name, vid);
+ strncpy(netdev->name,newname,sizeof(netdev->name));
+
+ /* Mark as link up; we don't yet handle link state */
+ netdev_link_up ( netdev );
+
+ char value[4];
+ snprintf( value, sizeof(value), "%d", vid);
+ storef_setting ( netdev_settings(netdev), ðernet_802_1q_vid_setting, value );
+
+ /* Register network device */
+ if ( register_netdev ( netdev ) != 0 )
+ goto err_register_netdev;
+
+ /* write-back ll_addr init */
+ memcpy(netdev->ll_addr,target->ll_addr,sizeof(netdev->ll_addr));
+
+ /* add netdev to list */
+ struct vlan_device* vdev = zalloc(sizeof(*vdev));
+ vdev->netdev = netdev;
+ list_add(&vdev->list, &ieee8021q_devices);
+
+ /* set target device */
+ netdev->priv = netdev_get(target);
+
+ DBG("Added device %s.\n", netdev->name);
+
+ return netdev;
+
+ err_register_netdev:
+ free(netdev->ll_protocol);
+ err_register_llprotocol:
+ netdev_put( netdev->priv );
+ netdev->priv = NULL;
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ return 0;
+}
+
+void ieee8021q_remove ( struct net_device* netdev) {
+ if (netdev->op->open != ieee8021q_open) {
+ DBG("Sorry, but this does not look like a ieee8021q device.\n"
+ "So I won't remove it!\n");
+ return;
+ }
+
+ struct vlan_device* vdev;
+ list_for_each_entry(vdev, &ieee8021q_devices, list) {
+ if (vdev->netdev == netdev) {
+ list_del(&vdev->list);
+ }
+ }
+
+ unregister_netdev ( netdev );
+ free(netdev->ll_protocol);
+ netdev->ll_protocol = NULL;
+ netdev_put( netdev->priv );
+ netdev->priv = NULL;
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index ee0d0b7..91de351 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -354,8 +354,10 @@ int register_netdev ( struct net_device *netdev ) {
int rc;
/* Create device name */
- snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
+ if (strlen(netdev->name) < 1 || find_netdev(netdev->name)) {
+ snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
ifindex++ );
+ }
/* Set initial link-layer address */
netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
@@ -567,8 +569,9 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
/* Hand off to network-layer protocol, if any */
for_each_table_entry ( net_protocol, NET_PROTOCOLS ) {
- if ( net_protocol->net_proto == net_proto )
+ if ( net_protocol->net_proto == net_proto ) {
return net_protocol->rx ( iobuf, netdev, ll_source );
+ }
}
DBGC ( netdev, "NETDEV %p unknown network protocol %04x\n",
@@ -608,7 +611,8 @@ static void net_step ( struct process *process __unused ) {
*/
if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
- DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
+ DBGC ( netdev, "NETDEV %s %p processing %p (%p+%zx)\n",
+ netdev->name,
netdev, iobuf, iobuf->data,
iob_len ( iobuf ) );
diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c
index 2fa10e6..2061fa5 100644
--- a/src/usr/autoboot.c
+++ b/src/usr/autoboot.c
@@ -128,7 +128,7 @@ int boot_root_path ( const char *root_path ) {
* @v netdev Network device
* @ret rc Return status code
*/
-static int netboot ( struct net_device *netdev ) {
+int netboot ( struct net_device *netdev ) {
struct setting vendor_class_id_setting
= { .tag = DHCP_VENDOR_CLASS_ID };
struct setting pxe_discovery_control_setting
signature.asc
Description: PGP signature
signature.asc
Description: OpenPGP digital signature
_______________________________________________ gPXE-devel mailing list [email protected] http://etherboot.org/mailman/listinfo/gpxe-devel
