<commit_before>package neptulon

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/neptulon/cmap"
)

// Conn is a full-duplex bidirectional client-server connection.
type Conn struct {
	ID                 string     // Randomly generated unique connection ID
	Data               *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session
	conn               *tls.Conn
	headerSize         int
	maxMsgSize         int
	readDeadline       time.Duration
	debug              bool
	err                error
	clientDisconnected bool // hack: Whether the client disconnected from server before server closed connection
}

// NewConn creates a new server-side connection object.
// Default values for headerSize, maxMsgSize, and readDeadline are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
// Debug mode logs all raw TCP communication.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readDeadline int, debug bool) (*Conn, error) {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readDeadline == 0 {
		readDeadline = 300
	}

	id, err := GenID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:           id,
		Data:         cmap.New(),
		conn:         conn,
		headerSize:   headerSize,
		maxMsgSize:   maxMsgSize,
		readDeadline: time.Second * time.Duration(readDeadline),
		debug:        debug,
	}, nil
}

// Dial creates a new client side connection to a server at the given network address with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Conn, error) {
	var cas *x509.CertPool
	var certs []tls.Certificate
	if ca != nil {
		cas = x509.NewCertPool()
		ok := cas.AppendCertsFromPEM(ca)
		if !ok {
			return nil, errors.New("failed to parse the CA certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		c, _ := pem.Decode(clientCert)
		if tlsCert.Leaf, err = x509.ParseCertificate(c.Bytes); err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		certs = []tls.Certificate{tlsCert}
	}

	// todo: dial timeout like that of net.Conn.DialTimeout
	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: cas, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0, debug)
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Conn) SetReadDeadline(seconds int) {
	c.readDeadline = time.Second * time.Duration(seconds)
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	var n int
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total < n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			err = fmt.Errorf("errored while reading incoming message: %v", err)
			break
		}
		total += i
	}
	if total != n {
		err = fmt.Errorf("expected to read %v bytes instead read %v bytes", n, total)
	}

	if c.debug {
		log.Println("Incoming message:", string(msg))
	}

	return
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) error {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err := c.conn.Write(h)
	if err != nil {
		return err
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	// todo: do we need a loop? bufio uses a loop but it might be due to buff length limitation
	n, err = c.conn.Write(msg)
	if err != nil {
		return err
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
// Note: TCP/IP stack does not guarantee delivery of messages before the connection is closed.
func (c *Conn) Close() error {
	return c.conn.Close() // todo: if conn.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<commit_msg>doc indent<commit_after>package neptulon

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/neptulon/cmap"
)

// Conn is a full-duplex bidirectional client-server connection.
type Conn struct {
	ID                 string     // Randomly generated unique connection ID
	Data               *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session
	conn               *tls.Conn
	headerSize         int
	maxMsgSize         int
	readDeadline       time.Duration
	debug              bool
	err                error
	clientDisconnected bool // hack: Whether the client disconnected from server before server closed connection
}

// NewConn creates a new server-side connection object.
// Default values for headerSize, maxMsgSize, and readDeadline are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
// Debug mode logs all raw TCP communication.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readDeadline int, debug bool) (*Conn, error) {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readDeadline == 0 {
		readDeadline = 300
	}

	id, err := GenID()
	if err != nil {
		return nil, err
	}

	return &Conn{
		ID:           id,
		Data:         cmap.New(),
		conn:         conn,
		headerSize:   headerSize,
		maxMsgSize:   maxMsgSize,
		readDeadline: time.Second * time.Duration(readDeadline),
		debug:        debug,
	}, nil
}

// Dial creates a new client side connection to a server at the given network address,
// with optional CA and/or a client certificate (PEM encoded X.509 cert/key).
// Debug mode logs all raw TCP communication.
func Dial(addr string, ca []byte, clientCert []byte, clientCertKey []byte, debug bool) (*Conn, error) {
	var cas *x509.CertPool
	var certs []tls.Certificate
	if ca != nil {
		cas = x509.NewCertPool()
		ok := cas.AppendCertsFromPEM(ca)
		if !ok {
			return nil, errors.New("failed to parse the CA certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		c, _ := pem.Decode(clientCert)
		if tlsCert.Leaf, err = x509.ParseCertificate(c.Bytes); err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}

		certs = []tls.Certificate{tlsCert}
	}

	// todo: dial timeout like that of net.Conn.DialTimeout
	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: cas, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0, debug)
}

// SetReadDeadline set the read deadline for the connection in seconds.
func (c *Conn) SetReadDeadline(seconds int) {
	c.readDeadline = time.Second * time.Duration(seconds)
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	var n int
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total < n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			err = fmt.Errorf("errored while reading incoming message: %v", err)
			break
		}
		total += i
	}
	if total != n {
		err = fmt.Errorf("expected to read %v bytes instead read %v bytes", n, total)
	}

	if c.debug {
		log.Println("Incoming message:", string(msg))
	}

	return
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) error {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err := c.conn.Write(h)
	if err != nil {
		return err
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	// todo: do we need a loop? bufio uses a loop but it might be due to buff length limitation
	n, err = c.conn.Write(msg)
	if err != nil {
		return err
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return nil
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
// Note: TCP/IP stack does not guarantee delivery of messages before the connection is closed.
func (c *Conn) Close() error {
	return c.conn.Close() // todo: if conn.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported so everything can be messed with from outside
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit    chan []byte
	wait    chan bool
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	c.Send <- []byte(message)
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	c.Quit <- []byte(message)
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	c.Quit <- []byte("")
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	log.Println("Writing ", payload)
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
		close(c.wait) // Unblocks the ConnectionHandler
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer
		c.Close()
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
		wait: make(chan bool),
	}

	// Start read/write loop
	go c.read_write()

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Make sure connections that are opened will get closed
	defer func() {
		dst.ConnectionClosed(c)
	}()

	<- c.Wait
}
<commit_msg>Wait is private<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"time"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
const (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize = 512
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported so everything can be messed with from outside
	Ws      *websocket.Conn
	Send    chan []byte
	Dst     BottleDst
	Quit    chan []byte
	wait    chan bool
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	c.Send <- []byte(message)
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	c.Quit <- []byte(message)
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	c.Quit <- []byte("")
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	log.Println("Writing ", payload)
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
		close(c.wait) // Unblocks the ConnectionHandler
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer
		c.Close()
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send: make(chan []byte, 256),
		Ws: ws,
		Dst: dst,
		Quit: make(chan []byte),
		wait: make(chan bool),
	}

	// Start read/write loop
	go c.read_write()

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Make sure connections that are opened will get closed
	defer func() {
		dst.ConnectionClosed(c)
	}()

	<- c.wait
}
<|endoftext|>


<commit_before>package peerstream

import (
	"errors"
	"net"
	"net/http"
	"sync"

	ss "github.com/docker/spdystream"
)

// SelectConn selects a connection out of list. It allows
// delegation of decision making to clients. Clients can
// make SelectConn functons that check things connection
// qualities -- like latency andbandwidth -- or pick from
// a logical set of connections.
type SelectConn func([]*Conn) *Conn

// ErrInvalidConnSelected signals that a connection selected
// with a SelectConn function is invalid. This may be due to
// the Conn not being part of the original set given to the
// function, or the value being nil.
var ErrInvalidConnSelected = errors.New("invalid selected connection")

// ErrNoConnections signals that no connections are available
var ErrNoConnections = errors.New("no connections")

// Conn is a Swarm-associated connection.
type Conn struct {
	ssConn  *ss.Connection
	netConn net.Conn // underlying connection

	swarm  *Swarm
	groups groupSet

	streams    map[*stream]struct{}
	streamLock sync.RWMutex
}

func newConn(nconn net.Conn, sconn *ss.Connection, s *Swarm) *Conn {
	return &Conn{
		netConn: nconn,
		ssConn:  sconn,
		swarm:   s,
		groups:  groupSet{m: make(map[Group]struct{})},
		streams: make(map[*stream]struct{}),
	}
}

// Swarm returns the Swarm associated with this Conn
func (c *Conn) Swarm() *Swarm {
	return c.swarm
}

// NetConn returns the underlying net.Conn
func (c *Conn) NetConn() net.Conn {
	return c.netConn
}

// SPDYConn returns the spdystream.Connection we use
// Warning: modifying this object is undefined.
func (c *Conn) SPDYConn() *ss.Connection {
	return c.ssConn
}

// Stream returns a stream associated with this Conn
func (c *Conn) NewStream() (Stream, error) {
	return c.swarm.NewStreamWithConn(c)
}

// ConnsWithGroup narrows down a set of connections to
// those in a given group.
func ConnsWithGroup(g Group, conns []*Conn) []*Conn {
	var out []*Conn
	for _, c := range conns {
		if c.groups.Has(g) {
			out = append(out, c)
		}
	}
	return out
}

func ConnInConns(c1 *Conn, conns []*Conn) bool {
	for _, c2 := range conns {
		if c2 == c1 {
			return true
		}
	}
	return false
}

// ------------------------------------------------------------------
// All the connection setup logic here, in one place.
// these are mostly *Swarm methods, but i wanted a less-crowded place
// for them.
// ------------------------------------------------------------------

// addConn is the internal version of AddConn. we need the server bool
// as spdystream requires it.
func (s *Swarm) addConn(netConn net.Conn, server bool) (*Conn, error) {
	if netConn == nil {
		return nil, errors.New("nil conn")
	}

	s.connLock.Lock()
	defer s.connLock.Unlock()

	// first, check if we already have it...
	for c := range s.conns {
		if c.netConn == netConn {
			return c, nil
		}
	}

	// create a new spdystream connection
	ssConn, err := ss.NewConnection(netConn, server)
	if err != nil {
		return nil, err
	}

	// add the connection
	c := newConn(netConn, ssConn, s)
	s.conns[c] = struct{}{}

	// go listen for incoming streams on this connection
	go c.ssConn.Serve(func(ssS *ss.Stream) {
		// log.Printf("accepted stream %d from %s\n", ssS.Identifier(), netConn.RemoteAddr())
		ssS.SendReply(http.Header{}, false)
		stream := s.setupSSStream(ssS, c)
		s.StreamHandler()(stream) // call our handler
	})

	return c, nil
}

// createStream is the internal function that creates a new stream. assumes
// all validation has happened.
func (s *Swarm) createStream(c *Conn) (*stream, error) {

	// Create a new ss.Stream
	ssStream, err := c.ssConn.CreateStream(http.Header{}, nil, false)
	if err != nil {
		return nil, err
	}

	// create a new stream
	return s.setupSSStream(ssStream, c), nil
}

// newStream is the internal function that creates a new stream. assumes
// all validation has happened.
func (s *Swarm) setupSSStream(ssS *ss.Stream, c *Conn) *stream {
	// create a new *stream
	stream := newStream(ssS, c)

	// add it to our streams maps

	// add it to our map
	s.streamLock.Lock()
	c.streamLock.Lock()
	s.streams[stream] = struct{}{}
	c.streams[stream] = struct{}{}
	s.streamLock.Unlock()
	c.streamLock.Unlock()
	return stream
}

func (s *Swarm) removeStream(stream *stream) error {

	// remove from our maps
	s.streamLock.Lock()
	stream.conn.streamLock.Lock()
	delete(s.streams, stream)
	delete(stream.conn.streams, stream)
	s.streamLock.Unlock()
	stream.conn.streamLock.Unlock()

	return stream.ssStream.Close()
}
<commit_msg>conn Close<commit_after>package peerstream

import (
	"errors"
	"net"
	"net/http"
	"sync"

	ss "github.com/docker/spdystream"
)

// SelectConn selects a connection out of list. It allows
// delegation of decision making to clients. Clients can
// make SelectConn functons that check things connection
// qualities -- like latency andbandwidth -- or pick from
// a logical set of connections.
type SelectConn func([]*Conn) *Conn

// ErrInvalidConnSelected signals that a connection selected
// with a SelectConn function is invalid. This may be due to
// the Conn not being part of the original set given to the
// function, or the value being nil.
var ErrInvalidConnSelected = errors.New("invalid selected connection")

// ErrNoConnections signals that no connections are available
var ErrNoConnections = errors.New("no connections")

// Conn is a Swarm-associated connection.
type Conn struct {
	ssConn  *ss.Connection
	netConn net.Conn // underlying connection

	swarm  *Swarm
	groups groupSet

	streams    map[*stream]struct{}
	streamLock sync.RWMutex
}

func newConn(nconn net.Conn, sconn *ss.Connection, s *Swarm) *Conn {
	return &Conn{
		netConn: nconn,
		ssConn:  sconn,
		swarm:   s,
		groups:  groupSet{m: make(map[Group]struct{})},
		streams: make(map[*stream]struct{}),
	}
}

// Swarm returns the Swarm associated with this Conn
func (c *Conn) Swarm() *Swarm {
	return c.swarm
}

// NetConn returns the underlying net.Conn
func (c *Conn) NetConn() net.Conn {
	return c.netConn
}

// SPDYConn returns the spdystream.Connection we use
// Warning: modifying this object is undefined.
func (c *Conn) SPDYConn() *ss.Connection {
	return c.ssConn
}

// Stream returns a stream associated with this Conn
func (c *Conn) NewStream() (Stream, error) {
	return c.swarm.NewStreamWithConn(c)
}

func (c *Conn) Streams() []Stream {
	c.streamLock.RLock()
	defer c.streamLock.RUnlock()

	streams := make([]Stream, 0, len(c.streams))
	for s := range c.streams {
		streams = append(streams, s)
	}
	return streams
}

// Close closes this connection
func (c *Conn) Close() error {
	// close streams
	streams := c.Streams()
	for _, s := range streams {
		s.Close()
	}

	// close underlying connection
	c.netConn.Close()
	return c.swarm.removeConn(c)
}

// ConnsWithGroup narrows down a set of connections to
// those in a given group.
func ConnsWithGroup(g Group, conns []*Conn) []*Conn {
	var out []*Conn
	for _, c := range conns {
		if c.groups.Has(g) {
			out = append(out, c)
		}
	}
	return out
}

func ConnInConns(c1 *Conn, conns []*Conn) bool {
	for _, c2 := range conns {
		if c2 == c1 {
			return true
		}
	}
	return false
}

// ------------------------------------------------------------------
// All the connection setup logic here, in one place.
// these are mostly *Swarm methods, but i wanted a less-crowded place
// for them.
// ------------------------------------------------------------------

// addConn is the internal version of AddConn. we need the server bool
// as spdystream requires it.
func (s *Swarm) addConn(netConn net.Conn, server bool) (*Conn, error) {
	if netConn == nil {
		return nil, errors.New("nil conn")
	}

	s.connLock.Lock()
	defer s.connLock.Unlock()

	// first, check if we already have it...
	for c := range s.conns {
		if c.netConn == netConn {
			return c, nil
		}
	}

	// create a new spdystream connection
	ssConn, err := ss.NewConnection(netConn, server)
	if err != nil {
		return nil, err
	}

	// add the connection
	c := newConn(netConn, ssConn, s)
	s.conns[c] = struct{}{}

	// go listen for incoming streams on this connection
	go c.ssConn.Serve(func(ssS *ss.Stream) {
		// log.Printf("accepted stream %d from %s\n", ssS.Identifier(), netConn.RemoteAddr())
		ssS.SendReply(http.Header{}, false)
		stream := s.setupSSStream(ssS, c)
		s.StreamHandler()(stream) // call our handler
	})

	return c, nil
}

// createStream is the internal function that creates a new stream. assumes
// all validation has happened.
func (s *Swarm) createStream(c *Conn) (*stream, error) {

	// Create a new ss.Stream
	ssStream, err := c.ssConn.CreateStream(http.Header{}, nil, false)
	if err != nil {
		return nil, err
	}

	// create a new stream
	return s.setupSSStream(ssStream, c), nil
}

// newStream is the internal function that creates a new stream. assumes
// all validation has happened.
func (s *Swarm) setupSSStream(ssS *ss.Stream, c *Conn) *stream {
	// create a new *stream
	stream := newStream(ssS, c)

	// add it to our streams maps

	// add it to our map
	s.streamLock.Lock()
	c.streamLock.Lock()
	s.streams[stream] = struct{}{}
	c.streams[stream] = struct{}{}
	s.streamLock.Unlock()
	c.streamLock.Unlock()
	return stream
}

func (s *Swarm) removeStream(stream *stream) error {

	// remove from our maps
	s.streamLock.Lock()
	stream.conn.streamLock.Lock()
	delete(s.streams, stream)
	delete(stream.conn.streams, stream)
	s.streamLock.Unlock()
	stream.conn.streamLock.Unlock()

	return stream.ssStream.Close()
}

func (s *Swarm) removeConn(conn *Conn) error {
	// remove from our maps
	s.connLock.Lock()
	delete(s.conns, conn)
	s.connLock.Unlock()
	return nil
}
<|endoftext|>


<commit_before>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"strconv"
	"time"
	"math"
	"sync"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
var (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize int64 = math.MaxInt64
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported actual webscoket so can be messed with from outside
	Ws        *websocket.Conn
	Send      chan []byte
	Dst       BottleDst
	Quit      chan []byte
	valid     bool

	// Ping Calculation
	// TODO make sure pings pair off, right now really bad lagg would case ping ping pong to give a false ping value
	lastping  int64
	ping      int64
	pinglock  sync.Mutex
}

//
// Get remote address
//
func (c *Conn) RemoteAddr() string {
	return c.Ws.RemoteAddr().String()
}

//
// Get local address
//
func (c *Conn) LocalAddr() string {
	return c.Ws.LocalAddr().String()
}

//
// Change global properties of rhynock
// Options are: writeWait, pongWait, pingPeriod, maxMessageSize
// Value is a string that will be parsed into the correct type.
// For writeWait, pongWait, and pingPeriod a duration string is expected, for maxMessageSize an int string.
//
func SetProperty(key, value string) {
	switch key {
	case "writeWait":
		val, err := time.ParseDuration(value)
		if err == nil {
			writeWait = val
		} else {
			log.Println(err)
		}
	case "pongWait":
		val, err := time.ParseDuration(value)
		if err == nil {
			pongWait = val
		} else {
			log.Println(err)
		}
	case "pingPeriod":
		val, err := time.ParseDuration(value)
		if err == nil {
			pingPeriod = val
		} else {
			log.Println(err)
		}
	case "maxMessageSize":
		val, err := strconv.ParseInt(value, 10, 64)
		if err == nil {
			maxMessageSize = val
		} else {
			log.Println(err)
		}
	}
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	if c.valid {
		c.Send <- []byte(message)
	} else {
		log.Println("rhynock.Conn.SendMsg: Socket closed.")
	}
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	if c.valid {
		c.Quit <- []byte(message)
	} else {
		log.Println("rhynock.Conn.CloseMsg: Socket already closed.")
	}
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	if c.valid {
		c.Quit <- []byte("")
	} else {
		log.Println("rhynock.Conn.Close: Socket already closed.")
	}
}

//
// Get the current ping of this connection
//
func (c *Conn) GetPing() int64 {
	c.pinglock.Lock()
	defer c.pinglock.Unlock()
	return c.ping
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.valid = false
		c.Dst.ConnectionClosed(c)
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		c.pinglock.Lock()
		c.ping = (time.Now().UnixNano() - c.lastping) / 2000000
		c.pinglock.Unlock()
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer if needed
		if c.valid {
			c.Close()
		}
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Record timestamp of ping
			c.lastping = time.Now().UnixNano()

			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send:     make(chan []byte, 256),
		Ws:       ws,
		Dst:      dst,
		Quit:     make(chan []byte),
		valid:    true,
		lastping: 0,
		ping:     0,
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start read write loop blocking
	c.read_write()
}

//
// Wrap a websocket in a rhynock handler
//
func WrapWebsocket(ws *websocket.Conn, dst BottleDst)  *Conn {
	// Create new connection object
	c := &Conn{
		Send:     make(chan []byte, 256),
		Ws:       ws,
		Dst:      dst,
		Quit:     make(chan []byte),
		valid:    true,
		lastping: 0,
		ping:     0,
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start read write loop blocking
	go c.read_write()

	return c
}

//
// Wraps a websocket in a rhynock.conn
//
func (c *Conn) Reroute(dst BottleDst) {
	c.Dst.ConnectionClosed(c)
	c.Dst = dst
	c.Dst.ConnectionOpened(c)
}
<commit_msg>Minor race condition on ping time setting<commit_after>package rhynock

import (
	"github.com/gorilla/websocket"
	"net/http"
	"strconv"
	"time"
	"math"
	"sync"
	"log"
)

// Some defaults for pinging
// Needs to be settable from outside
var (
	writeWait = 10 * time.Second
	pongWait = 60 * time.Second
	pingPeriod = (pongWait * 9) / 10
	maxMessageSize int64 = math.MaxInt64
)

// Conn encapsulates our websocket
type Conn struct {
	// Exported actual webscoket so can be messed with from outside
	Ws        *websocket.Conn
	Send      chan []byte
	Dst       BottleDst
	Quit      chan []byte
	valid     bool

	// Ping Calculation
	// TODO make sure pings pair off, right now really bad lagg would case ping ping pong to give a false ping value
	lastping  int64
	ping      int64
	pinglock  sync.Mutex
}

//
// Get remote address
//
func (c *Conn) RemoteAddr() string {
	return c.Ws.RemoteAddr().String()
}

//
// Get local address
//
func (c *Conn) LocalAddr() string {
	return c.Ws.LocalAddr().String()
}

//
// Change global properties of rhynock
// Options are: writeWait, pongWait, pingPeriod, maxMessageSize
// Value is a string that will be parsed into the correct type.
// For writeWait, pongWait, and pingPeriod a duration string is expected, for maxMessageSize an int string.
//
func SetProperty(key, value string) {
	switch key {
	case "writeWait":
		val, err := time.ParseDuration(value)
		if err == nil {
			writeWait = val
		} else {
			log.Println(err)
		}
	case "pongWait":
		val, err := time.ParseDuration(value)
		if err == nil {
			pongWait = val
		} else {
			log.Println(err)
		}
	case "pingPeriod":
		val, err := time.ParseDuration(value)
		if err == nil {
			pingPeriod = val
		} else {
			log.Println(err)
		}
	case "maxMessageSize":
		val, err := strconv.ParseInt(value, 10, 64)
		if err == nil {
			maxMessageSize = val
		} else {
			log.Println(err)
		}
	}
}

//
// Convenience function so you dont have to use the Send channel
//
func (c *Conn) SendMsg(message string) {
	// Basically just typecasting for convenience
	if c.valid {
		c.Send <- []byte(message)
	} else {
		log.Println("rhynock.Conn.SendMsg: Socket closed.")
	}
}

//
// Convenience function to call the quit channel with a message
//
func (c *Conn) CloseMsg(message string) {
	if c.valid {
		c.Quit <- []byte(message)
	} else {
		log.Println("rhynock.Conn.CloseMsg: Socket already closed.")
	}
}

//
// This function chews through the power cables
//
func (c *Conn) Close() {
	// Send ourself the quit signal with no message
	if c.valid {
		c.Quit <- []byte("")
	} else {
		log.Println("rhynock.Conn.Close: Socket already closed.")
	}
}

//
// Get the current ping of this connection
//
func (c *Conn) GetPing() int64 {
	c.pinglock.Lock()
	defer c.pinglock.Unlock()
	return c.ping
}

//
// Used to write a single message to the client and report any errors
//
func (c *Conn) write(t int, payload []byte) error {
	c.Ws.SetWriteDeadline(time.Now().Add(writeWait))
	return c.Ws.WriteMessage(t, payload)
}

//
// Maintains both a reader and a writer, cleans up both if one fails
//
func (c *Conn) read_write() {
	// Ping timer
	ticker := time.NewTicker(pingPeriod)

	// Clean up Connection and Connection resources
	defer func() {
		ticker.Stop()
		c.valid = false
		c.Dst.ConnectionClosed(c)
		close(c.Send)
		close(c.Quit)
		c.Ws.Close()
	}()

	// Config websocket settings
	c.Ws.SetReadLimit(maxMessageSize)
	c.Ws.SetReadDeadline(time.Now().Add(pongWait))
	c.Ws.SetPongHandler(func(string) error {
		// Give each client pongWait seconds after the ping to respond
		c.Ws.SetReadDeadline(time.Now().Add(pongWait))
		c.pinglock.Lock()
		c.ping = (time.Now().UnixNano() - c.lastping) / 2000000
		c.pinglock.Unlock()
		return nil
	})

	// Start a reading goroutine
	// The reader will stop when the c.Ws.Close is called at
	// in the defered cleanup function, so we do not manually
	// have to close the reader
	go func() {
		for {
			// This blocks until it reads EOF or an error
			// occurs trying to read, the error can be
			// used to detect when the client closes the Connection
			_, message, err := c.Ws.ReadMessage()
			if err != nil {
				break // If we get an error escape the loop
			}

			// Bottle the message with its sender
			bottle := &Bottle{
				Sender: c,
				Message: message,
			}

			// Send to the destination for processing
			c.Dst.GetBottleChan() <- bottle
		}
		// The reader has been terminated, alert the writer if needed
		if c.valid {
			c.Close()
		}
	}()

	// Main writing loop
	for {
		select {
		case message, ok := <- c.Send:
			// Our send channel has something in it or the channel closed
			if !ok {
				// Our channel was closed, gracefully close socket Conn
				c.write(websocket.CloseMessage, []byte{})
				return
			}
			// Attempt to write the message to the websocket
			if err := c.write(websocket.TextMessage, message); err != nil {
				// If we get an error we can no longer communcate with client
				// return, no need to send CloseMessage since that would
				// just yield another error
				return
			}

		case <- ticker.C:
			// Record timestamp of ping
			c.pingLock.Lock()
			c.lastping = time.Now().UnixNano()
			c.pingLock.Unlock()

			// Ping ticker went off. We need to ping to check for connectivity.
			if err := c.write(websocket.PingMessage, []byte{}); err != nil {
				// We got an error pinging, return and call defer
				// defer will close the socket which will kill the reader
				return
			}

		case bytes := <- c.Quit:
			// Close connection and send a final message
			c.write(websocket.TextMessage, bytes)
			c.write(websocket.CloseMessage, []byte{})
			return
		}
	}
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r* http.Request) bool { return true }}

//
// Hanlder function to start a websocket connection
//
func ConnectionHandler(w http.ResponseWriter, r *http.Request, dst BottleDst) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	// Create new connection object
	c := &Conn{
		Send:     make(chan []byte, 256),
		Ws:       ws,
		Dst:      dst,
		Quit:     make(chan []byte),
		valid:    true,
		lastping: 0,
		ping:     0,
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start read write loop blocking
	c.read_write()
}

//
// Wrap a websocket in a rhynock handler
//
func WrapWebsocket(ws *websocket.Conn, dst BottleDst)  *Conn {
	// Create new connection object
	c := &Conn{
		Send:     make(chan []byte, 256),
		Ws:       ws,
		Dst:      dst,
		Quit:     make(chan []byte),
		valid:    true,
		lastping: 0,
		ping:     0,
	}

	// Alert the destination that a new connection has opened
	dst.ConnectionOpened(c)

	// Start read write loop blocking
	go c.read_write()

	return c
}

//
// Wraps a websocket in a rhynock.conn
//
func (c *Conn) Reroute(dst BottleDst) {
	c.Dst.ConnectionClosed(c)
	c.Dst = dst
	c.Dst.ConnectionOpened(c)
}
<|endoftext|>


<commit_before>// Package core provides common definitions and functionalities of V2Ray.
package core

import (
	"fmt"

	"github.com/v2ray/v2ray-core/common/platform"
)

var (
	version  = "0.8"
	build    = "Custom"
	codename = "Post Apocalypse"
	intro    = "A stable and unbreakable connection for everyone."
)

func PrintVersion() {
	fmt.Printf("V2Ray %s (%s) %s%s", version, codename, build, platform.LineSeparator())
	fmt.Print("%s%s", intro, platform.LineSeparator())
}
<commit_msg>Fix PrintVersion<commit_after>// Package core provides common definitions and functionalities of V2Ray.
package core

import (
	"fmt"

	"github.com/v2ray/v2ray-core/common/platform"
)

var (
	version  = "0.8"
	build    = "Custom"
	codename = "Post Apocalypse"
	intro    = "A stable and unbreakable connection for everyone."
)

func PrintVersion() {
	fmt.Printf("V2Ray %s (%s) %s%s", version, codename, build, platform.LineSeparator())
	fmt.Printf("%s%s", intro, platform.LineSeparator())
}
<|endoftext|>


<commit_before>package core

import (
	"encoding/json"
	"fmt"
	"github.com/thethingsnetwork/core/lorawan"
)

var ErrInvalidPacket error = fmt.Errorf("Invalid Packet")

type Packet struct {
	Metadata Metadata
	Payload  lorawan.PHYPayload
}

func (p Packet) DevAddr() (lorawan.DevAddr, error) {
	return p.Payload.DevAddr()
}

func (p Packet) String() string {
	str := "Packet {"
	if p.Metadata != nil {
		str += fmt.Sprintf("\n\t%s}", p.Metadata.String())
	}
	str += fmt.Sprintf("\n\tPayload%+v\n}", p.Payload)
	return str
}

type Recipient struct {
	Address interface{}
	Id      interface{}
}

type Metadata interface {
	json.Marshaler
	json.Unmarshaler
	String() string
}

type AckNacker interface {
	Ack(p Packet) error
	Nack(p Packet) error
}

type Component interface {
	Handle(p Packet, an AckNacker) error
	NextUp() (Packet, error)
	NextDown() (Packet, error)
}

type Adapter interface {
	Send(p Packet, r ...Recipient) error
	Next() (Packet, AckNacker, error)
}

type Registration struct {
	DevAddr lorawan.DevAddr
	Handler Recipient
	NwsKey  lorawan.AES128Key
}

type BrkHdlAdapter interface {
	Adapter
	NextRegistration() (Registration, AckNacker, error)
}

type Router Component
type Broker interface {
	Component
	Register(reg Registration) error
}
type Handler Component
type NetworkController Component
<commit_msg>[broker] Update core.go to reflect last changes.<commit_after>package core

import (
	"encoding/json"
	"fmt"
	"github.com/thethingsnetwork/core/lorawan"
)

var ErrInvalidPacket error = fmt.Errorf("Invalid Packet")

type Packet struct {
	Metadata Metadata
	Payload  lorawan.PHYPayload
}

func (p Packet) DevAddr() (lorawan.DevAddr, error) {
	return p.Payload.DevAddr()
}

func (p Packet) String() string {
	str := "Packet {"
	if p.Metadata != nil {
		str += fmt.Sprintf("\n\t%s}", p.Metadata.String())
	}
	str += fmt.Sprintf("\n\tPayload%+v\n}", p.Payload)
	return str
}

type Recipient struct {
	Address interface{}
	Id      interface{}
}

type Metadata interface {
	json.Marshaler
	json.Unmarshaler
	String() string
}

type AckNacker interface {
	Ack(p Packet) error
	Nack(p Packet) error
}

type Component interface {
	Register(reg Registration) error
	HandleUp(p Packet, an AckNacker) error
	HandleDown(p Packet, an AckNacker) error
}

type Adapter interface {
	Send(p Packet, r ...Recipient) error
	Next() (Packet, AckNacker, error)
	NextRegistration() (Registration, AckNacker, error)
}

type Registration struct {
	DevAddr   lorawan.DevAddr
	Recipient Recipient
	Options   interface{}
}

type Router Component
type Broker Component
type Handler Component
type NetworkController Component
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package core

import (
	"fmt"
	. "github.com/thethingsnetwork/core/lorawan/semtech"
)

type BrokerAddress string
type GatewayAddress string

type Router interface {
	// HandlerError manages all kind of error that occur during the router lifecycle
	HandleError(err interface{})

	// HandleUplink manages uplink packets coming from a gateway
	HandleUplink(packet Packet, gateway GatewayAddress)

	// HandleDownlink manages downlink packets coming from a broker
	HandleDownlink(payload Payload, broker BrokerAddress)

	// RegisterDevice associates a device address to a set of brokers for a given period
	RegisterDevice(devAddr DeviceAddress, broAddrs ...BrokerAddress)

	// Connect the router to its adapters
	Connect(upAdapter GatewayRouterAdapter, downAdapter RouterBrokerAdapter)
}

// The error types belows are going to be more complex in order to handle custom behavior for
// each error type.
var ErrBadOptions error = fmt.Errorf("Unreckonized or invalid options")
var ErrNotInitialized error = fmt.Errorf("Structure not initialized")
var ErrBadGatewayAddress error = fmt.Errorf("Invalid gateway address")
var ErrMissingConnection error = fmt.Errorf("Can't proceed without establishing connection")
var ErrInvalidPacket error = fmt.Errorf("Invalid semtech packet")
var ErrInvalidPayload error = fmt.Errorf("Invalid semtech payload")
var ErrBroadcast error = fmt.Errorf("Unable to broadcast the given payload")
var ErrForward error = fmt.Errorf("Unable to forward the given payload")

type Adapter interface {
	// Establish the adapter connection, whatever protocol is being used.
	Listen(router Router, options interface{}) error
}

type GatewayRouterAdapter interface {
	Adapter
	// Ack allows the router to send back a response to a gateway. The name of the method is quite a
	// bad call and will probably change soon.
	Ack(router Router, packet Packet, gateway GatewayAddress) error
}

type RouterBrokerAdapter interface {
	Adapter

	// Broadcast makes the adapter discover all available brokers by sending them a the given packets.
	//
	// We assume that broadcast is also registering a device address towards the router depending
	// on the brokers responses.
	Broadcast(router Router, payload Payload) error

	// Forward is an explicit forwarding of a packet which is known being handled by a set of
	// brokers. None of the contacted broker is supposed to reject the incoming payload; They all
	// ave been queried before and are known as dedicated brokers for the related end-device.
	Forward(router Router, payload Payload, broAddrs ...BrokerAddress) error
}
<commit_msg>[simulators.gateway] Add fake accessor for Network Session Key. Ideally, should be stored in ENV<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package core

import (
	"fmt"
	. "github.com/thethingsnetwork/core/lorawan/semtech"
)

type BrokerAddress string
type GatewayAddress string

type Router interface {
	// HandlerError manages all kind of error that occur during the router lifecycle
	HandleError(err interface{})

	// HandleUplink manages uplink packets coming from a gateway
	HandleUplink(packet Packet, gateway GatewayAddress)

	// HandleDownlink manages downlink packets coming from a broker
	HandleDownlink(payload Payload, broker BrokerAddress)

	// RegisterDevice associates a device address to a set of brokers for a given period
	RegisterDevice(devAddr DeviceAddress, broAddrs ...BrokerAddress)

	// Connect the router to its adapters
	Connect(upAdapter GatewayRouterAdapter, downAdapter RouterBrokerAdapter)
}

// The error types belows are going to be more complex in order to handle custom behavior for
// each error type.
var ErrBadOptions error = fmt.Errorf("Unreckonized or invalid options")
var ErrNotInitialized error = fmt.Errorf("Structure not initialized")
var ErrBadGatewayAddress error = fmt.Errorf("Invalid gateway address")
var ErrMissingConnection error = fmt.Errorf("Can't proceed without establishing connection")
var ErrInvalidPacket error = fmt.Errorf("Invalid semtech packet")
var ErrInvalidPayload error = fmt.Errorf("Invalid semtech payload")
var ErrBroadcast error = fmt.Errorf("Unable to broadcast the given payload")
var ErrForward error = fmt.Errorf("Unable to forward the given payload")

type Adapter interface {
	// Establish the adapter connection, whatever protocol is being used.
	Listen(router Router, options interface{}) error
}

type GatewayRouterAdapter interface {
	Adapter
	// Ack allows the router to send back a response to a gateway. The name of the method is quite a
	// bad call and will probably change soon.
	Ack(router Router, packet Packet, gateway GatewayAddress) error
}

type RouterBrokerAdapter interface {
	Adapter

	// Broadcast makes the adapter discover all available brokers by sending them a the given packets.
	//
	// We assume that broadcast is also registering a device address towards the router depending
	// on the brokers responses.
	Broadcast(router Router, payload Payload) error

	// Forward is an explicit forwarding of a packet which is known being handled by a set of
	// brokers. None of the contacted broker is supposed to reject the incoming payload; They all
	// ave been queried before and are known as dedicated brokers for the related end-device.
	Forward(router Router, payload Payload, broAddrs ...BrokerAddress) error
}

func GetNwSKey() [16]byte {
	return [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
}
<|endoftext|>


<commit_before>package healthcheck_test

import (
	"net"
	"net/http"
	"strconv"
	"time"

	"code.cloudfoundry.org/healthcheck"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	itReturnsHealthCheckError := func(healthCheck func() error, code int, reason string) {
		It("returns healthcheck error with code "+strconv.Itoa(code)+" with an appropriate message", func() {
			err := healthCheck()
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))
			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(code))
			Expect(hErr.Message).To(ContainSubstring(reason))
		})
	}

	var (
		server     *ghttp.Server
		serverAddr string

		ip string

		uri         string
		port        string
		timeout     time.Duration
		serverDelay time.Duration

		hc healthcheck.HealthCheck
	)

	BeforeEach(func() {
		ip = getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()

		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		timeout = 100 * time.Millisecond
		serverDelay = 0

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		_, port, err = net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
	})

	JustBeforeEach(func() {
		server.RouteToHandler("GET", "/api/_ping",
			func(http.ResponseWriter, *http.Request) {
				time.Sleep(serverDelay)
			})
		server.Start()

		hc = healthcheck.NewHealthCheck("tcp", uri, port, timeout)
	})

	AfterEach(func() {
		if server != nil {
			server.CloseClientConnections()
			server.Close()
		}
	})

	Describe("check interfaces", func() {
		It("succeeds when there are healthy interfaces", func() {
			interfaces, err := net.Interfaces()
			Expect(err).NotTo(HaveOccurred())

			err = hc.CheckInterfaces(interfaces)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when the server is failing", func() {
			AfterEach(func() {
				server = nil
			})

			It("fails appropriately when there are unhealthy interfaces", func() {
				server.Close()

				interfaces, err := net.Interfaces()
				Expect(err).NotTo(HaveOccurred())

				err = hc.CheckInterfaces(interfaces)
				Expect(err).To(HaveOccurred())
				Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

				hErr := err.(healthcheck.HealthCheckError)
				// fails with different error codes on Linux (4) or OSX (64)
				// check to see it was not the NO interfaces error (3)
				Expect(hErr.Code).ToNot(Equal(3))
			})
		})

		It("fails appropriately when there are no interfaces", func() {
			err := hc.CheckInterfaces(nil)
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(3))
			Expect(hErr.Message).To(ContainSubstring("failure to find suitable interface"))
		})
	})

	Describe("port healthcheck", func() {
		portHealthCheck := func() error {
			return hc.PortHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = ""
		})

		Context("when the address is listening", func() {
			It("succeeds", func() {
				Expect(portHealthCheck()).To(Succeed())
			})
		})

		Context("when the address is not listening", func() {
			BeforeEach(func() {
				port = "-1"
			})

			itReturnsHealthCheckError(portHealthCheck, 4, "failure to make TCP connection")
		})

		Context("when the server is slow in responding", func() {
			BeforeEach(func() {
				timeout = time.Nanosecond
				serverDelay = time.Second
			})

			itReturnsHealthCheckError(portHealthCheck, 64, "timeout when making TCP connection")
		})
	})

	Describe("http healthcheck", func() {
		httpHealthCheck := func() error {
			return hc.HTTPHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = "/api/_ping"
		})

		Context("when the healthcheck is properly invoked", func() {
			Context("when the address is listening", func() {
				It("succeeds", func() {
					Expect(httpHealthCheck()).To(Succeed())
				})
			})

			Context("when the address returns error http code", func() {
				JustBeforeEach(func() {
					server.RouteToHandler("GET", "/api/_ping", ghttp.RespondWith(500, ""))
				})

				itReturnsHealthCheckError(httpHealthCheck, 6, "failure to get valid HTTP status code: 500")
			})

			Context("when the address is not listening", func() {
				BeforeEach(func() {
					port = "-1"
				})

				itReturnsHealthCheckError(httpHealthCheck, 5, "failure to make HTTP request")
			})

			Context("when the server is too slow to respond", func() {
				BeforeEach(func() {
					timeout = time.Nanosecond
					serverDelay = time.Second
				})

				itReturnsHealthCheckError(httpHealthCheck, 65, "timeout when making HTTP request")
			})
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<commit_msg>Skip the TCP timeout test on Windows.<commit_after>package healthcheck_test

import (
	"net"
	"net/http"
	"runtime"
	"strconv"
	"time"

	"code.cloudfoundry.org/healthcheck"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("HealthCheck", func() {
	itReturnsHealthCheckError := func(healthCheck func() error, code int, reason string) {
		It("returns healthcheck error with code "+strconv.Itoa(code)+" with an appropriate message", func() {
			err := healthCheck()
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))
			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(code))
			Expect(hErr.Message).To(ContainSubstring(reason))
		})
	}

	var (
		server     *ghttp.Server
		serverAddr string

		ip string

		uri         string
		port        string
		timeout     time.Duration
		serverDelay time.Duration

		hc healthcheck.HealthCheck
	)

	BeforeEach(func() {
		ip = getNonLoopbackIP()
		server = ghttp.NewUnstartedServer()

		listener, err := net.Listen("tcp", ip+":0")
		Expect(err).NotTo(HaveOccurred())

		timeout = 100 * time.Millisecond
		serverDelay = 0

		server.HTTPTestServer.Listener = listener
		serverAddr = listener.Addr().String()
		_, port, err = net.SplitHostPort(serverAddr)
		Expect(err).NotTo(HaveOccurred())
	})

	JustBeforeEach(func() {
		server.RouteToHandler("GET", "/api/_ping",
			func(http.ResponseWriter, *http.Request) {
				time.Sleep(serverDelay)
			})
		server.Start()

		hc = healthcheck.NewHealthCheck("tcp", uri, port, timeout)
	})

	AfterEach(func() {
		if server != nil {
			server.CloseClientConnections()
			server.Close()
		}
	})

	Describe("check interfaces", func() {
		It("succeeds when there are healthy interfaces", func() {
			interfaces, err := net.Interfaces()
			Expect(err).NotTo(HaveOccurred())

			err = hc.CheckInterfaces(interfaces)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when the server is failing", func() {
			AfterEach(func() {
				server = nil
			})

			It("fails appropriately when there are unhealthy interfaces", func() {
				server.Close()

				interfaces, err := net.Interfaces()
				Expect(err).NotTo(HaveOccurred())

				err = hc.CheckInterfaces(interfaces)
				Expect(err).To(HaveOccurred())
				Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

				hErr := err.(healthcheck.HealthCheckError)
				// fails with different error codes on Linux (4) or OSX (64)
				// check to see it was not the NO interfaces error (3)
				Expect(hErr.Code).ToNot(Equal(3))
			})
		})

		It("fails appropriately when there are no interfaces", func() {
			err := hc.CheckInterfaces(nil)
			Expect(err).To(HaveOccurred())
			Expect(err).To(BeAssignableToTypeOf(healthcheck.HealthCheckError{}))

			hErr := err.(healthcheck.HealthCheckError)
			Expect(hErr.Code).To(Equal(3))
			Expect(hErr.Message).To(ContainSubstring("failure to find suitable interface"))
		})
	})

	Describe("port healthcheck", func() {
		portHealthCheck := func() error {
			return hc.PortHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = ""
		})

		Context("when the address is listening", func() {
			It("succeeds", func() {
				Expect(portHealthCheck()).To(Succeed())
			})
		})

		Context("when the address is not listening", func() {
			BeforeEach(func() {
				port = "-1"
			})

			itReturnsHealthCheckError(portHealthCheck, 4, "failure to make TCP connection")
		})

		Context("when the server is slow in responding", func() {
			BeforeEach(func() {
				if runtime.GOOS == "windows" {
					Skip("TCP timeout tests are unreliable on Windows")
				}

				timeout = time.Nanosecond
			})

			itReturnsHealthCheckError(portHealthCheck, 64, "timeout when making TCP connection")
		})
	})

	Describe("http healthcheck", func() {
		httpHealthCheck := func() error {
			return hc.HTTPHealthCheck(ip)
		}

		BeforeEach(func() {
			uri = "/api/_ping"
		})

		Context("when the healthcheck is properly invoked", func() {
			Context("when the address is listening", func() {
				It("succeeds", func() {
					Expect(httpHealthCheck()).To(Succeed())
				})
			})

			Context("when the address returns error http code", func() {
				JustBeforeEach(func() {
					server.RouteToHandler("GET", "/api/_ping", ghttp.RespondWith(500, ""))
				})

				itReturnsHealthCheckError(httpHealthCheck, 6, "failure to get valid HTTP status code: 500")
			})

			Context("when the address is not listening", func() {
				BeforeEach(func() {
					port = "-1"
				})

				itReturnsHealthCheckError(httpHealthCheck, 5, "failure to make HTTP request")
			})

			Context("when the server is too slow to respond", func() {
				BeforeEach(func() {
					timeout = time.Nanosecond
					serverDelay = time.Second
				})

				itReturnsHealthCheckError(httpHealthCheck, 65, "timeout when making HTTP request")
			})
		})
	})
})

func getNonLoopbackIP() string {
	interfaces, err := net.Interfaces()
	Expect(err).NotTo(HaveOccurred())
	for _, intf := range interfaces {
		addrs, err := intf.Addrs()
		if err != nil {
			continue
		}

		for _, a := range addrs {
			if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
				if ipnet.IP.To4() != nil {
					return ipnet.IP.String()
				}
			}
		}
	}
	Fail("no non-loopback address found")
	panic("non-reachable")
}
<|endoftext|>


<commit_before>// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package fs

import (
	"errors"
	"os"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

const isLinux = true

// syscall.EpollCtl() commands.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
const (
	epollCTLAdd = 1 // EPOLL_CTL_ADD
	epollCTLDel = 2 // EPOLL_CTL_DEL
	epollCTLMod = 3 // EPOLL_CTL_MOD
)

// Bitmask for field syscall.EpollEvent.Events.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
type epollEvent uint32

const (
	epollIN        epollEvent = 0x1        // EPOLLIN: available for read
	epollOUT       epollEvent = 0x4        // EPOLLOUT: available for write
	epollPRI       epollEvent = 0x2        // EPOLLPRI: exceptional urgent condition
	epollERR       epollEvent = 0x8        // EPOLLERR: error
	epollHUP       epollEvent = 0x10       // EPOLLHUP: hangup
	epollET        epollEvent = 0x80000000 // EPOLLET: Edge Triggered behavior
	epollONESHOT   epollEvent = 0x40000000 // EPOLLONESHOT: One shot
	epollWAKEUP    epollEvent = 0x20000000 // EPOLLWAKEUP: disable system sleep; kernel >=3.5
	epollEXCLUSIVE epollEvent = 0x10000000 // EPOLLEXCLUSIVE: only wake one; kernel >=4.5
)

var bitmaskString = [...]struct {
	e epollEvent
	s string
}{
	{epollIN, "IN"},
	{epollOUT, "OUT"},
	{epollPRI, "PRI"},
	{epollERR, "ERR"},
	{epollHUP, "HUP"},
	{epollET, "ET"},
	{epollONESHOT, "ONESHOT"},
	{epollWAKEUP, "WAKEUP"},
	{epollEXCLUSIVE, "EXCLUSIVE"},
}

// String is useful for debugging.
func (e epollEvent) String() string {
	var out []string
	for _, b := range bitmaskString {
		if e&b.e != 0 {
			out = append(out, b.s)
			e &^= b.e
		}
	}
	if e != 0 {
		out = append(out, "0x"+strconv.FormatUint(uint64(e), 16))
	}
	if len(out) == 0 {
		out = []string{"0"}
	}
	return strings.Join(out, "|")
}

func ioctl(f uintptr, op uint, arg uintptr) error {
	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f, uintptr(op), arg); errno != 0 {
		return syscall.Errno(errno)
	}
	return nil
}

type event struct {
	event   [1]syscall.EpollEvent
	epollFd int
	fd      int
}

// makeEvent creates an epoll *edge* triggered event.
//
// References:
// behavior and flags: http://man7.org/linux/man-pages/man7/epoll.7.html
// syscall.EpollCreate: http://man7.org/linux/man-pages/man2/epoll_create.2.html
// syscall.EpollCtl: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
func (e *event) makeEvent(fd uintptr) error {
	epollFd, err := syscall.EpollCreate(1)
	if err != nil {
		return err
	}
	e.epollFd = epollFd
	e.fd = int(fd)
	// EPOLLWAKEUP could be used to force the system to not go do sleep while
	// waiting for an edge. This is generally a bad idea, as we'd instead have
	// the system to *wake up* when an edge is triggered. Achieving this is
	// outside the scope of this interface.
	e.event[0].Events = uint32(epollPRI | epollET)
	e.event[0].Fd = int32(e.fd)
	return syscall.EpollCtl(e.epollFd, epollCTLAdd, e.fd, &e.event[0])
}

func (e *event) wait(timeoutms int) (int, error) {
	// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
	return syscall.EpollWait(e.epollFd, e.event[:], timeoutms)
}

//

// eventsListener listens for events for multiple files as a single system call.
//
// One OS thread is needed for all the events. This is more efficient on single
// core system.
type eventsListener struct {
	wg sync.WaitGroup

	mu      sync.Mutex
	closing bool
	// file descriptor of the epoll handle itself.
	epollFd int
	// pipes to wake up the loop()
	r, w *os.File
	// mapping of file descriptors to wait on with their corresponding channels.
	fds    map[int32]chan<- time.Time
	wakeUp <-chan time.Time
}

