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:
| field | type | توضیح |
|---|---|---|
name | string | نام دلخواه node؛ باید در فایل config یکتا باشد. |
type | string | باید دقیقاً "SoftIpLimiter" باشد. |
settings | object | تنظیمات SoftIpLimiter؛ نباید خالی باشد. |
next | string | الزامی است. ترافیک پذیرفتهشده به این node فرستاده میشود. |
تنظیمات الزامی:
| field | type | توضیح |
|---|---|---|
identifier | string | باید دقیقاً vless یا trojan باشد. |
simultaneous-user-limit | integer | تعداد source IPهای متفاوتی که اجازه دارند همزمان از یک identity استفاده کنند. مقدار مجاز 1 تا 6 است. |
tolerance-ms | integer | مدت بیفعالیتی بر حسب millisecond که پس از آن یک ردیف IP قدیمی محسوب میشود. باید دستکم 1 باشد. |
تنظیمات اختیاری
| field | default | توضیح |
|---|---|---|
on-identification-failure | passthrough | رفتار با بایتهایی که قطعاً برای identifier تنظیمشده معتبر نیستند: passthrough، line را بدون IP limit عبور میدهد و close آن را رد میکند. pass و pass-through نامهای مستعار passthrough هستند. |
verbose | false | warningهای مربوط به 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 را غیرفعال میکند.
SoftIpLimiternode 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 flags | kNodeFlagSupportsSplice |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayer4 |
layer_group_next_node | kNodeLayer4 |
required_padding_left | 0 بایت |