1 // gopacket is a simple example showing how to answer APR and ICMP echo
2 // requests through a memif interface. This example is mostly identical
3 // to icmp-responder example, but it is using MemifPacketHandle API to
4 // read and write packets using gopacket API.
6 // The appropriate VPP configuration for the opposite memif is:
7 // vpp$ create memif socket id 1 filename /tmp/gopacket-example
8 // vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
9 // vpp$ set int state memif1/1 up
10 // vpp$ set int ip address memif1/1 192.168.1.2/24
12 // To start the example, simply type:
15 // gopacket needs to be run as root so that it can access the socket
18 // Normally, the memif interface is in the master mode. Pass CLI flag "--slave"
19 // to create memif in the slave mode:
20 // root$ ./gopacket --slave
22 // Don't forget to put the opposite memif into the master mode in that case.
24 // To verify the connection, run:
25 // vpp$ ping 192.168.1.1
26 // 64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
27 // 64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
28 // 64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
29 // 64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms
31 // Statistics: 5 sent, 4 received, 20% packet loss
33 // Time IP4 Flags Ethernet Interface
34 // 68.5648 192.168.1.1 D aa:aa:aa:aa:aa:aa memif0/1
36 // Note: it is expected that the first ping is shown as lost. It was actually
37 // converted to an ARP request. This is a VPP feature common to all interface
40 // Stop the example with an interrupt signal.
46 "go.fd.io/govpp/extras/libmemif"
47 "github.com/google/gopacket"
48 "github.com/google/gopacket/layers"
56 // Used to signalize interrupt goroutines to stop
59 // MAC address assigned to the memif interface.
60 hwAddr net.HardwareAddr
62 // IPAddress assigned to the memif interface.
65 // ErrUnhandledPacket is thrown and printed when an unexpected packet is received.
66 ErrUnhandledPacket = errors.New("received an unhandled packet")
69 // OnConnect is called when a memif connection gets established.
70 func OnConnect(memif *libmemif.Memif) (err error) {
71 // Use Memif.GetDetails to get the number of queues.
72 details, err := memif.GetDetails()
74 fmt.Printf("libmemif.GetDetails() error: %v\n", err)
78 fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
79 stopCh = make(chan struct{})
81 // Start a separate go routine for each RX queue.
82 // (memif queue is a unit of parallelism for Rx/Tx).
83 // Beware: the number of queues created may be lower than what was requested
84 // in MemifConfiguration (the master makes the final decision).
85 for _, queue := range details.RxQueues {
86 ch, err := memif.GetQueueInterruptChan(queue.QueueID)
88 fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error %v\n", err)
92 go CreateInterruptCallback(memif.NewPacketHandle(queue.QueueID, 10), ch, OnInterrupt)
98 // OnDisconnect is called when a memif connection is lost.
99 func OnDisconnect(memif *libmemif.Memif) (err error) {
100 fmt.Printf("memif %s has been disconnected\n", memif.IfName)
101 // Stop all packet producers and consumers.
106 // OnInterrupt is called when interrupted
107 func OnInterrupt(handle *libmemif.MemifPacketHandle) {
108 source := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
109 var responses []gopacket.Packet
111 // Process ICMP pings
112 for packet := range source.Packets() {
113 fmt.Println("Received new packet:")
114 fmt.Println(packet.Dump())
116 response, err := GeneratePacketResponse(packet)
118 fmt.Printf("Failed to generate response: %v\n", err)
122 fmt.Println("Sending response:")
123 fmt.Println(response.Dump())
124 responses = append(responses, response)
127 // Answer with ICMP pongs
128 for i, response := range responses {
129 err := handle.WritePacketData(response.Data())
135 fmt.Printf("Succesfully sent packet #%v %v\n", i, len(response.Data()))
137 fmt.Printf("Got error while sending packet #%v %v\n", i, err)
142 // Creates user-friendly memif interrupt callback
143 func CreateInterruptCallback(handle *libmemif.MemifPacketHandle, interruptCh <-chan struct{}, callback func(handle *libmemif.MemifPacketHandle)) {
155 // GeneratePacketResponse returns an appropriate answer to an ARP request
156 // or an ICMP echo request.
157 func GeneratePacketResponse(packet gopacket.Packet) (response gopacket.Packet, err error) {
158 ethLayer := packet.Layer(layers.LayerTypeEthernet)
159 eth, ok := ethLayer.(*layers.Ethernet)
161 fmt.Println("Missing ETH layer.")
162 return nil, ErrUnhandledPacket
165 // Set up buffer and options for serialization.
166 buf := gopacket.NewSerializeBuffer()
167 opts := gopacket.SerializeOptions{
169 ComputeChecksums: true,
172 switch eth.EthernetType {
173 case layers.EthernetTypeARP:
174 // Handle ARP request.
175 arpLayer := packet.Layer(layers.LayerTypeARP)
176 arp, ok := arpLayer.(*layers.ARP)
178 fmt.Println("Missing ARP layer.")
179 return nil, ErrUnhandledPacket
182 if arp.Operation != layers.ARPRequest {
183 fmt.Println("Not ARP request.")
184 return nil, ErrUnhandledPacket
187 fmt.Println("Received an ARP request.")
189 // Build packet layers.
190 ethResp := layers.Ethernet{
193 EthernetType: layers.EthernetTypeARP,
196 arpResp := layers.ARP{
197 AddrType: layers.LinkTypeEthernet,
198 Protocol: layers.EthernetTypeIPv4,
201 Operation: layers.ARPReply,
202 SourceHwAddress: []byte(hwAddr),
203 SourceProtAddress: []byte(ipAddr),
204 DstHwAddress: arp.SourceHwAddress,
205 DstProtAddress: arp.SourceProtAddress,
208 if err := gopacket.SerializeLayers(buf, opts, ðResp, &arpResp); err != nil {
209 fmt.Println("SerializeLayers error: ", err)
210 return nil, ErrUnhandledPacket
212 case layers.EthernetTypeIPv4:
213 // Respond to ICMP request.
214 ipLayer := packet.Layer(layers.LayerTypeIPv4)
215 ipv4, ok := ipLayer.(*layers.IPv4)
217 fmt.Println("Missing IPv4 layer.")
218 return nil, ErrUnhandledPacket
221 if ipv4.Protocol != layers.IPProtocolICMPv4 {
222 fmt.Println("Not ICMPv4 protocol.")
223 return nil, ErrUnhandledPacket
226 icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
227 icmp, ok := icmpLayer.(*layers.ICMPv4)
229 fmt.Println("Missing ICMPv4 layer.")
230 return nil, ErrUnhandledPacket
233 if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
234 fmt.Println("Not ICMPv4 echo request.")
235 return nil, ErrUnhandledPacket
238 fmt.Println("Received an ICMPv4 echo request.")
240 // Build packet layers.
241 ethResp := layers.Ethernet{
244 EthernetType: layers.EthernetTypeIPv4,
247 ipv4Resp := layers.IPv4{
255 Protocol: layers.IPProtocolICMPv4,
260 icmpResp := layers.ICMPv4{
261 TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
266 if err := gopacket.SerializeLayers(buf, opts, ðResp, &ipv4Resp, &icmpResp, gopacket.Payload(icmp.Payload)); err != nil {
267 fmt.Println("SerializeLayers error: ", err)
268 return nil, ErrUnhandledPacket
271 return nil, ErrUnhandledPacket
274 return gopacket.NewPacket(buf.Bytes(), layers.LayerTypeEthernet, gopacket.Default), nil
278 fmt.Println("Starting 'gopacket' example...")
281 // Parse MAC address associated with memif interface
282 hwAddr, err = net.ParseMAC("aa:aa:aa:aa:aa:aa")
284 fmt.Printf("Failed to parse the MAC address: %v/n", err)
288 // Parse IP address associated with memif interface
289 ip := net.ParseIP("192.168.1.1")
294 fmt.Println("Failed to parse the IP address")
298 // If run with the "--slave" option, create memif in the slave mode.
301 if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
306 // Initialize libmemif first.
307 appName := "gopacket" + appSuffix
308 fmt.Println("Initializing libmemif as ", appName)
309 err = libmemif.Init(appName)
311 fmt.Printf("libmemif.Init() error: %v\n", err)
315 // Schedule automatic cleanup.
316 defer libmemif.Cleanup()
318 // Prepare callbacks to use with the memif.
319 // The same callbacks could be used with multiple memifs.
320 // The first input argument (*libmemif.Memif) can be used to tell which
321 // memif the callback was triggered for.
322 memifCallbacks := &libmemif.MemifCallbacks{
323 OnConnect: OnConnect,
324 OnDisconnect: OnDisconnect,
327 // Prepare memif1 configuration.
328 memifConfig := &libmemif.MemifConfig{
329 MemifMeta: libmemif.MemifMeta{
331 ConnID: 1, // ConnectionID is an identifier used to match opposite memifs.
332 SocketFilename: "/tmp/gopacket-example", // Socket through which the opposite memifs will establish the connection.
333 Secret: "secret", // Secret used to authenticate the memif connection.
335 Mode: libmemif.IfModeEthernet,
337 MemifShmSpecs: libmemif.MemifShmSpecs{
338 NumRxQueues: 3, // NumQueues is the (configured!) number of queues for both Rx & Tx.
339 NumTxQueues: 3, // The actual number agreed during connection establishment may be smaller!
345 fmt.Printf("Callbacks: %+v\n", memifCallbacks)
346 fmt.Printf("Config: %+v\n", memifConfig)
348 // Create memif1 interface.
349 memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
351 fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
355 // Schedule automatic cleanup of the interface.
358 // Wait until an interrupt signal is received.
359 sigChan := make(chan os.Signal, 1)
360 signal.Notify(sigChan, os.Interrupt)