-
-
Save austinmarton/2862515 to your computer and use it in GitHub Desktop.
/* | |
* 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; | |
} |
perhaps #include <unistd.h>
is missing as a compiler warning for implicit close definition occurs.
i've expanded this code significantly and joined it with the send example to relay packets. i'm adding thread support next for two-way relaying or a handy bridge.
one bug i've run into is that the kernel seems to re-assmble the TCP segments into large packets (>1500 bytes) requiring jumbo frames on the tx side. any comments on addressing this is appreciated.
Label "done" should be on a next line.
Nice example.
I updated it a little-bit with addition of ability to set DST MAC in command line like ./sendRawEth enp0s29u1u1u2 2E:58:2F:73:73:40
If of any interest please find it here https://gist.github.com/abrodkin/8854874f3b34e92f71653a7fa35c9900
Suggesting to use
#define ETHER_TYPE ETHERTYPE_IP
instead of
#define ETHER_TYPE 0x0800
ETHERTYPE_IP
is already defined when including netinet/ether.h
You should set sockopt
to 1 because at this moment it is undefined if SO_REUSEADDR is set or not.
SO_BINDTODEVICE works only with AF_INET
HI. I am getting an error of "SO_BINDTODEVICE : NO SUCH DEVICE". Would deeply appreciate if I can get help from anybody here. I had tried googling the error but i still cant find a prominent solution.
Thanks.
PS. I think your "done:" label is on the wrong line ;)
Can you please explain how to run these codes on both sender and receiver end using wlan0?
Thanks a lot for sharing. It does not show 802.1q tags similar to another program that I got from here.
I can capture the complete ethernet frame including the VLAN tags using tcpdump. For some reason, these programs drop/exclude 802.1q tags.
Does anybody know the reason?
Warning: sockopt
is undefined in this code. It probably should be initialized to 1.
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");
}
}
}
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.
Thanks for the software. This really helped to get input from the eth0 to my c program quick.