diff --git a/share/man/man9/tcp_functions.9 b/share/man/man9/tcp_functions.9 --- a/share/man/man9/tcp_functions.9 +++ b/share/man/man9/tcp_functions.9 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 6, 2024 +.Dd July 12, 2024 .Dt TCP_FUNCTIONS 9 .Os .Sh NAME @@ -37,6 +37,7 @@ .Ft int .Fn register_tcp_functions_as_name "struct tcp_function_block *blk" \ "const char *name" "int wait" +.Ft int .Fn register_tcp_functions_as_names "struct tcp_function_block *blk" \ "int wait" "const char *names[]" "int *num_names" .Ft int @@ -112,6 +113,7 @@ The .Fa num_names argument provides a pointer to the number of names. +This number must not exceed TCP_FUNCTION_NAME_NUM_MAX. This function will either succeed in registering all of the names in the array, or none of the names in the array. On failure, the @@ -328,8 +330,11 @@ specified TCP stack. .Sh ERRORS The -.Fn register_tcp_functions -function will fail if: +.Fn register_tcp_functions , +.Fn register_tcp_functions_as_name , +and +.Fn register_tcp_functions_as_names +functions will fail if: .Bl -tag -width Er .It Bq Er EINVAL Any of the members of the @@ -338,7 +343,19 @@ .It Bq Er ENOMEM The function could not allocate memory for its internal data. .It Bq Er EALREADY -A function block is already registered with the same name. +The +.Fa blk +is already registered or a function block is already registered with the same +name. +.El +Additionally, +.Fn register_tcp_functions_as_names +will fail if: +.Bl -tag -width Er +.It Bq Er E2BIG +The number of names pointed to by the +.Fa num_names +argument is larger than TCP_FUNCTION_NAME_NUM_MAX. .El The .Fn deregister_tcp_functions diff --git a/sys/netinet/tcp_log_buf.h b/sys/netinet/tcp_log_buf.h --- a/sys/netinet/tcp_log_buf.h +++ b/sys/netinet/tcp_log_buf.h @@ -269,7 +269,7 @@ TCP_LOG_PRU, /* TCP protocol user request 70 */ TCP_POLICER_DET, /* TCP Policer detectionn 71 */ TCP_PCM_MEASURE, /* TCP Path Capacity Measurement 72 */ - TCP_LOG_END /* End (keep at end) 72 */ + TCP_LOG_END /* End (keep at end) 73 */ }; enum tcp_log_states { diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -1172,9 +1172,9 @@ register_tcp_functions_as_names(struct tcp_function_block *blk, int wait, const char *names[], int *num_names) { - struct tcp_function *n; + struct tcp_function *f[TCP_FUNCTION_NAME_NUM_MAX]; struct tcp_function_set fs; - int error, i; + int error, i, num_registered; KASSERT(names != NULL, ("%s: Called with NULL name list", __func__)); KASSERT(*num_names > 0, @@ -1182,71 +1182,73 @@ KASSERT(rw_initialized(&tcp_function_lock), ("%s: called too early", __func__)); + if (*num_names > TCP_FUNCTION_NAME_NUM_MAX) { + /* Too many names. */ + *num_names = 0; + return (E2BIG); + } if ((blk->tfb_tcp_output == NULL) || (blk->tfb_tcp_do_segment == NULL) || (blk->tfb_tcp_ctloutput == NULL) || (blk->tfb_tcp_handoff_ok == NULL) || (strlen(blk->tfb_tcp_block_name) == 0)) { - /* - * These functions are required and you - * need a name. - */ + /* These functions are required and a name is needed. */ *num_names = 0; return (EINVAL); } - if (blk->tfb_flags & TCP_FUNC_BEING_REMOVED) { - *num_names = 0; - return (EINVAL); + for (i = 0; i < *num_names; i++) { + f[i] = malloc(sizeof(struct tcp_function), M_TCPFUNCTIONS, wait); + if (f[i] == NULL) { + while (--i >= 0) + free(f[i], M_TCPFUNCTIONS); + *num_names = 0; + return (ENOMEM); + } } + num_registered = 0; + rw_wlock(&tcp_function_lock); + if (find_tcp_fb_locked(blk, NULL) != NULL) { + /* A TCP function block can only be registered once. */ + error = EALREADY; + goto cleanup; + } + if (blk->tfb_flags & TCP_FUNC_BEING_REMOVED) { + error = EINVAL; + goto cleanup; + } refcount_init(&blk->tfb_refcnt, 0); blk->tfb_id = atomic_fetchadd_int(&next_tcp_stack_id, 1); for (i = 0; i < *num_names; i++) { - n = malloc(sizeof(struct tcp_function), M_TCPFUNCTIONS, wait); - if (n == NULL) { - error = ENOMEM; - goto cleanup; - } - n->tf_fb = blk; - (void)strlcpy(fs.function_set_name, names[i], sizeof(fs.function_set_name)); - rw_wlock(&tcp_function_lock); if (find_tcp_functions_locked(&fs) != NULL) { /* Duplicate name space not allowed */ - rw_wunlock(&tcp_function_lock); - free(n, M_TCPFUNCTIONS); error = EALREADY; goto cleanup; } - (void)strlcpy(n->tf_name, names[i], sizeof(n->tf_name)); - TAILQ_INSERT_TAIL(&t_functions, n, tf_next); + f[i]->tf_fb = blk; + (void)strlcpy(f[i]->tf_name, names[i], sizeof(f[i]->tf_name)); + TAILQ_INSERT_TAIL(&t_functions, f[i], tf_next); tcp_fb_cnt++; - rw_wunlock(&tcp_function_lock); + num_registered++; } + rw_wunlock(&tcp_function_lock); return (0); cleanup: - /* - * Deregister the names we just added. Because registration failed - * for names[i], we don't need to deregister that name. - */ - *num_names = i; - rw_wlock(&tcp_function_lock); - while (--i >= 0) { - TAILQ_FOREACH(n, &t_functions, tf_next) { - if (!strncmp(n->tf_name, names[i], - TCP_FUNCTION_NAME_LEN_MAX)) { - TAILQ_REMOVE(&t_functions, n, tf_next); - tcp_fb_cnt--; - n->tf_fb = NULL; - free(n, M_TCPFUNCTIONS); - break; - } + /* Remove the entries just added. */ + for (i = 0; i < *num_names; i++) { + if (i < num_registered) { + TAILQ_REMOVE(&t_functions, f[i], tf_next); + tcp_fb_cnt--; } + f[i]->tf_fb = NULL; + free(f[i], M_TCPFUNCTIONS); } rw_wunlock(&tcp_function_lock); + *num_names = num_registered; return (error); } diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -640,6 +640,9 @@ struct tcp_function_block *tf_fb; }; +/* Maximum number of function names a single TCP function block can use. */ +#define TCP_FUNCTION_NAME_NUM_MAX 16 + TAILQ_HEAD(tcp_funchead, tcp_function); struct tcpcb * tcp_drop(struct tcpcb *, int);