mosers
/
eaas-vde-proxy
Archiviert
1
0
Fork 0
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
Dieses Repo ist archiviert. Du kannst Dateien sehen und es klonen, kannst aber nicht pushen oder Issues/Pull-Requests öffnen.

377 Zeilen
11 KiB
Go

package main
import (
"bytes"
"encoding/binary"
"flag"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
dhcp "github.com/krolaw/dhcp4"
log "github.com/sirupsen/logrus"
"io"
"net"
"os"
"proxy/cmd"
"proxy/util"
"time"
)
var OldMAC net.HardwareAddr
var NewMAC net.HardwareAddr
var OldIP net.IP
var NewIP net.IP
var VmReader io.Reader
var VmWriter io.Writer
var NetReader io.Reader
var NetWriter io.Writer
var Passthrough bool
// Start the two plugs and run two concurrent forward methods
func main() {
// Get command line arguments
logLvl := flag.Int("log", 4, "allowed: 5 (debug), 4 (info), 3 (warning), 2 (error), 1 (fatal)")
oldIP := flag.String("oldip", "", "IP before change")
newIP := flag.String("newip", "10.0.0.15", "IP after change")
oldMAC := flag.String("oldmac", "", "MAC before change")
newMAC := flag.String("newmac", "", "MAC after change")
passthrough := flag.Bool("passthrough", false, "Whether to pass every traffic through")
sockMain := flag.String("smain", "/run/vde/sw_main.sock", "Main switch sock path, - for stdin/out")
sockProxy := flag.String("sproxy", "/run/vde/sw_proxy1.sock", "Proxy switch sock path")
pidFile := flag.String("pidfile", "", "Location to write the pid to")
logFile := flag.String("logfile", "", "Location to write output to")
flag.Parse()
log.SetLevel(log.Level(*logLvl))
OldMAC, _ = net.ParseMAC(*oldMAC)
NewMAC = util.GenerateMac(*newMAC)
OldIP = net.ParseIP(*oldIP).To4()
NewIP = net.ParseIP(*newIP).To4()
Passthrough = *passthrough
log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
})
if *logFile != "" {
if f, err := os.OpenFile(*logFile, os.O_WRONLY | os.O_CREATE, 0755); err != nil {
log.Error("Error opening logFile ", *logFile)
} else {
log.SetOutput(f)
}
}
util.WritePIDFile(*pidFile)
var c1, c2 *cmd.Cmd
if *sockMain != "-" {
c1, NetReader, NetWriter = cmd.Start(*sockMain)
} else {
NetReader = os.Stdout
NetWriter = os.Stdin
}
c2, VmReader, VmWriter = cmd.Start(*sockProxy)
go pipeForward(cmd.In)
go pipeForward(cmd.Out)
if *sockMain != "-" {
c1.WaitH()
}
c2.WaitH()
}
// Reads from an input and writes to and output,
// do things to the content in between.
// Is meant to be run concurrently with "go pipeForward(...)"
func pipeForward(prefix string) {
var reader io.Reader
var writer io.Writer
if prefix == cmd.In {
reader = NetReader
writer = VmWriter
} else {
reader = VmReader
writer = NetWriter
}
for {
// Read frame length
frameLength := make([]byte, 2)
if _, err := reader.Read(frameLength); err == io.EOF {
log.Fatal(prefix, "Error reading frame length")
}
// Read actual frame
frameBytes := make([]byte, int(binary.BigEndian.Uint16(frameLength)))
if _, err := reader.Read(frameBytes); err == io.EOF {
log.Fatal(prefix, "Error reading frame data")
}
// Convert frame to full stack packet
packet := gopacket.NewPacket(frameBytes, layers.LayerTypeEthernet, gopacket.Default)
isInteresting := false // Debug Help
// Handle Ethernet frame
frame := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
log.Debug("Start packet")
filterMAC(prefix, &frame.DstMAC, &frame.SrcMAC, frame.LayerType())
// Handle IPv4 packet
if ipv4layer := packet.Layer(layers.LayerTypeIPv4); ipv4layer != nil {
ipv4Packet, _ := ipv4layer.(*layers.IPv4)
log.Debug("IP Protocol ", ipv4Packet.Protocol)
// Handle DHCPv4 packet (based on IPv4)
if dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4); dhcpLayer != nil && !Passthrough {
handleDHCP(dhcpLayer.LayerContents(), frame.DstMAC, prefix)
continue
}
filterIP(prefix, &ipv4Packet.DstIP, &ipv4Packet.SrcIP, ipv4Packet.LayerType())
// Handle ICMP packet (based on IPv4)
if icmpLayer := packet.Layer(layers.LayerTypeICMPv4); icmpLayer != nil {
icmpPacket, _ := icmpLayer.(*layers.ICMPv4)
log.Debug(prefix, "ICMP Type ", icmpPacket.TypeCode)
}
// Handle TCP packet
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcpPacket, _ := tcpLayer.(*layers.TCP)
if err := tcpPacket.SetNetworkLayerForChecksum(ipv4Packet); err != nil {
log.Error(prefix, "Error setting network layer for checksum", err)
}
}
}
// Drop IPv6 packets
if ipv6layer := packet.Layer(layers.LayerTypeIPv6); ipv6layer != nil && !Passthrough {
log.Info(prefix, "IPv6 packet dropped")
continue
}
// Handle ARP packet
if frame.EthernetType == layers.EthernetTypeARP {
arpPacket := packet.Layer(layers.LayerTypeARP).(*layers.ARP)
log.Debug(prefix, "ARP Type ", arpPacket.Operation)
filterIP(prefix, &arpPacket.DstProtAddress, &arpPacket.SourceProtAddress, arpPacket.LayerType())
filterMAC(prefix, &arpPacket.DstHwAddress, &arpPacket.SourceHwAddress, arpPacket.LayerType())
}
log.Debug("End packet")
// Forward original frame to other plug
if Passthrough {
writer.Write(frameLength)
writer.Write(frameBytes)
continue
}
// Serialize packet back to binary
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
if err := gopacket.SerializePacket(buf, opts, packet); err != nil {
log.Errorf("%s Error serializing packet to send\n%s\nSrc:\t%s\nDst:\t%s\n", prefix, err, frame.SrcMAC, frame.DstMAC)
continue
}
newFrameBytes := buf.Bytes()
newFrameLength := make([]byte, 2)
binary.BigEndian.PutUint16(newFrameLength, uint16(len(newFrameBytes)))
// Write interesting things to debug file
if isInteresting {
util.WriteBinary(fmt.Sprintf("/tmp/pck_%di.dat", time.Now().Unix()), frameBytes)
util.WriteBinary(fmt.Sprintf("/tmp/pck_%do.dat", time.Now().Unix()), newFrameBytes)
//util.WritePcapNg("xyz.pcap", packet.Data(), packet.Metadata().CaptureInfo)
}
// Forward modified frame to other plug
writer.Write(newFrameLength)
writer.Write(newFrameBytes)
}
}
// filterIP checks whether an IP target selected from src and dst equals a given value. If yes, it is changed
func filterIP(prefix string, dst interface{}, src interface{}, context gopacket.LayerType) {
var target interface{}
var condVal net.IP
var newVal net.IP
var which string
if prefix == cmd.In {
target = dst
which = "dst"
condVal = NewIP
newVal = OldIP
} else if prefix == cmd.Out {
target = src
which = "src"
condVal = OldIP
newVal = NewIP
}
ip, isIp := target.(*net.IP)
bs, isBs := target.(*[]byte)
// If no OldIP is set yet, get it from outgoing src field
if OldIP == nil {
if prefix == cmd.In {
return
} else if prefix == cmd.Out {
if isIp {
if!ip.IsGlobalUnicast() {
return
}
OldIP = *ip
} else if isBs {
OldIP = *bs
}
log.Info("OldIP set to ", OldIP)
condVal = OldIP
}
}
if isIp && bytes.Equal(*ip, condVal) {
*ip = newVal
log.Debugf("%s%s %s IP %s changed to %s", prefix, context, which, condVal, newVal)
}
if isBs && bytes.Equal(*bs, condVal) {
*bs = newVal
log.Debugf("%s%s %s IP %s changed to %s", prefix, context, which, condVal, newVal)
}
}
// filterMAC checks whether a MAC target selected from src and dst equals a given value. If yes, it is changed
func filterMAC(prefix string, dst interface{}, src interface{}, context gopacket.LayerType) {
// If no OldMAC is set yet, get it from outgoing src field
// Has to be HardwareAddr because this is used for ethernet frames which call this method first
if OldMAC == nil {
if prefix == cmd.In {
return
} else if prefix == cmd.Out {
OldMAC = *src.(*net.HardwareAddr)
log.Info("OldMAC set to ", OldMAC)
}
}
var target interface{}
var condVal net.HardwareAddr
var newVal net.HardwareAddr
var which string
if prefix == cmd.In {
target = dst
which = "dst"
condVal = NewMAC
newVal = OldMAC
} else if prefix == cmd.Out {
target = src
which = "src"
condVal = OldMAC
newVal = NewMAC
}
mac, isMac := target.(*net.HardwareAddr)
bs, isBs := target.(*[]byte)
if isMac && bytes.Equal(*mac, condVal) {
*mac = newVal
log.Debugf("%s%s %s MAC %s changed to %s", prefix, context, which, condVal, newVal)
}
if isBs && bytes.Equal(*bs, condVal) {
*bs = newVal
log.Debugf("%s%s %s MAC %s changed to %s", prefix, context, which, condVal, newVal)
}
}
func handleDHCP(content []byte, srcMAC net.HardwareAddr, prefix string) {
req := dhcp.Packet(content)
if req.HLen() > 16 { // Invalid size
log.Error(prefix, "Invalid DHCP size")
return
}
options := req.ParseOptions()
var reqType dhcp.MessageType
if t := options[dhcp.OptionDHCPMessageType]; len(t) != 1 {
log.Error(prefix, "Invalid DHCP message type")
return
} else {
reqType = dhcp.MessageType(t[0])
if reqType < dhcp.Discover || reqType > dhcp.Inform {
log.Error(prefix, "Invalid DHCP message type: ", reqType)
return
}
}
log.Debug(prefix, "DHCP message registered: ", reqType)
if prefix == cmd.Out {
switch reqType {
case dhcp.Discover:
if OldIP == nil {
log.Fatal(prefix, "DHCPDISCOVER but not previous address is known")
}
sendDHCPReply(req, dhcp.Offer, OldIP, options, srcMAC)
case dhcp.Request:
reqIP := net.IP(options[dhcp.OptionRequestedIPAddress])
if reqIP == nil {
reqIP = req.CIAddr()
}
if len(reqIP) != 4 || reqIP.Equal(net.IPv4zero) {
log.Error(prefix, "Invalid IP requested in DHCP: ", reqIP)
return
}
sendDHCPReply(req, dhcp.ACK, reqIP, options, srcMAC)
default:
return
}
} else {
// TODO: talk to dhcp server
}
}
// sendDHCPReply creates a response dhcp packet and sends it
func sendDHCPReply(req dhcp.Packet, mt dhcp.MessageType, lease net.IP, reqOpt dhcp.Options, dstMAC net.HardwareAddr) {
log.Info("Sending DHCP response: ", mt, lease)
// Getting the options
serverIP := []byte{10, 0, 0, 1} // TODO: serverIP
serverMAC := util.GenerateMac("") // TODO: server MAC
opt := dhcp.Options{
dhcp.OptionSubnetMask: []byte{255, 255, 255, 0}, // TODO: subnet mask
dhcp.OptionRouter: serverIP, // TODO: Presuming Server is also your router
dhcp.OptionDomainNameServer: serverIP, // TODO: Presuming Server is also your DNS server
}.SelectOrderOrAll(reqOpt[dhcp.OptionParameterRequestList])
// Creating the full packet layer by layer
dhcplayer := dhcp.ReplyPacket(req, mt, serverIP, lease, 24 * time.Hour, opt)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
ipv4 := layers.IPv4{
Version: 4,
TTL: 64,
Protocol: layers.IPProtocolUDP,
SrcIP: serverIP,
DstIP: lease,
}
udp := layers.UDP{
SrcPort: 67,
DstPort: 68,
}
udp.SetNetworkLayerForChecksum(&ipv4)
err := gopacket.SerializeLayers(buf, opts,
&layers.Ethernet{
SrcMAC: serverMAC,
DstMAC: dstMAC,
EthernetType: layers.EthernetTypeIPv4,
},
&ipv4,
&udp,
gopacket.Payload(dhcplayer))
if err != nil {
return
}
packetData := buf.Bytes()
// Sending layer through VM's pipe
packetLength := make([]byte, 2)
binary.BigEndian.PutUint16(packetLength, uint16(len(packetData)))
util.WriteBinary(fmt.Sprintf("/tmp/dhcp_%d.dat", time.Now().Unix()), packetData)
util.WritePcap(fmt.Sprintf("/tmp/dhcp_%d.pcap", time.Now().Unix()), packetData)
util.WritePcap(fmt.Sprintf("/tmp/dhcp_%d2.pcap", time.Now().Unix()), packetData[36:])
VmWriter.Write(packetLength)
VmWriter.Write(packetData)
}