/*
|
|
* ethloop loops back all received frames.
|
|
*
|
|
* ethloop is the virtual equivalence of a RJ45 loopback plug.
|
|
* It's main objective is to see, how a network behaves with a
|
|
* loop in it's topology.
|
|
*
|
|
* This program is put into the public domain, use it as you like.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <pcap.h>
|
|
|
|
pcap_t *pcap = NULL; /* pcap handle */
|
|
|
|
/* print pcap error/warning and exit on error */
|
|
void handle_pcap_err(char *prefix, int err) {
|
|
char *msg = NULL;
|
|
|
|
switch(err) {
|
|
case 0: /* no error */
|
|
break;
|
|
case PCAP_ERROR:
|
|
fprintf(stderr, "%s: error: %s\n", prefix, pcap_geterr(pcap));
|
|
break;
|
|
case PCAP_ERROR_BREAK:
|
|
msg = "pcap_loop terminated by pcap_break";
|
|
break;
|
|
case PCAP_ERROR_NOT_ACTIVATED:
|
|
msg = "pcap not activated";
|
|
break;
|
|
case PCAP_ERROR_ACTIVATED:
|
|
msg = "pcap is already activated";
|
|
break;
|
|
case PCAP_ERROR_NO_SUCH_DEVICE:
|
|
msg = "no such device";
|
|
break;
|
|
case PCAP_ERROR_RFMON_NOTSUP:
|
|
msg = "rfmon mode not supported";
|
|
break;
|
|
case PCAP_ERROR_NOT_RFMON:
|
|
msg = "rfmon mode not activated";
|
|
break;
|
|
case PCAP_ERROR_PERM_DENIED:
|
|
msg = "permission denied";
|
|
break;
|
|
case PCAP_ERROR_IFACE_NOT_UP:
|
|
msg = "interface isn't up";
|
|
break;
|
|
case PCAP_ERROR_CANTSET_TSTAMP_TYPE:
|
|
msg = "device can't set the time stamp type";
|
|
break;
|
|
case PCAP_ERROR_PROMISC_PERM_DENIED:
|
|
msg = "no permission to capture in promiscuous mode";
|
|
break;
|
|
case PCAP_ERROR_TSTAMP_PRECISION_NOTSUP:
|
|
msg = "requested time stamp precision not supported";
|
|
break;
|
|
case PCAP_WARNING:
|
|
fprintf(stderr, "%s: warning: %s\n", prefix, pcap_geterr(pcap));
|
|
break;
|
|
case PCAP_WARNING_PROMISC_NOTSUP:
|
|
msg = "warning: promiscuous mode not supported";
|
|
break;
|
|
case PCAP_WARNING_TSTAMP_TYPE_NOTSUP:
|
|
msg = "warning: requested time stamp type not supported";
|
|
break;
|
|
default:
|
|
if (err > 0)
|
|
fprintf(stderr, "%s: unknown warning, err = %d\n", prefix, err);
|
|
else
|
|
fprintf(stderr, "%s: unknown error, err = %d\n", prefix, err);
|
|
break;
|
|
}
|
|
|
|
if (msg != NULL)
|
|
fprintf(stderr, "%s: %s\n", prefix, msg);
|
|
if (err < 0)
|
|
exit(1);
|
|
}
|
|
|
|
/* break pcap loop by a signal */
|
|
void break_pcap_loop(int signum) {
|
|
if (pcap != NULL) pcap_breakloop(pcap);
|
|
}
|
|
|
|
/* print packet counter */
|
|
void print_counter(int counter) {
|
|
printf("\r%6d", counter); fflush(stdout);
|
|
}
|
|
|
|
/* callback on received packet */
|
|
void echo_packet(u_char *user, const struct pcap_pkthdr *pkthdr,
|
|
const u_char *packet) {
|
|
static int counter = 0;
|
|
int ret;
|
|
|
|
ret = pcap_inject(pcap, packet, pkthdr->len);
|
|
if (ret < 0) {
|
|
printf("\n");
|
|
handle_pcap_err("pcap_inject", ret);
|
|
}
|
|
print_counter(++counter);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
sig_t old_sighup, old_sigint, old_sigquit, old_sigterm;
|
|
char *device;
|
|
int ret;
|
|
|
|
/* first (and only) argument is device name */
|
|
if (argc != 2 || argv[1][0] == '-') {
|
|
fprintf(stderr, "ethloop loops back all received frames.\n\n"
|
|
"Usage: ethloop <network device>\n");
|
|
exit(1);
|
|
}
|
|
device = argv[1];
|
|
|
|
/* open capture device */
|
|
if ((pcap = pcap_create(device, errbuf)) == NULL) {
|
|
fprintf(stderr, "pcap_create: can't open network device: %s\n", errbuf);
|
|
exit(1);
|
|
}
|
|
|
|
/* set capture options */
|
|
handle_pcap_err("pcap_set_snaplen", pcap_set_snaplen(pcap, 65535));
|
|
handle_pcap_err("pcap_set_promisc", pcap_set_promisc(pcap, 1));
|
|
|
|
/* activate capture */
|
|
handle_pcap_err("pcap_activate", pcap_activate(pcap));
|
|
handle_pcap_err("pcap_setdirection", pcap_setdirection(pcap, PCAP_D_IN));
|
|
|
|
/* receive packets until HUP, INT, QUIT or TERM signal */
|
|
old_sighup = signal(SIGHUP, break_pcap_loop);
|
|
old_sigint = signal(SIGINT, break_pcap_loop);
|
|
old_sigquit = signal(SIGQUIT, break_pcap_loop);
|
|
old_sigterm = signal(SIGTERM, break_pcap_loop);
|
|
print_counter(0);
|
|
ret = pcap_loop(pcap, -1, echo_packet, NULL);
|
|
printf("\n");
|
|
if (ret != PCAP_ERROR_BREAK)
|
|
handle_pcap_err("pcap_loop", ret);
|
|
signal(SIGHUP, old_sighup);
|
|
signal(SIGINT, old_sigint);
|
|
signal(SIGQUIT, old_sigquit);
|
|
signal(SIGTERM, old_sigterm);
|
|
|
|
pcap_close(pcap);
|
|
return 0;
|
|
}
|