1// SPDX-License-Identifier: GPL-2.0
2#include <linux/ceph/ceph_debug.h>
3
4#include <linux/inet.h>
5
6#include <linux/ceph/decode.h>
7#include <linux/ceph/messenger.h>  /* for ceph_pr_addr() */
8
9static int
10ceph_decode_entity_addr_versioned(void **p, void *end,
11				  struct ceph_entity_addr *addr)
12{
13	int ret;
14	u8 struct_v;
15	u32 struct_len, addr_len;
16	void *struct_end;
17
18	ret = ceph_start_decoding(p, end, 1, "entity_addr_t", &struct_v,
19				  &struct_len);
20	if (ret)
21		goto bad;
22
23	ret = -EINVAL;
24	struct_end = *p + struct_len;
25
26	ceph_decode_copy_safe(p, end, &addr->type, sizeof(addr->type), bad);
27
28	ceph_decode_copy_safe(p, end, &addr->nonce, sizeof(addr->nonce), bad);
29
30	ceph_decode_32_safe(p, end, addr_len, bad);
31	if (addr_len > sizeof(addr->in_addr))
32		goto bad;
33
34	memset(&addr->in_addr, 0, sizeof(addr->in_addr));
35	if (addr_len) {
36		ceph_decode_copy_safe(p, end, &addr->in_addr, addr_len, bad);
37
38		addr->in_addr.ss_family =
39			le16_to_cpu((__force __le16)addr->in_addr.ss_family);
40	}
41
42	/* Advance past anything the client doesn't yet understand */
43	*p = struct_end;
44	ret = 0;
45bad:
46	return ret;
47}
48
49static int
50ceph_decode_entity_addr_legacy(void **p, void *end,
51			       struct ceph_entity_addr *addr)
52{
53	int ret = -EINVAL;
54
55	/* Skip rest of type field */
56	ceph_decode_skip_n(p, end, 3, bad);
57
58	/*
59	 * Clients that don't support ADDR2 always send TYPE_NONE, change it
60	 * to TYPE_LEGACY for forward compatibility.
61	 */
62	addr->type = CEPH_ENTITY_ADDR_TYPE_LEGACY;
63	ceph_decode_copy_safe(p, end, &addr->nonce, sizeof(addr->nonce), bad);
64	memset(&addr->in_addr, 0, sizeof(addr->in_addr));
65	ceph_decode_copy_safe(p, end, &addr->in_addr,
66			      sizeof(addr->in_addr), bad);
67	addr->in_addr.ss_family =
68			be16_to_cpu((__force __be16)addr->in_addr.ss_family);
69	ret = 0;
70bad:
71	return ret;
72}
73
74int
75ceph_decode_entity_addr(void **p, void *end, struct ceph_entity_addr *addr)
76{
77	u8 marker;
78
79	ceph_decode_8_safe(p, end, marker, bad);
80	if (marker == 1)
81		return ceph_decode_entity_addr_versioned(p, end, addr);
82	else if (marker == 0)
83		return ceph_decode_entity_addr_legacy(p, end, addr);
84bad:
85	return -EINVAL;
86}
87EXPORT_SYMBOL(ceph_decode_entity_addr);
88
89/*
90 * Return addr of desired type (MSGR2 or LEGACY) or error.
91 * Make sure there is only one match.
92 *
93 * Assume encoding with MSG_ADDR2.
94 */
95int ceph_decode_entity_addrvec(void **p, void *end, bool msgr2,
96			       struct ceph_entity_addr *addr)
97{
98	__le32 my_type = msgr2 ? CEPH_ENTITY_ADDR_TYPE_MSGR2 :
99				 CEPH_ENTITY_ADDR_TYPE_LEGACY;
100	struct ceph_entity_addr tmp_addr;
101	int addr_cnt;
102	bool found;
103	u8 marker;
104	int ret;
105	int i;
106
107	ceph_decode_8_safe(p, end, marker, e_inval);
108	if (marker != 2) {
109		pr_err("bad addrvec marker %d\n", marker);
110		return -EINVAL;
111	}
112
113	ceph_decode_32_safe(p, end, addr_cnt, e_inval);
114	dout("%s addr_cnt %d\n", __func__, addr_cnt);
115
116	found = false;
117	for (i = 0; i < addr_cnt; i++) {
118		ret = ceph_decode_entity_addr(p, end, &tmp_addr);
119		if (ret)
120			return ret;
121
122		dout("%s i %d addr %s\n", __func__, i, ceph_pr_addr(&tmp_addr));
123		if (tmp_addr.type == my_type) {
124			if (found) {
125				pr_err("another match of type %d in addrvec\n",
126				       le32_to_cpu(my_type));
127				return -EINVAL;
128			}
129
130			memcpy(addr, &tmp_addr, sizeof(*addr));
131			found = true;
132		}
133	}
134
135	if (found)
136		return 0;
137
138	if (!addr_cnt)
139		return 0;  /* normal -- e.g. unused OSD id/slot */
140
141	if (addr_cnt == 1 && !memchr_inv(&tmp_addr, 0, sizeof(tmp_addr)))
142		return 0;  /* weird but effectively the same as !addr_cnt */
143
144	pr_err("no match of type %d in addrvec\n", le32_to_cpu(my_type));
145	return -ENOENT;
146
147e_inval:
148	return -EINVAL;
149}
150EXPORT_SYMBOL(ceph_decode_entity_addrvec);
151
152static int get_sockaddr_encoding_len(sa_family_t family)
153{
154	union {
155		struct sockaddr sa;
156		struct sockaddr_in sin;
157		struct sockaddr_in6 sin6;
158	} u;
159
160	switch (family) {
161	case AF_INET:
162		return sizeof(u.sin);
163	case AF_INET6:
164		return sizeof(u.sin6);
165	default:
166		return sizeof(u);
167	}
168}
169
170int ceph_entity_addr_encoding_len(const struct ceph_entity_addr *addr)
171{
172	sa_family_t family = get_unaligned(&addr->in_addr.ss_family);
173	int addr_len = get_sockaddr_encoding_len(family);
174
175	return 1 + CEPH_ENCODING_START_BLK_LEN + 4 + 4 + 4 + addr_len;
176}
177
178void ceph_encode_entity_addr(void **p, const struct ceph_entity_addr *addr)
179{
180	sa_family_t family = get_unaligned(&addr->in_addr.ss_family);
181	int addr_len = get_sockaddr_encoding_len(family);
182
183	ceph_encode_8(p, 1);  /* marker */
184	ceph_start_encoding(p, 1, 1, sizeof(addr->type) +
185				     sizeof(addr->nonce) +
186				     sizeof(u32) + addr_len);
187	ceph_encode_copy(p, &addr->type, sizeof(addr->type));
188	ceph_encode_copy(p, &addr->nonce, sizeof(addr->nonce));
189
190	ceph_encode_32(p, addr_len);
191	ceph_encode_16(p, family);
192	ceph_encode_copy(p, addr->in_addr.__data, addr_len - sizeof(family));
193}
194