// 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 }
// 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 }
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 }
// 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") }
// 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") }
// 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 }
// 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 }
// 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 }
// 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 }
// 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 }
// 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) }
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 }
// 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) }
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 }
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") } }()
// 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")