mirror of https://git.zx2c4.com/WireGuard
net: implement ECN handling, rfc6040 style
To decide whether we should use the compatibility mode or the normal mode with a peer, we use the handshake messages as a signaling channel. If we receive the expected ECN bits, it most likely means they're running a compatible version. Signed-off-by: Florent Daigniere <nextgens@freenetproject.org>
This commit is contained in:
parent
516f05862e
commit
6bf5a3c48b
|
@ -123,6 +123,6 @@ enum message_alignments {
|
|||
#define DATA_PACKET_HEAD_ROOM \
|
||||
ALIGN(sizeof(struct message_data) + SKB_HEADER_LEN, 4)
|
||||
|
||||
enum { HANDSHAKE_DSCP = 0x88 /* AF41, plus 00 ECN */ };
|
||||
enum { HANDSHAKE_DSCP = 0x8A /* AF41, plus 10 ECN */ };
|
||||
|
||||
#endif /* _WG_MESSAGES_H */
|
||||
|
|
|
@ -63,6 +63,7 @@ struct wg_peer {
|
|||
u64 internal_id;
|
||||
struct napi_struct napi;
|
||||
bool is_dead;
|
||||
bool is_ecn_aware;
|
||||
};
|
||||
|
||||
struct wg_peer *wg_peer_create(struct wg_device *wg,
|
||||
|
|
|
@ -153,6 +153,7 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
|||
return;
|
||||
}
|
||||
wg_socket_set_peer_endpoint_from_skb(peer, skb);
|
||||
peer->is_ecn_aware = INET_ECN_is_capable(PACKET_CB(skb)->ds);
|
||||
net_dbg_ratelimited("%s: Receiving handshake initiation from peer %llu (%pISpfsc)\n",
|
||||
wg->dev->name, peer->internal_id,
|
||||
&peer->endpoint.addr);
|
||||
|
@ -175,6 +176,7 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
|||
return;
|
||||
}
|
||||
wg_socket_set_peer_endpoint_from_skb(peer, skb);
|
||||
peer->is_ecn_aware = INET_ECN_is_capable(PACKET_CB(skb)->ds);
|
||||
net_dbg_ratelimited("%s: Receiving handshake response from peer %llu (%pISpfsc)\n",
|
||||
wg->dev->name, peer->internal_id,
|
||||
&peer->endpoint.addr);
|
||||
|
@ -297,6 +299,59 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 6040 4.2
|
||||
* To decapsulate the inner header at the tunnel egress, a compliant
|
||||
* tunnel egress MUST set the outgoing ECN field to the codepoint at the
|
||||
* intersection of the appropriate arriving inner header (row) and outer
|
||||
* header (column) in Figure 4
|
||||
*
|
||||
* +---------+------------------------------------------------+
|
||||
* |Arriving | Arriving Outer Header |
|
||||
* | Inner +---------+------------+------------+------------+
|
||||
* | Header | Not-ECT | ECT(0) | ECT(1) | CE |
|
||||
* +---------+---------+------------+------------+------------+
|
||||
* | Not-ECT | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)|
|
||||
* | ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE |
|
||||
* | ECT(1) | ECT(1) | ECT(1) (!) | ECT(1) | CE |
|
||||
* | CE | CE | CE | CE(!!!)| CE |
|
||||
* +---------+---------+------------+------------+------------+
|
||||
*
|
||||
* Figure 4: New IP in IP Decapsulation Behaviour
|
||||
*
|
||||
* returns 0 on success
|
||||
* 1 if the packet should be dropped
|
||||
*/
|
||||
static inline int ECN_RFC6040_decapsulate(struct sk_buff *skb)
|
||||
{
|
||||
__u8 outer = PACKET_CB(skb)->ds;
|
||||
__u8 inner = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
|
||||
|
||||
switch (outer & INET_ECN_MASK) {
|
||||
case INET_ECN_CE:
|
||||
PACKET_CB(skb)->ds =
|
||||
(inner & ~INET_ECN_MASK) |
|
||||
INET_ECN_CE;
|
||||
switch (inner & INET_ECN_MASK) {
|
||||
case INET_ECN_NOT_ECT:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case INET_ECN_ECT_1:
|
||||
switch (inner & INET_ECN_MASK) {
|
||||
case INET_ECN_ECT_0:
|
||||
PACKET_CB(skb)->ds =
|
||||
(inner & ~INET_ECN_MASK) |
|
||||
INET_ECN_ECT_1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PACKET_CB(skb)->ds = inner;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is RFC6479, a replay detection bitmap algorithm that avoids bitshifts */
|
||||
static bool counter_validate(union noise_counter *counter, u64 their_counter)
|
||||
{
|
||||
|
@ -394,13 +449,24 @@ static void wg_packet_consume_data_done(struct wg_peer *peer,
|
|||
len = ntohs(ip_hdr(skb)->tot_len);
|
||||
if (unlikely(len < sizeof(struct iphdr)))
|
||||
goto dishonest_packet_size;
|
||||
if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
|
||||
IP_ECN_set_ce(ip_hdr(skb));
|
||||
|
||||
if (ECN_RFC6040_decapsulate(skb)) {
|
||||
net_dbg_ratelimited("%s: Dropping packet from peer %llu (%pISpfsc) - ECN\n",
|
||||
dev->name, peer->internal_id,
|
||||
&peer->endpoint.addr);
|
||||
++dev->stats.rx_dropped;
|
||||
goto packet_processed;
|
||||
}
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
len = ntohs(ipv6_hdr(skb)->payload_len) +
|
||||
sizeof(struct ipv6hdr);
|
||||
if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
|
||||
IP6_ECN_set_ce(skb, ipv6_hdr(skb));
|
||||
if (ECN_RFC6040_decapsulate(skb)) {
|
||||
net_dbg_ratelimited("%s: Dropping packet from peer %llu (%pISpfsc) - ECN\n",
|
||||
dev->name, peer->internal_id,
|
||||
&peer->endpoint.addr);
|
||||
++dev->stats.rx_dropped;
|
||||
goto packet_processed;
|
||||
}
|
||||
} else {
|
||||
goto dishonest_packet_type;
|
||||
}
|
||||
|
@ -582,6 +648,7 @@ void wg_packet_receive(struct wg_device *wg, struct sk_buff *skb)
|
|||
goto err;
|
||||
}
|
||||
skb_queue_tail(&wg->incoming_handshakes, skb);
|
||||
PACKET_CB(skb)->ds = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
|
||||
/* Queues up a call to packet_process_queued_handshake_
|
||||
* packets(skb):
|
||||
*/
|
||||
|
|
|
@ -389,10 +389,10 @@ void wg_packet_send_staged_packets(struct wg_peer *peer)
|
|||
* handshake.
|
||||
*/
|
||||
skb_queue_walk(&packets, skb) {
|
||||
/* 0 for no outer TOS: no leak. TODO: at some later point, we
|
||||
* might consider using flowi->tos as outer instead.
|
||||
*/
|
||||
PACKET_CB(skb)->ds = ip_tunnel_ecn_encap(0, ip_hdr(skb), skb);
|
||||
PACKET_CB(skb)->ds = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
|
||||
if(!peer->is_ecn_aware) {
|
||||
PACKET_CB(skb)->ds &= ~INET_ECN_MASK;
|
||||
}
|
||||
PACKET_CB(skb)->nonce =
|
||||
atomic64_inc_return(&key->counter.counter) - 1;
|
||||
if (unlikely(PACKET_CB(skb)->nonce >= REJECT_AFTER_MESSAGES))
|
||||
|
|
Loading…
Reference in New Issue