Friday, July 29, 2011

user-space-ip.c

This some quick and dirty test code that creates a userspace virtual IP network interface.
 
Currently it arp's and ping's and can send and receive UDP, which is fine for RTP based video and audio.
I wanted to setup some user space TCP/IP stack like Apline or LWIP and make a web server,

The MAC address is hard coded.
The network card in put promiscuous mode so DO NOT RUN THIS ON A HEAVILY LOADED SERVER.
It uses raw sockets so it must run as root.
It's only been tested on Linux.


/*****************************************************************************
 *   Copyright   John Sokol (C) 2004 
 *   You may use this code as long as you credit me and link to videotechnology.com
 *****************************************************************************/
#include
#include
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/ip.h>
// #include
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>



// Evil Globals

/*our MAC address*/
unsigned char src_mac[6] = { 0x00, 0x0c, 0xf1, 0x75, 0xf8, 0xaa };      // Bogus
unsigned char src_ip[4] = { 192, 168, 0, 88 };  // Bogus

/*other host MAC address*/
unsigned char bcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };    //Broadcast

unsigned char dest_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };     //Broadcast
unsigned char dest_ip[4] = { 255, 255, 255, 255 };      // Bogus

unsigned char arp_query_ip[4] = { 192, 168, 0, 16 };    // Bogus



u_int16_t
csum_partial (void *buffer, unsigned int len, u_int16_t prevsum)
{
  u_int32_t sum = 0;
  u_int16_t *ptr = buffer;

  while (len > 1)
    {
      sum += *ptr++;
      len -= 2;
    }
  if (len)
sokol@server:~$
sokol@server:~$
sokol@server:~$
sokol@server:~$ cat user-space-ip.c
#include
#include
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/ip.h>
// #include
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>



// Evil Globals

/*our MAC address*/
unsigned char src_mac[6] = { 0x00, 0x0c, 0xf1, 0x75, 0xf8, 0xaa };      // Bogus
unsigned char src_ip[4] = { 192, 168, 0, 88 };  // Bogus

/*other host MAC address*/
unsigned char bcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };    //Broadcast

unsigned char dest_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };     //Broadcast
unsigned char dest_ip[4] = { 255, 255, 255, 255 };      // Bogus

unsigned char arp_query_ip[4] = { 192, 168, 0, 16 };    // Bogus



u_int16_t
csum_partial (void *buffer, unsigned int len, u_int16_t prevsum)
{
  u_int32_t sum = 0;
  u_int16_t *ptr = buffer;

  while (len > 1)
    {
      sum += *ptr++;
      len -= 2;
    }
  if (len)
    {
      union
      {
        u_int8_t byte;
        u_int16_t wyde;
      } odd;
      odd.wyde = 0;
      odd.byte = *((u_int8_t *) ptr);
      sum += odd.wyde;
    }
  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += prevsum;
  return (sum + (sum >> 16));
}


