1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 * Michael Jerris <mike@jerris.com>
28 * Paul D. Tinsley <pdt at jackhammer.org>
29 *
30 *
31 * switch_core_port_allocator.c -- Main Core Library (port allocator)
32 *
33 */
34
35 #include <switch.h>
36 #include "private/switch_core_pvt.h"
37
38 struct switch_core_port_allocator {
39 char *ip;
40 switch_port_t start;
41 switch_port_t end;
42 switch_port_t next;
43 int8_t *track;
44 uint32_t track_len;
45 uint32_t track_used;
46 switch_port_flag_t flags;
47 switch_mutex_t *mutex;
48 switch_memory_pool_t *pool;
49 };
50
switch_core_port_allocator_new(const char * ip,switch_port_t start,switch_port_t end,switch_port_flag_t flags,switch_core_port_allocator_t ** new_allocator)51 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(const char *ip, switch_port_t start,
52 switch_port_t end, switch_port_flag_t flags, switch_core_port_allocator_t **new_allocator)
53 {
54 switch_status_t status;
55 switch_memory_pool_t *pool;
56 switch_core_port_allocator_t *alloc;
57 int even, odd;
58
59 if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
60 return status;
61 }
62
63 if (!(alloc = switch_core_alloc(pool, sizeof(*alloc)))) {
64 switch_core_destroy_memory_pool(&pool);
65 return SWITCH_STATUS_MEMERR;
66 }
67
68 alloc->flags = flags;
69 alloc->ip = switch_core_strdup(pool, ip);
70 even = switch_test_flag(alloc, SPF_EVEN);
71 odd = switch_test_flag(alloc, SPF_ODD);
72
73 alloc->flags |= runtime.port_alloc_flags;
74
75
76 if (!(even && odd)) {
77 if (even) {
78 if ((start % 2) != 0) {
79 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd start port %d to %d\n", start, start + 1);
80 start++;
81 }
82 if ((end % 2) != 0) {
83 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd end port %d to %d\n", end, end - 1);
84 end--;
85 }
86 } else if (odd) {
87 if ((start % 2) == 0) {
88 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even start port %d to %d\n", start, start + 1);
89 start++;
90 }
91 if ((end % 2) == 0) {
92 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even end port %d to %d\n", end, end - 1);
93 end--;
94 }
95 }
96 }
97
98 alloc->track_len = (end - start) + 2;
99
100 if (!(even && odd)) {
101 alloc->track_len /= 2;
102 }
103
104 alloc->track = switch_core_alloc(pool, (alloc->track_len + 2) * sizeof(switch_byte_t));
105
106 alloc->start = start;
107 alloc->next = start;
108 alloc->end = end;
109
110
111 switch_mutex_init(&alloc->mutex, SWITCH_MUTEX_NESTED, pool);
112 alloc->pool = pool;
113 *new_allocator = alloc;
114
115 return SWITCH_STATUS_SUCCESS;
116 }
117
test_port(switch_core_port_allocator_t * alloc,int family,int type,switch_port_t port)118 static switch_bool_t test_port(switch_core_port_allocator_t *alloc, int family, int type, switch_port_t port)
119 {
120 switch_memory_pool_t *pool = NULL;
121 switch_sockaddr_t *local_addr = NULL;
122 switch_socket_t *sock = NULL;
123 switch_bool_t r = SWITCH_FALSE;
124
125 if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
126 return SWITCH_FALSE;
127 }
128
129 if (switch_sockaddr_info_get(&local_addr, alloc->ip, SWITCH_UNSPEC, port, 0, pool) == SWITCH_STATUS_SUCCESS) {
130 if (switch_socket_create(&sock, family, type, 0, pool) == SWITCH_STATUS_SUCCESS) {
131 if (switch_socket_bind(sock, local_addr) == SWITCH_STATUS_SUCCESS) {
132 r = SWITCH_TRUE;
133 }
134 switch_socket_close(sock);
135 }
136 }
137
138 switch_core_destroy_memory_pool(&pool);
139
140 return r;
141 }
142
switch_core_port_allocator_request_port(switch_core_port_allocator_t * alloc,switch_port_t * port_ptr)143 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc, switch_port_t *port_ptr)
144 {
145 switch_port_t port = 0;
146 switch_status_t status = SWITCH_STATUS_FALSE;
147 int even = switch_test_flag(alloc, SPF_EVEN);
148 int odd = switch_test_flag(alloc, SPF_ODD);
149
150 switch_mutex_lock(alloc->mutex);
151 srand((unsigned) ((unsigned) (intptr_t) port_ptr + (unsigned) (intptr_t) switch_thread_self() + switch_micro_time_now()));
152
153 while (alloc->track_used < alloc->track_len) {
154 uint32_t index;
155 uint32_t tries = 0;
156
157 /* randomly pick a port */
158 index = rand() % alloc->track_len;
159
160 /* if it is used walk up the list to find a free one */
161 while (alloc->track[index] && tries < alloc->track_len) {
162 tries++;
163 if (alloc->track[index] < 0) {
164 alloc->track[index]++;
165 }
166 if (++index >= alloc->track_len) {
167 index = 0;
168 }
169 }
170
171 if (tries < alloc->track_len) {
172 switch_bool_t r = SWITCH_TRUE;
173
174 if ((even && odd)) {
175 port = (switch_port_t) (index + alloc->start);
176 } else {
177 port = (switch_port_t) (index + (alloc->start / 2));
178 port *= 2;
179 }
180
181 if ((alloc->flags & SPF_ROBUST_UDP)) {
182 r = test_port(alloc, AF_INET, SOCK_DGRAM, port);
183 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "UDP port robustness check for port %d %s\n", port, r ? "pass" : "fail");
184 }
185
186 if ((alloc->flags & SPF_ROBUST_TCP)) {
187 r = test_port(alloc, AF_INET, SOCK_STREAM, port);
188 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TCP port robustness check for port %d %s\n", port, r ? "pass" : "fail");
189 }
190
191 if (r) {
192 alloc->track[index] = 1;
193 alloc->track_used++;
194 status = SWITCH_STATUS_SUCCESS;
195 goto end;
196 } else {
197 alloc->track[index] = -4;
198 }
199 }
200 }
201
202
203 end:
204
205 switch_mutex_unlock(alloc->mutex);
206
207 if (status == SWITCH_STATUS_SUCCESS) {
208 *port_ptr = port;
209 } else {
210 *port_ptr = 0;
211 }
212
213
214 return status;
215
216 }
217
switch_core_port_allocator_free_port(switch_core_port_allocator_t * alloc,switch_port_t port)218 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_free_port(switch_core_port_allocator_t *alloc, switch_port_t port)
219 {
220 switch_status_t status = SWITCH_STATUS_FALSE;
221 int even = switch_test_flag(alloc, SPF_EVEN);
222 int odd = switch_test_flag(alloc, SPF_ODD);
223 int index;
224
225 if (port < alloc->start) {
226 return SWITCH_STATUS_GENERR;
227 }
228
229 index = port - alloc->start;
230
231 if (!(even && odd)) {
232 index /= 2;
233 }
234
235 switch_mutex_lock(alloc->mutex);
236 if (alloc->track[index] > 0) {
237 alloc->track[index] = -4;
238 alloc->track_used--;
239 status = SWITCH_STATUS_SUCCESS;
240 }
241 switch_mutex_unlock(alloc->mutex);
242
243 return status;
244 }
245
switch_core_port_allocator_destroy(switch_core_port_allocator_t ** alloc)246 SWITCH_DECLARE(void) switch_core_port_allocator_destroy(switch_core_port_allocator_t **alloc)
247 {
248 switch_memory_pool_t *pool = (*alloc)->pool;
249 switch_core_destroy_memory_pool(&pool);
250 *alloc = NULL;
251 }
252
253 /* For Emacs:
254 * Local Variables:
255 * mode:c
256 * indent-tabs-mode:t
257 * tab-width:4
258 * c-basic-offset:4
259 * End:
260 * For VIM:
261 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
262 */
263