martin-ger / esp_wifi_repeater

A full functional WiFi Repeater (correctly: a WiFi NAT Router)
MIT License
4.78k stars 903 forks source link

L2 bridge without NAT #333

Open tsipa opened 5 years ago

tsipa commented 5 years ago

I don't want to deal with NAT and all this pain with port forwarding within my network. I did just a bit of research and it looks like it's close to impossible to get pure L2 bridge between soft-ap and station, is there any particular reason for this?

martin-ger commented 5 years ago

I started with experiments to build a L2 bridge. I didn't get it to work, but this was about 3 years ago. Maybe it is a driver issue (closed src), than it is obvioulsy hopeless. Maybe it is simply because I didn't understand the mechanisms, especially ARP handling in lwip, completly. I wasn't able to send a packet with another MAC as src than the original MAC of the ESP. And as far as I understood, this has to be done...

Do you have a spec, what exactly a WiFi L2 bridge has to do, when relaying frames? Especially, I would be interested in address and ARP handling.

tsipa commented 5 years ago

Accoding to this http://www.nongnu.org/lwip/2_1_x/group__bridgeif.html#ga3d41c7905b61aef3a3b8c61b3af7879f for the very first glance it looks like br = bridgeif_init() bridgeif_add_port(br, netif_soft_ap) bridgeif_add_port(br, netif_sta)

tsipa commented 5 years ago

I'll try to play with this around today evening.

tsipa commented 5 years ago

I mean if this implemented and will work - there is no point to implement bridge in code of this project, right? In general it's relatively simple and looks like this https://gitlab.indel.ch/thirdparty/lwip/commit/51a07661ccdee6ec3f943c96c01272e6225daf0b#1640840ca0dfbe7a5cde3ad750975942ac735606_0_334 coed is doing the right thing.

martin-ger commented 5 years ago

It shoud be implemented as a part of the lwip-lib. Best perhaps with the latest lwip2 for Arduino. https://github.com/esp8266/Arduino/tree/master/tools/sdk/lwip2

Or directly for the esp32? https://github.com/espressif/esp-idf/tree/master/components/lwip

Once this really works, one can think about an application around it. My experience here is, that while the basic functionality, the API, and a proof of concept is the real exciting thing for a developer, most user are looking for the ready-made binaries.

This code here is also just a giant shell with lots of functionality around the lwip NAT extension https://github.com/martin-ger/esp-open-lwip .

devyte commented 5 years ago

CC @d-a-v for the ESP8266 lwip2 integration.

I'm also looking for a L2 bridge/mesh.

d-a-v commented 5 years ago

These defines are in lwIP-v2 so we have it in esp8266 arduino lwip2 (probably in esp32 too). From what I can see

include/netif/bridgeif.h:err_t bridgeif_init(struct netif *netif);
include/netif/bridgeif.h:err_t bridgeif_add_port(struct netif *bridgeif, struct netif *portif);

If we use such functions, once the AP+STA bridge is setup, we need to forget about AP and STA, and only deal with the new bridge interface (all bridged interface are seen as a single new one - like the openwrt's br-nat interface bridging/replacing ethernet and wlan interfaces). This is doable but that makes sense to me only if there is a third real interface (the "uplink" interface like "wan" on a openwrt router). Indeed:

In both case we'd have two separate wifi interfaces on the same network (same address plan, like two wifi dongles on a PC connected to the same AP).

From what I understand in mesh networks, interfaces are not bridged. To be meaningful on esp8266 (esp32), that would imply a third PPP or ethernet interface. What is your use-case ?

(OTOH I reckon NAT is a missing feature with lwip2, which could be imported from neonat's lwip-v1 patches)

d-a-v commented 5 years ago

I don't want to deal with NAT and all this pain with port forwarding within my network.

IPv6 is your friend.

I did just a bit of research and it looks like it's close to impossible to get pure L2 bridge between soft-ap and station, is there any particular reason for this?

A use case example is welcome.

tsipa commented 5 years ago

the bridge interface could for example be DHCP client, so what would be the use of the AP interface ?

It certainly could be DHCP client, but bridges is on L2 of teh model. But still they could have an L3 address if they want.

In both case we'd have two separate wifi interfaces on the same network (same address plan, like two wifi dongles on a PC connected to the same AP).

Right, it's called bridge. https://en.wikipedia.org/wiki/Bridging_(networking)

IPv6 is your friend.

It is not since i want a single L2 segment.

A use case example is welcome.

The goal of bridge is to:

  1. get frames from interface where it came from
  2. iterate over arp table looking for destination
  3. if destination is found and destination learned not on the same l2 segment - relay this frame to the segment where destination is found if destination is not found relay this frame to all segments except of one where frame came from
tsipa commented 5 years ago

Well, okay, sorry, i sincerely apologies for this stupid sarcasm and bad manners.

My use case is pretty simple. First i want to be to able to manage all my devices from single dhcp server, second i want to have predictable L3 address of my devices and know that my, let's say RPI is always at 192.168.1.111 disregarding if it roamed from one corner of my mansion to other corner of my mansion. And all new devices discovered on my dhcp(as long as they are dhcp clients ofc)

d-a-v commented 5 years ago

Well, okay, sorry, i sincerely apologies for this stupid sarcasm and bad manners.

That's allright, and I really need to understand the use case of two interfaces on the same network (Well I tend to stay stuck to L3). With two interfaces on the same network you'll always have a default route, one interface doing nothing, maybe receiving data so if you have an arpwatch sitting on your network it gets mad (been there several time on my lab network with hundreds of users, sometimes configuring their LAN+WLAN laptop that way).

It is not since i want a single L2 segment.

Reading your second message, you want your RPI to be reachable everywhere (IPv6 could be a solution, mDNS, allthat).

Let's stay with IPv4. You want a constant IP address for your RPI. I don't see how it is possible when an esp or another is between your RPI and your router. Let's not speak of L3. On L2 where we'd have a big mesh network, bridges would locally bring packets from any interface to the top esp network layer. Then what would an esp do with the packet except from resending it to some "peer" esp or router ? Which one ? Would we have to broadcast then ?

Now I can see what is needed. We'd need an intelligent broadcasting (like ethernet switches do, with a mac-table on each port - we are indeed in L2) and a spanning tree algorithm to avoid madness in the network. Unfortunately I don't think we have that in lwIP (haven't checked).

Maybe @devyte has more input with some mesh algorithm he told me about. About that there are hooks in lwip2 that allow to receive custom protocol packets that are not handled by lwIP itself (and to answer back).

tsipa commented 5 years ago

With two interfaces on the same network you'll always have a default route, one interface doing nothing

In terms of L3 you will have one iface - the one returned by bridgeif_init(). Under the hood, on L2, both interfaces would be functional, depeding on DST field of the frame on L2 the switching decision will be made. If destination mac was learned on Soft_AP iface it will relay it to Soft_AP, if it was learned on STA iface frame will be relayed to STA iface. If DST mac was not learned yet they frame must be relayed to all ifaces.

You want a constant IP address for your RPI. I don't see how it is possible when an esp or another is between your RPI and your router

I've started writing about ARP, mac learning and all this stuff, wrote almost one screen and realized there is at least two screens more to cover just basics.

There is awesome POC https://github.com/yarrick/lwip/blob/master/src/netif/bridgeif.c#L326 with mac learning https://github.com/yarrick/lwip/blob/master/src/netif/bridgeif.c#L351 and logic which looks operational for the first glance https://github.com/yarrick/lwip/blob/master/src/netif/bridgeif.c#L356

d-a-v commented 5 years ago

Well it looks like a network switch. Feel free to PR on lwip2 repository if you have patches. We have several good ones, some of them already integrated upstream.

tsipa commented 5 years ago

Well, bridge is effectively a network switch. I'll see, if i can cherry-pick this i'll make a pull request, but, you know, talk and write the code is different, i doubt i'm good enough to backport this.

martin-ger commented 5 years ago

The most prominent use case is obviously a real "WiFi repeater" like the ones you can buy in tons e.g. on ebay. A L2 bridge (= switch when the connected L2 technologies are the same), that extends the range of the uplink cell towards the STAs on the downlink.

DHCP ist not a problem here: all STAs of the repeater's AP use the DHCP server from on the repeater's STA uplink.

You don't need STP, as with WiFi you cannot create loops topologies, it's always a tree (one repeater has one uplink and many downlinks).

This kind of "mesh" (tree-like) can work (as in my Automesh feature) even with swichtes. A real IEEE 802.11s WiFi mesh is impossible, as it would require massive extension in the closed WiFi driver. All "mesh" extensions for the ESP8266/ESP32 use either a WiFi tree with overlay routing or plain L2-frames with promiscuous mode (without WiFi AP/STA, WPA etc. and incompatible to standard WiFi nodes).

devyte commented 5 years ago

You don't need STP, as with WiFi you cannot create loops

True when using only the AP and STA interfaces. However, @d-a-v has a prototype that integrates Ethernet with lwip, in which case loops are possible. There are other possibilities I can think of as well.

What I thought[1] at one point was to have a simplified implementation for wifi-only, and a complete implementation when other interfaces are added.

[1]: I've been toying with the idea of a layer2 mesh like B.A.T.M.A.N. Adv on the ESP8266.

niclasfredrik commented 5 years ago

Hi, I would like to make a low cost wifi repeater for South American users. Please suggest someone that could help me. Thanks Niclas Arnberger

Den fre 19 juli 2019 kl 02:57 skrev Develo notifications@github.com:

You don't need STP, as with WiFi you cannot create loops

True when using only the AP and STA interfaces. However, @d-a-v https://github.com/d-a-v has a prototype that integrates Ethernet with lwip, in which case loops are possible. There are other possibilities I can think of as well.

What I thought[1] at one point was to have a simplified implementation for wifi-only, and a complete implementation when other interfaces are added.

[1]: I've been toying with the idea of a layer2 mesh like B.A.T.M.A.N. Adv on the ESP8266.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/martin-ger/esp_wifi_repeater/issues/333?email_source=notifications&email_token=ALEV5JMHNO2DP7GG7MBBELTQAFXVZA5CNFSM4IEXNNH2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2K4SFQ#issuecomment-513132822, or mute the thread https://github.com/notifications/unsubscribe-auth/ALEV5JP3RJSWEJXHQWLKGTLQAFXVZANCNFSM4IEXNNHQ .

tsipa commented 5 years ago

Well, i've managed to backport it more or less (it's compiles at least) without any significant changes, but after that i've not managed to get how this piece of code is even supposed to work.

https://www.nongnu.org/lwip/2_1_x/group__bridgeif.html#ga23cc2c5f8fccefc470093840cc53727c is it supposed to take netif from netif_add, then it makes global variable which supposed to be provided to netif_add? The fuck what?

martin-ger commented 5 years ago

Funny - I tried the same over the weekend...

I assume, it is initialized like this:

#if BRIDGE_MODE
struct netif bridge_netif;

    //bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA1(2, 1024, 16, ETH_ADDR(0, 1, 2, 3, 4, 5));
    bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA2(2, 1024, 16, 0, 1, 2, 3, 4, 5);
    ip_addr_t bridgeif_ip;
    ip_addr_t bridgeif_netmask;
    ip_addr_t bridgeif_gw;
    netif_add(&bridge_netif, &bridgeif_ip, &bridgeif_netmask, &bridgeif_gw, &mybridge_initdata, bridgeif_init, ethernet_input);
#endif

Of course, we should add some reasonable values for MAC, IP, mask, and gw. Then you have the global bridge_netif and add the AP and STA interface...

It also compiles for me - no idea whether it works...

d-a-v commented 5 years ago

Did you backport this to lwIP-v1.4 ? What did you think of forward-porting NAT to lwIP-v2 instead ? (FWIW lwip2 works with esp-open-sdk)

martin-ger commented 5 years ago

Yes, backported it to "my" version of esp-open-lwip (lwip v1.4) for the esp8266. The reason is, I know it and I understand it. Going forward to lwip 2.x is probebly easier as the brifgeif is part of v2.

I have no source-level experience with lwip v2.x and in the past I had some trouble with it with my uMQTTBroker in Arduino (blocks after 5 TCP connects).

Guess you are talking about this: https://github.com/d-a-v/esp82xx-nonos-linklayer ? There you say: "UDP/TCP codes using lwIP-1.4 need some updates" - what has to be done?

Forward porting the NAT to lwip v2 is shurely easy - a student of mine did it successfully for the esp32: https://github.com/jonask1337/esp-lwip

d-a-v commented 5 years ago

About uMQTTBroker not working with lwip2, the reason is explained: https://github.com/martin-ger/uMQTTBroker/issues/12#issuecomment-504710446 (either way, espconn port would have to be fixed in lwip2, or a rewrite without espconn is needed in uMQTTBroker)

There you say: "UDP/TCP codes using lwIP-1.4 need some updates" - what has to be done?

This doesn't apply to espconn. It's about lwIP API signatures which has changed for some functions between v1 and v2 (see changes following the links)

porting the NAT to lwip v2 is shurely easy - a student of mine did it successfully for the esp32

Well I should also buy time by employing students. That's great, I'm right away going to steal his patches for lwip2 ! (credits will go to owners)

d-a-v commented 5 years ago

@martin-ger @jonask1337 Thanks for porting NeoCat's NAPT patch to lwip-2.0.3. I ported it to lwIP-2.1.2 and it's damn working ! (still wip due to the not very compliant nonos-sdk-lwip2 glue)

@martin-ger While hacking around with lwip2 I realized then while espconn is ported, it is not initialized by default (in Arduino port). Maybe you could have a try by calling espconn_init(). BTW there is also a esp-open-sdk fork using lwip2: https://github.com/someburner/esp-open-sdk (with espconn supposed to work)

@tsipa: sorry to pollute your thread :]