package main
import (
"encoding/binary"
"flag"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
log "github.com/sirupsen/logrus"
"io"
"net"
"proxy/cmd"
"proxy/util"
"strconv"
"time"
)
var OldMac net . HardwareAddr
var NewMac net . HardwareAddr
var OldIP net . IP
var NewIP net . IP
// 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" , "10.0.0.11" , "IP before change" )
newip := flag . String ( "newip" , "10.0.0.15" , "IP after change" )
oldmac := flag . String ( "oldmac" , "52:54:00:12:34:56" , "MAC before change" )
newmac := flag . String ( "newmac" , "52:54:00:12:34:aa" , "MAC after change" )
passthrough := flag . Bool ( "passthrough" , false , "Whether to pass every traffic through" )
proxy := flag . String ( "proxy" , "1" , "Number of the proxy switch" )
flag . Parse ( )
log . SetLevel ( log . Level ( * logLvl ) )
OldMac , _ = net . ParseMAC ( * oldmac )
NewMac , _ = net . ParseMAC ( * newmac )
OldIP = net . ParseIP ( * oldip ) . To4 ( )
NewIP = net . ParseIP ( * newip ) . To4 ( )
log . SetFormatter ( & log . TextFormatter {
DisableTimestamp : true ,
} )
c1 := cmd . New ( "vde_plug" , "/run/vde/sw_main.sock" )
c2 := cmd . New ( "vde_plug" , "/run/vde/sw_proxy" + * proxy + ".sock" )
c1 . Execute ( )
c2 . Execute ( )
go pipeForward ( c1 . OutReader , c2 . InWriter , cmd . In , * passthrough )
go pipeForward ( c2 . OutReader , c1 . InWriter , cmd . Out , * passthrough )
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 ( reader io . Reader , writer io . Writer , prefix string , passthrough bool ) {
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 )
if prefix == cmd . In {
log . Debug ( prefix , "Incoming MAC rewritten " , frame . DstMAC , OldMac )
frame . DstMAC = OldMac
} else if prefix == cmd . Out {
log . Debug ( prefix , "Outgoing MAC rewritten " , frame . SrcMAC , NewMac )
frame . SrcMAC = NewMac
}
// Handle IPv6 packet
if ipv4layer := packet . Layer ( layers . LayerTypeIPv4 ) ; ipv4layer != nil {
ipv4Packet , _ := ipv4layer . ( * layers . IPv4 )
if prefix == cmd . In {
log . Debug ( prefix , "Incoming IP rewritten " , ipv4Packet . DstIP , OldIP )
ipv4Packet . DstIP = OldIP
} else if prefix == cmd . Out {
log . Debug ( prefix , "Outgoing IP rewritten " , ipv4Packet . SrcIP , NewIP )
ipv4Packet . SrcIP = NewIP
}
// Handle ICMP packet (based on IPv4)
if icmpLayer := packet . Layer ( layers . LayerTypeICMPv4 ) ; icmpLayer != nil {
icmpPacket , _ := icmpLayer . ( * layers . ICMPv4 )
log . Debugf ( "%s ICMP packet\nType/Code:\t%s\n" , prefix , icmpPacket . TypeCode . String ( ) )
}
// Handle DHCP packet (based on IPv4) - drop for now
if dhcpLayer := packet . Layer ( layers . LayerTypeDHCPv4 ) ; dhcpLayer != nil && ! passthrough {
//dhcpPacket, _ := dhcpLayer.(*layers.DHCPv4)
log . Info ( prefix , "DHCP packet dropped" )
continue
}
// 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 . Debugf (
"%sARP before modification\nAddrType:\t%s\nProtocol:\t%s\nOperation:\t%s\n" +
"SrcHwAddress:\t%s\nSrcProtAddress:\t%s\nDstHwAddress:\t%s\nDstProtAddress:\t%s\n" ,
prefix ,
arpPacket . AddrType ,
arpPacket . Protocol ,
strconv . Itoa ( int ( arpPacket . Operation ) ) ,
net . HardwareAddr ( arpPacket . SourceHwAddress ) ,
net . IP ( arpPacket . SourceProtAddress ) ,
net . HardwareAddr ( arpPacket . DstHwAddress ) ,
net . IP ( arpPacket . DstProtAddress ) ,
)
if prefix == cmd . In {
arpPacket . DstProtAddress = OldIP
log . Debugf (
"%sARP after modification\nDstProtAddress:\t%s\n" ,
prefix ,
net . IP ( arpPacket . DstProtAddress ) ,
)
if arpPacket . Operation == layers . ARPReply {
isInteresting = true
arpPacket . DstHwAddress = OldMac
log . Debugf (
"%sDstHwAddress:\t%s\n" ,
prefix ,
net . HardwareAddr ( arpPacket . DstHwAddress ) ,
)
}
} else if prefix == cmd . Out {
arpPacket . SourceHwAddress = NewMac
arpPacket . SourceProtAddress = NewIP
log . Debugf (
"%sARP after modification\nSrcHwAddress:\t%s\nSrcProtAddress:\t%s\n" ,
prefix ,
net . HardwareAddr ( arpPacket . SourceHwAddress ) ,
net . IP ( arpPacket . SourceProtAddress ) ,
)
}
}
// 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.WritePcap("xyz.pcap", packet.Data(), packet.Metadata().CaptureInfo)
}
// Forward modified frame to other plug
writer . Write ( newFrameLength )
writer . Write ( newFrameBytes )
}
}