/* Copyright 2003 Renzo Davoli
* Based on the code of uml_switch Copyright 2002 Yon Uriarte and Jeff Dike
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/un.h>
#ifdef __FreeBSD__
#include <string.h>
#endif
#include "switch.h"
#include "hash.h"
#include "port.h"
struct packet {
struct {
unsigned char dest[ETH_ALEN];
unsigned char src[ETH_ALEN];
unsigned char proto[2];
} header;
unsigned char data[1500];
};
// for dedugging if needed
/*
void packet_dump (struct packet *p)
{
register int i;
printf ("packet dump dst");
for (i=0;i<ETH_ALEN;i++)
printf(":%02x",p->header.dest[i]);
printf(" src");
for (i=0;i<ETH_ALEN;i++)
printf(":%02x",p->header.src[i]);
printf(" proto");
for (i=0;i<2;i++)
printf(":%02x",p->header.proto[i]);
printf("\n");
}
*/
struct portgroup {
int ident;
int counter;
};
struct port {
int control;
void *data;
int data_len;
struct portgroup *pg;
void (*sender)(int fd, void *packet, int len, void *data);
};
#define IS_BROADCAST(addr) ((addr[0] & 1) == 1)
void close_port(int i, int fd)
{
struct port *port=(struct port *)(g_fdsdata[i]);
if(port == NULL){
printlog(LOG_WARNING, "No port associated with descriptor %d", fd);
return;
}
else if(port->control != fd){
printlog(LOG_WARNING, "file descriptor mismatch %d %d", port->control, fd);
return;
}
if (port->pg != NULL) {
if (--port->pg->counter == 0) {
free(port->pg);
port->pg=NULL;
hash_delete_port(port);
} else {
struct port *p;
register int i;
p=NULL;
for(i=g_minfds ; i<g_nfds ; i++) {
p=(struct port *)(g_fdsdata[i]);
if(port != NULL && port != p && port->pg == p->pg) break;
}
if (p == NULL) {
printlog(LOG_WARNING,"portgroup inconsistency");
} else
{
hash_reassign(port,p);
}
}
}
else {
hash_delete_port(port);
}
#ifdef INFO
printlog(LOG_INFO,"Disconnect %d",port->control);
#endif
free(port);
}
static void update_src(struct port *port, struct packet *p)
{
struct port *last;
/* We don't like broadcast source addresses */
if(IS_BROADCAST(p->header.src)) return;
last = find_in_hash(p->header.src);
if(last == NULL || (port != last && (port->pg == NULL ||
port->pg != last->pg))){
/* old value differs from actual input port */
#ifdef INFO
printlog(LOG_INFO," Addr: %02x:%02x:%02x:%02x:%02x:%02x New port %d",
p->header.src[0], p->header.src[1], p->header.src[2],
p->header.src[3], p->header.src[4], p->header.src[5],
port->control);
#endif
if(last != NULL){
#ifdef INFO
printlog(LOG_INFO," old port %d", last->control);
#endif
delete_hash(p->header.src);
}
insert_into_hash(p->header.src, port);
}
update_entry_time(p->header.src);
}
static void send_dst(struct port *port, struct packet *packet, int len,
int hub)
{
struct port *target, *p;
register int i;
target = find_in_hash(packet->header.dest);
if((target == NULL) || IS_BROADCAST(packet->header.dest) || hub){
#ifdef INFO
if((target == NULL) && !IS_BROADCAST(packet->header.dest)){
if(port == NULL)
printlog(LOG_WARNING,"unknown Addr: %02x:%02x:%02x:%02x:%02x:%02x from port UNKNOWN",
packet->header.dest[0], packet->header.dest[1],
packet->header.dest[2], packet->header.dest[3],
packet->header.dest[4], packet->header.dest[5]);
else
printlog(LOG_WARNING,"unknown Addr: %02x:%02x:%02x:%02x:%02x:%02x from port %d",
packet->header.dest[0], packet->header.dest[1],
packet->header.dest[2], packet->header.dest[3],
packet->header.dest[4], packet->header.dest[5], port->control);
}
#endif
/* no cache or broadcast/multicast == all ports */
for(i=g_minfds ; i<g_nfds ; i++) {
p=(struct port *)(g_fdsdata[i]);
if (p != NULL && ((p->pg == NULL && p != port) ||
p->pg != port->pg))
(*p->sender)(p->control, packet, len, p->data);
}
}
else {
if (target->pg == NULL)
(*target->sender)(target->control, packet, len, target->data);
else if (target->pg != port->pg) {
if (target->pg->counter == 1)
(*target->sender)(target->control, packet, len, target->data);
else {
for(i=g_minfds ; i<g_nfds ; i++) {
p=(struct port *)(g_fdsdata[i]);
if (p != NULL && p->pg == target->pg)
(*p->sender)(p->control, packet, len, p->data);
}
}
}
}
}
static void handle_direct_data (struct port *p, int hub, struct packet *packet, int len)
{
/* if we have an incoming port (we should) */
if(p != NULL) update_src(p, packet);
#ifdef INFO
else {
printlog(LOG_WARNING,"Unknown connection for packet, shouldn't happen.");
}
#endif
send_dst(p, packet, len, hub);
}
void handle_tap_data(int i, int fd, int hub)
{
struct packet packet;
int len;
struct port *port;
len = read(fd, &packet, sizeof(packet));
if(len < 0){
if(errno != EAGAIN) printlog(LOG_WARNING,"Reading tap data %s",strerror(errno));
return;
}
port=(struct port *)(g_fdsdata[i]);
handle_direct_data(port, hub, &packet, len);
}
int setup_port(int i, int fd, void (*sender)(int fd, void *packet, int len,
void *data), void *data, int data_len, int portgroup)
{
struct port *port;
port = malloc(sizeof(struct port));
if(port == NULL){
printlog(LOG_WARNING,"malloc %s",strerror(errno));
return(-1);
}
g_fdsdata[i]=port;
port->control = fd;
port->data = data;
port->data_len = data_len;
port->sender = sender;
port->pg = NULL;
if (portgroup != 0)
{
// search for other port on the same group
struct port *p=NULL;
for(i=g_minfds ; i<g_nfds ; i++) {
if (g_fdsdata != NULL) {
p=(struct port *)(g_fdsdata[i]);
if (p->pg != NULL && p->pg->ident == portgroup) {
port->pg=p->pg;
port->pg->counter++;
break;
}
}
}
if (port->pg == NULL)
{
port->pg=malloc(sizeof(struct portgroup));
if(port->pg == NULL){
printlog(LOG_WARNING,"malloc %s",strerror(errno));
return(-1);
}
port->pg->ident=portgroup;
port->pg->counter=1;
}
}
#ifdef INFO
printlog(LOG_INFO,"New connection %d",fd);
#endif
return(0);
}
struct sock_data {
int fd;
struct sockaddr_un sock;
};
static void send_sock(int fd, void *packet, int len, void *data)
{
struct sock_data *mine = data;
int err;
do {
err = sendto(mine->fd, packet, len, 0, (struct sockaddr *) &mine->sock,
sizeof(mine->sock));
} while (err == -1 && errno == EAGAIN);
if(err != len)
printlog(LOG_WARNING, "send_sock sending to fd %d %s", mine->fd, strerror(errno));
}
//static int match_sock(int port_fd, int data_fd, void *port_data,
//int port_data_len, void *data)
//{
//struct sock_data *mine = data;
//struct sock_data *port = port_data;
//
//if(port_data_len != sizeof(*mine)) return(0);
//return(!memcmp(&port->sock, &mine->sock, sizeof(mine->sock)));
//}
int handle_sock_data(int fd, int hub)
{
struct packet packet;
struct sock_data data;
int len, socklen = sizeof(data.sock);
struct port *p;
register int i;
struct sock_data *mine;
struct sock_data *port;
len = recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *) &data.sock, &socklen);
if(len <= 0){
if(len < 0 && errno != EAGAIN) printlog(LOG_WARNING,"handle_sock_data %s",strerror(errno));
if (len == 0) return 1;
else return 0;
}
data.fd = fd;
p=NULL;
for(i=g_minfds ; i<g_nfds ; i++) {
if (g_fdsdata != NULL) {
p=(struct port *)(g_fdsdata[i]);
mine=&data;
port=p->data;
//if(match_sock(p->control, fd, p->data, p->data_len, &data)) break;
if(p->data_len == sizeof(struct sock_data) &&
#ifdef __FreeBSD__
port->sock.sun_family == mine->sock.sun_family &&
!(strcmp(port->sock.sun_path, mine->sock.sun_path)))
#else
!(memcmp(&(port->sock), &mine->sock, sizeof(mine->sock))))
#endif
break;
}
}
if (i < g_nfds)
handle_direct_data(p,hub,&packet,len);
else
printlog(LOG_WARNING, "No port associated with descriptor %d", fd);
return 0;
}
int handle_sock_direct_data(int i, int fd, int hub)
{
struct packet packet;
struct sock_data data;
struct port *port;
int len, socklen = sizeof(data.sock);
len = recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *) &data.sock, &socklen);
if(len <= 0){
if(len < 0 && errno != EAGAIN) printlog(LOG_WARNING,"handle_sock_direct_data %s",strerror(errno));
if (len == 0) return 1;
else return 0;
}
data.fd = fd;
port=(struct port *)(g_fdsdata[i]);
handle_direct_data(port, hub, &packet, len);
return 0;
}
int setup_sock_port(int i, int fd, struct sockaddr_un *name, int data_fd, int portgroup)
{
struct sock_data *data;
data = malloc(sizeof(*data));
if(data == NULL){
printlog(LOG_WARNING,"setup_sock_port %s",strerror(errno));
return(-1);
}
*data = ((struct sock_data) { fd : data_fd,
sock : *name });
return(setup_port(i, fd, send_sock, data, sizeof(*data), portgroup));
}
syntax highlighted by Code2HTML, v. 0.9.1