شبیه سازی شبکه Thread با OpenThread

1. مقدمه

26b7f4f6b3ea0700.png

OpenThread منتشر شده توسط گوگل یک پیاده سازی متن باز از پروتکل شبکه Thread است. Google Nest برای تسریع در توسعه محصولات برای خانه متصل، OpenThread را منتشر کرده است تا فناوری مورد استفاده در محصولات Nest را به طور گسترده در دسترس توسعه دهندگان قرار دهد.

مشخصات Thread یک پروتکل ارتباطی دستگاه به دستگاه بی سیم قابل اعتماد، ایمن و کم مصرف مبتنی بر IPv6 را برای برنامه های خانگی تعریف می کند. OpenThread تمام لایه های شبکه Thread از جمله IPv6، 6LoWPAN، IEEE 802.15.4 را با امنیت MAC، Mesh Link Establishment و Mesh Routing پیاده سازی می کند.

این Codelab شما را در شبیه سازی شبکه Thread در دستگاه های شبیه سازی شده راهنمایی می کند.

چیزی که یاد خواهید گرفت

  • چگونه زنجیره ابزار ساخت OpenThread را راه اندازی کنیم
  • چگونه یک شبکه Thread را شبیه سازی کنیم
  • نحوه احراز هویت گره های Thread
  • نحوه مدیریت شبکه Thread با OpenThread Daemon

آنچه شما نیاز دارید

  • git
  • دانش مقدماتی لینوکس، مسیریابی شبکه

2. سیستم ساخت را راه اندازی کنید

Git

Git برای تکمیل این Codelab مورد نیاز است. قبل از ادامه آن را دانلود و نصب کنید.

پس از نصب، دستورالعمل های مربوط به سیستم عامل خاص خود را برای دانلود و ساخت OpenThread دنبال کنید.

XCode برای Mac OS X

XCode برای نصب و ساخت OpenThread در Mac OS X مورد نیاز است.

پس از نصب XCode، ابزارهای خط فرمان XCode را نصب کنید:

$ xcode-select --install

ساخت بر روی Linux / Mac OS X

این دستورالعمل های نصب بر روی سرور اوبونتو 14.04 LTS و Mac OS X Sierra 10.12.6 آزمایش شده است.

OpenThread را نصب کنید. دستورات bootstrap مطمئن می شوند که زنجیره ابزار نصب شده است و محیط به درستی پیکربندی شده است:

$ mkdir -p ~/src
$ cd ~/src
$ git clone --recursive https://github.com/openthread/openthread.git
$ cd openthread
$ ./script/bootstrap

با استفاده از ویندوز

اگر ویندوز را ترجیح می دهید، توصیه می کنیم نسخه Docker این Codelab را امتحان کنید.

3. برنامه های OpenThread را بسازید

پس از اتمام نصب، نمونه برنامه OpenThread را بسازید. برای این Codelab از مثال شبیه سازی استفاده می کنیم.

$ cd ~/src/openthread
$ ./script/cmake-build simulation

اکنون OpenThread Daemon را بسازید:

$ ./script/cmake-build posix -DOT_DAEMON=ON

4. یک شبکه Thread را شبیه سازی کنید

برنامه مثالی که برای این Codelab استفاده می کنید، یک برنامه OpenThread حداقلی را نشان می دهد که پیکربندی OpenThread و رابط های مدیریتی را از طریق یک رابط خط فرمان اولیه (CLI) در معرض دید قرار می دهد.

این تمرین شما را از حداقل مراحل مورد نیاز برای پینگ کردن یک دستگاه Thread شبیه سازی شده از یک دستگاه Thread شبیه سازی شده دیگر عبور می دهد.

شکل زیر یک توپولوژی پایه شبکه Thread را توصیف می کند. برای این تمرین، ما دو گره را در دایره سبز شبیه سازی می کنیم: یک Thread Leader و Thread Router با یک اتصال واحد بین آنها.

6e3aa07675f902dc.png

یک گره را پینگ کنید

1. نود 1 را راه اندازی کنید

به دایرکتوری openthread بروید و فرآیند CLI را برای دستگاه Thread شبیه سازی شده با استفاده از باینری ot-cli-ftd ایجاد کنید.

