// Delete an existing record, prepending the prefix to the key if necessary func (s *RedisKVStore) Delete(key string) error { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } err := s.client.Watch(func(tx *redis.Tx) error { exists, err := tx.Exists(key).Result() if err != nil { return err } if !exists { return errors.NewErrNotFound(key) } _, err = tx.Pipelined(func(pipe *redis.Pipeline) error { pipe.Del(key) return nil }) if err != nil { return err } return nil }, key) if err != nil { return err } return nil }
// Contains returns wheter the set contains a given value, prepending the prefix to the key if necessary func (s *RedisSetStore) Contains(key string, value string) (res bool, err error) { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } res, err = s.client.SIsMember(key, value).Result() if err == redis.Nil { return res, errors.NewErrNotFound(key) } return res, err }
// Get one result, prepending the prefix to the key if necessary func (s *RedisSetStore) Get(key string) (res []string, err error) { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } res, err = s.client.SMembers(key).Result() if err == redis.Nil || len(res) == 0 { return res, errors.NewErrNotFound(key) } sort.Strings(res) return res, err }
// Get one result, prepending the prefix to the key if necessary func (s *RedisKVStore) Get(key string) (string, error) { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } result, err := s.client.Get(key).Result() if err == redis.Nil || result == "" { return "", errors.NewErrNotFound(key) } if err != nil { return "", err } return result, nil }
// Get one result, prepending the prefix to the key if necessary func (s *RedisMapStore) Get(key string) (interface{}, error) { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } result, err := s.client.HGetAll(key).Result() if err == redis.Nil || len(result) == 0 { return nil, errors.NewErrNotFound(key) } if err != nil { return nil, err } i, err := s.decoder(result) if err != nil { return nil, err } return i, nil }
func (n *networkServer) getDevAddr(constraints ...string) (types.DevAddr, error) { // Generate random DevAddr bytes var devAddr types.DevAddr copy(devAddr[:], random.Bytes(4)) // Get a random prefix that matches the constraints prefixes := n.GetPrefixesFor(constraints...) if len(prefixes) == 0 { return types.DevAddr{}, errors.NewErrNotFound(fmt.Sprintf("DevAddr prefix with constraints %v", constraints)) } // Select a prefix prefix := prefixes[random.Intn(len(prefixes))] // Apply the prefix devAddr = devAddr.WithPrefix(prefix) return devAddr, nil }
// Update an existing record, prepending the prefix to the key if necessary, optionally setting only the given properties func (s *RedisMapStore) Update(key string, value interface{}, properties ...string) error { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } if len(properties) == 0 { if i, ok := value.(ChangedFielder); ok { properties = i.ChangedFields() } } vmap, err := s.encoder(value, properties...) if err != nil { return err } if len(vmap) == 0 { return nil } err = s.client.Watch(func(tx *redis.Tx) error { exists, err := tx.Exists(key).Result() if err != nil { return err } if !exists { return errors.NewErrNotFound(key) } _, err = tx.Pipelined(func(pipe *redis.Pipeline) error { pipe.HMSet(key, vmap) return nil }) if err != nil { return err } return nil }, key) if err != nil { return err } return nil }
// GetFields for a record, prepending the prefix to the key if necessary func (s *RedisMapStore) GetFields(key string, fields ...string) (interface{}, error) { if !strings.HasPrefix(key, s.prefix) { key = s.prefix + key } result, err := s.client.HMGet(key, fields...).Result() if err == redis.Nil { return nil, errors.NewErrNotFound(key) } if err != nil { return nil, err } res := make(map[string]string) for i, field := range fields { if str, ok := result[i].(string); ok { res[field] = str } } i, err := s.decoder(res) if err != nil { return nil, err } return i, nil }
// see interface func (s *schedule) Schedule(id string, downlink *router_pb.DownlinkMessage) error { ctx := s.ctx.WithField("Identifier", id) s.Lock() defer s.Unlock() if item, ok := s.items[id]; ok { item.payload = downlink if lorawan := downlink.GetProtocolConfiguration().GetLorawan(); lorawan != nil { var time time.Duration if lorawan.Modulation == pb_lorawan.Modulation_LORA { // Calculate max ToA time, _ = toa.ComputeLoRa( uint(len(downlink.Payload)), lorawan.DataRate, lorawan.CodingRate, ) } if lorawan.Modulation == pb_lorawan.Modulation_FSK { // Calculate max ToA time, _ = toa.ComputeFSK( uint(len(downlink.Payload)), int(lorawan.BitRate), ) } item.length = uint32(time / 1000) } if time.Now().Before(item.deadlineAt) { // Schedule transmission before the Deadline go func() { waitTime := item.deadlineAt.Sub(time.Now()) ctx.WithField("Remaining", waitTime).Info("Scheduled downlink") <-time.After(waitTime) s.RLock() defer s.RUnlock() if s.downlink != nil { s.downlink <- item.payload } }() } else { go func() { s.RLock() defer s.RUnlock() if s.downlink != nil { overdue := time.Now().Sub(item.deadlineAt) if overdue < Deadline { // Immediately send it ctx.WithField("Overdue", overdue).Warn("Send Late Downlink") s.downlink <- item.payload } else { ctx.WithField("Overdue", overdue).Warn("Discard Late Downlink") } } else { ctx.Warn("Unable to send Downlink") } }() } return nil } return errors.NewErrNotFound(id) }
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 }
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 (h *handler) ConvertFromLoRaWAN(ctx log.Interface, ttnUp *pb_broker.DeduplicatedUplinkMessage, appUp *types.UplinkMessage) error { // Find Device dev, err := h.devices.Get(ttnUp.AppId, ttnUp.DevId) if err != nil { return err } // Check for LoRaWAN if lorawan := ttnUp.ProtocolMetadata.GetLorawan(); lorawan == nil { return errors.NewErrInvalidArgument("Activation", "does not contain LoRaWAN metadata") } // LoRaWAN: Unmarshal Uplink var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(ttnUp.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Uplink", "does not contain a MAC payload") } macPayload.FHDR.FCnt = ttnUp.ProtocolMetadata.GetLorawan().FCnt appUp.FCnt = macPayload.FHDR.FCnt ctx = ctx.WithField("FCnt", appUp.FCnt) // LoRaWAN: Validate MIC ok, err = phyPayload.ValidateMIC(lorawan.AES128Key(dev.NwkSKey)) if err != nil { return err } if !ok { return errors.NewErrNotFound("device that validates MIC") } // LoRaWAN: Decrypt if macPayload.FPort != nil && *macPayload.FPort != 0 && len(macPayload.FRMPayload) == 1 { appUp.FPort = *macPayload.FPort ctx = ctx.WithField("FCnt", appUp.FPort) if err := phyPayload.DecryptFRMPayload(lorawan.AES128Key(dev.AppSKey)); err != nil { return errors.NewErrInternal("Could not decrypt payload") } if len(macPayload.FRMPayload) == 1 { payload, ok := macPayload.FRMPayload[0].(*lorawan.DataPayload) if !ok { return errors.NewErrInvalidArgument("Uplink FRMPayload", "must be of type *lorawan.DataPayload") } appUp.PayloadRaw = payload.Bytes } } // LoRaWAN: Publish ACKs as events if macPayload.FHDR.FCtrl.ACK { h.mqttEvent <- &types.DeviceEvent{ AppID: appUp.AppID, DevID: appUp.DevID, Event: types.DownlinkAckEvent, } } return nil }