ipf のルールファイルは、どんな名前でも良く、標準入力でもかまいません。 カーネル内部のフィルタリストを表示するとき、 ipfstat は解釈可能なルールを出力しますので、 この出力を ipf への入力としてフィードバックするのに使えます。 よって、入力パケットに対する全フィルタを除去するためには、次のようにします:
# ipfstat -i | ipf -rf -
ipf がフィルタルール構築に使用するフォーマットは、 BNF を使った文法で次のように示すことができます:
filter-rule = [ insert ] action in-out [ options ] [ tos ] [ ttl ] [ proto ] [ ip ] [ group ]. insert = "@" decnumber . action = block | "pass" | log | "count" | skip | auth | call . in-out = "in" | "out" . options = [ log ] [ "quick" ] [ "on" interface-name [ dup ] [ froute ] ] . tos = "tos" decnumber | "tos" hexnumber . ttl = "ttl" decnumber . proto = "proto" protocol . ip = srcdst [ flags ] [ with withopt ] [ icmp ] [ keep ] . group = [ "head" decnumber ] [ "group" decnumber ] . block = "block" [ return-icmp[return-code] | "return-rst" ] . auth = "auth" | "preauth" . log = "log" [ "body" ] [ "first" ] [ "or-block" ] [ "level" loglevel ] . call = "call" [ "now" ] function-name . skip = "skip" decnumber . dup = "dup-to" interface-name[":"ipaddr] . froute = "fastroute" | "to" interface-name[":"ipaddr] . protocol = "tcp/udp" | "udp" | "tcp" | "icmp" | decnumber . srcdst = "all" | fromto . fromto = "from" [ "!" ] object "to" [ "!" ] object . return-icmp = "return-icmp" | "return-icmp-as-dest" . object = addr [ port-comp | port-range ] . addr = "any" | nummask | host-name [ "mask" ipaddr | "mask" hexnumber ] . port-comp = "port" compare port-num . port-range = "port" port-num range port-num . flags = "flags" flag { flag } [ "/" flag { flag } ] . with = "with" | "and" . icmp = "icmp-type" icmp-type [ "code" decnumber ] . return-code = "("icmp-code")" . keep = "keep" "state" | "keep" "frags" . loglevel = facility"."priority | priority . nummask = host-name [ "/" decnumber ] . host-name = ipaddr | hostname | "any" . ipaddr = host-num "." host-num "." host-num "." host-num . host-num = digit [ digit [ digit ] ] . port-num = service-name | decnumber . withopt = [ "not" | "no" ] opttype [ withopt ] . opttype = "ipopts" | "short" | "frag" | "opt" optname . optname = ipopts [ "," optname ] . ipopts = optlist | "sec-class" [ secname ] . secname = seclvl [ "," secname ] . seclvl = "unclass" | "confid" | "reserv-1" | "reserv-2" | "reserv-3" | "reserv-4" | "secret" | "topsecret" . icmp-type = "unreach" | "echo" | "echorep" | "squench" | "redir" | "timex" | "paramprob" | "timest" | "timestrep" | "inforeq" | "inforep" | "maskreq" | "maskrep" | decnumber . icmp-code = decumber | "net-unr" | "host-unr" | "proto-unr" | "port-unr" | "needfrag" | "srcfail" | "net-unk" | "host-unk" | "isolate" | "net-prohib" | "host-prohib" | "net-tos" | "host-tos" | "filter-prohib" | "host-preced" | "cutoff-preced" . optlist = "nop" | "rr" | "zsu" | "mtup" | "mtur" | "encode" | "ts" | "tr" | "sec" | "lsrr" | "e-sec" | "cipso" | "satid" | "ssrr" | "addext" | "visa" | "imitd" | "eip" | "finn" . facility = "kern" | "user" | "mail" | "daemon" | "auth" | "syslog" | "lpr" | "news" | "uucp" | "cron" | "ftp" | "authpriv" | "audit" | "logalert" | "local0" | "local1" | "local2" | "local3" | "local4" | "local5" | "local6" | "local7" . priority = "emerg" | "alert" | "crit" | "err" | "warn" | "notice" | "info" | "debug" . hexnumber = "0" "x" hexstring . hexstring = hexdigit [ hexstring ] . decnumber = digit [ decnumber ] . compare = "=" | "!=" | "<" | ">" | "<=" | ">=" | "eq" | "ne" | "lt" | "gt" | "le" | "ge" . range = "<>" | "><" . hexdigit = digit | "a" | "b" | "c" | "d" | "e" | "f" . digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" . flag = "F" | "S" | "R" | "P" | "A" | "U" .
この文法は、可読性のためにいくぶん簡略化しています。 この文法にマッチする組み合わせであっても、 意味をなさないためにソフトウェアが許可しないものがあります (非 TCP パケットに対する tcp flags など)。
「最短」かつ有効なルールは (現在のところ) 無動作と次の形式です:
block in all pass in all log out all count in all
フィルタルールは順番通りにチェックされ、 最後にマッチしたルールがパケットの運命を決めます (例外: 後述 quick オプションを参照)。
デフォルトでは、 フィルタはカーネルのフィルタリストの最後にインストールされます。 ルールの前に @n を付けると、 現在のリストの n 番目のエントリとして挿入するようになります。 これは、現在有効なフィルタのルールセットを修正したりテストする場合に有用です。 更なる情報は ipf(8) を参照してください。
アクションは、 フィルタルールの残りの部分にパケットがマッチする場合に、 そのパケットをどのように扱うのかを示します。 各ルールは、アクションを 1 つ持つことが「必要です」。 次のアクションが認識されます:
block return-icmp(11) ...
とすると、Type-Of-Service (TOS) ICMP 到達不可エラーを返します。
次の語は in か out のいずれかである必要があります。 カーネル内部を通過するパケットは、内向き (インタフェースにて受信された ばかりで、 カーネルのプロトコル処理部に向って移動している) か、 外向き (プロトコルスタックにより送出または転送され、 インタフェースに向かっている) かのいずれかです。 各フィルタルールが入出力のどちら側に適用されるのかを、 明示的に示す必要があります。
オプションの一覧は短く、事実すべて省略可能です。 オプションが使用されるところでは、ここに示す順序で置かれる必要があります。 次のオプションが現在サポートされています:
この節に記載されているキーワードは、ルールがマッチするか否かを決定するときに、 パケットのどの属性を使用するのかを記述するために使用されます。 以下の汎用属性がマッチングに使用でき、この順序で使用する必要があります:
from と to のキーワードは、 IP アドレス (および省略可能なポート番号) とマッチさせるために使用されます。 送信元と送信先の「両方の」パラメータを指定する必要があります。
IP アドレスの指定方法は、次の 2 つのうちのいずれかです: 数値によるアドレス/マスクまたは、ホスト名 mask ネットマスク。 ホスト名は、hosts ファイルまたは DNS 中 (設定やライブラリに依存します) の有効なホスト名か、ドット付き数値形式です。 ネットワーク指定として特別な記法はありませんが、ネットワーク名は認識されます。 フィルタルールを DNS に依存させると攻撃の余地を導入してしまうので、 勧められません。
ホスト名には特殊な any が許され、0.0.0.0/0 と認識されます (後述のマスク書式参照)。これは全 IP アドレスにマッチします。 "any" だけがマスクを暗黙的に指定しますので、 他の状況では、ホスト名はマスクとともに指定する必要があります。 ホストとマスクに対して "any" を指定できるものの、 この言語においては、意味を持たなくなります。
数値フォーマット "x/y" は、 1 のビットが MSB から開始して y 個連続するマスクの生成を示します。 よって、y の値が 16 である場合には、0xffff0000 になります。 シンボリックな "x mask y" は、 マスク y がドット付き IP 表現、 または 0x12345678 の形式の 16 進数であることを示します。 ビットマスクが示す IP アドレスの全ビットと、 パケットのアドレスとが、厳密にマッチする必要があります; 現在、マッチの意味を反転する方法はありませんし、 ビットマスクにて容易に表現可能ではない IP アドレス範囲にマッチさせる方法もありません (たとえるなら、ここまで実現すると、もはや朝食とは言えないですね)。
送信元と送信先のどちらかまたは両者に port マッチを含む場合、 TCP と UDP のパケットに対してのみ適用されます。 proto マッチパラメータが無い場合、 どちらのプロトコルのパケットも比較されます。 これは、"proto tcp/udp" と等価です。 port の比較を行うときには、 サービス名および数値のポート番号のどちらでも使用できます。 ポートの比較を行う際、数値形式を比較演算子とともに使用したり、 ポート範囲を指定したりできます。 ポートが from オブジェクトの一部として登場する場合、 送信元ポート番号にマッチします。 ポートが to オブジェクトの一部として登場する場合、 送信先ポート番号にマッチします。 更なる情報は使用例を参照してください。
all キーワードは、本質的に、 他のマッチパラメータを伴わない "from any to any" の同義語です。
送信元および送信先のマッチパラメータの後に、次の追加のパラメータを使用可能です:
F - FIN S - SYN R - RST P - PUSH A - ACK U - URG
... flags S # "flags S/AUPRFS" になり、SYN フラグ「のみ」 # が設定されているパケットにマッチします。 ... flags SA # "flags SA/AUPRFSC" になり、SYN および ACK のフラグ # のみが設定されているパケットにマッチします。 ... flags S/SA # SYN-ACK の組のうち、SYN フラグのみが設定されている # パケットにのみマッチします。これは共通の「確立」 # キーワード動作です。"S/SA" は SYN と ACK の組の # 「両方」が設定されているものにはマッチ「しません」 # が、"SFP" にはマッチ「します」。
フィルタルールに設定可能な、最後から 2 番目のパラメータは、 パケットの履歴情報を記録するか否か、およびどのような履歴を保存するかです。 以下の情報を保存できます:
これらにマッチするパケットは素通しし、アクセス制御リストを通しません。
あるルールは、新規グループの頭でありかつ、 非デフォルトグループのメンバであることが可能です (head と group を同一ルール内で同時に使用可能です)。
log アクションまたはオプションにて、パケットのログを行うとき、 パケットのヘッダが ipl パケットロギング擬似デバイスに書き込まれます。 log キーワードの直後に、次の修飾語句を (この順序で) 使用できます:
このデバイスに書き込まれるレコードのフォーマットについては ipl(4) を参照してください。 このログを読み取って整形するには、ipmon(8) を使用します。
quick オプションは次のようなルールに対して都合が良いです:
block in quick from any to any with ipopts
これは、 標準的な長さではないヘッダを持つ (IP オプションを持つ) パケットにマッチし、 この先のルール処理を行わずに、 マッチが発生したこととパケットをブロックすべきことを記録します。
次のような「継続」ルールの解釈により:
block in from any to any port < 6000 pass in from any to any port >= 6000 block in from any to any port > 6003
範囲 6000-6003 が許され、他は許さないように設定できます。 最初のルールの効果よりも、後続ルールが優先することに注意してください。 同じことを行う、他の (容易な) 方法は次の通りです:
block in from any to any port 6000 <> 6003 pass in from any to any port 5999 >< 6004
効果を持たせるためには、 "block" および "pass" の両方をここに書く必要があります。 なぜなら、"block" アクションにマッチしないことが通過を意味するわけではなく、 ルールが効果を持たないことを意味するだけだからです。 ポートが1024未満のものを許すには、次のようなルールを使用します:
pass in quick from any to any port < 1024
これは、最初のブロックの前に置く必要があります。 le0/le1/lo0 からのすべての内向きパケットを処理し、 デフォルトでは内向きの全パケットをブロックする 新規グループを作成するには、次のようにします:
block in all block in quick on le0 all head 100 block in quick on le1 all head 200 block in quick on lo0 all head 300
そして、le0 で ICMP パケットのみを許すには、次のようにします:
pass in proto icmp all group 100
le0 からの内向きパケットのみがグループ 100 で処理されますので、 インタフェース名を再度指定する必要がないことに注意してください。 同様に、次のように TCP などの処理を分解できます:
block in proto tcp all head 110 group 100 pass in from any to any port = 23 group 110
最終行を、グループを使用せずに記述すると、次のようになります:
pass in on le0 proto tcp from any to any port = telnet
"port = telnet" と記述したい場合には、"proto tcp" を指定する必要があることに 注意してください。 なぜなら、 パーザは自己に基づいてルールを解釈し、 指定されたプロトコルによって全サービス/ポート名を修飾するからです。