juha-h / baresip-studio

baresip library based SIP client for Android
BSD 3-Clause "New" or "Revised" License
276 stars 70 forks source link

baresip inherits notion of single chosen network interface #54

Closed gdt closed 2 years ago

gdt commented 4 years ago

I have up-to-date baresip from f-droid. I can register to a normal internet SIP provider just fine. I am trying to add an account for a local asterisk instance, with a numeric username and the on-LAN IP address of the server, while my phone is on the LAN. I am seeing messages arrive at the server from 192.168.200.1, where that has absolutely nothing to do with my local LAN addresses (which are RFC1968 but from an entirely different chunk). I am totally boggled as to where this address is coming from.

gdt commented 4 years ago

Strangely: linphone also does not register, but I don't see 192.168.200.1. jitsi registers and can make and receive calls.

I realize I should run adb...

gdt commented 4 years ago

With adb, I realized that the address is coming from tun0, which I am 99% sure is associated with running Orbot (in VPN mode, for an app that is not baresip). Stopping orbot resolved the mysterious address.

juha-h commented 4 years ago

There is no possibility in baresip lib to configure, which interface baresip app is using as its outbound interface. My understanding is that the decision is made by the system based on the IP address.

Is this still an issue?

gdt commented 4 years ago

Yes, it's definitely still an issue.

The packets are coming out on the wifi interface, but they had the tun address on them.

Is baresip just calling sendto()? (Yes, I know I should read the code, now on my todo list :-)

You might be able to repro by 1) installing orbot 2) enabling vpn mode for some apps and adding an app (not baresip) to the list.

licaon-kter commented 4 years ago

Sounds like VPN issues in general, like I saw with Netguard elsewhere: https://github.com/juha-h/baresip-studio/issues/35#issuecomment-497029468

Easy to test, install Netguard, enable Filtering in Advanced (not sure this was needed but it brings some complexity too) then try to connect (over a mobile non-LAN one or mixed to see the full issue).

You can setup the VPN address to some specific one to make it standout.

juha-h commented 4 years ago

Greg Troxel writes:

Yes, it's definitely still an issue.

The packets are coming out on the wifi interface, but they had the tun address on them.

Is baresip just calling sendto()? (Yes, I know I should read the code, now on my todo list :-)

In baresip Linux app, there is config variable that can be used to bind the app to an interface or ip address, for example:

net_interface wlp1s0

or

net_interface 192.168.43.98

It not currently supported by this baresio Android app.

I could add it if you think it would help.

gdt commented 4 years ago

Let me look into this and ask the orbot people. I am sympathetic to the notion that this should not need configuring. baresip doesn't feel special here; in general the source address needs to match the interface that the packet goes out, and in general that should just happen. What I'm not clear on is how the per-app VPN stuff works, and firewall rules to move things around tend to break normal routing flow.

juha-h commented 4 years ago

When you start baresip, it shows with adb logcat to which interface/ip address it is bound. My Android phone for example:

10-13 20:47:54.523 12951 12975 D Baresip Lib: Starting baresip 10-13 20:47:54.553 12951 12976 D Baresip Lib: Local network address: IPv4=ccmni0|10.25.86.27

That can be changed by net_interface config var.

juha-h commented 4 years ago

Version 11.1.0 now has 'Bind Address' configuration item that can be used to choose, which network interface or address baresip is binding itself to. I tested it with my device, where adb shell 'ip addr' shows three IP address interfaces:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ccmni0: <NOARP,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN group default qlen 1000
    link/ppp 0e:3d:e1:38:58:e3 brd ff:ff:ff:ff:ff:ff
    inet 10.25.86.27/8 scope global ccmni0
       valid_lft forever preferred_lft forever
...
144: ap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether aa:3e:0e:ab:65:dd brd ff:ff:ff:ff:ff:ff
    inet 192.168.43.33/24 brd 192.168.43.255 scope global ap0
       valid_lft forever preferred_lft forever
    inet6 fe80::a83e:eff:feab:65dd/64 scope link 
       valid_lft forever preferred_lft forever

