Change module name to go.fd.io/govpp
[govpp.git] / extras / libmemif / examples / icmp-responder / icmp-responder.go
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.
4 //
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
10 //
11 // To start the example, simply type:
12 //   root$ ./icmp-responder
13 //
14 // icmp-responder needs to be run as root so that it can access the socket
15 // created by VPP.
16 //
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
20 //
21 // Don't forget to put the opposite memif into the master mode in that case.
22 //
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
29 //
30 //   Statistics: 5 sent, 4 received, 20% packet loss
31 //   vpp$ sh ip arp
32 //   Time           IP4       Flags      Ethernet              Interface
33 //   68.5648   192.168.1.1     D    aa:aa:aa:aa:aa:aa memif0/1
34 //
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
37 // types.
38 //
39 // Stop the example with an interrupt signal.
40 package main
41
42 import (
43         "errors"
44         "fmt"
45         "net"
46         "os"
47         "os/signal"
48         "sync"
49
50         "github.com/google/gopacket"
51         "github.com/google/gopacket/layers"
52
53         "go.fd.io/govpp/extras/libmemif"
54 )
55
56 const (
57         // Socket through which the opposite memifs will establish the connection.
58         Socket = "/tmp/icmp-responder-example"
59
60         // Secret used to authenticate the memif connection.
61         Secret = "secret"
62
63         // ConnectionID is an identifier used to match opposite memifs.
64         ConnectionID = 1
65
66         // IPAddress assigned to the memif interface.
67         IPAddress = "192.168.1.1"
68
69         // MAC address assigned to the memif interface.
70         MAC = "aa:aa:aa:aa:aa:aa"
71
72         // NumQueues is the (configured!) number of queues for both Rx & Tx.
73         // The actual number agreed during connection establishment may be smaller!
74         NumQueues uint8 = 3
75 )
76
77 // For management of go routines.
78 var wg sync.WaitGroup
79 var stopCh chan struct{}
80
81 // Parsed addresses.
82 var hwAddr net.HardwareAddr
83 var ipAddr net.IP
84
85 // ErrUnhandledPacket is thrown and printed when an unexpected packet is received.
86 var ErrUnhandledPacket = errors.New("received an unhandled packet")
87
88 // OnConnect is called when a memif connection gets established.
89 func OnConnect(memif *libmemif.Memif) (err error) {
90         details, err := memif.GetDetails()
91         if err != nil {
92                 fmt.Printf("libmemif.GetDetails() error: %v\n", err)
93         }
94         fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
95
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.
102         var i uint8
103         for i = 0; i < uint8(len(details.RxQueues)); i++ {
104                 wg.Add(1)
105                 go IcmpResponder(memif, i)
106         }
107         return nil
108 }
109
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.
114         close(stopCh)
115         wg.Wait()
116         return nil
117 }
118
119 // IcmpResponder answers to ICMP pings with ICMP pongs.
120 func IcmpResponder(memif *libmemif.Memif, queueID uint8) {
121         defer wg.Done()
122
123         // Get channel which fires every time there are packets to read on the queue.
124         interruptCh, err := memif.GetQueueInterruptChan(queueID)
125         if err != nil {
126                 // Example of libmemif error handling code:
127                 switch err {
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...
131                 default:
132                         fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error: %v\n", err)
133                 }
134                 return
135         }
136
137         for {
138                 select {
139                 case <-interruptCh:
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.
146                         for {
147                                 packets, err := memif.RxBurst(queueID, 10)
148                                 if err != nil {
149                                         fmt.Printf("libmemif.Memif.RxBurst() error: %v\n", err)
150                                         // Skip this burst, continue with the next one 3secs later...
151                                         break
152                                 }
153                                 if len(packets) == 0 {
154                                         // No more packets to read until the next interrupt.
155                                         break
156                                 }
157                                 // Generate response for each supported request.
158                                 var responses []libmemif.RawPacketData
159                                 for _, packet := range packets {
160                                         fmt.Println("Received new packet:")
161                                         DumpPacket(packet)
162                                         response, err := GeneratePacketResponse(packet)
163                                         if err == nil {
164                                                 fmt.Println("Sending response:")
165                                                 DumpPacket(response)
166                                                 responses = append(responses, response)
167                                         } else {
168                                                 fmt.Printf("Failed to generate response: %v\n", err)
169                                         }
170                                 }
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
173                                 // contain it.
174                                 sent := 0
175                                 for {
176                                         count, err := memif.TxBurst(queueID, responses[sent:])
177                                         if err != nil {
178                                                 fmt.Printf("libmemif.Memif.TxBurst() error: %v\n", err)
179                                                 break
180                                         } else {
181                                                 fmt.Printf("libmemif.Memif.TxBurst() has sent %d packets.\n", count)
182                                                 sent += int(count)
183                                                 if sent == len(responses) {
184                                                         break
185                                                 }
186                                         }
187                                 }
188                         }
189                 case <-stopCh:
190                         return
191                 }
192         }
193 }
194
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())
199 }
200
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)
205
206         ethLayer := packet.Layer(layers.LayerTypeEthernet)
207         if ethLayer == nil {
208                 fmt.Println("Missing ETH layer.")
209                 return nil, ErrUnhandledPacket
210         }
211         eth, _ := ethLayer.(*layers.Ethernet)
212
213         if eth.EthernetType == layers.EthernetTypeARP {
214                 // Handle ARP request.
215                 arpLayer := packet.Layer(layers.LayerTypeARP)
216                 if arpLayer == nil {
217                         fmt.Println("Missing ARP layer.")
218                         return nil, ErrUnhandledPacket
219                 }
220                 arp, _ := arpLayer.(*layers.ARP)
221                 if arp.Operation != layers.ARPRequest {
222                         fmt.Println("Not ARP request.")
223                         return nil, ErrUnhandledPacket
224                 }
225                 fmt.Println("Received an ARP request.")
226
227                 // Build packet layers.
228                 ethResp := layers.Ethernet{
229                         SrcMAC:       hwAddr,
230                         DstMAC:       eth.SrcMAC,
231                         EthernetType: layers.EthernetTypeARP,
232                 }
233                 arpResp := layers.ARP{
234                         AddrType:          layers.LinkTypeEthernet,
235                         Protocol:          layers.EthernetTypeIPv4,
236                         HwAddressSize:     6,
237                         ProtAddressSize:   4,
238                         Operation:         layers.ARPReply,
239                         SourceHwAddress:   []byte(hwAddr),
240                         SourceProtAddress: []byte(ipAddr),
241                         DstHwAddress:      arp.SourceHwAddress,
242                         DstProtAddress:    arp.SourceProtAddress,
243                 }
244                 // Set up buffer and options for serialization.
245                 buf := gopacket.NewSerializeBuffer()
246                 opts := gopacket.SerializeOptions{
247                         FixLengths:       true,
248                         ComputeChecksums: true,
249                 }
250                 err := gopacket.SerializeLayers(buf, opts, &ethResp, &arpResp)
251                 if err != nil {
252                         fmt.Println("SerializeLayers error: ", err)
253                 }
254                 return buf.Bytes(), nil
255         }
256
257         if eth.EthernetType == layers.EthernetTypeIPv4 {
258                 // Respond to ICMP request.
259                 ipLayer := packet.Layer(layers.LayerTypeIPv4)
260                 if ipLayer == nil {
261                         fmt.Println("Missing IPv4 layer.")
262                         return nil, ErrUnhandledPacket
263                 }
264                 ipv4, _ := ipLayer.(*layers.IPv4)
265                 if ipv4.Protocol != layers.IPProtocolICMPv4 {
266                         fmt.Println("Not ICMPv4 protocol.")
267                         return nil, ErrUnhandledPacket
268                 }
269                 icmpLayer := packet.Layer(layers.LayerTypeICMPv4)
270                 if icmpLayer == nil {
271                         fmt.Println("Missing ICMPv4 layer.")
272                         return nil, ErrUnhandledPacket
273                 }
274                 icmp, _ := icmpLayer.(*layers.ICMPv4)
275                 if icmp.TypeCode.Type() != layers.ICMPv4TypeEchoRequest {
276                         fmt.Println("Not ICMPv4 echo request.")
277                         return nil, ErrUnhandledPacket
278                 }
279                 fmt.Println("Received an ICMPv4 echo request.")
280
281                 // Build packet layers.
282                 ethResp := layers.Ethernet{
283                         SrcMAC:       hwAddr,
284                         DstMAC:       eth.SrcMAC,
285                         EthernetType: layers.EthernetTypeIPv4,
286                 }
287                 ipv4Resp := layers.IPv4{
288                         Version:    4,
289                         IHL:        5,
290                         TOS:        0,
291                         Id:         0,
292                         Flags:      0,
293                         FragOffset: 0,
294                         TTL:        255,
295                         Protocol:   layers.IPProtocolICMPv4,
296                         SrcIP:      ipAddr,
297                         DstIP:      ipv4.SrcIP,
298                 }
299                 icmpResp := layers.ICMPv4{
300                         TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoReply, 0),
301                         Id:       icmp.Id,
302                         Seq:      icmp.Seq,
303                 }
304
305                 // Set up buffer and options for serialization.
306                 buf := gopacket.NewSerializeBuffer()
307                 opts := gopacket.SerializeOptions{
308                         FixLengths:       true,
309                         ComputeChecksums: true,
310                 }
311                 err := gopacket.SerializeLayers(buf, opts, &ethResp, &ipv4Resp, &icmpResp,
312                         gopacket.Payload(icmp.Payload))
313                 if err != nil {
314                         fmt.Println("SerializeLayers error: ", err)
315                 }
316                 return buf.Bytes(), nil
317         }
318
319         return nil, ErrUnhandledPacket
320 }
321
322 func main() {
323         var err error
324         fmt.Println("Starting 'icmp-responder' example...")
325
326         hwAddr, err = net.ParseMAC(MAC)
327         if err != nil {
328                 fmt.Printf("Failed to parse the MAC address: %v", err)
329                 return
330         }
331
332         ip := net.ParseIP(IPAddress)
333         if ip != nil {
334                 ipAddr = ip.To4()
335         }
336         if ipAddr == nil {
337                 fmt.Printf("Failed to parse the IP address: %v", err)
338                 return
339         }
340
341         // If run with the "--slave" option, create memif in the slave mode.
342         var isMaster = true
343         var appSuffix string
344         if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
345                 isMaster = false
346                 appSuffix = "-slave"
347         }
348
349         // Initialize libmemif first.
350         appName := "ICMP-Responder" + appSuffix
351         fmt.Println("Initializing libmemif as ", appName)
352         err = libmemif.Init(appName)
353         if err != nil {
354                 fmt.Printf("libmemif.Init() error: %v\n", err)
355                 return
356         }
357         // Schedule automatic cleanup.
358         defer libmemif.Cleanup()
359
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,
367         }
368
369         // Prepare memif1 configuration.
370         memifConfig := &libmemif.MemifConfig{
371                 MemifMeta: libmemif.MemifMeta{
372                         IfName:         "memif1",
373                         ConnID:         ConnectionID,
374                         SocketFilename: Socket,
375                         Secret:         Secret,
376                         IsMaster:       isMaster,
377                         Mode:           libmemif.IfModeEthernet,
378                 },
379                 MemifShmSpecs: libmemif.MemifShmSpecs{
380                         NumRxQueues:  NumQueues,
381                         NumTxQueues:  NumQueues,
382                         BufferSize:   2048,
383                         Log2RingSize: 10,
384                 },
385         }
386
387         fmt.Printf("Callbacks: %+v\n", memifCallbacks)
388         fmt.Printf("Config: %+v\n", memifConfig)
389
390         // Create memif1 interface.
391         memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
392         if err != nil {
393                 fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
394                 return
395         }
396         // Schedule automatic cleanup of the interface.
397         defer memif.Close()
398
399         // Wait until an interrupt signal is received.
400         sigChan := make(chan os.Signal, 1)
401         signal.Notify(sigChan, os.Interrupt)
402         var intErrch = memif.GetInterruptErrorChan()
403         select {
404         case err = <-intErrch:
405                 fmt.Printf("Exit due to interface error: %v\n", err)
406                 return
407         case <-sigChan:
408                 fmt.Printf("Exit by os.Interrupt")
409                 return
410         }
411 }