1 // icmp-responder is a simple example showing how to answer APR and ICMP echo
2 // requests through a memif interface. Package "google/gopacket" is used to decode
3 // and construct packets.
5 // The appropriate VPP configuration for the opposite memif is:
6 // vpp$ create memif id 1 socket /tmp/icmp-responder-example slave secret secret
7 // vpp$ set int state memif0/1 up
8 // vpp$ set int ip address memif0/1 192.168.1.2/24
10 // To start the example, simply type:
11 // root$ ./icmp-responder
13 // icmp-responder needs to be run as root so that it can access the socket
16 // Normally, the memif interface is in the master mode. Pass CLI flag "--slave"
17 // to create memif in the slave mode:
18 // root$ ./icmp-responder --slave
20 // Don't forget to put the opposite memif into the master mode in that case.
22 // To verify the connection, run:
23 // vpp$ ping 192.168.1.1
24 // 64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
25 // 64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
26 // 64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
27 // 64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms
29 // Statistics: 5 sent, 4 received, 20% packet loss
31 // Time IP4 Flags Ethernet Interface
32 // 68.5648 192.168.1.1 D aa:aa:aa:aa:aa:aa memif0/1
34 // Note: it is expected that the first ping is shown as lost. It was actually
35 // converted to an ARP request. This is a VPP feature common to all interface
38 // Stop the example with an interrupt signal.
49 "github.com/google/gopacket"
50 "github.com/google/gopacket/layers"
52 "git.fd.io/govpp.git/extras/libmemif"
56 // Socket through which the opposite memifs will establish the connection.
57 Socket = "/tmp/icmp-responder-example"
59 // Secret used to authenticate the memif connection.
62 // ConnectionID is an identifier used to match opposite memifs.
65 // IPAddress assigned to the memif interface.
66 IPAddress = "192.168.1.1"
68 // MAC address assigned to the memif interface.
69 MAC = "aa:aa:aa:aa:aa:aa"
71 // NumQueues is the (configured!) number of queues for both Rx & Tx.
72 // The actual number agreed during connection establishment may be smaller!
76 // For management of go routines.
78 var stopCh chan struct{}
81 var hwAddr net.HardwareAddr
84 // ErrUnhandledPacket is thrown and printed when an unexpected packet is received.
85 var ErrUnhandledPacket = errors.New("received an unhandled packet")
87 // OnConnect is called when a memif connection gets established.
88 func OnConnect(memif *libmemif.Memif) (err error) {
89 details, err := memif.GetDetails()
91 fmt.Printf("libmemif.GetDetails() error: %v\n", err)
93 fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
95 stopCh = make(chan struct{})
96 // Start a separate go routine for each RX queue.
97 // (memif queue is a unit of parallelism for Rx/Tx).
98 // Beware: the number of queues created may be lower than what was requested
99 // in MemifConfiguration (the master makes the final decision).
100 // Use Memif.GetDetails to get the number of queues.
102 for i = 0; i < uint8(len(details.RxQueues)); i++ {
104 go IcmpResponder(memif, i)
109 // OnDisconnect is called when a memif connection is lost.
110 func OnDisconnect(memif *libmemif.Memif) (err error) {
111 fmt.Printf("memif %s has been disconnected\n", memif.IfName)
112 // Stop all packet producers and consumers.
118 // IcmpResponder answers to ICMP pings with ICMP pongs.
119 func IcmpResponder(memif *libmemif.Memif, queueID uint8) {
122 // Get channel which fires every time there are packets to read on the queue.
123 interruptCh, err := memif.GetQueueInterruptChan(queueID)
125 // Example of libmemif error handling code:
127 case libmemif.ErrQueueID:
128 fmt.Printf("libmemif.Memif.GetQueueInterruptChan() complains about invalid queue id!?")
129 // Here you would put all the errors that need to be handled individually...
131 fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error: %v\n", err)
139 // Read all packets from the queue but at most 10 at once.
140 // Since there is only one interrupt signal sent for an entire burst
141 // of packets, an interrupt handling routine should repeatedly call
142 // RxBurst() until the function returns an empty slice of packets.
143 // This way it is ensured that there are no packets left
144 // on the queue unread when the interrupt signal is cleared.
146 packets, err := memif.RxBurst(queueID, 10)
148 fmt.Printf("libmemif.Memif.RxBurst() error: %v\n", err)
149 // Skip this burst, continue with the next one 3secs later...
152 if len(packets) == 0 {
153 // No more packets to read until the next interrupt.
156 // Generate response for each supported request.
157 responses := []libmemif.RawPacketData{}
158 for _, packet := range packets {
159 fmt.Println("Received new packet:")
161 response, err := GeneratePacketResponse(packet)
163 fmt.Println("Sending response:")
165 responses = append(responses, response)
167 fmt.Printf("Failed to generate response: %v\n", err)
170 // Send pongs / ARP responses. We may not be able to do it in one
171 // burst if the ring is (almost) full or the internal buffer cannot
175 count, err := memif.TxBurst(queueID, responses[sent:])
177 fmt.Printf("libmemif.Memif.TxBurst() error: %v\n", err)
180 fmt.Printf("libmemif.Memif.TxBurst() has sent %d packets.\n", count)
182 if sent == len(responses) {
194 // DumpPacket prints a human-readable description of the packet.
195 func DumpPacket(packetData libmemif.RawPacketData) {
196 packet := gopacket.NewPacket(packetData, layers.LayerTypeEthernet, gopacket.Default)
197 fmt.Println(packet.Dump())
200 // GeneratePacketResponse returns an appropriate answer to an ARP request
201 // or an ICMP echo request.
202 func GeneratePacketResponse(packetData libmemif.RawPacketData) (response libmemif.RawPacketData, err error) {
203 packet := gopacket.NewPacket(packetData, layers.LayerTypeEthernet, gopacket.Default)
205 ethLayer := packet.Layer(layers.LayerTypeEthernet)
207 fmt.Println("Missing ETH layer.")
208 return nil, ErrUnhandledPacket
210 eth, _ := ethLayer.(*layers.Ethernet)
212 if eth.EthernetType == layers.EthernetTypeARP {
213 // Handle ARP request.
214 arpLayer := packet.Layer(layers.LayerTypeARP)
216 fmt.Println("Missing ARP layer.")
217 return nil, ErrUnhandledPacket
219 arp, _ := arpLayer.(*layers.ARP)
220 if arp.Operation != layers.ARPRequest {
221 fmt.Println("Not ARP request.")
222 return nil, ErrUnhandledPacket
224 fmt.Println("Received an ARP request.")
226 // Build packet layers.
227 ethResp := layers.Ethernet{
230 EthernetType: layers.EthernetTypeARP,
232 arpResp := layers.ARP{
233 AddrType: layers.LinkTypeEthernet,
234 Protocol: layers.EthernetTypeIPv4,
237 Operation: layers.ARPReply,
238 SourceHwAddress: []byte(hwAddr),
239 SourceProtAddress: []byte(ipAddr),
240 DstHwAddress: arp.SourceHwAddress,
241 DstProtAddress: arp.SourceProtAddress,
243 // Set up buffer and options for serialization.
244 buf := gopacket.NewSerializeBuffer()
245 opts := gopacket.SerializeOptions{
247 ComputeChecksums: true,
249 err := gopacket.SerializeLayers(buf, opts, ðResp, &arpResp)
251 fmt.Println("SerializeLayers error: ", err)
253 return buf.Bytes(), nil
256 if eth.EthernetType == layers.EthernetTypeIPv4 {
257 // Respond to ICMP request.
258 ipLayer := packet.Layer(layers.LayerTypeIPv4)
260 fmt.Println("Missing IPv4 layer.")
261 return nil, ErrUnhandledPacket
263 ipv4, _ := ipLayer.(*layers.IPv4)
264 if ipv4.Protocol != layers.IPProtocolICMPv4 {
265 fmt.Println("Not ICMPv4 protocol.")
266 return nil, ErrUnhandledPacket
268 icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
269 if icmpLayer == nil {
270 fmt.Println("Missing ICMPv4 layer.")
271 return nil, ErrUnhandledPacket
273 icmp, _ := icmpLayer.(*layers.ICMPv4)
274 if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
275 fmt.Println("Not ICMPv4 echo request.")
276 return nil, ErrUnhandledPacket
278 fmt.Println("Received an ICMPv4 echo request.")
280 // Build packet layers.
281 ethResp := layers.Ethernet{
284 EthernetType: layers.EthernetTypeIPv4,
286 ipv4Resp := layers.IPv4{
294 Protocol: layers.IPProtocolICMPv4,
298 icmpResp := layers.ICMPv4{
299 TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
304 // Set up buffer and options for serialization.
305 buf := gopacket.NewSerializeBuffer()
306 opts := gopacket.SerializeOptions{
308 ComputeChecksums: true,
310 err := gopacket.SerializeLayers(buf, opts, ðResp, &ipv4Resp, &icmpResp,
311 gopacket.Payload(icmp.Payload))
313 fmt.Println("SerializeLayers error: ", err)
315 return buf.Bytes(), nil
318 return nil, ErrUnhandledPacket
323 fmt.Println("Starting 'icmp-responder' example...")
325 hwAddr, err = net.ParseMAC(MAC)
327 fmt.Println("Failed to parse the MAC address: %v", err)
331 ip := net.ParseIP(IPAddress)
336 fmt.Println("Failed to parse the IP address: %v", err)
340 // If run with the "--slave" option, create memif in the slave mode.
343 if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
348 // Initialize libmemif first.
349 appName := "ICMP-Responder" + appSuffix
350 fmt.Println("Initializing libmemif as ", appName)
351 err = libmemif.Init(appName)
353 fmt.Printf("libmemif.Init() error: %v\n", err)
356 // Schedule automatic cleanup.
357 defer libmemif.Cleanup()
359 // Prepare callbacks to use with the memif.
360 // The same callbacks could be used with multiple memifs.
361 // The first input argument (*libmemif.Memif) can be used to tell which
362 // memif the callback was triggered for.
363 memifCallbacks := &libmemif.MemifCallbacks{
364 OnConnect: OnConnect,
365 OnDisconnect: OnDisconnect,
368 // Prepare memif1 configuration.
369 memifConfig := &libmemif.MemifConfig{
370 MemifMeta: libmemif.MemifMeta{
372 ConnID: ConnectionID,
373 SocketFilename: Socket,
376 Mode: libmemif.IfModeEthernet,
378 MemifShmSpecs: libmemif.MemifShmSpecs{
379 NumRxQueues: NumQueues,
380 NumTxQueues: NumQueues,
386 fmt.Printf("Callbacks: %+v\n", memifCallbacks)
387 fmt.Printf("Config: %+v\n", memifConfig)
389 // Create memif1 interface.
390 memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
392 fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
395 // Schedule automatic cleanup of the interface.
398 // Wait until an interrupt signal is received.
399 sigChan := make(chan os.Signal, 1)
400 signal.Notify(sigChan, os.Interrupt)