Linuxでのファイアウォールの作成とテスト、パート2.3。 ファイアウォールを完成させます。 ユーザースペースでトラフィックを処理する

最初の部分の内容:





2番目の部分の内容:



2.1-2番目のパートの紹介。 ネットワークとプロトコルを調べます。 Wireshark

2.2-ファイアウォールテーブル。 トランスポート層 構造TCP、UDP。 ファイアウォールを拡張します。

2.3-機能を拡張します。 ユーザー空間でデータを処理します。 libnetfilter_queue。

2.4-ボーナス。 実際のバッファオーバーフロー攻撃を調査し、ファイアウォールで防止します。



パート2.3-はじめに



最後のパートを終えました



if(dest_port == HTTP_PORT || src_port == HTTP_PORT) { printk("HTTP packet\n"); }
      
      





このパートでは、パケットをカーネル空間からユーザー空間に、またはその逆に送信することがどれほど簡単かを見ていきます。 たとえば、HTTP接続を使用して、個々のサイトのブロックを追加します。 ボーナスパートでは、既存の脆弱性と、例としてその対処方法を検討します。



libnetfilter_queue。



幸運にも、既成のソリューション- ユーザー空間の netfilterと簡単にやり取りできるようにCで書かれた低レベルのライブラリ-libnetfilter_queueがあります。 名前が示すように、次のパッケージを受け取り、私たちが設定したhook_functionが呼び出されると、パッケージを受け入れ( NF_ACCEPTを返す )、それを捨てる( NF_DROPを返す )ことができ、次のようにするだけで特別なキューに入れることができます



 #define HTTP_QUEUE_NUMBER 1 return NF_QUEUE_NR(HTTP_QUEUE_NUMBER);
      
      





HTTP_QUEUE_NUMBER-これは、パケットの送信先として定義したキュー番号です(複数のキューがある場合があり、 タイプごとにパケットを簡単に分離するのに便利です)

NF_QUEUE_NRは使用する必要があるマクロです(目的の数値を返すためにビット単位のシフトを行います)。

デフォルトでは、次のように書くこともできます。



NF_QUEUEを返します



そして、すべてのパケットは番号ゼロでキューに送信されます。 つまり、 httpプロトコルに関連するすべてのパケットを送信するためにコードを変更する必要があるのは、次の行を追加することだけです。



 if(dest_port == HTTP_PORT || src_port == HTTP_PORT) { printk("HTTP packet\n"); return NF_QUEUE_NR(HTTP_QUEUE_NUMBER); }
      
      





次に、これらのパッケージをユーザー空間で取得する必要があります。 そして、ここでlibnetfilter_queueが役立ちます。



使用するには、必要なライブラリをダウンロードしてインストールする必要があります。 インストールの詳細- ここでは、インストールする必要があるすべてのもの。



内部には簡単な例もあり、検索エンジンを使用してインストールの問題を解決しています(別のパッケージをインストールする必要がありました)。 例から最も重要な点を見てみましょう。 メイン()関数(ユーザー空間にいることを思い出します!)では、異なる初期化が発生します



 int main(int argc, char **argv) { … printf("opening library handle\n"); h = nfq_open(); if (!h) { exit(1); } … qh = nfq_create_queue(h, 0, &cb, NULL); if (!qh) { exit(1); } … for (;;) { if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) { printf("pkt received\n"); nfq_handle_packet(h, buf, rv); continue; } …
      
      





正しいQUEUE番号を示すことが重要です。 例では、これはゼロですが、 qh = nfq_create_queue(h、HTTP_QUEUE_NUMBER、&cb、NULL);に変更します。

すべての初期化が成功した場合、無限ループになり、 カーネルからの新しいパッケージが到着するまで「待機」します。 このようなパケットが到着すると、cb関数が呼び出されます。これは、 nfw_create_queueにも渡したポインターです。



 static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { u_int32_t id = print_pkt(nfa); printf("entering callback\n"); return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); }
      
      





次に、この関数はprint_pktを呼び出します。この関数は、 到着したパッケージを含む構造体へのポインターを渡します。 各パケットは、受信の順序に従って、 print_pktが返すID番号(これを見ていきます)を受信し、最後にパケットの判定関数(この場合はNF_ACCEPT )が受け入れられます。



例の「中心」にあるのはprint_pktです:



 static u_int32_t print_pkt(struct nfq_data *tb)
      
      





