From d48d89002247e4a2832bf1108a4530245d65f3d0 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sat, 5 Jan 2019 21:49:11 +0100 Subject: [PATCH] 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)