Code Search for Developers
 
 
  

ip_connector.c from EmStar at Krugle


Show ip_connector.c syntax highlighted

/* ex: set tabstop=2 expandtab shiftwidth=2 softtabstop=2: */

/*
 *
 * Copyright (c) 2003 The Regents of the University of California.  All 
 * rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Neither the name of the University nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 

char ip_connector_c_id[] = "$Id: ip_connector.c,v 1.10 2005/10/22 00:28:59 karenyc Exp $";

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>			// for offsetof

#include "libmisc/misc.h"
#include "link/link.h"
#include "libdev/packet_dev.h"
#include "emrun/emrun.h"


#include <fcntl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/if_tun.h>
#include <libdev/glib_dev.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define CORRECT_NET     0x00010000    //0.1.0.0
#define BOGUS_NET       0x00020000    //0.2.0.0
#define CHECK_MASK      0x00FF0000    //0.255.0.0
#define BOGUS_OCTET     2       // the bogus net as an octet (i.e, 2)
#define BOGUS_OCTET_OFFSET  1   // offset when IP is a char array

#define HWADDR_SIZE   6     // ethernet address size, in bytes
#define IPADDR_SIZE   4     // ip address size, in bytes

#define PKT_ERR                 -1
#define PKT_DONT_FWD            0
#define PKT_FWD_TO_TAP          1
#define PKT_FWD_TO_RAW          2
#define PKT_FWD_TO_TAP_AND_RAW  3

#define ARP_NORMAL_IFACE  1
#define ARP_BOGUS_IFACE   2
#define ARP_NOT_MINE      0

// first and second octets are pre-defined, the rest are created from the
// interface ids
#define IP_FIRST_OCTET  10
#define IP_SECOND_OCTET 1

#define ARP_PKTLEN (sizeof(struct ether_header) + sizeof(struct arphdr) + sizeof(eth_arp_t))

// copied off of if_arp.h (it's ifdefed out over there)
typedef struct _eth_arp {
  uint8_t ar_sha[HWADDR_SIZE];
  uint8_t ar_sip[IPADDR_SIZE];
  uint8_t ar_tha[HWADDR_SIZE];
  uint8_t ar_tip[IPADDR_SIZE];
} eth_arp_t;


typedef struct ipconn_state {
  /* state needed for the "lower" (mote) side of the interface */
  char *uses_name;
  char *provides_name;


  /* state needed for the link-interface side */
  if_id_t link_if_id;
  lu_context_t *link_ref;


  /* other state */
  uint16_t link_mtu;

  buf_t *sendbuf;
  buf_t *recvbuf;


  g_event_t *tap_fd_cb_ref;
  g_event_handler_cb_t tap_fd_cb;

  int tap_fd;     // the tun/tap fd

  struct ifreq *ifr;
  char real_hwaddr[HWADDR_SIZE];
  char noarp_hwaddr[HWADDR_SIZE];
  char ifaddr[IPADDR_SIZE];

  char ifaddr_bogus[IPADDR_SIZE];
  char ifaddr_dotted[20];

  int bogus_on;
  int arp_on;

  int raw_sd;

  char *pkt_buf;
  int pkt_len;

} ipconn_state_t;



int open_tap(ipconn_state_t *ipc);
int close_tap(ipconn_state_t *ipc);
int notify_rw(void *data, int fd, int fusd_condition, g_event_t *event);

int open_link(ipconn_state_t *ipc);
int link_pkt_rcvd(lu_context_t *link, link_pkt_t *link_pkt, int datalen);
int link_pkt_writable(lu_context_t *link);
int send_eth_pkt_over_link_dev(ipconn_state_t *ipc, 
    char *pktbuf, uint16_t len);
int send_arp_reply(ipconn_state_t *ipc, struct ether_header *r_ethhdr,
    int bogus);
int create_raw_socket(ipconn_state_t *ipc);
int create_packet_socket();
int handle_arp_packet(ipconn_state_t *ipc, struct ether_header *ethhdr, 
    char *data, int datalen);
int handle_ip_packet(ipconn_state_t *ipc, struct ether_header *ethhdr, 
    char *data, int datalen);
struct ether_header *generate_arp_reply(ipconn_state_t *ipc, 
    struct ether_header *request_ethhdr, int bogus);
int fwd_data_to_tap(ipconn_state_t *ipc, char *data, int length);
char *create_bogus_ip_from_real_ip(char *ip);
char *get_hwaddr_from_ip(char *ip);
int fake_arp(ipconn_state_t *ipc, struct ether_header *ethhdr);
int check_ip(ipconn_state_t *ipc, eth_arp_t *etharp);
int check_hwaddr_of_eth_header(ipconn_state_t *ipc, 
    struct ether_header *ethhdr);
int check_eth_bcast_addr(char *dhost);






/*****************************************************************/

/* Callback when we are asked (by emrun) to shut down */
static void ipconn_shutdown(void *data)
{
  ipconn_state_t *ipc = (ipconn_state_t *)data;
  close_tap(ipc);
  elog(LOG_DEBUG(0), "shutting down IP connector");
  exit(0);
}


