Commit | Line | Data |
---|---|---|
1cac41cb MB |
1 | #include <linux/module.h> |
2 | ||
3 | #include <net/mptcp.h> | |
4 | #include <net/mptcp_v4.h> | |
5 | ||
6 | #if IS_ENABLED(CONFIG_IPV6) | |
7 | #include <net/mptcp_v6.h> | |
8 | #endif | |
9 | ||
10 | struct ndiffports_priv { | |
11 | /* Worker struct for subflow establishment */ | |
12 | struct work_struct subflow_work; | |
13 | ||
14 | struct mptcp_cb *mpcb; | |
15 | }; | |
16 | ||
17 | static int num_subflows __read_mostly = 2; | |
18 | module_param(num_subflows, int, 0644); | |
19 | MODULE_PARM_DESC(num_subflows, "choose the number of subflows per MPTCP connection"); | |
20 | ||
21 | /** | |
22 | * Create all new subflows, by doing calls to mptcp_initX_subsockets | |
23 | * | |
24 | * This function uses a goto next_subflow, to allow releasing the lock between | |
25 | * new subflows and giving other processes a chance to do some work on the | |
26 | * socket and potentially finishing the communication. | |
27 | **/ | |
28 | static void create_subflow_worker(struct work_struct *work) | |
29 | { | |
30 | const struct ndiffports_priv *pm_priv = container_of(work, | |
31 | struct ndiffports_priv, | |
32 | subflow_work); | |
33 | struct mptcp_cb *mpcb = pm_priv->mpcb; | |
34 | struct sock *meta_sk = mpcb->meta_sk; | |
35 | int iter = 0; | |
36 | ||
37 | next_subflow: | |
38 | if (iter) { | |
39 | release_sock(meta_sk); | |
40 | mutex_unlock(&mpcb->mpcb_mutex); | |
41 | ||
42 | cond_resched(); | |
43 | } | |
44 | mutex_lock(&mpcb->mpcb_mutex); | |
45 | lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); | |
46 | ||
47 | iter++; | |
48 | ||
49 | if (sock_flag(meta_sk, SOCK_DEAD)) | |
50 | goto exit; | |
51 | ||
52 | if (mpcb->master_sk && | |
53 | !tcp_sk(mpcb->master_sk)->mptcp->fully_established) | |
54 | goto exit; | |
55 | ||
56 | if (num_subflows > iter && num_subflows > mpcb->cnt_subflows) { | |
57 | if (meta_sk->sk_family == AF_INET || | |
58 | mptcp_v6_is_v4_mapped(meta_sk)) { | |
59 | struct mptcp_loc4 loc; | |
60 | struct mptcp_rem4 rem; | |
61 | ||
62 | loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; | |
63 | loc.loc4_id = 0; | |
64 | loc.low_prio = 0; | |
65 | ||
66 | rem.addr.s_addr = inet_sk(meta_sk)->inet_daddr; | |
67 | rem.port = inet_sk(meta_sk)->inet_dport; | |
68 | rem.rem4_id = 0; /* Default 0 */ | |
69 | ||
70 | mptcp_init4_subsockets(meta_sk, &loc, &rem); | |
71 | } else { | |
72 | #if IS_ENABLED(CONFIG_IPV6) | |
73 | struct mptcp_loc6 loc; | |
74 | struct mptcp_rem6 rem; | |
75 | ||
76 | loc.addr = inet6_sk(meta_sk)->saddr; | |
77 | loc.loc6_id = 0; | |
78 | loc.low_prio = 0; | |
79 | ||
80 | rem.addr = meta_sk->sk_v6_daddr; | |
81 | rem.port = inet_sk(meta_sk)->inet_dport; | |
82 | rem.rem6_id = 0; /* Default 0 */ | |
83 | ||
84 | mptcp_init6_subsockets(meta_sk, &loc, &rem); | |
85 | #endif | |
86 | } | |
87 | goto next_subflow; | |
88 | } | |
89 | ||
90 | exit: | |
91 | release_sock(meta_sk); | |
92 | mutex_unlock(&mpcb->mpcb_mutex); | |
93 | sock_put(meta_sk); | |
94 | } | |
95 | ||
96 | static void ndiffports_new_session(const struct sock *meta_sk) | |
97 | { | |
98 | struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; | |
99 | struct ndiffports_priv *fmp = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; | |
100 | ||
101 | /* Initialize workqueue-struct */ | |
102 | INIT_WORK(&fmp->subflow_work, create_subflow_worker); | |
103 | fmp->mpcb = mpcb; | |
104 | } | |
105 | ||
106 | static void ndiffports_create_subflows(struct sock *meta_sk) | |
107 | { | |
108 | const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; | |
109 | struct ndiffports_priv *pm_priv = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; | |
110 | ||
111 | if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || | |
112 | mpcb->send_infinite_mapping || | |
113 | mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) | |
114 | return; | |
115 | ||
116 | if (!work_pending(&pm_priv->subflow_work)) { | |
117 | sock_hold(meta_sk); | |
118 | queue_work(mptcp_wq, &pm_priv->subflow_work); | |
119 | } | |
120 | } | |
121 | ||
122 | static int ndiffports_get_local_id(sa_family_t family, union inet_addr *addr, | |
123 | struct net *net, bool *low_prio) | |
124 | { | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static struct mptcp_pm_ops ndiffports __read_mostly = { | |
129 | .new_session = ndiffports_new_session, | |
130 | .fully_established = ndiffports_create_subflows, | |
131 | .get_local_id = ndiffports_get_local_id, | |
132 | .name = "ndiffports", | |
133 | .owner = THIS_MODULE, | |
134 | }; | |
135 | ||
136 | /* General initialization of MPTCP_PM */ | |
137 | static int __init ndiffports_register(void) | |
138 | { | |
139 | BUILD_BUG_ON(sizeof(struct ndiffports_priv) > MPTCP_PM_SIZE); | |
140 | ||
141 | if (mptcp_register_path_manager(&ndiffports)) | |
142 | goto exit; | |
143 | ||
144 | return 0; | |
145 | ||
146 | exit: | |
147 | return -1; | |
148 | } | |
149 | ||
150 | static void ndiffports_unregister(void) | |
151 | { | |
152 | mptcp_unregister_path_manager(&ndiffports); | |
153 | } | |
154 | ||
155 | module_init(ndiffports_register); | |
156 | module_exit(ndiffports_unregister); | |
157 | ||
158 | MODULE_AUTHOR("Christoph Paasch"); | |
159 | MODULE_LICENSE("GPL"); | |
160 | MODULE_DESCRIPTION("NDIFF-PORTS MPTCP"); | |
161 | MODULE_VERSION("0.88"); |