initial commit

This commit is contained in:
ston1th 2020-11-22 12:59:12 +01:00
commit 7715fcf373
37 changed files with 2957 additions and 0 deletions

10
pkg/vip/arp.go Normal file
View file

@ -0,0 +1,10 @@
// +build !linux
package vip
import "fmt"
// ARPSendGratuitous is only supported on Linux, so return an error
func ARPSendGratuitous(cidr, iface string) error {
return fmt.Errorf("Unsupported on this OS")
}

173
pkg/vip/arp_linux.go.bak Normal file
View file

@ -0,0 +1,173 @@
// +build linux
// These syscalls are only supported on Linux, so this uses a build directive during compilation. Other OS's will use the arp_unsupported.go and receive an error
package vip
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
"unsafe"
)
const (
opARPRequest = 1
opARPReply = 2
hwLen = 6
)
var (
ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
// arpRequest is used to flip between garp request or garp reply
arpRequest = true
)
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
}
// gratuitousARP return a gARP request or gARP reply alternatively
// because different devices may support either one of them
func gratuitousARP(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: arpHeader{
1, // Ethernet
0x0800, // IPv4
hwLen, // 48-bit MAC Address
net.IPv4len, // 32-bit IPv4 Address
opARPReply, // ARP Reply
},
}
// https://tools.ietf.org/html/rfc5944#section-4.6
// In either case, the ARP Sender Hardware Address is
// set to the link-layer address to which this cache entry should be
// updated.
m.senderHardwareAddress = mac
// When using an ARP Reply packet, the Target Hardware
// Address is also set to the link-layer address to which this cache
// entry should be updated (this field is not used in an ARP Request
// packet).
m.targetHardwareAddress = mac
// In either case, the ARP Sender Protocol Address and
// ARP Target Protocol Address are both set to the IP address of the
// cache entry to be updated,
m.senderProtocolAddress = ip.To4()
m.targetProtocolAddress = ip.To4()
// send arpRequest and arpReply alternatively
arpRequest = !arpRequest
if arpRequest {
m.arpHeader.opcode = opARPRequest
// this field is not used in an ARP Request packet
m.targetHardwareAddress = ethernetBroadcast
}
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
for i := 0; i < len(target); i++ {
ll.Addr[i] = target[i]
}
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
}
// ARPSendGratuitous sends a gratuitous ARP message via the specified interface.
func ARPSendGratuitous(cidr, iface string) error {
i, err := net.InterfaceByName(iface)
if err != nil {
return fmt.Errorf("failed to get interface %q: %v", iface, err)
}
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("failed to parse cidr: %s", cidr)
}
//log.Infof("Broadcasting ARP update for %s (%s) via %s", address, iface.HardwareAddr, iface.Name)
m, err := gratuitousARP(ip, i.HardwareAddr)
if err != nil {
return err
}
return sendARP(i, m)
}

38
pkg/vip/arp_linux2.go Normal file
View file

@ -0,0 +1,38 @@
package vip
import (
"fmt"
"net"
"github.com/mdlayher/arp"
"github.com/mdlayher/ethernet"
)
func sendARP(ip net.IP, iface *net.Interface) error {
c, err := arp.Dial(iface)
if err != nil {
return err
}
for _, op := range []arp.Operation{arp.OperationRequest, arp.OperationReply} {
pkt, err := arp.NewPacket(op, iface.HardwareAddr, ip, ethernet.Broadcast, ip)
if err != nil {
return fmt.Errorf("assembling %q gratuitous packet for %q: %s", op, ip, err)
}
if err = c.WriteTo(pkt, ethernet.Broadcast); err != nil {
return fmt.Errorf("writing %q gratuitous packet for %q: %s", op, ip, err)
}
}
return nil
}
func ARPSendGratuitous(cidr, iface string) error {
i, err := net.InterfaceByName(iface)
if err != nil {
return fmt.Errorf("failed to get interface %q: %v", iface, err)
}
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
return fmt.Errorf("failed to parse cidr: %s", cidr)
}
return sendARP(ip, i)
}

96
pkg/vip/vip.go Normal file
View file

@ -0,0 +1,96 @@
package vip
import (
"errors"
"github.com/vishvananda/netlink"
"net"
)
var ErrNoDefaultInterface = errors.New("no default interface found")
func DefaultInterface() (iface string, err error) {
routes, err := netlink.RouteGet(net.IPv4bcast)
if err != nil {
return
}
if len(routes) >= 1 {
link, err := netlink.LinkByIndex(routes[0].LinkIndex)
if err != nil {
return "", err
}
return link.Attrs().Name, nil
}
err = ErrNoDefaultInterface
return
}
type Network struct {
iface string
label string
link netlink.Link
}
func NewNetwork(iface string) (n *Network, err error) {
return NewNetworkWithLabel(iface, "")
}
func NewNetworkWithLabel(iface, label string) (n *Network, err error) {
if iface == "" {
iface, err = DefaultInterface()
if err != nil {
return
}
}
link, err := netlink.LinkByName(iface)
if err != nil {
return
}
n = &Network{iface, label, link}
return
}
func (n *Network) Interface() string {
return n.iface
}
func (n *Network) HasIP(a *netlink.Addr) bool {
addrs, err := netlink.AddrList(n.link, 0)
if err != nil {
return false
}
for _, addr := range addrs {
if addr.Equal(*a) {
return true
}
}
return false
}
func (n *Network) AddIP(cidr string) error {
addr, err := netlink.ParseAddr(cidr)
if err != nil {
return err
}
if n.HasIP(addr) {
return nil
}
if n.label != "" {
addr.Label = n.iface + ":" + n.label
}
err = netlink.AddrReplace(n.link, addr)
if err != nil {
return err
}
return ARPSendGratuitous(cidr, n.iface)
}
func (n *Network) DeleteIP(cidr string) error {
addr, err := netlink.ParseAddr(cidr)
if err != nil {
return err
}
if !n.HasIP(addr) {
return nil
}
return netlink.AddrDel(n.link, addr)
}