[PATCH] Implementation of tap device for Ethernet level 2 in pfinet
Ralph Ronnquist <[email protected]> Thu, 30 Jul 2026 19:57:02 +1000
| Newsgroups | gmane.os.hurd.bugs |
|---|---|
| Message-ID | <amsf7hJ8N3DaTClu@smulan> |
So, as a last kick on the horse, this is a re-send as seemeingly expected.
Ralph.
---
pfinet/linux-src/include/linux/if_ether.h | 44 +++++++++++++
pfinet/linux-src/net/core/dev.c | 4 +-
pfinet/main.c | 2 +
pfinet/tunnel.c | 77 ++++++++++++++++++-----
4 files changed, 110 insertions(+), 17 deletions(-)
diff --git a/pfinet/linux-src/include/linux/if_ether.h b/pfinet/linux-src/include/linux/if_ether.h
index 99bb97fa2..efb566e05 100644
--- a/pfinet/linux-src/include/linux/if_ether.h
+++ b/pfinet/linux-src/include/linux/if_ether.h
@@ -87,6 +87,50 @@ struct ethhdr
unsigned short h_proto; /* packet type ID field */
};
+/* Alternative Ethernet header with VLAN tagging. The header without
+ VLAN tagging (the original header) is found in <net/ethernet.h>.
+ This header modelling accounts for packets optionally having VLAN
+ tagging inserted before the ether_type field.
+*/
+
+struct ethhdr_vlan
+{
+ uint8_t h_dest[ETH_ALEN]; /* destination eth addr */
+ uint8_t h_source[ETH_ALEN]; /* source ether addr */
+ uint16_t h_vlan_tag; /* htons( 0x8100 ) */
+ uint16_t h_vlan_id; /* VLAN ID */
+ uint16_t h_proto; /* packet type ID field */
+};
+
+/* Generic header that incorporates both without and with VLAN alternatives */
+union ethhdr_either {
+ struct ethhdr without_vlan;
+ struct ethhdr_vlan with_vlan;
+};
+
+/* Repeating from net/ethernet.h */
+#define ETHERTYPE_VLAN 0x8100
+
+/* Detecting presence of VLAN tag */
+static inline int has_vlan_tag(unsigned char *frame) {
+ uint16_t v = ntohs( ((struct ethhdr_vlan*)frame)->h_vlan_tag );
+ return ( v == ETHERTYPE_VLAN );
+}
+
+/* Obtain ether_type handling optional VLAN tag. */
+static inline uint16_t get_protocol(unsigned char *frame)
+{
+ return ntohs( has_vlan_tag(frame)?
+ (((struct ethhdr_vlan*)frame)->h_proto) :
+ (((struct ethhdr*)frame)->h_proto) );
+}
+
+/* Obtain payload base address handling optional VLAN tag */
+static inline unsigned char *get_ether_payload_p(unsigned char *frame) {
+ return frame + ( has_vlan_tag(frame)?
+ sizeof(struct ethhdr_vlan) : sizeof(struct ethhdr) );
+}
+
/*
* We Have changed the ethernet statistics collection data. This
* is just for partial compatibility for now.
diff --git a/pfinet/linux-src/net/core/dev.c b/pfinet/linux-src/net/core/dev.c
index b47c50270..93e5aab1f 100644
--- a/pfinet/linux-src/net/core/dev.c
+++ b/pfinet/linux-src/net/core/dev.c
@@ -923,7 +923,9 @@ void net_bh(void)
*/
/* XXX until we figure out every place to modify.. */
- skb->h.raw = skb->nh.raw = skb->data;
+ if ( skb->nh.raw < skb->data ||
+ skb->nh.raw > skb->data + sizeof(struct ethhdr_vlan))
+ skb->h.raw = skb->nh.raw = skb->data;
if (skb->mac.raw < skb->head || skb->mac.raw > skb->data) {
printk(KERN_CRIT "%s: wrong mac.raw ptr, proto=%04x\n", skb->dev->name, skb->protocol);
diff --git a/pfinet/main.c b/pfinet/main.c
index cd50950b7..233735a3b 100644
--- a/pfinet/main.c
+++ b/pfinet/main.c
@@ -244,6 +244,8 @@ find_device (char *name, struct device **device)
if (strncmp(base_name, "tun", 3) == 0)
setup_tunnel_device (name, device);
+ else if (strncmp(base_name, "tap", 3) == 0)
+ setup_tunnel_device (name, device);
else if (strncmp(base_name, "dummy", 5) == 0)
setup_dummy_device (name, device);
else
diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
index 4301d054f..6532cac5c 100644
--- a/pfinet/tunnel.c
+++ b/pfinet/tunnel.c
@@ -58,7 +58,6 @@ struct tunnel_device
struct net_device_stats stats;
};
-
/* Linked list of all tunnel devices. */
struct tunnel_device *tunnel_dev;
@@ -146,6 +145,14 @@ tunnel_xmit (struct sk_buff *skb, struct device *dev)
return 0;
}
+/* Obtained from ethernet.c and edited */
+static int
+tunnel_change_flags (struct device *dev, short flags)
+{
+ /* unsupported, but ignore */
+ return 0;
+}
+
void
setup_tunnel_device (char *name, struct device **device)
{
@@ -168,16 +175,17 @@ setup_tunnel_device (char *name, struct device **device)
tunnel_dev = tdev;
*device = dev = &tdev->dev;
+ dev->priv = tdev;
+ skb_queue_head_init(&tdev->xq); // Initialise tdev->xq
base_name = strrchr (name, '/');
if (base_name)
base_name++;
else
base_name = name;
-
+
dev->name = strdup (base_name);
- dev->priv = tdev;
dev->get_stats = tunnel_get_stats;
/* Functions. These ones are the true "hardware layer" in Linux. */
@@ -186,21 +194,44 @@ setup_tunnel_device (char *name, struct device **device)
dev->hard_start_xmit = tunnel_xmit;
dev->set_multicast_list = tunnel_set_multi;
- /* These are the ones set by drivers/net/ppp_generic.c::ppp_net_init. */
- dev->hard_header = 0;
- dev->hard_header_len = 0;
- dev->mtu = PPP_MTU;
- dev->addr_len = 0;
- dev->tx_queue_len = 3;
- dev->type = ARPHRD_PPP;
- dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ if ( strncmp( base_name, "tap", 3 ) == 0 ) {
+ /* The setup for a tap is most similar the ethernet.c */
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->hard_header_cache = eth_header_cache;
+ dev->header_cache_update = eth_header_cache_update;
+ dev->hard_header_parse = eth_header_parse;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = PPP_MTU;
+ dev->addr_len = ETH_ALEN;
+ dev->tx_queue_len = 3;
+ dev->type = ARPHRD_ETHER;
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST | IFF_ALLMULTI;
+ dev->change_flags = tunnel_change_flags;
+
+ // We *know* that ETH_ALEN == 6
+ unsigned char num[ 4 ]; //
+ sscanf( name, "tap%u", (unsigned int*)&num );
+ memcpy( dev->dev_addr, (char[6]) {
+ 0x06, 'T', num[3], num[2], num[1], num[0] }, 6 );
+ } else {
+ /* These are the ones set by drivers/net/ppp_generic.c::ppp_net_init. */
+ dev->hard_header = 0;
+ dev->hard_header_len = 0;
+ dev->mtu = PPP_MTU;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 3;
+ dev->type = ARPHRD_ETHER;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ dev->change_flags = tunnel_change_flags;
+ }
dev_init_buffers (dev);
if (base_name != name)
tdev->devname = strdup (name);
else
- /* Setting up the translator at /dev/tunX. */
+ /* Setting up the translator at /dev/tunX or /dev/tapX */
asprintf (&tdev->devname, "/dev/%s", tdev->dev.name);
tdev->underlying = file_name_lookup (tdev->devname, O_CREAT|O_NOTRANS, 0664);
@@ -299,7 +330,8 @@ trivfs_S_io_read (struct trivfs_protid *cred,
/* Deny access if they have bad credentials. */
if (! cred)
return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_READ))
+
+ if (! (cred->po->openmodes & O_READ))
return EBADF;
if (cred->pi.class != tunnel_class)
@@ -380,7 +412,8 @@ trivfs_S_io_write (struct trivfs_protid *cred,
/* Deny access if they have bad credentials. */
if (! cred)
return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_WRITE))
+
+ if (! (cred->po->openmodes & O_WRITE))
return EBADF;
if (cred->pi.class != tunnel_class)
@@ -388,6 +421,10 @@ trivfs_S_io_write (struct trivfs_protid *cred,
tdev = (struct tunnel_device *) cred->po->cntl->hook;
+ if ((tdev->dev.hard_header != 0) && (datalen < 18)) // small packet
+ return EBADF;
+
+
pthread_mutex_lock (&tdev->lock);
pthread_mutex_lock (&net_bh_lock);
@@ -400,7 +437,14 @@ trivfs_S_io_write (struct trivfs_protid *cred,
/* Drop it on the queue. */
skb->mac.raw = skb->data;
- skb->protocol = htons (ETH_P_IP);
+ if ( tdev->dev.hard_header != 0 ) {
+ skb->protocol = get_protocol(skb->data);
+ skb->h.raw = skb->nh.raw = get_ether_payload_p(skb->data);
+ } else {
+ skb->protocol = htons(ETH_P_IP);
+ skb->h.raw = skb->nh.raw = skb->data;
+ }
+
netif_rx (skb);
pthread_mutex_unlock (&net_bh_lock);
@@ -424,7 +468,8 @@ trivfs_S_io_readable (struct trivfs_protid *cred,
/* Deny access if they have bad credentials. */
if (! cred)
return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_READ))
+
+ if (! (cred->po->openmodes & O_READ))
return EBADF;
if (cred->pi.class != tunnel_class)
--
2.47.3