iptablesルールに基づいてカーネルからパケットを受け取るlibnetfilter_queueアプリケーションがあります。私の問題に直接進む前に、問題の定義と可能な解決策がより正確で堅牢になるように、実用的なサンプルコードとテスト環境を設定するためのツールを提供しています。 2つのコールがmy_mangling_funするコールバック関数でループバックネットワークを使用せずに同じホストのサービスにパケットを転送する
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
#include <errno.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#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 < hlen - 1; i++)
printf ("%02x:", hwph->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] __attribute__ ((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)
{
fprintf (stderr, "can't set packet_copy mode\n");
exit (1);
}
fd = nfq_fd (h);
for (;;)
{
if ((rv = recv (fd, buf, sizeof (buf), 0)) >= 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);
}
お知らせ()コメントアウトされている:
次のコードは、アプリケーションのコア機能を記載しています。これは、着信パケットと発信パケットをマングルするところです。私はこのコードが私の場合を説明するのに十分であると思う。さらなる明確化が必要な場合は、私は詳細を掲載します。
$iptables -t mangle -A PREROUTING -p udp --dport 5000 -j NFQUEUE
$iptables -t mangle -A OUTPUT -p udp --sport 5000 -j NFQUEUE
事UDPコンパイルして火災ができます:
は、添付のiptablesのルールは以下の通りとしましょう。
$gcc -g3 nfq_test.c -lnfnetlink -lnetfilter_queue
$./a.out (should be as root)
今はこれが標準出力で私のnetfilter_queueアプリからパケットを出力します
$nc -ul 5000
$nc -uvv <IP> 5000
netcatをすることによって、この事にクライアントとサーバーの両方のモードをゴミUDPペイロードを養うことができます。開発環境が整ったので、次のものに移行することができます。
当社のサーバは5000ポートで待機している:私たちが達成しようとしている何
は次のとおりです。 udpポート5000宛てのすべての着信パケットは、カーネルによってキューに入れられます。このキューのハンドルは、先ほどリストしたユーザアプリケーションに与えられます。このキューメカニズムは次のように機能します。パケットが利用可能な場合、コールバック関数(コード内のcb())が呼び出されます。処理後、コールバック関数はnfq_set_verdict()を呼び出します。 判定後にはが返され、次のパケットがキューからポップされます。前のパケットが判定されなかった場合、パケットはキューからポップしないことに注意してください。この判定値は、パケットを受け入れるためのNF_ACCEPT、パケットを廃棄するためのNF_DROPです。
クライアントとサーバー側のコードに触れることなく、着信パケットと発信パケットのudpペイロードを連結したい場合はどうすればよいですか?
私はこのアプリからudpペイロードを連結したいと思うなら、複数のパケットを手元に置く必要があります。しかし、パケットがキューからポップしていないことがわかりました。
これはどのようにすることができますか?
可能な解決策の1つは、すべてのパケットにNF_DROPを発行し、それらのパケットを中間データ構造に保存することです。私たちはそれをやったとしよう。しかし、このパケットは、5000ポートでリッスンするサービスにどのように配信できますか?
パケットを配信するためにネットワークスタックを使用することはできません。なぜなら、パケットがNFQUEUEに再び終わるからです。
もう1つの問題は、サーバーがこのアプリケーションに完全に無関心であることです。つまり、パケットに違いは見られないはずです。パケットは元のクライアントからのものであるかのように見えるはずです。
アプリケーションはネットワークレイヤ(ip、port)を使わずに同じホストのサーバにデータを送ることができると聞いています。私はこの声明の妥当性を知らない。しかし、誰かがそれについて何かを知っていれば、それはすばらしいことになります。
あまりにも冗長に投票してしまうことがあります。しかし、これは楽しいセッションだと思う。私たちは、私は以下のソリューションを提案:)一緒
ありがとう、これは私がやろうとしているように聞こえます。私はそれについていくつかのテストを行い、結果を更新します。 – Aftnix
最初のオプション(カスタム構造体に捕捉された各パケットのペイロードとヘッダーを格納してリンクリストに入れる)を使用すると、必要に応じてnfq_set_verdictまたはrawソケットを使ってパケットを再注入する必要があります? – elmazzun