
Protocollo IP
Il protocollo IP è alla base della comunicazione attraverso la rete internet, non per niente IP sta per Internet Protocol; il suo ruolo è quello di permettere l'instradamento dei pacchetti attraverso la rete, affinché questi possano giungere al destinatario.
Elemento fondamentale è l'indirizzo IP (di cui abbiamo già discusso), per mezzo del quale si possono identificare mittente e destinatario in modo univoco all'interno della stessa rete; attenzione!
non a livello mondiale come avvine invece per l'indirizzo MAC. Nell'ambito di una rete privata, l'indirizzo IP può
essere scelto a piacere.
Intestazione IP
Come si è già capito, un'intestazione IP (IP Header) contiene l'indirizzo IP del mittente e quello del destinatario. Oltre a queste,
sono presenti altre informazioni, più o meno importanti; vediamone alcune:
- Version: la versione del protocollo IP in uso; useremo soltanto la versione 4.
- Header Length: lunghezza dell'intestazione in parole di 4 byte.
- Total Length: lunghezza totale del pacchetto IP (intestazione + dati).
- Identification: numero sequenziale usato per identificare univocamente un pacchetto IP durante una comunicazione.
- Time To Live (TTL): contiene il numero di passaggi (attraverso router) effettuabili prima che il pacchetto venga eliminato (destinazione irraggiungibile).
- Protocol: indica il tipo di protocollo di quarto livello contenuto nel campo dati.
- Checksum: campo per il controllo di integrità dell'intestazione; riguarda soltanto l'intestazione e non i dati.
Ci sono poi altri campi, ma non verranno usati.

Che in C diventa:
typedef struct { u8 b[4]; } IPAddr; typedef struct { u8 verlen; u8 typeOfService; u16 totalLength; u16 id; u16 fragmentInfo; u8 TTL; u8 protocol; u16 checksum; IPAddr sourceIP; IPAddr destIP; } IP_Header;
Ricezione
Alla ricezione di un pacchetto IP, le operazioni da eseguire sono piuttosto semplici, e sono implementate nel metodo processIP.
- Controllo del destinatario.
- Controllo degli errori.
- Passaggio al livello superiore.
Innanzi tutto bisogna verificare che il destinatario del pacchetto sia l'indirizzo IP
assegnato al server; questo controllo viene effettuato confrontando la variabile MyIP con
il campo Destination Address del pacchetto.
La variabile MyIp è definita nel file ip.c e viene inizializzata nel metodo encInit:
// in ip.c IPAddr MyIP; // in define.h #define MY_IP1 10 #define MY_IP2 0 #define MY_IP3 0 #define MY_IP4 7 // in enc28j60.c, encInit() MyIP.b[0] = MY_IP1; MyIP.b[1] = MY_IP2; MyIP.b[2] = MY_IP3; MyIP.b[3] = MY_IP4;
Il controllo di integrità dell'intestazione IP è costituito dalla verifica del campo checksum; tale operazione viene effettuata dal metodo DMAChecksum che vedremo tra poco.
Vediamo quindi il metodo processIP:
void processIP(){ IP_Header header; u16 chksum; u8 optlen; encGetArray((u8*)&header, sizeof(header)); if (!ipMatch(header.destIP,MyIP)) return; // controlla il checksum if (DMAChecksum(0, (header.verlen & 0x0F)*4, FALSE)) return; // scarta eventuali campi option optlen = (header.verlen & 0x0F)*4 - 20; if (optlen) encDiscard(optlen); // big endian --> little endian swapIPHeader(&header); switch (header.protocol){ case IPPROTO_ICMP: processICMP(header); break; /* case IPPROTO_UDP : break; case IPPROTO_TCP: break; */ default: break; } }
Il metodo ipMatch confronta due indirizzi IP:
u8 ipMatch(IPAddr ip1, IPAddr ip2){ return (ip1.b[0] == ip2.b[0] && ip1.b[1] == ip2.b[1] && ip1.b[2] == ip2.b[2] && ip1.b[3] == ip2.b[3]); }
Invio
Per inviare un pacchetto IP è necessario preparare un'intestazione MAC ed un'intestazione IP da scrivere nel buffer di trasmissione;
a queste seguirà il campo dati che conterrà un pacchetto di quarto livello. Di questo si occupa il metodo IPPutHeader,
al quale vengono passati alcuni parametri:
- target: indirizzo IP del destinatario;
- protocol: protocollo di quarto livello contenuto nel campo dati;
- data: puntatore alla locazione di memoria che contiene i dati da inserire nel pacchetto;
- datalen: la dimensione dei dati puntati da data;
- totalLength: dimensione totale del pacchetto, esclusa l'intestazione (non sempre coincideràcon dataLen)
Ecco il metodo completo:
void IPPutHeader(IPAddr target, u8 protocol, u8* data, u16 datalen, u16 totalLength){ IP_Header header; u16 tmp; header.verlen = 0x45; header.typeOfService = 0x00; header.totalLength = 20+totalLength; header.id = ++id; header.fragmentInfo = 0x00; header.TTL = 128; header.protocol = protocol; header.checksum = 0x00;; header.sourceIP = MyIP; header.destIP = target; swapIPHeader(&header); MACPutHeader(remoteAddr, TYPE_IP); encPutArray((u8*)&header, sizeof(header)); encPutArray(data, datalen); putChecksum(IP_Offset+10, DMAChecksum(IP_Offset, sizeof(header), TRUE)); }
Innanzitutto viene preparata l'intestazione nella variabile header utilizzando alcuni parametri ed altre costanti; poi viene chiamata
la funzione MACPutHeader che oltre a scrivere nel buffer l'intesazione MAC, prepara anche l'operazione di scrittura e invio. Di seguito
vengono inviati al controller l'intestazione IP ed i dati, infine viene calcolato il checksum del pacchetto e anch'esso scritto nel buffer (alla locazione IPOffset+10).
Per trovare il checksum, il relativo campo nel pacchetto deve essere settato a zero, dopodiché attraverso il metodo DMAChecksum viene calcolato
direttamente dal buffer dell'ENC28J60.
