Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom dhcp options #8571

Closed
wants to merge 6 commits into from
Closed

Conversation

bwjohns4
Copy link

@bwjohns4 bwjohns4 commented May 17, 2022

Re-write of the PR #8451 which adds class member functions to add custom DHCP options. There is function called add_dhcps_custom_options() which adds a DHCP option to a char[100] array. Everytime add_dhcps_custom_options() is called, the new option is appended to the char[100] array. There is also a remove_dhcps_custom_options() which clears all the options from the char[100] array.

Actually meant for this to be a DRAFT PR. I left #include "Arduino.h" and Serial.println's in there to assist with debugging, etc.

@bwjohns4 bwjohns4 marked this pull request as draft May 17, 2022 01:33
Copy link
Collaborator

@d-a-v d-a-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
There are numbers of comments, sorry :]

More generally, you made your former #8451 more complex and ram consuming
by storing user data in a temporary place which is always / not dynamically allocated.

This comment I made was a proposal to keep with the initial simplicity of your original pull request, but with adding the same checks that you wrote several times in this pull request.

The benefit of the former one is simplicity and much more reducing ram consumption.

Serial.print("OfferCode: ");
Serial.println(String(offerCode)+offerContent);
int sizeOfCustomOptions = strlen(dhcpCustomOffers);
if (sizeOfCustomOptions + strlen(offerContent) + 1 < 100){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constants are generally forbidden in any code.
In this case 100 must be replaced by sizeof(dhcpCustomOffers).

uint16 DhcpServer::add_dhcps_custom_options(uint8 offerCode, char *offerContent)
{
Serial.print("OfferCode: ");
Serial.println(String(offerCode)+offerContent);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, debug code must be optional with #if DHCPS_DEBUG barriers and be using os_printf(),
like in other places in this file.

{
Serial.print("OfferCode: ");
Serial.println(String(offerCode)+offerContent);
int sizeOfCustomOptions = strlen(dhcpCustomOffers);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is misleading: sizeof generally means a constant which is the size of a type.
This name may be changed to lengthOfCustomOptions.

dhcpCustomOffers[sizeOfCustomOptions +1] = strlen(offerContent);
for(int i = 0; i<(strlen(offerContent)); i++){
dhcpCustomOffers[sizeOfCustomOptions + 2 + i] = offerContent[i];
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may simply want to use strcpy_P(&dhcpCustomOffers[sizeOfCustomOptions + 1], offerContent); ?
(using _P so user input can be in flash with PROGMEM / PSTR)

@@ -1591,3 +1594,51 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip)

return pdhcps_pool->ip.addr;
}

uint16 DhcpServer::add_dhcps_custom_options(uint8 offerCode, char *offerContent)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using int8 or int16 generates more code than int on esp8266.
if 16 is important but not critical, we have int_fast16_t.
https://en.cppreference.com/w/c/types/integer

const char* is always preferred than char*

A comment may mention that the first bytes indicates the option length.

{
for(uint16 i = 0; i < 100; i++){
dhcpCustomOffers[i] = '\0';
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memset(dhcpCustomOffers, 0, sizeof(dhcpCustomOffers)) which is libc-optimized ?

Serial.println(sizeOfCustomOptions);
uint16 i = 0;
while (i < sizeOfCustomOptions){
if((uint16(dhcpCustomOffers[i+1]) +1) < (uint16(312) - uint16(optptr - optionsStart))){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

casting to size_t is more efficient (casting to 16 or 8 bits produces larger code).

Serial.print("OfferCode: ");
Serial.println(String(offerCode)+offerContent);
int sizeOfCustomOptions = strlen(dhcpCustomOffers);
if (sizeOfCustomOptions + strlen(offerContent) + 1 < 100){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strlen(offerContent) seems to be invalid, since this is not a regular C-string.
Should it be offerContent[0] instead ?
(= length of the DHCP option)

Serial.println("DHCP: Made it into IF:");
Serial.println((uint16(312) - uint16(optptr - optionsStart)));
for(int y = 0; y < uint16(dhcpCustomOffers[i+1]) + 2; y++){
*optptr++ = dhcpCustomOffers[i+y];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memcpy(optptr, &dhcpCustomOffers[i+1], dhcpCustomOffers[i+1] + 1) ?

@bwjohns4 bwjohns4 changed the title Custom dhc poptions Custom dhcp options May 17, 2022
@bwjohns4
Copy link
Author

@d-a-v , Thanks so much for the detailed comments! You've got a great point that weak function was much simpler and resulted in less RAM for 99% of cases where this will not be used. My only thought was that it was less clear and inviting to only provide the weak function (users would really have to look closely to see this as an option), where adding the member functions makes it stand out a bit more that this functionality is available. This leads me to two questions:

  1. Should I revert back to the original PR and just go with the improvements suggested to that and keep it simple since most users won't need this.
  2. OR, simplify this newer version and require the memory allocation to be done by the user and pointer passed per your suggestion. I think the tradeoff would be that the functions would always exist in memory in this PR but allow more clear features to users, whereas with the old PR, the additional memory would only be consumed if the weak function was overridden at compile time by certain users (however the features would be less obviously available).

I'm happy to proceed either way!

@bwjohns4
Copy link
Author

@d-a-v , I revised this to simplify and allow just passing a pointer to the user custom options. Your thoughts? Should I continue down this path and potentially improve/optimize or revert back to the weak function option?

@mcspr
Copy link
Collaborator

mcspr commented May 21, 2022

5c: option content is u8, not string or char. yes, we have unsigned chars, but it is a different type from the compiler pov nonetheless. plus, like you said - if you struggle to find api user for you code, just use it internally where the options are applied?

@bwjohns4
Copy link
Author

bwjohns4 commented May 22, 2022

@mcspr From user code, I am able to successfully get this to work by calling dhcpSoftAP.add_dhcps_custom_options("\x72\x0eThis is a test");

Are you saying that I should change void DhcpServer::add_dhcps_custom_options(char *offerContent) to void DhcpServer::add_dhcps_custom_options(uint8_t *offerContent)

@mcspr
Copy link
Collaborator

mcspr commented May 23, 2022

I am suggesting to take a look at existing options prepended inside of the class, and reference RFCs related to options and DHCP itself.
We know what the option is, API might as well have something structured to work with
[code, one byte] [length, one byte] [payload, whatever was in length (limited to 255 overall)]?

Can we write it in a way useful for both? What happens if you get length wrong with the char string encoding? Why not something like vector instead of a fixed-size storage (struct Option { ... }, vector, etc.)?

Also note that any option may appear only once https://datatracker.ietf.org/doc/html/rfc2131#section-4.1

@bwjohns4
Copy link
Author

@mcspr , I feel like I'm not sure where to go with this at this point. I could add more code to validate the custom user options by checking that each option is added only once and iterating through the user provided options to check them. But this would add a lot more complexity to this PR. Should we just leave it as it is now (it works) and allow the user to ensure correct options when/if they choose to use this feature? I don't mind adding the complexity to validate this, I just don't want to bloat the codebase beyond what's needed. Your thoughts?

@bwjohns4 bwjohns4 marked this pull request as ready for review May 26, 2022 14:08
@mcspr
Copy link
Collaborator

mcspr commented May 27, 2022

Let me write up my proposal as a patch, then. I feel like my comment was not clear enough.
edit: #8582

@mcspr mcspr mentioned this pull request May 27, 2022
@mcspr
Copy link
Collaborator

mcspr commented Jun 14, 2022

superseded by #8582

@mcspr mcspr closed this Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants