Skip to content

Commit

Permalink
Add support for ARP cache
Browse files Browse the repository at this point in the history
 * 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
  • Loading branch information
Arnaud authored and Arnaud committed Jan 6, 2019
1 parent 713e9b0 commit d48d890
Show file tree
Hide file tree
Showing 4 changed files with 417 additions and 139 deletions.
42 changes: 42 additions & 0 deletions src/EtherCard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)(
Expand Down Expand Up @@ -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 <i>unit8_t</i> True if gateway found
*/
static uint8_t clientWaitIp(const uint8_t *ip);

/** @brief Check if got gateway hardware address (ARP lookup)
* @return <i>unit8_t</i> True if gateway found
*/
Expand Down Expand Up @@ -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 <i>bool</i> 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 <i>uint8_t *</i> 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
Expand Down
93 changes: 93 additions & 0 deletions src/arp.cpp
Original file line number Diff line number Diff line change
@@ -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));
}
110 changes: 89 additions & 21 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit d48d890

Please sign in to comment.