Well, normally libpcap is used to recieve packets, so it don't neccesarily
help with sending them.
Second, sending packets is one thing, sending packets with a "fake" source
MAC address is yet another thing (as a short note, the DEUNA and DELUA
ethernet controllers for the Unibus can never do this. They set the source
MAC address from the controller, no matter what you place in the packet you
want to send).
But please report if you have success with this.
This is what i'm using right now to have two versions of SIMH send
pings to eachother... I added the tcp/ip stuff, and removed all of
the libpcap parts of the code.. I also noticed it uses blocking
sockets so it'll get 'stuck' from time to time reading stuff. I
started work on porting it to windows but I've been away on vacation
so it didn't get any work done...
Hopefully gmail doesn't screw the formatting up too badly.
---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<
/* A simple DECnet bridge program
* (c) 2003, 2005 by Johnny Billquist
* Version 2.1 Fixed code for OpenBSD and FreeBSD as well.
* Version 2.0 (I had to start using a version number sometime, and
* since I don't have any clue to the history of my
* development here, I just picked 2.0 because I liked
* it.)
* Some more text will come here later. */
#define DEBUG 0
#define MAX_HOST 16
#define CONF_FILE "bridge.conf"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
/* Throttling control:
* THROTTLETIME - (mS)
* If packets come closer in time than this, they are
* a base for perhaps considering throttling.
* THROTTLEPKT - (#)
* The number of packets in sequence that fulfill
* THROTTLETIME that means throttling will kick in.
* THROTTLEDELAY - (uS)
* The delay to insert when throttling is active.
*
* Passive connection control:
* PASSIVE_TMO - (mS)
* If nothing has been received from a passive node
* in this time, sending to it will stop.
*/
#define THROTTLETIME 5
#define THROTTLEPKT 4
#define THROTTLEDELAY 10
#define PASSIVE_TMO 180000L
#define THROTTLEMASK ((1 << THROTTLEPKT) - 1)
#define ETHERTYPE_DECnet 0x6003
#define ETHERTYPE_LAT 0x6004
#define ETHERTYPE_MOPDL 0x6001
#define ETHERTYPE_MOPRC 0x6002
#define ETHERTYPE_IP 0x0800
#define ETHERTYPE_ARP 0x0806
#define ETHERTYPE_REVARP 0x8035
#define MAX(a,b) (a>b?a:b)
/* The structures and other global data we keep info in.
It would perhaps be nice if we could reload this, and
in case of failure keep the old stuff, but for now we
don't care that much... */
/* The data structures we have are the port, which describe
a source/destination for data. It also holds info about which
kind of traffic should be forwarded to this site. It is also
used to filter incoming packets. If we don't send something, we
don't want it from that side either.
We have the host table, which is a hash table for all known
destinations, so that we can optimize the traffic a bit.
When data arrives, we filter, process and send it out again.
*/
typedef enum {DECnet, LAT, IP} pkttyp;
#define MAXTYP 3
struct BRIDGE {
char name[40];
char host[80];
in_addr_t addr;
short port;
int passive;
int fd;
int types[MAXTYP];
char last[8][14];
int lastptr;
int rcount;
int tcount;
int xcount;
struct timeval lasttime;
int throttle;
int throttlecount;
struct timeval lastrcv;
};
struct DATA {
int source;
pkttyp type;
int len;
const char *data;
};
struct HOST {
struct HOST *next;
unsigned char mac[6];
int bridge;
};
#define HOST_HASH 65536
struct HOST *hosts[HOST_HASH];
struct BRIDGE bridge[MAX_HOST];
int bcnt = 0;
int sd;
/* Here come the code... */
int lookup(struct sockaddr_in *sa, char *data)
{
int i;
for (i=0; i<bcnt; i++) {
if ((bridge[i].addr == sa->sin_addr.s_addr) &&
(bridge[i].port == sa->sin_port))
return i;
}
return -1;
}
int lookup_bridge(char *newbridge)
{
int i;
int l = strlen(newbridge);
#if DEBUG
printf("Trying to match %s\n", newbridge);
#endif
for (i=0; i<bcnt; i++) {
#if DEBUG
printf("Matching against: %s\n", bridge[i].name);
#endif
if ((strcmp(newbridge,bridge[i].name) == 0) &&
(l == strlen(bridge[i].name))) {
#if DEBUG
printf("Found match: %s == %s\n", newbridge, bridge[i].name);
#endif
return i;
}
}
#if DEBUG
printf("No match found\n");
#endif
return -1;
}
void add_bridge(char *name, char *dst)
{
struct hostent *he;
char rhost[40];
int port;
int i,found=0;
in_addr_t addr;
char *p;
int passive = 0;
if (bcnt < MAX_HOST) {
bzero(&bridge[bcnt],sizeof(struct BRIDGE));
if (*name == '~') {
passive = 1;
name++;
}
strcpy(bridge[bcnt].name,name);
p = strchr(dst,':');
if (p == NULL) { /* Assume local descriptor */
found = -1;
} else {
*p = ' ';
sscanf(dst,"%s %d", rhost, &port);
if ((he = gethostbyname(rhost)) != NULL) {
addr = *(in_addr_t *)he->h_addr;
found = -1;
} else {
found = inet_aton(rhost,&addr);
}
if (found) {
strcpy(bridge[bcnt].host,rhost);
bridge[bcnt].addr = addr;
bridge[bcnt].port = htons(port);
bridge[bcnt].fd = sd;
}
}
if (found) {
for (i=0; i<MAXTYP; i++) bridge[bcnt].types[i] = 0;
bridge[bcnt].rcount = 0;
bridge[bcnt].tcount = 0;
bridge[bcnt].passive = passive;
bcnt++;
#if DEBUG
printf("Adding router ''%s''. %08x:%d\n", name, addr,
port);
#endif
}
} else {
printf("Warning. Bridge table full. Not adding %d (%d)\n", name, dst);
}
}
int add_service(char *newbridge, pkttyp type, char *name)
{
int i;
#if DEBUG
printf("Adding %s bridge %s.\n", name, newbridge);
#endif
if ((i = lookup_bridge(newbridge)) >= 0) {
if (bridge[i].types[type]++ > 0) {
printf("%s bridge %s added multiple times.\n", name, bridge);
}
return 1;
}
return 0;
}
void read_conf()
{
FILE *f;
int mode = 0;
int area,node;
int line;
char buf[80];
char buf1[40],buf2[40];
int i;
if ((f = fopen(CONF_FILE,"r")) == NULL) {
perror("opening bridge.conf");
exit(1);
}
for (i=0; i<bcnt; i++) {
if (bridge[i].fd != sd) close(bridge[i].fd);
}
bcnt = 0;
for (i=0; i<HOST_HASH; i++) {
struct HOST *h, *n;
h = hosts[i];
hosts[i] = NULL;
while(h) {
n = h->next;
free(h);
h = n;
}
}
line = 0;
while (!feof(f)) {
if (fgets(buf,80,f) == NULL) continue;
buf[strlen(buf)-1] = 0;
line++;
if((strlen(buf) > 2) && (buf[0] != '!')) {
if(buf[0]=='[') {
mode = -1;
if(strcmp(buf,"[bridge]") == 0) mode = 0;
if(strcmp(buf,"[decnet]") == 0) mode = 1;
if(strcmp(buf,"[lat]") == 0) mode = 2;
if(sscanf(buf,"[source %d.%d]", &area, &node) == 2) mode = 3;
if(strcmp(buf,"[relay]") == 0) mode = 4;
if(strcmp(buf,"[ip]") == 0) mode = 5;
if(mode < 0) {
printf("Bad configuration at line %d\n%s\n", line,buf);
exit(1);
}
} else {
switch (mode) {
case 0:
if (sscanf(buf, "%s %s", buf1, buf2) == 2) {
add_bridge(buf1,buf2);
} else {
printf("Bad bridge at line %d\n%s\n", line, buf);
exit(1);
}
break;
case 1:
if (!add_service(buf,DECnet,"DECnet"))
printf("%d: DECnet bridge %s don't exist.\n", line, buf);
break;
case 2:
if (!add_service(buf,LAT,"LAT"))
printf("%d: LAT bridge %s don't exist.\n", line, buf);
break;
case 3:
break;
case 4:
break;
case 5:
if (!add_service(buf,IP,"IP"))
printf("%d: IP bridge %s don't exist.\n", line, buf);
break;
default:
printf("weird state at line %d\n",line);
exit(1);
}
}
}
}
fclose(f);
}
int is_ethertype(struct DATA *d, int type)
{
return ((d->data[13] == (type & 0xff)) &&
(d->data[12] == (type >> 8)));
}
int is_decnet(struct DATA *data)
{
return is_ethertype(data, ETHERTYPE_DECnet);
}
int is_lat(struct DATA *data)
{
return (is_ethertype(data, ETHERTYPE_LAT) ||
is_ethertype(data, ETHERTYPE_MOPDL) ||
is_ethertype(data, ETHERTYPE_MOPRC));
}
int is_ip(struct DATA *data)
{
return (is_ethertype(data, ETHERTYPE_IP) ||
is_ethertype(data, ETHERTYPE_ARP) ||
is_ethertype(data, ETHERTYPE_REVARP));
}
unsigned long timedelta(struct timeval old)
{
struct timeval now;
unsigned long delta;
gettimeofday(&now, NULL);
delta = now.tv_sec - old.tv_sec;
delta *= 1000;
delta += ((now.tv_usec - old.tv_usec) / 1000);
return delta;
}
void throttle(int index)
{
long delta;
delta = timedelta(bridge[index].lasttime);
bridge[index].throttle <<= 1;
bridge[index].throttle += (delta < THROTTLETIME ? 1 : 0);
if ((bridge[index].throttle & THROTTLEMASK) == THROTTLEMASK) {
bridge[index].throttlecount++;
usleep(THROTTLEDELAY);
}
gettimeofday(&bridge[index].lasttime,NULL);
}
int active(int index)
{
if (bridge[index].passive == 0) return 1;
if (timedelta(bridge[index].lastrcv) < PASSIVE_TMO) return 1;
return 0;
}
/* do the actual sending */
void send_packet(int index, struct DATA *d)
{
struct sockaddr_in sa;
if (index == d->source) return; /* Avoid loopback of data. */
if (bridge[index].types[d->type] == 0) return; /* Avoid sending
unwanted frames */
if (active(index)) {
bridge[index].tcount++;
throttle(index);
if (bridge[index].addr == 0) {
write(bridge[index].fd,d->data,d->len); /* Local network. */
} else {
sa.sin_family = AF_INET; /* Remote network. */
sa.sin_port = bridge[index].port;
sa.sin_addr.s_addr = bridge[index].addr;
if (sendto(bridge[index].fd,d->data,d->len,0,(struct sockaddr
*)&sa,sizeof(sa)) == -1)
perror("sendto");
}
bridge[index].lastptr = (bridge[index].lastptr+1) & 7;
memcpy(bridge[index].last[bridge[index].lastptr],d->data,14);
}
}
void register_source(struct DATA *d)
{
unsigned short hash;
struct HOST *h;
hash = *(unsigned short *)(d->data+10);
h = hosts[hash];
while (h) {
if (memcmp(h->mac, d->data+6, 6) == 0) {
h->bridge = d->source;
#if DEBUG
printf("Setting existing hash to bridge %d\n", h->bridge);
#endif
return;
}
h = h->next;
}
h = malloc(sizeof(struct HOST));
h->next = hosts[hash];
memcpy(h->mac,d->data+6,6);
h->bridge = d->source;
#if DEBUG
printf("Adding new hash entry. Port is %d\n", h->bridge);
#endif
hosts[hash] = h;
}
int locate_dest(struct DATA *d)
{
unsigned short hash;
struct HOST *h;
if (d->data[0] & 1) return -1; /* Ethernet multicast */
hash = *(unsigned short *)(d->data+4);
h = hosts[hash];
while (h) {
if (memcmp(h->mac, d->data, 6) == 0) return h->bridge;
h = h->next;
}
return -1;
}
void process_packet(struct DATA *d)
{
int dst;
int i;
bridge[d->source].rcount++;
gettimeofday(&bridge[d->source].lastrcv, NULL);
for (i=0; i<8; i++) {
if (memcmp(bridge[d->source].last[i],d->data,14) == 0) {
return;
}
}
if (is_decnet(d)) d->type = DECnet;
if (is_lat(d)) d->type = LAT;
if (is_ip(d)) d->type = IP;
if (bridge[d->source].types[d->type] == 0) return;
if (d->type == -1) return;
bridge[d->source].xcount++;
register_source(d);
dst = locate_dest(d);
if (dst == -1) {
int i;
for (i=0; i<bcnt; i++) send_packet(i, d);
} else {
send_packet(dst, d);
}
}
void dump_data()
{
int i;
printf("Host table:\n");
for (i=0; i<bcnt; i++)
printf("%d: %s %s:%d (Rx: %d Tx: %d (Drop rx: %d)) Active: %d
Throttle: %d(%03o)\n",
i,
bridge[i].name,
inet_ntoa(bridge[i].addr),
ntohs(bridge[i].port),
bridge[i].rcount,
bridge[i].tcount,
bridge[i].rcount - bridge[i].xcount,
active(i),
bridge[i].throttlecount,
bridge[i].throttle & 255);
printf("Hash of known destinations:\n");
for (i=0; i<HOST_HASH; i++) {
struct HOST *h;
h=hosts[i];
while (h) {
printf("%02x%02x%02x%02x%02x%02x -> %d",
(unsigned char)h->mac[0],
(unsigned char)h->mac[1],
(unsigned char)h->mac[2],
(unsigned char)h->mac[3],
(unsigned char)h->mac[4],
(unsigned char)h->mac[5],
h->bridge);
if ((unsigned char)h->mac[0] == 0xaa &&
(unsigned char)h->mac[1] == 0x00 &&
(unsigned char)h->mac[2] == 0x04 &&
(unsigned char)h->mac[3] == 0x00) {
printf(" (%d.%d)", h->mac[5] >> 2, ((h->mac[5] & 3) << 8)
+ h->mac[4]);
}
printf("\n");
h = h->next;
}
}
}
int main(int argc, char **argv)
{
struct sockaddr_in sa,rsa;
int len,i,hsock;
fd_set fds;
socklen_t ilen;
int port;
struct DATA d;
char buf[8192];
signal(SIGHUP, read_conf);
signal(SIGUSR1, dump_data);
if (argc != 2) {
printf("usage: %s <port>\n", argv[0]);
exit(1);
}
if ((sd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
sa.sin_family = AF_INET;
sa.sin_port = htons(atoi(argv[1]));
sa.sin_addr.s_addr = INADDR_ANY;
if (bind(sd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
perror("bind");
exit(1);
}
read_conf();
#if DEBUG
dump_data();
#endif
while(1) {
FD_ZERO(&fds);
hsock = 0;
for (i=0; i<bcnt; i++) {
FD_SET(bridge[i].fd, &fds);
if (bridge[i].fd > hsock) hsock = bridge[i].fd;
}
if (select(hsock+1,&fds,NULL,NULL,NULL) == -1) {
if (errno != EINTR) {
perror("select");
exit(1);
}
}
for (i=0; i<bcnt; i++) {
if (FD_ISSET(bridge[i].fd,&fds)) {
d.source = i;
d.type = -1;
if (bridge[i].addr == 0) {
d.data = NULL;
d.len = NULL;//h.caplen;
if (d.data) {
process_packet(&d);
}
} else {
ilen = sizeof(rsa);
if ((d.len = recvfrom(bridge[i].fd, buf, 1518, 0,
(struct sockaddr *)&rsa, &ilen)) > 0) {
if ((d.source = lookup(&rsa, buf)) >= 0) {
d.data = buf;
process_packet(&d);
}
}
}
FD_CLR(bridge[i].fd, &fds);
}
}
}
}