CentOS7

CentOS7インストール St.2 - Firewalld

 CATEGORY LINUX

CentOS7インストール St.1はコチラ。

CONTENT

1.Firewalld構築

CentOS7から実装されたFirewalldでFirewallを構築していきます。

IPV4/IPV6の国別IPを精製し、特定の国をブロックするFirewallを作ります。

今回はブラックリストDROP前提で書いていますが、ホワイトリストACCEPTにJPを設定して日本のみ接続可能なFirewalldを構築することも可能です。
ですが、GoogleBOTのIPが非公開の為ブラックリストのFirewalldで良いかと思います。

■Google  https://support.google.com/webmasters/answer/80553?hl=ja

ファイル・スクリプト

ディレクトリ/root/firewalldに、メインのシェルスクリプトと、
国別ipリストを精製するphpを5ファイル作成します。
[firewall.sh]
[ipv4_cidr_client_01.php]
[ipv4_cidr_client_02.php]
[ipv4_cidr_client_03.php]
[ipv6_cidr_client_01.php]
[ipv6_cidr_client_02.php]

[ ipv4_cidr_client_01.php ]で、wgetコマンドを使用してapnicから世界のipアドレスを取得し他のphpで整頓し精製してくれています。これはOXYNOTEさんのをほぼそのまま使用させて頂いております。

参考元サイト : OXYNOTE 自前の国別IPv6、IPv4アドレス割当リストを作成しよう

Firewallコマンド一覧はこちら。

#firewalldディレクトリ作成
mkdir /root/firewalld/

#phpインストール
yum install php php-mbstring

# wgetインストール
yum install wget

# firewalld.sh
vi /root/firewalld/firewalld.sh

firewalld.sh

ipアドレス・メールアドレスは任意変更。 メールアドレスはSt1で設定したメールアドレスに変更してください。

#!/bin/bash
#-------------------------------------------------------------------------------#
#  firewalld設定用シェルスクリプト - 国別ipリストも精製する。
#  管理者用のzoneは[manage]でdrop、固定ip[192.168.1.3]のみアクセス許可。
# 公開用のzoneは[public]でdefault、国別ipsetを作成しdrop。
#  1.国別ipリスト
#  ipv4_cidr_client_01~03.phpでipv4_cidr.txtを精製
#  ipv6_cidr_client_01~02.phpでipv6_cidr.txtを精製
#  ACCEPT_COUNTRY関数・DROP_COUNTRY関数でipリストを作成し、ipsetを作成して各設定に使用する。
#  ※※※固定ipアドレス[192.168.1.3]・このスクリプトとipv4_cidr_client_03のipv6_cidr_client_02メールアドレスを変更する事。※※
#-------------------------------------------------------------------------------#

#-------------------------------------------------------------------------------#
#  1. ipv4_cidr.txt/ipv6_cidr.txtを精製
#-------------------------------------------------------------------------------#
php /root/firewalld/ipv4_cidr_client_01.php
php /root/firewalld/ipv4_cidr_client_02.php
php /root/firewalld/ipv4_cidr_client_03.php
php /root/firewalld/ipv6_cidr_client_01.php
php /root/firewalld/ipv6_cidr_client_02.php

#-------------------------------------------------------------------------------#
#  2. DROP_COUNTRY関数
#  ipv4_cidr.txt/ipv6_cidr.txtから[drop_country_ipv4][drop_country_ipv6]を精製。
#
#  ipv4_cidr.txtのDROP対象国[$country]から始まる行を、
#  コマンドawk(オプションの[-F]無しだと、区切り文字タブorスペースになる)で
#  $2(2フィールド目)のipを出力してdrop_country_ipv4に追記していく。
#  国を追加したい場合はCOUNTRYLISTにスペースを入れて国名を入力する。
#-------------------------------------------------------------------------------#
#  ファイルが無ければ生成
if [ ! -e /root/firewalld/drop_country_ipv4 ];then
  touch /root/firewalld/drop_country_ipv4
fi
if [ ! -e /root/firewalld/drop_country_ipv6 ];then
  touch /root/firewalld/drop_country_ipv6
fi

# ipv4
DROP_COUNTRY_IPV4(){
  COUNTRYLIST='CN' #変更すること。
  for country in $COUNTRYLIST
  do
  	for ip in ` cat ipv4_cidr.txt | grep ^$country | awk '{print $2}'`
  	do
      echo "$ip" >> /root/firewalld/drop_country_ipv4
  	done
  done
}

# ipv6
DROP_COUNTRY_IPV6(){
  COUNTRYLIST='CN' #変更すること。
  for country in $COUNTRYLIST
  do
  	for ip in ` cat ipv6_cidr.txt | grep ^$country | awk '{print $2}'`
  	do
      echo "$ip" >> /root/firewalld/drop_country_ipv6
  	done
  done
}
#-------------------------------------------------------------------------------#
#  3. 2で作成した関数を実行
#-------------------------------------------------------------------------------#
DROP_COUNTRY_IPV4
DROP_COUNTRY_IPV6

#-------------------------------------------------------------------------------#
#  4. 3で作成したipsetを読み込む
#-------------------------------------------------------------------------------#

# IPV4--------------------------------------------------------------------------------#

# ipset[ drop_country_set_ipv4 ] をdeleteして初期化。
firewall-cmd --permanent --delete-ipset=drop_country_set_ipv4 --type=hash:net

# ipset[ drop_country_set_ipv4 ] を作成して type を hash:net にする。
firewall-cmd --permanent --new-ipset=drop_country_set_ipv4 --type=hash:net

# ipset[ drop_country_set_ipv4 ] に [drop_country_ipv4] を読み込む。
firewall-cmd --permanent --ipset=drop_country_set_ipv4 --add-entries-from-file=/root/firewalld/drop_country_ipv4

# ipset[ drop_country_set_ipv4 ] のルールをremoveして初期化。
firewall-cmd --permanent --zone=public --remove-rich-rule='rule family=ipv4 source ipset=drop_country_set_ipv4 drop'

# ipset[ drop_country_set_ipv4 ] のipは zone[ public ] に来た場合ドロップされる
firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source ipset=drop_country_set_ipv4 drop'

# IPV6--------------------------------------------------------------------------------#

# ipset[ drop_country_set_ipv6 ] をdeleteして初期化。
firewall-cmd --permanent --delete-ipset=drop_country_set_ipv6 --option=family=inet6 --type=hash:net

# ipset[ drop_country_set_ipv6] を作成して type を hash:net にする。
firewall-cmd --permanent --new-ipset=drop_country_set_ipv6 --option=family=inet6 --type=hash:net

# ipset[ drop_country_set_ipv6 ] に [drop_country_ipv4] を読み込む。
firewall-cmd --permanent --ipset=drop_country_set_ipv6 --add-entries-from-file=/root/firewalld/drop_country_ipv6

# ipset[ drop_country_set_ipv6 ] のルールをremoveして初期化。
firewall-cmd --permanent --zone=public --remove-rich-rule='rule family=ipv6 source ipset=drop_country_set_ipv6 drop'

# ipset[ drop_country_set_ipv6 ] のipは zone[ public ] に来た場合ドロップされる
firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv6 source ipset=drop_country_set_ipv6 drop'

#-------------------------------------------------------------------------------#
#  5. 管理者用zone[manage]
#-------------------------------------------------------------------------------#
# 管理者用のゾーン[manage]をdeleteして初期化。
firewall-cmd --permanent --delete-zone=manage

# 管理者用のゾーン[manage]を作成。
firewall-cmd --permanent --new-zone=manage

# ゾーン[manage]の接続許可を[DROP]に。
firewall-cmd --permanent --zone=manage --set-target=DROP

