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) } }