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