پرش به مطلب اصلی

SoftIpLimiter

state مسیر ادامه پیش از فراخوانی Init node بعدی ثبت می‌شود؛ بنابراین transport Est همگام بلافاصله و فقط یک بار منتقل می‌شود. ورودی re-entrant از Init، Est یا replay اولیه پشت بایت‌های قدیمی‌تر می‌ماند. صف موقت ترتیب حداکثر ۲ MiB و ۱٬۰۲۴ buffer دارد و در همان dispatch همگام تخلیه می‌شود؛ مسیر عادی مستقیم صفی نگه نمی‌دارد. سرریز، line از نوع borrowed را از طریق owner می‌بندد.

SoftIpLimiter یک tunnel میانی stream است که تعداد source IP‌های مجاز برای استفاده هم‌زمان از یک identity اولیه VLESS یا Trojan را محدود می‌کند. این node به دیتابیس user‌های WaterWall یا فهرست کاربران VLESS/Trojan نیاز ندارد؛ کلید limit را مستقیماً از بایت‌های identity موجود در TCP connection کاربر به دست می‌آورد.

این node باید بلافاصله پیش از parser مربوط به protocol خام یا پیش از raw TCP relay به سروری قرار بگیرد که آن parser را اجرا می‌کند:

  • با identifier: vless، پیش از VlessServer یا TCP relay به سرور VLESS بالادستی
  • با identifier: trojan، پیش از TrojanServer یا TCP relay به سرور Trojan بالادستی

این node از بایت‌های ابتدایی فقط به‌اندازه‌ای می‌خواند که برای تشخیص identity لازم است. پس از تشخیص، source IP را در یک table داخلی بررسی می‌کند و همان بایت‌های bufferشده را بدون تغییر می‌فرستد. رفتار پیش‌فرض برای identity تشخیص‌ناپذیر، عبور دادن connection بدون اعمال limit است؛ در deployment‌های سخت‌گیرانه می‌توانید آن را به close تغییر دهید. SoftIpLimiter کاربر را authenticate نمی‌کند، مقصد را تجزیه نمی‌کند، header را تغییر نمی‌دهد و payload را بازنویسی نمی‌کند. احراز هویت واقعی protocol همچنان بر عهده VlessServer یا TrojanServer بعدی است.

جایگاه رایج​

VLESS روی TCP خام برای محیط local یا تست:

TcpListener -> SoftIpLimiter(identifier=vless) -> VlessServer -> TcpUdpConnector

VLESS روی TLS برای deployment عمومی:

TcpListener -> TlsServer -> SoftIpLimiter(identifier=vless) -> VlessServer -> TcpUdpConnector

Trojan روی TLS:

TcpListener -> TlsServer -> SoftIpLimiter(identifier=trojan) -> TrojanServer -> TcpUdpConnector

Relay جلوی یک سرور VLESS/Trojan موجود:

TcpListener -> TlsServer -> SoftIpLimiter(identifier=trojan) -> TcpConnector(upstream Trojan server)

SoftIpLimiter را پس از هر لایه encryption یا transportی قرار دهید که بایت‌های VLESS/Trojan را پنهان می‌کند و پیش از server مربوط به protocol یا raw TCP relay بگذارید که آن بایت‌ها را مصرف یا عبور می‌دهد. این node فقط برای stream است و نباید در packet chain به کار برود.

شکل ترافیک قابل پشتیبانی​

SoftIpLimiter باید یک TCP stream ساده ببیند که نخستین بایت‌های application در آن همان identity مربوط به VLESS یا Trojan باشد. client‌های TLS تنها زمانی پشتیبانی می‌شوند که WaterWall پیش از این node، معمولاً با TlsServer، TLS را terminate کرده باشد.

WebSocket، gRPC، XHTTP، wrapper‌های مبتنی بر HTTP Host/header، UDP و packet chain‌ها پشتیبانی نمی‌شوند. در این حالت‌ها identity مربوط به VLESS/Trojan در ابتدای TCP stream دیده نمی‌شود و node قادر به شناسایی کاربر نیست.

نمونه VLESS​

در این نمونه هر UUID مربوط به VLESS می‌تواند هم‌زمان حداکثر از دو source IP فعال استفاده شود:

[
{
"name": "listener",
"type": "TcpListener",
"settings": {
"address": "0.0.0.0",
"port": 443
},
"next": "tls-server"
},
{
"name": "tls-server",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem"
},
"next": "soft-ip-limit"
},
{
"name": "soft-ip-limit",
"type": "SoftIpLimiter",
"settings": {
"identifier": "vless",
"simultaneous-user-limit": 2,
"tolerance-ms": 30000,
"on-identification-failure": "passthrough",
"verbose": false
},
"next": "vless-server"
},
{
"name": "vless-server",
"type": "VlessServer",
"settings": {
"uuid": "5783a3e7-e373-51cd-8642-c83782b807c5",
"connect": true,
"udp": true
},
"next": "outbound"
},
{
"name": "outbound",
"type": "TcpUdpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port"
}
}
]

نمونه Trojan​

در این نمونه هر identity مربوط به password در Trojan تنها از یک source IP فعال در هر لحظه قابل استفاده است:

[
{
"name": "listener",
"type": "TcpListener",
"settings": {
"address": "0.0.0.0",
"port": 443
},
"next": "tls-server"
},
{
"name": "tls-server",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem"
},
"next": "trojan-soft-ip-limit"
},
{
"name": "trojan-soft-ip-limit",
"type": "SoftIpLimiter",
"settings": {
"identifier": "trojan",
"simultaneous-user-limit": 1,
"tolerance-ms": 45000,
"on-identification-failure": "close",
"verbose": true
},
"next": "trojan-server"
},
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"password": "secret-password",
"connect": true,
"udp": true
},
"next": "outbound"
}
]

نمونه حداقلی برای relay، زمانی که سرور دیگری پس از WaterWall ترافیک Trojan را تجزیه می‌کند:

{
"name": "trojan-soft-ip-limit",
"type": "SoftIpLimiter",
"settings": {
"identifier": "trojan",
"simultaneous-user-limit": 1,
"tolerance-ms": 3000,
"verbose": false
},
"next": "tcp-connector"
}

فیلدهای اجباری​

فیلدهای top-level:

fieldtypeتوضیح
namestringنام دلخواه node؛ باید در فایل config یکتا باشد.
typestringباید دقیقاً "SoftIpLimiter" باشد.
settingsobjectتنظیمات SoftIpLimiter؛ نباید خالی باشد.
nextstringالزامی است. ترافیک پذیرفته‌شده به این node فرستاده می‌شود.

تنظیمات الزامی:

fieldtypeتوضیح
identifierstringباید دقیقاً vless یا trojan باشد.
simultaneous-user-limitintegerتعداد source IP‌های متفاوتی که اجازه دارند هم‌زمان از یک identity استفاده کنند. مقدار مجاز 1 تا 6 است.
tolerance-msintegerمدت بی‌فعالیتی بر حسب millisecond که پس از آن یک ردیف IP قدیمی محسوب می‌شود. باید دست‌کم 1 باشد.

تنظیمات اختیاری​

fielddefaultتوضیح
on-identification-failurepassthroughرفتار با بایت‌هایی که قطعاً برای identifier تنظیم‌شده معتبر نیستند: passthrough، line را بدون IP limit عبور می‌دهد و close آن را رد می‌کند. pass و pass-through نام‌های مستعار passthrough هستند.
verbosefalsewarning‌های مربوط به admission ردشده و بسته شدن payload‌های فعال را در log ثبت می‌کند.

استخراج Identity​

در mode vless، بایت‌های upstream باید با این prefix شروع شوند:

version:  1 byte, must be 00
uuid: 16 raw UUID bytes

شانزده بایت UUID با calcHashBytes(uuid, 16) به یک کلید داخلی ۶۴ بیتی تبدیل می‌شوند.

در mode trojan، بایت‌های upstream باید با hash استاندارد password شروع شوند:

password hash: 56 ASCII hex bytes, hex(SHA224(password))