// init must be called on a fresh instance.
func (e *eventsListener) init() error {
	e.mu.Lock()
	if e.epollFd != 0 {
		// Was already initialized.
		e.mu.Unlock()
		return nil
	}
	e.closing = false
	var err error
	e.epollFd, err = syscall.EpollCreate(1)
	if err != nil {
		return err
	}
	e.r, e.w, err = os.Pipe()
	e.fds = map[int32]chan<- time.Time{}
	// Only need epollIN. epollPRI has no effect on pipes.
	const flags = epollET | epollIN
	if err := e.addFdInner(e.r.Fd(), flags); err != nil {
		return err
	}
	wakeUp := make(chan time.Time)
	e.fds[int32(e.r.Fd())] = wakeUp
	e.wakeUp = wakeUp
	// The mutex is still held after this function exits, it's loop() that will
	// release the mutex.
	//
	// This forces loop() to be started before addFd() can be called by users.
	e.wg.Add(1)
	go e.loop()
	return nil
}

// stopLoop releases the epoll resource and terminates the polling loop.
//
// This is used in unit tests only.
func (e *eventsListener) stopLoop() error {
	e.mu.Lock()
	if e.epollFd == 0 {
		// Was already closed.
		e.mu.Unlock()
		return nil
	}
	if len(e.fds) != 1 {
		// Disallow closing if there is any listener, as wakeUpLoop() could
		// deadlock otherwise.
		e.mu.Unlock()
		return errors.New("cannot stop loop() while there's an active listener")
	}
	e.closing = true
	e.mu.Unlock()
	// Wake up the poller so it notices it should quit.
	e.wakeUpLoop(nil)
	// It is important to wait for the loop() function to exit, as EpollWait()
	// will not return on a closed file handle.
	e.wg.Wait()
	e.mu.Lock()
	_ = e.w.Close()
	e.w = nil
	_ = e.r.Close()
	e.r = nil
	err := syscall.Close(e.epollFd)
	e.epollFd = 0
	e.mu.Unlock()
	return err
}

