@@ -1793,10 +1793,14 @@ void picoquic_delete_abandoned_paths(picoquic_cnx_t* cnx, uint64_t current_time,
1793
1793
}
1794
1794
}
1795
1795
1796
- while (cnx -> nb_paths > path_index_good ) {
1797
- int d_path = cnx -> nb_paths - 1 ;
1798
- picoquic_dereference_stashed_cnxid (cnx , cnx -> path [d_path ], 0 );
1799
- picoquic_delete_path (cnx , d_path );
1796
+ if (cnx -> nb_paths > path_index_good ) {
1797
+ do {
1798
+ int d_path = cnx -> nb_paths - 1 ;
1799
+ picoquic_dereference_stashed_cnxid (cnx , cnx -> path [d_path ], 0 );
1800
+ picoquic_delete_path (cnx , d_path );
1801
+ } while (cnx -> nb_paths > path_index_good );
1802
+ /* If paths have been deleted, it may become possible to create new ones. */
1803
+ picoquic_test_and_signal_new_path_allowed (cnx );
1800
1804
}
1801
1805
1802
1806
/* TODO: what if there are no paths left? */
@@ -2094,52 +2098,164 @@ int picoquic_assign_peer_cnxid_to_path(picoquic_cnx_t* cnx, int path_index)
2094
2098
return ret ;
2095
2099
}
2096
2100
2097
- /* Create a new path in order to trigger a migration */
2098
- int picoquic_probe_new_path_ex (picoquic_cnx_t * cnx , const struct sockaddr * addr_peer ,
2099
- const struct sockaddr * addr_local , int if_index , uint64_t current_time , int to_preferred_address )
2101
+ /* Check whether the connection state, number of paths, path ID and
2102
+ * available CID will allow creation of a new path
2103
+ */
2104
+ int picoquic_check_new_path_allowed (picoquic_cnx_t * cnx , int to_preferred_address )
2100
2105
{
2101
2106
int ret = 0 ;
2102
- int partial_match_path = -1 ;
2103
- int path_id = -1 ;
2104
2107
2105
- if ((cnx -> remote_parameters .migration_disabled && !to_preferred_address ) ||
2108
+ if ((cnx -> remote_parameters .migration_disabled && !to_preferred_address ) ||
2106
2109
cnx -> local_parameters .migration_disabled ) {
2107
2110
/* Do not create new paths if migration is disabled */
2108
- ret = PICOQUIC_ERROR_MIGRATION_DISABLED ;
2109
2111
DBG_PRINTF ("Tried to create probe with migration disabled = %d" , cnx -> remote_parameters .migration_disabled );
2112
+ ret = PICOQUIC_ERROR_MIGRATION_DISABLED ;
2110
2113
}
2111
- else if ((path_id = picoquic_find_path_by_address (cnx , addr_local , addr_peer , & partial_match_path )) >= 0 ) {
2112
- /* This path already exists. Will not create it, but will restore it in working order if disabled. */
2113
- ret = -1 ;
2114
- }
2115
- else if (partial_match_path >= 0 && addr_peer -> sa_family == 0 ) {
2116
- /* This path already exists. Will not create it, but will restore it in working order if disabled. */
2117
- ret = -1 ;
2118
- }
2119
- else if (cnx -> first_remote_cnxid_stash -> cnxid_stash_first == NULL ) {
2120
- /* No CNXID available yet. */
2121
- ret = -1 ;
2114
+ else if (cnx -> cnx_state < picoquic_state_client_almost_ready ) {
2115
+ ret = PICOQUIC_ERROR_PATH_NOT_READY ;
2122
2116
}
2123
2117
else if (cnx -> nb_paths >= PICOQUIC_NB_PATH_TARGET ) {
2124
2118
/* Too many paths created already */
2125
- ret = -1 ;
2119
+ ret = PICOQUIC_ERROR_PATH_LIMIT_EXCEEDED ;
2126
2120
}
2127
- else if (picoquic_create_path (cnx , current_time , addr_local , addr_peer , UINT64_MAX ) > 0 ) {
2128
- path_id = cnx -> nb_paths - 1 ;
2129
- ret = picoquic_assign_peer_cnxid_to_path (cnx , path_id );
2121
+ else {
2122
+ /* testing availability of connection ID is sufficient.
2123
+ * If multipath is enabled, connection IDs will
2124
+ * only be received if both peers have negotiated a sufficient path ID.
2125
+ * In any case, connection IDs can only be received if the connection
2126
+ * is almost ready.
2127
+ */
2128
+ uint64_t unique_path_id = 0 ;
2129
+ if (cnx -> is_multipath_enabled ) {
2130
+ unique_path_id = cnx -> unique_path_id_next ;
2131
+ }
2132
+ if (picoquic_obtain_stashed_cnxid (cnx , unique_path_id ) == NULL ) {
2133
+ if (cnx -> unique_path_id_next > cnx -> max_path_id_remote ) {
2134
+ ret = PICOQUIC_ERROR_PATH_ID_BLOCKED ;
2135
+ }
2136
+ else
2137
+ {
2138
+ ret = PICOQUIC_ERROR_PATH_CID_BLOCKED ;
2139
+ }
2140
+ }
2141
+ }
2142
+ return ret ;
2143
+ }
2130
2144
2131
- if (ret != 0 ) {
2132
- /* delete the path that was just created! */
2133
- picoquic_dereference_stashed_cnxid (cnx , cnx -> path [path_id ], 0 );
2134
- picoquic_delete_path (cnx , path_id );
2145
+ int picoquic_subscribe_new_path_allowed (picoquic_cnx_t * cnx , int * is_already_allowed )
2146
+ {
2147
+ int ret = picoquic_check_new_path_allowed (cnx , 0 );
2148
+
2149
+ * is_already_allowed = 0 ;
2150
+ if (ret == 0 ) {
2151
+ /* is allowed. Just say so -- get return code. */
2152
+ * is_already_allowed = 1 ;
2153
+ cnx -> is_subscribed_to_path_allowed = 0 ;
2154
+ cnx -> is_notified_that_path_is_allowed = 0 ;
2155
+ }
2156
+ else if (ret == PICOQUIC_ERROR_PATH_NOT_READY ||
2157
+ ret == PICOQUIC_ERROR_PATH_LIMIT_EXCEEDED ||
2158
+ ret == PICOQUIC_ERROR_PATH_ID_BLOCKED ||
2159
+ ret == PICOQUIC_ERROR_PATH_CID_BLOCKED ) {
2160
+ /* transient error. Subscribe to the event and return 0 */
2161
+ cnx -> is_subscribed_to_path_allowed = 1 ;
2162
+ cnx -> is_notified_that_path_is_allowed = 0 ;
2163
+ ret = 0 ;
2164
+ }
2165
+ return ret ;
2166
+ }
2167
+
2168
+ /* Internal only API, notify that next path is now allowed. */
2169
+ void picoquic_test_and_signal_new_path_allowed (picoquic_cnx_t * cnx )
2170
+ {
2171
+ if (cnx -> is_subscribed_to_path_allowed &&
2172
+ !cnx -> is_notified_that_path_is_allowed )
2173
+ {
2174
+ if (picoquic_check_new_path_allowed (cnx , 0 ) == 0 ) {
2175
+ cnx -> is_notified_that_path_is_allowed = 1 ;
2176
+ if (cnx -> callback_fn != NULL ) {
2177
+ (void )cnx -> callback_fn (cnx , 0 , NULL , 0 , picoquic_callback_next_path_allowed , cnx -> callback_ctx , NULL );
2178
+ }
2179
+ }
2180
+ }
2181
+ }
2182
+
2183
+ /* Create a new path in order to trigger a migration, or just a parallel
2184
+ * path if multipath is enabled.
2185
+ */
2186
+ int picoquic_probe_new_path_ex (picoquic_cnx_t * cnx , const struct sockaddr * addr_peer ,
2187
+ const struct sockaddr * addr_local , int if_index , uint64_t current_time , int to_preferred_address )
2188
+ {
2189
+ int partial_match_path = -1 ;
2190
+ int path_id = -1 ;
2191
+
2192
+ int ret = picoquic_check_new_path_allowed (cnx , to_preferred_address );
2193
+
2194
+
2195
+ if (ret == 0 ) {
2196
+ /* verify that the peer and local addresses are correctly set */
2197
+ if (addr_peer == NULL || addr_peer -> sa_family == 0 ) {
2198
+ if (addr_local == NULL || addr_local -> sa_family == 0 ) {
2199
+ ret = PICOQUIC_ERROR_UNEXPECTED_ERROR ;
2200
+ }
2201
+ else {
2202
+ /* Find the peer address from existing paths */
2203
+ for (int i = 0 ; i < cnx -> nb_paths ; i ++ ) {
2204
+ if (cnx -> path [i ]-> peer_addr .ss_family == addr_local -> sa_family ) {
2205
+ addr_peer = (struct sockaddr * )& cnx -> path [i ]-> peer_addr ;
2206
+ break ;
2207
+ }
2208
+ }
2209
+ if (addr_peer == NULL || addr_peer -> sa_family == 0 ) {
2210
+ ret = PICOQUIC_ERROR_UNEXPECTED_ERROR ;
2211
+ }
2212
+ }
2213
+ }
2214
+ else if (addr_local == NULL || addr_local -> sa_family == 0 ) {
2215
+ /* Find the local address from existing paths */
2216
+ for (int i = 0 ; i < cnx -> nb_paths ; i ++ ) {
2217
+ if (cnx -> path [i ]-> local_addr .ss_family == addr_peer -> sa_family ) {
2218
+ addr_local = (struct sockaddr * )& cnx -> path [i ]-> local_addr ;
2219
+ break ;
2220
+ }
2221
+ }
2222
+ if (addr_peer == NULL ) {
2223
+ ret = PICOQUIC_ERROR_UNEXPECTED_ERROR ;
2224
+ }
2225
+ }
2226
+ else if (addr_peer -> sa_family != addr_local -> sa_family ) {
2227
+ ret = PICOQUIC_ERROR_PATH_ADDRESS_FAMILY ;
2228
+ }
2229
+ }
2230
+
2231
+ if (ret == 0 && !cnx -> is_multipath_enabled ) {
2232
+ if ((path_id = picoquic_find_path_by_address (cnx , addr_local , addr_peer , & partial_match_path )) >= 0 ) {
2233
+ /* This path already exists. Will not create it, but will restore it in working order if disabled. */
2234
+ ret = PICOQUIC_ERROR_PATH_DUPLICATE ;
2235
+ }
2236
+ }
2237
+
2238
+ if (ret == 0 ){
2239
+ if (picoquic_create_path (cnx , current_time , addr_local , addr_peer , UINT64_MAX ) > 0 ) {
2240
+ path_id = cnx -> nb_paths - 1 ;
2241
+ ret = picoquic_assign_peer_cnxid_to_path (cnx , path_id );
2242
+
2243
+ if (ret != 0 ) {
2244
+ /* delete the path that was just created! */
2245
+ picoquic_dereference_stashed_cnxid (cnx , cnx -> path [path_id ], 0 );
2246
+ picoquic_delete_path (cnx , path_id );
2247
+ }
2248
+ else {
2249
+ cnx -> path [path_id ]-> path_is_published = 1 ;
2250
+ picoquic_register_path (cnx , cnx -> path [path_id ]);
2251
+ picoquic_set_path_challenge (cnx , path_id , current_time );
2252
+ cnx -> path [path_id ]-> path_is_preferred_path = to_preferred_address ;
2253
+ cnx -> path [path_id ]-> is_nat_challenge = 0 ;
2254
+ cnx -> path [path_id ]-> if_index_dest = if_index ;
2255
+ }
2135
2256
}
2136
2257
else {
2137
- cnx -> path [path_id ]-> path_is_published = 1 ;
2138
- picoquic_register_path (cnx , cnx -> path [path_id ]);
2139
- picoquic_set_path_challenge (cnx , path_id , current_time );
2140
- cnx -> path [path_id ]-> path_is_preferred_path = to_preferred_address ;
2141
- cnx -> path [path_id ]-> is_nat_challenge = 0 ;
2142
- cnx -> path [path_id ]-> if_index_dest = if_index ;
2258
+ ret = PICOQUIC_ERROR_MEMORY ;
2143
2259
}
2144
2260
}
2145
2261
0 commit comments