diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 85a411ac57fc65d4c7d39e27b2fb27a7dacf9323..0b696791da302884e446ee78b4c5a039648f817e 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -334,6 +334,8 @@ iov_len				return IOV_LEN;
 headers				return SF_HDTR_HEADERS;
 trailers			return SF_HDTR_TRAILERS;
 [.][.][.]			return ELLIPSIS;
+function_set_name		return FUNCTION_SET_NAME;
+pcbcnt				return PCBCNT;
 assoc_id			return ASSOC_ID;
 assoc_value			return ASSOC_VALUE;
 shmac_number_of_idents		return SHMAC_NUMBER_OF_IDENTS;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 715bfd2bbd8481ea3450e86b68a7c4d2b7be13d9..a25ffb5b994d6488f27dd643c59a565a4bc7a649 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -528,6 +528,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> IPV4 IPV6 ICMP SCTP UDP UDPLITE GRE MTU
 %token <reserved> MPLS LABEL TC TTL
 %token <reserved> OPTION
+%token <reserved> FUNCTION_SET_NAME PCBCNT
 %token <reserved> SRTO_ASSOC_ID SRTO_INITIAL SRTO_MAX SRTO_MIN
 %token <reserved> SINIT_NUM_OSTREAMS SINIT_MAX_INSTREAMS SINIT_MAX_ATTEMPTS
 %token <reserved> SINIT_MAX_INIT_TIMEO
@@ -641,6 +642,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> inaddr sockaddr msghdr cmsghdr cmsg_level cmsg_type cmsg_data
 %type <expression> sf_hdtr iovec pollfd opt_revents
 %type <expression> linger l_onoff l_linger
+%type <expression> tcp_function_set function_set_name pcbcnt
 %type <udp_encaps_info> opt_udp_encaps_info
 %type <sctp_header_spec> sctp_header_spec
 %type <expression> sctp_assoc_id
@@ -2850,6 +2852,9 @@ expression
 | linger            {
 	$$ = $1;
 }
+| tcp_function_set  {
+	$$ = $1;
+}
 | sf_hdtr           {
 	$$ = $1;
 }
@@ -3163,7 +3168,9 @@ l_onoff
 	}
 	$$ = new_integer_expression($3, "%ld");
 }
-| ONOFF '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+| ONOFF '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
 ;
 
 l_linger
@@ -3173,7 +3180,9 @@ l_linger
 	}
 	$$ = new_integer_expression($3, "%ld");
 }
-| LINGER '=' ELLIPSIS { $$ = new_expression(EXPR_ELLIPSIS); }
+| LINGER '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
 ;
 
 linger
@@ -3185,6 +3194,42 @@ linger
 }
 ;
 
+function_set_name
+: FUNCTION_SET_NAME '=' STRING {
+	$$ = new_expression(EXPR_STRING);
+	$$->value.string = $3;
+	$$->format = "\"%s\"";
+}
+| FUNCTION_SET_NAME '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+pcbcnt
+: PCBCNT '=' INTEGER {
+	if (!is_valid_u32($3)) {
+		semantic_error("linger out of range");
+	}
+	$$ = new_integer_expression($3, "%lu");
+}
+| PCBCNT '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+tcp_function_set
+: '{' function_set_name ',' pcbcnt '}' {
+#if defined(__FreeBSD__)
+	$$ = new_expression(EXPR_TCP_FUNCTION_SET);
+	$$->value.tcp_function_set = calloc(1, sizeof(struct tcp_function_set_expr));
+	$$->value.tcp_function_set->function_set_name = $2;
+	$$->value.tcp_function_set->pcbcnt = $4;
+#else
+	$$ = NULL;
+#endif
+}
+;
+
 sf_hdtr
 : '{' SF_HDTR_HEADERS '(' decimal_integer ')' '=' array ','
       SF_HDTR_TRAILERS '('decimal_integer ')' '=' array '}' {
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 87b3e7a939561033e4c97bef42f8a8ed8953cee2..ffca2f8feaeed7f10c716787ee30e15a456d3a4c 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -3175,6 +3175,27 @@ static int check_sctp_udpencaps(struct sctp_udpencaps_expr *expr,
 }
 #endif
 
+#ifdef TCP_FUNCTION_BLK
+static int check_tcp_function_set(struct tcp_function_set_expr *expr,
+			          struct tcp_function_set *tcp_function_set,
+			          char **error) {
+	if (strncmp(expr->function_set_name->value.string,
+	            tcp_function_set->function_set_name,
+	            TCP_FUNCTION_NAME_LEN_MAX)) {
+		asprintf(error, "tcp_function_set.function_set_name: expected: %s, actual: %.*s\n",
+		         expr->function_set_name->value.string,
+		         TCP_FUNCTION_NAME_LEN_MAX,
+		         tcp_function_set->function_set_name);
+		return STATUS_ERR;
+	}
+	if (check_u32_expr(expr->pcbcnt, tcp_function_set->pcbcnt,
+	                   "tcp_function_set.pcbcnt", error))
+		return STATUS_ERR;
+	return STATUS_OK;
+}
+
+#endif
+
 static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 			      struct expression_list *args, char **error)
 {
@@ -3487,6 +3508,15 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 		live_optval = live_udpencaps;
 		break;
 	}
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET: {
+		struct tcp_function_set *live_tcp_function_set = malloc(sizeof(struct tcp_function_set));
+
+		memset(live_tcp_function_set, 0, sizeof(struct tcp_function_set));
+		live_optval = live_tcp_function_set;
+		break;
+	}
 #endif
 	case EXPR_LIST:
 		s32_bracketed_arg(args, 3, &script_optval, error);
@@ -3639,6 +3669,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_UDPENCAPS:
 		result = check_sctp_udpencaps(val_expression->value.sctp_udpencaps, live_optval, error);
 		break;
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET:
+		result = check_tcp_function_set(val_expression->value.tcp_function_set, live_optval, error);
+		break;
 #endif
 	case EXPR_LIST:
 		if (*(int*)live_optval != script_optval) {
@@ -3739,6 +3774,9 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
 	struct sctp_udpencaps udpencaps;
 #endif
+#ifdef TCP_FUNCTION_BLK
+	struct tcp_function_set tcp_function_set;
+#endif
 
 	if (check_arg_count(args, 5, error))
 		return STATUS_ERR;
@@ -4270,6 +4308,22 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 		}
 		optval = &udpencaps;
 		break;
+#endif
+#ifdef TCP_FUNCTION_BLK
+	case EXPR_TCP_FUNCTION_SET:
+		if (val_expression->value.tcp_function_set->function_set_name->type == EXPR_STRING) {
+			strncpy(tcp_function_set.function_set_name,
+			        val_expression->value.tcp_function_set->function_set_name->value.string,
+			        TCP_FUNCTION_NAME_LEN_MAX);
+		} else {
+			return STATUS_ERR;
+		}
+		if (get_u32(val_expression->value.tcp_function_set->pcbcnt,
+			    &tcp_function_set.pcbcnt, error)) {
+			return STATUS_ERR;
+		}
+		optval = &tcp_function_set;
+		break;
 #endif
 	default:
 		asprintf(error, "unsupported value type: %s",
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 6c1a54c4adb2aeee83de6ae1822c26af453ed7a8..5e61430111575c11c27887f6f76d2387daf470dc 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -70,6 +70,7 @@ struct expression_type_entry expression_type_table[] = {
 	{ EXPR_POLLFD,                      "pollfd"                          },
 #if defined(__FreeBSD__)
 	{ EXPR_SF_HDTR,                     "sf_hdtr"                         },
+	{ EXPR_TCP_FUNCTION_SET,            "tcp_function_set"                },
 #endif
 	{ EXPR_SCTP_RTOINFO,                "sctp_rtoinfo"                    },
 	{ EXPR_SCTP_INITMSG,                "sctp_initmsg"                    },
@@ -331,6 +332,11 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.linger->l_onoff);
 		free_expression(expression->value.linger->l_linger);
 		break;
+	case EXPR_TCP_FUNCTION_SET:
+		assert(expression->value.tcp_function_set);
+		free_expression(expression->value.tcp_function_set->function_set_name);
+		free_expression(expression->value.tcp_function_set->pcbcnt);
+		break;
 	case EXPR_SCTP_RTOINFO:
 		assert(expression->value.sctp_rtoinfo);
 		free_expression(expression->value.sctp_rtoinfo->srto_assoc_id);
@@ -2759,6 +2765,10 @@ static int evaluate(struct expression *in,
 		memcpy(&out->value.linger, &in->value.linger,
 		       sizeof(in->value.linger));
 		break;
+	case EXPR_TCP_FUNCTION_SET:		/* copy as-is */
+		memcpy(&out->value.tcp_function_set, &in->value.tcp_function_set,
+		       sizeof(in->value.tcp_function_set));
+		break;
 	case EXPR_SCTP_RTOINFO:
 		result = evaluate_sctp_rtoinfo_expression(in, out, error);
 		break;
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 2b0be6b6aaaa22e0f562edca00d26aa50e780dc4..b4d75eaf18eb815cb3d663c52689711d939f85d3 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -49,6 +49,7 @@ enum expression_t {
 	EXPR_POLLFD,		  /* expression tree for a pollfd struct */
 #if defined(__FreeBSD__)
 	EXPR_SF_HDTR,		  /* struct sf_hdtr for sendfile */
+	EXPR_TCP_FUNCTION_SET,	  /* struct tcp_function_set */
 #endif
 	EXPR_SCTP_RTOINFO,	  /* struct sctp_rtoinfo for SCTP_RTOINFO */
 	EXPR_SCTP_INITMSG,	  /* struct sctp_initmsg for SCTP_INITMSG */
@@ -119,6 +120,7 @@ struct expression {
 		struct pollfd_expr *pollfd;
 #if defined(__FreeBSD__)
 		struct sf_hdtr_expr *sf_hdtr;
+		struct tcp_function_set_expr *tcp_function_set;
 #endif
 		struct sctp_rtoinfo_expr *sctp_rtoinfo;
 		struct sctp_initmsg_expr *sctp_initmsg;
@@ -232,6 +234,11 @@ struct sf_hdtr_expr {
 	struct expression *trailers;
 	struct expression *trl_cnt;
 };
+
+struct tcp_function_set_expr {
+	struct expression *function_set_name;
+	struct expression *pcbcnt;
+};
 #endif
 
 /* Parse tree for a sctp_rtoinfo struct in a [gs]etsockopt syscall. */
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index 9e21d3752be19eb6b23f02603a1e91dfcad47964..350aa3ed419f2ade54d60818afce2afb95f8ced7 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -376,9 +376,13 @@ struct int_symbol platform_symbols_table[] = {
 #if defined(TCP_FASTOPEN)
 	{ TCP_FASTOPEN,                     "TCP_FASTOPEN"                    },
 #endif
+#if defined(TCP_FUNCTION_BLK)
+	{ TCP_FUNCTION_BLK,                 "TCP_FUNCTION_BLK"                },
+#endif
 #if defined(TCP_REMOTE_UDP_ENCAPS_PORT)
 	{ TCP_REMOTE_UDP_ENCAPS_PORT,       "TCP_REMOTE_UDP_ENCAPS_PORT"      },
 #endif
+
 #if defined(UDPLITE_RECV_CSCOV) && defined(UDPLITE_SEND_CSCOV)
 	/* /usr/include/netinet/udplite.h */
 	{ UDPLITE_RECV_CSCOV,               "UDPLITE_RECV_CSCOV"              },