Message ID | 20220923201319.493208-24-dima@arista.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | net/tcp: Add TCP-AO support | expand |
Context | Check | Description |
---|---|---|
netdev/tree_selection | success | Guessing tree name failed - patch did not apply, async |
Hi Dmitry, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on bf682942cd26ce9cd5e87f73ae099b383041e782] url: https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220924-042311 base: bf682942cd26ce9cd5e87f73ae099b383041e782 config: um-x86_64_defconfig compiler: gcc-11 (Debian 11.3.0-5) 11.3.0 reproduce (this is a W=1 build): # https://github.com/intel-lab-lkp/linux/commit/464de48068a05d5b8690ca346f382b74914e5ce3 git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220924-042311 git checkout 464de48068a05d5b8690ca346f382b74914e5ce3 # save the config file mkdir build_dir && cp config build_dir/.config make W=1 O=build_dir ARCH=um SUBARCH=x86_64 SHELL=/bin/bash net/ipv4/ If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): net/ipv4/tcp_ao.c:113:20: warning: no previous prototype for 'tcp_ao_do_lookup_keyid' [-Wmissing-prototypes] 113 | struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao, | ^~~~~~~~~~~~~~~~~~~~~~ net/ipv4/tcp_ao.c:270:20: warning: no previous prototype for 'tcp_ao_copy_key' [-Wmissing-prototypes] 270 | struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, struct tcp_ao_key *key) | ^~~~~~~~~~~~~~~ >> net/ipv4/tcp_ao.c:1778:5: warning: no previous prototype for 'tcp_ao_copy_mkts_to_user' [-Wmissing-prototypes] 1778 | int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, | ^~~~~~~~~~~~~~~~~~~~~~~~ vim +/tcp_ao_copy_mkts_to_user +1778 net/ipv4/tcp_ao.c 1742 1743 /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 1744 * 1745 * @ao_info: struct tcp_ao_info on the socket that 1746 * socket getsockopt(TCP_AO_GET) is executed on 1747 * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 1748 * Must be != NULL. 1749 * @optlen: pointer to size of tcp_ao_getsockopt structure. 1750 * Must be != NULL. 1751 * 1752 * Return value: 0 on success, a negative error number otherwise. 1753 * 1754 * optval points to an array of tcp_ao_getsockopt structures in user space. 1755 * optval[0] is used as both input and output to getsockopt. It determines 1756 * which keys are returned by the kernel. 1757 * optval[0].nkeys is the size of the array in user space. On return it contains 1758 * the number of keys matching the search criteria. 1759 * If TCP_AO_GET_ALL is set in "flags", then all keys in the socket are 1760 * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 1761 * in optval[0] are returned. 1762 * optlen is also used as both input and output. The user provides the size 1763 * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 1764 * of the structure in kernel space. 1765 * The size of struct tcp_ao_getsockopt may differ between user and kernel. 1766 * There are three cases to consider: 1767 * * If usize == ksize, then keys are copied verbatim. 1768 * * If usize < ksize, then the userspace has passed an old struct to a 1769 * newer kernel. The rest of the trailing bytes in optval[0] 1770 * (ksize - usize) are interpreted as 0 by the kernel. 1771 * * If usize > ksize, then the userspace has passed a new struct to an 1772 * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 1773 * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 1774 * On return the kernel fills in min(usize, ksize) in each entry of the array. 1775 * The layout of the fields in the user and kernel structures is expected to 1776 * be the same (including in the 32bit vs 64bit case). 1777 */ > 1778 int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 1779 char __user *optval, int __user *optlen) 1780 { 1781 struct tcp_ao_getsockopt opt_in; 1782 struct tcp_ao_getsockopt opt_out; 1783 struct tcp_ao_getsockopt __user *optval_in; 1784 int user_len; 1785 unsigned int max_keys; /* maximum number of keys to copy to user */ 1786 u32 copied_keys; /* keys copied to user so far */ 1787 int matched_keys; /* keys from ao_info matched so far */ 1788 int bytes_to_write; /* number of bytes to write to user level */ 1789 struct tcp_ao_key *key; 1790 struct sockaddr_in *sin; /* (struct sockaddr_in *)&opt_in.addr */ 1791 struct sockaddr_in6 *sin6; /* (struct sockaddr_in6 *)&opt_in.addr */ 1792 struct in6_addr *addr6; /* &sin6->sin6_addr */ 1793 __kernel_sa_family_t ss_family; 1794 union tcp_ao_addr *addr; 1795 int optlen_out; 1796 u8 prefix_in; 1797 u16 port = 0; 1798 bool copy_all, copy_current, copy_next; 1799 int err; 1800 1801 if (get_user(user_len, optlen)) 1802 return -EFAULT; 1803 1804 if (user_len <= 0) 1805 return -EINVAL; 1806 1807 memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 1808 err = copy_struct_from_user(&opt_in, sizeof(struct tcp_ao_getsockopt), 1809 optval, user_len); 1810 if (err < 0) 1811 return err; 1812 1813 optval_in = (struct tcp_ao_getsockopt __user *)optval; 1814 ss_family = opt_in.addr.ss_family; 1815 1816 BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); 1817 if (opt_in.flags & ~TCP_AO_GETF_VALID) 1818 return -EINVAL; 1819 1820 max_keys = opt_in.nkeys; 1821 copy_all = !!(opt_in.flags & TCP_AO_GET_ALL); 1822 copy_current = !!(opt_in.flags & TCP_AO_GET_CURR); 1823 copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT); 1824 1825 if (!(copy_all || copy_current || copy_next)) { 1826 prefix_in = opt_in.prefix; 1827 1828 switch (ss_family) { 1829 case AF_INET: { 1830 sin = (struct sockaddr_in *)&opt_in.addr; 1831 port = sin->sin_port; 1832 addr = (union tcp_ao_addr *)&sin->sin_addr; 1833 1834 if (prefix_in > 32) 1835 return -EINVAL; 1836 1837 if (sin->sin_addr.s_addr == INADDR_ANY && 1838 prefix_in != 0) 1839 return -EINVAL; 1840 1841 break; 1842 } 1843 case AF_INET6: { 1844 sin6 = (struct sockaddr_in6 *)&opt_in.addr; 1845 addr = (union tcp_ao_addr *)&sin6->sin6_addr; 1846 addr6 = &sin6->sin6_addr; 1847 port = sin6->sin6_port; 1848 1849 if (prefix_in != 0) { 1850 if (ipv6_addr_v4mapped(addr6)) { 1851 __be32 addr4 = addr6->s6_addr32[3]; 1852 1853 if (prefix_in > 32 || 1854 addr4 == INADDR_ANY) 1855 return -EINVAL; 1856 } else { 1857 if (ipv6_addr_any(addr6) || 1858 prefix_in > 128) 1859 return -EINVAL; 1860 } 1861 } else if (!ipv6_addr_any(addr6)) { 1862 return -EINVAL; 1863 } 1864 1865 break; 1866 } 1867 default: 1868 return -EINVAL; 1869 } 1870 } 1871 1872 bytes_to_write = min(user_len, (int)sizeof(struct tcp_ao_getsockopt)); 1873 copied_keys = 0; 1874 matched_keys = 0; 1875 1876 hlist_for_each_entry_rcu(key, &ao_info->head, node) { 1877 if (copy_all) 1878 goto match; 1879 1880 if (copy_current || copy_next) { 1881 if (copy_current && key == ao_info->current_key) 1882 goto match; 1883 if (copy_next && key == ao_info->rnext_key) 1884 goto match; 1885 continue; 1886 } 1887 1888 if (tcp_ao_key_cmp(key, addr, opt_in.prefix, 1889 opt_in.addr.ss_family, 1890 opt_in.sndid, opt_in.rcvid, port) != 0) 1891 continue; 1892 match: 1893 matched_keys++; 1894 if (copied_keys >= max_keys) 1895 continue; 1896 1897 memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 1898 1899 if (key->family == AF_INET) { 1900 struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 1901 1902 sin_out->sin_family = key->family; 1903 sin_out->sin_port = ntohs(key->port); 1904 memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 1905 } else { 1906 struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 1907 1908 sin6_out->sin6_family = key->family; 1909 sin6_out->sin6_port = ntohs(key->port); 1910 memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 1911 } 1912 opt_out.sndid = key->sndid; 1913 opt_out.rcvid = key->rcvid; 1914 opt_out.prefix = key->prefixlen; 1915 opt_out.keyflags = key->keyflags; 1916 opt_out.flags = 0; 1917 if (key == ao_info->current_key) 1918 opt_out.flags |= TCP_AO_GET_CURR; 1919 if (key == ao_info->rnext_key) 1920 opt_out.flags |= TCP_AO_GET_NEXT; 1921 opt_out.nkeys = 0; 1922 opt_out.maclen = key->maclen; 1923 opt_out.keylen = key->keylen; 1924 memcpy(&opt_out.key, key->key, key->keylen); 1925 crypto_pool_algo(key->crypto_pool_id, opt_out.alg_name, 64); 1926 1927 /* Copy key to user */ 1928 if (copy_to_user(optval, &opt_out, bytes_to_write)) 1929 return -EFAULT; 1930 optval += user_len; 1931 copied_keys++; 1932 } 1933 1934 optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 1935 if (copy_to_user(optlen, &optlen_out, sizeof(int))) 1936 return -EFAULT; 1937 1938 if (copy_to_user(&optval_in->nkeys, &matched_keys, sizeof(u32))) 1939 return -EFAULT; 1940 1941 return 0; 1942 } 1943
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 743a910ba508..b5088d4c5587 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -174,6 +174,7 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code); +int tcp_ao_get_mkts(struct sock *sk, char __user *optval, int __user *optlen); enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index b60933ee2a27..453187d21da8 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -132,6 +132,7 @@ enum { #define TCP_AO 38 /* (Add/Set MKT) */ #define TCP_AO_DEL 39 /* (Delete MKT) */ #define TCP_AO_MOD 40 /* (Modify MKT) */ +#define TCP_AO_GET 41 /* (Get MKTs) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -353,6 +354,10 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ +#define TCP_AO_GET_CURR TCP_AO_CMDF_CURR +#define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT +#define TCP_AO_GET_ALL (1 << 2) + struct tcp_ao { /* setsockopt(TCP_AO) */ struct __kernel_sockaddr_storage tcpa_addr; char tcpa_alg_name[64]; @@ -382,6 +387,20 @@ struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */ __u8 tcpa_rnext; } __attribute__((aligned(8))); +struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET) */ + struct __kernel_sockaddr_storage addr; + __u8 sndid; + __u8 rcvid; + __u32 nkeys; + char alg_name[64]; + __u16 flags; + __u8 prefix; + __u8 maclen; + __u8 keyflags; + __u8 keylen; + __u8 key[TCP_AO_MAXKEYLEN]; +} __attribute__((aligned(8))); + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index f7ad4443c350..edc27b0d7cb2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4382,6 +4382,17 @@ static int do_tcp_getsockopt(struct sock *sk, int level, err = -EFAULT; return err; } +#endif +#ifdef CONFIG_TCP_AO + case TCP_AO_GET: { + int err; + + lock_sock(sk); + err = tcp_ao_get_mkts(sk, optval, optlen); + release_sock(sk); + + return err; + } #endif default: return -ENOPROTOOPT; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index f5489b73fae0..8f569f43e9c2 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1462,6 +1462,8 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) +#define TCP_AO_GETF_VALID \ + (TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT) static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) @@ -1738,3 +1740,224 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); } +/* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) + * + * @ao_info: struct tcp_ao_info on the socket that + * socket getsockopt(TCP_AO_GET) is executed on + * @optval: pointer to array of tcp_ao_getsockopt structures in user space. + * Must be != NULL. + * @optlen: pointer to size of tcp_ao_getsockopt structure. + * Must be != NULL. + * + * Return value: 0 on success, a negative error number otherwise. + * + * optval points to an array of tcp_ao_getsockopt structures in user space. + * optval[0] is used as both input and output to getsockopt. It determines + * which keys are returned by the kernel. + * optval[0].nkeys is the size of the array in user space. On return it contains + * the number of keys matching the search criteria. + * If TCP_AO_GET_ALL is set in "flags", then all keys in the socket are + * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> + * in optval[0] are returned. + * optlen is also used as both input and output. The user provides the size + * of struct tcp_ao_getsockopt in user space, and the kernel returns the size + * of the structure in kernel space. + * The size of struct tcp_ao_getsockopt may differ between user and kernel. + * There are three cases to consider: + * * If usize == ksize, then keys are copied verbatim. + * * If usize < ksize, then the userspace has passed an old struct to a + * newer kernel. The rest of the trailing bytes in optval[0] + * (ksize - usize) are interpreted as 0 by the kernel. + * * If usize > ksize, then the userspace has passed a new struct to an + * older kernel. The trailing bytes unknown to the kernel (usize - ksize) + * are checked to ensure they are zeroed, otherwise -E2BIG is returned. + * On return the kernel fills in min(usize, ksize) in each entry of the array. + * The layout of the fields in the user and kernel structures is expected to + * be the same (including in the 32bit vs 64bit case). + */ +int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, + char __user *optval, int __user *optlen) +{ + struct tcp_ao_getsockopt opt_in; + struct tcp_ao_getsockopt opt_out; + struct tcp_ao_getsockopt __user *optval_in; + int user_len; + unsigned int max_keys; /* maximum number of keys to copy to user */ + u32 copied_keys; /* keys copied to user so far */ + int matched_keys; /* keys from ao_info matched so far */ + int bytes_to_write; /* number of bytes to write to user level */ + struct tcp_ao_key *key; + struct sockaddr_in *sin; /* (struct sockaddr_in *)&opt_in.addr */ + struct sockaddr_in6 *sin6; /* (struct sockaddr_in6 *)&opt_in.addr */ + struct in6_addr *addr6; /* &sin6->sin6_addr */ + __kernel_sa_family_t ss_family; + union tcp_ao_addr *addr; + int optlen_out; + u8 prefix_in; + u16 port = 0; + bool copy_all, copy_current, copy_next; + int err; + + if (get_user(user_len, optlen)) + return -EFAULT; + + if (user_len <= 0) + return -EINVAL; + + memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); + err = copy_struct_from_user(&opt_in, sizeof(struct tcp_ao_getsockopt), + optval, user_len); + if (err < 0) + return err; + + optval_in = (struct tcp_ao_getsockopt __user *)optval; + ss_family = opt_in.addr.ss_family; + + BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); + if (opt_in.flags & ~TCP_AO_GETF_VALID) + return -EINVAL; + + max_keys = opt_in.nkeys; + copy_all = !!(opt_in.flags & TCP_AO_GET_ALL); + copy_current = !!(opt_in.flags & TCP_AO_GET_CURR); + copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT); + + if (!(copy_all || copy_current || copy_next)) { + prefix_in = opt_in.prefix; + + switch (ss_family) { + case AF_INET: { + sin = (struct sockaddr_in *)&opt_in.addr; + port = sin->sin_port; + addr = (union tcp_ao_addr *)&sin->sin_addr; + + if (prefix_in > 32) + return -EINVAL; + + if (sin->sin_addr.s_addr == INADDR_ANY && + prefix_in != 0) + return -EINVAL; + + break; + } + case AF_INET6: { + sin6 = (struct sockaddr_in6 *)&opt_in.addr; + addr = (union tcp_ao_addr *)&sin6->sin6_addr; + addr6 = &sin6->sin6_addr; + port = sin6->sin6_port; + + if (prefix_in != 0) { + if (ipv6_addr_v4mapped(addr6)) { + __be32 addr4 = addr6->s6_addr32[3]; + + if (prefix_in > 32 || + addr4 == INADDR_ANY) + return -EINVAL; + } else { + if (ipv6_addr_any(addr6) || + prefix_in > 128) + return -EINVAL; + } + } else if (!ipv6_addr_any(addr6)) { + return -EINVAL; + } + + break; + } + default: + return -EINVAL; + } + } + + bytes_to_write = min(user_len, (int)sizeof(struct tcp_ao_getsockopt)); + copied_keys = 0; + matched_keys = 0; + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (copy_all) + goto match; + + if (copy_current || copy_next) { + if (copy_current && key == ao_info->current_key) + goto match; + if (copy_next && key == ao_info->rnext_key) + goto match; + continue; + } + + if (tcp_ao_key_cmp(key, addr, opt_in.prefix, + opt_in.addr.ss_family, + opt_in.sndid, opt_in.rcvid, port) != 0) + continue; +match: + matched_keys++; + if (copied_keys >= max_keys) + continue; + + memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); + + if (key->family == AF_INET) { + struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; + + sin_out->sin_family = key->family; + sin_out->sin_port = ntohs(key->port); + memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); + } else { + struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; + + sin6_out->sin6_family = key->family; + sin6_out->sin6_port = ntohs(key->port); + memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); + } + opt_out.sndid = key->sndid; + opt_out.rcvid = key->rcvid; + opt_out.prefix = key->prefixlen; + opt_out.keyflags = key->keyflags; + opt_out.flags = 0; + if (key == ao_info->current_key) + opt_out.flags |= TCP_AO_GET_CURR; + if (key == ao_info->rnext_key) + opt_out.flags |= TCP_AO_GET_NEXT; + opt_out.nkeys = 0; + opt_out.maclen = key->maclen; + opt_out.keylen = key->keylen; + memcpy(&opt_out.key, key->key, key->keylen); + crypto_pool_algo(key->crypto_pool_id, opt_out.alg_name, 64); + + /* Copy key to user */ + if (copy_to_user(optval, &opt_out, bytes_to_write)) + return -EFAULT; + optval += user_len; + copied_keys++; + } + + optlen_out = (int)sizeof(struct tcp_ao_getsockopt); + if (copy_to_user(optlen, &optlen_out, sizeof(int))) + return -EFAULT; + + if (copy_to_user(&optval_in->nkeys, &matched_keys, sizeof(u32))) + return -EFAULT; + + return 0; +} + +int tcp_ao_get_mkts(struct sock *sk, char __user *optval, int __user *optlen) +{ + struct tcp_ao_info *ao_info; + u32 state; + + /* Check socket state */ + state = (1 << sk->sk_state) & + (TCPF_CLOSE | TCPF_ESTABLISHED | TCPF_LISTEN); + if (!state) + return -ESOCKTNOSUPPORT; + + /* Check ao_info */ + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + + return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); +} +