120 lines
2.3 KiB
C
120 lines
2.3 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Multipath TCP
|
||
|
*
|
||
|
* Copyright (c) 2017 - 2019, Intel Corporation.
|
||
|
*/
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <net/sock.h>
|
||
|
#include <net/inet_common.h>
|
||
|
#include <net/inet_hashtables.h>
|
||
|
#include <net/protocol.h>
|
||
|
#include <net/tcp.h>
|
||
|
#include <net/mptcp.h>
|
||
|
#include "protocol.h"
|
||
|
|
||
|
int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock)
|
||
|
{
|
||
|
struct mptcp_subflow_context *subflow;
|
||
|
struct net *net = sock_net(sk);
|
||
|
struct socket *sf;
|
||
|
int err;
|
||
|
|
||
|
err = sock_create_kern(net, PF_INET, SOCK_STREAM, IPPROTO_TCP, &sf);
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
lock_sock(sf->sk);
|
||
|
|
||
|
/* kernel sockets do not by default acquire net ref, but TCP timer
|
||
|
* needs it.
|
||
|
*/
|
||
|
sf->sk->sk_net_refcnt = 1;
|
||
|
get_net(net);
|
||
|
this_cpu_add(*net->core.sock_inuse, 1);
|
||
|
err = tcp_set_ulp(sf->sk, "mptcp");
|
||
|
release_sock(sf->sk);
|
||
|
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
subflow = mptcp_subflow_ctx(sf->sk);
|
||
|
pr_debug("subflow=%p", subflow);
|
||
|
|
||
|
*new_sock = sf;
|
||
|
subflow->conn = sk;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct mptcp_subflow_context *subflow_create_ctx(struct sock *sk,
|
||
|
gfp_t priority)
|
||
|
{
|
||
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
||
|
struct mptcp_subflow_context *ctx;
|
||
|
|
||
|
ctx = kzalloc(sizeof(*ctx), priority);
|
||
|
if (!ctx)
|
||
|
return NULL;
|
||
|
|
||
|
rcu_assign_pointer(icsk->icsk_ulp_data, ctx);
|
||
|
|
||
|
pr_debug("subflow=%p", ctx);
|
||
|
|
||
|
ctx->tcp_sock = sk;
|
||
|
|
||
|
return ctx;
|
||
|
}
|
||
|
|
||
|
static int subflow_ulp_init(struct sock *sk)
|
||
|
{
|
||
|
struct mptcp_subflow_context *ctx;
|
||
|
struct tcp_sock *tp = tcp_sk(sk);
|
||
|
int err = 0;
|
||
|
|
||
|
/* disallow attaching ULP to a socket unless it has been
|
||
|
* created with sock_create_kern()
|
||
|
*/
|
||
|
if (!sk->sk_kern_sock) {
|
||
|
err = -EOPNOTSUPP;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ctx = subflow_create_ctx(sk, GFP_KERNEL);
|
||
|
if (!ctx) {
|
||
|
err = -ENOMEM;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
pr_debug("subflow=%p, family=%d", ctx, sk->sk_family);
|
||
|
|
||
|
tp->is_mptcp = 1;
|
||
|
out:
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static void subflow_ulp_release(struct sock *sk)
|
||
|
{
|
||
|
struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(sk);
|
||
|
|
||
|
if (!ctx)
|
||
|
return;
|
||
|
|
||
|
kfree_rcu(ctx, rcu);
|
||
|
}
|
||
|
|
||
|
static struct tcp_ulp_ops subflow_ulp_ops __read_mostly = {
|
||
|
.name = "mptcp",
|
||
|
.owner = THIS_MODULE,
|
||
|
.init = subflow_ulp_init,
|
||
|
.release = subflow_ulp_release,
|
||
|
};
|
||
|
|
||
|
void mptcp_subflow_init(void)
|
||
|
{
|
||
|
if (tcp_register_ulp(&subflow_ulp_ops) != 0)
|
||
|
panic("MPTCP: failed to register subflows to ULP\n");
|
||
|
}
|