ippool: Add prefixlen argument to __connman_ippool_new/deladdr()
[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                                 unsigned char prefixlen)
201 {
202         struct connman_ippool *pool;
203         struct in_addr inp;
204         uint32_t block;
205         uint32_t *key;
206         unsigned int count;
207
208         if (inet_aton(address, &inp) == 0)
209                 return;
210
211         block = ntohl(inp.s_addr) & 0xffffff00;
212
213         key = GUINT_TO_POINTER(block);
214         count = GPOINTER_TO_UINT(g_hash_table_lookup(hash_addresses, key));
215         count = count + 1;
216         g_hash_table_replace(hash_addresses, key, GUINT_TO_POINTER(count));
217
218         pool = g_hash_table_lookup(hash_pool, key);
219         if (pool == NULL)
220                 return;
221
222         if (pool->index == index)
223                 return;
224
225         if (pool->collision_cb != NULL)
226                 pool->collision_cb(pool, pool->user_data);
227 }
228
229 void __connman_ippool_deladdr(int index, const char *address,
230                                 unsigned char prefixlen)
231 {
232         struct in_addr inp;
233         uint32_t block;
234         uint32_t *key;
235         unsigned int count;
236
237         if (inet_aton(address, &inp) == 0)
238                 return;
239
240         block = ntohl(inp.s_addr) & 0xffffff00;
241
242         key = GUINT_TO_POINTER(block);
243         count = GPOINTER_TO_UINT(g_hash_table_lookup(hash_addresses, key));
244         count = count - 1;
245
246         if (count == 0)
247                 g_hash_table_remove(hash_addresses, key);
248         else
249                 g_hash_table_replace(hash_addresses, key, GUINT_TO_POINTER(count));
250 }
251
252 struct connman_ippool *__connman_ippool_create(int index,
253                                         unsigned int start,
254                                         unsigned int range,
255                                         ippool_collision_cb_t collision_cb,
256                                         void *user_data)
257 {
258         struct connman_ippool *pool;
259         uint32_t block;
260
261         /*
262          * The range is at max 255 and we don't support overlapping
263          * blocks.
264          */
265         if (start + range > 254)
266                 return NULL;
267
268         block = find_free_block();
269         if (block == 0)
270                 return NULL;
271
272         pool = g_try_new0(struct connman_ippool, 1);
273         if (pool == NULL)
274                 return NULL;
275
276         pool->refcount = 1;
277         pool->index = index;
278         pool->block = block;
279         pool->collision_cb = collision_cb;
280         pool->user_data = user_data;
281
282         last_block = block;
283
284         if (range == 0)
285                 range = 1;
286
287         pool->gateway = get_ip(block + 1);
288         pool->broadcast = get_ip(block + 255);
289         pool->subnet_mask = get_ip(subnet_mask_24);
290         pool->start_ip = get_ip(block + start);
291         pool->end_ip = get_ip(block + start + range);
292
293         g_hash_table_insert(hash_pool, GUINT_TO_POINTER(pool->block), pool);
294
295         return pool;
296 }
297
298 const char *__connman_ippool_get_gateway(struct connman_ippool *pool)
299 {
300         return pool->gateway;
301 }
302
303 const char *__connman_ippool_get_broadcast(struct connman_ippool *pool)
304 {
305         return pool->broadcast;
306 }
307
308 const char *__connman_ippool_get_start_ip(struct connman_ippool *pool)
309 {
310         return pool->start_ip;
311 }
312
313 const char *__connman_ippool_get_end_ip(struct connman_ippool *pool)
314 {
315         return pool->end_ip;
316 }
317
318 const char *__connman_ippool_get_subnet_mask(struct connman_ippool *pool)
319 {
320         return pool->subnet_mask;
321 }
322
323 static void pool_free(gpointer data)
324 {
325         struct connman_ippool *pool = data;
326
327         g_free(pool->gateway);
328         g_free(pool->broadcast);
329         g_free(pool->start_ip);
330         g_free(pool->end_ip);
331         g_free(pool->subnet_mask);
332
333         g_free(pool);
334 }
335
336 int __connman_ippool_init(void)
337 {
338         DBG("");
339
340         /* We start at 254 by default to avoid common addresses */
341         block_16_bits = ntohl(inet_addr("192.168.254.0"));
342         block_20_bits = ntohl(inet_addr("172.16.0.0"));
343         block_24_bits = ntohl(inet_addr("10.0.0.0"));
344         subnet_mask_24 = ntohl(inet_addr("255.255.255.0"));
345
346         hash_pool = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
347                                                 pool_free);
348         hash_addresses = g_hash_table_new_full(g_direct_hash, g_direct_equal,
349                                                 NULL, NULL);
350
351         return 0;
352 }
353
354 void __connman_ippool_cleanup(void)
355 {
356         DBG("");
357
358         g_hash_table_destroy(hash_pool);
359         hash_pool = NULL;
360
361         g_hash_table_destroy(hash_addresses);
362         hash_addresses = NULL;
363 }