transfert de paquets vers le même hôte sans utiliser de réseau de bouclage

J’ai cette application libnetfilter_queue qui reçoit les paquets du kernel en fonction de certaines règles iptables. Avant de passer directement à mon problème, je donne un exemple de code exploitable et d’autres outils pour configurer un environnement de test afin que la définition du problème et les solutions possibles soient plus précises et plus robustes.

Le code suivant décrit les fonctionnalités principales de l’application:

#include  #include  #include  #include  #include  #include  /* for NF_ACCEPT */ #include  #include  #define PREROUTING 0 #define POSTROUTING 4 #define OUTPUT 3 /* returns packet id */ static u_int32_t print_pkt (struct nfq_data *tb) { int id = 0; struct nfqnl_msg_packet_hdr *ph; struct nfqnl_msg_packet_hw *hwph; u_int32_t mark, ifi; int ret; unsigned char *data; ph = nfq_get_msg_packet_hdr (tb); if (ph) { id = ntohl (ph->packet_id); printf ("hw_protocol=0x%04x hook=%u id=%u ", ntohs (ph->hw_protocol), ph->hook, id); } hwph = nfq_get_packet_hw (tb); if (hwph) { int i, hlen = ntohs (hwph->hw_addrlen); printf ("hw_src_addr="); for (i = 0; i hw_addr[i]); printf ("%02x ", hwph->hw_addr[hlen - 1]); } mark = nfq_get_nfmark (tb); if (mark) printf ("mark=%u ", mark); ifi = nfq_get_indev (tb); if (ifi) printf ("indev=%u ", ifi); ifi = nfq_get_outdev (tb); if (ifi) printf ("outdev=%u ", ifi); ifi = nfq_get_physindev (tb); if (ifi) printf ("physindev=%u ", ifi); ifi = nfq_get_physoutdev (tb); if (ifi) printf ("physoutdev=%u ", ifi); ret = nfq_get_payload (tb, &data); if (ret >= 0) printf ("payload_len=%d ", ret); fputc ('\n', stdout); return id; } static int cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { uint32_t ip_src, ip_dst; struct in_addr s_ip; struct in_addr d_ip; uint16_t src_port; uint16_t dst_port; int verdict; int id; int ret; unsigned char *buffer; struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa); if (ph) { id = ntohl (ph->packet_id); printf ("received packet with id %d", id); } ret = nfq_get_payload (nfa, &buffer); ip_src = *((uint32_t *) (buffer + 12)); ip_dst = *((uint32_t *) (buffer + 16)); src_port = *((uint16_t *) (buffer + 20)); dst_port = *((uint16_t *) (buffer + 22)); s_ip.s_addr = (uint32_t) ip_src; d_ip.s_addr = (uint32_t) ip_dst; *(buffer + 26) = 0x00; *(buffer + 27) = 0x00; printf ( "source IP %s", inet_ntoa (s_ip)); printf ( "destination IP %s", inet_ntoa (d_ip)); printf ( "source port %d", src_port); printf ( "destination port %d", dst_port); if (ret) { switch (ph->hook) { case PREROUTING: printf ( "inbound packet"); //my_mangling_fun(); break; case OUTPUT: printf ( "outbound packet"); //my_mangling_fun(); break; } } verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer); if (verdict) printf ( "verdict ok"); return verdict; } int main (int argc, char **argv) { struct nfq_handle *h; struct nfq_q_handle *qh; struct nfnl_handle *nh; int fd; int rv; char buf[4096] __atsortingbute__ ((aligned)); printf ("opening library handle\n"); h = nfq_open (); if (!h) { fprintf (stderr, "error during nfq_open()\n"); exit (1); } printf ("unbinding existing nf_queue handler for AF_INET (if any)\n"); if (nfq_unbind_pf (h, AF_INET) < 0) { fprintf (stderr, "error during nfq_unbind_pf()\n"); exit (1); } printf ("binding nfnetlink_queue as nf_queue handler for AF_INET\n"); if (nfq_bind_pf (h, AF_INET) < 0) { fprintf (stderr, "error during nfq_bind_pf()\n"); exit (1); } printf ("binding this socket to queue '0'\n"); qh = nfq_create_queue (h, 0, &cb, NULL); if (!qh) { fprintf (stderr, "error during nfq_create_queue()\n"); exit (1); } printf ("setting copy_packet mode\n"); if (nfq_set_mode (qh, NFQNL_COPY_PACKET, 0xffff) = 0) { printf ("pkt received\n"); nfq_handle_packet (h, buf, rv); continue; } /* if your application is too slow to digest the packets that * are sent from kernel-space, the socket buffer that we use * to enqueue packets may fill up returning ENOBUFS. Depending * on your application, this error may be ignored. Please, see * the doxygen documentation of this library on how to improve * this situation. */ if (rv < 0 && errno == ENOBUFS) { printf ("losing packets!\n"); continue; } perror ("recv failed"); break; } printf ("unbinding from queue 0\n"); nfq_destroy_queue (qh); #ifdef INSANE /* normally, applications SHOULD NOT issue this command, since * it detaches other programs/sockets from AF_INET, too ! */ printf ("unbinding from AF_INET\n"); nfq_unbind_pf (h, AF_INET); #endif printf ("closing library handle\n"); nfq_close (h); exit (0); } 

