[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Mapping sk_buff data to userspace.



I have a need to fetch sk_buff-based packets from my Loadable Kernel Module that utilizes a Netfilter hook and to present them to user space in an efficient manner.
 
The best overall approach I have come up with is:
 
Userspace calls my LKM once to initialize things:
 
        LKM creates a 64KB VMA on behalf of the user proces using:
 
      ulVaddr = do_mmap_pgoff(NULL,     /* file        */
                                               0L,      /* Address: 0 = pick VMA for us. */
                                               ulLength,    /* Length       */
                                               PROT_READ | PROT_WRITE, /* Protocol.      */
                                               MAP_SHARED,    /* Flags.   */
                                               0);      /* Offset       */ 
       if (((long)ulVaddr) < 0)
     {
              printk(KERN_ALERT"do_mmap_pgoff error = %lu.\n", ulVaddr);
              return(NULL);
     }
 /* At this point we have a valid VMA added to the calling processes address space. There is a */
 /* new VMA for the returned address. Now, we find it and then muck with it to map it to our   */
 /* kernel allocated memory using kmalloc(). Note, to do so with vmalloc requires additional   */
 /* stuff. See notes.                    */
 
 /* Step two: Fetch the VMA that was just created for the virtual address. */
 /* We save this for later access to the user space. */
 pVma = find_vma(current->mm, ulVaddr);
 
 
Then for each packet netfilter sends to my LKM, we queue it and then require the user process to call a GetNextPacket() call which does:
 
pvKernel = pNextSkb->head;  // Start of IP Header.
iSize     = PAGE_ALIGN(pNextSkb->end - pNextSkb->head);
 
 for (ulVaddr=(unsigned long)pvKernel; ulVaddr<(unsigned long)pvKernel + iSize;ulVaddr+=PAGE_SIZE)
 {
  /* Reserve all pages to make them remapable. remap_page_range() will ignore mapping if this is not done. */
  SetPageReserved(virt_to_page(ulVaddr));
 }
 /* Now, map the kernel memory into the user's VMA. */
 /* Set mm semaphore per memory.c. */
 down(&current->mm->mmap_sem);
 /* We had better hit this with a previous page(s) PG_reserved set or else () frees the mapped pages and later */
 /* when kfree() runs the kernel panics and the system stops(if we are VERY lucky). Meaning, it can do far worse */
 /* things!    */
 if (remap_page_range(pVma->vm_start,
      virt_to_phys(pvKernel),
      iSize,
      PAGE_SHARED))
 {
  up(&current->mm->mmap_sem);
  printk(KERN_ALERT"remap_page_range() error for K=%p, V=%08lX, Size=%d.\n",
    pvKernel,
    pVma->vm_start,
    iSize);
  return(-EAGAIN);
 }
 up(&current->mm->mmap_sem);
   
 
 
This approach works just fine except for one thing, a bunch of "forget_pte: old mapping existed!" messages in syslog. I traced to mm/memory.c#287. This message is printed since I am re-mapping a Page Table Entry that is already mapped. The call then frees the PTE and all appears well. There is a function zap_page_range() called by mem_unmap() which does what I need but it is not exported as a Kernel symbol. When I commented out printk() in forget_pte() in mm/memory.c#287 and rebuilt 2.4.1 kernel, my code seems to work over hundreds of millions of frames with no apparent problems.
 
I would eventually like to release a product based on the technique above but am limited by the printk() in mm/memory.c#287.
 
Any suggestions on how to proceed?
 
Thanks in advance.
 
Stuart