Ethernet 6/7

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.

IP Header

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.

Scarica subito una copia gratis
Tags:

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend