0. 概要
BSD系では、ioctl()のコールによりipfilter(ipfコマンド)の機能が実装できる。
カーネルバージョンによって実装が異なるので注意。
1. フィルタを有効にする:SIOCFRENB
SIOCFRENBで有効(ipf -E)/無効(ipf -D)の実装ができる。
先に有効にしないと、以降のフィルタ操作はEIO(5):Input/output errorとなる。
unsigned int enable = 1; /* 0で無効 */
int ipf_fd = open(IPL_NAME, O_RDWR);
ioctl(ipf_fd, SIOCFRENB, &enable);
2. フィルタをFLUSHする:SIOCIPFFL
SIOCIPFFLフィルタを削除(ipf -F)できる
int fl = 0; /* 0で全削除。1で確立していないものを削除 */
fl = FR_OUTQUE|FR_INQUE; /* ipf -Fa */
ioctl(ipf_fd, SIOCIPFFL, &fl);
3. フィルタを追加/削除する:SIOCADAFR/SIOCRMAFR
SIOCADAFRで追加、SIOCRMAFRで削除ができる。
以下に設定例を記述する。
ipf.confの以下に該当するフィルタである。
pass in quick from 192.168.11.0/24 to any port=22
pass in quick from any to any port 80:443
block in quick from any to any port all
ipfobj_t obj; /* obj.ipfo_ptr = &fre*/
frentry_t fre; /* fre.fr_ipf = &ipft*/
fripf_t ipft;
/* ipfilter */
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(fre);
obj.ipfo_type = IPFOBJ_FRENTRY;
obj.ipfo_ptr = &fre;
memset(&obj, 0x00, sizeof(obj));
fre.fr_ipf = &ipft; /* 以降で参照されるので、先にセットした方が良い。 */
fre.fr_isc = (struct ipscan *)-1;
fre.fr_loglevel = 0xffff;
fre.fr_type = FR_T_IPF;
fre.fr_dsize = sizeof(fripf_t);
/* 適用するI/Fを限定したい場合 */
/* ipf.conf: on eth0 */
strncpy(fre.fr_ifname, "eth0", LIFNAMSIZ - 1);
/* フィルタ設定 */
memset(&ipft, 0, sizeof(ipft));
/* freを参照しているつもりでもfre.fr_ipfを参照する場合がある。 */
/* fre.fr_ipfがNULLだとSegmentation Faultとなるので注意。 */
fre.fr_sifpidx = -1; /* #define fr_sifpidx fr_ipf->fri_sifpidx */
fre.fr_difpidx = -1; /* #define fr_difpidx fr_ipf->fr_difpidx */
/* フィルタ設定 */
/* 受信:192.168.11.0/24からのSSHのみ受け入れ */
/* ipf.conf: pass in quick */
/* FR_QUICKで最初の一致。*/
/* FR_KEEPSTATEで反対方向の許可(受信に対する応答送信の許可)。 */
fre.fr_flags = FR_PASS | FR_INQUE | FR_QUICK | FR_KEEPSTATE;
fre.fr_v = 4; /* IPv4指定。IPv6の場合は6。 */
fre.fr_ip.fi_v = fre.fr_v;
fre.fr_mip.fi_v = 0xf;
/* IPアドレス範囲。受信パケットのSrcアドレスを指定する。 */
/* ipf.conf: from 192.168.11.0/24 */
inet_pton(AF_INET, "192.168.11.0", &fre.fr_src);
inet_pton(AF_INET, "255.255.255.0", &fre.fr_smsk);
/* ポート。受信パケット中のDstポートを指定する。 */
fre.fr_proto = IPPROTO_TCP;
fre.fr_dcmp = FR_EQUAL;
fre.fr_dport = 22;
ioctl(ipf_fd, SIOCADAFR, &obj); /* 削除はSIOCRMAFR */
/* 送信:特定のポートのみ可 */
fre.fr_flags = FR_PASS | FR_OUTQUE | FR_QUICK;
/* フィルタ構造体を使いまわす場合はクリアを忘れずに */
memset(&fre.fr_src, 0, sizeof(fre.fr_src));
memset(&fre.fr_smsk, 0, sizeof(fre.fr_smsk));
fre.fr_proto = 0;
fre.fr_dcmp = 0;
fre.fr_dport = 0;
/* IPアドレス範囲。全ての送信先。 */
/* ipf.conf: to any */
memset(&fre.fr_dst, 0, sizeof(fre.fr_dst));
memset(&fre.fr_dmsk, 0, sizeof(fre.fr_dmsk));
/* ポート。TCP 80から443への送信可。送信パケット中のDstポートを指定する。 */
/* ipf.conf: port 80:443 */
fre.fr_proto = IPPROTO_TCP;
fre.fr_dcmp = FR_INCRANGE;
fre.fr_dport = 80;
fre.fr_dtop = 443;
ioctl(ipf_fd, SIOCADAFR, &obj); /* 削除はSIOCRMAFR */
/* 上記以外の送受信すべてブロック */
memset(&ipft, 0, sizeof(ipft));
fre.fr_flags = FR_BLOCK | FR_INQUE | FR_QUICK;
ioctl(ipf_fd, SIOCADAFR, &obj);
fre.fr_flags = FR_BLOCK | FR_OUTQUE| FR_QUICK;
ioctl(ipf_fd, SIOCADAFR, &obj);
4. キーワード
ipf(4) : netinet/ip_fil.hの内容が記載。ioctlの定義や関連定義が記載されている。
ipf(5) : ipf.confの記述法について。
man ipf : ipfコマンドについて。