Skip to content

Commit b80c436

Browse files
authored
Merge pull request #31 from akos011221/feat/arp-reply-handling
feat: handle ARP replies
2 parents 939171e + f88dff2 commit b80c436

4 files changed

Lines changed: 41 additions & 17 deletions

File tree

include/parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#define ARP_HW_ETHERNET 1
2828
#define ARP_HW_LEN_ETH 6
2929
#define ARP_PROTO_LEN 4
30+
#define ARP_OP_REQUEST 1
31+
#define ARP_OP_REPLY 2
3032

3133
/* Network Headers */
3234
/*

include/tx.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
typedef struct {
1010
int sock_fd;
1111
int ifindex;
12-
uint8_t eth_addr[6]; /* TX interface's MAC */
12+
uint8_t eth_addr[6];
13+
uint32_t ip4_addr;
1314
} tx_ctx_t;
1415

1516
/* Initialize a TX context bound to an interface. Returns 0 on success, -1 on failure. */

src/tx_afpacket.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ int tx_init(tx_ctx_t *tx, const char *out_iface) {
4040
return -1;
4141
}
4242

43+
/* Get IPv4 address of the interface */
44+
memset(&ifr, 0, sizeof(ifr));
45+
strncpy(ifr.ifr_name, out_iface, IFNAMSIZ - 1);
46+
if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
47+
struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
48+
tx->ip4_addr = ntohl(sin->sin_addr.s_addr);
49+
} else {
50+
log_msg(LOG_WARN, "ioctl(SIOCGIFADDR) failed (ARP replies disabled): %s", strerror(errno));
51+
tx->ip4_addr = 0;
52+
}
53+
4354
tx->sock_fd = fd;
4455
tx->ifindex = ifindex;
4556
memcpy(tx->eth_addr, ifr.ifr_hwaddr.sa_data, 6);

src/worker.c

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,32 @@ static bool handle_control_packet(worker_t *w, pktbuf_t *b) {
2626

2727
/* Handling of ARP packets */
2828
if (ethertype == ETH_TYPE_ARP) {
29-
if (b->len >= sizeof(struct eth_hdr) + sizeof(struct arp_hdr)) {
30-
struct arp_hdr *arp = (struct arp_hdr *)(b->data + sizeof(struct eth_hdr));
31-
32-
/* Hardware Type 1 (Ethernet), Protocol 0x0800 (IPv4), HW Len 6, Proto Len 4 */
33-
if (ntohs(arp->htype) == 1 && ntohs(arp->ptype) == ETH_TYPE_IPV4 &&
34-
arp->hlen == ARP_HW_LEN_ETH && arp->plen == ARP_PROTO_LEN) {
35-
uint32_t spa = ntohl(arp->spa); /* Sender Protocol Address (IP) */
36-
arp_update(w->arpt, spa, arp->sha);
37-
log_msg(LOG_DEBUG, "Learned ARP: %08X -> %02X:%02X:%02X:%02X:%02X:%02X", spa,
38-
arp->sha[0], arp->sha[1], arp->sha[2], arp->sha[3], arp->sha[4],
39-
arp->sha[5]);
29+
struct arp_hdr *arp = (struct arp_hdr *)(b->data + sizeof(struct eth_hdr));
30+
31+
if (ntohs(arp->htype) == ARP_HW_ETHERNET && ntohs(arp->ptype) == ETH_TYPE_IPV4 &&
32+
arp->hlen == ARP_HW_LEN_ETH && arp->plen == ARP_PROTO_LEN) {
33+
/* Learn the sender's MAC/IP mapping */
34+
uint32_t spa = ntohl(arp->spa);
35+
arp_update(w->arpt, spa, arp->sha);
36+
log_msg(LOG_DEBUG, "Learned ARP: %08X -> %02X:%02X:%02X:%02X:%02X:%02X", spa,
37+
arp->sha[0], arp->sha[1], arp->sha[2], arp->sha[3], arp->sha[4], arp->sha[5]);
38+
39+
/* If ARP request is for local IP: Re-use incoming buffer to build a reply in-place */
40+
if (ntohs(arp->op) == ARP_OP_REQUEST && w->tx->ip4_addr != 0 &&
41+
ntohl(arp->tpa) == w->tx->ip4_addr) {
42+
memcpy(eth->dst, eth->src, 6);
43+
memcpy(eth->src, w->tx->eth_addr, 6);
44+
45+
arp->op = htons(ARP_OP_REPLY);
46+
memcpy(arp->tha, arp->sha, 6);
47+
arp->tpa = arp->spa;
48+
memcpy(arp->sha, w->tx->eth_addr, 6);
49+
arp->spa = htonl(w->tx->ip4_addr);
50+
51+
tx_send(w->tx, b->data, b->len);
52+
log_msg(LOG_DEBUG, "Sent ARP reply for %08X", w->tx->ip4_addr);
4053
}
4154
}
42-
/* Consume the packet */
43-
pktbuf_free(w->pool, b);
44-
return true;
4555
}
4656

4757
/* Handling for IPv6 NDP packets (Neighbor Advertisement/Solicitation) */
@@ -220,8 +230,8 @@ static void process_packet(worker_t *w, pktbuf_t *b) {
220230
}
221231
}
222232

223-
/* Latency is recorded before queuing for TX (sendmmsg() is kernel/NIC latency, should not
224-
* be included in the dataplane latency.)*/
233+
/* Latency is recorded before queuing for TX (sendmmsg() is kernel/NIC latency, should
234+
* not be included in the dataplane latency.)*/
225235
if (b->timestamp > 0) {
226236
latency_record(&w->latency_hist, rdtsc() - b->timestamp, g_cycles_per_ns);
227237
}

0 commit comments

Comments
 (0)