Skip to content

Commit 720b015

Browse files
committed
Implement unique IDs (UUID) for events and commands.
1 parent 040e19f commit 720b015

File tree

13 files changed

+436
-130
lines changed

13 files changed

+436
-130
lines changed

go.mod

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@ module github.com/brocaar/lora-gateway-bridge
33
go 1.12
44

55
require (
6-
github.com/brocaar/loraserver v0.0.0-20190729122155-2f0bb9c308bc
6+
github.com/brocaar/loraserver v0.0.0-20190909075954-156cedb76807
77
github.com/brocaar/lorawan v0.0.0-20190814113539-8eb2a8d6da09
88
github.com/dgrijalva/jwt-go v3.2.0+incompatible
99
github.com/eclipse/paho.mqtt.golang v1.2.0
10+
github.com/gofrs/uuid v3.2.0+incompatible
1011
github.com/golang/protobuf v1.3.2
1112
github.com/goreleaser/goreleaser v0.106.0
1213
github.com/gorilla/websocket v1.4.0
1314
github.com/pkg/errors v0.8.1
14-
github.com/prometheus/client_golang v1.0.0
15-
github.com/prometheus/common v0.6.0 // indirect
16-
github.com/prometheus/procfs v0.0.3 // indirect
15+
github.com/prometheus/client_golang v1.1.0
1716
github.com/sirupsen/logrus v1.4.2
18-
github.com/spf13/cobra v0.0.3
19-
github.com/spf13/viper v1.3.2
20-
github.com/stretchr/testify v1.3.0
17+
github.com/spf13/cobra v0.0.5
18+
github.com/spf13/viper v1.4.0
19+
github.com/stretchr/testify v1.4.0
2120
golang.org/x/lint v0.0.0-20190409202823-959b441ac422
2221
golang.org/x/tools v0.0.0-20190709211700-7b25e351ac0e // indirect
2322
)

go.sum

Lines changed: 119 additions & 50 deletions
Large diffs are not rendered by default.

internal/backend/basicstation/backend.go

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package basicstation
22

