diff -ur trunk/daemon/acl_list.c trunk.new/daemon/acl_list.c
--- trunk/daemon/acl_list.c	2013-03-28 04:14:02.000000000 +0900
+++ trunk.new/daemon/acl_list.c	2013-03-28 03:42:59.000000000 +0900
@@ -105,6 +105,8 @@
 		control = acl_refuse;
 	else if(strcmp(s2, "allow_snoop") == 0)
 		control = acl_allow_snoop;
+	else if(strcmp(s2, "allow_minimal") == 0)
+		control = acl_allow_minimal;
 	else {
 		log_err("access control type %s unknown", str);
 		return 0;
diff -ur trunk/daemon/acl_list.h trunk.new/daemon/acl_list.h
--- trunk/daemon/acl_list.h	2013-03-28 04:14:02.000000000 +0900
+++ trunk.new/daemon/acl_list.h	2013-03-28 03:42:59.000000000 +0900
@@ -58,7 +58,9 @@
 	/** allow full access for recursion (+RD) queries */
 	acl_allow,
 	/** allow full access for all queries, recursion and cache snooping */
-	acl_allow_snoop
+	acl_allow_snoop,
+	/** allow minimal access for recursion queries */
+	acl_allow_minimal
 };
 
 /**
diff -ur trunk/daemon/worker.c trunk.new/daemon/worker.c
--- trunk/daemon/worker.c	2013-03-28 04:14:02.000000000 +0900
+++ trunk.new/daemon/worker.c	2013-03-29 21:09:05.000000000 +0900
@@ -830,6 +830,20 @@
 			(int)edns.udp_size);
 		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
 		edns.udp_size = NORMAL_UDP_SIZE;
+	} else if (edns.edns_present &&
+		edns.udp_size > worker->daemon->cfg->max_udp_size &&
+		c->type == comm_udp) {
+		verbose(VERB_QUERY, "worker request: EDNS bufsize %d exceeds "
+			"max-udp-size, fixed", (int)edns.udp_size);
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		edns.udp_size = worker->daemon->cfg->max_udp_size;
+	}
+	if(edns.edns_present && acl == acl_allow_minimal &&
+		edns.udp_size > NORMAL_UDP_SIZE && c->type == comm_udp) {
+		verbose(VERB_QUERY, "worker request: UDP EDNS0 query matching "
+			"ACL allow_minimal, EDNS bufsize ignored");
+		log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen);
+		edns.udp_size = NORMAL_UDP_SIZE;
 	}
 	if(edns.edns_present && edns.udp_size < LDNS_HEADER_SIZE) {
 		verbose(VERB_ALGO, "worker request: edns is too small.");
diff -ur trunk/doc/example.conf.in trunk.new/doc/example.conf.in
--- trunk/doc/example.conf.in	2013-03-28 04:14:07.000000000 +0900
+++ trunk.new/doc/example.conf.in	2013-03-29 21:18:07.000000000 +0900
@@ -89,6 +89,10 @@
 	# is set with msg-buffer-size). 1480 can solve fragmentation (timeouts).
 	# edns-buffer-size: 4096
 
+	# Maximum UDP response size (not applied to TCP response).
+	# Valid values are 512 to 4096. Default is 4096. 
+	# max-udp-size: 4096
+
 	# buffer size for handling DNS data. No messages larger than this
 	# size can be sent or received, by UDP or TCP. In bytes.
 	# msg-buffer-size: 65552
@@ -160,7 +164,8 @@
 	# to this server. Specify classless netblocks with /size and action.
 	# By default everything is refused, except for localhost.
 	# Choose deny (drop message), refuse (polite error reply),
-	# allow (recursive ok), allow_snoop (recursive and nonrecursive ok)
+	# allow (recursive ok), allow_snoop (recursive and nonrecursive ok),
+	# allow_minimal (recursive ok but UDP response size <= 512 bytes)
 	# access-control: 0.0.0.0/0 refuse
 	# access-control: 127.0.0.0/8 allow
 	# access-control: ::0/0 refuse
diff -ur trunk/doc/unbound.conf.5.in trunk.new/doc/unbound.conf.5.in
--- trunk/doc/unbound.conf.5.in	2013-03-28 04:14:07.000000000 +0900
+++ trunk.new/doc/unbound.conf.5.in	2013-03-29 21:28:20.000000000 +0900
@@ -183,6 +183,10 @@
 of TCP fallback generated is excessive (probably also for this resolver,
 consider tuning the outgoing tcp number).
 .TP
+.B max-udp-size: \fI<number>
+Maximum UDP response size (not applied to TCP response).
+Valid values are 512 to 4096. Default is 4096. 
+.TP
 .B msg\-buffer\-size: \fI<number>
 Number of bytes size of the message buffers. Default is 65552 bytes, enough
 for 64 Kb packets, the maximum DNS message size. No message larger than this
@@ -351,6 +355,11 @@
 also be a valuable debugging tool (when you want to examine the cache 
 contents). In that case use \fIallow_snoop\fR for your administration host.
 .IP
+The action \fIallow_minimal\fR gives access to client from that netblock
+like action \fIallow\fR but the UDP response size is limited to 512 bytes.
+\fIallow_minimal\fR would mitigate DNS reflection attack against
+hosts applied to.
+.IP
 By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
 The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS 
 protocol is not designed to handle dropped packets due to policy, and 
diff -ur trunk/util/config_file.c trunk.new/util/config_file.c
--- trunk/util/config_file.c	2013-03-28 04:14:16.000000000 +0900
+++ trunk.new/util/config_file.c	2013-03-29 20:41:51.000000000 +0900
@@ -201,6 +201,7 @@
 	cfg->control_port = UNBOUND_CONTROL_PORT;
 	cfg->minimal_responses = 0;
 	cfg->rrset_roundrobin = 0;
+	cfg->max_udp_size = 4096;
 	if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) 
 		goto error_exit;
 	if(!(cfg->server_cert_file = strdup(RUN_DIR"/unbound_server.pem"))) 
@@ -407,6 +408,7 @@
 	else S_MEMSIZE("neg-cache-size:", neg_cache_size)
 	else S_YNO("minimal-responses:", minimal_responses)
 	else S_YNO("rrset-roundrobin:", rrset_roundrobin)
+	else S_SIZET_NONZERO("max-udp-size:", max_udp_size) 
 	else S_STRLIST("local-data:", local_data)
 	else S_YNO("control-enable:", remote_control_enable)
 	else S_STRLIST("control-interface:", control_ifs)
diff -ur trunk/util/config_file.h trunk.new/util/config_file.h
--- trunk/util/config_file.h	2013-03-28 04:14:16.000000000 +0900
+++ trunk.new/util/config_file.h	2013-03-29 20:10:20.000000000 +0900
@@ -296,6 +296,9 @@
 
 	/* RRSet roundrobin */
 	int rrset_roundrobin;
