Site logo
Stories around the Genode Operating System RSS feed
Johannes Schlatow avatar

Mobile networking in Sculpt


Last year, ssumpf added basic LTE modem support to Genode. Since many Sculpt-compatible laptops come with integrated LTE modems already or can be easily upgraded, I started a little side project to equip Sculpt with easy-to-deploy mobile networking support. In this article, I summarise how to reap the fruits of this project. As a side effect, it showcases the varied possibilities of component compositions.

For structuring this article, I split it in three sections. The first section covers the recommended usage scenario. The second section goes a bit more into detail by describing a more minimalistic setup. Last not least, the last section is targeted at experienced users and developers who want to get some background information for troubleshooting.

The average user (who just wants to use it)

The all-in-one solution is provided in form of a mobile_network package, which comprises all necessary ingredients. You find a deployable package in my personal depot or you can build your own from genode-world. Note, in Sculpt 21.03b, you must manually add my depot files. You can take the download and pubkey files from this pull request.

Before installing mobile_network though, you should look up the vendor id and product id of your modem. You find the list of connected USB devices in /report/drivers/usb_devices. Here is an excerpt of this file on my ThinkPad T490s:

  <devices>
    <device label="usb-1-13" vendor_id="0x2cb7" product_id="0x210" bus="0x1" dev="0x7a" 
            class="0x2">
      <interface class="0x2"/>
      <interface class="0xa"/>
      <interface class="0x2"/>
      <interface class="0xa"/>
    </device>
    <device label="usb-1-12" vendor_id="0x17ef" product_id="0x3083" bus="0x1" dev="0xc"
            class="0x3">
      <interface class="0x1"/>
      <interface class="0x1"/>
      <interface class="0x1"/>
      <interface class="0x3"/>
    </device>
    <device label="usb-2-4" vendor_id="0x17ef" product_id="0x3082" bus="0x2" dev="0x4"
            class="0x2">
      <interface class="0x2"/>
      <interface class="0xa"/>
    </device>
    <device label="usb-1-11" vendor_id="0x17ef" product_id="0x3081" bus="0x1" dev="0xb"
            class="0x9">
      <interface class="0x9"/>
    </device>
  [...]

What I’m looking for is a <device> entry with class=0x2, which is the device class for communication devices. However, as you can see, there are two candidates. Knowing that my dock contains a USB Ethernet controller, I can simply detach the dock, recheck /report/drivers/usb_devices, and find out that my modem has a vendor_id/product_id of 0x2cb7/0x210.

Let’s memorise this and add the following policy rule into /config/usb in order to assign the device to the modem driver component.

   <policy label_suffix="usb_modem_drv -> " vendor_id="0x2cb7" product_id="0x210"/>

With this preparation, you are ready to install the mobile_network package. On deployment, as usual, you will be asked for some routing decisions. Connect Nic to your nic_router. (If it is not available, open the Network dialogue in your Leitzentrale and choose "Local".) For the File_system service, I recommend using recall_fs and for Report, choose "system reports".

The following figure illustrates the (simplified) integration of this package in Sculpt. The package connects to the USB Host Driver and the NIC Router. Moreover, the File_system service (not depicted) is required in order to provide the mbimcli.config.

Integration of the mobile_network package in Sculpt.

