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.

C ‘memcpy’ Examples

The C memcpy command provides mechanism to copy a set of bytes from location to another. It is quite similar to the strcpy function. The advantage of memcpy is that you can copy strings, or bytes, or data, or structures, or anything you want. The memcpy function does not discriminate.

The C memcpy function definition:

The definition of the memcpy function takes 3 arguments and is defined in the string.h header file.

#include <string.h>
 
void *memcpy(void *dest, const void *src, size_t n);

The first argument is the copy destination, the place where you want your data to be copied to. The second argument is the source data, and finally the third argument is the size of the area you want to be copied, defined as the number of bytes. The programmer also must ensure that the memory areas are not overlapping memory areas. Once the copy is complete the function will return a pointer to the start of the newly copied data area.

C memcpy Examples

What is a memcpy examples article without an example?

#include <string.h>
#include <stdio.h>
 
int main(int argc, char **argv)
{
	int i, j;
 
	i = 10;
	memcpy(&j, &i, sizeof(i));
 
	fprintf(stdout, "J = %d\n", j);
 
	return 0;
}

This is just a silly example of using memcpy to copy the data at the address of i to the address of j. Thus the output from this function is:

$ ./main
J = 10

Obviously memcpy is far more powerful than this and we could copy data from one structure to another quite easily:

typedef struct {
  int i;
  char c;
} memcpy_ex_t;
 
