From 24d2a27fb59a01f0fb292b52edce31a3ad61ff46 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 10 Sep 2015 22:35:33 +0100 Subject: [PATCH] net: working files for start of coding up the protocol stack Don't get too excited yet! --- Kernel/include/net.h | 226 +++++++++++++++++++++++++++++++++++++++++++ Kernel/net_icmp.c | 65 +++++++++++++ Kernel/net_ip.c | 94 ++++++++++++++++++ Kernel/net_pkt.c | 18 ++++ Kernel/net_tcp.c | 173 +++++++++++++++++++++++++++++++++ Kernel/net_udp.c | 43 ++++++++ 6 files changed, 619 insertions(+) create mode 100644 Kernel/include/net.h create mode 100644 Kernel/net_icmp.c create mode 100644 Kernel/net_ip.c create mode 100644 Kernel/net_pkt.c create mode 100644 Kernel/net_tcp.c create mode 100644 Kernel/net_udp.c diff --git a/Kernel/include/net.h b/Kernel/include/net.h new file mode 100644 index 00000000..2ec84572 --- /dev/null +++ b/Kernel/include/net.h @@ -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 " +#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 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 index 00000000..4adddc42 --- /dev/null +++ b/Kernel/net_icmp.c @@ -0,0 +1,65 @@ +#include +#include + +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 index 00000000..d89bec3c --- /dev/null +++ b/Kernel/net_ip.c @@ -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 index 00000000..89f97b87 --- /dev/null +++ b/Kernel/net_pkt.c @@ -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 index 00000000..efbb0f55 --- /dev/null +++ b/Kernel/net_tcp.c @@ -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 index 00000000..bea4402e --- /dev/null +++ b/Kernel/net_udp.c @@ -0,0 +1,43 @@ +#include +#include + +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); +} -- 2.34.1