mosers
/
eaas-vde-proxy
Archiviert
1
0
Fork 0

DHCP client implemented [Close #9]

main
Simon Moser vor 3 Jahren
Ursprung 03f1221dc8
Commit 86c4d38f98
Signiert von: mosers
GPG-Schlüssel-ID: 96B3365A234B500C

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"flag" "flag"
"fmt"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/krolaw/dhcp4" "github.com/krolaw/dhcp4"
@ -13,7 +12,8 @@ import (
"net" "net"
"os" "os"
"proxy/cmd" "proxy/cmd"
"proxy/util" . "proxy/util"
"strconv"
"time" "time"
) )
@ -26,6 +26,15 @@ var VmWriter io.Writer
var NetReader io.Reader var NetReader io.Reader
var NetWriter io.Writer var NetWriter io.Writer
var Passthrough bool var Passthrough bool
var XId []byte
var DHCPIP net.IP
var RouterIP net.IP
var DNSIP net.IP
var DHCPMAC net.HardwareAddr
var DHCPMask []byte
var DHCPState dhcp4.MessageType
var DHCPCandidate net.IP
var UId string
// Start the two plugs and run two concurrent forward methods // Start the two plugs and run two concurrent forward methods
func main() { func main() {
@ -43,10 +52,11 @@ func main() {
flag.Parse() flag.Parse()
log.SetLevel(log.Level(*logLvl)) log.SetLevel(log.Level(*logLvl))
OldMAC, _ = net.ParseMAC(*oldMAC) OldMAC, _ = net.ParseMAC(*oldMAC)
NewMAC = util.GenerateMac(*newMAC) NewMAC = GenerateMac(*newMAC)
OldIP = net.ParseIP(*oldIP).To4() OldIP = net.ParseIP(*oldIP).To4()
NewIP = net.ParseIP(*newIP).To4() NewIP = net.ParseIP(*newIP).To4()
Passthrough = *passthrough Passthrough = *passthrough
UId = GenerateUId(*sockProxy)
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true, DisableTimestamp: true,
}) })
@ -57,7 +67,7 @@ func main() {
log.SetOutput(f) log.SetOutput(f)
} }
} }
util.WritePIDFile(*pidFile) WritePIDFile(*pidFile)
var c1, c2 *cmd.Cmd var c1, c2 *cmd.Cmd
if *sockMain != "-" { if *sockMain != "-" {
c1, NetReader, NetWriter = cmd.Start(*sockMain) c1, NetReader, NetWriter = cmd.Start(*sockMain)
@ -68,6 +78,7 @@ func main() {
c2, VmReader, VmWriter = cmd.Start(*sockProxy) c2, VmReader, VmWriter = cmd.Start(*sockProxy)
go pipeForward(cmd.In) go pipeForward(cmd.In)
go pipeForward(cmd.Out) go pipeForward(cmd.Out)
sendDHCPRequest(dhcp4.Discover, net.IPv4zero)
if *sockMain != "-" { if *sockMain != "-" {
c1.WaitH() c1.WaitH()
} }
@ -103,7 +114,7 @@ func pipeForward(prefix string) {
// Convert frame to full stack packet // Convert frame to full stack packet
packet := gopacket.NewPacket(frameBytes, layers.LayerTypeEthernet, gopacket.Default) packet := gopacket.NewPacket(frameBytes, layers.LayerTypeEthernet, gopacket.Default)
isInteresting := false // Debug Help // isInteresting := false // Debug Help
// Handle Ethernet frame // Handle Ethernet frame
frame := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) frame := packet.Layer(layers.LayerTypeEthernet).(*layers.Ethernet)
@ -117,7 +128,7 @@ func pipeForward(prefix string) {
// Handle DHCPv4 packet (based on IPv4) // Handle DHCPv4 packet (based on IPv4)
if dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4); dhcpLayer != nil && !Passthrough { if dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4); dhcpLayer != nil && !Passthrough {
handleDHCP(dhcpLayer.LayerContents(), frame.DstMAC, prefix) handleDHCP(dhcpLayer.LayerContents(), frame.DstMAC, frame.SrcMAC, prefix)
continue continue
} }
@ -177,11 +188,11 @@ func pipeForward(prefix string) {
binary.BigEndian.PutUint16(newFrameLength, uint16(len(newFrameBytes))) binary.BigEndian.PutUint16(newFrameLength, uint16(len(newFrameBytes)))
// Write interesting things to debug file // Write interesting things to debug file
if isInteresting { /*if isInteresting {
util.WriteBinary(fmt.Sprintf("/tmp/pck_%di.dat", time.Now().Unix()), frameBytes) WriteBinary(fmt.Sprintf("/tmp/pck_%di.dat", time.Now().Unix()), frameBytes)
util.WriteBinary(fmt.Sprintf("/tmp/pck_%do.dat", time.Now().Unix()), newFrameBytes) WriteBinary(fmt.Sprintf("/tmp/pck_%do.dat", time.Now().Unix()), newFrameBytes)
//util.WritePcapNg("xyz.pcap", packet.Data(), packet.Metadata().CaptureInfo) //WritePcapNg("xyz.pcap", packet.Data(), packet.Metadata().CaptureInfo)
} }*/
// Forward modified frame to other plug // Forward modified frame to other plug
if _, err := writer.Write(newFrameLength); err != nil { if _, err := writer.Write(newFrameLength); err != nil {
@ -281,7 +292,7 @@ func filterMAC(prefix string, dst interface{}, src interface{}, context gopacket
} }
} }
func handleDHCP(content []byte, srcMAC net.HardwareAddr, prefix string) { func handleDHCP(content []byte, dstMAC net.HardwareAddr, srcMAC net.HardwareAddr, prefix string) {
req := dhcp4.Packet(content) req := dhcp4.Packet(content)
if req.HLen() > 16 { // Invalid size if req.HLen() > 16 { // Invalid size
log.Error(prefix, "Invalid DHCP size") log.Error(prefix, "Invalid DHCP size")
@ -306,7 +317,9 @@ func handleDHCP(content []byte, srcMAC net.HardwareAddr, prefix string) {
if OldIP == nil { if OldIP == nil {
log.Fatal(prefix, "DHCPDISCOVER but not previous address is known") log.Fatal(prefix, "DHCPDISCOVER but not previous address is known")
} }
sendDHCPReply(req, dhcp4.Offer, OldIP, options, srcMAC) sendDHCPReply(req, dhcp4.Offer, OldIP, options, dstMAC)
case dhcp4.Inform:
fallthrough
case dhcp4.Request: case dhcp4.Request:
reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress]) reqIP := net.IP(options[dhcp4.OptionRequestedIPAddress])
if reqIP == nil { if reqIP == nil {
@ -316,25 +329,63 @@ func handleDHCP(content []byte, srcMAC net.HardwareAddr, prefix string) {
log.Error(prefix, "Invalid IP requested in DHCP: ", reqIP) log.Error(prefix, "Invalid IP requested in DHCP: ", reqIP)
return return
} }
sendDHCPReply(req, dhcp4.ACK, reqIP, options, srcMAC) sendDHCPReply(req, dhcp4.ACK, reqIP, options, dstMAC)
default:
return
} }
} else { } else {
// TODO: talk to DHCP server switch reqType {
case dhcp4.Offer:
if DHCPState == dhcp4.Discover {
DHCPMAC = srcMAC
offIP := req.YIAddr()
if len(offIP) != 4 || offIP.Equal(net.IPv4zero) {
log.Error(prefix, "Invalid IP offered in DHCP: ", offIP)
return
}
if dhcpip := options[dhcp4.OptionServerIdentifier]; dhcpip != nil {
DHCPIP = dhcpip
}
if mask := options[dhcp4.OptionSubnetMask]; mask != nil {
DHCPMask = mask
}
if dns := options[dhcp4.OptionDomainNameServer]; dns != nil {
DNSIP = dns
}
if router := options[dhcp4.OptionRouter]; router != nil {
RouterIP = router
}
DHCPCandidate = offIP
sendDHCPRequest(dhcp4.Request, offIP)
}
case dhcp4.ACK:
if DHCPState == dhcp4.Request {
NewIP = DHCPCandidate
DHCPCandidate = nil
DHCPState = 0
log.Info("DHCP lease accepted: ", NewIP)
}
case dhcp4.NAK:
if DHCPState == dhcp4.Request {
sendDHCPRequest(dhcp4.Discover, net.IPv4zero)
}
}
} }
} }
// sendDHCPReply creates a response DHCP packet and sends it // sendDHCPReply creates a response DHCP packet and sends it
func sendDHCPReply(req dhcp4.Packet, mt dhcp4.MessageType, lease net.IP, reqOpt dhcp4.Options, dstMAC net.HardwareAddr) { func sendDHCPReply(req dhcp4.Packet, mt dhcp4.MessageType, lease net.IP, reqOpt dhcp4.Options, dstMAC net.HardwareAddr) {
if DHCPIP == nil || DHCPMAC == nil {
log.Info("DHCP server is not known, discover request from VM discarded")
sendDHCPRequest(dhcp4.Discover, net.IPv4zero)
return
}
log.Info("Sending DHCP response: ", mt, lease) log.Info("Sending DHCP response: ", mt, lease)
// Getting the options // Getting the options
serverIP := []byte{10, 0, 0, 1} // TODO: serverIP
serverMAC := util.GenerateMac("") // TODO: server MAC
opt := dhcp4.Options{ opt := dhcp4.Options{
dhcp4.OptionSubnetMask: []byte{255, 255, 255, 0}, // TODO: subnet mask dhcp4.OptionSubnetMask: DHCPMask,
dhcp4.OptionRouter: serverIP, // TODO: Presuming Server is also your router dhcp4.OptionRouter: RouterIP,
dhcp4.OptionDomainNameServer: serverIP, // TODO: Presuming Server is also your DNS server dhcp4.OptionDomainNameServer: DNSIP,
}.SelectOrderOrAll(reqOpt[dhcp4.OptionParameterRequestList]) }.SelectOrderOrAll(reqOpt[dhcp4.OptionParameterRequestList])
// Creating the full packet layer by layer // Creating the full packet layer by layer
@ -344,27 +395,27 @@ func sendDHCPReply(req dhcp4.Packet, mt dhcp4.MessageType, lease net.IP, reqOpt
FixLengths: true, FixLengths: true,
} }
eth := layers.Ethernet{ eth := layers.Ethernet{
SrcMAC: serverMAC, SrcMAC: DHCPMAC,
DstMAC: dstMAC, DstMAC: dstMAC,
EthernetType: layers.EthernetTypeIPv4, EthernetType: layers.EthernetTypeIPv4,
} }
ipv4 := layers.IPv4{ ipv4 := layers.IPv4{
Version: 4, Version: 4,
TTL: 64, TTL: 128,
Protocol: layers.IPProtocolUDP, Protocol: layers.IPProtocolUDP,
SrcIP: serverIP, SrcIP: DHCPIP,
DstIP: lease, DstIP: lease,
} }
udp := layers.UDP{ udp := layers.UDP{
SrcPort: 67, SrcPort: 67,
DstPort: 68, DstPort: 68,
} }
dhcp := dhcp4.ReplyPacket(req, mt, serverIP, lease, 24*time.Hour, opt) dhcp := dhcp4.ReplyPacket(req, mt, DHCPIP, lease, 24*time.Hour, opt)
if err := udp.SetNetworkLayerForChecksum(&ipv4); err != nil { if err := udp.SetNetworkLayerForChecksum(&ipv4); err != nil {
log.Error("Error building DHCP response", err) log.Error("Error building DHCP response:", err)
} }
if err := gopacket.SerializeLayers(buf, opts, &eth, &ipv4, &udp, gopacket.Payload(dhcp)); err != nil { if err := gopacket.SerializeLayers(buf, opts, &eth, &ipv4, &udp, gopacket.Payload(dhcp)); err != nil {
log.Error("Error serializing DHCP response", err) log.Error("Error serializing DHCP response:", err)
} }
packetData := buf.Bytes() packetData := buf.Bytes()
@ -379,3 +430,69 @@ func sendDHCPReply(req dhcp4.Packet, mt dhcp4.MessageType, lease net.IP, reqOpt
log.Error("Error writing DHCP response data", err) log.Error("Error writing DHCP response data", err)
} }
} }
func sendDHCPRequest(mt dhcp4.MessageType, reqIP net.IP) {
log.Info("Sending DHCP request: ", mt)
if mt == dhcp4.Discover {
XId = GenerateXID()
}
DHCPState = mt
// Creating the full packet layer by layer
buf := gopacket.NewSerializeBuffer()
serializeOpts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
eth := layers.Ethernet{
SrcMAC: NewMAC,
DstMAC: If(mt == dhcp4.Discover).MAC([]byte{255,255,255,255,255,255}, DHCPMAC),
EthernetType: layers.EthernetTypeIPv4,
}
ipv4 := layers.IPv4{
Version: 4,
TTL: 128,
Protocol: layers.IPProtocolUDP,
SrcIP: net.IPv4zero,
DstIP: net.IPv4bcast,
}
udp := layers.UDP{
SrcPort: 68,
DstPort: 67,
}
dhcpOpts := []dhcp4.Option{
{
Code: dhcp4.OptionHostName,
Value: []byte("vdeproxy" + UId),
},
{
Code: dhcp4.OptionParameterRequestList,
Value: []byte{1, 3, 6}, // Subnet Mask, Router, Domain Name Server
},
}
if mt == dhcp4.Request {
dhcpOpts = append(dhcpOpts, dhcp4.Option{
Code: dhcp4.OptionRequestedIPAddress,
Value: reqIP.To4(),
})
}
dhcp := dhcp4.RequestPacket(mt, NewMAC, reqIP, XId, false, dhcpOpts)
if err := udp.SetNetworkLayerForChecksum(&ipv4); err != nil {
log.Error("Error building DHCP request: ", err)
}
if err := gopacket.SerializeLayers(buf, serializeOpts, &eth, &ipv4, &udp, gopacket.Payload(dhcp)); err != nil {
log.Error("Error serializing DHCP request: ", err)
}
packetData := buf.Bytes()
WritePcap("/tmp/dhcpreq_" +strconv.FormatInt(time.Now().Unix(), 10)+ ".pcap", packetData)
// Sending layer through VM's pipe
packetLength := make([]byte, 2)
binary.BigEndian.PutUint16(packetLength, uint16(len(packetData)))
if _, err := NetWriter.Write(packetLength); err != nil {
log.Error("Error writing DHCP response length: ", err)
}
if _, err := NetWriter.Write(packetData); err != nil {
log.Error("Error writing DHCP response data: ", err)
}
}

