Skip to content

Commit

Permalink
Support listener events (#304)
Browse files Browse the repository at this point in the history
* events: add listener created/closed support

These events have been added to the upstream kernel a while ago, see
commit f8c9dfbd875b ("mptcp: add pm listener events") in the kernel.

To better explain why these events are useful, better to quote [1]:

  MPTCP for Linux, when not using the in-kernel PM, depends on the
  userspace PM to create extra listening sockets before announcing
  addresses and ports. Let's call these "PM listeners".

  With the existing MPTCP netlink events, a userspace PM can create PM
  listeners at startup time, or in response to an incoming connection.
  Creating sockets in response to connections is not optimal: ADD_ADDRs
  can't be sent until the sockets are created and listen()ed, and if all
  connections are closed then it may not be clear to the userspace PM
  daemon that PM listener sockets should be cleaned up.

  With the addition of MPTCP netlink events for listening socket close &
  create, PM listening sockets can be managed based on application
  activity.

These new events are then now handled by mptcpd, and plugins can be
notified via two new hooks:

 - listener_created(laddr, pm)
 - listener_closed(laddr, pm)

Link: multipath-tcp/mptcp_net-next#313 [1]
Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>

* plugins: sspi: add hooks for listener events

Just to serve as an example, similar to what is done for other events
like ADD_ADDR, RM_ADDR, and MP_PRIO.

Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>

* tests: lib: support new listener events

Just the structure to be able to test the new hooks.

Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>

* tests: plugins: add listener event support

Adding new hooks, checking the laddr value -- similar to what is done
when a new connection is created -- and incrementing the linked counter,
like the other hooks.

Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>

* tests: validating new listener hooks

Tests 1 and 2 are imitating the server side: a new listening socket is
created. Not in the other ones, imitating the client side.

Also validate the null plugin.

Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>

---------

Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>
  • Loading branch information
matttbe authored Oct 26, 2024
1 parent 269f10a commit 824ff8c
Show file tree
Hide file tree
Showing 14 changed files with 318 additions and 4 deletions.
20 changes: 20 additions & 0 deletions include/mptcpd/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,26 @@ struct mptcpd_plugin_ops
struct sockaddr const *raddr,
bool backup,
struct mptcpd_pm *pm);

/**
* @brief New MPTCP listener socket has been created.
*
* @param[in] laddr Local address information.
* @param[in] pm Opaque pointer to mptcpd path
* manager object.
*/
void (*listener_created)(struct sockaddr const *laddr,
struct mptcpd_pm *pm);

/**
* @brief MPTCP listener socket has been closed.
*
* @param[in] laddr Local address information.
* @param[in] pm Opaque pointer to mptcpd path
* manager object.
*/
void (*listener_closed)(struct sockaddr const *laddr,
struct mptcpd_pm *pm);
///@}

// --------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions include/mptcpd/private/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,28 @@ MPTCPD_API void mptcpd_plugin_subflow_priority(
struct sockaddr const *raddr,
bool backup,
struct mptcpd_pm *pm);

/**
* @brief Notify plugin of MPTCP listener creation.
*
* @param[in] laddr Local address information.
* @param[in] pm Opaque pointer to mptcpd path manager object.
*/
MPTCPD_API void mptcpd_plugin_listener_created(
char const *name,
struct sockaddr const *laddr,
struct mptcpd_pm *pm);

/**
* @brief Notify plugin of MPTCP listener closure.
*
* @param[in] laddr Local address information.
* @param[in] pm Opaque pointer to mptcpd path manager object.
*/
MPTCPD_API void mptcpd_plugin_listener_closed(
char const *name,
struct sockaddr const *laddr,
struct mptcpd_pm *pm);
///@}

/**
Expand Down
22 changes: 22 additions & 0 deletions lib/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,28 @@ void mptcpd_plugin_subflow_priority(mptcpd_token_t token,

}

void mptcpd_plugin_listener_created(char const *name,
struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
struct mptcpd_plugin_ops const *const ops = name_to_ops(name);

if (ops && ops->listener_created)
ops->listener_created(laddr, pm);

}

void mptcpd_plugin_listener_closed(char const *name,
struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
struct mptcpd_plugin_ops const *const ops = name_to_ops(name);

if (ops && ops->listener_closed)
ops->listener_closed(laddr, pm);

}

// ----------------------------------------------------------------
// Network Monitoring Related Plugin Operation Callback Invocation
// ----------------------------------------------------------------
Expand Down
27 changes: 26 additions & 1 deletion plugins/path_managers/sspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,29 @@ static void sspi_subflow_priority(mptcpd_token_t token,
*/
}

static void sspi_listener_created(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) laddr;
(void) pm;

/*
The sspi plugin doesn't do anything with newly created listener
sockets.
*/
}

static void sspi_listener_closed(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) laddr;
(void) pm;

