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