]> git.llucax.com Git - z.facultad/66.09/etherled.git/commitdiff
Stack de red funcionando. Es básicamente un echo server implementado sobre
authorLeandro Lucarella <llucax@gmail.com>
Mon, 28 Nov 2005 03:53:52 +0000 (03:53 +0000)
committerLeandro Lucarella <llucax@gmail.com>
Mon, 28 Nov 2005 03:53:52 +0000 (03:53 +0000)
nuestro stack de red utilizando raw sockets.

pruebas/c/net/Makefile [new file with mode: 0644]
pruebas/c/net/eth.c [new file with mode: 0644]
pruebas/c/net/eth.h [new file with mode: 0644]
pruebas/c/net/ip.c [new file with mode: 0644]
pruebas/c/net/ip.h [new file with mode: 0644]
pruebas/c/net/main.c [new file with mode: 0644]
pruebas/c/net/net.c [new file with mode: 0644]
pruebas/c/net/net.h [new file with mode: 0644]
pruebas/c/net/types.h [new file with mode: 0644]
pruebas/c/net/udp.c [new file with mode: 0644]
pruebas/c/net/udp.h [new file with mode: 0644]

diff --git a/pruebas/c/net/Makefile b/pruebas/c/net/Makefile
new file mode 100644 (file)
index 0000000..2658049
--- /dev/null
@@ -0,0 +1,5 @@
+#CFLAGS=-std=c99
+
+objs=main.o eth.o net.o ip.o udp.o
+
+main: $(objs)
diff --git a/pruebas/c/net/eth.c b/pruebas/c/net/eth.c
new file mode 100644 (file)
index 0000000..0f9e7d2
--- /dev/null
@@ -0,0 +1,52 @@
+#include "eth.h"
+
+/** tipos de paquetes transportados soportados */
+enum { IP = 0x0800, ARP = 0x0806 };
+
+byte eth_addr_local[ETH_ADDR_SIZE];
+
+byte eth_addr_remote[ETH_ADDR_SIZE];
+
+bool eth_frame_arp;
+
+bool eth_read_frame_header()
+{
+       /* variable para iterar */
+       byte i;
+       /* vemos si es para nosotros */
+       for (i = 0; i < ETH_ADDR_SIZE; ++i)
+               if (eth_addr_local[i] != net_getb())
+                       return false; /* no es para nosotros (drop) */
+       /* obtenemos MAC de origen */
+       for (i = 0; i < ETH_ADDR_SIZE; ++i)
+               eth_addr_remote[i] = net_getb();
+       /* obtenemos tipo de protocolo transportado por el frame, (sólo
+        * aceptamos IP y ARP) */
+       switch (net_getw())
+       {
+               case IP:
+                       eth_frame_arp = false;
+                       break;
+               case ARP:
+                       eth_frame_arp = true;
+                       break;
+               default:
+                       return false; /* drop */
+       }
+       return true;
+}
+
+void eth_write_frame_header()
+{
+       /* variable para iterar */
+       byte i;
+       /* mandamos como MAC de destino la remota */
+       for (i = 0; i < ETH_ADDR_SIZE; ++i)
+               net_putb(eth_addr_remote[i]);
+       /* y como fuente la nuestra */
+       for (i = 0; i < ETH_ADDR_SIZE; ++i)
+               net_putb(eth_addr_local[i]);
+       /* escribimos el tipo de paquete que transporta el frame */
+       net_putw(eth_frame_arp ? ARP : IP);
+}
+
diff --git a/pruebas/c/net/eth.h b/pruebas/c/net/eth.h
new file mode 100644 (file)
index 0000000..6ae4155
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _ETH_H_
+#define _ETH_H_
+
+#include "types.h"
+#include "net.h"
+
+/** @file
+ * Estructura de un frame ethernet.
+ *
+ * <pre>
+ * /--- 8 ----/--- 6 ----/--- 6 ----/- 2 --/- 46-1500 -/- 4 -/  bytes
+ * +----------+----------+----------+------+-----------+-----+
+ * | preamble | dst addr | src addr | type |   data    | crc |
+ * +----------+----------+----------+------+-----------+-----+
+ * </pre>
+ *
+ * type es 0x0800 para IP y 0x0806 para ARP, los únicos dos protocolos que
+ * soportamos sobre ethernet.
+ * El preamble y el crc los pone la placa de red automáticamente.
+ */
+
+/** Tamaño de dirección MAC (en bytes) */
+#define ETH_ADDR_SIZE 6
+
+/** Dirección MAC nuestra */
+extern byte eth_addr_local[ETH_ADDR_SIZE];
+
+/** Dirección MAC de destino */
+extern byte eth_addr_remote[ETH_ADDR_SIZE];
+
+/** Indica si el frame transporta ARP (si no transporta IP) */
+extern bool eth_frame_arp; /* FIXME: debería ser un bit manejado con ASM supongo */
+
+/**
+ * Lee la cabecera del frame ethernet.
+ *
+ * Deja en mac_addr_remote la MAC de origen y eth_frame_arp en 1 si es ARP.
+ * Si devuelve false (0) hay un error o es un frame no soportado, por lo que
+ * hay que descartarlo.
+ */
+bool eth_read_frame_header();
+
+/**
+ * Escribe la cabecera del frame ethernet.
+ *
+ * Pone como destino a mac_addr_remote, como origen a mac_addr_local y como
+ * tipo ARP si eth_frame_arp está en 1 (si no el tipo es IP).
+ */
+void eth_write_frame_header();
+
+#endif /* _ETH_H_ */
diff --git a/pruebas/c/net/ip.c b/pruebas/c/net/ip.c
new file mode 100644 (file)
index 0000000..9dcac1b
--- /dev/null
@@ -0,0 +1,159 @@
+#include "ip.h"
+
+/** protocolos soportados */
+enum { ICMP = 0x01, UDP = 0x11 };
+
+byte ip_addr_local[IP_ADDR_SIZE];
+
+byte ip_addr_remote[IP_ADDR_SIZE];
+
+byte ip_packet_len;
+
+bool ip_proto_icmp;
+
+/* para calcular checksum */
+uint16 checksum;
+
+/* agrega un word al checksum calculado */
+static void sum(uint16 w)
+{
+       checksum += w;
+       if (checksum < w) /* corrección de carry (hubo OV) */
+               ++checksum;
+}
+
+bool ip_read_packet_header()
+{
+       /* 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 */
+       h = net_getb();
+       /* sólo soportamos versión 4 */
+       if ((h >> 4) != 4)
+               return false; /* drop */
+       /* tamaño de cabecera */
+       if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */
+               return false; /* drop */
+       /* ignoramos el TOS y vamos calculando checksum */
+       sum(WORD(h, net_getb()));
+       /* obtenemos tamaño del paquete */
+       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 */
+       /* 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) */
+       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 =) */
+       h = net_getb();
+       /* protocolo (sólo soportamos UDP e ICMP) */
+       l = net_getb();
+       switch (l)
+       {
+               case ICMP:
+                       ip_proto_icmp = true;
+                       break;
+               case UDP:
+                       ip_proto_icmp = false;
+                       break;
+               default:
+                       return false; /* drop */
+       }
+       /* 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 ((uint16)~checksum)
+               return false; /* checksum malo, drop */
+       return true;
+}
+
+void ip_write_packet_header()
+{
+       /* variables utilitarias (iterar y/o buffer) */
+       byte h, l;
+       /* identificador del paquete IP (incrementa con cada paquete) */
+       static uint16 id;
+       /* reseteamos checksum */
+       checksum = 0;
+       /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
+       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(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(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(h = 0x40);
+       /* protocolo (sólo soportamos UDP e ICMP) */
+       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 (l = 0; l < IP_ADDR_SIZE; ++l)
+               net_putb(ip_addr_local[l]);
+       /* IP de destino, la remota */
+       for (l = 0; l < IP_ADDR_SIZE; ++l)
+               net_putb(ip_addr_remote[l]);
+}
+
diff --git a/pruebas/c/net/ip.h b/pruebas/c/net/ip.h
new file mode 100644 (file)
index 0000000..ec3a892
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _IP_H_
+#define _IP_H_
+
+#include "types.h"
+#include "net.h"
+
+/** @file
+ * Paquete IP.
+ *
+ *  0                   1                   2                   3   
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version|  IHL  |Type of Service|          Total Length         |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |         Identification        |Flags|      Fragment Offset    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Time to Live |    Protocol   |         Header Checksum       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                       Source Address                          |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                    Destination Address                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                    Options                    |    Padding    |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                             Data                              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 
+ * Nosotros sólo implementamos la versión 4, sin opciones, con tamaño de paquete
+ * como máximo de 255 bytes, sin fragmentación y sólo los protocolos UDP e ICMP
+ * (este último sólo para responder al ping). Todos los paquetes que no cumplan
+ * con estas restricciones son descartados.
+ */
+
+/** Tamaño de dirección IP (en bytes) */
+#define IP_ADDR_SIZE 4
+
+/** Dirección IP nuestra */
+extern byte ip_addr_local[IP_ADDR_SIZE];
+
+/** Dirección IP de destino */
+extern byte ip_addr_remote[IP_ADDR_SIZE];
+
+/** Tamaño del paquete IP */
+extern byte ip_packet_len;
+
+/** Indica si el paquete es ICMP (si no es UDP) */
+bool ip_proto_icmp;
+
+/** Lee la cabecera del paquete IP.
+ *
+ * Deja en ip_addr_remote la ip de origen.
+ * Si devuelve false (0) es que hubo un error o es un paquete no soportado, por
+ * lo que hay que descartarlo.
+ */
+bool ip_read_packet_header();
+
+/** Escribe la cabecera del paquete IP.
+ *
+ * Pone como destino a ip_addr_remote, como origen a ip_addr_local, el protocolo
+ * lo especifica según ip_proto_icmp y como tamaño pone ip_packet_len.
+ */
+void ip_write_packet_header();
+
+#endif /* _IP_H_ */
diff --git a/pruebas/c/net/main.c b/pruebas/c/net/main.c
new file mode 100644 (file)
index 0000000..80e156c
--- /dev/null
@@ -0,0 +1,175 @@
+#include "net.h"
+#include "eth.h"
+#include "ip.h"
+#include "udp.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+int main()
+{
+       int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
+       int opt = 1;
+       struct sockaddr_ll sll;
+       socklen_t sll_len = sizeof(sll);
+       struct ifreq ifr;
+       //struct sockaddr_in sin;
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
+       {
+               perror("setsockopt()");
+               return 3;
+       }
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, "eth0", sizeof(ifr));
+       if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
+       {
+               perror("ioctl()");
+               return 1;
+       }
+       memset(&sll, 0, sizeof(struct sockaddr_ll));
+       sll.sll_family = AF_PACKET;
+       sll.sll_halen = 6;
+       sll.sll_ifindex = ifr.ifr_ifindex;
+       sll.sll_pkttype = PACKET_HOST; // nada de recibir broadcasts, etc.
+
+       // Bind
+       if (bind(s, (struct sockaddr*)&sll, sizeof(sll)))
+       {
+               perror("bind()");
+               return 3;
+       }
+
+/*
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons(2900);
+       sin.sin_addr.s_addr = INADDR_ANY;
+       if (bind(s, (struct sockaddr*)&sin, sizeof(sin)))
+       {
+               perror("bind()");
+               return 3;
+       }
+*/
+
+       // Setup de ethernet
+       eth_addr_local[0] = 0x00;
+       eth_addr_local[1] = 0x0c;
+       eth_addr_local[2] = 0x6e;
+       eth_addr_local[3] = 0x37;
+       eth_addr_local[4] = 0x19;
+       eth_addr_local[5] = 0xbf;
+       // Setup de IP
+       ip_addr_local[0] = 0x0a;
+       ip_addr_local[1] = 0x0a;
+       ip_addr_local[2] = 0x0a;
+       ip_addr_local[3] = 0x02;
+       // Setup de UDP
+       udp_port_local = WORD(0x0b, 0x54); // 2900
+
+       while (1) // escuchamos forever
+       {
+               unsigned int i;
+               net_resetrp();
+               net_resetwp();
+               printf("Recibiendo... "); fflush(stdout);
+               //if (recvfrom(s, net_buffer, sizeof(net_buffer), 0,
+               //              (struct sockaddr*) &sll, &sll_len) < 0)
+               if (recv(s, net_buffer, sizeof(net_buffer), 0) < 0)
+               {
+                       perror("recvfrom()");
+                       return 2;
+               }
+               printf("OK!\n");
+               if (!eth_read_frame_header())
+               {
+                       printf("Header ethernet inválido: DROP!\n");
+                       continue;
+               }
+               if (eth_frame_arp)
+               {
+                       printf("No le damos bola a ARP por ahora: DROP!\n");
+                       continue;
+               }
+               if (!ip_read_packet_header())
+               {
+                       printf("Header ip inválido: DROP!\n");
+                       continue;
+               }
+               if (ip_proto_icmp)
+               {
+                       printf("No le damos bola a ICMP por ahora: DROP!\n");
+                       continue;
+               }
+               if (!udp_read_dgram_header())
+               {
+                       printf("Header udp inválido: DROP!\n");
+                       continue;
+               }
+               printf("Payload: ");
+               for (i = 0; i < (udp_dgram_len - 8); ++i) // 8 == hdr udp
+                       printf("0x%02X ", net_getb());
+               printf("\n");
+
+               // Como hago echo, y el tamaño de las cabeceras no cambian,
+               // basta con sólo reescribir las cabeceras
+               eth_write_frame_header();
+               ip_write_packet_header();
+               udp_write_dgram_header();
+
+#if 0
+               // Setup para escribir header eth
+               eth_addr_remote[0] = 0x00;
+               eth_addr_remote[1] = 0xd0;
+               eth_addr_remote[2] = 0x09;
+               eth_addr_remote[3] = 0xac;
+               eth_addr_remote[4] = 0x32;
+               eth_addr_remote[5] = 0xe0;
+               eth_frame_arp = 0;
+               // Escribo header eth
+               eth_write_frame_header();
+               // Setup para escribir header ip
+               ip_addr_remote[0] = 0x0a;
+               ip_addr_remote[1] = 0x0a;
+               ip_addr_remote[2] = 0x0a;
+               ip_addr_remote[3] = 0x01;
+               // TODO para setear el tamaño del paquete habría que poner solo el
+               // tamaño del payload de UDP y calcular el resto en base a eso.
+               ip_packet_len = 20 + 8 + 5; // ip + udp + "hola"
+               ip_proto_icmp = 0;
+               // Escribo header ip
+               ip_write_packet_header();
+               // Setup para escribir header udp
+               udp_port_remote = WORD(0x0b, 0x54);
+               udp_dgram_len = 8 + 5; // udp + "hola"
+               // Escribo header udp
+               udp_write_dgram_header();
+               // Escribo "hola"
+               net_putb(0x68); net_putb(0x6f); net_putb(0x6c); net_putb(0x61);
+               net_putb(0x00);
+#endif
+               // Trato de mandar...
+               printf("Mandando a %u.%u.%u.%u:%u (%02X:%02X:%02X:%02X:%02X:%02X)\n",
+                       ip_addr_remote[0], ip_addr_remote[1], ip_addr_remote[2],
+                       ip_addr_remote[3], udp_port_remote,
+                       eth_addr_remote[0], eth_addr_remote[1], eth_addr_remote[2],
+                       eth_addr_remote[3], eth_addr_remote[4], eth_addr_remote[5]);
+               i = 14 /* eth hdr */ + ip_packet_len;
+               if (i < 60) // tamaño mínimo de ethernet
+                       i = 60;
+               if (send(s, net_buffer, i, 0) < 0)
+               {
+                       perror("send()");
+                       return 1;
+               }
+       }
+       close(s);
+       return 0;
+}
+
diff --git a/pruebas/c/net/net.c b/pruebas/c/net/net.c
new file mode 100644 (file)
index 0000000..ec59b25
--- /dev/null
@@ -0,0 +1,38 @@
+#include "net.h"
+
+byte net_buffer[4000]; // buffer
+static uint16 rp = 0; // puntero de lectura
+static uint16 wp = 0; // puntero de escritura
+
+void net_resetrp()
+{
+       rp = 0;
+}
+
+void net_resetwp()
+{
+       wp = 0;
+}
+
+byte net_getb()
+{
+       return net_buffer[rp++];
+}
+
+void net_putb(byte b)
+{
+       net_buffer[wp++] = b;
+}
+
+uint16 net_getw()
+{
+       uint16 w = net_getb() << 8;
+       return w + net_getb();
+}
+
+void net_putw(uint16 w)
+{
+       net_putb(HIGH(w));
+       net_putb(LOW(w));
+}
+
diff --git a/pruebas/c/net/net.h b/pruebas/c/net/net.h
new file mode 100644 (file)
index 0000000..5acc67c
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _NET_H_
+#define _NET_H_
+
+#include "types.h"
+
+extern byte net_buffer[4000];
+
+void net_resetrp();
+
+void net_resetwp();
+
+/** lee un byte de la placa de red */
+byte net_getb();
+
+/** escribe un byte a la placa de red */
+void net_putb(byte);
+
+/** lee un word de la placa de red */
+uint16 net_getw();
+
+/** escribe un word a la placa de red */
+void net_putw(uint16);
+
+#endif /* _NET_H_ */
diff --git a/pruebas/c/net/types.h b/pruebas/c/net/types.h
new file mode 100644 (file)
index 0000000..82d3cad
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+/** valores posibles de un booleano */
+enum { false = 0, true = 1 };
+
+/** convierte 2 bytes (high, low) en un word */
+#define WORD(high, low) ((high << 8) + low)
+
+/** convierte un word en 2 bytes */
+#define UNPACK(word, high, low) (high = (word >> 8), low = word)
+
+/** obtiene parte alta de un word */
+#define HIGH(word) (word >> 8)
+
+/** obtiene parte baja de un word */
+#define LOW(word) (word)
+
+#if 1 /* i386 */
+#include <stdint.h>
+
+/** booleano */
+typedef unsigned char bool;
+
+/** entero sin signo de 8 bits */
+typedef uint8_t byte;
+
+/** entero sin signo de 16 bits */
+typedef uint16_t uint16;
+
+#endif
+
+#if 0 /* 8051 */
+
+/** booleano */
+typedef unsigned char bool;
+
+/** entero sin signo de 8 bits */
+typedef unsigned char byte;
+
+/** entero sin signo de 16 bits */
+typedef unsigned int uint16;
+
+#endif
+
+#endif /* _TYPES_H_ */
diff --git a/pruebas/c/net/udp.c b/pruebas/c/net/udp.c
new file mode 100644 (file)
index 0000000..8778501
--- /dev/null
@@ -0,0 +1,80 @@
+#include "ip.h"
+#include "udp.h"
+
+uint16 udp_port_local;
+
+uint16 udp_port_remote;
+
+byte udp_dgram_len;
+
+/* para calcular checksum */
+uint16 checksum;
+
+/* agrega un word al checksum calculado */
+void udp_checksum_sum(uint16 w)
+{
+       checksum += w;
+       if (checksum < w) /* corrección de carry (hubo OV) */
+               ++checksum;
+}
+
+/* indica si el checksum calculado está ok */
+bool udp_checksum_ok()
+{
+       return (uint16)~checksum;
+}
+
+bool udp_read_dgram_header()
+{
+       uint16 p;
+       /* reseteamos checksum */
+       checksum = 0;
+       /* el UDP tiene un checksum que incluye parte de la cabecera IP */
+       /* ip de origen */
+       udp_checksum_sum(WORD(ip_addr_remote[0], ip_addr_remote[1]));
+       udp_checksum_sum(WORD(ip_addr_remote[2], ip_addr_remote[3]));
+       /* ip de destino */
+       udp_checksum_sum(WORD(ip_addr_local[0], ip_addr_local[1]));
+       udp_checksum_sum(WORD(ip_addr_local[2], ip_addr_local[3]));
+       /* protocolo expresado en 16 bits (0x11 es UDP) */
+       udp_checksum_sum(0x11);
+       /* tamaño del paquete UDP (sin las cabeceras que son 20 bytes) */
+       udp_checksum_sum(ip_packet_len - 20);
+       /* de ahora en más todos los datos del checksum corresponden a UDP */
+       /* puerto origen (remoto) */
+       udp_port_remote = net_getw();
+       /* agregamos puerto de origen al checksum */
+       udp_checksum_sum(udp_port_remote);
+       /* sólo aceptamos datagramas a nuestro puerto */
+       p = net_getw();
+       if (p != udp_port_local)
+               return false; /* drop */
+       /* agregamos puerto de destino al checksum */
+       udp_checksum_sum(udp_port_local);
+       /* tamaño del datagrama */
+       if (net_getb()) /* no soportamos más de 255 bytes */
+               return false; /* drop */
+       udp_dgram_len = net_getb();
+       /* agregamos tamaño al checksum */
+       udp_checksum_sum(udp_dgram_len);
+       /* agregamos checksum al checksum */
+       udp_checksum_sum(net_getw());
+       /* falta agregar el cuerpo del mensaje para verificar la suma
+        * esto debe hacerlo el protocolo que sigue para poder seguir obteniendo
+        * los datos de la placa de red byte a byte */
+       return true;
+}
+
+void udp_write_dgram_header()
+{
+       /* puerto origen */
+       net_putw(udp_port_local);
+       /* puerto destino */
+       net_putw(udp_port_remote);
+       /* tamaño del datagrama */
+       net_putb(0x00); /* parte alta en 0 porque no soportamos más de 255 */
+       net_putb(udp_dgram_len);
+       /* indicamos que no se usa checksum */
+       net_putw(0x0000);
+}
+
diff --git a/pruebas/c/net/udp.h b/pruebas/c/net/udp.h
new file mode 100644 (file)
index 0000000..9530a13
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#include "types.h"
+#include "net.h"
+
+/** @file
+ * Datagrama UDP.
+ *
+ * <pre>
+ *  0      7 8     15 16    23 24    31  
+ * +--------+--------+--------+--------+ 
+ * |     Source      |   Destination   | 
+ * |      Port       |      Port       | 
+ * +--------+--------+--------+--------+ 
+ * |                 |                 | 
+ * |     Length      |    Checksum     | 
+ * +--------+--------+--------+--------+ 
+ * |                                     
+ * |          data octets ...            
+ * +---------------- ...                 
+ * </pre>
+ *
+ * Aceptamos sólo datagramas UDP que vayan a el puerto de nuestra aplicación y
+ * cuyo tamaño sea menor a 255. El resto es descartado.
+ * El Length es tanto de la cabecera como datos, por lo tanto el tamaño mínimo
+ * es 8.
+ *
+ * El checksum se calcula utilizando algunos datos de la capa inferior e incluye
+ * a los datos. Entonces es calculado sobre el siguiente paquete 'virtual':
+ *
+ * <pre>
+ *  0      7 8     15 16    23 24    31  
+ * +--------+--------+--------+--------+
+ * |         ip source address         |
+ * +--------+--------+--------+--------+
+ * |      ip destination address       |
+ * +--------+--------+--------+--------+
+ * |  zero  |protocol|   UDP length    |
+ * +--------+--------+--------+--------+
+ * |     Source      |   Destination   | 
+ * |      Port       |      Port       | 
+ * +--------+--------+--------+--------+ 
+ * |                 |                 | 
+ * |     Length      |    Checksum     | 
+ * +--------+--------+--------+--------+ 
+ * |                                     
+ * |          data octets ...            
+ * +---------------- ...                 
+ * </pre>
+ */
+
+/** Puerto UDP nuestro */
+extern uint16 udp_port_local;
+
+/** Puerto UDP de destino */
+extern uint16 udp_port_remote;
+
+/** Tamaño del datagrama UDP */
+extern byte udp_dgram_len;
+
+/** Lee la cabecera del datagrama UDP.
+ *
+ * Deja en udp_port_remote el puerto de origen.
+ * Si devuelve false (0) es que hubo un error o es un datagrama no soportado,
+ * por lo que hay que descartarlo.
+ */
+bool udp_read_dgram_header();
+
+/** Escribe la cabecera del datagrama UDP.
+ *
+ * Pone como puerto destino a udp_port_remote, como origen udp_port_local y como
+ * tamaño a udp_dgram_len (en la parte baja y 0 en la parte alta).
+ */
+void udp_write_dgram_header();
+
+#endif /* _UDP_H_ */