feat: implement I1-I5 CPS-specified packets

This commit is contained in:
Yaroslav Gurov
2025-09-24 00:03:46 +02:00
committed by Yaroslav Gurov
parent 613e5d5410
commit e543fc2d7f
8 changed files with 449 additions and 3 deletions

View File

@@ -8,7 +8,7 @@ ccflags-$(CONFIG_AMNEZIAWG_DEBUG) += -DDEBUG -g
ccflags-$(if $(WIREGUARD_VERSION),y,) += -D'WIREGUARD_VERSION="$(WIREGUARD_VERSION)"'
ccflags-$(if $(OMIT_ENDPOINTS),y,) += -D'OMIT_ENDPOINTS="$(OMIT_ENDPOINTS)"'
amneziawg-y := main.o noise.o device.o peer.o timers.o queueing.o send.o receive.o socket.o peerlookup.o allowedips.o ratelimiter.o cookie.o netlink.o magic_header.o
amneziawg-y := main.o noise.o device.o peer.o timers.o queueing.o send.o receive.o socket.o peerlookup.o allowedips.o ratelimiter.o cookie.o netlink.o junk.o magic_header.o
ifeq ($(shell [ $(VERSION) -lt 6 -o \( $(VERSION) -eq 6 -a $(PATCHLEVEL) -lt 1 \) ] && echo y),y)
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

View File

