Skip to content

Commit 953af8e

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Ignore specific ICMPs for TCP-AO connections
Similarly to IPsec, RFC5925 prescribes: ">> A TCP-AO implementation MUST default to ignore incoming ICMPv4 messages of Type 3 (destination unreachable), Codes 2-4 (protocol unreachable, port unreachable, and fragmentation needed -- ’hard errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 (administratively prohibited) and Code 4 (port unreachable) intended for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs." A selftest (later in patch series) verifies that this attack is not possible in this TCP-AO implementation. Co-developed-by: Francesco Ruggeri <[email protected]> Signed-off-by: Francesco Ruggeri <[email protected]> Co-developed-by: Salam Noureddine <[email protected]> Signed-off-by: Salam Noureddine <[email protected]> Signed-off-by: Dmitry Safonov <[email protected]> Acked-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2717b5a commit 953af8e

File tree

7 files changed

+87
-2
lines changed

7 files changed

+87
-2
lines changed

include/net/tcp_ao.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct tcp_ao_counters {
2424
atomic64_t pkt_bad;
2525
atomic64_t key_not_found;
2626
atomic64_t ao_required;
27+
atomic64_t dropped_icmp;
2728
};
2829

2930
struct tcp_ao_key {
@@ -92,7 +93,8 @@ struct tcp_ao_info {
9293
struct tcp_ao_key *rnext_key;
9394
struct tcp_ao_counters counters;
9495
u32 ao_required :1,
95-
__unused :31;
96+
accept_icmps :1,
97+
__unused :30;
9698
__be32 lisn;
9799
__be32 risn;
98100
/* Sequence Number Extension (SNE) are upper 4 bytes for SEQ,
@@ -191,6 +193,7 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
191193
unsigned int len, struct tcp_sigpool *hp);
192194
void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
193195
void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
196+
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
194197
enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
195198
const struct sk_buff *skb, unsigned short int family,
196199
const struct request_sock *req,
@@ -274,6 +277,12 @@ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
274277
{
275278
}
276279

280+
static inline bool tcp_ao_ignore_icmp(const struct sock *sk, int family,
281+
int type, int code)
282+
{
283+
return false;
284+
}
285+
277286
static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
278287
const struct sk_buff *skb, unsigned short int family,
279288
const struct request_sock *req, const struct tcp_ao_hdr *aoh)

include/uapi/linux/snmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ enum
301301
LINUX_MIB_TCPAOBAD, /* TCPAOBad */
302302
LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */
303303
LINUX_MIB_TCPAOGOOD, /* TCPAOGood */
304+
LINUX_MIB_TCPAODROPPEDICMPS, /* TCPAODroppedIcmps */
304305
__LINUX_MIB_MAX
305306
};
306307

include/uapi/linux/tcp.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,16 @@ struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */
405405
set_rnext :1, /* corresponding ::rnext */
406406
ao_required :1, /* don't accept non-AO connects */
407407
set_counters :1, /* set/clear ::pkt_* counters */
408-
reserved :28; /* must be 0 */
408+
accept_icmps :1, /* accept incoming ICMPs */
409+
reserved :27; /* must be 0 */
409410
__u16 reserved2; /* padding, must be 0 */
410411
__u8 current_key; /* KeyID to set as Current_key */
411412
__u8 rnext; /* KeyID to set as Rnext_key */
412413
__u64 pkt_good; /* verified segments */
413414
__u64 pkt_bad; /* failed verification */
414415
__u64 pkt_key_not_found; /* could not find a key to verify */
415416
__u64 pkt_ao_required; /* segments missing TCP-AO sign */
417+
__u64 pkt_dropped_icmp; /* ICMPs that were ignored */
416418
} __attribute__((aligned(8)));
417419

418420
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */

net/ipv4/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ static const struct snmp_mib snmp4_net_list[] = {
303303
SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD),
304304
SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND),
305305
SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD),
306+
SNMP_MIB_ITEM("TCPAODroppedIcmps", LINUX_MIB_TCPAODROPPEDICMPS),
306307
SNMP_MIB_SENTINEL
307308
};
308309

net/ipv4/tcp_ao.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <net/tcp.h>
1717
#include <net/ipv6.h>
18+
#include <net/icmp.h>
1819

1920
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
2021
unsigned int len, struct tcp_sigpool *hp)
@@ -44,6 +45,60 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
4445
return 1;
4546
}
4647

48+
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code)
49+
{
50+
bool ignore_icmp = false;
51+
struct tcp_ao_info *ao;
52+
53+
/* RFC5925, 7.8:
54+
* >> A TCP-AO implementation MUST default to ignore incoming ICMPv4
55+
* messages of Type 3 (destination unreachable), Codes 2-4 (protocol
56+
* unreachable, port unreachable, and fragmentation needed -- ’hard
57+
* errors’), and ICMPv6 Type 1 (destination unreachable), Code 1
58+
* (administratively prohibited) and Code 4 (port unreachable) intended
59+
* for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN-
60+
* WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs.
61+
*/
62+
if (family == AF_INET) {
63+
if (type != ICMP_DEST_UNREACH)
64+
return false;
65+
if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED)
66+
return false;
67+
} else {
68+
if (type != ICMPV6_DEST_UNREACH)
69+
return false;
70+
if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH)
71+
return false;
72+
}
73+
74+
rcu_read_lock();
75+
switch (sk->sk_state) {
76+
case TCP_TIME_WAIT:
77+
ao = rcu_dereference(tcp_twsk(sk)->ao_info);
78+
break;
79+
case TCP_SYN_SENT:
80+
case TCP_SYN_RECV:
81+
case TCP_LISTEN:
82+
case TCP_NEW_SYN_RECV:
83+
/* RFC5925 specifies to ignore ICMPs *only* on connections
84+
* in synchronized states.
85+
*/
86+
rcu_read_unlock();
87+
return false;
88+
default:
89+
ao = rcu_dereference(tcp_sk(sk)->ao_info);
90+
}
91+
92+
if (ao && !ao->accept_icmps) {
93+
ignore_icmp = true;
94+
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS);
95+
atomic64_inc(&ao->counters.dropped_icmp);
96+
}
97+
rcu_read_unlock();
98+
99+
return ignore_icmp;
100+
}
101+
47102
/* Optimized version of tcp_ao_do_lookup(): only for sockets for which
48103
* it's known that the keys in ao_info are matching peer's
49104
* family/address/VRF/etc.
@@ -1086,6 +1141,7 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
10861141
new_ao->lisn = htonl(tcp_rsk(req)->snt_isn);
10871142
new_ao->risn = htonl(tcp_rsk(req)->rcv_isn);
10881143
new_ao->ao_required = ao->ao_required;
1144+
new_ao->accept_icmps = ao->accept_icmps;
10891145

10901146
if (family == AF_INET) {
10911147
addr = (union tcp_ao_addr *)&newsk->sk_daddr;
@@ -1792,9 +1848,11 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
17921848
atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad);
17931849
atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found);
17941850
atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required);
1851+
atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp);
17951852
}
17961853

17971854
ao_info->ao_required = cmd.ao_required;
1855+
ao_info->accept_icmps = cmd.accept_icmps;
17981856
if (new_current)
17991857
WRITE_ONCE(ao_info->current_key, new_current);
18001858
if (new_rnext)

net/ipv4/tcp_ipv4.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,8 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
494494
return -ENOENT;
495495
}
496496
if (sk->sk_state == TCP_TIME_WAIT) {
497+
/* To increase the counter of ignored icmps for TCP-AO */
498+
tcp_ao_ignore_icmp(sk, AF_INET, type, code);
497499
inet_twsk_put(inet_twsk(sk));
498500
return 0;
499501
}
@@ -507,6 +509,11 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
507509
return 0;
508510
}
509511

512+
if (tcp_ao_ignore_icmp(sk, AF_INET, type, code)) {
513+
sock_put(sk);
514+
return 0;
515+
}
516+
510517
bh_lock_sock(sk);
511518
/* If too many ICMPs get dropped on busy
512519
* servers this needs to be solved differently.

net/ipv6/tcp_ipv6.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
396396
}
397397

398398
if (sk->sk_state == TCP_TIME_WAIT) {
399+
/* To increase the counter of ignored icmps for TCP-AO */
400+
tcp_ao_ignore_icmp(sk, AF_INET6, type, code);
399401
inet_twsk_put(inet_twsk(sk));
400402
return 0;
401403
}
@@ -406,6 +408,11 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
406408
return 0;
407409
}
408410

411+
if (tcp_ao_ignore_icmp(sk, AF_INET6, type, code)) {
412+
sock_put(sk);
413+
return 0;
414+
}
415+
409416
bh_lock_sock(sk);
410417
if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG)
411418
__NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS);

0 commit comments

Comments
 (0)