ipfilter 実装

#0. 概要
BSD系では、ioctl()のコールによりipfilter(ipfコマンド)の機能が実装できる。
カーネルバージョンによって実装が異なるので注意。

 1. [フィルタを有効/無効する:SIOCFRENB](https://qiita.com/nhadachi/items/12969e9b389e527a8b89#1-%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8Bsiocfrenb)
 2. [フィルタをFLUSHする:SIOCIPFFL](https://qiita.com/nhadachi/items/12969e9b389e527a8b89#2-%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%82%92flush%E3%81%99%E3%82%8Bsiocipffl) 
 3. [フィルタを追加/削除する:SIOCADAFR](https://qiita.com/nhadachi/items/12969e9b389e527a8b89#3-%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%82%92%E8%BF%BD%E5%8A%A0%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8Bsiocadafrsiocrmafr)
 4. [キーワード](https://qiita.com/nhadachi/items/12969e9b389e527a8b89#4-%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89)

#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コマンドについて。