I have a nanosecond libpcap (nanosec.pcap) file and the nanosecond timestamp (eg 2.123456789) can be displayed by using Wireshark. Now i would like to open the nanosecond libpcap file using C language and have the source code as following. When I try to open the the nanosec.pcap by using pcap_open_offine(), it would return "unknown file format" error. Additionally, by changing the magic number at the header of nanosec.pcap to that of normal pcap (0x1A2B3C4D) and I got a segmentation fault error from the terminal (Ubuntu). Any expert here could advice how could I display the nanosecond part of the timestamp by using libpcap? Thanks in advance!
Following is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <pcap.h>
struct UDP_hdr {
u_short uh_sport; /* source port */
u_short uh_dport; /* destination port */
u_short uh_ulen; /* datagram length */
u_short uh_sum; /* datagram checksum */
};
/* Some helper functions, which we define at the end of this file. */
/* Returns a string representation of a timestamp. */
const char *timestamp_string(struct timeval ts);
/* Report a problem with dumping the packet with the given timestamp. */
void problem_pkt(struct timeval ts, const char *reason);
/* Report the specific problem of a packet being too short. */
void too_short(struct timeval ts, const char *truncated_hdr);
void dump_UDP_packet(const unsigned char *packet, struct timeval ts,
unsigned int capture_len)
{
struct ip *ip;
struct UDP_hdr *udp;
unsigned int IP_header_length;
/* For simplicity, we assume Ethernet encapsulation. */
if (capture_len < sizeof(struct ether_header))
{
/* We didn't even capture a full Ethernet header, so we
* can't analyze this any further.
*/
too_short(ts, "Ethernet header");
return;
}
/* Skip over the Ethernet header. */
packet += sizeof(struct ether_header);
capture_len -= sizeof(struct ether_header);
if (capture_len < sizeof(struct ip))
{ /* Didn't capture a full IP header */
too_short(ts, "IP header");
return;
}
ip = (struct ip*) packet;
IP_header_length = ip->ip_hl * 4; /* ip_hl is in 4-byte words */
if (capture_len < IP_header_length)
{ /* didn't capture the full IP header including options */
too_short(ts, "IP header with options");
return;
}
if (ip->ip_p != IPPROTO_UDP)
{
problem_pkt(ts, "non-UDP packet");
return;
}
/* Skip over the IP header to get to the UDP header. */
packet += IP_header_length;
capture_len -= IP_header_length;
if (capture_len < sizeof(struct UDP_hdr))
{
too_short(ts, "UDP header");
return;
}
udp = (struct UDP_hdr*) packet;
printf("%s UDP src_port=%d dst_port=%d length=%d\n",
timestamp_string(ts),
ntohs(udp->uh_sport),
ntohs(udp->uh_dport),
ntohs(udp->uh_ulen));
}
int main(int argc, char *argv[])
{
pcap_t *pcap;
const unsigned char *packet;
char errbuf[PCAP_ERRBUF_SIZE];
struct pcap_pkthdr header;
/* Skip over the program name. */
++argv; --argc;
/* We expect exactly one argument, the name of the file to dump. */
if ( argc != 1 )
{
fprintf(stderr, "program requires one argument, the trace file to dump\n");
exit(1);
}
pcap = pcap_open_offline(argv[0], errbuf);
if (pcap == NULL)
{
fprintf(stderr, "error reading pcap file: %s\n", errbuf);
exit(1);
}
/* Now just loop through extracting packets as long as we have
* some to read.
*/
while ((packet = pcap_next(pcap, &header)) != NULL)
dump_UDP_packet(packet, header.ts, header.caplen);
// terminate
return 0;
}
/* Note, this routine returns a pointer into a static buffer, and
* so each call overwrites the value returned by the previous call.
*/
const char *timestamp_string(struct timeval ts)
{
static char timestamp_string_buf[256];
sprintf(timestamp_string_buf, "%d.%09d",
(int) ts.tv_sec, (int) ts.tv_usec);
return timestamp_string_buf;
}
void problem_pkt(struct timeval ts, const char *reason)
{
fprintf(stderr, "%s: %s\n", timestamp_string(ts), reason);
}
void too_short(struct timeval ts, const char *truncated_hdr)
{
fprintf(stderr, "packet with timestamp %s is truncated and lacks a full %s\n",
timestamp_string(ts), truncated_hdr);
}
Any expert here could advice how could I display the nanosecond part of the timestamp by using libpcap?
Use the top-of-the-Git-trunk version of libpcap, open the capture file with
pcap_open_offline_with_tstamp_precision(pathname, PCAP_TSTAMP_PRECISION_NANO, errbuf);
and treat the struct timeval in the pcap_pkthdr structure as being seconds and nanoseconds rather than seconds and microseconds (i.e., have your program treat tv_usec as nanoseconds rather than microseconds - a bit confusing, but I'm not sure there's a less-ugly solution).
Related
I am trying to write a C program using the I/O call system in Ubuntu.
I found this documentation, CDROM API from Linux-sxs.org, but I don't understand where to find those arguments.
Can you please give me an example about how to use the ioctl() function?
struct cdrom_read_audio ra
{
union cdrom_addr addr; /* REQUIRED frame address */
u_char addr_format; /* REQUIRED .....CDROM_LBA or CDROM_MSF */
int nframes; /* REQUIRED number of 2352-byte-frames to read*/
u_char *buf; /* REQUIRED frame buffer (size: nframes*2352 bytes) */
};
if (ioctl(cdrom, CDROMREADAUDIO, &ra)<0)
{
perror("ioctl");
exit(1);
}
According to the kernel documentation for the cdrom driver, cdrom.txt, the format of the command is as follows:
CDROMREADAUDIO (struct cdrom_read_audio)
usage:
struct cdrom_read_audio ra;
ioctl(fd, CDROMREADAUDIO, &ra);
inputs:
cdrom_read_audio structure containing read start
point and length
outputs:
audio data, returned to buffer indicated by ra
error return:
EINVAL format not CDROM_MSF or CDROM_LBA
EINVAL nframes not in range [1 75]
ENXIO drive has no queue (probably means invalid fd)
ENOMEM out of memory
The format of the cdrom_read_audio struct can be found in cdrom.h:
/* This struct is used by the CDROMREADAUDIO ioctl */
struct cdrom_read_audio
{
union cdrom_addr addr; /* frame address */
__u8 addr_format; /* CDROM_LBA or CDROM_MSF */
int nframes; /* number of 2352-byte-frames to read at once */
__u8 __user *buf; /* frame buffer (size: nframes*2352 bytes) */
};
It uses a union cdrom_addr type, defined in the same file:
/* Address in either MSF or logical format */
union cdrom_addr
{
struct cdrom_msf0 msf;
int lba;
};
Here we have a choice - use MSF (Mintues-Seconds-Frames) or LBA (Logical Block Addressing). Since you're reading audio, you'll probably want MSF. struct cdrom_msf0 can also be found in the header file:
/* Address in MSF format */
struct cdrom_msf0
{
__u8 minute;
__u8 second;
__u8 frame;
};
With this research, we can write a simple test:
#include <sys/ioctl.h> //Provides ioctl()
#include <linux/cdrom.h> //Provides struct and #defines
#include <unistd.h> //Provides open() and close()
#include <sys/types.h> //Provides file-related #defines and functions
#include <sys/stat.h> //Ditto
#include <fcntl.h> //Ditto
#include <stdlib.h> //Provides malloc()
#include <string.h> //Provides memset()
#include <stdint.h> //Provides uint8_t, etc
#include <errno.h> //Provides errno
#include <stdio.h> //Provides printf(), fprintf()
int main()
{
int fd = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
if (errno != 0)
{
fprintf(stderr, "Error opening file: %u\n", errno);
return -1;
}
struct cdrom_msf0 time; //The start read time ...
time.minute = 2;
time.second = 45;
time.frame = 0;
union cdrom_addr address; //... in a union
address.msf = time;
struct cdrom_read_audio ra; //Our data object
ra.addr = address; //With the start time
ra.addr_format = CDROM_MSF; //We used MSF
ra.nframes = CD_FRAMES; //A second - 75 frames (the most we can read at a time anyway)
uint8_t* buff = malloc(CD_FRAMES * CD_FRAMESIZE_RAW); //Frames per second (75) * bytes per frame (2352)
memset(buff, 0, CD_FRAMES * CD_FRAMESIZE_RAW); //Make sure it's empty
ra.buf = buff; //Set our buffer in our object
if (ioctl(fd, CDROMREADAUDIO, &ra) != 0) //The ioctl call
{
fprintf(stderr, "Error giving ioctl command: %u\n", errno);
return -1;
}
for (int frame = 0; frame < CD_FRAMES; frame++) //A hexdump (could be a real use for the data)
{
printf("Frame %u:", frame);
for (int byte = 0; byte < CD_FRAMESIZE_RAW; byte++)
{
printf(" %.2X", buff[frame * CD_FRAMESIZE_RAW + byte]);
}
printf("\n");
}
close(fd); //Close our file
return 0; //And exit
}
Make sure you use an audio CD, or the ioctl call will throw EIO (with a CD-ROM, for example). In reality, you might write this data to file, or process it. Either way, you'd likely end up reading more than one second using a loop.
i have made a packet sniffer using libpcap on C++.
I am using pcap_loop and calling a loopback function , which at the moment i havent put much thought of.
Here is my code.
int PacketSniff(int *count)
{
int ifnum;
int NumOfDevs=0;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 ip;
bpf_u_int32 netmask;
struct in_addr ip_addr , netmask_addr;
pcap_if_t *devs , *d;
pcap_t *handler;
char packet_filter[] = "ip";
struct bpf_program fcode;
/* Find all interface devices */
pcap_findalldevs(&devs, errbuf);
for(d=devs; d; d=d->next)
{
printf("%d. %s", ++NumOfDevs, d->name);
if (d->description)
{
printf(" (%s)\n", d->description);
}
else
{
printf(" (No description available)\n");
}
}
if(NumOfDevs==0)
{
printf("\nNo interfaces found!\n");
return (-1);
}
/* Prompt User to select interface */
printf("Enter the interface number (1-%d):\n",NumOfDevs);
scanf("%d",&ifnum);
if(ifnum < 1 || ifnum > NumOfDevs)
{
printf("\nInterface number out of range.\n");
/* Free the device list */
pcap_freealldevs(devs);
return (-1);
}
/* Jump to the selected adapter/interface */
for(d=devs; ifnum>1 ;d=d->next, ifnum--);
/* Open the selected adapter/interface */
handler = pcap_open_live(d->name, 65535, 0, 2000, errbuf);
if ((handler = pcap_open_live(d->name, 65535, 0, 2000, errbuf)) == NULL)
{
fprintf(stderr, "Couldn't open device %s: %s\n", d->name, errbuf);
return(-1);
}
if (pcap_datalink(handler) != DLT_EN10MB )
{
fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
pcap_freealldevs(devs);
return -1;
}
/* This means that we set the datalink layer header size at 14 */
int linkhdrlen = 14;
if (pcap_lookupnet(d->name, &ip, &netmask, errbuf) <0 )
{
fprintf(stderr, "Can't get netmask for device %s\n", d->name);
netmask = 0;
ip = 0;
}
/* Compile the filter */
if (pcap_compile(handler, &fcode, packet_filter, 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax. Error: %s\n", errbuf);
pcap_freealldevs(devs);
return -1;
}
/* Set the filter */
if (pcap_setfilter(handler, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter. Error: %s\n", errbuf);
pcap_freealldevs(devs);
return -1;
}
printf("\nListening for packets on interface <%s>...\n", d->name);
/* At this point, we don't need any more the device list. Free it */
pcap_freealldevs(devs);
pcap_loop(handler, 0, my_callback, NULL);}
And my_callback is like this:
void my_callback(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_ptr){
struct tm ltime;
char timestr[16];
struct ip_header *iphdr;
struct tcp_header *tcphdr;
time_t local_tv_sec;
/* Convert the timestamp to readable format */
local_tv_sec = header->ts.tv_sec;
localtime_r(&local_tv_sec , <ime);
strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);
/* Print timestamp and length of the packet */
printf("Time >> %s.%.6d \nPacket Length:%d \n\n", timestr, header->ts.tv_usec, header->len);
/* Retireve the position of the ip header http://www.tcpdump.org/pcap.html */
iphdr = (ip_header *) (pkt_ptr +14);
// Advance to the transport layer header then parse and display
// the fields based on the type of hearder: tcp, udp or icmp.
pkt_ptr += 4*iphdr->ver_ihl ;
tcphdr = (tcp_header *)(pkt_ptr + 14);
/* print ip addresses and tcp ports */
printf("%d.%d.%d.%d : %d ---> %d.%d.%d.%d : %d\n\n",
iphdr->saddr.byte1,
iphdr->saddr.byte2,
iphdr->saddr.byte3,
iphdr->saddr.byte4,
tcphdr->src_port,
iphdr->daddr.byte1,
iphdr->daddr.byte2,
iphdr->daddr.byte3,
iphdr->daddr.byte4,
tcphdr->dst_port);}
Now i can sniff packets and print various things .
But my goal is to Extract Stats from the packets (like numOfpackets , numOfTCPpackets , numOfIncomingPAcket , numOfOutgoingPackets , Packet Size Variance , Time Interval Variance ) while they are being sniffed .
But i want this to be done in 1000ms Time-Windows.
For example: Every 1000ms i want an output file of..
numOfTCPPackets = ....
numof = ....
.
.
.
My questions are :
How can i incorporate those Time-Windows?
How to extract the needed stats without interfering too muchwith the sniffing speed.?
Thank you a lot!
Use pcap_next() instead of pcap_loop() to get the packet and set the timeout with pcap_set_timeout() to a small value such as 10 milliseconds to prevent pcap_next() blocking forever so that your code to write the statistics to the file gets a chance to run. You need to call pcap_next() in a loop and have code like the following after the pcap_next() call:
if (cur_time64() - last_time64 >= stat_time64)
{
last_time64 += stat_time64;
print_statistics_to_file();
}
...where cur_time64() returns the current time as a 64-bit integer in microseconds since the epoch (you can use gettimeofday() to implement cur_time64() if you use a Unix-like operating system). stat_time64 would be 1 second, i.e. 1000*1000, in your example.
Then, proceed to process the packet. Check the return value of pcap_next() to see if it returned a packet: if no, continue the loop; if yes, process the packet.
To do the checks without interfering too much with the sniffing speed, your only option is to write the code as efficiently as possible. Handle only those header fields you absolutely need to handle, i.e. you can avoid checking the checksums of IP and TCP headers.
How can I get the interface name for the IP address in linux from C code ?
e.g. I'd like to get the interface name ( like etho , eth1 , l0 ) assigned for the IP address 192.168.0.1
Using /proc/net/arp you can match it. Here is a command line tool example.
usage: getdevicebyip 192.168.0.1
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv){
if (argc < 2) return 1;
FILE *fp = fopen("/proc/net/arp", "r");
char ip[99], hw[99], flags[99], mac[99], mask[99], dev[99], dummy[99];
fgets(dummy, 99, fp); //header line
while (fscanf(fp, "%s %s %s %s %s %s\n", ip, hw, flags, mac, mask, dev) != EOF)
if (!strcmp(argv[1],ip))
printf("%s\n",dev);
return 0;
}
You can use getifaddrs. See man 3 getifaddrs for usage information. This will only work on a Unix-like systems.
netlink is a way to do this on Linux. I think it might even be a proper way to do it on Linux (even though it isn't portable).
The strategy is:
Get a list of addresses on interfaces from the kernel by sending a netlink message.
Find the address you want (I have hard coded the one I want as address_dq) and record its interface (a number at this stage)
Get a list of interfaces by sending another netlink message,
Find the number of the interface matching the number you recorded in step (2).
Get the name of the interface.
The code below is not pretty, but I'm sure you could do a better job of it. I have been a especially sloppy by not checking for a multipart message (checking for the NLM_F_MULTI flag and for a message type of NLMSG_DONE is the way to do it). Instead I have just assumed the response to the first message is multipart -- it is on my machine -- and chewed up the NLMSG_DONE message which follows.
Code...
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, void ** argv) {
// This is the address we want the interface name for,
// expressed in dotted-quad format
char * address_dq = "127.0.0.1";
// Convert it to decimal format
unsigned int address;
inet_pton(AF_INET, address_dq, &address);
char buf[16384];
// Our first message will be a header followed by an address payload
struct {
struct nlmsghdr nlhdr;
struct ifaddrmsg addrmsg;
} msg;
// Our second message will be a header followed by a link payload
struct {
struct nlmsghdr nlhdr;
struct ifinfomsg infomsg;
} msg2;
struct nlmsghdr *retmsg;
// Set up the netlink socket
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
// Fill in the message
// NLM_F_REQUEST means we are asking the kernel for data
// NLM_F_ROOT means provide all the addresses
// RTM_GETADDR means we want address information
// AF_INET means limit the response to ipv4 addresses
memset(&msg, 0, sizeof(msg));
msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
msg.nlhdr.nlmsg_type = RTM_GETADDR;
msg.addrmsg.ifa_family = AF_INET;
// As above, but RTM_GETLINK means we want link information
memset(&msg2, 0, sizeof(msg2));
msg2.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg2.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
msg2.nlhdr.nlmsg_type = RTM_GETLINK;
msg2.infomsg.ifi_family = AF_UNSPEC;
// Send the first netlink message
send(sock, &msg, msg.nlhdr.nlmsg_len, 0);
int len;
// Get the netlink reply
len = recv(sock, buf, sizeof(buf), 0);
retmsg = (struct nlmsghdr *)buf;
// Loop through the reply messages (one for each address)
// Each message has a ifaddrmsg structure in it, which
// contains the prefix length as a member. The ifaddrmsg
// structure is followed by one or more rtattr structures,
// some of which (should) contain raw addresses.
while NLMSG_OK(retmsg, len) {
struct ifaddrmsg *retaddr;
retaddr = (struct ifaddrmsg *)NLMSG_DATA(retmsg);
int iface_idx = retaddr->ifa_index;
struct rtattr *retrta;
retrta = (struct rtattr *)IFA_RTA(retaddr);
int attlen;
attlen = IFA_PAYLOAD(retmsg);
char pradd[128];
// Loop through the routing information to look for the
// raw address.
while RTA_OK(retrta, attlen) {
if (retrta->rta_type == IFA_ADDRESS) {
// Found one -- is it the one we want?
unsigned int * tmp = RTA_DATA(retrta);
if (address == *tmp) {
// Yes!
inet_ntop(AF_INET, RTA_DATA(retrta), pradd, sizeof(pradd));
printf("Address %s ", pradd);
// Now we need to get the interface information
// First eat up the "DONE" message waiting for us
len = recv(sock, buf, sizeof(buf), 0);
// Send the second netlink message and get the reply
send(sock, &msg2, msg2.nlhdr.nlmsg_len, 0);
len = recv(sock, buf, sizeof(buf), 0);
retmsg = (struct nlmsghdr *)buf;
while NLMSG_OK(retmsg, len) {
struct ifinfomsg *retinfo;
retinfo = NLMSG_DATA(retmsg);
if (retinfo->ifi_index == iface_idx) {
retrta = IFLA_RTA(retinfo);
attlen = IFLA_PAYLOAD(retmsg);
char prname[128];
// Loop through the routing information
// to look for the interface name.
while RTA_OK(retrta, attlen) {
if (retrta->rta_type == IFLA_IFNAME) {
strcpy(prname, RTA_DATA(retrta));
printf("on %s\n", prname);
exit(EXIT_SUCCESS);
}
retrta = RTA_NEXT(retrta, attlen);
}
}
retmsg = NLMSG_NEXT(retmsg, len);
}
}
}
retrta = RTA_NEXT(retrta, attlen);
}
retmsg = NLMSG_NEXT(retmsg, len);
}
}
When run as above, returns Address 127.0.0.1 on lo.
Using "192.168.1.x" instead of "127.0.0.1" it instead returns Address 192.168.1.x on eth0.
I have written a program to set the MTU size of the particular interface(say eth0 or eth1) to 1100. And the Request message is send from user space using Netlink sockets via NETLINK_ROUTE option.
The message is sent successfully from user space, but when i verified the ifconfig eth0, the MTU size still shows the old value (1500). Am I verifying correctly? And how do I know the kernel is setting the MTU size correctly or not? Please find my program below and correct me if i am wrong.
#include <stdio.h>
#include <stdlib.h>
#include <net/if.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdbool.h>
struct {
struct nlmsghdr nh;
struct ifinfomsg ifinfo;
char data[100];
}req;
int ret;
struct rtattr *rta;
/* MTU Set */
unsigned int mtu = 1100;
/* rtNetlinkSockFd */
int rtNetlinkSockFd = -1;
int main()
{
int ret;
rtNetlinkSockFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if(rtNetlinkSockFd < 0)
{
printf("Creation of NetLink Socket is failed \n");
return -1;
}
/* Memset the Requested Structure */
memset( &req, 0x00, sizeof(req));
/* Populate the Netlink Header Fields */
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
/* Link Layer: RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK, RTM_SETLINK */
req.nh.nlmsg_type = RTM_SETLINK;
/* NLM_F_REQUEST Must be set on all request messages. */
req.nh.nlmsg_flags = NLM_F_REQUEST;
req.nh.nlmsg_seq = 0;
req.nh.nlmsg_pid = 0; //getpid();
/* Populate the Ifinfo Structure Attributes */
req.ifinfo.ifi_family = AF_UNSPEC;
/* Give the Interface Name and get the Index */
req.ifinfo.ifi_index = if_nametoindex("eth0");
printf(" The NetLink Ifi_index :%d\n", req.ifinfo.ifi_index);
/* ifi_change is reserved for future use and
* should be always set to 0xFFFFFFFF. */
req.ifinfo.ifi_change = 0xFFFFFFFF;
req.ifinfo.ifi_type = 0;
req.ifinfo.ifi_flags = 0;
/* RTA is Pointed to (req+32) it means points to the DATA */
rta = (struct rtattr *)(((char *) &req) + NLMSG_ALIGN(req.nh.nlmsg_len));
/* IFLA_MTU unsigned int MTU of the device. */
rta->rta_type = IFLA_MTU;
/* Len Attribute */
rta->rta_len = sizeof(unsigned int);
req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + RTA_LENGTH(sizeof(mtu));
memcpy(RTA_DATA(rta), &mtu, sizeof(mtu));
ret = send(rtNetlinkSockFd, &req, req.nh.nlmsg_len, 0);
if (ret < 0)
{
printf( "netlink: Sending failed: (assume operstate is not supported)");
}
else
{
printf( "netlink: Sent successfully");
}
return 0;
}
I think you need to write:
rta->rta_len = RTA_LENGTH(sizeof(unsigned int));
instead of:
rta->rta_len = sizeof(unsigned int);
Use RTM_NEWLINK instead of RTM_SETLINK.
req.nh.nlmsg_type = RTM_NEWLINK;
See the manpage example.
I'm following http://backreference.org/2010/03/26/tuntap-interface-tutorial/
The following code successfully gets a fd (usually 3) when I run it as root, but it does not create a /dev/tun77 device.
Should it?
#include <stdio.h>
#include <stdlib.h>
#include <net/if.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <linux/if_tun.h>
#include <linux/ioctl.h>
#define IFNAMSIZ 16
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
/* Arguments taken by the function:
*
* char *dev: the name of an interface (or '\0'). MUST have enough
* space to hold the interface name if '\0' is passed
* int flags: interface flags (eg, IFF_TUN etc.)
*/
/* open the clone device */
if( (fd = open(clonedev, O_RDWR)) < 0 ) {
return fd;
}
/* preparation of the struct ifr, of type "struct ifreq" */
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
if (*dev) {
/* if a device name was specified, put it in the structure; otherwise,
* the kernel will try to allocate the "next" device of the
* specified type */
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
close(fd);
return err;
}
/* if the operation was successful, write back the name of the
* interface to the variable "dev", so the caller can know
* it. Note that the caller MUST reserve space in *dev (see calling
* code below) */
strcpy(dev, ifr.ifr_name);
/* this is the special file descriptor that the caller will use to talk
* with the virtual interface */
return fd;
}
int main(void) {
char tun_name[IFNAMSIZ];
int nread, tun_fd;
char buffer[2048];
/* Connect to the device */
strcpy(tun_name, "tun77");
tun_fd = tun_alloc(tun_name, IFF_TUN | IFF_NO_PI); /* tun interface */
if (tun_fd < 0){
perror("Allocating interface");
exit(1);
} else {
printf("connected to %s on fd: %i\n", tun_name, tun_fd);
}
/* Now read data coming from the kernel */
while (1) {
/* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
nread = read(tun_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
}
return EXIT_SUCCESS;
}
Waldner answered this on http://backreference.org/2010/03/26/tuntap-interface-tutorial/ with:
Neither. Network interfaces in Linux don't appear under /dev; the only thing you'll see there is /dev/net/tun, which is the device that should be opened as the first step to create a tun/tap interface.
If you run the sample code, you'll be able to see and configure the interface you create by using "ip link" while the program is running; when the program terminates, the interface disappears.
Alternatively, the interface can be made persistent, as explained, and in that case it will survive program termination.
In any case, no device is created under /dev (apart from the already mentioned /dev/net/tun).