$ cd ~/src/openthread
$ ./build/simulation/examples/apps/cli/ot-cli-ftd 1

توجه: اگر پس از اجرای این دستور، فرمان > را مشاهده نکردید، enter فشار دهید.

این باینری یک دستگاه OpenThread شبیه سازی شده در بالای POSIX را پیاده سازی می کند. درایور رادیویی IEEE 802.15.4 در بالای UDP پیاده‌سازی می‌شود (فریم‌های IEEE 802.15.4 درون بارهای UDP ارسال می‌شوند).

آرگومان 1 یک توصیفگر فایل است که کم اهمیت ترین بیت های "تخصیص کارخانه" IEEE EUI-64 را برای دستگاه شبیه سازی شده نشان می دهد. این مقدار همچنین هنگام اتصال به یک پورت UDP برای شبیه سازی رادیویی IEEE 802.15.4 (پورت = 9000 + توصیفگر فایل) استفاده می شود. هر نمونه از یک دستگاه Thread شبیه سازی شده در این Codelab از یک توصیفگر فایل متفاوت استفاده می کند.

توجه: هنگام تخم ریزی فرآیند برای دستگاه شبیه سازی شده، فقط از توصیف کننده های فایل 1 یا بیشتر همانطور که در این Codelab ذکر شده است استفاده کنید. یک توصیفگر فایل 0 برای استفاده های دیگر رزرو شده است.

یک مجموعه داده عملیاتی جدید ایجاد کنید و آن را به عنوان مجموعه فعال متعهد کنید. مجموعه داده عملیاتی پیکربندی شبکه Thread است که در حال ایجاد آن هستید.

> dataset init new
Done
> dataset
Active Timestamp: 1
Channel: 20
Channel Mask: 07fff800
Ext PAN ID: d6263b6d857647da
Mesh Local Prefix: fd61:2344:9a52:ede0/64
Network Key: e4344ca17d1dca2a33f064992f31f786
Network Name: OpenThread-c169
PAN ID: 0xc169
PSKc: ebb4f2f8a68026fc55bcf3d7be3e6fe4
Security Policy: 0, onrcb
Done

این مجموعه داده را به عنوان مجموعه فعال متعهد کنید:

> dataset commit active
Done

رابط IPv6 را بیاورید:

> ifconfig up
Done

شروع عملیات پروتکل Thread:

> thread start
Done

چند ثانیه صبر کنید و بررسی کنید که دستگاه به Thread Leader تبدیل شده است. Leader دستگاهی است که مسئول مدیریت تخصیص شناسه روتر است.

> state
leader
Done

آدرس های IPv6 اختصاص داده شده به رابط Thread Node 1 را مشاهده کنید (خروجی شما متفاوت خواهد بود):

> ipaddr
fd61:2344:9a52:ede0:0:ff:fe00:fc00
fd61:2344:9a52:ede0:0:ff:fe00:5000
fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6
fe80:0:0:0:94da:92ea:1353:4f3b
Done

به انواع آدرس IPv6 خاص توجه کنید:

  • با fd = mesh-local شروع می شود
  • با fe80 = پیوند محلی شروع می شود

انواع آدرس های مش-محلی بیشتر طبقه بندی می شوند:

  • حاوی ff:fe00 = روتر یاب (RLOC)
  • حاوی ff:fe00 = شناسه نقطه پایانی (EID) نیست

EID را در خروجی کنسول خود شناسایی کنید و آن را برای استفاده بعدی یادداشت کنید. در خروجی نمونه بالا، EID عبارت است از:

fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6

2. نود 2 را راه اندازی کنید

یک ترمینال جدید باز کنید و به دایرکتوری openthread بروید و فرآیند CLI را ایجاد کنید. این دومین دستگاه Thread شبیه سازی شده شما است:

$ cd ~/src/openthread
$ ./build/simulation/examples/apps/cli/ot-cli-ftd 2

توجه: اگر پس از اجرای این دستور، فرمان > را مشاهده نکردید، enter فشار دهید.

کلید شبکه Thread و PAN ID را با استفاده از مقادیر مشابه مجموعه داده عملیاتی Node 1 پیکربندی کنید:

> dataset networkkey e4344ca17d1dca2a33f064992f31f786
Done
> dataset panid 0xc169
Done

