Skip to content

Commit

Permalink
mptcp: Implement basic path manager
Browse files Browse the repository at this point in the history
Two features: 1) After an incoming connection is fully established
announce a second local address and 2) When an outgoing connection
is fully established, and an ADD_ADDR option has been received,
initiate a secondary subflow to that address.

The second local address may be configured through the
net.mptcp.pm.announce_addr sysctl variable. If configured
then for listeners this address will be announced with the
ADD_ADDR option, and for clients the local socket will be bound
to this address (or INADDR_ANY if not specified).

Co-developed-by: Matthieu Baerts <[email protected]>
Signed-off-by: Matthieu Baerts <[email protected]>
Signed-off-by: Peter Krystad <[email protected]>
  • Loading branch information
Peter Krystad authored and jenkins-tessares committed Oct 15, 2019
1 parent 9c140e8 commit 62be47f
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 1 deletion.
2 changes: 1 addition & 1 deletion net/mptcp/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MPTCP) += mptcp.o

mptcp-y := protocol.o subflow.o options.o token.o crypto.o pm.o ctrl.o diag.o mib.o
mptcp-y := protocol.o subflow.o options.o token.o crypto.o pm.o ctrl.o diag.o mib.o basic.o
247 changes: 247 additions & 0 deletions net/mptcp/basic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0
/* Multipath TCP
*
* Copyright (c) 2019, Intel Corporation.
*/
#include <linux/inet.h>
#include <linux/kernel.h>
#include <net/tcp.h>
#include <net/netns/generic.h>
#include <net/mptcp.h>
#include "protocol.h"

static int basic_pernet_id;

struct basic_pernet {
struct ctl_table_header *ctl_table_hdr;

union {
struct in_addr announce_v4_addr;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr announce_v6_addr;
#endif
};
u8 has_announce_v4 : 1,
has_announce_v6 : 1;
};

static struct workqueue_struct *basic_wq;
static void announce_addr_worker(struct work_struct *work);
static void create_subflow_worker(struct work_struct *work);

static int parse_addr(struct basic_pernet *pernet, const char *addr)
{
#if IS_ENABLED(CONFIG_IPV6)
if (in6_pton(addr, -1, (u8 *)&pernet->announce_v6_addr.s6_addr, '\0',
NULL) > 0) {
pernet->has_announce_v4 = 0;
pernet->has_announce_v6 = 1;
return 0;
}
#endif

if (in4_pton(addr, -1, (u8 *)&pernet->announce_v4_addr.s_addr, '\0',
NULL) > 0) {
pernet->has_announce_v4 = 1;
pernet->has_announce_v6 = 0;
return 0;
}

pernet->has_announce_v4 = 0;
pernet->has_announce_v6 = 0;

return -1;
}

static int proc_parse_addr(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct net *net = current->nsproxy->net_ns;
struct basic_pernet *pernet = net_generic(net, basic_pernet_id);
struct ctl_table tbl;

char *none = "none";
char tmp[INET6_ADDRSTRLEN] = { 0 };
int ret;

memset(&tbl, 0, sizeof(struct ctl_table));

if (write) {
tbl.data = tmp;
tbl.maxlen = sizeof(tmp);
} else {
if (pernet->has_announce_v4) {
snprintf(tmp, INET_ADDRSTRLEN, "%pI4",
&pernet->announce_v4_addr);
tbl.data = tmp;
#if IS_ENABLED(CONFIG_IPV6)
} else if (pernet->has_announce_v6) {
snprintf(tmp, INET6_ADDRSTRLEN, "%pI6c",
&pernet->announce_v6_addr);
tbl.data = tmp;
#endif
} else {
tbl.data = none;
}
tbl.maxlen = strlen(tbl.data);
}

ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
if (write && ret == 0) {
/* "none" string: we want to remove it */
if (strncmp(none, tmp, 5) == 0) {
pernet->has_announce_v4 = 0;
pernet->has_announce_v6 = 0;
} else if (parse_addr(pernet, tmp) < 0) {
ret = -EINVAL;
}
}

return ret;
}

static struct ctl_table basic_sysctl_table[] = {
{
.procname = "announce_addr",
.maxlen = sizeof(char) * (INET6_ADDRSTRLEN),
.mode = 0644,
.proc_handler = proc_parse_addr
},
{}
};

static int basic_pernet_create_table(struct net *net,
struct basic_pernet *pernet)
{
struct ctl_table *table;
struct ctl_table_header *hdr;

table = basic_sysctl_table;
if (!net_eq(net, &init_net)) {
table = kmemdup(table, sizeof(basic_sysctl_table),
GFP_KERNEL);
if (!table)
goto err_alloc;
}

hdr = register_net_sysctl(net, "net/mptcp/pm", table);
if (!hdr)
goto err_reg;

pernet->ctl_table_hdr = hdr;

return 0;

err_reg:
if (!net_eq(net, &init_net))
kfree(table);
err_alloc:
return -ENOMEM;
}

