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