Git Product home page Git Product logo

Comments (21)

rfuchs avatar rfuchs commented on June 24, 2024

Alright, so the current implementation is as follows.

All UDP/RTP sockets are opened the same way. They're opened as an AF_INET6 socket, bound to IN6ADDR_ANY and the respective UDP port (function get_port6()). IPv4 is handled through IPv4-in-IPv6 mapped addresses.

There used to be a function get_port4(), which would open an AF_INET socket when no IPv6 support was available. But support for non-IPv6 enabled hosts was abandoned and thus this function was dropped. Now all sockets are AF_INET6 sockets, always.

The source address of outgoing packets is selected in callmaster_msg_mh_src() (through sendmsg() extensions) and is done solely based on the address family of the destination address. If the destination is an IPv4-in-IPv6 mapped address, then the local IPv4 address (also mapped into IPv6) is chosen, otherwise the IPv6 address is chosen.

This enables rtpengine to freely bridge between IPv4 and IPv6. It doesn't care what kind of address family the incoming packet used, as addresses are always mapped into IPv6 anyway. It only needs to know which source address to use for outgoing packets, IPv4 or IPv6.

As for signalling, the local address advertised to the remote SIP endpoint is determined in call_stream_address(). If the "desired family" for a particular endpoint is known, then this local address (family) is chosen, otherwise the address (family) is determined from the address family of the destination address, if known, same as when sending a UDP/RTP packet.

The "desired family" flag in turn is derived either explicitly from the respective flag within the NG protocol ("address family", see call_ng_process_flags()), or implicitly from the legacy internal/external "direction" flags. The latter method will be obsoleted at some point, but for now must remain intact as it's still being used. The logic for this can be found in monologue_offer_answer().

So to implement same-family bridging, you can either re-use the existing "direction" flags, or introduce a new flag into the NG protocol akin to the "desired family" option to select from multiple local addresses. If you go for the former option, then you'll need a case distinction between IPv4/IPv6 and IPv4/IPv4 bridging. With the latter option, it should be possible to handle multiple addresses and multiple address families simultaneously. You'll also need a way to tell rtpengine about the different addresses that it can use (more CLI arguments?)

from rtpengine.

dipakb avatar dipakb commented on June 24, 2024

Thanks for the detailed explanation. Glad, we're having this conversation. Two key points that you've mentioned -

"But support for non-IPv6 enabled hosts was abandoned and thus this function was dropped. Now all sockets are AF_INET6 sockets, always."

"The "desired family" flag in turn is derived either explicitly from the respective flag within the NG protocol ("address family", see call_ng_process_flags()), or implicitly from the legacy internal/external "direction" flags. The latter method will be obsoleted at some point, but for now must remain intact as it's still being used."

I don't have that luxury. I need to support IPv4 only hosts (no dual-stack). So, I need something like get_port4() definitely. So, can I make following assumptions/modifications:
(a) '-i' argument accepts IPv4 only address and it's logically associated with internal (private) interface of the machine.

(b) '-I' accepts IPv4/v6 address (parsing and storing it will be taken care) and will be associated with external (public) interface of the machine.

(c) Introduce 2 new functions (which replace get_port6() function) -
       get_internal_port() - opens a AF_INET socket and binds to a port on a Internal IP (v4) interface (provided thru '-i')
       get_external_port() - opens a AF_INET or AF_INET6 (based on type of IP provided in '-I' argument) socket and binds to a port on a external IP (v4/v6) interface.

 (d) Introduce a new CLI argument, say -d/--mode. Possible values would be
       1. bridge - default operational mode. In this mode, it would work in current way.
       2. nat - new mode, where packets would be forwarded from internal to external IP and vice versa providing nat traversal based on ng-call-control directives (to be tested with Kamailio).

 (e) The legacy internal/external "direction" flag can't be obsoleted as that would be key in 'nat' mode of operation. Is that acceptable?

Thanks,
Dipak

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

Just to clarify: with "no IPv6 support" I meant that AF_INET6 sockets are unsupported by the system. Whether or not the system actually has IPv6 connectivity isn't relevant. Hosts without IPv6 stacks are pretty rare these days, are you sure you require this?

from rtpengine.

dipakb avatar dipakb commented on June 24, 2024

Yeah, I know. But, I'm targeting for a tricked out (or rather I would say, down) linux machine where IPv6 is compiled out. So, sock call with AF_INET6 is going to fail regardless. I understand that with IPv6 mapped IPv4, life would have been easier.

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

I see, in that case perhaps a compile-time switch (no-ipv6) would be a good solution, where it always gets an AF_INET socket.

You mention binding to a particular address. Is this required? Right now, IN(6)ADDR_ANY is used, with the source address selected each time a packet is sent. It would be easier to keep this logic in place.

As for "direction" being obsoleted, I meant obsoleted as mechanism for selecting the address family. The flags themselves will stick around.

Perhaps the proposed --mode option isn't even necessary. If both addresses -i and -I are IPv4, then there's only one thing it can do anyway.

from rtpengine.

dipakb avatar dipakb commented on June 24, 2024

"The source address of outgoing packets is selected in callmaster_msg_mh_src() (through sendmsg() extensions) and is done solely based on the address family of the destination address. If the destination is an IPv4-in-IPv6 mapped address, then the local IPv4 address (also mapped into IPv6) is chosen, otherwise the IPv6 address is chosen."

I think my use case is different. Though, my destination address is never going to be IPv6, but I've to choose which of the IPv4 I'm going to use as my source address. One option would be to look for source and destination would always be the other IPv4. I thought that would need a (--mode like) option to make that decision, as opposed to current way of making the decision of choosing source family based on destination family.

Can we have a call to discuss and then I can write up the final outline of the decided approach after you agree?

Thanks,
Dipak

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

What I meant was that, if I understand correctly, I think the "mode" can be implicitly inferred from the type of address given to the -I option. If it's IPv6, then everything works as it does now, but if it's IPv4, then not the address family is used as criterion for which source address to choose, but something else (i.e. the "direction" given).

In fact, I think this can be generalized to both -i and -I accepting addresses of either address family. If they're the same address family, then internal/external is used as decision criterion, otherwise the destination address family is used. In the future, this could then be extended to more than 2 local addresses of arbitrary families being supported.

Anyway, just a suggestion. Either way is fine with me.

from rtpengine.

dipakb avatar dipakb commented on June 24, 2024

Got it. I, for a while, forgot that you clarified previously, the direction itself is not obsoleted but use of it to determine address family is obsoleted.

So,
1. I'll use "direction" to determine the source address of an outgoing UDP/RTP when both interfaces are of same family (at this time IPv4/IPv4).

2. Use compilation switch to compile out IPv6 socket always and use IPv4 always, instead.

Do you think, I should rather use runtime decision based on '-I' provided address family?

Thanks,
Dipak

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

mmmm, whatever is easier. Hard to tell right now. Generally I'd prefer to only have a single socket type throughout the code to avoid constant case distinctions, but since those distinctions have to be made anyway, either at compile time or at run time, I'd just make it as painless as possible.

from rtpengine.

camilleoudot avatar camilleoudot commented on June 24, 2024

Hi,
We are also interested in ipv4 to ipv4 bridging (but our system can do ipv6). Although playing with routing and IP masquerading does the trick in our lab, this kind of solution won't scale very easily for us. The best solution would be to specify explicitly the direction (internal <=> external) in the offer or answer commands.

@dipakb, have you had any success on this topic? Have you implemented something already?

@rfuchs, how could this kind of feature fit in your current architecture?

Cheers,
Camille

from rtpengine.

pabelanger avatar pabelanger commented on June 24, 2024

I'm also surprised this feature is not implemented. Unless I am mistake, there is no way currently to proxy RTP between a public and private IPv4 interface.

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

@camilleoudot it shouldn't be too hard to implement this, all sockets listen on inaddr_any/in6addr_any, so the only requirement is to select a different source address through the msghdr struct in sendmsg() and also communicate this to the kernel module.

from rtpengine.

camilleoudot avatar camilleoudot commented on June 24, 2024

Ok, i'll start looking at the daemon part first. A second IPv4 address should be specified on the command line, and the offer/answer direction flag should be used to choose the desired bridging direction, instead of ipv4/ipv6 bridging option, right?

Is stream_packet() [call.c:553] the only function responsible for packet transmission? I guess the stream->rtp_sink just has to be initalized with the desired address beforehand.

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

My personal preference would be an arbitrary number of local addresses that you can specify at the command line, something like --ip=default/1.2.3.4 --ip=foobar/2.3.4.5 --ip=blah/3.4.5.6 etc... and then have another key within the NG protocol to select one of them, like local-interface=foobar. This could render the internal/external mechanism obsolete, or it could be retained as an alternative selection mechanism. It also allows for grouping of multiple addresses (even from different address families), for example for multiple ICE candidates. The advertised-address switches should also be considered.

The code selecting the source address on outgoing packets is callmaster_msg_mh_src() and is the only place where this needs to be done. For the kernel module, this happens in kernelize() and for signalling/SDP, it's call_stream_address() -- it should be possible to unify the selection code between those three.

The selected source interface can be stored in either one of call_monologue, call_media or packet_stream, perhaps even in the stream_fd. Locking must be considered for safe packet forwarding, since stream_packet doesn't lock the entire call.

from rtpengine.

camilleoudot avatar camilleoudot commented on June 24, 2024

It also allows for grouping of multiple addresses (even from different address families)

do you mean we should be able to specify multiple addresses from both families under the same name, like --ip=foo/1.2.3.4 --ip=foo/9.8.7.6 --ip=foo/::1:2:3:4? Or should we uniquely associate one name to one address?

Also, could you elaborate on grouping for ICE candidates?

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

Well, right now you can have one IPv4 address and one IPv6 address. If both are given and IPv4 is to be used for a particular SIP message, then IPv4 will be used in the c= line. If ICE is also active, then IPv4 will be used as the first ICE candidate, but a second ICE candidate will also be generated with the IPv6 address. And vice versa.

So in this example, the IPv4 address and the IPv6 address form a "group". It would make sense to allow an arbitrary number of address of arbitrary families to be grouped in an arbitrary way. The primary address of each group would be used for c= lines (depending on which family is to be used) and the remaining ones would be used for ICE candidates if ICE is enabled.

So in your example, you'd have 3 addresses in one group named "foo". If IPv4 is desired, 1.2.3.4 would be put into the c= line and ::1:2:3:4 if IPv6 is desired. In either case, all addresses would be used for ICE candidates, but the c= line address always as first candidate (and perhaps all other addresses from the same family as the higher-priority candidates).

This is how I would do it, anyway. Just a suggestion! If you have other/better ideas, then by all means.

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

Just a heads-up that this is in development right and nearing completion. Keep an eye out on the branch rfuchs/interface-bridging

from rtpengine.

camilleoudot avatar camilleoudot commented on June 24, 2024

Hi Richard,
thanks, this is great news. I started implementing something a couple of weeks ago, but did not finish, so we'll now focus on your branch. I'll test it very soon.

from rtpengine.

camilleoudot avatar camilleoudot commented on June 24, 2024

I've tested it, and it's working fine. Is there something left to implement?

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

Nope, just some more testing from our side, but I think it's looking pretty good.

from rtpengine.

rfuchs avatar rfuchs commented on June 24, 2024

Merged into master as of d1165df therefore closing

from rtpengine.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.