void
sendpack (int sock)
{

  struct sockaddr_ll socket_address;    /*target address */

  void *buffer = (void *) malloc (ETH_FRAME_LEN);       /*buffer for ethernet frame */
  unsigned char *etherhead = buffer;    /*pointer to ethenet header */
  struct ethhdr *eh = (struct ethhdr *) etherhead;      /*another pointer to ethernet header */
  unsigned char *data = buffer + 14;    /*userdata in ethernet frame */

  int send_result = 0;


/*prepare sockaddr_ll*/

/*RAW communication*/
  socket_address.sll_family = PF_PACKET;
/*we don't use a protocoll above ethernet layer ->just use anything here*/
  socket_address.sll_protocol = htons (ETH_P_ALL);

/*index of the network device see full code later how to retrieve it*/
  socket_address.sll_ifindex = if_nametoindex ("eth0");
  printf ("if index for eth0 = %d\n", if_nametoindex ("eth0"));

/*ARP hardware identifier is ethernet*/
  socket_address.sll_hatype = ARPHRD_ETHER;     // for recv only

/*target is another host*/
  socket_address.sll_pkttype = PACKET_BROADCAST;        // for recv only

/*address length*/
  socket_address.sll_halen = ETH_ALEN;
/*MAC - begin*/
  socket_address.sll_addr[0] = 0x00;
  socket_address.sll_addr[1] = 0x00;
  socket_address.sll_addr[2] = 0x00;
  socket_address.sll_addr[3] = 0x00;
  socket_address.sll_addr[4] = 0x00;
  socket_address.sll_addr[5] = 0x00;
/*MAC - end*/
  socket_address.sll_addr[6] = 0x00;    /*not used */
  socket_address.sll_addr[7] = 0x00;    /*not used */

  memcpy ((void *) socket_address.sll_addr, (void *) dest_mac, ETH_ALEN);

/*set the frame header*/
  memcpy ((void *) buffer, (void *) dest_mac, ETH_ALEN);
  memcpy ((void *) (buffer + ETH_ALEN), (void *) src_mac, ETHALEN);
  eh->h_proto = htons (ETH_P_ARP);


  data[0] = 0;
  data[1] = 1;                  // Hardware Type Ethernet
  data[2] = 8;
  data[3] = 0;                  // Protocol IP
  data[4] = 6;
  data[5] = 4;                  // Hardware size 6, protocol size 4
  data[6] = 0;
  data[7] = 1;                  // Opcode Request

  memcpy ((void *) &data[8], (void *) src_mac, ETH_ALEN);
  memcpy ((void *) &data[14], (void *) src_ip, 4);

  memset ((void *) &data[18], 0, ETH_ALEN);

  memcpy ((void *) &data[24], (void *) arp_query_ip, 4);



/*send the packet*/
  send_result = sendto (sock, buffer, 61, 0,
                        (struct sockaddr *) &socket_address,
                        sizeof (socket_address));

  if (send_result == -1)
    {
      printf ("error sending packet %d\n", errno);
      if (errno == ENOTSOCK)
        printf ("ENOTSOCK\n");
      if (errno == EAGAIN)
        printf ("EAGAIN\n");
      if (errno == ENOBUFS)
        printf ("ENOBUFS\n");
      if (errno == EINVAL)
        printf ("EINVAL\n");
      if (errno == EPIPE)
        printf ("EPIPE\n");
      if (errno == EMSGSIZE)
        printf ("EMSGSIZE\n");
      if (errno == EBADF)
        printf ("EBADF\n");
      if (errno == EFAULT)
        printf ("EFAULT\n");

    }


}



