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 | |
9 | static int |
10 | ceph_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, v: 1, name: "entity_addr_t" , struct_v: &struct_v, |
19 | struct_len: &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; |
45 | bad: |
46 | return ret; |
47 | } |
48 | |
49 | static int |
50 | ceph_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; |
70 | bad: |
71 | return ret; |
72 | } |
73 | |
74 | int |
75 | ceph_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); |
84 | bad: |
85 | return -EINVAL; |
86 | } |
87 | EXPORT_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 | */ |
95 | int 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(p: &tmp_addr, c: 0, size: 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 | |
147 | e_inval: |
148 | return -EINVAL; |
149 | } |
150 | EXPORT_SYMBOL(ceph_decode_entity_addrvec); |
151 | |
152 | static 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 | |
170 | int 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 | |
178 | void 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, v: 1); /* marker */ |
184 | ceph_start_encoding(p, struct_v: 1, struct_compat: 1, struct_len: sizeof(addr->type) + |
185 | sizeof(addr->nonce) + |
186 | sizeof(u32) + addr_len); |
187 | ceph_encode_copy(p, s: &addr->type, len: sizeof(addr->type)); |
188 | ceph_encode_copy(p, s: &addr->nonce, len: sizeof(addr->nonce)); |
189 | |
190 | ceph_encode_32(p, v: addr_len); |
191 | ceph_encode_16(p, v: family); |
192 | ceph_encode_copy(p, s: addr->in_addr.__data, len: addr_len - sizeof(family)); |
193 | } |
194 | |