func (bt *baseTunnel) closeAllSessions() {
	sessions := []session{}
	bt.sessionLock.Lock()
	for name, s := range bt.sessionsByName {
		sessions = append(sessions, s)
		delete(bt.sessionsByName, name)
		delete(bt.sessionsByID, s.getCfg().SessionID)
	}
	bt.sessionLock.Unlock()
	for _, s := range sessions {
		s.kill()
	}
}
Close all sessions in a tunnel without kicking their FSM instances.
When a tunnel goes down, StopCCN is sufficient to implicitly terminate
all session instances running in that tunnel.
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
}
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
}
func (ctx *Context) NewQuiescentTunnel(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.Version != ProtocolVersion3 && myCfg.Encap == EncapTypeIP {
		return nil, fmt.Errorf("IP encapsulation only supported for L2TPv3 tunnels")
	}
	if myCfg.Version == ProtocolVersion2 {
		if myCfg.TunnelID == 0 || myCfg.TunnelID > 65535 {
			return nil, fmt.Errorf("L2TPv2 connection ID %v out of range", myCfg.TunnelID)
		} else if myCfg.PeerTunnelID == 0 || myCfg.PeerTunnelID > 65535 {
			return nil, fmt.Errorf("L2TPv2 peer connection ID %v out of range", myCfg.PeerTunnelID)
		}
	} else {
		if myCfg.TunnelID == 0 || myCfg.PeerTunnelID == 0 {
			return nil, fmt.Errorf("L2TPv3 tunnel IDs %v and %v must both be > 0",
				myCfg.TunnelID, myCfg.PeerTunnelID)
		}
	}
	if myCfg.Local == "" {
		return nil, fmt.Errorf("must specify local address for quiescent tunnel")
	}
	if myCfg.Peer == "" {
		return nil, fmt.Errorf("must specify peer address for quiescent tunnel")
	}
	if _, ok := ctx.findTunnelByID(myCfg.TunnelID); ok {
		return nil, fmt.Errorf("already have tunnel with TID %q", myCfg.TunnelID)
	}
	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 := newQuiescentTunnel(name, ctx, sal, sap, &myCfg)
	if err != nil {
		return nil, err
	}
	ctx.linkTunnel(t)
	tunl = t
	return
}
func (ctx *Context) NewStaticTunnel(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.Version != ProtocolVersion3 {
		return nil, fmt.Errorf("static tunnels can be L2TPv3 only")
	}
	if myCfg.TunnelID == 0 || myCfg.PeerTunnelID == 0 {
		return nil, fmt.Errorf("L2TPv3 tunnel IDs %v and %v must both be > 0",
			myCfg.TunnelID, myCfg.PeerTunnelID)
	}
	if myCfg.Local == "" {
		return nil, fmt.Errorf("must specify local address for static tunnel")
	}
	if myCfg.Peer == "" {
		return nil, fmt.Errorf("must specify peer address for static tunnel")
	}
	if _, ok := ctx.findTunnelByID(myCfg.TunnelID); ok {
		return nil, fmt.Errorf("already have tunnel with TID %q", myCfg.TunnelID)
	}
	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 := newStaticTunnel(name, ctx, sal, sap, &myCfg)
	if err != nil {
		return nil, err
	}
	ctx.linkTunnel(t)
	tunl = t
	return
}
func (ctx *Context) RegisterEventHandler(handler EventHandler) {
	ctx.evtLock.Lock()
	defer ctx.evtLock.Unlock()
	ctx.eventHandlers = append(ctx.eventHandlers, handler)
}
func (ctx *Context) UnregisterEventHandler(handler EventHandler) {
	ctx.evtLock.Lock()
	defer ctx.evtLock.Unlock()
	for i, hdlr := range ctx.eventHandlers {
		if hdlr == handler {
			ctx.eventHandlers = append(ctx.eventHandlers[:], ctx.eventHandlers[i+1:]...)
			break
		}
	}
}
func (ctx *Context) handleUserEvent(event interface{}) {
	ctx.evtLock.RLock()
	defer ctx.evtLock.RUnlock()
	for _, hdlr := range ctx.eventHandlers {
		hdlr.HandleEvent(event)
	}
}
func (ctx *Context) Close() {
	tunnels := []Tunnel{}
	ctx.tlock.Lock()
	for name, tunl := range ctx.tunnelsByName {
		tunnels = append(tunnels, tunl)
		delete(ctx.tunnelsByName, name)
		delete(ctx.tunnelsByID, tunl.getCfg().TunnelID)
	}
	ctx.tlock.Unlock()
	for _, tunl := range tunnels {
		tunl.Close()
	}
	ctx.dp.Close()
}
func (ctx *Context) allocTid(version ProtocolVersion) (ControlConnID, error) {
	for i := 0; i < 10; i++ {
		id, err := generateControlConnID(version)
		if err != nil {
			return 0, fmt.Errorf("failed to generate tunnel ID: %v", err)
		}
		if _, ok := ctx.findTunnelByID(id); !ok {
			return id, nil
		}
	}
	return 0, fmt.Errorf("ID space exhausted")
}
func (ctx *Context) linkTunnel(tunl tunnel) {
	ctx.tlock.Lock()
	defer ctx.tlock.Unlock()
	ctx.tunnelsByName[tunl.getName()] = tunl
	ctx.tunnelsByID[tunl.getCfg().TunnelID] = tunl
}
func (ctx *Context) unlinkTunnel(tunl tunnel) {
	ctx.tlock.Lock()
	defer ctx.tlock.Unlock()
	delete(ctx.tunnelsByName, tunl.getName())
	delete(ctx.tunnelsByID, tunl.getCfg().TunnelID)
}
func (ctx *Context) findTunnelByName(name string) (tunl tunnel, ok bool) {
	ctx.tlock.RLock()
	defer ctx.tlock.RUnlock()
	tunl, ok = ctx.tunnelsByName[name]
	return
}
func (ctx *Context) findTunnelByID(tid ControlConnID) (tunl tunnel, ok bool) {
	ctx.tlock.RLock()
	defer ctx.tlock.RUnlock()
	tunl, ok = ctx.tunnelsByID[tid]
	return
}
func (ctx *Context) allocCallSerial() uint32 {
	ctx.serialLock.Lock()
	defer ctx.serialLock.Unlock()
	ctx.callSerial++
	return ctx.callSerial
}
func newUDPTunnelAddress(address string) (unix.Sockaddr, error) {
	u, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, fmt.Errorf("resolve %v: %v", address, err)
	}
	if b := u.IP.To4(); b != nil {
		return &unix.SockaddrInet4{
			Port: u.Port,
			Addr: [4]byte{b[0], b[1], b[2], b[3]},
		}, nil
	} else if b := u.IP.To16(); b != nil {
		return &unix.SockaddrInet6{
			Port: u.Port,
			Addr: [16]byte{
				b[0], b[1], b[2], b[3],
				b[4], b[5], b[6], b[7],
				b[8], b[9], b[10], b[11],
				b[12], b[13], b[14], b[15],
			},
		}, nil
	}
	return nil, fmt.Errorf("unhandled address family")
}
func newUDPAddressPair(local, remote string) (sal, sap unix.Sockaddr, err error) {
	sap, err = newUDPTunnelAddress(remote)
	if err != nil {
		return nil, nil, fmt.Errorf("remote address %q: %v", remote, err)
	}
	if local != "" {
		sal, err = newUDPTunnelAddress(local)
		if err != nil {
			return nil, nil, fmt.Errorf("local address %q: %v", local, err)
		}
	} else {
		switch sap.(type) {
		case *unix.SockaddrInet4:
			sal = &unix.SockaddrInet4{}
		case *unix.SockaddrInet6:
			sal = &unix.SockaddrInet6{}
		default:
			return nil, nil, fmt.Errorf("unhanded address family")
		}
	}
	return
}
func newIPTunnelAddress(address string, ccid ControlConnID) (unix.Sockaddr, error) {
	u, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, fmt.Errorf("resolve %v: %v", address, err)
	}
	if b := u.IP.To4(); b != nil {
		return &unix.SockaddrL2TPIP{
			Addr:   [4]byte{b[0], b[1], b[2], b[3]},
			ConnId: uint32(ccid),
		}, nil
	} else if b := u.IP.To16(); b != nil {
		return &unix.SockaddrL2TPIP6{
			Addr: [16]byte{
				b[0], b[1], b[2], b[3],
				b[4], b[5], b[6], b[7],
				b[8], b[9], b[10], b[11],
				b[12], b[13], b[14], b[15],
			},
			ConnId: uint32(ccid),
		}, nil
	}
	return nil, fmt.Errorf("unhandled address family")
}
func newIPAddressPair(local string, ccid ControlConnID, remote string, pccid ControlConnID) (sal, sap unix.Sockaddr, err error) {
	sap, err = newIPTunnelAddress(remote, pccid)
	if err != nil {
		return nil, nil, fmt.Errorf("remote address %q: %v", remote, err)
	}
	if local != "" {
		sal, err = newIPTunnelAddress(local, ccid)
		if err != nil {
			return nil, nil, fmt.Errorf("local address %q: %v", local, err)
		}
	} else {
		switch sap.(type) {
		case *unix.SockaddrL2TPIP:
			sal = &unix.SockaddrL2TPIP{}
		case *unix.SockaddrL2TPIP6:
			sal = &unix.SockaddrL2TPIP6{}
		default:
			return nil, nil, fmt.Errorf("unhanded address family")
		}
	}
	return
}
func initDataPlane(dp DataPlane) (DataPlane, error) {
	if dp == nil {
		return &nullDataPlane{}, nil
	} else if dp == LinuxNetlinkDataPlane {
		return newNetlinkDataPlane()
	}
	return dp, nil
}
func generateControlConnID(version ProtocolVersion) (ControlConnID, error) {
	var id ControlConnID
	switch version {
	case ProtocolVersion2:
		id = ControlConnID(uint16(rand.Uint32()))
	case ProtocolVersion3:
		id = ControlConnID(rand.Uint32())
	default:
		return 0, fmt.Errorf("unhandled version %v", version)
	}
	return id, nil
}
type baseTunnel struct {
	logger         log.Logger
	name           string
	parent         *Context
	cfg            *TunnelConfig
	sessionLock    sync.RWMutex
	sessionsByName map[string]session
	sessionsByID   map[ControlConnID]session
}
func newBaseTunnel(logger log.Logger, name string, parent *Context, config *TunnelConfig) *baseTunnel {
	return &baseTunnel{
		logger:         logger,
		name:           name,
		parent:         parent,
		cfg:            config,
		sessionsByName: make(map[string]session),
		sessionsByID:   make(map[ControlConnID]session),
	}
}
func (bt *baseTunnel) getName() string {
	return bt.name
}
func (bt *baseTunnel) getCfg() *TunnelConfig {
	return bt.cfg
}
func (bt *baseTunnel) getDP() DataPlane {
	return bt.parent.dp
}
func (bt *baseTunnel) getLogger() log.Logger {
	return bt.logger
}
func (bt *baseTunnel) linkSession(s session) {
	bt.sessionLock.Lock()
	defer bt.sessionLock.Unlock()
	bt.sessionsByName[s.getName()] = s
	bt.sessionsByID[s.getCfg().SessionID] = s
}
func (bt *baseTunnel) unlinkSession(s session) {
	bt.sessionLock.Lock()
	defer bt.sessionLock.Unlock()
	delete(bt.sessionsByName, s.getName())
	delete(bt.sessionsByID, s.getCfg().SessionID)
}
func (bt *baseTunnel) handleUserEvent(event interface{}) {
	bt.parent.handleUserEvent(event)
}
func (bt *baseTunnel) findSessionByName(name string) (s session, ok bool) {
	bt.sessionLock.RLock()
	defer bt.sessionLock.RUnlock()
	s, ok = bt.sessionsByName[name]
	return
}
func (bt *baseTunnel) findSessionByID(id ControlConnID) (s session, ok bool) {
	bt.sessionLock.RLock()
	defer bt.sessionLock.RUnlock()
	s, ok = bt.sessionsByID[id]
	return
}
func (bt *baseTunnel) allSessions() (sessions []session) {
	bt.sessionLock.RLock()
	defer bt.sessionLock.RUnlock()
	for _, s := range bt.sessionsByName {
		sessions = append(sessions, s)
	}
	return
}