I’ve been working closely with LwIP for over 10 years now. I’ve shipped a number of products based on it, and become intimately familiar with its workings. Based on my experience I can definitely say that LwIP is A MESS. However, in the right hands, it is a fairly capable mess.
As an example, one of the products I shipped was a consumer sensor device based on a (now very old) SiLabs 802.15.4 radio MCU. This part had 64K RAM and 512K flash. On this we ran LwIP over a Thread network stack (IPv6 only) serving a fairly sophisticated application with time-critical sensing, status reporting and UI behaviors. At peak activity, the system was capable of carrying on 3 simultaneous network conversations (one each with a cloud service, a local coordinator device, and the end-user’s mobile device) using 12 network buffers totaling 7.5K in size. All interactions were encrypted and secured end-to-end with mutual certificate-based authentication, and the system ran on a modest-sized battery with a minimum 2 years lifetime.
My point is that you can use LwIP to make fairly sophisticated products that run with very modest resources. But it isn’t easy.
As has been stated, LwIP’s biggest problem is its near complete lack of useful documentation. Really, the only way to truly understand LwIP’s API is to know the associated networking standards really well, at which point you can look at the code and realize how the designer intended it to be used.
I’ve long considered writing some tutorial documentation for LwIP based on my experiences. Trouble is, when you go to document something as complicated as a network stack, irregularities in the design make the process super hard.
This is the other big problem with LwIP—in many areas its design is inconsistent, under-specified and needlessly opaque. For example, trying to understand which functions can be called in what threading context, or what the consequences are of using one type of pbuf over another can be maddening. There are just too many options, and many are poorly named (so, there’s the "raw" API, and then there’s the API for sending raw packets?).
At this point, LwIP is in need of a thorough (API-breaking) clean-up. There’s a lot of useful code there, but it’s just not setup for success.