2021-12-20 01:55:30 +01:00
|
|
|
package kernel.net;
|
|
|
|
|
2022-12-19 17:35:14 +01:00
|
|
|
import haxe.ds.ReadOnlyArray;
|
2022-02-24 02:05:43 +01:00
|
|
|
import kernel.net.Package.NetworkID;
|
2021-12-20 01:55:30 +01:00
|
|
|
import kernel.peripherals.Peripherals.Peripheral;
|
|
|
|
import kernel.Log;
|
|
|
|
import kernel.Timer;
|
|
|
|
import cc.OS;
|
|
|
|
|
2022-12-19 21:06:23 +01:00
|
|
|
using tink.CoreApi;
|
2021-12-20 01:55:30 +01:00
|
|
|
using Lambda;
|
2022-12-19 21:06:23 +01:00
|
|
|
using lib.Extender.LambdaExtender;
|
2021-12-20 01:55:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
Class responsible for everything network related.
|
|
|
|
Used to send and recceive packages.
|
|
|
|
**/
|
2022-02-21 15:35:37 +01:00
|
|
|
class Net {
|
2022-02-24 19:47:27 +01:00
|
|
|
/**
|
|
|
|
Depends on: KernelEvents
|
|
|
|
**/
|
2022-02-21 01:50:19 +01:00
|
|
|
public static var instance:Net;
|
2022-02-22 02:28:53 +01:00
|
|
|
public static inline final BRODCAST_PORT:Int = 65533;
|
|
|
|
public static inline final MESSAGE_TIMEOUT:Int = 3;
|
2022-03-01 16:59:53 +01:00
|
|
|
public static inline final DEFAULT_TTL:Int = 10;
|
2021-12-20 01:55:30 +01:00
|
|
|
|
2022-02-24 02:05:43 +01:00
|
|
|
public final networkID:NetworkID = OS.getComputerID();
|
2022-12-19 22:33:17 +01:00
|
|
|
private final responseBus:Map<Int, Callback<Outcome<Package,Error>>> = new Map();
|
2022-02-24 02:05:43 +01:00
|
|
|
private final protoHandlers:Map<String, Callback<Package>> = new Map();
|
2022-02-24 19:47:27 +01:00
|
|
|
private var interfaces:Array<INetworkInterface>;
|
2022-02-22 01:47:55 +01:00
|
|
|
|
2022-02-21 01:50:19 +01:00
|
|
|
@:allow(kernel.Init)
|
2022-02-21 15:35:37 +01:00
|
|
|
private function new() {
|
2022-02-24 19:47:27 +01:00
|
|
|
this.interfaces = [for (e in Peripheral.instance.getModems()) e ]; // TODO: is this the way to do it?
|
|
|
|
this.interfaces.push(Loopback.instance);
|
|
|
|
|
|
|
|
for (interf in interfaces){
|
2022-03-01 12:56:15 +01:00
|
|
|
setupInterf(interf);
|
2022-02-24 19:47:27 +01:00
|
|
|
}
|
2022-12-19 17:35:14 +01:00
|
|
|
|
|
|
|
setupPingHandle();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function setupPingHandle() {
|
2022-12-19 22:33:17 +01:00
|
|
|
this.registerProto("icmp", pack -> {
|
|
|
|
switch pack.data.type {
|
|
|
|
case "ping":
|
|
|
|
this.respondTo(pack, "pong");
|
|
|
|
case "died":
|
|
|
|
// If we get a "died" message from a node when one of our packages ttl hits 0
|
|
|
|
// the `data.msgId` prop is the message id
|
|
|
|
var msgID:Int = pack.data.msgID;
|
|
|
|
if (responseBus.exists(msgID)) {
|
|
|
|
responseBus[msgID].invoke(Outcome.Failure(new Error("TTL reached 0")));
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
Log.silly('Unknown icmp message: ${pack.data}');
|
|
|
|
}
|
2022-12-19 17:35:14 +01:00
|
|
|
});
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
2022-02-21 15:35:37 +01:00
|
|
|
|
2022-03-01 12:56:15 +01:00
|
|
|
private function setupInterf(interf: INetworkInterface) {
|
|
|
|
interf.onMessage.handle(pack -> handle(pack,interf));
|
|
|
|
interf.listen(networkID);
|
|
|
|
interf.listen(BRODCAST_PORT);
|
|
|
|
}
|
|
|
|
|
2022-02-24 02:05:43 +01:00
|
|
|
/**
|
|
|
|
Called when a new package comes in.
|
|
|
|
**/
|
2022-02-24 19:47:27 +01:00
|
|
|
private function handle(pack:Package,interf: INetworkInterface) {
|
2022-03-01 16:59:53 +01:00
|
|
|
if (pack.toID == this.networkID || pack.toID == Net.BRODCAST_PORT){
|
|
|
|
switch pack.type {
|
|
|
|
case Data(_) | DataNoResponse(_):
|
|
|
|
// Let a local proccess handle it
|
|
|
|
routeToProto(pack);
|
|
|
|
case Response:
|
|
|
|
// Got a response to a send message. Invoke the callback
|
|
|
|
if (responseBus.exists(pack.msgID)) {
|
2022-12-19 22:33:17 +01:00
|
|
|
responseBus[pack.msgID].invoke(Outcome.Success(pack));
|
2022-03-01 16:59:53 +01:00
|
|
|
}
|
2022-03-01 17:28:15 +01:00
|
|
|
case RouteDiscover(_) | RouteDiscoverResponse(_) | RouteDiscoverUpdate(_):
|
2022-03-01 16:59:53 +01:00
|
|
|
// Delegate to Routing
|
|
|
|
Routing.instance.handleRoutePackage(pack,interf);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
// New message received but its not ment for us. Forward if possible.
|
|
|
|
forwardPackage(pack);
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
private function generateMessageID():Int {
|
2021-12-20 01:55:30 +01:00
|
|
|
return Std.random(2147483647); // TODO: better uniqe number
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Send a message. Dont care if its reaches its destination nor it has a response.
|
|
|
|
**/
|
2022-02-21 15:35:37 +01:00
|
|
|
public function sendAndForget(dest:Int, proto:String, data:Dynamic) {
|
|
|
|
var pack:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
toID: dest,
|
|
|
|
fromID: networkID,
|
|
|
|
msgID: generateMessageID(),
|
|
|
|
type: DataNoResponse(proto),
|
2022-03-01 16:59:53 +01:00
|
|
|
data: data,
|
|
|
|
ttl: Net.DEFAULT_TTL,
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sendRaw(pack);
|
|
|
|
}
|
|
|
|
|
2022-03-01 16:59:53 +01:00
|
|
|
private function forwardPackage(pack: Package) {
|
|
|
|
if (pack.ttl == 0){
|
2022-12-19 22:33:17 +01:00
|
|
|
|
|
|
|
if (pack.type.match(Data(_))) {
|
|
|
|
// If the package is a data package and the ttl hits 0
|
|
|
|
// we send a "died" message to the sender
|
|
|
|
sendAndForget(pack.fromID, "icmp", {type:"died", msgID: pack.msgID});
|
|
|
|
}
|
|
|
|
|
2022-03-01 16:59:53 +01:00
|
|
|
// Drop package
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pack.ttl--;
|
|
|
|
|
2022-03-02 11:51:17 +01:00
|
|
|
if (!sendRaw(pack)){
|
2022-03-01 16:59:53 +01:00
|
|
|
// Cant forward
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function respondTo(pack:Package, data:Dynamic) {
|
|
|
|
if (pack.type.match(DataNoResponse(_))) {
|
2021-12-20 01:55:30 +01:00
|
|
|
Log.warn("Responed to a no response package. Ignoring");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var response = pack.createResponse(data);
|
|
|
|
|
|
|
|
sendRaw(response);
|
|
|
|
}
|
|
|
|
|
2022-02-24 02:05:43 +01:00
|
|
|
/**
|
|
|
|
Send to package to the localy register handler based on the proto
|
|
|
|
**/
|
|
|
|
private function routeToProto(pack:Package) {
|
2022-03-01 16:59:53 +01:00
|
|
|
var proto = switch pack.type {
|
2021-12-20 01:55:30 +01:00
|
|
|
case Data(proto):
|
|
|
|
proto;
|
|
|
|
case DataNoResponse(proto):
|
|
|
|
proto;
|
2022-03-01 16:59:53 +01:00
|
|
|
default:
|
2021-12-20 01:55:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-01 16:59:53 +01:00
|
|
|
if (!protoHandlers.exists(proto)) {
|
2022-03-04 13:28:25 +01:00
|
|
|
Log.warn('Trying to route package to proto: $proto but nothing was register');
|
2021-12-20 01:55:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-22 02:28:53 +01:00
|
|
|
protoHandlers[proto].invoke(pack);
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
|
2022-02-24 02:05:43 +01:00
|
|
|
/**
|
|
|
|
Just send the package to the right modem.
|
2022-03-01 16:59:53 +01:00
|
|
|
Returns true if message was send
|
2022-02-24 02:05:43 +01:00
|
|
|
**/
|
2022-03-01 16:59:53 +01:00
|
|
|
private function sendRaw(pack:Package): Bool {
|
|
|
|
var route = Routing.instance.getRouteToID(pack.toID);
|
|
|
|
if (route == null){
|
|
|
|
return false;
|
|
|
|
}
|
2022-02-24 19:47:27 +01:00
|
|
|
|
2022-03-01 16:59:53 +01:00
|
|
|
route.interf.send(route.rep,this.networkID,pack);
|
|
|
|
return true;
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
|
2022-02-24 02:05:43 +01:00
|
|
|
/**
|
|
|
|
Send a message and wait for a response.
|
|
|
|
**/
|
|
|
|
public function sendAndAwait(dest:NetworkID, proto:String, data:Dynamic):Promise<Package> {
|
2021-12-20 01:55:30 +01:00
|
|
|
return new Promise<Package>((resolve, reject) -> {
|
2022-02-21 15:35:37 +01:00
|
|
|
var pack:Package = {
|
2021-12-20 01:55:30 +01:00
|
|
|
toID: dest,
|
|
|
|
fromID: networkID,
|
|
|
|
msgID: generateMessageID(),
|
|
|
|
type: Data(proto),
|
2022-03-01 16:59:53 +01:00
|
|
|
data: data,
|
|
|
|
ttl: Net.DEFAULT_TTL,
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
2022-02-21 15:35:37 +01:00
|
|
|
|
|
|
|
var timeout:Timer = null;
|
2022-02-22 02:28:53 +01:00
|
|
|
|
2022-12-19 22:33:17 +01:00
|
|
|
responseBus[pack.msgID] = ((reponse:Outcome<Package,Error>) -> {
|
|
|
|
|
|
|
|
switch reponse {
|
|
|
|
case Success(pack):
|
|
|
|
resolve(pack);
|
|
|
|
case Failure(err):
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always remove the timeout
|
2022-02-21 15:35:37 +01:00
|
|
|
if (timeout != null) {
|
2021-12-20 01:55:30 +01:00
|
|
|
timeout.cancle();
|
|
|
|
}
|
|
|
|
});
|
2022-02-21 15:35:37 +01:00
|
|
|
|
|
|
|
timeout = new Timer(MESSAGE_TIMEOUT, () -> {
|
2022-02-22 02:28:53 +01:00
|
|
|
responseBus.remove(pack.msgID);
|
2022-02-24 02:05:43 +01:00
|
|
|
reject(new Error(InternalError,"Message timeout"));
|
2021-12-20 01:55:30 +01:00
|
|
|
});
|
2022-02-21 15:35:37 +01:00
|
|
|
|
2022-03-01 16:59:53 +01:00
|
|
|
if (!sendRaw(pack)){
|
|
|
|
reject(new Error("ID unreachable"));
|
|
|
|
}
|
2022-02-24 02:05:43 +01:00
|
|
|
|
|
|
|
// No callback link for you
|
|
|
|
return null;
|
2021-12-20 01:55:30 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-22 02:28:53 +01:00
|
|
|
public function registerProto(proto:String, callback:Callback<Package>) {
|
2022-02-21 15:35:37 +01:00
|
|
|
if (protoHandlers.exists(proto)) {
|
2021-12-20 01:55:30 +01:00
|
|
|
// Failed. Handler already exist.
|
|
|
|
// TODO: return error
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-22 02:28:53 +01:00
|
|
|
protoHandlers[proto] = callback;
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|
|
|
|
|
2022-02-21 15:35:37 +01:00
|
|
|
public function removeProto(proto:String) {
|
2021-12-20 01:55:30 +01:00
|
|
|
protoHandlers.remove(proto);
|
|
|
|
}
|
2022-12-17 15:08:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
Sends a ping package to the given id. Returns true if there was a response.
|
|
|
|
**/
|
2022-12-19 22:33:17 +01:00
|
|
|
public function ping(toID: NetworkID): Promise<Noise> {
|
|
|
|
return new Promise<Noise>((resolve,reject)->{
|
|
|
|
this.sendAndAwait(toID,"icmp",{type:"ping"}).handle(pack -> {
|
2022-12-17 15:08:07 +01:00
|
|
|
switch pack {
|
|
|
|
case Success(_):
|
2022-12-19 22:33:17 +01:00
|
|
|
resolve(Noise);
|
2022-12-17 15:08:07 +01:00
|
|
|
case Failure(err):
|
2022-12-19 22:33:17 +01:00
|
|
|
reject(err);
|
2022-12-17 15:08:07 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
}
|
2022-12-19 17:35:14 +01:00
|
|
|
|
|
|
|
public function getActiveProtocols(): ReadOnlyArray<String> {
|
|
|
|
var arr = new Array<String>();
|
|
|
|
|
|
|
|
for (proto in protoHandlers.keys()) {
|
|
|
|
arr.push(proto);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arr;
|
|
|
|
}
|
2021-12-20 01:55:30 +01:00
|
|
|
}
|