diff --git a/pkg/eve/networks.go b/pkg/eve/networks.go index df1eb3757..cf2edee28 100644 --- a/pkg/eve/networks.go +++ b/pkg/eve/networks.go @@ -46,13 +46,17 @@ func (ctx *State) initNetworks(ctrl controller.Cloud, dev *device.Ctx) error { if err != nil { return fmt.Errorf("no netInst in cloud %s: %s", el, err) } + var subnet string + if ni.Ip != nil { + subnet = ni.Ip.Subnet + } netInstStateObj := &NetInstState{ Name: ni.GetDisplayname(), UUID: ni.Uuidandversion.Uuid, Stats: "-", AdamState: inControllerConfig, EveState: "UNKNOWN", - CIDR: ni.Ip.Subnet, + CIDR: subnet, NetworkType: ni.InstType.String(), } ctx.networks[ni.Uuidandversion.Uuid] = netInstStateObj diff --git a/sdn/examples/vlans-and-lags/README.md b/sdn/examples/vlans-and-lags/README.md new file mode 100644 index 000000000..9b99815b4 --- /dev/null +++ b/sdn/examples/vlans-and-lags/README.md @@ -0,0 +1,159 @@ +# SDN Example with VLANs and LAGs + +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. + +On the other hand, Link Aggregation Groups, commonly known as bonds or LAGs, aggregate +multiple physical links between network devices into a single logical link. +This not only increases the available bandwidth but also provides redundancy +and load balancing. LAGs ensure a more resilient and reliable network infrastructure +by distributing traffic across multiple links, thereby avoiding bottlenecks and improving +overall network performance. Moreover, LAGs enhance fault tolerance as they continue +to operate even if some individual links fail. + +EVE supports: + +- VLAN filtering for switch network instances +- VLAN sub-interfaces used as uplinks for management traffic or for local network instances +- LAG (bond) interfaces aggregating multiple physical interfaces and used as uplinks +- VLAN sub-interfaces over LAGs used as uplinks + +Here we combine all these cases into one example: + +- device has 3 physical interfaces all connected to the same switch +- `eth0` and `eth1` are aggregated by Round-Robin LAG +- Both `eth0+eth1` and `eth2` are trunks for VLANs 10, 20, 30 +- VLAN 10 is used for EVE management (controller is reachable) +- VLAN 20 is used by `app1` +- VLAN 30 is used by 2 apps: `app2` and `app3` +- These VLANs are isolated from each other (router does not have routes from one VLAN to another) +- `app1` is connected to VLAN 20 indirectly through local network instance which uses + VLAN sub-interface as uplink +- `app2` is connected to VLAN 30 directly through switch network instance with VLAN + sub-interface as uplink +- `app3` is also connected to VLAN 30 directly, but in this case through switch network + instance with `eth2` as uplink (trunk) and with traffic filtered down to VLAN 30 + by switch NI bridge +- dedicated HTTP "hello-world" servers are deployed for VLAN 20 and VLAN 30, + accessible exclusively through their respective VLAN networks + (HTTP servers are deployed in different segments but router is configured accordingly) + +Network topology diagram: + +```text + +-----+ +----------------+ + | EVE |--| VLAN 10 (mgmt) |----- + +-----+ +----------------+ | +------+ +---------+ + | ----| eth0 |......| p0 --+ | ++------+ +--------------+ +---------+ +-----+ | +------+ | | | +| app1 |--| NI1 (local) |--| VLAN 20 |--| LAG |--| | LAG | ++------+ +--------------+ +---------+ +-----+ | +------+ | | | VLAN20 +---------------+ + | ----| eth1 |......| p1 --+ |..........| httpserver-20 | ++------+ +--------------+ +---------+ | +------+ | | +---------------+ +| app2 |--| NI2 (switch) |--| VLAN 30 |----- | | ++------+ +--------------+ +---------+ | SWITCH | VLAN30 +---------------+ + | |..........| httpserver-30 | ++------+ +--------------+ +------+ | | +---------------+ +| app3 |----| NI3 (switch) |----------| eth2 |......| p2 | ++------+ +--------------+ +------+ | | + +---------+ +``` + +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/vlans-and-lags/device-config.json +./eden start --sdn-network-model $(pwd)/sdn/examples/vlans-and-lags/network-model.json +./eden eve onboard +./eden controller edge-node set-config --file $(pwd)/sdn/examples/vlans-and-lags/device-config.json +``` + +Note that VLAN IP subnets are `172.22..0/24`. Apps `app2` and `app3` will have IPs +from the subnet `172.22.30.0/24`. `app1` is connected to VLAN 20 indirectly and will use IP +from the local NI subnet (`10.50.0.0/24`). + +Check management interface IP: + +```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 +``` + +Check app IPs: + +```shell +./eden pod ps +NAME IMAGE UUID INTERNAL EXTERNAL MEMORY STATE(ADAM) LAST_STATE(EVE) +app1 lfedge/eden-eclient:8a279cd cee082fd-3a43-4599-bbd3-8216ffa8652d 10.50.0.2 - 177 MB/709 MB IN_CONFIG RUNNING +app2 lfedge/eden-eclient:8a279cd 45ff198d-b295-4ff2-bf69-76977af809fd 172.22.30.11 - 154 MB/732 MB IN_CONFIG RUNNING +app3 lfedge/eden-eclient:8a279cd 0c569673-988d-4d32-874c-2b09de12e0fc 172.22.30.12 - 155 MB/731 MB IN_CONFIG RUNNING +``` + +Check that `app1` can access HTTP server deployed for VLAN 20 (`10.20.20.70`), +but not HTTP server deployed for VLAN 30 (`10.30.30.70`): + +```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 10.20.20.70/helloworld +Hello world from HTTP server for VLAN 20 +app1$ curl 10.30.30.70/helloworld +curl: (7) Failed to connect to 10.30.30.70 port 80 after 1 ms: Couldn't connect to server +``` + +Check that `app2` and `app3` can access each other: + +```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$ ping -c 3 172.22.30.12 +PING 172.22.30.12 (172.22.30.12): 56 data bytes +64 bytes from 172.22.30.12: seq=0 ttl=64 time=45.818 ms +64 bytes from 172.22.30.12: seq=1 ttl=64 time=5.318 ms +64 bytes from 172.22.30.12: seq=2 ttl=64 time=4.895 ms + +--- 172.22.30.12 ping statistics --- +3 packets transmitted, 3 packets received, 0% packet loss +round-trip min/avg/max = 4.895/18.677/45.818 ms +``` + +Check that `app2` can access HTTP server deployed for VLAN 30 (`10.30.30.70`), +but not HTTP server deployed for VLAN 20 (`10.20.20.70`): + +```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 10.20.20.70/helloworld +curl: (7) Failed to connect to 10.20.20.70 port 80 after 3 ms: Couldn't connect to server +app2$ curl 10.30.30.70/helloworld +Hello world from HTTP server for VLAN 30 +``` + +Check that `app3` can access HTTP server deployed for VLAN 30 (`10.30.30.70`), +but not HTTP server deployed for VLAN 20 (`10.20.20.70`): + +```shell +./eden eve ssh +CONSOLE="$(eve list-app-consoles | grep 0c569673-988d-4d32-874c-2b09de12e0fc | grep CONTAINER | awk '{print $4}')" +eve attach-app-console "$CONSOLE" + +app3$ curl 10.20.20.70/helloworld +curl: (7) Failed to connect to 10.20.20.70 port 80 after 3 ms: Couldn't connect to server +app3$ curl 10.30.30.70/helloworld +Hello world from HTTP server for VLAN 30 +``` diff --git a/sdn/examples/vlans-and-lags/device-config.json b/sdn/examples/vlans-and-lags/device-config.json new file mode 100644 index 000000000..05dbc68b7 --- /dev/null +++ b/sdn/examples/vlans-and-lags/device-config.json @@ -0,0 +1,369 @@ +{ + "deviceIoList": [ + { + "ptype": 1, + "phylabel": "eth0", + "phyaddrs": { + "Ifname": "eth0" + }, + "logicallabel": "eth0", + "assigngrp": "eth0", + "usage": 1 + }, + { + "ptype": 1, + "phylabel": "eth1", + "phyaddrs": { + "Ifname": "eth1" + }, + "logicallabel": "eth1", + "assigngrp": "eth1", + "usage": 1 + }, + { + "ptype": 1, + "phylabel": "eth2", + "phyaddrs": { + "Ifname": "eth2" + }, + "logicallabel": "eth2", + "assigngrp": "eth2", + "usage": 2 + } + ], + "bonds": [ + { + "logicallabel": "bond-eth0-eth1", + "interfaceName": "bond", + "lowerLayerNames": ["eth0", "eth1"], + "bondMode": 1 + } + ], + "vlans": [ + { + "logicallabel": "vlan-10", + "interfaceName": "vlan10", + "lowerLayerName": "bond-eth0-eth1", + "vlanId": 10 + }, + { + "logicallabel": "vlan-20", + "interfaceName": "vlan20", + "lowerLayerName": "bond-eth0-eth1", + "vlanId": 20 + }, + { + "logicallabel": "vlan-30", + "interfaceName": "vlan30", + "lowerLayerName": "bond-eth0-eth1", + "vlanId": 30 + } + ], + "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": "vlan-10", + "uplink": true, + "networkUUID": "6605d17b-3273-4108-8e6e-4965441ebe01" + }, + { + "name": "vlan-20", + "networkUUID": "6605d17b-3273-4108-8e6e-4965441ebe01" + }, + { + "name": "vlan-30", + "networkUUID": "b970ac70-2ef7-4c6b-8bb8-ff8626321313" + }, + { + "name": "eth2", + "networkUUID": "b970ac70-2ef7-4c6b-8bb8-ff8626321313" + } + ], + "networkInstances": [ + { + "uuidandversion": { + "uuid": "9ca83da9-94e8-48b4-9ae8-3f188c5c694a", + "version": "1" + }, + "displayname": "ni1", + "instType": 2, + "activate": true, + "port": { + "type": 1, + "name": "vlan-20" + }, + "ipType": 1, + "ip": { + "subnet": "10.50.0.0/24", + "gateway": "10.50.0.1", + "dns": [ + "10.50.0.1" + ], + "dhcpRange": { + "start": "10.50.0.2", + "end": "10.50.0.254" + } + } + }, + { + "uuidandversion": { + "uuid": "dfb79e0e-ebaf-40e7-93a5-e1267a366416", + "version": "1" + }, + "displayname": "ni2", + "instType": 1, + "activate": true, + "port": { + "type": 1, + "name": "vlan-30" + }, + "ipType": 1 + }, + { + "uuidandversion": { + "uuid": "c4475613-73bc-4556-83c2-0b82751262be", + "version": "1" + }, + "displayname": "ni3", + "instType": 1, + "activate": true, + "port": { + "type": 1, + "name": "eth2" + }, + "ipType": 1 + } + ], + "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": "dfb79e0e-ebaf-40e7-93a5-e1267a366416", + "acls": [ + { + "matches": [ + { + "type": "ip", + "value": "0.0.0.0/0" + } + ], + "id": 1 + } + ] + } + ], + "volumeRefList": [ + { + "uuid": "cee944a3-ae6f-4887-9d8d-adcc0ed02370", + "mount_dir": "/" + } + ] + }, + { + "uuidandversion": { + "uuid": "0c569673-988d-4d32-874c-2b09de12e0fc", + "version": "1" + }, + "displayname": "app3", + "fixedresources": { + "memory": 512000, + "maxmem": 512000, + "vcpus": 1, + "virtualizationMode": 1 + }, + "drives": [ + { + "image": { + "uuidandversion": { + "uuid": "0579daf7-c6d0-480e-a459-bc845fd94dba", + "version": "1" + }, + "name": "lfedge/eden-eclient:8a279cd", + "iformat": 8, + "dsId": "f204830d-cce1-4316-aa5e-3e8567cd09a9" + } + } + ], + "activate": true, + "interfaces": [ + { + "name": "eth0", + "networkId": "c4475613-73bc-4556-83c2-0b82751262be", + "accessVlanId": 30, + "acls": [ + { + "matches": [ + { + "type": "ip", + "value": "0.0.0.0/0" + } + ], + "id": 1 + } + ] + } + ], + "volumeRefList": [ + { + "uuid": "5605093b-c7cf-4beb-bb6b-14d86d39c42b", + "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" + }, + { + "uuid": "5605093b-c7cf-4beb-bb6b-14d86d39c42b", + "origin": { + "type": 2, + "downloadContentTreeID": "63d3b01f-f44f-4007-ba33-6e720bd52992" + }, + "displayName": "app3-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/vlans-and-lags/network-model.json b/sdn/examples/vlans-and-lags/network-model.json new file mode 100644 index 000000000..f0812d9b4 --- /dev/null +++ b/sdn/examples/vlans-and-lags/network-model.json @@ -0,0 +1,140 @@ +{ + "ports": [ + { + "logicalLabel": "eveport0", + "adminUP": true + }, + { + "logicalLabel": "eveport1", + "adminUP": true + }, + { + "logicalLabel": "eveport2", + "adminUP": true + } + ], + "bonds": [ + { + "logicalLabel": "bond0", + "ports": ["eveport0", "eveport1"], + "mode": "balance-rr" + } + ], + "bridges": [ + { + "logicalLabel": "bridge0", + "bonds": ["bond0"], + "ports": ["eveport2"] + } + ], + "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"] + } + }, + { + "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-30", + "bridge": "bridge0", + "vlanID": 30, + "subnet": "172.22.30.0/24", + "gwIP": "172.22.30.1", + "dhcp": { + "enable": true, + "ipRange": { + "fromIP": "172.22.30.10", + "toIP": "172.22.30.20" + }, + "domainName": "sdn", + "privateDNS": ["dns-server"] + }, + "router": { + "outsideReachability": false, + "reachableEndpoints": ["dns-server", "httpserver-30"] + } + } + ], + "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" + } + ], + "upstreamServers": [ + "1.1.1.1", + "8.8.8.8" + ] + } + ], + "httpServers": [ + { + "logicalLabel": "httpserver-20", + "fqdn": "httpserver-20.sdn", + "subnet": "10.20.20.0/24", + "ip": "10.20.20.70", + "httpPort": 80, + "paths": { + "/helloworld": { + "contentType": "text/plain", + "content": "Hello world from HTTP server for VLAN 20\n" + } + } + }, + { + "logicalLabel": "httpserver-30", + "fqdn": "httpserver-30.sdn", + "subnet": "10.30.30.0/24", + "ip": "10.30.30.70", + "httpPort": 80, + "paths": { + "/helloworld": { + "contentType": "text/plain", + "content": "Hello world from HTTP server for VLAN 30\n" + } + } + } + ] + } +} \ No newline at end of file diff --git a/sdn/vm/pkg/configitems/bridge.go b/sdn/vm/pkg/configitems/bridge.go index b5068b373..50f453ace 100644 --- a/sdn/vm/pkg/configitems/bridge.go +++ b/sdn/vm/pkg/configitems/bridge.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "os/exec" "reflect" "github.com/lf-edge/eden/sdn/vm/pkg/maclookup" @@ -226,9 +227,18 @@ func (c *BridgeConfigurator) handleModify(oldBridgeCfg, newBridgeCfg Bridge) (er // Update VLAN filering. vlanFiltering := len(newBridgeCfg.VLANs) > 0 if *bridge.VlanFiltering != vlanFiltering { - if err := netlink.BridgeSetVlanFiltering(bridge, vlanFiltering); err != nil { - err = fmt.Errorf("failed to set VLAN filtering to %t for bridge %s: %v", - vlanFiltering, ifName, err) + // netlink.BridgeSetVlanFiltering seems to be broken, it keeps returning EBUSY. + // Let's use ip command instead. + val := "0" + if vlanFiltering { + val = "1" + } + args := []string{"link", "set", "dev", ifName, "type", "bridge", + "vlan_filtering", val} + output, err := exec.Command("ip", args...).CombinedOutput() + if err != nil { + err = fmt.Errorf("failed to set VLAN filtering to %t for bridge %s: %s", + vlanFiltering, ifName, output) log.Error(err) return err }