summaryrefslogtreecommitdiffstats
path: root/extras/gomemif/examples/bridge.go
diff options
context:
space:
mode:
Diffstat (limited to 'extras/gomemif/examples/bridge.go')
-rw-r--r--extras/gomemif/examples/bridge.go286
1 files changed, 286 insertions, 0 deletions
diff --git a/extras/gomemif/examples/bridge.go b/extras/gomemif/examples/bridge.go
new file mode 100644
index 00000000000..a192034e3be
--- /dev/null
+++ b/extras/gomemif/examples/bridge.go
@@ -0,0 +1,286 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+package main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/pkg/profile"
+ "memif"
+)
+
+func Disconnected(i *memif.Interface) error {
+ fmt.Println("Disconnected: ", i.GetName())
+
+ data, ok := i.GetPrivateData().(*interfaceData)
+ if !ok {
+ return fmt.Errorf("Invalid private data")
+ }
+ close(data.quitChan) // stop polling
+ close(data.errChan)
+ data.wg.Wait() // wait until polling stops, then continue disconnect
+
+ return nil
+}
+
+func Connected(i *memif.Interface) error {
+ fmt.Println("Connected: ", i.GetName())
+
+ data, ok := i.GetPrivateData().(*interfaceData)
+ if !ok {
+ return fmt.Errorf("Invalid private data")
+ }
+ data.errChan = make(chan error, 1)
+ data.quitChan = make(chan struct{}, 1)
+ data.wg.Add(1)
+
+ go func(errChan chan<- error, quitChan <-chan struct{}, wg *sync.WaitGroup) {
+ defer wg.Done()
+ // allocate packet buffer
+ pkt := make([]byte, 2048)
+ // get rx queue
+ rxq0, err := i.GetRxQueue(0)
+ if err != nil {
+ errChan <- err
+ return
+ }
+
+ // wait until both interfaces are connected
+ for !data.bri.IsConnected() {
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ // get bridged interfaces tx queue
+ txq0, err := data.bri.GetTxQueue(0)
+ if err != nil {
+ errChan <- err
+ return
+ }
+ for {
+ select {
+ case <-quitChan: // channel closed
+ return
+ default:
+ // read packet from shared memory
+ pktLen, err := rxq0.ReadPacket(pkt)
+ if pktLen > 0 {
+ // FIXME: prevent packet write if interface is disconencted
+ // write packet to shared memory
+ txq0.WritePacket(pkt[:pktLen])
+ } else if err != nil {
+ errChan <- err
+ return
+ }
+ }
+ }
+ }(data.errChan, data.quitChan, &data.wg)
+
+ return nil
+}
+
+type interfaceData struct {
+ errChan chan error
+ quitChan chan struct{}
+ wg sync.WaitGroup
+ // bridged interface
+ bri *memif.Interface
+}
+
+func interractiveHelp() {
+ fmt.Println("help - print this help")
+ fmt.Println("start - start connecting loop")
+ fmt.Println("show - print interface details")
+ fmt.Println("exit - exit the application")
+}
+
+func newMemifInterface(socket *memif.Socket, id uint32, isMaster bool, name string) (*memif.Interface, *interfaceData, error) {
+ data := &interfaceData{}
+ args := &memif.Arguments{
+ Id: id,
+ IsMaster: isMaster,
+ ConnectedFunc: Connected,
+ DisconnectedFunc: Disconnected,
+ PrivateData: data,
+ Name: name,
+ }
+
+ i, err := socket.NewInterface(args)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
+ }
+
+ // slave attempts to connect to control socket
+ // to handle control communication call socket.StartPolling()
+ if !i.IsMaster() {
+ fmt.Println(args.Name, ": Connecting to control socket...")
+ for !i.IsConnecting() {
+ err = i.RequestConnection()
+ if err != nil {
+ /* TODO: check for ECONNREFUSED errno
+ * if error is ECONNREFUSED it may simply mean that master
+ * interface is not up yet, use i.RequestConnection()
+ */
+ return nil, nil, fmt.Errorf("Faild to connect: ", err)
+ }
+ }
+ }
+
+ return i, data, nil
+}
+
+func printMemifInterfaceDetails(i *memif.Interface) {
+ fmt.Println(i.GetName(), ":")
+ fmt.Println("\trole: ", memif.RoleToString(i.IsMaster()))
+ fmt.Println("\tid: ", i.GetId())
+ link := "down"
+ if i.IsConnected() {
+ link = "up"
+ }
+ fmt.Println("\tlink: ", link)
+ fmt.Println("\tremote: ", i.GetRemoteName())
+ fmt.Println("\tpeer: ", i.GetPeerName())
+ if i.IsConnected() {
+ mc := i.GetMemoryConfig()
+ fmt.Println("queue pairs: ", mc.NumQueuePairs)
+ fmt.Println("ring size: ", (1 << mc.Log2RingSize))
+ fmt.Println("buffer size: ", mc.PacketBufferSize)
+ }
+}
+
+func main() {
+ memifErrChan := make(chan error)
+ exitChan := make(chan struct{})
+ var i0, i1 *memif.Interface
+ var d0, d1 *interfaceData
+
+ cpuprof := flag.String("cpuprof", "", "cpu profiling output file")
+ memprof := flag.String("memprof", "", "mem profiling output file")
+ role := flag.String("role", "slave", "interface role")
+ name := flag.String("name", "gomemif", "interface name")
+ socketName := flag.String("socket", "", "control socket filename")
+
+ flag.Parse()
+
+ // profiling options
+ if *cpuprof != "" {
+ defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
+ }
+ if *memprof != "" {
+ defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
+ }
+
+ // memif options
+ var isMaster bool
+ switch *role {
+ case "slave":
+ isMaster = false
+ case "master":
+ isMaster = true
+ default:
+ fmt.Println("Invalid role")
+ return
+ }
+
+ // create memif socket
+ socket, err := memif.NewSocket("gomemif_example", *socketName)
+ if err != nil {
+ fmt.Println("Failed to create socket: ", err)
+ return
+ }
+
+ i0, d0, err = newMemifInterface(socket, 0, isMaster, *name)
+ if err != nil {
+ fmt.Println(err)
+ goto exit
+ }
+
+ // TODO: update name
+ i1, d1, err = newMemifInterface(socket, 1, isMaster, *name)
+ if err != nil {
+ fmt.Println(err)
+ goto exit
+ }
+
+ // set up bridge
+ d0.bri = i1
+ d1.bri = i0
+
+ // user input goroutine
+ go func(exitChan chan<- struct{}) {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Println("GoMemif: Responder")
+ fmt.Println("-----------------------")
+ for {
+ fmt.Print("gomemif# ")
+ text, _ := reader.ReadString('\n')
+ // convert CRLF to LF
+ text = strings.Replace(text, "\n", "", -1)
+ switch text {
+ case "help":
+ interractiveHelp()
+ case "start":
+ // start polling for events on this socket
+ socket.StartPolling(memifErrChan)
+ case "show":
+ printMemifInterfaceDetails(i0)
+ printMemifInterfaceDetails(i1)
+ case "exit":
+ err = socket.StopPolling()
+ if err != nil {
+ fmt.Println("Failed to stop polling: ", err)
+ }
+ close(exitChan)
+ return
+ default:
+ fmt.Println("Unknown input")
+ }
+ }
+ }(exitChan)
+
+ // main loop
+ for {
+ select {
+ case <-exitChan:
+ goto exit
+ case err, ok := <-memifErrChan:
+ if ok {
+ fmt.Println(err)
+ }
+ case err, ok := <-d0.errChan:
+ if ok {
+ fmt.Println(err)
+ }
+ case err, ok := <-d1.errChan:
+ if ok {
+ fmt.Println(err)
+ }
+ default:
+ continue
+ }
+ }
+
+exit:
+ socket.Delete()
+ close(memifErrChan)
+}