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

sock: Introduction of new application layer API #5758

Merged
merged 19 commits into from
Sep 27, 2016

Conversation

miri64
Copy link
Member

@miri64 miri64 commented Aug 18, 2016

This introduces a new alternative and better API to conn. It differs in the
following aspects:

  • a common address type for both IPv4 and IPv6 addresses is introduced
  • communication end-points are abstracted as end-point types sock_x_ep_t,
    containing the address, its family, its port (if required for protocol) and
    the interface identifier.
  • All functions require some kind of state. Sending of datagrams to the same
    source or replying to incoming datagrams is thus simplified
  • TCP connection establishment was overall reworked: connected sockets and
    listening sockets are now two distinct types. An accept on a listening socket
    than yields a connected socket

This introduces a new alternative and better API to `conn`. It differs in the
following aspects:

* a common address type for both IPv4 and IPv6 addresses is introduced
* communication end-points are abstracted as end-point types `sock_x_ep_t`,
  containing the address, its family, its port (if required for protocol) and
  the interface identifier.
* All functions require some kind of state. Sending of datagrams to the same
  source or replying to incoming datagrams is thus simplified
* TCP connection establishment was overall reworked: connected sockets and
  listening sockets are now two distinct types. An accept on a listening socket
  than yields a connected socket
@miri64 miri64 added Area: network Area: Networking Process: API change Integration Process: PR contains or issue proposes an API change. Should be handled with care. labels Aug 18, 2016
@miri64 miri64 added this to the Release 2016.10 milestone Aug 18, 2016
*/
typedef struct {
sock_addr_ip_t addr; /**< IP address */
int family; /**< family of sock_tcp_ep_t::addr */
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason why this is an int? (maybe we can carve off two bytes here)

Copy link
Member Author

Choose a reason for hiding this comment

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

At least on 32-bit platforms those would return anyways due to alignment. But I'm thinking of moving it to the top, that so that endpoints can be identified to a degree even after casting, if addr isn't an IP address, but e.g. a name for an ICN-based sock. And then int is the only type that would make sense, due to the alignment of the following array.

@OlegHahm
Copy link
Member

To be nitpicky: the correct title of this PR should be: "sock: Introduction of a new transport layer API" since the API is usually described by the services that can be accessed through it, not by the services that are calling it.

@kaspar030
Copy link
Contributor

Some things:

  • Could we completely get rid of the ip address type? Like:
typedef struct {
    union {
        uint8_t ipv6[16];
        uint8_t ipv4[4];
    } addr;
    [..]
}
  • I'm afraid pre-defining the error return values to the values defined by errno.h will lead to a lot of code converting a network stack's result value to the errno ones.
  • Could we get rid of the #ifdef'ed includes and replace them with e.g., #include "sock_impl.h" and select the right one via include path?

@kaspar030
Copy link
Contributor

  • many of the functions should return ssize_t instead of int

@OlegHahm
Copy link
Member

I'm afraid pre-defining the error return values to the values defined by errno.h will lead to a lot of code converting a network stack's result value to the errno ones.

What do you mean by converting a network stack's result value to the errno ones? What needs to be converted? Can you provide an example?

@kaspar030
Copy link
Contributor

What do you mean by converting a network stack's result value to the errno ones? What needs to be converted? Can you provide an example?

The API as is documents error return values like -ENOTCON, -EINVAL,
-ENOSPC, practically forcing the use of errno.h as they are usually
defined there.

Imagine a hypothetical implementation like

sock_udp_send(...) {
    [...]
    int res = _stack_udp_send(...);
    [...]
}

The network stack might return STACK_CONNECTION_NOT_SETUP defined to an
arbitrary value, instead of _ENOTCON, but the function needs to return
-ENOTCON to comply to the API.

I fear that implementations will have, at the end of every function of
the API it implements, a bunch of if/else/... or even switch statement
converting the underlying stack's return value to what this API predictates.

@OlegHahm OlegHahm added the State: waiting for other PR State: The PR requires another PR to be merged first label Aug 19, 2016
@kaspar030
Copy link
Contributor

Could we completely get rid of the ip address type?

@miri64 What do you think?

@miri64
Copy link
Member Author

miri64 commented Aug 19, 2016

@OlegHahm:

To be nitpicky: the correct title of this PR should be: "sock: Introduction of a new transport layer API" since the API is usually described by the services that can be accessed through it, not by the services that are calling it.

Then I can be nitpicky, too: raw IP isn't transport layer 😜 How about "network application API"?

@OlegHahm added the Waiting For Other PR label an hour ago

That's news to me... which PR are you referring to? #5511 is only important for 100% waterproof implementation of this API, but it certainly can also use stack-specific netif identifiers for now (which I did for GNRC for now, because otherwise the number of interdependent PRs will grow very large again ;-)).

@kaspar030 (sorry had to go to a meeting so I couldn't finish my replies):

Could we completely get rid of the ip address type?

In the scope of sock you mean? TBH I find it uglier to "redefine" a union in each end-point struct than just redefining the ipvx_addr_t structs.

If globally: I guess that will make a lot of people mad, that came to love the net/ipv6/addr.h header ;-).

I'm afraid pre-defining the error return values to the values defined by errno.h will lead to a lot of code converting a network stack's result value to the errno ones.

True, but worst-case this can be dealt with in a switch-case which shouldn't take as much memory. For our user-friendliness goal it is certainly more helpful to have a well-defined error behavior (because it simplifies debugging and testability "oh well I had an error, but I don't know what I did wrong exactly").

Could we get rid of the #ifdef'ed includes and replace them with e.g., #include "sock_impl.h" and select the right one via include path?

While I'm not a friend of wild include pathes, I tend to agree. Especially since this will definitely prevent some double inclusions that might happen due to faulty USEMODULE composition.

many of the functions should return ssize_t instead of int

Is this type available for all platforms. I remember some issues last time I tried to use it (way back when I implemented the first POSIX socket wrapper for DEStiny).

@miri64
Copy link
Member Author

miri64 commented Aug 19, 2016

Addressed comments that were straight forward ;-)

@kaspar030
Copy link
Contributor

Could we completely get rid of the ip address type?

In the scope of sock you mean? TBH I find it uglier to "redefine" a union in each end-point struct than just redefining the ipvx_addr_t structs.

Hm, if we remove the redundancy of the endpoint structs, it's not so
bad. And it would remove another ipv6 addr type.

I'm afraid pre-defining the error return values to the values defined by errno.h will lead to a lot of code converting a network stack's result value to the errno ones.

True, but worst-case this can be dealt with in a switch-case which shouldn't take as much memory. For our user-friendliness goal it is certainly more helpful to have a well-defined error behavior (because it simplifies debugging and testability "oh well I had an error, but I don't know what I did wrong exactly").
I had sock-specific error values in mind, which a stack would define.
Not sure if that would confuse users again... The switch-statements
would certainly enlarge code.

While I'm not a friend of wild include pathes, I tend to agree. Especially since this will definitely prevent some double inclusions that might happen due to faulty USEMODULE composition.
What's cleaner, "#include SOCK_IMPL_HEADER" or include path meddling?

many of the functions should return ssize_t instead of int

Is this type available for all platforms. I remember some issues last time I tried to use it (way back when I implemented the first POSIX socket wrapper for DEStiny).

I think it is. (quick grep shows avr and msp430 support, the rest has it
through newlib).

@miri64
Copy link
Member Author

miri64 commented Aug 19, 2016

Hm, if we remove the redundancy of the endpoint structs, it's not so
bad. And it would remove another ipv6 addr type.

Do you mean something like the following?

addr.h:

typedef struct {
    int family;
    union {
#ifdef SOCK_HAS_IPV6
        uint8_t ipv6[16];
#endif
        uint32_t ipv4;
    } addr;
} sock_addr_ip_t;

x.h:

typedef struct {
    sock_addr_ip_t addr;
    /* ... */
} sock_x_ep_t

Otherwise I don't know what you mean by "remov[ing] the redundancy of the endpoint structs".

I had sock-specific error values in mind, which a stack would define.
Not sure if that would confuse users again... The switch-statements
would certainly enlarge code.

And then those sock-specific errors need to be translated for the POSIX wrapper again :-/. I'm really not a fan of API-specific error values. They tend to produce exactly the problem you are describing: need for translating the errors in APIs that use the other API. True, if a stack also has its own error values we have the problem, too, but I see it way less problematic translating them one time here, than translating it for every abstraction layer.

What's cleaner, "#include SOCK_IMPL_HEADER" or include path meddling?

I dislike both equally :P But in this particular case I guess path meddling can help with the readability and maintainability of the API definition ;-).

I think it is. (quick grep shows avr and msp430 support, the rest has it
through newlib).

Okay great, then I will use it :-).

@kaspar030
Copy link
Contributor

And then those sock-specific errors need to be translated for the POSIX wrapper again :-/.

No, I mean, if the API saiy "SOCK_ENOSPC", than the sock implementation
header can "#define SOCK_ENOSPC ENOSPC". A different stack could define
"#define SOCK_ENOSPC 2342". No need to define the actual value, but
certainly important to define a symbolic name in the API.

@miri64
Copy link
Member Author

miri64 commented Sep 2, 2016

Mhhh, then EBADF might indeed be the better choice, since that is the only error in the POSIX specs that sounds similar to that.

@brummer-simon
Copy link
Member

Hmm then i think about it:

Why not use -ENOTCONN as well. I mean the existing handle is not connected anymore to the original connection ...

@miri64
Copy link
Member Author

miri64 commented Sep 2, 2016

Would also work, yes. Do you really need that for disconnect as well?

@miri64
Copy link
Member Author

miri64 commented Sep 2, 2016

What I mean is: for disconnect I imagine it is easier to just fail gracefully (i.e. in case of an error: do nothing).

@kaspar030
Copy link
Contributor

A passive connection (sock_tcp_t that is part of listens() queue parameter), re-opens into listening mode after closing of the previous connection. As soon as the connection is in listening mode, a new connection can connect to the sock_tcp_t struct.

I think this doesn't pose a problem. A ptr to "sock_tcp_t" is the only handle an application gets. There's no (simple enough) way to find out if the underlying connection completely disconnected and is now used for another connection. I rather propose to always require an explicit "close()" issued by the application, even if the connection is already disconnected. Only after the explicit "close()" the stack can re-use that connection object for a new connection.

@miri64
Copy link
Member Author

miri64 commented Sep 2, 2016

I found some more error classes myself while porting lwIP.

@miri64 miri64 mentioned this pull request Sep 2, 2016
5 tasks
@brummer-simon
Copy link
Member

@kaspar - That a good Idea, then only read() and write() need a -ENOTCONN.

However the documentation should mention that a passive connection can only be reopend by issueing a disconnect()-call.

@miri64
Copy link
Member Author

miri64 commented Sep 3, 2016

@kaspar - That a good Idea, then only read() and write() need a -ENOTCONN.

They already have :-).

However the documentation should mention that a passive connection can only be reopend by issueing a disconnect()-call.

I'm always confused by what you mean with "passive connection". In terms of socket-speak that a listening connection? Where should I put that documentation.

@brummer-simon
Copy link
Member

brummer-simon commented Sep 3, 2016

In socket-speak:
"Passive connection" = listen() and accept() = thats what usually servers do.
"Active connection" = open() = thats what clients do.

Hmm maybe you could a note at the disconnect()-call that says:
Reopens a connection, that was previously initialized by the listen()-call after connection termination.

@miri64
Copy link
Member Author

miri64 commented Sep 3, 2016

Hmm maybe you could a note at the disconnect()-call that says:
Reopens a connection that was previously initialized by the listen()-call.

That wording is a little bit confusing IMHO. How about "A sock_tcp_t instance that was initialized by a listen() call shall only be usable again after a disconnect() call."?

@kaspar030
Copy link
Contributor

Let's try to keep both the RFC nomenclature (active, passive) and the notion that queue members are "connected, disconnected or listening" out of the docs.

