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-AO getsockopt()s

Introduce getsockopt(TCP_AO_GET_KEYS) that lets a user get TCP-AO keys
and their properties from a socket. The user can provide a filter
to match the specific key to be dumped or ::get_all = 1 may be
used to dump all keys in one syscall.

Add another getsockopt(TCP_AO_INFO) for providing per-socket/per-ao_info
stats: packet counters, Current_key/RNext_key and flags like
::ao_required and ::accept_icmps.

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
ef84703a 7753c2f0

+369 -14
+12
include/net/tcp_ao.h
··· 194 194 void tcp_ao_destroy_sock(struct sock *sk, bool twsk); 195 195 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); 196 196 bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); 197 + int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); 198 + int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen); 197 199 enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, 198 200 const struct sk_buff *skb, unsigned short int family, 199 201 const struct request_sock *req, ··· 317 315 318 316 static inline void tcp_ao_connect_init(struct sock *sk) 319 317 { 318 + } 319 + 320 + static inline int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 321 + { 322 + return -ENOPROTOOPT; 323 + } 324 + 325 + static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 326 + { 327 + return -ENOPROTOOPT; 320 328 } 321 329 #endif 322 330
+49 -14
include/uapi/linux/tcp.h
··· 131 131 132 132 #define TCP_AO_ADD_KEY 38 /* Add/Set MKT */ 133 133 #define TCP_AO_DEL_KEY 39 /* Delete MKT */ 134 - #define TCP_AO_INFO 40 /* Modify TCP-AO per-socket options */ 134 + #define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ 135 + #define TCP_AO_GET_KEYS 41 /* List MKT(s) */ 135 136 136 137 #define TCP_REPAIR_ON 1 137 138 #define TCP_REPAIR_OFF 0 ··· 406 405 __u8 keyflags; /* see TCP_AO_KEYF_ */ 407 406 } __attribute__((aligned(8))); 408 407 409 - struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ 410 - __u32 set_current :1, /* corresponding ::current_key */ 411 - set_rnext :1, /* corresponding ::rnext */ 412 - ao_required :1, /* don't accept non-AO connects */ 413 - set_counters :1, /* set/clear ::pkt_* counters */ 414 - accept_icmps :1, /* accept incoming ICMPs */ 408 + struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO), getsockopt(TCP_AO_INFO) */ 409 + /* Here 'in' is for setsockopt(), 'out' is for getsockopt() */ 410 + __u32 set_current :1, /* in/out: corresponding ::current_key */ 411 + set_rnext :1, /* in/out: corresponding ::rnext */ 412 + ao_required :1, /* in/out: don't accept non-AO connects */ 413 + set_counters :1, /* in: set/clear ::pkt_* counters */ 414 + accept_icmps :1, /* in/out: accept incoming ICMPs */ 415 415 reserved :27; /* must be 0 */ 416 416 __u16 reserved2; /* padding, must be 0 */ 417 - __u8 current_key; /* KeyID to set as Current_key */ 418 - __u8 rnext; /* KeyID to set as Rnext_key */ 419 - __u64 pkt_good; /* verified segments */ 420 - __u64 pkt_bad; /* failed verification */ 421 - __u64 pkt_key_not_found; /* could not find a key to verify */ 422 - __u64 pkt_ao_required; /* segments missing TCP-AO sign */ 423 - __u64 pkt_dropped_icmp; /* ICMPs that were ignored */ 417 + __u8 current_key; /* in/out: KeyID of Current_key */ 418 + __u8 rnext; /* in/out: keyid of RNext_key */ 419 + __u64 pkt_good; /* in/out: verified segments */ 420 + __u64 pkt_bad; /* in/out: failed verification */ 421 + __u64 pkt_key_not_found; /* in/out: could not find a key to verify */ 422 + __u64 pkt_ao_required; /* in/out: segments missing TCP-AO sign */ 423 + __u64 pkt_dropped_icmp; /* in/out: ICMPs that were ignored */ 424 + } __attribute__((aligned(8))); 425 + 426 + struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */ 427 + struct __kernel_sockaddr_storage addr; /* in/out: dump keys for peer 428 + * with this address/prefix 429 + */ 430 + char alg_name[64]; /* out: crypto hash algorithm */ 431 + __u8 key[TCP_AO_MAXKEYLEN]; 432 + __u32 nkeys; /* in: size of the userspace buffer 433 + * @optval, measured in @optlen - the 434 + * sizeof(struct tcp_ao_getsockopt) 435 + * out: number of keys that matched 436 + */ 437 + __u16 is_current :1, /* in: match and dump Current_key, 438 + * out: the dumped key is Current_key 439 + */ 440 + 441 + is_rnext :1, /* in: match and dump RNext_key, 442 + * out: the dumped key is RNext_key 443 + */ 444 + get_all :1, /* in: dump all keys */ 445 + reserved :13; /* padding, must be 0 */ 446 + __u8 sndid; /* in/out: dump keys with SendID */ 447 + __u8 rcvid; /* in/out: dump keys with RecvID */ 448 + __u8 prefix; /* in/out: dump keys with address/prefix */ 449 + __u8 maclen; /* out: key's length of authentication 450 + * code (hash) 451 + */ 452 + __u8 keyflags; /* in/out: see TCP_AO_KEYF_ */ 453 + __u8 keylen; /* out: length of ::key */ 454 + __s32 ifindex; /* in/out: L3 dev index for VRF */ 455 + __u64 pkt_good; /* out: verified segments */ 456 + __u64 pkt_bad; /* out: segments that failed verification */ 424 457 } __attribute__((aligned(8))); 425 458 426 459 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
+13
net/ipv4/tcp.c
··· 4284 4284 return err; 4285 4285 } 4286 4286 #endif 4287 + case TCP_AO_GET_KEYS: 4288 + case TCP_AO_INFO: { 4289 + int err; 4290 + 4291 + sockopt_lock_sock(sk); 4292 + if (optname == TCP_AO_GET_KEYS) 4293 + err = tcp_ao_get_mkts(sk, optval, optlen); 4294 + else 4295 + err = tcp_ao_get_sock_info(sk, optval, optlen); 4296 + sockopt_release_sock(sk); 4297 + 4298 + return err; 4299 + } 4287 4300 default: 4288 4301 return -ENOPROTOOPT; 4289 4302 }
+295
net/ipv4/tcp_ao.c
··· 1894 1894 return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 1895 1895 } 1896 1896 1897 + /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 1898 + * 1899 + * @ao_info: struct tcp_ao_info on the socket that 1900 + * socket getsockopt(TCP_AO_GET_KEYS) is executed on 1901 + * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 1902 + * Must be != NULL. 1903 + * @optlen: pointer to size of tcp_ao_getsockopt structure. 1904 + * Must be != NULL. 1905 + * 1906 + * Return value: 0 on success, a negative error number otherwise. 1907 + * 1908 + * optval points to an array of tcp_ao_getsockopt structures in user space. 1909 + * optval[0] is used as both input and output to getsockopt. It determines 1910 + * which keys are returned by the kernel. 1911 + * optval[0].nkeys is the size of the array in user space. On return it contains 1912 + * the number of keys matching the search criteria. 1913 + * If tcp_ao_getsockopt::get_all is set, then all keys in the socket are 1914 + * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 1915 + * in optval[0] are returned. 1916 + * optlen is also used as both input and output. The user provides the size 1917 + * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 1918 + * of the structure in kernel space. 1919 + * The size of struct tcp_ao_getsockopt may differ between user and kernel. 1920 + * There are three cases to consider: 1921 + * * If usize == ksize, then keys are copied verbatim. 1922 + * * If usize < ksize, then the userspace has passed an old struct to a 1923 + * newer kernel. The rest of the trailing bytes in optval[0] 1924 + * (ksize - usize) are interpreted as 0 by the kernel. 1925 + * * If usize > ksize, then the userspace has passed a new struct to an 1926 + * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 1927 + * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 1928 + * On return the kernel fills in min(usize, ksize) in each entry of the array. 1929 + * The layout of the fields in the user and kernel structures is expected to 1930 + * be the same (including in the 32bit vs 64bit case). 1931 + */ 1932 + static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 1933 + sockptr_t optval, sockptr_t optlen) 1934 + { 1935 + struct tcp_ao_getsockopt opt_in, opt_out; 1936 + struct tcp_ao_key *key, *current_key; 1937 + bool do_address_matching = true; 1938 + union tcp_ao_addr *addr = NULL; 1939 + unsigned int max_keys; /* maximum number of keys to copy to user */ 1940 + size_t out_offset = 0; 1941 + size_t bytes_to_write; /* number of bytes to write to user level */ 1942 + int err, user_len; 1943 + u32 matched_keys; /* keys from ao_info matched so far */ 1944 + int optlen_out; 1945 + __be16 port = 0; 1946 + 1947 + if (copy_from_sockptr(&user_len, optlen, sizeof(int))) 1948 + return -EFAULT; 1949 + 1950 + if (user_len <= 0) 1951 + return -EINVAL; 1952 + 1953 + memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 1954 + err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), 1955 + optval, user_len); 1956 + if (err < 0) 1957 + return err; 1958 + 1959 + if (opt_in.pkt_good || opt_in.pkt_bad) 1960 + return -EINVAL; 1961 + 1962 + if (opt_in.reserved != 0) 1963 + return -EINVAL; 1964 + 1965 + max_keys = opt_in.nkeys; 1966 + 1967 + if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { 1968 + if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) 1969 + return -EINVAL; 1970 + do_address_matching = false; 1971 + } 1972 + 1973 + switch (opt_in.addr.ss_family) { 1974 + case AF_INET: { 1975 + struct sockaddr_in *sin; 1976 + __be32 mask; 1977 + 1978 + sin = (struct sockaddr_in *)&opt_in.addr; 1979 + port = sin->sin_port; 1980 + addr = (union tcp_ao_addr *)&sin->sin_addr; 1981 + 1982 + if (opt_in.prefix > 32) 1983 + return -EINVAL; 1984 + 1985 + if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY && 1986 + opt_in.prefix != 0) 1987 + return -EINVAL; 1988 + 1989 + mask = inet_make_mask(opt_in.prefix); 1990 + if (sin->sin_addr.s_addr & ~mask) 1991 + return -EINVAL; 1992 + 1993 + break; 1994 + } 1995 + case AF_INET6: { 1996 + struct sockaddr_in6 *sin6; 1997 + struct in6_addr *addr6; 1998 + 1999 + sin6 = (struct sockaddr_in6 *)&opt_in.addr; 2000 + addr = (union tcp_ao_addr *)&sin6->sin6_addr; 2001 + addr6 = &sin6->sin6_addr; 2002 + port = sin6->sin6_port; 2003 + 2004 + /* We don't have to change family and @addr here if 2005 + * ipv6_addr_v4mapped() like in key adding: 2006 + * tcp_ao_key_cmp() does it. Do the sanity checks though. 2007 + */ 2008 + if (opt_in.prefix != 0) { 2009 + if (ipv6_addr_v4mapped(addr6)) { 2010 + __be32 mask, addr4 = addr6->s6_addr32[3]; 2011 + 2012 + if (opt_in.prefix > 32 || 2013 + ntohl(addr4) == INADDR_ANY) 2014 + return -EINVAL; 2015 + mask = inet_make_mask(opt_in.prefix); 2016 + if (addr4 & ~mask) 2017 + return -EINVAL; 2018 + } else { 2019 + struct in6_addr pfx; 2020 + 2021 + if (ipv6_addr_any(addr6) || 2022 + opt_in.prefix > 128) 2023 + return -EINVAL; 2024 + 2025 + ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); 2026 + if (ipv6_addr_cmp(&pfx, addr6)) 2027 + return -EINVAL; 2028 + } 2029 + } else if (!ipv6_addr_any(addr6)) { 2030 + return -EINVAL; 2031 + } 2032 + break; 2033 + } 2034 + case 0: 2035 + if (!do_address_matching) 2036 + break; 2037 + fallthrough; 2038 + default: 2039 + return -EAFNOSUPPORT; 2040 + } 2041 + 2042 + if (!do_address_matching) { 2043 + /* We could just ignore those, but let's do stricter checks */ 2044 + if (addr || port) 2045 + return -EINVAL; 2046 + if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) 2047 + return -EINVAL; 2048 + } 2049 + 2050 + bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); 2051 + matched_keys = 0; 2052 + /* May change in RX, while we're dumping, pre-fetch it */ 2053 + current_key = READ_ONCE(ao_info->current_key); 2054 + 2055 + hlist_for_each_entry_rcu(key, &ao_info->head, node) { 2056 + if (opt_in.get_all) 2057 + goto match; 2058 + 2059 + if (opt_in.is_current || opt_in.is_rnext) { 2060 + if (opt_in.is_current && key == current_key) 2061 + goto match; 2062 + if (opt_in.is_rnext && key == ao_info->rnext_key) 2063 + goto match; 2064 + continue; 2065 + } 2066 + 2067 + if (tcp_ao_key_cmp(key, addr, opt_in.prefix, 2068 + opt_in.addr.ss_family, 2069 + opt_in.sndid, opt_in.rcvid) != 0) 2070 + continue; 2071 + match: 2072 + matched_keys++; 2073 + if (matched_keys > max_keys) 2074 + continue; 2075 + 2076 + memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 2077 + 2078 + if (key->family == AF_INET) { 2079 + struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 2080 + 2081 + sin_out->sin_family = key->family; 2082 + sin_out->sin_port = 0; 2083 + memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 2084 + } else { 2085 + struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 2086 + 2087 + sin6_out->sin6_family = key->family; 2088 + sin6_out->sin6_port = 0; 2089 + memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 2090 + } 2091 + opt_out.sndid = key->sndid; 2092 + opt_out.rcvid = key->rcvid; 2093 + opt_out.prefix = key->prefixlen; 2094 + opt_out.keyflags = key->keyflags; 2095 + opt_out.is_current = (key == current_key); 2096 + opt_out.is_rnext = (key == ao_info->rnext_key); 2097 + opt_out.nkeys = 0; 2098 + opt_out.maclen = key->maclen; 2099 + opt_out.keylen = key->keylen; 2100 + opt_out.pkt_good = atomic64_read(&key->pkt_good); 2101 + opt_out.pkt_bad = atomic64_read(&key->pkt_bad); 2102 + memcpy(&opt_out.key, key->key, key->keylen); 2103 + tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); 2104 + 2105 + /* Copy key to user */ 2106 + if (copy_to_sockptr_offset(optval, out_offset, 2107 + &opt_out, bytes_to_write)) 2108 + return -EFAULT; 2109 + out_offset += user_len; 2110 + } 2111 + 2112 + optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 2113 + if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) 2114 + return -EFAULT; 2115 + 2116 + out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); 2117 + if (copy_to_sockptr_offset(optval, out_offset, 2118 + &matched_keys, sizeof(u32))) 2119 + return -EFAULT; 2120 + 2121 + return 0; 2122 + } 2123 + 2124 + int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2125 + { 2126 + struct tcp_ao_info *ao_info; 2127 + 2128 + ao_info = setsockopt_ao_info(sk); 2129 + if (IS_ERR(ao_info)) 2130 + return PTR_ERR(ao_info); 2131 + if (!ao_info) 2132 + return -ENOENT; 2133 + 2134 + return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); 2135 + } 2136 + 2137 + int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2138 + { 2139 + struct tcp_ao_info_opt out, in = {}; 2140 + struct tcp_ao_key *current_key; 2141 + struct tcp_ao_info *ao; 2142 + int err, len; 2143 + 2144 + if (copy_from_sockptr(&len, optlen, sizeof(int))) 2145 + return -EFAULT; 2146 + 2147 + if (len <= 0) 2148 + return -EINVAL; 2149 + 2150 + /* Copying this "in" only to check ::reserved, ::reserved2, 2151 + * that may be needed to extend (struct tcp_ao_info_opt) and 2152 + * what getsockopt() provides in future. 2153 + */ 2154 + err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); 2155 + if (err) 2156 + return err; 2157 + 2158 + if (in.reserved != 0 || in.reserved2 != 0) 2159 + return -EINVAL; 2160 + 2161 + ao = setsockopt_ao_info(sk); 2162 + if (IS_ERR(ao)) 2163 + return PTR_ERR(ao); 2164 + if (!ao) 2165 + return -ENOENT; 2166 + 2167 + memset(&out, 0, sizeof(out)); 2168 + out.ao_required = ao->ao_required; 2169 + out.accept_icmps = ao->accept_icmps; 2170 + out.pkt_good = atomic64_read(&ao->counters.pkt_good); 2171 + out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); 2172 + out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); 2173 + out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); 2174 + out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); 2175 + 2176 + current_key = READ_ONCE(ao->current_key); 2177 + if (current_key) { 2178 + out.set_current = 1; 2179 + out.current_key = current_key->sndid; 2180 + } 2181 + if (ao->rnext_key) { 2182 + out.set_rnext = 1; 2183 + out.rnext = ao->rnext_key->rcvid; 2184 + } 2185 + 2186 + if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) 2187 + return -EFAULT; 2188 + 2189 + return 0; 2190 + } 2191 +