view src/lwwire-packet.c @ 14:e4d98cbf95eb

packet: add get value semantics
author Brett Gordon
date Tue, 13 Dec 2016 11:26:37 -0500
parents f8226a33698d
children
line wrap: on
line source

/*
  Packet extension for lwwire

  Talks Layer 2, Ethernet Frames, sends them to a Linux TAP/TUN device.

  TODO:
  * better (any) RXMTU and TXMTU, Queue Length handling

*/

#include <stdio.h>
#include "lwwire.h"

#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>


#define MAX_FRAME 1526
static int tunfd = -1;
static char tundev[10];
static unsigned char opacket[MAX_FRAME];
static unsigned char packet[MAX_FRAME];
static int tunz = -1;
static unsigned char *packet_ptr;
static int csize = 511;
static int rxmtu = 1500;
static int txmtu = 1500;
static int qlen = 10;

/*

  NOTE: the __attribute__((used)) tags are there to prevent the functions
  from being optimized out as would often be the case with functions declared
  static. This could be avoided by removing the static declarations. However,
  by declaring everything static except for the lwwire_register() function,
  we avoid polluting the global namespace and possibly having conflicts with
  other extensions.
*/


/* Allocate (open) a new tap device
   Take symbolic name of device and tap/tun flags. Returns fd, or < 0 on
   error.
*/
static int tun_alloc(char *dev, int flags)
{
    struct ifreq ifr;
    int fd, err;
    char *clonedev = "/dev/net/tun";

    /* open the clone device */
    if ((fd = open(clonedev, O_RDWR)) < 0)
    {
	perror("tun_alloc");
	return fd;
    }
    /* preparation of the struct ifr, of type "struct ifreq" */
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = flags;
    if (*dev)
    {
	strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    }
    /* try to create the device */
    if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0)
    {
	perror("tun_alloc");
	close(fd);
	return err;
    }
    /* write acquired tap device name back to struct */
    strcpy(dev, ifr.ifr_name);

    nonblock(fd);
    return fd;
}


/* Tell client how many bytes to expect for the next read. */
static int tun_proto_poll()
{
    unsigned char buf[2];
    while (1)
    {
	if (tunz < 1)
	    tunz = read(tunfd, packet, MAX_FRAME);
	if (tunz < rxmtu)
	    break;
	else
	    tunz = 0;
    }
    buf[0] = (unsigned char)(tunz >> 8);
    buf[1] = (unsigned char)(tunz & 0xff);
    lwwire_write(buf,2);
    packet_ptr = packet;
    return 0;
}

/* Drop the current packet */
static int tun_proto_drop()
{
    tunz = 0;
    return 0;
}


/* receive *entire* frame */
static int tun_proto_recv()
{
    if (tunz < 1)
	return -1;
    lwwire_write(packet, tunz);
    tunz = 0;
    return 0;
}

/* receive next chunk (upto 511 bytes) of frame */
static int tun_proto_recv_chunk()
{
    int s;
    if (csize < tunz)
	s = csize;
    else
	s = tunz;
    lwwire_write(packet_ptr, s);
    packet_ptr += s;
    tunz -= s;
    return 0;
}

/* send a frame */
static int tun_proto_send()
{
    int len;
    unsigned char buf[2];
    if (lwwire_read(buf, 2) < 0)
	return -1;
    len = (buf[0]<<8) + buf[1];
    if (lwwire_read(opacket, len) < 0)
	return -1;
    if (write(tunfd, opacket, len) < 0)
	return -1;
    return 0;
}


/* set/return receive mtu */
static int tun_proto_setrxmtu()
{
    unsigned int val;
    unsigned char buf[2];
    if (lwwire_read(buf, 2) < 0)
	return -1;
    val = (buf[0] << 8) + buf[1];
    if (val)
    {
	rxmtu = val;
    }
    else
    {
	buf[0] = rxmtu >> 8;
	buf[1] = rxmtu & 255;
    }
    lwwire_write(buf, 2);
    return 0;
}

/* set/return transmit mtu */
static int tun_proto_settxmtu()
{
    unsigned int val;
    unsigned char buf[2];
    if (lwwire_read(buf, 2) < 0)
	return -1;
    val = (buf[0] << 8) + buf[1];
    if (val)
    {
	txmtu = val;
    }
    else
    {
	buf[0] = txmtu >> 8;
	buf[1] = txmtu & 255;
    }
    lwwire_write(buf, 2);
    return 0;
}

/* set/return queue length */
static int tun_proto_setqlen()
{
    unsigned char buf[1];
    if (lwwire_read(buf, 1) < 0)
	return -1;
    if (buf[0])
    {
	qlen = buf[0];
    }
    else
    {
	buf[0] = qlen;
    }
    lwwire_write(buf, 1);
    return 0;
}



// this is called to handle an operation for this extension
// it should return nonzero if the operation code (op) is not
// supported. Otherwise, it should implement the operation codes.
__attribute__((used)) static int packet_handler(int op)
{
    switch (op)
    {
    case 0:
	return tun_proto_poll();
    case 1:
	return tun_proto_recv();
    case 2:
	return tun_proto_send();
    case 3:
	return tun_proto_drop();
    case 4:
	return tun_proto_setrxmtu();
    case 5:
	return tun_proto_setqlen();
    case 6:
	return tun_proto_settxmtu();
    case 7:
	return tun_proto_recv_chunk();
    default:
	return -1;
    }
    return 0;
}

// this will be called when the extension is enabled
// return nonzero if enabling the extension cannot happen for some reason
__attribute__((used)) static int packet_enable(void)
{
    if (tunfd < 0)
    {
	strcpy(tundev, "tap0");
	tunfd = tun_alloc(tundev, IFF_TAP | IFF_NO_PI);
	if (tunfd < 0) return -1;
	tunz = -1;
    }
    return 0;
}

// this will be called when an extension is disabled
// if this extension cannot be disabled after it is enabled, it should return
// nonzero; otherwise, this MUST succeed
__attribute__((used)) static int packet_disable(void)
{
    close(tunfd);
    tunfd = -1;
    return 0;
}

// this call should do anything needed to disable the extension
// and anything special that might be needed on a protocol reset
// it will only be called if the extension is enabled when the
// reset occurs. Even if the extension isn't disablable, this call
// MUST disable it and reset things as if the extension was never
// enabled.
__attribute__((used)) static void packet_reset(void)
{
    packet_disable();
}

int lwwire_register(void)
{
    fprintf(stderr, "Registering PACKET extension\n");
    return lwwire_register_extension(0x01, packet_handler, packet_enable, packet_disable, packet_reset);
}