# ゾーン[manage]の接続許可ipを指定。
firewall-cmd --permanent --zone=manage --add-source=192.168.1.3

# ゾーン[public]の初期設定サービスをremoveして初期化。
firewall-cmd --permanent --zone=public --remove-service=ssh
firewall-cmd --permanent --zone=public --remove-service=ftp
firewall-cmd --permanent --zone=public --remove-service=smtp
firewall-cmd --permanent --zone=public --remove-service=smtps

# ゾーン[manage]で利用するサービス[ssh]を登録する。
firewall-cmd --permanent --zone=manage --add-service=ssh
firewall-cmd --permanent --zone=manage --add-service=ftp
firewall-cmd --permanent --zone=manage --add-service=smtp
firewall-cmd --permanent --zone=manage --add-service=smtps

#-------------------------------------------------------------------------------#
#  6. 公開zone[public]
#-------------------------------------------------------------------------------#
firewall-cmd --permanent --zone=public --add-service=http

#-------------------------------------------------------------------------------#
#  7.  ダイレクトルール/チェインを初期化。
#-------------------------------------------------------------------------------#
(
IFS=$'\n';
RULES=(`firewall-cmd --direct --get-all-rules`)
for rule in ${RULES[@]}; do
    (
    IFS=$' '
    CMD="firewall-cmd --permanent --direct --remove-rule $rule"
    eval $CMD
    )
done
)
(
IFS=$'\n';
CHAINS=(`firewall-cmd --direct --get-all-chains`)
for chain in ${CHAINS[@]}; do
    (
    IFS=$' '
    CMD="firewall-cmd --permanent --direct --remove-chain $chain"
    eval $CMD
    )
done
)

#-------------------------------------------------------------------------------#
#  8 .ダイレクトルール/チェインを記述
#-------------------------------------------------------------------------------#
# データを持たないパケットの接続を破棄する
firewall-cmd  --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --tcp-flags ALL NONE -j DROP

# SYNflood攻撃と思われる接続を破棄する
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp ! --syn -m state --state NEW -j DROP

# ステルススキャンと思われる接続を破棄する
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --tcp-flags ALL ALL -j DROP

# ブロードキャストアドレスを破棄する
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m pkttype --pkt-type broadcast -j DROP

# マルチキャストアドレスを破棄する
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -m pkttype --pkt-type multicast -j DROP

#ポートスキャン 1秒間に5回を超えるSYNパケットはログに記録してDROP。
firewall-cmd --permanent --direct --add-chain ipv4 filter Port_Scan
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 400 -i ens33 -p tcp --tcp-flags SYN,ACK,FIN,RST SYN -j Port_Scan
firewall-cmd --permanent --direct --add-rule ipv4 filter Port_Scan 450 -m limit --limit 1/s --limit-burst 4 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 filter Port_Scan 451 -j LOG --log-prefix "[CyberAttack_Port_Scan]:"
firewall-cmd --permanent --direct --add-rule ipv4 filter Port_Scan 452 -j DROP

#Ping of Death 1秒間に4回を超えるpingはログに記録してDROP。
firewall-cmd --permanent --direct --add-chain ipv4 filter Ping_of_Death
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p icmp --icmp-type echo-request -j Ping_of_Death
firewall-cmd --permanent --direct --add-rule ipv4 filter Ping_of_Death 1 -m limit --limit 1/s --limit-burst 4 -j RETURN
firewall-cmd --permanent --direct --add-rule ipv4 filter Ping_of_Death 1 -j LOG --log-prefix "[CyberAttack_Ping_Of_Death]:"
firewall-cmd --permanent --direct --add-rule ipv4 filter Ping_of_Death 1 -j DROP

#-------------------------------------------------------------------------------#
#  9 .ログ unicast broadcast multicast off
#  allだとbroadcastやmulticastのログも記録される
#-------------------------------------------------------------------------------#
firewall-cmd --set-log-denied=unicast

