新しく確立されたTCP接続(つまり、実際のペイロードを持つ最初のパケット)の最初のパケットのみを調べる方法を探しています。 iptablesでこれを行う方法は?ESTABLISHEDパケットを一致させると、ハンドシェイク後の接続のすべてのパケットが一致しますよね?
iptables
を使用して(乱用して)目標を達成できます。具体的には、connbytes
一致とNFQUEUE
ターゲットです。 connbytes
を使用すると、接続内のN番目のパケットを照合できます。NFQUEUE
は、iptablesルールに一致するパケットをユーザースペースプログラムに渡すためのメカニズムです。さらに、カーネルから関連するパケットを受信して処理するプログラムを使用する必要があります。
ここでは、サーバー側でパケットをキャプチャすることに関心があると想定しています(クライアント側でのキャプチャに関心がある場合は変更できます)。その場合、接続ごとに3番目の着信パケット(つまり、3ウェイハンドシェイク後の最初の着信パケット)をキャプチャする必要があります。パケットをnetfilterキュー(この場合はキュー#1)に入れます。
iptables -I INPUT -p tcp -m tcp --dport 12345 -m connbytes --connbytes-mode packets --connbytes-dir original --connbytes 3:3 -j NFQUEUE --queue-num 1
パケットがこのルールに一致するとすぐに、キュー#1にバインドされたユーザースペースプログラムに渡されます。その後、プログラムはパケットを調べて、それを受け入れるかドロップするかを決定できます。
libnetfilter_queue
library を使用してユーザースペースでパケットを受信するプログラムが必要になります。ライブラリのバインディングは、さまざまな言語で利用できます。以下は、Pythonで記述されたサンプルプログラムです。
import struct
from netfilterqueue import NetfilterQueue
def ip_to_string(ip):
return ".".join(map(lambda n: str(ip>>n & 0xff), [24,16,8,0]))
def print_and_accept(pkt):
pl = pkt.get_payload()
src_ip = struct.unpack('>I', pl[12:16])[0]
tcp_offset = (struct.unpack('>B', pl[0:1])[0] & 0xf) * 4
tmp = struct.unpack('>B', pl[tcp_offset+12:tcp_offset+13])[0]
data_offset = ((tmp & 0xf0) >> 4) * 4
src_port = struct.unpack('>H', pl[tcp_offset+0:tcp_offset+2])[0]
data = pl[tcp_offset + data_offset:]
print 'from {}:{}, "{}"'.format(ip_to_string(src_ip), src_port, data)
pkt.accept()
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
nfqueue.run()
except KeyboardInterrupt:
print
プログラムは、キューに入れられたパケットがIPv4 TCPパケットであると想定し、送信元ip:portペアとパケットのTCPペイロードを出力します。
NFQUEUE
ターゲットを使用する場合は注意が必要です。キューにバインドされたユーザースペースプログラムがハングしたり、クラッシュしたり、パケットの処理が遅い場合、パケットはドロップ/スタックし、指定されたポートにバインドされたサービスに到達できなくなります。指定されたキューにユーザースペースプログラムがバインドされていない場合は、--queue-bypass
オプションをNFQUEUE
ターゲットに渡して一致したパケットをACCEPT
に渡すことができます。これは、プログラムがクラッシュする問題に役立つはずです。しかし、ハングしたプログラムや遅いプログラムには役立ちません。ESTABLISHEDパケットを一致させると、ハンドシェイク後の接続のすべてのパケットが一致します。
正しい !
「キャプチャ」の意味がわかりません。 iptables
は別のものであり、ネットワークパケットキャプチャ(tcpdump
)は別のものです。
私の理解が「新しい接続のみをログに記録する」であると仮定すると、NEW
状態に一致するルールのみをログに記録する必要があります。
Icmp(ping)リクエストの新しい接続のみをログに記録するサンプルケースを使用してみましょう。
# Define LOG settings
iptables -N LOG_ACCEPT
iptables -A LOG_ACCEPT -j LOG --log-prefix '[IPTABLES ACCEPT] :'
iptables -A LOG_ACCEPT -j ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j ACCEPT
ここでは、LOG
接続が確立されたときにNEW
を実行します。したがって、(同じ送信元ホストから)1000以上のpingを送信でき、最初のパケットのみがログに記録されます。
Apr 4 21:28:17 UBN-1 kernel: [78512.613705] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=32571 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47088
LOG_ACCEPT
をESTABLISHED,RELATED
ルールに入れると:
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT
その後、各ping要求がログに記録されます。したがって、(同じソースホストからの)1000以上のpingは、ログに1000以上のエントリを生成します。
Apr 4 21:33:49 UBN-1 kernel: [78844.130615] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=244 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47092
Apr 4 21:33:50 UBN-1 kernel: [78845.130551] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=247 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47093
Apr 4 21:33:51 UBN-1 kernel: [78846.131295] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=250 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47094
Apr 4 21:33:52 UBN-1 kernel: [78847.132160] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=252 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47095
......
......
私が質問を理解したことを願っています!
在庫iptables
では探していることはできません。レイヤー7の検査コードを書く必要があります。
別の方法として、後処理に我慢したい場合は、tcpdump
を使用してトラフィックをPCAPファイルにキャプチャし、探しているパケットを解析して、残りを破棄することもできます。 WiresharkのSSLディセクタとディスプレイフィルタがあなたが探しているものを手に入れることができることを私は知っています。
キャプチャがリアルタイムより少し遅れることを気にしない場合は、tcpdump
を使用してローテーションするPCAPファイルへのトラフィックを簡単にキャプチャできます。完了したPCAPファイルを監視し、それらをtshark
と表示フィルターで解析して、探しているものだけを新しいPCAPファイルに書き出すスクリプトを実行できます。