Skip to content

Commit

Permalink
Merge pull request #85 from synfinatic/pcap-debug
Browse files Browse the repository at this point in the history
Add writing pcap files for debugging
  • Loading branch information
synfinatic authored Feb 22, 2022
2 parents b79b4a6 + b31a4e7 commit 52a3839
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 162 deletions.
8 changes: 1 addition & 7 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,4 @@ jobs:
run: make

- name: Run tests
run: make test

- name: Check Go Format
run: make test-fmt

- name: Check go.mod is tidy
run: make test-tidy
run: make test test-fmt test-tidy
43 changes: 43 additions & 0 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: golangci-lint
on:
push:
branches: [main]
pull_request:
# The branches below must be a subset of the branches above
branches: [main]

permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v1.43.0

# Optional: working directory, useful for monorepos
# working-directory: somedir

# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0

# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

# Optional: if set to true then the action will use pre-installed Go.
# skip-go-installation: true

# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true

# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
*.pcap

# Test binary, built with `go test -c`
*.test
Expand Down
12 changes: 12 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
linters:
enable:
- asciicheck
- ineffassign
- gocyclo
- dupl
# - funlen
- gofmt
- gosec
- misspell
- whitespace
# - unparam
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

## Unreleased

## v0.0.9 - 2022-01-10
## v0.0.9 - Unreleased

Added:

- Linux/ARMv5 support
- Add support for writing pcap files for debugging #79

Changed:

Expand All @@ -15,6 +16,9 @@ Changed:
- --cache-ttl is now 3 hours
- ARMv6/v7 now have unique binaries and use hardware floating point
- No more "arm32" builds which isn't a real ARM arch
- Remove str2pcap since we have direct pcap support now
- Switch from pflag to Kong for CLI arg parsing
- Update to logrus 1.8.1

## v0.0.8 - 2021-11-07

Expand Down
19 changes: 10 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,9 @@ LDFLAGS := -X "main.Version=$(PROJECT_VERSION)" -X "main.Delta=$(PROJ
LDFLAGS += -X "main.Buildinfos=$(BUILDINFOS)" -X "main.Tag=$(PROJECT_TAG)"
LDFLAGS += -X "main.CommitID=$(PROJECT_COMMIT)" -s -w
OUTPUT_NAME := $(DIST_DIR)$(PROJECT_NAME)-$(GOOS)-$(GOARCH)
STR2PCAP_NAME := $(DIST_DIR)str2pcap-$(PROJECT_VERSION)-$(GOOS)-$(GOARCH)
DOCKER_VERSION ?= v$(PROJECT_VERSION)

ALL: $(OUTPUT_NAME) str2pcap ## Build our current str2pcap platform binary

str2pcap: $(STR2PCAP_NAME)