#-------------------------------------------------------------------------------#
#  10 .リロード
#-------------------------------------------------------------------------------#
# 設定反映
firewall-cmd --reload

#-------------------------------------------------------------------------------#
#  11 .完了通知
#-------------------------------------------------------------------------------#
# 設定確認
SETFILE1=$(firewall-cmd --list-all --zone=manage)
SETFILE2=$(firewall-cmd --list-all --zone=public)

# 完了メール送信
address="メールアドレス@メールアドレス"
subject="firewalld更新完了"
contents="firewalldの更新が完了しました。"
echo "$contents $SETFILE1 $SETFILE2" | mail -s "$subject" "$address"


ipv4_cidr_client_01.php

<?php
/*
 * 1.IPのリストを取得
 *
 * 2.後処理のためにIPアドレスを長整数表現に変換し
 * 国別コードと合わせて書き出す
 *
*/




define('TEMP_PATH', '/root/firewalld');
define('CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr_01.txt');




// リストのダウンロード
passthru( 'wget -qO ' . TEMP_PATH . '/delegated-arin-extended-latest ftp://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest' );
passthru( 'wget -qO ' . TEMP_PATH . '/delegated-ripencc-extended-latest ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest' );
passthru( 'wget -qO ' . TEMP_PATH . '/delegated-apnic-extended-latest ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-extended-latest' );
passthru( 'wget -qO ' . TEMP_PATH . '/delegated-lacnic-extended-latest ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest' );
passthru( 'wget -qO ' . TEMP_PATH . '/delegated-afrinic-extended-latest ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest' );




$wfp = fopen( CIDR_LIST_PATH, 'w' );

// ダウンロードしたファイルを全て回す
foreach ( glob( TEMP_PATH . '/delegated-*-extended-latest' ) as $filename ) {

     // ファイルを読み込み空行と行末の改行を飛ばす
     $lists = new SplFileObject( $filename );
     $lists->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     foreach ( $lists as $line ) {
          if ( preg_match( '/(arin|ripencc|apnic|lacnic|afrinic)\|[A-Z]+\|ipv4/', $line ) ) {
               $rows = explode('|', $line);
               $country = $rows[1]; // 国別コード取得
               $ipMin = ip2long( $rows[3] ); // IPの一番下を指定。後に使う関数用にip2longでIPアドレスを整数型へ変換
               $ipMax = $ipMin + $rows[4] - 1; // IPの一番上を指定。上のIPに範囲を足して1引いたもの

               fwrite( $wfp, $country . "\t" . $ipMin . "\t" . $ipMax . "\n" );
          }
     }
}

fclose( $wfp );

ipv4_cidr_client_02.php

<?php
/**
 * 1.リストをソートし国とIPが連続する場合結合
 *
 * 2.IPがCIDR形式(サブネットマスク形式)で割り切れない場合、
 * CIDR形式で表現できるように分割するスクリプト
 *
 */




define( 'TEMP_PATH', '/root/firewalld' );
define( 'OLD_CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr_01.txt' );
define( 'CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr_02.txt' ); // 書き出すIPリスト




// IPリストをソートしてキーを振り直す
$lists = new SplFileObject(OLD_CIDR_LIST_PATH);
$lists->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

$arr = array();

foreach( $lists as $val ){
     if ($val === false) continue;
     $arr[] = $val;
}
asort($arr);
$arr = array_merge($arr);



// 国の名前が同じで、現在のip_maxと次のip_minが連続している場合結合する
$i = 0;

foreach ( $arr as $key => &$val ) {

     if ( isset( $arr[$i + 1] ) ){
          $j = explode( "\t", $val);
          $k = explode( "\t", $arr[$i + 1]);
          if (
               $j[0] == $k[0] &&
               $j[2] == $k[1] - 1
          ){
               $arr[ $i + 1 ] = $j[0] . "\t" . $j[1] . "\t" . $k[2];
               unset( $arr[$i] );
          }
     }
     $i++;
}

unset($val); // 参照渡しのリセット




