コード例 #1
0
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)
}
コード例 #2
0
ファイル: uplink_test.go プロジェクト: TheThingsNetwork/ttn
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
}
コード例 #3
0
// 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
}
コード例 #4
0
// 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
}
コード例 #5
0
ファイル: uplink.go プロジェクト: TheThingsNetwork/ttn
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
}
コード例 #6
0
ファイル: store_test.go プロジェクト: TheThingsNetwork/ttn
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)

}
コード例 #7
0
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)
	}

}
コード例 #8
0
ファイル: uplink.go プロジェクト: TheThingsNetwork/ttn
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
}