simple reliable networking over ethernet. Provides IPv4 ARP, ICMP echo reply, and UDP unicast. Does NOT provide TCP, that's not simple :-).

snet.h

Committer:
altasoul
Date:
2015-04-01
Revision:
4:8c34f9cc4ba1
Parent:
3:59f3c806f127
Child:
5:7ed0abcc02d1

File content as of revision 4:8c34f9cc4ba1:

/*
    The MIT License (MIT)
    
    Copyright (c) 2015 Tom Soulanille
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    
    @file          snet.h 
    @purpose       simple networking
    @version       0.1
    @date          31 March 2015
    @author        Tom Soulanille    
*/

#ifndef _SNET_H_
#define _SNET_H_

#include "mbed.h"

#define ETH_RxOverrunInt     (1 << 0)
#define ETH_RxErrorInt       (1 << 1)
#define ETH_RxFinishedInt    (1 << 2)
#define ETH_RxDoneInt        (1 << 3) 
#define ETH_TxUnderrunInt    (1 << 4)
#define ETH_TxErrorInt       (1 << 5)
#define ETH_TxFinishedInt    (1 << 6)
#define ETH_TxDoneInt        (1 << 7)

#define ETH_RxStatus         (1 << 0)
#define ETH_TxStatus         (1 << 1)

// offsets into an ethernet packet
#define ENET_DMAC_O 0
#define ENET_SMAC_O 6
#define ENET_ETHERTYPE_O 12
#define ENET_PAYLOAD_O 14

// ARP offsets
#define ENET_ARP_HTYPE_O (ENET_PAYLOAD + 0)
#define ENET_ARP_PTYPE_O (ENET_PAYLOAD + 2)
#define ENET_ARP_HLEN_O (ENET_PAYLOAD + 4)
#define ENET_ARP_PLEN_O (ENET_PAYLOAD + 5)
#define ENET_ARP_OPER_O (ENET_PAYLOAD_O + 6)
#define ENET_ARP_SHA_O (ENET_PAYLOAD_O + 8)
#define ENET_ARP_SPA_O (ENET_PAYLOAD_O + 14)
#define ENET_ARP_THA_O (ENET_PAYLOAD_O + 18)
#define ENET_ARP_TPA_O (ENET_PAYLOAD_O + 24)

// IP offsets
#define IP_HEADER_O ENET_PAYLOAD_O
#define IP_VIHL_O (ENET_PAYLOAD_O + 0)
#define IP_DSCP_ECN_O (ENET_PAYLOAD_O + 1)
#define IP_TOTAL_LENGTH_O (ENET_PAYLOAD_O + 2)  // Total Length
#define IP_ID_O (ENET_PAYLOAD_O + 4)
#define IP_FLAGS_FRAGOFF_O (ENET_PAYLOAD_O + 6)
#define IP_TTL_O (ENET_PAYLOAD_O + 8)
#define IP_PROTO_O (ENET_PAYLOAD_O + 9)
#define IP_CHKSUM_O (ENET_PAYLOAD_O + 10)
#define IP_SADDR_O (ENET_PAYLOAD_O + 12)
#define IP_DADDR_O (ENET_PAYLOAD_O + 16)
#define IP_PAYLOAD_O (ENET_PAYLOAD_O + 20)
#define IP_HEADER_LEN (IP_PAYLOAD_O - ENET_PAYLOAD_O)

// ICMP offsets
#define ICMP_HEADER_O IP_PAYLOAD_O
#define ICMP_TYPE_O (ICMP_HEADER_O + 0)
#define ICMP_CODE_O (ICMP_HEADER_O + 1)
#define ICMP_CHECKSUM_O (ICMP_HEADER_O + 2)
#define ICMP_REST_OF_HEADER_O (ICMP_HEADER_O + 4)
#define ICMP_PAYLOAD_O (ICMP_HEADER_O + 8)
#define ICMP_HEADER_LEN (ICMP_PAYLOAD_O - ICMP_HEADER_O)

// UDP offsets
#define UDP_HEADER_O IP_PAYLOAD_O
#define UDP_SRC_PORT_O (UDP_HEADER_O + 0)
#define UDP_DST_PORT_O (UDP_HEADER_O + 2)
#define UDP_LENGTH_O (UDP_HEADER_O + 4)
#define UDP_CHECKSUM_O (UDP_HEADER_O + 6)
#define UDP_PAYLOAD_O (UDP_HEADER_O + 8)
#define UDP_HEADER_LEN (UDP_PAYLOAD_O - UDP_HEADER_O)

