What to Do If Your OpenWrt Firewall Custom Rules Don’t Work
Sometimes you have to dig deep to get what you need. See how this developer went into the code to activate his custom rules.
Join the DZone community and get the full member experience.Join For Free
In the last article, I explained how to flush a brand new OpenWrt firmware on a router. However, the prebuild images provided by OpenWrt by default do not have a lot of must-have tools for network administration. Luckily, you can install them easily with the opkg package manager. For example, installing the controlling graphical web-interface is as simple as the 2 commands:
$ opkg update # This will update information about the actual available packages $ opkg install luci
And immediately after that you can access it with your browser! It's obviously called "LuCI" and makes it really easy to make some basic setup of the device. You can configure your wired and wireless networks, DHCP and DNS servers, network static routes, hostnames, etc. Also, it's extendable to configure non-standard services, like ddns, transmission, adblock etc. They are installed with packages named starting with "luci-app-", for example:
$ opkg install luci-app-ddns
The same way you can install a lot of other tools, there are about 4000 packages available in OpenWrt repositories, so there is a very good chance you'll find everything you might ever need.
Well, for my project I had to allow IGMP protocol traffic from specific network card and allow access from some known ports, so I created some rules for the firewall just like that:
Easy, isn't it? Just like with any other router web-UI. One small note is that it did not have predefined "IGMP" value for the protocol, so I had to add a custom one, pretty obviously called "IGMP." Can anyone see a problem there? It was saved, no any error messages shown, what could possibly go wrong? It seemed just fine...but it did not work. Even worse, all my other custom rules did not work as well! The predefined ones were just fine, I could easily turn them on and off! No matter which or how many of them are switched on or off, the result was the same: no custom rule was applied, even the simplest one! Still, there were no any error messages on the UI. At that moment, I was thinking that either the world had gone crazy or I was missing something really obvious. Trying to convince myself it was the second case, I opened that lovely command-prompt again. After all, it's still the best practice to admin the network devices in the text mode even nowadays. The quick research unfolded 2 important things:
1. Restarting the firewall service spawned some warnings...
though they were warnings, not errors! Also, even though for some reason the "IGMP" protocol was not recognized, what on earth could be wrong with the destination port "22" value?
2. The firewall service in OpenWrt was implemented with the tool "fw3".
So I decided to find out what was wrong with that tool and why it was ignoring all my custom rules. The invalid port value problem was the most surprising one. The code was very nice and readable, and the naming was absolutely understandable, so it was really not hard to find out the configuration file was parsed with functions from file options.c, and the port parsing was implemented with the function fw3_parse_port. A closer look on the code revealed this place:
426 n = strtoul(val, &p, 10); 427 428 if (errno == ERANGE || errno == EINVAL) 429 return false;
Obviously, they were parsing the string containing port into unsigned long (which looks to be a bit of overkill, by the way, but should not cause a problem here) and checking if there was an error afterwards by only looking on the global errno value. The problem here was that they were skipping a legal rule due to errors in previous rules' errors, as errno was NOT set back to zero "no error" state anywhere in the code, as well as system calls like strtoul() didn’t do it either - that's clearly stated in the manuals: http://man7.org/linux/man-pages/man3/errno.3.html
The value of errno is never set to zero by any system call or library function.
So, a quick fix for this bug was to set errno to zero before parsing next rule. That did work for me.
Then I decided to find out what was wrong with the protocol name. I guessed it was the same file, options.c and quickly found the method fw3_parse_protocol().
If the protocol name is not one of the predefined ones, the netdb method
getprotobyname() is called to get this protocol entity. This method only compares a given string to the first column in the /etc/protocols file, which is always spelled in lowercase... Hmm... on the web interface they have all protocols names in uppercase... To verify my theory, I changed the protocol name in my rule to lowercased "igmp". The warning was gone and all my custom rules were finally working.
After all, I'm still a huge fan of the OpenWrt project and it's really flexible to serve any crazy thing that you’d want to do with your router, but if your configuration is more complex than the one of any other router - be ready to dive into the Linux shell or even the code.
Published at DZone with permission of Andrey Bondarenko. See the original article here.
Opinions expressed by DZone contributors are their own.