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:
Daniel Béreš
2022-07-27 12:22:39 +00:00
committed by Beno�t Ganne
parent a58055d6b2
commit 82ec908acb
7 changed files with 553 additions and 10 deletions

50
extras/gomemif/README.rst Normal file
View 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

View File

@ -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"
)

View File

@ -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"
],
)

File diff suppressed because it is too large Load Diff

View File

@ -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, &ethResp, &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

View File

@ -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

View File

@ -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)