func TestAssociatePGPackets(t *testing.T) {
	metric := &metrics.QueryMetric{
		Query: "select * from table",
		QueryNetUniqueIDs: []*metrics.QueryNetUniqueID{
			&metrics.QueryNetUniqueID{
				SrcIP: net.IPv4(111, 111, 111, 111),
				Syn:   uint32(43212),
			},
		},
	}

	combinedQueryMetrics := metrics.NewQueryMetrics()
	combinedQueryMetrics.Add(metric)

	responses := []*ResponsePacket{
		&ResponsePacket{
			DstIP: net.IPv4(111, 111, 111, 111),
			Ack:   uint32(43212),
			Size:  uint64(1000),
		},
		&ResponsePacket{
			DstIP: net.IPv4(111, 111, 111, 111),
			Ack:   uint32(43212),
			Size:  uint64(1000),
		},
	}

	AssociatePGPackets(combinedQueryMetrics, responses)

	assert.Equal(t, uint64(2), combinedQueryMetrics.List[0].TotalResponsePackets)
	assert.Equal(t, uint64(2000), combinedQueryMetrics.List[0].TotalNetworkLoad)
}
Exemple #2
0
func ExtractPGPackets(handle *pcap.Handle) (*metrics.QueryMetrics, []*ResponsePacket) {
	combinedQueryMetrics := metrics.NewQueryMetrics()
	responses := []*ResponsePacket{}
	// Sorts packets into queries or responses
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	var raw string
	for {
		packet, err := packetSource.NextPacket()
		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Println("Error: ", err)
			continue
		}

		ipLayer := packet.Layer(layers.LayerTypeIPv4)
		if ipLayer == nil {
			continue
		}
		ip, _ := ipLayer.(*layers.IPv4)

		tcpLayer := packet.Layer(layers.LayerTypeTCP)
		if tcpLayer == nil {
			continue
		}
		tcp, _ := tcpLayer.(*layers.TCP)

		// If the destination port is 5432...
		if tcp.DstPort == 5432 {
			// And the packet payload starts with P...
			raw = fmt.Sprintf("%s", tcp.Payload)
			if strings.HasPrefix(raw, "P") {
				// It is a (Parse) packet that contains a Query
				combinedQueryMetrics.Add(
					metrics.New(
						NormalizeQuery(raw),
						1,
						ip.SrcIP,
						tcp.Seq,
					),
				)
			}
		} else if tcp.SrcPort == 5432 && tcp.ACK {
			responses = append(responses, &ResponsePacket{
				DstIP: ip.DstIP,
				Ack:   tcp.Ack,
				Size:  uint64(len(tcp.Payload)),
			})
		}
	}
	return combinedQueryMetrics, responses
}
Exemple #3
0
func main() {
	app := cli.NewApp()
	app.Name = "pgnetdetective"
	app.Version = "0.1"
	app.Usage = "Analyze Postgres Network Traffic Captures"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "bytes",
			Usage: "Display bytes instead of as Human-Readable",
		},
		cli.StringFlag{
			Name:  "output",
			Value: "text",
			Usage: "Specify output file format: [text|json|csv]",
		},
		cli.IntFlag{
			Name:  "limit",
			Value: 0,
			Usage: "Limit output based on NetworkLoad size in kilobytes",
		},
	}

	app.Action = func(c *cli.Context) {
		if len(c.Args()) != 1 {
			cli.ShowAppHelp(c)
			os.Exit(0)
		}
		path := c.Args()[0]

		// Open the .cap file
		handle, err := pcap.OpenOffline(path)
		if err != nil {
			panic(err)
		}

		combinedQueryMetrics, responses := processing.ExtractPGPackets(handle)

		processing.AssociatePGPackets(combinedQueryMetrics, responses)

		if c.Int("limit") > 0 {
			limitedQueryMetrics := metrics.NewQueryMetrics()
			limit := uint64(c.Int("limit"))
			for _, m := range combinedQueryMetrics.List {
				if m.TotalNetworkLoad/1000 >= limit {
					limitedQueryMetrics.List = append(limitedQueryMetrics.List, m)
				}
			}
			combinedQueryMetrics = limitedQueryMetrics
		}

		combinedQueryMetrics.DisplayBytes = c.Bool("bytes")

		sort.Sort(combinedQueryMetrics)

		if c.String("output") == "json" {
			out, err := json.Marshal(combinedQueryMetrics)
			if err != nil {
				panic(err)
			}
			os.Stdout.Write(out)
		} else if c.String("output") == "csv" {
			w := csv.NewWriter(os.Stdout)
			w.WriteAll(combinedQueryMetrics.CsvString())

			if err := w.Error(); err != nil {
				panic(err)
			}
		} else {
			combinedQueryMetrics.PrintText()
		}
	}

	app.Run(os.Args)
}