/** Simple network class.
 *  Very simple ARP, ICMP echo reply (ping), and UDP
 *
 * @author Tom Soulanille
 *
 * Typically you make a singleton instance of the Snet class, and register
 * a UDP handler function. Here's an example RFC865 server:
 *
 * @code
 * #include "mbed.h"
 * #include "snet.h"
 *
 * DigitalOut led1(LED1);
 * DigitalOut led2(LED2);
 *
 * uint8_t my_ip[] = { 192, 168, 1, 234 };
 * Snet snet(my_ip);
 * unsigned int r = 0;
 *
 * char *fortunes[] = {
 *   "Force has no place where there is need of skill.\n        -- Herodotus\n",
 *   "Getting into trouble is easy.\n        -- D. Winkel and F. Prosser\n",
 *   "If you find a solution and become attached to it, the solution may become\nyour next problem.\n",
 *   "If you fool around with something long enough, it will eventually break.\n",
 *   "Getting there is only half as far as getting there and back.\n"
 * };
 *
 * #define N_FORTUNES (sizeof(fortunes)/sizeof(char *))
 *
 * void handle_udp_packet(uint8_t *buf, int len) {
 *   if (buf[UDP_DST_PORT_O+0] == 0
 *       && buf[UDP_DST_PORT_O+1] == 17) {
 *     char *s = fortunes[r];
 *     snet.return_udp_packet(buf, (uint8_t *) s, strlen(s));
 *     led2 = !led2;
 *     //printf("%s\r\n", s);                                                                                                                    
 *   }
 * }
 *
 * int main() {
 *   //printf("N_FORTUNES = %d\r\n", N_FORTUNES);                                                                                                
 *   snet.registered_udp_handler = &handle_udp_packet;
 *   while(1) {
 *     if (snet.rx_and_process_available_packets() > 0) {
 *       led1 = !led1;
 *       //printf("%d\r\n", snet.enet_rx_cnt);                                                                                                   
 *     }
 *     r = (r + 1) % N_FORTUNES;
 *   }
 * }
 * @endcode
 *
 * Change the `{ 192, 168, 1, 234 }` to an available IP address
 * on your network.
 *
 * You can ping it, e.g. `ping 192.168.1.234` 
 * at the same time as you obtain fortunes from it by e.g.
 * `nc -u 192.168.1.234 17` and hitting returns to request
 * randomly-selected fortunes. You can flood-ping it, with
 * e.g `sudo ping -f 192.168.1.234`, and still get your
 * fortunes.
 */
class Snet {
    private:
        static const uint8_t broadcast_mac[];// = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
        static const uint8_t ipEtherType[];// = { 0x08, 0x00 };
        static const uint8_t arp_req_payload_prefix[];// = { 0x0, 0x1, 0x8, 0x0, 0x6, 0x4, 0x0, 0x1 };
        Ethernet eth;
        
        uint8_t correspondent_facing_packet_header[UDP_PAYLOAD_O];
        
        void got_broadcast(uint8_t *buf, int len);
        void got_unicast(uint8_t *buf, int len);
        void handle_arp_request(uint8_t *buf, int len);
        uint16_t ones_complement_sum(const uint8_t *buf, int len, int sum);
        uint16_t ip_checksum_of(const uint8_t *buf, const int len);
        void turn_ip_packet_around(uint8_t *buf);
        void handle_icmp_packet(uint8_t *buf, int len);
        void interpret_inet_packet(uint8_t *buf, int len);
        void turn_udp_packet_around(uint8_t *buf);
        /**
         * Send a UDP packet addressed as in buf, with specified payload
         */
        void send_udp_packet(uint8_t *buf, const uint8_t *payload, int payload_len);
        void handle_udp_packet(uint8_t *buf, int len);
        
    public:
    
        /** Constructor
         */
        Snet();
        
        /** Constructor
         * @param <my_ip> The IP address to use for me, pointer to array of four bytes
         */
        Snet(const uint8_t *my_ip);

        uint8_t my_mac[6];
        uint8_t my_ip[4];
        volatile uint32_t enet_rx_cnt;
        uint8_t correspondent_mac[6];    // correspondent's MAC
        
        /**
         * Receive and process packets until none available
         */
        int rx_and_process_available_packets();
        
        /** UDP handler function pointer
         */
        void (*registered_udp_handler)(uint8_t *buf, int len);
        
        /**
         * Set the correspondent to whom we will send UDP packets
         */
        void set_udp_correspondent(uint8_t *buf, int len);
        
        /**
         * Send UDP packet to correspondent
         *
         * @param <what> 
         */
        void send_to_correspondent(const uint8_t *what, int what_len);
        
        /**
         * Send string to correspondent
         * @param <s> string to send
         */
        void tell_udp_correspondent(char *s);
        
        /**
         * Send a UDP packet to the sender of this one
         *
         * @param <buf> The UDP packet to which we are replying
         * @param <payload> The UDP payload to use for this packet
         */
        void return_udp_packet(uint8_t *buf, const uint8_t *payload, int payload_len);
};

#endif