[ Top page ]

« Simulation of RDB queries using Perl | Main | Connection check program for non IP network environment »

Network and communication

A test program for sending/receiving packets with a VLAN tag

I wrote a program that sends and receives packets with a VLAN tag using Linux promiscuous mode. This program is for playing with a VLAN switch. If you compile it without a switch (#define VLAN), you can send and receive untagged (i.e., normal Ethernet) packets.

Header file

The header file called "Ether.h" is listed first. This file includes a VLAN tag when "VLAN" is valued, and excludes it when "VLAN" is not valued.

/***
 *
 * Software-based Ethernet Common Header
 *
 * Coded by Yasusi Kanada
 * Ver 1.0  2012-5-20   Initial version
 *
 ***/

#include <linux/if_packet.h>
#include <linux/if_ether.h> 
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdio.h>
#include <elf.h>
#include <string.h>


// 0       6      12      12/16 14/18           18/22
// +-------+-------+---------+----+---------------+
// | DMAC  | SMAC  |8100 VLAN|Type|Payload (4Bfix)|
// +-------+-------+---------+----+---------------+
//                  <-------> when VLAN == Yes

struct _EtherHeader {
  uint16_t destMAC1;
  uint32_t destMAC2;
  uint16_t srcMAC1;
  uint32_t srcMAC2;
#ifdef VLAN
  uint32_t VLANTag;
#endif
  uint16_t type;
  int32_t  payload;
} __attribute__((packed));

typedef struct _EtherHeader EtherPacket;

Program body

The following is the main program. This program use locally defined MAC addresses from 0200 0000 0001 to 0200 0000 0004, instead of using hardware-defined addresses. The command syntax is as follows (the command name is assumed to be "sendrecv").

sendrecv <Sender> <Receiver> <Port> <VLAN ID>

The range of <Sender> and <Receiver> is 0 to 3. (These values are used because I cannot tolerate typing MAC addresses every time I type a command.) If you use 1 for <Port>, eth1 is used. If a prefix other than "eth" is used, you have to update this program. If you compile it without "VLAN", the value of <VLAN ID> is not used.

BTW, this program use 0x88b5 (IEEE 802.1 Local Experimental Ethertype 1) for the type field. This value may cause a trouble by an interface card (such as one with Realtek RTL8169). So, be careful!

/***
 *
 * Generate/Receive Ethernet Packet
 *
 * Coded by Yasusi Kanada
 * Ver 1.0  2012-5-20	Initial version
 *
 ***/

#define VLAN		Yes
#define DEBUG		0

#include "Ether.h"
#include <fcntl.h>

#define MAX_PACKET_SIZE	2048
	// Sufficiently larger than the MTU

#define Period		1

enum commMode {SendAndReceive = 0, ReceiveThenSend = 1};

#define ETH_P_Exp	0x88b5
	// Ethernet type = IEEE 802.1 Local Experimental Ethertype 1

#define NTerminals	4
uint16_t MAC1[NTerminals] = {0x0200, 0x0200, 0x0200, 0x0200};
uint32_t MAC2[NTerminals] = {0x00000001, 0x00000002, 0x00000003, 0x00000004};

#define InitialReplyDelay	40
#define MaxCommCount		9999

#define IFNAME	"ethX"
	// or "gretapX"


extern void _exit(int32_t);


/**
 * Open a socket for the network interface
 */
int32_t open_socket(int32_t index, int32_t *rifindex) {
  unsigned char buf[MAX_PACKET_SIZE];
  int32_t i;
  int32_t ifindex;
  struct ifreq ifr;
  struct sockaddr_ll sll;
  unsigned char ifname[IFNAMSIZ];
  strncpy(ifname, IFNAME, IFNAMSIZ);
  ifname[strlen(ifname) - 1] = '0' + index;

  int32_t fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
  if (fd == -1) {
    printf("%s - ", ifname);
    perror("socket");
    _exit(1);
  };

  // get interface index
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
  if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
    printf("%s - ", ifname);
    perror("SIOCGIFINDEX");
    _exit(1);
  };
  ifindex = ifr.ifr_ifindex;
  *rifindex = ifindex;

  // set promiscuous mode
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
  ioctl(fd, SIOCGIFFLAGS, &ifr);
  ifr.ifr_flags |= IFF_PROMISC;
  ioctl(fd, SIOCSIFFLAGS, &ifr);

  memset(&sll, 0xff, sizeof(sll));
  sll.sll_family = AF_PACKET;
  sll.sll_protocol = htons(ETH_P_ALL);
  sll.sll_ifindex = ifindex;
  if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) == -1) {
    printf("%s - ", ifname);
    perror("bind");
    _exit(1);
  };

  /* flush all received packets. 
   *
   * raw-socket receives packets from all interfaces
   * when the socket is not bound to an interface
   */
  do {
    fd_set fds;
    struct timeval t;
    FD_ZERO(&fds);	
    FD_SET(fd, &fds);
    memset(&t, 0, sizeof(t));
    i = select(FD_SETSIZE, &fds, NULL, NULL, &t);
    if (i > 0) {
      recv(fd, buf, i, 0);
    };
    if (DEBUG) printf("interface %d flushed\n", ifindex);
  } while (i);

  if (DEBUG) printf("%s opened (fd=%d interface=%d)\n", ifname, fd, ifindex);

  return fd;
}


