Showing posts with label Linux kernel. Show all posts
Showing posts with label Linux kernel. Show all posts

Saturday, September 30, 2017

How do I limit software from executing kernel attacks? Seccomp

Hi all,
Today I'll be talking about a security facility called Seccomp.

As we may know there are about 400 system-calls,
you can take a glimpse in the linux tree, here: syscall table. according to the path arch/x86/entry/syscalls/syscall_64.tbl, you can easily understand the table's content varies among other architectures.

The seccomp facility is used to restrict specific system-calls which are invoked by a process, so in other words it's security mechanism similar to a sandbox, which is embedded into the kernel.

We can see below a linked list of filters:

struct seccomp_filter {
 refcount_t usage;
 bool log;
 struct seccomp_filter *prev;
 struct bpf_prog *prog;
};

It resides in the task_struct (sched.h), and holds the actual filters:

struct seccomp {
 int mode;
 struct seccomp_filter *filter;
};

if the program executes an unexpected system-calls then the kernel will terminate the process (kill signal will be sent) since it might be a malicious code.

Probably you are saying to yourself that's COOL,
so how do I set the seccomp filters?

compile the kernel with  CONFIG_SECCOMP_FILTER flag set.

prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,..)

Apply filters on system-calls. the filters make use of the well known Berkeley Packet filter, which was implemented years ago for packet filtering such as tcpdump.

So who is using secomp?
1) Chrome browser (in chrome's address bar enter: chrome://sandbox/)
2) OpenSSH
3) systemd
4) Firfox OS
5) Docker (Seccomp security profiles for Docker)
6) LXC

if you would like to know if the software is in seccomp mode,
you can easily read /proc/<pid>/status.
there would be a field called seccomp:
0 means SECCOMP_MODE_DISABLED;
1 means SEC‐COMP_MODE_STRICT
2 means SECCOMP_MODE_FILTER.

If the value is 2, once we created the filter and installed it into the kernel now every system call we make will be tested through the list of filters.

So on each system-call the kernel would return one of the 5 return values:

#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow in case you are using a debugger*/
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */

Taken from: http://elixir.free-electrons.com/linux/latest/source/include/uapi/linux/seccomp.h

So let's assume you have read a great article about a new functionality,
to test this functionality you are given access for downloading a shared object.
On the other hand this website might infect you with a malware, so perhaps you should use the seccomp mechanism, since the shared object might execute a malicious code.

so a solution suggestion would be using seccomp, which would filter the unwanted system from being executed.
I have illustrated a flowchart:




So the code I wrote, looks like this:

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>

#define ArchField offsetof(struct seccomp_data, arch)

#define Allow(syscall) \
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_##syscall, 0, 1), \
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)


void complex_computation(int *);

struct sock_filter filter[] = {
    /* validate arch */
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ArchField),
    BPF_JUMP( BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0),
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),

    /* load syscall */
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),

    /* list of allowed syscalls */
    Allow(exit_group),  /* exits a processs */
    Allow(brk),     /* for malloc(), inside libc */
    Allow(mmap),        /* also for malloc() */
    Allow(munmap),      /* for free(), inside libc */
    Allow(write),       /* called by printf */
    Allow(fstat),       /* called by printf */

    /* and if we don't match above, die */
    BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog filterprog = {
    .len = sizeof(filter)/sizeof(filter[0]),
    .filter = filter
};

int main(int argc, char **argv) {
    char buf[1024];

    /* set up the restricted environment */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        perror("Could not start seccomp:");
        exit(1);
    }
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &filterprog) == -1) {
        perror("Could not start seccomp:");
        exit(1);
    }
 
    complex_computation(buf); /* fuctionality taken form the shared object*/

    printf("Task was completed (no malware was reported)!\n");
} 



for example here below we see the actual strace's dump of the bin file,
which have blocked the actual unlink system-call since the malicious code intention was to damage my file-system:

unlink("/home/gil/my_important_file.txt" <unfinished ...>
+++ killed by SIGSYS +++
Bad system call (core dumped)

Monday, December 7, 2015

Lets perform some magic with Cgroups!

cgroups is mechanisem for monitoring and managing the computer's resources such as:
  • CPU runtime
  • Memory usage
  • Read/write speed for block device
  • Network bandwidth
As we know Linux is a great system for sharing resources around running applications/process, but let's say this time I wouldn't like to share and distribute my resources equally among processes, I want to guarantee more resources to a specific process .
this could be done via control groups aka cgroups.
Lets say we have one process which is highly important over the others, so I would declare a profile which consists of limits resources and then assign this profile to the process.
similar example can be given while speaking of containers/vm machines which we would like to  prioritize the resources between the containers.
this way we limit the impact of VM machines which hogs the CPUs.

I suggest you to read the kernel's documentation on cgroups which
elaborates very well, here is the link:
https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt

In case your cgroups commands are not installed on your system you can install it right away via (I'm using Ubuntu 14.04 for demonstration purposes):

sudo apt-get install cgroup-bin

after reboot, we can see a folder named cgroup:
cgroups is now located at /sys/fs/cgroup.
list the contents in the folder, you should see the following subdirectories:


Those subdirectories present the control group subsystems which can be managed by you.

In this post I'll be giving three demonstrations each one would be demonstrating managment on a different kind of resource. So let the fun begin!

Example #1 - CPU cores usage

lets say I would like to run a specific process on a specific core, I can easily do it on the fly. you can create a control group under the cpuset folder. and then you should echo the number of cpu you want to assign to the process, for example:

echo 0 > ./cpuset.cpus

run your process, and then assign it's pid to the specific core via:

echo <PID> > ./cpuset.tasks

Below you can see a screen-shot of the graph I took while monitoring the 4 cores, there are 4 intervals which I'll explain:

Interval #1 (50-60 sec):
  • Demonstrates the 4 cores which run on normal load.
Interval #2 (30-50 sec):
  • I have invoked my complexCalculation process, you can easily notice a ramp on CPU3.
Interval #3 (10-30 sec):
  • I have applied cgroup rule, so now the process will run only on CPU, we can see the decline on CPU3 and a ramp on CPU1.
Interval #4 (0-10 sec):
  • I have stopped the process complexCalculation so as we would expect there is a graceful degradation on CPU1.
For getting live updates regarding the cores usage, I have used the top command:

top -p<PID>

press 1 to toggle to Separate-Cpu-States screen.

So what actually happened under the hood?
The hard affinity is stored as bitmask in the task's task_struct as cpu_allowed (see sched.h). The bitmask contains one bit per possible processor on the system (In my case I have 4 CPUs). By default, all bits are set and, therefore, a process is potentially runnable on any processor.
After I have echoed to cpuset.tasks the function sched_setaffinity() was invoked,
we can easily see it via ftrace or by setting a break point.

Example #2 - Limiting memory usage

We can easily write a c program which grabs on each loop iteration chunk of memory of about the size of 5MB.
So after about 15 iterations we have consumed 75MB of RAM.I'll be calling this small app "processWastingMemory".
for avoiding this scenario (wasting memory) we can enter a new rule regarding the memory consumption, the rule would reside at the memory controller:

1) memory.limit_in_bytes (physical memory)
2) memory.memsw.limit_in_bytes (swap usage)

lets create a control group of name "myDemo".

20 MB = 20971520Bytes

echo 20971520 > /cgroup/memory/myDemo/memory.limit_in_bytes
echo 20971520 > /cgroup/memory/myDemo/memory.memsw.limit_in_bytes

now lets run the process/task in a given control groups:

cgexec -g memory:myDemo ./processWastingMemory

So here I'm defining the control groups in which the task will be run. the controller is "memory", After executing the command we can easily notice the program got closed (killed) immediately after reaching the memory limit of 20MB for the process.

We can easily check dmesg which shows the following message:

"Memory cgroup out of memory: kill process"

Example #3 - Read/write speed for block device

Will be given next week with interesting graphs... so stay tune!

Meanwhile enjoy exploring new intriguing stuff in the Linux world! :)

Sunday, September 28, 2014

Likely & Unlikely macros in the kernel

Hey Guys!
Today I'll be explaining about two well used macros in the kernel for better branch prediction via gcc.
but first of all lets starts from the basics and fundamentals, here is a short refresh. As we may know each instruction in c is translated to assembler language, as I have mentioned in the past post Get familiar with gcc compiler,  few years ago.

Each c instruction is translated into assembler instructions,
which pushed into the pipeline. I'll elaborate more about the meaning of pipeline:

Pipeline is processor's mechanism for executing the instructions in parallel.
Moreover as more stages there would be (In the picture I drew, n = 4), we would increase the parallelization property.



