initial commit

This commit is contained in:
Nick Peng
2018-04-22 23:14:26 +08:00
parent b82bf516e2
commit c6af293927
15 changed files with 3432 additions and 0 deletions

9
.clang-format Executable file
View File

@@ -0,0 +1,9 @@
#http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
MaxEmptyLinesToKeep: 1
AllowShortFunctionsOnASingleLine: Empty
BreakBeforeBraces: Linux

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.vscode
.o
.DS_Store
.swp.

15
Makefile Executable file
View File

@@ -0,0 +1,15 @@
BIN=smartdns
OBJS=smartdns.o fast_ping.o
CFLAGS=-g -O0
CFLAGS=-Iinclude
.PHONY: all
all: $(BIN)
$(BIN) : $(OBJS)
$(CC) $(OBJS) -o $@ -lpthread
clean:
$(RM) $(OBJS) $(BIN)

19
ReadMe.md Executable file
View File

@@ -0,0 +1,19 @@
Smart DNS
==============
Smard DNS会根据上级DNS服务返回的IP地址进行检查如果IP地址无效则继续进行DNS请求。
特性
--------------
1. 多级DNS配置
使用
==============
License
===============
GPL V2 License

750
fast_ping.c Executable file
View File

@@ -0,0 +1,750 @@
#include "fast_ping.h"
#include "atomic.h"
#include "hashtable.h"
#include <errno.h>
#include <linux/filter.h>
#include <netdb.h>
#include <netinet/icmp6.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_icmp.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PING_MAX_EVENTS 128
#define PING_MAX_HOSTLEN 128
#define ICMP_PACKET_SIZE (1024 * 64)
#define ICMP_INPACKET_SIZE 1024
struct fast_ping_packet_msg {
struct timeval tv;
};
struct fast_ping_packet {
union {
struct icmp icmp;
struct icmp6_hdr icmp6;
};
struct fast_ping_packet_msg msg;
};
struct ping_host_struct {
atomic_t ref;
struct hlist_node host_node;
struct hlist_node addr_node;
int type;
void *userptr;
char host[PING_MAX_HOSTLEN];
int fd;
unsigned int seq;
struct sockaddr_storage addr;
socklen_t addr_len;
struct fast_ping_packet packet;
};
struct fast_ping_struct {
int run;
pthread_t tid;
pthread_mutex_t lock;
int ident;
int epoll_fd;
int fd_icmp;
struct ping_host_struct icmp_host;
int fd_icmp6;
struct ping_host_struct icmp6_host;
pthread_mutex_t map_lock;
DECLARE_HASHTABLE(hostmap, 6);
DECLARE_HASHTABLE(addrmap, 6);
};
static struct fast_ping_struct ping;
static fast_ping_result ping_callback;
uint16_t _fast_ping_checksum(uint16_t *header, size_t len)
{
uint32_t sum = 0;
int i;
for (i = 0; i < len / sizeof(uint16_t); i++) {
sum += ntohs(header[i]);
}
return htons(~((sum >> 16) + (sum & 0xffff)));
}
int fast_ping_result_callback(fast_ping_result result)
{
ping_callback = result;
}
void _fast_ping_install_filter_v6(int sock)
{
struct icmp6_filter icmp6_filter;
ICMP6_FILTER_SETBLOCKALL(&icmp6_filter);
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &icmp6_filter);
setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &icmp6_filter, sizeof(struct icmp6_filter));
static int once;
static struct sock_filter insns[] = {
BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 4), /* Load icmp echo ident */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 0, 1), /* Ours? */
BPF_STMT(BPF_RET | BPF_K, ~0U), /* Yes, it passes. */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 0), /* Load icmp type */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */
BPF_STMT(BPF_RET | BPF_K, ~0U), /* No. It passes. This must not happen. */
BPF_STMT(BPF_RET | BPF_K, 0), /* Echo with wrong ident. Reject. */
};
static struct sock_fprog filter = { sizeof insns / sizeof(insns[0]), insns };
if (once) {
return;
}
once = 1;
/* Patch bpflet for current identifier. */
insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(getpid()), 0, 1);
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
perror("WARNING: failed to install socket filter\n");
}
}
void _fast_ping_install_filter_v4(int sock)
{
static int once;
static struct sock_filter insns[] = {
BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping6. */
BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 0, 1), /* Ours? */
BPF_STMT(BPF_RET | BPF_K, ~0U), /* Yes, it passes. */
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFF), /* No. It passes. */
BPF_STMT(BPF_RET | BPF_K, 0) /* Echo with wrong ident. Reject. */
};
static struct sock_fprog filter = { sizeof insns / sizeof(insns[0]), insns };
if (once) {
return;
}
once = 1;
/* Patch bpflet for current identifier. */
insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(getpid()), 0, 1);
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) {
perror("WARNING: failed to install socket filter\n");
}
}
static struct addrinfo *_fast_ping_getaddr(const u_char *host, int type, int protocol)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = type;
hints.ai_protocol = protocol;
if (getaddrinfo(host, NULL, &hints, &result) != 0) {
fprintf(stderr, "get addr info failed. %s\n", strerror(errno));
goto errout;
}
return result;
errout:
if (result) {
freeaddrinfo(result);
}
return NULL;
}
static int _fast_ping_getdomain(const u_char *host)
{
struct addrinfo hints;
struct addrinfo *result = NULL;
int domain = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if (getaddrinfo(host, NULL, &hints, &result) != 0) {
fprintf(stderr, "get addr info failed. %s\n", strerror(errno));
goto errout;
}
domain = result->ai_family;
freeaddrinfo(result);
return domain;
errout:
if (result) {
freeaddrinfo(result);
}
return -1;
}
int _fast_ping_host_get(struct ping_host_struct *ping_host)
{
atomic_inc(&ping_host->ref);
}
int _fast_ping_host_put(struct ping_host_struct *ping_host)
{
pthread_mutex_lock(&ping.map_lock);
if (atomic_dec_and_test(&ping_host->ref)) {
hlist_del(&ping_host->host_node);
hlist_del(&ping_host->addr_node);
} else {
ping_host = NULL;
}
pthread_mutex_unlock(&ping.map_lock);
if (ping_host == NULL) {
return -1;
}
free(ping_host);
}
static int _fast_ping_sendping_v6(struct ping_host_struct *ping_host)
{
struct fast_ping_packet *packet = &ping_host->packet;
struct icmp6_hdr *icmp6 = &packet->icmp6;
int len = 0;
ping_host->seq++;
memset(icmp6, 0, sizeof(*icmp6));
icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
icmp6->icmp6_code = 0;
icmp6->icmp6_cksum = 0;
icmp6->icmp6_id = getpid();
icmp6->icmp6_seq = htons(ping_host->seq);
gettimeofday(&packet->msg.tv, 0);
icmp6->icmp6_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet));
len = sendto(ping_host->fd, &ping_host->packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len);
if (len < 0 || len != sizeof(struct fast_ping_packet)) {
fprintf(stderr, "sendto %s\n", strerror(errno));
goto errout;
}
return 0;
errout:
return -1;
}
static int _fast_ping_sendping_v4(struct ping_host_struct *ping_host)
{
struct fast_ping_packet *packet = &ping_host->packet;
struct icmp *icmp = &packet->icmp;
int len;
ping_host->seq++;
memset(icmp, 0, sizeof(*icmp));
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_id = ping.ident;
icmp->icmp_seq = htons(ping_host->seq);
gettimeofday(&packet->msg.tv, 0);
icmp->icmp_cksum = _fast_ping_checksum((void *)packet, sizeof(struct fast_ping_packet));
len = sendto(ping_host->fd, packet, sizeof(struct fast_ping_packet), 0, (struct sockaddr *)&ping_host->addr, ping_host->addr_len);
if (len < 0 || len != sizeof(struct fast_ping_packet)) {
fprintf(stderr, "sendto %s\n", strerror(errno));
goto errout;
}
return 0;
errout:
return -1;
}
static int _fast_ping_sendping(struct ping_host_struct *ping_host)
{
if (ping_host->type == AF_INET) {
return _fast_ping_sendping_v4(ping_host);
} else if (ping_host->type == AF_INET6) {
return _fast_ping_sendping_v6(ping_host);
}
return -1;
}
static int _fast_ping_create_sock(int protocol)
{
int fd;
struct ping_host_struct *icmp_host = NULL;
struct epoll_event event;
fd = socket(AF_INET, SOCK_RAW, protocol);
if (fd < 0) {
fprintf(stderr, "create icmp socket failed.\n");
goto errout;
}
switch (protocol) {
case IPPROTO_ICMP:
_fast_ping_install_filter_v4(fd);
icmp_host = &ping.icmp_host;
break;
case IPPROTO_ICMPV6:
_fast_ping_install_filter_v6(fd);
icmp_host = &ping.icmp_host;
break;
}
event.events = EPOLLIN;
event.data.ptr = icmp_host;
if (epoll_ctl(ping.epoll_fd, EPOLL_CTL_ADD, fd, &event) != 0) {
goto errout;
}
icmp_host->fd = fd;
icmp_host->type = AF_PACKET;
return fd;
errout:
close(fd);
return -1;
}
static int _fast_ping_create_icmp(int protocol)
{
int fd = 0;
int *set_fd = NULL;
pthread_mutex_lock(&ping.lock);
switch (protocol) {
case IPPROTO_ICMP:
set_fd = &ping.fd_icmp;
break;
case IPPROTO_ICMPV6:
set_fd = &ping.fd_icmp6;
break;
default:
goto errout;
break;
}
if (*set_fd > 0) {
goto out;
}
fd = _fast_ping_create_sock(protocol);
if (fd < 0) {
goto errout;
}
*set_fd = fd;
out:
pthread_mutex_unlock(&ping.lock);
return *set_fd;
errout:
if (fd > 0) {
close(fd);
}
pthread_mutex_unlock(&ping.lock);
return -1;
}
int fast_ping_start(const char *host, int timeout, void *userptr)
{
struct ping_host_struct *ping_host = NULL;
struct addrinfo *gai = NULL;
int domain = -1;
int icmp_proto = 0;
char ip[PING_MAX_HOSTLEN];
uint32_t hostkey;
uint32_t addrkey;
domain = _fast_ping_getdomain(host);
if (domain < 0) {
return -1;
}
switch (domain) {
case AF_INET:
icmp_proto = IPPROTO_ICMP;
break;
case AF_INET6:
icmp_proto = IPPROTO_ICMPV6;
break;
default:
return -1;
break;
}
gai = _fast_ping_getaddr(host, SOCK_RAW, icmp_proto);
if (gai == NULL) {
return -1;
}
ping_host = malloc(sizeof(*ping_host));
if (ping_host == NULL) {
goto errout;
}
memset(ping_host, 0, sizeof(ping_host));
strncpy(ping_host->host, host, PING_MAX_HOSTLEN);
ping_host->type = domain;
ping_host->fd = _fast_ping_create_icmp(icmp_proto);
memcpy(&ping_host->addr, gai->ai_addr, gai->ai_addrlen);
ping_host->addr_len = gai->ai_addrlen;
atomic_set(&ping_host->ref, 0);
hostkey = hash_string(ping_host->host);
addrkey = jhash(&ping_host->addr, ping_host->addr_len, 0);
pthread_mutex_lock(&ping.map_lock);
_fast_ping_host_get(ping_host);
hash_add(ping.hostmap, &ping_host->host_node, hostkey);
hash_add(ping.addrmap, &ping_host->addr_node, addrkey);
pthread_mutex_unlock(&ping.map_lock);
freeaddrinfo(gai);
_fast_ping_sendping(ping_host);
return 0;
errout:
if (gai) {
freeaddrinfo(gai);
}
if (ping_host) {
free(ping_host);
}
return -1;
}
int fast_ping_stop(const char *host)
{
struct ping_host_struct *ping_host;
uint32_t key;
key = hash_string(host);
pthread_mutex_lock(&ping.map_lock);
hash_for_each_possible(ping.hostmap, ping_host, host_node, key)
{
if (strncmp(host, ping_host->host, PING_MAX_HOSTLEN) == 0) {
break;
}
}
if (ping_host == NULL) {
pthread_mutex_unlock(&ping.map_lock);
return -1;
}
pthread_mutex_unlock(&ping.map_lock);
_fast_ping_host_put(ping_host);
return 0;
}
void tv_sub(struct timeval *out, struct timeval *in)
{
if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */
--out->tv_sec;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
static int _fast_ping_icmp6_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv)
{
int hlen;
int icmp_len;
struct fast_ping_packet *packet = (struct fast_ping_packet *)packet_data;
struct icmp6_hdr *icmp6 = &packet->icmp6;
struct timeval tvresult = *tvrecv;
double rtt;
if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) {
return -1;
}
icmp_len = data_len;
if (icmp_len < 16) {
return -1;
}
if (icmp6->icmp6_id != ping.ident) {
return -1;
}
struct timeval *tvsend = &packet->msg.tv;
tv_sub(&tvresult, tvsend);
ping_callback(ping_host->host, ping_host->seq, &tvresult, ping_host->userptr);
return 0;
}
static int _fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len, struct timeval *tvrecv)
{
struct ip *ip = (struct ip *)packet_data;
struct fast_ping_packet *packet;
struct icmp *icmp;
struct timeval tvresult = *tvrecv;
int hlen;
int icmp_len;
if (ip->ip_p != IPPROTO_ICMP) {
return -1;
}
hlen = ip->ip_hl << 2;
packet = (struct fast_ping_packet *)(packet_data + hlen);
icmp = &packet->icmp;
icmp_len = data_len - hlen;
if (icmp_len < 16) {
return -1;
}
if (icmp->icmp_type != ICMP_ECHOREPLY) {
return -1;
}
if (icmp->icmp_id != ping.ident) {
return -1;
}
struct timeval *tvsend = &packet->msg.tv;
tv_sub(&tvresult, tvsend);
ping_callback(ping_host->host, ping_host->seq, &tvresult, ping_host->userptr);
return 0;
}
static int _fast_ping_recvping(struct ping_host_struct *ping_host, u_char *inpacket, int len, struct timeval *tvrecv)
{
if (ping_host->type == AF_INET6) {
if (_fast_ping_icmp6_packet(ping_host, inpacket, len, tvrecv)) {
goto errout;
}
} else if (ping_host->type == AF_INET) {
if (_fast_ping_icmp_packet(ping_host, inpacket, len, tvrecv)) {
goto errout;
}
}
return 0;
errout:
return -1;
}
static int _fast_ping_create_tcp(struct ping_host_struct *ping_host)
{
return -1;
}
static int _fast_ping_ping_host(struct ping_host_struct *ping_host) {}
static int _fast_ping_gethost_by_addr(u_char *host, struct sockaddr *addr, socklen_t addr_len)
{
struct sockaddr_storage *addr_store = (struct sockaddr_storage *)addr;
host[0] = 0;
switch (addr_store->ss_family) {
case AF_INET: {
struct sockaddr_in *addr_in;
addr_in = (struct sockaddr_in *)addr;
inet_ntop(AF_INET, &addr_in->sin_addr, host, addr_len);
} break;
case AF_INET6: {
struct sockaddr_in6 *addr_in6;
addr_in6 = (struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) {
struct sockaddr_in addr_in4;
memset(&addr_in4, 0, sizeof(addr_in4));
memcpy(&addr_in4.sin_addr.s_addr, addr_in6->sin6_addr.s6_addr + 12, sizeof(addr_in4.sin_addr.s_addr));
} else {
inet_ntop(AF_INET6, &addr_in6->sin6_addr, host, addr_len);
}
} break;
default:
goto errout;
break;
}
return 0;
errout:
return -1;
}
static int _fast_ping_process(struct ping_host_struct *ping_host, struct timeval *now)
{
int len;
u_char inpacket[ICMP_INPACKET_SIZE];
struct sockaddr_storage from;
struct ping_host_struct *recv_ping_host;
socklen_t from_len = sizeof(from);
uint32_t addrkey;
len = recvfrom(ping_host->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len);
if (len < 0) {
fprintf(stderr, "recvfrom failed, %s\n", strerror(errno));
goto errout;
}
addrkey = jhash(&from, from_len, 0);
pthread_mutex_lock(&ping.map_lock);
hash_for_each_possible(ping.addrmap, recv_ping_host, addr_node, addrkey)
{
if (recv_ping_host->addr_len == from_len && memcmp(&recv_ping_host->addr, &from, from_len) == 0) {
break;
}
}
pthread_mutex_unlock(&ping.map_lock);
if (recv_ping_host == NULL) {
return -1;
}
_fast_ping_recvping(recv_ping_host, inpacket, len, now);
return 0;
errout:
return -1;
}
static void _fast_ping_period_run()
{
struct ping_host_struct *ping_host;
struct hlist_node *tmp;
int i = 0;
pthread_mutex_lock(&ping.map_lock);
hash_for_each_safe(ping.addrmap, i, tmp, ping_host, addr_node)
{
_fast_ping_sendping(ping_host);
}
pthread_mutex_unlock(&ping.map_lock);
}
static void *_fast_ping_work(void *arg)
{
struct epoll_event events[PING_MAX_EVENTS + 1];
int num;
int i;
struct timeval last = { 0 };
struct timeval now = { 0 };
while (ping.run) {
if (last.tv_sec != now.tv_sec) {
_fast_ping_period_run();
last = now;
}
num = epoll_wait(ping.epoll_fd, events, PING_MAX_EVENTS, 1000);
if (num < 0) {
gettimeofday(&now, 0);
usleep(100000);
continue;
}
if (num == 0) {
gettimeofday(&now, 0);
continue;
}
gettimeofday(&now, 0);
for (i = 0; i < num; i++) {
struct epoll_event *event = &events[i];
struct ping_host_struct *ping_host = (struct ping_host_struct *)event->data.ptr;
_fast_ping_process(ping_host, &now);
}
}
close(ping.epoll_fd);
ping.epoll_fd = -1;
return NULL;
}
int fast_ping_init()
{
pthread_attr_t attr;
int epollfd = -1;
int ret;
if (ping.epoll_fd > 0) {
return -1;
}
memset(&ping, 0, sizeof(ping));
pthread_attr_init(&attr);
epollfd = epoll_create1(EPOLL_CLOEXEC);
if (epollfd < 0) {
fprintf(stderr, "create epoll failed, %s\n", strerror(errno));
goto errout;
}
ping.run = 1;
ret = pthread_create(&ping.tid, &attr, _fast_ping_work, NULL);
if (ret != 0) {
fprintf(stderr, "create ping work thread failed, %s\n", strerror(errno));
goto errout;
}
pthread_mutex_init(&ping.map_lock, 0);
pthread_mutex_init(&ping.lock, 0);
hash_init(ping.hostmap);
hash_init(ping.addrmap);
ping.epoll_fd = epollfd;
ping.ident = getpid();
return 0;
errout:
if (ping.tid > 0) {
void *retval = NULL;
ping.run = 0;
pthread_join(ping.tid, &retval);
}
if (epollfd) {
close(epollfd);
}
pthread_mutex_destroy(&ping.lock);
pthread_mutex_destroy(&ping.map_lock);
return -1;
}
int fast_ping_exit()
{
if (ping.tid > 0) {
void *ret = NULL;
ping.run = 0;
pthread_join(ping.tid, &ret);
}
if (ping.fd_icmp > 0) {
close(ping.fd_icmp);
ping.fd_icmp < 0;
}
if (ping.fd_icmp6 > 0) {
close(ping.fd_icmp6);
ping.fd_icmp6 < 0;
}
pthread_mutex_destroy(&ping.lock);
pthread_mutex_destroy(&ping.map_lock);
}

31
fast_ping.h Executable file
View File

@@ -0,0 +1,31 @@
#ifndef FAST_PING_H
#define FAST_PING_H
#include <sys/time.h>
#ifdef __cpluscplus
extern "C" {
#endif
typedef enum {
FAST_PING_ICMP = 1,
FAST_PING_TCP,
FAST_PING_UDP
} FAST_PING_TYPE;
typedef void (*fast_ping_result)(const char *host, int seqno, struct timeval *tv, void *userptr);
int fast_ping_result_callback(fast_ping_result result);
int fast_ping_start(const char *host, int timeout, void *userptr);
int fast_ping_stop(const char *host);
int fast_ping_init();
int fast_ping_exit();
#ifdef __cpluscplus
}
#endif
#endif // !FAST_PING_H

133
include/atomic.h Executable file
View File

@@ -0,0 +1,133 @@
//Source: http://golubenco.org/atomic-operations.html
#ifndef _GENERIC_ATOMIC_H
#define _GENERIC_ATOMIC_H
/* Check GCC version, just to be safe */
#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC_MINOR__ < 1)
# error atomic.h works only with GCC newer than version 4.1
#endif /* GNUC >= 4.1 */
/**
* Atomic type.
*/
typedef struct {
volatile int counter;
} atomic_t;
#define ATOMIC_INIT(i) { (i) }
/**
* Read atomic variable
* @param v pointer of type atomic_t
*
* Atomically reads the value of @v.
*/
#define atomic_read(v) ((v)->counter)
/**
* Set atomic variable
* @param v pointer of type atomic_t
* @param i required value
*/
#define atomic_set(v,i) (((v)->counter) = (i))
/**
* Add to the atomic variable
* @param i integer value to add
* @param v pointer of type atomic_t
*/
static inline void atomic_add( int i, atomic_t *v )
{
(void)__sync_add_and_fetch(&v->counter, i);
}
/**
* Subtract the atomic variable
* @param i integer value to subtract
* @param v pointer of type atomic_t
*
* Atomically subtracts @i from @v.
*/
static inline void atomic_sub( int i, atomic_t *v )
{
(void)__sync_sub_and_fetch(&v->counter, i);
}
/**
* Subtract value from variable and test result
* @param i integer value to subtract
* @param v pointer of type atomic_t
*
* Atomically subtracts @i from @v and returns
* true if the result is zero, or false for all
* other cases.
*/
static inline int atomic_sub_and_test( int i, atomic_t *v )
{
return !(__sync_sub_and_fetch(&v->counter, i));
}
/**
* Increment atomic variable
* @param v pointer of type atomic_t
*
* Atomically increments @v by 1.
*/
static inline void atomic_inc( atomic_t *v )
{
(void)__sync_fetch_and_add(&v->counter, 1);
}
/**
* @brief decrement atomic variable
* @param v: pointer of type atomic_t
*
* Atomically decrements @v by 1. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static inline void atomic_dec( atomic_t *v )
{
(void)__sync_fetch_and_sub(&v->counter, 1);
}
/**
* @brief Decrement and test
* @param v pointer of type atomic_t
*
* Atomically decrements @v by 1 and
* returns true if the result is 0, or false for all other
* cases.
*/
static inline int atomic_dec_and_test( atomic_t *v )
{
return !(__sync_sub_and_fetch(&v->counter, 1));
}
/**
* @brief Increment and test
* @param v pointer of type atomic_t
*
* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases.
*/
static inline int atomic_inc_and_test( atomic_t *v )
{
return !(__sync_add_and_fetch(&v->counter, 1));
}
/**
* @brief add and test if negative
* @param v pointer of type atomic_t
* @param i integer value to add
*
* Atomically adds @i to @v and returns true
* if the result is negative, or false when
* result is greater than or equal to zero.
*/
static inline int atomic_add_negative( int i, atomic_t *v )
{
return (__sync_add_and_fetch(&v->counter, i) < 0);
}
#endif

