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.

How To: View or Set Socket Receive Buffer Size

A Linux system has a default socket buffer size for receiving and sending data. These values can be modified on most Linux systems, provided your process is running as root. Modifying these values can help increase network processing performance on both send and receive of packets.

Linux has a set of default values that are viewable via the /proc filesystem:

/proc/sys/net/core/
 
/rmem_default: The default setting of the socket receive buffer in bytes.
 
/rmem_max: The maximum receive socket buffer size in bytes.
 
/wmem_default: The default setting (in bytes) of the socket send buffer.
 
/wmem_max: The maximum send socket buffer size in bytes.

If you output the rmem_max you can view the size in bytes:

$ cat /proc/sys/net/core/rmem_max
131071

These values can be modified by echoing a value to these variables. If you plan on modifying the socket receive buffer size via a C command then the largest value you can set will be less than or equal to what is specified in the rmem_max variable. So if you require a very large value, you must modify the kernel maximum value rmem_max, then modify the value of your socket.

Read Current Socket Receive Buffer Size

To read your current socket receive buffer size is quite easy in C, its just a matter of using the correct function with the correct set of arguments. The function used to get socket information is known as getsockopt, which takes the arguments:

int getsockopt(int sockfd, int level, int optname, void *optval,
 socklen_t *optlen);

The first argument is your socket, the second argument is the API level, optname is the socket option (SO_RCVBUF), optval is value read back from the socket stating the receive buffer size, and optlen is the is the size of the buffer point to by optval. So lets put this into practice:

unsigned int m;
int n;
 
m = sizeof(n);
getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (void *)&n, &m);
fprintf(stdout, "The socket receive buffer size is: %u\n", n);

The integer n contains the returned buffer size. You will notice the option name used SO_RCVBUF. This is outlined in the socket manual page, among other read/set values available. The manual page output for SO_RCVBUF is:

SO_RCVBUF
Sets or gets the maximum socket receive buffer in bytes. The kernel doubles this value (to allow space for bookkeeping overhead) when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). The default value is set by the /proc/sys/net/core/rmem_default file, and the maximum allowed value is set by the /proc/sys/net/core/rmem_max file.

Now that we can read the value, lets increase the maximum size and set a new value.

Set Socket Receive Buffer Size

To increase the socket buffer size for receives we utilize the setsockopt function.

echo 2097152 > /proc/sys/net/core/rmem_max

Then in C we can now set our socket to a value half that size…lets not break the bank.

int setsockopt(int sockfd, int level, int optname, cont void *optval,
 socklen_t optlen);

The arguments for the function are nearly identical. The optval is now a const void, and the socklen_t now takes the actual variable rather than address of. To set a size:

int buff_size;
 
// In bytes
buff_size = 1048576;
 
setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buff_size, (int)sizeof(buff_size));

As mentioned earlier this function will set the value to less than or equal to the maximum size set within the kernel variable (rmem_max). There are also other values that can be set or modified in kernel. Have fun tweaking these values to get better performance.

Set File Permissions With chmod In Linux

If you have ever needed to protect your files, or set a directory to read only in Linux, you have probably used chmod. If you didn’t, then you probably wished that you had heard of chmod. Chmod stands for change mode and allows the user to modify access permissions to specific files.

To display a file or directories current permissions you can use the ls command. For example:

$ ls -l
-rw-r--r--  1 erik erik       1014 2010-10-28 13:30 taps.sh

The first column of -rw-r–r– is actually showing you the current file permission for taps.sh . The output is actually broken into four parts.

-| rw- | r– | r– . The first column is used to identify:

  • – denotes a regular file
  • d denotes a directory
  • b denotes a block special file
  • c denotes a character special file
  • l denotes a symbolic link
  • p denotes a named pipe
  • s denotes a domain socket

The second column outlines what the current users permissions are, the third column outlines what group members are allowed to do, and the forth column outlines what other users may do. The following table outlines in detail the column information. The chmod functions as:

$ chmod [Reference][Operator][Mode] file1 file2 etc.

ReferenceClassDescription
uuserthe current file owner
ggroupthe file group members
oothersgeneric users who are not part of the group or owner of the file
aallugo is equivalent to the ‘a’

The chmod command will use the following operations to modify the user, group, and other fields. For example:

OperatorDescription
+the addition sign tells chmod to add the value
the dash will remove the modes from the class we specify
=this sets the exact mode for the file

The modes indicate which permissions are to be set for read, write, execute. For example:

ModeNameDescription
rreadsets the read permission
wwriteuse to write to a file or directory
xexecuteuse to set the execute for a file or directory

