/* Copyright © 2026 Masato Kikuchi */ package utils import ( "encoding/binary" "fmt" "net" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" ) const ( IFLA_ETHERIP6_UNSPEC = iota IFLA_ETHERIP6_LOCAL IFLA_ETHERIP6_REMOTE IFLA_ETHERIP6_LINK IFLA_ETHERIP6_HOP_LIMIT ) func u32(v uint32) []byte { b := make([]byte, 4) binary.NativeEndian.PutUint32(b, v) return b } func AddDev(DevName string, local net.IP, remote net.IP, linkName string) error { local = local.To16() if local == nil { return fmt.Errorf("local must be an IPv6 address") } remote = remote.To16() if remote == nil { return fmt.Errorf("remote must be an IPv6 address") } req := nl.NewNetlinkRequest( unix.RTM_NEWLINK, unix.NLM_F_REQUEST|unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK, ) msg := nl.NewIfInfomsg(unix.AF_UNSPEC) req.AddData(msg) req.AddData(nl.NewRtAttr(unix.IFLA_IFNAME, []byte(DevName))) linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) linkInfo.AddRtAttr(nl.IFLA_INFO_KIND, nl.ZeroTerminated("etherip6")) infoData := nl.NewRtAttr(nl.IFLA_INFO_DATA, nil) infoData.AddRtAttr(IFLA_ETHERIP6_LOCAL, local) infoData.AddRtAttr(IFLA_ETHERIP6_REMOTE, remote) if linkName != "" { link, err := netlink.LinkByName(linkName) if err != nil { return err } infoData.AddRtAttr(IFLA_ETHERIP6_LINK, u32(uint32(link.Attrs().Index))) } infoData.AddRtAttr(IFLA_ETHERIP6_HOP_LIMIT, []byte{255}) linkInfo.AddChild(infoData) req.AddData(linkInfo) _, err := req.Execute(unix.NETLINK_ROUTE, 0) if err != nil { return err } return nil }