blob: 10147d5e3cca12bf3868462663cad67c43361b1c [file] [log] [blame]
#include <linux/module.h>
#include <net/mptcp.h>
#include <net/mptcp_v4.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/mptcp_v6.h>
#endif
struct ndiffports_priv {
/* Worker struct for subflow establishment */
struct work_struct subflow_work;
struct mptcp_cb *mpcb;
};
static int num_subflows __read_mostly = 2;
module_param(num_subflows, int, 0644);
MODULE_PARM_DESC(num_subflows, "choose the number of subflows per MPTCP connection");
/**
* Create all new subflows, by doing calls to mptcp_initX_subsockets
*
* This function uses a goto next_subflow, to allow releasing the lock between
* new subflows and giving other processes a chance to do some work on the
* socket and potentially finishing the communication.
**/
static void create_subflow_worker(struct work_struct *work)
{
const struct ndiffports_priv *pm_priv = container_of(work,
struct ndiffports_priv,
subflow_work);
struct mptcp_cb *mpcb = pm_priv->mpcb;
struct sock *meta_sk = mpcb->meta_sk;
int iter = 0;
next_subflow:
if (iter) {
release_sock(meta_sk);
mutex_unlock(&mpcb->mpcb_mutex);
cond_resched();
}
mutex_lock(&mpcb->mpcb_mutex);
lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING);
iter++;
if (sock_flag(meta_sk, SOCK_DEAD))
goto exit;
if (mpcb->master_sk &&
!tcp_sk(mpcb->master_sk)->mptcp->fully_established)
goto exit;
if (num_subflows > iter && num_subflows > mpcb->cnt_subflows) {
if (meta_sk->sk_family == AF_INET ||
mptcp_v6_is_v4_mapped(meta_sk)) {
struct mptcp_loc4 loc;
struct mptcp_rem4 rem;
loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr;
loc.loc4_id = 0;
loc.low_prio = 0;
if (mpcb->master_sk)
loc.if_idx = mpcb->master_sk->sk_bound_dev_if;
else
loc.if_idx = 0;
rem.addr.s_addr = inet_sk(meta_sk)->inet_daddr;
rem.port = inet_sk(meta_sk)->inet_dport;
rem.rem4_id = 0; /* Default 0 */
mptcp_init4_subsockets(meta_sk, &loc, &rem);
} else {
#if IS_ENABLED(CONFIG_IPV6)
struct mptcp_loc6 loc;
struct mptcp_rem6 rem;
loc.addr = inet6_sk(meta_sk)->saddr;
loc.loc6_id = 0;
loc.low_prio = 0;
if (mpcb->master_sk)
loc.if_idx = mpcb->master_sk->sk_bound_dev_if;
else
loc.if_idx = 0;
rem.addr = meta_sk->sk_v6_daddr;
rem.port = inet_sk(meta_sk)->inet_dport;
rem.rem6_id = 0; /* Default 0 */
mptcp_init6_subsockets(meta_sk, &loc, &rem);
#endif
}
goto next_subflow;
}
exit:
release_sock(meta_sk);
mutex_unlock(&mpcb->mpcb_mutex);
sock_put(meta_sk);
}
static void ndiffports_new_session(const struct sock *meta_sk)
{
struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb;
struct ndiffports_priv *fmp = (struct ndiffports_priv *)&mpcb->mptcp_pm[0];
/* Initialize workqueue-struct */
INIT_WORK(&fmp->subflow_work, create_subflow_worker);
fmp->mpcb = mpcb;
}
static void ndiffports_create_subflows(struct sock *meta_sk)
{
const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb;
struct ndiffports_priv *pm_priv = (struct ndiffports_priv *)&mpcb->mptcp_pm[0];
if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv ||
mpcb->send_infinite_mapping ||
mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD))
return;
if (!work_pending(&pm_priv->subflow_work)) {
sock_hold(meta_sk);
queue_work(mptcp_wq, &pm_priv->subflow_work);
}
}
static int ndiffports_get_local_id(sa_family_t family, union inet_addr *addr,
struct net *net, bool *low_prio)
{
return 0;
}
static struct mptcp_pm_ops ndiffports __read_mostly = {
.new_session = ndiffports_new_session,
.fully_established = ndiffports_create_subflows,
.get_local_id = ndiffports_get_local_id,
.name = "ndiffports",
.owner = THIS_MODULE,
};
/* General initialization of MPTCP_PM */
static int __init ndiffports_register(void)
{
BUILD_BUG_ON(sizeof(struct ndiffports_priv) > MPTCP_PM_SIZE);
if (mptcp_register_path_manager(&ndiffports))
goto exit;
return 0;
exit:
return -1;
}
static void ndiffports_unregister(void)
{
mptcp_unregister_path_manager(&ndiffports);
}
module_init(ndiffports_register);
module_exit(ndiffports_unregister);
MODULE_AUTHOR("Christoph Paasch");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("NDIFF-PORTS MPTCP");
MODULE_VERSION("0.88");