Document of c-modernization-kit (porter) 1.0.0
Loading...
Searching...
No Matches
potrPeerTable.c
Go to the documentation of this file.
1
13
14#include <stdlib.h>
15#include <inttypes.h>
16#include <string.h>
17
18#ifndef _WIN32
19 #include <arpa/inet.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <time.h>
23 #include <unistd.h>
24#else /* _WIN32 */
25 #include <winsock2.h>
26 #include <ws2tcpip.h>
27 #include <windows.h>
28#endif /* _WIN32 */
29
30#include <porter_const.h>
31#include <porter.h>
32
33#include "potrContext.h"
34#include "potrPeerTable.h"
35#include "protocol/packet.h"
36#include "protocol/window.h"
37#include "infra/potrLog.h"
38#include "infra/crypto/crypto.h"
39
40/* --------------------------------------------------------------------------
41 * プラットフォーム別 ミューテックスラッパー
42 * -------------------------------------------------------------------------- */
43#ifndef _WIN32
44 #define POTR_MUTEX_INIT(m) pthread_mutex_init((m), NULL)
45 #define POTR_MUTEX_DESTROY(m) pthread_mutex_destroy(m)
46#else /* _WIN32 */
47 #define POTR_MUTEX_INIT(m) InitializeCriticalSection(m)
48 #define POTR_MUTEX_DESTROY(m) DeleteCriticalSection(m)
49#endif /* _WIN32 */
50
51/* ピアのセッション識別子・開始時刻を生成して peer に格納する */
53{
54#ifndef _WIN32
55 struct timespec ts;
56
57 srand((unsigned)((unsigned long)time(NULL) ^ (unsigned long)getpid()));
58 peer->session_id = (uint32_t)rand();
59
60 clock_gettime(CLOCK_REALTIME, &ts);
61 peer->session_tv_sec = (int64_t)ts.tv_sec;
62 peer->session_tv_nsec = (int32_t)ts.tv_nsec;
63#else /* _WIN32 */
64 FILETIME ft;
65 ULARGE_INTEGER uli;
66
67 srand((unsigned)(GetTickCount() ^ GetCurrentProcessId()));
68 peer->session_id = (uint32_t)rand();
69
70 GetSystemTimeAsFileTime(&ft);
71 uli.LowPart = ft.dwLowDateTime;
72 uli.HighPart = ft.dwHighDateTime;
73 peer->session_tv_sec = (int64_t)(uli.QuadPart / 10000000ULL) - 11644473600LL;
74 peer->session_tv_nsec = (int32_t)((uli.QuadPart % 10000000ULL) * 100ULL);
75#endif /* _WIN32 */
76}
77
78/* 使用中でない peer_id を単調増加カウンタから生成する (peers_mutex 取得済みの文脈で呼ぶ) */
80{
81 PotrPeerId candidate = ctx->next_peer_id;
82 int i;
83 int in_use;
84
85 for (;;)
86 {
87 /* 予約値をスキップ */
88 if (candidate == 0 || candidate == POTR_PEER_ALL)
89 {
90 candidate++;
91 continue;
92 }
93
94 /* 現在接続中ピアとの衝突チェック */
95 in_use = 0;
96 for (i = 0; i < ctx->max_peers; i++)
97 {
98 if (ctx->peers[i].active && ctx->peers[i].peer_id == candidate)
99 {
100 in_use = 1;
101 break;
102 }
103 }
104
105 if (!in_use)
106 {
107 break;
108 }
109 candidate++;
110 }
111
112 ctx->next_peer_id = candidate + 1;
113 return candidate;
114}
115
116/* doxygen コメントは、ヘッダに記載 */
118{
119 PotrPacket fin_pkt;
121 size_t wire_len;
122 int i;
123
124 shdr.service_id = ctx->service.service_id;
125 shdr.session_id = peer->session_id;
126 shdr.session_tv_sec = peer->session_tv_sec;
127 shdr.session_tv_nsec = peer->session_tv_nsec;
128
129 if (packet_build_fin(&fin_pkt, &shdr) != POTR_SUCCESS)
130 {
131 return;
132 }
133
134 if (ctx->service.encrypt_enabled)
135 {
136 uint8_t wire_buf[PACKET_HEADER_SIZE + POTR_CRYPTO_TAG_SIZE];
137 uint8_t nonce[POTR_CRYPTO_NONCE_SIZE];
138 size_t enc_out = POTR_CRYPTO_TAG_SIZE;
139
140 fin_pkt.flags |= htons(POTR_FLAG_ENCRYPTED);
141 fin_pkt.payload_len = htons((uint16_t)POTR_CRYPTO_TAG_SIZE);
142
143 /* ノンス: session_id(4B) + flags(2B, FIN|ENCRYPTED NBO) + 0(4B) + padding(2B) */
144 memcpy(nonce, &fin_pkt.session_id, 4);
145 memcpy(nonce + 4, &fin_pkt.flags, 2);
146 memset(nonce + 6, 0, 4);
147 memset(nonce + 10, 0, 2);
148
149 memcpy(wire_buf, &fin_pkt, PACKET_HEADER_SIZE);
150 if (potr_encrypt(wire_buf + PACKET_HEADER_SIZE, &enc_out,
151 NULL, 0,
152 ctx->service.encrypt_key,
153 nonce,
154 wire_buf, PACKET_HEADER_SIZE) != 0)
155 {
156 return;
157 }
158 wire_len = PACKET_HEADER_SIZE + enc_out;
159
160 for (i = 0; i < (int)POTR_MAX_PATH; i++)
161 {
162 if (peer->dest_addr[i].sin_family == 0) continue;
163 if (ctx->sock[i] == POTR_INVALID_SOCKET) continue;
164#ifndef _WIN32
165 sendto(ctx->sock[i], wire_buf, wire_len, 0,
166 (const struct sockaddr *)&peer->dest_addr[i],
167 sizeof(peer->dest_addr[i]));
168#else /* _WIN32 */
169 sendto(ctx->sock[i], (const char *)wire_buf, (int)wire_len, 0,
170 (const struct sockaddr *)&peer->dest_addr[i],
171 sizeof(peer->dest_addr[i]));
172#endif /* _WIN32 */
173 }
174 }
175 else
176 {
177 wire_len = packet_wire_size(&fin_pkt);
178
179 for (i = 0; i < (int)POTR_MAX_PATH; i++)
180 {
181 if (peer->dest_addr[i].sin_family == 0) continue;
182 if (ctx->sock[i] == POTR_INVALID_SOCKET) continue;
183#ifndef _WIN32
184 sendto(ctx->sock[i], &fin_pkt, wire_len, 0,
185 (const struct sockaddr *)&peer->dest_addr[i],
186 sizeof(peer->dest_addr[i]));
187#else /* _WIN32 */
188 sendto(ctx->sock[i], (const char *)&fin_pkt, (int)wire_len, 0,
189 (const struct sockaddr *)&peer->dest_addr[i],
190 sizeof(peer->dest_addr[i]));
191#endif /* _WIN32 */
192 }
193 }
194}
195
196/* doxygen コメントは、ヘッダに記載 */
198{
199 int i;
200
201 ctx->peers = (PotrPeerContext *)calloc((size_t)ctx->max_peers,
202 sizeof(PotrPeerContext));
203 if (ctx->peers == NULL)
204 {
206 "peer_table_init: service_id=%" PRId64 " calloc failed (max_peers=%d)",
207 ctx->service.service_id, ctx->max_peers);
208 return POTR_ERROR;
209 }
210
211 for (i = 0; i < ctx->max_peers; i++)
212 {
213 ctx->peers[i].active = 0;
214 }
215
217 ctx->n_peers = 0;
218 ctx->next_peer_id = 1U;
219
221 "peer_table_init: service_id=%" PRId64 " max_peers=%d",
222 ctx->service.service_id, ctx->max_peers);
223
224 return POTR_SUCCESS;
225}
226
227/* doxygen コメントは、ヘッダに記載 */
229{
230 int i;
231
232 if (ctx->peers == NULL)
233 {
234 return;
235 }
236
238 "peer_table_destroy: service_id=%" PRId64 " n_peers=%d",
239 ctx->service.service_id, ctx->n_peers);
240
241 for (i = 0; i < ctx->max_peers; i++)
242 {
243 if (!ctx->peers[i].active)
244 {
245 continue;
246 }
247
248 /* 各ピアへ FIN を送信 */
249 peer_send_fin(ctx, &ctx->peers[i]);
250
251 /* リソース解放 */
255 free(ctx->peers[i].frag_buf);
256 ctx->peers[i].frag_buf = NULL;
257 ctx->peers[i].active = 0;
258 }
259
261
262 free(ctx->peers);
263 ctx->peers = NULL;
264 ctx->n_peers = 0;
265}
266
267/* doxygen コメントは、ヘッダに記載 */
269 uint32_t session_id,
270 int64_t session_tv_sec,
271 int32_t session_tv_nsec)
272{
273 int i;
274
275 for (i = 0; i < ctx->max_peers; i++)
276 {
277 if (!ctx->peers[i].active)
278 {
279 continue;
280 }
281 if (ctx->peers[i].peer_session_id == session_id &&
282 ctx->peers[i].peer_session_tv_sec == session_tv_sec &&
283 ctx->peers[i].peer_session_tv_nsec == session_tv_nsec)
284 {
285 return &ctx->peers[i];
286 }
287 }
288 return NULL;
289}
290
291/* doxygen コメントは、ヘッダに記載 */
293{
294 int i;
295
296 for (i = 0; i < ctx->max_peers; i++)
297 {
298 if (ctx->peers[i].active && ctx->peers[i].peer_id == peer_id)
299 {
300 return &ctx->peers[i];
301 }
302 }
303 return NULL;
304}
305
306/* doxygen コメントは、ヘッダに記載 */
308 const struct sockaddr_in *sender_addr,
309 int path_idx)
310{
311 int i;
312 PotrPeerContext *peer = NULL;
313
314 /* max_peers 超過チェック */
315 if (ctx->n_peers >= ctx->max_peers)
316 {
317 char ip_str[INET_ADDRSTRLEN];
318 inet_ntop(AF_INET, &sender_addr->sin_addr, ip_str, sizeof(ip_str));
320 "peer_create: service_id=%" PRId64 " max_peers=%d reached, "
321 "rejecting new connection from %s:%u",
322 ctx->service.service_id, ctx->max_peers,
323 ip_str, (unsigned)ntohs(sender_addr->sin_port));
324 return NULL;
325 }
326
327 /* 空きスロットを確保 */
328 for (i = 0; i < ctx->max_peers; i++)
329 {
330 if (!ctx->peers[i].active)
331 {
332 peer = &ctx->peers[i];
333 break;
334 }
335 }
336
337 if (peer == NULL)
338 {
339 /* n_peers < max_peers のはずなのにスロットが見つからない (内部整合性エラー) */
341 "peer_create: service_id=%" PRId64 " no free slot (internal error)",
342 ctx->service.service_id);
343 return NULL;
344 }
345
346 /* スロットを初期化 */
347 memset(peer, 0, sizeof(*peer));
348
349 peer->peer_id = allocate_peer_id(ctx);
350 peer->active = 1;
351
352 /* 自セッション生成 */
354
355 /* ウィンドウ初期化 */
356 if (window_init(&peer->send_window, 0,
358 {
359 peer->active = 0;
361 "peer_create: service_id=%" PRId64 " send_window init failed",
362 ctx->service.service_id);
363 return NULL;
364 }
365
366 if (window_init(&peer->recv_window, 0,
368 {
370 peer->active = 0;
372 "peer_create: service_id=%" PRId64 " recv_window init failed",
373 ctx->service.service_id);
374 return NULL;
375 }
376
378
379 /* フラグメント結合バッファ確保 */
380 peer->frag_buf = (uint8_t *)malloc(ctx->global.max_message_size);
381 if (peer->frag_buf == NULL)
382 {
386 peer->active = 0;
388 "peer_create: service_id=%" PRId64 " frag_buf alloc failed",
389 ctx->service.service_id);
390 return NULL;
391 }
392 peer->frag_buf_len = 0;
393 peer->frag_compressed = 0;
394
395 /* 送信元アドレスを最初のパスとして記録 (インデックス = path_idx = ctx->sock[] の添字) */
396 peer->dest_addr[path_idx] = *sender_addr;
397 peer->path_last_recv_sec[path_idx] = 0; /* n1_update_path_recv() で更新される */
398 peer->n_paths = 1;
399
400 ctx->n_peers++;
401
403 "peer_create: service_id=%" PRId64 " peer_id=%u created (n_peers=%d)",
404 ctx->service.service_id, (unsigned)peer->peer_id, ctx->n_peers);
405
406 return peer;
407}
408
409/* doxygen コメントは、ヘッダに記載 */
410void peer_path_clear(struct PotrContext_ *ctx, PotrPeerContext *peer, int path_idx)
411{
412 if (peer->dest_addr[path_idx].sin_family == 0)
413 {
414 return; /* 既に未使用スロット */
415 }
416
418 "peer_path_clear: service_id=%" PRId64 " peer=%u path %d cleared",
419 ctx->service.service_id, (unsigned)peer->peer_id, path_idx);
420
421 memset(&peer->dest_addr[path_idx], 0, sizeof(peer->dest_addr[path_idx]));
422 peer->path_last_recv_sec[path_idx] = 0;
423 peer->path_last_recv_nsec[path_idx] = 0;
424 peer->n_paths--;
425}
426
427/* doxygen コメントは、ヘッダに記載 */
428void peer_free(struct PotrContext_ *ctx, PotrPeerContext *peer)
429{
430 if (peer == NULL || !peer->active)
431 {
432 return;
433 }
434
436 "peer_free: service_id=%" PRId64 " peer_id=%u freed",
437 ctx->service.service_id, (unsigned)peer->peer_id);
438
442
443 free(peer->frag_buf);
444 peer->frag_buf = NULL;
445
446 peer->active = 0;
447 ctx->n_peers--;
448}
データ暗号化・復号モジュールの内部ヘッダー。
int potr_encrypt(uint8_t *dst, size_t *dst_len, const uint8_t *src, size_t src_len, const uint8_t *key, const uint8_t *nonce, const uint8_t *aad, size_t aad_len)
AES-256-GCM でデータを暗号化します。
#define POTR_CRYPTO_NONCE_SIZE
AES-256-GCM ノンスサイズ (バイト)。session_id (4B NBO) + flags (2B NBO) + seq_or_ack_num (4B NBO) + padding (2B...
#define POTR_CRYPTO_TAG_SIZE
AES-256-GCM 認証タグサイズ (バイト)。暗号文の直後に付加する。
#define POTR_MAX_PATH
マルチパスの最大パス数。
#define POTR_FLAG_ENCRYPTED
AES-256-GCM 認証タグが付与されていることを示す外側パケットフラグ。 POTR_FLAG_DATA と組み合わせる場合: [ヘッダー 32B][暗号文: packed_len B][GCM ...
#define POTR_PEER_ALL
全接続ピアへの一斉送信を指示する予約ピア ID。 N:1 モードでは全アクティブピアへユニキャスト送信する。 1:1 モードでは唯一のピアへの送信として動作する。
#define POTR_SUCCESS
成功の戻り値を表す定数。
#define POTR_ERROR
失敗の戻り値を表す定数。
int packet_build_fin(PotrPacket *packet, const PotrPacketSessionHdr *shdr)
正常終了通知 (FIN) パケットを構築します。
Definition packet.c:163
size_t packet_wire_size(const PotrPacket *packet)
パケットのヘッダー + ペイロードの合計バイト数を返します。
Definition packet.c:347
パケット構築・解析モジュールの内部ヘッダー。
#define PACKET_HEADER_SIZE
パケットヘッダーの固定長 (バイト)。payload フィールドの開始オフセット。
Definition packet.h:23
通信ライブラリ (動的リンク用) のヘッダーファイル。
通信ライブラリの定数ファイル。
@ POTR_TRACE_ERROR
エラー。操作の失敗を記録。TRACE_LV_ERROR (1) と同値。
@ POTR_TRACE_INFO
情報。TRACE_LV_INFO (3) と同値。
@ POTR_TRACE_VERBOSE
詳細情報 (デバッグ)。TRACE_LV_VERBOSE (4) と同値。
@ POTR_TRACE_WARNING
警告。回復可能な異常を記録。TRACE_LV_WARNING (2) と同値。
uint32_t PotrPeerId
ピア識別子。
Definition porter_type.h:32
セッションコンテキスト内部定義ヘッダー。
struct PotrPeerContext_ PotrPeerContext
N:1 モードにおける個別ピアのコンテキスト。
#define POTR_INVALID_SOCKET
Definition potrContext.h:36
porter 内部ログマクロ定義ヘッダー。
#define POTR_LOG(level,...)
porter 内部ログ出力マクロ。
Definition potrLog.h:68
PotrPeerContext * peer_find_by_session(struct PotrContext_ *ctx, uint32_t session_id, int64_t session_tv_sec, int32_t session_tv_nsec)
session_triplet でピアを検索する。
void peer_send_fin(struct PotrContext_ *ctx, PotrPeerContext *peer)
ピアの全パスへ FIN パケットを送信する。
#define POTR_MUTEX_INIT(m)
void peer_path_clear(struct PotrContext_ *ctx, PotrPeerContext *peer, int path_idx)
ピアの特定パスをクリアしてスロットを未使用に戻す。
PotrPeerContext * peer_find_by_id(struct PotrContext_ *ctx, PotrPeerId peer_id)
peer_id でピアを検索する。
void peer_table_destroy(struct PotrContext_ *ctx)
ピアテーブルを破棄する。
PotrPeerContext * peer_create(struct PotrContext_ *ctx, const struct sockaddr_in *sender_addr, int path_idx)
新規ピアを作成する。
void peer_free(struct PotrContext_ *ctx, PotrPeerContext *peer)
ピアリソースを解放してスロットをクリアする。
static PotrPeerId allocate_peer_id(struct PotrContext_ *ctx)
#define POTR_MUTEX_DESTROY(m)
int peer_table_init(struct PotrContext_ *ctx)
ピアテーブルを初期化する。
static void peer_generate_session(PotrPeerContext *peer)
N:1 モード用ピアテーブル管理モジュールの内部ヘッダー。
セッションコンテキスト構造体。PotrHandle の実体。
PotrGlobalConfig global
グローバル設定。
int n_peers
現在の接続ピア数。
PotrServiceDef service
サービス定義。
PotrMutex peers_mutex
ピアテーブル保護用ミューテックス。
PotrSocket sock[POTR_MAX_PATH]
各パスの UDP ソケット。
int max_peers
ピアテーブルサイズ (service.max_peers から取得)。
uint32_t next_peer_id
次に発行するピア ID (単調増加、初期値 1)。
PotrPeerContext * peers
ピアテーブル (動的確保。max_peers エントリ)。
uint16_t window_size
スライディングウィンドウサイズ (パケット数)。
uint16_t max_payload
最大ペイロード長 (バイト)。
uint32_t max_message_size
1 回の potrSend で送信できる最大メッセージ長 (バイト)。デフォルト: POTR_MAX_MESSAGE_SIZE。
パケットに付与するセッション識別情報。
Definition packet.h:31
int32_t session_tv_nsec
セッション開始時刻 ナノ秒部。
Definition packet.h:35
int64_t service_id
サービス識別子。
Definition packet.h:32
uint32_t session_id
セッション識別子 (乱数)。
Definition packet.h:34
int64_t session_tv_sec
セッション開始時刻 秒部。
Definition packet.h:33
ネットワーク送受信用パケット構造体。
uint32_t session_id
セッション識別子 (NBO)。potrOpenService 時に決定する乱数。
uint16_t flags
パケット種別フラグ (POTR_FLAG_*) (NBO)。
uint16_t payload_len
ペイロード長 (バイト) (NBO)。
PotrWindow send_window
送信ウィンドウ (NACK 再送用)。
size_t frag_buf_len
現在のデータ長。
int frag_compressed
圧縮フラグ (非 0: 圧縮あり)。
int32_t session_tv_nsec
自セッション開始時刻 ナノ秒部。
int64_t session_tv_sec
自セッション開始時刻 秒部。
int active
1: 有効スロット, 0: 空き。
uint8_t * frag_buf
フラグメント結合バッファ (動的確保)。
PotrWindow recv_window
受信ウィンドウ (順序整列)。
int n_paths
アクティブパス数。ループ境界には使わず管理カウンタとして使用する。
int64_t peer_session_tv_sec
追跡中のピアセッション開始時刻 秒部。
struct sockaddr_in dest_addr[POTR_MAX_PATH]
送信先ソケットアドレス (インデックス = ctx->sock[] の添字)。未使用スロットは sin_family == 0。
uint32_t peer_session_id
追跡中のピアセッション識別子。
uint32_t session_id
自セッション識別子 (乱数)。
PotrPeerId peer_id
外部公開用ピア識別子 (単調増加カウンタから付与)。
int64_t path_last_recv_sec[POTR_MAX_PATH]
パスごとの最終受信時刻 秒部。未使用スロットは 0。
PotrMutex send_window_mutex
send_window 保護 (送信・受信・ヘルスチェックスレッド競合)。
int32_t peer_session_tv_nsec
追跡中のピアセッション開始時刻 ナノ秒部。
int32_t path_last_recv_nsec[POTR_MAX_PATH]
パスごとの最終受信時刻 ナノ秒部。
int encrypt_enabled
非 0 のとき暗号化有効。設定ファイルに有効な encrypt_key が存在するときに 1 に設定される。
int64_t service_id
サービス ID。
uint8_t encrypt_key[POTR_CRYPTO_KEY_SIZE]
AES-256-GCM 事前共有鍵 (32 バイト)。encrypt_enabled が 0 の場合は未使用。
int window_init(PotrWindow *win, uint32_t initial_seq, uint16_t window_size, uint16_t max_payload)
ウィンドウを初期化します。
Definition window.c:42
void window_destroy(PotrWindow *win)
ウィンドウが保持する動的確保バッファを解放します。
Definition window.c:110
スライディングウィンドウ管理モジュールの内部ヘッダー。