33
import (
4+
"crypto/rand"
45
"crypto/tls"
56
"crypto/x509"
7+
"encoding/binary"
68
"encoding/json"
79
"fmt"
810
"io/ioutil"
@@ -12,6 +14,7 @@ import (
1214
"sync"
1315
"time"
1416

17+
"github.com/gofrs/uuid"
1518
"github.com/golang/protobuf/ptypes"
1619
"github.com/gorilla/websocket"
1720
"github.com/pkg/errors"
@@ -55,6 +58,11 @@ type Backend struct {
5558
joinEUIs [][2]lorawan.EUI64
5659
frequencyMin uint32
5760
frequencyMax uint32
61+
62+
// diidMap stores the mapping of diid to UUIDs. This should take ~ 1MB of
63+
// memory. Optionaly this could be optimized by letting keys expire after
64+
// a given time.
65+
diidMap map[uint16][]byte
5866
}
5967

6068
// NewBackend creates a new Backend.
@@ -79,6 +87,8 @@ func NewBackend(conf config.Config) (*Backend, error) {
7987
region: band.Name(conf.Backend.BasicStation.Region),
8088
frequencyMin: conf.Backend.BasicStation.FrequencyMin,
8189
frequencyMax: conf.Backend.BasicStation.FrequencyMax,
90+
91+
diidMap: make(map[uint16][]byte),
8292
}
8393

8494
for _, n := range conf.Filters.NetIDs {
@@ -191,20 +201,41 @@ func (b *Backend) GetDisconnectChan() chan lorawan.EUI64 {
191201
}
192202

193203
func (b *Backend) SendDownlinkFrame(df gw.DownlinkFrame) error {
204+
b.Lock()
205+
defer b.Unlock()
206+
207+
// for backwards compatibility
208+
if df.Token == 0 {
209+
tokenB := make([]byte, 2)
210+
if _, err := rand.Read(tokenB); err != nil {
211+
return errors.Wrap(err, "read random bytes error")
212+
}
213+
214+
df.Token = uint32(binary.BigEndian.Uint16(tokenB))
215+
}
216+
194217
pl, err := structs.DownlinkFrameFromProto(b.band, df)
195218
if err != nil {
196219
return errors.Wrap(err, "downlink frame from proto error")
197220
}
198221

199222
var gatewayID lorawan.EUI64
200-
copy(gatewayID[:], df.TxInfo.GatewayId)
223+
var downID uuid.UUID
224+
copy(gatewayID[:], df.GetTxInfo().GetGatewayId())
225+
copy(downID[:], df.GetDownlinkId())
226+
227+
// store token to UUID mapping
228+
b.diidMap[uint16(df.Token)] = df.GetDownlinkId()
201229

202230
websocketSendCounter("dnmsg").Inc()
203231
if err := b.sendToGateway(gatewayID, pl); err != nil {
204232
return errors.Wrap(err, "send to gateway error")
205233
}
206234

207-
log.WithField("gateway_id", gatewayID).Info("backend/basicstation: downlink-frame message sent to gateway")
235+
log.WithFields(log.Fields{
236+
"gateway_id": gatewayID,
237+
"downlink_id": downID,
238+
}).Info("backend/basicstation: downlink-frame message sent to gateway")
208239

209240
return nil
210241
}
@@ -216,7 +247,7 @@ func (b *Backend) ApplyConfiguration(gwConfig gw.GatewayConfiguration) error {
216247
}
217248

218249
var gatewayID lorawan.EUI64
219-
copy(gatewayID[:], gwConfig.GatewayId)
250+
copy(gatewayID[:], gwConfig.GetGatewayId())
220251

221252
websocketSendCounter("router_config").Inc()
222253
if err := b.sendToGateway(gatewayID, rc); err != nil {
@@ -292,14 +323,13 @@ func (b *Backend) handleGateway(r *http.Request, c *websocket.Conn) {
292323
var cn lorawan.EUI64
293324
if err := cn.UnmarshalText([]byte(r.TLS.PeerCertificates[0].Subject.CommonName)); err != nil || cn != gatewayID {
294325
log.WithFields(log.Fields{
295-
"gateway_id": gatewayID,
326+
"gateway_id": gatewayID,
296327
"common_name": r.TLS.PeerCertificates[0].Subject.CommonName,
297328
}).Error("backend/basicstation: CommonName verification failed")
298329
return
299330
}
300331
}
301332

302-
303333
// make sure we're not overwriting an existing connection
304334
_, err := b.gateways.get(gatewayID)
305335
if err == nil {
@@ -459,10 +489,6 @@ func (b *Backend) handleVersion(gatewayID lorawan.EUI64, pl structs.Version) {
459489
}
460490

461491
func (b *Backend) handleJoinRequest(gatewayID lorawan.EUI64, v structs.JoinRequest) {
462-
log.WithFields(log.Fields{
463-
"gateway_id": gatewayID,
464-
}).Info("backend/basicstation: join-request received")
465-
466492
uplinkFrame, err := structs.JoinRequestToProto(b.band, gatewayID, v)
467493
if err != nil {
468494
log.WithError(err).WithFields(log.Fields{
@@ -471,14 +497,25 @@ func (b *Backend) handleJoinRequest(gatewayID lorawan.EUI64, v structs.JoinReque
471497
return
472498
}
473499

474-
b.uplinkFrameChan <- uplinkFrame
475-
}
500+
// set uplink id
501+
uplinkID, err := uuid.NewV4()
502+
if err != nil {
503+
log.WithError(err).WithFields(log.Fields{
504+
"gateway_id": gatewayID,
505+
}).Error("backend/basicstation: get random uplink id error")
506+
return
507+
}
508+
uplinkFrame.RxInfo.UplinkId = uplinkID[:]
476509

477-
func (b *Backend) handleProprietaryDataFrame(gatewayID lorawan.EUI64, v structs.UplinkProprietaryFrame) {
478510
log.WithFields(log.Fields{
479511
"gateway_id": gatewayID,
480-
}).Info("backend/basicstation: proprietary uplink frame received")
512+
"uplink_id": uplinkID,
513+
}).Info("backend/basicstation: join-request received")
514+
515+
b.uplinkFrameChan <- uplinkFrame
516+
}
481517

518+
func (b *Backend) handleProprietaryDataFrame(gatewayID lorawan.EUI64, v structs.UplinkProprietaryFrame) {
482519
uplinkFrame, err := structs.UplinkProprietaryFrameToProto(b.band, gatewayID, v)
483520
if err != nil {
484521
log.WithError(err).WithFields(log.Fields{
@@ -487,13 +524,27 @@ func (b *Backend) handleProprietaryDataFrame(gatewayID lorawan.EUI64, v structs.
487524
return
488525
}
489526

527+
// set uplink id
528+
uplinkID, err := uuid.NewV4()
529+
if err != nil {
530+
log.WithError(err).WithFields(log.Fields{
531+
"gateway_id": gatewayID,
532+
}).Error("backend/basicstation: get random uplink id error")
533+
return
534+
}
535+
uplinkFrame.RxInfo.UplinkId = uplinkID[:]
536+
537+
log.WithFields(log.Fields{
538+
"gateway_id": gatewayID,
539+
"uplink_id": uplinkID,
540+
}).Info("backend/basicstation: proprietary uplink frame received")
541+
490542
b.uplinkFrameChan <- uplinkFrame
491543
}
492544

493545
func (b *Backend) handleDownlinkTransmittedMessage(gatewayID lorawan.EUI64, v structs.DownlinkTransmitted) {
494-
log.WithFields(log.Fields{
495-
"gateway_id": gatewayID,
496-
}).Info("backend/basicstation: downlink transmitted message received")
546+
b.RLock()
547+
defer b.RUnlock()
497548

498549
txack, err := structs.DownlinkTransmittedToProto(gatewayID, v)
499550
if err != nil {
@@ -502,15 +553,20 @@ func (b *Backend) handleDownlinkTransmittedMessage(gatewayID lorawan.EUI64, v st
502553
}).Error("backend/basicstation: error converting downlink transmitted to protobuf message")
503554
return
504555
}
556+
txack.DownlinkId = b.diidMap[uint16(v.DIID)]
557+
558+
var downID uuid.UUID
559+
copy(downID[:], txack.GetDownlinkId())
560+
561+
log.WithFields(log.Fields{
562+
"gateway_id": gatewayID,
563+
"downlink_id": downID,
564+
}).Info("backend/basicstation: downlink transmitted message received")
505565

506566
b.downlinkTXAckChan <- txack
507567
}
508568

509569
func (b *Backend) handleUplinkDataFrame(gatewayID lorawan.EUI64, v structs.UplinkDataFrame) {
510-
log.WithFields(log.Fields{
511-
"gateway_id": gatewayID,
512-
}).Info("backend/basicstation: uplink frame received")
513-
514570
uplinkFrame, err := structs.UplinkDataFrameToProto(b.band, gatewayID, v)
515571
if err != nil {
516572
log.WithError(err).WithFields(log.Fields{
@@ -519,6 +575,21 @@ func (b *Backend) handleUplinkDataFrame(gatewayID lorawan.EUI64, v structs.Uplin
519575
return
520576
}
521577

578+
// set uplink id
579+
uplinkID, err := uuid.NewV4()
580+
if err != nil {
581+
log.WithError(err).WithFields(log.Fields{
582+
"gateway_id": gatewayID,
583+
}).Error("backend/basicstation: get random uplink id error")
584+
return
585+
}
586+
uplinkFrame.RxInfo.UplinkId = uplinkID[:]
587+
588+
log.WithFields(log.Fields{
589+
"gateway_id": gatewayID,
590+
"uplink_id": uplinkID,
591+
}).Info("backend/basicstation: uplink frame received")
592+
522593
b.uplinkFrameChan <- uplinkFrame
523594
}
524595

internal/backend/basicstation/backend_test.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66
"time"
77

8+
"github.com/gofrs/uuid"
89
"github.com/golang/protobuf/ptypes"
910
"github.com/gorilla/websocket"
1011
log "github.com/sirupsen/logrus"
@@ -136,6 +137,10 @@ func (ts *BackendTestSuite) TestUplinkDataFrame() {
136137
assert.NoError(ts.wsClient.WriteJSON(upf))
137138

138139
uplinkFrame := <-ts.backend.GetUplinkFrameChan()
140+
141+
assert.Len(uplinkFrame.RxInfo.UplinkId, 16)
142+
uplinkFrame.RxInfo.UplinkId = nil
143+
139144
assert.Equal(gw.UplinkFrame{
140145
PhyPayload: []byte{0x40, 0xf6, 0xff, 0xff, 0x0ff, 0x80, 0x90, 0x01, 0x01, 0x02, 0xec, 0xff, 0xff, 0xff},
141146
TxInfo: &gw.UplinkTXInfo{
@@ -184,6 +189,10 @@ func (ts *BackendTestSuite) TestJoinRequest() {
184189
assert.NoError(ts.wsClient.WriteJSON(jr))
185190

186191
uplinkFrame := <-ts.backend.GetUplinkFrameChan()
192+
193+
assert.Len(uplinkFrame.RxInfo.UplinkId, 16)
194+
uplinkFrame.RxInfo.UplinkId = nil
195+
187196
assert.Equal(gw.UplinkFrame{
188197
PhyPayload: []byte{0x00, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x14, 0x00, 0xf6, 0xff, 0xff, 0xff},
189198
TxInfo: &gw.UplinkTXInfo{
@@ -227,6 +236,10 @@ func (ts *BackendTestSuite) TestProprietaryDataFrame() {
227236
assert.NoError(ts.wsClient.WriteJSON(propf))
228237

229238
uplinkFrame := <-ts.backend.GetUplinkFrameChan()
239+
240+
assert.Len(uplinkFrame.RxInfo.UplinkId, 16)
241+
uplinkFrame.RxInfo.UplinkId = nil
242+
230243
assert.Equal(gw.UplinkFrame{
231244
PhyPayload: []byte{0x01, 0x02, 0x03, 0x04},
232245
TxInfo: &gw.UplinkTXInfo{
@@ -251,6 +264,10 @@ func (ts *BackendTestSuite) TestProprietaryDataFrame() {
251264

252265
func (ts *BackendTestSuite) TestDownlinkTransmitted() {
253266
assert := require.New(ts.T())
267+
id, err := uuid.NewV4()
268+
assert.NoError(err)
269+
270+
ts.backend.diidMap[12345] = id[:]
254271

255272
dtx := structs.DownlinkTransmitted{
256273
MessageType: structs.DownlinkTransmittedMessage,
@@ -260,9 +277,11 @@ func (ts *BackendTestSuite) TestDownlinkTransmitted() {
260277
assert.NoError(ts.wsClient.WriteJSON(dtx))
261278

262279
txAck := <-ts.backend.GetDownlinkTXAckChan()
280+
263281
assert.Equal(gw.DownlinkTXAck{
264-
GatewayId: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
265-
Token: 12345,
282+
GatewayId: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
283+
Token: 12345,
284+
DownlinkId: id[:],
266285
}, txAck)
267286
}
268287

@@ -359,8 +378,10 @@ func (ts *BackendTestSuite) TestApplyConfiguration() {
359378

360379
func (ts *BackendTestSuite) TestSendDownlinkFrame() {
361380
assert := require.New(ts.T())
381+
id, err := uuid.NewV4()
382+
assert.NoError(err)
362383

363-
err := ts.backend.SendDownlinkFrame(gw.DownlinkFrame{
384+
err = ts.backend.SendDownlinkFrame(gw.DownlinkFrame{
364385
PhyPayload: []byte{1, 2, 3, 4},
365386
TxInfo: &gw.DownlinkTXInfo{
366387
GatewayId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
@@ -383,10 +404,13 @@ func (ts *BackendTestSuite) TestSendDownlinkFrame() {
383404
},
384405
Context: []byte{0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4},
385406
},
386-
Token: 1234,
407+
Token: 1234,
408+
DownlinkId: id[:],
387409
})
388410
assert.NoError(err)
389411

412+
assert.Equal(id[:], ts.backend.diidMap[1234])
413+
390414
var df structs.DownlinkFrame
391415
assert.NoError(ts.wsClient.ReadJSON(&df))
392416

0 commit comments

Comments
 (0)