gomemif: update to libmemif version 4.0
Type: improvement This patch provides: 1. interrupt mode support, 2. abstract socket support, 3. overriding responder example and divides it to two examples: -icmp_responder_cb -icmp_responder_poll Signed-off-by: Daniel Béreš <dberes@cisco.com> Change-Id: I99c86d053521760c457541fc596ed554f4077608
This commit is contained in:

committed by
Beno�t Ganne

parent
a58055d6b2
commit
82ec908acb
50
extras/gomemif/README.rst
Normal file
50
extras/gomemif/README.rst
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
.. _gomemif_doc:
|
||||||
|
|
||||||
|
Gomemif library
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Memif library implemented in Go. The package contains 3 examples: Bridge and ICMP responder in interrupt and polling mode.
|
||||||
|
|
||||||
|
setup and run
|
||||||
|
-------------
|
||||||
|
To Build all examples
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
bazel build //...
|
||||||
|
|
||||||
|
To Run ICMP responder in interrupt mode:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
DBGvpp# create interface memif id 0 master no-zero-copy
|
||||||
|
DBGvpp# set int ip addr memif0/0 192.168.1.2/24
|
||||||
|
DBGvpp# set int state memif0/0 up
|
||||||
|
|
||||||
|
bazel-bin/examples/linux_amd64_stripped/icmp_responder_cb
|
||||||
|
gomemif# start
|
||||||
|
|
||||||
|
DBGvpp# ping 192.168.1.1
|
||||||
|
|
||||||
|
To Run ICMP responder in polling mode:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
DBGvpp# create interface memif id 0 master no-zero-copy
|
||||||
|
DBGvpp# set int ip addr memif0/0 192.168.1.2/24
|
||||||
|
DBGvpp# set int state memif0/0 up
|
||||||
|
|
||||||
|
bazel-bin/examples/linux_amd64_stripped/icmp_responder_poll
|
||||||
|
gomemif# start
|
||||||
|
|
||||||
|
DBGvpp# ping 192.168.1.1
|
||||||
|
|
||||||
|
To Run Bridge:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
bazel-bin/examples/linux_amd64_stripped/bridge
|
||||||
|
gomemif# start
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -33,3 +33,8 @@ go_repository(
|
|||||||
importpath = "github.com/pkg/profile",
|
importpath = "github.com/pkg/profile",
|
||||||
commit = "acd64d450fd45fb2afa41f833f3788c8a7797219"
|
commit = "acd64d450fd45fb2afa41f833f3788c8a7797219"
|
||||||
)
|
)
|
||||||
|
go_repository(
|
||||||
|
name = "com_github_gopacket",
|
||||||
|
importpath = "github.com/google/gopacket",
|
||||||
|
commit = "3eaba08943250fd212520e5cff00ed808b8fc60a"
|
||||||
|
)
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
|
||||||
|
|
||||||
go_binary(
|
go_binary(
|
||||||
name = "responder",
|
name = "icmp_responder_poll",
|
||||||
srcs = ["responder.go"],
|
srcs = ["icmp_responder_poll.go"],
|
||||||
visibility = ["//visibility:public",],
|
visibility = ["//visibility:public",],
|
||||||
deps = [
|
deps = [
|
||||||
"//memif:memif",
|
"//memif:memif",
|
||||||
"@com_github_profile//:go_default_library",
|
"@com_github_profile//:go_default_library",
|
||||||
|
"@com_github_gopacket//layers:go_default_library",
|
||||||
|
"@com_github_gopacket//:go_default_library"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,3 +21,16 @@ go_binary(
|
|||||||
"@com_github_profile//:go_default_library",
|
"@com_github_profile//:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "icmp_responder_cb",
|
||||||
|
srcs = ["icmp_responder_cb.go"],
|
||||||
|
visibility = ["//visibility:public",],
|
||||||
|
deps = [
|
||||||
|
"//memif:memif",
|
||||||
|
"@com_github_profile//:go_default_library",
|
||||||
|
"@com_github_gopacket//layers:go_default_library",
|
||||||
|
"@com_github_gopacket//:go_default_library"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
326
extras/gomemif/examples/icmp_responder_cb.go
Normal file
326
extras/gomemif/examples/icmp_responder_cb.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,12 +21,16 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/profile"
|
|
||||||
"memif"
|
"memif"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/pkg/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Disconnected(i *memif.Interface) error {
|
func Disconnected(i *memif.Interface) error {
|
||||||
@ -78,8 +82,94 @@ func Connected(i *memif.Interface) error {
|
|||||||
// read packet from shared memory
|
// read packet from shared memory
|
||||||
pktLen, err := rxq0.ReadPacket(pkt)
|
pktLen, err := rxq0.ReadPacket(pkt)
|
||||||
if pktLen > 0 {
|
if pktLen > 0 {
|
||||||
// write packet to shared memory
|
fmt.Printf("pktLen: %d\n", pktLen)
|
||||||
txq0.WritePacket(pkt[:pktLen])
|
gopkt := gopacket.NewPacket(pkt[:pktLen], layers.LayerTypeEthernet, gopacket.NoCopy)
|
||||||
|
etherLayer := gopkt.Layer(layers.LayerTypeEthernet)
|
||||||
|
if etherLayer.(*layers.Ethernet).EthernetType == layers.EthernetTypeARP {
|
||||||
|
|
||||||
|
rEth := layers.Ethernet{
|
||||||
|
SrcMAC: net.HardwareAddr{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa},
|
||||||
|
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||||
|
EthernetType: layers.EthernetTypeARP,
|
||||||
|
}
|
||||||
|
rArp := layers.ARP{
|
||||||
|
AddrType: layers.LinkTypeEthernet,
|
||||||
|
Protocol: layers.EthernetTypeIPv4,
|
||||||
|
HwAddressSize: 6,
|
||||||
|
ProtAddressSize: 4,
|
||||||
|
Operation: layers.ARPReply,
|
||||||
|
SourceHwAddress: []byte(net.HardwareAddr{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}),
|
||||||
|
SourceProtAddress: []byte("\xc0\xa8\x01\x01"),
|
||||||
|
DstHwAddress: []byte(net.HardwareAddr{0x02, 0xfe, 0x08, 0x88, 0x45, 0x7f}),
|
||||||
|
DstProtAddress: []byte("\xc0\xa8\x01\x02"),
|
||||||
|
}
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{
|
||||||
|
FixLengths: true,
|
||||||
|
ComputeChecksums: true,
|
||||||
|
}
|
||||||
|
gopacket.SerializeLayers(buf, opts, &rEth, &rArp)
|
||||||
|
// write packet to shared memory
|
||||||
|
txq0.WritePacket(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
if etherLayer.(*layers.Ethernet).EthernetType == layers.EthernetTypeIPv4 {
|
||||||
|
ipLayer := gopkt.Layer(layers.LayerTypeIPv4)
|
||||||
|
if ipLayer == nil {
|
||||||
|
fmt.Println("Missing IPv4 layer.")
|
||||||
|
|
||||||
|
}
|
||||||
|
ipv4, _ := ipLayer.(*layers.IPv4)
|
||||||
|
if ipv4.Protocol != layers.IPProtocolICMPv4 {
|
||||||
|
fmt.Println("Not ICMPv4 protocol.")
|
||||||
|
}
|
||||||
|
icmpLayer := gopkt.Layer(layers.LayerTypeICMPv4)
|
||||||
|
if icmpLayer == nil {
|
||||||
|
fmt.Println("Missing ICMPv4 layer.")
|
||||||
|
}
|
||||||
|
icmp, _ := icmpLayer.(*layers.ICMPv4)
|
||||||
|
if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
|
||||||
|
fmt.Println("Not ICMPv4 echo request.")
|
||||||
|
}
|
||||||
|
fmt.Println("Received an ICMPv4 echo request.")
|
||||||
|
|
||||||
|
// Build packet layers.
|
||||||
|
ethResp := layers.Ethernet{
|
||||||
|
DstMAC: net.HardwareAddr{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa},
|
||||||
|
//DstMAC: net.HardwareAddr{0x02, 0xfe, 0xa8, 0x77, 0xaf, 0x20},
|
||||||
|
SrcMAC: []byte(net.HardwareAddr{0x02, 0xfe, 0x08, 0x88, 0x45, 0x7f}),
|
||||||
|
|
||||||
|
EthernetType: layers.EthernetTypeIPv4,
|
||||||
|
}
|
||||||
|
ipv4Resp := layers.IPv4{
|
||||||
|
Version: 4,
|
||||||
|
IHL: 5,
|
||||||
|
TOS: 0,
|
||||||
|
Id: 0,
|
||||||
|
Flags: 0,
|
||||||
|
FragOffset: 0,
|
||||||
|
TTL: 255,
|
||||||
|
Protocol: layers.IPProtocolICMPv4,
|
||||||
|
SrcIP: []byte("\xc0\xa8\x01\x01"),
|
||||||
|
DstIP: []byte("\xc0\xa8\x01\x02"),
|
||||||
|
}
|
||||||
|
icmpResp := layers.ICMPv4{
|
||||||
|
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
|
||||||
|
Id: icmp.Id,
|
||||||
|
Seq: icmp.Seq,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up buffer and options for serialization.
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{
|
||||||
|
FixLengths: true,
|
||||||
|
ComputeChecksums: true,
|
||||||
|
}
|
||||||
|
gopacket.SerializeLayers(buf, opts, ðResp, &ipv4Resp, &icmpResp,
|
||||||
|
gopacket.Payload(icmp.Payload))
|
||||||
|
// write packet to shared memory
|
||||||
|
txq0.WritePacket(buf.Bytes())
|
||||||
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
return
|
return
|
@ -67,11 +67,22 @@ type Socket struct {
|
|||||||
interfaceList *list.List
|
interfaceList *list.List
|
||||||
ccList *list.List
|
ccList *list.List
|
||||||
epfd int
|
epfd int
|
||||||
|
interruptfd int
|
||||||
wakeEvent syscall.EpollEvent
|
wakeEvent syscall.EpollEvent
|
||||||
stopPollChan chan struct{}
|
stopPollChan chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type interrupt struct {
|
||||||
|
socket *Socket
|
||||||
|
event syscall.EpollEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
type memifInterrupt struct {
|
||||||
|
connection *Socket
|
||||||
|
qid uint16
|
||||||
|
}
|
||||||
|
|
||||||
// StopPolling stops polling events on the socket
|
// StopPolling stops polling events on the socket
|
||||||
func (socket *Socket) StopPolling() error {
|
func (socket *Socket) StopPolling() error {
|
||||||
if socket.stopPollChan != nil {
|
if socket.stopPollChan != nil {
|
||||||
@ -220,6 +231,15 @@ func (socket *Socket) handleEvent(event *syscall.EpollEvent) error {
|
|||||||
if socket.listener != nil && socket.listener.event.Fd == event.Fd {
|
if socket.listener != nil && socket.listener.event.Fd == event.Fd {
|
||||||
return socket.listener.handleEvent(event)
|
return socket.listener.handleEvent(event)
|
||||||
}
|
}
|
||||||
|
intf := socket.interfaceList.Back().Value.(*Interface)
|
||||||
|
if intf.args.InterruptFunc != nil {
|
||||||
|
if int(event.Fd) == int(intf.args.InterruptFd) {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
syscall.Read(int(event.Fd), b)
|
||||||
|
intf.onInterrupt(intf)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for elt := socket.ccList.Front(); elt != nil; elt = elt.Next() {
|
for elt := socket.ccList.Front(); elt != nil; elt = elt.Next() {
|
||||||
cc, ok := elt.Value.(*controlChannel)
|
cc, ok := elt.Value.(*controlChannel)
|
||||||
@ -233,6 +253,25 @@ func (socket *Socket) handleEvent(event *syscall.EpollEvent) error {
|
|||||||
return fmt.Errorf(errorFdNotFound)
|
return fmt.Errorf(errorFdNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (socket *Socket) addInterrupt(fd int) (err error) {
|
||||||
|
l := &interrupt{
|
||||||
|
// we will need this to look up master interface by id
|
||||||
|
socket: socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
l.event = syscall.EpollEvent{
|
||||||
|
Events: syscall.EPOLLIN,
|
||||||
|
Fd: int32(fd),
|
||||||
|
}
|
||||||
|
err = socket.addEvent(&l.event)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to add event: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// handleEvent handles epoll event for listener
|
// handleEvent handles epoll event for listener
|
||||||
func (l *listener) handleEvent(event *syscall.EpollEvent) error {
|
func (l *listener) handleEvent(event *syscall.EpollEvent) error {
|
||||||
// hang up
|
// hang up
|
||||||
@ -725,7 +764,6 @@ func (cc *controlChannel) parseConnect() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.isConnected = true
|
cc.isConnected = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -764,7 +802,6 @@ func (cc *controlChannel) parseConnected() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.isConnected = true
|
cc.isConnected = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -60,6 +60,8 @@ type ConnectedFunc func(i *Interface) error
|
|||||||
// DisconnectedFunc is a callback called when an interface is disconnected
|
// DisconnectedFunc is a callback called when an interface is disconnected
|
||||||
type DisconnectedFunc func(i *Interface) error
|
type DisconnectedFunc func(i *Interface) error
|
||||||
|
|
||||||
|
type InterruptFunc func(i *Interface) error
|
||||||
|
|
||||||
// MemoryConfig represents shared memory configuration
|
// MemoryConfig represents shared memory configuration
|
||||||
type MemoryConfig struct {
|
type MemoryConfig struct {
|
||||||
NumQueuePairs uint16 // number of queue pairs
|
NumQueuePairs uint16 // number of queue pairs
|
||||||
@ -77,7 +79,9 @@ type Arguments struct {
|
|||||||
MemoryConfig MemoryConfig
|
MemoryConfig MemoryConfig
|
||||||
ConnectedFunc ConnectedFunc // callback called when interface changes status to connected
|
ConnectedFunc ConnectedFunc // callback called when interface changes status to connected
|
||||||
DisconnectedFunc DisconnectedFunc // callback called when interface changes status to disconnected
|
DisconnectedFunc DisconnectedFunc // callback called when interface changes status to disconnected
|
||||||
PrivateData interface{} // private data used by client program
|
InterruptFunc InterruptFunc
|
||||||
|
PrivateData interface{} // private data used by client program
|
||||||
|
InterruptFd uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// memoryRegion represents a shared memory mapped file
|
// memoryRegion represents a shared memory mapped file
|
||||||
@ -110,6 +114,7 @@ type Interface struct {
|
|||||||
regions []memoryRegion
|
regions []memoryRegion
|
||||||
txQueues []Queue
|
txQueues []Queue
|
||||||
rxQueues []Queue
|
rxQueues []Queue
|
||||||
|
onInterrupt InterruptFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsMaster returns true if the interfaces role is master, else returns false
|
// IsMaster returns true if the interfaces role is master, else returns false
|
||||||
@ -270,6 +275,10 @@ func RoleToString(isMaster bool) string {
|
|||||||
return "Slave"
|
return "Slave"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memifPathIsAbstract(filename string) bool {
|
||||||
|
return (filename[0] == '@')
|
||||||
|
}
|
||||||
|
|
||||||
// RequestConnection is used by slave interface to connect to a socket and
|
// RequestConnection is used by slave interface to connect to a socket and
|
||||||
// create a control channel
|
// create a control channel
|
||||||
func (i *Interface) RequestConnection() error {
|
func (i *Interface) RequestConnection() error {
|
||||||
@ -283,6 +292,9 @@ func (i *Interface) RequestConnection() error {
|
|||||||
}
|
}
|
||||||
usa := &syscall.SockaddrUnix{Name: i.socket.filename}
|
usa := &syscall.SockaddrUnix{Name: i.socket.filename}
|
||||||
|
|
||||||
|
if memifPathIsAbstract(i.socket.GetFilename()) {
|
||||||
|
usa.Name = "\000" + usa.Name[1:]
|
||||||
|
}
|
||||||
// Connect to listener socket
|
// Connect to listener socket
|
||||||
err = syscall.Connect(fd, usa)
|
err = syscall.Connect(fd, usa)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -315,7 +327,8 @@ func (socket *Socket) NewInterface(args *Arguments) (*Interface, error) {
|
|||||||
|
|
||||||
// copy interface configuration
|
// copy interface configuration
|
||||||
i := Interface{
|
i := Interface{
|
||||||
args: *args,
|
args: *args,
|
||||||
|
onInterrupt: args.InterruptFunc,
|
||||||
}
|
}
|
||||||
// set default values
|
// set default values
|
||||||
if i.args.MemoryConfig.NumQueuePairs == 0 {
|
if i.args.MemoryConfig.NumQueuePairs == 0 {
|
||||||
@ -434,6 +447,7 @@ func (i *Interface) initializeQueues() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
i.socket.addInterrupt(q.interruptFd)
|
||||||
q.putRing()
|
q.putRing()
|
||||||
i.txQueues = append(i.txQueues, *q)
|
i.txQueues = append(i.txQueues, *q)
|
||||||
|
|
||||||
@ -452,11 +466,17 @@ func (i *Interface) initializeQueues() (err error) {
|
|||||||
i: i,
|
i: i,
|
||||||
}
|
}
|
||||||
q.ring.setCookie(cookie)
|
q.ring.setCookie(cookie)
|
||||||
q.ring.setFlags(1)
|
if i.args.InterruptFunc == nil {
|
||||||
|
q.ring.setFlags(1)
|
||||||
|
} else {
|
||||||
|
q.ring.setFlags(0)
|
||||||
|
}
|
||||||
q.interruptFd, err = eventFd()
|
q.interruptFd, err = eventFd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
i.args.InterruptFd = uint16(q.interruptFd)
|
||||||
|
i.socket.addInterrupt(q.interruptFd)
|
||||||
q.putRing()
|
q.putRing()
|
||||||
i.rxQueues = append(i.rxQueues, *q)
|
i.rxQueues = append(i.rxQueues, *q)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user