Basic code for the data distribution daemon

This commit is contained in:
Matthias Schiffer 2012-09-07 03:50:44 +02:00
parent 5998b096b9
commit f22dc0880f
9 changed files with 619 additions and 1 deletions

View file

@ -3,4 +3,5 @@ project(FFD C)
set(CMAKE_MODULE_PATH ${FFD_SOURCE_DIR})
add_subdirectory(ffvisd)
add_subdirectory(ffd)
#add_subdirectory(ffvisd)

10
ffd/CMakeLists.txt Normal file
View file

@ -0,0 +1,10 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FFD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR})
add_executable(ffd
ffd.c
netif.c
util.c
)
target_link_libraries(ffd rt)
install(TARGETS ffd RUNTIME DESTINATION sbin)

162
ffd/ffd.c Normal file
View file

@ -0,0 +1,162 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ffd.h"
#include "netif.h"
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netpacket/packet.h>
#define FFD_PROTO 0xffd
#define FFD_VERSION 0
static const eth_addr_t ffd_addr = {{0x03, 0x00, 0x00, 0x00, 0x0f, 0xfd}};
#define ANNOUNCE_INTERVAL 10
static char *mesh = "bat0";
static unsigned mtu = 1500;
static int sockfd;
static struct timespec now;
//static ffd_neigh_t self = {NULL, ETH_ADDR_UNSPEC, 0};
//static ffd_orig_t own_data = {NULL, ETH_ADDR_UNSPEC, 0, NULL};
//static ffd_neigh_t *neigh_data = NULL;
static ffd_orig_t *orig_data = NULL;
static void update_time() {
clock_gettime(CLOCK_MONOTONIC, &now);
}
static bool check_config() {
if (!netif_is_mesh(mesh)) {
fprintf(stderr, "error: configured interface is no mesh\n");
return false;
}
return true;
}
static bool init_socket() {
sockfd = socket(AF_PACKET, SOCK_DGRAM, htons(FFD_PROTO));
if (sockfd < 0) {
fprintf(stderr, "error: socket: %m\n");
return false;
}
return true;
}
static inline bool use_netif(const char *ifname) {
char *if_mesh = netif_get_mesh(ifname);
bool ret = (if_mesh && !strcmp(if_mesh, mesh));
free(if_mesh);
return ret;
}
static void join_mcast(const char *ifname, unsigned ifindex, void *arg) {
if (!use_netif(ifname))
return;
struct packet_mreq mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = ifindex;
mr.mr_type = PACKET_MR_MULTICAST;
mr.mr_alen = ETH_ALEN;
memcpy(mr.mr_address, ffd_addr.d, ETH_ALEN);
if (setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) && errno != EADDRINUSE)
fprintf(stderr, "warning: setsockopt: %m\n");
}
static void send_announce() {
if (!orig_data)
return;
}
static void receive_packet() {
uint8_t buf[mtu];
int readlen = read(sockfd, buf, sizeof(buf));
if (readlen < 0) {
fprintf(stderr, "error: read: %m\n");
return;
}
fprintf(stderr, "debug: read %i bytes.\n", readlen);
}
int main() {
if (!check_config())
return 1;
if (!init_socket())
return 1;
update_time();
struct timespec next_announce = now;
while (true) {
netif_foreach(join_mcast, NULL);
int timeout = timespec_diff(&next_announce, &now);
if (timeout <= 0) {
send_announce();
next_announce.tv_sec += ANNOUNCE_INTERVAL;
continue;
}
struct pollfd fds[1];
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, 1, timeout);
update_time();
if (fds[0].revents & POLLIN)
receive_packet();
}
return 0;
}

70
ffd/ffd.h Normal file
View file

@ -0,0 +1,70 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _FFD_FFD_H_
#define _FFD_FFD_H_
#include "util.h"
typedef enum _ffd_msg_type_t {
MSG_UNSPEC = 0,
MSG_SERVICE,
MSG_ROUTE4,
MSG_ROUTE6,
} ffd_msg_type_t;
typedef struct _ffd_msg_head_t {
struct _ffd_msg_head_t *next;
uint16_t changed_rev;
bool deleted;
ffd_msg_type_t type;
uint16_t len;
uint8_t data[];
} ffd_msg_head_t;
typedef struct _ffd_orig_t {
struct _ffd_orig_t *next;
uint64_t id;
eth_addr_t addr;
uint16_t orig_interval;
uint16_t rev;
ffd_msg_head_t *messages;
} ffd_orig_t;
typedef struct _ffd_neigh_t {
struct _ffd_neigh_t *next;
uint64_t id;
eth_addr_t addr;
uint16_t rev;
} ffd_neigh_t;
#endif /* _FFD_FFD_H_ */

182
ffd/netif.c Normal file
View file