Calls worked with both ccmni0 and ap0. I don't know why 'ip route' does not show any default route:

$ ip route
10.0.0.0/8 dev ccmni0 proto kernel scope link src 10.25.86.27 
192.168.43.0/24 dev ap0 proto kernel scope link src 192.168.43.33 

If bind address is not set, baresip perhaps chooses the first non-local interface.

Give it a try.

gdt commented 4 years ago

I will try that when it hits fdroid or I hand build it, whichever comes first.

But, I am quite boggled by this notion of binding to interface at startup. baresip is a long running process and will need to switch between wifi and WAN data dynamically. Or, if it could work over TCP or tor did UDP, and if I configured orbot to proxy it, to that.

gdt commented 4 years ago

Indeed I am seeing tun0 chosen at startup sometimes {{{ 10-14 12:12:15.550 18587 18606 D Baresip Lib: Local network address: IPv4=tun0|192.168.200.1 }}}

It seems to be sensitive to the order interfaces have become valid; while on WiFi I started orbot and still wlan0 was picked, but after turning wifi off and on, it started choosing tun0 on startup (was force-killing and restarting, watching logcat).

juha-h commented 4 years ago

Greg Troxel writes:

But, I am quite boggled by this notion of binding to interface at startup. baresip is a long running process and will need to switch between wifi and WAN data dynamically.

I'm sure libre (that handles baresip networking stuff) is able to dynamically switch to currently active network. That is why the idea of binding the network interface in config is not a good idea except in some special situations. VPN may be one of those.

juha-h commented 4 years ago

Greg Troxel writes:

Indeed I am seeing tun0 chosen at startup sometimes {{{ 10-14 12:12:15.550 18587 18606 D Baresip Lib: Local network address: IPv4=tun0|192.168.200.1 }}}

It seems to be sensitive to the order interfaces have become valid; while on WiFi I started orbot and still wlan0 was picked, but after turning wifi off and on, it started choosing tun0 on startup (was force-killing and restarting, watching logcat).

Perhaps it chooses the first active interface at startup. "adb shell ip addr" tells the order.

juha-h commented 4 years ago

juha-h writes:

Perhaps it chooses the first active interface at startup. "adb shell ip addr" tells the order.

I checked the code (rem src/net/net.c) and it chooses source IP address first from default network interface (if one exists) and if not then from first non-local IP interface.

libbaresip check every 60 seconds if this IP is still exists and if not chooses another one.

juha-h commented 4 years ago

@gdt Version that includes Bind Address setting is now available in F-Droid. Perhaps bind address setting helps with VPN.

afcady commented 4 years ago

baresip (NB not baresip-studio) has a deeply-embedded design problem: it assumes there will only be one IP address per address family (one IPv4, and one IPv6). It creates a single struct network value that contains a single global value for IP address. There is just one global IP address field for each family (named laddr and laddr6). This was a mistake in the design from the beginning, that results in many problems.

The proper behavior would be to have SIP listen on all interfaces and addresses; then, when a connection is received, find the route to the remote IP address, and use that to determine the IP addresses to send over to establish the RTP link (the way SIP works, IP addresses are actually sent over to the remote side using the SIP channel).

Outgoing connections should work the same way: the IP address to use on the local side should be determined by the address of the remote side (first resolve the name to an IP, then get the route to that IP).

Looking briefly at baresip-studio it appears that it is using the same struct network as returned by baresip_network(). Actually it appears that juha-h wrote the net_set_address() function and contributed it to baresip.

This isn't sufficient to actually get the correct behavior. The only way to do a totally correct fix that does not involve modifying the baresip repo is to have a separate instance of struct network for every IP address.

Better is to modify struct network itself, but the author of baresip does not appear interested in fixing the fundamental problem. He is closing any bug reports deriving from this problem. See e.g. https://github.com/alfredh/baresip/issues/330.

