diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index e2f4deca9..72f83ebbe 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -53,7 +53,7 @@ const ( DefaultRegistryPort = 5050 //tags, versions, repos - DefaultEVETag = "13.3.0" // DefaultEVETag tag for EVE image + DefaultEVETag = "13.7.0" // DefaultEVETag tag for EVE image DefaultAdamTag = "0.0.57" DefaultRedisTag = "7" DefaultRegistryTag = "2.7" diff --git a/sdn/examples/vlan-subinterfaces/README.md b/sdn/examples/vlan-subinterfaces/README.md new file mode 100644 index 000000000..a0fa8c7e7 --- /dev/null +++ b/sdn/examples/vlan-subinterfaces/README.md @@ -0,0 +1,103 @@ +# SDN Example with VLAN sub-interfaces + +VLANs enable the segmentation of a physical network into multiple logical networks, +allowing for better traffic control, security, and resource optimization. +On EVE, the use of VLANs helps isolate the management traffic from application traffic +or even to split applications and their traffic into different logical networks. +This allows the external networks to give preferential treatment and apply different +policies as per their requirements. + +VLAN configurations supported by EVE: + +1. VLAN filtering for switch network instances +2. VLAN sub-interfaces over physical NICs used for management traffic or for Local NIs +3. VLAN sub-interfaces over LAGs used for management traffic or for Local NIs + +In this example, we focus on the second use-case, where VLANs are used to separate management +traffic from the application traffic routed via Local network instances. + +Network topology diagram: + +```text + +-----+ +----------------+ + | EVE |--| VLAN 10 (mgmt) |----- + +-----+ +----------------+ | + | ++------+ +--------------+ +---------+ +------+ +| app1 |--| NI1 (local) |--| VLAN 20 |--| eth0 | ++------+ +--------------+ +---------+ +------+ + | ++------+ +--------------+ | +| app2 |--| NI2 (local) |-------- ++------+ +--------------+ +``` + +Deploy example with: + +```shell +make clean && make build-tests +./eden config add default +./eden config set default --key sdn.disable --value false +./eden setup --eve-bootstrap-file $(pwd)/sdn/examples/vlan-subinterfaces/device-config.json +./eden start --sdn-network-model $(pwd)/sdn/examples/vlan-subinterfaces/network-model.json +./eden eve onboard +./eden controller edge-node set-config --file $(pwd)/sdn/examples/vlan-subinterfaces/device-config.json +``` + +Note that VLAN IP subnets are `172.22..0/24`. EVE will therefore use IP address from +the subnet `172.22.10.0/24` to access the controller. Network traffic from `app1` will be NATed +to an IP address from `172.22.20.0/24` before it leaves the edge node. +`app2` will be using the underlying `eth0` interface instead of a VLAN sub-interface to access +the untagged portion of the network with subnet `192.168.77.0/24`. + +Once deployed, check DHCP-assigned IPs: + +```shell +./eden eve ssh +$ ifconfig vlan10 +vlan10 Link encap:Ethernet HWaddr 02:FE:22:1A:87:00 + inet addr:172.22.10.13 Bcast:172.22.10.255 Mask:255.255.255.0 + ... + +$ ifconfig vlan20 +vlan20 Link encap:Ethernet HWaddr 02:FE:22:1A:87:00 + inet addr:172.22.20.13 Bcast:172.22.20.255 Mask:255.255.255.0 + +$ ifconfig eth0 +eth0 Link encap:Ethernet HWaddr 02:FE:22:1A:87:00 + inet addr:192.168.77.13 Bcast:192.168.77.255 Mask:255.255.255.0 + ... +``` + +Check that `app1` can access HTTP server deployed for VLAN 20 (`httpserver-20.sdn`), +but not HTTP server deployed for VLAN 10 (`httpserver-10.sdn`) or for the untagged +network (`httpserver-untagged.sdn`): + +```shell +./eden eve ssh +CONSOLE="$(eve list-app-consoles | grep cee082fd-3a43-4599-bbd3-8216ffa8652d | grep CONTAINER | awk '{print $4}')" +eve attach-app-console "$CONSOLE" + +app1$ curl httpserver-20.sdn/helloworld +Hello world from HTTP server for VLAN 20 +app1$ curl httpserver-10.sdn/helloworld +curl: (7) Failed to connect to httpserver-10.sdn port 80 after 44 ms: Couldn't connect to server +app1$ curl httpserver-untagged.sdn/helloworld +curl: (7) Failed to connect to httpserver-untagged.sdn port 80 after 48 ms: Couldn't connect to server +``` + +Check that `app2` can access HTTP server deployed for the untagged network (`httpserver-untagged.sdn`), +but not HTTP server deployed for VLAN 10 (`httpserver-10.sdn`) or for VLAN 20 (`httpserver-20.sdn`): + +```shell +./eden eve ssh +CONSOLE="$(eve list-app-consoles | grep 45ff198d-b295-4ff2-bf69-76977af809fd | grep CONTAINER | awk '{print $4}')" +eve attach-app-console "$CONSOLE" + +app2$ curl httpserver-untagged.sdn/helloworld +Hello world from HTTP server for untagged network +app1$ curl httpserver-10.sdn/helloworld +curl: (7) Failed to connect to httpserver-10.sdn port 80 after 47 ms: Couldn't connect to server +app1$ curl httpserver-20.sdn/helloworld +curl: (7) Failed to connect to httpserver-20.sdn port 80 after 47 ms: Couldn't connect to server +``` \ No newline at end of file diff --git a/sdn/examples/vlan-subinterfaces/device-config.json b/sdn/examples/vlan-subinterfaces/device-config.json new file mode 100644 index 000000000..a2d467d71 --- /dev/null +++ b/sdn/examples/vlan-subinterfaces/device-config.json @@ -0,0 +1,269 @@ +{ + "deviceIoList": [ + { + "ptype": 1, + "phylabel": "eth0", + "phyaddrs": { + "Ifname": "eth0" + }, + "logicallabel": "ethernet0", + "assigngrp": "eth0", + "usage": 1 + } + ], + "vlans": [ + { + "logicallabel": "mgmt-vlan", + "interfaceName": "vlan10", + "lowerLayerName": "ethernet0", + "vlanId": 10 + }, + { + "logicallabel": "app-vlan", + "interfaceName": "vlan20", + "lowerLayerName": "ethernet0", + "vlanId": 20 + } + ], + "networks": [ + { + "id": "6605d17b-3273-4108-8e6e-4965441ebe01", + "type": 4, + "ip": { + "dhcp": 4 + } + }, + { + "id": "b970ac70-2ef7-4c6b-8bb8-ff8626321313", + "type": 4, + "ip": { + "dhcp": 2 + } + } + ], + "systemAdapterList": [ + { + "name": "mgmt-vlan", + "uplink": true, + "networkUUID": "6605d17b-3273-4108-8e6e-4965441ebe01" + }, + { + "name": "app-vlan", + "networkUUID": "6605d17b-3273-4108-8e6e-4965441ebe01" + }, + { + "name": "ethernet0", + "networkUUID": "6605d17b-3273-4108-8e6e-4965441ebe01" + } + ], + "networkInstances": [ + { + "uuidandversion": { + "uuid": "9ca83da9-94e8-48b4-9ae8-3f188c5c694a", + "version": "1" + }, + "displayname": "ni1", + "instType": 2, + "activate": true, + "port": { + "type": 1, + "name": "app-vlan" + }, + "ipType": 1, + "ip": { + "subnet": "10.50.10.0/24", + "gateway": "10.50.10.1", + "dns": [ + "10.50.10.1" + ], + "dhcpRange": { + "start": "10.50.10.2", + "end": "10.50.10.254" + } + } + }, + { + "uuidandversion": { + "uuid": "0125b9fa-9054-446d-9e9a-1b99175d24a7", + "version": "1" + }, + "displayname": "ni2", + "instType": 2, + "activate": true, + "port": { + "type": 1, + "name": "ethernet0" + }, + "ipType": 1, + "ip": { + "subnet": "10.50.77.0/24", + "gateway": "10.50.77.1", + "dns": [ + "10.50.77.1" + ], + "dhcpRange": { + "start": "10.50.77.2", + "end": "10.50.77.254" + } + } + } + ], + "apps": [ + { + "uuidandversion": { + "uuid": "cee082fd-3a43-4599-bbd3-8216ffa8652d", + "version": "1" + }, + "displayname": "app1", + "fixedresources": { + "memory": 512000, + "maxmem": 512000, + "vcpus": 1, + "virtualizationMode": 1 + }, + "drives": [ + { + "image": { + "uuidandversion": { + "uuid": "398710ca-bf4f-46b0-b012-0d4e32214ba4", + "version": "1" + }, + "name": "lfedge/eden-eclient:8a279cd", + "iformat": 8, + "dsId": "f204830d-cce1-4316-aa5e-3e8567cd09a9" + } + } + ], + "activate": true, + "interfaces": [ + { + "name": "eth0", + "networkId": "9ca83da9-94e8-48b4-9ae8-3f188c5c694a", + "acls": [ + { + "matches": [ + { + "type": "ip", + "value": "0.0.0.0/0" + } + ], + "id": 1 + } + ] + } + ], + "volumeRefList": [ + { + "uuid": "d8fe3e53-cc6c-4cee-8562-b406a1a8ada7", + "mount_dir": "/" + } + ] + }, + { + "uuidandversion": { + "uuid": "45ff198d-b295-4ff2-bf69-76977af809fd", + "version": "1" + }, + "displayname": "app2", + "fixedresources": { + "memory": 512000, + "maxmem": 512000, + "vcpus": 1, + "virtualizationMode": 1 + }, + "drives": [ + { + "image": { + "uuidandversion": { + "uuid": "3eec1356-a469-43e3-80e2-67467d06deaf", + "version": "1" + }, + "name": "lfedge/eden-eclient:8a279cd", + "iformat": 8, + "dsId": "f204830d-cce1-4316-aa5e-3e8567cd09a9" + } + } + ], + "activate": true, + "interfaces": [ + { + "name": "eth0", + "networkId": "0125b9fa-9054-446d-9e9a-1b99175d24a7", + "acls": [ + { + "matches": [ + { + "type": "ip", + "value": "0.0.0.0/0" + } + ], + "id": 1 + } + ] + } + ], + "volumeRefList": [ + { + "uuid": "cee944a3-ae6f-4887-9d8d-adcc0ed02370", + "mount_dir": "/" + } + ] + } + ], + "volumes": [ + { + "uuid": "d8fe3e53-cc6c-4cee-8562-b406a1a8ada7", + "origin": { + "type": 2, + "downloadContentTreeID": "63d3b01f-f44f-4007-ba33-6e720bd52992" + }, + "displayName": "app1-volume" + }, + { + "uuid": "cee944a3-ae6f-4887-9d8d-adcc0ed02370", + "origin": { + "type": 2, + "downloadContentTreeID": "63d3b01f-f44f-4007-ba33-6e720bd52992" + }, + "displayName": "app2-volume" + } + ], + "contentInfo": [ + { + "uuid": "63d3b01f-f44f-4007-ba33-6e720bd52992", + "dsId": "f204830d-cce1-4316-aa5e-3e8567cd09a9", + "URL": "lfedge/eden-eclient:8a279cd", + "iformat": 8, + "displayName": "eden-eclient" + } + ], + "datastores": [ + { + "id": "f204830d-cce1-4316-aa5e-3e8567cd09a9", + "dType": 5, + "fqdn": "docker://index.docker.io" + } + ], + "configItems": [ + { + "key": "newlog.allow.fastupload", + "value": "true" + }, + { + "key": "timer.config.interval", + "value": "10" + }, + { + "key": "timer.download.retry", + "value": "60" + }, + { + "key": "debug.default.loglevel", + "value": "debug" + }, + { + "key": "debug.disable.dhcp.all-ones.netmask", + "value": "false" + } + ] +} diff --git a/sdn/examples/vlan-subinterfaces/network-model.json b/sdn/examples/vlan-subinterfaces/network-model.json new file mode 100644 index 000000000..6ba171c9a --- /dev/null +++ b/sdn/examples/vlan-subinterfaces/network-model.json @@ -0,0 +1,148 @@ +{ + "ports": [ + { + "logicalLabel": "eveport0", + "adminUP": true + } + ], + "bridges": [ + { + "logicalLabel": "bridge0", + "ports": ["eveport0"] + } + ], + "networks": [ + { + "logicalLabel": "network-10", + "bridge": "bridge0", + "vlanID": 10, + "subnet": "172.22.10.0/24", + "gwIP": "172.22.10.1", + "dhcp": { + "enable": true, + "ipRange": { + "fromIP": "172.22.10.10", + "toIP": "172.22.10.20" + }, + "domainName": "sdn", + "privateDNS": ["dns-server"] + }, + "router": { + "outsideReachability": true, + "reachableEndpoints": ["dns-server", "httpserver-10"] + } + }, + { + "logicalLabel": "network-20", + "bridge": "bridge0", + "vlanID": 20, + "subnet": "172.22.20.0/24", + "gwIP": "172.22.20.1", + "dhcp": { + "enable": true, + "ipRange": { + "fromIP": "172.22.20.10", + "toIP": "172.22.20.20" + }, + "domainName": "sdn", + "privateDNS": ["dns-server"] + }, + "router": { + "outsideReachability": false, + "reachableEndpoints": ["dns-server", "httpserver-20"] + } + }, + { + "logicalLabel": "network-untagged", + "bridge": "bridge0", + "subnet": "192.168.77.0/24", + "gwIP": "192.168.77.1", + "dhcp": { + "enable": true, + "ipRange": { + "fromIP": "192.168.77.10", + "toIP": "192.168.77.20" + }, + "domainName": "sdn", + "privateDNS": ["dns-server"] + }, + "router": { + "outsideReachability": false, + "reachableEndpoints": ["dns-server", "httpserver-untagged"] + } + } + ], + "endpoints": { + "dnsServers": [ + { + "logicalLabel": "dns-server", + "fqdn": "dns-server.sdn", + "subnet": "10.16.16.0/24", + "ip": "10.16.16.25", + "staticEntries": [ + { + "fqdn": "mydomain.adam", + "ip": "adam-ip" + }, + { + "fqdn": "endpoint-fqdn.httpserver-10", + "ip": "endpoint-ip.httpserver-10" + }, + { + "fqdn": "endpoint-fqdn.httpserver-20", + "ip": "endpoint-ip.httpserver-20" + }, + { + "fqdn": "endpoint-fqdn.httpserver-untagged", + "ip": "endpoint-ip.httpserver-untagged" + } + ], + "upstreamServers": [ + "1.1.1.1", + "8.8.8.8" + ] + } + ], + "httpServers": [ + { + "logicalLabel": "httpserver-10", + "fqdn": "httpserver-10.sdn", + "subnet": "10.16.10.0/24", + "ip": "10.16.10.70", + "httpPort": 80, + "paths": { + "/helloworld": { + "contentType": "text/plain", + "content": "Hello world from HTTP server for VLAN 10\n" + } + } + }, + { + "logicalLabel": "httpserver-20", + "fqdn": "httpserver-20.sdn", + "subnet": "10.16.20.0/24", + "ip": "10.16.20.70", + "httpPort": 80, + "paths": { + "/helloworld": { + "contentType": "text/plain", + "content": "Hello world from HTTP server for VLAN 20\n" + } + } + }, + { + "logicalLabel": "httpserver-untagged", + "fqdn": "httpserver-untagged.sdn", + "subnet": "10.16.77.0/24", + "ip": "10.16.77.70", + "httpPort": 80, + "paths": { + "/helloworld": { + "contentType": "text/plain", + "content": "Hello world from HTTP server for untagged network\n" + } + } + } + ] + } +} \ No newline at end of file diff --git a/sdn/vm/cmd/sdnagent/parse.go b/sdn/vm/cmd/sdnagent/parse.go index 26da43f81..9479369f1 100644 --- a/sdn/vm/cmd/sdnagent/parse.go +++ b/sdn/vm/cmd/sdnagent/parse.go @@ -210,32 +210,6 @@ func (a *agent) validateNetworks(netModel *parsedNetModel) (err error) { } } - // Do not mix VLAN and non-VLAN network/endpoint with the same bridge - for _, bridge := range netModel.Bridges { - var netWithVlan, netWithoutVlan bool - labeledItem := netModel.items.getItem(api.Bridge{}.ItemType(), bridge.LogicalLabel) - for refKey, refBy := range labeledItem.referencedBy { - var vlanID uint16 - if strings.HasPrefix(refKey, api.NetworkBridgeRefPrefix) { - network := netModel.items[refBy].LabeledItem - vlanID = network.(api.Network).VlanID - } else if strings.HasPrefix(refKey, api.EndpointBridgeRefPrefix) { - endpoint := a.labeledItemToEndpoint(netModel.items[refBy]) - vlanID = endpoint.DirectL2Connect.VlanID - } - if (vlanID == 0 && netWithVlan) || (vlanID != 0 && netWithoutVlan) { - err = fmt.Errorf("bridge %s with both VLAN and non-VLAN networks/endpoints", - bridge.LogicalLabel) - return - } - if vlanID == 0 { - netWithoutVlan = true - } else { - netWithVlan = true - } - } - } - // Validate routes towards EVE. for _, network := range netModel.Networks { if network.Router == nil { diff --git a/sdn/vm/pkg/configitems/veth.go b/sdn/vm/pkg/configitems/veth.go index 18f087d91..bebf010ae 100644 --- a/sdn/vm/pkg/configitems/veth.go +++ b/sdn/vm/pkg/configitems/veth.go @@ -208,6 +208,14 @@ func (c *VethConfigurator) configurePeer(peer VethPeer) error { return fmt.Errorf("failed to add VLAN %d to veth peer %s: %w", peer.MasterBridge.VLAN, peer.IfName, err) } + // Do not let untagged traffic to come through this VETH interface. + err = netlink.BridgeVlanDel(link, 1, false, true, false, false) + // Ignore error if the rule for native VLAN was not automatically added. + if err != nil { + log.Warnf("Failed to remove native VLAN from veth peer %s: %v", + peer.IfName, err) + err = nil + } } } // Set link UP.