on each CPU cycle we shift to the right (the next chain) the current instruction.
So via the parallelization we speed up the execution by fetching the next instruction while the other instruction are getting decoded and executed.
if the pipeline is full, on each cycle tick an instruction will get executed.

of course the number of stages depends on the architecture, for example:
On my BeagleBoard xM (ARM Cortex-A8) implements ARM v7 (32-bit) instruction set architecture consist of 5 stages.

So probably you are asking yourself so how come there are no more stages in nowadays cpu's core. Well although the level of parallelism increases there are few drawbacks which I'll be discuss with you now:
1) Core Latency
The actual time (latency) to execute the instruction would get larger as we add more stages cause in more cycles we would fill the pipeline.
2) True Data Dependency
If two consecutive instructions are fetched/loaded into the pipeline such as:
First instruction: INC_REGISTER R1
Second instruction: INC_REGISTER R1
I'll demonstrate here let's say register R1 consists the value 0x4447, after 3 cycles the value would be 0x4448, and then in the next cycle,
the second instruction gets executed and would still hold the value 0x4448 since the initial value was 0x4447 for the second instruction
too. So my conclusion was we/compiler should avoid instructions which have dependencies from the previous cycle.
3) Procedural Dependency (branch instruction)
In case we have a branch and the condition is satisfied the consecutive instructions are already preloaded into the pipeline we will execute those instructions, but eventually after few loop iterations we would fail on the condition branch, so we should get rid of all the instructions which were
loaded into the pipe, and fetch all new sequential instructions which appear along our new flow.
For getting rid of the irrelevant instructions you
the core flushes those instructions. This kind of operation of changing the program counter unpredictably can easily reduce the performance of the processor.


So now after I clarified the third problematic scenario, I'll now talk about
In the kernel there are two well-known macros, which I use quite often:
likely and unlikely, those macros take advantage of the gcc compiler that can optimize the compilation of the code based on that information.
In case you are quite curios you're more then welcome to check the outcome of using those macros, In case you were wondering how the assembly code would be set for getting an optimization for the processor pipeline. write down a code snippet, and afterwards compiled it via gcc with optimization flag on: gcc –O2
For example I wrote down in my vim editor the following short snippet:



Afterwards you can take a look of the disassembled the binary file via:
 objdump -S   .

modify the code to the likely macro from the unlikely instance.
So here is the neat results which I got, the comparison between the two is presented in the meld window (gnu diff program):


Likely macro Vs Unlikely macro

We can easily see the compiler have generated the assembly code (x86) with arranging the code according to the likelihood of the branch,
(I have marked the different assembly lines with colourful rectangle)
So here above we got a simple nice demonstration of avoiding the penalty of flushing the processor pipeline.

I hope you enjoyed today's session, next time I'll be more lifting the hood about the kernel stuff! enjoy!

Wednesday, May 21, 2014

Device tree for my Beagleboard-xM

Hey Guys!

About 3 years ago I purchased my  beagleboard-xM (rev B) over the web.
For those who are less familiar with this toy, it's a SoC equipped with ARM Cortex A8, serial interface, I2C etc. (For more details)
 
So back in those days, after setting it up (partitions, u-boot , image kernel) the operating system which called Angstrom (kernel version 2.6.17) As you may guessed?!

I decided to join few google groups  which were discussing about any subject regarding the new toy in the block :-)

but since then, many things have changed and few days ago I decided to update my kernel to the latest. eventually I decided to move to Ubuntu distribution, and give a try to Ubuntu LTS (3.14.2) .

After settings things up, I tried yo lunch some of my past written code which were written for the old kernel. but apparently the subsystem for muxing the pins has been changed.
Eventually I decided to delve into the subject, and see how come I can't interact with  those gpios.

After some reading I found out, since Linux kernel 3.7 (ARM family) a new method was introduced for describing the hardware, by this I mean a well configured Device Tree should be deliver to the uboot.
A device tree is a data-structure which is responsible for describing the hardware on the system. such as:
  • The number and name of CPUs running on the system
  • Base address and size of the RAM
  • The buses 
  • The peripheral device connections, such as gpios, which i'll be talking about it now.
This data-structure is loaded into the kernel during boot time. before the kernel is loaded. the device tree can be easily configured since it is stored as a readable file with the dts extension., So the developer can modify the tree according to his own needs.
example for a device tree:

Before the Device Tree was introduced to the ARMs, the kernel was actually storing this valuable information inside himself ( either the binary image uImage or zImage), but now two binary files are supplied to the u-boot:
  1. device tree blob
  2. uImage/zImage

Comment: A complete coverage about the device tree can be found at the linux kernel directory:   /Documentation/devicetree


So After configuring the gpios in the device tree, we should compile the this source file, via device tree compiler :


The command:
dtc -O dtb -o omap3-beagle-xm-ab.dtb -b 0 -@ omap3-beagle-xm-ab.dts

In case you got lucky you haven't received any syntax errors, you good to go!
now we should order the kernel to use this updated dtb file, we do that by simple overwriting the corresponding file (I suggest you to backup first the original dtb file) in the directory:

/boot/uboot/dtbs/

now you should reboot and check if the system recognized your attached device.

That's all for now, I hope you enjoyed today's section. see ya on the next post!


P.S

I have explained in short the configuration settings, since in my opinion it's less interesting, but for giving you a good start you should read thoroughly the pages taken from:
  • The "Technical Reference Manual" of the Texas Instrument's processor (Pages 2,444-2,453).

Tuesday, May 20, 2014

Removing Linux kernel images in a snap of finger!

Recently I have been writing and modifying some code in the Linux kernel tree,
I have been using the  configuration file: .config, which was generated via localmodconfig.
probably you're asking yourself what is localmodconfig?

I'll elaborate about it, it's a tool which generates the .config file.
The generated .config file is quite slim compare to the default distribution kernel's configuration,
since many unnecessary kernel modules are not getting compiled during compilation phase (make modules_install),
so it's compilation state is much shorter in time.
During this installation routine, few basic steps are taken:


  1. Copies the final image to the folder /boot .  You can easily recognize the file since the name consists the prefix "vm-linuz-" following with the Kernel-version name.
  2. Copies the compiled kernel modules to /lib/modules and other necessary stuff while working with modules (module dependency trees, etc.)
  3. Modifies the /boot/grub/grub.cfg, so now your fresh linux kernel image entry was added to the GRUB menu. Check it while rebooting your system.  

Some times it occurred to me, that I need to get rid of those old kernel images which are installed on my system.
Doing it manually annoys me, So I decided to write a script which does the work for me.


take a look, see below:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
#!/bin/bash

option=1
names=""
cd /boot
clear
echo "The kernels which are installed on your system are:"
for file in ./vmlinuz-*
do
 temp_kernel_name=${file:2:`expr length $file`-2}
 temp_kernel_name=`echo ${temp_kernel_name} | cut -d "-" -f 2-7`
 echo "[${option}] " ${temp_kernel_name}
 names=${names}" "${temp_kernel_name}
 let option=option+1
done
echo "[${option}] Exit"

exit_code=option

echo "Pick the linux kernel you would like to remove from your system?"
read user_pick

if [ ${user_pick}==${exit_code} ]; then
 echo "Exiting... Bye!" 
 exit -1
fi

kernel_name_to_remove=`echo ${names} | cut -d " "  -f ${user_pick}` # f option in cut commad holds the number of field after the space delimeter
kernel_version=`echo ${kernel_name_to_remove} | cut -d "-" -f 1-2`

if [ `uname -r` == ${kernel_name_to_remove} ]; then
 echo "attention: Can't remove kernel, the current kernel is running on your system!"
 echo "please check your request, exiting the script..."
 exit -1
fi

echo "Are you sure you would like to remove kernel: " ${kernel_name_to_remove} "? [y/n]"
read ans

if [ "n" == ${ans} ]; then
 echo "please re-think about it, exiting the script..."
 exit -1;
fi

echo "Starting to remove kernel: " ${kernel_name_to_remove} 
echo "Kernel version: " ${kernel_version}

mkdir -p /boot/removed_kernel_images

