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