diff --git a/sql/database-create.sql b/sql/database-create.sql
index 9e21a1b7ae0af4192241658b07e7a025255c430d..8fafaa231fa9f421555607e1d9123042d50cd7c4 100644
--- a/sql/database-create.sql
+++ b/sql/database-create.sql
@@ -507,7 +507,7 @@ CREATE TABLE `datapository_databases` (
 
 DROP TABLE IF EXISTS `default_firewall_rules`;
 CREATE TABLE `default_firewall_rules` (
-  `type` enum('ipfw','ipfw2','iptables','ipfw2-vlan','iptables-vlan') NOT NULL default 'ipfw',
+  `type` enum('ipfw','ipfw2','iptables','ipfw2-vlan','iptables-vlan','iptables-dom0','iptables-domU') NOT NULL default 'ipfw',
   `style` enum('open','closed','basic','emulab') NOT NULL default 'basic',
   `enabled` tinyint(4) NOT NULL default '0',
   `ruleno` int(10) unsigned NOT NULL default '0',
@@ -4957,6 +4957,8 @@ CREATE TABLE `virt_nodes` (
   `numeric_id` int(11) default NULL,
   `sharing_mode` varchar(32) default NULL,
   `role` enum('node','bridge') NOT NULL default 'node',
+  `firewall_style` tinytext,
+  `firewall_log` tinytext,
   PRIMARY KEY  (`exptidx`,`vname`),
   UNIQUE KEY `pideid` (`pid`,`eid`,`vname`),
   KEY `pid` (`pid`,`eid`,`vname`)
diff --git a/sql/database-fill.sql b/sql/database-fill.sql
index 522233fb69366d083e26ad64041adb79ea9ff22b..63efe1d25f6811537ee9ebd18ae8723d05941315 100644
--- a/sql/database-fill.sql
+++ b/sql/database-fill.sql
@@ -1109,6 +1109,8 @@ REPLACE INTO table_regex VALUES ('virt_firewalls','fwname','text','redirect','vi
 REPLACE INTO table_regex VALUES ('virt_firewalls','type','text','regex','^(ipfw|ipfw2|iptables|ipfw2-vlan|iptables-vlan)$',0,0,NULL);
 REPLACE INTO table_regex VALUES ('virt_firewalls','style','text','regex','^(open|closed|basic|emulab)$',0,0,NULL);
 
+REPLACE INTO table_regex VALUES ('virt_nodes','firewall_style','text','regex','^(open|closed|basic|emulab)$',0,0,NULL);
+
 REPLACE INTO table_regex VALUES ('mailman_lists','pid_idx','text','redirect','projects:pid_idx',0,0,NULL);
 REPLACE INTO table_regex VALUES ('mailman_lists','password1','text','redirect','default:tinytext',0,0,NULL);
 REPLACE INTO table_regex VALUES ('mailman_lists','password2','text','redirect','default:tinytext',0,0,NULL);
diff --git a/sql/updates/4/387 b/sql/updates/4/387
new file mode 100644
index 0000000000000000000000000000000000000000..8d7fab9c6f0c676f25d7bdb23a8dc162e83e3aef
--- /dev/null
+++ b/sql/updates/4/387
@@ -0,0 +1,33 @@
+#
+# New firewall type.
+#
+use strict;
+use libdb;
+
+sub DoUpdate($$$)
+{
+    my ($dbhandle, $dbname, $version) = @_;
+
+    DBQueryFatal("alter table default_firewall_rules change `type` ".
+		 " `type` enum('ipfw','ipfw2','iptables','ipfw2-vlan',".
+		 "             'iptables-vlan','iptables-dom0', ".
+		 "             'iptables-domU') ".
+		 "  NOT NULL default 'ipfw'");
+
+    if (!DBSlotExists("virt_nodes", "firewall_style")) {
+	DBQueryFatal("alter table virt_nodes add ".
+		     "  `firewall_style` tinytext");
+    }
+    DBQueryFatal("REPLACE INTO table_regex VALUES ".
+		 "('virt_nodes','firewall_style','text','regex',".
+		 "'^(open|closed|basic|emulab)\$',0,0,NULL)");
+    
+    if (!DBSlotExists("virt_nodes", "firewall_log")) {
+	DBQueryFatal("alter table virt_nodes add ".
+		     "  `firewall_log` tinytext");
+    }
+}
+
+# Local Variables:
+# mode:perl
+# End:
diff --git a/tbsetup/ns2ir/node.tcl b/tbsetup/ns2ir/node.tcl
index 3f5894d800a6998b4cacfdfd7d68ca040c597ca8..441eebfc5ef15eec0f3701a1ec4e213c93482395 100644
--- a/tbsetup/ns2ir/node.tcl
+++ b/tbsetup/ns2ir/node.tcl
@@ -1,6 +1,6 @@
 # -*- tcl -*-
 #
-# Copyright (c) 2000-2013 University of Utah and the Flux Group.
+# Copyright (c) 2000-2014 University of Utah and the Flux Group.
 # 
 # {{{EMULAB-LICENSE
 # 
@@ -146,6 +146,9 @@ Node instproc init {s} {
 
     # This is a blockstore thing.
     $self set bstore_agent 0
+
+    # Per node firewall thing.
+    $self set fw_style ""
 }
 
 Bridge instproc init {s} {
@@ -229,6 +232,7 @@ Node instproc updatedb {DB} {
     $self instvar simulated
     $self instvar sharing_mode
     $self instvar topo
+    $self instvar fw_style
     $self instvar X_
     $self instvar Y_
     $self instvar orientation_
@@ -402,6 +406,11 @@ Node instproc updatedb {DB} {
 	lappend values $parent_osid
     }
 
+    if { $fw_style != "" } {
+	lappend fields "firewall_style"
+	lappend values $fw_style
+    }
+
     $sim spitxml_data "virt_nodes" $fields $values
 
     if {$topo != "" && ($type == "robot" || $hwtype_class($type) == "robot")} {
diff --git a/tbsetup/ns2ir/nstb_compat.tcl b/tbsetup/ns2ir/nstb_compat.tcl
index 3f2b0ef60f8bb5eb69a85d1f29f7df25468c2ca8..c2c152e41d47cfd9d7e3c262e6e30a717cf371c9 100644
--- a/tbsetup/ns2ir/nstb_compat.tcl
+++ b/tbsetup/ns2ir/nstb_compat.tcl
@@ -73,6 +73,7 @@ proc tb-set-endnodeshaping {link onoff} {}
 proc tb-set-noshaping {link onoff} {}
 proc tb-set-useveth {link onoff} {}
 proc tb-set-link-encap {link style} {}
+proc tb-set-fw-style {vnode style} {}
 proc tb-set-allowcolocate {lanlink onoff} {}
 proc tb-set-colocate-factor {factor} {}
 proc tb-set-sync-server {node} {}
diff --git a/tbsetup/ns2ir/tb_compat.tcl.in b/tbsetup/ns2ir/tb_compat.tcl.in
index fc636a1c1dda323b39f10a68d8e10850873dfc34..1e20f5b55ec20fa8da00a6c618adbca5d6e49484 100644
--- a/tbsetup/ns2ir/tb_compat.tcl.in
+++ b/tbsetup/ns2ir/tb_compat.tcl.in
@@ -2290,6 +2290,20 @@ proc tb-set-elabinelab-fw-type {type} {
     set elabinelab_fw_type $type
 }
 
+#
+# Set firewall style for an individual node. Really only makes sense
+# for linux nodes with iptables. Might need to add an os_feature.
+#
+proc tb-set-fw-style {vnode style}
+{
+    if ($style != "basic" && $style != "closed" &&
+	$style != "open" && $style != "elabinelab") {
+	perror "\[tb-set-fw-style] $style is not a valid type"
+	return
+    }
+    $vnode set fw_style $style
+}
+
 #
 # Set numeric ID (this is a mote thing)
 #
diff --git a/tmcd/tmcd.c b/tmcd/tmcd.c
index 0d0ec33a8a70274abbe1a75cd3d03a5eaa23a38e..f541fc7eb43f135e9fef917d9ee4679d970839b8 100644
--- a/tmcd/tmcd.c
+++ b/tmcd/tmcd.c
@@ -246,6 +246,7 @@ typedef struct {
 	char		sfshostid[TBDB_FLEN_SFSHOSTID];
 	char		testdb[TBDB_FLEN_TINYTEXT];
 	char		sharing_mode[TBDB_FLEN_TINYTEXT];
+	char		erole[TBDB_FLEN_TINYTEXT];
 	char            privkey[PRIVKEY_LEN+1];
 	char		nodeuuid[TBDB_FLEN_UUID];
         /* This key is a replacement for privkey, on protogeni resources */
@@ -6795,7 +6796,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 "   AS isdedicated_wa, "
 				 " r.genisliver_idx,r.tmcd_redirect, "
 				 " r.sharing_mode,e.geniflags,n.uuid, "
-				 " n.nonfsmounts,e.nonfsmounts AS enonfs "
+				 " n.nonfsmounts,e.nonfsmounts AS enonfs, "
+				 " r.erole "
 				 "FROM nodes AS n "
 				 "LEFT JOIN reserved AS r ON "
 				 "  r.node_id=n.node_id "
@@ -6824,7 +6826,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 "     (SELECT node_id FROM widearea_nodeinfo "
 				 "      WHERE privkey='%s') "
 				 "  AND notmcdinfo_types.attrvalue IS NULL",
-				 37, nodekey);
+				 38, nodekey);
 	}
 	else if (reqp->isvnode) {
 		char	clause[BUFSIZ];
@@ -6860,7 +6862,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 " u.admin,null, "
 				 " r.genisliver_idx,r.tmcd_redirect, "
 				 " r.sharing_mode,e.geniflags,nv.uuid, "
-				 " nv.nonfsmounts,e.nonfsmounts AS enonfs "
+				 " nv.nonfsmounts,e.nonfsmounts AS enonfs, "
+				 " r.erole "
 				 "from nodes as nv "
 				 "left join nodes as np on "
 				 " np.node_id=nv.phys_nodeid "
@@ -6881,7 +6884,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 "left join users as u on "
 				 " u.uid_idx=e.swapper_idx "
 				 "where nv.node_id='%s' and (%s)",
-				 37, reqp->vnodeid, clause);
+				 38, reqp->vnodeid, clause);
 	}
 	else {
 		char	clause[BUFSIZ];
@@ -6910,7 +6913,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 "   as isdedicated_wa, "
 				 " r.genisliver_idx,r.tmcd_redirect, "
 				 " r.sharing_mode,e.geniflags,n.uuid, "
-				 " n.nonfsmounts,e.nonfsmounts AS enonfs "
+				 " n.nonfsmounts,e.nonfsmounts AS enonfs, "
+				 " r.erole "
 				 "from interfaces as i "
 				 "left join nodes as n on n.node_id=i.node_id "
 				 "left join reserved as r on "
@@ -6938,7 +6942,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 				 "  on n.type=dedicated_wa_types.type "
 				 "where (%s) "
 				 "  and notmcdinfo_types.attrvalue is NULL",
-				 37, clause);
+				 38, clause);
 	}
 
 	if (!res) {
@@ -7048,6 +7052,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
 			reqp->geniflags = atoi(row[33]);
 		else
 			reqp->geniflags = 0;
+		if (row[37])
+			strcpy(reqp->erole, row[37]);
 	}
 
 	if (row[9])
@@ -8156,34 +8162,12 @@ COMMAND_PROTOTYPE(doroutelist)
  */
 COMMAND_PROTOTYPE(dorole)
 {
-	MYSQL_RES	*res;
-	MYSQL_ROW	row;
 	char		buf[MYBUFSIZE];
 
-	/*
-	 * Get the erole from the reserved table
-	 */
-	res = mydb_query("select erole from reserved where node_id='%s'",
-			 1, reqp->nodeid);
-
-	if (!res) {
-		error("ROLE: %s: DB Error getting the role of this node!\n",
-		      reqp->nodeid);
-		return 1;
-	}
-
-	if ((int)mysql_num_rows(res) == 0) {
-		mysql_free_result(res);
-		return 0;
-	}
-
-	row = mysql_fetch_row(res);
-	if (! row[0] || !row[0][0]) {
-		mysql_free_result(res);
+	if (! reqp->allocated) {
 		return 0;
 	}
-	sprintf(buf, "%s\n", row[0]);
-	mysql_free_result(res);
+	sprintf(buf, "%s\n", reqp->erole);
 
 	client_writeback(sock, buf, strlen(buf), tcp);
 	if (verbose)
@@ -8572,16 +8556,82 @@ COMMAND_PROTOTYPE(dofwinfo)
 	char		fwname[TBDB_FLEN_VNAME+2];
 	char		fwtype[TBDB_FLEN_VNAME+2];
 	char		fwstyle[TBDB_FLEN_VNAME+2];
+	char		fwlog[TBDB_FLEN_TINYTEXT+1];
 	int		n, nrows;
 	char		*vlan;
 
+	bzero(fwlog, sizeof(fwlog));
+
 	/*
-	 * See if this node's experiment has an associated firewall
-	 *
-	 * XXX will only work if there is one firewall per experiment.
+	 * Containers and shared hosts can each have specific rules.
+	 * The shared host can protect itself with iptables-dom0 rules,
+	 * and containers themselves can be firewalled with iptables-domU
+	 * rules (in the default_firewall_rules table). The main point
+	 * though, is that this done on the virt host dom0, not with a
+	 * separate firewall node. 
 	 */
-	res = mydb_query("select r.node_id,v.type,v.style,v.log,f.fwname,"
-			 "  i.IP,i.mac,f.vlan "
+	if (reqp->isvnode ||
+	    strcmp(reqp->erole, "virthost") == 0 ||
+	    strcmp(reqp->erole, "sharedhost") == 0) {
+		/*
+		 * Since we implement the firewall code on the outside of
+		 * the container (dom0), containers themselves know
+		 * nothing about it, nor can containers act *as* firewall
+		 * nodes for an entire experiment (although this is
+		 * something we should implement some day).  So just tell
+		 * the container not to worry about it.
+		 */
+		if (reqp->asvnode) {
+			goto nofirewall;
+		}
+		res = mydb_query("select firewall_style,firewall_log "
+				 "from virt_nodes "
+				 "where exptidx='%d' and "
+				 "      vname='%s' and "
+				 "      firewall_style is not null ",
+				 2, reqp->exptidx, reqp->nickname);
+		if (!res) {
+			error("FWINFO: %s: DB Error getting firewall info!\n",
+			      reqp->nodeid);
+			return 1;
+		}
+		if ((int)mysql_num_rows(res) == 0) {
+			mysql_free_result(res);
+			goto nofirewall;
+		}
+		row = mysql_fetch_row(res);
+		/*
+		 * Ignore the type, we decide. 
+		 */
+		if (reqp->isvnode) {
+			strcpy(fwtype, "iptables-domU");
+		}
+		else {
+			strcpy(fwtype, "iptables-dom0");
+		}
+		strncpy(fwstyle, row[0], sizeof(fwstyle));
+		if (row[1] && row[1][0])
+			strncpy(fwlog, row[1], sizeof(fwlog));
+		mysql_free_result(res);
+
+		OUTPUT(buf, sizeof(buf),
+		       "TYPE=%s STYLE=%s "
+		       "IN_IF=na OUT_IF=na IN_VLAN=0 OUT_VLAN=0\n",
+		       fwtype, fwstyle);
+		client_writeback(sock, buf, strlen(buf), tcp);
+		if (verbose)
+			info("FWINFO: %s", buf);
+	
+		goto vnodefw;
+	}
+	else {
+		/*
+		 * See if this node's experiment has an associated firewall
+		 *
+		 * XXX will only work if there is one firewall per experiment.
+		 */
+		res = mydb_query("select r.node_id,v.type,v.style,v.log,"
+			 "  f.fwname,i.IP,i.mac,f.vlan "
 			 "from firewalls as f "
 			 "left join reserved as r on"
 			 "  f.pid=r.pid and f.eid=r.eid and f.fwname=r.vname "
@@ -8592,10 +8642,11 @@ COMMAND_PROTOTYPE(dofwinfo)
 			 "and i.role='ctrl'",	/* XXX */
 			 8, reqp->pid, reqp->eid);
 
-	if (!res) {
-		error("FWINFO: %s: DB Error getting firewall info!\n",
-		      reqp->nodeid);
-		return 1;
+		if (!res) {
+			error("FWINFO: %s: DB Error getting firewall info!\n",
+			      reqp->nodeid);
+			return 1;
+		}
 	}
 
 	/*
@@ -8603,6 +8654,7 @@ COMMAND_PROTOTYPE(dofwinfo)
 	 */
 	if ((int)mysql_num_rows(res) == 0) {
 		mysql_free_result(res);
+	nofirewall:
 		strncpy(buf, "TYPE=none\n", sizeof(buf));
 		client_writeback(sock, buf, strlen(buf), tcp);
 		return 0;
@@ -8666,21 +8718,24 @@ COMMAND_PROTOTYPE(dofwinfo)
 	if (verbose)
 		info("FWINFO: %s", buf);
 
+	strncpy(fwtype, row[1], sizeof(fwtype));
+	strncpy(fwstyle, row[2], sizeof(fwstyle));
+	strncpy(fwname, row[4], sizeof(fwname));
+	if (row[3] && row[3][0])
+		strncpy(fwlog, row[3], sizeof(fwlog));
+	mysql_free_result(res);
+
+vnodefw:
 	/*
 	 * Put out info about firewall rule logging
 	 */
-	if (vers > 25 && row[3] && row[3][0]) {
-		OUTPUT(buf, sizeof(buf), "LOG=%s\n", row[3]);
+	if (vers > 25 && fwlog[0]) {
+		OUTPUT(buf, sizeof(buf), "LOG=%s\n", fwlog);
 		client_writeback(sock, buf, strlen(buf), tcp);
 		if (verbose)
 			info("FWINFO: %s", buf);
 	}
 
-	strncpy(fwtype, row[1], sizeof(fwtype));
-	strncpy(fwstyle, row[2], sizeof(fwstyle));
-	strncpy(fwname, row[4], sizeof(fwname));
-	mysql_free_result(res);
-
 	/*
 	 * Return firewall variables
 	 */
@@ -8729,13 +8784,41 @@ COMMAND_PROTOTYPE(dofwinfo)
 			info("FWINFO: %d variables\n", nrows);
 	}
 
+	if (reqp->isvnode) {
+		/*
+		 * Write a var for the sshdport number.
+		 */
+		res = mydb_query("select n.sshdport from nodes as n "
+				 "where n.node_id='%s'",
+				 1, reqp->nodeid);
+
+		if (!res) {
+			error("FWINFO: %s: DB Error getting sshdport!\n",
+			      reqp->nodeid);
+			return 1;
+		}
+
+		if ((int)mysql_num_rows(res)) {
+			row  = mysql_fetch_row(res);
+
+			OUTPUT(buf, sizeof(buf), "VAR=%s VALUE=\"%s\"\n",
+			       "SSHDPORT", row[0]);
+			client_writeback(sock, buf, strlen(buf), tcp);
+		}
+		mysql_free_result(res);
+	}
+
 	/*
 	 * Get the user firewall rules from the DB and return them.
+	 * Note difference for vnodes/vhosts; these can store rules
+	 # for each vnode/vhost in the topo.
 	 */
 	res = mydb_query("select ruleno,rule from firewall_rules "
 			 "where pid='%s' and eid='%s' and fwname='%s' "
 			 "order by ruleno",
-			 2, reqp->pid, reqp->eid, fwname);
+			 2, reqp->pid, reqp->eid,
+			 ((reqp->isvnode || reqp->sharing_mode[0]) ?
+			  reqp->nickname ? fwname));
 	if (!res) {
 		error("FWINFO: %s: DB Error getting firewall rules!\n",
 		      reqp->nodeid);
@@ -8749,10 +8832,9 @@ COMMAND_PROTOTYPE(dofwinfo)
 		       row[0], row[1]);
 		client_writeback(sock, buf, strlen(buf), tcp);
 	}
-
 	mysql_free_result(res);
 	if (verbose)
-	    info("FWINFO: %d user rules\n", nrows);
+		info("FWINFO: %d user rules\n", nrows);
 
 	/*
 	 * Get the default firewall rules from the DB and return them.
@@ -10253,7 +10335,7 @@ COMMAND_PROTOTYPE(doarpinfo)
 	MYSQL_RES	*res;
 	MYSQL_ROW	row;
 	int		nrows, xenvifrouting = 0;
-	char		buf[MYBUFSIZE], erole[32], arptype[32];
+	char		buf[MYBUFSIZE], arptype[32];
 #ifdef GET_SERVERS_FROM_SITEVARS
 	struct serv {
 		char name[8];
@@ -10331,25 +10413,6 @@ COMMAND_PROTOTYPE(doarpinfo)
 	}
 	mysql_free_result(res);
 
-	res = mydb_query("select erole from reserved where node_id='%s'",
-			 1, reqp->nodeid);
-	if (!res) {
-		error("doarpinfo: %s: DB Error checking for reserved.erole\n",
-		      reqp->nodeid);
-		return 1;
-	}
-
-	if (mysql_num_rows(res) == 0 || (row = mysql_fetch_row(res)) == NULL ||
-	    row[0] == NULL) {
-		error("doarpinfo: %s: Could not deterimine erole\n",
-		      reqp->nodeid);
-		mysql_free_result(res);
-		return 1;
-	}
-
-	strncpy(erole, row[0], sizeof(erole));
-	mysql_free_result(res);
-
 	/*
 	 * Get the GW and primary server (boss, ops, fs) info.
 	 */
@@ -10571,7 +10634,7 @@ COMMAND_PROTOTYPE(doarpinfo)
 	/*
 	 * Subbosses get info for all nodes they provide a service for. 
 	 */
-	if (strcmp(erole, "subboss") == 0) {
+	if (strcmp(reqp->erole, "subboss") == 0) {
 		res = mydb_query("select distinct i.node_id,i.IP,i.mac from "
 				 "interfaces as i,subbosses as s where "
 				 "s.node_id=i.node_id and "