/**
 * Create an IPEC packet
 */
ssize_t createPacket(EtherPacket *packet, uint16_t destMAC1, uint32_t destMAC2,
		     uint16_t srcMAC1, uint32_t srcMAC2, uint16_t type, uint32_t vlanTag,
		     int32_t payload) {
  ssize_t packetSize = sizeof(EtherPacket);
  // ssize_t packetSize = payloadLength + sizeof(EtherPacket);
  packet->destMAC1 = htons(destMAC1);
  packet->destMAC2 = htonl(destMAC2);
  packet->srcMAC1 = htons(srcMAC1);
  packet->srcMAC2 = htonl(srcMAC2);
#ifdef VLAN
  packet->VLANTag = htonl(vlanTag);
#endif
  packet->type = htons(type);
  packet->payload = htonl(payload);
  // strncpy(packet->payload, payload, packetSize);
  return packetSize;
}


int32_t lastPayload = -1;

/**
 * Print IPEC packet content
 */
void printPacket(EtherPacket *packet, ssize_t packetSize, char *message) {
#ifdef VLAN
  printf("%s #%d (VLAN %d) from %08x%04x to %08x%04x\n",
	 message, ntohl(packet->payload), ntohl(packet->VLANTag) & 0xFFF,
#else
  printf("%s #%d from %08x%04x to %08x%04x\n",
	 message, ntohl(packet->payload),
#endif
	 ntohs(packet->srcMAC1), ntohl(packet->srcMAC2),
	 ntohs(packet->destMAC1), ntohl(packet->destMAC2));
  lastPayload = ntohl(packet->payload);
}


/**
 * Send packets to terminals
 */
void sendPackets(int32_t fd, int32_t ifindex, uint16_t SrcMAC1, uint32_t SrcMAC2,
		 uint16_t DestMAC1, uint32_t DestMAC2, uint16_t type, uint32_t vlanTag,
		 int32_t *count) {
  int32_t i;
  unsigned char packet[MAX_PACKET_SIZE];
  // unsigned char *payload = "Hello!";

  struct sockaddr_ll sll;
  memset(&sll, 0, sizeof(sll));
  sll.sll_family = AF_PACKET;
  sll.sll_protocol = htons(ETH_P_ALL);	// Ethernet type = Trans. Ether Bridging
  sll.sll_ifindex = ifindex;

  ssize_t packetSize = createPacket((EtherPacket*)packet, DestMAC1, DestMAC2,
				    SrcMAC1, SrcMAC2, type, vlanTag, (*count)++);
  ssize_t sizeout = sendto(fd, packet, packetSize, 0,
			   (struct sockaddr *)&sll, sizeof(sll));
  printPacket((EtherPacket*)packet, packetSize, "Sent:    ");
  if (sizeout < 0) {
    perror("sendto");
  } else {
    if (DEBUG) {
      printf("%d bytes sent through interface (ifindex) %d\n",
	     (int32_t)sizeout, (int32_t)ifindex);
    }
  }
}


void sendReceive(int32_t fd, int32_t ifindex, uint16_t SrcMAC1, uint32_t SrcMAC2,
		 uint16_t DestMAC1, uint32_t DestMAC2, uint16_t type, uint16_t vlanID) {
  unsigned char buf[MAX_PACKET_SIZE];
  int32_t sendCount = 0;
  int32_t receiveCount = 0;
  time_t lastTime = time(NULL);
  int32_t replyDelay = 0;
  int32_t i;
  uint32_t vlanTag = 0x81000000 | vlanID;

  // Sending and receiving packets:
  for (; sendCount < MaxCommCount && receiveCount < MaxCommCount;) {
    if (DestMAC2 != 0 && replyDelay <= 0) {
      int32_t currTime = time(NULL);
      if (currTime - lastTime >= Period) {
	if (DEBUG) printf("currTime=%d lastTime=%d\n", currTime, (int32_t)lastTime);
	sendPackets(fd, ifindex, SrcMAC1, SrcMAC2, DestMAC1, DestMAC2, type, vlanTag,
		    &sendCount);
	lastTime = currTime;
      }
    }
    ssize_t sizein = recv(fd, buf, MAX_PACKET_SIZE, 0);
    if (sizein >= 0) {
      EtherPacket *packet = (EtherPacket*) buf;
      if (DestMAC2 == 0) {
	DestMAC1 = ntohs(packet->srcMAC1);
	DestMAC2 = ntohl(packet->srcMAC2);
	replyDelay = InitialReplyDelay;
      } else if (replyDelay > 0) {
	replyDelay--;
      }
      printPacket(packet, sizein, "Received:");
      receiveCount++;
    } else {
      usleep(10000); // sleep for 10 ms
    }
  }
}


/**
 * Main program
 */
int32_t main(int32_t argc, char **argv) {
  int32_t ifindex;
  int32_t myTermNum = 0;
  int32_t destTermNum = 0;
  int32_t ifnum = 0;
  uint16_t vlanID = 1;
  int32_t i;

  // Get terminal and interface numbers from the command line:
  int32_t count = 0;
  if (++count < argc) {
    myTermNum = atoi(argv[count]);	// My terminal number
  }
  if (myTermNum >= NTerminals || myTermNum < 0) {
    printf("My terminal number (%d) too large\n", myTermNum);
    myTermNum = 0;
  }

  if (++count < argc) {
    destTermNum = atoi(argv[count]);	// Destination terminal number
  }
  if (destTermNum >= NTerminals || destTermNum < 0) {
    printf("Destination terminal number (%d) too large\n", destTermNum);
    destTermNum = 1;
  }

  if (++count < argc) {
    ifnum = atoi(argv[count]);		// Interface number
  }

  if (++count < argc) {
    vlanID = atoi(argv[count]);		// VLAN ID
  }
  if (vlanID < 1 || 4095 < vlanID) {
    printf("VLAN ID out of range (1..4095)\n", vlanID);
    vlanID = 1;
  }

  // Set locators and IDs using terminal number:
  uint16_t SrcMAC1  = MAC1[myTermNum];
  uint32_t SrcMAC2  = MAC2[myTermNum];
  uint16_t DestMAC1 = MAC1[destTermNum];
  uint32_t DestMAC2 = MAC2[destTermNum];
  printf("eth%d terminal#=%d VLAN:%d srcMAC:%08x%04x destMAC:%08x%04x\n",
	 ifnum, myTermNum, vlanID,
	 (int32_t)SrcMAC1, (int32_t)SrcMAC2, (int32_t)DestMAC1, (int32_t)DestMAC2);

  int32_t fd = open_socket(ifnum, &ifindex);

  // Set non-blocking mode:
  int32_t flags = fcntl(fd, F_GETFL, 0);
  fcntl(fd, F_SETFL, O_NONBLOCK | flags);

  sendReceive(fd, ifindex, SrcMAC1, SrcMAC2, DestMAC1, DestMAC2, ETH_P_Exp, vlanID);
}
Keywords:

TrackBack

TrackBack URL for this entry:
https://www.kanadas.com/mt/mt-tb.cgi/5628

Post a comment

About

This page contains a single entry from the blog posted on May 20, 2012 9:31 AM.

Many more can be found on the main index page or by looking through the archives.

Creative Commons License
This weblog is licensed under a Creative Commons License.
Powered by Movable Type