initial commit
This commit is contained in:
9
.clang-format
Executable file
9
.clang-format
Executable 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
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.vscode
|
||||
.o
|
||||
.DS_Store
|
||||
.swp.
|
||||
15
Makefile
Executable file
15
Makefile
Executable 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
19
ReadMe.md
Executable 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
750
fast_ping.c
Executable 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
31
fast_ping.h
Executable 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
133
include/atomic.h
Executable 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
132
include/bitmap.h
Normal 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
65
include/bitops.h
Normal 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
201
include/hash.h
Normal 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
153
include/hashtable.h
Normal 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
184
include/jhash.h
Normal 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
792
include/list.h
Normal 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
189
smartdns.c
Executable 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
755
test-dns.c
Executable 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user