--- /dev/null
+#include "eth.h"
+
+/** tipos de paquetes transportados soportados */
+enum { IP = 0x0800, ARP = 0x0806 };
+
+byte mac_addr_local[MAC_ADDR_SIZE];
+
+byte mac_addr_remote[MAC_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 < MAC_ADDR_SIZE; ++i)
+ if (mac_addr_local[i] != net_getb())
+ return false; /* no es para nosotros (drop) */
+ /* obtenemos MAC de origen */
+ for (i = 0; i < MAC_ADDR_SIZE; ++i)
+ mac_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;
+ /* tipo de paquete transportado */
+ uint16 type = ;
+ /* mandamos como MAC de destino la remota */
+ for (i = 0; i < ETH_MAC_SIZE; ++i)
+ net_putb(mac_addr_remote[i]);
+ /* y como fuente la nuestra */
+ for (i = 0; i < ETH_MAC_SIZE; ++i)
+ net_putb(mac_addr_local[i]);
+ /* escribimos el tipo de paquete que transporta el frame */
+ net_putw(eth_frame_arp ? TYPE_ARP : TYPE_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 MAC_ADDR_SIZE 6
+
+/** Dirección MAC nuestra */
+extern byte mac_addr_local[ETH_MAC_SIZE];
+
+/** Dirección MAC de destino */
+extern byte mac_addr_remote[ETH_MAC_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 };
+
+extern byte ip_addr_local[IP_ADDR_SIZE];
+
+extern byte ip_addr_remote[IP_ADDR_SIZE];
+
+byte ip_packet_len;
+
+bool ip_proto_icmp;
+
+bool ip_read_packet_header()
+{
+ /* variable para utilitaria (iterar y/o buffer) */
+ byte c;
+ /* versión y tamaño de cabecera vienen en el 1er byte */
+ c = net_getb();
+ /* sólo soportamos versión 4 */
+ if ((c >> 4) != 4)
+ return false; /* drop */
+ /* tamaño de cabecera */
+ if ((c & 0x0F) != 5) /* no aceptamos opciones raras =) */
+ return false; /* drop */
+ /* ignoramos el TOS */
+ net_getb();
+ /* obtenemos tamaño del paquete */
+ if (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 */
+ /* ignoramos identificación */
+ net_getb(); net_getb(); /* 2 bytes */
+ /* 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) */
+ if (net_getw() & 0x3FFF)
+ return false; /* drop */
+ /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta
+ * acá el pobre =) */
+ net_getb();
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ switch (net_getb())
+ {
+ case ICMP:
+ ip_proto_icmp = true;
+ break;
+ case UDP:
+ ip_proto_icmp = false;
+ break;
+ default:
+ return false; /* drop */
+ }
+ /* checksum */
+ net_getb(); net_getb(); /* TODO: verificar checksum */
+ /* obtenemos IP de origen */
+ for (c = 0; c < IP_ADDR_SIZE; ++c)
+ ip_addr_remote[c] = net_getb();
+ /* vemos si el paquete es para nosotros (ningún soportar broadcast =) */
+ for (c = 0; c < IP_ADDR_SIZE; ++c)
+ if (ip_addr_local[c] != net_getb())
+ return false; /* no es para nosotros */
+ return true;
+}
+
+void ip_write_packet_header()
+{
+ /* variable para iterar */
+ byte i;
+ /* identificador del paquete IP (incrementa con cada paquete) */
+ static uint16 id = 0;
+ /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */
+ net_putb(0x45);
+ /* TOS */
+ if (ip_proto_icmp)
+ net_putb(0xc0); /* Precedence: Internetwork Control */
+ else
+ net_putb(0x00); /* Precedence: Normal */
+ net_getb();
+ /* escribimos tamaño del paquete */
+ net_putb(0x00); /* nunca vamos a mandar algo de más de 255 bytes */
+ net_putb(ip_packet_len);
+ /* identificación (sirve para reensamblar paquetes) */
+ net_putw(id);
+ /* pedimos que no se fragmente */
+ net_putb(0x40); /* Don't Fragment (DF) = 1 */
+ net_putb(0x00); /* offset de fragmento = 0 */
+ /* TTL de 64 saltos porque está de moda */
+ net_putb(0x40);
+ /* protocolo (sólo soportamos UDP e ICMP) */
+ if (ip_proto_icmp)
+ net_putb(ICMP);
+ else
+ net_putb(UDP);
+ /* checksum */
+ net_putb(0x00); net_putb(0x00); /* TODO: calcular checksum */
+ /* ponemos como dirección IP de origen la nuestra */
+ for (i = 0; i < IP_ADDR_SIZE; ++i)
+ net_putb(ip_addr_local[i]);
+ /* IP de destino, la remota */
+ for (i = 0; i < IP_ADDR_SIZE; ++i)
+ net_putb(ip_addr_remote[i]);
+}
+
--- /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"
+
+uint16 net_getw()
+{
+ byte b = net_getb();
+ return (b << 8) + net_getb();
+}
+
+void net_putw(uint16 w)
+{
+ net_putb(w >> 8); /* parte alta */;
+ net_putb(w); /* parte baja */;
+}
+
--- /dev/null
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+/** valores posibles de un booleano */
+enum { false = 0, true = 1 };
+
+#if 1 /* i386 */
+
+/** booleano */
+typedef unsigned char bool;
+
+/** entero sin signo de 8 bits */
+typedef unsigned char byte;
+
+/** entero sin signo de 16 bits */
+typedef unsigned short 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"
+
+extern uint16 udp_port_local;
+
+extern uint16 udp_port_remote;
+
+extern byte udp_dgram_len;
+
+bool udp_read_dgram_header()
+{
+ /* puerto origen */
+ udp_port_remote = net_getw();
+ /* sólo aceptamos datagramas a nuestro puerto */
+ if (net_getw() != udp_port_local)
+ return false; /* drop */
+ /* tamaño del datagrama */
+ if (net_getb()) /* no soportamos más de 255 bytes */
+ return false; /* drop */
+ udp_dgram_len = net_getb();
+ /* descartamos checksum */
+ net_getw();
+ 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);
+ return true;
+}
+
--- /dev/null
+#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 no se calcula ni se verifica, ya que sabemos que la capa
+ * inferior (ethernet) tiene verificación de errores de los datos.
+ */
+
+/** 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_ */