@@ -3,6 +3,7 @@
* Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include "junk.h"
#include "queueing.h"
#include "socket.h"
#include "timers.h"
@@ -235,6 +236,10 @@ static const struct net_device_ops netdev_ops = {
static void wg_destruct(struct net_device *dev)
{
struct wg_device *wg = netdev_priv(dev);
int i;
for (i = 0; i < ARRAY_SIZE(wg->ispecs); ++i)
jp_spec_free(&wg->ispecs[i]);
rtnl_lock();
list_del(&wg->device_list);
@@ -501,6 +506,8 @@ int wg_device_handle_post_config(struct net_device *dev, struct amnezia_config *
struct wg_device *wg = netdev_priv(dev);
bool a_sec_on = false;
int ret = 0;
int err;
int i, j;
if (!asc->advanced_security)
goto out;
@@ -579,6 +586,15 @@ int wg_device_handle_post_config(struct net_device *dev, struct amnezia_config *
}
}
for (i = 0; i < ARRAY_SIZE(wg->ispecs); ++i) {
err = jp_spec_setup(&wg->ispecs[i]);
if (err) {
net_dbg_ratelimited("%s: I%d-packet invalid format\n", wg->dev->name, i + 1);
ret = err;
}
a_sec_on = true;
}
wg->advanced_security_config.advanced_security = a_sec_on;
out:
return ret;

View File

@@ -6,6 +6,7 @@
#ifndef _WG_DEVICE_H
#define _WG_DEVICE_H
#include "junk.h"
#include "noise.h"
#include "allowedips.h"
#include "peerlookup.h"
@@ -64,6 +65,7 @@ struct wg_device {
u32 fwmark;
u16 incoming_port;
struct jp_spec ispecs[5];
struct magic_header headers[4];
u16 junk_size[4];
};

328
src/junk.c Normal file
View File

@@ -0,0 +1,328 @@
#include "junk.h"
#include "messages.h"
#include "peer.h"
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/random.h>
#include <linux/ktime.h>
static int parse_b_tag(char* val, struct list_head* head) {
int err;
int i;
int len;
u8* pkt;
struct jp_tag* tag;
if (!val || strncmp(val, "0x", 2))
return -EINVAL;
val += 2;
len = strlen(val);
if (len == 0 || len % 2 != 0)
return -EINVAL;
len /= 2;
pkt = kmalloc(len, GFP_KERNEL);
if (!pkt)
return -ENOMEM;
for (i = len - 1; i >= 0; --i) {
err = kstrtou8(val + i * 2, 16, pkt + i);
if (err) {
err = -EINVAL;
goto error;
}
val[i * 2] = '\0';
}
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag) {
err = -ENOMEM;
goto error;
}
tag->pkt = pkt;
tag->pkt_size = len;
list_add(&tag->head, head);
return 0;
error:
kfree(pkt);
return err;
}
static void pkt_counter_modifier(char* buf, int len, struct wg_peer *peer) {
int val = atomic_read(&peer->jp_packet_counter);
val = htonl(val);
memcpy(buf, &val, sizeof(val));
}
static int parse_c_tag(char* val, struct list_head* head) {
struct jp_tag* tag;
if (val)
return -EINVAL;
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag)
return -ENOMEM;
tag->pkt_size = sizeof(u32);
tag->func = pkt_counter_modifier;
list_add(&tag->head, head);
return 0;
}
static void unix_time_modifier(char* buf, int len, struct wg_peer *peer) {
u32 time = (u32)ktime_get_real_seconds();
time = htonl(time);
memcpy(buf, &time, sizeof(time));
}
static int parse_t_tag(char* val, struct list_head* head) {
struct jp_tag* tag;
if (val)
return -EINVAL;
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag)
return -ENOMEM;
tag->pkt_size = sizeof(u32);
tag->func = unix_time_modifier;
list_add(&tag->head, head);
return 0;
}
static void random_byte_modifier(char* buf, int len, struct wg_peer *peer) {
get_random_bytes(buf, len);
}
static int parse_r_tag(char* val, struct list_head* head) {
struct jp_tag* tag;
int len;
if (!val || 0 > kstrtoint(val, 10, &len))
return -EINVAL;
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag)
return -ENOMEM;
tag->pkt_size = len;
tag->func = random_byte_modifier;
list_add(&tag->head, head);
return 0;
}
#define ALPHABET_LEN 26
#define LETTER_LEN (ALPHABET_LEN * 2)
static void random_char_modifier(char* buf, int len, struct wg_peer *peer) {
int i;
u32 byte;
for (i = 0; i < len; ++i) {
byte = get_random_u32() % LETTER_LEN;
buf[i] = (byte < ALPHABET_LEN) ? 'a' + byte : 'A' + byte - ALPHABET_LEN;
}
}
static int parse_rc_tag(char* val, struct list_head* head) {
struct jp_tag* tag;
int len;
if (!val || 0 > kstrtoint(val, 10, &len))
return -EINVAL;
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag)
return -ENOMEM;
tag->pkt_size = len;
tag->func = random_char_modifier;
list_add(&tag->head, head);
return 0;
}
#define DIGIT_LEN 10
static void random_digit_modifier(char* buf, int len, struct wg_peer *peer) {
int i;
for (i = 0; i < len; ++i)
buf[i] = '0' + get_random_u32() % DIGIT_LEN;
}
static int parse_rd_tag(char* val, struct list_head* head) {
struct jp_tag* tag;
int len;
if (!val || 0 > kstrtoint(val, 10, &len))
return -EINVAL;
tag = kzalloc(sizeof(*tag), GFP_KERNEL);
if (!tag)
return -ENOMEM;
tag->pkt_size = len;
tag->func = random_digit_modifier;
list_add(&tag->head, head);
return 0;
}
int jp_parse_tags(char* str, struct list_head* head) {
int err = 0;
char* key;
char* val;
while (true)
{
strsep(&str, "<");
val = strsep(&str, ">");
if (!val)
break;
key = strsep(&val, " ");
if (!strcmp(key, "b")) {
err = parse_b_tag(val, head);
if (err)
return err;
}
else if (!strcmp(key, "c")) {
err = parse_c_tag(val, head);
if (err)
return err;
}
else if (!strcmp(key, "t")) {
err = parse_t_tag(val, head);
if (err)
return err;
}
else if (!strcmp(key, "r")) {
err = parse_r_tag(val, head);
if (err)
return err;
}
else if (!strcmp(key, "rc")) {
err = parse_rc_tag(val, head);
if (err)
return err;
}
else if (!strcmp(key, "rd")) {
err = parse_rd_tag(val, head);
if (err)
return err;
}
else
return -EINVAL;
}
return 0;
}
void jp_tag_free(struct jp_tag* tag) {
kfree(tag->pkt);
}
void jp_spec_free(struct jp_spec *spec) {
kfree(spec->pkt);
kfree(spec->mods);
}
int jp_spec_setup(struct jp_spec *spec) {
int err = 0;
int pkt_size, mods_size;
struct jp_tag *tag, *tmp;
struct jp_modifier *mod;
char* buf;
LIST_HEAD(head);
mutex_init(&spec->lock);
if (spec->desc == NULL)
return 0;
mutex_lock(&spec->lock);
buf = kstrdup(spec->desc, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto error;
}
err = jp_parse_tags(buf, &head);
if (err)
goto error;
pkt_size = 0;
mods_size = 0;
list_for_each_entry(tag, &head, head) {
pkt_size += tag->pkt_size;
if (tag->func)
++mods_size;
}
if (pkt_size > MESSAGE_MAX_SIZE) {
err = -EINVAL;
goto error;
}
spec->pkt = kzalloc(pkt_size, GFP_KERNEL);
spec->mods = kzalloc(mods_size * sizeof(*spec->mods), GFP_KERNEL);
if (!spec->pkt || !spec->mods) {
err = -ENOMEM;
goto error;
}
list_for_each_entry_reverse(tag, &head, head) {
if (tag->pkt) {
memcpy(spec->pkt + spec->pkt_size, tag->pkt, tag->pkt_size);
}
if (tag->func) {
mod = spec->mods + spec->mods_size;
mod->func = tag->func;
mod->buf = spec->pkt + spec->pkt_size;
mod->buf_len = tag->pkt_size;
spec->mods_size++;
}
spec->pkt_size += tag->pkt_size;
}
error:
list_for_each_entry_safe(tag, tmp, &head, head) {
jp_tag_free(tag);
list_del(&tag->head);
kfree(tag);
}
kfree(buf);
mutex_unlock(&spec->lock);
return err;
}
void jp_spec_applymods(struct jp_spec* spec, struct wg_peer* peer) {
int i;
struct jp_modifier* mod;
for (i = 0; i < spec->mods_size; i++) {
mod = &spec->mods[i];
if(mod->func)
mod->func(mod->buf, mod->buf_len, peer);
}
}