/*
The sspi plugin doesn't do anything with closed listener sockets.
*/
}

static struct mptcpd_plugin_ops const pm_ops = {
.new_connection = sspi_new_connection,
.connection_established = sspi_connection_established,
Expand All @@ -789,7 +812,9 @@ static struct mptcpd_plugin_ops const pm_ops = {
.address_removed = sspi_address_removed,
.new_subflow = sspi_new_subflow,
.subflow_closed = sspi_subflow_closed,
.subflow_priority = sspi_subflow_priority
.subflow_priority = sspi_subflow_priority,
.listener_created = sspi_listener_created,
.listener_closed = sspi_listener_closed
};

static int sspi_init(struct mptcpd_pm *pm)
Expand Down
93 changes: 93 additions & 0 deletions src/path_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,89 @@ static void handle_priority_changed(struct pm_event_attrs const *attrs,
pm);
}

#ifdef HAVE_UPSTREAM_KERNEL
/**
* @brief Retrieve listener event attributes.
*
* All listener events have the same payload attributes. Share
* attribute validation and addr initialization in one location.
*
* @param[in] attrs Generic netlink MPTCP subflow event message.
* @param[in,out] laddr MPTCP subflow local address and port.
*
* @return @c true on success, @c false otherwise.
*/
static bool handle_listener(struct pm_event_attrs const *attrs,
struct sockaddr_storage *laddr)
{
assert(attrs != NULL);
assert(laddr != NULL);

/*
Payload:
Address family
Local address
Local port
*/
if (!(attrs->laddr4 || attrs->laddr6)
|| !attrs->local_port) {
l_error("Required MPTCP_EVENT_LISTENER_*"
"message attributes are missing.");

return false;
}

if (!mptcpd_sockaddr_storage_init(attrs->laddr4,
attrs->laddr6,
*attrs->local_port,
laddr)) {
l_error("Unable to initialize address information");

return false;
}

return true;
}

static void handle_listener_created(struct pm_event_attrs const *attrs,
struct mptcpd_pm *pm)
{
/*
Payload:
Address family
Local address
Local port
*/
struct sockaddr_storage laddr;

if (!handle_listener(attrs, &laddr))
return;

static char const *const pm_name = NULL;

mptcpd_plugin_listener_created(pm_name, (struct sockaddr *) &laddr, pm);
}

static void handle_listener_closed(struct pm_event_attrs const *attrs,
struct mptcpd_pm *pm)
{
/*
Payload:
Address family
Local address
Local port
*/
struct sockaddr_storage laddr;

if (!handle_listener(attrs, &laddr))
return;

static char const *const pm_name = NULL;

mptcpd_plugin_listener_closed(pm_name, (struct sockaddr *) &laddr, pm);
}
#endif // HAVE_UPSTREAM_KERNEL

