1 // vim: set et sw=4 sts=4 :
7 /** protocolos soportados */
8 enum { ICMP = 0x01, UDP = 0x11 };
10 byte ip_addr_local[IP_ADDR_SIZE];
12 byte ip_addr_remote[IP_ADDR_SIZE];
18 /* para calcular checksum */
19 static uint16 checksum;
21 /* agrega un word al checksum calculado */
22 static void sum(uint16 w)
25 if (checksum < w) /* corrección de carry (hubo OV) */
29 bool ip_read_packet_header()
31 /* variables utilitarias (iterar y/o buffer) */
34 netdev_read_start(IP_HEADER_SIZE);
35 /* reseteamos checksum */
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 */
42 /* tamaño de cabecera */
43 if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
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) */
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();
68 /* seguimos calculando checksum */
70 /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta
72 h = netdev_read_byte();
73 /* protocolo (sólo soportamos UDP e ICMP) */
74 l = netdev_read_byte();
86 /* sigo calculando checksum */
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)
93 ip_addr_remote[l] = netdev_read_byte();
95 sum(WORD(h, ip_addr_remote[l]));
97 h = ip_addr_remote[l];
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)
104 if (ip_addr_local[l] != netdev_read_byte())
107 sum(WORD(h, ip_addr_local[l]));
109 h = ip_addr_local[l];
111 /* verificamos checksum */
112 if ((uint16)~checksum)
118 void ip_write_packet_header()
120 /* variables utilitarias (iterar y/o buffer) */
122 /* identificador del paquete IP (incrementa con cada paquete) */
124 /* reseteamos checksum */
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
153 for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */
155 sum(WORD(h, ip_addr_local[l]));
157 h = ip_addr_local[l];
158 for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */
160 sum(WORD(h, ip_addr_remote[l]));
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]);