Network

include collections
include udp_impl
include tcp_serdes
include serdes

global {

    object udp = {

        object endpoint = {
            type this = struct {
                addr : ip.addr,
                port : ip.port
            }
        }

        action to_ip_endpoint(id:endpoint) returns (res:ip.endpoint) = {
            res.addr := id.addr;
            res.port := id.port;
            res.protocol := ip.udp;
        }

        action from_ip_endpoint(id:ip.endpoint) returns (res:endpoint) = {
            res.addr := id.addr;
            res.port := id.port;
        }

        type host = {me}

        module net_serdes(msg_t,ser,des) = {

            module socket = {

                parameter id : udp.endpoint
                attribute id.global_parameter = true

                action send(dst : udp.endpoint, msg : msg_t)
                action recv(src : udp.endpoint, msg : msg_t)

                implementation {

                    instance intf : udp_impl(udp.host,msg_t,ser,des)
                    var sock :intf.socket

                    after init {
                        sock := intf.open(udp.me,udp.to_ip_endpoint(id));
                    }

                    implement send {
                        call intf.send(udp.me,sock,udp.to_ip_endpoint(dst),msg);
                    }

                    implement intf.recv(h:udp.host, s:intf.socket, src:ip.endpoint, msg:msg_t) {
                        call recv(udp.from_ip_endpoint(src), msg:msg_t);
                    }
                }
            }

            trusted isolate iso = this

            attribute test = impl
        }

        module net(msg_t) = {
            instantiate net_serdes(msg_t,std_serdes.serializer,std_serdes.deserializer)
        }
    }


    object tcp = {

        object endpoint = {
            type this = struct {
                addr : ip.addr,
                port : ip.port
            }
        }

        module net(msg_t) = {

            specification {

                alias tag = index
                relation sent(S:tcp.endpoint,D:tcp.endpoint,T:tag,M:msg_t)
                function head(S:tcp.endpoint,D:tcp.endpoint) : tag
                function tail(S:tcp.endpoint,D:tcp.endpoint) : tag
                    after init {
                        sent(X,Y,T,M) := false;
                        head(X,Y) := 0:index;
                        tail(X,Y) := 0;
                    }
            }

            module socket = {

                parameter id : tcp.endpoint
                attribute id.global_parameter = true

                action send(dst : tcp.endpoint, msg : msg_t)
                action recv(src : tcp.endpoint, msg : msg_t)

                specification {

                    before send {
                        net.sent(id,dst,net.head(id,dst),msg) := true;
                        net.head(id,dst) := index.next(net.head(id,dst));
                    }
                    before recv {
                        assume net.tail(src,id) < net.head(src,id);
                        assume net.sent(src,id,net.tail(src,id),msg);
                        net.tail(src,id) := index.next(net.tail(src,id));
                    }

                    invariant net.tail(X,Y) <= net.head(X,Y)
                    invariant net.sent(X,Y,T,M1) & net.sent(X,Y,T,M2) -> M1 = M2
                }

                implementation {

                    instance intf : tcp_serdes(tcp.endpoint,msg_t)
                    var sock :intf.socket
                    relation connected_to(X:tcp.endpoint)
                    function dst_socket(X:tcp.endpoint) : intf.socket
                    function socket_src(X:intf.socket) : tcp.endpoint

                    after init {
                        sock := intf.open(id);
                    }

                    implement send {
                        if ~connected_to(dst) {
                            var dst_sock := intf.connect(id,dst);
                            dst_socket(dst) := dst_sock;
                            connected_to(dst) := true;
                        }
                        var ok := intf.send(dst_socket(dst),msg);
If send fails, it means the TCP connection closed. We can't recover from this because we don't know which messages were received, so we can't maintain message order.
                    }

                    implement intf.accept(s:intf.socket, src:tcp.endpoint) {
                        socket_src(s) := src;
                    }

                    implement intf.recv(s:intf.socket, msg:msg_t) {
                        call recv(socket_src(s), msg:msg_t);
                    }
                }
                trusted isolate iso = this

                attribute test = impl
            }

            trusted isolate iso = this

            attribute test = impl
        }

    }

    object tcp_ordered = {

        module net(msg_t) = {

            alias endpoint = tcp.endpoint

            specification {
                relation sent(S:tcp.endpoint,D:tcp.endpoint,M:msg_t)
            }

            module socket = {

                parameter id : tcp.endpoint
                attribute id.global_parameter = true

                action send(dst : tcp.endpoint, msg : msg_t)
                action recv(src : tcp.endpoint, msg : msg_t)

                specification {

                    after init {
                        net.sent(X,Y,M) := false;
                    }

                    before send {
                        require net.sent(id,dst,M) -> M < msg;
                    }
                    after send {
                        net.sent(id,dst,msg) := true;
                    }
                    before recv {
                        require net.sent(src,id,msg);
                        require net.sent(src,id,M) -> ~(M < msg)
                    }
                    after recv {
                        net.sent(src,id,msg) := false;
                    }
                }

                implementation {

                    instance intf : tcp_serdes(tcp.endpoint,msg_t)
                    var sock :intf.socket
                    relation connected_to(X:tcp.endpoint)
                    function dst_socket(X:tcp.endpoint) : intf.socket
                    function socket_src(X:intf.socket) : tcp.endpoint

                    after init {
                        sock := intf.open(id);
                    }

                    implement send {
                        if ~connected_to(dst) {
                            var dst_sock := intf.connect(id,dst);
                            dst_socket(dst) := dst_sock;
                            connected_to(dst) := true;
                        }
                        var ok := intf.send(dst_socket(dst),msg);
If send fails, it means the TCP connection closed. We can't recover from this because we don't know which messages were received, so we can't maintain message order.
                    }

                    implement intf.accept(s:intf.socket, src:tcp.endpoint) {
                        socket_src(s) := src;
                    }

                    implement intf.recv(s:intf.socket, msg:msg_t) {
                        call recv(socket_src(s), msg:msg_t);
                    }
                }
                trusted isolate iso = this

                attribute test = impl
            }

            trusted isolate iso = this

            attribute test = impl
        }

    }
This is a model of tcp that is useful for testing. It uses queues to store channel messages, which is much easier for the test generator to handle than the relational model above.

    object tcp_test = {

        module net(msg_t) = {

            specification {
                instance msgvec : vector(msg_t)
                relation sent(S:tcp.endpoint,D:tcp.endpoint,M:msg_t)
                function queue(S:tcp.endpoint,D:tcp.endpoint) : msgvec
                function len(S:tcp.endpoint,D:tcp.endpoint) : index
                after init {
                    len(S,D) := 0;
                }
            }

            module socket = {

                parameter id : tcp.endpoint
                attribute id.global_parameter = true

                action send(dst : tcp.endpoint, msg : msg_t)
                export action recv(src : tcp.endpoint, msg : msg_t)

                specification {

                    after send {
                        var thing := net.queue(id,dst);
                        net.queue(id,dst) := thing.append(msg);
                        var tmp2 := net.queue(id,dst);
                        net.len(id,dst) := tmp2.end;
                        net.sent(id,dst,msg) := true;
                    }
                    before recv {
                        require net.len(src,id) > 0;
                        var thing := net.queue(src,id);
                        require msg = thing.value(0);
                    }
                    after recv {
                        var thing := net.queue(src,id);
                        net.queue(src,id) := thing.segment(1,thing.end);
                        var thing2 := net.queue(src,id);
                        net.len(src,id) := thing2.end;
                    }
                }
                implementation {

                    instance intf : tcp_serdes(tcp.endpoint,msg_t)
                    var sock :intf.socket
                    relation connected_to(X:tcp.endpoint)
                    function dst_socket(X:tcp.endpoint) : intf.socket
                    function socket_src(X:intf.socket) : tcp.endpoint

                    after init {
                        sock := intf.open(id);
                    }

                    implement send {
                        if ~connected_to(dst) {
                            var dst_sock := intf.connect(id,dst);
                            dst_socket(dst) := dst_sock;
                            connected_to(dst) := true;
                        }
                        var ok := intf.send(dst_socket(dst),msg);
If send fails, it means the TCP connection closed. We can't recover from this because we don't know which messages were received, so we can't maintain message order.
                    }

                    implement intf.accept(s:intf.socket, src:tcp.endpoint) {
                        socket_src(s) := src;
                    }

                    implement intf.recv(s:intf.socket, msg:msg_t) {
                        call recv(socket_src(s), msg:msg_t);
                    }
                }
                trusted isolate iso = this
            }
            trusted isolate iso = this

        }
    }
This is a model of tcp that is useful for finite-state model echecking. It uses queues of bounded size to store channel messages. The model assumes that the bounded queue does not overflow. That means that behaviors in which the number of messages in the channel exceed the queue size are effectively ignored.

    object tcp_bounded = {

        module net(msg_t,max) = {

            specification {
                type len_t = {0..max}
                function queue(S:tcp.endpoint,D:tcp.endpoint,P:len_t) : msg_t
                function len(S:tcp.endpoint,D:tcp.endpoint) : len_t
                after init {
                    len(S,D) := 0;
                }
            }

            module socket = {

                parameter id : tcp.endpoint
                attribute id.global_parameter = true

                action send(dst : tcp.endpoint, msg : msg_t)
                export action recv(src : tcp.endpoint, msg : msg_t)

                specification {

                    after send {
                        var pos := net.len(id,dst);
                        assume pos < max;  # unsound!
                        net.queue(id,dst,pos) := msg;
                        net.len(id,dst) := pos + 1;
                    }
                    before recv {
                        require net.len(src,id) > 0;
                        require msg = net.queue(src,id,0);
                    }
                    after recv {
                        net.queue(src,id,P) := net.queue(src,id,P+1);
                        net.len(src,id) := net.len(src,id) - 1;
                    }
                }
                trusted isolate iso = this
            }
            trusted isolate iso = this
        }
    }

}