> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# ربط المقسم PBX بشبكة الهاتف عبر SIP Trunk والأرقام DID
- URL: https://arabroot.io/articles/ربط-المقسم-بشبكة-الهاتف-عبر-sip-trunk/
- Published: 2026-10-19T06:15:00.000Z
- Updated: 2026-10-20T20:46:24.000Z
- Description: نبني مزود خدمة ومقسماً في Docker ونربطهما بخط SIP Trunk، ونوجه الأرقام DID ونضبط هوية المتصل بصيغة E.164 ونغلق ثغرة في الخط.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, الاستضافة الذاتية والتطبيقات, الشبكات والوصول, تطبيقات لفريقك, الاستضافة الذاتية

المقسم الذي بنيناه في [تتبع مكالمة SIP عبر الشبكة](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9-sip-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D9%88%D8%A7%D8%AD%D8%AF/) يربط موظفي المكتب ببعضهم، ولكن العميل لا يتصل بالتحويلة 1001، وإنما برقم الشركة المكتوب على موقعها، والموظف يحتاج أن يتصل بجوال العميل. وهذا يعني أن مقسمك يحتاج طريقاً إلى شبكة الهاتف العامة، وأرقاماً حقيقية يعرفها الناس، وهنا يأتي دور الخط الخارجي SIP Trunk والأرقام المباشرة DID.

المواضيع في السلسلة:

- [مصطلحات الاتصالات الهاتفية VoIP للمبتدئين](https://arabroot.io/articles/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-voip-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)
- [تتبع مكالمة SIP عبر الشبكة: لماذا يصل الصوت في اتجاه واحد؟](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9-sip-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D9%88%D8%A7%D8%AD%D8%AF/)
- [لماذا يتقطع الصوت في مكالمات VoIP؟ قياس Jitter وPacket Loss](https://arabroot.io/articles/%D9%84%D9%85%D8%A7%D8%B0%D8%A7-%D9%8A%D8%AA%D9%82%D8%B7%D8%B9-%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A7%D8%AA-voip/)
- ربط المقسم PBX بشبكة الهاتف عبر SIP Trunk والأرقام DID (الموضوع الحالي)
- [دليل Asterisk العملي: من التحويلة Extension إلى الرد الآلي IVR](https://arabroot.io/articles/%D8%AF%D9%84%D9%8A%D9%84-asterisk-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A-%D9%85%D9%86-%D8%A7%D9%84%D8%AA%D8%AD%D9%88%D9%8A%D9%84%D8%A9-%D8%A7%D9%84%D9%89-%D8%A7%D9%84%D8%B1%D8%AF-%D8%A7%D9%84%D8%A7%D9%84%D9%8A/)
- [FreePBX: مقسم هاتف PBX كامل تديره من المتصفح](https://arabroot.io/articles/freepbx-%D9%85%D9%82%D8%B3%D9%85-%D9%87%D8%A7%D8%AA%D9%81-%D9%83%D8%A7%D9%85%D9%84-%D8%AA%D8%AF%D9%8A%D8%B1%D9%87-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD/)
- [FreeSWITCH للمنصات الكبيرة ومراكز الاتصال Contact Center](https://arabroot.io/articles/freeswitch-%D9%84%D9%84%D9%85%D9%86%D8%B5%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A8%D9%8A%D8%B1%D8%A9-%D9%88%D9%85%D8%B1%D8%A7%D9%83%D8%B2-%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84/)
- [3CX أم FreePBX أم FreeSWITCH؟ كيف تختار المقسم PBX](https://arabroot.io/articles/3cx-%D8%A7%D9%85-freepbx-%D8%A7%D9%85-freeswitch/)
- [هواتف SIP المكتبية والتطبيقات: لماذا لا يرن الجوال النائم؟](https://arabroot.io/articles/%D9%87%D9%88%D8%A7%D8%AA%D9%81-sip-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84/)
- [مركز اتصال Call Center على Asterisk: الطابور وتقرير SLA](https://arabroot.io/articles/%D9%85%D8%B1%D9%83%D8%B2-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-call-center-%D8%B9%D9%84%D9%89-asterisk/)
- [تأمين المقسم من الاحتيال على المكالمات Toll Fraud](https://arabroot.io/articles/%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D9%82%D8%B3%D9%85-%D9%85%D9%86-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D9%84-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A7%D8%AA/)
- [موازنة الحمل Load Balancing أمام خوادم Asterisk مع Kamailio](https://arabroot.io/articles/%D9%85%D9%88%D8%A7%D8%B2%D9%86%D8%A9-%D8%A7%D9%84%D8%AD%D9%85%D9%84-%D8%A3%D9%85%D8%A7%D9%85-%D8%AE%D9%88%D8%A7%D8%AF%D9%85-asterisk-%D9%85%D8%B9-kamailio/)

ولأنك لن تحصل على خط حقيقي من مزود لتجربة، سوف نبني المزود نفسه: مقسم Asterisk ثان يلعب دور شركة الاتصالات، وفيه مشتركون بأرقام جوال، ونربط به مقسم المكتب كما يربط أي مقسم بمزوده، ثم نكسر الإعداد في كل خطوة لنرى الأعطال التي تقابلها عند أول ربط حقيقي. وسوف نتناول في هذا المقال ما يلي:

- ما هو SIP Trunk وDID، وصيغة الأرقام الدولية E.164، وأرقام المملكة كما تنشرها هيئة الاتصالات والفضاء والتقنية.
- الفرق بين الخط بالتسجيل Registration والخط بعنوان IP، ومتى تختار كلاً منهما.
- ربط مقسم المكتب بالمزود بالتسجيل، وتوجيه المكالمات القادمة بحسب الرقم المطلوب، وأربعة أعطال حقيقية في الطريق.
- المكالمات الخارجة: تحويل الرقم المحلي إلى E.164، وهوية المتصل Caller ID، ولماذا يرفض المزود المكالمة ب 403.
- الخط بعنوان IP، وثغرة تجعل أي جهاز في الشبكة يتصل باسم المقسم دون كلمة مرور، وكيف تغلقها.

## ما هو SIP Trunk؟

قبل VoIP كان المقسم يتصل بشركة الاتصالات بخطوط نحاسية، كل خط تماثلي Analog يحمل مكالمة واحدة، أو بخط رقمي يحمل 30 مكالمة في الدول التي تعمل بنظام E1\. أما الخط الخارجي SIP Trunk فهو الاتفاق نفسه ولكن عبر الإنترنت أو عبر وصلة خاصة من المزود: مقسمك يرسل رسائل SIP إلى سيرفر المزود، والمزود يوصل المكالمة إلى أي هاتف في العالم، وعدد المكالمات في الوقت نفسه يحدده العقد وسرعة الخط وليس عدد الأسلاك. والصورة التالية تبين الأطراف:

![مكتب فيه ثلاث تحويلات 1001 و1002 و1003 متصلة بمقسم PBX، والمقسم متصل عبر الإنترنت بخط SIP Trunk يحمل SIP وRTP إلى مزود خدمة ITSP عنده كتلة أرقام DID من +966 11 000 0100 إلى +966 11 000 0109، والمزود متصل بشبكة الهاتف العامة والجوال وفيها هاتف برقم +966 55 000 0123](https://arabroot.io/content/images/2026/10/voip-trunk-01-trunk-overview.webp)

خط واحد عبر الإنترنت يحمل مكالمات كل الأرقام، والمقسم يقرر أي تحويلة ترن

وفيما يلي المصطلحات التي سوف تقابلها في أي عقد أو لوحة تحكم لمزود:

- مزود خدمة الاتصال عبر الإنترنت Internet Telephony Service Provider واختصاراً ITSP: الشركة التي تبيعك الخط والأرقام، وقد تكون شركة الاتصالات نفسها أو شركة مرخصة أخرى.
- الرقم المباشر Direct Inward Dialing واختصاراً DID: رقم هاتف حقيقي يملكه المزود ويوجه مكالماته إلى خطك، وغالباً تشتري كتلة أرقام متتالية، فيكون للاستقبال رقم وللمبيعات رقم وللمدير رقم، وكلها تأتي عبر الخط نفسه.
- القنوات Channels أو المكالمات المتزامنة Concurrent Calls: الحد الأقصى للمكالمات التي يمررها الخط في الوقت نفسه، وهو ما تدفع عليه في أغلب العقود.
- هوية المتصل Caller ID أو CLI: الرقم الذي يظهر عند من تتصل به، والمزود يقرر هل يقبل ما ترسله أم يستبدله.

## صيغة الأرقام E.164

الموظف في المكتب يطلب الجوال بالشكل الذي يعرفه، أي `0550000123`، ولكن هذا الشكل لا معنى له خارج المملكة، فالصفر في البداية يعني «رقم محلي» في بلد ويعني شيئاً آخر في بلد آخر. لذلك تتفاهم شبكات الهاتف بصيغة واحدة تعرفها توصية [ITU-T E.164](https://www.itu.int/rec/T-REC-E.164?ref=arabroot.io): علامة `+` ثم رمز الدولة ثم الرقم الوطني دون الصفر، وبحد أقصى 15 رقماً. فالرقم أعلاه يصبح `+966550000123`، وأغلب المزودين يطلبون منك أن ترسل الأرقام بهذه الصيغة، ويرسلون لك الأرقام بها.

وفي المملكة تنشر هيئة الاتصالات والفضاء والتقنية [خطة الترقيم الوطنية](https://www.cst.gov.sa/en/about/Numbering?ref=arabroot.io)، ومنها ما يهمك عند كتابة خطة الاتصال:

| النوع                   | الشكل المحلي            | بصيغة E.164              |
| ----------------------- | ----------------------- | ------------------------ |
| جوال، مثال الهيئة نفسها | 0551234567، عشرة أرقام  | +966551234567            |
| ثابت في منطقة الرياض    | 011 ثم سبعة أرقام       | +96611 ثم سبعة أرقام     |
| مناطق الثابت الأخرى     | 012 و013 و014 و016 و017 | +96612 وهكذا             |
| رقم مجاني، مثال الهيئة  | 8001232222              | يحدد المزود طريقة إرساله |

ولاحظ أن كل الأرقام في التجربة وهمية، فقد اخترنا `+96611000010X` لكتلة الشركة و`+9665500001XX` للجوالات، لأن رقم المشترك لا يبدأ بأصفار، فلا يمكن أن تتصل بأحد بالخطأ إذا نسختها.

## الترخيص في المملكة

الاتصال بشبكة الهاتف العامة عبر الإنترنت خدمة منظمة، وفي صفحة [تصريح تقديم خدمات الصوت الافتراضية VVSP](https://www.cst.gov.sa/en/business/services/Permit-to-provide-virtual-voice-services-VVSP?ref=arabroot.io) تقول الهيئة إن هذا التصريح يمكن من تقديم خدمات تسمح للمستخدمين في المملكة بإجراء المكالمات واستقبالها مع شبكات الهاتف العامة PSTN عبر بروتوكول الإنترنت باستخدام ترقيم E.164، وإنه لمقدمي الخدمات المرخصين بحسب تنظيمات الهيئة، ورسومه المنشورة في الصفحة عند كتابة المقال 5000 ريال. وما يعنيك كمؤسسة تشغل مقسمها بنفسها أن تشتري الخط والأرقام من مزود مرخص، وأن تطلب منه شروطه في هوية المتصل وأرقام الطوارئ وتسجيل المكالمات قبل التوقيع، والسبب أن هذه الشروط تختلف بين المزودين وتحدد ما تكتبه في خطة الاتصال. ولا تقم بتمرير مكالماتك المحلية عبر مزود في الخارج لأن سعره أقل، فهذا ليس قراراً تقنياً فقط.

## كيف يعرفك المزود؟ التسجيل أم عنوان IP

عندما تصل رسالة INVITE إلى المزود، يحتاج أن يعرف أنها من مقسمك أنت وليست من شخص يريد أن يتصل على حسابك، وعندما يرسل المزود مكالمة إليك يحتاج أن يعرف أين يجدك. وهناك طريقتان شائعتان لذلك، والصورة التالية تبينهما:

![خط بالتسجيل: المقسم يرسل REGISTER باسم المستخدم acme وكلمة المرور، والمزود يرد 200 OK ويحفظ عنوانه، ثم يرسل المكالمات القادمة إلى العنوان المحفوظ، ويعمل من عنوان متغير وخلف NAT ولكن المقسم يجب أن يجدد التسجيل. وخط بعنوان IP: المزود يسمح بالعنوان الثابت 203.0.113.10 ويرسل المكالمات إليه، والمقسم يرسل INVITE دون كلمة مرور، ولا يوجد تسجيل يضيع ولكنه يحتاج عنواناً عاماً ثابتاً، والعنوان هو المفتاح الوحيد](https://arabroot.io/content/images/2026/10/voip-trunk-02-reg-vs-ip.webp)

الخطان يحملان المكالمات نفسها، والفرق في طريقة التعرف فقط

|                      | التسجيل Registration                         | عنوان IP Authentication                  |
| -------------------- | -------------------------------------------- | ---------------------------------------- |
| كيف يعرفك المزود     | اسم مستخدم وكلمة مرور في كل REGISTER وINVITE | عنوان IP الذي تأتي منه الرسالة           |
| كيف يجدك المزود      | العنوان الذي حفظه من آخر تسجيل               | عنوان ثابت كتبه في إعداده                |
| يحتاج عنواناً ثابتاً | لا، ويعمل خلف NAT                            | نعم                                      |
| يناسب                | المكاتب الصغيرة والإنترنت المنزلي            | المقسمات في مراكز البيانات والسحابة      |
| نقطة الضعف           | إذا سقط التسجيل لا تصلك المكالمات            | أي خطأ في مطابقة العنوان يفتح الخط لغيرك |

وسوف نبدأ بالتسجيل لأنه الأكثر شيوعاً مع المزودين، ثم نحول الخط نفسه إلى عنوان IP في آخر التجربة.

## بناء التجربة: مزود ومقسم وأربعة هواتف

التجربة كلها في شبكة Docker داخلية واحدة دون أي منفذ على جهازك، وفيها:

| ال Container            | العنوان          | الدور                                                                                  |
| ----------------------- | ---------------- | -------------------------------------------------------------------------------------- |
| lab-vt-carrier          | 10.89.10.5       | المزود: Asterisk يملك كتلة الأرقام +966110000100 إلى +966110000109 ويبيعها للعميل acme |
| lab-vt-office           | 10.89.10.10      | مقسم شركة acme، وفيه التحويلتان 1001 للاستقبال و1002 للمبيعات                          |
| lab-vt-ext1001 وext1002 | 10.89.10.21 و.22 | هاتفا المكتب، مسجلان في مقسم المكتب                                                    |
| lab-vt-mobile1 وmobile2 | 10.89.10.31 و.32 | جوالان على شبكة المزود برقمي +966550000123 و+966550000456                              |

ونستخدم ال Image نفسها من [المقال السابق](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9-sip-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D9%88%D8%A7%D8%AD%D8%AF/#lab-image)، أي Asterisk 20.6.0 وbaresip وtcpdump من حزم Ubuntu 24.04، باسم `lab-voip-tools:1`، مع السكربت `sipflow.sh` الذي يطبع سطراً لكل رسالة SIP في ملف الالتقاط. قم بإنشاء مجلدين `carrier` و`office`، وفي كل منهما الملفات `pjsip.conf` و`extensions.conf` و`rtp.conf` و`logger.conf` ونسخة من `sipflow.sh`.

### المزود

هذا هو `carrier/pjsip.conf`، وفيه حساب العميل acme الذي يسجل به مقسم المكتب، والجوالان:

```ini
[global]
type=global
user_agent=Lab-Carrier

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060

; Template for every endpoint on the carrier side
[ep](!)
type=endpoint
disallow=all
allow=alaw,ulaw
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes

; The customer trunk "acme": the office PBX registers with this account
[acme]
type=auth
auth_type=userpass
username=acme
password=Trunk-Lab-7f3k9q

[acme]
type=aor
max_contacts=1
remove_existing=yes

[acme](ep)
context=from-customer
auth=acme
aors=acme

; Two mobile subscribers on the public network
[mobile1]
type=auth
auth_type=userpass
username=mobile1
password=Mobile1-Lab-pass

[mobile1]
type=aor
max_contacts=1
remove_existing=yes

[mobile1](ep)
context=from-pstn
auth=mobile1
aors=mobile1
callerid=<+966550000123>

[mobile2]
type=auth
auth_type=userpass
username=mobile2
password=Mobile2-Lab-pass

[mobile2]
type=aor
max_contacts=1
remove_existing=yes

[mobile2](ep)
context=from-pstn
auth=mobile2
aors=mobile2
callerid=<+966550000456>
```

وخطة الاتصال عند المزود في `carrier/extensions.conf` فيها سياستان كما يفعل المزود الحقيقي: كل مكالمة إلى رقم من كتلة acme ترسل عبر خطها، وكل مكالمة تخرج من acme يجب أن تكون بصيغة E.164 وأن تحمل هوية متصل من أرقامها:

```ini
[from-pstn]
; DID block +966110000100 to +966110000109 belongs to the customer acme
exten => _+96611000010X,1,NoOp(DID ${EXTEN} for acme from ${CALLERID(num)})
 same => n,Dial(PJSIP/${EXTEN}@acme,30)
 same => n,Hangup()
exten => +966550000123,1,Dial(PJSIP/mobile1,30)
exten => +966550000456,1,Dial(PJSIP/mobile2,30)

[from-customer]
; Numbers must arrive in E.164 form: + then country code
exten => _X.,1,NoOp(Rejected, not E.164: ${EXTEN})
 same => n,Hangup(28)
exten => _+X.,1,NoOp(Outbound from acme to ${EXTEN} with caller ID ${CALLERID(num)})
 same => n,GotoIf($["${CALLERID(num):0:12}" = "+96611000010"]?allowed)
 same => n,NoOp(Caller ID ${CALLERID(num)} is not one of acme's numbers)
 same => n,Hangup(21)
 same => n(allowed),Goto(from-pstn,${EXTEN},1)
```

في الإعداد أعلاه لاحظ التالي:

- `Dial(PJSIP/${EXTEN}@acme)` يرسل المكالمة إلى العنوان الذي سجل منه مقسم acme، ويضع الرقم المطلوب في أول سطر من INVITE، أي `INVITE sip:+966110000105@…`، وهذا ما يفعله أغلب المزودين.
- النمط `_X.` يطابق أي رقم لا يبدأ ب `+`، فيرفضه بالسبب 28 الذي يتحول في SIP إلى `484 Address Incomplete`.
- السبب 21 يعني «رفض المكالمة» ويتحول إلى `403 Forbidden`، وهو ما يرسله المزود عندما تكون هوية المتصل ليست من أرقامك. وبعض المزودين يستبدلون الهوية برقمك الرئيسي بدل الرفض، فاسأل مزودك أي السلوكين يتبع.

### مقسم المكتب: الخط بالتسجيل

في مقسم المكتب نعرف الخط بأربعة أجزاء بالاسم نفسه `carrier`، وهذا هو الجزء الخاص بالخط في `office/pjsip.conf`، والتحويلتان بعده بالقالب نفسه الذي استخدمناه في المقالات السابقة:

```ini
[global]
type=global
user_agent=Acme-Office-PBX

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060

; ---------- The trunk to the carrier ----------
[carrier]
type=registration
outbound_auth=carrier
server_uri=sip:10.89.10.5
client_uri=sip:acme@10.89.10.5
retry_interval=30
expiration=300

[carrier]
type=auth
auth_type=userpass
username=acme
password=Trunk-Lab-7f3k9q

[carrier]
type=aor
contact=sip:10.89.10.5:5060

[carrier]
type=endpoint
context=from-trunk
disallow=all
allow=alaw,ulaw
outbound_auth=carrier
aors=carrier
from_user=acme
direct_media=no

; ---------- Office extensions ----------
[phone](!)
type=endpoint
context=office
disallow=all
allow=alaw,ulaw
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes

[1001]
type=auth
auth_type=userpass
username=1001
password=Lab-1001-pass

[1001]
type=aor
max_contacts=1
remove_existing=yes

[1001](phone)
auth=1001
aors=1001
callerid="Reception" <1001>

[1002]
type=auth
auth_type=userpass
username=1002
password=Lab-1002-pass

[1002]
type=aor
max_contacts=1
remove_existing=yes

[1002](phone)
auth=1002
aors=1002
callerid="Sales" <1002>
```

في الإعداد أعلاه لاحظ التالي:

- `type=registration` يجعل المقسم يسجل نفسه عند المزود كما يسجل الهاتف عند المقسم، ويجدد التسجيل قبل انتهاء `expiration`، ويعيد المحاولة كل 30 ثانية إذا فشل. والخيارات كلها موثقة في [صفحة res\_pjsip\_outbound\_registration](https://docs.asterisk.org/Latest%5FAPI/API%5FDocumentation/Module%5FConfiguration/res%5Fpjsip%5Foutbound%5Fregistration/?ref=arabroot.io).
- `outbound_auth` في ال endpoint يعني أن المقسم يستخدم كلمة المرور نفسها عندما يطلب منه المزود المصادقة على INVITE، وليس فقط على REGISTER.
- `aor` فيه عنوان ثابت للمزود، فالمقسم يعرف أين يرسل المكالمات الخارجة دون أن يسجل المزود عنده.
- `from_user=acme` يضع اسم الحساب في ترويسة `From`، وهو ما يطلبه كثير من المزودين ليعرفوا الحساب من أول رسالة، وسوف نرى أثره الجانبي بعد قليل.

وخطة الاتصال في `office/extensions.conf` فيها ثلاثة سياقات: `from-trunk` للمكالمات القادمة من المزود، و`office` لما يطلبه الموظفون، و`to-carrier` لكل ما يخرج:

```ini
[from-trunk]
; Calls from the carrier: the DID decides who rings
exten => +966110000100,1,Goto(office,1001,1)
exten => +966110000102,1,Goto(office,1002,1)
; Any other number of the block goes to reception
exten => _+96611000010X,1,NoOp(Unassigned DID ${EXTEN})
 same => n,Goto(office,1001,1)

[office]
exten => _10XX,1,Dial(PJSIP/${EXTEN},20)
 same => n,Hangup()
; Outgoing calls: local mobile 05XXXXXXXX and landline 01XXXXXXXX become E.164
exten => _05XXXXXXXX,1,Goto(to-carrier,+966${EXTEN:1},1)
exten => _01XXXXXXXX,1,Goto(to-carrier,+966${EXTEN:1},1)
exten => _00X.,1,Goto(to-carrier,+${EXTEN:2},1)

[to-carrier]
exten => _+X.,1,Set(CALLERID(num)=+966110000100)
 same => n,Dial(PJSIP/${EXTEN}@carrier,30)
 same => n,Hangup()
```

ولاحظ أن الرقم الأدق يغلب في Asterisk، فالرقم `+966110000102` يذهب إلى 1002 مع أنه يطابق النمط `_+96611000010X` أيضاً، وكل رقم آخر من الكتلة يذهب إلى الاستقبال حتى لا تضيع مكالمة على رقم لم توزعه بعد. وطريقة المطابقة بالأنماط موثقة في [صفحة Pattern Matching](https://docs.asterisk.org/Configuration/Dialplan/Pattern-Matching/?ref=arabroot.io). والملفان الباقيان هما نفسهما في المجلدين:

```bash
for h in carrier office; do
  printf '[general]\nrtpstart=10000\nrtpend=10099\n' > $h/rtp.conf
  printf '[general]\n[logfiles]\nconsole => notice,warning,error,verbose\nmessages => notice,warning,error,verbose(3)\n' > $h/logger.conf
done
```

والسطر `messages` يكتب تنفيذ خطة الاتصال في الملف `/var/log/asterisk/messages`، ومنه سوف نعرف أين ذهبت كل مكالمة.

### التشغيل والتسجيل

نشغل المقسمين، وكل منهما ينسخ ملفاته من المجلد إلى `/etc/asterisk` عند البدء، حتى نعدل الملفات ونعيد تحميلها بالسكربت `reload.sh`:

```bash
docker network create --internal --subnet 10.89.10.0/24 lab-vt-net
for n in carrier:5 office:10; do name=${n%%:*}; ip=${n#*:}
  docker run -d --name lab-vt-$name --network lab-vt-net --ip 10.89.10.$ip --cap-add NET_ADMIN --cap-add NET_RAW \
    -v "$PWD/$name:/lab" lab-voip-tools:1 sh -c 'cp /lab/*.conf /etc/asterisk/ && exec asterisk -f'
done
cat > reload.sh <<'EOF'
#!/bin/sh
# copies the edited files into the PBX and reloads them: ./reload.sh carrier|office
docker exec lab-vt-$1 sh -c 'cp /lab/*.conf /etc/asterisk/ && asterisk -rx "pjsip reload" >/dev/null && asterisk -rx "dialplan reload" >/dev/null'
EOF
chmod +x reload.sh
sleep 10
docker exec lab-vt-office asterisk -rx 'pjsip show registrations'
docker exec lab-vt-carrier asterisk -rx 'pjsip show contacts'
```

والمخرج سوف يكون كما يلي:

```text
 carrier/sip:10.89.10.5                                  carrier                     Registered        (exp. 273s)

  Contact:  acme/sip:s@10.89.10.10:5060                    b486a41273 NonQual         nan
```

مقسم المكتب مسجل، والمزود حفظ له العنوان `sip:s@10.89.10.10:5060`. ولاحظ الحرف `s` قبل العنوان، فهو القيمة الافتراضية للخيار `contact_user` في Asterisk، وسوف نحتاج هذه الملاحظة بعد قليل.

ثم نكتب إعداد الهواتف الأربعة، وهو إعداد baresip من المقال السابق مع نغمة ثابتة مصدراً للصوت، وكل هاتف مسجل في المقسم الذي يتبعه:

```bash
mkdir -p sounds
docker run --rm -v "$PWD/sounds:/sounds" lab-voip-tools:1 sox -n -r 8000 -c 1 -b 16 /sounds/tone.wav synth 30 sine 440 vol 0.5
for p in ext1001:1001:Lab-1001-pass:10.89.10.10 ext1002:1002:Lab-1002-pass:10.89.10.10          mobile1:mobile1:Mobile1-Lab-pass:10.89.10.5 mobile2:mobile2:Mobile2-Lab-pass:10.89.10.5; do
IFS=: read n u pw srv <<EOF
$p
EOF
mkdir -p phones/$n
cat > phones/$n/config <<CFG
poll_method      epoll
sip_listen       0.0.0.0:5060
audio_source     aufile,/sounds/tone.wav
audio_player     alsa,null
audio_alert      alsa,null
rtp_ports        20000-20019
module_path      /usr/lib/baresip/modules
module           g711.so
module           aufile.so
module           menu.so
module_app       account.so
module           alsa.so
CFG
echo "<sip:$u@$srv>;auth_pass=$pw;answermode=auto" > phones/$n/accounts
done
echo "module           stdio.so" | tee -a phones/ext1002/config phones/mobile2/config
```

هاتف الاستقبال 1001 وجوال 1 يعملان طوال التجربة ويردان تلقائياً، والهاتفان الآخران فيهما الوحدة `stdio`، ونشغلهما عند كل مكالمة بالسكربت `dial.sh`، الذي يلتقط SIP على المقسمين، ويرسل أمر الاتصال، ويطبع رسائل المكالمة كما رآها المزود:

```bash
for p in ext1001:10.89.10.21 mobile1:10.89.10.31; do
  docker run -d --name lab-vt-${p%%:*} --network lab-vt-net --ip ${p#*:} \
    -v "$PWD/phones/${p%%:*}:/conf" -v "$PWD/sounds:/sounds:ro" lab-voip-tools:1 baresip -f /conf
done
cat > dial.sh <<'EOF'
#!/bin/sh
# usage: ./dial.sh <phone> <ip> <number> <name>   one call, captured on both PBXs
for h in carrier office; do docker exec -d lab-vt-$h tcpdump -i eth0 -n -s0 -w /tmp/$4.pcap udp port 5060; done
sleep 1
(sleep 5; echo "/dial $3"; sleep 8; echo "/hangup"; sleep 2; echo "q") | \
  docker run -i --rm --name lab-vt-dialer --network lab-vt-net --ip $2 \
    -v "$PWD/phones/$1:/conf" -v "$PWD/sounds:/sounds:ro" lab-voip-tools:1 baresip -f /conf 2>&1 | tr '\r' '\n' > $4-$1.log
sleep 1; for h in carrier office; do docker exec lab-vt-$h pkill tcpdump; done; sleep 1
docker exec lab-vt-carrier sh /lab/sipflow.sh /tmp/$4.pcap | grep -v -E "REGISTER|OPTIONS"
grep -a -E "audio=" $4-$1.log | tail -1
EOF
chmod +x dial.sh
```

## المكالمات القادمة: من الرقم إلى التحويلة

### العطل الأول: المقسم يطلب من المزود كلمة مرور

الجوال 2 يتصل برقم الشركة الرئيسي:

```bash
./dial.sh mobile2 10.89.10.32 +966110000100 in1
docker logs lab-vt-office 2>&1 | grep NOTICE | tail -1
```

والمخرج سوف يكون كما يلي، بعد أن حذفنا منه ردود التسجيل:

```text
11:50:24  10.89.10.32.5060 > 10.89.10.5.5060  INVITE sip:+966110000100@10.89.10.5 SIP/2.0
11:50:24  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 401 Unauthorized
11:50:24  10.89.10.32.5060 > 10.89.10.5.5060  ACK sip:+966110000100@10.89.10.5 SIP/2.0
11:50:24  10.89.10.32.5060 > 10.89.10.5.5060  INVITE sip:+966110000100@10.89.10.5 SIP/2.0
11:50:24  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 100 Trying
11:50:24  10.89.10.5.5060  > 10.89.10.10.5060 INVITE sip:+966110000100@10.89.10.10:5060 SIP/2.0
11:50:24  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 401 Unauthorized
11:50:24  10.89.10.5.5060  > 10.89.10.10.5060 ACK sip:+966110000100@10.89.10.10:5060 SIP/2.0
11:50:24  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 403 Forbidden
[Oct 20 11:50:24] NOTICE[37]: res_pjsip/pjsip_distributor.c:673 log_failed_request: Request 'INVITE' from '<sip:+966550000456@10.89.10.5>' failed for '10.89.10.5:5060' (callid: 6e2de7b6-2de3-4a91-87d5-cfd6b0c59ef0) - No matching endpoint found
```

المزود أوصل المكالمة إلى مقسم المكتب بالرقم الصحيح في أول سطر، ولكن المقسم رد ب `401 Unauthorized`، أي «من أنت؟ أعطني كلمة المرور»، والمزود لا يعطي كلمة مرور لعملائه، فأنهى المكالمة، وسمع المتصل أن الرقم مرفوض. والسبب في السطر الأخير: المقسم لم يجد ال endpoint الذي جاءت منه الرسالة، لأنه يبحث افتراضياً باسم المستخدم في ترويسة `From`، والاسم هنا هو رقم المتصل `+966550000456` وليس `carrier`. والتسجيل الناجح لا يعني أن المقسم يعرف المزود عندما يتصل هو به، فالتسجيل يعمل في اتجاه واحد.

والحل أن نخبر المقسم أن كل ما يأتي من عنوان المزود يخص ال endpoint المسمى `carrier`، وهذا هو الجزء `identify`، فنضيفه إلى `office/pjsip.conf`:

```ini
[carrier]
type=identify
endpoint=carrier
match=10.89.10.5
```

ثم نعيد التحميل ونتصل مرة أخرى:

```bash
./reload.sh office
./dial.sh mobile2 10.89.10.32 +966110000100 in2
```

```text
11:53:05  10.89.10.5.5060  > 10.89.10.10.5060 INVITE sip:+966110000100@10.89.10.10:5060 SIP/2.0
11:53:05  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 100 Trying
11:53:05  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 180 Ringing
11:53:05  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 180 Ringing
11:53:05  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 200 OK
11:53:05  10.89.10.5.5060  > 10.89.10.10.5060 ACK sip:10.89.10.10:5060 SIP/2.0
11:53:05  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 200 OK
11:53:13  10.89.10.32.5060 > 10.89.10.5.5060  BYE sip:10.89.10.5:5060 SIP/2.0
[0:00:08] audio=64000/63573 (bit/s)
```

رن هاتف الاستقبال ورد، والصوت يمر في الاتجاهين بمعدل G.711.

### توجيه الأرقام DID

نتصل الآن برقم من الكتلة لم نوزعه على أحد، ونقرأ ما فعلته خطة الاتصال في المقسمين:

```bash
./dial.sh mobile2 10.89.10.32 +966110000105 in3 >/dev/null
for h in carrier office; do docker exec lab-vt-$h sh -c 'grep -E "Executing|answered" /var/log/asterisk/messages'; done
```

والمخرج سوف يكون كما يلي، بعد أن حذفنا منه الوقت من كل سطر:

```text
VERBOSE[201][C-00000004] pbx.c: Executing [+966110000105@from-pstn:1] NoOp("PJSIP/mobile2-00000006", "DID +966110000105 for acme from +966550000456") in new stack
VERBOSE[201][C-00000004] pbx.c: Executing [+966110000105@from-pstn:2] Dial("PJSIP/mobile2-00000006", "PJSIP/+966110000105@acme,30") in new stack
VERBOSE[201][C-00000004] app_dial.c: PJSIP/acme-00000007 answered PJSIP/mobile2-00000006
VERBOSE[199][C-00000003] pbx.c: Executing [+966110000105@from-trunk:1] NoOp("PJSIP/carrier-00000004", "Unassigned DID +966110000105") in new stack
VERBOSE[199][C-00000003] pbx.c: Executing [+966110000105@from-trunk:2] Goto("PJSIP/carrier-00000004", "office,1001,1") in new stack
VERBOSE[199][C-00000003] pbx.c: Executing [1001@office:1] Dial("PJSIP/carrier-00000004", "PJSIP/1001,20") in new stack
VERBOSE[199][C-00000003] app_dial.c: PJSIP/1001-00000005 answered PJSIP/carrier-00000004
```

المزود طابق الرقم بكتلة acme وأرسله عبر خطها، ومقسم المكتب استقبله في السياق `from-trunk` لأن ال endpoint `carrier` مضبوط عليه، ولم يجد له تحويلة محددة فأرسله إلى الاستقبال. والصورة التالية تجمع المسار في الاتجاهين، ومنه المكالمات الخارجة التي سوف نصل إليها:

![مكالمة قادمة: الجوال +966550000456 يطلب +966110000105، فيرسل المزود INVITE sip:+966110000105 إلى مقسم المكتب، والسياق from-trunk يوجه +966110000100 إلى 1001 و+966110000102 إلى 1002 وبقية الكتلة إلى 1001، وما لا يطابق يرجع 404. ومكالمة خارجة: التحويلة 1002 تطلب 0550000123 فتحولها خطة الاتصال إلى +966550000123 وتضع هوية المتصل +966110000102، والمزود يقبل الرقم بصيغة E.164 والهوية من أرقام العميل، ويرد 484 على 0550000123 و403 على هوية ليست من أرقامه، والجوال يرى +966110000102](https://arabroot.io/content/images/2026/10/voip-trunk-03-did-routing.webp)

الرقم المطلوب يختار التحويلة، والرقم بصيغة E.164 وهوية من كتلتك تفتحان الطريق للخارج

### العطل الثاني: المكالمة تصل إلى الامتداد s

ليس كل مزود يضع الرقم المطلوب في أول سطر من INVITE، فبعض المزودين يرسلون المكالمة إلى العنوان الذي سجلت به كما هو حرفياً، ولنرى ماذا يحدث نغير سطر الإرسال عند المزود إلى `Dial(PJSIP/acme,30)`، أي دون الرقم:

```bash
sed -i 's#Dial(PJSIP/${EXTEN}@acme,30)#Dial(PJSIP/acme,30)#' carrier/extensions.conf
./reload.sh carrier
./dial.sh mobile2 10.89.10.32 +966110000101 in4
docker exec lab-vt-office sh -c 'grep NOTICE /var/log/asterisk/messages | tail -1'
docker exec lab-vt-office sh -c 'tcpdump -r /tmp/in4.pcap -n -A 2>/dev/null | grep -A14 "INVITE sip:s@" | grep -E "^(From|To)"'
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
11:54:48  10.89.10.5.5060  > 10.89.10.10.5060 INVITE sip:s@10.89.10.10:5060 SIP/2.0
11:54:48  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 404 Not Found
11:54:48  10.89.10.5.5060  > 10.89.10.32.5060 SIP/2.0 404 Not Found
NOTICE[126] res_pjsip_session.c:  carrier: Call (UDP:10.89.10.5:5060) to extension 's' rejected because extension not found in context 'from-trunk'.
From: <sip:+966550000456@10.89.10.5>
To: <sip:s@10.89.10.10>
```

المكالمة وصلت إلى الامتداد `s`، وهو الحرف الذي كتبه المقسم في `Contact` عند التسجيل، ولا يوجد `s` في `from-trunk`، فرد المقسم ب 404 وسمع المتصل أن الرقم غير موجود. ولاحظ أن الرقم المطلوب ضاع تماماً، فلا هو في أول سطر ولا في `To`، أي أن هذا المزود لا يستطيع أن يميز لك بين أرقام الكتلة عبر حساب واحد. وفي هذه الحالة إما أن تطلب من المزود أن يرسل الرقم، وهو الحل الصحيح، أو أن تجعل المقسم يسجل باسم رقمك الرئيسي بالخيار `contact_user` في جزء `registration`، فتصل كل المكالمات إلى الاستقبال على الأقل:

```ini
contact_user=+966110000100
```

```bash
./reload.sh office
docker exec lab-vt-office asterisk -rx 'pjsip send register carrier'
./dial.sh mobile2 10.89.10.32 +966110000101 in5
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
11:55:28  10.89.10.5.5060  > 10.89.10.10.5060 INVITE sip:+966110000100@10.89.10.10:5060 SIP/2.0
11:55:28  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 180 Ringing
[0:00:08] audio=64000/64000 (bit/s)
```

ثم نعيد سطر المزود إلى `Dial(PJSIP/${EXTEN}@acme,30)` ونكمل، ويبقى `contact_user` في الإعداد لأنه لا يضر مع مزود يرسل الرقم، ويحمي من مزود لا يرسله.

## المكالمات الخارجة وهوية المتصل

### العطل الثالث: المزود يرفض ب 403

موظف المبيعات يطلب جوال العميل بالشكل المحلي:

```bash
sed -i 's#Dial(PJSIP/acme,30)#Dial(PJSIP/${EXTEN}@acme,30)#' carrier/extensions.conf
./reload.sh carrier
./dial.sh ext1002 10.89.10.22 0550000123 out1
docker exec lab-vt-carrier sh -c 'grep Executing /var/log/asterisk/messages | tail -4'
docker exec lab-vt-carrier sh -c 'tcpdump -r /tmp/out1.pcap -n -A 2>/dev/null | grep -A14 "INVITE sip:+9665" | grep -E "^(From|To)" | head -2'
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
11:56:02  10.89.10.10.5060 > 10.89.10.5.5060  INVITE sip:+966550000123@10.89.10.5:5060 SIP/2.0
11:56:02  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 401 Unauthorized
11:56:02  10.89.10.10.5060 > 10.89.10.5.5060  INVITE sip:+966550000123@10.89.10.5:5060 SIP/2.0
11:56:02  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 100 Trying
11:56:02  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 403 Forbidden
VERBOSE[332][C-00000007] pbx.c: Executing [+966550000123@from-customer:1] NoOp("PJSIP/acme-0000000c", "Outbound from acme to +966550000123 with caller ID acme") in new stack
VERBOSE[332][C-00000007] pbx.c: Executing [+966550000123@from-customer:2] GotoIf("PJSIP/acme-0000000c", "0?allowed") in new stack
VERBOSE[332][C-00000007] pbx.c: Executing [+966550000123@from-customer:3] NoOp("PJSIP/acme-0000000c", "Caller ID acme is not one of acme's numbers") in new stack
VERBOSE[332][C-00000007] pbx.c: Executing [+966550000123@from-customer:4] Hangup("PJSIP/acme-0000000c", "21") in new stack
From: <sip:acme@10.89.10.10>;tag=b0d8eb47-2f82-4508-abd7-5f355797bbb7
To: <sip:+966550000123@10.89.10.5>
```

الجزء الأول نجح: خطة الاتصال حولت `0550000123` إلى `+966550000123`، والمقسم أجاب طلب المصادقة 401 بكلمة مرور الخط. ولكن المزود رفض المكالمة ب 403، لأن هوية المتصل التي وصلته هي `acme` وليست رقماً من الكتلة. والسبب أن `from_user=acme` يستبدل الجزء الأول من `From` كله، وهو المكان نفسه الذي يحمل رقم المتصل، فضاع الرقم الذي وضعناه في `Set(CALLERID(num)=…)`.

قد يتساءل البعض: لماذا لا نحذف `from_user`؟ والإجابة أن كثيراً من المزودين يحتاجون اسم الحساب في `From` ليعرفوك، فالحل الصحيح أن تحمل الهوية في ترويسة أخرى مخصصة لها، وهي `P-Asserted-Identity` المعرفة في [RFC 3325](https://www.rfc-editor.org/rfc/rfc3325?ref=arabroot.io)، فنضيف إلى ال endpoint `carrier` في مقسم المكتب:

```ini
send_pai=yes
```

والمزود من جهته يجب أن يثق بهذه الترويسة من عملائه فقط، وهذا عنده الخيار `trust_id_inbound=yes` على ال endpoint `acme`، وهو قرار المزود وليس قرارك، فاسأله في أي ترويسة يقرأ الهوية:

```bash
./reload.sh office; ./reload.sh carrier
./dial.sh ext1002 10.89.10.22 0550000123 out2
docker exec lab-vt-carrier sh -c 'tcpdump -r /tmp/out2.pcap -n -A 2>/dev/null | grep -A16 "INVITE sip:+9665" | grep -E "^(From|P-Asserted)" | head -2'
docker logs lab-vt-mobile1 2>&1 | grep -a "Call established" | tail -1
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
11:56:37  10.89.10.5.5060  > 10.89.10.31.5060 INVITE sip:mobile1-0x55f3ef5a3920@10.89.10.31:5060 SIP/2.0
11:56:37  10.89.10.31.5060 > 10.89.10.5.5060  SIP/2.0 180 Ringing
11:56:37  10.89.10.31.5060 > 10.89.10.5.5060  SIP/2.0 200 Answering
[0:00:08] audio=64405/63978 (bit/s)
From: <sip:acme@10.89.10.10>;tag=ba7a0023-412e-4620-af6d-39c80773ddec
P-Asserted-Identity: "Sales" <sip:+966110000100@10.89.10.10>
mobile1@10.89.10.5: Call established: sip:+966110000100@10.89.10.5
```

`From` ما زال يحمل اسم الحساب، والرقم انتقل إلى `P-Asserted-Identity`، والجوال رأى الرقم الرئيسي للشركة. ولاحظ أن الترويسة تحمل الاسم `"Sales"` أيضاً من إعداد التحويلة، فإذا كنت لا تريد أن تخرج أسماء الأقسام الداخلية فاضبط `CALLERID(name)` في `to-carrier` كما ضبطت الرقم.

### رقم مباشر لكل تحويلة

المكالمة خرجت برقم الشركة الرئيسي، ولكن موظف المبيعات له رقمه المباشر `+966110000102`، والعميل الذي يعيد الاتصال يجب أن يصل إليه هو. والطريقة الأنظف أن تضع الرقم مع التحويلة نفسها بالخيار `set_var`، فيصبح متغيراً في كل مكالمة تبدأ منها:

```ini
[1001](phone)
auth=1001
aors=1001
callerid="Reception" <1001>
set_var=OUTBOUND_CID=+966110000100

[1002](phone)
auth=1002
aors=1002
callerid="Sales" <1002>
set_var=OUTBOUND_CID=+966110000102
```

ونجعل `to-carrier` يستخدم هذا المتغير إذا وجد، والرقم الرئيسي إذا لم يوجد:

```ini
[to-carrier]
exten => _+X.,1,Set(CALLERID(num)=${IF($["${OUTBOUND_CID}" != ""]?${OUTBOUND_CID}:+966110000100)})
 same => n,Dial(PJSIP/${EXTEN}@carrier,30)
 same => n,Hangup()
```

ونجرب هذه المرة الطلب الدولي بالبادئة `00`:

```bash
./reload.sh office
./dial.sh ext1002 10.89.10.22 00966550000123 out4 | tail -1
docker exec lab-vt-office sh -c 'grep Executing /var/log/asterisk/messages | tail -3'
docker logs lab-vt-mobile1 2>&1 | grep -a "Call established" | tail -1
```

```text
[0:00:08] audio=64000/64000 (bit/s)
VERBOSE[421][C-00000008] pbx.c: Executing [00966550000123@office:1] Goto("PJSIP/1002-0000000e", "to-carrier,+966550000123,1") in new stack
VERBOSE[421][C-00000008] pbx.c: Executing [+966550000123@to-carrier:1] Set("PJSIP/1002-0000000e", "CALLERID(num)=+966110000102") in new stack
VERBOSE[421][C-00000008] pbx.c: Executing [+966550000123@to-carrier:2] Dial("PJSIP/1002-0000000e", "PJSIP/+966550000123@carrier,30") in new stack
mobile1@10.89.10.5: Call established: sip:+966110000102@10.89.10.5
```

البادئة `00` تحولت إلى `+`، والجوال رأى الرقم المباشر لموظف المبيعات.

### العطل الرابع: رقم بغير صيغة E.164

لنرى ما يحدث لمن يكتب خطة اتصال ترسل الرقم كما طلبه الموظف، نغير مؤقتاً سطر الجوال في `office` إلى `Dial(PJSIP/${EXTEN}@carrier,30)` مباشرة:

```text
11:57:16  10.89.10.10.5060 > 10.89.10.5.5060  INVITE sip:0550000123@10.89.10.5:5060 SIP/2.0
11:57:16  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 401 Unauthorized
11:57:16  10.89.10.10.5060 > 10.89.10.5.5060  INVITE sip:0550000123@10.89.10.5:5060 SIP/2.0
11:57:16  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 100 Trying
11:57:16  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 484 Address Incomplete
VERBOSE[441][C-00000009] pbx.c: Executing [0550000123@from-customer:1] NoOp("PJSIP/acme-0000000f", "Rejected, not E.164: 0550000123") in new stack
```

المزود رد ب `484 Address Incomplete`، وبعض المزودين يقبلون الشكل المحلي ويحولونه بأنفسهم، وبعضهم يرفضه كما هنا، وبعضهم يقبله ثم يخطئ فهمه. لذلك حول كل رقم إلى E.164 في مقسمك، والسبب أن خطة الاتصال عندها تبقى صحيحة إذا غيرت المزود، ثم أعدنا السطر كما كان.

## الخط بعنوان IP

الآن نحول الخط نفسه إلى المصادقة بعنوان IP، كما يحدث عندما ينقل المقسم إلى مركز بيانات بعنوان ثابت. عند المزود نحذف كلمة المرور من حساب acme، ونكتب عنوان المقسم الثابت، ونطابق كل ما يأتي من هذا العنوان بالحساب:

```ini
; The customer trunk "acme": recognised by the IP address of its PBX
[acme]
type=aor
contact=sip:10.89.10.10:5060

[acme](ep)
context=from-customer
aors=acme
trust_id_inbound=yes

[acme]
type=identify
endpoint=acme
match=10.89.10.10
```

وفي مقسم المكتب نحذف جزأي `registration` و`auth`، ونحذف `outbound_auth` و`from_user` من ال endpoint، فيصبح الخط:

```ini
[carrier]
type=aor
contact=sip:10.89.10.5:5060

[carrier]
type=endpoint
context=from-trunk
disallow=all
allow=alaw,ulaw
aors=carrier
send_pai=yes
direct_media=no

[carrier]
type=identify
endpoint=carrier
match=10.89.10.5
```

ولأن حذف التسجيل لا يكفيه إعادة التحميل، نعيد تشغيل المقسمين ثم نتصل في الاتجاهين:

```bash
docker restart lab-vt-carrier lab-vt-office
docker exec lab-vt-office asterisk -rx 'pjsip show registrations'
./dial.sh ext1002 10.89.10.22 0550000123 ip2
./dial.sh mobile2 10.89.10.32 +966110000100 ip3
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
No objects found.

11:59:33  10.89.10.10.5060 > 10.89.10.5.5060  INVITE sip:+966550000123@10.89.10.5:5060 SIP/2.0
11:59:33  10.89.10.5.5060  > 10.89.10.10.5060 SIP/2.0 100 Trying
11:59:33  10.89.10.5.5060  > 10.89.10.31.5060 INVITE sip:mobile1-0x56442d568920@10.89.10.31:5060 SIP/2.0
[0:00:08] audio=63573/64000 (bit/s)
12:00:13  10.89.10.5.5060  > 10.89.10.10.5060 INVITE sip:+966110000100@10.89.10.10:5060 SIP/2.0
12:00:13  10.89.10.10.5060 > 10.89.10.5.5060  SIP/2.0 180 Ringing
[0:00:06] audio=64000/64000 (bit/s)
```

لا يوجد تسجيل، والمكالمات تعمل في الاتجاهين، ولاحظ أن المكالمة الخارجة لم تعد تمر ب `401 Unauthorized`، فالمزود يقبلها من أول INVITE لأنها جاءت من العنوان الصحيح.

### ثغرة: من يتصل باسم الخط؟

هل هذا الإعداد آمن؟ لا، والتفصيل فيما يلي. نشغل جهازاً غريباً على عنوان آخر في الشبكة، 10.89.10.66، بإعداد الهاتف 1002 نفسه، ونعطيه حساباً اسمه `acme` دون كلمة مرور ودون تسجيل، ونجعله يتصل بجوال عبر المزود:

```bash
mkdir -p phones/stranger && cp phones/ext1002/config phones/stranger/
echo "<sip:acme@10.89.10.5>;regint=0;answermode=auto" > phones/stranger/accounts
./dial.sh stranger 10.89.10.66 +966550000123 ip4
docker exec lab-vt-carrier sh -c 'grep Executing /var/log/asterisk/messages | tail -4'
docker exec lab-vt-carrier asterisk -rx 'pjsip show endpoint acme' | grep identify_by
```

```text
12:00:35  10.89.10.66.5060 > 10.89.10.5.5060  INVITE sip:+966550000123@10.89.10.5 SIP/2.0
12:00:35  10.89.10.5.5060  > 10.89.10.66.5060 SIP/2.0 100 Trying
12:00:35  10.89.10.5.5060  > 10.89.10.66.5060 SIP/2.0 403 Forbidden
VERBOSE[182][C-00000004] pbx.c: Executing [+966550000123@from-customer:1] NoOp("PJSIP/acme-00000006", "Outbound from acme to +966550000123 with caller ID acme") in new stack
VERBOSE[182][C-00000004] pbx.c: Executing [+966550000123@from-customer:2] GotoIf("PJSIP/acme-00000006", "0?allowed") in new stack
VERBOSE[182][C-00000004] pbx.c: Executing [+966550000123@from-customer:3] NoOp("PJSIP/acme-00000006", "Caller ID acme is not one of acme's numbers") in new stack
VERBOSE[182][C-00000004] pbx.c: Executing [+966550000123@from-customer:4] Hangup("PJSIP/acme-00000006", "21") in new stack
 identify_by                        : username,ip
```

الرد 403 قد يبدو مطمئناً، ولكن اقرأ السطر الأول من خطة الاتصال: المكالمة دخلت باسم القناة `PJSIP/acme` وفي سياق العميل `from-customer`، أي أن المزود عاملها كأنها من مقسم acme، مع أنها جاءت من عنوان آخر ودون كلمة مرور. والذي أوقفها هو قاعدة هوية المتصل في خطة الاتصال فقط، وقاعدة في خطة الاتصال ليست حدوداً أمنية. والسبب في السطر الأخير: `identify_by` قيمته الافتراضية `username,ip`، فيبحث Asterisk أولاً باسم المستخدم في `From`، فإذا وجد endpoint بالاسم نفسه دون كلمة مرور قبله مباشرة، ولا يصل إلى مطابقة العنوان أصلاً.

والمشكلة نفسها موجودة في مقسم المكتب، فال endpoint اسمه `carrier`، وأي جهاز يرسل INVITE مباشرة إلى المقسم وفي `From` الاسم `carrier` يدخل في سياق `from-trunk`، ولنجرب ذلك نغير حساب الجهاز الغريب ونتصل بالرقم الرئيسي على عنوان المقسم مباشرة:

```bash
echo "<sip:carrier@10.89.10.10>;regint=0;answermode=auto" > phones/stranger/accounts
./dial.sh stranger 10.89.10.66 +966110000100 ip7 >/dev/null
docker exec lab-vt-office sh /lab/sipflow.sh /tmp/ip7.pcap | grep 10.89.10.66 | head -4
docker exec lab-vt-office sh -c 'grep Executing /var/log/asterisk/messages | tail -2'
```

```text
12:02:04  10.89.10.66.5060 > 10.89.10.10.5060 INVITE sip:+966110000100@10.89.10.10 SIP/2.0
12:02:04  10.89.10.10.5060 > 10.89.10.66.5060 SIP/2.0 100 Trying
12:02:04  10.89.10.10.5060 > 10.89.10.66.5060 SIP/2.0 180 Ringing
12:02:04  10.89.10.10.5060 > 10.89.10.66.5060 SIP/2.0 200 OK
VERBOSE[209][C-00000005] pbx.c: Executing [+966110000100@from-trunk:1] Goto("PJSIP/carrier-00000007", "office,1001,1") in new stack
VERBOSE[209][C-00000005] pbx.c: Executing [1001@office:1] Dial("PJSIP/carrier-00000007", "PJSIP/1001,20") in new stack
```

هاتف الاستقبال رن ورد على جهاز غريب دخل باسم المزود. وهنا الضرر إزعاج فقط لأن `from-trunk` لا يخرج إلى الخارج، ولكن خطأ واحداً في السياق، مثل أن تضع فيه `Goto` إلى `office` كله، يجعل هذا الجهاز يتصل بأي رقم في العالم على حسابك. والحل أن تجعل الخطوط التي تعتمد على العنوان تطابق بالعنوان فقط، فنضيف إلى ال endpoint في الجهتين:

```ini
identify_by=ip
```

```bash
./reload.sh carrier; ./reload.sh office
./dial.sh stranger 10.89.10.66 +966550000123 ip5
docker exec lab-vt-carrier sh -c 'grep NOTICE /var/log/asterisk/messages | tail -1'
```

والمخرج سوف يكون كما يلي، بعد أن اختصرناه إلى الأسطر المهمة:

```text
12:01:10  10.89.10.66.5060 > 10.89.10.5.5060  INVITE sip:+966550000123@10.89.10.5 SIP/2.0
12:01:10  10.89.10.5.5060  > 10.89.10.66.5060 SIP/2.0 401 Unauthorized
NOTICE[37] res_pjsip/pjsip_distributor.c: Request 'INVITE' from '<sip:acme@10.89.10.5>' failed for '10.89.10.66:5060' (callid: c717f183078bd768) - Failed to authenticate
```

الجهاز الغريب لم يعد يطابق أي endpoint، فطلب منه المزود كلمة مرور لا يملكها، ولم تصل المكالمة إلى خطة الاتصال أصلاً، وفي مقسم المكتب ظهر السطر نفسه `No matching endpoint found` للتجربة المقابلة، بينما بقيت مكالمات المزود الحقيقي تعمل. وتذكر أن الخط بالتسجيل لم يكن فيه هذا الخطر عند المزود، لأن الحساب `acme` كان له كلمة مرور، فالمطابقة بالاسم تنتهي بطلب المصادقة.

🛑

أي endpoint دون كلمة مرور يجب أن يطابق بالعنوان فقط `identify_by=ip`، وأن يكون سياقه لا يخرج إلى الخارج إلا بما تريده، وأن يغلق جدار الحماية منفذ SIP لكل العناوين ما عدا عناوين المزود. والاحتيال على المكالمات Toll Fraud يبدأ غالباً من خطأ صغير كهذا، وسوف نخصص له مقال تأمين المقسم في هذه السلسلة.

## قبل أن تربط مقسمك بمزود حقيقي

- اطلب من المزود ورقة الإعداد كاملة: عنوان السيرفر، ونوع الخط تسجيل أم IP، وأي ترويسة يقرأ منها هوية المتصل، وأين يضع الرقم المطلوب في المكالمات القادمة، والترميزات المسموحة، وعدد القنوات.
- افتح منفذ SIP ونطاق RTP لعناوين المزود فقط، والمزود الجاد يعطيك قائمة عناوينه، وإذا كان خطك بالتسجيل خلف NAT فطبق ما شرحناه في [المقال السابق](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9-sip-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D9%88%D8%A7%D8%AD%D8%AF/#production).
- استخدم TLS وSRTP إذا كان المزود يدعمهما، لأن الخط يمر عبر الإنترنت ويحمل مكالمات عملائك.
- حدد في خطة الاتصال ما يسمح بالخروج: الأرقام المحلية والجوال، والدولي فقط للتحويلات التي تحتاجه، والسبب أن فاتورة المكالمات الدولية هي ما يبحث عنه المحتال.
- لا تترك رقماً من الكتلة دون وجهة، فكل رقم لم توزعه يذهب إلى الاستقبال أو إلى رسالة صوتية، حتى لا يسمع العميل «الرقم غير موجود» على رقم مطبوع في إعلان قديم.
- راقب حالة التسجيل في الخط بالتسجيل، ف `Unregistered` في `pjsip show registrations` تعني أن المكالمات القادمة لا تصلك، ولن يخبرك أحد بذلك إلا العملاء.

## جدول التشخيص السريع

| العرض                                                  | السبب الأرجح                                              | أين تنظر                                 |
| ------------------------------------------------------ | --------------------------------------------------------- | ---------------------------------------- |
| المتصل يسمع أن الرقم مرفوض، والمقسم يرد 401 على المزود | لا يوجد identify لعنوان المزود                            | No matching endpoint found في سجل المقسم |
| المكالمات القادمة ترجع 404                             | المزود يرسل إلى s أو برقم لا يوجد في from-trunk           | أول سطر من INVITE وترويسة To في الالتقاط |
| المكالمات القادمة لا تصل أبداً والخارجة تعمل           | التسجيل سقط أو ربط NAT انتهى                              | pjsip show registrations                 |
| المكالمة الخارجة ترفض ب 403                            | هوية المتصل ليست من أرقامك، أو في ترويسة لا يقرؤها المزود | From وP-Asserted-Identity في الالتقاط    |
| المكالمة الخارجة ترفض ب 484 أو 404                     | الرقم ليس بصيغة E.164                                     | أول سطر من INVITE الخارج                 |
| جهاز غريب يدخل في سياق الخط                            | endpoint دون كلمة مرور يطابق باسم المستخدم                | identify\_by في pjsip show endpoint      |

## إزالة التجربة

```bash
docker rm -f lab-vt-carrier lab-vt-office lab-vt-ext1001 lab-vt-mobile1
docker network rm lab-vt-net
```

## تدريب عملي

- أضف إلى المزود مكالمات الأرقام الثابتة `+9661…`، واجعل التحويلة 1001 فقط تستطيع الاتصال الدولي، ثم جرب الدولي من 1002 واقرأ ما يحدث.
- في الخط بالتسجيل أوقف مقسم المزود دقيقتين ثم أعد تشغيله، وراقب `pjsip show registrations` في مقسم المكتب، وقس كم يتأخر رجوع المكالمات القادمة بحسب `retry_interval`.

## الخلاصة

- الخط الخارجي SIP Trunk يربط مقسمك بمزود يوصلك بشبكة الهاتف العامة، والأرقام DID كلها تأتي عبر الخط نفسه، والمقسم يختار التحويلة من الرقم المطلوب.
- حول كل رقم إلى E.164 داخل مقسمك، فهي اللغة التي يفهمها كل مزود، ولا تعتمد على أن يفهم المزود الشكل المحلي.
- التسجيل الناجح لا يعني أن المقسم يعرف المزود، فأضف `identify` لعناوين المزود، وانتبه لمزود يرسل المكالمات إلى `s`.
- هوية المتصل تحتاج أن تصل في الترويسة التي يقرؤها المزود، وغالباً `P-Asserted-Identity`، وأن تكون من أرقامك، وإلا رفض المكالمة ب 403.
- الخط بعنوان IP لا يحتاج تسجيلاً ولكنه يجعل العنوان هو المفتاح الوحيد، فاجعل مطابقته `identify_by=ip` واغلق SIP لكل العناوين ما عدا عناوين المزود.

وفي المقال القادم بإذن الله ندخل في Asterisk نفسه بالتفصيل: خطة الاتصال، والبريد الصوتي، والرد الآلي IVR، والتحكم فيه من برامجك.

[![](https://arabroot.io/content/images/2026/10/voip-terms-cover-cover.webp)دليلمصطلحات الاتصالات الهاتفية VoIP للمبتدئينشرح مبسط لمصطلحات الهاتف عبر الإنترنت، وما يمر في الشبكة بين ضغط زر الاتصال ووصول الصوت إلى الطرف الآخر.](https://arabroot.io/articles/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-voip-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/voip-call-path-cover-cover.webp)دليلتتبع مكالمة SIP عبر الشبكة: لماذا يصل الصوت في اتجاه واحد؟نبني مقسماً وهاتفين خلف NAT في تجربة عملية، ونتتبع المكالمة حزمة بحزمة حتى نعرف لماذا يضيع الصوت وكيف نصلحه.](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%83%D8%A7%D9%84%D9%85%D8%A9-sip-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%88%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%81%D9%8A-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D9%88%D8%A7%D8%AD%D8%AF/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وبناء التجربة على Asterisk 20.6.0 مزوداً ومقسماً وbaresip 1.0.0 من حزم Ubuntu 24.04 في Docker، ومراجعة صفحتي خطة الترقيم الوطنية وتصريح خدمات الصوت الافتراضية في موقع هيئة الاتصالات والفضاء والتقنية.