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)