// getNeighbors sends a request to netlink to retrieve all neighbors using // the specified address family. func getNeighbors(family Family) ([]*Neighbor, error) { // Request neighbors belonging to a specific family from netlink tab, err := syscall.NetlinkRIB(syscall.RTM_GETNEIGH, int(family)) if err != nil { return nil, os.NewSyscallError("netlink rib", err) } // Parse netlink information into individual messages msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("netlink message", err) } // Check messages for information var nn []*Neighbor for _, m := range msgs { // Ignore any messages which don't indicate a new neighbor if m.Header.Type != syscall.RTM_NEWNEIGH { continue } // Attempt to parse an individual neighbor from a message n, err := newNeighbor(&m) if err != nil { return nil, err } nn = append(nn, n) } return nn, nil }
// If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. func interfaceTable(ifindex int) ([]Interface, error) { tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) if err != nil { return nil, os.NewSyscallError("netlink rib", err) } msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("netlink message", err) } var ift []Interface for _, m := range msgs { switch m.Header.Type { case syscall.NLMSG_DONE: goto done case syscall.RTM_NEWLINK: ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifim.Index) { attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, os.NewSyscallError("netlink routeattr", err) } ifi := newLink(ifim, attrs) ift = append(ift, ifi) } } } done: return ift, nil }
func getDefaultRoute() (*syscall.NetlinkMessage, error) { dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) if err != nil { return nil, err } msgs, msgErr := syscall.ParseNetlinkMessage(dat) if msgErr != nil { return nil, msgErr } rtmsg := syscall.RtMsg{} for _, m := range msgs { if m.Header.Type != syscall.RTM_NEWROUTE { continue } buf := bytes.NewBuffer(m.Data[:syscall.SizeofRtMsg]) if rerr := binary.Read(buf, binary.LittleEndian, &rtmsg); rerr != nil { continue } if rtmsg.Dst_len == 0 { // zero-length Dst_len implies default route return &m, nil } } return nil, errNoDefaultRoute }
func getIface(idx uint32) (*syscall.NetlinkMessage, error) { dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) if err != nil { return nil, err } msgs, msgErr := syscall.ParseNetlinkMessage(dat) if msgErr != nil { return nil, msgErr } ifaddrmsg := syscall.IfAddrmsg{} for _, m := range msgs { if m.Header.Type != syscall.RTM_NEWADDR { continue } buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfAddrmsg]) if rerr := binary.Read(buf, binary.LittleEndian, &ifaddrmsg); rerr != nil { continue } if ifaddrmsg.Index == idx { return &m, nil } } return nil, errNoDefaultRoute }
// If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otherwise it returns a mapping of a specific // interface. func interfaceTable(ifindex int) ([]Interface, error) { // 如果ifindex为0,返回所有网络接口 tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) // 获得netlink rib if err != nil { return nil, os.NewSyscallError("netlinkrib", err) } msgs, err := syscall.ParseNetlinkMessage(tab) // 解析netlink信息 if err != nil { return nil, os.NewSyscallError("parsenetlinkmessage", err) } var ift []Interface loop: for _, m := range msgs { // 遍历获取的netlink信息 switch m.Header.Type { case syscall.NLMSG_DONE: break loop case syscall.RTM_NEWLINK: ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifim.Index) { attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, os.NewSyscallError("parsenetlinkrouteattr", err) } ift = append(ift, *newLink(ifim, attrs)) // 加入到Interface Slice中 if ifindex == int(ifim.Index) { break loop } } } } return ift, nil }
// If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { var ( tab []byte e int err os.Error ifat []Addr msgs []syscall.NetlinkMessage ) tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) if e != 0 { return nil, os.NewSyscallError("netlink rib", e) } msgs, e = syscall.ParseNetlinkMessage(tab) if e != 0 { return nil, os.NewSyscallError("netlink message", e) } ifat, err = addrTable(msgs, ifindex) if err != nil { return nil, err } return ifat, nil }
// If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { var ( tab []byte e int err os.Error ifat4 []Addr ifat6 []Addr msgs4 []syscall.NetlinkMessage msgs6 []syscall.NetlinkMessage ) tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET) if e != 0 { return nil, os.NewSyscallError("netlink rib", e) } msgs4, e = syscall.ParseNetlinkMessage(tab) if e != 0 { return nil, os.NewSyscallError("netlink message", e) } ifat4, err = addrTable(msgs4, ifindex) if err != nil { return nil, err } tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET6) if e != 0 { return nil, os.NewSyscallError("netlink rib", e) } msgs6, e = syscall.ParseNetlinkMessage(tab) if e != 0 { return nil, os.NewSyscallError("netlink message", e) } ifat6, err = addrTable(msgs6, ifindex) if err != nil { return nil, err } return append(ifat4, ifat6...), nil }
// If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. func interfaceAddrTable(ifindex int) ([]Addr, error) { tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) if err != nil { return nil, os.NewSyscallError("netlink rib", err) } msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("netlink message", err) } ifat, err := addrTable(msgs, ifindex) if err != nil { return nil, err } return ifat, nil }
// If the ifi is nil, interfaceAddrTable returns addresses for all // network interfaces. Otherwise it returns addresses for a specific // interface. func interfaceAddrTable(ifi *Interface) ([]Addr, error) { tab, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) if err != nil { return nil, os.NewSyscallError("netlinkrib", err) } msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, os.NewSyscallError("parsenetlinkmessage", err) } var ift []Interface if ifi == nil { var err error ift, err = interfaceTable(0) if err != nil { return nil, err } } ifat, err := addrTable(ift, ifi, msgs) if err != nil { return nil, err } return ifat, nil }
func find_gateway(ip_mask *net.IPNet) (ip net.IP, dev string, err error) { buf, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_INET) if err != nil { log.Println("Failed to open netlink:", err) return } msgs, err := syscall.ParseNetlinkMessage(buf) if err != nil { log.Println("Failed to parse nl msg:", err) return } var def route set := false var once sync.Once loop: for _, m := range msgs { switch m.Header.Type { case syscall.NLMSG_DONE: break loop case syscall.RTM_NEWROUTE: // a route enrty var r route rtmsg := (*syscall.RtMsg)(unsafe.Pointer(&m.Data[0])) attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { // err is shadowed log.Println("Failed to parse nl rtattr:", err) return ip, dev, err } // parse a route entry for _, a := range attrs { switch a.Attr.Type { case syscall.RTA_DST: addr := a.Value _, r.dst, err = net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", addr[0], addr[1], addr[2], addr[3], rtmsg.Dst_len)) if err != nil { log.Println("Failed to parse ip addr:", err) return ip, dev, err } case syscall.RTA_GATEWAY: addr := a.Value r.gateway = net.IPv4(addr[0], addr[1], addr[2], addr[3]) case syscall.RTA_OIF: r.if_index = int(a.Value[0]) } } if r.dst == nil { once.Do(func() { def = r set = true }) } else { if r.dst.Contains(ip_mask.IP) { ifce, err := net.InterfaceByIndex(r.if_index) if err != nil { log.Println("Failed to get interface by index:", err) return ip, dev, err } return r.gateway, ifce.Name, nil } } } } if set { ifce, err := net.InterfaceByIndex(def.if_index) if err != nil { log.Println("Failed to get interface by index:", err) return ip, dev, err } return def.gateway, ifce.Name, nil } err = fmt.Errorf("Route not found.") return }
// New creates a new router object. The router returned by New currently does // not update its routes after construction... care should be taken for // long-running programs to call New() regularly to take into account any // changes to the routing table which have occurred since the last New() call. func New() (Router, error) { rtr := &router{} tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) if err != nil { return nil, err } msgs, err := syscall.ParseNetlinkMessage(tab) if err != nil { return nil, err } loop: for _, m := range msgs { switch m.Header.Type { case syscall.NLMSG_DONE: break loop case syscall.RTM_NEWROUTE: rt := (*routeInfoInMemory)(unsafe.Pointer(&m.Data[0])) routeInfo := rtInfo{} attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, err } switch rt.Family { case syscall.AF_INET: rtr.v4 = append(rtr.v4, &routeInfo) case syscall.AF_INET6: rtr.v6 = append(rtr.v6, &routeInfo) default: continue loop } for _, attr := range attrs { switch attr.Attr.Type { case syscall.RTA_DST: routeInfo.Dst = &net.IPNet{ IP: net.IP(attr.Value), Mask: net.CIDRMask(int(rt.DstLen), len(attr.Value)*8), } case syscall.RTA_SRC: routeInfo.Src = &net.IPNet{ IP: net.IP(attr.Value), Mask: net.CIDRMask(int(rt.SrcLen), len(attr.Value)*8), } case syscall.RTA_GATEWAY: routeInfo.Gateway = net.IP(attr.Value) case syscall.RTA_PREFSRC: routeInfo.PrefSrc = net.IP(attr.Value) case syscall.RTA_IIF: routeInfo.InputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0])) case syscall.RTA_OIF: routeInfo.OutputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0])) case syscall.RTA_PRIORITY: routeInfo.Priority = *(*uint32)(unsafe.Pointer(&attr.Value[0])) } } } } sort.Sort(rtr.v4) sort.Sort(rtr.v6) ifaces, err := net.Interfaces() if err != nil { return nil, err } for i, iface := range ifaces { if i != iface.Index-1 { return nil, fmt.Errorf("out of order iface %d = %v", i, iface) } rtr.ifaces = append(rtr.ifaces, iface) var addrs ipAddrs ifaceAddrs, err := iface.Addrs() if err != nil { return nil, err } for _, addr := range ifaceAddrs { if inet, ok := addr.(*net.IPNet); ok { // Go has a nasty habit of giving you IPv4s as ::ffff:1.2.3.4 instead of 1.2.3.4. // We want to use mapped v4 addresses as v4 preferred addresses, never as v6 // preferred addresses. if v4 := inet.IP.To4(); v4 != nil { if addrs.v4 == nil { addrs.v4 = v4 } } else if addrs.v6 == nil { addrs.v6 = inet.IP } } } rtr.addrs = append(rtr.addrs, addrs) } return rtr, nil }
// List all available routes on the system. func Routes() ([]*Route, error) { var routes []*Route rib, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC) if err != nil { return nil, fmt.Errorf("Could not retrieve RIB: %s", err) } msgs, err := syscall.ParseNetlinkMessage(rib) if err != nil { return nil, fmt.Errorf("Could not parse messages: %s") } for _, m := range msgs { if m.Header.Type == syscall.NLMSG_DONE { break } if m.Header.Type != syscall.RTM_NEWROUTE { continue } route := &Route{Default: true} rtmsg := (*rtmsg)(unsafe.Pointer(&m.Data[0])) attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, fmt.Errorf("Could not parse attr: %s", err) } for _, a := range attrs { switch a.Attr.Type { case syscall.RTA_SRC: route.SrcNet = &net.IPNet{ IP: net.IP(a.Value), Mask: net.CIDRMask( int(rtmsg.src_len), len(a.Value)*8, ), } case syscall.RTA_DST: route.DstNet = &net.IPNet{ IP: net.IP(a.Value), Mask: net.CIDRMask( int(rtmsg.dst_len), len(a.Value)*8, ), } route.Default = false case syscall.RTA_GATEWAY: route.Gateway = net.IP(a.Value) case syscall.RTA_OIF: oif := *(*uint32)(unsafe.Pointer(&a.Value[0])) iface, err := net.InterfaceByIndex(int(oif)) if err != nil { } route.Iface = iface case syscall.RTA_PRIORITY: } } routes = append(routes, route) } return routes, nil }