// loop is the main event loop.
func (e *eventsListener) loop() {
	defer e.wg.Done()
	var events []syscall.EpollEvent
	type lookup struct {
		c     chan<- time.Time
		event epollEvent
	}
	var lookups []lookup
	for first := true; ; {
		if !first {
			e.mu.Lock()
		}
		if len(events) < len(e.fds) {
			events = make([]syscall.EpollEvent, len(e.fds))
		}
		closing := e.closing
		e.mu.Unlock()
		first = false

		if closing {
			// We're done.
			return
		}
		if len(events) == 0 {
			panic("internal error: there's should be at least one pipe")
		}

		// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
		n, err := syscall.EpollWait(e.epollFd, events, -1)
		if n <= 0 {
			// -1 if an error occurred (EBADF, EFAULT, EINVAL) or the call was
			// interrupted by a signal (EINTR).
			// 0 is the timeout occurred. In this case there's no timeout specified.
			// Still handle this explicitly in case a timeout could be triggered by
			// external events, like system sleep.
			continue
		}
		if err != nil {
			// TODO(maruel): It'd be nice to be able to surface this.
			// This may cause a busy loop. Hopefully the user will notice and will
			// fix their code.
			// This can happen when removeFd() is called, in this case silently
			// ignore the error.
			continue
		}

		now := time.Now()
		// Create a look up table with the lock, so that then the channel can be
		// pushed to without the lock.
		if cap(lookups) < n {
			lookups = make([]lookup, 0, n)
		} else {
			lookups = lookups[:0]
		}

		e.mu.Lock()
		for _, ev := range events[:n] {
			ep := epollEvent(ev.Events)
			// Skip over file descriptors that are not present.
			c, ok := e.fds[ev.Fd]
			if !ok {
				// That's a race condition where the file descriptor was removed by
				// removeFd() but it still triggered. Ignore this event.
				continue
			}
			// Look at the event to determine if it's worth sending a pulse. It's
			// maybe not worth it.
			if ep&epollERR != 0 {
				// TODO(maruel): We should stop listening to this file descriptor.
				// Same for epollHUP.
			}
			// pipe and sockets trigger epollIN and epollOUT, but GPIO sysfs
			// triggers epollPRI.
			if ep&(epollPRI|epollIN|epollOUT) != 0 {
				lookups = append(lookups, lookup{c: c, event: ep})
			}
		}
		e.mu.Unlock()

		// Once the lock is released, send the timestamps.
		for _, t := range lookups {
			t.c <- now
		}
	}
}

