esp-idf icon indicating copy to clipboard operation
esp-idf copied to clipboard

feat(esp_netfi): Add DHCPS option for captive portal identification (IDFGH-11885)

Open jkingsman opened this issue 1 year ago • 15 comments

RFC 8910 provides a DHCP option to notify clients of a captive portal to be visited prior to using the network.

This is supported by vendors (see Apple since iOS 14 and macOS Big Sur, Android since Android 11), and provides a more standards-compliant mechanism than DNS-based redirection.

This is a simple addition and will be useful in scenarios where the ESP32 broadcasts a network and needs to direct clients to a portal or served content in a modern, standards-compliant way.

This would also address #2723.

jkingsman avatar Jan 13 '24 20:01 jkingsman

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar Jan 13 '24 20:01 CLAassistant

Messages
:book: 🎉 Good Job! All checks are passing!

👋 Hello jkingsman, we appreciate your contribution to this project!


📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more.

🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project.

Click to see more instructions ...


This automated output is generated by the PR linter DangerJS, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.

DangerJS is triggered with each push event to a Pull Request and modify the contents of this comment.

Please consider the following:
- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.
- Danger is not a substitute for human code reviews; it's still important to request a code review from your colleagues.
- To manually retry these Danger checks, please navigate to the Actions tab and re-run last Danger workflow.

Review and merge process you can expect ...


We do welcome contributions in the form of bug reports, feature requests and pull requests via this public GitHub repository.

This GitHub project is public mirror of our internal git repository

1. An internal issue has been created for the PR, we assign it to the relevant engineer.
2. They review the PR and either approve it or ask you for changes or clarifications.
3. Once the GitHub PR is approved, we synchronize it into our internal git repository.
4. In the internal git repository we do the final review, collect approvals from core owners and make sure all the automated tests are passing.
- At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
5. If the change is approved and passes the tests it is merged into the default branch.
5. On next sync from the internal git repository merged change will appear in this public GitHub repository.

Generated by :no_entry_sign: dangerJS against 593194d6228e7aac6e16577f8dc7dff0978877f5

github-actions[bot] avatar Jan 13 '24 20:01 github-actions[bot]

Looks like a nice, additive, patch. I like it!

mickeyl avatar Mar 10 '24 22:03 mickeyl

Hi @jkingsman

Thanks for the PR, but is it really all that's needed for this option to work? Haven't you forgotten to add other files to your commit?

I mean, it could work from the dhcpserver API using the recently introduced hooks, but since you added the option to esp_netif layer, we probably need some more. I would be also great to update the captive portal example.

david-cermak avatar Mar 18 '24 08:03 david-cermak

Hm, adding to the enum so that you can serve that DHCP option seems all that's necessary to me -- it's up to a user to actually write the captive portal, of course; we have no way of knowing what they want for that. Is there something obvious I've missed?

I will do up a demo this evening if I can find some time. I'm happy to overwrite that captive portal example if desired, but it uses DNS redirection which is probably a reasonable demo to keep around -- DHCP opt is the better, more modern way to accomplish a redirect but DNS is still a valid approach. Do you have any preferences on overwrite vs. add (and I a suppose perhaps annotate each example to emphasize the different approaches and when they're appropriate for use)?

Thanks for the tips!

jkingsman avatar Mar 18 '24 15:03 jkingsman

adding to the enum so that you can serve that DHCP option seems all that's necessary to me

This is probably enough for the DHCP client to parse and use it, that is if the ESP32 is connecting to a captive portal. If ESP32 as acting as a captive portal, it needs to add this option to DHCP server's messages. Our DHCP server is very simple, it just uses these few (hardcoded) options:

https://github.com/espressif/esp-idf/blob/6b9c2fca79a7b77a42242d48b0cfa82d612e9b85/components/lwip/apps/dhcpserver/dhcpserver.c#L407-L456

It's easy to append more options using the LWIP_HOOK_DHCPS_POST_APPEND_OPTS() macro (as I mentioned ^^):

https://github.com/espressif/esp-idf/blob/6b9c2fca79a7b77a42242d48b0cfa82d612e9b85/components/lwip/apps/dhcpserver/dhcpserver.c#L609

But that's again, just the lower layer. If you're adding this enum to esp_netif you'd like to make the netif API aware of this and allow users to configure this option.

Do you have any preferences on overwrite vs. add

It would be great if you use some local Kconfig.projbuld configuration to enable/disable this method in the example (perhaps you can also make the current DNS method configurable)

david-cermak avatar Mar 18 '24 16:03 david-cermak

Ah, that's helpful context; thank you. Seems like I misunderstood how the DHCP system fit together in that regard -- I did indeed intend this as an offer value and not a client value.

I'll look into the funcs you linked and get my head around the least invasive way to do this.

jkingsman avatar Mar 18 '24 16:03 jkingsman

I've got a draft but have just moved states so am waiting for my collection of devices to arrive with our moving van in a few days or so for real testing + sniffing the offer packet for validation. Thanks for your patience!

jkingsman avatar Apr 06 '24 21:04 jkingsman

Alrighty, sorry for the force push noise but I've got this working now. Thank you again for your patience and such a complete explanation to remedy my super surface level understanding -- I've deepened that a bit now, haha.

I've also got a demo "done" in that it's code complete but I still want to make it its own little demo folder with a README etc. so I will have that ready for you tomorrow evening.

The gist is

index 4eba17b50b..4d14061864 100644
--- a/examples/wifi/getting_started/softAP/main/softap_example_main.c
+++ b/examples/wifi/getting_started/softAP/main/softap_example_main.c
@@ -85,6 +85,14 @@ void wifi_init_softap(void)
     ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
     ESP_ERROR_CHECK(esp_wifi_start());

+    esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
+
+    char* captive="http://captiveportal.local";
+
+    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(netif));
+    ESP_ERROR_CHECK(esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_CAPTIVEPORTAL_URI, captive, strlen(captive)));
+    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(netif));
+
     ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
              EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
 }

which updates the DHCP OFFER with the option:

image

Also wanted to add that I haven't written any C since college and had to muddle through with some help from my partner who is an embedded dev, so if I've made any boneheaded decisions or used any blindingly bad antipatterns, please call me out -- I won't be offended 🙂

jkingsman avatar Apr 08 '24 07:04 jkingsman

Alrighty @david-cermak I think this is ready to go. I've validated this with an example, including tcpdump output of the result as a demo in the example file, and tested everything on real hardware.

Per the contributing docs, I also did an astyle run which moved some bits and bobs around in the files I worked on, but all seem like good changes.

Please let me know your feedback and if you want any changes. Thank you again for your patience!

jkingsman avatar Apr 09 '24 02:04 jkingsman

Thanks for the update @jkingsman ! The changes look very good to me. The only thing is the allocation in the netif layers. Could we let users "own" that buffer, so we just copy the pointer? This would usually be a const char* to the program memory, so no allocation would be needed in most cases.

david-cermak avatar Apr 09 '24 04:04 david-cermak

Sounds good! I've moved that malloc out.

jkingsman avatar Apr 09 '24 05:04 jkingsman

Woohoo! Glad to hear it's made it to the next round.

I've squashed down my commits to one, and I'll sit tight. If you have a rough order of magnitude, please, is 'some time' for review usually weeks? Months? No worries no matter what it is; just curious.

Thanks again!

jkingsman avatar Apr 12 '24 06:04 jkingsman

is 'some time' for review usually weeks? Months?

:-) I'd say ~ weeks (1 month on average, but the standard deviation is 'big')

feat(esp_netfi): add support for captive portal DHCP Option 114

just a typo with your last commit message -> feat(esp_netif)

david-cermak avatar Apr 12 '24 06:04 david-cermak

Whoops, fixed.

Thanks for the info! I will wait to hear more from you when things progress.

jkingsman avatar Apr 12 '24 07:04 jkingsman

It's been merged and published as https://github.com/espressif/esp-idf/commit/3035ce294d94cec9d801ab72866211cb534dac43 (had to rebase to pass the internal tests, so it didn't close automatically)

david-cermak avatar Jun 07 '24 09:06 david-cermak

Eyy awesome; thank you!!

jkingsman avatar Jun 08 '24 20:06 jkingsman

@david-cermak Hi. Is this on the roadmap? I just upgraded to v5.3 and it's not merged yet. Thanks

mksafavi avatar Aug 26 '24 08:08 mksafavi