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

conn: Simplify and overhaul conn API #5533

Closed
wants to merge 4 commits into from

Conversation

miri64
Copy link
Member

@miri64 miri64 commented Jun 8, 2016

This change (as discussed in #5091) overhauls the conn API in the following manner:

  • for every conn objects now also one can add a callback for asynchronous
    reception on creation
  • The source address on creation may now be NULL, implying an implicit bind
    when sending. This makes the conn_find_best_source() function unnecessary.
  • a common address type for both IPv4 and IPv6 addresses was introduced
  • instead of having addresses, address length and ports/protocols given
    separately a new data type called "end points" for every connectivity type is
    introduced conn_ep_x_t, consisting of the address, its family, an (optional)
    interface identifier (see pkg/oonf_api: use new repo url #5111 netif: initial import of a common network interface API #5511) and its port/protocol
  • send for connection-less communication was simplified by not requiring source
    information anymore (a stack is encouraged to find a best match itself).
    It and the interface one is supposed to send over can however be supplied by
    (optionally) providing an already created conn object
  • TCP connection establishment was simplified: listen/connect functions were
    dropped, instead a user can either give the remote end point on creation
    (implicit connect) or omit it (implicit listen)

For actual porting this API change needs #5111 #5511 for interface. #5526 would be helpful for efficient porting to GNRC, but it isn't required (one can easily do the same with a dispatching thread).

Some doc changes from #5509 are also included in this PR.

@miri64 miri64 added Area: network Area: Networking Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. labels Jun 8, 2016
@miri64 miri64 added this to the Release 2016.07 milestone Jun 8, 2016
@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from b974bc9 to 2629544 Compare June 8, 2016 14:51
* @return pointer to an IPv6 address configured on an interface with the best
* match to @p dst
*/
ipv6_addr_t *conn_find_best_source(const ipv6_addr_t *dst);
Copy link
Member Author

Choose a reason for hiding this comment

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

As stated in the description: the new implicit binding capabilities of the API make this function unnecessary.

@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from 2629544 to ba6852c Compare June 8, 2016 16:34
@miri64
Copy link
Member Author

miri64 commented Jun 8, 2016

I also provided a small simple application for conn_udp to give you an impression of how straight-forward the implementation of a server could be.

conn_ep_udp_t client_address;
int res;

msg_receive(&msg); /* wait for message */
Copy link
Member Author

Choose a reason for hiding this comment

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

For none-asynchronous handling of incoming messages one could just start from the code from 88 here and remove the surrounding switch-case.

@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from ba6852c to 55d9f57 Compare June 8, 2016 16:54
@kaspar030
Copy link
Contributor

I also provided a small simple application for conn_udp to give you an impression of how straight-forward the implementation of a server could be.

IMHO that can be a little more straight forward. pls compare this.

@miri64
Copy link
Member Author

miri64 commented Jun 8, 2016

Yes, but it isn't asynchronous. As stated in #5533 (comment) this is also possible with this API. Here's your example (and pretty much the synchronous version without shell of my example) with my API (and RIOT-fied).

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[128];

    conn_udp_t conn;
    conn_ep_udp_t local = { .family=AF_INET6, .port=60001 };
    conn_ep_udp_t remote = { 0 };

    int res = conn_udp_create(&conn, &local, NULL);
    if (res < 0) {
        return -1;
    }

    while(1) {
        res = conn_udp_recv(&conn, buf, sizeof(buf), &remote);
        if (res < 0) {
            errno = -res; perror("recv");
            return 0;
        }
        else {
            buf[res] = '\0';
            printf("res=%zu text=\"%s\"\n", res, buf);
            conn_udp_send(&conn, buf, res, &remote);
        }
    }

    return 0;
}

Bahm, one line less ;-P (okay, I cheated with the errno thing). But come on, by now you should see, that both our proposals are pretty comparable, mine is just more stack implementor friendly. If you think your example is better suited for newcomers I can change that. I just wanted to show case the asynchronous capabilities of this API.

@kaspar030
Copy link
Contributor

Bahm, one line less ;-P (okay, I cheated with the errno thing).

try the other side (the echo client).

But come on, by now you should see, that both our proposals are pretty comparable

Yep, and I like it a lot better. Feels like you took my implementation, "s/sock_/conn_/", then added the callback. ;)

Let's find the optimum. I'll concentrate on UDP for now.

mine is just more stack implementor friendly.

Why do you think that?

Some thoughts:

  • let's keep asynchonous optional

e.g., remove it from the _init/_create() function, and introduce sth like _set_callback().
That way applications not using it can omit even compiling the support for that. And as plain callbacks are a pain in the ass to implement within RIOT (unless they just get executed in the network stack's context), we can maybe come up with a generic event loop (like uloop from openwrt) and tie async operation to that, in a portable way.

  • IMHO keeping a remote endpoint parameter in the initialization function is useful
    That way all possible combinations can be expressed:
  1. specify local, but no remote -> classic server bind
  2. specify local and remote -> create classic 1:1 "UDP connection"
  3. only specify remote -> same but with "don't care" local endpoint

Not sure the last two can be expressed with this PR's API.

  • for all uses but the first, the _send() function doesn't need a remote parameter.

That means for all but server uses, there'd be a NULL argument, which seems clumsy / redundant.
With possible file descriptor mapping in mind (with only read/write(fd, ptr, len) available), I went the _set_dst() way so mapping gets easier, but it requires that extra function call and address copying when a server replies.
The last "recv" could implicitly set the following "send" recipient, but that would only work for very simple servers and is maybe not intuitive at all.
If there's a second function (like _sendto() having both source and destination endpoint parameters, servers could use that to reply, but then there's no link to the bound connection object, and it would require copying the source address for every reply a server makes.

I'm not entirely happy with any of those options.
Maybe a third send option would be a good idea, so we'd have:

  • send(conn, ptr, len) <- for a 1:1 (client) "connection"
  • send_x(conn, remote, ptr, len) <- for replying in the server case
  • send_y(dst, src, ptr, len) <- for raw sending without object

What do you think?

  • we cannot leave out timeouts on receiving. Even simple use-cases require them for UDP, as even correctly expected packets might get lost or the remote might just die. So there needs to be an extra parameter to recv() in order to be able to deal with that.

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

try the other side (the echo client).

Did not actually compile this one, but this should do it (given the server's address is "abcd::1"):

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[] = "Hello!";

    conn_udp_t conn;
    conn_ep_udp_t remote = { .family=AF_INET6, port=60001 };

    ipv6_addr_from_str(&remote.addr.ipv6, "abcd::1");
    int res = conn_udp_create(&conn, NULL, NULL);
    if (res < 0) {
        return -1;
    }

    res = conn_udp_send(&conn, buf, sizeof(buf), &remote);
    if (res < 0) {
        errno = -res; perror("send");
        return -1;
    }
    res = conn_udp_recv(&conn, buf, sizeof(buf), &remote);
    if (res < 0) {
        errno = -res; perror("recv");
        return 0;
    }
    else {
        buf[res] = '\0';
        printf("res=%zu text=\"%s\"\n", res, buf);
    }
    return 0;
}

mine is just more stack implementor friendly.

Why do you think that?

Because due to send always coming with some state, we don't have to maintain a list of all states to successfully send on stacks that come with their own port management.

Some thoughts:

  • let's keep asynchonous optional

e.g., remove it from the _init/_create() function, and introduce sth like _set_callback().
That way applications not using it can omit even compiling the support for that. And as plain callbacks are a pain in the ass to implement within RIOT (unless they just get executed in the network stack's context), we can maybe come up with a generic event loop (like uloop from openwrt) and tie async operation to that, in a portable way.

Yupp, just realized that when trying to port this interface to GNRC: even with #5526 I need to allocate three pointers for callback support (one for the GNRC-internal callback, one for the context, and one for the user-owned conn callback). Having some central event loop might also be beneficial for that, but probably not as straight forward as just using the stack's context with the callback feature of #5526. Will make it optional.

  • IMHO keeping a remote endpoint parameter in the initialization function is useful
    That way all possible combinations can be expressed:
  1. specify local, but no remote -> classic server bind
  2. specify local and remote -> create classic 1:1 "UDP connection"
  3. only specify remote -> same but with "don't care" local endpoint

Not sure the last two can be expressed with this PR's API.

Point 3 is simple (again no guarantee for compilability ;-)):

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[] = "Hello!";

    conn_udp_t conn;
    conn_ep_udp_t remote = { .family=AF_INET6, port=60001 };

    ipv6_addr_from_str(&remote.addr.ipv6, "abcd::1");
    res = conn_udp_send(NULL, buf, sizeof(buf), &remote);
    if (res < 0) {
        errno = -res; perror("send");
        return -1;
    }
    return 0;
}

For 2. I see that it might be a nice-to-have feature, but in the end UDP just isn't working this way. I always found it baffling that you can call connect() with UDP sockets. Here's how it could look like in this PR's API:

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[] = "Hello!";

    conn_udp_t conn;
    conn_ep_udp_t local = { .family=AF_INET6, port=60001 };
    conn_ep_udp_t remote = { .family=AF_INET6, port=60001 };

    ipv6_addr_from_str(&remote.addr.ipv6, "abcd::1");
    int res = conn_udp_create(&conn, &local, NULL);
    if (res < 0) {
        return -1;
    }
    res = conn_udp_send(&conn, buf, sizeof(buf), &remote);
    if (res < 0) {
        errno = -res; perror("send");
        return -1;
    }
    return 0;
}

I'm not sure adding remote as a parameter would save code (both work-wise or binary size-wise) here. It would just move the &remote parameter from conn_udp_send() to conn_udp_create(). On the other hand side, by doing it the way presented we keep the API simple and clean (and thus - I argue - more comprehensible).

That means for all but server uses, there'd be a NULL argument, which seems clumsy / redundant.
[…]

Only if conn_udp_create() had a remote parameter this would be the case (as you also point out in your further going on). This is another reason why I decided against both having the remote parameter for UDP and IP in conn_udp_create() and providing an alternative send function: it makes the API less stream-lined and thus less elegant / comprehensible. Sure, having a function for every possible use-case can be nice, but not at the cost of API comprehensiveness, especially if you don't save any actual code by it (as shown above).

  • we cannot leave out timeouts on receiving. Even simple use-cases require them for UDP, as even correctly expected packets might get lost or the remote might just die. So there needs to be an extra parameter to recv() in order to be able to deal with that.

Sure, timeouts are needed, but I don't want to pull-in xtimer dependencies just to serve this use-cases (there are enough were it isn't needed). Timeouts can very well be implemented using asynchronous communication (again uncompiled):

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"
#include "xtimer.h"

#define RECV        (0x9999)

void callback(conn_udp_t *conn)
{
    msg_t msg = { .type = RECV, .content = { .ptr = conn } };
    msg_send(&msg. handler_pid);
}

void *handler(void *)
{
    char buf[128];
    msg_t msg;

    conn_udp_t conn;
    conn_ep_udp_t local = { .family=AF_INET6, port=60001 };
    conn_ep_udp_t remote;

    int res = conn_udp_create(&conn, &local, callback);
    if (res < 0) {
        return -1;
    }
    if (xtimer_msg_receive_timeout(&msg, 1000) < 0) {
        puts("timeout");
        return -1;
    }
    if (msg.type == RECV) {
        res = conn_udp_recv(msg.content.ptr, buf, sizeof(buf), &remote);
        if (res < 0) {
            errno = -res; perror("send");
            return -1;
        }
    }
    return 0;
}

The code part from the xtimer_msg_receive_timeout() to the conn_udp_recv() could very well be supplied as a submodule of conn_udp that also depends on xtimer, but as you can see it's also possible in bare-metal ;-).

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

(edited away some errors in the last example)

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

Made callbacks optional, not by removing the parameter though. It is still set by parameter, but a macro (CONN_HAS_CALLBACKS) needs to be defined to have this feature.

Also updated some doc.

@kaspar030
Copy link
Contributor

kaspar030 commented Jun 9, 2016

try the other side (the echo client).

