Alma Mater Studiorum - Universita' di Bologna Sede di Cesena II Facolta' di Ingegneria Reti di Calcolatori Esercitazione 5 Implementazione del TFTP tramite RPC Copyright © 2006-2013 by D. Romagnoli & C. Salati 1 Servizio di file transfer TFTP in C con RPC (su TCP) • Questa esercitazione richiede l’implementazione di un servizio di file transfer. • Il protocollo applicativo utilizzato per fornire questo servizio e’ derivato dal protocollo TFTP. • In questo caso pero’ il protocollo applicativo e’ realizzato appoggiandosi all’ASE RPC del Layer di Applicazione. ASE RPC = Sun RPC version 2. N.B.: l’ASE RPC si appoggia a sua volta al Layer di Presentazione (XDR) e si interfaccia, in modo nascosto all’utente, con il Layer di Trasporto. • Come Servizio di Trasporto si usera’ quello fornito dal protocollo TCP, e quindi si ignoreranno i problemi legati all’affidabilita’ delle comunicazioni. • Assumeremo quindi che le nostre RPC realizzino la semantica exactly-once senza alcun impegno aggiuntivo da parte del SW utente per aumentare la robustezza delle comunicazioni. 2 Servizio di file transfer TFTP in C con RPC (su TCP) • Il linguaggio di programmazione da utilizzare e’ il C. • Il ruolo di chiamante e’ svolto dal client di file transfer, quello di chiamato dal server (sequenziale) di file transfer, indipendentemente dalla direzione di trasferimento dei file (che puo’ cambiare all’interno di una stessa sessione). • Bisognera’ quindi definire delle procedure remote che realizzino il trasferimento secondo le modalita’ previste da TFTP. • N.B.: utilizzando l’RPC in forma confermata/sincrona la finestra di trasmissione e’ automaticamente di dimensione 1. • L’invocazione/ritorno delle singole procedure e i relativi parametri di ingresso e uscita sostituiranno l’invio esplicito di PDU. • Ovviamente procedure e relativi parametri devono essere definiti in modo opportuno. • Per la definizione del protocollo TFTP vedi esercitazioni 3 e 4. • N.B.: il protocollo TFTP su RPC non e’ definito completamente solo dalla definizione dell’interfaccia RPC: bisogna anche definire il comportamento di ciascuna procedura e quale e’ la sequenza legittima 3 delle procedure che il lato client puo’/deve chiamare sul lato server. PDU del protocollo TFTP: mappa di byte read-request (RRQ) write-request (WRQ) data (DAT) Acknowledgment (ACK) error (ERR) opcode string EOS string EOS 01 filename 0 mode 0 2 bytes n bytes 1 byte n bytes 1 byte opcode string EOS string EOS 02 filename 0 mode 0 2 bytes n bytes 1 byte n bytes 1 byte opcode 03 block# data 2 bytes 2 bytes n bytes, 0 <= n <= 512 opcode 04 block# 2 bytes 2 bytes opcode string EOS 05 errcode errstring 0 2 bytes 2 bytes n bytes 1 byte 4 Come non definire il protocollo • In XDR avevamo definito il PDU come union TftpMsg switch (OpType opType) { case RRQ: RrqAndWrqMsg rrqMsg; case WRQ: RrqAndWrqMsg wrqMsg; case DAT: DatMsg datMsg; case ACK: AckMsg ackMsg; case ERR: ErrMsg errMsg; }; /* tipo XDR per generico PDU tra client e server */ • Per cui potremmo definire una interfaccia (programma in terminologia RPC Sun) contenente una unica procedura che ha un parametro di tipo TftpMsg sia in ingresso che in uscita. program TFTP_PROG { version TFTP_VERS { TftpMsg TFTP_PROC(TftpMsg) } = 1; } = 0x20000001; = 1; Ma questo (limitarsi a utilizzare RPC solo per fare viaggiare dei PDU che sarebbero interpretati come nell’esercitazione 4!) e’ proprio quello che non bisogna fare! 5 Come definire il protocollo • Bisogna sfruttare appieno le possibilita’ offerte da RPC. • Che cosa si vuole che una procedura faccia non lo dice tanto un parametro di ingresso (o una sua parte) quanto il nome della procedura. • Tante operazioni diverse, tante procedure diverse, ciascuna con parametri specifici, adatti ad essa. • Decidere quante e quali procedure definire e’ parte essenziale dell’esercitazione. • Esempio: • Ci deve essere una procedura specifica per inviare un blocco di byte durante il trasferimento del file da client a server. • Come parametro di ingresso questa procedura prevedera’ di avere un blocco di byte (ed eventualmente un sequence number). • Come parametro di ritorno questa procedura prevedera’ di avere un valore che indica se il trasferimento/scrittura del blocco e’ avvenuto con successo (con eventualmente il sequence number) o con errore (con il motivo dell’errore). 6 Come definire le procedure • Ci sono due operazioni di trasferimento file (a livello di applicazione utente): • “get” per l’operazione di download di un file da server a client. • “put” per l’operazione di upload di un file da client a server. Per ciascuna di queste operazioni dovra’ essere definita una procedura RPC capace di darle inizio (e una procedura locale nella protocol entity TFTP client per eseguire tutta l’operazione). • Essendoci due direzioni di trasferimento ci devono essere due funzioni RPC: • una per trasferire dei byte da client a server, • una per trasferire dei byte da server a client. N.B.: anche l’operazione di trasferimento di byte da server a client deve essere attivata dal client! • Il client dovra’ avere a disposizione anche una funzione che gli consenta di segnalare un errore verso il server. • Il server dovra’ utilizzare il valore di ritorno anche per informare il client di eventuali situazioni di errore. 7 Servizio applicativo TFTP vs. protocol entity TFTP • Come nel caso dell’esercitazione 3, quello che si chiede di implementare e’ • sia il protocollo applicativo (di Layer 7) TFTP, lati client e server, • che due moduli, client e server, di applicazioni utente TFTP. • I due diversi layer dovrebbero essere mantenuti il piu’ possibile separati. • In questo caso, pero’, le protocol entity TFTP dovranno essere implementate utilizzando il supporto RPC (avendo cioe’ definito tramite RPC le interazioni tra le due protocol entity TFTP). • La struttura delle due applicazioni client e server deve quindi essere: User Application RPC based TFTP Protocol Entity RPC Middleware Presentation Layer TSAP Transport Layer • I Layer di Trasporto e di Presentazione non e’ che non ci siano, ma sono nascosti alla protocol entity TFTP e all’applicazione utente dall’infrastruttura RPC. • La protocol entity TFTP sara’ composta: • Lato client da due procedure, get() e put(), che utilizzano le RPC del protocollo. • Lato server dalla implementazione delle RPC del protocollo. 8 Specifiche per il client 1. Le specifiche per l’applicazione utente lato client sono sostanzialmente le stesse utilizzate per le esercitazioni 3 e 4. 2. Ovviamente in questo caso, a differenza che nelle esercitazioni 3 e 4, il client recuperera’ dalla stringa di lancio solo l’indirizzo IP: La porta del servizio RPC-TFTP sara’ scoperta e collegata tramite chiamata a clnt_create(), e conseguente interazione con il Port Mapper locale alla macchina server. L’esecuzione dell’operazione clnt_create()marchera’ anche l’inizio della sessione di trasferimento file dal punto di vista dell’applicazione client. 3. Notare che, per come e’ stata definita l’applicazione client, questa mettera’ in esecuzione una unica operazione di file transfer per volta. 4. L’applicazione client gestisce una nozione di sessione legata alla connessione TCP con il server, ma questa nozione e’ invisibile per l’applicazione server che non ha visibilita’ dello stato del Layer di Trasporto. L’applicazione client termina, dal suo punto di vista, la sessione di trasferimento file tramite chiamata della funzione 9 clnt_destroy(). Specifiche per il server .1 1. Il lato server deve contenere la definizione delle procedure remote che implementano il protocollo TFTP. 2. Come gestire i problemi di concorrenza tra eventuali richieste da parte di diversi client? N.B.: lo skeleton server RPC garantisce la sequenzialita’ di esecuzione delle singole RPC, ma chiamate provenienti da diversi client possono inframmischiarsi. • Sostanzialmente si deve realizzare un server sequenziale. • Per fare questo si devono rifiutare nuove richieste di trasferimento file una volta che ne sia in corso uno. Il server deve pero’ gestire anche una nozione di sessione analoga a quella definita nell’esercitazione 4. • Una sessione di file transfer viene considerata chiusa (in modo normale) dal server se, esaurito il trasferimento di un file, passa un tempo “abbastanza” lungo (dimensione definita staticamente) senza che il client richieda il trasferimento di un altro file. Il server dovra’ quindi mantenere annotato quando e’ 10 terminato l’ultimo trasferimento file. Specifiche per il server .2 1. Il protocollo TFTP deve essere esteso per gestire esplicitamente la nozione di sessione tra client e server: • Ogni richiesta di operazione del client deve trasportare un ID di sessione che consenta al server di verificare se quella richiesta e’ relativa alla sessione correntemente attiva. • Il client marchera’ la prima richiesta di una sessione (una richiesta di inizio upload o download) con ID=0. • Quando il server accetta di iniziare una sessione con un client gli assegna un ID univoco di sessione 0. Questo ID e’ ritornato al client nella risposta di tutte le operazioni, e non solo della prima. • Tutte le successive richieste del client relative alla stessa sessione devono essere marcate con quell’ID. • Il server rifiutera’ l’esecuzione di operazioni marcate con un ID diverso da quello della sessione corrente. 2. Su indicazione di errore da parte del client il server interrompe il trasferimento in corso e la sessione e si mette in attesa di una nuova 11 richiesta di inizio trasferimento/sessione da parte di un client. Specifiche per il server .3 1. Anche nel caso sia lui a generare una risposta di errore per il client, il server dovra’ considerare terminato il trasferimento in corso e la sessione corrente. 2. Il server deve considerare abortito un trasferimento file anche se per troppo tempo (dimensione definita staticamente) non ha ricevuto dal client attivo in quel momento chiamate di procedure che lo facessero progredire nel trasferimento. Nel caso di abort di un trasferimento il server considera abortita anche la sessione di cui esso fa parte. Il server dovra’ quindi mantenere annotato quando e’ avvenuta l’ultima operazione del trasferimento in corso. 3. Il controllo di esaurimento timeout deve essere effettuato all’inizio dell’esecuzione di ogni operazione e determina quello che e’ lo stato corrente del server. 4. All’interno di una sessione un client non puo’ chiedere l’inizio di un trasferimento se non ha gia’ terminato il trasferimento precedente: se lo fa, cio’ e’ considerato un errore di protocollo e porta all’abort 12 del trasferimento gia’ in corso e alla terminazione della sessione. Specifiche per il server .4 • Quando il server riceve una chiamata di attivazione di un trasferimento deve quindi verificare il suo stato e rispondere di conseguenza: 1. Se in quel momento non e’ attiva nessuna sessione e questa e’ la prima richiesta di una nuova sessione (il suo ID e’ =0), accetta la richiesta, attiva una nuova sessione e da’ il via al trasferimento. 2. Se in quel momento non e’ attiva nessuna sessione e questa non e’ la prima richiesta di una nuova sessione (il suo ID e’ 0), rifiuta la richiesta. 3. Se in quel momento e’ attiva una sessione e questa e’ la prima richiesta di una nuova sessione (il suo ID e’ =0), rifiuta la richiesta. 4. Se in quel momento e’ attiva una sessione e questa richiesta e’ relativa ad una sessione diversa da quella corrente, rifiuta la richiesta. 5. Se in quel momento e’ attiva una sessione, non e’ attivo nessun trasferimento e questa richiesta e’ relativa alla sessione corrente, accetta la richiesta e da’ il via al trasferimento. 13 Specifiche per il server .5 • Quando il server riceve una chiamata di attivazione di un trasferimento deve quindi verificare il suo stato e rispondere di conseguenza: 1. Se in quel momento e’ attivo un trasferimento e questa richiesta e’ relativa alla sessione corrente, questo e’ un errore di protocollo. 2. Se in quel momento e’ attivo un trasferimento, e questa richiesta non e’ la prima richiesta di una nuova sessione (il suo ID e’ 0) ed e’ relativa ad una sessione diversa da quella corrente, rifiuta la richiesta. 3. Se in quel momento e’ attivo un trasferimento e questa e’ la prima richiesta di una nuova sessione (il suo ID e’ =0) • Se e’ passato troppo tempo dall’ultima operazione relativa al trasferimento in corso, lo abortisce e abortisce la sessione corrente e accetta la richiesta: da’ il via ad una nuova sessione e ad un nuovo trasferimento. • Se non e’ passato abbastanza tempo dall’ultima operazione relativa al trasferimento in corso, rifiuta la nuova richiesta di 14 inizio trasferimento. Specifiche per il server .6 • Quando e’ in corso un trasferimento file il server deve verificare l’eventuale spirare del timeout di trasferimento per ogni operazione richiesta dal client correntemente attivo (cioe’ le cui richieste sono marcate con un ID uguale a quello della sessione in corso). • Quando e’ in corso un trasferimento file qualunque richiesta da parte di un cliente diverso da quello correntemente attivo che non sia di inizio di un trasferimento costituisce una violazione di protocollo. • E’ ovvio che l’unico tipo di richiesta che puo’ essere legittimamente marcato con ID=0 e’ quello di inizio di trasferimento file (upload o download). • Il server si puo’ trovare in 3 stati: 1. Nessuna sessione attiva 2. Sessione attiva, nessun trasferimento in corso 3. Sessione attiva, trasferimento in corso • Il server dovrebbe evitare di compiere sul file in corso di trasferimento operazioni inutili e ridondanti, come apertura, posizionamento e chiusura per ogni blocco trasferito. 15 Ci si deve basare sul fatto che si opera sequenzialmente. Acquisizione dell’istante corrente • Si e’ visto che molte operazioni del server dipendono dall’intervallo di tempo che intercorre tra la ricezione di una chiamata di RPC e quella successiva. • Per misurare questo intervallo di tempo si puo’ utilizzare la system call gettimeofday() (vedi http://linux.die.net/man/2/gettimeofday): #include <sys/time.h> int gettimeofday(struct timeval *tv, struct timezone *tz); The function gettimeofday() can get the time as well as a timezone. The tv argument is a struct timeval (as specified in sys/time.h): struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; and gives the number of seconds and microseconds since the Epoch (1970-0101 00:00:00 +0000 (UTC)). The tz argument is a struct timezone: struct timezone { int tz_minuteswest; int tz_dsttime; }; If either tv or tz is NULL, the corresponding structure is not returned. The use of the timezone structure is obsolete: the tz argument should be specified as NULL. 16 Diagnostici e tracciamenti • E’ opportuno prevedere l’abilitazione/disabilitazione di diversi livelli di tracciamento (e.g. procedura remota attivata, parametri di ingresso e uscita): cio’, lato client, puo’ essere fatto facilmente passando un opportuno parametro nella stringa di lancio. • La cosa si applica pero’ sia lato client che lato server. • Lato server la cosa e’ particolarmente significativa per poter controllare la correttezza della gestione della concorrenza delle interazioni con i client. Dovrebbero essere verificate tutte le condizioni di terminazione di un trasferimento e di una sessione, sia di successo che di errore. Dovrebbe essere verificata la gestione, da parte del server, di richieste concorrenti di diversi client. • Lato server l’introduzione di parametri nella stringa di attivazione puo’ sembrare non immediata: In realta’ si tratta solo di introdurre qualche semplice modifica nel 17 testo dello skeleton generato da rpcgen. Generazione del codice eseguibile .1 • Per compilare la sintassi astratta dare il comando (si assume che la sintassi astratta sia contenuta nel file tftp.x): rpcgen –C tftp.x che produce 4 file: • tftp.h che deve essere incluso nei file sorgente del vostro programma, sia lato client che lato server. • tftp_xdr.c che deve essere compilato e linkato sia al programma client che a quello server. • tftp_clnt.c che deve essere compilato e linkato al programma client. • tftp_svc.c che deve essere compilato e linkato al programma server. 18 Generazione del codice eseguibile .2 • Per la compilazione del client: gcc –g client.c tftp_xdr.c tftp_clnt.c –o client • Per la compilazione del server: gcc –g server.c tftp_xdr.c tftp_svc.c–o server • Si e’ fatta l’ipotesi che i file tftp.x, client.c e server.c siano tutti nella stessa directory. • L’output della compilazione/link saranno i 2 eseguibili client e server. 19