mirror of
https://github.com/amnezia-vpn/amneziawg-linux-kernel-module.git
synced 2026-05-17 00:16:14 +03:00
feat: implement I1-I5 CPS-specified packets
This commit is contained in:
committed by
Yaroslav Gurov
parent
613e5d5410
commit
e543fc2d7f
@@ -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))
|
||||
|
||||
16
src/device.c
16
src/device.c
@@ -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;
|
||||
|
||||
@@ -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
328
src/junk.c
Normal 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
43
src/junk.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
16
src/send.c
16
src/send.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user