// ensureInterHostRoutes ensures we have routes to every other host. func (h Helper) ensureInterHostRoutes() error { log.Trace(trace.Inside, "Acquiring mutex ensureInterhostRoutes") h.ensureInterHostRoutesMutex.Lock() defer func() { log.Trace(trace.Inside, "Releasing mutex ensureInterhostRoutes") h.ensureInterHostRoutesMutex.Unlock() }() log.Trace(trace.Inside, "Acquired mutex ensureInterhostRoutes") via := "via" log.Tracef(trace.Inside, "In ensureInterHostRoutes over %v\n", h.Agent.networkConfig.otherHosts) for _, host := range h.Agent.networkConfig.otherHosts { log.Tracef(trace.Inside, "In ensureInterHostRoutes ensuring route for %v\n", host) _, romanaCidr, err := net.ParseCIDR(host.RomanaIp) if err != nil { return failedToParseOtherHosts(host.RomanaIp) } romanaMaskInt, _ := romanaCidr.Mask.Size() romanaMask := fmt.Sprintf("%d", romanaMaskInt) dest := host.Ip // wait until no one messing with routes // If route doesn't exist yet if err := h.isRouteExist(romanaCidr.IP, romanaMask); err != nil { // Create it err2 := h.createRoute(romanaCidr.IP, romanaMask, via, dest) if err2 != nil { return routeCreateError(err, romanaCidr.IP.String(), romanaMask, dest) } } } return nil }
// rootState is a state at the beginning of the input and outside of any other state. func rootState(l *Lexer) stateFn { log.Trace(trace.Private, "In root state") for { b := l.nextByte() // There are 5 states we can go from root. switch string(b) { case string(endOfText): return l.errorEof("EOF reached in root section") case "#": log.Trace(trace.Inside, "In root state, switching into the comment state") return stateInComment case "*": log.Trace(trace.Inside, "In root state, switching into the table state") return stateInTable case ":": log.Trace(trace.Inside, "In root state, switching into the chain state") return stateInChain case "-": // Checking one byte ahead of reader to detect "-A" if l.accept("A ") { log.Trace(trace.Inside, "In root state, switching into the rule state") return stateInRule } case "C": // Whenever we arrive at "C" we need to check if it is a "COMMIT" token. if l.accept("OMMIT\n") { l.items <- Item{Type: itemCommit, Body: "COMMIT"} return rootState } } } }
// lastTable returns pointer to the last IPtable in IPtables. func (i *IPtables) lastTable() *IPtable { log.Trace(trace.Private, "In lastTable()") if len(i.Tables) == 0 { return nil } t := i.Tables[len(i.Tables)-1] log.Trace(trace.Inside, "In lastTable returning with ", t.Name) return t }
// SetConfig implements SetConfig function of the Service interface. func (a *Agent) SetConfig(config common.ServiceConfig) error { log.Trace(trace.Public, config) a.config = config leaseFileName := config.ServiceSpecific["lease_file"].(string) lf := NewLeaseFile(leaseFileName, a) a.leaseFile = &lf a.waitForIfaceTry = int(config.ServiceSpecific["wait_for_iface_try"].(float64)) a.networkConfig = &NetworkConfig{} a.store = *NewStore(config) log.Trace(trace.Inside, "Agent.SetConfig() finished.") return nil }
// podDownHandler cleans up after pod deleted. func (a *Agent) podDownHandler(input interface{}, ctx common.RestContext) (interface{}, error) { log.Trace(trace.Private, "Agent: Entering podDownHandler()") netReq := input.(*NetworkRequest) netif := netReq.NetIf // We need new firewall instance here to use its Cleanup() // to uninstall firewall rules related to the endpoint. fw, err := firewall.NewFirewall(a.getFirewallType()) if err != nil { return nil, err } err = fw.Init(a.Helper.Executor, a.store, a.networkConfig) if err != nil { return nil, err } err = fw.Cleanup(netif) if err != nil { return nil, err } // Spawn new thread to process the request log.Infof("Agent: Got request for pod teardown %v\n", netReq) return "OK", nil }
// NextItem returns next item from input stream. func (l *Lexer) NextItem() Item { log.Trace(trace.Public, "In NextItem()") for { select { case item := <-l.items: log.Trace(trace.Inside, "In NextItem() returning item ", item) return item default: if l.state == nil { panic("Lexer failed to process input stream") } log.Trace(trace.Inside, "In NextItem(), next state") l.state = l.state(l) } } }
func (agentStore *agentStore) listRoutes() ([]Route, error) { log.Trace(trace.Inside, "Acquiring store mutex for listRoutes") agentStore.mu.Lock() defer func() { log.Trace(trace.Inside, "Releasing store mutex for listRoutes") agentStore.mu.Unlock() }() log.Trace(trace.Inside, "Acquired store mutex for listRoutes") var routes []Route agentStore.DbStore.Db.Find(&routes) err := common.MakeMultiError(agentStore.DbStore.Db.GetErrors()) if err != nil { return nil, err } return routes, nil }
// SetConfig implements SetConfig function of the Service interface. func (l *KubeListener) SetConfig(config common.ServiceConfig) error { confString := "/etc/romana/romana.conf.yml:kubernetesListener:config:" log.Trace(trace.Inside, confString, config) m := config.ServiceSpecific if kl, ok := m["kubernetes_url"]; !ok || kl == "" { return fmt.Errorf("%s%s", confString, "kubernetes_url required in config.") } l.kubeURL = m["kubernetes_url"].(string) if nnp, ok := m["namespace_notification_path"]; !ok || nnp == "" { return fmt.Errorf("%s%s", confString, "namespace_notification_path required in config.") } l.namespaceNotificationPath = m["namespace_notification_path"].(string) if pnppre, ok := m["policy_notification_path_prefix"]; !ok || pnppre == "" { return fmt.Errorf("%s%s", confString, "policy_notification_path_prefix required in config.") } l.policyNotificationPathPrefix = m["policy_notification_path_prefix"].(string) if pnppost, ok := m["policy_notification_path_prefix"]; !ok || pnppost == "" { return fmt.Errorf("%s%s", confString, "policy_notification_path_postfix required in config.") } l.policyNotificationPathPostfix = m["policy_notification_path_postfix"].(string) if sln, ok := m["segment_label_name"]; !ok || sln == "" { return fmt.Errorf("%s%s", confString, "segment_label_name required in config.") } l.segmentLabelName = m["segment_label_name"].(string) if tln, ok := m["tenant_label_name"]; !ok || tln == "" { return fmt.Errorf("%s%s", confString, "tenant_label_name required in config.") } l.tenantLabelName = m["tenant_label_name"].(string) l.namespaceBufferSize = 1000 if kc, ok := m["kubernetes_config"]; !ok || kc == "" { // Default kubernetes config location on ubuntu // TODO: this should not be hard coded, other // distributions may have other user names. m["kubernetes_config"] = "/home/ubuntu/.kube/config" } // TODO, this loads kubernetes config from flags provided in main // should be loading from path provided by romana-root. Stas. kubeClientConfig, err := clientcmd.BuildConfigFromFlags("", m["kubernetes_config"].(string)) if err != nil { return errors.New(fmt.Sprintf("Failed to load kubernetes kubeClientConfig %s", err)) } clientset, err := kubernetes.NewForConfig(kubeClientConfig) if err != nil { return fmt.Errorf("Failed to make kubernetes client %s", err) } l.kubeClient = clientset return nil }
// lastChain returns pointer to the last IPchain in IPtable. func (i *IPtable) lastChain() *IPchain { log.Trace(trace.Private, "In lastChain()") if len(i.Chains) == 0 { return nil } c := i.Chains[len(i.Chains)-1] return c }
// stateInComment consumes entire line. func stateInComment(l *Lexer) stateFn { log.Trace(trace.Private, "In comment state") item := Item{Type: itemComment} for { b := l.nextByte() c := string(b) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in comment section") case "\n": l.items <- item log.Trace(trace.Inside, "In comment state, switching into the root state") return rootState default: item.Body += c } } }
// createRoute creates IP route, returns nil if success and error otherwise. func (h Helper) createRoute(ip net.IP, netmask string, via string, dest string, extraArgs ...string) error { log.Trace(trace.Private, "Helper: creating route") cmd := "/sbin/ip" targetIP := fmt.Sprintf("%s/%v", ip, netmask) args := []string{"ro", "add", targetIP, via, dest} args = append(args, extraArgs...) if _, err := h.Executor.Exec(cmd, args); err != nil { return shelloutError(err, cmd, args) } return nil // success }
// getFirewallType converts configuration option firewall_provider into // firewall.Provider type. func (a Agent) getFirewallType() firewall.Provider { provider, ok := a.config.ServiceSpecific["firewall_provider"].(string) if !ok { panic("Unable to read firewall_provider from config") } // Value of "shellex" stands for firewall provider that executes iptables // commands line by line and value of "save-restore" stands for // firewall provider that uses iptables-save/iptables-restore. switch provider { case "shellex": log.Trace(trace.Inside, "Agent: using ShellexProvider firewall provider") return firewall.ShellexProvider case "save-restore": log.Trace(trace.Inside, "Agent: using IPTsaveProvider firewall provider") return firewall.IPTsaveProvider default: panic(fmt.Sprintf("Unsupported firewall type value %s, supported values are 'shellex' and 'save-restore'", provider)) } }
func (agentStore *agentStore) deleteRoute(route *Route) error { log.Trace(trace.Inside, "Acquiring store mutex for deleteRoute") agentStore.mu.Lock() defer func() { log.Trace(trace.Inside, "Releasing store mutex for deleteRoute") agentStore.mu.Unlock() }() log.Trace(trace.Inside, "Acquired store mutex for deleteRoute") db := agentStore.DbStore.Db agentStore.DbStore.Db.Delete(route) err := common.MakeMultiError(db.GetErrors()) if err != nil { return err } if db.Error != nil { return db.Error } return nil }
// ChainByName looks for IPchain with corresponding name and returns a pointer to it. func (i *IPtable) ChainByName(name string) *IPchain { log.Trace(trace.Private, "In ChainByName()") for n, c := range i.Chains { if c.Name == name { ret := i.Chains[n] return ret } } return nil }
func (agentStore *agentStore) findRouteByIface(routeIface string) (*Route, error) { log.Trace(trace.Inside, "Acquiring store mutex for findRoute") agentStore.mu.Lock() defer func() { log.Trace(trace.Inside, "Releasing store mutex for findRoute") agentStore.mu.Unlock() }() log.Trace(trace.Inside, "Acquired store mutex for findRoute") var route Route db := agentStore.DbStore.Db agentStore.DbStore.Db.Where("ip = ?", routeIface).First(&route) err := common.MakeMultiError(db.GetErrors()) if err != nil { return nil, err } if db.Error != nil { return nil, db.Error } return &route, nil }
// ensureLine ensures that line is present in a file. func (h Helper) ensureLine(path string, token string, op leaseOp) error { // if file exist if err := h.OS.CreateIfMissing(path); err != nil { return ensureLineError(err) } // wait until no one using the file log.Trace(trace.Inside, "Acquiring mutex ensureLine") h.ensureLineMutex.Lock() defer func() { log.Trace(trace.Inside, "Releasing mutex ensureLine") h.ensureLineMutex.Unlock() }() log.Trace(trace.Inside, "Acquired mutex ensureLine") lineInFile, err := h.isLineInFile(path, token) if err != nil { return ensureLineError(err) } switch op { case leaseAdd: if !lineInFile { if err := h.appendLineToFile(path, token); err != nil { return ensureLineError(err) } } else { // nothing to do } case leaseRemove: if lineInFile { if err := h.removeLineFromFile(path, token); err != nil { return ensureLineError(err) } } else { // nothing to do } } return nil }
// ensureRouteToEndpoint verifies that ip route to endpoint interface exists, creates it otherwise. // Error if failed, nil if success. func (h Helper) ensureRouteToEndpoint(netif *NetIf) error { mask := fmt.Sprintf("%d", h.Agent.networkConfig.EndpointNetmaskSize()) log.Trace(trace.Private, "Ensuring routes for ", netif.IP, " ", netif.Name) log.Trace(trace.Inside, "Acquiring mutex ensureRouteToEndpoint") h.ensureRouteToEndpointMutex.Lock() defer func() { log.Trace(trace.Inside, "Releasing mutex ensureRouteToEndpoint") h.ensureRouteToEndpointMutex.Unlock() }() log.Trace(trace.Inside, "Acquired mutex ensureRouteToEndpoint") // If route not exist if err := h.isRouteExist(netif.IP.IP, mask); err != nil { // Create route via := "dev" dest := netif.Name err := h.createRoute(netif.IP.IP, mask, via, dest, "src", h.Agent.networkConfig.romanaGW.String()) if err != nil { return netIfRouteCreateError(err, *netif) } } return nil }
// podUpHandler handles HTTP requests for endpoints provisioning. func (a *Agent) podUpHandler(input interface{}, ctx common.RestContext) (interface{}, error) { log.Trace(trace.Private, "Agent: Entering podUpHandler()") netReq := input.(*NetworkRequest) log.Infof("Agent: Got request for network configuration: %v\n", netReq) // Spawn new thread to process the request // TODO don't know if fork-bombs are possible in go but if they are this // need to be refactored as buffered channel with fixed pool of workers go a.podUpHandlerAsync(*netReq) // TODO I wonder if this should actually return something like a // link to a status of this request which will later get updated // with success or failure -- Greg. return "OK", nil }
// stateInChainPolicy consumes chain deafult policy if any. func stateInChainPolicy(l *Lexer) stateFn { log.Trace(trace.Private, "In chain policy state") item := Item{Type: itemChainPolicy} b := l.nextByte() c := string(b) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in chain section") case "-": item.Body = "-" l.items <- item _ = l.nextByte() // Discard next space to prevent it from getting captured as counter return stateInChainCounter case "A": if l.accept("CCEPT ") { item.Body = "ACCEPT" l.items <- item return stateInChainCounter } else { l.items <- Item{Type: ItemError, Body: "Unexpected deafult policy for a chain"} return nil } case "R": if l.accept("ETURN ") { item.Body = "RETURN" l.items <- item return stateInChainCounter } else { l.items <- Item{Type: ItemError, Body: "Unexpected deafult policy for a chain"} return nil } case "D": if l.accept("ROP ") { item.Body = "DROP" l.items <- item return stateInChainCounter } else { l.items <- Item{Type: ItemError, Body: "Unexpected deafult policy for a chain"} return nil } default: return l.errorf("Unexpectend end of line in chain state") } }
// waitForIface waits for network interface to become available in the system. func (h Helper) waitForIface(expectedIface string) bool { for i := 0; i <= h.Agent.waitForIfaceTry; i++ { log.Tracef(trace.Inside, "Helper: Waiting for interface %s, %d attempt", expectedIface, i) ifaceList, err := net.Interfaces() log.Trace(trace.Inside, "Agent: Entering podUpHandlerAsync()") if err != nil { log.Warn("Warning: Helper: failed to read net.Interfaces()") } for iface := range ifaceList { if ifaceList[iface].Name == expectedIface { return true } } time.Sleep(10 * time.Second) } return false }
func stateInChainCounter(l *Lexer) stateFn { log.Trace(trace.Private, "In chain counter state") item := Item{Type: itemChainCounter} for { b := l.nextByte() c := string(b) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in chain counter section") case "\n": l.items <- item return rootState default: item.Body += c } } }
func stateInRule(l *Lexer) stateFn { log.Trace(trace.Private, "In rule state") item := Item{Type: itemRule} for { b := l.nextByte() c := string(b) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in rule section") case " ": l.items <- item return stateRuleMatch default: item.Body += c } } }
// stateInChain consumes chain name and checks for default policy token. func stateInChain(l *Lexer) stateFn { log.Trace(trace.Private, "In chain state") item := Item{Type: itemChain} for { b := l.nextByte() c := string(b) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in chain section") case " ": l.items <- item return stateInChainPolicy case "\n": return l.errorf("Unexpectend end of line in chain state") default: item.Body += c } } }
// statusHandler reports operational statistics. func (a *Agent) statusHandler(input interface{}, ctx common.RestContext) (interface{}, error) { log.Trace(trace.Private, "Agent: Entering statusHandler()") fw, err := firewall.NewFirewall(a.getFirewallType()) if err != nil { return nil, err } err = fw.Init(a.Helper.Executor, a.store, a.networkConfig) if err != nil { return nil, err } rules, err := fw.ListRules() if err != nil { return nil, err } ifaces, err := a.store.listNetIfs() if err != nil { return nil, err } status := Status{Rules: rules, Interfaces: ifaces} return status, nil }
// Initialize implements the Initialize method of common.Service // interface. func (a *Agent) Initialize(client *common.RestClient) error { log.Trace(trace.Public, "Entering Agent.Initialize()") err := a.store.Connect() if err != nil { log.Error("Agent.Initialize() : Failed to connect to database.") return err } log.Info("Agent: Attempting to identify current host.") if err := a.identifyCurrentHost(); err != nil { log.Error("Agent: ", agentError(err)) return agentError(err) } a.client = client // Ensure we have all the routes to our neighbours log.Info("Agent: ensuring interhost routes exist") if err := a.Helper.ensureInterHostRoutes(); err != nil { log.Error("Agent: ", agentError(err)) return agentError(err) } return nil }
func stateInRuleAction(l *Lexer) stateFn { log.Trace(trace.Private, "In rule action state") item := Item{Type: itemAction} for { b := l.nextByte() c := string(b) switch c { case string(endOfText): // Rule action is one of the places where // we can accept EOF. This is only legal when // single rule is being parsed. l.items <- item return l.errorEof("EOF reached in rule action section") case "\n": l.items <- item return rootState default: item.Body += c } } }
// vmUpHandlerAsync does a number of operations on given endpoint to ensure // it's connected: // 1. Ensures interface is ready // 2. Checks if DHCP is running // 3. Creates ip route pointing new interface // 4. Provisions static DHCP lease for new interface // 5. Provisions firewall rules func (a *Agent) vmUpHandlerAsync(netif NetIf) error { log.Trace(trace.Private, "Agent: Entering interfaceHandle()") currentProvider := a.getFirewallType() if !a.Helper.waitForIface(netif.Name) { // TODO should we resubmit failed interface in queue for later // retry ? ... considering oenstack will give up as well after // timeout return agentErrorString(fmt.Sprintf("Requested interface not available in time - %s", netif.Name)) } // dhcpPid is only needed here for fail fast check // will try to poll the pid again in provisionLease log.Trace(trace.Inside, "Agent: Checking if DHCP is running") _, err := a.Helper.DhcpPid() if err != nil { log.Error(agentError(err)) return agentError(err) } err = a.store.addNetIf(&netif) if err != nil { return agentError(err) } log.Infof("Agent: Creating endpoint routes - %s", netif.Name) if err := a.Helper.ensureRouteToEndpoint(&netif); err != nil { log.Error(agentError(err)) return agentError(err) } log.Infof("Agent: Provisioning DHCP - %s", netif.Name) if err := a.leaseFile.provisionLease(&netif, leaseAdd); err != nil { log.Error(agentError(err)) return agentError(err) } log.Infof("Agent: Provisioning firewall - %s", netif.Name) fw, err := firewall.NewFirewall(currentProvider) if err != nil { return err } err = fw.Init(a.Helper.Executor, a.store, a.networkConfig) if err != nil { log.Error(agentError(err)) return agentError(err) } if err1 := fw.SetEndpoint(netif); err1 != nil { log.Error(agentError(err1)) return agentError(err1) } var rules RuleSet switch currentProvider { case firewall.ShellexProvider: rules = OpenStackShellRules case firewall.IPTsaveProvider: rules = OpenStackSaveRestoreRules default: err := fmt.Errorf("Unkown firewall provider in vmUpHandler") log.Error(agentError(err)) return agentError(err) } if err := prepareFirewallRules(fw, a.networkConfig, rules, currentProvider); err != nil { log.Error(agentError(err)) return agentError(err) } if err := fw.ProvisionEndpoint(); err != nil { log.Error(agentError(err)) return agentError(err) } log.Trace(trace.Inside, "All good", netif) return nil }
// podUpHandlerAsync does a number of operations on given endpoint to ensure // it's connected: // 1. Ensures interface is ready // 2. Creates ip route pointing new interface // 3. Provisions firewall rules func (a *Agent) podUpHandlerAsync(netReq NetworkRequest) error { log.Trace(trace.Private, "Agent: Entering podUpHandlerAsync()") currentProvider := a.getFirewallType() netif := netReq.NetIf if netif.Name == "" { return agentErrorString("Agent: Interface name required") } if !a.Helper.waitForIface(netif.Name) { // TODO should we resubmit failed interface in queue for later // retry ? ... considering openstack will give up as well after // timeout msg := fmt.Sprintf("Requested interface not available in time - %s", netif.Name) log.Warn("Agent: ", msg) return agentErrorString(msg) } log.Infof("Agent: Creating endpoint routes - %s", netif.Name) if err := a.Helper.ensureRouteToEndpoint(&netif); err != nil { log.Error(agentError(err)) return agentError(err) } log.Infof("Agent: Provisioning firewall - %s", netif.Name) fw, err := firewall.NewFirewall(currentProvider) if err != nil { return err } err = fw.Init(a.Helper.Executor, a.store, a.networkConfig) if err != nil { return err } if err1 := fw.SetEndpoint(netif); err1 != nil { log.Error(agentError(err)) return agentError(err) } var rules RuleSet switch currentProvider { case firewall.ShellexProvider: rules = KubeShellXRules case firewall.IPTsaveProvider: rules = KubeSaveRestoreRules default: err := fmt.Errorf("Unkown firewall provider in podUpHandler") log.Error(agentError(err)) return agentError(err) } if err := prepareFirewallRules(fw, a.networkConfig, rules, currentProvider); err != nil { log.Error(agentError(err)) return agentError(err) } if err := fw.ProvisionEndpoint(); err != nil { log.Error(agentError(err)) return agentError(err) } log.Trace(trace.Inside, "Agent: All good", netif) return nil }
func stateRuleMatch(l *Lexer) stateFn { log.Trace(trace.Private, "In rule match state") var exMarkConsumed, matchLiteralConsumed bool item := Item{Type: itemRuleMatch} for { b := l.nextByte() c := string(b) log.Trace(trace.Inside, "In rule match with char ", c) switch c { case string(endOfText): return l.errorf("Error: unexpected EOF in rule section") case "!": // exclamation mark can appear only once per match // and only before match literal if exMarkConsumed == true { return l.errorf("Unexpected ! in rules spec") } if matchLiteralConsumed { l.items <- item // put current '!' back into stream for the next iteration // can not fail _ = l.input.UnreadByte() return stateRuleMatch } exMarkConsumed = true item.Body += c case "-": // '-' dash can appear in 4 cases // in module literal like '-p' or '-m' // in module opts like '--dport' // in inside opts literal like '--to-destination' or inside body '-j MY-CHAIN' // in action '-j' onLiteral := false if l.expect("p ") || l.expect("m ") || l.expect("i ") || l.expect("o ") || l.expect("s ") || l.expect("d ") { // Single dash, single char and a space indicate module literal. onLiteral = true } else if l.expect("-") { // double dash indicate module opts // nothing to do, just let it be consumed } else if l.accept("j ") { l.items <- item return stateInRuleAction } // any other dash is inside a body if onLiteral { if matchLiteralConsumed { l.items <- item // put current '-' back into stream for the next iteration // can not fail _ = l.input.UnreadByte() return stateRuleMatch } else { matchLiteralConsumed = true } } item.Body += c default: item.Body += c } } }
// identifyCurrentHost discovers network configuration // of the host we are running on. // We need to know public IP and Romana gateway IP of the current host. // This is done by matching current host IP addresses against what topology // service thinks the host address is. // If no match is found we assume we are running on host which is not // part of the Romana setup and spit error out. func (a Agent) identifyCurrentHost() error { client, err := common.NewRestClient(common.GetRestClientConfig(a.config)) if err != nil { return agentError(err) } topologyURL, err := client.GetServiceUrl("topology") if err != nil { return agentError(err) } index := common.IndexResponse{} err = client.Get(topologyURL, &index) if err != nil { return agentError(err) } dcURL := index.Links.FindByRel("datacenter") a.networkConfig.dc = common.Datacenter{} err = client.Get(dcURL, &a.networkConfig.dc) if err != nil { return agentError(err) } hostURL := index.Links.FindByRel("host-list") hosts := []common.Host{} err = client.Get(hostURL, &hosts) if err != nil { return agentError(err) } log.Trace(trace.Inside, "Retrieved hosts list, found", len(hosts), "hosts") addrs, err := net.InterfaceAddrs() if err != nil { return err } log.Tracef(trace.Inside, "Searching %d interfaces for a matching host configuration: %v", len(addrs), addrs) // Find an interface that matches a Romana CIDR // and store that interface's IP address. // It will be used when configuring iptables and routes to tap interfaces. for i, host := range hosts { _, romanaCIDR, err := net.ParseCIDR(host.RomanaIp) if err != nil { log.Tracef(trace.Inside, "Unable to parse '%s' (%s). Skipping.", host.RomanaIp, err) continue } for _, addr := range addrs { ipnet, ok := addr.(*net.IPNet) if !ok { continue } if romanaCIDR.Contains(ipnet.IP) { // Check that it's the same subnet size s1, _ := romanaCIDR.Mask.Size() s2, _ := ipnet.Mask.Size() if s1 != s2 { continue } // OK, we're happy with this result a.networkConfig.romanaGW = ipnet.IP a.networkConfig.romanaGWMask = ipnet.Mask // Retain the other hosts that were listed. // This will be used for creating inter-host routes. a.networkConfig.otherHosts = append(a.networkConfig.otherHosts, hosts[0:i]...) a.networkConfig.otherHosts = append(a.networkConfig.otherHosts, hosts[i+1:]...) log.Trace(trace.Inside, "Found match for CIDR", romanaCIDR, "using address", ipnet.IP) return nil } } } return agentErrorString("Unable to find interface matching any Romana CIDR") }