void usage(void)
{
  fprintf(stderr,
          "ipconn release: %s\n"
	  "\n"
	  "usage: ipconn [--uses <link_name>] [--provides <ethernet interface name>]"
    " --bogus --arp\n"
    "\n"
    "       -b: turns on 'bogus' mode. In this mode, interfaces have a \n"
    "           normal IP address as well as a 'bogus' one. The bogus\n"
    "           address is 10.2.x.x while the normal address is 10.1.x.x\n"
    "           Interfaces use the bogus address to fool the kernel into\n"
    "           thinking that packets are arriving from some other machine\n"
    "           NOTE: when running in simulation mode, bogus mode is ON\n"
    "       -a: turns on ARP mode. In this mode, ARP packets are sent\n"
    "           and received normally through the link interfaces\n."
    "           If ARP mode is OFF, arp packets are faked and do not\n"
    "           get sent over the link\n. Default is OFF\n"
	  "\n"
	  ,CVSTAG
	  );

  exit(1);
}


int main(int argc, char *argv[])
{
  ipconn_state_t *ipc = NULL;
  int help=0;
  buf_t *if_name;

  emrun_opts_t emrun_opts = {
    shutdown: ipconn_shutdown,
    data: ipc,
  };



  ipc = malloc(sizeof(ipconn_state_t));
  memset(ipc, 0, sizeof(ipconn_state_t));
  emrun_opts.data = ipc;

  /** generic init **/
  misc_init(&argc, argv, CVSTAG);

  // the default name is motenic+node_id
  // this enables ipconn to run in sim_mode
  if_name = buf_new();
  bufprintf(if_name, "motenic%d", my_node_id);

  /********** Parse command-line arguments ************/

  ipc->uses_name = link_parse_uses(&argc, argv, "mote0");
  ipc->provides_name = link_parse_provides(&argc, argv, if_name->buf);
  ipc->bogus_on = misc_parse_out_switch(&argc, argv, "bogus", 'b');
  ipc->arp_on = misc_parse_out_switch(&argc, argv, "arp", 'a');
  help = misc_parse_out_switch(&argc, argv, "help", 'h');
  if (help==1) {
    usage();
  }

  if (ipc->arp_on) {
    elog(LOG_DEBUG(0), "ARP mode ON");
  }

  if (ipc->bogus_on) {
    elog(LOG_DEBUG(0), "Bogus mode ON");
  }

  // if we are in sim mode, bogus should be always on
  if (in_sim) {
    ipc->bogus_on = 1;
  }
  

  emrun_init(&emrun_opts);

  // first open the underlying link dev
  open_link(ipc);
  // now open the tap
  open_tap(ipc);

  // create the raw socket
  ipc->raw_sd = create_raw_socket(ipc);
  //ipc->raw_sd = create_packet_socket();

  // start the beer flow!
  g_main();
  elog(LOG_CRIT, "event loop exited unexpectedly!");
  return 1;
}


int open_tap(ipconn_state_t *ipc)
{
  struct ifreq ifr = {};
  long owner = geteuid();
  buf_t *buf;
  int n=0;
  int skfd=-1;

  buf = buf_new();

  if (ipc==NULL) {
    elog(LOG_ERR, "NULL ipc pointer");
    exit(1);
  }

  // modprobe tun
  system("/sbin/modprobe tun");

  if ((ipc->tap_fd = open("/dev/net/tun", O_RDWR)) < 0) {
    elog(LOG_ERR, "Can't open /dev/net/tun: %m");
    exit(1);
  }


  // set the interface request flags
  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

  // set the name
  strncpy(ifr.ifr_name, ipc->provides_name, sizeof(ifr.ifr_name) - 1);

  // set the interface---ioctl
  if (ioctl(ipc->tap_fd, TUNSETIFF, (void *)&ifr) < 0) {
    elog(LOG_ERR, "ioctl(TUNSETIFF) error: %m");
    if (errno==EBUSY) {
      // hmm, device exists and is busy...probably stale
      // remove it and try again
      elog(LOG_ERR, "Got EBUSY, closing and reopening");
      close_tap(ipc);
      if (ioctl(ipc->tap_fd, TUNSETIFF, (void *)&ifr) < 0) {
        elog(LOG_ERR, "ioctl(TUNSETIFF) error: %m");
        elog(LOG_ERR, "Reopen failed, giving up");
        exit(1);
      }
    } else {
      exit(1);
    }
  }

  // set persistent mode---ioctl
  if (ioctl(ipc->tap_fd, TUNSETPERSIST, 1) < 0) {
    elog(LOG_ERR, "ioctl(TUNSETPERSIST) error: %m");
    exit(1);
  }

  // set ownership---ioctl
  if (ioctl(ipc->tap_fd, TUNSETOWNER, owner) < 0) {
    elog(LOG_ERR, "ioctl(TUNSETOWNER) error: %m");
    exit(1);
  }

  if (ioctl(ipc->tap_fd, TUNSETNOCSUM, 1) < 0) {
    elog(LOG_ERR, "ioctl(TUNSETNOCSUM) error: %m");
    exit(1);
  }

  //fcntl(ipc->tap_fd, F_SETFL, O_NONBLOCK | O_ASYNC);

  //open a basic socket
  if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    elog(LOG_ERR, "socket() failed: %m");
    exit(1);
  }

  if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) {
    elog(LOG_ERR, "ioctl failed: %m");
    exit(1);
  } else {
    char *hwaddr = ifr.ifr_hwaddr.sa_data;
    elog(LOG_DEBUG(0), "Interface's HW address is %2.2x:%2.2x:%2.2x:%2.2x"
        ":%2.2x:%2.2x.",
        (uint8_t)hwaddr[0], (uint8_t)hwaddr[1], (uint8_t)hwaddr[2], 
        (uint8_t)hwaddr[3], (uint8_t)hwaddr[4], (uint8_t)hwaddr[5]);
    memcpy(ipc->real_hwaddr, hwaddr, HWADDR_SIZE);
  }

  // Register the FD with glib
  ipc->tap_fd_cb = notify_rw;
 
  if (g_event_add(ipc->tap_fd, FUSD_NOTIFY_INPUT /*| FUSD_NOTIFY_OUTPUT*/,
        ipc->tap_fd_cb, ipc, NULL, &ipc->tap_fd_cb_ref) < 0) {

    elog(LOG_ERR, "Cannot add event: %m");
    exit(1);
  }

  // system is LAME but I was unsuccessful in doing it the 'right' way i.e.
  // by copying ifconfig functionality, so...
  bufprintf(buf, "/sbin/ifconfig %s arp %d.%d.%d.%d netmask 255.0.0.0 "
      "broadcast 10.255.255.255 mtu %d up\n",
      ifr.ifr_name,
      IP_FIRST_OCTET,
      IP_SECOND_OCTET,
      (uint8_t)(((ipc->link_if_id >> 8) & 0xFF)),
      (uint8_t)(((ipc->link_if_id) & 0xFF)),
      // subtract 14, the ethernet header size, from the mtu since
      // what we link to is an 'ethernet' device
      (uint16_t)(((ipc->link_mtu-14) & 0xFFFF)));
      //(uint8_t)(((ipc->link_if_id >> 8) & 0xFF)));

  n=system(buf->buf);

  if (n==0) {
    elog(LOG_DEBUG(0), "Successufully configured TAP interface '%s' "
      "with IP addr %d.%d.%d.%d and MTU %d", 
      ifr.ifr_name, 
      IP_FIRST_OCTET,
      IP_SECOND_OCTET,
      (uint8_t)(((ipc->link_if_id >> 8) & 0xFF)),
      (uint8_t)(((ipc->link_if_id) & 0xFF)),
      (uint16_t)(((ipc->link_mtu-14) & 0xFFFF)));

  } else {
    elog(LOG_ERR, "Couldn't create interface; "
        "make sure you're running as root");
    exit(1);
  }

  if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) {
    elog(LOG_ERR, "ioctl failed: %m");
    exit(1);
  } else {
    memcpy(ipc->ifaddr, &ifr.ifr_addr.sa_data[2], IPADDR_SIZE);
    elog(LOG_NOTICE, "Interface normal IP address is %d.%d.%d.%d",
        ipc->ifaddr[0], ipc->ifaddr[1], ipc->ifaddr[2], ipc->ifaddr[3]);

    // POLICY NOTE: The bogus address is the ip address that we have with the
    // only difference that we add '1' to the second octet
    // so if an interface has 10.1.0.1 as its normal address, its bogus
    // address will be 10.2.0.1


    memcpy(ipc->ifaddr_bogus, ipc->ifaddr, IPADDR_SIZE);
    if (ipc->bogus_on) {
      ipc->ifaddr_bogus[1]=BOGUS_OCTET;

      elog(LOG_NOTICE, "Interface bogus IP address is %d.%d.%d.%d",
          ipc->ifaddr_bogus[0], ipc->ifaddr_bogus[1], 
          ipc->ifaddr_bogus[2], ipc->ifaddr_bogus[3]);

      sprintf(ipc->ifaddr_dotted, "%d.%d.%d.%d",
          ipc->ifaddr[0], ipc->ifaddr[1], ipc->ifaddr[2], ipc->ifaddr[3]);

    }
  }


  // copy the ifr struct to the state struct
  ipc->ifr = malloc(sizeof(struct ifreq));
  memcpy(ipc->ifr, &ifr, sizeof(struct ifreq));

  // if ARP mode is OFF, set our noarp_hwaddr based on our IP
  if (ipc->arp_on==0) {
    // set the HWADDR
    ipc->noarp_hwaddr[0] = 0x00;
    ipc->noarp_hwaddr[1] = 0x01;
    ipc->noarp_hwaddr[2] = 0x02;
    ipc->noarp_hwaddr[3] = 0x03;
    ipc->noarp_hwaddr[4] = ipc->ifaddr[2];
    ipc->noarp_hwaddr[5] = ipc->ifaddr[3];

    elog(LOG_NOTICE, "Interface's NOARP HW address is %2.2x:%2.2x:%2.2x:%2.2x"
        ":%2.2x:%2.2x.",
        (uint8_t)ipc->noarp_hwaddr[0], (uint8_t)ipc->noarp_hwaddr[1], 
        (uint8_t)ipc->noarp_hwaddr[2], (uint8_t)ipc->noarp_hwaddr[3],
        (uint8_t)ipc->noarp_hwaddr[4], (uint8_t)ipc->noarp_hwaddr[5]);
  }
        


  buf_free(buf);
  return 0;

}



int close_tap(ipconn_state_t *ipc)
{
  if (ipc==NULL) {
    elog(LOG_ERR, "NULL ipc pointer");
    exit(1);
  }


  if (ioctl(ipc->tap_fd, TUNSETPERSIST, 0) < 0) {
    elog(LOG_ERR, "ioctl(TUNSETPERSIST) error: %m");
    exit(1);
  }


  return 0;
}


int fwd_data_to_tap(ipconn_state_t *ipc, char *data, int length)
{
  int n=0;

  if (data==NULL) {
    elog(LOG_ERR, "NULL data pointer");
    exit(1);
  }

  n=write(ipc->tap_fd, data, length);

  if (n<0) {
    elog(LOG_ERR, "Can't send packet to TAP interface: %m");
  } else {
    elog(LOG_DEBUG(0), "Sent %d bytes to TAP interface", n);
    elog_raw(LOG_DEBUG(2), data, length);
  }
  return n;
}



