97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
// Copyright 2018-present the CoreDHCP Authors. All rights reserved
|
|
// This source code is licensed under the MIT license found in the
|
|
// LICENSE file in the root directory of this source tree.
|
|
|
|
// +build linux
|
|
|
|
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"syscall"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
|
)
|
|
|
|
//this function sends an unicast to the hardware address defined in resp.ClientHWAddr,
|
|
//the layer3 destination address is still the broadcast address;
|
|
//iface: the interface where the DHCP message should be sent;
|
|
//resp: DHCPv4 struct, which should be sent;
|
|
func sendEthernet(iface net.Interface, resp *dhcpv4.DHCPv4) error {
|
|
|
|
eth := layers.Ethernet{
|
|
EthernetType: layers.EthernetTypeIPv4,
|
|
SrcMAC: iface.HardwareAddr,
|
|
DstMAC: resp.ClientHWAddr,
|
|
}
|
|
ip := layers.IPv4{
|
|
Version: 4,
|
|
TTL: 64,
|
|
SrcIP: resp.ServerIPAddr,
|
|
DstIP: resp.YourIPAddr,
|
|
Protocol: layers.IPProtocolUDP,
|
|
Flags: layers.IPv4DontFragment,
|
|
}
|
|
udp := layers.UDP{
|
|
SrcPort: dhcpv4.ServerPort,
|
|
DstPort: dhcpv4.ClientPort,
|
|
}
|
|
|
|
err := udp.SetNetworkLayerForChecksum(&ip)
|
|
if err != nil {
|
|
return fmt.Errorf("Send Ethernet: Couldn't set network layer: %v", err)
|
|
}
|
|
|
|
buf := gopacket.NewSerializeBuffer()
|
|
opts := gopacket.SerializeOptions{
|
|
ComputeChecksums: true,
|
|
FixLengths: true,
|
|
}
|
|
|
|
// Decode a packet
|
|
packet := gopacket.NewPacket(resp.ToBytes(), layers.LayerTypeDHCPv4, gopacket.NoCopy)
|
|
dhcpLayer := packet.Layer(layers.LayerTypeDHCPv4)
|
|
dhcp, ok := dhcpLayer.(gopacket.SerializableLayer)
|
|
if !ok {
|
|
return fmt.Errorf("Layer %s is not serializable", dhcpLayer.LayerType().String())
|
|
}
|
|
err = gopacket.SerializeLayers(buf, opts, ð, &ip, &udp, dhcp)
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot serialize layer: %v", err)
|
|
}
|
|
data := buf.Bytes()
|
|
|
|
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("Send Ethernet: Cannot open socket: %v", err)
|
|
}
|
|
defer func() {
|
|
err = syscall.Close(fd)
|
|
if err != nil {
|
|
log.Errorf("Send Ethernet: Cannot close socket: %v", err)
|
|
}
|
|
}()
|
|
|
|
err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
if err != nil {
|
|
log.Errorf("Send Ethernet: Cannot set option for socket: %v", err)
|
|
}
|
|
|
|
var hwAddr [8]byte
|
|
copy(hwAddr[0:6], resp.ClientHWAddr[0:6])
|
|
ethAddr := syscall.SockaddrLinklayer{
|
|
Protocol: 0,
|
|
Ifindex: iface.Index,
|
|
Halen: 6,
|
|
Addr: hwAddr, //not used
|
|
}
|
|
err = syscall.Sendto(fd, data, 0, ðAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("Cannot send frame via socket: %v", err)
|
|
}
|
|
return nil
|
|
}
|