static int __net_init basic_init_net(struct net *net)
{
struct basic_pernet *pernet = net_generic(net, basic_pernet_id);
int ret;

ret = basic_pernet_create_table(net, pernet);
if (ret < 0)
return ret;

return 0;
}

static void __net_exit basic_exit_net(struct net *net)
{
struct basic_pernet *pernet = net_generic(net, basic_pernet_id);
struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;

unregister_net_sysctl_table(pernet->ctl_table_hdr);

/* Note: the callback will only be called per extra netns */
kfree(table);
}

static struct pernet_operations basic_pernet_ops = {
.init = basic_init_net,
.exit = basic_exit_net,
.id = &basic_pernet_id,
.size = sizeof(struct basic_pernet),
};

void mptcp_basic_init(void)
{
if (register_pernet_subsys(&basic_pernet_ops) < 0)
panic("Failed to register MPTCP PM pernet subsystem.\n");

basic_wq = alloc_workqueue("basic_wq",
WQ_UNBOUND | WQ_MEM_RECLAIM, 8);
if (!basic_wq)
panic("Failed to allocate workqueue");
}

static void announce_addr_worker(struct work_struct *work)
{
struct mptcp_pm_data *pm = container_of(work, struct mptcp_pm_data,
addr_work);
struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm);
struct basic_pernet *pernet;

pernet = net_generic(sock_net((struct sock *)msk), basic_pernet_id);

if (pernet->has_announce_v4)
mptcp_pm_announce_addr(pm->token, 1, AF_INET,
&pernet->announce_v4_addr);
sock_put((struct sock *)msk);
}

static void create_subflow_worker(struct work_struct *work)
{
struct mptcp_pm_data *pm = container_of(work, struct mptcp_pm_data,
subflow_work);
struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm);
struct basic_pernet *pernet;

pernet = net_generic(sock_net((struct sock *)msk), basic_pernet_id);

if (pernet->has_announce_v4) {
mptcp_pm_create_subflow(pm->token, pm->remote_id, AF_INET,
&pernet->announce_v4_addr);
} else {
mptcp_pm_create_subflow(pm->token, pm->remote_id, 0, NULL);
}
sock_put((struct sock *)msk);
}

void mptcp_basic_new_connection(struct mptcp_pm_data *pm)
{
struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm);

if (pm->server_side) {
INIT_WORK(&pm->addr_work, announce_addr_worker);
if (queue_work(basic_wq, &pm->addr_work))
sock_hold((struct sock *)msk);
}
}

void mptcp_basic_fully_established(struct mptcp_pm_data *pm)
{
struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm);

if (!pm->server_side && !pm->fully_established && pm->remote_valid) {
INIT_WORK(&pm->subflow_work, create_subflow_worker);
if (queue_work(basic_wq, &pm->subflow_work))
sock_hold((struct sock *)msk);
}
}

void mptcp_basic_add_addr(struct mptcp_pm_data *pm)
{
struct mptcp_sock *msk = container_of(pm, struct mptcp_sock, pm);

if (!pm->server_side && !pm->remote_valid && pm->fully_established) {
INIT_WORK(&pm->subflow_work, create_subflow_worker);
if (queue_work(basic_wq, &pm->subflow_work))
sock_hold((struct sock *)msk);
}
}
7 changes: 7 additions & 0 deletions net/mptcp/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ void mptcp_pm_new_connection(struct mptcp_sock *msk, int server_side)

pm->server_side = server_side;
pm->token = msk->token;

mptcp_basic_new_connection(pm);
}

void mptcp_pm_fully_established(struct mptcp_sock *msk)
Expand All @@ -110,6 +112,8 @@ void mptcp_pm_fully_established(struct mptcp_sock *msk)

pr_debug("msk=%p", msk);

mptcp_basic_fully_established(pm);

pm->fully_established = 1;
}

Expand Down Expand Up @@ -138,6 +142,9 @@ void mptcp_pm_add_addr(struct mptcp_sock *msk, const struct in_addr *addr,
pm->remote_addr.s_addr = addr->s_addr;
pm->remote_id = id;
pm->remote_family = AF_INET;

mptcp_basic_add_addr(pm);

pm->remote_valid = 1;
}

Expand Down
1 change: 1 addition & 0 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,7 @@ void mptcp_proto_init(void)
panic("Failed to allocate MPTCP pcpu counter\n");

mptcp_subflow_init();
mptcp_basic_init();

if (proto_register(&mptcp_prot, 1) != 0)
panic("Failed to register MPTCP proto.\n");
Expand Down
5 changes: 5 additions & 0 deletions net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ int mptcp_pm_addr_signal(struct mptcp_sock *msk, u8 *id,
int mptcp_pm_get_local_id(struct request_sock *req, struct sock *sk,
const struct sk_buff *skb);

void mptcp_basic_init(void);
void mptcp_basic_new_connection(struct mptcp_pm_data *pm);
void mptcp_basic_fully_established(struct mptcp_pm_data *pm);
void mptcp_basic_add_addr(struct mptcp_pm_data *pm);

static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
{
return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP);
Expand Down

0 comments on commit 62be47f

Please sign in to comment.