func NewRawReader(rdr io.Reader) ArchiveReader {
	return &rawReader{rdr: rdr}
}
NewRawReader wraps an io.Reader to create and ArchiveReader
package netlink
import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net"
	"time"
	"unsafe"
	"github.com/m-lab/go/logx"
	"github.com/m-lab/tcp-info/inetdiag"
	"github.com/m-lab/tcp-info/tcp"
)
type Metadata struct {
	UUID      string
	Sequence  int
	StartTime time.Time
}
type ArchivalRecord struct {
	Timestamp time.Time `json:",omitempty"`
	RawIDM inetdiag.RawInetDiagMsg `json:",omitempty"`  
	Attributes [][]byte `json:",omitempty"`  
	Metadata *Metadata `json:",omitempty"`
}
func ParseRouteAttr(b []byte) ([]NetlinkRouteAttr, error) {
	var attrs []NetlinkRouteAttr
	for len(b) >= SizeofRtAttr {
		a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
		if err != nil {
			return nil, err
		}
		ra := NetlinkRouteAttr{Attr: RtAttr(*a), Value: vbuf[:int(a.Len)-SizeofRtAttr]}
		attrs = append(attrs, ra)
		b = b[alen:]
	}
	return attrs, nil
}
func MakeArchivalRecord(msg *NetlinkMessage, skipLocal bool) (*ArchivalRecord, error) {
	if msg.Header.Type != 20 {
		return nil, ErrNotType20
	}
	raw, attrBytes := inetdiag.SplitInetDiagMsg(msg.Data)
	if raw == nil {
		return nil, ErrParseFailed
	}
	if skipLocal {
		idm, err := raw.Parse()
		if err != nil {
			return nil, err
		}
		if isLocal(idm.ID.SrcIP()) || isLocal(idm.ID.DstIP()) {
			return nil, nil
		}
	}
	record := ArchivalRecord{RawIDM: raw}
	attrs, err := ParseRouteAttr(attrBytes)
	if err != nil {
		return nil, err
	}
	maxAttrType := uint16(0)
	for _, a := range attrs {
		t := a.Attr.Type
		if t > maxAttrType {
			maxAttrType = t
		}
	}
	if maxAttrType > 2*inetdiag.INET_DIAG_MAX {
		maxAttrType = 2 * inetdiag.INET_DIAG_MAX
	}
	record.Attributes = make([][]byte, maxAttrType+1, maxAttrType+1)
	for _, a := range attrs {
		t := a.Attr.Type
		if t > maxAttrType {
			log.Println("Error!! Received RouteAttr with very large Type:", t)
			continue
		}
		if record.Attributes[t] != nil {
			log.Println("Parse error - Attribute appears more than once:", t)
		}
		record.Attributes[t] = a.Value
	}
	return &record, nil
}
type ChangeType int
const (
	NoMajorChange        ChangeType = iota
	IDiagStateChange                 
	NoTCPInfo                        
	NewAttribute                     
	LostAttribute                    
	AttributeLength                  
	StateOrCounterChange             
	PacketCountChange                
	PreviousWasNil                   
	Other                            
)
const (
	lastDataSentOffset  = unsafe.Offsetof(tcp.LinuxTCPInfo{}.LastDataSent)
	pmtuOffset          = unsafe.Offsetof(tcp.LinuxTCPInfo{}.PMTU)
	busytimeOffset      = unsafe.Offsetof(tcp.LinuxTCPInfo{}.BusyTime)
	bytesReceivedOffset = unsafe.Offsetof(tcp.LinuxTCPInfo{}.BytesReceived)  
	bytesSentOffset     = unsafe.Offsetof(tcp.LinuxTCPInfo{}.BytesSent)      
)
func isLocal(addr net.IP) bool {
	return addr.IsLoopback() || addr.IsLinkLocalUnicast() || addr.IsMulticast() || addr.IsUnspecified()
}
func (pm *ArchivalRecord) Compare(previous *ArchivalRecord) (ChangeType, error) {
	if previous == nil {
		return PreviousWasNil, nil
	}
	prevIDM, err := previous.RawIDM.Parse()
	if err != nil {
		return NoMajorChange, ErrParseFailed
	}
	pmIDM, err := pm.RawIDM.Parse()
	if err != nil {
		return NoMajorChange, ErrParseFailed
	}
	if prevIDM.IDiagState != pmIDM.IDiagState {
		return IDiagStateChange, nil
	}
	if len(previous.Attributes) <= inetdiag.INET_DIAG_INFO || len(pm.Attributes) <= inetdiag.INET_DIAG_INFO {
		return NoTCPInfo, nil
	}
	a := previous.Attributes[inetdiag.INET_DIAG_INFO]
	b := pm.Attributes[inetdiag.INET_DIAG_INFO]
	if a == nil || b == nil {
		return NoTCPInfo, nil
	}
	if 0 != bytes.Compare(a[pmtuOffset:busytimeOffset], b[pmtuOffset:busytimeOffset]) {
		return StateOrCounterChange, nil
	}
	if 0 != bytes.Compare(a[:lastDataSentOffset], b[:lastDataSentOffset]) {
		return StateOrCounterChange, nil
	}
	if len(previous.Attributes) < len(pm.Attributes) {
		return NewAttribute, nil
	}
	if len(previous.Attributes) > len(pm.Attributes) {
		return LostAttribute, nil
	}
	for tp := range previous.Attributes {
		if tp >= len(pm.Attributes) {
			return LostAttribute, nil
		}
		switch tp {
		case inetdiag.INET_DIAG_INFO:
		default:
			a := previous.Attributes[tp]
			b := pm.Attributes[tp]
			if a == nil && b != nil {
				return NewAttribute, nil
			}
			if a != nil && b == nil {
				return LostAttribute, nil
			}
			if a == nil && b == nil {
				continue
			}
			if len(a) != len(b) {
				return AttributeLength, nil
			}
			if 0 != bytes.Compare(a, b) {
				return Other, nil
			}
		}
	}
	return NoMajorChange, nil
}
func LoadRawNetlinkMessage(rdr io.Reader) (*NetlinkMessage, error) {
	var header NlMsghdr
	err := binary.Read(rdr, binary.LittleEndian, &header)
	if err != nil {
		return nil, err
	}
	data := make([]byte, header.Len-uint32(binary.Size(header)))
	err = binary.Read(rdr, binary.LittleEndian, data)
	if err != nil {
		return nil, err
	}
	return &NetlinkMessage{Header: header, Data: data}, nil
}
type ArchiveReader interface {
	Next() (*ArchivalRecord, error)
}
type rawReader struct {
	rdr io.Reader
}