GSoC 2024: Development of a Modular Assistant for OpenWrt Update

Project Objectives

What has been achieved in this first half?

The goal is to design an OpenWrt configuration wizard to simplify the device configuration process, especially for those users who do not have deep technical knowledge.

1. Improve UI:

A clean, modern and easy-to-use user interface was developed for everyone. It allows you to follow a step-by-step process to configure the device with clear and well-defined options, with intuitive navigation between steps, allowing users to move back and forth with ease.

2. Detailed Configuration Steps

Step 1: Language Selection

Users can choose the language of their preference for the wizard, thus allowing a better understanding of the process.

Step 2: Security

At this stage, users can enter the device name and set administrator passwords. Validations have been implemented to ensure that passwords meet security requirements: at least 10 characters, including numbers, symbols, and a combination of upper and lower case letters.

Step 3: Internet Connection

Here you select the type of Internet connection you want to configure:

– DHCP: The router automatically obtains the IP address and other network configuration parameters from the Internet Service Provider (ISP), simplifying the configuration process.

– Static IP: Allows users to manually enter the IP address, subnet mask, gateway and DNS servers, useful for networks that require specific configurations or when using a fixed IP address.

– PPPoE: Primarily used in DSL connections, it requires the user to enter a username and password provided by the ISP.

Step 4: Wireless Configuration

At this stage, users can configure their router’s wireless network:

– SSID: The name of the Wi-Fi network that devices will see when searching for available networks.

– Wi-Fi Password: Users can set a password for their Wi-Fi network, with security validations similar to administrator passwords.

– Wireless Encryption Type: We have implemented the selection of the encryption type to improve network security.

– Mesh Network: Users can configure mesh networks to expand the coverage of their Wi-Fi network, improving connectivity in large areas.

Step 5: Additional Services

In this section additional services such as longitude, latitude or activating options are enabled:

– VPN: Allows users to securely connect to the local network from remote locations.

– DHCP: Allows the router to automatically assign IP addresses to devices on the local network.

Step 6: Summary

The last step that users encounter is a confirmation summary of the process choices.

Importance of the Advances Made

Flexibility and Security

Allowing selection of wireless encryption type is crucial for users to secure their network according to their specific needs and device compatibility. WPA3, for example, offers significantly improved security compared to WEP.

Easy to use

The new interface and step-by-step navigation simplify the setup process, making it accessible even to those without deep technical knowledge. This lowers the barrier to entry and allows more people to use and benefit from OpenWrt.

Mesh Network Configuration

Integrating the mesh networking option expands network coverage, improving connectivity over large areas and providing a more consistent and reliable user experience.

Next steps

Integration of More Options for Additional Services

Add additional services to add more functionality.

User Interface Optimization:

Modify the user interface based on the feedback received to make it as easy and intuitive as possible.

Exhaustive Testing

Perform tests to ensure the operation and stability of the assistant.

GSoC 2024: eBPF performance optimizations for a new OpenWrt Firewall, Midterm update

Hello again, everybody! This is the Midterm follow-up blog post for my GSoC 2024 topic: “eBPF performance optimizations for a new OpenWrt Firewall.” It will cover how I started trying to solve the task, what the current implementation looks like, and what I will do in the upcoming weeks.

As a quick reminder: The project’s goal is to implement a new OpenWrt Firewall offloading variant using eBPF. Why eBPF? Because with eBPF, you can intercept an incoming data packet from the NIC very soon inside or even before the Linux network stack. After intercepting the packet with an eBPF program at the so-called XDP or TC hook, you can mangle it, redirect it to another or out of the same network interface, or drop it. Mangling the packet could mean, for example, applying possible Network Address Translation (NAT), adjusting the Time-To-Live (TTL), or recalculating the checksum(s).

The result should be that we see a performance increase, either by having a higher throughput, dropping packets faster, or lowering the CPU load.

Current implementation

The implementation consists of three components:

  • The eBPF program which intercepts, mangles, and forwards or drops incoming data packets from a network interface
  • A user-space program that attaches the eBPF program to the appropriate network interfaces and determines whether to forward a received packet and where to
  • An eBPF map (in this case, a key-value hash map) so that the eBPF and user-space program can communicate with each other

Originally, I wanted to parse all OpenWrt Firewall rules and dump them into the eBPF map when the user-space program starts. When the eBPF program received a packet, it would try to match it with one of the parsed rules. But I had a few talks with the OpenWrt community and my mentor and concluded that this approach poses some problems:

  1. eBPF has limited looping support, but for rule matching, it is necessary to loop.
  2. OpenWrt uses the Netfilter framework as its firewall backend that has (too) complex features to implement in eBPF, like for example the logging of packets.

That is why we decided to go for a “flow-based” approach. When the eBPF program receives a packet, it creates a tuple from some crucial packet identifiers (Ingress interface index, L3 and L4 protocols, and source and destination IPs and ports). The program uses this tuple as the key for the eBPF hash map to signal the user-space program that it has received a packet for a new flow so that it can look up what the eBPF program should do with packets for that particular flow.

Until the user-space program responds, the eBPF program passes all packets belonging to that flow to the network stack, where the Netfilter framework processes it for now. In the meantime, the user-space program checks what the eBPF program should do with packets from that flow and stores the result inside the hash map as the value.

Connection Tracking must also be available because the to-be-implemented offloading variant should be stateful instead of stateless. I first thought about implementing it in the eBPF or user-space program. But then I realized I would somewhat reinvent the wheel because OpenWrt uses the Netfilter framework, which has a connection tracking implementation called nf_conntrack.

The Netfilter project provides an API through their user-space library libnetfilter_conntrack to add, retrieve, modify, and delete connection tracking entries. I am using this API in my implementation to check whether a conntrack entry exists for a packet flow. In the case of TCP, it only forwards packets while a connection is in the “Established” state so that Netfilter can still handle the opening and closing states of the TCP connections. In the case of UDP, the eBPF offloader starts forwarding packets on its own as soon as and as long as a conntrack exists. The user-space program meanwhile updates the timeouts for offloaded connections.

And there is a charm when using nf_conntrack: Such a connection tracking entry directly has NAT information available, so you don’t have to retrieve them by parsing OpenWrt firewall rules. Furthermore, this means that the forwarding part of the eBPF offloader can run independently of the Linux operating system used. It is only dependent on an OS that runs the Netfilter framework, including nf_conntrack.

Packet Forwarding

The following simplified activity diagram illustrates how incoming packets are forwarded by the current implementation of the offloader:

Figure 1: eBPF Packet Forwarding

Here is a step-by-step explanation of what is happening:

  1. The eBPF program receives a data packet from the NIC for a not-yet-seen flow. It creates the packet tuple key and uses it to check whether an entry for that flow already exists inside the eBPF hash map. Since it hasn’t seen the flow yet, there is no entry, so the eBPF program creates a new empty entry inside that map to signal the user-space program. Meanwhile, it passes all the following packets of that flow to the network stack until the user-space program responds.
  2. When the user-space program wakes up, it retrieves the new flow entry from the map and checks through libnetfilter_conntrack whether a conntrack entry for the flow exists. If not, or the TCP state isn’t established, it doesn’t respond to the eBPF program (yet), so packets continue passing to the network stack. If there is an (established) conntrack entry, it also looks up inside that entry if NAT needs to be applied and, if so, calculates the checksum difference. Finally, it updates the flow entry accordingly to signal the eBPF program that it can take over now.
  3. When the eBPF program receives a new data packet for that flow again, it reads from the flow entry that it can forward the packet now, so it does possible NAT and checksum adjustments and redirects the packet to the target network interface. When there is a TCP FIN or RST or a conntrack timeout occurs, the eBPF program doesn’t forward the packet anymore and passes it to the network stack again.

Where to attach? Where to send?

There are two things I didn’t mention yet about the implementation:

  1. On which network interfaces should I attach my eBPF program?
  2. What is the next hop for the packet, i.e., to which output interface and neighbor to send it?

I implemented the latter within the user-space program using the Linux routing socket RTNETLINK. When I started to implement this, I performed the following three steps to determine the next hop:

  1. Send an RTM_GETROUTE message containing the packet tuple to determine the route type and output interface. I only offload unicast flows.
  2. Send an RTM_GETLINK message containing the output interface to determine the source MAC address.
  3. Send an RTM_GETNEIGH message containing the output interface and the destination IP to determine the destination MAC address.

Finally, the user-space program stores the output interface, source, and destination MAC address inside the flow entry. The eBPF program then rewrites the MAC header and redirects the packet to the output interface. But I wasn’t satisfied with that approach yet; I will explain the reason based on the following picture:

Figure 2: Example network interfaces on an OpenWrt device

The picture shows the network interfaces of my AVM FritzBox 7530 running OpenWrt. As you can see, all four LAN ports of my private network and my WiFi are bridged (which is typical, I think, and generally default for an OpenWrt installation). My dsl0 WAN port has a Point-to-Point Protocol over Ethernet (PPPoE) interface on top to establish a VDSL connection to my ISP, which additionally requires tagged VLAN packets (dsl0.7).

When no offloading is happening and, for example, my Notebook connected to phy1-ap0 would send traffic to the internet, the packets would travel through all shown interfaces except the LAN ports. (Figure 3). Regarding the eBPF offloader, the simple way would be to attach the eBPF program to the br-lan and pppoe-wan interfaces because I wouldn’t have to parse any additional L2 headers. The same goes when making routing decision(s) since you won’t have to query more interface information or push L2 headers. But the eBPF fast path would be minimal in that case. (Figure 4)

I thought this was not an acceptable solution for this project because the idea is to intercept an incoming packet as soon as possible. At the same time, the offloader should also send out packets at the lowest possible network interface. Therefore, the user-space program currently attaches the eBPF program to the lowest possible network interface and, while making the routing decision, also tries to resolve to the lowest possible network interface (Figure 5).

Figure 3, 4, and 5: Packet traversal for different offloading variants

The following flowchart shows how the user-space program currently does the next-hop determination:

Figure 6: Next Hop determination via Netlink

The eBPF program can currently parse the following headers of the respective layers. If it receives any packet containing a L2, L3, or L4 header not mentioned here, it passes the packet to the network stack.

  • L2: VLAN (currently only one) and PPPoE
  • L3: IPv4 and IPv6
  • L4: TCP and UDP

DSA: Going one step further down

As you might have seen in the flowchart of Figure 6, the user-space program also parses DSA interfaces, which stands for Distributed Switch Architecture. Routers typically contain an Ethernet Switch for their LAN ports, which has a management port connected to an Ethernet controller capable of receiving Ethernet frames from the switch. While Linux creates a network interface for that Ethernet controller, you can observe that the DSA driver also creates network interfaces (DSA ports) for the front panel ports.

Ideally, when the switch and management interface exchange packets, they tag the packets with a switch resp. DSA tag, which contains the front panel port ID. When the management interface receives a packet from the switch, it can determine from the tag from which front panel port the packet comes and pass it to the appropriate DSA port/interface. When the switch receives a packet from the management interface, it can figure out from the tag to which front panel port it must send the packet.

Let’s consider the following picture, which shows how OpenWrt on default settings uses DSA on a Banana Pi BPI-R64. The DSA switch resp. conduit is eth0 and lan1, lan2, lan3, lan4, and wan are the DSA ports resp. users.

Figure 7: Example network interfaces on an OpenWrt device using a DSA driver

Without offloading, a network packet sent from the private LAN to WAN would go through eth0, lan*, br-lan, wan, and eth0 again (Figure 8). When using the eBPF offloader without attaching to the DSA switch eth0, it is possible to avoid the bridge br-lan (Figure 9). But if you now attach the eBPF program to the DSA switch eth0, it can read and write the DSA tags of packets on itself, and the user-space program can then figure out which front panel received the package and to which one to send a packet. So when the eBPF program receives a packet on eth0, it can send it out of eth0 again without any intermediate interface (Figure 10).

Figure 8, 9, and 10: Packet traversal through a DSA switch for different offloading variants

Although this has the disadvantage that an eBPF program isn’t “generic” anymore because you need to compile it for the DSA driver used by the target device, it has the potential to further increase the forwarding performance.

Work to do in the upcoming weeks

There are a few problems I have encountered, resp., thought of:

  • I am unsure if nf_conntrack is sufficient for connection tracking because it isn’t possible to query conntrack entries based on the interface that received the packet. I think this can lead to collisions when different interfaces receive identical L3 and L4 flows.
  • Unfortunately, it is currently impossible to update the nf_conntrack packet and byte counters. This might be patchable in the Linux kernel, but my current workaround is to turn off the counters because I think it is better to have no counters than wrong counters.
  • I have shown that I retrieve PPPoE information in user space. The problem is that you cannot do that directly via Netlink since the interface attributes don’t provide PPPoE information. This is why I currently retrieve the interface’s link-local peer IPv6 address, convert it to a MAC address, and try to find that MAC inside the file “/proc/net/pppoe”, which is populated by the ppp daemon. I am anything but satisfied with that, but I haven’t found a better way yet.

Next to trying to solve those problems, the next milestone is to implement an eBPF package dropper into the offloader because, for now, it only forwards packets on its own. And then to finally make a performance evaluation of the implementation.

If you have questions, as always, feel free to ask them, and thank you for reading my Midterm update!

GSoC 2024: New release for Project Libremesh Pirania – Part II

Hello! This post is about my progress so far while working on the new release of Pirania package for the new version of LibreMesh 2024.1 which runs on top of OpenWrt 23.5.3.

During last month there was a lot of interaction with the community via mailing lists and Matrix chat room.

Goals of this project

Pirania is a captive portal designed for community networks. It allows community members to create vouchers (or tickets ) in order to manage access to the internet. When a device access the network for the first time it redirects for the captive portal. Then, it’s needed to insert the voucher previously create by a community operator.

This promotes the sustainability of the network, since there’s costs involved in maintaining one.

What needs to be done

In version 22.03 of OpenWrt the new framework for packet processing and firewall was change from iptables (firewall3) to nftables (firewall4). Since Pirania captive portal uses iptables rules to redirect and allow/deny traffic from clients, there is a need to also update the rules that are created by captive-portal script.

First try

Here i will discuss what worked and what’s not.

Since i have a compatible router with Lime old version 2020.4, a TP-Link Archer c50 v1, i wanted to flash it and see Pirania functionalities in practice. Downloaded a pre-compiled firmware and flashed. It worked and the next step was to install Pirania and start it.

I got some errors (in feeds, while running “opkg update”, more specifically) while installing Pirania which i reported in Matrix chat. Community members helped me and confirmed that this error was not present in recent versions.

Error:

Collected errors:
opkg_download: Failed to download http://downloads.openwrt.org/releases/19.07.10/packages/mipsel_24kc/libremesh/Packages.gz, wget returned 8.
opkg_download: Failed to download http://downloads.openwrt.org/releases/19.07.10/packages/mipsel_24kc/profiles/Packages.gz, wget returned 8.

If you run into error during update and install process of Pirania, do the following:

“it should be enough to delete the libremesh and profiles rows in /etc/opkg/distfeeds.conf as the correct info should be already present in /etc/opkg/limefeeds.conf”

After changing this files, i was able to install Pirania package. But, forgot to install ip6tables-mod-nat and ipset, then my router entered in a weird state. Moving on..

Second try

One of the last GSoC there was a project that aim on easing the virtualization of LibreMesh. Available here. But since the contributor has not changed the requested modifications, it is still open the issue.

I was able to virtualize both Lime 2020.1 and 2024.1 versions. I used the scripts available in lime-packages/tools in order to emulate with Qemu software. Unfortunately wasn’t able to provide internet access to the node itself.

Third try

I had a Rocket M5 MX standing idle and decided to flash with latest version of LibreMesh on it. The installation was easy and is working fine. Just had to add the following line to /etc/config/lime-node in order to get a valid IP from my local network since it only have one physical interface, in order to install ipset package.              

config lime network
config net portwan                                       
      option linux_name ‘eth0’                      
      list protocols ‘wan’   

Then, i was able to install the dependencies necessary to test my code.

Workflow

It’s really easy to test new software in Libremesh, since are usually scripts that need to be modified and can be run at run time. Just modify and upload the script to the working node and you are ready to go.

Code so far

I’m currently working on this branch, which link is below:

https://github.com/henmohr/lime-packages/blob/mohr-patch-nftables-1/packages/pirania/files/usr/bin/captive-portal

Next steps

The next step is to upload this script to a running node and see what happens.

There is a need to add more comments on the code and also with nftables is possible to enable remote logging of each rule that is executed, so will help a lot on debugging this script.

Also, i managed to setup a working node using VirtualBox. Maybe an alternative would be to create a VM with some Linux distribution and then connect it to the LibreMesh node, easing the process of testing.

GSoC 2024: Visualise community data, update 1

I’ve set up a lot of services since planning out the data pipeline in June; the new infrastructure diagram looks like this:

So, I wrote a little Rust utility called json_adder which takes the JSON files and reads the data into a MongoDB collection. We also have a GraphQL server, running through Node.js, which can handle some (very) simple queries and return data. Many of these services are individually easy to set up, the tricky part is making sure everything works together correctly. This is what your favourite technical consultancies call “fullstack development”.

Data loading

The first change from the initial plan was to use MongoDB instead of MySQL as a database. MongoDB has a built-in feature for handling time-series data, which is what we’re working with. It integrates well enough with GraphQL, and since one of my mentors (Andi Bräu) works with MongoDB on a daily basis, there’s a lot of experience to draw upon.

Here is the Rust code for the utility which adds the data to the database, and here’s what it does:

  • Read over each JSON file in the directory in a loop.
  • Extract the last-modified time from each community in the file and do some conversion to BSON.
  • Read the data into a struct, and add each object to a vector.
  • Insert the vector into the MongoDB collection.
  • GOTO next file.

When setting up the connection to MongoDB, the setup_db module will also check to create the collection if it doesn’t exist. This extra bit of logic is ready for when this has to be run in an automated process, which might involve setting up the database and filling it with data in one go.

I don’t have a binary to link here, and will write build instructions in future.1 On each commit which changes Rust code, Github Actions runs cargo check on the repository. When it comes to deploying this in reality, we can change that workflow to provide release builds.

At the moment it is a one-way process. You point json_adder at the data directory and it adds everything to the database. For future work, I’ll need a way to keep the database in sync, only add the files which have been modified, run it on a schedule. For now, it works fine.

GraphQL

Here is the GraphQL server code. Fetch the dependencies, in this order:

npm install express express-graphql graphql mongodb

The ordering of these is important.

Then, run npm start, and open http://localhost:4000/api in your browser, you should see the GraphQL IDE.

At the moment, the GraphQL can handle a query with an argument for metadata. I’ll build out the schema, and the resolver, to handle more arguments / more complicated queries. A lot of the documentation for GraphQL assumes a fairly simple schema, built for handling calls to a web app, which is slightly different from our case. The JSON Schema for the Freifunk API is not only quite long, it is also versioned, and newer versions are not necessarily backwards-compatible. I am going to sidestep this complexity by writing a GraphQL schema which only includes fields available in all versions. My first working example to try is a query which counts the number of nodes per community.

You’ll notice that the last step in in the pipeline is yet to be completed, I don’t have a visualisation to show. But, now that we can reliably pull data all the way through the pipeline, we’re almost ready to make some graphs.

  1. In the meantime, you only need to run cargo build in the json_adder directory. ↩︎

GSoC 2024: LibreMesh Cable Purpose Autodetection Update

Hello everyone! This blog post is meant as a mid-point update on the Cable Purpose Autodetection project.

This first part of the project was the occasion for a lot of discussions on how the project will take place to occur, and discuss how it will materialize for a LibreMesh user. These discussions mostly happened in the LibreMesh mailing list, and in the public matrix channel.

What is the goal of the project again?

In some scenarios, routers running LibreMesh benefit greatly from having specific configurations applied, which are not yet natively integrated — As of now, an expert needs to step in and setup these configurations manually, which is not always an option for communities. For this project, I am investigating scenarios that would benefit from automatically applied configurations.

What was discussed

There was a lot of discussions in the LibreMesh mailing list and in private messages, talking about the implementation and the scope of the project

Here is a sample of configurations where it would be useful to have such a system:

  • A LibreMesh router is connected to the internet and is serving internet access to its clients.
  • A LibreMesh router is connected using Ethernet to another LibreMesh router in the same network, to extend coverage.
  • A LibreMesh router is connected to another router not running LibreMesh, but working as an access point (in a point-to-point link) for client connecting to that router, and transferring data to the LibreMesh router.

Multiple solutions where discussed in the mailing list, for example, it would be useful to detect if a neighbor router is running LibreMesh or another firmware. A solution consisting of LibreMesh having its own ipv6 multicast has been discussed. This would allow a LibreMesh-running router to be more aware of what is around itself, and it could also detect if another LibreMesh router is part of the same network or a different one.

Implementation Solutions

One main point of discussion was “how to apply the configurations”. There was a lot of back-and-forth on that subject. The final software would take the shape of multiple scripts, each dedicated to tackle a single configuration setting.

  • The configuration scripts could be ran automatically at runtime. For example, a script could run every time an interface goes up or down. This means that if a link between two routers is broken, and somehow goes on and offline regularly, the configuration setting would be applied repeatedly, and in the process, could break some other part of the configuration. In that situation, potential problems would be very hard to troubleshoot if the user is not warned that this auto-configuration is occurring.
  • The configuration scripts could be ran when the user runs “lime-config”, which is an existing configuration tool for LibreMesh. This would avoid the problem of a script running repeatedly, because the user would stay in control, but it kind of ruins the whole “running configurations automatically” aspect of the project.
  • Another solution would be to implement buttons & switches in the Lime-app (that currently allows you to manage a node and its network), and add a new page or field where a user could choose if a specific configuration should be automatically applied, apply it manually or not at all. The problem is that the name of these settings might be obscure to less tech-savvy users.

So, we have different possibilities, each with their advantages and drawbacks. The solution will be to have some auto-configuration applied in a way, and other auto-configurations applied in another way.

Virtualization and problems encountered

One of my biggest roadblock in starting this project was running virtualization software. A previous Google Summer of Code participant made modifications to the LibreMesh code to allow virtualization using Qemu or Ansible (available here), but this code wasn’t pulled into the LibreMesh repository because it is not fully ready yet. This implementations contains instructions and modifications that allows LibreMesh to virtualize with Qemu and Ansible, and both of those software have different working and breaking points, and I had to experiment a lot to learn how to solve or work around them.
Using Qemu, it is possible to run up to 100 routers that act as nodes in a network. You can access each node using ssh to ping, create interfaces or check connection from one to the other.
Using Ansible, you can use the qemu_cloud_start.yml file to pre-configure and fine-tune the layout of your network before starting the virtualization in batches. (This makes it easier to specify which node should be an edge node, connected to the network, etc…). This .yml file is working as a blueprint for Ansible, and it will build your interfaces and network of nodes from the instructions contained. You can then run the nodes as a single batch, and connect to each of them using ssh.

However, virtualization is limiting when trying to understand a system. Simulating the cables connecting each node of a network is harder to visualize, in lieu of plugging everything by hand, and you often encounter problems that would not happen if you were working on actual hardware.
For example, connecting the virtual machine to the Internet is challenging (and not working 100% of the time), because you need libraries that are not checked from starting the virtual machine software, and the error messages you get from the machine are not clearly related to what the actual problem is.

Alternative: Working on actual hardware

I am currently waiting to receive a few routers that a community member (Aparcar) sent to me. I plan on using these routers to setup a LibreMesh network, and implement the changes I made in a more physical way than my current virtualization setups.
This setup will allow me to bypass some of the problems I am regularly encountering specifically because of the virtualization, at the cost of a slower iteration cycle. I’m looking forward to setting up and tinkering with actual hardware!

Next steps

  • The biggest next step is implementing code that can actually detect the specified configurations. The foundation for this is done, but more work is needed to make code that can fit the use cases.
  • From the mailing list, a lot of different solutions were discussed. We seemingly have more solutions than problems, so the next steps are implementing some of those solutions, seeing how they fit together with the current LibreMesh code, and choosing which are the best candidates to solve the auto-configuration problems.
  • Another big next step is transferring my changes to an actual router running LibreMesh code, to be able to test and code in a real-world setting

Bonus learning material

Q&A video

A big help in this project was a Questions & Answers session we organized with seasoned LibreMesh developer. We recorded the session, thinking that it would be good to keep track of some ‘newbie questions’ and their answers, for future onboarding. I uploaded the video to guifi-net’s peertube, using LibreMesh’s account. Thanks again to everyone who participated!
The video is available here

Vocabulary list of technologies

Another help in my learning process was a vocabulary list of the technologies I learned of, and how they were used within LibreMesh. This list gets extended every time I lean about a new part of a technology.
There was a lot more abbreviations I had to learn than I anticipated, and I’m still encountering more new ones every time!

Closing words

And that’s about it for a mid-project update! Thank you for reading, and don’t hesitate to send me a message if you’d like to know more.

GSoC 2024 Qaul: Qaul BLE module for Linux: Part-II

This post is a continuation of my last one, which discussed the theory behind Qaul BLE support for Linux where I explained the theoretical concepts and jargon and high-level system design for Bluetooth low energy flow using GATT. Please refer to that blog before proceeding.

In this blog, we will explore the implementation and low-level design of the BLE support in greater depth.

Introduction

We will begin with creating a GATT server serving two Bluetooth services “main_service” and “msg_service” with their respective UUIDs and characteristics for our qaul device. Then we will use the adapter to advertise the “main_service_uuid” for another qaul device to connect to the GATT server. Afterward, we will create a GATT client by starting a scanning service to discover nearby advertisements. The last thing to do is to set a few functionalities and callbacks like read and write characteristic data methods and request_read and request_write callbacks for characteristics to set a basic GATT prototype with message transfer support.

GATT Server

If you remember the GATT data hierarchy diagram from the last blog shows the GATT server is made up of services and each service is made up of characteristics and each characteristic of descriptors. Here, we will dive into details of how to implement it using BlueR.

GATT Service: GATT services are a set of similar attributes in one common section of the GATT server. For Qaul GATT Server implementation, we have two services – mainService, and msgService and their UUIDs (99xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx23). Below is a struct for Service where you specify uuid, characteristics and whether the service is primary or secondary.

GATT Characteristics: Characteristics are the containers for user data. For the Qaul Gatt Server, we have one characteristic for each service ➖ mainChar, msgChar, and their UUIDs with respective configurations. You can specify the uuid, descriptors(if any), read permission, and callbacks under the read attribute, and write permissions and conditions under the write attribute.

Now, you can create a new session to get an instance of your Bluetooth adapter and start the service GATT application using the above configurations. Below is a sample example of a GATT server with read characteristics functionality enabled.

Advertisements and Device discovery

We will use the above instance of the adapter to start advertising the main_service_uuid to be discovered by other devices. (Refer to sample code).

We can start scanning for nearby devices advertising the same UUID for the presence of qaul. For this, BlueR gives us adapter.set_discovery_filter(get_filter()).await; a feature for filter-based UUID scanning. and stream the discovered device adapter.discover_devices().await.

Till now we have created a Ble GATT server and client, the next thing we will implement is confirming the discovered devices for a qaul node, maintaining a list of discovered devices to save the throughput of scanning devices again and again, and keeping track of devices out of range.

For Qaul ble architecture, we have stored the qaul_id in the main_service’s characteristics, Now, whenever any device discovers this node, it will connect to it, check for the presence of both services along with their characteristics, and mark that qaul_id as discoverable. If the conditions are satisfied, it adds the node and its properties to a lookup map. To keep track of the device’s availability after discovery, Qaul spawns a new task out_of_range_checker to ping recently discovered devices at regular intervals to confirm their presence.

Any message to be sent can be written into msg_service’s characteristics using CharacteristicWriteMethod::Io and receive any write request using CharacteristicControlEvent::Write(write). The message read and write process is a bit more complex than function calls. We will discuss it in more depth in the next blog.

So, in the current blog, we are able to create a GATT server and a GATT client, advertise our UUIDs, and scan for nearby BLE devices. We also learned to identify the correct device and maintain the lookup table for better connectivity and more stability.

Concluding Thoughts

The first half of the GSoC coding period had more code implementation for ble connectivity. The major part was into integrating the prototype to work with async rust on multiple threads. The next half will be implementing loss-less transmission of data and experimenting with bluer for better stability. If you want to know more about my project, please feel free to reach out. Thanks for reading!

Progress Update on GSoC 2024 Project: Throughput-based Dynamic WiFi Power Control for Real Networks

Hello everyone! With the first evaluation of GSoC 2024 approaching, I am sharing this blog post to give a comprehensive update on the progress of the Throughput-based dynamic Power Control Approach. Please check out my initial blog post for more information in case you need to become more familiar with WiFi power control. 

New Developments

1. Basic Python Package Creation

A basic Python package for the power control algorithm was successfully created. Let’s refer to this package as ‘power_controller’. The power controller includes a main class and exposes two primary functions: ‘configure’ and ‘run’.

The ‘configure’ function is designed to facilitate the setup of the Transmit Power Control (TPC). It allows for manual activation of the Transmit Power Control (TPC) mode. It initializes the modes of allowed power and parses the incoming control parameters. These parsed parameters are then returned to the ‘run’ function to execute the main logic.

The ‘run’ function implements the core logic of the power controller. This function is responsible for dynamically adjusting the transmission power at configurable intervals. It supports various power selections ensuring the power control algorithm can adapt to different network conditions in real time.

2. Integration with Python-WiFi-Manager

The power_controller has been connected with the existing Python-WiFi-Manager package from SupraCoNex. The WiFi-Manager package provides an interface to monitor each transmitted frame and control the transmission rate and power.

We utilize this interface by linking the power_control package and exposing the  ‘configure’ and ‘run’ functions for the WiFi-manager package for further power control. 

3. Development of Power Controller Logic

The ‘run’ command now includes a simple yet effective power controller logic. Key features of this logic are:

a) Configurable Update Interval: A customizable update interval has been added, allowing the system to adjust transmission power at defined intervals.

b) Dynamic Power Selection: At each update interval, a random supported power level is set as the transmission power which helps in dynamically exploring different power settings.

4. Power Setting Modes

Talking about dynamic power selection, various modes of power setting are introduced. These modes define how the transmission power is selected during each update interval:

  1. Highest: Always selects the highest supported power level.
  2. Lowest: Always selects the lowest supported power level.
  3. Random: Selects a random power level from the supported range.
  4. Round Robin: Cycles through the supported power levels in a round-robin manner.
  5. Fixed: Uses a predefined, fixed power level for all intervals.

5. Experimentation and Observation

A settings file was created to run the power controller with different power modes using the Experimentation Python package from SupraCoNex. The observations from the trace files were promising

  • Power Control Verification: It was confirmed that the power levels were being dynamically controlled as per the logic defined.
  • Rate Adaptation: The packet transmission rate was observed to change and adapt in response to the varying power levels, demonstrating the algorithm’s effectiveness in real-time conditions.

Next Steps

The next phase of the project will focus on advancement of the power_controller as well as data analysis and visualization to gain deeper insights into the performance of the developed power control algorithm. We will also advance the power_controller package.

Here is the outline:

1. Advancement of Power Controller

The major step would be extracting real-time throughput information for the power controller. Another step will be to implement the strategy to lower transmit power when the transmission is optimal. If in case transmission fails in this low power transmission, we stratetigize a method to fallback to higher transmission again.

2. Data Extraction and Plotting

  • Data Collection: Write a Python script to extract data on rate-time and power-time relationships from the trace files generated during the experimentation phase.
  • Visualization Setup: Utilization of visualization libraries such as ‘’matplotlib’ or ‘seaborn’ to create plots that can illustrate:
    1. Rate vs Power Selection Over Time: Plot how the selected transmission rate varies with different power levels over time.
    2. Throughput Analysis: Visualize throughput variations based on the chosen power settings and rate adaptations.
    3. Frame Aggregation: Analyze frame aggregation patterns and efficiencies under different power control strategies.

3. Analysis and Insights

Graphical Analysis: Interpret the plotted graphs to derive insights into:

a. Optimal Power Settings: Identify trends where certain power levels consistently lead to higher transmission rates and better throughput.

b. Impact of Rate Adaption: Understand how dynamic rate adjustments influence overall network performance.

c. Efficiency of Frame Aggregation: Evaluate the effectiveness of frame aggregation techniques in optimizing data transmission.

Based on this analysis, we can further iterate the power control algorithm to fine-tune parameters and enhance performance.

Conclusion

With the successful creation of the Python package, integration with SupraCoNex tools, and promising initial experiments, the second half of the coding period will be focused on power control in a real changing environment through data analysis and visualization. Please feel free to reach out to me if you would like to know more about the ongoing project. Thank you for reading. 

GSoC 2024: Visualise community data

Hello everyone, I’m Pierre, I was first introduced to Freifunk when searching for free WiFi in a cafe in Berlin, and I’m inspired by the radical visions of people attaching antennas to apartment buildings. Now I’m here to work on the community data API.

Since 2014, Freifunk has been collecting a snapshot of information about all communities, every hour, in a big directory available here. At the moment the data is being used for a calendar, a news feed,1 and a map of communities, among other services. Maybe you can think of lots of other things which could be done with that data, but first you would have to parse some 80,000+ compressed JSON files.

The goal of this Summer of Code project is to overcome that hurdle up front; make this community data more easily accessible, and uncover some insights about the network along the way. This is a typical situation for large archives, where one of the key challenges is making data readable and useful.

I intend to build a pipeline from the JSON directory through to some visualisations on a web page. One step behind that, I want to expose a GraphQL interface so that anyone else can query the data for their own purposes. The main part of this project revolves around setting up infrastructure to handle each section of this pipeline.

Although this system is conceptually very simple, there are lots of moving parts involved. The JSON files are loaded into a SQL database, the database talks to GraphQL, which itself talks to some browser-based visualisation library.

I’ve tried to make choices which prioritise boring technologies, reduce the fragility of this process, and hopefully make it easy to maintain in future. I will build it from the top down; starting with the database, and finishing with the visualisations themselves.

I’ll be working in this repository. There’s not much to show at the moment, but it should rapidly take shape in the coming weeks. My next post will be about the process of loading these files into a database.

  1. On which this blog appears, hello! 👋 ↩︎

GSoC 2024: New Transmit Power Control Approach for IEEE 802.11ax Wifi chips

1. Introduction

Hi everyone. I am Raul Shahi. I am currently pursuing a Master’s degree in Computer Engineering for IoT Systems from Nordhausen University of Applied Sciences, Nordhausen, Germany. For the past couple of months, I have undertaken the position of Research Assistant at my university, which involves researching and developing open-source software for IEEE 802.11 resource allocation.

For GSoC 2024, I will work on a novel Power Control algorithm to reduce interference and increase aggregate network throughput. This introductory blog post intends to summarize the necessary background to understand the details of the project.

2. Leveraging TPC performance for enhanced WiFi Performance

Regarding WiFi networks, ensuring everything runs smoothly is all about how we manage the available resources. As the concept of WiFi resource allocation has been covered by Prashiddha during his GSoC projects before, I would suggest the readers refer to his blog posts [3][4]. For IEEE 802.11 devices, deep diving into the system internals is required which can be overwhelming for developers as they would have to be familiar with kernel subsystems and Ansi C. This technical barrier makes it tough to adapt and innovate with new algorithms.

On top of that, many of the latest WiFi chips are becoming closed-source which means the open-source community can have a harder time developing new and advanced algorithms. However, chips like Mediatek, still share throughput information which can be utilized for the optimization of WiFi networks.

Active Transmit Power Control (TPC) is an exciting development area. By adjusting the transmit power for different devices, TPC algorithm extensions like Minstrel-Blues which is built on top of the Minstrel rate control algorithm are promising. They can reduce interference, boost overall network throughput, and improve how the network handles multiple devices in crowded spaces.

3. Motivation

To solve the challenges in WiFi resource allocation, the SupraCoNex project introduced the user-space WiFi Parameter Control API and an interface called Python-WiFi-Manger to enable the development of algorithms using Python. Developers can use these tools to create and test their algorithms on real WiFi chips. On top of the API, a remote socket extension has been realized to facilitate the remote management of access points. The API allows users to monitor transmission data and adjust rates and power on access points without technical knowledge of the kernel. 

I propose developing a new transmit power control algorithm using the WiFi-Manager for IEEE 802.11 ac chips, based on throughput estimates. This approach could potentially extend to Mediatek ax chips, paving the way for smarter, more efficient WiFi networks. However, the approach can be easily extended to any chips that expose the estimated throughput information and provide an interface to fine-tune power settings in real-time.

4. Methodology

The WiFi-Manager package in Python allows the user to monitor each transmitted frame while leveraging the options to control the rate and power for each connected access point. The first task will be to create a new Python package for the power controller and integrate it with WiFi-Manager. Since WiFi-Manager does not yet include throughput information, we will either add it to WiFi-Manager for broader use or directly into the power controller.

The power controller will track throughput at different power levels and average these observations using techniques like the Butterworth filter on EWMA. This can be the basis for determining the optimal transmission power. If throughput suddenly drops, the controller will have a fallback strategy, such as increasing power aggressively to restore the link quickly.

Like the Minstrel-HT [1] and Minstrel-Blues [2] algorithms, our power controller will have update and sample intervals. At the end of each update interval, it will adjust the power level based on recent observations. During sample intervals, it will explore different power levels to find the best settings.

The existing solutions from the Experimentation Python package from SupraConeX can be used to compare the proposed approach, evaluating performance based on rate selection, aggregation, throughput, and power levels.

5. Deliverables of The Project

The end goals of my GSoC 2024 project are as follows:

a. Implementation of a dynamic power control algorithm to adjust power levels
based on throughput information with the ready-to-run demo.


b. A detailed report documenting the research methodology, experimental setup,
results, analysis, and conclusions drawn from the study.


c. A study conducted using the Experimentation Python package that compares
the performance of the proposed approach with the existing solutions on
productive Freifunk Mesh and Access deployments.

6. What has been done until now?

a. Developed a foundational understanding of the importance of resource allocation in wireless transmission.

b. Familiarization with the ‘WiFi-Manager’ package as well as other packages from ‘SupraConeX’.

c. Remote access point setup for monitoring of data transmission and power, rate control.

7. What’s next?

a. Create a Python package for the power controller.

b. Preliminary performance comparison and evaluation of the developed algorithm.

8. Conclusion

I am incredibly grateful to be part of GSoC 2024 as we embark on this exciting journey. Developing and implementing a new power control algorithm with WiFi-Manager is just the beginning, and I am thrilled to contribute to innovations that could significantly improve WiFi performance. This project promises to push the boundaries of what’s possible in wireless communication, and I look forward to sharing our progress and successes with you. 

Please feel free to reach out and connect with me.

References

[1] Minstrel-HT Source Code in the Linux Kernel 

[2] A Measurement-Based Joint Power and Rate Controller for IEEE 802.11 Networks

[3] Minstrel TX Rate Control in User Space, Prashiddha, GSoC ‘22

[4] GSoC ‘22 and ‘23 blog posts from Prashiddha

GSoC 2024: Development of a Modular Assistant for OpenWrt

Hi everyone, I am Gabriel, and I am currently studying Web Application Development and Mobile Application Development. This summer, I will be participating in Google Summer of Code 2024 to work on developing a Modular Assistant for OpenWrt.

Abstract

The goal of this project is to develop a modular assistant for OpenWrt that simplifies the initial configuration of OpenWrt devices. This assistant will enable users to configure their devices quickly and easily, even without deep knowledge of wireless routers. The assistant will be implemented using LuCI, a framework for creating web interfaces for configuration and monitoring.

Plan Of Action

This project will focus on creating an assistant that guides users through the initial configuration of OpenWrt devices. The assistant will have a modular design, allowing it to be adapted to different scenarios and specific needs of communities like Freifunk.

Implementation

  1. Analysis of LuCI’s Client-Side API:
    • Understanding the operation of LuCI’s client-side API will be fundamental for the development of the assistant.
  2. Development of rpcd Services:
    • Learn how to write rpcd services for communication between the user interface and the backend of the OpenWrt system.
  3. Configuration of Permissions and Menu Entries:
    • Configure permissions and menu entries to ensure an intuitive user interface that is easy to navigate.
  4. Adaptable Modular Implementation:
    • Develop the assistant in a modular manner, allowing it to be adapted to different scenarios through scripts.

Deliverables

  • Creation of a stable, working modular assistant for OpenWrt.
  • Detailed documentation explaining the implementation and use of the assistant.
  • Ensure the assistant is adaptable for different scenarios and needs of the Freifunk community.

Concluding

I am delighted to be part of the GSoC 2024 program and contribute to the Freifunk project. I’m looking forward to collaborating with the Freifunk team to create a valuable tool for the community.

I would like to thank my mentors, Martin and Tobias, for their guidance and support. I hope to have a productive and successful summer working on this project.