--- /dev/null
+#ifndef __FUZIX__NET_DOT_H
+#define __FUZIX__NET_DOT_H
+
+/* This code is based upon Linux 1.2.13 TCP/IP
+ Ross Biro,
+ Fred N. van Kempen
+ Mark Evans
+ Corey Minyard
+ Florian La Roche
+ Charles Hedrick
+ Linus Torvalds
+ Alan Cox
+ Matthew Dillon
+ Arnt Gulbrandsen
+*/
+
+#define MAX_IP 1500 /* Largest frame we support */
+
+/*
+ * Networking Functionality
+ */
+
+struct iphdr {
+#if defined(LITTLE_ENDIAN_BITFIELD)
+ uint8_t ihl:4,
+ version:4;
+#elif defined (BIG_ENDIAN_BITFIELD)
+ uint8_t version:4,
+ ihl:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ uint8_t tos;
+ uint16_t tot_len;
+ uint16_t id;
+ uint16_t frag_off;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t check;
+ uint32_t saddr;
+ uint32_t daddr;
+ /*The options start here. */
+};
+
+#define IPOPT_END 0
+#define IPOPT_NOOP 1
+#define IPOPT_SEC 130
+#define IPOPT_LSRR 131
+#define IPOPT_SSRR 137
+#define IPOPT_RR 7
+#define IPOPT_SID 136
+#define IPOPT_TIMESTAMP 68
+
+#define ICMP_ECHOREPLY 0 /* Echo Reply */
+#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
+#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
+#define ICMP_REDIRECT 5 /* Redirect (change route) */
+#define ICMP_ECHO 8 /* Echo Request */
+#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
+#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
+#define ICMP_TIMESTAMP 13 /* Timestamp Request */
+#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
+#define ICMP_INFO_REQUEST 15 /* Information Request */
+#define ICMP_INFO_REPLY 16 /* Information Reply */
+#define ICMP_ADDRESS 17 /* Address Mask Request */
+#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
+
+
+/* Codes for UNREACH. */
+#define ICMP_NET_UNREACH 0 /* Network Unreachable */
+#define ICMP_HOST_UNREACH 1 /* Host Unreachable */
+#define ICMP_PROT_UNREACH 2 /* Protocol Unreachable */
+#define ICMP_PORT_UNREACH 3 /* Port Unreachable */
+#define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
+#define ICMP_SR_FAILED 5 /* Source Route failed */
+#define ICMP_NET_UNKNOWN 6
+#define ICMP_HOST_UNKNOWN 7
+#define ICMP_HOST_ISOLATED 8
+#define ICMP_NET_ANO 9
+#define ICMP_HOST_ANO 10
+#define ICMP_NET_UNR_TOS 11
+#define ICMP_HOST_UNR_TOS 12
+
+/* Codes for REDIRECT. */
+#define ICMP_REDIR_NET 0 /* Redirect Net */
+#define ICMP_REDIR_HOST 1 /* Redirect Host */
+#define ICMP_REDIR_NETTOS 2 /* Redirect Net for TOS */
+#define ICMP_REDIR_HOSTTOS 3 /* Redirect Host for TOS */
+
+/* Codes for TIME_EXCEEDED. */
+#define ICMP_EXC_TTL 0 /* TTL count exceeded */
+#define ICMP_EXC_FRAGTIME 1 /* Fragment Reass time exceeded */
+
+
+struct icmphdr {
+ uint8_t type;
+ uint8_t code;
+ uint16_t checksum;
+ union {
+ struct {
+ uint16_t id;
+ uint16_t sequence;
+ } echo;
+ uint32_t gateway;
+ } un;
+};
+
+struct tcphdr {
+ uint16_t source;
+ uint16_t dest;
+ uint32_t seq;
+ uint32_t ack_seq;
+#if defined(LITTLE_ENDIAN_BITFIELD)
+ uint16_t res1:4,
+ doff:4,
+ fin:1,
+ syn:1,
+ rst:1,
+ psh:1,
+ ack:1,
+ urg:1,
+ res2:2;
+#elif defined(BIG_ENDIAN_BITFIELD)
+ uint16_t doff:4,
+ res1:4,
+ res2:2,
+ urg:1,
+ ack:1,
+ psh:1,
+ rst:1,
+ syn:1,
+ fin:1;
+#else
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+ uint16_t window;
+ uint16_t check;
+ uint16_t urg_ptr;
+};
+
+struct udphdr {
+ uint16_t source;
+ uint16_t dest;
+ uint16_t len;
+ uint16_t check;
+};
+
+/* Standard well-defined IP protocols. */
+enum {
+ IPPROTO_IP = 0, /* Dummy protocol for TCP */
+ IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
+ IPPROTO_IGMP = 2, /* Internet Gateway Management Protocol */
+ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
+ IPPROTO_TCP = 6, /* Transmission Control Protocol */
+ IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
+ IPPROTO_PUP = 12, /* PUP protocol */
+ IPPROTO_UDP = 17, /* User Datagram Protocol */
+ IPPROTO_IDP = 22, /* XNS IDP protocol */
+
+ IPPROTO_RAW = 255, /* Raw IP packets */
+ IPPROTO_MAX
+};
+
+
+/* Internet address. */
+struct in_addr {
+ uint32_t s_addr;
+};
+
+struct sockaddr_in {
+ int16_t sin_family;
+ uint16_t sin_port;
+ struct in_addr sin_addr;
+ uint8_t sin_zero[8];
+};
+
+extern int mac_queue(void);
+extern int loopback_queue(void);
+
+extern struct socket *sock_find(uint16_t src, uint16_t dst);
+
+extern void output_begin(void);
+extern void output_add(void *ptr, uint16_t len);
+
+extern int pkt_pull(void *ptr, uint16_t len);
+extern uint8_t *pkt_next;
+extern uint16_t pkt_len;
+
+
+extern void ip_mirror(void);
+extern void ip_prepare(struct socket *s, uint16_t len);
+extern int ip_output(void *pbuf, uint16_t plen, void *dbuf, uint16_t dlen);
+extern void ip_rcv(void);
+
+extern uint16_t ip_compute_csum(void *ptr, uint16_t size);
+extern uint16_t ip_compute_csum_data(void *ptr, uint16_t size, void *dptr, uint16_t dsize);
+
+extern struct iphdr iph;
+extern struct iphdr iph2;
+extern struct iphdr oiph;
+
+extern void icmp_rcv(void);
+extern void icmp_send_unreach(uint8_t type, uint8_t code);
+
+extern int udp_send(struct socket *s, void *buf, uint16_t len);
+extern void udp_rcv(void);
+
+extern void tcp_rcv(void);
+extern int tcp_send(struct socket *s, void *buf, uint16_t len);
+
+#ifdef LITTLE_ENDIAN
+#define LOOPBACK(x) (((uint8_t)x)==0x7F)
+#define MULTICAST(x) ((((uint8_t)x) & 0xF0) == 0xE0)
+#else
+#define LOOPBACK(x) (((uint8_t)((x) >> 24)) == 0x7F)
+#define MULTICAST(x) (((uint8_t)((x) >> 28)) == 0xE)
+#endif
+
+#define SNMP(x)
+
+#define PKT_HOST 0
+#define PKT_BROADCAST 1
+#define PKT_MULTICAST 2
+#define PKT_OTHER 3
+
+#endif
--- /dev/null
+#include <kernel.h>
+#include <net.h>
+
+static void icmp_echo(void)
+{
+ ip_mirror(&iph); /* Flip the source and destination into oiph*/
+ icmph.type = ICMP_ECHOREPLY;
+ icmph.code = 0;
+ icmph.checksum = 0;
+ icmph.checksum = ip_compute_csum_data(&icmph, sizeof(icmph), pkt_next, pkt_left);
+ ip_output(&icmph, sizeof(icmph), pkt_next, pkt_left) ;
+ SNMP(icmp_out_echoreply);
+}
+
+static void icmp_unreach(void)
+{
+ /* Get the header of the packet that caused the problem */
+ if (!pkt_pull(&iph2, sizeof(struct iphdr)))
+ return;
+ if (iph2.protocol == IPROTO_TCP)
+ tcp_unreach();
+}
+
+/*
+ * We handle the various unreachables and echo (ping) but as per
+ * usual practice we don't touch broadcasts and we don't listen
+ * to redirects and the like.
+ */
+void icmp_rcv(void)
+{
+ uint16_t mask;
+ SNMP(icmp_in);
+ if (pkt_type != PKT_HOST)
+ return;
+ if (pkt_left < sizeof(struct icmphdr) || ip_compute_csum(pkt_next, pkt_left)) {
+ SNMP(icmp_in_error);
+ return;
+ }
+ pkt_pull(&icmph, sizeof(struct icmphdr));
+ if (icmph.type > ICMP_ADDRESREPLY)
+ return;
+ SNMP(icmp_in_type[icmph.type]);
+ mask = 1 << icmph.type;
+ if (mask & ICMP_UNREACH_MASK)
+ icmp_unreach();
+ else if (icmph.type == ICMP_ECHO)
+ icmp_echo();
+}
+
+/* Used by upper layers to send ICMP messages responding to frames. iph holds
+ the original ip header, pkt_ip points to the ip header in the data block */
+
+void icmp_send_unreach(uint8_t type, uint8_t code)
+{
+ len = iph.len << 2 + 8;
+ ip_mirror(&iph);
+ icmph.type = type;
+ icmph.code = code;
+ icmph.checksum = 0;
+ /* FIXME: better to send more for modern stacks */
+ icmph.checksum = ip_compute_csum_data(&icmph, sizeof(icmph), pkt_ip, len);
+ ip_output(&icmph, sizeof(icmph), pkt_ip, len);
+ SNMP(icmp_out_unreach);
+}
+
--- /dev/null
+#include "kernel.h"
+#include "net.h"
+
+void ip_mirror(void)
+{
+ memcpy(&oiph, &iph, sizeof(iph));
+ if (pkt_type != PKT_HOST)
+ oiph.saddr = ip_addr;
+ else
+ oiph.saddr = iph.daddr;
+ oiph.daddr = iph.saddr;
+ oiph.check = 0;
+ oiph.ihl = 5;
+ oiph.frag_off = 0;
+}
+
+void ip_prepare(struct socket *s, uint16_t len)
+{
+ oiph.saddr = s->saddr;
+ oiph.daddr = s->daddr;
+ oiph.tot_len = htons(len);
+ oiph.protocol = s->proto;
+ oiph.ihl = 5;
+ oiph.check = 0;
+ oiph.frag_off = 0;
+ oiph.tos = 0;
+}
+
+int ip_output(void *pbuf, uint16_t plen, void *dbuf, uint16_t dlen)
+{
+ oiph.id = htons_inc(ip_id);
+ oiph.ttl = IP_TTL;
+ oiph.check = 0;
+ oiph.version = 4;
+ /* Options not supported */
+ oiph.ihl = 5;
+ oiph.check = ip_checksum(&oiph, sizeof(oiph));
+
+ output_begin(); /* Set up output buffer (space left for header) */
+ output_add(&oiph, 4 * oiph.ihl);
+ output_add(pbuf, plen);
+ ouput_add(dbuf, dlen);
+ if (LOOPBACK(oiph.daddr) || oiph.daddr == ip_addr)
+ return loopback_queue();
+ else
+ return mac_queue();
+ /* We do blocking writes, when this code returns the buffer is on the
+ wire and we don't have to fret about re-use. Does mean slip has to be
+ careful to buffer the receive side while queueing output */
+}
+
+void ip_rcv(void)
+{
+ uint16_t len = pkt_left;
+ SNMP(ip_in_receives);
+ if (!pkt_pull(&iph, sizeof(struct iphdr)))
+ return;
+ plen = ntohs(iph.tot_len);
+ /* FIXME: for speed fold ihl/version and be smarter */
+ if (iph.ihl < 5 || iph.version != 4 || len < plen) {
+ SNMP(ip_in_hdr_errors);
+ return;
+ }
+ plen -= sizeof(struct iphdr));
+ if (pkt_len > plen)
+ pkt_len = plen;
+
+ /* FIXME: checksum */
+ if (iph.ihl != 5)
+ if (ip_options())
+ return;
+ /* No frags for now (memory limits on 8bit) */
+ if (iph.frag_off)
+ return;
+ if (iph.daddr == 0xFFFFFFFF)
+ pkt_type = PKT_BROADCAST;
+ else if (MULTICAST(iph.daddr))
+ pkt_type = PKT_MULTICAST;
+ else if (iph.daddr == ip_addr || LOOPBACK(iph.daddr))
+ pkt_type = PKT_HOST;
+ else
+ /* No forwarding so we don't have to worry about martians either */
+ return;
+
+ /* FIXME: raw sockets ?? */
+ if (iph.protocol == IPPROTO_TCP)
+ tcp_rcv();
+ else if (iph.protocol == IPPROTO_UDP)
+ udp_rcv();
+ else if (iph.protocol == IPPROTO_ICMP)
+ icmp_rcv();
+ else
+ icmp_send_unreach(ICMP_DEST_UNREACH, ICMP_PROT_UNREACH);
+}
--- /dev/null
+#include "kernel.h"
+#include "net.h"
+
+/*
+ * We copy headers into fixed target locations so that
+ * processors with poor indexing generate references to
+ * link time resolved static memory addresses.
+ */
+
+int pkt_pull(void *ptr, uint16_t len)
+{
+ if (len > pkt_left)
+ return -1;
+ memcpy(ptr, pkt_buf, len);
+ pkt_buf += len;
+ pkt_left -= len;
+ return 0;
+}
--- /dev/null
+
+static void tcp_fix_endian(void)
+{
+ ntohl_insitu(&tcph.seq);
+ ntohl_insitu(&tcph.ack_seq);
+}
+
+static void tcp_tx_reset(void)
+{
+ tcp_fix_endian(); /* Reverse the fixes of endianness */
+ ip_mirror(); /* Going back where it came from */
+ /* Now concoct a tcp reset frame and send it */
+ /* FIXME */
+}
+
+static int tcp_sequence(struct sock *sk)
+{
+ /* We share next_seq with tcp_queue */
+ next_seq = pkt_left - 4 *tcph.doff;
+ if (tcph.fin)
+ next_seq++;
+ next_seq += tcph.seq;
+ /* We don't do out of order - if it doesn't overlap or directly follow
+ then we kick it out */
+ if (after(tcph.seq, s->acked_seq) || /* Off the start */
+ !after(next_seq, s->acked_seq)) { /* Already consumed */
+ if (tcph.rst)
+ return 0;
+ if (s->pstate == TCP_SYN_SENT || s->pstate == TCP_SYN_RECV) {
+ tcp_tx_reset();
+ return 1;
+ }
+ /* Tell the other end we are out of sync */
+ tcp_send_ack(s);
+ }
+ /* Ok data is relevant */
+ return 1;
+}
+
+/* We don't accept data + SYN together. This code assumes that as it doesn't
+ factor tcph.syn into the maths */
+
+static void tcp_queue(struct sock *s)
+{
+ uint8_t fin = tcph.fin;
+ /* We know this is 16bit safe */
+ uint16_t usable = next_seq - sk->acked_seq;
+ uint8_t *dptr;
+
+ if (fin) /* FIN isn't a data byte */
+ usable--;
+ /* First byte of data to consume */
+ dptr = pkt_next + pkt_left - usable;
+ /* Might be nicer to allow a FIN anyway if all data fitted ? */
+ if (usable > s->window)
+ usable = s->window;
+ fin = 0;
+ }
+ if (usable) {
+ if (s->shutdown & RCV_SHUTDOWN) {
+ tcp_tx_reset();
+ return;
+ }
+ /* Queue the data */
+ dgram_init();
+ dgram.sport = tcph.src;
+ dgram.dport = tcph.dst;
+ queue_stream(s->inode, dptr, usable);
+ /* Our ack position moves on */
+ s->acked_seq += usable;
+ }
+ if (fin) { /* We have accepted the FIN */
+ s->acked_seq++;
+ tcp_fin_recv(s);
+ }
+ /* Queue an ack. More likely we'll actually do the ack when the user
+ consumes the data */
+ tcp_queue_ack(s);
+}
+
+void tcp_rcv(void)
+{
+ struct socket *s;
+ uint16_t len = pkt_left;
+ void *thp = pkt_next;
+ SNMP(tcp_in_segs);
+ if (pkt_type != PKT_HOST)
+ return;
+ if (!pkt_pull(&tcph, sizeof(tcph)))
+ return;
+ tcp_fix_endian(); /* Cheaper to do it once */
+ if (ip_compute_csum(thp, pkt_left) || tcph.doff < sizeof(tcph) / 4)
+ return;
+ if (tcph.doff != sizeof(tcph) / 4)
+ if (!tcp_options())
+ return;
+ s = tcp_find();
+
+ if (s == NULL) {
+ goto reset;
+ }
+
+ if (s->pstate != TCP_ESTABLISHED) {
+ /* New connection check */
+ if (s->pstate == TCP_LISTEN) {
+ if (thp.ack || thp.ack || !thp.syn)
+ goto reset;
+ tcp_create(s);
+ return;
+ }
+ /* Duplicate SYN */
+ if (s->pstate == TCP_SYN_RECV && tcph.syn && tcph->seq + 1 == sk->ack_seq)
+ return;
+ if (s->pstate == TCP_SYN_SENT) {
+ if (tcph.ack) {
+ if (!tcp_valid_ack(s))
+ goto reset;
+ if (tcph.rst) {
+ tcp_rx_reset(s); /* Reset the socket */
+ return;
+ }
+ if (!tcph.syn)
+ return;
+ s->acked_seq = tcph.seq + 1;
+ s->fin_seq = tcp.seq;
+ s->pstate = TCP_ESTABLISHED;
+ s->copied_seq = s->acked_seq;
+ tcp_established(s);
+ if (sk->max_window == 0)
+ s->max_window = 32;
+ s->mss = min(s->max_window, s->mtu);
+ }
+ } else {
+ /* Not an ACK */
+ if (tcph.syn && !tcph.rst) {
+ if (iph.saddr == iph.daddr && tcph.source == tcph.dest)
+ goto reset;
+ s->pstate = TCP_SYN_RECV;
+ /* Check: SYN|ACK reply needed ? */
+ }
+ return;
+ }
+ goto rfc_step6;
+ }
+ }
+ /* Established states. Everything must be in window to be valid */
+ if (!tcp_sequence(s))
+ return;
+ if (tcph.rst) {
+ tcp_rx_reset(s);
+ return;
+ }
+ if (tcph.syn && !syn_ok)
+ goto reset;
+ if (tcph.ack && !tcp_ack(s))
+ if (s->pstate == TCP_SYN_RECV)
+ goto reset;
+rfc_step6:
+ /* Urgent data - skipping for now */
+ s->bytes_rcv += pkt_left;
+ /* Empty probe */
+ if (pkt_left == 0 && !tcph.fin) }
+ if (!tcph.ack)
+ tcp_send_ack(s);
+ return;
+ }
+ /* Queue data */
+ tcp_queue(s);
+ return;
+
+reset:
+ tcp_tx_reset();
+}
--- /dev/null
+#include <kernel.h>
+#include <net.h>
+
+static struct socket *udp_find(void)
+{
+ return sock_find(udph.src, udph.dst);
+}
+
+void udp_rcv(void)
+{
+ struct socket *s;
+ SNMP(ip_in_delivers);
+ if (!pkt_pull(&udph, sizeof(struct udphdr)) ||
+ udph.csum && ip_compute_csum_data(&udph, sizeof(udph), pkt_next, pkt_left))) {
+ SNMP(udp_in_errors);
+ return;
+ }
+
+ s = udp_find();
+ if (s == NULL)
+ icmp_send_unreach(ICMP_DEST_UNREACH, ICMP_PORT_UNREACH);
+ else {
+ dgram_init();/* msg.len = pkt_left, msg.ptr = pkt_next, sets src/dst ip */
+ dgram.sport = udph.src;
+ dgram.dport = udph.dst;
+ queue_dgram(s->inode);
+ }
+}
+
+int udp_send(struct socket *s, void *buf, uint16_t len)
+{
+ udph.src = s->sport;
+ udph.dst = s->dport;
+ udph.len = htons(len);
+ udph.check = 0;
+ len += sizeof(struct udphdr);
+ ip_prepare(s, IPPROTO_UDP, len);
+ udph.check = ip_compute_csum_data(&udph, sizeof(udph), buf, len);
+ len += sizeof(struct iphdr);
+ if (len > MAX_IP)
+ return -EMSGSIZE;
+ return ip_output(&udph, sizeof(udph), buf, len);
+}