Here are some examples using the symbolic notation of changing the file permissions.

Add Read and Write Permissions to User and Group classes

For example:

$ ls -l taps.sh
-rw-r--r-- 1 erik erik 1014 2010-10-28 13:30 taps.sh
$ chmod ug+rw taps.sh
$ ls -l taps.sh
-rw-rw-r-- 1 erik erik 1014 2010-10-28 13:30 taps.sh

Notice in this example, only the group class (third column) changed because I already had write permissions as the current user.

Add Read, Write, Execute to User and Group

Lets say we want taps.sh to have all permissions to those who are lucky enough to be in our group, or be us. To do so:

$ chmod +rwx taps.sh
$ ls -l taps.sh
-rwxrwxr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh

Using the chmod +rwx command, we ‘add’ the read, write, and execute option to our file.

Well that is all great, but for me I usually want to other make a file read only for myself, or executable for anyone. This can be done using numerics rather than using these symbolic notations mentioned above.

Numeric Example For Read-Only

The numeric’s group values together, for example:

#Permission
7full
6read and write
5read and execute
4read only
3write and execute
2write only
1execute only
0none

I use chmod 644 filename quite often. This sets the permissions to read only for group and other, i.e. only the user may read or write to the file.

$ ls -l taps.sh
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh
$ chmod 644 taps.sh 
$ ls -l taps.sh
-rw-r--r-- 1 erik erik 1014 2010-10-28 13:30 taps.sh

Numeric Example For Execute

Another favourite of mine is execute by anyone. This comes in the form of 755.

$ ls -l taps.sh
-rw-r--r-- 1 erik erik 1014 2010-10-28 13:30 taps.sh
$ chmod 755 taps.sh 
$ ls -l taps.sh
-rwxr-xr-x 1 erik erik 1014 2010-10-28 13:30 taps.sh

So the 755, referring to the table means. User = 7 = all, Group = 5 = read and execute, Other = 5 = read and execute, as shown in the ls output above. This way you can mix and match the numerics, if you want it open to everyone you can use 777.

Change Permissions Recursively With ‘chmod -R’

If you want to set the permissions for all files under a directory to the same setting, you can pass the -R command in front of the permission you would like to use.

$ chmod -R 777 /home/erik/

This will set every file under my home directory to ‘all’, i.e. read, write, execute by anyone.

Hopefully this makes sense to you. Rather than giving you a huge set of examples, I hope I have taught you how to fish!

Linux Kernel: Unlikely / Likely Macros

At one point I was debugging some issue in the linux kernel and I came across an odd “if” statement. It was something along the lines of:

if(likely(buf)){
  ... do something fancy...
}

First I thought to myself…if likely what? Its likely that car runs on gas? I really had no idea what it meant.  It turns out the likely and unlikely macros actually inform the C compiler that a block of code will “likely” be called more often than say the else case, essentially branch prediction.  This little hint of information will change how the compiler boils the code down to assembly and can give your code a performance increase.  The likely and unlikely are defined in the “compiler.h” of the linux kernel.

#define likely(x)        __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

So if the case of likely is called, you expect !!x (not not of x) or x to be true (equal to 1).

As of GCC version >= 2.96 that __builtin_expect feature allows us developers the ability to tell the compiler branch prediction information that it in turn can use to create more efficient assembly code. According to the GCC manual page:

if (__builtin_expect (x, 0))
                foo ();
 
     [This] would indicate that we do not expect to call `foo', since we
     expect `x' to be zero.

Behind the scenes GCC will setup the assembly code to execute sequentially for the more ‘likely’ case, and save the jump for the more ‘unlikely’ case. Since the jump assembly commands are more costly and time consuming it makes sense to help inform the compiler of how your code should be run. If you know where your bottleneck is in code, this optimization can be helpful. This is especially why it is done in Kernel code!

C ‘offsetof’ Macro Explanation And Example

If you have ever had to deal with pointer alignment issues then the C offsetof macro is your friend. It is often quite difficult to identify alignment issues other than seeing odd behaviour in your program. It is assumed the compiler will pad structures accordingly based on the architecture you are compiling your code for. Unfortunately, this is not always the case, some architectures need all structures to be 32-bit aligned. This can be mitigated by using some C macros like __packed__ or __padded__, or even using some gcc flags. You can however, use some of the predefined macros in your program to help identify these odd behaviours, one of which is the offsetof Macro.

The offsetof macro will return the offset of the field member from the start of the structure type. It is defined as:

// Header required
#include <stddef.h>
 
// Function definition
size_t offsetof(type, member);

This is useful because the size of the fields that make the structure can differ across implementations. Some compilers will automatically pad bytes between fields to align them correctly for that CPU, these means that just because you have 1 char followed by 1 int, the int may not be 1 byte after the start of the first char.

Knowing how the bytes are laid out in memory can help identify where pointer alignment issues may occur, especially if you are pointing to structures and modifying data in memory.

An example of using the offsetof macro:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
 
struct test {
        char a;
        int b;
        double c;
        long d;
};
 
int main()
{
        struct test data;
 
        fprintf(stdout, "char: %u, int: %u, double: %u, long: %u\n", 
                offsetof(struct test, a), 
                offsetof(struct test, b), 
                offsetof(struct test, c), 
                offsetof(struct test, d));
 
        return 0;
}

Running the program:

$ ./offset 
char: 0, int: 4, double: 8, long: 16

All calculations are based off the start of the structure, and thus our char is at boundary 0, the integer begins 4 bytes after. Wait?! I thought a char was only 1 byte? Well guess what, it is, the compiler has padded the structure to have the integer begin on a 32bit boundary to help alleviate pointer alignment issues. This is what we want, and from our perspective it really makes no difference as long as when we reference the integer it references 4 bytes after char instead of simply 1 byte after char.

The offsetof macro is defined using a special form, lets take a look:

#define offsetof(st, m) __builtin_offsetof(st, m)

Behind the scenes this function does some pointer arithmetic to identify the offset of each member of the structure then returns that offset to the programmer. Nifty function! Using the offsetof function and displaying pointer addresses you can help narrow down oddities in your code that may be due to pointer alignment and byte boundary issues.

Byte Swap Little Endian and Big Endian

I have run into the Endian (not Indian) debate on numerous occasions. I even wrote an article explaining how to detect the Endianness of your operating system earlier. Simply detecting the endianness of your system is a good starting point in understand Endianess and what it means. In some cases you may need to flip your bytes from big Endian to little Endian, this is especially relevant when dealing with network packets and network programming in C. In network programming sometimes fields in a network packet will have fields that are in little Endian when most of your network fields are in big Endian, and to verify these fields you may need to swap Endianess.

For more information regarding Endianness head to the wiki article. There are also built-in C functions that do this including ntohs, ntohl, htons, htonl, however these functions are not always transferable to another architecture (MIPS, ARM) which also differ in Endianness. Therefore, I have written a few functions to byte swap 16-bit, 32-bit and 64-bit unsigned integers, if you are dealing with signed integers simply modify the functions to be signed.

u_int16_t swap_u16(u_int16_t i) {
    u_int8_t c1, c2;
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
 
    return (c1 << 8) + c2;
}
 
u_int32_t swap_u32(u_int32_t i) {
    u_int8_t c1, c2, c3, c4;    
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
    c3 = (i >> 16) & 255;
    c4 = (i >> 24) & 255;
 
    return ((u_int32_t)c1 << 24) + ((u_int32_t)c2 << 16) + ((u_int32_t)c3 << 8) + c4;
}
 
u_int64_t swap_u64(u_int64_t i) {
    u_int8_t c1, c2, c3, c4, c5, c6, c7, c8; 
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
    c3 = (i >> 16) & 255;
    c4 = (i >> 24) & 255;
    c5 = (i >> 32) & 255;
    c6 = (i >> 40) & 255;
    c7 = (i >> 48) & 255;
    c8 = (i >> 56) & 255;
 
    return ((u_int64_t)c1 << 56) + 
            ((u_int64_t)c2 << 48) + 
            ((u_int64_t)c3 << 40) + 
            ((u_int64_t)c4 << 32) + 
            ((u_int64_t)c5 << 24) + 
            ((u_int64_t)c6 << 16) + 
            ((u_int64_t)c7 << 8) + 
            c8;
}

As you can see these functions simply use a logical & with 0xFF (or 255 in decimal) then bit shift the value to its appropriate location depending on the size of the integer. If you apply the function again it will swap the values to their initial position.

Linux Bridge With ‘brctl’ Tutorial

Ever wanted to setup your desktop computer as a network bridge? A bridge differs from a router in that it only looks at layer 2 traffic (MAC addresses) whereas a router inspects at layer 3 of the OSI model (IP addresses). An interesting advantage of running a bridge on your Linux machine is that you can configure it as a transparent bridge with firewall filtering, you could even run something like SNORT, an intrusion detection system for monitoring traffic on the wire. But these are discussions for another day. I would like to cover the functionality of the brctl command in Linux.

