Re: Updated: Enabling tap interfaces in pfinet

Ralph Ronnquist <[email protected]> Thu, 30 Jul 2026 09:16:41 +1000
Newsgroups gmane.os.hurd.bugs
Message-ID <amqJ2dDgt7Vfv3L0@smulan>
--YNtnX2em3eM7NacX
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline
Content-Transfer-Encoding: 8bit

Fair enough. I've attached the merged patch here, and then took the
opportunity to fix up the tap mac address logic as well.

There's still a problem in using a direct mapping of the tap name to
mac address since then a virtual cable between two hosts will require
there being differently named taps. Perhaps it really needs an
additional pfnet argument for that.

Ralph.

On Wed, Jul 29, 2026 at 05:53:33PM +0200, Samuel Thibault wrote:
> Hello,
> 
> Ralph Ronnquist, le mer. 29 juil. 2026 01:07:31 +1000, a ecrit:
> > This is a follow-on patch for the tap implementation, with a bit of
> > cleanup after feedback from Alperen Erkan <[email protected]>
> 
> Please merge the two, so I just review the merged version instead of two
> separate patches.
> 
> Samuel
> 
> > From 2c68b2e7ea443c167f3d9280132fa53b40fe7d83 Mon Sep 17 00:00:00 2001
> > From: Ralph Ronnquist <[email protected]>
> > Date: Tue, 28 Jul 2026 17:50:09 +1000
> > Subject: [PATCH 2/2] Code cleanup for Ethernet level 2 implementation
> > 
> > ---
> >  pfinet/linux-src/include/linux/if_ether.h | 44 +++++++++++++++
> >  pfinet/linux-src/net/core/dev.c           |  3 +-
> >  pfinet/tunnel.c                           | 65 +++--------------------
> >  3 files changed, 54 insertions(+), 58 deletions(-)
> > 
> > diff --git a/pfinet/linux-src/include/linux/if_ether.h b/pfinet/linux-src/include/linux/if_ether.h
> > index 99bb97fa..efb566e0 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 2f480865..93e5aab1 100644
> > --- a/pfinet/linux-src/net/core/dev.c
> > +++ b/pfinet/linux-src/net/core/dev.c
> > @@ -923,7 +923,8 @@ void net_bh(void)
> >  		 */
> >  
> >  		/* XXX until we figure out every place to modify.. */
> > -		if ( skb->nh.raw < skb->data || skb->nh.raw > skb->data + 20 ) 
> > +		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) {
> > diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
> > index b22cd2a2..b7e4ab53 100644
> > --- a/pfinet/tunnel.c
> > +++ b/pfinet/tunnel.c
> > @@ -58,8 +58,6 @@ struct tunnel_device
> >    struct net_device_stats stats;
> >  };
> >  
> > -static int istap = 0;
> > -
> >  /* Linked list of all tunnel devices.  */
> >  struct tunnel_device *tunnel_dev;
> >  
> > @@ -186,8 +184,6 @@ setup_tunnel_device (char *name, struct device **device)
> >    else
> >      base_name = name;
> >    
> > -  istap = ( strncmp( base_name, "tap", 3 ) == 0 );
> > -
> >    dev->name = strdup (base_name);
> >  
> >    dev->get_stats = tunnel_get_stats;
> > @@ -198,7 +194,7 @@ setup_tunnel_device (char *name, struct device **device)
> >    dev->hard_start_xmit = tunnel_xmit;
> >    dev->set_multicast_list = tunnel_set_multi;
> >  
> > -  if ( istap ) {
> > +  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;
> > @@ -214,12 +210,8 @@ setup_tunnel_device (char *name, struct device **device)
> >        dev->change_flags = tunnel_change_flags;
> >  
> >        // We *know* that ETH_ALEN == 6
> > -      char *nametail = name + strlen(name) - 4;
> > -      assert_backtrace ( nametail >= name );
> > -      memcpy( dev->dev_addr, (char[ETH_ALEN]) {
> > -	      0x06, 'H', 'u', 'r', 'd', 0xff & ( nametail[3] - '0' )
> > -	  }, ETH_ALEN );
> > -
> > +      memcpy( dev->dev_addr, "\006Tap\000\000", 6 );
> > +      sscanf( name, "tap%u", (unsigned int*)&dev->dev_addr[4] );
> >    } else {
> >        /* These are the ones set by drivers/net/ppp_generic.c::ppp_net_init.  */
> >        dev->hard_header = 0;
> > @@ -396,48 +388,6 @@ trivfs_S_io_read (struct trivfs_protid *cred,
> >    return 0;
> >  }
> >  
> > -/* 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 */
> > -};
> > -
> > -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*) );
> > -}
> > -
> >  /* Write data to an IO object.  If offset is -1, write at the object
> >     maintained file pointer.  If the object is not seekable, offset is
> >     ignored.  The amount successfully written is returned in amount.  A
> > @@ -464,14 +414,15 @@ trivfs_S_io_write (struct trivfs_protid *cred,
> >    if (! (cred->po->openmodes & O_WRITE))
> >      return EBADF;
> >  
> > -  if ( istap && ( datalen < 18 ) ) // Too small Ethernet packet
> > -    return EBADF;
> > -
> >    if (cred->pi.class != tunnel_class)
> >      return EOPNOTSUPP;
> >  
> >    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);
> > @@ -484,7 +435,7 @@ trivfs_S_io_write (struct trivfs_protid *cred,
> >  
> >    /* Drop it on the queue. */
> >    skb->mac.raw = skb->data;
> > -  if ( istap ) {
> > +  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 {
> > -- 
> > 2.53.0
> > 
> 
> 
> -- 
> Samuel
>   bien sûr que ça convient mieux à tout le monde
>   enfin, dans la mesure où tout le monde c'est comme moi
>  -+- le consensus, c'est facile -+-

--YNtnX2em3eM7NacX
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="0001-Implementation-of-tap-device-for-Ethernet-level-2-in.patch"

From 9bed4aa661cd3e52500718022f581c4c04c2416a Mon Sep 17 00:00:00 2001
From: Ralph Ronnquist <[email protected]>
Date: Thu, 30 Jul 2026 09:01:21 +1000
Subject: [PATCH] Implementation of tap device for Ethernet level 2 in pfinet

---
 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


--YNtnX2em3eM7NacX--