ODPM 266: Go-libmemif + 2 examples.
[govpp.git] / vendor / github.com / google / gopacket / ip4defrag / defrag.go
diff --git a/vendor/github.com/google/gopacket/ip4defrag/defrag.go b/vendor/github.com/google/gopacket/ip4defrag/defrag.go
new file mode 100644 (file)
index 0000000..9d3862f
--- /dev/null
@@ -0,0 +1,350 @@
+// Copyright 2013 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// Package ip4defrag implements a IPv4 defragmenter
+package ip4defrag
+
+import (
+       "container/list"
+       "errors"
+       "fmt"
+       "log"
+       "sync"
+       "time"
+
+       "github.com/google/gopacket"
+       "github.com/google/gopacket/layers"
+)
+
+// Quick and Easy to use debug code to trace
+// how defrag works.
+var debug debugging = false // or flip to true
+type debugging bool
+
+func (d debugging) Printf(format string, args ...interface{}) {
+       if d {
+               log.Printf(format, args...)
+       }
+}
+
+// Constants determining how to handle fragments.
+const (
+       IPv4MinimumFragmentSize    = 576   // Minimum size of a single fragment
+       IPv4MaximumSize            = 65535 // Maximum size of a fragment (2^16)
+       IPv4MaximumFragmentOffset  = 8189  // Maximum offset of a fragment
+       IPv4MaximumFragmentListLen = 8     // Back out if we get more than this many fragments
+)
+
+// DefragIPv4 takes in an IPv4 packet with a fragment payload.
+//
+// It do not modify the IPv4 layer in place, 'in' remains untouched
+// It returns a ready-to be used IPv4 layer.
+//
+// If the passed-in IPv4 layer is NOT fragmented, it will
+// immediately return it without modifying the layer.
+//
+// If the IPv4 layer is a fragment and we don't have all
+// fragments, it will return nil and store whatever internal
+// information it needs to eventually defrag the packet.
+//
+// If the IPv4 layer is the last fragment needed to reconstruct
+// the packet, a new IPv4 layer will be returned, and will be set to
+// the entire defragmented packet,
+//
+// It use a map of all the running flows
+//
+// Usage example:
+//
+// func HandlePacket(in *layers.IPv4) err {
+//     defragger := ip4defrag.NewIPv4Defragmenter()
+//     in, err := defragger.DefragIPv4(in)
+//     if err != nil {
+//         return err
+//     } else if in == nil {
+//         return nil  // packet fragment, we don't have whole packet yet.
+//     }
+//     // At this point, we know that 'in' is defragmented.
+//     //It may be the same 'in' passed to
+//        // HandlePacket, or it may not, but we don't really care :)
+//        ... do stuff to 'in' ...
+//}
+//
+func (d *IPv4Defragmenter) DefragIPv4(in *layers.IPv4) (*layers.IPv4, error) {
+       return d.DefragIPv4WithTimestamp(in, time.Now())
+}
+
+// DefragIPv4WithTimestamp provides functionality of DefragIPv4 with
+// an additional timestamp parameter which is used for discarding
+// old fragments instead of time.Now()
+//
+// This is useful when operating on pcap files instead of live captured data
+//
+func (d *IPv4Defragmenter) DefragIPv4WithTimestamp(in *layers.IPv4, t time.Time) (*layers.IPv4, error) {
+       // check if we need to defrag
+       if st := d.dontDefrag(in); st == true {
+               debug.Printf("defrag: do nothing, do not need anything")
+               return in, nil
+       }
+       // perfom security checks
+       st, err := d.securityChecks(in)
+       if err != nil || st == false {
+               debug.Printf("defrag: alert security check")
+               return nil, err
+       }
+
+       // ok, got a fragment
+       debug.Printf("defrag: got a new fragment in.Id=%d in.FragOffset=%d in.Flags=%d\n",
+               in.Id, in.FragOffset*8, in.Flags)
+
+       // have we already seen a flow between src/dst with that Id?
+       ipf := newIPv4(in)
+       var fl *fragmentList
+       var exist bool
+       d.Lock()
+       fl, exist = d.ipFlows[ipf]
+       if !exist {
+               debug.Printf("defrag: unknown flow, creating a new one\n")
+               fl = new(fragmentList)
+               d.ipFlows[ipf] = fl
+       }
+       d.Unlock()
+       // insert, and if final build it
+       out, err2 := fl.insert(in, t)
+
+       // at last, if we hit the maximum frag list len
+       // without any defrag success, we just drop everything and
+       // raise an error
+       if out == nil && fl.List.Len()+1 > IPv4MaximumFragmentListLen {
+               d.flush(ipf)
+               return nil, fmt.Errorf("defrag: Fragment List hits its maximum"+
+                       "size(%d), without success. Flushing the list",
+                       IPv4MaximumFragmentListLen)
+       }
+
+       // if we got a packet, it's a new one, and he is defragmented
+       if out != nil {
+               // when defrag is done for a flow between two ip
+               // clean the list
+               d.flush(ipf)
+               return out, nil
+       }
+       return nil, err2
+}
+
+// DiscardOlderThan forgets all packets without any activity since
+// time t. It returns the number of FragmentList aka number of
+// fragment packets it has discarded.
+func (d *IPv4Defragmenter) DiscardOlderThan(t time.Time) int {
+       var nb int
+       d.Lock()
+       for k, v := range d.ipFlows {
+               if v.LastSeen.Before(t) {
+                       nb = nb + 1
+                       delete(d.ipFlows, k)
+               }
+       }
+       d.Unlock()
+       return nb
+}
+
+// flush the fragment list for a particular flow
+func (d *IPv4Defragmenter) flush(ipf ipv4) {
+       d.Lock()
+       fl := new(fragmentList)
+       d.ipFlows[ipf] = fl
+       d.Unlock()
+}
+
+// dontDefrag returns true if the IPv4 packet do not need
+// any defragmentation
+func (d *IPv4Defragmenter) dontDefrag(ip *layers.IPv4) bool {
+       // don't defrag packet with DF flag
+       if ip.Flags&layers.IPv4DontFragment != 0 {
+               return true
+       }
+       // don't defrag not fragmented ones
+       if ip.Flags&layers.IPv4MoreFragments == 0 && ip.FragOffset == 0 {
+               return true
+       }
+       return false
+}
+
+// securityChecks performs the needed security checks
+func (d *IPv4Defragmenter) securityChecks(ip *layers.IPv4) (bool, error) {
+       // don't allow too big fragment offset
+       if ip.FragOffset > IPv4MaximumFragmentOffset {
+               return false, fmt.Errorf("defrag: fragment offset too big "+
+                       "(handcrafted? %d > %d)", ip.FragOffset, IPv4MaximumFragmentOffset)
+       }
+       fragOffset := ip.FragOffset * 8
+
+       // don't allow fragment that would oversize an IP packet
+       if fragOffset+ip.Length > IPv4MaximumSize {
+               return false, fmt.Errorf("defrag: fragment will overrun "+
+                       "(handcrafted? %d > %d)", ip.FragOffset*8+ip.Length, IPv4MaximumSize)
+       }
+
+       return true, nil
+}
+
+// fragmentList holds a container/list used to contains IP
+// packets/fragments.  It stores internal counters to track the
+// maximum total of byte, and the current length it has received.
+// It also stores a flag to know if he has seen the last packet.
+type fragmentList struct {
+       List          list.List
+       Highest       uint16
+       Current       uint16
+       FinalReceived bool
+       LastSeen      time.Time
+}
+
+// insert insert an IPv4 fragment/packet into the Fragment List
+// It use the following strategy : we are inserting fragment based
+// on their offset, latest first. This is sometimes called BSD-Right.
+// See: http://www.sans.org/reading-room/whitepapers/detection/ip-fragment-reassembly-scapy-33969
+func (f *fragmentList) insert(in *layers.IPv4, t time.Time) (*layers.IPv4, error) {
+       // TODO: should keep a copy of *in in the list
+       // or not (ie the packet source is reliable) ? -> depends on Lazy / last packet
+       fragOffset := in.FragOffset * 8
+       if fragOffset >= f.Highest {
+               f.List.PushBack(in)
+       } else {
+               for e := f.List.Front(); e != nil; e = e.Next() {
+                       frag, _ := e.Value.(*layers.IPv4)
+                       if in.FragOffset == frag.FragOffset {
+                               // TODO: what if we receive a fragment
+                               // that begins with duplicate data but
+                               // *also* has new data? For example:
+                               //
+                               // AAAA
+                               //     BB
+                               //     BBCC
+                               //         DDDD
+                               //
+                               // In this situation we completely
+                               // ignore CC and the complete packet can
+                               // never be reassembled.
+                               debug.Printf("defrag: ignoring frag %d as we already have it (duplicate?)\n",
+                                       fragOffset)
+                               return nil, nil
+                       }
+                       if in.FragOffset < frag.FragOffset {
+                               debug.Printf("defrag: inserting frag %d before existing frag %d\n",
+                                       fragOffset, frag.FragOffset*8)
+                               f.List.InsertBefore(in, e)
+                               break
+                       }
+               }
+       }
+
+       f.LastSeen = t
+
+       fragLength := in.Length - 20
+       // After inserting the Fragment, we update the counters
+       if f.Highest < fragOffset+fragLength {
+               f.Highest = fragOffset + fragLength
+       }
+       f.Current = f.Current + fragLength
+
+       debug.Printf("defrag: insert ListLen: %d Highest:%d Current:%d\n",
+               f.List.Len(),
+               f.Highest, f.Current)
+
+       // Final Fragment ?
+       if in.Flags&layers.IPv4MoreFragments == 0 {
+               f.FinalReceived = true
+       }
+       // Ready to try defrag ?
+       if f.FinalReceived && f.Highest == f.Current {
+               return f.build(in)
+       }
+       return nil, nil
+}
+
+// Build builds the final datagram, modifying ip in place.
+// It puts priority to packet in the early position of the list.
+// See Insert for more details.
+func (f *fragmentList) build(in *layers.IPv4) (*layers.IPv4, error) {
+       var final []byte
+       var currentOffset uint16
+
+       debug.Printf("defrag: building the datagram \n")
+       for e := f.List.Front(); e != nil; e = e.Next() {
+               frag, _ := e.Value.(*layers.IPv4)
+               if frag.FragOffset*8 == currentOffset {
+                       debug.Printf("defrag: building - adding %d\n", frag.FragOffset*8)
+                       final = append(final, frag.Payload...)
+                       currentOffset = currentOffset + frag.Length - 20
+               } else if frag.FragOffset*8 < currentOffset {
+                       // overlapping fragment - let's take only what we need
+                       startAt := currentOffset - frag.FragOffset*8
+                       debug.Printf("defrag: building - overlapping, starting at %d\n",
+                               startAt)
+                       if startAt > frag.Length-20 {
+                               return nil, errors.New("defrag: building - invalid fragment")
+                       }
+                       final = append(final, frag.Payload[startAt:]...)
+                       currentOffset = currentOffset + frag.FragOffset*8
+               } else {
+                       // Houston - we have an hole !
+                       debug.Printf("defrag: hole found while building, " +
+                               "stopping the defrag process\n")
+                       return nil, errors.New("defrag: building - hole found")
+               }
+               debug.Printf("defrag: building - next is %d\n", currentOffset)
+       }
+
+       // TODO recompute IP Checksum
+       out := &layers.IPv4{
+               Version:    in.Version,
+               IHL:        in.IHL,
+               TOS:        in.TOS,
+               Length:     f.Highest,
+               Id:         0,
+               Flags:      0,
+               FragOffset: 0,
+               TTL:        in.TTL,
+               Protocol:   in.Protocol,
+               Checksum:   0,
+               SrcIP:      in.SrcIP,
+               DstIP:      in.DstIP,
+               Options:    in.Options,
+               Padding:    in.Padding,
+       }
+       out.Payload = final
+
+       return out, nil
+}
+
+// ipv4 is a struct to be used as a key.
+type ipv4 struct {
+       ip4 gopacket.Flow
+       id  uint16
+}
+
+// newIPv4 returns a new initialized IPv4 Flow
+func newIPv4(ip *layers.IPv4) ipv4 {
+       return ipv4{
+               ip4: ip.NetworkFlow(),
+               id:  ip.Id,
+       }
+}
+
+// IPv4Defragmenter is a struct which embedded a map of
+// all fragment/packet.
+type IPv4Defragmenter struct {
+       sync.RWMutex
+       ipFlows map[ipv4]*fragmentList
+}
+
+// NewIPv4Defragmenter returns a new IPv4Defragmenter
+// with an initialized map.
+func NewIPv4Defragmenter() *IPv4Defragmenter {
+       return &IPv4Defragmenter{
+               ipFlows: make(map[ipv4]*fragmentList),
+       }
+}