bool ip_proto_icmp;
+uint16 checksum;
+
+static void sum(uint16 w)
+{
+ checksum += w;
+ if (checksum < w) /* corrección de carry (hubo OV) */
+ ++checksum;
+}
+
bool ip_read_packet_header()
{
- /* variable para utilitaria (iterar y/o buffer) */
- byte c;
+ /* variables utilitarias (iterar y/o buffer) */
+ byte h, l;
+ /* reseteamos checksum */
+ checksum = 0;
/* versión y tamaño de cabecera vienen en el 1er byte */
- c = net_getb();
+ h = net_getb();
/* sólo soportamos versión 4 */
- if ((c >> 4) != 4)
+ if ((h >> 4) != 4)
return false; /* drop */
/* tamaño de cabecera */
- if ((c & 0x0F) != 5) /* no aceptamos opciones raras =) */
+ if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
return false; /* drop */
- /* ignoramos el TOS */
- net_getb();
+ /* ignoramos el TOS y vamos calculando checksum */
+ sum(WORD(h, net_getb()));
/* obtenemos tamaño del paquete */
- if (net_getb()) /* tiene más de 255 bytes (no lo soportamos) */
+ if (h = net_getb()) /* tiene más de 255 bytes (no lo soportamos) */
return false; /* drop */
ip_packet_len = net_getb(); /* hasta 255 bytes tienen los nuestros */
- /* ignoramos identificación */
- net_getb(); net_getb(); /* 2 bytes */
+ /* vamos calculando checksum */
+ sum(WORD(h, ip_packet_len));
+ /* ignoramos identificación (2 bytes) y vamos calculando checksum */
+ sum(net_getw());
/* si tiene prendido el bit de MF (More Fragments, bit 5 del byte, bit 2
* de los flags de la cabecera) o si tiene un offset de fragmento (bits
* del 4 al 0 y todos los bits del byte siguiente), dropeamos (no
* soportamos fragmentación) */
- if (net_getw() & 0x3FFF)
+ h = net_getb();
+ l = net_getb();
+ if ((h & 0x3F) || l)
return false; /* drop */
+ /* seguimos calculando checksum */
+ sum(WORD(h, l));
/* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta
* acá el pobre =) */
- net_getb();
+ h = net_getb();
/* protocolo (sólo soportamos UDP e ICMP) */
- switch (net_getb())
+ l = net_getb();
+ switch (l)
{
case ICMP:
ip_proto_icmp = true;
default:
return false; /* drop */
}
- /* checksum */
- net_getb(); net_getb(); /* TODO: verificar checksum */
- /* obtenemos IP de origen */
- for (c = 0; c < IP_ADDR_SIZE; ++c)
- ip_addr_remote[c] = net_getb();
- /* vemos si el paquete es para nosotros (ningún soportar broadcast =) */
- for (c = 0; c < IP_ADDR_SIZE; ++c)
- if (ip_addr_local[c] != net_getb())
- return false; /* no es para nosotros */
+ /* sigo calculando checksum */
+ sum(WORD(h, l));
+ /* obtenemos checksum y seguimos el cálculo */
+ sum(net_getw());
+ /* obtenemos IP de origen (mientras seguimos calculando el checksum) */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ {
+ ip_addr_remote[l] = net_getb();
+ if (l % 2)
+ sum(WORD(h, ip_addr_remote[l]));
+ else
+ h = ip_addr_remote[l];
+ }
+ /* vemos si el paquete es para nosotros (ningún soportar broadcast =)
+ * (mientras seguimos calculando el checksum) */
+ for (l = 0; l < IP_ADDR_SIZE; ++l)
+ {
+ if (ip_addr_local[l] != net_getb())
+ return false; /* drop (no es para nosotros) */
+ if (l % 2)
+ sum(WORD(h, ip_addr_local[l]));
+ else
+ h = ip_addr_local[l];
+ }
+ /* verificamos checksum */
+ if (~checksum)
+ return false; /* checksum malo, drop */
return true;
}
void ip_write_packet_header()
{
- /* variable para iterar */
- byte i;
+ /* variables utilitarias (iterar y/o buffer) */
+ byte h, l;
/* identificador del paquete IP (incrementa con cada paquete) */
- static uint16 id = 0;
+ static uint16 id;
+ /* reseteamos checksum */
+ checksum = 0;
/* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
- net_putb(0x45);
- /* TOS */
- if (ip_proto_icmp)
- net_putb(0xc0); /* Precedence: Internetwork Control */
- else
- net_putb(0x00); /* Precedence: Normal */
- net_getb();
+ net_putb(h = 0x45);
+ /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */
+ l = ip_proto_icmp ? 0xc0 : 0x00;
+ net_putb(l);
+ sum(WORD(h, l)); /* actualizamos checksum */
/* escribimos tamaño del paquete */
- net_putb(0x00); /* nunca vamos a mandar algo de más de 255 bytes */
+ net_putb(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */
net_putb(ip_packet_len);
+ sum(WORD(h, ip_packet_len)); /* actualizamos checksum */
/* identificación (sirve para reensamblar paquetes) */
net_putw(id);
+ sum(id); /* actualizamos checksum */
/* pedimos que no se fragmente */
- net_putb(0x40); /* Don't Fragment (DF) = 1 */
- net_putb(0x00); /* offset de fragmento = 0 */
+ net_putb(h = 0x40); /* Don't Fragment (DF) = 1 */
+ net_putb(l = 0x00); /* offset de fragmento = 0 */
+ sum(WORD(h, l)); /* actualizamos checksum */
/* TTL de 64 saltos porque está de moda */
- net_putb(0x40);
+ net_putb(h = 0x40);
/* protocolo (sólo soportamos UDP e ICMP) */
- if (ip_proto_icmp)
- net_putb(ICMP);
- else
- net_putb(UDP);
- /* checksum */
- net_putb(0x00); net_putb(0x00); /* TODO: calcular checksum */
+ l = ip_proto_icmp ? ICMP : UDP;
+ net_putb(l);
+ sum(WORD(h, l)); /* actualizamos checksum */
+ /* checksum: antes de poder escribir el checksum hay que terminar de
+ * calcularlo según las direcciones IP de origen y destino, así que eso
+ * hacemos */
+ for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */
+ if (l % 2)
+ sum(WORD(h, ip_addr_local[l]));
+ else
+ h = ip_addr_local[l];
+ for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */
+ if (l % 2)
+ sum(WORD(h, ip_addr_remote[l]));
+ else
+ h = ip_addr_remote[l];
+ /* ahora sí grabamos el checksum */
+ net_putw(~checksum);
+ /* ahora sí, continuamos poniendo las direcciones */
/* ponemos como dirección IP de origen la nuestra */
for (i = 0; i < IP_ADDR_SIZE; ++i)
net_putb(ip_addr_local[i]);