Network Service Mesh
A Narrative Introduction
Frederick Kautz and Kyle Mestery
Sarah
and...
secure-intranet-connectivity
Meet Sarah
Sarah is writing a Kubernetes app to be deployed in the public cloud
One of the Pods in Sarah’s app needs secure access to her corporate intranet
Corporate Intranet
From Sarah’s point of view her needs look like this
Sarah’s Pod
L2/L3 connection
Security goes here...
K8s interface
Corporate Intranet
Sarah’s Pod
L2/L3 connection
K8s interface
Security goes here...
Sarah still wants her normal Kubernetes Networking...
Corporate Intranet
Sarah’s Pod
L2/L3 connection
Security goes here...
But she also wants to send and receive traffic to her corporate intranet ...
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
net interface
VPN Gateway Pod
net interface
Corporate Intranet
VPN Concentrator
How do I find out what subnet this connects to?
Sarah
K8s net interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
Wait, who defines the subnet?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
What if the subnet is too small in the future?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
I have to define the interface on this end too?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
And insert routes for all my corporate IPs into *my* Pod?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
I need more replica’s, the subnet is to small now. I have to do this all over again!
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
What if the subnet changes? Do I have to change all this stuff too?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
Arrrg… my Enterprise Network guys say I chose a subnet that is incompatible with the corporate intranet. I have to redo all of this again?
Sarah
K8s interface
Sarah’s definition of hell...
Sarah’s Pod
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
Wait… my network guys re-IPed *something* in the intranet and now my subnet is incompatible again… this is hell!
Sarah
K8s interface
Then Sarah’s IT guys decide ‘secure’ also includes a Firewall Pod
Sarah’s Pod
K8s interface
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
Firewall Pod
Do I need a new subnet for this? Why am I doing network design with 1990s concepts?
Sarah
Sarah’s definition of hell...
Sarah’s Pod
K8s interface
Subnet
interface
VPN Gateway Pod
interface
Corporate Intranet
VPN Concentrator
Corporate added a new CIDR to the corporate network. Time to update the routes… why God! Why?
Sarah
Maybe I can help?
Sarah
Sarah
Who are you?
I’m the Ariadne the NSM (Network Service Mesh) Spider
Sarah
Sort of. You know all the cool things Istio does for you with TCP connections and HTTP?
What is Network Service Mesh? Is that like Istio?
Sarah
I do that for IP, Ethernet, and other L2/L3 protocols. Tell me about your problems.
Yeah, it's awesome!
Sarah
I have a Pod and I just want to securely connect it to my corporate intranet.
Corporate Intranet
Sarah’s Pod
L2/L3 connection
K8s interface
Security goes here...
Sarah
Sarah
Cool, this is how you would do it with NSM.
kind: NetworkService
apiVersion: V1
metadata:
name: secure-intranet-connectivity
spec:
selector:
app: secure-intranet-gateway payload: IP
Sarah
Yep! You use selectors on Pods to find the Pods providing the Network Service, and it exposes ‘payloads’ instead of ports and tcp.
kind: NetworkService
apiVersion: V1
metadata:
name: secure-intranet-connectivity
spec:
selector:
app: secure-intranet-gateway payload: IP
That looks a lot like a Service Resource in K8s.
Sarah
Close! You would also want to insert our standard Network Service Mesh init-container and a Config Map telling it what Network Service you want to connect to into your Pod. That’s it.
kind: NetworkService
apiVersion: V1
metadata:
name: secure-intranet-connectivity
spec:
selector:
app: secure-intranet-gateway payload: IP
So I just need this and a Deployment for the VPN-GW Pod?
Sarah
kind: NetworkService
apiVersion: V1
metadata:
name: secure-intranet-connectivity
spec:
selector:
app: secure-intranet-gateway payload: IP
No interfaces? No subnets? No routes? How does all this magic work?
Sarah
Network Service
Network Service Mesh has three basic concepts. You’ve already met the first: Network Service (NS).
It’s something you send L2/L3 payloads to and from and it does something you want.
Sarah
Exactly!
Network Service
Example: secure-intranet-connectivity
Like giving me secure connectivity to my intranet?
Sarah
The Second concept is a Network Service Endpoint (NSE).
It’s a Pod that is providing the Network Service you want.
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Sarah
Very much. We tried to use familiar concepts in Network Service Mesh.
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Is that like Endpoints for Services?
Sarah
You’re getting it! :)
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Example: VPN Gateway Pod
So the VPN Gateway Pod is a Network Service Endpoint?
Sarah
The Third concept is the L2/L3 ‘connection’ between your Pod and the NSE.
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Example: VPN Gateway Pod
Sarah’s Pod
L2/L3 connection
Sarah
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Example: VPN Gateway Pod
Sarah’s Pod
L2/L3 connection
Is that an interface?
Usually it is instantiated as a kernel interface in your Pod.
There are NSM users with more complex use cases who want more exotic things like memif or vhost user, and we can give that to them.
You probably want a kernel interface.
Sarah
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Example: VPN Gateway Pod
Sarah’s Pod
L2/L3 connection
What about subnets?
L2/L3 connections are point to point cross connects between your Pod, and the Network Service you want. You don’t have to think about subnets.
If you want a Bridge Domain, that’s a Network Service itself.
Sarah
Generally, addresses and routes for an L2/L3 connection come from the Network Service Endpoint, like your VPN Gateway. They are in a better position to know what they should be.
Advanced use cases can be done with more flexibility, but that’s probably not what you want here.
Network Service
Example: secure-intranet-connectivity
Network Service Endpoint
Example: VPN Gateway Pod
Sarah’s Pod
L2/L3 connection
And routes? My network guys added an intranet CIDR last month and I had to update the routes on *all* of my Pods. It sucked!
Sarah
Network Service Mesh is a Mesh.
The Firewall Pod and the VPN Gateway Pod work together to give you the Network Service you want:
secure-intranet-connectivity
Network Service
secure-intranet-connectivity
Firewall
Pod
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
L2/L3 connection
What about the new Firewall Pod they want to get traffic before the VPN Gateway Pod?
Sarah
You know VirtualHosts/RouteRules in Istio?
Network Service
secure-intranet-connectivity
Firewall
Pod
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
L2/L3 connection
How does that work?
Sarah
Network Service Mesh has an analogous concept, we call them:
�Network Service Wirings
Network Service
secure-intranet-connectivity
Firewall
Pod
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
L2/L3 connection
Yeah, they are super useful.
Sarah
The ‘secure-intranet-connectivity-wiring-1’ Network Service Wiring says:
‘target: secure-intranet-connectivity’
That means it applies to L2/L3 connections trying to reach the secure-intranet-connectivity Network Service.
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-1
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
sourceService:
!secure-intranet-connectivity
action:
route:
-destination:
podSelector:
firewall=true
Sarah
The ‘secure-intranet-connectivity-wiring-1’ Network Service Wiring then has a qualifier that only matches things not providing the secure-intranet-connectivity service themselves.
Things like your Pod.
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-1
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
sourceService:
!secure-intranet-connectivity
action:
route:
-destination:
podSelector:
firewall=true
Sarah
And it has a ‘route’ that sends those connections to one of the Network Service Endpoints with label ‘firewall=true’... like the Firewall Pod.
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-1
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
sourceService:
!secure-intranet-connectivity
action:
route:
-destination:
podSelector:
firewall=true
Sarah
When your Pod tries to connect to the secure-intranet-connectivity Network Service, it matches the Network Service Wiring:
secure-intranet-connectivity-1
Network Service
secure-intranet-connectivity
Firewall
Pod
firewall=true
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
vpngateway=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-1
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
sourceService:
!secure-intranet-connectivity
action:
route:
-destination:
podSelector:
firewall=true
Sarah
Network Service Wiring
secure-intranet-connectivity-wiring-1
Causes your connection to be routed to the Firewall Pod
Network Service
secure-intranet-connectivity
Firewall
Pod
firewall=true
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
vpngateway=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-1
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
sourceService:
!secure-intranet-connectivity
action:
route:
-destination:
podSelector:
firewall=true
Sarah
You would have a second Network Service Wiring ‘secure-intranet-connectivity-wiring-2’
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-2
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
podSelector:
firewall=true
action:
route:
-destination:
podSelector:
vpngateway=true
OK. How does the Firewall Pod get connected to the VPN Gateway Pod?
Sarah
That has a qualifier that matches sources that have label firewall=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-2
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
podSelector:
firewall=true
action:
route:
-destination:
podSelector:
vpngateway=true
Sarah
And routes them to Pods with vpngateway=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-2
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
podSelector:
firewall=true
action:
route:
-destination:
podSelector:
vpngateway=true
Sarah
The Firewall Pod does its Firewall thing, but doesn’t know anything about how to connect to your intranet. So it opens a connection to secure-intranet-connectivity itself.
The Firewall Pod’s connection matches Network Service Wiring:
secure-intranet-connectivity-wiring-2
Network Service
secure-intranet-connectivity
Firewall
Pod
firewall=true
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
vpngateway=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-2
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
podSelector:
firewall=true
action:
route:
-destination:
podSelector:
vpngateway=true
L2/L3 connection
Sarah
Network Service Wiring
secure-intranet-connectivity-2
causes the Firewall Pod’s connection to be routed to the VPN Gateway Pod
Network Service
secure-intranet-connectivity
Firewall
Pod
firewall=true
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
vpngateway=true
kind: NetworkServiceWiring
apiVersion: V1
metadata:
name: secure-intranet-connectivity-wiring-2
spec:
target:
- secure-intranet-connectivity
qualifiers:
source:
podSelector:
firewall=true
action:
route:
-destination:
podSelector:
vpngateway=true
L2/L3 connection
Sarah
Yep!
Network Service
secure-intranet-connectivity
Firewall
Pod
Sarah’s Pod
L2/L3 connection
VPN Gateway
Pod
L2/L3 connection
So when IT decides to put something else in there for more security, I just add a Deployment for that and a couple of Network Service Wiring Resources?
Sarah
Nope!
No interfaces, no IPs, no subnets, no routes?
IPs
Subnets
Routes
Interfaces
Sarah
Network Service Mesh is completely orthogonal to normal Kubernetes Networking. It doesn’t use CNI, and you can use it with your existing Kubernetes and CNI.
Do I need a new version of Kubernetes?
Do I have to use a specific CNI plugin?
K8s upgrades
Special CNI Plugin
Sarah
You know how Istio Service Mesh’s are often described as handling things like Service Routing etc?
That’s awesome, how does it work?
Computer A
Service A
Business Logic
Networking Stack
Connection Protocol Stack
Sidecar
Service Discovery/Routing
Computer B
Networking Stack
Connection Protocol Stack
Sidecar
Service Discovery/Routing
Service B
Business Logic
Sarah
Network Service Mesh has something like the Envoy Proxy that does the Service Discovery and Routing too. It’s called the:
Network Service Manager (NSM)
It runs as a DaemonSet so you have one on each Node.
Yep! I’ve read Phil Calçado ‘s paper.
Node
NSM
Dataplane (kernel/vswitch)
Sarah
The NSM InitContainer reads your Config Map, and sends a GRPC call across a unix file socket to the NSM to Request an L2/L3 Connection to the secure-intranet-connectivity Network Service.
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
Sarah
Request Connection has any information needed to be clear about how you want the connection to look to your Pod locally, like that you want it to be a kernel interface, the interface name you’d prefer if you care, etc.
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
Sarah
Let’s talk first about the case where your VPN Gateway Pod is on the same Node.
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
VPN Gateway Pod (NSE)
1. Request Connection
Sarah
The NSM sends a Request Connection to the VPN Gateway Pod, which sends back an Accept.L2/L3 connection
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
Sarah
The NSM creates an interface for the connection for the VPN Gateway and injects it into the VPN Gateway Pod.3 connection
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
Sarah
The NSM creates and injects an interface into Sarah’s Pod
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
5.Create & Inject Interface
Sarah
The NSM then cross connects the two interfaces in the dataplane. connection
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
5.Create & Inject Interface
6 .Cross connect
cross-connect
Sarah
Finally the NSM responds to your Pod’s NSMInitContainer with an Accept.
Then you are ready to go.
ThTheconne
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
5.Create & Inject Interface
6 .Cross connect
cross-connect
7. Accept
Sarah
Yep.
For L2/L3 connections we don’t have a universal mechanism like TCP in the kernel, so we have the NSM do it.
ThTheconne
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
5.Create & Inject Interface
6 .Cross connect
cross-connect
7. Accept
So the NSM does Service Discovery and sets up the connection?
Sarah
Node
NSM
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Request Connection
VPN Gateway Pod (NSE)
2. Req Con
3. Accept
4.Create & Inject Interface
5.Create & Inject Interface
6 .Cross connect
cross-connect
What if the VPN Gateway Pod is on a different Node.
7. Accept
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
If the VPN Gateway Pod is on a different Node, it looks exactly the same to your Pod.
You send a Request Connection request to NSM using GRPC over a unix file socket.
Node2
NSM2
Dataplane (kernel/vswitch)
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
If the VPN Gateway Pod is on a different Node, it looks exactly the same to your Pod.
You send a Request Connection request to NSM using GRPC over a unix file socket.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM1 then looks up (or more likely caches) NetworkServiceEndpoints and NetworkServiceWiring from the K8s API Server and selects a Network Service Endpoint for the secure-intranet-connectivity Network Service.
From that NetworkServiceEndpoint resource it learns how to reach NSM2.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM1 sends a Request Connection GRPC call to NSM2, listing out what its preferences are for tunnel mechanism (VXLAN, GRE, etc) and its preferences for parameters (VNI, etc.)
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM2 sends a Request Connection to the VPN Gateway which sends back an Accept.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
5. Accept
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM2 creates and injects an interface into the VPN Gateway Pod for the connection.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
5. Accept
6.Create & Inject Interface
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM2 sets up its end of the tunnel.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
5. Accept
6.Create & Inject Interface
tunnel
7.Create tunnel
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM2 send an Accept back to NSM1 with the selected tunnel mechanism and tunnel parameters.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
8. Accept
6.Create & Inject Interface
tunnel
7.Create tunnel
5. Accept
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM1 creates and injects an interface for the connection into Sarah’s Pod.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
8. Accept
6.Create & Inject Interface
tunnel
9.Create & Inject Interface
5. Accept
7.Create tunnel
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
NSM1 creates its end of the tunnel and completes the cross connect.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
8. Accept
6.Create & Inject Interface
9.Create & Inject Interface
10.Create tunnel
7.Create tunnel
5. Accept
tunnel
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
1. Req Con
VPN Gateway Pod (NSE)
Finally NSM1 sends an Accept back to the InitContainer your Pod.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
2. Select based on
NetworkServiceEndpoint
NetworkServiceWiring
3. Req Con
4. Req Con
8. Accept
6.Create & Inject Interface
9.Create & Inject Interface
11. Accept
5. Accept
7.Create tunnel
tunnel
10.Create tunnel
Sarah
Nope, let's talk through that.
How do the NetworkEndpoint Resources get into the API Server? Do I have to put them there?
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
VPN Gateway Pod (NSE)
When the VPN Gateway Pod comes up, it Exposes to the NSM1 that it has a secure-intranet-connectivity Network Service willing to accept connections.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
1. Expose Service
Sarah
Node1
NSM1
Dataplane (kernel/vswitch)
Sarah’s
Pod
NSM InitContainer
VPN Gateway Pod (NSE)
Then the NSM creates a NetworkServiceEndpoint CRD for it in the K8s API server.
Node2
NSM2
Dataplane (kernel/vswitch)
K8s API Server
1. Expose Service
2. Create
NetworkServiceEndpoint
Sarah
You are welcome! Have fun!
Cool! This looks so much easier than trying to manually string together interfaces and subnets myself! Thank you!
Thank you!