// addFd starts listening to events generated by file descriptor |fd|.
//
// fd is the OS file descriptor. In practice, it must fit a int32 value. It
// works on pipes, sockets and sysfs objects like GPIO but not on standard
// files.
//
// c is the channel to send events to. Unbuffered channel will block the event
// loop, which may mean lost events, especially if multiple files are listened
// to simultaneously.
//
// flags is the events to listen to. No need to specify epollERR and epollHUP,
// they are sent anyway.
//
// addFd lazy initializes eventsListener if it was not initialized yet.
//
// It can fail due to various reasons, a few are:
//   ENOSPC: /proc/sys/fs/epoll/max_user_watches limit was exceeded
//   ENOMEM: No memory available
//   EPERM: fd is a regular file or directory
func (e *eventsListener) addFd(fd uintptr, c chan<- time.Time, flags epollEvent) error {
	if c == nil {
		return errors.New("fd: addFd requires a valid channel")
	}
	if err := e.init(); err != nil {
		return err
	}
	if err := e.addFdInner(fd, flags); err != nil {
		return err
	}
	e.mu.Lock()
	e.fds[int32(fd)] = c
	e.mu.Unlock()
	// Wake up the poller so it notices there's one new file.
	e.wakeUpLoop(nil)
	return nil
}

func (e *eventsListener) addFdInner(fd uintptr, flags epollEvent) error {
	ev := syscall.EpollEvent{Events: uint32(flags), Fd: int32(fd)}
	return syscall.EpollCtl(e.epollFd, epollCTLAdd, int(fd), &ev)
}

// removeFd stop listening to events on this file descriptor.
func (e *eventsListener) removeFd(fd uintptr) error {
	if err := syscall.EpollCtl(e.epollFd, epollCTLDel, int(fd), nil); err != nil {
		return err
	}
	e.mu.Lock()
	delete(e.fds, int32(fd))
	e.mu.Unlock()
	// Wake up the poller so it notices there's one less file.
	e.wakeUpLoop(nil)
	return nil
}

// wakeUpLoop wakes up the poller and waits for it.
//
// Must not be called with the lock held.
func (e *eventsListener) wakeUpLoop(c <-chan time.Time) time.Time {
	e.mu.Lock()
	running := e.epollFd != 0
	e.mu.Unlock()
	if !running {
		// The loop is not running, nothing to wake up.
		return time.Now()
	}
	// TODO(maruel): Figure out a way to wake up that doesn't require emptying.
	var b [1]byte
	_, _ = e.w.Write(b[:])
	var t time.Time
	if c != nil {
		// To prevent deadlock, also empty c.
		for {
			select {
			case <-c:
			case t = <-e.wakeUp:
				goto out
			}
		}
	out:
	} else {
		t = <-e.wakeUp
	}
	// Don't forget to empty the pipe. Sadly, this will wake up the loop a second
	// time.
	_, _ = e.r.Read(b[:])
	return t
}

// events is the global events listener.
//
// It uses a single global goroutine lazily initialized to call
// syscall.EpollWait() to listen to many file descriptors at once.
var events eventsListener
<commit_msg>host/fs: arm64 EpollCreate (#369)<commit_after>// Copyright 2017 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package fs

import (
	"errors"
	"os"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

const isLinux = true

// syscall.EpollCtl() commands.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
const (
	epollCTLAdd = 1 // EPOLL_CTL_ADD
	epollCTLDel = 2 // EPOLL_CTL_DEL
	epollCTLMod = 3 // EPOLL_CTL_MOD
)

// Bitmask for field syscall.EpollEvent.Events.
//
// These are defined here so we don't need to import golang.org/x/sys/unix.
//
// http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
type epollEvent uint32

const (
	epollIN        epollEvent = 0x1        // EPOLLIN: available for read
	epollOUT       epollEvent = 0x4        // EPOLLOUT: available for write
	epollPRI       epollEvent = 0x2        // EPOLLPRI: exceptional urgent condition
	epollERR       epollEvent = 0x8        // EPOLLERR: error
	epollHUP       epollEvent = 0x10       // EPOLLHUP: hangup
	epollET        epollEvent = 0x80000000 // EPOLLET: Edge Triggered behavior
	epollONESHOT   epollEvent = 0x40000000 // EPOLLONESHOT: One shot
	epollWAKEUP    epollEvent = 0x20000000 // EPOLLWAKEUP: disable system sleep; kernel >=3.5
	epollEXCLUSIVE epollEvent = 0x10000000 // EPOLLEXCLUSIVE: only wake one; kernel >=4.5
)

var bitmaskString = [...]struct {
	e epollEvent
	s string
}{
	{epollIN, "IN"},
	{epollOUT, "OUT"},
	{epollPRI, "PRI"},
	{epollERR, "ERR"},
	{epollHUP, "HUP"},
	{epollET, "ET"},
	{epollONESHOT, "ONESHOT"},
	{epollWAKEUP, "WAKEUP"},
	{epollEXCLUSIVE, "EXCLUSIVE"},
}

// String is useful for debugging.
func (e epollEvent) String() string {
	var out []string
	for _, b := range bitmaskString {
		if e&b.e != 0 {
			out = append(out, b.s)
			e &^= b.e
		}
	}
	if e != 0 {
		out = append(out, "0x"+strconv.FormatUint(uint64(e), 16))
	}
	if len(out) == 0 {
		out = []string{"0"}
	}
	return strings.Join(out, "|")
}

func ioctl(f uintptr, op uint, arg uintptr) error {
	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f, uintptr(op), arg); errno != 0 {
		return syscall.Errno(errno)
	}
	return nil
}

type event struct {
	event   [1]syscall.EpollEvent
	epollFd int
	fd      int
}

// makeEvent creates an epoll *edge* triggered event.
//
// References:
// behavior and flags: http://man7.org/linux/man-pages/man7/epoll.7.html
// syscall.EpollCreate: http://man7.org/linux/man-pages/man2/epoll_create.2.html
// syscall.EpollCtl: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
func (e *event) makeEvent(fd uintptr) error {
	epollFd, err := syscall.EpollCreate(1)
	switch {
	case err == nil:
		break
	case err.Error() == "function not implemented":
		// Some arch (arm64) do not implement EpollCreate().
		if epollFd, err = syscall.EpollCreate1(0); err != nil {
			return err
		}
	default:
		return err
	}
	e.epollFd = epollFd
	e.fd = int(fd)
	// EPOLLWAKEUP could be used to force the system to not go do sleep while
	// waiting for an edge. This is generally a bad idea, as we'd instead have
	// the system to *wake up* when an edge is triggered. Achieving this is
	// outside the scope of this interface.
	e.event[0].Events = uint32(epollPRI | epollET)
	e.event[0].Fd = int32(e.fd)
	return syscall.EpollCtl(e.epollFd, epollCTLAdd, e.fd, &e.event[0])
}

func (e *event) wait(timeoutms int) (int, error) {
	// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
	return syscall.EpollWait(e.epollFd, e.event[:], timeoutms)
}

//

// eventsListener listens for events for multiple files as a single system call.
//
// One OS thread is needed for all the events. This is more efficient on single
// core system.
type eventsListener struct {
	wg sync.WaitGroup

	mu      sync.Mutex
	closing bool
	// file descriptor of the epoll handle itself.
	epollFd int
	// pipes to wake up the loop()
	r, w *os.File
	// mapping of file descriptors to wait on with their corresponding channels.
	fds    map[int32]chan<- time.Time
	wakeUp <-chan time.Time
}

// init must be called on a fresh instance.
func (e *eventsListener) init() error {
	e.mu.Lock()
	if e.epollFd != 0 {
		// Was already initialized.
		e.mu.Unlock()
		return nil
	}
	e.closing = false
	var err error
	e.epollFd, err = syscall.EpollCreate(1)
	switch {
	case err == nil:
		break
	case err.Error() == "function not implemented":
		// Some arch (arm64) do not implement EpollCreate().
		if e.epollFd, err = syscall.EpollCreate1(0); err != nil {
			return err
		}
	default:
		return err
	}
	e.r, e.w, err = os.Pipe()
	e.fds = map[int32]chan<- time.Time{}
	// Only need epollIN. epollPRI has no effect on pipes.
	const flags = epollET | epollIN
	if err := e.addFdInner(e.r.Fd(), flags); err != nil {
		return err
	}
	wakeUp := make(chan time.Time)
	e.fds[int32(e.r.Fd())] = wakeUp
	e.wakeUp = wakeUp
	// The mutex is still held after this function exits, it's loop() that will
	// release the mutex.
	//
	// This forces loop() to be started before addFd() can be called by users.
	e.wg.Add(1)
	go e.loop()
	return nil
}

// stopLoop releases the epoll resource and terminates the polling loop.
//
// This is used in unit tests only.
func (e *eventsListener) stopLoop() error {
	e.mu.Lock()
	if e.epollFd == 0 {
		// Was already closed.
		e.mu.Unlock()
		return nil
	}
	if len(e.fds) != 1 {
		// Disallow closing if there is any listener, as wakeUpLoop() could
		// deadlock otherwise.
		e.mu.Unlock()
		return errors.New("cannot stop loop() while there's an active listener")
	}
	e.closing = true
	e.mu.Unlock()
	// Wake up the poller so it notices it should quit.
	e.wakeUpLoop(nil)
	// It is important to wait for the loop() function to exit, as EpollWait()
	// will not return on a closed file handle.
	e.wg.Wait()
	e.mu.Lock()
	_ = e.w.Close()
	e.w = nil
	_ = e.r.Close()
	e.r = nil
	err := syscall.Close(e.epollFd)
	e.epollFd = 0
	e.mu.Unlock()
	return err
}

// loop is the main event loop.
func (e *eventsListener) loop() {
	defer e.wg.Done()
	var events []syscall.EpollEvent
	type lookup struct {
		c     chan<- time.Time
		event epollEvent
	}
	var lookups []lookup
	for first := true; ; {
		if !first {
			e.mu.Lock()
		}
		if len(events) < len(e.fds) {
			events = make([]syscall.EpollEvent, len(e.fds))
		}
		closing := e.closing
		e.mu.Unlock()
		first = false

		if closing {
			// We're done.
			return
		}
		if len(events) == 0 {
			panic("internal error: there's should be at least one pipe")
		}

		// http://man7.org/linux/man-pages/man2/epoll_wait.2.html
		n, err := syscall.EpollWait(e.epollFd, events, -1)
		if n <= 0 {
			// -1 if an error occurred (EBADF, EFAULT, EINVAL) or the call was
			// interrupted by a signal (EINTR).
			// 0 is the timeout occurred. In this case there's no timeout specified.
			// Still handle this explicitly in case a timeout could be triggered by
			// external events, like system sleep.
			continue
		}
		if err != nil {
			// TODO(maruel): It'd be nice to be able to surface this.
			// This may cause a busy loop. Hopefully the user will notice and will
			// fix their code.
			// This can happen when removeFd() is called, in this case silently
			// ignore the error.
			continue
		}

		now := time.Now()
		// Create a look up table with the lock, so that then the channel can be
		// pushed to without the lock.
		if cap(lookups) < n {
			lookups = make([]lookup, 0, n)
		} else {
			lookups = lookups[:0]
		}

		e.mu.Lock()
		for _, ev := range events[:n] {
			ep := epollEvent(ev.Events)
			// Skip over file descriptors that are not present.
			c, ok := e.fds[ev.Fd]
			if !ok {
				// That's a race condition where the file descriptor was removed by
				// removeFd() but it still triggered. Ignore this event.
				continue
			}
			// Look at the event to determine if it's worth sending a pulse. It's
			// maybe not worth it.
			if ep&epollERR != 0 {
				// TODO(maruel): We should stop listening to this file descriptor.
				// Same for epollHUP.
			}
			// pipe and sockets trigger epollIN and epollOUT, but GPIO sysfs
			// triggers epollPRI.
			if ep&(epollPRI|epollIN|epollOUT) != 0 {
				lookups = append(lookups, lookup{c: c, event: ep})
			}
		}
		e.mu.Unlock()

		// Once the lock is released, send the timestamps.
		for _, t := range lookups {
			t.c <- now
		}
	}
}

// addFd starts listening to events generated by file descriptor |fd|.
//
// fd is the OS file descriptor. In practice, it must fit a int32 value. It
// works on pipes, sockets and sysfs objects like GPIO but not on standard
// files.
//
// c is the channel to send events to. Unbuffered channel will block the event
// loop, which may mean lost events, especially if multiple files are listened
// to simultaneously.
//
// flags is the events to listen to. No need to specify epollERR and epollHUP,
// they are sent anyway.
//
// addFd lazy initializes eventsListener if it was not initialized yet.
//
// It can fail due to various reasons, a few are:
//   ENOSPC: /proc/sys/fs/epoll/max_user_watches limit was exceeded
//   ENOMEM: No memory available
//   EPERM: fd is a regular file or directory
func (e *eventsListener) addFd(fd uintptr, c chan<- time.Time, flags epollEvent) error {
	if c == nil {
		return errors.New("fd: addFd requires a valid channel")
	}
	if err := e.init(); err != nil {
		return err
	}
	if err := e.addFdInner(fd, flags); err != nil {
		return err
	}
	e.mu.Lock()
	e.fds[int32(fd)] = c
	e.mu.Unlock()
	// Wake up the poller so it notices there's one new file.
	e.wakeUpLoop(nil)
	return nil
}

func (e *eventsListener) addFdInner(fd uintptr, flags epollEvent) error {
	ev := syscall.EpollEvent{Events: uint32(flags), Fd: int32(fd)}
	return syscall.EpollCtl(e.epollFd, epollCTLAdd, int(fd), &ev)
}

// removeFd stop listening to events on this file descriptor.
func (e *eventsListener) removeFd(fd uintptr) error {
	if err := syscall.EpollCtl(e.epollFd, epollCTLDel, int(fd), nil); err != nil {
		return err
	}
	e.mu.Lock()
	delete(e.fds, int32(fd))
	e.mu.Unlock()
	// Wake up the poller so it notices there's one less file.
	e.wakeUpLoop(nil)
	return nil
}

// wakeUpLoop wakes up the poller and waits for it.
//
// Must not be called with the lock held.
func (e *eventsListener) wakeUpLoop(c <-chan time.Time) time.Time {
	e.mu.Lock()
	running := e.epollFd != 0
	e.mu.Unlock()
	if !running {
		// The loop is not running, nothing to wake up.
		return time.Now()
	}
	// TODO(maruel): Figure out a way to wake up that doesn't require emptying.
	var b [1]byte
	_, _ = e.w.Write(b[:])
	var t time.Time
	if c != nil {
		// To prevent deadlock, also empty c.
		for {
			select {
			case <-c:
			case t = <-e.wakeUp:
				goto out
			}
		}
	out:
	} else {
		t = <-e.wakeUp
	}
	// Don't forget to empty the pipe. Sadly, this will wake up the loop a second
	// time.
	_, _ = e.r.Read(b[:])
	return t
}

// events is the global events listener.
//
// It uses a single global goroutine lazily initialized to call
// syscall.EpollWait() to listen to many file descriptors at once.
var events eventsListener
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"github.com/subutai-io/base/agent/config"
	"github.com/subutai-io/base/agent/lib/net/p2p"
	"github.com/subutai-io/base/agent/log"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func P2P(c, d, u, l, p bool, args []string) {
	if c {
		if len(args) > 8 {
			p2p.Create(args[4], args[5], args[6], args[7], args[8])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if u {
		if len(args) > 6 {
			p2p.UpdateKey(args[4], args[5], args[6])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if d {
		if len(args) > 4 {
			p2p.Remove(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if p {
		if len(args) > 4 {
			p2p.Peers(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if l {
		p2p.Print()
	}
}

func LxcManagementNetwork(args []string) {
	if len(args) < 3 {
		log.Error("Not enough arguments")
	}
	switch args[2] {
	case "-v", "--listvnimap":
		displayVNIMap()
	case "-r", "--removetunnel":
		removeTunnel(args[3])
	case "-T", "--creategateway":
		return
	case "-M", "--removevni":
		return
	case "-E", "--reservevni":
		reserveVNI(args[3], args[4], args[5])
	case "-m", "--createvnimap":
		createVNIMap(args[3], args[4], args[5], args[6])
	case "-c", "--createtunnel":
		createTunnel(args[3], args[4], args[5])
	case "-l", "--listtunnel":
		listTunnel()
	case "-Z", "--vniop":
		switch args[3] {
		case "deleteall":
			return
		case "delete":
			return
		case "list":
			listVNI()
		}
	}
}

func createFile() {
	if _, err := os.Stat(config.Agent.DataPrefix + "var/subutai-network/"); os.IsNotExist(err) {
		log.Check(log.FatalLevel, "Creating network data folder", os.MkdirAll(config.Agent.DataPrefix+"var/subutai-network", 0755))
	}
	if _, err := os.Stat(config.Agent.DataPrefix + "var/subutai-network/vni_reserve"); os.IsNotExist(err) {
		_, err = os.Create(config.Agent.DataPrefix + "var/subutai-network/vni_reserve")
		log.Check(log.ErrorLevel, "Creating VNI file", err)
	}
}

func listVNI() {
	createFile()
	f, err := ioutil.ReadFile(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve")
	log.Check(log.ErrorLevel, "Reading "+config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", err)

	for _, v := range strings.Split(string(f), "\n") {
		s := strings.Fields(v)
		if len(s) > 2 {
			fmt.Printf("%s,%s,%s\n", s[0], s[1], s[2])
		}
	}
}

func checkVNI(vni, vlan, envid string) {
	f, _ := ioutil.ReadFile(config.Agent.DataPrefix + "var/subutai-network/vni_reserve")
	lines := strings.Split(string(f), "\n")
	for _, v := range lines {
		if v == vni+" "+vlan+" "+envid {
			log.Error("Reservation already exist")
		}
	}
}

func reserveVNI(vni, vlan, envid string) {
	createFile()
	checkVNI(vni, vlan, envid)
	f, err := os.OpenFile(config.Agent.DataPrefix+"var/subutai-network/vni_reserve", os.O_APPEND|os.O_WRONLY, 0600)
	log.Check(log.ErrorLevel, "Appending line", err)

	defer f.Close()

	if _, err = f.WriteString(vni + " " + vlan + " " + envid + "\n"); err != nil {
		log.Error("Writing reserver")
	}
}

func listTunnel() {
	println("List of Tunnels\n--------")
	ret, err := exec.Command("ovs-vsctl", "show").CombinedOutput()
	log.Check(log.FatalLevel, "Getting OVS interfaces list", err)
	ports := strings.Split(string(ret), "\n")

	for k, port := range ports {
		if strings.Contains(string(port), "remote_ip") {
			iface := strings.Fields(ports[k-2])
			tunnel := strings.Trim(iface[1], "\"")
			addr := strings.Fields(port)
			fmt.Println(tunnel + "-" + strings.Trim(strings.Trim(addr[3], "remote_ip="), "\","))
		}
	}

}

func createTunnel(tunnel, addr, tunType string) {
	ifTunExist(tunnel)

	log.Check(log.FatalLevel, "Creating tunnel port",
		exec.Command("ovs-vsctl", "--may-exist", "add-port", "wan", tunnel, "--", "set", "interface", tunnel, "type="+tunType,
			"options:stp_enable=true", "options:key=flow", "options:remote_ip="+addr).Run())
}

func ifTunExist(name string) {
	ret, err := exec.Command("ovs-vsctl", "list-ports", "wan").CombinedOutput()
	log.Check(log.FatalLevel, "Getting port list", err)
	ports := strings.Split(string(ret), "\n")

	for _, port := range ports {
		if port == name {
			log.Error("Tunnel port " + name + " is already exists")
		}
	}
}

func displayVNIMap() {
	// tunnel1	8880164	100	04c088b9-e2f5-40b3-bd6c-2305b9a88058
	ret, err := exec.Command("ovs-vsctl", "show").CombinedOutput()
	log.Check(log.FatalLevel, "Getting OVS interfaces list", err)
	ports := strings.Split(string(ret), "\n")

	for k, port := range ports {
		if strings.Contains(string(port), "env") {
			iface := strings.Fields(ports[k-2])
			tunname := strings.Trim(iface[1], "\"")
			tag := strings.Fields(ports[k-3])[1]
			addr := strings.Fields(port)
			// ip := strings.Trim(strings.Trim(addr[3], "remote_ip="), "\",")
			key := strings.Trim(strings.Trim(addr[2], "key="), "\",")
			env := strings.Trim(strings.Trim(addr[1], "{env="), "\",")
			fmt.Println(tunname + " " + key + " " + tag + " " + env)
		}
	}
}

func createVNIMap(tunnel, vni, vlan, envid string) {
	log.Check(log.FatalLevel, "MakeVNIMap set interface: ",
		exec.Command("ovs-vsctl", "--if-exists", "set", "interface", tunnel, "options:key="+vni, "options:env="+envid).Run())
	log.Check(log.FatalLevel, "MakeVNIMap set port: ",
		exec.Command("ovs-vsctl", "--if-exists", "set", "port", tunnel, "tag="+vlan).Run())

}

func removeTunnel(tunnel string) {
	log.Check(log.WarnLevel, "Removing port "+tunnel,
		exec.Command("ovs-vsctl", "--if-exists", "del-port", tunnel).Run())
}

func ClearVlan(vlan string) {
	var lines []string
	f, err := ioutil.ReadFile(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve")
	if !log.Check(log.DebugLevel, "Reading "+config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", err) {
		lines = strings.Split(string(f), "\n")
		for k, v := range lines {
			s := strings.Fields(v)
			if len(s) > 2 && s[1] == vlan {
				p2p.Remove(s[2])
				lines[k] = ""
			}
		}
	}
	err = ioutil.WriteFile(config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", []byte(strings.Join(lines, "\n")), 0744)
	log.Check(log.WarnLevel, "config.Agent.DataPrefix + /var/subutai-network/vni_reserve delete vni", err)
}
<commit_msg>Added removing tunnel to cleanup binding<commit_after>package lib

import (
	"fmt"
	"github.com/subutai-io/base/agent/config"
	"github.com/subutai-io/base/agent/lib/net/p2p"
	"github.com/subutai-io/base/agent/log"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func P2P(c, d, u, l, p bool, args []string) {
	if c {
		if len(args) > 8 {
			p2p.Create(args[4], args[5], args[6], args[7], args[8])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if u {
		if len(args) > 6 {
			p2p.UpdateKey(args[4], args[5], args[6])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if d {
		if len(args) > 4 {
			p2p.Remove(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if p {
		if len(args) > 4 {
			p2p.Peers(args[4])
		} else {
			fmt.Println("Wrong usage")
		}
	} else if l {
		p2p.Print()
	}
}

func LxcManagementNetwork(args []string) {
	if len(args) < 3 {
		log.Error("Not enough arguments")
	}
	switch args[2] {
	case "-v", "--listvnimap":
		displayVNIMap()
	case "-r", "--removetunnel":
		removeTunnel(args[3])
	case "-T", "--creategateway":
		return
	case "-M", "--removevni":
		return
	case "-E", "--reservevni":
		reserveVNI(args[3], args[4], args[5])
	case "-m", "--createvnimap":
		createVNIMap(args[3], args[4], args[5], args[6])
	case "-c", "--createtunnel":
		createTunnel(args[3], args[4], args[5])
	case "-l", "--listtunnel":
		listTunnel()
	case "-Z", "--vniop":
		switch args[3] {
		case "deleteall":
			return
		case "delete":
			return
		case "list":
			listVNI()
		}
	}
}

func createFile() {
	if _, err := os.Stat(config.Agent.DataPrefix + "var/subutai-network/"); os.IsNotExist(err) {
		log.Check(log.FatalLevel, "Creating network data folder", os.MkdirAll(config.Agent.DataPrefix+"var/subutai-network", 0755))
	}
	if _, err := os.Stat(config.Agent.DataPrefix + "var/subutai-network/vni_reserve"); os.IsNotExist(err) {
		_, err = os.Create(config.Agent.DataPrefix + "var/subutai-network/vni_reserve")
		log.Check(log.ErrorLevel, "Creating VNI file", err)
	}
}

func listVNI() {
	createFile()
	f, err := ioutil.ReadFile(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve")
	log.Check(log.ErrorLevel, "Reading "+config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", err)

	for _, v := range strings.Split(string(f), "\n") {
		s := strings.Fields(v)
		if len(s) > 2 {
			fmt.Printf("%s,%s,%s\n", s[0], s[1], s[2])
		}
	}
}

func checkVNI(vni, vlan, envid string) {
	f, _ := ioutil.ReadFile(config.Agent.DataPrefix + "var/subutai-network/vni_reserve")
	lines := strings.Split(string(f), "\n")
	for _, v := range lines {
		if v == vni+" "+vlan+" "+envid {
			log.Error("Reservation already exist")
		}
	}
}

func reserveVNI(vni, vlan, envid string) {
	createFile()
	checkVNI(vni, vlan, envid)
	f, err := os.OpenFile(config.Agent.DataPrefix+"var/subutai-network/vni_reserve", os.O_APPEND|os.O_WRONLY, 0600)
	log.Check(log.ErrorLevel, "Appending line", err)

	defer f.Close()

	if _, err = f.WriteString(vni + " " + vlan + " " + envid + "\n"); err != nil {
		log.Error("Writing reserver")
	}
}

func listTunnel() {
	println("List of Tunnels\n--------")
	ret, err := exec.Command("ovs-vsctl", "show").CombinedOutput()
	log.Check(log.FatalLevel, "Getting OVS interfaces list", err)
	ports := strings.Split(string(ret), "\n")

	for k, port := range ports {
		if strings.Contains(string(port), "remote_ip") {
			iface := strings.Fields(ports[k-2])
			tunnel := strings.Trim(iface[1], "\"")
			addr := strings.Fields(port)
			fmt.Println(tunnel + "-" + strings.Trim(strings.Trim(addr[3], "remote_ip="), "\","))
		}
	}

}

func createTunnel(tunnel, addr, tunType string) {
	ifTunExist(tunnel)

	log.Check(log.FatalLevel, "Creating tunnel port",
		exec.Command("ovs-vsctl", "--may-exist", "add-port", "wan", tunnel, "--", "set", "interface", tunnel, "type="+tunType,
			"options:stp_enable=true", "options:key=flow", "options:remote_ip="+addr).Run())
}

func ifTunExist(name string) {
	ret, err := exec.Command("ovs-vsctl", "list-ports", "wan").CombinedOutput()
	log.Check(log.FatalLevel, "Getting port list", err)
	ports := strings.Split(string(ret), "\n")

	for _, port := range ports {
		if port == name {
			log.Error("Tunnel port " + name + " is already exists")
		}
	}
}

func displayVNIMap() {
	// tunnel1	8880164	100	04c088b9-e2f5-40b3-bd6c-2305b9a88058
	ret, err := exec.Command("ovs-vsctl", "show").CombinedOutput()
	log.Check(log.FatalLevel, "Getting OVS interfaces list", err)
	ports := strings.Split(string(ret), "\n")

	for k, port := range ports {
		if strings.Contains(string(port), "env") {
			iface := strings.Fields(ports[k-2])
			tunname := strings.Trim(iface[1], "\"")
			tag := strings.Fields(ports[k-3])[1]
			addr := strings.Fields(port)
			// ip := strings.Trim(strings.Trim(addr[3], "remote_ip="), "\",")
			key := strings.Trim(strings.Trim(addr[2], "key="), "\",")
			env := strings.Trim(strings.Trim(addr[1], "{env="), "\",")
			fmt.Println(tunname + " " + key + " " + tag + " " + env)
		}
	}
}

func createVNIMap(tunnel, vni, vlan, envid string) {
	log.Check(log.FatalLevel, "MakeVNIMap set interface: ",
		exec.Command("ovs-vsctl", "--if-exists", "set", "interface", tunnel, "options:key="+vni, "options:env="+envid).Run())
	log.Check(log.FatalLevel, "MakeVNIMap set port: ",
		exec.Command("ovs-vsctl", "--if-exists", "set", "port", tunnel, "tag="+vlan).Run())

}

func removeTunnel(tunnel string) {
	log.Check(log.WarnLevel, "Removing port "+tunnel,
		exec.Command("ovs-vsctl", "--if-exists", "del-port", tunnel).Run())
}

func delTunById(envId string) {
	ret, err := exec.Command("ovs-vsctl", "show").CombinedOutput()
	log.Check(log.FatalLevel, "Getting OVS interfaces list", err)
	ports := strings.Split(string(ret), "\n")

	for k, port := range ports {
		if strings.Contains(string(port), envId) {
			tunnel := strings.Split(ports[k-2], "\"")[1]
			log.Check(log.WarnLevel, "Removing port "+tunnel,
				exec.Command("ovs-vsctl", "--if-exists", "del-port", tunnel).Run())
		}
	}

}

func ClearVlan(vlan string) {
	var lines []string
	f, err := ioutil.ReadFile(config.Agent.DataPrefix + "/var/subutai-network/vni_reserve")
	if !log.Check(log.DebugLevel, "Reading "+config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", err) {
		lines = strings.Split(string(f), "\n")
		for k, v := range lines {
			s := strings.Fields(v)
			if len(s) > 2 && s[1] == vlan {
				delTunById(s[2])
				p2p.Remove(s[2])
				lines[k] = ""
			}
		}
	}
	err = ioutil.WriteFile(config.Agent.DataPrefix+"/var/subutai-network/vni_reserve", []byte(strings.Join(lines, "\n")), 0744)
	log.Check(log.WarnLevel, "config.Agent.DataPrefix + /var/subutai-network/vni_reserve delete vni", err)
}
<|endoftext|>