@ -0,0 +1,182 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "netif.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sys/stat.h>
#include <sys/types.h>
#define SYSFS_PATH_MAX 256
#define SYSFS_CLASS_NET "/sys/class/net"
static bool netif_file_read(const char *ifname, const char *file, const char *format, ...) {
char filename[SYSFS_PATH_MAX];
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
va_list ap;
va_start(ap, format);
bool ret = file_readv(filename, format, ap);
va_end(ap);
return ret;
}
static bool netif_file_exists(const char *ifname, const char *file) {
char filename[SYSFS_PATH_MAX];
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
struct stat st;
return (stat(filename, &st) == 0);
}
bool netif_is_mesh(const char *ifname) {
return netif_file_exists(ifname, "mesh");
}
char* netif_get_mesh(const char *ifname) {
char *mesh = NULL;
if (!netif_file_read(ifname, "batman_adv/mesh_iface", "%as", &mesh) || !strcmp(mesh, "none")) {
free(mesh);
return NULL;
}
return mesh;
}
char* netif_get_bridge(const char *ifname) {
char filename[SYSFS_PATH_MAX], filename2[SYSFS_PATH_MAX] = {0};
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/brport/bridge", ifname);
if (readlink(filename, filename2, sizeof(filename2)) < 0)
return NULL;
return strdup(basename(filename2));
}
eth_addr_t netif_get_eth_addr(const char *ifname) {
eth_addr_t ret;
uint8_t *a = ret.d;
if (!netif_file_read(ifname, "address", "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))
return ETH_ADDR_UNSPEC;
return ret;
}
struct in6_addr netif_get_addr(unsigned ifindex, int scope) {
struct msg {
struct nlmsghdr nh;
struct ifaddrmsg addr;
uint8_t attrbuf[16384];
} msg;
memset(&msg, 0, sizeof(msg));
int rtnetlink_sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
msg.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
msg.nh.nlmsg_type = RTM_GETADDR;
msg.addr.ifa_family = AF_INET6;
if(write(rtnetlink_sk, &msg, msg.nh.nlmsg_len) <= 0) {
fprintf(stderr, "error: write: %m\n");
close(rtnetlink_sk);
return in6addr_any;
}
int readlen = read(rtnetlink_sk, &msg, sizeof(msg));
if (readlen <= 0) {
fprintf(stderr, "error: read: %m\n");
close(rtnetlink_sk);
return in6addr_any;
}
close(rtnetlink_sk);
struct msg *chunk;
for (chunk = &msg; readlen > sizeof(struct nlmsghdr);) {
int len = chunk->nh.nlmsg_len - sizeof(struct nlmsghdr);
if (len < sizeof(struct ifaddrmsg) || readlen < len)
return in6addr_any;
if (!NLMSG_OK(&chunk->nh, readlen))
return in6addr_any;
if (chunk->nh.nlmsg_type == RTM_NEWADDR && chunk->addr.ifa_scope == scope && chunk->addr.ifa_index == ifindex) {
struct rtattr *rta = (struct rtattr *)IFA_RTA(&chunk->addr);
int rtattrlen = IFA_PAYLOAD(&chunk->nh);
for (; RTA_OK(rta, rtattrlen); rta = RTA_NEXT(rta, rtattrlen)) {
if(rta->rta_type == IFA_ADDRESS && rta->rta_len == RTA_LENGTH(16)) {
struct in6_addr ret;
memcpy(ret.s6_addr, RTA_DATA(rta), 16);
return ret;
}
}
}
readlen -= NLMSG_ALIGN(chunk->nh.nlmsg_len);
chunk = (struct msg*)((uint8_t*)chunk + NLMSG_ALIGN(chunk->nh.nlmsg_len));
}
return in6addr_any;
}
void netif_foreach(netif_cb cb, void *arg) {
struct if_nameindex *ifaces = if_nameindex();
if (!ifaces) {
fprintf(stderr, "error: if_nameindex: %m\n");
return;
}
int i;
for (i = 0; ifaces[i].if_name; i++)
cb(ifaces[i].if_name, ifaces[i].if_index, arg);
if_freenameindex(ifaces);
}

56
ffd/netif.h Normal file
View file

@ -0,0 +1,56 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _FFD_NETIF_H_
#define _FFD_NETIF_H_
#include "types.h"
#include <netinet/in.h>
typedef enum _netif_type_t {
IF_UNKNOWN = 0,
IF_WIRED,
IF_WIRELESS,
IF_VIRTUAL,
IF_BRIDGE,
IF_MESH,
IF_MAX
} netif_type_t;
typedef void (*netif_cb)(const char *ifname, unsigned ifindex, void *arg);
bool netif_is_mesh(const char *ifname);
char* netif_get_mesh(const char *ifname);
char* netif_get_bridge(const char *ifname);
eth_addr_t netif_get_eth_addr(const char *ifname);
struct in6_addr netif_get_addr(unsigned ifindex, int scope);
void netif_foreach(netif_cb cb, void *arg);
#endif /* _FFD_NETIF_H_ */

44
ffd/types.h Normal file
View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _FFD_TYPES_H_
#define _FFD_TYPES_H_
#include <stdbool.h>
#include <stdint.h>
#include <net/ethernet.h>
typedef struct __attribute__((__packed__)) _eth_addr_t {
uint8_t d[ETH_ALEN];
} eth_addr_t;
#define ETH_ADDR_UNSPEC ((eth_addr_t){{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}})
#endif /* _FFD_TYPES_H_ */

42
ffd/util.c Normal file
View file

@ -0,0 +1,42 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "util.h"
#include <stdio.h>
bool file_readv(const char *file, const char *format, va_list ap) {
FILE *f = fopen(file, "r");
if (!f)
return false;
int ret = vfscanf(f, format, ap);
fclose(f);
return (ret > 0);
}

51
ffd/util.h Normal file
View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _FFD_UTIL_H_
#define _FFD_UTIL_H_
#include "types.h"
#include <stdarg.h>
#include <time.h>
bool file_readv(const char *file, const char *format, va_list ap);
static inline bool are_eth_addrs_equal(const eth_addr_t *address1, const eth_addr_t *address2) {
const uint8_t *a = address1->d;
const uint8_t *b = address2->d;
return (a[0]==b[0] && a[1]==b[1] && a[2]==b[2] && a[3]==b[3] && a[4]==b[4] && a[5]==b[5]);
}
/* returns (tp1 - tp2) in milliseconds */
static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) {
return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6;
}
#endif /* _FFD_UTIL_H_ */