2012-10-25 4 views
5

Voglio echo un pacchetto nello spazio del kernel. Eseguo un server echo su questa macchina con la porta 6000. Ora un client viene eseguito su un'altra macchina che invia i dati al server echo. Ora, quello che voglio fare è fare eco al pacchetto dallo spazio del kernel. Non voglio disturbare il server con il pacchetto, e verrà echeggiato silenziosamente dallo spazio del kernel. Sto presentando i miei codici di seguito:Come echo un pacchetto nello spazio del kernel usando gli hook di netfilter?

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/skbuff.h> 
#include <linux/netfilter.h> 
#include <linux/netdevice.h> 
#include <linux/ip.h> 
#include <linux/udp.h> 
#include <linux/mm.h> 
#include <linux/err.h> 
#include <linux/crypto.h> 
#include <linux/init.h> 
#include <linux/crypto.h> 
#include <linux/scatterlist.h> 
#include <net/ip.h> 
#include <net/udp.h> 
#include <net/route.h> 
#include <net/checksum.h> 
#include <linux/netfilter_ipv4.h> 

#define IP_HDR_LEN 20 
#define UDP_HDR_LEN 8 
#define TOT_HDR_LEN 28 

static unsigned int pkt_echo_begin(unsigned int hooknum, 
         struct sk_buff *skb, 
         const struct net_device *in, 
         const struct net_device *out, 
         int (*okfn)(struct sk_buff *)); 

static struct nf_hook_ops pkt_echo_ops __read_mostly = { 
    .pf = NFPROTO_IPV4, 
    .priority = 1, 
    .hooknum = NF_INET_PRE_ROUTING, 
    .hook = pkt_echo_begin, 
}; 

static int __init pkt_echo_init(void) 
{ 
    printk(KERN_ALERT "\npkt_echo module started ..."); 
    return nf_register_hook(&pkt_echo_ops); 
} 

static void __exit pkt_echo_exit(void) 
{ 
    nf_unregister_hook(&pkt_echo_ops); 
    printk(KERN_ALERT "pkt_echo module stopped ..."); 
} 

static unsigned int pkt_echo_begin (unsigned int hooknum, 
         struct sk_buff *skb, 
         const struct net_device *in, 
         const struct net_device *out, 
         int (*okfn)(struct sk_buff *)) 
{ 
    struct iphdr *iph; 
    struct udphdr *udph; 
    unsigned char *data; 

    unsigned char *temp; 

    __u16 dst_port, src_port; 
    int data_len; 



    if (skb) { 
     iph = (struct iphdr *) skb_header_pointer (skb, 0, 0, NULL); 

     if (iph && iph->protocol &&(iph->protocol == IPPROTO_UDP)) { 
      udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL); 
      src_port = ntohs (udph->source); 
      dst_port = ntohs (udph->dest); 

      if (dst_port == 6000) { 
       printk(KERN_ALERT "\nUDP packet goes in"); 

       data = (unsigned char *) skb_header_pointer (skb, TOT_HDR_LEN, 0, NULL); 
       data_len = skb->len - TOT_HDR_LEN; 

       struct sk_buff *newskb; 
       struct iphdr *newiph; 
       struct udphdr *newudph; 
       unsigned char *newdata; 
       unsigned int newdata_len; 

       newskb = skb_copy(skb, GFP_ATOMIC); 
       newiph = (struct iphdr *) skb_header_pointer (newskb, 0, 0, NULL); 
       newudph = (struct udphdr *) skb_header_pointer (newskb, IP_HDR_LEN, 0, NULL); 
       newdata = (unsigned char *) skb_header_pointer (newskb, TOT_HDR_LEN, 0, NULL); 

       newiph->saddr = iph->daddr; 
       newiph->daddr = iph->saddr; 

       newudph->source = udph->dest; 
       newudph->dest = udph->source; 

       struct sk_buff *tempskb; 

       tempskb = skb_copy(skb, GFP_ATOMIC); 

       *tempskb = *skb; 
       *skb = *newskb; 
       *newskb = *tempskb; 

       kfree_skb(newskb); 

      } 
     } 
    } 
    return NF_ACCEPT; 
} 


module_init(pkt_echo_init); 
module_exit(pkt_echo_exit); 

MODULE_AUTHOR("Rifat Rahman Ovi: <[email protected]>"); 
MODULE_DESCRIPTION("Echoing a packet from kernel space."); 
MODULE_LICENSE("GPL"); 

Ora non capisco cosa è necessario. Ho catturato il pacchetto nel gancio PRE_ROUTING. Quindi ho creato un nuovo skb, lo ho popolato dal vecchio skb ricevuto e poi ho modificato l'indirizzo di origine (saddr), l'indirizzo di destinazione (daddr), la porta di origine (sorgente) e la porta di destinazione (dest), in modo che il il pacchetto può essere inoltrato dal gancio PRE_ROUTING. Poiché le decisioni di routing vengono prese dopo che il pacchetto è passato dal gancio, cerco il pacchetto da inoltrare alla sua origine. Ma per qualche ragione non sta facendo così. Il pacchetto viene ricevuto e tutto viene modificato, ma il pacchetto non sembra muoversi all'indietro. Non capisco cosa manca e anche ciò che è necessario per farlo funzionare. Più specificamente, cosa è necessario per inviare un pacchetto alla rete da un hook PRE_ROUTING?