Wait a minute! Why does the package need a Nic session at all when it is supposed to provide network access? Counterintuitively, the Nic session actually serves as an uplink, i.e. to provide the nic_router with network access. (I'll provide more details in the last section of this article.)

The first start of the package will not be successful because it needs to be configured with the correct APN and PIN settings. Yet, the package writes a default config to the provided file system if it does not exist. Let’s thus have a look at "recall_fs/mobile_network/config/mbimcli.config":

 <config nic_client_enable="yes">
   <network apn="internet.eplus.de" user="eplus" password="eplus" pin="XXXX"/>
   <default-domain interface="10.0.3.1/24" ip_first="10.0.3.2" ip_last="10.0.3.200"/>
   <vfs>
     <dir name="dev">
       <log/>
       <inline name="rtc">2020-08-05 00:01</inline>
       <dir name="pipe"> <pipe/> </dir>
       <terminal name="cdc-wdm0" raw="yes"/>
     </dir>
     <ram/>
   </vfs>
   <libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/dev/pipe"/>
 </config>

At the moment, we only care about the <network> node. Here, you must at least enter the correct APN for your provider (the search engine of your choice might help) and the PIN code for your SIM card. Unused attributes can be removed.

Having set your credentials, you can restart mobile_network and glance at the log.

 [runtime] child "mobile_network"
 [runtime]   RAM quota:  46856K
 [runtime]   cap quota:  966
 [runtime]   ELF binary: init
 [runtime]   priority:   3
 [runtime]   provides service Nic
 [runtime -> mobile_network -> usb_modem_drv] --- USB net driver ---
 [runtime -> mobile_network -> usb_modem_drv] Using configured mac: 02:00:00:00:01:01
 [runtime -> mobile_network -> vfs] Warning: skipping copy of file /mbimcli.config, 
                                    OPEN_ERR_EXISTS
 [runtime] child "dynamic_depot_rom" requests resources: ram_quota=4369760
 [runtime -> mobile_network -> usb_modem_drv] dev_info: setting rx_max = 16384
 [runtime -> mobile_network -> usb_modem_drv] dev_info: 
 [runtime -> mobile_network -> usb_modem_drv] : USB WDM device
 [runtime -> mobile_network -> usb_modem_drv] netif_info: open: enable queueing (rx 4, tx 4)
                                              mtu 1500 simple framing
 [runtime -> mobile_network -> usb_modem_drv] netif_info: register '
 [runtime -> mobile_network -> usb_modem_drv] ' at usb-usbbus-, CDC MBIM, 02:00:00:00:01:01
 [runtime -> mobile_network -> usb_modem_drv] MAC address 02:00:00:00:01:01
 [runtime -> mobile_network -> nic_router] Warning: no policy defined for label
                                           'usb_modem_drv -> '
 [runtime -> mobile_network -> mbimcli] Warning: unsupported ioctl (request=0x400248a0)
 [runtime -> mobile_network -> mbimcli] 
 [runtime -> mobile_network -> mbimcli] ** (process:0): CRITICAL **: g_file_test: assertion 
                                        'filename != NULL' failed
 [runtime -> mobile_network -> mbimcli] 
 [runtime -> mobile_network -> mbimcli] ** (process:0): CRITICAL **: g_file_test: assertion
                                        'filename != NULL' failed
 [runtime -> mobile_network -> mbimcli] 
 [runtime -> mobile_network -> mbimcli] ** (process:0): WARNING **: [/dev/cdc-wdm0] Couldn't
                                        get descriptors file path
 [runtime -> mobile_network -> mbimcli] Warning: Lost network registration
 [runtime -> mobile_network -> mbimcli] Successfully attached packet service
 [runtime -> mobile_network -> mbimcli] ip     : 10.69.51.107
 [runtime -> mobile_network -> mbimcli] mask   : 255.255.0.0
 [runtime -> mobile_network -> mbimcli] gateway: 10.69.51.1
 [runtime -> mobile_network -> mbimcli] dns0   : 10.74.210.210
 [runtime -> mobile_network -> mbimcli] dns1   : 10.74.210.211
 [runtime -> mobile_network -> nic_router] [uplink] static IP config: interface
                                           10.69.51.107/16, gateway 10.69.51.1 P2P 0
 [runtime -> mobile_network -> nic_router] [uplink] NIC sessions: 0
 [runtime -> mobile_network -> nic_router] [downlink] static IP config: interface
                                           10.0.2.1/24, gateway 0.0.0.0 P2P 0
 [runtime -> mobile_network -> nic_router] [downlink] NIC sessions: 0
 [runtime -> mobile_network -> nic_router] [default] static IP config: interface
                                           10.0.3.1/24, gateway 0.0.0.0 P2P 0
 [runtime -> mobile_network -> nic_router] [default] NIC sessions: 0
 [runtime -> nic_router] [default] NIC sessions: 2
 [runtime -> mobile_network -> nic_router] [downlink] NIC sessions: 1
 [runtime -> mobile_network -> nic_router] [uplink] NIC sessions: 1

Alright, what happened here? Apparently, the mobile_network package deploys a usb_modem_drv, a mbimcli component and its own nic_router. In this section, we don’t care about the details and only take note of the fact that we got an IPv4 address from our network provider as logged by mbimcli. The last missing piece is hidden in the following message:

 [runtime -> nic_router] [default] NIC sessions: 2

The experienced user might already have a clue: The mobile_network was assigned to the default domain of our central nic_router, yet, as indicated in the figure by the two separate NIC interfaces of the NIC Router, we wanted to use mobile_network as a (secondary) uplink to which the traffic from the default domain is forwarded.

In order to achieve this, you first copy /config/managed/nic_router to /config/nic_router (if the latter not already exists) and add a few lines:

 <policy label_prefix="mobile_network -> " domain="mobile_uplink"/>
 <domain name="mobile_uplink">
   <nat domain="default" tcp-ports="1000" udp-ports="1000" icmp-ids="1000"/>
 </domain>

This adds a new domain "mobile_uplink" and the corresponding rule to assign the session request from mobile_network to this domain. Note, that you can keep an already existing "uplink" domain that is typically used for wired/wireless network uplinks. Now, you can control whether to forward your default-domain traffic to "mobile_uplink" instead of "uplink" simply by changing the default-domain config as follows:

 <domain name="default" interface="10.0.1.1/24">
   <dhcp-server ip_first="10.0.1.2"
                ip_last="10.0.1.200"
                dns_server_from="mobile_uplink"/>
   <tcp dst="0.0.0.0/0">
     <permit-any domain="mobile_uplink"/>
   </tcp>
   <udp>
     <permit-any domain="mobile_uplink"/>
   </udp>
   <icmp dst="0.0.0.0/0" domain="mobile_uplink"/>
 </domain>

Done. With this solution, you can switch between mobile and wired/wireless networking by changing the forwarding rules in /config/nic_router. There is no need to stop mobile_network. Don’t worry if you cannot visualise yet how the mobile_network package integrates into Sculpt and how it cooperates with the nic_router. I’ll shed some light on this in the last section.

The minimalist (who wants a stripped down solution)

If you are not keen on maintaining your own /config/nic_router, I have two, more minimalistic, deployment options for you. The first option covers the case that you occasionally want to provide a single application (or a small subset) with mobile connectivity. You may have already noticed a special attribute in the default mbimcli.config:

 <config nic_client_enable="yes">

When setting the nic_client_enable attribute to no, the package will not request a NIC session anymore and solely act as a NIC server as shown in the figure below. You can therefore route Nic requests from any other component to the mobile_network component rather than to Sculpt’s nic_router.

Integration of the mobile_network package in Sculpt with nic_client_enable="no"

The second option is an option in case you don’t like editing /config/nic_router to switch between mobile and wired/wireless networking. What we can do instead is to let the mbimcli component manage /config/nic_router directly and route usb_modem_drv’s Uplink session to Sculpt’s nic_router. For this purpose, you must install usb_modem_drv and mbimcli separately. The latter generates a nic_router.config report once a connection has been established. The resulting setup is shown in the figure below.

Integration of usb_modem_drv and mbimcli in Sculpt.

In order to write make the generated config available to the NIC Router, we only need to write it to /config/nic_router. To achive this, you can deploy the fs_report component with the following launcher and route mbimcli’s config report service to this component.

 <launcher pkg="jschlatow/pkg/fs_report/2021-02-22">
   <route>
     <service name="File_system">
       <parent label="config"/>
     </service>
   </route>
   <config> <vfs> <fs/> </vfs> </config>
 </launcher>

Since the sculpt_manager does not handle Uplink requirements, you must also create a launcher for usb_modem_drv in order to route the Uplink service:

 <launcher pkg="jschlatow/pkg/usb_modem_drv/2021-07-20" priority="-2">
   <route>
     <service name="Uplink">
       <child name="nic_router"/>
     </service>
     <service name="Usb">
       <parent/>
     </service>
     <service name="RM">
       <parent/>
     </service>
   </route>
 </launcher>

Lastly, creating a launcher for mbimcli as well allows to perform label rewriting and to specify the APN and PIN settings. Label rewriting is required for writing the generated config to /config/nic_router instead of /config/nic_router.config.

 <launcher pkg="jschlatow/pkg/mbimcli/2021-07-20" priority="-2">
   <route>
     <service name="Terminal">
       <child name="usb_modem_drv"/>
     </service>
     <service name="Report" label="nic_router.config">
       <child name="config_fs_report" label="nic_router"/>
     </service>
     <service name="Report" label="state">
       <parent/>
     </service>
   </route>
   <config>
     <network apn="internet.telekom" user="t-mobile" password="tm" pin="XXXX"/>
     <vfs>
       <dir name="dev">
         <log/>
         <inline name="rtc">2020-08-05 00:01</inline>
         <dir name="pipe"> <pipe/> </dir>
         <terminal name="cdc-wdm0" raw="yes"/>
       </dir>
       <ram/>
     </vfs>
     <libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/dev/pipe"/>
   </config>
 </launcher>

In result, you end up with three launchers. By default, the Sculpt-managed nic_router config is used but if you get the urge to use your modem, select the "Local" option in Sculpt Network dialogue and simply start all three launchers. Once connected (i.e. '/config/nic_router' was created), you will recognise that the "Wired" and "Wireless" options in the Network dialogue will disappear.

The obvious downside of this approach is that mbimcli gets write access to your system configuration. Alternatively, you could route mbimcli’s config report service to "system reports" and manually copy /reports/runtime/mbimcli/nic_router.config to /config/nic_router. However, this requires your intervention every time you get a new IP address from your provider.

The developer (who wants to know all details)

In the previous sections, I intentionally focused on describing the usage and omitted the details on the internals of the mobile_network package. As promised, I am going to convey the missing pieces in this last section.

Detailed integration of the mobile_network package in Sculpt.

The figure shows the integration of the multi-component mobile_network package in Sculpt. The main ingredients of the package are the USB Modem Driver, MBIM CLI, a VFS and a NIC Router. The modem driver connects to the USB service provided by the USB Host Driver and provides a Terminal service. The latter is used by MBIM CLI to interact with the modem. MBIM CLI gets its config from a file system via the VFS component. Note, that I omitted the intermediate fs_rom component in the figure. When a mobile network connection could be established, MBIM CLI generates and reports a config for the internal NIC Router. The config is made available to the NIC Router via a report_rom component (not shown in the figure) and may look like this, e.g.:

 <config>
   <default-policy domain="default"/>
   <policy label_prefix="usb_modem_drv" domain="uplink"/>
   <domain name="uplink" interface="10.207.25.25/30" gateway="10.207.25.26" use_arp="no">
     <nat domain="default" tcp-ports="1000" udp-ports="1000" icmp-ids="1000"/>
     <nat domain="downlink" tcp-ports="1000" udp-ports="1000" icmp-ids="1000"/>
   </domain>
   <uplink domain="downlink"/>
   <domain name="downlink" interface="10.0.2.1/24">
     <dhcp-server ip_first="10.0.2.2" ip_last="10.0.2.3" dns_server="62.109.121.17"/>
     <tcp dst="0.0.0.0/0">
       <permit-any domain="uplink"/>
     </tcp>
     <udp dst="0.0.0.0/0">
       <permit-any domain="uplink"/>
     </udp>
     <icmp dst="0.0.0.0/0" domain="uplink"/>
   </domain>
   <domain name="default" interface="10.0.3.1/24">
     <dhcp-server ip_first="10.0.3.2" ip_last="10.0.3.200" dns_server="62.109.121.17"/>
     <tcp dst="0.0.0.0/0">
       <permit-any domain="uplink"/>
     </tcp>
     <udp dst="0.0.0.0/0">
       <permit-any domain="uplink"/>
     </udp>
     <icmp dst="0.0.0.0/0" domain="uplink"/>
   </domain>
 </config>

It configures three domains: uplink, downlink and default. The uplink domain is assigned to the USB Modem Driver, which connects to the Uplink service. As usual, every NIC client that connects to the NIC Router is assigned to the default domain. The third domain, downlink, is assigned to the NIC client session that the NIC Router requests itself. This is hidden behind the line <uplink domain="downlink"/>. As you can see, both domains, downlink and default, have a similar configuration. TCP, UDP and ICMP packets from both domains are forwarded to the uplink domain where they are NATed.

Since the internal NIC Router requests a NIC session, it can be cascaded with Sculpt’s NIC Router. By adapting /config/nic_router as mentioned in the first section of this article, we can treat the opened NIC session by mobile_network’s NIC Router like an uplink session.

The figure also shows that MBIM CLI generates a state report. When routed to "system reports", you will find this report at /report/runtime/mobile_network/state. It provides further details on the SIM status, network registration, and connection settings, e.g.:

 <state>
   <device sim="initialized"/>
   <network error="unknown"
            registered="home"
            provider="Telekom.de"
            data_class="lte"
            roaming=""/>
   <signal rssi_dbm="-101" error_rate="5"/>
 </state>

The state report helps identifying any connectivity issues. E.g. if you see a <device sim="device-locked"/>, you probably used a wrong PIN.

On rare occasions, when restarting mbimcli and usb_modem_drv multiple times, I got the modem into a state where mbimcli could not open the device any more and failed with a timeout. To leave this state, I had to reboot the laptop.

Note, that hotplugging the SIM card does not work properly. If you see a <device sim="sim-not-inserted"/> state but are sure that your SIM card is inserted, try rebooting your laptop.