diff --git a/include/nat64/common/config.h b/include/nat64/common/config.h index 194a5b6dd..cbab5c1c8 100644 --- a/include/nat64/common/config.h +++ b/include/nat64/common/config.h @@ -57,6 +57,8 @@ enum config_mode { MODE_SESSION = (1 << 4), /** The current message is talking about log times for benchmark. */ MODE_LOGTIME = (1 << 5), + /** The user requested performance status. */ + MODE_TIMESTAMPS = (1 << 12), /** The current message is talking about the JSON configuration file */ MODE_PARSE_FILE = (1 << 9), /** The current message is talking about synchronization entries.*/ @@ -85,6 +87,7 @@ char *configmode_to_string(enum config_mode mode); #define SESSION_OPS (OP_DISPLAY | OP_COUNT) #define JOOLD_OPS (OP_ADVERTISE | OP_TEST) #define LOGTIME_OPS (OP_DISPLAY) +#define TIMESTAMPS_OPS (OP_DISPLAY) #define INSTANCE_OPS (OP_ADD | OP_REMOVE) /** * @} @@ -151,10 +154,11 @@ enum parse_section { #define UPDATE_MODES (MODE_GLOBAL | MODE_POOL4 | MODE_PARSE_FILE) #define SIIT_MODES (MODE_GLOBAL | MODE_POOL6 | MODE_BLACKLIST | MODE_RFC6791 \ - | MODE_EAMT | MODE_LOGTIME | MODE_PARSE_FILE | MODE_INSTANCE) + | MODE_EAMT | MODE_LOGTIME | MODE_PARSE_FILE | MODE_INSTANCE \ + | MODE_TIMESTAMPS) #define NAT64_MODES (MODE_GLOBAL | MODE_POOL6 | MODE_POOL4 | MODE_BIB \ | MODE_SESSION | MODE_LOGTIME | MODE_PARSE_FILE \ - | MODE_INSTANCE | MODE_JOOLD) + | MODE_INSTANCE | MODE_JOOLD | MODE_TIMESTAMPS) /** * @} */ @@ -467,6 +471,54 @@ struct logtime_entry_usr { #endif +typedef enum timestamp_type { + TST64_FULL_TRANSLATION, + TST_PKT6_INIT, + + TST46_FULL_TRANSLATION, + TST_PKT4_INIT, + + TST_DIT, /* determine incoming tuple */ + TST_FAU, /* filtering and updating */ + TST_COT, /* compute outgoing tuple */ + TST_TTP, /* translating the packet */ + TST_HH, /* handling hairpinning */ + TST_SP, /* send packet */ + + TST_FAU64_VALIDATIONS, + TST_FAU46_VALIDATIONS, + TST_FAU64_MDS, /* Mask domain search */ + + TST64_SESSION_GENERIC_OLD, + TST64_SESSION_GENERIC_NEW, + TST46_SESSION_GENERIC, + TST64_SESSION_TCP_OLD, + TST64_SESSION_TCP_NEW, + TST46_SESSION_TCP, + TST_SESSION_OLD, + TST_SESSION_NEW, + TST_SESSION_TIMER, + TST_SESSION_PROBE, + TST_SESSION_MASK, + + TST_ICMP6, + TST_ICMP4, + + TST_LENGTH, +} timestamp_type; + +struct timestamps_entry_usr { + __u32 success_count; + __u32 success_min; + __u32 success_avg; + __u32 success_max; + + __u32 failure_count; + __u32 failure_min; + __u32 failure_avg; + __u32 failure_max; +}; + /** * A BIB entry, from the eyes of userspace. * diff --git a/include/nat64/common/xlat.h b/include/nat64/common/xlat.h index a80c39fc0..d1c6e299d 100644 --- a/include/nat64/common/xlat.h +++ b/include/nat64/common/xlat.h @@ -13,7 +13,7 @@ #define JOOL_VERSION_MAJOR 3 #define JOOL_VERSION_MINOR 5 #define JOOL_VERSION_REV 5 -#define JOOL_VERSION_DEV 0 +#define JOOL_VERSION_DEV 1 /** See http://stackoverflow.com/questions/195975 */ #define STR_VALUE(arg) #arg diff --git a/include/nat64/mod/common/nl/timestamp.h b/include/nat64/mod/common/nl/timestamp.h new file mode 100644 index 000000000..d6a226eb5 --- /dev/null +++ b/include/nat64/mod/common/nl/timestamp.h @@ -0,0 +1,8 @@ +#ifndef MOD_COMMON_NL_TIMESTAMP_H_ +#define MOD_COMMON_NL_TIMESTAMP_H_ + +#include + +int handle_timestamp(struct genl_info *info); + +#endif /* MOD_COMMON_NL_TIMESTAMPS_H_ */ diff --git a/include/nat64/mod/common/timestamp.h b/include/nat64/mod/common/timestamp.h new file mode 100644 index 000000000..c37cf0871 --- /dev/null +++ b/include/nat64/mod/common/timestamp.h @@ -0,0 +1,51 @@ +#ifndef INCLUDE_NAT64_MOD_COMMON_TIMESTAMP_H_ +#define INCLUDE_NAT64_MOD_COMMON_TIMESTAMP_H_ + +#include "nat64/common/config.h" + +/* + * Ehh... this should be private. Feel free to ignore it if you're reading the + * API... --U + */ +#define TS_BATCH_COUNT 1 + +#if defined(TIMESTAMP_JIFFIES) + +typedef unsigned long timestamp; +#define TIMESTAMP_DECLARE_START(name) timestamp name = jiffies +#define TIMESTAMP_DECLARE(name) timestamp name +#define TIMESTAMP_START(name) name = jiffies +#define TIMESTAMP_STOP(a, b, c) timestamp_stop(a, b, c) + +#elif defined(TIMESTAMP_TIMESPEC) + +#include +#include +#include +#include +typedef struct timespec64 timestamp; +#define TIMESTAMP_DECLARE_START(name) timestamp name; getnstimeofday64(&name) +#define TIMESTAMP_DECLARE(name) timestamp name +#define TIMESTAMP_START(name) getnstimeofday64(&name) +#define TIMESTAMP_STOP(a, b, c) timestamp_stop(a, b, c) + +#else + +#define timestamp int /* Whatevs */ +#define TIMESTAMP_DECLARE_START(name) /* Empty */ +#define TIMESTAMP_DECLARE(name) /* Empty */ +#define TIMESTAMP_START(name) /* Empty */ +#define TIMESTAMP_STOP(a, b, c) /* Empty */ + +#endif + +void timestamp_stop(timestamp ts, timestamp_type type, bool success); + +struct timestamp_foreach_func { + int (*cb)(struct timestamps_entry_usr *, void *); + void *arg; +}; + +int timestamp_foreach(struct timestamp_foreach_func *func, void *args); + +#endif /* INCLUDE_NAT64_MOD_COMMON_TIMESTAMP_H_ */ diff --git a/include/nat64/mod/stateful/bib/db.h b/include/nat64/mod/stateful/bib/db.h index 4d26d5c2c..59066017d 100644 --- a/include/nat64/mod/stateful/bib/db.h +++ b/include/nat64/mod/stateful/bib/db.h @@ -58,7 +58,7 @@ enum session_fate { int bib_init(void); void bib_destroy(void); -struct bib *bib_create(void); +struct bib *bib_create(struct net *ns); void bib_get(struct bib *db); void bib_put(struct bib *db); @@ -96,7 +96,6 @@ int bib_find(struct bib *db, struct tuple *tuple, struct bib_session *result); int bib_add_session(struct bib *db, struct session_entry *new, struct collision_cb *cb); -void bib_clean(struct bib *db, struct net *ns); /* These are used by userspace request handling. */ diff --git a/include/nat64/mod/stateful/bib/pkt_queue.h b/include/nat64/mod/stateful/bib/pkt_queue.h index eed671562..a45d00610 100644 --- a/include/nat64/mod/stateful/bib/pkt_queue.h +++ b/include/nat64/mod/stateful/bib/pkt_queue.h @@ -146,7 +146,8 @@ void pktqueue_put_node(struct pktqueue_session *node); * outside. */ unsigned int pktqueue_prepare_clean(struct pktqueue *queue, - struct list_head *probes); + struct list_head *probes, u64 *max_session_rm, u64 *sessions_rm, + bool *pending_rm); /** * Sends the ICMP errors contained in the @probe list. */ diff --git a/include/nat64/mod/stateful/global_timer.h b/include/nat64/mod/stateful/global_timer.h new file mode 100644 index 000000000..7f1ead025 --- /dev/null +++ b/include/nat64/mod/stateful/global_timer.h @@ -0,0 +1,13 @@ +#ifndef _JOOL_MOD_GLOBAL_TIMER_H +#define _JOOL_MOD_GLOBAL_TIMER_H + +/** + * @file + * Timer used to trigger some of Jool's events. Always runs, as long as Jool + * is modprobed. At time of writing, this induces fragment expiration. + */ + +int global_timer_init(void); +void global_timer_destroy(void); + +#endif /* _JOOL_MOD_GLOBAL_TIMER_H */ diff --git a/include/nat64/mod/stateful/timer.h b/include/nat64/mod/stateful/timer.h deleted file mode 100644 index 7047a9143..000000000 --- a/include/nat64/mod/stateful/timer.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _JOOL_MOD_TIMER_H -#define _JOOL_MOD_TIMER_H - -/** - * @file - * An all-purpose timer used to trigger some of Jool's events. Always runs, as - * long as Jool is modprobed. At time of writing, this induces session and - * fragment expiration. - * - * Why don't the session and fragment code manage their own timers? - * Because that's more code and I don't see how it would improve anything. - */ - -int timer_init(void); -void timer_destroy(void); - -#endif /* _JOOL_MOD_TIMER_H */ diff --git a/include/nat64/usr/argp/options.h b/include/nat64/usr/argp/options.h index 2fc6ed122..dec9542df 100644 --- a/include/nat64/usr/argp/options.h +++ b/include/nat64/usr/argp/options.h @@ -27,6 +27,7 @@ enum argp_flags { ARGP_BLACKLIST = 7000, ARGP_RFC6791 = 6791, ARGP_LOGTIME = 'l', + ARGP_TIMESTAMPS = 7003, ARGP_GLOBAL = 'g', ARGP_PARSE_FILE = 'p', ARGP_INSTANCE = 7001, diff --git a/include/nat64/usr/timestamp.h b/include/nat64/usr/timestamp.h new file mode 100644 index 000000000..20aed60b9 --- /dev/null +++ b/include/nat64/usr/timestamp.h @@ -0,0 +1,6 @@ +#ifndef INCLUDE_NAT64_USR_TIMESTAMP_H_ +#define INCLUDE_NAT64_USR_TIMESTAMP_H_ + +int timestamp_display(void); + +#endif /* INCLUDE_NAT64_USR_TIMESTAMP_H_ */ diff --git a/mod/common/core.c b/mod/common/core.c index 113a95224..4841cadfa 100644 --- a/mod/common/core.c +++ b/mod/common/core.c @@ -3,6 +3,7 @@ #include "nat64/mod/common/config.h" #include "nat64/mod/common/handling_hairpinning.h" #include "nat64/mod/common/xlator.h" +#include "nat64/mod/common/timestamp.h" #include "nat64/mod/common/translation_state.h" #include "nat64/mod/common/rfc6145/core.h" #include "nat64/mod/stateful/compute_outgoing_tuple.h" @@ -14,31 +15,40 @@ #include #include +#define EXECUTE(action, tst) ({\ + TIMESTAMP_START(timer); \ + result = action; \ + TIMESTAMP_STOP(timer, tst, result == VERDICT_CONTINUE); \ + result; \ + }) -static verdict core_common(struct xlation *state) + +static verdict core_common(struct xlation *state, bool *success) { verdict result; + TIMESTAMP_DECLARE(timer); if (xlat_is_nat64()) { - result = determine_in_tuple(state); + result = EXECUTE(determine_in_tuple(state), TST_DIT); if (result != VERDICT_CONTINUE) goto end; - result = filtering_and_updating(state); + result = EXECUTE(filtering_and_updating(state), TST_FAU); if (result != VERDICT_CONTINUE) goto end; - result = compute_out_tuple(state); + result = EXECUTE(compute_out_tuple(state), TST_COT); if (result != VERDICT_CONTINUE) goto end; } - result = translating_the_packet(state); + + result = EXECUTE(translating_the_packet(state), TST_TTP); if (result != VERDICT_CONTINUE) goto end; if (is_hairpin(state)) { - result = handling_hairpinning(state); + result = EXECUTE(handling_hairpinning(state), TST_HH); kfree_skb(state->out.skb); /* Put this inside of hh()? */ } else { - result = sendpkt_send(state); + result = EXECUTE(sendpkt_send(state), TST_SP); /* sendpkt_send() releases out's skb regardless of verdict. */ } @@ -56,6 +66,7 @@ static verdict core_common(struct xlation *state) */ kfree_skb(state->in.skb); result = VERDICT_STOLEN; + *success = true; /* Fall through. */ end: @@ -64,11 +75,24 @@ static verdict core_common(struct xlation *state) return result; } +static int init_pkt4(struct packet *pkt, struct sk_buff *skb) +{ + int error; + TIMESTAMP_DECLARE_START(timer); + + error = pkt_init_ipv4(pkt, skb); + + TIMESTAMP_STOP(timer, TST_PKT4_INIT, !error); + return error; +} + unsigned int core_4to6(struct sk_buff *skb, const struct net_device *dev) { struct xlation state; struct iphdr *hdr = ip_hdr(skb); + bool success = false; verdict result; + TIMESTAMP_DECLARE_START(timer); xlation_init(&state); @@ -83,21 +107,36 @@ unsigned int core_4to6(struct sk_buff *skb, const struct net_device *dev) log_debug("Catching IPv4 packet: %pI4->%pI4", &hdr->saddr, &hdr->daddr); /* Reminder: This function might change pointers. */ - if (pkt_init_ipv4(&state.in, skb) != 0) { + if (init_pkt4(&state.in, skb)) { xlation_put(&state); return NF_DROP; } - result = core_common(&state); + result = core_common(&state, &success); + xlation_put(&state); + TIMESTAMP_STOP(timer, TST46_FULL_TRANSLATION, success); return result; } +static int init_pkt6(struct packet *pkt, struct sk_buff *skb) +{ + int error; + TIMESTAMP_DECLARE_START(timer); + + error = pkt_init_ipv6(pkt, skb); + + TIMESTAMP_STOP(timer, TST_PKT6_INIT, !error); + return error; +} + unsigned int core_6to4(struct sk_buff *skb, const struct net_device *dev) { struct xlation state; struct ipv6hdr *hdr = ipv6_hdr(skb); + bool success = false; verdict result; + TIMESTAMP_DECLARE_START(timer); xlation_init(&state); @@ -115,7 +154,7 @@ unsigned int core_6to4(struct sk_buff *skb, const struct net_device *dev) &hdr->daddr); /* Reminder: This function might change pointers. */ - if (pkt_init_ipv6(&state.in, skb) != 0) { + if (init_pkt6(&state.in, skb)) { xlation_put(&state); return NF_DROP; } @@ -124,13 +163,15 @@ unsigned int core_6to4(struct sk_buff *skb, const struct net_device *dev) if (xlat_is_nat64()) { result = fragdb_handle(state.jool.nat64.frag, &state.in); - if (result != VERDICT_CONTINUE) - goto end; + if (result != VERDICT_CONTINUE) { + xlation_put(&state); + return result; + } } - result = core_common(&state); - /* Fall through. */ -end: + result = core_common(&state, &success); + xlation_put(&state); + TIMESTAMP_STOP(timer, TST64_FULL_TRANSLATION, success); return result; } diff --git a/mod/common/icmp_wrapper.c b/mod/common/icmp_wrapper.c index ac588f4df..8899c9b19 100644 --- a/mod/common/icmp_wrapper.c +++ b/mod/common/icmp_wrapper.c @@ -5,6 +5,7 @@ #include #include "nat64/common/types.h" #include "nat64/mod/common/route.h" +#include "nat64/mod/common/timestamp.h" static char *icmp_error_to_string(icmp_error_code error) { @@ -36,6 +37,7 @@ static void icmp64_send4(struct sk_buff *skb, icmp_error_code error, __u32 info) { int type, code; int err; + TIMESTAMP_DECLARE_START(timer); /* * I don't know why the kernel needs this nonsense, @@ -44,6 +46,7 @@ static void icmp64_send4(struct sk_buff *skb, icmp_error_code error, __u32 info) err = route4_input(skb); if (err) { log_debug("Can't send an ICMPv4 Error: %d", err); + TIMESTAMP_STOP(timer, TST_ICMP4, false); return; } @@ -77,17 +80,20 @@ static void icmp64_send4(struct sk_buff *skb, icmp_error_code error, __u32 info) code = ICMP_SR_FAILED; break; default: + TIMESTAMP_STOP(timer, TST_ICMP4, false); return; /* Not supported or needed. */ } log_debug("Sending ICMPv4 error: %s, type: %d, code: %d.", icmp_error_to_string(error), type, code); icmp_send(skb, type, code, cpu_to_be32(info)); + TIMESTAMP_STOP(timer, TST_ICMP4, true); } static void icmp64_send6(struct sk_buff *skb, icmp_error_code error, __u32 info) { int type, code; + TIMESTAMP_DECLARE_START(timer); switch (error) { case ICMPERR_ADDR_UNREACHABLE: @@ -117,6 +123,7 @@ static void icmp64_send6(struct sk_buff *skb, icmp_error_code error, __u32 info) code = 0; /* No code. */ break; default: + TIMESTAMP_STOP(timer, TST_ICMP6, false); return; /* Not supported or needed. */ } @@ -133,6 +140,7 @@ static void icmp64_send6(struct sk_buff *skb, icmp_error_code error, __u32 info) #else #warning "You're compiling in kernel 3.12. See https://github.com/NICMx/Jool/issues/90" #endif + TIMESTAMP_STOP(timer, TST_ICMP6, true); } void icmp64_send(struct packet *pkt, icmp_error_code error, __u32 info) diff --git a/mod/common/nl/nl_handler2.c b/mod/common/nl/nl_handler2.c index ebb32790a..4d76781ce 100644 --- a/mod/common/nl/nl_handler2.c +++ b/mod/common/nl/nl_handler2.c @@ -3,6 +3,7 @@ #include #include #include + #include "nat64/common/types.h" #include "nat64/mod/common/config.h" #include "nat64/mod/common/linux_version.h" @@ -20,6 +21,7 @@ #include "nat64/mod/common/nl/pool4.h" #include "nat64/mod/common/nl/pool6.h" #include "nat64/mod/common/nl/session.h" +#include "nat64/mod/common/nl/timestamp.h" static struct genl_multicast_group mc_groups[1] = { { @@ -110,6 +112,8 @@ static int multiplex_request(struct xlator *jool, struct genl_info *info) return handle_blacklist_config(jool, info); case MODE_LOGTIME: return handle_logtime_config(info); + case MODE_TIMESTAMPS: + return handle_timestamp(info); case MODE_GLOBAL: return handle_global_config(jool, info); case MODE_PARSE_FILE: diff --git a/mod/common/nl/timestamp.c b/mod/common/nl/timestamp.c new file mode 100644 index 000000000..48f6e32a3 --- /dev/null +++ b/mod/common/nl/timestamp.c @@ -0,0 +1,63 @@ +#include "nat64/mod/common/nl/timestamp.h" + +#include "nat64/common/config.h" +#include "nat64/mod/common/timestamp.h" +#include "nat64/mod/common/nl/nl_common.h" +#include "nat64/mod/common/nl/nl_core2.h" + +static int timestamp_to_userspace(struct timestamps_entry_usr *entry, void *arg) +{ + return nlbuffer_write(arg, entry, sizeof(*entry)); +} + +static int handle_global_display(struct genl_info *info) +{ + struct nlcore_buffer buffer; + struct timestamp_foreach_func func = { + .cb = timestamp_to_userspace, + .arg = &buffer, + }; + size_t msg_size; + int error; + + if (verify_superpriv()) + return nlcore_respond(info, -EPERM); + + log_debug("Sending timestamps to userspace."); + + log_info("Note: Our maximum NL message size is %zu bytes.", + nlbuffer_response_max_size()); + log_info("Each timestamp group is %zu bytes long.", + TST_LENGTH * sizeof(struct timestamps_entry_usr)); + + msg_size = TS_BATCH_COUNT * TST_LENGTH * sizeof(struct timestamps_entry_usr); + if (msg_size > nlbuffer_response_max_size()) { + log_err("The timestamps do not fit in a netlink message. More programming is required."); + return nlcore_respond(info, -EINVAL); + } + + error = nlbuffer_init_response(&buffer, info, msg_size); + if (error) + return nlcore_respond(info, error); + + error = timestamp_foreach(&func, &buffer); + error = (error >= 0) + ? nlbuffer_send(info, &buffer) + : nlcore_respond(info, error); + + nlbuffer_free(&buffer); + return error; +} + +int handle_timestamp(struct genl_info *info) +{ + struct request_hdr *hdr = get_jool_hdr(info); + + switch (be16_to_cpu(hdr->operation)) { + case OP_DISPLAY: + return handle_global_display(info); + } + + log_err("Unknown operation: %u", be16_to_cpu(hdr->operation)); + return nlcore_respond(info, -EINVAL); +} diff --git a/mod/common/timestamp.c b/mod/common/timestamp.c new file mode 100644 index 000000000..abd00b882 --- /dev/null +++ b/mod/common/timestamp.c @@ -0,0 +1,291 @@ +#include "nat64/mod/common/timestamp.h" + +#include +#include + +/* + * Note: In this context, "wrap" means as in the sense of returning to the + * beginning of a circular data structure. + */ + +struct timestamp_stats { + timestamp min; + timestamp max; + + /* These two are intended for the computation of the average later. */ + timestamp total; + unsigned int count; + + bool initialized; +}; + +struct timestamp_stat_group { + struct timestamp_stats successes; + struct timestamp_stats failures; +}; + + +#define BATCH_PERIOD (1) /* In seconds. */ + +/** + * Keep in mind that this whole thing needs to fit in a single netlink message, + * because the copy to userspace doesn't currently bother with fragmenting it. + * + * Only BATCH_COUNT batches are kept in memory. Older batches get overriden, as + * the first dimension of this is meant to be a circular array. + * (This is fine because the userspace app is meant to request this information + * often enough.) + */ +static struct timestamp_stat_group stats[TS_BATCH_COUNT][TST_LENGTH]; +/** + * Batch counter. It is the absolute total number of batches we've processed. + * (Excluding the one we're currently at.) + * + * -1 stands for "we haven't even initialized this module". + */ +static int b = -1; +/** Jiffy at which the current batch's period started. */ +static unsigned long epoch; + +static DEFINE_SPINLOCK(lock); + + +static bool timestamps_enabled(void) +{ +#if defined(TIMESTAMP_JIFFIES) || defined(TIMESTAMP_TIMESPEC) + return true; +#else + return false; +#endif +} + +static bool need_new_batch(void) +{ + if (b == -1) + return true; + + return time_after(jiffies, + epoch + msecs_to_jiffies(BATCH_PERIOD * 1000)); +} + +static int wrap(int batch) +{ + /* + * I know this could be "& 3", but I might need to tweak BATCH_COUNT + * in the future. I'm hoping gcc will realize it can optimize this. + */ + return batch % TS_BATCH_COUNT; +} + +#if defined(TIMESTAMP_JIFFIES) + +/** + * Returns the time difference between now and beginning. + */ +static timestamp compute_delta(timestamp beginning) +{ + return jiffies - beginning; +} + +/* + * lhs < rhs: return <0 + * lhs == rhs: return 0 + * lhs > rhs: return >0 + */ +static int timestamp_compare(timestamp *lhs, timestamp *rhs) +{ + return (*lhs) - (*rhs); +} + +static timestamp timestamp_add(timestamp t1, timestamp t2) +{ + return t1 + t2; +} + +#elif defined(TIMESTAMP_TIMESPEC) + +static timestamp compute_delta(timestamp beginning) +{ + timestamp now; + getnstimeofday64(&now); + return timespec64_sub(now, beginning); +} + +static int timestamp_compare(timestamp *lhs, timestamp *rhs) +{ + return timespec_compare(lhs, rhs); +} + +static timestamp timestamp_add(timestamp lhs, timestamp rhs) +{ + return timespec_add(lhs, rhs); +} + +#else + +#define compute_delta(a) 0 +#define timestamp_compare(a, b) 0 +#define timestamp_add(a, b) 0 + +#endif + +/** + * The reason why the first argument is a struct and not a pointer is because + * I'm following along with the timespec's API's idiosyncrasies. It's weird. + */ +void timestamp_stop(timestamp beginning, timestamp_type type, bool success) +{ + struct timestamp_stats *stat; + timestamp delta; + int wb; + + if (WARN(!timestamps_enabled(), "Timestamps feature disabled but someone called a timestamps function.")) + return; + + delta = compute_delta(beginning); + wb = wrap(b); + + spin_lock_bh(&lock); + + if (need_new_batch()) { + b++; + wb = wrap(b); + memset(&stats[wb], 0, sizeof(stats[wb])); + epoch = jiffies; + } + + stat = success ? &stats[wb][type].successes : &stats[wb][type].failures; + + if (!stat->initialized) { + stat->min = delta; + stat->max = delta; + stat->total = delta; + stat->count = 1; + stat->initialized = true; + spin_unlock_bh(&lock); + return; + } + + if (timestamp_compare(&delta, &stat->min) < 0) + stat->min = delta; + else if (timestamp_compare(&delta, &stat->max) > 0) + stat->max = delta; + stat->total = timestamp_add(stat->total, delta); + stat->count++; + spin_unlock_bh(&lock); +} + +#if defined(TIMESTAMP_JIFFIES) + +/** + * Not sure if this name is self-explanatory. Think of "cap" as in like a Fire + * Emblem stat "cap". + */ +static __u32 cap_u32(unsigned int number) +{ + return (number > U32_MAX) ? U32_MAX : number; +} + +static __u32 tstou32(timestamp *ts) +{ + return cap_u32(jiffies_to_msecs(*ts)); +} + +static __u32 compute_avg(struct timestamp_stats *stat) +{ + if (stat->count == 0) + return 0; + return cap_u32(jiffies_to_msecs(stat->total / stat->count)); +} + +#elif defined(TIMESTAMP_TIMESPEC) + +static __u32 cap_u32(__u64 number) +{ + return (number > U32_MAX) ? U32_MAX : number; +} + +static __u64 get_total_microseconds(timestamp *ts) +{ + __u64 micros = 0; + + micros += ((__u64)1000000) * (__u64)ts->tv_sec; + micros += ts->tv_nsec / 1000; + + return micros; +} + +/** + * AFAIK, even though timespecs have a nanosecond field, the precision is far + * lower than that due to both hardware and software constraints. + * Also, we don't have all that much room in a __u32, so I'm going to convert + * them to microseconds. + */ +static __u32 tstou32(timestamp *ts) +{ + return cap_u32(get_total_microseconds(ts)); +} + +static __u32 compute_avg(struct timestamp_stats *stat) +{ + if (stat->count == 0) + return 0; + return cap_u32(get_total_microseconds(&stat->total) / stat->count); +} + +#else + +#define tstou32(a) 0 +#define compute_avg(a) 0 + +#endif + +int timestamp_foreach(struct timestamp_foreach_func *func, void *args) +{ + struct timestamp_stats *stat; + struct timestamps_entry_usr usr; + /* + * I literally have no clue what to call these variables. + * bb is a local batch counter (on top of the global batch counter, "b") + * and the latter is just the wrapped version of bb to prevent so many + * modulos. + */ + int bb, wbb; + unsigned int s; /* Stat group counter. */ + int result = 0; + + if (!timestamps_enabled()) { + log_err("This binary was not compiled to support timestamps."); + return -EINVAL; + } + + /* + * Rrrrg. This sucks pretty hard. It risks increasing the experiment's + * averages and max's. I dunno. Try not requesting the stats too often. + */ + spin_lock_bh(&lock); + + for (bb = b; bb > b - TS_BATCH_COUNT && bb >= 0; bb--) { + wbb = wrap(bb); + for (s = 0; s < TST_LENGTH; s++) { + stat = &stats[wbb][s].successes; + usr.success_count = stat->count; + usr.success_min = tstou32(&stat->min); + usr.success_avg = compute_avg(stat); + usr.success_max = tstou32(&stat->max); + stat = &stats[wbb][s].failures; + usr.failure_count = stat->count; + usr.failure_min = tstou32(&stat->min); + usr.failure_avg = compute_avg(stat); + usr.failure_max = tstou32(&stat->max); + + result = func->cb(&usr, args); + if (result) + goto end; + } + } + +end: + spin_unlock_bh(&lock); + return result; +} diff --git a/mod/common/xlator.c b/mod/common/xlator.c index 3006c86dc..520d98af3 100644 --- a/mod/common/xlator.c +++ b/mod/common/xlator.c @@ -201,7 +201,7 @@ static int init_nat64(struct xlator *jool) error = pool4db_init(&jool->nat64.pool4); if (error) goto pool4_fail; - jool->nat64.bib = bib_create(); + jool->nat64.bib = bib_create(jool->ns); if (!jool->nat64.bib) { error = -ENOMEM; goto bib_fail; diff --git a/mod/stateful/Kbuild b/mod/stateful/Kbuild index 7c6e97853..63e3b0f03 100644 --- a/mod/stateful/Kbuild +++ b/mod/stateful/Kbuild @@ -32,6 +32,7 @@ jool_common += ../common/send_packet.o jool_common += ../common/core.o jool_common += ../common/error_pool.o jool_common += ../common/wkmalloc.o +jool_common += ../common/timestamp.o jool_common += ../common/xlator.o jool_common += ../common/nl/atomic_config.o jool_common += ../common/nl/nl_handler2.o @@ -47,6 +48,7 @@ jool_common += ../common/nl/nl_common.o jool_common += ../common/nl/pool4.o jool_common += ../common/nl/pool6.o jool_common += ../common/nl/session.o +jool_common += ../common/nl/timestamp.o jool += pool4/empty.o jool += pool4/db.o @@ -56,7 +58,7 @@ jool += bib/db.o jool += bib/entry.o jool += bib/pkt_queue.o -jool += timer.o +jool += global_timer.o jool += fragment_db.o jool += determine_incoming_tuple.o jool += filtering_and_updating.o diff --git a/mod/stateful/bib/db.c b/mod/stateful/bib/db.c index ea554d50f..d420db24f 100644 --- a/mod/stateful/bib/db.c +++ b/mod/stateful/bib/db.c @@ -8,6 +8,7 @@ #include "nat64/mod/common/linux_version.h" #include "nat64/mod/common/rbtree.h" #include "nat64/mod/common/route.h" +#include "nat64/mod/common/timestamp.h" #include "nat64/mod/common/wkmalloc.h" #include "nat64/mod/stateful/bib/pkt_queue.h" @@ -105,6 +106,14 @@ struct expire_timer { fate_cb decide_fate_cb; }; +struct session_timer { + struct timer_list timer; + struct net *ns; + unsigned long run_period_jiff; + bool pend_rm; + u64 max_session_rm; +}; + struct bib_table { /** Indexes the entries using their IPv6 identifiers. */ struct rb_root tree6; @@ -164,6 +173,9 @@ struct bib_table { * This is NULL in UDP/ICMP. */ struct pktqueue *pkt_queue; + + /** Drops expired sessions */ + struct session_timer sess_timer; }; struct bib { @@ -185,6 +197,9 @@ static struct kmem_cache *session_cache; #define free_bib(bib) wkmem_cache_free("bib entry", bib_cache, bib) #define free_session(session) wkmem_cache_free("session", session_cache, session) +#define RM_SESSION_TIMER_INIT msecs_to_jiffies(2000) +#define RM_SESSION_MAX_INIT 1024 + static struct tabled_bib *bib6_entry(const struct rb_node *node) { return node ? rb_entry(node, struct tabled_bib, hook6) : NULL; @@ -290,6 +305,11 @@ static void kill_stored_pkt(struct bib_table *table, table->pkt_count--; } +static void destroy_session_timer(struct timer_list *timer) +{ + del_timer_sync(timer); +} + int bib_init(void) { bib_cache = kmem_cache_create("bib_nodes", @@ -331,61 +351,6 @@ static void init_expirer(struct expire_timer *expirer, expirer->decide_fate_cb = fate_cb; } -static void init_table(struct bib_table *table, - unsigned long est_timeout, - unsigned long trans_timeout, - fate_cb est_cb) -{ - table->tree6 = RB_ROOT; - table->tree4 = RB_ROOT; - table->log_bibs = DEFAULT_BIB_LOGGING; - table->log_sessions = DEFAULT_SESSION_LOGGING; - table->drop_by_addr = DEFAULT_ADDR_DEPENDENT_FILTERING; - table->bib_count = 0; - table->session_count = 0; - spin_lock_init(&table->lock); - init_expirer(&table->est_timer, est_timeout, SESSION_TIMER_EST, est_cb); - - init_expirer(&table->trans_timer, trans_timeout, SESSION_TIMER_TRANS, - just_die); - /* TODO "just_die"? what about the stored packet? */ - init_expirer(&table->syn4_timer, TCP_INCOMING_SYN, SESSION_TIMER_SYN4, - just_die); - table->pkt_count = 0; - table->pkt_limit = 0; - table->drop_v4_syn = DEFAULT_DROP_EXTERNAL_CONNECTIONS; - table->pkt_queue = NULL; -} - -struct bib *bib_create(void) -{ - struct bib *db; - - db = wkmalloc(struct bib, GFP_KERNEL); - if (!db) - return NULL; - - init_table(&db->udp, UDP_DEFAULT, 0, just_die); - init_table(&db->tcp, TCP_EST, TCP_TRANS, tcp_est_expire_cb); - init_table(&db->icmp, ICMP_DEFAULT, 0, just_die); - - db->tcp.pkt_limit = DEFAULT_MAX_STORED_PKTS; - db->tcp.pkt_queue = pktqueue_create(); - if (!db->tcp.pkt_queue) { - wkfree(struct bib, db); - return NULL; - } - /* - * Just in case some crazy psycho decides to change the default. - * THERE IS NO ADRESS-DEPENDENT FILTERING ON ICMP; the RFC is wrong. - */ - db->icmp.drop_by_addr = false; - - kref_init(&db->refs); - - return db; -} - void bib_get(struct bib *db) { kref_get(&db->refs); @@ -423,6 +388,10 @@ static void release_bib(struct kref *refs) struct bib *db; db = container_of(refs, struct bib, refs); + destroy_session_timer(&db->udp.sess_timer.timer); + destroy_session_timer(&db->tcp.sess_timer.timer); + destroy_session_timer(&db->icmp.sess_timer.timer); + /* * The trees share the entries, so only one tree of each protocol * needs to be emptied. @@ -747,6 +716,8 @@ static void send_probe_packet(struct net *ns, struct session_entry *session) unsigned int l3_hdr_len = sizeof(*iph); unsigned int l4_hdr_len = sizeof(*th); + TIMESTAMP_DECLARE_START(timer); + skb = alloc_skb(LL_MAX_HEADER + l3_hdr_len + l4_hdr_len, GFP_ATOMIC); if (!skb) { log_debug("Could now allocate a probe packet."); @@ -813,9 +784,11 @@ static void send_probe_packet(struct net *ns, struct session_entry *session) goto fail; } + TIMESTAMP_STOP(timer, TST_SESSION_PROBE, true); return; fail: + TIMESTAMP_STOP(timer, TST_SESSION_PROBE, false); log_debug("A TCP connection will probably break."); } @@ -1586,6 +1559,7 @@ int bib_add6(struct bib *db, struct slot_group slots; struct bib_delete_list rm_list = { NULL }; int error; + TIMESTAMP_DECLARE_START(timer); table = get_table(db, tuple6->l4_proto); if (!table) @@ -1627,10 +1601,14 @@ int bib_add6(struct bib *db, end: spin_unlock_bh(&table->lock); - if (new.bib) + if (new.bib) { free_bib(new.bib); - if (new.session) + TIMESTAMP_STOP(timer, TST64_SESSION_GENERIC_OLD, !error); + } + if (new.session) { free_session(new.session); + TIMESTAMP_STOP(timer, TST64_SESSION_GENERIC_NEW, !error); + } commit_delete_list(&rm_list); return error; @@ -1663,6 +1641,7 @@ int bib_add4(struct bib *db, struct tree_slot session_slot; bool allow; int error = 0; + TIMESTAMP_DECLARE_START(timer); table = get_table(db, tuple4->l4_proto); if (!table) @@ -1701,6 +1680,7 @@ int bib_add4(struct bib *db, spin_unlock_bh(&table->lock); if (new) free_session(new); + TIMESTAMP_STOP(timer, TST46_SESSION_GENERIC, !error); return error; } @@ -1721,6 +1701,7 @@ verdict bib_add_tcp6(struct bib *db, struct slot_group slots; struct bib_delete_list rm_list = { NULL }; verdict verdict; + TIMESTAMP_DECLARE_START(timer); if (WARN(pkt->tuple.l4_proto != L4PROTO_TCP, "Incorrect l4 proto in TCP handler.")) return VERDICT_DROP; @@ -1766,10 +1747,16 @@ verdict bib_add_tcp6(struct bib *db, end: spin_unlock_bh(&table->lock); - if (new.bib) + if (new.bib) { free_bib(new.bib); - if (new.session) + TIMESTAMP_STOP(timer, TST64_SESSION_TCP_OLD, + verdict == VERDICT_CONTINUE); + } + if (new.session) { free_session(new.session); + TIMESTAMP_STOP(timer, TST64_SESSION_TCP_NEW, + verdict == VERDICT_CONTINUE); + } commit_delete_list(&rm_list); return verdict; @@ -1791,6 +1778,7 @@ verdict bib_add_tcp4(struct bib *db, struct tree_slot session_slot; verdict verdict; int error; + TIMESTAMP_DECLARE_START(timer); if (WARN(pkt->tuple.l4_proto != L4PROTO_TCP, "Incorrect l4 proto in TCP handler.")) return VERDICT_DROP; @@ -1885,7 +1873,7 @@ verdict bib_add_tcp4(struct bib *db, if (new) free_session(new); - + TIMESTAMP_STOP(timer, TST46_SESSION_TCP, verdict == VERDICT_CONTINUE); return verdict; too_many_pkts: @@ -1934,6 +1922,7 @@ int bib_add_session(struct bib *db, struct slot_group slots; struct bib_delete_list rm_list = { NULL }; int error; + TIMESTAMP_DECLARE_START(timer); table = get_table(db, session->proto); if (!table) @@ -1961,10 +1950,14 @@ int bib_add_session(struct bib *db, end: spin_unlock_bh(&table->lock); - if (new.bib) + if (new.bib) { free_bib(new.bib); - if (new.session) + TIMESTAMP_STOP(timer, TST_SESSION_OLD, !error); + } + if (new.session) { free_session(new.session); + TIMESTAMP_STOP(timer, TST_SESSION_NEW, !error); + } commit_delete_list(&rm_list); return error; @@ -1972,11 +1965,13 @@ int bib_add_session(struct bib *db, static void __clean(struct expire_timer *expirer, struct bib_table *table, - struct list_head *probes) + struct list_head *probes, + u64 *sessions_rm) { struct tabled_session *session; struct tabled_session *tmp; struct collision_cb cb; + u64 session_cnt; cb.cb = expirer->decide_fate_cb; cb.arg = NULL; @@ -1988,37 +1983,141 @@ static void __clean(struct expire_timer *expirer, */ if (time_before(jiffies, session->update_time + expirer->timeout)) break; + + if (table->sess_timer.pend_rm + || *sessions_rm >= table->sess_timer.max_session_rm) { + table->sess_timer.pend_rm = true; + break; + } + session_cnt = table->session_count; decide_fate(&cb, table, session, probes); - } + *sessions_rm += session_cnt - table->session_count; + }; } static void clean_table(struct bib_table *table, struct net *ns) { LIST_HEAD(probes); LIST_HEAD(icmps); + u64 sessions_rm = 0; + TIMESTAMP_DECLARE_START(timer); spin_lock_bh(&table->lock); - __clean(&table->est_timer, table, &probes); - __clean(&table->trans_timer, table, &probes); - __clean(&table->syn4_timer, table, &probes); + __clean(&table->est_timer, table, &probes, &sessions_rm); + __clean(&table->trans_timer, table, &probes, &sessions_rm); + __clean(&table->syn4_timer, table, &probes, &sessions_rm); + if (table->pkt_queue) { - table->pkt_count -= pktqueue_prepare_clean(table->pkt_queue, - &icmps); + table->pkt_count -= pktqueue_prepare_clean(table->pkt_queue, &icmps, + &table->sess_timer.max_session_rm, &sessions_rm, + &table->sess_timer.pend_rm); } spin_unlock_bh(&table->lock); post_fate(ns, &probes); pktqueue_clean(&icmps); + + TIMESTAMP_STOP(timer, TST_SESSION_TIMER, true); } -/** - * Forgets or downgrades (from EST to TRANS) old sessions. - */ -void bib_clean(struct bib *db, struct net *ns) +static void update_timer(struct bib_table *table) +{ + struct session_timer *sess_timer = &table->sess_timer; + if (sess_timer->pend_rm) { + sess_timer->run_period_jiff = msecs_to_jiffies( + jiffies_to_msecs(sess_timer->run_period_jiff) >> 1); + sess_timer->max_session_rm <<= 1; + } else { + sess_timer->run_period_jiff = RM_SESSION_TIMER_INIT; + sess_timer->max_session_rm = RM_SESSION_MAX_INIT; + } + sess_timer->pend_rm = false; + sess_timer->timer.data = (unsigned long)(table); +} + +static void timer_function(unsigned long arg) +{ + struct bib_table *table = (struct bib_table *)arg; + clean_table(table, table->sess_timer.ns); + update_timer(table); + mod_timer(&table->sess_timer.timer, + jiffies + table->sess_timer.run_period_jiff); +} + +static void init_session_timer(struct net *ns, struct bib_table *table) +{ + struct session_timer *sess_timer = &table->sess_timer; + struct timer_list *timer = &sess_timer->timer; + + init_timer(timer); + timer->function = timer_function; + timer->expires = 0; + timer->data = (unsigned long)(table); + + sess_timer->pend_rm = false; + sess_timer->max_session_rm = RM_SESSION_MAX_INIT; + sess_timer->run_period_jiff = RM_SESSION_TIMER_INIT; + sess_timer->ns = ns; + mod_timer(timer, jiffies + RM_SESSION_TIMER_INIT); +} + +static void init_table(struct bib_table *table, + unsigned long est_timeout, + unsigned long trans_timeout, + fate_cb est_cb) +{ + table->tree6 = RB_ROOT; + table->tree4 = RB_ROOT; + table->log_bibs = DEFAULT_BIB_LOGGING; + table->log_sessions = DEFAULT_SESSION_LOGGING; + table->drop_by_addr = DEFAULT_ADDR_DEPENDENT_FILTERING; + table->bib_count = 0; + table->session_count = 0; + spin_lock_init(&table->lock); + init_expirer(&table->est_timer, est_timeout, SESSION_TIMER_EST, est_cb); + + init_expirer(&table->trans_timer, trans_timeout, SESSION_TIMER_TRANS, + just_die); + /* TODO "just_die"? what about the stored packet? */ + init_expirer(&table->syn4_timer, TCP_INCOMING_SYN, SESSION_TIMER_SYN4, + just_die); + table->pkt_count = 0; + table->pkt_limit = 0; + table->drop_v4_syn = DEFAULT_DROP_EXTERNAL_CONNECTIONS; + table->pkt_queue = NULL; +} + +struct bib *bib_create(struct net *ns) { - clean_table(&db->udp, ns); - clean_table(&db->tcp, ns); - clean_table(&db->icmp, ns); + struct bib *db; + + db = wkmalloc(struct bib, GFP_KERNEL); + if (!db) + return NULL; + + init_table(&db->udp, UDP_DEFAULT, 0, just_die); + init_table(&db->tcp, TCP_EST, TCP_TRANS, tcp_est_expire_cb); + init_table(&db->icmp, ICMP_DEFAULT, 0, just_die); + + db->tcp.pkt_limit = DEFAULT_MAX_STORED_PKTS; + db->tcp.pkt_queue = pktqueue_create(); + if (!db->tcp.pkt_queue) { + wkfree(struct bib, db); + return NULL; + } + /* + * Just in case some crazy psycho decides to change the default. + * THERE IS NO ADRESS-DEPENDENT FILTERING ON ICMP; the RFC is wrong. + */ + db->icmp.drop_by_addr = false; + + init_session_timer(ns, &db->udp); + init_session_timer(ns, &db->tcp); + init_session_timer(ns, &db->icmp); + + kref_init(&db->refs); + + return db; } static struct rb_node *find_starting_point(struct bib_table *table, diff --git a/mod/stateful/bib/pkt_queue.c b/mod/stateful/bib/pkt_queue.c index e62df10a7..d41b885cd 100644 --- a/mod/stateful/bib/pkt_queue.c +++ b/mod/stateful/bib/pkt_queue.c @@ -197,7 +197,10 @@ void pktqueue_put_node(struct pktqueue_session *node) * @probes. */ unsigned int pktqueue_prepare_clean(struct pktqueue *queue, - struct list_head *probes) + struct list_head *probes, + u64 *max_session_rm, + u64 *sessions_rm, + bool *pending_rm) { struct pktqueue_session *node, *tmp; const unsigned long TIMEOUT = get_timeout(); @@ -211,9 +214,14 @@ unsigned int pktqueue_prepare_clean(struct pktqueue *queue, if (time_before(jiffies, node->update_time + TIMEOUT)) break; + if (*pending_rm || *sessions_rm >= *max_session_rm) { + *pending_rm = true; + break; + } rm(queue, node); list_add(&node->list_hook, probes); removed++; + (*sessions_rm)++; } return removed; diff --git a/mod/stateful/filtering_and_updating.c b/mod/stateful/filtering_and_updating.c index 3379ec08d..51c06faeb 100644 --- a/mod/stateful/filtering_and_updating.c +++ b/mod/stateful/filtering_and_updating.c @@ -7,6 +7,7 @@ #include "nat64/mod/common/rfc6052.h" #include "nat64/mod/common/stats.h" #include "nat64/mod/common/rfc6145/6to4.h" +#include "nat64/mod/common/timestamp.h" #include "nat64/mod/stateful/joold.h" #include "nat64/mod/stateful/pool4/db.h" #include "nat64/mod/stateful/bib/db.h" @@ -49,6 +50,7 @@ static void log_entries(struct bib_session *entries) { struct session_entry *session = &entries->session; + /* if (entries->bib_set) { log_debug("BIB entry: %pI6c#%u - %pI4#%u (%s)", &session->src6.l3, session->src6.l4, @@ -57,11 +59,11 @@ static void log_entries(struct bib_session *entries) } else { log_debug("BIB entry: None"); } + */ if (entries->session_set) { - log_debug("Session entry: %pI6c#%u - %pI6c#%u | %pI4#%u - %pI4#%u (%s)", + log_debug("Session entry: %pI6c#%u | %pI4#%u | %pI4#%u (%s)", &session->src6.l3, session->src6.l4, - &session->dst6.l3, session->dst6.l4, &session->src4.l3, session->src4.l4, &session->dst4.l3, session->dst4.l4, l4proto_to_string(session->proto)); @@ -136,8 +138,11 @@ static int find_mask_domain(struct xlation *state, .mark = state->in.skb->mark, }; + TIMESTAMP_DECLARE_START(timer); *masks = mask_domain_find(state->jool.nat64.pool4, &state->in.tuple, state->jool.global->cfg.nat64.f_args, &args); + TIMESTAMP_STOP(timer, TST_FAU64_MDS, masks); + if (*masks) return 0; @@ -546,42 +551,56 @@ verdict filtering_and_updating(struct xlation *state) struct packet *in = &state->in; struct ipv6hdr *hdr_ip6; verdict result = VERDICT_CONTINUE; + TIMESTAMP_DECLARE(timer); log_debug("Step 2: Filtering and Updating"); switch (pkt_l3_proto(in)) { case L3PROTO_IPV6: + TIMESTAMP_START(timer); + /* Get rid of hairpinning loops and unwanted packets. */ hdr_ip6 = pkt_ip6_hdr(in); if (pool6_contains(state->jool.pool6, &hdr_ip6->saddr)) { log_debug("Hairpinning loop. Dropping..."); inc_stats(in, IPSTATS_MIB_INADDRERRORS); + TIMESTAMP_STOP(timer, TST_FAU64_VALIDATIONS, false); return VERDICT_DROP; } if (!pool6_contains(state->jool.pool6, &hdr_ip6->daddr)) { log_debug("Packet does not belong to pool6."); + TIMESTAMP_STOP(timer, TST_FAU64_VALIDATIONS, false); return VERDICT_ACCEPT; } /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp6_error(in)) { log_debug("Packet is ICMPv6 error; skipping step..."); + TIMESTAMP_STOP(timer, TST_FAU64_VALIDATIONS, true); return VERDICT_CONTINUE; } + + TIMESTAMP_STOP(timer, TST_FAU64_VALIDATIONS, true); break; case L3PROTO_IPV4: + TIMESTAMP_START(timer); + /* Get rid of unexpected packets */ if (!pool4db_contains(state->jool.nat64.pool4, state->jool.ns, in->tuple.l4_proto, &in->tuple.dst.addr4)) { log_debug("Packet does not belong to pool4."); + TIMESTAMP_STOP(timer, TST_FAU46_VALIDATIONS, false); return VERDICT_ACCEPT; } /* ICMP errors should not be filtered or affect the tables. */ if (pkt_is_icmp4_error(in)) { log_debug("Packet is ICMPv4 error; skipping step..."); + TIMESTAMP_STOP(timer, TST_FAU46_VALIDATIONS, true); return VERDICT_CONTINUE; } + + TIMESTAMP_STOP(timer, TST_FAU46_VALIDATIONS, true); break; } diff --git a/mod/stateful/timer.c b/mod/stateful/global_timer.c similarity index 87% rename from mod/stateful/timer.c rename to mod/stateful/global_timer.c index 51b6cd244..56eb17277 100644 --- a/mod/stateful/timer.c +++ b/mod/stateful/global_timer.c @@ -1,4 +1,4 @@ -#include "nat64/mod/stateful/timer.h" +#include "nat64/mod/stateful/global_timer.h" #include "nat64/mod/common/xlator.h" #include "nat64/mod/stateful/fragment_db.h" @@ -12,7 +12,6 @@ static struct timer_list timer; static int clean_state(struct xlator *jool, void *args) { fragdb_clean(jool->nat64.frag); - bib_clean(jool->nat64.bib, jool->ns); joold_clean(jool->nat64.joold, jool->nat64.bib); return 0; } @@ -26,7 +25,7 @@ static void timer_function(unsigned long arg) /** * This function should be always called *after* other init()s. */ -int timer_init(void) +int global_timer_init(void) { init_timer(&timer); timer.function = timer_function; @@ -39,7 +38,7 @@ int timer_init(void) /** * This function should be always called *before* other destroy()s. */ -void timer_destroy(void) +void global_timer_destroy(void) { del_timer_sync(&timer); } diff --git a/mod/stateful/nf_hook.c b/mod/stateful/nf_hook.c index a7f7344f6..da67d82c3 100644 --- a/mod/stateful/nf_hook.c +++ b/mod/stateful/nf_hook.c @@ -12,8 +12,8 @@ #include "nat64/mod/common/xlator.h" #include "nat64/mod/common/nl/nl_handler.h" #include "nat64/mod/stateful/fragment_db.h" +#include "nat64/mod/stateful/global_timer.h" #include "nat64/mod/stateful/joold.h" -#include "nat64/mod/stateful/timer.h" #include "nat64/mod/stateful/pool4/db.h" #include "nat64/mod/stateful/pool4/rfc6056.h" #include "nat64/mod/stateful/bib/db.h" @@ -135,9 +135,9 @@ static int __init jool_init(void) error = nlhandler_init(); if (error) goto nlhandler_fail; - error = timer_init(); + error = global_timer_init(); if (error) - goto timer_fail; + goto global_timer_fail; error = logtime_init(); if (error) goto logtime_fail; @@ -161,8 +161,8 @@ static int __init jool_init(void) instance_fail: logtime_destroy(); logtime_fail: - timer_destroy(); -timer_fail: + global_timer_destroy(); +global_timer_fail: nlhandler_destroy(); nlhandler_fail: xlator_destroy(); @@ -183,7 +183,7 @@ static void __exit jool_exit(void) nf_unregister_hooks(nfho, ARRAY_SIZE(nfho)); logtime_destroy(); - timer_destroy(); + global_timer_destroy(); nlhandler_destroy(); xlator_destroy(); rfc6056_destroy(); diff --git a/mod/stateless/impersonator.c b/mod/stateless/impersonator.c index b2585a56a..71bc08cb0 100644 --- a/mod/stateless/impersonator.c +++ b/mod/stateless/impersonator.c @@ -160,7 +160,7 @@ bool pool4db_contains(struct pool4 *pool, struct net *ns, return false; } -struct bib *bib_create(void) +struct bib *bib_create(struct net *ns) { fail(__func__); return NULL; diff --git a/test/unit/bibdb/bibdb_test.c b/test/unit/bibdb/bibdb_test.c index 0ed58a1c8..a63ad74ef 100644 --- a/test/unit/bibdb/bibdb_test.c +++ b/test/unit/bibdb/bibdb_test.c @@ -210,7 +210,7 @@ static bool init(void) { if (bib_init()) return false; - db = bib_create(); + db = bib_create(NULL); if (!db) bib_destroy(); return db; diff --git a/test/unit/bibtable/bibtable_test.c b/test/unit/bibtable/bibtable_test.c index e76d585a4..933c2b821 100644 --- a/test/unit/bibtable/bibtable_test.c +++ b/test/unit/bibtable/bibtable_test.c @@ -161,7 +161,7 @@ static bool init(void) { if (bib_init()) return false; - db = bib_create(); + db = bib_create(NULL); if (!db) bib_destroy(); return db; diff --git a/test/unit/impersonator/bib.c b/test/unit/impersonator/bib.c index 541df5ac1..94eb7530e 100644 --- a/test/unit/impersonator/bib.c +++ b/test/unit/impersonator/bib.c @@ -65,7 +65,8 @@ void pktqueue_put_node(struct pktqueue_session *node) } unsigned int pktqueue_prepare_clean(struct pktqueue *queue, - struct list_head *probes) + struct list_head *probes, u64 *max_session_rm, u64 *sessions_rm, + bool *pending_rm) { return broken_unit_call(__func__); } diff --git a/test/unit/sessiondb/sessiondb_test.c b/test/unit/sessiondb/sessiondb_test.c index 0c8ab7a28..680ec2a39 100644 --- a/test/unit/sessiondb/sessiondb_test.c +++ b/test/unit/sessiondb/sessiondb_test.c @@ -263,7 +263,7 @@ static bool init(void) { if (bib_init()) return false; - db = bib_create(); + db = bib_create(NULL); if (!db) bib_destroy(); return db; diff --git a/test/unit/sessiontable/sessiontable_test.c b/test/unit/sessiontable/sessiontable_test.c index 5fb9eeafc..8c91d10fe 100644 --- a/test/unit/sessiontable/sessiontable_test.c +++ b/test/unit/sessiontable/sessiontable_test.c @@ -280,7 +280,7 @@ static bool init(void) { if (bib_init()) return false; - db = bib_create(); + db = bib_create(NULL); if (!db) bib_destroy(); return db; diff --git a/usr/common/argp/options.c b/usr/common/argp/options.c index af611d11b..3fc0d9793 100644 --- a/usr/common/argp/options.c +++ b/usr/common/argp/options.c @@ -93,6 +93,15 @@ static const struct argp_option benchmark_opt = { }; #endif +static const struct argp_option timestamps_opt = { + .name = "timestamps", + .key = ARGP_TIMESTAMPS, + .arg = NULL, + .flags = 0, + .doc = "Report performance numbers.", + .group = 0, +}; + static const struct argp_option global_opt = { .name = OPTNAME_GLOBAL, .key = ARGP_GLOBAL, @@ -601,6 +610,7 @@ static const struct argp_option *opts_siit[] = { &blacklist_opt, &pool6791_opt, &global_opt, + ×tamps_opt, #ifdef BENCHMARK &benchmark_opt, #endif @@ -644,6 +654,7 @@ static const struct argp_option *opts_nat64[] = { #ifdef BENCHMARK &benchmark_opt, #endif + ×tamps_opt, &parse_file_opt, &instance_opt, diff --git a/usr/common/jool.c b/usr/common/jool.c index b19f71690..f3d49bbaf 100644 --- a/usr/common/jool.c +++ b/usr/common/jool.c @@ -26,6 +26,7 @@ #include "nat64/usr/pool4.h" #include "nat64/usr/bib.h" #include "nat64/usr/session.h" +#include "nat64/usr/timestamp.h" #include "nat64/usr/eam.h" #include "nat64/usr/global.h" #include "nat64/usr/log_time.h" @@ -417,6 +418,9 @@ static int parse_opt(int key, char *str, struct argp_state *state) case ARGP_LOGTIME: error = update_state(args, MODE_LOGTIME, LOGTIME_OPS); break; + case ARGP_TIMESTAMPS: + error = update_state(args, MODE_TIMESTAMPS, TIMESTAMPS_OPS); + break; case ARGP_INSTANCE: error = update_state(args, MODE_INSTANCE, INSTANCE_OPS); break; @@ -994,6 +998,16 @@ static int handle_instance(struct arguments *args) } } +static int handle_timestamps(struct arguments *args) +{ + switch (args->op) { + case OP_DISPLAY: + return timestamp_display(); + default: + return unknown_op("timestamps", args->op); + } +} + static int main_wrapped(struct arguments *args) { switch (args->mode) { @@ -1020,6 +1034,8 @@ static int main_wrapped(struct arguments *args) return handle_joold(args); case MODE_INSTANCE: return handle_instance(args); + case MODE_TIMESTAMPS: + return handle_timestamps(args); } log_err("Unknown configuration mode: %u", args->mode); diff --git a/usr/common/target/timestamp.c b/usr/common/target/timestamp.c new file mode 100644 index 000000000..02bbdd4dc --- /dev/null +++ b/usr/common/target/timestamp.c @@ -0,0 +1,96 @@ +#include + +#include "nat64/usr/timestamp.h" +#include "nat64/common/config.h" +#include "nat64/usr/netlink.h" + +static void print_entry(struct timestamps_entry_usr *entries, char *prefix) +{ + if (entries->success_count == 0 && entries->failure_count == 0) + return; + + printf("%s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\n", prefix, + entries->success_count, entries->success_min, + entries->success_avg, entries->success_max, + entries->failure_count, entries->failure_min, + entries->failure_avg, entries->failure_max); + + /* + printf("Success count: %u\n", ); + printf("Success minimum: %u\n", ); + printf("Success average: %u\n", ); + printf("Success maximum: %u\n", ); + printf("Failure count: %u\n", ); + printf("Failure minimum: %u\n", ); + printf("Failure average: %u\n", ); + printf("Failure maximum: %u\n", ); + printf("\n"); + */ +} + +static void print_batch(unsigned int index, struct timestamps_entry_usr *entry) +{ + printf("\tBatch %u\n", index); + + print_entry(entry++, "6->4: Full translations"); + print_entry(entry++, "6->4: Incoming packet validations"); + print_entry(entry++, "4->6: Full translations"); + print_entry(entry++, "4->6: Incoming packet validations"); + + print_entry(entry++, "'Determine Incoming Tuple' step"); + print_entry(entry++, "'Filtering and Updating' step"); + print_entry(entry++, "'Compute Outgoing Tuple' step"); + print_entry(entry++, "'Translating the Packet' step"); + print_entry(entry++, "'Handling Hairpinning' step"); + print_entry(entry++, "'Send packet' step"); + + print_entry(entry++, "6->4 Filtering and Updating validations"); + print_entry(entry++, "4->6 Filtering and Updating validations"); + print_entry(entry++, "Mask Domain searches"); + + print_entry(entry++, "6->4 UDP/ICMP session lookup - Found"); + print_entry(entry++, "6->4 UDP/ICMP session lookup - Created"); + print_entry(entry++, "4->6 UDP/ICMP session lookup - Found"); + print_entry(entry++, "6->4 TCP session lookup - Found"); + print_entry(entry++, "6->4 TCP session lookup - Created"); + print_entry(entry++, "4->6 TCP session lookup - Found"); + print_entry(entry++, "Generic session lookup - Found"); + print_entry(entry++, "Generic session lookup - Created"); + print_entry(entry++, "Session expiration timer"); + print_entry(entry++, "Session probing"); + print_entry(entry++, "Session mask allocation"); + + print_entry(entry++, "ICMPv6 error"); + print_entry(entry++, "ICMPv4 error"); + + printf("\n"); +} + +static int handle_display(struct jool_response *response, void *arg) +{ + struct timestamps_entry_usr *entries = response->payload; + unsigned int b, batch_count; + size_t batch_size; + + batch_size = TST_LENGTH * sizeof(*entries); + + if (response->payload_len % batch_size != 0) { + printf("Error: The kernel module responded %zu bytes, multiple of %zu expected.\n", + response->payload_len, + TST_LENGTH * sizeof(*entries)); + return -EINVAL; + } + + batch_count = response->payload_len / batch_size; + for (b = 0; b < batch_count; b++) + print_batch(b + 1, &entries[TST_LENGTH * b]); + + return 0; +} + +int timestamp_display(void) +{ + struct request_hdr hdr; + init_request_hdr(&hdr, MODE_TIMESTAMPS, OP_DISPLAY); + return netlink_request(&hdr, sizeof(hdr), handle_display, NULL); +} diff --git a/usr/stateful/Makefile.am b/usr/stateful/Makefile.am index 9f3ce9c4d..93bb13c85 100644 --- a/usr/stateful/Makefile.am +++ b/usr/stateful/Makefile.am @@ -23,7 +23,8 @@ jool_SOURCES = \ ../common/target/pool.c \ ../common/target/pool4.c \ ../common/target/pool6.c \ - ../common/target/session.c + ../common/target/session.c \ + ../common/target/timestamp.c jool_LDADD = ${LIBNLGENL3_LIBS} jool_CFLAGS = -Wall -O2 diff --git a/usr/stateless/Makefile.am b/usr/stateless/Makefile.am index 8d8ec8be4..071889f31 100644 --- a/usr/stateless/Makefile.am +++ b/usr/stateless/Makefile.am @@ -23,7 +23,8 @@ jool_siit_SOURCES = \ ../common/target/pool.c \ ../common/target/pool4.c \ ../common/target/pool6.c \ - ../common/target/session.c + ../common/target/session.c \ + ../common/target/timestamp.c jool_siit_LDADD = ${LIBNLGENL3_LIBS} jool_siit_CFLAGS = -Wall -O2