static void handle_mptcp_event(struct l_genl_msg *msg, void *user_data)
{
int const cmd = l_genl_msg_get_command(msg);
Expand Down Expand Up @@ -583,6 +666,16 @@ static void handle_mptcp_event(struct l_genl_msg *msg, void *user_data)
handle_priority_changed(&attrs, pm);
break;

#ifdef HAVE_UPSTREAM_KERNEL
case MPTCP_EVENT_LISTENER_CREATED:
handle_listener_created(&attrs, pm);
break;

case MPTCP_EVENT_LISTENER_CLOSED:
handle_listener_closed(&attrs, pm);
break;
#endif // HAVE_UPSTREAM_KERNEL

default:
l_error("Unhandled MPTCP event: %d", cmd);
break;
Expand Down
6 changes: 6 additions & 0 deletions tests/lib/call_count.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ void call_count_reset(struct plugin_call_count *p)
p->new_subflow = 0;
p->subflow_closed = 0;
p->subflow_priority = 0;
p->listener_created = 0;
p->listener_closed = 0;
p->new_interface = 0;
p->update_interface = 0;
p->delete_interface = 0;
Expand All @@ -39,6 +41,8 @@ bool call_count_all_positive(struct plugin_call_count const *p)
&& p->new_subflow >= 0
&& p->subflow_closed >= 0
&& p->subflow_priority >= 0
&& p->listener_created >= 0
&& p->listener_closed >= 0
&& p->new_interface >= 0
&& p->update_interface >= 0
&& p->delete_interface >= 0
Expand Down Expand Up @@ -71,6 +75,8 @@ bool call_count_is_equal(struct plugin_call_count const *lhs,
&& lhs->new_subflow == rhs->new_subflow
&& lhs->subflow_closed == rhs->subflow_closed
&& lhs->subflow_priority == rhs->subflow_priority
&& lhs->listener_created == rhs->listener_created
&& lhs->listener_closed == rhs->listener_closed
&& lhs->new_interface == rhs->new_interface
&& lhs->update_interface == rhs->update_interface
&& lhs->delete_interface == rhs->delete_interface
Expand Down
10 changes: 10 additions & 0 deletions tests/lib/call_plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ void call_plugin_ops(struct plugin_call_count const *count,
args->backup,
args->pm);

for (int i = 0; i < count->listener_created; ++i)
mptcpd_plugin_listener_created(args->name,
args->laddr,
args->pm);

for (int i = 0; i < count->listener_closed; ++i)
mptcpd_plugin_listener_closed(args->name,
args->laddr,
args->pm);

for (int i = 0; i < count->connection_closed; ++i)
mptcpd_plugin_connection_closed(args->token, args->pm);

Expand Down
12 changes: 10 additions & 2 deletions tests/lib/test-plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct plugin_call_count
int new_subflow;
int subflow_closed;
int subflow_priority;
int listener_created;
int listener_closed;
int new_interface;
int update_interface;
int delete_interface;
Expand Down Expand Up @@ -97,7 +99,9 @@ static struct plugin_call_count const test_count_1 = {
.address_removed = 0,
.new_subflow = 0,
.subflow_closed = 0,
.subflow_priority = 0
.subflow_priority = 0,
.listener_created = 1,
.listener_closed = 1
};

static struct plugin_call_count const test_count_2 = {
Expand All @@ -109,6 +113,8 @@ static struct plugin_call_count const test_count_2 = {
.new_subflow = 1,
.subflow_closed = 1,
.subflow_priority = 1,
.listener_created = 1,
.listener_closed = 1,
.new_interface = 1,
.update_interface = 2,
.delete_interface = 1,
Expand All @@ -124,7 +130,9 @@ static struct plugin_call_count const test_count_4 = {
.address_removed = 1,
.new_subflow = 0,
.subflow_closed = 0,
.subflow_priority = 0
.subflow_priority = 0,
.listener_created = 0,
.listener_closed = 0
};
///@}

Expand Down
16 changes: 16 additions & 0 deletions tests/plugins/noop/noop.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ static void plugin_noop_subflow_priority(mptcpd_token_t token,
(void) pm;
}

static void plugin_noop_listener_created(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) laddr;
(void) pm;
}

static void plugin_noop_listener_closed(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) laddr;
(void) pm;
}

void plugin_noop_new_interface(struct mptcpd_interface const *i,
struct mptcpd_pm *pm)
{
Expand Down Expand Up @@ -161,6 +175,8 @@ static struct mptcpd_plugin_ops const pm_ops = {
.new_subflow = plugin_noop_new_subflow,
.subflow_closed = plugin_noop_subflow_closed,
.subflow_priority = plugin_noop_subflow_priority,
.listener_created = plugin_noop_listener_created,
.listener_closed = plugin_noop_listener_closed,
.new_interface = plugin_noop_new_interface,
.update_interface = plugin_noop_update_interface,
.delete_interface = plugin_noop_delete_interface,
Expand Down
28 changes: 27 additions & 1 deletion tests/plugins/priority/one.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ static void plugin_one_subflow_priority(mptcpd_token_t token,
++call_count.subflow_priority;
}

static void plugin_one_listener_created(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) pm;

assert(laddr != NULL);
assert(sockaddr_is_equal(laddr, local_addr));

++call_count.listener_created;
}

static void plugin_one_listener_closed(struct sockaddr const *laddr,
struct mptcpd_pm *pm)
{
(void) pm;

assert(laddr != NULL);
assert(sockaddr_is_equal(laddr, local_addr));

++call_count.listener_closed;
}

static struct mptcpd_plugin_ops const pm_ops = {
.new_connection = plugin_one_new_connection,
.connection_established = plugin_one_connection_established,
Expand All @@ -166,7 +188,9 @@ static struct mptcpd_plugin_ops const pm_ops = {
.address_removed = plugin_one_address_removed,
.new_subflow = plugin_one_new_subflow,
.subflow_closed = plugin_one_subflow_closed,
.subflow_priority = plugin_one_subflow_priority
.subflow_priority = plugin_one_subflow_priority,
.listener_created = plugin_one_listener_created,
.listener_closed = plugin_one_listener_closed
};

static int plugin_one_init(struct mptcpd_pm *pm)
Expand Down Expand Up @@ -204,6 +228,8 @@ static void plugin_one_exit(struct mptcpd_pm *pm)
.new_subflow = test_count_1.new_subflow * 2,
.subflow_closed = test_count_1.subflow_closed * 2,
.subflow_priority = test_count_1.subflow_priority * 2,
.listener_created = test_count_1.listener_created * 2,
.listener_closed = test_count_1.listener_closed * 2,
};

assert(call_count_is_sane(&call_count));
Expand Down
Loading

0 comments on commit 824ff8c

Please sign in to comment.