# Step 1
for file in ./*${kernel_version}*
do
 temp_file_name=${file:2:`expr length $file`-2}
 echo "removing file: " ${temp_file_name} 
 mv ${file} /boot/removed_kernel_images
done

echo "Finished step 1!"

#Step 2
mkdir -p /lib/modules/removed_kernel_modules
mv /lib/modules/${kernel_name_to_remove} to /lib/modules/removed_kernel_modules
echo "Finished step 2!"

#Step 3 modifying: /boot/grub/grub.cfg
mkdir -p /boot/grub/grub_conf_files_removed

if [[ ! -f /boot/grub/grub_conf_files_removed/grub`date +"_%m_%d_%Y_%H_%M_%S"`.cfg ]]; then
 cp /boot/grub/grub.cfg /boot/grub/grub_conf_files_removed/grub`date +"_%m_%d_%Y_%H_%M_%S"`.cfg #backing up the file
fi

cd /boot/grub
res=`grep -n ${kernel_name_to_remove} /boot/grub/grub.cfg | cut -d : -f 1`
echo "res = " ${res}
echo ""
echo ""
echo ""
echo ""

number_of_matches=`echo ${res} | wc -w`
#echo "number_of_matches = " ${number_of_matches}
last_line=`echo ${res} | cut -d " " -f ${number_of_matches}`
let last_line=last_line+1 #removing the last bracket too
start_line=`echo ${res} | cut -d " " -f 1`

echo "start_line = " ${start_line}
echo "last_line = " ${last_line}

numbers=`seq ${start_line} 1 ${last_line}`

#echo "numbers = " ${numbers}

for row_number in ${numbers}
do
 sed -i ${rownumber}" d" /boot/grub/grub.cfg
done

echo "Finished step 3!"

#Step 4:
update-grub
echo "Finished step 4!"

cd -

echo "Finished, Bye :-)"

Fill free to grab the script in my GitHub repository: 

https://github.com/codingforpleasure

So that's all for today, next time I'll be talking about exciting concept of device trees, See you till then!

Saturday, October 27, 2012

net_device, who are you?

Hi all,
Today I'll be talking about a basic well known structure in the Linux
networking kernel. It's the net_device structure.
The net_device holds large amount of information regarding the device.

do not forget a net_device can be virtual device too,
It doesn't necessarily has to be a physical device, such as NIC.
Examples for virtual devices are:
such as a bridge interface which is a virtual representation of a bridge.
and Tunnel interfaces - The implementation of IP-over-IP tunnelling (IPIP) and the Generalized Routing Encapsulation (GRE) Protocol is based on the creation of the virtual device.


So now I'll try simplify this well-known structure, and it's usage.
Oh by the way my reference source code which I'll be talking about is Linux kernel 3.5.4 .
Some of the fields have been changed, but it's not that difficult to understand.

So Let's start, I'll try with the more simple fields
The fields of the net_device structure can be classified into the categories:

a. Configuration
Configuration fields are:



(1) char name[IFNAMESIZ]
Name of the device. for example: wifi0, the name is wifi

(2) int ifindex;
Interface index is a unique device identifier, for example eth0
the ifindex value is zero.
     
(3) unsigned char if_port ;
The type of ports being used for this interface (10BASE2, 10BASE).
if_port stores the media type of the network adapter currently used.
For Ethernet, we distinguish between BNC, Twisted Pair (TP), and AUI.


(4) unsigned short flags;

see file if.h under /include/linux
Standard interface flags (netdevice->flags)

for example: IFF_PROMISC - receive all packets
IFF_UP - interface is up
IFF_LOOPBACK - is a loopback net
IFF_ALLMULTI - receive all multicast packets
IFF_RUNNING -


(5) unsigned int mtu;
MTU stands for Maximum Transmission Unit and represents then maximum size of frames that the device can handle.

       of-course you can use ioctl and set/get MTU and make use of SIOCGIFMTU, 
       SIOCSIFMTU.

(6) unsigned short type;
interface hardware type, the category of devices to which it belongs (Ethernet, Frame Relay, etc.)  /include/linux/if_arp.h contains the complete list of possible types.
 
     
(7) unsigned char addr_len;
dev_addr is the device link layer address (No IP address). The value of addr_len depends on the type of device. ethernet addresses are six octets long.

(8) unsigned short hard_header_len;
hardware header length, for example if we are talking about Ethernet's header it features source and destination MAC addresses which have 6 octets each, the EtherType protocol identifier field and optional IEEE 802.1Q tag.


(9) struct net_device_stats stats;
Holds statistics regarding transmitting and receiving, number of packets, number of bytes, collisions,crc_errors, unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors, tx_errors etc.
If you would like to see those stats, you can easily retrieve them through
the sysfs  mounted on:
/sys/class/net/<device_name>/statistics


(10) unsigned int promiscuity;
Probably you are asking yourself, haven't we mentioned promiscuous mode before?and how come we need an unsigned int for that, actually you are right the flags field holds the the current state of the device, so for checking out we could AND with the IFF_PROMISC mask.
but let's think things through the net_dev serves the system (OS), and over the OS runs many processes which use those net devices.
The netdevice is shared between processes so if lets say I use Wireshark and Tcpdumpp for sniffing traffic the two would use the promiscuous functionality, so it's actually a reference counter.
when the promiscuity reaches zero, we turnoff the bit, via the
IFF_PROMISC  mask.



(11) unsigned long state;
Device status, there are 3 states for each net_device.

a .  __LINK_STATE_START
     Interface state is either up  or down, is checked via function
      netif_running().

b.  __LINK_STATE_PRESENT
    D
evice is either present or has been removed from system, is checked
    via function
  netif_device_present().


c. __LINK_STATE_NOCARRIER
    Carrier is present on device, is checked via function netif_carrier_ok().


(12) unsigned short padded;
How much padding added by alloc_netdev_mqs()

(13) unsigned int num_rx_queues;
Number of RX queues allocated at register_netdev() time

(14) unsigned int real_num_rx_queues;
Number of RX queues currently active in device

(15)

unsigned int num_tx_queues;

Number of TX queues allocated at alloc_netdev_mq() time

(16) unsigned int real_num_tx_queues;
Number of TX queues currently active in device
 

  • Next to each net_device structure resides a  priv structure which is set by the driver, it's a private data structure storing information about the interface. The private data consists of statistics such as the number of packets transmitted and received and the number of errors encountered. The priv structure size is not necessarily the same for each net_device, since we are talking about a complete distinct net device which belong to another vendor. For getting the network device private data , we should use the function:
    static inline void *netdev_priv(const struct net_device *dev)


(17) unsigned short priv_flags;
Flags can be changed through the dev_change_flags function.
 

b. List management

  • So Now I'll be talking about  how those net devices get stored, of course We would like retrieve net_device’s data  fast and quick as possible . So let's see how it is done:

(18) struct list_head dev_list;
Each net_device holds field named dev_list, which is two pointers one to the next and another to the previous net_device.
next and previous is in the list_head struct.


(19) struct hlist_node index_hlist;
device index hash chain

 
(20) struct hlist_node name_hlist;
device name hash chain


For inserting a net_device into our "database" we use:
static int list_netdevice(struct net_device *dev);


according to (19) and (20)  we can understand a new net_device gets stored via 2 hash arrays. one for the name of the net_device and the second for the if_index's net_device.

static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex)
{
    return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
}


static inline struct hlist_head *dev_name_hash(struct net *net, const char *name)
{
    unsigned int hash = full_name_hash(name, strnlen(name, IFNAMSIZ));

    return &net->dev_name_head[hash_32(hash, NETDEV_HASHBITS)];
}




For example I have illustrated a sketch, for storing 3 net_device:
let's say an eth1 net_device was added, it was the first net_device to be added to the system.

Afterwards a net_device called wifi2 was added, so a new entry gets initialised in the hash array of names (wifi entry) .

Now a new device was added called eth2, so now it becomes the head of the eth list.

So we should get the following image:





That’s it for now,
I hope you learned few neat things from today’s talk , c u on the next blog post!

Friday, May 11, 2012

ebtables for the rescue! ;-)

Before delving into ebtable tool, I would like to give a short introduction.
ebtables is part of the netfilter framework. it's a mechanism for inspecting packets in L2 (Data-link Layer).
The inspection is done by entering rules into the tables,
Those tables are divided according to theirs functionality, which holds different sets of rules.
The ebtables supports 3 built-in tables: FILTER, NAT and  BROUTE.

comment: In case you haven't specified the table's name in the rule , the rule would be added by default to the FILTER table.


Chain is set of ordered list of rules that can match a L2 frame.
In case a rule matches a frame it would take an appropriate action defined by the user, the action is usually called TARGET.

A Target can be one of the following values: DROP, ACCEPT, CONTINUE, RETURN or user's extension for jumping to user defined chain. The meaning of each value is simple :


DROP - The frame should be dropped in the current chain (expect for the broute table, which means the packet should be routed)

ACCEPT - The frame should be passed away (expect for the broute table, which means the packet should be bridged)

CONTINUE - The following rule should be checked on the current frame
RETURN - Exiting from the current chain and returning to the calling chain, and checking the next rule from the previous chain.


Here I'll be talking about each table, and it's properties.


So probably you are asking yourself what are the differences between those tables? and what are their usage?



  • The NAT table is used mostly for MAC-NAT, which means for modifying the mac addresses , either the source mac or destination mac or both. It consists three built-in chains, I have illustrated in the sketch below those chains: PREROUTING, POSTROUTING, OUTPUT.




Comment: A more precise name would be PREFORWARDING instead of  PREROUTING since we are talking about L2 which no routing takes part. The reason for this convention was since the ebtables is the ancestor of iptables.
                  



  • The FILTER table consists three built-in chains, I have illustrated in the sketch below, those chains are: FORWARD, INPUT, OUTPUT.








  • The broute table is for deciding which traffic to route between two interfaces, and which traffic to bridge between two interfaces so it has a single chain. 


In case according to user's rule the traffic should be route between two interfaces, it means the bridge code won't touch the frame and it would be sent up to the higher network layers.


The hooks are in specific places in the network code on which software can attach itself to process the packets/frames passing that place. For example, the kernel module responsible for the ebtables FORWARD chain is attached onto the bridge FORWARD hook. This is done when the module is loaded into the kernel or at bootup. 

Those seven hooks defined in the Linux bridging code, can be seen in the sketch #3:



As we can see
the Broute chain is traversed very early.


The ebtable tool also provides the ability to mark frames via an unsigned numbers, those numbers is embedded into the frame.

Those marks are meaningful and later can be easily observed, via those marks the correct actions can be taken later on.


In case you are curious where those marks are stored, you can easily find it in the sk_buff struct (\include\linux\skbuff.h), there is a field called mark which saves the marks for each frame in the skb.



In this post I haven't referred to the commands syntax, since it is written clear on the man pages of ebtables, I suggest you to read those pages :-)

Oh I stumble upon Jan Engelhardt's illustration which demonstrates very well the frame's flow in the network stack, it can be seen here:

Netfilter packet flow

As we can see in the image there are much more chains since iptables' chains are
mentioned too. In the future posts to come, I'll be talking about iptables.

 

Saturday, July 2, 2011

Under the hood of pthread


Hi all,

Today I’ll be talking about the implementation of user space threads in Linux .
Nowadays almost every program on your pc holds multiple threads. Threads as we know it, share open files, resources and memory address space.

The interesting fact about Linux kernel is, it sees threads as standard processes, so it doesn’t provide any special treatment for dealing with them. From the kernel point of view a thread is just a process which shares few resources with other processes. This approach makes things much easier and elegant than Windows approach for distinguishing between a process and thread.

So now let’s see what happens when we create a thread using the pthread_create() method:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

void * thread1()
{
  while(1)
  {
      printf("Hello!!\n");
  }
}

void * thread2()
{
   while(1)
   {
      printf("How are you?\n");
   }
}

int main()
{
 int status;
 pthread_t tid1,tid2;
 pthread_create(&amp;tid1,NULL,thread1,NULL);
 pthread_create(&amp;tid2,NULL,thread2,NULL);
 return 0;
}

The POSIX library methods creates threads the same as normal tasks, except it calls the system call clone().
Yes you heard right, the system call clone() has been invoked. You can check and see
for youself With the strace tool, which I wrote here a month ago. I’ll show you the output of the trace file showing that there was indeed a system call named clone().

... clone(child_stack=0xb7768494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND| CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS| CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb7768bd8, {entry_number:6, base_addr:0xb7768b70, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb7768bd8) = 5877 ...

We can see from the straceout file which I generated, that to the clone() system call were passed flags.
So what do you think those flags stand for?
Those flags say which resources are being shared, so they are actually responsible for the difference between a simple process to a thread in Linux. The flags help specifying in detail which resources the parent and child will share.
I’ll try to explain few of the flags in the example above, for more information you can check for yourself in  <linux/sched.h>:

Flag Name
Short explanation
CLONE_VM
Parent and child share address space
CLONE_FS
Parent and child share filesystem information
CLONE_FILES
Parent and child share open files
CLONE_SIGHAND
Parent and child share signal handlers and blocked signals
CLONE_THREAD
Parent and child are in the same thread group


Pretty neat, right? :-)
yap interesting stuff…

unfortunately I gotta go, so that’s it for today, next time I’ll be talking about Kernel threads!!

About