Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <fruggeri@arista.com>
Signed-off-by: Francesco Ruggeri <fruggeri@arista.com>
Co-developed-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Dmitry Safonov and committed by
David S. Miller
953af8e3 2717b5ad

+87 -2
+10 -1
include/net/tcp_ao.h
··· 24 24 atomic64_t pkt_bad; 25 25 atomic64_t key_not_found; 26 26 atomic64_t ao_required; 27 + atomic64_t dropped_icmp; 27 28 }; 28 29 29 30 struct tcp_ao_key { ··· 93 92 struct tcp_ao_key *rnext_key; 94 93 struct tcp_ao_counters counters; 95 94 u32 ao_required :1, 96 - __unused :31; 95 + accept_icmps :1, 96 + __unused :30; 97 97 __be32 lisn; 98 98 __be32 risn; 99 99 /* Sequence Number Extension (SNE) are upper 4 bytes for SEQ, ··· 193 191 unsigned int len, struct tcp_sigpool *hp); 194 192 void tcp_ao_destroy_sock(struct sock *sk, bool twsk); 195 193 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); 194 + bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); 196 195 enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, 197 196 const struct sk_buff *skb, unsigned short int family, 198 197 const struct request_sock *req, ··· 275 272 struct tcp_request_sock *treq, 276 273 unsigned short int family) 277 274 { 275 + } 276 + 277 + static inline bool tcp_ao_ignore_icmp(const struct sock *sk, int family, 278 + int type, int code) 279 + { 280 + return false; 278 281 } 279 282 280 283 static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
+1
include/uapi/linux/snmp.h
··· 301 301 LINUX_MIB_TCPAOBAD, /* TCPAOBad */ 302 302 LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */ 303 303 LINUX_MIB_TCPAOGOOD, /* TCPAOGood */ 304 + LINUX_MIB_TCPAODROPPEDICMPS, /* TCPAODroppedIcmps */ 304 305 __LINUX_MIB_MAX 305 306 }; 306 307
+3 -1
include/uapi/linux/tcp.h
··· 405 405 set_rnext :1, /* corresponding ::rnext */ 406 406 ao_required :1, /* don't accept non-AO connects */ 407 407 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 */ 409 410 __u16 reserved2; /* padding, must be 0 */ 410 411 __u8 current_key; /* KeyID to set as Current_key */ 411 412 __u8 rnext; /* KeyID to set as Rnext_key */ ··· 414 413 __u64 pkt_bad; /* failed verification */ 415 414 __u64 pkt_key_not_found; /* could not find a key to verify */ 416 415 __u64 pkt_ao_required; /* segments missing TCP-AO sign */ 416 + __u64 pkt_dropped_icmp; /* ICMPs that were ignored */ 417 417 } __attribute__((aligned(8))); 418 418 419 419 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
+1
net/ipv4/proc.c
··· 303 303 SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD), 304 304 SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND), 305 305 SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD), 306 + SNMP_MIB_ITEM("TCPAODroppedIcmps", LINUX_MIB_TCPAODROPPEDICMPS), 306 307 SNMP_MIB_SENTINEL 307 308 }; 308 309
+58
net/ipv4/tcp_ao.c
··· 15 15 16 16 #include <net/tcp.h> 17 17 #include <net/ipv6.h> 18 + #include <net/icmp.h> 18 19 19 20 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 20 21 unsigned int len, struct tcp_sigpool *hp) ··· 43 42 clear_hash: 44 43 memset(key, 0, tcp_ao_digest_size(mkt)); 45 44 return 1; 45 + } 46 + 47 + bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) 48 + { 49 + bool ignore_icmp = false; 50 + struct tcp_ao_info *ao; 51 + 52 + /* RFC5925, 7.8: 53 + * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 54 + * messages of Type 3 (destination unreachable), Codes 2-4 (protocol 55 + * unreachable, port unreachable, and fragmentation needed -- ’hard 56 + * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 57 + * (administratively prohibited) and Code 4 (port unreachable) intended 58 + * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- 59 + * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. 60 + */ 61 + if (family == AF_INET) { 62 + if (type != ICMP_DEST_UNREACH) 63 + return false; 64 + if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) 65 + return false; 66 + } else { 67 + if (type != ICMPV6_DEST_UNREACH) 68 + return false; 69 + if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) 70 + return false; 71 + } 72 + 73 + rcu_read_lock(); 74 + switch (sk->sk_state) { 75 + case TCP_TIME_WAIT: 76 + ao = rcu_dereference(tcp_twsk(sk)->ao_info); 77 + break; 78 + case TCP_SYN_SENT: 79 + case TCP_SYN_RECV: 80 + case TCP_LISTEN: 81 + case TCP_NEW_SYN_RECV: 82 + /* RFC5925 specifies to ignore ICMPs *only* on connections 83 + * in synchronized states. 84 + */ 85 + rcu_read_unlock(); 86 + return false; 87 + default: 88 + ao = rcu_dereference(tcp_sk(sk)->ao_info); 89 + } 90 + 91 + if (ao && !ao->accept_icmps) { 92 + ignore_icmp = true; 93 + __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); 94 + atomic64_inc(&ao->counters.dropped_icmp); 95 + } 96 + rcu_read_unlock(); 97 + 98 + return ignore_icmp; 46 99 } 47 100 48 101 /* Optimized version of tcp_ao_do_lookup(): only for sockets for which ··· 1141 1086 new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); 1142 1087 new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); 1143 1088 new_ao->ao_required = ao->ao_required; 1089 + new_ao->accept_icmps = ao->accept_icmps; 1144 1090 1145 1091 if (family == AF_INET) { 1146 1092 addr = (union tcp_ao_addr *)&newsk->sk_daddr; ··· 1848 1792 atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); 1849 1793 atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); 1850 1794 atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); 1795 + atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); 1851 1796 } 1852 1797 1853 1798 ao_info->ao_required = cmd.ao_required; 1799 + ao_info->accept_icmps = cmd.accept_icmps; 1854 1800 if (new_current) 1855 1801 WRITE_ONCE(ao_info->current_key, new_current); 1856 1802 if (new_rnext)
+7
net/ipv4/tcp_ipv4.c
··· 494 494 return -ENOENT; 495 495 } 496 496 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); 497 499 inet_twsk_put(inet_twsk(sk)); 498 500 return 0; 499 501 } ··· 506 504 (type == ICMP_DEST_UNREACH && 507 505 (code == ICMP_NET_UNREACH || 508 506 code == ICMP_HOST_UNREACH))); 507 + return 0; 508 + } 509 + 510 + if (tcp_ao_ignore_icmp(sk, AF_INET, type, code)) { 511 + sock_put(sk); 509 512 return 0; 510 513 } 511 514
+7
net/ipv6/tcp_ipv6.c
··· 396 396 } 397 397 398 398 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); 399 401 inet_twsk_put(inet_twsk(sk)); 400 402 return 0; 401 403 } ··· 405 403 fatal = icmpv6_err_convert(type, code, &err); 406 404 if (sk->sk_state == TCP_NEW_SYN_RECV) { 407 405 tcp_req_err(sk, seq, fatal); 406 + return 0; 407 + } 408 + 409 + if (tcp_ao_ignore_icmp(sk, AF_INET6, type, code)) { 410 + sock_put(sk); 408 411 return 0; 409 412 } 410 413