// $cidr_rangesにCIDR形式で使われる4から2147483648の倍数をセット
// 参照:https://note.cman.jp/network/subnetmask.cgi
$range = array();

for( $i = 0; $i < 30; $i++ ){
     $ranges[] = pow( 2, $i + 2 );
}

$cidr_ranges = array_reverse( $ranges );



/**
 * IPの範囲を調べる
 * ip_range_cidr_splitへIPの範囲を渡してCIDR形式に分割し
 * 新たなファイルに書き出す
 */
$wfp = fopen( CIDR_LIST_PATH, 'w' );

foreach( $arr as $key => $val ){

     // IPのrangeの取得
     if( ! empty( $val ) ){
          $j = explode( "\t", $val );
          $country = $j[0];
          $ip_min = $j[1];
          $ip_max = $j[2];
          $ip_range = $ip_max - $ip_min + 1;

          $split_lists = ip_range_cidr_split( $ip_range, $cidr_ranges );

          foreach ( $split_lists as $row ) {
               $ip_max = $ip_min + $row - 1;
               fwrite( $wfp, $country . "\t" . $ip_min . "\t" . $ip_max . "\n" );
               $ip_min = $ip_min + $row;
          }
     }
}

fclose( $wfp );




/**
 * IPの範囲とCIDR表記で割り切れる値を渡すと
 * CIDRで表現できるIPの範囲を配列で返す
 *
 * @param int       IPの範囲
 * @param arr       CIDR表記で割り切れる値
 * @return     arr       CIDRで表現できるIPの範囲を配列で返す
 */
function ip_range_cidr_split( $ip_range, $cidr_ranges ) {

     $split_lists = array();

     foreach( $cidr_ranges as $cidr_range ){
          // $rangeで引いて、残りをもう一度この関数で処理(割り切れるまで実行)
          if( $ip_range == $cidr_range ){ // 割り切れる場合は処理終了
               $split_lists[] = $ip_range;
               return $split_lists;
          } elseif ( $ip_range > $cidr_range ){
               $split_lists[] = $cidr_range;
               $ip_range = $ip_range - $cidr_range;
          }
     }
     return $split_lists;
}

ipv4_cidr_client_03.php

<?php
/**
 * 1.IPをCIDR表記に変換する
 *
 * 2.リストの差分がしきい値を超えた場合はリストを破棄
 *
 * 3.リスト作成の成否をメールで通知する
 *
 */




define( 'TEMP_PATH', '/root/firewalld' );
define( 'OLD_CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr_02.txt' );
define( 'TMP_CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr_03.txt' );
define( 'CIDR_LIST_PATH', TEMP_PATH . '/ipv4_cidr.txt' );




$lists = new SplFileObject(OLD_CIDR_LIST_PATH);
$lists->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

/**
 * IPをCIDR形式に変換するスクリプトに渡して
 * ファイルへ書き出す
 */
$wfp = fopen( TMP_CIDR_LIST_PATH, 'w' );

foreach( $lists as $list ){
     // IPのrangeの取得
     $j = explode( "\t", $list );
     $country = $j[0];
     $ip_min = $j[1];
     $ip_max = $j[2];

     $split_lists = PlageVersCIDRs( $ip_min, $ip_max );

     foreach ( $split_lists as $row ) {
          fwrite( $wfp, $country . "\t" . $row . "\n" );
     }
}

fclose( $wfp );




/**
 * 保存済みの /tmp/ipv4_cidr.txt にあって
 * 作成した /tmp/ipv4_cidr_03.txt にないものをカウント
 *
 * 変更点が多すぎる場合は
 * ファイルが正常に取得できていないものとみなし更新しない
 * 検査自体は1/2のみで、500の差分がある場合失敗(全体換算で1000)
 *
 * そしてメールで通知する
 */
if( is_readable( CIDR_LIST_PATH ) ) {

     $new_file = new SplFileObject( TMP_CIDR_LIST_PATH );
     $new_file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     $old_file = new SplFileObject( CIDR_LIST_PATH );
     $old_file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     // 保存されたファイルの1/2の行数を取得
     $old_file->seek( $old_file->getSize() );
     $lines_total = $old_file->key();
     $lines_total = $lines_total / 2;

     $i = 0;
     foreach( $new_file as $val ){
          if( $lines_total < $i ){
               break;
          }
          $new_data[] = $val;
          $i++;
     }

     $i = 0;
     foreach( $old_file as $val ){
          if( $lines_total < $i ){
               break;
          }
          $old_data[] = $val;
          $i++;
     }

     $diff = count( array_diff( $new_data, $old_data ) );
     $diff2 = count( array_diff( $old_data, $new_data ) );

     // 差分が500以下のときに成功、それ以上のときに失敗コピーしない
     if ( $diff < 500 && $diff2 < 500  ){
          send_mail( "成功", $diff + $diff2 );
          copy( TMP_CIDR_LIST_PATH, CIDR_LIST_PATH );
     } else {
          send_mail( "失敗", $diff + $diff2 );
     }
} else {
     send_mail( "新規作成成功", 0 );
     copy( TMP_CIDR_LIST_PATH, CIDR_LIST_PATH );
}




/**
 * メール送信用
 * 成否と差分を記載したメールを送信する
 *
 * @param string    成功もしくは失敗
 * @param int       差分の数値
 */
function send_mail( $flag, $diff ){

     mb_language("Japanese");
     mb_internal_encoding("UTF-8");

     if ( mb_send_mail( "メールアドレス@メールアドレス",
     "IPv4のCIDRリスト作成に「" . $flag . "」しました",
     "CIDRの作成:" . $flag . "。\nCIDRの差分:" . $diff . "。",
     "From: メールアドレス@メールアドレス" ) ){
     } else {
          echo "メールの送信に失敗しました。";
     }
}




/**
 * IPの開始と終了の範囲を渡すCIDR形式(サブネット形式)で返す
 * オリジナルのものはCIDRで表現できない端数の出る値を丸めていた
 * 参照:http://php.net/manual/ja/ref.network.php#75922
 *
 * 一つ前の処理でCIDRで表現可能な数値に分割しているため
 * オリジナルのものはCIDRで表現できない端数の出る値を丸めていたが
 * 正確な値で分割が可能となっている
 *
 * @param int       IPの開始点
 * @param arr       IPの終了点
 * @return     arr       CIDR形式で返す
 */
function PlageVersCIDRs($ip_min, $ip_max) {
     $cidrs = array();
     $ip_min_bin = sprintf('%032b', $ip_min);
     $ip_max_bin = sprintf('%032b', $ip_max);
     $ip_cour_bin = $ip_min_bin;
     while (strcmp($ip_cour_bin, $ip_max_bin) <= 0) {
          $lng_reseau = 32;
          $ip_reseau_bin = $ip_cour_bin;
          while (($ip_cour_bin[$lng_reseau - 1] == '0') && (strcmp(substr_replace($ip_reseau_bin, '1', $lng_reseau - 1, 1), $ip_max_bin) <= 0)) {
               $ip_reseau_bin[$lng_reseau - 1] = '1';
               $lng_reseau--;
          }
          $cidrs[] = long2ip(bindec($ip_cour_bin)).'/'.$lng_reseau;
          $ip_cour_bin = sprintf('%032b', bindec($ip_reseau_bin) + 1);
     }
     return $cidrs;
}

ipv6_cidr_client_01.php

<?php
/*
 * IPv6のCIDR形式の割当リストを作成する
 * IPv4で取得したリストを利用する
 *
*/




define('TEMP_PATH', '/root/firewalld');
define('CIDR_FILTER_PATH', TEMP_PATH . '/ipv6_cidr_01.txt');




$wfp = fopen( CIDR_FILTER_PATH, 'w' );

// ダウンロードしたファイルを全て回す
foreach( glob( TEMP_PATH.'/delegated-*-extended-latest' ) as $filename ) {

     $lists = new SplFileObject( $filename );
     $lists->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     foreach ( $lists as $line ) {

          if( preg_match( '/(arin|ripencc|apnic|lacnic|afrinic)\|[A-Z]+\|ipv6/', $line ) ) {
               $rows = explode( '|', $line );

               $country = $rows[1]; // 国別コード取得
               $ip = $rows[3]; // IPの一番下を指定。後に使う関数用にip2longでIPアドレスを整数型へ変換
               $mask = $rows[4]; // IPの一番上を指定。上のIPに範囲を足して1引いたもの

               fwrite( $wfp, $country . "\t" . $ip . "/" . $mask ."\n" );
          }
     }
}
fclose($wfp);

ipv6_cidr_client_02.php

<?php
/*
 * 1.リストをソートし使える形式で書き出す
 *
 * var    1.0.0     2017/5/11
*/




define( 'TEMP_PATH', '/root/firewalld' );
define( 'OLD_CIDR_LIST_PATH', TEMP_PATH . '/ipv6_cidr_01.txt' );
define( 'TMP_CIDR_LIST_PATH', TEMP_PATH . '/ipv6_cidr_02.txt' );
define( 'CIDR_LIST_PATH', TEMP_PATH . '/ipv6_cidr.txt' ); // 書き出すIPリスト




// IPリストをソートしてキーを振り直す
$lists = new SplFileObject(OLD_CIDR_LIST_PATH);
$lists->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

foreach( $lists as $val ){
     if ($val === false) continue;
     $arr[] = $val;
}
asort($arr);

$wfp = fopen( TMP_CIDR_LIST_PATH, 'w' );

foreach( $arr as $line ){
     fwrite( $wfp, $line ."\n" );
}

fclose( $wfp );




/**
 * 保存済みの /tmp/ipv6_cidr.txt にあって
 * 作成した /tmp/ipv6_cidr_02.txt の差分を調べる
 *
 * 変更点が多すぎる場合は
 * ファイルが正常に取得できていないものとみなし更新しない
 *
 * そしてメールで通知する
 */
if( is_readable( CIDR_LIST_PATH ) ) {

     $new_file = new SplFileObject( TMP_CIDR_LIST_PATH );
     $new_file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     $old_file = new SplFileObject( CIDR_LIST_PATH );
     $old_file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);

     foreach( $new_file as $val ){
          $new_data[] = $val;
     }

     foreach( $old_file as $val ){
          $old_data[] = $val;
     }

     $diff = count( array_diff( $new_data, $old_data ) );
     $diff2 = count( array_diff( $old_data, $new_data ) );

     // 差分が1000以下のときに成功、それ以上のときに失敗コピーしない
     if ( $diff < 1000 && $diff2 < 1000  ){
          send_mail( "成功", $diff + $diff2 );
          copy( TMP_CIDR_LIST_PATH, CIDR_LIST_PATH );
     } else {
          send_mail( "失敗", $diff + $diff2 );
     }
} else {
     send_mail( "新規作成成功", 0 );
     copy( TMP_CIDR_LIST_PATH, CIDR_LIST_PATH );
}




/**
 * メール送信用
 * 成否と差分を記載したメールを送信する
 *
 * @param string    成功もしくは失敗
 * @param int       差分の数値
 */
function send_mail( $flag, $diff ){

     mb_language("Japanese");
     mb_internal_encoding("UTF-8");

     if ( mb_send_mail( "メールアドレス@メールアドレス",
     "IPv6のCIDRリスト作成に「" . $flag . "」しました",
     "CIDRの作成:" . $flag . "。\nCIDRの差分:" . $diff . "。",
     "From: メールアドレス@メールアドレス" ) ){
     } else {
          echo "メールの送信に失敗しました。";
     }
}

NEXT
次の投稿