43
src/junk.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef _AWG_JUNK_H
#define _AWG_JUNK_H
#include <linux/list.h>
#include <linux/mutex.h>
struct wg_peer;
typedef void(*jp_modifier_func)(char*, int, struct wg_peer*);
struct jp_tag
{
u8* pkt;
jp_modifier_func func;
struct list_head head;
int pkt_size;
};
void jp_tag_free(struct jp_tag* tag);
int jp_parse_tags(char* str, struct list_head* head);
struct jp_modifier
{
jp_modifier_func func;
char* buf;
int buf_len;
};
struct jp_spec
{
char* desc;
u8* pkt;
struct jp_modifier* mods;
struct mutex lock;
int pkt_size;
int mods_size;
};
void jp_spec_free(struct jp_spec* spec);
int jp_spec_setup(struct jp_spec* spec);
void jp_spec_applymods(struct jp_spec* spec, struct wg_peer* peer);
#endif

View File

@@ -53,7 +53,12 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
[WGDEVICE_A_H4] = { .type = NLA_NUL_STRING },
[WGDEVICE_A_PEER] = { .type = NLA_NESTED },
[WGDEVICE_A_S3] = { .type = NLA_U16 },
[WGDEVICE_A_S4] = { .type = NLA_U16 }
[WGDEVICE_A_S4] = { .type = NLA_U16 },
[WGDEVICE_A_I1] = { .type = NLA_NUL_STRING },
[WGDEVICE_A_I2] = { .type = NLA_NUL_STRING },
[WGDEVICE_A_I3] = { .type = NLA_NUL_STRING },
[WGDEVICE_A_I4] = { .type = NLA_NUL_STRING },
[WGDEVICE_A_I5] = { .type = NLA_NUL_STRING }
};
static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
@@ -447,7 +452,17 @@ static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
(mh_genspec(&wg->headers[MSGIDX_TRANSPORT], buf, sizeof(buf)) &&
nla_put_string(skb, WGDEVICE_A_H4, buf)) ||
nla_put_u16(skb, WGDEVICE_A_S3, wg->junk_size[MSGIDX_HANDSHAKE_COOKIE]) ||
nla_put_u16(skb, WGDEVICE_A_S4, wg->junk_size[MSGIDX_TRANSPORT])
nla_put_u16(skb, WGDEVICE_A_S4, wg->junk_size[MSGIDX_TRANSPORT]) ||
(wg->ispecs[0].desc &&
nla_put_string(skb, WGDEVICE_A_I1, wg->ispecs[0].desc)) ||
(wg->ispecs[1].desc &&
nla_put_string(skb, WGDEVICE_A_I2, wg->ispecs[1].desc)) ||
(wg->ispecs[2].desc &&
nla_put_string(skb, WGDEVICE_A_I3, wg->ispecs[2].desc)) ||
(wg->ispecs[3].desc &&
nla_put_string(skb, WGDEVICE_A_I4, wg->ispecs[3].desc)) ||
(wg->ispecs[4].desc &&
nla_put_string(skb, WGDEVICE_A_I5, wg->ispecs[4].desc)))
goto out;
down_read(&wg->static_identity.lock);
@@ -830,6 +845,31 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info)
wg->junk_size[MSGIDX_TRANSPORT] = nla_get_u16(info->attrs[WGDEVICE_A_S4]);
}
if (info->attrs[WGDEVICE_A_I1]) {
asc->advanced_security = true;
wg->ispecs[0].desc = nla_strdup(info->attrs[WGDEVICE_A_I1], GFP_KERNEL);
}
if (info->attrs[WGDEVICE_A_I2]) {
asc->advanced_security = true;
wg->ispecs[1].desc = nla_strdup(info->attrs[WGDEVICE_A_I2], GFP_KERNEL);
}
if (info->attrs[WGDEVICE_A_I3]) {
asc->advanced_security = true;
wg->ispecs[2].desc = nla_strdup(info->attrs[WGDEVICE_A_I3], GFP_KERNEL);
}
if (info->attrs[WGDEVICE_A_I4]) {
asc->advanced_security = true;
wg->ispecs[3].desc = nla_strdup(info->attrs[WGDEVICE_A_I4], GFP_KERNEL);
}
if (info->attrs[WGDEVICE_A_I5]) {
asc->advanced_security = true;
wg->ispecs[4].desc = nla_strdup(info->attrs[WGDEVICE_A_I5], GFP_KERNEL);
}
if (flags & WGDEVICE_F_REPLACE_PEERS)
wg_peer_remove_all(wg);

View File

@@ -64,6 +64,7 @@ struct wg_peer {
struct list_head allowedips_list;
struct napi_struct napi;
u64 internal_id;
atomic_t jp_packet_counter;
bool advanced_security;
};

View File

@@ -4,6 +4,7 @@
*/
#include "compat/compat.h"
#include "junk.h"
#include "magic_header.h"
#include "queueing.h"
#include "timers.h"
@@ -35,6 +36,8 @@ static void wg_packet_send_handshake_initiation(struct wg_peer *peer)
void *buffer;
u8 ds;
u16 junk_packet_count, junk_packet_size;
int i;
struct jp_spec* spec;
if (!wg_birthdate_has_expired(atomic64_read(&peer->last_sent_handshake),
REKEY_TIMEOUT))
@@ -45,6 +48,19 @@ static void wg_packet_send_handshake_initiation(struct wg_peer *peer)
peer->device->dev->name, peer->internal_id,
&peer->endpoint.addr);
atomic_set(&peer->jp_packet_counter, get_random_u32());
for (i = 0; i < ARRAY_SIZE(wg->ispecs); ++i)
{
spec = &wg->ispecs[i];
if (spec->pkt_size > 0) {
mutex_lock(&spec->lock);
jp_spec_applymods(spec, peer);
wg_socket_send_buffer_to_peer(peer, spec->pkt, spec->pkt_size, 0, 0);
atomic_inc(&peer->jp_packet_counter);
mutex_unlock(&spec->lock);
}
}
if (wg->advanced_security_config.advanced_security && peer->advanced_security) {
junk_packet_count = wg->advanced_security_config.junk_packet_count;
buffer = kzalloc(wg->advanced_security_config.junk_packet_max_size, GFP_KERNEL);