int fake_arp(ipconn_state_t *ipc, struct ether_header *ethhdr)
{
  struct ether_header *fake_ehdr=NULL;
  int bogus=0;

  if (ipc==NULL || ethhdr == NULL) {
    elog(LOG_ERR, "NULL pointer(s)");
    exit(1);
  }

  fake_ehdr = generate_arp_reply(ipc, ethhdr, bogus);
  // forward to tap. This is an ARP REPLY pkt so we know its length
  fwd_data_to_tap(ipc, (char *)fake_ehdr, ARP_PKTLEN);
  // free the generated ethernet frame
  free(fake_ehdr);
  return 1;
}



int notify_rw(void *data, int fd, int fusd_condition, g_event_t *event)
{
  ipconn_state_t *ipc = (ipconn_state_t *)data;
  char readbuf[4096]={0};     // ideally this should be the MTU of the
                              // interface
  struct ether_header *ethhdr;
  int n=0;

  switch (fusd_condition) {

    case FUSD_NOTIFY_INPUT:
      n = read(fd, readbuf, sizeof(readbuf));
      elog(LOG_DEBUG(0), "Read %d bytes from TAP fd %d", n, fd);
      elog_raw(LOG_DEBUG(2), readbuf, n);
      ethhdr = (struct ether_header *)readbuf;
      if (ntohs(ethhdr->ether_type)==ETHERTYPE_ARP) {
        elog(LOG_DEBUG(0), "GOT ARP, read size is %d", n);
        // DONT send the ARP if arps are faked
        if (ipc->arp_on==0) {
          elog(LOG_DEBUG(0), "ARPs are faked, packet not sent");
          // create the arp reply
          fake_arp(ipc, ethhdr); 

          return EVENT_RENEW;
        }
      }
        

      if (ipc->pkt_buf==NULL) {
        int m=0;
        // pkt buf is empty, malloc n bytes and copy the packet in it
        ipc->pkt_buf = malloc(n);
        ipc->pkt_len = n;
        memcpy(ipc->pkt_buf, readbuf, n);
        // now try to send
        m=send_eth_pkt_over_link_dev(ipc, ipc->pkt_buf, ipc->pkt_len);
        if (m<0) {
          elog(LOG_ERR, "Can't send packet over link dev: %m");
        }
      } else {
        elog(LOG_ERR, "IPC pkt buffer NOT NULL!");
      }

      break;

    default:
      elog(LOG_ERR, "Got unhandled FUSD CONDITION %d", fusd_condition);
      return EVENT_DONE;
      break;

  }

  return EVENT_RENEW;
}


char *create_bogus_ip_from_real_ip(char *ip)
{
  char *bogus = malloc(IPADDR_SIZE);
  memcpy(bogus, ip, IPADDR_SIZE);
  bogus[BOGUS_OCTET_OFFSET] = BOGUS_OCTET;

  return bogus;
}


char *get_hwaddr_from_ip(char *ip)
{
  char *hwaddr = malloc(HWADDR_SIZE);

  // array offsets, yea i know it's nasty...
  hwaddr[0] = 0x00;
  hwaddr[1] = 0x01;
  hwaddr[2] = 0x02;
  hwaddr[3] = 0x03;
  hwaddr[4] = ip[2];
  hwaddr[5] = ip[3];

  return hwaddr;
}


char *create_ip_from_hwaddr(char *hwaddr)
{
  char *ip = malloc(IPADDR_SIZE);
  ip[0] = IP_FIRST_OCTET;
  ip[1] = IP_SECOND_OCTET;
  ip[2] = hwaddr[4];
  ip[3] = hwaddr[5];

  return ip;
}


struct ether_header *generate_arp_reply(ipconn_state_t *ipc, 
    struct ether_header *request_ethhdr, int bogus)
{
  char *sendbuf=NULL;

  struct ether_header *reply_ethhdr=NULL;
  struct arphdr *request_ahdr=NULL;
  struct arphdr *reply_ahdr=NULL;
  eth_arp_t *request_etharp=NULL;
  eth_arp_t *reply_etharp=NULL;
  char *hwaddr=NULL;
  char *ip=NULL;
  char *bogus_ip=NULL;

/*
  int pktlen = sizeof(struct ether_header) + sizeof(struct arphdr)
    + sizeof(eth_arp_t);
*/
  int pktlen = ARP_PKTLEN;

  // allocate mem for our sendbuf
  sendbuf = malloc(pktlen);
  memset(sendbuf, 0, pktlen);

  // cast the sending headers
  request_ahdr = (struct arphdr *)((char *)request_ethhdr + 
      sizeof(struct ether_header));
  request_etharp= (eth_arp_t *)((char *)request_ethhdr +
      sizeof(struct ether_header) + sizeof(struct arphdr));


  // cast the reply headers
  reply_ethhdr = (struct ether_header *)(sendbuf);
  reply_ahdr = (struct arphdr *)(sendbuf + sizeof(struct ether_header));
  reply_etharp = (eth_arp_t *)(sendbuf + sizeof(struct ether_header)
                                 + sizeof(struct arphdr));