SoftIpLimiter این ۵۶ نویسه hex را اعتبارسنجی و decode می‌کند، سپس ۲۸ بایت SHA224 را با calcHashBytes(sha224, 28) به یک کلید داخلی ۶۴ بیتی تبدیل می‌کند.

بایت‌های identity نه مصرف می‌شوند و نه بازنویسی. پس از پذیرفته شدن connection، تمام stream ذخیره‌شده در buffer بدون تغییر به node بعدی می‌رود.

رفتار شکست تشخیص​

تا زمانی که بایت‌های موجود برای تصمیم‌گیری کافی نباشند، SoftIpLimiter منتظر می‌ماند. وقتی بایت‌ها قطعاً برای mode تنظیم‌شده نامعتبر باشند، on-identification-failure رفتار بعدی را تعیین می‌کند.

در حالت پیش‌فرض passthrough، node برای آن line کاملاً شفاف عمل می‌کند: هیچ ردیفی برای identity ساخته نمی‌شود، simultaneous-user-limit و tolerance-ms اعمال نمی‌شوند، بایت‌های bufferشده بدون تغییر روی node بعدی بازپخش می‌شوند و payload‌ها و callback‌های بعدی lifecycle به شکل عادی عبور می‌کنند.

اگر VlessServer یا TrojanServer بعدی fallback خودش را دارد، یا chain عمداً probe‌های غیر VLESS/Trojan را هم می‌پذیرد، از passthrough استفاده کنید. اگر early data نامعتبر نباید به node بعدی برسد، close را انتخاب کنید.

رفتار Limit​

limit برای هر identity جداگانه است، نه سراسری برای کل node. اگر simultaneous-user-limit برابر 2 باشد، یک UUID یا password hash مربوط به Trojan می‌تواند هم‌زمان از دو source IP متفاوت استفاده شود. source IP سوم برای همان identity رد می‌شود، اما هر identity دیگر فهرست IP مستقل خودش را دارد.

چند connection با source IP و identity یکسان فقط یک IP محسوب می‌شوند. ردیف مربوط reference count دارد و تا آزاد شدن آخرین line زنده آن source IP یا قدیمی شدن ردیف، باقی می‌ماند.

رفتار Tolerance​

tolerance-ms تنها هنگام دسترسی به table بررسی می‌شود:

  • هنگام admission یک connection جدید
  • پیش از فرستادن upstream payload
  • پیش از فرستادن downstream payload
  • هنگام release شدن row توسط یک line

timer پس‌زمینه‌ای وجود ندارد؛ بنابراین ممکن است یک line ساکت بیشتر از tolerance-ms idle بماند. اگر connection یا payload دیگری همان identity را لمس کند و ردیف IP مربوط به line ساکت حذف شود، آن line با payload بعدی خودش بسته خواهد شد. اتصال دوباره از همان IP پس از انقضا، ردیف مستقلی می‌سازد؛ line قدیمی نمی‌تواند ردیف جایگزین را تمدید یا آزاد کند.

اگر client‌ها معمولاً connection‌های idle را مدت زیادی نگه می‌دارند، tolerance را بزرگ‌تر بگذارید. برای آزاد شدن سریع‌تر slot‌های IP قدیمی، مقدار کوچک‌تری انتخاب کنید.

با مقدارهای خیلی بزرگ مراقب باشید. با simultaneous-user-limit: 1، اگر tolerance-ms را مثلاً یک ساعت بگذارید، وقتی کاربر از اینترنت گوشی به Wi-Fi جابه‌جا شود، IP قبلی ممکن است تا یک ساعت تنها slot همان identity را نگه دارد. برای client‌های mobile یا کاربرهایی که network عوض می‌کنند، مقدار کوتاهی مثل 3000 تا 5000 ms معمولاً نقطه شروع عملی‌تری است.

نیاز به source IP​

برای identity‌های تشخیص‌داده‌شده، SoftIpLimiter به یک source IP معتبر در source context مربوط به line نیاز دارد. listener‌های معمول stream مانند TcpListener این مقدار را فراهم می‌کنند.

اگر source context وجود نداشته باشد یا از نوع IPv4/IPv6 نباشد، connection پذیرفته نمی‌شود.

اگر تشخیص identity شکست بخورد و on-identification-failure برابر passthrough باشد، source-IP row ساخته نمی‌شود و این نیازمندی source IP برای آن line استفاده نمی‌شود.

Logging​

این node به‌طور پیش‌فرض log اضافه‌ای نمی‌نویسد.

وقتی verbose برابر true باشد، admission‌های ردشده و بسته شدن payload‌های فعال همراه mode مربوط به identifier، کلید داخلی ۶۴ بیتی، source IP، worker id، دلیل، تعداد IP فعلی و limit تنظیم‌شده ثبت می‌شوند. UUID خام VLESS و SHA224 خام Trojan هرگز در log نوشته نمی‌شوند.

نکته‌های عملیاتی​

این node فلگ kNodeFlagSupportsSplice دارد. تا زمانی که identity مشخص نشده، هر payload ورودی را به‌طور کامل با sbufEnsureOrdinary() به buffer معمولی تبدیل می‌کند تا parser بتواند بایت‌ها را بخواند. پس از پذیرش identity یا انتخاب passthrough، buffer‌های splice در هر دو جهت بدون تبدیل عبور می‌کنند. ورودی تودرتو در FIFO موجود، پس از replay اولیه قرار می‌گیرد. این بررسی موقت، ordinary read دائمی را درخواست نمی‌کند. فعال شدن splice به پشتیبانی کل chain نهایی وابسته است؛ حضور node مربوط به TLS در همان chain، splice را غیرفعال می‌کند.

  • SoftIpLimiter node authentication نیست. VlessServer یا TrojanServer بعدی هنوز authentication را انجام می‌دهد.
  • می‌تواند قبل از VlessServer/TrojanServer محلی قرار بگیرد، یا قبل از یک TcpConnector که ترافیک را به سرور VLESS/Trojan بالادستی relay می‌کند.
  • به fail2ban یا limiter خارجی نیاز ندارد؛ table مربوط به identity/IP در خود WaterWall نگه‌داری می‌شود.
  • early data نامعتبر یا قابل تشخیص نبودن به‌صورت پیش‌فرض عبور داده می‌شود. برای reject شدن در همین node، on-identification-failure را close بگذارید.
  • این node username را نمی‌شناسد. key فقط از UUID اولیه VLESS یا مقدار SHA224 اولیه Trojan ساخته می‌شود.
  • اگر simultaneous-user-limit برابر 1 باشد، یک identity همچنان می‌تواند چند line هم‌زمان از همان source IP باز کند.
  • اگر client‌ها بین network‌ها جابه‌جا می‌شوند، limit را آن‌قدر بالا بگذارید که overlap معمول را پوشش دهد.
  • table در tunnel state قرار دارد، میان worker‌های همان tunnel instance مشترک است و با RW lock محافظت می‌شود.
  • وقتی می‌خواهید بفهمید connection به‌خاطر IP limit reject یا active-close شده یا نه، موقتا verbose را فعال کنید.

اشتباه‌های رایج​

  • قرار دادن SoftIpLimiter(identifier=trojan) پیش از TlsServer؛ در آن نقطه بایت‌های Trojan هنوز رمز شده‌اند و limiter نمی‌تواند connection را شناسایی کند.
  • انتظار پشتیبانی از WebSocket، gRPC، XHTTP، یا config‌هایی که HTTP Host/header دارند.
  • قرار دادن SoftIpLimiter بعد از VlessServer یا TrojanServer. این limiter باید raw protocol identity را قبل از مصرف شدن توسط parser ببیند.
  • انتظار authentication از این node.
  • انتظار اعمال limit روی line‌های pass-through پیش‌فرض؛ این line‌ها بدون ردیف identity عبور می‌کنند. برای enforcement سخت‌گیرانه از "on-identification-failure": "close" استفاده کنید.
  • تنظیم simultaneous-user-limit بیشتر از 6.
  • استفاده از این node در packet chain‌ها.

متادیتای node​

متادیتای برگرفته از کد منبع:

ویژگیمقدار
node flagskNodeFlagSupportsSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left0 بایت