blob: f9f1c3e8208b0243d00e782efd01a6cdd6cc456e [file] [log] [blame]
/*
*------------------------------------------------------------------
* 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"
"net"
"os"
"strings"
"sync"
"memif"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/pkg/profile"
)
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
}
// get tx queue
txq0, err := i.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 {
fmt.Printf("pktLen: %d\n", 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 {
errChan <- err
return
}
}
}
}(data.errChan, data.quitChan, &data.wg)
return nil
}
type interfaceData struct {
errChan chan error
quitChan chan struct{}
wg sync.WaitGroup
}
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 main() {
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()
if *cpuprof != "" {
defer profile.Start(profile.CPUProfile, profile.ProfilePath(*cpuprof)).Stop()
}
if *memprof != "" {
defer profile.Start(profile.MemProfile, profile.ProfilePath(*memprof)).Stop()
}
memifErrChan := make(chan error)
exitChan := make(chan struct{})
var isMaster bool
switch *role {
case "slave":
isMaster = false
case "master":
isMaster = true
default:
fmt.Println("Invalid role")
return
}
fmt.Println("GoMemif: Responder")
fmt.Println("-----------------------")
socket, err := memif.NewSocket("gomemif_example", *socketName)
if err != nil {
fmt.Println("Failed to create socket: ", err)
return
}
data := interfaceData{}
args := &memif.Arguments{
IsMaster: isMaster,
ConnectedFunc: Connected,
DisconnectedFunc: Disconnected,
PrivateData: &data,
Name: *name,
}
i, err := socket.NewInterface(args)
if err != nil {
fmt.Println("Failed to create interface on socket %s: %s", socket.GetFilename(), err)
goto exit
}
// 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()
*/
fmt.Println("Faild to connect: ", err)
goto exit
}
}
}
go func(exitChan chan<- struct{}) {
reader := bufio.NewReader(os.Stdin)
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":
fmt.Println("remote: ", i.GetRemoteName())
fmt.Println("peer: ", i.GetPeerName())
case "exit":
err = socket.StopPolling()
if err != nil {
fmt.Println("Failed to stop polling: ", err)
}
close(exitChan)
return
default:
fmt.Println("Unknown input")
}
}
}(exitChan)
for {
select {
case <-exitChan:
goto exit
case err, ok := <-memifErrChan:
if ok {
fmt.Println(err)
}
case err, ok := <-data.errChan:
if ok {
fmt.Println(err)
}
default:
continue
}
}
exit:
socket.Delete()
close(memifErrChan)
}