132
include/bitmap.h Normal file
View File

@@ -0,0 +1,132 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PERF_BITOPS_H
#define _PERF_BITOPS_H
#include <string.h>
#include <stdlib.h>
#include "bitops.h"
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
int __bitmap_weight(const unsigned long *bitmap, int bits);
void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int bits);
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
((nbits) % BITS_PER_LONG) ? \
(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
)
#define small_const_nbits(nbits) \
(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
static inline void bitmap_zero(unsigned long *dst, int nbits)
{
if (small_const_nbits(nbits))
*dst = 0UL;
else {
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memset(dst, 0, len);
}
}
static inline void bitmap_fill(unsigned long *dst, unsigned int nbits)
{
unsigned int nlongs = BITS_TO_LONGS(nbits);
if (!small_const_nbits(nbits)) {
unsigned int len = (nlongs - 1) * sizeof(unsigned long);
memset(dst, 0xff, len);
}
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
}
static inline int bitmap_empty(const unsigned long *src, unsigned nbits)
{
if (small_const_nbits(nbits))
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
return find_first_bit(src, nbits) == nbits;
}
static inline int bitmap_full(const unsigned long *src, unsigned int nbits)
{
if (small_const_nbits(nbits))
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
return find_first_zero_bit(src, nbits) == nbits;
}
static inline int bitmap_weight(const unsigned long *src, int nbits)
{
if (small_const_nbits(nbits))
return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
return __bitmap_weight(src, nbits);
}
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_const_nbits(nbits))
*dst = *src1 | *src2;
else
__bitmap_or(dst, src1, src2, nbits);
}
/**
* test_and_set_bit - Set a bit and return its old value
* @nr: Bit to set
* @addr: Address to count from
*/
static inline int test_and_set_bit(int nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
unsigned long old;
old = *p;
*p = old | mask;
return (old & mask) != 0;
}
/**
* bitmap_alloc - Allocate bitmap
* @nbits: Number of bits
*/
static inline unsigned long *bitmap_alloc(int nbits)
{
return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long));
}
/*
* bitmap_scnprintf - print bitmap list into buffer
* @bitmap: bitmap
* @nbits: size of bitmap
* @buf: buffer to store output
* @size: size of @buf
*/
size_t bitmap_scnprintf(unsigned long *bitmap, int nbits,
char *buf, size_t size);
/**
* bitmap_and - Do logical and on bitmaps
* @dst: resulting bitmap
* @src1: operand 1
* @src2: operand 2
* @nbits: size of bitmap
*/
static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, unsigned int nbits)
{
if (small_const_nbits(nbits))
return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0;
return __bitmap_and(dst, src1, src2, nbits);
}
#endif /* _PERF_BITOPS_H */

