Repository: scionproto/scion
Pull request #3644 (2020-01-29 11:50:29)

Remove snet.Addr
Remove snet.Addr, and constructors for snet.SVCAddr and snet.UDPAddr, instead use the struct directly.

Contributes https://github.com/scionproto/scion/issues/3136

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3644)
<!-- Reviewable:end -->



Patch:
diff --git a/go/acceptance/sigcmn.go b/go/acceptance/sigcmn.go
index 4651b93cb..c9a96e0ec 100644
--- a/go/acceptance/sigcmn.go
+++ b/go/acceptance/sigcmn.go
@@ -31,7 +31,7 @@ var (
 )
 
 var SigAddr integration.HostAddr = func(ia addr.IA) *snet.UDPAddr {
-	return snet.NewUDPAddr(ia, nil, nil, &net.UDPAddr{IP: net.IP(IAIPMap[ia])})
+	return &snet.UDPAddr{IA: ia, Host: &net.UDPAddr{IP: net.IP(IAIPMap[ia])}}
 }
 
 func ReadTestingConf() error {
diff --git a/go/border/rctrl/ifstate.go b/go/border/rctrl/ifstate.go
index 7df9a34f3..1d69687cb 100644
--- a/go/border/rctrl/ifstate.go
+++ b/go/border/rctrl/ifstate.go
@@ -81,7 +81,7 @@ func genIFStateReq() error {
 
 	var errors common.MultiError
 	for _, a := range bsAddrs {
-		dst := snet.NewSVCAddr(ia, nil, a, addr.SvcBS.Multicast())
+		dst := &snet.SVCAddr{IA: ia, NextHop: a, SVC: addr.SvcBS.Multicast()}
 		if _, err := snetConn.WriteTo(pld, dst); err != nil {
 			cl.Result = metrics.ErrWrite
 			metrics.Control.SentIFStateReq(cl).Inc()
diff --git a/go/border/rctrl/revinfo.go b/go/border/rctrl/revinfo.go
index c1480f50e..2a8b300d6 100644
--- a/go/border/rctrl/revinfo.go
+++ b/go/border/rctrl/revinfo.go
@@ -73,7 +73,7 @@ func fwdRevInfo(sRevInfo *path_mgmt.SignedRevInfo, dstHost addr.HostSVC) {
 		logger.Error("Writing RevInfo signed Ctrl payload", "err", err)
 		return
 	}
-	dst := snet.NewSVCAddr(ia, nil, nil, dstHost)
+	dst := &snet.SVCAddr{IA: ia, SVC: dstHost}
 	dst.NextHop, err = ctx.ResolveSVCAny(dstHost)
 	if err != nil {
 		cl.Result = metrics.ErrResolveSVC
diff --git a/go/cs/beaconing/handler.go b/go/cs/beaconing/handler.go
index 713d37b3c..1d65a1c69 100644
--- a/go/cs/beaconing/handler.go
+++ b/go/cs/beaconing/handler.go
@@ -199,7 +199,11 @@ func (h *handler) verifySegment(segment *seg.PathSegment) error {
 	if err != nil {
 		return common.NewBasicError("path error", err)
 	}
-	svcToQuery := snet.NewSVCAddr(snetPeer.IA, peerPath.Path(),
-		peerPath.OverlayNextHop(), addr.SvcBS)
+	svcToQuery := &snet.SVCAddr{
+		IA:      snetPeer.IA,
+		Path:    peerPath.Path(),
+		NextHop: peerPath.OverlayNextHop(),
+		SVC:     addr.SvcBS,
+	}
 	return segverifier.VerifySegment(h.request.Context(), h.verifier, svcToQuery, segment)
 }
diff --git a/go/cs/beaconing/registrar.go b/go/cs/beaconing/registrar.go
index cc489d7e0..6d82f9281 100644
--- a/go/cs/beaconing/registrar.go
+++ b/go/cs/beaconing/registrar.go
@@ -247,7 +247,7 @@ func (r *segmentRegistrar) onSuccess() {
 func (r *segmentRegistrar) chooseServer(pseg *seg.PathSegment) (net.Addr, error) {
 	if r.segType != proto.PathSegType_down {
 		topo := r.topoProvider.Get()
-		return snet.NewSVCAddr(topo.IA(), nil, nil, addr.SvcPS), nil
+		return &snet.SVCAddr{IA: topo.IA(), SVC: addr.SvcPS}, nil
 	}
 	return addrutil.GetPath(addr.SvcPS, pseg, r.topoProvider)
 }
diff --git a/go/cs/handlers/segreg.go b/go/cs/handlers/segreg.go
index cc3c9ab4e..4be04adce 100644
--- a/go/cs/handlers/segreg.go
+++ b/go/cs/handlers/segreg.go
@@ -92,8 +92,12 @@ func (h *segRegHandler) Handle() *infra.HandlerResult {
 		sendAck(proto.Ack_ErrCode_reject, messenger.AckRejectFailedToParse)
 		return infra.MetricsErrInvalid
 	}
-	svcToQuery := snet.NewSVCAddr(snetPeer.IA, peerPath.Path(),
-		peerPath.OverlayNextHop(), addr.SvcBS)
+	svcToQuery := &snet.SVCAddr{
+		IA:      snetPeer.IA,
+		Path:    peerPath.Path(),
+		NextHop: peerPath.OverlayNextHop(),
+		SVC:     addr.SvcBS,
+	}
 	segs := seghandler.Segments{
 		Segs:      segReg.Recs,
 		SRevInfos: segReg.SRevInfos,
diff --git a/go/cs/handlers/segsync.go b/go/cs/handlers/segsync.go
index 5d0471465..411dc0eb3 100644
--- a/go/cs/handlers/segsync.go
+++ b/go/cs/handlers/segsync.go
@@ -89,8 +89,12 @@ func (h *syncHandler) Handle() *infra.HandlerResult {
 		metrics.Sync.Registrations(labels.WithResult(metrics.ErrParse)).Inc()
 		return infra.MetricsErrInvalid
 	}
-	svcToQuery := snet.NewSVCAddr(snetPeer.IA, peerPath.Path(),
-		peerPath.OverlayNextHop(), addr.SvcPS)
+	svcToQuery := &snet.SVCAddr{
+		IA:      snetPeer.IA,
+		Path:    peerPath.Path(),
+		NextHop: peerPath.OverlayNextHop(),
+		SVC:     addr.SvcPS,
+	}
 
 	segs := seghandler.Segments{
 		Segs:      segSync.Recs,
diff --git a/go/cs/ifstate/revoker.go b/go/cs/ifstate/revoker.go
index 399497146..1f19367d2 100644
--- a/go/cs/ifstate/revoker.go
+++ b/go/cs/ifstate/revoker.go
@@ -171,7 +171,7 @@ func (r *Revoker) pushRevocationsToPS(ctx context.Context,
 	topo := r.cfg.TopoProvider.Get()
 	labels := metrics.SentLabels{Dst: metrics.DstPS}
 
-	a := snet.NewSVCAddr(topo.IA(), nil, nil, addr.SvcPS)
+	a := &snet.SVCAddr{IA: topo.IA(), SVC: addr.SvcPS}
 	for ifid, srev := range revs {
 		if err := r.cfg.Msgr.SendRev(ctx, srev, a, messenger.NextId()); err != nil {
 			log.FromCtx(ctx).Error("[ifstate.Revoker] Failed to send revocation to PS",
diff --git a/go/cs/onehop/sender.go b/go/cs/onehop/sender.go
index 01e938a1e..67b26621f 100644
--- a/go/cs/onehop/sender.go
+++ b/go/cs/onehop/sender.go
@@ -172,7 +172,7 @@ func (s *BeaconSender) attemptQUIC(ctx context.Context, ia addr.IA, path *spath.
 		return false, nil
 	}
 
-	t := snet.NewSVCAddr(ia, path, nextHop, addr.SvcBS)
+	t := &snet.SVCAddr{IA: ia, Path: path, NextHop: nextHop, SVC: addr.SvcBS}
 	newAddr, redirect, err := s.AddressRewriter.RedirectToQUIC(ctx, t)
 
 	if err != nil || !redirect {
diff --git a/go/examples/pingpong/pingpong.go b/go/examples/pingpong/pingpong.go
index a921ed7db..55f667ef3 100644
--- a/go/examples/pingpong/pingpong.go
+++ b/go/examples/pingpong/pingpong.go
@@ -240,8 +240,7 @@ func (c *client) run() {
 	// IP address needs to be supplied explicitly. When supplied a local
 	// port of 0, Dial will assign a random free local port.
 
-	remoteUDP := snet.NewUDPAddr(remote.IA, remote.Path, remote.NextHop, remote.Host)
-	c.qsess, err = squic.Dial(network, local.Host, remoteUDP, addr.SvcNone, nil)
+	c.qsess, err = squic.Dial(network, local.Host, &remote, addr.SvcNone, nil)
 	if err != nil {
 		LogFatal("Unable to dial", "err", err)
 	}
diff --git a/go/hidden_path_srv/internal/hpsegreq/fetcher.go b/go/hidden_path_srv/internal/hpsegreq/fetcher.go
index e702c4d56..1560d08ba 100644
--- a/go/hidden_path_srv/internal/hpsegreq/fetcher.go
+++ b/go/hidden_path_srv/internal/hpsegreq/fetcher.go
@@ -138,7 +138,7 @@ func (f *DefaultFetcher) fetchRemote(ctx context.Context, ids []hiddenpath.Group
 		RawDstIA: endsAt.IAInt(),
 		GroupIds: rawIds,
 	}
-	addr := snet.NewSVCAddr(remote, nil, nil, addr.SvcHPS)
+	addr := &snet.SVCAddr{IA: remote, SVC: addr.SvcHPS}
 	reply, err := f.msger.GetHPSegs(ctx, req, addr, messenger.NextId())
 	if err != nil {
 		var recs = make([]*path_mgmt.HPSegRecs, 0, len(ids))
diff --git a/go/integration/cert_req/main.go b/go/integration/cert_req/main.go
index a729e9efe..70976d00d 100644
--- a/go/integration/cert_req/main.go
+++ b/go/integration/cert_req/main.go
@@ -224,7 +224,7 @@ func getRemote() error {
 	if svcHost, err = getSVCAddress(); err != nil {
 		return err
 	}
-	svc = snet.NewUDPAddr(integration.Local.IA, nil, nil, svcHost)
+	svc = &snet.UDPAddr{IA: integration.Local.IA, Host: svcHost}
 	return nil
 }
 
diff --git a/go/lib/infra/messenger/addr.go b/go/lib/infra/messenger/addr.go
index 3847a3189..93d67ed81 100644
--- a/go/lib/infra/messenger/addr.go
+++ b/go/lib/infra/messenger/addr.go
@@ -108,7 +108,7 @@ func (r AddressRewriter) RedirectToQUIC(ctx context.Context,
 			return a, false, err
 		}
 
-		ret := snet.NewUDPAddr(fa.IA, p.Path(), fa.NextHop, u)
+		ret := &snet.UDPAddr{IA: fa.IA, Path: p.Path(), NextHop: fa.NextHop, Host: u}
 		return ret, quicRedirect, err
 	}
 
@@ -124,12 +124,17 @@ func (r AddressRewriter) buildFullAddress(ctx context.Context,
 	s *snet.SVCAddr) (*snet.SVCAddr, error) {
 
 	if s.Path != nil {
-		ret := snet.NewSVCAddr(s.IA, s.Path.Copy(), snet.CopyUDPAddr(s.NextHop), s.SVC)
+		ret := &snet.SVCAddr{
+			IA:      s.IA,
+			Path:    s.Path.Copy(),
+			NextHop: snet.CopyUDPAddr(s.NextHop),
+			SVC:     s.SVC,
+		}
 		log.Trace("[Acceptance]", "overlay", ret.NextHop)
 		return ret, nil
 	}
 
-	ret := snet.NewSVCAddr(s.IA, nil, nil, s.SVC)
+	ret := &snet.SVCAddr{IA: s.IA, SVC: s.SVC}
 	p, err := r.Router.Route(ctx, s.IA)
 	if err != nil {
 		return nil, err
diff --git a/go/lib/infra/messenger/messenger.go b/go/lib/infra/messenger/messenger.go
index a9428e165..a9681d716 100644
--- a/go/lib/infra/messenger/messenger.go
+++ b/go/lib/infra/messenger/messenger.go
@@ -667,7 +667,7 @@ func (m *Messenger) SendBeacon(ctx context.Context, msg *seg.Beacon, a net.Addr,
 	case *snet.UDPAddr:
 	case *snet.SVCAddr:
 		return common.NewBasicError("[Messenger] Cannot send to SVC address on QUIC-only RPC", nil,
-			"svc", a.(*snet.SVCAddr).Host)
+			"svc", a.(*snet.SVCAddr))
 	default:
 		return common.NewBasicError("[Messenger] Cannot send to unknown address", nil)
 	}
diff --git a/go/lib/infra/modules/trust/handlers.go b/go/lib/infra/modules/trust/handlers.go
index 2abba2208..7b2d6efc6 100644
--- a/go/lib/infra/modules/trust/handlers.go
+++ b/go/lib/infra/modules/trust/handlers.go
@@ -321,10 +321,8 @@ func peerToLabel(peer net.Addr, local addr.IA) string {
 	var ia addr.IA
 	switch v := peer.(type) {
 	case *snet.SVCAddr:
-		ia = v.Addr.IA
-	case *snet.UDPAddr:
 		ia = v.IA
-	case *snet.Addr:
+	case *snet.UDPAddr:
 		ia = v.IA
 	default:
 		return infra.PromSrcUnknown
diff --git a/go/lib/infra/modules/trust/router.go b/go/lib/infra/modules/trust/router.go
index fd840255c..5c1484285 100644
--- a/go/lib/infra/modules/trust/router.go
+++ b/go/lib/infra/modules/trust/router.go
@@ -45,7 +45,7 @@ func (r LocalRouter) ChooseServer(_ context.Context, _ addr.ISD) (net.Addr, erro
 }
 
 func (r LocalRouter) chooseServer() net.Addr {
-	return snet.NewSVCAddr(r.IA, nil, nil, addr.SvcCS)
+	return &snet.SVCAddr{IA: r.IA, SVC: addr.SvcCS}
 }
 
 // AuthRouter routes requests for missing crypto material to the authoritative
@@ -73,7 +73,12 @@ func (r AuthRouter) ChooseServer(ctx context.Context, subjectISD addr.ISD) (net.
 	if err != nil {
 		return nil, serrors.WrapStr("unable to find path to any core AS", err, "isd", dstISD)
 	}
-	ret := snet.NewSVCAddr(path.Destination(), path.Path(), path.OverlayNextHop(), addr.SvcCS)
+	ret := &snet.SVCAddr{
+		IA:      path.Destination(),
+		Path:    path.Path(),
+		NextHop: path.OverlayNextHop(),
+		SVC:     addr.SvcCS,
+	}
 	return ret, nil
 }
 
diff --git a/go/lib/integration/integration.go b/go/lib/integration/integration.go
index f40e43575..b9b64441d 100644
--- a/go/lib/integration/integration.go
+++ b/go/lib/integration/integration.go
@@ -193,7 +193,7 @@ var DispAddr HostAddr = func(ia addr.IA) *snet.UDPAddr {
 		os.Exit(1)
 	}
 	cs := topo.CS["cs"+ia.FileFmt(false)+"-1"]
-	return snet.NewUDPAddr(ia, nil, nil, cs.SCIONAddress)
+	return &snet.UDPAddr{IA: ia, Host: cs.SCIONAddress}
 }
 
 // interface kept similar to go 1.10
diff --git a/go/lib/sciond/pathprobe/paths.go b/go/lib/sciond/pathprobe/paths.go
index 4ebba8d29..51d6c2299 100644
--- a/go/lib/sciond/pathprobe/paths.go
+++ b/go/lib/sciond/pathprobe/paths.go
@@ -143,8 +143,12 @@ func (p Prober) GetStatuses(ctx context.Context,
 }
 
 func (p Prober) send(scionConn snet.Conn, path snet.Path) error {
-	addr := snet.NewSVCAddr(p.DstIA, path.Path(), path.OverlayNextHop(),
-		addr.SvcNone)
+	addr := &snet.SVCAddr{
+		IA:      p.DstIA,
+		Path:    path.Path(),
+		NextHop: path.OverlayNextHop(),
+		SVC:     addr.SvcNone,
+	}
 	log.Debug("Sending test packet.", "path", fmt.Sprintf("%s", path))
 	_, err := scionConn.WriteTo([]byte{}, addr)
 	if err != nil {
diff --git a/go/lib/snet/BUILD.bazel b/go/lib/snet/BUILD.bazel
index 54acc19e6..37b2f501a 100644
--- a/go/lib/snet/BUILD.bazel
+++ b/go/lib/snet/BUILD.bazel
@@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
-        "addr.go",
         "base.go",
         "conn.go",
         "dispatcher.go",
@@ -39,7 +38,6 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
-        "addr_test.go",
         "export_test.go",
         "raw_test.go",
         "svcaddr_test.go",
diff --git a/go/lib/snet/addr.go b/go/lib/snet/addr.go
deleted file mode 100644
index 5673dd3a7..000000000
--- a/go/lib/snet/addr.go
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2017 ETH Zurich
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package snet
-
-import (
-	"flag"
-	"fmt"
-	"net"
-	"regexp"
-	"strconv"
-
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/spath"
-)
-
-var _ net.Addr = (*Addr)(nil)
-var _ flag.Value = (*Addr)(nil)
-
-var addrRegexp = regexp.MustCompile(
-	`^(?P<ia>\d+-[\d:A-Fa-f]+),\[(?P<host>[^\]]+)\](?P<port>:\d+)?$`)
-
-type Addr struct {
-	IA      addr.IA
-	Host    *addr.AppAddr
-	Path    *spath.Path
-	NextHop *net.UDPAddr
-}
-
-// ToNetUDPAddr returns a net.UDPAddr or nil.
-func (a *Addr) ToNetUDPAddr() *net.UDPAddr {
-	switch a.Host.L3.Type() {
-	case addr.HostTypeIPv4, addr.HostTypeIPv6:
-		return &net.UDPAddr{IP: a.Host.L3.IP(), Port: int(a.Host.L4)}
-	}
-	return nil
-}
-
-func newAddr(ia addr.IA, p *spath.Path, nh *net.UDPAddr) *Addr {
-	return &Addr{
-		IA:      ia,
-		Path:    p,
-		NextHop: nh,
-	}
-}
-
-// ToXAddr returns an snet.{SVC,UDP}addr.
-// TODO(karampok): Remove this once snet.Addr is removed.
-func (a *Addr) ToXAddr() net.Addr {
-	switch a.Host.L3.Type() {
-	case addr.HostTypeSVC:
-		return NewSVCAddr(a.IA, a.Path, a.NextHop, a.Host.L3.(addr.HostSVC))
-	case addr.HostTypeIPv4, addr.HostTypeIPv6:
-		u := &net.UDPAddr{IP: a.Host.L3.IP(), Port: int(a.Host.L4)}
-		return NewUDPAddr(a.IA, a.Path, a.NextHop, u)
-	}
-	return nil
-}
-
-func (a *Addr) Network() string {
-	return "scion"
-}
-
-// GetPath returns a path with attached metadata.
-func (a *Addr) GetPath() (Path, error) {
-	// Initialize path so it is always ready for use
-	var p *spath.Path
-	if a.Path != nil {
-		p = a.Path.Copy()
-		if err := p.InitOffsets(); err != nil {
-			return nil, err
-		}
-	}
-	return &partialPath{
-		spath:       p,
-		overlay:     a.NextHop,
-		destination: a.IA,
-	}, nil
-}
-
-func (a *Addr) String() string {
-	if a == nil {
-		return "<nil>"
-	}
-	if a.Host == nil {
-		return fmt.Sprintf("%s,<nil>", a.IA)
-	}
-	return fmt.Sprintf("%s,[%v]:%v", a.IA, a.Host.L3, a.Host.L4)
-}
-
-func (a *Addr) Desc() string {
-	if a == nil {
-		return "<nil>"
-	}
-	return fmt.Sprintf("%s Path: %t NextHop: %v", a, a.Path != nil, a.NextHop)
-}
-
-// EqualAddr compares the IA/Host/L4port values with the supplied Addr
-func (a *Addr) EqualAddr(o *Addr) bool {
-	if a == nil || o == nil {
-		return a == o
-	}
-	if !a.IA.Equal(o.IA) {
-		return false
-	}
-	return a.Host.Equal(o.Host)
-}
-
-func (a *Addr) Copy() *Addr {
-	if a == nil {
-		return nil
-	}
-	newA := &Addr{
-		IA:   a.IA,
-		Host: a.Host.Copy(),
-	}
-	if a.Path != nil {
-		newA.Path = a.Path.Copy()
-	}
-	if a.NextHop != nil {
-		newA.NextHop = CopyUDPAddr(a.NextHop)
-	}
-	return newA
-}
-
-// UnmarshalText implements encoding.TextUnmarshaler
-func (a *Addr) UnmarshalText(text []byte) error {
-	if len(text) == 0 {
-		*a = Addr{}
-	}
-	other, err := AddrFromString(string(text))
-	if err != nil {
-		return err
-	}
-	*a = *other
-	return nil
-}
-
-func (a *Addr) IsZero() bool {
-	return a.IA.IsZero() && a.Host == nil && a.Path == nil && a.NextHop == nil
-}
-
-// AddrFromString converts an address string of format isd-as,[ipaddr]:port
-// (e.g., 1-ff00:0:300,[192.168.1.1]:80) to a SCION address.
-func AddrFromString(s string) (*Addr, error) {
-	parts, err := parseAddr(s)
-	if err != nil {
-		return nil, err
-	}
-	ia, err := addr.IAFromString(parts["ia"])
-	if err != nil {
-		return nil, common.NewBasicError("Invalid IA string", err, "ia", ia)
-	}
-	var l3 addr.HostAddr
-	if hostSVC := addr.HostSVCFromString(parts["host"]); hostSVC != addr.SvcNone {
-		l3 = hostSVC
-	} else {
-		l3 = addr.HostFromIPStr(parts["host"])
-		if l3 == nil {
-			return nil, common.NewBasicError("Invalid IP address string", nil, "ip", parts["host"])
-		}
-	}
-	var l4 uint16
-	if parts["port"] != "" {
-		// skip the : (first character) from the port string
-		p, err := strconv.ParseUint(parts["port"][1:], 10, 16)
-		if err != nil {
-			return nil, common.NewBasicError("Invalid port string", err, "port", parts["port"][1:])
-		}
-		l4 = uint16(p)
-	}
-	return &Addr{IA: ia, Host: &addr.AppAddr{L3: l3, L4: l4}}, nil
-}
-
-func parseAddr(s string) (map[string]string, error) {
-	result := make(map[string]string)
-	match := addrRegexp.FindStringSubmatch(s)
-	if len(match) == 0 {
-		return nil, common.NewBasicError("Invalid address: regex match failed", nil, "addr", s)
-	}
-	for i, name := range addrRegexp.SubexpNames() {
-		if i == 0 {
-			continue
-		}
-		result[name] = match[i]
-	}
-	return result, nil
-}
-
-// Set implements the flag.Value interface
-func (a *Addr) Set(s string) error {
-	other, err := AddrFromString(s)
-	if err != nil {
-		return err
-	}
-	a.IA, a.Host = other.IA, other.Host
-	return nil
-}
-
-func CopyUDPAddr(a *net.UDPAddr) *net.UDPAddr {
-	if a == nil {
-		return nil
-	}
-	return &net.UDPAddr{
-		IP:   append(a.IP[:0:0], a.IP...),
-		Port: a.Port,
-	}
-}
diff --git a/go/lib/snet/addrutil/addrutil.go b/go/lib/snet/addrutil/addrutil.go
index 3e82cf120..6156d4cd6 100644
--- a/go/lib/snet/addrutil/addrutil.go
+++ b/go/lib/snet/addrutil/addrutil.go
@@ -26,7 +26,7 @@ import (
 	"github.com/scionproto/scion/go/lib/topology"
 )
 
-// GetPath creates a path from the given segment and then creates a snet.Addr.
+// GetPath creates a path from the given segment and then creates a snet.SVCAddr.
 func GetPath(svc addr.HostSVC, ps *seg.PathSegment, topoProv topology.Provider) (net.Addr, error) {
 	x := &bytes.Buffer{}
 	if _, err := ps.RawWriteTo(x); err != nil {
@@ -49,5 +49,5 @@ func GetPath(svc addr.HostSVC, ps *seg.PathSegment, topoProv topology.Provider)
 	if !ok {
 		return nil, common.NewBasicError("unable to find first-hop BR for path", nil, "ifID", ifID)
 	}
-	return snet.NewSVCAddr(ps.FirstIA(), p, overlayNextHop, svc), nil
+	return &snet.SVCAddr{IA: ps.FirstIA(), Path: p, NextHop: overlayNextHop, SVC: svc}, nil
 }
diff --git a/go/lib/snet/path.go b/go/lib/snet/path.go
index cdaea52b8..2b33a4d90 100644
--- a/go/lib/snet/path.go
+++ b/go/lib/snet/path.go
@@ -76,7 +76,7 @@ type PathInterface interface {
 
 // partialPath is a path object with incomplete metadata. It is used as a
 // temporary solution where a full path cannot be reconstituted from other
-// objects, notably snet.Addr.
+// objects, notably snet.UDPAddr and snet.SVCAddr.
 type partialPath struct {
 	spath       *spath.Path
 	overlay     *net.UDPAddr
diff --git a/go/lib/snet/snet.go b/go/lib/snet/snet.go
index 2ed8cfe46..ddcc24505 100644
--- a/go/lib/snet/snet.go
+++ b/go/lib/snet/snet.go
@@ -113,7 +113,7 @@ func (n *SCIONNetwork) Dial(ctx context.Context, network string, listen *net.UDP
 		return nil, err
 	}
 	snetConn := conn.(*SCIONConn)
-	snetConn.remote = NewUDPAddr(remote.IA, remote.Path.Copy(), remote.NextHop, remote.Host)
+	snetConn.remote = remote.Copy()
 	return conn, nil
 }
 
@@ -153,11 +153,7 @@ func (n *SCIONNetwork) Listen(ctx context.Context, network string, listen *net.U
 		net:      network,
 		scionNet: n,
 		svc:      svc,
-		listen: &net.UDPAddr{
-			IP:   append(listen.IP[:0:0], listen.IP...),
-			Port: listen.Port,
-			Zone: listen.Zone,
-		},
+		listen:   CopyUDPAddr(listen),
 	}
 	packetConn, port, err := conn.scionNet.dispatcher.Register(ctx, n.localIA, listen, svc)
 	if err != nil {
diff --git a/go/lib/snet/svcaddr.go b/go/lib/snet/svcaddr.go
index 3d93262db..ec03f202b 100644
--- a/go/lib/snet/svcaddr.go
+++ b/go/lib/snet/svcaddr.go
@@ -1,3 +1,17 @@
+// Copyright 2019 Anapaya Systems
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package snet
 
 import (
@@ -10,16 +24,10 @@ import (
 
 // SVCAddr is the address type for SVC destinations.
 type SVCAddr struct {
-	Addr
-
-	SVC addr.HostSVC
-}
-
-// NewSVCAddr returns a new instance of SVCAddr.
-// TODO(karampok). constructor to be removed once snet.Addr is removed.
-func NewSVCAddr(ia addr.IA, p *spath.Path, nh *net.UDPAddr, svc addr.HostSVC) *SVCAddr {
-	a := newAddr(ia, p, nh)
-	return &SVCAddr{*a, svc}
+	IA      addr.IA
+	Path    *spath.Path
+	NextHop *net.UDPAddr
+	SVC     addr.HostSVC
 }
 
 // Network implements net.Addr interface.
@@ -31,3 +39,33 @@ func (a *SVCAddr) Network() string {
 func (a *SVCAddr) String() string {
 	return fmt.Sprintf("%v,%v", a.IA, a.SVC)
 }
+
+// GetPath returns a path with attached metadata.
+func (a *SVCAddr) GetPath() (Path, error) {
+	// Initialize path so it is always ready for use
+	var p *spath.Path
+	if a.Path != nil {
+		p = a.Path.Copy()
+		if err := p.InitOffsets(); err != nil {
+			return nil, err
+		}
+	}
+	return &partialPath{
+		spath:       p,
+		overlay:     a.NextHop,
+		destination: a.IA,
+	}, nil
+}
+
+// Copy creates a deep copy of the address.
+func (a *SVCAddr) Copy() *SVCAddr {
+	if a == nil {
+		return nil
+	}
+	return &SVCAddr{
+		IA:      a.IA,
+		Path:    a.Path.Copy(),
+		NextHop: CopyUDPAddr(a.NextHop),
+		SVC:     a.SVC,
+	}
+}
diff --git a/go/lib/snet/udpaddr.go b/go/lib/snet/udpaddr.go
index b94e5489a..7d302f928 100644
--- a/go/lib/snet/udpaddr.go
+++ b/go/lib/snet/udpaddr.go
@@ -1,25 +1,67 @@
+// Copyright 2019 Anapaya Systems
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package snet
 
 import (
 	"fmt"
 	"net"
+	"regexp"
+	"strconv"
 
 	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/common"
+	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/spath"
 )
 
+var addrRegexp = regexp.MustCompile(
+	`^(?P<ia>\d+-[\d:A-Fa-f]+),\[(?P<host>[^\]]+)\](?P<port>:\d+)?$`)
+
 // UDPAddr to be used when UDP host.
 type UDPAddr struct {
-	Addr
-
-	Host *net.UDPAddr
+	IA      addr.IA
+	Path    *spath.Path
+	NextHop *net.UDPAddr
+	Host    *net.UDPAddr
 }
 
-// NewUDPAddr returns an instance of UDPAddr.
-// TODO(karampok). constructor to be removed once snet.Addr is removed.
-func NewUDPAddr(ia addr.IA, p *spath.Path, nh *net.UDPAddr, t *net.UDPAddr) *UDPAddr {
-	a := newAddr(ia, p, nh)
-	return &UDPAddr{*a, t}
+// UDPAddrFromString converts an address string of format isd-as,[ipaddr]:port
+// (e.g., 1-ff00:0:300,[192.168.1.1]:80) to a SCION address.
+func UDPAddrFromString(s string) (*UDPAddr, error) {
+	parts, err := parseAddr(s)
+	if err != nil {
+		return nil, err
+	}
+	ia, err := addr.IAFromString(parts["ia"])
+	if err != nil {
+		return nil, serrors.WrapStr("invalid IA string", err, "ia", ia)
+	}
+	ip := net.ParseIP(parts["host"])
+	if ip == nil {
+		return nil, serrors.New("invalid IP address string", "ip", parts["host"])
+	}
+	var port int
+	if parts["port"] != "" {
+		// skip the : (first character) from the port string
+		p, err := strconv.ParseUint(parts["port"][1:], 10, 16)
+		if err != nil {
+			return nil, serrors.WrapStr("invalid port string", err, "port", parts["port"][1:])
+		}
+		port = int(p)
+	}
+	return &UDPAddr{IA: ia, Host: &net.UDPAddr{IP: ip, Port: port}}, nil
 }
 
 // Network implements net.Addr interface.
@@ -41,12 +83,69 @@ func (a *UDPAddr) String() string {
 	return fmt.Sprintf("%v,[%v]:%v", a.IA, ip, port)
 }
 
+// GetPath returns a path with attached metadata.
+func (a *UDPAddr) GetPath() (Path, error) {
+	// Initialize path so it is always ready for use
+	var p *spath.Path
+	if a.Path != nil {
+		p = a.Path.Copy()
+		if err := p.InitOffsets(); err != nil {
+			return nil, err
+		}
+	}
+	return &partialPath{
+		spath:       p,
+		overlay:     a.NextHop,
+		destination: a.IA,
+	}, nil
+}
+
 // Set implements the flag.Value interface
 func (a *UDPAddr) Set(s string) error {
-	other, err := AddrFromString(s)
+	other, err := UDPAddrFromString(s)
 	if err != nil {
 		return err
 	}
-	*a = *other.ToXAddr().(*UDPAddr)
+	*a = *other
 	return nil
 }
+
+// Copy creates a deep copy of the address.
+func (a *UDPAddr) Copy() *UDPAddr {
+	if a == nil {
+		return nil
+	}
+	return &UDPAddr{
+		IA:      a.IA,
+		Path:    a.Path.Copy(),
+		NextHop: CopyUDPAddr(a.NextHop),
+		Host:    CopyUDPAddr(a.Host),
+	}
+}
+
+// CopyUDPAddr creates a deep copy of the net.UDPAddr.
+func CopyUDPAddr(a *net.UDPAddr) *net.UDPAddr {
+	if a == nil {
+		return nil
+	}
+	return &net.UDPAddr{
+		IP:   append(a.IP[:0:0], a.IP...),
+		Port: a.Port,
+		Zone: a.Zone,
+	}
+}
+
+func parseAddr(s string) (map[string]string, error) {
+	result := make(map[string]string)
+	match := addrRegexp.FindStringSubmatch(s)
+	if len(match) == 0 {
+		return nil, common.NewBasicError("Invalid address: regex match failed", nil, "addr", s)
+	}
+	for i, name := range addrRegexp.SubexpNames() {
+		if i == 0 {
+			continue
+		}
+		result[name] = match[i]
+	}
+	return result, nil
+}
diff --git a/go/lib/snet/writer.go b/go/lib/snet/writer.go
index 821bec29e..5a405a68e 100644
--- a/go/lib/snet/writer.go
+++ b/go/lib/snet/writer.go
@@ -58,8 +58,6 @@ func (c *scionConnWriter) WriteTo(b []byte, raddr net.Addr) (int, error) {
 	switch a := raddr.(type) {
 	case nil:
 		return 0, common.NewBasicError("Missing remote address", nil)
-	case *Addr:
-		return c.WriteTo(b, a.ToXAddr())
 	case *UDPAddr:
 		dst, port, path = SCIONAddress{IA: a.IA, Host: addr.HostFromIP(a.Host.IP)},
 			a.Host.Port, a.Path
diff --git a/go/lib/topology/interface.go b/go/lib/topology/interface.go
index 0144fa317..2dfbebae4 100644
--- a/go/lib/topology/interface.go
+++ b/go/lib/topology/interface.go
@@ -358,7 +358,11 @@ func (t *topologyS) SBRAddress(name string) *snet.UDPAddr {
 	if !ok {
 		return nil
 	}
-	return snet.NewUDPAddr(t.IA(), nil, br.CtrlAddrs.UnderlayAddr(), br.CtrlAddrs.SCIONAddress)
+	return &snet.UDPAddr{
+		IA:      t.IA(),
+		NextHop: br.CtrlAddrs.UnderlayAddr(),
+		Host:    br.CtrlAddrs.SCIONAddress,
+	}
 }
 
 func (t *topologyS) SVCNames(svc addr.HostSVC) ServiceNames {
diff --git a/go/sciond/internal/fetcher/fetcher.go b/go/sciond/internal/fetcher/fetcher.go
index 137400c47..4ee990eb0 100644
--- a/go/sciond/internal/fetcher/fetcher.go
+++ b/go/sciond/internal/fetcher/fetcher.go
@@ -323,7 +323,7 @@ type dstProvider struct {
 }
 
 func (r *dstProvider) Dst(_ context.Context, _ segfetcher.Request) (net.Addr, error) {
-	return snet.NewSVCAddr(r.IA, nil, nil, addr.SvcPS), nil
+	return &snet.SVCAddr{IA: r.IA, SVC: addr.SvcPS}, nil
 }
 
 type neverLocal struct{}
diff --git a/go/sig/egress/siginfo/sig.go b/go/sig/egress/siginfo/sig.go
index ad8256307..505b73a19 100644
--- a/go/sig/egress/siginfo/sig.go
+++ b/go/sig/egress/siginfo/sig.go
@@ -34,35 +34,33 @@ type Sig struct {
 func (s *Sig) CtrlSnetAddr(path *spath.Path, nextHop *net.UDPAddr) net.Addr {
 	switch s.Host.(type) {
 	case addr.HostSVC:
-		return snet.NewSVCAddr(
-			s.IA,
-			path,
-			nextHop,
-			addr.SvcSIG,
-		)
+		return &snet.SVCAddr{
+			IA:      s.IA,
+			Path:    path,
+			NextHop: nextHop,
+			SVC:     addr.SvcSIG,
+		}
 	default:
-		return snet.NewUDPAddr(
-			s.IA,
-			path,
-			nextHop,
-			&net.UDPAddr{
+		return &snet.UDPAddr{
+			IA:      s.IA,
+			Path:    path,
+			NextHop: nextHop,
+			Host: &net.UDPAddr{
 				IP:   s.Host.IP(),
 				Port: s.CtrlL4Port,
 			},
-		)
+		}
 	}
 }
 
 func (s *Sig) EncapSnetAddr() *snet.UDPAddr {
-	return snet.NewUDPAddr(
-		s.IA,
-		nil,
-		nil,
-		&net.UDPAddr{
+	return &snet.UDPAddr{
+		IA: s.IA,
+		Host: &net.UDPAddr{
 			IP:   s.Host.IP(),
 			Port: s.EncapL4Port,
 		},
-	)
+	}
 }
 
 func (s *Sig) Equal(x *Sig) bool {
diff --git a/go/sig/internal/base/pollhdlr.go b/go/sig/internal/base/pollhdlr.go
index 59cc44d4e..0b2e869e0 100644
--- a/go/sig/internal/base/pollhdlr.go
+++ b/go/sig/internal/base/pollhdlr.go
@@ -57,12 +57,12 @@ func PollReqHdlr() {
 			log.Error("PollReqHdlr: Error packing signed Ctrl payload", "err", err)
 			break
 		}
-		sigCtrlAddr := snet.NewUDPAddr(
-			rpld.Addr.IA,
-			rpld.Addr.Path,
-			snet.CopyUDPAddr(rpld.Addr.NextHop),
-			req.Addr.Ctrl.UDP(),
-		)
+		sigCtrlAddr := &snet.UDPAddr{
+			IA:      rpld.Addr.IA,
+			Path:    rpld.Addr.Path,
+			NextHop: snet.CopyUDPAddr(rpld.Addr.NextHop),
+			Host:    req.Addr.Ctrl.UDP(),
+		}
 		_, err = sigcmn.CtrlConn.WriteTo(raw, sigCtrlAddr)
 		if err != nil {
 			log.Error("PollReqHdlr: Error sending Ctrl payload", "dest", rpld.Addr, "err", err)
diff --git a/go/sig/internal/disp/disp.go b/go/sig/internal/disp/disp.go
index 64fe35db6..08e161560 100644
--- a/go/sig/internal/disp/disp.go
+++ b/go/sig/internal/disp/disp.go
@@ -134,12 +134,12 @@ func dispFunc(dp *pktdisp.DispPkt) {
 	var src *snet.UDPAddr
 	switch v := dp.Addr.(type) {
 	case *snet.UDPAddr:
-		src = snet.NewUDPAddr(
-			v.IA,
-			v.Path.Copy(),
-			snet.CopyUDPAddr(v.NextHop),
-			snet.CopyUDPAddr(v.Host),
-		)
+		src = &snet.UDPAddr{
+			IA:      v.IA,
+			Path:    v.Path.Copy(),
+			NextHop: snet.CopyUDPAddr(v.NextHop),
+			Host:    snet.CopyUDPAddr(v.Host),
+		}
 	default:
 		log.Error("Not valid snet address")
 		return
diff --git a/go/sig/internal/sigcmn/common.go b/go/sig/internal/sigcmn/common.go
index 7425df412..5a293a8ea 100644
--- a/go/sig/internal/sigcmn/common.go
+++ b/go/sig/internal/sigcmn/common.go
@@ -126,7 +126,7 @@ func initNetworkWithRealSCIOND(cfg sigconfig.SigConf,
 }
 
 func EncapSnetAddr() *snet.UDPAddr {
-	return snet.NewUDPAddr(IA, nil, nil, &net.UDPAddr{IP: Host.IP(), Port: int(encapPort)})
+	return &snet.UDPAddr{IA: IA, Host: &net.UDPAddr{IP: Host.IP(), Port: int(encapPort)}}
 }
 
 func ValidatePort(desc string, port int) error {


Test patch:
diff --git a/go/cs/beaconing/handler_test.go b/go/cs/beaconing/handler_test.go
index ce403e51f..f4b53fe0f 100644
--- a/go/cs/beaconing/handler_test.go
+++ b/go/cs/beaconing/handler_test.go
@@ -90,7 +90,7 @@ func TestNewHandler(t *testing.T) {
 	handler := NewHandler(localIA, intfs, inserter, verifier)
 	t.Run("Wrong payload type", func(t *testing.T) {
 		req := infra.NewRequest(context.Background(), &ctrl.Pld{}, nil,
-			snet.NewUDPAddr(addr.IA{}, testPath(localIF), nil, nil), 0)
+			&snet.UDPAddr{IA: addr.IA{}, Path: testPath(localIF)}, 0)
 		res := handler.Handle(req)
 		assert.Equal(t, res, infra.MetricsErrInternal)
 	})
@@ -114,14 +114,14 @@ func TestNewHandler(t *testing.T) {
 	t.Run("Invalid hop field", func(t *testing.T) {
 		req := infra.NewRequest(
 			infra.NewContextWithResponseWriter(context.Background(), rw),
-			pseg, nil, snet.NewUDPAddr(addr.IA{}, &spath.Path{}, nil, nil), 0)
+			pseg, nil, &snet.UDPAddr{IA: addr.IA{}, Path: &spath.Path{}}, 0)
 		res := handler.Handle(req)
 		assert.Equal(t, res, infra.MetricsErrInvalid)
 	})
 	t.Run("Invalid unknown interface", func(t *testing.T) {
 		req := infra.NewRequest(
 			infra.NewContextWithResponseWriter(context.Background(), rw),
-			pseg, nil, snet.NewUDPAddr(addr.IA{}, testPath(12), nil, nil), 0)
+			pseg, nil, &snet.UDPAddr{IA: addr.IA{}, Path: testPath(12)}, 0)
 		res := handler.Handle(req)
 		assert.Equal(t, res, infra.MetricsErrInvalid)
 	})
@@ -130,7 +130,7 @@ func TestNewHandler(t *testing.T) {
 	t.Run("Invalid link type", func(t *testing.T) {
 		req := infra.NewRequest(
 			infra.NewContextWithResponseWriter(context.Background(), rw),
-			pseg, nil, snet.NewUDPAddr(addr.IA{}, testPath(42), nil, nil), 0)
+			pseg, nil, &snet.UDPAddr{IA: addr.IA{}, Path: testPath(42)}, 0)
 		res := handler.Handle(req)
 		assert.Equal(t, res, infra.MetricsErrInvalid)
 	})
@@ -202,7 +202,7 @@ func defaultTestReq(rw infra.ResponseWriter, pseg *seg.PathSegment) *infra.Reque
 		infra.NewContextWithResponseWriter(context.Background(), rw),
 		pseg,
 		nil,
-		snet.NewUDPAddr(addr.IA{}, testPath(localIF), nil, nil),
+		&snet.UDPAddr{IA: addr.IA{}, Path: testPath(localIF)},
 		0,
 	)
 }
diff --git a/go/cs/keepalive/handler_test.go b/go/cs/keepalive/handler_test.go
index d98ac439d..87d9e14c7 100644
--- a/go/cs/keepalive/handler_test.go
+++ b/go/cs/keepalive/handler_test.go
@@ -79,7 +79,7 @@ func TestNewHandler(t *testing.T) {
 			RevDropper:    dropper,
 		})
 		req := infra.NewRequest(context.Background(), &ifid.IFID{OrigIfID: originIF}, nil,
-			snet.NewUDPAddr(originIA, testPath(localIF), nil, nil), 0)
+			&snet.UDPAddr{IA: originIA, Path: testPath(localIF)}, 0)
 		res := handler.Handle(req)
 		waitTimeout(t, wg)
 		assert.Equal(t, res, infra.MetricsResultOk)
@@ -91,7 +91,7 @@ func TestNewHandler(t *testing.T) {
 		intfs.Get(localIF).Activate(42)
 		handler := NewHandler(localIA, intfs, zeroCallTasks(mctrl))
 		req := infra.NewRequest(context.Background(), &ifid.IFID{OrigIfID: originIF}, nil,
-			snet.NewUDPAddr(originIA, testPath(localIF), nil, nil), 0)
+			&snet.UDPAddr{IA: originIA, Path: testPath(localIF)}, 0)
 		res := handler.Handle(req)
 		assert.Equal(t, res, infra.MetricsResultOk)
 	})
@@ -107,7 +107,7 @@ func TestNewHandler(t *testing.T) {
 			{
 				msg: "Wrong payload type",
 				req: infra.NewRequest(context.Background(), &ctrl.Pld{}, nil,
-					snet.NewUDPAddr(originIA, testPath(localIF), nil, nil), 0),
+					&snet.UDPAddr{IA: originIA, Path: testPath(localIF)}, 0),
 				exp: infra.MetricsErrInternal,
 			},
 			{
@@ -119,19 +119,19 @@ func TestNewHandler(t *testing.T) {
 			{
 				msg: "Invalid path",
 				req: infra.NewRequest(context.Background(), &ifid.IFID{OrigIfID: originIF}, nil,
-					snet.NewUDPAddr(originIA, &spath.Path{}, nil, nil), 0),
+					&snet.UDPAddr{IA: originIA, Path: &spath.Path{}}, 0),
 				exp: infra.MetricsErrInvalid,
 			},
 			{
 				msg: "Invalid ConsIngress ifid",
 				req: infra.NewRequest(context.Background(), &ifid.IFID{OrigIfID: originIF}, nil,
-					snet.NewUDPAddr(originIA, testPath(originIF), nil, nil), 0),
+					&snet.UDPAddr{IA: originIA, Path: testPath(originIF)}, 0),
 				exp: infra.MetricsErrInvalid,
 			},
 			{
 				msg: "Invalid IA",
 				req: infra.NewRequest(context.Background(), &ifid.IFID{OrigIfID: originIF}, nil,
-					snet.NewUDPAddr(localIA, testPath(localIF), nil, nil), 0),
+					&snet.UDPAddr{IA: localIA, Path: testPath(localIF)}, 0),
 				exp: infra.MetricsErrInvalid,
 			},
 		}
diff --git a/go/hidden_path_srv/internal/hpcfgreq/handler_test.go b/go/hidden_path_srv/internal/hpcfgreq/handler_test.go
index b49db3514..9c0e62ea8 100644
--- a/go/hidden_path_srv/internal/hpcfgreq/handler_test.go
+++ b/go/hidden_path_srv/internal/hpcfgreq/handler_test.go
@@ -92,7 +92,7 @@ func TestCfgReq(t *testing.T) {
 			msg := &path_mgmt.HPCfgReq{
 				ChangedSince: 0,
 			}
-			peer := snet.NewUDPAddr(ia110, nil, nil, nil)
+			peer := &snet.UDPAddr{IA: ia110}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			reply := &path_mgmt.HPCfgReply{
 				Cfgs: []*path_mgmt.HPCfg{group1.ToMsg(), group2.ToMsg(), group3.ToMsg()},
@@ -137,7 +137,7 @@ func TestCfgReq(t *testing.T) {
 			msg := &path_mgmt.HPCfgReq{
 				ChangedSince: 0,
 			}
-			peer := snet.NewUDPAddr(ia111, nil, nil, nil)
+			peer := &snet.UDPAddr{IA: ia111}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			ack := ack.Ack{
 				Err:     proto.Ack_ErrCode_reject,
diff --git a/go/hidden_path_srv/internal/hpsegreq/fetcher_test.go b/go/hidden_path_srv/internal/hpsegreq/fetcher_test.go
index 36085826b..c78ecd0ca 100644
--- a/go/hidden_path_srv/internal/hpsegreq/fetcher_test.go
+++ b/go/hidden_path_srv/internal/hpsegreq/fetcher_test.go
@@ -138,7 +138,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group1.Id.ToMsg(),
@@ -159,7 +159,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group2.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group2.Id.ToMsg(),
@@ -179,7 +179,7 @@ func TestFetcher(t *testing.T) {
 						},
 					},
 				}
-				addr := snet.NewSVCAddr(ia115, nil, nil, addr.SvcHPS)
+				addr := &snet.SVCAddr{IA: ia115, SVC: addr.SvcHPS}
 				mockMsgr.EXPECT().GetHPSegs(gomock.Any(), gomock.Any(),
 					addr, gomock.Any()).Return(reply, nil)
 			},
@@ -189,7 +189,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg(), group2.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group1.Id.ToMsg(),
@@ -218,7 +218,7 @@ func TestFetcher(t *testing.T) {
 						},
 					},
 				}
-				addr := snet.NewSVCAddr(ia115, nil, nil, addr.SvcHPS)
+				addr := &snet.SVCAddr{IA: ia115, SVC: addr.SvcHPS}
 				mockDB.EXPECT().Get(gomock.Any(), gomock.Any()).Return(res, nil)
 				mockMsgr.EXPECT().GetHPSegs(gomock.Any(), gomock.Any(), addr,
 					gomock.Any()).Return(reply, nil)
@@ -229,7 +229,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group2.Id.ToMsg(), group3.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group2.Id.ToMsg(),
@@ -265,10 +265,10 @@ func TestFetcher(t *testing.T) {
 						},
 					},
 				}
-				addr2 := snet.NewSVCAddr(ia115, nil, nil, addr.SvcHPS)
+				addr2 := &snet.SVCAddr{IA: ia115, SVC: addr.SvcHPS}
 				mockMsgr.EXPECT().GetHPSegs(gomock.Any(), gomock.Any(), addr2,
 					gomock.Any()).Return(reply2, nil)
-				addr3 := snet.NewSVCAddr(ia114, nil, nil, addr.SvcHPS)
+				addr3 := &snet.SVCAddr{IA: ia114, SVC: addr.SvcHPS}
 				mockMsgr.EXPECT().GetHPSegs(gomock.Any(), gomock.Any(), addr3,
 					gomock.Any()).Return(reply3, nil)
 			},
@@ -278,7 +278,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group1.Id.ToMsg(),
@@ -294,7 +294,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group2.Id.ToMsg()},
 			},
-			peer: snet.NewUDPAddr(ia113, nil, nil, nil),
+			peer: &snet.UDPAddr{IA: ia113},
 			res: []*path_mgmt.HPSegRecs{
 				{
 					GroupId: group2.Id.ToMsg(),
@@ -302,7 +302,7 @@ func TestFetcher(t *testing.T) {
 				},
 			},
 			setup: func(mockDB *mock_pathdb.MockPathDB, mockMsgr *mock_infra.MockMessenger) {
-				addr := snet.NewSVCAddr(ia115, nil, nil, addr.SvcHPS)
+				addr := &snet.SVCAddr{IA: ia115, SVC: addr.SvcHPS}
 				mockMsgr.EXPECT().GetHPSegs(gomock.Any(), gomock.Any(),
 					addr, gomock.Any()).Return(nil, errors.New("dummy"))
 			},
@@ -312,7 +312,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{wrongId.ToMsg()},
 			},
-			peer:  snet.NewUDPAddr(ia114, nil, nil, nil),
+			peer:  &snet.UDPAddr{IA: ia114},
 			err:   hpsegreq.ErrUnknownGroup,
 			setup: func(mockDB *mock_pathdb.MockPathDB, mockMsgr *mock_infra.MockMessenger) {},
 		},
@@ -321,7 +321,7 @@ func TestFetcher(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg()},
 			},
-			peer:  snet.NewUDPAddr(ia114, nil, nil, nil),
+			peer:  &snet.UDPAddr{IA: ia114},
 			err:   hpsegreq.ErrNotReader,
 			setup: func(mockDB *mock_pathdb.MockPathDB, mockMsgr *mock_infra.MockMessenger) {},
 		},
diff --git a/go/hidden_path_srv/internal/hpsegreq/handler_test.go b/go/hidden_path_srv/internal/hpsegreq/handler_test.go
index dbc80e18f..3babeb915 100644
--- a/go/hidden_path_srv/internal/hpsegreq/handler_test.go
+++ b/go/hidden_path_srv/internal/hpsegreq/handler_test.go
@@ -48,7 +48,7 @@ func TestSegReq(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg()},
 			}
-			peer := snet.NewUDPAddr(addr.IA{}, nil, nil, nil)
+			peer := &snet.UDPAddr{IA: addr.IA{}}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			recs := []*path_mgmt.HPSegRecs{
 				{
@@ -99,7 +99,7 @@ func TestSegReq(t *testing.T) {
 				RawDstIA: ia111.IAInt(),
 				GroupIds: []*path_mgmt.HPGroupId{group1.Id.ToMsg()},
 			}
-			peer := snet.NewUDPAddr(addr.IA{}, nil, nil, nil)
+			peer := &snet.UDPAddr{IA: addr.IA{}}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			m.fetcher.EXPECT().Fetch(gomock.Any(), gomock.Any(),
 				gomock.Any()).Return(nil, errors.New("dummy"))
diff --git a/go/hidden_path_srv/internal/registration/handler_test.go b/go/hidden_path_srv/internal/registration/handler_test.go
index 5dac1f2e4..4f3fcb52a 100644
--- a/go/hidden_path_srv/internal/registration/handler_test.go
+++ b/go/hidden_path_srv/internal/registration/handler_test.go
@@ -53,7 +53,7 @@ func TestSegReg(t *testing.T) {
 					Recs:    []*seg.Meta{seg110_133},
 				},
 			}
-			peer := snet.NewUDPAddr(addr.IA{}, nil, nil, nil)
+			peer := &snet.UDPAddr{IA: addr.IA{}}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			ack := ack.Ack{
 				Err:     proto.Ack_ErrCode_ok,
@@ -131,7 +131,7 @@ func TestSegReg(t *testing.T) {
 					Recs:    []*seg.Meta{seg110_133},
 				},
 			}
-			peer := snet.NewUDPAddr(addr.IA{}, &spath.Path{}, nil, nil)
+			peer := &snet.UDPAddr{IA: addr.IA{}, Path: &spath.Path{}}
 			req := infra.NewRequest(ctx, msg, nil, peer, 0)
 			ack := ack.Ack{
 				Err:     proto.Ack_ErrCode_reject,
diff --git a/go/lib/infra/messenger/addr_test.go b/go/lib/infra/messenger/addr_test.go
index 9b4fd3b19..9e2226531 100644
--- a/go/lib/infra/messenger/addr_test.go
+++ b/go/lib/infra/messenger/addr_test.go
@@ -63,8 +63,8 @@ func TestRedirectQUIC(t *testing.T) {
 			assertErr:             assert.Error,
 		},
 		"valid UDPAddr, returns unchanged": {
-			input:                 snet.NewUDPAddr(dummyIA, nil, nil, &net.UDPAddr{}),
-			wantAddr:              snet.NewUDPAddr(dummyIA, nil, nil, &net.UDPAddr{}),
+			input:                 &snet.UDPAddr{IA: dummyIA, Host: &net.UDPAddr{}},
+			wantAddr:              &snet.UDPAddr{IA: dummyIA, Host: &net.UDPAddr{}},
 			wantRedirect:          false,
 			SVCResolutionFraction: 1.0,
 			assertErr:             assert.NoError,
@@ -112,11 +112,15 @@ func TestRedirectQUIC(t *testing.T) {
 			SVCResolutionFraction: 0.5,
 		}
 
-		input := snet.NewSVCAddr(dummyIA, nil, nil, addr.SvcBS)
-		want := snet.NewSVCAddr(dummyIA, nil, &net.UDPAddr{IP: net.ParseIP("10.1.1.1")}, addr.SvcBS)
+		input := &snet.SVCAddr{IA: dummyIA, SVC: addr.SvcBS}
+		want := &snet.SVCAddr{
+			IA:      dummyIA,
+			NextHop: &net.UDPAddr{IP: net.ParseIP("10.1.1.1")},
+			SVC:     addr.SvcBS,
+		}
 		a, r, err := aw.RedirectToQUIC(context.Background(), input)
 		assert.NoError(t, err)
-		assert.Equal(t, a, want)
+		assert.Equal(t, want, a)
 		assert.False(t, r)
 	})
 
@@ -142,12 +146,16 @@ func TestRedirectQUIC(t *testing.T) {
 			SVCResolutionFraction: 1,
 		}
 
-		input := snet.NewSVCAddr(dummyIA, nil, nil, addr.SvcBS)
-		want := snet.NewUDPAddr(dummyIA, &spath.Path{}, &net.UDPAddr{IP: net.ParseIP("10.1.1.1")},
-			&net.UDPAddr{IP: net.ParseIP("192.168.1.1"), Port: 8000})
+		input := &snet.SVCAddr{IA: dummyIA, SVC: addr.SvcBS}
+		want := &snet.UDPAddr{
+			IA:      dummyIA,
+			Path:    &spath.Path{},
+			NextHop: &net.UDPAddr{IP: net.ParseIP("10.1.1.1")},
+			Host:    &net.UDPAddr{IP: net.ParseIP("192.168.1.1"), Port: 8000},
+		}
 		a, r, err := aw.RedirectToQUIC(context.Background(), input)
 		assert.NoError(t, err)
-		assert.Equal(t, a, want)
+		assert.Equal(t, want, a)
 		assert.True(t, r)
 	})
 }
@@ -163,7 +171,7 @@ func TestBuildFullAddress(t *testing.T) {
 			Router:    router,
 			SVCRouter: svcRouter,
 		}
-		input := snet.NewSVCAddr(remoteIA, nil, nil, addr.SvcBS)
+		input := &snet.SVCAddr{IA: remoteIA, SVC: addr.SvcBS}
 		router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("err"))
 		_, err := aw.BuildFullAddress(context.Background(), input)
 		assert.Error(t, err)
@@ -180,7 +188,7 @@ func TestBuildFullAddress(t *testing.T) {
 			SVCRouter: svcRouter,
 		}
 
-		input := snet.NewSVCAddr(remoteIA, &spath.Path{}, nil, addr.SvcBS)
+		input := &snet.SVCAddr{IA: remoteIA, Path: &spath.Path{}, SVC: addr.SvcBS}
 		a, err := aw.BuildFullAddress(context.Background(), input)
 		assert.Equal(t, a, input)
 		assert.NoError(t, err)
@@ -202,9 +210,14 @@ func TestBuildFullAddress(t *testing.T) {
 		path.EXPECT().OverlayNextHop().Return(&net.UDPAddr{})
 		path.EXPECT().Fingerprint().Return(snet.PathFingerprint("foo"))
 		router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil)
-		input := snet.NewSVCAddr(remoteIA, nil, nil, addr.SvcBS)
+		input := &snet.SVCAddr{IA: remoteIA, SVC: addr.SvcBS}
 		a, err := aw.BuildFullAddress(context.Background(), input)
-		want := snet.NewSVCAddr(remoteIA, &spath.Path{}, &net.UDPAddr{}, addr.SvcBS)
+		want := &snet.SVCAddr{
+			IA:      remoteIA,
+			Path:    &spath.Path{},
+			NextHop: &net.UDPAddr{},
+			SVC:     addr.SvcBS,
+		}
 		assert.Equal(t, a, want)
 		assert.NoError(t, err)
 	})
@@ -232,10 +245,10 @@ func TestBuildFullAddress(t *testing.T) {
 		path.EXPECT().OverlayNextHop()
 		router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil)
 
-		input := snet.NewSVCAddr(localIA, nil, nil, addr.SvcBS)
+		input := &snet.SVCAddr{IA: localIA, SVC: addr.SvcBS}
 		a, err := aw.BuildFullAddress(context.Background(), input)
 
-		want := snet.NewSVCAddr(localIA, nil, overlayAddr, addr.SvcBS)
+		want := &snet.SVCAddr{IA: localIA, NextHop: overlayAddr, SVC: addr.SvcBS}
 		assert.Equal(t, a, want)
 		assert.NoError(t, err)
 	})
@@ -259,7 +272,7 @@ func TestBuildFullAddress(t *testing.T) {
 		path.EXPECT().OverlayNextHop()
 		router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil)
 
-		input := snet.NewSVCAddr(localIA, nil, nil, addr.SvcBS)
+		input := &snet.SVCAddr{IA: localIA, SVC: addr.SvcBS}
 		a, err := aw.BuildFullAddress(context.Background(), input)
 		assert.Nil(t, a)
 		assert.Error(t, err)
diff --git a/go/lib/infra/modules/trust/inserter_test.go b/go/lib/infra/modules/trust/inserter_test.go
index b35645cf8..554cb826f 100644
--- a/go/lib/infra/modules/trust/inserter_test.go
+++ b/go/lib/infra/modules/trust/inserter_test.go
@@ -237,7 +237,7 @@ func TestFwdInserterInsertChain(t *testing.T) {
 				m.DB.EXPECT().ChainExists(gomock.Any(), dec).Return(
 					false, nil,
 				)
-				a := snet.NewSVCAddr(addr.IA{}, nil, nil, addr.SvcCS)
+				a := &snet.SVCAddr{IA: addr.IA{}, SVC: addr.SvcCS}
 				m.RPC.EXPECT().SendCertChain(gomock.Any(), dec.Raw, a).Return(nil)
 				m.DB.EXPECT().InsertChain(gomock.Any(), dec).Return(
 					true, true, nil,
@@ -264,7 +264,7 @@ func TestFwdInserterInsertChain(t *testing.T) {
 				m.DB.EXPECT().ChainExists(gomock.Any(), dec).Return(
 					false, nil,
 				)
-				a := snet.NewSVCAddr(addr.IA{}, nil, nil, addr.SvcCS)
+				a := &snet.SVCAddr{IA: addr.IA{}, SVC: addr.SvcCS}
 				m.RPC.EXPECT().SendCertChain(gomock.Any(), dec.Raw, a).Return(internal)
 			},
 			ExpectedErr: internal,
@@ -274,7 +274,7 @@ func TestFwdInserterInsertChain(t *testing.T) {
 				m.DB.EXPECT().ChainExists(gomock.Any(), dec).Return(
 					false, nil,
 				)
-				a := snet.NewSVCAddr(addr.IA{}, nil, nil, addr.SvcCS)
+				a := &snet.SVCAddr{IA: addr.IA{}, SVC: addr.SvcCS}
 				m.RPC.EXPECT().SendCertChain(gomock.Any(), dec.Raw, a).Return(nil)
 				m.DB.EXPECT().InsertChain(gomock.Any(), dec).Return(
 					false, false, trust.ErrContentMismatch,
diff --git a/go/lib/infra/modules/trust/recurser_test.go b/go/lib/infra/modules/trust/recurser_test.go
index 890855b44..42c368848 100644
--- a/go/lib/infra/modules/trust/recurser_test.go
+++ b/go/lib/infra/modules/trust/recurser_test.go
@@ -33,11 +33,11 @@ func TestASLocalRecurserAllowRecursion(t *testing.T) {
 			Assertion: assert.NoError,
 		},
 		"AS local": {
-			Addr:      snet.NewUDPAddr(ia110, nil, nil, nil),
+			Addr:      &snet.UDPAddr{IA: ia110},
 			Assertion: assert.NoError,
 		},
 		"remote AS": {
-			Addr:      snet.NewUDPAddr(ia120, nil, nil, nil),
+			Addr:      &snet.UDPAddr{IA: ia120},
 			Assertion: assert.Error,
 		},
 		"invalid Address": {
@@ -63,11 +63,11 @@ func TestLocalOnlyRecurserAllowRecursion(t *testing.T) {
 			Assertion: assert.NoError,
 		},
 		"AS local": {
-			Addr:      snet.NewUDPAddr(ia110, nil, nil, nil),
+			Addr:      &snet.UDPAddr{IA: ia110},
 			Assertion: assert.Error,
 		},
 		"remote AS": {
-			Addr:      snet.NewUDPAddr(ia120, nil, nil, nil),
+			Addr:      &snet.UDPAddr{IA: ia120},
 			Assertion: assert.Error,
 		},
 		"invalid Address": {
diff --git a/go/lib/infra/modules/trust/router_test.go b/go/lib/infra/modules/trust/router_test.go
index b432ead44..0bf9e1485 100644
--- a/go/lib/infra/modules/trust/router_test.go
+++ b/go/lib/infra/modules/trust/router_test.go
@@ -42,7 +42,7 @@ func TestLocalRouterChooseServer(t *testing.T) {
 	}
 	for name, isd := range tests {
 		t.Run(name, func(t *testing.T) {
-			localCS := snet.NewSVCAddr(ia122, nil, nil, addr.SvcCS)
+			localCS := &snet.SVCAddr{IA: ia122, SVC: addr.SvcCS}
 			router := trust.LocalRouter{
 				IA: localCS.IA,
 			}
@@ -146,8 +146,12 @@ func TestCSRouterChooseServer(t *testing.T) {
 				xtest.AssertErrorsIs(t, err, test.ExpectedErr)
 			} else {
 				require.NoError(t, err)
-				expected := snet.NewSVCAddr(p.Destination(), p.Path(),
-					p.OverlayNextHop(), addr.SvcCS)
+				expected := &snet.SVCAddr{
+					IA:      p.Destination(),
+					Path:    p.Path(),
+					NextHop: p.OverlayNextHop(),
+					SVC:     addr.SvcCS,
+				}
 				assert.Equal(t, expected, res)
 			}
 		})
diff --git a/go/lib/snet/addr_test.go b/go/lib/snet/addr_test.go
deleted file mode 100644
index 521171a1e..000000000
--- a/go/lib/snet/addr_test.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 ETH Zurich
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package snet_test
-
-import (
-	"fmt"
-	"net"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/snet"
-)
-
-func Test_Addr_String(t *testing.T) {
-	ia, _ := addr.IAFromString("1-ff00:0:320")
-	host4 := &addr.AppAddr{
-		L3: addr.HostIPv4(net.IPv4(1, 2, 3, 4)),
-		L4: 10000,
-	}
-	host6 := &addr.AppAddr{
-		L3: addr.HostFromIPStr("2001::1"),
-		L4: 20000,
-	}
-	tests := []struct {
-		address *snet.Addr
-		result  string
-	}{
-		{address: &snet.Addr{IA: ia, Host: host4}, result: "1-ff00:0:320,[1.2.3.4]:10000"},
-		{address: &snet.Addr{IA: ia, Host: host6}, result: "1-ff00:0:320,[2001::1]:20000"},
-	}
-	for _, test := range tests {
-		t.Run(fmt.Sprintf("given address object %v", test.address), func(t *testing.T) {
-			s := test.address.String()
-			assert.Equal(t, s, test.result, "String should match")
-		})
-	}
-}
-
-func Test_AddrFromString(t *testing.T) {
-	tests := []struct {
-		address string
-		isError bool
-		ia      string
-		host    string
-		l4      uint16
-	}{
-		{address: "foo", isError: true},
-		{address: "5-", isError: true},
-		{address: "2-ff00:0:300,[", isError: true},
-		{address: "5-ff00:0:300,[]:", isError: true},
-		{address: "40-ff00:0:300,[]:19", isError: true},
-		{address: "1-ff00:0:300,[]:13,[f", isError: true},
-		{address: "1-ff00:0:300,[abc]:12", isError: true},
-		{address: "1-ff00:0:300]:14,[1.2.3.4]", isError: true},
-		{address: "1-ff00:0:300,[1.2.3.4]:70000", isError: true},
-		{address: "", isError: true},
-		{address: "1-ff00:0:300,[1.2.3.4]:80",
-			ia:   "1-ff00:0:300",
-			host: "1.2.3.4",
-			l4:   80,
-		},
-		{address: "1-ff00:0:301,[1.2.3.4]",
-			ia:   "1-ff00:0:301",
-			host: "1.2.3.4",
-		},
-		{address: "50-ff00:0:350,[1.1.1.1]:5",
-			ia:   "50-ff00:0:350",
-			host: "1.1.1.1",
-			l4:   5,
-		},
-		{address: "1-ff00:0:302,[::1]:60000",
-			ia:   "1-ff00:0:302",
-			host: "::1",
-			l4:   60000,
-		},
-		{address: "4-ff00:0:300,[BS]",
-			ia:   "4-ff00:0:300",
-			host: "BS A (0x0000)",
-		},
-		{address: "4-ff00:0:300,[PS]",
-			ia:   "4-ff00:0:300",
-			host: "PS A (0x0001)",
-		},
-		{address: "4-ff00:0:300,[PS_A]",
-			ia:   "4-ff00:0:300",
-			host: "PS A (0x0001)",
-		},
-		{address: "4-ff00:0:300,[CS_M]",
-			ia:   "4-ff00:0:300",
-			host: "CS M (0x8002)",
-		},
-	}
-	for _, test := range tests {
-		t.Log(fmt.Sprintf("given address %q", test.address))
-		a, err := snet.AddrFromString(test.address)
-		if test.isError {
-			assert.Error(t, err)
-		} else {
-			assert.NoError(t, err)
-			assert.Equal(t, a.IA.String(), test.ia)
-			assert.Equal(t, a.Host.L3.String(), test.host)
-			assert.Equal(t, a.Host.L4, test.l4)
-		}
-	}
-}
diff --git a/go/lib/snet/svcaddr_test.go b/go/lib/snet/svcaddr_test.go
index d2356da3d..9e635ebfd 100644
--- a/go/lib/snet/svcaddr_test.go
+++ b/go/lib/snet/svcaddr_test.go
@@ -1,3 +1,17 @@
+// Copyright 2019 Anapaya Systems
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package snet_test
 
 import (
diff --git a/go/lib/snet/udpaddr_test.go b/go/lib/snet/udpaddr_test.go
index 0c3264bf5..8f65fa007 100644
--- a/go/lib/snet/udpaddr_test.go
+++ b/go/lib/snet/udpaddr_test.go
@@ -1,6 +1,21 @@
+// Copyright 2019 Anapaya Systems
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package snet_test
 
 import (
+	"fmt"
 	"net"
 	"testing"
 
@@ -29,6 +44,20 @@ func TestUDPAddrString(t *testing.T) {
 			input: &snet.UDPAddr{Host: &net.UDPAddr{}},
 			want:  "0-0,[<nil>]:0",
 		},
+		"ipv4": {
+			input: &snet.UDPAddr{
+				IA:   xtest.MustParseIA("1-ff00:0:320"),
+				Host: &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 10000},
+			},
+			want: "1-ff00:0:320,[1.2.3.4]:10000",
+		},
+		"ipv6": {
+			input: &snet.UDPAddr{
+				IA:   xtest.MustParseIA("1-ff00:0:320"),
+				Host: &net.UDPAddr{IP: net.ParseIP("2001::1"), Port: 20000},
+			},
+			want: "1-ff00:0:320,[2001::1]:20000",
+		},
 	}
 	for n, tc := range tests {
 		t.Run(n, func(t *testing.T) {
@@ -67,15 +96,13 @@ func TestUDPAddrSet(t *testing.T) {
 		"good input": {
 			Input: "1-ff00:0:1,[192.168.1.1]:80",
 			Error: assert.NoError,
-			Want: snet.NewUDPAddr(
-				xtest.MustParseIA("1-ff00:0:1"),
-				nil,
-				nil,
-				&net.UDPAddr{
-					IP:   net.IP{192, 168, 1, 1},
+			Want: &snet.UDPAddr{
+				IA: xtest.MustParseIA("1-ff00:0:1"),
+				Host: &net.UDPAddr{
+					IP:   net.ParseIP("192.168.1.1"),
 					Port: 80,
 				},
-			),
+			},
 		},
 	}
 	for n, tc := range testCases {
@@ -89,3 +116,55 @@ func TestUDPAddrSet(t *testing.T) {
 		})
 	}
 }
+
+func TestUDPAddrFromString(t *testing.T) {
+	tests := []struct {
+		address string
+		isError bool
+		ia      string
+		host    string
+		port    int
+	}{
+		{address: "foo", isError: true},
+		{address: "5-", isError: true},
+		{address: "2-ff00:0:300,[", isError: true},
+		{address: "5-ff00:0:300,[]:", isError: true},
+		{address: "40-ff00:0:300,[]:19", isError: true},
+		{address: "1-ff00:0:300,[]:13,[f", isError: true},
+		{address: "1-ff00:0:300,[abc]:12", isError: true},
+		{address: "1-ff00:0:300]:14,[1.2.3.4]", isError: true},
+		{address: "1-ff00:0:300,[1.2.3.4]:70000", isError: true},
+		{address: "", isError: true},
+		{address: "1-ff00:0:300,[1.2.3.4]:80",
+			ia:   "1-ff00:0:300",
+			host: "1.2.3.4",
+			port: 80,
+		},
+		{address: "1-ff00:0:301,[1.2.3.4]",
+			ia:   "1-ff00:0:301",
+			host: "1.2.3.4",
+		},
+		{address: "50-ff00:0:350,[1.1.1.1]:5",
+			ia:   "50-ff00:0:350",
+			host: "1.1.1.1",
+			port: 5,
+		},
+		{address: "1-ff00:0:302,[::1]:60000",
+			ia:   "1-ff00:0:302",
+			host: "::1",
+			port: 60000,
+		},
+	}
+	for _, test := range tests {
+		t.Log(fmt.Sprintf("given address %q", test.address))
+		a, err := snet.UDPAddrFromString(test.address)
+		if test.isError {
+			assert.Error(t, err)
+		} else {
+			assert.NoError(t, err)
+			assert.Equal(t, test.ia, a.IA.String())
+			assert.Equal(t, net.ParseIP(test.host), a.Host.IP)
+			assert.Equal(t, test.port, a.Host.Port)
+		}
+	}
+}
diff --git a/go/lib/xtest/matchers/matchers.go b/go/lib/xtest/matchers/matchers.go
index 0ebda4a5f..7c76f51dd 100644
--- a/go/lib/xtest/matchers/matchers.go
+++ b/go/lib/xtest/matchers/matchers.go
@@ -38,8 +38,8 @@ type addrIAMatcher struct {
 	ia addr.IA
 }
 
-// IsSnetAddrWithIA returns a matcher for a snet.Addr with the given IA.
-func IsSnetAddrWithIA(ia addr.IA) gomock.Matcher {
+// IsSnetSVCAddrWithIA returns a matcher for a snet.SVCAddr with the given IA.
+func IsSnetSVCAddrWithIA(ia addr.IA) gomock.Matcher {
 	return &addrIAMatcher{ia: ia}
 }
 
diff --git a/go/sig/internal/ingress/worker_test.go b/go/sig/internal/ingress/worker_test.go
index 076626570..7d3ed90c4 100644
--- a/go/sig/internal/ingress/worker_test.go
+++ b/go/sig/internal/ingress/worker_test.go
@@ -63,15 +63,13 @@ func SendFrame(t *testing.T, w *Worker, data []byte) {
 }
 
 func TestParsing(t *testing.T) {
-	addr := snet.NewUDPAddr(
-		xtest.MustParseIA("1-ff00:0:300"),
-		nil,
-		nil,
-		&net.UDPAddr{
+	addr := &snet.UDPAddr{
+		IA: xtest.MustParseIA("1-ff00:0:300"),
+		Host: &net.UDPAddr{
 			IP:   net.IP{192, 168, 1, 1},
 			Port: 80,
 		},
-	)
+	}
 	mt := &MockTun{}
 	w := NewWorker(addr, 1, mt)