Powered By Blogger

Monday, May 8, 2017

How tcpdump works????


packets are tapped at very end of software network stack (e.g. in Linux).


If you do code digging in tcpdump, libpcap and linux kernel 3.12:

Both Wireshark and tcpdump uses libpcap, for example,
    if (pcap_setfilter(pd, &fcode) < 0)
which in turn install a packet filter via setfilter_op and activate_op. There are lot of implementations of these operations, and I think that on recent Linux PF_PACKET will be used with pcap_activate_linuxlibpcap-1.5.3-2/pcap-linux.c#L1287:
/*
 * Current Linux kernels use the protocol family PF_PACKET to
 * allow direct access to all packets on the network while
 * older kernels had a special socket type SOCK_PACKET to
 * implement this feature.
 * While this old implementation is kind of obsolete we need
 * to be compatible with older kernels for a while so we are
 * trying both methods with the newer method preferred.
 */
status = activate_new(handle);

    ...
    activate_new(pcap_t *handle)
     ...
    /*
 * Open a socket with protocol family packet. If the
 * "any" device was specified, we open a SOCK_DGRAM
 * socket for the cooked interface, otherwise we first
 * try a SOCK_RAW socket for the raw interface.
 */
sock_fd = is_any_device ?
    socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) :
    socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
PF_PACKET is implemented in kernel, in the file net/packet/af_packet.c. Initialization of PF_SOCKET is done in packet_do_bind with register_prot_hook(sk) function (if the device is in UP state), which calls dev_add_pack from net/core/dev.c to register the hook:
 370 /**
 371 *      dev_add_pack - add packet handler
 372 *      @pt: packet type declaration
 373 *
 374 *      Add a protocol handler to the networking stack. The passed &packet_type
 375 *      is linked into kernel lists and may not be freed until it has been
 376 *      removed from the kernel lists.
 377 *
 378 *      This call does not sleep therefore it can not
 379 *      guarantee all CPU's that are in middle of receiving packets
 380 *      will see the new packet type (until the next received packet).
 381 */
 382
 383 void dev_add_pack(struct packet_type *pt)
 384 {
 385        struct list_head *head = ptype_head(pt);
 386
 387        spin_lock(&ptype_lock);
 388        list_add_rcu(&pt->list, head);
 389        spin_unlock(&ptype_lock);
 390 }
