Skip to content

Commit

Permalink
Merge pull request #1538 from ttys3/multi-ip-match
Browse files Browse the repository at this point in the history
feat: add support for --iface-regex match interface with multi IP addresses
  • Loading branch information
manuelbuil authored Feb 28, 2022
2 parents 4584509 + 7caa6ac commit 5e948c4
Show file tree
Hide file tree
Showing 6 changed files with 482 additions and 283 deletions.
280 changes: 17 additions & 263 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
Expand All @@ -33,6 +32,7 @@ import (
"github.com/coreos/pkg/flagutil"
"github.com/flannel-io/flannel/network"
"github.com/flannel-io/flannel/pkg/ip"
"github.com/flannel-io/flannel/pkg/ipmatch"
"github.com/flannel-io/flannel/subnet"
"github.com/flannel-io/flannel/subnet/etcdv2"
"github.com/flannel-io/flannel/subnet/kube"
Expand Down Expand Up @@ -105,13 +105,6 @@ var (
flannelFlags = flag.NewFlagSet("flannel", flag.ExitOnError)
)

const (
ipv4Stack int = iota
ipv6Stack
dualStack
noneStack
)

func init() {
flannelFlags.StringVar(&opts.etcdEndpoints, "etcd-endpoints", "http://127.0.0.1:4001,http://127.0.0.1:2379", "a comma-delimited list of etcd endpoints")
flannelFlags.StringVar(&opts.etcdPrefix, "etcd-prefix", "/coreos.com/network", "etcd prefix")
Expand Down Expand Up @@ -145,11 +138,10 @@ func init() {
// can flow into journald (if running under systemd)
err := flag.Set("logtostderr", "true")
if err != nil {
log.Error("Can't set the logtostderr flag", err)
os.Exit(1)
log.Error("Can't set the logtostderr flag", err)
os.Exit(1)
}


// Only copy the non file logging options from klog
copyFlag("v")
copyFlag("vmodule")
Expand All @@ -160,11 +152,10 @@ func init() {

// now parse command line args
err = flannelFlags.Parse(os.Args[1:])
if err != nil {
log.Error("Can't parse flannel flags", err)
os.Exit(1)
}

if err != nil {
log.Error("Can't parse flannel flags", err)
os.Exit(1)
}
}

func copyFlag(name string) {
Expand All @@ -177,17 +168,6 @@ func usage() {
os.Exit(0)
}

func getIPFamily(autoDetectIPv4, autoDetectIPv6 bool) (int, error) {
if autoDetectIPv4 && !autoDetectIPv6 {
return ipv4Stack, nil
} else if !autoDetectIPv4 && autoDetectIPv6 {
return ipv6Stack, nil
} else if autoDetectIPv4 && autoDetectIPv6 {
return dualStack, nil
}
return noneStack, errors.New("none defined stack")
}

func newSubnetManager(ctx context.Context) (subnet.Manager, error) {
if opts.kubeSubnetMgr {
return kube.NewSubnetManager(ctx, opts.kubeApiUrl, opts.kubeConfigFile, opts.kubeAnnotationPrefix, opts.netConfPath, opts.setNodeNetworkUnavailable)
Expand Down Expand Up @@ -269,24 +249,29 @@ func main() {
}

// Get ip family stack
ipStack, stackErr := getIPFamily(config.EnableIPv4, config.EnableIPv6)
ipStack, stackErr := ipmatch.GetIPFamily(config.EnableIPv4, config.EnableIPv6)
if stackErr != nil {
log.Error(stackErr.Error())
os.Exit(1)
}

// Work out which interface to use
var extIface *backend.ExternalInterface
optsPublicIP := ipmatch.PublicIPOpts{
PublicIP: opts.publicIP,
PublicIPv6: opts.publicIPv6,
}
// Check the default interface only if no interfaces are specified
if len(opts.iface) == 0 && len(opts.ifaceRegex) == 0 {
extIface, err = LookupExtIface(opts.publicIP, "", ipStack)
extIface, err = ipmatch.LookupExtIface(opts.publicIP, "", ipStack, optsPublicIP)
if err != nil {
log.Error("Failed to find any valid interface to use: ", err)
os.Exit(1)
}
} else {
// Check explicitly specified interfaces
for _, iface := range opts.iface {
extIface, err = LookupExtIface(iface, "", ipStack)
extIface, err = ipmatch.LookupExtIface(iface, "", ipStack, optsPublicIP)
if err != nil {
log.Infof("Could not find valid interface matching %s: %s", iface, err)
}
Expand All @@ -299,7 +284,7 @@ func main() {
// Check interfaces that match any specified regexes
if extIface == nil {
for _, ifaceRegex := range opts.ifaceRegex {
extIface, err = LookupExtIface("", ifaceRegex, ipStack)
extIface, err = ipmatch.LookupExtIface("", ifaceRegex, ipStack, optsPublicIP)
if err != nil {
log.Infof("Could not find valid interface matching %s: %s", ifaceRegex, err)
}
Expand Down Expand Up @@ -524,237 +509,6 @@ func MonitorLease(ctx context.Context, sm subnet.Manager, bn backend.Network, wg
}
}

func LookupExtIface(ifname string, ifregexS string, ipStack int) (*backend.ExternalInterface, error) {
var iface *net.Interface
var ifaceAddr net.IP
var ifaceV6Addr net.IP
var err error
var ifregex *regexp.Regexp

if ifregexS != "" {
ifregex, err = regexp.Compile(ifregexS)
if err != nil {
return nil, fmt.Errorf("could not compile the IP address regex '%s': %w", ifregexS, err)
}
}

// Check ip family stack
if ipStack == noneStack {
return nil, fmt.Errorf("none matched ip stack")
}

if len(ifname) > 0 {
if ifaceAddr = net.ParseIP(ifname); ifaceAddr != nil {
log.Infof("Searching for interface using %s", ifaceAddr)
switch ipStack {
case ipv4Stack:
iface, err = ip.GetInterfaceByIP(ifaceAddr)
if err != nil {
return nil, fmt.Errorf("error looking up interface %s: %s", ifname, err)
}
case ipv6Stack:
iface, err = ip.GetInterfaceByIP6(ifaceAddr)
if err != nil {
return nil, fmt.Errorf("error looking up v6 interface %s: %s", ifname, err)
}
case dualStack:
iface, err = ip.GetInterfaceByIP(ifaceAddr)
if err != nil {
return nil, fmt.Errorf("error looking up interface %s: %s", ifname, err)
}
v6Iface, err := ip.GetInterfaceByIP6(ifaceAddr)
if err != nil {
return nil, fmt.Errorf("error looking up v6 interface %s: %s", ifname, err)
}
if iface.Name != v6Iface.Name {
return nil, fmt.Errorf("v6 interface %s must be the same with v4 interface %s", v6Iface.Name, iface.Name)
}
}
} else {
iface, err = net.InterfaceByName(ifname)
if err != nil {
return nil, fmt.Errorf("error looking up interface %s: %s", ifname, err)
}
}
} else if ifregex != nil {
// Use the regex if specified and the iface option for matching a specific ip or name is not used
ifaces, err := net.Interfaces()
if err != nil {
return nil, fmt.Errorf("error listing all interfaces: %s", err)
}

ifaceLoop:
// Check IP
for _, ifaceToMatch := range ifaces {
switch ipStack {
case ipv4Stack:
ifaceIP, err := ip.GetInterfaceIP4Addr(&ifaceToMatch)
if err != nil {
// Skip if there is no IPv4 address
continue
}

if ifregex.MatchString(ifaceIP.String()) {
ifaceAddr = ifaceIP
iface = &ifaceToMatch
break ifaceLoop
}
case ipv6Stack:
ifaceIP, err := ip.GetInterfaceIP6Addr(&ifaceToMatch)
if err != nil {
// Skip if there is no IPv6 address
continue
}

if ifregex.MatchString(ifaceIP.String()) {
ifaceV6Addr = ifaceIP
iface = &ifaceToMatch
break ifaceLoop
}
case dualStack:
ifaceIP, err := ip.GetInterfaceIP4Addr(&ifaceToMatch)
if err != nil {
// Skip if there is no IPv4 address
continue
}

ifaceV6IP, err := ip.GetInterfaceIP6Addr(&ifaceToMatch)
if err != nil {
// Skip if there is no IPv6 address
continue
}

if ifregex.MatchString(ifaceIP.String()) && ifregex.MatchString(ifaceV6IP.String()) {
ifaceAddr = ifaceIP
ifaceV6Addr = ifaceV6IP
iface = &ifaceToMatch
break ifaceLoop
}
}
}

// Check Name
if iface == nil && (ifaceAddr == nil || ifaceV6Addr == nil) {
for _, ifaceToMatch := range ifaces {
if ifregex.MatchString(ifaceToMatch.Name) {
iface = &ifaceToMatch
break
}
}
}

// Check that nothing was matched
if iface == nil {
var availableFaces []string
for _, f := range ifaces {
var ipaddr net.IP
switch ipStack {
case ipv4Stack, dualStack:
ipaddr, _ = ip.GetInterfaceIP4Addr(&f) // We can safely ignore errors. We just won't log any ip
case ipv6Stack:
ipaddr, _ = ip.GetInterfaceIP6Addr(&f) // We can safely ignore errors. We just won't log any ip
}
availableFaces = append(availableFaces, fmt.Sprintf("%s:%s", f.Name, ipaddr))
}

return nil, fmt.Errorf("Could not match pattern %s to any of the available network interfaces (%s)", ifregexS, strings.Join(availableFaces, ", "))
}
} else {
log.Info("Determining IP address of default interface")
switch ipStack {
case ipv4Stack:
if iface, err = ip.GetDefaultGatewayInterface(); err != nil {
return nil, fmt.Errorf("failed to get default interface: %w", err)
}
case ipv6Stack:
if iface, err = ip.GetDefaultV6GatewayInterface(); err != nil {
return nil, fmt.Errorf("failed to get default v6 interface: %w", err)
}
case dualStack:
if iface, err = ip.GetDefaultGatewayInterface(); err != nil {
return nil, fmt.Errorf("failed to get default interface: %w", err)
}
v6Iface, err := ip.GetDefaultV6GatewayInterface()
if err != nil {
return nil, fmt.Errorf("failed to get default v6 interface: %w", err)
}
if iface.Name != v6Iface.Name {
return nil, fmt.Errorf("v6 default route interface %s "+
"must be the same with v4 default route interface %s", v6Iface.Name, iface.Name)
}
}
}

if ipStack == ipv4Stack && ifaceAddr == nil {
ifaceAddr, err = ip.GetInterfaceIP4Addr(iface)
if err != nil {
return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
}
} else if ipStack == ipv6Stack && ifaceV6Addr == nil {
ifaceV6Addr, err = ip.GetInterfaceIP6Addr(iface)
if err != nil {
return nil, fmt.Errorf("failed to find IPv6 address for interface %s", iface.Name)
}
} else if ipStack == dualStack && ifaceAddr == nil && ifaceV6Addr == nil {
ifaceAddr, err = ip.GetInterfaceIP4Addr(iface)
if err != nil {
return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
}
ifaceV6Addr, err = ip.GetInterfaceIP6Addr(iface)
if err != nil {
return nil, fmt.Errorf("failed to find IPv6 address for interface %s", iface.Name)
}
}

if ifaceAddr != nil {
log.Infof("Using interface with name %s and address %s", iface.Name, ifaceAddr)
}
if ifaceV6Addr != nil {
log.Infof("Using interface with name %s and v6 address %s", iface.Name, ifaceV6Addr)
}

if iface.MTU == 0 {
return nil, fmt.Errorf("failed to determine MTU for %s interface", ifaceAddr)
}

var extAddr net.IP
var extV6Addr net.IP

if len(opts.publicIP) > 0 {
extAddr = net.ParseIP(opts.publicIP)
if extAddr == nil {
return nil, fmt.Errorf("invalid public IP address: %s", opts.publicIP)
}
log.Infof("Using %s as external address", extAddr)
}

if extAddr == nil && ipStack != ipv6Stack {
log.Infof("Defaulting external address to interface address (%s)", ifaceAddr)
extAddr = ifaceAddr
}

if len(opts.publicIPv6) > 0 {
extV6Addr = net.ParseIP(opts.publicIPv6)
if extV6Addr == nil {
return nil, fmt.Errorf("invalid public IPv6 address: %s", opts.publicIPv6)
}
log.Infof("Using %s as external address", extV6Addr)
}

if extV6Addr == nil && ipStack != ipv4Stack {
log.Infof("Defaulting external v6 address to interface address (%s)", ifaceV6Addr)
extV6Addr = ifaceV6Addr
}

return &backend.ExternalInterface{
Iface: iface,
IfaceAddr: ifaceAddr,
IfaceV6Addr: ifaceV6Addr,
ExtAddr: extAddr,
ExtV6Addr: extV6Addr,
}, nil
}

func WriteSubnetFile(path string, config *subnet.Config, ipMasq bool, bn backend.Network) error {
dir, name := filepath.Split(path)
err := os.MkdirAll(dir, 0755)
Expand Down Expand Up @@ -793,7 +547,7 @@ func WriteSubnetFile(path string, config *subnet.Config, ipMasq bool, bn backend
// rename(2) the temporary file to the desired location so that it becomes
// atomically visible with the contents
return os.Rename(tempFile, path)
//TODO - is this safe? What if it's not on the same FS?
// TODO - is this safe? What if it's not on the same FS?
}

func mustRunHealthz(stopChan <-chan struct{}, wg *sync.WaitGroup) {
Expand Down
Loading

0 comments on commit 5e948c4

Please sign in to comment.