$(STR2PCAP_NAME): str2pcap/*.go
go build -o $(STR2PCAP_NAME) str2pcap/*.go
ALL: $(OUTPUT_NAME)

include help.mk # place after ALL target and before all other targets

Expand All @@ -44,6 +38,10 @@ release: build-release ## Build and sign official release

build-release: clean linux-amd64 linux-mips64 linux-arm darwin-amd64 freebsd docker ## Build our release binaries

tags: cmd/*.go ## Create tags file for vim, etc
@echo Make sure you have Universal Ctags installed: https://github.com/universal-ctags/ctags
ctags -R

.PHONY: run
run: cmd/*.go ## build and run udp-proxy-2020 using $UDP_PROXY_2020_ARGS
sudo go run cmd/*.go $(UDP_PROXY_2020_ARGS)
Expand Down Expand Up @@ -108,7 +106,10 @@ test-tidy: ## Test to make sure go.mod is tidy
exit -1 ; \
fi

precheck: test test-fmt test-tidy ## Run all tests that happen in a PR
precheck: test test-fmt test-tidy lint ## Run all tests that happen in a PR

lint: ## Run golangci-lint
golangci-lint run

######################################################################
# Linux targets for building Linux in Docker
Expand Down Expand Up @@ -339,7 +340,7 @@ docker-shell: ## Get a shell in the docker image
$(DOCKER_REPO)/$(PROJECT_NAME):$(DOCKER_VERSION) \
/bin/sh

docker-release: docker ## Tag and push docker images Linux AMD64/ARM64
docker-release: ## Tag and push docker images Linux AMD64/ARM64
docker buildx build \
-t $(DOCKER_REPO)/$(PROJECT_NAME):$(DOCKER_VERSION) \
-t $(DOCKER_REPO)/$(PROJECT_NAME):latest \
Expand Down
90 changes: 82 additions & 8 deletions cmd/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package main

import (
"encoding/binary"
"encoding/hex"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"sync"
"time"
Expand All @@ -12,6 +14,7 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/google/gopacket/pcapgo"
log "github.com/sirupsen/logrus"
)

Expand All @@ -25,9 +28,12 @@ type Listen struct {
ipaddr string // dstip we send packets to
promisc bool // do we enable promisc on this interface?
handle *pcap.Handle // gopacket.pcap handle
writer *pcapgo.Writer // in and outbound write packet handle
inwriter *pcapgo.Writer // inbound write packet handle
outwriter *pcapgo.Writer // outbound write packet handle
timeout time.Duration // timeout for loop
clientTTL time.Duration // ttl for client cache
sendpkt chan Send // channel used to recieve packets we need to send
sendpkt chan Send // channel used to receive packets we need to send
clients map[string]time.Time // keep track of clients for non-promisc interfaces
}

Expand Down Expand Up @@ -94,6 +100,37 @@ func newListener(netif *net.Interface, promisc bool, ports []int32, to time.Dura
return new
}

type Direction string

const (
In Direction = "in"
Out Direction = "out"
InOut Direction = "inout"
)

// OpenWrite will open the write file pcap handle
func (l *Listen) OpenWriter(path string, dir Direction) (string, error) {
var err error
fName := fmt.Sprintf("udp-proxy-%s-%s.pcap", dir, l.iname)
filePath := filepath.Join(path, fName)
f, err := os.Create(filePath)
if err != nil {
return fName, err
}
switch dir {
case "in":
l.inwriter = pcapgo.NewWriter(f)
return fName, l.inwriter.WriteFileHeader(65536, l.handle.LinkType())
case "out":
l.outwriter = pcapgo.NewWriter(f)
return fName, l.outwriter.WriteFileHeader(65536, l.handle.LinkType())
case "inout":
l.writer = pcapgo.NewWriter(f)
return fName, l.writer.WriteFileHeader(65536, l.handle.LinkType())
}
return fName, fmt.Errorf("Invalid direction: %s", dir)
}

// Our goroutine for processing packets
func (l *Listen) handlePackets(s *SendPktFeed, wg *sync.WaitGroup) {
// add ourself as a sender
Expand Down Expand Up @@ -128,6 +165,25 @@ func (l *Listen) handlePackets(s *SendPktFeed, wg *sync.WaitGroup) {

log.Debugf("%s: received packet and fowarding onto other interfaces", l.iname)
s.Send(packet, l.iname, l.handle.LinkType())

// write to pcap?
if l.inwriter != nil {
md := packet.Metadata()
ci := gopacket.CaptureInfo{
Timestamp: md.Timestamp,
CaptureLength: md.CaptureLength,
Length: md.Length,
InterfaceIndex: md.InterfaceIndex,
AncillaryData: md.AncillaryData,
}
if err := l.inwriter.WritePacket(ci, packet.Data()); err != nil {
log.WithError(err).Warnf("Unable to write packet to pcap file")
}
if err := l.writer.WritePacket(ci, packet.Data()); err != nil {
log.WithError(err).Warnf("Unable to write packet to pcap file")
}
}

case <-ticker: // our timer
log.Debugf("handlePackets(%s) ticker", l.iname)
// clean client cache
Expand Down Expand Up @@ -190,7 +246,7 @@ func (l *Listen) sendPackets(sndpkt Send) {
if !l.promisc {
// send one packet to broadcast IP
dstip := net.ParseIP(l.ipaddr).To4()
if err, bytes := l.sendPacket(dstip, eth, loop, ip4, udp, payload); err != nil {
if err, bytes := l.sendPacket(sndpkt, dstip, eth, loop, ip4, udp, payload); err != nil {
log.Warnf("Unable to send %d bytes from %s out %s: %s",
bytes, sndpkt.srcif, l.iname, err)
}
Expand All @@ -199,17 +255,17 @@ func (l *Listen) sendPackets(sndpkt Send) {
if len(l.clients) == 0 {
log.Debugf("%s: Unable to send packet; no discovered clients", l.iname)
}
for ip, _ := range l.clients {
for ip := range l.clients {
dstip := net.ParseIP(ip).To4()
if err, bytes := l.sendPacket(dstip, eth, loop, ip4, udp, payload); err != nil {
if err, bytes := l.sendPacket(sndpkt, dstip, eth, loop, ip4, udp, payload); err != nil {
log.Warnf("Unable to send %d bytes from %s out %s: %s",
bytes, sndpkt.srcif, l.iname, err)
}
}
}
}

func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopback,
func (l *Listen) sendPacket(sndpkt Send, dstip net.IP, eth layers.Ethernet, loop layers.Loopback,
ip4 layers.IPv4, udp layers.UDP, payload gopacket.Payload) (error, int) {
// Build our packet to send
buffer := gopacket.NewSerializeBuffer()
Expand Down Expand Up @@ -288,8 +344,26 @@ func (l *Listen) sendPacket(dstip net.IP, eth layers.Ethernet, loop layers.Loopb
}

outgoingPacket := buffer.Bytes()
log.Debugf("%s => %s: packet len: %d: %s",
l.iname, dstip.String(), len(outgoingPacket), hex.EncodeToString(outgoingPacket))
log.Debugf("%s => %s: packet len: %d", l.iname, dstip.String(), len(outgoingPacket))

// write to pcap?
if l.outwriter != nil {
md := sndpkt.packet.Metadata()
ci := gopacket.CaptureInfo{
Timestamp: md.Timestamp,
CaptureLength: len(outgoingPacket),
Length: len(outgoingPacket),
InterfaceIndex: md.InterfaceIndex,
AncillaryData: md.AncillaryData,
}
if err := l.outwriter.WritePacket(ci, outgoingPacket); err != nil {
log.WithError(err).Warnf("Unable to write packet to pcap file")
}
if err := l.writer.WritePacket(ci, outgoingPacket); err != nil {
log.WithError(err).Warnf("Unable to write packet to pcap file")
}
}

return l.handle.WritePacketData(outgoingPacket), len(outgoingPacket)
}

Expand Down
Loading

0 comments on commit 52a3839

Please sign in to comment.