I think, pf_packet handler - the tpacket_rcv(...) function - will be registered in ptype_all.
Hooks, registered in ptype_all are called for outgoing packets from dev_queue_xmit_nit ("Support routine. Sends outgoing frames to any network taps currently in use.") with list_for_each_entry_rcu(ptype, &ptype_all, list) { ... deliver_skb ...} .. func, deliver_skb calls the func which is tpacket_rcv for libpcap.
dev_queue_xmit_nit is called from dev_hard_start_xmit (Line 2539 in net/core/dev.c) which is AFAIK the last stage (for outgoing packets) of device-independent packet handling in Linux networking stack.
The same history is for incoming packets, ptype_all-registered hooks are called from __netif_receive_skb_core with same list_for_each_entry_rcu(ptype, &ptype_all, list) {.. deliver_skb..}. __netif_receive_skb_core is called from __netif_receive_skb in the very beginning of handling incoming packets
Linux foundation has good description of networking stack (http://www.linuxfoundation.org/collaborate/workgroups/networking/kernel_flow), you can see dev_hard_start_xmit on the image http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png (warning, it is huge) at left side just under the legend. And netif_receive_skb is inside the rightmost lower square ("net/core/dev.c"), which is fed from IRQ, then NAPI poll or netif_rx and the only exit from here is netif_receive_skb.
The picture even shows one of two pf_packet hooks - the leftmost square under legend ("net/packet/af_packet.c") - for outgoing packets.
What is your tool? How it connects to the networking stack? If you can locate the tool in the Network_data_flow picture, you will get the answer. For example, Netfilter is hooked (NF_HOOK) only in ip_rcv (incoming) ip_output (local outgoing) and ip_forward (outgoing from routing) - just after netif_receive_skb and just before dev_queue_xmit.


 http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png  (click here for bigger image)

Friday, April 14, 2017

spin_lock_irq vs spin_lock_irqsave

spin_lock_irq vs spin_lock_irqsave

spin_lock_irqsave is basically used to save the interrupt state before taking the spin lock, this is because spin lock disables the interrupt, when the lock is taken in interrupt context, and re-enables it when while unlocking. The interrupt state is saved so that it should reinstate the interrupts again.

Example:

  1. Lets say interrupt x was disabled before spin lock was acquired
  2. spin_lock_irq will disable the interrupt x and take the the lock
  3. spin_unlock_irq will enable the interrupt x.


So in the 3rd step above after releasing the lock we will have interrupt x enabled which was earlier disabled before the lock was acquired.

So only when you are sure that interrupts are not disabled only then you should use spin_lock_irq otherwise you should always use spin_lock_irqsave.

Thursday, March 16, 2017

User space memory access from the Linux kernel

Linux memory

In Linux, user memory and kernel memory are independent and implemented in separate address spaces. The address spaces are virtualized, meaning that the addresses are abstracted from physical memory (through a process detailed shortly). Because the address spaces are virtualized, many can exist. In fact, the kernel itself resides in one address space, and each process resides in its own address space. These address spaces consist of virtual memory addresses, permitting many processes with independent address spaces to refer to a considerably smaller physical address space (the physical memory in the machine). Not only is this convenient, but it's also secure, because each address space is independent and isolated and therefore secure.
But there's a cost associated with this security. Because each process (and the kernel) can have identical addresses that refer to different regions of physical memory, it's not immediately possible to share memory. Luckily, a few solutions exist. User processes can share memory through the Portable Operating System Interface for UNIX® (POSIX) shared memory mechanism (shmem), with the caveat that each process may have a different virtual address that refers to the same region of physical memory.
The mapping of virtual memory to physical memory occurs through page tables, which are implemented in the underlying hardware (see Figure 1). The hardware itself provides the mapping, but the kernel manages the tables and their configuration. Note that as shown here, a process may have a large address space, but it is sparse, meaning that small regions (pages) of the address space refer to physical memory through the page tables. This permits a process to have a massive address space that is defined only for the pages that are needed at any given time.
Page tables provide the mapping from virtual addresses to physical addresses
Figure 1. Page tables provide the mapping from virtual addresses to physical addresses

Having the ability to sparsely define memory for processes means that the underlying physical memory can be overcommitted. Through a process called paging (though in Linux, it's typically called swap), less-used pages are dynamically moved to a slower storage device (such as a disk) to accommodate other pages that need to be accessed (see Figure 2). This behavior allows the physical memory within the computer to serve pages that an application more readily needs while migrating less-needed pages to disk for improved utilization of the physical memory. Note that some pages can refer to files, in which case, the data can be flushed if dirty (through the page cache) or, if the page is clean, simply discarded.

Swap permits better use of the physical memory space by migrating          less-used pages to slower and less expensive storage
Figure 2. Swap permits better use of the physical memory space by migrating less-used pages to slower and less expensive storage

The process by which a page is selected to swap to storage is called a page-replacement algorithm and can be implemented using a number of algorithms (such as least recently used). This process can occur when a memory location is requested whose page is not in memory (no mapping is present in the memory management unit [MMU]). This event is called a page fault and is detected by hardware (the MMU), and then managed by firmware after a page fault interrupt occurs. See Figure 3 for a illustration of this stack.
Linux provides an interesting implementation of swap that offers some useful characteristics. The Linux swap system permits the creation and use of multiple swap partitions and priorities, which permits a hierarchy of swap over storage devices that provide different performance characteristics (for example, a first-level swap on a solid-state disk [SSD] and a larger, second-level swap space on a slower storage device). Attaching a higher priority to the SSD swap allows it to be used until exhausted; only then would pages be written to the lower-priority (slower) swap partition.
Figure 3. Address spaces and elements of virtual-to-physical address mapping
Virtual-to-physical address mapping

Not all pages are candidates for swapping. Consider kernel code that responds to interrupts or code that manages the page tables and swap logic. These are obvious pages that should never be swapped out and are therefore pinned, or permanently resident in memory. Although kernel pages are not candidates for swapping, user space pages are, but they can be pinned through the mlock (or mlockall) function to lock the page down. This is the purpose behind the user space memory access functions. If the kernel assumed that an address that a user passed was valid and accessible, a kernel panic would eventually occur (for example, because the user page was swapped out, resulting in a page fault in the kernel). This application programming interface (API) ensures that those corner cases are handled properly.

Kernel APIs

Now, let's explore the kernel APIs for manipulating user memory. Note that this covers the kernel and the user space interface, but the next section explores some of the other memory APIs. The user space memory access functions to be explored are listed in Table 1.
Table 1. The User Space Memory Access API
As you would expect, the implementation of these functions can be architecture dependent. For x86 architectures, you can find these functions and symbols defined in ./linux/arch/x86/include/asm/uaccess.h, with source in ./linux/arch/x86/lib/usercopy_32.c and usercopy_64.c.
The role of the data-movement functions is shown in Figure 4 as it relates to the types involved for copy (simple vs. aggregate).
Figure 4. Data movement using the User Space Memory Access API
Data movement using the User Space Memory Access API

The access_ok function

You use the access_ok function to check the validity of the pointer in user space that you intend to access. The caller provides the pointer, which refers to the start of the data block, the size of the block, and the type of access (whether the area is intended to be read or written). The function prototype is defined as:
1
access_ok( type, addr, size );
The type argument can be specified as VERIFY_READ or VERIFY_WRITE. The VERIFY_WRITE symbolic also identifies whether the memory region is readable as well as writable. The function returns non-zero if the region is likely accessible (though access may still result in -EFAULT). This function simply checks that the address is likely in user space, not in the kernel.

The get_user function

To read a simple variable from user space, you use the get_user function. This function is used for simple types such as charand int, but larger data types like structures must use the copy_from_user function, instead. The prototype accepts a variable (to store the data) and an address in user space for the Read operation:
1
get_user( x, ptr );
The get_user function maps to one of two internal functions. Internally, this function determines the size of the variable being accessed (based on the variable provided to store the result) and forms an internal call through __get_user_x. This function returns zero on success. In general, the get_user and put_user functions are faster than their block copy counterparts and should be used if small types are moved.

The put_user function

You use the put_user function to write a simple variable from the kernel into user space. Like get_user, it accepts a variable (which contains the value to write) and a user space address as the write target:
1
put_user( x, ptr );
Like get_user, the put_user function is internally mapped over the put_user_x function and returns 0 on success or -EFAULT on error.

The clear_user function

The clear_user function is used to zero a block of memory in user space. This function takes a pointer in user space and a size to zero, which is defined in bytes:
1
clear_user( ptr, n );
Internally, the clear_user function first checks to see whether the user space pointer is writable (via access_ok), and then invokes an internal function (coded in inline assembly) to perform the Clear operation. This function is optimized as a very tight loop using string instructions with the repeat prefix. It returns the number of bytes that were not clearable or zero if the operation was successful.

The copy_to_user function

The copy_to_user function copies a block of data from the kernel into user space. This function accepts a pointer to a user space buffer, a pointer to a kernel buffer, and a length defined in bytes. The function returns zero on success or non-zero to indicate the number of bytes that weren't transferred.
1
copy_to_user( to, from, n );
After checking the ability to write to the user buffer (through access_ok), the internal function __copy_to_user is invoked, which in turn calls __copy_from_user_inatomic (in ./linux/arch/x86/include/asm/uaccess_XX.h, where XX is 32 or 64 depending on architecture). This function (after determining whether to perform 1, 2 or 4 byte copies) finally calls __copy_to_user_ll, which is where the real work is done. In broken hardware (prior to the i486, where the WP bit was not honored from supervisory mode), the page tables could change at any time, requiring a the desired pages to be pinned into memory so that they could not be swapped out while being addressed. Post i486, the process is nothing more than an optimized copy.

The copy_from_user function

The copy_from_user function copies a block of data from user space into a kernel buffer. it accepts a destination buffer (in kernel space), a source buffer (from user space), and a length defined in bytes. As with copy_to_user, the function returns zero on success and non-zero to indicate a failure to copy some number of bytes.
1
copy_from_user( to, from, n );
The function begins by checking the ability to read from the source buffer in user space (via access_ok), and then calls__copy_from_user and eventually __copy_from_user_ll. From here, depending on architecture, a call is made to copy from the user buffer to a kernel buffer with zeroing (of unavailable bytes). The optimized assembly functions include the ability to manage.

The strnlen_user function

The strnlen_user function is used just like strnlen but assumes that the buffer is available in user space. The strnlen_userfunction takes two arguments: the user space buffer address and the maximum length to check.
1
strnlen_user( src, n );
The strnlen_user function first checks to see that the user buffer is readable through a call to access_ok. If accessible, the strlen function is called, and the max length argument is ignored.

The strncpy_from_user function

The strncpy_from_user function copies a string from user space into a kernel buffer, given a user space source address and max length.
1
strncpy_from_user( dest, src, n );
As a copy from user space, this function first checks that the buffer is readable via access_ok. Similar to copy_from_user, this function is implemented as an optimized assembly function (within ./linux/arch/x86/lib/usercopy_XX.c).

Other schemes for memory mapping

The previous section explored methods for moving data between the kernel and user space (with the kernel initiating the operation). Linux provides a number of other methods that you can use for data movement, both in the kernel and in user space. Although these methods may not necessarily provide identical functionality as described by the user space memory access functions, they are similar in their ability to map memory between address spaces.
In user space, note that because user processes appear in separate address spaces, moving data between them must occur through some form of inter-process communication mechanism. Linux provides a variety of schemes (such as message queues), but most notable is POSIX shared memory (shmem). This mechanism allows a process to create an area of memory, and then share that region with one or more processes. Note that each process can map the shared memory region to different addresses in their respective address spaces. Therefore, relative offset addressing is required.

The mmap function allows a user space application to create a mapping in the virtual address space. This functionality is common in certain classes of device drivers (for performance), allowing physical device memory to be mapped into the virtual address space of the process. Within a driver, the mmap function is implemented through the remap_pfn_range kernel function, which provides a linear mapping of device memory into a user's address space.