  // assign the hwaddr pointer to the right address, depending on
  // whether we use real arp or not
  if (ipc->arp_on==1) {
    hwaddr = ipc->real_hwaddr;
    ip = ipc->ifaddr;
    bogus_ip = ipc->ifaddr_bogus;
  } else {
    ip = (char *)request_etharp->ar_tip;
    bogus_ip = create_bogus_ip_from_real_ip(ip);
    hwaddr = get_hwaddr_from_ip(ip);
  }


  // construct the ethernet header
  // the dhost of the reply header is our hw_addr (as created by arp)
  memcpy(reply_ethhdr->ether_dhost, request_ethhdr->ether_shost, HWADDR_SIZE);
  // the shost of the reply header is either our real hwaddr OR
  // the noarp_hwaddr of the node that has the requested ip address
  memcpy(reply_ethhdr->ether_shost, hwaddr, HWADDR_SIZE);
  reply_ethhdr->ether_type = request_ethhdr->ether_type;


  // construct the arp header
  memcpy(reply_ahdr, request_ahdr, sizeof(struct arphdr));
  reply_ahdr->ar_op = htons(ARPOP_REPLY);

  //construct the arp payload--eth_arp_t
  
  // Sender MAC address is either our hwaddr or the 'mapped' one
  memcpy(reply_etharp->ar_sha, hwaddr, HWADDR_SIZE);
  // Sender IP address -> our ip addr
  if (bogus) {
    memcpy(reply_etharp->ar_sip, bogus_ip, IPADDR_SIZE);
  } else {
    memcpy(reply_etharp->ar_sip, ip, IPADDR_SIZE);
  }
  // Target MAC address -> the mac address of the requestor
  memcpy(reply_etharp->ar_tha, request_etharp->ar_sha, HWADDR_SIZE);
  // Target IP address -> the IP address of the requestor
  memcpy(reply_etharp->ar_tip, request_etharp->ar_sip, IPADDR_SIZE);


  // free hwaddr when arps are off since get_hwaddr_from_ip mallocs
  if (ipc->arp_on==0) {
    free(hwaddr);
    free(bogus_ip);
  }

  
  return (struct ether_header *)sendbuf;
}


int open_link(ipconn_state_t *ipc)
{
  lu_opts_t link_opts = {
    opts: {
      name: ipc->uses_name,
      q_opts: {
        outq_len: 10,
      },
      data: (void *)ipc,
    },
    receive: link_pkt_rcvd,
    writable: link_pkt_writable,
    //status_notify:              // will add this when I understand what it
                                  // does:)
  };

  if (ipc->uses_name == NULL) {
    elog(LOG_ERR, "NULL link name");
    exit(1);
  }

  if (lu_open(&link_opts, &(ipc->link_ref)) < 0) {
    elog(LOG_CRIT, "Can't open %s: %m", link_name(&link_opts.opts, NULL));
    exit(1);
  } else {
    elog(LOG_DEBUG(0), "Using link device %s", lu_name(ipc->link_ref, NULL));
  }

  if (lu_get_if_id(ipc->link_ref, &ipc->link_if_id) < 0) {
    elog(LOG_ERR, "Unable to get interface id for link %s",
        lu_name(ipc->link_ref, NULL));
      exit(1);
  }

  if (lu_get_mtu(ipc->link_ref, &ipc->link_mtu) < 0) {
    elog(LOG_ERR, "Unable to get mtu for link %s",
        lu_name(ipc->link_ref, NULL));
    exit(1);
  }


  return 0;
}


unsigned short in_cksum(unsigned short *addr,int len)
{
  register int sum = 0;
  u_short answer = 0;
  register u_short *w = addr;
  register int nleft = len;
  /*
   * * * Our algorithm is simple, using a 32 bit accumulator (sum), we add
   * * * sequential 16 bit words to it, and at the end, fold back all the
   * * * carry bits from the top 16 bits into the lower 16 bits.
   * * */
  while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
  }

  /* mop up an odd byte, if necessary */
  if (nleft == 1) {
    *(u_char *)(&answer) = *(u_char *)w ;
    sum += answer;
  }

  /* add back carry outs from top 16 bits to low 16 bits */
  sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
  sum += (sum >> 16); /* add carry */
  answer = ~sum; /* truncate to 16 bits */

  return(answer);
}


int check_eth_bcast_addr(char *dhost)
{
  int i=0;

  if (dhost==NULL) {
    elog(LOG_ERR, "NULL dhost pointer!");
    exit(1);
  }

  for (i=0; i<HWADDR_SIZE; i++) {
    if ((uint8_t)dhost[i] != 0xFF) {
      return 0;
    }
  }

  // if we come here, the check has succeeded
  return 1;

}


int check_hwaddr_of_eth_header(ipconn_state_t *ipc, 
    struct ether_header *ethhdr)
{
  int mismatch=0;
  int i=0;

  if (ipc==NULL || ethhdr==NULL) {
    elog(LOG_ERR, "NULL pointer(s)");
    exit(1);
  }

  if (ipc->arp_on==1) {
    elog(LOG_ERR, "ARPs are on, ignoring eth header");
    return 1;
  }

  // check ether_dhost with our noarp_hwaddr address
  for (i=0; i<HWADDR_SIZE; i++) {
    if (ethhdr->ether_dhost[i] != ipc->noarp_hwaddr[i]) {
      // found mismatch, set flag and break
      mismatch=-1;
      break;
    }
  }

  if (mismatch==0) {
    // addresses match! we now need to copy our REAL hwaddr
    // to ether_dhost
    memcpy(ethhdr->ether_dhost, ipc->real_hwaddr, HWADDR_SIZE);
  }

