diff --git a/ethereum.go b/ethereum.go
index ae0f2eed0ff1216babdc21e946b0c5a3eaabbfba..83243e23c46c01942a435d4cf6f3cf112d5d0abb 100644
--- a/ethereum.go
+++ b/ethereum.go
@@ -8,6 +8,7 @@ import (
 	"github.com/ethereum/ethwire-go"
 	"log"
 	"net"
+	"strconv"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -29,6 +30,7 @@ const (
 type Ethereum struct {
 	// Channel for shutting down the ethereum
 	shutdownChan chan bool
+	quit         chan bool
 	// DB interface
 	//db *ethdb.LDBDatabase
 	db *ethdb.MemDatabase
@@ -48,6 +50,8 @@ type Ethereum struct {
 
 	// Capabilities for outgoing peers
 	serverCaps Caps
+
+	nat NAT
 }
 
 func New(caps Caps) (*Ethereum, error) {
@@ -57,15 +61,30 @@ func New(caps Caps) (*Ethereum, error) {
 		return nil, err
 	}
 
+	/*
+		gateway := net.ParseIP("192.168.192.1")
+		nat := NewNatPMP(gateway)
+		port, err := nat.AddPortMapping("tcp", 30303, 30303, "", 60)
+		log.Println(port, err)
+	*/
+
+	nat, err := Discover()
+	if err != nil {
+		log.Println("UPnP failed", err)
+		return nil, err
+	}
+
 	ethutil.Config.Db = db
 
 	nonce, _ := ethutil.RandomUint64()
 	ethereum := &Ethereum{
 		shutdownChan: make(chan bool),
+		quit:         make(chan bool),
 		db:           db,
 		peers:        list.New(),
 		Nonce:        nonce,
 		serverCaps:   caps,
+		nat:          nat,
 	}
 	ethereum.TxPool = ethchain.NewTxPool()
 	ethereum.TxPool.Speaker = ethereum
@@ -217,6 +236,8 @@ func (s *Ethereum) Start() {
 		go s.peerHandler(ln)
 	}
 
+	go s.upnpUpdateThread()
+
 	// Start the reaping processes
 	go s.ReapDeadPeerHandler()
 
@@ -245,6 +266,8 @@ func (s *Ethereum) Stop() {
 		p.Stop()
 	})
 
+	close(s.quit)
+
 	s.shutdownChan <- true
 
 	s.TxPool.Stop()
@@ -254,3 +277,42 @@ func (s *Ethereum) Stop() {
 func (s *Ethereum) WaitForShutdown() {
 	<-s.shutdownChan
 }
+
+func (s *Ethereum) upnpUpdateThread() {
+	// Go off immediately to prevent code duplication, thereafter we renew
+	// lease every 15 minutes.
+	timer := time.NewTimer(0 * time.Second)
+	lport, _ := strconv.ParseInt("30303", 10, 16)
+	first := true
+out:
+	for {
+		select {
+		case <-timer.C:
+			listenPort, err := s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
+			if err != nil {
+				log.Println("can't add UPnP port mapping:", err)
+				break out
+			}
+			if first && err == nil {
+				externalip, err := s.nat.GetExternalAddress()
+				if err != nil {
+					log.Println("UPnP can't get external address:", err)
+					continue out
+				}
+				log.Println("Successfully bound via UPnP to", externalip, listenPort)
+				first = false
+			}
+			timer.Reset(time.Minute * 15)
+		case <-s.quit:
+			break out
+		}
+	}
+
+	timer.Stop()
+
+	if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
+		log.Println("unable to remove UPnP port mapping:", err)
+	} else {
+		log.Println("succesfully disestablished UPnP port mapping")
+	}
+}
diff --git a/nat.go b/nat.go
new file mode 100644
index 0000000000000000000000000000000000000000..999308eb267696493004f7b31614a1ad56be603b
--- /dev/null
+++ b/nat.go
@@ -0,0 +1,12 @@
+package eth
+
+import (
+	"net"
+)
+
+// protocol is either "udp" or "tcp"
+type NAT interface {
+	GetExternalAddress() (addr net.IP, err error)
+	AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
+	DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
+}
diff --git a/natpmp.go b/natpmp.go
new file mode 100644
index 0000000000000000000000000000000000000000..9a1fb652b1dc54533d2a0530bd1c6e1506553c6d
--- /dev/null
+++ b/natpmp.go
@@ -0,0 +1,54 @@
+package eth
+
+import (
+	natpmp "code.google.com/p/go-nat-pmp"
+	"fmt"
+	"net"
+)
+
+// Adapt the NAT-PMP protocol to the NAT interface
+
+// TODO:
+//  + Register for changes to the external address.
+//  + Re-register port mapping when router reboots.
+//  + A mechanism for keeping a port mapping registered.
+
+type natPMPClient struct {
+	client *natpmp.Client
+}
+
+func NewNatPMP(gateway net.IP) (nat NAT) {
+	return &natPMPClient{natpmp.NewClient(gateway)}
+}
+
+func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
+	response, err := n.client.GetExternalAddress()
+	if err != nil {
+		return
+	}
+	ip := response.ExternalIPAddress
+	addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
+	return
+}
+
+func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
+	description string, timeout int) (mappedExternalPort int, err error) {
+	if timeout <= 0 {
+		err = fmt.Errorf("timeout must not be <= 0")
+		return
+	}
+	// Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
+	response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
+	if err != nil {
+		return
+	}
+	mappedExternalPort = int(response.MappedExternalPort)
+	return
+}
+
+func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
+	// To destroy a mapping, send an add-port with
+	// an internalPort of the internal port to destroy, an external port of zero and a time of zero.
+	_, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
+	return
+}
diff --git a/upnp.go b/natupnp.go
similarity index 56%
rename from upnp.go
rename to natupnp.go
index c84ed04f8dc624d2b8cce41526d99f8eeba07ca6..e4072d0ddb6d966cf9e99eeb7d8b6cbd8a8f38de 100644
--- a/upnp.go
+++ b/natupnp.go
@@ -1,34 +1,5 @@
 package eth
 
-// Upnp code taken from Taipei Torrent license is below:
-// Copyright (c) 2010 Jack Palevich. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 // Just enough UPnP to be able to forward ports
 //
 
@@ -44,27 +15,11 @@ import (
 	"time"
 )
 
-// NAT is an interface representing a NAT traversal options for example UPNP or
-// NAT-PMP. It provides methods to query and manipulate this traversal to allow
-// access to services.
-type NAT interface {
-	// Get the external address from outside the NAT.
-	GetExternalAddress() (addr net.IP, err error)
-	// Add a port mapping for protocol ("udp" or "tcp") from externalport to
-	// internal port with description lasting for timeout.
-	AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
-	// Remove a previously added port mapping from externalport to
-	// internal port.
-	DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
-}
-
 type upnpNAT struct {
 	serviceURL string
 	ourIP      string
 }
 
-// Discover searches the local network for a UPnP router returning a NAT
-// for the network if so, nil if not.
 func Discover() (nat NAT, err error) {
 	ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
 	if err != nil {
@@ -77,7 +32,7 @@ func Discover() (nat NAT, err error) {
 	socket := conn.(*net.UDPConn)
 	defer socket.Close()
 
-	err = socket.SetDeadline(time.Now().Add(3 * time.Second))
+	err = socket.SetDeadline(time.Now().Add(10 * time.Second))
 	if err != nil {
 		return
 	}
@@ -134,7 +89,7 @@ func Discover() (nat NAT, err error) {
 		nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
 		return
 	}
-	err = errors.New("UPnP port discovery failed")
+	err = errors.New("UPnP port discovery failed.")
 	return
 }
 
@@ -190,39 +145,38 @@ type root struct {
 	Device      device
 }
 
-// getChildDevice searches the children of device for a device with the given
-// type.
 func getChildDevice(d *device, deviceType string) *device {
-	for i := range d.DeviceList.Device {
-		if d.DeviceList.Device[i].DeviceType == deviceType {
-			return &d.DeviceList.Device[i]
+	dl := d.DeviceList.Device
+	for i := 0; i < len(dl); i++ {
+		if dl[i].DeviceType == deviceType {
+			return &dl[i]
 		}
 	}
 	return nil
 }
 
-// getChildDevice searches the service list of device for a service with the
-// given type.
 func getChildService(d *device, serviceType string) *service {
-	for i := range d.ServiceList.Service {
-		if d.ServiceList.Service[i].ServiceType == serviceType {
-			return &d.ServiceList.Service[i]
+	sl := d.ServiceList.Service
+	for i := 0; i < len(sl); i++ {
+		if sl[i].ServiceType == serviceType {
+			return &sl[i]
 		}
 	}
 	return nil
 }
 
-// getOurIP returns a best guess at what the local IP is.
 func getOurIP() (ip string, err error) {
 	hostname, err := os.Hostname()
 	if err != nil {
 		return
 	}
-	return net.LookupCNAME(hostname)
+	p, err := net.LookupIP(hostname)
+	if err != nil && len(p) > 0 {
+		return
+	}
+	return p[0].String(), nil
 }
 
-// getServiceURL parses the xml description at the given root url to find the
-// url for the WANIPConnection service to be used for port forwarding.
 func getServiceURL(rootURL string) (url string, err error) {
 	r, err := http.Get(rootURL)
 	if err != nil {
@@ -235,34 +189,34 @@ func getServiceURL(rootURL string) (url string, err error) {
 	}
 	var root root
 	err = xml.NewDecoder(r.Body).Decode(&root)
+
 	if err != nil {
 		return
 	}
 	a := &root.Device
 	if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
-		err = errors.New("no InternetGatewayDevice")
+		err = errors.New("No InternetGatewayDevice")
 		return
 	}
 	b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
 	if b == nil {
-		err = errors.New("no WANDevice")
+		err = errors.New("No WANDevice")
 		return
 	}
 	c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
 	if c == nil {
-		err = errors.New("no WANConnectionDevice")
+		err = errors.New("No WANConnectionDevice")
 		return
 	}
 	d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
 	if d == nil {
-		err = errors.New("no WANIPConnection")
+		err = errors.New("No WANIPConnection")
 		return
 	}
 	url = combineURL(rootURL, d.ControlURL)
 	return
 }
 
-// combineURL appends subURL onto rootURL.
 func combineURL(rootURL, subURL string) string {
 	protocolEnd := "://"
 	protoEndIndex := strings.Index(rootURL, protocolEnd)
@@ -271,24 +225,7 @@ func combineURL(rootURL, subURL string) string {
 	return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
 }
 
-// soapBody represents the <s:Body> element in a SOAP reply.
-// fields we don't care about are elided.
-type soapBody struct {
-	XMLName xml.Name `xml:"Body"`
-	Data    []byte   `xml:",innerxml"`
-}
-
-// soapEnvelope represents the <s:Envelope> element in a SOAP reply.
-// fields we don't care about are elided.
-type soapEnvelope struct {
-	XMLName xml.Name `xml:"Envelope"`
-	Body    soapBody `xml:"Body"`
-}
-
-// soapRequests performs a soap request with the given parameters and returns
-// the xml replied stripped of the soap headers. in the case that the request is
-// unsuccessful the an error is returned.
-func soapRequest(url, function, message string) (replyXML []byte, err error) {
+func soapRequest(url, function, message string) (r *http.Response, err error) {
 	fullMessage := "<?xml version=\"1.0\" ?>" +
 		"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
 		"<s:Body>" + message + "</s:Body></s:Envelope>"
@@ -305,10 +242,10 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
 	req.Header.Set("Cache-Control", "no-cache")
 	req.Header.Set("Pragma", "no-cache")
 
-	r, err := http.DefaultClient.Do(req)
-	if err != nil {
-		return nil, err
-	}
+	// log.Stderr("soapRequest ", req)
+	//fmt.Println(fullMessage)
+
+	r, err = http.DefaultClient.Do(req)
 	if r.Body != nil {
 		defer r.Body.Close()
 	}
@@ -319,45 +256,39 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
 		r = nil
 		return
 	}
-	var reply soapEnvelope
-	err = xml.NewDecoder(r.Body).Decode(&reply)
-	if err != nil {
-		return nil, err
-	}
-	return reply.Body.Data, nil
+	return
 }
 
-// getExternalIPAddressResponse represents the XML response to a
-// GetExternalIPAddress SOAP request.
-type getExternalIPAddressResponse struct {
-	XMLName           xml.Name `xml:"GetExternalIPAddressResponse"`
-	ExternalIPAddress string   `xml:"NewExternalIPAddress"`
+type statusInfo struct {
+	externalIpAddress string
 }
 
-// GetExternalAddress implements the NAT interface by fetching the external IP
-// from the UPnP router.
-func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
-	message := "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>\r\n"
-	response, err := soapRequest(n.serviceURL, "GetExternalIPAddress", message)
-	if err != nil {
-		return nil, err
-	}
+func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
+
+	message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
+		"</u:GetStatusInfo>"
 
-	var reply getExternalIPAddressResponse
-	err = xml.Unmarshal(response, &reply)
+	var response *http.Response
+	response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
 	if err != nil {
-		return nil, err
+		return
 	}
 
-	addr = net.ParseIP(reply.ExternalIPAddress)
-	if addr == nil {
-		return nil, errors.New("unable to parse ip address")
+	// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
+
+	response.Body.Close()
+	return
+}
+
+func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
+	info, err := n.getStatusInfo()
+	if err != nil {
+		return
 	}
-	return addr, nil
+	addr = net.ParseIP(info.externalIpAddress)
+	return
 }
 
-// AddPortMapping implements the NAT interface by setting up a port forwarding
-// from the UPnP router to the local machine with the given ports and protocol.
 func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
 	// A single concatenation would break ARM compilation.
 	message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
@@ -370,22 +301,19 @@ func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int
 		"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
 		"</NewLeaseDuration></u:AddPortMapping>"
 
-	response, err := soapRequest(n.serviceURL, "AddPortMapping", message)
+	var response *http.Response
+	response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
 	if err != nil {
 		return
 	}
 
 	// TODO: check response to see if the port was forwarded
-	// If the port was not wildcard we don't get an reply with the port in
-	// it. Not sure about wildcard yet. miniupnpc just checks for error
-	// codes here.
+	// log.Println(message, response)
 	mappedExternalPort = externalPort
 	_ = response
 	return
 }
 
-// AddPortMapping implements the NAT interface by removing up a port forwarding
-// from the UPnP router to the local machine with the given ports and.
 func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
 
 	message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
@@ -393,7 +321,8 @@ func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort
 		"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
 		"</u:DeletePortMapping>"
 
-	response, err := soapRequest(n.serviceURL, "DeletePortMapping", message)
+	var response *http.Response
+	response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
 	if err != nil {
 		return
 	}