package main import ( "bytes" "encoding/binary" "fmt" "log" "net" "syscall" "unsafe" ) // This subpackage while adapted, is orignally from code from Google's Seesaw // Github link here: https://github.com/google/seesaw // see original copyright notice below: // Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Author: jsing@google.com (Joel Sing) // Gratuitous encapsulates a request to send a gratuitous ARP message. type Gratuitous struct { IfaceName string IP net.IP } const ( opARPRequest = 1 opARPReply = 2 hwLen = 6 ) var ( ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ) func htons(p uint16) uint16 { var b [2]byte binary.BigEndian.PutUint16(b[:], p) return *(*uint16)(unsafe.Pointer(&b)) } // arpHeader specifies the header for an ARP message. type arpHeader struct { hardwareType uint16 protocolType uint16 hardwareAddressLength uint8 protocolAddressLength uint8 opcode uint16 } // arpMessage represents an ARP message. type arpMessage struct { arpHeader senderHardwareAddress []byte senderProtocolAddress []byte targetHardwareAddress []byte targetProtocolAddress []byte } // bytes returns the wire representation of the ARP message. func (m *arpMessage) bytes() ([]byte, error) { buf := new(bytes.Buffer) if err := binary.Write(buf, binary.BigEndian, m.arpHeader); err != nil { return nil, fmt.Errorf("binary write failed: %v", err) } buf.Write(m.senderHardwareAddress) buf.Write(m.senderProtocolAddress) buf.Write(m.targetHardwareAddress) buf.Write(m.targetProtocolAddress) return buf.Bytes(), nil } // gratuitousARPReply returns an ARP message that contains a gratuitous ARP // reply from the specified sender. func gratuitousARPReply(ip net.IP, mac net.HardwareAddr) (*arpMessage, error) { if ip.To4() == nil { return nil, fmt.Errorf("%q is not an IPv4 address", ip) } if len(mac) != hwLen { return nil, fmt.Errorf("%q is not an Ethernet MAC address", mac) } m := &arpMessage{ arpHeader{ 1, // Ethernet 0x0800, // IPv4 hwLen, // 48-bit MAC Address net.IPv4len, // 32-bit IPv4 Address opARPReply, // ARP Reply }, mac, ip.To4(), ethernetBroadcast, net.IPv4bcast, } return m, nil } // sendARP sends the given ARP message via the specified interface. func sendARP(iface *net.Interface, m *arpMessage) error { fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_DGRAM, int(htons(syscall.ETH_P_ARP))) if err != nil { return fmt.Errorf("failed to get raw socket: %v", err) } defer syscall.Close(fd) if err := syscall.BindToDevice(fd, iface.Name); err != nil { return fmt.Errorf("failed to bind to device: %v", err) } ll := syscall.SockaddrLinklayer{ Protocol: htons(syscall.ETH_P_ARP), Ifindex: iface.Index, Pkttype: 0, // syscall.PACKET_HOST Hatype: m.hardwareType, Halen: m.hardwareAddressLength, } target := ethernetBroadcast if m.opcode == opARPReply { target = m.targetHardwareAddress } log.Println("Copied ARP: ", copy(ll.Addr[:], target)) b, err := m.bytes() if err != nil { return fmt.Errorf("failed to convert ARP message: %v", err) } if err := syscall.Bind(fd, &ll); err != nil { return fmt.Errorf("failed to bind: %v", err) } if err := syscall.Sendto(fd, b, 0, &ll); err != nil { return fmt.Errorf("failed to send: %v", err) } return nil } // SendGratuitous sends a gratuitous ARP message via the specified interface. func SendGratuitous(arp *Gratuitous) error { //, out *int) error { iface, err := net.InterfaceByName(arp.IfaceName) if err != nil { return fmt.Errorf("failed to get interface %q: %v", arp.IfaceName, err) } log.Printf("Sending gratuitous ARP for %s (%s) via %s\n", arp.IP, iface.HardwareAddr, iface.Name) m, err := gratuitousARPReply(arp.IP, iface.HardwareAddr) if err != nil { return err } return sendARP(iface, m) }