  return mismatch;

}



  

int link_pkt_rcvd(lu_context_t *link, link_pkt_t *link_pkt, int datalen)
{
  ipconn_state_t *ipc = (ipconn_state_t *)lu_data(link);
  struct ether_header *ethhdr;
  struct iphdr *iphdr;
  int n=0;
  int process_result=-1;
  struct sockaddr_in sin;     // for the raw socket

  if (link_pkt == NULL) {
    elog(LOG_ERR, "NULL link_pkt");
    exit(1);
  }

  if ((link_pkt->type != PKT_TYPE_ETH)
      && (link_pkt->type != PKT_TYPE_IP)) {
    elog(LOG_WARNING, "Got a non-IP/ETH type packet!");
    goto done;
  }


  ethhdr = (struct ether_header *)(link_pkt->data);
  elog(LOG_DEBUG(0), "Ethernet header type is %hx",
      ntohs(ethhdr->ether_type));

  // if arps are off, check the hw address, extract the ip, 
  // make sure it matches our own and then process the packet
  // if it doesn, we drop it 
  // or we can still let it go up but the kernel will throw it away
  if (ipc->arp_on==0) {
    if ((link_pkt->dst.id != LINK_BROADCAST) &&
	(check_hwaddr_of_eth_header(ipc, ethhdr) < 0)) {
      // mismatch. drop the packet
      goto done;
    }
  }

  // dispatch to different handlers
  switch (ntohs(ethhdr->ether_type)) {

    case ETHERTYPE_ARP:
      process_result = handle_arp_packet(ipc, ethhdr, (char *)link_pkt->data, 
					 datalen);
      break;

    case ETHERTYPE_IP:
      process_result = handle_ip_packet(ipc, ethhdr, (char *)link_pkt->data, 
					datalen);
      break;

    default:
      elog(LOG_DEBUG(0), "Got unhandled ETH type %d", 
          ntohs(ethhdr->ether_type));
      // send it to the tap interface, maybe the stack will know what
      // to do with it
      process_result = PKT_FWD_TO_TAP;
      break;
      
  }


  // decide what to do with the packet based on the return codes of the
  // handlers
  switch (process_result) {

    case PKT_FWD_TO_TAP:
      n=fwd_data_to_tap(ipc, (char *)link_pkt->data, datalen);
      break;


    case PKT_FWD_TO_RAW:

      iphdr = (struct iphdr *)(link_pkt->data 
                        + sizeof(struct ether_header));

      n = sendto(ipc->raw_sd, iphdr, datalen - sizeof(struct ether_header), 
          0x0, (struct sockaddr *)&sin, sizeof(sin));

      if (n<0) {
        elog(LOG_ERR, "Can't send to raw socket: %m");
      } else {
        elog(LOG_DEBUG(0), "Sent %d bytes to raw socket", n);
        //goto done;
      }

      break;


    case PKT_FWD_TO_TAP_AND_RAW:

      iphdr = (struct iphdr *)(link_pkt->data 
                        + sizeof(struct ether_header));

      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = iphdr->daddr;   // the dest. addr has already been
                                            // changed at this point
      n = sendto(ipc->raw_sd, iphdr, datalen - sizeof(struct ether_header), 
          0x0, (struct sockaddr *)&sin, sizeof(sin));

      if (n<0) {
        elog(LOG_ERR, "Can't send to raw socket: %m");
      } else {
        elog(LOG_DEBUG(0), "Sent %d bytes to raw socket", n);
        //goto done;
      }

      n=fwd_data_to_tap(ipc, (char *)link_pkt->data, datalen);
      break;


    case PKT_DONT_FWD:
    default:
      elog(LOG_DEBUG(0), "Dropping ethernet packet");
      break;

  }

done:
  free(link_pkt);
  return EVENT_RENEW;
}




int send_eth_pkt_over_link_dev(ipconn_state_t *ipc, 
    char *pktbuf, uint16_t len)
{
  link_pkt_t *pkt = NULL;
  char *buf = NULL;
  struct ether_header *ethhdr=NULL;
  int lpkt_len=sizeof(link_pkt_t) + len;

  if (pktbuf==NULL || ipc==NULL) {
    elog(LOG_ERR, "NULL pointer(s)");
    exit(1);
  }


  buf = malloc(lpkt_len);
  memset(buf, 0, lpkt_len);
  pkt = (link_pkt_t *)buf;

  // pkt type is ethernet
  pkt->type = PKT_TYPE_ETH;
  // cast pktbuf to an ethhdr to get the destination addr
  ethhdr = (struct ether_header *)pktbuf;
  // check to see if this is an ethernet broadcast
  // do some munging to get the right link id
  if (check_eth_bcast_addr((char *)ethhdr->ether_dhost)==1) {
    // this is an ethernet broadcast pkt, set dst_id to LINK_BCAST
    pkt->dst.id = LINK_BROADCAST;
  } else {
    if (ipc->arp_on==0) {
      // for now, we can only use unicasts when arps are off
      // otherwise, we have no idea where to send the packet...
      // we sort of need a resolution between emstar link ids
      // and hw addresses
      pkt->dst.id = ((ethhdr->ether_dhost[4] << 8) + 
          ethhdr->ether_dhost[5]) & 0xFFFF;
    } else {
      pkt->dst.id = LINK_BROADCAST;
    }
  }


  // copy the payload
  memcpy(pkt->data, pktbuf, len);

  // go
  if (lu_send(ipc->link_ref, pkt, len) < 0) {
    // see if we have an eagain; if so, we enable the writable callback,
    // disable the TAP readable callback and try again
    if (errno==EAGAIN) {
      // set writable on link
      lu_writable_cb_set_enable(ipc->link_ref, 1);
      // unset readable on tap 
      g_event_set_enable(ipc->tap_fd_cb_ref, 0);

      return 0;
    }

    elog(LOG_ERR, "Can't send on %s: %m", lu_name(ipc->link_ref, NULL));
    
  } else {
    elog(LOG_DEBUG(0), "Sending ethernet frame over link %s, dst=%s",
        lu_name(ipc->link_ref, NULL), print_if_id(pkt->dst.id));
  }




  // free the ip connector's buffered packet
  if (ipc->pkt_buf != NULL) {
    free(ipc->pkt_buf);
    ipc->pkt_buf = NULL;
  }
  // unset writable on link
  lu_writable_cb_set_enable(ipc->link_ref, 0);
  // set readable on tap
  g_event_set_enable(ipc->tap_fd_cb_ref, 1);
  free(pkt);

  return 0;
}