این مجموعه داده را به عنوان مجموعه فعال متعهد کنید:

> dataset commit active
Done

رابط IPv6 را بیاورید:

> ifconfig up
Done

شروع عملیات پروتکل Thread:

> thread start
Done

دستگاه خود را به عنوان یک کودک راه اندازی می کند. Thread Child معادل یک دستگاه End است، که یک دستگاه Thread است که فقط با یک دستگاه والد، ترافیک unicast را ارسال و دریافت می کند.

> state
child
Done

در عرض 2 دقیقه باید تغییر حالت از child به router را مشاهده کنید. Thread Router می تواند ترافیک بین دستگاه های Thread را مسیریابی کند. به آن پدر و مادر نیز گفته می شود.

> state
router
Done

شبکه را تایید کنید

یک راه آسان برای تأیید شبکه مش، نگاه کردن به جدول روتر است.

1. اتصال را بررسی کنید

در Node 2، RLOC16 را دریافت کنید. RLOC16 آخرین 16 بیت آدرس RLOC IPv6 دستگاه است.

> rloc16
5800
Done

در Node 1، جدول روتر را برای RLOC16 Node 2 بررسی کنید. اطمینان حاصل کنید که Node 2 ابتدا به وضعیت روتر تغییر کرده است.

> router table
| ID | RLOC16 | Next Hop | Path Cost | LQI In | LQI Out | Age | Extended MAC  |
+----+--------+----------+----------+-------+---------+-----+------------------+
| 20 | 0x5000 |       63 |         0 |     0 |      0 |   0 | 96da92ea13534f3b |
| 22 | 0x5800 |       63 |         0 |     3 |      3 |  23 | 5a4eb647eb6bc66c |

RLOC نود 1 با 0xa800 در جدول یافت می شود که تأیید می کند که به مش متصل است.

2. نود 1 را از نود 2 پینگ کنید

اتصال بین دو دستگاه Thread شبیه سازی شده را بررسی کنید. در Node 2، EID اختصاص داده شده به Node 1 را ping :

> ping fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6
> 16 bytes from fd61:2344:9a52:ede0:d041:c5ba:a7bc:5ce6: icmp_seq=1
hlim=64 time=12ms

برای بازگشت به فرمان > CLI enter فشار دهید.

شبکه را تست کنید

اکنون که می توانید با موفقیت بین دو دستگاه Thread شبیه سازی شده پینگ کنید، شبکه مش را با آفلاین کردن یک گره آزمایش کنید.

به Node 1 برگردید و Thread را متوقف کنید:

> thread stop
Done

به Node 2 بروید و وضعیت را بررسی کنید. در عرض دو دقیقه، Node 2 تشخیص می دهد که لیدر (Node 1) آفلاین است، و شما باید Node 2 Transition را به عنوان leader شبکه ببینید:

> state
router
Done
...
> state
leader
Done

پس از تأیید، Thread را متوقف کرده و Node 2 را به حالت Factory Reset کنید. بازنشانی کارخانه ای انجام می شود تا اطمینان حاصل شود که اعتبار شبکه Thread که در این تمرین استفاده کردیم به تمرین بعدی منتقل نمی شود.

> thread stop
Done
> factoryreset
>
> exit

همچنین تنظیم مجدد کارخانه و خروج از Node 1:

> factoryreset
>
> exit

برای بررسی همه دستورات CLI موجود، به مرجع OpenThread CLI مراجعه کنید.

5. تصدیق گره ها با راه اندازی

در تمرین قبلی، یک شبکه Thread با دو دستگاه شبیه سازی شده و اتصال تایید شده راه اندازی کردید. با این حال، این فقط اجازه می دهد تا ترافیک پیوند محلی IPv6 تایید نشده بین دستگاه ها منتقل شود. برای هدایت ترافیک جهانی IPv6 بین آنها (و اینترنت از طریق روتر مرزی Thread)، گره ها باید احراز هویت شوند.

برای احراز هویت، یک دستگاه باید به عنوان کمیسیونر عمل کند. کمیسیونر سرور احراز هویت منتخب در حال حاضر برای دستگاه‌های Thread جدید و مجوز دهنده برای ارائه اعتبار شبکه مورد نیاز برای پیوستن دستگاه‌ها به شبکه است.

