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 socket id 1 filename /tmp/icmp-responder-example
7 // vpp$ create interface memif id 1 socket-id 1 slave secret secret no-zero-copy
8 // vpp$ set int state memif1/1 up
9 // vpp$ set int ip address memif1/1 192.168.1.2/24
11 // To start the example, simply type:
12 // root$ ./icmp-responder
14 // icmp-responder needs to be run as root so that it can access the socket
17 // Normally, the memif interface is in the master mode. Pass CLI flag "--slave"
18 // to create memif in the slave mode:
19 // root$ ./icmp-responder --slave
21 // Don't forget to put the opposite memif into the master mode in that case.
23 // To verify the connection, run:
24 // vpp$ ping 192.168.1.1
25 // 64 bytes from 192.168.1.1: icmp_seq=2 ttl=255 time=.6974 ms
26 // 64 bytes from 192.168.1.1: icmp_seq=3 ttl=255 time=.6310 ms
27 // 64 bytes from 192.168.1.1: icmp_seq=4 ttl=255 time=1.0350 ms
28 // 64 bytes from 192.168.1.1: icmp_seq=5 ttl=255 time=.5359 ms
30 // Statistics: 5 sent, 4 received, 20% packet loss
32 // Time IP4 Flags Ethernet Interface
33 // 68.5648 192.168.1.1 D aa:aa:aa:aa:aa:aa memif0/1
35 // Note: it is expected that the first ping is shown as lost. It was actually
36 // converted to an ARP request. This is a VPP feature common to all interface
39 // Stop the example with an interrupt signal.
50 "github.com/google/gopacket"
51 "github.com/google/gopacket/layers"
53 "git.fd.io/govpp.git/extras/libmemif"
57 // Socket through which the opposite memifs will establish the connection.
58 Socket = "/tmp/icmp-responder-example"
60 // Secret used to authenticate the memif connection.
63 // ConnectionID is an identifier used to match opposite memifs.
66 // IPAddress assigned to the memif interface.
67 IPAddress = "192.168.1.1"
69 // MAC address assigned to the memif interface.
70 MAC = "aa:aa:aa:aa:aa:aa"
72 // NumQueues is the (configured!) number of queues for both Rx & Tx.
73 // The actual number agreed during connection establishment may be smaller!
77 // For management of go routines.
79 var stopCh chan struct{}
82 var hwAddr net.HardwareAddr
85 // ErrUnhandledPacket is thrown and printed when an unexpected packet is received.
86 var ErrUnhandledPacket = errors.New("received an unhandled packet")
88 // OnConnect is called when a memif connection gets established.
89 func OnConnect(memif *libmemif.Memif) (err error) {
90 details, err := memif.GetDetails()
92 fmt.Printf("libmemif.GetDetails() error: %v\n", err)
94 fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
96 stopCh = make(chan struct{})
97 // Start a separate go routine for each RX queue.
98 // (memif queue is a unit of parallelism for Rx/Tx).
99 // Beware: the number of queues created may be lower than what was requested
100 // in MemifConfiguration (the master makes the final decision).
101 // Use Memif.GetDetails to get the number of queues.
103 for i = 0; i < uint8(len(details.RxQueues)); i++ {
105 go IcmpResponder(memif, i)
110 // OnDisconnect is called when a memif connection is lost.
111 func OnDisconnect(memif *libmemif.Memif) (err error) {
112 fmt.Printf("memif %s has been disconnected\n", memif.IfName)
113 // Stop all packet producers and consumers.
119 // IcmpResponder answers to ICMP pings with ICMP pongs.
120 func IcmpResponder(memif *libmemif.Memif, queueID uint8) {
123 // Get channel which fires every time there are packets to read on the queue.
124 interruptCh, err := memif.GetQueueInterruptChan(queueID)
126 // Example of libmemif error handling code:
128 case libmemif.ErrQueueID:
129 fmt.Printf("libmemif.Memif.GetQueueInterruptChan() complains about invalid queue id!?")
130 // Here you would put all the errors that need to be handled individually...
132 fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error: %v\n", err)
140 // Read all packets from the queue but at most 10 at once.
141 // Since there is only one interrupt signal sent for an entire burst
142 // of packets, an interrupt handling routine should repeatedly call
143 // RxBurst() until the function returns an empty slice of packets.
144 // This way it is ensured that there are no packets left
145 // on the queue unread when the interrupt signal is cleared.
147 packets, err := memif.RxBurst(queueID, 10)
149 fmt.Printf("libmemif.Memif.RxBurst() error: %v\n", err)
150 // Skip this burst, continue with the next one 3secs later...
153 if len(packets) == 0 {
154 // No more packets to read until the next interrupt.
157 // Generate response for each supported request.
158 responses := []libmemif.RawPacketData{}
159 for _, packet := range packets {
160 fmt.Println("Received new packet:")
162 response, err := GeneratePacketResponse(packet)
164 fmt.Println("Sending response:")
166 responses = append(responses, response)
168 fmt.Printf("Failed to generate response: %v\n", err)
171 // Send pongs / ARP responses. We may not be able to do it in one
172 // burst if the ring is (almost) full or the internal buffer cannot
176 count, err := memif.TxBurst(queueID, responses[sent:])
178 fmt.Printf("libmemif.Memif.TxBurst() error: %v\n", err)
181 fmt.Printf("libmemif.Memif.TxBurst() has sent %d packets.\n", count)
183 if sent == len(responses) {
195 // DumpPacket prints a human-readable description of the packet.
196 func DumpPacket(packetData libmemif.RawPacketData) {
197 packet := gopacket.NewPacket(packetData, layers.LayerTypeEthernet, gopacket.Default)
198 fmt.Println(packet.Dump())
201 // GeneratePacketResponse returns an appropriate answer to an ARP request
202 // or an ICMP echo request.
203 func GeneratePacketResponse(packetData libmemif.RawPacketData) (response libmemif.RawPacketData, err error) {
204 packet := gopacket.NewPacket(packetData, layers.LayerTypeEthernet, gopacket.Default)
206 ethLayer := packet.Layer(layers.LayerTypeEthernet)
208 fmt.Println("Missing ETH layer.")
209 return nil, ErrUnhandledPacket
211 eth, _ := ethLayer.(*layers.Ethernet)
213 if eth.EthernetType == layers.EthernetTypeARP {
214 // Handle ARP request.
215 arpLayer := packet.Layer(layers.LayerTypeARP)
217 fmt.Println("Missing ARP layer.")
218 return nil, ErrUnhandledPacket
220 arp, _ := arpLayer.(*layers.ARP)
221 if arp.Operation != layers.ARPRequest {
222 fmt.Println("Not ARP request.")
223 return nil, ErrUnhandledPacket
225 fmt.Println("Received an ARP request.")
227 // Build packet layers.
228 ethResp := layers.Ethernet{
231 EthernetType: layers.EthernetTypeARP,
233 arpResp := layers.ARP{
234 AddrType: layers.LinkTypeEthernet,
235 Protocol: layers.EthernetTypeIPv4,
238 Operation: layers.ARPReply,
239 SourceHwAddress: []byte(hwAddr),
240 SourceProtAddress: []byte(ipAddr),
241 DstHwAddress: arp.SourceHwAddress,
242 DstProtAddress: arp.SourceProtAddress,
244 // Set up buffer and options for serialization.
245 buf := gopacket.NewSerializeBuffer()
246 opts := gopacket.SerializeOptions{
248 ComputeChecksums: true,
250 err := gopacket.SerializeLayers(buf, opts, ðResp, &arpResp)
252 fmt.Println("SerializeLayers error: ", err)
254 return buf.Bytes(), nil
257 if eth.EthernetType == layers.EthernetTypeIPv4 {
258 // Respond to ICMP request.
259 ipLayer := packet.Layer(layers.LayerTypeIPv4)
261 fmt.Println("Missing IPv4 layer.")
262 return nil, ErrUnhandledPacket
264 ipv4, _ := ipLayer.(*layers.IPv4)
265 if ipv4.Protocol != layers.IPProtocolICMPv4 {
266 fmt.Println("Not ICMPv4 protocol.")
267 return nil, ErrUnhandledPacket
269 icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
270 if icmpLayer == nil {
271 fmt.Println("Missing ICMPv4 layer.")
272 return nil, ErrUnhandledPacket
274 icmp, _ := icmpLayer.(*layers.ICMPv4)
275 if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
276 fmt.Println("Not ICMPv4 echo request.")
277 return nil, ErrUnhandledPacket
279 fmt.Println("Received an ICMPv4 echo request.")
281 // Build packet layers.
282 ethResp := layers.Ethernet{
285 EthernetType: layers.EthernetTypeIPv4,
287 ipv4Resp := layers.IPv4{
295 Protocol: layers.IPProtocolICMPv4,
299 icmpResp := layers.ICMPv4{
300 TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
305 // Set up buffer and options for serialization.
306 buf := gopacket.NewSerializeBuffer()
307 opts := gopacket.SerializeOptions{
309 ComputeChecksums: true,
311 err := gopacket.SerializeLayers(buf, opts, ðResp, &ipv4Resp, &icmpResp,
312 gopacket.Payload(icmp.Payload))
314 fmt.Println("SerializeLayers error: ", err)
316 return buf.Bytes(), nil
319 return nil, ErrUnhandledPacket
324 fmt.Println("Starting 'icmp-responder' example...")
326 hwAddr, err = net.ParseMAC(MAC)
328 fmt.Println("Failed to parse the MAC address: %v", err)
332 ip := net.ParseIP(IPAddress)
337 fmt.Println("Failed to parse the IP address: %v", err)
341 // If run with the "--slave" option, create memif in the slave mode.
344 if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
349 // Initialize libmemif first.
350 appName := "ICMP-Responder" + appSuffix
351 fmt.Println("Initializing libmemif as ", appName)
352 err = libmemif.Init(appName)
354 fmt.Printf("libmemif.Init() error: %v\n", err)
357 // Schedule automatic cleanup.
358 defer libmemif.Cleanup()
360 // Prepare callbacks to use with the memif.
361 // The same callbacks could be used with multiple memifs.
362 // The first input argument (*libmemif.Memif) can be used to tell which
363 // memif the callback was triggered for.
364 memifCallbacks := &libmemif.MemifCallbacks{
365 OnConnect: OnConnect,
366 OnDisconnect: OnDisconnect,
369 // Prepare memif1 configuration.
370 memifConfig := &libmemif.MemifConfig{
371 MemifMeta: libmemif.MemifMeta{
373 ConnID: ConnectionID,
374 SocketFilename: Socket,
377 Mode: libmemif.IfModeEthernet,
379 MemifShmSpecs: libmemif.MemifShmSpecs{
380 NumRxQueues: NumQueues,
381 NumTxQueues: NumQueues,
387 fmt.Printf("Callbacks: %+v\n", memifCallbacks)
388 fmt.Printf("Config: %+v\n", memifConfig)
390 // Create memif1 interface.
391 memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
393 fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
396 // Schedule automatic cleanup of the interface.
399 // Wait until an interrupt signal is received.
400 sigChan := make(chan os.Signal, 1)
401 signal.Notify(sigChan, os.Interrupt)