Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add packetgen #31

Merged
merged 5 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"

Expand All @@ -23,6 +24,7 @@ import (

"github.com/Arriven/db1000n/logs"
"github.com/Arriven/db1000n/metrics"
"github.com/Arriven/db1000n/packetgen"
"github.com/Arriven/db1000n/slowloris"
"github.com/Arriven/db1000n/synfloodraw"
)
Expand All @@ -45,6 +47,7 @@ var jobs = map[string]job{
"udp": udpJob,
"syn-flood": synFloodJob,
"slow-loris": slowLoris,
"packetgen": packetgenJob,
}

// Config comment for linter
Expand Down Expand Up @@ -84,11 +87,16 @@ func parseByteTemplate(input []byte) []byte {

func parseStringTemplate(input string) string {
funcMap := template.FuncMap{
"random_uuid": randomUUID,
"random_int_n": rand.Intn,
"random_int": rand.Int,
"base64_encode": base64.StdEncoding.EncodeToString,
"base64_decode": base64.StdEncoding.DecodeString,
"random_uuid": randomUUID,
"random_int_n": rand.Intn,
"random_int": rand.Int,
"random_payload": packetgen.RandomPayload,
"random_ip": packetgen.RandomIP,
"random_port": packetgen.RandomPort,
"random_mac_addr": packetgen.RandomMacAddr,
"base64_encode": base64.StdEncoding.EncodeToString,
"base64_decode": base64.StdEncoding.DecodeString,
"json_encode": json.Marshal,
}
// TODO: consider adding ability to populate custom data
tmpl, err := template.New("test").Funcs(funcMap).Parse(input)
Expand Down Expand Up @@ -307,6 +315,47 @@ func slowLoris(ctx context.Context, l *logs.Logger, args JobArgs) error {
return slowloris.Start(l, jobConfig)
}

func packetgenJob(ctx context.Context, l *logs.Logger, args JobArgs) error {
type packetgenJobConfig struct {
BasicJobConfig
Packet json.RawMessage
Host string
Port string
}
var jobConfig packetgenJobConfig
err := json.Unmarshal(args, &jobConfig)
if err != nil {
l.Error("error parsing json: %v", err)
return err
}

host := parseStringTemplate(jobConfig.Host)
port, err := strconv.Atoi(parseStringTemplate(jobConfig.Port))
if err != nil {
l.Error("error parsing port: %v", err)
return err
}

trafficMonitor := metrics.Default.NewWriter(ctx, "traffic", uuid.New().String())

for jobConfig.Next(ctx) {
packetConfigBytes := parseByteTemplate(jobConfig.Packet)
var packetConfig packetgen.PacketConfig
err := json.Unmarshal(packetConfigBytes, &packetConfig)
if err != nil {
l.Error("error parsing json: %v", err)
return err
}
len, err := packetgen.SendPacket(packetConfig, host, port)
if err != nil {
l.Error("error sending packet: %v", err)
return err
}
trafficMonitor.Add(len)
}
return nil
}

func fetchConfig(configPath string) (*Config, error) {
var configBytes []byte
var err error
Expand Down
6 changes: 6 additions & 0 deletions packetgen/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package packetgen

const (
ipRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
dnsRegex = "^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$"
)
181 changes: 181 additions & 0 deletions packetgen/packetgen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package packetgen

import (
"net"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"golang.org/x/net/ipv4"
)

type PacketConfig struct {
Ethernet EthernetPacketConfig
IP IPPacketConfig
TCP *TCPPacketConfig
UDP *UDPPacketConfig
Payload string
}

func SendPacket(c PacketConfig, destinationHost string, destinationPort int) (int, error) {
var (
ipHeader *ipv4.Header
packetConn net.PacketConn
rawConn *ipv4.RawConn
udpPacket *layers.UDP
tcpPacket *layers.TCP
err error
)
destinationHost, err = resolveHost(destinationHost)
if err != nil {
return 0, err
}

ipPacket := buildIpPacket(c.IP)
if c.UDP != nil {
udpPacket = buildUdpPacket(*c.UDP)
if err = udpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil {
return 0, err
}
} else if c.TCP != nil {
tcpPacket = buildTcpPacket(*c.TCP)
if err = tcpPacket.SetNetworkLayerForChecksum(ipPacket); err != nil {
return 0, err
}
}

// Serialize. Note: we only serialize the TCP layer, because the
// socket we get with net.ListenPacket wraps our data in IPv4 packets
// already. We do still need the IP layer to compute checksums
// correctly, though.
ipHeaderBuf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}

if err = ipPacket.SerializeTo(ipHeaderBuf, opts); err != nil {
return 0, err
}

if ipHeader, err = ipv4.ParseHeader(ipHeaderBuf.Bytes()); err != nil {
return 0, err
}

ethernetLayer := buildEthernetPacket(c.Ethernet)
payloadBuf := gopacket.NewSerializeBuffer()
pyl := gopacket.Payload(c.Payload)

if udpPacket != nil {
if err = gopacket.SerializeLayers(payloadBuf, opts, ethernetLayer, udpPacket, pyl); err != nil {
return 0, err
}

// XXX send packet
if packetConn, err = net.ListenPacket("ip4:udp", "0.0.0.0"); err != nil {
return 0, err
}
} else if tcpPacket != nil {
if err = gopacket.SerializeLayers(payloadBuf, opts, ethernetLayer, tcpPacket, pyl); err != nil {
return 0, err
}

// XXX send packet
if packetConn, err = net.ListenPacket("ip4:tcp", "0.0.0.0"); err != nil {
return 0, err
}
}

if rawConn, err = ipv4.NewRawConn(packetConn); err != nil {
return 0, err
}

if err = rawConn.WriteTo(ipHeader, payloadBuf.Bytes(), nil); err != nil {
return 0, err
}
return len(c.Payload), nil
}

type IPPacketConfig struct {
SrcIP string `json:"src_ip"`
DstIP string `json:"dst_ip"`
}

// buildIpPacket generates a layers.IPv4 and returns it with source IP address and destination IP address
func buildIpPacket(c IPPacketConfig) *layers.IPv4 {
return &layers.IPv4{
SrcIP: net.ParseIP(c.SrcIP).To4(),
DstIP: net.ParseIP(c.DstIP).To4(),
Version: 4,
Protocol: layers.IPProtocolTCP,
}
}

type UDPPacketConfig struct {
SrcPort int `json:"src_port,string"`
DstPort int `json:"dst_port,string"`
}

func buildUdpPacket(c UDPPacketConfig) *layers.UDP {
return &layers.UDP{
SrcPort: layers.UDPPort(c.SrcPort),
DstPort: layers.UDPPort(c.DstPort),
}
}

type TCPFlagsConfig struct {
SYN bool
ACK bool
FIN bool
RST bool
PSH bool
URG bool
ECE bool
CWR bool
NS bool
}

type TCPPacketConfig struct {
SrcPort int `json:"src_port,string"`
DstPort int `json:"dst_port,string"`
Seq uint32
Ack uint32
Window uint16
Urgent uint16
Flags TCPFlagsConfig
}

// buildTcpPacket generates a layers.TCP and returns it with source port and destination port
func buildTcpPacket(c TCPPacketConfig) *layers.TCP {
return &layers.TCP{
SrcPort: layers.TCPPort(c.SrcPort),
DstPort: layers.TCPPort(c.DstPort),
Window: c.Window,
Urgent: c.Urgent,
Seq: c.Seq,
Ack: c.Ack,
SYN: c.Flags.SYN,
ACK: c.Flags.ACK,
FIN: c.Flags.FIN,
RST: c.Flags.RST,
PSH: c.Flags.PSH,
URG: c.Flags.URG,
ECE: c.Flags.ECE,
CWR: c.Flags.CWR,
NS: c.Flags.NS,
}
}

type EthernetPacketConfig struct {
SrcMAC string `json:"src_mac"`
DstMAC string `json:"dst_mac"`
}

// buildEthernetPacket generates an layers.Ethernet and returns it with source MAC address and destination MAC address
func buildEthernetPacket(c EthernetPacketConfig) *layers.Ethernet {
srcMac := net.HardwareAddr(c.SrcMAC)
dstMac := net.HardwareAddr(c.DstMAC)
return &layers.Ethernet{
SrcMAC: net.HardwareAddr{srcMac[0], srcMac[1], srcMac[2], srcMac[3], srcMac[4], srcMac[5]},
DstMAC: net.HardwareAddr{dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5]},
}
}
Loading