aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--extras/libmemif/README.md70
-rw-r--r--extras/libmemif/adapter.go54
-rw-r--r--extras/libmemif/examples/gopacket/gopacket.go362
-rw-r--r--extras/libmemif/examples/icmp-responder/icmp-responder.go7
-rw-r--r--extras/libmemif/packethandle.go150
6 files changed, 612 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index cfc99f7..7bed92c 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ install:
extras:
@cd extras/libmemif/examples/raw-data && go build -v
@cd extras/libmemif/examples/icmp-responder && go build -v
+ @cd extras/libmemif/examples/gopacket && go build -v
clean:
@rm -f cmd/binapi-generator/binapi-generator
diff --git a/extras/libmemif/README.md b/extras/libmemif/README.md
index 39a6d58..854aa96 100644
--- a/extras/libmemif/README.md
+++ b/extras/libmemif/README.md
@@ -17,6 +17,8 @@ locations, execute:
```
$ git clone https://gerrit.fd.io/r/vpp
$ cd vpp/extras/libmemif
+$ ./bootstrap
+$ ./configure
$ make install
```
@@ -101,6 +103,14 @@ Do not touch memif after it was closed, let garbage collector to remove
the `Memif` instance. In the end, `Cleanup()` will also ensure that all
active memif interfaces are closed before the cleanup finalizes.
+To use libmemif with `google/gopacket`, simply call `Memif.NewPacketHandle()`
+to create `google/gopacket/PacketDataSource` from memif queue. After this you
+can use gopacket API to read from `MemifPacketHandle` as normal. You can pass
+optional `rxCount` when creating the packet handle and then when reading data,
+handle will try to read more packets at once and cache them for next iteration.
+Handle also includes convenience method `MemifPacketHandle.WritePacketData()`
+that is simply calling 1 `Memif.TxBurst()` for provided data.
+
### Examples
**Go-libmemif** ships with two simple examples demonstrating the usage
@@ -144,9 +154,10 @@ used to decode and construct packets.
The appropriate VPP configuration for the opposite memif is:
```
-vpp$ create memif id 1 socket /tmp/icmp-responder-example slave secret secret
-vpp$ set int state memif0/1 up
-vpp$ set int ip address memif0/1 192.168.1.2/24
+vpp$ create memif socket id 1 filename /tmp/icmp-responder-example
+vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
+vpp$ set int state memif1/1 up
+vpp$ set int ip address memif1/1 192.168.1.2/24
```
To start the example, simply type:
@@ -182,4 +193,55 @@ vpp$ sh ip arp
It was actually converted to an ARP request. This is a VPP
specific feature common to all interface types.
-Stop the example with an interrupt signal (^C). \ No newline at end of file
+Stop the example with an interrupt signal (^C).
+
+#### GoPacket ICMP Responder
+
+*gopacket* is a simple example showing how to answer APR and ICMP echo
+requests through a memif interface. This example is mostly identical
+to icmp-responder example, but it is using MemifPacketHandle API to
+read and write packets using gopacket API.
+
+The appropriate VPP configuration for the opposite memif is:
+```
+vpp$ create memif socket id 1 filename /tmp/gopacket-example
+vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
+vpp$ set int state memif1/1 up
+vpp$ set int ip address memif1/1 192.168.1.2/24
+```
+
+To start the example, simply type:
+```
+root$ ./gopacket
+```
+
+gopacket needs to be run as root so that it can access the socket
+created by VPP.
+
+Normally, the memif interface is in the master mode. Pass CLI flag "--slave"
+to create memif in the slave mode:
+```
+root$ ./gopacket --slave
+```
+
+Don't forget to put the opposite memif into the master mode in that case.
+
+To verify the connection, run:
+```
+vpp$ ping 192.168.1.1
+64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
+64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
+64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
+64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms
+
+Statistics: 5 sent, 4 received, 20% packet loss
+vpp$ sh ip arp
+Time IP4 Flags Ethernet Interface
+68.5648 192.168.1.1 D aa:aa:aa:aa:aa:aa memif0/1
+```
+
+*Note*: it is expected that the first ping is shown as lost.
+ It was actually converted to an ARP request. This is a VPP
+ specific feature common to all interface types.
+
+Stop the example with an interrupt signal (^C).
diff --git a/extras/libmemif/adapter.go b/extras/libmemif/adapter.go
index 6058c94..a74c5cf 100644
--- a/extras/libmemif/adapter.go
+++ b/extras/libmemif/adapter.go
@@ -55,11 +55,10 @@ typedef struct
uint8_t num_s2m_rings;
uint8_t num_m2s_rings;
uint16_t buffer_size;
- memif_log2_ring_size_t log2_ring_size;
+ uint8_t log2_ring_size;
uint8_t is_master;
- memif_interface_id_t interface_id;
+ uint32_t interface_id;
char *interface_name;
- char *instance_name;
memif_interface_mode_t mode;
} govpp_memif_conn_args_t;
@@ -124,11 +123,6 @@ govpp_memif_create (memif_conn_handle_t *conn, govpp_memif_conn_args_t *go_args,
strncpy ((char *)args.interface_name, go_args->interface_name,
sizeof(args.interface_name) - 1);
}
- if (go_args->instance_name != NULL)
- {
- strncpy ((char *)args.instance_name, go_args->instance_name,
- sizeof (args.instance_name) - 1);
- }
args.mode = go_args->mode;
return memif_create(conn, &args, govpp_on_connect_callback,
@@ -202,10 +196,10 @@ govpp_get_tx_queue_details (govpp_memif_details_t *md, int index)
// Copy packet data into the selected buffer.
static void
-govpp_copy_packet_data(memif_buffer_t *buffers, int index, void *data, uint32_t size)
+govpp_copy_packet_data(memif_buffer_t *buffers, int index, void *data, uint16_t size)
{
- buffers[index].data_len = (size > buffers[index].buffer_len ? buffers[index].buffer_len : size);
- memcpy(buffers[index].data, data, (size_t)buffers[index].data_len);
+ buffers[index].len = (size > buffers[index].len ? buffers[index].len : size);
+ memcpy(buffers[index].data, data, (size_t)buffers[index].len);
}
// Get packet data from the selected buffer.
@@ -213,7 +207,7 @@ govpp_copy_packet_data(memif_buffer_t *buffers, int index, void *data, uint32_t
static void *
govpp_get_packet_data(memif_buffer_t *buffers, int index, int *size)
{
- *size = (int)buffers[index].data_len;
+ *size = (int)buffers[index].len;
return buffers[index].data;
}
@@ -350,6 +344,7 @@ type Memif struct {
queueIntCh []chan struct{} // per RX queue interrupt channel
// Rx/Tx queues
+ ringSize int // number of items in each ring
stopQPollFd int // event file descriptor used to stop pollRxQueue-s
wg sync.WaitGroup // wait group for all pollRxQueue-s
rxQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each RX queue
@@ -444,11 +439,11 @@ func Init(appName string) error {
// Initialize C-libmemif.
var errCode int
if appName == "" {
- errCode = int(C.memif_init(nil, nil))
+ errCode = int(C.memif_init(nil, nil, nil, nil))
} else {
appName := C.CString(appName)
defer C.free(unsafe.Pointer(appName))
- errCode = int(C.memif_init(nil, appName))
+ errCode = int(C.memif_init(nil, appName, nil, nil))
}
err := getMemifError(errCode)
if err != nil {
@@ -517,11 +512,17 @@ func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Mem
log.WithField("ifName", config.IfName).Debug("Creating a new memif interface")
+ log2RingSize := config.Log2RingSize
+ if log2RingSize == 0 {
+ log2RingSize = 10
+ }
+
// Create memif-wrapper for Go-libmemif.
memif = &Memif{
MemifMeta: config.MemifMeta,
callbacks: &MemifCallbacks{},
ifIndex: context.nextMemifIndex,
+ ringSize: 1 << log2RingSize,
}
// Initialize memif callbacks.
@@ -547,17 +548,12 @@ func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Mem
defer C.free(unsafe.Pointer(args.socket_filename))
}
// - interface ID
- args.interface_id = C.memif_interface_id_t(config.ConnID)
+ args.interface_id = C.uint32_t(config.ConnID)
// - interface name
if config.IfName != "" {
args.interface_name = C.CString(config.IfName)
defer C.free(unsafe.Pointer(args.interface_name))
}
- // - instance name
- if config.InstanceName != "" {
- args.instance_name = C.CString(config.InstanceName)
- defer C.free(unsafe.Pointer(args.instance_name))
- }
// - mode
switch config.Mode {
case IfModeEthernet:
@@ -587,7 +583,7 @@ func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Mem
// - buffer size
args.buffer_size = C.uint16_t(config.BufferSize)
// - log_2(ring size)
- args.log2_ring_size = C.memif_log2_ring_size_t(config.Log2RingSize)
+ args.log2_ring_size = C.uint8_t(config.Log2RingSize)
// Create memif in C-libmemif.
errCode := C.govpp_memif_create(&memif.cHandle, args, unsafe.Pointer(uintptr(memif.ifIndex)))
@@ -757,7 +753,7 @@ func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint1
// Allocate ring slots.
cQueueID := C.uint16_t(queueID)
errCode := C.memif_buffer_alloc(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount),
- &allocated, C.uint16_t(bufSize))
+ &allocated, C.uint32_t(bufSize))
err = getMemifError(int(errCode))
if err == ErrNoBufRing {
// Not enough ring slots, <count> will be less than bufCount.
@@ -770,7 +766,7 @@ func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint1
// Copy packet data into the buffers.
for i := 0; i < int(allocated); i++ {
packetData := unsafe.Pointer(&packets[i][0])
- C.govpp_copy_packet_data(pb.buffers, C.int(i), packetData, C.uint32_t(len(packets[i])))
+ C.govpp_copy_packet_data(pb.buffers, C.int(i), packetData, C.uint16_t(len(packets[i])))
}
errCode = C.memif_tx_burst(memif.cHandle, cQueueID, pb.buffers, allocated, &sentCount)
@@ -793,7 +789,6 @@ func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint1
// Rx queue.
func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketData, err error) {
var recvCount C.uint16_t
- var freed C.uint16_t
if count == 0 {
return packets, nil
@@ -836,7 +831,9 @@ func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketDat
packets = append(packets, C.GoBytes(packetData, packetSize))
}
- errCode = C.memif_buffer_free(memif.cHandle, cQueueID, pb.buffers, recvCount, &freed)
+ if recvCount > 0 {
+ errCode = C.memif_refill_queue(memif.cHandle, cQueueID, recvCount, 0)
+ }
err = getMemifError(int(errCode))
if err != nil {
// Throw away packets to avoid duplicities.
@@ -895,6 +892,13 @@ func (memif *Memif) initQueues() error {
// Initialize Rx/Tx packet buffers.
for i = 0; i < len(details.RxQueues); i++ {
memif.rxQueueBufs = append(memif.rxQueueBufs, CPacketBuffers{})
+ if !memif.IsMaster {
+ errCode := C.memif_refill_queue(memif.cHandle, C.uint16_t(i), C.uint16_t(memif.ringSize-1), 0)
+ err = getMemifError(int(errCode))
+ if err != nil {
+ log.Warn(err.Error())
+ }
+ }
}
for i = 0; i < len(details.TxQueues); i++ {
memif.txQueueBufs = append(memif.txQueueBufs, CPacketBuffers{})
diff --git a/extras/libmemif/examples/gopacket/gopacket.go b/extras/libmemif/examples/gopacket/gopacket.go
new file mode 100644
index 0000000..ee452a2
--- /dev/null
+++ b/extras/libmemif/examples/gopacket/gopacket.go
@@ -0,0 +1,362 @@
+// gopacket is a simple example showing how to answer APR and ICMP echo
+// requests through a memif interface. This example is mostly identical
+// to icmp-responder example, but it is using MemifPacketHandle API to
+// read and write packets using gopacket API.
+//
+// The appropriate VPP configuration for the opposite memif is:
+// vpp$ create memif socket id 1 filename /tmp/gopacket-example
+// vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
+// vpp$ set int state memif1/1 up
+// vpp$ set int ip address memif1/1 192.168.1.2/24
+//
+// To start the example, simply type:
+// root$ ./gopacket
+//
+// gopacket needs to be run as root so that it can access the socket
+// created by VPP.
+//
+// Normally, the memif interface is in the master mode. Pass CLI flag "--slave"
+// to create memif in the slave mode:
+// root$ ./gopacket --slave
+//
+// Don't forget to put the opposite memif into the master mode in that case.
+//
+// To verify the connection, run:
+// vpp$ ping 192.168.1.1
+// 64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
+// 64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
+// 64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
+// 64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms
+//
+// Statistics: 5 sent, 4 received, 20% packet loss
+// vpp$ sh ip arp
+// Time IP4 Flags Ethernet Interface
+// 68.5648 192.168.1.1 D aa:aa:aa:aa:aa:aa memif0/1
+//
+// Note: it is expected that the first ping is shown as lost. It was actually
+// converted to an ARP request. This is a VPP feature common to all interface
+// types.
+//
+// Stop the example with an interrupt signal.
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "os/signal"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "git.fd.io/govpp.git/extras/libmemif"
+)
+
+var (
+ // Used to signalize interrupt goroutines to stop
+ stopCh chan struct{}
+
+ // MAC address assigned to the memif interface.
+ hwAddr net.HardwareAddr
+
+ // IPAddress assigned to the memif interface.
+ ipAddr net.IP
+
+ // ErrUnhandledPacket is thrown and printed when an unexpected packet is received.
+ ErrUnhandledPacket = errors.New("received an unhandled packet")
+)
+
+// OnConnect is called when a memif connection gets established.
+func OnConnect(memif *libmemif.Memif) (err error) {
+ // Use Memif.GetDetails to get the number of queues.
+ details, err := memif.GetDetails()
+ if err != nil {
+ fmt.Printf("libmemif.GetDetails() error: %v\n", err)
+ return
+ }
+
+ fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
+ stopCh = make(chan struct{})
+
+ // Start a separate go routine for each RX queue.
+ // (memif queue is a unit of parallelism for Rx/Tx).
+ // Beware: the number of queues created may be lower than what was requested
+ // in MemifConfiguration (the master makes the final decision).
+ for _, queue := range details.RxQueues {
+ ch, err := memif.GetQueueInterruptChan(queue.QueueID)
+ if err != nil {
+ fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error %v\n", err)
+ continue
+ }
+
+ go CreateInterruptCallback(memif.NewPacketHandle(queue.QueueID, 10), ch, OnInterrupt)
+ }
+
+ return
+}
+
+// OnDisconnect is called when a memif connection is lost.
+func OnDisconnect(memif *libmemif.Memif) (err error) {
+ fmt.Printf("memif %s has been disconnected\n", memif.IfName)
+ // Stop all packet producers and consumers.
+ close(stopCh)
+ return nil
+}
+
+// OnInterrupt is called when interrupted
+func OnInterrupt(handle *libmemif.MemifPacketHandle) {
+ source := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
+ var responses []gopacket.Packet
+
+ // Process ICMP pings
+ for packet := range source.Packets() {
+ fmt.Println("Received new packet:")
+ fmt.Println(packet.Dump())
+
+ response, err := GeneratePacketResponse(packet)
+ if err != nil {
+ fmt.Printf("Failed to generate response: %v\n", err)
+ continue
+ }
+
+ fmt.Println("Sending response:")
+ fmt.Println(response.Dump())
+ responses = append(responses, response)
+ }
+
+ // Answer with ICMP pongs
+ for i, response := range responses {
+ err := handle.WritePacketData(response.Data())
+
+ switch err {
+ case io.EOF:
+ return
+ case nil:
+ fmt.Printf("Succesfully sent packet #%v %v\n", i, len(response.Data()))
+ default:
+ fmt.Printf("Got error while sending packet #%v %v\n", i, err)
+ }
+ }
+}
+
+// Creates user-friendly memif interrupt callback
+func CreateInterruptCallback(handle *libmemif.MemifPacketHandle, interruptCh <-chan struct{}, callback func(handle *libmemif.MemifPacketHandle)) {
+ for {
+ select {
+ case <-interruptCh:
+ callback(handle)
+ case <-stopCh:
+ handle.Close()
+ return
+ }
+ }
+}
+
+// GeneratePacketResponse returns an appropriate answer to an ARP request
+// or an ICMP echo request.
+func GeneratePacketResponse(packet gopacket.Packet) (response gopacket.Packet, err error) {
+ ethLayer := packet.Layer(layers.LayerTypeEthernet)
+ eth, ok := ethLayer.(*layers.Ethernet)
+ if !ok {
+ fmt.Println("Missing ETH layer.")
+ return nil, ErrUnhandledPacket
+ }
+
+ // Set up buffer and options for serialization.
+ buf := gopacket.NewSerializeBuffer()
+ opts := gopacket.SerializeOptions{
+ FixLengths: true,
+ ComputeChecksums: true,
+ }
+
+ switch eth.EthernetType {
+ case layers.EthernetTypeARP:
+ // Handle ARP request.
+ arpLayer := packet.Layer(layers.LayerTypeARP)
+ arp, ok := arpLayer.(*layers.ARP)
+ if !ok {
+ fmt.Println("Missing ARP layer.")
+ return nil, ErrUnhandledPacket
+ }
+
+ if arp.Operation != layers.ARPRequest {
+ fmt.Println("Not ARP request.")
+ return nil, ErrUnhandledPacket
+ }
+
+ fmt.Println("Received an ARP request.")
+
+ // Build packet layers.
+ ethResp := layers.Ethernet{
+ SrcMAC: hwAddr,
+ DstMAC: eth.SrcMAC,
+ EthernetType: layers.EthernetTypeARP,
+ }
+
+ arpResp := layers.ARP{
+ AddrType: layers.LinkTypeEthernet,
+ Protocol: layers.EthernetTypeIPv4,
+ HwAddressSize: 6,
+ ProtAddressSize: 4,
+ Operation: layers.ARPReply,
+ SourceHwAddress: []byte(hwAddr),
+ SourceProtAddress: []byte(ipAddr),
+ DstHwAddress: arp.SourceHwAddress,
+ DstProtAddress: arp.SourceProtAddress,
+ }
+
+ if err := gopacket.SerializeLayers(buf, opts, &ethResp, &arpResp); err != nil {
+ fmt.Println("SerializeLayers error: ", err)
+ return nil, ErrUnhandledPacket
+ }
+ case layers.EthernetTypeIPv4:
+ // Respond to ICMP request.
+ ipLayer := packet.Layer(layers.LayerTypeIPv4)
+ ipv4, ok := ipLayer.(*layers.IPv4)
+ if !ok {
+ fmt.Println("Missing IPv4 layer.")
+ return nil, ErrUnhandledPacket
+ }
+
+ if ipv4.Protocol != layers.IPProtocolICMPv4 {
+ fmt.Println("Not ICMPv4 protocol.")
+ return nil, ErrUnhandledPacket
+ }
+
+ icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
+ icmp, ok := icmpLayer.(*layers.ICMPv4)
+ if !ok {
+ fmt.Println("Missing ICMPv4 layer.")
+ return nil, ErrUnhandledPacket
+ }
+
+ if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
+ fmt.Println("Not ICMPv4 echo request.")
+ return nil, ErrUnhandledPacket
+ }
+
+ fmt.Println("Received an ICMPv4 echo request.")
+
+ // Build packet layers.
+ ethResp := layers.Ethernet{
+ SrcMAC: hwAddr,
+ DstMAC: eth.SrcMAC,
+ EthernetType: layers.EthernetTypeIPv4,
+ }
+
+ ipv4Resp := layers.IPv4{
+ Version: 4,
+ IHL: 5,
+ TOS: 0,
+ Id: 0,
+ Flags: 0,
+ FragOffset: 0,
+ TTL: 255,
+ Protocol: layers.IPProtocolICMPv4,
+ SrcIP: ipAddr,
+ DstIP: ipv4.SrcIP,
+ }
+
+ icmpResp := layers.ICMPv4{
+ TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
+ Id: icmp.Id,
+ Seq: icmp.Seq,
+ }
+
+ if err := gopacket.SerializeLayers(buf, opts, &ethResp, &ipv4Resp, &icmpResp, gopacket.Payload(icmp.Payload)); err != nil {
+ fmt.Println("SerializeLayers error: ", err)
+ return nil, ErrUnhandledPacket
+ }
+ default:
+ return nil, ErrUnhandledPacket
+ }
+
+ return gopacket.NewPacket(buf.Bytes(), layers.LayerTypeEthernet, gopacket.Default), nil
+}
+
+func main() {
+ fmt.Println("Starting 'gopacket' example...")
+ var err error
+
+ // Parse MAC address associated with memif interface
+ hwAddr, err = net.ParseMAC("aa:aa:aa:aa:aa:aa")
+ if err != nil {
+ fmt.Printf("Failed to parse the MAC address: %v/n", err)
+ return
+ }
+
+ // Parse IP address associated with memif interface
+ ip := net.ParseIP("192.168.1.1")
+ if ip != nil {
+ ipAddr = ip.To4()
+ }
+ if ipAddr == nil {
+ fmt.Println("Failed to parse the IP address")
+ return
+ }
+
+ // If run with the "--slave" option, create memif in the slave mode.
+ var isMaster = true
+ var appSuffix string
+ if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
+ isMaster = false
+ appSuffix = "-slave"
+ }
+
+ // Initialize libmemif first.
+ appName := "gopacket" + appSuffix
+ fmt.Println("Initializing libmemif as ", appName)
+ err = libmemif.Init(appName)
+ if err != nil {
+ fmt.Printf("libmemif.Init() error: %v\n", err)
+ return
+ }
+
+ // Schedule automatic cleanup.
+ defer libmemif.Cleanup()
+
+ // Prepare callbacks to use with the memif.
+ // The same callbacks could be used with multiple memifs.
+ // The first input argument (*libmemif.Memif) can be used to tell which
+ // memif the callback was triggered for.
+ memifCallbacks := &libmemif.MemifCallbacks{
+ OnConnect: OnConnect,
+ OnDisconnect: OnDisconnect,
+ }
+
+ // Prepare memif1 configuration.
+ memifConfig := &libmemif.MemifConfig{
+ MemifMeta: libmemif.MemifMeta{
+ IfName: "memif1",
+ ConnID: 1, // ConnectionID is an identifier used to match opposite memifs.
+ SocketFilename: "/tmp/gopacket-example", // Socket through which the opposite memifs will establish the connection.
+ Secret: "secret", // Secret used to authenticate the memif connection.
+ IsMaster: isMaster,
+ Mode: libmemif.IfModeEthernet,
+ },
+ MemifShmSpecs: libmemif.MemifShmSpecs{
+ NumRxQueues: 3, // NumQueues is the (configured!) number of queues for both Rx & Tx.
+ NumTxQueues: 3, // The actual number agreed during connection establishment may be smaller!
+ BufferSize: 2048,
+ Log2RingSize: 10,
+ },
+ }
+
+ fmt.Printf("Callbacks: %+v\n", memifCallbacks)
+ fmt.Printf("Config: %+v\n", memifConfig)
+
+ // Create memif1 interface.
+ memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
+ if err != nil {
+ fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
+ return
+ }
+
+ // Schedule automatic cleanup of the interface.
+ defer memif.Close()
+
+ // Wait until an interrupt signal is received.
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, os.Interrupt)
+ <-sigChan
+}
diff --git a/extras/libmemif/examples/icmp-responder/icmp-responder.go b/extras/libmemif/examples/icmp-responder/icmp-responder.go
index f9867f7..5e9f2e0 100644
--- a/extras/libmemif/examples/icmp-responder/icmp-responder.go
+++ b/extras/libmemif/examples/icmp-responder/icmp-responder.go
@@ -3,9 +3,10 @@
// and construct packets.
//
// The appropriate VPP configuration for the opposite memif is:
-// vpp$ create memif id 1 socket /tmp/icmp-responder-example slave secret secret
-// vpp$ set int state memif0/1 up
-// vpp$ set int ip address memif0/1 192.168.1.2/24
+// vpp$ create memif socket id 1 filename /tmp/icmp-responder-example
+// vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
+// vpp$ set int state memif1/1 up
+// vpp$ set int ip address memif1/1 192.168.1.2/24
//
// To start the example, simply type:
// root$ ./icmp-responder
diff --git a/extras/libmemif/packethandle.go b/extras/libmemif/packethandle.go
new file mode 100644
index 0000000..83bf90a
--- /dev/null
+++ b/extras/libmemif/packethandle.go
@@ -0,0 +1,150 @@
+// Copyright (c) 2017 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 libmemif
+
+import (
+ "github.com/google/gopacket"
+ "time"
+ "sync"
+ "io"
+)
+
+type memoizedPacket struct {
+ data RawPacketData
+ ci gopacket.CaptureInfo
+}
+
+type MemifPacketHandle struct {
+ memif *Memif
+ queueId uint8
+ rxCount uint16
+
+ // Used for caching packets when larger rxburst is called
+ packetQueue []*memoizedPacket
+
+ // Used for synchronization of read/write calls
+ readMu sync.Mutex
+ writeMu sync.Mutex
+ closeMu sync.Mutex
+ stop bool
+}
+
+// Create new GoPacket packet handle from libmemif queue. rxCount determines how many packets will be read
+// at once, minimum value is 1
+func (memif *Memif) NewPacketHandle(queueId uint8, rxCount uint16) *MemifPacketHandle {
+ if rxCount == 0 {
+ rxCount = 1
+ }
+
+ return &MemifPacketHandle{
+ memif: memif,
+ queueId: queueId,
+ rxCount: rxCount,
+ }
+}
+
+// Reads packet data from memif in bursts, based on previously configured rxCount parameterer. Then caches the
+// resulting packets and returns them 1 by 1 from this method until queue is empty then tries to call new rx burst
+// to read more data. If no data is returned, io.EOF error is thrown and caller should stop reading.
+func (handle *MemifPacketHandle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ handle.readMu.Lock()
+ defer handle.readMu.Unlock()
+
+ if handle.stop {
+ err = io.EOF
+ return
+ }
+
+ queueLen := len(handle.packetQueue)
+
+ if queueLen == 0 {
+ packets, burstErr := handle.memif.RxBurst(handle.queueId, handle.rxCount)
+ packetsLen := len(packets)
+
+ if burstErr != nil {
+ err = burstErr
+ return
+ }
+
+ if packetsLen == 0 {
+ err = io.EOF
+ return
+ }
+
+ handle.packetQueue = make([]*memoizedPacket, packetsLen)
+
+ for i, packet := range packets {
+ packetLen := len(packet)
+
+ handle.packetQueue[i] = &memoizedPacket{
+ data: []byte(packet),
+ ci: gopacket.CaptureInfo{
+ Timestamp: time.Now(),
+ CaptureLength: packetLen,
+ Length: packetLen,
+ },
+ }
+ }
+ }
+
+ packet := handle.packetQueue[0]
+ handle.packetQueue = handle.packetQueue[1:]
+ data = packet.data
+ ci = packet.ci
+
+ return
+}
+
+// Writes packet data to memif in burst of 1 packet. In case no packet is sent, this method throws io.EOF error and
+// called should stop trying to write packets.
+func (handle *MemifPacketHandle) WritePacketData(data []byte) (err error) {
+ handle.writeMu.Lock()
+ defer handle.writeMu.Unlock()
+
+ if handle.stop {
+ err = io.EOF
+ return
+ }
+
+ count, err := handle.memif.TxBurst(handle.queueId, []RawPacketData{data})
+
+ if err != nil {
+ return
+ }
+
+ if count == 0 {
+ err = io.EOF
+ }
+
+ return
+}
+
+// Waits for all read and write operations to finish and then prevents more from occurring. Handle can be closed only
+// once and then can never be opened again.
+func (handle *MemifPacketHandle) Close() {
+ handle.closeMu.Lock()
+ defer handle.closeMu.Unlock()
+
+ // wait for packet reader to stop
+ handle.readMu.Lock()
+ defer handle.readMu.Unlock()
+
+ // wait for packet writer to stop
+ handle.writeMu.Lock()
+ defer handle.writeMu.Unlock()
+
+ // stop reading and writing
+ handle.stop = true
+}