package main
import (
"encoding/binary"
"flag"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
log "github.com/sirupsen/logrus"
"io"
"net"
"proxy/cmd"
"strconv"
)
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 )
NewIP = net . ParseIP ( * newip )
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.
// For now only output the packet's information
// 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 )
// Handle Ethernet frame
frame := packet . Layer ( layers . LayerTypeEthernet ) . ( * layers . Ethernet )
if ! passthrough && prefix == cmd . In {
log . Debug ( prefix , "Incoming MAC rewritten " , frame . DstMAC , OldMac )
frame . DstMAC = OldMac
} else if ! passthrough && prefix == cmd . Out {
log . Debug ( prefix , "Outgoing MAC rewritten " , frame . SrcMAC , NewMac )
frame . SrcMAC = NewMac
}
// Handle IP packet
if ipv4layer := packet . Layer ( layers . LayerTypeIPv4 ) ; ipv4layer != nil {
ipv4Packet , _ := ipv4layer . ( * layers . IPv4 )
if ! passthrough && prefix == cmd . In {
log . Debug ( prefix , "Incoming IP rewritten " , ipv4Packet . DstIP , OldIP )
ipv4Packet . DstIP = OldIP
} else if ! passthrough && prefix == cmd . Out {
log . Debug ( prefix , "Outgoing IP rewritten " , ipv4Packet . SrcIP , NewMac )
ipv4Packet . SrcIP = NewIP
}
}
// Handle ARP packet
if frame . EthernetType == layers . EthernetTypeARP {
arpPacket := packet . Layer ( layers . LayerTypeARP ) . ( * layers . ARP )
log . Infof (
"%sARP before modification\nAddrType:\t%s\nProtocol:\t%s\nOperation:\t%s\n" +
"SrcHwAddress:\t%s\nSrcProtAddress:\t%s\nDstHwAddress:\t%s\nDstProAddress:\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 ! passthrough && prefix == cmd . In {
log . Debug ( prefix , "Incoming MAC rewritten (ARP) " , net . HardwareAddr ( arpPacket . DstHwAddress ) , OldMac )
arpPacket . DstProtAddress = OldIP
log . Infof (
"%sARP after modification\nDstProtAddress:\t%s\n" ,
prefix ,
net . IP ( arpPacket . DstProtAddress ) ,
)
if arpPacket . Operation == layers . ARPReply {
arpPacket . DstHwAddress = OldMac
log . Infof (
"%sDstHwAddress:\t%s\n" ,
prefix ,
net . HardwareAddr ( arpPacket . DstHwAddress ) ,
)
}
} else if ! passthrough && prefix == cmd . Out {
log . Debug ( prefix , "Outgoing MAC rewritten (ARP) " , net . HardwareAddr ( arpPacket . SourceHwAddress ) , NewMac )
arpPacket . SourceHwAddress = NewMac
arpPacket . SourceProtAddress = NewIP
log . Infof (
"%sARP after modification\nSrcHwAddress:\t%s\nSrcProtAddress:\t%s\n" ,
prefix ,
net . HardwareAddr ( arpPacket . SourceHwAddress ) ,
net . IP ( arpPacket . SourceProtAddress ) ,
)
}
}
// Handle DHCP packet
if dhcpLayer := packet . Layer ( layers . LayerTypeDHCPv4 ) ; dhcpLayer != nil {
//dhcpPacket, _ := dhcpLayer.(*layers.DHCPv4)
log . Info ( prefix , "DHCP packet dropped" )
continue
}
buf := gopacket . NewSerializeBuffer ( )
if err := gopacket . SerializePacket ( buf , gopacket . SerializeOptions { } , packet ) ; err != nil {
log . Error ( prefix , "Error serializing packet to send" )
continue
}
newFrameBytes := buf . Bytes ( )
newFrameLength := make ( [ ] byte , 2 )
binary . BigEndian . PutUint16 ( newFrameLength , uint16 ( len ( newFrameBytes ) ) )
// Forward original frame to other plug
writer . Write ( newFrameLength )
writer . Write ( newFrameBytes )
}
}