You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/11/22 13:25:19 UTC

[plc4x] branch develop updated: feat(plc4go/bacnet): progress on network stack

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 09796b07b2 feat(plc4go/bacnet): progress on network stack
09796b07b2 is described below

commit 09796b07b20a52f4cf244e798a3a5579a48b9ded
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Tue Nov 22 14:25:11 2022 +0100

    feat(plc4go/bacnet): progress on network stack
---
 plc4go/internal/bacnetip/ApplicationLayer.go       |  16 +-
 .../bacnetip/BACnetVirtualLinkLayerService.go      |   6 +-
 plc4go/internal/bacnetip/CommunicationsModule.go   |   8 +-
 plc4go/internal/bacnetip/MessageCodec.go           |   2 +-
 plc4go/internal/bacnetip/NetworkService.go         | 424 +++++++++++++++++++--
 plc4go/internal/bacnetip/PDU.go                    |  83 ++--
 plc4go/internal/bacnetip/Settings.go               |  38 ++
 7 files changed, 489 insertions(+), 88 deletions(-)

diff --git a/plc4go/internal/bacnetip/ApplicationLayer.go b/plc4go/internal/bacnetip/ApplicationLayer.go
index 1f5ac4e167..2d7f279317 100644
--- a/plc4go/internal/bacnetip/ApplicationLayer.go
+++ b/plc4go/internal/bacnetip/ApplicationLayer.go
@@ -96,7 +96,7 @@ type SSM struct {
 
 	ssmSAP SSMSAPRequirements
 
-	pduAddress Address
+	pduAddress *Address
 	deviceInfo *DeviceInfo
 
 	invokeId uint8
@@ -121,10 +121,10 @@ type SSM struct {
 	maxApduLengthAccepted readWriteModel.MaxApduLengthAccepted
 }
 
-func NewSSM(sap SSMSAPRequirements, pduAddress Address) (SSM, error) {
+func NewSSM(sap SSMSAPRequirements, pduAddress *Address) (SSM, error) {
 	log.Debug().Interface("sap", sap).Interface("pdu_address", pduAddress).Msg("init")
 	var deviceInfo *DeviceInfo
-	deviceInfoTemp, ok := sap.GetDeviceInfoCache().GetDeviceInfo(DeviceInfoCacheKey{PduSource: &pduAddress})
+	deviceInfoTemp, ok := sap.GetDeviceInfoCache().GetDeviceInfo(DeviceInfoCacheKey{PduSource: pduAddress})
 	if ok {
 		deviceInfo = &deviceInfoTemp
 	}
@@ -377,7 +377,7 @@ type ClientSSM struct {
 	SSM
 }
 
-func NewClientSSM(sap SSMSAPRequirements, pduAddress Address) (*ClientSSM, error) {
+func NewClientSSM(sap SSMSAPRequirements, pduAddress *Address) (*ClientSSM, error) {
 	log.Debug().Interface("sap", sap).Interface("pduAddress", pduAddress).Msg("init")
 	ssm, err := NewSSM(sap, pduAddress)
 	if err != nil {
@@ -417,8 +417,7 @@ func (c *ClientSSM) Request(apdu _PDU) error {
 	log.Debug().Msgf("request\n%c", apdu)
 
 	// make sure it has a good source and destination
-	nullAddress, _ := NewAddress()
-	apdu = NewPDUFromPDU(apdu, WithPDUSource(*nullAddress), WithPDUDestination(c.pduAddress))
+	apdu = NewPDUFromPDU(apdu, WithPDUSource(nil), WithPDUDestination(c.pduAddress))
 
 	// send it via the device
 	return c.ssmSAP.Request(apdu)
@@ -533,8 +532,7 @@ func (c *ClientSSM) Response(apdu _PDU) error {
 	log.Debug().Msgf("response\n%c", apdu)
 
 	// make sure it has a good source and destination
-	nullAddress, _ := NewAddress()
-	apdu = NewPDUFromPDU(apdu, WithPDUSource(c.pduAddress), WithPDUDestination(*nullAddress))
+	apdu = NewPDUFromPDU(apdu, WithPDUSource(c.pduAddress), WithPDUDestination(nil))
 
 	// send it to the application
 	return c.ssmSAP.SapResponse(apdu)
@@ -951,7 +949,7 @@ type ServerSSM struct {
 	segmentedResponseAccepted bool
 }
 
-func NewServerSSM(sap SSMSAPRequirements, pduAddress Address) (*ServerSSM, error) {
+func NewServerSSM(sap SSMSAPRequirements, pduAddress *Address) (*ServerSSM, error) {
 	log.Debug().Interface("sap", sap).Interface("pduAddress", pduAddress).Msg("init")
 	ssm, err := NewSSM(sap, pduAddress)
 	if err != nil {
diff --git a/plc4go/internal/bacnetip/BACnetVirtualLinkLayerService.go b/plc4go/internal/bacnetip/BACnetVirtualLinkLayerService.go
index 70104186ec..9e7212872a 100644
--- a/plc4go/internal/bacnetip/BACnetVirtualLinkLayerService.go
+++ b/plc4go/internal/bacnetip/BACnetVirtualLinkLayerService.go
@@ -121,7 +121,7 @@ func NewUDPMultiplexer(address interface{}, noBroadcast bool) (*UDPMultiplexer,
 	log.Debug().Msgf("address: %v", u.address)
 	log.Debug().Msgf("addrTuple: %v", u.addrTuple)
 	log.Debug().Msgf("addrBroadcastTuple: %v", u.addrBroadcastTuple)
-	//log.Debug().Msgf("route_aware: %v", settings.RouteAware)
+	log.Debug().Msgf("route_aware: %v", settings.RouteAware)
 
 	// create and bind direct address
 	var err error
@@ -176,7 +176,7 @@ func (m *UDPMultiplexer) Indication(server *_MultiplexServer, pdu _PDU) error {
 	pduDestination := pdu.GetPDUDestination()
 
 	// broadcast message
-	var dest Address
+	var dest *Address
 	if pduDestination.AddrType == LOCAL_BROADCAST_ADDRESS {
 		// interface might not support broadcasts
 		if m.addrBroadcastTuple == nil {
@@ -187,7 +187,7 @@ func (m *UDPMultiplexer) Indication(server *_MultiplexServer, pdu _PDU) error {
 		if err != nil {
 			return errors.Wrap(err, "error getting address from tuple")
 		}
-		dest = *address
+		dest = address
 		log.Debug().Msgf("requesting local broadcast: %v", dest)
 	} else if pduDestination.AddrType == LOCAL_STATION_ADDRESS {
 		dest = pduDestination
diff --git a/plc4go/internal/bacnetip/CommunicationsModule.go b/plc4go/internal/bacnetip/CommunicationsModule.go
index 95d9e8c89c..c666e2d2ff 100644
--- a/plc4go/internal/bacnetip/CommunicationsModule.go
+++ b/plc4go/internal/bacnetip/CommunicationsModule.go
@@ -43,16 +43,16 @@ func init() {
 // TODO: implement me
 type _PCI struct {
 	pduUserData    interface{}
-	pduSource      Address
-	pduDestination Address
+	pduSource      *Address
+	pduDestination *Address
 }
 
-func _New_PCI(pduUserData interface{}, pduSource Address, pduDestination Address) *_PCI {
+func _New_PCI(pduUserData interface{}, pduSource *Address, pduDestination *Address) *_PCI {
 	return &_PCI{pduUserData, pduSource, pduDestination}
 }
 
 func (p *_PCI) String() string {
-	return fmt.Sprintf("pduUserData:\n%s\n, pduSource: %s, pduDestination: %s", p.pduUserData, &p.pduSource, &p.pduDestination)
+	return fmt.Sprintf("pduUserData:\n%s\n, pduSource: %s, pduDestination: %s", p.pduUserData, p.pduSource, p.pduDestination)
 }
 
 // _Client is an interface used for documentation
diff --git a/plc4go/internal/bacnetip/MessageCodec.go b/plc4go/internal/bacnetip/MessageCodec.go
index 2cc5735f0b..a037367ae5 100644
--- a/plc4go/internal/bacnetip/MessageCodec.go
+++ b/plc4go/internal/bacnetip/MessageCodec.go
@@ -96,7 +96,7 @@ func (m *ApplicationLayerMessageCodec) Send(message spi.Message) error {
 	if err2 != nil {
 		panic(err2)
 	}
-	iocb, err := NewIOCB(NewPDU(message, WithPDUDestination(*address)), m.remoteAddress)
+	iocb, err := NewIOCB(NewPDU(message, WithPDUDestination(address)), m.remoteAddress)
 	if err != nil {
 		return errors.Wrap(err, "error creating IOCB")
 	}
diff --git a/plc4go/internal/bacnetip/NetworkService.go b/plc4go/internal/bacnetip/NetworkService.go
index a0b1c80142..c00ff11c04 100644
--- a/plc4go/internal/bacnetip/NetworkService.go
+++ b/plc4go/internal/bacnetip/NetworkService.go
@@ -21,20 +21,92 @@ package bacnetip
 
 import (
 	"fmt"
+	readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model"
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
+	"math"
 )
 
+type RouterStatus uint8
+
+const (
+	ROUTER_AVAILABLE    RouterStatus = iota // normal
+	ROUTER_BUSY                             // router is busy
+	ROUTER_DISCONNECTED                     // could make a connection, but hasn't
+	ROUTER_UNREACHABLE                      // temporarily unreachable
+)
+
+func (r RouterStatus) String() string {
+	switch r {
+	case ROUTER_AVAILABLE:
+		return "ROUTER_AVAILABLE"
+	case ROUTER_BUSY:
+		return "ROUTER_BUSY"
+	case ROUTER_DISCONNECTED:
+		return "ROUTER_DISCONNECTED"
+	case ROUTER_UNREACHABLE:
+		return "ROUTER_UNREACHABLE"
+	default:
+		return "Unknown"
+	}
+}
+
+type RouterInfo struct {
+	snet    *uint16
+	address Address
+	dnets   map[*uint16]RouterStatus
+}
+
+func (r RouterInfo) String() string {
+	return fmt.Sprintf("%#q", r)
+}
+
+type RouterInfoCache struct {
+	routers  map[*uint16]*RouterInfo // TODO: snet -> {Address: RouterInfo}
+	pathInfo map[*uint16]*RouterInfo // TODO: (snet, dnet) -> RouterInfo
+}
+
+func NewRouterInfoCache() *RouterInfoCache {
+	log.Debug().Msg("NewRouterInfoCache")
+	return &RouterInfoCache{
+		routers:  map[*uint16]*RouterInfo{},
+		pathInfo: map[*uint16]*RouterInfo{},
+	}
+}
+
+func (n *RouterInfoCache) GetRouterInfo(*uint16, *uint16) *RouterInfo {
+	panic("not implemented yet")
+	return nil
+}
+
+func (n *RouterInfoCache) UpdateRouterInfo(*uint16, interface{}, interface{}) error {
+	panic("not implemented yet")
+	return nil
+}
+
+func (n *RouterInfoCache) UpdateRouterStatus(*uint16, *Address, []*uint16) {
+	panic("not implemented yet")
+}
+
+func (n *RouterInfoCache) DeleteRouterInfo(*uint16, interface{}, interface{}) error {
+	panic("not implemented yet")
+	return nil
+}
+
+func (n *RouterInfoCache) UpdateSourceNetwork() {
+	panic("not implemented yet")
+}
+
 // TODO: implement me
 type NetworkAdapter struct {
 	*Client
 	adapterSAP           *NetworkServiceAccessPoint
-	adapterNet           interface{}
+	adapterNet           *uint16
 	adapterAddr          *Address
 	adapterNetConfigured *int
 }
 
-func NewNetworkAdapter(sap *NetworkServiceAccessPoint, net interface{}, addr *Address, cid *int) (*NetworkAdapter, error) {
+func NewNetworkAdapter(sap *NetworkServiceAccessPoint, net *uint16, addr *Address, cid *int) (*NetworkAdapter, error) {
 	n := &NetworkAdapter{
 		adapterSAP:  sap,
 		adapterNet:  net,
@@ -55,28 +127,35 @@ func NewNetworkAdapter(sap *NetworkServiceAccessPoint, net interface{}, addr *Ad
 
 // Confirmation Decode upstream PDUs and pass them up to the service access point.
 func (n *NetworkAdapter) Confirmation(npdu _PDU) error {
-	log.Debug().Msgf("confirmation\n%s\n%s", npdu, n.adapterNet)
+	log.Debug().Msgf("confirmation\n%s\n%d", npdu, n.adapterNet)
 
-	// TODO: we need generics otherwise this won't work at all here
-	return n.adapterSAP.ProcessNPDU(npdu)
+	return n.adapterSAP.ProcessNPDU(n, npdu)
 }
 
 // ProcessNPDU Encode NPDUs from the service access point and send them downstream.
 func (n *NetworkAdapter) ProcessNPDU(npdu _PDU) error {
-	log.Debug().Msgf("ProcessNPDU\n%s\n(net=%s)", npdu, n.adapterNet)
+	log.Debug().Msgf("ProcessNPDU\n%s\n(net=%d)", npdu, n.adapterNet)
 	return n.Request(npdu)
 }
 
+func (n *NetworkAdapter) EstablishConnectionToNetwork(net interface{}) error {
+	panic("not implemented yet")
+}
+
+func (n *NetworkAdapter) DisconnectConnectionToNetwork(net interface{}) error {
+	panic("not implemented yet")
+}
+
 type NetworkServiceAccessPoint struct {
 	*ServiceAccessPoint
 	*Server
-	adapters        map[string]*NetworkAdapter
-	routerInfoCache interface{}
-	pendingNets     map[string]interface{}
+	adapters        map[*uint16]*NetworkAdapter
+	routerInfoCache *RouterInfoCache
+	pendingNets     map[*uint16][]_PDU
 	localAdapter    *NetworkAdapter
 }
 
-func NewNetworkServiceAccessPoint(routerInfoCache interface{}, sapID *int, sid *int) (*NetworkServiceAccessPoint, error) {
+func NewNetworkServiceAccessPoint(routerInfoCache *RouterInfoCache, sapID *int, sid *int) (*NetworkServiceAccessPoint, error) {
 	n := &NetworkServiceAccessPoint{}
 	var err error
 	n.ServiceAccessPoint, err = NewServiceAccessPoint(sapID, n)
@@ -89,16 +168,16 @@ func NewNetworkServiceAccessPoint(routerInfoCache interface{}, sapID *int, sid *
 	}
 
 	// map of directly connected networks
-	n.adapters = make(map[string]*NetworkAdapter)
+	n.adapters = make(map[*uint16]*NetworkAdapter)
 
 	// use the provided cache or make a default one
 	if routerInfoCache == nil {
-		// TODO: create a new cache
+		routerInfoCache = NewRouterInfoCache()
 	}
 	n.routerInfoCache = routerInfoCache
 
 	// map to a list of application layer packets waiting for a path
-	n.pendingNets = make(map[string]interface{})
+	n.pendingNets = make(map[*uint16][]_PDU)
 
 	return n, nil
 }
@@ -120,12 +199,11 @@ func NewNetworkServiceAccessPoint(routerInfoCache interface{}, sapID *int, sid *
        Called for applications or routers, bind to the network, send up
        APDUs with a metching address.
 */
-func (n *NetworkServiceAccessPoint) bind(server _Server, net interface{}, address *Address) error {
+func (n *NetworkServiceAccessPoint) bind(server _Server, net *uint16, address *Address) error {
 	log.Debug().Msgf("bind %v net=%v address=%v", server, net, address)
 
-	netKey := fmt.Sprintf("%v", net)
 	// make sure this hasn't already been called with this network
-	if _, ok := n.adapters[netKey]; ok {
+	if _, ok := n.adapters[net]; ok {
 		return errors.Errorf("Allready bound: %v", net)
 	}
 	// create an adapter object, add it to our map
@@ -133,8 +211,8 @@ func (n *NetworkServiceAccessPoint) bind(server _Server, net interface{}, addres
 	if err != nil {
 		return errors.Wrap(err, "error creating adapter")
 	}
-	n.adapters[netKey] = adapter
-	log.Debug().Msgf("adapter: %v, %v", netKey, adapter)
+	n.adapters[net] = adapter
+	log.Debug().Msgf("adapter: %v, %v", net, adapter)
 
 	// if the address was given, make it the "local" one
 	if address != nil {
@@ -156,28 +234,320 @@ func (n *NetworkServiceAccessPoint) bind(server _Server, net interface{}, addres
 	return bind(adapter, server)
 }
 
-func (n *NetworkServiceAccessPoint) UpdateRouterReference() error {
-	panic("not implemented yet")
+// UpdateRouterReference Update references to routers.
+func (n *NetworkServiceAccessPoint) UpdateRouterReference(snet *uint16, address, dnets interface{}) error {
+	log.Debug().Msgf("UpdateRouterReference %d %s %d", snet, address, dnets)
+
+	// see if we have an adapter for the snet
+	_, ok := n.adapters[snet]
+	if !ok {
+		return errors.Errorf("no adapter for network: %d", snet)
+	}
+
+	// pass this along to the cache
+	return n.routerInfoCache.UpdateRouterInfo(snet, address, dnets)
 }
 
-func (n *NetworkServiceAccessPoint) DeleteRouterReference() error {
-	panic("not implemented yet")
+// DeleteRouterReference Delete references to routers/networks.
+func (n *NetworkServiceAccessPoint) DeleteRouterReference(snet *uint16, address, dnets interface{}) error {
+	log.Debug().Msgf("NetworkServiceAccessPoint %d %s %s", snet, address, dnets)
+
+	// see if we have an adapter for the snet
+	_, ok := n.adapters[snet]
+	if !ok {
+		return errors.Errorf("no adapter for network: %d", snet)
+	}
+
+	//pass this along to the cache
+	return n.routerInfoCache.DeleteRouterInfo(snet, address, dnets)
 }
 
-func (n *NetworkServiceAccessPoint) Indication(npdu _PDU) error {
+func (n *NetworkServiceAccessPoint) Indication(pdu _PDU) error {
+	log.Debug().Msgf("Indication:\n%s", pdu)
+
+	// make sure our configuration is OK
+	if len(n.adapters) == 0 {
+		return errors.New("no adapters")
+	}
+
+	// get the local adapter
+	localAdapter := n.localAdapter
+	log.Debug().Msgf("localAdapter: %s", localAdapter)
+
+	// get the apdu
+	apdu := pdu.GetMessage().(readWriteModel.APDU)
+
+	// build a npdu
+	pduDestination := pdu.GetPDUDestination()
+
+	// the hop count always starts out big
+	hopCount := uint8(0xff)
+
+	// if this is route aware, use it for the destination
+	if settings.RouteAware && pduDestination.AddrRoute != nil {
+		// always a local station for now, in theory this could also be
+		// a local broadcast address, remote station, or remote broadcast
+		// but that is not supported by the patterns
+		if pduDestination.AddrRoute.AddrType != LOCAL_STATION_ADDRESS {
+			panic("Route must be of type local station")
+		}
+
+		var dadr *Address
+		switch pduDestination.AddrType {
+		case REMOTE_STATION_ADDRESS, REMOTE_BROADCAST_ADDRESS, GLOBAL_BROADCAST_ADDRESS:
+			dadr = pduDestination.AddrRoute
+		}
+
+		pdu.SetPDUDestination(pduDestination.AddrRoute)
+		npdu, err := buildNPDU(hopCount, nil, dadr, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+		if err != nil {
+			return errors.Wrap(err, "error building NPDU")
+		}
+		return localAdapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu))
+	}
+
+	// local stations given to local adapter
+	if pduDestination.AddrType == LOCAL_STATION_ADDRESS {
+		npdu, err := buildNPDU(hopCount, nil, nil, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+		if err != nil {
+			return errors.Wrap(err, "error building NPDU")
+		}
+		return localAdapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu))
+	}
+
+	// local broadcast given to local adapter
+	if pduDestination.AddrType == LOCAL_BROADCAST_ADDRESS {
+		npdu, err := buildNPDU(hopCount, nil, nil, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+		if err != nil {
+			return errors.Wrap(err, "error building NPDU")
+		}
+		return localAdapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu))
+	}
+
+	// global broadcast
+	if pduDestination.AddrType == GLOBAL_BROADCAST_ADDRESS {
+		pdu.SetPDUDestination(NewLocalBroadcast(nil))
+		npdu, err := buildNPDU(hopCount, nil, pduDestination, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+		if err != nil {
+			return errors.Wrap(err, "error building NPDU")
+		}
+
+		// send it to all of connected adapters
+		for _, xadapter := range n.adapters {
+			if err := xadapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu)); err != nil {
+				return errors.Wrap(err, "error processing NPDU")
+			}
+		}
+		return nil
+	}
+
+	// remote broadcast
+	switch pduDestination.AddrType {
+	case REMOTE_BROADCAST_ADDRESS, REMOTE_STATION_ADDRESS:
+	default:
+		return errors.Errorf("invalid destination address type: %s", pduDestination.AddrType)
+	}
+
+	dnet := pduDestination.AddrNet
+	log.Debug().Msgf("dnet %d", dnet)
+
+	// if the network matches the local adapter it's local
+	if dnet == localAdapter.adapterNet {
+		switch pduDestination.AddrType {
+		case REMOTE_STATION_ADDRESS:
+			log.Debug().Msg("mapping remote station to local station")
+			localStation, err := NewLocalStation(pduDestination.AddrAddress, nil)
+			if err != nil {
+				return errors.Wrap(err, "error building local station")
+			}
+			pdu.SetPDUDestination(localStation)
+		case REMOTE_BROADCAST_ADDRESS:
+			pdu.SetPDUDestination(NewLocalBroadcast(nil))
+		default:
+			return errors.New("Addressing problem")
+		}
+		npdu, err := buildNPDU(hopCount, nil, pduDestination, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+		if err != nil {
+			return errors.Wrap(err, "error building NPDU")
+		}
+
+		return localAdapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu))
+	}
+
+	// get it ready to send when the path is found
+	pdu.SetPDUDestination(nil)
+
+	npdu, err := buildNPDU(hopCount, nil, pduDestination, pdu.GetExpectingReply(), pdu.GetNetworkPriority(), apdu)
+	if err != nil {
+		return errors.Wrap(err, "error building NPDU")
+	}
+
+	// we might already be waiting for a path for this network
+	if pendingNet, ok := n.pendingNets[dnet]; ok {
+		log.Debug().Msg("already waiting for a path")
+		var pdu _PDU = NewPDUFromPDUWithNewMessage(pdu, npdu)
+		n.pendingNets[dnet] = append(pendingNet, pdu)
+		return nil
+	}
+
+	// look for routing information from the network of one of our adapters to the destination network
+	var routerInfo *RouterInfo
+	var snetAdapter *NetworkAdapter
+	for snet, adapter := range n.adapters {
+		routerInfo = n.routerInfoCache.GetRouterInfo(snet, dnet)
+		if routerInfo != nil {
+			snetAdapter = adapter
+			break
+		}
+	}
+
+	// if there is info, we have a path
+	if routerInfo != nil {
+		log.Debug().Msgf("routerInfo found %s", routerInfo)
+
+		// check the path status
+		dnetStatus := routerInfo.dnets[dnet]
+		log.Debug().Msgf("dnetStatus %s", dnetStatus)
+
+		// fix the destination
+		pdu.SetPDUDestination(&routerInfo.address)
+
+		// send it along
+		return snetAdapter.ProcessNPDU(NewPDUFromPDUWithNewMessage(pdu, npdu))
+	} else {
+		log.Debug().Msg("no known path to network")
+
+		// add it to the list of packets waiting for the network
+		netList := append(n.pendingNets[dnet], NewPDUFromPDUWithNewMessage(pdu, npdu))
+		n.pendingNets[dnet] = netList
+
+		// build a request for the network and send it to all the adapters
+		whoIsRouterToNetwork := readWriteModel.NewNLMWhoIsRouterToNetwork(dnet, 0)
+
+		// send it to all the adapters
+		for _, adapter := range n.adapters {
+			if err := n.SapIndicationWithAdapter(adapter, NewPDU(whoIsRouterToNetwork, WithPDUDestination(NewLocalBroadcast(nil)))); err != nil {
+				return errors.Wrap(err, "error doing SapIndication")
+			}
+		}
+	}
+
 	panic("not implemented yet")
 }
 
-func (n *NetworkServiceAccessPoint) ProcessNPDU(npdu _PDU) error {
-	panic("not implemented yet")
+func buildNPDU(hopCount uint8, source *Address, destination *Address, expectingReply bool, networkPriority readWriteModel.NPDUNetworkPriority, apdu readWriteModel.APDU) (readWriteModel.NPDU, error) {
+	sourceSpecified := source != nil
+	var sourceNetworkAddress *uint16
+	var sourceLength *uint8
+	var sourceAddress []uint8
+	if sourceSpecified {
+		sourceSpecified = true
+		sourceNetworkAddress = source.AddrNet
+		sourceLengthValue := *source.AddrLen
+		if sourceLengthValue > math.MaxUint8 {
+			return nil, errors.New("source address length overflows")
+		}
+		sourceLengthValueUint8 := uint8(sourceLengthValue)
+		sourceLength = &sourceLengthValueUint8
+		sourceAddress = source.AddrAddress
+		if sourceLengthValueUint8 == 0 {
+			// If we define the len 0 we must not send the array
+			sourceAddress = nil
+		}
+	}
+	destinationSpecified := destination != nil
+	var destinationNetworkAddress *uint16
+	var destinationLength *uint8
+	var destinationAddress []uint8
+	if destinationSpecified {
+		destinationSpecified = true
+		destinationNetworkAddress = destination.AddrNet
+		destinationLengthValue := *destination.AddrLen
+		if destinationLengthValue > math.MaxUint8 {
+			return nil, errors.New("source address length overflows")
+		}
+		destinationLengthValueUint8 := uint8(destinationLengthValue)
+		destinationLength = &destinationLengthValueUint8
+		destinationAddress = destination.AddrAddress
+		if destinationLengthValueUint8 == 0 {
+			// If we define the len 0 we must not send the array
+			destinationAddress = nil
+		}
+	}
+	control := readWriteModel.NewNPDUControl(false, destinationSpecified, sourceSpecified, expectingReply, networkPriority)
+	return readWriteModel.NewNPDU(1, control, destinationNetworkAddress, destinationLength, destinationAddress, sourceNetworkAddress, sourceLength, sourceAddress, &hopCount, nil, apdu, 0), nil
+}
+
+func (n *NetworkServiceAccessPoint) ProcessNPDU(adapter *NetworkAdapter, pdu _PDU) error {
+	log.Debug().Msgf("ProcessNPDU %s, %s", adapter, pdu)
+
+	// make sure our configuration is OK
+	if len(n.adapters) == 0 {
+		return errors.New("no adapters")
+	}
+
+	npdu := pdu.GetMessage().(readWriteModel.NPDU)
+
+	var snet *uint16
+	// check for source routing
+	if npdu.GetControl().GetSourceSpecified() {
+		log.Debug().Msg("check source path")
+
+		// see if this is attempting to spoof a directly connected network
+		snet = npdu.GetSourceNetworkAddress()
+		if _, ok := n.adapters[snet]; !ok {
+			log.Warn().Msg("path error (1)")
+			return nil
+		}
+
+		// pass this new path along to the cache
+		n.routerInfoCache.UpdateRouterStatus(adapter.adapterNet, pdu.GetPDUSource(), []*uint16{snet})
+	}
+
+	var (
+		processLocally bool
+		forwardMessage bool
+	)
+	// check for destination routing
+	if npdu.GetControl().GetDestinationSpecified() {
+		log.Debug().Msg("no DADR")
+
+		processLocally = adapter == n.localAdapter || npdu.GetControl().GetMessageTypeFieldPresent()
+		forwardMessage = false
+	}
+	// TODO: we need the type from the DADR which we don't have in our readwrite.NPDU so we might need a special _NPDU
+
+	panic("implement me")
+	_ = processLocally
+	_ = forwardMessage
+	return nil
 }
 
 func (n *NetworkServiceAccessPoint) SapIndication(npdu _PDU) error {
-	panic("not implemented yet")
+	panic("unused")
+}
+
+func (n *NetworkServiceAccessPoint) SapIndicationWithAdapter(adapter *NetworkAdapter, npdu _PDU) error {
+	log.Debug().Msgf("SapIndication %s %s", adapter, npdu)
+
+	// encode it as a generic NPDU
+	// TODO: we don't need that as a npdu is a npdu
+
+	// tell the adapter to process the NPDU
+	return adapter.ProcessNPDU(npdu)
 }
 
 func (n *NetworkServiceAccessPoint) SapConfirmation(npdu _PDU) error {
-	panic("not implemented yet")
+	panic("unused")
+}
+
+func (n *NetworkServiceAccessPoint) SapConfirmationWithAdapter(adapter *NetworkAdapter, npdu _PDU) error {
+	log.Debug().Msgf("SapConfirmationWithAdapter %s %s", adapter, npdu)
+
+	// encode it as a generic NPDU
+	// TODO: we don't need that as a npdu is a npdu
+
+	return adapter.ProcessNPDU(npdu)
 }
 
 type NetworkServiceElement struct {
diff --git a/plc4go/internal/bacnetip/PDU.go b/plc4go/internal/bacnetip/PDU.go
index 1ea8bed052..a468c9caf4 100644
--- a/plc4go/internal/bacnetip/PDU.go
+++ b/plc4go/internal/bacnetip/PDU.go
@@ -96,7 +96,7 @@ type Address struct {
 	AddrNet     *uint16
 	AddrAddress []byte
 	AddrLen     *uint32
-	AddrRoute   *uint32
+	AddrRoute   *Address
 
 	AddrIP             *uint32
 	AddrMask           *uint32
@@ -333,12 +333,8 @@ func uint32ToIpv4(number uint32) net.IP {
 	return ipv4
 }
 
-type LocalStation struct {
-	Address
-}
-
-func NewLocalStation(addr interface{}, route *uint32) (*LocalStation, error) {
-	l := &LocalStation{}
+func NewLocalStation(addr interface{}, route *Address) (*Address, error) {
+	l := &Address{}
 	l.AddrType = LOCAL_STATION_ADDRESS
 	l.AddrRoute = route
 
@@ -361,12 +357,8 @@ func NewLocalStation(addr interface{}, route *uint32) (*LocalStation, error) {
 	return l, nil
 }
 
-type RemoteStation struct {
-	Address
-}
-
-func NewRemoteStation(net *uint16, addr interface{}, route *uint32) (*RemoteStation, error) {
-	l := &RemoteStation{}
+func NewRemoteStation(net *uint16, addr interface{}, route *Address) (*Address, error) {
+	l := &Address{}
 	l.AddrType = REMOTE_STATION_ADDRESS
 	l.AddrNet = net
 	l.AddrRoute = route
@@ -390,38 +382,26 @@ func NewRemoteStation(net *uint16, addr interface{}, route *uint32) (*RemoteStat
 	return l, nil
 }
 
-type LocalBroadcast struct {
-	Address
-}
-
-func NewLocalBroadcast(route *uint32) (*LocalBroadcast, error) {
-	l := &LocalBroadcast{}
+func NewLocalBroadcast(route *Address) *Address {
+	l := &Address{}
 	l.AddrType = LOCAL_BROADCAST_ADDRESS
 	l.AddrRoute = route
-	return l, nil
-}
-
-type RemoteBroadcast struct {
-	Address
+	return l
 }
 
-func NewRemoteBroadcast(net *uint16, route *uint32) (*RemoteBroadcast, error) {
-	r := &RemoteBroadcast{}
+func NewRemoteBroadcast(net *uint16, route *Address) *Address {
+	r := &Address{}
 	r.AddrType = REMOTE_BROADCAST_ADDRESS
 	r.AddrNet = net
 	r.AddrRoute = route
-	return r, nil
+	return r
 }
 
-type GlobalBroadcast struct {
-	Address
-}
-
-func NewGlobalBroadcast(route *uint32) (*GlobalBroadcast, error) {
-	g := &GlobalBroadcast{}
+func NewGlobalBroadcast(route *Address) *Address {
+	g := &Address{}
 	g.AddrType = GLOBAL_BROADCAST_ADDRESS
 	g.AddrRoute = route
-	return g, nil
+	return g
 }
 
 type PCI struct {
@@ -434,7 +414,7 @@ func (p *PCI) String() string {
 	return fmt.Sprintf("PCI{%s, expectingReply: %t, networkPriority: %s}", p._PCI, p.expectingReply, p.networkPriority)
 }
 
-func NewPCI(msg spi.Message, pduSource Address, pduDestination Address, expectingReply bool, networkPriority readWriteModel.NPDUNetworkPriority) *PCI {
+func NewPCI(msg spi.Message, pduSource *Address, pduDestination *Address, expectingReply bool, networkPriority readWriteModel.NPDUNetworkPriority) *PCI {
 	return &PCI{
 		_New_PCI(msg, pduSource, pduDestination),
 		expectingReply,
@@ -445,8 +425,9 @@ func NewPCI(msg spi.Message, pduSource Address, pduDestination Address, expectin
 type _PDU interface {
 	spi.Message
 	GetMessage() spi.Message
-	GetPDUSource() Address
-	GetPDUDestination() Address
+	GetPDUSource() *Address
+	GetPDUDestination() *Address
+	SetPDUDestination(*Address)
 	GetExpectingReply() bool
 	GetNetworkPriority() readWriteModel.NPDUNetworkPriority
 }
@@ -457,10 +438,9 @@ type PDU struct {
 }
 
 func NewPDU(msg spi.Message, pduOptions ...PDUOption) *PDU {
-	nullAddress, _ := NewAddress()
 	p := &PDU{
 		msg,
-		NewPCI(msg, *nullAddress, *nullAddress, false, readWriteModel.NPDUNetworkPriority_NORMAL_MESSAGE),
+		NewPCI(msg, nil, nil, false, readWriteModel.NPDUNetworkPriority_NORMAL_MESSAGE),
 	}
 	for _, option := range pduOptions {
 		option(p)
@@ -480,7 +460,18 @@ func NewPDUFromPDU(pdu _PDU, pduOptions ...PDUOption) *PDU {
 	return p
 }
 
-func NewPDUWithAllOptions(msg spi.Message, pduSource Address, pduDestination Address, expectingReply bool, networkPriority readWriteModel.NPDUNetworkPriority) *PDU {
+func NewPDUFromPDUWithNewMessage(pdu _PDU, msg spi.Message, pduOptions ...PDUOption) *PDU {
+	p := &PDU{
+		msg,
+		NewPCI(msg, pdu.GetPDUSource(), pdu.GetPDUDestination(), pdu.GetExpectingReply(), pdu.GetNetworkPriority()),
+	}
+	for _, option := range pduOptions {
+		option(p)
+	}
+	return p
+}
+
+func NewPDUWithAllOptions(msg spi.Message, pduSource *Address, pduDestination *Address, expectingReply bool, networkPriority readWriteModel.NPDUNetworkPriority) *PDU {
 	return &PDU{
 		msg,
 		NewPCI(msg, pduSource, pduDestination, expectingReply, networkPriority),
@@ -489,13 +480,13 @@ func NewPDUWithAllOptions(msg spi.Message, pduSource Address, pduDestination Add
 
 type PDUOption func(pdu *PDU)
 
-func WithPDUSource(pduSource Address) PDUOption {
+func WithPDUSource(pduSource *Address) PDUOption {
 	return func(pdu *PDU) {
 		pdu.pduSource = pduSource
 	}
 }
 
-func WithPDUDestination(pduDestination Address) PDUOption {
+func WithPDUDestination(pduDestination *Address) PDUOption {
 	return func(pdu *PDU) {
 		pdu.pduDestination = pduDestination
 	}
@@ -517,14 +508,18 @@ func (p *PDU) GetMessage() spi.Message {
 	return p.Message
 }
 
-func (p *PDU) GetPDUSource() Address {
+func (p *PDU) GetPDUSource() *Address {
 	return p.pduSource
 }
 
-func (p *PDU) GetPDUDestination() Address {
+func (p *PDU) GetPDUDestination() *Address {
 	return p.pduDestination
 }
 
+func (p *PDU) SetPDUDestination(destination *Address) {
+	p.pduDestination = destination
+}
+
 func (p *PDU) GetExpectingReply() bool {
 	return p.expectingReply
 }
diff --git a/plc4go/internal/bacnetip/Settings.go b/plc4go/internal/bacnetip/Settings.go
new file mode 100644
index 0000000000..d27cc72c71
--- /dev/null
+++ b/plc4go/internal/bacnetip/Settings.go
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ *
+ *   https://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 bacnetip
+
+type Settings struct {
+	Debug       bool
+	Color       bool
+	DebugFile   string
+	MaxBytes    uint
+	BackupCount uint
+	RouteAware  bool
+}
+
+var settings = Settings{
+	Debug:       false,
+	Color:       false,
+	DebugFile:   "",
+	MaxBytes:    1048576,
+	BackupCount: 5,
+	RouteAware:  false,
+}