Feeds:
Posts
Comments

Posts Tagged ‘kernel’

This is the scenario. I have a working ethernet device eth0 with an IP address 192.168.1.x. There is one more special interface in the system, say if1 which is connected to another network. That network is mainly used for transmitting multicast data.

Received multicast data has to be processed by a userland application. Due to some reason I want to project this multicast data as unicast, with a destination MAC as the MAC address of eth0 and destination IP address as the IP address of eth0. So, the application feels that the multicast is coming through eth0 interface. This has to be done for UDP packets to port 10000 only.

This is achieved by changing the driver of if1 interface the following way.

#include <ltnet/checksum.h>
#include <ltlinux/inetdevice.h>


static struct net_device *eth_dev;
static u32 local_ip;
static int init_done = 0;


static void init_fwd_process(struct net_device* net_dev)
{
  /* Here net_dev is the if1 device */
  struct in_ifaddr* ifa;

  if(likely(init_done == 1))
    return;

  /* Find out the net_device of eth0 */
  eth_dev = dev_get_by_name(&init_net, "eth0");

  /* First find out the IP address of eth0 */
  ifa = ((struct in_device*)eth_dev->ip_ptr)->ifa_list;
  local_ip = ifa->ifa_address;

  init_done = 1;
}


/* Initial 2 octets are used by the driver, for this particular driver */
#define ETH_DST_OFF 2
#define ETH_SRC_OFF 16
#define ETH_TYPE_OFF 30
#define IP_HDR_OFF 32
#define IP_TYPE_OFF (IP_HDR_OFF + 9)
#define IP_DSTABSOLUTE_OFF (IP_HDR_OFF + 16)
#define UDP_HDR_OFF (IP_HDR_OFF + 20)
#define UDP_DPORT_OFF (UDP_HDR_OFF + 2)

#define IP_TYPE_UDP 0x11

/* These are relative to IP packet */
#define IP_SRC_OFF 12
#define IP_DST_OFF 16
#define IP_CS_OFF 10
#define UDP_CS_OFF (20 + 6)

/* UDP Port we are interested in, 10000 in this case */
#define UDP_PORT 10000

static int fwd_mcast_packet(struct sk_buff *skb, unsigned char *data,
  int len, struct net_device  *net_dev)
{
  int n;
  u16 c;
  unsigned char* eth_src;
  unsigned char* eth_type;
  unsigned char* ip_hdr;
  unsigned char* ip_type;
  u32 ip_dst;
  u16 udp_dst;
  unsigned char hdr_802_3[14];

  do {
    init_fwd_process(net_dev);

    eth_src = data + ETH_SRC_OFF;
    eth_type = data + ETH_TYPE_OFF;
    ip_hdr = data + IP_HDR_OFF;
    ip_type = data + IP_TYPE_OFF;
    ip_dst = (u32)(ntohl(*((u32 *)(data + IP_DSTABSOLUTE_OFF))));
    udp_dst = (u16)(ntohs(*((u16 *)(data + UDP_DPORT_OFF))));

    /* only IP packet */
    if((*(u16 *)eth_type) != (htons(ETH_P_IP)))
      break;
    /* only UDP */
    if(*ip_type != IP_TYPE_UDP)
      break;
    /* only multicast */
    if(!(ntohl(ip_dst) & 0xe0000000))
      break;
    /* only interesting port */
    if(ntohs(udp_dst) != UDP_PORT)
      break;

    /* We are interested in this packet
    Let other packets go through the usual path */
    /* destination is eth0 */
    memcpy(&hdr_802_3[0], eth_dev->dev_addr, 6);
    /* source is the original one */
    memcpy(&hdr_802_3[6], eth_src, 6);
    /* ethernet type is the original one */
    memcpy(&hdr_802_3[12], eth_type, 2);

    /* for aligning IP header on 16 byte */
    skb_reserve(skb, 2);
    /* copy the generated MAC header */
    memcpy(skb_put(skb, 14), hdr_802_3, 14);

    /* copy from the original IP header onwards */
    n = len - IP_HDR_OFF;
    memcpy(skb_put(skb, n), ip_hdr, n);

    skb->protocol = htons(ETH_P_IP);
    skb->pkt_type = PACKET_HOST;
    skb_reset_mac_header(skb); /* just like eth_type_trans */
    skb_pull(skb, ETH_HLEN);

    /* Destination IP address should be local_ip,
    Here skb->data is the beginning of IP header */
    memcpy(&skb->data[IP_DST_OFF], &local_ip, 4);

    /* regenerate IP checksum */
    skb->data[IP_CS_OFF] = 0;
    skb->data[IP_CS_OFF + 1] = 0;
    c = ip_fast_csum(skb->data, 5);
    memcpy(&skb->data[IP_CS_OFF], &c, 2);

    /* Only clear the UDP checksum */
    skb->data[UDP_CS_OFF] = 0;
    skb->data[UDP_CS_OFF + 1] = 0;

    /* call the main receive function */
    netif_rx(skb);

    return len;
  }while(0);

  return -1;
}

/* Inside the main packet receive function of driver */
  if(fwd_mcast_packet(skb, (unsigned char *)data, length, net_dev) > 0) {
    /* This packet has been already forwarded to IP stack,
    Just do no do anything */
    return;
  }

Advertisements

Read Full Post »

I wanted to setup a Linux kernel development system and the best choice I found was using KVM/Qemu. Both KVM and Qemu use the same virtual hardware specification, but KVM could give near native performance using special processor instructions. Since both of them use the same virtual hardware, the same virtual disk image could be used by either KVM or Qemu. So it is possible to replace ‘kvm’ command everywhere instead of ‘qemu’ at the penalty of performance.

I am using a Debian Unstable amd64 as both host and guest. It is possible to use other distributions, but package names etc. would be different.

First of all install the required packages on host, as root
apt-get install build-essential kernel-package fakeroot \
libncurses5-dev qemu kvm

Compiling the kernel

  1. Get the kernel source code. As of now I am using v3.0-rc5
    Untar/Clone it to say /path/to/linux_src
  2. Kernel Configuration.
    I found the following website for a minimal Linux Kernel Configuration
    http://gruba.blogspot.com/2009/08/qemu-minimal-linux-kernel-config.html
    But this configuration does not support libata, udev and ext4 which are used by current Debian systems. So, I created a configuration myself which also supports es1370 soundcard and 8139too network card, but there is no USB support. It could be downloaded from here. config-3.0.0.txt. Save it as /path/to/linux_src/.config
  3. Build the kernel. I used make-kpkg because of its simplicity, use
    a CONCURRENCY_LEVEL according to the number of processors in the build machine. It is used to set the “-jn” option for make.
    export CONCURRENCY_LEVEL=2
    make-kpkg --initrd --revision=1custorm kernel_image

    Time Taken: With this kernel configuration and concurrency option the build takes around 4 minutes on my AMD Athlon X2 3600 machine.

Setting up a Debian virtual machine

  1. Check the host hardware could support KVM
    egrep 'svm|vmx' /proc/cpuinfo
  2. Create a filesystem image, I am using 3GB raw image format instead of qcow2, because the image could be mounted using a loopback device.
    qemu-img create -f raw debian.img 3G
  3. Get the latest x86_64 Debian netinstall CD image from,
    http://www.debian.org/devel/debian-installer/ Instead, you could install the i386 image, but the custom kernel also should be compiled for x86 architecture.
  4. Boot kvm(or qemu) and install the image. Use something like 512MB of RAM if possible
    kvm -m 512 -boot d -hda /path/to/debian.img \
    -cdrom /path/to/debian-testing-amd64-netinst.iso
  5. To transfer files to the virtual machine, there are two methods, a) using SSH to a running virtual machine, or b) mount the virtual machine image using a loopback device.
    To mount loopback, first find the sector offset
    fdisk -u -l /path/to/debian.img
    offset = sector size * start offset
    Normally that would be 32256 or 1048576
    mount -o loop,offset=1048576 /path/to/debian.img \ /path/to/mountpoint
    To ssh to a running guest, first install ssh server in the guest,
    restart guest with the “hostfwd” qemu option as given in the next section.
    Then do
    ssh root@127.0.0.1 -p 10022
  6. To boot into the newly installed virtual machine using the stock Debian kernel
    kvm -smp 2 -m 512 -boot c -vga std -soundhw es1370 \
    -net nic -net user,hostfwd=tcp::10022-:22 \
    -hda /path/to/debian.img

    Here,
    * SMP: use SMP if host supports it.
    * Networking: Realtek 8139too with “hostfwd” port forwarding so that it is possibe to SSH to the guest.
    * Sound: ES1370 card
  7. To boot using the custom kernel
    kvm -smp 2 -m 512 -boot c -vga std -soundhw es1370 \
    -net nic -net user,hostfwd=tcp::10022-:22 \
    -kernel /path/to/linux_src/arch/x86/boot/bzImage \
    -append root=/dev/sda1 \
    -hda /path/to/debian.img

Read Full Post »

%d bloggers like this: