diff --git a/src/hostinfo.py b/src/hostinfo.py
index c18ecbe16feffd7c637d25e71724d44ba99a27dd..4d3b3412744fb231a96a3ff875ff2cbaf3010924 100755
--- a/src/hostinfo.py
+++ b/src/hostinfo.py
@@ -233,9 +233,9 @@ if __name__ == '__main__':
     optParser.add_argument("--ifconfig",
                            action="store", metavar="DIR",
                            help="Generate DIR/ifcfg-eth*")
-    optParser.add_argument("--kickstart",
-                           action="store",  metavar="DIR",
-                           help="kickstart files should be fetched from DIR")
+    optParser.add_argument("--kickstart", 
+                           action="store",  metavar="PREFIX",
+                           help="kickstart file PREFIX")
     optParser.add_argument("--macosx_auto",
                            action="store", metavar="DIR",
                            help="Generate MacOSX autmount maps")
@@ -288,13 +288,8 @@ if __name__ == '__main__':
             file["%s/%s" % (options.automount, f)] = c
 
     if options.dhcpd:
-        if options.kickstart:
-            kickstart = os.path.abspath(options.kickstart)
-        else:
-            kickstart = None
-            pass
         file["%s/dhcpd.conf" % options.dhcpd] = hostinfo.dhcpd.generate(
-            tree, host, kickstart, next_server=options.next_server)
+            tree, options)
         pass
 
     if options.dhcpd6:
diff --git a/src/hostinfo/dhcpd.py b/src/hostinfo/dhcpd.py
index e1f69efd718669b31589269b8c82453d18e50568..9654a47ffdd76619924f13af852d1a854bcac805 100755
--- a/src/hostinfo/dhcpd.py
+++ b/src/hostinfo/dhcpd.py
@@ -3,19 +3,120 @@ import os
 from hostinfo.util import ntoa, aton
 import hostinfo.util as util
 
-CLASS_PXECLIENT = """
-  class "pxeclient" {
-    match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
-      next-server %(next_server)s;
+"""
+ddns-update-style none;
+authoritative;
+
+option space PXE;
+option PXE.mtftp-ip    code 1 = ip-address;
+option PXE.mtftp-cport code 2 = unsigned integer 16;
+option PXE.mtftp-sport code 3 = unsigned integer 16;
+option PXE.mtftp-tmout code 4 = unsigned integer 8;
+option PXE.mtftp-delay code 5 = unsigned integer 8;
+option arch code 93 = unsigned integer 16; # RFC4578
+
+shared-network "MAC(02:64:01:00:00:02)" {
+
+  subnet 192.168.73.0 netmask 255.255.255.0 {
+    option subnet-mask 255.255.255.0;
+    option broadcast-address 192.168.73.255;
+    option routers 192.168.73.1;
+    option domain-name "i.control.lth.se";
+    option domain-name-servers 192.168.73.1;
+    default-lease-time 14400; # 4 hours
+    max-lease-time 86400; # 1 day
+    get-lease-hostnames true;
+    use-host-decl-names on;
+  
+    class "pxeclient" {
+      match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
+      next-server dist-01.i.control.lth.se;
       if option arch = 00:06 {
-        filename "pxelinux/bootia32.efi";
+        filename "bootia32.efi";
       } else if option arch = 00:07 {
-        filename "pxelinux/bootx64.efi";
+        filename "bootx64.efi";
       } else {
         filename "pxelinux.0";
       }
     }
+
+    class "anaconda" {
+      match if substring (option vendor-class-identifier, 0, 14) = "anaconda-Linux";
+      match hardware;
+      next-server dist-01.i.control.lth.se;
+    }
+
+    pool { 
+      allow members of "pxeclient"; 
+      #allow members of "anaconda"; 
+      range 192.168.73.100 192.168.73.119;
+    }
+      
+    pool { 
+      allow members of "anaconda"; 
+      range 192.168.73.200 192.168.73.219;
+    }
+      
+    host test.control.lth.se {
+      hardware ethernet 02:13:10:10:18:51;
+      option host-name "test";
+      #filename "/bootserver/kickstart/Fedora-19-qemu-dist";
+    }
+
+    subclass "anaconda" 1:02:13:10:10:18:51 {
+    #  filename "/bootserver/kickstart/Fedora-19-qemu-dist";
+    }
+  }
+  
+}
+"""
+
+PXE_ALLOW="""
+option space PXE;
+option PXE.mtftp-ip    code 1 = ip-address;
+option PXE.mtftp-cport code 2 = unsigned integer 16;
+option PXE.mtftp-sport code 3 = unsigned integer 16;
+option PXE.mtftp-tmout code 4 = unsigned integer 8;
+option PXE.mtftp-delay code 5 = unsigned integer 8;
+option arch code 93 = unsigned integer 16; # RFC4578
+
+class "pxeclient_allow" {
+  match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
+  if option arch = 00:06 {
+    filename "bootia32.efi";
+  } else if option arch = 00:07 {
+    filename "bootx64.efi";
+  } else {
+    filename "pxelinux.0";
+  }
+}
+"""
+
+PXE_DENY="""
+class "pxeclient_deny" {
+  match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
+  ignore booting;
+}
+"""
+
+CLASS_PXECLIENT = """
+class "pxeclient" {
+  match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
+    next-server %(next_server)s;
+    if option arch = 00:06 {
+      filename "bootia32.efi";
+    } else if option arch = 00:07 {
+      filename "bootx64.efi";
+    } else {
+      filename "pxelinux.0";
+    }
   }
+}
+
+class "noboot" {
+  match if substring (option vendor-class-identifier, 0, 9) != "PXEClient";
+  ignore booting;
+}
 """
 def MacOS_NETBOOT(dhcphost):
     if not os.path.exists('/local/macos'):
@@ -61,248 +162,196 @@ class "AppleNBI-ppc" {
 
 """ % (dhcphost, dhcphost)
     
-def generate(tree, dhcphost, kickstart, next_server=None):
-    #
-    # A. Get interfaces to serve
-    #
-    interface = []
-    for ds in tree._host_._interface_._dhcpserver_:
-        if dhcphost in ds.name[0::1]:
-            # Host is dhcpserver for this interface
-            dhcpip = ds._parent._ip_[0].address[0]
-            interface.append(ds._parent)
-            pass
-        pass
-    if not interface:
-        raise Exception("%s is not a dhcpserver" % dhcphost)
+def generate(tree, options):
+    result = util.StringArray()
+    dhcp = set(filter(lambda d: options.host in d.name[0::1],
+                     tree._host_._interface_._ip_._dhcpserver_))
+    assert dhcp, '%s is not a dhcp server' % options.host
 
-    #
-    # B. Emit header
-    #
-    result = "ddns-update-style none;\n"
-    result += "authoritative;\n"
-    result += MacOS_NETBOOT(dhcpip)
+    result.append_lines("""
+      |ddns-update-style none;
+      |authoritative;
+      |get-lease-hostnames true;
+      |use-host-decl-names on;
+    """)
+    if get_pxeboot(dhcp) == 'only':
+        result += PXE_ALLOW
+        pass
+    else:
+        result += PXE_DENY
+        pass
+    #result += MacOS_NETBOOT(dhcpip)
 
-    #
-    # C. Emit subnet declarations for each interface
-    #
+    interface = set(map(lambda d: d._parent._parent, dhcp))
     for i in interface:
-        address = []
-        vlan = []
-        for ip in i._ip_:
-            if ip.alias[0] and ip.address[0]:
-                # Alias addresses
-                address.append(ip.address[0])
-                pass
-            elif ip.vlan[0] and ip.address[0]:
-                vlan.append(ip.address[0])
-                pass
-            elif ip.address[0]:
-                address.append(ip.address[0])
-                pass
-            pass
-        result += 'shared-network "MAC(%s)" {\n\n' % i.ethernet[0]
-        result += emit_network(tree, dhcphost, address, kickstart,
-                               interface=i, next_server=next_server)
-        result += "}\n"
-        for v in vlan:
-            result += emit_network(tree, dhcphost, [ v ], kickstart,
-                                   next_server=next_server)
-            pass
+        result += emit_interface(tree, options, i)
         pass
-    return result
-                    
-def emit_network(tree, dhcphost, addr, kickstart, 
-                 interface=None, next_server=None):
-    result = ""
-    subnet = {}
-    for s in filter(util.network, tree._subnet_):
-        n = util.network(s)
-        for a in addr:
-            if util.address(a) in n:
-                subnet[n] = s
-                pass
-            pass
-        pass
-    
-    for n in sorted(subnet):
-        for l in emit_subnet(tree, subnet[n], dhcphost, kickstart,
-                             interface=interface, 
-                             next_server=next_server).split("\n"):
-            result += "  %s\n" % l
-            pass
+    served_networks = map(lambda d: util.network(get_subnet(tree, d._parent)), 
+                          dhcp)
+    result += emit_hosts(tree, options, served_networks)
+    return str(result)
+
+def emit_interface(tree, options, interface):
+    result = util.StringArray()
+    subnet = dict(map(lambda d: (get_subnet(tree, d._parent), d),
+                      interface._ip_._dhcpserver_))
+    result += 'shared-network "MAC(%s)" {' % interface.ethernet[0]
+    for sn,dhcp in map(lambda n: (n, subnet[n]), sorted(subnet)): 
+        result += emit_network(tree, options, sn, dhcp).indent()
         pass
+    result += "}"
     return result
 
-def emit_subnet(tree, subnet, dhcphost, kickstart, 
-                interface=None, next_server=None):
-    result = ""
+def emit_network(tree, options, subnet, dhcp):
+    result = util.StringArray()
     net = util.network(subnet)
-    if next_server == None:
-        next_server = dhcphost
-        pass
-    static = {}
-    dynamic = set()
-    never = []
-    for ip in tree._host_._interface_._ip_:
-        # Find all hosts that belong to this network
-        a = util.address(ip)
-        if a:
-            a = util.address(ip)
-            if a in net:
-                if ip.dynamic[0] == dhcphost:
-                    # Dynamic address served by this host
-                    dynamic.add(a)
-                    pass
-                else:
-                    static[ip.name[0:]] = ip
-                    pass
-                pass
+    pxeboot = get_pxeboot(dhcp)
+    result.append_lines("""
+      |subnet %(network)s netmask %(netmask)s {
+      |  option subnet-mask %(netmask)s;
+      |  option broadcast-address %(broadcast)s;
+      |  server-name "%(host)s";
+      |  default-lease-time 14400; # 4 hours
+      |  max-lease-time 86400; # 1 day
+      """ % dict(network=net.network,
+                 netmask=net.netmask,
+                 broadcast=net.broadcast,
+                 host=options.host))
+    if dhcp.first[0] and dhcp.last[0]:
+        first = util.address(dhcp.first[0])
+        last = util.address(dhcp.last[0])
+        assert first in net, '%s not part of %s' % (first, net)
+        assert last in net, '%s not part of %s' % (last, net)
+        if pxeboot == 'no':
+            result += "  range %s %s;" % (first, last)
             pass
-        if ip.never[0]:
-            a = util.address(ip.never[0])
-            if a in net:
-                never.append(ip.ethernet[0:])
-                pass
+        elif pxeboot == 'only':
+            result.append_lines("""
+              |  pool {
+              |    allow members of "pxeclient";
+              |    range %(first)s %(last)s;
+              |  }""" % dict(first=first, last=last))
             pass
         pass
-    result += "subnet %s netmask %s {\n" % (net.network, net.netmask)
-    result += "  option subnet-mask %s;\n" % net.netmask
-    result += "  option broadcast-address %s;\n" % net.broadcast
+    result += emit_subnet_info(subnet).indent()
+    result += '}'
+    return result
+
+def emit_subnet_info(subnet):
+    result = util.StringArray()
     if subnet.gateway[0]:
-        result += "  option routers %s;\n" % subnet.gateway[0]
+        result += "option routers %s;" % subnet.gateway[0]
         pass
     if subnet.domain[0]:
-        result += "  option domain-name \"%s\";\n" % subnet.domain[0]
+        result += "option domain-name \"%s\";" % subnet.domain[0]
         pass
     if subnet.name_servers[0]:
-        result += "  option domain-name-servers %s;\n" % subnet.name_servers[0]
+        result += "option domain-name-servers %s;" % subnet.name_servers[0]
         pass
     if subnet.ntp_servers[0]:
-        result += "  option ntp-servers %s;\n" % (subnet.ntp_servers[0])
+        result += "option ntp-servers %s;" % (subnet.ntp_servers[0])
         pass
-    #
-    # Emit dynamic hosts
-    #
-    result += "  default-lease-time 14400; # 4 hours\n"
-    result += "  max-lease-time 86400; # 1 day\n"
-    for s in interface_subnets_matching(interface, net):
-        if s.first[0] and s.last[0]:
-            result += "  range %s %s;\n" % (util.address(s.first[0]),
-                                            util.address(s.last[0]))
+    result += ''
+    return result
+
+def emit_hosts(tree, options, networks):
+    result = util.StringArray()
+    def match(a):
+        return filter(lambda n: a in n, networks)
+    static = {}
+    never = {}
+    for ip in tree._host_._interface_._ip_:
+        # Find all hosts that associated with this network
+        ethernet = ip.ethernet[0:]
+        if not ethernet:
+            continue
+        if ethernet.lower() != ethernet:
+            raise util.HostinfoException('%s not lower-case' % ethernet)
+        if ip.never[0]:
+            if match(util.address(ip.never[0])):
+                if not ethernet in never:
+                    never[ethernet] = []
+                    pass
+                never[ethernet].append(ip)
+                pass
+            continue
+        if match(util.address(ip)):
+            if not ethernet in static:
+                static[ethernet] = []
+                pass
+            static[ethernet].append(ip)
             pass
         pass
-    # FIXME: Old style dynamic...
-    min = 0
-    max = 0
-    for d in sorted(dynamic):
-        if d == max + 1:
-            max = d
+    def by_name(ether_ip_dict):
+        result = {}
+        for e in ether_ip_dict:
+            name = set(map(lambda n: n.name[0:], ether_ip_dict[e]))
+            if len(name) != 1:
+                raise util.HostinfoException('Multiple names %s' % name,
+                                             where=ether_ip_dict[e])
+            name = name.pop()
+            result[name] = (e, ether_ip_dict[e])
             pass
-        else:
-            if min:
-                result += "  range %s %s;\n" % (min, max)
-                pass
-            min = d
-            max = d
+        for name in sorted(result):
+            ether, ip = result[name]
+            yield name, ether, ip
             pass
         pass
-    if min:
-        result += "  range %s %s;\n" % (min, max)
-        pass
-    result += "  get-lease-hostnames true;\n"
-    result += "  use-host-decl-names on;\n"
-
-    #
-    # Emit static hosts
-    #
-    result += "\n  group {\n"
-    result += "    default-lease-time 315360000; # 10 year\n"
-    result += "    max-lease-time     315360000; # 10 years\n\n"
 
-    sk = static.keys()
-    sk.sort()
-    for i in [static[x] for x in sk]:
-        ether = i._parent.ethernet[0]
-        ip = i.address[0]
-        if ether:
-            assert ether.lower() == ether, "%s is not lower-case" % ether
-            result += "    host %s.%s {\n"% (i.name[0:], subnet.domain[0])
-            result += "      hardware ethernet %s;\n" % ether
-            result += "      fixed-address %s;\n" % ip
-            result += "      option host-name \"%s\";\n" % i.name[0:]
-            if i._parent._kickstart_:
-                if not kickstart:
-                    raise Exception("--kickstart needed for %s" % i.name[0:])
-                kf = i._parent._kickstart_[0].file[0]
-                result += "      server-name \"%s\";\n" % dhcphost
-                result += "      next-server %s;\n" % next_server
-                result += "      if substring(option vendor-class-identifier, "
-                result += "0, 20) = \n"
-                result += "         \"PXEClient:Arch:00000\" {\n"
-                result += "        filename \"pxelinux.0\";\n"
-                result += "      } else {\n"
-                result += "        filename \"%s/%s\";\n" % (kickstart, kf)
-                result += "      }\n"
-                pass
-            for d in i._dhcp_:
-                result += "      %s\n" % d.value[0]
-                pass
-            result += "    }\n"
+    if static:
+        result.append_lines("""
+          |group { # Static hosts
+          |  default-lease-time 315360000; # 10 year
+          |  max-lease-time     315360000; # 10 years
+          |""")
+        for name, ether, ip in by_name(static):
+            if ether in never:
+                never.pop(ether)
+            result.append_lines("""
+              |  host %(id)s {
+              |    hardware ethernet %(ethernet)s;
+              |    fixed-address %(address)s;
+              |    option host-name "%(name)s";
+              |  }""" % dict(id='%s_%s' % (name, ether.replace(':','')),
+                             name=name,
+                             ethernet=ether,
+                             address=', '.join(map(str, 
+                                                   map(util.address, ip)))))
             pass
+        result += "}"
         pass
-    for e in never:
-        result += "    host never_%s {\n" % e.replace(':', '')
-        result += "      hardware ethernet %s;\n" % e
-        result += "      ignore booting;\n"
-        result += "    }\n"
-        pass
-
-#    result += CLASS_PXECLIENT % { 'next_server': next_server }
-    for s in interface_subnets_matching(interface, net):
-        if s.kickstart[0] != 'any':
-            continue
-        else:
-            continue
-        for i in filter(lambda i: i.ethernet[0] and i._kickstart_, 
-                        tree._host_._interface_):
-            e = i.ethernet[0] 
-            assert e.lower() == e, "%s not lower-case" % e
-            result += "    host %s {\n"% (i.name[0:])
-            result += "      hardware ethernet %s;\n" % e
-            result += "      option host-name \"%s\";\n" % i.name[0:]
-            if not kickstart:
-                raise Exception("--kickstart needed for %s" % i.name[0:])
-            kf = i._kickstart_[0].file[0]
-            result += "      server-name \"%s\";\n" % dhcphost
-            result += "      next-server %s;\n" % next_server
-            result += "      if substring(option vendor-class-identifier, "
-            result += "0, 20) = \n"
-            result += "         \"PXEClient:Arch:00000\" {\n"
-            result += "        filename \"pxelinux.0\";\n"
-            result += "      } else {\n"
-            result += "        filename \"%s/%s\";\n" % (kickstart, kf)
-            result += "      }\n"
-            result += "    }\n"
+    if never:
+        result += 'group { # Disallowed hosts'
+        for name, ether, ip in by_name(never):
+            result.append_lines("""
+              |  host %(name)s { 
+              |    hardware ethernet %(ethernet)s; 
+              |    ignore booting;
+              |  }""" % dict(name='%s_never_%s' % (name, ether.replace(':','')),
+                             ethernet=ether))
             pass
-        result += "    if substring(option vendor-class-identifier, "
-        result += "0, 20) = \n"
-        result += "       \"PXEClient:Arch:00000\" {\n"
-        result += "      filename \"pxelinux.0\";\n"
-        result += "    } else {\n"
-        result += "      filename \"%s/default\";\n" % (kickstart)
-        result += "    }\n"
-        break
-
-    result += "  }\n"
-    result += "}\n"
-    
+        result += "}"
+        pass
     return result
 
-def interface_subnets_matching(interface, net):
-    if not interface:
-        return
-    for s in filter(lambda s: util.network(s) == net, interface._subnet_):
-        yield s
+def get_subnet(tree, ip):
+    a = util.address(ip)
+    def match(subnet):
+        s = util.network(subnet)
+        return s and a in s
+    subnet = filter(match, tree._subnet_)
+    assert len(subnet) == 1, '%s matches multiple networks %s' % (a, subnet)
+    return subnet[0]
+
+def get_pxeboot(dhcp):
+    try:
+        p = set(map(lambda d: d.pxeboot[0] or 'no', dhcp))
+        assert len(p) == 1, "Mixed pxeboot not allowed (%s)" % ','.join(p)
+        result = p.pop()
         pass
-    pass
+    except TypeError:
+        result = dhcp.pxeboot[0] or 'no'
+        pass
+    allowed = [ 'no', 'only' ]
+    assert result in allowed,"pxeboot ('%s') shold be in %s" % (result, allowed)
+    return result
diff --git a/src/hostinfo/dhcpd_ipv6.py b/src/hostinfo/dhcpd_ipv6.py
index 840f9229d3c3b18696ffaff801f25c744e853279..7ea126229621cc76878068900b1b0825a56e0ff0 100755
--- a/src/hostinfo/dhcpd_ipv6.py
+++ b/src/hostinfo/dhcpd_ipv6.py
@@ -2,88 +2,9 @@ import sys
 import os
 import re
 import ipaddr
+import hostinfo.util as util
 
-class AttributeDict(dict): 
-    __getattr__ = dict.__getitem__
-    __setattr__ = dict.__setitem__
-
-class StringArray(list):
-
-    def append_lines(self, lines, sep='|'):
-        for l in map(lambda s: s.strip(), lines.split('\n')):
-            if not l:
-                continue
-            if not l.startswith(sep):
-                raise Exception('%s do not start with "%s"' % (l, pattern))
-            self.append(l[len(sep):])
-        pass
-    
-    def indent(self):
-        for l in self:
-            yield '  %s' % l
-            pass
-        pass
-
-    pass
-
-def generate(tree, options):
-    result = StringArray()
-    interface = dict([ (h.name[0], [ i for i in h._interface_ ])
-                       for h in tree._host_ if h._interface_ ])
-    host = [ h for h in  tree._host_ if h.name[0] == options.host ][0]
-    with_dhcp = list([ i for i in host._interface_ if i._dhcpserver_ ])
-    if not with_dhcp:
-        raise Exception("%s is not a dhcpserver" % options.host)
-    with_ipv6 = [ i for i in with_dhcp if i._ipv6_ ]
-    if not with_ipv6:
-        return ""
-    result.append_lines("""
-      |ddns-update-style none;
-      |authoritative;
-      |allow leasequery;
-      """)
-    for i in with_ipv6:
-        result.append('shared-network "MAC(%s)" {' % i.ethernet[0])
-        result.extend(emit_interface_ipv6(tree, i, options).indent())
-        result.append('}')
-    return '\n'.join(result) + '\n'
-
-def emit_interface_ipv6(tree, interface, options):
-    result = StringArray()
-    subnet = set()
-    for ip in [ ip for ip in interface._ipv6_ if ip.address[0] ]:
-        a = ipaddr.IPAddress(ip.address[0])
-        subnet.update([ (s.prefix[0], s) for s in tree._subnet_ 
-                        if (s.prefix[0] and a in ipaddr.IPNetwork(s.prefix[0])) 
-                        ])
-        pass
-    for k,s in sorted(subnet):
-        result = StringArray()
-        dynamic = [ map(ipaddr.IPAddress, (r.first[0], r.last[0])) 
-                    for r in interface._dhcpserver_._subnet_ 
-                    if r.first[0] and r.last[0] and r.prefix[0] == s.prefix[0] ]
-        result.append('subnet6 %s {' % ipaddr.IPNetwork(s.prefix[0]) )
-        result.extend(emit_subnet_ipv6(tree, s, dynamic, options).indent())
-        result.append('}' )
-        pass
-    return result
-
-def emit_subnet_ipv6(tree, subnet, dynamic, options):
-    result = StringArray()
-    for first,last in dynamic:
-        result.append('range6 %s %s;' % (first, last))
-        pass
-    for name,ip in sorted([ (ip.name[0:], ip) 
-                              for ip in tree._host_._interface_._ipv6_ 
-                              if ip.address[0]]):
-        result.append('host %s.%s {' % (name, subnet.domain[0]))
-        result.append('  fixed-address6 %s;' % ip.address[0])
-        result.append('  hardware ethernet %s;' % ip.ethernet[0:].upper())
-        result.append('}')
-    return result
-
-def ignore():
-    """
+"""
     
 default-lease-time 2592000;
 preferred-lifetime 604800;
@@ -107,3 +28,138 @@ subnet6 2001:ed8:77b5::/64 {
 	range6 2001:ed8:77b5::1 2001:ed8:77b5::ffff:ffff;
 }
 """
+
+def generate(tree, options):
+    result = util.StringArray()
+    dhcp = set(filter(lambda d: options.host in d.name[0::1],
+                     tree._host_._interface_._ipv6_._dhcpserver_))
+    assert dhcp, '%s is not a dhcp6 server' % options.host
+
+    result.append_lines("""
+      |ddns-update-style none;
+      |authoritative;
+      |get-lease-hostnames true;
+      |use-host-decl-names on;
+      |allow leasequery;
+      """)
+    interface = set(map(lambda d: d._parent._parent, dhcp))
+    for i in interface:
+        result += emit_interface(tree, options, i)
+        pass
+    return str(result)
+    
+def emit_interface(tree, options, interface):
+    result = util.StringArray()
+    subnet = dict(map(lambda d: (get_subnet(tree, d._parent), d),
+                      interface._ipv6_._dhcpserver_))
+    result += 'shared-network "MAC(%s)" {' % interface.ethernet[0]
+    for sn,dhcp in map(lambda n: (n, subnet[n]), sorted(subnet)): 
+        result += emit_network(tree, options, sn, dhcp).indent()
+        pass
+    result += "}"
+    return result
+
+def emit_network(tree, options, subnet, dhcp):
+    result = util.StringArray()
+    net = util.network(subnet)
+    result.append_lines("""
+      |subnet6 %(prefix)s {
+      |  server-name "%(host)s";
+      |  default-lease-time 14400; # 4 hours
+      |  max-lease-time 86400; # 1 day
+      """ % dict(prefix=str(net),
+                 netmask=net.netmask,
+                 broadcast=net.broadcast,
+                 host=options.host))
+    if dhcp.first[0] and dhcp.last[0]:
+        first = util.address(dhcp.first[0])
+        last = util.address(dhcp.last[0])
+        assert first in net, '%s not part of %s' % (first, net)
+        assert last in net, '%s not part of %s' % (last, net)
+        result += "  range6 %s %s;" % (first, last)
+        pass
+    result += emit_subnet_info(subnet).indent()
+    result += emit_hosts(tree, options, net).indent()
+    result += '}'
+    return result
+
+def emit_subnet_info(subnet):
+    result = util.StringArray()
+    if subnet.gateway[0]:
+        result += "option routers %s;" % subnet.gateway[0]
+        pass
+    if subnet.domain[0]:
+        result += "option domain-name \"%s\";" % subnet.domain[0]
+        pass
+    if subnet.name_servers[0]:
+        result += "option dhcp6.domain-name-servers %s;" % (
+            subnet.name_servers[0])
+        pass
+    if subnet.ntp_servers[0]:
+        result += "option ntp-servers %s;" % (subnet.ntp_servers[0])
+        pass
+    result += ''
+    return result
+
+def emit_hosts(tree, options, net):
+    result = util.StringArray()
+    static = {}
+    never = {}
+    for ip in tree._host_._interface_._ipv6_:
+        # Find all hosts that associated with this network
+        ethernet = ip.ethernet[0:]
+        if not ethernet:
+            continue
+        assert ethernet.lower() == ethernet, '%s not lower-case' % ethernet
+        if ip.never[0]:
+            a = util.address(ip.never[0])
+            if a in net:
+                never[ip.name[0:]] =ip
+                pass
+            pass
+        else:
+            a = util.address(ip)
+            if a in net:
+                static[ip.name[0:]] = ip
+                pass
+            pass
+        pass
+    if static:
+        result.append_lines("""
+          |group { # Static hosts
+          |  default-lease-time 315360000; # 10 year
+          |  max-lease-time     315360000; # 10 years
+          |""")
+        for h in sorted(static):
+            result.append_lines("""
+              |  host %(name)s {
+              |    hardware ethernet %(ethernet)s;
+              |    fixed-address6 %(address)s;
+              |    option host-name "%(name)s";
+              |  }""" % dict(name=h, 
+                             ethernet=static[h].ethernet[0:],
+                             address=util.address(static[h])))
+        result += "}"
+        pass
+    if never:
+        result.append_lines("""
+          |group { # Disallowed hosts
+          |  ignore booting;
+          |""")
+        for h in sorted(never):
+            result.append(
+                '  host never_%(name)s { hardware ethernet %(ether)s; }' %
+                dict(name=h, ether=never[h].ethernet[0:]))
+        result += "}"
+       
+    return result
+
+def get_subnet(tree, ip):
+    a = util.address(ip)
+    def match(subnet):
+        s = util.network(subnet)
+        return s and a in s
+    subnet = filter(match, tree._subnet_)
+    assert len(subnet) == 1, '%s matches multiple networks %s' % (a, subnet)
+    return subnet[0]
+
diff --git a/src/hostinfo/ifconfig.py b/src/hostinfo/ifconfig.py
index cd7f8ca9434dbc52208f2667fe66cf06ce264b35..4936e1156c63c4e991133268ab179e6db9d9c296 100755
--- a/src/hostinfo/ifconfig.py
+++ b/src/hostinfo/ifconfig.py
@@ -1,4 +1,3 @@
-from hostinfo.util import aton
 import hostinfo.util as util
 import subprocess
 import re
@@ -28,81 +27,60 @@ def is_static(interface):
         pass
     return True
     
-def generate_ifcfgv4(tree, interface):
+def generate_ifcfgv4(tree, interface, search, nameservers):
     config = []
-    static_config = []
-    search = []
-    nameservers = []
-    index = ''
-    for ip in interface._ip_:
-        if not (ip.address[0] and (is_static(interface) or
-                                   (not ip.vlan[0] and
-                                    ip.alias[0]))):
-            continue
-        sub = subnet(tree, ip.address[0])
-        if ip.search[0]: 
-            search.extend(ip.search[0].split())
-            pass
-        static_config.append('IPADDR%s=%s' % (index, ip.address[0]))
-        netmask = ip.netmask[0] or sub and sub.netmask[0] or '255.255.255.255'
-        gateway = ip.gateway[0] or sub and sub.gateway[0]
-        network = ip.network[0] or sub and sub.network[0]
-        broadcast = ip.broadcast[0] or sub and sub.broadcast[0]
-        name_servers = ip.name_servers[0] or sub and sub.name_servers[0]
-        if netmask:
-            static_config.append('NETMASK%s=%s' % (index, netmask))
-            pass
-        if gateway:
-            static_config.append('GATEWAY%s=%s' % (index, gateway))
-            pass
-        if network:
-            static_config.append('NETWORK%s=%s' % (index,network))
-            pass
-        if broadcast:
-            static_config.append('BROADCAST%s=%s' % (index, broadcast))
-            pass
-        
-        if sub:
-            for n in re.split('[, ]+', sub.name_servers[0]):
-                # Domain nameservers
-                if not n in nameservers:
-                    nameservers.append(n)
-                    pass
-                pass
-            for n in interface._nameserver_:
-                if n.domain[0] == sub.domain[0]:
-                    if not '127.0.0.1' in nameservers:
-                        # Insert own address first in nameserver list
-                        nameservers.insert(0, '127.0.0.1')
+    if not is_static(interface):
+        config.append('BOOTPROTO=dhcp')
+        pass
+    else:
+        config.append('BOOTPROTO=none')
+        index = ''
+        for ip in filter(util.address, interface._ip_):
+            a = util.address(ip)
+            netmask = None
+            network = None
+            broadcast = None
+            gateway = None
+            for s in filter(util.network, tree._subnet_ ):
+                n = util.network(s)
+                if a in n:
+                    netmask = n.netmask
+                    network = n.network
+                    broadcast = n.broadcast
+                    gateway = s.gateway[0]
+                    if s.name_servers[0]:
+                        nameservers.extend(re.split('[, ]+', s.name_servers[0]))
+                        pass
+                    if s.domain[0]: 
+                        search.update(s.domain[0].split())
                         pass
                     pass
                 pass
+            if ip.netmask[0]:   netmask = ip.netmask[0]
+            if ip.network[0]:   network = ip.network[0]
+            if ip.broadcast[0]: broadcast = ip.broadcast[0]
+            if ip.gateway[0]:   gateway = ip.gateway[0]
+            if ip.search[0]:    search.update(ip.search[0].split())
+            config.append('IPADDR%s=%s' % (index, a))
+            if netmask:
+                config.append('NETMASK%s=%s' % (index, netmask))
+                pass
+            if gateway:
+                config.append('GATEWAY%s=%s' % (index, gateway))
+                pass
+            if network:
+                config.append('NETWORK%s=%s' % (index, network))
+                pass
+            if broadcast:
+                config.append('BROADCAST%s=%s' % (index, broadcast))
+                pass
+            index = int('0%s' % index)+1
             pass
-        index = int('0%s' % index)+1
-        pass
-    if interface.defroute[0]:
-        config.append('DEFROUTE=%s' % interface.defroute[0])
-        pass
-    index = 0
-    for n in nameservers:
-        index += 1
-        static_config.append('DNS%d=%s' % (index, n))
-        pass
-    if search:
-        static_config.append('SEARCH="%s"' % (' '.join(search)))
-        pass
-    if not is_static(interface):
-        config.insert(0, 'BOOTPROTO=dhcp')
-        config.extend(map(lambda s: '# %s' % s, static_config))
-        pre= '# '
-        pass
-    else:
-        config.insert(0, 'BOOTPROTO=none')
-        config.extend(static_config)
         pass
+
     return config
 
-def generate_ifcfgv6(tree, interface):
+def generate_ifcfgv6(tree, interface, search, nameservers):
     config = []
     if not is_static(interface):
         config.append('IPV6INIT=yes')
@@ -116,9 +94,13 @@ def generate_ifcfgv6(tree, interface):
         address = []
         for ipv6 in filter(util.address, interface._ipv6_):
             a = util.address(ipv6)
-            for s in map(util.network, filter(util.network, tree._subnet_ )):
-                if a in s:
-                    address.append('%s/%d' % (a, s.prefixlen))
+            for s in filter(util.network, tree._subnet_ ):
+                n = util.network(s)
+                if a in n:
+                    address.append('%s/%d' % (a, n.prefixlen))
+                    if s.domain[0]: 
+                        search.update(s.domain[0].split())
+                        pass
                     pass
                 pass
             pass
@@ -136,10 +118,34 @@ def generate_ifcfgv6(tree, interface):
         pass
     return config
 
+def indexed_assign(label, values, start_index=''):
+    index = start_index
+    for v in values:
+        yield '%s%s=%s' % (label, index, v)
+        index = int('0%s' % index)+1
+
 def generate_ifcfg(tree, interface):
     config = []
-    config.extend(generate_ifcfgv4(tree, interface))
-    config.extend(generate_ifcfgv6(tree, interface))
+    search = set()
+    nameservers = []
+    config.extend(generate_ifcfgv4(tree, interface, 
+                                   search=search, nameservers=nameservers))
+    config.extend(generate_ifcfgv6(tree, interface, 
+                                   search=search, nameservers=nameservers))
+    if interface._nameserver_:
+        # Nameservers should ask themselves first
+        nameservers.insert(0, '127.0.0.1')
+        pass
+    # Remove duplicate nameservers
+    nameservers = reduce(lambda r,v: v in r and r or r + [v], 
+                         nameservers, [])
+    config.extend(indexed_assign('DNS', nameservers, 1))
+    if search:
+        config.append('SEARCH="%s"' % (' '.join(search)))
+        pass
+    if interface.defroute[0]:
+        config.append('DEFROUTE=%s' % interface.defroute[0])
+        pass
     return '\n'.join(config) + '\n'
     
 
@@ -192,15 +198,6 @@ def generate(tree, host):
     return result
                                 
 
-def subnet(tree, ip):
-    for s in tree._subnet_:
-        if not s.netmask[0] or not s.network[0]:
-            continue
-        if aton(s.network[0]) == aton(ip) & aton(s.netmask[0]):
-            return s
-        pass
-    return None
-        
 def device(ethernet):
     """ Map ethernet to device name"""
     if not ethernet:
diff --git a/src/hostinfo/util.py b/src/hostinfo/util.py
index 24532f10ad5d66013ef62239032982a0ba518378..3e4c177046094f1d6a2e05fb8b6fa1e522c4a344 100755
--- a/src/hostinfo/util.py
+++ b/src/hostinfo/util.py
@@ -1,6 +1,7 @@
 import ipaddr
 import itertools
 import types
+import hostinfo.parser
 
 def network(s):
     if s.network[0] and s.netmask[0]:
@@ -77,3 +78,60 @@ def by_mac(a, b):
         elif int(aa[i],16) > int(ba[i],16):
             return 1
     return 0
+
+class StringArray(list, object):
+
+    def append_lines(self, lines, sep='|'):
+        for l in map(lambda s: s.strip(), lines.split('\n')):
+            if not l:
+                continue
+            if not l.startswith(sep):
+                raise Exception('%s do not start with "%s"' % (l, sep))
+            self.append(l[len(sep):])
+        pass
+    
+    def __iadd__(self, other):
+        try:
+            self.extend(other.split('\n'))
+            pass
+        except AttributeError:
+            self.extend(other)
+            pass
+        return self
+        
+    def indent(self, indent='  '):
+        for l in self:
+            yield '%s%s' % (indent, l)
+            pass
+        pass
+
+    def __str__(self):
+        return '\n'.join(self) + '\n'
+
+    pass
+
+class HostinfoException(Exception):
+
+    def __init__(self, *args, **kwargs):
+        self.reason = list(args)
+        def get(key, args):
+            return key in args and args[key] or None
+                
+        where = get('where', kwargs)
+        if where:
+            if isinstance(where, hostinfo.parser.Node):
+                where = [ where ]
+                pass
+            for node in where:
+                root = node
+                while root._parent:
+                    root = root._parent
+                    pass
+                self.reason.append('(%s:%d)' % (root._url, node._line))
+                pass
+            pass
+        pass
+
+    def __str__(self):
+        return ' '.join(self.reason)
+    pass