Personally I'm wanting to solve this problem because I want VOIP links both on my local LAN, and over a VPN, and I suppose I would want it to work over the normal unencrypted internet as well. Right now I would need 3 separate baresip instances on my laptop (one for each IP address) to get it to work completely. On the phone where I run baresip-studio, there is only one IP address, so this isn't actually an issue for me in baresip-studio. But apparently the fix is common to both clients.

I would be willing to work on baresip and fix this problem the right way (to modify struct network so only one instance is needed) but I am not interested in maintaining a fork (it is a deep change so it would be a lot of work to maintain this as a fork). Doing it the ugly way with separate instances is probably the best option unless we get the upstream author(s) on board (which is probably not going to happen).

Another ugly option, which is less correct, is to call net_set_address() on every incoming connection and before initiating any outgoing connection, according to the address of the remote side. This is probably the easiest fix. This approach, a global value that changes automatically, isn't truly capable of correctness but is, nevertheless, strictly more correct than the status quo. I.e. it will only fail to fix some existing bugs but will not create new bugs.

gdt commented 4 years ago

@afcady Thanks for the analysis and comments. What you say makes sense in terms of me understanding how things seem to be with how I think they ought to be (which matches your view, but you have said it more clearly). It's unfortunate that this serious problem in baresip does not seem likely to get fixed.

juha-h commented 4 years ago

@afcady writes:

Another ugly option, which is less correct, is to call net_set_address() on every incoming connection and before initiating any outgoing connection, according to the address of the remote side. This is probably the easiest fix. This approach, a global value that changes automatically, isn't truly capable of correctness but is, nevertheless, strictly more correct than the status quo. I.e. it will only fail to fix some existing bugs but will not create new bugs.

I also thank for the analysis.

baresip-studio gets this kind of information from Android OS regarding each active network connection:

11-18 09:12:38.801 19360 19387 W Baresip Service: Active network 191 link properties changed: {InterfaceName: ccmni0 LinkAddresses: [10.132.222.97/32,]  Routes: [0.0.0.0/0 -> 10.132.222.97 ccmni0,10.132.222.97/32 -> 0.0.0.0 ccmni0,] DnsAddresses: [192.168.170.28,] UsePrivateDns: false PrivateDnsServerName: null Domains: null MTU: 1500 TcpBufferSizes: 40778,244668,734003,16777,100663,301990}

Then based on that, it modifies baresip addresses:

11-18 09:12:38.813 19360 19387 D Baresip Lib: setting dns servers '192.168.170.28:53'
11-18 09:12:38.816 19360 19387 D Baresip Lib: unsetting IPv6 address
11-18 09:12:38.816 19360 19387 D Baresip Lib: setting af '2'
11-18 09:12:38.817 19360 19387 W Baresip : Found IPv4 address '10.132.222.97'
11-18 09:12:38.818 19360 19387 D Baresip Lib: setting address '10.132.222.97'
11-18 09:12:38.818 19360 19387 D Baresip Lib: setting af '2'
11-18 09:12:38.818 19360 19387 D Baresip Lib: forcing net change
11-18 09:12:38.818 19360 19391 D Baresip Lib: IP-address changed: 10.132.222.97

But as you point out, in baresip there can currently be only one network active at any one time, I don't see how baresip-studio at the application layer could handle the case where, for example, a call is going on over VPN and at the same a chat session over the public default interface.

Also, it would be quite a task for the application (baresip-studio) to analyze which active network to use for each outgoing SIP request and set the baresip network accordingly.

Also, registrations are handled by libre out of control of baresip application. So I don't see any way a baresip app could change the network address before each registration event when the registering UAs are using different networks.

gdt commented 4 years ago

(I think this bug is now being left open about the larger issue of source selection.)

I am now able to register with a server on the LAN when Orbot VPN is enabled but not doing VPN for baresip.

afcady commented 4 years ago

I had looked into this more over a week ago, and meant to reply, but I had to get surgery and didn't get back to it. The main takeaway is that I didn't realize earlier, that there is a global structure in baresip that holds the *struct network and so it is not even possible, without modifying baresip, to have multiple struct network values.

However, I think there is still advantage to automatically calling set_network at various points. I intend to write this code and see if I can improve my own use cases.

I don't see how baresip-studio at the application layer could handle the case where, for example, a call is going on over VPN and at the same a chat session over the public default interface.

Right, it couldn't. But it can't right now either.

it would be quite a task for the application (baresip-studio) to analyze which active network to use for each outgoing SIP request

Not sure if this is what you mean, but it's no hard task to find out which active network to use, if you have a remote address. In Linux you can just do this at the shell to handle the remote address google.com:

$ ip route get $(dig +short +answer -t aaaa google.com) | sed 's/.* src //; s/ .*//'
2601:500:88:200::7

The output (the second line) is the default source IP address for the remote address google.com.

Also, registrations are handled by libre out of control of baresip application. So I don't see any way a baresip app could change the network address before each registration event when the registering UAs are using different networks

I see. Good point. Personally I am not even using registrars, though, so it does not matter to my use case. But I think maybe even for the registrar use case, patching re on this point might not be so invasive, so that it could be kept as a fork, if necessary. (Keeping in mind that, practically speaking, the registrar is always going to be on the same interface as every peer connected through that registrar.)

I'm not sure if I remember this correctly, or even understood it correctly before, but I think there is a struct sip, that contains somewhere a reference to the network, and that gets passed to re, and it's there that we could make this value contain more than one network, for example using a map from registrar address to network.

juha-h commented 4 years ago

Thanks for your reply. Hope everything is fine after surgery. I'm not eager to make an extensive fork of libre, but rather try to convince libre folks to add multi-homed network support to it.

What I could do is to reintroduce net_interface setting in baresip-studio so that users could fix the net that the app is using. Negative side effect of it is that if the fixed network is not available, no other network will be used.

gdt commented 4 years ago

I've changed the issue title to reflect that this is now about the problem that baresip-studio, because of libraries it uses, is choosing a network interface overall, rather than one per destination. (This is likely to be trouble only for VPN users.)

m32 commented 3 years ago

In my case (openvpn) no network is selected and baresip is running without a network address, adding this piece solved the vpn connection problem. in BaresipService.kt after line: val connectivityManager=getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    val alln=connectivityManager.allNetworks
    for(n in alln) {
        val linkProps = cm.getLinkProperties(n)
        if (linkProps != null) {
            val interfaceName = linkProps.interfaceName!!
            if (n == cm.activeNetwork) {
                Log.i(LOG_TAG, "Active network $n@$interfaceName " +
                        " is available: $linkProps")
                activeNetwork = "$n"
                if (isServiceRunning) {
                    Utils.updateLinkProperties(linkProps)
                } else {
                    dnsServers = linkProps.dnsServers
                    linkAddresses = linkProps.linkAddresses
                }
            } else {
                Log.i(LOG_TAG, "Non-active network $n@$interfaceName " +
                        " is available: $linkProps")
            }
        }
    }
juha-h commented 3 years ago

Thanks for coming back to this old issue.

If I start baresip with a VPN connected, I get this to logcat:

11-14 09:59:06.998 27176 27212 I Baresip Service: Non-active network 106@ccmni0  is available: {InterfaceName: ccmni0 LinkAddresses: [10.240.196.214/32,2001:14bb:70:aedb:63b:d39c:e5f0:82c4/64,]  Routes: [0.0.0.0/0 -> 10.240.196.214 ccmni0,::/0 -> fe80::63b:d39c:e5f0:82c4 ccmni0,10.240.196.214/32 -> 0.0.0.0 ccmni0,2001:14bb:70:aedb::/64 -> :: ccmni0,] DnsAddresses: [62.241.198.245,2001:14b8:1000::1,62.241.198.246,2001:14b8:1000::2,] UsePrivateDns: false PrivateDnsServerName: null Domains: null MTU: 1500 TcpBufferSizes: 2097152,4194304,8388608,262144,524288,1048576}
11-14 09:59:07.006 27176 27212 D Baresip Service: Network 106@ccmni0  link properties changed: {InterfaceName: ccmni0 LinkAddresses: [10.240.196.214/32,2001:14bb:70:aedb:63b:d39c:e5f0:82c4/64,]  Routes: [0.0.0.0/0 -> 10.240.196.214 ccmni0,::/0 -> fe80::63b:d39c:e5f0:82c4 ccmni0,10.240.196.214/32 -> 0.0.0.0 ccmni0,2001:14bb:70:aedb::/64 -> :: ccmni0,] DnsAddresses: [62.241.198.245,2001:14b8:1000::1,62.241.198.246,2001:14b8:1000::2,] UsePrivateDns: false PrivateDnsServerName: null Domains: null MTU: 1500 TcpBufferSizes: 2097152,4194304,8388608,262144,524288,1048576}

