Skip to content

Commit 7e8c0d0

Browse files
committed
feat: session timeout after 60s by default
1 parent dc1c8a4 commit 7e8c0d0

File tree

6 files changed

+42
-4
lines changed

6 files changed

+42
-4
lines changed

src/cli_args.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ std::optional<cli_args> parse_args(int argc, char** argv)
3333
app.add_option("-t,--timeout", args.timeout, "Sequence timeout in milliseconds")
3434
->default_val(5000);
3535

36+
app.add_option("-s,--session-timeout", args.session_timeout, "Session timeout in milliseconds")
37+
->default_val(60000);
38+
3639
try {
3740
app.parse(argc, argv);
3841
} catch (const CLI::ParseError& e) {

src/cli_args.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct cli_args {
99
std::uint16_t target_port;
1010
std::vector<std::uint16_t> sequence;
1111
std::uint64_t timeout;
12+
std::uint64_t session_timeout;
1213
};
1314

1415
std::optional<cli_args> parse_args(int argc, char** argv);

src/knock.bpf.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static __always_inline int handle_udp_knock(
6565
return XDP_PASS;
6666
}
6767

68-
static __always_inline int handle_tcp(u32 source_ip, u16 port, u16 target_port)
68+
static __always_inline int handle_tcp(u32 source_ip, u16 port, u16 target_port, u64 session_timeout)
6969
{
7070
if (port != target_port) {
7171
return XDP_PASS;
@@ -81,6 +81,13 @@ static __always_inline int handle_tcp(u32 source_ip, u16 port, u16 target_port)
8181
}
8282

8383
if (state->sequence_complete) {
84+
85+
const __u64 current_time = bpf_ktime_get_ns();
86+
if (current_time - state->last_packet_time > MS_TO_NS(session_timeout)) {
87+
log_info("session timed out");
88+
return XDP_DROP;
89+
}
90+
8491
log_info("allowing packet because sequence is complete");
8592
return XDP_PASS;
8693
}
@@ -105,7 +112,7 @@ int knock(struct xdp_md* ctx)
105112
case IPPROTO_UDP:
106113
return handle_udp_knock(source_ip, port, &config->seq);
107114
case IPPROTO_TCP:
108-
return handle_tcp(source_ip, port, config->target_port);
115+
return handle_tcp(source_ip, port, config->target_port, config->session_timeout);
109116
}
110117

111118
return XDP_PASS;

src/knock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ struct ip_state {
2222
struct knock_config {
2323
__u16 target_port;
2424
struct port_sequence seq;
25+
__u64 session_timeout;
2526
};

src/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ static void print_config(const struct knock_config& config)
3030
std::cout << port << ' ';
3131
}
3232
std::cout << '\n';
33-
std::cout << "timeout: " << config.seq.timeout_ms << " ms\n";
33+
std::cout << "sequence timeout: " << config.seq.timeout_ms << " ms\n";
34+
std::cout << "session timeout: " << config.session_timeout << " ms\n";
3435
}
3536

3637
static void set_memory_limit()
@@ -59,6 +60,7 @@ static void parse_config(const cli_args& args, struct knock_config& config)
5960
for (size_t i = 0; i < args.sequence.size(); i++) {
6061
config.seq.ports[i] = static_cast<__u16>(args.sequence[i]);
6162
}
63+
config.session_timeout = static_cast<__u64>(args.session_timeout);
6264
}
6365

6466
int main(int argc, char** argv)

test/test_knock.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ def test_port_filtered_when_wrong_code_udp_packet_sent():
121121
assert port_filtered(DST_IP, DEFAULT_TARGET_PORT)
122122

123123

124-
@pytest.mark.parametrize("loader", [{"extra_args": ["-t", "500"]}], indirect=True)
124+
@pytest.mark.parametrize(
125+
"loader",
126+
[{"extra_args": ["-t", "500"]}, {"extra_args": ["--timeout", "500"]}],
127+
indirect=True,
128+
)
125129
def test_port_filtered_when_wrong_code_udp_packet_sent_with_timeout(loader):
126130
send_udp_packet(DST_IP, DEFAULT_KNOCK_SEQUENCE[0])
127131
assert wait_for_trace("info: code 1 passed")
@@ -132,3 +136,23 @@ def test_port_filtered_when_wrong_code_udp_packet_sent_with_timeout(loader):
132136
assert wait_for_trace("info: sequence timeout")
133137

134138
assert port_filtered(DST_IP, DEFAULT_TARGET_PORT)
139+
140+
141+
@pytest.mark.parametrize(
142+
"loader",
143+
[
144+
{"knock_sequence": [111], "extra_args": ["-s", "500"]},
145+
{"knock_sequence": [111], "extra_args": ["--session-timeout", "500"]},
146+
],
147+
indirect=True,
148+
)
149+
def test_port_filtered_when_wrong_code_udp_packet_sent_with_session_timeout(loader):
150+
config, _ = loader
151+
send_udp_packet(DST_IP, config["knock_sequence"][0])
152+
assert wait_for_trace("info: code 1 passed")
153+
assert wait_for_trace("info: sequence complete")
154+
155+
assert port_closed(DST_IP, DEFAULT_TARGET_PORT)
156+
time.sleep(1)
157+
assert port_filtered(DST_IP, DEFAULT_TARGET_PORT)
158+
assert wait_for_trace("info: session timed out")

0 commit comments

Comments
 (0)