int main(int argc, char **argv)
{
    memcpy_ex_t a, b;
 
    a.i = 123;
    a.c = 'e';
 
    fprintf(stdout, "Mem structure A: i = %u | c = %c\n", a.i, a.c);
 
    memcpy(&b, &a, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    return 0;
}

The output from this you can probably guess:

$ ./main 
Mem structure A: i = 123 | c = e
Mem structure B: i = 123 | c = e

You could even use memcpy to clear your structure or element by simply creating a structure that is zeroed. For example if we modify the above program, and looking at lines 3, 14, 16 specifically for the additions:

int main(int argc, char **argv)
{
    memcpy_ex_t a, b, c = { 0 };
 
    a.i = 123;
    a.c = 'e';
 
    fprintf(stdout, "Mem structure A: i = %u | c = %c\n", a.i, a.c);
 
    memcpy(&b, &a, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    memcpy(&b, &c, sizeof(memcpy_ex_t));
 
    fprintf(stdout, "Mem structure B: i = %u | c = %c\n", b.i, b.c);
 
    return 0;
}

The output then becomes:

$ ./main 
Mem structure A: i = 123 | c = e
Mem structure B: i = 123 | c = e
Mem structure B: i = 0 | c =

Your imagination … and the compiler are probably your limits. Be careful though, especially with overlapping memory locations and the cost of doing memory copies is not cheap.

Using The Bash Shell Environment

The Bash shell (bourne-again shell) is the most common default shell on most Linux distributions. Having a working knowledge of the environment is useful for any novice Linux user. This article will outline how to view, set, change, and retain environment variables in your shell.

Out-of-the-box Linux installs have a somewhat long list of environment variables that are set for various programs to use, so do not be alarmed by the amount of output.

1. How To View Bash Shell Environment Variables Using env

To view all the variables currently set in your Bash environment you can enter the command env. This will output all variables currently set in your environment. For example:

erik@debian:~$ env
TERM=xterm
SHELL=/bin/bash
SSH_CLIENT=X.X.X.X 51688 22
SSH_TTY=/dev/pts/2
USER=erik
LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:
SSH_AUTH_SOCK=/tmp/ssh-Hdelx27792/agent.27792
MAIL=/var/mail/erik
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
PWD=/home/erik
LANG=en_CA.UTF-8
HISTCONTROL=ignoredups
SHLVL=1
HOME=/home/erik
LOGNAME=erik
SSH_CONNECTION=X.X.X.X 51688 X.X.X.X 22
LESSOPEN=| /usr/bin/lesspipe %s
DISPLAY=localhost:11.0
LESSCLOSE=/usr/bin/lesspipe %s %s
_=/usr/bin/env

As you can see by the output, there are a large set of values. Some of which make logical sense. For instance, HOME=/home/erik is my home directory upon login. My current working directory of PWD=/home/erik. An interesting variable is the _=/usr/bin/env displays the last command executed by me.

2. Display Specific Environment Variable In Bash Using echo $VAR

Lets say there is a specific variable that you want to view instead of displaying the whole set and grepping for that variable. You can just enter the command echo $VAR where $VAR is the environment variable you want to view.

erik@debian:~$ echo $SHELL
/bin/bash

3. Create Environment Variable Temporarily In Bash

To temporarily create a variable in Bash you can simply enter erik=”hi” . Then doing a echo $erik the prompt will display “hi”. However, in doing so, the variable will not show up in a env output. To modify the variable you can:

erik@debian:~$ echo $erik
hi
erik@debian:~$ erik="no"
erik@debian:~$ echo $erik
no

These methods of creating variables is local only to this shell and will not be seen to child shells.

4. Creating Environment Variable In Bash Using export

For a Bash variable to persist to a child shell, and also show up during an env output, the user must use the export feature.

erik@debian:~$ data=hi
erik@debian:~$ echo $data
hi
erik@debian:~$ env | grep data
erik@debian:~$ export data
erik@debian:~$ env | grep data
data=hi

In this example I have created an environment variable called data in which a child shell could use. Keep in mind that this variable is only present in this session. If you were to logout, then log back in, the data variable would disappear.

5. Removing Environment Variable In Bash Using unset

At this point we now have the data variable in our environment. But perhaps we want to remove it because it smells bad. Its actually quite easy to do so.

erik@debian:~$ echo $data
hi
erik@debian:~$ env | grep data
data=hi
erik@debian:~$ unset data
erik@debian:~$ echo $data
 
erik@debian:~$ env | grep data
erik@debian:~$

So in this example I showed the data variable is set, and can be found in our environment. I then ran the unset command to remove the variable. I then tried to display the contents of our variable, and as you can see it no longer has a value and is no longer part of our environment.

Linux Interface Configuration: ifconfig

This article will outline how to display, configure, modify your network interface using ifconfig from the Linux command line.

Most Linux distributions these days have a fancy graphical user interface for changing your system settings, but sometimes you have to get down and dirty and use the command line. In some cases you are just ssh’d into your machine and do not have the option of using your gui.

Here are a set of examples for using ifconfig (depending on your distribution you may have to use sudo or run as root).

1. Display All Interface(s) Information Using ifconfig -a

If you do not know the interface names currently on your system using the -a flag will display all devices.

erik@debian:~$ /sbin/ifconfig -a
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1047636 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1101531 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152028471 (144.9 MiB)  TX bytes:1003430862 (956.9 MiB)
          Interrupt:185 Base address:0x2000
 
eth7      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:EA
          inet6 addr: fe80::20c:29ff:fe5c:8eea/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7456 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:984140 (961.0 KiB)  TX bytes:468 (468.0 b)
          Interrupt:169 Base address:0x2080
 
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:28841 errors:0 dropped:0 overruns:0 frame:0
          TX packets:28841 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:4458291 (4.2 MiB)  TX bytes:4458291 (4.2 MiB)

As you can see, on my system I have eth6, eth7, an lo (the loopback) interface. This also displays the type of Link I have, the HWaddr or MAC address of that interface. You may recognize that I am using a VMware virtual machine via the OUI. I currently have IPv6 support, hence the IPv6 address. Among other things, there is also the packet count of received and sent packets and the total byte count. Pretty handy!

2. Display Specific Interface Information Using ifconfig ethX

Lets say you know which interface you want to look at. Execute the command ifconfig eth0 where eth0 is the interface name you want to look at.

erik@debian:~$ /sbin/ifconfig eth6
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1047863 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1101749 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152082031 (145.0 MiB)  TX bytes:1003540856 (957.0 MiB)
          Interrupt:185 Base address:0x2000

3. Set The IP Address Of A Specific Interface

Now that you know how to display your interface, you probably want to know how to set the IP address. It is quite easy:

erik@debian:~$ /sbin/ifconfig eth6 192.168.3.100 up

Or if you need to set your network mask as well:

erik@debian:~$ /sbin/ifconfig eth6 192.168.3.100 netmask 255.255.255.0 up

4. Set The MAC Address Of A Specific Interface

To set the MAC address of an interface using ifconfig do the following:

erik@debian:~$ /sbin/ifconfig eth0 hw ether de:ad:be:ef:fe:ed

5. Set the MTU Size For Your Interface

Sometimes you may need to say the maximum transmission unit for your interface, especially if you are part of a virtual lan (vlan). To set the MTU size with ifconfig:

erik@debian:~$ /sbin/ifconfig eth0 mtu 1300 up

6. Clear The IP Address And Bring The Interface Down

To clear the IP address and bring your interface offline execute the command with ifconfig:

erik@debian:~$ /sbin/ifconfig eth0 0.0.0.0 down

7. Create Alias or Virtual Interface With ifconfig:

To make a virtual interface or network alias with ifconfig use the colon. For example, your interface is connected to a network with a switch, and on that switch there are two distinct networks. You can create an alias that will allow you to be on both networks simultaneously.

debian:/home/erik# ifconfig eth6:1 192.168.100.23 up
debian:/home/erik# ifconfig -a
eth6      Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.3.123  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe5c:8ee0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1048915 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1102915 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:152305452 (145.2 MiB)  TX bytes:1004365368 (957.8 MiB)
          Interrupt:185 Base address:0x2000
 
eth6:1    Link encap:Ethernet  HWaddr 00:0C:29:5C:8E:E0
          inet addr:192.168.100.23  Bcast:192.168.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:185 Base address:0x2000

To remove the alias just enter:

debian:/home/erik# ifconfig eth6:1 down

Get Bytes Available on C Socket

Ever wondered how to read how many bytes are received on a socket before you decide you need to do some processing on it? It can be achieved by doing a simple ioctl(input/output control) call on the socket itself. Here is a little function I have used in the past.

long bytes_on_socket(int socket)
{
        size_t nbytes = 0;
        if ( ioctl(fd, FIONREAD, (char*)&nbytes) < 0 )  {
                fprintf(stderr, "%s - failed to get byte count on socket.\n", __func__);
                syslog(LOG_ERR, " %s - failed to get byte count on socket.\n", __func__);
                return -1;
        }
        return( (long)nbytes );
}

It is simple and easy to use. It could be placed right after select has returned because data has arrived, perhaps you only want to process packets above a certain byte count.