The former is confusing (I assume as only actual implementors seem to know the meaning.

The latter is IMHO mixing up things, as it is weird to have a memory area "listening", and it also seems implementation dependent.

-----Original Message-----
From: Simon Brummer notifications@github.com
To: RIOT-OS/RIOT RIOT@noreply.github.com
Cc: Kaspar Schleiser kaspar@schleiser.de, Mention mention@noreply.github.com
Sent: Sa., 03 Sep. 2016 14:22
Subject: Re: [RIOT-OS/RIOT] sock: Introduction of new application layer API (#5758)

In socket-speak:
"Passive connection" = listen() and accept() = thats what usually servers do.
"Active connection" = open() = thats what clients do.

Hmm maybe you could a note at the disconnect()-call that says:
Reopens a connection that was previously initialized by the listen()-call.

You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#5758 (comment)

@kaspar030
Copy link
Contributor

yes, or maybe clarify that it will not be reused (by accept) for new connections until closed.

-----Original Message-----
From: Martine Lenders notifications@github.com
To: RIOT-OS/RIOT RIOT@noreply.github.com
Cc: Kaspar Schleiser kaspar@schleiser.de, Mention mention@noreply.github.com
Sent: Sa., 03 Sep. 2016 14:25
Subject: Re: [RIOT-OS/RIOT] sock: Introduction of new application layer API (#5758)

Hmm maybe you could a note at the disconnect()-call that says:
Reopens a connection that was previously initialized by the listen()-call.

That wording is a little bit confusing IMHO. How about "A sock_tcp_t instance that was initialized by a listen() call shall only be usable again after a disconnect() call."?

You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#5758 (comment)

@brummer-simon
Copy link
Member

How about?

A sock_tcp_t instance that was initialized by a listen() call can only be reused by an accept() call after a disconnect() call."?

Copy link
Contributor

@kaspar030 kaspar030 left a comment

Choose a reason for hiding this comment

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

I'm not happy with the documentation, but let's merge this and (maybe) improve later.

/**
* @defgroup net_sock Sock API
* @ingroup net
* @brief Provides a minimal common API for applications to connect to the
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like that tagline.

The API is supposed to be used to write network applications. Would you mind to rephrase?

Copy link
Member Author

Choose a reason for hiding this comment

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

If there is so much doc change needed, why did you merge?!?

* ~~~~~~~~~~~~~~~~~~~~
*
* This module provides a minimal set of functions to establish connections or
* send and receives datagrams using different types of communication. Together,
Copy link
Contributor

Choose a reason for hiding this comment

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

  • why minimal set? (pls drop if you can't explain)
  • receives -> receive

"differnet types of communication"? please rephrase

*
* This module provides a minimal set of functions to establish connections or
* send and receives datagrams using different types of communication. Together,
* they serve as a common API that connects application- and network stack code.
Copy link
Contributor

Choose a reason for hiding this comment

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

  • why common?
  • again, I find it misleading to describe this API as "connecting application with network stack code".

* * @ref sock_tcp_t (net/sock/tcp.h): TCP sock
* * @ref sock_udp_t (net/sock/udp.h): UDP sock
*
* Each network stack must implement at least one sock type.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can drop this.

*
* Each network stack must implement at least one sock type.
*
* Note that there might be no relation between the different sock types.
Copy link
Contributor

Choose a reason for hiding this comment

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

what does that mean?

* @param[in] data Pointer where the received data should be stored.
* May be `NULL` if `len == 0`.
* @param[in] len Maximum space available at @p data.
* @param[in] proto Protocol to use in the packet send, in case
Copy link
Contributor

Choose a reason for hiding this comment

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

send -> sent or "to be sent"

* @param[in] proto Protocol to use in the packet send, in case
* `sock == NULL`. If `sock != NULL` this parameter will be
* ignored.
* @param[in] remote Remote end point for the send data.
Copy link
Contributor

Choose a reason for hiding this comment

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

"to be sent" as above

* sock_ip_ep_t::family may be AF_UNSPEC, if local
* end point of @p sock provides this information.
*
* @note Function blocks until packet is handed to the stack.
Copy link
Contributor

Choose a reason for hiding this comment

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

?

*
* @note Function blocks until packet is handed to the stack.
*
* @return The number of bytes send on success.
Copy link
Contributor

Choose a reason for hiding this comment

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

send -> sent

Copy link
Member

Choose a reason for hiding this comment

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

If anyone is going to fix this: there are various more occurrences of this typo all over the place.

typedef struct _sock_tl_ep sock_tcp_ep_t; /**< An end point for a TCP sock object */

/**
* @brief Implementation-specific type of a TCP sock object
Copy link
Contributor

Choose a reason for hiding this comment

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

pls drop implementation specific

@kaspar030 kaspar030 merged commit 526917b into RIOT-OS:master Sep 27, 2016
@cgundogan
Copy link
Member

cgundogan commented Sep 27, 2016

@kaspar030 accidently hit the merge button? 🍻

@kaspar030
Copy link
Contributor

No, I realized that the API has been thoroughly discussed and my only complaints are either doxygen typos or require a longer (out-of-scope discussion) of the documentation style as a whole.

@cgundogan
Copy link
Member

I am refering to the commit list. 18 commits marked as fixup (:

@kaspar030
Copy link
Contributor

Why does this happen to me? :)

What do we do?

@cgundogan
Copy link
Member

What do we do?

simple solution. just bring some beer for the next hack'n'ack (:

@kaspar030
Copy link
Contributor

simple solution. just bring some beer for the next hack'n'ack (:

Will do. Sorry @everyone for messing up our beautiful commit history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: network Area: Networking Community: Hack'n'ACK candidate This PR is a candidate for review and discussion during one of RIOT's monthly Hack'n'ACK parties 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