func (ctx *Context) NewDynamicTunnel(name string, cfg *TunnelConfig) (tunl Tunnel, err error) {
	var sal, sap unix.Sockaddr
	if cfg == nil {
		return nil, fmt.Errorf("invalid nil config")
	}
	myCfg := *cfg
	if _, ok := ctx.findTunnelByName(name); ok {
		return nil, fmt.Errorf("already have tunnel %q", name)
	}
	if myCfg.HostName == "" {
		name, err := os.Hostname()
		if err != nil {
			return nil, fmt.Errorf("failed to look up host name: %v", err)
		}
		myCfg.HostName = name
	}
	if myCfg.StopCCNTimeout == 0 {
		myCfg.StopCCNTimeout = 31 * time.Second
	}
	if myCfg.Version != ProtocolVersion3 && myCfg.Encap == EncapTypeIP {
		return nil, fmt.Errorf("IP encapsulation only supported for L2TPv3 tunnels")
	}
	if myCfg.Version == ProtocolVersion2 {
		if myCfg.TunnelID > 65535 {
			return nil, fmt.Errorf("L2TPv2 connection ID %v out of range", myCfg.TunnelID)
		}
	}
	if myCfg.PeerTunnelID != 0 {
		return nil, fmt.Errorf("L2TPv2 peer connection ID cannot be specified for dynamic tunnels")
	}
	if myCfg.Peer == "" {
		return nil, fmt.Errorf("must specify peer address for dynamic tunnel")
	}
	if myCfg.TunnelID != 0 {
		if _, ok := ctx.findTunnelByID(myCfg.TunnelID); ok {
			return nil, fmt.Errorf("already have tunnel with TID %q", myCfg.TunnelID)
		}
	} else {
		myCfg.TunnelID, err = ctx.allocTid(myCfg.Version)
		if err != nil {
			return nil, fmt.Errorf("failed to allocate a TID: %q", err)
		}
	}
	switch myCfg.Encap {
	case EncapTypeUDP:
		sal, sap, err = newUDPAddressPair(myCfg.Local, myCfg.Peer)
	case EncapTypeIP:
		sal, sap, err = newIPAddressPair(myCfg.Local, myCfg.TunnelID,
			myCfg.Peer, myCfg.PeerTunnelID)
	default:
		err = fmt.Errorf("unrecognised encapsulation type %v", myCfg.Encap)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to initialise tunnel addresses: %v", err)
	}
	t, err := newDynamicTunnel(name, ctx, sal, sap, &myCfg)
	if err != nil {
		return nil, err
	}
	ctx.linkTunnel(t)
	tunl = t
	return
}
NewDynamicTunnel creates a new dynamic L2TP.

A dynamic L2TP tunnel runs a full RFC2661 (L2TPv2) or
RFC3931 (L2TPv3) tunnel instance using the control protocol
for tunnel instantiation and management.

The name provided must be unique in the Context.
package l2tp
import (
	"fmt"
	"math/rand"
	"net"
	"os"
	"sync"
	"time"
	"github.com/go-kit/kit/log"
	"golang.org/x/sys/unix"
)
type Context struct {
	logger        log.Logger
	tunnelsByName map[string]tunnel
	tunnelsByID   map[ControlConnID]tunnel
	tlock         sync.RWMutex
	dp            DataPlane
	callSerial    uint32
	serialLock    sync.Mutex
	eventHandlers []EventHandler
	evtLock       sync.RWMutex
}
type Tunnel interface {
	NewSession(name string, cfg *SessionConfig) (Session, error)
	Close()
}
type tunnel interface {
	Tunnel
	getName() string
	getCfg() *TunnelConfig
	getDP() DataPlane
	getLogger() log.Logger
	unlinkSession(s session)
	handleUserEvent(event interface{})
}
type Session interface {
	Close()
}
type session interface {
	Session
	getName() string
	getCfg() *SessionConfig
	kill()
}
type DataPlane interface {
	NewTunnel(
		tcfg *TunnelConfig,
		localAddress, peerAddress unix.Sockaddr,
		fd int) (TunnelDataPlane, error)
	NewSession(tunnelID, peerTunnelID ControlConnID, scfg *SessionConfig) (SessionDataPlane, error)
	Close()
}
type TunnelDataPlane interface {
	Down() error
}
type SessionDataPlaneStatistics struct {
	TxPackets, TxBytes, TxErrors, RxPackets, RxBytes, RxErrors uint64
}
type SessionDataPlane interface {
	GetStatistics() (*SessionDataPlaneStatistics, error)
	GetInterfaceName() (string, error)
	Down() error
}
type EventHandler interface {
	HandleEvent(event interface{})
}
type TunnelUpEvent struct {
	TunnelName                string
	Tunnel                    Tunnel
	Config                    *TunnelConfig
	LocalAddress, PeerAddress unix.Sockaddr
}
type TunnelDownEvent struct {
	TunnelName                string
	Tunnel                    Tunnel
	Config                    *TunnelConfig
	LocalAddress, PeerAddress unix.Sockaddr
}
type SessionUpEvent struct {
	TunnelName    string
	Tunnel        Tunnel
	TunnelConfig  *TunnelConfig
	SessionName   string
	Session       Session
	SessionConfig *SessionConfig
	InterfaceName string
}
type SessionDownEvent struct {
	TunnelName    string
	Tunnel        Tunnel
	TunnelConfig  *TunnelConfig
	SessionName   string
	Session       Session
	SessionConfig *SessionConfig
	InterfaceName string
	Result        string
}
var LinuxNetlinkDataPlane DataPlane = &nullDataPlane{}
func NewContext(dataPlane DataPlane, logger log.Logger) (*Context, error) {
	if logger == nil {
		logger = log.NewNopLogger()
	}
	rand.Seed(time.Now().UnixNano())
	dp, err := initDataPlane(dataPlane)
	if err != nil {
		return nil, fmt.Errorf("failed to initialise data plane: %v", err)
	}
	return &Context{
		logger:        logger,
		tunnelsByName: make(map[string]tunnel),
		tunnelsByID:   make(map[ControlConnID]tunnel),
		dp:            dp,
		callSerial:    rand.Uint32(),
	}, nil
}