IPv4
Protocolul IP este utilizat pentru a permite dispozitivelor conectate în rețele diferite să schimbe informații prin intermediul unui dispozitiv intermediar numit router. Header-ul unui pachet (packet) IP este următorul:
+----+---------------+---------------+---------------+---------------+
|Word| 1 | 2 | 3 | 4 |
+----+---------------+---------------+---------------+---------------+
|Byte|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0|Version| IHL |Type of Service| Total Length |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 4| Identification |Flags| Fragment Offset |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 8| Time to Live | Protocol | Header Checksum |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12| Source Address |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 16| Destination Address |
+----+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
De notat faptul că astăzi nu vom atinge decât câmpurile Time to Live,
checksum și adresa IP destinație. Câmpul Time to Live este un număr
decrementat de fiecare router pentru a evita buclele. checksum este câmpul
folosit pentru a verifică integritatea header-ului IP. Destionation Address
este adresa IP a destinației.
Următoarea strucura poate fi folosită pentru a reprezenta un pachet IPV4.
struct iphdr {
// The following syntax means that version has 4 bits and ihl 4 bits.
uint8_t ihl:4, version:4; // don't care
uint8_t tos; // don't care
uint16_t tot_len; // don't care
uint16_t id; // don't care
uint16_t frag_off; // don't care
uint8_t ttl; // Time to Live -> to avoid loops, we will decrement
uint8_t protocol; // don't care
uint16_t check; // checksum -> Since we modify TTL,
// we need to recompute the checksum
uint32_t saddr; // don't care
uint32_t daddr; // the destination of the packet
};
Observăm că o adresă IP precum 10.30.4.2 poate fi reprezentată în memorie ca
un integer pe 32 de biți, uint32_t.
Câmpul checksum este complementul față de 1 al sumei tuturor cuvintelor de 16
biți din header. Totuși, cum noi modificăm doar câmpul TTL și pentru că checksum
este o sumă, există o metodă mai rapidă de a calcula noul checksum folosind
formulă:
Fie:
HC - vechiul checksum din header
C - complementul față de 1 al sumei campurilor din header
HC' - noul checksum
m - vechea valoare a câmpului de 16 biți (TTL în cazul nostru)
m' - moua valoare a câmpului de 16 biți (TTl --)
HC' = ~(C + (-m) + m') = HC + (m - m') = HC + m + ~m'
complement față
de 1 al noului
mesaj
Cum header-ul IP o să aibă mereu un câmp diferit de zero, iar checksumul este complementul sumei, valoarea checksumului nu o să ajungă niciodată -0 (0xFFFF). Astfel, formula precedentă poate ajunge să returneze -0 când ar trebui sa fie +0 (0x0000). Pentru a rezolva această problemă vom face următoarea modificare:
HC' = ~(C + (-m) + m')
= ~(~HC + ~m + m')
Avem astfel formula finală:
new_checksum = ~(~old_check + ~((uint16_t)old_ttl) + (uint16_t)ip_hdr->ttl) - 1;
Acel -1 de la final apare pentru a evita translația din network order în host order pentru valorile de ttl pe 16 biți.