|
|
@@ -0,0 +1,96 @@
|
|
|
+// 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
|
|
|
+}
|