The brctl command allows the user to interface with the kernel to actually configure the bridge. The brctl binary is from the bridge-utils package that can be found in the Debian or Ubuntu repositories. To utilize the brctl function you must be running as root or under sudo privileges. The set of commands that brctl provides is as follows:

# brctl 
Usage: brctl [commands]
commands:
        addbr           <bridge>                add bridge
        delbr           <bridge>                delete bridge
        addif           <bridge> <device>       add interface to bridge
        delif           <bridge> <device>       delete interface from bridge
        setageing       <bridge> <time>         set ageing time
        setbridgeprio   <bridge> <prio>         set bridge priority
        setfd           <bridge> <time>         set bridge forward delay
        sethello        <bridge> <time>         set hello time
        setmaxage       <bridge> <time>         set max message age
        setpathcost     <bridge> <port> <cost>  set path cost
        setportprio     <bridge> <port> <prio>  set port priority
        show                                    show a list of bridges
        showmacs        <bridge>                show a list of mac addrs
        showstp         <bridge>                show bridge stp info
        stp             <bridge> {on|off}       turn stp on/off

Display All Bridge Interfaces

To see all current bridge interfaces, execute the command:

# brctl show
bridge name     bridge id               STP enabled     interfaces

As you can see, I currently have no bridge interfaces noted by just column output. So lets add a bridge interface.

Create A Bridge

To create a bridge interface simply run the command:

# brctl addbr br0

Most people create their initial bridge as ‘br0′, you will see that on most OpenWRT or DD-WRT routers. Now if we output our interfaces using ifconfig we can see our interface. I will also bring the interface up.

# ifconfig br0 up
# ifconfig br0
br0       Link encap:Ethernet  HWaddr 26:bc:c7:e4:68:20  
          inet6 addr: fe80::24bc:c7ff:fee4:6820/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:168 (168.0 B)

Our bridge is just like any other interface, it can even have an IP address assigned to it if you wanted (using ifconfig). Lets display our current bridges now:

# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.000000000000       no

You will notice that we do not have STP enabled. STP is the spanning tree protocol that is used to avoid bridging loops. We can enable STP using a brctl command I will outline later. As you can also see here, our bridge has no interfaces in it. Lets add an interface.

Add Interfaces To A Bridge

To add an interface to your bridge is simple:

# brctl addif br0 eth0
# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.001a921ddc9b       no              eth0

Notice how the bridge id changed once I added the interface. The bridge will also take on the MAC address of the first interface added to your bridge.

To make it a true bridge, we should probably have two interfaces within that bridge, executing the same command:

# brctl addif br0 eth1
# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.001a921ddc9b       no              eth0
                                                        eth1

To remove interfaces from the bridge we utilize the delif flag of the brctl command.

Remove Interface From A Bridge

To remove eth1 from our bridge we can enter the command:

# brctl delif br0 eth1
# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.001a921ddc9b       no              eth0

Those are the basic features of creating, adding, removing a bridge and its interfaces. There are a few more commands I would like to outline, including STP.

Turning STP On For Your Bridge

To configure your bridge to participate in a spanning tree, you can enter the command:

# brctl stp br0 on
# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.001a921ddc9b       yes              eth0

Display Learned MAC Address On Your Bridge

For the bridge to properly send traffic out the correct interface it keeps a table of all MAC addresses that it has seen and the interface it arrived on. To display this enter the command:

# brctl showmacs br0
port no mac addr                is local?       ageing timer
  2     00:01:29:d4:bd:59       no               139.95
  1     00:0c:29:2b:3e:77       no                19.50
  1     00:ab:76:ba:d0:22       yes                0.00
  2     00:ce:d6:aa:de:fa       yes                0.00

Notice there is an ageing timer. This is the amount of time (in seconds) since this mac address has been seen on the bridge. A ‘garbage’ collector will check every interval if the age is passed the acceptable limit and remove it from the table.

From the brctl manual page:

brctl setageingtime <brname> <time> sets the ethernet (MAC) address ageing time, in seconds. After seconds of not having seen a frame coming from a certain address, the bridge will time out (delete) that address from the Forwarding DataBase (fdb).

brctl setgcint <brname> <time> sets the garbage collection interval for the bridge to seconds. This means that the bridge will check the forwarding database for timed out entries every seconds.

The areas I have covered include the most used features of the brctl command. There are however, other features as shown by my first output of the brctl command. Refer to the manual page for more information (man brctl).