Skip to content

Commit 3e8661e

Browse files
committed
fix: Source route table duplicating entries
1 parent a2102d3 commit 3e8661e

6 files changed

+77
-32
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zigbee-on-host",
3-
"version": "0.1.5",
3+
"version": "0.1.6",
44
"description": "ZigBee stack designed to run on a host and communicate with a radio co-processor (RCP)",
55
"engines": {
66
"node": ">=20.17.0"

src/dev/zohsave-to-readable.ts

+12
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ async function printSave(dataPath: string): Promise<void> {
5151
console.log(devicesJson);
5252

5353
await writeFile(join(dataPath, "zohsave-devices.json"), devicesJson, "utf8");
54+
55+
const routes = [];
56+
57+
for (const [addr16, entries] of driver.sourceRouteTable) {
58+
routes.push({ addr16, entries });
59+
}
60+
61+
const routesJson = JSON.stringify(routes, undefined, 2);
62+
63+
console.log(routesJson);
64+
65+
await writeFile(join(dataPath, "zohsave-routes.json"), routesJson, "utf8");
5466
}
5567

5668
if (require.main === module) {

src/drivers/ot-rcp-driver.ts

+37-1
Original file line numberDiff line numberDiff line change
@@ -2079,7 +2079,7 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
20792079

20802080
if (entries === undefined) {
20812081
this.sourceRouteTable.set(source16, [entry]);
2082-
} else {
2082+
} else if (!this.hasSourceRoute(source16, entry, entries)) {
20832083
entries.push(entry);
20842084
}
20852085
}
@@ -4252,6 +4252,42 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
42524252
}
42534253
}
42544254

4255+
/**
4256+
* Check if a source route entry for the given address is already present.
4257+
* If `existingEntries` not given and address16 doesn't have any entries, always returns false.
4258+
* @param address16 The network address to check for
4259+
* @param newEntry The entry to check
4260+
* @param existingEntries If given, skip the retrieval from `sourceRouteTable` and use these entries to check against instead
4261+
* @returns
4262+
*/
4263+
public hasSourceRoute(address16: number, newEntry: SourceRouteTableEntry, existingEntries?: SourceRouteTableEntry[]): boolean {
4264+
if (!existingEntries) {
4265+
existingEntries = this.sourceRouteTable.get(address16);
4266+
4267+
if (!existingEntries) {
4268+
return false;
4269+
}
4270+
}
4271+
4272+
for (const existingEntry of existingEntries) {
4273+
if (newEntry.pathCost !== existingEntry.pathCost) {
4274+
return false;
4275+
}
4276+
4277+
if (newEntry.relayAddresses.length !== existingEntry.relayAddresses.length) {
4278+
return false;
4279+
}
4280+
4281+
for (const relay of newEntry.relayAddresses) {
4282+
if (!existingEntry.relayAddresses.includes(relay)) {
4283+
return false;
4284+
}
4285+
}
4286+
}
4287+
4288+
return true;
4289+
}
4290+
42554291
/**
42564292
* Finds the best source route to the destination.
42574293
* Bails early if source routing is disabled, or destination16 is broadcast.

test/ot-rcp-driver.test.ts

+24-27
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,6 @@ describe("OT RCP Driver", () => {
180180
driver.networkUp = true;
181181
});
182182

183-
afterEach(async () => {});
184-
185183
it("handles loading with given network params - first start", async () => {
186184
const saveStateSpy = vi.spyOn(driver, "saveState");
187185

@@ -457,11 +455,6 @@ describe("OT RCP Driver", () => {
457455
driver.networkUp = true;
458456
});
459457

460-
afterEach(() => {
461-
driver.deviceTable.clear();
462-
driver.address16ToAddress64.clear();
463-
});
464-
465458
it("ignores bogus data before start of HDLC frame", async () => {
466459
const parserEmit = vi.spyOn(driver.parser, "emit");
467460
const frame = makeSpinelLastStatus(1);
@@ -815,11 +808,6 @@ describe("OT RCP Driver", () => {
815808
driver.networkUp = true;
816809
});
817810

818-
afterEach(() => {
819-
driver.deviceTable.clear();
820-
driver.address16ToAddress64.clear();
821-
});
822-
823811
it("receives frame NET2_TRANSPORT_KEY_NWK_FROM_COORD - not for coordinator", async () => {
824812
// encrypted only APS
825813
const onStreamRawFrameSpy = vi.spyOn(driver, "onStreamRawFrame");
@@ -1039,11 +1027,6 @@ describe("OT RCP Driver", () => {
10391027
driver.networkUp = true;
10401028
});
10411029

1042-
afterEach(() => {
1043-
driver.deviceTable.clear();
1044-
driver.address16ToAddress64.clear();
1045-
});
1046-
10471030
it("registers timers", async () => {
10481031
// creates a bottleneck with vitest & promises, noop it
10491032
vi.spyOn(driver, "savePeriodicState").mockImplementation(() => Promise.resolve());
@@ -1216,11 +1199,6 @@ describe("OT RCP Driver", () => {
12161199
driver.networkUp = true;
12171200
});
12181201

1219-
afterEach(() => {
1220-
driver.deviceTable.clear();
1221-
driver.address16ToAddress64.clear();
1222-
});
1223-
12241202
it("handles source routing", async () => {
12251203
// creates a bottleneck with vitest & promises, noop it
12261204
vi.spyOn(driver, "savePeriodicState").mockImplementation(() => Promise.resolve());
@@ -1375,6 +1353,30 @@ describe("OT RCP Driver", () => {
13751353
expect(findBestSourceRouteSpy).toHaveBeenLastCalledWith(0x4b8e, 5149013573816379n);
13761354
expect(findBestSourceRouteSpy).toHaveLastReturnedWith([0, [0xcb47], 2]);
13771355
expect(sendMACFrameSpy).toHaveBeenLastCalledWith(expect.any(Number), expect.any(Buffer), 0xcb47, undefined);
1356+
1357+
//-- no duplication of existing entries
1358+
driver.parser._transform(makeSpinelStreamRaw(6, NET4_ROUTE_RECORD_FROM_4B8E_RELAY_CB47), "utf8", () => {});
1359+
await vi.advanceTimersByTimeAsync(10);
1360+
// ROUTE_RECORD => OK
1361+
driver.parser._transform(makeSpinelLastStatus(6), "utf8", () => {});
1362+
await vi.advanceTimersByTimeAsync(10);
1363+
1364+
expect(driver.sourceRouteTable.get(0x4b8e)!.length).toStrictEqual(1);
1365+
});
1366+
1367+
it("checks if source route exists in entries for a given device", () => {
1368+
driver.sourceRouteTable.set(0x4b8e, [{ relayAddresses: [1, 2], pathCost: 3 }]);
1369+
const existingEntries = driver.sourceRouteTable.get(0x4b8e)!;
1370+
1371+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [1, 2], pathCost: 3 }, existingEntries)).toStrictEqual(true);
1372+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [1, 2], pathCost: 2 }, existingEntries)).toStrictEqual(false);
1373+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [3], pathCost: 2 }, existingEntries)).toStrictEqual(false);
1374+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [4, 5], pathCost: 3 }, existingEntries)).toStrictEqual(false);
1375+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [1, 2], pathCost: 3 })).toStrictEqual(true);
1376+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [1, 2], pathCost: 2 })).toStrictEqual(false);
1377+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [3], pathCost: 2 })).toStrictEqual(false);
1378+
expect(driver.hasSourceRoute(0x4b8e, { relayAddresses: [4, 5], pathCost: 3 })).toStrictEqual(false);
1379+
expect(driver.hasSourceRoute(0x12345, { relayAddresses: [4, 5], pathCost: 3 })).toStrictEqual(false);
13781380
});
13791381
});
13801382

@@ -1416,11 +1418,6 @@ describe("OT RCP Driver", () => {
14161418
driver.networkUp = true;
14171419
});
14181420

1419-
afterEach(() => {
1420-
driver.deviceTable.clear();
1421-
driver.address16ToAddress64.clear();
1422-
});
1423-
14241421
it("receives from NET5_GP_CHANNEL_REQUEST_BCAST while in commissioning mode", async () => {
14251422
driver.gpEnterCommissioningMode(0xfe); // in commissioning mode
14261423

test/zigbee.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ describe("Zigbee", () => {
919919

920920
expect(encFrame.subarray(0, -2)).toStrictEqual(NETDEF_ZGP_FRAME_BCAST_RECALL_SCENE_0.subarray(0, -2));
921921

922-
// TODO: Works against spec test vectors but not actual sniffed frame...
922+
// XXX: don't have the GP security key for this one
923923
// const encNWKGPHeader = structuredClone(nwkGPHeader);
924924

925925
// const encNWKFrame = encodeZigbeeNWKGPFrame(encNWKGPHeader, nwkGPPayload, NETDEF_NETWORK_KEY, macHeader.source64);

0 commit comments

Comments
 (0)