+2

Dove sei bloccato? Cosa non funziona? ** Qual è la tua domanda? ** ("Qualcuno può aiutarmi?" Non è una domanda accettabile) –

+1

Grazie per i suggerimenti: "" Qualcuno può aiutarmi? "Non è una domanda accettabile" ... Non posterò mai qualcosa di simile .. e tra poco modifico la domanda per essere specifica. In realtà non ha senso inserire il codice e chiedere aiuto. –

+0

Qual è la tua ragione per farlo nello spazio del kernel? o semplicemente imparando? – Fingolfin

risposta

5

Manca molto.

Prima di tutto, il gancio netfilter che hai usato sia pre-routing che cattura i pacchetti in entrata e così se non si utilizza una qualche funzione del kernel per trasmettere il pacchetto che hai costruito, tornare NF_ACCEPT lascerà solo il pacchetto che si alterato (o no) continua sulla sua strada (che è per il sistema locale, non da esso).
Leggere informazioni su funzioni come dev_queue_xmit(struct sk_buff *) ma si noti che prima di utilizzare questa funzione, l'SKB deve avere l'intestazione Link-layer perché questa funzione in realtà mette in coda il proprio pacchetto in coda per essere inviato immediatamente alla scheda NIC ed è compito dell'utente impostare Link a livello di indirizzi.

In secondo luogo, ricorda che dopo aver modificato gli indirizzi dell'header IP devi ricalcolare il checksum del pacchetto, altrimenti il ​​tuo pacchetto verrà scartato all'altro capo.

In terzo luogo, si noti che fare ciò che si sta tentando di fare nello spazio del kernel è in gran parte considerato una pratica MOLTO cattiva. I moduli del kernel esistono per un motivo e questo non è uno di questi, netfilter è un ottimo strumento, ma in realtà non dovrebbe essere usato per inviare traffico normale.

EDIT:

Leggendo il tuo ultimo commento; Ti suggerisco di leggere la libreria libPCap, dovrebbe servire molto bene e mantenere il tuo lavoro al posto giusto, lo spazio utente.

+0

"Terzo, nota che facendo ciò che 'cercare di fare nello spazio del kernel è in gran parte considerato una pratica MOLTO cattiva.I moduli del kernel esistono per un motivo e questo non è uno di questi, netfilter è un ottimo strumento, ma in realtà non dovrebbe essere usato per inviare traffico normale. " Questo è solo un esperimento. Ho visto echoing dei pacchetti dal xt_ECHO.c dei moduli helper del pacchetto xtables-addons, e ho cercato di implementarlo da zero, perché le funzioni che usavano non erano quelle presenti nel kernel di Linux (in qualche modo diverso mi sembrava). Così ho provato a farlo usando le funzioni presenti nel kernel. –

+0

Oh, è grandioso. Sto solo citando questo perché ho usato net-filter in modo simile in un progetto serio e mi sta ancora dando un enorme mal di testa. – Fingolfin

+0

in realtà, l'eco dei pacchetti non fa parte del mio progetto. Nel mio progetto avevo bisogno di riempire un pacchetto, decifrare pacchetti di crittografia, pacchetti che si dividono e così..per gli esperimenti, ho provato a echo per l'esperimento. Ecco i link delle mie domande: http://stackoverflow.com/questions/12529497/how-to-append-data-on-a-packet-from-kernel-space?rq=1 e http: // stackoverflow. com/domande/12999548/how-to-route-la-spacco-pacchetti-utilizzando-netfilter-ganci-in-kernel-space. Sì, è abbastanza disgustoso gestire cose non standard nello spazio del kernel. –

1

Oltre alla risposta precedente, un'altra tecnica che può essere usato per riprodurre un pacchetto UDP la richiamata netfilter è:

inviare il pacchetto indietro, come un nuovo pacchetto UDP, utilizzando:

int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)

in net/socket.c

o anche utilizzando la libreria netpoll, come ha detto nella risposta here.

Il pacchetto originale può essere eliminato utilizzando NF_DROP.

Nel callback netfilter, che viene eseguito in un "contesto di interruzione" è possibile inviare pacchetti, ma non è possibile riceverli (poiché ogni tentativo di attesa provoca un errore di kernel). Per questo motivo la soluzione che ho proposto funziona con UDP, ma non può funzionare con TCP (l'handshake TCP richiede che sia ricevuto il messaggio ACK).

In ogni caso, come già detto, fare questo genere di cose nello spazio del kernel è BAD e dovrebbe essere usato solo per scopi di apprendimento.