1 /*
2 * virportallocator.c: Allocate & track TCP port allocations
3 *
4 * Copyright (C) 2013-2014 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <config.h>
23
24 #include <unistd.h>
25
26 #include "virsocket.h"
27 #include "viralloc.h"
28 #include "virbitmap.h"
29 #include "virportallocator.h"
30 #include "virthread.h"
31 #include "virerror.h"
32 #include "virfile.h"
33 #include "virstring.h"
34 #include "virutil.h"
35
36 #define VIR_FROM_THIS VIR_FROM_NONE
37
38 #define VIR_PORT_ALLOCATOR_NUM_PORTS 65536
39
40 typedef struct _virPortAllocator virPortAllocator;
41 struct _virPortAllocator {
42 virObjectLockable parent;
43 virBitmap *bitmap;
44 };
45
46 struct _virPortAllocatorRange {
47 char *name;
48
49 unsigned short start;
50 unsigned short end;
51 };
52
53 static virClass *virPortAllocatorClass;
54 static virPortAllocator *virPortAllocatorInstance;
55
56 static void
virPortAllocatorDispose(void * obj)57 virPortAllocatorDispose(void *obj)
58 {
59 virPortAllocator *pa = obj;
60
61 virBitmapFree(pa->bitmap);
62 }
63
64 static virPortAllocator *
virPortAllocatorNew(void)65 virPortAllocatorNew(void)
66 {
67 virPortAllocator *pa;
68
69 if (!(pa = virObjectLockableNew(virPortAllocatorClass)))
70 return NULL;
71
72 pa->bitmap = virBitmapNew(VIR_PORT_ALLOCATOR_NUM_PORTS);
73
74 return pa;
75 }
76
77 static int
virPortAllocatorOnceInit(void)78 virPortAllocatorOnceInit(void)
79 {
80 if (!VIR_CLASS_NEW(virPortAllocator, virClassForObjectLockable()))
81 return -1;
82
83 if (!(virPortAllocatorInstance = virPortAllocatorNew()))
84 return -1;
85
86 return 0;
87 }
88
89 VIR_ONCE_GLOBAL_INIT(virPortAllocator);
90
91 virPortAllocatorRange *
virPortAllocatorRangeNew(const char * name,unsigned short start,unsigned short end)92 virPortAllocatorRangeNew(const char *name,
93 unsigned short start,
94 unsigned short end)
95 {
96 virPortAllocatorRange *range;
97
98 if (start >= end) {
99 virReportInvalidArg(start, "start port %d must be less than end port %d",
100 start, end);
101 return NULL;
102 }
103
104 range = g_new0(virPortAllocatorRange, 1);
105
106 range->start = start;
107 range->end = end;
108 range->name = g_strdup(name);
109
110 return range;
111 }
112
113 void
virPortAllocatorRangeFree(virPortAllocatorRange * range)114 virPortAllocatorRangeFree(virPortAllocatorRange *range)
115 {
116 if (!range)
117 return;
118
119 g_free(range->name);
120 g_free(range);
121 }
122
123 static int
virPortAllocatorBindToPort(bool * used,unsigned short port,int family)124 virPortAllocatorBindToPort(bool *used,
125 unsigned short port,
126 int family)
127 {
128 struct sockaddr_in6 addr6 = {
129 .sin6_family = AF_INET6,
130 .sin6_port = htons(port),
131 .sin6_addr = in6addr_any
132 };
133 struct sockaddr_in addr4 = {
134 .sin_family = AF_INET,
135 .sin_port = htons(port),
136 .sin_addr.s_addr = htonl(INADDR_ANY)
137 };
138 struct sockaddr* addr;
139 size_t addrlen;
140 int v6only = 1;
141 int ret = -1;
142 int fd = -1;
143 bool ipv6 = false;
144
145 if (family == AF_INET6) {
146 addr = (struct sockaddr*)&addr6;
147 addrlen = sizeof(addr6);
148 ipv6 = true;
149 } else if (family == AF_INET) {
150 addr = (struct sockaddr*)&addr4;
151 addrlen = sizeof(addr4);
152 } else {
153 virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown family %d"), family);
154 return -1;
155 }
156
157 *used = false;
158
159 fd = socket(family, SOCK_STREAM, 0);
160 if (fd < 0) {
161 if (errno == EAFNOSUPPORT)
162 return 0;
163 virReportSystemError(errno, "%s", _("Unable to open test socket"));
164 goto cleanup;
165 }
166
167 if (virSetSockReuseAddr(fd, true) < 0)
168 goto cleanup;
169
170 if (ipv6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only,
171 sizeof(v6only)) < 0) {
172 virReportSystemError(errno, "%s",
173 _("Unable to set IPV6_V6ONLY flag"));
174 goto cleanup;
175 }
176
177 if (bind(fd, addr, addrlen) < 0) {
178 if (errno == EADDRINUSE) {
179 *used = true;
180 ret = 0;
181 } else {
182 virReportSystemError(errno, _("Unable to bind to port %d"), port);
183 }
184 goto cleanup;
185 }
186
187 ret = 0;
188 cleanup:
189 if (fd != -1)
190 closesocket(fd);
191 return ret;
192 }
193
194 static virPortAllocator *
virPortAllocatorGet(void)195 virPortAllocatorGet(void)
196 {
197 if (virPortAllocatorInitialize() < 0)
198 return NULL;
199
200 return virPortAllocatorInstance;
201 }
202
203 int
virPortAllocatorAcquire(const virPortAllocatorRange * range,unsigned short * port)204 virPortAllocatorAcquire(const virPortAllocatorRange *range,
205 unsigned short *port)
206 {
207 int ret = -1;
208 size_t i;
209 virPortAllocator *pa = virPortAllocatorGet();
210
211 *port = 0;
212
213 if (!pa)
214 return -1;
215
216 virObjectLock(pa);
217
218 for (i = range->start; i <= range->end && !*port; i++) {
219 bool used = false, v6used = false;
220
221 if (virBitmapIsBitSet(pa->bitmap, i))
222 continue;
223
224 if (virPortAllocatorBindToPort(&v6used, i, AF_INET6) < 0 ||
225 virPortAllocatorBindToPort(&used, i, AF_INET) < 0)
226 goto cleanup;
227
228 if (!used && !v6used) {
229 /* Add port to bitmap of reserved ports */
230 if (virBitmapSetBit(pa->bitmap, i) < 0) {
231 virReportError(VIR_ERR_INTERNAL_ERROR,
232 _("Failed to reserve port %zu"), i);
233 goto cleanup;
234 }
235 *port = i;
236 ret = 0;
237 }
238 }
239
240 if (*port == 0) {
241 virReportError(VIR_ERR_INTERNAL_ERROR,
242 _("Unable to find an unused port in range '%s' (%d-%d)"),
243 range->name, range->start, range->end);
244 }
245 cleanup:
246 virObjectUnlock(pa);
247 return ret;
248 }
249
250 int
virPortAllocatorRelease(unsigned short port)251 virPortAllocatorRelease(unsigned short port)
252 {
253 int ret = -1;
254 virPortAllocator *pa = virPortAllocatorGet();
255
256 if (!pa)
257 return -1;
258
259 if (!port)
260 return 0;
261
262 virObjectLock(pa);
263
264 if (virBitmapClearBit(pa->bitmap, port) < 0) {
265 virReportError(VIR_ERR_INTERNAL_ERROR,
266 _("Failed to release port %d"),
267 port);
268 goto cleanup;
269 }
270
271 ret = 0;
272 cleanup:
273 virObjectUnlock(pa);
274 return ret;
275 }
276
277 int
virPortAllocatorSetUsed(unsigned short port)278 virPortAllocatorSetUsed(unsigned short port)
279 {
280 int ret = -1;
281 virPortAllocator *pa = virPortAllocatorGet();
282
283 if (!pa)
284 return -1;
285
286 if (!port)
287 return 0;
288
289 virObjectLock(pa);
290
291 if (virBitmapIsBitSet(pa->bitmap, port) ||
292 virBitmapSetBit(pa->bitmap, port) < 0) {
293 virReportError(VIR_ERR_INTERNAL_ERROR,
294 _("Failed to reserve port %d"), port);
295 goto cleanup;
296 }
297
298 ret = 0;
299 cleanup:
300 virObjectUnlock(pa);
301 return ret;
302 }
303