void
recvpack (int sock)
{
  char buffer[2048];
  unsigned char *iphead, *ethhead;
  char proto[10];
  int n;

  struct iphdr *ip;
  struct icmphdr *icmp;  //= (void *)((u_int32_t *)ip + ip.ihl );
  struct tcphdr *tcp;
  struct udphdr *udp;



  n = recvfrom (sock, buffer, 2048, 0, NULL, NULL);
  printf ("%d bytes read\n", n);

  /* Check to see if the packet contains at least
   * complete Ethernet (14), IP (20) and TCP/UDP
   * (8) headers.
   */
  if (n < 20)
    {                           // was 42
      perror ("recvfrom():");
      printf ("Incomplete packet (errno is %d)\n", errno);
      close (sock);
      exit (0);
    }

  ethhead = buffer;

  if (!memcmp(ethhead, (void *) src_mac, 6) || !memcmp(ethhead, (void *) bcast_mac, 6) )  // IS it for our MAC?
    {

      printf ("Destination MAC address: "
              "%02x:%02x:%02x:%02x:%02x:%02x\n",
              ethhead[0], ethhead[1], ethhead[2],
              ethhead[3], ethhead[4], ethhead[5]);
      printf ("Source MAC address: "
              "%02x:%02x:%02x:%02x:%02x:%02x\n",
              ethhead[6], ethhead[7], ethhead[8],
              ethhead[9], ethhead[10], ethhead[11]);


      if (ethhead[12] == 0x08 && ethhead[13] == 0x00)
        {

          iphead = buffer + 14; /* Skip Ethernet header */
          if (*iphead == 0x45) {  /* Double check for IPv4  and no options present */
              ip = (void *)iphead;

              printf ("Source host %d.%d.%d.%d\n",
                      iphead[12], iphead[13], iphead[14], iphead[15]);
              printf ("Dest host %d.%d.%d.%d\n",
                      iphead[16], iphead[17], iphead[18], iphead[19]);
              printf ("Source,Dest ports %d,%d\n",
                      (iphead[20] << 8) + iphead[21],
                      (iphead[22] << 8) + iphead[23]);

              sprintf (proto, "%d", iphead[9]);
              if (iphead[9] == IPPROTO_ICMP) {
                strcpy (proto, "ICMP");
                 icmp = (void *)iphead + 20;

                if (icmp->type == ICMP_ECHO) {
                                fprintf(stderr, "PONG!\n");
        u_int32_t tmp;
        char tbuf[10];
                                /* Turn it around */

  memcpy ( tbuf, (void *) (buffer + ETH_ALEN), ETH_ALEN);
  memcpy ((void *) buffer, (void *) tbuf, ETH_ALEN);
  memcpy ((void *) (buffer + ETH_ALEN), (void *) src_mac, ETH_ALEN);

                        //memcpy ((void *) tmp, (void *)&ip->saddr , 4);

                        tmp = ip->saddr;
                        ip->saddr = ip->daddr;
                        ip->daddr = tmp;

                        icmp->type = ICMP_ECHOREPLY;
                        icmp->checksum = 0;
                        icmp->checksum = ~csum_partial(icmp, ntohs(ip->tot_len) - ip->ihl*4, 0);

                        {
                                unsigned int i;
                        for (i = 44; i < ntohs(ip->tot_len); i++){
                                        printf("%u:0x%02X ", i, ((unsigned char *)ip)[i]);
                        }
                        printf("\n");

                     int send_result;
                     struct sockaddr_ll socket_address; /*target address */

    /*RAW communication*/
  socket_address.sll_family = PF_PACKET;
/*we don't use a protocoll above ethernet layer ->just use anything here*/
  socket_address.sll_protocol = htons (ETH_P_ALL);

/*index of the network device see full code later how to retrieve it*/
  socket_address.sll_ifindex = if_nametoindex ("eth0");
  printf ("if index for eth0 = %d\n", if_nametoindex ("eth0"));

/*ARP hardware identifier is ethernet*/
  socket_address.sll_hatype = ARPHRD_ETHER;     // for recv only

/*target is another host*/
  socket_address.sll_pkttype = PACKET_BROADCAST;        // for recv only

/*address length*/
  socket_address.sll_halen = ETH_ALEN;
/*MAC - begin*/
  socket_address.sll_addr[0] = 0x00;
  socket_address.sll_addr[1] = 0x00;
  socket_address.sll_addr[2] = 0x00;
  socket_address.sll_addr[3] = 0x00;
  socket_address.sll_addr[4] = 0x00;
  socket_address.sll_addr[5] = 0x00;
/*MAC - end*/
  socket_address.sll_addr[6] = 0x00;    /*not used */
  socket_address.sll_addr[7] = 0x00;    /*not used */

  memcpy ((void *) socket_address.sll_addr, (void *)tbuf , ETH_ALEN);

 sleep(2);
                     send_result = sendto (sock, buffer, n, 0,
                        (struct sockaddr *) &socket_address,
                        sizeof (socket_address));

  if (send_result == -1)
    {
      printf ("error sending packet %d\n", errno);
     }
                     //write(fd, &u, len);

                    }
                }

                }
              if (iphead[9] == IPPROTO_IGMP)
                strcpy (proto, "IGMP");
              if (iphead[9] == IPPROTO_TCP)
                strcpy (proto, "TCP");
              if (iphead[9] == IPPROTO_UDP)
                strcpy (proto, "UDP");

              printf ("Layer-4 protocol %s\n", proto);
              printf ("TTL %d\n", iphead[8]);
            }

        }
      else
       if (ethhead[12] == 0x08 && ethhead[13] == 0x06)
        {
          printf ("proto = ARP");
        }
      else
        printf ("proto = %02x:%02x\n", ethhead[12], ethhead[13]);
    }

}


int
main (int argc, char **argv)
{
  int sock;
  struct ifreq ethreq;

  if ((sock = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
    {
      perror ("socket");
      exit (1);
    }

  /* Set the network card in promiscuous mode */
  strncpy (ethreq.ifr_name, "eth0", IFNAMSIZ);
  if (ioctl (sock, SIOCGIFFLAGS, &ethreq) == -1)
    {
      perror ("ioctl");
      close (sock);
      exit (1);
    }
  ethreq.ifr_flags |= IFF_PROMISC;
  if (ioctl (sock, SIOCSIFFLAGS, &ethreq) == -1)
    {
      perror ("ioctl");
      close (sock);
      exit (1);
    }

  sendpack (sock);              // ARP Request


  while (1)
    {
      printf ("----------\n");
      recvpack (sock);
    }

}

No comments: