@@ -1142,7 +1142,6 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
* for frames that lack any 802.1Q header later. */
if (tag || !strcmp(binding->type, "localnet")
|| !strcmp(binding->type, "l2gateway")) {
- match_set_dl_vlan(&match, htons(tag), 0);
if (nested_container) {
/* When a packet comes from a container sitting behind a
* parent_port, we should let it loopback to other containers
@@ -1151,7 +1150,16 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
put_load(1, MFF_LOG_FLAGS, MLF_NESTED_CONTAINER_BIT, 1,
ofpacts_p);
}
- ofpact_put_STRIP_VLAN(ofpacts_p);
+
+ /* For vlan-passthru switch ports that are untagged, skip
+ * matching/stripping VLAN header that originates from the VIF
+ * itself. */
+ bool passthru = smap_get_bool(&binding->options,
+ "vlan-passthru", false);
+ if (!passthru || tag) {
+ match_set_dl_vlan(&match, htons(tag), 0);
+ ofpact_put_STRIP_VLAN(ofpacts_p);
+ }
}
/* Remember the size with just strip vlan added so far,
@@ -3174,6 +3174,12 @@ ovn_port_update_sbrec(struct northd_context *ctx,
smap_add_format(&options,
"qdisc_queue_id", "%d", queue_id);
}
+
+ if (smap_get_bool(&op->od->nbs->other_config,
+ "vlan-passthru", false)) {
+ smap_add(&options, "vlan-passthru", "true");
+ }
+
sbrec_port_binding_set_options(op->sb, &options);
smap_destroy(&options);
if (ovn_is_known_nb_lsp_type(op->nbsp->type)) {
@@ -561,7 +561,11 @@
<group title="Other options">
<column name="other_config" key="vlan-passthru"
type='{"type": "boolean"}'>
- Determines whether VLAN tagged incoming traffic should be allowed.
+ Determines whether VLAN tagged incoming traffic should be allowed. Note
+ that this may have security implications when enabled for a logical
+ switch with a tag=0 localnet port. If not properly isolated from other
+ localnet ports, fabric traffic that belongs to other tagged networks
+ may be passed through such a port.
</column>
</group>
@@ -3086,6 +3086,137 @@ OVN_CLEANUP([hv-1],[hv-2])
AT_CLEANUP
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- VLAN transparency, passthru=true, multiple hosts])
+ovn_start
+
+check ovn-nbctl ls-add ls
+check ovn-nbctl --wait=sb add Logical-Switch ls other_config vlan-passthru=true
+
+ln_port_name=ln-100
+ovn-nbctl lsp-add ls $ln_port_name "" 100
+ovn-nbctl lsp-set-addresses $ln_port_name unknown
+ovn-nbctl lsp-set-type $ln_port_name localnet
+ovn-nbctl lsp-set-options $ln_port_name network_name=phys-100
+net_add n-100
+
+# two hypervisors, each connected to the same network
+for i in 1 2; do
+ sim_add hv-$i
+ as hv-$i
+ ovs-vsctl add-br br-phys
+ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys-100:br-phys
+ ovn_attach n-100 br-phys 192.168.0.$i
+done
+
+for i in 1 2; do
+ check ovn-nbctl lsp-add ls lsp$i
+ check ovn-nbctl lsp-set-addresses lsp$i f0:00:00:00:00:0$i
+done
+
+for i in 1 2; do
+ as hv-$i
+ ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lsp$i \
+ options:tx_pcap=vif$i-tx.pcap \
+ options:rxq_pcap=vif$i-rx.pcap \
+ ofport-request=$i
+ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lsp$i` = xup])
+done
+
+test_packet() {
+ local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6
+
+ # First try tracing the packet.
+ uflow="inport==\"lsp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth && vlan.present==1"
+ echo "output(\"$lout\");" > expout
+ AT_CAPTURE_FILE([trace])
+ AT_CHECK([ovn-trace --all ls "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout])
+
+ # Then actually send a packet, for an end-to-end test.
+ local packet=$(echo $dst$src | sed 's/://g')${eth}fefefefe
+ vif=vif$inport
+ as hv-$1
+ ovs-appctl netdev-dummy/receive $vif $packet
+ echo $packet >> ${eout#lsp}.expected
+}
+
+test_packet 1 f0:00:00:00:00:02 f0:00:00:00:00:01 8100 lsp2 lsp2
+test_packet 2 f0:00:00:00:00:01 f0:00:00:00:00:02 8100 lsp1 lsp1
+for i in 1 2; do
+ OVN_CHECK_PACKETS_REMOVE_BROADCAST([vif$i-tx.pcap], [$i.expected])
+done
+
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- VLAN transparency, passthru=true, multiple hosts, flat/untagged])
+ovn_start
+
+check ovn-nbctl ls-add ls
+check ovn-nbctl --wait=sb add Logical-Switch ls other_config vlan-passthru=true
+
+ln_port_name=ln
+ovn-nbctl lsp-add ls $ln_port_name
+ovn-nbctl lsp-set-addresses $ln_port_name unknown
+ovn-nbctl lsp-set-type $ln_port_name localnet
+ovn-nbctl lsp-set-options $ln_port_name network_name=phys
+net_add n
+
+# two hypervisors, each connected to the same network
+for i in 1 2; do
+ sim_add hv-$i
+ as hv-$i
+ ovs-vsctl add-br br-phys
+ ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ ovn_attach n br-phys 192.168.0.$i
+done
+
+for i in 1 2; do
+ check ovn-nbctl lsp-add ls lsp$i
+ check ovn-nbctl lsp-set-addresses lsp$i f0:00:00:00:00:0$i
+done
+
+for i in 1 2; do
+ as hv-$i
+ ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lsp$i \
+ options:tx_pcap=vif$i-tx.pcap \
+ options:rxq_pcap=vif$i-rx.pcap \
+ ofport-request=$i
+ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lsp$i` = xup])
+done
+
+for i in 1 2; do
+ : > $i.expected
+done
+
+test_packet() {
+ local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6
+
+ # First try tracing the packet.
+ uflow="inport==\"lsp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth && vlan.present==1"
+ echo "output(\"$lout\");" > expout
+ AT_CAPTURE_FILE([trace])
+ AT_CHECK([ovn-trace --all ls "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout])
+
+ # Then actually send a packet, for an end-to-end test.
+ local packet=$(echo $dst$src | sed 's/://g')${eth}fefefefe
+ vif=vif$inport
+ as hv-$1
+ ovs-appctl netdev-dummy/receive $vif $packet
+ echo $packet >> ${eout#lsp}.expected
+}
+
+test_packet 1 f0:00:00:00:00:02 f0:00:00:00:00:01 8100 lsp2 lsp2
+test_packet 2 f0:00:00:00:00:01 f0:00:00:00:00:02 8100 lsp1 lsp1
+
+for i in 1 2; do
+ OVN_CHECK_PACKETS_REMOVE_BROADCAST([vif$i-tx.pcap], [$i.expected])
+done
+
+AT_CLEANUP
+])
+
AT_SETUP([ovn -- VLAN transparency, passthru=true])
ovn_start