From e4deb74f19f74637a8447f7938e6b7911ef0666b Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sat, 5 Jan 2019 21:49:11 +0100 Subject: [PATCH 1/2] Add support for ARP cache * keep MAC/IP relations into an ARP 'store'. * fix issue when IP packet is received from local network: answer is no more broadcasted * define ETHERCARD_ARP_STORE_SIZE to set cache size --- src/EtherCard.h | 42 +++++++ src/arp.cpp | 93 +++++++++++++++ src/net.h | 110 +++++++++++++---- src/tcpip.cpp | 311 ++++++++++++++++++++++++++++++------------------ 4 files changed, 417 insertions(+), 139 deletions(-) create mode 100644 src/arp.cpp diff --git a/src/EtherCard.h b/src/EtherCard.h index ff17ea5..4f2ee6e 100644 --- a/src/EtherCard.h +++ b/src/EtherCard.h @@ -85,6 +85,11 @@ */ #define ETHERCARD_STASH 1 +/** Set ARP cache max entry count */ +#ifndef ETHERCARD_ARP_STORE_SIZE +# define ETHERCARD_ARP_STORE_SIZE 4 +#endif + /** This type definition defines the structure of a UDP server event handler callback function */ typedef void (*UdpServerCallback)( @@ -190,6 +195,16 @@ class EtherCard : public Ethernet { */ static void updateBroadcastAddress(); + /** @brief Request the IP associated hardware address (ARP lookup). Use + * clientWaitIp(ip) to get the ARP lookup status + */ + static void clientResolveIp(const uint8_t *ip); + + /** @brief Check if got IP associated hardware address (ARP lookup) + * @return unit8_t True if gateway found + */ + static uint8_t clientWaitIp(const uint8_t *ip); + /** @brief Check if got gateway hardware address (ARP lookup) * @return unit8_t True if gateway found */ @@ -484,6 +499,33 @@ class EtherCard : public Ethernet { /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); + + /** @brief Check if IP is in ARP store + * @param ip IP to check (size must be IP_LEN) + * @return bool True if IP is in ARP store + */ + static bool arpStoreHasMac(const uint8_t *ip); + + /** @brief convert IP into associated MAC address + * @param ip IP to convert to MAC address (size must be IP_LEN) + * @return uint8_t * mac MAC address or NULL if not found + */ + static const uint8_t *arpStoreGetMac(const uint8_t *ip); + + /** @brief set/refresh new couple IP/MAC addresses into ARP store + * @param ip IP address + * @param mac MAC address + */ + static void arpStoreSet(const uint8_t *ip, const uint8_t *mac); + + /** @brief remove IP from ARP store + * @param ip IP address to remove + */ + static void arpStoreInvalidIp(const uint8_t *ip); + +private: + static void packetLoopIdle(); + static void packetLoopArp(const uint8_t *first, const uint8_t *last); }; extern EtherCard ether; //!< Global presentation of EtherCard class diff --git a/src/arp.cpp b/src/arp.cpp new file mode 100644 index 0000000..e169dfe --- /dev/null +++ b/src/arp.cpp @@ -0,0 +1,93 @@ +#include "EtherCard.h" + +struct ArpEntry +{ + uint8_t ip[IP_LEN]; + uint8_t mac[ETH_LEN]; + uint8_t count; +}; + +static ArpEntry store[ETHERCARD_ARP_STORE_SIZE]; + +static void incArpEntry(ArpEntry &e) +{ + if (e.count < 0xFF) + ++e.count; +} + +// static ArpEntry print_store() +// { +// Serial.println("ARP store: "); +// for (ArpEntry *iter = store, *last = store + ETHERCARD_ARP_STORE_SIZE; +// iter != last; ++iter) +// { +// ArpEntry &e = *iter; +// Serial.print('\t'); +// EtherCard::printIp(e.ip); +// Serial.print(' '); +// for (int i = 0; i < ETH_LEN; ++i) +// { +// if (i > 0) +// Serial.print(':'); +// Serial.print(e.mac[i], HEX); +// } +// Serial.print(' '); +// Serial.println(e.count); +// } +// } + +static ArpEntry *findArpStoreEntry(const uint8_t *ip) +{ + for (ArpEntry *iter = store, *last = store + ETHERCARD_ARP_STORE_SIZE; + iter != last; ++iter) + { + if (memcmp(ip, iter->ip, IP_LEN) == 0) + return iter; + } + return NULL; +} + +bool EtherCard::arpStoreHasMac(const uint8_t *ip) +{ + return findArpStoreEntry(ip) != NULL; +} + +const uint8_t *EtherCard::arpStoreGetMac(const uint8_t *ip) +{ + ArpEntry *e = findArpStoreEntry(ip); + if (e) + return e->mac; + return NULL; +} + +void EtherCard::arpStoreSet(const uint8_t *ip, const uint8_t *mac) +{ + ArpEntry *e = findArpStoreEntry(ip); + if (!e) + { + // find less used entry + e = store; + for (ArpEntry *iter = store + 1, *last = store + ETHERCARD_ARP_STORE_SIZE; + iter != last; ++iter) + { + if (iter->count < e->count) + e = iter; + } + + // and replace it with new ip/mac + copyIp(e->ip, ip); + e->count = 1; + } + else + incArpEntry(*e); + + copyMac(e->mac, mac); + // print_store(); +} + +void EtherCard::arpStoreInvalidIp(const uint8_t *ip) +{ + ArpEntry *e = findArpStoreEntry(ip); + if (e) + memset(e, 0, sizeof(ArpEntry)); +} diff --git a/src/net.h b/src/net.h index 0bbe89a..6b585d8 100755 --- a/src/net.h +++ b/src/net.h @@ -13,6 +13,39 @@ #ifndef NET_H #define NET_H +// macro to swap bytes from 2 bytes value +#define BSWAP_16(x) \ + ( \ + (uint16_t) \ + ( \ + ((x & 0xFF) << 8) \ + | ((x >> 8) & 0xFF) \ + ) \ + ) + +// macro to swap bytes from 4 bytes value +#define BSWAP_32(x) \ + ( \ + (uint32_t) \ + ( \ + ((x & 0xFF) << 24) \ + | ((x & 0xFF00) << 8) \ + | ((x & 0xFF0000) >> 8) \ + | ((x & 0xFF000000) >> 24) \ + ) \ + ) + +#ifndef __BYTE_ORDER__ +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define HTONS(x) BSWAP_16(x) + #define HTONL(x) BSWAP_32(x) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define HTONS(x) x + #define HTONL(x) x +#else +# error __BYTE_ORDER__ not defined! PLease define it for your platform +#endif + // ******* SERVICE PORTS ******* #define HTTP_PORT 80 #define DNS_PORT 53 @@ -22,8 +55,7 @@ #define ETH_HEADER_LEN 14 #define ETH_LEN 6 // values of certain bytes: -#define ETHTYPE_ARP_H_V 0x08 -#define ETHTYPE_ARP_L_V 0x06 +#define ETHTYPE_ARP_V HTONS(0x0806) #define ETHTYPE_IP_H_V 0x08 #define ETHTYPE_IP_L_V 0x00 // byte positions in the ethernet frame: @@ -36,25 +68,14 @@ #define ETH_SRC_MAC 6 -// ******* ARP ******* -#define ETH_ARP_OPCODE_REPLY_H_V 0x0 -#define ETH_ARP_OPCODE_REPLY_L_V 0x02 -#define ETH_ARP_OPCODE_REQ_H_V 0x0 -#define ETH_ARP_OPCODE_REQ_L_V 0x01 -// start of arp header: -#define ETH_ARP_P 0xe -// -#define ETHTYPE_ARP_L_V 0x06 -// arp.dst.ip -#define ETH_ARP_DST_IP_P 0x26 -// arp.opcode -#define ETH_ARP_OPCODE_H_P 0x14 -#define ETH_ARP_OPCODE_L_P 0x15 -// arp.src.mac -#define ETH_ARP_SRC_MAC_P 0x16 -#define ETH_ARP_SRC_IP_P 0x1c -#define ETH_ARP_DST_MAC_P 0x20 -#define ETH_ARP_DST_IP_P 0x26 +// Ethernet II header +struct EthHeader +{ + uint8_t thaddr[ETH_LEN]; // target MAC address + uint8_t shaddr[ETH_LEN]; // source MAC address + uint16_t etype; // Ethertype +}; + // ******* IP ******* #define IP_HEADER_LEN 20 @@ -76,6 +97,53 @@ #define IP_PROTO_TCP_V 6 // 17=0x11 #define IP_PROTO_UDP_V 17 + +struct IpHeader +{ + uint8_t version:4; + uint8_t ihl:4; + uint8_t dscp:6; + uint8_t ecn:2; + uint16_t totalLen; + uint16_t identification; + uint8_t flags:3; + uint16_t fragmentOffset:13; + uint8_t ttl; + uint8_t protocol; + uint16_t hchecksum; + uint8_t spaddr[IP_LEN]; + uint8_t tpaddr[IP_LEN]; +}; + + +// ******* ARP ******* +// ArpHeader.htypeH/L +#define ETH_ARP_HTYPE_ETHERNET HTONS(0x0001) + +// ArpHeader.ptypeH/L +#define ETH_ARP_PTYPE_IPV4 HTONS(0x0800) + +// ArpHeader.opcodeH/L +#define ETH_ARP_OPCODE_REPLY HTONS(0x0002) +#define ETH_ARP_OPCODE_REQ HTONS(0x0001) + +struct ArpHeader +{ + uint16_t htype; // hardware type + uint16_t ptype; // protocol type + uint8_t hlen; // hardware address length + uint8_t plen; // protocol address length + uint16_t opcode; // operation + + // only htype "ethernet" and ptype "IPv4" is supported for the moment, + // so hardcode addresses' lengths + uint8_t shaddr[ETH_LEN]; + uint8_t spaddr[IP_LEN]; + uint8_t thaddr[ETH_LEN]; + uint8_t tpaddr[IP_LEN]; +}; + + // ******* ICMP ******* #define ICMP_TYPE_ECHOREPLY_V 0 #define ICMP_TYPE_ECHOREQUEST_V 8 diff --git a/src/tcpip.cpp b/src/tcpip.cpp index 9c2cf39..bc5f2e2 100644 --- a/src/tcpip.cpp +++ b/src/tcpip.cpp @@ -49,18 +49,6 @@ static const char *client_urlbuf; // Pointer to c-string path part of HTTP reque static const char *client_urlbuf_var; // Pointer to c-string filename part of HTTP request URL static const char *client_hoststr; // Pointer to c-string hostname of current HTTP request static void (*icmp_cb)(uint8_t *ip); // Pointer to callback function for ICMP ECHO response handler (triggers when localhost receives ping response (pong)) -static uint8_t destmacaddr[ETH_LEN]; // storing both dns server and destination mac addresses, but at different times because both are never needed at same time. -static boolean waiting_for_dns_mac = false; //might be better to use bit flags and bitmask operations for these conditions -static boolean has_dns_mac = false; -static boolean waiting_for_dest_mac = false; -static boolean has_dest_mac = false; -static uint8_t gwmacaddr[ETH_LEN]; // Hardware (MAC) address of gateway router -static uint8_t waitgwmac; // Bitwise flags of gateway router status - see below for states -//Define gateway router ARP statuses -#define WGW_INITIAL_ARP 1 // First request, no answer yet -#define WGW_HAVE_GW_MAC 2 // Have gateway router MAC -#define WGW_REFRESHING 4 // Refreshing but already have gateway MAC -#define WGW_ACCEPT_ARP_REPLY 8 // Accept an ARP reply static uint16_t info_data_len; // Length of TCP/IP payload static uint8_t seqnum = 0xa; // My initial tcp sequence number @@ -71,7 +59,6 @@ static unsigned long SEQ; // TCP/IP sequence number #define CLIENTMSS 550 #define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P]>>4)*4) // Get offset of TCP/IP payload data -const unsigned char arpreqhdr[] PROGMEM = { 0,1,8,0,6,4,0,1 }; // ARP request header const unsigned char iphdr[] PROGMEM = { 0x45,0,0,0x82,0,0,0x40,0,0x20 }; //IP header const unsigned char ntpreqhdr[] PROGMEM = { 0xE3,0,4,0xFA,0,1,0,0,0,1 }; //NTP request header extern const uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Used for hardware (MAC) and IP broadcast addresses @@ -94,6 +81,50 @@ static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) gPB[dest+1] = ck; } +static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP_LEN]); + +static void init_eth_header(EthHeader &h, const uint8_t *thaddr) +{ + EtherCard::copyMac(h.thaddr, thaddr); + EtherCard::copyMac(h.shaddr, EtherCard::mymac); +} + +static void init_eth_header( + EthHeader &h, + const uint8_t *thaddr, + const uint16_t etype + ) +{ + init_eth_header(h, thaddr); + h.etype = etype; +} + +// check if ARP request is ongoing +static bool client_arp_waiting(const uint8_t *ip) +{ + const uint8_t *mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip); + return !mac || memcmp(mac, allOnes, ETH_LEN) == 0; +} + +// check if ARP is request is done +static bool client_arp_ready(const uint8_t *ip) +{ + const uint8_t *mac = EtherCard::arpStoreGetMac(ip); + return mac && memcmp(mac, allOnes, ETH_LEN) != 0; +} + +// return +// - IP MAC address if IP is part of LAN +// - gwip MAC address if IP is outside of LAN +// - broadcast MAC address if none are found +static const uint8_t *client_arp_get(const uint8_t *ip) +{ + const uint8_t *mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip); + if (mac) + return mac; + return allOnes; +} + static void setMACs (const uint8_t *mac) { EtherCard::copyMac(gPB + ETH_DST_MAC, mac); EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac); @@ -105,6 +136,10 @@ static void setMACandIPs (const uint8_t *mac, const uint8_t *dst) { EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); } +static void setMACandIPs (const uint8_t *dst) { + setMACandIPs(client_arp_get(dst), dst); +} + static uint8_t check_ip_message_is_from(const uint8_t *ip) { return memcmp(gPB + IP_SRC_P, ip, IP_LEN) == 0; } @@ -120,12 +155,6 @@ static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP return true; } -static uint8_t eth_type_is_arp_and_my_ip(uint16_t len) { - return len >= 41 && gPB[ETH_TYPE_H_P] == ETHTYPE_ARP_H_V && - gPB[ETH_TYPE_L_P] == ETHTYPE_ARP_L_V && - memcmp(gPB + ETH_ARP_DST_IP_P, EtherCard::myip, IP_LEN) == 0; -} - static uint8_t eth_type_is_ip_and_my_ip(uint16_t len) { return len >= 42 && gPB[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && gPB[ETH_TYPE_L_P] == ETHTYPE_IP_L_V && @@ -183,14 +212,25 @@ static void make_tcphead(uint16_t rel_ack_num,uint8_t cp_seq) { } static void make_arp_answer_from_request() { - setMACs(gPB + ETH_SRC_MAC); - gPB[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; - gPB[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; - EtherCard::copyMac(gPB + ETH_ARP_DST_MAC_P, gPB + ETH_ARP_SRC_MAC_P); - EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac); - EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, gPB + ETH_ARP_SRC_IP_P); - EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip); - EtherCard::packetSend(42); + uint8_t *iter = gPB; + EthHeader &eh = *(EthHeader *)(iter); + iter += sizeof(EthHeader); + + ArpHeader &arp = *(ArpHeader *)(iter); + iter += sizeof(ArpHeader); + + // set ethernet layer mac addresses + init_eth_header(eh, arp.shaddr); + + // set ARP answer from the request buffer + arp.opcode = ETH_ARP_OPCODE_REPLY; + EtherCard::copyMac(arp.thaddr, arp.shaddr); + EtherCard::copyMac(arp.shaddr, EtherCard::mymac); + EtherCard::copyIp(arp.tpaddr, arp.spaddr); + EtherCard::copyIp(arp.spaddr, EtherCard::myip); + + // send ethernet frame + EtherCard::packetSend(iter - gPB); // 42 } static void make_echo_reply_from_request(uint16_t len) { @@ -312,11 +352,7 @@ void EtherCard::httpServerReply_with_flags (uint16_t dlen , uint8_t flags) { } void EtherCard::clientIcmpRequest(const uint8_t *destip) { - if(is_lan(EtherCard::myip, destip)) { - setMACandIPs(destmacaddr, destip); - } else { - setMACandIPs(gwmacaddr, destip); - } + setMACandIPs(destip); gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); @@ -337,11 +373,7 @@ void EtherCard::clientIcmpRequest(const uint8_t *destip) { } void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { - if(is_lan(myip, ntpip)) { - setMACandIPs(destmacaddr, ntpip); - } else { - setMACandIPs(gwmacaddr, ntpip); - } + setMACandIPs(ntpip); gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); @@ -374,11 +406,7 @@ uint8_t EtherCard::ntpProcessAnswer (uint32_t *time,uint8_t dstport_l) { } void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) { - if(is_lan(myip, dip)) { // this works because both dns mac and destinations mac are stored in same variable - destmacaddr - setMACandIPs(destmacaddr, dip); // at different times. The program could have separate variable for dns mac, then here should be - } else { // checked if dip is dns ip and separately if dip is hisip and then use correct mac. - setMACandIPs(gwmacaddr, dip); - } + setMACandIPs(dip); // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html // multicast or broadcast address, https://github.com/njh/EtherCard/issues/59 if ((dip[0] & 0xF0) == 0xE0 || *((unsigned long*) dip) == 0xFFFFFFFF || !memcmp(broadcastip,dip,IP_LEN)) @@ -443,43 +471,62 @@ void EtherCard::sendWol (uint8_t *wolmac) { } // make a arp request -static void client_arp_whohas(uint8_t *ip_we_search) { - setMACs(allOnes); - gPB[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; - memcpy_P(gPB + ETH_ARP_P, arpreqhdr, sizeof arpreqhdr); - memset(gPB + ETH_ARP_DST_MAC_P, 0, ETH_LEN); - EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac); - EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, ip_we_search); - EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip); - EtherCard::packetSend(42); +static void client_arp_whohas(const uint8_t *ip_we_search) { + uint8_t *iter = gPB; + EthHeader &eh = *(EthHeader *)(iter); + iter += sizeof(EthHeader); + + ArpHeader &arp = *(ArpHeader *)(iter); + iter += sizeof(ArpHeader); + + // set ethernet layer mac addresses + init_eth_header(eh, allOnes, ETHTYPE_ARP_V); + + arp.htype = ETH_ARP_HTYPE_ETHERNET; + arp.ptype = ETH_ARP_PTYPE_IPV4; + arp.hlen = ETH_LEN; + arp.plen = IP_LEN; + arp.opcode = ETH_ARP_OPCODE_REQ; + EtherCard::copyMac(arp.shaddr, EtherCard::mymac); + EtherCard::copyIp(arp.spaddr, EtherCard::myip); + memset(arp.thaddr, 0, sizeof(arp.thaddr)); + EtherCard::copyIp(arp.tpaddr, ip_we_search); + + // send ethernet frame + EtherCard::packetSend(iter - gPB); + + // mark ip as "waiting" using broadcast mac address + EtherCard::arpStoreSet(ip_we_search, allOnes); } -uint8_t EtherCard::clientWaitingGw () { - return !(waitgwmac & WGW_HAVE_GW_MAC); +static void client_arp_refresh(const uint8_t *ip) +{ + // Check every 65536 (no-packet) cycles whether we need to retry ARP requests + if (is_lan(EtherCard::myip, ip) && (!EtherCard::arpStoreHasMac(ip) || EtherCard::delaycnt == 0)) + client_arp_whohas(ip); } -uint8_t EtherCard::clientWaitingDns () { - if(is_lan(myip, dnsip)) - return !has_dns_mac; - return !(waitgwmac & WGW_HAVE_GW_MAC); +void EtherCard::clientResolveIp(const uint8_t *ip) +{ + client_arp_refresh(ip); } -static uint8_t client_store_mac(uint8_t *source_ip, uint8_t *mac) { - if (memcmp(gPB + ETH_ARP_SRC_IP_P, source_ip, IP_LEN) != 0) - return 0; - EtherCard::copyMac(mac, gPB + ETH_ARP_SRC_MAC_P); - return 1; +uint8_t EtherCard::clientWaitIp(const uint8_t *ip) +{ + return client_arp_waiting(ip); } -// static void client_gw_arp_refresh() { -// if (waitgwmac & WGW_HAVE_GW_MAC) -// waitgwmac |= WGW_REFRESHING; -// } +uint8_t EtherCard::clientWaitingGw () { + return clientWaitIp(gwip); +} + +uint8_t EtherCard::clientWaitingDns () { + return clientWaitIp(dnsip); +} void EtherCard::setGwIp (const uint8_t *gwipaddr) { - delaycnt = 0; //request gateway ARP lookup - waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop + if (memcmp(gwipaddr, gwip, IP_LEN) != 0) + arpStoreInvalidIp(gwip); copyIp(gwip, gwipaddr); } @@ -490,11 +537,7 @@ void EtherCard::updateBroadcastAddress() } static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { - if(is_lan(EtherCard::myip, EtherCard::hisip)) { - setMACandIPs(destmacaddr, EtherCard::hisip); - } else { - setMACandIPs(gwmacaddr, EtherCard::hisip); - } + setMACandIPs(EtherCard::hisip); gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); @@ -670,6 +713,63 @@ uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { return 0; } +void EtherCard::packetLoopArp(const uint8_t *first, const uint8_t *last) +{ + // security: check if received data has expected size, only htype + // "ethernet" and ptype "IPv4" is supported for the moment. + // '<' and not '==' because Ethernet II require padding if ethernet frame + // size is less than 60 bytes includes Ethernet II header + if ((uint8_t)(last - first) < sizeof(ArpHeader)) + return; + + const ArpHeader &arp = *(const ArpHeader *)first; + + // check hardware type is "ethernet" + if (arp.htype != ETH_ARP_HTYPE_ETHERNET) + return; + + // check protocol type is "IPv4" + if (arp.ptype != ETH_ARP_PTYPE_IPV4) + return; + + // security: assert lengths are correct + if (arp.hlen != ETH_LEN || arp.plen != IP_LEN) + return; + + // ignore if not for us + if (memcmp(arp.tpaddr, myip, IP_LEN) != 0) + return; + + // add sender to cache... + arpStoreSet(arp.spaddr, arp.shaddr); + + if (arp.opcode == ETH_ARP_OPCODE_REQ) + { + // ...and answer to sender + make_arp_answer_from_request(); + } +} + +void EtherCard::packetLoopIdle() +{ + if (isLinkUp()) + { + client_arp_refresh(gwip); + client_arp_refresh(dnsip); + client_arp_refresh(hisip); + } + delaycnt++; + +#if ETHERCARD_TCPCLIENT + //Initiate TCP/IP session if pending + if (tcp_client_state==TCP_STATE_SENDSYN && client_arp_ready(gwip)) { // send a syn + tcp_client_state = TCP_STATE_SYNSENT; + tcpclient_src_port_l++; // allocate a new port + client_syn(((tcp_fd<<5) | (0x1f & tcpclient_src_port_l)),tcp_client_port_h,tcp_client_port_l); + } +#endif +} + uint16_t EtherCard::packetLoop (uint16_t plen) { uint16_t len; @@ -679,53 +779,20 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { } #endif - if (plen==0) { - //Check every 65536 (no-packet) cycles whether we need to retry ARP request for gateway - if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && - delaycnt==0 && isLinkUp()) { - client_arp_whohas(gwip); - waitgwmac |= WGW_ACCEPT_ARP_REPLY; - } - delaycnt++; - -#if ETHERCARD_TCPCLIENT - //Initiate TCP/IP session if pending - if (tcp_client_state==TCP_STATE_SENDSYN && (waitgwmac & WGW_HAVE_GW_MAC)) { // send a syn - tcp_client_state = TCP_STATE_SYNSENT; - tcpclient_src_port_l++; // allocate a new port - client_syn(((tcp_fd<<5) | (0x1f & tcpclient_src_port_l)),tcp_client_port_h,tcp_client_port_l); - } -#endif - - //!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed. - if(is_lan(myip, dnsip) && !has_dns_mac && !waiting_for_dns_mac) { - client_arp_whohas(dnsip); - waiting_for_dns_mac = true; - } - - //!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed. - if(is_lan(myip, hisip) && !has_dest_mac && !waiting_for_dest_mac) { - client_arp_whohas(hisip); - waiting_for_dest_mac = true; - } - + if (plen < sizeof(EthHeader)) { + packetLoopIdle(); return 0; } - if (eth_type_is_arp_and_my_ip(plen)) - { //Service ARP request - if (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V) - make_arp_answer_from_request(); - if (waitgwmac & WGW_ACCEPT_ARP_REPLY && (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V) && client_store_mac(gwip, gwmacaddr)) - waitgwmac = WGW_HAVE_GW_MAC; - if (!has_dns_mac && waiting_for_dns_mac && client_store_mac(dnsip, destmacaddr)) { - has_dns_mac = true; - waiting_for_dns_mac = false; - } - if (!has_dest_mac && waiting_for_dest_mac && client_store_mac(hisip, destmacaddr)) { - has_dest_mac = true; - waiting_for_dest_mac = false; - } + const uint8_t *iter = gPB; + const uint8_t *last = gPB + plen; + const EthHeader &eh = *(const EthHeader *)(iter); + iter += sizeof(EthHeader); + + // arp payload + if (eh.etype == ETHTYPE_ARP_V) + { + packetLoopArp(iter, last); return 0; } @@ -735,6 +802,14 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { return 0; } + // refresh arp store + if (memcmp(eh.thaddr, mymac, ETH_LEN) == 0) + { + const IpHeader &ip = *(const IpHeader *)(iter); + iter += sizeof(IpHeader); + arpStoreSet(is_lan(myip, ip.spaddr) ? ip.spaddr : gwip, eh.shaddr); + } + #if ETHERCARD_ICMP if (gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) { //Service ICMP echo request (ping) From b6c47e1ae0061893242b47d0dbeda911ad7d6ee2 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 30 Jan 2019 21:51:16 +0100 Subject: [PATCH 2/2] [IP] use IP header instead of array offsets * less error prone * decrease code size to flash * introduce ethernet_header(), ethernet_payload(), ip_header(), ip_payload().... basic functions to retrieve headers and payloads * use ntoh and hton instead of manual multibytes adaptations. getBigEndianLong has been replaced by standard function ntohl. --- src/EtherCard.h | 5 +- src/net.h | 80 +++++++--- src/tcpip.cpp | 363 +++++++++++++++++++++++++--------------------- src/udpserver.cpp | 4 +- 4 files changed, 261 insertions(+), 191 deletions(-) diff --git a/src/EtherCard.h b/src/EtherCard.h index 4f2ee6e..d6373cf 100644 --- a/src/EtherCard.h +++ b/src/EtherCard.h @@ -105,6 +105,7 @@ typedef void (*DhcpOptionCallback)( const byte* data, ///< DHCP option data uint8_t len); ///< Length of the DHCP option data +typedef void (*IcmpCallback)(const uint8_t *src_ip); /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. @@ -298,7 +299,7 @@ class EtherCard : public Ethernet { /** @brief Resister the function to handle ping events * @param cb Pointer to function */ - static void registerPingCallback (void (*cb)(uint8_t*)); + static void registerPingCallback (const IcmpCallback cb); /** @brief Send ping * @param destip Pointer to 4 byte destination IP address @@ -357,7 +358,7 @@ class EtherCard : public Ethernet { * @param len Not used * @return bool True if packet processed */ - static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop + static bool udpServerHasProcessedPacket(const IpHeader &ip, const uint8_t *iter, const uint8_t *last); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state diff --git a/src/net.h b/src/net.h index 6b585d8..19bc435 100755 --- a/src/net.h +++ b/src/net.h @@ -39,13 +39,29 @@ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define HTONS(x) BSWAP_16(x) #define HTONL(x) BSWAP_32(x) + #define NTOHS(x) BSWAP_16(x) + #define NTOHL(x) BSWAP_32(x) #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define HTONS(x) x #define HTONL(x) x + #define NTOHS(x) x + #define NTOHL(x) x #else # error __BYTE_ORDER__ not defined! PLease define it for your platform #endif +inline uint16_t htons(const uint16_t v) +{ return HTONS(v); } + +inline uint32_t htonl(const uint32_t v) +{ return HTONL(v); } + +inline uint16_t ntohs(const uint16_t v) +{ return NTOHS(v); } + +inline uint32_t ntohl(const uint32_t v) +{ return NTOHL(v); } + // ******* SERVICE PORTS ******* #define HTTP_PORT 80 #define DNS_PORT 53 @@ -56,8 +72,7 @@ #define ETH_LEN 6 // values of certain bytes: #define ETHTYPE_ARP_V HTONS(0x0806) -#define ETHTYPE_IP_H_V 0x08 -#define ETHTYPE_IP_L_V 0x00 +#define ETHTYPE_IP_V HTONS(0x0800) // byte positions in the ethernet frame: // // Ethernet type field (2bytes): @@ -78,20 +93,15 @@ struct EthHeader // ******* IP ******* -#define IP_HEADER_LEN 20 #define IP_LEN 4 -// ip.src -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_HEADER_LEN_VER_P 0xe -#define IP_CHECKSUM_P 0x18 -#define IP_TTL_P 0x16 -#define IP_FLAGS_P 0x14 -#define IP_P 0xe -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 - -#define IP_PROTO_P 0x17 +#define IP_V4 0x4 +#define IP_IHL 0x5 + +enum IpFlags +{ + IP_DF = 1 << 1, + IP_MF = 1 << 0, +}; #define IP_PROTO_ICMP_V 1 #define IP_PROTO_TCP_V 6 @@ -100,20 +110,46 @@ struct EthHeader struct IpHeader { - uint8_t version:4; - uint8_t ihl:4; - uint8_t dscp:6; - uint8_t ecn:2; + uint8_t versionIhl; + uint8_t dscpEcn; uint16_t totalLen; uint16_t identification; - uint8_t flags:3; - uint16_t fragmentOffset:13; + uint16_t flagsFragmentOffset; uint8_t ttl; uint8_t protocol; uint16_t hchecksum; uint8_t spaddr[IP_LEN]; uint8_t tpaddr[IP_LEN]; -}; + + static uint8_t version_mask() { return 0xF0; } + uint8_t version() const { return (versionIhl & version_mask()) >> 4; } + void version(const uint8_t v) + { + versionIhl &= ~version_mask(); + versionIhl |= (v << 4) & version_mask(); + } + + uint8_t ihl() const { return versionIhl & 0xF; } + void ihl(const uint8_t i) + { + versionIhl &= version_mask(); + versionIhl |= i & ~version_mask(); + } + + static uint16_t flags_mask() { return HTONS(0xE000); } + + void flags(const uint16_t f) + { + flagsFragmentOffset &= ~flags_mask(); + flagsFragmentOffset |= HTONS(f << 13) & flags_mask(); + } + void fragmentOffset(const uint16_t o) + { + flagsFragmentOffset &= flags_mask(); + flagsFragmentOffset |= HTONS(o) & ~flags_mask(); + } + +} __attribute__((__packed__)); // ******* ARP ******* diff --git a/src/tcpip.cpp b/src/tcpip.cpp index bc5f2e2..ab9136d 100644 --- a/src/tcpip.cpp +++ b/src/tcpip.cpp @@ -48,7 +48,7 @@ static const char *client_postval; static const char *client_urlbuf; // Pointer to c-string path part of HTTP request URL static const char *client_urlbuf_var; // Pointer to c-string filename part of HTTP request URL static const char *client_hoststr; // Pointer to c-string hostname of current HTTP request -static void (*icmp_cb)(uint8_t *ip); // Pointer to callback function for ICMP ECHO response handler (triggers when localhost receives ping response (pong)) +static IcmpCallback icmp_cb; // Pointer to callback function for ICMP ECHO response handler (triggers when localhost receives ping response (pong)) static uint16_t info_data_len; // Length of TCP/IP payload static uint8_t seqnum = 0xa; // My initial tcp sequence number @@ -59,12 +59,59 @@ static unsigned long SEQ; // TCP/IP sequence number #define CLIENTMSS 550 #define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P]>>4)*4) // Get offset of TCP/IP payload data -const unsigned char iphdr[] PROGMEM = { 0x45,0,0,0x82,0,0,0x40,0,0x20 }; //IP header const unsigned char ntpreqhdr[] PROGMEM = { 0xE3,0,4,0xFA,0,1,0,0,0,1 }; //NTP request header extern const uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Used for hardware (MAC) and IP broadcast addresses -static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) { - const uint8_t* ptr = gPB + off; +// static void log_data(const char *message, const uint8_t *ptr, uint16_t len) +// { +// Serial.print(message); +// Serial.println(':'); +// for (const uint8_t *last = ptr + len; ptr != last; ++ptr) +// { +// Serial.print(*ptr, HEX); +// Serial.print(' '); +// } +// Serial.println(); +// } + +static inline EthHeader ðernet_header() +{ + uint8_t *iter = gPB; // avoid strict aliasing warning + return *(EthHeader *)iter; +} + +static inline uint8_t *ethernet_payload() +{ + return (uint8_t *)ðernet_header() + sizeof(EthHeader); +} + +static inline ArpHeader &arp_header() +{ + return *(ArpHeader *)ethernet_payload(); +} + +static inline IpHeader &ip_header() +{ + return *(IpHeader *)ethernet_payload(); +} + +static inline uint8_t *ip_payload() +{ + return (uint8_t *)&ip_header() + sizeof(IpHeader); +} + +static inline uint8_t *tcp_header() +{ + return (uint8_t *)ip_payload(); +} + +static inline uint8_t *udp_header() +{ + return (uint8_t *)ip_payload(); +} + + +static void fill_checksum(uint16_t &checksum, const uint8_t *ptr, uint16_t len, uint8_t type) { uint32_t sum = type==1 ? IP_PROTO_UDP_V+len-8 : type==2 ? IP_PROTO_TCP_V+len-8 : 0; while(len >1) { @@ -77,26 +124,32 @@ static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) while (sum>>16) sum = (uint16_t) sum + (sum >> 16); uint16_t ck = ~ (uint16_t) sum; - gPB[dest] = ck>>8; - gPB[dest+1] = ck; + checksum = HTONS(ck); +} + +static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len, uint8_t type) { + uint8_t *iter = gPB; + const uint8_t* ptr = iter + off; + uint16_t &checksum = *(uint16_t *)(iter + dest); + fill_checksum(checksum, ptr, len, type); } static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP_LEN]); -static void init_eth_header(EthHeader &h, const uint8_t *thaddr) +static void init_eth_header(const uint8_t *thaddr) { + EthHeader &h = ethernet_header(); EtherCard::copyMac(h.thaddr, thaddr); EtherCard::copyMac(h.shaddr, EtherCard::mymac); } static void init_eth_header( - EthHeader &h, const uint8_t *thaddr, const uint16_t etype ) { - init_eth_header(h, thaddr); - h.etype = etype; + init_eth_header(thaddr); + ethernet_header().etype = etype; } // check if ARP request is ongoing @@ -119,29 +172,34 @@ static bool client_arp_ready(const uint8_t *ip) // - broadcast MAC address if none are found static const uint8_t *client_arp_get(const uint8_t *ip) { - const uint8_t *mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip); - if (mac) - return mac; - return allOnes; -} - -static void setMACs (const uint8_t *mac) { - EtherCard::copyMac(gPB + ETH_DST_MAC, mac); - EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac); -} + // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html + // multicast or broadcast address, https://github.com/njh/EtherCard/issues/59 + const uint8_t *mac; + if ( + (ip[0] & 0xF0) == 0xE0 + || *((uint32_t *) ip) == 0xFFFFFFFF + || !memcmp(EtherCard::broadcastip, ip, IP_LEN) + || (mac = EtherCard::arpStoreGetMac(is_lan(EtherCard::myip, ip) ? ip : EtherCard::gwip)) == NULL + ) + return allOnes; -static void setMACandIPs (const uint8_t *mac, const uint8_t *dst) { - setMACs(mac); - EtherCard::copyIp(gPB + IP_DST_P, dst); - EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); + return mac; } -static void setMACandIPs (const uint8_t *dst) { - setMACandIPs(client_arp_get(dst), dst); +static void init_ip_header( + const uint8_t *dst + ) +{ + IpHeader &iph = ip_header(); + EtherCard::copyIp(iph.tpaddr, dst); + EtherCard::copyIp(iph.spaddr, EtherCard::myip); + iph.flags(IP_DF); + iph.fragmentOffset(0); + iph.ttl = 64; } -static uint8_t check_ip_message_is_from(const uint8_t *ip) { - return memcmp(gPB + IP_SRC_P, ip, IP_LEN) == 0; +static uint8_t check_ip_message_is_from(const IpHeader &iph, const uint8_t *ip) { + return memcmp(iph.spaddr, ip, IP_LEN) == 0; } static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP_LEN]) { @@ -155,30 +213,28 @@ static boolean is_lan(const uint8_t source[IP_LEN], const uint8_t destination[IP return true; } -static uint8_t eth_type_is_ip_and_my_ip(uint16_t len) { - return len >= 42 && gPB[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && - gPB[ETH_TYPE_L_P] == ETHTYPE_IP_L_V && - gPB[IP_HEADER_LEN_VER_P] == 0x45 && - (memcmp(gPB + IP_DST_P, EtherCard::myip, IP_LEN) == 0 //not my IP - || (memcmp(gPB + IP_DST_P, EtherCard::broadcastip, IP_LEN) == 0) //not subnet broadcast - || (memcmp(gPB + IP_DST_P, allOnes, IP_LEN) == 0)); //not global broadcasts +static uint8_t is_my_ip(const IpHeader &iph) { + return iph.version() == IP_V4 && iph.ihl() == IP_IHL && + (memcmp(iph.tpaddr, EtherCard::myip, IP_LEN) == 0 //not my IP + || (memcmp(iph.tpaddr, EtherCard::broadcastip, IP_LEN) == 0) //not subnet broadcast + || (memcmp(iph.tpaddr, allOnes, IP_LEN) == 0)); //not global broadcasts //!@todo Handle multicast } -static void fill_ip_hdr_checksum() { - gPB[IP_CHECKSUM_P] = 0; - gPB[IP_CHECKSUM_P+1] = 0; - gPB[IP_FLAGS_P] = 0x40; // don't fragment - gPB[IP_FLAGS_P+1] = 0; // fragment offset - gPB[IP_TTL_P] = 64; // ttl - fill_checksum(IP_CHECKSUM_P, IP_P, IP_HEADER_LEN,0); +static void fill_ip_hdr_checksum(IpHeader &iph) { + iph.hchecksum = 0; + fill_checksum(iph.hchecksum, (const uint8_t *)&iph, sizeof(IpHeader), 0); } -static void make_eth_ip() { - setMACs(gPB + ETH_SRC_MAC); - EtherCard::copyIp(gPB + IP_DST_P, gPB + IP_SRC_P); - EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); - fill_ip_hdr_checksum(); +static void make_eth_ip_reply(const uint16_t payloadlen = 0xFFFF) { + init_eth_header(ethernet_header().shaddr); + init_ip_header(ip_header().spaddr); + + IpHeader &iph = ip_header(); + if (payloadlen != 0xFFFF) + iph.totalLen = htons(ip_payload() - (uint8_t *)&iph + payloadlen); + + fill_ip_hdr_checksum(iph); } static void step_seq(uint16_t rel_ack_num,uint8_t cp_seq) { @@ -212,17 +268,11 @@ static void make_tcphead(uint16_t rel_ack_num,uint8_t cp_seq) { } static void make_arp_answer_from_request() { - uint8_t *iter = gPB; - EthHeader &eh = *(EthHeader *)(iter); - iter += sizeof(EthHeader); - - ArpHeader &arp = *(ArpHeader *)(iter); - iter += sizeof(ArpHeader); - // set ethernet layer mac addresses - init_eth_header(eh, arp.shaddr); + init_eth_header(arp_header().shaddr); // set ARP answer from the request buffer + ArpHeader &arp = arp_header(); arp.opcode = ETH_ARP_OPCODE_REPLY; EtherCard::copyMac(arp.thaddr, arp.shaddr); EtherCard::copyMac(arp.shaddr, EtherCard::mymac); @@ -230,11 +280,11 @@ static void make_arp_answer_from_request() { EtherCard::copyIp(arp.spaddr, EtherCard::myip); // send ethernet frame - EtherCard::packetSend(iter - gPB); // 42 + EtherCard::packetSend((uint8_t *)&arp + sizeof(ArpHeader) - gPB); // 42 } static void make_echo_reply_from_request(uint16_t len) { - make_eth_ip(); + make_eth_ip_reply(); gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; if (gPB[ICMP_CHECKSUM_P] > (0xFF-0x08)) gPB[ICMP_CHECKSUM_P+1]++; @@ -245,9 +295,9 @@ static void make_echo_reply_from_request(uint16_t len) { void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) { if (datalen>220) datalen = 220; - gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >>8; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - make_eth_ip(); + + make_eth_ip_reply(UDP_HEADER_LEN + datalen); + IpHeader &iph = ip_header(); gPB[UDP_DST_PORT_H_P] = gPB[UDP_SRC_PORT_H_P]; gPB[UDP_DST_PORT_L_P] = gPB[UDP_SRC_PORT_L_P]; gPB[UDP_SRC_PORT_H_P] = port>>8; @@ -257,14 +307,12 @@ void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) { gPB[UDP_CHECKSUM_H_P] = 0; gPB[UDP_CHECKSUM_L_P] = 0; memcpy(gPB + UDP_DATA_P, data, datalen); - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); - packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + datalen,1); + packetSend(udp_header() - gPB + UDP_HEADER_LEN + datalen); } static void make_tcp_synack_from_syn() { - gPB[IP_TOTLEN_H_P] = 0; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; - make_eth_ip(); + make_eth_ip_reply(TCP_HEADER_LEN_PLAIN+4); gPB[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V; make_tcphead(1,0); gPB[TCP_SEQ_H_P+0] = 0; @@ -279,13 +327,12 @@ static void make_tcp_synack_from_syn() { gPB[TCP_HEADER_LEN_P] = 0x60; gPB[TCP_WIN_SIZE] = 0x5; // 1400=0x578 gPB[TCP_WIN_SIZE+1] = 0x78; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+4,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN+4,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN+4); } uint16_t EtherCard::getTcpPayloadLength() { - int16_t i = (((int16_t)gPB[IP_TOTLEN_H_P])<<8)|gPB[IP_TOTLEN_L_P]; - i -= IP_HEADER_LEN; + int16_t i = ntohs(ip_header().totalLen) - sizeof(IpHeader); i -= (gPB[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; if (i<=0) i = 0; @@ -297,25 +344,21 @@ static void make_tcp_ack_from_any(int16_t datlentoack,uint8_t addflags) { if (addflags!=TCP_FLAGS_RST_V && datlentoack==0) datlentoack = 1; make_tcphead(datlentoack,1); // no options - uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; - gPB[IP_TOTLEN_H_P] = j>>8; - gPB[IP_TOTLEN_L_P] = j; - make_eth_ip(); + make_eth_ip_reply(TCP_HEADER_LEN_PLAIN); gPB[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 1280=0x500 2048=0x800 768=0x300 gPB[TCP_WIN_SIZE+1] = 0; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN); } static void make_tcp_ack_with_data_noflags(uint16_t dlen) { - uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - gPB[IP_TOTLEN_H_P] = j>>8; - gPB[IP_TOTLEN_L_P] = j; - fill_ip_hdr_checksum(); + IpHeader &iph = ip_header(); + iph.totalLen = htons(ip_payload() - (uint8_t *)&ip_header() + TCP_HEADER_LEN_PLAIN + dlen); + fill_ip_hdr_checksum(iph); gPB[TCP_CHECKSUM_H_P] = 0; gPB[TCP_CHECKSUM_L_P] = 0; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+dlen,2); - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&ip_header().spaddr - gPB, 8+TCP_HEADER_LEN_PLAIN+dlen,2); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN + dlen); } void EtherCard::httpServerReply (uint16_t dlen) { @@ -324,10 +367,6 @@ void EtherCard::httpServerReply (uint16_t dlen) { make_tcp_ack_with_data_noflags(dlen); // send data } -static uint32_t getBigEndianLong(byte offs) { //get the sequence number of packets after an ack from GET - return (((unsigned long)gPB[offs]*256+gPB[offs+1])*256+gPB[offs+2])*256+gPB[offs+3]; -} //thanks to mstuetz for the missing (unsigned long) - static void setSequenceNumber(uint32_t seq) { gPB[TCP_SEQ_H_P] = (seq & 0xff000000 ) >> 24; gPB[TCP_SEQ_H_P+1] = (seq & 0xff0000 ) >> 16; @@ -336,11 +375,11 @@ static void setSequenceNumber(uint32_t seq) { } uint32_t EtherCard::getSequenceNumber() { - return getBigEndianLong(TCP_SEQ_H_P); + return ntohl(*(uint32_t *)(gPB + TCP_SEQ_H_P)); } void EtherCard::httpServerReplyAck () { - make_tcp_ack_from_any(getTcpPayloadLength(),0); // send ack for http request + make_tcp_ack_from_any(getTcpPayloadLength(), 0); // send ack for http request SEQ = getSequenceNumber(); //get the sequence number of packets after an ack from GET } @@ -351,14 +390,27 @@ void EtherCard::httpServerReply_with_flags (uint16_t dlen , uint8_t flags) { SEQ=SEQ+dlen; } +// initialize ethernet frame and IP header +static IpHeader &init_ip_frame( + const uint8_t *destip, + const uint8_t protocol + ) +{ + init_eth_header(client_arp_get(destip), ETHTYPE_IP_V); + init_ip_header(destip); + IpHeader &iph = ip_header(); + iph.version(IP_V4); + iph.ihl(IP_IHL); + iph.dscpEcn = 0; + iph.identification = 0x0; + iph.protocol = protocol; + return iph; +} + void EtherCard::clientIcmpRequest(const uint8_t *destip) { - setMACandIPs(destip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 0x54; - gPB[IP_PROTO_P] = IP_PROTO_ICMP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(destip, IP_PROTO_ICMP_V); + iph.totalLen = HTONS(0x54); + fill_ip_hdr_checksum(iph); gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; gPB[ICMP_TYPE_P+1] = 0; // code gPB[ICMP_CHECKSUM_H_P] = 0; @@ -372,14 +424,10 @@ void EtherCard::clientIcmpRequest(const uint8_t *destip) { packetSend(98); } -void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { - setMACandIPs(ntpip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 0x4c; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; - fill_ip_hdr_checksum(); +void EtherCard::ntpRequest (uint8_t *ntpip, uint8_t srcport) { + IpHeader &iph = init_ip_frame(ntpip, IP_PROTO_UDP_V); + iph.totalLen = HTONS(0x4c); + fill_ip_hdr_checksum(iph); gPB[UDP_DST_PORT_H_P] = 0; gPB[UDP_DST_PORT_L_P] = NTP_PORT; // ntp = 123 gPB[UDP_SRC_PORT_H_P] = 10; @@ -390,7 +438,7 @@ void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { gPB[UDP_CHECKSUM_L_P] = 0; memset(gPB + UDP_DATA_P, 0, 48); memcpy_P(gPB + UDP_DATA_P,ntpreqhdr,10); - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 48,1); + fill_checksum(UDP_CHECKSUM_H_P, iph.spaddr - gPB, 16 + 48, 1); packetSend(90); } @@ -406,16 +454,7 @@ uint8_t EtherCard::ntpProcessAnswer (uint32_t *time,uint8_t dstport_l) { } void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) { - setMACandIPs(dip); - // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html - // multicast or broadcast address, https://github.com/njh/EtherCard/issues/59 - if ((dip[0] & 0xF0) == 0xE0 || *((unsigned long*) dip) == 0xFFFFFFFF || !memcmp(broadcastip,dip,IP_LEN)) - EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_H_P] = 0; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; + init_ip_frame(dip, IP_PROTO_UDP_V); gPB[UDP_DST_PORT_H_P] = (dport>>8); gPB[UDP_DST_PORT_L_P] = dport; gPB[UDP_SRC_PORT_H_P] = (sport>>8); @@ -426,13 +465,13 @@ void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) } void EtherCard::udpTransmit (uint16_t datalen) { - gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >> 8; - gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - fill_ip_hdr_checksum(); + IpHeader &iph = ip_header(); + iph.totalLen = htons(udp_header() - (uint8_t *)&ip_header() + UDP_HEADER_LEN + datalen); + fill_ip_hdr_checksum(iph); gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) >>8; gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen; - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); - packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + datalen,1); + packetSend(udp_header() - gPB + UDP_HEADER_LEN + datalen); } void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport, @@ -445,13 +484,9 @@ void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport, } void EtherCard::sendWol (uint8_t *wolmac) { - setMACandIPs(allOnes, allOnes); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,9); - gPB[IP_TOTLEN_L_P] = 0x82; - gPB[IP_PROTO_P] = IP_PROTO_UDP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(allOnes, IP_PROTO_UDP_V); + iph.totalLen = HTONS(0x82); + fill_ip_hdr_checksum(iph); gPB[UDP_DST_PORT_H_P] = 0; gPB[UDP_DST_PORT_L_P] = 0x9; // wol = normally 9 gPB[UDP_SRC_PORT_H_P] = 10; @@ -466,22 +501,16 @@ void EtherCard::sendWol (uint8_t *wolmac) { pos += 6; copyMac(gPB + pos, wolmac); } - fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 102,1); + fill_checksum(UDP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 16 + 102,1); packetSend(pos + 6); } // make a arp request static void client_arp_whohas(const uint8_t *ip_we_search) { - uint8_t *iter = gPB; - EthHeader &eh = *(EthHeader *)(iter); - iter += sizeof(EthHeader); - - ArpHeader &arp = *(ArpHeader *)(iter); - iter += sizeof(ArpHeader); - // set ethernet layer mac addresses - init_eth_header(eh, allOnes, ETHTYPE_ARP_V); + init_eth_header(allOnes, ETHTYPE_ARP_V); + ArpHeader &arp = arp_header(); arp.htype = ETH_ARP_HTYPE_ETHERNET; arp.ptype = ETH_ARP_PTYPE_IPV4; arp.hlen = ETH_LEN; @@ -493,7 +522,7 @@ static void client_arp_whohas(const uint8_t *ip_we_search) { EtherCard::copyIp(arp.tpaddr, ip_we_search); // send ethernet frame - EtherCard::packetSend(iter - gPB); + EtherCard::packetSend((uint8_t *)&arp - gPB + sizeof(ArpHeader)); // mark ip as "waiting" using broadcast mac address EtherCard::arpStoreSet(ip_we_search, allOnes); @@ -537,13 +566,9 @@ void EtherCard::updateBroadcastAddress() } static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { - setMACandIPs(EtherCard::hisip); - gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - memcpy_P(gPB + IP_P,iphdr,sizeof iphdr); - gPB[IP_TOTLEN_L_P] = 44; // good for syn - gPB[IP_PROTO_P] = IP_PROTO_TCP_V; - fill_ip_hdr_checksum(); + IpHeader &iph = init_ip_frame(EtherCard::hisip, IP_PROTO_TCP_V); + iph.totalLen = HTONS(44); // good for syn + fill_ip_hdr_checksum(iph); gPB[TCP_DST_PORT_H_P] = dstport_h; gPB[TCP_DST_PORT_L_P] = dstport_l; gPB[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H; @@ -563,9 +588,9 @@ static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { gPB[TCP_OPTIONS_P+1] = 4; gPB[TCP_OPTIONS_P+2] = (CLIENTMSS>>8); gPB[TCP_OPTIONS_P+3] = (uint8_t) CLIENTMSS; - fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8 +TCP_HEADER_LEN_PLAIN+4,2); + fill_checksum(TCP_CHECKSUM_H_P, (uint8_t *)&iph.spaddr - gPB, 8 + TCP_HEADER_LEN_PLAIN + 4, 2); // 4 is the tcp mss option: - EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4); + EtherCard::packetSend(tcp_header() - gPB + TCP_HEADER_LEN_PLAIN + 4); } uint8_t EtherCard::clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), @@ -642,7 +667,7 @@ void EtherCard::httpPost (const char *urlbuf, const char *hoststr, const char *a www_fd = clientTcpReq(&www_client_internal_result_cb,&www_client_internal_datafill_cb,hisport); } -static uint16_t tcp_datafill_cb(uint8_t fd) { +static uint16_t tcp_datafill_cb(uint8_t /* fd */) { uint16_t len = Stash::length(); Stash::extract(0, len, EtherCard::tcpOffset()); Stash::cleanup(); @@ -656,11 +681,10 @@ static uint16_t tcp_datafill_cb(uint8_t fd) { return len; } -static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t datalen) { +static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t /* datalen */) { if (status == 0) { result_fd = fd; // a valid result has been received, remember its session id result_ptr = (char*) ether.buffer + datapos; - // result_ptr[datalen] = 0; } return 1; } @@ -677,15 +701,16 @@ const char* EtherCard::tcpReply (uint8_t fd) { return result_ptr; } -void EtherCard::registerPingCallback (void (*callback)(uint8_t *srcip)) { +void EtherCard::registerPingCallback (const IcmpCallback callback) { icmp_cb = callback; } uint8_t EtherCard::packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost) { - return gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && + const IpHeader &iph = ip_header(); + return iph.protocol == IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V && gPB[ICMP_DATA_P]== PINGPATTERN && - check_ip_message_is_from(ip_monitoredhost); + check_ip_message_is_from(iph, ip_monitoredhost); } uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { @@ -707,7 +732,7 @@ uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { return pos; } else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) - make_tcp_ack_from_any(0,0); //No data so close connection + make_tcp_ack_from_any(0, 0); //No data so close connection } } return 0; @@ -786,9 +811,11 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { const uint8_t *iter = gPB; const uint8_t *last = gPB + plen; - const EthHeader &eh = *(const EthHeader *)(iter); + const EthHeader &eh = ethernet_header(); iter += sizeof(EthHeader); + // log_data("packetLoop", gPB, plen); + // arp payload if (eh.etype == ETHTYPE_ARP_V) { @@ -796,44 +823,50 @@ uint16_t EtherCard::packetLoop (uint16_t plen) { return 0; } - if (eth_type_is_ip_and_my_ip(plen)==0) + if (eh.etype != ETHTYPE_IP_V) { //Not IP so ignoring - //!@todo Add other protocols (and make each optional at compile time) return 0; } + if ((uint8_t)(last - iter) < sizeof(IpHeader)) + { // not enough data for IP packet + return 0; + } + + const IpHeader &iph = ip_header(); + iter += sizeof(IpHeader); + + if (is_my_ip(iph)==0) + return 0; + // refresh arp store if (memcmp(eh.thaddr, mymac, ETH_LEN) == 0) - { - const IpHeader &ip = *(const IpHeader *)(iter); - iter += sizeof(IpHeader); - arpStoreSet(is_lan(myip, ip.spaddr) ? ip.spaddr : gwip, eh.shaddr); - } + arpStoreSet(is_lan(myip, iph.spaddr) ? iph.spaddr : gwip, eh.shaddr); #if ETHERCARD_ICMP - if (gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) + if (iph.protocol == IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) { //Service ICMP echo request (ping) if (icmp_cb) - (*icmp_cb)(&(gPB[IP_SRC_P])); + (*icmp_cb)(iph.spaddr); make_echo_reply_from_request(plen); return 0; } #endif #if ETHERCARD_UDPSERVER - if (ether.udpServerListening() && gPB[IP_PROTO_P]==IP_PROTO_UDP_V) + if (ether.udpServerListening() && iph.protocol ==IP_PROTO_UDP_V) { //Call UDP server handler (callback) if one is defined for this packet - if(ether.udpServerHasProcessedPacket(plen)) + if(ether.udpServerHasProcessedPacket(iph, iter, last)) return 0; //An UDP server handler (callback) has processed this packet } #endif - if (plen<54 || gPB[IP_PROTO_P]!=IP_PROTO_TCP_V ) + if (plen<54 || iph.protocol != IP_PROTO_TCP_V) return 0; //from here on we are only interested in TCP-packets; these are longer than 54 bytes #if ETHERCARD_TCPCLIENT if (gPB[TCP_DST_PORT_H_P]==TCPCLIENT_SRC_PORT_H) { //Source port is in range reserved (by EtherCard) for client TCP/IP connections - if (check_ip_message_is_from(hisip)==0) + if (check_ip_message_is_from(iph, hisip)==0) return 0; //Not current TCP/IP connection (only handle one at a time) if (gPB[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { //TCP reset flagged diff --git a/src/udpserver.cpp b/src/udpserver.cpp index aa3b74f..2b0dc2e 100644 --- a/src/udpserver.cpp +++ b/src/udpserver.cpp @@ -53,7 +53,7 @@ bool EtherCard::udpServerListening() { return numListeners > 0; } -bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) { +bool EtherCard::udpServerHasProcessedPacket(const IpHeader &iph, const uint8_t *iter, const uint8_t *last) { bool packetProcessed = false; for(int i = 0; i < numListeners; i++) { @@ -62,7 +62,7 @@ bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) { uint16_t datalen = (uint16_t) (gPB[UDP_LEN_H_P] << 8) + gPB[UDP_LEN_L_P] - UDP_HEADER_LEN; listeners[i].callback( listeners[i].port, - gPB + IP_SRC_P, + (uint8_t *)iph.spaddr, // TODO: change definition of UdpServerCallback to const uint8_t * (gPB[UDP_SRC_PORT_H_P] << 8) | gPB[UDP_SRC_PORT_L_P], (const char *) (gPB + UDP_DATA_P), datalen);