65
include/bitops.h Normal file
View File

@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _GENERIC_BITOPS_H_
#define _GENERIC_BITOPS_H_
#ifndef __WORDSIZE
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
#endif
#ifndef BITS_PER_LONG
# define BITS_PER_LONG __WORDSIZE
#endif
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
extern unsigned int __sw_hweight8(unsigned int w);
extern unsigned int __sw_hweight16(unsigned int w);
extern unsigned int __sw_hweight32(unsigned int w);
extern unsigned long __sw_hweight64(uint64_t w);
#define for_each_set_bit(bit, addr, size) \
for ((bit) = find_first_bit((addr), (size)); \
(bit) < (size); \
(bit) = find_next_bit((addr), (size), (bit) + 1))
#define for_each_clear_bit(bit, addr, size) \
for ((bit) = find_first_zero_bit((addr), (size)); \
(bit) < (size); \
(bit) = find_next_zero_bit((addr), (size), (bit) + 1))
/* same as for_each_set_bit() but use bit as value to start with */
#define for_each_set_bit_from(bit, addr, size) \
for ((bit) = find_next_bit((addr), (size), (bit)); \
(bit) < (size); \
(bit) = find_next_bit((addr), (size), (bit) + 1))
static inline unsigned long hweight_long(unsigned long w)
{
return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
}
static inline unsigned fls_long(unsigned long l)
{
if (sizeof(l) == 4)
return fls(l);
return fls64(l);
}
/**
* rol32 - rotate a 32-bit value left
* @word: value to rotate
* @shift: bits to roll
*/
static inline uint32_t rol32(uint32_t word, unsigned int shift)
{
return (word << shift) | (word >> ((-shift) & 31));
}
#endif

201
include/hash.h Normal file
View File

@@ -0,0 +1,201 @@
#ifndef _GENERIC_HASH_H
#define _GENERIC_HASH_H
#include "jhash.h"
/* Fast hashing routine for ints, longs and pointers.
(C) 2002 Nadia Yvette Chambers, IBM */
#ifndef __WORDSIZE
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
#endif
#ifndef BITS_PER_LONG
# define BITS_PER_LONG __WORDSIZE
#endif
/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
* - the arch is not required to handle n==0 if implementing the fallback
*/
static inline __attribute__((const))
int __ilog2_u32(uint32_t n)
{
return fls(n) - 1;
}
static inline __attribute__((const))
int __ilog2_u64(uint64_t n)
{
return fls64(n) - 1;
}
/**
* ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value
* @n - parameter
*
* constant-capable log of base 2 calculation
* - this can be used to initialise global variables from constant data, hence
* the massive ternary operator construction
*
* selects the appropriately-sized optimised version depending on sizeof(n)
*/
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
(n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
(n) & (1ULL << 60) ? 60 : \
(n) & (1ULL << 59) ? 59 : \
(n) & (1ULL << 58) ? 58 : \
(n) & (1ULL << 57) ? 57 : \
(n) & (1ULL << 56) ? 56 : \
(n) & (1ULL << 55) ? 55 : \
(n) & (1ULL << 54) ? 54 : \
(n) & (1ULL << 53) ? 53 : \
(n) & (1ULL << 52) ? 52 : \
(n) & (1ULL << 51) ? 51 : \
(n) & (1ULL << 50) ? 50 : \
(n) & (1ULL << 49) ? 49 : \
(n) & (1ULL << 48) ? 48 : \
(n) & (1ULL << 47) ? 47 : \
(n) & (1ULL << 46) ? 46 : \
(n) & (1ULL << 45) ? 45 : \
(n) & (1ULL << 44) ? 44 : \
(n) & (1ULL << 43) ? 43 : \
(n) & (1ULL << 42) ? 42 : \
(n) & (1ULL << 41) ? 41 : \
(n) & (1ULL << 40) ? 40 : \
(n) & (1ULL << 39) ? 39 : \
(n) & (1ULL << 38) ? 38 : \
(n) & (1ULL << 37) ? 37 : \
(n) & (1ULL << 36) ? 36 : \
(n) & (1ULL << 35) ? 35 : \
(n) & (1ULL << 34) ? 34 : \
(n) & (1ULL << 33) ? 33 : \
(n) & (1ULL << 32) ? 32 : \
(n) & (1ULL << 31) ? 31 : \
(n) & (1ULL << 30) ? 30 : \
(n) & (1ULL << 29) ? 29 : \
(n) & (1ULL << 28) ? 28 : \
(n) & (1ULL << 27) ? 27 : \
(n) & (1ULL << 26) ? 26 : \
(n) & (1ULL << 25) ? 25 : \
(n) & (1ULL << 24) ? 24 : \
(n) & (1ULL << 23) ? 23 : \
(n) & (1ULL << 22) ? 22 : \
(n) & (1ULL << 21) ? 21 : \
(n) & (1ULL << 20) ? 20 : \
(n) & (1ULL << 19) ? 19 : \
(n) & (1ULL << 18) ? 18 : \
(n) & (1ULL << 17) ? 17 : \
(n) & (1ULL << 16) ? 16 : \
(n) & (1ULL << 15) ? 15 : \
(n) & (1ULL << 14) ? 14 : \
(n) & (1ULL << 13) ? 13 : \
(n) & (1ULL << 12) ? 12 : \
(n) & (1ULL << 11) ? 11 : \
(n) & (1ULL << 10) ? 10 : \
(n) & (1ULL << 9) ? 9 : \
(n) & (1ULL << 8) ? 8 : \
(n) & (1ULL << 7) ? 7 : \
(n) & (1ULL << 6) ? 6 : \
(n) & (1ULL << 5) ? 5 : \
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
)
/*
* This hash multiplies the input by a large odd number and takes the
* high bits. Since multiplication propagates changes to the most
* significant end only, it is essential that the high bits of the
* product be used for the hash value.
*
* Chuck Lever verified the effectiveness of this technique:
* http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
*
* Although a random odd number will do, it turns out that the golden
* ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
* properties. (See Knuth vol 3, section 6.4, exercise 9.)
*
* These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2,
* which is very slightly easier to multiply by and makes no
* difference to the hash distribution.
*/
#define GOLDEN_RATIO_32 0x61C88647
#define GOLDEN_RATIO_64 0x61C8864680B583EBull
/*
* The _generic versions exist only so lib/test_hash.c can compare
* the arch-optimized versions with the generic.
*
* Note that if you change these, any <asm/hash.h> that aren't updated
* to match need to have their HAVE_ARCH_* define values updated so the
* self-test will not false-positive.
*/
#ifndef HAVE_ARCH__HASH_32
#define __hash_32 __hash_32_generic
#endif
static inline uint32_t __hash_32_generic(uint32_t val)
{
return val * GOLDEN_RATIO_32;
}
#ifndef HAVE_ARCH_HASH_32
#define hash_32 hash_32_generic
#endif
static inline uint32_t hash_32_generic(uint32_t val, unsigned int bits)
{
/* High bits are more random, so use them. */
return __hash_32(val) >> (32 - bits);
}
#ifndef HAVE_ARCH_HASH_64
#define hash_64 hash_64_generic
#endif
static inline uint32_t hash_64(uint64_t val, unsigned int bits)
{
#if BITS_PER_LONG == 64
/* 64x64-bit multiply is efficient on all 64-bit processors */
return val * GOLDEN_RATIO_64 >> (64 - bits);
#else
/* Hash 64 bits using only 32x32-bit multiply. */
return hash_32((uint32_t)val ^ __hash_32(val >> 32), bits);
#endif
}
static inline uint32_t hash_ptr(const void *ptr, unsigned int bits)
{
return hash_long((unsigned long)ptr, bits);
}
/* This really should be called fold32_ptr; it does no hashing to speak of. */
static inline uint32_t hash32_ptr(const void *ptr)
{
unsigned long val = (unsigned long)ptr;
#if BITS_PER_LONG == 64
val ^= (val >> 32);
#endif
return (uint32_t)val;
}
static inline unsigned long
hash_string(const char *str)
{
unsigned long v = 0;
const char *c;
for (c = str; *c; )
v = (((v << 1) + (v >> 14)) ^ (*c++)) & 0x3fff;
return(v);
}
#endif /* _GENERIC_HASH_H */

