- Make them safe by using the safe version of WithNetNS. - Improve naming.
177 lines
4.3 KiB
Go
177 lines
4.3 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/vishvananda/netns"
|
|
|
|
weavenet "github.com/weaveworks/weave/net"
|
|
)
|
|
|
|
// Assert test is true, panic otherwise
|
|
func Assert(test bool) {
|
|
if !test {
|
|
panic("Assertion failure")
|
|
}
|
|
}
|
|
|
|
func ErrorMessages(errors []error) string {
|
|
var result []string
|
|
for _, err := range errors {
|
|
result = append(result, err.Error())
|
|
}
|
|
return strings.Join(result, "\n")
|
|
}
|
|
|
|
var netdevRegExp = regexp.MustCompile(`^([^ ]+?) ([^ ]+?) \[([^]]*)\]$`)
|
|
|
|
type NetDev struct {
|
|
Name string
|
|
MAC net.HardwareAddr
|
|
CIDRs []*net.IPNet
|
|
}
|
|
|
|
func (d NetDev) String() string {
|
|
return fmt.Sprintf("%s %s %s", d.Name, d.MAC, d.CIDRs)
|
|
}
|
|
|
|
func ParseNetDev(netdev string) (NetDev, error) {
|
|
match := netdevRegExp.FindStringSubmatch(netdev)
|
|
if match == nil {
|
|
return NetDev{}, fmt.Errorf("invalid netdev: %s", netdev)
|
|
}
|
|
|
|
iface := match[1]
|
|
mac, err := net.ParseMAC(match[2])
|
|
if err != nil {
|
|
return NetDev{}, fmt.Errorf("cannot parse mac %s: %s", match[2], err)
|
|
}
|
|
|
|
var cidrs []*net.IPNet
|
|
for _, cidr := range strings.Split(match[3], " ") {
|
|
if cidr != "" {
|
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
return NetDev{}, fmt.Errorf("cannot parse cidr %s: %s", cidr, err)
|
|
}
|
|
ipnet.IP = ip
|
|
cidrs = append(cidrs, ipnet)
|
|
}
|
|
}
|
|
|
|
return NetDev{Name: iface, MAC: mac, CIDRs: cidrs}, nil
|
|
}
|
|
|
|
func LinkToNetDev(link netlink.Link) (NetDev, error) {
|
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
|
if err != nil {
|
|
return NetDev{}, err
|
|
}
|
|
|
|
netDev := NetDev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
|
for _, addr := range addrs {
|
|
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
|
}
|
|
return netDev, nil
|
|
}
|
|
|
|
// ConnectedToBridgeVethPeerIds returns peer indexes of veth links connected to
|
|
// the given bridge. The peer index is used to query from a container netns
|
|
// whether the container is connected to the bridge.
|
|
func ConnectedToBridgeVethPeerIds(bridgeName string) ([]int, error) {
|
|
var ids []int
|
|
|
|
br, err := netlink.LinkByName(bridgeName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
links, err := netlink.LinkList()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, link := range links {
|
|
if _, isveth := link.(*netlink.Veth); isveth && link.Attrs().MasterIndex == br.Attrs().Index {
|
|
peerID := link.Attrs().ParentIndex
|
|
if peerID == 0 {
|
|
// perhaps running on an older kernel where ParentIndex doesn't work.
|
|
// as fall-back, assume the peers are consecutive
|
|
peerID = link.Attrs().Index - 1
|
|
}
|
|
ids = append(ids, peerID)
|
|
}
|
|
}
|
|
|
|
return ids, nil
|
|
}
|
|
|
|
// Lookup the weave interface of a container
|
|
func GetWeaveNetDevs(processID int) ([]NetDev, error) {
|
|
peerIDs, err := ConnectedToBridgeVethPeerIds("weave")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return GetNetDevsByVethPeerIds(processID, peerIDs)
|
|
}
|
|
|
|
func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]NetDev, error) {
|
|
// Bail out if this container is running in the root namespace
|
|
netnsRoot, err := netns.GetFromPid(1)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to open root namespace: %s", err)
|
|
}
|
|
defer netnsRoot.Close()
|
|
netnsContainer, err := netns.GetFromPid(processID)
|
|
if err != nil {
|
|
// Unable to find a namespace for this process - just return nothing
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("unable to open process %d namespace: %s", processID, err)
|
|
}
|
|
defer netnsContainer.Close()
|
|
if netnsRoot.Equal(netnsContainer) {
|
|
return nil, nil
|
|
}
|
|
|
|
var netdevs []NetDev
|
|
|
|
peersStr := make([]string, len(peerIDs))
|
|
for i, id := range peerIDs {
|
|
peersStr[i] = strconv.Itoa(id)
|
|
}
|
|
netdevsStr, err := weavenet.WithNetNSByPid(processID, "list-netdevs", strings.Join(peersStr, ","))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list-netdevs failed: %s", err)
|
|
}
|
|
for _, netdevStr := range strings.Split(netdevsStr, "\n") {
|
|
if netdevStr != "" {
|
|
netdev, err := ParseNetDev(netdevStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot parse netdev %s: %s", netdevStr, err)
|
|
}
|
|
netdevs = append(netdevs, netdev)
|
|
}
|
|
}
|
|
|
|
return netdevs, nil
|
|
}
|
|
|
|
// Get the weave bridge interface.
|
|
// NB: Should be called from the root network namespace.
|
|
func GetBridgeNetDev(bridgeName string) (NetDev, error) {
|
|
link, err := netlink.LinkByName(bridgeName)
|
|
if err != nil {
|
|
return NetDev{}, err
|
|
}
|
|
|
|
return LinkToNetDev(link)
|
|
}
|