mirror of
https://github.com/clearlinux/rkt.git
synced 2026-09-03 20:31:35 +00:00
293 lines
6.6 KiB
Go
293 lines
6.6 KiB
Go
// Copyright 2015 The rkt 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 networking
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/cni/pkg/ns"
|
|
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
|
|
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink"
|
|
|
|
"github.com/coreos/rkt/common"
|
|
"github.com/coreos/rkt/networking/netinfo"
|
|
)
|
|
|
|
const (
|
|
ifnamePattern = "eth%d"
|
|
selfNetNS = "/proc/self/ns/net"
|
|
)
|
|
|
|
// ForwardedPort describes a port that will be
|
|
// forwarded (mapped) from the host to the pod
|
|
type ForwardedPort struct {
|
|
Protocol string
|
|
HostPort uint
|
|
PodPort uint
|
|
}
|
|
|
|
// Networking describes the networking details of a pod.
|
|
type Networking struct {
|
|
podEnv
|
|
|
|
hostNS *os.File
|
|
nets []activeNet
|
|
}
|
|
|
|
// Setup creates a new networking namespace and executes network plugins to
|
|
// setup private networking. It returns in the new pod namespace
|
|
func Setup(podRoot string, podID types.UUID, fps []ForwardedPort, privateNetList common.PrivateNetList) (*Networking, error) {
|
|
// TODO(jonboulle): currently podRoot is _always_ ".", and behaviour in other
|
|
// circumstances is untested. This should be cleaned up.
|
|
n := Networking{
|
|
podEnv: podEnv{
|
|
podRoot: podRoot,
|
|
podID: podID,
|
|
netsLoadList: privateNetList,
|
|
},
|
|
}
|
|
|
|
hostNS, podNS, err := basicNetNS()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// we're in podNS!
|
|
n.hostNS = hostNS
|
|
|
|
nspath := n.podNSPath()
|
|
|
|
if err = bindMountFile(selfNetNS, nspath); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
if err := syscall.Unmount(nspath, 0); err != nil {
|
|
log.Printf("Error unmounting %q: %v", nspath, err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
n.nets, err = n.loadNets()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading network definitions: %v", err)
|
|
}
|
|
|
|
err = withNetNS(podNS, hostNS, func() error {
|
|
if err := n.setupNets(n.nets); err != nil {
|
|
return err
|
|
}
|
|
return n.forwardPorts(fps, n.GetDefaultIP())
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &n, nil
|
|
}
|
|
|
|
// Load creates the Networking object from saved state.
|
|
// Assumes the current netns is that of the host.
|
|
func Load(podRoot string, podID *types.UUID) (*Networking, error) {
|
|
// the current directory is pod root
|
|
pdirfd, err := syscall.Open(podRoot, syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to open pod root directory (%v): %v", podRoot, err)
|
|
}
|
|
defer syscall.Close(pdirfd)
|
|
|
|
nis, err := netinfo.LoadAt(pdirfd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hostNS, err := os.Open(selfNetNS)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nets := []activeNet{}
|
|
for _, ni := range nis {
|
|
n, err := loadNet(ni.ConfPath)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
log.Printf("Error loading %q: %v; ignoring", ni.ConfPath, err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
// make a copy of ni to make it a unique object as it's saved via ptr
|
|
rti := ni
|
|
n.runtime = &rti
|
|
nets = append(nets, *n)
|
|
}
|
|
|
|
return &Networking{
|
|
podEnv: podEnv{
|
|
podRoot: podRoot,
|
|
podID: *podID,
|
|
},
|
|
hostNS: hostNS,
|
|
nets: nets,
|
|
}, nil
|
|
}
|
|
|
|
func (n *Networking) GetDefaultIP() net.IP {
|
|
if len(n.nets) == 0 {
|
|
return nil
|
|
}
|
|
return n.nets[len(n.nets)-1].runtime.IP
|
|
}
|
|
|
|
func (n *Networking) GetDefaultHostIP() (net.IP, error) {
|
|
if len(n.nets) == 0 {
|
|
return nil, fmt.Errorf("no networks found")
|
|
}
|
|
return n.nets[len(n.nets)-1].runtime.HostIP, nil
|
|
}
|
|
|
|
// Teardown cleans up a produced Networking object.
|
|
func (n *Networking) Teardown() {
|
|
// Teardown everything in reverse order of setup.
|
|
// This should be idempotent -- be tolerant of missing stuff
|
|
|
|
if err := n.enterHostNS(); err != nil {
|
|
log.Printf("Error switching to host netns: %v", err)
|
|
return
|
|
}
|
|
|
|
if err := n.unforwardPorts(); err != nil {
|
|
log.Printf("Error removing forwarded ports: %v", err)
|
|
}
|
|
|
|
n.teardownNets(n.nets)
|
|
|
|
if err := syscall.Unmount(n.podNSPath(), 0); err != nil {
|
|
// if already unmounted, umount(2) returns EINVAL
|
|
if !os.IsNotExist(err) && err != syscall.EINVAL {
|
|
log.Printf("Error unmounting %q: %v", n.podNSPath(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// sets up new netns with just lo
|
|
func basicNetNS() (hostNS, podNS *os.File, err error) {
|
|
hostNS, podNS, err = newNetNS()
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to create new netns: %v", err)
|
|
return
|
|
}
|
|
// we're in podNS!!
|
|
|
|
if err = loUp(); err != nil {
|
|
hostNS.Close()
|
|
podNS.Close()
|
|
return nil, nil, err
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// enterHostNS moves into the host's network namespace.
|
|
func (n *Networking) enterHostNS() error {
|
|
return ns.SetNS(n.hostNS, syscall.CLONE_NEWNET)
|
|
}
|
|
|
|
// Save writes out the info about active nets
|
|
// for "rkt list" and friends to display
|
|
func (e *Networking) Save() error {
|
|
nis := []netinfo.NetInfo{}
|
|
for _, n := range e.nets {
|
|
nis = append(nis, *n.runtime)
|
|
}
|
|
|
|
return netinfo.Save(e.podRoot, nis)
|
|
}
|
|
|
|
func newNetNS() (hostNS, childNS *os.File, err error) {
|
|
defer func() {
|
|
if err != nil {
|
|
if hostNS != nil {
|
|
hostNS.Close()
|
|
}
|
|
if childNS != nil {
|
|
childNS.Close()
|
|
}
|
|
}
|
|
}()
|
|
|
|
hostNS, err = os.Open(selfNetNS)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err = syscall.Unshare(syscall.CLONE_NEWNET); err != nil {
|
|
return
|
|
}
|
|
|
|
childNS, err = os.Open(selfNetNS)
|
|
if err != nil {
|
|
ns.SetNS(hostNS, syscall.CLONE_NEWNET)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// execute f() in tgtNS
|
|
func withNetNS(curNS, tgtNS *os.File, f func() error) error {
|
|
if err := ns.SetNS(tgtNS, syscall.CLONE_NEWNET); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := f(); err != nil {
|
|
// Attempt to revert the net ns in a known state
|
|
if err := ns.SetNS(curNS, syscall.CLONE_NEWNET); err != nil {
|
|
log.Printf("Cannot revert the net namespace: %v", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
return ns.SetNS(curNS, syscall.CLONE_NEWNET)
|
|
}
|
|
|
|
func loUp() error {
|
|
lo, err := netlink.LinkByName("lo")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to lookup lo: %v", err)
|
|
}
|
|
|
|
if err := netlink.LinkSetUp(lo); err != nil {
|
|
return fmt.Errorf("failed to set lo up: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func bindMountFile(src, dst string) error {
|
|
// mount point has to be an existing file
|
|
f, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Close()
|
|
|
|
return syscall.Mount(src, dst, "none", syscall.MS_BIND, "")
|
|
}
|