Hi,
I am trying to write user-app that utilizes TAP driver in linux to send/receive Ethernet packets from TAP interfaces.
I don’t find a good example of this anywhere on the internet. I am having difficulty in receiving packets.
I am kind of struck. If you could share some thoughts or sample program I would appreciate it.
Here is what I have done.
1) TAP driver is installed with “sudo insmod tun.ko”
2) tap0 interface is created with tunctl program and assigned an ip address, interface link is set to up
3) Userapp sends the data to tap0 by opening the device, write an Ethernet frame and waiting for the written frame from the device
4) User program does not receive the data
User program is provided below.
As far as TAP driver goes, tun_get_user receives the packet from iovect, allocates skbuff. But, the packet does not go out of the device.
1) tun_alloc_skb(align, len, gso.hdr_len, GFP_KERNEL)): success with 1510 bytes
as 4 bytes are stripped
2) skb_copy_datagram_from_iovec(skb, 0, iv, len): copies data from iovect to skbuffer
I verified the data in the trace logs. It is the same data that send_ethernet_frame
sends the data
3) switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TAP_DEV:
skb->protocol = eth_type_trans(skb, tun->dev);
pdata = skb->data;
for (i = 0; i < 20; i++)
printk ("%02x ", (*(pdata+i) & 0xff));
printk ("\n");
skb->dev = tun->dev;
wake_up_interruptible(&tun->read_wait);
}
Wake_up_interruptable does not wake up the read wait queue. Even if I use dev_queue_xmit, linux kernel crashes. I have the problem here.
I understand that this code has been tested. Unfortunately, I could not find any sample user-app that writes Ethernet frames to TAP device
And reads the frames.
I appreciate if you could help me with this. I have not got response from linux forums also.
Thanks in advance,
David
UserApp
int
main(int argc, char **argv)
{
struct ifreq ifr;
int tap_fd, option;
uint16_t nread;
char buffer[BUFSIZE];
int sent_fr_sz;
char *tun = "", *file = "/dev/net/tun";
while ((option = getopt(argc, argv, "h:i:")) > 0) {
switch (option)
{
case 'i':
tun = optarg;
break;
case 'h':
default:
usage(argv[0]);
}
}
argv += optind;
argc -= optind;
if (argc > 0)
usage(argv[0]);
if ((tap_fd = open(file, O_RDWR)) < 0) {
perror(file);
exit(1);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | TUN_NO_PI;
strncpy(ifr.ifr_name, tun, sizeof(ifr.ifr_name) - 1);
if (ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0) {
perror(ifr.ifr_name);
exit(1);
}
for (;
{
/* send ethernet frame to the tap device */
sent_fr_sz = send_ethernet_frame(tap_fd);
/* read the packet from tap device */
nread = cread(tap_fd, buffer, ETH_FRAME_LEN);
printf ("Read %d bytes\n", nread);
}
}
/*************************************************************/
/* send_ethernet_frame: sends an ether frame with prepended */
/* DSA tag (marvel portid0 to the tap device. */
/*************************************************************/
int send_ethernet_frame(int fd)
{
int j;
int sent_frame_sz = 0;
int mrvl_portid = 0;
/*buffer for ethernet frame*/
void* buffer = (void*) malloc(sizeof (mrvl_portid) + ETH_FRAME_LEN);
/* pointer to ethernet header*/
unsigned char* etherhead = buffer ;
/*userdata in ethernet frame*/
unsigned char* data = buffer + 14;
/*another pointer to ethernet header*/
struct ethhdr *eh = (struct ethhdr *) etherhead;
/*our MAC address*/
unsigned char src_mac[6] = {0x00, 0x01, 0x02, 0xFA, 0x70, 0xAA};
/*other host MAC address*/
unsigned char dest_mac[6] = {0x00, 0x08, 0x75, 0xC8, 0x28, 0xE5};
/* set the frame header*/
memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
memcpy((void*)(buffer+ ETH_ALEN ), (void*)src_mac, ETH_ALEN);
eh->h_proto = 0x0800;
/* fill the frame with some data */
for (j = 0; j < 1500; j++) {
data[j] = (unsigned char)(j);
}
/* send the frame */
sent_frame_sz = write (fd, buffer, ETH_FRAME_LEN);
if (sent_frame_sz < 0)
{
perror("ethernet frame write error\n");
exit(1);
}
printf ("ethernet frame sent %d successfully\n", sent_frame_sz);
return sent_frame_sz;
}