Skip to content

Instantly share code, notes, and snippets.

@austinmarton
Created June 3, 2012 07:55
Show Gist options
  • Save austinmarton/2862515 to your computer and use it in GitHub Desktop.
Save austinmarton/2862515 to your computer and use it in GitHub Desktop.
Receive raw Ethernet frames in Linux
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*/
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#define DEST_MAC0 0x00
#define DEST_MAC1 0x00
#define DEST_MAC2 0x00
#define DEST_MAC3 0x00
#define DEST_MAC4 0x00
#define DEST_MAC5 0x00
#define ETHER_TYPE 0x0800
#define DEFAULT_IF "eth0"
#define BUF_SIZ 1024
int main(int argc, char *argv[])
{
char sender[INET6_ADDRSTRLEN];
int sockfd, ret, i;
int sockopt;
ssize_t numbytes;
struct ifreq ifopts; /* set promiscuous mode */
struct ifreq if_ip; /* get ip addr */
struct sockaddr_storage their_addr;
uint8_t buf[BUF_SIZ];
char ifName[IFNAMSIZ];
/* Get interface name */
if (argc > 1)
strcpy(ifName, argv[1]);
else
strcpy(ifName, DEFAULT_IF);
/* Header structures */
struct ether_header *eh = (struct ether_header *) buf;
struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct iphdr) + sizeof(struct ether_header));
memset(&if_ip, 0, sizeof(struct ifreq));
/* Open PF_PACKET socket, listening for EtherType ETHER_TYPE */
if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1) {
perror("listener: socket");
return -1;
}
/* Set interface to promiscuous mode - do we need to do this every time? */
strncpy(ifopts.ifr_name, ifName, IFNAMSIZ-1);
ioctl(sockfd, SIOCGIFFLAGS, &ifopts);
ifopts.ifr_flags |= IFF_PROMISC;
ioctl(sockfd, SIOCSIFFLAGS, &ifopts);
/* Allow the socket to be reused - incase connection is closed prematurely */
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) {
perror("setsockopt");
close(sockfd);
exit(EXIT_FAILURE);
}
/* Bind to device */
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ-1) == -1) {
perror("SO_BINDTODEVICE");
close(sockfd);
exit(EXIT_FAILURE);
}
repeat: printf("listener: Waiting to recvfrom...\n");
numbytes = recvfrom(sockfd, buf, BUF_SIZ, 0, NULL, NULL);
printf("listener: got packet %lu bytes\n", numbytes);
/* Check the packet is for me */
if (eh->ether_dhost[0] == DEST_MAC0 &&
eh->ether_dhost[1] == DEST_MAC1 &&
eh->ether_dhost[2] == DEST_MAC2 &&
eh->ether_dhost[3] == DEST_MAC3 &&
eh->ether_dhost[4] == DEST_MAC4 &&
eh->ether_dhost[5] == DEST_MAC5) {
printf("Correct destination MAC address\n");
} else {
printf("Wrong destination MAC: %x:%x:%x:%x:%x:%x\n",
eh->ether_dhost[0],
eh->ether_dhost[1],
eh->ether_dhost[2],
eh->ether_dhost[3],
eh->ether_dhost[4],
eh->ether_dhost[5]);
ret = -1;
goto done;
}
/* Get source IP */
((struct sockaddr_in *)&their_addr)->sin_addr.s_addr = iph->saddr;
inet_ntop(AF_INET, &((struct sockaddr_in*)&their_addr)->sin_addr, sender, sizeof sender);
/* Look up my device IP addr if possible */
strncpy(if_ip.ifr_name, ifName, IFNAMSIZ-1);
if (ioctl(sockfd, SIOCGIFADDR, &if_ip) >= 0) { /* if we can't check then don't */
printf("Source IP: %s\n My IP: %s\n", sender,
inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
/* ignore if I sent it */
if (strcmp(sender, inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr)) == 0) {
printf("but I sent it :(\n");
ret = -1;
goto done;
}
}
/* UDP payload length */
ret = ntohs(udph->len) - sizeof(struct udphdr);
/* Print packet */
printf("\tData:");
for (i=0; i<numbytes; i++) printf("%02x:", buf[i]);
printf("\n");
done: goto repeat;
close(sockfd);
return ret;
}
@Josh798
Copy link

Josh798 commented Oct 12, 2020

Warning: sockopt is undefined in this code. It probably should be initialized to 1.

@marco20240618
Copy link

marco20240618 commented Nov 4, 2022

I modify it a little bit, so that it only prints the packet with specified source or dest mac address.

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>

