Add ability to set static routes

add routes using the --route flag.
the no_default_route option in --opt prevents a default route from
getting added automatically.

Signed-off-by: Jan Hendrik Farr <github@jfarr.cc>
This commit is contained in:
Jan Hendrik Farr 2023-05-25 01:05:47 +00:00 committed by Paul Holzinger
parent 68183b07d6
commit 8bf168cc13
No known key found for this signature in database
GPG Key ID: EB145DD938A3CAF2
6 changed files with 286 additions and 1 deletions

View File

@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/libnetwork/util"
@ -77,6 +79,10 @@ func networkCreateFlags(cmd *cobra.Command) {
flags.StringArrayVar(&networkCreateOptions.Subnets, subnetFlagName, nil, "subnets in CIDR format")
_ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone)
routeFlagName := "route"
flags.StringArrayVar(&networkCreateOptions.Routes, routeFlagName, nil, "static routes")
_ = cmd.RegisterFlagCompletionFunc(routeFlagName, completion.AutocompleteNone)
interfaceFlagName := "interface-name"
flags.StringVar(&networkCreateOptions.InterfaceName, interfaceFlagName, "", "interface name which is used by the driver")
_ = cmd.RegisterFlagCompletionFunc(interfaceFlagName, completion.AutocompleteNone)
@ -176,6 +182,16 @@ func networkCreate(cmd *cobra.Command, args []string) error {
return errors.New("cannot set gateway or range without subnet")
}
for i := range networkCreateOptions.Routes {
route, err := parseRoute(networkCreateOptions.Routes[i])
if err != nil {
return err
}
network.Routes = append(network.Routes, *route)
}
extraCreateOptions := types.NetworkCreateOptions{
IgnoreIfExists: networkCreateOptions.IgnoreIfExists,
}
@ -188,6 +204,46 @@ func networkCreate(cmd *cobra.Command, args []string) error {
return nil
}
func parseRoute(routeStr string) (*types.Route, error) {
s := strings.Split(routeStr, ",")
var metric *uint32
if len(s) == 2 || len(s) == 3 {
dstStr := s[0]
gwStr := s[1]
destination, err := types.ParseCIDR(dstStr)
gateway := net.ParseIP(gwStr)
if err != nil {
return nil, fmt.Errorf("invalid route destination %s", dstStr)
}
if gateway == nil {
return nil, fmt.Errorf("invalid route gateway %s", gwStr)
}
if len(s) == 3 {
mtr, err := strconv.ParseUint(s[2], 10, 32)
if err != nil {
return nil, fmt.Errorf("invalid route metric %s", s[2])
}
x := uint32(mtr)
metric = &x
}
r := types.Route{
Destination: destination,
Gateway: gateway,
Metric: metric,
}
return &r, nil
}
return nil, fmt.Errorf("invalid route: %s\nFormat: --route <destination in CIDR>,<gateway>,<metric (optional)>", routeStr)
}
func parseRange(iprange string) (*types.LeaseRange, error) {
_, subnet, err := net.ParseCIDR(iprange)
if err != nil {

View File

@ -95,10 +95,12 @@ Set metadata for a network (e.g., --label mykey=value).
Set driver specific options.
All drivers accept the `mtu` and `metric` options.
All drivers accept the `mtu`, `metric`, `no_default_route` and options.
- `mtu`: Sets the Maximum Transmission Unit (MTU) and takes an integer value.
- `metric` Sets the Route Metric for the default route created in every container joined to this network. Accepts a positive integer value. Can only be used with the Netavark network backend.
- `no_default_route`: If set to 1, Podman will not automatically add a default route to subnets. Routes can still be added
manually by creating a custom route using `--route`.
Additionally the `bridge` driver supports the following options:
@ -114,6 +116,10 @@ The `macvlan` and `ipvlan` driver support the following options:
- Supported values for `macvlan` are `bridge`, `private`, `vepa`, `passthru`. Defaults to `bridge`.
- Supported values for `ipvlan` are `l2`, `l3`, `l3s`. Defaults to `l2`.
#### **--route**=*route*
A static route in the format <destination in CIDR notation>,<gateway>,<route metric (optional)>. This route will be added to every container in this network. Only available with the netavark backend. It can be specified multiple times if more than one static route is desired.
#### **--subnet**=*subnet*
The subnet in CIDR notation. Can be specified multiple times to allocate more than one subnet for this network.
@ -158,6 +164,17 @@ $ podman network create --subnet 192.168.55.0/24 --gateway 192.168.55.3 --subnet
podman4
```
Create a network with a static subnet and a static route.
```
$ podman network create --subnet 192.168.33.0/24 --route 10.1.0.0/24,192.168.33.10 newnet
```
Create a network with a static subnet and a static route without a default
route.
```
$ podman network create --subnet 192.168.33.0/24 --route 10.1.0.0/24,192.168.33.10 --opt no_default_route=1 newnet
```
Create a Macvlan based network using the host interface eth0. Macvlan networks can only be used as root.
```
$ sudo podman network create -d macvlan -o parent=eth0 --subnet 192.5.0.0/16 newnet

View File

@ -28,6 +28,7 @@ Pretty-print networks to JSON or using a Go template.
| .NetworkDNSServers | Array of DNS servers used in this network |
| .NetworkInterface | Name of the network interface on the host |
| .Options | Network options |
| .Routes | List of static routes for this network |
| .Subnets | List of subnets on this network |
## EXAMPLE

View File

@ -56,6 +56,7 @@ Valid placeholders for the Go template are listed below:
| .NetworkDNSServers | Array of DNS servers used in this network |
| .NetworkInterface | Name of the network interface on the host |
| .Options | Network options |
| .Routes | List of static routes for this network |
| .Subnets | List of subnets on this network |
#### **--no-trunc**

View File

@ -50,6 +50,7 @@ type NetworkCreateOptions struct {
NetworkDNSServers []string
Ranges []string
Subnets []string
Routes []string
IPv6 bool
// Mapping of driver options and values.
Options map[string]string

View File

@ -62,6 +62,215 @@ var _ = Describe("Podman network create", func() {
Expect(subnet.Contains(containerIP)).To(BeTrue(), "subnet contains containerIP")
})
It("podman network create with name and subnet and static route", func() {
SkipIfCNI(podmanTest)
netName := "subnet-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{
"network",
"create",
"--subnet",
"10.19.12.0/24",
"--route",
"10.21.0.0/24,10.19.12.250",
netName,
})
nc.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
Expect(nc).Should(Exit(0))
// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", netName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
// JSON the network configuration into something usable
var results []types.Network
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result).To(HaveField("Name", netName))
Expect(result.Subnets).To(HaveLen(1))
Expect(result.Subnets[0].Subnet.String()).To(Equal("10.19.12.0/24"))
Expect(result.Subnets[0].Gateway.String()).To(Equal("10.19.12.1"))
Expect(result.Routes[0].Destination.String()).To(Equal("10.21.0.0/24"))
Expect(result.Routes[0].Gateway.String()).To(Equal("10.19.12.250"))
Expect(result.Routes[0].Metric).To(BeNil())
// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(result.NetworkInterface)
})
It("podman network create with name and subnet and static route and metric", func() {
SkipIfCNI(podmanTest)
netName := "subnet-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{
"network",
"create",
"--subnet",
"10.19.13.0/24",
"--route",
"10.21.1.0/24,10.19.13.250,120",
netName,
})
nc.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
Expect(nc).Should(Exit(0))
// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", netName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
// JSON the network configuration into something usable
var results []types.Network
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result).To(HaveField("Name", netName))
Expect(result.Subnets).To(HaveLen(1))
Expect(result.Subnets[0].Subnet.String()).To(Equal("10.19.13.0/24"))
Expect(result.Subnets[0].Gateway.String()).To(Equal("10.19.13.1"))
Expect(result.Routes[0].Destination.String()).To(Equal("10.21.1.0/24"))
Expect(result.Routes[0].Gateway.String()).To(Equal("10.19.13.250"))
Expect(*result.Routes[0].Metric).To(Equal(uint32(120)))
// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(result.NetworkInterface)
})
It("podman network create with name and subnet and two static routes", func() {
SkipIfCNI(podmanTest)
netName := "subnet-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{
"network",
"create",
"--subnet",
"10.19.14.0/24",
"--route",
"10.21.2.0/24,10.19.14.250",
"--route",
"10.21.3.0/24,10.19.14.251,120",
netName,
})
nc.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
Expect(nc).Should(Exit(0))
// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", netName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
// JSON the network configuration into something usable
var results []types.Network
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result).To(HaveField("Name", netName))
Expect(result.Subnets).To(HaveLen(1))
Expect(result.Subnets[0].Subnet.String()).To(Equal("10.19.14.0/24"))
Expect(result.Subnets[0].Gateway.String()).To(Equal("10.19.14.1"))
Expect(result.Routes).To(HaveLen(2))
Expect(result.Routes[0].Destination.String()).To(Equal("10.21.2.0/24"))
Expect(result.Routes[0].Gateway.String()).To(Equal("10.19.14.250"))
Expect(result.Routes[0].Metric).To(BeNil())
Expect(result.Routes[1].Destination.String()).To(Equal("10.21.3.0/24"))
Expect(result.Routes[1].Gateway.String()).To(Equal("10.19.14.251"))
Expect(*result.Routes[1].Metric).To(Equal(uint32(120)))
// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(result.NetworkInterface)
})
It("podman network create with name and subnet and static route (ipv6)", func() {
SkipIfCNI(podmanTest)
netName := "subnet-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{
"network",
"create",
"--subnet",
"fd:ab04::/64",
"--route",
"fd:1::/64,fd::1,120",
netName,
})
nc.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
Expect(nc).Should(Exit(0))
// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", netName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
// JSON the network configuration into something usable
var results []types.Network
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result).To(HaveField("Name", netName))
Expect(result.Subnets).To(HaveLen(1))
Expect(result.Subnets[0].Subnet.String()).To(Equal("fd:ab04::/64"))
Expect(result.Subnets[0].Gateway.String()).To(Equal("fd:ab04::1"))
Expect(result.Routes[0].Destination.String()).To(Equal("fd:1::/64"))
Expect(result.Routes[0].Gateway.String()).To(Equal("fd::1"))
Expect(*result.Routes[0].Metric).To(Equal(uint32(120)))
// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(result.NetworkInterface)
})
It("podman network create with name and subnet with --opt no_default_route=1", func() {
SkipIfCNI(podmanTest)
netName := "subnet-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{
"network",
"create",
"--subnet",
"10.19.15.0/24",
"--opt",
"no_default_route=1",
netName,
})
nc.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(netName)
Expect(nc).Should(Exit(0))
// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", netName})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
// JSON the network configuration into something usable
var results []types.Network
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result).To(HaveField("Name", netName))
Expect(result.Subnets).To(HaveLen(1))
Expect(result.Subnets[0].Subnet.String()).To(Equal("10.19.15.0/24"))
Expect(result.Subnets[0].Gateway.String()).To(Equal("10.19.15.1"))
Expect(result.Options[types.NoDefaultRoute]).To(Equal("true"))
// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(result.NetworkInterface)
})
It("podman network create with name and IPv6 subnet", func() {
netName := "ipv6-" + stringid.GenerateRandomID()
nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:1:2:3:4::/64", netName})