res = conn_udp_send(&conn, buf, sizeof(buf), &remote);
res = conn_udp_recv(&conn, buf, sizeof(buf), &remote);

(mark here)

    mine is just more stack implementor friendly.

Why do you think that?

Because due to send always coming with some state, we don't have to
maintain a list of all states to successfully send on stacks that come
with their own port management.

I don't buy that these stacks are that inflexible. Any of those that
doesn't have the equivalent of SO_REUSEPORT?

Point 3 is simple (again no guarantee for compilability ;-)):
For 2. I see that it might be a nice-to-have feature, but in the end UDP
just isn't working this way.

I see, this PR just doesn't support this. Well, posix sockets do, and if
our API doesn't support it, we force application writers to check every
incoming packet if it matches the expected source.
In your example, where I wrote "mark here", if a random UDP packet
arrives, recv picks it up, instead of the stack dropping it. That means
DOS just by knowing a service's address and port.

I'm not sure adding |remote| as a parameter would save code (both
work-wise or binary size-wise) here. It would just move the |&remote|
parameter from |conn_udp_send()| to |conn_udp_create()|.

That alone clearly saves copying the remote address & port for every packet sent.

On the other
hand side, by doing it the way presented we keep the API simple and
clean (and thus - I argue - more comprehensible).

Let's not rate simplicity, cleanliness and comprehensiblity yet, too
subjective. ;)

That means for all but server uses, there'd be a NULL argument,
which seems clumsy / redundant.

Sure, having a function for every possible use-case can

Some way of setting a connection object's remote is needed, see above.

  * we cannot leave out timeouts on receiving. Even simple use-cases
    require them for UDP, as even correctly expected packets might
    get lost or the remote might just die. So there needs to be an
    extra parameter to |recv()| in order to be able to deal with that.

Sure, timeouts are needed, but I don't want to pull-in |xtimer|
dependencies just to serve this use-cases (there are enough were it
isn't needed).

Personally I'd consider not setting a timeout a bug, but that aside:

  • it doesn't necessarily mean xtimer is pulled in
  • show me a network stack which doesn't already depend on timers in some way

Timeouts can very well be implemented using asynchronous
communication (again uncompiled):

There are perfectly valid, simple programs that need timeouts, but for
which asynchronous operation means a lot of (complexity-) burden.

The example (using xtimer_msg_receive_timeout) has so many conceptual
flaws, it should prove my point. ;)

Also, please consider that the API might be useful on other OSs, without
messages, xtimer, ... I'm using the one in my github repo to write
network applications on Linux, it works well there. I will argue hard
against limiting this to RIOT.

@kaspar030
Copy link
Contributor

Sure, having a function for every possible use-case can be nice

How many are there?

  1. server binding to one port, handling multiple clients
    2a. p2p "server" dealing with only one client
    2b. client connecting to server, specifying local endpoint
  2. client connecting to server, doesn't care about local addr/port

2a and 2b are identical.

@kaspar030
Copy link
Contributor

another reason why I decided against both having the remote parameter for UDP and IP in conn_udp_create() and providing an alternative send function: it makes the API less stream-lined and thus less elegant / comprehensible.

conn_tcp_create() does have a 'remote' parameter. Having it for UDP wins us consistency points.

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

I don't buy that these stacks are that inflexible. Any of those that
doesn't have the equivalent of SO_REUSEPORT?

Need to research this, but you might be right, at least for lwIP.

I see, this PR just doesn't support this. Well, posix sockets do, and if
our API doesn't support it, we force application writers to check every
incoming packet if it matches the expected source.
In your example, where I wrote "mark here", if a random UDP packet
arrives, recv picks it up, instead of the stack dropping it. That means
DOS just by knowing a service's address and port.

You might have a point with this (as well with the conn_tcp_create() argument). Let me think about it for a while.

Personally I'd consider not setting a timeout a bug, but that aside:

  • it doesn't necessarily mean xtimer is pulled in
  • show me a network stack which doesn't already depend on timers in some way

Let me think about this, too ;-)

@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from e708560 to 3669d98 Compare June 9, 2016 14:23
@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

Okay, thought about it, and changed it. Now _create() of all connectivity types also receives a dst argument and _recv()/_recvfrom() (the latter was introduced together with _sendto() to differentiate from the functions without end point required) got an (optional) timeout parameter.

I'm thinking about also adding a _set_local() and _set_remote() function.

Here are the examples for the three use-cases now

\1. server handling multiple clients

int main(void)
{
    char buf[128];

    conn_udp_t conn;
    conn_ep_udp_t local = { .family=AF_INET6, .port=60001 };
    conn_ep_udp_t remote = { 0 };

    int res = conn_udp_create(&conn, &local, NULL, NULL);
    if (res < 0) {
        return -1;
    }

    while(1) {
        res = conn_udp_recvfrom(&conn, buf, sizeof(buf), &remote, 0);
        if (res < 0) {
            errno = -res; perror("recvfrom");
            return 0;
        }
        else {
            buf[res] = '\0';
            printf("res=%zu text=\"%s\"\n", res, buf);
            conn_udp_sendto(&conn, buf, res, &remote);
        }
    }

    return 0;
}

\2. p2p connection

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[] = "Hello!";

    conn_udp_t conn;
    conn_ep_udp_t local = { .family=AF_INET6, port=60001 };
    conn_ep_udp_t remote = { .family=AF_INET6, port=60001 };

    ipv6_addr_from_str(&remote.addr.ipv6, "abcd::1");
    int res = conn_udp_create(&conn, &local, &remote, NULL);
    if (res < 0) {
        return -1;
    }
    res = conn_udp_send(&conn, buf, sizeof(buf));
    if (res < 0) {
        errno = -res; perror("send");
        return -1;
    }
    return 0;
}

\3. client with arbitrary local endpoint

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "net/af.h"
#include "net/conn/udp.h"

int main(void)
{
    char buf[] = "Hello!";

    conn_udp_t conn;
    conn_ep_udp_t remote = { .family=AF_INET6, port=60001 };

    ipv6_addr_from_str(&remote.addr.ipv6, "abcd::1");
    res = conn_udp_send(NULL, buf, sizeof(buf), &remote);
    if (res < 0) {
        errno = -res; perror("send");
        return -1;
    }
    return 0;
}

@kaspar030
Copy link
Contributor

kaspar030 commented Jun 9, 2016 via email

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

Are you very tied to the name conn? For IP it doesn't make sense at all,
for UDP just so-so, only with TCP it fits.

Says the one criticizing me for sticking to close to sockets ;-P. I like conn, and I don't see how it doesn't make sense. IP and UDP also provide connectivity, though they don't necessarily provide connection ;-)

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

And still, IMHO the callback parameter must go, as we (currently) cannot
execute the callback in the same thread's context.

I don't see that as a blocker... And I want the callback support!

@miri64
Copy link
Member Author

miri64 commented Jun 9, 2016

Is there ever a way to execute callbacks in a running thread's context? That sounds really messy. I want callbacks to finally bring the much requested select() support to posix_sockets. For that the thread context is irrelevant, since it just needs to set some flags.

@kaspar030
Copy link
Contributor

Is there ever a way to execute callbacks in a running thread's context? That sounds really messy.

Asynchronously? Probably not. That's even supermessy on Posix.

I want callbacks to finally bring the much requested select() support to posix_sockets. For that the thread context is irrelevant, since it just needs to set some flags.

How about a simple event loop?
sth like:

conn_udp_create(conn1, ...);
event_loop_t eloop;
event_loop_init(&eloop);
event_loop_add(&eloop, conn_udp_get_eventsource(&conn));

event_loop(esink);       /* loops, executing event handlers */

event_loop() could exit the loop whenever a callback exits with a specific exit code, or always just return the event (event_wait()) and let the caller decide what to do.

I want asynchronous conn, too! ;) Let's try to make it somewhat nice.

@miri64
Copy link
Member Author

miri64 commented Jul 20, 2016

This is a lot to read through and think about for one night being absent from github ;-). More detail reply will come in, but for now a small factual correction:

@kaspar030 wrote

sock_udp doesn't support callbacks ATM.

Neither does conn in the most current version. Look at the code ;-).

By now, I'm also thinking that some kind of external event loop might be the better solution.

@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

Okay let me summarize the current discussion (and give my rebuttal where I didn't already):

  • name:
    • @kaspar030 prefers sock because he prefers the similarities to socket to not confuse new users. Also, conn makes no sense for connection-less transport layers (note: though conn stands for connectivity not connection)
    • @gebart, @OlegHahm and me prefer to stick with the current naming (conn). Originally that name was chosen to clearly distinguish it from sockets, so users don't confuse the two APIs.
  • callbacks:
    • all agree that asynchronous API needs to be postponed until we have some kind of API for this in general (e.g. some kind of event-handling lib)
  • multi-level include-structure:
    • @kaspar030 is against it
    • I see no way around it
      At least in the past I always ran into some include loop when having multiple implementations, so if you can tell me how to prevent those here, please go ahead and help me
    • @OlegHahm deems this discussion as unimportant
  • recvfrom:
    • I prefer to have recv/recvfrom symmetrical to send/sendto
  • parameter-order:
    • that's not how I learned it (obj.method(out, out, ..., in, in, ...), but that was in OOP, so I don't think that applies here. So agreed :-). Will adapt.

@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

Does this overall reflect the opinions stated above? Regarding F2F meeting agreed. When do you have time for that in the next days?

@jnohlgard
Copy link
Member

@miri64
Good summary!

  • parameter-order:
    • that's not how I learned it (obj.method(out, out, ..., in, in, ...), but that was in OOP, so I don't think that applies here. So agreed :-). Will adapt.

In my opinion it doesn't matter, I personally will still have to look it up in the manual every time I use it regardless if it's in-out or out-in.

@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

Oh one thing I forgot:

  • local/remote setter: I don't see the use-case for them and even somewhat critical to allow for changing of the destination after creating the connection. For UDP packets that constantly change their remote we have sendto, having a setter for that remote, if the remote changes [edit]more rarely[/edit] I would prefer creating a new conn for that (might even be the same conn that was destroyed before).

@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

note: edited the post (in case someone follows discussion via mail) ;-)

@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

I see no way around it At least in the past I always ran into some include loop when having multiple implementations, so if you can tell me how to prevent those here, please go ahead and help me

Some example for this kind of include-loop: Let's say I want to define conn_udp_t in net/gnrc/conn/udp.h for GNRC and conn_ep_udp_t would be defined in net/conn/udp.h instead of net/conn/ep.h.
I need to include net/conn/udp.h into net/gnrc/conn/udp.h because of this.
But the first already includes the latter, so the compiler is able to determine the width of conn_udp_t and not throw an error (the application can not include net/gnrc/conn/udp.h before, otherwise we lose stack-independence) => include-loop. That's why the eps are in their own file.
Now net/gnrc/conn/udp.h only needs to include net/conn/ep.h and net/conn/udp.h can include both of the above.

@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from 7f575f3 to 84a2100 Compare August 5, 2016 16:18
@miri64
Copy link
Member Author

miri64 commented Aug 5, 2016

I made the parameter change, and rebased and squashed (the latter because I think the commit history does not much contribute to the discussion).

miri64 added 2 commits August 11, 2016 15:09
This change overhauls the `conn` API in the following manner:

* The source address on creation may now be `NULL`, implying an implicit bind
  when sending. This makes the `conn_find_best_source()` function unnecessary.
* a common address type for both IPv4 and IPv6 addresses was introduced
* instead of having addresses, address length and ports/protocols given
  separately a new data type called "end points" for every connectivity type is
  introduced `conn_ep_x_t`, consisting of the address, its family, an (optional)
  interface identifier (see RIOT-OS#5111) and its port/protocol
* send for connection-less communication was simplified by not requiring source
  information anymore (a stack is encouraged to find a best match itself).
  It and the interface one is supposed to send over can however be supplied by
  (optionally) providing an already created `conn` object
* TCP connection establishment was simplified: listen/connect functions were
  dropped, instead a user can either give the remote end point on creation
  (implicit connect) or omit it (implicit listen)
@miri64 miri64 force-pushed the conn/api/simplify+overhaul branch from 84a2100 to b808249 Compare August 11, 2016 13:09
@miri64
Copy link
Member Author

miri64 commented Aug 11, 2016

Updated doc based on some lessons learned from trying to implement it (which is really fun on both sides of the API now! :D). Will provide the backend, when some dependencies got merged in. Otherwise this PR will be even more hard to read for people coming in ;-)

@kaspar030
Copy link
Contributor

Regarding F2F meeting agreed. When do you have time for that in the next days?

Sorry for the late reply. Sometime next week would be fine.

@miri64
Copy link
Member Author

miri64 commented Aug 11, 2016

Regarding F2F meeting agreed. When do you have time for that in the next days?

Sorry for the late reply. Sometime next week would be fine.

How about Wednesday? I will write a mail to devel to get some other devs involved, too.

@kaspar030
Copy link
Contributor

How about Wednesday? I will write a mail to devel to get some other devs involved, too.

Perfect!

@miri64
Copy link
Member Author

miri64 commented Aug 15, 2016

I just finished my examplary GNRC port for conn_udp (including extensive tests). Please have a look: https://github.com/miri64/RIOT/tree/conn/api/simplify%2Boverhaul-port-tmp. Dependent PRs of that branch are this PR (of course), #5526 (for mbox support of GNRC) and #5749 (to be able to run the tests)

@brummer-simon
Copy link
Member

brummer-simon commented Aug 16, 2016

Without having read the whole discussion, is this still a Point ?

" TCP connection establishment was simplified: listen/connect functions were dropped, instead a user can either give the remote end point on creation (implicit connect) or omit it (implicit listen)"

Is it still up to debate? This would remove the possiblity to allow only specific connections that came from a specified IP-Address. Oh and I would like to seen the listen call prototype changed to:

int conn_tcp_listen(conn_tcp_t *conn, conn_tcp_t queue[], int queue_len);

@miri64
Copy link
Member Author

miri64 commented Aug 16, 2016

@brummer-simon are you available tomorrow? If yes, let's discuss this at the meeting.

@miri64
Copy link
Member Author

miri64 commented Aug 16, 2016

This would remove the possiblity to allow only specific connections that came from a specified IP-Address.

I don't understand that comment. Do you mean that listen and connect would be called on the same socket (with POSIX sockets)?

@brummer-simon
Copy link
Member

Nope but in posix, if you call listen with a specified address in the connection defining socket, the passive connection structures will only accept connection requests from peers that connect that specific address and port combination. If you call listen with an unspecified address, only the port number is important.

If you remove listen() and use only conn_create, you lose this option because you get rid of the connection specifing structure thats needed for the listen call.

@miri64
Copy link
Member Author

miri64 commented Aug 16, 2016

Oh and something else:

int conn_tcp_listen(conn_tcp_t *conn, conn_tcp_t queue[], int queue_len);

Your conn_tcp_t looks quite big. Are you sure there isn't a subset of conn_tcp_t you could utilize as a new conn_tcp_queue_t type?

@miri64
Copy link
Member Author

miri64 commented Aug 16, 2016

Nope but in posix, if you call listen with a specified address in the connection defining socket, the passive connection structures will only accept connection requests from peers that connect that specific address and port combination. If you call listen with an unspecified address, only the port number is important.

Ah... Okay then lets discuss this tomorrow, how to handle this.

@brummer-simon
Copy link
Member

brummer-simon commented Aug 16, 2016

Yes i am sure. Every queue element handles a full tcp connection. The data structure have no shared subset (except destination port number maybe). In simpelst scenario where only a single TCP connection is handled at a time, the queue has a size of one element.

About my availablity tomorrow: I'll try to join the mumble session, but I can't promise that i'll be there.

@miri64
Copy link
Member Author

miri64 commented Aug 18, 2016

Closed in favor for #5533.

@miri64 miri64 closed this Aug 18, 2016
@miri64 miri64 deleted the conn/api/simplify+overhaul branch August 18, 2016 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: network Area: Networking Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants