From 4fa2ceaea23436e6052f961c48f15e13e5a61bef Mon Sep 17 00:00:00 2001
From: Michael Tuexen <tuexen@fh-muenster.de>
Date: Thu, 4 May 2017 22:26:28 +0200
Subject: [PATCH] Add support for the SCTP_REMOTE_UDP_ENCAPS_PORT socket
 option.

---
 gtests/net/packetdrill/lexer.l           |   9 +-
 gtests/net/packetdrill/parser.y          |  42 ++++++
 gtests/net/packetdrill/run_system_call.c |  87 +++++++++++-
 gtests/net/packetdrill/script.c          | 170 ++++++++++++++---------
 gtests/net/packetdrill/script.h          |   9 ++
 gtests/net/packetdrill/symbols_freebsd.c |   1 +
 6 files changed, 247 insertions(+), 71 deletions(-)

diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index 372ebd48..08b26bab 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -542,6 +542,9 @@ strchange_length		return STRCHANGE_LENGTH;
 strchange_assoc_id		return STRCHANGE_ASSOC_ID;
 strchange_instrms		return STRCHANGE_INSTRMS;
 strchange_outstrms		return STRCHANGE_OUTSTRMS;
+sue_assoc_id			return SUE_ASSOC_ID;
+sue_address			return SUE_ADDRESS;
+sue_port			return SUE_PORT;
 CHUNK				return CHUNK;
 DATA				return DATA;
 INIT				return INIT;
@@ -562,8 +565,8 @@ SHUTDOWN_COMPLETE		return SHUTDOWN_COMPLETE;
 I_DATA				return I_DATA;
 PAD				return PAD;
 RECONFIG			return RECONFIG;
-FORWARD_TSN                     return FORWARD_TSN;
-I_FORWARD_TSN                   return I_FORWARD_TSN;
+FORWARD_TSN			return FORWARD_TSN;
+I_FORWARD_TSN			return I_FORWARD_TSN;
 type				return TYPE;
 flgs				return FLAGS;
 len				return LEN;
@@ -609,7 +612,7 @@ HOSTNAME_ADDRESS		return HOSTNAME_ADDRESS;
 SUPPORTED_ADDRESS_TYPES		return SUPPORTED_ADDRESS_TYPES;
 ADAPTATION_INDICATION		return ADAPTATION_INDICATION;
 ECN_CAPABLE			return ECN_CAPABLE;
-FORWARD_TSN_SUPPORTED           return FORWARD_TSN_SUPPORTED;
+FORWARD_TSN_SUPPORTED		return FORWARD_TSN_SUPPORTED;
 SUPPORTED_EXTENSIONS		return SUPPORTED_EXTENSIONS;
 addr				return ADDR;
 incr				return INCR;
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index 1e92d415..ffa9d175 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -590,6 +590,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %token <reserved> STRRESET_TYPE STRRESET_FLAGS STRRESET_LENGTH STRRESET_ASSOC_ID STRRESET_STREAM_LIST
 %token <reserved> ASSOCRESET_TYPE ASSOCRESET_FLAGS ASSOCRESET_LENGTH ASSOCRESET_ASSOC_ID ASSOCRESET_LOCAL_TSN ASSOCRESET_REMOTE_TSN
 %token <reserved> STRCHANGE_TYPE STRCHANGE_FLAGS STRCHANGE_LENGTH STRCHANGE_ASSOC_ID STRCHANGE_INSTRMS STRCHANGE_OUTSTRMS
+%token <reserved> SUE_ASSOC_ID SUE_ADDRESS SUE_PORT
 %token <floating> FLOAT
 %token <integer> INTEGER HEX_INTEGER
 %token <string> WORD STRING BACK_QUOTED CODE IPV4_ADDR IPV6_ADDR
@@ -670,6 +671,7 @@ static struct tcp_option *new_tcp_fast_open_option(const char *cookie_string,
 %type <expression> assocreset_local_tsn assocreset_remote_tsn
 %type <expression> sctp_stream_change_event strchange_type strchange_flags strchange_length strchange_instrms strchange_outstrms
 %type <expression> sctp_add_streams
+%type <expression> sctp_udpencaps sue_address sue_port
 %type <errno_info> opt_errno
 %type <chunk_list> sctp_chunk_list_spec
 %type <chunk_list_item> sctp_chunk_spec
@@ -2954,6 +2956,9 @@ expression
 | sctp_add_streams  {
 	$$ = $1;
 }
+| sctp_udpencaps  {
+	$$ = $1;
+}
 | null              {
 	$$ = $1;
 }
@@ -5563,7 +5568,44 @@ sctp_stream_change_event
 	$$->value.sctp_stream_change_event->strchange_assoc_id = $10;
 	$$->value.sctp_stream_change_event->strchange_instrms = $12;
 	$$->value.sctp_stream_change_event->strchange_outstrms = $14;
+}
+;
+
+sue_address
+: SUE_ADDRESS '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+| SUE_ADDRESS '=' sockaddr {
+	$$ = $3;
+}
+;
 
+sue_port
+: SUE_PORT '=' _HTONS_ '(' INTEGER ')' {
+	if (!is_valid_u16($5)) {
+		semantic_error("sue_port out of range");
+	}
+	$$ = new_integer_expression(htons($5), "%u");
+}
+| SUE_PORT '=' ELLIPSIS {
+	$$ = new_expression(EXPR_ELLIPSIS);
+}
+;
+
+sctp_udpencaps
+: '{' SUE_ASSOC_ID '=' sctp_assoc_id ',' sue_address ',' sue_port '}' {
+	$$ = new_expression(EXPR_SCTP_UDPENCAPS);
+	$$->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+	$$->value.sctp_udpencaps->sue_assoc_id = $4;
+	$$->value.sctp_udpencaps->sue_address = $6;
+	$$->value.sctp_udpencaps->sue_port = $8;
+}
+| '{' sue_address ',' sue_port '}' {
+	$$ = new_expression(EXPR_SCTP_UDPENCAPS);
+	$$->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+	$$->value.sctp_udpencaps->sue_assoc_id = new_expression(EXPR_ELLIPSIS);
+	$$->value.sctp_udpencaps->sue_address = $2;
+	$$->value.sctp_udpencaps->sue_port = $4;
 }
 ;
 
diff --git a/gtests/net/packetdrill/run_system_call.c b/gtests/net/packetdrill/run_system_call.c
index 2639ba23..87b3e7a9 100644
--- a/gtests/net/packetdrill/run_system_call.c
+++ b/gtests/net/packetdrill/run_system_call.c
@@ -696,6 +696,24 @@ int check_u8_expr(struct expression *expr, u8 value, char *val_name, char **erro
 #endif
 
 #if defined(__FreeBSD__) || defined(linux)
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+int check_u16_htons_expr(struct expression *expr, u16 value, char *val_name, char **error) {
+	if (expr->type != EXPR_ELLIPSIS) {
+		u16 script_val;
+
+		if (get_u16(expr, &script_val, error)) {
+			return STATUS_ERR;
+		}
+		if (ntohs(script_val) != ntohs(value)) {
+			asprintf(error, "%s: expected: %hu actual: %hu",
+				 val_name, ntohs(script_val), ntohs(value));
+			return STATUS_ERR;
+		}
+	}
+	return STATUS_OK;
+}
+#endif
+
 int check_u16_expr(struct expression *expr, u16 value, char *val_name, char **error) {
 	if (expr->type != EXPR_ELLIPSIS) {
 		u16 script_val;
@@ -736,7 +754,7 @@ int check_u32_hton_expr(struct expression *expr, u32 value, char *val_name, char
 		}
 		if (htonl(value) != htonl(script_val)) {
 			asprintf(error, "%s: expected: %u actual: %u", val_name,
-				 htonl(script_val), htonl(value));
+				 ntohl(script_val), ntohl(value));
 			return STATUS_ERR;
 		}
 	}
@@ -3145,6 +3163,18 @@ static int check_sctp_authchunks(struct sctp_authchunks_expr *expr,
 }
 #endif
 
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+static int check_sctp_udpencaps(struct sctp_udpencaps_expr *expr,
+			        struct sctp_udpencaps *sctp_udpencaps,
+			        char **error) {
+	if (check_u16_htons_expr(expr->sue_port, sctp_udpencaps->sue_port,
+				 "sctp_udpencaps.sue_port", 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)
 {
@@ -3435,6 +3465,28 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 			return STATUS_ERR;
 		}
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS: {
+		struct sctp_udpencaps_expr *expr_udpencaps = val_expression->value.sctp_udpencaps;
+		struct sctp_udpencaps *live_udpencaps = malloc(sizeof(struct sctp_udpencaps));
+
+		live_optlen = (socklen_t) sizeof(struct sctp_udpencaps);
+		memset(live_udpencaps, 0, sizeof(struct sctp_udpencaps));
+		if (get_sctp_assoc_t(expr_udpencaps->sue_assoc_id,
+				     &(live_udpencaps->sue_assoc_id), error)) {
+			free(live_udpencaps);
+			return STATUS_ERR;
+		}
+		if (get_sockstorage_arg(expr_udpencaps->sue_address,
+					&(live_udpencaps->sue_address), live_fd)) {
+			asprintf(error, "can't determine sue_address");
+			free(live_udpencaps);
+			return STATUS_ERR;
+		}
+		live_optval = live_udpencaps;
+		break;
+	}
 #endif
 	case EXPR_LIST:
 		s32_bracketed_arg(args, 3, &script_optval, error);
@@ -3582,6 +3634,11 @@ static int syscall_getsockopt(struct state *state, struct syscall_spec *syscall,
 	case EXPR_SCTP_ADD_STREAMS:
 		// SCTP_ADD_STREAMS should not be a successfull option
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS:
+		result = check_sctp_udpencaps(val_expression->value.sctp_udpencaps, live_optval, error);
+		break;
 #endif
 	case EXPR_LIST:
 		if (*(int*)live_optval != script_optval) {
@@ -3679,6 +3736,10 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_ADD_STREAMS
 	struct sctp_add_streams add_streams;
 #endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	struct sctp_udpencaps udpencaps;
+#endif
+
 	if (check_arg_count(args, 5, error))
 		return STATUS_ERR;
 	if (s32_arg(args, 0, &script_fd, error))
@@ -3864,7 +3925,7 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 #ifdef SCTP_GET_PEER_ADDR_INFO
 	case EXPR_SCTP_PADDRINFO:
 		if (get_sctp_assoc_t(val_expression->value.sctp_paddrinfo->spinfo_assoc_id,
-				    &paddrinfo.spinfo_assoc_id, error)) {
+				     &paddrinfo.spinfo_assoc_id, error)) {
 			return STATUS_ERR;
 		}
 		if (get_sockstorage_arg(val_expression->value.sctp_paddrinfo->spinfo_address,
@@ -4171,7 +4232,6 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 			expr = get_arg(list, i, error);
 			get_u16(expr, &(reset_streams->srs_stream_list[i]), error);
 		}
-
 		optval = reset_streams;
 		break;
 	}
@@ -4190,9 +4250,26 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 			    &add_streams.sas_outstrms, error)) {
 			return STATUS_ERR;
 		}
-
 		optval = &add_streams;
 		break;
+#endif
+#ifdef SCTP_REMOTE_UDP_ENCAPS_PORT
+	case EXPR_SCTP_UDPENCAPS:
+		if (get_sctp_assoc_t(val_expression->value.sctp_udpencaps->sue_assoc_id,
+				     &udpencaps.sue_assoc_id, error)) {
+			return STATUS_ERR;
+		}
+		if (get_sockstorage_arg(val_expression->value.sctp_udpencaps->sue_address,
+					&udpencaps.sue_address, live_fd)) {
+			asprintf(error, "can't determine sue_address");
+			return STATUS_ERR;
+		}
+		if (get_u16(val_expression->value.sctp_udpencaps->sue_port,
+			    &udpencaps.sue_port, error)) {
+			return STATUS_ERR;
+		}
+		optval = &udpencaps;
+		break;
 #endif
 	default:
 		asprintf(error, "unsupported value type: %s",
@@ -4200,6 +4277,8 @@ static int syscall_setsockopt(struct state *state, struct syscall_spec *syscall,
 		return STATUS_ERR;
 		break;
 	}
+	assert(optval != NULL);
+
 	begin_syscall(state, syscall);
 
 	result = setsockopt(live_fd, level, optname, optval, optlen);
diff --git a/gtests/net/packetdrill/script.c b/gtests/net/packetdrill/script.c
index 5a0e16d7..6c1a54c4 100644
--- a/gtests/net/packetdrill/script.c
+++ b/gtests/net/packetdrill/script.c
@@ -53,71 +53,72 @@ struct expression_type_entry {
 	const char *name;
 };
 struct expression_type_entry expression_type_table[] = {
-	{ EXPR_NONE,                 "none" },
-	{ EXPR_NULL,		     "null" },
-	{ EXPR_ELLIPSIS,             "ellipsis" },
-	{ EXPR_INTEGER,              "integer" },
-	{ EXPR_WORD,                 "word" },
-	{ EXPR_STRING,               "string" },
-	{ EXPR_SOCKET_ADDRESS_IPV4,  "sockaddr_in" },
-	{ EXPR_SOCKET_ADDRESS_IPV6,  "sockaddr_in6" },
-	{ EXPR_LINGER,               "linger" },
-	{ EXPR_BINARY,               "binary_expression" },
-	{ EXPR_LIST,                 "list" },
-	{ EXPR_IOVEC,                "iovec" },
-	{ EXPR_MSGHDR,               "msghdr" },
-	{ EXPR_CMSGHDR,              "cmsghdr"},
-	{ EXPR_POLLFD,               "pollfd" },
+	{ EXPR_NONE,                        "none"                            },
+	{ EXPR_NULL,                        "null"                            },
+	{ EXPR_ELLIPSIS,                    "ellipsis"                        },
+	{ EXPR_INTEGER,                     "integer"                         },
+	{ EXPR_WORD,                        "word"                            },
+	{ EXPR_STRING,                      "string"                          },
+	{ EXPR_SOCKET_ADDRESS_IPV4,         "sockaddr_in"                     },
+	{ EXPR_SOCKET_ADDRESS_IPV6,         "sockaddr_in6"                    },
+	{ EXPR_LINGER,                      "linger"                          },
+	{ EXPR_BINARY,                      "binary_expression"               },
+	{ EXPR_LIST,                        "list"                            },
+	{ EXPR_IOVEC,                       "iovec"                           },
+	{ EXPR_MSGHDR,                      "msghdr"                          },
+	{ EXPR_CMSGHDR,                     "cmsghdr"                         },
+	{ EXPR_POLLFD,                      "pollfd"                          },
 #if defined(__FreeBSD__)
-	{ EXPR_SF_HDTR,              "sf_hdtr" },
+	{ EXPR_SF_HDTR,                     "sf_hdtr"                         },
 #endif
-	{ EXPR_SCTP_RTOINFO,         "sctp_rtoinfo"},
-	{ EXPR_SCTP_INITMSG,         "sctp_initmsg"},
-	{ EXPR_SCTP_ASSOC_VALUE,     "sctp_assoc_value"},
-	{ EXPR_SCTP_HMACALGO,        "sctp_hmacalgo"},
-	{ EXPR_SCTP_AUTHKEYID,       "sctp_authkeyid"},
-	{ EXPR_SCTP_SACKINFO,        "sctp_sackinfo"},
-	{ EXPR_SCTP_STATUS,          "sctp_status"},
-	{ EXPR_SCTP_PADDRINFO,	     "sctp_paddrinfo"},
-	{ EXPR_SCTP_PEER_ADDR_PARAMS,"sctp_peer_addr_params"},
-	{ EXPR_SCTP_STREAM_VALUE,    "sctp_stream_value"},
-	{ EXPR_SCTP_ASSOCPARAMS,     "sctp_assocparams"},
-	{ EXPR_SCTP_EVENT,	     "sctp_event"      },
-	{ EXPR_SCTP_EVENT_SUBSCRIBE, "sctp_event_subscribe"},
-	{ EXPR_SCTP_SNDINFO,         "sctp_sndinfo"    },
-	{ EXPR_SCTP_SETPRIM,         "sctp_setprim"    },
-	{ EXPR_SCTP_SETADAPTATION,   "sctp_setadaptation"},
-	{ EXPR_SCTP_SNDRCVINFO,      "sctp_sndrcvinfo" },
-	{ EXPR_SCTP_PRINFO,          "sctp_prinfo"     },
-	{ EXPR_SCTP_DEFAULT_PRINFO,  "sctp_default_prinfo"},
-	{ EXPR_SCTP_AUTHINFO,        "sctp_authinfo"   },
-	{ EXPR_SCTP_SENDV_SPA,       "sctp_sendv_spa"  },
-	{ EXPR_SCTP_RCVINFO,         "sctp_rcvinfo"    },
-	{ EXPR_SCTP_NXTINFO,         "sctp_nxtinfo"    },
-	{ EXPR_SCTP_RECVV_RN,        "sctp_recvv_rn "  },
-	{ EXPR_SCTP_ASSOC_CHANGE,    "sctp_assoc_change"},
-	{ EXPR_SCTP_PADDR_CHANGE,    "sctp_paddr_change"},
-	{ EXPR_SCTP_REMOTE_ERROR,    "sctp_remote_error"},
-	{ EXPR_SCTP_SEND_FAILED,     "sctp_send_failed"},
-	{ EXPR_SCTP_SHUTDOWN_EVENT,  "sctp_shutdown_event"},
-	{ EXPR_SCTP_ADAPTATION_EVENT,"sctp_adaptation_event"},
-	{ EXPR_SCTP_PDAPI_EVENT,     "sctp_pdapi_event"},
-	{ EXPR_SCTP_AUTHKEY_EVENT,   "sctp_authkey_event"},
-	{ EXPR_SCTP_SENDER_DRY_EVENT,"sctp_sender_dry_event"},
-	{ EXPR_SCTP_SEND_FAILED_EVENT,"sctp_send_failed_event"},
-	{ EXPR_SCTP_TLV,             "sctp_tlv"        },
-	{ EXPR_SCTP_EXTRCVINFO,      "sctp_extrcvinfo" },
-	{ EXPR_SCTP_ASSOC_IDS,       "sctp_assoc_ids"  },
-	{ EXPR_SCTP_AUTHCHUNKS,      "sctp_authchunks" },
-	{ EXPR_SCTP_SETPEERPRIM,     "sctp_setpeerprim"},
-	{ EXPR_SCTP_AUTHCHUNK,       "sctp_authchunk"  },
-	{ EXPR_SCTP_AUTHKEY,         "sctp_authkey"    },
-	{ EXPR_SCTP_RESET_STREAMS,   "sctp_reset_streams"},
-	{ EXPR_SCTP_ADD_STREAMS,     "sctp_add_streams"},
-	{ EXPR_SCTP_STREAM_RESET_EVENT, "sctp_stream_reset_event"},
-	{ EXPR_SCTP_ASSOC_RESET_EVENT, "sctp_assoc_reset_event"},
-	{ EXPR_SCTP_STREAM_CHANGE_EVENT, "sctp_stream_change_event"},
-	{ NUM_EXPR_TYPES,            NULL}
+	{ EXPR_SCTP_RTOINFO,                "sctp_rtoinfo"                    },
+	{ EXPR_SCTP_INITMSG,                "sctp_initmsg"                    },
+	{ EXPR_SCTP_ASSOC_VALUE,            "sctp_assoc_value"                },
+	{ EXPR_SCTP_HMACALGO,               "sctp_hmacalgo"                   },
+	{ EXPR_SCTP_AUTHKEYID,              "sctp_authkeyid"                  },
+	{ EXPR_SCTP_SACKINFO,               "sctp_sackinfo"                   },
+	{ EXPR_SCTP_STATUS,                 "sctp_status"                     },
+	{ EXPR_SCTP_PADDRINFO,              "sctp_paddrinfo"                  },
+	{ EXPR_SCTP_PEER_ADDR_PARAMS,       "sctp_peer_addr_params"           },
+	{ EXPR_SCTP_STREAM_VALUE,           "sctp_stream_value"               },
+	{ EXPR_SCTP_ASSOCPARAMS,            "sctp_assocparams"                },
+	{ EXPR_SCTP_EVENT,                  "sctp_event"                      },
+	{ EXPR_SCTP_EVENT_SUBSCRIBE,        "sctp_event_subscribe"            },
+	{ EXPR_SCTP_SNDINFO,                "sctp_sndinfo"                    },
+	{ EXPR_SCTP_SETPRIM,                "sctp_setprim"                    },
+	{ EXPR_SCTP_SETADAPTATION,          "sctp_setadaptation"              },
+	{ EXPR_SCTP_SNDRCVINFO,             "sctp_sndrcvinfo"                 },
+	{ EXPR_SCTP_PRINFO,                 "sctp_prinfo"                     },
+	{ EXPR_SCTP_DEFAULT_PRINFO,         "sctp_default_prinfo"             },
+	{ EXPR_SCTP_AUTHINFO,               "sctp_authinfo"                   },
+	{ EXPR_SCTP_SENDV_SPA,              "sctp_sendv_spa"                  },
+	{ EXPR_SCTP_RCVINFO,                "sctp_rcvinfo"                    },
+	{ EXPR_SCTP_NXTINFO,                "sctp_nxtinfo"                    },
+	{ EXPR_SCTP_RECVV_RN,               "sctp_recvv_rn"                   },
+	{ EXPR_SCTP_ASSOC_CHANGE,           "sctp_assoc_change"               },
+	{ EXPR_SCTP_PADDR_CHANGE,           "sctp_paddr_change"               },
+	{ EXPR_SCTP_REMOTE_ERROR,           "sctp_remote_error"               },
+	{ EXPR_SCTP_SEND_FAILED,            "sctp_send_failed"                },
+	{ EXPR_SCTP_SHUTDOWN_EVENT,         "sctp_shutdown_event"             },
+	{ EXPR_SCTP_ADAPTATION_EVENT,       "sctp_adaptation_event"           },
+	{ EXPR_SCTP_PDAPI_EVENT,            "sctp_pdapi_event"                },
+	{ EXPR_SCTP_AUTHKEY_EVENT,          "sctp_authkey_event"              },
+	{ EXPR_SCTP_SENDER_DRY_EVENT,       "sctp_sender_dry_event"           },
+	{ EXPR_SCTP_SEND_FAILED_EVENT,      "sctp_send_failed_event"          },
+	{ EXPR_SCTP_TLV,                    "sctp_tlv"                        },
+	{ EXPR_SCTP_EXTRCVINFO,             "sctp_extrcvinfo"                 },
+	{ EXPR_SCTP_ASSOC_IDS,              "sctp_assoc_ids"                  },
+	{ EXPR_SCTP_AUTHCHUNKS,             "sctp_authchunks"                 },
+	{ EXPR_SCTP_SETPEERPRIM,            "sctp_setpeerprim"                },
+	{ EXPR_SCTP_AUTHCHUNK,              "sctp_authchunk"                  },
+	{ EXPR_SCTP_AUTHKEY,                "sctp_authkey"                    },
+	{ EXPR_SCTP_RESET_STREAMS,          "sctp_reset_streams"              },
+	{ EXPR_SCTP_ADD_STREAMS,            "sctp_add_streams"                },
+	{ EXPR_SCTP_STREAM_RESET_EVENT,     "sctp_stream_reset_event"         },
+	{ EXPR_SCTP_ASSOC_RESET_EVENT,      "sctp_assoc_reset_event"          },
+	{ EXPR_SCTP_STREAM_CHANGE_EVENT,    "sctp_stream_change_event"        },
+	{ EXPR_SCTP_UDPENCAPS,              "sctp_udpencaps"                  },
+	{ NUM_EXPR_TYPES,                   NULL                              }
 };
 
 const char *expression_type_to_string(enum expression_t type)
@@ -219,7 +220,7 @@ struct flag_name poll_flags[] = {
 #endif
 
 #ifdef POLLINIGNEOF
-	{ POLLINIGNEOF, "POLLINIGNEOF"                    },
+	{ POLLINIGNEOF, "POLLINIGNEOF" },
 #endif
 
 	{ POLLERR,	"POLLERR" },
@@ -651,6 +652,11 @@ void free_expression(struct expression *expression)
 		free_expression(expression->value.sctp_stream_change_event->strchange_instrms);
 		free_expression(expression->value.sctp_stream_change_event->strchange_outstrms);
 		break;
+	case EXPR_SCTP_UDPENCAPS:
+		free_expression(expression->value.sctp_udpencaps->sue_assoc_id);
+		free_expression(expression->value.sctp_udpencaps->sue_address);
+		free_expression(expression->value.sctp_udpencaps->sue_port);
+		break;
 	case EXPR_WORD:
 		assert(expression->value.string);
 		free(expression->value.string);
@@ -2695,6 +2701,39 @@ static int evaluate_sctp_stream_change_event_expression(struct expression *in,
 	return STATUS_OK;
 }
 
+static int evaluate_sctp_udpencaps_expression(struct expression *in,
+					      struct expression *out,
+					      char **error)
+{
+	struct sctp_udpencaps_expr *in_udpencaps;
+	struct sctp_udpencaps_expr *out_udpencaps;
+
+	assert(in->type == EXPR_SCTP_UDPENCAPS);
+	assert(in->value.sctp_udpencaps);
+	assert(out->type == EXPR_SCTP_UDPENCAPS);
+
+	out->value.sctp_udpencaps = calloc(1, sizeof(struct sctp_udpencaps_expr));
+
+	in_udpencaps = in->value.sctp_udpencaps;
+	out_udpencaps = out->value.sctp_udpencaps;
+
+	if (evaluate(in_udpencaps->sue_assoc_id,
+		     &out_udpencaps->sue_assoc_id,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_udpencaps->sue_address,
+		     &out_udpencaps->sue_address,
+		     error))
+		return STATUS_ERR;
+	if (evaluate(in_udpencaps->sue_port,
+		     &out_udpencaps->sue_port,
+		     error))
+		return STATUS_ERR;
+
+	return STATUS_OK;
+}
+
+
 static int evaluate(struct expression *in,
 		    struct expression **out_ptr, char **error)
 {
@@ -2858,6 +2897,9 @@ static int evaluate(struct expression *in,
 	case EXPR_SCTP_STREAM_CHANGE_EVENT:
 		result = evaluate_sctp_stream_change_event_expression(in, out, error);
 		break;
+	case EXPR_SCTP_UDPENCAPS:
+		result = evaluate_sctp_udpencaps_expression(in, out, error);
+		break;
 	case EXPR_WORD:
 		out->type = EXPR_INTEGER;
 		if (symbol_to_int(in->value.string,
diff --git a/gtests/net/packetdrill/script.h b/gtests/net/packetdrill/script.h
index 41bf74a6..2b0be6b6 100644
--- a/gtests/net/packetdrill/script.h
+++ b/gtests/net/packetdrill/script.h
@@ -96,6 +96,7 @@ enum expression_t {
 	EXPR_SCTP_STREAM_RESET_EVENT, /* expression tree for sctp_stream_reset_event struct for sctp notifications */
 	EXPR_SCTP_ASSOC_RESET_EVENT,  /* expression tree for sctp_assoc_reset_event struct for sctp notifications */
 	EXPR_SCTP_STREAM_CHANGE_EVENT, /* expression tree for sctp_stream_change_event struct for sctp notifications */
+	EXPR_SCTP_UDPENCAPS,      /* expression tree for sctp_udpencaps struct for [gs]etsockopt */
 	NUM_EXPR_TYPES,
 };
 /* Convert an expression type to a human-readable string */
@@ -165,6 +166,7 @@ struct expression {
 		struct sctp_stream_reset_event_expr *sctp_stream_reset_event;
 		struct sctp_assoc_reset_event_expr *sctp_assoc_reset_event;
 		struct sctp_stream_change_event_expr *sctp_stream_change_event;
+		struct sctp_udpencaps_expr *sctp_udpencaps;
 	} value;
 	const char *format;	/* the printf format for printing the value */
 };
@@ -635,6 +637,13 @@ struct sctp_stream_change_event_expr {
 	struct expression *strchange_outstrms;
 };
 
+/* Parse tree for sctp_udpencaps struct */
+struct sctp_udpencaps_expr {
+	struct expression *sue_assoc_id;
+	struct expression *sue_address;
+	struct expression *sue_port;
+};
+
 /* The errno-related info from strace to summarize a system call error */
 struct errno_spec {
 	const char *errno_macro;	/* errno symbol (C macro name) */
diff --git a/gtests/net/packetdrill/symbols_freebsd.c b/gtests/net/packetdrill/symbols_freebsd.c
index c9199060..d8b0f52f 100644
--- a/gtests/net/packetdrill/symbols_freebsd.c
+++ b/gtests/net/packetdrill/symbols_freebsd.c
@@ -193,6 +193,7 @@ struct int_symbol platform_symbols_table[] = {
 #if defined(SCTP_INTERLEAVING_SUPPORTED)
 	{ SCTP_INTERLEAVING_SUPPORTED,      "SCTP_INTERLEAVING_SUPPORTED"     },
 #endif
+	{ SCTP_REMOTE_UDP_ENCAPS_PORT,      "SCTP_REMOTE_UDP_ENCAPS_PORT"     },
 	{ SCTP_CLOSED,                      "SCTP_CLOSED"                     },
 	{ SCTP_BOUND,                       "SCTP_BOUND"                      },
 	{ SCTP_LISTEN,                      "SCTP_LISTEN"                     },
-- 
GitLab