예제 #1
0
파일: eui.go 프로젝트: TheThingsNetwork/ttn
// UnmarshalBinary implements the BinaryUnmarshaler interface.
func (eui *EUI64) UnmarshalBinary(data []byte) error {
	if len(data) != 8 {
		return errors.New("ttn/core: Invalid length for EUI64")
	}
	copy(eui[:], data)
	return nil
}
예제 #2
0
// UnmarshalBinary implements the BinaryUnmarshaler interface.
func (key *AES128Key) UnmarshalBinary(data []byte) error {
	if len(data) != 16 {
		return errors.New("ttn/core: Invalid length for AES128Key")
	}
	copy(key[:], data)
	return nil
}
예제 #3
0
func ConvertDataRate(input band.DataRate) (datr *DataRate, err error) {
	if input.Modulation != band.LoRaModulation {
		err = errors.New(fmt.Sprintf("ttn/core: %s can not be converted to a LoRa DataRate", input.Modulation))
	}
	datr = &DataRate{
		SpreadingFactor: uint(input.SpreadFactor),
		Bandwidth:       uint(input.Bandwidth),
	}
	return
}
예제 #4
0
// Get a specific Application
func (s *RedisApplicationStore) Get(appID string) (*Application, error) {
	applicationI, err := s.store.Get(appID)
	if err != nil {
		return nil, err
	}
	if application, ok := applicationI.(Application); ok {
		return &application, nil
	}
	return nil, errors.New("Database did not return a Application")
}
예제 #5
0
// Get a specific Device
func (s *RedisDeviceStore) Get(appEUI types.AppEUI, devEUI types.DevEUI) (*Device, error) {
	deviceI, err := s.store.Get(fmt.Sprintf("%s:%s", appEUI, devEUI))
	if err != nil {
		return nil, err
	}
	if device, ok := deviceI.(Device); ok {
		return &device, nil
	}
	return nil, errors.New("Database did not return a Device")
}
예제 #6
0
// Unmarshal a byte slice into a Message
func (m *Message) Unmarshal(bytes []byte) error {
	payload := &lorawan.PHYPayload{}
	payload.UnmarshalBinary(bytes)
	if micOK, _ := payload.ValidateMIC(lorawan.AES128Key(m.nwkSKey)); !micOK {
		return errors.New("Invalid MIC")
	}
	macPayload, ok := payload.MACPayload.(*lorawan.MACPayload)
	if !ok {
		return errors.New("No MACPayload")
	}
	m.FCnt = int(macPayload.FHDR.FCnt)
	m.FPort = -1
	if macPayload.FPort != nil {
		m.FPort = int(*macPayload.FPort)
	}
	m.Payload = []byte{}
	if len(macPayload.FRMPayload) > 0 {
		payload.DecryptFRMPayload(lorawan.AES128Key(m.appSKey))
		m.Payload = macPayload.FRMPayload[0].(*lorawan.DataPayload).Bytes
	}
	return nil
}
예제 #7
0
// ParseDataRate parses a 32-bit hex-encoded string to a Devdatr
func ParseDataRate(input string) (datr *DataRate, err error) {
	re := regexp.MustCompile("SF(7|8|9|10|11|12)BW(125|250|500)")
	matches := re.FindStringSubmatch(input)
	if len(matches) != 3 {
		return nil, errors.New("ttn/core: Invalid DataRate")
	}

	sf, _ := strconv.ParseUint(matches[1], 10, 64)
	bw, _ := strconv.ParseUint(matches[2], 10, 64)

	return &DataRate{
		SpreadingFactor: uint(sf),
		Bandwidth:       uint(bw),
	}, nil
}
예제 #8
0
// SendUplink sends uplink to the monitor
func (cl *gatewayClient) SendUplink(uplink *router.UplinkMessage) (err error) {
	if !cl.IsConfigured() {
		return nil
	}

	cl.uplink.init.Do(cl.initUplink)

	select {
	case cl.uplink.ch <- uplink:
	default:
		cl.Ctx.Warn("Not sending uplink to monitor, buffer full")
		return errors.New("Not sending uplink to monitor, buffer full")
	}
	return
}
예제 #9
0
// SendStatus sends status to the monitor
func (cl *gatewayClient) SendStatus(status *gateway.Status) (err error) {
	if !cl.IsConfigured() {
		return nil
	}

	cl.status.init.Do(cl.initStatus)

	select {
	case cl.status.ch <- status:
	default:
		cl.Ctx.Warn("Not sending status to monitor, buffer full")
		return errors.New("Not sending status to monitor, buffer full")
	}
	return
}
예제 #10
0
// Get a specific service Announcement
// The result *does* include metadata
func (s *RedisAnnouncementStore) Get(serviceName, serviceID string) (*Announcement, error) {
	announcementI, err := s.store.Get(fmt.Sprintf("%s:%s", serviceName, serviceID))
	if err != nil {
		return nil, err
	}
	announcement, ok := announcementI.(Announcement)
	if !ok {
		return nil, errors.New("Database did not return an Announcement")
	}
	announcement.Metadata, err = s.GetMetadata(serviceName, serviceID)
	if err != nil {
		return nil, err
	}
	return &announcement, nil
}
예제 #11
0
// Delete a Device
func (s *RedisDeviceStore) Delete(appEUI types.AppEUI, devEUI types.DevEUI) error {
	key := fmt.Sprintf("%s:%s", appEUI, devEUI)

	deviceI, err := s.store.GetFields(key, "dev_addr")
	if err != nil {
		return err
	}

	device, ok := deviceI.(Device)
	if !ok {
		errors.New("Database did not return a Device")
	}

	if !device.DevAddr.IsEmpty() {
		if err := s.devAddrIndex.Remove(device.DevAddr.String(), key); err != nil {
			return err
		}
	}

	return s.store.Delete(key)
}
예제 #12
0
func (r *router) HandleActivation(gatewayID string, activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) {
	ctx := r.Ctx.WithFields(log.Fields{
		"GatewayID": gatewayID,
		"AppEUI":    *activation.AppEui,
		"DevEUI":    *activation.DevEui,
	})
	start := time.Now()
	defer func() {
		if err != nil {
			ctx.WithError(err).Warn("Could not handle activation")
		} else {
			ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation")
		}
	}()
	r.status.activations.Mark(1)

	gateway := r.getGateway(gatewayID)
	gateway.LastSeen = time.Now()

	uplink := &pb.UplinkMessage{
		Payload:          activation.Payload,
		ProtocolMetadata: activation.ProtocolMetadata,
		GatewayMetadata:  activation.GatewayMetadata,
	}

	if err = gateway.HandleUplink(uplink); err != nil {
		return nil, err
	}

	if !gateway.Schedule.IsActive() {
		return nil, errors.NewErrInternal(fmt.Sprintf("Gateway %s not available for downlink", gatewayID))
	}

	downlinkOptions := r.buildDownlinkOptions(uplink, true, gateway)

	// Find Broker
	brokers, err := r.Discovery.GetAll("broker")
	if err != nil {
		return nil, err
	}

	// Prepare request
	request := &pb_broker.DeviceActivationRequest{
		Payload:          activation.Payload,
		DevEui:           activation.DevEui,
		AppEui:           activation.AppEui,
		ProtocolMetadata: activation.ProtocolMetadata,
		GatewayMetadata:  activation.GatewayMetadata,
		ActivationMetadata: &pb_protocol.ActivationMetadata{
			Protocol: &pb_protocol.ActivationMetadata_Lorawan{
				Lorawan: &pb_lorawan.ActivationMetadata{
					AppEui: activation.AppEui,
					DevEui: activation.DevEui,
				},
			},
		},
		DownlinkOptions: downlinkOptions,
	}

	// Prepare LoRaWAN activation
	status, err := gateway.Status.Get()
	if err != nil {
		return nil, err
	}
	region := status.Region
	if region == "" {
		region = band.Guess(uplink.GatewayMetadata.Frequency)
	}
	band, err := band.Get(region)
	if err != nil {
		return nil, err
	}
	lorawan := request.ActivationMetadata.GetLorawan()
	lorawan.Rx1DrOffset = 0
	lorawan.Rx2Dr = uint32(band.RX2DataRate)
	lorawan.RxDelay = uint32(band.ReceiveDelay1.Seconds())
	if band.CFList != nil {
		lorawan.CfList = new(pb_lorawan.CFList)
		for _, freq := range band.CFList {
			lorawan.CfList.Freq = append(lorawan.CfList.Freq, freq)
		}
	}

	ctx = ctx.WithField("NumBrokers", len(brokers))

	// Forward to all brokers and collect responses
	var wg sync.WaitGroup
	responses := make(chan *pb_broker.DeviceActivationResponse, len(brokers))
	for _, broker := range brokers {
		broker, err := r.getBroker(broker)
		if err != nil {
			continue
		}

		// Do async request
		wg.Add(1)
		go func() {
			res, err := broker.client.Activate(r.Component.GetContext(""), request)
			if err == nil && res != nil {
				responses <- res
			}
			wg.Done()
		}()
	}

	// Make sure to close channel when all requests are done
	go func() {
		wg.Wait()
		close(responses)
	}()

	var gotFirst bool
	for res := range responses {
		if gotFirst {
			ctx.Warn("Duplicate Activation Response")
		} else {
			gotFirst = true
			downlink := &pb_broker.DownlinkMessage{
				Payload:        res.Payload,
				Message:        res.Message,
				DownlinkOption: res.DownlinkOption,
			}
			err := r.HandleDownlink(downlink)
			if err != nil {
				ctx.Warn("Could not send downlink for Activation")
				gotFirst = false // try again
			}
		}
	}

	// Activation not accepted by any broker
	if !gotFirst {
		ctx.Debug("Activation not accepted at this gateway")
		return nil, errors.New("Activation not accepted at this Gateway")
	}

	// Activation accepted by (at least one) broker
	ctx.Debug("Activation accepted")
	return &pb.DeviceActivationResponse{}, nil
}
예제 #13
0
// Validate implements the api.Validator interface
func (m *TxConfiguration) Validate() error {
	if m.Protocol == nil {
		return errors.New("RxMetadata.Protocol is nil")
	}
	return api.Validate(m.Protocol)
}
예제 #14
0
func (b *broker) HandleActivation(activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) {
	ctx := b.Ctx.WithFields(log.Fields{
		"GatewayID": activation.GatewayMetadata.GatewayId,
		"AppEUI":    *activation.AppEui,
		"DevEUI":    *activation.DevEui,
	})
	start := time.Now()
	defer func() {
		if err != nil {
			ctx.WithError(err).Warn("Could not handle activation")
		} else {
			ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation")
		}
	}()

	time := time.Now()

	b.status.activations.Mark(1)

	// De-duplicate uplink messages
	duplicates := b.deduplicateActivation(activation)
	if len(duplicates) == 0 {
		return nil, errDuplicateActivation
	}

	b.status.activationsUnique.Mark(1)

	base := duplicates[0]

	// Collect GatewayMetadata and DownlinkOptions
	var gatewayMetadata []*gateway.RxMetadata
	var downlinkOptions []*pb.DownlinkOption
	var deviceActivationResponse *pb.DeviceActivationResponse
	for _, duplicate := range duplicates {
		gatewayMetadata = append(gatewayMetadata, duplicate.GatewayMetadata)
		downlinkOptions = append(downlinkOptions, duplicate.DownlinkOptions...)
	}

	// Select best DownlinkOption
	if len(downlinkOptions) > 0 {
		deviceActivationResponse = &pb.DeviceActivationResponse{
			DownlinkOption: selectBestDownlink(downlinkOptions),
		}
	}

	// Build Uplink
	deduplicatedActivationRequest := &pb.DeduplicatedDeviceActivationRequest{
		Payload:            base.Payload,
		DevEui:             base.DevEui,
		AppEui:             base.AppEui,
		ProtocolMetadata:   base.ProtocolMetadata,
		GatewayMetadata:    gatewayMetadata,
		ActivationMetadata: base.ActivationMetadata,
		ServerTime:         time.UnixNano(),
		ResponseTemplate:   deviceActivationResponse,
	}

	// Send Activate to NS
	deduplicatedActivationRequest, err = b.ns.PrepareActivation(b.Component.GetContext(b.nsToken), deduplicatedActivationRequest)
	if err != nil {
		return nil, errors.Wrap(errors.FromGRPCError(err), "NetworkServer refused to prepare activation")
	}

	ctx = ctx.WithFields(log.Fields{
		"AppID": deduplicatedActivationRequest.AppId,
		"DevID": deduplicatedActivationRequest.DevId,
	})

	// Find Handler (based on AppEUI)
	var announcements []*pb_discovery.Announcement
	announcements, err = b.Discovery.GetAllHandlersForAppID(deduplicatedActivationRequest.AppId)
	if err != nil {
		return nil, err
	}
	if len(announcements) == 0 {
		return nil, errors.NewErrNotFound(fmt.Sprintf("Handler for AppID %s", deduplicatedActivationRequest.AppId))
	}

	ctx = ctx.WithField("NumHandlers", len(announcements))

	// LoRaWAN: Unmarshal and prepare version without MIC
	var phyPayload lorawan.PHYPayload
	err = phyPayload.UnmarshalBinary(deduplicatedActivationRequest.Payload)
	if err != nil {
		return nil, err
	}
	correctMIC := phyPayload.MIC
	phyPayload.MIC = [4]byte{0, 0, 0, 0}
	phyPayloadWithoutMIC, err := phyPayload.MarshalBinary()
	if err != nil {
		return nil, err
	}

	// Build Challenge
	challenge := &pb.ActivationChallengeRequest{
		Payload: phyPayloadWithoutMIC,
		AppId:   deduplicatedActivationRequest.AppId,
		DevId:   deduplicatedActivationRequest.DevId,
		AppEui:  deduplicatedActivationRequest.AppEui,
		DevEui:  deduplicatedActivationRequest.DevEui,
	}

	// Send Challenge to all handlers and collect responses
	var wg sync.WaitGroup
	responses := make(chan *challengeResponseWithHandler, len(announcements))
	for _, announcement := range announcements {
		conn, err := b.getHandlerConn(announcement.Id)
		if err != nil {
			ctx.WithError(err).Warn("Could not dial handler for Activation")
			continue
		}
		client := pb_handler.NewHandlerClient(conn)

		// Do async request
		wg.Add(1)
		go func(announcement *pb_discovery.Announcement) {
			res, err := client.ActivationChallenge(b.Component.GetContext(""), challenge)
			if err == nil && res != nil {
				responses <- &challengeResponseWithHandler{
					handler:  announcement,
					client:   client,
					response: res,
				}
			}
			wg.Done()
		}(announcement)
	}

	// Make sure to close channel when all requests are done
	go func() {
		wg.Wait()
		close(responses)
	}()

	var gotFirst bool
	var joinHandler *pb_discovery.Announcement
	var joinHandlerClient pb_handler.HandlerClient
	for res := range responses {
		var phyPayload lorawan.PHYPayload
		err = phyPayload.UnmarshalBinary(res.response.Payload)
		if err != nil {
			continue
		}
		if phyPayload.MIC != correctMIC {
			continue
		}

		if gotFirst {
			ctx.Warn("Duplicate Activation Response")
		} else {
			gotFirst = true
			joinHandler = res.handler
			joinHandlerClient = res.client
		}
	}

	// Activation not accepted by any broker
	if !gotFirst {
		ctx.Debug("Activation not accepted by any Handler")
		return nil, errors.New("Activation not accepted by any Handler")
	}

	ctx.WithField("HandlerID", joinHandler.Id).Debug("Forward Activation")

	handlerResponse, err := joinHandlerClient.Activate(b.Component.GetContext(""), deduplicatedActivationRequest)
	if err != nil {
		return nil, errors.Wrap(errors.FromGRPCError(err), "Handler refused activation")
	}
	handlerResponse, err = b.ns.Activate(b.Component.GetContext(b.nsToken), handlerResponse)
	if err != nil {
		return nil, errors.Wrap(errors.FromGRPCError(err), "NetworkServer refused activation")
	}

	res = &pb.DeviceActivationResponse{
		Payload:        handlerResponse.Payload,
		Message:        handlerResponse.Message,
		DownlinkOption: handlerResponse.DownlinkOption,
	}

	return res, nil
}
예제 #15
0
	pb "github.com/TheThingsNetwork/ttn/api/broker"
	pb_discovery "github.com/TheThingsNetwork/ttn/api/discovery"
	"github.com/TheThingsNetwork/ttn/api/gateway"
	pb_handler "github.com/TheThingsNetwork/ttn/api/handler"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/apex/log"
	"github.com/brocaar/lorawan"
)

type challengeResponseWithHandler struct {
	handler  *pb_discovery.Announcement
	client   pb_handler.HandlerClient
	response *pb.ActivationChallengeResponse
}

var errDuplicateActivation = errors.New("Not handling duplicate activation on this gateway")

func (b *broker) HandleActivation(activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) {
	ctx := b.Ctx.WithFields(log.Fields{
		"GatewayID": activation.GatewayMetadata.GatewayId,
		"AppEUI":    *activation.AppEui,
		"DevEUI":    *activation.DevEui,
	})
	start := time.Now()
	defer func() {
		if err != nil {
			ctx.WithError(err).Warn("Could not handle activation")
		} else {
			ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation")
		}
	}()
예제 #16
0
// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/utils/errors"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/apex/log"
)

// UplinkProcessor processes an uplink protobuf to an application-layer uplink message
type UplinkProcessor func(ctx log.Interface, ttnUp *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage) error

// DownlinkProcessor processes an application-layer downlink message to a downlik protobuf
type DownlinkProcessor func(ctx log.Interface, appDown *types.DownlinkMessage, ttnDown *pb_broker.DownlinkMessage) error

// ErrNotNeeded indicates that the processing of a message should be aborted
var ErrNotNeeded = errors.New("Further processing not needed")