+
+	/* maximum UDP response size */
+	int max_udp_size;
 };
 
 /**
diff -ur trunk/util/configlexer.lex trunk.new/util/configlexer.lex
--- trunk/util/configlexer.lex	2013-03-28 04:14:16.000000000 +0900
+++ trunk.new/util/configlexer.lex	2013-03-29 20:13:49.000000000 +0900
@@ -291,6 +291,7 @@
 domain-insecure{COLON}		{ YDVAR(1, VAR_DOMAIN_INSECURE) }
 minimal-responses{COLON}	{ YDVAR(1, VAR_MINIMAL_RESPONSES) }
 rrset-roundrobin{COLON}		{ YDVAR(1, VAR_RRSET_ROUNDROBIN) }
+max-udp-size{COLON}		{ YDVAR(1, VAR_MAX_UDP_SIZE) }
 <INITIAL,val>{NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++; }
 
 	/* Quoted strings. Strip leading and ending quotes */
diff -ur trunk/util/configparser.y trunk.new/util/configparser.y
--- trunk/util/configparser.y	2013-03-28 04:14:16.000000000 +0900
+++ trunk.new/util/configparser.y	2013-03-29 20:39:08.000000000 +0900
@@ -105,6 +105,7 @@
 %token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
 %token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
 %token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
+%token VAR_MAX_UDP_SIZE
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -161,7 +162,7 @@
 	server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
 	server_log_queries | server_tcp_upstream | server_ssl_upstream |
 	server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
-	server_minimal_responses | server_rrset_roundrobin
+	server_minimal_responses | server_rrset_roundrobin | server_max_udp_size
 	;
 stubstart: VAR_STUB_ZONE
 	{
@@ -865,9 +866,10 @@
 		OUTYY(("P(server_access_control:%s %s)\n", $2, $3));
 		if(strcmp($3, "deny")!=0 && strcmp($3, "refuse")!=0 &&
 			strcmp($3, "allow")!=0 && 
-			strcmp($3, "allow_snoop")!=0) {
-			yyerror("expected deny, refuse, allow or allow_snoop "
-				"in access control action");
+			strcmp($3, "allow_snoop")!=0 &&
+			strcmp($3, "allow_minimal")!=0 ) {
+			yyerror("expected deny, refuse, allow, allow_snoop or "
+				"allow_minimal in access control action");
 		} else {
 			if(!cfg_str2list_insert(&cfg_parser->cfg->acls, $2, $3))
 				fatal_exit("out of memory adding acl");
@@ -1117,6 +1119,15 @@
 		free($2);
 	}
 	;
+server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG
+	{
+		OUTYY(("P(server_max_udp_size:%s)\n", $2));
+		if(atoi($2) < 512 || atoi($2) > 4096)
+			yyerror("number from 512 to 4096 expected");
+		else cfg_parser->cfg->max_udp_size = atoi($2);
+		free($2);
+	}
+	;
 stub_name: VAR_NAME STRING_ARG
 	{
 		OUTYY(("P(name:%s)\n", $2));
