diff --git a/pkg/pillar/cmd/zedagent/handlemetrics.go b/pkg/pillar/cmd/zedagent/handlemetrics.go index c1e5729c833..c4a3165e388 100644 --- a/pkg/pillar/cmd/zedagent/handlemetrics.go +++ b/pkg/pillar/cmd/zedagent/handlemetrics.go @@ -27,7 +27,6 @@ import ( "github.com/lf-edge/eve/pkg/pillar/utils" "github.com/lf-edge/eve/pkg/pillar/vault" "github.com/lf-edge/eve/pkg/pillar/zedcloud" - uuid "github.com/satori/go.uuid" "github.com/shirou/gopsutil/host" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" @@ -343,18 +342,16 @@ func publishMetrics(ctx *zedagentContext, iteration int) { // Use the network metrics from zedrouter subscription // Only report stats for the ports in DeviceNetworkStatus - labelList := types.ReportLogicallabels(*deviceNetworkStatus) - for _, label := range labelList { + for _, p := range deviceNetworkStatus.Ports { var metric *types.NetworkMetric - ports := deviceNetworkStatus.GetPortsByLogicallabel(label) - if len(ports) == 0 { - continue - } - p := ports[0] if !p.IsL3Port { // metrics for ports from lower layers are not reported continue } + if p.IfName == "" { + // Cannot associate metrics with the port until interface name is known. + continue + } for _, m := range networkMetrics.MetricList { if p.IfName == m.IfName { metric = &m @@ -366,7 +363,7 @@ func publishMetrics(ctx *zedagentContext, iteration int) { } networkDetails := new(metrics.NetworkMetric) networkDetails.LocalName = metric.IfName - networkDetails.IName = label + networkDetails.IName = p.Logicallabel networkDetails.Alias = p.Alias networkDetails.TxPkts = metric.TxPkts networkDetails.RxPkts = metric.RxPkts @@ -1033,55 +1030,6 @@ func encodeProxyStatus(proxyConfig *types.ProxyConfig) *info.ProxyStatus { return status } -func encodeNetworkPortConfig(ctx *zedagentContext, - npc *types.NetworkPortConfig) *info.DevicePort { - aa := ctx.assignableAdapters - - dp := new(info.DevicePort) - dp.Ifname = npc.IfName - // XXX rename the protobuf field Name to Logicallabel and add Phylabel? - dp.Name = npc.Logicallabel - // XXX Add Alias in proto file? - // dp.Alias = npc.Alias - - ibPtr := aa.LookupIoBundlePhylabel(npc.Phylabel) - if ibPtr != nil { - dp.Usage = evecommon.PhyIoMemberUsage(ibPtr.Usage) - } - - dp.IsMgmt = npc.IsMgmt - dp.Cost = uint32(npc.Cost) - dp.Free = npc.Cost == 0 // To be deprecated - // DhcpConfig - dp.DhcpType = uint32(npc.Dhcp) - dp.Subnet = npc.AddrSubnet - - dp.DefaultRouters = make([]string, 0) - dp.DefaultRouters = append(dp.DefaultRouters, npc.Gateway.String()) - - dp.NtpServer = npc.NtpServer.String() - - dp.Dns = new(info.ZInfoDNS) - dp.Dns.DNSdomain = npc.DomainName - dp.Dns.DNSservers = make([]string, 0) - for _, d := range npc.DnsServers { - dp.Dns.DNSservers = append(dp.Dns.DNSservers, d.String()) - } - // XXX Not in definition. Remove? - // XXX string dhcpRangeLow = 17; - // XXX string dhcpRangeHigh = 18; - - dp.Proxy = encodeProxyStatus(&npc.ProxyConfig) - - dp.Err = encodeTestResults(npc.TestResults) - - var nilUUID uuid.UUID - if npc.NetworkUUID != nilUUID { - dp.NetworkUUID = npc.NetworkUUID.String() - } - return dp -} - // This function is called per change, hence needs to try over all management ports // When aiStatus is nil it means a delete and we send a message // containing only the UUID to inform zedcloud about the delete. @@ -1175,8 +1123,9 @@ func PublishAppInfoToZedCloud(ctx *zedagentContext, uuid string, if niStatus != nil { networkInfo.NtpServers = []string{} if niStatus.NtpServer != nil { - networkInfo.NtpServers = append(networkInfo.NtpServers, niStatus.NtpServer.String()) - } else { + networkInfo.NtpServers = append(networkInfo.NtpServers, + niStatus.NtpServer.String()) + } else if niStatus.SelectedUplinkIntfName != "" { ntpServers := types.GetNTPServers(*deviceNetworkStatus, niStatus.SelectedUplinkIntfName) for _, server := range ntpServers { diff --git a/pkg/pillar/cmd/zedagent/parseconfig.go b/pkg/pillar/cmd/zedagent/parseconfig.go index ebe544d9295..11c09af0ee8 100644 --- a/pkg/pillar/cmd/zedagent/parseconfig.go +++ b/pkg/pillar/cmd/zedagent/parseconfig.go @@ -19,10 +19,12 @@ import ( "github.com/google/go-cmp/cmp" zconfig "github.com/lf-edge/eve/api/go/config" + zevecommon "github.com/lf-edge/eve/api/go/evecommon" "github.com/lf-edge/eve/pkg/pillar/objtonum" "github.com/lf-edge/eve/pkg/pillar/sriov" "github.com/lf-edge/eve/pkg/pillar/types" fileutils "github.com/lf-edge/eve/pkg/pillar/utils/file" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" ) @@ -761,7 +763,7 @@ func validateAndAssignNetPorts(dpc *types.DevicePortConfig, newPorts []*types.Ne port2.RecordFailure(errStr) break } - if port.IfName == port2.IfName { + if port.IfName != "" && port.IfName == port2.IfName { errStr := fmt.Sprintf( "Port collides with another port with the same interface name (%s)", port.IfName) @@ -890,18 +892,47 @@ func propagateError(higherLayerPort, lowerLayerPort *types.NetworkPortConfig) { func propagatePhyioAttrsToPort(port *types.NetworkPortConfig, phyio *types.PhysicalIOAdapter) { port.Phylabel = phyio.Phylabel port.IfName = phyio.Phyaddr.Ifname + port.USBAddr = phyio.Phyaddr.UsbAddr + port.PCIAddr = phyio.Phyaddr.PciLong if port.IfName == "" { - // Might not be set for all models - log.Warnf("Physical IO %s (Phylabel %s) has no ifname", - phyio.Logicallabel, phyio.Phylabel) - if phyio.Logicallabel != "" { - port.IfName = phyio.Logicallabel - } else { - port.IfName = phyio.Phylabel + // Inside device model, network adapter may be referenced by PCI or USB address + // instead of the interface name. In fact, with multiple network ports, interface naming + // is not necessary deterministic and may depend on the order of network adapter + // initialization and discovery by the kernel. + // Moreover, once EVE supports userspace vswitch, interface names of ports will differ + // depending on if they are assigned to the kernel or vswitch. + // For the reasons above, it is preferred to reference network adapters by PCI/USB + // addresses going forward. + // For now, we will allow network port configs without interface name at least for + // cellular modems. + // TODO: Allow any type of network port to be defined in PhysicalIOAdapter without + // interface name. + switch types.IoType(phyio.Ptype) { + case types.IoNetWWAN: + if port.USBAddr == "" && port.PCIAddr == "" { + log.Warnf("Physical IO %s (Phylabel %s) has no physical address", + phyio.Logicallabel, phyio.Phylabel) + handleMissingIfname(port, phyio) + } + default: + log.Warnf("Physical IO %s (Phylabel %s) has no ifname", + phyio.Logicallabel, phyio.Phylabel) + handleMissingIfname(port, phyio) } } } +func handleMissingIfname(port *types.NetworkPortConfig, phyio *types.PhysicalIOAdapter) { + // Try to use logical or physical label as interface name. + // If such interface name is not valid, NIM will report error in DeviceNetworkStatus + // under the port's TestResults. + if phyio.Logicallabel != "" { + port.IfName = phyio.Logicallabel + } else { + port.IfName = phyio.Phylabel + } +} + // Make NetworkPortConfig entry for PhysicalIO which is below an L2 port. // The port configuration will contain only labels and the interface name. func makeL2PhyioPort(phyio *types.PhysicalIOAdapter) *types.NetworkPortConfig { @@ -1081,7 +1112,7 @@ func parseOneSystemAdapterConfig(getconfigCtx *getconfigContext, errStr := fmt.Sprintf("Device Config Error. Port %s configured with "+ "UNKNOWN Network UUID (%s). Err: %s. Please fix the "+ "device configuration.", - port.IfName, sysAdapter.NetworkUUID, err) + port.Logicallabel, sysAdapter.NetworkUUID, err) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } else { @@ -1092,7 +1123,7 @@ func parseOneSystemAdapterConfig(getconfigCtx *getconfigContext, if network.HasError() { errStr := fmt.Sprintf("Port %s configured with a network "+ "(UUID: %s) which has an error (%s).", - port.IfName, port.NetworkUUID, network.Error) + port.Logicallabel, port.NetworkUUID, network.Error) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } @@ -1116,7 +1147,7 @@ func parseOneSystemAdapterConfig(getconfigCtx *getconfigContext, if port.AddrSubnet == "" { errStr := fmt.Sprintf("Port %s Configured as DT_STATIC but "+ "missing subnet address. SysAdapter - Name: %s, Addr:%s", - port.IfName, sysAdapter.Name, sysAdapter.Addr) + port.Logicallabel, sysAdapter.Name, sysAdapter.Addr) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } @@ -1127,14 +1158,14 @@ func parseOneSystemAdapterConfig(getconfigCtx *getconfigContext, errStr := fmt.Sprintf("Port %s configured as Management port "+ "with an unsupported DHCP type %d. Client and static are "+ "the only allowed DHCP modes for management ports.", - port.IfName, types.DT_NONE) + port.Logicallabel, types.DT_NONE) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } default: errStr := fmt.Sprintf("Port %s configured with unknown DHCP type %v", - port.IfName, network.Dhcp) + port.Logicallabel, network.Dhcp) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } @@ -1146,7 +1177,7 @@ func parseOneSystemAdapterConfig(getconfigCtx *getconfigContext, } else if isMgmt { errStr := fmt.Sprintf("Port %s Configured as Management port without "+ "configuring a Network. Network is required for Management ports", - port.IfName) + port.Logicallabel) log.Errorf("parseSystemAdapterConfig: %s", errStr) port.RecordFailure(errStr) } @@ -1813,37 +1844,97 @@ func parseNetworkWirelessConfig(ctx *getconfigContext, key string, netEnt *zconf wType := netWireless.GetType() switch wType { case zconfig.WirelessType_Cellular: - // wconfig.WType = types.WirelessTypeCellular - cellulars := netWireless.GetCellularCfg() - for _, cellular := range cellulars { - var wcell types.CellConfig - wcell.APN = cellular.GetAPN() - wcell.ProbeAddr = cellular.GetProbe().GetProbeAddress() - wcell.DisableProbe = cellular.GetProbe().GetDisable() - wcell.LocationTracking = cellular.GetLocationTracking() - wconfig.Cellular = append(wconfig.Cellular, wcell) + cellNetConfigs := netWireless.GetCellularCfg() + if len(cellNetConfigs) == 0 { + log.Errorf("parseNetworkWirelessConfig: missing cellular config in: %v", + netWireless) + return wconfig + } + // CellularCfg should really have been defined in the EVE API as a single entry + // rather than as a list (for multiple SIM cards and APNs there is AccessPoints list + // underneath). However, marking this field as deprecated and creating a new non-list + // field seems unnecessary - let's instead expect single entry. + if len(cellNetConfigs) > 1 { + log.Errorf( + "parseNetworkWirelessConfig: unexpected multiple cellular configs in: %v", + netWireless) + return wconfig + } + cellNetConfig := cellNetConfigs[0] + for _, accessPoint := range cellNetConfig.AccessPoints { + var ap types.CellularAccessPoint + ap.APN = accessPoint.Apn + ap.SIMSlot = uint8(accessPoint.SimSlot) + // By default (ActivatedSimSlot is not defined), any configured Access Point + // should be activated. + ap.Activated = cellNetConfig.ActivatedSimSlot == 0 || + cellNetConfig.ActivatedSimSlot == accessPoint.SimSlot + switch accessPoint.AuthProtocol { + case zconfig.CellularAuthProtocol_CELLULAR_AUTH_PROTOCOL_PAP: + ap.AuthProtocol = types.WwanAuthProtocolPAP + case zconfig.CellularAuthProtocol_CELLULAR_AUTH_PROTOCOL_CHAP: + ap.AuthProtocol = types.WwanAuthProtocolCHAP + case zconfig.CellularAuthProtocol_CELLULAR_AUTH_PROTOCOL_PAP_AND_CHAP: + ap.AuthProtocol = types.WwanAuthProtocolPAPAndCHAP + default: + log.Errorf("parseNetworkWirelessConfig: unrecognized AuthProtocol: %+v", + accessPoint) + } + ap.CipherBlockStatus = parseCipherBlock(ctx, key, accessPoint.GetCipherData()) + for _, plmn := range accessPoint.PreferredPlmns { + ap.PreferredPLMNs = append(ap.PreferredPLMNs, plmn) + } + for _, rat := range accessPoint.PreferredRats { + switch rat { + case zevecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_GSM: + ap.PreferredRATs = append(ap.PreferredRATs, types.WwanRATGSM) + case zevecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_UMTS: + ap.PreferredRATs = append(ap.PreferredRATs, types.WwanRATUMTS) + case zevecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_LTE: + ap.PreferredRATs = append(ap.PreferredRATs, types.WwanRATLTE) + case zevecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_5GNR: + ap.PreferredRATs = append(ap.PreferredRATs, types.WwanRAT5GNR) + default: + log.Errorf("parseNetworkWirelessConfig: unrecognized RAT: %+v", + accessPoint) + } + } + ap.ForbidRoaming = accessPoint.ForbidRoaming + wconfig.Cellular.AccessPoints = append(wconfig.Cellular.AccessPoints, ap) + } + // For backward compatibility. + if len(cellNetConfig.AccessPoints) == 0 && cellNetConfig.APN != "" { + ap := types.CellularAccessPoint{ + Activated: true, + APN: cellNetConfig.APN, + } + wconfig.Cellular.AccessPoints = append(wconfig.Cellular.AccessPoints, ap) } + wconfig.Cellular.Probe.Disable = cellNetConfig.Probe.GetDisable() + wconfig.Cellular.Probe.Address = cellNetConfig.Probe.GetProbeAddress() + wconfig.Cellular.LocationTracking = cellNetConfig.GetLocationTracking() log.Functionf("parseNetworkWirelessConfig: Wireless of network Cellular, %v", wconfig.Cellular) case zconfig.WirelessType_WiFi: - // wconfig.WType = types.WirelessTypeWifi wificfgs := netWireless.GetWifiCfg() - for _, wificfg := range wificfgs { var wifi types.WifiConfig wifi.SSID = wificfg.GetWifiSSID() - if wificfg.GetKeyScheme() == zconfig.WiFiKeyScheme_WPAPSK { + switch wificfg.GetKeyScheme() { + case zconfig.WiFiKeyScheme_WPAPSK: wifi.KeyScheme = types.KeySchemeWpaPsk - } else if wificfg.GetKeyScheme() == zconfig.WiFiKeyScheme_WPAEAP { + case zconfig.WiFiKeyScheme_WPAEAP: wifi.KeyScheme = types.KeySchemeWpaEap + default: + log.Errorf("parseNetworkWirelessConfig: unrecognized WiFi Key scheme: %+v", + wificfg) } wifi.Identity = wificfg.GetIdentity() wifi.Password = wificfg.GetPassword() wifi.Priority = wificfg.GetPriority() - key = fmt.Sprintf("%s-%s", key, wifi.SSID) - wifi.CipherBlockStatus = parseCipherBlock(ctx, key, wificfg.GetCipherData()) - + wifiKey := fmt.Sprintf("%s-%s", key, wifi.SSID) + wifi.CipherBlockStatus = parseCipherBlock(ctx, wifiKey, wificfg.GetCipherData()) wconfig.Wifi = append(wconfig.Wifi, wifi) } log.Functionf("parseNetworkWirelessConfig: Wireless of network Wifi, %v", wconfig.Wifi) @@ -1957,7 +2048,7 @@ func parseIpspec(ipspec *zconfig.Ipspec, config.DhcpRange.End = end } - addrCount := types.GetIPAddrCountOnSubnet(config.Subnet) + addrCount := netutils.GetIPAddrCountOnSubnet(config.Subnet) if addrCount < types.MinSubnetSize { return fmt.Errorf("network(%s), Subnet too small(%d)", config.Key(), addrCount) @@ -1965,7 +2056,7 @@ func parseIpspec(ipspec *zconfig.Ipspec, // if not set, take some default if config.Gateway == nil { - config.Gateway = types.AddToIP(config.Subnet.IP, 1) + config.Gateway = netutils.AddToIP(config.Subnet.IP, 1) log.Warnf("network(%s), No Gateway, setting default(%s)", config.Key(), config.Gateway.String()) } @@ -1983,39 +2074,39 @@ func parseIpspec(ipspec *zconfig.Ipspec, // if not set, take some default if config.DhcpRange.Start == nil { - config.DhcpRange.Start = types.AddToIP(config.Subnet.IP, + config.DhcpRange.Start = netutils.AddToIP(config.Subnet.IP, dhcpRangeStart) log.Warnf("network(%s), No Dhcp Start, setting default(%s)", config.Key(), config.DhcpRange.Start.String()) } if config.DhcpRange.End == nil { - config.DhcpRange.End = types.AddToIP(config.Subnet.IP, + config.DhcpRange.End = netutils.AddToIP(config.Subnet.IP, dhcpRangeEnd) log.Warnf("network(%s), No Dhcp End, setting default(%s)", config.Key(), config.DhcpRange.End.String()) } // check whether the dhcp range(start, end) // equal (network, gateway, broadcast) addresses - if network := types.GetIPNetwork(config.Subnet); network != nil { + if network := netutils.GetIPNetwork(config.Subnet); network != nil { if network.Equal(config.DhcpRange.Start) { log.Warnf("network(%s), Dhcp Start is Network(%s), correcting", config.Key(), config.Subnet.IP.String()) config.DhcpRange.Start = - types.AddToIP(config.DhcpRange.Start, 1) + netutils.AddToIP(config.DhcpRange.Start, 1) } if config.Gateway.Equal(config.DhcpRange.Start) { log.Warnf("network(%s), Dhcp Start is Gateway(%s), correcting", config.Key(), config.Gateway.String()) config.DhcpRange.Start = - types.AddToIP(config.Gateway, 1) + netutils.AddToIP(config.Gateway, 1) } } - if bcast := types.GetIPBroadcast(config.Subnet); bcast != nil { + if bcast := netutils.GetIPBroadcast(config.Subnet); bcast != nil { if bcast.Equal(config.DhcpRange.End) { log.Warnf("network(%s), Dhcp End is Broadcast(%s), correcting", config.Key(), bcast.String()) config.DhcpRange.End = - types.AddToIP(config.DhcpRange.End, -1) + netutils.AddToIP(config.DhcpRange.End, -1) } } // Gateway should not be inside the DhcpRange @@ -2031,7 +2122,7 @@ func parseIpspec(ipspec *zconfig.Ipspec, // what ByteAllocator can provide) and use it for network instances // that require bigger subnets. if addressesInRange > objtonum.ByteAllocatorMaxNum { - config.DhcpRange.End = types.AddToIP(config.DhcpRange.Start, objtonum.ByteAllocatorMaxNum) + config.DhcpRange.End = netutils.AddToIP(config.DhcpRange.Start, objtonum.ByteAllocatorMaxNum) } return nil } diff --git a/pkg/pillar/cmd/zedagent/radiosilence.go b/pkg/pillar/cmd/zedagent/radiosilence.go index fa6d366db4b..0072d19fc04 100644 --- a/pkg/pillar/cmd/zedagent/radiosilence.go +++ b/pkg/pillar/cmd/zedagent/radiosilence.go @@ -144,7 +144,7 @@ func getRadioStatus(ctx *getconfigContext) *profile.RadioStatus { Logicallabel: port.Logicallabel, Module: encodeCellModuleInfo(wwanStatus.Module), SimCards: encodeSimCards(wwanStatus.Module.Name, wwanStatus.SimCards), - Providers: encodeCellProviders(wwanStatus.Providers), + Providers: encodeCellProviders(wwanStatus), ConfigError: wwanStatus.ConfigError, ProbeError: wwanStatus.ProbeError, }) diff --git a/pkg/pillar/cmd/zedagent/reportinfo.go b/pkg/pillar/cmd/zedagent/reportinfo.go index 11b17173921..b324ebac906 100644 --- a/pkg/pillar/cmd/zedagent/reportinfo.go +++ b/pkg/pillar/cmd/zedagent/reportinfo.go @@ -26,9 +26,11 @@ import ( "github.com/lf-edge/eve/pkg/pillar/utils" fileutils "github.com/lf-edge/eve/pkg/pillar/utils/file" "github.com/lf-edge/eve/pkg/pillar/vault" + uuid "github.com/satori/go.uuid" "github.com/shirou/gopsutil/host" "golang.org/x/sys/unix" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) const ( @@ -440,28 +442,25 @@ func PublishDeviceInfoToZedCloud(ctx *zedagentContext, dest destinationBitset) { } // We report all the ports in DeviceNetworkStatus - // TODO: report also modems not used by any port. - labelList := types.ReportLogicallabels(*deviceNetworkStatus) - for _, label := range labelList { - ports := deviceNetworkStatus.GetPortsByLogicallabel(label) - if len(ports) == 0 { - continue - } - p := ports[0] - ReportDeviceNetworkInfo := encodeNetInfo(*p) + // Note that this is deprecated in favour of SystemAdapterInfo. + for _, p := range deviceNetworkStatus.Ports { + ReportDeviceNetworkInfo := encodeNetInfo(p) // XXX rename DevName to Logicallabel in proto file - ReportDeviceNetworkInfo.DevName = *proto.String(label) + ReportDeviceNetworkInfo.DevName = *proto.String(p.Logicallabel) ReportDeviceInfo.Network = append(ReportDeviceInfo.Network, ReportDeviceNetworkInfo) - // Report all SIM cards and cellular modules - if p.WirelessStatus.WType == types.WirelessTypeCellular { - wwanStatus := p.WirelessStatus.Cellular - ReportDeviceInfo.CellRadios = append( - ReportDeviceInfo.CellRadios, - encodeCellModuleInfo(wwanStatus.Module)) - ReportDeviceInfo.Sims = append( - ReportDeviceInfo.Sims, - encodeSimCards(wwanStatus.Module.Name, wwanStatus.SimCards)...) + } + // Report all SIM cards and cellular modules, including those not in use. + if statusObj, _ := ctx.subWwanStatus.Get("global"); statusObj != nil { + if status, ok := statusObj.(types.WwanStatus); ok { + for _, cellNet := range status.Networks { + ReportDeviceInfo.CellRadios = append( + ReportDeviceInfo.CellRadios, + encodeCellModuleInfo(cellNet.Module)) + ReportDeviceInfo.Sims = append( + ReportDeviceInfo.Sims, + encodeSimCards(cellNet.Module.Name, cellNet.SimCards)...) + } } } // Fill in global ZInfoDNS dns from /etc/resolv.conf @@ -803,18 +802,16 @@ func addUserSwInfo(ctx *zedagentContext, swInfo *info.ZInfoDevSW, tooEarly bool) func encodeNetInfo(port types.NetworkPortStatus) *info.ZInfoNetwork { networkInfo := new(info.ZInfoNetwork) - networkInfo.LocalName = *proto.String(port.IfName) + networkInfo.LocalName = port.IfName networkInfo.IPAddrs = make([]string, len(port.AddrInfoList)) for index, ai := range port.AddrInfoList { - networkInfo.IPAddrs[index] = *proto.String(ai.Addr.String()) + networkInfo.IPAddrs[index] = ai.Addr.String() } networkInfo.Ipv4Up = port.Up - networkInfo.MacAddr = *proto.String(port.MacAddr) + networkInfo.MacAddr = port.MacAddr.String() + networkInfo.DevName = port.Logicallabel - // In case caller doesn't override - networkInfo.DevName = *proto.String(port.IfName) - - networkInfo.Alias = *proto.String(port.Alias) + networkInfo.Alias = port.Alias // Default routers from kernel whether or not we are using DHCP networkInfo.DefaultRouters = make([]string, len(port.DefaultRouters)) for index, dr := range port.DefaultRouters { @@ -839,13 +836,13 @@ func encodeNetInfo(port types.NetworkPortStatus) *info.ZInfoNetwork { continue } geo := new(info.GeoLoc) - geo.UnderlayIP = *proto.String(ai.Geo.IP) - geo.Hostname = *proto.String(ai.Geo.Hostname) - geo.City = *proto.String(ai.Geo.City) - geo.Country = *proto.String(ai.Geo.Country) - geo.Loc = *proto.String(ai.Geo.Loc) - geo.Org = *proto.String(ai.Geo.Org) - geo.Postal = *proto.String(ai.Geo.Postal) + geo.UnderlayIP = ai.Geo.IP + geo.Hostname = ai.Geo.Hostname + geo.City = ai.Geo.City + geo.Country = ai.Geo.Country + geo.Loc = ai.Geo.Loc + geo.Org = ai.Geo.Org + geo.Postal = ai.Geo.Postal networkInfo.Location = geo break } @@ -895,6 +892,7 @@ func encodeCellModuleInfo(wwanModule types.WwanCellModule) *info.ZCellularModule Imei: wwanModule.IMEI, FirmwareVersion: wwanModule.Revision, Model: wwanModule.Model, + Manufacturer: wwanModule.Manufacturer, OperatingState: opState, ControlProtocol: ctrlProto, } @@ -907,20 +905,34 @@ func encodeSimCards(cellModule string, wwanSimCards []types.WwanSimCard) (simCar CellModuleName: cellModule, Imsi: simCard.IMSI, Iccid: simCard.ICCID, - State: simCard.Status, + State: simCard.State, + SlotNumber: uint32(simCard.SlotNumber), + SlotActivated: simCard.SlotActivated, }) } return simCards } -func encodeCellProviders(wwanProviders []types.WwanProvider) (providers []*info.ZCellularProvider) { - for _, provider := range wwanProviders { - providers = append(providers, &info.ZCellularProvider{ - Plmn: provider.PLMN, - Description: provider.Description, - CurrentServing: provider.CurrentServing, - Roaming: provider.Roaming, - }) +func encodeCellProvider(wwanProvider types.WwanProvider) (provider *info.ZCellularProvider) { + return &info.ZCellularProvider{ + Plmn: wwanProvider.PLMN, + Description: wwanProvider.Description, + CurrentServing: wwanProvider.CurrentServing, + Roaming: wwanProvider.Roaming, + Forbidden: wwanProvider.Forbidden, + } +} + +func encodeCellProviders(wwanStatus types.WwanNetworkStatus) (providers []*info.ZCellularProvider) { + var includedCurrentProvider bool + for _, provider := range wwanStatus.VisibleProviders { + if provider == wwanStatus.CurrentProvider { + includedCurrentProvider = true + } + providers = append(providers, encodeCellProvider(provider)) + } + if !includedCurrentProvider && wwanStatus.CurrentProvider.PLMN != "" { + providers = append(providers, encodeCellProvider(wwanStatus.CurrentProvider)) } return providers } @@ -952,31 +964,17 @@ func encodeSystemAdapterInfo(ctx *zedagentContext) *info.SystemAdapterInfo { // info for ports from lower layers is not published continue } - // FIXME: publish status here, not config! - // TODO: include DNS servers, MTU, etc. - dps.Ports[j] = encodeNetworkPortConfig(ctx, &p) - if i == dpcl.CurrentIndex && p.WirelessCfg.WType == types.WirelessTypeCellular { + if i == dpcl.CurrentIndex { + // For the currently used DPC we publish the status (DeviceNetworkStatus). ports := deviceNetworkStatus.GetPortsByLogicallabel(p.Logicallabel) - if len(ports) == 0 { + if len(ports) == 1 { + dps.Ports[j] = encodeNetworkPortStatus(ctx, ports[0], p.NetworkUUID) continue } - portStatus := ports[0] - wwanStatus := portStatus.WirelessStatus.Cellular - var simCards []string - for _, simCard := range wwanStatus.SimCards { - simCards = append(simCards, simCard.Name) - } - dps.Ports[j].WirelessStatus = &info.WirelessStatus{ - Type: info.WirelessType_WIRELESS_TYPE_CELLULAR, - Cellular: &info.ZCellularStatus{ - CellularModule: wwanStatus.Module.Name, - SimCards: simCards, - Providers: encodeCellProviders(wwanStatus.Providers), - ConfigError: wwanStatus.ConfigError, - ProbeError: wwanStatus.ProbeError, - }, - } } + // For inactive DPCs (or if DNS is not available) we publish the config + // (DevicePortConfig). + dps.Ports[j] = encodeNetworkPortConfig(ctx, &p) } sainfo.Status[i] = dps } @@ -984,6 +982,159 @@ func encodeSystemAdapterInfo(ctx *zedagentContext) *info.SystemAdapterInfo { return sainfo } +// encodeNetInfo encodes info from the port +func encodeNetworkPortStatus(ctx *zedagentContext, + port *types.NetworkPortStatus, network uuid.UUID) *info.DevicePort { + aa := ctx.assignableAdapters + ioBundle := aa.LookupIoBundleLogicallabel(port.Logicallabel) + + devicePort := new(info.DevicePort) + devicePort.Ifname = port.IfName + devicePort.Name = port.Logicallabel + devicePort.Err = encodeTestResults(port.TestResults) + if ioBundle != nil { + devicePort.Usage = ioBundle.Usage + } + devicePort.Cost = uint32(port.Cost) + devicePort.IsMgmt = port.IsMgmt + devicePort.Free = port.Cost == 0 // To be deprecated + devicePort.NetworkUUID = network.String() + devicePort.DhcpType = uint32(port.Dhcp) + devicePort.Subnet = port.Subnet.String() + devicePort.Up = port.Up + devicePort.Mtu = uint32(port.MTU) + devicePort.Domainname = port.DomainName + // TODO: modify EVE APIs and allow to publish full list of NTP servers + if port.NtpServer != nil { + devicePort.NtpServer = port.NtpServer.String() + } else if len(port.NtpServers) > 0 { + devicePort.NtpServer = port.NtpServers[0].String() + } + devicePort.Proxy = encodeProxyStatus(&port.ProxyConfig) + devicePort.MacAddr = port.MacAddr.String() + for _, ipAddr := range port.AddrInfoList { + devicePort.IPAddrs = append(devicePort.IPAddrs, ipAddr.Addr.String()) + } + // devicePort.Gateway is deprecated - replaced by DefaultRouters + for _, router := range port.DefaultRouters { + devicePort.DefaultRouters = append(devicePort.DefaultRouters, router.String()) + } + // devicePort.DnsServers is deprecated - replaced by Dns + devicePort.Dns = new(info.ZInfoDNS) + devicePort.Dns.DNSdomain = port.DomainName + for _, dnsServer := range port.DNSServers { + devicePort.Dns.DNSservers = append(devicePort.Dns.DNSservers, dnsServer.String()) + } + // TODO We may have geoloc information for each IP address. + // For now fill in using the first IP address which has location info available. + for _, ai := range port.AddrInfoList { + if ai.Geo == nilIPInfo { + continue + } + geo := new(info.GeoLoc) + geo.UnderlayIP = ai.Geo.IP + geo.Hostname = ai.Geo.Hostname + geo.City = ai.Geo.City + geo.Country = ai.Geo.Country + geo.Loc = ai.Geo.Loc + geo.Org = ai.Geo.Org + geo.Postal = ai.Geo.Postal + devicePort.Location = geo + break + } + switch port.WirelessStatus.WType { + case types.WirelessTypeCellular: + wwanStatus := port.WirelessStatus.Cellular + var simCards []string + for _, simCard := range wwanStatus.SimCards { + simCards = append(simCards, simCard.Name) + } + var rats []evecommon.RadioAccessTechnology + for _, rat := range wwanStatus.CurrentRATs { + switch rat { + case types.WwanRATGSM: + rats = append(rats, evecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_GSM) + case types.WwanRATUMTS: + rats = append(rats, evecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_UMTS) + case types.WwanRATLTE: + rats = append(rats, evecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_LTE) + case types.WwanRAT5GNR: + rats = append(rats, evecommon.RadioAccessTechnology_RADIO_ACCESS_TECHNOLOGY_5GNR) + } + } + var connectedAt *timestamppb.Timestamp + if wwanStatus.ConnectedAt != 0 { + connectedAt = timestamppb.New(time.Unix(int64(wwanStatus.ConnectedAt), 0)) + } + devicePort.WirelessStatus = &info.WirelessStatus{ + Type: info.WirelessType_WIRELESS_TYPE_CELLULAR, + Cellular: &info.ZCellularStatus{ + CellularModule: wwanStatus.Module.Name, + SimCards: simCards, + Providers: encodeCellProviders(wwanStatus), + CurrentRats: rats, + ConnectedAt: connectedAt, + ConfigError: wwanStatus.ConfigError, + ProbeError: wwanStatus.ProbeError, + }, + } + case types.WirelessTypeWifi: + devicePort.WirelessStatus = &info.WirelessStatus{ + Type: info.WirelessType_WIRELESS_TYPE_WIFI, + } + } + return devicePort +} + +func encodeNetworkPortConfig(ctx *zedagentContext, + npc *types.NetworkPortConfig) *info.DevicePort { + aa := ctx.assignableAdapters + + dp := new(info.DevicePort) + dp.Ifname = npc.IfName + // XXX rename the protobuf field Name to Logicallabel and add Phylabel? + dp.Name = npc.Logicallabel + // XXX Add Alias in proto file? + // dp.Alias = npc.Alias + + ibPtr := aa.LookupIoBundleLogicallabel(npc.Logicallabel) + if ibPtr != nil { + dp.Usage = ibPtr.Usage + } + + dp.IsMgmt = npc.IsMgmt + dp.Cost = uint32(npc.Cost) + dp.Free = npc.Cost == 0 // To be deprecated + // DhcpConfig + dp.DhcpType = uint32(npc.Dhcp) + dp.Subnet = npc.AddrSubnet + + dp.DefaultRouters = make([]string, 0) + dp.DefaultRouters = append(dp.DefaultRouters, npc.Gateway.String()) + + dp.NtpServer = npc.NtpServer.String() + + dp.Dns = new(info.ZInfoDNS) + dp.Dns.DNSdomain = npc.DomainName + dp.Dns.DNSservers = make([]string, 0) + for _, d := range npc.DnsServers { + dp.Dns.DNSservers = append(dp.Dns.DNSservers, d.String()) + } + // XXX Not in definition. Remove? + // XXX string dhcpRangeLow = 17; + // XXX string dhcpRangeHigh = 18; + + dp.Proxy = encodeProxyStatus(&npc.ProxyConfig) + + dp.Err = encodeTestResults(npc.TestResults) + + var nilUUID uuid.UUID + if npc.NetworkUUID != nilUUID { + dp.NetworkUUID = npc.NetworkUUID.String() + } + return dp +} + // getDataSecAtRestInfo prepares status related to Data security at Rest func getDataSecAtRestInfo(ctx *zedagentContext) *info.DataSecAtRest { subVaultStatus := ctx.subVaultStatus diff --git a/pkg/pillar/cmd/zedagent/zedagent.go b/pkg/pillar/cmd/zedagent/zedagent.go index 6b6f5053d87..4206d10dc2c 100644 --- a/pkg/pillar/cmd/zedagent/zedagent.go +++ b/pkg/pillar/cmd/zedagent/zedagent.go @@ -137,6 +137,7 @@ type zedagentContext struct { subCapabilities pubsub.Subscription subAppInstMetaData pubsub.Subscription subWwanMetrics pubsub.Subscription + subWwanStatus pubsub.Subscription subLocationInfo pubsub.Subscription subZFSPoolStatus pubsub.Subscription subZFSPoolMetrics pubsub.Subscription @@ -713,6 +714,9 @@ func waitUntilDNSReady(zedagentCtx *zedagentContext, stillRunning *time.Ticker) getconfigCtx.localServerMap.upToDate = false getconfigCtx.subAppNetworkStatus.ProcessChange(change) + case change := <-zedagentCtx.subWwanStatus.MsgChan(): + zedagentCtx.subWwanStatus.ProcessChange(change) + case change := <-zedagentCtx.subWwanMetrics.MsgChan(): zedagentCtx.subWwanMetrics.ProcessChange(change) @@ -973,6 +977,9 @@ func mainEventLoop(zedagentCtx *zedagentContext, stillRunning *time.Ticker) { case change := <-zedagentCtx.subAppInstMetaData.MsgChan(): zedagentCtx.subAppInstMetaData.ProcessChange(change) + case change := <-zedagentCtx.subWwanStatus.MsgChan(): + zedagentCtx.subWwanStatus.ProcessChange(change) + case change := <-zedagentCtx.subWwanMetrics.MsgChan(): zedagentCtx.subWwanMetrics.ProcessChange(change) @@ -1651,6 +1658,22 @@ func initPostOnboardSubs(zedagentCtx *zedagentContext) { } zedagentCtx.subAppInstMetaData.Activate() + // Used to publish info about unused cellular modems. + // Cellular modems used by configured network ports have status published + // as part of DeviceNetworkStatus. + zedagentCtx.subWwanStatus, err = ps.NewSubscription(pubsub.SubscriptionOptions{ + AgentName: "nim", + MyAgentName: agentName, + TopicImpl: types.WwanStatus{}, + Activate: true, + Ctx: zedagentCtx, + WarningTime: warningTime, + ErrorTime: errorTime, + }) + if err != nil { + log.Fatal(err) + } + zedagentCtx.subWwanMetrics, err = ps.NewSubscription(pubsub.SubscriptionOptions{ AgentName: "nim", MyAgentName: agentName, diff --git a/pkg/pillar/cmd/zedrouter/appnetwork.go b/pkg/pillar/cmd/zedrouter/appnetwork.go index 2d7ac107620..ec02a0eddc0 100644 --- a/pkg/pillar/cmd/zedrouter/appnetwork.go +++ b/pkg/pillar/cmd/zedrouter/appnetwork.go @@ -5,9 +5,10 @@ package zedrouter import ( "fmt" + "github.com/lf-edge/eve/pkg/pillar/nireconciler" "github.com/lf-edge/eve/pkg/pillar/types" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" uuid "github.com/satori/go.uuid" ) @@ -50,7 +51,7 @@ func (z *zedrouter) updateVIFsForStateCollecting( networks = append(networks, ul.Network) } } - networks = utils.FilterDuplicates(networks) + networks = generics.FilterDuplicates(networks) // Update state collecting for NIs that the app is or was connected to. for _, network := range networks { netConfig := z.lookupNetworkInstanceConfig(network.String()) diff --git a/pkg/pillar/cmd/zedrouter/ipam.go b/pkg/pillar/cmd/zedrouter/ipam.go index 7d96a460a6e..7c4592ba066 100644 --- a/pkg/pillar/cmd/zedrouter/ipam.go +++ b/pkg/pillar/cmd/zedrouter/ipam.go @@ -10,6 +10,7 @@ import ( "github.com/lf-edge/eve/pkg/pillar/nistate" "github.com/lf-edge/eve/pkg/pillar/types" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" ) @@ -127,7 +128,7 @@ func (z *zedrouter) lookupOrAllocateIPv4ForVIF(niStatus *types.NetworkInstanceSt return nil, err } // Pick an IP address from the subnet. - ipAddr = types.AddToIP(niStatus.DhcpRange.Start, appNum) + ipAddr = netutils.AddToIP(niStatus.DhcpRange.Start, appNum) // Check if the address falls into the Dhcp Range. if !niStatus.DhcpRange.Contains(ipAddr) { err := fmt.Errorf("no free IP addresses in DHCP range(%v, %v)", diff --git a/pkg/pillar/conntester/mock.go b/pkg/pillar/conntester/mock.go index 3209933d771..a8beb84304e 100644 --- a/pkg/pillar/conntester/mock.go +++ b/pkg/pillar/conntester/mock.go @@ -10,13 +10,15 @@ import ( "time" "github.com/lf-edge/eve/pkg/pillar/netdump" + "github.com/lf-edge/eve/pkg/pillar/netmonitor" "github.com/lf-edge/eve/pkg/pillar/types" ) // MockConnectivityTester is used for unit testing. type MockConnectivityTester struct { sync.Mutex - TestDuration time.Duration // inject + TestDuration time.Duration // inject + NetworkMonitor netmonitor.NetworkMonitor // inject iteration int connErrors map[ifRef]error @@ -71,14 +73,13 @@ func (t *MockConnectivityTester) TestConnectivity(dns types.DeviceNetworkStatus, // We have enough uplinks with cloud connectivity working. break } - port := dns.GetPortByIfName(ifName) - missingErr := fmt.Sprintf("port %s does not exist - ignored", ifName) - if port == nil || port.LastError == missingErr { - err := fmt.Errorf("interface %s is missing", ifName) + if _, exists, _ := t.NetworkMonitor.GetInterfaceIndex(ifName); !exists { + err = fmt.Errorf("interface %s is missing", ifName) errorList = append(errorList, err) intfStatusMap.RecordFailure(ifName, err.Error()) continue } + port := dns.GetPortByIfName(ifName) if !port.IsMgmt { continue } diff --git a/pkg/pillar/dpcmanager/dns.go b/pkg/pillar/dpcmanager/dns.go index 961440335b6..bd3f40dffec 100644 --- a/pkg/pillar/dpcmanager/dns.go +++ b/pkg/pillar/dpcmanager/dns.go @@ -66,33 +66,58 @@ func (m *DpcManager) updateDNS() { m.deviceNetStatus.Ports[ix].DomainName = port.DomainName m.deviceNetStatus.Ports[ix].DNSServers = port.DnsServers m.deviceNetStatus.Ports[ix].NtpServer = port.NtpServer + // Prefer errors recorded by DPC verification. + // Errors are recorded from this function only when there is none yet. m.deviceNetStatus.Ports[ix].TestResults = port.TestResults + m.deviceNetStatus.Ports[ix].WirelessStatus.WType = port.WirelessCfg.WType + // If this is a cellular network connectivity, add status information + // obtained from the wwan service. + if port.WirelessCfg.WType == types.WirelessTypeCellular { + wwanNetStatus, found := m.wwanStatus.LookupNetworkStatus(port.Logicallabel) + if found { + m.deviceNetStatus.Ports[ix].WirelessStatus.Cellular = wwanNetStatus + } + } // Do not try to get state data for interface which is in PCIback. - ioBundle := m.adapters.LookupIoBundleIfName(port.IfName) + ioBundle := m.adapters.LookupIoBundleLogicallabel(port.Logicallabel) if ioBundle != nil && ioBundle.IsPCIBack { - err := fmt.Errorf("port %s is in PCIBack - ignored", port.IfName) + err := fmt.Errorf("port %s is in PCIBack", port.Logicallabel) m.Log.Warnf("updateDNS: %v", err) m.deviceNetStatus.Ports[ix].RecordFailure(err.Error()) + if !m.deviceNetStatus.Ports[ix].HasError() { + m.deviceNetStatus.Ports[ix].RecordFailure(err.Error()) + } + continue + } + if port.IfName == "" { + err := fmt.Errorf("port %s is missing interface name", port.Logicallabel) + if !m.deviceNetStatus.Ports[ix].HasError() { + m.deviceNetStatus.Ports[ix].RecordFailure(err.Error()) + } + m.Log.Warnf("updateDNS: interface name of port %s is not yet known, "+ + "will not retrieve some attributes", port.Logicallabel) continue } // Get interface state data from the network stack. ifindex, exists, err := m.NetworkMonitor.GetInterfaceIndex(port.IfName) if !exists || err != nil { - err = fmt.Errorf("port %s does not exist - ignored", port.IfName) + err = fmt.Errorf("port %s is missing", port.Logicallabel) m.Log.Warnf("updateDNS: %v", err) m.deviceNetStatus.Ports[ix].RecordFailure(err.Error()) + if !m.deviceNetStatus.Ports[ix].HasError() { + m.deviceNetStatus.Ports[ix].RecordFailure(err.Error()) + } continue } - var isUp bool ifAttrs, err := m.NetworkMonitor.GetInterfaceAttrs(ifindex) if err != nil { m.Log.Warnf( "updateDNS: failed to get attrs for interface %s with index %d: %v", port.IfName, ifindex, err) } else { - isUp = ifAttrs.AdminUp + m.deviceNetStatus.Ports[ix].Up = ifAttrs.AdminUp + m.deviceNetStatus.Ports[ix].MTU = ifAttrs.MTU } - m.deviceNetStatus.Ports[ix].Up = isUp ipAddrs, macAddr, err := m.NetworkMonitor.GetInterfaceAddrs(ifindex) if err != nil { m.Log.Warnf( @@ -100,7 +125,7 @@ func (m *DpcManager) updateDNS() { port.IfName, ifindex, err) ipAddrs = nil } - m.deviceNetStatus.Ports[ix].MacAddr = macAddr.String() + m.deviceNetStatus.Ports[ix].MacAddr = macAddr m.deviceNetStatus.Ports[ix].AddrInfoList = make([]types.AddrInfo, len(ipAddrs)) if len(ipAddrs) == 0 { m.Log.Functionf("updateDNS: interface %s has NO IP addresses", port.IfName) @@ -141,18 +166,6 @@ func (m *DpcManager) updateDNS() { // Already have TestResults set from above m.Log.Error(err) } - - // If this is a cellular network connectivity, add status information - // obtained from the wwan service. - if port.WirelessCfg.WType == types.WirelessTypeCellular { - wwanNetStatus, found := m.wwanStatus.LookupNetworkStatus(port.Logicallabel) - if found { - m.deviceNetStatus.Ports[ix].WirelessStatus = types.WirelessStatus{ - WType: types.WirelessTypeCellular, - Cellular: wwanNetStatus, - } - } - } } // Preserve geo info for existing interface and IP address @@ -185,6 +198,9 @@ func (m *DpcManager) updateGeo() { if m.deviceNetStatus.Version >= types.DPCIsMgmt && !port.IsMgmt { continue } + if port.IfName == "" { + continue + } for i := range port.AddrInfoList { // Need pointer since we are going to modify. ai := &port.AddrInfoList[i] diff --git a/pkg/pillar/dpcmanager/dpc.go b/pkg/pillar/dpcmanager/dpc.go index ae7b2b9b7e2..062f5c7693a 100644 --- a/pkg/pillar/dpcmanager/dpc.go +++ b/pkg/pillar/dpcmanager/dpc.go @@ -22,6 +22,7 @@ func (m *DpcManager) currentDPC() *types.DevicePortConfig { } func (m *DpcManager) doAddDPC(ctx context.Context, dpc types.DevicePortConfig) { + m.setDiscoveredWwanIfNames(&dpc) mgmtCount := dpc.CountMgmtPorts() if mgmtCount == 0 { // This DPC will be ignored when we check IsDPCUsable which @@ -55,6 +56,7 @@ func (m *DpcManager) doAddDPC(ctx context.Context, dpc types.DevicePortConfig) { } func (m *DpcManager) doDelDPC(ctx context.Context, dpc types.DevicePortConfig) { + m.setDiscoveredWwanIfNames(&dpc) configChanged := m.updateDPCListAndPublish(dpc, true) if !configChanged { m.Log.Functionf("doDelDPC: System current. No change detected.\n") diff --git a/pkg/pillar/dpcmanager/dpcmanager.go b/pkg/pillar/dpcmanager/dpcmanager.go index 7adf3df0883..9dfd0cafcbd 100644 --- a/pkg/pillar/dpcmanager/dpcmanager.go +++ b/pkg/pillar/dpcmanager/dpcmanager.go @@ -406,7 +406,7 @@ func (m *DpcManager) run(ctx context.Context) { case WwanEventUndefined: m.Log.Warnf("Undefined event received from WwanWatcher") case WwanEventNewStatus: - m.reloadWwanStatus() + m.reloadWwanStatus(ctx) case WwanEventNewMetrics: m.reloadWwanMetrics() case WwanEventNewLocationInfo: diff --git a/pkg/pillar/dpcmanager/dpcmanager_test.go b/pkg/pillar/dpcmanager/dpcmanager_test.go index be444224bef..295237f3f89 100644 --- a/pkg/pillar/dpcmanager/dpcmanager_test.go +++ b/pkg/pillar/dpcmanager/dpcmanager_test.go @@ -122,7 +122,8 @@ func initTest(test *testing.T) *GomegaWithT { wwanWatcher = &MockWwanWatcher{} geoService = &MockGeoService{} connTester = &conntester.MockConnectivityTester{ - TestDuration: 2 * time.Second, + TestDuration: 2 * time.Second, + NetworkMonitor: networkMonitor, } dpcManager = &dpcmngr.DpcManager{ Log: logObj, @@ -488,12 +489,22 @@ func mockWwan0Status() types.WwanStatus { IMSI: "310180933695713", }, }, - Providers: []types.WwanProvider{ + CurrentProvider: types.WwanProvider{ + PLMN: "310-410", + Description: "AT&T", + CurrentServing: true, + }, + VisibleProviders: []types.WwanProvider{ { PLMN: "310-410", Description: "AT&T", CurrentServing: true, }, + { + PLMN: "231-02", + Description: "Telekom", + CurrentServing: false, + }, }, }, }, @@ -616,11 +627,14 @@ func makeDPC(key string, timePrio time.Time, intfs selectedIntfs) types.DevicePo }, WirelessCfg: types.WirelessConfig{ WType: types.WirelessTypeCellular, - Cellular: []types.CellConfig{ - { - APN: "apn", - LocationTracking: true, + Cellular: types.CellNetPortConfig{ + AccessPoints: []types.CellularAccessPoint{ + { + APN: "apn", + Activated: true, + }, }, + LocationTracking: true, }, }, }) @@ -1043,7 +1057,7 @@ func TestDNS(test *testing.T) { t.Expect(eth0State.NtpServers).To(HaveLen(1)) t.Expect(eth0State.NtpServers[0].String()).To(Equal("132.163.96.5")) t.Expect(eth0State.Subnet.String()).To(Equal("192.168.10.0/24")) - t.Expect(eth0State.MacAddr).To(Equal("02:00:00:00:00:01")) + t.Expect(eth0State.MacAddr.String()).To(Equal("02:00:00:00:00:01")) t.Expect(eth0State.Up).To(BeTrue()) t.Expect(eth0State.Type).To(BeEquivalentTo(types.NT_IPV4)) t.Expect(eth0State.Dhcp).To(BeEquivalentTo(types.DT_CLIENT)) @@ -1066,7 +1080,7 @@ func TestDNS(test *testing.T) { t.Expect(eth1State.NtpServers).To(HaveLen(1)) t.Expect(eth1State.NtpServers[0].String()).To(Equal("132.163.96.6")) t.Expect(eth1State.Subnet.String()).To(Equal("172.20.1.0/24")) - t.Expect(eth1State.MacAddr).To(Equal("02:00:00:00:00:02")) + t.Expect(eth1State.MacAddr.String()).To(Equal("02:00:00:00:00:02")) t.Expect(eth1State.Up).To(BeTrue()) t.Expect(eth1State.Type).To(BeEquivalentTo(types.NT_IPV4)) t.Expect(eth1State.Dhcp).To(BeEquivalentTo(types.DT_CLIENT)) @@ -1128,7 +1142,7 @@ func TestWireless(test *testing.T) { PhysAddrs: types.WwanPhysAddrs{ Interface: "wwan0", }, - Apns: []string{"apn"}, + APN: "apn", LocationTracking: true, }, }, @@ -1152,10 +1166,16 @@ func TestWireless(test *testing.T) { t.Expect(wwanDNS.Cellular.Module.Revision).To(Equal("SWI9X50C_01.08.04.00")) t.Expect(wwanDNS.Cellular.ConfigError).To(BeEmpty()) t.Expect(wwanDNS.Cellular.ProbeError).To(BeEmpty()) - t.Expect(wwanDNS.Cellular.Providers).To(HaveLen(1)) - t.Expect(wwanDNS.Cellular.Providers[0].Description).To(Equal("AT&T")) - t.Expect(wwanDNS.Cellular.Providers[0].CurrentServing).To(BeTrue()) - t.Expect(wwanDNS.Cellular.Providers[0].PLMN).To(Equal("310-410")) + t.Expect(wwanDNS.Cellular.CurrentProvider.Description).To(Equal("AT&T")) + t.Expect(wwanDNS.Cellular.CurrentProvider.CurrentServing).To(BeTrue()) + t.Expect(wwanDNS.Cellular.CurrentProvider.PLMN).To(Equal("310-410")) + t.Expect(wwanDNS.Cellular.VisibleProviders).To(HaveLen(2)) + t.Expect(wwanDNS.Cellular.VisibleProviders[0].Description).To(Equal("AT&T")) + t.Expect(wwanDNS.Cellular.VisibleProviders[0].CurrentServing).To(BeTrue()) + t.Expect(wwanDNS.Cellular.VisibleProviders[0].PLMN).To(Equal("310-410")) + t.Expect(wwanDNS.Cellular.VisibleProviders[1].Description).To(Equal("Telekom")) + t.Expect(wwanDNS.Cellular.VisibleProviders[1].CurrentServing).To(BeFalse()) + t.Expect(wwanDNS.Cellular.VisibleProviders[1].PLMN).To(Equal("231-02")) t.Expect(wwanDNS.Cellular.SimCards).To(HaveLen(1)) t.Expect(wwanDNS.Cellular.SimCards[0].Name).To(Equal("89012703578345957137")) // ICCID put by DoSanitize() t.Expect(wwanDNS.Cellular.SimCards[0].ICCID).To(Equal("89012703578345957137")) @@ -1718,7 +1738,7 @@ func TestVlansAndBonds(test *testing.T) { t.Expect(eth0State.DNSServers).To(BeEmpty()) t.Expect(eth0State.NtpServers).To(BeEmpty()) t.Expect(eth0State.Subnet.IP).To(BeNil()) - t.Expect(eth0State.MacAddr).To(Equal("02:00:00:00:00:01")) + t.Expect(eth0State.MacAddr.String()).To(Equal("02:00:00:00:00:01")) t.Expect(eth0State.Up).To(BeTrue()) t.Expect(eth0State.Type).To(BeEquivalentTo(types.NT_NOOP)) t.Expect(eth0State.Dhcp).To(BeEquivalentTo(types.DT_NOOP)) @@ -1735,7 +1755,7 @@ func TestVlansAndBonds(test *testing.T) { t.Expect(eth1State.DNSServers).To(BeEmpty()) t.Expect(eth1State.NtpServers).To(BeEmpty()) t.Expect(eth1State.Subnet.IP).To(BeNil()) - t.Expect(eth1State.MacAddr).To(Equal("02:00:00:00:00:02")) + t.Expect(eth1State.MacAddr.String()).To(Equal("02:00:00:00:00:02")) t.Expect(eth1State.Up).To(BeTrue()) t.Expect(eth1State.Type).To(BeEquivalentTo(types.NT_NOOP)) t.Expect(eth1State.Dhcp).To(BeEquivalentTo(types.DT_NOOP)) @@ -1751,7 +1771,7 @@ func TestVlansAndBonds(test *testing.T) { t.Expect(bond0State.DNSServers).To(BeEmpty()) t.Expect(bond0State.NtpServers).To(BeEmpty()) t.Expect(bond0State.Subnet.IP).To(BeNil()) - t.Expect(bond0State.MacAddr).To(Equal("02:00:00:00:00:03")) + t.Expect(bond0State.MacAddr.String()).To(Equal("02:00:00:00:00:03")) t.Expect(bond0State.Up).To(BeTrue()) t.Expect(bond0State.Type).To(BeEquivalentTo(types.NT_NOOP)) t.Expect(bond0State.Dhcp).To(BeEquivalentTo(types.DT_NOOP)) @@ -1770,7 +1790,7 @@ func TestVlansAndBonds(test *testing.T) { t.Expect(vlan100State.NtpServers).To(HaveLen(1)) t.Expect(vlan100State.NtpServers[0].String()).To(Equal("132.163.96.5")) t.Expect(vlan100State.Subnet.String()).To(Equal("192.168.10.0/24")) - t.Expect(vlan100State.MacAddr).To(Equal("02:00:00:00:00:04")) + t.Expect(vlan100State.MacAddr.String()).To(Equal("02:00:00:00:00:04")) t.Expect(vlan100State.Up).To(BeTrue()) t.Expect(vlan100State.Type).To(BeEquivalentTo(types.NT_IPV4)) t.Expect(vlan100State.Dhcp).To(BeEquivalentTo(types.DT_CLIENT)) @@ -1790,7 +1810,7 @@ func TestVlansAndBonds(test *testing.T) { t.Expect(vlan200State.NtpServers).To(HaveLen(1)) t.Expect(vlan200State.NtpServers[0].String()).To(Equal("132.163.96.6")) t.Expect(vlan200State.Subnet.String()).To(Equal("172.20.1.0/24")) - t.Expect(vlan200State.MacAddr).To(Equal("02:00:00:00:00:05")) + t.Expect(vlan200State.MacAddr.String()).To(Equal("02:00:00:00:00:05")) t.Expect(vlan200State.Up).To(BeTrue()) t.Expect(vlan200State.Type).To(BeEquivalentTo(types.NT_IPV4)) t.Expect(vlan200State.Dhcp).To(BeEquivalentTo(types.DT_CLIENT)) diff --git a/pkg/pillar/dpcmanager/verify.go b/pkg/pillar/dpcmanager/verify.go index c81b758d3b3..aebcf7fd46b 100644 --- a/pkg/pillar/dpcmanager/verify.go +++ b/pkg/pillar/dpcmanager/verify.go @@ -63,6 +63,9 @@ func (m *DpcManager) setupVerify(index int, reason string) { m.dpcList.CurrentIndex = index m.dpcVerify.inProgress = true m.dpcVerify.startedAt = time.Now() + if dpc := m.currentDPC(); dpc != nil { + m.setDiscoveredWwanIfNames(dpc) + } m.Log.Functionf("DPC verify: Started testing DPC (index %d): %v", m.dpcList.CurrentIndex, m.dpcList.PortConfigList[m.dpcList.CurrentIndex]) } diff --git a/pkg/pillar/dpcmanager/wwan.go b/pkg/pillar/dpcmanager/wwan.go index cc54b41e7c0..6ba850f7476 100644 --- a/pkg/pillar/dpcmanager/wwan.go +++ b/pkg/pillar/dpcmanager/wwan.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "os" - "reflect" "strings" "time" @@ -142,7 +141,7 @@ func (w *wwanWatcher) createWwanDir() error { } // reloadWwanStatus loads the latest state data published by the wwan service. -func (m *DpcManager) reloadWwanStatus() { +func (m *DpcManager) reloadWwanStatus(ctx context.Context) { status, err := m.WwanWatcher.LoadStatus() if err != nil { // Already logged. @@ -163,7 +162,7 @@ func (m *DpcManager) reloadWwanStatus() { } status.DoSanitize() - changed := !reflect.DeepEqual(m.wwanStatus, status) + changed := !m.wwanStatus.Equal(status) if changed { m.Log.Functionf("Have new wwan status: %v", m.wwanStatus) } @@ -203,6 +202,13 @@ func (m *DpcManager) reloadWwanStatus() { } if changed || wasInProgress { + if m.currentDPC() != nil { + changedDPC := m.setDiscoveredWwanIfNames(m.currentDPC()) + if changedDPC { + m.publishDPCL() + } + } + m.restartVerify(ctx, "wwan status changed") m.updateDNS() } if changed && m.PubWwanStatus != nil { @@ -222,7 +228,7 @@ func (m *DpcManager) reloadWwanMetrics() { // Already logged. return } - if reflect.DeepEqual(metrics, m.wwanMetrics) { + if m.wwanMetrics.Equal(metrics) { // nothing really changed return } @@ -342,3 +348,62 @@ func (m *DpcManager) doUpdateRadioSilence(ctx context.Context, newRS types.Radio m.rsStatus.ConfigError = strings.Join(errMsgs, "\n") m.updateDNS() } + +// Handle cellular modems referenced in the device model by USB or PCI addresses +// but without interface name included. +// Use status published by the wwan microservice to learn the name of the interface +// created by the kernel for the modem data-path. +func (m *DpcManager) setDiscoveredWwanIfNames(dpc *types.DevicePortConfig) bool { + var changed bool + ifNames := make(map[string]string) // interface name -> logical label + currentDPC := m.currentDPC() + for i := range dpc.Ports { + port := &dpc.Ports[i] + if port.WirelessCfg.WType != types.WirelessTypeCellular { + continue + } + wwanNetStatus, found := m.wwanStatus.LookupNetworkStatus(port.Logicallabel) + if found && wwanNetStatus.PhysAddrs.Interface != "" { + ifNames[wwanNetStatus.PhysAddrs.Interface] = port.Logicallabel + if port.IfName != wwanNetStatus.PhysAddrs.Interface { + changed = true + } + } else if port.IfName == "" && currentDPC != nil && currentDPC != dpc { + // Maybe we received new DPC while modem status is not yet available. + // See if we can get interface name from the current DPC. + currentPortConfig := currentDPC.LookupPortByLogicallabel(port.Logicallabel) + if currentPortConfig != nil && currentPortConfig.IfName != "" && + currentPortConfig.USBAddr == port.USBAddr && + currentPortConfig.PCIAddr == port.PCIAddr { + if _, used := ifNames[currentPortConfig.IfName]; !used { + ifNames[currentPortConfig.IfName] = port.Logicallabel + changed = true + } + } + } + } + if !changed { + return false + } + updatedPorts := make([]types.NetworkPortConfig, len(dpc.Ports)) + // First see if any wwan modem has changed interface name. + for i := range dpc.Ports { + port := &dpc.Ports[i] + updatedPorts[i] = *port // copy + if port.IfName != "" { + if port2 := ifNames[port.IfName]; port2 != "" && port2 != port.Logicallabel { + // This interface name was taken by port2. + updatedPorts[i].IfName = "" + m.Log.Noticef("Interface name %s was taken from port %s by port %s", + port.IfName, port.Logicallabel, port2) + } + } + for ifName, port2 := range ifNames { + if port.Logicallabel == port2 { + updatedPorts[i].IfName = ifName + } + } + } + dpc.Ports = updatedPorts + return true +} diff --git a/pkg/pillar/dpcreconciler/genericitems/wwan.go b/pkg/pillar/dpcreconciler/genericitems/wwan.go index 57887ea1aef..a81dd6824fe 100644 --- a/pkg/pillar/dpcreconciler/genericitems/wwan.go +++ b/pkg/pillar/dpcreconciler/genericitems/wwan.go @@ -54,22 +54,10 @@ func (w Wwan) String() string { return fmt.Sprintf("WWAN configuration: %+v", w.Config) } -// Dependencies lists every adapter referenced from the wwan config -// as a dependency. +// Dependencies return empty list - wwan config file can be created even before +// the referenced wwanX interface(s) are ready (the wwan microservice can deal with it). func (w Wwan) Dependencies() (deps []depgraph.Dependency) { - for _, network := range w.Config.Networks { - if network.PhysAddrs.Interface == "" { - continue - } - deps = append(deps, depgraph.Dependency{ - RequiredItem: depgraph.ItemRef{ - ItemType: AdapterTypename, - ItemName: network.PhysAddrs.Interface, - }, - Description: "The referenced (LTE) adapter must exist", - }) - } - return deps + return nil } // WwanConfigurator implements Configurator interface (libs/reconciler) for WWAN config. diff --git a/pkg/pillar/dpcreconciler/linux.go b/pkg/pillar/dpcreconciler/linux.go index 5de1345f11f..4da37a94fae 100644 --- a/pkg/pillar/dpcreconciler/linux.go +++ b/pkg/pillar/dpcreconciler/linux.go @@ -646,10 +646,10 @@ func (r *LinuxDpcReconciler) updateCurrentPhysicalIO( dpc types.DevicePortConfig, aa types.AssignableAdapters) (changed bool) { currentIO := dg.New(dg.InitArgs{Name: PhysicalIoSG}) for _, port := range dpc.Ports { - if port.L2Type != types.L2LinkTypeNone { + if port.L2Type != types.L2LinkTypeNone || port.IfName == "" { continue } - ioBundle := aa.LookupIoBundleIfName(port.IfName) + ioBundle := aa.LookupIoBundleLogicallabel(port.Logicallabel) if ioBundle != nil && ioBundle.IsPCIBack { // Until confirmed by domainmgr that the interface is out of PCIBack // and ready, pretend that it doesn't exist. This is because domainmgr @@ -660,6 +660,9 @@ func (r *LinuxDpcReconciler) updateCurrentPhysicalIO( // entries in AssignableAdapters should not be ignored. continue } + if port.IfName == "" { + continue + } _, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) if err != nil { r.Log.Errorf("updateCurrentPhysicalIO: failed to get ifIndex for %s: %v", @@ -690,7 +693,7 @@ func (r *LinuxDpcReconciler) updateCurrentAdapterAddrs( sgPath := dg.NewSubGraphPath(L3SG, AdaptersSG, AdapterAddrsSG) currentAddrs := dg.New(dg.InitArgs{Name: AdapterAddrsSG}) for _, port := range dpc.Ports { - if !port.IsL3Port { + if !port.IsL3Port || port.IfName == "" { continue } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) @@ -740,6 +743,9 @@ func (r *LinuxDpcReconciler) updateCurrentRoutes(dpc types.DevicePortConfig) (ch } } for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) if err != nil { r.Log.Errorf("updateCurrentRoutes: failed to get ifIndex for %s: %v", @@ -830,7 +836,7 @@ func (r *LinuxDpcReconciler) getIntendedGlobalCfg(dpc types.DevicePortConfig) dg // Intended content of /etc/resolv.conf dnsServers := make(map[string][]net.IP) for _, port := range dpc.Ports { - if !port.IsMgmt { + if !port.IsMgmt || port.IfName == "" { continue } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) @@ -861,6 +867,9 @@ func (r *LinuxDpcReconciler) getIntendedPhysicalIO(dpc types.DevicePortConfig) d } intendedIO := dg.New(graphArgs) for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } if port.L2Type == types.L2LinkTypeNone { intendedIO.PutItem(generic.PhysIf{ LogicalLabel: port.Logicallabel, @@ -878,6 +887,9 @@ func (r *LinuxDpcReconciler) getIntendedLogicalIO(dpc types.DevicePortConfig) dg } intendedIO := dg.New(graphArgs) for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } switch port.L2Type { case types.L2LinkTypeVLAN: parent := dpc.LookupPortByLogicallabel(port.VLAN.ParentPort) @@ -962,7 +974,7 @@ func (r *LinuxDpcReconciler) getIntendedAdapters(dpc types.DevicePortConfig) dg. } intendedAdapters := dg.New(graphArgs) for _, port := range dpc.Ports { - if !port.IsL3Port { + if !port.IsL3Port || port.IfName == "" { continue } adapter := linux.Adapter{ @@ -1010,6 +1022,9 @@ func (r *LinuxDpcReconciler) getIntendedSrcIPRules(dpc types.DevicePortConfig) d } intendedRules := dg.New(graphArgs) for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) if err != nil { r.Log.Errorf("getIntendedSrcIPRules: failed to get ifIndex for %s: %v", @@ -1066,6 +1081,9 @@ func (r *LinuxDpcReconciler) getIntendedRoutes(dpc types.DevicePortConfig) dg.Gr } } for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) if err != nil { r.Log.Errorf("getIntendedRoutes: failed to get ifIndex for %s: %v", @@ -1133,6 +1151,9 @@ type portAddr struct { func (r *LinuxDpcReconciler) groupPortAddrs(dpc types.DevicePortConfig) map[string][]portAddr { arpGroups := map[string][]portAddr{} for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } ifIndex, found, err := r.NetworkMonitor.GetInterfaceIndex(port.IfName) if err != nil { r.Log.Errorf("groupPortAddrs: failed to get ifIndex for %s: %v", @@ -1217,8 +1238,17 @@ func (r *LinuxDpcReconciler) getIntendedWirelessCfg(dpc types.DevicePortConfig, rsImposed := radioSilence.Imposed intendedWirelessCfg.PutItem( r.getIntendedWlanConfig(dpc, rsImposed), nil) - intendedWirelessCfg.PutItem( - r.getIntendedWwanConfig(dpc, aa, rsImposed), nil) + if dpc.Key != "" { + // Do not send config to wwan microservice until we receive DPC. + // The default behaviour of wwan microservice (i.e. without config) is to disable + // radios of all cellular modems, which is the same effect we would get with empty + // config anyway. However, without config the wwan microservice does just that + // and does not waste time collecting e.g. modem state data. This is important + // because when the DPC arrives, wwan microservice won't be blocked on retrieving + // some state data but will be ready to apply the config immediately. + intendedWirelessCfg.PutItem( + r.getIntendedWwanConfig(dpc, aa, rsImposed), nil) + } return intendedWirelessCfg } @@ -1307,38 +1337,66 @@ func (r *LinuxDpcReconciler) getWifiCredentials(wifi types.WifiConfig) (types.En func (r *LinuxDpcReconciler) getIntendedWwanConfig(dpc types.DevicePortConfig, aa types.AssignableAdapters, radioSilence bool) dg.Item { - config := types.WwanConfig{RadioSilence: radioSilence, Networks: []types.WwanNetworkConfig{}} - + config := types.WwanConfig{ + RadioSilence: radioSilence, + Networks: []types.WwanNetworkConfig{}, + } for _, port := range dpc.Ports { - if port.WirelessCfg.WType != types.WirelessTypeCellular || len(port.WirelessCfg.Cellular) == 0 { + if port.WirelessCfg.WType != types.WirelessTypeCellular { continue } - ioBundle := aa.LookupIoBundleLogicallabel(port.Logicallabel) - if ioBundle == nil { - r.Log.Warnf("Failed to find adapter with logical label '%s'", port.Logicallabel) - continue + if !aa.Initialized { + r.Log.Warnf("getIntendedWwanConfig: AA is not yet initialized, "+ + "skipping IsPCIBack check for port %s", port.Logicallabel) + } else { + ioBundle := aa.LookupIoBundleLogicallabel(port.Logicallabel) + if ioBundle == nil { + r.Log.Warnf("Failed to find adapter with logical label '%s'", + port.Logicallabel) + continue + } + if ioBundle.IsPCIBack { + r.Log.Warnf("getIntendedWwanConfig: wwan adapter with the logical label "+ + "'%s' is assigned to pciback, skipping", port.Logicallabel) + continue + } } - if ioBundle.IsPCIBack { - r.Log.Warnf("wwan adapter with the logical label '%s' is assigned to pciback, skipping", - port.Logicallabel) + var accessPoint *types.CellularAccessPoint + for _, ap := range port.WirelessCfg.Cellular.AccessPoints { + if ap.Activated { + accessPoint = &ap + break + } + } + if accessPoint == nil { + r.Log.Warnf("getIntendedWwanConfig: no activated access point for port %s, "+ + "skipping", port.Logicallabel) continue } - // XXX Limited to a single APN for now - cellCfg := port.WirelessCfg.Cellular[0] + // Prefer USB and PCI addresses over interface name. + // Plus we want to avoid changing /run/wwan/config.json just to put there + // discovered interface name (it is the wwan microservice that discovered it anyway + // and changing config.json would just trigger many redundant operations inside + // of that microservice) + var physAddress types.WwanPhysAddrs + if port.USBAddr != "" || port.PCIAddr != "" { + physAddress.USB = port.USBAddr + physAddress.PCI = port.PCIAddr + } else { + physAddress.Interface = port.IfName + } network := types.WwanNetworkConfig{ - LogicalLabel: port.Logicallabel, - PhysAddrs: types.WwanPhysAddrs{ - Interface: ioBundle.Ifname, - USB: ioBundle.UsbAddr, - PCI: ioBundle.PciLong, - }, - Apns: []string{cellCfg.APN}, - Proxies: port.Proxies, - Probe: types.WwanProbe{ - Disable: cellCfg.DisableProbe, - Address: cellCfg.ProbeAddr, - }, - LocationTracking: cellCfg.LocationTracking, + // TODO: Username + Password (will be done in the next PR) + LogicalLabel: port.Logicallabel, + PhysAddrs: physAddress, + SIMSlot: accessPoint.SIMSlot, + APN: accessPoint.APN, + PreferredPLMNs: accessPoint.PreferredPLMNs, + PreferredRATs: accessPoint.PreferredRATs, + ForbidRoaming: accessPoint.ForbidRoaming, + Proxies: port.Proxies, + Probe: port.WirelessCfg.Cellular.Probe, + LocationTracking: port.WirelessCfg.Cellular.LocationTracking, } config.Networks = append(config.Networks, network) } @@ -1651,6 +1709,9 @@ func (r *LinuxDpcReconciler) getIntendedACLs( // i.e. these rules are below protoMarkV4Rules/protoMarkV6Rules var dropMarkRules []iptables.Rule for _, port := range dpc.Ports { + if port.IfName == "" { + continue + } dropIngressRule := iptables.Rule{ RuleLabel: fmt.Sprintf("Ingress from %s", port.IfName), MatchOpts: []string{"-i", port.IfName}, diff --git a/pkg/pillar/dpcreconciler/linux_test.go b/pkg/pillar/dpcreconciler/linux_test.go index dde66f3ae70..1ed99dff14a 100644 --- a/pkg/pillar/dpcreconciler/linux_test.go +++ b/pkg/pillar/dpcreconciler/linux_test.go @@ -579,7 +579,7 @@ func TestWireless(test *testing.T) { }, }, { - IfName: "wwan0", + USBAddr: "3:7.4", Phylabel: "wwan0", Logicallabel: "mock-wwan0", IsMgmt: true, @@ -590,9 +590,12 @@ func TestWireless(test *testing.T) { }, WirelessCfg: types.WirelessConfig{ WType: types.WirelessTypeCellular, - Cellular: []types.CellConfig{ - { - APN: "my-apn", + Cellular: types.CellNetPortConfig{ + AccessPoints: []types.CellularAccessPoint{ + { + APN: "my-apn", + Activated: true, + }, }, }, }, @@ -619,7 +622,7 @@ func TestWireless(test *testing.T) { Logicallabel: "mock-wwan0", Usage: evecommon.PhyIoMemberUsage_PhyIoUsageMgmtAndApps, Cost: 0, - Ifname: "wwan0", + UsbAddr: "3:7.4", MacAddr: wwan0Mac, IsPCIBack: false, IsPort: true, @@ -641,10 +644,23 @@ func TestWireless(test *testing.T) { t.Expect(itemDescription(wlan)).ToNot(ContainSubstring("my-password")) t.Expect(itemDescription(wlan)).To(ContainSubstring("enable RF: true")) wwan := dg.Reference(generic.Wwan{}) - t.Expect(itemDescription(wwan)).To(ContainSubstring("Apns:[my-apn]")) - t.Expect(itemDescription(wwan)).To(ContainSubstring("Interface:wwan0")) + t.Expect(itemDescription(wwan)).To(ContainSubstring("SIMSlot:0 APN:my-apn")) + t.Expect(itemDescription(wwan)).To(ContainSubstring("PhysAddrs:{Interface: USB:3:7.4 PCI: Dev:}")) t.Expect(itemDescription(wwan)).To(ContainSubstring("LogicalLabel:mock-wwan0")) t.Expect(itemDescription(wwan)).To(ContainSubstring("RadioSilence:false")) + t.Expect(itemCountWithType(generic.IOHandleTypename)).To(Equal(1)) + t.Expect(itemCountWithType(generic.AdapterTypename)).To(Equal(1)) + t.Expect(itemCountWithType(generic.AdapterAddrsTypename)).To(Equal(1)) + t.Expect(itemCountWithType(generic.DhcpcdTypename)).To(Equal(1)) + t.Expect(itemCountWithType(generic.IPv4RouteTypename)).To(Equal(0)) + t.Expect(itemCountWithType(generic.ArpTypename)).To(Equal(0)) + + // Simulate that DPC Manager learnt the wwan interface name. + dpc.Ports = []types.NetworkPortConfig{dpc.Ports[0], dpc.Ports[1]} // copy + dpc.Ports[1].IfName = "wwan0" + ctx = reconciler.MockRun(context.Background()) + status = dpcReconciler.Reconcile(ctx, dpcrec.Args{GCP: *gcp, DPC: dpc, AA: aa}) + t.Expect(status.Error).To(BeNil()) t.Expect(itemCountWithType(generic.IOHandleTypename)).To(Equal(2)) t.Expect(itemCountWithType(generic.AdapterTypename)).To(Equal(2)) t.Expect(itemCountWithType(generic.AdapterAddrsTypename)).To(Equal(2)) diff --git a/pkg/pillar/iptables/configitem.go b/pkg/pillar/iptables/configitem.go index 1dc6ca4e530..72b5c3c2ee5 100644 --- a/pkg/pillar/iptables/configitem.go +++ b/pkg/pillar/iptables/configitem.go @@ -16,7 +16,7 @@ import ( "github.com/lf-edge/eve/libs/reconciler" "github.com/lf-edge/eve/pkg/pillar/base" "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" ) const ( @@ -192,9 +192,9 @@ func (r Rule) Equal(other depgraph.Item) bool { r.Table == r2.Table && r.ChainName == r2.ChainName && r.ForIPv6 == r2.ForIPv6 && - utils.EqualSets(r.AppliedBefore, r2.AppliedBefore) && - utils.EqualLists(r.MatchOpts, r2.MatchOpts) && - utils.EqualLists(r.TargetOpts, r2.TargetOpts) && + generics.EqualSets(r.AppliedBefore, r2.AppliedBefore) && + generics.EqualLists(r.MatchOpts, r2.MatchOpts) && + generics.EqualLists(r.TargetOpts, r2.TargetOpts) && r.Target == r2.Target && r.Description == r2.Description } diff --git a/pkg/pillar/netmonitor/linux.go b/pkg/pillar/netmonitor/linux.go index 31eff967a42..43d1d979d80 100644 --- a/pkg/pillar/netmonitor/linux.go +++ b/pkg/pillar/netmonitor/linux.go @@ -154,6 +154,7 @@ func (m *LinuxNetworkMonitor) ifAttrsFromLink(link netlink.Link) IfAttrs { LowerUp: link.Attrs().OperState == netlink.OperUp, Enslaved: link.Attrs().MasterIndex != 0, MasterIfIndex: link.Attrs().MasterIndex, + MTU: uint16(link.Attrs().MTU), } } diff --git a/pkg/pillar/netmonitor/netmonitor.go b/pkg/pillar/netmonitor/netmonitor.go index bdcc129b8ed..11e4fa53ba6 100644 --- a/pkg/pillar/netmonitor/netmonitor.go +++ b/pkg/pillar/netmonitor/netmonitor.go @@ -146,6 +146,8 @@ type IfAttrs struct { Enslaved bool // If interface is enslaved, this should contain index of the master interface. MasterIfIndex int + // Maximum Transmission Unit configured on the interface. + MTU uint16 } // Equal allows to compare two sets of interface attributes for equality. diff --git a/pkg/pillar/nireconciler/genericitems/dnsmasq.go b/pkg/pillar/nireconciler/genericitems/dnsmasq.go index 8d9823fe33f..b1747f7fcf5 100644 --- a/pkg/pillar/nireconciler/genericitems/dnsmasq.go +++ b/pkg/pillar/nireconciler/genericitems/dnsmasq.go @@ -19,7 +19,8 @@ import ( "github.com/lf-edge/eve/libs/reconciler" "github.com/lf-edge/eve/pkg/pillar/base" "github.com/lf-edge/eve/pkg/pillar/devicenetwork" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" ) @@ -82,16 +83,16 @@ func (d DHCPServer) String() string { // Equal compares two DHCPServer instances func (d DHCPServer) Equal(d2 DHCPServer, withStaticEntries bool) bool { - return utils.EqualIPNets(d.Subnet, d2.Subnet) && + return netutils.EqualIPNets(d.Subnet, d2.Subnet) && d.AllOnesNetmask == d2.AllOnesNetmask && - utils.EqualIPs(d.IPRange.FromIP, d2.IPRange.FromIP) && - utils.EqualIPs(d.IPRange.ToIP, d2.IPRange.ToIP) && - utils.EqualIPs(d.GatewayIP, d2.GatewayIP) && + netutils.EqualIPs(d.IPRange.FromIP, d2.IPRange.FromIP) && + netutils.EqualIPs(d.IPRange.ToIP, d2.IPRange.ToIP) && + netutils.EqualIPs(d.GatewayIP, d2.GatewayIP) && d.DomainName == d2.DomainName && - utils.EqualSetsFn(d.DNSServers, d2.DNSServers, utils.EqualIPs) && - utils.EqualSetsFn(d.NTPServers, d2.NTPServers, utils.EqualIPs) && + generics.EqualSetsFn(d.DNSServers, d2.DNSServers, netutils.EqualIPs) && + generics.EqualSetsFn(d.NTPServers, d2.NTPServers, netutils.EqualIPs) && (!withStaticEntries || - utils.EqualSetsFn(d.StaticEntries, d2.StaticEntries, equalMACToIP)) + generics.EqualSetsFn(d.StaticEntries, d2.StaticEntries, equalMACToIP)) } // DNSServer : part of the dnsmasq config specific to DNS server. @@ -125,12 +126,12 @@ func (d DNSServer) String() string { // Equal compares two DNSServer instances func (d DNSServer) Equal(d2 DNSServer, withStaticEntries bool) bool { - return utils.EqualIPs(d.ListenIP, d2.ListenIP) && + return netutils.EqualIPs(d.ListenIP, d2.ListenIP) && d.UplinkIf == d2.UplinkIf && - utils.EqualSetsFn(d.UpstreamServers, d2.UpstreamServers, utils.EqualIPs) && - utils.EqualSetsFn(d.LinuxIPSets, d2.LinuxIPSets, equalLinuxIPSet) && + generics.EqualSetsFn(d.UpstreamServers, d2.UpstreamServers, netutils.EqualIPs) && + generics.EqualSetsFn(d.LinuxIPSets, d2.LinuxIPSets, equalLinuxIPSet) && (!withStaticEntries || - utils.EqualSetsFn(d.StaticEntries, d2.StaticEntries, equalHostnameToIP)) + generics.EqualSetsFn(d.StaticEntries, d2.StaticEntries, equalHostnameToIP)) } // IPRange : a range of IP addresses. @@ -150,7 +151,7 @@ type MACToIP struct { func equalMACToIP(a, b MACToIP) bool { return bytes.Equal(a.MAC, b.MAC) && - utils.EqualIPs(a.IP, b.IP) && + netutils.EqualIPs(a.IP, b.IP) && a.Hostname == b.Hostname } @@ -162,7 +163,7 @@ type HostnameToIP struct { func equalHostnameToIP(a, b HostnameToIP) bool { return a.Hostname == b.Hostname && - utils.EqualIPs(a.IP, b.IP) + netutils.EqualIPs(a.IP, b.IP) } // LinuxIPSet : see https://www.netfilter.org/projects/ipset/index.html @@ -174,8 +175,8 @@ type LinuxIPSet struct { } func equalLinuxIPSet(a, b LinuxIPSet) bool { - return utils.EqualSets(a.Domains, b.Domains) && - utils.EqualSets(a.Sets, b.Sets) + return generics.EqualSets(a.Domains, b.Domains) && + generics.EqualSets(a.Sets, b.Sets) } // NetworkIf : network interface used by dnsmasq. @@ -351,7 +352,7 @@ func (c *DnsmasqConfigurator) Modify(ctx context.Context, oldItem, newItem dg.It if !isDnsmasq { return fmt.Errorf("invalid item type %T, expected Dnsmasq", newItem) } - obsoleteDHCPHosts, newDHCPHosts := utils.DiffSetsFn( + obsoleteDHCPHosts, newDHCPHosts := generics.DiffSetsFn( oldDnsmasq.DHCPServer.StaticEntries, newDnsmasq.DHCPServer.StaticEntries, equalMACToIP) for _, host := range obsoleteDHCPHosts { @@ -364,7 +365,7 @@ func (c *DnsmasqConfigurator) Modify(ctx context.Context, oldItem, newItem dg.It return err } } - obsoleteDNSHosts, newDNSHosts := utils.DiffSetsFn( + obsoleteDNSHosts, newDNSHosts := generics.DiffSetsFn( oldDnsmasq.DNSServer.StaticEntries, newDnsmasq.DNSServer.StaticEntries, equalHostnameToIP) for _, host := range obsoleteDNSHosts { diff --git a/pkg/pillar/nireconciler/genericitems/httpsrv.go b/pkg/pillar/nireconciler/genericitems/httpsrv.go index 4bcdeac8777..a02a42838d1 100644 --- a/pkg/pillar/nireconciler/genericitems/httpsrv.go +++ b/pkg/pillar/nireconciler/genericitems/httpsrv.go @@ -19,7 +19,7 @@ import ( "github.com/lf-edge/eve/libs/reconciler" "github.com/lf-edge/eve/pkg/pillar/agentlog" "github.com/lf-edge/eve/pkg/pillar/base" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" ) @@ -68,7 +68,7 @@ func (s HTTPServer) Type() string { func (s HTTPServer) Equal(other dg.Item) bool { s2 := other.(HTTPServer) return s.ForNI == s2.ForNI && - utils.EqualIPs(s.ListenIP, s2.ListenIP) && + netutils.EqualIPs(s.ListenIP, s2.ListenIP) && s.ListenIf == s2.ListenIf && s.Port == s2.Port } diff --git a/pkg/pillar/nireconciler/genericitems/ipreserve.go b/pkg/pillar/nireconciler/genericitems/ipreserve.go index a86c7d2f320..2dac6c70038 100644 --- a/pkg/pillar/nireconciler/genericitems/ipreserve.go +++ b/pkg/pillar/nireconciler/genericitems/ipreserve.go @@ -11,7 +11,7 @@ import ( dg "github.com/lf-edge/eve/libs/depgraph" "github.com/lf-edge/eve/pkg/pillar/base" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" ) // IPReserve : an item representing allocation and use of an IP address (for bridge). @@ -50,7 +50,7 @@ func (ip IPReserve) Equal(other dg.Item) bool { return false } return ip.NetIf == ip2.NetIf && - utils.EqualIPNets(ip.AddrWithMask, ip2.AddrWithMask) + netutils.EqualIPNets(ip.AddrWithMask, ip2.AddrWithMask) } // External returns false - not used for IPs assigned by NIM. @@ -92,7 +92,7 @@ func (c *IPReserveConfigurator) Delete(ctx context.Context, item dg.Item) error } // NeedsRecreate returns true - change in IPReserve.NetIf usage intentionally triggers -// recreate which cascades to the bridge and other dependent higher-layer items. +// recreate which cascades to the bridge and other dependant higher-layer items. func (c *IPReserveConfigurator) NeedsRecreate(oldItem, newItem dg.Item) (recreate bool) { return true } diff --git a/pkg/pillar/nireconciler/genericitems/uplink.go b/pkg/pillar/nireconciler/genericitems/uplink.go index a1f1870b9e2..a705f2cf5aa 100644 --- a/pkg/pillar/nireconciler/genericitems/uplink.go +++ b/pkg/pillar/nireconciler/genericitems/uplink.go @@ -8,7 +8,8 @@ import ( "net" dg "github.com/lf-edge/eve/libs/depgraph" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" ) // Uplink : uplink interface used by network instance for connectivity to outside networks. @@ -51,7 +52,7 @@ func (u Uplink) Equal(other dg.Item) bool { u.LogicalLabel == u2.LogicalLabel && u.MasterIfName == u2.MasterIfName && u.AdminUp == u2.AdminUp && - utils.EqualSetsFn(u.IPAddresses, u2.IPAddresses, utils.EqualIPNets) + generics.EqualSetsFn(u.IPAddresses, u2.IPAddresses, netutils.EqualIPNets) } // External returns true - uplinks are physical interfaces, i.e. not created by zedrouter. diff --git a/pkg/pillar/nireconciler/linux_acl.go b/pkg/pillar/nireconciler/linux_acl.go index d1c64e105c9..71bbc423c4b 100644 --- a/pkg/pillar/nireconciler/linux_acl.go +++ b/pkg/pillar/nireconciler/linux_acl.go @@ -13,7 +13,7 @@ import ( "github.com/lf-edge/eve/pkg/pillar/devicenetwork" "github.com/lf-edge/eve/pkg/pillar/iptables" "github.com/lf-edge/eve/pkg/pillar/types" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" uuid "github.com/satori/go.uuid" ) @@ -497,7 +497,7 @@ func (r *LinuxNIReconciler) getIntendedAppConnACLs(niID uuid.UUID, "%s: getIntendedAppConnACLs: failed to get uplink %s addresses: %v", LogAndErrPrefix, uplink, err) } - uplinkIPs = utils.FilterList(uplinkIPs, func(ipNet *net.IPNet) bool { + uplinkIPs = generics.FilterList(uplinkIPs, func(ipNet *net.IPNet) bool { return ipNet.IP.IsGlobalUnicast() }) } @@ -509,7 +509,7 @@ func (r *LinuxNIReconciler) getIntendedAppConnACLs(niID uuid.UUID, continue } } - uplinkIPvXs := utils.FilterList(uplinkIPs, func(ipNet *net.IPNet) bool { + uplinkIPvXs := generics.FilterList(uplinkIPs, func(ipNet *net.IPNet) bool { return (ipNet.IP.To4() == nil) == ipv6 }) for _, item := range r.getIntendedAppConnRawIptables(vif, ul, ipv6) { diff --git a/pkg/pillar/nireconciler/linux_config.go b/pkg/pillar/nireconciler/linux_config.go index c94a8c0be3c..7cc683db71e 100644 --- a/pkg/pillar/nireconciler/linux_config.go +++ b/pkg/pillar/nireconciler/linux_config.go @@ -19,7 +19,8 @@ import ( generic "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" linux "github.com/lf-edge/eve/pkg/pillar/nireconciler/linuxitems" "github.com/lf-edge/eve/pkg/pillar/types" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" "github.com/vishvananda/netlink" ) @@ -805,7 +806,7 @@ func (r *LinuxNIReconciler) getIntendedDnsmasqCfg(niID uuid.UUID) (items []dg.It if ni.config.NtpServer != nil { ntpServers = append(ntpServers, ni.config.NtpServer) } - ntpServers = utils.FilterDuplicatesFn(ntpServers, utils.EqualIPs) + ntpServers = generics.FilterDuplicatesFn(ntpServers, netutils.EqualIPs) dhcpCfg := generic.DHCPServer{ Subnet: r.getNISubnet(ni), AllOnesNetmask: !r.disableAllOnesNetmask, @@ -988,7 +989,7 @@ func (r *LinuxNIReconciler) getIntendedAppConnCfg(niID uuid.UUID, if vif.GuestIP != nil { ips = append(ips, vif.GuestIP) } - ips = utils.FilterDuplicatesFn(ips, utils.EqualIPs) + ips = generics.FilterDuplicatesFn(ips, netutils.EqualIPs) ipv4Eids := linux.IPSet{ SetName: eidsIpsetName(vif, false), TypeName: "hash:ip", diff --git a/pkg/pillar/nireconciler/linux_reconciler.go b/pkg/pillar/nireconciler/linux_reconciler.go index e13f86bdec6..4f69ec3ae2a 100644 --- a/pkg/pillar/nireconciler/linux_reconciler.go +++ b/pkg/pillar/nireconciler/linux_reconciler.go @@ -21,8 +21,8 @@ import ( generic "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" linux "github.com/lf-edge/eve/pkg/pillar/nireconciler/linuxitems" "github.com/lf-edge/eve/pkg/pillar/types" - "github.com/lf-edge/eve/pkg/pillar/utils" fileutils "github.com/lf-edge/eve/pkg/pillar/utils/file" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" @@ -718,14 +718,14 @@ func (r *LinuxNIReconciler) scheduleGlobalCfgRebuild(reason string) { r.pendingReconcile.rebuildGlobalCfg = &pendingCfgRebuild{} } rebuild := r.pendingReconcile.rebuildGlobalCfg - if !utils.ContainsItem(rebuild.reasons, reason) { + if !generics.ContainsItem(rebuild.reasons, reason) { rebuild.reasons = append(rebuild.reasons, reason) } } func (r *LinuxNIReconciler) scheduleNICfgRebuild(niID uuid.UUID, reason string) { rebuild := r.pendingReconcile.rebuildNICfg[niID] - if !utils.ContainsItem(rebuild.reasons, reason) { + if !generics.ContainsItem(rebuild.reasons, reason) { rebuild.reasons = append(rebuild.reasons, reason) } r.pendingReconcile.rebuildNICfg[niID] = rebuild diff --git a/pkg/pillar/nireconciler/linuxitems/bridge.go b/pkg/pillar/nireconciler/linuxitems/bridge.go index e802204146d..368d60434ba 100644 --- a/pkg/pillar/nireconciler/linuxitems/bridge.go +++ b/pkg/pillar/nireconciler/linuxitems/bridge.go @@ -13,7 +13,8 @@ import ( dg "github.com/lf-edge/eve/libs/depgraph" "github.com/lf-edge/eve/pkg/pillar/base" "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" "github.com/vishvananda/netlink" ) @@ -55,7 +56,7 @@ func (b Bridge) Equal(other dg.Item) bool { return b.IfName == b2.IfName && b.CreatedByNIM == b2.CreatedByNIM && bytes.Equal(b.MACAddress, b2.MACAddress) && - utils.EqualSetsFn(b.IPAddresses, b2.IPAddresses, utils.EqualIPNets) + generics.EqualSetsFn(b.IPAddresses, b2.IPAddresses, netutils.EqualIPNets) } // External returns true if it was created by NIM and not be zedrouter. diff --git a/pkg/pillar/nireconciler/linuxitems/iprule.go b/pkg/pillar/nireconciler/linuxitems/iprule.go index d62d440304c..09312867e2d 100644 --- a/pkg/pillar/nireconciler/linuxitems/iprule.go +++ b/pkg/pillar/nireconciler/linuxitems/iprule.go @@ -11,7 +11,7 @@ import ( dg "github.com/lf-edge/eve/libs/depgraph" "github.com/lf-edge/eve/pkg/pillar/base" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" "github.com/vishvananda/netlink" ) @@ -63,8 +63,8 @@ func (r IPRule) Equal(other dg.Item) bool { r.Table == r2.Table && r.Mark == r2.Mark && r.Mask == r2.Mask && - utils.EqualIPNets(r.Src, r2.Src) && - utils.EqualIPNets(r.Dst, r2.Dst) + netutils.EqualIPNets(r.Src, r2.Src) && + netutils.EqualIPNets(r.Dst, r2.Dst) } // External returns false. diff --git a/pkg/pillar/nireconciler/linuxitems/ipset.go b/pkg/pillar/nireconciler/linuxitems/ipset.go index 71d905754f4..da9e7c26796 100644 --- a/pkg/pillar/nireconciler/linuxitems/ipset.go +++ b/pkg/pillar/nireconciler/linuxitems/ipset.go @@ -12,7 +12,7 @@ import ( dg "github.com/lf-edge/eve/libs/depgraph" "github.com/lf-edge/eve/pkg/pillar/base" generic "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" "github.com/vishvananda/netlink" ) @@ -56,7 +56,7 @@ func (s IPSet) Equal(other dg.Item) bool { } return s.SetName == s2.SetName && s.TypeName == s2.TypeName && - utils.EqualSets(s.Entries, s2.Entries) + generics.EqualSets(s.Entries, s2.Entries) } // External returns false. diff --git a/pkg/pillar/nireconciler/linuxitems/vlanport.go b/pkg/pillar/nireconciler/linuxitems/vlanport.go index c94aaa2d344..08ce62da912 100644 --- a/pkg/pillar/nireconciler/linuxitems/vlanport.go +++ b/pkg/pillar/nireconciler/linuxitems/vlanport.go @@ -12,7 +12,7 @@ import ( "github.com/lf-edge/eve/pkg/pillar/base" "github.com/lf-edge/eve/pkg/pillar/netmonitor" generic "github.com/lf-edge/eve/pkg/pillar/nireconciler/genericitems" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" "github.com/vishvananda/netlink" ) @@ -87,7 +87,7 @@ func (v VLANPort) Equal(other dg.Item) bool { } if isTrunk1 { if v.VLANConfig.TrunkPort.AllVIDs != v2.VLANConfig.TrunkPort.AllVIDs || - !utils.EqualSets(v.VLANConfig.TrunkPort.VIDs, v2.VLANConfig.TrunkPort.VIDs) { + !generics.EqualSets(v.VLANConfig.TrunkPort.VIDs, v2.VLANConfig.TrunkPort.VIDs) { return false } } else { diff --git a/pkg/pillar/nireconciler/nireconciler.go b/pkg/pillar/nireconciler/nireconciler.go index 2e2f780b92e..4566c4b1b2f 100644 --- a/pkg/pillar/nireconciler/nireconciler.go +++ b/pkg/pillar/nireconciler/nireconciler.go @@ -17,7 +17,8 @@ import ( dg "github.com/lf-edge/eve/libs/depgraph" "github.com/lf-edge/eve/pkg/pillar/types" - "github.com/lf-edge/eve/pkg/pillar/utils" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" + "github.com/lf-edge/eve/pkg/pillar/utils/netutils" uuid "github.com/satori/go.uuid" ) @@ -111,8 +112,8 @@ type Uplink struct { func (u Uplink) Equal(u2 Uplink) bool { return u.LogicalLabel == u2.LogicalLabel && u.IfName == u2.IfName && - utils.EqualSetsFn(u.DNSServers, u2.DNSServers, utils.EqualIPs) && - utils.EqualSetsFn(u.NTPServers, u2.NTPServers, utils.EqualIPs) + generics.EqualSetsFn(u.DNSServers, u2.DNSServers, netutils.EqualIPs) && + generics.EqualSetsFn(u.NTPServers, u2.NTPServers, netutils.EqualIPs) } // AppVIF : describes interface created to connect application with network instance. @@ -222,7 +223,7 @@ type AppConnReconcileStatus struct { // Equal compares two instances of AppConnReconcileStatus. func (s AppConnReconcileStatus) Equal(s2 AppConnReconcileStatus) bool { return s.App == s2.App && s.Deleted == s2.Deleted && - utils.EqualSetsFn(s.VIFs, s2.VIFs, + generics.EqualSetsFn(s.VIFs, s2.VIFs, func(v1, v2 AppVIFReconcileStatus) bool { return v1.Equal(v2) }) diff --git a/pkg/pillar/types/cipherinfotypes.go b/pkg/pillar/types/cipherinfotypes.go index 8ff10dd1dda..9ca26017da6 100644 --- a/pkg/pillar/types/cipherinfotypes.go +++ b/pkg/pillar/types/cipherinfotypes.go @@ -143,5 +143,7 @@ type EncryptionBlock struct { DsPassword string WifiUserName string // If the authentication type is EAP WifiPassword string + CellNetUsername string + CellNetPassword string ProtectedUserData string } diff --git a/pkg/pillar/types/zedroutertypes.go b/pkg/pillar/types/zedroutertypes.go index 1e5deb8e791..20558256700 100644 --- a/pkg/pillar/types/zedroutertypes.go +++ b/pkg/pillar/types/zedroutertypes.go @@ -16,6 +16,7 @@ import ( "github.com/eriknordmark/ipinfo" "github.com/google/go-cmp/cmp" "github.com/lf-edge/eve/pkg/pillar/base" + "github.com/lf-edge/eve/pkg/pillar/utils/generics" uuid "github.com/satori/go.uuid" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" @@ -848,6 +849,8 @@ func (config *DevicePortConfig) MostlyEqual(config2 *DevicePortConfig) bool { for i, p1 := range config.Ports { p2 := config2.Ports[i] if p1.IfName != p2.IfName || + p1.PCIAddr != p2.PCIAddr || + p1.USBAddr != p2.USBAddr || p1.Phylabel != p2.Phylabel || p1.Logicallabel != p2.Logicallabel || p1.Alias != p2.Alias || @@ -1002,20 +1005,49 @@ type WifiConfig struct { CipherBlockStatus } -// CellConfig - Cellular part of the configure -type CellConfig struct { - APN string // LTE APN - ProbeAddr string - DisableProbe bool - // Enable to get location info from the GNSS receiver of the LTE modem. +// CellNetPortConfig - configuration for cellular network port (part of DPC). +type CellNetPortConfig struct { + // Parameters to apply for connecting to cellular networks. + // Configured separately for every SIM card inserted into the modem. + AccessPoints []CellularAccessPoint + // Probe used to detect broken connection. + Probe WwanProbe + // Enable to get location info from the GNSS receiver of the cellular modem. LocationTracking bool } +// CellularAccessPoint contains config parameters for connecting to a cellular network. +type CellularAccessPoint struct { + // SIM card slot to which this configuration applies. + // 0 - unspecified (apply to currently activated or the only available) + // 1 - config for SIM card in the first slot + // 2 - config for SIM card in the second slot + // etc. + SIMSlot uint8 + // If true, then this configuration is currently activated. + Activated bool + // Access Point Network + APN string + // Authentication protocol used by the network. + AuthProtocol WwanAuthProtocol + // CipherBlockStatus with encrypted credentials. + CipherBlockStatus + // The set of cellular network operators that modem should preferably try to register + // and connect into. + // Network operator should be referenced by PLMN (Public Land Mobile Network) code. + PreferredPLMNs []string + // The list of preferred Radio Access Technologies (RATs) to use for connecting + // to the network. + PreferredRATs []WwanRAT + // If true, then modem will avoid connecting to networks with roaming. + ForbidRoaming bool +} + // WirelessConfig - wireless structure type WirelessConfig struct { - WType WirelessType // Wireless Type - Cellular []CellConfig // LTE APN - Wifi []WifiConfig // Wifi Config params + WType WirelessType // Wireless Type + Cellular CellNetPortConfig // Cellular connectivity config params + Wifi []WifiConfig // Wifi Config params } // WirelessStatus : state information for a single wireless device @@ -1136,6 +1168,8 @@ type BondArpMonitor struct { // Note that if fields are added the MostlyEqual function needs to be updated. type NetworkPortConfig struct { IfName string + USBAddr string + PCIAddr string Phylabel string // Physical name set by controller/model Logicallabel string // SystemAdapter's name which is logical label in phyio Alias string // From SystemAdapter's alias @@ -1169,8 +1203,9 @@ type NetworkPortStatus struct { NtpServers []net.IP // This comes from DHCP done on uplink port AddrInfoList []AddrInfo Up bool - MacAddr string + MacAddr net.HardwareAddr DefaultRouters []net.IP + MTU uint16 WirelessCfg WirelessConfig WirelessStatus WirelessStatus ProxyConfig @@ -1369,7 +1404,7 @@ func (status DeviceNetworkStatus) MostlyEqual(status2 DeviceNetworkStatus) bool } } if p1.Up != p2.Up || - p1.MacAddr != p2.MacAddr { + !bytes.Equal(p1.MacAddr, p2.MacAddr) { return false } if len(p1.DefaultRouters) != len(p2.DefaultRouters) { @@ -2982,53 +3017,6 @@ func (data AppInstMetaData) Key() string { return data.AppInstUUID.String() + "-" + string(data.Type) } -// AddToIP : -func AddToIP(ip net.IP, addition int) net.IP { - if addr := ip.To4(); addr != nil { - val := uint32(addr[0])<<24 + uint32(addr[1])<<16 + - uint32(addr[2])<<8 + uint32(addr[3]) - val += uint32(addition) - byte0 := byte((val >> 24) & 0xFF) - byte1 := byte((val >> 16) & 0xFF) - byte2 := byte((val >> 8) & 0xFF) - byte3 := byte(val & 0xFF) - return net.IPv4(byte0, byte1, byte2, byte3) - } - //TBD:XXX, IPv6 handling - return net.IP{} -} - -// GetIPAddrCountOnSubnet IP address count on subnet -func GetIPAddrCountOnSubnet(subnet net.IPNet) int { - prefixLen, _ := subnet.Mask.Size() - if prefixLen != 0 { - if subnet.IP.To4() != nil { - return 0x01 << (32 - prefixLen) - } - if subnet.IP.To16() != nil { - return 0x01 << (128 - prefixLen) - } - } - return 0 -} - -// GetIPNetwork : -// returns the first IP Address of the subnet(Network Address) -func GetIPNetwork(subnet net.IPNet) net.IP { - return subnet.IP.Mask(subnet.Mask) -} - -// GetIPBroadcast : -// returns the last IP Address of the subnet(Broadcast Address) -func GetIPBroadcast(subnet net.IPNet) net.IP { - if network := GetIPNetwork(subnet); network != nil { - if addrCount := GetIPAddrCountOnSubnet(subnet); addrCount != 0 { - return AddToIP(network, addrCount-1) - } - } - return net.IP{} -} - // At the MinSubnetSize there is room for one app instance (.0 being reserved, // .3 broadcast, .1 is the bridgeIPAddr, and .2 is usable). const ( @@ -3038,40 +3026,57 @@ const ( // WwanConfig is published by nim and consumed by the wwan service. type WwanConfig struct { - RadioSilence bool `json:"radio-silence"` - Networks []WwanNetworkConfig `json:"networks"` + RadioSilence bool `json:"radio-silence"` + // Enable verbose logging in the wwan microservice. + Verbose bool `json:"verbose"` + Networks []WwanNetworkConfig `json:"networks"` } // Equal compares two instances of WwanConfig for equality. func (wc WwanConfig) Equal(wc2 WwanConfig) bool { - if wc.RadioSilence != wc2.RadioSilence { - return false - } - if len(wc.Networks) != len(wc2.Networks) { + if wc.RadioSilence != wc2.RadioSilence || + wc.Verbose != wc2.Verbose { return false } - for _, m1 := range wc.Networks { - var found bool - for _, m2 := range wc2.Networks { - if m1.Equal(m2) { - found = true - break - } - } - if !found { - return false - } - } - return true + return generics.EqualSetsFn(wc.Networks, wc2.Networks, + func(wnc1, wnc2 WwanNetworkConfig) bool { + return wnc1.Equal(wnc2) + }) } // WwanNetworkConfig contains configuration for a single cellular network. +// In case there are multiple SIM cards/slots in the modem, WwanNetworkConfig +// contains config only for the activated one. +// TODO: Add username + password (will be done in the next PR) type WwanNetworkConfig struct { // Logical label in PhysicalIO. LogicalLabel string `json:"logical-label"` PhysAddrs WwanPhysAddrs `json:"physical-addrs"` - // XXX Multiple APNs are not yet supported. - Apns []string `json:"apns"` + // Index of the SIM slot to activate and use for the connection. + // Note that slots are indexed incrementally, starting with 1. + // Zero value means that the slot is undefined and EVE will not change + // SIM slot activation settings, meaning that the currently activated + // slot will remain being used. + SIMSlot uint8 `json:"sim-slot"` + // Access Point Network to connect into. + // By default, it is "internet". + APN string `json:"apn"` + // The set of cellular network operators that modem should preferably try to register + // and connect into. + // Network operator should be referenced by PLMN (Public Land Mobile Network) code, + // consisting of 3-digits MCC (Mobile Country Code) and 2 or 3-digits MNC + // (Mobile Network Code), separated by a dash, e.g. "310-260". + // If empty, then modem will select the network automatically based on the SIM + // card config. + PreferredPLMNs []string `json:"preferred-plmns,omitempty"` + // The list of preferred Radio Access Technologies (RATs) to use for connecting + // to the network. + // Order matters, first is the most preferred, second is tried next, etc. + // Not listed technologies will not be tried. + // If empty, then modem will select RAT automatically. + PreferredRATs []WwanRAT `json:"preferred-rats,omitempty"` + // Enable or disable data roaming. + ForbidRoaming bool `json:"forbid-roaming"` // Proxies configured for the cellular network. Proxies []ProxyEntry `json:"proxies"` // Probe used to detect broken connection. @@ -3085,6 +3090,36 @@ type WwanNetworkConfig struct { LocationTracking bool `json:"location-tracking"` } +// WwanAuthProtocol : authentication protocol used by cellular network. +type WwanAuthProtocol string + +const ( + // WwanAuthProtocolNone : no authentication. + WwanAuthProtocolNone WwanAuthProtocol = "" + // WwanAuthProtocolPAP : Password Authentication Protocol. + WwanAuthProtocolPAP WwanAuthProtocol = "pap" + // WwanAuthProtocolCHAP : Challenge-Handshake Authentication Protocol. + WwanAuthProtocolCHAP WwanAuthProtocol = "chap" + // WwanAuthProtocolPAPAndCHAP : Both PAP and CHAP. + WwanAuthProtocolPAPAndCHAP WwanAuthProtocol = "pap-and-chap" +) + +// WwanRAT : Radio Access Technology. +type WwanRAT string + +const ( + // WwanRATUnspecified : select RAT automatically. + WwanRATUnspecified WwanRAT = "" + // WwanRATGSM : Global System for Mobile Communications (2G). + WwanRATGSM WwanRAT = "gsm" + // WwanRATUMTS : Universal Mobile Telecommunications System (3G). + WwanRATUMTS WwanRAT = "umts" + // WwanRATLTE : Long-Term Evolution (4G). + WwanRATLTE WwanRAT = "lte" + // WwanRAT5GNR : 5th Generation New Radio (5G). + WwanRAT5GNR WwanRAT = "5gnr" +) + // WwanProbe : cellular connectivity verification probe. type WwanProbe struct { Disable bool `json:"disable"` @@ -3095,40 +3130,26 @@ type WwanProbe struct { // Equal compares two instances of WwanNetworkConfig for equality. func (wnc WwanNetworkConfig) Equal(wnc2 WwanNetworkConfig) bool { if wnc.LogicalLabel != wnc2.LogicalLabel || - wnc.PhysAddrs.PCI != wnc2.PhysAddrs.PCI || - wnc.PhysAddrs.USB != wnc2.PhysAddrs.USB || - wnc.PhysAddrs.Interface != wnc2.PhysAddrs.Interface { + wnc.PhysAddrs != wnc2.PhysAddrs { return false } - if wnc.Probe.Address != wnc2.Probe.Address || - wnc.Probe.Disable != wnc2.Probe.Disable { + if wnc.SIMSlot != wnc2.SIMSlot || + wnc.APN != wnc2.APN { return false } - if len(wnc.Proxies) != len(wnc2.Proxies) { + if !generics.EqualLists(wnc.PreferredPLMNs, wnc2.PreferredPLMNs) || + !generics.EqualLists(wnc.PreferredRATs, wnc2.PreferredRATs) || + wnc.ForbidRoaming != wnc2.ForbidRoaming { return false } - for i := range wnc.Proxies { - if wnc.Proxies[i] != wnc2.Proxies[i] { - return false - } - } - if wnc.LocationTracking != wnc2.LocationTracking { + if !generics.EqualLists(wnc.Proxies, wnc2.Proxies) { return false } - if len(wnc.Apns) != len(wnc2.Apns) { + if wnc.Probe != wnc2.Probe { return false } - for _, apn1 := range wnc.Apns { - var found bool - for _, apn2 := range wnc2.Apns { - if apn1 == apn2 { - found = true - break - } - } - if !found { - return false - } + if wnc.LocationTracking != wnc2.LocationTracking { + return false } return true } @@ -3146,15 +3167,28 @@ type WwanPhysAddrs struct { // PCI address in the long format. // For example: 0000:00:15.0 PCI string `json:"pci"` + // Dev : device file representing the modem (e.g. /dev/cdc-wdm0). + Dev string `json:"dev"` } // WwanStatus is published by the wwan service and consumed by nim. type WwanStatus struct { Networks []WwanNetworkStatus `json:"networks"` - // MD5 checksum of the corresponding WwanConfig (as config.json). + // SHA256 hash of the corresponding WwanConfig (as config.json). ConfigChecksum string `json:"config-checksum,omitempty"` } +// Equal compares two instances of WwanStatus for equality. +func (ws WwanStatus) Equal(ws2 WwanStatus) bool { + if ws.ConfigChecksum != ws2.ConfigChecksum { + return false + } + return generics.EqualSetsFn(ws.Networks, ws2.Networks, + func(wns1, wns2 WwanNetworkStatus) bool { + return wns1.Equal(wns2) + }) +} + // LookupNetworkStatus returns status corresponding to the given cellular network. func (ws WwanStatus) LookupNetworkStatus(logicalLabel string) (WwanNetworkStatus, bool) { for _, status := range ws.Networks { @@ -3193,14 +3227,16 @@ func (ws WwanStatus) DoSanitize() { if simCard.ICCID != "" { simCard.Name = simCard.ICCID } else { - simCard.Name = fmt.Sprintf("%s - SIM%d", network.Module.Name, j) + simCard.Name = fmt.Sprintf("%s-SIM%d", + network.Module.Name, simCard.SlotNumber) } } } } } -// WwanNetworkStatus contains status information for a single cellular network. +// WwanNetworkStatus contains status information for a single cellular network +// (i.e. one modem but possibly multiple SIM slots/cards). type WwanNetworkStatus struct { // Logical label of the cellular modem in PhysicalIO. // Can be empty if this device is not configured by the controller @@ -3208,36 +3244,77 @@ type WwanNetworkStatus struct { LogicalLabel string `json:"logical-label"` PhysAddrs WwanPhysAddrs `json:"physical-addrs"` Module WwanCellModule `json:"cellular-module"` - SimCards []WwanSimCard `json:"sim-cards"` - ConfigError string `json:"config-error"` - ProbeError string `json:"probe-error"` - Providers []WwanProvider `json:"providers"` + // One entry for every SIM slot (incl. those without SIM card). + SimCards []WwanSimCard `json:"sim-cards"` + // Non-empty if the wwan microservice failed to apply config submitted by NIM. + ConfigError string `json:"config-error"` + // Error message from the last connectivity probing. + ProbeError string `json:"probe-error"` + // Network where the modem is currently registered. + CurrentProvider WwanProvider `json:"current-provider"` + // All networks that the modem is able to detect. + // This will include the currently used provider as well as other visible networks. + VisibleProviders []WwanProvider `json:"visible-providers"` + // The list of Radio Access Technologies (RATs) currently used for registering/connecting + // to the network (typically just one). + CurrentRATs []WwanRAT `json:"current-rats"` + // Unix timestamp in seconds made when the current connection was established. + // Zero value if the modem is not connected. + ConnectedAt uint64 `json:"connected-at"` } // WwanCellModule contains cellular module specs. type WwanCellModule struct { - Name string `json:"name,omitempty"` - IMEI string `json:"imei"` - Model string `json:"model"` - Revision string `json:"revision"` + // Name is a module identifier. For example IMEI if available. + // Guaranteed to be unique among all modems attached to the edge node. + Name string `json:"name,omitempty"` + // International Mobile Equipment Identity. + IMEI string `json:"imei"` + Model string `json:"model"` + Manufacturer string `json:"manufacturer"` + // Firmware version identifier. + Revision string `json:"revision"` + // QMI or MBIM. ControlProtocol WwanCtrlProt `json:"control-protocol"` OpMode WwanOpMode `json:"operating-mode"` } -// WwanSimCard contains SIM card information. +// WwanSimCard describes either empty SIM slot or a slot with a SIM card inserted. type WwanSimCard struct { - Name string `json:"name,omitempty"` - ICCID string `json:"iccid"` - IMSI string `json:"imsi"` - Status string `json:"status"` + // Name is a SIM card/slot identifier. + // Guaranteed to be unique across all modems and their SIM slots attached + // to the edge node. + Name string `json:"name,omitempty"` + // SIM slot number which this WwanSimCard instance describes. + SlotNumber uint8 `json:"slot-number"` + // True if this SIM slot is activated, i.e. the inserted SIM card (if any) can be used + // to connect to a cellular network. + SlotActivated bool `json:"slot-activated"` + // Integrated Circuit Card Identifier. + // Empty if no SIM card is inserted into the slot or if the SIM card is not recognized. + ICCID string `json:"iccid,omitempty"` + // International Mobile Subscriber Identity. + // Empty if no SIM card is inserted into the slot or if the SIM card is not recognized. + IMSI string `json:"imsi,omitempty"` + // The current state of the SIM card (absent, initialized, not recognized, etc.). + // This state is not modeled using enum because the set of possible values differs + // between QMI and MBIM protocols (used to control cellular modules) and there is + // no 1:1 mapping between them. + State string `json:"state"` } // WwanProvider contains information about a cellular connectivity provider. type WwanProvider struct { - PLMN string `json:"plmn"` - Description string `json:"description"` - CurrentServing bool `json:"current-serving"` - Roaming bool `json:"roaming"` + // Public Land Mobile Network identifier. + PLMN string `json:"plmn"` + // Human-readable label identifying the provider. + Description string `json:"description"` + // True if this is the provider currently being used. + CurrentServing bool `json:"current-serving"` + // True if data romaing is ON. + Roaming bool `json:"roaming"` + // True if this provider is forbidden by SIM card config. + Forbidden bool `json:"forbidden"` } // WwanOpMode : wwan operating mode @@ -3270,11 +3347,45 @@ const ( WwanCtrlProtMBIM WwanCtrlProt = "mbim" ) +// Equal compares two instances of WwanNetworkStatus for equality. +func (wns WwanNetworkStatus) Equal(wns2 WwanNetworkStatus) bool { + if wns.LogicalLabel != wns2.LogicalLabel || + wns.PhysAddrs != wns2.PhysAddrs { + return false + } + if wns.Module != wns2.Module { + return false + } + if !generics.EqualSets(wns.SimCards, wns2.SimCards) { + return false + } + if wns.ConfigError != wns2.ConfigError || + wns.ProbeError != wns2.ProbeError { + return false + } + if wns.CurrentProvider != wns2.CurrentProvider || + !generics.EqualSets(wns.VisibleProviders, wns2.VisibleProviders) { + return false + } + if !generics.EqualSets(wns.CurrentRATs, wns2.CurrentRATs) { + return false + } + if wns.ConnectedAt != wns2.ConnectedAt { + return false + } + return true +} + // WwanMetrics is published by the wwan service. type WwanMetrics struct { Networks []WwanNetworkMetrics `json:"networks"` } +// Equal compares two instances of WwanMetrics for equality. +func (wm WwanMetrics) Equal(wm2 WwanMetrics) bool { + return generics.EqualSets(wm.Networks, wm2.Networks) +} + // LookupNetworkMetrics returns metrics corresponding to the given cellular network. func (wm WwanMetrics) LookupNetworkMetrics(logicalLabel string) (WwanNetworkMetrics, bool) { for _, metrics := range wm.Networks { diff --git a/pkg/pillar/utils/generics.go b/pkg/pillar/utils/generics/generics.go similarity index 94% rename from pkg/pillar/utils/generics.go rename to pkg/pillar/utils/generics/generics.go index 45db569dfd8..b9e0cf65b38 100644 --- a/pkg/pillar/utils/generics.go +++ b/pkg/pillar/utils/generics/generics.go @@ -1,7 +1,7 @@ // Copyright (c) 2023 Zededa, Inc. // SPDX-License-Identifier: Apache-2.0 -package utils +package generics // EqualLists returns true if the two slices representing lists (i.e. order dependent) // are equal in size and items they contain. @@ -161,3 +161,12 @@ func ContainsItemFn[Type any](list []Type, item Type, equal func(a, b Type) bool } return false } + +// RotateList rotates the list items by the given amount. +func RotateList[Type any](arr []Type, amount int) []Type { + if len(arr) == 0 { + return []Type{} + } + amount = amount % len(arr) + return append(append([]Type{}, arr[amount:]...), arr[:amount]...) +} diff --git a/pkg/pillar/utils/ip.go b/pkg/pillar/utils/ip.go deleted file mode 100644 index 43c735afec4..00000000000 --- a/pkg/pillar/utils/ip.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023 Zededa, Inc. -// SPDX-License-Identifier: Apache-2.0 - -package utils - -import ( - "bytes" - "net" -) - -// EqualIPs compares two IP addresses. -func EqualIPs(ip1 net.IP, ip2 net.IP) bool { - if ip1 == nil { - return ip2 == nil - } - if ip2 == nil { - return ip1 == nil - } - return ip1.Equal(ip2) -} - -// EqualIPNets compares two IP addresses with masks. -func EqualIPNets(ipNet1, ipNet2 *net.IPNet) bool { - if ipNet1 == nil || ipNet2 == nil { - return ipNet1 == ipNet2 - } - return ipNet1.IP.Equal(ipNet2.IP) && - bytes.Equal(ipNet1.Mask, ipNet2.Mask) -} diff --git a/pkg/pillar/utils/netutils/ip.go b/pkg/pillar/utils/netutils/ip.go new file mode 100644 index 00000000000..06ca0115ee3 --- /dev/null +++ b/pkg/pillar/utils/netutils/ip.go @@ -0,0 +1,74 @@ +// Copyright (c) 2023 Zededa, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package netutils + +import ( + "bytes" + "net" +) + +// EqualIPs compares two IP addresses. +func EqualIPs(ip1 net.IP, ip2 net.IP) bool { + if ip1 == nil { + return ip2 == nil + } + if ip2 == nil { + return ip1 == nil + } + return ip1.Equal(ip2) +} + +// EqualIPNets compares two IP addresses with masks. +func EqualIPNets(ipNet1, ipNet2 *net.IPNet) bool { + if ipNet1 == nil || ipNet2 == nil { + return ipNet1 == ipNet2 + } + return ipNet1.IP.Equal(ipNet2.IP) && + bytes.Equal(ipNet1.Mask, ipNet2.Mask) +} + +// AddToIP increments IP address by the given integer number. +func AddToIP(ip net.IP, addition int) net.IP { + if addr := ip.To4(); addr != nil { + val := uint32(addr[0])<<24 + uint32(addr[1])<<16 + + uint32(addr[2])<<8 + uint32(addr[3]) + val += uint32(addition) + byte0 := byte((val >> 24) & 0xFF) + byte1 := byte((val >> 16) & 0xFF) + byte2 := byte((val >> 8) & 0xFF) + byte3 := byte(val & 0xFF) + return net.IPv4(byte0, byte1, byte2, byte3) + } + //TBD:XXX, IPv6 handling + return net.IP{} +} + +// GetIPAddrCountOnSubnet return the number or available IP addresses inside a subnet. +func GetIPAddrCountOnSubnet(subnet net.IPNet) int { + prefixLen, _ := subnet.Mask.Size() + if prefixLen != 0 { + if subnet.IP.To4() != nil { + return 0x01 << (32 - prefixLen) + } + if subnet.IP.To16() != nil { + return 0x01 << (128 - prefixLen) + } + } + return 0 +} + +// GetIPNetwork returns the first IP Address of the subnet(Network Address) +func GetIPNetwork(subnet net.IPNet) net.IP { + return subnet.IP.Mask(subnet.Mask) +} + +// GetIPBroadcast returns the last IP Address of the subnet(Broadcast Address) +func GetIPBroadcast(subnet net.IPNet) net.IP { + if network := GetIPNetwork(subnet); network != nil { + if addrCount := GetIPAddrCountOnSubnet(subnet); addrCount != 0 { + return AddToIP(network, addrCount-1) + } + } + return net.IP{} +} diff --git a/pkg/wwan/usr/bin/wwan-init.sh b/pkg/wwan/usr/bin/wwan-init.sh index 45372f29316..47d062f966e 100755 --- a/pkg/wwan/usr/bin/wwan-init.sh +++ b/pkg/wwan/usr/bin/wwan-init.sh @@ -356,13 +356,13 @@ collect_network_status() { "$(json_str_attr control-protocol "$PROTOCOL")" \ "$(json_str_attr operating-mode "$("${PROTOCOL}_get_op_mode")")")" local NETWORK_STATUS="$(json_struct \ - "$(json_str_attr logical-label "$LOGICAL_LABEL")" \ - "$(json_attr physical-addrs "$ADDRS")" \ - "$(json_attr cellular-module "$MODULE")" \ - "$(json_attr sim-cards "$("${PROTOCOL}_get_sim_cards")")" \ - "$(json_str_attr config-error "$CONFIG_ERROR")" \ - "$(json_str_attr probe-error "$PROBE_ERROR")" \ - "$(json_attr providers "$PROVIDERS")")" + "$(json_str_attr logical-label "$LOGICAL_LABEL")" \ + "$(json_attr physical-addrs "$ADDRS")" \ + "$(json_attr cellular-module "$MODULE")" \ + "$(json_attr sim-cards "$("${PROTOCOL}_get_sim_cards")")" \ + "$(json_str_attr config-error "$CONFIG_ERROR")" \ + "$(json_str_attr probe-error "$PROBE_ERROR")" \ + "$(json_attr visible-providers "$PROVIDERS")")" STATUS="${STATUS}${NETWORK_STATUS}\n" } @@ -480,7 +480,7 @@ event_stream | while read -r EVENT; do PROBE_DISABLED="$(parse_json_attr "$PROBE" "disable")" PROBE_ADDR="$(parse_json_attr "$PROBE" "address")" PROXIES="$(parse_json_attr "$NETWORK" "proxies")" - APN="$(parse_json_attr "$NETWORK" "apns[0]")" # FIXME XXX limited to a single APN for now + APN="$(parse_json_attr "$NETWORK" "apn")" APN="${APN:-$DEFAULT_APN}" LOC_TRACKING="$(parse_json_attr "$NETWORK" "\"location-tracking\"")"