diff options
101 files changed, 1232 insertions, 8300 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index e925fb4f1..193f09e85 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -11,6 +11,7 @@ import ( "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/rootless" @@ -1108,9 +1109,9 @@ func AutocompleteManifestFormat(cmd *cobra.Command, args []string, toComplete st } // AutocompleteNetworkDriver - Autocomplete network driver option. -// -> "bridge" +// -> "bridge", "macvlan" func AutocompleteNetworkDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - drivers := []string{"bridge"} + drivers := []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} return drivers, cobra.ShellCompDirectiveNoFileComp } @@ -1252,16 +1253,13 @@ func AutocompletePruneFilters(cmd *cobra.Command, args []string, toComplete stri // AutocompleteNetworkFilters - Autocomplete network ls --filter options. func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { kv := keyValueCompletion{ - "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeNames) }, - "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeIDs) }, - "plugin=": func(_ string) ([]string, cobra.ShellCompDirective) { - return []string{"bridge", "portmap", - "firewall", "tuning", "dnsname", "macvlan"}, cobra.ShellCompDirectiveNoFileComp - }, + "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeNames) }, + "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeIDs) }, "label=": nil, "driver=": func(_ string) ([]string, cobra.ShellCompDirective) { - return []string{"bridge"}, cobra.ShellCompDirectiveNoFileComp + return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}, cobra.ShellCompDirectiveNoFileComp }, + "until=": nil, } return completeKeyValues(toComplete, kv) } diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go index db66fc9a0..f309390c3 100644 --- a/cmd/podman/containers/port.go +++ b/cmd/podman/containers/port.go @@ -8,8 +8,8 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -73,7 +73,7 @@ func port(_ *cobra.Command, args []string) error { var ( container string err error - userPort ocicni.PortMapping + userPort types.OCICNIPortMapping ) if len(args) == 0 && !portOpts.Latest && !portOpts.All { @@ -105,7 +105,7 @@ func port(_ *cobra.Command, args []string) error { if err != nil { return err } - userPort = ocicni.PortMapping{ + userPort = types.OCICNIPortMapping{ HostPort: 0, ContainerPort: int32(portNum), Protocol: fields[1], diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index a5b0795cd..ff792b78b 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -15,8 +15,8 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -469,7 +469,7 @@ func (l psReporter) UTS() string { // portsToString converts the ports used to a string of the from "port1, port2" // and also groups a continuous list of ports into a readable format. -func portsToString(ports []ocicni.PortMapping) string { +func portsToString(ports []types.OCICNIPortMapping) string { if len(ports) == 0 { return "" } @@ -478,8 +478,8 @@ func portsToString(ports []ocicni.PortMapping) string { return comparePorts(ports[i], ports[j]) }) - portGroups := [][]ocicni.PortMapping{} - currentGroup := []ocicni.PortMapping{} + portGroups := [][]types.OCICNIPortMapping{} + currentGroup := []types.OCICNIPortMapping{} for i, v := range ports { var prevPort, nextPort *int32 if i > 0 { @@ -492,17 +492,17 @@ func portsToString(ports []ocicni.PortMapping) string { port := v.ContainerPort // Helper functions - addToCurrentGroup := func(x ocicni.PortMapping) { + addToCurrentGroup := func(x types.OCICNIPortMapping) { currentGroup = append(currentGroup, x) } - addToPortGroup := func(x ocicni.PortMapping) { - portGroups = append(portGroups, []ocicni.PortMapping{x}) + addToPortGroup := func(x types.OCICNIPortMapping) { + portGroups = append(portGroups, []types.OCICNIPortMapping{x}) } finishCurrentGroup := func() { portGroups = append(portGroups, currentGroup) - currentGroup = []ocicni.PortMapping{} + currentGroup = []types.OCICNIPortMapping{} } // Single entry slice @@ -600,7 +600,7 @@ func portsToString(ports []ocicni.PortMapping) string { return strings.Join(portDisplay, ", ") } -func comparePorts(i, j ocicni.PortMapping) bool { +func comparePorts(i, j types.OCICNIPortMapping) bool { if i.ContainerPort != j.ContainerPort { return i.ContainerPort < j.ContainerPort } diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go index b5ddd215f..21d720f55 100644 --- a/cmd/podman/networks/create.go +++ b/cmd/podman/networks/create.go @@ -8,7 +8,8 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/libpod/network/util" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -38,11 +39,11 @@ func networkCreateFlags(cmd *cobra.Command) { flags := cmd.Flags() driverFlagName := "driver" - flags.StringVarP(&networkCreateOptions.Driver, driverFlagName, "d", "bridge", "driver to manage the network") + flags.StringVarP(&networkCreateOptions.Driver, driverFlagName, "d", types.DefaultNetworkDriver, "driver to manage the network") _ = cmd.RegisterFlagCompletionFunc(driverFlagName, common.AutocompleteNetworkDriver) optFlagName := "opt" - flags.StringArrayVarP(&opts, optFlagName, "o", []string{}, "Set driver specific options (default [])") + flags.StringArrayVarP(&opts, optFlagName, "o", nil, "Set driver specific options (default [])") _ = cmd.RegisterFlagCompletionFunc(optFlagName, completion.AutocompleteNone) gatewayFlagName := "gateway" @@ -55,6 +56,7 @@ func networkCreateFlags(cmd *cobra.Command) { flags.IPNetVar(&networkCreateOptions.Range, ipRangeFlagName, net.IPNet{}, "allocate container IP from range") _ = cmd.RegisterFlagCompletionFunc(ipRangeFlagName, completion.AutocompleteNone) + // TODO consider removing this for 4.0 macvlanFlagName := "macvlan" flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device") // This option is deprecated @@ -88,9 +90,6 @@ func networkCreate(cmd *cobra.Command, args []string) error { name string ) if len(args) > 0 { - if !define.NameRegex.MatchString(args[0]) { - return define.RegexError - } name = args[0] } var err error @@ -100,17 +99,60 @@ func networkCreate(cmd *cobra.Command, args []string) error { } networkCreateOptions.Options, err = parse.GetAllLabels([]string{}, opts) if err != nil { - return errors.Wrapf(err, "unable to process options") + return errors.Wrapf(err, "unable to parse options") + } + + network := types.Network{ + Name: name, + Driver: networkCreateOptions.Driver, + Options: networkCreateOptions.Options, + Labels: networkCreateOptions.Labels, + IPv6Enabled: networkCreateOptions.IPv6, + DNSEnabled: !networkCreateOptions.DisableDNS, + Internal: networkCreateOptions.Internal, } + // old --macvlan option if networkCreateOptions.MacVLAN != "" { logrus.Warn("The --macvlan option is deprecated, use `--driver macvlan --opt parent=<device>` instead") + network.Driver = types.MacVLANNetworkDriver + network.NetworkInterface = networkCreateOptions.MacVLAN + } else if networkCreateOptions.Driver == types.MacVLANNetworkDriver { + // new -d macvlan --opt parent=... syntax + if parent, ok := network.Options["parent"]; ok { + network.NetworkInterface = parent + delete(network.Options, "parent") + } + } + + if networkCreateOptions.Subnet.IP != nil { + s := types.Subnet{ + Subnet: types.IPNet{IPNet: networkCreateOptions.Subnet}, + Gateway: networkCreateOptions.Gateway, + } + if networkCreateOptions.Range.IP != nil { + startIP, err := util.FirstIPInSubnet(&networkCreateOptions.Range) + if err != nil { + return errors.Wrap(err, "failed to get first ip in range") + } + lastIP, err := util.LastIPInSubnet(&networkCreateOptions.Range) + if err != nil { + return errors.Wrap(err, "failed to get last ip in range") + } + s.LeaseRange = &types.LeaseRange{ + StartIP: startIP, + EndIP: lastIP, + } + } + network.Subnets = append(network.Subnets, s) + } else if networkCreateOptions.Range.IP != nil || networkCreateOptions.Gateway != nil { + return errors.New("cannot set gateway or range without subnet") } - response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions) + response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), network) if err != nil { return err } - fmt.Println(response.Filename) + fmt.Println(response.Name) return nil } diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 0ac637ea5..124a17d5d 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -10,8 +10,8 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/network" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -90,13 +90,13 @@ func networkList(cmd *cobra.Command, args []string) error { return err } -func quietOut(responses []*entities.NetworkListReport) { +func quietOut(responses []types.Network) { for _, r := range responses { fmt.Println(r.Name) } } -func jsonOut(responses []*entities.NetworkListReport) error { +func jsonOut(responses []types.Network) error { prettyJSON, err := json.MarshalIndent(responses, "", " ") if err != nil { return err @@ -105,7 +105,7 @@ func jsonOut(responses []*entities.NetworkListReport) error { return nil } -func templateOut(responses []*entities.NetworkListReport, cmd *cobra.Command) error { +func templateOut(responses []types.Network, cmd *cobra.Command) error { nlprs := make([]ListPrintReports, 0, len(responses)) for _, r := range responses { nlprs = append(nlprs, ListPrintReports{r}) @@ -113,12 +113,10 @@ func templateOut(responses []*entities.NetworkListReport, cmd *cobra.Command) er // Headers() gets lost resolving the embedded field names so add them headers := report.Headers(ListPrintReports{}, map[string]string{ - "Name": "name", - "CNIVersion": "version", - "Version": "version", - "Plugins": "plugins", - "Labels": "labels", - "ID": "network id", + "Name": "name", + "Driver": "driver", + "Labels": "labels", + "ID": "network id", }) renderHeaders := report.HasTable(networkListOptions.Format) @@ -127,7 +125,7 @@ func templateOut(responses []*entities.NetworkListReport, cmd *cobra.Command) er row = report.NormalizeFormat(networkListOptions.Format) } else { // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" ' renderHeaders = true - row = "{{.ID}}\t{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" + row = "{{.ID}}\t{{.Name}}\t{{.Driver}}\n" } format = report.EnforceRange(row) @@ -153,23 +151,13 @@ func templateOut(responses []*entities.NetworkListReport, cmd *cobra.Command) er // ListPrintReports returns the network list report type ListPrintReports struct { - *entities.NetworkListReport -} - -// Version returns the CNI version -func (n ListPrintReports) Version() string { - return n.CNIVersion -} - -// Plugins returns the CNI Plugins -func (n ListPrintReports) Plugins() string { - return network.GetCNIPlugins(n.NetworkConfigList) + types.Network } // Labels returns any labels added to a Network func (n ListPrintReports) Labels() string { - list := make([]string, 0, len(n.NetworkListReport.Labels)) - for k, v := range n.NetworkListReport.Labels { + list := make([]string, 0, len(n.Network.Labels)) + for k, v := range n.Network.Labels { list = append(list, k+"="+v) } return strings.Join(list, ",") @@ -181,5 +169,5 @@ func (n ListPrintReports) ID() string { if noTrunc { length = 64 } - return network.GetNetworkID(n.Name)[:length] + return n.Network.ID[:length] } diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index d0c348d58..41b155943 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -173,6 +173,9 @@ esac case "$PRIV_NAME" in root) ;; rootless) + # load kernel modules since the rootless user has no permission to do so + modprobe ip6_tables || : + modprobe ip6table_nat || : # Needs to exist for setup_rootless() ROOTLESS_USER="${ROOTLESS_USER:-some${RANDOM}dude}" echo "ROOTLESS_USER=$ROOTLESS_USER" >> /etc/ci_environment diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md index 04290c188..816dd53ea 100644 --- a/docs/source/markdown/podman-network-create.1.md +++ b/docs/source/markdown/podman-network-create.1.md @@ -15,7 +15,7 @@ with the host network's DHCP server. If no options are provided, Podman will assign a free subnet and name for your network. -Upon completion of creating the network, Podman will display the path to the newly added network file. +Upon completion of creating the network, Podman will display the name of the newly added network. ## OPTIONS #### **--disable-dns** @@ -61,49 +61,50 @@ The subnet in CIDR notation. #### **--ipv6** -Enable IPv6 (Dual Stack) networking. You must pass a IPv6 subnet. The *subnet* option must be used with the *ipv6* option. +Enable IPv6 (Dual Stack) networking. ## EXAMPLE -Create a network with no options +Create a network with no options. ``` -# podman network create -/etc/cni/net.d/cni-podman-4.conflist +$ podman network create +cni-podman2 ``` Create a network named *newnet* that uses *192.5.0.0/16* for its subnet. ``` -# podman network create --subnet 192.5.0.0/16 newnet -/etc/cni/net.d/newnet.conflist +$ podman network create --subnet 192.5.0.0/16 newnet +newnet ``` -Create an IPv6 network named *newnetv6*, you must specify the subnet for this network, otherwise the command will fail. -For this example, we use *2001:db8::/64* for its subnet. +Create an IPv6 network named *newnetv6* with a subnet of *2001:db8::/64*. ``` -# podman network create --subnet 2001:db8::/64 --ipv6 newnetv6 -/etc/cni/net.d/newnetv6.conflist +$ podman network create --subnet 2001:db8::/64 --ipv6 newnetv6 +newnetv6 ``` -Create a network named *newnet* that uses *192.168.33.0/24* and defines a gateway as *192.168.133.3* +Create a network named *newnet* that uses *192.168.33.0/24* and defines a gateway as *192.168.133.3*. ``` -# podman network create --subnet 192.168.33.0/24 --gateway 192.168.33.3 newnet -/etc/cni/net.d/newnet.conflist +$ podman network create --subnet 192.168.33.0/24 --gateway 192.168.33.3 newnet +newnet ``` Create a network that uses a *192.168.55.0/24** subnet and has an IP address range of *192.168.55.129 - 192.168.55.254*. ``` -# podman network create --subnet 192.168.55.0/24 --ip-range 192.168.55.128/25 -/etc/cni/net.d/cni-podman-5.conflist +$ podman network create --subnet 192.168.55.0/24 --ip-range 192.168.55.128/25 +cni-podman5 ``` -Create a Macvlan based network using the host interface eth0 +Create a Macvlan based network using the host interface eth0. Macvlan networks can only be used as root. ``` # podman network create -d macvlan -o parent=eth0 newnet -/etc/cni/net.d/newnet.conflist +newnet ``` ## SEE ALSO -podman(1), podman-network(1), podman-network-inspect(1) +**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-network-ls(1)](podman-network-ls.1.md)** ## HISTORY +August 2021, Updated with the new network format by Paul Holzinger <pholzing@redhat.com> + August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/source/markdown/podman-network-inspect.1.md b/docs/source/markdown/podman-network-inspect.1.md index 56515d0c1..726f167e5 100644 --- a/docs/source/markdown/podman-network-inspect.1.md +++ b/docs/source/markdown/podman-network-inspect.1.md @@ -4,57 +4,71 @@ podman\-network\-inspect - Displays the raw CNI network configuration for one or more networks ## SYNOPSIS -**podman network inspect** [*options*] [*network* ...] +**podman network inspect** [*options*] *network* [*network* ...] ## DESCRIPTION Display the raw (JSON format) network configuration. ## OPTIONS -#### **--format**, **-f** +#### **--format**, **-f**=*format* Pretty-print networks to JSON or using a Go template. +| **Placeholder** | **Description** | +| ----------------- | ----------------------------------------- | +| .ID | Network ID | +| .Name | Network name | +| .Driver | Network driver | +| .Labels | Network labels | +| .Options | Network options | +| .IPAMOptions | Network ipam options | +| .Created | Timestamp when the network was created | +| .Internal | Network is internal (boolean) | +| .IPv6Enabled | Network has ipv6 subnet (boolean) | +| .DNSEnabled | Network has dns enabled (boolean) | +| .NetworkInterface | Name of the network interface on the host | +| .Subnets | List of subnets on this network | + ## EXAMPLE -Inspect the default podman network +Inspect the default podman network. ``` -# podman network inspect podman -[{ - "cniVersion": "0.3.0", - "name": "podman", - "plugins": [ - { - "type": "bridge", - "bridge": "cni0", - "isGateway": true, - "ipMasq": true, - "ipam": { - "type": "host-local", - "subnet": "10.88.1.0/24", - "routes": [ - { "dst": "0.0.0.0/0" } - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true +$ podman network inspect podman +[ + { + "name": "podman", + "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", + "driver": "bridge", + "network_interface": "cni-podman0", + "created": "2021-06-03T12:04:33.088567413+02:00", + "subnets": [ + { + "subnet": "10.88.0.0/16", + "gateway": "10.88.0.1" + } + ], + "ipv6_enabled": false, + "internal": false, + "dns_enabled": false, + "ipam_options": { + "driver": "host-local" } - } - ] -} + } ] ``` +Show the subnet and gateway for a network. + ``` -# podman network inspect podman --format '{{(index .plugins 0).ipam.ranges}}' -[[map[gateway:10.88.0.1 subnet:10.88.0.0/16]]] +$ podman network inspect podman --format "{{range .Subnets}}Subnet: {{.Subnet}} Gateway: {{.Gateway}}{{end}}" +Subnet: 10.88.0.0/16 Gateway: 10.88.0.1 ``` ## SEE ALSO -podman(1), podman-network(1), podman-network-ls(1) +**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-ls(1)](podman-network-ls.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)** ## HISTORY +August 2021, Updated with the new network format by Paul Holzinger <pholzing@redhat.com> + August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/source/markdown/podman-network-ls.1.md b/docs/source/markdown/podman-network-ls.1.md index b7fd13ec6..99b734157 100644 --- a/docs/source/markdown/podman-network-ls.1.md +++ b/docs/source/markdown/podman-network-ls.1.md @@ -10,7 +10,7 @@ podman\-network\-ls - Display a summary of CNI networks Displays a list of existing podman networks. ## OPTIONS -#### **--filter**, **-f** +#### **--filter**, **-f**=*filter=value* Filter output based on conditions given. Multiple filters can be given with multiple uses of the --filter option. @@ -19,27 +19,34 @@ Filters with the same key work inclusive with the only exception being Valid filters are listed below: -| **Filter** | **Description** | -| ---------- | ------------------------------------------------------------------------------------- | -| name | [Name] Network name (accepts regex) | -| id | [ID] Full or partial network ID | -| label | [Key] or [Key=Value] Label assigned to a network | -| plugin | [Plugin] CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) | -| driver | [Driver] Only `bridge` is supported | +| **Filter** | **Description** | +| ---------- | ----------------------------------------------------------------- | +| name | [Name] Network name (accepts regex) | +| id | [ID] Full or partial network ID | +| label | [Key] or [Key=Value] Label assigned to a network | +| driver | [Driver] `bridge` or ,`macvlan` is supported | +| until | [Until] Show all networks that were created before the given time | -#### **--format** +#### **--format**=*format* Change the default output format. This can be of a supported type like 'json' or a Go template. Valid placeholders for the Go template are listed below: -| **Placeholder** | **Description** | -| --------------- | --------------------------------| -| .ID | Network ID | -| .Name | Network name | -| .Plugins | Network Plugins | -| .Labels | Network labels | -| .Version | CNI Version of the config file | +| **Placeholder** | **Description** | +| ----------------- | ----------------------------------------- | +| .ID | Network ID | +| .Name | Network name | +| .Driver | Network driver | +| .Labels | Network labels | +| .Options | Network options | +| .IPAMOptions | Network ipam options | +| .Created | Timestamp when the network was created | +| .Internal | Network is internal (boolean) | +| .IPv6Enabled | Network has ipv6 subnet (boolean) | +| .DNSEnabled | Network has dns enabled (boolean) | +| .NetworkInterface | Name of the network interface on the host | +| .Subnets | List of subnets on this network | #### **--noheading** @@ -47,7 +54,7 @@ Omit the table headings from the listing of networks. #### **--no-trunc** -Do not truncate the network ID. The network ID is not displayed by default and must be specified with **--format**. +Do not truncate the network ID. #### **--quiet**, **-q** @@ -58,17 +65,16 @@ The `quiet` option will restrict the output to only the network names. Display networks ``` -# podman network ls -NAME VERSION PLUGINS -podman 0.3.0 bridge,portmap -podman2 0.3.0 bridge,portmap -outside 0.3.0 bridge -podman9 0.3.0 bridge,portmap +$ podman network ls +NETWORK ID NAME DRIVER +88a7120ee19d podman bridge +6dd508dbf8cd cni-podman6 bridge +8e35c2cd3bf6 cni-podman5 macvlan ``` Display only network names ``` -# podman network ls -q +$ podman network ls -q podman podman2 outside @@ -77,14 +83,23 @@ podman9 Display name of network which support bridge plugin ``` -# podman network ls --filter plugin=portmap --format {{.Name}} +$ podman network ls --filter driver=bridge --format {{.Name}} podman podman2 podman9 ``` +List networks with their subnets +``` +$ podman network ls --format "{{.Name}}: {{range .Subnets}}{{.Subnet}} {{end}}" +podman: 10.88.0.0/16 +cni-podman3: 10.89.30.0/24 fde4:f86f:4aab:e68f::/64 +macvlan: +``` ## SEE ALSO -podman(1), podman-network(1), podman-network-inspect(1) +**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)** ## HISTORY +August 2021, Updated with the new network format by Paul Holzinger <pholzing@redhat.com> + August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> @@ -20,7 +20,6 @@ require ( github.com/containers/storage v1.36.0 github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 - github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283 github.com/cyphar/filepath-securejoin v0.2.3 github.com/davecgh/go-spew v1.1.1 github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f @@ -241,7 +241,6 @@ github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containers/buildah v1.23.0 h1:qGIeSNOczUHzvnaaOS29HSMiYAjw6JgIXYksAyvqnLs= @@ -270,7 +269,6 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -293,8 +291,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283 h1:7FyIYKksGvRF8XjMkG5T6uIxg8PcgZoPyO+f6kHT5+s= -github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283/go.mod h1:vingr1ztOAzP2WyTgGbpMov9dFhbjNxdLtDv0+PhAvY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -703,7 +699,6 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -714,7 +709,6 @@ github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -823,7 +817,6 @@ github.com/rootless-containers/rootlesskit v0.14.5/go.mod h1:Ai3detLzryb/4EkzXmN github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 5df3e8961..56b4bafd3 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -1223,7 +1223,7 @@ func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []str } ctrNetworks := ctr.config.Networks if len(ctrNetworks) == 0 { - ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()} + ctrNetworks = []string{ctr.runtime.config.Network.DefaultNetwork} } // Copy in all the container's CNI networks for _, net := range ctrNetworks { diff --git a/libpod/common_test.go b/libpod/common_test.go index 4c419cfa8..4662a33bd 100644 --- a/libpod/common_test.go +++ b/libpod/common_test.go @@ -10,7 +10,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" - "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/containers/podman/v3/libpod/network/types" "github.com/opencontainers/runtime-tools/generate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -41,7 +41,7 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error) ContainerNetworkConfig: ContainerNetworkConfig{ DNSServer: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.2.2")}, DNSSearch: []string{"example.com", "example.example.com"}, - PortMappings: []ocicni.PortMapping{ + PortMappings: []types.OCICNIPortMapping{ { HostPort: 80, ContainerPort: 90, diff --git a/libpod/container.go b/libpod/container.go index a4bbb5dd0..cf727926c 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -8,14 +8,14 @@ import ( "os" "time" - "github.com/containernetworking/cni/pkg/types" cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" + "github.com/containers/podman/v3/libpod/network/cni" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/storage" - "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -114,14 +114,11 @@ type Container struct { rootlessPortSyncR *os.File rootlessPortSyncW *os.File - // A restored container should have the same IP address as before - // being checkpointed. If requestedIP is set it will be used instead - // of config.StaticIP. - requestedIP net.IP - // A restored container should have the same MAC address as before - // being checkpointed. If requestedMAC is set it will be used instead - // of config.StaticMAC. - requestedMAC net.HardwareAddr + // perNetworkOpts should be set when you want to use special network + // options when calling network setup/teardown. This should be used for + // container restore or network reload for example. Leave this nil if + // the settings from the container config should be used. + perNetworkOpts map[string]types.PerNetworkOptions // This is true if a container is restored from a checkpoint. restoreFromCheckpoint bool @@ -173,11 +170,20 @@ type ContainerState struct { // Podman. // These are DEPRECATED and will be removed in a future release. LegacyExecSessions map[string]*legacyExecSession `json:"execSessions,omitempty"` - // NetworkStatus contains the configuration results for all networks + // NetworkStatusOld contains the configuration results for all networks // the pod is attached to. Only populated if we created a network // namespace for the container, and the network namespace is currently - // active - NetworkStatus []*cnitypes.Result `json:"networkResults,omitempty"` + // active. + // These are DEPRECATED and will be removed in a future release. + // This field is only used for backwarts compatibility. + NetworkStatusOld []*cnitypes.Result `json:"networkResults,omitempty"` + // NetworkStatus contains the network Status for all networks + // the container is attached to. Only populated if we created a network + // namespace for the container, and the network namespace is currently + // active. + // To read this field use container.getNetworkStatus() instead, this will + // take care of migrating the old DEPRECATED network status to the new format. + NetworkStatus map[string]types.StatusBlock `json:"networkStatus,omitempty"` // BindMounts contains files that will be bind-mounted into the // container when it is mounted. // These include /etc/hosts and /etc/resolv.conf @@ -454,7 +460,7 @@ func (c *Container) NewNetNS() bool { // PortMappings returns the ports that will be mapped into a container if // a new network namespace is created // If NewNetNS() is false, this value is unused -func (c *Container) PortMappings() ([]ocicni.PortMapping, error) { +func (c *Container) PortMappings() ([]types.OCICNIPortMapping, error) { // First check if the container belongs to a network namespace (like a pod) if len(c.config.NetNsCtr) > 0 { netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr) @@ -788,66 +794,6 @@ func (c *Container) ExecSession(id string) (*ExecSession, error) { return returnSession, nil } -// IPs retrieves a container's IP address(es) -// This will only be populated if the container is configured to created a new -// network namespace, and that namespace is presently active -func (c *Container) IPs() ([]net.IPNet, error) { - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - - if err := c.syncContainer(); err != nil { - return nil, err - } - } - - if !c.config.CreateNetNS { - return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID()) - } - - ips := make([]net.IPNet, 0) - - for _, r := range c.state.NetworkStatus { - for _, ip := range r.IPs { - ips = append(ips, ip.Address) - } - } - - return ips, nil -} - -// Routes retrieves a container's routes -// This will only be populated if the container is configured to created a new -// network namespace, and that namespace is presently active -func (c *Container) Routes() ([]types.Route, error) { - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - - if err := c.syncContainer(); err != nil { - return nil, err - } - } - - if !c.config.CreateNetNS { - return nil, errors.Wrapf(define.ErrInvalidArg, "container %s network namespace is not managed by libpod", c.ID()) - } - - routes := make([]types.Route, 0) - - for _, r := range c.state.NetworkStatus { - for _, route := range r.Routes { - newRoute := types.Route{ - Dst: route.Dst, - GW: route.GW, - } - routes = append(routes, newRoute) - } - } - - return routes, nil -} - // BindMounts retrieves bind mounts that were created by libpod and will be // added to the container // All these mounts except /dev/shm are ignored if a mount in the given spec has @@ -1230,7 +1176,7 @@ func (c *Container) networks() ([]string, bool, error) { networks, err := c.runtime.state.GetNetworks(c) if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { if len(c.config.Networks) == 0 && c.config.NetMode.IsBridge() { - return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil + return []string{c.runtime.config.Network.DefaultNetwork}, true, nil } return c.config.Networks, false, nil } @@ -1267,3 +1213,37 @@ func (d ContainerNetworkDescriptions) getInterfaceByName(networkName string) (st } return fmt.Sprintf("eth%d", val), exists } + +// getNetworkStatus get the current network status from the state. If the container +// still uses the old network status it is converted to the new format. This function +// should be used instead of reading c.state.NetworkStatus directly. +func (c *Container) getNetworkStatus() map[string]types.StatusBlock { + if c.state.NetworkStatus != nil { + return c.state.NetworkStatus + } + if c.state.NetworkStatusOld != nil { + // Note: NetworkStatusOld does not contain the network names so we get them extra + // Generally the order should be the same + networks, _, err := c.networks() + if err != nil { + return nil + } + if len(networks) != len(c.state.NetworkStatusOld) { + return nil + } + result := make(map[string]types.StatusBlock, len(c.state.NetworkStatusOld)) + for i := range c.state.NetworkStatusOld { + status, err := cni.CNIResultToStatus(c.state.NetworkStatusOld[i]) + if err != nil { + return nil + } + result[networks[i]] = status + } + c.state.NetworkStatus = result + _ = c.save() + // TODO remove debug for final version + logrus.Debugf("converted old network result to new result %v", result) + return result + } + return nil +} diff --git a/libpod/container_config.go b/libpod/container_config.go index a2c989a1a..0374c25fe 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -6,9 +6,9 @@ import ( "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/storage" - "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" ) @@ -230,7 +230,7 @@ type ContainerNetworkConfig struct { // PortMappings are the ports forwarded to the container's network // namespace // These are not used unless CreateNetNS is true - PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"` + PortMappings []types.OCICNIPortMapping `json:"portMappings,omitempty"` // ExposedPorts are the ports which are exposed but not forwarded // into the container. // The map key is the port and the string slice contains the protocols, diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1033729ae..9ac2cd5bd 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -644,17 +644,8 @@ func (c *Container) refresh() error { } c.lock = lock - // Try to delete any lingering IP allocations. - // If this fails, just log and ignore. - // I'm a little concerned that this is so far down in refresh() and we - // could fail before getting to it - but the worst that would happen is - // that Inspect() would return info on IPs we no longer own. - if len(c.state.NetworkStatus) > 0 { - if err := c.removeIPv4Allocations(); err != nil { - logrus.Errorf("Error removing IP allocations for container %s: %v", c.ID(), err) - } - } c.state.NetworkStatus = nil + c.state.NetworkStatusOld = nil if err := c.save(); err != nil { return errors.Wrapf(err, "error refreshing state for container %s", c.ID()) @@ -668,57 +659,6 @@ func (c *Container) refresh() error { return nil } -// Try and remove IP address allocations. Presently IPv4 only. -// Should be safe as rootless because NetworkStatus should only be populated if -// CNI is running. -func (c *Container) removeIPv4Allocations() error { - cniNetworksDir, err := getCNINetworksDir() - if err != nil { - return err - } - - if len(c.state.NetworkStatus) == 0 { - return nil - } - - cniDefaultNetwork := "" - if c.runtime.netPlugin != nil { - cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName() - } - - networks, _, err := c.networks() - if err != nil { - return err - } - - if len(networks) != len(c.state.NetworkStatus) { - return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus)) - } - - for index, result := range c.state.NetworkStatus { - for _, ctrIP := range result.IPs { - if ctrIP.Version != "4" { - continue - } - candidate := "" - if len(networks) > 0 { - // CNI returns networks in order we passed them. - // So our index into results should be our index - // into networks. - candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String()) - } else { - candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String()) - } - logrus.Debugf("Going to try removing IP address reservation file %q for container %s", candidate, c.ID()) - if err := os.Remove(candidate); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error removing CNI IP reservation file %q for container %s", candidate, c.ID()) - } - } - } - - return nil -} - // Remove conmon attach socket and terminal resize FIFO // This is necessary for restarting containers func (c *Container) removeConmonFiles() error { @@ -1017,11 +957,9 @@ func (c *Container) completeNetworkSetup() error { } state := c.state // collect any dns servers that cni tells us to use (dnsname) - for _, cni := range state.NetworkStatus { - if cni.DNS.Nameservers != nil { - for _, server := range cni.DNS.Nameservers { - outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server)) - } + for _, status := range c.getNetworkStatus() { + for _, server := range status.DNSServerIPs { + outResolvConf = append(outResolvConf, fmt.Sprintf("nameserver %s", server)) } } // check if we have a bindmount for /etc/hosts @@ -1062,9 +1000,12 @@ func (c *Container) completeNetworkSetup() error { func (c *Container) cniHosts() string { var hosts string - if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 { - ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] - hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name) + for _, status := range c.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + hosts += fmt.Sprintf("%s\t%s %s\n", netAddress.Subnet.IP.String(), c.Hostname(), c.Config().Name) + } + } } return hosts } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 4194a0d93..0557b30d0 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -22,7 +22,6 @@ import ( metadata "github.com/checkpoint-restore/checkpointctl/lib" cdi "github.com/container-orchestrated-devices/container-device-interface/pkg" - cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/buildah/pkg/chrootuser" "github.com/containers/buildah/pkg/overlay" @@ -34,6 +33,7 @@ import ( "github.com/containers/common/pkg/umask" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/annotations" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/checkpoint/crutils" @@ -81,7 +81,7 @@ func (c *Container) prepare() error { var ( wg sync.WaitGroup netNS ns.NetNS - networkStatus []*cnitypes.Result + networkStatus map[string]types.StatusBlock createNetNSErr, mountStorageErr error mountPoint string tmpStateLock sync.Mutex @@ -263,6 +263,7 @@ func (c *Container) cleanupNetwork() error { c.state.NetNS = nil c.state.NetworkStatus = nil + c.state.NetworkStatusOld = nil if c.valid { return c.save() @@ -1121,7 +1122,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO // Save network.status. This is needed to restore the container with // the same IP. Currently limited to one IP address in a container // with one interface. - if _, err := metadata.WriteJSONFile(c.state.NetworkStatus, c.bundlePath(), metadata.NetworkStatusFile); err != nil { + // FIXME: will this break something? + if _, err := metadata.WriteJSONFile(c.getNetworkStatus(), c.bundlePath(), metadata.NetworkStatusFile); err != nil { return err } @@ -1261,8 +1263,11 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti } // Read network configuration from checkpoint - // Currently only one interface with one IP is supported. - networkStatus, _, err := metadata.ReadContainerCheckpointNetworkStatus(c.bundlePath()) + var netStatus map[string]types.StatusBlock + _, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile) + if err != nil { + logrus.Infof("failed to unmarshal network status, cannot restore the same ip/mac: %v", err) + } // If the restored container should get a new name, the IP address of // the container will not be restored. This assumes that if a new name is // specified, the container is restored multiple times. @@ -1271,19 +1276,41 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // best solution. if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) { // The file with the network.status does exist. Let's restore the - // container with the same IP address / MAC address as during checkpointing. - if !options.IgnoreStaticIP { - if IP := metadata.GetIPFromNetworkStatus(networkStatus); IP != nil { - // Tell CNI which IP address we want. - c.requestedIP = IP - } + // container with the same networks settings as during checkpointing. + aliases, err := c.runtime.state.GetAllNetworkAliases(c) + if err != nil { + return err } - if !options.IgnoreStaticMAC { - if MAC := metadata.GetMACFromNetworkStatus(networkStatus); MAC != nil { - // Tell CNI which MAC address we want. - c.requestedMAC = MAC + netOpts := make(map[string]types.PerNetworkOptions, len(netStatus)) + for network, status := range netStatus { + perNetOpts := types.PerNetworkOptions{} + for name, netInt := range status.Interfaces { + perNetOpts = types.PerNetworkOptions{ + InterfaceName: name, + Aliases: aliases[network], + } + if !options.IgnoreStaticMAC { + perNetOpts.StaticMAC = netInt.MacAddress + } + if !options.IgnoreStaticIP { + for _, netAddress := range netInt.Networks { + perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP) + } + } + // Normally interfaces have a length of 1, only for some special cni configs we could get more. + // For now just use the first interface to get the ips this should be good enough for most cases. + break + } + if perNetOpts.InterfaceName == "" { + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network) + } + perNetOpts.InterfaceName = eth } + netOpts[network] = perNetOpts } + c.perNetworkOpts = netOpts } defer func() { @@ -1785,9 +1812,9 @@ rootless=%d // generateResolvConf generates a containers resolv.conf func (c *Container) generateResolvConf() (string, error) { var ( - nameservers []string - cniNameServers []string - cniSearchDomains []string + nameservers []string + networkNameServers []string + networkSearchDomains []string ) resolvConf := "/etc/resolv.conf" @@ -1827,22 +1854,27 @@ func (c *Container) generateResolvConf() (string, error) { } ipv6 := false - // Check if CNI gave back and DNS servers for us to add in - cniResponse := c.state.NetworkStatus - for _, i := range cniResponse { - for _, ip := range i.IPs { - // Note: only using To16() does not work since it also returns a valid ip for ipv4 - if ip.Address.IP.To4() == nil && ip.Address.IP.To16() != nil { - ipv6 = true + // If network status is set check for ipv6 and dns namesevers + netStatus := c.getNetworkStatus() + for _, status := range netStatus { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + // Note: only using To16() does not work since it also returns a valid ip for ipv4 + if netAddress.Subnet.IP.To4() == nil && netAddress.Subnet.IP.To16() != nil { + ipv6 = true + } } } - if i.DNS.Nameservers != nil { - cniNameServers = append(cniNameServers, i.DNS.Nameservers...) - logrus.Debugf("adding nameserver(s) from cni response of '%q'", i.DNS.Nameservers) + + if status.DNSServerIPs != nil { + for _, nsIP := range status.DNSServerIPs { + networkNameServers = append(networkNameServers, nsIP.String()) + } + logrus.Debugf("adding nameserver(s) from network status of '%q'", status.DNSServerIPs) } - if i.DNS.Search != nil { - cniSearchDomains = append(cniSearchDomains, i.DNS.Search...) - logrus.Debugf("adding search domain(s) from cni response of '%q'", i.DNS.Search) + if status.DNSSearchDomains != nil { + networkSearchDomains = append(networkSearchDomains, status.DNSSearchDomains...) + logrus.Debugf("adding search domain(s) from network status of '%q'", status.DNSSearchDomains) } } @@ -1882,8 +1914,8 @@ func (c *Container) generateResolvConf() (string, error) { for _, server := range dnsServers { nameservers = append(nameservers, server.String()) } - case len(cniNameServers) > 0: - nameservers = append(nameservers, cniNameServers...) + case len(networkNameServers) > 0: + nameservers = append(nameservers, networkNameServers...) default: // Make a new resolv.conf nameservers = resolvconf.GetNameservers(resolv.Content) @@ -1899,11 +1931,11 @@ func (c *Container) generateResolvConf() (string, error) { } var search []string - if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(cniSearchDomains) > 0 { + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 { if !util.StringInSlice(".", c.config.DNSSearch) { search = c.runtime.config.Containers.DNSSearches search = append(search, c.config.DNSSearch...) - search = append(search, cniSearchDomains...) + search = append(search, networkSearchDomains...) } } else { search = resolvconf.GetSearchDomains(resolv.Content) @@ -2019,20 +2051,22 @@ func (c *Container) getHosts() string { // Add gateway entry var depCtr *Container + netStatus := c.getNetworkStatus() if c.config.NetNsCtr != "" { // ignoring the error because there isn't anything to do depCtr, _ = c.getRootNetNsDepCtr() - } else if len(c.state.NetworkStatus) != 0 { + } else if len(netStatus) != 0 { depCtr = c - } else { - depCtr = nil } if depCtr != nil { - for _, pluginResultsRaw := range depCtr.state.NetworkStatus { - pluginResult, _ := cnitypes.GetResult(pluginResultsRaw) - for _, ip := range pluginResult.IPs { - hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway) + for _, status := range depCtr.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + if netAddress.Gateway != nil { + hosts += fmt.Sprintf("%s host.containers.internal\n", netAddress.Gateway.String()) + } + } } } } else if c.config.NetMode.IsSlirp4netns() { diff --git a/libpod/info.go b/libpod/info.go index 7b60ee46f..36dc8bc2a 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -18,7 +18,6 @@ import ( "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/linkmode" - "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/storage" @@ -73,8 +72,7 @@ func (r *Runtime) info() (*define.Info, error) { volumePlugins = append(volumePlugins, plugin) } info.Plugins.Volume = volumePlugins - // TODO move this into the new network interface - info.Plugins.Network = []string{network.BridgeNetworkDriver, network.MacVLANNetworkDriver} + info.Plugins.Network = r.network.Drivers() info.Plugins.Log = logDrivers info.Registries = registries diff --git a/libpod/kube.go b/libpod/kube.go index 812bb101b..54e8a7c50 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -10,11 +10,11 @@ import ( "time" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/lookup" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" @@ -544,7 +544,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts // it to a v1.ContainerPort format for kube output -func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.ContainerPort, error) { +func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([]v1.ContainerPort, error) { containerPorts := make([]v1.ContainerPort, 0, len(portMappings)) for _, p := range portMappings { var protocol v1.Protocol diff --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go index 09943948b..060794ebe 100644 --- a/libpod/network/cni/cni_conversion.go +++ b/libpod/network/cni/cni_conversion.go @@ -185,6 +185,9 @@ func convertIPAMConfToNetwork(network *types.Network, ipam ipamConfig, confPath s.LeaseRange.StartIP = rangeStart s.LeaseRange.EndIP = rangeEnd } + if util.IsIPv6(s.Subnet.IP) { + network.IPv6Enabled = true + } network.Subnets = append(network.Subnets, s) } } diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go index ee203f80d..d31cd3002 100644 --- a/libpod/network/cni/config.go +++ b/libpod/network/cni/config.go @@ -24,7 +24,7 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { if err != nil { return types.Network{}, err } - network, err := n.networkCreate(net, true) + network, err := n.networkCreate(net, false) if err != nil { return types.Network{}, err } @@ -33,89 +33,106 @@ func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { return *network.libpodNet, nil } -func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*network, error) { +// networkCreate will fill out the given network struct and return the new network entry. +// If defaultNet is true it will not validate against used subnets and it will not write the cni config to disk. +func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*network, error) { // if no driver is set use the default one - if net.Driver == "" { - net.Driver = types.DefaultNetworkDriver + if newNetwork.Driver == "" { + newNetwork.Driver = types.DefaultNetworkDriver } // FIXME: Should we use a different type for network create without the ID field? // the caller is not allowed to set a specific ID - if net.ID != "" { + if newNetwork.ID != "" { return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create") } - if net.Labels == nil { - net.Labels = map[string]string{} + if newNetwork.Labels == nil { + newNetwork.Labels = map[string]string{} } - if net.Options == nil { - net.Options = map[string]string{} + if newNetwork.Options == nil { + newNetwork.Options = map[string]string{} } - if net.IPAMOptions == nil { - net.IPAMOptions = map[string]string{} + if newNetwork.IPAMOptions == nil { + newNetwork.IPAMOptions = map[string]string{} } var name string var err error // validate the name when given - if net.Name != "" { - if !define.NameRegex.MatchString(net.Name) { - return nil, errors.Wrapf(define.RegexError, "network name %s invalid", net.Name) + if newNetwork.Name != "" { + if !define.NameRegex.MatchString(newNetwork.Name) { + return nil, errors.Wrapf(define.RegexError, "network name %s invalid", newNetwork.Name) } - if _, ok := n.networks[net.Name]; ok { - return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", net.Name) + if _, ok := n.networks[newNetwork.Name]; ok { + return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", newNetwork.Name) } } else { name, err = n.getFreeDeviceName() if err != nil { return nil, err } - net.Name = name + newNetwork.Name = name } - switch net.Driver { + // Only get the used networks for validation if we do not create the default network. + // The default network should not be validated against used subnets, we have to ensure + // that this network can always be created even when a subnet is already used on the host. + // This could happen if you run a container on this net, then the cni interface will be + // created on the host and "block" this subnet from being used again. + // Therefore the next podman command tries to create the default net again and it would + // fail because it thinks the network is used on the host. + var usedNetworks []*net.IPNet + if !defaultNet { + usedNetworks, err = n.getUsedSubnets() + if err != nil { + return nil, err + } + } + + switch newNetwork.Driver { case types.BridgeNetworkDriver: // if the name was created with getFreeDeviceName set the interface to it as well - if name != "" && net.NetworkInterface == "" { - net.NetworkInterface = name + if name != "" && newNetwork.NetworkInterface == "" { + newNetwork.NetworkInterface = name } - err = n.createBridge(&net) + err = n.createBridge(&newNetwork, usedNetworks) if err != nil { return nil, err } case types.MacVLANNetworkDriver: - err = createMacVLAN(&net) + err = createMacVLAN(&newNetwork) if err != nil { return nil, err } default: - return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", net.Driver) + return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver) } - for i := range net.Subnets { - err := validateSubnet(&net.Subnets[i], !net.Internal) + for i := range newNetwork.Subnets { + err := validateSubnet(&newNetwork.Subnets[i], !newNetwork.Internal, usedNetworks) if err != nil { return nil, err } - if util.IsIPv6(net.Subnets[i].Subnet.IP) { - net.IPv6Enabled = true + if util.IsIPv6(newNetwork.Subnets[i].Subnet.IP) { + newNetwork.IPv6Enabled = true } } // generate the network ID - net.ID = getNetworkIDFromName(net.Name) + newNetwork.ID = getNetworkIDFromName(newNetwork.Name) // FIXME: Should this be a hard error? - if net.DNSEnabled && net.Internal && hasDNSNamePlugin(n.cniPluginDirs) { - logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", net.Name) - net.DNSEnabled = false + if newNetwork.DNSEnabled && newNetwork.Internal && hasDNSNamePlugin(n.cniPluginDirs) { + logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", newNetwork.Name) + newNetwork.DNSEnabled = false } - cniConf, path, err := n.createCNIConfigListFromNetwork(&net, writeToDisk) + cniConf, path, err := n.createCNIConfigListFromNetwork(&newNetwork, !defaultNet) if err != nil { return nil, err } - return &network{cniNet: cniConf, libpodNet: &net, filename: path}, nil + return &network{cniNet: cniConf, libpodNet: &newNetwork, filename: path}, nil } // NetworkRemove will remove the Network with the given name or ID. @@ -218,7 +235,7 @@ func createMacVLAN(network *types.Network) error { return nil } -func (n *cniNetwork) createBridge(network *types.Network) error { +func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) error { if network.NetworkInterface != "" { bridges := n.getBridgeInterfaceNames() if pkgutil.StringInSlice(network.NetworkInterface, bridges) { @@ -236,7 +253,7 @@ func (n *cniNetwork) createBridge(network *types.Network) error { } if len(network.Subnets) == 0 { - freeSubnet, err := n.getFreeIPv4NetworkSubnet() + freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks) if err != nil { return err } @@ -256,14 +273,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error { } } if !ipv4 { - freeSubnet, err := n.getFreeIPv4NetworkSubnet() + freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks) if err != nil { return err } network.Subnets = append(network.Subnets, *freeSubnet) } if !ipv6 { - freeSubnet, err := n.getFreeIPv6NetworkSubnet() + freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks) if err != nil { return err } @@ -278,10 +295,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error { // given gateway and lease range are part of this subnet. If the // gateway is empty and addGateway is true it will get the first // available ip in the subnet assigned. -func validateSubnet(s *types.Subnet, addGateway bool) error { +func validateSubnet(s *types.Subnet, addGateway bool, usedNetworks []*net.IPNet) error { if s == nil { return errors.New("subnet is nil") } + if s.Subnet.IP == nil { + return errors.New("subnet ip is nil") + } + // Reparse to ensure subnet is valid. // Do not use types.ParseCIDR() because we want the ip to be // the network address and not a random ip in the subnet. @@ -289,6 +310,12 @@ func validateSubnet(s *types.Subnet, addGateway bool) error { if err != nil { return errors.Wrap(err, "subnet invalid") } + + // check that the new subnet does not conflict with existing ones + if util.NetworkIntersectsWithNetworks(net, usedNetworks) { + return errors.Errorf("subnet %s is already used on the host or by another config", net.String()) + } + s.Subnet = types.IPNet{IPNet: *net} if s.Gateway != nil { if !s.Subnet.Contains(s.Gateway) { diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go index f67402657..11ad71870 100644 --- a/libpod/network/cni/config_test.go +++ b/libpod/network/cni/config_test.go @@ -313,6 +313,14 @@ var _ = Describe("Config", func() { Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) Expect(network1.Subnets[0].Gateway.String()).To(Equal("fdcc::1")) Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + + // reload configs from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + // check the the networks are identical + network2, err := libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network1).To(Equal(network2)) }) It("create bridge with ipv6 enabled", func() { @@ -508,6 +516,9 @@ var _ = Describe("Config", func() { Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) + err = libpodNet.NetworkRemove(network1.Name) + Expect(err).To(BeNil()) + endIP := "10.0.0.10" network = types.Network{ Driver: "bridge", @@ -529,6 +540,9 @@ var _ = Describe("Config", func() { Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) + err = libpodNet.NetworkRemove(network1.Name) + Expect(err).To(BeNil()) + network = types.Network{ Driver: "bridge", Subnets: []types.Subnet{ @@ -590,7 +604,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("subnet invalid")) + Expect(err.Error()).To(ContainSubstring("subnet ip is nil")) }) It("create network with name", func() { @@ -886,6 +900,25 @@ var _ = Describe("Config", func() { Expect(err.Error()).To(Equal("default network podman cannot be removed")) }) + It("network create with same subnet", func() { + subnet := "10.0.0.0/24" + n, _ := types.ParseCIDR(subnet) + subnet2 := "10.10.0.0/24" + n2, _ := types.ParseCIDR(subnet2) + network := types.Network{Subnets: []types.Subnet{{Subnet: n}, {Subnet: n2}}} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Subnets).To(HaveLen(2)) + network = types.Network{Subnets: []types.Subnet{{Subnet: n}}} + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("subnet 10.0.0.0/24 is already used on the host or by another config")) + network = types.Network{Subnets: []types.Subnet{{Subnet: n2}}} + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config")) + }) + }) Context("network load valid existing ones", func() { diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go index fde08a0c6..46e07f780 100644 --- a/libpod/network/cni/network.go +++ b/libpod/network/cni/network.go @@ -106,6 +106,12 @@ func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { return n, nil } +// Drivers will return the list of supported network drivers +// for this interface. +func (n *cniNetwork) Drivers() []string { + return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver} +} + func (n *cniNetwork) loadNetworks() error { // skip loading networks if they are already loaded if n.networks != nil { @@ -179,7 +185,7 @@ func (n *cniNetwork) createDefaultNetwork() (*network, error) { {Subnet: n.defaultSubnet}, }, } - return n.networkCreate(net, false) + return n.networkCreate(net, true) } // getNetwork will lookup a network by name or ID. It returns an @@ -221,12 +227,7 @@ func getNetworkIDFromName(name string) string { } // getFreeIPv6NetworkSubnet returns a unused ipv4 subnet -func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) { - networks, err := n.getUsedSubnets() - if err != nil { - return nil, err - } - +func (n *cniNetwork) getFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) { // the default podman network is 10.88.0.0/16 // start locking for free /24 networks network := &net.IPNet{ @@ -236,12 +237,13 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) { // TODO: make sure to not use public subnets for { - if intersectsConfig := util.NetworkIntersectsWithNetworks(network, networks); !intersectsConfig { + if intersectsConfig := util.NetworkIntersectsWithNetworks(network, usedNetworks); !intersectsConfig { logrus.Debugf("found free ipv4 network subnet %s", network.String()) return &types.Subnet{ Subnet: types.IPNet{IPNet: *network}, }, nil } + var err error network, err = util.NextSubnet(network) if err != nil { return nil, err @@ -250,12 +252,7 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) { } // getFreeIPv6NetworkSubnet returns a unused ipv6 subnet -func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) { - networks, err := n.getUsedSubnets() - if err != nil { - return nil, err - } - +func (n *cniNetwork) getFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) { // FIXME: Is 10000 fine as limit? We should prevent an endless loop. for i := 0; i < 10000; i++ { // RFC4193: Choose the ipv6 subnet random and NOT sequentially. @@ -263,7 +260,7 @@ func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) { if err != nil { return nil, err } - if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, networks); !intersectsConfig { + if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, usedNetworks); !intersectsConfig { logrus.Debugf("found free ipv6 network subnet %s", network.String()) return &types.Subnet{ Subnet: types.IPNet{IPNet: network}, @@ -279,9 +276,8 @@ func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) { // first, load all used subnets from network configs subnets := make([]*net.IPNet, 0, len(n.networks)) for _, val := range n.networks { - for _, subnet := range val.libpodNet.Subnets { - // nolint:exportloopref - subnets = append(subnets, &subnet.Subnet.IPNet) + for i := range val.libpodNet.Subnets { + subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet) } } // second, load networks from the current system diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go index 14634262c..b69953c4b 100644 --- a/libpod/network/cni/run.go +++ b/libpod/network/cni/run.go @@ -114,7 +114,7 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma } logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, cnires) var status types.StatusBlock - status, retErr = cniResultToStatus(cnires) + status, retErr = CNIResultToStatus(cnires) if retErr != nil { return nil, retErr } @@ -123,8 +123,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma return results, nil } -// cniResultToStatus convert the cni result to status block -func cniResultToStatus(cniResult *current.Result) (types.StatusBlock, error) { +// CNIResultToStatus convert the cni result to status block +// nolint:golint +func CNIResultToStatus(cniResult *current.Result) (types.StatusBlock, error) { result := types.StatusBlock{} nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers)) for _, nameserver := range cniResult.DNS.Nameservers { diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go index 32e88ca61..f6da22a76 100644 --- a/libpod/network/cni/run_test.go +++ b/libpod/network/cni/run_test.go @@ -140,6 +140,10 @@ var _ = Describe("run CNI", func() { Expect(res[defNet].DNSServerIPs).To(BeEmpty()) Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) + // reload the interface so the networks are reload from disk + libpodNet, err := getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) Expect(err).To(BeNil()) }) diff --git a/libpod/network/config.go b/libpod/network/config.go deleted file mode 100644 index 9a3bc4763..000000000 --- a/libpod/network/config.go +++ /dev/null @@ -1,159 +0,0 @@ -package network - -import ( - "encoding/json" - "net" - - "github.com/containers/storage/pkg/lockfile" -) - -// TODO once the containers.conf file stuff is worked out, this should be modified -// to honor defines in the containers.conf as well as overrides? - -const ( - // CNIConfigDir is the path where CNI config files exist - CNIConfigDir = "/etc/cni/net.d" - // CNIDeviceName is the default network device name and in - // reality should have an int appended to it (cni-podman4) - CNIDeviceName = "cni-podman" - // DefaultPodmanDomainName is used for the dnsname plugin to define - // a localized domain name for a created network - DefaultPodmanDomainName = "dns.podman" - // LockFileName is used for obtaining a lock and is appended - // to libpod's tmpdir in practice - LockFileName = "cni.lock" -) - -// CNILock is for preventing name collision and -// unpredictable results when doing some CNI operations. -type CNILock struct { - lockfile.Locker -} - -// GetDefaultPodmanNetwork outputs the default network for podman -func GetDefaultPodmanNetwork() (*net.IPNet, error) { - _, n, err := net.ParseCIDR("10.88.1.0/24") - return n, err -} - -// CNIPlugins is a way of marshalling a CNI network configuration to disk -type CNIPlugins interface { - Bytes() ([]byte, error) -} - -// HostLocalBridge describes a configuration for a bridge plugin -// https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference -type HostLocalBridge struct { - PluginType string `json:"type"` - BrName string `json:"bridge,omitempty"` - IsGW bool `json:"isGateway"` - IsDefaultGW bool `json:"isDefaultGateway,omitempty"` - ForceAddress bool `json:"forceAddress,omitempty"` - IPMasq bool `json:"ipMasq,omitempty"` - MTU int `json:"mtu,omitempty"` - HairpinMode bool `json:"hairpinMode,omitempty"` - PromiscMode bool `json:"promiscMode,omitempty"` - Vlan int `json:"vlan,omitempty"` - IPAM IPAMConfig `json:"ipam"` -} - -// Bytes outputs []byte -func (h *HostLocalBridge) Bytes() ([]byte, error) { - return json.MarshalIndent(h, "", "\t") -} - -// IPAMConfig describes an IPAM configuration -// https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local#network-configuration-reference -type IPAMConfig struct { - PluginType string `json:"type"` - Routes []IPAMRoute `json:"routes,omitempty"` - ResolveConf string `json:"resolveConf,omitempty"` - DataDir string `json:"dataDir,omitempty"` - Ranges [][]IPAMLocalHostRangeConf `json:"ranges,omitempty"` -} - -// IPAMLocalHostRangeConf describes the new style IPAM ranges -type IPAMLocalHostRangeConf struct { - Subnet string `json:"subnet"` - RangeStart string `json:"rangeStart,omitempty"` - RangeEnd string `json:"rangeEnd,omitempty"` - Gateway string `json:"gateway,omitempty"` -} - -// Bytes outputs the configuration as []byte -func (i IPAMConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(i, "", "\t") -} - -// IPAMRoute describes a route in an ipam config -type IPAMRoute struct { - Dest string `json:"dst"` -} - -// PortMapConfig describes the default portmapping config -type PortMapConfig struct { - PluginType string `json:"type"` - Capabilities map[string]bool `json:"capabilities"` -} - -// Bytes outputs the configuration as []byte -func (p PortMapConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} - -// MacVLANConfig describes the macvlan config -type MacVLANConfig struct { - PluginType string `json:"type"` - Master string `json:"master"` - IPAM IPAMConfig `json:"ipam"` - MTU int `json:"mtu,omitempty"` -} - -// Bytes outputs the configuration as []byte -func (p MacVLANConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} - -// FirewallConfig describes the firewall plugin -type FirewallConfig struct { - PluginType string `json:"type"` - Backend string `json:"backend"` -} - -// Bytes outputs the configuration as []byte -func (f FirewallConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(f, "", "\t") -} - -// TuningConfig describes the tuning plugin -type TuningConfig struct { - PluginType string `json:"type"` -} - -// Bytes outputs the configuration as []byte -func (f TuningConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(f, "", "\t") -} - -// DNSNameConfig describes the dns container name resolution plugin config -type DNSNameConfig struct { - PluginType string `json:"type"` - DomainName string `json:"domainName"` - Capabilities map[string]bool `json:"capabilities"` -} - -// PodmanMachineConfig enables port handling on the host OS -type PodmanMachineConfig struct { - PluginType string `json:"type"` - Capabilities map[string]bool `json:"capabilities"` -} - -// Bytes outputs the configuration as []byte -func (d DNSNameConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(d, "", "\t") -} - -// Bytes outputs the configuration as []byte -func (p PodmanMachineConfig) Bytes() ([]byte, error) { - return json.MarshalIndent(p, "", "\t") -} diff --git a/libpod/network/create.go b/libpod/network/create.go deleted file mode 100644 index aca8150b5..000000000 --- a/libpod/network/create.go +++ /dev/null @@ -1,310 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - - "github.com/containernetworking/cni/pkg/version" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// Create the CNI network -func Create(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (*entities.NetworkCreateReport, error) { - var fileName string - if err := isSupportedDriver(options.Driver); err != nil { - return nil, err - } - // Acquire a lock for CNI - l, err := acquireCNILock(runtimeConfig) - if err != nil { - return nil, err - } - defer l.releaseCNILock() - if len(options.MacVLAN) > 0 || options.Driver == MacVLANNetworkDriver { - fileName, err = createMacVLAN(name, options, runtimeConfig) - } else { - fileName, err = createBridge(name, options, runtimeConfig) - } - if err != nil { - return nil, err - } - return &entities.NetworkCreateReport{Filename: fileName}, nil -} - -// validateBridgeOptions validate the bridge networking options -func validateBridgeOptions(options entities.NetworkCreateOptions) error { - subnet := &options.Subnet - ipRange := &options.Range - gateway := options.Gateway - // if IPv6 is set an IPv6 subnet MUST be specified - if options.IPv6 && ((subnet.IP == nil) || (subnet.IP != nil && !IsIPv6(subnet.IP))) { - return errors.Errorf("ipv6 option requires an IPv6 --subnet to be provided") - } - // range and gateway depend on subnet - if subnet.IP == nil && (ipRange.IP != nil || gateway != nil) { - return errors.Errorf("every ip-range or gateway must have a corresponding subnet") - } - - // if a range is given, we need to ensure it is "in" the network range. - if ipRange.IP != nil { - firstIP, err := FirstIPInSubnet(ipRange) - if err != nil { - return errors.Wrapf(err, "failed to get first IP address from ip-range") - } - lastIP, err := LastIPInSubnet(ipRange) - if err != nil { - return errors.Wrapf(err, "failed to get last IP address from ip-range") - } - if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { - return errors.Errorf("the ip range %s does not fall within the subnet range %s", ipRange.String(), subnet.String()) - } - } - - // if network is provided and if gateway is provided, make sure it is "in" network - if gateway != nil && !subnet.Contains(gateway) { - return errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) - } - - return nil -} - -// parseMTU parses the mtu option -func parseMTU(mtu string) (int, error) { - if mtu == "" { - return 0, nil // default - } - m, err := strconv.Atoi(mtu) - if err != nil { - return 0, err - } - if m < 0 { - return 0, errors.Errorf("the value %d for mtu is less than zero", m) - } - return m, nil -} - -// parseVlan parses the vlan option -func parseVlan(vlan string) (int, error) { - if vlan == "" { - return 0, nil // default - } - return strconv.Atoi(vlan) -} - -// createBridge creates a CNI network -func createBridge(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { - var ( - ipamRanges [][]IPAMLocalHostRangeConf - err error - routes []IPAMRoute - ) - isGateway := true - ipMasq := true - - // validate options - if err := validateBridgeOptions(options); err != nil { - return "", err - } - - // For compatibility with the docker implementation: - // if IPv6 is enabled (it really means dual-stack) then an IPv6 subnet has to be provided, and one free network is allocated for IPv4 - // if IPv6 is not specified the subnet may be specified and can be either IPv4 or IPv6 (podman, unlike docker, allows IPv6 only networks) - // If not subnet is specified an IPv4 subnet will be allocated - subnet := &options.Subnet - ipRange := &options.Range - gateway := options.Gateway - if subnet.IP != nil { - // if network is provided, does it conflict with existing CNI or live networks - err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet) - if err != nil { - return "", err - } - // obtain CNI subnet default route - defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) - if err != nil { - return "", err - } - routes = append(routes, defaultRoute) - // obtain CNI range - ipamRange, err := NewIPAMLocalHostRange(subnet, ipRange, gateway) - if err != nil { - return "", err - } - ipamRanges = append(ipamRanges, ipamRange) - } - // if no network is provided or IPv6 flag used, figure out the IPv4 network - if options.IPv6 || len(routes) == 0 { - subnetV4, err := GetFreeNetwork(runtimeConfig) - if err != nil { - return "", err - } - // obtain IPv4 default route - defaultRoute, err := NewIPAMDefaultRoute(false) - if err != nil { - return "", err - } - routes = append(routes, defaultRoute) - // the CNI bridge plugin does not need to set - // the range or gateway options explicitly - ipamRange, err := NewIPAMLocalHostRange(subnetV4, nil, nil) - if err != nil { - return "", err - } - ipamRanges = append(ipamRanges, ipamRange) - } - - // create CNI config - ipamConfig, err := NewIPAMHostLocalConf(routes, ipamRanges) - if err != nil { - return "", err - } - - if options.Internal { - isGateway = false - ipMasq = false - } - - var mtu int - var vlan int - for k, v := range options.Options { - var err error - switch k { - case "mtu": - mtu, err = parseMTU(v) - if err != nil { - return "", err - } - - case "vlan": - vlan, err = parseVlan(v) - if err != nil { - return "", err - } - - default: - return "", errors.Errorf("unsupported option %s", k) - } - } - - // obtain host bridge name - bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig) - if err != nil { - return "", err - } - - if len(name) > 0 { - netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) - if err != nil { - return "", err - } - if util.StringInSlice(name, netNames) { - return "", errors.Errorf("the network name %s is already used", name) - } - } else { - // If no name is given, we give the name of the bridge device - name = bridgeDeviceName - } - - // create CNI plugin configuration - ncList := NewNcList(name, version.Current(), options.Labels) - var plugins []CNIPlugins - // TODO need to iron out the role of isDefaultGW and IPMasq - bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, mtu, vlan, ipamConfig) - plugins = append(plugins, bridge) - plugins = append(plugins, NewPortMapPlugin()) - plugins = append(plugins, NewFirewallPlugin()) - plugins = append(plugins, NewTuningPlugin()) - // if we find the dnsname plugin we add configuration for it - if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS { - if options.Internal { - logrus.Warnf("dnsname and --internal networks are incompatible. dnsname plugin not configured for network %s", name) - } else { - // Note: in the future we might like to allow for dynamic domain names - plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName)) - } - } - // Add the podman-machine CNI plugin if we are in a machine - if runtimeConfig.MachineEnabled() { // check if we are in a machine vm - plugins = append(plugins, NewPodmanMachinePlugin()) - } - ncList["plugins"] = plugins - b, err := json.MarshalIndent(ncList, "", " ") - if err != nil { - return "", err - } - if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil { - return "", err - } - cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) - err = ioutil.WriteFile(cniPathName, b, 0644) - return cniPathName, err -} - -func createMacVLAN(name string, options entities.NetworkCreateOptions, runtimeConfig *config.Config) (string, error) { - var ( - mtu int - plugins []CNIPlugins - ) - liveNetNames, err := GetLiveNetworkNames() - if err != nil { - return "", err - } - - // The parent can be defined with --macvlan or as an option (-o parent:device) - parentNetworkDevice := options.MacVLAN - if len(parentNetworkDevice) < 1 { - if parent, ok := options.Options["parent"]; ok { - parentNetworkDevice = parent - } - } - - // Make sure the host-device exists if provided - if len(parentNetworkDevice) > 0 && !util.StringInSlice(parentNetworkDevice, liveNetNames) { - return "", errors.Errorf("failed to find network interface %q", parentNetworkDevice) - } - if len(name) > 0 { - netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) - if err != nil { - return "", err - } - if util.StringInSlice(name, netNames) { - return "", errors.Errorf("the network name %s is already used", name) - } - } else { - name, err = GetFreeDeviceName(runtimeConfig) - if err != nil { - return "", err - } - } - ncList := NewNcList(name, version.Current(), options.Labels) - if val, ok := options.Options["mtu"]; ok { - intVal, err := strconv.Atoi(val) - if err != nil { - return "", err - } - if intVal > 0 { - mtu = intVal - } - } - macvlan, err := NewMacVLANPlugin(parentNetworkDevice, options.Gateway, &options.Range, &options.Subnet, mtu) - if err != nil { - return "", err - } - plugins = append(plugins, macvlan) - ncList["plugins"] = plugins - b, err := json.MarshalIndent(ncList, "", " ") - if err != nil { - return "", err - } - cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) - err = ioutil.WriteFile(cniPathName, b, 0644) - return cniPathName, err -} diff --git a/libpod/network/create_test.go b/libpod/network/create_test.go deleted file mode 100644 index c3824bd91..000000000 --- a/libpod/network/create_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package network - -import ( - "net" - "testing" - - "github.com/containers/podman/v3/pkg/domain/entities" -) - -func Test_validateBridgeOptions(t *testing.T) { - tests := []struct { - name string - subnet net.IPNet - ipRange net.IPNet - gateway net.IP - isIPv6 bool - wantErr bool - }{ - { - name: "IPv4 subnet only", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - }, - { - name: "IPv4 subnet and range", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - }, - { - name: "IPv4 subnet and gateway", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - gateway: net.ParseIP("192.168.0.10"), - }, - { - name: "IPv4 subnet, range and gateway", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - }, - { - name: "IPv6 subnet only", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - }, - { - name: "IPv6 subnet and range", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - isIPv6: true, - }, - { - name: "IPv6 subnet and gateway", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - }, - { - name: "IPv6 subnet, range and gateway", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - }, - { - name: "IPv6 subnet, range and gateway without IPv6 option (PODMAN SUPPORTS IT UNLIKE DOCKER)", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:DB8:0:0:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: false, - }, - { - name: "range provided but not subnet", - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - wantErr: true, - }, - { - name: "gateway provided but not subnet", - gateway: net.ParseIP("192.168.0.10"), - wantErr: true, - }, - { - name: "IPv4 subnet but IPv6 required", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - isIPv6: true, - wantErr: true, - }, - { - name: "IPv6 required but IPv4 options used", - subnet: net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gateway: net.ParseIP("192.168.0.10"), - isIPv6: true, - wantErr: true, - }, - { - name: "IPv6 required but not subnet provided", - isIPv6: true, - wantErr: true, - }, - { - name: "range out of the subnet", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: net.IPNet{IP: net.ParseIP("2001:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001:DB8::2"), - isIPv6: true, - wantErr: true, - }, - { - name: "gateway out of the subnet", - subnet: net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - gateway: net.ParseIP("2001::2"), - isIPv6: true, - wantErr: true, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - options := entities.NetworkCreateOptions{ - Subnet: tt.subnet, - Range: tt.ipRange, - Gateway: tt.gateway, - IPv6: tt.isIPv6, - } - if err := validateBridgeOptions(options); (err != nil) != tt.wantErr { - t.Errorf("validateBridgeOptions() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/libpod/network/devices.go b/libpod/network/devices.go deleted file mode 100644 index fc9aff337..000000000 --- a/libpod/network/devices.go +++ /dev/null @@ -1,59 +0,0 @@ -package network - -import ( - "fmt" - - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/util" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// GetFreeDeviceName returns a device name that is unused; used when no network -// name is provided by user -func GetFreeDeviceName(config *config.Config) (string, error) { - var ( - deviceNum uint - deviceName string - ) - networkNames, err := GetNetworkNamesFromFileSystem(config) - if err != nil { - return "", err - } - liveNetworksNames, err := GetLiveNetworkNames() - if err != nil { - return "", err - } - bridgeNames, err := GetBridgeNamesFromFileSystem(config) - if err != nil { - return "", err - } - for { - deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum) - logrus.Debugf("checking if device name %q exists in other cni networks", deviceName) - if util.StringInSlice(deviceName, networkNames) { - deviceNum++ - continue - } - logrus.Debugf("checking if device name %q exists in live networks", deviceName) - if util.StringInSlice(deviceName, liveNetworksNames) { - deviceNum++ - continue - } - logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName) - if !util.StringInSlice(deviceName, bridgeNames) { - break - } - deviceNum++ - } - return deviceName, nil -} - -// RemoveInterface removes an interface by the given name -func RemoveInterface(interfaceName string) error { - link, err := netlink.LinkByName(interfaceName) - if err != nil { - return err - } - return netlink.LinkDel(link) -} diff --git a/libpod/network/files.go b/libpod/network/files.go deleted file mode 100644 index d876113f9..000000000 --- a/libpod/network/files.go +++ /dev/null @@ -1,211 +0,0 @@ -package network - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "sort" - "strings" - - "github.com/containernetworking/cni/libcni" - "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/network" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ErrNoSuchNetworkInterface indicates that no network interface exists -var ErrNoSuchNetworkInterface = errors.New("unable to find interface name for network") - -// GetCNIConfDir get CNI configuration directory -func GetCNIConfDir(configArg *config.Config) string { - if len(configArg.Network.NetworkConfigDir) < 1 { - dc, err := config.DefaultConfig() - if err != nil { - // Fallback to hard-coded dir - return CNIConfigDir - } - return dc.Network.NetworkConfigDir - } - return configArg.Network.NetworkConfigDir -} - -// LoadCNIConfsFromDir loads all the CNI configurations from a dir -func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) { - files, err := libcni.ConfFiles(dir, []string{".conflist"}) - if err != nil { - return nil, err - } - sort.Strings(files) - - configs := make([]*libcni.NetworkConfigList, 0, len(files)) - for _, confFile := range files { - conf, err := libcni.ConfListFromFile(confFile) - if err != nil { - return nil, errors.Wrapf(err, "in %s", confFile) - } - configs = append(configs, conf) - } - return configs, nil -} - -// GetCNIConfigPathByNameOrID finds a CNI network by name and -// returns its configuration file path -func GetCNIConfigPathByNameOrID(config *config.Config, name string) (string, error) { - files, err := libcni.ConfFiles(GetCNIConfDir(config), []string{".conflist"}) - if err != nil { - return "", err - } - idMatch := 0 - file := "" - for _, confFile := range files { - conf, err := libcni.ConfListFromFile(confFile) - if err != nil { - return "", errors.Wrapf(err, "in %s", confFile) - } - if conf.Name == name { - return confFile, nil - } - if strings.HasPrefix(network.GetNetworkID(conf.Name), name) { - idMatch++ - file = confFile - } - } - if idMatch == 1 { - return file, nil - } - if idMatch > 1 { - return "", errors.Errorf("more than one result for network ID %s", name) - } - return "", errors.Wrap(define.ErrNoSuchNetwork, fmt.Sprintf("unable to find network configuration for %s", name)) -} - -// ReadRawCNIConfByNameOrID reads the raw CNI configuration for a CNI -// network by name -func ReadRawCNIConfByNameOrID(config *config.Config, name string) ([]byte, error) { - confFile, err := GetCNIConfigPathByNameOrID(config, name) - if err != nil { - return nil, err - } - b, err := ioutil.ReadFile(confFile) - return b, err -} - -// GetNetworkLabels returns a list of labels as a string -func GetNetworkLabels(list *libcni.NetworkConfigList) NcLabels { - cniJSON := make(map[string]interface{}) - err := json.Unmarshal(list.Bytes, &cniJSON) - if err != nil { - logrus.Errorf("failed to unmarshal network config %v %v", cniJSON["name"], err) - return nil - } - if args, ok := cniJSON["args"]; ok { - if key, ok := args.(map[string]interface{}); ok { - if labels, ok := key[PodmanLabelKey]; ok { - if labels, ok := labels.(map[string]interface{}); ok { - result := make(NcLabels, len(labels)) - for k, v := range labels { - if v, ok := v.(string); ok { - result[k] = v - } else { - logrus.Errorf("network config %v invalid label value type %T should be string", cniJSON["name"], labels) - } - } - return result - } - logrus.Errorf("network config %v invalid label type %T should be map[string]string", cniJSON["name"], labels) - } - } - } - return nil -} - -// GetNetworksFromFilesystem gets all the networks from the cni configuration -// files -func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) { - var cniNetworks []*allocator.Net - - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - for _, n := range networks { - for _, cniplugin := range n.Plugins { - if cniplugin.Network.Type == "bridge" { - ipamConf := allocator.Net{} - if err := json.Unmarshal(cniplugin.Bytes, &ipamConf); err != nil { - return nil, err - } - cniNetworks = append(cniNetworks, &ipamConf) - break - } - } - } - return cniNetworks, nil -} - -// GetNetworkNamesFromFileSystem gets all the names from the cni network -// configuration files -func GetNetworkNamesFromFileSystem(config *config.Config) ([]string, error) { - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - networkNames := []string{} - for _, n := range networks { - networkNames = append(networkNames, n.Name) - } - return networkNames, nil -} - -// GetInterfaceNameFromConfig returns the interface name for the bridge plugin -func GetInterfaceNameFromConfig(path string) (string, error) { - var name string - conf, err := libcni.ConfListFromFile(path) - if err != nil { - return "", err - } - for _, cniplugin := range conf.Plugins { - if cniplugin.Network.Type == "bridge" { - plugin := make(map[string]interface{}) - if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil { - return "", err - } - name = plugin["bridge"].(string) - break - } - } - if len(name) == 0 { - return "", ErrNoSuchNetworkInterface - } - return name, nil -} - -// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge -// names from the configured networks -func GetBridgeNamesFromFileSystem(config *config.Config) ([]string, error) { - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) - if err != nil { - return nil, err - } - - bridgeNames := []string{} - for _, n := range networks { - var name string - // iterate network conflists - for _, cniplugin := range n.Plugins { - // iterate plugins - if cniplugin.Network.Type == "bridge" { - plugin := make(map[string]interface{}) - if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil { - continue - } - name = plugin["bridge"].(string) - } - } - bridgeNames = append(bridgeNames, name) - } - return bridgeNames, nil -} diff --git a/libpod/network/ip.go b/libpod/network/ip.go deleted file mode 100644 index ba93a0d05..000000000 --- a/libpod/network/ip.go +++ /dev/null @@ -1,19 +0,0 @@ -package network - -import ( - "net" - - "github.com/containernetworking/plugins/pkg/ip" -) - -// CalcGatewayIP takes a network and returns the first IP in it. -func CalcGatewayIP(ipn *net.IPNet) net.IP { - // taken from cni bridge plugin as it is not exported - nid := ipn.IP.Mask(ipn.Mask) - return ip.NextIP(nid) -} - -// IsIPv6 returns if netIP is IPv6. -func IsIPv6(netIP net.IP) bool { - return netIP != nil && netIP.To4() == nil -} diff --git a/libpod/network/lock.go b/libpod/network/lock.go deleted file mode 100644 index 037f41efa..000000000 --- a/libpod/network/lock.go +++ /dev/null @@ -1,35 +0,0 @@ -package network - -import ( - "os" - "path/filepath" - - "github.com/containers/common/pkg/config" - "github.com/containers/storage" -) - -// acquireCNILock gets a lock that should be used in create and -// delete cases to avoid unwanted collisions in network names. -// TODO this uses a file lock and should be converted to shared memory -// when we have a more general shared memory lock in libpod -func acquireCNILock(config *config.Config) (*CNILock, error) { - cniDir := GetCNIConfDir(config) - err := os.MkdirAll(cniDir, 0755) - if err != nil { - return nil, err - } - l, err := storage.GetLockfile(filepath.Join(cniDir, LockFileName)) - if err != nil { - return nil, err - } - l.Lock() - cnilock := CNILock{ - Locker: l, - } - return &cnilock, nil -} - -// ReleaseCNILock unlocks the previously held lock -func (l *CNILock) releaseCNILock() { - l.Unlock() -} diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go deleted file mode 100644 index d6c33740e..000000000 --- a/libpod/network/netconflist.go +++ /dev/null @@ -1,312 +0,0 @@ -package network - -import ( - "net" - "os" - "path/filepath" - "strings" - "syscall" - "time" - - "github.com/containernetworking/cni/libcni" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/pkg/network" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" -) - -const ( - defaultIPv4Route = "0.0.0.0/0" - defaultIPv6Route = "::/0" -) - -// NcList describes a generic map -type NcList map[string]interface{} - -// NcArgs describes the cni args field -type NcArgs map[string]NcLabels - -// NcLabels describes the label map -type NcLabels map[string]string - -// PodmanLabelKey key used to store the podman network label in a cni config -const PodmanLabelKey = "podman_labels" - -// NewNcList creates a generic map of values with string -// keys and adds in version and network name -func NewNcList(name, version string, labels NcLabels) NcList { - n := NcList{} - n["cniVersion"] = version - n["name"] = name - if len(labels) > 0 { - n["args"] = NcArgs{PodmanLabelKey: labels} - } - return n -} - -// NewHostLocalBridge creates a new LocalBridge for host-local -func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, mtu int, vlan int, ipamConf IPAMConfig) *HostLocalBridge { - hostLocalBridge := HostLocalBridge{ - PluginType: "bridge", - BrName: name, - IPMasq: ipMasq, - MTU: mtu, - HairpinMode: true, - Vlan: vlan, - IPAM: ipamConf, - } - if isGateWay { - hostLocalBridge.IsGW = true - } - if isDefaultGW { - hostLocalBridge.IsDefaultGW = true - } - return &hostLocalBridge -} - -// NewIPAMHostLocalConf creates a new IPAMHostLocal configuration -func NewIPAMHostLocalConf(routes []IPAMRoute, ipamRanges [][]IPAMLocalHostRangeConf) (IPAMConfig, error) { - ipamConf := IPAMConfig{ - PluginType: "host-local", - Routes: routes, - // Possible future support ? Leaving for clues - //ResolveConf: "", - //DataDir: "" - } - - ipamConf.Ranges = ipamRanges - return ipamConf, nil -} - -// NewIPAMLocalHostRange create a new IPAM range -func NewIPAMLocalHostRange(subnet *net.IPNet, ipRange *net.IPNet, gw net.IP) ([]IPAMLocalHostRangeConf, error) { //nolint:interfacer - var ranges []IPAMLocalHostRangeConf - hostRange := IPAMLocalHostRangeConf{ - Subnet: subnet.String(), - } - // an user provided a range, we add it here - if ipRange != nil && ipRange.IP != nil { - first, err := FirstIPInSubnet(ipRange) - if err != nil { - return nil, err - } - last, err := LastIPInSubnet(ipRange) - if err != nil { - return nil, err - } - hostRange.RangeStart = first.String() - hostRange.RangeEnd = last.String() - } - if gw != nil { - hostRange.Gateway = gw.String() - } else { - // Add first ip in subnet as gateway. It is not required - // by cni but should be included because of network inspect. - hostRange.Gateway = CalcGatewayIP(subnet).String() - } - ranges = append(ranges, hostRange) - return ranges, nil -} - -// NewIPAMRoute creates a new IPAM route configuration -func NewIPAMRoute(r *net.IPNet) IPAMRoute { //nolint:interfacer - return IPAMRoute{Dest: r.String()} -} - -// NewIPAMDefaultRoute creates a new IPAMDefault route of -// 0.0.0.0/0 for IPv4 or ::/0 for IPv6 -func NewIPAMDefaultRoute(isIPv6 bool) (IPAMRoute, error) { - route := defaultIPv4Route - if isIPv6 { - route = defaultIPv6Route - } - _, n, err := net.ParseCIDR(route) - if err != nil { - return IPAMRoute{}, err - } - return NewIPAMRoute(n), nil -} - -// NewPortMapPlugin creates a predefined, default portmapping -// configuration -func NewPortMapPlugin() PortMapConfig { - caps := make(map[string]bool) - caps["portMappings"] = true - p := PortMapConfig{ - PluginType: "portmap", - Capabilities: caps, - } - return p -} - -// NewFirewallPlugin creates a generic firewall plugin -func NewFirewallPlugin() FirewallConfig { - return FirewallConfig{ - PluginType: "firewall", - } -} - -// NewTuningPlugin creates a generic tuning section -func NewTuningPlugin() TuningConfig { - return TuningConfig{ - PluginType: "tuning", - } -} - -// NewDNSNamePlugin creates the dnsname config with a given -// domainname -func NewDNSNamePlugin(domainName string) DNSNameConfig { - caps := make(map[string]bool, 1) - caps["aliases"] = true - return DNSNameConfig{ - PluginType: "dnsname", - DomainName: domainName, - Capabilities: caps, - } -} - -// HasDNSNamePlugin looks to see if the dnsname cni plugin is present -func HasDNSNamePlugin(paths []string) bool { - for _, p := range paths { - if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil { - return true - } - } - return false -} - -// NewMacVLANPlugin creates a macvlanconfig with a given device name -func NewMacVLANPlugin(device string, gateway net.IP, ipRange *net.IPNet, subnet *net.IPNet, mtu int) (MacVLANConfig, error) { - i := IPAMConfig{PluginType: "dhcp"} - if gateway != nil || - (ipRange != nil && ipRange.IP != nil && ipRange.Mask != nil) || - (subnet != nil && subnet.IP != nil && subnet.Mask != nil) { - ipam, err := NewIPAMLocalHostRange(subnet, ipRange, gateway) - if err != nil { - return MacVLANConfig{}, err - } - ranges := make([][]IPAMLocalHostRangeConf, 0) - ranges = append(ranges, ipam) - i.Ranges = ranges - route, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) - if err != nil { - return MacVLANConfig{}, err - } - i.Routes = []IPAMRoute{route} - i.PluginType = "host-local" - } - - m := MacVLANConfig{ - PluginType: "macvlan", - IPAM: i, - } - if mtu > 0 { - m.MTU = mtu - } - // CNI is supposed to use the default route if a - // parent device is not provided - if len(device) > 0 { - m.Master = device - } - return m, nil -} - -// IfPassesFilter filters NetworkListReport and returns true if the filter match the given config -func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]string) (bool, error) { - result := true - for key, filterValues := range filters { - result = false - switch strings.ToLower(key) { - case "name": - // matches one name, regex allowed - result = util.StringMatchRegexSlice(netconf.Name, filterValues) - - case "plugin": - // match one plugin - plugins := network.GetCNIPlugins(netconf) - for _, val := range filterValues { - if strings.Contains(plugins, val) { - result = true - break - } - } - - case "label": - // matches all labels - result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)) - - case "driver": - // matches only for the DefaultNetworkDriver - for _, filterValue := range filterValues { - plugins := network.GetCNIPlugins(netconf) - if filterValue == DefaultNetworkDriver && - strings.Contains(plugins, DefaultNetworkDriver) { - result = true - } - } - - case "id": - // matches part of one id - for _, filterValue := range filterValues { - if strings.Contains(network.GetNetworkID(netconf.Name), filterValue) { - result = true - break - } - } - - // TODO: add dangling filter - - default: - return false, errors.Errorf("invalid filter %q", key) - } - } - return result, nil -} - -// IfPassesPruneFilter filters NetworkListReport and returns true if the prune filter match the given config -func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigList, f map[string][]string) (bool, error) { - for key, filterValues := range f { - switch strings.ToLower(key) { - case "label": - return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil - case "until": - until, err := util.ComputeUntilTimestamp(filterValues) - if err != nil { - return false, err - } - created, err := getCreatedTimestamp(config, netconf) - if err != nil { - return false, err - } - if created.Before(until) { - return true, nil - } - default: - return false, errors.Errorf("invalid filter %q", key) - } - } - return false, nil -} - -func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) { - networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name) - if err != nil { - return nil, err - } - f, err := os.Stat(networkConfigPath) - if err != nil { - return nil, err - } - stat := f.Sys().(*syscall.Stat_t) - created := time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) // nolint: unconvert - return &created, nil -} - -func NewPodmanMachinePlugin() PodmanMachineConfig { - caps := make(map[string]bool, 1) - caps["portMappings"] = true - return PodmanMachineConfig{ - PluginType: "podman-machine", - Capabilities: caps, - } -} diff --git a/libpod/network/netconflist_test.go b/libpod/network/netconflist_test.go deleted file mode 100644 index 161764ed9..000000000 --- a/libpod/network/netconflist_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package network - -import ( - "net" - "reflect" - "testing" -) - -func TestNewIPAMDefaultRoute(t *testing.T) { - tests := []struct { - name string - isIPv6 bool - want IPAMRoute - }{ - { - name: "IPv4 default route", - isIPv6: false, - want: IPAMRoute{defaultIPv4Route}, - }, - { - name: "IPv6 default route", - isIPv6: true, - want: IPAMRoute{defaultIPv6Route}, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := NewIPAMDefaultRoute(tt.isIPv6) - if err != nil { - t.Errorf("no error expected: %v", err) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewIPAMDefaultRoute() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNewIPAMLocalHostRange(t *testing.T) { - tests := []struct { - name string - subnet *net.IPNet - ipRange *net.IPNet - gw net.IP - want []IPAMLocalHostRangeConf - }{ - { - name: "IPv4 subnet", - subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - want: []IPAMLocalHostRangeConf{ - { - Subnet: "192.168.0.0/24", - Gateway: "192.168.0.1", - }, - }, - }, - { - name: "IPv4 subnet, range and gateway", - subnet: &net.IPNet{IP: net.IPv4(192, 168, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, - ipRange: &net.IPNet{IP: net.IPv4(192, 168, 0, 128), Mask: net.IPv4Mask(255, 255, 255, 128)}, - gw: net.ParseIP("192.168.0.10"), - want: []IPAMLocalHostRangeConf{ - { - Subnet: "192.168.0.0/24", - RangeStart: "192.168.0.129", - RangeEnd: "192.168.0.255", - Gateway: "192.168.0.10", - }, - }, - }, - { - name: "IPv6 subnet", - subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - want: []IPAMLocalHostRangeConf{ - { - Subnet: "2001:db8::/48", - Gateway: "2001:db8::1", - }, - }, - }, - { - name: "IPv6 subnet, range and gateway", - subnet: &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, - ipRange: &net.IPNet{IP: net.ParseIP("2001:DB8:1:1::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::"))}, - gw: net.ParseIP("2001:DB8::2"), - want: []IPAMLocalHostRangeConf{ - { - Subnet: "2001:db8::/48", - RangeStart: "2001:db8:1:1::1", - RangeEnd: "2001:db8:1:1:ffff:ffff:ffff:ffff", - Gateway: "2001:db8::2", - }, - }, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := NewIPAMLocalHostRange(tt.subnet, tt.ipRange, tt.gw) - if err != nil { - t.Errorf("no error expected: %v", err) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewIPAMLocalHostRange() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/libpod/network/network.go b/libpod/network/network.go deleted file mode 100644 index 3b81ce776..000000000 --- a/libpod/network/network.go +++ /dev/null @@ -1,288 +0,0 @@ -package network - -import ( - "encoding/json" - "net" - "os" - - "github.com/containernetworking/cni/libcni" - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/podman/v3/pkg/util" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -var ( - // BridgeNetworkDriver defines the bridge cni driver - BridgeNetworkDriver = "bridge" - // DefaultNetworkDriver is the default network type used - DefaultNetworkDriver = BridgeNetworkDriver - // MacVLANNetworkDriver defines the macvlan cni driver - MacVLANNetworkDriver = "macvlan" -) - -// SupportedNetworkDrivers describes the list of supported drivers -var SupportedNetworkDrivers = []string{BridgeNetworkDriver, MacVLANNetworkDriver} - -// isSupportedDriver checks if the user provided driver is supported -func isSupportedDriver(driver string) error { - if util.StringInSlice(driver, SupportedNetworkDrivers) { - return nil - } - return errors.Errorf("driver '%s' is not supported", driver) -} - -// GetLiveNetworks returns a slice of networks representing what the system -// has defined as network interfaces -func GetLiveNetworks() ([]*net.IPNet, error) { - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, err - } - nets := make([]*net.IPNet, 0, len(addrs)) - for _, address := range addrs { - _, n, err := net.ParseCIDR(address.String()) - if err != nil { - return nil, err - } - nets = append(nets, n) - } - return nets, nil -} - -// GetLiveNetworkNames returns a list of network interfaces on the system -func GetLiveNetworkNames() ([]string, error) { - liveInterfaces, err := net.Interfaces() - if err != nil { - return nil, err - } - interfaceNames := make([]string, 0, len(liveInterfaces)) - for _, i := range liveInterfaces { - interfaceNames = append(interfaceNames, i.Name) - } - return interfaceNames, nil -} - -// GetFreeNetwork looks for a free network according to existing cni configuration -// files and network interfaces. -func GetFreeNetwork(config *config.Config) (*net.IPNet, error) { - networks, err := GetNetworksFromFilesystem(config) - if err != nil { - return nil, err - } - liveNetworks, err := GetLiveNetworks() - if err != nil { - return nil, err - } - nextNetwork, err := GetDefaultPodmanNetwork() - if err != nil { - return nil, err - } - logrus.Debugf("default network is %s", nextNetwork.String()) - for { - newNetwork, err := NextSubnet(nextNetwork) - if err != nil { - return nil, err - } - logrus.Debugf("checking if network %s intersects with other cni networks", nextNetwork.String()) - if intersectsConfig, _ := networkIntersectsWithNetworks(newNetwork, allocatorToIPNets(networks)); intersectsConfig { - logrus.Debugf("network %s is already being used by a cni configuration", nextNetwork.String()) - nextNetwork = newNetwork - continue - } - logrus.Debugf("checking if network %s intersects with any network interfaces", nextNetwork.String()) - if intersectsLive, _ := networkIntersectsWithNetworks(newNetwork, liveNetworks); !intersectsLive { - break - } - logrus.Debugf("network %s is being used by a network interface", nextNetwork.String()) - nextNetwork = newNetwork - } - return nextNetwork, nil -} - -func allocatorToIPNets(networks []*allocator.Net) []*net.IPNet { - var nets []*net.IPNet - for _, network := range networks { - if len(network.IPAM.Ranges) > 0 { - // this is the new IPAM range style - // append each subnet from ipam the rangeset - for _, allocatorRange := range network.IPAM.Ranges { - for _, r := range allocatorRange { - nets = append(nets, newIPNetFromSubnet(r.Subnet)) - } - } - } else { - // looks like the old, deprecated style - nets = append(nets, newIPNetFromSubnet(network.IPAM.Subnet)) - } - } - return nets -} - -func newIPNetFromSubnet(subnet types.IPNet) *net.IPNet { - n := net.IPNet{ - IP: subnet.IP, - Mask: subnet.Mask, - } - return &n -} - -func networkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) (bool, *net.IPNet) { - for _, nw := range networklist { - if networkIntersect(n, nw) { - return true, nw - } - } - return false, nil -} - -func networkIntersect(n1, n2 *net.IPNet) bool { - return n2.Contains(n1.IP) || n1.Contains(n2.IP) -} - -// ValidateUserNetworkIsAvailable returns via an error if a network is available -// to be used -func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) error { - if len(userNet.IP) == 0 || len(userNet.Mask) == 0 { - return errors.Errorf("network %s's ip or mask cannot be empty", userNet.String()) - } - - ones, bit := userNet.Mask.Size() - if ones == 0 || bit == 0 { - return errors.Errorf("network %s's mask is invalid", userNet.String()) - } - - networks, err := GetNetworksFromFilesystem(config) - if err != nil { - return err - } - liveNetworks, err := GetLiveNetworks() - if err != nil { - return err - } - logrus.Debugf("checking if network %s exists in cni networks", userNet.String()) - if intersectsConfig, _ := networkIntersectsWithNetworks(userNet, allocatorToIPNets(networks)); intersectsConfig { - return errors.Errorf("network %s is already being used by a cni configuration", userNet.String()) - } - logrus.Debugf("checking if network %s exists in any network interfaces", userNet.String()) - if intersectsLive, _ := networkIntersectsWithNetworks(userNet, liveNetworks); intersectsLive { - return errors.Errorf("network %s is being used by a network interface", userNet.String()) - } - return nil -} - -// removeNetwork is removes a cni network without a lock and should only be called -// when a lock was otherwise acquired. -func removeNetwork(config *config.Config, name string) error { - cniPath, err := GetCNIConfigPathByNameOrID(config, name) - if err != nil { - return err - } - // Before we delete the configuration file, we need to make sure we can read and parse - // it to get the network interface name so we can remove that too - interfaceName, err := GetInterfaceNameFromConfig(cniPath) - if err == nil { - // Don't try to remove the network interface if we are not root - if !rootless.IsRootless() { - liveNetworkNames, err := GetLiveNetworkNames() - if err != nil { - return errors.Wrapf(err, "failed to get live network names") - } - if util.StringInSlice(interfaceName, liveNetworkNames) { - if err = RemoveInterface(interfaceName); err != nil { - // only log the error, it is not fatal - logrus.Infof("failed to remove network interface %s: %v", interfaceName, err) - } - } - } - } else if err != ErrNoSuchNetworkInterface { - // Don't error if we couldn't find the network interface name - return err - } - // Remove the configuration file - if err := os.Remove(cniPath); err != nil { - return errors.Wrap(err, "failed to remove network configuration") - } - return nil -} - -// RemoveNetwork removes a given network by name. If the network has container associated with it, that -// must be handled outside the context of this. -func RemoveNetwork(config *config.Config, name string) error { - l, err := acquireCNILock(config) - if err != nil { - return err - } - defer l.releaseCNILock() - return removeNetwork(config, name) -} - -// InspectNetwork reads a CNI config and returns its configuration -func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) { - b, err := ReadRawCNIConfByNameOrID(config, name) - if err != nil { - return nil, err - } - rawList := make(map[string]interface{}) - err = json.Unmarshal(b, &rawList) - return rawList, err -} - -// Exists says whether a given network exists or not; it meant -// specifically for restful responses so 404s can be used -func Exists(config *config.Config, name string) (bool, error) { - _, err := ReadRawCNIConfByNameOrID(config, name) - if err != nil { - if errors.Cause(err) == define.ErrNoSuchNetwork { - return false, nil - } - return false, err - } - return true, nil -} - -// PruneNetworks removes networks that are not being used and that is not the default -// network. To keep proper fencing for imports, you must provide the used networks -// to this function as a map. the key is meaningful in the map, the book is a no-op -func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) { - var reports []*entities.NetworkPruneReport - lock, err := acquireCNILock(rtc) - if err != nil { - return nil, err - } - defer lock.releaseCNILock() - nets, err := GetNetworkNamesFromFileSystem(rtc) - if err != nil { - return nil, err - } - for _, n := range nets { - _, found := usedNetworks[n] - // Remove is not default network and not found in the used list - if n != rtc.Network.DefaultNetwork && !found { - reports = append(reports, &entities.NetworkPruneReport{ - Name: n, - Error: removeNetwork(rtc, n), - }) - } - } - return reports, nil -} - -// NormalizeName translates a network ID into a name. -// If the input is a name the name is returned. -func NormalizeName(config *config.Config, nameOrID string) (string, error) { - path, err := GetCNIConfigPathByNameOrID(config, nameOrID) - if err != nil { - return "", err - } - conf, err := libcni.ConfListFromFile(path) - if err != nil { - return "", err - } - return conf.Name, nil -} diff --git a/libpod/network/network_test.go b/libpod/network/network_test.go deleted file mode 100644 index 1969e792c..000000000 --- a/libpod/network/network_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package network - -import ( - "net" - "testing" -) - -func parseCIDR(n string) *net.IPNet { - _, parsedNet, _ := net.ParseCIDR(n) - return parsedNet -} - -func Test_networkIntersect(t *testing.T) { - type args struct { - n1 *net.IPNet - n2 *net.IPNet - } - tests := []struct { - name string - args args - want bool - }{ - {"16 and 24 intersects", args{n1: parseCIDR("192.168.0.0/16"), n2: parseCIDR("192.168.1.0/24")}, true}, - {"24 and 25 intersects", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.1.0/25")}, true}, - {"Two 24s", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.2.0/24")}, false}, - } - for _, tt := range tests { - test := tt - t.Run(tt.name, func(t *testing.T) { - if got := networkIntersect(test.args.n1, test.args.n2); got != test.want { - t.Errorf("networkIntersect() = %v, want %v", got, test.want) - } - }) - } -} diff --git a/libpod/network/subnet.go b/libpod/network/subnet.go deleted file mode 100644 index 120038e57..000000000 --- a/libpod/network/subnet.go +++ /dev/null @@ -1,78 +0,0 @@ -package network - -/* - The code in this was kindly contributed by Dan Williams(dcbw@redhat.com). Many thanks - for his contributions. -*/ - -import ( - "fmt" - "net" -) - -func incByte(subnet *net.IPNet, idx int, shift uint) error { - if idx < 0 { - return fmt.Errorf("no more subnets left") - } - if subnet.IP[idx] == 255 { - subnet.IP[idx] = 0 - return incByte(subnet, idx-1, 0) - } - subnet.IP[idx] += 1 << shift - return nil -} - -// NextSubnet returns subnet incremented by 1 -func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) { - newSubnet := &net.IPNet{ - IP: subnet.IP, - Mask: subnet.Mask, - } - ones, bits := newSubnet.Mask.Size() - if ones == 0 { - return nil, fmt.Errorf("%s has only one subnet", subnet.String()) - } - zeroes := uint(bits - ones) - shift := zeroes % 8 - idx := ones/8 - 1 - if idx < 0 { - idx = 0 - } - if err := incByte(newSubnet, idx, shift); err != nil { - return nil, err - } - return newSubnet, nil -} - -// LastIPInSubnet gets the last IP in a subnet -func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - for i := range cidr.IP { - cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i] - } - return cidr.IP, nil -} - -// FirstIPInSubnet gets the first IP in a subnet -func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer - // re-parse to ensure clean network address - _, cidr, err := net.ParseCIDR(addr.String()) - if err != nil { - return nil, err - } - ones, bits := cidr.Mask.Size() - if ones == bits { - return cidr.IP, nil - } - cidr.IP[len(cidr.IP)-1]++ - return cidr.IP, nil -} diff --git a/libpod/network/subnet_test.go b/libpod/network/subnet_test.go deleted file mode 100644 index 55b2443bd..000000000 --- a/libpod/network/subnet_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package network - -import ( - "net" - "reflect" - "testing" -) - -func TestNextSubnet(t *testing.T) { - type args struct { - subnet *net.IPNet - } - tests := []struct { - name string - args args - want *net.IPNet - wantErr bool - }{ - {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false}, - {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := NextSubnet(test.args.subnet) - if (err != nil) != test.wantErr { - t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("NextSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestFirstIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := FirstIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} - -func TestLastIPInSubnet(t *testing.T) { - tests := []struct { - name string - args *net.IPNet - want net.IP - wantErr bool - }{ - {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false}, - {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false}, - {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false}, - {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false}, - {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false}, - {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false}, - {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, - {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, - } - for _, tt := range tests { - test := tt - t.Run(test.name, func(t *testing.T) { - got, err := LastIPInSubnet(test.args) - if (err != nil) != test.wantErr { - t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr) - return - } - if !got.Equal(test.want) { - t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want) - } - }) - } -} diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go index c2c598f46..6053ceb29 100644 --- a/libpod/network/types/network.go +++ b/libpod/network/types/network.go @@ -23,6 +23,10 @@ type ContainerNetwork interface { Setup(namespacePath string, options SetupOptions) (map[string]StatusBlock, error) // Teardown will teardown the container network namespace. Teardown(namespacePath string, options TeardownOptions) error + + // Drivers will return the list of supported network drivers + // for this interface. + Drivers() []string } // Network describes the Network attributes. @@ -36,8 +40,7 @@ type Network struct { // InterfaceName is the network interface name on the host. NetworkInterface string `json:"network_interface,omitempty"` // Created contains the timestamp when this network was created. - // This is not guaranteed to stay exactly the same. - Created time.Time + Created time.Time `json:"created,omitempty"` // Subnets to use. Subnets []Subnet `json:"subnets,omitempty"` // IPv6Enabled if set to true an ipv6 subnet should be created for this net. @@ -92,9 +95,11 @@ func (n *IPNet) UnmarshalText(text []byte) error { } type Subnet struct { - // Subnet for this Network. + // Subnet for this Network in CIDR form. + // swagger:strfmt string Subnet IPNet `json:"subnet,omitempty"` // Gateway IP for this Network. + // swagger:strfmt string Gateway net.IP `json:"gateway,omitempty"` // LeaseRange contains the range where IP are leased. Optional. LeaseRange *LeaseRange `json:"lease_range,omitempty"` @@ -103,8 +108,10 @@ type Subnet struct { // LeaseRange contains the range where IP are leased. type LeaseRange struct { // StartIP first IP in the subnet which should be used to assign ips. + // swagger:strfmt string StartIP net.IP `json:"start_ip,omitempty"` // EndIP last IP in the subnet which should be used to assign ips. + // swagger:strfmt string EndIP net.IP `json:"end_ip,omitempty"` } @@ -196,6 +203,20 @@ type PortMapping struct { Protocol string `json:"protocol,omitempty"` } +// OCICNIPortMapping maps to the standard CNI portmapping Capability. +// Deprecated, do not use this struct for new fields. This only exists +// for backwards compatibility. +type OCICNIPortMapping struct { + // HostPort is the port number on the host. + HostPort int32 `json:"hostPort"` + // ContainerPort is the port number inside the sandbox. + ContainerPort int32 `json:"containerPort"` + // Protocol is the protocol of the port mapping. + Protocol string `json:"protocol"` + // HostIP is the host ip to use. + HostIP string `json:"hostIP"` +} + type SetupOptions struct { NetworkOptions } diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go index 48e769196..c3c80b352 100644 --- a/libpod/network/util/filters.go +++ b/libpod/network/util/filters.go @@ -28,12 +28,6 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err return util.StringMatchRegexSlice(net.Name, filterValues) }, nil - case "label": - // matches all labels - return func(net types.Network) bool { - return util.MatchLabelFilters(filterValues, net.Labels) - }, nil - case "driver": // matches network driver return func(net types.Network) bool { @@ -46,9 +40,39 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err return util.StringMatchRegexSlice(net.ID, filterValues) }, nil - // FIXME: What should we do with the old plugin filter - // TODO: add dangling, dns enabled, internal filter + // TODO: add dns enabled, internal filter + } + return createPruneFilterFuncs(key, filterValues) +} + +func GenerateNetworkPruneFilters(filters map[string][]string) ([]types.FilterFunc, error) { + filterFuncs := make([]types.FilterFunc, 0, len(filters)) + for key, filterValues := range filters { + filterFunc, err := createPruneFilterFuncs(key, filterValues) + if err != nil { + return nil, err + } + filterFuncs = append(filterFuncs, filterFunc) + } + return filterFuncs, nil +} + +func createPruneFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) { + switch strings.ToLower(key) { + case "label": + // matches all labels + return func(net types.Network) bool { + return util.MatchLabelFilters(filterValues, net.Labels) + }, nil + case "until": + until, err := util.ComputeUntilTimestamp(filterValues) + if err != nil { + return nil, err + } + return func(net types.Network) bool { + return net.Created.Before(until) + }, nil default: return nil, errors.Errorf("invalid filter %q", key) } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index b0d4e0b2d..96b6fb298 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -11,17 +11,15 @@ import ( "os/exec" "path/filepath" "regexp" - "sort" "strconv" "strings" "syscall" "time" - cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/netns" @@ -29,7 +27,6 @@ import ( "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage/pkg/lockfile" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -51,58 +48,57 @@ const ( persistentCNIDir = "/var/lib/cni" ) -// Get an OCICNI network config -func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP, staticMAC net.HardwareAddr, netDescriptions ContainerNetworkDescriptions) ocicni.PodNetwork { - var networkKey string - if len(networks) > 0 { - // This is inconsistent for >1 ctrNetwork, but it's probably the - // best we can do. - networkKey = networks[0] - } else { - networkKey = r.netPlugin.GetDefaultNetworkName() - } - ctrNetwork := ocicni.PodNetwork{ - Name: name, - Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces - ID: id, - NetNS: nsPath, - RuntimeConfig: map[string]ocicni.RuntimeConfig{ - networkKey: {PortMappings: ports}, - }, +func (c *Container) getNetworkOptions() (types.NetworkOptions, error) { + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + // TODO remove ocicni PortMappings from container config and store as types PortMappings + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + networks, _, err := c.networks() + if err != nil { + return opts, err + } + aliases, err := c.runtime.state.GetAllNetworkAliases(c) + if err != nil { + return opts, err } - // If we have extra networks, add them - if len(networks) > 0 { - ctrNetwork.Networks = make([]ocicni.NetAttachment, len(networks)) - for i, netName := range networks { - ctrNetwork.Networks[i].Name = netName - if eth, exists := netDescriptions.getInterfaceByName(netName); exists { - ctrNetwork.Networks[i].Ifname = eth - } - } + // If the container requested special network options use this instead of the config. + // This is the case for container restore or network reload. + if c.perNetworkOpts != nil { + opts.Networks = c.perNetworkOpts + return opts, nil } - if staticIP != nil || staticMAC != nil { - // For static IP or MAC, we need to populate networks even if - // it's just the default. - if len(networks) == 0 { - // If len(networks) == 0 this is guaranteed to be the - // default ctrNetwork. - ctrNetwork.Networks = []ocicni.NetAttachment{{Name: networkKey}} - } - var rt ocicni.RuntimeConfig = ocicni.RuntimeConfig{PortMappings: ports} - if staticIP != nil { - rt.IP = staticIP.String() + // Update container map of interface descriptions + if err := c.setupNetworkDescriptions(networks); err != nil { + return opts, err + } + + nets := make(map[string]types.PerNetworkOptions, len(networks)) + for i, network := range networks { + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return opts, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network) } - if staticMAC != nil { - rt.MAC = staticMAC.String() + netOpts := types.PerNetworkOptions{ + InterfaceName: eth, + Aliases: aliases[network], } - ctrNetwork.RuntimeConfig = map[string]ocicni.RuntimeConfig{ - networkKey: rt, + // only set the static ip/mac on the first network + if i == 0 { + if c.config.StaticIP != nil { + netOpts.StaticIPs = []net.IP{c.config.StaticIP} + } + netOpts.StaticMAC = c.config.StaticMAC } + nets[network] = netOpts } - - return ctrNetwork + opts.Networks = nets + return opts, nil } type RootlessCNI struct { @@ -571,9 +567,9 @@ func setPrimaryMachineIP() error { return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String()) } -// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni +// setUpNetwork will set up the the networks, on error it will also tear down the cni // networks. If rootless it will join/create the rootless cni namespace. -func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) { +func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string]types.StatusBlock, error) { if r.config.MachineEnabled() { if err := setPrimaryMachineIP(); err != nil { return nil, err @@ -583,16 +579,10 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu if err != nil { return nil, err } - var results []ocicni.NetResult + var results map[string]types.StatusBlock setUpPod := func() error { - results, err = r.netPlugin.SetUpPod(podNetwork) - if err != nil { - if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil { - logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", podNetwork.ID, err2) - } - return errors.Wrapf(err, "error configuring network namespace for container %s", podNetwork.ID) - } - return nil + results, err = r.network.Setup(ns, types.SetupOptions{NetworkOptions: opts}) + return err } // rootlessCNINS is nil if we are root if rootlessCNINS != nil { @@ -609,7 +599,7 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu // If we are in the pod network namespace use the pod name otherwise the container name func getCNIPodName(c *Container) string { if c.config.NetMode.IsPod() || c.IsInfra() { - pod, err := c.runtime.GetPod(c.PodID()) + pod, err := c.runtime.state.Pod(c.PodID()) if err == nil { return pod.Name() } @@ -618,26 +608,7 @@ func getCNIPodName(c *Container) string { } // Create and configure a new network namespace for a container -func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) { - var requestedIP net.IP - if ctr.requestedIP != nil { - requestedIP = ctr.requestedIP - // cancel request for a specific IP in case the container is reused later - ctr.requestedIP = nil - } else { - requestedIP = ctr.config.StaticIP - } - - var requestedMAC net.HardwareAddr - if ctr.requestedMAC != nil { - requestedMAC = ctr.requestedMAC - // cancel request for a specific MAC in case the container is reused later - ctr.requestedMAC = nil - } else { - requestedMAC = ctr.config.StaticMAC - } - - podName := getCNIPodName(ctr) +func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]types.StatusBlock, error) { networks, _, err := ctr.networks() if err != nil { return nil, err @@ -648,39 +619,15 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re return nil, nil } - // Update container map of interface descriptions - if err := ctr.setupNetworkDescriptions(networks); err != nil { - return nil, err - } - podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions) - aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr) + netOpts, err := ctr.getNetworkOptions() if err != nil { return nil, err } - if len(aliases) > 0 { - podNetwork.Aliases = aliases - } - - results, err := r.setUpOCICNIPod(podNetwork) - if err != nil { - return nil, err - } - - networkStatus := make([]*cnitypes.Result, 0) - for idx, r := range results { - logrus.Debugf("[%d] CNI result: %v", idx, r.Result) - resultCurrent, err := cnitypes.GetResult(r.Result) - if err != nil { - return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err) - } - networkStatus = append(networkStatus, resultCurrent) - } - - return networkStatus, nil + return r.setUpNetwork(ctrNS.Path(), netOpts) } // Create and configure a new network namespace for a container -func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, retErr error) { +func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.StatusBlock, retErr error) { ctrNS, err := netns.NewNS() if err != nil { return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) @@ -698,7 +645,7 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) - networkStatus := []*cnitypes.Result{} + var networkStatus map[string]types.StatusBlock if !ctr.config.NetMode.IsSlirp4netns() { networkStatus, err = r.configureNetNS(ctr, ctrNS) } @@ -797,14 +744,14 @@ func (r *Runtime) closeNetNS(ctr *Container) error { // Tear down a container's CNI network configuration and joins the // rootless net ns as rootless user -func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error { +func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error { rootlessCNINS, err := r.GetRootlessCNINetNs(false) if err != nil { return err } tearDownPod := func() error { - err := r.netPlugin.TearDownPod(podNetwork) - return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", podNetwork.ID) + err := r.network.Teardown(ns, types.TeardownOptions{NetworkOptions: opts}) + return errors.Wrapf(err, "error tearing down network namespace configuration for container %s", opts.ContainerID) } // rootlessCNINS is nil if we are root @@ -837,27 +784,11 @@ func (r *Runtime) teardownCNI(ctr *Container) error { } if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 { - var requestedIP net.IP - if ctr.requestedIP != nil { - requestedIP = ctr.requestedIP - // cancel request for a specific IP in case the container is reused later - ctr.requestedIP = nil - } else { - requestedIP = ctr.config.StaticIP - } - - var requestedMAC net.HardwareAddr - if ctr.requestedMAC != nil { - requestedMAC = ctr.requestedMAC - // cancel request for a specific MAC in case the container is reused later - ctr.requestedMAC = nil - } else { - requestedMAC = ctr.config.StaticMAC + netOpts, err := ctr.getNetworkOptions() + if err != nil { + return err } - - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC, ctr.state.NetInterfaceDescriptions) - err = r.teardownOCICNIPod(podNetwork) - return err + return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts) } return nil } @@ -918,40 +849,15 @@ func isBridgeNetMode(n namespaces.NetworkMode) error { // single MAC or IP. // Only works on root containers at present, though in the future we could // extend this to stop + restart slirp4netns -func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) { +func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.StatusBlock, error) { if ctr.state.NetNS == nil { return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID()) } if err := isBridgeNetMode(ctr.config.NetMode); err != nil { return nil, err } - logrus.Infof("Going to reload container %s network", ctr.ID()) - var requestedIP net.IP - var requestedMAC net.HardwareAddr - // Set requested IP and MAC address, if possible. - if len(ctr.state.NetworkStatus) == 1 { - result := ctr.state.NetworkStatus[0] - if len(result.IPs) == 1 { - resIP := result.IPs[0] - - requestedIP = resIP.Address.IP - ctr.requestedIP = requestedIP - logrus.Debugf("Going to preserve container %s IP address %s", ctr.ID(), ctr.requestedIP.String()) - - if resIP.Interface != nil && *resIP.Interface < len(result.Interfaces) && *resIP.Interface >= 0 { - var err error - requestedMAC, err = net.ParseMAC(result.Interfaces[*resIP.Interface].Mac) - if err != nil { - return nil, errors.Wrapf(err, "error parsing container %s MAC address %s", ctr.ID(), result.Interfaces[*resIP.Interface].Mac) - } - ctr.requestedMAC = requestedMAC - logrus.Debugf("Going to preserve container %s MAC address %s", ctr.ID(), ctr.requestedMAC.String()) - } - } - } - err := r.teardownCNI(ctr) if err != nil { // teardownCNI will error if the iptables rules do not exists and this is the case after @@ -966,9 +872,40 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, er } } - // teardownCNI will clean the requested IP and MAC so we need to set them again - ctr.requestedIP = requestedIP - ctr.requestedMAC = requestedMAC + aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr) + if err != nil { + return nil, err + } + + // Set the same network settings as before.. + netStatus := ctr.getNetworkStatus() + netOpts := make(map[string]types.PerNetworkOptions, len(netStatus)) + for network, status := range netStatus { + perNetOpts := types.PerNetworkOptions{} + for name, netInt := range status.Interfaces { + perNetOpts = types.PerNetworkOptions{ + InterfaceName: name, + Aliases: aliases[network], + StaticMAC: netInt.MacAddress, + } + for _, netAddress := range netInt.Networks { + perNetOpts.StaticIPs = append(perNetOpts.StaticIPs, netAddress.Subnet.IP) + } + // Normally interfaces have a length of 1, only for some special cni configs we could get more. + // For now just use the first interface to get the ips this should be good enough for most cases. + break + } + if perNetOpts.InterfaceName == "" { + eth, exists := ctr.state.NetInterfaceDescriptions.getInterfaceByName(network) + if !exists { + return nil, errors.Errorf("no network interface name for container %s on network %s", ctr.config.ID, network) + } + perNetOpts.InterfaceName = eth + } + netOpts[network] = perNetOpts + } + ctr.perNetworkOpts = netOpts + return r.configureNetNS(ctr, ctr.state.NetNS) } @@ -989,7 +926,8 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { return nil, nil } err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error { - link, err := netlink.LinkByName(ocicni.DefaultInterfaceName) + // FIXME get the interface from the container netstatus + link, err := netlink.LinkByName("eth0") if err != nil { return err } @@ -1047,27 +985,26 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e // Set network namespace path settings.SandboxKey = c.state.NetNS.Path() + netStatus := c.getNetworkStatus() // If this is empty, we're probably slirp4netns - if len(c.state.NetworkStatus) == 0 { + if len(netStatus) == 0 { return settings, nil } - // If we have CNI networks - handle that here + // If we have networks - handle that here if len(networks) > 0 { - if len(networks) != len(c.state.NetworkStatus) { - return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI network(s) %v, but have information on %d network(s)", len(networks), networks, len(c.state.NetworkStatus)) + if len(networks) != len(netStatus) { + return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d network(s) %v, but have information on %d network(s)", len(networks), networks, len(netStatus)) } settings.Networks = make(map[string]*define.InspectAdditionalNetwork) - // CNI results should be in the same order as the list of - // networks we pass into CNI. - for index, name := range networks { - cniResult := c.state.NetworkStatus[index] + for _, name := range networks { + result := netStatus[name] addedNet := new(define.InspectAdditionalNetwork) addedNet.NetworkID = name - basicConfig, err := resultToBasicNetworkConfig(cniResult) + basicConfig, err := resultToBasicNetworkConfig(result) if err != nil { return nil, err } @@ -1089,19 +1026,19 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e } // If not joining networks, we should have at most 1 result - if len(c.state.NetworkStatus) > 1 { - return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 CNI result if not joining networks, instead got %d", len(c.state.NetworkStatus)) + if len(netStatus) > 1 { + return nil, errors.Wrapf(define.ErrInternal, "should have at most 1 network status result if not joining networks, instead got %d", len(netStatus)) } - if len(c.state.NetworkStatus) == 1 { - basicConfig, err := resultToBasicNetworkConfig(c.state.NetworkStatus[0]) - if err != nil { - return nil, err + if len(netStatus) == 1 { + for _, status := range netStatus { + basicConfig, err := resultToBasicNetworkConfig(status) + if err != nil { + return nil, err + } + settings.InspectBasicNetworkConfig = basicConfig } - - settings.InspectBasicNetworkConfig = basicConfig } - return settings, nil } @@ -1130,45 +1067,40 @@ func (c *Container) setupNetworkDescriptions(networks []string) error { // resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI // result -func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNetworkConfig, error) { +func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) { config := define.InspectBasicNetworkConfig{} - - for _, ctrIP := range result.IPs { - size, _ := ctrIP.Address.Mask.Size() - switch { - case ctrIP.Version == "4" && config.IPAddress == "": - config.IPAddress = ctrIP.Address.IP.String() - config.IPPrefixLen = size - config.Gateway = ctrIP.Gateway.String() - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { - config.MacAddress = result.Interfaces[*ctrIP.Interface].Mac - } - case ctrIP.Version == "4" && config.IPAddress != "": - config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, ctrIP.Address.String()) - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { - config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, result.Interfaces[*ctrIP.Interface].Mac) + for _, netInt := range result.Interfaces { + for _, netAddress := range netInt.Networks { + size, _ := netAddress.Subnet.Mask.Size() + if netAddress.Subnet.IP.To4() != nil { + //ipv4 + if config.IPAddress == "" { + config.IPAddress = netAddress.Subnet.IP.String() + config.IPPrefixLen = size + config.Gateway = netAddress.Gateway.String() + } else { + config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, netAddress.Subnet.IP.String()) + } + } else { + //ipv6 + if config.GlobalIPv6Address == "" { + config.GlobalIPv6Address = netAddress.Subnet.IP.String() + config.GlobalIPv6PrefixLen = size + config.IPv6Gateway = netAddress.Gateway.String() + } else { + config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, netAddress.Subnet.IP.String()) + } } - case ctrIP.Version == "6" && config.IPAddress == "": - config.GlobalIPv6Address = ctrIP.Address.IP.String() - config.GlobalIPv6PrefixLen = size - config.IPv6Gateway = ctrIP.Gateway.String() - case ctrIP.Version == "6" && config.IPAddress != "": - config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, ctrIP.Address.String()) - default: - return config, errors.Wrapf(define.ErrInternal, "unrecognized IP version %q", ctrIP.Version) + } + if config.MacAddress == "" { + config.MacAddress = netInt.MacAddress.String() + } else { + config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, netInt.MacAddress.String()) } } - return config, nil } -// This is a horrible hack, necessary because CNI does not properly clean up -// after itself on an unclean reboot. Return what we're pretty sure is the path -// to CNI's internal files (it's not really exposed to us). -func getCNINetworksDir() (string, error) { - return filepath.Join(persistentCNIDir, "networks"), nil -} - type logrusDebugWriter struct { prefix string } @@ -1185,28 +1117,31 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return err } + c.lock.Lock() + defer c.lock.Unlock() + networks, err := c.networksByNameIndex() if err != nil { return err } // check if network exists and if the input is a ID we get the name - // ocicni only uses names so it is important that we only use the name - netName, err = network.NormalizeName(c.runtime.config, netName) + // CNI only uses names so it is important that we only use the name + netName, err = c.runtime.normalizeNetworkName(netName) if err != nil { return err } - index, nameExists := networks[netName] + _, nameExists := networks[netName] if !nameExists && len(networks) > 0 { return errors.Errorf("container %s is not connected to network %s", nameOrID, netName) } - c.lock.Lock() - defer c.lock.Unlock() if err := c.syncContainer(); err != nil { return err } + // get network status before we disconnect + networkStatus := c.getNetworkStatus() if err := c.runtime.state.NetworkDisconnect(c, netName); err != nil { return err @@ -1221,41 +1156,38 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro return errors.Wrapf(define.ErrNoNetwork, "unable to disconnect %s from %s", nameOrID, netName) } - podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions) - if err := c.runtime.teardownOCICNIPod(podConfig); err != nil { - return err + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName) + } + opts.Networks = map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: eth, + }, } - // update network status if container is not running - networkStatus := c.state.NetworkStatus - // clip out the index of the network - tmpNetworkStatus := make([]*cnitypes.Result, 0, len(networkStatus)-1) - for k, v := range networkStatus { - if index != k { - tmpNetworkStatus = append(tmpNetworkStatus, v) - } + if err := c.runtime.teardownNetwork(c.state.NetNS.Path(), opts); err != nil { + return err } - c.state.NetworkStatus = tmpNetworkStatus + + // update network status if container is running + delete(networkStatus, netName) + c.state.NetworkStatus = networkStatus err = c.save() if err != nil { return err } - // OCICNI will set the loopback adapter down on teardown so we should set it up again - err = c.state.NetNS.Do(func(_ ns.NetNS) error { - link, err := netlink.LinkByName("lo") - if err != nil { - return err - } - err = netlink.LinkSetUp(link) - return err - }) - if err != nil { - logrus.Warnf("failed to set loopback adapter up in the container: %v", err) - } // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. // Reloading without connected networks does not make sense, so we can skip this step. - if rootless.IsRootless() && len(tmpNetworkStatus) > 0 { + if rootless.IsRootless() && len(networkStatus) > 0 { return c.reloadRootlessRLKPortMapping() } return nil @@ -1268,24 +1200,28 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return err } + c.lock.Lock() + defer c.lock.Unlock() + networks, err := c.networksByNameIndex() if err != nil { return err } // check if network exists and if the input is a ID we get the name - // ocicni only uses names so it is important that we only use the name - netName, err = network.NormalizeName(c.runtime.config, netName) + // CNI only uses names so it is important that we only use the name + netName, err = c.runtime.normalizeNetworkName(netName) if err != nil { return err } - c.lock.Lock() - defer c.lock.Unlock() if err := c.syncContainer(); err != nil { return err } + // get network status before we connect + networkStatus := c.getNetworkStatus() + if err := c.runtime.state.NetworkConnect(c, netName, aliases); err != nil { return err } @@ -1305,10 +1241,26 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e if err := c.setupNetworkDescriptions(ctrNetworks); err != nil { return err } - podConfig := c.runtime.getPodNetwork(c.ID(), c.Name(), c.state.NetNS.Path(), []string{netName}, c.config.PortMappings, nil, nil, c.state.NetInterfaceDescriptions) - podConfig.Aliases = make(map[string][]string, 1) - podConfig.Aliases[netName] = aliases - results, err := c.runtime.setUpOCICNIPod(podConfig) + + opts := types.NetworkOptions{ + ContainerID: c.config.ID, + ContainerName: getCNIPodName(c), + } + if len(c.config.PortMappings) > 0 { + opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings) + } + eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName) + if !exists { + return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName) + } + opts.Networks = map[string]types.PerNetworkOptions{ + netName: { + Aliases: aliases, + InterfaceName: eth, + }, + } + + results, err := c.runtime.setUpNetwork(c.state.NetNS.Path(), opts) if err != nil { return err } @@ -1316,40 +1268,13 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e return errors.New("when adding aliases, results must be of length 1") } - networkResults := make([]*cnitypes.Result, 0) - for _, r := range results { - resultCurrent, err := cnitypes.GetResult(r.Result) - if err != nil { - return errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.Result, err) - } - networkResults = append(networkResults, resultCurrent) - } - // update network status - networkStatus := c.state.NetworkStatus - // if len is one and we confirmed earlier that the container is in - // fact connected to the network, then just return an empty slice - if len(networkStatus) == 0 { - c.state.NetworkStatus = append(c.state.NetworkStatus, networkResults...) - } else { - // build a list of network names so we can sort and - // get the new name's index - var networkNames []string - for name := range networks { - networkNames = append(networkNames, name) - } - networkNames = append(networkNames, netName) - // sort - sort.Strings(networkNames) - // get index of new network name - index := sort.SearchStrings(networkNames, netName) - // Append a zero value to to the slice - networkStatus = append(networkStatus, &cnitypes.Result{}) - // populate network status - copy(networkStatus[index+1:], networkStatus[index:]) - networkStatus[index] = networkResults[0] - c.state.NetworkStatus = networkStatus + if networkStatus == nil { + networkStatus = make(map[string]types.StatusBlock, 1) } + networkStatus[netName] = results[netName] + c.state.NetworkStatus = networkStatus + err = c.save() if err != nil { return err @@ -1379,3 +1304,27 @@ func (r *Runtime) ConnectContainerToNetwork(nameOrID, netName string, aliases [] } return ctr.NetworkConnect(nameOrID, netName, aliases) } + +// normalizeNetworkName takes a network name, a partial or a full network ID and returns the network name. +// If the network is not found a errors is returned. +func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) { + net, err := r.network.NetworkInspect(nameOrID) + if err != nil { + return "", err + } + return net.Name, nil +} + +func ocicniPortsToNetTypesPorts(ports []types.OCICNIPortMapping) []types.PortMapping { + newPorts := make([]types.PortMapping, 0, len(ports)) + for _, port := range ports { + newPorts = append(newPorts, types.PortMapping{ + HostIP: port.HostIP, + HostPort: uint16(port.HostPort), + ContainerPort: uint16(port.ContainerPort), + Protocol: port.Protocol, + Range: 1, + }) + } + return newPorts +} diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index a09027b72..a5c035757 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -615,14 +615,21 @@ func getRootlessPortChildIP(c *Container) string { return slirp4netnsIP.String() } - for _, r := range c.state.NetworkStatus { - for _, i := range r.IPs { - ipv4 := i.Address.IP.To4() - if ipv4 != nil { - return ipv4.String() + var ipv6 net.IP + for _, status := range c.getNetworkStatus() { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Networks { + ipv4 := netAddress.Subnet.IP.To4() + if ipv4 != nil { + return ipv4.String() + } + ipv6 = netAddress.Subnet.IP } } } + if ipv6 != nil { + return ipv6.String() + } return "" } diff --git a/libpod/oci_util.go b/libpod/oci_util.go index f2843b09b..7db267915 100644 --- a/libpod/oci_util.go +++ b/libpod/oci_util.go @@ -9,7 +9,7 @@ import ( "time" "github.com/containers/podman/v3/libpod/define" - "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/containers/podman/v3/libpod/network/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -32,7 +32,7 @@ func createUnitName(prefix string, name string) string { } // Bind ports to keep them closed on the host -func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { +func bindPorts(ports []types.OCICNIPortMapping) ([]*os.File, error) { var files []*os.File notifySCTP := false for _, i := range ports { diff --git a/libpod/options.go b/libpod/options.go index 7b0c6641a..3f6ccf1cb 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -14,14 +14,13 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" - netTypes "github.com/containers/podman/v3/libpod/network/types" + nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -1040,7 +1039,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption { // namespace with a minimal configuration. // An optional array of port mappings can be provided. // Conflicts with WithNetNSFrom(). -func WithNetNS(portMappings []ocicni.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption { +func WithNetNS(portMappings []nettypes.OCICNIPortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return define.ErrCtrFinalized @@ -2063,10 +2062,10 @@ func WithInfraContainer() PodCreateOption { } // WithInfraContainerPorts tells the pod to add port bindings to the pause container -func WithInfraContainerPorts(bindings []ocicni.PortMapping, infraSpec *specgen.SpecGenerator) []netTypes.PortMapping { - bindingSpec := []netTypes.PortMapping{} +func WithInfraContainerPorts(bindings []nettypes.OCICNIPortMapping, infraSpec *specgen.SpecGenerator) []nettypes.PortMapping { + bindingSpec := []nettypes.PortMapping{} for _, bind := range bindings { - currBind := netTypes.PortMapping{} + currBind := nettypes.PortMapping{} currBind.ContainerPort = uint16(bind.ContainerPort) currBind.HostIP = bind.HostIP currBind.HostPort = uint16(bind.HostPort) diff --git a/libpod/runtime.go b/libpod/runtime.go index 761fa08a2..d2b3d36da 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -20,7 +20,6 @@ import ( "github.com/containers/buildah/pkg/parse" "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" - "github.com/containers/common/pkg/defaultnet" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/pkg/sysregistriesv2" is "github.com/containers/image/v5/storage" @@ -28,6 +27,8 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/libpod/lock" + "github.com/containers/podman/v3/libpod/network/cni" + nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/libpod/plugin" "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/cgroups" @@ -36,7 +37,6 @@ import ( "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/unshare" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/docker/pkg/namesgenerator" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -80,7 +80,7 @@ type Runtime struct { defaultOCIRuntime OCIRuntime ociRuntimes map[string]OCIRuntime runtimeFlags []string - netPlugin ocicni.CNIPlugin + network nettypes.ContainerNetwork conmonPath string libimageRuntime *libimage.Runtime libimageEventsShutdown chan bool @@ -482,17 +482,20 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { } } - // If we need to make a default network - do so now. - if err := defaultnet.Create(runtime.config.Network.DefaultNetwork, runtime.config.Network.DefaultSubnet, runtime.config.Network.NetworkConfigDir, runtime.config.Engine.StaticDir, runtime.config.Engine.MachineEnabled); err != nil { - logrus.Errorf("Failed to created default CNI network: %v", err) - } - - // Set up the CNI net plugin - netPlugin, err := ocicni.InitCNINoInotify(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, "", runtime.config.Network.CNIPluginDirs...) + netInterface, err := cni.NewCNINetworkInterface(cni.InitConfig{ + CNIConfigDir: runtime.config.Network.NetworkConfigDir, + CNIPluginDirs: runtime.config.Network.CNIPluginDirs, + DefaultNetwork: runtime.config.Network.DefaultNetwork, + DefaultSubnet: runtime.config.Network.DefaultSubnet, + IsMachine: runtime.config.Engine.MachineEnabled, + // TODO use cni.lock + LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni1.lock"), + }) if err != nil { - return errors.Wrapf(err, "error configuring CNI network plugin") + return errors.Wrapf(err, "could not create network interface") } - runtime.netPlugin = netPlugin + + runtime.network = netInterface // We now need to see if the system has restarted // We check for the presence of a file in our tmp directory to verify this @@ -1166,3 +1169,13 @@ func (r *Runtime) graphRootMountedFlag(mounts []spec.Mount) string { } return "" } + +// Network returns the network interface which is used by the runtime +func (r *Runtime) Network() nettypes.ContainerNetwork { + return r.network +} + +// Network returns the network interface which is used by the runtime +func (r *Runtime) GetDefaultNetworkName() string { + return r.config.Network.DefaultNetwork +} diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 7d3891f6e..d4f67a115 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -12,7 +12,6 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" - "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities/reports" @@ -248,7 +247,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if len(ctr.config.Networks) > 0 { netNames := make([]string, 0, len(ctr.config.Networks)) for _, nameOrID := range ctr.config.Networks { - netName, err := network.NormalizeName(r.config, nameOrID) + netName, err := r.normalizeNetworkName(nameOrID) if err != nil { return nil, err } @@ -262,7 +261,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if len(ctr.config.NetworkAliases) > 0 { netAliases := make(map[string][]string, len(ctr.config.NetworkAliases)) for nameOrID, aliases := range ctr.config.NetworkAliases { - netName, err := network.NormalizeName(r.config, nameOrID) + netName, err := r.normalizeNetworkName(nameOrID) if err != nil { return nil, err } diff --git a/libpod/util.go b/libpod/util.go index ed5c4e6c6..d3f7da91e 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -15,8 +15,8 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/utils" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/fsnotify/fsnotify" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" @@ -295,8 +295,8 @@ func writeHijackHeader(r *http.Request, conn io.Writer) { } // Convert OCICNI port bindings into Inspect-formatted port bindings. -func makeInspectPortBindings(bindings []ocicni.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort { - portBindings := make(map[string][]define.InspectHostPort, len(bindings)) +func makeInspectPortBindings(bindings []types.OCICNIPortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort { + portBindings := make(map[string][]define.InspectHostPort) for _, port := range bindings { key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol) hostPorts := portBindings[key] diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 847e6dcff..28727a22b 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -4,20 +4,15 @@ import ( "encoding/json" "net" "net/http" - "os" - "strings" - "syscall" - "time" - "github.com/containernetworking/cni/libcni" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network" + nettypes "github.com/containers/podman/v3/libpod/network/types" + netutil "github.com/containers/podman/v3/libpod/network/util" "github.com/containers/podman/v3/pkg/api/handlers/utils" api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" - networkid "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/util" "github.com/docker/docker/api/types" @@ -27,12 +22,6 @@ import ( "github.com/sirupsen/logrus" ) -type pluginInterface struct { - PluginType string `json:"type"` - IPAM network.IPAMConfig `json:"ipam"` - IsGW bool `json:"isGateway"` -} - func InspectNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -54,18 +43,13 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Invalid scope value. Can only be local.", http.StatusBadRequest, define.ErrInvalidArg) return } - config, err := runtime.GetConfig() - if err != nil { - utils.InternalServerError(w, err) - return - } name := utils.GetName(r) - _, err = network.InspectNetwork(config, name) + net, err := runtime.Network().NetworkInspect(name) if err != nil { utils.NetworkNotFound(w, name, err) return } - report, err := getNetworkResourceByNameOrID(name, runtime, nil) + report, err := convertLibpodNetworktoDockerNetwork(runtime, net) if err != nil { utils.InternalServerError(w, err) return @@ -73,65 +57,18 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, report) } -func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filters map[string][]string) (*types.NetworkResource, error) { - var ( - ipamConfigs []dockerNetwork.IPAMConfig - ) - config, err := runtime.GetConfig() - if err != nil { - return nil, err - } - containerEndpoints := map[string]types.EndpointResource{} - // Get the network path so we can get created time - networkConfigPath, err := network.GetCNIConfigPathByNameOrID(config, nameOrID) - if err != nil { - return nil, err - } - f, err := os.Stat(networkConfigPath) - if err != nil { - return nil, err - } - stat := f.Sys().(*syscall.Stat_t) +func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, network nettypes.Network) (*types.NetworkResource, error) { cons, err := runtime.GetAllContainers() if err != nil { return nil, err } - conf, err := libcni.ConfListFromFile(networkConfigPath) - if err != nil { - return nil, err - } - if len(filters) > 0 { - ok, err := network.IfPassesFilter(conf, filters) - if err != nil { - return nil, err - } - if !ok { - // do not return the config if we did not match the filter - return nil, nil - } - } - - plugin, err := getPlugin(conf.Plugins) - if err != nil { - return nil, err - } - - for _, outer := range plugin.IPAM.Ranges { - for _, n := range outer { - ipamConfig := dockerNetwork.IPAMConfig{ - Subnet: n.Subnet, - Gateway: n.Gateway, - } - ipamConfigs = append(ipamConfigs, ipamConfig) - } - } - + containerEndpoints := make(map[string]types.EndpointResource, len(cons)) for _, con := range cons { data, err := con.Inspect(false) if err != nil { return nil, err } - if netData, ok := data.NetworkSettings.Networks[conf.Name]; ok { + if netData, ok := data.NetworkSettings.Networks[network.Name]; ok { containerEndpoint := types.EndpointResource{ Name: netData.NetworkID, EndpointID: netData.EndpointID, @@ -142,60 +79,47 @@ func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filt containerEndpoints[con.ID()] = containerEndpoint } } - - labels := network.GetNetworkLabels(conf) - if labels == nil { - labels = map[string]string{} + ipamConfigs := make([]dockerNetwork.IPAMConfig, 0, len(network.Subnets)) + for _, sub := range network.Subnets { + ipamConfig := dockerNetwork.IPAMConfig{ + Subnet: sub.Subnet.String(), + Gateway: sub.Gateway.String(), + // TODO add range + } + ipamConfigs = append(ipamConfigs, ipamConfig) } - - isInternal := false - dockerDriver := plugin.PluginType - if plugin.PluginType == network.DefaultNetworkDriver { - isInternal = !plugin.IsGW - dockerDriver = "default" + ipamDriver := network.IPAMOptions["driver"] + if ipamDriver == nettypes.HostLocalIPAMDriver { + ipamDriver = "default" + } + ipam := dockerNetwork.IPAM{ + Driver: ipamDriver, + Options: network.IPAMOptions, + Config: ipamConfigs, } report := types.NetworkResource{ - Name: conf.Name, - ID: networkid.GetNetworkID(conf.Name), - Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert + Name: network.Name, + ID: network.ID, + Driver: network.Driver, + // TODO add Created: , + Internal: network.Internal, + EnableIPv6: network.IPv6Enabled, + Labels: network.Labels, + Options: network.Options, + IPAM: ipam, Scope: "local", - Driver: plugin.PluginType, - EnableIPv6: false, - IPAM: dockerNetwork.IPAM{ - Driver: dockerDriver, - Options: map[string]string{}, - Config: ipamConfigs, - }, - Internal: isInternal, Attachable: false, Ingress: false, ConfigFrom: dockerNetwork.ConfigReference{}, ConfigOnly: false, Containers: containerEndpoints, - Options: map[string]string{}, - Labels: labels, Peers: nil, Services: nil, } return &report, nil } -func getPlugin(plugins []*libcni.NetworkConfig) (pluginInterface, error) { - var plugin pluginInterface - - for _, p := range plugins { - for _, pluginType := range network.SupportedNetworkDrivers { - if pluginType == p.Network.Type { - err := json.Unmarshal(p.Bytes, &plugin) - return plugin, err - } - } - } - - return plugin, errors.New("unable to find supported plugin") -} - func ListNetworks(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) @@ -204,37 +128,32 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { return } - config, err := runtime.GetConfig() - if err != nil { - utils.InternalServerError(w, err) - return + options := entities.NetworkListOptions{ + Filters: *filterMap, } - netNames, err := network.GetNetworkNamesFromFileSystem(config) + ic := abi.ContainerEngine{Libpod: runtime} + nets, err := ic.NetworkList(r.Context(), options) if err != nil { utils.InternalServerError(w, err) return } - - reports := []*types.NetworkResource{} - logrus.Debugf("netNames: %q", strings.Join(netNames, ", ")) - for _, name := range netNames { - report, err := getNetworkResourceByNameOrID(name, runtime, *filterMap) + reports := make([]*types.NetworkResource, 0, len(nets)) + for _, net := range nets { + report, err := convertLibpodNetworktoDockerNetwork(runtime, net) if err != nil { utils.InternalServerError(w, err) return } - if report != nil { - reports = append(reports, report) - } + reports = append(reports, report) } utils.WriteResponse(w, http.StatusOK, reports) } func CreateNetwork(w http.ResponseWriter, r *http.Request) { var ( - name string networkCreate types.NetworkCreateRequest + network nettypes.Network ) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { @@ -242,65 +161,80 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { return } - if len(networkCreate.Name) > 0 { - name = networkCreate.Name - } - if len(networkCreate.Driver) < 1 { - networkCreate.Driver = network.DefaultNetworkDriver + network.Name = networkCreate.Name + if networkCreate.Driver == "" { + networkCreate.Driver = nettypes.DefaultNetworkDriver } - // At present I think we should just support the bridge driver - // and allow demand to make us consider more - if networkCreate.Driver != network.DefaultNetworkDriver { - utils.InternalServerError(w, errors.New("network create only supports the bridge driver")) - return - } - ncOptions := entities.NetworkCreateOptions{ - Driver: network.DefaultNetworkDriver, - Internal: networkCreate.Internal, - Labels: networkCreate.Labels, + network.Driver = networkCreate.Driver + network.Labels = networkCreate.Labels + network.Internal = networkCreate.Internal + network.IPv6Enabled = networkCreate.EnableIPv6 + + // FIXME use docker options and convert them to valid libpod options + // network.Options = networkCreate.Options + + // dns is only enabled for the bridge driver + if network.Driver == nettypes.BridgeNetworkDriver { + network.DNSEnabled = true } - if networkCreate.IPAM != nil && len(networkCreate.IPAM.Config) > 0 { - if len(networkCreate.IPAM.Config) > 1 { - utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config")) - return - } - if len(networkCreate.IPAM.Config[0].Subnet) > 0 { - _, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet) - if err != nil { - utils.InternalServerError(w, err) - return + if networkCreate.IPAM != nil && len(networkCreate.IPAM.Config) > 0 { + for _, conf := range networkCreate.IPAM.Config { + s := nettypes.Subnet{} + if len(conf.Subnet) > 0 { + var err error + subnet, err := nettypes.ParseCIDR(conf.Subnet) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "failed to parse subnet")) + return + } + s.Subnet = subnet } - ncOptions.Subnet = *subnet - } - if len(networkCreate.IPAM.Config[0].Gateway) > 0 { - ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway) - } - if len(networkCreate.IPAM.Config[0].IPRange) > 0 { - _, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange) - if err != nil { - utils.InternalServerError(w, err) - return + if len(conf.Gateway) > 0 { + gw := net.ParseIP(conf.Gateway) + if gw == nil { + utils.InternalServerError(w, errors.Errorf("failed to parse gateway ip %s", conf.Gateway)) + return + } + s.Gateway = gw + } + if len(conf.IPRange) > 0 { + _, net, err := net.ParseCIDR(conf.IPRange) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "failed to parse ip range")) + return + } + startIP, err := netutil.FirstIPInSubnet(net) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "failed to get first ip in range")) + return + } + lastIP, err := netutil.LastIPInSubnet(net) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "failed to get last ip in range")) + return + } + s.LeaseRange = &nettypes.LeaseRange{ + StartIP: startIP, + EndIP: lastIP, + } } - ncOptions.Range = *IPRange + network.Subnets = append(network.Subnets, s) } - } - ce := abi.ContainerEngine{Libpod: runtime} - if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil { - utils.InternalServerError(w, err) - return + // FIXME can we use the IPAM driver and options? } - net, err := getNetworkResourceByNameOrID(name, runtime, nil) + network, err := runtime.Network().NetworkCreate(network) if err != nil { utils.InternalServerError(w, err) return } + body := struct { ID string `json:"Id"` Warning []string }{ - ID: net.ID, + ID: network.ID, } utils.WriteResponse(w, http.StatusCreated, body) } diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go index 122ab1d3d..fcd8e0231 100644 --- a/pkg/api/handlers/libpod/networks.go +++ b/pkg/api/handlers/libpod/networks.go @@ -6,7 +6,7 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/api/handlers/utils" api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" @@ -18,27 +18,14 @@ import ( func CreateNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) - options := entities.NetworkCreateOptions{} - if err := json.NewDecoder(r.Body).Decode(&options); err != nil { - utils.Error(w, "unable to marshall input", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + network := types.Network{} + if err := json.NewDecoder(r.Body).Decode(&network); err != nil { + utils.Error(w, "unable to marshall input", http.StatusInternalServerError, errors.Wrap(err, "decode body")) return } - query := struct { - Name string `schema:"name"` - }{ - // override any golang type defaults - } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) - return - } - if len(options.Driver) < 1 { - options.Driver = network.DefaultNetworkDriver - } + ic := abi.ContainerEngine{Libpod: runtime} - report, err := ic.NetworkCreate(r.Context(), query.Name, options) + report, err := ic.Libpod.Network().NetworkCreate(network) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go index 6116a7274..7ccfdd0f3 100644 --- a/pkg/api/handlers/libpod/swagger.go +++ b/pkg/api/handlers/libpod/swagger.go @@ -4,9 +4,9 @@ import ( "net/http" "os" - "github.com/containernetworking/cni/libcni" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" @@ -103,21 +103,27 @@ type swagNetworkRmReport struct { // swagger:response NetworkInspectReport type swagNetworkInspectReport struct { // in:body - Body libcni.NetworkConfigList + Body types.Network } // Network list // swagger:response NetworkListReport type swagNetworkListReport struct { // in:body - Body []entities.NetworkListReport + Body []types.Network +} + +// Network create +// swagger:model NetworkCreateLibpod +type swagNetworkCreateLibpod struct { + types.Network } // Network create // swagger:response NetworkCreateReport type swagNetworkCreateReport struct { // in:body - Body entities.NetworkCreateReport + Body types.Network } // Network prune diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index cacf83a7f..641bce333 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -267,7 +267,7 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // - `id=[id]` Matches for full or partial ID. // - `driver=[driver]` Only bridge is supported. // - `label=[key]` or `label=[key=value]` Matches networks based on the presence of a label alone or a label and a value. - // - `plugin=[plugin]` Matches CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) + // - `until=[timestamp]` Matches all networks that were create before the given timestamp. // produces: // - application/json // responses: @@ -306,19 +306,15 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // tags: // - networks // summary: Create network - // description: Create a new CNI network configuration + // description: Create a new network configuration // produces: // - application/json // parameters: - // - in: query - // name: name - // type: string - // description: optional name for new network // - in: body // name: create // description: attributes for creating a container // schema: - // $ref: "#/definitions/NetworkCreateOptions" + // $ref: "#/definitions/NetworkCreateLibpod" // responses: // 200: // $ref: "#/responses/NetworkCreateReport" diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go index 59207aa8d..5a0a34f56 100644 --- a/pkg/bindings/network/network.go +++ b/pkg/bindings/network/network.go @@ -6,58 +6,51 @@ import ( "net/url" "strings" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" jsoniter "github.com/json-iterator/go" ) // Create makes a new CNI network configuration -func Create(ctx context.Context, options *CreateOptions) (*entities.NetworkCreateReport, error) { - var report entities.NetworkCreateReport - if options == nil { - options = new(CreateOptions) - } +func Create(ctx context.Context, network *types.Network) (types.Network, error) { + var report types.Network conn, err := bindings.GetClient(ctx) if err != nil { - return nil, err + return report, err } - params := url.Values{} - if options.Name != nil { - params.Set("name", options.GetName()) + // create empty network if the caller did not provide one + if network == nil { + network = &types.Network{} } - networkConfig, err := jsoniter.MarshalToString(options) + networkConfig, err := jsoniter.MarshalToString(*network) if err != nil { - return nil, err + return report, err } - stringReader := strings.NewReader(networkConfig) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/networks/create", params, nil) + reader := strings.NewReader(networkConfig) + response, err := conn.DoRequest(reader, http.MethodPost, "/networks/create", nil, nil) if err != nil { - return nil, err + return report, err } defer response.Body.Close() - return &report, response.Process(&report) + return report, response.Process(&report) } // Inspect returns low level information about a CNI network configuration -func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) ([]entities.NetworkInspectReport, error) { - var reports []entities.NetworkInspectReport - reports = append(reports, entities.NetworkInspectReport{}) - if options == nil { - options = new(InspectOptions) - } - _ = options +func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Network, error) { + var net types.Network conn, err := bindings.GetClient(ctx) if err != nil { - return nil, err + return net, err } response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nil, nameOrID) if err != nil { - return nil, err + return net, err } defer response.Body.Close() - return reports, response.Process(&reports[0]) + return net, response.Process(&net) } // Remove deletes a defined CNI network configuration by name. The optional force boolean @@ -86,10 +79,8 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*en } // List returns a summary of all CNI network configurations -func List(ctx context.Context, options *ListOptions) ([]*entities.NetworkListReport, error) { - var ( - netList []*entities.NetworkListReport - ) +func List(ctx context.Context, options *ListOptions) ([]types.Network, error) { + var netList []types.Network if options == nil { options = new(ListOptions) } diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go index b53fc4bd3..85ceeb998 100644 --- a/pkg/bindings/test/networks_test.go +++ b/pkg/bindings/test/networks_test.go @@ -6,6 +6,7 @@ import ( "net/http" "time" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/containers" "github.com/containers/podman/v3/pkg/bindings/network" @@ -41,10 +42,10 @@ var _ = Describe("Podman networks", func() { It("podman prune unused networks with filters", func() { name := "foobar" - opts := network.CreateOptions{ - Name: &name, + net := types.Network{ + Name: name, } - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).To(BeNil()) // Invalid filters should return error @@ -88,20 +89,20 @@ var _ = Describe("Podman networks", func() { It("create network", func() { // create a network with blank config should work - _, err = network.Create(connText, &network.CreateOptions{}) + _, err = network.Create(connText, nil) Expect(err).To(BeNil()) name := "foobar" - opts := network.CreateOptions{ - Name: &name, + net := types.Network{ + Name: name, } - report, err := network.Create(connText, &opts) + report, err := network.Create(connText, &net) Expect(err).To(BeNil()) - Expect(report.Filename).To(ContainSubstring(name)) + Expect(report.Name).To(Equal(name)) // create network with same name should 500 - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -109,24 +110,24 @@ var _ = Describe("Podman networks", func() { It("inspect network", func() { name := "foobar" - opts := network.CreateOptions{ - Name: &name, + net := types.Network{ + Name: name, } - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).To(BeNil()) data, err := network.Inspect(connText, name, nil) Expect(err).To(BeNil()) - Expect(data[0]["name"]).To(Equal(name)) + Expect(data.Name).To(Equal(name)) }) It("list networks", func() { // create a bunch of named networks and make verify with list netNames := []string{"homer", "bart", "lisa", "maggie", "marge"} for i := 0; i < 5; i++ { - opts := network.CreateOptions{ - Name: &netNames[i], + net := types.Network{ + Name: netNames[i], } - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).To(BeNil()) } list, err := network.List(connText, nil) @@ -166,10 +167,10 @@ var _ = Describe("Podman networks", func() { // Removing an unused network should work name := "unused" - opts := network.CreateOptions{ - Name: &name, + net := types.Network{ + Name: name, } - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).To(BeNil()) report, err := network.Remove(connText, name, nil) Expect(err).To(BeNil()) @@ -177,10 +178,10 @@ var _ = Describe("Podman networks", func() { // Removing a network that is being used without force should be 500 name = "used" - opts = network.CreateOptions{ - Name: &name, + net = types.Network{ + Name: name, } - _, err = network.Create(connText, &opts) + _, err = network.Create(connText, &net) Expect(err).To(BeNil()) // Start container and wait diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index 572d6b9e9..7c255b0ea 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -5,8 +5,8 @@ import ( "strings" "time" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/ps/define" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" ) @@ -54,7 +54,7 @@ type ListContainer struct { // boolean to be set PodName string // Port mappings - Ports []ocicni.PortMapping + Ports []types.OCICNIPortMapping // Size of the container rootfs. Requires the size boolean to be true Size *define.ContainerSize // Time when container started diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 607e68256..a302cdb7d 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -11,7 +11,6 @@ import ( nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/storage/pkg/archive" - "github.com/cri-o/ocicni/pkg/ocicni" ) // ContainerRunlabelOptions are the options to execute container-runlabel. @@ -422,7 +421,7 @@ type ContainerPortOptions struct { // the CLI to output ports type ContainerPortReport struct { Id string //nolint - Ports []ocicni.PortMapping + Ports []nettypes.OCICNIPortMapping } // ContainerCpOptions describes input options for cp. diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 3da31d8a0..b916d6fc6 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -6,6 +6,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/specgen" ) @@ -58,11 +59,11 @@ type ContainerEngine interface { HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) NetworkConnect(ctx context.Context, networkname string, options NetworkConnectOptions) error - NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) + NetworkCreate(ctx context.Context, network types.Network) (*NetworkCreateReport, error) NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error NetworkExists(ctx context.Context, networkname string) (*BoolReport, error) - NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error) - NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error) + NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]types.Network, []error, error) + NetworkList(ctx context.Context, options NetworkListOptions) ([]types.Network, error) NetworkPrune(ctx context.Context, options NetworkPruneOptions) ([]*NetworkPruneReport, error) NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index a89501664..b61297d41 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -2,8 +2,6 @@ package entities import ( "net" - - "github.com/containernetworking/cni/libcni" ) // NetworkListOptions describes options for listing networks in cli @@ -13,15 +11,6 @@ type NetworkListOptions struct { Filters map[string][]string } -// NetworkListReport describes the results from listing networks -type NetworkListReport struct { - *libcni.NetworkConfigList - Labels map[string]string -} - -// NetworkInspectReport describes the results from inspect networks -type NetworkInspectReport map[string]interface{} - // NetworkReloadOptions describes options for reloading container network // configuration. type NetworkReloadOptions struct { @@ -48,7 +37,6 @@ type NetworkRmReport struct { } // NetworkCreateOptions describes options to create a network -// swagger:model NetworkCreateOptions type NetworkCreateOptions struct { DisableDNS bool Driver string @@ -65,7 +53,7 @@ type NetworkCreateOptions struct { // NetworkCreateReport describes a created network for the cli type NetworkCreateReport struct { - Filename string + Name string } // NetworkDisconnectOptions describes options for disconnecting diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 7900caaa6..45d2c6925 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -4,60 +4,38 @@ import ( "context" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" + netutil "github.com/containers/podman/v3/libpod/network/util" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) -func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { - reports := make([]*entities.NetworkListReport, 0) - - config, err := ic.Libpod.GetConfig() - if err != nil { - return nil, err - } - - networks, err := network.LoadCNIConfsFromDir(network.GetCNIConfDir(config)) +func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) { + filters, err := netutil.GenerateNetworkFilters(options.Filters) if err != nil { return nil, err } - - for _, n := range networks { - ok, err := network.IfPassesFilter(n, options.Filters) - if err != nil { - return nil, err - } - if ok { - reports = append(reports, &entities.NetworkListReport{ - NetworkConfigList: n, - Labels: network.GetNetworkLabels(n), - }) - } - } - return reports, nil + nets, err := ic.Libpod.Network().NetworkList(filters...) + return nets, err } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { - config, err := ic.Libpod.GetConfig() - if err != nil { - return nil, nil, err - } +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]types.Network, []error, error) { var errs []error - rawCNINetworks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) + networks := make([]types.Network, 0, len(namesOrIds)) for _, name := range namesOrIds { - rawList, err := network.InspectNetwork(config, name) + net, err := ic.Libpod.Network().NetworkInspect(name) if err != nil { if errors.Cause(err) == define.ErrNoSuchNetwork { - errs = append(errs, errors.Errorf("no such network %s", name)) + errs = append(errs, errors.Wrapf(err, "network %s", name)) continue } else { return nil, nil, errors.Wrapf(err, "error inspecting network %s", name) } } - rawCNINetworks = append(rawCNINetworks, rawList) + networks = append(networks, net) } - return rawCNINetworks, errs, nil + return networks, errs, nil } func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { @@ -83,12 +61,7 @@ func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, op } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { - reports := []*entities.NetworkRmReport{} - - config, err := ic.Libpod.GetConfig() - if err != nil { - return nil, err - } + reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { report := entities.NetworkRmReport{Name: name} @@ -126,7 +99,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o } } } - if err := network.RemoveNetwork(config, name); err != nil { + if err := ic.Libpod.Network().NetworkRemove(name); err != nil { report.Err = err } reports = append(reports, &report) @@ -134,12 +107,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, nil } -func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { - runtimeConfig, err := ic.Libpod.GetConfig() +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*entities.NetworkCreateReport, error) { + network, err := ic.Libpod.Network().NetworkCreate(network) if err != nil { return nil, err } - return network.Create(name, options, runtimeConfig) + return &entities.NetworkCreateReport{Name: network.Name}, nil } // NetworkDisconnect removes a container from a given network @@ -153,12 +126,12 @@ func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname strin // NetworkExists checks if the given network exists func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string) (*entities.BoolReport, error) { - config, err := ic.Libpod.GetConfig() - if err != nil { - return nil, err - } - exists, err := network.Exists(config, networkname) - if err != nil { + _, err := ic.Libpod.Network().NetworkInspect(networkname) + exists := true + // if err is ErrNoSuchNetwork do not return it + if errors.Is(err, define.ErrNoSuchNetwork) { + exists = false + } else if err != nil { return nil, err } return &entities.BoolReport{ @@ -168,19 +141,10 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string // Network prune removes unused cni networks func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) { - runtimeConfig, err := ic.Libpod.GetConfig() - if err != nil { - return nil, err - } cons, err := ic.Libpod.GetAllContainers() if err != nil { return nil, err } - networks, err := network.LoadCNIConfsFromDir(network.GetCNIConfDir(runtimeConfig)) - if err != nil { - return nil, err - } - // Gather up all the non-default networks that the // containers want networksToKeep := make(map[string]bool) @@ -193,20 +157,34 @@ func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.Ne networksToKeep[n] = true } } - if len(options.Filters) != 0 { - for _, n := range networks { - // This network will be kept anyway - if _, found := networksToKeep[n.Name]; found { - continue - } - ok, err := network.IfPassesPruneFilter(runtimeConfig, n, options.Filters) - if err != nil { - return nil, err - } - if !ok { - networksToKeep[n.Name] = true + // ignore the default network, this one cannot be deleted + networksToKeep[ic.Libpod.GetDefaultNetworkName()] = true + + // get all filters + filters, err := netutil.GenerateNetworkPruneFilters(options.Filters) + if err != nil { + return nil, err + } + danglingFilterFunc := func(net types.Network) bool { + for network := range networksToKeep { + if network == net.Name { + return false } } + return true + } + filters = append(filters, danglingFilterFunc) + nets, err := ic.Libpod.Network().NetworkList(filters...) + if err != nil { + return nil, err + } + + pruneReport := make([]*entities.NetworkPruneReport, 0, len(nets)) + for _, net := range nets { + pruneReport = append(pruneReport, &entities.NetworkPruneReport{ + Name: net.Name, + Error: ic.Libpod.Network().NetworkRemove(net.Name), + }) } - return network.PruneNetworks(runtimeConfig, networksToKeep) + return pruneReport, nil } diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 7e59e44c2..711c2e00c 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -3,20 +3,22 @@ package tunnel import ( "context" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/bindings/network" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/pkg/errors" ) -func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { +func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]types.Network, error) { options := new(network.ListOptions).WithFilters(opts.Filters) return network.List(ic.ClientCtx, options) } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]types.Network, []error, error) { var ( - reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds)) + reports = make([]types.Network, 0, len(namesOrIds)) errs = []error{} ) options := new(network.InspectOptions) @@ -28,12 +30,12 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return nil, nil, err } if errModel.ResponseCode == 404 { - errs = append(errs, errors.Errorf("no such network %q", name)) + errs = append(errs, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name)) continue } return nil, nil, err } - reports = append(reports, report...) + reports = append(reports, report) } return reports, errs, nil } @@ -60,11 +62,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, nil } -func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, opts entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { - options := new(network.CreateOptions).WithName(name).WithDisableDNS(opts.DisableDNS).WithDriver(opts.Driver).WithGateway(opts.Gateway) - options.WithInternal(opts.Internal).WithIPRange(opts.Range).WithIPv6(opts.IPv6).WithLabels(opts.Labels).WithIPv6(opts.IPv6) - options.WithMacVLAN(opts.MacVLAN).WithOptions(opts.Options).WithSubnet(opts.Subnet) - return network.Create(ic.ClientCtx, options) +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, net types.Network) (*entities.NetworkCreateReport, error) { + net, err := network.Create(ic.ClientCtx, &net) + if err != nil { + return nil, err + } + return &entities.NetworkCreateReport{Name: net.Name}, nil } // NetworkDisconnect removes a container from a given network diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go index 10d135e0b..6c7b8e6d7 100644 --- a/pkg/rootlessport/rootlessport_linux.go +++ b/pkg/rootlessport/rootlessport_linux.go @@ -23,8 +23,8 @@ import ( "path/filepath" "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/storage/pkg/reexec" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" rkport "github.com/rootless-containers/rootlesskit/pkg/port" rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin" @@ -44,7 +44,7 @@ const ( // Config needs to be provided to the process via stdin as a JSON string. // stdin needs to be closed after the message has been written. type Config struct { - Mappings []ocicni.PortMapping + Mappings []types.OCICNIPortMapping NetNSPath string ExitFD int ReadyFD int @@ -313,7 +313,7 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error { return nil } -func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping, childIP string) error { +func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error { ctx := context.TODO() for _, i := range portMappings { hostIP := i.HostIP diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index a300f8014..992b4a8e9 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -11,7 +11,6 @@ import ( "github.com/containers/podman/v3/utils" "github.com/containers/podman/v3/pkg/specgen" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -25,11 +24,11 @@ const ( // Parse port maps to OCICNI port mappings. // Returns a set of OCICNI port mappings, and maps of utilized container and // host ports. -func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { +func ParsePortMapping(portMappings []types.PortMapping) ([]types.OCICNIPortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { // First, we need to validate the ports passed in the specgen, and then // convert them into CNI port mappings. type tempMapping struct { - mapping ocicni.PortMapping + mapping types.OCICNIPortMapping startOfRange bool isInRange bool } @@ -159,7 +158,7 @@ func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, m // struct. // Don't use hostIP - we want to preserve the // empty string hostIP by default for compat. - cniPort := ocicni.PortMapping{ + cniPort := types.OCICNIPortMapping{ HostPort: int32(hPort), ContainerPort: int32(cPort), Protocol: p, @@ -179,7 +178,7 @@ func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, m // Handle any 0 host ports now by setting random container ports. if postAssignHostPort { - remadeMappings := make([]ocicni.PortMapping, 0, len(tempMappings)) + remadeMappings := make([]types.OCICNIPortMapping, 0, len(tempMappings)) var ( candidate int @@ -245,7 +244,7 @@ func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, m return remadeMappings, containerPortValidate, hostPortValidate, nil } - finalMappings := []ocicni.PortMapping{} + finalMappings := []types.OCICNIPortMapping{} for _, m := range tempMappings { finalMappings = append(finalMappings, m.mapping) } @@ -254,7 +253,7 @@ func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, m } // Make final port mappings for the container -func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, map[uint16][]string, error) { +func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]types.OCICNIPortMapping, map[uint16][]string, error) { finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings) if err != nil { return nil, nil, err @@ -356,7 +355,7 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData logrus.Debugf("Mapping exposed port %d/%s to host port %d", port, p, hostPort) // Make a CNI port mapping - cniPort := ocicni.PortMapping{ + cniPort := types.OCICNIPortMapping{ HostPort: int32(candidate), ContainerPort: int32(port), Protocol: p, diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index 7a36b605f..a4cb5a480 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -6,42 +6,52 @@ t GET networks/non-existing-network 404 \ .cause='network not found' -t POST libpod/networks/create?name=network1 200 \ - .Filename~.*/network1\\.conflist - -# --data '{"Subnet":{"IP":"10.10.254.0","Mask":[255,255,255,0]},"Labels":{"abc":"val"}}' -t POST libpod/networks/create?name=network2 \ - Subnet='{"IP":"10.10.254.0","Mask":[255,255,255,0]}' \ - Labels='{"abc":"val"}' \ +t POST libpod/networks/create name='"network1"' 200 \ + .name=network1 + .created~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + +# --data '{"name":"network2","subnets":[{"subnet":"10.10.254.0/24"}],"Labels":{"abc":"val"}}' +t POST libpod/networks/create name='"network2"' \ + subnets='[{"subnet":"10.10.254.0/24"}]' \ + labels='{"abc":"val"}' \ 200 \ - .Filename~.*/network2\\.conflist - -# --data '{"Subnet":{"IP":"10.10.133.0","Mask":[255,255,255,0]},"Labels":{"xyz":"val"}}' -t POST libpod/networks/create?name=network3 \ - Subnet='{"IP":"10.10.133.0","Mask":[255,255,255,0]}' \ - Labels='{"xyz":"val"}' \ + .name=network2 \ + .subnets[0].subnet=10.10.254.0/24 \ + .subnets[0].gateway=10.10.254.1 \ + .labels.abc=val + +# --data '{"name":"network3","subnets":[{"subnet":"10.10.133.0/24"}],"Labels":{"xyz":"val"}}' +t POST libpod/networks/create name="network3" \ + subnets='[{"subnet":"10.10.133.0/24"}]' \ + labels='{"xyz":"val"}' \ 200 \ - .Filename~.*/network3\\.conflist - -# --data '{"Subnet":{"IP":"10.10.134.0","Mask":[255,255,255,0]},"Labels":{"zaq":"val"}}' -t POST libpod/networks/create?name=network4 \ - Subnet='{"IP":"10.10.134.0","Mask":[255,255,255,0]}' \ - Labels='{"zaq":"val"}' \ + .name=network3 \ + .subnets[0].subnet=10.10.133.0/24 \ + .subnets[0].gateway=10.10.133.1 \ + .labels.xyz=val + +# --data '{"name":"network4","subnets":[{"subnet":"10.10.134.0/24"}],"Labels":{"zaq":"val"}}' +t POST libpod/networks/create name="network4" \ + subnets='[{"subnet":"10.10.134.0/24"}]' \ + labels='{"zaq":"val"}' \ 200 \ - .Filename~.*/network4\\.conflist + .name=network4 \ + .subnets[0].subnet=10.10.134.0/24 \ + .subnets[0].gateway=10.10.134.1 \ + .labels.zaq=val # test for empty mask -t POST libpod/networks/create Subnet='{"IP":"10.10.1.0","Mask":[]}' 500 \ - .cause~'.*cannot be empty' +t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0"}]' 500 \ + .cause~'.*invalid CIDR address: 10.10.134.0' # test for invalid mask -t POST libpod/networks/create Subnet='{"IP":"10.10.1.0","Mask":[0,255,255,0]}' 500 \ - .cause~'.*mask is invalid' +t POST libpod/networks/create subnets='[{"subnet":"10.10.134.0/65"}]' 500 \ + .cause~'.*invalid CIDR address: 10.10.134.0/65' # network list t GET libpod/networks/json 200 t GET libpod/networks/json?filters='{"name":["network1"]}' 200 \ length=1 \ - .[0].Name=network1 + .[0].name=network1 t GET networks 200 #inspect network @@ -121,7 +131,8 @@ t DELETE libpod/networks/network2 200 \ .[0].Err=null # test until filter - libpod api -podman network create network5 --label xyz +t POST libpod/networks/create name='"network5"' labels='{"xyz":""}' 200 \ + .name=network5 # with date way back in the past, network should not be deleted t POST libpod/networks/prune?filters='{"until":["500000"]}' 200 @@ -132,7 +143,8 @@ t POST libpod/networks/prune?filters='{"until":["5000000000"]}' 200 t GET libpod/networks/json?filters='{"label":["xyz"]}' 200 length=0 # test until filter - compat api -podman network create network6 --label zaq +t POST networks/create Name='"network6"' Labels='{"zaq":""}' 201 \ + .Id~[0-9a-f]\\{64\\} # with date way back in the past, network should not be deleted t POST networks/prune?filters='{"until":["500000"]}' 200 @@ -143,7 +155,9 @@ t POST networks/prune?filters='{"until":["5000000000"]}' 200 t GET networks?filters='{"label":["zaq"]}' 200 length=0 # test macvlan network response -podman network create --driver macvlan macvlan1 +t POST libpod/networks/create name='"macvlan1"' driver="macvlan" 200 \ + .name=macvlan1 \ + .driver=macvlan # libpod api inspect the macvlan network t GET libpod/networks/macvlan1/json 200 .name="macvlan1" diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 59615d009..12165d92d 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -374,10 +374,10 @@ var _ = Describe("Podman inspect", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) - session := podmanTest.Podman([]string{"inspect", name, "--format", "{{.cniVersion}}"}) + session := podmanTest.Podman([]string{"inspect", name, "--format", "{{.Driver}}"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("bridge")) }) It("podman inspect a volume", func() { diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index fb4a144fa..d419a701d 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -2,61 +2,17 @@ package integration import ( "encoding/json" - "io/ioutil" "net" "os" - "strings" - cniversion "github.com/containernetworking/cni/pkg/version" - "github.com/containers/podman/v3/libpod/network" + "github.com/containers/podman/v3/libpod/network/types" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" - "github.com/pkg/errors" ) -var ErrPluginNotFound = errors.New("plugin not found") - -func findPluginByName(plugins interface{}, pluginType string) (interface{}, error) { - for _, p := range plugins.([]interface{}) { - r := p.(map[string]interface{}) - if pluginType == r["type"] { - return p, nil - } - } - return nil, errors.Wrap(ErrPluginNotFound, pluginType) -} - -func genericPluginsToBridge(plugins interface{}, pluginType string) (network.HostLocalBridge, error) { - var bridge network.HostLocalBridge - generic, err := findPluginByName(plugins, pluginType) - if err != nil { - return bridge, err - } - b, err := json.Marshal(generic) - if err != nil { - return bridge, err - } - err = json.Unmarshal(b, &bridge) - return bridge, err -} - -func genericPluginsToPortMap(plugins interface{}, pluginType string) (network.PortMapConfig, error) { - var portMap network.PortMapConfig - generic, err := findPluginByName(plugins, "portmap") - if err != nil { - return portMap, err - } - b, err := json.Marshal(generic) - if err != nil { - return portMap, err - } - err = json.Unmarshal(b, &portMap) - return portMap, err -} - func removeNetworkDevice(name string) { session := SystemExec("ip", []string{"link", "delete", name}) session.WaitWithDefaultTimeout() @@ -85,59 +41,7 @@ var _ = Describe("Podman network create", func() { processTestResult(f) }) - It("podman network create with no input", func() { - var result network.NcList - - nc := podmanTest.Podman([]string{"network", "create"}) - nc.WaitWithDefaultTimeout() - Expect(nc).Should(Exit(0)) - - fileContent, err := ioutil.ReadFile(nc.OutputToString()) - Expect(err).To(BeNil()) - err = json.Unmarshal(fileContent, &result) - Expect(err).To(BeNil()) - defer podmanTest.removeCNINetwork(result["name"].(string)) - Expect(result["cniVersion"]).To(Equal(cniversion.Current())) - Expect(strings.HasPrefix(result["name"].(string), "cni-podman")).To(BeTrue()) - - bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge") - Expect(err).To(BeNil()) - portMapPlugin, err := genericPluginsToPortMap(result["plugins"], "portmap") - Expect(err).To(BeNil()) - - Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("0.0.0.0/0")) - Expect(bridgePlugin.IsGW).To(BeTrue()) - Expect(bridgePlugin.IPMasq).To(BeTrue()) - Expect(bridgePlugin.IPAM.Ranges[0][0].Gateway).ToNot(BeEmpty()) - Expect(portMapPlugin.Capabilities["portMappings"]).To(BeTrue()) - - }) - - It("podman network create with name", func() { - var ( - results []network.NcList - ) - - netName := "inspectnet-" + stringid.GenerateNonCryptoID() - nc := podmanTest.Podman([]string{"network", "create", netName}) - nc.WaitWithDefaultTimeout() - defer podmanTest.removeCNINetwork(netName) - Expect(nc).Should(Exit(0)) - - inspect := podmanTest.Podman([]string{"network", "inspect", netName}) - inspect.WaitWithDefaultTimeout() - - err := json.Unmarshal([]byte(inspect.OutputToString()), &results) - Expect(err).To(BeNil()) - result := results[0] - Expect(result["name"]).To(Equal(netName)) - - }) - It("podman network create with name and subnet", func() { - var ( - results []network.NcList - ) netName := "subnet-" + stringid.GenerateNonCryptoID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", netName}) nc.WaitWithDefaultTimeout() @@ -147,25 +51,25 @@ var _ = Describe("Podman network create", func() { // 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).To(BeNil()) + Expect(results).To(HaveLen(1)) result := results[0] - Expect(result["name"]).To(Equal(netName)) - - // JSON the bridge info - bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge") - Expect(err).To(BeNil()) - // check that gateway is added to config - Expect(bridgePlugin.IPAM.Ranges[0][0].Gateway).To(Equal("10.11.12.1")) + Expect(result.Name).To(Equal(netName)) + Expect(result.Subnets).To(HaveLen(1)) + Expect(result.Subnets[0].Gateway.String()).To(Equal("10.11.12.1")) // Once a container executes a new network, the nic will be created. We should clean those up // best we can - defer removeNetworkDevice(bridgePlugin.BrName) + defer removeNetworkDevice(result.NetworkInterface) try := podmanTest.Podman([]string{"run", "-it", "--rm", "--network", netName, ALPINE, "sh", "-c", "ip addr show eth0 | awk ' /inet / {print $2}'"}) try.WaitWithDefaultTimeout() + Expect(try).To(Exit(0)) _, subnet, err := net.ParseCIDR("10.11.12.0/24") Expect(err).To(BeNil()) @@ -177,10 +81,6 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with name and IPv6 subnet", func() { - SkipIfRootless("FIXME It needs the ip6tables modules loaded") - var ( - results []network.NcList - ) netName := "ipv6-" + stringid.GenerateNonCryptoID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:1:2:3:4::/64", netName}) nc.WaitWithDefaultTimeout() @@ -190,24 +90,26 @@ var _ = Describe("Podman network create", func() { // 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).To(BeNil()) + Expect(results).To(HaveLen(1)) result := results[0] - Expect(result["name"]).To(Equal(netName)) - - // JSON the bridge info - bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge") - Expect(err).To(BeNil()) - Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) + Expect(result.Name).To(Equal(netName)) + Expect(result.Subnets).To(HaveLen(1)) + Expect(result.Subnets[0].Gateway.String()).To(Equal("fd00:1:2:3::1")) + Expect(result.Subnets[0].Subnet.String()).To(Equal("fd00:1:2:3::/64")) // Once a container executes a new network, the nic will be created. We should clean those up // best we can - defer removeNetworkDevice(bridgePlugin.BrName) + defer removeNetworkDevice(result.NetworkInterface) try := podmanTest.Podman([]string{"run", "-it", "--rm", "--network", netName, ALPINE, "sh", "-c", "ip addr show eth0 | grep global | awk ' /inet6 / {print $2}'"}) try.WaitWithDefaultTimeout() + Expect(try).To(Exit(0)) _, subnet, err := net.ParseCIDR("fd00:1:2:3:4::/64") Expect(err).To(BeNil()) @@ -218,12 +120,8 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with name and IPv6 flag (dual-stack)", func() { - SkipIfRootless("FIXME It needs the ip6tables modules loaded") - var ( - results []network.NcList - ) netName := "dual-" + stringid.GenerateNonCryptoID() - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:3:2:1::/64", "--ipv6", netName}) + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:3:2::/64", "--ipv6", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) Expect(nc).Should(Exit(0)) @@ -231,38 +129,32 @@ var _ = Describe("Podman network create", func() { // 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).To(BeNil()) + Expect(results).To(HaveLen(1)) result := results[0] - Expect(result["name"]).To(Equal(netName)) - - // JSON the bridge info - bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge") - Expect(err).To(BeNil()) - Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) - Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) - - Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) - Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) - Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) - Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) - Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) + Expect(result.Name).To(Equal(netName)) + Expect(result.Subnets).To(HaveLen(2)) + Expect(result.Subnets[0].Subnet.IP).ToNot(BeNil()) + Expect(result.Subnets[1].Subnet.IP).ToNot(BeNil()) - _, subnet11, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + _, subnet11, err := net.ParseCIDR(result.Subnets[0].Subnet.String()) Expect(err).To(BeNil()) - _, subnet12, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + _, subnet12, err := net.ParseCIDR(result.Subnets[1].Subnet.String()) Expect(err).To(BeNil()) // Once a container executes a new network, the nic will be created. We should clean those up // best we can - defer removeNetworkDevice(bridgePlugin.BrName) + defer removeNetworkDevice(result.NetworkInterface) // create a second network to check the auto assigned ipv4 subnet does not overlap // https://github.com/containers/podman/issues/11032 netName2 := "dual-" + stringid.GenerateNonCryptoID() - nc = podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:6:3:2:1::/64", "--ipv6", netName2}) + nc = podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:10:3:2::/64", "--ipv6", netName2}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName2) Expect(nc).Should(Exit(0)) @@ -270,27 +162,21 @@ var _ = Describe("Podman network create", func() { // Inspect the network configuration inspect = podmanTest.Podman([]string{"network", "inspect", netName2}) inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) // JSON the network configuration into something usable err = json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).To(BeNil()) + Expect(results).To(HaveLen(1)) result = results[0] - Expect(result["name"]).To(Equal(netName2)) + Expect(result.Name).To(Equal(netName2)) + Expect(result.Subnets).To(HaveLen(2)) + Expect(result.Subnets[0].Subnet.IP).ToNot(BeNil()) + Expect(result.Subnets[1].Subnet.IP).ToNot(BeNil()) - // JSON the bridge info - bridgePlugin, err = genericPluginsToBridge(result["plugins"], "bridge") - Expect(err).To(BeNil()) - Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) - Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) - Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) - Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) - Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) - Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) - Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) - - _, subnet21, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + _, subnet21, err := net.ParseCIDR(result.Subnets[0].Subnet.String()) Expect(err).To(BeNil()) - _, subnet22, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + _, subnet22, err := net.ParseCIDR(result.Subnets[1].Subnet.String()) Expect(err).To(BeNil()) // check that the subnets do not overlap @@ -321,15 +207,31 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with ipv4 subnet and ipv6 flag", func() { - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--ipv6", stringid.GenerateNonCryptoID()}) + name := stringid.GenerateNonCryptoID() + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--ipv6", name}) nc.WaitWithDefaultTimeout() - Expect(nc).To(ExitWithError()) + Expect(nc).To(Exit(0)) + defer podmanTest.removeCNINetwork(name) + + nc = podmanTest.Podman([]string{"network", "inspect", name}) + nc.WaitWithDefaultTimeout() + Expect(nc).To(Exit(0)) + Expect(nc.OutputToString()).To(ContainSubstring(`::/64`)) + Expect(nc.OutputToString()).To(ContainSubstring(`10.11.12.0/24`)) }) It("podman network create with empty subnet and ipv6 flag", func() { - nc := podmanTest.Podman([]string{"network", "create", "--ipv6", stringid.GenerateNonCryptoID()}) + name := stringid.GenerateNonCryptoID() + nc := podmanTest.Podman([]string{"network", "create", "--ipv6", name}) nc.WaitWithDefaultTimeout() - Expect(nc).To(ExitWithError()) + Expect(nc).To(Exit(0)) + defer podmanTest.removeCNINetwork(name) + + nc = podmanTest.Podman([]string{"network", "inspect", name}) + nc.WaitWithDefaultTimeout() + Expect(nc).To(Exit(0)) + Expect(nc.OutputToString()).To(ContainSubstring(`::/64`)) + Expect(nc.OutputToString()).To(ContainSubstring(`.0/24`)) }) It("podman network create with invalid IP", func() { @@ -371,7 +273,6 @@ var _ = Describe("Podman network create", func() { }) It("podman network create two IPv6 networks with same subnet should fail", func() { - SkipIfRootless("FIXME It needs the ip6tables modules loaded") netName1 := "subipv61-" + stringid.GenerateNonCryptoID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:4:4:4::/64", "--ipv6", netName1}) nc.WaitWithDefaultTimeout() @@ -401,7 +302,7 @@ var _ = Describe("Podman network create", func() { nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() Expect(nc).Should(Exit(0)) - Expect(nc.OutputToString()).To(ContainSubstring(`"mtu": 9000,`)) + Expect(nc.OutputToString()).To(ContainSubstring(`"mtu": "9000"`)) }) It("podman network create with vlan option", func() { @@ -414,7 +315,7 @@ var _ = Describe("Podman network create", func() { nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() Expect(nc).Should(Exit(0)) - Expect(nc.OutputToString()).To(ContainSubstring(`"vlan": 9`)) + Expect(nc.OutputToString()).To(ContainSubstring(`"vlan": "9"`)) }) It("podman network create with invalid option", func() { @@ -434,7 +335,7 @@ var _ = Describe("Podman network create", func() { // Not performing this check on remote tests because it is a logrus error which does // not come back via stderr on the remote client. if !IsRemote() { - Expect(nc.ErrorToString()).To(ContainSubstring("dnsname and --internal networks are incompatible")) + Expect(nc.ErrorToString()).To(ContainSubstring("dnsname and internal networks are incompatible")) } nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 1959ed555..7e56b8a25 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -1,11 +1,13 @@ package integration import ( + "encoding/json" "fmt" "os" "strings" "time" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/rootless" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" @@ -61,17 +63,17 @@ var _ = Describe("Podman network", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) - session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge"}) + session := podmanTest.Podman([]string{"network", "ls", "--filter", "driver=bridge"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeTrue()) }) - It("podman network list --filter plugin and name", func() { + It("podman network list --filter driver and name", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) - session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge", "--filter", "name=" + name}) + session := podmanTest.Podman([]string{"network", "ls", "--filter", "driver=bridge", "--filter", "name=" + name}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(name)) @@ -136,7 +138,7 @@ var _ = Describe("Podman network", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) - session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=test"}) + session := podmanTest.Podman([]string{"network", "ls", "--filter", "label=abc"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeFalse()) @@ -155,7 +157,7 @@ var _ = Describe("Podman network", func() { session = podmanTest.Podman([]string{"network", "ls", "--filter", "id=" + netID}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - expectedTable := "NETWORK ID NAME VERSION PLUGINS" + expectedTable := "NETWORK ID NAME DRIVER" Expect(session.OutputToString()).To(ContainSubstring(expectedTable)) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID}) @@ -176,7 +178,7 @@ var _ = Describe("Podman network", func() { session = podmanTest.Podman([]string{"network", "inspect", netID[1:]}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitWithError()) - Expect(session.ErrorToString()).To(ContainSubstring("no such network")) + Expect(session.ErrorToString()).To(ContainSubstring("network not found")) session = podmanTest.Podman([]string{"network", "rm", netID}) session.WaitWithDefaultTimeout() @@ -239,10 +241,10 @@ var _ = Describe("Podman network", func() { name, path := generateNetworkConfig(podmanTest) defer removeConf(path) - session := podmanTest.Podman([]string{"network", "inspect", name, "--format", "{{.cniVersion}}"}) + session := podmanTest.Podman([]string{"network", "inspect", name, "--format", "{{.Driver}}"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("bridge")) }) It("podman inspect container single CNI network", func() { @@ -512,9 +514,14 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - out, err := inspect.jq(".[0].plugins[0].master") + // JSON the network configuration into something usable + var results []types.Network + err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).To(BeNil()) - Expect(out).To(Equal("\"\"")) + Expect(results).To(HaveLen(1)) + result := results[0] + Expect(result.NetworkInterface).To(Equal("")) + Expect(result.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() @@ -532,13 +539,16 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - out, err := inspect.jq(".[0].plugins[0].master") + var results []types.Network + err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).To(BeNil()) - Expect(out).To(Equal(`"lo"`)) + Expect(results).To(HaveLen(1)) + result := results[0] - ipamType, err := inspect.jq(".[0].plugins[0].ipam.type") - Expect(err).To(BeNil()) - Expect(ipamType).To(Equal(`"dhcp"`)) + Expect(result.Driver).To(Equal("macvlan")) + Expect(result.NetworkInterface).To(Equal("lo")) + Expect(result.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + Expect(result.Subnets).To(HaveLen(0)) nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() @@ -572,33 +582,20 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - mtu, err := inspect.jq(".[0].plugins[0].mtu") - Expect(err).To(BeNil()) - Expect(mtu).To(Equal("1500")) - - name, err := inspect.jq(".[0].plugins[0].type") + var results []types.Network + err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).To(BeNil()) - Expect(name).To(Equal(`"macvlan"`)) + Expect(results).To(HaveLen(1)) + result := results[0] - netInt, err := inspect.jq(".[0].plugins[0].master") - Expect(err).To(BeNil()) - Expect(netInt).To(Equal(`"lo"`)) - - ipamType, err := inspect.jq(".[0].plugins[0].ipam.type") - Expect(err).To(BeNil()) - Expect(ipamType).To(Equal(`"host-local"`)) + Expect(result.Options).To(HaveKeyWithValue("mtu", "1500")) + Expect(result.Driver).To(Equal("macvlan")) + Expect(result.NetworkInterface).To(Equal("lo")) + Expect(result.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) - gw, err := inspect.jq(".[0].plugins[0].ipam.ranges[0][0].gateway") - Expect(err).To(BeNil()) - Expect(gw).To(Equal(`"192.168.1.254"`)) - - subnet, err := inspect.jq(".[0].plugins[0].ipam.ranges[0][0].subnet") - Expect(err).To(BeNil()) - Expect(subnet).To(Equal(`"192.168.1.0/24"`)) - - routes, err := inspect.jq(".[0].plugins[0].ipam.routes[0].dst") - Expect(err).To(BeNil()) - Expect(routes).To(Equal(`"0.0.0.0/0"`)) + Expect(result.Subnets).To(HaveLen(1)) + Expect(result.Subnets[0].Subnet.String()).To(Equal("192.168.1.0/24")) + Expect(result.Subnets[0].Gateway.String()).To(Equal("192.168.1.254")) nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index bdedfae19..ef00d0366 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -6,7 +6,7 @@ load helpers @test "podman network - basic tests" { - heading="*NETWORK*ID*NAME*VERSION*PLUGINS*" + heading="*NETWORK*ID*NAME*DRIVER*" run_podman network ls if [[ ${output} != ${heading} ]]; then die "network ls expected heading is not available" @@ -151,7 +151,7 @@ load helpers local mysubnet=$(random_rfc1918_subnet) run_podman network create --subnet "${mysubnet}.0/24" $mynetname - is "$output" ".*/cni/net.d/$mynetname.conflist" "output of 'network create'" + is "$output" "$mynetname" "output of 'network create'" # (Assert that output is formatted, not a one-line blob: #8011) run_podman network inspect $mynetname @@ -189,7 +189,7 @@ load helpers # Cannot create network with the same name run_podman 125 network create $mynetname - is "$output" "Error: the network name $mynetname is already used" \ + is "$output" "Error: network name $mynetname already used: network already exists" \ "Trying to create an already-existing network" run_podman rm $cid @@ -208,14 +208,8 @@ load helpers INDEX1=$PODMAN_TMPDIR/hello.txt echo $random_1 > $INDEX1 - # use default network for root + # use default network local netname=podman - # for rootless we have to create a custom network since there is no default network - if is_rootless; then - netname=testnet-$(random_string 10) - run_podman network create $netname - is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" - fi # Bind-mount this file with a different name to a container running httpd run_podman run -d --name myweb -p "$HOST_PORT:80" \ @@ -226,9 +220,9 @@ load helpers cid=$output run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" - ip="$output" + ip1="$output" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" - mac="$output" + mac1="$output" # Verify http contents: curl from localhost run curl -s $SERVER/index.txt @@ -248,22 +242,51 @@ load helpers # reload the network to recreate the iptables rules run_podman network reload $cid - is "$output" "$cid" "Output does not match container ID" + is "$output" "$cid" "Output does match container ID" # check that we still have the same mac and ip run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" - is "$output" "$ip" "IP address changed after podman network reload" + is "$output" "$ip1" "IP address changed after podman network reload" run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" - is "$output" "$mac" "MAC address changed after podman network reload" + is "$output" "$mac1" "MAC address changed after podman network reload" # check that we can still curl run curl -s $SERVER/index.txt is "$output" "$random_1" "curl 127.0.0.1:/index.txt" + # create second network + netname2=testnet-$(random_string 10) + # TODO add --ipv6 and uncomment the ipv6 checks below once cni plugins 1.0 is available on ubuntu CI VMs. + run_podman network create $netname2 + is "$output" "$netname2" "output of 'network create'" + + # connect the container to the second network + run_podman network connect $netname2 $cid + + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").IPAddress}}" + ip2="$output" + #run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").GlobalIPv6Address}}" + #is "$output" "fd.*:.*" "IPv6 address should start with fd..." + #ipv6="$output" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").MacAddress}}" + mac2="$output" + # make sure --all is working and that this # cmd also works if the iptables still exists run_podman network reload --all - is "$output" "$cid" "Output does not match container ID" + is "$output" "$cid" "Output does match container ID" + + # check that both network keep there ip and mac + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" + is "$output" "$ip1" "IP address changed after podman network reload ($netname)" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" + is "$output" "$mac1" "MAC address changed after podman network reload ($netname)" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").IPAddress}}" + is "$output" "$ip2" "IP address changed after podman network reload ($netname2)" + #run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").GlobalIPv6Address}}" + #is "$output" "$ipv6" "IPv6 address changed after podman network reload ($netname2)" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").MacAddress}}" + is "$output" "$mac2" "MAC address changed after podman network reload ($netname2)" # check that we can still curl run curl -s $SERVER/index.txt @@ -272,9 +295,11 @@ load helpers # cleanup the container run_podman rm -f $cid - if is_rootless; then - run_podman network rm -f $netname - fi + # test that we cannot remove the default network + run_podman 125 network rm -f $netname + is "$output" "Error: default network $netname cannot be removed" "Remove default network" + + run_podman network rm -f $netname2 } @test "podman rootless cni adds /usr/sbin to PATH" { @@ -325,7 +350,7 @@ load helpers local netname=testnet-$(random_string 10) run_podman network create --subnet $mysubnet.0/24 $netname - is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + is "$output" "$netname" "output of 'network create'" run_podman run --rm --network $netname $IMAGE cat /etc/resolv.conf if grep -E "$ipv6_regex" <<< $output; then @@ -339,7 +364,7 @@ load helpers netname=testnet-$(random_string 10) run_podman network create --subnet $mysubnet $netname - is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + is "$output" "$netname" "output of 'network create'" run_podman run --rm --network $netname $IMAGE cat /etc/resolv.conf # "is" does not like the ipv6 regex @@ -362,11 +387,11 @@ load helpers local netname=testnet-$(random_string 10) run_podman network create $netname - is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + is "$output" "$netname" "output of 'network create'" local netname2=testnet2-$(random_string 10) run_podman network create $netname2 - is "$output" ".*/cni/net.d/$netname2.conflist" "output of 'network create'" + is "$output" "$netname2" "output of 'network create'" # First, run a container in background to ensure that the rootless cni ns # is not destroyed after network disconnect. @@ -447,7 +472,7 @@ load helpers local netname=testnet-$(random_string 10) run_podman network create $netname - is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + is "$output" "$netname" "output of 'network create'" for network in "slirp4netns" "$netname"; do # Start container with the restart always policy diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go deleted file mode 100644 index b4db50b9a..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/addr_linux.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "fmt" - "syscall" - "time" - - "github.com/vishvananda/netlink" -) - -const SETTLE_INTERVAL = 50 * time.Millisecond - -// SettleAddresses waits for all addresses on a link to leave tentative state. -// This is particularly useful for ipv6, where all addresses need to do DAD. -// There is no easy way to wait for this as an event, so just loop until the -// addresses are no longer tentative. -// If any addresses are still tentative after timeout seconds, then error. -func SettleAddresses(ifName string, timeout int) error { - link, err := netlink.LinkByName(ifName) - if err != nil { - return fmt.Errorf("failed to retrieve link: %v", err) - } - - deadline := time.Now().Add(time.Duration(timeout) * time.Second) - for { - addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) - if err != nil { - return fmt.Errorf("could not list addresses: %v", err) - } - - if len(addrs) == 0 { - return nil - } - - ok := true - for _, addr := range addrs { - if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 { - ok = false - break // Break out of the `range addrs`, not the `for` - } - } - - if ok { - return nil - } - if time.Now().After(deadline) { - return fmt.Errorf("link %s still has tentative addresses after %d seconds", - ifName, - timeout) - } - - time.Sleep(SETTLE_INTERVAL) - } -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go b/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go deleted file mode 100644 index 7acc2d47c..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/cidr.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "math/big" - "net" -) - -// NextIP returns IP incremented by 1 -func NextIP(ip net.IP) net.IP { - i := ipToInt(ip) - return intToIP(i.Add(i, big.NewInt(1))) -} - -// PrevIP returns IP decremented by 1 -func PrevIP(ip net.IP) net.IP { - i := ipToInt(ip) - return intToIP(i.Sub(i, big.NewInt(1))) -} - -// Cmp compares two IPs, returning the usual ordering: -// a < b : -1 -// a == b : 0 -// a > b : 1 -func Cmp(a, b net.IP) int { - aa := ipToInt(a) - bb := ipToInt(b) - return aa.Cmp(bb) -} - -func ipToInt(ip net.IP) *big.Int { - if v := ip.To4(); v != nil { - return big.NewInt(0).SetBytes(v) - } - return big.NewInt(0).SetBytes(ip.To16()) -} - -func intToIP(i *big.Int) net.IP { - return net.IP(i.Bytes()) -} - -// Network masks off the host portion of the IP -func Network(ipn *net.IPNet) *net.IPNet { - return &net.IPNet{ - IP: ipn.IP.Mask(ipn.Mask), - Mask: ipn.Mask, - } -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go deleted file mode 100644 index 8216a2c38..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/ipforward_linux.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "bytes" - "io/ioutil" - - "github.com/containernetworking/cni/pkg/types/current" -) - -func EnableIP4Forward() error { - return echo1("/proc/sys/net/ipv4/ip_forward") -} - -func EnableIP6Forward() error { - return echo1("/proc/sys/net/ipv6/conf/all/forwarding") -} - -// EnableForward will enable forwarding for all configured -// address families -func EnableForward(ips []*current.IPConfig) error { - v4 := false - v6 := false - - for _, ip := range ips { - if ip.Version == "4" && !v4 { - if err := EnableIP4Forward(); err != nil { - return err - } - v4 = true - } else if ip.Version == "6" && !v6 { - if err := EnableIP6Forward(); err != nil { - return err - } - v6 = true - } - } - return nil -} - -func echo1(f string) error { - if content, err := ioutil.ReadFile(f); err == nil { - if bytes.Equal(bytes.TrimSpace(content), []byte("1")) { - return nil - } - } - return ioutil.WriteFile(f, []byte("1"), 0644) -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go deleted file mode 100644 index cc640a605..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/ipmasq_linux.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "fmt" - "net" - - "github.com/coreos/go-iptables/iptables" -) - -// SetupIPMasq installs iptables rules to masquerade traffic -// coming from ip of ipn and going outside of ipn -func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error { - isV6 := ipn.IP.To4() == nil - - var ipt *iptables.IPTables - var err error - var multicastNet string - - if isV6 { - ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) - multicastNet = "ff00::/8" - } else { - ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) - multicastNet = "224.0.0.0/4" - } - if err != nil { - return fmt.Errorf("failed to locate iptables: %v", err) - } - - // Create chain if doesn't exist - exists := false - chains, err := ipt.ListChains("nat") - if err != nil { - return fmt.Errorf("failed to list chains: %v", err) - } - for _, ch := range chains { - if ch == chain { - exists = true - break - } - } - if !exists { - if err = ipt.NewChain("nat", chain); err != nil { - return err - } - } - - // Packets to this network should not be touched - if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil { - return err - } - - // Don't masquerade multicast - pods should be able to talk to other pods - // on the local network via multicast. - if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil { - return err - } - - // Packets from the specific IP of this network will hit the chain - return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) -} - -// TeardownIPMasq undoes the effects of SetupIPMasq -func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error { - isV6 := ipn.IP.To4() == nil - - var ipt *iptables.IPTables - var err error - - if isV6 { - ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) - } else { - ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) - } - if err != nil { - return fmt.Errorf("failed to locate iptables: %v", err) - } - - err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) - if err != nil && !isNotExist(err) { - return err - } - - // for downward compatibility - err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment) - if err != nil && !isNotExist(err) { - return err - } - - err = ipt.ClearChain("nat", chain) - if err != nil && !isNotExist(err) { - return err - - } - - err = ipt.DeleteChain("nat", chain) - if err != nil && !isNotExist(err) { - return err - } - - return nil -} - -// isNotExist returnst true if the error is from iptables indicating -// that the target does not exist. -func isNotExist(err error) bool { - e, ok := err.(*iptables.Error) - if !ok { - return false - } - return e.IsNotExist() -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go deleted file mode 100644 index f8781cf19..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/link_linux.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "crypto/rand" - "errors" - "fmt" - "net" - "os" - - "github.com/safchain/ethtool" - "github.com/vishvananda/netlink" - - "github.com/containernetworking/plugins/pkg/ns" - "github.com/containernetworking/plugins/pkg/utils/hwaddr" - "github.com/containernetworking/plugins/pkg/utils/sysctl" -) - -var ( - ErrLinkNotFound = errors.New("link not found") -) - -func makeVethPair(name, peer string, mtu int) (netlink.Link, error) { - veth := &netlink.Veth{ - LinkAttrs: netlink.LinkAttrs{ - Name: name, - Flags: net.FlagUp, - MTU: mtu, - }, - PeerName: peer, - } - if err := netlink.LinkAdd(veth); err != nil { - return nil, err - } - // Re-fetch the link to get its creation-time parameters, e.g. index and mac - veth2, err := netlink.LinkByName(name) - if err != nil { - netlink.LinkDel(veth) // try and clean up the link if possible. - return nil, err - } - - return veth2, nil -} - -func peerExists(name string) bool { - if _, err := netlink.LinkByName(name); err != nil { - return false - } - return true -} - -func makeVeth(name, vethPeerName string, mtu int) (peerName string, veth netlink.Link, err error) { - for i := 0; i < 10; i++ { - if vethPeerName != "" { - peerName = vethPeerName - } else { - peerName, err = RandomVethName() - if err != nil { - return - } - } - - veth, err = makeVethPair(name, peerName, mtu) - switch { - case err == nil: - return - - case os.IsExist(err): - if peerExists(peerName) && vethPeerName == "" { - continue - } - err = fmt.Errorf("container veth name provided (%v) already exists", name) - return - - default: - err = fmt.Errorf("failed to make veth pair: %v", err) - return - } - } - - // should really never be hit - err = fmt.Errorf("failed to find a unique veth name") - return -} - -// RandomVethName returns string "veth" with random prefix (hashed from entropy) -func RandomVethName() (string, error) { - entropy := make([]byte, 4) - _, err := rand.Reader.Read(entropy) - if err != nil { - return "", fmt.Errorf("failed to generate random veth name: %v", err) - } - - // NetworkManager (recent versions) will ignore veth devices that start with "veth" - return fmt.Sprintf("veth%x", entropy), nil -} - -func RenameLink(curName, newName string) error { - link, err := netlink.LinkByName(curName) - if err == nil { - err = netlink.LinkSetName(link, newName) - } - return err -} - -func ifaceFromNetlinkLink(l netlink.Link) net.Interface { - a := l.Attrs() - return net.Interface{ - Index: a.Index, - MTU: a.MTU, - Name: a.Name, - HardwareAddr: a.HardwareAddr, - Flags: a.Flags, - } -} - -// SetupVethWithName sets up a pair of virtual ethernet devices. -// Call SetupVethWithName from inside the container netns. It will create both veth -// devices and move the host-side veth into the provided hostNS namespace. -// hostVethName: If hostVethName is not specified, the host-side veth name will use a random string. -// On success, SetupVethWithName returns (hostVeth, containerVeth, nil) -func SetupVethWithName(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { - hostVethName, contVeth, err := makeVeth(contVethName, hostVethName, mtu) - if err != nil { - return net.Interface{}, net.Interface{}, err - } - - if err = netlink.LinkSetUp(contVeth); err != nil { - return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err) - } - - hostVeth, err := netlink.LinkByName(hostVethName) - if err != nil { - return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err) - } - - if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { - return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err) - } - - err = hostNS.Do(func(_ ns.NetNS) error { - hostVeth, err = netlink.LinkByName(hostVethName) - if err != nil { - return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err) - } - - if err = netlink.LinkSetUp(hostVeth); err != nil { - return fmt.Errorf("failed to set %q up: %v", hostVethName, err) - } - - // we want to own the routes for this interface - _, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", hostVethName), "0") - return nil - }) - if err != nil { - return net.Interface{}, net.Interface{}, err - } - return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil -} - -// SetupVeth sets up a pair of virtual ethernet devices. -// Call SetupVeth from inside the container netns. It will create both veth -// devices and move the host-side veth into the provided hostNS namespace. -// On success, SetupVeth returns (hostVeth, containerVeth, nil) -func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) { - return SetupVethWithName(contVethName, "", mtu, hostNS) -} - -// DelLinkByName removes an interface link. -func DelLinkByName(ifName string) error { - iface, err := netlink.LinkByName(ifName) - if err != nil { - if _, ok := err.(netlink.LinkNotFoundError); ok { - return ErrLinkNotFound - } - return fmt.Errorf("failed to lookup %q: %v", ifName, err) - } - - if err = netlink.LinkDel(iface); err != nil { - return fmt.Errorf("failed to delete %q: %v", ifName, err) - } - - return nil -} - -// DelLinkByNameAddr remove an interface and returns its addresses -func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) { - iface, err := netlink.LinkByName(ifName) - if err != nil { - if _, ok := err.(netlink.LinkNotFoundError); ok { - return nil, ErrLinkNotFound - } - return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err) - } - - addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL) - if err != nil { - return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err) - } - - if err = netlink.LinkDel(iface); err != nil { - return nil, fmt.Errorf("failed to delete %q: %v", ifName, err) - } - - out := []*net.IPNet{} - for _, addr := range addrs { - if addr.IP.IsGlobalUnicast() { - out = append(out, addr.IPNet) - } - } - - return out, nil -} - -func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error { - iface, err := netlink.LinkByName(ifName) - if err != nil { - return fmt.Errorf("failed to lookup %q: %v", ifName, err) - } - - switch { - case ip4 == nil && ip6 == nil: - return fmt.Errorf("neither ip4 or ip6 specified") - - case ip4 != nil: - { - hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix) - if err != nil { - return fmt.Errorf("failed to generate hardware addr: %v", err) - } - if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil { - return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err) - } - } - case ip6 != nil: - // TODO: IPv6 - } - - return nil -} - -// GetVethPeerIfindex returns the veth link object, the peer ifindex of the -// veth, or an error. This peer ifindex will only be valid in the peer's -// network namespace. -func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) { - link, err := netlink.LinkByName(ifName) - if err != nil { - return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err) - } - if _, ok := link.(*netlink.Veth); !ok { - return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName) - } - - // veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex) - // on 4.1 and higher kernels - peerIndex := link.Attrs().ParentIndex - if peerIndex <= 0 { - // Fall back to ethtool for 4.0 and earlier kernels - e, err := ethtool.NewEthtool() - if err != nil { - return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err) - } - defer e.Close() - - stats, err := e.Stats(link.Attrs().Name) - if err != nil { - return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err) - } - n, ok := stats["peer_ifindex"] - if !ok { - return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats") - } - if n > 32767 || n == 0 { - return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n) - } - peerIndex = int(n) - } - - return link, peerIndex, nil -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go deleted file mode 100644 index f5c0d0803..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/route_linux.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015-2017 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "net" - - "github.com/vishvananda/netlink" -) - -// AddRoute adds a universally-scoped route to a device. -func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { - return netlink.RouteAdd(&netlink.Route{ - LinkIndex: dev.Attrs().Index, - Scope: netlink.SCOPE_UNIVERSE, - Dst: ipn, - Gw: gw, - }) -} - -// AddHostRoute adds a host-scoped route to a device. -func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { - return netlink.RouteAdd(&netlink.Route{ - LinkIndex: dev.Attrs().Index, - Scope: netlink.SCOPE_HOST, - Dst: ipn, - Gw: gw, - }) -} - -// AddDefaultRoute sets the default route on the given gateway. -func AddDefaultRoute(gw net.IP, dev netlink.Link) error { - _, defNet, _ := net.ParseCIDR("0.0.0.0/0") - return AddRoute(defNet, gw, dev) -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go b/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go deleted file mode 100644 index 7623c5e13..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/ip/utils_linux.go +++ /dev/null @@ -1,120 +0,0 @@ -// +build linux - -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ip - -import ( - "fmt" - "net" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" - "github.com/vishvananda/netlink" -) - -func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error { - - // Ensure ips - for _, ips := range resultIPs { - ourAddr := netlink.Addr{IPNet: &ips.Address} - match := false - - link, err := netlink.LinkByName(ifName) - if err != nil { - return fmt.Errorf("Cannot find container link %v", ifName) - } - - addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL) - if err != nil { - return fmt.Errorf("Cannot obtain List of IP Addresses") - } - - for _, addr := range addrList { - if addr.Equal(ourAddr) { - match = true - break - } - } - if match == false { - return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName) - } - - // Convert the host/prefixlen to just prefix for route lookup. - _, ourPrefix, err := net.ParseCIDR(ourAddr.String()) - - findGwy := &netlink.Route{Dst: ourPrefix} - routeFilter := netlink.RT_FILTER_DST - var family int - - switch { - case ips.Version == "4": - family = netlink.FAMILY_V4 - case ips.Version == "6": - family = netlink.FAMILY_V6 - default: - return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName) - } - - gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter) - if err != nil { - return fmt.Errorf("Error %v trying to find Gateway %v for interface %v", err, ips.Gateway, ifName) - } - if gwy == nil { - return fmt.Errorf("Failed to find Gateway %v for interface %v", ips.Gateway, ifName) - } - } - - return nil -} - -func ValidateExpectedRoute(resultRoutes []*types.Route) error { - - // Ensure that each static route in prevResults is found in the routing table - for _, route := range resultRoutes { - find := &netlink.Route{Dst: &route.Dst, Gw: route.GW} - routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW - var family int - - switch { - case route.Dst.IP.To4() != nil: - family = netlink.FAMILY_V4 - // Default route needs Dst set to nil - if route.Dst.String() == "0.0.0.0/0" { - find = &netlink.Route{Dst: nil, Gw: route.GW} - routeFilter = netlink.RT_FILTER_DST - } - case len(route.Dst.IP) == net.IPv6len: - family = netlink.FAMILY_V6 - // Default route needs Dst set to nil - if route.Dst.String() == "::/0" { - find = &netlink.Route{Dst: nil, Gw: route.GW} - routeFilter = netlink.RT_FILTER_DST - } - default: - return fmt.Errorf("Invalid static route found %v", route) - } - - wasFound, err := netlink.RouteListFiltered(family, find, routeFilter) - if err != nil { - return fmt.Errorf("Expected Route %v not route table lookup error %v", route, err) - } - if wasFound == nil { - return fmt.Errorf("Expected Route %v not found in routing table", route) - } - } - - return nil -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go b/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go deleted file mode 100644 index aaf3b8a02..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/utils/hwaddr/hwaddr.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hwaddr - -import ( - "fmt" - "net" -) - -const ( - ipRelevantByteLen = 4 - PrivateMACPrefixString = "0a:58" -) - -var ( - // private mac prefix safe to use - PrivateMACPrefix = []byte{0x0a, 0x58} -) - -type SupportIp4OnlyErr struct{ msg string } - -func (e SupportIp4OnlyErr) Error() string { return e.msg } - -type MacParseErr struct{ msg string } - -func (e MacParseErr) Error() string { return e.msg } - -type InvalidPrefixLengthErr struct{ msg string } - -func (e InvalidPrefixLengthErr) Error() string { return e.msg } - -// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. -func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) { - switch { - - case ip.To4() == nil: - return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"} - - case len(prefix) != len(PrivateMACPrefix): - return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf( - "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)), - } - } - - ipByteLen := len(ip) - return (net.HardwareAddr)( - append( - prefix, - ip[ipByteLen-ipRelevantByteLen:ipByteLen]...), - ), nil -} diff --git a/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go b/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go deleted file mode 100644 index 7ee47e1ce..000000000 --- a/vendor/github.com/containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sysctl - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" -) - -// Sysctl provides a method to set/get values from /proc/sys - in linux systems -// new interface to set/get values of variables formerly handled by sysctl syscall -// If optional `params` have only one string value - this function will -// set this value into corresponding sysctl variable -func Sysctl(name string, params ...string) (string, error) { - if len(params) > 1 { - return "", fmt.Errorf("unexcepted additional parameters") - } else if len(params) == 1 { - return setSysctl(name, params[0]) - } - return getSysctl(name) -} - -func getSysctl(name string) (string, error) { - fullName := filepath.Join("/proc/sys", toNormalName(name)) - fullName = filepath.Clean(fullName) - data, err := ioutil.ReadFile(fullName) - if err != nil { - return "", err - } - - return string(data[:len(data)-1]), nil -} - -func setSysctl(name, value string) (string, error) { - fullName := filepath.Join("/proc/sys", toNormalName(name)) - fullName = filepath.Clean(fullName) - if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil { - return "", err - } - - return getSysctl(name) -} - -// Normalize names by using slash as separator -// Sysctl names can use dots or slashes as separator: -// - if dots are used, dots and slashes are interchanged. -// - if slashes are used, slashes and dots are left intact. -// Separator in use is determined by first occurrence. -func toNormalName(name string) string { - interchange := false - for _, c := range name { - if c == '.' { - interchange = true - break - } - if c == '/' { - break - } - } - - if interchange { - r := strings.NewReplacer(".", "/", "/", ".") - return r.Replace(name) - } - return name -} diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go deleted file mode 100644 index 4cec1a74e..000000000 --- a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package allocator - -import ( - "fmt" - "log" - "net" - "os" - "strconv" - - "github.com/containernetworking/cni/pkg/types/current" - "github.com/containernetworking/plugins/pkg/ip" - "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" -) - -type IPAllocator struct { - rangeset *RangeSet - store backend.Store - rangeID string // Used for tracking last reserved ip -} - -func NewIPAllocator(s *RangeSet, store backend.Store, id int) *IPAllocator { - return &IPAllocator{ - rangeset: s, - store: store, - rangeID: strconv.Itoa(id), - } -} - -// Get allocates an IP -func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) { - a.store.Lock() - defer a.store.Unlock() - - var reservedIP *net.IPNet - var gw net.IP - - if requestedIP != nil { - if err := canonicalizeIP(&requestedIP); err != nil { - return nil, err - } - - r, err := a.rangeset.RangeFor(requestedIP) - if err != nil { - return nil, err - } - - if requestedIP.Equal(r.Gateway) { - return nil, fmt.Errorf("requested ip %s is subnet's gateway", requestedIP.String()) - } - - reserved, err := a.store.Reserve(id, ifname, requestedIP, a.rangeID) - if err != nil { - return nil, err - } - if !reserved { - return nil, fmt.Errorf("requested IP address %s is not available in range set %s", requestedIP, a.rangeset.String()) - } - reservedIP = &net.IPNet{IP: requestedIP, Mask: r.Subnet.Mask} - gw = r.Gateway - - } else { - // try to get allocated IPs for this given id, if exists, just return error - // because duplicate allocation is not allowed in SPEC - // https://github.com/containernetworking/cni/blob/master/SPEC.md - allocatedIPs := a.store.GetByID(id, ifname) - for _, allocatedIP := range allocatedIPs { - // check whether the existing IP belong to this range set - if _, err := a.rangeset.RangeFor(allocatedIP); err == nil { - return nil, fmt.Errorf("%s has been allocated to %s, duplicate allocation is not allowed", allocatedIP.String(), id) - } - } - - iter, err := a.GetIter() - if err != nil { - return nil, err - } - for { - reservedIP, gw = iter.Next() - if reservedIP == nil { - break - } - - reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID) - if err != nil { - return nil, err - } - - if reserved { - break - } - } - } - - if reservedIP == nil { - return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String()) - } - version := "4" - if reservedIP.IP.To4() == nil { - version = "6" - } - - return ¤t.IPConfig{ - Version: version, - Address: *reservedIP, - Gateway: gw, - }, nil -} - -// Release clears all IPs allocated for the container with given ID -func (a *IPAllocator) Release(id string, ifname string) error { - a.store.Lock() - defer a.store.Unlock() - - return a.store.ReleaseByID(id, ifname) -} - -type RangeIter struct { - rangeset *RangeSet - - // The current range id - rangeIdx int - - // Our current position - cur net.IP - - // The IP and range index where we started iterating; if we hit this again, we're done. - startIP net.IP - startRange int -} - -// GetIter encapsulates the strategy for this allocator. -// We use a round-robin strategy, attempting to evenly use the whole set. -// More specifically, a crash-looping container will not see the same IP until -// the entire range has been run through. -// We may wish to consider avoiding recently-released IPs in the future. -func (a *IPAllocator) GetIter() (*RangeIter, error) { - iter := RangeIter{ - rangeset: a.rangeset, - } - - // Round-robin by trying to allocate from the last reserved IP + 1 - startFromLastReservedIP := false - - // We might get a last reserved IP that is wrong if the range indexes changed. - // This is not critical, we just lose round-robin this one time. - lastReservedIP, err := a.store.LastReservedIP(a.rangeID) - if err != nil && !os.IsNotExist(err) { - log.Printf("Error retrieving last reserved ip: %v", err) - } else if lastReservedIP != nil { - startFromLastReservedIP = a.rangeset.Contains(lastReservedIP) - } - - // Find the range in the set with this IP - if startFromLastReservedIP { - for i, r := range *a.rangeset { - if r.Contains(lastReservedIP) { - iter.rangeIdx = i - iter.startRange = i - - // We advance the cursor on every Next(), so the first call - // to next() will return lastReservedIP + 1 - iter.cur = lastReservedIP - break - } - } - } else { - iter.rangeIdx = 0 - iter.startRange = 0 - iter.startIP = (*a.rangeset)[0].RangeStart - } - return &iter, nil -} - -// Next returns the next IP, its mask, and its gateway. Returns nil -// if the iterator has been exhausted -func (i *RangeIter) Next() (*net.IPNet, net.IP) { - r := (*i.rangeset)[i.rangeIdx] - - // If this is the first time iterating and we're not starting in the middle - // of the range, then start at rangeStart, which is inclusive - if i.cur == nil { - i.cur = r.RangeStart - i.startIP = i.cur - if i.cur.Equal(r.Gateway) { - return i.Next() - } - return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway - } - - // If we've reached the end of this range, we need to advance the range - // RangeEnd is inclusive as well - if i.cur.Equal(r.RangeEnd) { - i.rangeIdx += 1 - i.rangeIdx %= len(*i.rangeset) - r = (*i.rangeset)[i.rangeIdx] - - i.cur = r.RangeStart - } else { - i.cur = ip.NextIP(i.cur) - } - - if i.startIP == nil { - i.startIP = i.cur - } else if i.rangeIdx == i.startRange && i.cur.Equal(i.startIP) { - // IF we've looped back to where we started, give up - return nil, nil - } - - if i.cur.Equal(r.Gateway) { - return i.Next() - } - - return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway -} diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go deleted file mode 100644 index c8cb2a746..000000000 --- a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package allocator - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" -) - -// The top-level network config - IPAM plugins are passed the full configuration -// of the calling plugin, not just the IPAM section. -type Net struct { - Name string `json:"name"` - CNIVersion string `json:"cniVersion"` - IPAM *IPAMConfig `json:"ipam"` - RuntimeConfig struct { // The capability arg - IPRanges []RangeSet `json:"ipRanges,omitempty"` - } `json:"runtimeConfig,omitempty"` - Args *struct { - A *IPAMArgs `json:"cni"` - } `json:"args"` -} - -// IPAMConfig represents the IP related network configuration. -// This nests Range because we initially only supported a single -// range directly, and wish to preserve backwards compatability -type IPAMConfig struct { - *Range - Name string - Type string `json:"type"` - Routes []*types.Route `json:"routes"` - DataDir string `json:"dataDir"` - ResolvConf string `json:"resolvConf"` - Ranges []RangeSet `json:"ranges"` - IPArgs []net.IP `json:"-"` // Requested IPs from CNI_ARGS and args -} - -type IPAMEnvArgs struct { - types.CommonArgs - IP net.IP `json:"ip,omitempty"` -} - -type IPAMArgs struct { - IPs []net.IP `json:"ips"` -} - -type RangeSet []Range - -type Range struct { - RangeStart net.IP `json:"rangeStart,omitempty"` // The first ip, inclusive - RangeEnd net.IP `json:"rangeEnd,omitempty"` // The last ip, inclusive - Subnet types.IPNet `json:"subnet"` - Gateway net.IP `json:"gateway,omitempty"` -} - -// NewIPAMConfig creates a NetworkConfig from the given network name. -func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { - n := Net{} - if err := json.Unmarshal(bytes, &n); err != nil { - return nil, "", err - } - - if n.IPAM == nil { - return nil, "", fmt.Errorf("IPAM config missing 'ipam' key") - } - - // Parse custom IP from both env args *and* the top-level args config - if envArgs != "" { - e := IPAMEnvArgs{} - err := types.LoadArgs(envArgs, &e) - if err != nil { - return nil, "", err - } - - if e.IP != nil { - n.IPAM.IPArgs = []net.IP{e.IP} - } - } - - if n.Args != nil && n.Args.A != nil && len(n.Args.A.IPs) != 0 { - n.IPAM.IPArgs = append(n.IPAM.IPArgs, n.Args.A.IPs...) - } - - for idx := range n.IPAM.IPArgs { - if err := canonicalizeIP(&n.IPAM.IPArgs[idx]); err != nil { - return nil, "", fmt.Errorf("cannot understand ip: %v", err) - } - } - - // If a single range (old-style config) is specified, prepend it to - // the Ranges array - if n.IPAM.Range != nil && n.IPAM.Range.Subnet.IP != nil { - n.IPAM.Ranges = append([]RangeSet{{*n.IPAM.Range}}, n.IPAM.Ranges...) - } - n.IPAM.Range = nil - - // If a range is supplied as a runtime config, prepend it to the Ranges - if len(n.RuntimeConfig.IPRanges) > 0 { - n.IPAM.Ranges = append(n.RuntimeConfig.IPRanges, n.IPAM.Ranges...) - } - - if len(n.IPAM.Ranges) == 0 { - return nil, "", fmt.Errorf("no IP ranges specified") - } - - // Validate all ranges - numV4 := 0 - numV6 := 0 - for i := range n.IPAM.Ranges { - if err := n.IPAM.Ranges[i].Canonicalize(); err != nil { - return nil, "", fmt.Errorf("invalid range set %d: %s", i, err) - } - - if n.IPAM.Ranges[i][0].RangeStart.To4() != nil { - numV4++ - } else { - numV6++ - } - } - - // CNI spec 0.2.0 and below supported only one v4 and v6 address - if numV4 > 1 || numV6 > 1 { - for _, v := range types020.SupportedVersions { - if n.CNIVersion == v { - return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) - } - } - } - - // Check for overlaps - l := len(n.IPAM.Ranges) - for i, p1 := range n.IPAM.Ranges[:l-1] { - for j, p2 := range n.IPAM.Ranges[i+1:] { - if p1.Overlaps(&p2) { - return nil, "", fmt.Errorf("range set %d overlaps with %d", i, (i + j + 1)) - } - } - } - - // Copy net name into IPAM so not to drag Net struct around - n.IPAM.Name = n.Name - - return n.IPAM, n.CNIVersion, nil -} diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go deleted file mode 100644 index 9bf389e80..000000000 --- a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2017 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package allocator - -import ( - "fmt" - "net" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/plugins/pkg/ip" -) - -// Canonicalize takes a given range and ensures that all information is consistent, -// filling out Start, End, and Gateway with sane values if missing -func (r *Range) Canonicalize() error { - if err := canonicalizeIP(&r.Subnet.IP); err != nil { - return err - } - - // Can't create an allocator for a network with no addresses, eg - // a /32 or /31 - ones, masklen := r.Subnet.Mask.Size() - if ones > masklen-2 { - return fmt.Errorf("Network %s too small to allocate from", (*net.IPNet)(&r.Subnet).String()) - } - - if len(r.Subnet.IP) != len(r.Subnet.Mask) { - return fmt.Errorf("IPNet IP and Mask version mismatch") - } - - // Ensure Subnet IP is the network address, not some other address - networkIP := r.Subnet.IP.Mask(r.Subnet.Mask) - if !r.Subnet.IP.Equal(networkIP) { - return fmt.Errorf("Network has host bits set. For a subnet mask of length %d the network address is %s", ones, networkIP.String()) - } - - // If the gateway is nil, claim .1 - if r.Gateway == nil { - r.Gateway = ip.NextIP(r.Subnet.IP) - } else { - if err := canonicalizeIP(&r.Gateway); err != nil { - return err - } - } - - // RangeStart: If specified, make sure it's sane (inside the subnet), - // otherwise use the first free IP (i.e. .1) - this will conflict with the - // gateway but we skip it in the iterator - if r.RangeStart != nil { - if err := canonicalizeIP(&r.RangeStart); err != nil { - return err - } - - if !r.Contains(r.RangeStart) { - return fmt.Errorf("RangeStart %s not in network %s", r.RangeStart.String(), (*net.IPNet)(&r.Subnet).String()) - } - } else { - r.RangeStart = ip.NextIP(r.Subnet.IP) - } - - // RangeEnd: If specified, verify sanity. Otherwise, add a sensible default - // (e.g. for a /24: .254 if IPv4, ::255 if IPv6) - if r.RangeEnd != nil { - if err := canonicalizeIP(&r.RangeEnd); err != nil { - return err - } - - if !r.Contains(r.RangeEnd) { - return fmt.Errorf("RangeEnd %s not in network %s", r.RangeEnd.String(), (*net.IPNet)(&r.Subnet).String()) - } - } else { - r.RangeEnd = lastIP(r.Subnet) - } - - return nil -} - -// IsValidIP checks if a given ip is a valid, allocatable address in a given Range -func (r *Range) Contains(addr net.IP) bool { - if err := canonicalizeIP(&addr); err != nil { - return false - } - - subnet := (net.IPNet)(r.Subnet) - - // Not the same address family - if len(addr) != len(r.Subnet.IP) { - return false - } - - // Not in network - if !subnet.Contains(addr) { - return false - } - - // We ignore nils here so we can use this function as we initialize the range. - if r.RangeStart != nil { - // Before the range start - if ip.Cmp(addr, r.RangeStart) < 0 { - return false - } - } - - if r.RangeEnd != nil { - if ip.Cmp(addr, r.RangeEnd) > 0 { - // After the range end - return false - } - } - - return true -} - -// Overlaps returns true if there is any overlap between ranges -func (r *Range) Overlaps(r1 *Range) bool { - // different familes - if len(r.RangeStart) != len(r1.RangeStart) { - return false - } - - return r.Contains(r1.RangeStart) || - r.Contains(r1.RangeEnd) || - r1.Contains(r.RangeStart) || - r1.Contains(r.RangeEnd) -} - -func (r *Range) String() string { - return fmt.Sprintf("%s-%s", r.RangeStart.String(), r.RangeEnd.String()) -} - -// canonicalizeIP makes sure a provided ip is in standard form -func canonicalizeIP(ip *net.IP) error { - if ip.To4() != nil { - *ip = ip.To4() - return nil - } else if ip.To16() != nil { - *ip = ip.To16() - return nil - } - return fmt.Errorf("IP %s not v4 nor v6", *ip) -} - -// Determine the last IP of a subnet, excluding the broadcast if IPv4 -func lastIP(subnet types.IPNet) net.IP { - var end net.IP - for i := 0; i < len(subnet.IP); i++ { - end = append(end, subnet.IP[i]|^subnet.Mask[i]) - } - if subnet.IP.To4() != nil { - end[3]-- - } - - return end -} diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go deleted file mode 100644 index da957f535..000000000 --- a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package allocator - -import ( - "fmt" - "net" - "strings" -) - -// Contains returns true if any range in this set contains an IP -func (s *RangeSet) Contains(addr net.IP) bool { - r, _ := s.RangeFor(addr) - return r != nil -} - -// RangeFor finds the range that contains an IP, or nil if not found -func (s *RangeSet) RangeFor(addr net.IP) (*Range, error) { - if err := canonicalizeIP(&addr); err != nil { - return nil, err - } - - for _, r := range *s { - if r.Contains(addr) { - return &r, nil - } - } - - return nil, fmt.Errorf("%s not in range set %s", addr.String(), s.String()) -} - -// Overlaps returns true if any ranges in any set overlap with this one -func (s *RangeSet) Overlaps(p1 *RangeSet) bool { - for _, r := range *s { - for _, r1 := range *p1 { - if r.Overlaps(&r1) { - return true - } - } - } - return false -} - -// Canonicalize ensures the RangeSet is in a standard form, and detects any -// invalid input. Call Range.Canonicalize() on every Range in the set -func (s *RangeSet) Canonicalize() error { - if len(*s) == 0 { - return fmt.Errorf("empty range set") - } - - fam := 0 - for i := range *s { - if err := (*s)[i].Canonicalize(); err != nil { - return err - } - if i == 0 { - fam = len((*s)[i].RangeStart) - } else { - if fam != len((*s)[i].RangeStart) { - return fmt.Errorf("mixed address families") - } - } - } - - // Make sure none of the ranges in the set overlap - l := len(*s) - for i, r1 := range (*s)[:l-1] { - for _, r2 := range (*s)[i+1:] { - if r1.Overlaps(&r2) { - return fmt.Errorf("subnets %s and %s overlap", r1.String(), r2.String()) - } - } - } - - return nil -} - -func (s *RangeSet) String() string { - out := []string{} - for _, r := range *s { - out = append(out, r.String()) - } - - return strings.Join(out, ",") -} diff --git a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go b/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go deleted file mode 100644 index 7211ddf6a..000000000 --- a/vendor/github.com/containernetworking/plugins/plugins/ipam/host-local/backend/store.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 CNI authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package backend - -import "net" - -type Store interface { - Lock() error - Unlock() error - Close() error - Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) - LastReservedIP(rangeID string) (net.IP, error) - Release(ip net.IP) error - ReleaseByID(id string, ifname string) error - GetByID(id string, ifname string) []net.IP -} diff --git a/vendor/github.com/coreos/go-iptables/LICENSE b/vendor/github.com/coreos/go-iptables/LICENSE deleted file mode 100644 index 37ec93a14..000000000 --- a/vendor/github.com/coreos/go-iptables/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/coreos/go-iptables/NOTICE b/vendor/github.com/coreos/go-iptables/NOTICE deleted file mode 100644 index 23a0ada2f..000000000 --- a/vendor/github.com/coreos/go-iptables/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2018 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go deleted file mode 100644 index 8d6f68906..000000000 --- a/vendor/github.com/coreos/go-iptables/iptables/iptables.go +++ /dev/null @@ -1,659 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "bytes" - "fmt" - "io" - "net" - "os/exec" - "regexp" - "strconv" - "strings" - "syscall" -) - -// Adds the output of stderr to exec.ExitError -type Error struct { - exec.ExitError - cmd exec.Cmd - msg string - exitStatus *int //for overriding -} - -func (e *Error) ExitStatus() int { - if e.exitStatus != nil { - return *e.exitStatus - } - return e.Sys().(syscall.WaitStatus).ExitStatus() -} - -func (e *Error) Error() string { - return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg) -} - -// IsNotExist returns true if the error is due to the chain or rule not existing -func (e *Error) IsNotExist() bool { - if e.ExitStatus() != 1 { - return false - } - msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n" - msgNoChainExist := "No chain/target/match by that name.\n" - return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) -} - -// Protocol to differentiate between IPv4 and IPv6 -type Protocol byte - -const ( - ProtocolIPv4 Protocol = iota - ProtocolIPv6 -) - -type IPTables struct { - path string - proto Protocol - hasCheck bool - hasWait bool - hasRandomFully bool - v1 int - v2 int - v3 int - mode string // the underlying iptables operating mode, e.g. nf_tables - timeout int // time to wait for the iptables lock, default waits forever -} - -// Stat represents a structured statistic entry. -type Stat struct { - Packets uint64 `json:"pkts"` - Bytes uint64 `json:"bytes"` - Target string `json:"target"` - Protocol string `json:"prot"` - Opt string `json:"opt"` - Input string `json:"in"` - Output string `json:"out"` - Source *net.IPNet `json:"source"` - Destination *net.IPNet `json:"destination"` - Options string `json:"options"` -} - -type option func(*IPTables) - -func IPFamily(proto Protocol) option { - return func(ipt *IPTables) { - ipt.proto = proto - } -} - -func Timeout(timeout int) option { - return func(ipt *IPTables) { - ipt.timeout = timeout - } -} - -// New creates a new IPTables configured with the options passed as parameter. -// For backwards compatibility, by default always uses IPv4 and timeout 0. -// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing -// the IPFamily and Timeout options as follow: -// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5)) -func New(opts ...option) (*IPTables, error) { - - ipt := &IPTables{ - proto: ProtocolIPv4, - timeout: 0, - } - - for _, opt := range opts { - opt(ipt) - } - - path, err := exec.LookPath(getIptablesCommand(ipt.proto)) - if err != nil { - return nil, err - } - ipt.path = path - - vstring, err := getIptablesVersionString(path) - if err != nil { - return nil, fmt.Errorf("could not get iptables version: %v", err) - } - v1, v2, v3, mode, err := extractIptablesVersion(vstring) - if err != nil { - return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) - } - ipt.v1 = v1 - ipt.v2 = v2 - ipt.v3 = v3 - ipt.mode = mode - - checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) - ipt.hasCheck = checkPresent - ipt.hasWait = waitPresent - ipt.hasRandomFully = randomFullyPresent - - return ipt, nil -} - -// New creates a new IPTables for the given proto. -// The proto will determine which command is used, either "iptables" or "ip6tables". -func NewWithProtocol(proto Protocol) (*IPTables, error) { - return New(IPFamily(proto), Timeout(0)) -} - -// Proto returns the protocol used by this IPTables. -func (ipt *IPTables) Proto() Protocol { - return ipt.proto -} - -// Exists checks if given rulespec in specified table/chain exists -func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) { - if !ipt.hasCheck { - return ipt.existsForOldIptables(table, chain, rulespec) - - } - cmd := append([]string{"-t", table, "-C", chain}, rulespec...) - err := ipt.run(cmd...) - eerr, eok := err.(*Error) - switch { - case err == nil: - return true, nil - case eok && eerr.ExitStatus() == 1: - return false, nil - default: - return false, err - } -} - -// Insert inserts rulespec to specified table/chain (in specified pos) -func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error { - cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...) - return ipt.run(cmd...) -} - -// Append appends rulespec to specified table/chain -func (ipt *IPTables) Append(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-A", chain}, rulespec...) - return ipt.run(cmd...) -} - -// AppendUnique acts like Append except that it won't add a duplicate -func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error { - exists, err := ipt.Exists(table, chain, rulespec...) - if err != nil { - return err - } - - if !exists { - return ipt.Append(table, chain, rulespec...) - } - - return nil -} - -// Delete removes rulespec in specified table/chain -func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-D", chain}, rulespec...) - return ipt.run(cmd...) -} - -func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error { - exists, err := ipt.Exists(table, chain, rulespec...) - if err == nil && exists { - err = ipt.Delete(table, chain, rulespec...) - } - return err -} - -// List rules in specified table/chain -func (ipt *IPTables) List(table, chain string) ([]string, error) { - args := []string{"-t", table, "-S", chain} - return ipt.executeList(args) -} - -// List rules (with counters) in specified table/chain -func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) { - args := []string{"-t", table, "-v", "-S", chain} - return ipt.executeList(args) -} - -// ListChains returns a slice containing the name of each chain in the specified table. -func (ipt *IPTables) ListChains(table string) ([]string, error) { - args := []string{"-t", table, "-S"} - - result, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - // Iterate over rules to find all default (-P) and user-specified (-N) chains. - // Chains definition always come before rules. - // Format is the following: - // -P OUTPUT ACCEPT - // -N Custom - var chains []string - for _, val := range result { - if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") { - chains = append(chains, strings.Fields(val)[1]) - } else { - break - } - } - return chains, nil -} - -// '-S' is fine with non existing rule index as long as the chain exists -// therefore pass index 1 to reduce overhead for large chains -func (ipt *IPTables) ChainExists(table, chain string) (bool, error) { - err := ipt.run("-t", table, "-S", chain, "1") - eerr, eok := err.(*Error) - switch { - case err == nil: - return true, nil - case eok && eerr.ExitStatus() == 1: - return false, nil - default: - return false, err - } -} - -// Stats lists rules including the byte and packet counts -func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { - args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} - lines, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - appendSubnet := func(addr string) string { - if strings.IndexByte(addr, byte('/')) < 0 { - if strings.IndexByte(addr, '.') < 0 { - return addr + "/128" - } - return addr + "/32" - } - return addr - } - - ipv6 := ipt.proto == ProtocolIPv6 - - rows := [][]string{} - for i, line := range lines { - // Skip over chain name and field header - if i < 2 { - continue - } - - // Fields: - // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options - line = strings.TrimSpace(line) - fields := strings.Fields(line) - - // The ip6tables verbose output cannot be naively split due to the default "opt" - // field containing 2 single spaces. - if ipv6 { - // Check if field 6 is "opt" or "source" address - dest := fields[6] - ip, _, _ := net.ParseCIDR(dest) - if ip == nil { - ip = net.ParseIP(dest) - } - - // If we detected a CIDR or IP, the "opt" field is empty.. insert it. - if ip != nil { - f := []string{} - f = append(f, fields[:4]...) - f = append(f, " ") // Empty "opt" field for ip6tables - f = append(f, fields[4:]...) - fields = f - } - } - - // Adjust "source" and "destination" to include netmask, to match regular - // List output - fields[7] = appendSubnet(fields[7]) - fields[8] = appendSubnet(fields[8]) - - // Combine "options" fields 9... into a single space-delimited field. - options := fields[9:] - fields = fields[:9] - fields = append(fields, strings.Join(options, " ")) - rows = append(rows, fields) - } - return rows, nil -} - -// ParseStat parses a single statistic row into a Stat struct. The input should -// be a string slice that is returned from calling the Stat method. -func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) { - // For forward-compatibility, expect at least 10 fields in the stat - if len(stat) < 10 { - return parsed, fmt.Errorf("stat contained fewer fields than expected") - } - - // Convert the fields that are not plain strings - parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse packets") - } - parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse bytes") - } - _, parsed.Source, err = net.ParseCIDR(stat[7]) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse source") - } - _, parsed.Destination, err = net.ParseCIDR(stat[8]) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse destination") - } - - // Put the fields that are strings - parsed.Target = stat[2] - parsed.Protocol = stat[3] - parsed.Opt = stat[4] - parsed.Input = stat[5] - parsed.Output = stat[6] - parsed.Options = stat[9] - - return parsed, nil -} - -// StructuredStats returns statistics as structured data which may be further -// parsed and marshaled. -func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) { - rawStats, err := ipt.Stats(table, chain) - if err != nil { - return nil, err - } - - structStats := []Stat{} - for _, rawStat := range rawStats { - stat, err := ipt.ParseStat(rawStat) - if err != nil { - return nil, err - } - structStats = append(structStats, stat) - } - - return structStats, nil -} - -func (ipt *IPTables) executeList(args []string) ([]string, error) { - var stdout bytes.Buffer - if err := ipt.runWithOutput(args, &stdout); err != nil { - return nil, err - } - - rules := strings.Split(stdout.String(), "\n") - - // strip trailing newline - if len(rules) > 0 && rules[len(rules)-1] == "" { - rules = rules[:len(rules)-1] - } - - for i, rule := range rules { - rules[i] = filterRuleOutput(rule) - } - - return rules, nil -} - -// NewChain creates a new chain in the specified table. -// If the chain already exists, it will result in an error. -func (ipt *IPTables) NewChain(table, chain string) error { - return ipt.run("-t", table, "-N", chain) -} - -const existsErr = 1 - -// ClearChain flushed (deletes all rules) in the specified table/chain. -// If the chain does not exist, a new one will be created -func (ipt *IPTables) ClearChain(table, chain string) error { - err := ipt.NewChain(table, chain) - - eerr, eok := err.(*Error) - switch { - case err == nil: - return nil - case eok && eerr.ExitStatus() == existsErr: - // chain already exists. Flush (clear) it. - return ipt.run("-t", table, "-F", chain) - default: - return err - } -} - -// RenameChain renames the old chain to the new one. -func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error { - return ipt.run("-t", table, "-E", oldChain, newChain) -} - -// DeleteChain deletes the chain in the specified table. -// The chain must be empty -func (ipt *IPTables) DeleteChain(table, chain string) error { - return ipt.run("-t", table, "-X", chain) -} - -func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error { - exists, err := ipt.ChainExists(table, chain) - if err != nil || !exists { - return err - } - err = ipt.run("-t", table, "-F", chain) - if err == nil { - err = ipt.run("-t", table, "-X", chain) - } - return err -} - -// ChangePolicy changes policy on chain to target -func (ipt *IPTables) ChangePolicy(table, chain, target string) error { - return ipt.run("-t", table, "-P", chain, target) -} - -// Check if the underlying iptables command supports the --random-fully flag -func (ipt *IPTables) HasRandomFully() bool { - return ipt.hasRandomFully -} - -// Return version components of the underlying iptables command -func (ipt *IPTables) GetIptablesVersion() (int, int, int) { - return ipt.v1, ipt.v2, ipt.v3 -} - -// run runs an iptables command with the given arguments, ignoring -// any stdout output -func (ipt *IPTables) run(args ...string) error { - return ipt.runWithOutput(args, nil) -} - -// runWithOutput runs an iptables command with the given arguments, -// writing any stdout output to the given writer -func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { - args = append([]string{ipt.path}, args...) - if ipt.hasWait { - args = append(args, "--wait") - if ipt.timeout != 0 { - args = append(args, strconv.Itoa(ipt.timeout)) - } - } else { - fmu, err := newXtablesFileLock() - if err != nil { - return err - } - ul, err := fmu.tryLock() - if err != nil { - syscall.Close(fmu.fd) - return err - } - defer ul.Unlock() - } - - var stderr bytes.Buffer - cmd := exec.Cmd{ - Path: ipt.path, - Args: args, - Stdout: stdout, - Stderr: &stderr, - } - - if err := cmd.Run(); err != nil { - switch e := err.(type) { - case *exec.ExitError: - return &Error{*e, cmd, stderr.String(), nil} - default: - return err - } - } - - return nil -} - -// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables". -func getIptablesCommand(proto Protocol) string { - if proto == ProtocolIPv6 { - return "ip6tables" - } else { - return "iptables" - } -} - -// Checks if iptables has the "-C" and "--wait" flag -func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) { - return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) -} - -// getIptablesVersion returns the first three components of the iptables version -// and the operating mode (e.g. nf_tables or legacy) -// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) -func extractIptablesVersion(str string) (int, int, int, string, error) { - versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) - result := versionMatcher.FindStringSubmatch(str) - if result == nil { - return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) - } - - v1, err := strconv.Atoi(result[1]) - if err != nil { - return 0, 0, 0, "", err - } - - v2, err := strconv.Atoi(result[2]) - if err != nil { - return 0, 0, 0, "", err - } - - v3, err := strconv.Atoi(result[3]) - if err != nil { - return 0, 0, 0, "", err - } - - mode := "legacy" - if result[4] != "" { - mode = result[4] - } - return v1, v2, v3, mode, nil -} - -// Runs "iptables --version" to get the version string -func getIptablesVersionString(path string) (string, error) { - cmd := exec.Command(path, "--version") - var out bytes.Buffer - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return "", err - } - return out.String(), nil -} - -// Checks if an iptables version is after 1.4.11, when --check was added -func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 11 { - return true - } - return false -} - -// Checks if an iptables version is after 1.4.20, when --wait was added -func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 20 { - return true - } - return false -} - -// Checks if an iptables version is after 1.6.2, when --random-fully was added -func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 6 { - return true - } - if v1 == 1 && v2 == 6 && v3 >= 2 { - return true - } - return false -} - -// Checks if a rule specification exists for a table -func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { - rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") - args := []string{"-t", table, "-S"} - var stdout bytes.Buffer - err := ipt.runWithOutput(args, &stdout) - if err != nil { - return false, err - } - return strings.Contains(stdout.String(), rs), nil -} - -// counterRegex is the regex used to detect nftables counter format -var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) - -// filterRuleOutput works around some inconsistencies in output. -// For example, when iptables is in legacy vs. nftables mode, it produces -// different results. -func filterRuleOutput(rule string) string { - out := rule - - // work around an output difference in nftables mode where counters - // are output in iptables-save format, rather than iptables -S format - // The string begins with "[0:0]" - // - // Fixes #49 - if groups := counterRegex.FindStringSubmatch(out); groups != nil { - // drop the brackets - out = out[len(groups[0]):] - out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) - } - - return out -} diff --git a/vendor/github.com/coreos/go-iptables/iptables/lock.go b/vendor/github.com/coreos/go-iptables/iptables/lock.go deleted file mode 100644 index a88e92b4e..000000000 --- a/vendor/github.com/coreos/go-iptables/iptables/lock.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "os" - "sync" - "syscall" -) - -const ( - // In earlier versions of iptables, the xtables lock was implemented - // via a Unix socket, but now flock is used via this lockfile: - // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 - // Note the LSB-conforming "/run" directory does not exist on old - // distributions, so assume "/var" is symlinked - xtablesLockFilePath = "/var/run/xtables.lock" - - defaultFilePerm = 0600 -) - -type Unlocker interface { - Unlock() error -} - -type nopUnlocker struct{} - -func (_ nopUnlocker) Unlock() error { return nil } - -type fileLock struct { - // mu is used to protect against concurrent invocations from within this process - mu sync.Mutex - fd int -} - -// tryLock takes an exclusive lock on the xtables lock file without blocking. -// This is best-effort only: if the exclusive lock would block (i.e. because -// another process already holds it), no error is returned. Otherwise, any -// error encountered during the locking operation is returned. -// The returned Unlocker should be used to release the lock when the caller is -// done invoking iptables commands. -func (l *fileLock) tryLock() (Unlocker, error) { - l.mu.Lock() - err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) - switch err { - case syscall.EWOULDBLOCK: - l.mu.Unlock() - return nopUnlocker{}, nil - case nil: - return l, nil - default: - l.mu.Unlock() - return nil, err - } -} - -// Unlock closes the underlying file, which implicitly unlocks it as well. It -// also unlocks the associated mutex. -func (l *fileLock) Unlock() error { - defer l.mu.Unlock() - return syscall.Close(l.fd) -} - -// newXtablesFileLock opens a new lock on the xtables lockfile without -// acquiring the lock -func newXtablesFileLock() (*fileLock, error) { - fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) - if err != nil { - return nil, err - } - return &fileLock{fd: fd}, nil -} diff --git a/vendor/github.com/cri-o/ocicni/LICENSE b/vendor/github.com/cri-o/ocicni/LICENSE deleted file mode 100644 index 3fd703072..000000000 --- a/vendor/github.com/cri-o/ocicni/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 Red Hat, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go deleted file mode 100644 index 90d5b6c50..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go +++ /dev/null @@ -1,870 +0,0 @@ -package ocicni - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "os" - "path" - "path/filepath" - "sort" - "strings" - "sync" - - "github.com/containernetworking/cni/libcni" - cniinvoke "github.com/containernetworking/cni/pkg/invoke" - cnitypes "github.com/containernetworking/cni/pkg/types" - cnicurrent "github.com/containernetworking/cni/pkg/types/current" - cniversion "github.com/containernetworking/cni/pkg/version" - "github.com/fsnotify/fsnotify" - "github.com/sirupsen/logrus" -) - -type cniNetworkPlugin struct { - cniConfig *libcni.CNIConfig - - sync.RWMutex - defaultNetName netName - networks map[string]*cniNetwork - - nsManager *nsManager - confDir string - binDirs []string - - shutdownChan chan struct{} - watcher *fsnotify.Watcher - done *sync.WaitGroup - - // The pod map provides synchronization for a given pod's network - // operations. Each pod's setup/teardown/status operations - // are synchronized against each other, but network operations of other - // pods can proceed in parallel. - podsLock sync.Mutex - pods map[string]*podLock - - // For testcases - exec cniinvoke.Exec - cacheDir string -} - -type netName struct { - name string - changeable bool -} - -type cniNetwork struct { - name string - filePath string - config *libcni.NetworkConfigList -} - -var errMissingDefaultNetwork = "No CNI configuration file in %s. Has your network provider started?" - -type podLock struct { - // Count of in-flight operations for this pod; when this reaches zero - // the lock can be removed from the pod map - refcount uint - - // Lock to synchronize operations for this specific pod - mu sync.Mutex -} - -func buildFullPodName(podNetwork PodNetwork) string { - return podNetwork.Namespace + "_" + podNetwork.Name -} - -// Lock network operations for a specific pod. If that pod is not yet in -// the pod map, it will be added. The reference count for the pod will -// be increased. -func (plugin *cniNetworkPlugin) podLock(podNetwork PodNetwork) *sync.Mutex { - plugin.podsLock.Lock() - defer plugin.podsLock.Unlock() - - fullPodName := buildFullPodName(podNetwork) - lock, ok := plugin.pods[fullPodName] - if !ok { - lock = &podLock{} - plugin.pods[fullPodName] = lock - } - lock.refcount++ - return &lock.mu -} - -// Unlock network operations for a specific pod. The reference count for the -// pod will be decreased. If the reference count reaches zero, the pod will be -// removed from the pod map. -func (plugin *cniNetworkPlugin) podUnlock(podNetwork PodNetwork) { - plugin.podsLock.Lock() - defer plugin.podsLock.Unlock() - - fullPodName := buildFullPodName(podNetwork) - lock, ok := plugin.pods[fullPodName] - if !ok { - logrus.Errorf("Cannot find reference in refcount map for %s. Refcount cannot be determined.", fullPodName) - return - } else if lock.refcount == 0 { - // This should never ever happen, but handle it anyway - delete(plugin.pods, fullPodName) - logrus.Errorf("Pod lock for %s still in map with zero refcount", fullPodName) - return - } - lock.refcount-- - lock.mu.Unlock() - if lock.refcount == 0 { - delete(plugin.pods, fullPodName) - } -} - -func newWatcher(confDir string) (*fsnotify.Watcher, error) { - // Ensure plugin directory exists, because the following monitoring logic - // relies on that. - if err := os.MkdirAll(confDir, 0755); err != nil { - return nil, fmt.Errorf("failed to create directory %q: %v", confDir, err) - } - - watcher, err := fsnotify.NewWatcher() - if err != nil { - return nil, fmt.Errorf("failed to create new watcher %v", err) - } - defer func() { - // Close watcher on error - if err != nil { - watcher.Close() - } - }() - - if err = watcher.Add(confDir); err != nil { - return nil, fmt.Errorf("failed to add watch on %q: %v", confDir, err) - } - - return watcher, nil -} - -func (plugin *cniNetworkPlugin) monitorConfDir(start *sync.WaitGroup) { - start.Done() - plugin.done.Add(1) - defer plugin.done.Done() - for { - select { - case event := <-plugin.watcher.Events: - logrus.Infof("CNI monitoring event %v", event) - - var defaultDeleted bool - createWrite := (event.Op&fsnotify.Create == fsnotify.Create || - event.Op&fsnotify.Write == fsnotify.Write) - if event.Op&fsnotify.Remove == fsnotify.Remove { - // Care about the event if the default network - // was just deleted - defNet := plugin.getDefaultNetwork() - if defNet != nil && event.Name == defNet.filePath { - defaultDeleted = true - } - - } - if !createWrite && !defaultDeleted { - continue - } - - if err := plugin.syncNetworkConfig(); err != nil { - logrus.Errorf("CNI config loading failed, continue monitoring: %v", err) - continue - } - - case err := <-plugin.watcher.Errors: - if err == nil { - continue - } - logrus.Errorf("CNI monitoring error %v", err) - return - - case <-plugin.shutdownChan: - return - } - } -} - -// InitCNI takes a binary directory in which to search for CNI plugins, and -// a configuration directory in which to search for CNI JSON config files. -// If no valid CNI configs exist, network requests will fail until valid CNI -// config files are present in the config directory. -// If defaultNetName is not empty, a CNI config with that network name will -// be used as the default CNI network, and container network operations will -// fail until that network config is present and valid. -// If defaultNetName is empty, CNI config files should be reloaded real-time and -// defaultNetName should be changeable and determined by file sorting. -func InitCNI(defaultNetName string, confDir string, binDirs ...string) (CNIPlugin, error) { - return initCNI(nil, "", defaultNetName, confDir, true, binDirs...) -} - -// InitCNIWithCache works like InitCNI except that it takes the cni cache directory as third param. -func InitCNIWithCache(defaultNetName, confDir, cacheDir string, binDirs ...string) (CNIPlugin, error) { - return initCNI(nil, cacheDir, defaultNetName, confDir, true, binDirs...) -} - -// InitCNINoInotify works like InitCNI except that it does not use inotify to watch for changes in the CNI config dir. -func InitCNINoInotify(defaultNetName, confDir, cacheDir string, binDirs ...string) (CNIPlugin, error) { - return initCNI(nil, cacheDir, defaultNetName, confDir, false, binDirs...) -} - -// Internal function to allow faking out exec functions for testing -func initCNI(exec cniinvoke.Exec, cacheDir, defaultNetName string, confDir string, useInotify bool, binDirs ...string) (CNIPlugin, error) { - if confDir == "" { - confDir = DefaultConfDir - } - if len(binDirs) == 0 { - binDirs = []string{DefaultBinDir} - } - - plugin := &cniNetworkPlugin{ - cniConfig: libcni.NewCNIConfigWithCacheDir(binDirs, cacheDir, exec), - defaultNetName: netName{ - name: defaultNetName, - // If defaultNetName is not assigned in initialization, - // it should be changeable - changeable: defaultNetName == "", - }, - networks: make(map[string]*cniNetwork), - confDir: confDir, - binDirs: binDirs, - shutdownChan: make(chan struct{}), - done: &sync.WaitGroup{}, - pods: make(map[string]*podLock), - exec: exec, - cacheDir: cacheDir, - } - - if exec == nil { - exec = &cniinvoke.DefaultExec{ - RawExec: &cniinvoke.RawExec{Stderr: os.Stderr}, - PluginDecoder: cniversion.PluginDecoder{}, - } - } - - nsm, err := newNSManager() - if err != nil { - return nil, err - } - plugin.nsManager = nsm - - plugin.syncNetworkConfig() - - if useInotify { - plugin.watcher, err = newWatcher(plugin.confDir) - if err != nil { - return nil, err - } - - startWg := sync.WaitGroup{} - startWg.Add(1) - go plugin.monitorConfDir(&startWg) - startWg.Wait() - } - - return plugin, nil -} - -func (plugin *cniNetworkPlugin) Shutdown() error { - close(plugin.shutdownChan) - if plugin.watcher != nil { - plugin.watcher.Close() - } - plugin.done.Wait() - return nil -} - -func loadNetworks(confDir string, cni *libcni.CNIConfig) (map[string]*cniNetwork, string, error) { - files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"}) - if err != nil { - return nil, "", err - } - - networks := make(map[string]*cniNetwork) - defaultNetName := "" - - sort.Strings(files) - for _, confFile := range files { - var confList *libcni.NetworkConfigList - if strings.HasSuffix(confFile, ".conflist") { - confList, err = libcni.ConfListFromFile(confFile) - if err != nil { - // do not log ENOENT errors - if !os.IsNotExist(err) { - logrus.Errorf("Error loading CNI config list file %s: %v", confFile, err) - } - continue - } - } else { - conf, err := libcni.ConfFromFile(confFile) - if err != nil { - // do not log ENOENT errors - if !os.IsNotExist(err) { - logrus.Errorf("Error loading CNI config file %s: %v", confFile, err) - } - continue - } - if conf.Network.Type == "" { - logrus.Warningf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile) - continue - } - confList, err = libcni.ConfListFromConf(conf) - if err != nil { - logrus.Errorf("Error converting CNI config file %s to list: %v", confFile, err) - continue - } - } - if len(confList.Plugins) == 0 { - logrus.Infof("CNI config list %s has no networks, skipping", confFile) - continue - } - - // Validation on CNI config should be done to pre-check presence - // of plugins which are necessary. - if _, err := cni.ValidateNetworkList(context.TODO(), confList); err != nil { - logrus.Warningf("Error validating CNI config file %s: %v", confFile, err) - continue - } - - if confList.Name == "" { - confList.Name = path.Base(confFile) - } - - cniNet := &cniNetwork{ - name: confList.Name, - filePath: confFile, - config: confList, - } - - logrus.Infof("Found CNI network %s (type=%v) at %s", confList.Name, confList.Plugins[0].Network.Type, confFile) - - if _, ok := networks[confList.Name]; !ok { - networks[confList.Name] = cniNet - } else { - logrus.Infof("Ignored CNI network %s (type=%v) at %s because already exists", confList.Name, confList.Plugins[0].Network.Type, confFile) - } - - if defaultNetName == "" { - defaultNetName = confList.Name - } - } - - return networks, defaultNetName, nil -} - -const ( - loIfname string = "lo" -) - -func (plugin *cniNetworkPlugin) syncNetworkConfig() error { - networks, defaultNetName, err := loadNetworks(plugin.confDir, plugin.cniConfig) - if err != nil { - return err - } - - plugin.Lock() - defer plugin.Unlock() - - // Update defaultNetName if it is changeable - if plugin.defaultNetName.changeable { - plugin.defaultNetName.name = defaultNetName - logrus.Infof("Updated default CNI network name to %s", defaultNetName) - } else { - logrus.Debugf("Default CNI network name %s is unchangeable", plugin.defaultNetName.name) - } - - plugin.networks = networks - - return nil -} - -func (plugin *cniNetworkPlugin) getNetwork(name string) (*cniNetwork, error) { - plugin.RLock() - defer plugin.RUnlock() - net, ok := plugin.networks[name] - if !ok { - return nil, fmt.Errorf("CNI network %q not found", name) - } - return net, nil -} - -func (plugin *cniNetworkPlugin) GetDefaultNetworkName() string { - plugin.RLock() - defer plugin.RUnlock() - return plugin.defaultNetName.name -} - -func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork { - defaultNetName := plugin.GetDefaultNetworkName() - if defaultNetName == "" { - return nil - } - network, _ := plugin.getNetwork(defaultNetName) - return network -} - -// networksAvailable returns an error if the pod requests no networks and the -// plugin has no default network, and thus the plugin has no idea what network -// to attach the pod to. -func (plugin *cniNetworkPlugin) networksAvailable(podNetwork *PodNetwork) error { - if len(podNetwork.Networks) == 0 && plugin.getDefaultNetwork() == nil { - return fmt.Errorf(errMissingDefaultNetwork, plugin.confDir) - } - return nil -} - -func (plugin *cniNetworkPlugin) Name() string { - return CNIPluginName -} - -func (plugin *cniNetworkPlugin) loadNetworkFromCache(name string, rt *libcni.RuntimeConf) (*cniNetwork, *libcni.RuntimeConf, error) { - cniNet := &cniNetwork{ - name: name, - config: &libcni.NetworkConfigList{ - Name: name, - }, - } - - var confBytes []byte - var err error - confBytes, rt, err = plugin.cniConfig.GetNetworkListCachedConfig(cniNet.config, rt) - if err != nil { - return nil, nil, err - } else if confBytes == nil { - return nil, nil, fmt.Errorf("network %q not found in CNI cache", name) - } - - cniNet.config, err = libcni.ConfListFromBytes(confBytes) - if err != nil { - // Might be a plain NetworkConfig - netConf, err := libcni.ConfFromBytes(confBytes) - if err != nil { - return nil, nil, err - } - // Up-convert to a NetworkConfigList - cniNet.config, err = libcni.ConfListFromConf(netConf) - if err != nil { - return nil, nil, err - } - } - - return cniNet, rt, nil -} - -type forEachNetworkFn func(*cniNetwork, *PodNetwork, *libcni.RuntimeConf) error - -func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, fromCache bool, actionFn forEachNetworkFn) error { - networks := podNetwork.Networks - if len(networks) == 0 { - networks = append(networks, NetAttachment{ - Name: plugin.GetDefaultNetworkName(), - }) - } - - allIfNames := make(map[string]bool) - for _, req := range networks { - if req.Ifname != "" { - // Make sure the requested name isn't already assigned - if allIfNames[req.Ifname] { - return fmt.Errorf("network %q requested interface name %q already assigned", req.Name, req.Ifname) - } - allIfNames[req.Ifname] = true - } - } - - for _, network := range networks { - ifName := network.Ifname - if ifName == "" { - for i := 0; i < 10000; i++ { - candidate := fmt.Sprintf("eth%d", i) - if !allIfNames[candidate] { - allIfNames[candidate] = true - ifName = candidate - break - } - } - if ifName == "" { - return fmt.Errorf("failed to find free interface name for network %q", network.Name) - } - } - - rt, err := buildCNIRuntimeConf(podNetwork, ifName, podNetwork.RuntimeConfig[network.Name]) - if err != nil { - logrus.Errorf("error building CNI runtime config: %v", err) - return err - } - - var cniNet *cniNetwork - if fromCache { - var newRt *libcni.RuntimeConf - cniNet, newRt, err = plugin.loadNetworkFromCache(network.Name, rt) - if err != nil { - logrus.Errorf("error loading cached network config: %v", err) - logrus.Warningf("falling back to loading from existing plugins on disk") - } else { - // Use the updated RuntimeConf - rt = newRt - } - } - if cniNet == nil { - cniNet, err = plugin.getNetwork(network.Name) - if err != nil { - // try to load the networks again - if err2 := plugin.syncNetworkConfig(); err2 != nil { - logrus.Error(err2) - return err - } - cniNet, err = plugin.getNetwork(network.Name) - if err != nil { - return err - } - } - } - - if err := actionFn(cniNet, podNetwork, rt); err != nil { - return err - } - } - return nil -} - -func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]NetResult, error) { - return plugin.SetUpPodWithContext(context.Background(), podNetwork) -} - -func (plugin *cniNetworkPlugin) SetUpPodWithContext(ctx context.Context, podNetwork PodNetwork) ([]NetResult, error) { - if err := plugin.networksAvailable(&podNetwork); err != nil { - return nil, err - } - - plugin.podLock(podNetwork).Lock() - defer plugin.podUnlock(podNetwork) - - // Set up loopback interface - if err := bringUpLoopback(podNetwork.NetNS); err != nil { - logrus.Errorf(err.Error()) - return nil, err - } - - results := make([]NetResult, 0) - if err := plugin.forEachNetwork(&podNetwork, false, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { - fullPodName := buildFullPodName(*podNetwork) - logrus.Infof("Adding pod %s to CNI network %q (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) - result, err := network.addToNetwork(ctx, rt, plugin.cniConfig) - if err != nil { - return fmt.Errorf("error adding pod %s to CNI network %q: %v", fullPodName, network.name, err) - } - results = append(results, NetResult{ - Result: result, - NetAttachment: NetAttachment{ - Name: network.name, - Ifname: rt.IfName, - }, - }) - return nil - }); err != nil { - return nil, err - } - - return results, nil -} - -func (plugin *cniNetworkPlugin) getCachedNetworkInfo(containerID string) ([]NetAttachment, error) { - cacheDir := libcni.CacheDir - if plugin.cacheDir != "" { - cacheDir = plugin.cacheDir - } - - dirPath := filepath.Join(cacheDir, "results") - entries, err := ioutil.ReadDir(dirPath) - if err != nil { - return nil, err - } - - fileNames := make([]string, 0, len(entries)) - for _, e := range entries { - fileNames = append(fileNames, e.Name()) - } - sort.Strings(fileNames) - - attachments := []NetAttachment{} - for _, fname := range fileNames { - part := fmt.Sprintf("-%s-", containerID) - pos := strings.Index(fname, part) - if pos <= 0 || pos+len(part) >= len(fname) { - continue - } - - cacheFile := filepath.Join(dirPath, fname) - bytes, err := ioutil.ReadFile(cacheFile) - if err != nil { - logrus.Errorf("failed to read CNI cache file %s: %v", cacheFile, err) - continue - } - - cachedInfo := struct { - Kind string `json:"kind"` - IfName string `json:"ifName"` - ContainerID string `json:"containerID"` - NetName string `json:"networkName"` - }{} - - if err := json.Unmarshal(bytes, &cachedInfo); err != nil { - logrus.Errorf("failed to unmarshal CNI cache file %s: %v", cacheFile, err) - continue - } - if cachedInfo.Kind != libcni.CNICacheV1 { - logrus.Warningf("unknown CNI cache file %s kind %q", cacheFile, cachedInfo.Kind) - continue - } - if cachedInfo.ContainerID != containerID { - continue - } - // Ignore the loopback interface; it's handled separately - if cachedInfo.IfName == loIfname && cachedInfo.NetName == "cni-loopback" { - continue - } - if cachedInfo.IfName == "" || cachedInfo.NetName == "" { - logrus.Warningf("missing CNI cache file %s ifname %q or netname %q", cacheFile, cachedInfo.IfName, cachedInfo.NetName) - continue - } - - attachments = append(attachments, NetAttachment{ - Name: cachedInfo.NetName, - Ifname: cachedInfo.IfName, - }) - } - return attachments, nil -} - -// TearDownPod tears down pod networks. Prefers cached pod attachment information -// but falls back to given network attachment information. -func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error { - return plugin.TearDownPodWithContext(context.Background(), podNetwork) -} - -func (plugin *cniNetworkPlugin) TearDownPodWithContext(ctx context.Context, podNetwork PodNetwork) error { - if len(podNetwork.Networks) == 0 { - attachments, err := plugin.getCachedNetworkInfo(podNetwork.ID) - if err == nil && len(attachments) > 0 { - podNetwork.Networks = attachments - } - } - - if err := plugin.networksAvailable(&podNetwork); err != nil { - return err - } - - plugin.podLock(podNetwork).Lock() - defer plugin.podUnlock(podNetwork) - - if err := tearDownLoopback(podNetwork.NetNS); err != nil { - // ignore error - logrus.Warningf("Ignoring error tearing down loopback interface: %v", err) - } - - return plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { - fullPodName := buildFullPodName(*podNetwork) - logrus.Infof("Deleting pod %s from CNI network %q (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) - if err := network.deleteFromNetwork(ctx, rt, plugin.cniConfig); err != nil { - return fmt.Errorf("error removing pod %s from CNI network %q: %v", fullPodName, network.name, err) - } - return nil - }) -} - -// GetPodNetworkStatus returns IP addressing and interface details for all -// networks attached to the pod. -func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]NetResult, error) { - return plugin.GetPodNetworkStatusWithContext(context.Background(), podNetwork) -} - -// GetPodNetworkStatusWithContext returns IP addressing and interface details for all -// networks attached to the pod. -func (plugin *cniNetworkPlugin) GetPodNetworkStatusWithContext(ctx context.Context, podNetwork PodNetwork) ([]NetResult, error) { - plugin.podLock(podNetwork).Lock() - defer plugin.podUnlock(podNetwork) - - if err := checkLoopback(podNetwork.NetNS); err != nil { - logrus.Errorf(err.Error()) - return nil, err - } - - results := make([]NetResult, 0) - if err := plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { - fullPodName := buildFullPodName(*podNetwork) - logrus.Infof("Checking pod %s for CNI network %s (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) - result, err := network.checkNetwork(ctx, rt, plugin.cniConfig, plugin.nsManager, podNetwork.NetNS) - if err != nil { - return fmt.Errorf("error checking pod %s for CNI network %q: %v", fullPodName, network.name, err) - } - if result != nil { - results = append(results, NetResult{ - Result: result, - NetAttachment: NetAttachment{ - Name: network.name, - Ifname: rt.IfName, - }, - }) - } - return nil - }); err != nil { - return nil, err - } - - return results, nil -} - -func (network *cniNetwork) addToNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig) (cnitypes.Result, error) { - return cni.AddNetworkList(ctx, network.config, rt) -} - -func (network *cniNetwork) checkNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig, nsManager *nsManager, netns string) (cnitypes.Result, error) { - gtet, err := cniversion.GreaterThanOrEqualTo(network.config.CNIVersion, "0.4.0") - if err != nil { - return nil, err - } - - var result cnitypes.Result - - // When CNIVersion supports Check, use it. Otherwise fall back on what was done initially. - if gtet { - err = cni.CheckNetworkList(ctx, network.config, rt) - logrus.Infof("Checking CNI network %s (config version=%v)", network.name, network.config.CNIVersion) - if err != nil { - logrus.Errorf("Error checking network: %v", err) - return nil, err - } - } - - result, err = cni.GetNetworkListCachedResult(network.config, rt) - if err != nil { - logrus.Errorf("Error getting network list cached result: %v", err) - return nil, err - } else if result != nil { - return result, nil - } - - // result doesn't exist, create one - logrus.Infof("Checking CNI network %s (config version=%v) nsManager=%v", network.name, network.config.CNIVersion, nsManager) - - var cniInterface *cnicurrent.Interface - ips := []*cnicurrent.IPConfig{} - errs := []error{} - for _, version := range []string{"4", "6"} { - ip, mac, err := getContainerDetails(nsManager, netns, rt.IfName, "-"+version) - if err == nil { - if cniInterface == nil { - cniInterface = &cnicurrent.Interface{ - Name: rt.IfName, - Mac: mac.String(), - Sandbox: netns, - } - } - ips = append(ips, &cnicurrent.IPConfig{ - Version: version, - Interface: cnicurrent.Int(0), - Address: *ip, - }) - } else { - errs = append(errs, err) - } - } - if cniInterface == nil || len(ips) == 0 { - return nil, fmt.Errorf("neither IPv4 nor IPv6 found when retrieving network status: %v", errs) - } - - result = &cnicurrent.Result{ - CNIVersion: network.config.CNIVersion, - Interfaces: []*cnicurrent.Interface{cniInterface}, - IPs: ips, - } - - // Result must be the same CNIVersion as the CNI config - converted, err := result.GetAsVersion(network.config.CNIVersion) - if err != nil { - return nil, err - } - - return converted, nil -} - -func (network *cniNetwork) deleteFromNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig) error { - return cni.DelNetworkList(ctx, network.config, rt) -} - -func buildCNIRuntimeConf(podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (*libcni.RuntimeConf, error) { - logrus.Infof("Got pod network %+v", podNetwork) - - rt := &libcni.RuntimeConf{ - ContainerID: podNetwork.ID, - NetNS: podNetwork.NetNS, - IfName: ifName, - Args: [][2]string{ - {"IgnoreUnknown", "1"}, - {"K8S_POD_NAMESPACE", podNetwork.Namespace}, - {"K8S_POD_NAME", podNetwork.Name}, - {"K8S_POD_INFRA_CONTAINER_ID", podNetwork.ID}, - }, - CapabilityArgs: map[string]interface{}{}, - } - - // Propagate existing CNI_ARGS to non-k8s consumers - for _, kvpairs := range strings.Split(os.Getenv("CNI_ARGS"), ";") { - if keyval := strings.SplitN(kvpairs, "=", 2); len(keyval) == 2 { - rt.Args = append(rt.Args, [2]string{keyval[0], keyval[1]}) - } - } - - // Add requested static IP to CNI_ARGS - ip := runtimeConfig.IP - if ip != "" { - if tstIP := net.ParseIP(ip); tstIP == nil { - return nil, fmt.Errorf("unable to parse IP address %q", ip) - } - rt.Args = append(rt.Args, [2]string{"IP", ip}) - } - - // Add the requested static MAC to CNI_ARGS - mac := runtimeConfig.MAC - if mac != "" { - _, err := net.ParseMAC(mac) - if err != nil { - return nil, fmt.Errorf("unable to parse MAC address %q: %v", mac, err) - } - rt.Args = append(rt.Args, [2]string{"MAC", mac}) - } - - // Set PortMappings in Capabilities - if len(runtimeConfig.PortMappings) != 0 { - rt.CapabilityArgs["portMappings"] = runtimeConfig.PortMappings - } - - // Set Bandwidth in Capabilities - if runtimeConfig.Bandwidth != nil { - rt.CapabilityArgs["bandwidth"] = map[string]uint64{ - "ingressRate": runtimeConfig.Bandwidth.IngressRate, - "ingressBurst": runtimeConfig.Bandwidth.IngressBurst, - "egressRate": runtimeConfig.Bandwidth.EgressRate, - "egressBurst": runtimeConfig.Bandwidth.EgressBurst, - } - } - - // Set IpRanges in Capabilities - if len(runtimeConfig.IpRanges) > 0 { - rt.CapabilityArgs["ipRanges"] = runtimeConfig.IpRanges - } - - // Set Aliases in Capabilities - if len(podNetwork.Aliases) > 0 { - rt.CapabilityArgs["aliases"] = podNetwork.Aliases - } - return rt, nil -} - -func (plugin *cniNetworkPlugin) Status() error { - if plugin.getDefaultNetwork() == nil { - return fmt.Errorf(errMissingDefaultNetwork, plugin.confDir) - } - return nil -} diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go deleted file mode 100644 index 7326b4b40..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go +++ /dev/null @@ -1,152 +0,0 @@ -package ocicni - -import ( - "context" - - "github.com/containernetworking/cni/pkg/types" -) - -const ( - // DefaultInterfaceName is the string to be used for the interface name inside the net namespace - DefaultInterfaceName = "eth0" - // CNIPluginName is the default name of the plugin - CNIPluginName = "cni" -) - -// PortMapping maps to the standard CNI portmapping Capability -// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md -type PortMapping struct { - // HostPort is the port number on the host. - HostPort int32 `json:"hostPort"` - // ContainerPort is the port number inside the sandbox. - ContainerPort int32 `json:"containerPort"` - // Protocol is the protocol of the port mapping. - Protocol string `json:"protocol"` - // HostIP is the host ip to use. - HostIP string `json:"hostIP"` -} - -// IpRange maps to the standard CNI ipRanges Capability -// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md -type IpRange struct { - // Subnet is the whole CIDR - Subnet string `json:"subnet"` - // RangeStart is the first available IP in subnet - RangeStart string `json:"rangeStart,omitempty"` - // RangeEnd is the last available IP in subnet - RangeEnd string `json:"rangeEnd,omitempty"` - // Gateway is the gateway of subnet - Gateway string `json:"gateway,omitempty"` -} - -// RuntimeConfig is additional configuration for a single CNI network that -// is pod-specific rather than general to the network. -type RuntimeConfig struct { - // IP is a static IP to be specified in the network. Can only be used - // with the hostlocal IP allocator. If left unset, an IP will be - // dynamically allocated. - IP string - // MAC is a static MAC address to be assigned to the network interface. - // If left unset, a MAC will be dynamically allocated. - MAC string - // PortMappings is the port mapping of the sandbox. - PortMappings []PortMapping - // Bandwidth is the bandwidth limiting of the pod - Bandwidth *BandwidthConfig - // IpRanges is the ip range gather which is used for address allocation - IpRanges [][]IpRange -} - -// BandwidthConfig maps to the standard CNI bandwidth Capability -// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md -type BandwidthConfig struct { - // IngressRate is a limit for incoming traffic in bps - IngressRate uint64 - IngressBurst uint64 - - // EgressRate is a limit for outgoing traffic in bps - EgressRate uint64 - EgressBurst uint64 -} - -// PodNetwork configures the network of a pod sandbox. -type PodNetwork struct { - // Name is the name of the sandbox. - Name string - // Namespace is the namespace of the sandbox. - Namespace string - // ID is the id of the sandbox container. - ID string - // NetNS is the network namespace path of the sandbox. - NetNS string - - // Networks is a list of CNI network names (and optional interface - // names) to attach to the sandbox. Leave this list empty to attach the - // default network to the sandbox - Networks []NetAttachment - - // NetworkConfig is configuration specific to a single CNI network. - // It is optional, and can be omitted for some or all specified networks - // without issue. - RuntimeConfig map[string]RuntimeConfig - - // Aliases are network-scoped names for resolving a container - // by name. The key value is the network name and the value is - // is a string slice of aliases - Aliases map[string][]string -} - -// NetAttachment describes a container network attachment -type NetAttachment struct { - // NetName contains the name of the CNI network to which the container - // should be or is attached - Name string - // Ifname contains the optional interface name of the attachment - Ifname string -} - -// NetResult contains the result the network attachment operation -type NetResult struct { - // Result is the CNI Result - Result types.Result - // NetAttachment contains the network and interface names of this - // network attachment - NetAttachment -} - -// CNIPlugin is the interface that needs to be implemented by a plugin -type CNIPlugin interface { - // Name returns the plugin's name. This will be used when searching - // for a plugin by name, e.g. - Name() string - - // GetDefaultNetworkName returns the name of the plugin's default - // network. - GetDefaultNetworkName() string - - // SetUpPod is the method called after the sandbox container of - // the pod has been created but before the other containers of the - // pod are launched. - SetUpPod(network PodNetwork) ([]NetResult, error) - - // SetUpPodWithContext is the same as SetUpPod but takes a context - SetUpPodWithContext(ctx context.Context, network PodNetwork) ([]NetResult, error) - - // TearDownPod is the method called before a pod's sandbox container will be deleted - TearDownPod(network PodNetwork) error - - // TearDownPodWithContext is the same as TearDownPod but takes a context - TearDownPodWithContext(ctx context.Context, network PodNetwork) error - - // GetPodNetworkStatus is the method called to obtain the ipv4 or ipv6 addresses of the pod sandbox - GetPodNetworkStatus(network PodNetwork) ([]NetResult, error) - - // GetPodNetworkStatusWithContext is the same as GetPodNetworkStatus but takes a context - GetPodNetworkStatusWithContext(ctx context.Context, network PodNetwork) ([]NetResult, error) - - // NetworkStatus returns error if the network plugin is in error state - Status() error - - // Shutdown terminates all driver operations - Shutdown() error -} diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_unix.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_unix.go deleted file mode 100644 index 88010f737..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_unix.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build !windows - -package ocicni - -const ( - // DefaultConfDir is the default place to look for CNI Network - DefaultConfDir = "/etc/cni/net.d" - // DefaultBinDir is the default place to look for CNI config files - DefaultBinDir = "/opt/cni/bin" -) diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_windows.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_windows.go deleted file mode 100644 index 061ecae5c..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types_windows.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build windows - -package ocicni - -const ( - // DefaultConfDir is the default place to look for CNI Network - DefaultConfDir = "C:\\cni\\etc\\net.d" - // DefaultBinDir is the default place to look for cni config files - DefaultBinDir = "C:\\cni\\bin" -) diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/util.go deleted file mode 100644 index 2af786593..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util.go +++ /dev/null @@ -1,8 +0,0 @@ -package ocicni - -// newNSManager initializes a new namespace manager, which is a platform dependent struct. -func newNSManager() (*nsManager, error) { - nsm := &nsManager{} - err := nsm.init() - return nsm, err -} diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_linux.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_linux.go deleted file mode 100644 index 53c22f83f..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_linux.go +++ /dev/null @@ -1,150 +0,0 @@ -// +build linux - -package ocicni - -import ( - "fmt" - "net" - "os/exec" - "strings" - - "github.com/containernetworking/plugins/pkg/ns" - "github.com/vishvananda/netlink" -) - -var defaultNamespaceEnterCommandName = "nsenter" - -type nsManager struct { - nsenterPath string -} - -func (nsm *nsManager) init() error { - var err error - nsm.nsenterPath, err = exec.LookPath(defaultNamespaceEnterCommandName) - return err -} - -func getContainerDetails(nsm *nsManager, netnsPath, interfaceName, addrType string) (*net.IPNet, *net.HardwareAddr, error) { - // Try to retrieve ip inside container network namespace - output, err := exec.Command(nsm.nsenterPath, fmt.Sprintf("--net=%s", netnsPath), "-F", "--", - "ip", "-o", addrType, "addr", "show", "dev", interfaceName, "scope", "global").CombinedOutput() - if err != nil { - return nil, nil, fmt.Errorf("Unexpected command output %s with error: %v", output, err) - } - - lines := strings.Split(string(output), "\n") - if len(lines) < 1 { - return nil, nil, fmt.Errorf("Unexpected command output %s", output) - } - fields := strings.Fields(lines[0]) - if len(fields) < 4 { - return nil, nil, fmt.Errorf("Unexpected address output %s ", lines[0]) - } - ip, ipNet, err := net.ParseCIDR(fields[3]) - if err != nil { - return nil, nil, fmt.Errorf("CNI failed to parse ip from output %s due to %v", output, err) - } - if ip.To4() == nil { - ipNet.IP = ip - } else { - ipNet.IP = ip.To4() - } - - // Try to retrieve MAC inside container network namespace - output, err = exec.Command(nsm.nsenterPath, fmt.Sprintf("--net=%s", netnsPath), "-F", "--", - "ip", "link", "show", "dev", interfaceName).CombinedOutput() - if err != nil { - return nil, nil, fmt.Errorf("unexpected 'ip link' command output %s with error: %v", output, err) - } - - lines = strings.Split(string(output), "\n") - if len(lines) < 2 { - return nil, nil, fmt.Errorf("unexpected 'ip link' command output %s", output) - } - fields = strings.Fields(lines[1]) - if len(fields) < 4 { - return nil, nil, fmt.Errorf("unexpected link output %s ", lines[0]) - } - mac, err := net.ParseMAC(fields[1]) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse MAC from output %s due to %v", output, err) - } - - return ipNet, &mac, nil -} - -func tearDownLoopback(netns string) error { - return ns.WithNetNSPath(netns, func(_ ns.NetNS) error { - link, err := netlink.LinkByName(loIfname) - if err != nil { - return err // not tested - } - err = netlink.LinkSetDown(link) - if err != nil { - return err // not tested - } - return nil - }) -} - -func bringUpLoopback(netns string) error { - if err := ns.WithNetNSPath(netns, func(_ ns.NetNS) error { - link, err := netlink.LinkByName(loIfname) - if err == nil { - err = netlink.LinkSetUp(link) - } - if err != nil { - return err - } - - v4Addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) - if err != nil { - return err - } - if len(v4Addrs) != 0 { - // sanity check that this is a loopback address - for _, addr := range v4Addrs { - if !addr.IP.IsLoopback() { - return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP) - } - } - } - - v6Addrs, err := netlink.AddrList(link, netlink.FAMILY_V6) - if err != nil { - return err - } - if len(v6Addrs) != 0 { - // sanity check that this is a loopback address - for _, addr := range v6Addrs { - if !addr.IP.IsLoopback() { - return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP) - } - } - } - - return nil - }); err != nil { - return fmt.Errorf("error adding loopback interface: %s", err) - } - return nil -} - -func checkLoopback(netns string) error { - // Make sure loopback interface is up - if err := ns.WithNetNSPath(netns, func(_ ns.NetNS) error { - link, err := netlink.LinkByName(loIfname) - if err != nil { - return err - } - - if link.Attrs().Flags&net.FlagUp != net.FlagUp { - return fmt.Errorf("loopback interface is down") - } - - return nil - }); err != nil { - return fmt.Errorf("error checking loopback interface: %v", err) - } - return nil -} diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_unsupported.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_unsupported.go deleted file mode 100644 index b87f0d373..000000000 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/util_unsupported.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build !linux - -package ocicni - -import ( - "errors" - "net" -) - -type nsManager struct { -} - -var errUnsupportedPlatform = errors.New("unsupported platform") - -func (nsm *nsManager) init() error { - return nil -} - -func getContainerDetails(nsm *nsManager, netnsPath, interfaceName, addrType string) (*net.IPNet, *net.HardwareAddr, error) { - return nil, nil, errUnsupportedPlatform -} - -func tearDownLoopback(netns string) error { - return errUnsupportedPlatform -} - -func bringUpLoopback(netns string) error { - return errUnsupportedPlatform -} - -func checkLoopback(netns string) error { - return errUnsupportedPlatform - -} diff --git a/vendor/github.com/safchain/ethtool/.gitignore b/vendor/github.com/safchain/ethtool/.gitignore deleted file mode 100644 index db6cadffd..000000000 --- a/vendor/github.com/safchain/ethtool/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -# Skip compiled example binary file -/example/example diff --git a/vendor/github.com/safchain/ethtool/.travis.yml b/vendor/github.com/safchain/ethtool/.travis.yml deleted file mode 100644 index 4f2ee4d97..000000000 --- a/vendor/github.com/safchain/ethtool/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: go diff --git a/vendor/github.com/safchain/ethtool/LICENSE b/vendor/github.com/safchain/ethtool/LICENSE deleted file mode 100644 index 8f71f43fe..000000000 --- a/vendor/github.com/safchain/ethtool/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/vendor/github.com/safchain/ethtool/Makefile b/vendor/github.com/safchain/ethtool/Makefile deleted file mode 100644 index 67d2da395..000000000 --- a/vendor/github.com/safchain/ethtool/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: build - -build: - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build diff --git a/vendor/github.com/safchain/ethtool/README.md b/vendor/github.com/safchain/ethtool/README.md deleted file mode 100644 index 1f146229c..000000000 --- a/vendor/github.com/safchain/ethtool/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# ethtool go package # - -[![Build Status](https://travis-ci.org/safchain/ethtool.png?branch=master)](https://travis-ci.org/safchain/ethtool) -[![GoDoc](https://godoc.org/github.com/safchain/ethtool?status.svg)](https://godoc.org/github.com/safchain/ethtool) - -The ethtool package aims to provide a library giving a simple access to the Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations from a network device like statistics, driver related informations or even the peer of a VETH interface. - -## Build and Test ## - -go get command: - - go get github.com/safchain/ethtool - -Testing - -In order to run te - - go test github.com/safchain/ethtool - -## Examples ## - -```go -package main - -import ( - "fmt" - - "github.com/safchain/ethtool" -) - -func main() { - ethHandle, err := ethtool.NewEthtool() - if err != nil { - panic(err.Error()) - } - defer ethHandle.Close() - - // Retrieve tx from eth0 - stats, err := ethHandle.Stats("eth0") - if err != nil { - panic(err.Error()) - } - fmt.Printf("TX: %d\n", stats["tx_bytes"]) - - // Retrieve peer index of a veth interface - stats, err = ethHandle.Stats("veth0") - if err != nil { - panic(err.Error()) - } - fmt.Printf("Peer Index: %d\n", stats["peer_ifindex"]) -} -``` - -## LICENSE ## - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/vendor/github.com/safchain/ethtool/ethtool.go b/vendor/github.com/safchain/ethtool/ethtool.go deleted file mode 100644 index 8dcc78c05..000000000 --- a/vendor/github.com/safchain/ethtool/ethtool.go +++ /dev/null @@ -1,541 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -// Package ethtool aims to provide a library giving a simple access to the -// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations -// from a network device like statistics, driver related informations or -// even the peer of a VETH interface. -package ethtool - -import ( - "bytes" - "encoding/hex" - "fmt" - "strings" - "syscall" - "unsafe" -) - -// Maximum size of an interface name -const ( - IFNAMSIZ = 16 -) - -// ioctl ethtool request -const ( - SIOCETHTOOL = 0x8946 -) - -// ethtool stats related constants. -const ( - ETH_GSTRING_LEN = 32 - ETH_SS_STATS = 1 - ETH_SS_FEATURES = 4 - ETHTOOL_GDRVINFO = 0x00000003 - ETHTOOL_GSTRINGS = 0x0000001b - ETHTOOL_GSTATS = 0x0000001d - // other CMDs from ethtool-copy.h of ethtool-3.5 package - ETHTOOL_GSET = 0x00000001 /* Get settings. */ - ETHTOOL_SSET = 0x00000002 /* Set settings. */ - ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */ - ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */ - /* Get link status for host, i.e. whether the interface *and* the - * physical port (if there is one) are up (ethtool_value). */ - ETHTOOL_GLINK = 0x0000000a - ETHTOOL_GMODULEINFO = 0x00000042 /* Get plug-in module information */ - ETHTOOL_GMODULEEEPROM = 0x00000043 /* Get plug-in module eeprom */ - ETHTOOL_GPERMADDR = 0x00000020 - ETHTOOL_GFEATURES = 0x0000003a /* Get device offload settings */ - ETHTOOL_SFEATURES = 0x0000003b /* Change device offload settings */ - ETHTOOL_GFLAGS = 0x00000025 /* Get flags bitmap(ethtool_value) */ - ETHTOOL_GSSET_INFO = 0x00000037 /* Get string set info */ -) - -// MAX_GSTRINGS maximum number of stats entries that ethtool can -// retrieve currently. -const ( - MAX_GSTRINGS = 1000 - MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32 - EEPROM_LEN = 640 - PERMADDR_LEN = 32 -) - -type ifreq struct { - ifr_name [IFNAMSIZ]byte - ifr_data uintptr -} - -// following structures comes from uapi/linux/ethtool.h -type ethtoolSsetInfo struct { - cmd uint32 - reserved uint32 - sset_mask uint32 - data uintptr -} - -type ethtoolGetFeaturesBlock struct { - available uint32 - requested uint32 - active uint32 - never_changed uint32 -} - -type ethtoolGfeatures struct { - cmd uint32 - size uint32 - blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock -} - -type ethtoolSetFeaturesBlock struct { - valid uint32 - requested uint32 -} - -type ethtoolSfeatures struct { - cmd uint32 - size uint32 - blocks [MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock -} - -type ethtoolDrvInfo struct { - cmd uint32 - driver [32]byte - version [32]byte - fw_version [32]byte - bus_info [32]byte - erom_version [32]byte - reserved2 [12]byte - n_priv_flags uint32 - n_stats uint32 - testinfo_len uint32 - eedump_len uint32 - regdump_len uint32 -} - -type ethtoolGStrings struct { - cmd uint32 - string_set uint32 - len uint32 - data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte -} - -type ethtoolStats struct { - cmd uint32 - n_stats uint32 - data [MAX_GSTRINGS]uint64 -} - -type ethtoolEeprom struct { - cmd uint32 - magic uint32 - offset uint32 - len uint32 - data [EEPROM_LEN]byte -} - -type ethtoolModInfo struct { - cmd uint32 - tpe uint32 - eeprom_len uint32 - reserved [8]uint32 -} - -type ethtoolLink struct { - cmd uint32 - data uint32 -} - -type ethtoolPermAddr struct { - cmd uint32 - size uint32 - data [PERMADDR_LEN]byte -} - -type Ethtool struct { - fd int -} - -// DriverName returns the driver name of the given interface name. -func (e *Ethtool) DriverName(intf string) (string, error) { - info, err := e.getDriverInfo(intf) - if err != nil { - return "", err - } - return string(bytes.Trim(info.driver[:], "\x00")), nil -} - -// BusInfo returns the bus information of the given interface name. -func (e *Ethtool) BusInfo(intf string) (string, error) { - info, err := e.getDriverInfo(intf) - if err != nil { - return "", err - } - return string(bytes.Trim(info.bus_info[:], "\x00")), nil -} - -// ModuleEeprom returns Eeprom information of the given interface name. -func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) { - eeprom, _, err := e.getModuleEeprom(intf) - if err != nil { - return nil, err - } - - return eeprom.data[:eeprom.len], nil -} - -// ModuleEeprom returns Eeprom information of the given interface name. -func (e *Ethtool) ModuleEepromHex(intf string) (string, error) { - eeprom, _, err := e.getModuleEeprom(intf) - if err != nil { - return "", err - } - - return hex.EncodeToString(eeprom.data[:eeprom.len]), nil -} - -// DriverInfo returns driver information of the given interface name. -func (e *Ethtool) DriverInfo(intf string) (ethtoolDrvInfo, error) { - drvInfo, err := e.getDriverInfo(intf) - if err != nil { - return ethtoolDrvInfo{}, err - } - - return drvInfo, nil -} - -// PermAddr returns permanent address of the given interface name. -func (e *Ethtool) PermAddr(intf string) (string, error) { - permAddr, err := e.getPermAddr(intf) - if err != nil { - return "", err - } - - if permAddr.data[0] == 0 && permAddr.data[1] == 0 && - permAddr.data[2] == 0 && permAddr.data[3] == 0 && - permAddr.data[4] == 0 && permAddr.data[5] == 0 { - return "", nil - } - - return fmt.Sprintf("%x:%x:%x:%x:%x:%x", - permAddr.data[0:1], - permAddr.data[1:2], - permAddr.data[2:3], - permAddr.data[3:4], - permAddr.data[4:5], - permAddr.data[5:6], - ), nil -} - -func (e *Ethtool) ioctl(intf string, data uintptr) error { - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: data, - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return syscall.Errno(ep) - } - - return nil -} - -func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) { - drvinfo := ethtoolDrvInfo{ - cmd: ETHTOOL_GDRVINFO, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { - return ethtoolDrvInfo{}, err - } - - return drvinfo, nil -} - -func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) { - permAddr := ethtoolPermAddr{ - cmd: ETHTOOL_GPERMADDR, - size: PERMADDR_LEN, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&permAddr))); err != nil { - return ethtoolPermAddr{}, err - } - - return permAddr, nil -} - -func (e *Ethtool) getModuleEeprom(intf string) (ethtoolEeprom, ethtoolModInfo, error) { - modInfo := ethtoolModInfo{ - cmd: ETHTOOL_GMODULEINFO, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&modInfo))); err != nil { - return ethtoolEeprom{}, ethtoolModInfo{}, err - } - - eeprom := ethtoolEeprom{ - cmd: ETHTOOL_GMODULEEEPROM, - len: modInfo.eeprom_len, - offset: 0, - } - - if modInfo.eeprom_len > EEPROM_LEN { - return ethtoolEeprom{}, ethtoolModInfo{}, fmt.Errorf("eeprom size: %d is larger than buffer size: %d", modInfo.eeprom_len, EEPROM_LEN) - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&eeprom))); err != nil { - return ethtoolEeprom{}, ethtoolModInfo{}, err - } - - return eeprom, modInfo, nil -} - -func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool { - return (blocks)[index/32].active&(1<<(index%32)) != 0 -} - -func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) { - blockIndex, bitIndex := index/32, index%32 - - blocks[blockIndex].valid |= 1 << bitIndex - - if value { - blocks[blockIndex].requested |= 1 << bitIndex - } else { - blocks[blockIndex].requested &= ^(1 << bitIndex) - } -} - -// FeatureNames shows supported features by their name. -func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) { - ssetInfo := ethtoolSsetInfo{ - cmd: ETHTOOL_GSSET_INFO, - sset_mask: 1 << ETH_SS_FEATURES, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil { - return nil, err - } - - length := uint32(ssetInfo.data) - if length == 0 { - return map[string]uint{}, nil - } else if length > MAX_GSTRINGS { - return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length) - } - - gstrings := ethtoolGStrings{ - cmd: ETHTOOL_GSTRINGS, - string_set: ETH_SS_FEATURES, - len: length, - data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { - return nil, err - } - - var result = make(map[string]uint) - for i := 0; i != int(length); i++ { - b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] - key := string(bytes.Trim(b, "\x00")) - if key != "" { - result[key] = uint(i) - } - } - - return result, nil -} - -// Features retrieves features of the given interface name. -func (e *Ethtool) Features(intf string) (map[string]bool, error) { - names, err := e.FeatureNames(intf) - if err != nil { - return nil, err - } - - length := uint32(len(names)) - if length == 0 { - return map[string]bool{}, nil - } - - features := ethtoolGfeatures{ - cmd: ETHTOOL_GFEATURES, - size: (length + 32 - 1) / 32, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil { - return nil, err - } - - var result = make(map[string]bool, length) - for key, index := range names { - result[key] = isFeatureBitSet(features.blocks, index) - } - - return result, nil -} - -// Change requests a change in the given device's features. -func (e *Ethtool) Change(intf string, config map[string]bool) error { - names, err := e.FeatureNames(intf) - if err != nil { - return err - } - - length := uint32(len(names)) - - features := ethtoolSfeatures{ - cmd: ETHTOOL_SFEATURES, - size: (length + 32 - 1) / 32, - } - - for key, value := range config { - if index, ok := names[key]; ok { - setFeatureBit(&features.blocks, index, value) - } else { - return fmt.Errorf("unsupported feature %q", key) - } - } - - return e.ioctl(intf, uintptr(unsafe.Pointer(&features))) -} - -// Get state of a link. -func (e *Ethtool) LinkState(intf string) (uint32, error) { - x := ethtoolLink{ - cmd: ETHTOOL_GLINK, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&x))); err != nil { - return 0, err - } - - return x.data, nil -} - -// Stats retrieves stats of the given interface name. -func (e *Ethtool) Stats(intf string) (map[string]uint64, error) { - drvinfo := ethtoolDrvInfo{ - cmd: ETHTOOL_GDRVINFO, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil { - return nil, err - } - - if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN { - return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats) - } - - gstrings := ethtoolGStrings{ - cmd: ETHTOOL_GSTRINGS, - string_set: ETH_SS_STATS, - len: drvinfo.n_stats, - data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{}, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil { - return nil, err - } - - stats := ethtoolStats{ - cmd: ETHTOOL_GSTATS, - n_stats: drvinfo.n_stats, - data: [MAX_GSTRINGS]uint64{}, - } - - if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil { - return nil, err - } - - var result = make(map[string]uint64) - for i := 0; i != int(drvinfo.n_stats); i++ { - b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN] - key := string(b[:strings.Index(string(b), "\x00")]) - if len(key) != 0 { - result[key] = stats.data[i] - } - } - - return result, nil -} - -// Close closes the ethool handler -func (e *Ethtool) Close() { - syscall.Close(e.fd) -} - -// NewEthtool returns a new ethtool handler -func NewEthtool() (*Ethtool, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP) - if err != nil { - return nil, err - } - - return &Ethtool{ - fd: int(fd), - }, nil -} - -// BusInfo returns bus information of the given interface name. -func BusInfo(intf string) (string, error) { - e, err := NewEthtool() - if err != nil { - return "", err - } - defer e.Close() - return e.BusInfo(intf) -} - -// DriverName returns the driver name of the given interface name. -func DriverName(intf string) (string, error) { - e, err := NewEthtool() - if err != nil { - return "", err - } - defer e.Close() - return e.DriverName(intf) -} - -// Stats retrieves stats of the given interface name. -func Stats(intf string) (map[string]uint64, error) { - e, err := NewEthtool() - if err != nil { - return nil, err - } - defer e.Close() - return e.Stats(intf) -} - -// PermAddr returns permanent address of the given interface name. -func PermAddr(intf string) (string, error) { - e, err := NewEthtool() - if err != nil { - return "", err - } - defer e.Close() - return e.PermAddr(intf) -} diff --git a/vendor/github.com/safchain/ethtool/ethtool_cmd.go b/vendor/github.com/safchain/ethtool/ethtool_cmd.go deleted file mode 100644 index d0c35e476..000000000 --- a/vendor/github.com/safchain/ethtool/ethtool_cmd.go +++ /dev/null @@ -1,207 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -// Package ethtool aims to provide a library giving a simple access to the -// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations -// from a network device like statistics, driver related informations or -// even the peer of a VETH interface. -package ethtool - -import ( - "math" - "reflect" - "syscall" - "unsafe" -) - -type EthtoolCmd struct { /* ethtool.c: struct ethtool_cmd */ - Cmd uint32 - Supported uint32 - Advertising uint32 - Speed uint16 - Duplex uint8 - Port uint8 - Phy_address uint8 - Transceiver uint8 - Autoneg uint8 - Mdio_support uint8 - Maxtxpkt uint32 - Maxrxpkt uint32 - Speed_hi uint16 - Eth_tp_mdix uint8 - Reserved2 uint8 - Lp_advertising uint32 - Reserved [2]uint32 -} - -// CmdGet returns the interface settings in the receiver struct -// and returns speed -func (ecmd *EthtoolCmd) CmdGet(intf string) (uint32, error) { - e, err := NewEthtool() - if err != nil { - return 0, err - } - defer e.Close() - return e.CmdGet(ecmd, intf) -} - -// CmdSet sets and returns the settings in the receiver struct -// and returns speed -func (ecmd *EthtoolCmd) CmdSet(intf string) (uint32, error) { - e, err := NewEthtool() - if err != nil { - return 0, err - } - defer e.Close() - return e.CmdSet(ecmd, intf) -} - -func (f *EthtoolCmd) reflect(retv *map[string]uint64) { - val := reflect.ValueOf(f).Elem() - - for i := 0; i < val.NumField(); i++ { - valueField := val.Field(i) - typeField := val.Type().Field(i) - - t := valueField.Interface() - //tt := reflect.TypeOf(t) - //fmt.Printf(" t %T %v tt %T %v\n", t, t, tt, tt) - switch t.(type) { - case uint32: - //fmt.Printf(" t is uint32\n") - (*retv)[typeField.Name] = uint64(t.(uint32)) - case uint16: - (*retv)[typeField.Name] = uint64(t.(uint16)) - case uint8: - (*retv)[typeField.Name] = uint64(t.(uint8)) - case int32: - (*retv)[typeField.Name] = uint64(t.(int32)) - case int16: - (*retv)[typeField.Name] = uint64(t.(int16)) - case int8: - (*retv)[typeField.Name] = uint64(t.(int8)) - default: - (*retv)[typeField.Name+"_unknown_type"] = 0 - } - - //tag := typeField.Tag - //fmt.Printf("Field Name: %s,\t Field Value: %v,\t Tag Value: %s\n", - // typeField.Name, valueField.Interface(), tag.Get("tag_name")) - } -} - -// CmdGet returns the interface settings in the receiver struct -// and returns speed -func (e *Ethtool) CmdGet(ecmd *EthtoolCmd, intf string) (uint32, error) { - ecmd.Cmd = ETHTOOL_GSET - - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(ecmd)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return 0, syscall.Errno(ep) - } - - var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | - (uint32(ecmd.Speed) & 0xffff) - if speedval == math.MaxUint16 { - speedval = math.MaxUint32 - } - - return speedval, nil -} - -// CmdSet sets and returns the settings in the receiver struct -// and returns speed -func (e *Ethtool) CmdSet(ecmd *EthtoolCmd, intf string) (uint32, error) { - ecmd.Cmd = ETHTOOL_SSET - - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(ecmd)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return 0, syscall.Errno(ep) - } - - var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | - (uint32(ecmd.Speed) & 0xffff) - if speedval == math.MaxUint16 { - speedval = math.MaxUint32 - } - - return speedval, nil -} - -// CmdGetMapped returns the interface settings in a map -func (e *Ethtool) CmdGetMapped(intf string) (map[string]uint64, error) { - ecmd := EthtoolCmd{ - Cmd: ETHTOOL_GSET, - } - - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(&ecmd)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return nil, syscall.Errno(ep) - } - - var result = make(map[string]uint64) - - // ref https://gist.github.com/drewolson/4771479 - // Golang Reflection Example - ecmd.reflect(&result) - - var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) | - (uint32(ecmd.Speed) & 0xffff) - result["speed"] = uint64(speedval) - - return result, nil -} - -func CmdGetMapped(intf string) (map[string]uint64, error) { - e, err := NewEthtool() - if err != nil { - return nil, err - } - defer e.Close() - return e.CmdGetMapped(intf) -} diff --git a/vendor/github.com/safchain/ethtool/ethtool_msglvl.go b/vendor/github.com/safchain/ethtool/ethtool_msglvl.go deleted file mode 100644 index 91836f019..000000000 --- a/vendor/github.com/safchain/ethtool/ethtool_msglvl.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -// Package ethtool aims to provide a library giving a simple access to the -// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations -// from a network device like statistics, driver related informations or -// even the peer of a VETH interface. -package ethtool - -import ( - "syscall" - "unsafe" -) - -type ethtoolValue struct { /* ethtool.c: struct ethtool_value */ - cmd uint32 - data uint32 -} - -// MsglvlGet returns the msglvl of the given interface. -func (e *Ethtool) MsglvlGet(intf string) (uint32, error) { - edata := ethtoolValue{ - cmd: ETHTOOL_GMSGLVL, - } - - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(&edata)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return 0, syscall.Errno(ep) - } - - return edata.data, nil -} - -// MsglvlSet returns the read-msglvl, post-set-msglvl of the given interface. -func (e *Ethtool) MsglvlSet(intf string, valset uint32) (uint32, uint32, error) { - edata := ethtoolValue{ - cmd: ETHTOOL_GMSGLVL, - } - - var name [IFNAMSIZ]byte - copy(name[:], []byte(intf)) - - ifr := ifreq{ - ifr_name: name, - ifr_data: uintptr(unsafe.Pointer(&edata)), - } - - _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return 0, 0, syscall.Errno(ep) - } - - readval := edata.data - - edata.cmd = ETHTOOL_SMSGLVL - edata.data = valset - - _, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), - SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr))) - if ep != 0 { - return 0, 0, syscall.Errno(ep) - } - - return readval, edata.data, nil -} - -// MsglvlGet returns the msglvl of the given interface. -func MsglvlGet(intf string) (uint32, error) { - e, err := NewEthtool() - if err != nil { - return 0, err - } - defer e.Close() - return e.MsglvlGet(intf) -} - -// MsglvlSet returns the read-msglvl, post-set-msglvl of the given interface. -func MsglvlSet(intf string, valset uint32) (uint32, uint32, error) { - e, err := NewEthtool() - if err != nil { - return 0, 0, err - } - defer e.Close() - return e.MsglvlSet(intf, valset) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 00f3dd743..324487b7c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -75,12 +75,7 @@ github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/version # github.com/containernetworking/plugins v0.9.1 -github.com/containernetworking/plugins/pkg/ip github.com/containernetworking/plugins/pkg/ns -github.com/containernetworking/plugins/pkg/utils/hwaddr -github.com/containernetworking/plugins/pkg/utils/sysctl -github.com/containernetworking/plugins/plugins/ipam/host-local/backend -github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator # github.com/containers/buildah v1.23.0 github.com/containers/buildah github.com/containers/buildah/bind @@ -249,8 +244,6 @@ github.com/containers/storage/pkg/tarlog github.com/containers/storage/pkg/truncindex github.com/containers/storage/pkg/unshare github.com/containers/storage/types -# github.com/coreos/go-iptables v0.5.0 -github.com/coreos/go-iptables/iptables # github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/coreos/go-systemd/activation # github.com/coreos/go-systemd/v22 v22.3.2 @@ -265,8 +258,6 @@ github.com/coreos/stream-metadata-go/fedoracoreos github.com/coreos/stream-metadata-go/fedoracoreos/internals github.com/coreos/stream-metadata-go/stream github.com/coreos/stream-metadata-go/stream/rhcos -# github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283 -github.com/cri-o/ocicni/pkg/ocicni # github.com/cyphar/filepath-securejoin v0.2.3 github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.1 @@ -579,8 +570,6 @@ github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/tcp github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy github.com/rootless-containers/rootlesskit/pkg/port/portutil -# github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 -github.com/safchain/ethtool # github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf github.com/seccomp/libseccomp-golang # github.com/sirupsen/logrus v1.8.1 |