diff --git a/common/libnetwork/internal/util/validate.go b/common/libnetwork/internal/util/validate.go index 011a101ac6..adf04dd726 100644 --- a/common/libnetwork/internal/util/validate.go +++ b/common/libnetwork/internal/util/validate.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "net" - "strings" - "unicode" "go.podman.io/common/libnetwork/types" "go.podman.io/common/libnetwork/util" @@ -98,43 +96,6 @@ func ValidateSubnets(network *types.Network, addGateway bool, usedNetworks []*ne return nil } -func ValidateRoutes(routes []types.Route) error { - for _, route := range routes { - err := ValidateRoute(route) - if err != nil { - return err - } - } - return nil -} - -func ValidateRoute(route types.Route) error { - if route.Destination.IP == nil { - return errors.New("route destination ip nil") - } - - if route.Destination.Mask == nil { - return errors.New("route destination mask nil") - } - - if route.Gateway == nil { - return errors.New("route gateway nil") - } - - // Reparse to ensure destination is valid. - ip, ipNet, err := net.ParseCIDR(route.Destination.String()) - if err != nil { - return fmt.Errorf("route destination invalid: %w", err) - } - - // check that destination is a network and not an address - if !ip.Equal(ipNet.IP) { - return errors.New("route destination invalid") - } - - return nil -} - func ValidateSetupOptions(n NetUtil, namespacePath string, options types.SetupOptions) error { if namespacePath == "" { return errors.New("namespacePath is empty") @@ -176,23 +137,3 @@ func validatePerNetworkOpts(network *types.Network, netOpts *types.PerNetworkOpt } return nil } - -// ValidateInterfaceName validates the interface name based on the following rules: -// 1. The name must be less than MaxInterfaceNameLength characters -// 2. The name must not be "." or ".." -// 3. The name must not contain / or : or any whitespace characters -// ref to https://github.com/torvalds/linux/blob/81e4f8d68c66da301bb881862735bd74c6241a19/include/uapi/linux/if.h#L33C18-L33C20 -func ValidateInterfaceName(ifName string) error { - if len(ifName) > types.MaxInterfaceNameLength { - return fmt.Errorf("interface name is too long: interface names must be %d characters or less: %w", types.MaxInterfaceNameLength, types.ErrInvalidArg) - } - if ifName == "." || ifName == ".." { - return fmt.Errorf("interface name is . or ..: %w", types.ErrInvalidArg) - } - if strings.ContainsFunc(ifName, func(r rune) bool { - return r == '/' || r == ':' || unicode.IsSpace(r) - }) { - return fmt.Errorf("interface name contains / or : or whitespace characters: %w", types.ErrInvalidArg) - } - return nil -} diff --git a/common/libnetwork/netavark/config_test.go b/common/libnetwork/netavark/config_test.go index 45e301ccda..e26396319e 100644 --- a/common/libnetwork/netavark/config_test.go +++ b/common/libnetwork/netavark/config_test.go @@ -1817,59 +1817,6 @@ var _ = Describe("Config", func() { Expect(err.Error()).To(ContainSubstring("failed to load network create: IO error: invalid IP address syntax")) }) - It("create bridge config with invalid static route (gw = \"foo\")", func() { - dest := "10.1.0.0/24" - gw := "foo" - d, _ := types.ParseCIDR(dest) - g := net.ParseIP(gw) - network := types.Network{ - Driver: "bridge", - Routes: []types.Route{ - {Destination: d, Gateway: g}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) - }) - - It("create macvlan config with invalid static route (gw = \"foo\")", func() { - dest := "10.1.0.0/24" - gw := "foo" - d, _ := types.ParseCIDR(dest) - g := net.ParseIP(gw) - network := types.Network{ - Driver: "macvlan", - Routes: []types.Route{ - {Destination: d, Gateway: g}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) - }) - - It("create ipvlan config with invalid static route (gw = \"foo\")", func() { - subnet := "10.1.0.0/24" - n, _ := types.ParseCIDR(subnet) - dest := "10.1.0.0/24" - gw := "foo" - d, _ := types.ParseCIDR(dest) - g := net.ParseIP(gw) - network := types.Network{ - Driver: "ipvlan", - Subnets: []types.Subnet{ - {Subnet: n}, - }, - Routes: []types.Route{ - {Destination: d, Gateway: g}, - }, - } - _, err := libpodNet.NetworkCreate(network, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) - }) - It("create macvlan config with metric option", func() { network := types.Network{ Driver: "macvlan", @@ -1968,6 +1915,36 @@ var _ = Describe("Config", func() { Expect(err.Error()).To(ContainSubstring("unable to parse \"no_default_route\"")) }) + It("create bridge custom route type route", func() { + dest := "10.1.0.0/24" + d, _ := types.ParseCIDR(dest) + for _, routeType := range []types.RouteType{types.RouteTypeBlackhole, types.RouteTypeProhibit, types.RouteTypeUnreachable} { + network := types.Network{ + Driver: "bridge", + Routes: []types.Route{ + {Destination: d, RouteType: routeType}, + }, + } + network1, err := libpodNet.NetworkCreate(network, nil) + Expect(err).ToNot(HaveOccurred()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.Routes).To(HaveLen(1)) + Expect(network1.Routes[0].Destination.String()).To(Equal(dest)) + Expect(network1.Routes[0].RouteType).To(Equal(routeType)) + } + + // without gateway a unicast route should fail + network := types.Network{ + Driver: "bridge", + Routes: []types.Route{ + {Destination: d, RouteType: types.RouteTypeUnicast}, + }, + } + _, err := libpodNet.NetworkCreate(network, nil) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("route 10.1.0.0/24 requires a gateway for type unicast")) + }) + Context("network load valid existing ones", func() { BeforeEach(func() { dir := "testfiles/valid" diff --git a/common/libnetwork/types/network.go b/common/libnetwork/types/network.go index 1878bea6b4..11ba341355 100644 --- a/common/libnetwork/types/network.go +++ b/common/libnetwork/types/network.go @@ -201,15 +201,31 @@ type Subnet struct { LeaseRange *LeaseRange `json:"lease_range,omitempty"` } +// RouteType represents the type of a route. +type RouteType string + +const ( + // RouteTypeUnicast is a regular route with a gateway (default). + RouteTypeUnicast RouteType = "unicast" + // RouteTypeBlackhole silently discards packets. + RouteTypeBlackhole RouteType = "blackhole" + // RouteTypeUnreachable rejects with "destination unreachable". + RouteTypeUnreachable RouteType = "unreachable" + // RouteTypeProhibit rejects with "administratively prohibited". + RouteTypeProhibit RouteType = "prohibit" +) + type Route struct { // Destination for this route in CIDR form. // swagger:strfmt string Destination IPNet `json:"destination"` - // Gateway IP for this route. + // Gateway IP for this route. Required for unicast routes, must be empty for blackhole/unreachable/prohibit. // swagger:strfmt string - Gateway net.IP `json:"gateway"` + Gateway net.IP `json:"gateway,omitempty"` // Metric for this route. Optional. Metric *uint32 `json:"metric,omitempty"` + // RouteType is the type of route: unicast (default), blackhole, unreachable, prohibit. + RouteType RouteType `json:"route_type,omitempty"` } // LeaseRange contains the range where IP are leased.