// #define DEST_MAC0 0x00
// #define DEST_MAC1 0x00
// #define DEST_MAC2 0x00
// #define DEST_MAC3 0x00
// #define DEST_MAC4 0x00
// #define DEST_MAC5 0x00

#define DEST_MAC0 0x52
#define DEST_MAC1 0x54
#define DEST_MAC2 0x00
#define DEST_MAC3 0xcb
#define DEST_MAC4 0x4b
#define DEST_MAC5 0xca

#define ETHER_TYPE 0x0800

#define DEFAULT_IF "eth0"
#define BUF_SIZ 1024

int main(int argc, char *argv[])
{
	char sender[INET6_ADDRSTRLEN];
	int sockfd, ret, i;
	int sockopt;
	ssize_t numbytes;
	struct ifreq ifopts; /* set promiscuous mode */
	struct sockaddr_storage their_addr;
	uint8_t buf[BUF_SIZ];
	char ifName[IFNAMSIZ];

	/* Get interface name */
	if (argc > 1)
		strcpy(ifName, argv[1]);
	else
		strcpy(ifName, DEFAULT_IF);

	/* Header structures */
	struct ether_header *eh = (struct ether_header *)buf;

	/* Open PF_PACKET socket, listening for EtherType ETHER_TYPE */
	if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1)
	{
		perror("listener: socket");
		return -1;
	}

	/* Set interface to promiscuous mode - do we need to do this every time? */
	strncpy(ifopts.ifr_name, ifName, IFNAMSIZ - 1);
	ioctl(sockfd, SIOCGIFFLAGS, &ifopts);
	ifopts.ifr_flags |= IFF_PROMISC;
	ioctl(sockfd, SIOCSIFFLAGS, &ifopts);
	/* Allow the socket to be reused - incase connection is closed prematurely */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1)
	{
		perror("setsockopt");
		close(sockfd);
		exit(EXIT_FAILURE);
	}
	/* Bind to device */
	if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ - 1) == -1)
	{
		perror("SO_BINDTODEVICE");
		close(sockfd);
		exit(EXIT_FAILURE);
	}

	while (1)
	{
		numbytes = recvfrom(sockfd, buf, BUF_SIZ, 0, NULL, NULL);
		if (
			// eh->ether_dhost[0] == DEST_MAC0 &&
			// eh->ether_dhost[1] == DEST_MAC1 &&
			// eh->ether_dhost[2] == DEST_MAC2 &&
			// eh->ether_dhost[3] == DEST_MAC3 &&
			// eh->ether_dhost[4] == DEST_MAC4 &&
			// eh->ether_dhost[5] == DEST_MAC5 &&
			eh->ether_shost[0] == 0x52 &&
			eh->ether_shost[1] == 0x54 &&
			eh->ether_shost[2] == 0x00 &&
			eh->ether_shost[3] == 0xb7 &&
			eh->ether_shost[4] == 0xd4 &&
			eh->ether_shost[5] == 0x39)
		{
			printf("source mac: %x:%x:%x:%x:%x:%x\n",
				   eh->ether_shost[0],
				   eh->ether_shost[1],
				   eh->ether_shost[2],
				   eh->ether_shost[3],
				   eh->ether_shost[4],
				   eh->ether_shost[5]);
			printf("destination mac: %x:%x:%x:%x:%x:%x\n",
				   eh->ether_dhost[0],
				   eh->ether_dhost[1],
				   eh->ether_dhost[2],
				   eh->ether_dhost[3],
				   eh->ether_dhost[4],
				   eh->ether_dhost[5]);

			printf("Data: ");
			for (i = 0; i < numbytes; i++)
				printf("%02x ", buf[i]);
			printf("\n\n");
		}
	}
}

@slothfull
Copy link

slothfull commented Jun 21, 2024

thanks for these two send/recv progam, really helps for a greener like me!
hello, currently Im trying to chain the send & recv program together:

i've got eth0 & docker0 bridge, try send raw pkts using the sender via docker0 to eth0:

$ ./send docker0   # set the pkt dest mac as eth0's mac

try recv raw pkts by receiver via eth0:

$ ./recv eth0      # set pkt dest mac as eth0's mac

however, nothing shown.

try capture the pkts on eth0 using tcp, got ya:

$ tcpdump -nettti eth0 '(ether dst host fa:16:xx:xx:xx:xx)' -vvnnXX -l | grep beef
         0x0010:  beef                                     ..

actually the pkt got send, but not captured by recv program.
so any way to point me out there, some thing missing?

Actually I forget about the iptables,, the answer is that: raw pkt are simply layer2,
the iptables rules get some filter on route from docker0 <-> eth0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment