Enabling tap interfaces in pfinet
Ralph Ronnquist <[email protected]> Tue, 28 Jul 2026 21:24:01 +1000
| Newsgroups | gmane.os.hurd.bugs |
|---|---|
| Message-ID | <amiRUadh9kIcE4jL@smulan> |
--LjJjk2rOxIg0vPhn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hi,
attaching a patch for the hurd/hurd.git package, to enable "tap
device" usage with pfinet. The patch is intended to be applied at the
to levele of the git project, and it affects three pfinet files:
pfinet/linux-src/net/core/dev.c
pfinet/main.c
pfinet/tunnel.c
A "tap" is basically a virtual network device to allow a user program
to send/receive Ethernet level 2 packets by means of file I/O in the
tap. Similar to "tun" which however is for IP packets (level 3) rather
than Ethernet (level 2). Implementation wise it means to register like
an ethernet device but with the hardware and instead transfer packets
to and from the "tap" pathname as a fifo.
I.e. the user program "opens the tap" as RDWR, then use oridnary read
and write calls for dealing with networking packets so as to "be" a
virutal networking cable connecting to the host, as if the tap was a
real network device. At end, the user program closes the tap, which
then corresponds to disconnecting that virtual cable.
Setup for this is similar to /dev/eth0 setup, i.e. either using
ifconfig or configuration in /etc/network/interfaces for ifupdown
commands. In this patch, the taps must be named as /dev/tapX where X
is a single character, typically a digit. Thus, a setup for a "tap0"
tap would be a command like the following:
# settrans -cap /dev/tap0 /hurd/devnode -M /dev/net tap0
# ifconfig tap0 192.168.8.2 netmask 255.255.255.0 broadcast 192.168.8.255
Note there must not be a running /hurd/devnode for the tap when it is
configured. (That's a pfinet bug with tunnel devices that has not been
addressed in this patch).
Alternatively to using ifconfig, you can set up an iface block in
/etc/network/interface to declare the configuration, like so:
# eg. in file /etc/network/interfaces.d/dev-tap0
# An example tap0 configuration
iface tap0 inet static
address 192.168.8.2
netmask 255.255.255.0
broadcast 192.168.8.255
That confguration would allow ifup/ifdown to be used for tap0.
Significatly taps are named like "tap0" in commands and named by
pathname "/dev/tap0" in user programs.
This patch implements "tap" as a variation in the pfinet tunnel code
with in particular "setup_tunnel_device" modified so as to prepare the
actual device as similar to an etherenet device. It otherwise uses the
tunnel functionality of "tun" devices as already implement, except
that the incoming packet protocol is obtained from the packets rather
than forced to IP. The implementation is VLAN aware.
Note the patch to pfinet/linux-src/net/core/dev.c, which probably a
bug fix to allow the higher-level code to pin-point the Ethernet
packet payload appropriately.
Ralph.
--LjJjk2rOxIg0vPhn
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="0001-Implementation-tap-device-for-Ethernet-level-2-in-pf.patch"
From 043e211ce32210140f2d48d3488ac5504ba88977 Mon Sep 17 00:00:00 2001
From: Ralph Ronnquist <[email protected]>
Date: Tue, 28 Jul 2026 14:13:03 +1000
Subject: [PATCH] Implementation tap device for Ethernet level 2 in pfinet.
---
pfinet/linux-src/net/core/dev.c | 3 +-
pfinet/main.c | 2 +
pfinet/tunnel.c | 120 ++++++++++++++++++++++++++++----
3 files changed, 110 insertions(+), 15 deletions(-)
diff --git a/pfinet/linux-src/net/core/dev.c b/pfinet/linux-src/net/core/dev.c
index b47c5027..2f480865 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.. */
- skb->h.raw = skb->nh.raw = skb->data;
+ if ( skb->nh.raw < skb->data || skb->nh.raw > skb->data + 20 )
+ 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 cd50950b..233735a3 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 4301d054..b22cd2a2 100644
--- a/pfinet/tunnel.c
+++ b/pfinet/tunnel.c
@@ -58,6 +58,7 @@ struct tunnel_device
struct net_device_stats stats;
};
+static int istap = 0;
/* Linked list of all tunnel devices. */
struct tunnel_device *tunnel_dev;
@@ -146,6 +147,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 +177,19 @@ 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;
+
+ istap = ( strncmp( base_name, "tap", 3 ) == 0 );
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 +198,46 @@ 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 ( istap ) {
+ /* 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
+ 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 );
+
+ } 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 +336,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)
@@ -358,6 +396,48 @@ 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
@@ -380,7 +460,11 @@ 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 ( istap && ( datalen < 18 ) ) // Too small Ethernet packet
return EBADF;
if (cred->pi.class != tunnel_class)
@@ -400,7 +484,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 ( istap ) {
+ 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 +515,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.53.0
--LjJjk2rOxIg0vPhn--