// Scan implements driver.Scanner interface on IP func (i *IP) Scan(src interface{}) error { switch src := src.(type) { case string: ip := net.ParseIP(src) if ip == nil { return common.NewError("Cannot parse IP %s", src) } i.IP = ip return nil case []uint8: i.IP = net.ParseIP(string(src)) return nil default: return common.NewError("Incompatible type for IP") } }
// SetConfig implements SetConfig function of the Service interface. // Returns an error if cannot connect to the data store func (topology *TopologySvc) SetConfig(config common.ServiceConfig) error { topology.config = config dcMap := config.ServiceSpecific["datacenter"].(map[string]interface{}) dc := common.Datacenter{} dc.IpVersion = uint(dcMap["ip_version"].(float64)) if dc.IpVersion != 4 { return common.NewError("Only IPv4 is currently supported.") } dc.Cidr = dcMap["cidr"].(string) _, ipNet, err := net.ParseCIDR(dc.Cidr) if err != nil { return err } prefixBits, _ := ipNet.Mask.Size() dc.PrefixBits = uint(prefixBits) dc.PortBits = uint(dcMap["host_bits"].(float64)) dc.TenantBits = uint(dcMap["tenant_bits"].(float64)) dc.SegmentBits = uint(dcMap["segment_bits"].(float64)) dc.EndpointBits = uint(dcMap["endpoint_bits"].(float64)) dc.EndpointSpaceBits = uint(dcMap["endpoint_space_bits"].(float64)) if dc.EndpointBits == 0 { return common.NewError("Endpoint bits may not be 0") } bitSum := dc.PrefixBits + dc.PortBits + dc.TenantBits + dc.SegmentBits + dc.EndpointBits + dc.EndpointSpaceBits if bitSum != 32 { bitSumStr := fmt.Sprintf("%s+%d+%d+%d+%d+%d", dc.Cidr, dc.PortBits, dc.TenantBits, dc.SegmentBits, dc.EndpointBits, dc.EndpointSpaceBits) return common.NewError("Sum of prefix, port, tenant, segment, endpoint and endpoint space bits must be exactly 32, but it is %s=%d", bitSumStr, bitSum) } // TODO this should have worked but it doesn't... // err := mapstructure.Decode(dcMap, &dc) // if err != nil { // return err // } log.Printf("Datacenter information: was %s, decoded to %+v\n", dcMap, dc) topology.datacenter = &dc storeConfig := config.ServiceSpecific["store"].(map[string]interface{}) topology.store = topoStore{} topology.store.ServiceStore = &topology.store return topology.store.SetConfig(storeConfig) }
// translateTarget analizes kubePolicy and fills romanaPolicy.AppliedTo field. func (tg *TranslateGroup) translateTarget(translator *Translator) error { // Translate kubernetes namespace into romana tenant. Must be defined. tenantCacheEntry := translator.checkTenantInCache(tg.kubePolicy.ObjectMeta.Namespace) if tenantCacheEntry == nil { log.Errorf("Tenant not found when translating policy %v", tg.romanaPolicy) return TranslatorError{ErrorTenantNotInCache, nil} } // Empty PodSelector means policy applied to the entire namespace. if len(tg.kubePolicy.Spec.PodSelector.MatchLabels) == 0 { tg.romanaPolicy.AppliedTo = []common.Endpoint{ common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID}, } log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy) return nil } // If PodSelector is not empty then segment label must be defined. kubeSegmentID, ok := tg.kubePolicy.Spec.PodSelector.MatchLabels[translator.segmentLabelName] if !ok || kubeSegmentID == "" { log.Errorf("Expected segment to be specified in podSelector part as %s", translator.segmentLabelName) return common.NewError("Expected segment to be specified in podSelector part as '%s'", translator.segmentLabelName) } // Translate kubernetes segment label into romana segment. segment, err := translator.getOrAddSegment(tg.kubePolicy.ObjectMeta.Namespace, kubeSegmentID) if err != nil { log.Errorf("Error in translate while calling l.getOrAddSegment with %s and %s - error %s", tg.kubePolicy.ObjectMeta.Namespace, kubeSegmentID, err) return err } tg.romanaPolicy.AppliedTo = []common.Endpoint{ common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID, SegmentID: segment.ID}, } return nil }
// deleteIPtablesRulesBySubstring uninstalls iptables Rules matching given // substring and deletes them from database. Has no effect on 'inactive' Rules. func (fw *IPtables) deleteIPtablesRulesBySubstring(substring string) error { if substring == "" { return common.NewError("Empty substring specified to deleteIPtablesRulesBySubstring") } rules, err := fw.Store.findIPtablesRules(substring) if err != nil { return err } for _, rule := range *rules { if rule.State == setRuleInactive.String() { continue } err = fw.deleteIPtablesRule(&rule) if err != nil { return err } } return nil }
/// makeNextIngressPeer analyzes current Ingress rule and adds new Peer to romanaPolicy.Peers. func (tg *TranslateGroup) makeNextIngressPeer(translator *Translator) error { ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex] for _, fromEntry := range ingress.From { tenantCacheEntry := &TenantCacheEntry{} // Exactly one of From.PodSelector or From.NamespaceSelector must be specified. if fromEntry.PodSelector == nil && fromEntry.NamespaceSelector == nil { log.Errorf("Either PodSElector or NamespacesSelector must be specified") return common.NewError("Either PodSElector or NamespacesSelector must be specified") } else if fromEntry.PodSelector != nil && fromEntry.NamespaceSelector != nil { log.Errorf("Exactly one of PodSElector or NamespacesSelector must be specified") return common.NewError("Exactly on of PodSElector or NamespacesSelector must be specified") } // This ingress field matching a namespace which will be our source tenant. if fromEntry.NamespaceSelector != nil { tenantName, ok := fromEntry.NamespaceSelector.MatchLabels[translator.tenantLabelName] if !ok || tenantName == "" { log.Errorf("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName) return common.NewError("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName) } tenantCacheEntry = translator.checkTenantInCache(tenantName) if tenantCacheEntry == nil { log.Errorf("Tenant not not found when translating policy %v", tg.romanaPolicy) return TranslatorError{ErrorTenantNotInCache, nil} } // Found a source tenant, let's register it as romana Peeer. tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID}) } // This ingress field matches a segment and source tenant is a same as target tenant. if fromEntry.PodSelector != nil { // Check if source/target tenant in cache. tenantCacheEntry = translator.checkTenantInCache(tg.kubePolicy.ObjectMeta.Namespace) if tenantCacheEntry == nil { log.Errorf("Tenant not not found when translating policy %v", tg.romanaPolicy) return TranslatorError{ErrorTenantNotInCache, nil} } // If podSelector is empty match all traffic from the tenant. if len(fromEntry.PodSelector.MatchLabels) == 0 { tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID}) log.Tracef(trace.Inside, "No segment specified when translating ingress rule %v", tg.kubePolicy.Spec.Ingress[tg.ingressIndex]) return nil } // Get segment name from podSelector. kubeSegmentID, ok := fromEntry.PodSelector.MatchLabels[translator.segmentLabelName] if !ok || kubeSegmentID == "" { log.Errorf("Expected segment to be specified in podSelector part as %s", translator.segmentLabelName) return common.NewError("Expected segment to be specified in podSelector part as '%s'", translator.segmentLabelName) } // Translate kubernetes segment name into romana segment. segment, err := translator.getOrAddSegment(tenantCacheEntry.Tenant.Name, kubeSegmentID) if err != nil { log.Errorf("Error in translate while calling l.getOrAddSegment with %s and %s - error %s", tenantCacheEntry.Tenant.Name, kubeSegmentID, err) return err } // Register source tenant/segment as a romana Peer. tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, common.Endpoint{TenantID: tenantCacheEntry.Tenant.ID, TenantExternalID: tenantCacheEntry.Tenant.ExternalID, SegmentID: segment.ID}) } } return nil }
// addEndpoint allocates an IP address and stores it in the // database. func (ipamStore *ipamStore) addEndpoint(endpoint *Endpoint, upToEndpointIpInt uint64, dc common.Datacenter) error { var err error tx := ipamStore.DbStore.Db.Begin() if endpoint.RequestToken.Valid && endpoint.RequestToken.String != "" { var existingEndpoints []Endpoint var count int tx.Where("request_token = ?", endpoint.RequestToken.String).Find(&existingEndpoints).Count(&count) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 1: %v", err) tx.Rollback() return err } if count > 0 { // This will only be 1, because of unique constraint. tx.Rollback() log.Printf("Found existing %s: %+v", endpoint.RequestToken.String, existingEndpoints[0]) endpoint.EffectiveNetworkID = existingEndpoints[0].EffectiveNetworkID endpoint.HostId = existingEndpoints[0].HostId endpoint.Id = existingEndpoints[0].Id endpoint.InUse = existingEndpoints[0].InUse endpoint.Name = existingEndpoints[0].Name endpoint.NetworkID = existingEndpoints[0].NetworkID endpoint.RequestToken = existingEndpoints[0].RequestToken endpoint.SegmentID = existingEndpoints[0].SegmentID endpoint.TenantID = existingEndpoints[0].TenantID endpoint.Ip = existingEndpoints[0].Ip return nil } } hostId := endpoint.HostId endpoint.InUse = true tenantId := endpoint.TenantID segId := endpoint.SegmentID filter := "host_id = ? AND tenant_id = ? AND segment_id = ? " var sel string // First, find the MAX network ID available for this host/segment combination. sel = "IFNULL(MAX(network_id),-1)+1" log.Printf("IpamStore: Calling SELECT %s FROM endpoints WHERE %s;", sel, fmt.Sprintf(strings.Replace(filter, "?", "%s", 3), hostId, tenantId, segId)) row := tx.Model(Endpoint{}).Where(filter, hostId, tenantId, segId).Select(sel).Row() err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 2: %v", err) tx.Rollback() return err } netID := sql.NullInt64{} row.Scan(&netID) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 3: %v", err) tx.Rollback() return err } log.Printf("IpamStore: max net ID: %v", netID) maxEffNetID := uint64(1<<(dc.EndpointSpaceBits+dc.EndpointBits) - 1) // Does this exceed max bits? endpoint.NetworkID = uint64(netID.Int64) endpoint.EffectiveNetworkID = getEffectiveNetworkID(endpoint.NetworkID, dc.EndpointSpaceBits) if endpoint.EffectiveNetworkID <= maxEffNetID { // Does not exceed max bits, all good. // log.Printf("IpamStore: Effective network ID for network ID %d (stride %d): %d\n", endpoint.NetworkID, dc.EndpointSpaceBits, endpoint.EffectiveNetworkID) ipInt := upToEndpointIpInt | endpoint.EffectiveNetworkID // log.Printf("IpamStore: %d | %d = %d", upToEndpointIpInt, endpoint.EffectiveNetworkID, ipInt) endpoint.Ip = common.IntToIPv4(ipInt).String() tx = tx.Create(endpoint) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 4: %v", err) tx.Rollback() return err } log.Printf("IpamStore: Allocated %d: %s", endpoint.NetworkID, endpoint.Ip) tx.Commit() return nil } // Out of bits, see if we can reuse an earlier allocated address... log.Printf("IpamStore: New effective network ID is %d, exceeds maximum %d\n", endpoint.EffectiveNetworkID, maxEffNetID) // See if there is a formerly allocated IP already that has been released // (marked "in_use") sel = "MIN(network_id), ip" log.Printf("IpamStore: Calling SELECT %s FROM endpoints WHERE %s;", sel, fmt.Sprintf(strings.Replace(filter+"AND in_use = 0", "?", "%s", 3), hostId, tenantId, segId)) // In containerized setup, not using group by leads to failure due to // incompatible sql mode, thus use "GROUP BY network_id, ip" to avoid // this failure. row = tx.Model(Endpoint{}).Where(filter+"AND in_use = 0", hostId, tenantId, segId).Select(sel).Group("ip").Order("MIN(network_id) ASC").Row() err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 5: %v", err) tx.Rollback() return err } netID = sql.NullInt64{} var ip string row.Scan(&netID, &ip) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 6: %v", err) tx.Rollback() return err } if netID.Valid { log.Printf("IpamStore: Reusing %d: %s", netID.Int64, ip) endpoint.Ip = ip tx = tx.Model(Endpoint{}).Where("ip = ?", ip).Update("in_use", true) err = common.GetDbErrors(tx) if err != nil { log.Printf("IPAM Errors 7: %v", err) tx.Rollback() return err } tx.Commit() return nil } tx.Rollback() return common.NewError("Out of IP addresses.") }