nuestro stack de red utilizando raw sockets.
--- /dev/null
+#CFLAGS=-std=c99
+
+objs=main.o eth.o net.o ip.o udp.o
+
+main: $(objs)
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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_ */
--- /dev/null
+#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]);
+}
+
--- /dev/null
+#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_ */
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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));
+}
+
--- /dev/null
+#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_ */
--- /dev/null
+#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_ */
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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_ */