// Update updates the node matching the given DevEUI. func (a *NodeAPI) Update(ctx context.Context, req *pb.UpdateNodeRequest) (*pb.UpdateNodeResponse, error) { var appEUI, devEUI lorawan.EUI64 var appKey lorawan.AES128Key if err := appEUI.UnmarshalText([]byte(req.AppEUI)); err != nil { return nil, err } if err := devEUI.UnmarshalText([]byte(req.DevEUI)); err != nil { return nil, err } if err := appKey.UnmarshalText([]byte(req.AppKey)); err != nil { return nil, err } node, err := storage.GetNode(a.ctx.DB, devEUI) if err != nil { return nil, err } node.AppEUI = appEUI node.AppKey = appKey node.RXDelay = uint8(req.RxDelay) node.RX1DROffset = uint8(req.Rx1DROffset) if req.ChannelListID > 0 { node.ChannelListID = &req.ChannelListID } else { node.ChannelListID = nil } if err := storage.UpdateNode(a.ctx.DB, node); err != nil { return nil, err } return &pb.UpdateNodeResponse{}, nil }
// handleCollectedJoinRequestPackets handles the received join-request. func handleCollectedJoinRequestPackets(ctx Context, rxPackets RXPackets) error { if len(rxPackets) == 0 { return errors.New("packet collector returned 0 packets") } rxPacket := rxPackets[0] var macs []string for _, p := range rxPackets { macs = append(macs, p.RXInfo.MAC.String()) } // MACPayload must be of type *lorawan.JoinRequestPayload jrPL, ok := rxPacket.PHYPayload.MACPayload.(*lorawan.JoinRequestPayload) if !ok { return fmt.Errorf("expected *lorawan.JoinRequestPayload, got: %T", rxPacket.PHYPayload.MACPayload) } log.WithFields(log.Fields{ "dev_eui": jrPL.DevEUI, "gw_count": len(rxPackets), "gw_macs": strings.Join(macs, ", "), "mtype": rxPackets[0].PHYPayload.MHDR.MType, }).Info("packet(s) collected") // get node information for this DevEUI node, err := storage.GetNode(ctx.DB, jrPL.DevEUI) if err != nil { return err } // validate the given nonce if !node.ValidateDevNonce(jrPL.DevNonce) { return fmt.Errorf("given dev-nonce %x has already been used before for node %s", jrPL.DevNonce, jrPL.DevEUI) } // get random (free) DevAddr devAddr, err := storage.GetRandomDevAddr(ctx.RedisPool, ctx.NetID) if err != nil { return fmt.Errorf("get random DevAddr error: %s", err) } // get app nonce appNonce, err := getAppNonce() if err != nil { return fmt.Errorf("get AppNonce error: %s", err) } // get the (optional) CFList cFList, err := storage.GetCFListForNode(ctx.DB, node) if err != nil { return fmt.Errorf("get CFList for node error: %s", err) } // get keys nwkSKey, err := getNwkSKey(node.AppKey, ctx.NetID, appNonce, jrPL.DevNonce) if err != nil { return fmt.Errorf("get NwkSKey error: %s", err) } appSKey, err := getAppSKey(node.AppKey, ctx.NetID, appNonce, jrPL.DevNonce) if err != nil { return fmt.Errorf("get AppSKey error: %s", err) } ns := models.NodeSession{ DevAddr: devAddr, DevEUI: jrPL.DevEUI, AppSKey: appSKey, NwkSKey: nwkSKey, FCntUp: 0, FCntDown: 0, AppEUI: node.AppEUI, RXDelay: node.RXDelay, RX1DROffset: node.RX1DROffset, CFList: cFList, } if err = storage.SaveNodeSession(ctx.RedisPool, ns); err != nil { return fmt.Errorf("save node-session error: %s", err) } // update the node (with updated used dev-nonces) if err = storage.UpdateNode(ctx.DB, node); err != nil { return fmt.Errorf("update node error: %s", err) } // construct the lorawan packet phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.JoinAccept, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.JoinAcceptPayload{ AppNonce: appNonce, NetID: ctx.NetID, DevAddr: ns.DevAddr, RXDelay: ns.RXDelay, DLSettings: lorawan.DLSettings{ RX2DataRate: uint8(common.Band.RX2DataRate), RX1DROffset: ns.RX1DROffset, }, CFList: cFList, }, } if err = phy.SetMIC(node.AppKey); err != nil { return fmt.Errorf("set MIC error: %s", err) } if err = phy.EncryptJoinAcceptPayload(node.AppKey); err != nil { return fmt.Errorf("encrypt join-accept error: %s", err) } // get data-rate uplinkDR, err := common.Band.GetDataRate(rxPacket.RXInfo.DataRate) if err != nil { return err } rx1DR := common.Band.RX1DataRate[uplinkDR][0] // get frequency rx1Freq, err := common.Band.GetRX1Frequency(rxPacket.RXInfo.Frequency) if err != nil { return err } txPacket := models.TXPacket{ TXInfo: models.TXInfo{ MAC: rxPacket.RXInfo.MAC, Timestamp: rxPacket.RXInfo.Timestamp + uint32(common.Band.JoinAcceptDelay1/time.Microsecond), Frequency: rx1Freq, Power: common.Band.DefaultTXPower, DataRate: common.Band.DataRates[rx1DR], CodeRate: rxPacket.RXInfo.CodeRate, }, PHYPayload: phy, } // window 1 if err = ctx.Gateway.SendTXPacket(txPacket); err != nil { return fmt.Errorf("send tx packet (rx window 1) to gateway error: %s", err) } // send a notification to the application that a node joined the network return ctx.Application.SendNotification(ns.AppEUI, ns.DevEUI, models.JoinNotificationType, models.JoinNotification{ DevAddr: ns.DevAddr, DevEUI: ns.DevEUI, }) }