func TestUsePrefix(t *testing.T) { a := New(t) var client redis.Client ns := NewRedisNetworkServer(&client, 19) a.So(ns.UsePrefix(types.DevAddrPrefix{DevAddr: types.DevAddr([4]byte{0, 0, 0, 0}), Length: 0}, []string{"otaa"}), ShouldNotBeNil) a.So(ns.UsePrefix(types.DevAddrPrefix{DevAddr: types.DevAddr([4]byte{0x14, 0, 0, 0}), Length: 7}, []string{"otaa"}), ShouldNotBeNil) a.So(ns.UsePrefix(types.DevAddrPrefix{DevAddr: types.DevAddr([4]byte{0x26, 0, 0, 0}), Length: 7}, []string{"otaa"}), ShouldBeNil) a.So(ns.(*networkServer).prefixes, ShouldHaveLength, 1) }
func TestHandleUplink(t *testing.T) { a := New(t) r := getTestRouter(t) r.discovery.EXPECT().GetAllBrokersForDevAddr(types.DevAddr([4]byte{1, 2, 3, 4})).Return([]*discovery.Announcement{}, nil) uplink := newReferenceUplink() gtwID := "eui-0102030405060708" err := r.HandleUplink(gtwID, uplink) a.So(err, ShouldBeNil) utilization := r.getGateway(gtwID).Utilization utilization.Tick() rx, _ := utilization.Get() a.So(rx, ShouldBeGreaterThan, 0) // TODO: Integration test that checks broker forward }
// MACPayloadFromPayload creates a new MACPayload from a lorawan.Payload func MACPayloadFromPayload(payload lorawan.Payload) (mac MACPayload) { if payload, ok := payload.(*lorawan.MACPayload); ok { mac.DevAddr = types.DevAddr(payload.FHDR.DevAddr) mac.Adr = payload.FHDR.FCtrl.ADR mac.AdrAckReq = payload.FHDR.FCtrl.ADRACKReq mac.Ack = payload.FHDR.FCtrl.ACK mac.FPending = payload.FHDR.FCtrl.FPending mac.FCnt = payload.FHDR.FCnt for _, cmd := range payload.FHDR.FOpts { mac.FOpts = append(mac.FOpts, MACCommandFromMACCommand(cmd)) } if payload.FPort != nil { mac.FPort = int32(*payload.FPort) } if len(payload.FRMPayload) == 1 { if payload, ok := payload.FRMPayload[0].(*lorawan.DataPayload); ok { mac.FrmPayload = payload.Bytes } } } return }
// JoinAcceptPayloadFromPayload creates a new JoinAcceptPayload from a lorawan.Payload func JoinAcceptPayloadFromPayload(payload lorawan.Payload) (accept JoinAcceptPayload) { if dataPayload, ok := payload.(*lorawan.DataPayload); ok { accept.Encrypted = dataPayload.Bytes joinAccept := &lorawan.JoinAcceptPayload{} joinAccept.UnmarshalBinary(false, dataPayload.Bytes) payload = joinAccept } if payload, ok := payload.(*lorawan.JoinAcceptPayload); ok { accept.AppNonce = types.AppNonce(payload.AppNonce) accept.NetId = types.NetID(payload.NetID) accept.DevAddr = types.DevAddr(payload.DevAddr) accept.DLSettings.Rx1DrOffset = uint32(payload.DLSettings.RX1DROffset) accept.DLSettings.Rx2Dr = uint32(payload.DLSettings.RX2DataRate) accept.RxDelay = uint32(payload.RXDelay) if payload.CFList != nil { accept.CfList = &CFList{ Freq: payload.CFList[:], } } } return }
func (b *broker) HandleUplink(uplink *pb.UplinkMessage) (err error) { ctx := b.Ctx.WithField("GatewayID", uplink.GatewayMetadata.GatewayId) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle uplink") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") } }() time := time.Now() b.status.uplink.Mark(1) // De-duplicate uplink messages duplicates := b.deduplicateUplink(uplink) if len(duplicates) == 0 { return nil } b.status.uplinkUnique.Mark(1) ctx = ctx.WithField("Duplicates", len(duplicates)) base := duplicates[0] if base.ProtocolMetadata.GetLorawan() == nil { return errors.NewErrInvalidArgument("Uplink", "does not contain LoRaWAN metadata") } // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(base.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } // Request devices from NS devAddr := types.DevAddr(macPayload.FHDR.DevAddr) ctx = ctx.WithFields(log.Fields{ "DevAddr": devAddr, "FCnt": macPayload.FHDR.FCnt, }) var getDevicesResp *networkserver.DevicesResponse getDevicesResp, err = b.ns.GetDevices(b.Component.GetContext(b.nsToken), &networkserver.DevicesRequest{ DevAddr: &devAddr, FCnt: macPayload.FHDR.FCnt, }) if err != nil { return errors.Wrap(errors.FromGRPCError(err), "NetworkServer did not return devices") } b.status.deduplication.Update(int64(len(getDevicesResp.Results))) if len(getDevicesResp.Results) == 0 { return errors.NewErrNotFound(fmt.Sprintf("Device with DevAddr %s and FCnt <= %d", devAddr, macPayload.FHDR.FCnt)) } ctx = ctx.WithField("DevAddrResults", len(getDevicesResp.Results)) // Sort by FCntUp to optimize the number of MIC checks sort.Sort(ByFCntUp(getDevicesResp.Results)) // Find AppEUI/DevEUI through MIC check var device *pb_lorawan.Device var micChecks int var originalFCnt uint32 for _, candidate := range getDevicesResp.Results { nwkSKey := lorawan.AES128Key(*candidate.NwkSKey) // First check with the 16 bit counter micChecks++ ok, err = phyPayload.ValidateMIC(nwkSKey) if err != nil { return err } if ok { device = candidate break } originalFCnt = macPayload.FHDR.FCnt if candidate.Uses32BitFCnt { macPayload.FHDR.FCnt = fcnt.GetFull(candidate.FCntUp, uint16(originalFCnt)) // If 32 bit counter has different value, perform another MIC check if macPayload.FHDR.FCnt != originalFCnt { micChecks++ ok, err = phyPayload.ValidateMIC(nwkSKey) if err != nil { return err } if ok { device = candidate break } } } return errors.NewErrNotFound("device that validates MIC") } ctx = ctx.WithFields(log.Fields{ "MICChecks": micChecks, "DevEUI": device.DevEui, "AppEUI": device.AppEui, "AppID": device.AppId, "DevID": device.DevId, "FCnt": originalFCnt, }) if macPayload.FHDR.FCnt != originalFCnt { ctx = ctx.WithField("RealFCnt", macPayload.FHDR.FCnt) } if device.DisableFCntCheck { // TODO: Add warning to message? } else if device.FCntUp == 0 { } else if macPayload.FHDR.FCnt <= device.FCntUp || macPayload.FHDR.FCnt-device.FCntUp > maxFCntGap { // Replay attack or FCnt gap too big return errors.NewErrNotFound("device with matching FCnt") } // Add FCnt to Metadata (because it's not marshaled in lorawan payload) base.ProtocolMetadata.GetLorawan().FCnt = macPayload.FHDR.FCnt // Collect GatewayMetadata and DownlinkOptions var gatewayMetadata []*gateway.RxMetadata var downlinkOptions []*pb.DownlinkOption var downlinkMessage *pb.DownlinkMessage for _, duplicate := range duplicates { gatewayMetadata = append(gatewayMetadata, duplicate.GatewayMetadata) downlinkOptions = append(downlinkOptions, duplicate.DownlinkOptions...) } // Select best DownlinkOption if len(downlinkOptions) > 0 { downlinkMessage = &pb.DownlinkMessage{ DevEui: device.DevEui, AppEui: device.AppEui, AppId: device.AppId, DevId: device.DevId, DownlinkOption: selectBestDownlink(downlinkOptions), } } // Build Uplink deduplicatedUplink := &pb.DeduplicatedUplinkMessage{ Payload: base.Payload, DevEui: device.DevEui, DevId: device.DevId, AppEui: device.AppEui, AppId: device.AppId, ProtocolMetadata: base.ProtocolMetadata, GatewayMetadata: gatewayMetadata, ServerTime: time.UnixNano(), ResponseTemplate: downlinkMessage, } // Pass Uplink through NS deduplicatedUplink, err = b.ns.Uplink(b.Component.GetContext(b.nsToken), deduplicatedUplink) if err != nil { return errors.Wrap(errors.FromGRPCError(err), "NetworkServer did not handle uplink") } var announcements []*pb_discovery.Announcement announcements, err = b.Discovery.GetAllHandlersForAppID(device.AppId) if err != nil { return err } if len(announcements) == 0 { return errors.NewErrNotFound(fmt.Sprintf("Handler for AppID %s", device.AppId)) } if len(announcements) > 1 { return errors.NewErrInternal(fmt.Sprintf("Multiple Handlers for AppID %s", device.AppId)) } var handler chan<- *pb.DeduplicatedUplinkMessage handler, err = b.getHandlerUplink(announcements[0].Id) if err != nil { return err } handler <- deduplicatedUplink return nil }
func TestDeviceStore(t *testing.T) { a := New(t) NewRedisDeviceStore(GetRedisClient(), "") s := NewRedisDeviceStore(GetRedisClient(), "handler-test-device-store") // Get non-existing dev, err := s.Get("AppID-1", "DevID-1") a.So(err, ShouldNotBeNil) a.So(dev, ShouldBeNil) devs, err := s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 0) // Create err = s.Set(&Device{ DevAddr: types.DevAddr([4]byte{0, 0, 0, 1}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppID: "AppID-1", DevID: "DevID-1", }) a.So(err, ShouldBeNil) defer func() { s.Delete("AppID-1", "DevID-1") }() // Get existing dev, err = s.Get("AppID-1", "DevID-1") a.So(err, ShouldBeNil) a.So(dev, ShouldNotBeNil) devs, err = s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 1) // Create extra and update dev = &Device{ DevAddr: types.DevAddr([4]byte{0, 0, 0, 2}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 1}), AppID: "AppID-1", DevID: "DevID-2", } err = s.Set(dev) a.So(err, ShouldBeNil) err = s.Set(&Device{ old: dev, DevAddr: types.DevAddr([4]byte{0, 0, 0, 3}), DevEUI: types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 3}), AppEUI: types.AppEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 2}), AppID: "AppID-1", DevID: "DevID-2", }) a.So(err, ShouldBeNil) dev, err = s.Get("AppID-1", "DevID-2") a.So(err, ShouldBeNil) a.So(dev, ShouldNotBeNil) a.So(dev.DevEUI, ShouldEqual, types.DevEUI([8]byte{0, 0, 0, 0, 0, 0, 0, 3})) defer func() { s.Delete("AppID-1", "DevID-2") }() // List devices, err := s.List() a.So(err, ShouldBeNil) a.So(devices, ShouldHaveLength, 2) // Delete err = s.Delete("AppID-1", "DevID-1") a.So(err, ShouldBeNil) // Get deleted dev, err = s.Get("AppID-1", "DevID-1") a.So(err, ShouldNotBeNil) a.So(dev, ShouldBeNil) devs, err = s.ListForApp("AppID-1") a.So(err, ShouldBeNil) a.So(devs, ShouldHaveLength, 1) }
func TestMarshalUnmarshalPayload(t *testing.T) { a := New(t) var subjects []payloadMarshalerUnmarshaler // Do nothing when message and payload are nil subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{}, &DownlinkMessage{}, &DeviceActivationRequest{}, } for _, sub := range subjects { a.So(sub.MarshalPayload(), ShouldEqual, nil) a.So(sub.UnmarshalPayload(), ShouldEqual, nil) } rxMeta := &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}} txConf := &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{Lorawan: &pb_lorawan.TxConfiguration{}}} macMsg := &pb_protocol.Message{Protocol: &pb_protocol.Message_Lorawan{Lorawan: &pb_lorawan.Message{ MHDR: pb_lorawan.MHDR{ Major: 1, MType: pb_lorawan.MType_UNCONFIRMED_UP, }, Payload: &pb_lorawan.Message_MacPayload{MacPayload: &pb_lorawan.MACPayload{ FHDR: pb_lorawan.FHDR{ DevAddr: types.DevAddr([4]byte{1, 2, 3, 4}), FCnt: 1, }, }}, Mic: []byte{1, 2, 3, 4}, }}} macBin := []byte{65, 4, 3, 2, 1, 0, 1, 0, 0, 1, 2, 3, 4} joinReqMsg := &pb_protocol.Message{Protocol: &pb_protocol.Message_Lorawan{Lorawan: &pb_lorawan.Message{ MHDR: pb_lorawan.MHDR{ Major: 1, MType: pb_lorawan.MType_JOIN_REQUEST, }, Payload: &pb_lorawan.Message_JoinRequestPayload{JoinRequestPayload: &pb_lorawan.JoinRequestPayload{ AppEui: types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}), DevEui: types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}), DevNonce: types.DevNonce([2]byte{1, 2}), }}, Mic: []byte{1, 2, 3, 4}, }}} joinReqBin := []byte{1, 8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1, 2, 1, 1, 2, 3, 4} // Only Marshal subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{ ProtocolMetadata: rxMeta, Message: macMsg, }, &DownlinkMessage{ ProtocolConfiguration: txConf, Message: macMsg, }, &DeviceActivationRequest{ ProtocolMetadata: rxMeta, Message: joinReqMsg, }, } for _, sub := range subjects { a.So(sub.UnmarshalPayload(), ShouldEqual, nil) a.So(sub.MarshalPayload(), ShouldEqual, nil) } // Only Unmarshal subjects = []payloadMarshalerUnmarshaler{ &UplinkMessage{ ProtocolMetadata: rxMeta, Payload: macBin, }, &DownlinkMessage{ ProtocolConfiguration: txConf, Payload: macBin, }, &DeviceActivationRequest{ ProtocolMetadata: rxMeta, Payload: joinReqBin, }, } for _, sub := range subjects { a.So(sub.MarshalPayload(), ShouldEqual, nil) a.So(sub.UnmarshalPayload(), ShouldEqual, nil) } }
func (r *router) HandleUplink(gatewayID string, uplink *pb.UplinkMessage) (err error) { ctx := r.Ctx.WithField("GatewayID", gatewayID) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle uplink") } }() r.status.uplink.Mark(1) // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(uplink.Payload) if err != nil { return err } if phyPayload.MHDR.MType == lorawan.JoinRequest { joinRequestPayload, ok := phyPayload.MACPayload.(*lorawan.JoinRequestPayload) if !ok { return errors.NewErrInvalidArgument("Join Request", "does not contain a JoinRequest payload") } devEUI := types.DevEUI(joinRequestPayload.DevEUI) appEUI := types.AppEUI(joinRequestPayload.AppEUI) ctx.WithFields(log.Fields{ "DevEUI": devEUI, "AppEUI": appEUI, }).Debug("Handle Uplink as Activation") r.HandleActivation(gatewayID, &pb.DeviceActivationRequest{ Payload: uplink.Payload, DevEui: &devEUI, AppEui: &appEUI, ProtocolMetadata: uplink.ProtocolMetadata, GatewayMetadata: uplink.GatewayMetadata, }) return nil } if lorawan := uplink.ProtocolMetadata.GetLorawan(); lorawan != nil { ctx = ctx.WithField("Modulation", lorawan.Modulation.String()) if lorawan.Modulation == pb_lorawan.Modulation_LORA { ctx = ctx.WithField("DataRate", lorawan.DataRate) } else { ctx = ctx.WithField("BitRate", lorawan.BitRate) } } if gateway := uplink.GatewayMetadata; gateway != nil { ctx = ctx.WithFields(log.Fields{ "Frequency": gateway.Frequency, "RSSI": gateway.Rssi, "SNR": gateway.Snr, }) } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } devAddr := types.DevAddr(macPayload.FHDR.DevAddr) ctx = ctx.WithFields(log.Fields{ "DevAddr": devAddr, "FCnt": macPayload.FHDR.FCnt, }) gateway := r.getGateway(gatewayID) if err = gateway.HandleUplink(uplink); err != nil { return err } var downlinkOptions []*pb_broker.DownlinkOption if gateway.Schedule.IsActive() { downlinkOptions = r.buildDownlinkOptions(uplink, false, gateway) } ctx = ctx.WithField("DownlinkOptions", len(downlinkOptions)) // Find Broker brokers, err := r.Discovery.GetAllBrokersForDevAddr(devAddr) if err != nil { return err } if len(brokers) == 0 { ctx.Debug("No brokers to forward message to") return nil } ctx = ctx.WithField("NumBrokers", len(brokers)) // Forward to all brokers for _, broker := range brokers { broker, err := r.getBroker(broker) if err != nil { continue } broker.uplink <- &pb_broker.UplinkMessage{ Payload: uplink.Payload, ProtocolMetadata: uplink.ProtocolMetadata, GatewayMetadata: uplink.GatewayMetadata, DownlinkOptions: downlinkOptions, } } ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled uplink") return nil }