Hi, I've been happy using gpxe to boot linux for a while and now'd like to deploy it in an environment where support 802.1q is essential. Namely, I'd like to deploy access points (based on an alix2 board) on ports where the untagged vlan is already in use.
Therefore, I've had a quick glance at the gpxe source and it looked quite easy to add. I just changed net/ethernet.c to add/verify 802.1q headers and a 802.1q header declaration to if_ether.h . No changes to legacy.c are required, as it looks like a packet of kind 0x8100 to it and will split and join it correctly. To configure the vlan identifiers, use set net0/vid 10,11,12 which means tagged with vlan 10, 11 and 12 where vlan 10 is the outer identity. Please find a patch attached, feedback is welcomed. Though, I could test it only with a single vlan header added (the switches here don't support nested vlans, yet) and did it by embedding set net0/vid 514 autoboot into the image. Sincerely, M. Braun
diff --git a/src/include/gpxe/if_ether.h b/src/include/gpxe/if_ether.h
index b96bee0..c837a1e 100644
--- a/src/include/gpxe/if_ether.h
+++ b/src/include/gpxe/if_ether.h
@@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */
#ifndef ETH_MAX_MTU
#define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN)
+#define ETH_8021Q_LEN 4
#endif
#define ETH_P_RAW 0x0000 /* Raw packet */
@@ -23,7 +24,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ETH_P_EAPOL 0x888E /* 802.1X EAP over LANs */
#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
-/** An Ethernet link-layer header */
+#define MAX_NUM_VLAN 20
+#define MAX_VLAN_ID_LENGTH 4 /* number of bytes needed for textual representation of VID */
+
+/** An Ethernet link-layer header (untagged) */
struct ethhdr {
/** Destination MAC address */
uint8_t h_dest[ETH_ALEN];
@@ -33,4 +37,12 @@ struct ethhdr {
uint16_t h_protocol;
} __attribute__ ((packed));
+/** An Ethernet link-layer header (tagged) */
+struct ethhdrtagged {
+ /** 802.1Q tag */
+ uint8_t h_8021q[ETH_8021Q_LEN];
+ /** Protocol ID */
+ uint16_t h_protocol;
+} __attribute__ ((packed));
+
#endif /* _GPXE_IF_ETHER_H */
diff --git a/src/net/ethernet.c b/src/net/ethernet.c
index 79ed1dc..779bc40 100644
--- a/src/net/ethernet.c
+++ b/src/net/ethernet.c
@@ -19,6 +19,7 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <byteswap.h>
@@ -40,6 +41,44 @@ FILE_LICENCE ( GPL2_OR_LATER );
/** Ethernet broadcast MAC address */
static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+/** The 802.1Q VLAN-ID to use */
+struct setting ethernet_802_1q_vid_setting __setting = {
+ .name = "vid",
+ .description = "802.1Q VID (vlan id)",
+ .type = &setting_type_string,
+};
+
+/** The 802.1P priority to use */
+struct setting ethernet_802_1q_prio_setting __setting = {
+ .name = "prio",
+ .description = "802.1Q PRIO (user_priority)",
+ .type = &setting_type_uint8,
+};
+
+/**
+ * Parses the 802.1Q vid string (= VID1,VID2,...) and writes the VIDs
+ * it into the vidbuf buffer. It returns the number of vlans.
+ * @v netdev Network device
+ * @v vidbuf buffer containing VIDs (preallocated for MAX_NUM_VLANs)
+ * @ret vidcounter number of vlans configured.
+ */
+unsigned int fetch_vlans(struct net_device *netdev, unsigned int* vidbuf ) {
+ char buf[(MAX_VLAN_ID_LENGTH+1) * MAX_NUM_VLAN];
+ unsigned int len = fetch_string_setting( netdev_settings(netdev) , ðernet_802_1q_vid_setting, buf, sizeof(buf)-1);
+ unsigned int vidcounter = 0, pos, start;
+ for (pos=0, start=0; vidcounter < MAX_NUM_VLAN && pos < len+1; pos++) {
+ if (pos == len || buf[pos] < '0' || buf[pos] > '9') { /* not a digit or end of line */
+ buf[pos] = 0x00;
+ vidbuf[vidcounter] = (unsigned int) strtoul ( buf+start, NULL, 0 );
+ if (vidbuf[vidcounter] > 0 && vidbuf[vidcounter] < 4095) { /* it is a valid vid */
+ vidcounter++;
+ }
+ start = pos+1;
+ }
+ }
+ return vidcounter;
+}
+
/**
* Add Ethernet link-layer header
*
@@ -50,16 +89,37 @@ static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
* @v net_proto Network-layer protocol, in network-byte order
* @ret rc Return status code
*/
-static int eth_push ( struct net_device *netdev __unused,
+static int eth_push ( struct net_device *netdev,
struct io_buffer *iobuf, const void *ll_dest,
const void *ll_source, uint16_t net_proto ) {
- struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) );
+ unsigned int prio = fetch_uintz_setting( netdev_settings(netdev) , ðernet_802_1q_prio_setting);
+ unsigned int vid[MAX_NUM_VLAN];
+ unsigned int vidcounter = fetch_vlans(netdev, vid);
+
+ struct ethhdr *ethhdr;
+ struct ethhdrtagged * ethhdrtagged;
+ unsigned int iobuflen = sizeof(*ethhdr) + vidcounter * (sizeof(*ethhdrtagged) - sizeof(ethhdrtagged->h_protocol));
+ ethhdr = iob_push ( iobuf, iobuflen);
/* Build Ethernet header */
memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN );
memcpy ( ethhdr->h_source, ll_source, ETH_ALEN );
ethhdr->h_protocol = net_proto;
+ unsigned int i;
+ ethhdrtagged = (struct ethhdrtagged*) ðhdr->h_protocol;
+ for (i=0; i < vidcounter; i++) {
+ /* Build 802.1Q header */
+ ethhdrtagged->h_8021q[0] = 0x81;
+ ethhdrtagged->h_8021q[1] = 0x00;
+ ethhdrtagged->h_8021q[2] = vid[i] / 256;
+ ethhdrtagged->h_8021q[3] = vid[i] % 256;
+ /* CFI set to zero, PRIO has 3 Bit */
+ ethhdrtagged->h_8021q[2] |= (prio << 5) & 0xE0 ;
+ ethhdrtagged->h_protocol = net_proto;
+ ethhdrtagged = (struct ethhdrtagged*) ðhdrtagged->h_protocol;
+ }
+
return 0;
}
@@ -77,22 +137,46 @@ static int eth_pull ( struct net_device *netdev __unused,
struct io_buffer *iobuf, const void **ll_dest,
const void **ll_source, uint16_t *net_proto ) {
struct ethhdr *ethhdr = iobuf->data;
+ struct ethhdrtagged *ethhdrtagged = (struct ethhdrtagged*) ðhdr->h_protocol;
+ unsigned int vid[MAX_NUM_VLAN];
+ unsigned int vidcounter = fetch_vlans(netdev, vid);
+ unsigned int iobuflen = sizeof(*ethhdr) + vidcounter * (sizeof(*ethhdrtagged) - sizeof(ethhdrtagged->h_protocol));
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) {
+ if ( iob_len ( iobuf ) < iobuflen ) {
DBG ( "Ethernet packet too short (%zd bytes)\n",
iob_len ( iobuf ) );
return -EINVAL;
}
- /* Strip off Ethernet header */
- iob_pull ( iobuf, sizeof ( *ethhdr ) );
-
/* Fill in required fields */
*ll_dest = ethhdr->h_dest;
*ll_source = ethhdr->h_source;
*net_proto = ethhdr->h_protocol;
+ unsigned int tagcounter = 0;
+ /* Test if this is a 802.1Q tagged packet */
+ while (ethhdrtagged->h_8021q[0] == 0x81 && ethhdrtagged->h_8021q[1] == 0x00) {
+ /* check if we expect a tagged header here */
+ if (tagcounter >= vidcounter) return -EINVAL;
+
+ /* check for correct vid */
+ unsigned long packet_vid = ethhdrtagged->h_8021q[3] + 256 * (ethhdrtagged->h_8021q[2] & 0x0F);
+ if (packet_vid != vid[tagcounter]) return -EINVAL; /* wrong vlan identifier */
+
+ /* Fill in required fields */
+ *net_proto = ethhdrtagged->h_protocol;
+
+ /* step next*/
+ ethhdrtagged = (struct ethhdrtagged*) ðhdrtagged->h_protocol;
+ tagcounter++;
+ }
+
+ if (vidcounter != tagcounter) return -EINVAL;
+
+ /* Strip off Ethernet header */
+ iob_pull ( iobuf, iobuflen );
+
return 0;
}
signature.asc
Description: OpenPGP digital signature
_______________________________________________ gPXE-devel mailing list [email protected] http://etherboot.org/mailman/listinfo/gpxe-devel
