Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ARP cache #351

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This struct should also be __attribute__((__packed__)), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't make mistakes all supported AVR are 8bits aligned, so adding this attribute won't change anything.

If this library support 16/32bits chipsets the EthHeader should have this attribute. Is it the case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, but there's no harm in adding it for future proof :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful with this attribute, it's not always harmless, you can generate some "bus error" when data are not aligned with some architectures (like MIPS for example).

All those structures are correctly aligned by default, so I prefer to not add this attribute, it is useless in this case, even with EthHeader. I've just checked on amd64 and this code:

#include <iostream>

struct EthHeader
{
    uint8_t target[6];
    uint8_t source[6];
    uint16_t    etype;
};

int main()
{
    std::cout << "sizeof(EthHeader) = " << sizeof(EthHeader) << std::endl;
    return 0;
}

output: sizeof(EthHeader) = 14

So struct even EthHeader is fine.

{
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