در این تمرین از همان توپولوژی دو گره ای قبلی استفاده خواهیم کرد. برای احراز هویت، Thread Leader به عنوان کمیسیونر و Thread Router به عنوان Joiner عمل می کند.

d6a67e8a0d0b5dcb.png

1. یک شبکه ایجاد کنید

اگر تمرین قبلی را ادامه دهید، باید از قبل دو پنجره ترمینال باز داشته باشید. اگر نه، مطمئن شوید که دو مورد باز و آماده استفاده هستند. یکی به عنوان Node 1 و دیگری به عنوان Node 2 خدمت می کند.

در Node 1، فرآیند CLI را ایجاد کنید:

$ cd ~/src/openthread
$ ./build/simulation/examples/apps/cli/ot-cli-ftd 1

توجه: اگر پس از اجرای این دستور، فرمان > را مشاهده نکردید، enter فشار دهید.

یک مجموعه داده عملیاتی جدید ایجاد کنید، آن را به عنوان یک مجموعه فعال متعهد کنید و Thread را شروع کنید:

> dataset init new
Done
> dataset
Active Timestamp: 1
Channel: 12
Channel Mask: 07fff800
Ext PAN ID: e68d05794bf13052
Mesh Local Prefix: fd7d:ddf7:877b:8756/64
Network Key: a77fe1d03b0e8028a4e13213de38080e
Network Name: OpenThread-8f37
PAN ID: 0x8f37
PSKc: f9debbc1532487984b17f92cd55b21fc
Security Policy: 0, onrcb
Done

این مجموعه داده را به عنوان مجموعه فعال متعهد کنید:

> dataset commit active
Done

رابط IPv6 را بیاورید:

> ifconfig up
Done

شروع عملیات پروتکل Thread:

> thread start
Done

چند ثانیه صبر کنید و بررسی کنید که دستگاه به Thread Leader تبدیل شده است:

> state
leader
Done

2. نقش کمیسر را شروع کنید

در حالی که هنوز در Node 1 هستید، نقش کمیسیونر را شروع کنید:

> commissioner start
Done

به هر Joiner (با استفاده از علامت * ) با J01NME Joiner Credential اجازه دهید تا در شبکه راه اندازی شود. Joiner دستگاهی است که توسط یک مدیر انسانی به شبکه Thread راه اندازی شده اضافه می شود.

> commissioner joiner add * J01NME
Done

3. نقش Joiner را شروع کنید

در پنجره ترمینال دوم، یک فرآیند CLI جدید ایجاد کنید. این نود 2 است.

$ cd ~/src/openthread
$ ./build/simulation/examples/apps/cli/ot-cli-ftd 2

در Node 2، نقش Joiner را با استفاده از J01NME Joiner Credential فعال کنید.

> ifconfig up
Done
> joiner start J01NME
Done

برای تایید چند ثانیه صبر کنید...

Join success

به عنوان یک Joiner، دستگاه (Node 2) با موفقیت خود را با کمیسیونر (Node 1) احراز هویت کرده و اعتبار شبکه Thread را دریافت کرده است.

اکنون که Node 2 احراز هویت شد، Thread را شروع کنید:

> thread start
Done

4. تأیید اعتبار شبکه

state گره 2 را بررسی کنید تا تأیید کنید که اکنون به شبکه پیوسته است. در عرض دو دقیقه، Node 2 از child به router منتقل می شود:

> state
child
Done
...
> state
router
Done

5. تنظیمات را بازنشانی کنید

برای آماده شدن برای تمرین بعدی، پیکربندی را بازنشانی کنید. در هر گره، Thread را متوقف کنید، بازنشانی کارخانه ای انجام دهید و از دستگاه Thread شبیه سازی شده خارج شوید:

> thread stop
Done
> factoryreset
>
> exit

ممکن است مجبور شوید چند بار enter فشار دهید تا فرمان > را پس از یک فرمان factoryreset بازگردانید.

6. شبکه را با OpenThread Daemon مدیریت کنید

برای این تمرین، یک نمونه CLI (یک دستگاه SoC Thread منفرد تعبیه شده) و یک نمونه رادیویی Co-Processor (RCP) را شبیه سازی می کنیم.

