Cours 8



next up previous
Next: Chapitre 9 Up: Cours systèmes Previous: Chapitre 7

Plan

  • Datagrammes, Talk
  • Options des socket, inetd
  • Routage, RIP
  • Serveurs de noms, DNS, nslookup
  • Généraux byzantins
  • Exercices.

  • Cours 8

    Datagrammes

  • les datagrammes UDP connaissent les noms de ports (à la différence d'IP)
  • les datagrammes ne sont pas surs: peuvent ne pas arriver, ou arriver dans le désordre
  • les datagrammes sont utiles pour gérer une petite transaction, car l'ouverture du connection TCP coûte (plusieurs aller-retour dans l'établissement de la connexion).
    kpr% talk levy@toul
    ...
    kpr% netstat -a
    Active Internet connections (including servers)
    tcp        0      0  kpriss.inria.fr.1042   toul.inria.fr.1026     ESTABLISHED
    udp        0      0  kpriss.inria.fr.1416   *.*
    
    tou% talk levy@kpriss
    tou% netstat -a
    Active Internet connections (including servers)
    Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
    tcp        0      0  toul.inria.fr.1026     kpriss.inria.fr.1042   ESTABLISHED
    udp        0      0  toul.inria.fr.1143     *.*
    

  • Cours 8

    Datagrammes, un exemple: Talk

    main(argc, argv)
            int argc;
            char *argv[];
    {
    
            get_names(argc, argv);
            init_display();
            open_ctl();
            open_sockt();
            start_msgs();
            if (!check_local() )
                    invite_remote();
            end_msgs();
            set_edit_chars();
            talk();
    }
    
    get_addrs(my_machine_name, his_machine_name)
            char *my_machine_name, *his_machine_name;
    {       ...
            /* find the server's port */
            sp = getservbyname("ntalk", "udp");
            if (sp == 0) {
                    fprintf(stderr, "talk: %s/%s: service is not registered.\n",
                         "ntalk", "udp");
                    exit(-1);
            }
            daemon_port = sp->s_port;
    }
    u_short daemon_port;    /* port number of the talk daemon */
    
    int    ctl_sockt;
    int    sockt;
    int    invitation_waiting = 0;
    
    CTL_MSG msg;
    

    Cours 8

    Datagrammes, un exemple: Talk

    /* open the ctl socket */
    open_ctl() 
    {
        int length;
    
        ctl_addr.sin_port = 0;
        ctl_addr.sin_addr = my_machine_addr;
        ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0);
        if (ctl_sockt <= 0)
            p_error("Bad socket");
        if (bind(ctl_sockt, &ctl_addr, sizeof(ctl_addr), 0) != 0)
            p_error("Couldn't bind to control socket");
        length = sizeof(ctl_addr);
        if (getsockname(ctl_sockt, &ctl_addr, &length) == -1)
            p_error("Bad address for ctl socket");
    }
    
    open_sockt()
    {
        int length;
    
        my_addr.sin_addr = my_machine_addr;
        my_addr.sin_port = 0;
        sockt = socket(AF_INET, SOCK_STREAM, 0);
        if (sockt <= 0)
            p_error("Bad socket");
        if (bind(sockt, &my_addr, sizeof(my_addr)) != 0)
            p_error("Binding local socket");
        length = sizeof(my_addr);
        if (getsockname(sockt, &my_addr, &length) == -1)
            p_error("Bad address for socket");
    }
    
    invite_remote()
    {       ...
        announce_invite();
        setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
        message("Waiting for your party to respond");
        signal(SIGALRM, re_invite);
        (void) setjmp(invitebuf);
        while ((new_sockt = accept(sockt, 0, 0)) < 0) {
            if (errno == EINTR)
                continue;
            p_error("Unable to connect with your partÿ);
        }
        close(sockt);
        sockt = new_sockt;
        current_state = "Waiting for your party to respond";
        start_msgs();  ...
    }
    
    announce_invite()
    {
        CTL_RESPONSE response;
    
        current_state = "Trying to connect to your party's talk daemon";
        ctl_transact(his_machine_addr, msg, ANNOUNCE, &response);
        remote_id = response.id_num;
        if (response.answer != SUCCESS) {
            if (response.answer < NANSWERS)
                message(answers[response.answer]);
            quit();
        }
        /* leave the actual invitation on my talk daemon */
        ctl_transact(my_machine_addr, msg, LEAVE_INVITE, &response);
        local_id = response.id_num;
    }
    
    /*
     * SOCKDGRAM is unreliable, so we must repeat messages if we have
     * not recieved an acknowledgement within a reasonable amount
     * of time
     */
    ctl_transact(target, msg, type, rp)
        struct in_addr target;
        CTL_MSG msg;
        int type;
        CTL_RESPONSE *rp;
    {
        int read_mask, ctl_mask, nready, cc;
        struct timeval wait;
    
        msg.type = type;
        daemon_addr.sin_addr = target;
        daemon_addr.sin_port = daemon_port;
        ctl_mask = 1 << ctl_sockt;
    
        /*
         * Keep sending the message until a response of
         * the proper type is obtained.
         */
        do {
            wait.tv_sec = CTL_WAIT;
            wait.tv_usec = 0;
            /* resend message until a response is obtained */
            do {
                cc = sendto(ctl_sockt, (char *)&msg, sizeof (msg), 0,
                    &daemon_addr, sizeof (daemon_addr));
                if (cc != sizeof (msg)) {
                    if (errno == EINTR)
                        continue;
                    p_error("Error on write to talk daemon");
                }
                read_mask = ctl_mask;
                nready = select(32, &read_mask, 0, 0, &wait);
                if (nready < 0) {
                    if (errno == EINTR)
                        continue;
                    p_error("Error waiting for daemon responsë);
                }
            } while (nready == 0);
            /*
             * Keep reading while there are queued messages 
             * (this is not necessary, it just saves extra
             * request/acknowledgements being sent)
             */
            do {
                cc = recv(ctl_sockt, (char *)rp, sizeof (*rp), 0);
                if (cc < 0) {
                    if (errno == EINTR)
                        continue;
                    p_error("Error on read from talk daemon");
                }
                read_mask = ctl_mask;
                /* an immediate poll */
                timerclear(&wait);
                nready = select(32, &read_mask, 0, 0, &wait);
            } while (nready > 0 && (rp->vers != TALK_VERSION ||
                rp->type != type));
        } while (rp->vers != TALK_VERSION || rp->type != type);
        rp->id_num = ntohl(rp->id_num);
        rp->addr.sa_family = ntohs(rp->addr.sa_family);
    }
    

    Cours 8

    Datagrammes, un exemple: Talkd

    /* talkd */
    
    main(argc, argv)
            int argc;
            char *argv[];
    {
            struct sockaddr_in from;
            int fromlen, cc;
    
           if (getuid()) {
                    fprintf(stderr, "Talkd : not super user\n");
                    exit(1);
            }
            (void) gethostname(hostname, sizeof (hostname));
            (void) chdir("/dev");
            (void) signal(SIGALRM, timeout);
            (void) alarm(TIMEOUT);
            for (;;) {
                    extern int errno;
    
                    fromlen = sizeof(from);
                    cc = recvfrom(0, (char *)&request, sizeof (request), 0,
                        &from, &fromlen);
                    if (cc != sizeof(request)) {
                            if (cc < 0 && errno != EINTR)
                            perror("recvfrom");
                            continue;
                    }
                    lastmsgtime = time(0);
                    swapmsg(&request);
                    if (debug) print_request(&request);
                    process_request(&request, &response);
                    /* can block here, is this what I want? */
                    cc = sendto(0, (char *) &response,
                        sizeof (response), 0, &request.ctl_addr,
                        sizeof (request.ctl_addr));
                    if (cc != sizeof(response))
                            perror("sendto");
            }
    }
    

    Cours 8

    Options des sockets, inetd

  • Pour éviter trop de serveurs en attente, il existe un super-serveur internet pour chaque site: inetd. Il lit la table /etc/services et lance le serveur correspondant.
    echo            7/tcp
    echo            7/udp
    daytime         13/tcp
    daytime         13/udp
    ftp             21/tcp
    telnet          23/tcp
    smtp            25/tcp          mail
    time            37/tcp          timserver
    time            37/udp          timserver
    finger          79/tcp
    login           513/tcp
    who             513/udp         whod
    dictionary      10300/tcp       webster
    
  • on peut mettre des options sur un socket avec setsockopt() et getsockopt(). Exemples: SO_KEEPALIVE, SO_LINGER.
  • htonl, htons, ntohl, ntohl convertissent les entiers cours ou longs entre les machines hotes et la convention réseau (grand endian), puisqu'il faut bien un standard de communication entre des machines d'endianité différente.

  • Cours 8

    Serveurs de noms

  • le réseau est organisé en domaines et sous-domaines. Exemple: pauillac.inria.fr. Il y a le domaine fr, puis le sous-domaine inria, etc.
  • pour retrouver l'adresse IP, on peut la prendre dans /etc/hosts ou par gethostbyname(). Mais si l'adresse est très lointaine on la retrouve par un serveur de noms (DNS ou Domain Name Server et BIND).
  • nslookup est une commande qui permet d'avoir des informations en intéractif. Par exemple, les serveurs connaissant l'INRIA.
    % nslookup
    Default Server:  concorde.inria.fr
    Address:  192.93.2.39
    
    > set type=any
    > inria.fr
    Server:  concorde.inria.fr
    Address:  192.93.2.39
    
    inria.fr        origin = ns1.nic.fr
            mail addr = hostmaster.inria.fr
            serial=96031908, refresh=21600, retry=3600, expire=3600000, min=172800
    inria.fr        nameserver = ns1.nic.fr
    inria.fr        nameserver = ns2.nic.fr
    inria.fr        nameserver = mirsa.inria.fr
    inria.fr        nameserver = imag.imag.fr
    inria.fr        nameserver = ns.eu.net
    inria.fr        nameserver = princeton.edu
    

  • Cours 8

    Routage

  • il y a des passerelles sur le réseau. Il faut trouver les chemins entre 2 adresses IP. Le problème revient à trouver un arbre de recouvrement sur le graphe de l'internet.
  • la commande traceroute donne le chemin emprunté. Exemple:
    % /usr/sbin/traceroute poly.polytechnique.fr
    traceroute to poly.polytechnique.fr (129.104.252.1), 30 hops max, 40 byte packets
     1  rocq-gwf.inria.fr (128.93.1.100)  4 ms  3 ms  4 ms
     2  rocq-gwr.inria.fr (192.93.1.92)  3 ms  4 ms  5 ms
     3  inria-rocquencourt.rerif.ft.net (192.93.122.1)  4 ms  4 ms  4 ms
     4  boulogne.rerif.ft.net (193.48.55.57)  6 ms  7 ms  7 ms
     5  stlamb12.rerif.ft.net (193.48.53.41)  10 ms  10 ms  12 ms
     6  stlamb1.rerif.ft.net (193.48.53.161)  11 ms  11 ms  14 ms
     7  stlamb3.rerif.ft.net (193.48.53.11)  11 ms  11 ms  11 ms
     8  massy1.rerif.ft.net (193.48.53.18)  11 ms  12 ms  11 ms
     9  ep-palaiseau.rerif.ft.net (193.48.54.26)  17 ms  15 ms  15 ms
    10  192.70.27.13 (192.70.27.13)  17 ms  14 ms  16 ms
    11  192.70.27.13 (192.70.27.13)  17 ms !H  17 ms !H *
    
  • une bonne référence!
    TIT     .Le routage dans l'Internet
    SLA     .Christian Huitema; Eyrolles, 1995
    

  • Cours 8

    Problèmes distribués typiques

  • Le problème des généraux byzantins

    On a des processus fiables et non fiables. On veut que:

    1. tous les fiables doivent produire le même résultat
    2. si tous les fiables commence avec la même valeur, alors cette valeur doit être le résultat de tous les fiables.
  • TIT     .Parallel program design : a foundation
    SLA     .K. Mani Chandy, Jayadev Misra. - Reading, MA US ; Don Mills,
            .Ontario ; Wokingham, England : Addison-Wesley, 1988. -
            .XXVIII-516 p. ; 24 cm.
    
    TIT     .Handbook of Theoretical Computer Science. MIT Press, ed.
            van Leewen, 1990.
            Chapter 18, Leslie Lamport, Nancy Lynch, pp 1159-1199.
    

    Cours 8

    Consensus en milieu distribué non fiable

    Généraux byzantins

    Enoncé (bis): Quelques généraux félons se sont mêlés à l'armée byzantine qui doit néanmoins avancer en ordre. Il faut donc que:

    1. tous les généraux fiables prennent la même décision,
    2. si le général en chef n'est pas un traitre, tous les généraux fiables obéissent à l'ordre du général en chef.
    Remarque: cet énoncé se ramène à celui du dernier cours. Exercice: montrez le.
    Leslie Lamport, Rob Shostak, Marshall Pease
    The Byzantine Generals Problem
    ACM/TOPLASS, Vol 4, N 3, Jul 1982
    
    Danny Dolev 
    The  Byzantine Generals Strike Again
    The Journal of Algorithms,1982
    
    On peut montrer qu'il n'existe de solution que si le nombre de généraux n est plus grand que 3 m + 1 où m est le nombre de traîtres.

    Cours 8

    Généraux byzantins

    Solution (Lamport, Shostak, Pease -- Dolev)

    OM(m) si m = 0

    1. Le chef 0 envoie son ordre à tous ses adjoints,
    2. Tous les généraux obéissent à l'ordre du chef, ou battent en retraite si rien reçu.
    OM(m) si m > 0
    1. Le chef 0 envoie son ordre à tous ses adjoints,
    2. Pour tout i, soit v_i la valeur reçue par le général i. Si rien reçu, v_i = RETRAITE. Le général i fait comme le général en chef en lancant la valeur recue avec OM(m-1) pour les n-2 autres lieutenants,
    3. Pour tout i et j différent de i, soit v_j la valeur reçue par le lieutenant i du lieutenant j à la 2ème étape (RETRAITE si rien reçu). Le général i fait un vote à majorité sur l'ensemble {v_1, v_2, ... v_n-1}.

    Exercice: Faire l'exemple avec 4 généraux (les 2 cas où le chef ou un lieutenant est traître). Prouver la correction de l'algorithme.


    Cours 8

    Exercices en TD et à la maison

  • Forts: remote shell
  • Débutants: continuer à faire un serveur, avec un consensus distribué ou une élection. Faire un serveur d'oral.