Files
weave/plugin/net/cni.go
T
Martynas Pumputis 7670fed72c Add AWSVPC setup routines to the attach script
When running in the AWSVPC mode, we need to do the following:

- Keep the HW TX offloading on.
- Check that the allocated address belongs to the default subnet,
  because only one subnet is allowed and it should be the default one.

If it is not possible to determine whether AWSVPC is on, we ignore
all the steps, because the attachment routines have to work
regardless whether the router is running and the AWSVPC detection
is done by querying the /ipinfo/tracker endpoint served by
the router.
2016-06-08 16:44:54 +01:00

194 lines
4.9 KiB
Go

package plugin
import (
"crypto/rand"
"encoding/json"
"fmt"
"net"
"github.com/appc/cni/pkg/ipam"
"github.com/appc/cni/pkg/skel"
"github.com/appc/cni/pkg/types"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
weaveapi "github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common"
weavenet "github.com/weaveworks/weave/net"
ipamplugin "github.com/weaveworks/weave/plugin/ipam"
)
var (
zeroNetwork = net.IPNet{IP: net.IPv4zero, Mask: net.IPv4Mask(0, 0, 0, 0)}
mask32 = net.IPv4Mask(0xff, 0xff, 0xff, 0xff)
)
type CNIPlugin struct {
weave *weaveapi.Client
}
func NewCNIPlugin(weave *weaveapi.Client) *CNIPlugin {
return &CNIPlugin{weave: weave}
}
func loadNetConf(bytes []byte) (*NetConf, error) {
n := &NetConf{
BrName: weavenet.WeaveBridgeName,
}
if err := json.Unmarshal(bytes, n); err != nil {
return nil, fmt.Errorf("failed to load netconf: %v", err)
}
return n, nil
}
func (c *CNIPlugin) CmdAdd(args *skel.CmdArgs) error {
conf, err := loadNetConf(args.StdinData)
if err != nil {
return err
}
if conf.IsGW {
return fmt.Errorf("Gateway functionality not supported")
}
if conf.IPMasq {
return fmt.Errorf("IP Masquerading functionality not supported")
}
var result *types.Result
// Default IPAM is Weave's own
if conf.IPAM.Type == "" {
result, err = ipamplugin.NewIpam(c.weave).Allocate(args)
} else {
result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
}
if err != nil {
return fmt.Errorf("unable to allocate IP address: %s", err)
}
if result.IP4 == nil {
return fmt.Errorf("IPAM plugin failed to allocate IP address")
}
// If config says nothing about routes or gateway, default one will be via the bridge
if result.IP4.Routes == nil && result.IP4.Gateway == nil {
bridgeIP, err := findBridgeIP(conf.BrName, result.IP4.IP)
if err != nil {
return err
}
result.IP4.Gateway = bridgeIP
}
ns, err := netns.GetFromPath(args.Netns)
if err != nil {
return err
}
defer ns.Close()
id := args.ContainerID
if len(id) < 5 {
data := make([]byte, 5)
_, err := rand.Reader.Read(data)
if err != nil {
return err
}
id = fmt.Sprintf("%x", data)
}
if err := weavenet.AttachContainer(ns, id, args.IfName, conf.BrName, conf.MTU, false, []*net.IPNet{&result.IP4.IP}, false); err != nil {
return err
}
if err := weavenet.WithNetNSLink(ns, args.IfName, func(link netlink.Link) error {
return setupRoutes(link, args.IfName, result.IP4.IP, result.IP4.Gateway, result.IP4.Routes)
}); err != nil {
return fmt.Errorf("error setting up routes: %s", err)
}
result.DNS = conf.DNS
return result.Print()
}
func setupRoutes(link netlink.Link, name string, ipnet net.IPNet, gw net.IP, routes []types.Route) error {
var err error
if routes == nil { // If config says nothing about routes, add a default one
if !ipnet.Contains(gw) {
// The bridge IP is not on the same subnet; add a specific route to it
gw32 := &net.IPNet{IP: gw, Mask: mask32}
if err = weavenet.AddRoute(link, netlink.SCOPE_LINK, gw32, nil); err != nil {
return err
}
}
routes = []types.Route{{Dst: zeroNetwork}}
}
for _, r := range routes {
if r.GW != nil {
err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, r.GW)
} else {
err = weavenet.AddRoute(link, netlink.SCOPE_UNIVERSE, &r.Dst, gw)
}
if err != nil {
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, name, err)
}
}
return nil
}
func findBridgeIP(bridgeName string, subnet net.IPNet) (net.IP, error) {
netdevs, err := common.GetBridgeNetDev(bridgeName)
if err != nil {
return nil, fmt.Errorf("Failed to get netdev for %q bridge: %s", bridgeName, err)
}
if len(netdevs) == 0 {
return nil, fmt.Errorf("Could not find %q bridge", bridgeName)
}
if len(netdevs[0].CIDRs) == 0 {
return nil, fmt.Errorf("Bridge %q has no IP addresses", bridgeName)
}
for _, cidr := range netdevs[0].CIDRs {
if subnet.Contains(cidr.IP) {
return cidr.IP, nil
}
}
// None in the required subnet; just return the first one
return netdevs[0].CIDRs[0].IP, nil
}
func (c *CNIPlugin) CmdDel(args *skel.CmdArgs) error {
conf, err := loadNetConf(args.StdinData)
if err != nil {
return err
}
ns, err := netns.GetFromPath(args.Netns)
if err != nil {
return err
}
defer ns.Close()
err = weavenet.WithNetNS(ns, func() error {
link, err := netlink.LinkByName(args.IfName)
if err != nil {
return err
}
return netlink.LinkDel(link)
})
if err != nil {
return fmt.Errorf("error removing interface: %s", err)
}
// Default IPAM is Weave's own
if conf.IPAM.Type == "" {
err = ipamplugin.NewIpam(c.weave).Release(args)
} else {
err = ipam.ExecDel(conf.IPAM.Type, args.StdinData)
}
if err != nil {
return fmt.Errorf("unable to release IP address: %s", err)
}
return nil
}
type NetConf struct {
types.NetConf
BrName string `json:"bridge"`
IsGW bool `json:"isGateway"`
IPMasq bool `json:"ipMasq"`
MTU int `json:"mtu"`
}