ippool: Add API to notify when IP is externally assigned
[framework/connectivity/connman.git] / src / ippool.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <getopt.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/errno.h>
33 #include <sys/socket.h>
34
35 #include "connman.h"
36
37 struct connman_ippool {
38         unsigned int refcount;
39
40         int index;
41         uint32_t block;
42
43         char *gateway;
44         char *broadcast;
45         char *start_ip;
46         char *end_ip;
47         char *subnet_mask;
48
49         ippool_collision_cb_t collision_cb;
50         void *user_data;
51 };
52
53 static GHashTable *hash_pool;
54 static GHashTable *hash_addresses;
55 static uint32_t last_block;
56 static uint32_t block_16_bits;
57 static uint32_t block_20_bits;
58 static uint32_t block_24_bits;
59 static uint32_t subnet_mask_24;
60
61 struct connman_ippool *
62 __connman_ippool_ref_debug(struct connman_ippool *pool,
63                                 const char *file, int line, const char *caller)
64 {
65         DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1,
66                 file, line, caller);
67
68         __sync_fetch_and_add(&pool->refcount, 1);
69
70         return pool;
71 }
72
73 void __connman_ippool_unref_debug(struct connman_ippool *pool,
74                                 const char *file, int line, const char *caller)
75 {
76         if (pool == NULL)
77                 return;
78
79         DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1,
80                 file, line, caller);
81
82         if (__sync_fetch_and_sub(&pool->refcount, 1) != 1)
83                 return;
84
85         g_hash_table_remove(hash_pool, GUINT_TO_POINTER(pool->block));
86 }
87
88 static char *get_ip(uint32_t ip)
89 {
90         struct in_addr addr;
91
92         addr.s_addr = htonl(ip);
93
94         return g_strdup(inet_ntoa(addr));
95 }
96
97 static uint32_t next_block(uint32_t block)
98 {
99         uint32_t next;
100
101         /*
102          * Return the next IP block within the private IP range
103          *
104          * 16-bit block 192.168.0.0 – 192.168.255.255
105          * 20-bit block  172.16.0.0 –  172.31.255.255
106          * 24-bit block    10.0.0.0 –  10.255.255.255
107          */
108
109         next = (block & 0x0000ff00) >> 8;
110         next += 1;
111
112         if (next == 255) {
113                 if ((block & 0xffffff00) == block_16_bits) {
114                         /*
115                          * Reached the end of the 16 bit block, switch
116                          * to the 20-bit block.
117                          */
118                         return block_20_bits;
119                 }
120
121                 if ((block & 0xffff0000) >= block_20_bits) {
122                         next = (block & 0x00ff0000) >> 16;
123                         if (next >= 16 && next < 32)
124                                 next += 1;
125
126                         if (next == 32) {
127                                 /*
128                                  * Reached the end of the 20 bit
129                                  * block, switch to the 24-bit block.
130                                  */
131                                 return block_24_bits;
132                         }
133
134                         return (block & 0xff000000) |
135                                 ((next << 16) & 0x00ff0000);
136                 }
137
138                 if ((block & 0xff000000) == block_24_bits) {
139                         next = (block & 0x00ff0000) >> 16;
140                         if (next < 255)
141                                 next += 1;
142
143                         if (next == 255) {
144                                 /*
145                                  * Reached the end of the 24 bit
146                                  * block, switch to the 16-bit block.
147                                  */
148                                 return block_16_bits;
149                         }
150
151                         return (block & 0xff000000) |
152                                 ((next << 16) & 0x00ff0000);
153                 }
154         }
155
156         return (block & 0xffff0000) | ((next << 8) & 0x0000ff00);
157 }
158
159 static uint32_t find_free_block()
160 {
161         struct connman_ippool *pool;
162         uint32_t start;
163         uint32_t block;
164         uint32_t *key;
165
166         if (last_block == 0)
167                 return block_16_bits;
168
169         /*
170          * Instead starting always from the 16 bit block, we start
171          * from the last assigned block. This is a simple optimimazion
172          * for the case where a lot of blocks have been assigned, e.g.
173          * the first half of the private IP pool is in use and a new
174          * we need to find a new block.
175          *
176          * To only thing we have to make sure is that we terminated if
177          * there is no block left.
178          */
179         start = last_block;
180
181         block = next_block(start);
182         while (start != block) {
183                 block = next_block(block);
184
185                 key = GUINT_TO_POINTER(block);
186                 pool = g_hash_table_lookup(hash_pool, key);
187                 if (pool != NULL)
188                         continue;
189
190                 if (g_hash_table_lookup(hash_addresses, key) != NULL)
191                         continue;
192
193                 return block;
194         }
195
196         return 0;
197 }
198
199 void __connman_ippool_newaddr(int index, const char *address)
200 {
201         struct connman_ippool *pool;
202         struct in_addr inp;
203         uint32_t block;
204         uint32_t *key;
205         unsigned int count;
206
207         if (inet_aton(address, &inp) == 0)
208                 return;
209
210         block = ntohl(inp.s_addr) & 0xffffff00;
211
212         key = GUINT_TO_POINTER(block);
213         count = GPOINTER_TO_UINT(g_hash_table_lookup(hash_addresses, key));
214         count = count + 1;
215         g_hash_table_replace(hash_addresses, key, GUINT_TO_POINTER(count));
216
217         pool = g_hash_table_lookup(hash_pool, key);
218         if (pool == NULL)
219                 return;
220
221         if (pool->index == index)
222                 return;
223
224         if (pool->collision_cb != NULL)
225                 pool->collision_cb(pool, pool->user_data);
226 }
227
228 void __connman_ippool_deladdr(int index, const char *address)
229 {
230         struct in_addr inp;
231         uint32_t block;
232         uint32_t *key;
233         unsigned int count;
234
235         if (inet_aton(address, &inp) == 0)
236                 return;
237
238         block = ntohl(inp.s_addr) & 0xffffff00;
239
240         key = GUINT_TO_POINTER(block);
241         count = GPOINTER_TO_UINT(g_hash_table_lookup(hash_addresses, key));
242         count = count - 1;
243
244         if (count == 0)
245                 g_hash_table_remove(hash_addresses, key);
246         else
247                 g_hash_table_replace(hash_addresses, key, GUINT_TO_POINTER(count));
248 }
249
250 struct connman_ippool *__connman_ippool_create(int index,
251                                         unsigned int start,
252                                         unsigned int range,
253                                         ippool_collision_cb_t collision_cb,
254                                         void *user_data)
255 {
256         struct connman_ippool *pool;
257         uint32_t block;
258
259         /*
260          * The range is at max 255 and we don't support overlapping
261          * blocks.
262          */
263         if (start + range > 254)
264                 return NULL;
265
266         block = find_free_block();
267         if (block == 0)
268                 return NULL;
269
270         pool = g_try_new0(struct connman_ippool, 1);
271         if (pool == NULL)
272                 return NULL;
273
274         pool->refcount = 1;
275         pool->index = index;
276         pool->block = block;
277         pool->collision_cb = collision_cb;
278         pool->user_data = user_data;
279
280         last_block = block;
281
282         if (range == 0)
283                 range = 1;
284
285         pool->gateway = get_ip(block + 1);
286         pool->broadcast = get_ip(block + 255);
287         pool->subnet_mask = get_ip(subnet_mask_24);
288         pool->start_ip = get_ip(block + start);
289         pool->end_ip = get_ip(block + start + range);
290
291         g_hash_table_insert(hash_pool, GUINT_TO_POINTER(pool->block), pool);
292
293         return pool;
294 }
295
296 const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
297 {
298         return pool->gateway;
299 }
300
301 const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
302 {
303         return pool->broadcast;
304 }
305
306 const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
307 {
308         return pool->start_ip;
309 }
310
311 const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
312 {
313         return pool->end_ip;
314 }
315
316 const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
317 {
318         return pool->subnet_mask;
319 }
320
321 static void pool_free(gpointer data)
322 {
323         struct connman_ippool *pool = data;
324
325         g_free(pool->gateway);
326         g_free(pool->broadcast);
327         g_free(pool->start_ip);
328         g_free(pool->end_ip);
329         g_free(pool->subnet_mask);
330
331         g_free(pool);
332 }
333
334 int __connman_ippool_init(void)
335 {
336         DBG("");
337
338         /* We start at 254 by default to avoid common addresses */
339         block_16_bits = ntohl(inet_addr("192.168.254.0"));
340         block_20_bits = ntohl(inet_addr("172.16.0.0"));
341         block_24_bits = ntohl(inet_addr("10.0.0.0"));
342         subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
343
344         hash_pool = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
345                                                 pool_free);
346         hash_addresses = g_hash_table_new_full(g_direct_hash, g_direct_equal,
347                                                 NULL, NULL);
348
349         return 0;
350 }
351
352 void __connman_ippool_cleanup(void)
353 {
354         DBG("");
355
356         g_hash_table_destroy(hash_pool);
357         hash_pool = NULL;
358
359         g_hash_table_destroy(hash_addresses);
360         hash_addresses = NULL;
361 }