I am building a small network stack based on libpcap
for receiving and sending. The goal is to respond to pings (ICMP packets). Thus the stack implements Ethernet, ARP, IP, and ICMP.
The stack is running on my computer, from which I also invoke the ping command. When I ping my stack, an ARP request is issued, which my stack replies to. I can see that response in a Wireshark session. However, my computer does not seem to receive this response. It keeps sending and sending an ARP request.
Interestingly, an arping
to my stack succeeds!
Below, you can find the implementation of my reduced stack, which can be compiled with gcc server.c -lpcap -o server
. The program needs to be started with sudo
to have network access. The main function defines the IP of the server program and network interface. This might be changed when trying to reproduce it.
#include <stdlib.h>
#include <string.h>
#include <pcap/pcap.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
pcap_t* pcap_device;
struct ether_addr mac_addr;
struct in_addr ip_addr;
struct arp_frame {
struct arphdr hdr;
struct ether_addr src_mac;
struct in_addr src_ip;
struct ether_addr dst_mac;
struct in_addr dst_ip;
} __attribute__((packed));
void send_bytes(char *buf, size_t bufsiz) {
if (pcap_inject(pcap_device, buf, bufsiz) == -1) {
exit(-1);
}
}
static uint16_t checksum(void *data, size_t len) {
uint16_t * p = (uint16_t *) data;
uint32_t sum = 0;
if (len & 1) sum = ((uint8_t*)p)[len - 1];
len /= 2;
while (len--) {
sum += *p++;
if (sum & 0xffff0000)
sum = (sum >> 16) + (sum & 0xffff);
}
return (uint16_t)~sum;
}
void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) {
struct ether_header* eth = (struct ether_header*) bytes;
char response[1500];
if(ntohs(eth->ether_type) == 0x0806) { // ARP
struct arp_frame* arp = (struct arp_frame*) (bytes + sizeof(struct ether_header));
if(ntohs(arp->hdr.ar_op) == ARPOP_REQUEST && !memcmp(&arp->dst_ip, &ip_addr, sizeof(struct in_addr))) {
printf("Received ARP request\n");
struct ether_header* resp_eth = (struct ether_header*) response;
resp_eth->ether_type = htons(0x0806); // ARP
memcpy(resp_eth->ether_dhost, eth->ether_shost, ETH_ALEN);
memcpy(resp_eth->ether_shost, &mac_addr, ETH_ALEN);
struct arp_frame* resp_arp = (struct arp_frame*) (response + sizeof(struct ether_header));
resp_arp->hdr.ar_hrd = htons(ARPHRD_ETHER);
resp_arp->hdr.ar_pro = htons(2048); // ARPPRO_IP
resp_arp->hdr.ar_hln = ETH_ALEN;
resp_arp->hdr.ar_pln = 4;
resp_arp->hdr.ar_op = htons(ARPOP_REPLY);
memcpy(&resp_arp->src_mac, &mac_addr, ETH_ALEN);
memcpy(&resp_arp->src_ip, &ip_addr, sizeof(struct in_addr));
memcpy(&resp_arp->dst_mac, eth->ether_shost, ETH_ALEN);
memcpy(&resp_arp->dst_ip, &arp->src_ip, sizeof(struct in_addr));
send_bytes(response, sizeof(struct ether_header) + sizeof(struct arp_frame));
}
} else if(ntohs(eth->ether_type) == 0x0800) { // IP
struct ip* ip = (struct ip*) (bytes + sizeof(struct ether_header));
if (ip->ip_p == IPPROTO_ICMP && !memcmp(&ip->ip_dst, &ip_addr, sizeof(struct in_addr))) {
printf("Received ICMP packet\n");
struct ether_header* resp_eth = (struct ether_header*) response;
struct ip* resp_ip = (struct ip*) (response + sizeof(struct ether_header));
resp_eth->ether_type = htons(0x0800); // IP
memcpy(resp_eth->ether_dhost, eth->ether_shost, ETH_ALEN);
memcpy(resp_eth->ether_shost, &mac_addr, ETH_ALEN);
resp_ip->ip_v = 4;
resp_ip->ip_hl = 5;
resp_ip->ip_tos = 0;
resp_ip->ip_len = ip->ip_len;
resp_ip->ip_id = htons(0);
resp_ip->ip_off = htons(0);
resp_ip->ip_ttl = 64;
resp_ip->ip_p = IPPROTO_ICMP;
memcpy(&resp_ip->ip_src, &ip_addr, sizeof(struct in_addr));
memcpy(&resp_ip->ip_dst, &ip->ip_src, sizeof(struct in_addr));
resp_ip->ip_sum = 0;
resp_ip->ip_sum = checksum(resp_ip, sizeof(struct ip));
struct icmphdr* icmp = (struct icmphdr*) (bytes + sizeof(struct ether_header) + sizeof(struct ip));
struct icmphdr* resp_icmp = (struct icmphdr*) (response + sizeof(struct ether_header) + sizeof(struct ip));
resp_icmp->type = ICMP_ECHOREPLY;
resp_icmp->code = 0;
resp_icmp->checksum = 0;
resp_icmp->un.echo.id = icmp->un.echo.id;
resp_icmp->un.echo.sequence = icmp->un.echo.sequence;
int data_size = ntohs(ip->ip_len) - sizeof(struct ip) - sizeof(struct icmphdr);
memcpy((response + sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct icmphdr)),
(bytes + sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct icmphdr)),
data_size);
resp_icmp->checksum = checksum(resp_icmp, sizeof(struct icmphdr) + data_size);
send_bytes(response, sizeof(struct ether_header) + ntohs(ip->ip_len));
}
}
}
int main(int argc, char **argv) {
char errbuf[PCAP_ERRBUF_SIZE];
ether_aton_r("D6:E3:4A:53:18:A0", &mac_addr);
inet_aton("192.168.2.22", &ip_addr);
char* dev = "wlp4s0";
pcap_device = pcap_open_live(dev, BUFSIZ, 1, 1, errbuf);
if (!pcap_device)
exit(-1);
if (pcap_loop(pcap_device, 0, handle_packet, NULL) < 0)
exit(-1);
return 0;
}
I am really confused as, why my host computer does not receive the ARP request and continues with the ping and I struggle to debug any further.
Can this be related to my host computer's configuration? Does my host computer (Ubuntu 19.04) drops the received packets somehow?
User contributions licensed under CC BY-SA 3.0