Notez que dans la fonction de rappel, deux appels à my_mangling_fun () sont commentés. C’est là que je décompose les paquets entrant et sortant. Je pense que ce code serait suffisant pour décrire mon cas. Si des précisions supplémentaires sont nécessaires, veuillez demander, je posterai plus de détails.

Disons que les règles iptables sont les suivantes:

 $iptables -t mangle -A PREROUTING -p udp --dport 5000 -j NFQUEUE $iptables -t mangle -A OUTPUT -p udp --sport 5000 -j NFQUEUE 

permet de comstackr et de tirer UDP la chose.

 $gcc -g3 nfq_test.c -lnfnetlink -lnetfilter_queue $./a.out (should be as root) 

maintenant nous pouvons nourrir des déchets UDP charge utile à cette chose par Netcat deux mode client et serveur

 $nc -ul 5000 $nc -uvv  5000 

Cela va imprimer le paquet de mon application netfilter_queue dans stdout. Maintenant que l’environnement de développement est configuré, nous pouvons passer à autre chose.

Ce que nous essayons de réaliser est le suivant:

Notre serveur écoute sur le port 5000. Maintenant, tous les paquets entrants destinés au port UDP 5000 seront mis en queue par le kernel. Et le descripteur de cette queue sera atsortingbué à l’application utilisateur indiquée précédemment. Ce mécanisme de queue fonctionne comme suit: lorsqu’un paquet est disponible, la fonction de rappel (cb () dans notre code) est appelée. après le traitement, la fonction de rappel appelle nfq_set_verdict (). après qu’un verdict est retourné, le prochain paquet sortira de la queue. notez qu’un paquet ne sortira pas de la file d’attente si le précédent paquet n’a pas fait l’object d’un verdict. Ces valeurs de verdict sont NF_ACCEPT pour accepter un paquet et NF_DROP pour supprimer le paquet.

Maintenant, que se passe-t-il si je veux concaténer les charges utiles udp des paquets entrants et sortants sans toucher au code côté client et côté serveur?

Si je veux concaténer les charges utiles udp de notre application, cette application même, nous devons avoir plusieurs paquets à scope de main. Mais nous avons vu qu’un paquet ne sort pas de la queue avant qu’un verdict ne soit émis vers le précédent.

Alors, comment cela peut-il être fait?

Une solution possible consiste à atsortingbuer un NF_DROP à chaque paquet et à enregistrer ces paquets dans une structure de données intermédiaire. Disons que nous l’avons fait. Mais comment ce paquet peut-il être remis au service en écoutant sur le port 5000?

Nous ne pouvons pas utiliser la stack réseau pour livrer le paquet, car si nous le faisons, les paquets se retrouveront dans NFQUEUE.

Un autre problème est que le serveur est totalement agnostique à propos de cette application. Cela signifie qu’il ne devrait y avoir aucune différence dans les paquets. Il devrait voir les paquets comme s’ils venaient du client d’origine.

J’ai entendu dire qu’une application peut envoyer des données à un serveur du même hôte sans utiliser la couche réseau (ip, port) en écrivant des fichiers. Je ne connais pas la validité de cette déclaration. Mais si quelqu’un sait quelque chose à ce sujet, ce sera merveilleux.

Je suis peut-être trop voté pour trop de verbosité. Mais je pense que cela peut être une session amusante. nous pouvons trouver la solution ensemble 🙂

Je propose la solution suivante:

  • stocker les paquets dans l’application et rendre le verdict NF_DROP
  • réinjecter des paquets dans la stack réseau à l’aide de sockets RAW
  • balise les paquets UDP concaténés avec un DSCP (voir le format de paquet IP)
  • dans iptables, ajoutez une règle pour faire correspondre ce DSCP (–dscp) et ACCEPTER le paquet directement, sans qu’il passe par votre application netfilter

Si votre fournisseur marque déjà certains paquets avec DSCP, vous pouvez append des règles iptables pour les effacer, telles que:

 iptables -t mangle -A INPUT -j DSCP --set-dscp 0 

J’espère que cela résout votre cas d’utilisation.

Tout d’abord, merci beaucoup Aftnix! Votre exemple de démarrage a démarré mon projet de réveil sur LAN par inspection de paquets automatique. Je souhaite que mon serveur domestique s’endorme lorsqu’il est inactif, mais me réveille dès que certaines demandes arrivent. L’idée est d’inspecter la demande sur un routeur ddwrt, de décider qu’il s’agit d’une demande légitime et d’envoyer un paquet wol. Pour SMTP, l’idée est de mettre plusieurs paquets en file d’attente, de contenter l’autre extrémité avec des réponses fictives et de lancer le serveur réel de manière transparente.

J’ai modifié un peu votre exemple pour mettre en attente 1 paquet et l’envoyer avec le prochain paquet. Ceci est juste une preuve de concept, mais cela fonctionne bien.

 // struct and variable needed to store 1 packet struct packetbuffer { struct nfq_q_handle *qh; u_int32_t id; u_int32_t verdict; u_int32_t data_len; unsigned char *buf; }; struct packetbuffer pbuf; int counter = 0; static int cb (struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { uint32_t ip_src, ip_dst; struct in_addr s_ip; struct in_addr d_ip; uint16_t src_port; uint16_t dst_port; int verdict; int id; int ret; unsigned char *buffer; struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa); if (ph) { id = ntohl (ph->packet_id); printf ("received packet with id %d", id); } ret = nfq_get_payload (nfa, &buffer); ip_src = *((uint32_t *) (buffer + 12)); ip_dst = *((uint32_t *) (buffer + 16)); src_port = *((uint16_t *) (buffer + 20)); dst_port = *((uint16_t *) (buffer + 22)); s_ip.s_addr = (uint32_t) ip_src; d_ip.s_addr = (uint32_t) ip_dst; *(buffer + 26) = 0x00; *(buffer + 27) = 0x00; printf ( "source IP %s", inet_ntoa (s_ip)); printf ( "destination IP %s", inet_ntoa (d_ip)); printf ( "source port %d", src_port); printf ( "destination port %d", dst_port); if (ret) { switch (ph->hook) { case PREROUTING: printf ( "inbound packet"); //my_mangling_fun(); break; case OUTPUT: printf ( "outbound packet"); //my_mangling_fun(); break; } } // My modification starts here if ((counter % 2) == 0) { pbuf.qh = qh; pbuf.id = id; pbuf.data_len = ret; pbuf.buf = malloc(ret); memcpy(pbuf.buf, buffer, ret); printf(" queue package %d \n", id); } else { printf(" output 1st package %d, len=%d\n", pbuf.id, pbuf.data_len); verdict = nfq_set_verdict (pbuf.qh, pbuf.id, NF_ACCEPT, pbuf.data_len, pbuf.buf); free(pbuf.buf); printf(" output 2nd package %d, len=%d\n", id, ret); verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer); } counter++; return 0; } 

Ce n’est pas tout le code, mais il devrait être assez évident ce qui a changé.

Je sais que je suis un peu en retard pour la fête, mais j’ai décidé de poster cette solution pour référence future et, espérons-le, pour certains critiques.

edit: hmm je ferais peut-être mieux d’écrire un processus utilisateur tel que rinetd.