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) , &ethernet_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) , &ethernet_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*) &ethhdr->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*) &ethhdrtagged->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*) &ethhdr->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*) &ethhdrtagged->h_protocol;
+		tagcounter++;
+	}
+
+	if (vidcounter != tagcounter) return -EINVAL;
+
+	/* Strip off Ethernet header */
+	iob_pull ( iobuf, iobuflen );
+
 	return 0;
 }
 

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
gPXE-devel mailing list
[email protected]
http://etherboot.org/mailman/listinfo/gpxe-devel

Reply via email to