Quectel UC20: Correct way to get it up and running on embedded?

Einar Jón tolvupostur at gmail.com
Wed Mar 22 20:36:29 UTC 2017


TLDR: I guess most of my problems are from calling ifup in the wrong
state (CONNECTING instead of CONNECTED). More inline.
I might bring some follow-up questions next week.

On 20 March 2017 at 19:33, Dan Williams <dcbw at redhat.com> wrote:
> On Mon, 2017-03-20 at 12:14 +0100, Einar Jón wrote:
>> Hello,
>>
>
> Let me preface my response by saying I have no idea what ifup/ifdown do
> on your platform, and whether they correctly handle teardown of the
> interface and DHCP.
>
>> Our main issues:
>> Operstate seems to be always UNKNOWN
>
> You can basically ignore operstate.  You care about interface flags
> instead (UP, DOWN, LOWER_UP, etc).

I should have also mentioned that the interface flags are always UP,
LOWER_UP with an operstate UNKNOWN. So ip addr/ifconfig is always the
same for me.

When using the Quectel UC20 with ModemManager - no matter what the state is:
$ ip a show dev wwan0
4: wwan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
state UNKNOWN qlen 1000
    link/ether 2a:eb:68:af:9e:1b brd ff:ff:ff:ff:ff:ff
    inet6 fe80::28eb:68ff:feaf:9e1b/64 scope link
       valid_lft forever preferred_lft forever

Different OS using the Quectel UC20 without ModemManager (Using custom
C code with AT commands):
$ ip a show dev wwan0
5: wwan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group
default qlen 1000
    link/ether 0a:f6:61:71:71:e4 brd ff:ff:ff:ff:ff:ff
$ sudo rild -q &  # this connects the modem
[1] 1417
$ ip a show dev wwan0
5: wwan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
state UNKNOWN group default qlen 1000
    link/ether 0a:f6:61:71:71:e4 brd ff:ff:ff:ff:ff:ff
    inet XX.YY.153.249/30 brd XX.YY.153.251 scope global wwan0
       valid_lft forever preferred_lft forever

>
>> "ip link set dev wwan0 up" and "ip link set dev wwan0 down" seem to
>> do nothing.
>
> Right.  That's normal Linux.  These set/clear the IFF_UP flag which
> means "administratively up".  Which means that when the interface is
> UP, it can do IP and other stuff.  When it's down, it can't.  That's
> all.  It doesn't affect *what* addressing is done on the interface,
> it's simply a pre-requisite for doing anything with the interface.

Good to know...

>> We need to reset the connection once a day (with mmcli --disable),
>> because of external reasons.
>
> I presume the provider terminates the packet data session after 24h or
> something like that?  Not uncommon.

Exactly why we need that.

>
>> This mostly works, but sometimes we lose connectivity after the
>> reconnect.
>> In those cases I have 2 IP addresses in the interface, and ip route
>> tells me that both of them are gateways.
>
> Since MM doesn't do any IP address setup/teardown on the interface
> itself, I wouldn't expect '--disable' alone to clean anything up.  When
> disabling, you'll need to either have a process monitor the modem state
> and call "ifdown" or do some other kind of cleanup, or do the ifdown
> along with the --disable.
>
>> "ifup wwan0 && ifdown wwan0" fixes the connection again without any
>> action from the python script (this automatically starts a new
>> dhclient and updates ip route).
>> This seems to be just a timing issue, or a question of doing things
>> in
>> the right order.
>
> The order would probably be to disconnect the bearer (mmcli -m X -b Y
> --disconnect), "ifdown wwan0", "mmcli -m X --disable".

That's worth a try.

>
>> My main questions:
>> 1) what do I need to do to have a reliable connection?
>
> That depends on quite a few things, many of them not ModemManager
> related, so let's try to address them individually as they come up.
>
>> 2) when do I need to do those things?
>>   -  in what state should I call ifup ?
>
> After the modem state becomes CONNECTED, or after the bearer
> activation/connect returns successfully and you can retrieve the bearer
> IP details from MM.  CONNECTED state requires that the bearer
> activation be successful, so if you see CONNECTED you should be fine.
>
>>   -  in what state should I call connect_bearer()? Any exceptions?
>
> Bearer-interface specific operations require the modem to be in the
> right state (eg, something like REGISTERED).  I would wait to connect a
> bearer until the modem has found the network and registered.

So CONNECTING is too soon.
We currently try to connect_bearer when we reach REGISTERED. At least
that's right...

>
> If you use the Simple interface instead, you can call Simple.Connect()
> at any time other than FAILED state, as the Simple interface will do
> everything it can to get connected, including enabling the modem,
> waiting for registration, and then starting the bearer.  That could
> take a few minutes, but it's meant as a dead-simple interface.

I would like to stick with what we have.

>
>> 3) I switch modem states really fast (from DISABLING to CONNECTING in
>> under a second - see syslog below). Do some of these states need to
>> wait a bit?
>
> ModemManager won't return from the D-Bus requests for
> disabling/enabling/connecting/etc until it's ready; if it does so and
> the modem hasn't fully entered that state, I'd consider it a bug.
>
> But I don't think that's your problem, see below.

Probably just incorrect use.

>
>> 4) Is ModemManager 1.4.10 too old? Are there any major improvements
>> I'm missing from staying on Ubuntu 12.04?
>
> Obviously we always recommend the most recent released version
> (currently 1.6.x) but I can't think of anything specific that 1.4.x has
> that you'd need in terms of bug fixes or features.

Good to know. Upgrading would be a pain.

>> NOTE: To find out the status of the connection, I check if the
>> IfState
>> is up or down (basically "grep wwan0  /run/network/ifstate"), instead
>> of checking operstate (which is UNKNOWN no matter what I do).
>> If ifstate is down I call "ifup wwan0" manually.
>> When disconnecting (and on the pyhon startup), I call "ifdown wwan0"
>> manually.
>
> This could be part of the problem.  The network stack of your distro
> (especially ifup/ifdown) has literally no idea what state the modem is
> in.  There is no real connection between the modem firmware state and
> the Linux-side network interface; it's just a packet tunnel.  Its
> Linux-side state doesn't represent anything about the data connection
> in the modem firmware itself.

Right. I as an admin have literally no idea what state the modem is in.
So this seemed to make sense at the time.

>
> While perhaps unfortunate, there are various reasons why this is the
> case and I'm happy to explain further if you'd like.
>
> The point being:
>
> 1) when the modem enters the CONNECTED state, call "ifup wwan0"
> 2) when the mode leaves the CONNECTED state, call "ifdown wwan0" and
> wait for that to complete before doing anything else

I guess I'm calling it too soon (in CONNECTING state), and I wait for
ifup to finish before going to CONNECTED.
This may be causing many of my issues.
I did that because we have had a bearer_connect failure with
PolicyMismatch that does an endless
CONNECTING -> REGISTERED -> CONNECTING -> REGISTERED loop until the
modem is reset.
So I thought ifup would be needed for that.

> don't bother checking the existing ifup/ifdown state because it may not
> be accurate, because ifup/ifdown don't know anything about the actual
> modem state.

True. But it seems to be the only way to know if something is actually
happening or not, since the
And calling "ifdown wwan0; ifup wwan0" manually seems to fix the
double IP issue.
So it seemed like a good idea at the time.

>
> One last comment about the double-address thing; it's in the logs below
> but I'll say it here instead.
>
> I don't think your process is waiting for "ifdown wwan0" to complete
> before attempting to re-enable the modem.  ifdown may take a short
> period of time to clean things up, including letting DHCP release the
> address.
>
> Mar 19 06:35:52 ifdown
> Mar 19 06:35:52 re-enable
> Mar 19 06:35:52 registered
> Mar 19 06:35:53 dhclient is calling DISCOVER
> Mar 19 06:36:53 dhclient gets a lease
> Mar 19 06:37:16 ifup
> Mar 19 06:37:17 finally CONNECTED state
>
> I think the old dhclient process is sticking around requesting a lease
> long before you're at the point where you call 'ifup'.  I'd suggest
> waiting until the 'ifdown' completes before re-enabling the modem.
> That is likely where your duplicate addresses are coming from.

I don't think you are correct. What is actually happening:
Mar 19 06:35:52 Modem state: CONNECTED -> DISABLING
--- ifdown is called when we reach state DISABLING
Mar 19 06:35:52 ifdown RETURNS and logs everything that happened since
it was called
Mar 19 06:35:52 Modem state: DISABLING -> DISABLED
Mar 19 06:35:52 Modem state: DISABLED -> ENABLING
Mar 19 06:35:52 Modem state: ENABLING -> REGISTERED
Mar 19 06:35:52 Modem state: REGISTERED -> CONNECTING
--- ifup is called when we reach state CONNECTING - this surely too soon
Mar 19 06:35:53 dhclient is calling DISCOVER
Mar 19 06:36:53 dhclient gets a lease
Mar 19 06:37:16 ifup RETURNS and logs everything that happened since
it was called at 06:35:52
Mar 19 06:37:17 finally CONNECTED state
-- I should be calling ifup here

Thanks for the detailed response.
Einar

>
> Dan
>
>> Relevant python code:
>> def handle_modem_state(self, oldState, state):
>>     logging.debug('Handle modem state, old: ' +
>> self.modem_state_str(oldState) + ' new: ' +
>> self.modem_state_str(state))
>>     if state == ModemManager.ModemState.DISABLED:
>>         self.enable_modem()     # calls modemProxy.Enable(1, ...)
>>     elif state == ModemManager.ModemState.ENABLED:
>>         self.register_modem()   # calls modemProxy.Register(...)
>>     elif state == ModemManager.ModemState.SEARCHING:
>>         pass
>>     elif state == ModemManager.ModemState.REGISTERED:
>>         if oldState != ModemManager.ModemState.CONNECTING and
>> oldState
>> != ModemManager.ModemState.DISCONNECTING:
>>             self.connect_bearer()  # get bearer from DBUS and connect
>>     elif state == ModemManager.ModemState.CONNECTED:
>>         self.triedPreferredOperator = False
>>     elif state == ModemManager.ModemState.CONNECTING:
>>         if not self.is_network_interface_up('wwan0'): # check
>> operstate/ifstate
>>             self.set_network_state('wwan0', 'up')     # calls ifup
>> wwan0 in a new process
>>     elif state == ModemManager.ModemState.DISABLING:
>>         if self.is_network_interface_up('wwan0'):     # check
>> operstate/ifstate
>>             self.set_network_state('wwan0', 'down')   # calls ifdown
>> wwan0 in a new process
>>

... snip ...



-- 
Regards
Einar Jón
+31 610 957234


More information about the ModemManager-devel mailing list