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.






