This discussion revolves around some platforms on which it is difficult to efficiently determine if IPv6 is *not* available.

Observations:

 * The proposed solutions involve determining the converse.
 * On a given platform, answer is likely to be stable for a very long
   time.  E.g. until a kernel/network/system change
 * The OP reports that getting the "no IPv6" result efficiently is very
   situation - and probably customer - dependent - e.g. is using
   heuristics that would not be a good fit in libcurl
 * libcurl isn't the right place for customer-dependent work-arounds;
   at best the detection would be once/process

Here's an alternative approach to consider:

 * Have libcurl read an optional configuration file; e.g.
   /etc/default/libcurl
 * The initial contents would be 'ipv6_disable:1"
 * When a similar issue arises for IPv4, or DNS, codesets, or some
   other system behavior, it's easy to add controls
 * If the file doesn't exist, libcurl uses its current
   defaults/behaviors - configure and runtime
 * Setting up the file becomes an installation-time issue on your
   platform.  It can take as long as you like, and be as system
   specific as you like.
 * The overhead for libcurl is very small.  On most systems, the file
   won't exist - and as strace will tell you, many, many files are
   looked-up and ignored when any non-trivial process starts. If the
   file does exist, the flags can be cached when the file is parsed. 
   If performance ever becomes an issue (seems unlikely), the cache
   could be in a shared memory segment.
 * Optionally, libcurl could provide an API function to query a flag by
   name.

This would seem to meet the OP's requirements: no change to the application, easy to install on the affected platforms, efficient.

And it is minimally disruptive to libcurl, which should satisfy the developers.


Timothe Litt
ACM Distinguished Engineer
--------------------------
This communication may not represent the ACM or my employer's views,
if any, on the matters discussed.

On 23-Sep-22 15:15, Dmitry Karpov via curl-library wrote:
Let me reply a bit out of order and combine some arguments:

Exactly. Because it would create exactly the same outcome.
This would create the same outcome ONLY after your PR fixing IPv4 mode is 
applied.
Before that PR, the problem existed for ALL modes, so there was no work around 
even for IPv4 mode.

First: most developers won't write this kind of function at all.
curl has supported IPv6 since January 2001 and we are talking about doing this 
in a future release - after nearly 22 years of shipping IPv6 suppport. Clearly 
a lot of users manage decently without this.
Yes, it may be supported since a while ago, but when I started migrating to 
dual-stack more than a year ago, I stepped on multiple issues, which made me 
think that dual-stack has been hardly used on a
wide scale where performance was quite critical - at least on embedded 
platforms with large populations.

My applications run on different HW platforms, use a wide range of linux 
kernels with different configurations and user population is greater than 62M, 
which covers a very wide range of ISPs, routers and network setups.  That's why 
maybe I stepped on the issues which other didn't see - or just didn't care.
The 30ms delay during connection setup maybe not a critical issue for everyone, 
but it is a still an issue and regression when migrating from a single-stack 
for time critical applications.

.They use the same code to determine IPv6 functionality: your function, and 
they do it roughly at the same time in the process: right before setting up a 
new connection.
The only difference is how that function is called.
That's a huge difference. Your proposal (which will work only after your PR is 
integrated) requires modifying transfer setup code in ALL the places (for current and new 
code ) whereas my proposal allows to do it in just one place and not worry about if 
someone will miss "manual" check in some place and create a timing issue.

For some reason you think this particular transfer detail deserves a completely 
new and different take, and this reason seems to be because of the way you have 
designed and setup your libcurl usage. In my view, it > does not seem to be 
particularly well founded from a libcurl architectural point of view.
I'm sorry, but I think consistency and simplicity is more important here.
I would argue that I see quite the opposite - that the "consistency and 
simplicity" is on my side rather than on yours.

The "IPv6 works" is not a transfer level detail - it is a system level detail.
It is important to outline that the "IPv6 works" check is not a IPv6 connectivity check - 
it is just a check if IPv6 is "enabled" on the system and can be used in connection setup.

This check is currently done when a multi handle is created and then its result 
is applied to every easy handle added to that multi-handle - regardless the 
resolve mode set on the easy handle.
(And a while ago that check was done only once in curl_global_init() which 
created a base for multi-threading and race condition issues)

What you suggest is essentially to consider "IPv6 doesn't work on a system" as 
a transfer level detail and do such check for every transfer setup.
And this just goes against consistency and simplicity, in my opinion.

So, if someone steps on the same connection delay issues using dual-stack 
libcurl as I did - your recommendation (again after you PR is integrated) would 
be:

     Because our default "IPv6 works" check doesn't work for you do the 
following:
     - write your own function doing " IPv6 works" check which works better for 
you (the same step as in my approach)
     - change ALL the places in the code where your transfers are set up, invoke your 
"IPv6 works" check function
        and force your transfers to use IPv4 resolve mode if that check fails.

     Fine print details:
         - be careful not to miss any single place where transfers might be 
using dual-stack in your code.
          - when you add new transfers to your code be careful not to miss the "IPv6 
works" check function if they might be using dual-stack.
          - if you code integrates (or will do that future) some "3rd party" 
components which might be using libcurl and dual-stack resolve modes, which you can't 
modify
             then, sorry, you are out of luck. This is an "engineering issue", so the 
connection delays when "IPv6 doesn't work" will be there.

With my approach:
       Because our default "IPv6 works" check doesn't work for you do the 
following:
       - write your own function doing "IPv6 works" check which works better 
for you.
       - set your function as "IPv6 works" callback in curl_global_init_ipv6()

     Fine print details:
          None. It will work for current and future code using dual-stack modes and for 
"closed code" of 3rd party components
          without any modifications in the transfer setup code.

So, I guess from this side-by-side comparison it is pretty obvious which 
approach adheres more to the principles of consistency and simplicity.
Also, there are already "system" level callbacks which allow to set system wide 
memory management functions in curl_global_init_mem().

And I just propose to add a similar system wide "IPv6 works on the system" 
check callback, so I am quite consistent here with what libcurl does.

Thanks,
Dmitry  Karpov

-----Original Message-----
From: Daniel Stenberg<dan...@haxx.se> Sent: Friday, September 23, 2022 12:54 AM
To: Dmitry Karpov<dkar...@roku.com>
Cc: Dmitry Karpov via curl-library<curl-library@lists.haxx.se>
Subject: RE: [EXTERNAL] Re: Feature request: provide ability to set a global 
callback function telling libcurl if IPv6 works on the system

On Thu, 22 Sep 2022, Dmitry Karpov wrote:

If I understand what you suggest, then in all the places where the "AUTO"
(CURL_IPRESOLVE_WHATEVER) is used, I would need to use something like:

long resolve_mode = my_ipv6_works() ? CURL_IPRESOLVE_WHATEVER :
CURL_IPRESOLVE_V4; curl_easy_setopt(curl, CURLOPT_IPRESOLVE,
resolve_mode);
Exactly. Because it would create exactly the same outcome.

First one is that the "AUTO" will stop being the most efficient mode
for dual-stack, because its efficiency will depend on how "IPv6 works"
check (provided by "factory default" Curl_ipv6works()) works on some
systems (kernels, network configs etc).
Not at all. You say you can write a "ipv6works" function (as a callback) that 
can detect/set the status, so clearly then it can be called by your own application as 
well. There is no difference in effectiveness between these two approaches. They use the 
same code to determine IPv6 functionality: your function, and they do it roughly at the 
same time in the process: right before setting up a new connection.

The only difference is how that function is called.

And if some curl application accidentally steps on this issue and
needs to implement some custom "IPv6 works" mechanism, then it is
suggested that after implementing it, application developer should go
through all the places where transfer options are set, explicitly use
application specific
"IPv6 works" function and explicitly select IPv4 mode if that function
returns "false".
Exactly like you do when you set other properties and control other 
characteristcs of libcurl transfers. There are *300* options for 
curl_easy_setopt(), I'm sure you realized by now that this is the libcurl API 
model.

For some reason you think this particular transfer detail deserves a completely 
new and different take, and this reason seems to be because of the way you have 
designed and setup your libcurl usage. In my view, it does not seem to be 
particularly well founded from a libcurl architectural point of view.

I'm sorry, but I think consistency and simplicity is more important here.

Frankly, I think it is just cruel to make developers do that when
everything can be solved in just one system level "IPv6 works" callback.
First: most developers won't write this kind of function at all. curl has 
supported IPv6 since January 2001 and we are talking about doing this in a 
future release - after nearly 22 years of shipping IPv6 suppport. Clearly a lot 
of users manage decently without this.

Then, many users do their setopt logic in a single place or at least offers 
mechanics to extend the set without going through hoops. Doing this logic as a 
callback or as a setopt then means roughly the same effort.

I don't think insisting on consistency is cruel.

And for me the biggest problem is that I just can't change the code of
certain curl-based components used in my application. They are written
by some other developers and closed for any modifications.
This is the core of your issue, not necessarily related to the libcurl issue at 
hand. I would argue that this is a fragile way to go about libcurl use (and 
fix). What if you find another flaw tomorrow where a setopt is used wrongly?
Would we solve those as well with additional work-arounds and coming in through 
the backdoor just because someone else has the key to the front door?

In other words, it is much easier and cleaner to override how
Curl_ipv6works() to adjust to demands of certain systems/scenarios
than do checks if "IPv6 works" and change resolve mode in every place
where application sets a transfer.
Easier and cleaner for you. I maintain that it is dirtier for libcurl and its 
API (design).

Also, Curl_ipv6works() is called only once per multi-handle, and the
"IPv6 works" setting is then used for all easy handles added to that 
multi-handle.
So, essentially there is no need to do "IPv6 works"  for every easy
handle - that's also what the system "IPv6 works" callback efficiently
allows to avoid.
When you write your own ipv6works function, you can decide yourself how often 
and when it needs to rerun the check. There's nothing that says it needs to do 
anything for every easy handle, it could just return the previous result.

Attachment: OpenPGP_signature
Description: OpenPGP digital signature

-- 
Unsubscribe: https://lists.haxx.se/listinfo/curl-library
Etiquette:   https://curl.se/mail/etiquette.html

Reply via email to