func (w *Worker) createRawSocket() (fd int) { socket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_TCP) if err != nil { fmt.Fprintf(os.Stderr, "fail to Socket :%s\n", err.Error()) os.Exit(1) } err = syscall.SetsockoptString(socket, syscall.IPPROTO_IP, syscall.IP_HDRINCL, "1") if err != nil { fmt.Fprintf(os.Stderr, "SetsockoptString error :%s\ns", err.Error()) os.Exit(1) } timeVal := new(syscall.Timeval) timeVal.Sec = 6 err = syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, timeVal) if err != nil { fmt.Fprintf(os.Stderr, "SetsockoptTimeval error :%s", err.Error()) os.Exit(1) } return socket }
func traceOne(addr *syscall.SockaddrInet4, ttl int) *ReturnArgs { cli, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { exitWithError(err) } srv, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) if err != nil { exitWithError(err) } defer syscall.Close(cli) defer syscall.Close(srv) // set ttl, stolen from somewhere else... // https://github.com/aeden/traceroute/blob/master/traceroute.go#L195 if err := syscall.SetsockoptInt(cli, syscall.SOL_IP, syscall.IP_TTL, ttl); err != nil { exitWithError(err) } // set timeout, stolen from somewhere else... // https://github.com/aeden/traceroute/blob/master/traceroute.go#L197 tv := syscall.NsecToTimeval(1e6 * TIMEOUT) if err := syscall.SetsockoptTimeval(srv, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv); err != nil { exitWithError(err) } if err := syscall.Bind(srv, toAddr(HOST, RECV_PORT)); err != nil { exitWithError(err) } rr := &ReturnArgs{} start := time.Now() if err := syscall.Sendto(cli, makeICMP(), 0, addr); err != nil { return rr } buf := make([]byte, 512) _, from, err := syscall.Recvfrom(srv, buf, 0) if err != nil { return rr } rr.elapsed = float64(time.Since(start).Nanoseconds()) / 1e6 t, c := parseICMP(buf) if t == 3 && c == 3 { // Destination port unreachable, type==3 && code==3 rr.done = true } else if t != 11 { // Time Exceeded, type==11 && code in (0,1) return rr } rr.ok = true rr.ip = toStr(from) addrs, err := net.LookupAddr(rr.ip) if err != nil { rr.addr = rr.ip } else { rr.addr = addrs[0] } return rr }
// SetSocketTimeout sets the send and receive timeout for each socket in the // netlink handle. Although the socket timeout has granularity of one // microsecond, the effective granularity is floored by the kernel timer tick, // which default value is four milliseconds. func (h *Handle) SetSocketTimeout(to time.Duration) error { if to < time.Microsecond { return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond) } tv := syscall.NsecToTimeval(to.Nanoseconds()) for _, sh := range h.sockets { fd := sh.Socket.GetFd() err := syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) if err != nil { return err } err = syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &tv) if err != nil { return err } } return nil }
// Traceroute executes traceroute to given destination, using options from TracerouteOptions // and sending updates to chan c // // Outbound packets are UDP packets and inbound packets are ICMP. // // Returns an error or nil if no error occurred func Traceroute(dest *net.IPAddr, options *TracerouteOptions, c chan TraceUpdate) (err error) { var destAddr [4]byte copy(destAddr[:], dest.IP.To4()) socketAddr, err := getSocketAddr() if err != nil { return } timeoutMs := (int64)(options.TimeoutMs) tv := syscall.NsecToTimeval(1000 * 1000 * timeoutMs) ttl := 1 for { // Set up receiving socket recvSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) if err != nil { log.Fatal("Cannot setup receive socket, please run as root or with CAP_NET_RAW permissions") return err } // Set up sending socket sendSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { log.Fatal("Cannot setup sending socket") return err } start := time.Now() syscall.SetsockoptInt(sendSocket, 0x0, syscall.IP_TTL, ttl) syscall.SetsockoptTimeval(recvSocket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) syscall.Bind(recvSocket, &syscall.SockaddrInet4{Port: options.Port, Addr: socketAddr}) syscall.Sendto(sendSocket, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port, Addr: destAddr}) var p = make([]byte, options.PacketSize) n, from, err := syscall.Recvfrom(recvSocket, p, 0) elapsed := time.Since(start) if err == nil { currAddr := from.(*syscall.SockaddrInet4).Addr hop := TraceUpdate{Success: true, Address: currAddr, N: n, ElapsedTime: elapsed, TTL: ttl} currHost, err := net.LookupAddr(hop.addressString()) if err == nil { hop.Host = currHost[0] } // Send update c <- hop ttl += 1 // We reached the destination if ttl > options.MaxTTL || currAddr == destAddr { ttl = 1 } } else { c <- TraceUpdate{Success: false, TTL: ttl} ttl += 1 } syscall.Close(recvSocket) syscall.Close(sendSocket) } }
// setSocketTimeout sets the receive and send timeouts on the given socket. func setSocketTimeout(fd int, timeout time.Duration) error { tv := syscall.NsecToTimeval(timeout.Nanoseconds()) for _, opt := range []int{syscall.SO_RCVTIMEO, syscall.SO_SNDTIMEO} { if err := syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, opt, &tv); err != nil { return os.NewSyscallError("setsockopt", err) } } return nil }
func Hop(port, ttl int, IP_addr net.IP) (*Hop_ret, error) { ret_addr := net.IPv4(0, 0, 0, 0) success := false // make sockets send_udp_s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return nil, err } recv_icmp_s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) if err != nil { return nil, err } //editing TTL value for outgoing IPv4 packets if err := syscall.SetsockoptInt(send_udp_s, syscall.SOL_IP, syscall.IP_TTL, ttl); err != nil { return nil, err } tv := syscall.NsecToTimeval(1000 * 1000 * TIME_OUT_MS) syscall.SetsockoptTimeval(recv_icmp_s, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) defer syscall.Close(send_udp_s) defer syscall.Close(recv_icmp_s) //connect sockets if err := syscall.Bind(recv_icmp_s, &syscall.SockaddrInet4{Port: port, Addr: [4]byte{137, 224, 226, 47}}); err != nil { return nil, err } //send udp-packet var IP [4]byte copy(IP[:], IP_addr.To4()) if err := syscall.Sendto(send_udp_s, []byte{0x42, 0x42}, 0, &syscall.SockaddrInet4{Port: 1337, Addr: IP}); err != nil { return nil, err } //receive ICMP recv_buffer := make([]byte, 4096) _, _, err = syscall.Recvfrom(recv_icmp_s, recv_buffer, 0) if err == nil { header, err := ipv4.ParseHeader(recv_buffer) if err != nil { log.Errorf("%q", err) } success = true ret_addr = header.Src } else { //time out success = false ret_addr = net.IPv4(0, 0, 0, 0) //log.Errorf("%q", err) } //resolve (timeout) errors, retry or return false... return &Hop_ret{Addr: ret_addr, TTL: ttl, success: success}, nil }
// Traceroute uses the given dest (hostname) and options to execute a traceroute // from your machine to the remote host. // // Outbound packets are UDP packets and inbound packets are ICMP. // // Returns a TracerouteResult which contains an array of hops. Each hop includes // the elapsed time and its IP address. func Traceroute(dest string, options *TracerouteOptions, c ...chan TracerouteHop) (result TracerouteResult, err error) { result.Hops = []TracerouteHop{} destAddr, err := destAddr(dest) result.DestinationAddress = destAddr socketAddr, err := socketAddr() if err != nil { return } timeoutMs := (int64)(options.TimeoutMs()) tv := syscall.NsecToTimeval(1000 * 1000 * timeoutMs) ttl := 1 retry := 0 for { //log.Println("TTL: ", ttl) start := time.Now() // Set up the socket to receive inbound packets recvSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) if err != nil { return result, err } // Set up the socket to send packets out. sendSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return result, err } // This sets the current hop TTL syscall.SetsockoptInt(sendSocket, 0x0, syscall.IP_TTL, ttl) // This sets the timeout to wait for a response from the remote host syscall.SetsockoptTimeval(recvSocket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) defer syscall.Close(recvSocket) defer syscall.Close(sendSocket) // Bind to the local socket to listen for ICMP packets syscall.Bind(recvSocket, &syscall.SockaddrInet4{Port: options.Port(), Addr: socketAddr}) // Send a single null byte UDP packet syscall.Sendto(sendSocket, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port(), Addr: destAddr}) var p = make([]byte, options.PacketSize()) n, from, err := syscall.Recvfrom(recvSocket, p, 0) elapsed := time.Since(start) if err == nil { currAddr := from.(*syscall.SockaddrInet4).Addr hop := TracerouteHop{Success: true, Address: currAddr, N: n, ElapsedTime: elapsed, TTL: ttl} // TODO: this reverse lookup appears to have some standard timeout that is relatively // high. Consider switching to something where there is greater control. currHost, err := net.LookupAddr(hop.AddressString()) if err == nil { hop.Host = currHost[0] } notify(hop, c) result.Hops = append(result.Hops, hop) ttl += 1 retry = 0 if ttl > options.MaxHops() || currAddr == destAddr { closeNotify(c) return result, nil } } else { retry += 1 if retry > options.Retries() { notify(TracerouteHop{Success: false, TTL: ttl}, c) ttl += 1 retry = 0 } } } }
func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { var tv = syscall.NsecToTimeval(nsec) return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv)) }
func Traceroute(dest string, options *TracerouteOptions, c ...chan TracerouteHop) (result TracerouteResult, err error) { result.Hops = []TracerouteHop{} destAddr, err := destAddr(dest) result.DestinationAddress = destAddr sockAddr, err := socketAddr() if err != nil { return } timeoutMs := (int64)(options.TimeoutMs()) tv := syscall.NsecToTimeval(1000 * 1000 * timeoutMs) ttl := 1 retry := 0 for { start := time.Now() recvSock, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP) if err != nil { return result, err } sendSock, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return result, err } syscall.SetsockoptInt(sendSock, 0x0, syscall.IP_TTL, ttl) syscall.SetsockoptTimeval(recvSock, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv) defer syscall.Close(sendSock) defer syscall.Close(recvSock) syscall.Bind(recvSock, &syscall.SockaddrInet4{Port: options.Port(), Addr: sockAddr}) syscall.Sendto(sendSock, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port(), Addr: destAddr}) var p = make([]byte, options.PacketSize()) n, form, err := syscall.Recvfrom(recvSock, p, 0) elapsed := time.Since(start) if err == nil { currAddr := form.(*syscall.SockaddrInet4).Addr hop := TracerouteHop{Success: true, Address: currAddr, N: n, ElapsedTime: elapsed, TTL: ttl} currHost, err := net.LookupAddr(hop.AddressString()) if err == nil { hop.Host = currHost[0] } notify(hop, c) result.Hops = append(result.Hops, hop) ttl += 1 retry = 0 if ttl > options.MaxHops() || currAddr == destAddr { closeNotify(c) return result, nil } } else { retry += 1 if retry > options.Retries() { notify(TracerouteHop{Success: false, TTL: ttl}, c) ttl += 1 retry = 0 } } } return }