From 8023ed452d2200f35b60af35149bcc542c85efc4 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 28 Nov 2005 03:53:52 +0000 Subject: [PATCH] =?utf8?q?Stack=20de=20red=20funcionando.=20Es=20b=C3=A1si?= =?utf8?q?camente=20un=20echo=20server=20implementado=20sobre=20nuestro=20?= =?utf8?q?stack=20de=20red=20utilizando=20raw=20sockets.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- pruebas/c/net/Makefile | 5 ++ pruebas/c/net/eth.c | 52 ++++++++++++ pruebas/c/net/eth.h | 51 ++++++++++++ pruebas/c/net/ip.c | 159 +++++++++++++++++++++++++++++++++++++ pruebas/c/net/ip.h | 64 +++++++++++++++ pruebas/c/net/main.c | 175 +++++++++++++++++++++++++++++++++++++++++ pruebas/c/net/net.c | 38 +++++++++ pruebas/c/net/net.h | 24 ++++++ pruebas/c/net/types.h | 46 +++++++++++ pruebas/c/net/udp.c | 80 +++++++++++++++++++ pruebas/c/net/udp.h | 77 ++++++++++++++++++ 11 files changed, 771 insertions(+) create mode 100644 pruebas/c/net/Makefile create mode 100644 pruebas/c/net/eth.c create mode 100644 pruebas/c/net/eth.h create mode 100644 pruebas/c/net/ip.c create mode 100644 pruebas/c/net/ip.h create mode 100644 pruebas/c/net/main.c create mode 100644 pruebas/c/net/net.c create mode 100644 pruebas/c/net/net.h create mode 100644 pruebas/c/net/types.h create mode 100644 pruebas/c/net/udp.c create mode 100644 pruebas/c/net/udp.h diff --git a/pruebas/c/net/Makefile b/pruebas/c/net/Makefile new file mode 100644 index 0000000..2658049 --- /dev/null +++ b/pruebas/c/net/Makefile @@ -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 index 0000000..0f9e7d2 --- /dev/null +++ b/pruebas/c/net/eth.c @@ -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 index 0000000..6ae4155 --- /dev/null +++ b/pruebas/c/net/eth.h @@ -0,0 +1,51 @@ +#ifndef _ETH_H_ +#define _ETH_H_ + +#include "types.h" +#include "net.h" + +/** @file + * Estructura de un frame ethernet. + * + *
+ * /--- 8 ----/--- 6 ----/--- 6 ----/- 2 --/- 46-1500 -/- 4 -/  bytes
+ * +----------+----------+----------+------+-----------+-----+
+ * | preamble | dst addr | src addr | type |   data    | crc |
+ * +----------+----------+----------+------+-----------+-----+
+ * 
+ * + * 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 index 0000000..9dcac1b --- /dev/null +++ b/pruebas/c/net/ip.c @@ -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 index 0000000..ec3a892 --- /dev/null +++ b/pruebas/c/net/ip.h @@ -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 index 0000000..80e156c --- /dev/null +++ b/pruebas/c/net/main.c @@ -0,0 +1,175 @@ +#include "net.h" +#include "eth.h" +#include "ip.h" +#include "udp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..ec59b25 --- /dev/null +++ b/pruebas/c/net/net.c @@ -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 index 0000000..5acc67c --- /dev/null +++ b/pruebas/c/net/net.h @@ -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 index 0000000..82d3cad --- /dev/null +++ b/pruebas/c/net/types.h @@ -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 + +/** 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 index 0000000..8778501 --- /dev/null +++ b/pruebas/c/net/udp.c @@ -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 index 0000000..9530a13 --- /dev/null +++ b/pruebas/c/net/udp.h @@ -0,0 +1,77 @@ +#ifndef _UDP_H_ +#define _UDP_H_ + +#include "types.h" +#include "net.h" + +/** @file + * Datagrama UDP. + * + *
+ *  0      7 8     15 16    23 24    31  
+ * +--------+--------+--------+--------+ 
+ * |     Source      |   Destination   | 
+ * |      Port       |      Port       | 
+ * +--------+--------+--------+--------+ 
+ * |                 |                 | 
+ * |     Length      |    Checksum     | 
+ * +--------+--------+--------+--------+ 
+ * |                                     
+ * |          data octets ...            
+ * +---------------- ...                 
+ * 
+ * + * 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': + * + *
+ *  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 ...            
+ * +---------------- ...                 
+ * 
+ */ + +/** 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_ */ -- 2.43.0