Update containernetworking/plugins to current master

We need to pick up changes to the netns packages that are not yet
in a released tag.

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #1165
Approved by: baude
This commit is contained in:
Matthew Heon
2018-07-26 13:37:01 -04:00
committed by Atomic Bot
parent f9152d075a
commit 54967d7a10
4 changed files with 16 additions and 102 deletions

View File

@ -9,7 +9,7 @@ github.com/buger/goterm 2f8dfbc7dbbff5dd1d391ed91482c24df243b2d3
github.com/containerd/cgroups 77e628511d924b13a77cebdc73b757a47f6d751b github.com/containerd/cgroups 77e628511d924b13a77cebdc73b757a47f6d751b
github.com/containerd/continuity master github.com/containerd/continuity master
github.com/containernetworking/cni v0.7.0-alpha1 github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins 1fb94a4222eafc6f948eacdca9c9f2158b427e53 github.com/containernetworking/plugins 1562a1e60ed101aacc5e08ed9dbeba8e9f3d4ec1
github.com/containers/image c6e0eee0f8eb38e78ae2e44a9aeea0576f451617 github.com/containers/image c6e0eee0f8eb38e78ae2e44a9aeea0576f451617
github.com/containers/storage afdedba2d2ad573350aee35033d4e0c58fdbd57b github.com/containers/storage afdedba2d2ad573350aee35033d4e0c58fdbd57b
github.com/containers/psgo 382fc951fe0a8aba62043862ce1a56f77524db87 github.com/containers/psgo 382fc951fe0a8aba62043862ce1a56f77524db87

View File

@ -4,12 +4,14 @@
# plugins # plugins
Some CNI network plugins, maintained by the containernetworking team. For more information, see the individual READMEs. Some CNI network plugins, maintained by the containernetworking team. For more information, see the individual READMEs.
Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
## Plugins supplied: ## Plugins supplied:
### Main: interface-creating ### Main: interface-creating
* `bridge`: Creates a bridge, adds the host and the container to it. * `bridge`: Creates a bridge, adds the host and the container to it.
* `ipvlan`: Adds an [ipvlan](https://www.kernel.org/doc/Documentation/networking/ipvlan.txt) interface in the container * `ipvlan`: Adds an [ipvlan](https://www.kernel.org/doc/Documentation/networking/ipvlan.txt) interface in the container.
* `loopback`: Creates a loopback interface * `loopback`: Set the state of loopback interface to up.
* `macvlan`: Creates a new MAC address, forwards all traffic to that to the container * `macvlan`: Creates a new MAC address, forwards all traffic to that to the container.
* `ptp`: Creates a veth pair. * `ptp`: Creates a veth pair.
* `vlan`: Allocates a vlan device. * `vlan`: Allocates a vlan device.

View File

@ -12,10 +12,6 @@ For example, you cannot rely on the `ns.Set()` namespace being the current names
The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
```go ```go
targetNs, err := ns.NewNS()
if err != nil {
return err
}
err = targetNs.Do(func(hostNs ns.NetNS) error { err = targetNs.Do(func(hostNs ns.NetNS) error {
dummy := &netlink.Dummy{ dummy := &netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{ LinkAttrs: netlink.LinkAttrs{
@ -26,11 +22,16 @@ err = targetNs.Do(func(hostNs ns.NetNS) error {
}) })
``` ```
Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem.
Also: If the runtime spawns a new OS thread, it will inherit the network namespace of the parent thread, which may have been temporarily switched, and thus the new OS thread will be permanently "stuck in the wrong namespace". When a new thread is spawned in Linux, it inherits the namepaces of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled.
In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater.
### Creating network namespaces
Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentialy unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration.
In short, **there is no safe way to change network namespaces from within a long-lived, multithreaded Go process**. If your daemon process needs to be namespace aware, consider spawning a separate process (like a CNI plugin) for each namespace.
### Further Reading ### Further Reading
- https://github.com/golang/go/wiki/LockOSThread - https://github.com/golang/go/wiki/LockOSThread

View File

@ -15,10 +15,8 @@
package ns package ns
import ( import (
"crypto/rand"
"fmt" "fmt"
"os" "os"
"path"
"runtime" "runtime"
"sync" "sync"
"syscall" "syscall"
@ -38,82 +36,6 @@ func getCurrentThreadNetNSPath() string {
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
} }
// Creates a new persistent network namespace and returns an object
// representing that namespace, without switching to it
func NewNS() (NetNS, error) {
const nsRunDir = "/var/run/netns"
b := make([]byte, 16)
_, err := rand.Reader.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
err = os.MkdirAll(nsRunDir, 0755)
if err != nil {
return nil, err
}
// create an empty file at the mount point
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
nsPath := path.Join(nsRunDir, nsName)
mountPointFd, err := os.Create(nsPath)
if err != nil {
return nil, err
}
mountPointFd.Close()
// Ensure the mount point is cleaned up on errors; if the namespace
// was successfully mounted this will have no effect because the file
// is in-use
defer os.RemoveAll(nsPath)
var wg sync.WaitGroup
wg.Add(1)
// do namespace work in a dedicated goroutine, so that we can safely
// Lock/Unlock OSThread without upsetting the lock/unlock state of
// the caller of this function
var fd *os.File
go (func() {
defer wg.Done()
runtime.LockOSThread()
var origNS NetNS
origNS, err = GetNS(getCurrentThreadNetNSPath())
if err != nil {
return
}
defer origNS.Close()
// create a new netns on the current thread
err = unix.Unshare(unix.CLONE_NEWNET)
if err != nil {
return
}
defer origNS.Set()
// bind mount the new netns from the current thread onto the mount point
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
if err != nil {
return
}
fd, err = os.Open(nsPath)
if err != nil {
return
}
})()
wg.Wait()
if err != nil {
unix.Unmount(nsPath, unix.MNT_DETACH)
return nil, fmt.Errorf("failed to create namespace: %v", err)
}
return &netNS{file: fd, mounted: true}, nil
}
func (ns *netNS) Close() error { func (ns *netNS) Close() error {
if err := ns.errorIfClosed(); err != nil { if err := ns.errorIfClosed(); err != nil {
return err return err
@ -124,16 +46,6 @@ func (ns *netNS) Close() error {
} }
ns.closed = true ns.closed = true
if ns.mounted {
if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
}
if err := os.RemoveAll(ns.file.Name()); err != nil {
return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
}
ns.mounted = false
}
return nil return nil
} }
@ -180,9 +92,8 @@ type NetNS interface {
} }
type netNS struct { type netNS struct {
file *os.File file *os.File
mounted bool closed bool
closed bool
} }
// netNS implements the NetNS interface // netNS implements the NetNS interface