]> git.llucax.com Git - z.facultad/66.09/etherled.git/blob - src/ip.c
Bugfix en el cálculo de checksum.
[z.facultad/66.09/etherled.git] / src / ip.c
1 // vim: set et sw=4 sts=4 :     
2
3 #include "netdev.h"
4 #include "ip.h"
5
6 /** protocolos soportados */
7 enum { ICMP = 0x01, UDP = 0x11 };
8
9 byte ip_addr_local[IP_ADDR_SIZE];
10
11 byte ip_addr_remote[IP_ADDR_SIZE];
12
13 byte ip_packet_len;
14
15 ip_proto_t ip_proto;
16
17 /* para calcular checksum */
18 static uint16 checksum;
19
20 /* agrega un word al checksum calculado */
21 static void sum(uint16 w)
22 {
23     checksum += w;
24     if (checksum < w) /* corrección de carry (hubo OV) */
25         ++checksum;
26 }
27
28 bool ip_read_packet_header()
29 {
30     /* variables utilitarias (iterar y/o buffer) */
31     byte h, l;
32     /* reseteamos checksum */
33     checksum = 0;
34     /* versión y tamaño de cabecera vienen en el 1er byte */
35     h = netdev_recv_byte();
36     /* sólo soportamos versión 4 */
37     if ((h >> 4) != 4)
38         return false; /* drop */
39     /* tamaño de cabecera */
40     if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
41         return false; /* drop */
42     /* ignoramos el TOS y vamos calculando checksum */
43     sum(WORD(h, netdev_recv_byte()));
44     /* obtenemos tamaño del paquete */
45     if (h = netdev_recv_byte()) /* tiene más de 255 bytes (no lo soportamos) */
46         return false; /* drop */
47     ip_packet_len = netdev_recv_byte(); /* hasta 255 bytes tienen los nuestros */
48     /* vamos calculando checksum */
49     sum(WORD(h, ip_packet_len));
50     /* ignoramos identificación (2 bytes) y vamos calculando checksum */
51     sum(netdev_recv_word());
52     /* si tiene prendido el bit de MF (More Fragments, bit 5 del byte, bit 2
53      * de los flags de la cabecera) o si tiene un offset de fragmento (bits
54      * del 4 al 0 y todos los bits del byte siguiente), dropeamos (no
55      * soportamos fragmentación) */
56     h = netdev_recv_byte();
57     l = netdev_recv_byte();
58     if ((h & 0x3F) || l)
59         return false; /* drop */
60     /* seguimos calculando checksum */
61     sum(WORD(h, l));
62     /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta
63      * acá el pobre =) */
64     h = netdev_recv_byte();
65     /* protocolo (sólo soportamos UDP e ICMP) */
66     l = netdev_recv_byte();
67     switch (l)
68     {
69         case ICMP:
70             ip_proto = IP_ICMP;
71             break;
72         case UDP:
73             ip_proto = IP_UDP;
74             break;
75         default:
76             return false; /* drop */
77     }
78     /* sigo calculando checksum */
79     sum(WORD(h, l));
80     /* obtenemos checksum y seguimos el cálculo */
81     sum(netdev_recv_word());
82     /* obtenemos IP de origen (mientras seguimos calculando el checksum) */
83     for (l = 0; l < IP_ADDR_SIZE; ++l)
84     {
85         ip_addr_remote[l] = netdev_recv_byte();
86         if (l % 2)
87             sum(WORD(h, ip_addr_remote[l]));
88         else
89             h = ip_addr_remote[l];
90     }
91     /* vemos si el paquete es para nosotros (ningún soportar broadcast =)
92      * (mientras seguimos calculando el checksum) */
93     // TODO si soportamos DHCP hay que aceptar broadcasts!
94     for (l = 0; l < IP_ADDR_SIZE; ++l)
95     {
96         if (ip_addr_local[l] != netdev_recv_byte()) 
97             return false; /* drop (no es para nosotros) */
98         if (l % 2)
99             sum(WORD(h, ip_addr_local[l]));
100         else
101             h = ip_addr_local[l];
102     }
103     /* verificamos checksum */
104     if ((uint16)~checksum)
105         return false; /* checksum malo, drop */
106     return true;
107 }
108
109 void ip_write_packet_header()
110 {
111     /* variables utilitarias (iterar y/o buffer) */
112     byte h, l;
113     /* identificador del paquete IP (incrementa con cada paquete) */
114     static uint16 id;
115     /* reseteamos checksum */
116     checksum = 0;
117     /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
118     netdev_send_byte(h = 0x45);
119     /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */
120     l = (ip_proto == IP_ICMP) ? 0xc0 : 0x00;
121     netdev_send_byte(l);
122     sum(WORD(h, l)); /* actualizamos checksum */
123     /* escribimos tamaño del paquete */
124     netdev_send_byte(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */
125     netdev_send_byte(ip_packet_len);
126     sum(WORD(h, ip_packet_len)); /* actualizamos checksum */
127     /* identificación (sirve para reensamblar paquetes) */
128     netdev_send_word(id);
129     sum(id); /* actualizamos checksum */
130     /* pedimos que no se fragmente */
131     netdev_send_byte(h = 0x40); /* Don't Fragment (DF) = 1 */
132     netdev_send_byte(l = 0x00); /* offset de fragmento = 0 */
133     sum(WORD(h, l)); /* actualizamos checksum */
134     /* TTL de 64 saltos porque está de moda */
135     netdev_send_byte(h = 0x40);
136     /* protocolo (sólo soportamos UDP e ICMP) */
137     l = (ip_proto == IP_ICMP) ? ICMP : UDP;
138     netdev_send_byte(l);
139     sum(WORD(h, l)); /* actualizamos checksum */
140     /* checksum: antes de poder escribir el checksum hay que terminar de
141      * calcularlo según las direcciones IP de origen y destino, así que eso
142      * hacemos */
143     for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */
144         if (l % 2)
145             sum(WORD(h, ip_addr_local[l]));
146         else
147             h = ip_addr_local[l];
148     for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */
149         if (l % 2)
150             sum(WORD(h, ip_addr_remote[l]));
151         else
152             h = ip_addr_remote[l];
153     /* ahora sí grabamos el checksum */
154     netdev_send_word(~checksum);
155     /* ahora sí, continuamos poniendo las direcciones */
156     /* ponemos como dirección IP de origen la nuestra */
157     for (l = 0; l < IP_ADDR_SIZE; ++l)
158         netdev_send_byte(ip_addr_local[l]);
159     /* IP de destino, la remota */
160     for (l = 0; l < IP_ADDR_SIZE; ++l)
161         netdev_send_byte(ip_addr_remote[l]);
162 }
163