Impedisce al kernel di elaborare segmenti TCP legati a un socket non elaborato

Secondo http://linux.die.net/man/7/raw , raw_socket = socket(AF_INET, SOCK_RAW, int protocol); è il modo per creare un socket raw.

  1. Suppongo che i socket raw siano creati su layer 3 e quindi il protocollo non dovrebbe essere IPPROTO_TCP / IPPROTO_UDP ma dovrebbe essere IPPROTO_IP . Questa comprensione è corretta?

  2. Ma quando creo il socket raw con protocollo come IPPROTO_IP ( *socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_IP); ), la creazione del socket fallisce con l’errore Protocollo non supportato

  3. Quando creo il socket raw con protocollo come IPPROTO_RAW ( *socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); ), la mia applicazione non riceve alcun pacchetto

  4. Quando creo il socket raw con protocollo come IPPROTO_TCP ( socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); ), la mia applicazione riceve i pacchetti TCP, ma il kernel risponde anche a questi pacchetti (e nel mio caso rimanda il collegamento). Suppongo sia perché il kernel pensa che non ci sia nessuno ad ascoltare la porta a cui è destinato quel pacchetto.

La mia intenzione è solo quella di inviare risposte ai messaggi che arrivano alla mia applicazione con un’intestazione IP e TCP falsa. Dato che nessuno dei suddetti tentativi ha funzionato per me, come dovrei creare il socket raw e rendere il livello TCP del kernel silenzioso solo per quella connessione?

EDIT: salta le domande 1-3. Hanno già risposto da Filipe. Per ques 4, abbiamo una soluzione alternativa. Ma mantenendo aperta la domanda, se qualcuno qui fuori ha una risposta e vorrebbe rispondere.

Suppongo che i socket raw siano creati su layer-3 e quindi il protocollo non dovrebbe essere IPPROTO_TCP / IPPROTO_UDP ma dovrebbe essere IPPROTO_IP. Questa comprensione è corretta?

No. Hai ragione che i socket raw sono fondamentalmente pacchetti di livello 3, ma il protocollo non dovrebbe essere IPPROTO_IP . L’argomento del protocollo nel caso di socket non elaborati indica il tipo di pacchetti che ti interessa ricevere su quel socket. Ricorda che un protocollo esegue essenzialmente la demultiplexing a livello di trasporto, quindi devi specificare quale tipo di protocollo è interessato al tuo raw socket. Ciò è reso chiaro in man 7 raw :

Tutti i pacchetti o gli errori che corrispondono al numero di protocollo specificato per il socket non elaborato vengono passati a questo socket. Per un elenco dei protocolli consentiti, vedere i numeri assegnati da RFC 1700 e getprotobyname (3).

Dato che sei interessato a ricevere pacchetti IP per una connessione TCP, dovresti usare IPPROTO_TCP .

Ma quando creo il socket raw con protocollo IPPROTO_IP (* socketFd = socket (AF_INET, SOCK_RAW, IPPROTO_IP);), la creazione del socket fallisce con l’errore Protocollo non supportato.

Sì, è un po ‘prevedibile: il protocollo IP non è un protocollo di livello 4. Come ho detto, il campo del protocollo viene utilizzato per il demultiplexing del livello di trasporto, quindi non ha senso usare IPPROTO_IP .

Quando creo il socket raw con protocollo come IPPROTO_RAW (* socketFd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);), la mia applicazione non riceve alcun pacchetto

Questo perché IPPROTO_RAW significa che sei interessato a inviare tutti i tipi di pacchetti di protocollo (TCP, UDP o qualsiasi altro protocollo). Ma con IPPROTO_RAW non puoi fare il contrario: IPPROTO_RAW significherebbe che potresti ricevere qualsiasi protocollo in questo socket non elaborato, che non è supportato. Questo è anche chiaro in man 7 raw :

Un protocollo IPPROTO_RAW implica IP_HDRINCL abilitato ed è in grado di inviare qualsiasi protocollo IP specificato nell’intestazione passata. La ricezione di tutti i protocolli IP tramite IPPROTO_RAW non è ansible usando socket grezzi.

In altre parole, IPPROTO_RAW ti dà la possibilità di inviare pacchetti che corrispondono a qualsiasi protocollo, ma al costo di impedirti di ottenere una risposta. Potresti creare altri socket raw specifici legati a un protocollo per ottenere le risposte come soluzione alternativa, ma questo complica la progettazione perché devi gestire un pool di socket raw e non è assolutamente quello che vuoi fare qui.

Quando creo il socket raw con protocollo come IPPROTO_TCP (socketFd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP);), la mia applicazione riceve i pacchetti TCP, ma il kernel risponde anche a questi pacchetti (e nel mio caso rimanda il collegamento). Suppongo sia perché il kernel pensa che non ci sia nessuno ad ascoltare la porta a cui è destinato quel pacchetto.

Non puoi impedire al kernel di fare il suo lavoro. Dalla manpage dei socket grezzi:

Quando un pacchetto viene ricevuto, viene passato a qualsiasi socket non elaborato che è stato associato al suo protocollo prima di essere passato ad altri gestori di protocollo (ad esempio, moduli del protocollo del kernel).

Quindi hai ragione che il kernel invia un pacchetto RST perché non ha conoscenza dei socket TCP attivi o delle connessioni sulla porta specificata. Come ho detto, non puoi impedire al kernel di fare il suo lavoro, ma un hack relativamente rapido (e forse brutto) è quello di eliminare i pacchetti RST con iptables:

 iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP 

Sì, non molto elegante, ma penso che non ci sia molto che possiamo fare qui.

Come suggerito nei commenti, potresti anche creare un socket TCP fittizio associato alla stessa porta e all’indirizzo in cui ricevi e scarti i messaggi. In questo modo il kernel non invierà risposte RST , e non è necessario fare confusione con iptables.

Ricorda inoltre che, poiché è necessario specificare IPPROTO_TCP per il socket non IP_HDRINCL , è necessario impostare IP_HDRINCL sul socket con setsockopts(2) modo da poter creare l’intestazione IP personalizzata.

Infine, assicurati che il processo che sta eseguendo abbia un ID utente efficace di 0 o la capacità CAP_NET_RAW (in pratica: eseguilo come root).

[questo è un commento al commento di Jonathon Reinhart, ma non ho abbastanza reputazione]

per quanto riguarda le prese AF-PACKET

La creazione di socket grezzi con AF_PACKET è descritta nel man packet . Il valore per la variabile “protocollo” dovrebbe essere il valore di ethertype nell’ordine dei byte di rete. Questi valori sono definiti in (nell’ordine byte host). Quindi per aprire una presa grezza che fai

socketFd = socket ( AF_PACKET , SOCK_RAW , htons ( ETH_P_IP ) );

Se i socket AF_PACKET sono ciò di cui hai bisogno, puoi anche usare libpcap / tcpdump. Ti permetterà di catturare frame ethernet e inviare frame raw ethernet. Per l’acquisizione è ansible impostare un filtro su quali frame si desidera (ad esempio, porta TCP X). (fondamentalmente con libpcap puoi fare le stesse cose con i socket AF_PACKET, ma più facile)

collegamenti per la guida per l’acquisizione e la pagina man per l’invio

Il seguente link http://www.tenouk.com/Module43a.html è stato recuperato da una risposta a questa domanda Semplice socket server raw in C / C ++ su Linux E suggerisce che devi essere root o in esecuzione come setuid0 per usare un zoccolo crudo

 /* Must be root or SUID 0 to open RAW socket */ ... /* Create RAW socket */ if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket() error"); /* If something wrong, just exit */ exit(1); }