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: Add tcp_parse_auth_options()

Introduce a helper that:
(1) shares the common code with TCP-MD5 header options parsing
(2) looks for hash signature only once for both TCP-MD5 and TCP-AO
(3) fails with -EEXIST if any TCP sign option is present twice, see
RFC5925 (2.2):
">> A single TCP segment MUST NOT have more than one TCP-AO in its
options sequence. When multiple TCP-AOs appear, TCP MUST discard
the segment."

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
f7dca36f 1e03d32b

+93 -22
+6
include/net/dropreason-core.h
··· 20 20 FN(IP_NOPROTO) \ 21 21 FN(SOCKET_RCVBUFF) \ 22 22 FN(PROTO_MEM) \ 23 + FN(TCP_AUTH_HDR) \ 23 24 FN(TCP_MD5NOTFOUND) \ 24 25 FN(TCP_MD5UNEXPECTED) \ 25 26 FN(TCP_MD5FAILURE) \ ··· 143 142 * drop out of udp_memory_allocated. 144 143 */ 145 144 SKB_DROP_REASON_PROTO_MEM, 145 + /** 146 + * @SKB_DROP_REASON_TCP_AUTH_HDR: TCP-MD5 or TCP-AO hashes are met 147 + * twice or set incorrectly. 148 + */ 149 + SKB_DROP_REASON_TCP_AUTH_HDR, 146 150 /** 147 151 * @SKB_DROP_REASON_TCP_MD5NOTFOUND: no MD5 hash and one expected, 148 152 * corresponding to LINUX_MIB_TCPMD5NOTFOUND
+23 -1
include/net/tcp.h
··· 438 438 void tcp_parse_options(const struct net *net, const struct sk_buff *skb, 439 439 struct tcp_options_received *opt_rx, 440 440 int estab, struct tcp_fastopen_cookie *foc); 441 - const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); 442 441 443 442 /* 444 443 * BPF SKB-less helpers ··· 2671 2672 2672 2673 return tcp_clock_ns() + (u64)delay * NSEC_PER_USEC; 2673 2674 } 2675 + return 0; 2676 + } 2677 + 2678 + static inline int tcp_parse_auth_options(const struct tcphdr *th, 2679 + const u8 **md5_hash, const struct tcp_ao_hdr **aoh) 2680 + { 2681 + const u8 *md5_tmp, *ao_tmp; 2682 + int ret; 2683 + 2684 + ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp); 2685 + if (ret) 2686 + return ret; 2687 + 2688 + if (md5_hash) 2689 + *md5_hash = md5_tmp; 2690 + 2691 + if (aoh) { 2692 + if (!ao_tmp) 2693 + *aoh = NULL; 2694 + else 2695 + *aoh = (struct tcp_ao_hdr *)(ao_tmp - 2); 2696 + } 2697 + 2674 2698 return 0; 2675 2699 } 2676 2700
+16 -1
include/net/tcp_ao.h
··· 152 152 void tcp_ao_established(struct sock *sk); 153 153 void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); 154 154 void tcp_ao_connect_init(struct sock *sk); 155 - 155 + void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, 156 + struct tcp_request_sock *treq, 157 + unsigned short int family); 156 158 #else /* CONFIG_TCP_AO */ 157 159 158 160 static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, ··· 184 182 185 183 static inline void tcp_ao_connect_init(struct sock *sk) 186 184 { 185 + } 186 + #endif 187 + 188 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 189 + int tcp_do_parse_auth_options(const struct tcphdr *th, 190 + const u8 **md5_hash, const u8 **ao_hash); 191 + #else 192 + static inline int tcp_do_parse_auth_options(const struct tcphdr *th, 193 + const u8 **md5_hash, const u8 **ao_hash) 194 + { 195 + *md5_hash = NULL; 196 + *ao_hash = NULL; 197 + return 0; 187 198 } 188 199 #endif 189 200
+2 -1
net/ipv4/tcp.c
··· 4398 4398 l3index = sdif ? dif : 0; 4399 4399 4400 4400 hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); 4401 - hash_location = tcp_parse_md5sig_option(th); 4401 + if (tcp_parse_auth_options(th, &hash_location, NULL)) 4402 + return SKB_DROP_REASON_TCP_AUTH_HDR; 4402 4403 4403 4404 /* We've parsed the options - do we have a hash? */ 4404 4405 if (!hash_expected && !hash_location)
+29 -10
net/ipv4/tcp_input.c
··· 4255 4255 return true; 4256 4256 } 4257 4257 4258 - #ifdef CONFIG_TCP_MD5SIG 4258 + #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) 4259 4259 /* 4260 - * Parse MD5 Signature option 4260 + * Parse Signature options 4261 4261 */ 4262 - const u8 *tcp_parse_md5sig_option(const struct tcphdr *th) 4262 + int tcp_do_parse_auth_options(const struct tcphdr *th, 4263 + const u8 **md5_hash, const u8 **ao_hash) 4263 4264 { 4264 4265 int length = (th->doff << 2) - sizeof(*th); 4265 4266 const u8 *ptr = (const u8 *)(th + 1); 4267 + unsigned int minlen = TCPOLEN_MD5SIG; 4268 + 4269 + if (IS_ENABLED(CONFIG_TCP_AO)) 4270 + minlen = sizeof(struct tcp_ao_hdr) + 1; 4271 + 4272 + *md5_hash = NULL; 4273 + *ao_hash = NULL; 4266 4274 4267 4275 /* If not enough data remaining, we can short cut */ 4268 - while (length >= TCPOLEN_MD5SIG) { 4276 + while (length >= minlen) { 4269 4277 int opcode = *ptr++; 4270 4278 int opsize; 4271 4279 4272 4280 switch (opcode) { 4273 4281 case TCPOPT_EOL: 4274 - return NULL; 4282 + return 0; 4275 4283 case TCPOPT_NOP: 4276 4284 length--; 4277 4285 continue; 4278 4286 default: 4279 4287 opsize = *ptr++; 4280 4288 if (opsize < 2 || opsize > length) 4281 - return NULL; 4282 - if (opcode == TCPOPT_MD5SIG) 4283 - return opsize == TCPOLEN_MD5SIG ? ptr : NULL; 4289 + return -EINVAL; 4290 + if (opcode == TCPOPT_MD5SIG) { 4291 + if (opsize != TCPOLEN_MD5SIG) 4292 + return -EINVAL; 4293 + if (unlikely(*md5_hash || *ao_hash)) 4294 + return -EEXIST; 4295 + *md5_hash = ptr; 4296 + } else if (opcode == TCPOPT_AO) { 4297 + if (opsize <= sizeof(struct tcp_ao_hdr)) 4298 + return -EINVAL; 4299 + if (unlikely(*md5_hash || *ao_hash)) 4300 + return -EEXIST; 4301 + *ao_hash = ptr; 4302 + } 4284 4303 } 4285 4304 ptr += opsize - 2; 4286 4305 length -= opsize; 4287 4306 } 4288 - return NULL; 4307 + return 0; 4289 4308 } 4290 - EXPORT_SYMBOL(tcp_parse_md5sig_option); 4309 + EXPORT_SYMBOL(tcp_do_parse_auth_options); 4291 4310 #endif 4292 4311 4293 4312 /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM
+10 -5
net/ipv4/tcp_ipv4.c
··· 670 670 * Exception: precedence violation. We do not implement it in any case. 671 671 */ 672 672 673 - #ifdef CONFIG_TCP_MD5SIG 673 + #ifdef CONFIG_TCP_AO 674 + #define OPTION_BYTES MAX_TCP_OPTION_SPACE 675 + #elif defined(CONFIG_TCP_MD5SIG) 674 676 #define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED 675 677 #else 676 678 #define OPTION_BYTES sizeof(__be32) ··· 687 685 } rep; 688 686 struct ip_reply_arg arg; 689 687 #ifdef CONFIG_TCP_MD5SIG 688 + const __u8 *md5_hash_location = NULL; 690 689 struct tcp_md5sig_key *key = NULL; 691 - const __u8 *hash_location = NULL; 692 690 unsigned char newhash[16]; 693 691 int genhash; 694 692 struct sock *sk1 = NULL; ··· 729 727 730 728 net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); 731 729 #ifdef CONFIG_TCP_MD5SIG 730 + /* Invalid TCP option size or twice included auth */ 731 + if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL)) 732 + return; 733 + 732 734 rcu_read_lock(); 733 - hash_location = tcp_parse_md5sig_option(th); 734 735 if (sk && sk_fullsock(sk)) { 735 736 const union tcp_md5_addr *addr; 736 737 int l3index; ··· 744 739 l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0; 745 740 addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; 746 741 key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); 747 - } else if (hash_location) { 742 + } else if (md5_hash_location) { 748 743 const union tcp_md5_addr *addr; 749 744 int sdif = tcp_v4_sdif(skb); 750 745 int dif = inet_iif(skb); ··· 776 771 777 772 778 773 genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); 779 - if (genhash || memcmp(hash_location, newhash, 16) != 0) 774 + if (genhash || memcmp(md5_hash_location, newhash, 16) != 0) 780 775 goto out; 781 776 782 777 }
+7 -4
net/ipv6/tcp_ipv6.c
··· 990 990 u32 seq = 0, ack_seq = 0; 991 991 struct tcp_md5sig_key *key = NULL; 992 992 #ifdef CONFIG_TCP_MD5SIG 993 - const __u8 *hash_location = NULL; 993 + const __u8 *md5_hash_location = NULL; 994 994 unsigned char newhash[16]; 995 995 int genhash; 996 996 struct sock *sk1 = NULL; ··· 1012 1012 1013 1013 net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); 1014 1014 #ifdef CONFIG_TCP_MD5SIG 1015 + /* Invalid TCP option size or twice included auth */ 1016 + if (tcp_parse_auth_options(th, &md5_hash_location, NULL)) 1017 + return; 1018 + 1015 1019 rcu_read_lock(); 1016 - hash_location = tcp_parse_md5sig_option(th); 1017 1020 if (sk && sk_fullsock(sk)) { 1018 1021 int l3index; 1019 1022 ··· 1025 1022 */ 1026 1023 l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; 1027 1024 key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index); 1028 - } else if (hash_location) { 1025 + } else if (md5_hash_location) { 1029 1026 int dif = tcp_v6_iif_l3_slave(skb); 1030 1027 int sdif = tcp_v6_sdif(skb); 1031 1028 int l3index; ··· 1054 1051 goto out; 1055 1052 1056 1053 genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); 1057 - if (genhash || memcmp(hash_location, newhash, 16) != 0) 1054 + if (genhash || memcmp(md5_hash_location, newhash, 16) != 0) 1058 1055 goto out; 1059 1056 } 1060 1057 #endif