元の例では、 nfq_data * tbを介してさまざまなデータにアクセスするための多くの関数がありますが、前の部分( アプリケーショントランスポートネットワーク)で馴染みのあるレベルのデータへのアクセスを検討します



完全な機能コードは次のとおりです。



 static u_int32_t print_pkt(struct nfq_data *tb) { int i = 0; int packet_len = 0; unsigned char *data = NULL; int ip_src_array[4] = {0}; int ip_dst_array[4] = {0}; packet_len = nfq_get_payload(tb, &data); ip_dst_array[3] = data[16]; ip_dst_array[2] = data[17]; ip_dst_array[1] = data[18]; ip_dst_array[0] = data[19]; ip_src_array[3] = data[12]; ip_src_array[2] = data[13]; ip_src_array[1] = data[14]; ip_src_array[0] = data[15]; char ip_dst_str[20] = {0}; char ip_src_str[20] = {0}; ip_hl_to_str(ip_dst_array, ip_dst_str); ip_hl_to_str(ip_src_array, ip_src_str); printf("src_ip = %s, dst_ip = %s", ip_src_str, ip_dst_str); printf("\n"); if( packet_len >= 0x34 ) { for(i = 0x34; i < packet_len; ++i){ if(data[i] >= ' ' && data[i] <= '}') printf("%c", (int)data[i]); } } printf("End of packet checking\n"); return NF_ACCEPT; }
      
      





さて、これ以上の質問はないはずです。



コンパイル、チェック:



これが、すべての部品の外観、HTTPトラフィックをリッスンするHTTPサーバー、 インターフェース -デバイス管理、 モジュール -ファイアウォールです。 httpサーバーをコンパイルし、 ファイアウォールを起動し、 httpサーバーを起動します。







host2とは別に移動し、今回はポート80でhost1に接続し、 GETコマンドを送信してメインページを取得します。







httpサーバーが書き込む結果を確認します。 私たちから送信されたGETコマンドと、その後、 host1がリクエストへの応答として送信したページ全体を確認できます。







これで、たとえば、個々のサイトへのアクセスをブロックする方法が非常に明確になりました。



最初と2番目の部分の完了



最初の部分は、Linuxオペレーティングシステムで3台のコンピューターの単純なネットワークを構築することから始めました。 次に、コンピューターのネットワークカードを通過するパケットの数を巧みにカウントし、それ以上の送信をカットして禁止する単純なデバイスを作成しました。 これらはすべて、オペレーティングシステムのカーネルレベルで発生しました。 デバイスを制御し、デバイスから情報を受信するのを便利にするために、コマンドをデバイスに送信し、デバイスからデータを受信できるユーザーインターフェイスを追加しました。 そのため、統計は私たちにとって便利な形で得られました。



2番目の部分は、ネットワークとプロトコルの調査から始まり、それらの一部の詳細を調べます。 そして、知識を適用して、ファイアウォールの機能を大幅に拡張し、興味のあるOSIレベルでパッケージに含まれるすべての必要な情報にアクセスしました。 これはすべてカーネル空間で発生します。



このパートでは、一部のパッケージをユーザー空間に送信し、コードとその機能を書く便利さを大幅に拡大し、多くの利用可能な既製のC \ C ++ライブラリを使用する可能性を発見しました。 これを実行した後、パフォーマンスを「支払った」が、同時にオペレーティングシステムのカーネルをロードせず、トラフィックの分析をユーザーアプリケーションにシフトしたことに注意する必要があります。 ファイアウォールの速度は大幅に低下しましたが、今回のケースでは重要ではありません。



最新のファイアウォールは、すべてが速度と品質に合わせて調整された非常に複雑なハードウェアとソフトウェアの複合体であり、大規模ネットワークへのメイン「入口」にインストールされ、1秒あたりのギガバイトを処理します。 ギガバイト単位のわずかなパフォーマンスの低下(たとえば、パケットの処理)でも非常に高価になることが想像できます。 したがって、彼らは常に弱いリンクを探しており、パフォーマンスの改善に取り組んでいます。 さらに、最新のファイアウォールのタスクには、ルールのチェックだけでなく、はるかに複雑なタスクも含まれます:さまざまな種類のDOS攻撃、ウイルスの撃退、情報漏洩の防止、コンピューターのハッキングの試行、疑わしいサイトへのアクセス、疑わしいコンテンツのダウンロード ボーナス記事としての最も単純な例は、最後の部分-最後の部分で検討します。



All Articles