Based on that it might be possible to update link properties and thus make baresip to use that network.

But when I disconnect the VPN, baresip does not get any ConnectivityManager NetworkCallback about that.

So the question is, how to make baresip aware of all VPN connectivity changes?

m32 commented 3 years ago

I have similar messages and as a result baresip thread is started without ip assigned. As I added the default network, everything suddenly started and probably will stop working if I change wifi to lte, after some time (connection lost timeout) openvpn will start but it may go unnoticed and baresip will remain running with lte (active network change notification).

So this doesn't look like a bug with baresip(-studio) to me. A similar error https://community.openvpn.net/openvpn/ticket/313 is with network change detection in openvpn itself.

juha-h commented 3 years ago

I played a bit with OpenVPN. With the onAvailable function below, baresip starts OK both when VPN is connected and not. I'll still try to figure out if it is somehow possible to detect VPN connect/disconnect on the fly.

                    override fun onAvailable(network: Network) {
                        super.onAvailable(network)
                        if (Build.VERSION.SDK_INT >= 23) {
                            // Use VPN if available
                            for (n in cm.allNetworks) {
                                val caps = cm.getNetworkCapabilities(n) ?: continue
                                val linkProps = cm.getLinkProperties(n)
                                if (caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) &&
                                        (n == cm.activeNetwork) && (linkProps != null)) {
                                    Log.i(LOG_TAG, "Active VPN network $network " +
                                            " is available: $linkProps")
                                    activeNetwork = "$n"
                                    if (isServiceRunning) {
                                        Utils.updateLinkProperties(linkProps)
                                    } else {
                                        dnsServers = linkProps.dnsServers
                                        linkAddresses = linkProps.linkAddresses
                                    }
                                    return
                                }
                            }
                        }
                        if (Build.VERSION.SDK_INT >= 23) {
                            val linkProps = cm.getLinkProperties(network)
                            if ((linkProps != null) && (network == cm.activeNetwork)) {
                                Log.i(LOG_TAG, "Active network $network is available: $linkProps")
                                activeNetwork = "$network"
                                if (isServiceRunning) {
                                    Utils.updateLinkProperties(linkProps)
                                } else {
                                    dnsServers = linkProps.dnsServers
                                    linkAddresses = linkProps.linkAddresses
                                }
                            } else {
                                Log.i(LOG_TAG, "Non-active network $network" +
                                        " is available: $linkProps")
                            }
                        }
                    }
m32 commented 3 years ago

I'm not sure if that's okay. What if the sip connection is made not by vpn but by local network? Instead, specifying an IP address should be associated with a route to the registration server when network settings are changed independently of the VPN connection.

Depending on your VPN settings, it can take over all traffic or traffic only to the selected subnet, so in bash on ubuntu, finding ip looks like this: ip route get 8.8.8.8 | head -n1 | awk '{print $5}'

https://unix.stackexchange.com/questions/14961/how-to-find-out-which-interface-am-i-using-for-connecting-to-the-internet

juha-h commented 3 years ago

Grzegorz Makarewicz writes:

I'm not sure if that's okay. What if the sip connection is made not by vpn but by local network?

Instead, specifying an IP address should be associated with a route to the registration server when network settings are changed independently of the VPN connection.

Depending on your VPN settings, it can take over all traffic or traffic only to the selected subnet, so in bash on ubuntu, finding ip looks like this: ip route get 8.8.8.8 | head -n1 | awk '{print $5}'

I don't think that the above is practical, since it is not mandatory that an account registers or specifies an outbound proxy. Also, there can be multiple accounts with different outbound proxies.

When I check available networks at baresip startup, I get these two when OpenVPN is connected:

11-15 08:52:18.144 20030 20068 I Baresip Service: Network 106  is available with caps: [ Transports: CELLULAR Capabilities: SUPL&INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN&VALIDATED&NOT_ROAMING&FOREGROUND&NOT_CONGESTED&NOT_SUSPENDED Unwanted:  LinkUpBandwidth>=51200Kbps LinkDnBandwidth>=102400Kbps Specifier: <6>], props: {InterfaceName: ccmni0 LinkAddresses: [10.240.196.214/32,2001:14bb:70:aedb:63b:d39c:e5f0:82c4/64,]  Routes: [0.0.0.0/0 -> 10.240.196.214 ccmni0,::/0 -> fe80::63b:d39c:e5f0:82c4 ccmni0,10.240.196.214/32 -> 0.0.0.0 ccmni0,2001:14bb:70:aedb::/64 -> :: ccmni0,] DnsAddresses: [2001:14b8:1000::1,2001:14b8:1000::2,62.241.198.245,62.241.198.246,] UsePrivateDns: true PrivateDnsServerName: 1dot1dot1dot1.cloudflare-dns.com Domains: null MTU: 1500 TcpBufferSizes: 2097152,4194304,8388608,262144,524288,1048576}
11-15 08:52:18.151 20030 20068 I Baresip Service: Network 174  is available with caps: [ Transports: VPN Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&NOT_ROAMING&FOREGROUND&NOT_CONGESTED&NOT_SUSPENDED Unwanted: ], props: {InterfaceName: tun0 LinkAddresses: [10.8.0.10/30,]  Routes: [10.8.0.8/30 -> 0.0.0.0 tun0,10.8.0.1/32 -> 0.0.0.0 tun0,0.0.0.0/0 -> 0.0.0.0 tun0,::/0 unreachable,] DnsAddresses: [8.8.8.8,] UsePrivateDns: true PrivateDnsServerName: 1dot1dot1dot1.cloudflare-dns.com Domains:  MTU: 0}

Both of them are capable of reaching the internet, but only the latter (174) is currently active network.

So how about adding a setting that allows the user to tell if he/she wants to prefer an active VNP connection if one is available?

juha-h commented 3 years ago

I added support for VPNs. In my tests, baresip now starts OK with or without OpenVPN VPN connected and is able to switch between cellular and VPN Internet access when VPN is connected/disconnected.

There is no support for using VPN for some destinations and cellular for some others, since there is no such support in underlying baresip libraries.

@m32 Could you give it a try?

m32 commented 3 years ago

If I have baresip + turned on, and I change the openvpn status, the logged in status in baresip + does not change. Only restarting baresip + introduces a change, but it is much better. If I have a network, sip works fine.

juha-h commented 2 years ago

@gdt There has finally been some progress with this issue. baresip app version 34.0.0 (available from Play Store and soon from F-Droid) now supports multiple simultaneously active network interfaces including VPN interfaces. The implementation is not complete yet though. In outgoing calls baresip is not yet able to set correct media IP address in SDP in cases where destination of request is domain name. This issue can be circumvented by defining outbound proxy URI(s) so that host in the URI(s) is an IP address.

afcady commented 2 years ago

Very exciting!!!

juha-h commented 2 years ago

Thanks to recent commits to upstream baresip and re libraries, baresip app (version 36.0.0) is now able to use multiple network interfaces.

For example, if a VPN is connected that provides route only to, say, a corporate network, then baresip app uses the VPN for communication with the corporate network and normal WiFi or Cellular Data network for communication with the rest of the world.

Also, when Internet connectivity of Android device changes, for example, from Cellular Data to WiFi, baresip app immediately switches to use the new active network and (if a call is active) sends re-INVITE to its peer.

I'll now close this long standing issue. Reopen if problems still persist.