@ -2,19 +2,22 @@ package util
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base32"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo" "github.com/google/gopacket/pcapgo"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"hash/adler32"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
) )
// WritePcapNg writes the provided data to a given pcap file // WritePcap writes the provided data to a given pcap file
func WritePcapNg(file string, data []byte) { func WritePcap(file string, data []byte) {
f, err := os.Create(file) f, err := os.Create(file)
if err != nil { if err != nil {
log.Errorf("Error writing pcap file %s", file) log.Errorf("Error writing pcap file %s", file)
@ -36,19 +39,6 @@ func WritePcapNg(file string, data []byte) {
err = r.WritePacket(ci, data) err = r.WritePacket(ci, data)
} }
func WritePcap(file string, data []byte) {
f, _ := os.Create(file)
w := pcapgo.NewWriter(f)
w.WriteFileHeader(65536, layers.LinkTypeEthernet) // new file, must do this.
ci := gopacket.CaptureInfo{
Timestamp: time.Now(),
CaptureLength: len(data),
Length: len(data),
}
w.WritePacket(ci, data)
f.Close()
}
// WriteBinary writes the provided data to a given binary file // WriteBinary writes the provided data to a given binary file
func WriteBinary(file string, data []byte) { func WriteBinary(file string, data []byte) {
if err := ioutil.WriteFile(file, data, 0644); err != nil { if err := ioutil.WriteFile(file, data, 0644); err != nil {
@ -82,3 +72,55 @@ func GenerateMac(customMAC string) net.HardwareAddr {
mac = append(mac, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]) mac = append(mac, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5])
return mac return mac
} }
func GenerateXID() []byte {
buf := make([]byte, 4)
if _, err := rand.Read(buf); err != nil {
log.Error("Error generating random xid")
return []byte{36, 23, 250, 224} // chosen by fair dice roll
// guaranteed to be random --> https://xkcd.com/221/
}
return buf
}
func GenerateUId(path string) string {
adl := adler32.Checksum([]byte(path))
byt := make([]byte, 4)
for i := uint32(0); i < 4; i++ {
byt[i] = byte((adl >> (8 * i)) & 0xff)
}
b32 := base32.StdEncoding.EncodeToString(byt)
return strings.ReplaceAll(b32, "=", "")
}
// If is a helper type to form expressive ternary expressions being the
// concatenation of a type conversion and a method call such as:
//
// i := If(cond).Int(a, b)
//
// For details, see https://stackoverflow.com/a/59375088/1705598
type If bool
// If returns a if c is true, b otherwise.
func (c If) If(a, b interface{}) interface{} {
if c {
return a
}
return b
}
// MAC returns a if c is true, b otherwise.
func (c If) MAC(a, b net.HardwareAddr) net.HardwareAddr {
if c {
return a
}
return b
}
// IP returns a if c is true, b otherwise.
func (c If) IP(a, b net.IP) net.IP {
if c {
return a
}
return b
}