int link_pkt_writable(lu_context_t *link)
{
  ipconn_state_t *ipc = (ipconn_state_t *)lu_data(link);
  int n=0;

  if(ipc->pkt_buf==NULL || ipc->pkt_len==0) {
    elog(LOG_ERR, "NULL pkt buf or 0-length pkt");
    exit(1);
  }

  n=send_eth_pkt_over_link_dev(ipc, ipc->pkt_buf, ipc->pkt_len);
  if (n<0) {
    elog(LOG_ERR, "Can't send packet over link dev: %m");
  }


  return EVENT_RENEW;
}



int send_arp_reply(ipconn_state_t *ipc, struct ether_header *r_ethhdr,
    int bogus)
{
  int n=0;
  struct ether_header *reply_ehdr=NULL;

  reply_ehdr = generate_arp_reply(ipc, r_ethhdr, bogus);
  if (reply_ehdr == NULL) {
    elog(LOG_ERR, "NULL ARP reply header!");
    exit(1);
  }

  if (ipc->pkt_buf == NULL) {
    ipc->pkt_len = ARP_PKTLEN;
    ipc->pkt_buf = malloc(ipc->pkt_len);
    memcpy(ipc->pkt_buf, (char *)reply_ehdr, ARP_PKTLEN);
    n=send_eth_pkt_over_link_dev(ipc, (char *)reply_ehdr, ARP_PKTLEN);
  } else {
    // pkt buf wasn't null...
    elog(LOG_ERR, "IPC pkt buffer NOT NULL (arp pkt)");
  }

  
  // free the generated arp reply pkt!
  free(reply_ehdr);

  return n;
}



int create_raw_socket(ipconn_state_t *ipc)
{
  int sd;
  int on=1;

  sd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);

  if (sd<0) {
    elog(LOG_ERR, "Can't create raw socket: %m");
    exit(1);
  }

  // we are including the IP header
  if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0)  {
    elog(LOG_ERR, "setsockopt(HDRINCL) failed: %m");
    exit(1);
  }

  if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, ipc->provides_name, 
        strlen(ipc->provides_name)+1) < 0) {
//  if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth1\0",
//        strlen("eth0\0")) < 0) {
    
    elog(LOG_ERR, "setsockopt(BIND) failed: %m");
    exit(1);
  } else {
    elog(LOG_DEBUG(0), "Raw socket bound to device %s", ipc->provides_name);
  }


  return sd;
}


int create_packet_socket()
{
  int sd;
  
  sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));

  if (sd<0) {
    elog(LOG_ERR, "Can't create packet socket: %m");
    exit(1);
  }


  return sd;
}


int check_ip(ipconn_state_t *ipc, eth_arp_t *etharp)
{
  int i=0;
  int is_normal=0;
  int is_bogus=0;

  // this is probably too complicated and far from optimal, there must
  // be a better way to do it...

  for (i=0;i<IPADDR_SIZE;i++) {

    if (etharp->ar_tip[i] != ipc->ifaddr[i]) {
      is_normal = 0;
      break;
    } else {
      is_normal = 1;
    }
  }

  for (i=0;i<IPADDR_SIZE;i++) {

    if (etharp->ar_tip[i] != ipc->ifaddr_bogus[i]) {
      is_bogus=0;
      break;
    } else {
      is_bogus=1;
    }

  }

  if (is_normal) {
    return ARP_NORMAL_IFACE;
  }

  if (is_bogus) {
    return ARP_BOGUS_IFACE;
  }

  return ARP_NOT_MINE;

}