ot-daemon حالتی از برنامه OpenThread Posix است که از یک سوکت یونیکس به عنوان ورودی و خروجی استفاده می کند تا هسته OpenThread بتواند به عنوان یک سرویس اجرا شود. یک کلاینت می تواند با اتصال به سوکت با استفاده از OpenThread CLI به عنوان پروتکل با این سرویس ارتباط برقرار کند.

ot-ctl یک CLI است که توسط ot-daemon برای مدیریت و پیکربندی RCP ارائه شده است. با استفاده از این، RCP را به شبکه ایجاد شده توسط دستگاه Thread متصل می کنیم.

از ot-daemon استفاده کنید

این تمرین از سه پنجره ترمینال مربوط به موارد زیر استفاده می کند:

  1. نمونه CLI دستگاه Thread شبیه سازی شده (گره 1)
  2. فرآیند ot-daemon
  3. نمونه ot-ctl CLI

اگر تمرین قبلی را ادامه دهید، باید دو پنجره پایانه باز داشته باشید. یک سوم را باز کنید تا مطمئن شوید که سه پنجره ترمینال برای این تمرین در دسترس دارید.

1. نود 1 را راه اندازی کنید

در اولین پنجره ترمینال، فرآیند CLI را برای دستگاه Thread شبیه سازی شده خود ایجاد کنید:

$ cd ~/src/openthread
$ ./build/simulation/examples/apps/cli/ot-cli-ftd 1

توجه: اگر پس از اجرای این دستور، فرمان > را مشاهده نکردید، enter فشار دهید.

یک مجموعه داده عملیاتی جدید ایجاد کنید، آن را به عنوان یک مجموعه فعال متعهد کنید و Thread را شروع کنید:

> dataset init new
Done
> dataset
Active Timestamp: 1
Channel: 13
Channel Mask: 07fff800
Ext PAN ID: 97d584bcd493b824
Mesh Local Prefix: fd55:cf34:dea5:7994/64
Network Key: ba6e886c7af50598df1115fa07658a83
Network Name: OpenThread-34e4
PAN ID: 0x34e4
PSKc: 38d6fd32c866927a4dfcc06d79ae1192
Security Policy: 0, onrcb
Done

این مجموعه داده را به عنوان مجموعه فعال متعهد کنید:

> dataset commit active
Done

رابط IPv6 را بیاورید:

> ifconfig up
Done

شروع عملیات پروتکل Thread:

> thread start
Done

مشاهده آدرس های IPv6 اختصاص داده شده به رابط Thread Node 1:

> ipaddr
fd55:cf34:dea5:7994:0:ff:fe00:fc00
fd55:cf34:dea5:7994:0:ff:fe00:d000
fd55:cf34:dea5:7994:460:872c:e807:c4ab
fe80:0:0:0:9cd8:aab6:482f:4cdc
Done
>

همانطور که در مرحله شبیه سازی یک موضوع شبکه توضیح داده شد، یک آدرس لینک-محلی ( fe80 ) و سه آدرس مش-محلی ( fd ) هستند. EID یک آدرس محلی مش است که حاوی ff:fe00 در آدرس نیست. در این خروجی نمونه، EID fd55:cf34:dea5:7994:460:872c:e807:c4ab است.

EID خاص را از خروجی ipaddr خود که برای ارتباط با گره استفاده می شود، شناسایی کنید.

2. ot-daemon را شروع کنید

در پنجره ترمینال دوم، به دایرکتوری openthread بروید و ot-daemon -v یک گره RCP شروع کنید، که آن را Node 2 می نامیم. حتما از sudo استفاده کنید:

$ cd ~/src/openthread
$ sudo ./build/posix/src/posix/ot-daemon -v \
    'spinel+hdlc+forkpty://build/simulation/examples/apps/ncp/ot-rcp?forkpty-arg=2'

در صورت موفقیت آمیز بودن، ot-daemon در حالت کلامی خروجی مشابه زیر تولید می کند:

ot-daemon[12463]: Running OPENTHREAD/thread-reference-20200818-1938-g0f10480ed; POSIX; Aug 30 2022 10:55:05
ot-daemon[12463]: Thread version: 4
ot-daemon[12463]: Thread interface: wpan0
ot-daemon[12463]: RCP version: OPENTHREAD/thread-reference-20200818-1938-g0f10480ed; SIMULATION; Aug 30 2022 10:54:10

این ترمینال را باز بگذارید و در پس زمینه اجرا شود. شما هیچ دستور دیگری را در آن وارد نخواهید کرد.

3. برای پیوستن به شبکه از ot-ctl استفاده کنید

ما هنوز Node 2 (RCP ot-daemon ) را به هیچ شبکه Thread راه اندازی نکرده ایم. اینجا جایی است که ot-ctl وارد می شود. ot-ctl از همان CLI برنامه OpenThread CLI استفاده می کند. بنابراین، می توانید گره های ot-daemon را به همان شیوه ای که سایر دستگاه های Thread شبیه سازی شده کنترل کنید.

در پنجره ترمینال سوم، ot-ctl شروع کنید:

$ sudo ./build/posix/src/posix/ot-ctl
>

توجه: اگر پس از اجرای این دستور، فرمان > را مشاهده نکردید، enter فشار دهید.

شما ot-ctl در این پنجره ترمینال سوم برای مدیریت Node 2 (گره RCP) که در پنجره ترمینال دوم با ot-daemon شروع کرده اید استفاده خواهید کرد. state Node 2 را بررسی کنید:

> state
disabled
Done

برای محدود کردن اتصال به Joiner خاص، eui64 Node 2 را دریافت کنید:

> eui64
18b4300000000001
Done

در Node 1 (اولین پنجره ترمینال)، Commiser را راه اندازی کنید و اتصال را فقط به آن eui64 محدود کنید:

> commissioner start
Done
> commissioner joiner add 18b4300000000001 J01NME
Done

در Node 2 (سومین پنجره ترمینال)، رابط شبکه را باز کرده و به شبکه بپیوندید:

> ifconfig up
Done
> joiner start J01NME
Done

برای تایید چند ثانیه صبر کنید...

Join success

به عنوان یک Joiner، RCP (Node 2) با موفقیت خود را با کمیسیونر (Node 1) احراز هویت کرده و اعتبار شبکه Thread را دریافت کرده است.

حالا Node 2 را به شبکه Thread ملحق کنید:

> thread start
Done

4. تأیید اعتبار شبکه

state گره 2 را بررسی کنید تا تأیید کنید که اکنون به شبکه پیوسته است. در عرض دو دقیقه، Node 2 از child به router منتقل می شود:

> state
child
Done
...
> state
router
Done

5. اعتبارسنجی اتصال

با استفاده از دستورات Ctrl+D یا exit از ot-ctl خارج شوید و در خط فرمان ماشین میزبان خود، Node 1 را با استفاده از EID آن با دستور ping6 پینگ کنید. اگر نمونه ot-daemon RCP با موفقیت به شبکه Thread متصل شود و با آن ارتباط برقرار کند، پینگ موفق می شود:

$ ping6 -c 4 fd55:cf34:dea5:7994:460:872c:e807:c4ab
PING fd55:cf34:dea5:7994:460:872c:e807:c4ab (fd55:cf34:dea5:7994:460:872c:e807:c4ab): 56 data bytes
64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=0 ttl=64 time=4.568 ms
64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=1 ttl=64 time=6.396 ms
64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=2 ttl=64 time=7.594 ms
64 bytes from fd55:cf34:dea5:7994:460:872c:e807:c4ab: icmp_seq=3 ttl=64 time=5.461 ms
--- fd55:cf34:dea5:7994:460:872c:e807:c4ab ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 4.568/6.005/7.594/1.122 ms

7. تبریک می گویم!

شما با موفقیت اولین شبکه Thread خود را با استفاده از OpenThread شبیه سازی کرده اید. عالی!

در این Codelab شما یاد گرفتید که چگونه:

  • زنجیره ابزار ساخت OpenThread را راه اندازی کنید
  • شبیه سازی یک شبکه Thread
  • احراز هویت گره های Thread
  • یک شبکه Thread را با OpenThread Daemon مدیریت کنید

اگر می خواهید بیشتر بدانید، این منابع را بررسی کنید: