net: working files for start of coding up the protocol stack
authorAlan Cox <alan@linux.intel.com>
Thu, 10 Sep 2015 21:35:33 +0000 (22:35 +0100)
committerAlan Cox <alan@linux.intel.com>
Thu, 10 Sep 2015 21:35:33 +0000 (22:35 +0100)
Don't get too excited yet!

Kernel/include/net.h [new file with mode: 0644]
Kernel/net_icmp.c [new file with mode: 0644]
Kernel/net_ip.c [new file with mode: 0644]
Kernel/net_pkt.c [new file with mode: 0644]
Kernel/net_tcp.c [new file with mode: 0644]
Kernel/net_udp.c [new file with mode: 0644]

diff --git a/Kernel/include/net.h b/Kernel/include/net.h
new file mode 100644 (file)
index 0000000..2ec8457
--- /dev/null
@@ -0,0 +1,226 @@
+#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
diff --git a/Kernel/net_icmp.c b/Kernel/net_icmp.c
new file mode 100644 (file)
index 0000000..4adddc4
--- /dev/null
@@ -0,0 +1,65 @@
+#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);
+}
+
diff --git a/Kernel/net_ip.c b/Kernel/net_ip.c
new file mode 100644 (file)
index 0000000..d89bec3
--- /dev/null
@@ -0,0 +1,94 @@
+#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);
+}
diff --git a/Kernel/net_pkt.c b/Kernel/net_pkt.c
new file mode 100644 (file)
index 0000000..89f97b8
--- /dev/null
@@ -0,0 +1,18 @@
+#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;
+}
diff --git a/Kernel/net_tcp.c b/Kernel/net_tcp.c
new file mode 100644 (file)
index 0000000..efbb0f5
--- /dev/null
@@ -0,0 +1,173 @@
+
+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();
+}
diff --git a/Kernel/net_udp.c b/Kernel/net_udp.c
new file mode 100644 (file)
index 0000000..bea4402
--- /dev/null
@@ -0,0 +1,43 @@
+#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);
+}