* copy vendor drop to trunk
[lab.git] / Dev / utvpn / utvpn-unix-v101-7101-public / src / Cedar / Virtual.c
1 // SoftEther UT-VPN SourceCode\r
2 // \r
3 // Copyright (C) 2004-2010 SoftEther Corporation.\r
4 // Copyright (C) 2004-2010 University of Tsukuba, Japan.\r
5 // Copyright (C) 2003-2010 Daiyuu Nobori.\r
6 // All Rights Reserved.\r
7 // \r
8 // http://utvpn.tsukuba.ac.jp/\r
9 // \r
10 // This program is free software; you can redistribute it and/or\r
11 // modify it under the terms of the GNU General Public License\r
12 // version 2 as published by the Free Software Foundation.\r
13 // \r
14 // This program is distributed in the hope that it will be useful,\r
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
17 // GNU General Public License for more details.\r
18 // \r
19 // You should have received a copy of the GNU General Public License version 2\r
20 // along with this program; if not, write to the Free Software\r
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
22 // \r
23 // このファイルは GPL バージョン 2 ライセンスで公開されています。\r
24 // 誰でもこのファイルの内容を複製、改変したり、改変したバージョンを再配布\r
25 // することができます。ただし、原著作物を改変した場合は、原著作物の著作権表示\r
26 // を除去することはできません。改変した著作物を配布する場合は、改変実施者の\r
27 // 著作権表示を原著作物の著作権表示に付随して記載するようにしてください。\r
28 // \r
29 // この SoftEther UT-VPN オープンソース・プロジェクトは、日本国の\r
30 // ソフトイーサ株式会社 (SoftEther Corporation, http://www.softether.co.jp/ )\r
31 // および筑波大学 (University of Tsukuba, http://www.tsukuba.ac.jp/ ) によって\r
32 // ホストされています。\r
33 // 本プログラムの配布者は、本プログラムを、業としての利用以外のため、\r
34 // および、試験または研究のために利用が行われることを想定して配布\r
35 // しています。\r
36 // SoftEther UT-VPN プロジェクトの Web サイトは http://utvpn.tsukuba.ac.jp/ に\r
37 // あります。\r
38 // 本ソフトウェアの不具合の修正、機能改良、セキュリティホールの修復などのコード\r
39 // の改変を行った場合で、その成果物を SoftEther UT-VPN プロジェクトに提出して\r
40 // いただける場合は、 http://utvpn.tsukuba.ac.jp/ までソースコードを送付して\r
41 // ください。SoftEther UT-VPN プロジェクトの本体リリースまたはブランチリリース\r
42 // に組み込みさせていただきます。\r
43 // \r
44 // GPL に基づいて原著作物が提供される本ソフトウェアの改良版を配布、販売する\r
45 // 場合は、そのソースコードを GPL に基づいて誰にでも開示する義務が生じます。\r
46 // \r
47 // 本ソフトウェアに関連する著作権、特許権、商標権はソフトイーサ株式会社\r
48 // (SoftEther Corporation) およびその他の著作権保持者が保有しています。\r
49 // ソフトイーサ株式会社等はこれらの権利を放棄していません。本ソフトウェアの\r
50 // 二次著作物を配布、販売する場合は、これらの権利を侵害しないようにご注意\r
51 // ください。\r
52 // \r
53 // お願い: どのような通信ソフトウェアにも通常は必ず未発見の\r
54 // セキュリティホールが潜んでいます。本ソースコードをご覧いただいた結果、\r
55 // UT-VPN にセキュリティホールを発見された場合は、当該セキュリティホールの\r
56 // 情報を不特定多数に開示される前に、必ず、ソフトイーサ株式会社\r
57 // および脆弱性情報の届出を受け付ける公的機関まで通報いただき、\r
58 // 公益保護にご協力いただきますようお願い申し上げます。\r
59 // \r
60 // ソフトイーサ株式会社は、当該セキュリティホールについて迅速に対処を\r
61 // 行い、UT-VPN および UT-VPN に関連するソフトウェアのユーザー・顧客\r
62 // を保護するための努力を行います。\r
63 // \r
64 // ソフトイーサへの届出先: http://www.softether.co.jp/jp/contact/\r
65 // 日本国内の脆弱性情報届出受付公的機関:\r
66 //         独立行政法人 情報処理推進機構\r
67 //         http://www.ipa.go.jp/security/vuln/report/\r
68 // \r
69 // 上記各事項について不明な点は、ソフトイーサ株式会社までご連絡ください。\r
70 // 連絡先: http://www.softether.co.jp/jp/contact/\r
71 \r
72 // -----------------------------------------------\r
73 // [ChangeLog]\r
74 // 2010.05.20\r
75 //  新規リリース by SoftEther\r
76 // -----------------------------------------------\r
77 \r
78 // Virtual.c\r
79 // ユーザーモード仮想ホストプログラム\r
80 \r
81 // Q. このプログラムには大変多くの TCP/IP 関係の奥の深い処理が入っていて\r
82 //    さらに TCP スタックの簡易的な実装まで完結しているように見えるが\r
83 //    なぜこのように気合の入ったコードを書いたのか?\r
84 // A. 作者の所属していた筑波大学第三学群情報学類という学科では、\r
85 //    「情報特別演習」という授業がある。なんとこれは専門基礎単位を取得\r
86 //    することができる貴重なコマである。専門基礎単位を取得するための他の\r
87 //    方法としては、解析学Ⅲなどという授業等がある。しかし、作者は受験勉強\r
88 //    のような数学のテスト勉強は嫌いであるし、また、SoftEther に関する打ち合わせ\r
89 //    のために大学の某所に駐車したときにその解析学Ⅲの当時の担当教員の車のミラー\r
90 //    をこすってしまって少し怒られたのでその単位の取得は難しくなった (?) のである。\r
91 //    だが、専門基礎の単位を取得しなければ、卒業することができない。\r
92 //    そこで代わりに「情報特別演習」で単位をとることにしたのだが、この授業は、\r
93 //    何か面白いコンピュータの作品を作ってプレゼンテーションすれば 2 単位がくる\r
94 //    という名目なのであった。しかし、実際のところ、ろくな作品を 1 年間で\r
95 //    作ってプレゼンする人はとても少なく、たいていが「無線 LAN の実験」とか\r
96 //    そういうほとんどクリエイティブな作業を必要とせずにできるいいかげんな\r
97 //    「実習」を行ったと言って結果を報告すれば 2 単位来るという程度の難易度\r
98 //    であることが後になって分かった。\r
99 //    作者は大変真面目なので、部類としてはかなり難しい難易度に入る TCP/IP\r
100 //    スタックの開発を実習としてやってみようと思い、数週間かけていろいろ\r
101 //    プログラムを書いた。その成果の一部がこの Virtual.c というコードである。\r
102 //    逆にいうと、大学の授業の実習程度で開発することができるくらいの簡単な\r
103 //    プログラムなので、実装は簡易的であり、効率が悪く、メンテナンス性にも欠け\r
104 //    ている。結構場当たり的なプログラミングを行ったためである。\r
105 //    それでも 2004 年末に本プログラムをインターネットで配布してから今まで\r
106 //    一応は深刻な不具合は発生していない。ただし探せば深刻な障害が潜んでいる\r
107 //    かも知れない。\r
108 \r
109 \r
110 #include "CedarPch.h"\r
111 \r
112 static UCHAR broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
113 \r
114 // Virtual Host のログをとる\r
115 void VLog(VH *v, char *str)\r
116 {\r
117         // とらん!!\r
118         return;\r
119 }\r
120 \r
121 // NAT が使用可能かどうかチェック\r
122 bool CanCreateNewNatEntry(VH *v)\r
123 {\r
124         // 引数チェック\r
125         if (v == NULL)\r
126         {\r
127                 return false;\r
128         }\r
129 \r
130         if (v->UseNat == false)\r
131         {\r
132                 // NAT 停止中\r
133                 return false;\r
134         }\r
135 \r
136         if (v->NatTable->num_item > NAT_MAX_SESSIONS)\r
137         {\r
138                 // セッション数超過\r
139                 return false;\r
140         }\r
141 \r
142         return true;\r
143 }\r
144 \r
145 // NAT 処理スレッドのメイン関数\r
146 void NatThreadMain(VH *v)\r
147 {\r
148         bool halt_flag;\r
149         // 引数チェック\r
150         if (v == NULL)\r
151         {\r
152                 return;\r
153         }\r
154 \r
155         v->TmpBuf = Malloc(NAT_TMPBUF_SIZE);\r
156 \r
157         while (true)\r
158         {\r
159                 // 次のイベントがセットされるまで待機する\r
160                 WaitSockEvent(v->SockEvent, SELECT_TIME);\r
161 \r
162                 halt_flag = false;\r
163 \r
164                 LockVirtual(v);\r
165                 {\r
166                         // すべての NAT セッションに対して処理を行う\r
167                         UINT i, num;\r
168 \r
169                         v->Now = Tick64();\r
170                         v->NatDoCancelFlag = false;\r
171 \r
172 LIST_ELEMENT_DELETED:\r
173                         num = LIST_NUM(v->NatTable);\r
174                         for (i = 0;i < num;i++)\r
175                         {\r
176                                 NAT_ENTRY *n = LIST_DATA(v->NatTable, i);\r
177 \r
178                                 switch (n->Protocol)\r
179                                 {\r
180                                 case NAT_TCP:           // TCP\r
181                                         if (NatTransactTcp(v, n) == false)\r
182                                         {\r
183                                                 goto LIST_ELEMENT_DELETED;\r
184                                         }\r
185                                         break;\r
186 \r
187                                 case NAT_UDP:           // UDP\r
188                                         if (NatTransactUdp(v, n) == false)\r
189                                         {\r
190                                                 goto LIST_ELEMENT_DELETED;\r
191                                         }\r
192                                         break;\r
193 \r
194                                 case NAT_DNS:           // DNS\r
195                                         if (NatTransactDns(v, n) == false)\r
196                                         {\r
197                                                 goto LIST_ELEMENT_DELETED;\r
198                                         }\r
199                                         break;\r
200                                 }\r
201                         }\r
202 \r
203                         if (v->NatDoCancelFlag)\r
204                         {\r
205                                 // 親スレッドの Cancel を叩く\r
206                                 Cancel(v->Cancel);\r
207                         }\r
208 \r
209                         // 停止フラグチェック\r
210                         if (v->HaltNat)\r
211                         {\r
212                                 halt_flag = true;\r
213                         }\r
214                 }\r
215                 UnlockVirtual(v);\r
216 \r
217                 if (halt_flag)\r
218                 {\r
219                         // すべてのエントリを強制切断してからスレッドを終了する\r
220                         LockVirtual(v);\r
221                         {\r
222                                 UINT num = LIST_NUM(v->NatTable);\r
223                                 NAT_ENTRY **nn = ToArray(v->NatTable);\r
224                                 UINT i;\r
225 \r
226                                 for (i = 0;i < num;i++)\r
227                                 {\r
228                                         NAT_ENTRY *n = nn[i];\r
229                                         n->DisconnectNow = true;\r
230 \r
231                                         switch (n->Protocol)\r
232                                         {\r
233                                         case NAT_TCP:           // TCP\r
234                                                 NatTransactTcp(v, n);\r
235                                                 break;\r
236 \r
237                                         case NAT_UDP:           // UDP\r
238                                                 NatTransactUdp(v, n);\r
239                                                 break;\r
240 \r
241                                         case NAT_DNS:           // DNS\r
242                                                 NatTransactDns(v, n);\r
243                                                 break;\r
244                                         }\r
245                                 }\r
246 \r
247                                 Free(nn);\r
248                         }\r
249                         UnlockVirtual(v);\r
250                         break;\r
251                 }\r
252         }\r
253 \r
254         Free(v->TmpBuf);\r
255 }\r
256 \r
257 // DNS: IP アドレスを取得するスレッド\r
258 void NatGetIPThread(THREAD *t, void *param)\r
259 {\r
260         NAT_DNS_QUERY *q;\r
261         // 引数チェック\r
262         if (t == NULL || param == NULL)\r
263         {\r
264                 return;\r
265         }\r
266 \r
267         q = (NAT_DNS_QUERY *)param;\r
268         AddWaitThread(t);\r
269 \r
270         q->Ok = GetIP(&q->Ip, q->Hostname);\r
271 \r
272         DelWaitThread(t);\r
273 \r
274         if (Release(q->ref) == 0)\r
275         {\r
276                 Free(q);\r
277         }\r
278 }\r
279 \r
280 // DNS: ホスト名から IP アドレスを取得する\r
281 bool NatGetIP(IP *ip, char *hostname)\r
282 {\r
283         TOKEN_LIST *t;\r
284         bool ret = false;\r
285         // 引数チェック\r
286         if (ip == NULL || hostname == NULL)\r
287         {\r
288                 return false;\r
289         }\r
290 \r
291         t = ParseToken(hostname, ".");\r
292         if (t == NULL)\r
293         {\r
294                 return false;\r
295         }\r
296         if (t->NumTokens == 0)\r
297         {\r
298                 FreeToken(t);\r
299                 return false;\r
300         }\r
301 \r
302         if (t->NumTokens == 1)\r
303         {\r
304                 ret = GetIP(ip, hostname);\r
305         }\r
306         else\r
307         {\r
308                 char *hostname2 = t->Token[0];\r
309                 NAT_DNS_QUERY *q1, *q2;\r
310                 THREAD *t1, *t2;\r
311 \r
312                 q1 = ZeroMalloc(sizeof(NAT_DNS_QUERY));\r
313                 q2 = ZeroMalloc(sizeof(NAT_DNS_QUERY));\r
314                 q1->ref = NewRef();\r
315                 q2->ref = NewRef();\r
316                 AddRef(q1->ref);\r
317                 AddRef(q2->ref);\r
318                 StrCpy(q1->Hostname, sizeof(q1->Hostname), hostname);\r
319                 StrCpy(q2->Hostname, sizeof(q2->Hostname), hostname2);\r
320 \r
321                 t1 = NewThread(NatGetIPThread, q1);\r
322                 t2 = NewThread(NatGetIPThread, q2);\r
323 \r
324                 WaitThread(t1, NAT_DNS_QUERY_TIMEOUT);\r
325 \r
326                 if (q1->Ok)\r
327                 {\r
328                         ret = true;\r
329                         Copy(ip, &q1->Ip, sizeof(IP));\r
330                 }\r
331                 else\r
332                 {\r
333                         WaitThread(t2, NAT_DNS_QUERY_TIMEOUT);\r
334                         if (q1->Ok)\r
335                         {\r
336                                 ret = true;\r
337                                 Copy(ip, &q1->Ip, sizeof(IP));\r
338                         }\r
339                         else if (q2->Ok)\r
340                         {\r
341                                 ret = true;\r
342                                 Copy(ip, &q2->Ip, sizeof(IP));\r
343                         }\r
344                 }\r
345 \r
346                 ReleaseThread(t1);\r
347                 ReleaseThread(t2);\r
348 \r
349                 if (Release(q1->ref) == 0)\r
350                 {\r
351                         Free(q1);\r
352                 }\r
353                 if (Release(q2->ref) == 0)\r
354                 {\r
355                         Free(q2);\r
356                 }\r
357         }\r
358 \r
359         FreeToken(t);\r
360 \r
361         return ret;\r
362 }\r
363 \r
364 // DNS 問い合わせ関数\r
365 void NatDnsThread(THREAD *t, void *param)\r
366 {\r
367         NAT_ENTRY *n;\r
368         IP ip;\r
369         // 引数チェック\r
370         if (t == NULL || param == NULL)\r
371         {\r
372                 return;\r
373         }\r
374         n = (NAT_ENTRY *)param;\r
375 \r
376         // 初期化完了を通知\r
377         NoticeThreadInit(t);\r
378 \r
379         // 処理を実行\r
380         if (EndWith(n->DnsTargetHostName, ".in-addr.arpa") == false)\r
381         {\r
382                 // 正引き\r
383                 if (NatGetIP(&ip, n->DnsTargetHostName))\r
384                 {\r
385                         // 正引き成功\r
386                         Copy(&n->DnsResponseIp, &ip, sizeof(IP));\r
387                         n->DnsOk = true;\r
388                 }\r
389         }\r
390         else\r
391         {\r
392                 // 逆引き\r
393                 IP ip;\r
394                 n->DnsGetIpFromHost = true;             // 逆引きフラグを設定\r
395                 // *.in-addr.arpa 文字列を IP アドレスに変換してもらう\r
396                 if (ArpaToIP(&ip, n->DnsTargetHostName))\r
397                 {\r
398                         // 逆引き処理\r
399                         char tmp[256];\r
400                         if (GetHostName(tmp, sizeof(tmp), &ip))\r
401                         {\r
402                                 // 逆引き成功\r
403                                 n->DnsResponseHostName = CopyStr(tmp);\r
404                                 n->DnsOk = true;\r
405                         }\r
406                 }\r
407         }\r
408 \r
409         // 結果を通知\r
410         n->DnsFinished = true;\r
411 \r
412         SetSockEvent(n->v->SockEvent);\r
413 }\r
414 \r
415 // 逆引き用アドレスを IP アドレスに変換する\r
416 bool ArpaToIP(IP *ip, char *str)\r
417 {\r
418         TOKEN_LIST *token;\r
419         bool ret = false;\r
420         // 引数チェック\r
421         if (ip == NULL || str == NULL)\r
422         {\r
423                 return false;\r
424         }\r
425 \r
426         // トークン変換\r
427         token = ParseToken(str, ".");\r
428         if (token->NumTokens == 6)\r
429         {\r
430                 // token[0, 1, 2, 3] を IP に変換\r
431                 UINT i;\r
432                 Zero(ip, sizeof(IP));\r
433                 for (i = 0;i < 4;i++)\r
434                 {\r
435                         ip->addr[i] = (UCHAR)ToInt(token->Token[3 - i]);\r
436                 }\r
437                 ret = true;\r
438         }\r
439 \r
440         FreeToken(token);\r
441 \r
442         if (IPToUINT(ip) == 0)\r
443         {\r
444                 ret = false;\r
445         }\r
446 \r
447         return ret;\r
448 }\r
449 \r
450 // DNS エントリを処理する\r
451 bool NatTransactDns(VH *v, NAT_ENTRY *n)\r
452 {\r
453         // 引数チェック\r
454         if (v == NULL || n == NULL)\r
455         {\r
456                 return true;\r
457         }\r
458 \r
459         if (n->DisconnectNow)\r
460         {\r
461                 goto DISCONNECT;\r
462         }\r
463 \r
464         if (n->DnsThread == NULL && n->DnsFinished == false)\r
465         {\r
466                 // スレッドを作成する\r
467                 THREAD *t = NewThread(NatDnsThread, (void *)n);\r
468                 WaitThreadInit(t);\r
469                 n->DnsThread = t;\r
470         }\r
471         else\r
472         {\r
473                 // 結果を待機する\r
474                 if (n->DnsFinished)\r
475                 {\r
476                         // 結果が届いている\r
477                         WaitThread(n->DnsThread, INFINITE);\r
478                         ReleaseThread(n->DnsThread);\r
479                         n->DnsThread = NULL;\r
480                         // メインスレッドに通知\r
481                         v->NatDoCancelFlag = true;\r
482                 }\r
483         }\r
484 \r
485         return true;\r
486 \r
487 DISCONNECT:\r
488 \r
489         // 解放処理\r
490         if (n->DnsThread != NULL)\r
491         {\r
492                 WaitThread(n->DnsThread, INFINITE);\r
493                 ReleaseThread(n->DnsThread);\r
494                 n->DnsThread = NULL;\r
495         }\r
496 \r
497         if (n->DnsTargetHostName != NULL)\r
498         {\r
499                 Free(n->DnsTargetHostName);\r
500                 n->DnsTargetHostName = NULL;\r
501         }\r
502 \r
503         if (n->DnsResponseHostName != NULL)\r
504         {\r
505                 Free(n->DnsResponseHostName);\r
506                 n->DnsResponseHostName = NULL;\r
507         }\r
508 \r
509         DeleteLock(n->lock);\r
510         Delete(v->NatTable, n);\r
511         Free(n);\r
512 \r
513         return false;\r
514 }\r
515 \r
516 // UDP エントリを処理する\r
517 bool NatTransactUdp(VH *v, NAT_ENTRY *n)\r
518 {\r
519         void *buf;\r
520         UINT recv_size;\r
521         BLOCK *block;\r
522         UINT dest_port = n->DestPort;\r
523         IP dest_ip;\r
524         // 引数チェック\r
525         if (v == NULL || n == NULL)\r
526         {\r
527                 return true;\r
528         }\r
529 \r
530         if (n->DisconnectNow)\r
531         {\r
532                 goto DISCONNECT;\r
533         }\r
534 \r
535         if (n->UdpSocketCreated == false)\r
536         {\r
537                 // UDP ソケットを作成する\r
538                 n->Sock = NewUDP(0);\r
539                 if (n->Sock == NULL)\r
540                 {\r
541                         // ソケット作成失敗\r
542                         goto DISCONNECT;\r
543                 }\r
544                 else\r
545                 {\r
546                         n->PublicIp = IPToUINT(&n->Sock->LocalIP);\r
547                         n->PublicPort = n->Sock->LocalPort;\r
548 \r
549                         JoinSockToSockEvent(n->Sock, v->SockEvent);\r
550                         n->UdpSocketCreated = true;\r
551                 }\r
552         }\r
553 \r
554         buf = v->TmpBuf;\r
555         if (n->ProxyDns == false)\r
556         {\r
557                 UINTToIP(&dest_ip, n->DestIp);\r
558         }\r
559         else\r
560         {\r
561                 UINTToIP(&dest_ip, n->DestIpProxy);\r
562         }\r
563 \r
564         // UDP ソケットからデータを受信してみる\r
565         while (true)\r
566         {\r
567                 IP src_ip;\r
568                 UINT src_port;\r
569                 recv_size = RecvFrom(n->Sock, &src_ip, &src_port, buf, 65536);\r
570 \r
571                 if (recv_size == SOCK_LATER)\r
572                 {\r
573                         // パケットが届いていない\r
574                         break;\r
575                 }\r
576                 else if (recv_size == 0)\r
577                 {\r
578                         // エラー?\r
579                         if (n->Sock->IgnoreRecvErr == false)\r
580                         {\r
581                                 // 致命的なエラーが発生した\r
582                                 goto DISCONNECT;\r
583                         }\r
584                 }\r
585                 else\r
586                 {\r
587                         // パケットが届いた。送信元 IP をチェック\r
588                         if (IPToUINT(&src_ip) == n->DestIp || (IPToUINT(&src_ip) == n->DestIpProxy && n->ProxyDns) && src_port == n->DestPort)\r
589                         {\r
590                                 // キューに挿入\r
591                                 void *data = Malloc(recv_size);\r
592                                 Copy(data, buf, recv_size);\r
593                                 block = NewBlock(data, recv_size, 0);\r
594                                 InsertQueue(n->UdpRecvQueue, block);\r
595                                 v->NatDoCancelFlag = true;\r
596                                 n->LastCommTime = v->Now;\r
597                         }\r
598                 }\r
599         }\r
600 \r
601         // UDP ソケットにデータを送信してみる\r
602         while (block = GetNext(n->UdpSendQueue))\r
603         {\r
604                 UINT send_size = SendTo(n->Sock, &dest_ip, dest_port, block->Buf, block->Size);\r
605 \r
606                 FreeBlock(block);\r
607                 if (send_size == 0)\r
608                 {\r
609                         // 致命的なエラーかどうか判定\r
610                         if (n->Sock->IgnoreSendErr == false)\r
611                         {\r
612                                 // 致命的なエラーが発生した\r
613                                 goto DISCONNECT;\r
614                         }\r
615                 }\r
616                 else\r
617                 {\r
618                         n->LastCommTime = v->Now;\r
619                 }\r
620         }\r
621 \r
622         // このセッションがタイムアウトになっていないかどうか調べる\r
623         if ((n->LastCommTime + (UINT64)v->NatUdpTimeout) < v->Now || n->LastCommTime > v->Now)\r
624         {\r
625                 // タイムアウトである\r
626                 goto DISCONNECT;\r
627         }\r
628 \r
629         return true;\r
630 \r
631 DISCONNECT:\r
632         // このセッションを切断\r
633         if (n->UdpSocketCreated)\r
634         {\r
635                 // ソケットを閉じる\r
636                 Disconnect(n->Sock);\r
637                 ReleaseSock(n->Sock);\r
638                 n->Sock = NULL;\r
639         }\r
640 \r
641         // エントリを削除\r
642         DeleteNatUdp(v, n);\r
643 \r
644         return false;\r
645 }\r
646 \r
647 // TCP ホストへの接続処理を行うためのスレッド\r
648 void NatTcpConnectThread(THREAD *t, void *p)\r
649 {\r
650         NAT_ENTRY *n = (NAT_ENTRY *)p;\r
651         IP ip;\r
652         char hostname[MAX_SIZE];\r
653         UINT port_number;\r
654         SOCK *sock;\r
655         SOCK_EVENT *e;\r
656         // 引数チェック\r
657         if (n == NULL || t == NULL)\r
658         {\r
659                 return;\r
660         }\r
661 \r
662         UINTToIP(&ip, n->DestIp);\r
663         IPToStr(hostname, sizeof(hostname), &ip);\r
664         port_number = n->DestPort;\r
665         e = n->v->SockEvent;\r
666         AddRef(e->ref);\r
667 \r
668         // 初期化完了を通知\r
669         NoticeThreadInit(t);\r
670 \r
671         // TCP ホストへの接続を試行\r
672         Debug("NatTcpConnect Connecting to %s:%u\n", hostname, port_number);\r
673         sock = Connect(hostname, port_number);\r
674         if (sock == NULL)\r
675         {\r
676                 // 接続失敗\r
677                 n->TcpMakeConnectionFailed = true;\r
678         }\r
679         else\r
680         {\r
681                 // 接続成功\r
682                 n->TcpMakeConnectionSucceed = true;\r
683         }\r
684         n->Sock = sock;\r
685         JoinSockToSockEvent(sock, e);\r
686         SetSockEvent(e);\r
687 \r
688         ReleaseSockEvent(e);\r
689 }\r
690 \r
691 // TCP ホストに接続するためのスレッドを作成する\r
692 void CreateNatTcpConnectThread(VH *v, NAT_ENTRY *n)\r
693 {\r
694         // 引数チェック\r
695         if (v == NULL || n == NULL)\r
696         {\r
697                 return;\r
698         }\r
699 \r
700         // スレッド作成\r
701         n->NatTcpConnectThread = NewThread(NatTcpConnectThread, (void *)n);\r
702 \r
703         // スレッド初期化完了を待機\r
704         WaitThreadInit(n->NatTcpConnectThread);\r
705 }\r
706 \r
707 // TCP エントリを処理する\r
708 bool NatTransactTcp(VH *v, NAT_ENTRY *n)\r
709 {\r
710         char str[MAX_SIZE];\r
711         // 引数チェック\r
712         if (v == NULL || n == NULL)\r
713         {\r
714                 return false;\r
715         }\r
716 \r
717         if (n->DisconnectNow)\r
718         {\r
719                 goto DISCONNECT;\r
720         }\r
721 \r
722         // TCP の状態別に処理を行う\r
723         switch (n->TcpStatus)\r
724         {\r
725         case NAT_TCP_CONNECTING:                // 接続待機中\r
726                 if (n->NatTcpConnectThread == NULL)\r
727                 {\r
728                         // 接続スレッドを作成し接続を開始する\r
729                         CreateNatTcpConnectThread(v, n);\r
730                 }\r
731                 else\r
732                 {\r
733                         // すでに開始されている接続スレッドの結果を待機\r
734                         if (n->TcpMakeConnectionFailed || n->TcpMakeConnectionSucceed)\r
735                         {\r
736                                 // スレッドの動作はすでに完了しているので結果を使用する\r
737                                 WaitThread(n->NatTcpConnectThread, INFINITE);\r
738                                 ReleaseThread(n->NatTcpConnectThread);\r
739                                 n->NatTcpConnectThread = NULL;\r
740 \r
741                                 if (n->TcpMakeConnectionSucceed)\r
742                                 {\r
743                                         // 接続は成功し Sock が作成された\r
744                                         n->TcpStatus = NAT_TCP_CONNECTED;\r
745                                         IPToStr32(str, sizeof(str), n->DestIp);\r
746                                         NLog(v, "LH_NAT_TCP_SUCCEED", n->Id, n->Sock->RemoteHostname, str, n->DestPort);\r
747                                 }\r
748                                 else\r
749                                 {\r
750                                         // 接続に失敗した\r
751                                         n->TcpStatus = NAT_TCP_SEND_RESET;\r
752                                         IPToStr32(str, sizeof(str), n->DestIp);\r
753                                         NLog(v, "LH_NAT_TCP_FAILED", n->Id, str, n->DestPort);\r
754                                 }\r
755                                 v->NatDoCancelFlag = true;\r
756                         }\r
757                 }\r
758                 break;\r
759 \r
760         case NAT_TCP_CONNECTED:                 // TCP ソケット接続完了 クライアントホストとの間で交渉中\r
761                 break;\r
762 \r
763         case NAT_TCP_SEND_RESET:                // TCP 通信切断 クライアントホストへ RST を送信\r
764                 break;\r
765 \r
766         case NAT_TCP_ESTABLISHED:               // TCP 接続確立済み\r
767                 {\r
768                         // 受信バッファにデータがあればソケットに対して送信する\r
769                         while (n->RecvFifo->size > 0)\r
770                         {\r
771                                 UINT sent_size = Send(n->Sock, ((UCHAR *)n->RecvFifo->p) + n->RecvFifo->pos,\r
772                                         n->RecvFifo->size, false);\r
773                                 if (sent_size == 0)\r
774                                 {\r
775                                         // 通信が切断された\r
776                                         n->TcpFinished = true;\r
777                                         v->NatDoCancelFlag = true;\r
778                                         break;\r
779                                 }\r
780                                 else if (sent_size == SOCK_LATER)\r
781                                 {\r
782                                         // ブロッキング\r
783                                         break;\r
784                                 }\r
785                                 else\r
786                                 {\r
787                                         // 送信成功\r
788                                         ReadFifo(n->RecvFifo, NULL, sent_size);\r
789                                         n->SendAckNext = true;\r
790                                 }\r
791                         }\r
792                         // ソケットからデータを取得して送信バッファに書き込む\r
793                         while (true)\r
794                         {\r
795                                 void *buf = (void *)v->TmpBuf;\r
796                                 UINT want_to_recv_size = 0;\r
797                                 UINT recv_size;\r
798                                 // 受信したいサイズを計算する\r
799                                 if (n->SendFifo->size < NAT_SEND_BUF_SIZE)\r
800                                 {\r
801                                         // まだ受信できる\r
802                                         want_to_recv_size = MIN(NAT_SEND_BUF_SIZE - n->SendFifo->size, NAT_TMPBUF_SIZE);\r
803                                 }\r
804                                 if (want_to_recv_size == 0)\r
805                                 {\r
806                                         break;\r
807                                 }\r
808                                 recv_size = Recv(n->Sock, buf, want_to_recv_size, false);\r
809                                 if (recv_size == 0)\r
810                                 {\r
811                                         // 通信が切断された\r
812                                         n->TcpFinished = true;\r
813                                         v->NatDoCancelFlag = true;\r
814                                         break;\r
815                                 }\r
816                                 else if (recv_size == SOCK_LATER)\r
817                                 {\r
818                                         // ブロッキング\r
819                                         break;\r
820                                 }\r
821                                 else\r
822                                 {\r
823                                         // 受信成功\r
824                                         WriteFifo(n->SendFifo, buf, recv_size);\r
825                                         v->NatDoCancelFlag = true;\r
826                                 }\r
827                         }\r
828                 }\r
829                 break;\r
830 \r
831         }\r
832 \r
833         // タイムアウトの検出\r
834         if ((n->LastCommTime + (UINT64)v->NatTcpTimeout) < v->Now || n->LastCommTime > v->Now)\r
835         {\r
836                 // タイムアウトが発生、セッション切断\r
837                 n->TcpStatus = NAT_TCP_SEND_RESET;\r
838                 v->NatDoCancelFlag = true;\r
839         }\r
840 \r
841         return true;\r
842 \r
843 DISCONNECT:             // 切断とセッション廃棄処理\r
844         DeleteNatTcp(v, n);\r
845 \r
846         return false;\r
847 }\r
848 \r
849 // TCP NAT エントリの削除\r
850 void DeleteNatTcp(VH *v, NAT_ENTRY *n)\r
851 {\r
852         // 引数チェック\r
853         if (v == NULL || n == NULL)\r
854         {\r
855                 return;\r
856         }\r
857 \r
858         NLog(v, "LH_NAT_TCP_DELETED", n->Id);\r
859 \r
860         // 接続スレッドのシャットダウン\r
861         if (n->NatTcpConnectThread != NULL)\r
862         {\r
863                 WaitThread(n->NatTcpConnectThread, INFINITE);\r
864                 ReleaseThread(n->NatTcpConnectThread);\r
865                 n->NatTcpConnectThread = NULL;\r
866         }\r
867         if (n->Sock != NULL)\r
868         {\r
869                 // ソケット切断\r
870                 Disconnect(n->Sock);\r
871                 ReleaseSock(n->Sock);\r
872                 n->Sock = NULL;\r
873         }\r
874 \r
875         // ウインドウメモリ解放\r
876         if (n->TcpRecvWindow != NULL)\r
877         {\r
878                 ReleaseFifo(n->TcpRecvWindow);\r
879                 n->TcpRecvWindow = NULL;\r
880         }\r
881 \r
882         // ウインドウ受信リスト解放\r
883         if (n->TcpRecvList != NULL)\r
884         {\r
885                 UINT i;\r
886                 for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)\r
887                 {\r
888                         IP_PART *p = LIST_DATA(n->TcpRecvList, i);\r
889                         Free(p);\r
890                 }\r
891                 ReleaseList(n->TcpRecvList);\r
892                 n->TcpRecvList = NULL;\r
893         }\r
894 \r
895         // FIFO 解放\r
896         ReleaseFifo(n->SendFifo);\r
897         ReleaseFifo(n->RecvFifo);\r
898 \r
899         // NAT エントリから削除\r
900         Delete(v->NatTable, n);\r
901 \r
902         DeleteLock(n->lock);\r
903 \r
904         // メモリ解放\r
905         Free(n);\r
906 \r
907         Debug("NAT_ENTRY: DeleteNatTcp\n");\r
908 }\r
909 \r
910 // NAT 処理スレッド\r
911 void NatThread(THREAD *t, void *param)\r
912 {\r
913         // 引数チェック\r
914         if (t == NULL || param == NULL)\r
915         {\r
916                 return;\r
917         }\r
918 \r
919         // 初期化完了を通知\r
920         NoticeThreadInit(t);\r
921 \r
922         NatThreadMain((VH *)param);\r
923 }\r
924 \r
925 // ビーコンパケットの送信\r
926 void SendBeacon(VH *v)\r
927 {\r
928         UINT dest_ip;\r
929         ARPV4_HEADER arp;\r
930         static char beacon_str[] =\r
931                 "SecureNAT Virtual TCP/IP Stack Beacon";\r
932         // 引数チェック\r
933         if (v == NULL)\r
934         {\r
935                 return;\r
936         }\r
937 \r
938         // UDP を送信\r
939         dest_ip = (v->HostIP & v->HostMask) | (~v->HostMask);\r
940         SendUdp(v, dest_ip, 7, v->HostIP, 7, beacon_str, sizeof(beacon_str));\r
941 \r
942         // ARP ヘッダを構築\r
943         arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);\r
944         arp.ProtocolType = Endian16(MAC_PROTO_IPV4);\r
945         arp.HardwareSize = 6;\r
946         arp.ProtocolSize = 4;\r
947         arp.Operation = Endian16(ARP_OPERATION_RESPONSE);\r
948         Copy(arp.SrcAddress, v->MacAddress, 6);\r
949         arp.SrcIP = v->HostIP;\r
950         arp.TargetAddress[0] =\r
951                 arp.TargetAddress[1] =\r
952                 arp.TargetAddress[2] =\r
953                 arp.TargetAddress[3] =\r
954                 arp.TargetAddress[4] =\r
955                 arp.TargetAddress[5] = 0xff;\r
956         arp.TargetIP = dest_ip;\r
957 \r
958         // 送信\r
959         VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));\r
960 }\r
961 \r
962 // TCP パケットの送信\r
963 void SendTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT seq, UINT ack, UINT flag, UINT window_size, UINT mss, void *data, UINT size)\r
964 {\r
965         static UCHAR tcp_mss_option[] = {0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00};\r
966         TCPV4_PSEUDO_HEADER *vh;\r
967         TCP_HEADER *tcp;\r
968         UINT header_size = TCP_HEADER_SIZE;\r
969         UINT total_size;\r
970         // 引数チェック\r
971         if (v == NULL || (size != 0 && data == NULL))\r
972         {\r
973                 return;\r
974         }\r
975 \r
976         // メモリ確保\r
977         vh = Malloc(sizeof(TCPV4_PSEUDO_HEADER) + TCP_HEADER_SIZE + size + 32);\r
978         tcp = (TCP_HEADER *)(((UCHAR *)vh) + sizeof(TCPV4_PSEUDO_HEADER));\r
979 \r
980         if (mss != 0)\r
981         {\r
982                 USHORT *mss_size;\r
983                 mss_size = (USHORT *)(&tcp_mss_option[2]);\r
984                 *mss_size = Endian16((USHORT)mss);\r
985                 header_size += sizeof(tcp_mss_option);\r
986         }\r
987 \r
988         total_size = header_size + size;\r
989         if (total_size > 65536)\r
990         {\r
991                 // パケットが長すぎる\r
992                 Free(vh);\r
993                 return;\r
994         }\r
995 \r
996         // 擬似ヘッダ生成\r
997         vh->SrcIP = src_ip;\r
998         vh->DstIP = dest_ip;\r
999         vh->Reserved = 0;\r
1000         vh->Protocol = IP_PROTO_TCP;\r
1001         vh->PacketLength = Endian16((USHORT)total_size);\r
1002 \r
1003         // TCP ヘッダ生成\r
1004         tcp->SrcPort = Endian16((USHORT)src_port);\r
1005         tcp->DstPort = Endian16((USHORT)dest_port);\r
1006         tcp->SeqNumber = Endian32(seq);\r
1007         tcp->AckNumber = Endian32(ack);\r
1008         tcp->HeaderSizeAndReserved = 0;\r
1009         TCP_SET_HEADER_SIZE(tcp, (UCHAR)(header_size / 4));\r
1010         tcp->Flag = (UCHAR)flag;\r
1011         tcp->WindowSize = Endian16((USHORT)window_size);\r
1012         tcp->Checksum = 0;\r
1013         tcp->UrgentPointer = 0;\r
1014 \r
1015         // オプション値コピー\r
1016         if (mss != 0)\r
1017         {\r
1018                 Copy(((UCHAR *)tcp) + TCP_HEADER_SIZE, tcp_mss_option, sizeof(tcp_mss_option));\r
1019         }\r
1020 \r
1021         // データコピー\r
1022         Copy(((UCHAR *)tcp) + header_size, data, size);\r
1023 \r
1024         // チェックサム計算\r
1025         tcp->Checksum = IpChecksum(vh, total_size + 12);\r
1026 \r
1027         // IP パケットとして送信\r
1028         SendIp(v, dest_ip, src_ip, IP_PROTO_TCP, tcp, total_size);\r
1029 \r
1030         // メモリ解放\r
1031         Free(vh);\r
1032 }\r
1033 \r
1034 // TCP のポーリング処理\r
1035 void PollingNatTcp(VH *v, NAT_ENTRY *n)\r
1036 {\r
1037         // 引数チェック\r
1038         if (v == NULL || n == NULL)\r
1039         {\r
1040                 return;\r
1041         }\r
1042 \r
1043         switch (n->TcpStatus)\r
1044         {\r
1045         case NAT_TCP_CONNECTING:                // ソケット接続中: 何もしない\r
1046                 break;\r
1047 \r
1048         case NAT_TCP_CONNECTED:                 // ソケット接続が完了した SYN+ACK, ACK 処理\r
1049                 if ((n->LastSynAckSentTime > v->Now) || n->LastSynAckSentTime == 0 || ((n->LastSynAckSentTime + (UINT64)(NAT_TCP_SYNACK_SEND_TIMEOUT * (UINT64)(n->SynAckSentCount + 1)) <= v->Now)))\r
1050                 {\r
1051                         n->LastSynAckSentTime = v->Now;\r
1052                         // SYN+ACK を送信する\r
1053                         SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1054                                 (UINT)(n->SendSeqInit + n->SendSeq),\r
1055                                 (UINT)(n->RecvSeqInit + n->RecvSeq),\r
1056                                 TCP_SYN | TCP_ACK, n->TcpRecvWindowSize,\r
1057                                 v->TcpMss, NULL, 0);\r
1058                         n->SynAckSentCount++;\r
1059                 }\r
1060                 break;\r
1061 \r
1062         case NAT_TCP_SEND_RESET:                // コネクションのリセット\r
1063                 // RST を送信する\r
1064                 if (n->TcpFinished == false)\r
1065                 {\r
1066                         SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1067                                 (UINT)(n->SendSeq + n->SendSeqInit),\r
1068                                 (UINT)(n->SendSeq + n->SendSeqInit),\r
1069                                 TCP_RST, 0,\r
1070                                 0, NULL, 0);\r
1071                         // 切断する\r
1072                         n->TcpStatus = NAT_TCP_WAIT_DISCONNECT;\r
1073                         n->DisconnectNow = true;\r
1074                 }\r
1075                 else\r
1076                 {\r
1077                         // 合計 NAT_FIN_SEND_MAX_COUNT 回の FIN を送信する\r
1078                         if (n->FinSentTime == 0 || (n->FinSentTime > v->Now) || (n->FinSentTime + NAT_FIN_SEND_INTERVAL * (n->FinSentCount + 1)) < v->Now)\r
1079                         {\r
1080                                 SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1081                                         (UINT)(n->SendSeq + n->SendSeqInit),\r
1082                                         (UINT)(n->RecvSeq + n->RecvSeqInit),\r
1083                                         TCP_ACK | TCP_FIN, 0,\r
1084                                         0, NULL, 0);\r
1085                                 n->FinSentTime = v->Now;\r
1086                                 n->FinSentCount++;\r
1087                                 if (n->FinSentCount >= NAT_FIN_SEND_MAX_COUNT)\r
1088                                 {\r
1089                                         n->TcpFinished = false;\r
1090                                 }\r
1091                         }\r
1092                 }\r
1093                 break;\r
1094 \r
1095         case NAT_TCP_ESTABLISHED:               // 接続確立済み\r
1096                 {\r
1097                         UINT send_data_size;\r
1098                         UINT current_pointer;\r
1099                         UINT notice_window_size_value = 0;\r
1100                         UINT buf_free_bytes = 0;\r
1101                         // 通知するウインドウサイズの値を決定する\r
1102                         if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)\r
1103                         {\r
1104                                 buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);\r
1105                         }\r
1106                         notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);\r
1107                         if (n->LastSentKeepAliveTime == 0 ||\r
1108                                 (n->LastSentKeepAliveTime + (UINT64)NAT_ACK_KEEPALIVE_SPAN) < v->Now ||\r
1109                                 (n->LastSentKeepAliveTime > v->Now))\r
1110                         {\r
1111                                 if (n->LastSentKeepAliveTime != 0)\r
1112                                 {\r
1113                                         // Keep-Alive 用 ACK パケットの送信\r
1114                                         SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1115                                                         (UINT)(n->SendSeqInit + n->SendSeq),\r
1116                                                         (UINT)(n->RecvSeqInit + n->RecvSeq) - 1,\r
1117                                                         TCP_ACK,\r
1118                                                         notice_window_size_value,\r
1119                                                         0,\r
1120                                                         NULL,\r
1121                                                         0);\r
1122                                 }\r
1123                                 n->LastSentKeepAliveTime = v->Now;\r
1124                         }\r
1125                         if (n->TcpLastSentTime == 0 ||\r
1126                                 (n->TcpLastSentTime > v->Now) ||\r
1127                                 ((n->TcpLastSentTime + (UINT64)n->TcpSendTimeoutSpan) < v->Now) ||\r
1128                                 n->SendAckNext)\r
1129                         {\r
1130                                 // 送信すべきデータがある場合は送信する\r
1131                                 // 送信すべきセグメントサイズを計算する\r
1132                                 send_data_size = n->TcpSendWindowSize;\r
1133                                 if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))\r
1134                                 {\r
1135                                         // cwnd 値を適用する\r
1136                                         send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;\r
1137                                 }\r
1138                                 if (send_data_size > n->SendFifo->size)\r
1139                                 {\r
1140                                         // 現在保有しているデータ以上は送れない\r
1141                                         send_data_size = n->SendFifo->size;\r
1142                                 }\r
1143                                 if (send_data_size >= 1)\r
1144                                 {\r
1145                                         // セグメントに分割して送信する\r
1146                                         current_pointer = 0;\r
1147                                         while (send_data_size > 0)\r
1148                                         {\r
1149                                                 UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);\r
1150                                                 void *send_segment = (void *)(((UCHAR *)n->SendFifo->p) + n->SendFifo->pos + current_pointer);\r
1151                                                 SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1152                                                         (UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer),\r
1153                                                         (UINT)(n->RecvSeqInit + n->RecvSeq),\r
1154                                                         TCP_ACK | TCP_PSH,\r
1155                                                         notice_window_size_value,\r
1156                                                         0,\r
1157                                                         send_segment,\r
1158                                                         send_segment_size);\r
1159                                                 current_pointer += send_segment_size;\r
1160                                                 send_data_size -= send_segment_size;\r
1161                                         }\r
1162                                         // 送信時刻を記録する\r
1163                                         n->TcpLastSentTime = v->Now;\r
1164                                         // 今回送信するストリームサイズを記録する\r
1165                                         n->SendMissionSize = current_pointer;\r
1166                                         n->CurrentSendingMission = true;\r
1167                                         // RTT 測定\r
1168                                         if (n->CalcRTTStartTime == 0)\r
1169                                         {\r
1170                                                 n->CalcRTTStartTime = v->Now;\r
1171                                                 n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;\r
1172                                         }\r
1173                                         if (n->RetransmissionUsedFlag == false)\r
1174                                         {\r
1175                                                 n->RetransmissionUsedFlag = true;\r
1176                                         }\r
1177                                         else\r
1178                                         {\r
1179                                                 // 輻輳発生を検出\r
1180                                                 if (n->TcpSendCWnd > 2)\r
1181                                                 {\r
1182                                                         n->TcpSendCWnd--;\r
1183                                                 }\r
1184                                         }\r
1185                                 }\r
1186                                 else if (n->SendAckNext)\r
1187                                 {\r
1188                                         // ACK のみ送信する\r
1189                                         SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1190                                                         (UINT)(n->SendSeqInit + n->SendSeq),\r
1191                                                         (UINT)(n->RecvSeqInit + n->RecvSeq),\r
1192                                                         TCP_ACK,\r
1193                                                         notice_window_size_value,\r
1194                                                         0,\r
1195                                                         NULL,\r
1196                                                         0);\r
1197                                 }\r
1198                                 n->SendAckNext = false;\r
1199                         }\r
1200                         if (n->TcpFinished)\r
1201                         {\r
1202                                 // すべてのデータ送信が完了していたら切断する\r
1203                                 if (n->SendFifo->size == 0)\r
1204                                 {\r
1205                                         n->TcpStatus = NAT_TCP_SEND_RESET;\r
1206                                 }\r
1207                         }\r
1208                 }\r
1209                 break;\r
1210         }\r
1211 }\r
1212 \r
1213 // インターネットへ向かう TCP パケットの受信処理\r
1214 void TcpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, TCP_HEADER *tcp, void *data, UINT size)\r
1215 {\r
1216         NAT_ENTRY *n, t;\r
1217         UINT seq, ack;\r
1218         UINT64 seq64 = 0, ack64 = 0;\r
1219         // 引数チェック\r
1220         if (v == NULL || tcp == NULL || data == NULL)\r
1221         {\r
1222                 return;\r
1223         }\r
1224 \r
1225         // このパケットに関するセッションを NAT テーブルから検索\r
1226         SetNat(&t, NAT_TCP, src_ip, src_port, dest_ip, dest_port, 0, 0);\r
1227         n = SearchNat(v, &t);\r
1228 \r
1229         if (n == NULL)\r
1230         {\r
1231                 // 既存のセッションが存在しない\r
1232                 // SYN パケットのみ通過を許可する\r
1233                 if ((tcp->Flag & TCP_SYN) && ((tcp->Flag & TCP_ACK) == false))\r
1234                 {\r
1235                         TCP_OPTION o;\r
1236                         // 新しいセッションを作成する\r
1237                         n = CreateNatTcp(v, src_ip, src_port, dest_ip, dest_port);\r
1238                         if (n == NULL)\r
1239                         {\r
1240                                 return;\r
1241                         }\r
1242 \r
1243                         // オプションの取得\r
1244                         ParseTcpOption(&o, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);\r
1245                         if (o.MaxSegmentSize == 0)\r
1246                         {\r
1247                                 o.MaxSegmentSize = v->TcpMss;\r
1248                         }\r
1249 \r
1250                         Debug("TCP SYN: MSS=%u, WS=%u\n", o.MaxSegmentSize, o.WindowScaling);\r
1251 \r
1252                         // 初期シーケンス番号\r
1253                         n->RecvSeqInit = (UINT64)Endian32(tcp->SeqNumber);\r
1254                         n->RecvSeq = 1;\r
1255 \r
1256                         n->TcpSendMaxSegmentSize = o.MaxSegmentSize;\r
1257                         n->TcpRecvWindowSize = NAT_TCP_RECV_WINDOW_SIZE;\r
1258                         n->TcpSendWindowSize = (UINT)Endian16(tcp->WindowSize);\r
1259                         if (o.WindowScaling != 0)\r
1260                         {\r
1261                                 if (o.WindowScaling > 14)\r
1262                                 {\r
1263                                         o.WindowScaling = 14;\r
1264                                 }\r
1265                                 n->TcpSendWindowSize = (n->TcpSendWindowSize << o.WindowScaling);\r
1266                         }\r
1267                 }\r
1268         }\r
1269 \r
1270         seq = Endian32(tcp->SeqNumber);\r
1271         ack = Endian32(tcp->AckNumber);\r
1272 \r
1273         if (n == NULL)\r
1274         {\r
1275                 // NAT エントリに登録されていないパケットが届いたので RST を返す\r
1276                 SendTcp(v, dest_ip, dest_port, src_ip, src_port,\r
1277                         ack, ack, TCP_RST, 0, 0, NULL, 0);\r
1278                 return;\r
1279         }\r
1280 \r
1281         switch (n->TcpStatus)\r
1282         {\r
1283         case NAT_TCP_SEND_RESET:                // リセットを送信してコネクションを切断\r
1284                 break;\r
1285 \r
1286         case NAT_TCP_CONNECTED:                 // ソケット接続完了 SYN+ACK, ACK 処理\r
1287                 if ((tcp->Flag & TCP_ACK) && ((tcp->Flag & TCP_SYN) == false))\r
1288                 {\r
1289                         if (seq == (UINT)(n->RecvSeqInit + n->RecvSeq) &&\r
1290                                 ack == (UINT)(n->SendSeqInit + n->SendSeq + 1))\r
1291                         {\r
1292                                 // ACK パケットが戻ってきたのでハンドシェイク完了\r
1293                                 n->SendSeq++;           // SYN パケットは seq を 1 消費する\r
1294                                 Debug("TCP Connection Established.\n");\r
1295                                 n->TcpStatus = NAT_TCP_ESTABLISHED;\r
1296                                 // 輻輳ウインドウサイズを初期化\r
1297                                 n->TcpSendCWnd = 1;\r
1298                                 n->LastCommTime = v->Now;\r
1299                         }\r
1300                         else\r
1301                         {\r
1302                                 goto TCP_RESET;\r
1303                         }\r
1304                 }\r
1305                 else if (tcp->Flag & TCP_RST)\r
1306                 {\r
1307 TCP_RESET:\r
1308                         // RST を受信\r
1309                         Debug("TCP Connection Reseted.\n");\r
1310                         n->TcpStatus = NAT_TCP_SEND_RESET;\r
1311                 }\r
1312                 break;\r
1313 \r
1314         case NAT_TCP_ESTABLISHED:               // 接続確立済み\r
1315                 if (tcp->Flag & TCP_FIN)\r
1316                 {\r
1317                         // 接続を完了させる\r
1318                         n->TcpFinished = true;\r
1319                 }\r
1320                 if (tcp->Flag & TCP_RST)\r
1321                 {\r
1322                         // RST を受信\r
1323                         goto TCP_RESET;\r
1324                 }\r
1325                 else if (tcp->Flag & TCP_ACK)\r
1326                 {\r
1327                         TCP_OPTION opt;\r
1328                         n->LastCommTime = v->Now;\r
1329                         // ウインドウサイズなどのオプションの取得\r
1330                         n->TcpSendWindowSize = Endian16(tcp->WindowSize);\r
1331                         ParseTcpOption(&opt, ((UCHAR *)tcp) + TCP_HEADER_SIZE, TCP_GET_HEADER_SIZE(tcp) * 4 - TCP_HEADER_SIZE);\r
1332                         if (opt.WindowScaling != 0)\r
1333                         {\r
1334                                 if (opt.WindowScaling > 14)\r
1335                                 {\r
1336                                         opt.WindowScaling = 14;\r
1337                                 }\r
1338                                 n->TcpSendWindowSize = (n->TcpSendWindowSize << opt.WindowScaling);\r
1339                         }\r
1340                         // まず受信した ACK の処理を行う\r
1341                         // ack64 に応答確認を受けたストリームの終端位置を格納する\r
1342                         ack64 = n->SendSeq + (UINT64)ack - (n->SendSeqInit + n->SendSeq) % X32;\r
1343                         if ((n->SendSeqInit + n->SendSeq) % X32 > ack)\r
1344                         {\r
1345                                 if (((n->SendSeqInit + n->SendSeq) % X32 - ack) >= 0x80000000)\r
1346                                 {\r
1347                                         ack64 = n->SendSeq + (UINT64)ack + X32 - (n->SendSeqInit + n->SendSeq) % X32;\r
1348                                 }\r
1349                         }\r
1350                         if (ack64 > n->SendSeq)\r
1351                         {\r
1352                                 // クライアントによって 1 バイト以上の受信が完了したらしい\r
1353                                 UINT slide_offset = (UINT)(ack64 - n->SendSeq); // ウインドウのスライド量\r
1354                                 if (slide_offset == 0 || slide_offset > n->TcpSendWindowSize || slide_offset > n->SendFifo->size)\r
1355                                 {\r
1356                                         // 確認応答のオフセット値がこれまでに送信したはずのサイズ\r
1357                                         // よりも大きいので無視する\r
1358                                 }\r
1359                                 else\r
1360                                 {\r
1361                                         // RTT 測定\r
1362                                         if (n->CalcRTTStartTime != 0)\r
1363                                         {\r
1364                                                 if (n->CalcRTTStartValue < ack64)\r
1365                                                 {\r
1366                                                         UINT time_span;\r
1367                                                         if (v->Now > n->CalcRTTStartTime)\r
1368                                                         {\r
1369                                                                 time_span = (UINT)(v->Now - n->CalcRTTStartTime);\r
1370                                                         }\r
1371                                                         else\r
1372                                                         {\r
1373                                                                 time_span = 100;\r
1374                                                         }\r
1375                                                         n->CalcRTTStartTime = 0;\r
1376 \r
1377                                                         // 平滑化\r
1378                                                         n->CurrentRTT =\r
1379                                                                 (UINT)\r
1380                                                                 (\r
1381                                                                         ((UINT64)n->CurrentRTT * (UINT64)9 +\r
1382                                                                         (UINT64)time_span * (UINT64)1) / (UINT64)10\r
1383                                                                 );\r
1384                                                         n->TcpSendTimeoutSpan = n->CurrentRTT * 2;\r
1385                                                 }\r
1386                                         }\r
1387                                         // 送信サイズを減少させる\r
1388                                         n->SendMissionSize -= slide_offset;\r
1389                                         if (n->SendMissionSize == 0)\r
1390                                         {\r
1391                                                 // 今回送信する予定であったすべてのセグメントの送受信が完了した\r
1392                                                 // より送信セグメントサイズを大きくしてみる\r
1393                                                 if (n->TcpSendCWnd < 65536)\r
1394                                                 {\r
1395                                                         n->TcpSendCWnd++;\r
1396                                                 }\r
1397                                                 n->CurrentSendingMission = false;\r
1398                                                 n->TcpLastSentTime = 0;\r
1399                                                 n->RetransmissionUsedFlag = false;\r
1400                                         }\r
1401                                         // バッファをスライディングする\r
1402                                         n->SendSeq += slide_offset;\r
1403                                         ReadFifo(n->SendFifo, NULL, slide_offset);\r
1404                                         // 今回 ACK によって送信完了が確認できたサイズだけ、さらに送信を実行する\r
1405                                         if (n->SendMissionSize != 0 && false)\r
1406                                         {\r
1407                                                 UINT notice_window_size_value = 0;\r
1408                                                 UINT send_data_size;\r
1409                                                 UINT buf_free_bytes;\r
1410                                                 UINT send_offset = n->SendMissionSize;\r
1411                                                 // 通知するウインドウサイズの値を決定する\r
1412                                                 if (FifoSize(n->RecvFifo) < NAT_RECV_BUF_SIZE)\r
1413                                                 {\r
1414                                                         buf_free_bytes = NAT_RECV_BUF_SIZE - FifoSize(n->RecvFifo);\r
1415                                                 }\r
1416                                                 notice_window_size_value = MIN(n->TcpRecvWindowSize, buf_free_bytes);\r
1417                                                 // 送信すべきセグメントサイズを計算する\r
1418                                                 send_data_size = n->TcpSendWindowSize;\r
1419                                                 if (send_data_size > (n->TcpSendCWnd * n->TcpSendMaxSegmentSize))\r
1420                                                 {\r
1421                                                         // cwnd 値を適用する\r
1422                                                         send_data_size = n->TcpSendCWnd * n->TcpSendMaxSegmentSize;\r
1423                                                 }\r
1424                                                 if (n->SendFifo->size > send_offset)\r
1425                                                 {\r
1426                                                         send_data_size = MIN(send_data_size, n->SendFifo->size - send_offset);\r
1427                                                         send_data_size = MIN(send_data_size, slide_offset);\r
1428                                                 }\r
1429                                                 else\r
1430                                                 {\r
1431                                                         send_data_size = 0;\r
1432                                                 }\r
1433                                                 if (send_data_size >= 1)\r
1434                                                 {\r
1435                                                         // セグメントに分割して送信する\r
1436                                                         UINT current_pointer = 0;\r
1437                                                         while (send_data_size > 0)\r
1438                                                         {\r
1439                                                                 UINT send_segment_size = MIN(n->TcpSendMaxSegmentSize, send_data_size);\r
1440                                                                 void *send_segment = (void *)((\r
1441                                                                         (UCHAR *)n->SendFifo->p) + n->SendFifo->pos +\r
1442                                                                         current_pointer + send_offset);\r
1443 \r
1444                                                                 SendTcp(v, n->DestIp, n->DestPort, n->SrcIp, n->SrcPort,\r
1445                                                                         (UINT)(n->SendSeqInit + n->SendSeq + (UINT64)current_pointer\r
1446                                                                         + (UINT)send_offset),\r
1447                                                                         (UINT)(n->RecvSeqInit + n->RecvSeq),\r
1448                                                                         TCP_ACK | TCP_PSH,\r
1449                                                                         notice_window_size_value,\r
1450                                                                         0,\r
1451                                                                         send_segment,\r
1452                                                                         send_segment_size);\r
1453                                                                 current_pointer += send_segment_size;\r
1454                                                                 send_data_size -= send_segment_size;\r
1455                                                         }\r
1456                                                         n->SendMissionSize += current_pointer;\r
1457                                                         n->CurrentSendingMission = true;\r
1458                                                         n->TcpLastSentTime = v->Now;\r
1459                                                         // RTT 測定\r
1460                                                         if (n->CalcRTTStartTime == 0)\r
1461                                                         {\r
1462                                                                 n->CalcRTTStartTime = v->Now;\r
1463                                                                 n->CalcRTTStartValue = n->SendSeq + current_pointer - 1;\r
1464                                                         }\r
1465                                                 }\r
1466                                         }\r
1467                                         // イベント発生\r
1468                                         SetSockEvent(v->SockEvent);\r
1469                                 }\r
1470                         }\r
1471                         // 次にデータの受信処理を行う\r
1472                         seq64 = n->RecvSeq + (UINT64)seq - (n->RecvSeqInit + n->RecvSeq) % X32;\r
1473                         if ((n->RecvSeqInit + n->RecvSeq) % X32 > seq)\r
1474                         {\r
1475                                 if (((n->RecvSeqInit + n->RecvSeq) % X32 - ack) >= 0x80000000)\r
1476                                 {\r
1477                                         seq64 = n->RecvSeq + (UINT64)seq + X32 - (n->RecvSeqInit + n->RecvSeq) % X32;\r
1478                                 }\r
1479                         }\r
1480                         // この時点で seq64 にはクライアントからのデータ開始点の位置が入っている\r
1481                         if (seq64 >= n->RecvSeq && (seq64 + size) <= (n->RecvSeq + n->TcpRecvWindowSize))\r
1482                         {\r
1483                                 if (size >= 1)\r
1484                                 {\r
1485                                         // 受信ウインドウの範囲内に 1 バイト以上のデータが届いた\r
1486                                         UINT offset = (UINT)(seq64 - n->RecvSeq);\r
1487                                         UINT i;\r
1488                                         IP_PART *me;\r
1489                                         if (n->TcpRecvWindow == NULL)\r
1490                                         {\r
1491                                                 n->TcpRecvWindow = NewFifo();\r
1492                                         }\r
1493                                         if (n->TcpRecvList == NULL)\r
1494                                         {\r
1495                                                 n->TcpRecvList = NewListFast(NULL);\r
1496                                         }\r
1497                                         // 届いたパケットをバッファに上書きしリストに追加する\r
1498                                         if (FifoSize(n->TcpRecvWindow) < (offset + size))\r
1499                                         {\r
1500                                                 // バッファサイズ拡張\r
1501                                                 WriteFifo(n->TcpRecvWindow, NULL, offset + size - FifoSize(n->TcpRecvWindow));\r
1502                                         }\r
1503                                         Copy(((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos +\r
1504                                                 offset, data, size);\r
1505                                         me = ZeroMalloc(sizeof(IP_PART));\r
1506                                         me->Offset = offset;\r
1507                                         me->Size = size;\r
1508                                         for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)\r
1509                                         {\r
1510                                                 IP_PART *p = LIST_DATA(n->TcpRecvList, i);\r
1511                                                 // 重なる領域があれば重なっている部分があれば除去する\r
1512                                                 if (p->Size != 0)\r
1513                                                 {\r
1514                                                         if (me->Offset <= p->Offset && (me->Offset + me->Size) >= (p->Offset + p->Size))\r
1515                                                         {\r
1516                                                                 // このパケットが既存パケットを完全に上書きする\r
1517                                                                 p->Size = 0;\r
1518                                                         }\r
1519                                                         else if (me->Offset >= p->Offset && (me->Offset + me->Size) <= (p->Offset + p->Size))\r
1520                                                         {\r
1521                                                                 // 既存パケットがこのパケットを完全に上書きする\r
1522                                                                 me->Size = 0;\r
1523                                                         }\r
1524                                                         else if (me->Offset > p->Offset && me->Offset < (p->Offset + p->Size) &&\r
1525                                                                 (me->Offset + me->Size) > (p->Offset + p->Size))\r
1526                                                         {\r
1527                                                                 // 一部重なっている\r
1528                                                                 p->Size -= p->Offset + p->Size - me->Offset;\r
1529                                                         }\r
1530                                                         else if (me->Offset < p->Offset && (me->Offset + size) > p->Offset && (me->Offset + size) < (p->Offset + p->Size))\r
1531                                                         {\r
1532                                                                 // 一部重なっている\r
1533                                                                 me->Size -= me->Offset + me->Size - p->Offset;\r
1534                                                         }\r
1535                                                 }\r
1536                                         }\r
1537                                         if (me->Size == 0)\r
1538                                         {\r
1539                                                 Free(me);\r
1540                                         }\r
1541                                         else\r
1542                                         {\r
1543                                                 Add(n->TcpRecvList, me);\r
1544                                         }\r
1545 KILL_NULL_FIRST:\r
1546                                         // 受信リストから中身が空白のものをすべて削除する\r
1547                                         for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)\r
1548                                         {\r
1549                                                 IP_PART *p = LIST_DATA(n->TcpRecvList, i);\r
1550                                                 if (p->Size == 0)\r
1551                                                 {\r
1552                                                         Delete(n->TcpRecvList, p);\r
1553                                                         Free(p);\r
1554                                                         goto KILL_NULL_FIRST;\r
1555                                                 }\r
1556                                         }\r
1557 SCAN_FIRST:\r
1558                                         // 受信リストのうちオフセット 0 から始まるものがあれば抽出する\r
1559                                         for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)\r
1560                                         {\r
1561                                                 IP_PART *p = LIST_DATA(n->TcpRecvList, i);\r
1562                                                 UINT sz;\r
1563                                                 if (p->Offset == 0)\r
1564                                                 {\r
1565                                                         // 0 から始まるデータブロックを発見したので\r
1566                                                         // この分だけ左側にスライドさせてデータを抜き出す\r
1567                                                         // バッファを FIFO に書き出す\r
1568                                                         sz = p->Size;\r
1569                                                         WriteFifo(n->RecvFifo, ((UCHAR *)n->TcpRecvWindow->p) + n->TcpRecvWindow->pos, sz);\r
1570                                                         // リストから解放する\r
1571                                                         Delete(n->TcpRecvList, p);\r
1572                                                         Free(p);\r
1573                                                         ReadFifo(n->TcpRecvWindow, NULL, sz);\r
1574                                                         // すべての項目を左側にスライドする\r
1575                                                         for (i = 0;i < LIST_NUM(n->TcpRecvList);i++)\r
1576                                                         {\r
1577                                                                 p = LIST_DATA(n->TcpRecvList, i);\r
1578                                                                 p->Offset -= sz;\r
1579                                                         }\r
1580                                                         // TCB のパラメータを更新\r
1581                                                         n->RecvSeq += (UINT64)sz;\r
1582                                                         SetSockEvent(v->SockEvent);\r
1583                                                         n->SendAckNext = true;\r
1584                                                         // 最初からスキャンし直す\r
1585                                                         goto SCAN_FIRST;\r
1586                                                 }\r
1587                                         }\r
1588                                 }\r
1589                         }\r
1590                 }\r
1591                 break;\r
1592         }\r
1593 \r
1594         SetSockEvent(v->SockEvent);\r
1595 }\r
1596 \r
1597 // TCP オプションのパース\r
1598 void ParseTcpOption(TCP_OPTION *o, void *data, UINT size)\r
1599 {\r
1600         UCHAR *buf = (UCHAR *)data;\r
1601         UINT i;\r
1602         UINT value_size = 0;\r
1603         UINT value_id = 0;\r
1604         UCHAR value[128];\r
1605         // 引数チェック\r
1606         if (o == NULL || data == NULL)\r
1607         {\r
1608                 return;\r
1609         }\r
1610 \r
1611         Zero(o, sizeof(TCP_OPTION));\r
1612 \r
1613         for (i = 0;i < size;i++)\r
1614         {\r
1615                 if (buf[i] == 0)\r
1616                 {\r
1617                         return;\r
1618                 }\r
1619                 if (buf[i] != 1)\r
1620                 {\r
1621                         value_id = buf[i];\r
1622                         i++;\r
1623                         if (i >= size)\r
1624                         {\r
1625                                 return;\r
1626                         }\r
1627                         value_size = buf[i];\r
1628                         if (value_size <= 1 || value_size > sizeof(value))\r
1629                         {\r
1630                                 return;\r
1631                         }\r
1632                         i++;\r
1633                         if (i >= size)\r
1634                         {\r
1635                                 return;\r
1636                         }\r
1637                         value_size -= 2;\r
1638                         Copy(value, &buf[i], value_size);\r
1639                         i += value_size;\r
1640                         if (i >= size)\r
1641                         {\r
1642                                 return;\r
1643                         }\r
1644                         switch (value_id)\r
1645                         {\r
1646                         case 2: // MSS\r
1647                                 if (value_size == 2)\r
1648                                 {\r
1649                                         USHORT *mss = (USHORT *)value;\r
1650                                         o->MaxSegmentSize = Endian16(*mss);\r
1651                                 }\r
1652                                 break;\r
1653 \r
1654                         case 3: // WSS\r
1655                                 if (value_size == 1)\r
1656                                 {\r
1657                                         UCHAR *wss = (UCHAR *)value;\r
1658                                         o->WindowScaling = Endian16(*wss);\r
1659                                 }\r
1660                                 break;\r
1661 \r
1662                         }\r
1663                 }\r
1664         }\r
1665 \r
1666 }\r
1667 \r
1668 // 新しい NAT TCP セッションの作成\r
1669 NAT_ENTRY *CreateNatTcp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port)\r
1670 {\r
1671         NAT_ENTRY *n;\r
1672         // 引数チェック\r
1673         if (v == NULL)\r
1674         {\r
1675                 return NULL;\r
1676         }\r
1677 \r
1678         if (CanCreateNewNatEntry(v) == false)\r
1679         {\r
1680                 return NULL;\r
1681         }\r
1682 \r
1683         // NAT エントリの作成\r
1684         n = ZeroMalloc(sizeof(NAT_ENTRY));\r
1685         n->Id = Inc(v->Counter);\r
1686         n->v = v;\r
1687         n->lock = NewLock();\r
1688         n->Protocol = NAT_TCP;\r
1689         n->SrcIp = src_ip;\r
1690         n->SrcPort = src_port;\r
1691         n->DestIp = dest_ip;\r
1692         n->DestPort = dest_port;\r
1693         n->CreatedTime = n->LastCommTime = v->Now;\r
1694         n->Sock = NULL;\r
1695         n->DisconnectNow = false;\r
1696         n->TcpSendMaxSegmentSize = n->TcpRecvMaxSegmentSize = v->TcpMss;\r
1697 \r
1698         n->SendFifo = NewFifo();\r
1699         n->RecvFifo = NewFifo();\r
1700 \r
1701         n->TcpStatus = NAT_TCP_CONNECTING;\r
1702 \r
1703         n->SendSeqInit = Rand32();\r
1704         n->CurrentRTT = NAT_INITIAL_RTT_VALUE;\r
1705         n->TcpSendTimeoutSpan = n->CurrentRTT * 2;\r
1706 \r
1707         // NAT テーブルに追加\r
1708         Add(v->NatTable, n);\r
1709 \r
1710 \r
1711 #if     1\r
1712         {\r
1713                 IP ip1, ip2;\r
1714                 char s1[MAX_SIZE], s2[MAX_SIZE];\r
1715                 UINTToIP(&ip1, src_ip);\r
1716                 UINTToIP(&ip2, dest_ip);\r
1717                 IPToStr(s1, 0, &ip1);\r
1718                 IPToStr(s2, 0, &ip2);\r
1719                 Debug("NAT_ENTRY: CreateNatTcp %s %u -> %s %u\n", s1, src_port, s2, dest_port);\r
1720 \r
1721                 NLog(v, "LH_NAT_TCP_CREATED", n->Id, s1, src_port, s2, dest_port);\r
1722         }\r
1723 #endif\r
1724 \r
1725         return n;\r
1726 }\r
1727 \r
1728 // TCP パケットを仮想ネットワークから受信した\r
1729 void VirtualTcpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size)\r
1730 {\r
1731         TCP_HEADER *tcp;\r
1732         UINT src_port, dest_port;\r
1733         UINT header_size, buf_size;\r
1734         void *buf;\r
1735         IP ip1, ip2;\r
1736         // 引数チェック\r
1737         if (v == NULL || data == NULL)\r
1738         {\r
1739                 return;\r
1740         }\r
1741 \r
1742         // ヘッダを取得\r
1743         if (size < TCP_HEADER_SIZE)\r
1744         {\r
1745                 // サイズが小さすぎる\r
1746                 return;\r
1747         }\r
1748         tcp = (TCP_HEADER *)data;\r
1749         src_port = Endian16(tcp->SrcPort);\r
1750         dest_port = Endian16(tcp->DstPort);\r
1751         if (src_port == 0 || dest_port == 0)\r
1752         {\r
1753                 // ポート番号が不正\r
1754                 return;\r
1755         }\r
1756         if (src_ip == dest_ip || src_ip == 0 || src_ip == 0xffffffff || dest_ip == 0 || dest_ip == 0xffffffff)\r
1757         {\r
1758                 // IP アドレスが不正\r
1759                 return;\r
1760         }\r
1761         UINTToIP(&ip1, src_ip);\r
1762         UINTToIP(&ip2, dest_ip);\r
1763         if (ip1.addr[0] == 127 || ip2.addr[0] == 127)\r
1764         {\r
1765                 // ループバック IP アドレスは指定できない\r
1766                 return;\r
1767         }\r
1768         if (IsInNetwork(dest_ip, v->HostIP, v->HostMask))\r
1769         {\r
1770                 // 仮想 LAN 側のネットワーク向けのパケットは無視する\r
1771                 return;\r
1772         }\r
1773         // ヘッダサイズを取得\r
1774         header_size = TCP_GET_HEADER_SIZE(tcp) * 4;\r
1775         if (size < header_size)\r
1776         {\r
1777                 // ヘッダサイズが不正\r
1778                 return;\r
1779         }\r
1780         // バッファのサイズとアドレスを取得\r
1781         buf_size = size - header_size;\r
1782         buf = (void *)(((UCHAR *)data) + header_size);\r
1783 \r
1784         TcpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, tcp, buf, buf_size);\r
1785 }\r
1786 \r
1787 // NAT UDP ポーリング\r
1788 void PoolingNatUdp(VH *v, NAT_ENTRY *n)\r
1789 {\r
1790         // 引数チェック\r
1791         if (v == NULL || n == NULL)\r
1792         {\r
1793                 return;\r
1794         }\r
1795 \r
1796         // 受信キューにパケットが 1 つ以上あれば処理する\r
1797         if (n->UdpRecvQueue->num_item != 0)\r
1798         {\r
1799                 BLOCK *block;\r
1800 \r
1801                 // すべての UDP パケットを仮想ネットワークに送信する\r
1802                 while (block = GetNext(n->UdpRecvQueue))\r
1803                 {\r
1804                         SendUdp(v, n->SrcIp, n->SrcPort, n->DestIp, n->DestPort,\r
1805                                 block->Buf, block->Size);\r
1806 \r
1807                         FreeBlock(block);\r
1808                 }\r
1809         }\r
1810 }\r
1811 \r
1812 // NAT ポーリング\r
1813 void PoolingNat(VH *v)\r
1814 {\r
1815         UINT i;\r
1816         // 引数チェック\r
1817         if (v == NULL)\r
1818         {\r
1819                 return;\r
1820         }\r
1821 \r
1822         // すべての NAT エントリを走査し処理を行う\r
1823         for (i = 0;i < LIST_NUM(v->NatTable);i++)\r
1824         {\r
1825                 NAT_ENTRY *n = LIST_DATA(v->NatTable, i);\r
1826 \r
1827                 switch (n->Protocol)\r
1828                 {\r
1829                 case NAT_TCP:\r
1830                         PollingNatTcp(v, n);\r
1831                         break;\r
1832 \r
1833                 case NAT_UDP:\r
1834                         PoolingNatUdp(v, n);\r
1835                         break;\r
1836 \r
1837                 case NAT_DNS:\r
1838                         PollingNatDns(v, n);\r
1839                         break;\r
1840                 }\r
1841         }\r
1842 }\r
1843 \r
1844 // NAT テーブルの比較関数\r
1845 int CompareNat(void *p1, void *p2)\r
1846 {\r
1847         NAT_ENTRY *n1, *n2;\r
1848         if (p1 == NULL || p2 == NULL)\r
1849         {\r
1850                 return 0;\r
1851         }\r
1852         n1 = *(NAT_ENTRY **)p1;\r
1853         n2 = *(NAT_ENTRY **)p2;\r
1854         if (n1 == n2)\r
1855         {\r
1856                 return 0;\r
1857         }\r
1858 \r
1859         if (n1->SrcIp > n2->SrcIp) return 1;\r
1860         else if (n1->SrcIp < n2->SrcIp) return -1;\r
1861         else if (n1->DestIp > n2->DestIp) return 1;\r
1862         else if (n1->DestIp < n2->DestIp) return -1;\r
1863         else if (n1->SrcPort > n2->SrcPort) return 1;\r
1864         else if (n1->SrcPort < n2->SrcPort) return -1;\r
1865         else if (n1->DestPort > n2->DestPort) return 1;\r
1866         else if (n1->DestPort < n2->DestPort) return -1;\r
1867         else if (n1->Protocol > n2->Protocol) return 1;\r
1868         else if (n1->Protocol < n2->Protocol) return -1;\r
1869         else return 0;\r
1870 }\r
1871 \r
1872 // NAT 構造体の設定\r
1873 void SetNat(NAT_ENTRY *n, UINT protocol, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT public_ip, UINT public_port)\r
1874 {\r
1875         // 引数チェック\r
1876         if (n == NULL)\r
1877         {\r
1878                 return;\r
1879         }\r
1880 \r
1881         n->Protocol = protocol;\r
1882         n->SrcIp = src_ip;\r
1883         n->SrcPort = src_port;\r
1884         n->DestIp = dest_ip;\r
1885         n->DestPort = dest_port;\r
1886         n->PublicIp = public_ip;\r
1887         n->PublicPort = public_port;\r
1888 }\r
1889 \r
1890 // NAT の初期化\r
1891 void InitNat(VH *v)\r
1892 {\r
1893         // 引数チェック\r
1894         if (v == NULL)\r
1895         {\r
1896                 return;\r
1897         }\r
1898 \r
1899         // NAT テーブルの作成\r
1900         v->NatTable = NewList(CompareNat);\r
1901 \r
1902         // ソケットイベントの作成\r
1903         v->SockEvent = NewSockEvent();\r
1904 \r
1905         // NAT 用スレッドの作成\r
1906         v->HaltNat = false;\r
1907         v->NatThread = NewThread(NatThread, (void *)v);\r
1908         WaitThreadInit(v->NatThread);\r
1909 }\r
1910 \r
1911 // NAT の解放\r
1912 void FreeNat(VH *v)\r
1913 {\r
1914         // 引数チェック\r
1915         if (v == NULL)\r
1916         {\r
1917                 return;\r
1918         }\r
1919 \r
1920         // NAT 用スレッドの停止\r
1921         v->HaltNat = true;\r
1922         SetSockEvent(v->SockEvent);\r
1923         WaitThread(v->NatThread, INFINITE);\r
1924         ReleaseThread(v->NatThread);\r
1925         v->NatThread = NULL;\r
1926         ReleaseSockEvent(v->SockEvent);\r
1927         v->SockEvent = NULL;\r
1928 \r
1929         // NAT テーブルの解放\r
1930         ReleaseList(v->NatTable);\r
1931 }\r
1932 \r
1933 // NAT テーブルの検索\r
1934 NAT_ENTRY *SearchNat(VH *v, NAT_ENTRY *target)\r
1935 {\r
1936         NAT_ENTRY *n;\r
1937         // 引数チェック\r
1938         if (v == NULL || target == NULL)\r
1939         {\r
1940                 return NULL;\r
1941         }\r
1942 \r
1943         // バイナリサーチ\r
1944         n = (NAT_ENTRY *)Search(v->NatTable, target);\r
1945 \r
1946         return n;\r
1947 }\r
1948 \r
1949 // UDP NAT エントリの削除\r
1950 void DeleteNatUdp(VH *v, NAT_ENTRY *n)\r
1951 {\r
1952         BLOCK *block;\r
1953         // 引数チェック\r
1954         if (v == NULL || n == NULL)\r
1955         {\r
1956                 return;\r
1957         }\r
1958 \r
1959         NLog(v, "LH_NAT_UDP_DELETED", n->Id);\r
1960 \r
1961         // すべてのキューを解放\r
1962         while (block = GetNext(n->UdpRecvQueue))\r
1963         {\r
1964                 FreeBlock(block);\r
1965         }\r
1966         ReleaseQueue(n->UdpRecvQueue);\r
1967         while (block = GetNext(n->UdpSendQueue))\r
1968         {\r
1969                 FreeBlock(block);\r
1970         }\r
1971         ReleaseQueue(n->UdpSendQueue);\r
1972 \r
1973         // ソケットを解放\r
1974         if (n->Sock != NULL)\r
1975         {\r
1976                 Disconnect(n->Sock);\r
1977                 ReleaseSock(n->Sock);\r
1978                 n->Sock = NULL;\r
1979         }\r
1980 \r
1981         DeleteLock(n->lock);\r
1982 \r
1983         // テーブルから削除\r
1984         Delete(v->NatTable, n);\r
1985 \r
1986         // メモリ解放\r
1987         Free(n);\r
1988 \r
1989         Debug("NAT: DeleteNatUdp\n");\r
1990 \r
1991 }\r
1992 \r
1993 // NAT UDP エントリを作成\r
1994 NAT_ENTRY *CreateNatUdp(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, UINT dns_proxy_ip)\r
1995 {\r
1996         NAT_ENTRY *n;\r
1997         // 引数チェック\r
1998         if (v == NULL)\r
1999         {\r
2000                 return NULL;\r
2001         }\r
2002 \r
2003         if (CanCreateNewNatEntry(v) == false)\r
2004         {\r
2005                 return NULL;\r
2006         }\r
2007 \r
2008         n = ZeroMalloc(sizeof(NAT_ENTRY));\r
2009         n->Id = Inc(v->Counter);\r
2010         n->v = v;\r
2011         n->lock = NewLock();\r
2012         n->Protocol = NAT_UDP;\r
2013         n->SrcIp = src_ip;\r
2014         n->SrcPort = src_port;\r
2015         n->DestIp = dest_ip;\r
2016         n->DestPort = dest_port;\r
2017 \r
2018         if (dns_proxy_ip != 0)\r
2019         {\r
2020                 n->ProxyDns = true;\r
2021                 n->DestIpProxy = dns_proxy_ip;\r
2022         }\r
2023 \r
2024         n->CreatedTime = n->LastCommTime = v->Now;\r
2025 \r
2026         n->UdpSendQueue = NewQueue();\r
2027         n->UdpRecvQueue = NewQueue();\r
2028 \r
2029         n->UdpSocketCreated = false;\r
2030 \r
2031         SetSockEvent(v->SockEvent);\r
2032 \r
2033 #if     1\r
2034         {\r
2035                 IP ip1, ip2;\r
2036                 char s1[MAX_SIZE], s2[MAX_SIZE];\r
2037                 UINTToIP(&ip1, src_ip);\r
2038                 UINTToIP(&ip2, dest_ip);\r
2039                 IPToStr(s1, 0, &ip1);\r
2040                 IPToStr(s2, 0, &ip2);\r
2041                 Debug("NAT_ENTRY: CreateNatUdp %s %u -> %s %u\n", s1, src_port, s2, dest_port);\r
2042 \r
2043                 NLog(v, "LH_NAT_UDP_CREATED", n->Id, s1, src_port, s2, dest_port);\r
2044         }\r
2045 #endif\r
2046 \r
2047         Add(v->NatTable, n);\r
2048 \r
2049         return n;\r
2050 }\r
2051 \r
2052 // インターネットへの UDP パケットの処理\r
2053 void UdpRecvForInternet(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size, bool dns_proxy)\r
2054 {\r
2055         NAT_ENTRY *n, t;\r
2056         BLOCK *block;\r
2057         void *buf;\r
2058         UINT dns_ip = 0;\r
2059         // 引数チェック\r
2060         if (data == NULL || v == NULL)\r
2061         {\r
2062                 return;\r
2063         }\r
2064 \r
2065         if (dns_proxy)\r
2066         {\r
2067                 // プロキシ接続先の DNS サーバーを取得する\r
2068                 IP ip;\r
2069                 char tmp[MAX_SIZE];\r
2070                 if (GetDefaultDns(&ip) == false)\r
2071                 {\r
2072                         // 失敗\r
2073                         Debug("Failed to GetDefaultDns()\n");\r
2074                         return;\r
2075                 }\r
2076                 dns_ip = IPToUINT(&ip);\r
2077                 IPToStr(tmp, sizeof(tmp), &ip);\r
2078                 Debug("Redirect to DNS Server %s\n", tmp);\r
2079         }\r
2080 \r
2081         // このパケットに関する NAT エントリがすでに作成されているかどうかを調べる\r
2082         SetNat(&t, NAT_UDP, src_ip, src_port, dest_ip, dest_port, 0, 0);\r
2083         n = SearchNat(v, &t);\r
2084 \r
2085         if (n == NULL)\r
2086         {\r
2087                 // 最初のパケットなので NAT エントリを作成する\r
2088                 n = CreateNatUdp(v, src_ip, src_port, dest_ip, dest_port, dns_proxy ? dns_ip : 0);\r
2089                 if (n == NULL)\r
2090                 {\r
2091                         // エントリ作成失敗\r
2092                         return;\r
2093                 }\r
2094 \r
2095                 if (dns_proxy)\r
2096                 {\r
2097                         n->ProxyDns = true;\r
2098                         n->DestIpProxy = dns_ip;\r
2099                 }\r
2100         }\r
2101 \r
2102         // キューにパケットを挿入してイベントを呼び出す\r
2103         buf = Malloc(size);\r
2104         Copy(buf, data, size);\r
2105         block = NewBlock(buf, size, 0);\r
2106         InsertQueue(n->UdpSendQueue, block);\r
2107 \r
2108         SetSockEvent(v->SockEvent);\r
2109 }\r
2110 \r
2111 // DNS パケットの解釈を試行する\r
2112 bool ParseDnsPacket(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)\r
2113 {\r
2114         DNSV4_HEADER *dns;\r
2115         NAT_ENTRY *nat;\r
2116         UINT transaction_id;\r
2117         void *query_data;\r
2118         UINT query_data_size;\r
2119         char hostname[256];\r
2120         // 引数チェック\r
2121         if (v == NULL || data == NULL || size == 0)\r
2122         {\r
2123                 return false;\r
2124         }\r
2125 \r
2126         // ヘッダサイズのチェック\r
2127         if (size < sizeof(DNSV4_HEADER))\r
2128         {\r
2129                 // サイズ不足\r
2130                 return false;\r
2131         }\r
2132 \r
2133         // DNS ヘッダ取得\r
2134         dns = (DNSV4_HEADER *)data;\r
2135         transaction_id = Endian16(dns->TransactionId);\r
2136         if ((dns->Flag1 & 78) != 0 || (dns->Flag1 & 0x80) != 0)\r
2137         {\r
2138                 // オペコード不正\r
2139                 return false;\r
2140         }\r
2141         if (Endian16(dns->NumQuery) != 1)\r
2142         {\r
2143                 // クエリ数不正\r
2144                 return false;\r
2145         }\r
2146 \r
2147         query_data = ((UCHAR *)dns) + sizeof(DNSV4_HEADER);\r
2148         query_data_size = size - sizeof(DNSV4_HEADER);\r
2149 \r
2150         // クエリの解釈\r
2151         if (ParseDnsQuery(hostname, sizeof(hostname), query_data, query_data_size) == false)\r
2152         {\r
2153                 // 解釈失敗\r
2154                 return false;\r
2155         }\r
2156 \r
2157         // DNS エントリの作成\r
2158         nat = CreateNatDns(v, src_ip, src_port, dest_ip, dest_port, transaction_id,\r
2159                 false, hostname);\r
2160 \r
2161         if (nat == false)\r
2162         {\r
2163                 return false;\r
2164         }\r
2165 \r
2166         return true;\r
2167 }\r
2168 \r
2169 // NAT DNS 応答パケットの送信\r
2170 void SendNatDnsResponse(VH *v, NAT_ENTRY *n)\r
2171 {\r
2172         BUF *b;\r
2173         UINT dns_header_size;\r
2174         DNSV4_HEADER *dns;\r
2175         // 引数チェック\r
2176         if (n == NULL || v == NULL)\r
2177         {\r
2178                 return;\r
2179         }\r
2180 \r
2181         // データの生成\r
2182         b = NewBuf();\r
2183 \r
2184         // Query の追加\r
2185         if (n->DnsGetIpFromHost == false)\r
2186         {\r
2187                 BuildDnsQueryPacket(b, n->DnsTargetHostName, false);\r
2188         }\r
2189         else\r
2190         {\r
2191                 BuildDnsQueryPacket(b, n->DnsTargetHostName, true);\r
2192         }\r
2193 \r
2194         // Response の追加\r
2195         if (n->DnsOk)\r
2196         {\r
2197                 if (n->DnsGetIpFromHost == false)\r
2198                 {\r
2199                         BuildDnsResponsePacketA(b, &n->DnsResponseIp);\r
2200                 }\r
2201                 else\r
2202                 {\r
2203                         BuildDnsResponsePacketPtr(b, n->DnsResponseHostName);\r
2204                 }\r
2205         }\r
2206 \r
2207         // DNS ヘッダの生成\r
2208         dns_header_size = sizeof(DNSV4_HEADER) + b->Size;\r
2209 \r
2210         dns = ZeroMalloc(dns_header_size);\r
2211         dns->TransactionId = Endian16((USHORT)n->DnsTransactionId);\r
2212 \r
2213         // 応答フラグの生成\r
2214         if (n->DnsOk)\r
2215         {\r
2216                 dns->Flag1 = 0x85;\r
2217                 dns->Flag2 = 0x80;\r
2218         }\r
2219         else\r
2220         {\r
2221                 dns->Flag1 = 0x85;\r
2222                 dns->Flag2 = 0x83;\r
2223         }\r
2224 \r
2225         dns->NumQuery = Endian16(1);\r
2226         dns->AnswerRRs = Endian16(n->DnsOk != false ? 1 : 0);\r
2227         dns->AuthorityRRs = 0;\r
2228         dns->AdditionalRRs = 0;\r
2229 \r
2230         // データのコピー\r
2231         Copy(((UCHAR *)dns) + sizeof(DNSV4_HEADER), b->Buf, b->Size);\r
2232 \r
2233         // このパケットを送信\r
2234         SendUdp(v, n->SrcIp, n->SrcPort, n->DestIp, n->DestPort, dns, dns_header_size);\r
2235 \r
2236         // メモリ解放\r
2237         Free(dns);\r
2238         FreeBuf(b);\r
2239 }\r
2240 \r
2241 // DNS 応答パケット (ホスト名) の生成\r
2242 void BuildDnsResponsePacketPtr(BUF *b, char *hostname)\r
2243 {\r
2244         USHORT magic;\r
2245         USHORT type, clas;\r
2246         UINT ttl;\r
2247         USHORT len;\r
2248         BUF *c;\r
2249         // 引数チェック\r
2250         if (b == NULL || hostname == NULL)\r
2251         {\r
2252                 return;\r
2253         }\r
2254 \r
2255         magic = Endian16(0xc00c);\r
2256         type = Endian16(0x000c);\r
2257         clas = Endian16(0x0001);\r
2258         ttl = Endian32(NAT_DNS_RESPONSE_TTL);\r
2259 \r
2260         c = BuildDnsHostName(hostname);\r
2261         if (c == NULL)\r
2262         {\r
2263                 return;\r
2264         }\r
2265         len = Endian16((USHORT)c->Size);\r
2266 \r
2267         WriteBuf(b, &magic, 2);\r
2268         WriteBuf(b, &type, 2);\r
2269         WriteBuf(b, &clas, 2);\r
2270         WriteBuf(b, &ttl, 4);\r
2271         WriteBuf(b, &len, 2);\r
2272         WriteBuf(b, c->Buf, c->Size);\r
2273         FreeBuf(c);\r
2274 }\r
2275 \r
2276 // DNS 応答パケット (ホスト IP アドレス) の生成\r
2277 void BuildDnsResponsePacketA(BUF *b, IP *ip)\r
2278 {\r
2279         UINT ip_addr;\r
2280         USHORT magic;\r
2281         USHORT type, clas;\r
2282         UINT ttl;\r
2283         USHORT len;\r
2284         // 引数チェック\r
2285         if (b == NULL || ip == NULL)\r
2286         {\r
2287                 return;\r
2288         }\r
2289 \r
2290         ip_addr = IPToUINT(ip);\r
2291         magic = Endian16(0xc00c);\r
2292         type = Endian16(0x0001);\r
2293         clas = Endian16(0x0001);\r
2294         ttl = Endian32(NAT_DNS_RESPONSE_TTL);\r
2295         len = Endian16((USHORT)sizeof(ttl));\r
2296 \r
2297         WriteBuf(b, &magic, sizeof(magic));\r
2298         WriteBuf(b, &type, sizeof(type));\r
2299         WriteBuf(b, &clas, sizeof(clas));\r
2300         WriteBuf(b, &ttl, sizeof(ttl));\r
2301         WriteBuf(b, &len, sizeof(len));\r
2302         WriteBuf(b, &ip_addr, sizeof(ip_addr));\r
2303 }\r
2304 \r
2305 // DNS クエリデータパケットの生成\r
2306 void BuildDnsQueryPacket(BUF *b, char *hostname, bool ptr)\r
2307 {\r
2308         USHORT val;\r
2309         BUF *c;\r
2310         // 引数チェック\r
2311         if (b == NULL || hostname == NULL)\r
2312         {\r
2313                 return;\r
2314         }\r
2315 \r
2316         // ホスト名をバッファに変換\r
2317         c = BuildDnsHostName(hostname);\r
2318         if (c == NULL)\r
2319         {\r
2320                 return;\r
2321         }\r
2322 \r
2323         WriteBuf(b, c->Buf, c->Size);\r
2324         FreeBuf(c);\r
2325 \r
2326         // 種類とクラス\r
2327         if (ptr == false)\r
2328         {\r
2329                 val = Endian16(0x0001);\r
2330         }\r
2331         else\r
2332         {\r
2333                 val = Endian16(0x000c);\r
2334         }\r
2335         WriteBuf(b, &val, 2);\r
2336 \r
2337         val = Endian16(0x0001);\r
2338         WriteBuf(b, &val, 2);\r
2339 }\r
2340 \r
2341 // DNS ホスト名バッファの生成\r
2342 BUF *BuildDnsHostName(char *hostname)\r
2343 {\r
2344         UINT i;\r
2345         UCHAR size;\r
2346         TOKEN_LIST *token;\r
2347         BUF *b;\r
2348         // 引数チェック\r
2349         if (hostname == NULL)\r
2350         {\r
2351                 return NULL;\r
2352         }\r
2353 \r
2354         // ホスト名をトークンに分割\r
2355         token = ParseToken(hostname, ".");\r
2356         if (token == NULL)\r
2357         {\r
2358                 return NULL;\r
2359         }\r
2360 \r
2361         b = NewBuf();\r
2362 \r
2363         // ホスト文字列を追加\r
2364         for (i = 0;i < token->NumTokens;i++)\r
2365         {\r
2366                 size = (UCHAR)StrLen(token->Token[i]);\r
2367                 WriteBuf(b, &size, 1);\r
2368                 WriteBuf(b, token->Token[i], size);\r
2369         }\r
2370 \r
2371         // NULL 文字\r
2372         size = 0;\r
2373         WriteBuf(b, &size, 1);\r
2374 \r
2375         SeekBuf(b, 0, 0);\r
2376 \r
2377         FreeToken(token);\r
2378 \r
2379         return b;\r
2380 }\r
2381 \r
2382 // NAT DNS エントリの処理\r
2383 void PollingNatDns(VH *v, NAT_ENTRY *n)\r
2384 {\r
2385         // 引数チェック\r
2386         if (v == NULL || n == NULL)\r
2387         {\r
2388                 return;\r
2389         }\r
2390 \r
2391         if (n->DnsFinished)\r
2392         {\r
2393                 if (n->DnsPollingFlag == false)\r
2394                 {\r
2395                         n->DnsPollingFlag = true;\r
2396                         // 処理が完了した\r
2397                         SendNatDnsResponse(v, n);\r
2398 \r
2399                         // 終了処理\r
2400                         n->DisconnectNow = true;\r
2401                 }\r
2402         }\r
2403 }\r
2404 \r
2405 // NAT DNS エントリの作成\r
2406 NAT_ENTRY *CreateNatDns(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port,\r
2407                                   UINT transaction_id, bool dns_get_ip_from_host, char *dns_target_host_name)\r
2408 {\r
2409         NAT_ENTRY *n;\r
2410         // 引数チェック\r
2411         if (v == NULL || dns_target_host_name == NULL)\r
2412         {\r
2413                 return NULL;\r
2414         }\r
2415 \r
2416         if (CanCreateNewNatEntry(v) == false)\r
2417         {\r
2418                 return NULL;\r
2419         }\r
2420 \r
2421         n = ZeroMalloc(sizeof(NAT_ENTRY));\r
2422         n->Id = Inc(v->Counter);\r
2423         n->v = v;\r
2424         n->lock = NewLock();\r
2425         n->Protocol = NAT_DNS;\r
2426         n->SrcIp = src_ip;\r
2427         n->SrcPort = src_port;\r
2428         n->DestIp = dest_ip;\r
2429         n->DestPort = dest_port;\r
2430         n->DnsTransactionId = transaction_id;\r
2431         n->CreatedTime = n->LastCommTime = v->Now;\r
2432         n->DisconnectNow = false;\r
2433 \r
2434         n->DnsGetIpFromHost = false;\r
2435         n->DnsTargetHostName = CopyStr(dns_target_host_name);\r
2436 \r
2437         Add(v->NatTable, n);\r
2438 \r
2439 #if     1\r
2440         {\r
2441                 IP ip1, ip2;\r
2442                 char s1[MAX_SIZE], s2[MAX_SIZE];\r
2443                 UINTToIP(&ip1, src_ip);\r
2444                 UINTToIP(&ip2, dest_ip);\r
2445                 IPToStr(s1, 0, &ip1);\r
2446                 IPToStr(s2, 0, &ip2);\r
2447                 Debug("NAT_ENTRY: CreateNatDns %s %u -> %s %u\n", s1, src_port, s2, dest_port);\r
2448         }\r
2449 #endif\r
2450 \r
2451 \r
2452         return n;\r
2453 }\r
2454 \r
2455 // 次のバイトを取得\r
2456 UCHAR GetNextByte(BUF *b)\r
2457 {\r
2458         UCHAR c = 0;\r
2459         // 引数チェック\r
2460         if (b == NULL)\r
2461         {\r
2462                 return 0;\r
2463         }\r
2464 \r
2465         if (ReadBuf(b, &c, 1) != 1)\r
2466         {\r
2467                 return 0;\r
2468         }\r
2469 \r
2470         return c;\r
2471 }\r
2472 \r
2473 // DNS クエリの解釈\r
2474 bool ParseDnsQuery(char *name, UINT name_size, void *data, UINT data_size)\r
2475 {\r
2476         BUF *b;\r
2477         char tmp[257];\r
2478         bool ok = true;\r
2479         USHORT val;\r
2480         // 引数チェック\r
2481         if (name == NULL || data == NULL || data_size == 0)\r
2482         {\r
2483                 return false;\r
2484         }\r
2485         StrCpy(name, name_size, "");\r
2486 \r
2487         b = NewBuf();\r
2488         WriteBuf(b, data, data_size);\r
2489         SeekBuf(b, 0, 0);\r
2490 \r
2491         while (true)\r
2492         {\r
2493                 UINT next_len = (UINT)GetNextByte(b);\r
2494                 if (next_len > 0)\r
2495                 {\r
2496                         // 指定した文字だけ読む\r
2497                         Zero(tmp, sizeof(tmp));\r
2498                         if (ReadBuf(b, tmp, next_len) != next_len)\r
2499                         {\r
2500                                 ok = false;\r
2501                                 break;\r
2502                         }\r
2503                         // 追記\r
2504                         if (StrLen(name) != 0)\r
2505                         {\r
2506                                 StrCat(name, name_size, ".");\r
2507                         }\r
2508                         StrCat(name, name_size, tmp);\r
2509                 }\r
2510                 else\r
2511                 {\r
2512                         // すべて読み終えた\r
2513                         break;\r
2514                 }\r
2515         }\r
2516 \r
2517         if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))\r
2518         {\r
2519                 ok = false;\r
2520         }\r
2521         else\r
2522         {\r
2523                 if (Endian16(val) != 0x01 && Endian16(val) != 0x0c)\r
2524                 {\r
2525                         ok = false;\r
2526                 }\r
2527         }\r
2528 \r
2529         if (ReadBuf(b, &val, sizeof(val)) != sizeof(val))\r
2530         {\r
2531                 ok = false;\r
2532         }\r
2533         else\r
2534         {\r
2535                 if (Endian16(val) != 0x01)\r
2536                 {\r
2537                         ok = false;\r
2538                 }\r
2539         }\r
2540 \r
2541         FreeBuf(b);\r
2542 \r
2543         if (ok == false || StrLen(name) == 0)\r
2544         {\r
2545                 return false;\r
2546         }\r
2547         else\r
2548         {\r
2549                 return true;\r
2550         }\r
2551 }\r
2552 \r
2553 // DNS プロキシとして動作する\r
2554 void DnsProxy(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)\r
2555 {\r
2556         // 引数チェック\r
2557         if (v == NULL || data == NULL || size == 0)\r
2558         {\r
2559                 return;\r
2560         }\r
2561 \r
2562         // まず DNS クエリを解釈することができるかどうか試してみる\r
2563         //if (ParseDnsPacket(v, src_ip, src_port, dest_ip, dest_port, data, size) == false)\r
2564         {\r
2565                 // うまくいかない場合は要求をそのまま投げる\r
2566                 UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, data, size, true);\r
2567         }\r
2568 }\r
2569 \r
2570 // 仮想ホストへの UDP パケットの処理\r
2571 void UdpRecvForMe(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)\r
2572 {\r
2573         // 引数チェック\r
2574         if (data == NULL || v == NULL)\r
2575         {\r
2576                 return;\r
2577         }\r
2578 \r
2579         if (dest_port == NAT_DNS_PROXY_PORT)\r
2580         {\r
2581                 // DNS プロキシ起動\r
2582                 DnsProxy(v, src_ip, src_port, dest_ip, dest_port, data, size);\r
2583         }\r
2584 }\r
2585 \r
2586 // ブロードキャスト UDP パケットの処理\r
2587 void UdpRecvForBroadcast(VH *v, UINT src_ip, UINT src_port, UINT dest_ip, UINT dest_port, void *data, UINT size)\r
2588 {\r
2589         // 引数チェック\r
2590         if (data == NULL || v == NULL)\r
2591         {\r
2592                 return;\r
2593         }\r
2594 }\r
2595 \r
2596 // UDP パケットを受信した\r
2597 void VirtualUdpReceived(VH *v, UINT src_ip, UINT dest_ip, void *data, UINT size, bool mac_broadcast)\r
2598 {\r
2599         UDP_HEADER *udp;\r
2600         UINT packet_length;\r
2601         void *buf;\r
2602         UINT buf_size;\r
2603         UINT src_port, dest_port;\r
2604         // 引数チェック\r
2605         if (v == NULL || data == NULL)\r
2606         {\r
2607                 return;\r
2608         }\r
2609 \r
2610         // ヘッダのチェック\r
2611         udp = (UDP_HEADER *)data;\r
2612         if (size < UDP_HEADER_SIZE)\r
2613         {\r
2614                 return;\r
2615         }\r
2616         packet_length = Endian16(udp->PacketLength);\r
2617         if (packet_length != size)\r
2618         {\r
2619                 return;\r
2620         }\r
2621         buf = ((UCHAR *)data) + UDP_HEADER_SIZE;\r
2622         buf_size = size - UDP_HEADER_SIZE;\r
2623         src_port = Endian16(udp->SrcPort);\r
2624         dest_port = Endian16(udp->DstPort);\r
2625         // ポート番号をチェック\r
2626         if (dest_port == 0)\r
2627         {\r
2628                 // ポート番号が不正\r
2629                 return;\r
2630         }\r
2631 \r
2632         // 自分宛のパケットまたはブロードキャストパケットかどうか判別する\r
2633         if (dest_ip == v->HostIP)\r
2634         {\r
2635                 // 自分宛のパケットが届いた\r
2636                 UdpRecvForMe(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);\r
2637         }\r
2638         else if (mac_broadcast || dest_ip == 0xffffffff || dest_ip == GetBroadcastAddress(v->HostIP, v->HostMask))\r
2639         {\r
2640                 // ブロードキャストパケットが届いた\r
2641                 UdpRecvForBroadcast(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size);\r
2642         }\r
2643         else if (IsInNetwork(dest_ip, v->HostIP, v->HostMask) == false)\r
2644         {\r
2645                 // ローカルアドレス以外 (つまりインターネット上) へのパケットが届いた\r
2646                 UdpRecvForInternet(v, src_ip, src_port, dest_ip, dest_port, buf, buf_size, false);\r
2647         }\r
2648         else\r
2649         {\r
2650                 // ローカルアドレスが届いた。無視\r
2651         }\r
2652 }\r
2653 \r
2654 // 指定した IP アドレスの属するサブネットのネットワークアドレスを求める\r
2655 UINT GetNetworkAddress(UINT addr, UINT mask)\r
2656 {\r
2657         return (addr & mask);\r
2658 }\r
2659 \r
2660 // 指定した IP アドレスの属するサブネットのブロードキャストアドレスを求める\r
2661 UINT GetBroadcastAddress(UINT addr, UINT mask)\r
2662 {\r
2663         return ((addr & mask) | (~mask));\r
2664 }\r
2665 \r
2666 // 指定した IP アドレスが別の指定したアドレスとサブネットマスクによって表現される\r
2667 // サブネットワークに所属しているかどうか判別する\r
2668 bool IsInNetwork(UINT uni_addr, UINT network_addr, UINT mask)\r
2669 {\r
2670         if (GetNetworkAddress(uni_addr, mask) == GetNetworkAddress(network_addr, mask))\r
2671         {\r
2672                 return true;\r
2673         }\r
2674         return false;\r
2675 }\r
2676 \r
2677 // UDP パケットの送信\r
2678 void SendUdp(VH *v, UINT dest_ip, UINT dest_port, UINT src_ip, UINT src_port, void *data, UINT size)\r
2679 {\r
2680         UDPV4_PSEUDO_HEADER *vh;\r
2681         UDP_HEADER *udp;\r
2682         UINT udp_packet_length = UDP_HEADER_SIZE + size;\r
2683         USHORT checksum;\r
2684         // 引数チェック\r
2685         if (v == NULL || data == NULL)\r
2686         {\r
2687                 return;\r
2688         }\r
2689         if (udp_packet_length > 65536)\r
2690         {\r
2691                 return;\r
2692         }\r
2693 \r
2694         // 仮想ヘッダを生成\r
2695         vh = Malloc(sizeof(UDPV4_PSEUDO_HEADER) + size);\r
2696         udp = (UDP_HEADER *)(((UCHAR *)vh) + 12);\r
2697 \r
2698         vh->SrcIP = src_ip;\r
2699         vh->DstIP = dest_ip;\r
2700         vh->Reserved = 0;\r
2701         vh->Protocol = IP_PROTO_UDP;\r
2702         vh->PacketLength1 = Endian16((USHORT)udp_packet_length);\r
2703         udp->SrcPort = Endian16((USHORT)src_port);\r
2704         udp->DstPort = Endian16((USHORT)dest_port);\r
2705         udp->PacketLength = Endian16((USHORT)udp_packet_length);\r
2706         udp->Checksum = 0;\r
2707 \r
2708         // データをコピー\r
2709         Copy(((UCHAR *)udp) + UDP_HEADER_SIZE, data, size);\r
2710 \r
2711         // チェックサムを計算\r
2712         checksum = IpChecksum(vh, udp_packet_length + 12);\r
2713         if (checksum == 0x0000)\r
2714         {\r
2715                 checksum = 0xffff;\r
2716         }\r
2717         udp->Checksum = checksum;\r
2718 \r
2719         // パケットを送信\r
2720         SendIp(v, dest_ip, src_ip, IP_PROTO_UDP, udp, udp_packet_length);\r
2721 \r
2722         // メモリ解放\r
2723         Free(vh);\r
2724 }\r
2725 \r
2726 // IP 結合オブジェクトのポーリング\r
2727 void PollingIpCombine(VH *v)\r
2728 {\r
2729         LIST *o;\r
2730         UINT i;\r
2731         // 引数チェック\r
2732         if (v == NULL)\r
2733         {\r
2734                 return;\r
2735         }\r
2736 \r
2737         // 古い結合オブジェクトを破棄する\r
2738         o = NULL;\r
2739         for (i = 0;i < LIST_NUM(v->IpCombine);i++)\r
2740         {\r
2741                 IP_COMBINE *c = LIST_DATA(v->IpCombine, i);\r
2742 \r
2743                 if (c->Expire < v->Now)\r
2744                 {\r
2745                         if (o == NULL)\r
2746                         {\r
2747                                 o = NewListFast(NULL);\r
2748                         }\r
2749                         Add(o, c);\r
2750                 }\r
2751         }\r
2752 \r
2753         if (o != NULL)\r
2754         {\r
2755                 for (i = 0;i < LIST_NUM(o);i++)\r
2756                 {\r
2757                         IP_COMBINE *c = LIST_DATA(o, i);\r
2758 \r
2759                         // リストから削除\r
2760                         Delete(v->IpCombine, c);\r
2761 \r
2762                         // メモリ解放\r
2763                         FreeIpCombine(v, c);\r
2764                 }\r
2765                 ReleaseList(o);\r
2766         }\r
2767 }\r
2768 \r
2769 // ICMP パケットを送信する\r
2770 void VirtualIcmpSend(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)\r
2771 {\r
2772         ICMP_HEADER *icmp;\r
2773         void *data_buf;\r
2774         // 引数チェック\r
2775         if (v == NULL || data == NULL)\r
2776         {\r
2777                 return;\r
2778         }\r
2779 \r
2780         // ヘッダ組み立て\r
2781         icmp = ZeroMalloc(sizeof(ICMP_HEADER) + size);\r
2782         // データコピー\r
2783         data_buf = ((UCHAR *)icmp) + sizeof(ICMP_HEADER);\r
2784         Copy(data_buf, data, size);\r
2785         // その他\r
2786         icmp->Checksum = 0;\r
2787         icmp->Code = 0;\r
2788         icmp->Type = ICMP_TYPE_ECHO_RESPONSE;\r
2789         // チェックサム\r
2790         icmp->Checksum = IpChecksum(icmp, sizeof(ICMP_HEADER) + size);\r
2791 \r
2792         // IP パケット送信\r
2793         SendIp(v, dst_ip, src_ip, IP_PROTO_ICMPV4, icmp, sizeof(ICMP_HEADER) + size);\r
2794 \r
2795         // メモリ解放\r
2796         Free(icmp);\r
2797 }\r
2798 \r
2799 // ICMP Echo Response パケットを送信する\r
2800 void VirtualIcmpEchoSendResponse(VH *v, UINT src_ip, UINT dst_ip, USHORT id, USHORT seq_no, void *data, UINT size)\r
2801 {\r
2802         ICMP_ECHO *e;\r
2803         // 引数チェック\r
2804         if (v == NULL || data == NULL)\r
2805         {\r
2806                 return;\r
2807         }\r
2808 \r
2809         // ヘッダ組み立て\r
2810         e = ZeroMalloc(sizeof(ICMP_ECHO) + size);\r
2811         e->Identifier = Endian16(id);\r
2812         e->SeqNo = Endian16(seq_no);\r
2813 \r
2814         // データコピー\r
2815         Copy(((UCHAR *)e) + sizeof(ICMP_ECHO), data, size);\r
2816 \r
2817         // ICMP 送信\r
2818         VirtualIcmpSend(v, src_ip, dst_ip, e, sizeof(ICMP_ECHO) + size);\r
2819 \r
2820         // メモリ解放\r
2821         Free(e);\r
2822 }\r
2823 \r
2824 // ICMP Echo Request パケットを受信した\r
2825 void VirtualIcmpEchoRequestReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)\r
2826 {\r
2827         ICMP_ECHO *echo;\r
2828         UINT data_size;\r
2829         void *data_buf;\r
2830         USHORT id, seq_no;\r
2831         // 引数チェック\r
2832         if (v == NULL || data == NULL)\r
2833         {\r
2834                 return;\r
2835         }\r
2836 \r
2837         echo = (ICMP_ECHO *)data;\r
2838 \r
2839         // エコーサイズチェック\r
2840         if (size < sizeof(ICMP_ECHO))\r
2841         {\r
2842                 // データが足らない\r
2843                 return;\r
2844         }\r
2845 \r
2846         id = Endian16(echo->Identifier);\r
2847         seq_no = Endian16(echo->SeqNo);\r
2848 \r
2849         // データサイズ\r
2850         data_size = size - sizeof(ICMP_ECHO);\r
2851 \r
2852         // データ本体\r
2853         data_buf = ((UCHAR *)data) + sizeof(ICMP_ECHO);\r
2854 \r
2855         // ICMP Echo Response を返す\r
2856         VirtualIcmpEchoSendResponse(v, dst_ip, src_ip, id, seq_no, data_buf, data_size);\r
2857 }\r
2858 \r
2859 // ICMP パケットを受信した\r
2860 void VirtualIcmpReceived(VH *v, UINT src_ip, UINT dst_ip, void *data, UINT size)\r
2861 {\r
2862         ICMP_HEADER *icmp;\r
2863         UINT msg_size;\r
2864         USHORT checksum_calc, checksum_original;\r
2865         // 引数チェック\r
2866         if (v == NULL || data == NULL)\r
2867         {\r
2868                 return;\r
2869         }\r
2870 \r
2871         // サイズチェック\r
2872         if (size < sizeof(ICMP_HEADER))\r
2873         {\r
2874                 return;\r
2875         }\r
2876 \r
2877         // ICMP ヘッダ\r
2878         icmp = (ICMP_HEADER *)data;\r
2879 \r
2880         // ICMP メッセージサイズの取得\r
2881         msg_size = size - sizeof(ICMP_HEADER);\r
2882 \r
2883         // ICMP ヘッダのチェックサムをチェックする\r
2884         checksum_original = icmp->Checksum;\r
2885         icmp->Checksum = 0;\r
2886         checksum_calc = IpChecksum(data, size);\r
2887         icmp->Checksum = checksum_original;\r
2888 \r
2889         if (checksum_calc != checksum_original)\r
2890         {\r
2891                 // チェックサムが不正\r
2892                 Debug("ICMP CheckSum Failed.\n");\r
2893                 return;\r
2894         }\r
2895 \r
2896         // オペコードによって識別\r
2897         switch (icmp->Type)\r
2898         {\r
2899         case ICMP_TYPE_ECHO_REQUEST:    // ICMP Echo 要求\r
2900                 VirtualIcmpEchoRequestReceived(v, src_ip, dst_ip, ((UCHAR *)data) + sizeof(ICMP_HEADER), msg_size);\r
2901                 break;\r
2902 \r
2903         case ICMP_TYPE_ECHO_RESPONSE:   // ICMP Echo 応答\r
2904                 // 何もしない\r
2905                 break;\r
2906         }\r
2907 }\r
2908 \r
2909 // IP パケットを受信した\r
2910 void IpReceived(VH *v, UINT src_ip, UINT dest_ip, UINT protocol, void *data, UINT size, bool mac_broadcast)\r
2911 {\r
2912         // 引数チェック\r
2913         if (v == NULL || data == NULL)\r
2914         {\r
2915                 return;\r
2916         }\r
2917 \r
2918         // サポートされている上位プロトコルにデータを渡す\r
2919         switch (protocol)\r
2920         {\r
2921         case IP_PROTO_ICMPV4:   // ICMPv4\r
2922                 VirtualIcmpReceived(v, src_ip, dest_ip, data, size);\r
2923                 break;\r
2924 \r
2925         case IP_PROTO_TCP:              // TCP\r
2926                 if (mac_broadcast == false)\r
2927                 {\r
2928                         VirtualTcpReceived(v, src_ip, dest_ip, data, size);\r
2929                 }\r
2930                 break;\r
2931 \r
2932         case IP_PROTO_UDP:              // UDP\r
2933                 VirtualUdpReceived(v, src_ip, dest_ip, data, size, mac_broadcast);\r
2934                 break;\r
2935         }\r
2936 }\r
2937 \r
2938 // IP ヘッダのチェックサムを確認する\r
2939 bool IpCheckChecksum(IPV4_HEADER *ip)\r
2940 {\r
2941         UINT header_size;\r
2942         USHORT checksum_original, checksum_calc;\r
2943         // 引数チェック\r
2944         if (ip == NULL)\r
2945         {\r
2946                 return false;\r
2947         }\r
2948 \r
2949         header_size = IPV4_GET_HEADER_LEN(ip) * 4;\r
2950         checksum_original = ip->Checksum;\r
2951         ip->Checksum = 0;\r
2952         checksum_calc = IpChecksum(ip, header_size);\r
2953         ip->Checksum = checksum_original;\r
2954 \r
2955         if (checksum_original == checksum_calc)\r
2956         {\r
2957                 return true;\r
2958         }\r
2959         else\r
2960         {\r
2961                 return false;\r
2962         }\r
2963 }\r
2964 \r
2965 // チェックサムを計算する\r
2966 USHORT IpChecksum(void *buf, UINT size)\r
2967 {\r
2968         int sum = 0;\r
2969         USHORT *addr = (USHORT *)buf;\r
2970         int len = (int)size;\r
2971         USHORT *w = addr;\r
2972         int nleft = len;\r
2973         USHORT answer = 0;\r
2974 \r
2975         while (nleft > 1)\r
2976         {\r
2977                 sum += *w++;\r
2978                 nleft -= 2;\r
2979         }\r
2980 \r
2981         if (nleft == 1)\r
2982         {\r
2983                 *(UCHAR *)(&answer) = *(UCHAR *)w;\r
2984                 sum += answer;\r
2985         }\r
2986 \r
2987         sum = (sum >> 16) + (sum & 0xffff);\r
2988         sum += (sum >> 16);\r
2989 \r
2990         answer = ~sum;\r
2991         \r
2992         return answer;\r
2993 }\r
2994 \r
2995 // IP 結合オブジェクトに新しく受信した IP パケットを結合する\r
2996 void CombineIp(VH *v, IP_COMBINE *c, UINT offset, void *data, UINT size, bool last_packet)\r
2997 {\r
2998         UINT i;\r
2999         IP_PART *p;\r
3000         UINT need_size;\r
3001         UINT data_size_delta;\r
3002         // 引数チェック\r
3003         if (c == NULL || data == NULL)\r
3004         {\r
3005                 return;\r
3006         }\r
3007 \r
3008         // オフセットとサイズをチェック\r
3009         if ((offset + size) > 65535)\r
3010         {\r
3011                 // 64Kbytes を超えるパケットは処理しない\r
3012                 return;\r
3013         }\r
3014 \r
3015         if (last_packet == false && c->Size != 0)\r
3016         {\r
3017                 if ((offset + size) > c->Size)\r
3018                 {\r
3019                         // パケットサイズより大きいパケットは処理しない\r
3020                         return;\r
3021                 }\r
3022         }\r
3023 \r
3024         need_size = offset + size;\r
3025         data_size_delta = c->DataReserved;\r
3026         // バッファが不足している場合は十分確保する\r
3027         while (c->DataReserved < need_size)\r
3028         {\r
3029                 c->DataReserved = c->DataReserved * 4;\r
3030                 c->Data = ReAlloc(c->Data, c->DataReserved);\r
3031         }\r
3032         data_size_delta = c->DataReserved - data_size_delta;\r
3033         v->CurrentIpQuota += data_size_delta;\r
3034 \r
3035         // データをバッファに上書きする\r
3036         Copy(((UCHAR *)c->Data) + offset, data, size);\r
3037 \r
3038         if (last_packet)\r
3039         {\r
3040                 // No More Flagment パケットが届いた場合、このデータグラムのサイズが確定する\r
3041                 c->Size = offset + size;\r
3042         }\r
3043 \r
3044         // オフセットとサイズによって表現されている領域と既存の受信済みリストの\r
3045         // オフセットとサイズによって表現されている領域との間の重複をチェックする\r
3046         for (i = 0;i < LIST_NUM(c->IpParts);i++)\r
3047         {\r
3048                 UINT moving_size;\r
3049                 IP_PART *p = LIST_DATA(c->IpParts, i);\r
3050 \r
3051                 // 先頭領域と既存領域との重複をチェック\r
3052                 if ((p->Offset <= offset) && ((p->Offset + p->Size) > offset))\r
3053                 {\r
3054                         // このパケットと既存パケットとの間で先頭部分に重複が見つかったので\r
3055                         // このパケットのオフセットを後方に圧縮する\r
3056 \r
3057                         if ((offset + size) <= (p->Offset + p->Size))\r
3058                         {\r
3059                                 // このパケットは既存のパケットの中に埋もれている\r
3060                                 size = 0;\r
3061                         }\r
3062                         else\r
3063                         {\r
3064                                 // 後方領域は重なっていない\r
3065                                 moving_size = p->Offset + p->Size - offset;\r
3066                                 offset += moving_size;\r
3067                                 size -= moving_size;\r
3068                         }\r
3069                 }\r
3070                 if ((p->Offset < (offset + size)) && ((p->Offset + p->Size) >= (offset + size)))\r
3071                 {\r
3072                         // このパケットと既存パケットとの間で後方部分に重複が見つかったので\r
3073                         // このパケットのサイズを前方に圧縮する\r
3074 \r
3075                         moving_size = p->Offset + p->Size - offset - size;\r
3076                         size -= moving_size;\r
3077                 }\r
3078 \r
3079                 if ((p->Offset >= offset) && ((p->Offset + p->Size) <= (offset + size)))\r
3080                 {\r
3081                         // このパケットが既存のパケットを完全に覆いかぶさるように上書きされた\r
3082                         p->Size = 0;\r
3083                 }\r
3084         }\r
3085 \r
3086         if (size != 0)\r
3087         {\r
3088                 // このパケットを登録する\r
3089                 p = ZeroMalloc(sizeof(IP_PART));\r
3090 \r
3091                 p->Offset = offset;\r
3092                 p->Size = size;\r
3093 \r
3094                 Add(c->IpParts, p);\r
3095         }\r
3096 \r
3097         if (c->Size != 0)\r
3098         {\r
3099                 // すでに受信したデータ部分リストの合計サイズを取得する\r
3100                 UINT total_size = 0;\r
3101                 UINT i;\r
3102 \r
3103                 for (i = 0;i < LIST_NUM(c->IpParts);i++)\r
3104                 {\r
3105                         IP_PART *p = LIST_DATA(c->IpParts, i);\r
3106 \r
3107                         total_size += p->Size;\r
3108                 }\r
3109 \r
3110                 if (total_size == c->Size)\r
3111                 {\r
3112                         // IP パケットをすべて受信した\r
3113                         IpReceived(v, c->SrcIP, c->DestIP, c->Protocol, c->Data, c->Size, c->MacBroadcast);\r
3114 \r
3115                         // 結合オブジェクトの解放\r
3116                         FreeIpCombine(v, c);\r
3117 \r
3118                         // 結合オブジェクトをリストから削除\r
3119                         Delete(v->IpCombine, c);\r
3120                 }\r
3121         }\r
3122 }\r
3123 \r
3124 // IP 結合オブジェクトの解放\r
3125 void FreeIpCombine(VH *v, IP_COMBINE *c)\r
3126 {\r
3127         UINT i;\r
3128         // 引数チェック\r
3129         if (c == NULL)\r
3130         {\r
3131                 return;\r
3132         }\r
3133 \r
3134         // データ解放\r
3135         v->CurrentIpQuota -= c->DataReserved;\r
3136         Free(c->Data);\r
3137 \r
3138         // 部分リスト解放\r
3139         for (i = 0;i < LIST_NUM(c->IpParts);i++)\r
3140         {\r
3141                 IP_PART *p = LIST_DATA(c->IpParts, i);\r
3142 \r
3143                 Free(p);\r
3144         }\r
3145 \r
3146         ReleaseList(c->IpParts);\r
3147         Free(c);\r
3148 }\r
3149 \r
3150 // IP 結合リストを検索\r
3151 IP_COMBINE *SearchIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol)\r
3152 {\r
3153         IP_COMBINE *c, t;\r
3154         // 引数チェック\r
3155         if (v == NULL)\r
3156         {\r
3157                 return NULL;\r
3158         }\r
3159 \r
3160         t.DestIP = dest_ip;\r
3161         t.SrcIP = src_ip;\r
3162         t.Id = id;\r
3163         t.Protocol = protocol;\r
3164 \r
3165         c = Search(v->IpCombine, &t);\r
3166 \r
3167         return c;\r
3168 }\r
3169 \r
3170 // IP 結合リストに新しいオブジェクトを作成して挿入\r
3171 IP_COMBINE *InsertIpCombine(VH *v, UINT src_ip, UINT dest_ip, USHORT id, UCHAR protocol, bool mac_broadcast)\r
3172 {\r
3173         IP_COMBINE *c;\r
3174         // 引数チェック\r
3175         if (v == NULL)\r
3176         {\r
3177                 return NULL;\r
3178         }\r
3179 \r
3180         // クォータを調べる\r
3181         if ((v->CurrentIpQuota + IP_COMBINE_INITIAL_BUF_SIZE) > IP_COMBINE_WAIT_QUEUE_SIZE_QUOTA)\r
3182         {\r
3183                 // これ以上 IP パケットを格納できない\r
3184                 return NULL;\r
3185         }\r
3186 \r
3187         c = ZeroMalloc(sizeof(IP_COMBINE));\r
3188         c->DestIP = dest_ip;\r
3189         c->SrcIP = src_ip;\r
3190         c->Id = id;\r
3191         c->Expire = v->Now + (UINT64)IP_COMBINE_TIMEOUT;\r
3192         c->Size = 0;\r
3193         c->IpParts = NewList(NULL);\r
3194         c->Protocol = protocol;\r
3195         c->MacBroadcast = mac_broadcast;\r
3196 \r
3197         // メモリを確保\r
3198         c->DataReserved = IP_COMBINE_INITIAL_BUF_SIZE;\r
3199         c->Data = Malloc(c->DataReserved);\r
3200         v->CurrentIpQuota += c->DataReserved;\r
3201 \r
3202         Insert(v->IpCombine, c);\r
3203 \r
3204         return c;\r
3205 }\r
3206 \r
3207 // IP 結合リストの初期化\r
3208 void InitIpCombineList(VH *v)\r
3209 {\r
3210         // 引数チェック\r
3211         if (v == NULL)\r
3212         {\r
3213                 return;\r
3214         }\r
3215 \r
3216         v->IpCombine = NewList(CompareIpCombine);\r
3217 }\r
3218 \r
3219 // IP 結合リストの解放\r
3220 void FreeIpCombineList(VH *v)\r
3221 {\r
3222         UINT i;\r
3223         // 引数チェック\r
3224         if (v == NULL)\r
3225         {\r
3226                 return;\r
3227         }\r
3228 \r
3229         for (i = 0;i < LIST_NUM(v->IpCombine);i++)\r
3230         {\r
3231                 IP_COMBINE *c = LIST_DATA(v->IpCombine, i);\r
3232 \r
3233                 FreeIpCombine(v, c);\r
3234         }\r
3235 \r
3236         ReleaseList(v->IpCombine);\r
3237 }\r
3238 \r
3239 // IP 結合リストの比較\r
3240 int CompareIpCombine(void *p1, void *p2)\r
3241 {\r
3242         IP_COMBINE *c1, *c2;\r
3243         if (p1 == NULL || p2 == NULL)\r
3244         {\r
3245                 return 0;\r
3246         }\r
3247         c1 = *(IP_COMBINE **)p1;\r
3248         c2 = *(IP_COMBINE **)p2;\r
3249         if (c1 == NULL || c2 == NULL)\r
3250         {\r
3251                 return 0;\r
3252         }\r
3253         if (c1->Id > c2->Id)\r
3254         {\r
3255                 return 1;\r
3256         }\r
3257         else if (c1->Id < c2->Id)\r
3258         {\r
3259                 return -1;\r
3260         }\r
3261         else if (c1->DestIP > c2->DestIP)\r
3262         {\r
3263                 return 1;\r
3264         }\r
3265         else if (c1->DestIP < c2->DestIP)\r
3266         {\r
3267                 return -1;\r
3268         }\r
3269         else if (c1->SrcIP > c2->SrcIP)\r
3270         {\r
3271                 return 1;\r
3272         }\r
3273         else if (c1->SrcIP < c2->SrcIP)\r
3274         {\r
3275                 return -1;\r
3276         }\r
3277         else if (c1->Protocol > c2->Protocol)\r
3278         {\r
3279                 return 1;\r
3280         }\r
3281         else if (c1->Protocol < c2->Protocol)\r
3282         {\r
3283                 return -1;\r
3284         }\r
3285         return 0;\r
3286 }\r
3287 \r
3288 // IP パケットを受信した\r
3289 void VirtualIpReceived(VH *v, PKT *packet)\r
3290 {\r
3291         IPV4_HEADER *ip;\r
3292         void *data;\r
3293         UINT data_size_recved;\r
3294         UINT size;\r
3295         UINT ipv4_header_size;\r
3296         bool last_packet;\r
3297         // 引数チェック\r
3298         if (v == NULL || packet == NULL)\r
3299         {\r
3300                 return;\r
3301         }\r
3302 \r
3303         ip = packet->L3.IPv4Header;\r
3304 \r
3305         // IPv4 ヘッダのサイズを取得する\r
3306         ipv4_header_size = IPV4_GET_HEADER_LEN(packet->L3.IPv4Header) * 4;\r
3307 \r
3308         // IPv4 ヘッダのチェックサムを計算する\r
3309         if (IpCheckChecksum(ip) == false)\r
3310         {\r
3311                 return;\r
3312         }\r
3313 \r
3314         // データへのポインタを取得する\r
3315         data = ((UCHAR *)packet->L3.PointerL3) + ipv4_header_size;\r
3316 \r
3317         // ARP テーブルに登録しておく\r
3318         ArpIpWasKnown(v, packet->L3.IPv4Header->SrcIP, packet->MacAddressSrc);\r
3319 \r
3320         // データサイズを取得する\r
3321         size = Endian16(ip->TotalLength);\r
3322         if (size <= ipv4_header_size)\r
3323         {\r
3324                 // データが無い\r
3325                 return;\r
3326         }\r
3327         size -= ipv4_header_size;\r
3328 \r
3329         // 実際に受信したデータサイズを取得する\r
3330         data_size_recved = packet->PacketSize - (ipv4_header_size + MAC_HEADER_SIZE);\r
3331         if (data_size_recved < size)\r
3332         {\r
3333                 // データが足りない (途中で欠落しているかも知れない)\r
3334                 return;\r
3335         }\r
3336 \r
3337         if (IPV4_GET_OFFSET(ip) == 0 && (IPV4_GET_FLAGS(ip) & 0x01) == 0)\r
3338         {\r
3339                 // このパケットは分割されていないので直ちに上位層に渡すことができる\r
3340                 IpReceived(v, ip->SrcIP, ip->DstIP, ip->Protocol, data, size, packet->BroadcastPacket);\r
3341         }\r
3342         else\r
3343         {\r
3344                 // このパケットは分割されているので結合する必要がある\r
3345 \r
3346                 UINT offset = IPV4_GET_OFFSET(ip) * 8;\r
3347                 IP_COMBINE *c = SearchIpCombine(v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol);\r
3348 \r
3349                 last_packet = ((IPV4_GET_FLAGS(ip) & 0x01) == 0 ? true : false);\r
3350 \r
3351                 if (c != NULL)\r
3352                 {\r
3353                         // 2 個目移行のパケットである\r
3354                         CombineIp(v, c, offset, data, size, last_packet);\r
3355                 }\r
3356                 else\r
3357                 {\r
3358                         // 最初のパケットなので結合オブジェクトを作成する\r
3359                         c = InsertIpCombine(\r
3360                                 v, ip->SrcIP, ip->DstIP, Endian16(ip->Identification), ip->Protocol, packet->BroadcastPacket);\r
3361                         if (c != NULL)\r
3362                         {\r
3363                                 CombineIp(v, c, offset, data, size, last_packet);\r
3364                         }\r
3365                 }\r
3366         }\r
3367 }\r
3368 \r
3369 // 待機している IP パケットのうち指定した IP アドレスからのものを送信する\r
3370 void SendWaitingIp(VH *v, UCHAR *mac, UINT dest_ip)\r
3371 {\r
3372         UINT i;\r
3373         LIST *o = NULL;\r
3374         // 引数チェック\r
3375         if (v == NULL || mac == NULL)\r
3376         {\r
3377                 return;\r
3378         }\r
3379 \r
3380         // 対象リストを取得する\r
3381         for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)\r
3382         {\r
3383                 IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);\r
3384 \r
3385                 if (w->DestIP == dest_ip)\r
3386                 {\r
3387                         if (o == NULL)\r
3388                         {\r
3389                                 o = NewListFast(NULL);\r
3390                         }\r
3391                         Add(o, w);\r
3392                 }\r
3393         }\r
3394 \r
3395         // 対象となったパケットを一気に送信する\r
3396         if (o != NULL)\r
3397         {\r
3398                 for (i = 0;i < LIST_NUM(o);i++)\r
3399                 {\r
3400                         IP_WAIT *w = LIST_DATA(o, i);\r
3401 \r
3402                         // 送信処理\r
3403                         VirtualIpSend(v, mac, w->Data, w->Size);\r
3404 \r
3405                         // リストから削除\r
3406                         Delete(v->IpWaitTable, w);\r
3407 \r
3408                         // メモリ解放\r
3409                         Free(w->Data);\r
3410                         Free(w);\r
3411                 }\r
3412 \r
3413                 ReleaseList(o);\r
3414         }\r
3415 }\r
3416 \r
3417 // 古い IP 待ちテーブルを削除する\r
3418 void DeleteOldIpWaitTable(VH *v)\r
3419 {\r
3420         UINT i;\r
3421         LIST *o = NULL;\r
3422         // 引数チェック\r
3423         if (v == NULL)\r
3424         {\r
3425                 return;\r
3426         }\r
3427 \r
3428         // 削除対象リストを取得する\r
3429         for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)\r
3430         {\r
3431                 IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);\r
3432 \r
3433                 if (w->Expire < v->Now)\r
3434                 {\r
3435                         if (o == NULL)\r
3436                         {\r
3437                                 o = NewListFast(NULL);\r
3438                         }\r
3439                         Add(o, w);\r
3440                 }\r
3441         }\r
3442 \r
3443         // 一気に削除する\r
3444         if (o != NULL)\r
3445         {\r
3446                 for (i = 0;i < LIST_NUM(o);i++)\r
3447                 {\r
3448                         IP_WAIT *w = LIST_DATA(o, i);\r
3449 \r
3450                         // リストから削除\r
3451                         Delete(v->IpWaitTable, w);\r
3452 \r
3453                         // メモリ解放\r
3454                         Free(w->Data);\r
3455                         Free(w);\r
3456                 }\r
3457                 ReleaseList(o);\r
3458         }\r
3459 }\r
3460 \r
3461 // IP 待ちテーブルのポーリング\r
3462 void PollingIpWaitTable(VH *v)\r
3463 {\r
3464         // 古いテーブルの削除\r
3465         DeleteOldIpWaitTable(v);\r
3466 }\r
3467 \r
3468 // IP 待ちテーブルに IP パケットを挿入する\r
3469 void InsertIpWaitTable(VH *v, UINT dest_ip, UINT src_ip, void *data, UINT size)\r
3470 {\r
3471         IP_WAIT *w;\r
3472         // 引数チェック\r
3473         if (v == NULL || data == NULL || size == 0)\r
3474         {\r
3475                 return;\r
3476         }\r
3477 \r
3478         w = ZeroMalloc(sizeof(IP_WAIT));\r
3479         w->Data = data;\r
3480         w->Size = size;\r
3481         w->SrcIP = src_ip;\r
3482         w->DestIP = dest_ip;\r
3483         w->Expire = v->Now + (UINT64)IP_WAIT_FOR_ARP_TIMEOUT;\r
3484 \r
3485         Add(v->IpWaitTable, w);\r
3486 }\r
3487 \r
3488 // IP 待ちテーブルを初期化する\r
3489 void InitIpWaitTable(VH *v)\r
3490 {\r
3491         // 引数チェック\r
3492         if (v == NULL)\r
3493         {\r
3494                 return;\r
3495         }\r
3496 \r
3497         v->IpWaitTable = NewList(NULL);\r
3498 }\r
3499 \r
3500 // IP 待ちテーブルを解放する\r
3501 void FreeIpWaitTable(VH *v)\r
3502 {\r
3503         UINT i;\r
3504         // 引数チェック\r
3505         if (v == NULL)\r
3506         {\r
3507                 return;\r
3508         }\r
3509 \r
3510         for (i = 0;i < LIST_NUM(v->IpWaitTable);i++)\r
3511         {\r
3512                 IP_WAIT *w = LIST_DATA(v->IpWaitTable, i);\r
3513 \r
3514                 Free(w->Data);\r
3515                 Free(w);\r
3516         }\r
3517 \r
3518         ReleaseList(v->IpWaitTable);\r
3519 }\r
3520 \r
3521 // ARP Response が到着するなどして IP アドレスに対する MAC アドレスが判明した\r
3522 void ArpIpWasKnown(VH *v, UINT ip, UCHAR *mac)\r
3523 {\r
3524         // 引数チェック\r
3525         if (v == NULL || mac == NULL)\r
3526         {\r
3527                 return;\r
3528         }\r
3529 \r
3530         // ARP 待ち行列にこの IP アドレスに対する問い合わせがあった場合は削除する\r
3531         DeleteArpWaitTable(v, ip);\r
3532 \r
3533         // ARP テーブルに登録または更新する\r
3534         InsertArpTable(v, mac, ip);\r
3535 \r
3536         // IP 待機リストで待機している IP パケットを送信する\r
3537         SendWaitingIp(v, mac, ip);\r
3538 }\r
3539 \r
3540 // ARP 待ちリストをチェックし適時 ARP を再発行する\r
3541 void PollingArpWaitTable(VH *v)\r
3542 {\r
3543         UINT i;\r
3544         LIST *o;\r
3545         // 引数チェック\r
3546         if (v == NULL)\r
3547         {\r
3548                 return;\r
3549         }\r
3550 \r
3551         // 削除リストの初期化\r
3552         o = NULL;\r
3553 \r
3554         // すべての ARP 待ちリストを走査\r
3555         for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)\r
3556         {\r
3557                 ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);\r
3558 \r
3559                 if (w->GiveupTime < v->Now || (w->GiveupTime - 100 * 1000) > v->Now)\r
3560                 {\r
3561                         // ARP の送信を諦める\r
3562                         if (o == NULL)\r
3563                         {\r
3564                                 o = NewListFast(NULL);\r
3565                         }\r
3566                         Add(o, w);\r
3567                 }\r
3568                 else\r
3569                 {\r
3570                         if (w->TimeoutTime < v->Now)\r
3571                         {\r
3572                                 // ARP を再度送信する\r
3573                                 VirtualArpSendRequest(v, w->IpAddress);\r
3574 \r
3575                                 // 次のタイムアウト時刻をセット\r
3576                                 w->TimeoutTime = v->Now + (UINT64)w->NextTimeoutTimeValue;\r
3577                                 // 2 回目以降の ARP 送信間隔は増やしていく\r
3578                                 w->NextTimeoutTimeValue = w->NextTimeoutTimeValue + ARP_REQUEST_TIMEOUT;\r
3579                         }\r
3580                 }\r
3581         }\r
3582 \r
3583         // 削除対象の ARP 待ちレコードがある場合は削除する\r
3584         if (o != NULL)\r
3585         {\r
3586                 for (i = 0;i < LIST_NUM(o);i++)\r
3587                 {\r
3588                         ARP_WAIT *w = LIST_DATA(o, i);\r
3589 \r
3590                         DeleteArpWaitTable(v, w->IpAddress);\r
3591                 }\r
3592                 ReleaseList(o);\r
3593         }\r
3594 }\r
3595 \r
3596 // ARP を発行する\r
3597 void SendArp(VH *v, UINT ip)\r
3598 {\r
3599         ARP_WAIT *w;\r
3600         // 引数チェック\r
3601         if (v == NULL)\r
3602         {\r
3603                 return;\r
3604         }\r
3605 \r
3606         // まず ARP 待ちリストに宛先 IP アドレスが登録されているかどうか調べる\r
3607         w = SearchArpWaitTable(v, ip);\r
3608         if (w != NULL)\r
3609         {\r
3610                 // すでに登録されているので何もしない\r
3611                 return;\r
3612         }\r
3613 \r
3614         // まず ARP パケットを送信する\r
3615         VirtualArpSendRequest(v, ip);\r
3616 \r
3617         // ARP 待ちリストに登録する\r
3618         w = ZeroMalloc(sizeof(ARP_WAIT));\r
3619         w->GiveupTime = v->Now + (UINT64)ARP_REQUEST_GIVEUP;\r
3620         w->TimeoutTime = v->Now + (UINT64)ARP_REQUEST_TIMEOUT;\r
3621         w->NextTimeoutTimeValue = ARP_REQUEST_TIMEOUT;\r
3622         w->IpAddress = ip;\r
3623 \r
3624         InsertArpWaitTable(v, w);\r
3625 }\r
3626 \r
3627 // ARP 待ちテーブルの削除\r
3628 void DeleteArpWaitTable(VH *v, UINT ip)\r
3629 {\r
3630         ARP_WAIT *w;\r
3631         // 引数チェック\r
3632         if (v == NULL)\r
3633         {\r
3634                 return;\r
3635         }\r
3636 \r
3637         w = SearchArpWaitTable(v, ip);\r
3638         if (w == NULL)\r
3639         {\r
3640                 return;\r
3641         }\r
3642         Delete(v->ArpWaitTable, w);\r
3643 \r
3644         Free(w);\r
3645 }\r
3646 \r
3647 // ARP 待ちテーブルの検索\r
3648 ARP_WAIT *SearchArpWaitTable(VH *v, UINT ip)\r
3649 {\r
3650         ARP_WAIT *w, t;\r
3651         // 引数チェック\r
3652         if (v == NULL)\r
3653         {\r
3654                 return NULL;\r
3655         }\r
3656 \r
3657         t.IpAddress = ip;\r
3658         w = Search(v->ArpWaitTable, &t);\r
3659 \r
3660         return w;\r
3661 }\r
3662 \r
3663 // ARP 待ちテーブルに登録\r
3664 void InsertArpWaitTable(VH *v, ARP_WAIT *w)\r
3665 {\r
3666         // 引数チェック\r
3667         if (v == NULL || w == NULL)\r
3668         {\r
3669                 return;\r
3670         }\r
3671 \r
3672         Add(v->ArpWaitTable, w);\r
3673 }\r
3674 \r
3675 // ARP 待ちテーブルの初期化\r
3676 void InitArpWaitTable(VH *v)\r
3677 {\r
3678         // 引数チェック\r
3679         if (v == NULL)\r
3680         {\r
3681                 return;\r
3682         }\r
3683 \r
3684         v->ArpWaitTable = NewList(CompareArpWaitTable);\r
3685 }\r
3686 \r
3687 // ARP 待ちテーブルの解放\r
3688 void FreeArpWaitTable(VH *v)\r
3689 {\r
3690         UINT i;\r
3691         // 引数チェック\r
3692         if (v == NULL)\r
3693         {\r
3694                 return;\r
3695         }\r
3696 \r
3697         for (i = 0;i < LIST_NUM(v->ArpWaitTable);i++)\r
3698         {\r
3699                 ARP_WAIT *w = LIST_DATA(v->ArpWaitTable, i);\r
3700 \r
3701                 Free(w);\r
3702         }\r
3703 \r
3704         ReleaseList(v->ArpWaitTable);\r
3705 }\r
3706 \r
3707 // MAC アドレスが不正かどうかチェック\r
3708 bool IsMacInvalid(UCHAR *mac)\r
3709 {\r
3710         UINT i;\r
3711         // 引数チェック\r
3712         if (mac == NULL)\r
3713         {\r
3714                 return false;\r
3715         }\r
3716 \r
3717         for (i = 0;i < 6;i++)\r
3718         {\r
3719                 if (mac[i] != 0x00)\r
3720                 {\r
3721                         return false;\r
3722                 }\r
3723         }\r
3724         return true;\r
3725 }\r
3726 \r
3727 // MAC アドレスがブロードキャストアドレスかどうかチェック\r
3728 bool IsMacBroadcast(UCHAR *mac)\r
3729 {\r
3730         UINT i;\r
3731         // 引数チェック\r
3732         if (mac == NULL)\r
3733         {\r
3734                 return false;\r
3735         }\r
3736 \r
3737         for (i = 0;i < 6;i++)\r
3738         {\r
3739                 if (mac[i] != 0xff)\r
3740                 {\r
3741                         return false;\r
3742                 }\r
3743         }\r
3744         return true;\r
3745 }\r
3746 \r
3747 // ARP テーブルにエントリを挿入する\r
3748 void InsertArpTable(VH *v, UCHAR *mac, UINT ip)\r
3749 {\r
3750         ARP_ENTRY *e, t;\r
3751         // 引数チェック\r
3752         if (v == NULL || mac == NULL || ip == 0 || ip == 0xffffffff || IsMacBroadcast(mac) || IsMacInvalid(mac))\r
3753         {\r
3754                 return;\r
3755         }\r
3756 \r
3757         // すでに同じ IP アドレスが登録されていないかどうかチェック\r
3758         t.IpAddress = ip;\r
3759         e = Search(v->ArpTable, &t);\r
3760         if (e != NULL)\r
3761         {\r
3762                 // 登録されていたのでこれを上書きするだけ\r
3763                 if (Cmp(e->MacAddress, mac, 6) != 0)\r
3764                 {\r
3765                         e->Created = v->Now;\r
3766                         Copy(e->MacAddress, mac, 6);\r
3767                 }\r
3768                 e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;\r
3769         }\r
3770         else\r
3771         {\r
3772                 // 新しくエントリを作成する\r
3773                 e = ZeroMalloc(sizeof(ARP_ENTRY));\r
3774 \r
3775                 e->Created = v->Now;\r
3776                 e->Expire = v->Now + (UINT64)ARP_ENTRY_EXPIRES;\r
3777                 Copy(e->MacAddress, mac, 6);\r
3778                 e->IpAddress = ip;\r
3779 \r
3780                 Add(v->ArpTable, e);\r
3781         }\r
3782 }\r
3783 \r
3784 // ARP テーブルのポーリング\r
3785 void PollingArpTable(VH *v)\r
3786 {\r
3787         // 引数チェック\r
3788         if (v == NULL)\r
3789         {\r
3790                 return;\r
3791         }\r
3792 \r
3793         if (v->Now > v->NextArpTablePolling)\r
3794         {\r
3795                 v->NextArpTablePolling = v->Now + (UINT64)ARP_ENTRY_POLLING_TIME;\r
3796                 RefreshArpTable(v);\r
3797         }\r
3798 }\r
3799 \r
3800 // 古い ARP エントリを削除する\r
3801 void RefreshArpTable(VH *v)\r
3802 {\r
3803         UINT i;\r
3804         LIST *o;\r
3805         // 引数チェック\r
3806         if (v == NULL)\r
3807         {\r
3808                 return;\r
3809         }\r
3810 \r
3811         o = NewListFast(NULL);\r
3812         for (i = 0;i < LIST_NUM(v->ArpTable);i++)\r
3813         {\r
3814                 ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);\r
3815 \r
3816                 // 有効期限が切れたものを調べる\r
3817                 if (e->Expire < v->Now)\r
3818                 {\r
3819                         // 有効期限が切れている\r
3820                         Add(o, e);\r
3821                 }\r
3822         }\r
3823 \r
3824         // 有効期限が切れているものを一括して削除する\r
3825         for (i = 0;i < LIST_NUM(o);i++)\r
3826         {\r
3827                 ARP_ENTRY *e = LIST_DATA(o, i);\r
3828 \r
3829                 Delete(v->ArpTable, e);\r
3830                 Free(e);\r
3831         }\r
3832 \r
3833         ReleaseList(o);\r
3834 }\r
3835 \r
3836 // ARP テーブルの検索\r
3837 ARP_ENTRY *SearchArpTable(VH *v, UINT ip)\r
3838 {\r
3839         ARP_ENTRY *e, t;\r
3840         // 引数チェック\r
3841         if (v == NULL)\r
3842         {\r
3843                 return NULL;\r
3844         }\r
3845 \r
3846         t.IpAddress = ip;\r
3847         e = Search(v->ArpTable, &t);\r
3848 \r
3849         return e;\r
3850 }\r
3851 \r
3852 // ARP テーブルの初期化\r
3853 void InitArpTable(VH *v)\r
3854 {\r
3855         // 引数チェック\r
3856         if (v == NULL)\r
3857         {\r
3858                 return;\r
3859         }\r
3860 \r
3861         v->ArpTable = NewList(CompareArpTable);\r
3862 }\r
3863 \r
3864 // ARP テーブルの解放\r
3865 void FreeArpTable(VH *v)\r
3866 {\r
3867         UINT i;\r
3868         // 引数チェック\r
3869         if (v == NULL)\r
3870         {\r
3871                 return;\r
3872         }\r
3873 \r
3874         // すべてのエントリを削除する\r
3875         for (i = 0;i < LIST_NUM(v->ArpTable);i++)\r
3876         {\r
3877                 ARP_ENTRY *e = LIST_DATA(v->ArpTable, i);\r
3878                 Free(e);\r
3879         }\r
3880         ReleaseList(v->ArpTable);\r
3881 }\r
3882 \r
3883 // ARP 待ちテーブルの比較\r
3884 int CompareArpWaitTable(void *p1, void *p2)\r
3885 {\r
3886         ARP_WAIT *e1, *e2;\r
3887         if (p1 == NULL || p2 == NULL)\r
3888         {\r
3889                 return 0;\r
3890         }\r
3891         e1 = *(ARP_WAIT **)p1;\r
3892         e2 = *(ARP_WAIT **)p2;\r
3893         if (e1 == NULL || e2 == NULL)\r
3894         {\r
3895                 return 0;\r
3896         }\r
3897 \r
3898         if (e1->IpAddress > e2->IpAddress)\r
3899         {\r
3900                 return 1;\r
3901         }\r
3902         else if (e1->IpAddress < e2->IpAddress)\r
3903         {\r
3904                 return -1;\r
3905         }\r
3906         return 0;\r
3907 }\r
3908 \r
3909 // ARP テーブルの比較\r
3910 int CompareArpTable(void *p1, void *p2)\r
3911 {\r
3912         ARP_ENTRY *e1, *e2;\r
3913         if (p1 == NULL || p2 == NULL)\r
3914         {\r
3915                 return 0;\r
3916         }\r
3917         e1 = *(ARP_ENTRY **)p1;\r
3918         e2 = *(ARP_ENTRY **)p2;\r
3919         if (e1 == NULL || e2 == NULL)\r
3920         {\r
3921                 return 0;\r
3922         }\r
3923 \r
3924         if (e1->IpAddress > e2->IpAddress)\r
3925         {\r
3926                 return 1;\r
3927         }\r
3928         else if (e1->IpAddress < e2->IpAddress)\r
3929         {\r
3930                 return -1;\r
3931         }\r
3932         return 0;\r
3933 }\r
3934 \r
3935 // 仮想ホストの初期化\r
3936 bool VirtualInit(VH *v)\r
3937 {\r
3938         // ログ初期化\r
3939         v->Logger = NULL;\r
3940 \r
3941         LockVirtual(v);\r
3942         {\r
3943                 // 初期化\r
3944                 v->Cancel = NewCancel();\r
3945                 v->SendQueue = NewQueue();\r
3946         }\r
3947         UnlockVirtual(v);\r
3948 \r
3949         // カウンタリセット\r
3950         v->Counter->c = 0;\r
3951         v->DhcpId = 0;\r
3952 \r
3953         // ARP テーブルの初期化\r
3954         InitArpTable(v);\r
3955 \r
3956         // ARP 待ちテーブルの初期化\r
3957         InitArpWaitTable(v);\r
3958 \r
3959         // IP 待ちテーブルの初期化\r
3960         InitIpWaitTable(v);\r
3961 \r
3962         // IP 結合リストの初期化\r
3963         InitIpCombineList(v);\r
3964 \r
3965         // NAT の初期化\r
3966         InitNat(v);\r
3967 \r
3968         // DHCP サーバーの初期化\r
3969         InitDhcpServer(v);\r
3970 \r
3971         // その他初期化\r
3972         v->flag1 = false;\r
3973         v->NextArpTablePolling = Tick64() + (UINT64)ARP_ENTRY_POLLING_TIME;\r
3974         v->CurrentIpQuota = 0;\r
3975         v->Active = true;\r
3976 \r
3977         return true;\r
3978 }\r
3979 bool VirtualPaInit(SESSION *s)\r
3980 {\r
3981         VH *v;\r
3982         // 引数チェック\r
3983         if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)\r
3984         {\r
3985                 return false;\r
3986         }\r
3987 \r
3988         return VirtualInit(v);\r
3989 }\r
3990 \r
3991 // 仮想ホストのキャンセルオブジェクトを取得\r
3992 CANCEL *VirtualPaGetCancel(SESSION *s)\r
3993 {\r
3994         VH *v;\r
3995         // 引数チェック\r
3996         if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)\r
3997         {\r
3998                 return NULL;\r
3999         }\r
4000 \r
4001         AddRef(v->Cancel->ref);\r
4002         return v->Cancel;\r
4003 }\r
4004 \r
4005 // 仮想ホストから次のパケットを取得\r
4006 UINT VirtualGetNextPacket(VH *v, void **data)\r
4007 {\r
4008         UINT ret = 0;\r
4009 \r
4010 START:\r
4011         // 送信キューを調べる\r
4012         LockQueue(v->SendQueue);\r
4013         {\r
4014                 BLOCK *block = GetNext(v->SendQueue);\r
4015 \r
4016                 if (block != NULL)\r
4017                 {\r
4018                         // パケットがあった\r
4019                         ret = block->Size;\r
4020                         *data = block->Buf;\r
4021                         // 構造体は破棄する\r
4022                         Free(block);\r
4023                 }\r
4024         }\r
4025         UnlockQueue(v->SendQueue);\r
4026 \r
4027         if (ret == 0)\r
4028         {\r
4029                 LockVirtual(v);\r
4030                 {\r
4031                         v->Now = Tick64();\r
4032                         // ポーリング処理\r
4033                         VirtualPolling(v);\r
4034                 }\r
4035                 UnlockVirtual(v);\r
4036                 if (v->SendQueue->num_item != 0)\r
4037                 {\r
4038                         goto START;\r
4039                 }\r
4040         }\r
4041 \r
4042         return ret;\r
4043 }\r
4044 UINT VirtualPaGetNextPacket(SESSION *s, void **data)\r
4045 {\r
4046         VH *v;\r
4047         // 引数チェック\r
4048         if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)\r
4049         {\r
4050                 return INFINITE;\r
4051         }\r
4052 \r
4053         return VirtualGetNextPacket(v, data);\r
4054 }\r
4055 \r
4056 // ポーリング処理 (SessionMain ループ中に 1 回必ず呼ばれる)\r
4057 void VirtualPolling(VH *v)\r
4058 {\r
4059         // 引数チェック\r
4060         if (v == NULL)\r
4061         {\r
4062                 return;\r
4063         }\r
4064 \r
4065         // DHCP ポーリング\r
4066         PollingDhcpServer(v);\r
4067 \r
4068         // NAT ポーリング\r
4069         PoolingNat(v);\r
4070 \r
4071         // 古い ARP テーブルの清掃\r
4072         PollingArpTable(v);\r
4073 \r
4074         // ARP 待ちリストのポーリング\r
4075         PollingArpWaitTable(v);\r
4076 \r
4077         // IP 待ちリストのポーリング\r
4078         PollingIpWaitTable(v);\r
4079 \r
4080         // IP 結合リストのポーリング\r
4081         PollingIpCombine(v);\r
4082 \r
4083         // ビーコン送信プロシージャ\r
4084         PollingBeacon(v);\r
4085 }\r
4086 \r
4087 // ビーコン送信プロシージャ\r
4088 void PollingBeacon(VH *v)\r
4089 {\r
4090         // 引数チェック\r
4091         if (v == NULL)\r
4092         {\r
4093                 return;\r
4094         }\r
4095 \r
4096         if (v->LastSendBeacon == 0 ||\r
4097                 ((v->LastSendBeacon + BEACON_SEND_INTERVAL) <= Tick64()))\r
4098         {\r
4099                 v->LastSendBeacon = Tick64();\r
4100 \r
4101                 SendBeacon(v);\r
4102         }\r
4103 }\r
4104 \r
4105 // Layer-2 パケットを送信する\r
4106 void VirtualLayer2Send(VH *v, UCHAR *dest_mac, UCHAR *src_mac, USHORT protocol, void *data, UINT size)\r
4107 {\r
4108         MAC_HEADER *mac_header;\r
4109         UCHAR *buf;\r
4110         BLOCK *block;\r
4111         // 引数チェック\r
4112         if (v == NULL || dest_mac == NULL || src_mac == NULL || data == NULL || size > (MAX_PACKET_SIZE - sizeof(MAC_HEADER)))\r
4113         {\r
4114                 return;\r
4115         }\r
4116 \r
4117         // バッファ生成\r
4118         buf = Malloc(MAC_HEADER_SIZE + size);\r
4119 \r
4120         // MAC ヘッダ\r
4121         mac_header = (MAC_HEADER *)&buf[0];\r
4122         Copy(mac_header->DestAddress, dest_mac, 6);\r
4123         Copy(mac_header->SrcAddress, src_mac, 6);\r
4124         mac_header->Protocol = Endian16(protocol);\r
4125 \r
4126         // データのコピー\r
4127         Copy(&buf[sizeof(MAC_HEADER)], data, size);\r
4128 \r
4129         // サイズ\r
4130         size += sizeof(MAC_HEADER);\r
4131 \r
4132         // パケット生成\r
4133         block = NewBlock(buf, size, 0);\r
4134 \r
4135         // キューに挿入する\r
4136         LockQueue(v->SendQueue);\r
4137         {\r
4138                 InsertQueue(v->SendQueue, block);\r
4139         }\r
4140         UnlockQueue(v->SendQueue);\r
4141 \r
4142         // キャンセル\r
4143         Cancel(v->Cancel);\r
4144 }\r
4145 \r
4146 // IP パケットを送信する (自動的に分割処理を行う)\r
4147 void SendIp(VH *v, UINT dest_ip, UINT src_ip, UCHAR protocol, void *data, UINT size)\r
4148 {\r
4149         UINT mss;\r
4150         UCHAR *buf;\r
4151         USHORT offset;\r
4152         USHORT id;\r
4153         USHORT total_size;\r
4154         UINT size_of_this_packet;\r
4155         // 引数チェック\r
4156         if (v == NULL || data == NULL || size == 0 || size > MAX_IP_DATA_SIZE_TOTAL)\r
4157         {\r
4158                 return;\r
4159         }\r
4160 \r
4161         // 最大セグメントサイズ\r
4162         mss = v->IpMss;\r
4163 \r
4164         // バッファ\r
4165         buf = (UCHAR *)data;\r
4166 \r
4167         // ID\r
4168         id = (v->NextId++);\r
4169 \r
4170         // 合計サイズ\r
4171         total_size = (USHORT)size;\r
4172 \r
4173         // 分割作業を開始\r
4174         offset = 0;\r
4175 \r
4176         while (true)\r
4177         {\r
4178                 bool last_packet = false;\r
4179                 // このパケットのサイズを取得\r
4180                 size_of_this_packet = MIN((USHORT)mss, (total_size - offset));\r
4181                 if ((offset + (USHORT)size_of_this_packet) == total_size)\r
4182                 {\r
4183                         last_packet = true;\r
4184                 }\r
4185 \r
4186                 // 分割されたパケットの送信処理\r
4187                 SendFragmentedIp(v, dest_ip, src_ip, id,\r
4188                         total_size, offset, protocol, buf + offset, size_of_this_packet, NULL);\r
4189                 if (last_packet)\r
4190                 {\r
4191                         break;\r
4192                 }\r
4193 \r
4194                 offset += (USHORT)size_of_this_packet;\r
4195         }\r
4196 }\r
4197 \r
4198 // 分割済みの IP パケットを送信予約する\r
4199 void SendFragmentedIp(VH *v, UINT dest_ip, UINT src_ip, USHORT id, USHORT total_size, USHORT offset, UCHAR protocol, void *data, UINT size, UCHAR *dest_mac)\r
4200 {\r
4201         UCHAR *buf;\r
4202         IPV4_HEADER *ip;\r
4203         ARP_ENTRY *arp;\r
4204         // 引数チェック\r
4205         if (v == NULL || data == NULL || size == 0)\r
4206         {\r
4207                 return;\r
4208         }\r
4209 \r
4210         // メモリ確保\r
4211         buf = Malloc(size + IP_HEADER_SIZE);\r
4212         ip = (IPV4_HEADER *)&buf[0];\r
4213 \r
4214         // IP ヘッダ構築\r
4215         ip->VersionAndHeaderLength = 0;\r
4216         IPV4_SET_VERSION(ip, 4);\r
4217         IPV4_SET_HEADER_LEN(ip, (IP_HEADER_SIZE / 4));\r
4218         ip->TypeOfService = DEFAULT_IP_TOS;\r
4219         ip->TotalLength = Endian16((USHORT)(size + IP_HEADER_SIZE));\r
4220         ip->Identification = Endian16(id);\r
4221         ip->FlagsAndFlagmentOffset[0] = ip->FlagsAndFlagmentOffset[1] = 0;\r
4222         IPV4_SET_OFFSET(ip, (offset / 8));\r
4223         if ((offset + size) >= total_size)\r
4224         {\r
4225                 IPV4_SET_FLAGS(ip, 0x00);\r
4226         }\r
4227         else\r
4228         {\r
4229                 IPV4_SET_FLAGS(ip, 0x01);\r
4230         }\r
4231         ip->TimeToLive = DEFAULT_IP_TTL;\r
4232         ip->Protocol = protocol;\r
4233         ip->Checksum = 0;\r
4234         ip->SrcIP = src_ip;\r
4235         ip->DstIP = dest_ip;\r
4236 \r
4237         // チェックサム計算\r
4238         ip->Checksum = IpChecksum(ip, IP_HEADER_SIZE);\r
4239 \r
4240         // データコピー\r
4241         Copy(buf + IP_HEADER_SIZE, data, size);\r
4242 \r
4243         if (dest_mac == NULL)\r
4244         {\r
4245                 if (ip->DstIP == 0xffffffff ||\r
4246                         (IsInNetwork(ip->DstIP, v->HostIP, v->HostMask) && (ip->DstIP & (~v->HostMask)) == (~v->HostMask)))\r
4247                 {\r
4248                         // ブロードキャストアドレス\r
4249                         dest_mac = broadcast;\r
4250                 }\r
4251                 else\r
4252                 {\r
4253                         // 宛先 MAC アドレスが不明な場合は ARP 問い合わせ\r
4254                         arp = SearchArpTable(v, dest_ip);\r
4255                         if (arp != NULL)\r
4256                         {\r
4257                                 dest_mac = arp->MacAddress;\r
4258                         }\r
4259                 }\r
4260         }\r
4261         if (dest_mac != NULL)\r
4262         {\r
4263                 // 直ちにパケットを送信する\r
4264                 VirtualIpSend(v, dest_mac, buf, size + IP_HEADER_SIZE);\r
4265 \r
4266                 // パケットデータは解放して良い\r
4267                 Free(buf);\r
4268         }\r
4269         else\r
4270         {\r
4271                 // このパケットはまだ転送できないので IP 待ちテーブルに追加する\r
4272                 InsertIpWaitTable(v, dest_ip, src_ip, buf, size + IP_HEADER_SIZE);\r
4273 \r
4274                 // ARP を発行する\r
4275                 SendArp(v, dest_ip);\r
4276         }\r
4277 }\r
4278 \r
4279 // IP パケット (分割済み) を送信する\r
4280 void VirtualIpSend(VH *v, UCHAR *dest_mac, void *data, UINT size)\r
4281 {\r
4282         // 引数チェック\r
4283         if (v == NULL || dest_mac == NULL || data == NULL || size == 0)\r
4284         {\r
4285                 return;\r
4286         }\r
4287 \r
4288         // 送信\r
4289         VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_IPV4, data, size);\r
4290 }\r
4291 \r
4292 // ARP リクエストパケットを送信する\r
4293 void VirtualArpSendRequest(VH *v, UINT dest_ip)\r
4294 {\r
4295         ARPV4_HEADER arp;\r
4296         // 引数チェック\r
4297         if (v == NULL)\r
4298         {\r
4299                 return;\r
4300         }\r
4301 \r
4302         // ARP ヘッダを構築\r
4303         arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);\r
4304         arp.ProtocolType = Endian16(MAC_PROTO_IPV4);\r
4305         arp.HardwareSize = 6;\r
4306         arp.ProtocolSize = 4;\r
4307         arp.Operation = Endian16(ARP_OPERATION_REQUEST);\r
4308         Copy(arp.SrcAddress, v->MacAddress, 6);\r
4309         arp.SrcIP = v->HostIP;\r
4310         Zero(&arp.TargetAddress, 6);\r
4311         arp.TargetIP = dest_ip;\r
4312 \r
4313         // 送信\r
4314         VirtualLayer2Send(v, broadcast, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(arp));\r
4315 }\r
4316 \r
4317 // ARP レスポンスパケットを送信する\r
4318 void VirtualArpSendResponse(VH *v, UCHAR *dest_mac, UINT dest_ip, UINT src_ip)\r
4319 {\r
4320         ARPV4_HEADER arp;\r
4321         // 引数チェック\r
4322         if (v == NULL || dest_mac == NULL)\r
4323         {\r
4324                 return;\r
4325         }\r
4326 \r
4327         // ARP ヘッダを構築\r
4328         arp.HardwareType = Endian16(ARP_HARDWARE_TYPE_ETHERNET);\r
4329         arp.ProtocolType = Endian16(MAC_PROTO_IPV4);\r
4330         arp.HardwareSize = 6;\r
4331         arp.ProtocolSize = 4;\r
4332         arp.Operation = Endian16(ARP_OPERATION_RESPONSE);\r
4333         Copy(arp.SrcAddress, v->MacAddress, 6);\r
4334         Copy(arp.TargetAddress, dest_mac, 6);\r
4335         arp.SrcIP = src_ip;\r
4336         arp.TargetIP = dest_ip;\r
4337 \r
4338         // 送信\r
4339         VirtualLayer2Send(v, dest_mac, v->MacAddress, MAC_PROTO_ARPV4, &arp, sizeof(ARPV4_HEADER));\r
4340 }\r
4341 \r
4342 // ARP リクエストパケットを受信した\r
4343 void VirtualArpResponseRequest(VH *v, PKT *packet)\r
4344 {\r
4345         ARPV4_HEADER *arp;\r
4346         // 引数チェック\r
4347         if (v == NULL || packet == NULL)\r
4348         {\r
4349                 return;\r
4350         }\r
4351 \r
4352         arp = packet->L3.ARPv4Header;\r
4353 \r
4354         // 相手のホスト IP アドレスと MAC アドレスを既知の情報とする\r
4355         ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);\r
4356 \r
4357         // 自分のホスト IP アドレスと一致するかどうか検索\r
4358         if (v->HostIP == arp->TargetIP)\r
4359         {\r
4360                 // 一致したので応答する\r
4361                 VirtualArpSendResponse(v, arp->SrcAddress, arp->SrcIP, v->HostIP);\r
4362                 return;\r
4363         }\r
4364         // 一致しない場合は何もしない\r
4365 }\r
4366 \r
4367 // ARP レスポンスパケットを受信した\r
4368 void VirtualArpResponseReceived(VH *v, PKT *packet)\r
4369 {\r
4370         ARPV4_HEADER *arp;\r
4371         // 引数チェック\r
4372         if (v == NULL || packet == NULL)\r
4373         {\r
4374                 return;\r
4375         }\r
4376 \r
4377         arp = packet->L3.ARPv4Header;\r
4378 \r
4379         // この情報を既知の情報とする\r
4380         ArpIpWasKnown(v, arp->SrcIP, arp->SrcAddress);\r
4381 }\r
4382 \r
4383 // ARP パケットを受信した\r
4384 void VirtualArpReceived(VH *v, PKT *packet)\r
4385 {\r
4386         ARPV4_HEADER *arp;\r
4387         // 引数チェック\r
4388         if (v == NULL || packet == NULL)\r
4389         {\r
4390                 return;\r
4391         }\r
4392 \r
4393         arp = packet->L3.ARPv4Header;\r
4394 \r
4395         if (Endian16(arp->HardwareType) != ARP_HARDWARE_TYPE_ETHERNET)\r
4396         {\r
4397                 // ハードウェア種類が Ethernet 以外の場合は無視\r
4398                 return;\r
4399         }\r
4400         if (Endian16(arp->ProtocolType) != MAC_PROTO_IPV4)\r
4401         {\r
4402                 // プロトコル種類が IPv4 以外の場合は無視\r
4403                 return;\r
4404         }\r
4405         if (arp->HardwareSize != 6 || arp->ProtocolSize != 4)\r
4406         {\r
4407                 // ハードウェアアドレスまたはプロトコルアドレスのサイズが不正なので無視\r
4408                 return;\r
4409         }\r
4410         // 送信元 MAC アドレスをチェック\r
4411         if (Cmp(arp->SrcAddress, packet->MacAddressSrc, 6) != 0)\r
4412         {\r
4413                 // ARP パケットの MAC アドレスと MAC ヘッダの MAC アドレスが異なる\r
4414                 return;\r
4415         }\r
4416 \r
4417         switch (Endian16(arp->Operation))\r
4418         {\r
4419         case ARP_OPERATION_REQUEST:             // ARP リクエスト\r
4420                 VirtualArpResponseRequest(v, packet);\r
4421                 break;\r
4422 \r
4423         case ARP_OPERATION_RESPONSE:    // ARP レスポンス\r
4424                 VirtualArpResponseReceived(v, packet);\r
4425                 break;\r
4426         }\r
4427 }\r
4428 \r
4429 // DHCP サーバーの解放\r
4430 void FreeDhcpServer(VH *v)\r
4431 {\r
4432         UINT i;\r
4433         // 引数チェック\r
4434         if (v == NULL)\r
4435         {\r
4436                 return;\r
4437         }\r
4438 \r
4439         // すべてのリースエントリを削除する\r
4440         for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)\r
4441         {\r
4442                 DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);\r
4443                 FreeDhcpLease(d);\r
4444         }\r
4445 \r
4446         ReleaseList(v->DhcpLeaseList);\r
4447         v->DhcpLeaseList = NULL;\r
4448 }\r
4449 \r
4450 // DHCP サーバーの初期化\r
4451 void InitDhcpServer(VH *v)\r
4452 {\r
4453         // 引数チェック\r
4454         if (v == NULL)\r
4455         {\r
4456                 return;\r
4457         }\r
4458 \r
4459         // リスト作成\r
4460         v->DhcpLeaseList = NewList(CompareDhcpLeaseList);\r
4461 }\r
4462 \r
4463 // DHCP リース項目を IP アドレスで検索\r
4464 DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip)\r
4465 {\r
4466         UINT i;\r
4467         // 引数チェック\r
4468         if (v == NULL)\r
4469         {\r
4470                 return NULL;\r
4471         }\r
4472 \r
4473         for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)\r
4474         {\r
4475                 DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);\r
4476                 if (d->IpAddress == ip)\r
4477                 {\r
4478                         return d;\r
4479                 }\r
4480         }\r
4481 \r
4482         return NULL;\r
4483 }\r
4484 \r
4485 // DHCP リース項目を MAC アドレスで検索\r
4486 DHCP_LEASE *SearchDhcpLeaseByMac(VH *v, UCHAR *mac)\r
4487 {\r
4488         DHCP_LEASE *d, t;\r
4489         // 引数チェック\r
4490         if (v == NULL || mac == NULL)\r
4491         {\r
4492                 return NULL;\r
4493         }\r
4494 \r
4495         Copy(&t.MacAddress, mac, 6);\r
4496         d = Search(v->DhcpLeaseList, &t);\r
4497 \r
4498         return d;\r
4499 }\r
4500 \r
4501 // DHCP リース項目の解放\r
4502 void FreeDhcpLease(DHCP_LEASE *d)\r
4503 {\r
4504         // 引数チェック\r
4505         if (d == NULL)\r
4506         {\r
4507                 return;\r
4508         }\r
4509 \r
4510         Free(d->Hostname);\r
4511         Free(d);\r
4512 }\r
4513 \r
4514 // DHCP リース項目の作成\r
4515 DHCP_LEASE *NewDhcpLease(UINT expire, UCHAR *mac_address, UINT ip, UINT mask, char *hostname)\r
4516 {\r
4517         DHCP_LEASE *d;\r
4518         // 引数チェック\r
4519         if (mac_address == NULL || hostname == NULL)\r
4520         {\r
4521                 return NULL;\r
4522         }\r
4523 \r
4524         d = ZeroMalloc(sizeof(DHCP_LEASE));\r
4525         d->LeasedTime = (UINT64)Tick64();\r
4526         if (expire == INFINITE)\r
4527         {\r
4528                 d->ExpireTime = INFINITE;\r
4529         }\r
4530         else\r
4531         {\r
4532                 d->ExpireTime = d->LeasedTime + (UINT64)expire;\r
4533         }\r
4534         d->IpAddress = ip;\r
4535         d->Mask = mask;\r
4536         d->Hostname = CopyStr(hostname);\r
4537         Copy(d->MacAddress, mac_address, 6);\r
4538 \r
4539 \r
4540         return d;\r
4541 }\r
4542 \r
4543 // DHCP リストの項目の比較\r
4544 int CompareDhcpLeaseList(void *p1, void *p2)\r
4545 {\r
4546         DHCP_LEASE *d1, *d2;\r
4547         // 引数チェック\r
4548         if (p1 == NULL || p2 == NULL)\r
4549         {\r
4550                 return 0;\r
4551         }\r
4552         d1 = *(DHCP_LEASE **)p1;\r
4553         d2 = *(DHCP_LEASE **)p2;\r
4554         if (d1 == NULL || d2 == NULL)\r
4555         {\r
4556                 return 0;\r
4557         }\r
4558 \r
4559         return Cmp(d1->MacAddress, d2->MacAddress, 6);\r
4560 }\r
4561 \r
4562 // DHCP サーバーのポーリング\r
4563 void PollingDhcpServer(VH *v)\r
4564 {\r
4565         UINT i;\r
4566         // 引数チェック\r
4567         if (v == NULL)\r
4568         {\r
4569                 return;\r
4570         }\r
4571 \r
4572         if (v->LastDhcpPolling != 0)\r
4573         {\r
4574                 if ((v->LastDhcpPolling + (UINT64)DHCP_POLLING_INTERVAL) < v->Now ||\r
4575                         v->LastDhcpPolling > v->Now)\r
4576                 {\r
4577                         return;\r
4578                 }\r
4579         }\r
4580         v->LastDhcpPolling = v->Now;\r
4581 \r
4582         // 有効期限の切れたエントリを削除\r
4583 FIRST_LIST:\r
4584         for (i = 0;i < LIST_NUM(v->DhcpLeaseList);i++)\r
4585         {\r
4586                 DHCP_LEASE *d = LIST_DATA(v->DhcpLeaseList, i);\r
4587 \r
4588                 if (d->ExpireTime < v->Now)\r
4589                 {\r
4590                         FreeDhcpLease(d);\r
4591                         Delete(v->DhcpLeaseList, d);\r
4592                         goto FIRST_LIST;\r
4593                 }\r
4594         }\r
4595 }\r
4596 \r
4597 // DHCP REQUEST に対応する\r
4598 UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip)\r
4599 {\r
4600         UINT ret;\r
4601         // 引数チェック\r
4602         if (v == NULL || mac == NULL)\r
4603         {\r
4604                 return 0;\r
4605         }\r
4606 \r
4607         ret = ServeDhcpDiscover(v, mac, request_ip);\r
4608         if (ret != request_ip)\r
4609         {\r
4610                 if (request_ip != 0)\r
4611                 {\r
4612                         // 要求されている IP アドレスを割り当てることができない場合はエラー\r
4613                         return 0;\r
4614                 }\r
4615         }\r
4616 \r
4617         return ret;\r
4618 }\r
4619 \r
4620 // DHCP DISCOVER に対応する\r
4621 UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip)\r
4622 {\r
4623         UINT ret = 0;\r
4624         // 引数チェック\r
4625         if (v == NULL || mac == NULL)\r
4626         {\r
4627                 return 0;\r
4628         }\r
4629 \r
4630         if (request_ip != 0)\r
4631         {\r
4632                 // IP アドレスが指定されている\r
4633                 DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip);\r
4634                 if (d != NULL)\r
4635                 {\r
4636                         // 同じ IP アドレスのエントリがすでに存在している場合は\r
4637                         // 同じ MAC アドレスからの要求であることを調べる\r
4638                         if (Cmp(mac, d->MacAddress, 6) == 0)\r
4639                         {\r
4640                                 // 指定された IP アドレスが割り当て範囲内にあるかどうか調べる\r
4641                                 if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&\r
4642                                         Endian32(request_ip) <= Endian32(v->DhcpIpEnd))\r
4643                                 {\r
4644                                         // 範囲内にあるなら承諾する\r
4645                                         ret = request_ip;\r
4646                                 }\r
4647                         }\r
4648                 }\r
4649                 else\r
4650                 {\r
4651                         // 指定された IP アドレスが割り当て範囲内にあるかどうか調べる\r
4652                         if (Endian32(v->DhcpIpStart) <= Endian32(request_ip) &&\r
4653                                 Endian32(request_ip) <= Endian32(v->DhcpIpEnd))\r
4654                         {\r
4655                                 // 範囲内にあるなら承諾する\r
4656                                 ret = request_ip;\r
4657                         }\r
4658                         else\r
4659                         {\r
4660                                 // 範囲外であるが Discover なので範囲内の IP を 1 つ提案する\r
4661                         }\r
4662                 }\r
4663         }\r
4664 \r
4665         if (ret == 0)\r
4666         {\r
4667                 // すでに登録されているエントリで同じ MAC アドレスのものがあれば\r
4668                 // それを優先して使用する\r
4669                 DHCP_LEASE *d = SearchDhcpLeaseByMac(v, mac);\r
4670                 if (d != NULL)\r
4671                 {\r
4672                         // 見つかった IP アドレスが割り当て範囲内にあるかどうか調べる\r
4673                         if (Endian32(v->DhcpIpStart) <= Endian32(d->IpAddress) &&\r
4674                                 Endian32(d->IpAddress) <= Endian32(v->DhcpIpEnd))\r
4675                         {\r
4676                                 // 範囲内にあるなら見つかったIPアドレスを使用する\r
4677                                 ret = d->IpAddress;\r
4678                         }\r
4679                 }\r
4680         }\r
4681 \r
4682         if (ret == 0)\r
4683         {\r
4684                 // 新しく割り当てることが可能な IP アドレスを適当に取る\r
4685                 ret = GetFreeDhcpIpAddress(v);\r
4686         }\r
4687 \r
4688         return ret;\r
4689 }\r
4690 \r
4691 // 新しく割り当てることが可能な IP アドレスを適当に 1 つ取る\r
4692 UINT GetFreeDhcpIpAddress(VH *v)\r
4693 {\r
4694         UINT ip_start, ip_end;\r
4695         UINT i;\r
4696         // 引数チェック\r
4697         if (v == NULL)\r
4698         {\r
4699                 return 0;\r
4700         }\r
4701 \r
4702         ip_start = Endian32(v->DhcpIpStart);\r
4703         ip_end = Endian32(v->DhcpIpEnd);\r
4704 \r
4705         for (i = ip_start; i <= ip_end;i++)\r
4706         {\r
4707                 UINT ip = Endian32(i);\r
4708                 if (SearchDhcpLeaseByIp(v, ip) == NULL)\r
4709                 {\r
4710                         // 空き IP アドレスを発見した\r
4711                         return ip;\r
4712                 }\r
4713         }\r
4714 \r
4715         // 空きが無い\r
4716         return 0;\r
4717 }\r
4718 \r
4719 // 仮想 DHCP サーバー\r
4720 void VirtualDhcpServer(VH *v, PKT *p)\r
4721 {\r
4722         DHCPV4_HEADER *dhcp;\r
4723         UCHAR *data;\r
4724         UINT size;\r
4725         UINT dhcp_header_size;\r
4726         UINT dhcp_data_offset;\r
4727         UINT tran_id;\r
4728         UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE);\r
4729         bool ok;\r
4730         DHCP_OPTION_LIST *opt;\r
4731         // 引数チェック\r
4732         if (v == NULL || p == NULL)\r
4733         {\r
4734                 return;\r
4735         }\r
4736 \r
4737         dhcp = p->L7.DHCPv4Header;\r
4738 \r
4739         tran_id = Endian32(dhcp->TransactionId);\r
4740 \r
4741         // DHCP データとサイズを取得する\r
4742         dhcp_header_size = sizeof(DHCPV4_HEADER);\r
4743         dhcp_data_offset = (UINT)(((UCHAR *)p->L7.DHCPv4Header) - ((UCHAR *)p->MacHeader) + dhcp_header_size);\r
4744         data = ((UCHAR *)dhcp) + dhcp_header_size;\r
4745         size = p->PacketSize - dhcp_data_offset;\r
4746         if (dhcp_header_size < 5)\r
4747         {\r
4748                 // データサイズが不正\r
4749                 return;\r
4750         }\r
4751 \r
4752         // Magic Cookie を検索する\r
4753         ok = false;\r
4754         while (size >= 5)\r
4755         {\r
4756                 if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0)\r
4757                 {\r
4758                         // 発見\r
4759                         data += 4;\r
4760                         size -= 4;\r
4761                         ok = true;\r
4762                         break;\r
4763                 }\r
4764                 data++;\r
4765                 size--;\r
4766         }\r
4767 \r
4768         if (ok == false)\r
4769         {\r
4770                 // パケット不正\r
4771                 return;\r
4772         }\r
4773 \r
4774         // DHCP オプションリストのパース\r
4775         opt = ParseDhcpOptionList(data, size);\r
4776         if (opt == NULL)\r
4777         {\r
4778                 // パケット不正\r
4779                 return;\r
4780         }\r
4781 \r
4782         if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST))\r
4783         {\r
4784                 // サーバーとしての動作を行う\r
4785                 UINT ip;\r
4786                 if (opt->RequestedIp == 0)\r
4787                 {\r
4788                         opt->RequestedIp = p->L3.IPv4Header->SrcIP;\r
4789                 }\r
4790                 if (opt->Opcode == DHCP_DISCOVER)\r
4791                 {\r
4792                         // 利用できる IP アドレスを 1 つ返す\r
4793                         ip = ServeDhcpDiscover(v, p->MacAddressSrc, opt->RequestedIp);\r
4794                 }\r
4795                 else\r
4796                 {\r
4797                         // IP アドレスを確定する\r
4798                         ip = ServeDhcpRequest(v, p->MacAddressSrc, opt->RequestedIp);\r
4799                 }\r
4800                 if (ip != 0)\r
4801                 {\r
4802                         // 提供可能な IP アドレスがある場合は応答してやる\r
4803 \r
4804                         if (opt->Opcode == DHCP_REQUEST)\r
4805                         {\r
4806                                 DHCP_LEASE *d;\r
4807                                 char mac[MAX_SIZE];\r
4808                                 char str[MAX_SIZE];\r
4809                                 // 同じ IP アドレスで古いレコードがあれば削除する\r
4810                                 d = SearchDhcpLeaseByIp(v, ip);\r
4811                                 if (d != NULL)\r
4812                                 {\r
4813                                         FreeDhcpLease(d);\r
4814                                         Delete(v->DhcpLeaseList, d);\r
4815                                 }\r
4816 \r
4817                                 // 新しいエントリを作成する\r
4818                                 d = NewDhcpLease(v->DhcpExpire, p->MacAddressSrc,\r
4819                                         ip, v->DhcpMask,\r
4820                                         opt->Hostname);\r
4821                                 d->Id = ++v->DhcpId;\r
4822                                 Add(v->DhcpLeaseList, d);\r
4823                                 MacToStr(mac, sizeof(mac), d->MacAddress);\r
4824 \r
4825                                 IPToStr32(str, sizeof(str), d->IpAddress);\r
4826 \r
4827                                 NLog(v, "LH_NAT_DHCP_CREATED", d->Id, mac, str, d->Hostname, v->DhcpExpire / 1000);\r
4828                         }\r
4829 \r
4830                         // 応答する\r
4831                         if (ip != 0)\r
4832                         {\r
4833                                 DHCP_OPTION_LIST ret;\r
4834                                 LIST *o;\r
4835                                 Zero(&ret, sizeof(ret));\r
4836 \r
4837                                 ret.Opcode = (opt->Opcode == DHCP_DISCOVER ? DHCP_OFFER : DHCP_ACK);\r
4838                                 ret.ServerAddress = v->HostIP;\r
4839                                 if (v->DhcpExpire == INFINITE)\r
4840                                 {\r
4841                                         ret.LeaseTime = INFINITE;\r
4842                                 }\r
4843                                 else\r
4844                                 {\r
4845                                         ret.LeaseTime = Endian32(v->DhcpExpire / 1000);\r
4846                                 }\r
4847                                 StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);\r
4848                                 ret.SubnetMask = v->DhcpMask;\r
4849                                 ret.DnsServer = v->DhcpDns;\r
4850                                 ret.Gateway = v->DhcpGateway;\r
4851 \r
4852                                 if (1)\r
4853                                 {\r
4854                                         char client_mac[MAX_SIZE];\r
4855                                         char client_ip[64];\r
4856                                         IP ips;\r
4857                                         BinToStr(client_mac, sizeof(client_mac), p->MacAddressSrc, 6);\r
4858                                         UINTToIP(&ips, ip);\r
4859                                         IPToStr(client_ip, sizeof(client_ip), &ips);\r
4860                                         Debug("DHCP %s : %s given %s\n",\r
4861                                                 ret.Opcode == DHCP_OFFER ? "DHCP_OFFER" : "DHCP_ACK",\r
4862                                                 client_mac, client_ip);\r
4863                                 }\r
4864 \r
4865                                 // DHCP オプションのビルド\r
4866                                 o = BuildDhcpOption(&ret);\r
4867                                 if (o != NULL)\r
4868                                 {\r
4869                                         BUF *b = BuildDhcpOptionsBuf(o);\r
4870                                         if (b != NULL)\r
4871                                         {\r
4872                                                 UINT dest_ip = p->L3.IPv4Header->SrcIP;\r
4873                                                 if (dest_ip == 0)\r
4874                                                 {\r
4875                                                         dest_ip = 0xffffffff;\r
4876                                                 }\r
4877                                                 // 送信\r
4878                                                 VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),\r
4879                                                         ip, dhcp->ClientMacAddress, b);\r
4880 \r
4881                                                 // メモリ解放\r
4882                                                 FreeBuf(b);\r
4883                                         }\r
4884                                         FreeDhcpOptions(o);\r
4885                                 }\r
4886                         }\r
4887                 }\r
4888                 else\r
4889                 {\r
4890                         // 提供できる IP アドレスが無い\r
4891                         DHCP_OPTION_LIST ret;\r
4892                         LIST *o;\r
4893                         Zero(&ret, sizeof(ret));\r
4894 \r
4895                         ret.Opcode = DHCP_NACK;\r
4896                         ret.ServerAddress = v->HostIP;\r
4897                         StrCpy(ret.DomainName, sizeof(ret.DomainName), v->DhcpDomain);\r
4898                         ret.SubnetMask = v->DhcpMask;\r
4899 \r
4900                         // DHCP オプションのビルド\r
4901                         o = BuildDhcpOption(&ret);\r
4902                         if (o != NULL)\r
4903                         {\r
4904                                 BUF *b = BuildDhcpOptionsBuf(o);\r
4905                                 if (b != NULL)\r
4906                                 {\r
4907                                         UINT dest_ip = p->L3.IPv4Header->SrcIP;\r
4908                                         if (dest_ip == 0)\r
4909                                         {\r
4910                                                 dest_ip = 0xffffffff;\r
4911                                         }\r
4912                                         // 送信\r
4913                                         VirtualDhcpSend(v, tran_id, dest_ip, Endian16(p->L4.UDPHeader->SrcPort),\r
4914                                                 ip, dhcp->ClientMacAddress, b);\r
4915 \r
4916                                         // メモリ解放\r
4917                                         FreeBuf(b);\r
4918                                 }\r
4919                                 FreeDhcpOptions(o);\r
4920                         }\r
4921                 }\r
4922         }\r
4923 \r
4924         // メモリ解放\r
4925         Free(opt);\r
4926 }\r
4927 \r
4928 // DHCP 応答パケットの送信\r
4929 void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port,\r
4930                                          UINT new_ip, UCHAR *client_mac, BUF *b)\r
4931 {\r
4932         UINT blank_size = 128 + 64;\r
4933         UINT dhcp_packet_size;\r
4934         UINT magic = Endian32(DHCP_MAGIC_COOKIE);\r
4935         DHCPV4_HEADER *dhcp;\r
4936         void *magic_cookie_addr;\r
4937         void *buffer_addr;\r
4938         // 引数チェック\r
4939         if (v == NULL || b == NULL)\r
4940         {\r
4941                 return;\r
4942         }\r
4943 \r
4944         // DHCP パケットサイズを求める\r
4945         dhcp_packet_size = blank_size + sizeof(DHCPV4_HEADER) + sizeof(magic) + b->Size;\r
4946 \r
4947         // ヘッダ作成\r
4948         dhcp = ZeroMalloc(dhcp_packet_size);\r
4949 \r
4950         dhcp->OpCode = 2;\r
4951         dhcp->HardwareType = 1;\r
4952         dhcp->HardwareAddressSize = 6;\r
4953         dhcp->Hops = 0;\r
4954         dhcp->TransactionId = Endian32(tran_id);\r
4955         dhcp->Seconds = 0;\r
4956         dhcp->Flags = 0;\r
4957         dhcp->YourIP = new_ip;\r
4958         dhcp->ServerIP = v->HostIP;\r
4959         Copy(dhcp->ClientMacAddress, client_mac, 6);\r
4960 \r
4961         // アドレスを求める\r
4962         magic_cookie_addr = (((UCHAR *)dhcp) + sizeof(DHCPV4_HEADER) + blank_size);\r
4963         buffer_addr = ((UCHAR *)magic_cookie_addr) + sizeof(magic);\r
4964 \r
4965         // Magic Cookie\r
4966         Copy(magic_cookie_addr, &magic, sizeof(magic));\r
4967 \r
4968         // Buffer\r
4969         Copy(buffer_addr, b->Buf, b->Size);\r
4970 \r
4971         // 送信\r
4972         SendUdp(v, dest_ip, dest_port, v->HostIP, NAT_DHCP_SERVER_PORT, dhcp, dhcp_packet_size);\r
4973 \r
4974         Free(dhcp);\r
4975 }\r
4976 \r
4977 // DHCP オプション リストをバッファに変換する\r
4978 BUF *BuildDhcpOptionsBuf(LIST *o)\r
4979 {\r
4980         BUF *b;\r
4981         UINT i;\r
4982         UCHAR id;\r
4983         UCHAR sz;\r
4984         // 引数チェック\r
4985         if (o == NULL)\r
4986         {\r
4987                 return NULL;\r
4988         }\r
4989 \r
4990         b = NewBuf();\r
4991         for (i = 0;i < LIST_NUM(o);i++)\r
4992         {\r
4993                 DHCP_OPTION *d = LIST_DATA(o, i);\r
4994                 id = (UCHAR)d->Id;\r
4995                 sz = (UCHAR)d->Size;\r
4996                 WriteBuf(b, &id, 1);\r
4997                 WriteBuf(b, &sz, 1);\r
4998                 WriteBuf(b, d->Data, d->Size);\r
4999         }\r
5000 \r
5001         id = 0xff;\r
5002         WriteBuf(b, &id, 1);\r
5003 \r
5004         return b;\r
5005 }\r
5006 \r
5007 // DHCP オプション リストを DHCP オプションに変換する\r
5008 LIST *BuildDhcpOption(DHCP_OPTION_LIST *opt)\r
5009 {\r
5010         LIST *o;\r
5011         UCHAR opcode;\r
5012         // 引数チェック\r
5013         if (opt == NULL)\r
5014         {\r
5015                 return NULL;\r
5016         }\r
5017 \r
5018         o = NewListFast(NULL);\r
5019 \r
5020         // オペコード\r
5021         opcode = (UCHAR)opt->Opcode;\r
5022         Add(o, NewDhcpOption(DHCP_ID_MESSAGE_TYPE, &opcode, sizeof(opcode)));\r
5023         Add(o, NewDhcpOption(DHCP_ID_SERVER_ADDRESS, &opt->ServerAddress, sizeof(opt->ServerAddress)));\r
5024         Add(o, NewDhcpOption(DHCP_ID_LEASE_TIME, &opt->LeaseTime, sizeof(opt->LeaseTime)));\r
5025         if (StrLen(opt->DomainName) != 0 && opt->DnsServer != 0)\r
5026         {\r
5027                 Add(o, NewDhcpOption(DHCP_ID_DOMAIN_NAME, opt->DomainName, StrLen(opt->DomainName)));\r
5028         }\r
5029         if (opt->SubnetMask != 0)\r
5030         {\r
5031                 Add(o, NewDhcpOption(DHCP_ID_SUBNET_MASK, &opt->SubnetMask, sizeof(opt->SubnetMask)));\r
5032         }\r
5033         if (opt->Gateway != 0)\r
5034         {\r
5035                 Add(o, NewDhcpOption(DHCP_ID_GATEWAY_ADDR, &opt->Gateway, sizeof(opt->Gateway)));\r
5036         }\r
5037         if (opt->DnsServer != 0)\r
5038         {\r
5039                 Add(o, NewDhcpOption(DHCP_ID_DNS_ADDR, &opt->DnsServer, sizeof(opt->DnsServer)));\r
5040         }\r
5041 \r
5042         return o;\r
5043 }\r
5044 \r
5045 // 新しい DHCP オプション項目の作成\r
5046 DHCP_OPTION *NewDhcpOption(UINT id, void *data, UINT size)\r
5047 {\r
5048         DHCP_OPTION *ret;\r
5049         if (size != 0 && data == NULL)\r
5050         {\r
5051                 return NULL;\r
5052         }\r
5053 \r
5054         ret = ZeroMalloc(sizeof(DHCP_OPTION));\r
5055         ret->Data = ZeroMalloc(size);\r
5056         Copy(ret->Data, data, size);\r
5057         ret->Size = (UCHAR)size;\r
5058         ret->Id = (UCHAR)id;\r
5059 \r
5060         return ret;\r
5061 }\r
5062 \r
5063 // DHCP オプションリストのパース\r
5064 DHCP_OPTION_LIST *ParseDhcpOptionList(void *data, UINT size)\r
5065 {\r
5066         DHCP_OPTION_LIST *ret;\r
5067         LIST *o;\r
5068         DHCP_OPTION *a;\r
5069         // 引数チェック\r
5070         if (data == NULL)\r
5071         {\r
5072                 return NULL;\r
5073         }\r
5074 \r
5075         // リストのパース\r
5076         o = ParseDhcpOptions(data, size);\r
5077         if (o == NULL)\r
5078         {\r
5079                 return NULL;\r
5080         }\r
5081 \r
5082         ret = ZeroMalloc(sizeof(DHCP_OPTION_LIST));\r
5083 \r
5084         // オペコードの取得\r
5085         a = GetDhcpOption(o, DHCP_ID_MESSAGE_TYPE);\r
5086         if (a != NULL)\r
5087         {\r
5088                 if (a->Size == 1)\r
5089                 {\r
5090                         ret->Opcode = *((UCHAR *)a->Data);\r
5091                 }\r
5092         }\r
5093 \r
5094         switch (ret->Opcode)\r
5095         {\r
5096         case DHCP_DISCOVER:\r
5097         case DHCP_REQUEST:\r
5098                 // クライアント要求なのでより細かくパースする\r
5099                 // 要求された IP アドレス\r
5100                 a = GetDhcpOption(o, DHCP_ID_REQUEST_IP_ADDRESS);\r
5101                 if (a != NULL && a->Size == 4)\r
5102                 {\r
5103                         Copy(&ret->RequestedIp, a->Data, 4);\r
5104                 }\r
5105                 // ホスト名\r
5106                 a = GetDhcpOption(o, DHCP_ID_HOST_NAME);\r
5107                 if (a != NULL)\r
5108                 {\r
5109                         if (a->Size > 1)\r
5110                         {\r
5111                                 Copy(ret->Hostname, a->Data, MIN(a->Size, sizeof(ret->Hostname) - 1));\r
5112                         }\r
5113                 }\r
5114                 break;\r
5115 \r
5116         case DHCP_OFFER:\r
5117         case DHCP_ACK:\r
5118                 // 今のところこの 2 つのオプションをパースする必要は無い\r
5119                 break;\r
5120         }\r
5121 \r
5122         // リストの解放\r
5123         FreeDhcpOptions(o);\r
5124 \r
5125         return ret;\r
5126 }\r
5127 \r
5128 // DHCP オプションの検索\r
5129 DHCP_OPTION *GetDhcpOption(LIST *o, UINT id)\r
5130 {\r
5131         UINT i;\r
5132         DHCP_OPTION *ret = NULL;\r
5133         // 引数チェック\r
5134         if (o == NULL)\r
5135         {\r
5136                 return NULL;\r
5137         }\r
5138 \r
5139         for (i = 0;i < LIST_NUM(o);i++)\r
5140         {\r
5141                 DHCP_OPTION *opt = LIST_DATA(o, i);\r
5142                 if (opt->Id == id)\r
5143                 {\r
5144                         ret = opt;\r
5145                 }\r
5146         }\r
5147 \r
5148         return ret;\r
5149 }\r
5150 \r
5151 // DHCP オプションの解放\r
5152 void FreeDhcpOptions(LIST *o)\r
5153 {\r
5154         UINT i;\r
5155         // 引数チェック\r
5156         if (o == NULL)\r
5157         {\r
5158                 return;\r
5159         }\r
5160 \r
5161         for (i = 0;i < LIST_NUM(o);i++)\r
5162         {\r
5163                 DHCP_OPTION *opt = LIST_DATA(o, i);\r
5164                 Free(opt->Data);\r
5165                 Free(opt);\r
5166         }\r
5167 \r
5168         ReleaseList(o);\r
5169 }\r
5170 \r
5171 // DHCP オプションのパース\r
5172 LIST *ParseDhcpOptions(void *data, UINT size)\r
5173 {\r
5174         BUF *b;\r
5175         LIST *o;\r
5176         // 引数チェック\r
5177         if (data == NULL)\r
5178         {\r
5179                 return NULL;\r
5180         }\r
5181 \r
5182         b = NewBuf();\r
5183         WriteBuf(b, data, size);\r
5184         SeekBuf(b, 0, 0);\r
5185 \r
5186         o = NewListFast(NULL);\r
5187 \r
5188         while (true)\r
5189         {\r
5190                 UCHAR c = 0;\r
5191                 UCHAR sz = 0;\r
5192                 DHCP_OPTION *opt;\r
5193                 if (ReadBuf(b, &c, 1) != 1)\r
5194                 {\r
5195                         break;\r
5196                 }\r
5197                 if (c == 0xff)\r
5198                 {\r
5199                         break;\r
5200                 }\r
5201                 if (ReadBuf(b, &sz, 1) != 1)\r
5202                 {\r
5203                         break;\r
5204                 }\r
5205 \r
5206                 opt = ZeroMalloc(sizeof(DHCP_OPTION));\r
5207                 opt->Id = (UINT)c;\r
5208                 opt->Size = (UINT)sz;\r
5209                 opt->Data = ZeroMalloc((UINT)sz);\r
5210                 ReadBuf(b, opt->Data, sz);\r
5211                 Add(o, opt);\r
5212         }\r
5213 \r
5214         FreeBuf(b);\r
5215 \r
5216         return o;\r
5217 }\r
5218 \r
5219 // 仮想ホスト - Layer2 の処理\r
5220 void VirtualLayer2(VH *v, PKT *packet)\r
5221 {\r
5222         bool ok;\r
5223         // 引数チェック\r
5224         if (packet == NULL || v == NULL)\r
5225         {\r
5226                 return;\r
5227         }\r
5228 \r
5229         // パケットフィルタ\r
5230         if (VirtualLayer2Filter(v, packet) == false)\r
5231         {\r
5232                 // パケットは無視された\r
5233                 return;\r
5234         }\r
5235 \r
5236         ok = false;\r
5237         if (packet->TypeL3 == L3_IPV4 && packet->TypeL4 == L4_UDP && packet->TypeL7 == L7_DHCPV4)\r
5238         {\r
5239                 if (v->UseDhcp)\r
5240                 {\r
5241                         // DHCP パケットに関する特殊な処理\r
5242                         if (packet->BroadcastPacket || Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)\r
5243                         {\r
5244                                 // 仮想 DHCP サーバー処理\r
5245                                 VirtualDhcpServer(v, packet);\r
5246                                 ok = true;\r
5247                         }\r
5248                 }\r
5249         }\r
5250 \r
5251         if (ok == false)\r
5252         {\r
5253                 // サポートしているプロトコルごとに処理\r
5254                 switch (packet->TypeL3)\r
5255                 {\r
5256                 case L3_ARPV4:  // ARPv4\r
5257                         VirtualArpReceived(v, packet);\r
5258                         break;\r
5259 \r
5260                 case L3_IPV4:   // IPv4\r
5261                         VirtualIpReceived(v, packet);\r
5262                         break;\r
5263                 }\r
5264         }\r
5265 }\r
5266 \r
5267 // パケットフィルタ (自分以外へのパケットを遮断)\r
5268 bool VirtualLayer2Filter(VH *v, PKT *packet)\r
5269 {\r
5270         // 引数チェック\r
5271         if (v == NULL || packet == NULL)\r
5272         {\r
5273                 return false;\r
5274         }\r
5275 \r
5276         // ブロードキャストパケットなら通過\r
5277         if (packet->BroadcastPacket)\r
5278         {\r
5279                 return true;\r
5280         }\r
5281 \r
5282         // 送信元が自分のパケットなら無視\r
5283         if (Cmp(packet->MacAddressSrc, v->MacAddress, 6) == 0)\r
5284         {\r
5285                 return false;\r
5286         }\r
5287         // 自分宛のパケットなら通過\r
5288         if (Cmp(packet->MacAddressDest, v->MacAddress, 6) == 0)\r
5289         {\r
5290                 return true;\r
5291         }\r
5292 \r
5293         // それ以外のパケットなら破棄\r
5294         return false;\r
5295 }\r
5296 \r
5297 // 仮想ホストにパケットを受信させる\r
5298 bool VirtualPutPacket(VH *v, void *data, UINT size)\r
5299 {\r
5300         if (data == NULL)\r
5301         {\r
5302                 // フラッシュ\r
5303                 v->flag1 = false;\r
5304         }\r
5305         else\r
5306         {\r
5307                 // 受信したパケットを解釈する\r
5308                 PKT *packet = ParsePacket(data, size);\r
5309 \r
5310                 if (v->flag1 == false)\r
5311                 {\r
5312                         v->flag1 = true;\r
5313                         v->Now = Tick64();\r
5314                 }\r
5315 \r
5316                 // ここの中は仮想マシン全体をロックする\r
5317                 LockVirtual(v);\r
5318                 {\r
5319                         if (packet != NULL)\r
5320                         {\r
5321                                 // Layer-2 の処理を行う\r
5322                                 VirtualLayer2(v, packet);\r
5323 \r
5324                                 // パケット構造体の解放\r
5325                                 FreePacket(packet);\r
5326                         }\r
5327                 }\r
5328                 UnlockVirtual(v);\r
5329 \r
5330                 Free(data);\r
5331         }\r
5332 \r
5333         return true;\r
5334 }\r
5335 bool VirtualPaPutPacket(SESSION *s, void *data, UINT size)\r
5336 {\r
5337         VH *v;\r
5338         // 引数チェック\r
5339         if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)\r
5340         {\r
5341                 return false;\r
5342         }\r
5343 \r
5344         return VirtualPutPacket(v, data, size);\r
5345 }\r
5346 \r
5347 // 仮想ホストのオプションを取得する\r
5348 void GetVirtualHostOption(VH *v, VH_OPTION *o)\r
5349 {\r
5350         // 引数チェック\r
5351         if (v == NULL)\r
5352         {\r
5353                 return;\r
5354         }\r
5355 \r
5356         LockVirtual(v);\r
5357         {\r
5358                 Zero(o, sizeof(VH_OPTION));\r
5359 \r
5360                 // MAC アドレス\r
5361                 Copy(o->MacAddress, v->MacAddress, 6);\r
5362 \r
5363                 // ホスト情報\r
5364                 UINTToIP(&o->Ip, v->HostIP);\r
5365                 UINTToIP(&o->Mask, v->HostMask);\r
5366 \r
5367                 o->Mtu = v->Mtu;\r
5368 \r
5369                 // NAT タイムアウト情報\r
5370                 o->NatTcpTimeout = v->NatTcpTimeout / 1000;\r
5371                 o->NatUdpTimeout = v->NatUdpTimeout / 1000;\r
5372 \r
5373                 // NAT 使用フラグ\r
5374                 o->UseNat = v->UseNat;\r
5375 \r
5376                 // DHCP 使用フラグ\r
5377                 o->UseDhcp = v->UseDhcp;\r
5378 \r
5379                 // DHCP 配布 IP アドレス範囲\r
5380                 UINTToIP(&o->DhcpLeaseIPStart, v->DhcpIpStart);\r
5381                 UINTToIP(&o->DhcpLeaseIPEnd, v->DhcpIpEnd);\r
5382 \r
5383                 // サブネットマスク\r
5384                 UINTToIP(&o->DhcpSubnetMask, v->DhcpMask);\r
5385 \r
5386                 // 有効期限\r
5387                 if (v->DhcpExpire != INFINITE)\r
5388                 {\r
5389                         o->DhcpExpireTimeSpan = v->DhcpExpire / 1000;\r
5390                 }\r
5391                 else\r
5392                 {\r
5393                         o->DhcpExpireTimeSpan = INFINITE;\r
5394                 }\r
5395 \r
5396                 // ゲートウェイアドレス\r
5397                 UINTToIP(&o->DhcpGatewayAddress, v->DhcpGateway);\r
5398 \r
5399                 // DNS サーバーアドレス\r
5400                 UINTToIP(&o->DhcpDnsServerAddress, v->DhcpDns);\r
5401 \r
5402                 // ドメイン名\r
5403                 StrCpy(o->DhcpDomainName, sizeof(o->DhcpDomainName), v->DhcpDomain);\r
5404 \r
5405                 // ログの保存\r
5406                 o->SaveLog = v->SaveLog;\r
5407         }\r
5408         UnlockVirtual(v);\r
5409 }\r
5410 \r
5411 // 仮想ホストにオプションを設定する\r
5412 void SetVirtualHostOption(VH *v, VH_OPTION *vo)\r
5413 {\r
5414         UINT i;\r
5415         // 引数チェック\r
5416         if (v == NULL || vo == NULL)\r
5417         {\r
5418                 return;\r
5419         }\r
5420 \r
5421         LockVirtual(v);\r
5422         {\r
5423                 // MAC アドレスを設定する\r
5424                 for (i = 0;i < 6;i++)\r
5425                 {\r
5426                         if (vo->MacAddress[i] != 0)\r
5427                         {\r
5428                                 Copy(v->MacAddress, vo->MacAddress, 6);\r
5429                                 break;\r
5430                         }\r
5431                 }\r
5432 \r
5433                 // ホスト情報リストを設定する\r
5434                 v->HostIP = IPToUINT(&vo->Ip);\r
5435                 v->HostMask = IPToUINT(&vo->Mask);\r
5436 \r
5437                 // MTU, MMS を設定する\r
5438                 v->Mtu = MIN(vo->Mtu, MAX_L3_DATA_SIZE);\r
5439                 if (v->Mtu == 0)\r
5440                 {\r
5441                         v->Mtu = MAX_L3_DATA_SIZE;\r
5442                 }\r
5443                 v->Mtu = MAX(v->Mtu, TCP_HEADER_SIZE + IP_HEADER_SIZE + MAC_HEADER_SIZE + 8);\r
5444                 v->IpMss = ((v->Mtu - IP_HEADER_SIZE) / 8) * 8;\r
5445                 v->TcpMss = ((v->IpMss - TCP_HEADER_SIZE) / 8) * 8;\r
5446                 v->UdpMss = ((v->IpMss - UDP_HEADER_SIZE) / 8) * 8;\r
5447 \r
5448                 if (vo->NatTcpTimeout != 0)\r
5449                 {\r
5450                         v->NatTcpTimeout = MIN(vo->NatTcpTimeout, 4000000) * 1000;\r
5451                 }\r
5452                 if (vo->NatUdpTimeout != 0)\r
5453                 {\r
5454                         v->NatUdpTimeout = MIN(vo->NatUdpTimeout, 4000000) * 1000;\r
5455                 }\r
5456                 v->NatTcpTimeout = MAKESURE(v->NatTcpTimeout, NAT_TCP_MIN_TIMEOUT, NAT_TCP_MAX_TIMEOUT);\r
5457                 v->NatUdpTimeout = MAKESURE(v->NatUdpTimeout, NAT_UDP_MIN_TIMEOUT, NAT_UDP_MAX_TIMEOUT);\r
5458                 Debug("Timeout: %d , %d\n", v->NatTcpTimeout, v->NatUdpTimeout);\r
5459 \r
5460                 // NAT 使用フラグ\r
5461                 v->UseNat = vo->UseNat;\r
5462 \r
5463                 // DHCP 使用フラグ\r
5464                 v->UseDhcp = vo->UseDhcp;\r
5465 \r
5466                 // 有効期限\r
5467                 if (vo->DhcpExpireTimeSpan == 0 || vo->DhcpExpireTimeSpan == INFINITE)\r
5468                 {\r
5469                         v->DhcpExpire = INFINITE;\r
5470                 }\r
5471                 else\r
5472                 {\r
5473                         v->DhcpExpire = MAKESURE(DHCP_MIN_EXPIRE_TIMESPAN,\r
5474                                 MIN(vo->DhcpExpireTimeSpan * 1000, 2000000000),\r
5475                                 INFINITE);\r
5476                 }\r
5477 \r
5478                 // 配布するアドレス範囲\r
5479                 v->DhcpIpStart = IPToUINT(&vo->DhcpLeaseIPStart);\r
5480                 v->DhcpIpEnd = IPToUINT(&vo->DhcpLeaseIPEnd);\r
5481                 if (Endian32(v->DhcpIpEnd) < Endian32(v->DhcpIpStart))\r
5482                 {\r
5483                         v->DhcpIpEnd = v->DhcpIpStart;\r
5484                 }\r
5485 \r
5486                 // サブネットマスク\r
5487                 v->DhcpMask = IPToUINT(&vo->DhcpSubnetMask);\r
5488 \r
5489                 // ゲートウェイアドレス\r
5490                 v->DhcpGateway = IPToUINT(&vo->DhcpGatewayAddress);\r
5491 \r
5492                 // DNS サーバーアドレス\r
5493                 v->DhcpDns = IPToUINT(&vo->DhcpDnsServerAddress);\r
5494 \r
5495                 // ドメイン名\r
5496                 StrCpy(v->DhcpDomain, sizeof(v->DhcpDomain), vo->DhcpDomainName);\r
5497 \r
5498                 // ログの保存\r
5499                 v->SaveLog = vo->SaveLog;\r
5500         }\r
5501         UnlockVirtual(v);\r
5502 }\r
5503 \r
5504 // 仮想ホストの解放\r
5505 void Virtual_Free(VH *v)\r
5506 {\r
5507         // DHCP サーバー解放\r
5508         FreeDhcpServer(v);\r
5509 \r
5510         // NAT 解放\r
5511         FreeNat(v);\r
5512 \r
5513         LockVirtual(v);\r
5514         {\r
5515                 // IP 結合リスト解放\r
5516                 FreeIpCombineList(v);\r
5517 \r
5518                 // IP 待ちテーブル解放\r
5519                 FreeIpWaitTable(v);\r
5520 \r
5521                 // ARP 待ちテーブル解放\r
5522                 FreeArpWaitTable(v);\r
5523 \r
5524                 // ARP テーブル解放\r
5525                 FreeArpTable(v);\r
5526 \r
5527                 // 送信キュー解放\r
5528                 LockQueue(v->SendQueue);\r
5529                 {\r
5530                         BLOCK *block;\r
5531 \r
5532                         // すべてのキューを解放する\r
5533                         while (block = GetNext(v->SendQueue))\r
5534                         {\r
5535                                 FreeBlock(block);\r
5536                         }\r
5537                 }\r
5538                 UnlockQueue(v->SendQueue);\r
5539                 ReleaseQueue(v->SendQueue);\r
5540                 v->SendQueue = NULL;\r
5541 \r
5542                 // キャンセルオブジェクト解放\r
5543                 ReleaseCancel(v->Cancel);\r
5544 \r
5545                 v->Active = false;\r
5546         }\r
5547         UnlockVirtual(v);\r
5548 \r
5549         // ロガー解放\r
5550         FreeLog(v->Logger);\r
5551 }\r
5552 void VirtualPaFree(SESSION *s)\r
5553 {\r
5554         VH *v;\r
5555         // 引数チェック\r
5556         if (s == NULL || (v = (VH *)s->PacketAdapter->Param) == NULL)\r
5557         {\r
5558                 return;\r
5559         }\r
5560 \r
5561         Virtual_Free(v);\r
5562 }\r
5563 \r
5564 // 仮想ホストの解放\r
5565 void ReleaseVirtual(VH *v)\r
5566 {\r
5567         // 引数チェック\r
5568         if (v == NULL)\r
5569         {\r
5570                 return;\r
5571         }\r
5572 \r
5573         if (Release(v->ref) == 0)\r
5574         {\r
5575                 CleanupVirtual(v);\r
5576         }\r
5577 }\r
5578 \r
5579 // 仮想ホストのロック\r
5580 void LockVirtual(VH *v)\r
5581 {\r
5582         // 引数チェック\r
5583         if (v == NULL)\r
5584         {\r
5585                 return;\r
5586         }\r
5587 \r
5588         Lock(v->lock);\r
5589 }\r
5590 \r
5591 // 仮想ホストのロック解除\r
5592 void UnlockVirtual(VH *v)\r
5593 {\r
5594         // 引数チェック\r
5595         if (v == NULL)\r
5596         {\r
5597                 return;\r
5598         }\r
5599 \r
5600         Unlock(v->lock);\r
5601 }\r
5602 \r
5603 // 仮想ホストのクリーンアップ\r
5604 void CleanupVirtual(VH *v)\r
5605 {\r
5606         // 引数チェック\r
5607         if (v == NULL)\r
5608         {\r
5609                 return;\r
5610         }\r
5611 \r
5612         if (v->Session != NULL)\r
5613         {\r
5614                 ReleaseSession(v->Session);\r
5615         }\r
5616 \r
5617         DeleteCounter(v->Counter);\r
5618         DeleteLock(v->lock);\r
5619 \r
5620         Free(v);\r
5621 }\r
5622 \r
5623 // 仮想ホストの停止\r
5624 void StopVirtualHost(VH *v)\r
5625 {\r
5626         SESSION *s;\r
5627         // 引数チェック\r
5628         if (v == NULL)\r
5629         {\r
5630                 return;\r
5631         }\r
5632 \r
5633         // 仮想ホストに対応したセッションの取得\r
5634         LockVirtual(v);\r
5635         {\r
5636                 s = v->Session;\r
5637                 if (s != NULL)\r
5638                 {\r
5639                         AddRef(s->ref);\r
5640                 }\r
5641         }\r
5642         UnlockVirtual(v);\r
5643 \r
5644         if (s == NULL)\r
5645         {\r
5646                 // すでにこのセッションは停止している\r
5647                 return;\r
5648         }\r
5649 \r
5650         // セッションの停止\r
5651         StopSession(s);\r
5652 \r
5653         ReleaseSession(s);\r
5654 }\r
5655 \r
5656 // 新しい仮想ホストの作成\r
5657 VH *NewVirtualHost(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option)\r
5658 {\r
5659         return NewVirtualHostEx(cedar, option, auth, vh_option, NULL);\r
5660 }\r
5661 VH *NewVirtualHostEx(CEDAR *cedar, CLIENT_OPTION *option, CLIENT_AUTH *auth, VH_OPTION *vh_option, NAT *nat)\r
5662 {\r
5663         VH *v;\r
5664         // 引数チェック\r
5665         if (vh_option == NULL)\r
5666         {\r
5667                 return NULL;\r
5668         }\r
5669 \r
5670         // VH の作成\r
5671         v = ZeroMalloc(sizeof(VH));\r
5672         v->ref = NewRef();\r
5673         v->lock = NewLock();\r
5674         v->Counter = NewCounter();\r
5675 \r
5676         v->nat = nat;\r
5677 \r
5678         // オプションの設定\r
5679         SetVirtualHostOption(v, vh_option);\r
5680 \r
5681         return v;\r
5682 }\r
5683 \r
5684 // ランダムな MAC アドレスを生成する\r
5685 void GenMacAddress(UCHAR *mac)\r
5686 {\r
5687         UCHAR rand_data[32];\r
5688         UINT64 now;\r
5689         BUF *b;\r
5690         UCHAR hash[SHA1_SIZE];\r
5691         // 引数チェック\r
5692         if (mac == NULL)\r
5693         {\r
5694                 return;\r
5695         }\r
5696 \r
5697         // 現在時刻を取得\r
5698         now = SystemTime64();\r
5699 \r
5700         // 乱数を生成\r
5701         Rand(rand_data, sizeof(rand_data));\r
5702 \r
5703         // バッファに追加\r
5704         b = NewBuf();\r
5705         WriteBuf(b, &now, sizeof(now));\r
5706         WriteBuf(b, rand_data, sizeof(rand_data));\r
5707 \r
5708         // ハッシュ\r
5709         Hash(hash, b->Buf, b->Size, true);\r
5710 \r
5711         // MAC アドレスを生成\r
5712         mac[0] = 0x00;\r
5713         mac[1] = 0xAC;          // AC 万歳\r
5714         mac[2] = hash[0];\r
5715         mac[3] = hash[1];\r
5716         mac[4] = hash[2];\r
5717         mac[5] = hash[3];\r
5718 \r
5719         FreeBuf(b);\r
5720 }\r
5721 \r
5722 // 仮想ホストのパケットアダプタを取得\r
5723 PACKET_ADAPTER *VirtualGetPacketAdapter()\r
5724 {\r
5725         return NewPacketAdapter(VirtualPaInit, VirtualPaGetCancel,\r
5726                 VirtualPaGetNextPacket, VirtualPaPutPacket, VirtualPaFree);\r
5727 }\r
5728 \r
5729 \r