153
include/hashtable.h Normal file
View File

@@ -0,0 +1,153 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Statically sized hash table implementation
* (C) 2012 Sasha Levin <levinsasha928@gmail.com>
*/
#ifndef _GENERIC_HASHTABLE_H
#define _GENERIC_HASHTABLE_H
#include "list.h"
#include "hash.h"
#include "bitmap.h"
#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#endif
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
#define DEFINE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
#define DECLARE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)]
#define HASH_SIZE(name) (ARRAY_SIZE(name))
#define HASH_BITS(name) ilog2(HASH_SIZE(name))
/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
#define hash_min(val, bits) \
(sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @hashtable: hashtable to be initialized
*
* Calculates the size of the hashtable from the given parameter, otherwise
* same as hash_init_size.
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
/**
* hash_add - add an object to a hashtable
* @hashtable: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add(hashtable, node, key) \
hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
/**
* hash_hashed - check whether an object is in any hashtable
* @node: the &struct hlist_node of the object to be checked
*/
static inline bool hash_hashed(struct hlist_node *node)
{
return !hlist_unhashed(node);
}
static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
if (!hlist_empty(&ht[i]))
return false;
return true;
}
/**
* hash_empty - check whether a hashtable is empty
* @hashtable: hashtable to check
*
* This has to be a macro since HASH_BITS() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_for_each - iterate over a hashtable
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each(name, bkt, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry(obj, &name[bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @name: hashtable to iterate
* @bkt: integer to use as bucket loop cursor
* @tmp: a &struct used for temporary storage
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(name, bkt, tmp, obj, member) \
for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
(bkt)++)\
hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible(name, obj, member, key) \
hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @name: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp,\
&name[hash_min(key, HASH_BITS(name))], member)
#endif

184
include/jhash.h Normal file
View File

@@ -0,0 +1,184 @@
#ifndef _JHASH_H
#define _JHASH_H
#include <stdint.h>
/* jhash.h: Jenkins hash support.
*
* Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
*
* http://burtleburtle.net/bob/hash/
*
* These are the credits from Bob's sources:
*
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
*
* These are functions for producing 32-bit hashes for hash table lookup.
* hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
* are externally useful functions. Routines to test the hash are included
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
* Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
*
* I've modified Bob's hash to be useful in the Linux kernel, and
* any bugs present are my fault.
* Jozsef
*/
#include "bitops.h"
/* Best hash sizes are of power of two */
#define jhash_size(n) ((uint32_t)1<<(n))
/* Mask the hash value, i.e (value & jhash_mask(n)) instead of (value % n) */
#define jhash_mask(n) (jhash_size(n)-1)
/* __jhash_mix -- mix 3 32-bit values reversibly. */
#define __jhash_mix(a, b, c) \
{ \
a -= c; a ^= rol32(c, 4); c += b; \
b -= a; b ^= rol32(a, 6); a += c; \
c -= b; c ^= rol32(b, 8); b += a; \
a -= c; a ^= rol32(c, 16); c += b; \
b -= a; b ^= rol32(a, 19); a += c; \
c -= b; c ^= rol32(b, 4); b += a; \
}
/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */
#define __jhash_final(a, b, c) \
{ \
c ^= b; c -= rol32(b, 14); \
a ^= c; a -= rol32(c, 11); \
b ^= a; b -= rol32(a, 25); \
c ^= b; c -= rol32(b, 16); \
a ^= c; a -= rol32(c, 4); \
b ^= a; b -= rol32(a, 14); \
c ^= b; c -= rol32(b, 24); \
}
/* An arbitrary initial parameter */
#define JHASH_INITVAL 0xdeadbeef
struct __una_u32 { uint32_t x; } __packed;
static inline uint32_t __get_unaligned_cpu32(const void *p)
{
const struct __una_u32 *ptr = (const struct __una_u32 *)p;
return ptr->x;
}
/* jhash - hash an arbitrary key
* @k: sequence of bytes as key
* @length: the length of the key
* @initval: the previous hash, or an arbitray value
*
* The generic version, hashes an arbitrary sequence of bytes.
* No alignment or length assumptions are made about the input key.
*
* Returns the hash value of the key. The result depends on endianness.
*/
static inline uint32_t jhash(const void *key, uint32_t length, uint32_t initval)
{
uint32_t a, b, c;
const uint8_t *k = key;
/* Set up the internal state */
a = b = c = JHASH_INITVAL + length + initval;
/* All but the last block: affect some 32 bits of (a,b,c) */
while (length > 12) {
a += __get_unaligned_cpu32(k);
b += __get_unaligned_cpu32(k + 4);
c += __get_unaligned_cpu32(k + 8);
__jhash_mix(a, b, c);
length -= 12;
k += 12;
}
/* Last block: affect all 32 bits of (c) */
/* All the case statements fall through */
switch (length) {
case 12: c += (uint32_t)k[11]<<24;
case 11: c += (uint32_t)k[10]<<16;
case 10: c += (uint32_t)k[9]<<8;
case 9: c += k[8];
case 8: b += (uint32_t)k[7]<<24;
case 7: b += (uint32_t)k[6]<<16;
case 6: b += (uint32_t)k[5]<<8;
case 5: b += k[4];
case 4: a += (uint32_t)k[3]<<24;
case 3: a += (uint32_t)k[2]<<16;
case 2: a += (uint32_t)k[1]<<8;
case 1: a += k[0];
__jhash_final(a, b, c);
case 0: /* Nothing left to add */
break;
}
return c;
}
/* jhash2 - hash an array of uint32_t's
* @k: the key which must be an array of uint32_t's
* @length: the number of uint32_t's in the key
* @initval: the previous hash, or an arbitray value
*
* Returns the hash value of the key.
*/
static inline uint32_t jhash2(const uint32_t *k, uint32_t length, uint32_t initval)
{
uint32_t a, b, c;
/* Set up the internal state */
a = b = c = JHASH_INITVAL + (length<<2) + initval;
/* Handle most of the key */
while (length > 3) {
a += k[0];
b += k[1];
c += k[2];
__jhash_mix(a, b, c);
length -= 3;
k += 3;
}
/* Handle the last 3 uint32_t's: all the case statements fall through */
switch (length) {
case 3: c += k[2];
case 2: b += k[1];
case 1: a += k[0];
__jhash_final(a, b, c);
case 0: /* Nothing left to add */
break;
}
return c;
}
/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
static inline uint32_t __jhash_nwords(uint32_t a, uint32_t b, uint32_t c, uint32_t initval)
{
a += initval;
b += initval;
c += initval;
__jhash_final(a, b, c);
return c;
}
static inline uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval)
{
return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2));
}
static inline uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval)
{
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
}
static inline uint32_t jhash_1word(uint32_t a, uint32_t initval)
{
return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2));
}
#endif /* _JHASH_H */

792
include/list.h Normal file
View File

@@ -0,0 +1,792 @@
#ifndef _GENERIC_LIST_H
#define _GENERIC_LIST_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#ifndef container_of
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
#define LIST_POISON1 ((void *) 0x100)
#define LIST_POISON2 ((void *) 0x200)
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_first_entry_or_null - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note that if the list is empty, it returns NULL.
*/
#define list_first_entry_or_null(ptr, type, member) \
(!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
pos != (head); \
pos = n, n = pos->prev)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_head within the struct.
*
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
/**
* list_for_each_entry_continue - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Continue to iterate over list of given type, continuing after
* the current position.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_next_entry(pos, member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_continue_reverse - iterate backwards from the given point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Start to iterate over list of given type backwards, continuing after
* the current position.
*/
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; &pos->member != (head); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate over list of given type from current point, safe against
* removal of list entry.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
/**
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*
* Iterate backwards over list of given type, safe against removal
* of list entry.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member), \
n = list_prev_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_prev_entry(n, member))
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
* @n: temporary storage used in list_for_each_entry_safe
* @member: the name of the list_head within the struct.
*
* list_safe_reset_next is not safe to use in general if the list may be
* modified concurrently (eg. the lock is dropped in the loop body). An
* exception to this is if the cursor element (pos) is pinned in the list,
* and list_safe_reset_next is called after re-taking the lock and before
* completing the current iteration of the loop body.
*/
#define list_safe_reset_next(pos, n, member) \
n = list_next_entry(pos, member)
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
n->next = prev->next;
prev->next = n;
n->pprev = &prev->next;
if (n->next)
n->next->pprev = &n->next;
}
/* after that we'll appear to be on some hlist and hlist_del will work */
static inline void hlist_add_fake(struct hlist_node *n)
{
n->pprev = &n->next;
}
static inline bool hlist_fake(struct hlist_node *h)
{
return h->pprev == &h->next;
}
/*
* Move a list from one list head to another. Fixup the pprev
* reference of the first entry if it exists.
*/
static inline void hlist_move_list(struct hlist_head *old,
struct hlist_head *new)
{
new->first = old->first;
if (new->first)
new->first->pprev = &new->first;
old->first = NULL;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(pos, member) \
for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
* @pos: the type * to use as a loop cursor.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(pos, member) \
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
/**
* list_del_range - deletes range of entries from list.
* @begin: first element in the range to delete from the list.
* @end: last element in the range to delete from the list.
* Note: list_empty on the range of entries does not return true after this,
* the entries is in an undefined state.
*/
static inline void list_del_range(struct list_head *begin,
struct list_head *end)
{
begin->prev->next = end->next;
end->next->prev = begin->prev;
}
/**
* list_for_each_from - iterate over a list from one of its nodes
* @pos: the &struct list_head to use as a loop cursor, from where to start
* @head: the head for your list.
*/
#define list_for_each_from(pos, head) \
for (; pos != (head); pos = pos->next)
#endif /* _GENERIC_LIST_H */

189
smartdns.c Executable file
View File

@@ -0,0 +1,189 @@
#include <stdio.h>
#include <stdlib.h>
#include "fast_ping.h"
#include "list.h"
#include "bitmap.h"
#include "hashtable.h"
void smartdns_ping_result(const char *host, int seqno, struct timeval *tv, void *userptr)
{
double rtt = tv->tv_sec * 1000.0 + tv->tv_usec / 1000.0;
printf("%16s: seq=%d time=%.3f\n", host, seqno, rtt);
}
int smartdns_init()
{
int ret;
ret = fast_ping_init();
if (ret != 0) {
fprintf(stderr, "start ping failed.\n");
goto errout;
}
fast_ping_result_callback(smartdns_ping_result);
return 0;
errout:
return -1;
}
int smartdns_run()
{
sleep(1);
fast_ping_start("192.168.1.1", 2000, 0);
fast_ping_start("192.168.1.35", 2000, 0);
fast_ping_start("14.215.177.38", 2000, 0);
fast_ping_start("113.96.161.87", 2000, 0);
//fast_ping_start("::1", 2000, 0);
while (1) {
sleep(10);
//fast_ping_stop("192.168.1.35");
}
}
void smartdns_exit()
{
fast_ping_exit();
}
struct data {
struct list_head list;
int n;
};
void list_test()
{
struct list_head head ;
struct list_head *iter;
int i = 0;
INIT_LIST_HEAD(&head);
for (i = 0; i < 10; i++) {
struct data *h = malloc(sizeof(struct data));
h->n = i;
list_add(&h->list, &head);
}
list_for_each(iter, &head) {
struct data *d= list_entry(iter, struct data, list);
printf("%d\n", d->n);
}
}
struct data_hash {
struct hlist_node node;
int n;
char str[32];
};
int hash_test()
{
DEFINE_HASHTABLE(ht, 7);
struct data_hash *temp;
struct data_hash *obj;
int i;
int j;
int key;
for (i = 11; i < 17; i++) {
temp = malloc(sizeof(struct data_hash));
temp->n = i * i;
hash_add(ht, &temp->node, temp->n);
}
for (i = 11; i < 17; i++) {
key = i * i;
hash_for_each_possible(ht, obj, node, key) {
printf("value: %d\n", obj->n);
};
}
return 0;
}
int hash_string_test()
{
DEFINE_HASHTABLE(ht, 7);
struct data_hash *temp;
struct data_hash *obj;
int i;
int j;
int key;
for (i = 0; i < 10; i++) {
temp = malloc(sizeof(struct data_hash));
sprintf(temp->str, "%d", i);
hash_add(ht, &temp->node, hash_string(temp->str));
}
for (i = 0; i < 10; i++) {
char key_str[32];
sprintf(key_str, "%d", i);
key = hash_string(key_str);
hash_for_each_possible(ht, obj, node, key) {
printf("i = %d value: %s\n", i, obj->str);
};
}
return 0;
}
#if 0
struct data_rbtree {
struct rb_node list;
int value;
};
int rbtree_test()
{
struct rb_root root;
struct rb_node *n;
RB_EMPTY_ROOT(&root);
int i;
for (i = 0; i < 10; i++)
{
struct data_rbtree *r = malloc(sizeof(struct data_rbtree));
r->value = i;
rb_insert(&r->list, &root);
}
n = rb_first(&root);
int num = 5;
while (n) {
struct data_rbtree *r = container_of(n, struct data_rbtree, list);
if (r->value < num) {
n = n->rb_left;
} else if (r->value > num) {
n = n->rb_right;
} else {
printf("n = %d\n", r->value);
break;
}
}
return 0;
}
#endif
int main(int argc, char *argv[])
{
int ret;
atexit(smartdns_exit);
ret = smartdns_init();
if (ret != 0) {
fprintf(stderr, "init smartdns failed.\n");
goto errout;
}
return smartdns_run();
errout:
return 1;
}

755
test-dns.c Executable file
View File

@@ -0,0 +1,755 @@
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#define BUF_SIZE 1500
/*
* This software is licensed under the CC0.
*
* This is a _basic_ DNS Server for educational use.
* It does not prevent invalid packets from crashing
* the server.
*
* To test start the program and issue a DNS request:
* dig @127.0.0.1 -p 9000 foo.bar.com
*/
/*
* Masks and constants.
*/
static const uint32_t QR_MASK = 0x8000;
static const uint32_t OPCODE_MASK = 0x7800;
static const uint32_t AA_MASK = 0x0400;
static const uint32_t TC_MASK = 0x0200;
static const uint32_t RD_MASK = 0x0100;
static const uint32_t RA_MASK = 0x8000;
static const uint32_t RCODE_MASK = 0x000F;
/* Response Type */
enum {
Ok_ResponseType = 0,
FormatError_ResponseType = 1,
ServerFailure_ResponseType = 2,
NameError_ResponseType = 3,
NotImplemented_ResponseType = 4,
Refused_ResponseType = 5
};
/* Resource Record Types */
enum {
A_Resource_RecordType = 1,
NS_Resource_RecordType = 2,
CNAME_Resource_RecordType = 5,
SOA_Resource_RecordType = 6,
PTR_Resource_RecordType = 12,
MX_Resource_RecordType = 15,
TXT_Resource_RecordType = 16,
AAAA_Resource_RecordType = 28,
SRV_Resource_RecordType = 33
};
/* Operation Code */
enum {
QUERY_OperationCode = 0, /* standard query */
IQUERY_OperationCode = 1, /* inverse query */
STATUS_OperationCode = 2, /* server status request */
NOTIFY_OperationCode = 4, /* request zone transfer */
UPDATE_OperationCode = 5 /* change resource records */
};
/* Response Code */
enum {
NoError_ResponseCode = 0,
FormatError_ResponseCode = 1,
ServerFailure_ResponseCode = 2,
NameError_ResponseCode = 3
};
/* Query Type */
enum {
IXFR_QueryType = 251,
AXFR_QueryType = 252,
MAILB_QueryType = 253,
MAILA_QueryType = 254,
STAR_QueryType = 255
};
/*
* Types.
*/
/* Question Section */
struct Question {
char *qName;
uint16_t qType;
uint16_t qClass;
struct Question* next; // for linked list
};
/* Data part of a Resource Record */
union ResourceData {
struct {
char *txt_data;
} txt_record;
struct {
uint8_t addr[4];
} a_record;
struct {
char* MName;
char* RName;
uint32_t serial;
uint32_t refresh;
uint32_t retry;
uint32_t expire;
uint32_t minimum;
} soa_record;
struct {
char *name;
} name_server_record;
struct {
char name;
} cname_record;
struct {
char *name;
} ptr_record;
struct {
uint16_t preference;
char *exchange;
} mx_record;
struct {
uint8_t addr[16];
} aaaa_record;
struct {
uint16_t priority;
uint16_t weight;
uint16_t port;
char *target;
} srv_record;
};
/* Resource Record Section */
struct ResourceRecord {
char *name;
uint16_t type;
uint16_t class;
uint16_t ttl;
uint16_t rd_length;
union ResourceData rd_data;
struct ResourceRecord* next; // for linked list
};
struct Message {
uint16_t id; /* Identifier */
/* Flags */
uint16_t qr; /* Query/Response Flag */
uint16_t opcode; /* Operation Code */
uint16_t aa; /* Authoritative Answer Flag */
uint16_t tc; /* Truncation Flag */
uint16_t rd; /* Recursion Desired */
uint16_t ra; /* Recursion Available */
uint16_t rcode; /* Response Code */
uint16_t qdCount; /* Question Count */
uint16_t anCount; /* Answer Record Count */
uint16_t nsCount; /* Authority Record Count */
uint16_t arCount; /* Additional Record Count */
/* At least one question; questions are copied to the response 1:1 */
struct Question* questions;
/*
* Resource records to be send back.
* Every resource record can be in any of the following places.
* But every place has a different semantic.
*/
struct ResourceRecord* answers;
struct ResourceRecord* authorities;
struct ResourceRecord* additionals;
};
int get_A_Record(uint8_t addr[4], const char domain_name[])
{
if (strcmp("foo.bar.com", domain_name) == 0)
{
addr[0] = 192;
addr[1] = 168;
addr[2] = 1;
addr[3] = 1;
return 0;
}
else
{
return -1;
}
}
int get_AAAA_Record(uint8_t addr[16], const char domain_name[])
{
if (strcmp("foo.bar.com", domain_name) == 0)
{
addr[0] = 0xfe;
addr[1] = 0x80;
addr[2] = 0x00;
addr[3] = 0x00;
addr[4] = 0x00;
addr[5] = 0x00;
addr[6] = 0x00;
addr[7] = 0x00;
addr[8] = 0x00;
addr[9] = 0x00;
addr[10] = 0x00;
addr[11] = 0x00;
addr[12] = 0x00;
addr[13] = 0x00;
addr[14] = 0x00;
addr[15] = 0x01;
return 0;
}
else
{
return -1;
}
}
/*
* Debugging functions.
*/
void print_hex(uint8_t* buf, size_t len)
{
int i;
printf("%zu bytes:\n", len);
for(i = 0; i < len; ++i)
printf("%02x ", buf[i]);
printf("\n");
}
void print_resource_record(struct ResourceRecord* rr)
{
int i;
while (rr)
{
printf(" ResourceRecord { name '%s', type %u, class %u, ttl %u, rd_length %u, ",
rr->name,
rr->type,
rr->class,
rr->ttl,
rr->rd_length
);
union ResourceData *rd = &rr->rd_data;
switch (rr->type)
{
case A_Resource_RecordType:
printf("Address Resource Record { address ");
for(i = 0; i < 4; ++i)
printf("%s%u", (i ? "." : ""), rd->a_record.addr[i]);
printf(" }");
break;
case NS_Resource_RecordType:
printf("Name Server Resource Record { name %s }",
rd->name_server_record.name
);
break;
case CNAME_Resource_RecordType:
printf("Canonical Name Resource Record { name %u }",
rd->cname_record.name
);
break;
case SOA_Resource_RecordType:
printf("SOA { MName '%s', RName '%s', serial %u, refresh %u, retry %u, expire %u, minimum %u }",
rd->soa_record.MName,
rd->soa_record.RName,
rd->soa_record.serial,
rd->soa_record.refresh,
rd->soa_record.retry,
rd->soa_record.expire,
rd->soa_record.minimum
);
break;
case PTR_Resource_RecordType:
printf("Pointer Resource Record { name '%s' }",
rd->ptr_record.name
);
break;
case MX_Resource_RecordType:
printf("Mail Exchange Record { preference %u, exchange '%s' }",
rd->mx_record.preference,
rd->mx_record.exchange
);
break;
case TXT_Resource_RecordType:
printf("Text Resource Record { txt_data '%s' }",
rd->txt_record.txt_data
);
break;
case AAAA_Resource_RecordType:
printf("AAAA Resource Record { address ");
for(i = 0; i < 16; ++i)
printf("%s%02x", (i ? ":" : ""), rd->aaaa_record.addr[i]);
printf(" }");
break;
default:
printf("Unknown Resource Record { ??? }");
}
printf("}\n");
rr = rr->next;
}
}
void print_query(struct Message* msg)
{
printf("QUERY { ID: %02x", msg->id);
printf(". FIELDS: [ QR: %u, OpCode: %u ]", msg->qr, msg->opcode);
printf(", QDcount: %u", msg->qdCount);
printf(", ANcount: %u", msg->anCount);
printf(", NScount: %u", msg->nsCount);
printf(", ARcount: %u,\n", msg->arCount);
struct Question* q = msg->questions;
while (q)
{
printf(" Question { qName '%s', qType %u, qClass %u }\n",
q->qName,
q->qType,
q->qClass
);
q = q->next;
}
print_resource_record(msg->answers);
print_resource_record(msg->authorities);
print_resource_record(msg->additionals);
printf("}\n");
}
/*
* Basic memory operations.
*/
size_t get16bits(const uint8_t** buffer)
{
uint16_t value;
memcpy(&value, *buffer, 2);
*buffer += 2;
return ntohs(value);
}
void put8bits(uint8_t** buffer, uint8_t value)
{
memcpy(*buffer, &value, 1);
*buffer += 1;
}
void put16bits(uint8_t** buffer, uint16_t value)
{
value = htons(value);
memcpy(*buffer, &value, 2);
*buffer += 2;
}
void put32bits(uint8_t** buffer, uint32_t value)
{
value = htons(value);
memcpy(*buffer, &value, 4);
*buffer += 4;
}
/*
* Deconding/Encoding functions.
*/
// 3foo3bar3com0 => foo.bar.com
char* decode_domain_name(const uint8_t** buffer)
{
char name[256];
const uint8_t* buf = *buffer;
int j = 0;
int i = 0;
while (buf[i] != 0)
{
//if (i >= buflen || i > sizeof(name))
// return NULL;
if (i != 0)
{
name[j] = '.';
j += 1;
}
int len = buf[i];
i += 1;
memcpy(name+j, buf+i, len);
i += len;
j += len;
}
name[j] = '\0';
*buffer += i + 1; //also jump over the last 0
return strdup(name);
}
// foo.bar.com => 3foo3bar3com0
void encode_domain_name(uint8_t** buffer, const char* domain)
{
uint8_t* buf = *buffer;
const char* beg = domain;
const char* pos;
int len = 0;
int i = 0;
while ((pos = strchr(beg, '.')))
{
len = pos - beg;
buf[i] = len;
i += 1;
memcpy(buf+i, beg, len);
i += len;
beg = pos + 1;
}
len = strlen(domain) - (beg - domain);
buf[i] = len;
i += 1;
memcpy(buf + i, beg, len);
i += len;
buf[i] = 0;
i += 1;
*buffer += i;
}
void decode_header(struct Message* msg, const uint8_t** buffer)
{
msg->id = get16bits(buffer);
uint32_t fields = get16bits(buffer);
msg->qr = (fields & QR_MASK) >> 15;
msg->opcode = (fields & OPCODE_MASK) >> 11;
msg->aa = (fields & AA_MASK) >> 10;
msg->tc = (fields & TC_MASK) >> 9;
msg->rd = (fields & RD_MASK) >> 8;
msg->ra = (fields & RA_MASK) >> 7;
msg->rcode = (fields & RCODE_MASK) >> 0;
msg->qdCount = get16bits(buffer);
msg->anCount = get16bits(buffer);
msg->nsCount = get16bits(buffer);
msg->arCount = get16bits(buffer);
}
void encode_header(struct Message* msg, uint8_t** buffer)
{
put16bits(buffer, msg->id);
int fields = 0;
fields |= (msg->qr << 15) & QR_MASK;
fields |= (msg->rcode << 0) & RCODE_MASK;
// TODO: insert the rest of the fields
put16bits(buffer, fields);
put16bits(buffer, msg->qdCount);
put16bits(buffer, msg->anCount);
put16bits(buffer, msg->nsCount);
put16bits(buffer, msg->arCount);
}
int decode_msg(struct Message* msg, const uint8_t* buffer, int size)
{
int i;
decode_header(msg, &buffer);
if (msg->anCount != 0 || msg->nsCount != 0)
{
printf("Only questions expected!\n");
return -1;
}
// parse questions
uint32_t qcount = msg->qdCount;
struct Question* qs = msg->questions;
for (i = 0; i < qcount; ++i)
{
struct Question* q = malloc(sizeof(struct Question));
q->qName = decode_domain_name(&buffer);
q->qType = get16bits(&buffer);
q->qClass = get16bits(&buffer);
// prepend question to questions list
q->next = qs;
msg->questions = q;
}
// We do not expect any resource records to parse here.
return 0;
}
// For every question in the message add a appropiate resource record
// in either section 'answers', 'authorities' or 'additionals'.
void resolver_process(struct Message* msg)
{
struct ResourceRecord* beg;
struct ResourceRecord* rr;
struct Question* q;
int rc;
// leave most values intact for response
msg->qr = 1; // this is a response
msg->aa = 1; // this server is authoritative
msg->ra = 0; // no recursion available
msg->rcode = Ok_ResponseType;
// should already be 0
msg->anCount = 0;
msg->nsCount = 0;
msg->arCount = 0;
// for every question append resource records
q = msg->questions;
while (q)
{
rr = malloc(sizeof(struct ResourceRecord));
memset(rr, 0, sizeof(struct ResourceRecord));
rr->name = strdup(q->qName);
rr->type = q->qType;
rr->class = q->qClass;
rr->ttl = 60*60; // in seconds; 0 means no caching
printf("Query for '%s'\n", q->qName);
// We only can only answer two question types so far
// and the answer (resource records) will be all put
// into the answers list.
// This behavior is probably non-standard!
switch (q->qType)
{
case A_Resource_RecordType:
rr->rd_length = 4;
rc = get_A_Record(rr->rd_data.a_record.addr, q->qName);
if (rc < 0)
{
free(rr->name);
free(rr);
goto next;
}
break;
case AAAA_Resource_RecordType:
rr->rd_length = 16;
rc = get_AAAA_Record(rr->rd_data.aaaa_record.addr, q->qName);
if (rc < 0)
{
free(rr->name);
free(rr);
goto next;
}
break;
/*
case NS_Resource_RecordType:
case CNAME_Resource_RecordType:
case SOA_Resource_RecordType:
case PTR_Resource_RecordType:
case MX_Resource_RecordType:
case TXT_Resource_RecordType:
*/
default:
free(rr);
msg->rcode = NotImplemented_ResponseType;
printf("Cannot answer question of type %d.\n", q->qType);
goto next;
}
msg->anCount++;
// prepend resource record to answers list
beg = msg->answers;
msg->answers = rr;
rr->next = beg;
// jump here to omit question
next:
// process next question
q = q->next;
}
}
/* @return 0 upon failure, 1 upon success */
int encode_resource_records(struct ResourceRecord* rr, uint8_t** buffer)
{
int i;
while (rr)
{
// Answer questions by attaching resource sections.
encode_domain_name(buffer, rr->name);
put16bits(buffer, rr->type);
put16bits(buffer, rr->class);
put32bits(buffer, rr->ttl);
put16bits(buffer, rr->rd_length);
switch (rr->type)
{
case A_Resource_RecordType:
for(i = 0; i < 4; ++i)
put8bits(buffer, rr->rd_data.a_record.addr[i]);
break;
case AAAA_Resource_RecordType:
for(i = 0; i < 16; ++i)
put8bits(buffer, rr->rd_data.aaaa_record.addr[i]);
break;
default:
fprintf(stderr, "Unknown type %u. => Ignore resource record.\n", rr->type);
return 1;
}
rr = rr->next;
}
return 0;
}
/* @return 0 upon failure, 1 upon success */
int encode_msg(struct Message* msg, uint8_t** buffer)
{
struct Question* q;
int rc;
encode_header(msg, buffer);
q = msg->questions;
while (q)
{
encode_domain_name(buffer, q->qName);
put16bits(buffer, q->qType);
put16bits(buffer, q->qClass);
q = q->next;
}
rc = 0;
rc |= encode_resource_records(msg->answers, buffer);
rc |= encode_resource_records(msg->authorities, buffer);
rc |= encode_resource_records(msg->additionals, buffer);
return rc;
}
void free_resource_records(struct ResourceRecord* rr)
{
struct ResourceRecord* next;
while (rr) {
free(rr->name);
next = rr->next;
free(rr);
rr = next;
}
}
void free_questions(struct Question* qq)
{
struct Question* next;
while (qq) {
free(qq->qName);
next = qq->next;
free(qq);
qq = next;
}
}
int main()
{
// buffer for input/output binary packet
uint8_t buffer[BUF_SIZE];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in addr;
int nbytes, rc;
int sock;
int port = 9000;
struct Message msg;
memset(&msg, 0, sizeof(struct Message));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
sock = socket(AF_INET, SOCK_DGRAM, 0);
rc = bind(sock, (struct sockaddr*) &addr, addr_len);
if (rc != 0)
{
printf("Could not bind: %s\n", strerror(errno));
return 1;
}
printf("Listening on port %u.\n", port);
while (1)
{
free_questions(msg.questions);
free_resource_records(msg.answers);
free_resource_records(msg.authorities);
free_resource_records(msg.additionals);
memset(&msg, 0, sizeof(struct Message));
nbytes = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &addr_len);
if (decode_msg(&msg, buffer, nbytes) != 0) {
continue;
}
/* Print query */
print_query(&msg);
resolver_process(&msg);
/* Print response */
print_query(&msg);
uint8_t *p = buffer;
if (encode_msg(&msg, &p) != 0) {
continue;
}
int buflen = p - buffer;
sendto(sock, buffer, buflen, 0, (struct sockaddr*) &client_addr, addr_len);
}
}