* tar xzf utvpn-src-unix-v101-7101-public-2010.06.27.tar.gz
[lab.git] / utvpn / utvpn-unix-v101-7101-public / src / Cedar / Hub.c
diff --git a/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.c b/utvpn/utvpn-unix-v101-7101-public/src/Cedar/Hub.c
new file mode 100644 (file)
index 0000000..0717a4c
--- /dev/null
@@ -0,0 +1,5840 @@
+// SoftEther UT-VPN SourceCode\r
+// \r
+// Copyright (C) 2004-2010 SoftEther Corporation.\r
+// Copyright (C) 2004-2010 University of Tsukuba, Japan.\r
+// Copyright (C) 2003-2010 Daiyuu Nobori.\r
+// All Rights Reserved.\r
+// \r
+// http://utvpn.tsukuba.ac.jp/\r
+// \r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// version 2 as published by the Free Software Foundation.\r
+// \r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+// \r
+// You should have received a copy of the GNU General Public License version 2\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+// \r
+// このファイルは GPL バージョン 2 ライセンスで公開されています。\r
+// 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布\r
+// することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示\r
+// を除去することはできません。改変した著作物を配布する場合は、改変実施者の\r
+// 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。\r
+// \r
+// この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の\r
+// ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )\r
+// および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって\r
+// ホストされています。\r
+// 本プログラムの配布者は、本プログラムを、業としての利用以外のため、\r
+// および、試験または研究のために利用が行われることを想定して配布\r
+// しています。\r
+// SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に\r
+// あります。\r
+// 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード\r
+// の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して\r
+// いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して\r
+// ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース\r
+// に組み込みさせていただきます。\r
+// \r
+// GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する\r
+// 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。\r
+// \r
+// 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社\r
+// (SoftEther Corporation) およびその他の著作権保持者が保有しています。\r
+// ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの\r
+// 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意\r
+// ください。\r
+// \r
+// お願い: どのような通信ソフトウェアにも通常は必ず未発見の\r
+// セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、\r
+// UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの\r
+// 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社\r
+// および脆弱性情報の届出を受け付ける公的機関まで通報いただき、\r
+// 公益保護にご協力いただきますようお願い申し上げます。\r
+// \r
+// ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を\r
+// 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客\r
+// を保護するための努力を行います。\r
+// \r
+// ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/\r
+// 日本国内の脆弱性情報届出受付公的機関:\r
+//         独立行政法人 情報処理推進機構\r
+//         http://www.ipa.go.jp/security/vuln/report/\r
+// \r
+// 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。\r
+// 連絡先: http://www.softether.co.jp/jp/contact/\r
+\r
+// -----------------------------------------------\r
+// [ChangeLog]\r
+// 2010.05.20\r
+//  新規リリース by SoftEther\r
+// -----------------------------------------------\r
+\r
+// Hub.c\r
+// 仮想 HUB モジュール\r
+\r
+#include "CedarPch.h"\r
+\r
+static UCHAR broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
+\r
+// 現在サポートされている管理オプションの一覧とデフォルト値\r
+// 名前は 63 文字以内にすること\r
+ADMIN_OPTION admin_options[] =\r
+{\r
+       {"allow_hub_admin_change_option", 0},\r
+       {"max_users", 0},\r
+       {"max_multilogins_per_user", 0},\r
+       {"max_groups", 0},\r
+       {"max_accesslists", 0},\r
+       {"max_sessions_client_bridge_apply", 0},\r
+       {"max_sessions", 0},\r
+       {"max_sessions_client", 0},\r
+       {"max_sessions_bridge", 0},\r
+       {"max_bitrates_download", 0},\r
+       {"max_bitrates_upload", 0},\r
+       {"deny_empty_password", 0},\r
+       {"deny_bridge", 0},\r
+       {"deny_routing", 0},\r
+       {"deny_qos", 0},\r
+       {"deny_change_user_password", 0},\r
+       {"no_change_users", 0},\r
+       {"no_change_groups", 0},\r
+       {"no_securenat", 0},\r
+       {"no_securenat_enablenat", 0},\r
+       {"no_securenat_enabledhcp", 0},\r
+       {"no_cascade", 0},\r
+       {"no_online", 0},\r
+       {"no_offline", 0},\r
+       {"no_change_log_config", 0},\r
+       {"no_disconnect_session", 0},\r
+       {"no_delete_iptable", 0},\r
+       {"no_delete_mactable", 0},\r
+       {"no_enum_session", 0},\r
+       {"no_query_session", 0},\r
+       {"no_change_admin_password", 0},\r
+       {"no_change_log_switch_type", 0},\r
+       {"no_change_access_list", 0},\r
+       {"no_change_access_control_list", 0},\r
+       {"no_change_cert_list", 0},\r
+       {"no_change_crl_list", 0},\r
+       {"no_read_log_file", 0},\r
+       {"deny_hub_admin_change_ext_option", 0},\r
+       {"no_delay_jitter_packet_loss", 0},\r
+       {"no_change_msg", 0},\r
+};\r
+\r
+UINT num_admin_options = sizeof(admin_options) / sizeof(ADMIN_OPTION);\r
+\r
+// 指定されたメッセージが URL 文字列かどうか取得\r
+bool IsURLMsg(wchar_t *str, char *url, UINT url_size)\r
+{\r
+       UNI_TOKEN_LIST *t;\r
+       bool ret = false;\r
+       UINT i;\r
+       UINT n = 0;\r
+       // 引数チェック\r
+       if (str == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       t = UniParseToken(str, L"\r\n");\r
+\r
+       for (i = 0;i < t->NumTokens;i++)\r
+       {\r
+               wchar_t *str = t->Token[i];\r
+\r
+               if (IsEmptyUniStr(str) == false)\r
+               {\r
+                       n++;\r
+\r
+                       UniTrim(str);\r
+\r
+                       if (n == 1)\r
+                       {\r
+                               if (UniStartWith(str, L"http://") ||\r
+                                       UniStartWith(str, L"https://") ||\r
+                                       UniStartWith(str, L"ftp://"))\r
+                               {\r
+                                       ret = true;\r
+\r
+                                       UniToStr(url, url_size, str);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (n != 1)\r
+       {\r
+               ret = false;\r
+       }\r
+\r
+       UniFreeToken(t);\r
+\r
+       return ret;\r
+}\r
+\r
+// RPC_ADMIN_OPTION からデータを取得\r
+UINT GetHubAdminOptionData(RPC_ADMIN_OPTION *ao, char *name)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (ao == NULL || name == NULL)\r
+       {\r
+               return INFINITE;\r
+       }\r
+\r
+       for (i = 0;i < ao->NumItem;i++)\r
+       {\r
+               ADMIN_OPTION *a = &ao->Items[i];\r
+\r
+               if (StrCmpi(a->Name, name) == 0)\r
+               {\r
+                       return a->Value;\r
+               }\r
+       }\r
+\r
+       return INFINITE;\r
+}\r
+void GetHubAdminOptionDataAndSet(RPC_ADMIN_OPTION *ao, char *name, UINT *dest)\r
+{\r
+       UINT value;\r
+       // 引数チェック\r
+       if (ao == NULL || name == NULL || dest == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       value = GetHubAdminOptionData(ao, name);\r
+       if (value == INFINITE)\r
+       {\r
+               return;\r
+       }\r
+\r
+       *dest = value;\r
+}\r
+\r
+// データをもとに HUB_OPTION の内容を設定\r
+void DataToHubOptionStruct(HUB_OPTION *o, RPC_ADMIN_OPTION *ao)\r
+{\r
+       // 引数チェック\r
+       if (o == NULL || ao == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       GetHubAdminOptionDataAndSet(ao, "NoAddressPollingIPv4", &o->NoArpPolling);\r
+       GetHubAdminOptionDataAndSet(ao, "NoAddressPollingIPv6", &o->NoIPv6AddrPolling);\r
+       GetHubAdminOptionDataAndSet(ao, "NoIpTable", &o->NoIpTable);\r
+       GetHubAdminOptionDataAndSet(ao, "NoMacAddressLog", &o->NoMacAddressLog);\r
+       GetHubAdminOptionDataAndSet(ao, "ManageOnlyPrivateIP", &o->ManageOnlyPrivateIP);\r
+       GetHubAdminOptionDataAndSet(ao, "ManageOnlyLocalUnicastIPv6", &o->ManageOnlyLocalUnicastIPv6);\r
+       GetHubAdminOptionDataAndSet(ao, "DisableIPParsing", &o->DisableIPParsing);\r
+       GetHubAdminOptionDataAndSet(ao, "YieldAfterStorePacket", &o->YieldAfterStorePacket);\r
+       GetHubAdminOptionDataAndSet(ao, "NoSpinLockForPacketDelay", &o->NoSpinLockForPacketDelay);\r
+       GetHubAdminOptionDataAndSet(ao, "BroadcastStormDetectionThreshold", &o->BroadcastStormDetectionThreshold);\r
+       GetHubAdminOptionDataAndSet(ao, "ClientMinimumRequiredBuild", &o->ClientMinimumRequiredBuild);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterPPPoE", &o->FilterPPPoE);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterOSPF", &o->FilterOSPF);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterIPv4", &o->FilterIPv4);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterIPv6", &o->FilterIPv6);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterNonIP", &o->FilterNonIP);\r
+       GetHubAdminOptionDataAndSet(ao, "NoIPv4PacketLog", &o->NoIPv4PacketLog);\r
+       GetHubAdminOptionDataAndSet(ao, "NoIPv6PacketLog", &o->NoIPv6PacketLog);\r
+       GetHubAdminOptionDataAndSet(ao, "FilterBPDU", &o->FilterBPDU);\r
+       GetHubAdminOptionDataAndSet(ao, "NoIPv6DefaultRouterInRAWhenIPv6", &o->NoIPv6DefaultRouterInRAWhenIPv6);\r
+       GetHubAdminOptionDataAndSet(ao, "NoLookBPDUBridgeId", &o->NoLookBPDUBridgeId);\r
+       GetHubAdminOptionDataAndSet(ao, "NoManageVlanId", &o->NoManageVlanId);\r
+       GetHubAdminOptionDataAndSet(ao, "VlanTypeId", &o->VlanTypeId);\r
+       GetHubAdminOptionDataAndSet(ao, "FixForDLinkBPDU", &o->FixForDLinkBPDU);\r
+       GetHubAdminOptionDataAndSet(ao, "RequiredClientId", &o->RequiredClientId);\r
+}\r
+\r
+// HUB_OPTION の内容をデータに変換\r
+void HubOptionStructToData(RPC_ADMIN_OPTION *ao, HUB_OPTION *o, char *hub_name)\r
+{\r
+       LIST *aol;\r
+       UINT i;\r
+       // 引数チェック\r
+       if (ao == NULL || o == NULL || hub_name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       aol = NewListFast(NULL);\r
+\r
+       Add(aol, NewAdminOption("NoAddressPollingIPv4", o->NoArpPolling));\r
+       Add(aol, NewAdminOption("NoAddressPollingIPv6", o->NoIPv6AddrPolling));\r
+       Add(aol, NewAdminOption("NoIpTable", o->NoIpTable));\r
+       Add(aol, NewAdminOption("NoMacAddressLog", o->NoMacAddressLog));\r
+       Add(aol, NewAdminOption("ManageOnlyPrivateIP", o->ManageOnlyPrivateIP));\r
+       Add(aol, NewAdminOption("ManageOnlyLocalUnicastIPv6", o->ManageOnlyLocalUnicastIPv6));\r
+       Add(aol, NewAdminOption("DisableIPParsing", o->DisableIPParsing));\r
+       Add(aol, NewAdminOption("YieldAfterStorePacket", o->YieldAfterStorePacket));\r
+       Add(aol, NewAdminOption("NoSpinLockForPacketDelay", o->NoSpinLockForPacketDelay));\r
+       Add(aol, NewAdminOption("BroadcastStormDetectionThreshold", o->BroadcastStormDetectionThreshold));\r
+       Add(aol, NewAdminOption("ClientMinimumRequiredBuild", o->ClientMinimumRequiredBuild));\r
+       Add(aol, NewAdminOption("FilterPPPoE", o->FilterPPPoE));\r
+       Add(aol, NewAdminOption("FilterOSPF", o->FilterOSPF));\r
+       Add(aol, NewAdminOption("FilterIPv4", o->FilterIPv4));\r
+       Add(aol, NewAdminOption("FilterIPv6", o->FilterIPv6));\r
+       Add(aol, NewAdminOption("FilterNonIP", o->FilterNonIP));\r
+       Add(aol, NewAdminOption("NoIPv4PacketLog", o->NoIPv4PacketLog));\r
+       Add(aol, NewAdminOption("NoIPv6PacketLog", o->NoIPv6PacketLog));\r
+       Add(aol, NewAdminOption("FilterBPDU", o->FilterBPDU));\r
+       Add(aol, NewAdminOption("NoIPv6DefaultRouterInRAWhenIPv6", o->NoIPv6DefaultRouterInRAWhenIPv6));\r
+       Add(aol, NewAdminOption("NoLookBPDUBridgeId", o->NoLookBPDUBridgeId));\r
+       Add(aol, NewAdminOption("NoManageVlanId", o->NoManageVlanId));\r
+       Add(aol, NewAdminOption("VlanTypeId", o->VlanTypeId));\r
+       Add(aol, NewAdminOption("FixForDLinkBPDU", o->FixForDLinkBPDU));\r
+       Add(aol, NewAdminOption("RequiredClientId", o->RequiredClientId));\r
+\r
+       Zero(ao, sizeof(RPC_ADMIN_OPTION));\r
+\r
+       StrCpy(ao->HubName, sizeof(ao->HubName), hub_name);\r
+\r
+       ao->NumItem = LIST_NUM(aol);\r
+       ao->Items = ZeroMalloc(sizeof(ADMIN_OPTION) * ao->NumItem);\r
+\r
+       for (i = 0;i < LIST_NUM(aol);i++)\r
+       {\r
+               ADMIN_OPTION *a = LIST_DATA(aol, i);\r
+\r
+               Copy(&ao->Items[i], a, sizeof(ADMIN_OPTION));\r
+\r
+               Free(a);\r
+       }\r
+\r
+       ReleaseList(aol);\r
+}\r
+\r
+// 新しい ADMIN OPTION の作成\r
+ADMIN_OPTION *NewAdminOption(char *name, UINT value)\r
+{\r
+       ADMIN_OPTION *a;\r
+       // 引数チェック\r
+       if (name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       a = ZeroMalloc(sizeof(ADMIN_OPTION));\r
+       StrCpy(a->Name, sizeof(a->Name), name);\r
+       a->Value = value;\r
+\r
+       return a;\r
+}\r
+\r
+// AC リストのクローン\r
+LIST *CloneAcList(LIST *o)\r
+{\r
+       LIST *ret;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       ret = NewAcList();\r
+       SetAcList(ret, o);\r
+\r
+       return ret;\r
+}\r
+\r
+// AC リストをすべてセットする\r
+void SetAcList(LIST *o, LIST *src)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL || src == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       DelAllAc(o);\r
+\r
+       for (i = 0;i < LIST_NUM(src);i++)\r
+       {\r
+               AC *ac = LIST_DATA(src, i);\r
+\r
+               AddAc(o, ac);\r
+       }\r
+}\r
+\r
+// AC リストからすべての AC を削除する\r
+void DelAllAc(LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               AC *ac = LIST_DATA(o, i);\r
+\r
+               Free(ac);\r
+       }\r
+\r
+       DeleteAll(o);\r
+}\r
+\r
+// AC リストを解放する\r
+void FreeAcList(LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               AC *ac = LIST_DATA(o, i);\r
+\r
+               Free(ac);\r
+       }\r
+\r
+       ReleaseList(o);\r
+}\r
+\r
+// AC の内容を示す文字列を生成する\r
+char *GenerateAcStr(AC *ac)\r
+{\r
+       char tmp[MAX_SIZE];\r
+       char ip[64], mask[64];\r
+\r
+       if (ac == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       IPToStr(ip, sizeof(ip), &ac->IpAddress);\r
+       MaskToStr(mask, sizeof(mask), &ac->SubnetMask);\r
+\r
+       if (ac->Masked == false)\r
+       {\r
+               Format(tmp, sizeof(tmp), "%s", ip);\r
+       }\r
+       else\r
+       {\r
+               Format(tmp, sizeof(tmp), "%s/%s", ip, mask);\r
+       }\r
+\r
+       return CopyStr(tmp);\r
+}\r
+\r
+// AC の設定\r
+void SetAc(LIST *o, UINT id, AC *ac)\r
+{\r
+       // 引数チェック\r
+       if (o == NULL || id == 0 || ac == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (DelAc(o, id))\r
+       {\r
+               AddAc(o, ac);\r
+       }\r
+}\r
+\r
+// AC の取得\r
+AC *GetAc(LIST *o, UINT id)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL || id == 0)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               AC *ac = LIST_DATA(o, i);\r
+\r
+               if (ac->Id == id)\r
+               {\r
+                       return Clone(ac, sizeof(AC));\r
+               }\r
+       }\r
+\r
+       return NULL;\r
+}\r
+\r
+// AC の削除\r
+bool DelAc(LIST *o, UINT id)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL || id == 0)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               AC *ac = LIST_DATA(o, i);\r
+\r
+               if (ac->Id == id)\r
+               {\r
+                       if (Delete(o, ac))\r
+                       {\r
+                               Free(ac);\r
+\r
+                               NormalizeAcList(o);\r
+\r
+                               return true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// AC の追加\r
+void AddAc(LIST *o, AC *ac)\r
+{\r
+       // 引数チェック\r
+       if (o == NULL || ac == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (LIST_NUM(o) < MAX_HUB_ACS)\r
+       {\r
+               Insert(o, Clone(ac, sizeof(AC)));\r
+\r
+               NormalizeAcList(o);\r
+       }\r
+}\r
+\r
+// AC リストを正規化する\r
+void NormalizeAcList(LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               AC *ac = LIST_DATA(o, i);\r
+\r
+               if (IsIP6(&ac->IpAddress))\r
+               {\r
+                       ac->IpAddress.ipv6_scope_id = 0;\r
+               }\r
+\r
+               ac->Id = (i + 1);\r
+       }\r
+}\r
+\r
+// 新しい AC リストの作成\r
+LIST *NewAcList()\r
+{\r
+       return NewList(CmpAc);\r
+}\r
+\r
+// AC 比較\r
+int CmpAc(void *p1, void *p2)\r
+{\r
+       AC *a1, *a2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       a1 = *(AC **)p1;\r
+       a2 = *(AC **)p2;\r
+       if (a1 == NULL || a2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       if (a1->Priority > a2->Priority)\r
+       {\r
+               return 1;\r
+       }\r
+       else if (a1->Priority < a2->Priority)\r
+       {\r
+               return -1;\r
+       }\r
+       else if (a1->Deny > a2->Deny)\r
+       {\r
+               return 1;\r
+       }\r
+       else if (a1->Deny < a2->Deny)\r
+       {\r
+               return -1;\r
+       }\r
+       else\r
+       {\r
+               return 0;\r
+       }\r
+}\r
+\r
+// CRL のコピー\r
+CRL *CopyCrl(CRL *crl)\r
+{\r
+       CRL *ret;\r
+       // 引数チェック\r
+       if (crl == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       ret = ZeroMalloc(sizeof(CRL));\r
+\r
+       if (crl->Serial != NULL)\r
+       {\r
+               ret->Serial = NewXSerial(crl->Serial->data, crl->Serial->size);\r
+       }\r
+\r
+       ret->Name = CopyName(crl->Name);\r
+\r
+       Copy(ret->DigestMD5, crl->DigestMD5, MD5_SIZE);\r
+       Copy(ret->DigestSHA1, crl->DigestSHA1, SHA1_SIZE);\r
+\r
+       return ret;\r
+}\r
+\r
+// CRL の解放\r
+void FreeCrl(CRL *crl)\r
+{\r
+       // 引数チェック\r
+       if (crl == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (crl->Serial != NULL)\r
+       {\r
+               FreeXSerial(crl->Serial);\r
+       }\r
+\r
+       if (crl->Name != NULL)\r
+       {\r
+               FreeName(crl->Name);\r
+       }\r
+\r
+       Free(crl);\r
+}\r
+\r
+// 仮想 HUB の CRL リストを検索して証明書が無効化されていないかどうか調べる\r
+bool IsValidCertInHub(HUB *h, X *x)\r
+{\r
+       bool ret;\r
+       // 引数チェック\r
+       if (h == NULL || x == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (h->HubDb == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (IsXRevoked(x))\r
+       {\r
+               // ファイルに保存されている CRL によって無効化されている\r
+               return false;\r
+       }\r
+\r
+       LockList(h->HubDb->CrlList);\r
+       {\r
+               ret = IsCertMatchCrlList(x, h->HubDb->CrlList);\r
+       }\r
+       UnlockList(h->HubDb->CrlList);\r
+\r
+       if (ret)\r
+       {\r
+               // 一致するので無効である\r
+               return false;\r
+       }\r
+\r
+       // 一致しなかったので有効である\r
+       return true;\r
+}\r
+\r
+// CRL リストに証明書が一致するかどうか検索\r
+bool IsCertMatchCrlList(X *x, LIST *o)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (x == NULL || o == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               CRL *crl = LIST_DATA(o, i);\r
+\r
+               if (IsCertMatchCrl(x, crl))\r
+               {\r
+                       return true;\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// CRL を示す文字列に変換する\r
+wchar_t *GenerateCrlStr(CRL *crl)\r
+{\r
+       wchar_t tmp[2048];\r
+       // 引数チェック\r
+       if (crl == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       UniStrCpy(tmp, sizeof(tmp), L"");\r
+\r
+       if (crl->Name != NULL)\r
+       {\r
+               // 名前情報\r
+               wchar_t name[MAX_SIZE];\r
+\r
+               UniStrCat(tmp, sizeof(tmp), L"Subject=\"");\r
+\r
+               GetAllNameFromName(name, sizeof(name), crl->Name);\r
+               UniStrCat(tmp, sizeof(tmp), name);\r
+               UniStrCat(tmp, sizeof(tmp), L"\", ");\r
+       }\r
+\r
+       if (crl->Serial != NULL)\r
+       {\r
+               // シリアル情報\r
+               char str[128];\r
+               wchar_t uni[128];\r
+\r
+               BinToStrEx(str, sizeof(str), crl->Serial->data, crl->Serial->size);\r
+               StrToUni(uni, sizeof(uni), str);\r
+               UniStrCat(tmp, sizeof(tmp), L"Serial=\"");\r
+               UniStrCat(tmp, sizeof(tmp), uni);\r
+               UniStrCat(tmp, sizeof(tmp), L"\", ");\r
+       }\r
+\r
+       if (IsZero(crl->DigestMD5, MD5_SIZE) == false)\r
+       {\r
+               // MD5\r
+               char str[128];\r
+               wchar_t uni[128];\r
+\r
+               BinToStrEx(str, sizeof(str), crl->DigestMD5, MD5_SIZE);\r
+               StrToUni(uni, sizeof(uni), str);\r
+               UniStrCat(tmp, sizeof(tmp), L"MD5=\"");\r
+               UniStrCat(tmp, sizeof(tmp), uni);\r
+               UniStrCat(tmp, sizeof(tmp), L"\", ");\r
+       }\r
+\r
+       if (IsZero(crl->DigestSHA1, SHA1_SIZE) == false)\r
+       {\r
+               // MD5\r
+               char str[128];\r
+               wchar_t uni[128];\r
+\r
+               BinToStrEx(str, sizeof(str), crl->DigestSHA1, SHA1_SIZE);\r
+               StrToUni(uni, sizeof(uni), str);\r
+               UniStrCat(tmp, sizeof(tmp), L"SHA1=\"");\r
+               UniStrCat(tmp, sizeof(tmp), uni);\r
+               UniStrCat(tmp, sizeof(tmp), L"\", ");\r
+       }\r
+\r
+       if (UniEndWith(tmp, L", "))\r
+       {\r
+               tmp[UniStrLen(tmp) - 2] = 0;\r
+       }\r
+\r
+       return CopyUniStr(tmp);\r
+}\r
+\r
+// 証明書無効リストエントリに一致するかどうか検査する\r
+bool IsCertMatchCrl(X *x, CRL *crl)\r
+{\r
+       // このあたりは急いで実装したのでコードがあまり美しくない。\r
+       bool b = true;\r
+       // 引数チェック\r
+       if (x == NULL || crl == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (crl->Serial != NULL)\r
+       {\r
+               // CRL にシリアル番号が定義されている場合\r
+               if (x->serial == NULL || CompareXSerial(x->serial, crl->Serial) == false)\r
+               {\r
+                       // シリアル番号不一致\r
+                       b = false;\r
+               }\r
+       }\r
+\r
+       if (IsZero(crl->DigestMD5, sizeof(crl->DigestMD5)) == false)\r
+       {\r
+               UCHAR test[MD5_SIZE];\r
+               // CRL に DigestMD5 が定義されている場合\r
+               GetXDigest(x, test, false);\r
+\r
+               if (Cmp(test, crl->DigestMD5, MD5_SIZE) != 0)\r
+               {\r
+                       b = false;\r
+               }\r
+       }\r
+\r
+       if (IsZero(crl->DigestSHA1, sizeof(crl->DigestSHA1)) == false)\r
+       {\r
+               UCHAR test[SHA1_SIZE];\r
+               // CRL に DigestSHA1 が定義されている場合\r
+               GetXDigest(x, test, true);\r
+\r
+               if (Cmp(test, crl->DigestSHA1, SHA1_SIZE) != 0)\r
+               {\r
+                       b = false;\r
+               }\r
+       }\r
+\r
+       if (crl->Name != NULL)\r
+       {\r
+               // CRL に名前が定義されている場合\r
+               NAME *xn, *cn;\r
+               xn = x->subject_name;\r
+               cn = crl->Name;\r
+\r
+               if (cn->CommonName != NULL && (UniIsEmptyStr(cn->CommonName) == false))\r
+               {\r
+                       if (xn->CommonName == NULL || UniSoftStrCmp(xn->CommonName, cn->CommonName) != 0)\r
+                       {\r
+                               // CommonName 不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+\r
+               if (cn->Organization != NULL && (UniIsEmptyStr(cn->Organization) == false))\r
+               {\r
+                       if (xn->Organization == NULL || UniSoftStrCmp(xn->Organization, cn->Organization) != 0)\r
+                       {\r
+                               // Organization 不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+\r
+               if (cn->Unit != NULL && (UniIsEmptyStr(cn->Unit) == false))\r
+               {\r
+                       if (xn->Unit == NULL || UniSoftStrCmp(xn->Unit, cn->Unit) != 0)\r
+                       {\r
+                               // Unit不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+\r
+               if (cn->Country != NULL && (UniIsEmptyStr(cn->Country) == false))\r
+               {\r
+                       if (xn->Country == NULL || UniSoftStrCmp(xn->Country, cn->Country) != 0)\r
+                       {\r
+                               // Country 不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+\r
+               if (cn->State != NULL && (UniIsEmptyStr(cn->State) == false))\r
+               {\r
+                       if (xn->State == NULL || UniSoftStrCmp(xn->State, cn->State) != 0)\r
+                       {\r
+                               // State 不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+\r
+               if (cn->Local != NULL && (UniIsEmptyStr(cn->Local) == false))\r
+               {\r
+                       if (xn->Local == NULL || UniSoftStrCmp(xn->Local, cn->Local) != 0)\r
+                       {\r
+                               // Local 不一致\r
+                               b = false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return b;\r
+}\r
+\r
+// 管理オプションのヘルプ文字列を取得する\r
+wchar_t *GetHubAdminOptionHelpString(char *name)\r
+{\r
+       char tmp[MAX_SIZE];\r
+       wchar_t *ret;\r
+       // 引数チェック\r
+       if (name == NULL)\r
+       {\r
+               return L"";\r
+       }\r
+\r
+       Format(tmp, sizeof(tmp), "HUB_AO_%s", name);\r
+\r
+       ret = _UU(tmp);\r
+       if (UniIsEmptyStr(ret))\r
+       {\r
+               ret = _UU("HUB_AO_UNKNOWN");\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// 仮想 HUB にデフォルトの管理オプションを追加する\r
+void AddHubAdminOptionsDefaults(HUB *h, bool lock)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (lock)\r
+       {\r
+               LockList(h->AdminOptionList);\r
+       }\r
+\r
+       for (i = 0;i < num_admin_options;i++)\r
+       {\r
+               ADMIN_OPTION *e = &admin_options[i];\r
+               ADMIN_OPTION t, *r;\r
+\r
+               Zero(&t, sizeof(t));\r
+               StrCpy(t.Name, sizeof(t.Name), e->Name);\r
+\r
+               r = Search(h->AdminOptionList, &t);\r
+               if (r == NULL)\r
+               {\r
+                       ADMIN_OPTION *a = ZeroMalloc(sizeof(ADMIN_OPTION));\r
+\r
+                       StrCpy(a->Name, sizeof(a->Name), e->Name);\r
+                       a->Value = e->Value;\r
+\r
+                       Insert(h->AdminOptionList, a);\r
+               }\r
+       }\r
+\r
+       if (lock)\r
+       {\r
+               UnlockList(h->AdminOptionList);\r
+       }\r
+}\r
+\r
+// 仮想 HUB のすべての管理オプションの削除\r
+void DeleteAllHubAdminOption(HUB *h, bool lock)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (lock)\r
+       {\r
+               LockList(h->AdminOptionList);\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(h->AdminOptionList);i++)\r
+       {\r
+               Free(LIST_DATA(h->AdminOptionList, i));\r
+       }\r
+\r
+       DeleteAll(h->AdminOptionList);\r
+\r
+       if (lock)\r
+       {\r
+               UnlockList(h->AdminOptionList);\r
+       }\r
+}\r
+\r
+// 仮想 HUB の管理オプションの取得\r
+UINT GetHubAdminOptionEx(HUB *h, char *name, UINT default_value)\r
+{\r
+       UINT ret = default_value;\r
+       // 引数チェック\r
+       if (h == NULL || name == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       LockList(h->AdminOptionList);\r
+       {\r
+               ADMIN_OPTION *a, t;\r
+\r
+               Zero(&t, sizeof(t));\r
+               StrCpy(t.Name, sizeof(t.Name), name);\r
+               Trim(t.Name);\r
+\r
+               a = Search(h->AdminOptionList, &t);\r
+\r
+               if (a != NULL)\r
+               {\r
+                       ret = a->Value;\r
+               }\r
+       }\r
+       UnlockList(h->AdminOptionList);\r
+\r
+       return ret;\r
+}\r
+UINT GetHubAdminOption(HUB *h, char *name)\r
+{\r
+       return GetHubAdminOptionEx(h, name, 0);\r
+}\r
+\r
+// 管理オプション\r
+int CompareAdminOption(void *p1, void *p2)\r
+{\r
+       ADMIN_OPTION *a1, *a2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       a1 = *(ADMIN_OPTION **)p1;\r
+       a2 = *(ADMIN_OPTION **)p2;\r
+       if (a1 == NULL || a2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return StrCmpi(a1->Name, a2->Name);\r
+}\r
+\r
+// 番犬開始\r
+void StartHubWatchDog(HUB *h)\r
+{\r
+       THREAD *t;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       h->HaltWatchDog = false;\r
+       h->WatchDogEvent = NewEvent();\r
+\r
+       t = NewThread(HubWatchDogThread, h);\r
+       WaitThreadInit(t);\r
+       ReleaseThread(t);\r
+}\r
+\r
+// 番犬停止\r
+void StopHubWatchDog(HUB *h)\r
+{\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       h->HaltWatchDog = true;\r
+       Set(h->WatchDogEvent);\r
+\r
+       WaitThread(h->WatchDogThread, INFINITE);\r
+       ReleaseThread(h->WatchDogThread);\r
+       h->WatchDogThread = NULL;\r
+       h->HaltWatchDog = false;\r
+\r
+       ReleaseEvent(h->WatchDogEvent);\r
+       h->WatchDogEvent = NULL;\r
+}\r
+\r
+// 番犬スレッド\r
+void HubWatchDogThread(THREAD *t, void *param)\r
+{\r
+       UINT num_packets_v4 = 0;\r
+       UINT num_packets_v6 = 0;\r
+       HUB *hub;\r
+       // 引数チェック\r
+       if (t == NULL || param == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       hub = (HUB *)param;\r
+\r
+       hub->WatchDogThread = t;\r
+       AddRef(t->ref);\r
+\r
+       NoticeThreadInit(t);\r
+\r
+       while (true)\r
+       {\r
+               LIST *o;\r
+               LIST *o2;\r
+               UINT i, num;\r
+               UINT interval;\r
+               UINT wait_time = 100;\r
+               if (hub->HaltWatchDog)\r
+               {\r
+                       break;\r
+               }\r
+\r
+               o = NewListFast(NULL);\r
+               o2 = NewListFast(NULL);\r
+\r
+               // ARP パケットの送信\r
+               LockList(hub->IpTable);\r
+               {\r
+                       num = LIST_NUM(hub->IpTable);\r
+                       for (i = 0;i < LIST_NUM(hub->IpTable);i++)\r
+                       {\r
+                               IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);\r
+\r
+                               if ((e->UpdatedTime + (UINT64)(IP_TABLE_EXPIRE_TIME)) > Tick64())\r
+                               {\r
+                                       if (e->MacAddress[0] != 0xff || e->MacAddress[1] != 0xff || e->MacAddress[2] != 0xff ||\r
+                                               e->MacAddress[3] != 0xff || e->MacAddress[4] != 0xff || e->MacAddress[5] != 0xff)\r
+                                       {\r
+                                               if (hub->Option != NULL && hub->Option->NoArpPolling == false)\r
+                                               {\r
+                                                       if (IsIP4(&e->Ip))\r
+                                                       {\r
+                                                               // IPv4\r
+                                                               MAC_HEADER *mac = ZeroMalloc(sizeof(MAC_HEADER) + sizeof(ARPV4_HEADER));\r
+                                                               ARPV4_HEADER *p = (ARPV4_HEADER *)(((UCHAR *)mac) + sizeof(MAC_HEADER));\r
+\r
+                                                               Copy(mac->DestAddress, e->MacAddress, 6);\r
+                                                               Copy(mac->SrcAddress, hub->HubMacAddr, 6);\r
+                                                               mac->Protocol = Endian16(MAC_PROTO_ARPV4);\r
+\r
+                                                               p->HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);\r
+                                                               p->ProtocolType = Endian16(MAC_PROTO_IPV4);\r
+                                                               p->HardwareSize = 6;\r
+                                                               p->ProtocolSize = 4;\r
+                                                               p->Operation = Endian16(ARP_OPERATION_REQUEST);\r
+                                                               Copy(p->SrcAddress, hub->HubMacAddr, 6);\r
+                                                               p->SrcIP = IPToUINT(&hub->HubIp);\r
+                                                               p->TargetAddress[0] =\r
+                                                                       p->TargetAddress[1] =\r
+                                                                       p->TargetAddress[2] =\r
+                                                                       p->TargetAddress[3] =\r
+                                                                       p->TargetAddress[4] =\r
+                                                                       p->TargetAddress[5] = 0x00;\r
+                                                               p->TargetIP = IPToUINT(&e->Ip);\r
+                                                               Insert(o, mac);\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (hub->Option != NULL && hub->Option->NoIPv6AddrPolling == false)\r
+                                               {\r
+                                                       if (IsIP6(&e->Ip))\r
+                                                       {\r
+                                                               // IPv6\r
+                                                               BUF *buf;\r
+                                                               IPV6_ADDR ip6addr;\r
+\r
+                                                               if (IPToIPv6Addr(&ip6addr, &e->Ip))\r
+                                                               {\r
+                                                                       buf = BuildICMPv6NeighborSoliciation(&hub->HubIpV6,\r
+                                                                               &ip6addr,\r
+                                                                               hub->HubMacAddr, ++hub->HubIP6Id);\r
+\r
+                                                                       if (buf != NULL)\r
+                                                                       {\r
+                                                                               BUF *buf2 = NewBuf();\r
+                                                                               MAC_HEADER mac;\r
+\r
+                                                                               Zero(&mac, sizeof(mac));\r
+\r
+                                                                               Copy(mac.DestAddress, e->MacAddress, 6);\r
+                                                                               Copy(mac.SrcAddress, hub->HubMacAddr, 6);\r
+                                                                               mac.Protocol = Endian16(MAC_PROTO_IPV6);\r
+\r
+                                                                               WriteBuf(buf2, &mac, sizeof(MAC_HEADER));\r
+\r
+                                                                               WriteBuf(buf2, buf->Buf, buf->Size);\r
+\r
+                                                                               FreeBuf(buf);\r
+\r
+                                                                               Insert(o2, buf2);\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               UnlockList(hub->IpTable);\r
+\r
+               if ((LIST_NUM(o) + LIST_NUM(o2)) != 0)\r
+               {\r
+                       interval = HUB_ARP_SEND_INTERVAL / (LIST_NUM(o) + LIST_NUM(o2));\r
+               }\r
+               else\r
+               {\r
+                       interval = HUB_ARP_SEND_INTERVAL;\r
+               }\r
+\r
+               for (i = 0;i < LIST_NUM(o);i++)\r
+               {\r
+                       PKT *packet;\r
+                       void *p = LIST_DATA(o, i);\r
+\r
+                       Wait(hub->WatchDogEvent, interval);\r
+                       if (hub->HaltWatchDog)\r
+                       {\r
+                               for (;i < LIST_NUM(o);i++)\r
+                               {\r
+                                       Free(LIST_DATA(o, i));\r
+                               }\r
+                               ReleaseList(o);\r
+\r
+                               for (i = 0;i < LIST_NUM(o2);i++)\r
+                               {\r
+                                       FreeBuf(LIST_DATA(o2, i));\r
+                               }\r
+                               ReleaseList(o2);\r
+                               goto ESCAPE;\r
+                       }\r
+\r
+                       packet = ParsePacket((UCHAR *)p, sizeof(MAC_HEADER) + sizeof(ARPV4_HEADER));\r
+                       if (packet != NULL)\r
+                       {\r
+                               StorePacket(hub, NULL, packet);\r
+                               num_packets_v4++;\r
+                       }\r
+                       else\r
+                       {\r
+                               Free(p);\r
+                       }\r
+               }\r
+\r
+               for (i = 0;i < LIST_NUM(o2);i++)\r
+               {\r
+                       PKT *packet;\r
+                       BUF *buf = LIST_DATA(o2, i);\r
+\r
+                       Wait(hub->WatchDogEvent, interval);\r
+                       if (hub->HaltWatchDog)\r
+                       {\r
+                               ReleaseList(o);\r
+\r
+                               for (;i < LIST_NUM(o2);i++)\r
+                               {\r
+                                       FreeBuf(LIST_DATA(o2, i));\r
+                               }\r
+                               ReleaseList(o2);\r
+                               goto ESCAPE;\r
+                       }\r
+\r
+                       packet = ParsePacket(buf->Buf, buf->Size);\r
+                       if (packet != NULL)\r
+                       {\r
+                               StorePacket(hub, NULL, packet);\r
+                               num_packets_v6++;\r
+                       }\r
+                       else\r
+                       {\r
+                               Free(buf->Buf);\r
+                       }\r
+\r
+                       Free(buf);\r
+               }\r
+\r
+               ReleaseList(o);\r
+               ReleaseList(o2);\r
+\r
+               if (num == 0)\r
+               {\r
+                       wait_time = HUB_ARP_SEND_INTERVAL;\r
+               }\r
+\r
+               Wait(hub->WatchDogEvent, wait_time);\r
+       }\r
+ESCAPE:\r
+       return;\r
+}\r
+\r
+// SecureNAT を有効/無効に設定する\r
+void EnableSecureNAT(HUB *h, bool enable)\r
+{\r
+       EnableSecureNATEx(h, enable, false);\r
+}\r
+void EnableSecureNATEx(HUB *h, bool enable, bool no_change)\r
+{\r
+       bool for_cluster = false;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+       {\r
+               if (h->Type == HUB_TYPE_FARM_DYNAMIC)\r
+               {\r
+                       for_cluster = true;\r
+               }\r
+       }\r
+\r
+       Lock(h->lock_online);\r
+       {\r
+               if (no_change == false)\r
+               {\r
+                       h->EnableSecureNAT = enable;\r
+               }\r
+\r
+               if (h->EnableSecureNAT == false)\r
+               {\r
+STOP:\r
+                       // すでに開始している場合は停止する\r
+                       if (h->SecureNAT != NULL)\r
+                       {\r
+                               SnFreeSecureNAT(h->SecureNAT);\r
+                               h->SecureNAT = NULL;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (for_cluster)\r
+                       {\r
+                               if ((h->SecureNAT != NULL && LIST_NUM(h->SessionList) <= 1) ||\r
+                                       (h->SecureNAT == NULL && LIST_NUM(h->SessionList) == 0))\r
+                               {\r
+                                       // 開始モードだが、ダイナミック仮想 HUB で他にセッションが無い場合\r
+                                       // は停止する\r
+                                       goto STOP;\r
+                               }\r
+                       }\r
+\r
+                       // まだ開始していない場合で HUB がオンラインの場合は開始する\r
+                       if (h->SecureNAT == NULL && h->Offline == false)\r
+                       {\r
+                               h->SecureNAT = SnNewSecureNAT(h, h->SecureNATOption);\r
+                       }\r
+               }\r
+       }\r
+       Unlock(h->lock_online);\r
+}\r
+\r
+// アクセスリストを文字列に変換する\r
+void GetAccessListStr(char *str, UINT size, ACCESS *a)\r
+{\r
+       char tmp[MAX_SIZE];\r
+       char tmp1[MAX_SIZE];\r
+       char tmp2[MAX_SIZE];\r
+       bool l3 = false;\r
+       bool asterisk = false;\r
+       // 引数チェック\r
+       if (str == NULL || a == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       StrCpy(str, size, "");\r
+\r
+       if (a->IsIPv6 == false)\r
+       {\r
+               if (a->SrcIpAddress != 0 || a->SrcSubnetMask != 0)\r
+               {\r
+                       IPToStr32(tmp1, sizeof(tmp1), a->SrcIpAddress);\r
+                       MaskToStr32(tmp2, sizeof(tmp2), a->SrcSubnetMask);\r
+                       Format(tmp, sizeof(tmp), "SrcIPv4=%s/%s, ", tmp1, tmp2);\r
+                       StrCat(str, size, tmp);\r
+\r
+                       l3 = true;\r
+               }\r
+\r
+               if (a->DestIpAddress != 0 || a->DestSubnetMask != 0)\r
+               {\r
+                       IPToStr32(tmp1, sizeof(tmp1), a->DestIpAddress);\r
+                       MaskToStr32(tmp2, sizeof(tmp2), a->DestSubnetMask);\r
+                       Format(tmp, sizeof(tmp), "DstIPv4=%s/%s, ", tmp1, tmp2);\r
+                       StrCat(str, size, tmp);\r
+\r
+                       l3 = true;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (IsZeroIP6Addr(&a->SrcIpAddress6) == false || IsZeroIP6Addr(&a->SrcSubnetMask6) == false)\r
+               {\r
+                       IP6AddrToStr(tmp1, sizeof(tmp1), &a->SrcIpAddress6);\r
+                       Mask6AddrToStr(tmp2, sizeof(tmp2), &a->SrcSubnetMask6);\r
+                       Format(tmp, sizeof(tmp), "SrcIPv6=%s/%s, ", tmp1, tmp2);\r
+                       StrCat(str, size, tmp);\r
+\r
+                       l3 = true;\r
+               }\r
+\r
+               if (IsZeroIP6Addr(&a->DestIpAddress6) == false || IsZeroIP6Addr(&a->DestSubnetMask6) == false)\r
+               {\r
+                       IP6AddrToStr(tmp1, sizeof(tmp1), &a->DestIpAddress6);\r
+                       Mask6AddrToStr(tmp2, sizeof(tmp2), &a->DestSubnetMask6);\r
+                       Format(tmp, sizeof(tmp), "DstIPv6=%s/%s, ", tmp1, tmp2);\r
+                       StrCat(str, size, tmp);\r
+\r
+                       l3 = true;\r
+               }\r
+       }\r
+\r
+       if (a->Protocol != 0)\r
+       {\r
+               StrCpy(tmp1, sizeof(tmp1), "");\r
+               switch (a->Protocol)\r
+               {\r
+               case 1:\r
+                       StrCpy(tmp1, sizeof(tmp1), "ICMPv4");\r
+                       break;\r
+               case 3:\r
+                       StrCpy(tmp1, sizeof(tmp1), "GGP");\r
+                       break;\r
+               case 6:\r
+                       StrCpy(tmp1, sizeof(tmp1), "TCP");\r
+                       break;\r
+               case 8:\r
+                       StrCpy(tmp1, sizeof(tmp1), "EGP");\r
+                       break;\r
+               case 12:\r
+                       StrCpy(tmp1, sizeof(tmp1), "PUP");\r
+                       break;\r
+               case 17:\r
+                       StrCpy(tmp1, sizeof(tmp1), "UDP");\r
+                       break;\r
+               case 20:\r
+                       StrCpy(tmp1, sizeof(tmp1), "HMP");\r
+                       break;\r
+               case 22:\r
+                       StrCpy(tmp1, sizeof(tmp1), "XNS-IDP");\r
+                       break;\r
+               case 27:\r
+                       StrCpy(tmp1, sizeof(tmp1), "RDP");\r
+                       break;\r
+               case 58:\r
+                       StrCpy(tmp1, sizeof(tmp1), "ICMPv6");\r
+                       break;\r
+               case 66:\r
+                       StrCpy(tmp1, sizeof(tmp1), "RVD");\r
+                       break;\r
+               }\r
+               Format(tmp, sizeof(tmp), "Protocol=%s(%u), ", tmp1, a->Protocol);\r
+               StrCat(str, size, tmp);\r
+\r
+               l3 = true;\r
+       }\r
+\r
+       if (a->SrcPortStart != 0)\r
+       {\r
+               if (a->SrcPortEnd == a->SrcPortStart)\r
+               {\r
+                       Format(tmp, sizeof(tmp), "SrcPort=%u, ", a->SrcPortStart);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+               else\r
+               {\r
+                       Format(tmp, sizeof(tmp), "SrcPort=%u-%u, ", a->SrcPortStart, a->SrcPortEnd);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+\r
+               l3 = true;\r
+       }\r
+\r
+       if (a->DestPortStart != 0)\r
+       {\r
+               if (a->DestPortEnd == a->DestPortStart)\r
+               {\r
+                       Format(tmp, sizeof(tmp), "DstPort=%u, ", a->DestPortStart);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+               else\r
+               {\r
+                       Format(tmp, sizeof(tmp), "DstPort=%u-%u, ", a->DestPortStart, a->DestPortEnd);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+\r
+               l3 = true;\r
+       }\r
+\r
+       if (StrLen(a->SrcUsername) != 0)\r
+       {\r
+               Format(tmp, sizeof(tmp), "SrcUser=%s, ", a->SrcUsername);\r
+               StrCat(str, size, tmp);\r
+       }\r
+\r
+       if (StrLen(a->DestUsername) != 0)\r
+       {\r
+               Format(tmp, sizeof(tmp), "DstUser=%s, ", a->DestUsername);\r
+               StrCat(str, size, tmp);\r
+       }\r
+\r
+       if (a->CheckSrcMac != false)\r
+       {\r
+               char mac[MAX_SIZE], mask[MAX_SIZE];\r
+               MacToStr(mac, sizeof(mac), a->SrcMacAddress);\r
+               MacToStr(mask, sizeof(mask), a->SrcMacMask);\r
+               Format(tmp, sizeof(tmp), "SrcMac=%s/%s, ", mac, mask);\r
+               StrCat(str, size, tmp);\r
+       }\r
+       if (a->CheckDstMac != false)\r
+       {\r
+               char mac[MAX_SIZE], mask[MAX_SIZE];\r
+               MacToStr(mac, sizeof(mac), a->DstMacAddress);\r
+               MacToStr(mask, sizeof(mask), a->DstMacMask);\r
+               Format(tmp, sizeof(tmp), "DstMac=%s/%s, ", mac, mask);\r
+               StrCat(str, size, tmp);\r
+       }\r
+\r
+       if (a->CheckTcpState)\r
+       {\r
+               if(a->Established)\r
+               {\r
+                       StrCat(str, size, "Established, ");\r
+               }\r
+               else\r
+               {\r
+                       StrCat(str, size, "Unestablished, ");\r
+               }\r
+\r
+               l3 = true;\r
+       }\r
+\r
+       if (a->Discard == false)\r
+       {\r
+               if (a->Delay >= 1)\r
+               {\r
+                       Format(tmp, sizeof(tmp), "Delay=%u, ", a->Delay);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+\r
+               if (a->Jitter >= 1)\r
+               {\r
+                       Format(tmp, sizeof(tmp), "Jitter=%u, ", a->Jitter);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+\r
+               if (a->Loss >= 1)\r
+               {\r
+                       Format(tmp, sizeof(tmp), "Loss=%u, " , a->Loss);\r
+                       StrCat(str, size, tmp);\r
+               }\r
+       }\r
+\r
+       if (StrLen(str) == 0)\r
+       {\r
+               asterisk = true;\r
+       }\r
+\r
+       if (l3)\r
+       {\r
+               if (a->IsIPv6)\r
+               {\r
+                       StrCatLeft(str, size, "(ipv6) ");\r
+               }\r
+               else\r
+               {\r
+                       StrCatLeft(str, size, "(ipv4) ");\r
+               }\r
+       }\r
+       else\r
+       {\r
+               StrCatLeft(str, size, "(ether) ");\r
+       }\r
+\r
+       if (EndWith(str, ", "))\r
+       {\r
+               str[StrLen(str) - 2] = 0;\r
+       }\r
+\r
+       if (asterisk)\r
+       {\r
+               StrCat(str, size, "*");\r
+       }\r
+}\r
+\r
+// パケットをアクセスリストによってマスクすることができるかどうか判定する\r
+bool IsPacketMaskedByAccessList(SESSION *s, PKT *p, ACCESS *a, UINT dest_username, UINT dest_groupname)\r
+{\r
+       UINT src_username;\r
+       UINT src_groupname;\r
+       HUB_PA *pa;\r
+       IPV4_HEADER *ip = NULL;\r
+       IPV6_HEADER *ip6 = NULL;\r
+       bool is_ipv4_packet = false;\r
+       bool is_ipv6_packet = false;\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL || a == NULL)\r
+       {\r
+               return false;\r
+       }\r
+       if (a->Active == false)\r
+       {\r
+               // アクセスリストは無効\r
+               return false;\r
+       }\r
+\r
+       pa = (HUB_PA *)s->PacketAdapter->Param;\r
+\r
+       // 送信元のユーザー名ハッシュ\r
+       src_username = pa->UsernameHash;\r
+       src_groupname = pa->GroupnameHash;\r
+\r
+       // 送信元・宛先 MAC アドレスの判定\r
+       if (a->CheckSrcMac != false)\r
+       {\r
+               UINT i;\r
+               for (i = 0; i < 6; i++)\r
+               {\r
+                       if((a->SrcMacAddress[i] & a->SrcMacMask[i]) != (a->SrcMacMask[i] & p->MacAddressSrc[i]))\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (a->CheckDstMac != false)\r
+       {\r
+               UINT i;\r
+               for (i = 0; i < 6; i++)\r
+               {\r
+                       if ((a->DstMacAddress[i] & a->DstMacMask[i]) != (a->DstMacMask[i] & p->MacAddressDest[i]))\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // 送信元ユーザー名 / グループ名のチェック\r
+       if (a->SrcUsernameHash != 0)\r
+       {\r
+               if ((a->SrcUsernameHash != src_username) && (a->SrcUsernameHash != src_groupname))\r
+               {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // 宛先ユーザー名 / グループ名のチェック\r
+       if (a->DestUsernameHash != 0)\r
+       {\r
+               if ((a->DestUsernameHash != dest_username) && (a->DestUsernameHash != dest_groupname))\r
+               {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // IP パケットの判定\r
+       if (p->TypeL3 != L3_IPV4)\r
+       {\r
+               is_ipv4_packet = false;\r
+       }\r
+       else\r
+       {\r
+               is_ipv4_packet = true;\r
+       }\r
+\r
+       if (p->TypeL3 != L3_IPV6)\r
+       {\r
+               is_ipv6_packet = false;\r
+       }\r
+       else\r
+       {\r
+               is_ipv6_packet = true;\r
+       }\r
+\r
+       if (is_ipv4_packet)\r
+       {\r
+               ip = p->L3.IPv4Header;\r
+       }\r
+\r
+       if (is_ipv6_packet)\r
+       {\r
+               ip6 = p->L3.IPv6Header;\r
+       }\r
+\r
+       if (a->IsIPv6 == false)\r
+       {\r
+               // IPv4\r
+\r
+               // 送信元 IP アドレスのチェック\r
+               if (a->SrcIpAddress != 0 || a->SrcSubnetMask != 0)\r
+               {\r
+                       if (is_ipv4_packet == false)\r
+                       {\r
+                               if (p->TypeL3 == L3_ARPV4)\r
+                               {\r
+                                       bool arp_match = false;\r
+                                       if (p->L3.ARPv4Header->HardwareSize == 6 &&\r
+                                               Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&\r
+                                               p->L3.ARPv4Header->ProtocolSize == 4 &&\r
+                                               Endian16(p->L3.ARPv4Header->ProtocolType) == 0x0800)\r
+                                       {\r
+                                               UINT uint_ip = p->L3.ARPv4Header->SrcIP;\r
+\r
+                                               if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(p->MacAddressSrc)))\r
+                                               {\r
+                                                       if ((uint_ip & a->SrcSubnetMask) != (a->SrcIpAddress & a->SrcSubnetMask))\r
+                                                       {\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               arp_match = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if (arp_match == false)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if ((ip->SrcIP & a->SrcSubnetMask) != (a->SrcIpAddress & a->SrcSubnetMask))\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 宛先 IP アドレスのチェック\r
+               if (a->DestIpAddress != 0 || a->DestSubnetMask != 0)\r
+               {\r
+                       if (is_ipv4_packet == false)\r
+                       {\r
+                               if (p->TypeL3 == L3_ARPV4)\r
+                               {\r
+                                       bool arp_match = false;\r
+                                       if (p->L3.ARPv4Header->HardwareSize == 6 &&\r
+                                               Endian16(p->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&\r
+                                               p->L3.ARPv4Header->ProtocolSize == 4 &&\r
+                                               Endian16(p->L3.ARPv4Header->ProtocolType) == 0x0800)\r
+                                       {\r
+                                               UINT uint_ip = p->L3.ARPv4Header->TargetIP;\r
+\r
+                                               if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(p->MacAddressSrc)))\r
+                                               {\r
+                                                       if ((uint_ip & a->DestSubnetMask) != (a->DestIpAddress & a->DestSubnetMask))\r
+                                                       {\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               arp_match = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       if (arp_match == false)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               if ((ip->DstIP & a->DestSubnetMask) != (a->DestIpAddress & a->DestSubnetMask))\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               // IPv6\r
+\r
+               // 送信元 IP アドレスのチェック\r
+               if (IsZeroIP6Addr(&a->SrcIpAddress6) == false ||\r
+                       IsZeroIP6Addr(&a->SrcSubnetMask6) == false)\r
+               {\r
+                       if (is_ipv6_packet == false)\r
+                       {\r
+                               return false;\r
+                       }\r
+                       else\r
+                       {\r
+                               IP a_ip, a_subnet, p_ip;\r
+                               IP and1, and2;\r
+\r
+                               IPv6AddrToIP(&a_ip, &a->SrcIpAddress6);\r
+                               IPv6AddrToIP(&a_subnet, &a->SrcSubnetMask6);\r
+                               IPv6AddrToIP(&p_ip, &ip6->SrcAddress);\r
+\r
+                               IPAnd6(&and1, &a_ip, &a_subnet);\r
+                               IPAnd6(&and2, &p_ip, &a_subnet);\r
+\r
+                               if (CmpIpAddr(&and1, &and2) != 0)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // 宛先 IP アドレスのチェック\r
+               if (IsZeroIP6Addr(&a->DestIpAddress6) == false ||\r
+                       IsZeroIP6Addr(&a->DestSubnetMask6) == false)\r
+               {\r
+                       if (is_ipv6_packet == false)\r
+                       {\r
+                               return false;\r
+                       }\r
+                       else\r
+                       {\r
+                               IP a_ip, a_subnet, p_ip;\r
+                               IP and1, and2;\r
+\r
+                               IPv6AddrToIP(&a_ip, &a->DestIpAddress6);\r
+                               IPv6AddrToIP(&a_subnet, &a->DestSubnetMask6);\r
+                               IPv6AddrToIP(&p_ip, &ip6->DestAddress);\r
+\r
+                               IPAnd6(&and1, &a_ip, &a_subnet);\r
+                               IPAnd6(&and2, &p_ip, &a_subnet);\r
+\r
+                               if (CmpIpAddr(&and1, &and2) != 0)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // IPv4 でも IPv6 でもないパケットはマッチさせない。\r
+       if(is_ipv4_packet == false && is_ipv6_packet==false){\r
+               return false;\r
+       }\r
+\r
+       // プロトコル番号のチェック\r
+       if (a->Protocol != 0)\r
+       {\r
+               if (a->IsIPv6 == false)\r
+               {\r
+                       if (is_ipv4_packet == false)\r
+                       {\r
+                               return false;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (ip->Protocol != a->Protocol)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (is_ipv6_packet == false)\r
+                       {\r
+                               return false;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (p->IPv6HeaderPacketInfo.Protocol != a->Protocol)\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // ポート番号のチェック\r
+       if (a->SrcPortStart != 0 || a->DestPortStart != 0 ||\r
+               a->SrcPortEnd != 0 || a->DestPortEnd != 0)\r
+       {\r
+               if ((a->IsIPv6 == false && is_ipv4_packet == false) ||\r
+                       (a->IsIPv6 && is_ipv6_packet == false))\r
+               {\r
+                       return false;\r
+               }\r
+               else\r
+               {\r
+                       if (p->TypeL4 == L4_TCP)\r
+                       {\r
+                               TCP_HEADER *tcp = p->L4.TCPHeader;\r
+                               // 送信元ポートのチェック\r
+                               if (a->SrcPortStart != 0 || a->SrcPortEnd != 0)\r
+                               {\r
+                                       UINT src_port = Endian16(tcp->SrcPort);\r
+                                       if (src_port < a->SrcPortStart || src_port > a->SrcPortEnd)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+\r
+                               // 宛先ポート番号のチェック\r
+                               if (a->DestPortStart != 0 || a->DestPortEnd != 0)\r
+                               {\r
+                                       UINT dest_port = Endian16(tcp->DstPort);\r
+                                       if (dest_port < a->DestPortStart || dest_port > a->DestPortEnd)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else if (p->TypeL4 == L4_UDP)\r
+                       {\r
+                               UDP_HEADER *udp = p->L4.UDPHeader;\r
+                               // 送信元ポートのチェック\r
+                               if (a->SrcPortStart != 0 || a->SrcPortEnd != 0)\r
+                               {\r
+                                       UINT src_port = Endian16(udp->SrcPort);\r
+                                       if (src_port < a->SrcPortStart || src_port > a->SrcPortEnd)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+\r
+                               // 宛先ポート番号のチェック\r
+                               if (a->DestPortStart != 0 || a->DestPortEnd != 0)\r
+                               {\r
+                                       UINT dest_port = Endian16(udp->DstPort);\r
+                                       if (dest_port < a->DestPortStart || dest_port > a->DestPortEnd)\r
+                                       {\r
+                                               return false;\r
+                                       }\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               // アクセスリストにポート番号が指定されているときは\r
+                               // TCP か UDP 以外のパケットは適用されない\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // TCP コネクションの状態チェック\r
+       if (a->CheckTcpState != false)\r
+       {\r
+               if ((a->IsIPv6 == false && is_ipv4_packet == false) ||\r
+                       (a->IsIPv6 && is_ipv6_packet == false))\r
+               {\r
+                       return false;\r
+               }\r
+               else\r
+               {\r
+                       if(p->TypeL4 == L4_TCP)\r
+                       {\r
+                               // by shimizu\r
+                               TCP_HEADER *tcp = p->L4.TCPHeader;\r
+                               bool est = true;\r
+\r
+                               if (tcp->Flag & TCP_SYN)\r
+                               {\r
+                                       est = false;\r
+                               }\r
+\r
+                               if((MAKEBOOL(a->Established) ^ MAKEBOOL(est)))\r
+                               {\r
+                                       return false;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// フォワードするパケットに対してアクセスリストを適用する\r
+bool ApplyAccessListToForwardPacket(HUB *hub, SESSION *src_session, SESSION *dest_session, PKT *p)\r
+{\r
+       UINT i;\r
+       bool pass = true;       // デフォルトでは通過させる\r
+       bool skip = true;\r
+       // 引数チェック\r
+       if (hub == NULL || src_session == NULL || p == NULL || dest_session == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // 既にチェックされたパケットはアクセスリストを再適用しない。\r
+       if (p->AccessChecked)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       LockList(hub->AccessList);\r
+       {\r
+               for (i = 0;i < LIST_NUM(hub->AccessList);i++)\r
+               {\r
+                       ACCESS *a = LIST_DATA(hub->AccessList, i);\r
+\r
+                       // あて先ユーザー名が指定されているエントリ以降のみを走査する。\r
+                       if (a->DestUsernameHash != 0)\r
+                       {\r
+                               skip = false;\r
+                       }\r
+\r
+                       if (skip == false)\r
+                       {\r
+                               if (IsPacketMaskedByAccessList(src_session, p, a,\r
+                                       ((HUB_PA *)dest_session->PacketAdapter->Param)->UsernameHash,\r
+                                       ((HUB_PA *)dest_session->PacketAdapter->Param)->GroupnameHash))\r
+                               {\r
+                                       // パケットの通過または破棄を決定する\r
+                                       pass = a->Discard ? false : true;\r
+\r
+                                       // リストの走査をここで完了する\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       UnlockList(hub->AccessList);\r
+\r
+       return pass;\r
+}\r
+\r
+// ストアされたパケットに対してアクセスリストを適用する\r
+bool ApplyAccessListToStoredPacket(HUB *hub, SESSION *s, PKT *p)\r
+{\r
+       UINT i;\r
+       bool pass = true;       // デフォルトでは通過させる\r
+       // 引数チェック\r
+       if (hub == NULL || s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterPPPoE)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (proto == 0x8863 || proto == 0x8864)\r
+                       {\r
+                               // PPPoE Filter\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterOSPF)\r
+       {\r
+               if (p->TypeL3 == L3_IPV4)\r
+               {\r
+                       if (p->L3.IPv4Header != NULL)\r
+                       {\r
+                               if (p->L3.IPv4Header->Protocol == 89)\r
+                               {\r
+                                       // OSPF Filter\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterIPv4)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (proto == 0x0800 || proto == 0x0806)\r
+                       {\r
+                               // IPv4 Filter\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterIPv6)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (proto == 0x86dd)\r
+                       {\r
+                               // IPv6 Filter\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterNonIP)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (!(proto == 0x86dd || proto == 0x0800 || proto == 0x0806))\r
+                       {\r
+                               // Non-IP Filter\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (hub->Option != NULL && hub->Option->FilterBPDU)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       if (p->TypeL3 == L3_BPDU)\r
+                       {\r
+                               // BPDU Filter\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       LockList(hub->AccessList);\r
+       {\r
+               for (i = 0;i < LIST_NUM(hub->AccessList);i++)\r
+               {\r
+                       ACCESS *a = LIST_DATA(hub->AccessList, i);\r
+\r
+                       if (a->DestUsernameHash != 0)\r
+                       {\r
+                               // あて先ユーザー名が指定されていたら、そこでリストの走査を中断する。\r
+                               break;\r
+                       }\r
+\r
+                       if (IsPacketMaskedByAccessList(s, p, a, 0, 0))\r
+                       {\r
+                               // パケットの通過または破棄を決定する\r
+                               pass = a->Discard ? false : true;\r
+\r
+                               // ここで処理が決定したパケットはHUBを出るときに走査しない。\r
+                               p->AccessChecked = true;\r
+\r
+                               // 遅延・ジッタ・パケットロスのパラメータのコピー\r
+                               p->Delay = a->Delay;\r
+                               p->Jitter = a->Jitter;\r
+                               p->Loss = a->Loss;\r
+\r
+                               // リストの走査をここで完了する\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       UnlockList(hub->AccessList);\r
+\r
+       return pass;\r
+}\r
+\r
+// アクセスリストの追加\r
+void AddAccessList(HUB *hub, ACCESS *a)\r
+{\r
+       // 引数チェック\r
+       if (hub == NULL || a == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(hub->AccessList);\r
+       {\r
+               ACCESS *access;\r
+               UINT i;\r
+\r
+               // 個数のチェック\r
+               if (LIST_NUM(hub->AccessList) >= MAX_ACCESSLISTS)\r
+               {\r
+                       UnlockList(hub->AccessList);\r
+                       return;\r
+               }\r
+\r
+               access = Malloc(sizeof(ACCESS));\r
+               Copy(access, a, sizeof(ACCESS));\r
+               access->SrcUsernameHash = UsernameToInt(access->SrcUsername);\r
+               access->DestUsernameHash = UsernameToInt(access->DestUsername);\r
+\r
+               // ポート番号補正\r
+               if (access->SrcPortStart != 0)\r
+               {\r
+                       access->SrcPortEnd = MAX(access->SrcPortEnd, access->SrcPortStart);\r
+               }\r
+               if (access->DestPortStart != 0)\r
+               {\r
+                       access->DestPortEnd = MAX(access->DestPortEnd, access->DestPortStart);\r
+               }\r
+\r
+               // 遅延、ジッタ、パケットロスの補正\r
+               access->Delay = MAKESURE(access->Delay, 0, HUB_ACCESSLIST_DELAY_MAX);\r
+               access->Jitter = MAKESURE(access->Jitter, 0, HUB_ACCESSLIST_JITTER_MAX);\r
+               access->Loss = MAKESURE(access->Loss, 0, HUB_ACCESSLIST_LOSS_MAX);\r
+\r
+               Insert(hub->AccessList, access);\r
+\r
+               // ID を振り直す\r
+               for (i = 0;i < LIST_NUM(hub->AccessList);i++)\r
+               {\r
+                       ACCESS *a = LIST_DATA(hub->AccessList, i);\r
+                       a->Id = (i + 1);\r
+               }\r
+       }\r
+       UnlockList(hub->AccessList);\r
+}\r
+\r
+// アクセスリストの初期化\r
+void InitAccessList(HUB *hub)\r
+{\r
+       // 引数チェック\r
+       if (hub == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       hub->AccessList = NewList(CmpAccessList);\r
+}\r
+\r
+// アクセスリストの解放\r
+void FreeAccessList(HUB *hub)\r
+{\r
+       UINT i;\r
+       // 引数チェック\r
+       if (hub == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(hub->AccessList);i++)\r
+       {\r
+               ACCESS *a = LIST_DATA(hub->AccessList, i);\r
+               Free(a);\r
+       }\r
+\r
+       ReleaseList(hub->AccessList);\r
+       hub->AccessList = NULL;\r
+}\r
+\r
+// アクセスリストの比較\r
+int CmpAccessList(void *p1, void *p2)\r
+{\r
+       ACCESS *a1, *a2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       a1 = *(ACCESS **)p1;\r
+       a2 = *(ACCESS **)p2;\r
+       if (a1 == NULL || a2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       // 優先順位別にソートする\r
+       if (a1->Priority > a2->Priority)\r
+       {\r
+               return 1;\r
+       }\r
+       else if (a1->Priority < a2->Priority)\r
+       {\r
+               return -1;\r
+       }\r
+       else if (a1->Discard > a2->Discard)\r
+       {\r
+               return 1;\r
+       }\r
+       else if (a1->Discard < a2->Discard)\r
+       {\r
+               return -1;\r
+       }\r
+       else\r
+       {\r
+               return Cmp(&a1->Active, &a2->Active, sizeof(ACCESS) - 4);\r
+       }\r
+}\r
+\r
+// ユーザー名を UINT に変換\r
+UINT UsernameToInt(char *name)\r
+{\r
+       UCHAR hash[SHA1_SIZE];\r
+       UINT ret;\r
+       char tmp[MAX_USERNAME_LEN + 1];\r
+       // 引数チェック\r
+       if (name == 0 || StrLen(name) == 0)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       StrCpy(tmp, sizeof(tmp), name);\r
+       Trim(tmp);\r
+       StrUpper(tmp);\r
+\r
+       if (StrLen(tmp) == 0)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       Hash(hash, tmp, StrLen(tmp), true);\r
+       Copy(&ret, hash, sizeof(ret));\r
+\r
+       return ret;\r
+}\r
+\r
+// セッションポインタからセッションを検索\r
+SESSION *GetSessionByPtr(HUB *hub, void *ptr)\r
+{\r
+       // 引数チェック\r
+       if (hub == NULL || ptr == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       LockList(hub->SessionList);\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(hub->SessionList);i++)\r
+               {\r
+                       SESSION *s = LIST_DATA(hub->SessionList, i);\r
+                       if (s == (SESSION *)ptr)\r
+                       {\r
+                               // 発見\r
+                               AddRef(s->ref);\r
+                               UnlockList(hub->SessionList);\r
+                               return s;\r
+                       }\r
+               }\r
+       }\r
+       UnlockList(hub->SessionList);\r
+\r
+       return NULL;\r
+}\r
+\r
+// セッション名からセッションを検索\r
+SESSION *GetSessionByName(HUB *hub, char *name)\r
+{\r
+       // 引数チェック\r
+       if (hub == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       LockList(hub->SessionList);\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(hub->SessionList);i++)\r
+               {\r
+                       SESSION *s = LIST_DATA(hub->SessionList, i);\r
+                       if (StrCmpi(s->Name, name) == 0)\r
+                       {\r
+                               // 発見\r
+                               AddRef(s->ref);\r
+                               UnlockList(hub->SessionList);\r
+                               return s;\r
+                       }\r
+               }\r
+       }\r
+       UnlockList(hub->SessionList);\r
+\r
+       return NULL;\r
+}\r
+\r
+// STORM リストのソート\r
+int CompareStormList(void *p1, void *p2)\r
+{\r
+       STORM *s1, *s2;\r
+       UINT r;\r
+       // 引数チェック\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       s1 = *(STORM **)p1;\r
+       s2 = *(STORM **)p2;\r
+       if (s1 == NULL || s2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       r = CmpIpAddr(&s1->DestIp, &s2->DestIp);\r
+       if (r != 0)\r
+       {\r
+               return r;\r
+       }\r
+       r = CmpIpAddr(&s1->SrcIp, &s2->SrcIp);\r
+       if (r != 0)\r
+       {\r
+               return r;\r
+       }\r
+       r = Cmp(s1->MacAddress, s2->MacAddress, 6);\r
+       return r;\r
+}\r
+\r
+// パケットアダプタ初期化\r
+bool HubPaInit(SESSION *s)\r
+{\r
+       // パケットアダプタ情報の初期化\r
+       HUB_PA *pa = ZeroMalloc(sizeof(HUB_PA));\r
+       pa->Cancel = NewCancel();\r
+       pa->PacketQueue = NewQueue();\r
+       pa->Now = Tick64();\r
+       pa->Session = s;\r
+       pa->StormList = NewList(CompareStormList);\r
+       pa->UsernameHash = UsernameToInt(s->Username);\r
+       pa->GroupnameHash = UsernameToInt(s->GroupName);\r
+\r
+       s->PacketAdapter->Param = pa;\r
+\r
+       if (s->Policy->MonitorPort)\r
+       {\r
+               // このポートをモニタリングポートとしてマークする\r
+               pa->MonitorPort = true;\r
+\r
+               // HUB のモニタリングポート一覧にこのセッションを追加する\r
+               LockList(s->Hub->MonitorList);\r
+               {\r
+                       Insert(s->Hub->MonitorList, s);\r
+               }\r
+               UnlockList(s->Hub->MonitorList);\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// パケットアダプタ解放\r
+void HubPaFree(SESSION *s)\r
+{\r
+       HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;\r
+       HUB *hub = s->Hub;\r
+\r
+       if (pa->MonitorPort)\r
+       {\r
+               // HUB のモニタポート一覧からこのセッションを削除する\r
+               LockList(s->Hub->MonitorList);\r
+               {\r
+                       Delete(s->Hub->MonitorList, s);\r
+               }\r
+               UnlockList(s->Hub->MonitorList);\r
+       }\r
+\r
+       // このセッションに関連付けられている MAC アドレステーブルを消去\r
+       LockList(hub->MacTable);\r
+       {\r
+               UINT i, num = LIST_NUM(hub->MacTable);\r
+               LIST *o = NewListFast(NULL);\r
+               for (i = 0;i < num;i++)\r
+               {\r
+                       MAC_TABLE_ENTRY *e = (MAC_TABLE_ENTRY *)LIST_DATA(hub->MacTable, i);\r
+                       if (e->Session == s)\r
+                       {\r
+                               Add(o, e);\r
+                       }\r
+               }\r
+               for (i = 0;i < LIST_NUM(o);i++)\r
+               {\r
+                       MAC_TABLE_ENTRY *e = (MAC_TABLE_ENTRY *)LIST_DATA(o, i);\r
+                       Delete(hub->MacTable, e);\r
+                       Free(e);\r
+               }\r
+               ReleaseList(o);\r
+       }\r
+       {\r
+               UINT i, num = LIST_NUM(hub->IpTable);\r
+               LIST *o = NewListFast(NULL);\r
+               for (i = 0;i < num;i++)\r
+               {\r
+                       IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);\r
+                       if (e->Session == s)\r
+                       {\r
+                               Add(o, e);\r
+                       }\r
+               }\r
+               for (i = 0;i < LIST_NUM(o);i++)\r
+               {\r
+                       IP_TABLE_ENTRY *e = LIST_DATA(o, i);\r
+                       Delete(hub->IpTable, e);\r
+                       Free(e);\r
+               }\r
+               ReleaseList(o);\r
+       }\r
+       UnlockList(hub->MacTable);\r
+\r
+       // STORM リストを解放\r
+       LockList(pa->StormList);\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(pa->StormList);i++)\r
+               {\r
+                       STORM *s = (STORM *)LIST_DATA(pa->StormList, i);\r
+                       Free(s);\r
+               }\r
+               DeleteAll(pa->StormList);\r
+       }\r
+       UnlockList(pa->StormList);\r
+\r
+       ReleaseList(pa->StormList);\r
+\r
+       // キューに残っているパケットを解放\r
+       LockQueue(pa->PacketQueue);\r
+       {\r
+               BLOCK *b;\r
+\r
+               while (b = GetNext(pa->PacketQueue))\r
+               {\r
+                       // ブロックの解放\r
+                       FreeBlock(b);\r
+               }\r
+       }\r
+       UnlockQueue(pa->PacketQueue);\r
+\r
+       // キューを解放\r
+       ReleaseQueue(pa->PacketQueue);\r
+\r
+       // キャンセルオブジェクトの解放\r
+       ReleaseCancel(pa->Cancel);\r
+\r
+       // パケットアダプタ情報の解放\r
+       Free(pa);\r
+       s->PacketAdapter->Param = NULL;\r
+}\r
+\r
+// キャンセルオブジェクトの取得\r
+CANCEL *HubPaGetCancel(SESSION *s)\r
+{\r
+       HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;\r
+\r
+       AddRef(pa->Cancel->ref);\r
+       return pa->Cancel;\r
+}\r
+\r
+// 次の送信予定パケットの取得\r
+UINT HubPaGetNextPacket(SESSION *s, void **data)\r
+{\r
+       UINT ret = 0;\r
+       HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;\r
+\r
+       // キューの先頭から 1 つ取得する\r
+       LockQueue(pa->PacketQueue);\r
+       {\r
+               BLOCK *block = GetNext(pa->PacketQueue);\r
+               if (block == NULL)\r
+               {\r
+                       // キュー無し\r
+                       ret = 0;\r
+               }\r
+               else\r
+               {\r
+                       // あった\r
+                       *data = block->Buf;\r
+                       ret = block->Size;\r
+                       // ブロックの構造体のメモリは解放する\r
+                       Free(block);\r
+               }\r
+       }\r
+       UnlockQueue(pa->PacketQueue);\r
+\r
+       return ret;\r
+}\r
+\r
+// パケットの受信\r
+bool HubPaPutPacket(SESSION *s, void *data, UINT size)\r
+{\r
+       PKT *packet;\r
+       HUB_PA *pa = (HUB_PA *)s->PacketAdapter->Param;\r
+       bool b = false;\r
+       HUB *hub;\r
+       bool no_l3 = false;\r
+       LIST *o = NULL;\r
+       UINT i;\r
+       UINT vlan_type_id = 0;\r
+       bool no_look_bpdu_bridge_id = false;\r
+\r
+       hub = s->Hub;\r
+\r
+       pa->Now = Tick64();\r
+\r
+       if (data == NULL)\r
+       {\r
+               // 遅延パケットのチェック\r
+               o = NULL;\r
+               LockList(s->DelayedPacketList);\r
+               {\r
+                       UINT i;\r
+                       if (LIST_NUM(s->DelayedPacketList) >= 1)\r
+                       {\r
+                               UINT64 now = TickHighres64();\r
+                               for (i = 0;i < LIST_NUM(s->DelayedPacketList);i++)\r
+                               {\r
+                                       PKT *p = LIST_DATA(s->DelayedPacketList, i);\r
+\r
+                                       if (now >= p->DelayedForwardTick)\r
+                                       {\r
+                                               if (o == NULL)\r
+                                               {\r
+                                                       o = NewListFast(NULL);\r
+                                               }\r
+\r
+                                               Add(o, p);\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (o != NULL)\r
+                       {\r
+                               for (i = 0;i < LIST_NUM(o);i++)\r
+                               {\r
+                                       PKT *p = LIST_DATA(o, i);\r
+\r
+                                       Delete(s->DelayedPacketList, p);\r
+                               }\r
+                       }\r
+               }\r
+               UnlockList(s->DelayedPacketList);\r
+\r
+               // 遅延パケットがある場合はストアする\r
+               if (o != NULL)\r
+               {\r
+                       for (i = 0;i < LIST_NUM(o);i++)\r
+                       {\r
+                               PKT *p = LIST_DATA(o, i);\r
+\r
+                               StorePacket(s->Hub, s, p);\r
+                       }\r
+\r
+                       ReleaseList(o);\r
+               }\r
+\r
+               // このセッションからのすべてのパケットの受信が完了した\r
+               CancelList(s->CancelList);\r
+\r
+               // イールドする\r
+               if (hub->Option != NULL && hub->Option->YieldAfterStorePacket)\r
+               {\r
+                       YieldCpu();\r
+               }\r
+\r
+               return true;\r
+       }\r
+\r
+       if (hub != NULL && hub->Option != NULL && hub->Option->DisableIPParsing)\r
+       {\r
+               no_l3 = true;\r
+       }\r
+\r
+       if (hub != NULL && hub->Option != NULL)\r
+       {\r
+               vlan_type_id = hub->Option->VlanTypeId;\r
+               no_look_bpdu_bridge_id = hub->Option->NoLookBPDUBridgeId;\r
+       }\r
+\r
+       // VLAN タグを挿入する\r
+       if (s->VLanId != 0)\r
+       {\r
+               VLanInsertTag(&data, &size, s->VLanId);\r
+       }\r
+\r
+       // パケットをパースする\r
+       packet = ParsePacketEx3(data, size, no_l3, vlan_type_id, !no_look_bpdu_bridge_id);\r
+\r
+       if (packet != NULL)\r
+       {\r
+               if (packet->InvalidSourcePacket)\r
+               {\r
+                       // 不正な送信元のパケット\r
+                       FreePacket(packet);\r
+                       packet = NULL;\r
+               }\r
+       }\r
+\r
+       if (packet != NULL)\r
+       {\r
+               // パケットのストア\r
+               StorePacket(s->Hub, s, packet);\r
+       }\r
+       else\r
+       {\r
+               // 不良パケット (正しい MAC フレームではない)\r
+               // であるのでパケットデータを解放する\r
+               Free(data);\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// ブロードキャストストームが発生しないようにチェックするアルゴリズム\r
+// 特定のエンドポイントからのブロードキャストが頻繁に来た場合はフィルタリングする\r
+bool CheckBroadcastStorm(SESSION *s, PKT *p)\r
+{\r
+       IP src_ip, dest_ip;\r
+       HUB_PA *pa;\r
+       UINT64 now = Tick64();\r
+       UINT limit_start_count;\r
+       SESSION *sess = s;\r
+       bool ret = true;\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (s->Policy->NoBroadcastLimiter)\r
+       {\r
+               // ブロードキャスト数の制限無し\r
+               return true;\r
+       }\r
+\r
+       pa = (HUB_PA *)s->PacketAdapter->Param;\r
+\r
+       if (p->TypeL3 == L3_IPV4)\r
+       {\r
+               UINTToIP(&src_ip, p->L3.IPv4Header->SrcIP);\r
+               UINTToIP(&dest_ip, p->L3.IPv4Header->DstIP);\r
+       }\r
+       else if (p->TypeL3 == L3_ARPV4)\r
+       {\r
+               UINTToIP(&src_ip, p->L3.ARPv4Header->SrcIP);\r
+               Zero(&dest_ip, sizeof(IP));\r
+       }\r
+       else if (p->TypeL3 == L3_IPV6)\r
+       {\r
+               IPv6AddrToIP(&src_ip, &p->L3.IPv6Header->SrcAddress);\r
+               IPv6AddrToIP(&dest_ip, &p->L3.IPv6Header->DestAddress);\r
+       }\r
+       else\r
+       {\r
+               Zero(&src_ip, sizeof(IP));\r
+               Zero(&dest_ip, sizeof(IP));\r
+       }\r
+\r
+       // 1 間隔ごとに制限を開始する個数\r
+       limit_start_count = 32;\r
+\r
+       if (s->Hub != NULL && s->Hub->Option->BroadcastStormDetectionThreshold != 0)\r
+       {\r
+               limit_start_count = s->Hub->Option->BroadcastStormDetectionThreshold;\r
+       }\r
+\r
+       LockList(pa->StormList);\r
+       {\r
+               STORM *s;\r
+               UINT num;\r
+               s = SearchStormList(pa, p->MacAddressSrc, &src_ip, &dest_ip);\r
+               if (s == NULL)\r
+               {\r
+                       s = AddStormList(pa, p->MacAddressSrc, &src_ip, &dest_ip);\r
+               }\r
+\r
+               s->CurrentBroadcastNum++;\r
+\r
+               if ((s->CheckStartTick + STORM_CHECK_SPAN) < now ||\r
+                       s->CheckStartTick == 0 || s->CheckStartTick > now)\r
+               {\r
+                       // 一定期間ごとにブロードキャスト数を計測する\r
+                       UINT64 diff_time;\r
+                       if (s->CheckStartTick < now)\r
+                       {\r
+                               diff_time = now - s->CheckStartTick;\r
+                       }\r
+                       else\r
+                       {\r
+                               diff_time = 0;\r
+                       }\r
+                       s->CheckStartTick = now;\r
+                       num = (UINT)((UINT64)s->CurrentBroadcastNum * (UINT64)1000 / (UINT64)STORM_CHECK_SPAN);\r
+                       s->CurrentBroadcastNum = 0;\r
+                       if (num >= limit_start_count)\r
+                       {\r
+                               char ip1[64];\r
+                               char ip2[64];\r
+                               char mac[MAX_SIZE];\r
+                               IPToStr(ip1, sizeof(ip1), &src_ip);\r
+                               IPToStr(ip2, sizeof(ip2), &dest_ip);\r
+                               ret = false;\r
+                               if (s->DiscardValue < STORM_DISCARD_VALUE_END)\r
+                               {\r
+                                       s->DiscardValue = MAX(s->DiscardValue, 1) * 2;\r
+                               }\r
+                               Debug("s->DiscardValue: %u  (%u)\n", s->DiscardValue, num);\r
+\r
+                               MacToStr(mac, sizeof(mac), p->MacAddressSrc);\r
+\r
+                               HLog(sess->Hub, "LH_BCAST_STORM", sess->Name, mac, ip1, ip2, num);\r
+                       }\r
+                       else\r
+                       {\r
+                               if (s->DiscardValue >= 1)\r
+                               {\r
+                                       s->DiscardValue = (UINT)((UINT64)s->DiscardValue / MAX((UINT64)2, (UINT64)diff_time / (UINT64)STORM_CHECK_SPAN));\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if (s->DiscardValue >= STORM_DISCARD_VALUE_START)\r
+               {\r
+                       if (s->DiscardValue >= 128)\r
+                       {\r
+                               ret = false;\r
+                       }\r
+                       else if ((rand() % s->DiscardValue) != 0)\r
+                       {\r
+                               ret = false;\r
+                       }\r
+               }\r
+\r
+       }\r
+       UnlockList(pa->StormList);\r
+\r
+       return ret;\r
+}\r
+\r
+// パケットのストア\r
+void StorePacket(HUB *hub, SESSION *s, PKT *packet)\r
+{\r
+       MAC_TABLE_ENTRY *entry = NULL;\r
+       MAC_TABLE_ENTRY t;\r
+       void *data;\r
+       UINT size;\r
+       bool broadcast_mode;\r
+       HUB_PA *dest_pa;\r
+       SESSION *dest_session;\r
+       TRAFFIC traffic;\r
+       UINT64 now = Tick64();\r
+       // 引数チェック\r
+       if (hub == NULL || packet == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (s != NULL)\r
+       {\r
+               if (((HUB_PA *)s->PacketAdapter->Param)->MonitorPort)\r
+               {\r
+                       // モニタポートからもらったパケットはフォワードしてはならない\r
+                       Free(packet->PacketData);\r
+                       FreePacket(packet);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       // MAC アドレステーブル全体をロック\r
+       LockList(hub->MacTable);\r
+       {\r
+               // フィルタリング\r
+               if (s != NULL && (packet->DelayedForwardTick == 0 && StorePacketFilter(s, packet) == false))\r
+               {\r
+DISCARD_PACKET:\r
+                       // 通過が不許可となったのでパケットを解放する\r
+                       Free(packet->PacketData);\r
+                       FreePacket(packet);\r
+               }\r
+               else // 通過が許可された\r
+               {\r
+                       bool forward_now = true;\r
+\r
+                       if (packet->Loss >= 1)\r
+                       {\r
+                               // パケットロスを発生させる\r
+                               UINT r = rand() % 100;\r
+                               if ((packet->Loss >= 100) || (r < packet->Loss))\r
+                               {\r
+                                       // パケットロス\r
+                                       goto DISCARD_PACKET;\r
+                               }\r
+                       }\r
+\r
+                       if (packet->Delay >= 1)\r
+                       {\r
+                               float delay = (float)packet->Delay;\r
+                               float jitter;\r
+                               UINT delay_uint;\r
+                               bool f = Rand1();\r
+                               if (packet->Jitter == 0)\r
+                               {\r
+                                       jitter = 0;\r
+                               }\r
+                               else\r
+                               {\r
+                                       jitter = (float)(Rand32() % (int)((float)packet->Jitter * delay / 100.0f));\r
+                               }\r
+\r
+                               delay += jitter * (f ? 1 : -1);\r
+                               delay_uint = (UINT)delay;\r
+\r
+                               if (delay_uint >= 1)\r
+                               {\r
+                                       // 遅延を発生させる\r
+                                       forward_now = false;\r
+                                       packet->Loss = packet->Jitter = packet->Delay = 0;\r
+                                       packet->DelayedForwardTick = TickHighres64() + (UINT64)delay_uint;\r
+                                       packet->DelayedSrcSession = s;\r
+\r
+                                       LockList(s->DelayedPacketList);\r
+                                       {\r
+                                               Add(s->DelayedPacketList, packet);\r
+                                       }\r
+                                       UnlockList(s->DelayedPacketList);\r
+                               }\r
+                       }\r
+\r
+                       if (forward_now)\r
+                       {\r
+                               if (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) == 0)\r
+                               {\r
+                                       if (s != NULL)\r
+                                       {\r
+                                               // この HUB 自身が発信しようとしたパケットが外部から入力された\r
+                                               goto DISCARD_PACKET;\r
+                                       }\r
+                               }\r
+                               if (s != NULL && (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) != 0))\r
+                               {\r
+                                       // 送信元 MAC アドレスがテーブルに登録されているかどうか調べる\r
+                                       Copy(t.MacAddress, packet->MacAddressSrc, 6);\r
+                                       if (hub->Option->NoManageVlanId == false)\r
+                                       {\r
+                                               t.VlanId = packet->VlanId;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               t.VlanId = 0;\r
+                                       }\r
+                                       entry = Search(hub->MacTable, &t);\r
+\r
+                                       if (entry == NULL)\r
+                                       {\r
+                                               // 古いエントリを削除する\r
+                                               DeleteExpiredMacTableEntry(hub->MacTable);\r
+\r
+                                               // 登録されていないので登録する\r
+                                               if (s->Policy->MaxMac != 0 || s->Policy->NoBridge)\r
+                                               {\r
+                                                       UINT i, num_mac_for_me = 0;\r
+                                                       UINT limited_count;\r
+\r
+                                                       // 現在このセッションで登録されている MAC アドレス数を調べる\r
+                                                       for (i = 0;i < LIST_NUM(hub->MacTable);i++)\r
+                                                       {\r
+                                                               MAC_TABLE_ENTRY *e = LIST_DATA(hub->MacTable, i);\r
+                                                               if (e->Session == s)\r
+                                                               {\r
+                                                                       num_mac_for_me++;\r
+                                                               }\r
+                                                       }\r
+\r
+                                                       limited_count = 0xffffffff;\r
+                                                       if (s->Policy->NoBridge)\r
+                                                       {\r
+                                                               limited_count = MIN(limited_count, MAC_MIN_LIMIT_COUNT);\r
+                                                       }\r
+                                                       if (s->Policy->MaxMac != 0)\r
+                                                       {\r
+                                                               limited_count = MIN(limited_count, s->Policy->MaxMac);\r
+                                                       }\r
+                                                       limited_count = MAX(limited_count, MAC_MIN_LIMIT_COUNT);\r
+\r
+                                                       if (num_mac_for_me >= limited_count)\r
+                                                       {\r
+                                                               // すでに登録されている MAC アドレス数が上限を超えている\r
+                                                               char mac_str[64];\r
+\r
+                                                               if (s != NULL)\r
+                                                               {\r
+                                                                       MacToStr(mac_str, sizeof(mac_str), packet->MacAddressSrc);\r
+                                                                       if (s->Policy->NoBridge)\r
+                                                                       {\r
+                                                                               HLog(hub, "LH_BRIDGE_LIMIT", s->Name, mac_str, num_mac_for_me, limited_count);\r
+                                                                       }\r
+                                                                       else\r
+                                                                       {\r
+                                                                               HLog(hub, "LH_MAC_LIMIT", s->Name, mac_str, num_mac_for_me, limited_count);\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               goto DISCARD_PACKET;    // パケット破棄\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (LIST_NUM(hub->MacTable) >= MAX_MAC_TABLES)\r
+                                               {\r
+                                                       // MAC テーブルデータベースが最大件数を超えたので\r
+                                                       // 最も古いテーブルを削除する\r
+                                                       UINT i;\r
+                                                       UINT64 old_time = 0xffffffffffffffffULL;\r
+                                                       MAC_TABLE_ENTRY *old_entry = NULL;\r
+                                                       for (i = 0;i < LIST_NUM(hub->MacTable);i++)\r
+                                                       {\r
+                                                               MAC_TABLE_ENTRY *e = LIST_DATA(hub->MacTable, i);\r
+                                                               if (e->UpdatedTime <= old_time)\r
+                                                               {\r
+                                                                       old_time = e->CreatedTime;\r
+                                                                       old_entry = e;\r
+                                                               }\r
+                                                       }\r
+                                                       if (old_entry != NULL)\r
+                                                       {\r
+                                                               Delete(hub->MacTable, old_entry);\r
+                                                               Free(old_entry);\r
+                                                       }\r
+                                               }\r
+\r
+                                               entry = ZeroMalloc(sizeof(MAC_TABLE_ENTRY));\r
+                                               entry->HubPa = (HUB_PA *)s->PacketAdapter->Param;\r
+                                               Copy(entry->MacAddress, packet->MacAddressSrc, 6);\r
+                                               if (hub->Option->NoManageVlanId == false)\r
+                                               {\r
+                                                       entry->VlanId = packet->VlanId;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       entry->VlanId = 0;\r
+                                               }\r
+                                               entry->Session = s;\r
+                                               entry->UpdatedTime = entry->CreatedTime = now;\r
+\r
+                                               Insert(hub->MacTable, entry);\r
+\r
+                                               if (hub->Option->NoMacAddressLog == false)\r
+                                               {\r
+                                                       // デバッグ表示\r
+                                                       char mac_address[32];\r
+\r
+                                                       if (s != NULL)\r
+                                                       {\r
+                                                               MacToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc);\r
+//                                                             Debug("Register MAC Address %s to Session %X.\n", mac_address, s);\r
+\r
+                                                               if (packet->VlanId == 0)\r
+                                                               {\r
+                                                                       HLog(hub, "LH_MAC_REGIST", s->Name, mac_address);\r
+                                                               }\r
+                                                               else\r
+                                                               {\r
+                                                                       HLog(hub, "LH_MAC_REGIST_VLAN", s->Name, mac_address, packet->VlanId);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               if (entry->Session == s)\r
+                                               {\r
+                                                       // 既に登録されているので何もしない\r
+                                                       entry->UpdatedTime = now;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // 既に登録されていて自分のセッション以外である\r
+                                                       if (s->Policy->CheckMac && (Cmp(packet->MacAddressSrc, hub->HubMacAddr, 6) != 0) &&\r
+                                                               ((entry->UpdatedTime + MAC_TABLE_EXCLUSIVE_TIME) >= now))\r
+                                                       {\r
+                                                               UCHAR *mac = packet->MacAddressSrc;\r
+                                                               if (hub->Option != NULL && hub->Option->FixForDLinkBPDU &&\r
+                                                                       (mac[0] == 0x00 && mac[1] == 0x80 && mac[2] == 0xc8 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00) ||\r
+                                                                       (mac[0] == 0x00 && mac[1] == 0x0d && mac[2] == 0x88 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00))\r
+                                                               {\r
+                                                                       // D-Link 用バグ対策。D-Link のスパニングツリーパケットは上記アドレスから送出される。\r
+                                                                       // ローカルブリッジ時の CheckMac オプションが悪影響を与えることがある。\r
+                                                                       // そこで例外的に処理。\r
+                                                                       UCHAR hash[MD5_SIZE];\r
+                                                                       UINT64 tick_diff = Tick64() - s->LastDLinkSTPPacketSendTick;\r
+\r
+                                                                       Hash(hash, packet->PacketData, packet->PacketSize, false);\r
+\r
+                                                                       if ((s->LastDLinkSTPPacketSendTick != 0) &&\r
+                                                                               (tick_diff < 750ULL) &&\r
+                                                                               (Cmp(hash, s->LastDLinkSTPPacketDataHash, MD5_SIZE) == 0))\r
+                                                                       {\r
+                                                                               // 750ms より前に同一パケットを送信した場合は破棄\r
+                                                                               Debug("D-Link Discard %u\n", (UINT)tick_diff);\r
+                                                                               goto DISCARD_PACKET;    // パケット破棄\r
+                                                                       }\r
+                                                                       else\r
+                                                                       {\r
+                                                                               goto UPDATE_FDB;\r
+                                                                       }\r
+                                                               }\r
+                                                               else\r
+                                                               {\r
+                                                                       if (0)\r
+                                                                       {\r
+                                                                               // CheckMac ポリシーが有効な場合\r
+                                                                               // 別のセッションが同じ MAC アドレスを持っていることは禁止されている\r
+                                                                               // (2 バイト目が 0xAE の場合はこのチェックを行わない)\r
+                                                                               char mac_address[32];\r
+                                                                               BinToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc, 6);\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               goto DISCARD_PACKET;    // パケット破棄\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               // MAC アドレステーブルのセッションと HUB_PA を書き換える\r
+                                                               char mac_address[32];\r
+UPDATE_FDB:\r
+                                                               BinToStr(mac_address, sizeof(mac_address), packet->MacAddressSrc, 6);\r
+\r
+                                                               entry->Session = s;\r
+                                                               entry->HubPa = (HUB_PA *)s->PacketAdapter->Param;\r
+                                                               entry->UpdatedTime = entry->CreatedTime = now;\r
+\r
+                                                               if (1)\r
+                                                               {\r
+                                                                       // デバッグ表示\r
+                                                                       char mac_address[32];\r
+\r
+                                                                       if (s != NULL)\r
+                                                                       {\r
+                                                                               MacToStr(mac_address, sizeof(mac_address), packet->MacHeader->SrcAddress);\r
+                                                                               Debug("Register MAC Address %s to Session %X.\n", mac_address, s);\r
+                                                                               if (packet->VlanId == 0)\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_MAC_REGIST", s->Name, mac_address);\r
+                                                                               }\r
+                                                                               else\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_MAC_REGIST_VLAN", s->Name, mac_address, packet->VlanId);\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               broadcast_mode = false;\r
+                               dest_pa = NULL;\r
+                               dest_session = NULL;\r
+\r
+                               if (packet->BroadcastPacket)\r
+                               {\r
+                                       // ブロードキャストパケット\r
+                                       broadcast_mode = true;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // 宛先 MAC アドレスがテーブルに登録されているかどうか調べる\r
+                                       Copy(t.MacAddress, packet->MacAddressDest, 6);\r
+                                       if (hub->Option->NoManageVlanId == false)\r
+                                       {\r
+                                               t.VlanId = packet->VlanId;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               t.VlanId = 0;\r
+                                       }\r
+                                       entry = Search(hub->MacTable, &t);\r
+\r
+                                       if (entry == NULL)\r
+                                       {\r
+                                               // 宛先が見つからないのでブロードキャストする\r
+                                               broadcast_mode = true;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               if (entry->Session != s)\r
+                                               {\r
+                                                       // 宛先が見つかった\r
+                                                       dest_pa = entry->HubPa;\r
+                                                       dest_session = entry->Session;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       // 宛先が自分自身である不正なパケット\r
+                                                       goto DISCARD_PACKET;\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               if (s != NULL && hub->Option->NoIpTable == false)\r
+                               {\r
+                                       if (packet->TypeL3 == L3_IPV6)\r
+                                       {\r
+                                               // IPv6 パケット\r
+                                               IP ip;\r
+                                               bool b = true;\r
+                                               UINT ip_type;\r
+                                               bool dhcp_or_ra = false;\r
+\r
+                                               IPv6AddrToIP(&ip, &packet->L3.IPv6Header->SrcAddress);\r
+                                               ip_type = GetIPv6AddrType(&packet->L3.IPv6Header->SrcAddress);\r
+\r
+                                               if (!(ip_type & IPV6_ADDR_UNICAST))\r
+                                               {\r
+                                                       // マルチキャストアドレス\r
+                                                       b = false;\r
+                                               }\r
+                                               else if ((ip_type & IPV6_ADDR_LOOPBACK) || (ip_type & IPV6_ADDR_ZERO))\r
+                                               {\r
+                                                       // ループバックアドレスまたは All-Zero アドレス\r
+                                                       b = false;\r
+                                               }\r
+\r
+                                               if (packet->TypeL4 == L4_ICMPV6)\r
+                                               {\r
+                                                       if (packet->ICMPv6HeaderPacketInfo.Type == 133 ||\r
+                                                               packet->ICMPv6HeaderPacketInfo.Type == 134)\r
+                                                       {\r
+                                                               // ICMPv6 RS/RA\r
+                                                               dhcp_or_ra = true;\r
+                                                       }\r
+                                               }\r
+                                               else if (packet->TypeL4 == L4_UDP)\r
+                                               {\r
+                                                       if (Endian16(packet->L4.UDPHeader->DstPort) == 546 ||\r
+                                                               Endian16(packet->L4.UDPHeader->DstPort) == 547)\r
+                                                       {\r
+                                                               // DHCPv6\r
+                                                               dhcp_or_ra = true;\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (IsHubMacAddress(packet->MacAddressSrc) &&\r
+                                                       IsHubIpAddress64(&packet->L3.IPv6Header->SrcAddress))\r
+                                               {\r
+                                                       // 仮想 HUB のポーリング用送信元アドレス\r
+                                                       b = false;\r
+                                               }\r
+\r
+                                               if (b)\r
+                                               {\r
+                                                       // ICMPv6 RS/RA および DHCPv6 以外のパケット\r
+                                                       IP_TABLE_ENTRY t, *e;\r
+\r
+                                                       Copy(&t.Ip, &ip, sizeof(IP));\r
+\r
+                                                       // 既存のテーブルに登録されているかどうかチェック\r
+                                                       e = Search(hub->IpTable, &t);\r
+\r
+                                                       if (e == NULL)\r
+                                                       {\r
+                                                               // 登録されていないので登録する\r
+                                                               if (s->Policy->NoRoutingV6 || s->Policy->MaxIPv6 != 0)\r
+                                                               {\r
+                                                                       UINT i, num_ip_for_me = 0;\r
+                                                                       UINT limited_count = 0xffffffff;\r
+\r
+                                                                       for (i = 0;i < LIST_NUM(hub->IpTable);i++)\r
+                                                                       {\r
+                                                                               IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);\r
+\r
+                                                                               if (e->Session == s)\r
+                                                                               {\r
+                                                                                       if (IsIP6(&e->Ip))\r
+                                                                                       {\r
+                                                                                               num_ip_for_me++;\r
+                                                                                       }\r
+                                                                               }\r
+                                                                       }\r
+\r
+                                                                       if (s->Policy->NoRoutingV6)\r
+                                                                       {\r
+                                                                               limited_count = MIN(limited_count, IP_LIMIT_WHEN_NO_ROUTING_V6);\r
+                                                                       }\r
+                                                                       if (s->Policy->MaxIPv6 != 0)\r
+                                                                       {\r
+                                                                               limited_count = MIN(limited_count, s->Policy->MaxIPv6);\r
+                                                                       }\r
+                                                                       limited_count = MAX(limited_count, IP_MIN_LIMIT_COUNT_V6);\r
+\r
+                                                                       if (dhcp_or_ra)\r
+                                                                       {\r
+                                                                               limited_count = 0xffffffff;\r
+                                                                       }\r
+\r
+                                                                       if (num_ip_for_me >= limited_count)\r
+                                                                       {\r
+                                                                               // 使用できる IP アドレスの上限を超えているので\r
+                                                                               // このパケットを破棄する\r
+                                                                               char tmp[64];\r
+                                                                               IPToStr(tmp, sizeof(tmp), &ip);\r
+                                                                               if (s->Policy->NoRoutingV6 == false)\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_IP_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);\r
+                                                                               }\r
+                                                                               else\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_ROUTING_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);\r
+                                                                               }\r
+                                                                               goto DISCARD_PACKET;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (IsIPManagementTargetForHUB(&ip, hub))\r
+                                                               {\r
+                                                                       // エントリ作成\r
+                                                                       e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));\r
+                                                                       e->CreatedTime = e->UpdatedTime = now;\r
+                                                                       e->DhcpAllocated = false;\r
+                                                                       Copy(&e->Ip, &ip, sizeof(IP));\r
+                                                                       Copy(e->MacAddress, packet->MacAddressSrc, 6);\r
+                                                                       e->Session = s;\r
+\r
+                                                                       DeleteExpiredIpTableEntry(hub->IpTable);\r
+\r
+                                                                       if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)\r
+                                                                       {\r
+                                                                               // 古い IP テーブルエントリを削除する\r
+                                                                               DeleteOldIpTableEntry(hub->IpTable);\r
+                                                                       }\r
+\r
+                                                                       Insert(hub->IpTable, e);\r
+\r
+                                                                       if (0)\r
+                                                                       {\r
+                                                                               char ip_address[64];\r
+                                                                               IPToStr(ip_address, sizeof(ip_address), &ip);\r
+                                                                               Debug("Registered IP Address %s to Session %X.\n",\r
+                                                                                       ip_address, s);\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               if (e->Session == s)\r
+                                                               {\r
+                                                                       // 自分のセッションであるので何もしない\r
+                                                                       // 更新日時を上書きする\r
+                                                                       e->UpdatedTime = now;\r
+                                                                       Copy(e->MacAddress, packet->MacAddressSrc, 6);\r
+                                                               }\r
+                                                               else\r
+                                                               {\r
+                                                                       // 別のセッションが以前この IP アドレスを使っていた\r
+                                                                       if ((s->Policy->CheckIPv6) &&\r
+                                                                               ((e->UpdatedTime + IP_TABLE_EXCLUSIVE_TIME) >= now))\r
+                                                                       {\r
+                                                                               // 他のセッションがこの IP アドレスを使っているので\r
+                                                                               // パケットを破棄する\r
+                                                                               char ip_address[32];\r
+                                                                               IPToStr(ip_address, sizeof(ip_address), &ip);\r
+\r
+                                                                               Debug("IP Address %s is Already used by Session %X.\n",\r
+                                                                                       ip_address, s);\r
+\r
+                                                                               HLog(hub, "LH_IP_CONFLICT", s->Name, ip_address, e->Session->Name);\r
+\r
+                                                                               goto DISCARD_PACKET;\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               if (\r
+                                       (s != NULL) &&\r
+                                       (hub->Option->NoIpTable == false) &&\r
+                                       (\r
+                                               (packet->TypeL3 == L3_IPV4 ||\r
+                                                       (packet->TypeL3 == L3_ARPV4 && packet->L3.ARPv4Header->HardwareSize == 6 &&\r
+                                                       Endian16(packet->L3.ARPv4Header->HardwareType) == ARP_HARDWARE_TYPE_ETHERNET &&\r
+                                                       packet->L3.ARPv4Header->ProtocolSize == 4 &&\r
+                                                       Endian16(packet->L3.ARPv4Header->ProtocolType) == 0x0800)\r
+                                               ) &&\r
+                                               (packet->TypeL7 != L7_DHCPV4)\r
+                                       )\r
+                                       ) // DHCP パケット以外\r
+                               {\r
+                                       // IP パケットまたは ARP 応答パケットの場合は IP アドレステーブルを検索する\r
+                                       IP_TABLE_ENTRY t, *e;\r
+                                       IP ip;\r
+                                       UINT uint_ip = 0;\r
+\r
+                                       if (packet->TypeL3 == L3_IPV4)\r
+                                       {\r
+                                               uint_ip = packet->L3.IPv4Header->SrcIP;\r
+                                       }\r
+                                       else if (packet->TypeL3 == L3_ARPV4)\r
+                                       {\r
+                                               uint_ip = packet->L3.ARPv4Header->SrcIP;\r
+                                       }\r
+\r
+                                       if (uint_ip != 0 && uint_ip != 0xffffffff && !(IsHubIpAddress32(uint_ip) && IsHubMacAddress(packet->MacAddressSrc)))\r
+                                       {\r
+                                               UINTToIP(&ip, uint_ip);\r
+                                               Copy(&t.Ip, &ip, sizeof(IP));\r
+\r
+                                               // 既存のテーブルに登録されているかどうかチェック\r
+                                               e = Search(hub->IpTable, &t);\r
+\r
+                                               if (e == NULL)\r
+                                               {\r
+                                                       // 登録されていないので登録する\r
+                                                       if (s->Policy->DHCPForce)\r
+                                                       {\r
+                                                               char ipstr[MAX_SIZE];\r
+\r
+                                                               // DHCP サーバーによって割り当てられた IP アドレスではない\r
+                                                               // のでこのパケットを破棄する\r
+                                                               IPToStr32(ipstr, sizeof(ipstr), uint_ip);\r
+                                                               HLog(hub, "LH_DHCP_FORCE", s->Name, ipstr);\r
+                                                               goto DISCARD_PACKET;\r
+                                                       }\r
+\r
+       //                                              if (packet->TypeL3 == L3_ARPV4)\r
+                                                       {\r
+                                                               // すでにこのセッションで登録されている個数を調べる\r
+                                                               if (s->Policy->NoRouting || s->Policy->MaxIP != 0)\r
+                                                               {\r
+                                                                       UINT i, num_ip_for_me = 0;\r
+                                                                       UINT limited_count = 0xffffffff;\r
+\r
+                                                                       for (i = 0;i < LIST_NUM(hub->IpTable);i++)\r
+                                                                       {\r
+                                                                               IP_TABLE_ENTRY *e = LIST_DATA(hub->IpTable, i);\r
+\r
+                                                                               if (e->Session == s)\r
+                                                                               {\r
+                                                                                       if (IsIP4(&e->Ip))\r
+                                                                                       {\r
+                                                                                               num_ip_for_me++;\r
+                                                                                       }\r
+                                                                               }\r
+                                                                       }\r
+\r
+                                                                       if (s->Policy->NoRouting)\r
+                                                                       {\r
+                                                                               limited_count = MIN(limited_count, IP_MIN_LIMIT_COUNT);\r
+                                                                       }\r
+                                                                       if (s->Policy->MaxIP != 0)\r
+                                                                       {\r
+                                                                               limited_count = MIN(limited_count, s->Policy->MaxIP);\r
+                                                                       }\r
+                                                                       limited_count = MAX(limited_count, IP_MIN_LIMIT_COUNT);\r
+\r
+                                                                       if (num_ip_for_me >= limited_count)\r
+                                                                       {\r
+                                                                               // 使用できる IP アドレスの上限を超えているので\r
+                                                                               // このパケットを破棄する\r
+                                                                               char tmp[64];\r
+                                                                               IPToStr32(tmp, sizeof(tmp), uint_ip);\r
+                                                                               if (s->Policy->NoRouting == false)\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_IP_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);\r
+                                                                               }\r
+                                                                               else\r
+                                                                               {\r
+                                                                                       HLog(hub, "LH_ROUTING_LIMIT", s->Name, tmp, num_ip_for_me, limited_count);\r
+                                                                               }\r
+                                                                               goto DISCARD_PACKET;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (IsIPManagementTargetForHUB(&ip, hub))\r
+                                                               {\r
+                                                                       // エントリ作成\r
+                                                                       e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));\r
+                                                                       e->CreatedTime = e->UpdatedTime = now;\r
+                                                                       e->DhcpAllocated = false;\r
+                                                                       Copy(&e->Ip, &ip, sizeof(IP));\r
+                                                                       Copy(e->MacAddress, packet->MacAddressSrc, 6);\r
+                                                                       e->Session = s;\r
+\r
+                                                                       DeleteExpiredIpTableEntry(hub->IpTable);\r
+\r
+                                                                       if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)\r
+                                                                       {\r
+                                                                               // 古い IP テーブルエントリを削除する\r
+                                                                               DeleteOldIpTableEntry(hub->IpTable);\r
+                                                                       }\r
+\r
+                                                                       Insert(hub->IpTable, e);\r
+\r
+                                                                       if (0)\r
+                                                                       {\r
+                                                                               char ip_address[64];\r
+                                                                               IPToStr(ip_address, sizeof(ip_address), &ip);\r
+                                                                               Debug("Registered IP Address %s to Session %X.\n",\r
+                                                                                       ip_address, s);\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (e->Session == s)\r
+                                                       {\r
+                                                               // 自分のセッションであるので何もしない\r
+                                                               // 更新日時を上書きする\r
+                                                               e->UpdatedTime = now;\r
+                                                               Copy(e->MacAddress, packet->MacAddressSrc, 6);\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               // 別のセッションが以前この IP アドレスを使っていた\r
+                                                               if ((s->Policy->CheckIP || s->Policy->DHCPForce) &&\r
+                                                                       ((e->UpdatedTime + IP_TABLE_EXCLUSIVE_TIME) >= now))\r
+                                                               {\r
+                                                                       // 他のセッションがこの IP アドレスを使っているので\r
+                                                                       // パケットを破棄する\r
+                                                                       char ip_address[32];\r
+                                                                       IPToStr(ip_address, sizeof(ip_address), &ip);\r
+\r
+                                                                       Debug("IP Address %s is Already used by Session %X.\n",\r
+                                                                               ip_address, s);\r
+\r
+                                                                       HLog(hub, "LH_IP_CONFLICT", s->Name, ip_address, e->Session->Name);\r
+\r
+                                                                       goto DISCARD_PACKET;\r
+                                                               }\r
+\r
+                                                               if (s->Policy->DHCPForce)\r
+                                                               {\r
+                                                                       if (e->DhcpAllocated == false)\r
+                                                                       {\r
+                                                                               char ipstr[MAX_SIZE];\r
+\r
+                                                                               // DHCP サーバーによって割り当てられた IP アドレスではない\r
+                                                                               // のでこのパケットを破棄する\r
+                                                                               IPToStr32(ipstr, sizeof(ipstr), uint_ip);\r
+                                                                               HLog(hub, "LH_DHCP_FORCE", s->Name, ipstr);\r
+                                                                               goto DISCARD_PACKET;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               // エントリを上書きする\r
+                                                               e->Session = s;\r
+                                                               e->UpdatedTime = now;\r
+                                                               Copy(e->MacAddress, packet->MacAddressSrc, 6);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+\r
+                               if (s != NULL && broadcast_mode)\r
+                               {\r
+                                       // ブロードキャストパケットのループや\r
+                                       // 大量のブロードキャストの発生を防止するため\r
+                                       // Broadcast Storm 回避アルゴリズムを呼び出す\r
+                                       if (CheckBroadcastStorm(s, packet) == false)\r
+                                       {\r
+                                               goto DISCARD_PACKET;\r
+                                       }\r
+                               }\r
+\r
+                               // トラフィック加算\r
+                               Zero(&traffic, sizeof(traffic));\r
+                               if (packet->BroadcastPacket)\r
+                               {\r
+                                       // ブロードキャスト\r
+                                       traffic.Send.BroadcastBytes = packet->PacketSize;\r
+                                       traffic.Send.BroadcastCount = 1;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // ユニキャスト\r
+                                       traffic.Send.UnicastBytes = packet->PacketSize;\r
+                                       traffic.Send.UnicastCount = 1;\r
+                               }\r
+\r
+                               if (s != NULL)\r
+                               {\r
+                                       AddTrafficForSession(s, &traffic);\r
+                               }\r
+\r
+                               // トラフィック情報の Send と Recv を反転\r
+                               Copy(&traffic.Recv, &traffic.Send, sizeof(TRAFFIC_ENTRY));\r
+                               Zero(&traffic.Send, sizeof(TRAFFIC_ENTRY));\r
+\r
+                               // HUB のモニタポートにこのパケットをブロードキャストする\r
+                               if (hub->MonitorList->num_item != 0)\r
+                               {\r
+                                       LockList(hub->MonitorList);\r
+                                       {\r
+                                               UINT i;\r
+                                               void *data;\r
+                                               UINT size = packet->PacketSize;\r
+                                               for (i = 0;i < LIST_NUM(hub->MonitorList);i++)\r
+                                               {\r
+                                                       SESSION *monitor_session = (SESSION *)LIST_DATA(hub->MonitorList, i);\r
+\r
+                                                       // パケットをフラッディング\r
+                                                       if (monitor_session->PacketAdapter->Param != NULL)\r
+                                                       {\r
+                                                               data = MallocFast(size);\r
+                                                               Copy(data, packet->PacketData, size);\r
+                                                               StorePacketToHubPa((HUB_PA *)monitor_session->PacketAdapter->Param,\r
+                                                                       s, data, size, packet);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       UnlockList(hub->MonitorList);\r
+                               }\r
+\r
+                               if (broadcast_mode == false)\r
+                               {\r
+                                       if (dest_pa != NULL)\r
+                                       {\r
+                                               if (dest_session->Policy->NoIPv6DefaultRouterInRA ||\r
+                                                       (dest_session->Policy->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session) ||\r
+                                                       (hub->Option->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session))\r
+                                               {\r
+                                                       DeleteIPv6DefaultRouterInRA(packet);\r
+                                               }\r
+                                               if (dest_session->Policy->RSandRAFilter)\r
+                                               {\r
+                                                       if (packet->TypeL3 == L3_IPV6 &&\r
+                                                               packet->TypeL4 == L4_ICMPV6 &&\r
+                                                               (packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||\r
+                                                                packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->DHCPFilter)\r
+                                               {\r
+                                                       if (packet->TypeL3 == L3_IPV4 &&\r
+                                                               packet->TypeL4 == L4_UDP &&\r
+                                                               packet->TypeL7 == L7_DHCPV4)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->DHCPv6Filter)\r
+                                               {\r
+                                                       if (packet->TypeL3 == L3_IPV6 &&\r
+                                                               packet->TypeL4 == L4_UDP &&\r
+                                                               (Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->ArpDhcpOnly)\r
+                                               {\r
+                                                       if (packet->BroadcastPacket)\r
+                                                       {\r
+                                                               bool b = true;\r
+\r
+                                                               if (packet->TypeL3 == L3_IPV4 &&\r
+                                                                       packet->TypeL4 == L4_UDP &&\r
+                                                                       packet->TypeL7 == L7_DHCPV4)\r
+                                                               {\r
+                                                                       b = false;\r
+                                                               }\r
+                                                               else if (packet->TypeL3 == L3_ARPV4)\r
+                                                               {\r
+                                                                       b = false;\r
+                                                               }\r
+                                                               else if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                       packet->TypeL4 == L4_UDP &&\r
+                                                                       (Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))\r
+                                                               {\r
+                                                                       b = false;\r
+                                                               }\r
+                                                               else if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                       packet->TypeL4 == L4_ICMPV6)\r
+                                                               {\r
+                                                                       b = false;\r
+                                                               }\r
+\r
+                                                               if (b)\r
+                                                               {\r
+                                                                       goto DISCARD_UNICAST_PACKET;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->FilterIPv4)\r
+                                               {\r
+                                                       if (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->FilterIPv6)\r
+                                               {\r
+                                                       if (packet->TypeL3 == L3_IPV6)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+                                               if (dest_session->Policy->FilterNonIP)\r
+                                               {\r
+                                                       if (packet->TypeL3 != L3_IPV4 && packet->TypeL3 != L3_ARPV4 && packet->TypeL3 != L3_IPV6)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (s != NULL &&\r
+                                                       (packet->BroadcastPacket == false &&\r
+                                                       s->Policy->PrivacyFilter &&\r
+                                                       dest_session->Policy->PrivacyFilter)\r
+                                                       )\r
+                                               {\r
+                                                       // プライバシーフィルタ\r
+                                                       if (packet->TypeL3 != L3_ARPV4)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+\r
+                                               if (s != NULL)\r
+                                               {\r
+                                                       if (Cmp(packet->MacAddressSrc, s->Hub->HubMacAddr, 6) == 0 ||\r
+                                                               Cmp(packet->MacAddressDest, s->Hub->HubMacAddr, 6) == 0)\r
+                                                       {\r
+                                                               goto DISCARD_UNICAST_PACKET;\r
+                                                       }\r
+                                               }\r
+\r
+                                               // パケットログをとる\r
+                                               if (s != NULL)\r
+                                               {\r
+                                                       PacketLog(s->Hub, s, dest_session, packet);\r
+                                               }\r
+\r
+                                               // 宛先 HUB_PA にストアする\r
+                                               StorePacketToHubPa(dest_pa, s, packet->PacketData, packet->PacketSize, packet);\r
+\r
+                                               // トラフィック加算\r
+                                               AddTrafficForSession(dest_session, &traffic);\r
+                                       }\r
+                                       else\r
+                                       {\r
+DISCARD_UNICAST_PACKET:\r
+                                               Free(packet->PacketData);\r
+                                       }\r
+                               }\r
+                               else\r
+                               {\r
+                                       // すべてのセッションにストアする\r
+                                       LockList(hub->SessionList);\r
+                                       {\r
+                                               UINT i, num = LIST_NUM(hub->SessionList);\r
+                                               for (i = 0;i < num;i++)\r
+                                               {\r
+                                                       SESSION *dest_session = LIST_DATA(hub->SessionList, i);\r
+                                                       HUB_PA *dest_pa = (HUB_PA *)dest_session->PacketAdapter->Param;\r
+                                                       bool discard = false;\r
+\r
+                                                       if (dest_session != s)\r
+                                                       {\r
+                                                               bool delete_default_router_in_ra = false;\r
+\r
+                                                               if (dest_session->VLanId != 0 && packet->TypeL3 == L3_TAGVLAN &&\r
+                                                                       packet->VlanId != dest_session->VLanId)\r
+                                                               {\r
+                                                                       discard = true;\r
+                                                               }\r
+\r
+                                                               if (dest_session->Policy->NoIPv6DefaultRouterInRA ||\r
+                                                                       (dest_session->Policy->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session) ||\r
+                                                                       (hub->Option->NoIPv6DefaultRouterInRAWhenIPv6 && dest_session->IPv6Session))\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV6 && packet->TypeL4 == L4_ICMPV6 &&\r
+                                                                               (packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))\r
+                                                                       {\r
+                                                                               if (packet->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime != 0)\r
+                                                                               {\r
+                                                                                       delete_default_router_in_ra = true;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                               if (dest_session->Policy->RSandRAFilter)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                               packet->TypeL4 == L4_ICMPV6 &&\r
+                                                                               (packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||\r
+                                                                                packet->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (dest_session->Policy->DHCPFilter)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV4 &&\r
+                                                                               packet->TypeL4 == L4_UDP &&\r
+                                                                               packet->TypeL7 == L7_DHCPV4)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (dest_session->Policy->DHCPv6Filter)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                               packet->TypeL4 == L4_UDP &&\r
+                                                                               (Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (dest_session->Policy->ArpDhcpOnly)\r
+                                                               {\r
+                                                                       if (packet->BroadcastPacket)\r
+                                                                       {\r
+                                                                               bool b = true;\r
+\r
+                                                                               if (packet->TypeL3 == L3_IPV4 &&\r
+                                                                                       packet->TypeL4 == L4_UDP &&\r
+                                                                                       packet->TypeL7 == L7_DHCPV4)\r
+                                                                               {\r
+                                                                                       b = false;\r
+                                                                               }\r
+                                                                               else if (packet->TypeL3 == L3_ARPV4)\r
+                                                                               {\r
+                                                                                       b = false;\r
+                                                                               }\r
+                                                                               else if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                                       packet->TypeL4 == L4_UDP &&\r
+                                                                                       (Endian16(packet->L4.UDPHeader->DstPort) == 546 || Endian16(packet->L4.UDPHeader->DstPort) == 547))\r
+                                                                               {\r
+                                                                                       b = false;\r
+                                                                               }\r
+                                                                               else if (packet->TypeL3 == L3_IPV6 &&\r
+                                                                                       packet->TypeL4 == L4_ICMPV6)\r
+                                                                               {\r
+                                                                                       b = false;\r
+                                                                               }\r
+\r
+                                                                               if (discard == false)\r
+                                                                               {\r
+                                                                                       discard = b;\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (dest_session->Policy->FilterIPv4)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV4 || packet->TypeL3 == L3_ARPV4)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+                                                               if (dest_session->Policy->FilterIPv6)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 == L3_IPV6)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+                                                               if (dest_session->Policy->FilterNonIP)\r
+                                                               {\r
+                                                                       if (packet->TypeL3 != L3_IPV4 && packet->TypeL3 != L3_ARPV4 && packet->TypeL3 != L3_IPV6)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (s != NULL &&\r
+                                                                       (packet->BroadcastPacket == false &&\r
+                                                                       s->Policy->PrivacyFilter &&\r
+                                                                       dest_session->Policy->PrivacyFilter)\r
+                                                                       )\r
+                                                               {\r
+                                                                       // プライバシーフィルタ\r
+                                                                       if (packet->TypeL3 != L3_ARPV4)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (s != NULL)\r
+                                                               {\r
+                                                                       if (Cmp(packet->MacAddressSrc, s->Hub->HubMacAddr, 6) == 0 ||\r
+                                                                               Cmp(packet->MacAddressDest, s->Hub->HubMacAddr, 6) == 0)\r
+                                                                       {\r
+                                                                               discard = true;\r
+                                                                       }\r
+                                                               }\r
+\r
+                                                               if (discard == false && dest_pa != NULL)\r
+                                                               {\r
+                                                                       // 自分以外のセッションにストア\r
+                                                                       data = MallocFast(packet->PacketSize);\r
+                                                                       Copy(data, packet->PacketData, packet->PacketSize);\r
+                                                                       size = packet->PacketSize;\r
+\r
+                                                                       if (delete_default_router_in_ra)\r
+                                                                       {\r
+                                                                               PKT *pkt2 = ParsePacket(data, size);\r
+\r
+                                                                               DeleteIPv6DefaultRouterInRA(pkt2);\r
+\r
+                                                                               FreePacket(pkt2);\r
+                                                                       }\r
+\r
+                                                                       StorePacketToHubPa(dest_pa, s, data, size, packet);\r
+\r
+                                                                       // トラフィック加算\r
+                                                                       AddTrafficForSession(dest_session, &traffic);\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       UnlockList(hub->SessionList);\r
+\r
+                                       // パケットログをとる\r
+                                       if (s != NULL)\r
+                                       {\r
+                                               PacketLog(s->Hub, s, NULL, packet);\r
+                                       }\r
+\r
+                                       Free(packet->PacketData);\r
+                               }\r
+                               FreePacket(packet);\r
+                       }\r
+               }\r
+       }\r
+       UnlockList(hub->MacTable);\r
+}\r
+\r
+// 指定された IP アドレスがプライベート IP アドレスかどうかチェックする\r
+bool IsIPPrivate(IP *ip)\r
+{\r
+       // 引数チェック\r
+       if (ip == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (ip->addr[0] == 10)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       if (ip->addr[0] == 172)\r
+       {\r
+               if (ip->addr[1] >= 16 && ip->addr[1] <= 31)\r
+               {\r
+                       return true;\r
+               }\r
+       }\r
+\r
+       if (ip->addr[0] == 192 && ip->addr[1] == 168)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       if (ip->addr[0] == 2)\r
+       {\r
+               // Special !!\r
+               return true;\r
+       }\r
+\r
+       if (ip->addr[0] == 169 && ip->addr[1] == 254)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// 指定された IP アドレスが仮想 HUB による管理対象かどうか確認する\r
+bool IsIPManagementTargetForHUB(IP *ip, HUB *hub)\r
+{\r
+       // 引数チェック\r
+       if (ip == NULL || hub == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (hub->Option == NULL)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       if (IsIP4(ip))\r
+       {\r
+               if (hub->Option->ManageOnlyPrivateIP)\r
+               {\r
+                       if (IsIPPrivate(ip) == false)\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (hub->Option->ManageOnlyLocalUnicastIPv6)\r
+               {\r
+                       UINT ip_type = GetIPAddrType6(ip);\r
+\r
+                       if (!(ip_type & IPV6_ADDR_LOCAL_UNICAST))\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// 古い IP テーブルエントリを削除する\r
+void DeleteOldIpTableEntry(LIST *o)\r
+{\r
+       UINT i;\r
+       UINT64 oldest_time = 0xffffffffffffffffULL;\r
+       IP_TABLE_ENTRY *old = NULL;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               IP_TABLE_ENTRY *e = LIST_DATA(o, i);\r
+\r
+               if (e->UpdatedTime <= oldest_time)\r
+               {\r
+                       old = e;\r
+               }\r
+       }\r
+\r
+       if (old != NULL)\r
+       {\r
+               Delete(o, old);\r
+               Free(old);\r
+       }\r
+}\r
+\r
+// ストームリストの追加\r
+STORM *AddStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip)\r
+{\r
+       STORM *s;\r
+       // 引数チェック\r
+       if (pa == NULL || mac_address == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       s = ZeroMalloc(sizeof(STORM));\r
+       if (src_ip != NULL)\r
+       {\r
+               Copy(&s->SrcIp, src_ip, sizeof(IP));\r
+       }\r
+       if (dest_ip != NULL)\r
+       {\r
+               Copy(&s->DestIp, dest_ip, sizeof(IP));\r
+       }\r
+       Copy(s->MacAddress, mac_address, 6);\r
+\r
+       Insert(pa->StormList, s);\r
+\r
+       return s;\r
+}\r
+\r
+// ストームリストのサーチ\r
+STORM *SearchStormList(HUB_PA *pa, UCHAR *mac_address, IP *src_ip, IP *dest_ip)\r
+{\r
+       STORM t, *s;\r
+       // 引数チェック\r
+       if (pa == NULL || mac_address == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       Zero(&t, sizeof(t));\r
+       if (src_ip != NULL)\r
+       {\r
+               Copy(&t.SrcIp, src_ip, sizeof(IP));\r
+       }\r
+       if (dest_ip != NULL)\r
+       {\r
+               Copy(&t.DestIp, dest_ip, sizeof(IP));\r
+       }\r
+       Copy(t.MacAddress, mac_address, 6);\r
+\r
+       s = Search(pa->StormList, &t);\r
+\r
+       return s;\r
+}\r
+\r
+// パケットを宛先の HUB_PA にストアする\r
+void StorePacketToHubPa(HUB_PA *dest, SESSION *src, void *data, UINT size, PKT *packet)\r
+{\r
+       BLOCK *b;\r
+       // 引数チェック\r
+       if (dest == NULL || data == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (size < 14)\r
+       {\r
+               Free(data);\r
+               return;\r
+       }\r
+\r
+       if (src != NULL)\r
+       {\r
+               // フォワード用のアクセスリスト適用\r
+               if (ApplyAccessListToForwardPacket(src->Hub, src, dest->Session, packet) == false)\r
+               {\r
+                       Free(data);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       if (src != NULL)\r
+       {\r
+               if (dest->Session->Policy->MaxDownload != 0)\r
+               {\r
+                       // トラフィック制限\r
+                       if (packet != NULL && IsMostHighestPriorityPacket(dest->Session, packet) == false)\r
+                       {\r
+                               TRAFFIC_LIMITER *tr = &dest->DownloadLimiter;\r
+                               IntoTrafficLimiter(tr, packet);\r
+\r
+                               if ((tr->Value * (UINT64)1000 / (UINT64)LIMITER_SAMPLING_SPAN) > dest->Session->Policy->MaxDownload)\r
+                               {\r
+                                       // 制限する\r
+                                       Free(data);\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       if (src != NULL && src->Hub != NULL && src->Hub->Option != NULL && src->Hub->Option->FixForDLinkBPDU)\r
+       {\r
+               // D-Link バグ対策\r
+               UCHAR *mac = packet->MacAddressSrc;\r
+               if ((mac[0] == 0x00 && mac[1] == 0x80 && mac[2] == 0xc8 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00) ||\r
+                       (mac[0] == 0x00 && mac[1] == 0x0d && mac[2] == 0x88 && mac[3] == 0x00 && mac[4] == 0x00 && mac[5] == 0x00))\r
+               {\r
+                       SESSION *session = dest->Session;\r
+\r
+                       if (session != NULL)\r
+                       {\r
+                               if (session->Policy != NULL && session->Policy->CheckMac)\r
+                               {\r
+                                       UCHAR hash[MD5_SIZE];\r
+                                       Hash(hash, packet->PacketData, packet->PacketSize, false);\r
+\r
+                                       Copy(session->LastDLinkSTPPacketDataHash, hash, MD5_SIZE);\r
+                                       session->LastDLinkSTPPacketSendTick = Tick64();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // VLAN タグを除去\r
+       if (dest->Session != NULL && dest->Session->VLanId != 0)\r
+       {\r
+               if (VLanRemoveTag(&data, &size, dest->Session->VLanId) == false)\r
+               {\r
+                       Free(data);\r
+                       return;\r
+               }\r
+       }\r
+\r
+       // ブロック作成\r
+       b = NewBlock(data, size, 0);\r
+\r
+       LockQueue(dest->PacketQueue);\r
+       {\r
+               // キューの数を測定\r
+               if ((dest->PacketQueue->num_item < MAX_STORED_QUEUE_NUM) ||\r
+                       (((UCHAR *)data)[12] == 'S' && ((UCHAR *)data)[13] == 'E'))\r
+               {\r
+                       // ストア\r
+                       InsertQueue(dest->PacketQueue, b);\r
+               }\r
+               else\r
+               {\r
+                       // パケット破棄\r
+                       FreeBlock(b);\r
+               }\r
+       }\r
+       UnlockQueue(dest->PacketQueue);\r
+\r
+       // キャンセルの発行\r
+       if (src != NULL)\r
+       {\r
+               AddCancelList(src->CancelList, dest->Cancel);\r
+       }\r
+       else\r
+       {\r
+               Cancel(dest->Cancel);\r
+       }\r
+}\r
+\r
+// IPv6 ルータ広告からデフォルトルータ指定を削除\r
+bool DeleteIPv6DefaultRouterInRA(PKT *p)\r
+{\r
+       if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&\r
+               (p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))\r
+       {\r
+               if (p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime != 0)\r
+               {\r
+                       p->ICMPv6HeaderPacketInfo.Headers.RouterAdvertisementHeader->Lifetime = 0;\r
+\r
+                       p->L4.ICMPHeader->Checksum = 0;\r
+                       p->L4.ICMPHeader->Checksum =\r
+                               CalcChecksumForIPv6(&p->L3.IPv6Header->SrcAddress,\r
+                                       &p->L3.IPv6Header->DestAddress, IP_PROTO_ICMPV6,\r
+                                       p->L4.ICMPHeader, p->IPv6HeaderPacketInfo.PayloadSize);\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// ポリシーによるパケットフィルタ\r
+bool StorePacketFilterByPolicy(SESSION *s, PKT *p)\r
+{\r
+       POLICY *pol;\r
+       HUB *hub;\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       hub = s->Hub;\r
+\r
+       // ポリシー\r
+       pol = s->Policy;\r
+\r
+       // サーバーとしての動作を禁止する\r
+       if (pol->NoServer)\r
+       {\r
+               if (p->TypeL3 == L3_IPV4)\r
+               {\r
+                       if (p->TypeL4 == L4_TCP)\r
+                       {\r
+                               UCHAR flag = p->L4.TCPHeader->Flag;\r
+                               if ((flag & TCP_SYN) && (flag & TCP_ACK))\r
+                               {\r
+                                       char ip1[64], ip2[64];\r
+                                       // SYN + ACK パケットを送信させない\r
+                                       Debug("pol->NoServer: Discard SYN+ACK Packet.\n");\r
+\r
+                                       IPToStr32(ip1, sizeof(ip1), p->L3.IPv4Header->SrcIP);\r
+                                       IPToStr32(ip2, sizeof(ip2), p->L3.IPv4Header->DstIP);\r
+\r
+                                       HLog(s->Hub, "LH_NO_SERVER", s->Name, ip2, p->L4.TCPHeader->DstPort,\r
+                                               ip1, p->L4.TCPHeader->SrcPort);\r
+\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // サーバーとしての動作を禁止する (IPv6)\r
+       if (pol->NoServerV6)\r
+       {\r
+               if (p->TypeL3 == L3_IPV6)\r
+               {\r
+                       if (p->TypeL4 == L4_TCP)\r
+                       {\r
+                               UCHAR flag = p->L4.TCPHeader->Flag;\r
+                               if ((flag & TCP_SYN) && (flag & TCP_ACK))\r
+                               {\r
+                                       char ip1[128], ip2[128];\r
+                                       // SYN + ACK パケットを送信させない\r
+                                       Debug("pol->NoServerV6: Discard SYN+ACK Packet.\n");\r
+\r
+                                       IP6AddrToStr(ip1, sizeof(ip1), &p->IPv6HeaderPacketInfo.IPv6Header->SrcAddress);\r
+                                       IP6AddrToStr(ip2, sizeof(ip2), &p->IPv6HeaderPacketInfo.IPv6Header->DestAddress);\r
+\r
+                                       HLog(s->Hub, "LH_NO_SERVER", s->Name, ip2, p->L4.TCPHeader->DstPort,\r
+                                               ip1, p->L4.TCPHeader->SrcPort);\r
+\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // ブロードキャストは ARP と DHCP のみ許可\r
+       if (pol->ArpDhcpOnly && p->BroadcastPacket)\r
+       {\r
+               bool ok = false;\r
+\r
+               if (p->TypeL3 == L3_ARPV4)\r
+               {\r
+                       ok = true;\r
+               }\r
+               if (p->TypeL3 == L3_IPV4)\r
+               {\r
+                       if (p->TypeL4 == L4_UDP)\r
+                       {\r
+                               if (p->TypeL7 == L7_DHCPV4)\r
+                               {\r
+                                       ok = true;\r
+                               }\r
+                       }\r
+               }\r
+               if (p->TypeL3 == L3_IPV6)\r
+               {\r
+                       if (p->TypeL4 == L4_ICMPV6)\r
+                       {\r
+                               ok = true;\r
+                       }\r
+               }\r
+\r
+               if (p->TypeL3 == L3_IPV6 &&\r
+                       p->TypeL4 == L4_UDP &&\r
+                       (Endian16(p->L4.UDPHeader->DstPort) == 546 || Endian16(p->L4.UDPHeader->DstPort) == 547))\r
+               {\r
+                       ok = true;\r
+               }\r
+\r
+               if (ok == false)\r
+               {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // IPv4 パケットのフィルタリング\r
+       if (pol->FilterIPv4)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (proto == 0x0800 || proto == 0x0806)\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // IPv6 パケットのフィルタリング\r
+       if (pol->FilterIPv6)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (proto == 0x86dd)\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // 非 IP パケットのフィルタリング\r
+       if (pol->FilterNonIP)\r
+       {\r
+               if (p->MacHeader != NULL)\r
+               {\r
+                       USHORT proto = Endian16(p->MacHeader->Protocol);\r
+                       if (!(proto == 0x86dd || proto == 0x0800 || proto == 0x0806))\r
+                       {\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // DHCP パケットのフィルタリング\r
+       if (pol->DHCPFilter)\r
+       {\r
+               if (p->TypeL3 == L3_IPV4 &&\r
+                       p->TypeL4 == L4_UDP &&\r
+                       p->TypeL7 == L7_DHCPV4)\r
+               {\r
+                       // DHCP パケットを破棄する\r
+                       Debug("pol->DHCPFilter: Discard DHCP Packet.\n");\r
+\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // DHCPv6 パケットのフィルタリング\r
+       if (pol->DHCPv6Filter)\r
+       {\r
+               if (p->TypeL3 == L3_IPV6 &&\r
+                       p->TypeL4 == L4_UDP)\r
+               {\r
+                       if (Endian16(p->L4.UDPHeader->DstPort) == 546 ||\r
+                               Endian16(p->L4.UDPHeader->DstPort) == 547)\r
+                       {\r
+                               // DHCPv6 パケットを破棄する\r
+                               Debug("pol->DHCPv6Filter: Discard DHCPv6 Packet.\n");\r
+\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // DHCP サーバーとしての動作を禁止\r
+       if (pol->DHCPNoServer)\r
+       {\r
+               if (p->TypeL3 == L3_IPV4 &&\r
+                       p->TypeL4 == L4_UDP &&\r
+                       p->TypeL7 == L7_DHCPV4)\r
+               {\r
+                       DHCPV4_HEADER *h = p->L7.DHCPv4Header;\r
+                       if (h->OpCode == 2)\r
+                       {\r
+                               char ip1[64], ip2[64];\r
+\r
+                               // DHCP パケットを破棄する\r
+                               IPToStr32(ip1, sizeof(ip1), p->L3.IPv4Header->SrcIP);\r
+                               IPToStr32(ip2, sizeof(ip2), p->L3.IPv4Header->DstIP);\r
+\r
+                               HLog(s->Hub, "LH_NO_DHCP", s->Name, ip1, ip2);\r
+\r
+                               // DHCP 応答パケットを破棄する\r
+                               Debug("pol->DHCPNoServer: Discard DHCP Response Packet.\n");\r
+                               return false;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // DHCPv6 サーバーとしての動作を禁止\r
+       if (pol->DHCPv6NoServer)\r
+       {\r
+               if (p->TypeL3 == L3_IPV6 &&\r
+                       p->TypeL4 == L4_UDP &&\r
+                       (Endian16(p->L4.UDPHeader->DstPort) == 546 || Endian16(p->L4.UDPHeader->SrcPort) == 547))\r
+               {\r
+                       char ip1[128], ip2[128];\r
+\r
+                       // DHCP パケットを破棄する\r
+                       IP6AddrToStr(ip1, sizeof(ip1), &p->L3.IPv6Header->SrcAddress);\r
+                       IP6AddrToStr(ip2, sizeof(ip2), &p->L3.IPv6Header->DestAddress);\r
+\r
+                       HLog(s->Hub, "LH_NO_DHCP", s->Name, ip1, ip2);\r
+\r
+                       // DHCP 応答パケットを破棄する\r
+                       Debug("pol->DHCPv6NoServer: Discard DHCPv6 Response Packet.\n");\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // ルータ要請/広告パケットをフィルタリング (IPv6)\r
+       if (pol->RSandRAFilter)\r
+       {\r
+               if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&\r
+                       (p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_SOLICIATION ||\r
+                        p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT))\r
+               {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // ルータ広告パケットをフィルタリング (IPv6)\r
+       if (pol->RAFilter)\r
+       {\r
+               if (p->TypeL3 == L3_IPV6 && p->TypeL4 == L4_ICMPV6 &&\r
+                       p->ICMPv6HeaderPacketInfo.Type == ICMPV6_TYPE_ROUTER_ADVERTISEMENT)\r
+               {\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       // DHCP 応答パケットを記録して IP テーブルに登録\r
+       if (p->TypeL3 == L3_IPV4 &&\r
+               p->TypeL4 == L4_UDP &&\r
+               p->TypeL7 == L7_DHCPV4 &&\r
+               (s->Hub != NULL && s->Hub->Option->NoIpTable == false))\r
+       {\r
+               DHCPV4_HEADER *h = p->L7.DHCPv4Header;\r
+               if (h->OpCode == 2)\r
+               {\r
+                       // DHCP 応答パケットの中身を見て IP テーブルに登録する\r
+                       if (h->HardwareType == ARP_HARDWARE_TYPE_ETHERNET)\r
+                       {\r
+                               if (h->HardwareAddressSize == 6)\r
+                               {\r
+                                       if (h->YourIP != 0 && h->YourIP != 0xffffffff)\r
+                                       {\r
+                                               UINT ip_uint = h->YourIP;\r
+                                               IP ip;\r
+                                               IP_TABLE_ENTRY *e, t;\r
+                                               MAC_TABLE_ENTRY *mac_table, mt;\r
+                                               mt.VlanId = 0;\r
+                                               Copy(&mt.MacAddress, &h->ClientMacAddress, 6);\r
+                                               mac_table = Search(hub->MacTable, &mt);\r
+\r
+                                               if (mac_table != NULL)\r
+                                               {\r
+                                                       bool new_entry = true;\r
+                                                       UINTToIP(&ip, ip_uint);\r
+                                                       Copy(&t.Ip, &ip, sizeof(IP));\r
+\r
+                                                       e = Search(hub->IpTable, &t);\r
+                                                       if (e == NULL)\r
+                                                       {\r
+                                                               // 新しく登録\r
+                                                               e = ZeroMalloc(sizeof(IP_TABLE_ENTRY));\r
+UPDATE_DHCP_ALLOC_ENTRY:\r
+                                                               e->CreatedTime = e->UpdatedTime = Tick64();\r
+                                                               e->DhcpAllocated = true;\r
+                                                               Copy(&e->Ip, &ip, sizeof(IP));\r
+                                                               e->Session = mac_table->Session;\r
+                                                               Copy(e->MacAddress, p->MacAddressDest, 6);\r
+\r
+                                                               if (new_entry)\r
+                                                               {\r
+                                                                       // 有効期限の切れた IP テーブルエントリを削除する\r
+                                                                       DeleteExpiredIpTableEntry(hub->IpTable);\r
+                                                                       if (LIST_NUM(hub->IpTable) >= MAX_IP_TABLES)\r
+                                                                       {\r
+                                                                               // 古いエントリを削除する\r
+                                                                               DeleteOldIpTableEntry(hub->IpTable);\r
+                                                                       }\r
+                                                                       Insert(hub->IpTable, e);\r
+                                                               }\r
+\r
+                                                               if (new_entry)\r
+                                                               {\r
+                                                                       char dhcp_mac_addr[64];\r
+                                                                       char dest_mac_addr[64];\r
+                                                                       char dest_ip_addr[64];\r
+                                                                       char server_ip_addr[64];\r
+                                                                       MacToStr(dhcp_mac_addr, sizeof(dhcp_mac_addr), p->MacAddressSrc);\r
+                                                                       MacToStr(dest_mac_addr, sizeof(dest_mac_addr), h->ClientMacAddress);\r
+                                                                       IPToStr(dest_ip_addr, sizeof(dest_ip_addr), &ip);\r
+                                                                       IPToStr32(server_ip_addr, sizeof(server_ip_addr), p->L3.IPv4Header->SrcIP);\r
+                                                                       Debug("DHCP Allocated; dhcp server: %s, client: %s, new_ip: %s\n",\r
+                                                                               dhcp_mac_addr, dest_mac_addr, dest_ip_addr);\r
+\r
+                                                                       HLog(s->Hub, "LH_REGIST_DHCP", s->Name, dhcp_mac_addr, server_ip_addr,\r
+                                                                               mac_table->Session->Name, dest_mac_addr, dest_ip_addr);\r
+                                                               }\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               // 更新\r
+                                                               new_entry = false;\r
+                                                               goto UPDATE_DHCP_ALLOC_ENTRY;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// 有効期限の切れた MAC テーブルエントリを削除する\r
+void DeleteExpiredMacTableEntry(LIST *o)\r
+{\r
+       LIST *o2;\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       o2 = NewListFast(NULL);\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               MAC_TABLE_ENTRY *e = LIST_DATA(o, i);\r
+               if ((e->UpdatedTime + (UINT64)MAC_TABLE_EXPIRE_TIME) <= Tick64())\r
+               {\r
+                       Add(o2, e);\r
+               }\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o2);i++)\r
+       {\r
+               MAC_TABLE_ENTRY *e = LIST_DATA(o2, i);\r
+               Delete(o, e);\r
+               Free(e);\r
+       }\r
+\r
+       ReleaseList(o2);\r
+}\r
+\r
+// 有効期限の切れた IP テーブルエントリを削除する\r
+void DeleteExpiredIpTableEntry(LIST *o)\r
+{\r
+       LIST *o2;\r
+       UINT i;\r
+       // 引数チェック\r
+       if (o == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       o2 = NewListFast(NULL);\r
+\r
+       for (i = 0;i < LIST_NUM(o);i++)\r
+       {\r
+               IP_TABLE_ENTRY *e = LIST_DATA(o, i);\r
+               if ((e->UpdatedTime + (UINT64)(e->DhcpAllocated ? IP_TABLE_EXPIRE_TIME_DHCP : IP_TABLE_EXPIRE_TIME)) <= Tick64())\r
+               {\r
+                       Add(o2, e);\r
+               }\r
+       }\r
+\r
+       for (i = 0;i < LIST_NUM(o2);i++)\r
+       {\r
+               IP_TABLE_ENTRY *e = LIST_DATA(o2, i);\r
+               Delete(o, e);\r
+               Free(e);\r
+       }\r
+\r
+       ReleaseList(o2);\r
+}\r
+\r
+// 優先して取り扱うべきパケットかどうかを判断\r
+bool IsMostHighestPriorityPacket(SESSION *s, PKT *p)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (p->TypeL3 == L3_ARPV4)\r
+       {\r
+               // ARP パケット\r
+               return true;\r
+       }\r
+\r
+       if (p->TypeL3 == L3_IPV4)\r
+       {\r
+               if (p->TypeL4 == L4_ICMPV4)\r
+               {\r
+                       // ICMP パケット\r
+                       return true;\r
+               }\r
+\r
+               if (p->TypeL4 == L4_TCP)\r
+               {\r
+                       if ((p->L4.TCPHeader->Flag & TCP_SYN) || (p->L4.TCPHeader->Flag & TCP_FIN)\r
+                               || (p->L4.TCPHeader->Flag & TCP_RST))\r
+                       {\r
+                               // SYN, FIN, RST パケット\r
+                               return true;\r
+                       }\r
+               }\r
+\r
+               if (p->TypeL4 == L4_UDP)\r
+               {\r
+                       if (p->TypeL7 == L7_DHCPV4)\r
+                       {\r
+                               // DHCP パケット\r
+                               return true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// トラフィック リミッターへのパケット追加\r
+void IntoTrafficLimiter(TRAFFIC_LIMITER *tr, PKT *p)\r
+{\r
+       UINT64 now = Tick64();\r
+       // 引数チェック\r
+       if (tr == NULL || p == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (tr->LastTime == 0 || tr->LastTime > now ||\r
+               (tr->LastTime + LIMITER_SAMPLING_SPAN) < now)\r
+       {\r
+               // サンプリング初期化\r
+               tr->Value = 0;\r
+               tr->LastTime = now;\r
+       }\r
+\r
+       // 値増加\r
+       tr->Value += (UINT64)(p->PacketSize * 8);\r
+}\r
+\r
+// トラフィック リミッターによる帯域幅削減\r
+bool StorePacketFilterByTrafficLimiter(SESSION *s, PKT *p)\r
+{\r
+       HUB_PA *pa;\r
+       TRAFFIC_LIMITER *tr;\r
+       // 引数チェック\r
+       if (s == NULL || p == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (s->Policy->MaxUpload == 0)\r
+       {\r
+               // 制限無し\r
+               return true;\r
+       }\r
+\r
+       pa = (HUB_PA *)s->PacketAdapter->Param;\r
+       tr = &pa->UploadLimiter;\r
+\r
+       // 優先パケットは制限を適用しない\r
+       if (IsMostHighestPriorityPacket(s, p))\r
+       {\r
+               return true;\r
+       }\r
+\r
+       // リミッターへパケットを投入\r
+       IntoTrafficLimiter(tr, p);\r
+\r
+       // 現在の帯域幅と制限値を比較\r
+       if ((tr->Value * (UINT64)1000 / (UINT64)LIMITER_SAMPLING_SPAN) > s->Policy->MaxUpload)\r
+       {\r
+               // パケットを破棄\r
+               return false;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// ストアするパケットのフィルタリング\r
+bool StorePacketFilter(SESSION *s, PKT *packet)\r
+{\r
+       // 引数チェック\r
+       if (s == NULL || packet == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // トラフィック リミッターによる帯域幅削減\r
+       if (StorePacketFilterByTrafficLimiter(s, packet) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // ポリシーによるパケットフィルタ\r
+       if (StorePacketFilterByPolicy(s, packet) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       // アクセスリストによるパケットフィルタ\r
+       if (ApplyAccessListToStoredPacket(s->Hub, s, packet) == false)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+// HUB 用のパケットアダプタの取得\r
+PACKET_ADAPTER *GetHubPacketAdapter()\r
+{\r
+       // 関数リストを生成して引き渡す\r
+       PACKET_ADAPTER *pa = NewPacketAdapter(HubPaInit,\r
+               HubPaGetCancel, HubPaGetNextPacket, HubPaPutPacket, HubPaFree);\r
+\r
+       return pa;\r
+}\r
+\r
+// HUB のすべての SESSION を停止させる\r
+void StopAllSession(HUB *h)\r
+{\r
+       SESSION **s;\r
+       UINT i, num;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(h->SessionList);\r
+       {\r
+               num = LIST_NUM(h->SessionList);\r
+               s = ToArray(h->SessionList);\r
+               DeleteAll(h->SessionList);\r
+       }\r
+       UnlockList(h->SessionList);\r
+\r
+       for (i = 0;i < num;i++)\r
+       {\r
+               StopSession(s[i]);\r
+               ReleaseSession(s[i]);\r
+       }\r
+\r
+       Free(s);\r
+}\r
+\r
+// HUB から SESSION を削除\r
+void DelSession(HUB *h, SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (h == NULL || s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(h->SessionList);\r
+       {\r
+               if (Delete(h->SessionList, s))\r
+               {\r
+                       Debug("Session %s was Deleted from %s.\n", s->Name, h->Name);\r
+                       ReleaseSession(s);\r
+               }\r
+       }\r
+       UnlockList(h->SessionList);\r
+}\r
+\r
+// HUB に SESSION を追加\r
+void AddSession(HUB *h, SESSION *s)\r
+{\r
+       // 引数チェック\r
+       if (h == NULL || s == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(h->SessionList);\r
+       {\r
+               Insert(h->SessionList, s);\r
+               AddRef(s->ref);\r
+               Debug("Session %s Inserted to %s.\n", s->Name, h->Name);\r
+       }\r
+       UnlockList(h->SessionList);\r
+}\r
+\r
+// HUB の動作を停止する\r
+void StopHub(HUB *h)\r
+{\r
+       bool old_status = false;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       old_status = h->Offline;\r
+       h->HubIsOnlineButHalting = true;\r
+\r
+       SetHubOffline(h);\r
+\r
+       if (h->Halt == false)\r
+       {\r
+               SLog(h->Cedar, "LS_HUB_STOP", h->Name);\r
+               h->Halt = true;\r
+       }\r
+\r
+       h->Offline = old_status;\r
+       h->HubIsOnlineButHalting = false;\r
+}\r
+\r
+// HUB をオンラインにする\r
+void SetHubOnline(HUB *h)\r
+{\r
+       bool for_cluster = false;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+       {\r
+               if (h->Type == HUB_TYPE_FARM_DYNAMIC)\r
+               {\r
+                       for_cluster = true;\r
+               }\r
+       }\r
+\r
+       Lock(h->lock_online);\r
+       {\r
+               if (h->Offline == false)\r
+               {\r
+                       Unlock(h->lock_online);\r
+                       return;\r
+               }\r
+               HLog(h, "LH_ONLINE");\r
+\r
+               // すべてのリンクを開始\r
+               StartAllLink(h);\r
+\r
+               // SecureNAT を開始\r
+               if (h->EnableSecureNAT)\r
+               {\r
+                       if (h->SecureNAT == NULL)\r
+                       {\r
+                               if (for_cluster == false)\r
+                               {\r
+                                       h->SecureNAT = SnNewSecureNAT(h, h->SecureNATOption);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // この HUB に関連付けられているローカルブリッジをすべて開始する\r
+               if (h->Type != HUB_TYPE_FARM_DYNAMIC)\r
+               {\r
+                       LockList(h->Cedar->LocalBridgeList);\r
+                       {\r
+                               UINT i;\r
+                               for (i = 0;i < LIST_NUM(h->Cedar->LocalBridgeList);i++)\r
+                               {\r
+                                       LOCALBRIDGE *br = LIST_DATA(h->Cedar->LocalBridgeList, i);\r
+\r
+                                       if (StrCmpi(br->HubName, h->Name) == 0)\r
+                                       {\r
+                                               if (br->Bridge == NULL)\r
+                                               {\r
+                                                       br->Bridge = BrNewBridge(h, br->DeviceName, NULL, br->Local, br->Monitor,\r
+                                                               br->TapMode, br->TapMacAddress, br->FullBroadcast);\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+                       UnlockList(h->Cedar->LocalBridgeList);\r
+               }\r
+\r
+               h->Offline = false;\r
+       }\r
+       Unlock(h->lock_online);\r
+\r
+       if (h->Cedar->Server != NULL)\r
+       {\r
+               SiHubOnlineProc(h);\r
+       }\r
+}\r
+\r
+// HUB をオフラインにする\r
+void SetHubOffline(HUB *h)\r
+{\r
+       UINT i;\r
+       bool for_cluster = false;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+       {\r
+               if (h->Type == HUB_TYPE_FARM_DYNAMIC)\r
+               {\r
+                       for_cluster = true;\r
+               }\r
+       }\r
+\r
+       h->BeingOffline = true;\r
+\r
+       Lock(h->lock_online);\r
+       {\r
+               if (h->Offline || h->Halt)\r
+               {\r
+                       Unlock(h->lock_online);\r
+                       h->BeingOffline = false;\r
+                       return;\r
+               }\r
+\r
+               HLog(h, "LH_OFFLINE");\r
+\r
+               // すべてのリンクを停止\r
+               StopAllLink(h);\r
+\r
+               // SecureNAT を停止\r
+               SnFreeSecureNAT(h->SecureNAT);\r
+               h->SecureNAT = NULL;\r
+\r
+               // この HUB に関連付けられているローカルブリッジをすべて停止する\r
+               LockList(h->Cedar->LocalBridgeList);\r
+               {\r
+                       for (i = 0;i < LIST_NUM(h->Cedar->LocalBridgeList);i++)\r
+                       {\r
+                               LOCALBRIDGE *br = LIST_DATA(h->Cedar->LocalBridgeList, i);\r
+\r
+                               if (StrCmpi(br->HubName, h->Name) == 0)\r
+                               {\r
+                                       BrFreeBridge(br->Bridge);\r
+                                       br->Bridge = NULL;\r
+                               }\r
+                       }\r
+               }\r
+               UnlockList(h->Cedar->LocalBridgeList);\r
+\r
+               // オフラインにする\r
+               h->Offline = true;\r
+\r
+               // すべてのセッションを切断する\r
+               StopAllSession(h);\r
+       }\r
+       Unlock(h->lock_online);\r
+\r
+       h->BeingOffline = false;\r
+\r
+       if (h->Cedar->Server != NULL)\r
+       {\r
+               SiHubOfflineProc(h);\r
+       }\r
+}\r
+\r
+// 指定された名前の HUB が存在するかどうか取得\r
+bool IsHub(CEDAR *cedar, char *name)\r
+{\r
+       HUB *h;\r
+       // 引数チェック\r
+       if (cedar == NULL || name == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       h = GetHub(cedar, name);\r
+       if (h == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       ReleaseHub(h);\r
+\r
+       return true;\r
+}\r
+\r
+// HUB の取得\r
+HUB *GetHub(CEDAR *cedar, char *name)\r
+{\r
+       HUB *h, t;\r
+       // 引数チェック\r
+       if (cedar == NULL || name == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       LockHubList(cedar);\r
+\r
+       t.Name = name;\r
+       h = Search(cedar->HubList, &t);\r
+       if (h == NULL)\r
+       {\r
+               UnlockHubList(cedar);\r
+               return NULL;\r
+       }\r
+\r
+       AddRef(h->ref);\r
+\r
+       UnlockHubList(cedar);\r
+\r
+       return h;\r
+}\r
+\r
+// HUB リストのロック\r
+void LockHubList(CEDAR *cedar)\r
+{\r
+       // 引数チェック\r
+       if (cedar == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(cedar->HubList);\r
+}\r
+\r
+// HUB リストのロック解除\r
+void UnlockHubList(CEDAR *cedar)\r
+{\r
+       // 引数チェック\r
+       if (cedar == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       UnlockList(cedar->HubList);\r
+}\r
+\r
+// HUB の解放\r
+void ReleaseHub(HUB *h)\r
+{\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (Release(h->ref) == 0)\r
+       {\r
+               CleanupHub(h);\r
+       }\r
+}\r
+\r
+// Radius サーバー情報を取得\r
+bool GetRadiusServer(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size)\r
+{\r
+       UINT interval;\r
+       return GetRadiusServerEx(hub, name, size, port, secret, secret_size, &interval);\r
+}\r
+bool GetRadiusServerEx(HUB *hub, char *name, UINT size, UINT *port, char *secret, UINT secret_size, UINT *interval)\r
+{\r
+       bool ret = false;\r
+       // 引数チェック\r
+       if (hub == NULL || name == NULL || port == NULL || secret == NULL || interval == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       Lock(hub->RadiusOptionLock);\r
+       {\r
+               if (hub->RadiusServerName != NULL)\r
+               {\r
+                       char *tmp;\r
+                       UINT tmp_size;\r
+                       StrCpy(name, size, hub->RadiusServerName);\r
+                       *port = hub->RadiusServerPort;\r
+                       *interval = hub->RadiusRetryInterval;\r
+\r
+                       tmp_size = hub->RadiusSecret->Size + 1;\r
+                       tmp = ZeroMalloc(tmp_size);\r
+                       Copy(tmp, hub->RadiusSecret->Buf, hub->RadiusSecret->Size);\r
+                       StrCpy(secret, secret_size, tmp);\r
+                       Free(tmp);\r
+\r
+                       ret = true;\r
+               }\r
+       }\r
+       Unlock(hub->RadiusOptionLock);\r
+\r
+       return ret;\r
+}\r
+\r
+// Radius サーバー情報を設定\r
+void SetRadiusServer(HUB *hub, char *name, UINT port, char *secret)\r
+{\r
+       SetRadiusServerEx(hub, name, port, secret, RADIUS_RETRY_INTERVAL);\r
+}\r
+void SetRadiusServerEx(HUB *hub, char *name, UINT port, char *secret, UINT interval)\r
+{\r
+       // 引数チェック\r
+       if (hub == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Lock(hub->RadiusOptionLock);\r
+       {\r
+               if (hub->RadiusServerName != NULL)\r
+               {\r
+                       Free(hub->RadiusServerName);\r
+               }\r
+\r
+               if (name == NULL)\r
+               {\r
+                       hub->RadiusServerName = NULL;\r
+                       hub->RadiusServerPort = 0;\r
+                       hub->RadiusRetryInterval = RADIUS_RETRY_INTERVAL;\r
+                       FreeBuf(hub->RadiusSecret);\r
+               }\r
+               else\r
+               {\r
+                       hub->RadiusServerName = CopyStr(name);\r
+                       hub->RadiusServerPort = port;\r
+                       if (interval == 0)\r
+                       {\r
+                               hub->RadiusRetryInterval = RADIUS_RETRY_INTERVAL;\r
+                       }\r
+                       else if (interval > RADIUS_RETRY_TIMEOUT)\r
+                       {\r
+                               hub->RadiusRetryInterval = RADIUS_RETRY_TIMEOUT;\r
+                       }\r
+                       else\r
+                       {\r
+                               hub->RadiusRetryInterval = interval;\r
+                       }\r
+                       FreeBuf(hub->RadiusSecret);\r
+\r
+                       if (secret == NULL)\r
+                       {\r
+                               hub->RadiusSecret = NewBuf();\r
+                       }\r
+                       else\r
+                       {\r
+                               hub->RadiusSecret = NewBuf();\r
+                               WriteBuf(hub->RadiusSecret, secret, StrLen(secret));\r
+                               SeekBuf(hub->RadiusSecret, 0, 0);\r
+                       }\r
+               }\r
+       }\r
+       Unlock(hub->RadiusOptionLock);\r
+}\r
+\r
+// 仮想 HUB のトラフィック情報の追加\r
+void IncrementHubTraffic(HUB *h)\r
+{\r
+       TRAFFIC t;\r
+       // 引数チェック\r
+       if (h == NULL || h->FarmMember == false)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Zero(&t, sizeof(t));\r
+\r
+       Lock(h->TrafficLock);\r
+       {\r
+               t.Send.BroadcastBytes =\r
+                       h->Traffic->Send.BroadcastBytes - h->OldTraffic->Send.BroadcastBytes;\r
+               t.Send.BroadcastCount =\r
+                       h->Traffic->Send.BroadcastCount - h->OldTraffic->Send.BroadcastCount;\r
+               t.Send.UnicastBytes =\r
+                       h->Traffic->Send.UnicastBytes - h->OldTraffic->Send.UnicastBytes;\r
+               t.Send.UnicastCount =\r
+                       h->Traffic->Send.UnicastCount - h->OldTraffic->Send.UnicastCount;\r
+               t.Recv.BroadcastBytes =\r
+                       h->Traffic->Recv.BroadcastBytes - h->OldTraffic->Recv.BroadcastBytes;\r
+               t.Recv.BroadcastCount =\r
+                       h->Traffic->Recv.BroadcastCount - h->OldTraffic->Recv.BroadcastCount;\r
+               t.Recv.UnicastBytes =\r
+                       h->Traffic->Recv.UnicastBytes - h->OldTraffic->Recv.UnicastBytes;\r
+               t.Recv.UnicastCount =\r
+                       h->Traffic->Recv.UnicastCount - h->OldTraffic->Recv.UnicastCount;\r
+               Copy(h->OldTraffic, h->Traffic, sizeof(TRAFFIC));\r
+       }\r
+       Unlock(h->TrafficLock);\r
+\r
+       if (IsZero(&t, sizeof(TRAFFIC)))\r
+       {\r
+               return;\r
+       }\r
+\r
+       AddTrafficDiff(h, h->Name, TRAFFIC_DIFF_HUB, &t);\r
+}\r
+\r
+// トラフィック情報の追加\r
+void AddTrafficDiff(HUB *h, char *name, UINT type, TRAFFIC *traffic)\r
+{\r
+       TRAFFIC_DIFF *d;\r
+       // 引数チェック\r
+       if (h == NULL || h->FarmMember == false || name == NULL || traffic == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       if (LIST_NUM(h->Cedar->TrafficDiffList) > MAX_TRAFFIC_DIFF)\r
+       {\r
+               return;\r
+       }\r
+\r
+       d = ZeroMallocFast(sizeof(TRAFFIC_DIFF));\r
+       d->HubName = CopyStr(h->Name);\r
+       d->Name = CopyStr(name);\r
+       d->Type = type;\r
+       Copy(&d->Traffic, traffic, sizeof(TRAFFIC));\r
+\r
+       LockList(h->Cedar->TrafficDiffList);\r
+       {\r
+               Insert(h->Cedar->TrafficDiffList, d);\r
+       }\r
+       UnlockList(h->Cedar->TrafficDiffList);\r
+}\r
+\r
+// HUB のクリーンアップ\r
+void CleanupHub(HUB *h)\r
+{\r
+       UINT i;\r
+       char name[MAX_SIZE];\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       StrCpy(name, sizeof(name), h->Name);\r
+\r
+       if (h->WatchDogStarted)\r
+       {\r
+               StopHubWatchDog(h);\r
+       }\r
+\r
+       FreeAccessList(h);\r
+\r
+       if (h->RadiusServerName != NULL)\r
+       {\r
+               Free(h->RadiusServerName);\r
+               FreeBuf(h->RadiusSecret);\r
+       }\r
+       ReleaseAllLink(h);\r
+       DeleteHubDb(h->HubDb);\r
+       ReleaseCedar(h->Cedar);\r
+       DeleteLock(h->lock);\r
+       DeleteLock(h->lock_online);\r
+       Free(h->Name);\r
+       ReleaseList(h->SessionList);\r
+       ReleaseList(h->MacTable);\r
+       ReleaseList(h->IpTable);\r
+       ReleaseList(h->MonitorList);\r
+       ReleaseList(h->LinkList);\r
+       DeleteCounter(h->NumSessions);\r
+       DeleteCounter(h->NumSessionsClient);\r
+       DeleteCounter(h->NumSessionsBridge);\r
+       DeleteCounter(h->SessionCounter);\r
+       FreeTraffic(h->Traffic);\r
+       FreeTraffic(h->OldTraffic);\r
+       Free(h->Option);\r
+\r
+       Free(h->SecureNATOption);\r
+\r
+       DeleteLock(h->TrafficLock);\r
+\r
+       for (i = 0;i < LIST_NUM(h->TicketList);i++)\r
+       {\r
+               Free(LIST_DATA(h->TicketList, i));\r
+       }\r
+\r
+       ReleaseList(h->TicketList);\r
+\r
+       DeleteLock(h->RadiusOptionLock);\r
+\r
+       FreeLog(h->PacketLogger);\r
+       FreeLog(h->SecurityLogger);\r
+\r
+       for (i = 0;i < LIST_NUM(h->AdminOptionList);i++)\r
+       {\r
+               Free(LIST_DATA(h->AdminOptionList, i));\r
+       }\r
+       ReleaseList(h->AdminOptionList);\r
+\r
+       if (h->Msg != NULL)\r
+       {\r
+               Free(h->Msg);\r
+       }\r
+\r
+       Free(h);\r
+}\r
+\r
+// IP テーブルの比較関数\r
+int CompareIpTable(void *p1, void *p2)\r
+{\r
+       IP_TABLE_ENTRY *e1, *e2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       e1 = *(IP_TABLE_ENTRY **)p1;\r
+       e2 = *(IP_TABLE_ENTRY **)p2;\r
+       if (e1 == NULL || e2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return CmpIpAddr(&e1->Ip, &e2->Ip);\r
+}\r
+\r
+// MAC テーブルの比較関数\r
+int CompareMacTable(void *p1, void *p2)\r
+{\r
+       int r;\r
+       MAC_TABLE_ENTRY *e1, *e2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       e1 = *(MAC_TABLE_ENTRY **)p1;\r
+       e2 = *(MAC_TABLE_ENTRY **)p2;\r
+       if (e1 == NULL || e2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       r = Cmp(e1->MacAddress, e2->MacAddress, 6);\r
+       if (r != 0)\r
+       {\r
+               return r;\r
+       }\r
+       if (e1->VlanId > e2->VlanId)\r
+       {\r
+               return 1;\r
+       }\r
+       else if (e1->VlanId < e2->VlanId)\r
+       {\r
+               return -1;\r
+       }\r
+       return 0;\r
+}\r
+\r
+// HUB の比較関数\r
+int CompareHub(void *p1, void *p2)\r
+{\r
+       HUB *h1, *h2;\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       h1 = *(HUB **)p1;\r
+       h2 = *(HUB **)p2;\r
+       if (h1 == NULL || h2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       return StrCmpi(h1->Name, h2->Name);\r
+}\r
+\r
+// MAC アドレスが仮想 HUB の ARP ポーリング用の MAC アドレスかどうか調べる\r
+bool IsHubMacAddress(UCHAR *mac)\r
+{\r
+       // 引数チェック\r
+       if (mac == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (mac[0] == 0x00 && mac[1] == SE_HUB_MAC_ADDR_SIGN)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// IP アドレスが仮想 HUB の ARP ポーリング用の IP アドレスかどうか調べる\r
+bool IsHubIpAddress32(UINT ip32)\r
+{\r
+       IP ip;\r
+\r
+       UINTToIP(&ip, ip32);\r
+\r
+       return IsHubIpAddress(&ip);\r
+}\r
+bool IsHubIpAddress(IP *ip)\r
+{\r
+       // 引数チェック\r
+       if (ip == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (ip->addr[0] == 172 && ip->addr[1] == 31)\r
+       {\r
+               if (ip->addr[2] >= 1 && ip->addr[2] <= 254)\r
+               {\r
+                       if (ip->addr[3] >= 1 && ip->addr[3] <= 254)\r
+                       {\r
+                               return true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       return false;\r
+}\r
+bool IsHubIpAddress64(IPV6_ADDR *addr)\r
+{\r
+       // 引数チェック\r
+       if (addr == NULL)\r
+       {\r
+               return false;\r
+       }\r
+\r
+       if (addr->Value[0] == 0xfe && addr->Value[1] == 0x80 &&\r
+               addr->Value[2] == 0 &&\r
+               addr->Value[3] == 0 &&\r
+               addr->Value[4] == 0 &&\r
+               addr->Value[5] == 0 &&\r
+               addr->Value[6] == 0 &&\r
+               addr->Value[7] == 0 &&\r
+               addr->Value[8] == 0x02 && addr->Value[9] == 0xae && \r
+               addr->Value[11] == 0xff && addr->Value[12] == 0xfe)\r
+       {\r
+               return true;\r
+       }\r
+\r
+       return false;\r
+}\r
+\r
+// 仮想 HUB 用 IP アドレスの生成\r
+void GenHubIpAddress(IP *ip, char *name)\r
+{\r
+       char tmp1[MAX_SIZE];\r
+       char tmp2[MAX_SIZE];\r
+       UCHAR hash[SHA1_SIZE];\r
+       // 引数チェック\r
+       if (ip == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       StrCpy(tmp1, sizeof(tmp1), name);\r
+       Trim(tmp1);\r
+       GenerateMachineUniqueHash(hash);\r
+       BinToStr(tmp2, sizeof(tmp2), hash, sizeof(hash));\r
+       StrCat(tmp2, sizeof(tmp2), tmp1);\r
+       StrUpper(tmp2);\r
+\r
+       Hash(hash, tmp2, StrLen(tmp2), true);\r
+\r
+       Zero(ip, sizeof(IP));\r
+       ip->addr[0] = 172;\r
+       ip->addr[1] = 31;\r
+       ip->addr[2] = hash[0] % 254 + 1;\r
+       ip->addr[3] = hash[1] % 254 + 1;\r
+}\r
+\r
+// 仮想 HUB 用 MAC アドレスの生成\r
+void GenHubMacAddress(UCHAR *mac, char *name)\r
+{\r
+       char tmp1[MAX_SIZE];\r
+       char tmp2[MAX_SIZE];\r
+       UCHAR hash[SHA1_SIZE];\r
+       // 引数チェック\r
+       if (mac == NULL || name == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       StrCpy(tmp1, sizeof(tmp1), name);\r
+       Trim(tmp1);\r
+       GenerateMachineUniqueHash(hash);\r
+       BinToStr(tmp2, sizeof(tmp2), hash, sizeof(hash));\r
+       StrCat(tmp2, sizeof(tmp2), tmp1);\r
+       StrUpper(tmp2);\r
+\r
+       Hash(hash, tmp2, StrLen(tmp2), true);\r
+\r
+       mac[0] = 0x00;\r
+       mac[1] = SE_HUB_MAC_ADDR_SIGN;\r
+       mac[2] = hash[0];\r
+       mac[3] = hash[1];\r
+       mac[4] = hash[2];\r
+       mac[5] = hash[3];\r
+}\r
+\r
+// HUB からメッセージを取得\r
+wchar_t *GetHubMsg(HUB *h)\r
+{\r
+       wchar_t *ret = NULL;\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       Lock(h->lock);\r
+       {\r
+               if (h->Msg != NULL)\r
+               {\r
+                       ret = CopyUniStr(h->Msg);\r
+               }\r
+       }\r
+       Unlock(h->lock);\r
+\r
+       return ret;\r
+}\r
+\r
+// HUB にメッセージを設定\r
+void SetHubMsg(HUB *h, wchar_t *msg)\r
+{\r
+       // 引数チェック\r
+       if (h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Lock(h->lock);\r
+       {\r
+               if (h->Msg != NULL)\r
+               {\r
+                       Free(h->Msg);\r
+                       h->Msg = NULL;\r
+               }\r
+\r
+               if (UniIsEmptyStr(msg) == false)\r
+               {\r
+                       h->Msg = UniCopyStr(msg);\r
+               }\r
+       }\r
+       Unlock(h->lock);\r
+}\r
+\r
+// 新しい HUB の作成\r
+HUB *NewHub(CEDAR *cedar, char *HubName, HUB_OPTION *option)\r
+{\r
+       HUB *h;\r
+       char packet_logger_name[MAX_SIZE];\r
+       char tmp[MAX_SIZE];\r
+       char safe_hub_name[MAX_HUBNAME_LEN + 1];\r
+       UCHAR hash[SHA1_SIZE];\r
+       IP ip6;\r
+       // 引数チェック\r
+       if (cedar == NULL || option == NULL || HubName == NULL)\r
+       {\r
+               return NULL;\r
+       }\r
+\r
+       h = ZeroMalloc(sizeof(HUB));\r
+       Hash(h->HashedPassword, "", 0, true);\r
+       HashPassword(h->SecurePassword, ADMINISTRATOR_USERNAME, "");\r
+       h->lock = NewLock();\r
+       h->lock_online = NewLock();\r
+       h->ref = NewRef();\r
+       h->Cedar = cedar;\r
+       AddRef(h->Cedar->ref);\r
+       h->Type = HUB_TYPE_STANDALONE;\r
+\r
+       ConvertSafeFileName(safe_hub_name, sizeof(safe_hub_name), HubName);\r
+       h->Name = CopyStr(safe_hub_name);\r
+\r
+       h->AdminOptionList = NewList(CompareAdminOption);\r
+       AddHubAdminOptionsDefaults(h, true);\r
+\r
+       h->LastCommTime = SystemTime64();\r
+       h->LastLoginTime = SystemTime64();\r
+       h->NumLogin = 0;\r
+\r
+       h->TrafficLock = NewLock();\r
+\r
+       h->HubDb = NewHubDb();\r
+\r
+       h->SessionList = NewList(NULL);\r
+       h->SessionCounter = NewCounter();\r
+       h->NumSessions = NewCounter();\r
+       h->NumSessionsClient = NewCounter();\r
+       h->NumSessionsBridge = NewCounter();\r
+       h->MacTable = NewList(CompareMacTable);\r
+       h->IpTable = NewList(CompareIpTable);\r
+       h->MonitorList = NewList(NULL);\r
+       h->LinkList = NewList(NULL);\r
+\r
+       h->Traffic = NewTraffic();\r
+       h->OldTraffic = NewTraffic();\r
+\r
+       h->Option = ZeroMalloc(sizeof(HUB_OPTION));\r
+       Copy(h->Option, option, sizeof(HUB_OPTION));\r
+\r
+       if (h->Option->VlanTypeId == 0)\r
+       {\r
+               h->Option->VlanTypeId = MAC_PROTO_TAGVLAN;\r
+       }\r
+\r
+       Rand(h->HubSignature, sizeof(h->HubSignature));\r
+\r
+       // SecureNAT 関係\r
+       h->EnableSecureNAT = false;\r
+       h->SecureNAT = NULL;\r
+       h->SecureNATOption = ZeroMalloc(sizeof(VH_OPTION));\r
+       NiSetDefaultVhOption(NULL, h->SecureNATOption);\r
+\r
+       if (h->Cedar != NULL && h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_CONTROLLER)\r
+       {\r
+               NiClearUnsupportedVhOptionForDynamicHub(h->SecureNATOption, true);\r
+       }\r
+\r
+       // HUB 用の一時的な MAC アドレスを生成する\r
+       GenerateMachineUniqueHash(hash);\r
+       GenHubMacAddress(h->HubMacAddr, h->Name);\r
+       GenHubIpAddress(&h->HubIp, h->Name);\r
+\r
+       // HUB 用 IPv6 アドレス\r
+       GenerateEui64LocalAddress(&ip6, h->HubMacAddr);\r
+       IPToIPv6Addr(&h->HubIpV6, &ip6);\r
+\r
+       h->RadiusOptionLock = NewLock();\r
+       h->RadiusServerPort = RADIUS_DEFAULT_PORT;\r
+\r
+       h->TicketList = NewList(NULL);\r
+\r
+       InitAccessList(h);\r
+\r
+       // デフォルトのログ設定\r
+       h->LogSetting.SaveSecurityLog = true;\r
+       h->LogSetting.SavePacketLog = false;\r
+       h->LogSetting.PacketLogConfig[PACKET_LOG_TCP_CONN] =\r
+               h->LogSetting.PacketLogConfig[PACKET_LOG_DHCP] = PACKET_LOG_HEADER;\r
+       h->LogSetting.SecurityLogSwitchType = LOG_SWITCH_DAY;\r
+       h->LogSetting.PacketLogSwitchType = LOG_SWITCH_DAY;\r
+\r
+       MakeDir(HUB_SECURITY_LOG_DIR_NAME);\r
+       MakeDir(HUB_PACKET_LOG_DIR_NAME);\r
+\r
+       // パケットロガーの開始\r
+       Format(packet_logger_name, sizeof(packet_logger_name), HUB_PACKET_LOG_FILE_NAME, h->Name);\r
+       h->PacketLogger = NewLog(packet_logger_name, HUB_PACKET_LOG_PREFIX, h->LogSetting.PacketLogSwitchType);\r
+\r
+       // セキュリティロガーの開始\r
+       Format(tmp, sizeof(tmp), HUB_SECURITY_LOG_FILE_NAME, h->Name);\r
+       h->SecurityLogger = NewLog(tmp, HUB_SECURITY_LOG_PREFIX, h->LogSetting.SecurityLogSwitchType);\r
+\r
+       if (h->Cedar->Server != NULL && h->Cedar->Server->ServerType == SERVER_TYPE_FARM_MEMBER)\r
+       {\r
+               h->FarmMember = true;\r
+       }\r
+\r
+       // HUB の開始\r
+       SetHubOnline(h);\r
+\r
+       if (h->Cedar->Bridge)\r
+       {\r
+               h->Option->NoArpPolling = true;\r
+       }\r
+\r
+       if (h->Option->NoArpPolling == false && h->Option->NoIpTable == false)\r
+       {\r
+               StartHubWatchDog(h);\r
+               h->WatchDogStarted = true;\r
+       }\r
+\r
+       SLog(h->Cedar, "LS_HUB_START", h->Name);\r
+\r
+       MacToStr(tmp, sizeof(tmp), h->HubMacAddr);\r
+       SLog(h->Cedar, "LS_HUB_MAC", h->Name, tmp);\r
+\r
+       return h;\r
+}\r
+\r
+// HUBDB の削除\r
+void DeleteHubDb(HUBDB *d)\r
+{\r
+       // 引数チェック\r
+       if (d == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       LockList(d->UserList);\r
+       {\r
+               LockList(d->GroupList);\r
+               {\r
+                       // すべてのユーザーとグループを解放\r
+                       UINT i;\r
+                       USER **users;\r
+                       USERGROUP **groups;\r
+\r
+                       users = ToArray(d->UserList);\r
+                       groups = ToArray(d->GroupList);\r
+\r
+                       for (i = 0;i < LIST_NUM(d->UserList);i++)\r
+                       {\r
+                               ReleaseUser(users[i]);\r
+                       }\r
+                       for (i = 0;i < LIST_NUM(d->GroupList);i++)\r
+                       {\r
+                               ReleaseGroup(groups[i]);\r
+                       }\r
+\r
+                       Free(users);\r
+                       Free(groups);\r
+               }\r
+               UnlockList(d->GroupList);\r
+       }\r
+       UnlockList(d->UserList);\r
+\r
+       // ルート証明書一覧を解放\r
+       LockList(d->RootCertList);\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(d->RootCertList);i++)\r
+               {\r
+                       X *x = LIST_DATA(d->RootCertList, i);\r
+                       FreeX(x);\r
+               }\r
+       }\r
+       UnlockList(d->RootCertList);\r
+\r
+       // CRL を解放\r
+       LockList(d->CrlList);\r
+       {\r
+               UINT i;\r
+               for (i = 0;i < LIST_NUM(d->CrlList);i++)\r
+               {\r
+                       CRL *crl = LIST_DATA(d->CrlList, i);\r
+                       FreeCrl(crl);\r
+               }\r
+       }\r
+       UnlockList(d->CrlList);\r
+\r
+       ReleaseList(d->GroupList);\r
+       ReleaseList(d->UserList);\r
+       ReleaseList(d->RootCertList);\r
+       ReleaseList(d->CrlList);\r
+       Free(d);\r
+}\r
+\r
+// HUB のログ設定を取得する\r
+void GetHubLogSetting(HUB *h, HUB_LOG *setting)\r
+{\r
+       // 引数チェック\r
+       if (setting == NULL || h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       Copy(setting, &h->LogSetting, sizeof(HUB_LOG));\r
+}\r
+\r
+// HUB のログ設定を更新する\r
+void SetHubLogSettingEx(HUB *h, HUB_LOG *setting, bool no_change_switch_type)\r
+{\r
+       UINT i1, i2;\r
+       // 引数チェック\r
+       if (setting == NULL || h == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       i1 = h->LogSetting.PacketLogSwitchType;\r
+       i2 = h->LogSetting.SecurityLogSwitchType;\r
+\r
+       Copy(&h->LogSetting, setting, sizeof(HUB_LOG));\r
+\r
+       if (no_change_switch_type)\r
+       {\r
+               h->LogSetting.PacketLogSwitchType = i1;\r
+               h->LogSetting.SecurityLogSwitchType = i2;\r
+       }\r
+\r
+       // パケットロガー設定\r
+       SetLogSwitchType(h->PacketLogger, setting->PacketLogSwitchType);\r
+       SetLogSwitchType(h->SecurityLogger, setting->SecurityLogSwitchType);\r
+}\r
+void SetHubLogSetting(HUB *h, HUB_LOG *setting)\r
+{\r
+       SetHubLogSettingEx(h, setting, false);\r
+}\r
+\r
+// HUB に信頼するルート証明書を追加する\r
+void AddRootCert(HUB *hub, X *x)\r
+{\r
+       HUBDB *db;\r
+       // 引数チェック\r
+       if (hub == NULL || x == NULL)\r
+       {\r
+               return;\r
+       }\r
+\r
+       db = hub->HubDb;\r
+       if (db != NULL)\r
+       {\r
+               LockList(db->RootCertList);\r
+               {\r
+                       if (LIST_NUM(db->RootCertList) < MAX_HUB_CERTS)\r
+                       {\r
+                               UINT i;\r
+                               bool ok = true;\r
+\r
+                               for (i = 0;i < LIST_NUM(db->RootCertList);i++)\r
+                               {\r
+                                       X *exist_x = LIST_DATA(db->RootCertList, i);\r
+                                       if (CompareX(exist_x, x))\r
+                                       {\r
+                                               ok = false;\r
+                                               break;\r
+                                       }\r
+                               }\r
+\r
+                               if (ok)\r
+                               {\r
+                                       Insert(db->RootCertList, CloneX(x));\r
+                               }\r
+                       }\r
+               }\r
+               UnlockList(db->RootCertList);\r
+       }\r
+}\r
+\r
+// 証明書リストの比較\r
+int CompareCert(void *p1, void *p2)\r
+{\r
+       X *x1, *x2;\r
+       wchar_t tmp1[MAX_SIZE];\r
+       wchar_t tmp2[MAX_SIZE];\r
+       if (p1 == NULL || p2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+       x1 = *(X **)p1;\r
+       x2 = *(X **)p2;\r
+       if (x1 == NULL || x2 == NULL)\r
+       {\r
+               return 0;\r
+       }\r
+\r
+       GetPrintNameFromX(tmp1, sizeof(tmp1), x1);\r
+       GetPrintNameFromX(tmp2, sizeof(tmp2), x2);\r
+\r
+       return UniStrCmpi(tmp1, tmp2);\r
+}\r
+\r
+// 新しい HUBDB の作成\r
+HUBDB *NewHubDb()\r
+{\r
+       HUBDB *d = ZeroMalloc(sizeof(HUBDB));\r
+\r
+       d->GroupList = NewList(CompareGroupName);\r
+       d->UserList = NewList(CompareUserName);\r
+       d->RootCertList = NewList(CompareCert);\r
+       d->CrlList = NewList(NULL);\r
+\r
+       return d;\r
+}\r
+\r
+\r