eth_linux.c
author viric@llimona
Sun, 17 Feb 2008 22:27:55 +0100
changeset 88 a7f546938313
parent 87 be4ee314545c
permissions -rw-r--r--
Adding port number on the ethernet protocol.

/* Copyright Coraid, Inc. 2006.  All rights reserved. */
/* Modified the original with BSD license by Lluis Batlle */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <features.h>    /* for the glibc version number */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>     /* the L2 protocols */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>   /* The L2 protocols */
#endif

#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/fs.h>
#include <sys/stat.h>

#include "main.h"
#include "eth_linux.h"

enum {
    ETH_SERVER_PROTO = 0xCACA,
    ETH_CLIENT_PROTO = 0xCACC
};

static int fd;
static enum Eth_type eth_type;
static char srcaddr[6];
static int debug = 0;

static void dump(unsigned char *buf, int len)
{
  int i;
  for(i=0; i < len; ++i)
  {
    printf("%x ", (int) buf[i]);
  }
  putchar('\n');
}

// return the index of device 'name'
static int getindx(int s, char *name)
{
    struct ifreq xx;
    int n;

    strncpy(xx.ifr_name, name, sizeof(xx.ifr_name));
    xx.ifr_name[sizeof(xx.ifr_name)-1] = 0;
    n = ioctl(s, SIOCGIFINDEX, &xx);
    if (n == -1)
        return -1;
    return xx.ifr_ifindex;
}

// get us a raw connection to an interface
int eth_open(char *eth, enum Eth_type type)
{
    int i, n;
    struct sockaddr_ll sa;
    struct ifreq xx;
    short int proto_num;

    if (type == ETH_SERVER)
    {
        dump_line("eth_open for server\n");
        proto_num = htons(ETH_SERVER_PROTO);
    }
    else if (type == ETH_CLIENT)
    {
        dump_line("eth_open for client\n");
        proto_num = htons(ETH_CLIENT_PROTO);
    }
    else
        return -1;

    /* Note the type server or client for this process */
    eth_type = type;

    memset(&sa, 0, sizeof sa);
    fd = socket(PF_PACKET, SOCK_DGRAM, proto_num);
    if (fd == -1) {
        perror("got bad socket");
        return -1;
    }
    i = getindx(fd, eth);
    sa.sll_family = AF_PACKET;
    sa.sll_protocol = proto_num;
    sa.sll_ifindex = i;
    n = bind(fd, (struct sockaddr *)&sa, sizeof sa);
    if (n == -1) {
        perror("bind funky");
        return -1;
    }
    strncpy(xx.ifr_name, eth, sizeof(xx.ifr_name));
    xx.ifr_name[sizeof(xx.ifr_name)-1] = 0;
    n = ioctl(fd, SIOCGIFHWADDR, &xx);
    if (n == -1) {
        perror("Can't get hw addr");
        return -1;
    }
    memmove(srcaddr, xx.ifr_hwaddr.sa_data, 6);
    return fd;
}

int eth_recv(char *buf, int len, char *partner_mac)
{
    struct sockaddr_ll sa;
    int sa_len = sizeof(sa);
    int res;
    dump_line("eth_recv\n");
    res = recvfrom(fd, buf, len, 0, (struct sockaddr *) &sa, &sa_len);
    if (debug) {
        printf("read %d bytes\r\n", res);
        dump(buf, res);
    }
    /* Assume sa_len will be ok */
    if (partner_mac)
        memcpy(partner_mac, sa.sll_addr, sa.sll_halen);
    return res;
}

int eth_send(char *dev, char *mac, void *p, int len)
{
    struct sockaddr_ll sa;
    int i;

    i = getindx(fd, dev);
    sa.sll_family = AF_PACKET;
    if (eth_type == ETH_SERVER)
    {
        dump_line("eth_send from server to client\n");
        sa.sll_protocol = htons(ETH_CLIENT_PROTO);
    }
    else if (eth_type == ETH_CLIENT)
    {
        dump_line("eth_send from client to server\n");
        sa.sll_protocol = htons(ETH_SERVER_PROTO);
    }
    else
        return -1;
    sa.sll_ifindex = i;
    sa.sll_halen = 6;
    memcpy(sa.sll_addr, mac, 6);
    if (debug) {
        printf("sending %d bytes to eth_protocol %hi\r\n", len, sa.sll_protocol);
        dump(p, len);
    }
    if (len > 1500)
        len = 1500;
    return sendto(fd, p, len, 0, (struct sockaddr*) &sa, sizeof(sa));
}