[ 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:
http://www.kanadas.com/mt/mt-tb.cgi/5628

Comments (2)

Joseph:

Hey , I am not being able to run your program. I wanted to send packets from one port and receive it on same port and the port does not have an IP assign to it. Can you please help me .

Thanks,
Joseph.

Mahmoud:


Hi,
Thank for this code.
I would like to create a program on linux that will receive and send the frames between a DSLAM and a modem. I mean the tool will interface the network interface of the modem's side and of the DSLAM's side. And it will permit to send the frames to the other side with an introspecting by dropping some frames with an VlanId, @src, @dst and protocol...
Thank you

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 3.36