diff --git a/circle/firewall/models.py b/circle/firewall/models.py
index b4d4299..ccca58d 100644
--- a/circle/firewall/models.py
+++ b/circle/firewall/models.py
@@ -970,6 +970,10 @@ class Firewall(models.Model):
             logger.exception("get_dhcp_clients failed")
         return {}
 
+    @models.permalink
+    def get_absolute_url(self):
+        return ('network.firewall', None, {'pk': self.pk})
+
 
 class Domain(models.Model):
     name = models.CharField(max_length=40, validators=[val_domain],
diff --git a/circle/network/forms.py b/circle/network/forms.py
index af1417c..f356713 100644
--- a/circle/network/forms.py
+++ b/circle/network/forms.py
@@ -23,8 +23,10 @@ from crispy_forms.helper import FormHelper
 from crispy_forms.layout import Layout, Fieldset, Div, Submit, BaseInput
 from crispy_forms.bootstrap import FormActions, FieldWithButtons, StrictButton
 
-from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem,
-                             Rule, VlanGroup, SwitchPort)
+from firewall.models import (
+    Host, Vlan, Domain, Group, Record, BlacklistItem, Rule, VlanGroup,
+    SwitchPort, Firewall
+)
 
 
 class LinkButton(BaseInput):
@@ -88,6 +90,21 @@ class DomainForm(ModelForm):
         model = Domain
 
 
+class FirewallForm(ModelForm):
+    helper = FormHelper()
+    helper.layout = Layout(
+        Div(Fieldset('', 'name', ) ),
+        FormActions(
+            Submit('submit', _("Save")),
+            LinkButton('back', _("Back"),
+                       reverse_lazy('network.firewall_list'))
+        )
+    )
+
+    class Meta:
+        model = Firewall
+
+
 class GroupForm(ModelForm):
     helper = FormHelper()
     helper.layout = Layout(
diff --git a/circle/network/static/network/network.less b/circle/network/static/network/network.less
index 2d4551c..ff6d1c6 100644
--- a/circle/network/static/network/network.less
+++ b/circle/network/static/network/network.less
@@ -11,3 +11,20 @@
   text-align: center;
   width: 60px;
 }
+
+body {
+  padding-top: 40px;
+}
+
+/* note: this doesn't really work */
+a i:hover {
+  text-decoration: none;
+}
+
+footer {
+  margin-top: 45px;
+}
+
+.messagelist {
+  margin-top: 25px;
+}
diff --git a/circle/network/tables.py b/circle/network/tables.py
index b966ab6..6b38a5b 100644
--- a/circle/network/tables.py
+++ b/circle/network/tables.py
@@ -233,3 +233,14 @@ class HostRecordsTable(Table):
         fields = ("type", "fqdn")
         order_by = ("name", )
         empty_text = _("No records.")
+
+
+class FirewallTable(Table):
+    pk = LinkColumn('network.firewall', args=[A('pk')],
+                    verbose_name="ID")
+
+    class Meta:
+        model = SwitchPort
+        attrs = {'class': 'table table-striped'}
+        fields = ('pk', 'name', )
+        order_by = 'pk'
diff --git a/circle/network/templates/network/base.html b/circle/network/templates/network/base.html
index 3cdba7d..6df77d3 100644
--- a/circle/network/templates/network/base.html
+++ b/circle/network/templates/network/base.html
@@ -6,32 +6,6 @@
 
 {% block title-site %}{% trans "Network" %} | CIRCLE{% endblock %}
 
-{% block extra_link %}
-  <link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:200,400&amp;subset=latin,latin-ext' rel='stylesheet' type='text/css'>
-  <link href="{% static "network/network.css" %}" rel="stylesheet">
-{% endblock %}
-
-{% block extra_css %}
-<style type="text/css">
-  body {
-    padding-top:40px;
-  }
-
-  /* note: this doesn't really work */
-  a i:hover {
-      text-decoration: none;
-  }
-
-  footer {
-      margin-top: 45px;
-  }
-
-  .messagelist {
-      margin-top: 25px;
-  }
-</style>
-{% endblock %}
-
 {% block navbar-brand %}
   <a class="navbar-brand" href="{% url "network.index" %}">CIRCLE Network</a>
 {% endblock %}
diff --git a/circle/network/templates/network/firewall-create.html b/circle/network/templates/network/firewall-create.html
new file mode 100644
index 0000000..4fa1bd4
--- /dev/null
+++ b/circle/network/templates/network/firewall-create.html
@@ -0,0 +1,18 @@
+{% extends "network/base.html" %}
+{% load i18n %}
+{% load staticfiles %}
+{% load crispy_forms_tags %}
+
+{% block title-page %}{% trans "Create" %} | {% trans "firewall" %}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h2>{% trans "Create a new firewall" %}</h2>
+</div>
+
+<div class="row">
+  <div class="col-sm-8">
+    {% crispy form %}
+  </div>
+</div>
+{% endblock %}
diff --git a/circle/network/templates/network/firewall-edit.html b/circle/network/templates/network/firewall-edit.html
new file mode 100644
index 0000000..add9e55
--- /dev/null
+++ b/circle/network/templates/network/firewall-edit.html
@@ -0,0 +1,21 @@
+{% extends "network/base.html" %}
+{% load i18n %}
+{% load staticfiles %}
+{% load crispy_forms_tags %}
+
+{% block title-page %}{{ switch_port_pk }} | {% trans "firewall" %}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <a href="{% url "network.firewall_delete" pk=object.pk %}" class="btn btn-danger pull-right">
+    <i class="fa fa-times-circle"></i> {% trans "Delete this firewall" %}
+  </a>
+  <h2>{{ object.name }}</h2>
+</div>
+
+<div class="row">
+  <div class="col-sm-5">
+    {% crispy form %}
+  </div>
+</div>
+{% endblock %}
diff --git a/circle/network/templates/network/firewall-list.html b/circle/network/templates/network/firewall-list.html
new file mode 100644
index 0000000..eb47779
--- /dev/null
+++ b/circle/network/templates/network/firewall-list.html
@@ -0,0 +1,20 @@
+{% extends "network/base.html" %}
+{% load render_table from django_tables2 %}
+{% load i18n %}
+{% load l10n %}
+{% load staticfiles %}
+
+{% block title-page %}{% trans "Firewalls" %}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <a href="{% url "network.firewall_create" %}" class="btn btn-success pull-right">
+    <i class="fa fa-plus-circle"></i> {% trans "Create a new firewall" %}
+  </a>
+  <h1>{% trans "Firewalls" %}</h1>
+</div>
+
+<div class="table-responsive">
+  {% render_table table %}
+</div>
+{% endblock %}
diff --git a/circle/network/templates/network/menu.html b/circle/network/templates/network/menu.html
index e951e42..b3391f3 100644
--- a/circle/network/templates/network/menu.html
+++ b/circle/network/templates/network/menu.html
@@ -22,25 +22,19 @@
 {% url "network.switch_port_list" as u %}
 {% trans "Switch ports" as t %}
 {% include "network/menu-item.html" with href=u text=t %}
+{% url "network.firewall_list" as u %}
+{% trans "Firewalls" as t %}
+{% include "network/menu-item.html" with href=u text=t %}
 
 <li class="dropdown{% if "groups" in request.path %} active{% endif %}">
-    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Groups <b class="caret"></b></a>
-    <ul class="dropdown-menu">
-        {% url "network.vlan_group_list" as u %}
-        {% trans "Vlan groups" as t %}
-        {% include "network/menu-item.html" with href=u text=t %}
+  <a href="#" class="dropdown-toggle" data-toggle="dropdown">Groups <b class="caret"></b></a>
+  <ul class="dropdown-menu">
+    {% url "network.vlan_group_list" as u %}
+    {% trans "Vlan groups" as t %}
+    {% include "network/menu-item.html" with href=u text=t %}
 
-        {% url "network.group_list" as u %}
-        {% trans "Host groups" as t %}
-        {% include "network/menu-item.html" with href=u text=t %}
-    </ul>
+    {% url "network.group_list" as u %}
+    {% trans "Host groups" as t %}
+    {% include "network/menu-item.html" with href=u text=t %}
+  </ul>
 </li>
-
-{# <li><a href="/vlans/">{% trans "Vlans" %}</a></li> #}
-{# <li><a href="/vlangroups/">{% trans "Vlan groups" %}</a></li> #}
-{# <li><a href="/hostgroups/">{% trans "Host groups" %}</a></li> #}
-{# <li><a href="/hosts/">{% trans "Hosts" %}</a></li> #}
-{# <li><a href="/firewalls/">{% trans "Firewalls" %}</a></li> #}
-{# <li><a href="/domains/">{% trans "Domains" %}</a></li> #}
-{# <li><a href="/records/">{% trans "DNS records" %}</a></li> #}
-{# <li><a href="/blacklist/">{% trans "Blacklist" %}</a></li> #}
diff --git a/circle/network/urls.py b/circle/network/urls.py
index 7973248..dc13f31 100644
--- a/circle/network/urls.py
+++ b/circle/network/urls.py
@@ -16,26 +16,27 @@
 # with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
 
 from django.conf.urls import patterns, url
-from .views import (IndexView,
-                    HostList, HostDetail, HostCreate, HostDelete,
-                    VlanList, VlanDetail, VlanDelete, VlanCreate,
-                    DomainList, DomainDetail, DomainDelete, DomainCreate,
-                    GroupList, GroupDetail, GroupDelete, GroupCreate,
-                    RecordList, RecordDetail, RecordCreate, RecordDelete,
-                    BlacklistList, BlacklistDetail, BlacklistDelete,
-                    BlacklistCreate,
-                    RuleList, RuleDetail, RuleDelete, RuleCreate,
-                    SwitchPortList, SwitchPortDetail, SwitchPortCreate,
-                    SwitchPortDelete,
-                    VlanGroupList, VlanGroupDetail, VlanGroupDelete,
-                    VlanGroupCreate,
-                    remove_host_group, add_host_group,
-                    remove_switch_port_device, add_switch_port_device,
-                    VlanAclUpdateView)
+from .views import (
+    IndexView,
+    HostList, HostDetail, HostCreate, HostDelete,
+    VlanList, VlanDetail, VlanDelete, VlanCreate,
+    DomainList, DomainDetail, DomainDelete, DomainCreate,
+    GroupList, GroupDetail, GroupDelete, GroupCreate,
+    RecordList, RecordDetail, RecordCreate, RecordDelete,
+    BlacklistList, BlacklistDetail, BlacklistDelete, BlacklistCreate,
+    RuleList, RuleDetail, RuleDelete, RuleCreate,
+    SwitchPortList, SwitchPortDetail, SwitchPortCreate, SwitchPortDelete,
+    VlanGroupList, VlanGroupDetail, VlanGroupDelete, VlanGroupCreate,
+    FirewallList, FirewallDetail, FirewallCreate, FirewallDelete,
+    remove_host_group, add_host_group,
+    remove_switch_port_device, add_switch_port_device,
+    VlanAclUpdateView
+)
 
 urlpatterns = patterns(
     '',
     url('^$', IndexView.as_view(), name='network.index'),
+    # blacklist
     url('^blacklist/$', BlacklistList.as_view(),
         name='network.blacklist_list'),
     url('^blacklist/create$', BlacklistCreate.as_view(),
@@ -44,6 +45,8 @@ urlpatterns = patterns(
         name='network.blacklist'),
     url('^blacklist/delete/(?P<pk>\d+)/$', BlacklistDelete.as_view(),
         name="network.blacklist_delete"),
+
+    # domain
     url('^domains/$', DomainList.as_view(), name='network.domain_list'),
     url('^domains/create$', DomainCreate.as_view(),
         name='network.domain_create'),
@@ -51,17 +54,33 @@ urlpatterns = patterns(
         name='network.domain'),
     url('^domains/delete/(?P<pk>\d+)/$', DomainDelete.as_view(),
         name="network.domain_delete"),
+
+    # firewall
+    url('^firewalls/$', FirewallList.as_view(),
+        name='network.firewall_list'),
+    url('^firewalls/create$', FirewallCreate.as_view(),
+        name='network.firewall_create'),
+    url('^firewalls/(?P<pk>\d+)/$', FirewallDetail.as_view(),
+        name='network.firewall'),
+    url('^firewalls/delete/(?P<pk>\d+)/$', FirewallDelete.as_view(),
+        name="network.firewall_delete"),
+
+    # group (host)
     url('^groups/$', GroupList.as_view(), name='network.group_list'),
     url('^groups/create$', GroupCreate.as_view(),
         name='network.group_create'),
     url('^groups/(?P<pk>\d+)/$', GroupDetail.as_view(), name='network.group'),
     url('^groups/delete/(?P<pk>\d+)/$', GroupDelete.as_view(),
         name="network.group_delete"),
+
+    # host
     url('^hosts/$', HostList.as_view(), name='network.host_list'),
     url('^hosts/create$', HostCreate.as_view(), name='network.host_create'),
     url('^hosts/(?P<pk>\d+)/$', HostDetail.as_view(), name='network.host'),
     url('^hosts/delete/(?P<pk>\d+)/$', HostDelete.as_view(),
         name="network.host_delete"),
+
+    # record
     url('^records/$', RecordList.as_view(), name='network.record_list'),
     url('^records/create$', RecordCreate.as_view(),
         name='network.record_create'),
@@ -69,10 +88,14 @@ urlpatterns = patterns(
         name='network.record'),
     url('^records/delete/(?P<pk>\d+)/$', RecordDelete.as_view(),
         name="network.record_delete"),
+
+    # rule
     url('^rules/$', RuleList.as_view(), name='network.rule_list'),
     url('^rules/create$', RuleCreate.as_view(), name='network.rule_create'),
     url('^rules/(?P<pk>\d+)/$', RuleDetail.as_view(),
         name='network.rule'),
+
+    # switchport
     url('^switchports/$', SwitchPortList.as_view(),
         name='network.switch_port_list'),
     url('^switchports/create$', SwitchPortCreate.as_view(),
@@ -81,6 +104,8 @@ urlpatterns = patterns(
         name='network.switch_port'),
     url('^switchports/delete/(?P<pk>\d+)/$', SwitchPortDelete.as_view(),
         name="network.switch_port_delete"),
+
+    # vlan
     url('^vlans/$', VlanList.as_view(), name='network.vlan_list'),
     url('^vlans/create$', VlanCreate.as_view(), name='network.vlan_create'),
     url('^vlans/(?P<vid>\d+)/$', VlanDetail.as_view(), name='network.vlan'),
@@ -88,6 +113,8 @@ urlpatterns = patterns(
         name='network.vlan-acl'),
     url('^vlans/delete/(?P<vid>\d+)/$', VlanDelete.as_view(),
         name="network.vlan_delete"),
+
+    # vlangroup
     url('^vlangroups/$', VlanGroupList.as_view(),
         name='network.vlan_group_list'),
     url('^vlangroups/create$', VlanGroupCreate.as_view(),
diff --git a/circle/network/views.py b/circle/network/views.py
index bcb1c7a..c964d72 100644
--- a/circle/network/views.py
+++ b/circle/network/views.py
@@ -25,15 +25,20 @@ from django.http import HttpResponse, Http404
 
 from django_tables2 import SingleTableView
 
-from firewall.models import (Host, Vlan, Domain, Group, Record, BlacklistItem,
-                             Rule, VlanGroup, SwitchPort, EthernetDevice)
+from firewall.models import (
+    Host, Vlan, Domain, Group, Record, BlacklistItem, Rule, VlanGroup,
+    SwitchPort, EthernetDevice, Firewall)
 from vm.models import Interface
-from .tables import (HostTable, VlanTable, SmallHostTable, DomainTable,
-                     GroupTable, RecordTable, BlacklistItemTable, RuleTable,
-                     VlanGroupTable, SmallRuleTable, SmallGroupRuleTable,
-                     SmallRecordTable, SwitchPortTable, SmallDhcpTable, )
-from .forms import (HostForm, VlanForm, DomainForm, GroupForm, RecordForm,
-                    BlacklistItemForm, RuleForm, VlanGroupForm, SwitchPortForm)
+from .tables import (
+    HostTable, VlanTable, SmallHostTable, DomainTable, GroupTable,
+    RecordTable, BlacklistItemTable, RuleTable, VlanGroupTable,
+    SmallRuleTable, SmallGroupRuleTable, SmallRecordTable, SwitchPortTable,
+    SmallDhcpTable, FirewallTable
+)
+from .forms import (
+    HostForm, VlanForm, DomainForm, GroupForm, RecordForm, BlacklistItemForm,
+    RuleForm, VlanGroupForm, SwitchPortForm, FirewallForm
+)
 
 from django.contrib import messages
 from django.contrib.messages.views import SuccessMessageMixin
@@ -284,6 +289,50 @@ class DomainDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
         return context
 
 
+class FirewallList(LoginRequiredMixin, SuperuserRequiredMixin,
+                   SingleTableView):
+    model = Firewall
+    table_class = FirewallTable
+    template_name = "network/firewall-list.html"
+    table_pagination = False
+
+
+class FirewallDetail(LoginRequiredMixin, SuperuserRequiredMixin,
+                     SuccessMessageMixin, UpdateView):
+    model = Firewall
+    template_name = "network/firewall-edit.html"
+    form_class = FirewallForm
+    success_message = _(u'Succesfully modified firewall.')
+
+    def get_success_url(self):
+        if 'pk' in self.kwargs:
+            return reverse_lazy('network.firewall', kwargs=self.kwargs)
+
+    def get_context_data(self, **kwargs):
+        context = super(FirewallDetail, self).get_context_data(**kwargs)
+        return context
+
+
+class FirewallCreate(LoginRequiredMixin, SuperuserRequiredMixin,
+                     SuccessMessageMixin, CreateView):
+    model = Firewall
+    template_name = "network/firewall-create.html"
+    form_class = FirewallForm
+    success_message = _(u'Successfully created firewall.')
+
+
+class FirewallDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
+    model = Firewall
+    template_name = "network/confirm/base_delete.html"
+
+    def get_success_url(self):
+        next = self.request.POST.get('next')
+        if next:
+            return next
+        else:
+            return reverse_lazy('network.firewall_list')
+
+
 class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
     model = Group
     table_class = GroupTable