Slides for my "Am I sober or am I trunk? A Janus story" presentation at Kamailio World 2024.
They describe my prototype efforts to add an option to create a trunk between a Janus instance and a SIP server, with the related implementation challenges and the interesting opportunities it opens.
PLAI - Acceleration Program for Generative A.I. Startups
SIP trunking in Janus @ Kamailio World 2024
1. Am I Sober Or Am I Trunk?
A Janus Story
Lorenzo Miniero
@lminiero@fosstodon.org
Kamailio World
April 19th 2024,
2. Who am I?
Lorenzo Miniero
• Ph.D @ UniNA
• Chairman @ Meetecho
• Main author of Janus
Contacts and info
• lorenzo@meetecho.com
• https://fosstodon.org/@lminiero
• https://www.meetecho.com
• https://lminiero.it
3. Just a few words on Meetecho
• Co-founded in 2009 as an academic spin-off
• University research efforts brought to the market
• Completely independent from the University
• Focus on real-time multimedia applications
• Strong perspective on standardization and open source
• Several activities
• Consulting services
• Commercial support and Janus licenses
• Streaming of live events (IETF, ACM, etc.)
• Proudly brewed in sunny Napoli, Italy
4. A bit of context: Janus, WebRTC and SIP
Janus
General purpose, open source WebRTC server
• https://github.com/meetecho/janus-gateway
• Demos and documentation: https://janus.conf.meetecho.com
• Community: https://janus.discourse.group/
5. SIP plugin in Janus
https://janus.conf.meetecho.com/docs/sip
6. An endpoint of behalf of WebRTC users
• Janus SIP plugin acts as a collection of SIP endpoints, not a server/trunk
• SIP stack implemented with Sofia-SIP
• WebRTC users only see the Janus API (JSON), no SIP
• No transcoding, media is only relayed
• Built-in recording (separate media legs)
• Simplifies life for web developers
• No need to worry about a SIP stack (only SIP URIs)
• Basic methods/events to handle dialogs (call, answer, hangup, message, etc.)
• Allows SIP headers injection in many requests
• Support for more advanced features too (e.g., hold, transfer, real-time text, etc.)
7. An endpoint of behalf of WebRTC users
• Janus SIP plugin acts as a collection of SIP endpoints, not a server/trunk
• SIP stack implemented with Sofia-SIP
• WebRTC users only see the Janus API (JSON), no SIP
• No transcoding, media is only relayed
• Built-in recording (separate media legs)
• Simplifies life for web developers
• No need to worry about a SIP stack (only SIP URIs)
• Basic methods/events to handle dialogs (call, answer, hangup, message, etc.)
• Allows SIP headers injection in many requests
• Support for more advanced features too (e.g., hold, transfer, real-time text, etc.)
12. What’s a trunk anyway?
• A lot of buzzwords out there!
• Tried googling “SIP trunk”
• A gazillion of commercial fluff, very little technical details
• Basically (don’t quote me on that!), the digital equivalent of old analogue lines
• A group of phone lines implemented with SIP
• A way to connect multiple channels to, e.g., a PBX
• But isn’t that what SIP allows for already?
• No hardware limitations as in PSTN
• It’s over IP, we can have as many channels as we want
13. What’s a trunk anyway?
• A lot of buzzwords out there!
• Tried googling “SIP trunk”
• A gazillion of commercial fluff, very little technical details
• Basically (don’t quote me on that!), the digital equivalent of old analogue lines
• A group of phone lines implemented with SIP
• A way to connect multiple channels to, e.g., a PBX
• But isn’t that what SIP allows for already?
• No hardware limitations as in PSTN
• It’s over IP, we can have as many channels as we want
14. What’s a trunk anyway?
• A lot of buzzwords out there!
• Tried googling “SIP trunk”
• A gazillion of commercial fluff, very little technical details
• Basically (don’t quote me on that!), the digital equivalent of old analogue lines
• A group of phone lines implemented with SIP
• A way to connect multiple channels to, e.g., a PBX
• But isn’t that what SIP allows for already?
• No hardware limitations as in PSTN
• It’s over IP, we can have as many channels as we want
15. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
16. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
17. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
18. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
19. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
20. What’s a trunk anyway?
• More formally (maybe?), a way to connect private and public domains
• e.g., a private PBX and the PSTN
• More in general, interconnection between two SIP servers?
• e.g., IP based peering (address:port)
• Possibly with authentication (SIP based? TLS?)
• That’s something Janus could benefit from, in some cases
• Ensure all outgoing calls from WebRTC users go through a specific server
• Ensure all incoming calls for WebRTC users come from a specific server
• May help get rid of registrations, when unneeded (e.g., contact center)
21. How do we do that with Janus, though?
• As we said, Janus is a collection of endpoints, not a SIP server
• Setting an outbound proxy is easy (supported already)
• No way to limit incoming traffic too, though (users have their own bound address)
• In theory, a couple of potential approaches
1 Implement an internal proxy in Janus
2 Refactor the SIP plugin to allow a shared SIP stack
• Both approaches have pros and cons
• ... and headaches in terms of implementation details!
22. How do we do that with Janus, though?
• As we said, Janus is a collection of endpoints, not a SIP server
• Setting an outbound proxy is easy (supported already)
• No way to limit incoming traffic too, though (users have their own bound address)
• In theory, a couple of potential approaches
1 Implement an internal proxy in Janus
2 Refactor the SIP plugin to allow a shared SIP stack
• Both approaches have pros and cons
• ... and headaches in terms of implementation details!
23. How do we do that with Janus, though?
• As we said, Janus is a collection of endpoints, not a SIP server
• Setting an outbound proxy is easy (supported already)
• No way to limit incoming traffic too, though (users have their own bound address)
• In theory, a couple of potential approaches
1 Implement an internal proxy in Janus
2 Refactor the SIP plugin to allow a shared SIP stack
• Both approaches have pros and cons
• ... and headaches in terms of implementation details!
24. How do we do that with Janus, though?
• As we said, Janus is a collection of endpoints, not a SIP server
• Setting an outbound proxy is easy (supported already)
• No way to limit incoming traffic too, though (users have their own bound address)
• In theory, a couple of potential approaches
1 Implement an internal proxy in Janus
2 Refactor the SIP plugin to allow a shared SIP stack
• Both approaches have pros and cons
• ... and headaches in terms of implementation details!
25. How do we do that with Janus, though?
• As we said, Janus is a collection of endpoints, not a SIP server
• Setting an outbound proxy is easy (supported already)
• No way to limit incoming traffic too, though (users have their own bound address)
• In theory, a couple of potential approaches
1 Implement an internal proxy in Janus
2 Refactor the SIP plugin to allow a shared SIP stack
• Both approaches have pros and cons
• ... and headaches in terms of implementation details!
26. Implementing an internal proxy in Janus
• This was the first idea that came to mind, as in theory “simpler”
• Proxy implementing the trunking part
• Addressing calls based on SIP URI (which Janus knows even for unregistered users)
• PRO: No need to touch the existing Janus SIP code
• We can keep the “collection of endpoints” approach
• Stack simply uses the local proxy as outbound proxy
• Everything else remains the same
• CON: But it isn’t really simple, at all!
• Writing a proxy, even if internal, would be a huge undertaking!!
• Besides, it would be a “silent”, hidden and unneeded hop
27. Implementing an internal proxy in Janus
• This was the first idea that came to mind, as in theory “simpler”
• Proxy implementing the trunking part
• Addressing calls based on SIP URI (which Janus knows even for unregistered users)
• PRO: No need to touch the existing Janus SIP code
• We can keep the “collection of endpoints” approach
• Stack simply uses the local proxy as outbound proxy
• Everything else remains the same
• CON: But it isn’t really simple, at all!
• Writing a proxy, even if internal, would be a huge undertaking!!
• Besides, it would be a “silent”, hidden and unneeded hop
28. Implementing an internal proxy in Janus
• This was the first idea that came to mind, as in theory “simpler”
• Proxy implementing the trunking part
• Addressing calls based on SIP URI (which Janus knows even for unregistered users)
• PRO: No need to touch the existing Janus SIP code
• We can keep the “collection of endpoints” approach
• Stack simply uses the local proxy as outbound proxy
• Everything else remains the same
• CON: But it isn’t really simple, at all!
• Writing a proxy, even if internal, would be a huge undertaking!!
• Besides, it would be a “silent”, hidden and unneeded hop
29. Refactor the SIP plugin (shared SIP stack)
• What if we instead allow different Janus users to re-use the same Sofia-SIP stack?
• This SIP stack could implement the peering, and enforce an outbound proxy
• Incoming dialogs could be processed in terms of who they’re for
• Call is for User X −→ notify it to WebRTC User X
• CON: Does requite changes to the SIP plugin code
• Sofia event loop would not have 1-1 association with specific user
• Users management, interactions and cleanup would need to be updated too
• PRO: Would be much easier to implement than a full proxy, though
• And most importantly, wouldn’t add an unneeded hop in the process
30. Refactor the SIP plugin (shared SIP stack)
• What if we instead allow different Janus users to re-use the same Sofia-SIP stack?
• This SIP stack could implement the peering, and enforce an outbound proxy
• Incoming dialogs could be processed in terms of who they’re for
• Call is for User X −→ notify it to WebRTC User X
• CON: Does requite changes to the SIP plugin code
• Sofia event loop would not have 1-1 association with specific user
• Users management, interactions and cleanup would need to be updated too
• PRO: Would be much easier to implement than a full proxy, though
• And most importantly, wouldn’t add an unneeded hop in the process
31. Refactor the SIP plugin (shared SIP stack)
• What if we instead allow different Janus users to re-use the same Sofia-SIP stack?
• This SIP stack could implement the peering, and enforce an outbound proxy
• Incoming dialogs could be processed in terms of who they’re for
• Call is for User X −→ notify it to WebRTC User X
• CON: Does requite changes to the SIP plugin code
• Sofia event loop would not have 1-1 association with specific user
• Users management, interactions and cleanup would need to be updated too
• PRO: Would be much easier to implement than a full proxy, though
• And most importantly, wouldn’t add an unneeded hop in the process
32. A single stack to rule them all
https://github.com/meetecho/janus-gateway/tree/sip-trunk
33. A single stack to rule them all
https://github.com/meetecho/janus-gateway/tree/sip-trunk
34. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
35. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
36. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
37. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
38. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
39. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
40. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
41. Implementation details (and challenges)
1 Trunk has a dedicated structure (IP peering details)
2 Stack associated to a “fake” SIP plugin participant (the trunk)
3 Peering enforced at the UDP/TCP level (message from unknown address −→ 403)
4 Users can choose “regular” stack or trunk stack (UI in SIP demo page)
5 Trunk users present their SIP URI but don’t register (local mapping)
6 Outgoing dialogs −→ trunk SIP stack is used (map dialog to user)
7 Incoming dialogs −→ trunk SIP loop finds user from headers (or existing dialog)
8 Events for WebRTC users delegated to user thread (not to block shared loop)
43. What’s next/missing? A lot!
• Peering at the moment only done at the UDP/TCP level
• May be enough in most use cases, but what if auth is needed?
• Will we need auth at SIP level? TLS level? Something else?
• To make things simple, only a single hardcoded trunk is supported
• We should allow multiple trunks to be created (and dynamically, via API)
• There’s no auth done on WebRTC users either, at the moment
• Users are free to use the existing trunk, if it exists
• Users are free to choose any SIP URI for local mapping
• Does it make sense to wait for user, if they’re not there when a call arrives?
• Shared SIP stack allows for it (per-user stack doesn’t)
• May be useful for scenarios associated with push notifications
44. What’s next/missing? A lot!
• Peering at the moment only done at the UDP/TCP level
• May be enough in most use cases, but what if auth is needed?
• Will we need auth at SIP level? TLS level? Something else?
• To make things simple, only a single hardcoded trunk is supported
• We should allow multiple trunks to be created (and dynamically, via API)
• There’s no auth done on WebRTC users either, at the moment
• Users are free to use the existing trunk, if it exists
• Users are free to choose any SIP URI for local mapping
• Does it make sense to wait for user, if they’re not there when a call arrives?
• Shared SIP stack allows for it (per-user stack doesn’t)
• May be useful for scenarios associated with push notifications
45. What’s next/missing? A lot!
• Peering at the moment only done at the UDP/TCP level
• May be enough in most use cases, but what if auth is needed?
• Will we need auth at SIP level? TLS level? Something else?
• To make things simple, only a single hardcoded trunk is supported
• We should allow multiple trunks to be created (and dynamically, via API)
• There’s no auth done on WebRTC users either, at the moment
• Users are free to use the existing trunk, if it exists
• Users are free to choose any SIP URI for local mapping
• Does it make sense to wait for user, if they’re not there when a call arrives?
• Shared SIP stack allows for it (per-user stack doesn’t)
• May be useful for scenarios associated with push notifications
46. What’s next/missing? A lot!
• Peering at the moment only done at the UDP/TCP level
• May be enough in most use cases, but what if auth is needed?
• Will we need auth at SIP level? TLS level? Something else?
• To make things simple, only a single hardcoded trunk is supported
• We should allow multiple trunks to be created (and dynamically, via API)
• There’s no auth done on WebRTC users either, at the moment
• Users are free to use the existing trunk, if it exists
• Users are free to choose any SIP URI for local mapping
• Does it make sense to wait for user, if they’re not there when a call arrives?
• Shared SIP stack allows for it (per-user stack doesn’t)
• May be useful for scenarios associated with push notifications