Skip to content

(NetFlow: Niche module) Add backlog limit for a given flow::net_flow::Server_socket / Lower limit for back-logged DATA packets on not-yet-accepted such sockets. #113

@ygoldfeld

Description

@ygoldfeld

As noted in Flow's top README.md, its NetFlow module (flow::net_flow) is (unlike the rest of Flow which has general usability and has been actively developed for years) of niche interest and present for demo/historical reasons. However, even in that capacity, this TCP(-like)-over-UDP implementation should follow established knowledge about TCP attack vectors. There are places where we currently do not. Two of these are as follows.

Not too differently from TCP: One calls Node::listen(), yielding a Server_socket; at which point an opposing node can Node::[sync_]connect() to the resulting NetFlow-port (not UDP/IP port; the entire Node is using 1 UDP/IP port). Really this sends a SYN; server responds with SYN_ACK and expects SYN_ACK_ACK back. Server-side one must call Server_socket::[sync_]accept() (which returns a Peer_socket) to do actual transmission along the resulting conceptual connection.

However, if the server application does not call Server_socket::[sync_]accept(), and meanwhile client(s) bombard it with connection attempts, it is possible make server application use resources in unbounded fashion.

Inside the code, for each Server_socket, there is a set of SYN_RCVD-state Peer_sockets (SYN received, SYN_ACK sent, no SYN_ACK_ACK received); and a set of ESTABLISHED -- but not ->*accept()ed -- Peer_sockets.

Should at least limit the size (perhaps the combined size) of these two sets. E.g., TCP listen() universally takes an int backlog argument and will refuse connections once it is reached. We can/should, similarly, response with RST in this case. The backlog limit could be configurable at runtime or at least hard-coded; or configurable but defaulting to a hard-coded limit.

--- related ---

While in the aforementioned SYN_RCVD-state (SYN received, SYN_ACK sent, awaiting SYN_ACK_ACK before proceeding to ESTABLISHED), it is possible to receive DATA packets. In legit scenarios the SYN_ACK_ACK could have been lost and not yet successfully retransmitted; or DATA packet(s) could have arrived out-of-order. With an explanatory inline comment, we explain that due to the lack of cumulative (only selective) ACKing we had to choose between just dropping these or keeping them until ESTABLISHED, then "replaying" having received them. There is a limit to how many are kept before dropping any subsequent ones; the config setting for max regular buffer size (as used during ESTABLISHED, if the user is not taking them off the in-queue with ...receive()s) is applied to the total byte count of these accumulated DATAs.

This max-buffer-size is, by default, large -- which is generally considered acceptable with an ESTABLISHED connection. However reusing the same limit in SYN_RCVD, before the full handshake (with a security token et al) is completed, makes SYN floods easier.

Should default to a separate, smaller limit. The limit could be made configurable still; or not.

Priority: sooner is better.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions