diff --git a/gtests/net/packetdrill/lexer.l b/gtests/net/packetdrill/lexer.l
index ad44339817e9ff840ae45059f215d3c4bd495a08..5f6929fb36b8326d74d46b3a72598d19edc4c0e1 100644
--- a/gtests/net/packetdrill/lexer.l
+++ b/gtests/net/packetdrill/lexer.l
@@ -51,6 +51,7 @@
 #include "tcp_options.h"
 #include "parse.h"
 #include "config.h"
+#include "logging.h"
 
 /* This include of the bison-generated .h file must go last so that we
  * can first include all of the declarations on which it depends.
@@ -131,110 +132,6 @@ static s64 hextol(const char *s)
 {
 	return strtol(yytext + 2, NULL, 16);
 }
-
-enum ifdef_os {
-	Linux_IFDEF = 1, FreeBSD_IFDEF, NetBSD_IFDEF, OpenBSD_IFDEF, Omnet_IFDEF, Apple_IFDEF, Solaris_IFDEF
-};
-
-#define MAX_IFDEF_DEPTH 1
-YY_BUFFER_STATE ifdef_stack[MAX_IFDEF_DEPTH];
-int ifdef_stack_ptr = 0;
-
-static inline int get_os_name_length(enum ifdef_os os) {
-	switch (os) {
-	case Linux_IFDEF:
-		return strlen("Linux");
-	case FreeBSD_IFDEF:
-		return strlen("FreeBSD");
-	case NetBSD_IFDEF:
-		return strlen("NetBSD");
-	case OpenBSD_IFDEF:
-		return strlen("OpenBSD");
-	case Omnet_IFDEF:
-		return strlen("Omnet");
-	case Apple_IFDEF:
-		return strlen("Apple");
-	case Solaris_IFDEF:
-		return strlen("Solaris");
-	default:
-		return -1;
-	}
-}
-
-static inline bool ignore_ifdef(enum ifdef_os os) {
-	switch (os) {
-#ifdef linux
-	case Linux_IFDEF:
-#endif
-#ifdef __FreeBSD__
-	case FreeBSD_IFDEF:
-#endif
-#ifdef __OpenBSD__
-	case OpenBSD_IFDEF:
-#endif
-#ifdef __NetBSD__
-	case NetBSD_IFDEF:
-#endif
-#ifdef __APPLE__
-	case Apple_IFDEF:
-#endif
-#ifdef __SunOS_5_11
-	case Solaris_IFDEF:
-#endif
-	/* no need to handle Omnet here */
-		return false;
-	default:
-		return true;
-	}
-}
-
-static inline char* remove_ifdef_start_and_endtag(char *code, int os_name_length) {
-	unsigned int ifdef_length = strlen("#ifdef ");
-	unsigned int endif_length = strlen("#endif");
-	unsigned int newline_before_endif = 0;
-	char *code_without_ifdef = NULL;
-
-	code_without_ifdef = code + ifdef_length + os_name_length;
-	newline_before_endif = strlen(code_without_ifdef) - endif_length;
-	code_without_ifdef[newline_before_endif] = (char) 0;
-	return code_without_ifdef;
-}
-
-static int old_yylineno = 0;
-
-static void handle_ifdef(enum ifdef_os os, const char *s) {
-	char *code = NULL;
-	char *code_without_ifdef = NULL;
-	int os_name_length = get_os_name_length(os);
-
-	if (os_name_length == -1) {
-		fprintf(stderr, "handle_ifdef with unknown os called.\n");
-		exit(1);
-	}
-
-	if (ignore_ifdef(os)) {
-		return;
-	}
-
-	if (ifdef_stack_ptr >= MAX_IFDEF_DEPTH) {
-		fprintf(stderr, "Ifdefs nested too deeply");
-		exit(1);
-	}
-
-	code = strdup(s);
-
-	// keep track of the current value of yylineno, because we need to restore it later (see EOF-Condition),
-	// otherwise all ifdefs that were interpreted will count twice in yylineno,
-	// which will mess up the value in yylineno.
-	old_yylineno = yylineno;
-
-	code_without_ifdef = remove_ifdef_start_and_endtag(code, os_name_length);
-	ifdef_stack[ifdef_stack_ptr++] = YY_CURRENT_BUFFER;
-	yy_switch_to_buffer(yy_scan_string(code_without_ifdef));
-
-	free(code);
-}
-
 %}
 
 %{
@@ -254,35 +151,6 @@ cpp_comment	\/\/[^\n]*\n
  */
 c_comment	\/\*(([^*])|(\*[^\/]))*\*\/
 
-/* This matches the following platform specific #ifdef-forms:
- *   #ifdef Linux   => Code that only Linux hosts should execute
- *   #ifdef FreeBSD => Code that only FreeBSD hosts should execute
- *   #ifdef NetBSD  => Code that only NetBSD hosts should execute
- *   #ifdef OpenBSD => Code that only OpenBSD hosts should execute
- *   #ifdef Omnet   => Code that only an Omnet based simulation should execute
- *   #ifdef Apple   => Code that only Apple hosts should execute
- *   #ifdef Solaris => Code that only Solaris hosts should execute
- *
- *   the pattern for using #ifdef is like this:
- *   #ifdef Linux
- *   (specific code only for linux)
- *   #endif
- */
-
-/* these are the tags that identify the start and ending of an ifdef block */
-ifdef_begin #ifdef[ ]
-ifdef_end   #endif
-/* end_matcher actually matches everything except the "#endif" tag. */
-end_matcher (([^#])|(#[^e])|(#e[^n])|(#en[^d])|(#end[^i])|(#endi[^f]))*
-
-ifdef_freebsd	{ifdef_begin}(?i:FreeBSD){end_matcher}{ifdef_end}
-ifdef_linux	{ifdef_begin}(?i:Linux){end_matcher}{ifdef_end}
-ifdef_netbsd	{ifdef_begin}(?i:NetBSD){end_matcher}{ifdef_end}
-ifdef_openbsd	{ifdef_begin}(?i:OpenBSD){end_matcher}{ifdef_end}
-ifdef_omnet	{ifdef_begin}(?i:Omnet){end_matcher}{ifdef_end}
-ifdef_apple	{ifdef_begin}(?i:Apple){end_matcher}{ifdef_end}
-ifdef_solaris	{ifdef_begin}(?i:Solaris){end_matcher}{ifdef_end}
-
 /* The regexp for code snippets is analogous to that for C comments.
  * Here is a summary of the regexp for code snippets:
  *   %{
@@ -319,6 +187,13 @@ v11	[:][:]ffff[:](0){1,4}[:]{ipv4_addr}
 v12	({seg}[:]){1,4}[:]{ipv4_addr}
 ipv6_addr ({v0}|{v1}|{v2}|{v3}|{v4}|{v5}|{v6}|{v7}|{v8}|{v9}|{v10}|{v11}|{v12})
 
+%{
+static int last_ifdef_yylineno = 0;
+extern const char* current_script_path;
+%}
+
+%x IFDEF_VARIABLE IFDEF_COMMENT
+
 %%
 sa_family			return SA_FAMILY;
 sin_port			return SIN_PORT;
@@ -700,24 +575,46 @@ NULL				return NULL_;
 [ \t\n]+			/* ignore whitespace */;
 {cpp_comment}			/* ignore C++-style comment */;
 {c_comment}			/* ignore C-style comment */;
-{ifdef_freebsd}			handle_ifdef(FreeBSD_IFDEF, yytext);
-{ifdef_linux}			handle_ifdef(Linux_IFDEF, yytext);
-{ifdef_netbsd}			handle_ifdef(NetBSD_IFDEF, yytext);
-{ifdef_openbsd}			handle_ifdef(OpenBSD_IFDEF, yytext);
-{ifdef_omnet}			handle_ifdef(Omnet_IFDEF, yytext);
-{ifdef_apple}			handle_ifdef(Apple_IFDEF, yytext);
-{ifdef_solaris}			handle_ifdef(Solaris_IFDEF, yytext);
+#ifdef				{
+					if (last_ifdef_yylineno != 0) {
+						die("%s:%d: #ifdef already seen in line %d\n",
+						    current_script_path, yylineno, last_ifdef_yylineno);
+					} else {
+						last_ifdef_yylineno = yylineno;
+						BEGIN(IFDEF_VARIABLE);
+					}
+				}
+<IFDEF_VARIABLE>[ \t\n]+	;
+<IFDEF_VARIABLE>[a-zA-Z][a-zA-Z0-9_]+ {
+					if (definition_find(in_config->defines, yytext) != NULL) {
+						BEGIN(INITIAL);
+					} else {
+						BEGIN(IFDEF_COMMENT);
+					}
+				}
+<IFDEF_VARIABLE>[^a-zA-Z]	{
+					die("%s:%d: #ifdef must be followed by name\n",
+					    current_script_path, yylineno);
+				}
+<IFDEF_COMMENT>(.|\n) 		;
+<INITIAL,IFDEF_COMMENT>#endif	{
+					if (last_ifdef_yylineno == 0) {
+						die("%s:%d: missing #ifdef for #endif\n",
+						    current_script_path, yylineno);
+					} else {
+						last_ifdef_yylineno = 0;
+						BEGIN(INITIAL);
+					}
+				}
 {code}				yylval.string = code(yytext);   return CODE;
 {ipv4_addr}			yylval.string = strdup(yytext); return IPV4_ADDR;
 {ipv6_addr}			yylval.string = strdup(yytext); return IPV6_ADDR;
 <<EOF>>				{
-					if ( --ifdef_stack_ptr < 0 ) {
-						yyterminate();
+					if (last_ifdef_yylineno != 0) {
+						die("%s:%d: unterminated #ifdef in line %d\n",
+						    current_script_path, yylineno, last_ifdef_yylineno);
 					} else {
-						// here we need to restore the saved correct value of yylineno
-						yylineno = old_yylineno;
-						yy_delete_buffer(YY_CURRENT_BUFFER);
-						yy_switch_to_buffer(ifdef_stack[ifdef_stack_ptr]);
+						yyterminate();
 					}
 				}
 %%
diff --git a/gtests/net/packetdrill/parser.y b/gtests/net/packetdrill/parser.y
index d00f67756367caf4703d8c8f97d5063a1fdeb796..63908f77b67a6435c4c72bd5f75a4e5d1d5be2ef 100644
--- a/gtests/net/packetdrill/parser.y
+++ b/gtests/net/packetdrill/parser.y
@@ -128,7 +128,7 @@ extern int yywrap(void);
 pthread_mutex_t parser_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /* The input to the parser: the path name of the script file to parse. */
-static const char* current_script_path = NULL;
+const char* current_script_path = NULL;
 
 /* The starting line number of the input script statement that we're
  * currently parsing. This may be different than yylineno if bison had