diff --git a/cmd/pineconesim/sequences/attack_sybil_grid_bootstrap.json b/cmd/pineconesim/sequences/attack_sybil_grid_bootstrap.json new file mode 100644 index 00000000..512f5f52 --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_grid_bootstrap.json @@ -0,0 +1,976 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h16" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h16", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h4", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h8", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h12", + "Peer": "h16" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h13", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h14", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h15", + "Peer": "h16" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/sequences/attack_sybil_grid_setup.json b/cmd/pineconesim/sequences/attack_sybil_grid_setup.json new file mode 100644 index 00000000..efb46aaa --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_grid_setup.json @@ -0,0 +1,976 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h16" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h16", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h4", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h8", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h12", + "Peer": "h16" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h13", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h14", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h15", + "Peer": "h16" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/sequences/attack_sybil_hub_bootstrap.json b/cmd/pineconesim/sequences/attack_sybil_hub_bootstrap.json new file mode 100644 index 00000000..d176249b --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_hub_bootstrap.json @@ -0,0 +1,893 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/sequences/attack_sybil_hub_setup.json b/cmd/pineconesim/sequences/attack_sybil_hub_setup.json new file mode 100644 index 00000000..b170cdff --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_hub_setup.json @@ -0,0 +1,893 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/sequences/attack_sybil_line_bootstrap.json b/cmd/pineconesim/sequences/attack_sybil_line_bootstrap.json new file mode 100644 index 00000000..a9a85b6b --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_line_bootstrap.json @@ -0,0 +1,893 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "100", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "0", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h4", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h8", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h12", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h13", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h14", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/sequences/attack_sybil_line_setup.json b/cmd/pineconesim/sequences/attack_sybil_line_setup.json new file mode 100644 index 00000000..af0b56ce --- /dev/null +++ b/cmd/pineconesim/sequences/attack_sybil_line_setup.json @@ -0,0 +1,893 @@ +{ + "EventSequence": [ + { + "Command": "RemoveNode", + "Data": { + "Name": "m1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "m15" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "Sybil Proxy" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h1" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h2" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h3" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h4" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h5" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h6" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h7" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h8" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h9" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h10" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h11" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h12" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h13" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h14" + } + }, + { + "Command": "RemoveNode", + "Data": { + "Name": "h15" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m1", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m2", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m3", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m4", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m5", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m6", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m7", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m8", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m9", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m10", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m11", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m12", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m13", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m14", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "m15", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "Sybil Proxy", + "NodeType": "GeneralAdversary" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h1", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h2", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h3", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h4", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h5", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h6", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h7", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h8", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h9", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h10", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h11", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h12", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h13", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h14", + "NodeType": "Default" + } + }, + { + "Command": "AddNode", + "Data": { + "Name": "h15", + "NodeType": "Default" + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m1", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m2", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m3", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m4", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m5", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m6", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m7", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m8", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m9", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m10", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m11", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m12", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m13", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m14", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "ConfigureAdversaryDefaults", + "Data": { + "Node": "m15", + "DropRates": { + "Overall": "0", + "Keepalive": "0", + "TreeAnnouncement": "0", + "TreeRouted": "0", + "VirtualSnakeBootstrap": "0", + "VirtualSnakeBootstrapACK": "0", + "VirtualSnakeSetup": "100", + "VirtualSnakeSetupACK": "0", + "VirtualSnakeTeardown": "0", + "VirtualSnakeRouted": "0" + } + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "h2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h2", + "Peer": "h3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h3", + "Peer": "h4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h4", + "Peer": "h5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h5", + "Peer": "h6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h6", + "Peer": "h7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h7", + "Peer": "h8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h8", + "Peer": "h9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h9", + "Peer": "h10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h10", + "Peer": "h11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h11", + "Peer": "h12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h12", + "Peer": "h13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h13", + "Peer": "h14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h14", + "Peer": "h15" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m1" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m2" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m3" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m4" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m5" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m6" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m7" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m8" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m9" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m10" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m11" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m12" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m13" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m14" + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "Sybil Proxy", + "Peer": "m15" + } + }, + { + "Command": "Delay", + "Data": { + "Length": 10000 + } + }, + { + "Command": "AddPeer", + "Data": { + "Node": "h1", + "Peer": "Sybil Proxy" + } + } + ] +} diff --git a/cmd/pineconesim/simulator/adversary/drop_packets.go b/cmd/pineconesim/simulator/adversary/drop_packets.go index e8a0772a..1226fd51 100644 --- a/cmd/pineconesim/simulator/adversary/drop_packets.go +++ b/cmd/pineconesim/simulator/adversary/drop_packets.go @@ -94,6 +94,10 @@ func defaultFrameCount() PeerFrameCount { frameCount[types.TypeVirtualSnakeTeardown] = atomic.NewUint64(0) frameCount[types.TypeTreeRouted] = atomic.NewUint64(0) frameCount[types.TypeVirtualSnakeRouted] = atomic.NewUint64(0) + frameCount[types.TypeTreePing] = atomic.NewUint64(0) + frameCount[types.TypeTreePong] = atomic.NewUint64(0) + frameCount[types.TypeSNEKPing] = atomic.NewUint64(0) + frameCount[types.TypeSNEKPong] = atomic.NewUint64(0) peerFrameCount := PeerFrameCount{ frameCount: frameCount, @@ -123,8 +127,8 @@ func NewAdversaryRouter(log *log.Logger, sk ed25519.PrivateKey, debug bool) *Adv return adversary } -func (a *AdversaryRouter) Subscribe(ch chan events.Event) { - a.rtr.Subscribe(ch) +func (a *AdversaryRouter) Subscribe(ch chan events.Event) router.NodeState { + return a.rtr.Subscribe(ch) } func (a *AdversaryRouter) PublicKey() types.PublicKey { diff --git a/cmd/pineconesim/simulator/commands.go b/cmd/pineconesim/simulator/commands.go index 69295ac5..369af300 100644 --- a/cmd/pineconesim/simulator/commands.go +++ b/cmd/pineconesim/simulator/commands.go @@ -15,6 +15,7 @@ package simulator import ( + "crypto/ed25519" "fmt" "log" "strconv" @@ -47,6 +48,8 @@ func UnmarshalCommandJSON(command *SimCommandMsg) (SimCommand, error) { case SimAddNode: name := "" nodeType := UnknownType + privKeyStr := "" + var privKey *ed25519.PrivateKey = nil if val, ok := command.Event.(map[string]interface{})["Name"]; ok { name = val.(string) } else { @@ -58,7 +61,16 @@ func UnmarshalCommandJSON(command *SimCommandMsg) (SimCommand, error) { } else { err = fmt.Errorf("%sAddNode.NodeType field doesn't exist", FAILURE_PREAMBLE) } - msg = AddNode{name, nodeType} + + if val, ok := command.Event.(map[string]interface{})["PrivKey"]; ok { + privKeyStr = val.(string) + if len(privKeyStr) == ed25519.PrivateKeySize { + newPrivKey := ed25519.PrivateKey{} + copy(newPrivKey[:], privKeyStr) + privKey = &newPrivKey + } + } + msg = AddNode{name, nodeType, privKey} case SimRemoveNode: name := "" if val, ok := command.Event.(map[string]interface{})["Name"]; ok { @@ -315,21 +327,29 @@ func (c Delay) String() string { type AddNode struct { Node string NodeType APINodeType + PrivKey *ed25519.PrivateKey } // Tag AddNode as a Command func (c AddNode) Run(log *log.Logger, sim *Simulator) { log.Printf("Executing command %s", c) - if err := sim.CreateNode(c.Node, c.NodeType); err != nil { - log.Printf("Failed creating new node %s: %s", c.Node, err) - return + if c.PrivKey != nil { + if err := sim.CreateNodeWithKey(c.Node, c.NodeType, *c.PrivKey); err != nil { + log.Printf("Failed creating new node %s: %s", c.Node, err) + return + } + } else { + if err := sim.CreateNode(c.Node, c.NodeType); err != nil { + log.Printf("Failed creating new node %s: %s", c.Node, err) + return + } } sim.StartNodeEventHandler(c.Node, c.NodeType) } func (c AddNode) String() string { - return fmt.Sprintf("AddNode{Name:%s}", c.Node) + return fmt.Sprintf("AddNode{Name:%s, Type:%d, Private Key:%v}", c.Node, c.NodeType, c.PrivKey) } type RemoveNode struct { diff --git a/cmd/pineconesim/simulator/events.go b/cmd/pineconesim/simulator/events.go index 08e0fe7f..61161858 100644 --- a/cmd/pineconesim/simulator/events.go +++ b/cmd/pineconesim/simulator/events.go @@ -20,6 +20,11 @@ type SimEvent interface { isEvent() } +type NullEvent struct{} + +// Tag NullEvent as an Event +func (e NullEvent) isEvent() {} + type NodeAdded struct { Node string PublicKey string diff --git a/cmd/pineconesim/simulator/interface.go b/cmd/pineconesim/simulator/interface.go index 6ab182bf..48fc727d 100644 --- a/cmd/pineconesim/simulator/interface.go +++ b/cmd/pineconesim/simulator/interface.go @@ -36,11 +36,13 @@ func (sim *Simulator) ReportDistance(a, b string, l int64, snek bool) { sim.dists[a][b].ObservedTree = l } if sim.dists[a][b].Real == 0 { - na, _ := sim.graph.GetMapping(a) - nb, _ := sim.graph.GetMapping(b) - path, err := sim.graph.Shortest(na, nb) - if err == nil { - sim.dists[a][b].Real = path.Distance + if sim.graph != nil { + na, _ := sim.graph.GetMapping(a) + nb, _ := sim.graph.GetMapping(b) + path, err := sim.graph.Shortest(na, nb) + if err == nil { + sim.dists[a][b].Real = path.Distance + } } } } diff --git a/cmd/pineconesim/simulator/nodes.go b/cmd/pineconesim/simulator/nodes.go index 30994506..a85bab3b 100644 --- a/cmd/pineconesim/simulator/nodes.go +++ b/cmd/pineconesim/simulator/nodes.go @@ -35,6 +35,15 @@ func (sim *Simulator) Node(t string) *Node { } func (sim *Simulator) CreateNode(t string, nodeType APINodeType) error { + _, sk, err := ed25519.GenerateKey(nil) + if err != nil { + return fmt.Errorf("ed25519.GenerateKey: %w", err) + } + + return sim.CreateNodeWithKey(t, nodeType, sk) +} + +func (sim *Simulator) CreateNodeWithKey(t string, nodeType APINodeType, privKey ed25519.PrivateKey) error { if _, ok := sim.nodes[t]; ok { return fmt.Errorf("%s already exists!", t) } @@ -56,16 +65,12 @@ func (sim *Simulator) CreateNode(t string, nodeType APINodeType) error { return fmt.Errorf("net.Listen: %w", err) } } - _, sk, err := ed25519.GenerateKey(nil) - if err != nil { - return fmt.Errorf("ed25519.GenerateKey: %w", err) - } crc := crc32.ChecksumIEEE([]byte(t)) color := 31 + (crc % 6) logger := log.New(sim.log.Writer(), fmt.Sprintf("\033[%dmNode %s:\033[0m ", color, t), 0) n := &Node{ - SimRouter: sim.routerCreationMap[nodeType](logger, sk, true), + SimRouter: sim.routerCreationMap[nodeType](logger, privKey, true), l: l, ListenAddr: tcpaddr, } @@ -118,14 +123,14 @@ func (sim *Simulator) StartNodeEventHandler(t string, nodeType APINodeType) { ch := make(chan events.Event) handler := eventHandler{node: t, ch: ch} quit := make(chan bool) - go handler.Run(quit, sim) - sim.nodes[t].Subscribe(ch) + nodeState := sim.nodes[t].Subscribe(ch) sim.nodeRunnerChannelsMutex.Lock() sim.nodeRunnerChannels[t] = append(sim.nodeRunnerChannels[t], quit) sim.nodeRunnerChannelsMutex.Unlock() - phony.Block(sim.State, func() { sim.State._addNode(t, sim.nodes[t].PublicKey().String(), nodeType) }) + phony.Block(sim.State, func() { sim.State._addNode(t, sim.nodes[t].PublicKey().String(), nodeType, nodeState) }) + go handler.Run(quit, sim) } func (sim *Simulator) RemoveNode(node string) { diff --git a/cmd/pineconesim/simulator/pathfind.go b/cmd/pineconesim/simulator/pathfind.go index 19add920..d8e1ec72 100644 --- a/cmd/pineconesim/simulator/pathfind.go +++ b/cmd/pineconesim/simulator/pathfind.go @@ -70,6 +70,7 @@ func (sim *Simulator) PingSNEK(from, to string) (uint16, time.Duration, error) { } success = true + sim.log.Printf("Successful ping from %s to %s", from, to) sim.ReportDistance(from, to, int64(hops), true) return hops, rtt, nil } diff --git a/cmd/pineconesim/simulator/router.go b/cmd/pineconesim/simulator/router.go index 10a07914..16bea2f4 100644 --- a/cmd/pineconesim/simulator/router.go +++ b/cmd/pineconesim/simulator/router.go @@ -29,7 +29,7 @@ import ( type SimRouter interface { PublicKey() types.PublicKey Connect(conn net.Conn, options ...router.ConnectionOption) (types.SwitchPortID, error) - Subscribe(ch chan events.Event) + Subscribe(ch chan events.Event) router.NodeState Ping(ctx context.Context, a net.Addr) (uint16, time.Duration, error) Coords() types.Coordinates ConfigureFilterDefaults(rates adversary.DropRates) @@ -41,8 +41,8 @@ type DefaultRouter struct { rtr *router.Router } -func (r *DefaultRouter) Subscribe(ch chan events.Event) { - r.rtr.Subscribe(ch) +func (r *DefaultRouter) Subscribe(ch chan events.Event) router.NodeState { + return r.rtr.Subscribe(ch) } func (r *DefaultRouter) PublicKey() types.PublicKey { diff --git a/cmd/pineconesim/simulator/simulator.go b/cmd/pineconesim/simulator/simulator.go index e201b073..1b44ddda 100644 --- a/cmd/pineconesim/simulator/simulator.go +++ b/cmd/pineconesim/simulator/simulator.go @@ -217,6 +217,9 @@ func (sim *Simulator) handleTreeRootAnnUpdate(node string, root string, sequence rootName := "" if peerNode, err := sim.State.GetNodeName(root); err == nil { rootName = peerNode + } else { + log.Printf("Cannot convert %s to root for %s", root, node) + rootName = "UNKNOWN" } sim.State.Act(nil, func() { sim.State._updateTreeRootAnnouncement(node, rootName, sequence, time, coords) }) } diff --git a/cmd/pineconesim/simulator/state.go b/cmd/pineconesim/simulator/state.go index f628a305..f597fa34 100644 --- a/cmd/pineconesim/simulator/state.go +++ b/cmd/pineconesim/simulator/state.go @@ -19,6 +19,7 @@ import ( "reflect" "github.com/Arceliar/phony" + "github.com/matrix-org/pinecone/router" ) type RootAnnouncement struct { @@ -148,13 +149,7 @@ func (s *StateAccessor) GetNodeName(peerID string) (string, error) { node := "" err := fmt.Errorf("Provided peerID is not associated with a known node") - phony.Block(s, func() { - for k, v := range s._state.Nodes { - if v.PeerID == peerID { - node, err = k, nil - } - } - }) + phony.Block(s, func() { node, err = s._getNodeName(peerID) }) return node, err } @@ -169,8 +164,54 @@ func (s *StateAccessor) GetNodeCoords(name string) []uint64 { return coords } -func (s *StateAccessor) _addNode(name string, peerID string, nodeType APINodeType) { +func (s *StateAccessor) _getNodeName(peerID string) (string, error) { + node := "" + err := fmt.Errorf("Provided peerID is not associated with a known node") + + for k, v := range s._state.Nodes { + if v.PeerID == peerID { + node, err = k, nil + } + } + + return node, err +} + +func (s *StateAccessor) _addNode(name string, peerID string, nodeType APINodeType, nodeState router.NodeState) { s._state.Nodes[name] = NewNodeState(peerID, nodeType) + if peernode, err := s._getNodeName(nodeState.Parent); err == nil { + s._state.Nodes[name].Parent = peernode + } + connections := map[int]string{} + for i, node := range nodeState.Connections { + if i == 0 { + // NOTE : Skip connection on port 0 since it is the loopback port + continue + } + if peernode, err := s._getNodeName(node); err == nil { + connections[i] = peernode + } + } + s._state.Nodes[name].Connections = connections + s._state.Nodes[name].Coords = nodeState.Coords + root := "" + if peernode, err := s._getNodeName(nodeState.Announcement.RootPublicKey.String()); err == nil { + root = peernode + } + announcement := RootAnnouncement{ + Root: root, + Sequence: uint64(nodeState.Announcement.RootSequence), + Time: nodeState.AnnouncementTime, + } + s._state.Nodes[name].Announcement = announcement + if peernode, err := s._getNodeName(nodeState.AscendingPeer); err == nil { + s._state.Nodes[name].AscendingPeer = peernode + } + s._state.Nodes[name].AscendingPathID = nodeState.AscendingPathID + if peernode, err := s._getNodeName(nodeState.DescendingPeer); err == nil { + s._state.Nodes[name].DescendingPeer = peernode + } + s._state.Nodes[name].DescendingPathID = nodeState.DescendingPathID s._publish(NodeAdded{Node: name, PublicKey: peerID, NodeType: int(nodeType)}) } diff --git a/router/api.go b/router/api.go index 1b3f6c35..f32d297f 100644 --- a/router/api.go +++ b/router/api.go @@ -42,11 +42,64 @@ type PeerInfo struct { Zone string } +type NodeState struct { + PeerID string + Connections map[int]string + Parent string + Coords []uint64 + Announcement types.SwitchAnnouncement + AnnouncementTime uint64 + AscendingPeer string + AscendingPathID string + DescendingPeer string + DescendingPathID string +} + // Subscribe registers a subscriber to this node's events -func (r *Router) Subscribe(ch chan<- events.Event) { +func (r *Router) Subscribe(ch chan<- events.Event) NodeState { + var stateCopy NodeState phony.Block(r, func() { r._subscribers[ch] = &phony.Inbox{} + stateCopy.PeerID = r.public.String() + connections := map[int]string{} + for _, p := range r.state._peers { + if p == nil { + continue + } + connections[int(p.port)] = p.public.String() + } + stateCopy.Connections = connections + parent := "" + if r.state._parent != nil { + parent = r.state._parent.public.String() + } + stateCopy.Parent = parent + coords := []uint64{} + for _, coord := range r.Coords() { + coords = append(coords, uint64(coord)) + } + stateCopy.Coords = coords + announcement := r.state._rootAnnouncement() + stateCopy.Announcement = announcement.SwitchAnnouncement + stateCopy.AnnouncementTime = uint64(announcement.receiveTime.UnixNano()) + asc := "" + ascPath := "" + if r.state._ascending != nil { + asc = r.state._ascending.PublicKey.String() + ascPath = hex.EncodeToString(r.state._ascending.PathID[:]) + } + stateCopy.AscendingPeer = asc + stateCopy.AscendingPathID = ascPath + desc := "" + descPath := "" + if r.state._descending != nil { + desc = r.state._descending.PublicKey.String() + descPath = hex.EncodeToString(r.state._descending.PathID[:]) + } + stateCopy.DescendingPeer = desc + stateCopy.DescendingPathID = descPath }) + return stateCopy } func (r *Router) Coords() types.Coordinates { diff --git a/router/peer.go b/router/peer.go index 63a6640c..acc55b3d 100644 --- a/router/peer.go +++ b/router/peer.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "io" + "math" "net" "time" @@ -35,6 +36,7 @@ import ( const peerKeepaliveInterval = time.Second * 3 const peerKeepaliveTimeout = time.Second * 5 +const lowScoreThreshold = -100 // NOTE : peer scoring can go from -100 to 100 // Lower numbers for these consts are typically faster connections. const ( // These need to be a simple int type for gobind/gomobile to export them... @@ -49,21 +51,112 @@ const ( // These need to be a simple int type for gobind/gomobile to export them // the peering). Having separate actors allows reads and writes to take // place concurrently. type peer struct { - reader phony.Inbox - writer phony.Inbox - router *Router - port types.SwitchPortID // Not mutated after peer setup. - context context.Context // Not mutated after peer setup. - cancel context.CancelFunc // Not mutated after peer setup. - conn net.Conn // Not mutated after peer setup. - uri ConnectionURI // Not mutated after peer setup. - zone ConnectionZone // Not mutated after peer setup. - peertype ConnectionPeerType // Not mutated after peer setup. - public types.PublicKey // Not mutated after peer setup. - keepalives bool // Not mutated after peer setup. - started atomic.Bool // Thread-safe toggle for marking a peer as down. - proto *fifoQueue // Thread-safe queue for outbound protocol messages. - traffic *fairFIFOQueue // Thread-safe queue for outbound traffic messages. + reader phony.Inbox + writer phony.Inbox + router *Router + port types.SwitchPortID // Not mutated after peer setup. + context context.Context // Not mutated after peer setup. + cancel context.CancelFunc // Not mutated after peer setup. + conn net.Conn // Not mutated after peer setup. + uri ConnectionURI // Not mutated after peer setup. + zone ConnectionZone // Not mutated after peer setup. + peertype ConnectionPeerType // Not mutated after peer setup. + public types.PublicKey // Not mutated after peer setup. + keepalives bool // Not mutated after peer setup. + started atomic.Bool // Thread-safe toggle for marking a peer as down. + proto *fifoQueue // Thread-safe queue for outbound protocol messages. + traffic *fairFIFOQueue // Thread-safe queue for outbound traffic messages. + scoreCache float64 // Tracks peer score information from replaced sources + peerScoreAccumulator *time.Timer // Accumulates peer merit points over time. +} + +func (p *peer) EvaluatePeerScore(bootstraps neglectedNodeTable) int { + peerScore := 0.0 + // NOTE : more failing nodes through me lower peer score - no, this could result + // in a scenario where multiple attackers across the network aim to cause nodes + // near the center of the network to begin cutting off their peers. + // Distance truly is the best measure for proper attack isolation in this case. + // And the distance factor needs to grow exponentially to really ensure that + // central nodes don't inadvertently cutoff peers. + + for _, node := range bootstraps { + peerScore += p.EvaluatePeerScoreForNode(node) + } + + peerScore += float64(p.scoreCache) + + p.router.log.Printf("PeerScore: %s --- %f", p.public.String()[:8], peerScore) + return int(peerScore) +} + +func scoreMissingAck(hopCount uint64) float64 { + // TODO : Refine this + // NOTE : Gives exponential weighting the higher the hop count as that implies being closer to the problem + return -(0.5 + 0.01*math.Pow(float64(hopCount), 4)) +} + +func scoreAck(hopCount uint64) float64 { + // TODO : Better equation. Haven't refined this at all + return -(1 + 6/float64(hopCount-3)) +} + +func cachePeerScoreHistory(node *neglectedNodeEntry) { + for _, bootstrap := range node.FailedBootstraps { + if time.Since(bootstrap.ArrivalTime) > ackSettlingPeriod { + // NOTE : Cannot know which peer is suspect if an ACK was received. + // Since there isn't guaranteed to be a matching setup pair due to the forwarding + // logic being different for the setup frames, we have no way of guaranteeing + // if a setup frame was sent in response to the bootstrap ack. + if !bootstrap.Acknowledged { + bootstrap.Next.scoreCache += scoreMissingAck(node.HopCount) + } + } + } + + for _, setup := range node.FailedSetups { + if time.Since(setup.ArrivalTime) > ackSettlingPeriod { + if setup.Acknowledged { + setup.Prev.scoreCache += scoreAck(node.HopCount) + } else if !setup.Acknowledged { + setup.Next.scoreCache += scoreMissingAck(node.HopCount) + } + } + } +} + +func (p *peer) EvaluatePeerScoreForNode(node *neglectedNodeEntry) float64 { + peerScore := 0.0 + + for _, bootstrap := range node.FailedBootstraps { + if time.Since(bootstrap.ArrivalTime) > ackSettlingPeriod { + // NOTE : Cannot know which peer is suspect if an ACK was received. + // Since there isn't guaranteed to be a matching setup pair due to the forwarding + // logic being different for the setup frames, we have no way of guaranteeing + // if a setup frame was sent in response to the bootstrap ack. + if !bootstrap.Acknowledged && bootstrap.Next == p { + // NOTE : 1 for each new infraction + peerScore += scoreMissingAck(node.HopCount) + } + } else { + // NOTE : Ignoring this datapoint for scoring purposes + } + } + + for _, setup := range node.FailedSetups { + if time.Since(setup.ArrivalTime) > ackSettlingPeriod { + if setup.Acknowledged && setup.Prev == p { + // NOTE : 1 for each new infraction + peerScore += scoreAck(node.HopCount) + } else if !setup.Acknowledged && setup.Next == p { + // NOTE : 1 for each new infraction + peerScore += scoreMissingAck(node.HopCount) + } + } else { + // NOTE : Ignoring this datapoint for scoring purposes + } + } + + return peerScore } func (p *peer) MarshalJSON() ([]byte, error) { diff --git a/router/router.go b/router/router.go index ff352293..d6e4a5de 100644 --- a/router/router.go +++ b/router/router.go @@ -51,6 +51,7 @@ type Router struct { state *state secure bool _subscribers map[chan<- events.Event]*phony.Inbox + scorePeers bool } func NewRouter(logger types.Logger, sk ed25519.PrivateKey, debug bool) *Router { @@ -65,6 +66,7 @@ func NewRouter(logger types.Logger, sk ed25519.PrivateKey, debug bool) *Router { cancel: cancel, secure: !insecure, _subscribers: make(map[chan<- events.Event]*phony.Inbox), + scorePeers: false, } // Populate the node keys from the supplied private key. copy(r.private[:], sk) @@ -85,6 +87,17 @@ func NewRouter(logger types.Logger, sk ed25519.PrivateKey, debug bool) *Router { return r } +func (r *Router) EnablePeerScoring() { + r.scorePeers = true + r.state.assignResetNeglectTimer() + + for _, p := range r.state._peers { + if p != nil { + r.state.assignPeerScoreAccumulatorTimer(p) + } + } +} + func (r *Router) InjectPacketFilter(fn FilterFn) { phony.Block(r.state, func() { r.state._filterPacket = fn diff --git a/router/state.go b/router/state.go index 3f8b6197..583ba584 100644 --- a/router/state.go +++ b/router/state.go @@ -32,23 +32,32 @@ type FilterFn func(from types.PublicKey, f *types.Frame) bool // NOTE: Functions prefixed with an underscore (_) are only safe to be called // from the actor that owns them, in order to prevent data races. +type PeerScoreTable map[*peer]*PeerScore + +type PeerScore struct { + BootstrapFailures uint +} + // state is an actor that owns all of the mutable state for the Pinecone router. type state struct { phony.Inbox - r *Router - _peers []*peer // All switch ports, connected and disconnected - _ascending *virtualSnakeEntry // Next ascending node in keyspace - _descending *virtualSnakeEntry // Next descending node in keyspace - _candidate *virtualSnakeEntry // Candidate to replace the ascending node - _parent *peer // Our chosen parent in the tree - _announcements announcementTable // Announcements received from our peers - _table virtualSnakeTable // Virtual snake DHT entries - _ordering uint64 // Used to order incoming tree announcements - _sequence uint64 // Used to sequence our root tree announcements - _treetimer *time.Timer // Tree maintenance timer - _snaketimer *time.Timer // Virtual snake maintenance timer - _waiting bool // Is the tree waiting to reparent? - _filterPacket FilterFn // Function called when forwarding packets + r *Router + _peers []*peer // All switch ports, connected and disconnected + _ascending *virtualSnakeEntry // Next ascending node in keyspace + _descending *virtualSnakeEntry // Next descending node in keyspace + _candidate *virtualSnakeEntry // Candidate to replace the ascending node + _parent *peer // Our chosen parent in the tree + _announcements announcementTable // Announcements received from our peers + _table virtualSnakeTable // Virtual snake DHT entries + _neglectedNodes neglectedNodeTable // Nodes that are struggling to bootstrap + _bootstrapAttempt uint64 // Count of bootstrap attempts since last success + _ordering uint64 // Used to order incoming tree announcements + _sequence uint64 // Used to sequence our root tree announcements + _treetimer *time.Timer // Tree maintenance timer + _snaketimer *time.Timer // Virtual snake maintenance timer + _waiting bool // Is the tree waiting to reparent? + _filterPacket FilterFn // Function called when forwarding packets + _neglectReset *time.Timer } // _start resets the state and starts tree and virtual snake maintenance. @@ -64,6 +73,9 @@ func (s *state) _start() { s._announcements = make(announcementTable, portCount) s._table = virtualSnakeTable{} + s._bootstrapAttempt = 0 + s._neglectedNodes = make(neglectedNodeTable) + if s._treetimer == nil { s._treetimer = time.AfterFunc(announcementInterval, func() { s.Act(nil, s._maintainTree) @@ -71,15 +83,49 @@ func (s *state) _start() { } if s._snaketimer == nil { - s._snaketimer = time.AfterFunc(time.Second, func() { + s._snaketimer = time.AfterFunc(virtualSnakeMaintainInterval, func() { s.Act(nil, s._maintainSnake) }) } + if s.r.scorePeers { + s.assignResetNeglectTimer() + } + s._maintainTreeIn(0) s._maintainSnakeIn(0) } +func (s *state) assignResetNeglectTimer() { + if s._neglectReset == nil { + s._neglectReset = time.AfterFunc(0, func() { + s.Act(nil, s._resetNeglectedNodes) + }) + } +} + +func (s *state) assignPeerScoreAccumulatorTimer(p *peer) { + if p.peerScoreAccumulator == nil { + p.peerScoreAccumulator = time.AfterFunc(peerScoreResetPeriod, func() { + s.Act(nil, func() { s._accumulatePeerScore(p) }) + }) + } +} + +func (s *state) _resetNeglectedNodes() { + for pk := range s._neglectedNodes { + delete(s._neglectedNodes, pk) + } +} + +func (s *state) _accumulatePeerScore(p *peer) { + if p != nil && p.scoreCache < 100 { + p.scoreCache += 5 + } + + p.peerScoreAccumulator.Reset(peerScoreResetPeriod) +} + // _maintainTreeIn resets the tree maintenance timer to the specified // duration. func (s *state) _maintainTreeIn(d time.Duration) { @@ -119,19 +165,25 @@ func (s *state) _addPeer(conn net.Conn, public types.PublicKey, uri ConnectionUR queues = 16 } new = &peer{ - router: s.r, - port: types.SwitchPortID(i), - conn: conn, - public: public, - uri: uri, - zone: zone, - peertype: peertype, - keepalives: keepalives, - context: ctx, - cancel: cancel, - proto: newFIFOQueue(fifoNoMax), - traffic: newFairFIFOQueue(queues), + router: s.r, + port: types.SwitchPortID(i), + conn: conn, + public: public, + uri: uri, + zone: zone, + peertype: peertype, + keepalives: keepalives, + context: ctx, + cancel: cancel, + proto: newFIFOQueue(fifoNoMax), + traffic: newFairFIFOQueue(queues), + peerScoreAccumulator: nil, } + + if s.r.scorePeers { + s.assignPeerScoreAccumulatorTimer(new) + } + s._peers[i] = new s.r.log.Println("Connected to peer", new.public.String(), "on port", new.port) v, _ := s.r.active.LoadOrStore(hex.EncodeToString(new.public[:])+string(zone), atomic.NewUint64(0)) diff --git a/router/state_forward.go b/router/state_forward.go index 4998bfee..9b854873 100644 --- a/router/state_forward.go +++ b/router/state_forward.go @@ -52,10 +52,16 @@ func (s *state) _nextHopsFor(from *peer, frame *types.Frame) *peer { // teardowns, special handling will be done before forwarding if needed. func (s *state) _forward(p *peer, f *types.Frame) error { if s._filterPacket != nil && s._filterPacket(p.public, f) { - s.r.log.Printf("Packet of type %s destined for port %d [%s] was dropped due to filter rules", f.Type.String(), p.port, p.public.String()[:8]) + s.r.log.Printf("Packet of type %s destined for port %d [%s] from node %s [%s] was dropped due to filter rules", f.Type.String(), p.port, p.public.String()[:8], f.Source.String(), f.SourceKey.String()[:8]) return nil } + if s._ascending != nil { + if f.SourceKey.CompareTo(s.r.public) > 0 && f.SourceKey.CompareTo(s._ascending.PublicKey) < 0 { + s.r.log.Printf("Node detected that is a better ascending neighbour...") + } + } + nexthop := s._nextHopsFor(p, f) deadend := nexthop == p.router.local @@ -73,24 +79,21 @@ func (s *state) _forward(p *peer, f *types.Frame) error { return nil case types.TypeVirtualSnakeBootstrap: - // Bootstrap messages are only handled specially when they reach a dead end. - // Otherwise they are forwarded normally by falling through. - if deadend { - if err := s._handleBootstrap(p, f); err != nil { - return fmt.Errorf("s._handleBootstrap (port %d): %w", p.port, err) - } - return nil + // Bootstrap messages are handled at each node on the path. _handleBootstrap + // determines whether to respond to the bootstrap or further it along to the + // next hop so the packet is not forwarded here. + if err := s._handleBootstrap(p, f, nexthop, deadend); err != nil { + return fmt.Errorf("s._handleBootstrap (port %d): %w", p.port, err) } + return nil case types.TypeVirtualSnakeBootstrapACK: // Bootstrap ACK messages are only handled specially when they reach a dead end. // Otherwise they are forwarded normally by falling through. - if deadend { - if err := s._handleBootstrapACK(p, f); err != nil { - return fmt.Errorf("s._handleBootstrapACK (port %d): %w", p.port, err) - } - return nil + if err := s._handleBootstrapACK(p, f, nexthop, deadend); err != nil { + return fmt.Errorf("s._handleBootstrapACK (port %d): %w", p.port, err) } + return nil case types.TypeVirtualSnakeSetup: // Setup messages are handled at each node on the path. Since the _handleSetup diff --git a/router/state_snek.go b/router/state_snek.go index 1e58e98b..70ed9864 100644 --- a/router/state_snek.go +++ b/router/state_snek.go @@ -18,6 +18,7 @@ import ( "crypto/ed25519" "crypto/rand" "fmt" + "math" "time" "github.com/matrix-org/pinecone/types" @@ -29,6 +30,13 @@ import ( const virtualSnakeMaintainInterval = time.Second const virtualSnakeNeighExpiryPeriod = time.Hour +const bootstrapAttemptResetPoint = math.MaxUint32 // TODO : Pick a more meaningful value +const neglectedNodeTrackingPoint = 5 // NOTE : Start tracking a neglected node's bootstraps when their attempt count reaches this number +const maxNeglectedNodesToTrack = 10 // NOTE : This prevents an attacker from flooding large amounts of sybils into the network to cause a lot of inappropriate peer disconnections or memory spikes +const staleInformationPeriod = 3 * virtualSnakeMaintainInterval // Neglected node information older than this could be considered stale +const peerScoreResetPeriod = 2 * staleInformationPeriod // How long to wait before clearing peer scores +const ackSettlingPeriod = time.Second * 2 // TODO : Arrive at this value better +// NOTE : Must be < staleInformationPeriod to prevent exploit type virtualSnakeTable map[virtualSnakeIndex]*virtualSnakeEntry @@ -48,6 +56,32 @@ type virtualSnakeEntry struct { Active bool `json:"active"` } +type neglectedBootstrapData struct { + Acknowledged bool + ArrivalTime time.Time + Prev *peer + Next *peer +} + +type neglectedBootstrapTable map[types.VirtualSnakePathID]*neglectedBootstrapData + +type neglectedSetupData struct { + Acknowledged bool + ArrivalTime time.Time + Prev *peer + Next *peer +} + +type neglectedSetupTable map[types.VirtualSnakePathID]*neglectedSetupData + +type neglectedNodeTable map[types.PublicKey]*neglectedNodeEntry + +type neglectedNodeEntry struct { + HopCount uint64 // The hop count from the attempt when the entry was created + FailedBootstraps neglectedBootstrapTable // Map of failed bootstrap attempts + FailedSetups neglectedSetupTable // Map of failed setup attempts +} + // valid returns true if the update hasn't expired, or false if it has. It is // required for updates to time out eventually, in the case that paths don't get // torn down properly for some reason. @@ -143,9 +177,17 @@ func (s *state) _bootstrapNow() { // the same root node when processing the update. b := frameBufferPool.Get().(*[types.MaxFrameSize]byte) defer frameBufferPool.Put(b) + failing := byte(0) + if s.r.scorePeers { + if s._bootstrapAttempt > neglectedNodeTrackingPoint { + failing = 1 + } + } bootstrap := types.VirtualSnakeBootstrap{ - Root: ann.Root, + Root: ann.Root, + Failing: failing, } + // Generate a random path ID. if _, err := rand.Read(bootstrap.PathID[:]); err != nil { return @@ -158,6 +200,7 @@ func (s *state) _bootstrapNow() { ed25519.Sign(s.r.private[:], append(s.r.public[:], bootstrap.PathID[:]...)), ) } + n, err := bootstrap.MarshalBinary(b[:]) if err != nil { return @@ -174,6 +217,14 @@ func (s *state) _bootstrapNow() { // bootstrap packets. if p := s._nextHopsSNEK(send, true); p != nil && p.proto != nil { p.proto.push(send) + + if s.r.scorePeers { + s._bootstrapAttempt++ + if s._bootstrapAttempt >= bootstrapAttemptResetPoint { + s._bootstrapAttempt = 0 + s.r.log.Println("Resetting bootstrap attempt count") + } + } } } @@ -326,12 +377,16 @@ func getNextHopSNEK(params virtualSnakeNextHopParams) *peer { // _handleBootstrap is called in response to receiving a bootstrap packet. // This function will send a bootstrap ACK back to the sender. -func (s *state) _handleBootstrap(from *peer, rx *types.Frame) error { +func (s *state) _handleBootstrap(from *peer, rx *types.Frame, nexthop *peer, deadend bool) error { // Unmarshal the bootstrap. var bootstrap types.VirtualSnakeBootstrap if _, err := bootstrap.UnmarshalBinary(rx.Payload); err != nil { return fmt.Errorf("bootstrap.UnmarshalBinary: %w", err) } + if err := bootstrap.SanityCheck(from.public, s.r.public, rx.DestinationKey); err != nil { + return nil + } + if s.r.secure { // Check that the bootstrap message was signed by the node that claims // to have sent it. Silently drop it if there's a signature problem. @@ -343,6 +398,141 @@ func (s *state) _handleBootstrap(from *peer, rx *types.Frame) error { return nil } } + + if !deadend { + var frame *types.Frame = nil + if s.r.scorePeers { + // NOTE : Only add additional signatures if the node is struggling + if bootstrap.Failing > 0 { + trackBootstrap := false + if node, ok := s._neglectedNodes[rx.DestinationKey]; ok { + trackBootstrap = true + if uint64(len(bootstrap.Signatures)) > node.HopCount { + node.HopCount = uint64(len(bootstrap.Signatures)) + } + } else { + if len(s._neglectedNodes) < maxNeglectedNodesToTrack { + trackBootstrap = true + entry := &neglectedNodeEntry{ + HopCount: uint64(len(bootstrap.Signatures)), + FailedBootstraps: make(neglectedBootstrapTable), + FailedSetups: make(neglectedSetupTable), + } + s._neglectedNodes[rx.DestinationKey] = entry + } else { + for key, node := range s._neglectedNodes { + replaceNode := false + + latestArrival := time.UnixMicro(0) + for _, info := range node.FailedBootstraps { + if info.ArrivalTime.After(latestArrival) { + latestArrival = info.ArrivalTime + } + } + for _, info := range node.FailedSetups { + if info.ArrivalTime.After(latestArrival) { + latestArrival = info.ArrivalTime + } + } + + if len(bootstrap.Signatures) > int(node.HopCount) { + replaceNode = true + } else if time.Since(latestArrival) > staleInformationPeriod { + // NOTE : This is to prevent attackers from filling the neglected + // node list with artificially high hop counts then not continuing + // to send frames. + s.r.log.Println("Replace stale node") + replaceNode = true + } + + if replaceNode { + trackBootstrap = true + // NOTE : Reverse path routing guarantees still exist in this case. + // We just don't track this node for peer scoring purposes anymore. + cachePeerScoreHistory(node) + delete(s._neglectedNodes, key) + entry := &neglectedNodeEntry{ + HopCount: uint64(len(bootstrap.Signatures)), + FailedBootstraps: make(neglectedBootstrapTable), + FailedSetups: make(neglectedSetupTable), + } + s._neglectedNodes[rx.DestinationKey] = entry + break + } + } + } + } + + if trackBootstrap { + s._neglectedNodes[rx.DestinationKey].FailedBootstraps[bootstrap.PathID] = &neglectedBootstrapData{ + Acknowledged: false, + ArrivalTime: time.Now(), + Prev: from, + Next: nexthop, + } + } + + score := nexthop.EvaluatePeerScore(s._neglectedNodes) + if score <= lowScoreThreshold { + if s.r.scorePeers { + nexthop.stop(fmt.Errorf("peer score below threshold: %d", score)) + } + } + + longestHopCount := 1 + for _, node := range s._neglectedNodes { + if node.HopCount > uint64(longestHopCount) { + longestHopCount = int(node.HopCount) + } + } + + s._neglectReset.Reset(peerScoreResetPeriod) + if nexthop.started.Load() { + nexthop.peerScoreAccumulator.Reset(peerScoreResetPeriod) + } + } + + if s.r.public.CompareTo(rx.DestinationKey) > 0 { + switch { + case len(bootstrap.Signatures) == 0: + // Received a bootstrap with no signatures. Either it is directly + // from the originating node or the bootstrap hasn't passed by any + // bootstrap candidate. We are a candidate so sign the bootstrap. + fallthrough + case s.r.public.CompareTo(bootstrap.Signatures[len(bootstrap.Signatures)-1].PublicKey) < 0: + if s.r.scorePeers { + if err := bootstrap.Sign(s.r.private[:]); err != nil { + return fmt.Errorf("failed signing bootstrap: %w", err) + } + } + frame = getFrame() + frame.Type = types.TypeVirtualSnakeBootstrap + n, err := bootstrap.MarshalBinary(frame.Payload[:cap(frame.Payload)]) + if err != nil { + panic("failed to marshal bootstrap: " + err.Error()) + } + frame.Payload = frame.Payload[:n] + } + } + } + + if frame != nil { + of := rx + defer framePool.Put(of) + frame.DestinationKey = of.DestinationKey + frame.SourceKey = of.SourceKey + frame.Destination = of.Destination + frame.Source = of.Source + frame.Version = of.Version + frame.Extra = of.Extra + } else { + frame = rx + } + + nexthop.proto.push(frame) + return nil + } + // Check that the root key and sequence number in the update match our // current root, otherwise we won't be able to route back to them using // tree routing anyway. If they don't match, silently drop the bootstrap. @@ -396,23 +586,15 @@ func (s *state) _handleBootstrap(from *peer, rx *types.Frame) error { // packet. This function will work out whether the remote node is a suitable // candidate to set up an outbound path to, and if so, will send path setup // packets to the network. -func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame) error { +func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame, nexthop *peer, deadend bool) error { // Unmarshal the bootstrap ACK. var bootstrapACK types.VirtualSnakeBootstrapACK _, err := bootstrapACK.UnmarshalBinary(rx.Payload) if err != nil { return fmt.Errorf("bootstrapACK.UnmarshalBinary: %w", err) } + if s.r.secure { - // Verify that the source signature hasn't been changed by the remote - // side. If it has then it won't validate using our own public key. - if !ed25519.Verify( - s.r.public[:], - append(s.r.public[:], bootstrapACK.PathID[:]...), - bootstrapACK.SourceSig[:], - ) { - return nil - } // Verify that the destination signature is OK, which allows us to confirm // that the remote node accepted our bootstrap and that the remote node is // who they claim to be. @@ -424,6 +606,62 @@ func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame) error { return nil } } + + if !deadend { + knownFailure := false + if s.r.scorePeers { + if node, ok := s._neglectedNodes[rx.SourceKey]; ok { + if data, ok := node.FailedSetups[bootstrapACK.PathID]; ok { + if data.Acknowledged { + // NOTE : This peer is sending us duplicate frames. + return nil + } + + knownFailure = true + data.Prev.send(rx) + data.Acknowledged = true + score := data.Prev.EvaluatePeerScore(s._neglectedNodes) + if score <= lowScoreThreshold { + if s.r.scorePeers { + data.Prev.stop(fmt.Errorf("peer score below threshold: %d", score)) + } + } + + longestHopCount := 1 + for _, node := range s._neglectedNodes { + if node.HopCount > uint64(longestHopCount) { + longestHopCount = int(node.HopCount) + } + } + s._neglectReset.Reset(peerScoreResetPeriod) + if data.Prev.started.Load() { + data.Prev.peerScoreAccumulator.Reset(peerScoreResetPeriod) + } + } + } + } + + if !knownFailure { + if nexthop != nil && !nexthop.send(rx) { + s.r.log.Println("Dropping forwarded packet of type", rx.Type) + } + } + + return nil + } + + if s.r.secure { + // Verify that the source signature hasn't been changed by the remote + // side. If it has then it won't validate using our own public key. + if !ed25519.Verify( + s.r.public[:], + append(s.r.public[:], bootstrapACK.PathID[:]...), + bootstrapACK.SourceSig[:], + ) { + return nil + } + } + root := s._rootAnnouncement() update := false asc := s._ascending @@ -494,20 +732,20 @@ func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame) error { send.DestinationKey = rx.SourceKey send.SourceKey = s.r.public send.Payload = append(send.Payload[:0], b[:n]...) - nexthop := s.r.state._nextHopsTree(s.r.local, send) + setupNexthop := s.r.state._nextHopsTree(s.r.local, send) // Importantly, we will only create a DHT entry if it appears as though our next // hop has actually accepted the packet. Otherwise we'll create a path entry and // the setup message won't go anywhere. switch { - case nexthop == nil: + case setupNexthop == nil: fallthrough // No peer was identified, which shouldn't happen. - case nexthop.local(): + case setupNexthop.local(): fallthrough // The peer is local, which shouldn't happen. - case !nexthop.started.Load(): + case !setupNexthop.started.Load(): fallthrough // The peer has shut down or errored. - case nexthop.proto == nil: + case setupNexthop.proto == nil: fallthrough // The peer doesn't have a protocol queue for some reason. - case !nexthop.proto.push(send): + case !setupNexthop.proto.push(send): return nil // We failed to push the message into the peer queue. } index := virtualSnakeIndex{ @@ -519,7 +757,7 @@ func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame) error { Origin: rx.SourceKey, Target: rx.SourceKey, Source: s.r.local, - Destination: nexthop, + Destination: setupNexthop, LastSeen: time.Now(), Root: bootstrapACK.Root, } @@ -532,6 +770,7 @@ func (s *state) _handleBootstrapACK(from *peer, rx *types.Frame) error { s._sendTeardownForExistingPath(s.r.local, dhtKey.PublicKey, dhtKey.PathID) } } + // Install the new route into the DHT. s._table[index] = entry s._candidate = entry @@ -589,6 +828,47 @@ func (s *state) _handleSetup(from *peer, rx *types.Frame, nexthop *peer) error { s._sendTeardownForRejectedPath(rx.SourceKey, setup.PathID, from) // second call sends back to origin return nil } + + if s.r.scorePeers { + // NOTE : If you only see setups and not bootstraps then you can conclude that you aren't + // attached to a malicious peer. Otherwise you would have been guaranteed to also see the + // corresponding bootstrap frames from that node. + // Other than this case, there are no firm conclusions that can be drawn. + if node, ok := s._neglectedNodes[rx.SourceKey]; ok { + if nexthop != nil { + if _, ok := node.FailedSetups[setup.PathID]; ok { + // NOTE : This peer is sending us duplicate frames. + return nil + } + + node.FailedSetups[setup.PathID] = &neglectedSetupData{ + Acknowledged: false, + ArrivalTime: time.Now(), + Prev: from, + Next: nexthop, + } + + score := nexthop.EvaluatePeerScore(s._neglectedNodes) + if score <= lowScoreThreshold { + if s.r.scorePeers { + nexthop.stop(fmt.Errorf("peer score below threshold: %d", score)) + } + } + + longestHopCount := 1 + for _, node := range s._neglectedNodes { + if node.HopCount > uint64(longestHopCount) { + longestHopCount = int(node.HopCount) + } + } + s._neglectReset.Reset(peerScoreResetPeriod) + if nexthop.started.Load() { + nexthop.peerScoreAccumulator.Reset(peerScoreResetPeriod) + } + } + } + } + // If we're at the destination of the setup then update our predecessor // with information from the bootstrap. if rx.DestinationKey == s.r.public { @@ -624,6 +904,7 @@ func (s *state) _handleSetup(from *peer, rx *types.Frame, nexthop *peer) error { // there's a node out there that hasn't converged to a closer node // yet, so we'll just ignore the bootstrap. } + if !update { s._sendTeardownForRejectedPath(rx.SourceKey, setup.PathID, from) return nil @@ -723,10 +1004,43 @@ func (s *state) _handleSetupACK(from *peer, rx *types.Frame, nexthop *peer) erro continue } if v.Source.local() || v.Source.send(rx) { + if s.r.scorePeers { + if node, ok := s._neglectedNodes[rx.SourceKey]; ok { + if data, ok := node.FailedSetups[setup.PathID]; ok { + if data.Acknowledged { + // NOTE : This peer is sending us duplicate frames. + continue + } + data.Acknowledged = true + score := data.Prev.EvaluatePeerScore(s._neglectedNodes) + if score <= lowScoreThreshold { + if s.r.scorePeers { + data.Prev.stop(fmt.Errorf("peer score below threshold: %d", score)) + } + } + + longestHopCount := 1 + for _, node := range s._neglectedNodes { + if node.HopCount > uint64(longestHopCount) { + longestHopCount = int(node.HopCount) + } + } + s._neglectReset.Reset(peerScoreResetPeriod) + if data.Prev.started.Load() { + data.Prev.peerScoreAccumulator.Reset(peerScoreResetPeriod) + } + } + } + } + v.Active = true if v == s._candidate { s._setAscendingNode(v) s._candidate = nil + + if s.r.scorePeers { + s._bootstrapAttempt = 0 + } } } } diff --git a/router/tests/adversary_test.go b/router/tests/adversary_test.go new file mode 100644 index 00000000..6ee3d85a --- /dev/null +++ b/router/tests/adversary_test.go @@ -0,0 +1,246 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build test_adversary +// +build test_adversary + +package integration + +import ( + "crypto/ed25519" + "fmt" + "log" + "strings" + "testing" + "time" + + "github.com/matrix-org/pinecone/cmd/pineconesim/simulator/adversary" + "github.com/matrix-org/pinecone/types" +) + +var keyAA = ed25519.PrivateKey{162, 26, 23, 81, 135, 60, 226, 48, 76, 67, 143, 253, 156, 159, 49, 135, 0, 98, 62, 97, 233, 228, 220, 156, 6, 159, 71, 51, 158, 168, 98, 248, 170, 170, 180, 9, 248, 84, 20, 240, 141, 118, 183, 23, 7, 120, 31, 109, 72, 88, 6, 104, 51, 155, 117, 44, 50, 157, 81, 104, 18, 73, 249, 94} +var keyBB = ed25519.PrivateKey{126, 39, 115, 163, 236, 170, 93, 74, 222, 226, 27, 212, 18, 17, 52, 167, 199, 150, 49, 224, 81, 21, 77, 182, 245, 137, 255, 250, 142, 60, 57, 29, 187, 187, 168, 181, 14, 162, 182, 255, 57, 139, 163, 231, 33, 194, 45, 112, 112, 137, 68, 12, 23, 50, 62, 47, 208, 49, 37, 65, 112, 114, 153, 215} +var keyCC = ed25519.PrivateKey{88, 94, 100, 40, 136, 89, 209, 11, 241, 92, 136, 72, 232, 113, 52, 96, 173, 4, 73, 213, 223, 110, 222, 181, 169, 63, 140, 37, 169, 202, 227, 181, 204, 204, 102, 150, 191, 219, 16, 52, 178, 118, 12, 115, 3, 167, 138, 52, 255, 183, 159, 124, 134, 166, 167, 137, 65, 168, 16, 196, 41, 127, 21, 206} +var keyDD = ed25519.PrivateKey{70, 46, 50, 7, 34, 2, 71, 228, 187, 108, 175, 125, 158, 249, 114, 239, 111, 86, 10, 167, 27, 199, 131, 111, 50, 252, 162, 178, 37, 150, 219, 54, 221, 221, 113, 44, 216, 192, 87, 137, 164, 138, 177, 86, 229, 65, 246, 146, 191, 204, 107, 219, 4, 102, 213, 251, 71, 255, 51, 133, 168, 124, 30, 224} +var keyEE = ed25519.PrivateKey{8, 74, 248, 201, 148, 80, 73, 116, 112, 92, 53, 189, 238, 60, 68, 7, 18, 107, 63, 201, 29, 167, 243, 27, 21, 227, 204, 90, 225, 16, 113, 143, 238, 238, 73, 38, 91, 255, 177, 179, 179, 151, 55, 84, 129, 232, 94, 187, 42, 206, 159, 99, 249, 146, 32, 0, 133, 61, 101, 91, 77, 9, 179, 158} +var keyFF = ed25519.PrivateKey{110, 213, 18, 180, 196, 140, 66, 142, 132, 208, 11, 196, 242, 179, 47, 227, 153, 156, 76, 146, 254, 72, 89, 93, 42, 134, 28, 64, 153, 61, 18, 246, 255, 255, 144, 7, 51, 1, 43, 177, 12, 48, 76, 248, 107, 197, 3, 223, 189, 198, 162, 38, 4, 122, 61, 58, 142, 251, 63, 162, 33, 134, 9, 141} + +var sortedKeys = []ed25519.PrivateKey{keyAA, keyBB, keyCC, keyDD, keyEE, keyFF} + +func TestNodesAgreeOnCorrectTreeRootAdversaryDropTreeAnnouncements(t *testing.T) { + t.Parallel() + // Arrange + scenario := NewScenarioFixture(t) + genericNodes := []string{"Alice", "Bob", "Charlie"} + scenario.AddStandardNodes(genericNodes) + + adversaries := []string{"Mallory"} + scenario.AddAdversaryNodes(adversaries) + + defaultDropRates := adversary.NewDropRates() + defaultDropRates.Frames[types.TypeTreeAnnouncement] = 100 + peerDropRates := map[string]adversary.DropRates{} + scenario.ConfigureAdversary("Mallory", defaultDropRates, peerDropRates) + + // Act + scenario.ConnectNodes([]NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Charlie", "Mallory"}}) + + // Assert + nodesExpectedToConverge := (genericNodes) + scenario.Validate(createTreeStateCapture(nodesExpectedToConverge), nodesAgreeOnCorrectTreeRoot, 2*time.Second, 5*time.Second) +} + +type TestCase struct { + desc string + genericNodes []string + adversaries []string + connections []NodePair + framesToDrop []types.FrameType +} + +type KeyMap map[string]*ed25519.PrivateKey + +type SystemUnderTest struct { + scenario ScenarioFixture + genericNodeKeys KeyMap + adversaryKeys KeyMap +} + +func TestNodesCanPingAdversaryDropsFrames(t *testing.T) { + t.Parallel() + + cases := []TestCase{ + { + "Test2NodeLineSNEKBootstrap", + []string{"Alice", "Bob"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeBootstrap}, + }, + { + "Test3NodeLineSNEKBootstrap", + []string{"Alice", "Bob", "Charlie"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeBootstrap}, + }, + { + "Test3NodeLineSNEKBootstrapACK", + []string{"Alice", "Bob", "Charlie"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeBootstrapACK}, + }, + { + "Test3NodeLineSNEKSetup", + []string{"Alice", "Bob", "Charlie"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeSetup}, + }, + { + "Test3NodeLineSNEKSetupACK", + []string{"Alice", "Bob", "Charlie"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeSetupACK}, + }, + { + "Test3NodeTriangleSNEKBootstrap", + []string{"Alice", "Bob", "Charlie"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Charlie", "Alice"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeBootstrap}, + }, + { + "Test4NodeFanSNEKBootstrap", + []string{"Alice", "Bob", "Charlie", "Dan"}, + []string{"Mallory"}, + []NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}, NodePair{"Bob", "Dan"}, NodePair{"Dan", "Alice"}, NodePair{"Alice", "Mallory"}}, + []types.FrameType{types.TypeVirtualSnakeBootstrap}, + }, + } + + runSpecificTest := false + specificTest, specificPermutation := "Test4NodeFanSNEKBootstrap", 2 + + for _, tc := range cases { + if runSpecificTest && tc.desc != specificTest { + continue + } + tc := tc + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + totalNodes := len(tc.genericNodes) + len(tc.adversaries) + if totalNodes > len(sortedKeys) { + t.Fatalf("Too many nodes specified in the test case. There are only %d keys available.", len(sortedKeys)) + } + + keySubset := []*ed25519.PrivateKey{} + for i := 0; i < totalNodes; i++ { + keySubset = append(keySubset, &sortedKeys[i]) + } + keyPermutations := permutations(keySubset) + + for i, keyOrder := range keyPermutations { + if runSpecificTest && i != specificPermutation { + continue + } + i, keyOrder := i, keyOrder + t.Run(fmt.Sprintf("KeyPermutation#%d", i), func(t *testing.T) { + t.Parallel() + + log.Printf("Starting Test Case: %s", tc.desc) + // Arrange + sut := createSystemUnderTest(t, tc, keyOrder, []types.FrameType{types.TypeVirtualSnakeBootstrap}) + + // Act + sut.scenario.ConnectNodes(tc.connections) + + // Assert + sut.scenario.Validate(createTreeStateCapture(tc.genericNodes), nodesAgreeOnCorrectTreeRoot, SettlingTime, TestTimeout) + if !nodesCanAllPingEachOther(&sut.scenario, tc.genericNodes, false) { + time.Sleep(time.Second * 15) + if !nodesCanAllPingEachOther(&sut.scenario, tc.genericNodes, true) { + printSystemUnderTest(tc, &sut) + } + } + }) + } + }) + } +} + +func createSystemUnderTest(t *testing.T, tc TestCase, keyOrder []*ed25519.PrivateKey, dropTypes []types.FrameType) SystemUnderTest { + fixture := NewScenarioFixture(t) + keyIndex := 0 + genericNodeKeys := KeyMap{} + for _, node := range tc.genericNodes { + genericNodeKeys[node] = keyOrder[keyIndex] + keyIndex++ + } + fixture.AddStandardNodesWithKeys(genericNodeKeys) + + adversaryKeys := KeyMap{} + for _, node := range tc.adversaries { + adversaryKeys[node] = keyOrder[keyIndex] + keyIndex++ + } + fixture.AddAdversaryNodesWithKeys(adversaryKeys) + + defaultDropRates := adversary.NewDropRates() + for _, frameType := range dropTypes { + defaultDropRates.Frames[frameType] = 100 + } + peerDropRates := map[string]adversary.DropRates{} + for _, adversary := range tc.adversaries { + fixture.ConfigureAdversary(adversary, defaultDropRates, peerDropRates) + } + + return SystemUnderTest{fixture, genericNodeKeys, adversaryKeys} +} + +func printSystemUnderTest(tc TestCase, sut *SystemUnderTest) { + keys := map[string]string{} + for k, v := range sut.genericNodeKeys { + var sk types.PrivateKey + copy(sk[:], *v) + keys[k] = strings.ToUpper(sk.Public().String()[:8]) + } + for k, v := range sut.adversaryKeys { + var sk types.PrivateKey + copy(sk[:], *v) + keys[k] = strings.ToUpper(sk.Public().String()[:8]) + } + + sut.scenario.t.Errorf("Test Parameters: \nTest Case: %+v\nNode Keys: %+v", tc, keys) +} + +func permutations(xs []*ed25519.PrivateKey) (permuts [][]*ed25519.PrivateKey) { + var rc func([]*ed25519.PrivateKey, int) + rc = func(a []*ed25519.PrivateKey, k int) { + if k == len(a) { + permuts = append(permuts, append([]*ed25519.PrivateKey{}, a...)) + } else { + for i := k; i < len(xs); i++ { + a[k], a[i] = a[i], a[k] + rc(a, k+1) + a[k], a[i] = a[i], a[k] + } + } + } + rc(xs, 0) + + return permuts +} diff --git a/router/tests/basic_integration_test.go b/router/tests/basic_integration_test.go new file mode 100644 index 00000000..4d103dae --- /dev/null +++ b/router/tests/basic_integration_test.go @@ -0,0 +1,51 @@ +// copyright 2022 the matrix.org foundation c.i.c. +// +// licensed under the apache license, version 2.0 (the "license"); +// you may not use this file except in compliance with the license. +// you may obtain a copy of the license at +// +// http://www.apache.org/licenses/license-2.0 +// +// unless required by applicable law or agreed to in writing, software +// distributed under the license is distributed on an "as is" basis, +// without warranties or conditions of any kind, either express or implied. +// see the license for the specific language governing permissions and +// limitations under the license. + +package integration + +import ( + "testing" + "time" +) + +const SettlingTime time.Duration = time.Second * 2 +const TestTimeout time.Duration = time.Second * 5 + +func TestNodesAgreeOnCorrectTreeRoot(t *testing.T) { + t.Parallel() + // Arrange + scenario := NewScenarioFixture(t) + nodes := []string{"Alice", "Bob", "Charlie"} + scenario.AddStandardNodes(nodes) + + // Act + scenario.ConnectNodes([]NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}}) + + // Assert + scenario.Validate(createTreeStateCapture(nodes), nodesAgreeOnCorrectTreeRoot, SettlingTime, TestTimeout) +} + +func TestNodesAgreeOnCorrectSnakeFormation(t *testing.T) { + t.Parallel() + // Arrange + scenario := NewScenarioFixture(t) + nodes := []string{"Alice", "Bob", "Charlie"} + scenario.AddStandardNodes(nodes) + + // Act + scenario.ConnectNodes([]NodePair{NodePair{"Alice", "Bob"}, NodePair{"Bob", "Charlie"}}) + + // Assert + scenario.Validate(createSnakeStateCapture(nodes), nodesAgreeOnCorrectSnakeFormation, SettlingTime, TestTimeout) +} diff --git a/router/tests/scenario_fixture.go b/router/tests/scenario_fixture.go new file mode 100644 index 00000000..5c209d43 --- /dev/null +++ b/router/tests/scenario_fixture.go @@ -0,0 +1,194 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "crypto/ed25519" + "fmt" + "log" + "os" + "testing" + "time" + + "github.com/matrix-org/pinecone/cmd/pineconesim/simulator" + "github.com/matrix-org/pinecone/cmd/pineconesim/simulator/adversary" +) + +type EventHandlerResult int + +const ( + DoNothing EventHandlerResult = iota + StopSettlingTimer + StartSettlingTimer +) + +type InitialStateCapture func(state simulator.State) (initialState interface{}) +type EventHandler func(prevState interface{}, event simulator.SimEvent, isSettling bool) (newState interface{}, result EventHandlerResult) + +type NodePair struct { + A string + B string +} + +type ScenarioFixture struct { + t *testing.T + log *log.Logger + sim *simulator.Simulator +} + +func NewScenarioFixture(t *testing.T) ScenarioFixture { + log := log.New(os.Stdout, "\u001b[36m***\u001b[0m ", 0) + useSockets := false + runPing := false + acceptCommands := true + simulator := simulator.NewSimulator(log, useSockets, runPing, acceptCommands) + + return ScenarioFixture{ + t: t, + log: log, + sim: simulator, + } +} + +func (s *ScenarioFixture) AddStandardNodes(nodes []string) { + for _, node := range nodes { + cmd := simulator.AddNode{ + Node: node, + NodeType: simulator.DefaultNode, + } + cmd.Run(s.log, s.sim) + } +} + +func (s *ScenarioFixture) AddStandardNodesWithKeys(nodes map[string]*ed25519.PrivateKey) { + for node, key := range nodes { + cmd := simulator.AddNode{ + Node: node, + NodeType: simulator.DefaultNode, + PrivKey: key, + } + cmd.Run(s.log, s.sim) + } +} + +func (s *ScenarioFixture) AddAdversaryNodes(nodes []string) { + for _, node := range nodes { + cmd := simulator.AddNode{ + Node: node, + NodeType: simulator.GeneralAdversaryNode, + } + cmd.Run(s.log, s.sim) + } +} + +func (s *ScenarioFixture) AddAdversaryNodesWithKeys(nodes map[string]*ed25519.PrivateKey) { + for node, key := range nodes { + cmd := simulator.AddNode{ + Node: node, + NodeType: simulator.GeneralAdversaryNode, + PrivKey: key, + } + cmd.Run(s.log, s.sim) + } +} + +func (s *ScenarioFixture) ConnectNodes(conns []NodePair) { + for _, pair := range conns { + cmd := simulator.AddPeer{ + Node: pair.A, + Peer: pair.B, + } + cmd.Run(s.log, s.sim) + } +} + +func (s *ScenarioFixture) ConfigureAdversary(node string, defaults adversary.DropRates, peerDropRates map[string]adversary.DropRates) { + s.sim.ConfigureFilterDefaults(node, defaults) + for peer, rates := range peerDropRates { + s.sim.ConfigureFilterPeer(node, peer, rates) + } +} + +func (s *ScenarioFixture) SubscribeToSimState(ch chan simulator.SimEvent) simulator.State { + return s.sim.State.Subscribe(ch) +} + +func (s *ScenarioFixture) Validate(initialState InitialStateCapture, eventHandler EventHandler, settlingTime time.Duration, timeout time.Duration) { + testTimeout := time.NewTimer(timeout) + defer testTimeout.Stop() + + quit := make(chan bool) + output := make(chan string) + go assertState(s, initialState, eventHandler, quit, output, settlingTime) + + failed := false + + select { + case <-testTimeout.C: + failed = true + quit <- true + case <-output: + log.Println("Successful validation") + } + + if failed { + state := <-output + s.t.Fatalf("Test timeout reached. Current State: %s", state) + } +} + +func assertState(scenario *ScenarioFixture, stateCapture InitialStateCapture, eventHandler EventHandler, quit chan bool, output chan string, settlingTime time.Duration) { + settlingTimer := time.NewTimer(settlingTime) + settlingTimer.Stop() + + simUpdates := make(chan simulator.SimEvent) + state := scenario.SubscribeToSimState(simUpdates) + + prevState := stateCapture(state) + isSettling := false + + handleResult := func(newResult EventHandlerResult) { + switch newResult { + case StartSettlingTimer: + log.Println("Starting validation settling timer") + settlingTimer.Reset(settlingTime) + isSettling = true + case StopSettlingTimer: + if isSettling { + log.Println("Stopping validation settling timer") + settlingTimer.Stop() + isSettling = false + } + } + } + + newState, initialResult := eventHandler(prevState, simulator.NullEvent{}, isSettling) + handleResult(initialResult) + prevState = newState + + for { + select { + case <-quit: + output <- fmt.Sprintf("%+v", prevState) + return + case <-settlingTimer.C: + output <- "PASS" + case event := <-simUpdates: + newState, newResult := eventHandler(prevState, event, isSettling) + handleResult(newResult) + prevState = newState + } + } +} diff --git a/router/tests/snake_validation.go b/router/tests/snake_validation.go new file mode 100644 index 00000000..4347e9c3 --- /dev/null +++ b/router/tests/snake_validation.go @@ -0,0 +1,173 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "log" + "sort" + "sync" + + "github.com/matrix-org/pinecone/cmd/pineconesim/simulator" +) + +type SnakeNeighbours struct { + asc string + desc string +} + +type SnakeValidationState struct { + snake map[string]SnakeNeighbours + correctSnake map[string]SnakeNeighbours +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} + +func createSnakeStateCapture(nodes []string) InitialStateCapture { + return func(state simulator.State) interface{} { + snakeNeighbours := make(map[string]SnakeNeighbours) + for _, node := range nodes { + asc := state.Nodes[node].AscendingPeer + desc := state.Nodes[node].DescendingPeer + snakeNeighbours[node] = SnakeNeighbours{asc: asc, desc: desc} + } + + nodesByKey := make(byKey, 0, len(state.Nodes)) + for key, value := range state.Nodes { + if contains(nodes, key) { + nodesByKey = append(nodesByKey, Node{key, value.PeerID}) + } + } + sort.Sort(nodesByKey) + + correctSnake := make(map[string]SnakeNeighbours) + for i, node := range nodesByKey { + asc := "" + desc := "" + if i == 0 { + if len(nodesByKey) > 1 { + asc = nodesByKey[i+1].name + } + } else if i == len(nodesByKey)-1 { + if len(nodesByKey) > 1 { + desc = nodesByKey[i-1].name + } + } else { + asc = nodesByKey[i+1].name + desc = nodesByKey[i-1].name + } + + correctSnake[node.name] = SnakeNeighbours{asc: asc, desc: desc} + } + + return SnakeValidationState{snakeNeighbours, correctSnake} + } +} + +func nodesAgreeOnCorrectSnakeFormation(prevState interface{}, event simulator.SimEvent, isSettling bool) (newState interface{}, result EventHandlerResult) { + switch state := prevState.(type) { + case SnakeValidationState: + snakeWasCorrect := isSettling + isSnakeCorrect := func() bool { + snakeIsCorrect := true + for key, val := range state.snake { + if val.asc != state.correctSnake[key].asc || val.desc != state.correctSnake[key].desc { + snakeIsCorrect = false + break + } + } + return snakeIsCorrect + } + + switch e := event.(type) { + case simulator.SnakeAscUpdate: + if node, ok := state.snake[e.Node]; ok { + node.asc = e.Peer + state.snake[e.Node] = node + } + case simulator.SnakeDescUpdate: + if node, ok := state.snake[e.Node]; ok { + node.desc = e.Peer + state.snake[e.Node] = node + } + } + + action := DoNothing + isCorrect := isSnakeCorrect() + if isCorrect && !snakeWasCorrect { + action = StartSettlingTimer + } else if !isCorrect { + action = StopSettlingTimer + } + + return state, action + } + + return prevState, StopSettlingTimer +} + +func pingNode(scenario *ScenarioFixture, from string, to string, ch chan bool, wg *sync.WaitGroup, shouldFail bool) { + log.Printf("Pinging from %s to %s", from, to) + passed := true + if _, _, err := scenario.sim.PingSNEK(from, to); err != nil { + passed = false + if shouldFail { + scenario.t.Errorf("Failed pinging from %s to %s: %s", from, to, err) + } + } + + ch <- passed + wg.Done() +} + +func nodesCanAllPingEachOther(scenario *ScenarioFixture, nodes []string, shouldFail bool) bool { + wg := sync.WaitGroup{} + + numberOfPings := 0 + for range nodes { + for range nodes { + numberOfPings++ + } + } + + ch := make(chan bool, numberOfPings) + + for _, from := range nodes { + for _, to := range nodes { + if from != to { + wg.Add(1) + go pingNode(scenario, from, to, ch, &wg, shouldFail) + } + } + } + + wg.Wait() + close(ch) + + overallPass := true + for passed := range ch { + if !passed { + overallPass = false + } + } + + return overallPass +} diff --git a/router/tests/tree_validation.go b/router/tests/tree_validation.go new file mode 100644 index 00000000..f29f77d1 --- /dev/null +++ b/router/tests/tree_validation.go @@ -0,0 +1,92 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "sort" + + "github.com/matrix-org/pinecone/cmd/pineconesim/simulator" +) + +type TreeValidationState struct { + roots map[string]string + correctRoot string +} + +func createTreeStateCapture(nodes []string) InitialStateCapture { + return func(state simulator.State) interface{} { + lastRoots := make(map[string]string) + for _, node := range nodes { + lastRoots[node] = state.Nodes[node].Announcement.Root + } + + nodesByKey := make(byKey, 0, len(state.Nodes)) + for key, value := range state.Nodes { + nodesByKey = append(nodesByKey, Node{key, value.PeerID}) + } + sort.Sort(nodesByKey) + + correctRoot := nodesByKey[len(nodesByKey)-1].name + + return TreeValidationState{roots: lastRoots, correctRoot: correctRoot} + } +} + +func nodesAgreeOnCorrectTreeRoot(prevState interface{}, event simulator.SimEvent, isSettling bool) (newState interface{}, result EventHandlerResult) { + switch state := prevState.(type) { + case TreeValidationState: + treeWasConverged := isSettling + isTreeConverged := func() bool { + nodesAgreeOnRoot := true + rootSample := "" + for _, node := range state.roots { + rootSample = node + for _, comparison := range state.roots { + if node != comparison { + nodesAgreeOnRoot = false + break + } + } + } + return nodesAgreeOnRoot && rootSample == state.correctRoot + } + + switch e := event.(type) { + case simulator.TreeRootAnnUpdate: + if _, ok := state.roots[e.Node]; !ok { + // NOTE : only process events for nodes we care about + break + } + + if state.roots[e.Node] != e.Root { + state.roots[e.Node] = e.Root + } else { + break + } + } + + action := DoNothing + isConverged := isTreeConverged() + if isConverged && !treeWasConverged { + action = StartSettlingTimer + } else if !isConverged { + action = StopSettlingTimer + } + + return state, action + } + + return prevState, StopSettlingTimer +} diff --git a/router/tests/util.go b/router/tests/util.go new file mode 100644 index 00000000..243ba03c --- /dev/null +++ b/router/tests/util.go @@ -0,0 +1,34 @@ +// copyright 2022 the matrix.org foundation c.i.c. +// +// licensed under the apache license, version 2.0 (the "license"); +// you may not use this file except in compliance with the license. +// you may obtain a copy of the license at +// +// http://www.apache.org/licenses/license-2.0 +// +// unless required by applicable law or agreed to in writing, software +// distributed under the license is distributed on an "as is" basis, +// without warranties or conditions of any kind, either express or implied. +// see the license for the specific language governing permissions and +// limitations under the license. + +package integration + +type Node struct { + name string + key string +} + +type byKey []Node + +func (l byKey) Len() int { + return len(l) +} + +func (l byKey) Less(i, j int) bool { + return l[i].key < l[j].key +} + +func (l byKey) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} diff --git a/types/bootstrap_signature.go b/types/bootstrap_signature.go new file mode 100644 index 00000000..73d8df82 --- /dev/null +++ b/types/bootstrap_signature.go @@ -0,0 +1,48 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "crypto/ed25519" + "fmt" +) + +type BootstrapSignature struct { + PublicKey PublicKey + Signature Signature +} + +const BootstrapSignatureSize = ed25519.PublicKeySize + ed25519.SignatureSize + +func (s *BootstrapSignature) UnmarshalBinary(data []byte) (int, error) { + if size := len(data); size < BootstrapSignatureSize { + return 0, fmt.Errorf("BootstrapSignature expects at least %d bytes, got %d bytes", BootstrapSignatureSize, size) + } + offset := 0 + remaining := data[offset:] + remaining = remaining[copy(s.PublicKey[:], remaining):] + remaining = remaining[copy(s.Signature[:], remaining):] + return len(data) - len(remaining), nil +} + +func (s *BootstrapSignature) MarshalBinary(data []byte) (int, error) { + if len(data) < BootstrapSignatureSize { + return 0, fmt.Errorf("buffer is not big enough (must be %d bytes)", BootstrapSignatureSize) + } + offset := 0 + offset += copy(data[offset:offset+ed25519.PublicKeySize], s.PublicKey[:]) + offset += copy(data[offset:offset+ed25519.SignatureSize], s.Signature[:]) + return offset, nil +} diff --git a/types/virtualsnake.go b/types/virtualsnake.go index 8b74480a..9869cc40 100644 --- a/types/virtualsnake.go +++ b/types/virtualsnake.go @@ -5,6 +5,7 @@ import ( "crypto/ed25519" "encoding/hex" "fmt" + "os" ) const VirtualSnakePathIDLength = 8 @@ -24,10 +25,13 @@ type VirtualSnakeBootstrap struct { PathID VirtualSnakePathID SourceSig VirtualSnakePathSig Root + Failing byte + Signatures []BootstrapSignature } func (v *VirtualSnakeBootstrap) MarshalBinary(buf []byte) (int, error) { - if len(buf) < VirtualSnakePathIDLength+v.Root.Length()+ed25519.SignatureSize { + sizeOfSignatures := len(v.Signatures) * (BootstrapSignatureSize) + if len(buf) < VirtualSnakePathIDLength+v.Root.Length()+ed25519.SignatureSize+1+sizeOfSignatures { return 0, fmt.Errorf("buffer too small") } offset := 0 @@ -39,11 +43,20 @@ func (v *VirtualSnakeBootstrap) MarshalBinary(buf []byte) (int, error) { } offset += n offset += copy(buf[offset:], v.SourceSig[:]) + buf[offset] = v.Failing + offset += 1 + for _, sig := range v.Signatures { + n, err := sig.MarshalBinary(buf[offset:]) + if err != nil { + return 0, fmt.Errorf("sig.MarshalBinary: %w", err) + } + offset += n + } return offset, nil } func (v *VirtualSnakeBootstrap) UnmarshalBinary(buf []byte) (int, error) { - if len(buf) < VirtualSnakePathIDLength+v.Root.MinLength()+ed25519.SignatureSize { + if len(buf) < VirtualSnakePathIDLength+v.Root.MinLength()+ed25519.SignatureSize+1 { return 0, fmt.Errorf("buffer too small") } offset := 0 @@ -55,9 +68,65 @@ func (v *VirtualSnakeBootstrap) UnmarshalBinary(buf []byte) (int, error) { } offset += l offset += copy(v.SourceSig[:], buf[offset:]) + v.Failing = buf[offset] + offset += 1 + remaining := buf[offset:] + for i := uint64(0); len(remaining) >= BootstrapSignatureSize; i++ { + var signature BootstrapSignature + n, err := signature.UnmarshalBinary(remaining[:]) + if err != nil { + return 0, fmt.Errorf("signature.UnmarshalBinary: %w", err) + } + if _, ok := os.LookupEnv("PINECONE_DISABLE_SIGNATURES"); !ok { + if !ed25519.Verify(signature.PublicKey[:], buf[:len(buf)-len(remaining)], signature.Signature[:]) { + return 0, fmt.Errorf("signature verification failed for hop %d", i) + } + } + v.Signatures = append(v.Signatures, signature) + remaining = remaining[n:] + } + return offset, nil } +func (v *VirtualSnakeBootstrap) Sign(privKey ed25519.PrivateKey) error { + var body [65535]byte + n, err := v.MarshalBinary(body[:]) + if err != nil { + return fmt.Errorf("v.MarshalBinary: %w", err) + } + sig := BootstrapSignature{} + copy(sig.PublicKey[:], privKey.Public().(ed25519.PublicKey)) + if _, ok := os.LookupEnv("PINECONE_DISABLE_SIGNATURES"); !ok { + copy(sig.Signature[:], ed25519.Sign(privKey, body[:n])) + } + v.Signatures = append(v.Signatures, sig) + + return nil +} + +func (v *VirtualSnakeBootstrap) SanityCheck(from PublicKey, thisNode PublicKey, source PublicKey) error { + sigs := make(map[PublicKey]struct{}, len(v.Signatures)) + for index, sig := range v.Signatures { + if sig.PublicKey.CompareTo(thisNode) == 0 { + return fmt.Errorf("update already contains this node's signature") + } + if _, ok := sigs[sig.PublicKey]; ok { + return fmt.Errorf("update contains routing loop") + } + // NOTE : ensure each sig is a lower key than the last + if index > 0 && sig.PublicKey.CompareTo(v.Signatures[index-1].PublicKey) >= 0 { + return fmt.Errorf("update contains an invalid sequence of candidates") + } + // NOTE : ensure each sig is a higher key than the source + if sig.PublicKey.CompareTo(source) <= 0 { + return fmt.Errorf("update contains an invalid candidate") + } + sigs[sig.PublicKey] = struct{}{} + } + return nil +} + type VirtualSnakeBootstrapACK struct { PathID VirtualSnakePathID SourceSig VirtualSnakePathSig