diff options
-rw-r--r-- | doc/ARCHITECTURE.txt | 20 | ||||
-rw-r--r-- | gg_sniff/README.txt | 3 | ||||
-rw-r--r-- | gg_sniff/pcap.c | 23 | ||||
-rw-r--r-- | libglouglou/Makefile | 6 | ||||
-rw-r--r-- | libglouglou/README.txt | 24 | ||||
-rw-r--r-- | libglouglou/examples/Makefile | 1 | ||||
-rw-r--r-- | libglouglou/examples/traceroute.c | 103 | ||||
-rw-r--r-- | libglouglou/libggnet_traceroute.c | 420 | ||||
-rw-r--r-- | libglouglou/libggnet_traceroute.h | 62 |
9 files changed, 657 insertions, 5 deletions
diff --git a/doc/ARCHITECTURE.txt b/doc/ARCHITECTURE.txt index 721b094..70b32ab 100644 --- a/doc/ARCHITECTURE.txt +++ b/doc/ARCHITECTURE.txt @@ -1,4 +1,24 @@ =============================================================================== +2012-12-18_20-36 + +After Json talk: + +Probe +* sends packets / connections about nodes that are around +* sends informations about nodes that are around distance, path ... +* forwards informations about other probes + +Processing server +* a graph out of network informations, which nodes are connected to which +* has the state of all the network + +Visualisation client (small, no processing) +* transfers full graph on connection +* then graph updates, about nodes +* push / pull ? + + +=============================================================================== 2012-11-06_00-19 refer to architecture.xoj for big picture diff --git a/gg_sniff/README.txt b/gg_sniff/README.txt index 8d71005..2a43d02 100644 --- a/gg_sniff/README.txt +++ b/gg_sniff/README.txt @@ -36,6 +36,9 @@ Note that gg_sniff activates extra protections on libpcap file descriptor, by setting it to readonly, for now on OpenBSD only. It does so by reimplementing some of libpcap functions, see pcap.c my_pcap_open_live() +If you run gg_sniff in active mode (-a), it will open a readwrite raw socket +(with libpcap) and a readwrite ip socket (with libdnet) which render the +previous protection useless. Limitations =========== diff --git a/gg_sniff/pcap.c b/gg_sniff/pcap.c index 72b9133..fc5b978 100644 --- a/gg_sniff/pcap.c +++ b/gg_sniff/pcap.c @@ -81,6 +81,7 @@ static void phandler_sll(u_char *, static void cb_pcap(int, short, void *); static void cb_conntimer(int, short, void *); static void cb_nodename(struct ggnet *, struct ggnet_node *); +static void cb_nodetraceroute(struct ggnet *, struct ggnet_node *); static struct phandler phandlers[] = { { phandler_ether, DLT_EN10MB }, @@ -137,8 +138,10 @@ ggsniff_pcap_init(struct event_base *ev_base, struct gg_client *ggcli, if (evtimer_add(_cap.conntimer_ev, &_cap.conntimer_tv) == -1) gg_log_fatal("user: event_add conntimer failed: %s", strerror(errno)); - if (active) + if (active) { ggnet_set_dns(net, 1, ev_base, cb_nodename); + ggnet_set_traceroute(net, 1, ev_base, cb_nodetraceroute); + } _cap.ggcli = ggcli; _cap.net = net; @@ -348,6 +351,24 @@ cb_nodename(struct ggnet *net, struct ggnet_node *n) } } +static void +cb_nodetraceroute(struct ggnet *net, struct ggnet_node *n) +{ + struct gg_packet pkt; + int len; + + len = strnlen(n->fqdn, GGNET_DNSNAME_MAX); + if (len > 0) { + pkt.ver = PACKET_VERSION; + pkt.type = PACKET_TRACE; + pkt.name_addr = n->addr.s_addr; + // XXX IN PROGRESS + //pkt.name_len = len; + //strncpy((char *)pkt.name_fqdn, n->fqdn, sizeof(pkt.name_fqdn)); + gg_client_send(_cap.ggcli, &pkt); + } +} + /* * Parse an IP packet and descide what to do with it. * 'ip' is a pointer the the captured IP packet diff --git a/libglouglou/Makefile b/libglouglou/Makefile index 0343e6f..2065315 100644 --- a/libglouglou/Makefile +++ b/libglouglou/Makefile @@ -6,8 +6,10 @@ LIBDIR=$(PREFIX)/lib LIBNAME=libglouglou TARGET = ${LIBNAME}.so -SOURCES = libglouglou.c sendbuf.c utils.c libggnet.c libggnet_dns.c -HEADERS = libglouglou.h libggnet.h libggnet_dns.h +SOURCES = libglouglou.c sendbuf.c utils.c \ + libggnet.c libggnet_dns.c libggnet_traceroute.c +HEADERS = libglouglou.h \ + libggnet.h libggnet_dns.h libggnet_traceroute.h OBJECTS = $(SOURCES:.c=.o) all: $(TARGET) diff --git a/libglouglou/README.txt b/libglouglou/README.txt index 003db0a..577d7e8 100644 --- a/libglouglou/README.txt +++ b/libglouglou/README.txt @@ -3,15 +3,35 @@ libglouglou - underlaying library for glougloud and glouglou clients Requirements ============ -* libevent2 +* libevent +* libpcap +* libdnet * libbsd (linux only) +Fedora 17: +sudo yum install libevent-devel libpcap-devel libdnet-devel libbsd-devel + libggnet ======== -libglouglou includes libggnet a helper to keep track of network nodes and +libglouglou includes libggnet, a helper to keep track of network nodes and connections. +libggnet_dns +============ + +libglouglou includes libggnet_dns, a wrapper around evdns to do asyncronous +DNS reverse resolving with a simple API. +It can be reused independently by using libggnet_dns.c and libggnet_dns.h. + +libggnet_traceroute +=================== + +libglouglou includes libggnet_traceroute, a library to do asyncronous traceroute +based on libevent, libpcap and libdnet. +It can be reused independently by using libggnet_traceroute.c and +libggnet_traceroute.h. + sendbuf ======= diff --git a/libglouglou/examples/Makefile b/libglouglou/examples/Makefile index 5dc4bd5..d09388d 100644 --- a/libglouglou/examples/Makefile +++ b/libglouglou/examples/Makefile @@ -1,4 +1,5 @@ CFLAGS += -Wall -g -levent -lglouglou +CFLAGS += -ldnet -lpcap # for libggnet_traceroute SOURCES = $(shell echo *.c) OBJECTS = $(SOURCES:.c=.o) diff --git a/libglouglou/examples/traceroute.c b/libglouglou/examples/traceroute.c new file mode 100644 index 0000000..080274f --- /dev/null +++ b/libglouglou/examples/traceroute.c @@ -0,0 +1,103 @@ +#include <sys/types.h> + +#if !defined(__OpenBSD__) +#define __USE_GNU +#define _GNU_SOURCE +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <err.h> + +#include <libggnet_traceroute.h> + +static void _droppriv(char *, int, char *); +static void _cb_traceroute(struct in_addr *, + struct ggnet_traceroute_req *, void *); + +struct event_base *_ev_base; +struct ggnet_traceroute_req *_route = NULL; +int _answer = 0; + +int +main(int argc, char *argv[]) +{ + struct ggnet_traceroute *ggtr; + struct ggnet_traceroute_hop *hop; + struct in_addr ip; + struct timeval tv; + + if (argc < 2) { + printf("usage: traceroute <ip>\n"); + exit(1); + } + inet_aton(argv[1], &ip); + ip.s_addr = ntohl(ip.s_addr); + + bzero(&tv, sizeof(struct timeval)); + tv.tv_sec = 10; + + _ev_base = event_base_new(); + + ggtr = ggnet_traceroute_new(_ev_base, "lo"); // XXX rm iface + _droppriv("nobody", 1, NULL); /* bad practise you should use dedicated user */ + ggnet_traceroute_trace(ggtr, &ip, _cb_traceroute, NULL); + + event_base_loopexit(_ev_base, &tv); + event_base_dispatch(_ev_base); + + if (_answer) { + printf("%x:\n", ip.s_addr); + LIST_FOREACH(hop, &_route->hops, entry) { + printf("%x (%d)\n", hop->ip.s_addr, hop->delay); + } + } else { + printf("no answer\n"); + } + return 0; +} + +/* from libglouglou utils.c */ +static void +_droppriv(char *user, int do_chroot, char *chroot_path) +{ + struct passwd *pw; + + pw = getpwnam(user); + if (!pw) + err(1, "unknown user %s", user); + if (do_chroot) { + if (!chroot_path) + chroot_path = pw->pw_dir; + if (chroot(chroot_path) != 0) + err(1, "unable to chroot"); + } + if (chdir("/") != 0) + err(1, "unable to chdir"); + if (setgroups(1, &pw->pw_gid) == -1) + err(1, "setgroups() failed"); + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) + err(1, "setresgid failed"); + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + err(1, "setresuid() failed"); + endpwent(); + printf("droppriv done\n"); +} + + +static void +_cb_traceroute(struct in_addr *ip, + struct ggnet_traceroute_req *route, void *data) +{ + _route = route; + _answer = 1; + event_base_loopexit(_ev_base, NULL); +} diff --git a/libglouglou/libggnet_traceroute.c b/libglouglou/libggnet_traceroute.c new file mode 100644 index 0000000..af0d0ce --- /dev/null +++ b/libglouglou/libggnet_traceroute.c @@ -0,0 +1,420 @@ +/* + * Traceroute library using libevent, libdnet and libpcap + * + * 2012 Laurent Ghigonis <laurent@p1sec.com> + * + * Inspired from jtrace + * http://monkey.org/~jose/software/jtrace/ + * Copyright (c) 2003-2004 Jose Nazario <jose@monkey.org> + * All rights reserved. + * Inspired from OpenBSD's traceroute + * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/traceroute/traceroute.c?rev=HEAD + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * This code is derived from software contributed to Berkeley by + * Van Jacobson. + * Inspired from netsniff-ng's astraceroute + * http://www.netsniff-ng.org/ + * https://github.com/gnumaniacs/netsniff-ng/blob/master/src/astraceroute.c + * By Daniel Borkmann <daniel@netsniff-ng.org> + * Copyright 2011 Daniel Borkmann. + */ + +/* + * Traceroute algorithm + * + * Send algo (_cb_send) : + * + * send 3 * 15 packets to next 15 ttls + * add packets in hops_list + * set recv timeout in 3s + * + * Recv algo (_cb_recv) : + * + * check if dst IP is for us + * check if src IP is for us in hop_list + * tcp / udp + * is it answer from the target ? + XXX we might still want to continue a few hops after target + * target info write in hop_list + * recv timeout in 0.5s + * icmp unreach / exceed : + * did we send the inner pkt ? + * hop info write in hop_list + * which ttl ? + * is it answer from the target ? + * recv timeout in 0.5s + * did we receive all answers ? + * send more ! + */ + +/* TODO + * * hop store 3 answer results + * * send more packets of 15th answer + * * LATER hability to intrace all captured TCP session + * check for no incidence on the TCP connection + * can do on UDP ? + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <netinet/in.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "libggnet_traceroute.h" + +#define TRACE_DPORT 80 +#define TIMEOUT_TOTAL_S 3 +#define TIMEOUT_AFTERTARGET_US 500000 + +static void _req_free(struct ggnet_traceroute_req *); +static void _cb_recv(evutil_socket_t, short, void *); +static void _cb_send(evutil_socket_t, short, void *); +static int pcap_dloff(pcap_t *); // XXX move to libggnet_utils ? + +struct trace_pkt { + union { + struct ip_hdr ip; + } pkt_hdr_i; + union { + struct tcp_hdr tcp; + } pkt_hdr_t; +}; + +struct ggnet_traceroute * +ggnet_traceroute_new(struct event_base *ev_base, char *iface) +{ + struct ggnet_traceroute *ggtr; + char ebuff[PCAP_ERRBUF_SIZE]; + + ggtr = calloc(1, sizeof(struct ggnet_traceroute)); + if (!ggtr) { + printf("could not allocate ggnet_traceroute\n"); + exit(1); + } + ggtr->ev_base = ev_base; + ggtr->pkt_ip = ip_open(); + if (!ggtr->pkt_ip) { + printf("ip_open() failed\n"); + ggnet_traceroute_free(ggtr); + return NULL; + } + ggtr->pkt_rand = rand_open(); + + /* XXX get iface from route_open, route_get and pcap_lookupnet. + * will need to do so in ggnet_traceroute_trace() so user privs, so will need + * to prefetch the routes / interfaces, maybe */ + + ggtr->pcap = pcap_open_live(iface, 1500, 1, 500, ebuff); + if (ggtr->pcap == NULL) + err(1, "pcap_open_live()"); + ggtr->pcap_dllen = pcap_dloff(ggtr->pcap); + ggtr->pcap_fd = pcap_fileno(ggtr->pcap); + + return ggtr; +} + +void +ggnet_traceroute_free(struct ggnet_traceroute *ggtr) +{ + struct ggnet_traceroute_req *req; + + if (ggtr->pcap) + pcap_close(ggtr->pcap); + if (ggtr->pkt_ip) + ip_close(ggtr->pkt_ip); + if (ggtr->pkt_rand) + rand_close(ggtr->pkt_rand); + LIST_FOREACH(req, &ggtr->req_list, entry) + _req_free(req); + free(ggtr); +} + +struct ggnet_traceroute_req * +ggnet_traceroute_trace(struct ggnet_traceroute *ggtr, + struct in_addr *ip, + void (*cb_usr)(struct in_addr *, struct ggnet_traceroute_req *, void *), + void *data) +{ + struct ggnet_traceroute_req *req; + struct ggnet_traceroute_hop *target; + + req = calloc(1, sizeof(struct ggnet_traceroute_req)); + if (!req) { + printf("could not allocate ggnet_traceroute_req\n"); + exit(1); + } + target = calloc(1, sizeof(struct ggnet_traceroute_hop)); + if (!target) { + printf("cannot allocate ggnet_traceroute_hop\n"); + exit(1); + } + + addr_aton("127.0.0.1", &req->target->ip); + addr_aton("127.0.0.1", &req->srcip); + + req->ev_recv = event_new(ggtr->ev_base, + ggtr->pcap_fd, EV_READ, _cb_recv, req); + event_add(req->ev_recv, NULL); + req->ev_send = event_new(ggtr->ev_base, + ggtr->pcap_fd, EV_WRITE, _cb_send, req); + event_add(req->ev_send, NULL); + + // XXX evtimer timeout !!! + evtimer_new(ggtr->ev_base, _cb_trace_timeout, req); + + LIST_INSERT_HEAD(&ggtr->req_list, req, entry); + return req; +} + +void +ggnet_traceroute_cancel(struct ggnet_traceroute *ggtr, + struct ggnet_traceroute_req *req) +{ + LIST_REMOVE(req, entry); + ggtr->req_pending--; + _req_free(req); +} + +static void +_req_free(struct ggnet_traceroute_req *req) +{ + if (req->ev_recv) + event_free(req->ev_recv); + if (req->ev_send) + event_free(req->ev_send); + free(req); +} + +static void +_cb_recv(evutil_socket_t fd, short what, void *arg) +{ + struct ggnet_traceroute_hop *hop; + struct ggnet_traceroute_req *req; + struct ggnet_traceroute *ggtr; + struct pcap_pkthdr ph; + u_char *pread, *tmp; + struct ip_hdr *ip, ip_in; + struct tcp_hdr *tcp; + struct icmp_hdr *icmp; + struct addr ip_src; + char *reply = "U!"; + char *p; + + printf("cbggnet_traceroute cb_recv %s\n", addr_ntoa(&req->target->ip)); + req = arg; + ggtr = req->ggtr; + + if ((pread = (u_char *) pcap_next(ggtr->pcap, &ph)) == NULL) { + printf("libggnet_traceroute _cb_recv: read error\n"); + return; + } + + /* decode the current packet : ttl, ip, ... */ + tmp = pread + ctx->dl_len; + ip = (struct ip_hdr *) tmp; + if (ip_h->ip_v != 4) + return; + p = inet_ntoasc(ip_h->ip_src); + if ((addr_aton(p, &ip_src)) == -1) + return; + + switch(ip_h->ip_p) { + + case IP_PROTO_TCP: + /* should be our target */ + if (addr_cmp(&ip_src, &(req->target->ip)) != 0) + return; + tcp = (struct tcp_hdr *)(ip + IP_HDR_LEN); + if (((tcp->th_flags == TH_SYN + TH_ACK) || + (tcp->th_flags == TH_RST + TH_ACK)) && + (htons(tcp->th_sport) == ctx->dport)) { + hop = req->target; + hop->answer_count++; + flags = "SA"; + if (tcp->th_flags == TH_RST + TH_ACK) + flags = "R"; + strncpy(hop->answer, flags, sizeof(hop->answer)); + _timeout_set(req, 0, TIMEOUT_AFTERTARGET_US); + } + break; + + case IP_PROTO_ICMP: + /* answer from a hop */ + icmp = (struct icmp_hdr *)(ip + IP_HDR_LEN); + /* XXX ? look for TTL_EXPIRED, PROHIBITED msgs */ + ip_in = (struct iphdr *) (ip + sizeof(struct iphdr) + + sizeof(struct icmphdr)); + hop = _hop_ttl(req, ip_in->ttl); + if (!hop) { + printf("libggnet_traceroute _cb_recv: WARNING: hop ttl %d not found !", + ip_in->ttl); + return; + } + hop->answer_count++; + if (icmp->icmp_type == ICMP_UNREACH) { + switch (icmp->icmp_code) { + case ICMP_UNREACH_NET: + strncpy(hop->answer, "N!", sizeof(hop->answer)); + break; + case ICMP_UNREACH_HOST: + strncpy(hop->answer, "H!", sizeof(hop->answer)); + break; + case ICMP_UNREACH_PROTO: + strncpy(hop->answer, "P!", sizeof(hop->answer)); + break; + case ICMP_UNREACH_NET_PROHIB: + case ICMP_UNREACH_HOST_PROHIB: + strncpy(hop->answer, "A!", sizeof(hop->answer)); + break; + } + } else if (icmp->icmp_type == ICMP_TIMEXCEED) { + strncpy(hop->answer, "A!", sizeof(hop->answer)); + reply = "X!"; + event_add(&ctx->send_ev, &ctx->tv); + } + ctx->hop++; + break; + } + fflush(stdout); + + /* do we need to send more packets ? */ +} + +static void +_cb_send(evutil_socket_t fd, short what, void *arg) +{ + struct ggnet_traceroute_req *req; + struct ggnet_traceroute *ggtr; + u_char buf[BUFSIZ]; + struct trace_pkt *pkt; + int len; + int ttl; + + printf("cbggnet_traceroute cb_send %s\n", addr_ntoa(&req->ip)); + req = arg; + ggtr = req->ggtr; + pkt = (struct trace_pkt *)buf; + len = IP_HDR_LEN + TCP_HDR_LEN; + + for (ttl=req->ttl_sent; i<15; i++) { + hop = calloc(1, sizeof(struct ggnet_traceroute_hop)); + if (!hop) { + printf("cannot allocate ggnet_traceroute_hop\n"); + exit(1); + } + hop->ttl = ttl; + //addr_pack(hop->ip, ADDR_TYPE_IP, IP_ADDR_BITS, ip, IP_ADDR_LEN); + ip_pack_hdr(&pkt->pkt_hdr_i.ip, IP_TOS_LOWDELAY, len, + rand_uint16(ggtr->pkt_rand), 0, ttl, IP_PROTO_TCP, + req->src.addr_ip, req->dst.addr_ip); + tcp_pack_hdr(&pkt->pkt_hdr_t.tcp, rand_uint16(ggtr->pkt_rand), TRACE_DPORT, + rand_uint32(ggtr->pkt_rand), rand_uint32(ggtr->pkt_rand), + TH_SYN, rand_uint16(ggtr->pkt_rand), 0); + ip_checksum(pkt, len); + TAILQ_INSERT_TAIL(&req->hops_list, hop, entry); + for (i=0; i<TRACEROUTE_NB_PROBES; i++) + if (ip_send(ggtr->pkt_ip, pkt, len) < 0) + warn("_cb_send : ip_send short send"); + _timeout_set(req, TIMEOUT_TOTAL_S, 0); + } + req->ttl_sent = ttl; +} + +static void +_timeout_set(struct ggnet_traceroute_req *req, int sec, int usec) +{ + evtimer_del(req->run.ev_timeout); + req->run.tv_timeout.secs = sec; + req->run.tv_timeout.usecs = usec; + evtimer_add(req->run.ev_timeout, req->run.tv_timeout); +} + +static ggnet_traceroute_hop * +_hop_last(ggnet_traceroute_req *req) +{ + struct ggnet_traceroute_hop *h; + + TAILQ_FOREACH_REVERSE(h, req->hop_list, entry) { + if (h->answer_count > 0) + return h; + } + return NULL; +} + +void _cb_trace_timeout(evutil_socket_t fd, short what, void *arg) +{ + struct ggnet_traceroute_req *req; + + req = arg; + + // XXX IN PROGRESS + last = _hop_last(req); + req->target->ttl = last->ttl + 1; +} + +static int +pcap_dloff(pcap_t *pd) +{ + int i; + + i = pcap_datalink(pd); + + switch (i) { + case DLT_EN10MB: + i = 14; + break; + case DLT_IEEE802: + i = 22; + break; + case DLT_FDDI: + i = 21; + break; + case DLT_NULL: + i = 4; + break; + default: + i = -1; + break; + } + return (i); +} + +/* XXX do it with evdns +static int +get_asn(struct in_addr in) +{ + const u_char *uaddr = (const u_char *)&in.s_addr; + int i, counter; + struct rrsetinfo *answers = NULL; + char qbuf[MAXDNAME]; + int asn = -1; + + if (snprintf(qbuf, sizeof qbuf, "%u.%u.%u.%u.origin.asn.cymru.com", + (uaddr[3] & 0xff), (uaddr[2] & 0xff), + (uaddr[1] & 0xff), (uaddr[0] & 0xff)) >= sizeof (qbuf)) + return; + if (getrrsetbyname(qbuf, C_IN, T_TXT, 0, &answers) != 0) + return; + for (counter = 0; counter < answers->rri_nrdatas; counter++) { + char *p, *as = answers->rri_rdatas[counter].rdi_data; + as++; // skip first byte, it contains length + if (p = strchr(as,'|')) { + p[-1] = 0; + asn = atoi(as); + goto ret; + } + } + +ret: + freerrset(answers); + return asn; +} +*/ diff --git a/libglouglou/libggnet_traceroute.h b/libglouglou/libggnet_traceroute.h new file mode 100644 index 0000000..833705b --- /dev/null +++ b/libglouglou/libggnet_traceroute.h @@ -0,0 +1,62 @@ +#include <event.h> +#include <dnet.h> +#include <pcap.h> + +#if defined(__OpenBSD__) +#include <sys/queue.h> +#else +#include <bsd/sys/queue.h> +#endif + +#define TRACEROUTE_NB_PROBES 3 + +struct ggnet_traceroute_hop { + TAILQ_ENTRY(ggnet_traceroute_hop) entry; + int ttl; + int answer_count; /* on TRACEROUTE_NB_PROBES sent probes */ + char answer[2 * TRACEROUTE_NB_PROBES]; + struct addr ip; + int delay; + int delay_dev; + int loss; + int loss_dev; + int asn; +}; + +struct ggnet_traceroute_req { + LIST_ENTRY(ggnet_traceroute_req) entry; + struct ggnet_traceroute_hop *target; + struct addr srcip; + int probes_count; + int hops_count; + TAILQ_HEAD(, ggnet_traceroute_hop) hops_list; + struct { + struct event *ev_recv; + struct event *ev_send; + struct evtimer *ev_timeout; + struct timeval tv_timeout; + int last_ttl_sent; + } run; + void (*cb_usr)(struct in_addr *, char *, void *); + void *data; +}; + +struct ggnet_traceroute { + struct event_base *ev_base; + pcap_t *pcap; + ip_t *pkt_ip; + rand_t *pkt_rand; + int pcap_fd; + int pcap_dllen; /* datalink layer hdr len */ + int req_pending; + LIST_HEAD(, ggnet_traceroute_req) req_list; +}; + +struct ggnet_traceroute *ggnet_traceroute_new(struct event_base *, char *); +void ggnet_traceroute_free(struct ggnet_traceroute *); +struct ggnet_traceroute_req *ggnet_traceroute_trace(struct ggnet_traceroute *, + struct in_addr *ip, + void (*cb_usr)(struct in_addr *, struct ggnet_traceroute_req *, void *), + void *); +void ggnet_traceroute_cancel(struct ggnet_traceroute *, + struct ggnet_traceroute_req *); |