int handle_arp_packet(ipconn_state_t *ipc, struct ether_header *ethhdr, 
    char *data, int datalen)
{
  struct arphdr *ahdr;
  int n=0;

  if (ipc==NULL || ethhdr == NULL || data == NULL) {
    elog(LOG_ERR, "NULL pointer(s)");
    exit(1);
  }

  if (ipc->arp_on==0) {
    // hmm, arps are off but the handler was invoked! 
    // This is not good(tm)
    elog(LOG_ERR, "ARPs are off but ARP packet handler was invoked!");
    // don't send the packet to the kernel
    return PKT_DONT_FWD;
  }

  // cast the packet into an arp header
  ahdr = (struct arphdr *)(data + sizeof(struct ether_header));

  if (htons(ahdr->ar_op) == ARPOP_REQUEST) {
    eth_arp_t *etharp = (eth_arp_t *)(data
                          + sizeof(struct ether_header)
                          + sizeof(struct arphdr));
    // we have an arp request
    // We need to check if we own this IP
    n = check_ip(ipc, etharp);

    switch (n) {
      case ARP_NORMAL_IFACE:
        send_arp_reply(ipc, ethhdr, 0);
        break;

      case ARP_BOGUS_IFACE:
        send_arp_reply(ipc, ethhdr, 1);
        break;

      case ARP_NOT_MINE:
      default:
        break;
    }

    // don't forward in either case
    return PKT_DONT_FWD;

  } else if (htons(ahdr->ar_op) == ARPOP_REPLY) {
    elog(LOG_DEBUG(0), "Got ARPPOP_REPLY!");
    // send this to the tap iface
    return PKT_FWD_TO_TAP;
  }

  return PKT_DONT_FWD;
}




int handle_ip_packet(ipconn_state_t *ipc, struct ether_header *ethhdr, 
    char *data, int datalen)
{
  struct iphdr *iphdr;
  uint32_t check_octet;
  uint32_t tmpaddr;
  int send_to_raw=0;
  
  if (ipc==NULL || ethhdr == NULL || data == NULL) {
    elog(LOG_ERR, "NULL pointer(s)");
    exit(1);
  }

  iphdr = (struct iphdr *)(data + sizeof(struct ether_header));

  check_octet = (ntohl(iphdr->daddr) & CHECK_MASK);

  switch (check_octet) {

    case BOGUS_NET:
      // destination address of the packet is bogus
      // we need to turn it back to a normal address

      if (ipc->bogus_on) {
      
        // first we clear the network octet
        tmpaddr = ntohl(iphdr->daddr) & (~CHECK_MASK);

        // now we or the correct net octet
        tmpaddr = tmpaddr | CORRECT_NET;

        iphdr->daddr = htonl(tmpaddr);
        // if this is a tcp packet, fix its checksum--it should be off by 1 bit
        if (iphdr->protocol == IPPROTO_TCP) {
          // ip header length is in WORDS which are 32-bit in size
          uint8_t iphdr_len = iphdr->ihl * sizeof(uint32_t);  
          struct tcphdr *th;
          th = (struct tcphdr *)(data + sizeof(struct ether_header) +
            iphdr_len);
          elog(LOG_DEBUG(0), "TCP packet detected, recomputing TCP chksum."
            " Old = %hx, New = %hx", th->check, th->check+htons(1));
          // this is an ugly hack but it's much much faster than recomputing
          // the tcp checksum every time, especially on 1.5k packets
          //
          //
          // NOTE: on this side we ADD htons(1) from the check because
          // the source address is htons(1) less than it should be
          //
          th->check = th->check + htons(1);
        }

        // we need to send to the raw socket--raise the flag
        send_to_raw=1;
      }

      break;


    case CORRECT_NET:
      // Destination address of the packet is correct
      // if the packet is destined to us, we need to change its SOURCE
      // address to be bogus
      
      if (ipc->bogus_on) {
        tmpaddr = ntohl(iphdr->saddr) & (~CHECK_MASK);
        tmpaddr = tmpaddr | BOGUS_NET;

        iphdr->saddr = htonl(tmpaddr);
      }
      // if this is a tcp packet, fix its checksum--it should be off by 1 bit
      if (iphdr->protocol == IPPROTO_TCP) {
        // ip header length is in WORDS which are 32-bit in size
        uint8_t iphdr_len = iphdr->ihl * sizeof(uint32_t);  
        struct tcphdr *th;
        th = (struct tcphdr *)(data + sizeof(struct ether_header) +
            iphdr_len);
        elog(LOG_DEBUG(0), "TCP packet detected, recomputing TCP chksum."
            " Old = %hx, New = %hx", th->check, th->check-htons(1));
        // this is an ugly hack but it's much much faster than recomputing
        // the tcp checksum every time, especially on 1.5k packets
        //
        //
        // NOTE: on this side we SUBTRACT htons(1) from the check because
        // the source address is htons(1) greater than it should be
        //
        th->check = th->check - htons(1);
      }


      break;

    default:
      break;
  }

  // fix the checksum
  
  elog(LOG_DEBUG(0), "Original checksum = 0x%hx", htons(iphdr->check));
  iphdr->check = (0x0);
  iphdr->check = in_cksum((uint16_t *)iphdr, sizeof(struct iphdr));
  elog(LOG_DEBUG(0), "New checksum = 0x%hx", htons(iphdr->check));




  if (send_to_raw) {
    return PKT_FWD_TO_TAP_AND_RAW;
  } else {
    return PKT_FWD_TO_TAP;
  }
}




See more files for this project here

EmStar

EmStar is a software system for developing and deploying wireless sensor networks involving Linux-based platforms. As the wireless sensor network community has attempted to deploy more complex designs---large-scale, long-lived systems that need self-organization and adaptivity---a number of difficult software design issues have arisen. Advances in software design have not kept pace with the capabilities of hardware. This is because designing for an adaptive, efficient, and useful sensor network has turned out to be surprisingly complex and difficult. EmStar is a Linux-based software framework, whose goal is to dramatically reduce this complexity, enabling work to be shared and reused, and simplifying and speeding the design of new sensor network applications.

Project homepage: http://cvs.cens.ucla.edu/emstar/
Programming language(s): C,Shell Script
License: other

  ip_client.c
  ip_connector.c
  ip_server.c