I know how to create a tagged interface in linux using vconfig like eth1.10 which makes it receive and send tagged packets. Is it possible to create an interface which receives and sends untagged packet for vlan 10?
6There is no such thing as an "untagged packet for vlan 10". In 802.1Q, frames are either tagged or untagged. If tagged, they belong to the vlan mentioned in tha tag. If untagged, they belong to a default vlan.– CeladaJul 13, 2015 at 5:10
1I see no reason to close, while technically impossible, this is a valid question.– ArchemarJul 13, 2015 at 7:02
1There is absolutely such a thing as "untagged packet for vlan 10". It is a common phrase for the state of a port where the system takes untagged traffic and tags it with vlan 10 before further processing. It also means that if a frame needs to be forwarded to that host, ensure the system removes the tag prior to forwarding. It is the same as Cisco's "pvid" or "native" terms, which all deal with how we process untagged frames.– Greg SchmitOct 12, 2017 at 23:59
An untagged interface already exists: it's the parent interface, that is,
eth1 in this case.
Sending packets through
eth1.10 results in those packets being tagged while packets received with VLAN tag 10 come in through that interface.
Sending packets through
eth1 results in those packets being untagged while packets received without a VLAN tag come in through that interface.
There is no such thing as "sending untagged packets for vlan 10". Untagged packets don't have any vlan tag, by definition.
1In an HP switch, if you go into an interface and set it to be "untagged on vlan 10", that means that untagged traffic is assigned to vlan 10. What the OP seems to want is traffic incoming on eth1 to behave as if it came in on eth1.10. Here is a helpful HP link for their terminology of untagged vs tagged: community.hpe.com/t5/Switches-Hubs-and-Modems/… Sep 1, 2017 at 0:39
1The above may sound weird, but it is a standard feature in any network switch. In Cisco, it's called setting the port access vlan or trunk pvid, and in HP lingo it is called setting the interface untagged vlan (default is 1). Sep 1, 2017 at 0:40
2@GregSchmit that's true, a completely standard feature of ethernet switches is to assign incoming untagged packets to a vlan, but that is by definition not exposed on the wire and the Unix host at the other end has no idea it's happening.– CeladaSep 17, 2017 at 10:31
1I agree that it wouldn't be exposed on the wire, which is why the questioner asked for functionality that "receives and sends untagged packet for vlan 10". I was just clarifying that talking about sending or recieving "untagged" vlan packets is a coherent thing to talk about, even though it sounds weird. It just means that the switch internally treats the untagged packet on that port as in the vlan broadcast domain of vlan X, and knows to strip vlan tags before sending to that host on the untagged port. Sep 18, 2017 at 21:45
1I think currently the feature just isn't implemented. You can't set a port VLAN ID (PVID) and easily put a port "native" on a VLAN in the BSDs and Linux distros I've used. Sep 18, 2017 at 21:47
I've found the following method to work.
- Use TC to match packets without 802.1q on ingres and push a vlan header with id 0
- Use TC to match packets in vlan 0 on egress, and pop the vlan header
- Create the untagged logical interface with type=vlan and id=0
Here's an example:
$ tc qdisc del dev eth0 root $ tc qdisc add dev eth0 handle 1: root prio $ tc filter add dev eth0 parent 1: protocol 802.1q basic match 'meta(vlan eq 0)' action vlan pop $ tc qdisc del dev eth0 ingress $ tc qdisc add dev eth0 handle ffff: ingress $ tc filter add dev eth0 parent ffff: protocol all basic match 'not meta(protocol eq 0x8100)' action vlan push id 0 $ ip link add eth0-vlan0 type vlan id 0 $ ip link set eth0-vlan0 up $ ip addr add 192.168.10.253/24 dev eth0-vlan0 peer$ ping 192.168.10.253 PING 192.168.10.253 (192.168.10.253) 56(84) bytes of data. 64 bytes from 192.168.10.253: icmp_seq=1 ttl=64 time=0.209 ms 64 bytes from 192.168.10.253: icmp_seq=2 ttl=64 time=0.188 ms ^C
Note that adding the prio qdisc on egress (known as "root" above) disables the default mq discipline, which divides packets according to number of transmit queues on the NIC in order to allow multiple CPU threads to push packets to the different TX queues. To restore the default behavior, use mqprio as follows:
$ tx_q_count=$(ls -d /sys/class/net/eth0/queues/tx-* | wc -l) $ tc qdisc del dev eth0 root $ tc qdisc add dev eth0 handle 1: root prio bands $tx_q_count multiqueue $ tc filter add dev eth0 parent 1: protocol 802.1q basic match 'meta(vlan eq 0)' action vlan pop $ for i in $(seq 1 $tx_q_count); do tc qdisc add dev eth0 parent 1:$i pfifo_fast; done $ tc qdisc del dev eth0 ingress $ tc qdisc add dev eth0 handle ffff: ingress $ tc filter add dev eth0 parent ffff: protocol all basic match 'not meta(protocol eq 0x8100)' action vlan push id 0 $ ip link add eth0-vlan0 type vlan id 0 $ ip link set eth0-vlan0 up $ ip addr add 192.168.10.253/24 dev eth0-vlan0 peer$ ping 192.168.10.253 PING 192.168.10.253 (192.168.10.253) 56(84) bytes of data. 64 bytes from 192.168.10.253: icmp_seq=1 ttl=64 time=0.230 ms 64 bytes from 192.168.10.253: icmp_seq=2 ttl=64 time=0.198 ms ^C
A different solution that also works is to add a software bridge next to the vlan logical interfaces on the physical interface. The software bridge has a vlan filtering feature that allows limited vlan remapping (pvid->vid on ingres and vid->pvid on egress, but not full remapping) that can be used. Here's an example:
$ ip link add eth0-vlan0 type bridge $ echo 0 > /sys/class/net/eth0-vlan0/bridge/default_pvid $ echo 1 > /sys/class/net/eth0-vlan0/bridge/vlan_filtering $ ip link set dev eth0 master eth0-vlan0 $ bridge vlan add vid 4094 dev eth0 pvid untagged $ bridge vlan add vid 4094 dev eth0-vlan0 untagged pvid self $ ip link add eth0-vlan4094 link eth0 type vlan id 4094 # ... BEGIN: only if you intend to use vlan 4094 ... $ ip link set eth0-vlan4094 up $ ip addr add 10.10.40.253/24 dev eth0-vlan4094 # ... END ... $ ip link set eth0-vlan0 up $ ip addr add 192.168.10.253/24 dev eth0-vlan0 peer$ ping 192.168.10.253 PING 192.168.10.253 (192.168.10.253) 56(84) bytes of data. 64 bytes from 192.168.10.253: icmp_seq=1 ttl=64 time=0.238 ms 64 bytes from 192.168.10.253: icmp_seq=2 ttl=64 time=0.215 ms ^C
You need the VLAN interface matching the VID number used inside the bridge, because the VLAN filtering feature is a bit so-so. It accepts both untagged frames using the pvid->vid mapping, buuut it also accepts frames with a VLAN tag on wire matching the remapped vid. The VLAN interface eats up those frames instead. Leave it in the down state if you do not need that particular VLAN.
The software bridge has recently gained a "default_pvid" feature which makes it work like cheap hardware switches: it is vlan aware, but will per default do a pvid->vid mapping to vid 1, and allow vid 1 on all ports, so that you can plug the switch in and start using ports for untagged traffic without a single configuration directive.
Finally, a more modern solution would be to use tc_clsact and write a small BPF program to push/pop the tags for the PVID. Works just like the TC example above, except that some NICs allow hardware offload of BPF programs, so it could be more performant.
As mentioned in comments and another answer, there isn't specifically a way to send untagged packets belonging to a specific VLAN, because untagged packets have no way to describe which VLAN they belong to due to lack of a tag. As also mentioned, the parent interface (eth1 here) will send untagged packets.
However, this does not mean that untagged packets sent from your linux box can't belong to a particular VLAN on your network. It is up to the switch to assign untagged packets arriving on a given port to a particular VLAN. Typically, switches (which are capable of VLANs) will, by default, assign untagged packets to the "default VLAN (1)". However by changing the configuration on the switch, you can instruct it to put untagged packets received onto VLAN 10, and route them wherever else VLAN 10 is assigned.
If VLAN 10 is set to tagged on a different port, those same packets will come out tagged there if that's the destination of the packet and untagged on a different port that is set to untagged for VLAN 10
# ip link add link eth0 name untagged type vlan id 0
seems to do the trick.
tcpdump on the parent interface (eth0) shows untagged packets when you ping -I untagged (some-ip).
(tcpdump on the new logical interface should show only untagged traffic, while tcpdump on the parent interface should show all traffic, tagged and untagged, with 802.1q headers for the tagged stuff.)
you may need to disable ARP on the parent interface, so that untagged ARP replies are not put in the ARP table relative to the parent interface:
# ip link set eth0 arp off # ip link set untagged arp on
i would also disable IPv6 link-local addresses on the parent interface, and enable on the untagged logical interface, for good measure.
you can up/down the untagged interface without killing the whole parent interface (tagged traffic included) etc.
This can’t be the whole answer. At least: I’ve tried it on my local setup, and this does not work. I can bridge traffic from other VLANs, but VLAN 0 does not communicate on the LAN with other untagged hosts.– andrewfJun 12, 2019 at 20:40
I also tried this, and the "untagged" interface does not receive untagged frames, even after allowing arp on the vlan if and disallowing arp on the base interface. Sep 15, 2021 at 17:29
You can create a bridge between a physical interface (eth0) and a vlan interface (eth1:10). This bridge will then allow untagged packets from the physical interface (eth0) to communicate with the tagged network on the vlan interface (eth1:10).
I am using exactly this constellation. Whether this would work if the bridge was created over a parent interface (eth0) and its own subordinate vlan interface (eth0:10) I cannot say...
Here's the solution using eBPF.
To avoid having to install bcc and compile the code from source, here's the binary:
$ base64 -d > tag-native-vlan0.o f0VMRgIBAQAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAALABAAAAAAAAAAAAAEAAAAAA AEAABwABAGESFAAAAAAAVQIDAAAAAAC3AgAAAIEAALcDAAAAAAAAhQAAABIAAAC3AAAAAAAAAJUA AAAAAAAAYRIUAAAAAAAVAgMAAAAAAGESGAAAAAAAVQIBAAAAAACFAAAAEwAAALcAAAAAAAAAlQAA AAAAAABHUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF4AAAAAAAMAKAAAAAAAAAAAAAAA AAAAAFcAAAAAAAQAKAAAAAAAAAAAAAAAAAAAAD0AAAAQAAUAAAAAAAAAAAAAAAAAAAAAACMAAAAQ AAQAAAAAAAAAAAAAAAAAAAAAAAcAAAAQAAMAAAAAAAAAAAAAAAAAAAAAAAAudGV4dABjbHNhY3Rf aW5ncmVzcwBwdmlkX2luZ3Jlc3MAY2xzYWN0X2VncmVzcwBwdmlkX2VncmVzcwBfX2xpY2Vuc2UA LnN0cnRhYgAuc3ltdGFiAExCQjFfMwBMQkIwXzIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEcAAAADAAAAAAAAAAAAAAAA AAAAAAAAAEgBAAAAAAAAZQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAYA AAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAFgAA AAEAAAAGAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAA AAAAADEAAAABAAAABgAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAOAAAAAAAAAAAAAAAAAAAAAgAAAAA AAAAAAAAAAAAAAA/AAAAAQAAAAMAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAQAAAAAAAAAAAAAAAAA AAABAAAAAAAAAAAAAAAAAAAATwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAuAAAAAAAAACQAAAAAAAA AAEAAAADAAAACAAAAAAAAAAYAAAAAAAAAA== ^D
I've set the PVID to map to VID 0 instead of VID 10, for no particular reason.
(To change it to VID 10, use a disassembler and assembler, eg. ubpf or llvm-objdump, on the .o file, and change the VLAN tag. You can also talk to eBPF from user space, so it should be possible to make the pvid reconfigurable at runtime if need be.)
Make sure to run something like kernel 4.8.0 (
uname -a) and iproute2 4.9.0 (
Add the eBPF to the physical interface:
$ tc qdisc add dev eth0 clsact $ tc filter add dev eth0 ingress bpf object-file tag-native-vlan0.o section pvid_ingress $ tc filter add dev eth0 egress bpf object-file tag-native-vlan0.o section pvid_egress
Create a logical interface:
$ ip link add eth0-vlan0 link eth0 type vlan id 0 $ ip link set eth0-vlan0 up $ ip addr add 192.168.10.253/24 dev eth0-vlan0
L3 is configured on the logical interface, and the physical interface is used for L2.
Test from a connected box:
peer$ ping 192.168.10.253 PING 192.168.10.253 (192.168.10.253) 56(84) bytes of data. 64 bytes from 192.168.10.253: icmp_seq=1 ttl=64 time=0.380 ms 64 bytes from 192.168.10.253: icmp_seq=2 ttl=64 time=0.240 ms ^C
And there you have it, an "untagged interface on Linux".
4You should really post the C code that you used to produce this object file. For people ready to use bcc, it would be much easier to reuse. And easier to understand what happens for all people reading your answer. For some reason I had to pass
llvm-objdumpfor it to find the target, not sure why (usually not necessary). Also, nitpick: tc/ip commands should be run as root.– QeoleApr 11, 2020 at 21:52