Add listener for gateway changes.
[olsrd.git] / src / gateway.c
1 /*
2  * gateway.c
3  *
4  *  Created on: 05.01.2010
5  *      Author: henning
6  */
7
8 #include "common/avl.h"
9 #include "defs.h"
10 #include "ipcalc.h"
11 #include "olsr.h"
12 #include "olsr_cfg.h"
13 #include "olsr_cookie.h"
14 #include "scheduler.h"
15 #include "gateway.h"
16
17 struct avl_tree gateway_tree;
18 struct list_node gw_listener_list;;
19
20 static struct olsr_cookie_info *gw_mem_cookie = NULL;
21 static uint8_t smart_gateway_netmask[sizeof(union olsr_ip_addr)];
22 static struct gateway_entry *current_ipv4_gw, *current_ipv6_gw;
23
24 static uint32_t deserialize_gw_speed(uint8_t value) {
25   uint32_t speed, exp;
26
27   speed = (value >> 3)+1;
28   exp = value & 7;
29   while (exp-- > 0) {
30     speed *= 10;
31   }
32   return speed;
33 }
34
35 static uint8_t serialize_gw_speed(uint32_t speed) {
36   uint8_t exp = 0;
37
38   if (speed == 0 || speed > 320000000) {
39     return 0;
40   }
41
42   while (speed > 32 || (speed % 10) == 0) {
43     speed /= 10;
44     exp ++;
45   }
46   return ((speed-1) << 3) | exp;
47 }
48
49 void
50 olsr_init_gateways(void) {
51   uint8_t *ip;
52   gw_mem_cookie = olsr_alloc_cookie("Gateway cookie", OLSR_COOKIE_TYPE_MEMORY);
53   olsr_cookie_set_memory_size(gw_mem_cookie, sizeof(struct gateway_entry));
54
55   avl_init(&gateway_tree, avl_comp_default);
56   list_head_init(&gw_listener_list);
57   current_ipv4_gw = NULL;
58   current_ipv6_gw = NULL;
59
60   memset(&smart_gateway_netmask, 0, sizeof(smart_gateway_netmask));
61
62   if (olsr_cnf->smart_gw_active) {
63     union olsr_ip_addr gw_net;
64 #ifdef MAXIMUM_GATEWAY_PREFIX_LENGTH
65     int prefix;
66 #endif
67
68     memset(&gw_net, 0, sizeof(gw_net));
69
70 #ifdef MAXIMUM_GATEWAY_PREFIX_LENGTH
71     for (prefix = 1; prefix <= MAXIMUM_GATEWAY_PREFIX_LENGTH; prefix++) {
72       while (ip_prefix_list_remove(&olsr_cnf->hna_entries, &gw_net, prefix));
73     }
74 #endif
75     ip = (uint8_t *) &smart_gateway_netmask;
76
77     if (olsr_cnf->smart_gw_uplink > 0 || olsr_cnf->smart_gw_downlink > 0) {
78       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_LINKSPEED;
79       ip[GW_HNA_DOWNLINK] = serialize_gw_speed(olsr_cnf->smart_gw_downlink);
80       ip[GW_HNA_UPLINK] = serialize_gw_speed(olsr_cnf->smart_gw_uplink);
81     }
82     if (olsr_cnf->ip_version == AF_INET6 && olsr_cnf->smart_gw_prefix.prefix_len > 0) {
83       ip[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6PREFIX;
84       ip[GW_HNA_V6PREFIXLEN] = olsr_cnf->smart_gw_prefix.prefix_len;
85       memcpy(&ip[GW_HNA_V6PREFIX], &olsr_cnf->smart_gw_prefix.prefix, 8);
86     }
87   }
88 }
89
90 void
91 olsr_add_inetgw_listener(struct olsr_gw_change_handler *l) {
92   list_node_init(&l->node);
93   list_add_after(&gw_listener_list, &l->node);
94 }
95
96 void
97 olsr_remove_inetgw_listener(struct olsr_gw_change_handler *l) {
98   list_remove(&l->node);
99 }
100
101 struct gateway_entry *
102 olsr_find_gateway_entry(union olsr_ip_addr *originator) {
103   struct avl_node *node = avl_find(&gateway_tree, originator);
104
105   return node == NULL ? NULL : node2gateway(node);
106 }
107
108 void
109 olsr_update_gateway_entry(union olsr_ip_addr *originator, union olsr_ip_addr *mask, int prefixlen) {
110   struct gateway_entry *gw;
111   struct olsr_gw_change_handler *listener;
112   uint8_t *ptr;
113
114   ptr = ((uint8_t *)mask) + ((prefixlen+7)/8);
115
116   gw = olsr_find_gateway_entry(originator);
117   if (!gw) {
118     gw = olsr_cookie_malloc(gw_mem_cookie);
119
120     gw->originator = *originator;
121     gw->node.key = &gw->originator;
122
123     avl_insert(&gateway_tree, &gw->node, AVL_DUP_NO);
124   }
125
126   if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_LINKSPEED) != 0) {
127     gw->uplink = deserialize_gw_speed(ptr[GW_HNA_UPLINK]);
128     gw->downlink = deserialize_gw_speed(ptr[GW_HNA_DOWNLINK]);
129   }
130   else {
131     gw->uplink = 1;
132     gw->downlink = 1;
133   }
134
135   gw->ipv4 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4) != 0;
136   gw->ipv4nat = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV4_NAT) != 0;
137
138   if (olsr_cnf->ip_version == AF_INET6) {
139     gw->ipv6 = (ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6) != 0;
140
141     /* do not reset prefixlength for ::ffff:0:0 HNAs */
142     if (prefixlen == ipv6_internet_route.prefix_len) {
143       memset(&gw->external_prefix, 0, sizeof(gw->external_prefix));
144
145       if ((ptr[GW_HNA_FLAGS] & GW_HNA_FLAG_IPV6PREFIX) != 0
146           && memcmp(mask->v6.s6_addr, &ipv6_internet_route.prefix, olsr_cnf->ipsize) == 0) {
147         /* this is the right prefix (2000::/3), so we can copy the prefix */
148         gw->external_prefix.prefix_len = ptr[GW_HNA_V6PREFIXLEN];
149         memcpy(&gw->external_prefix.prefix, &ptr[GW_HNA_V6PREFIX], 8);
150       }
151     }
152   }
153
154   OLSR_FOR_ALL_GW_LISTENERS(listener) {
155     listener->handle_update_gw(gw);
156   } OLSR_FOR_ALL_GW_LISTENERS_END(listener)
157 }
158
159 void
160 olsr_delete_gateway_entry(union olsr_ip_addr *originator, uint8_t prefixlen) {
161   struct gateway_entry *gw;
162   struct olsr_gw_change_handler *listener;
163   bool change = false;
164
165   gw = olsr_find_gateway_entry(originator);
166   if (gw) {
167     if (olsr_cnf->ip_version == AF_INET && prefixlen == 0) {
168       gw->ipv4 = false;
169       gw->ipv4nat = false;
170       change = true;
171     }
172     if (olsr_cnf->ip_version == AF_INET6 && prefixlen == ipv6_internet_route.prefix_len) {
173       gw->ipv6 = false;
174       change = true;
175     }
176     if (olsr_cnf->ip_version == AF_INET6 && prefixlen == mapped_v4_gw.prefix_len) {
177       gw->ipv4 = false;
178       gw->ipv4nat = false;
179       change = true;
180     }
181
182     if (prefixlen == FORCE_DELETE_GW_ENTRY || !(gw->ipv4 || gw->ipv6)) {
183       OLSR_FOR_ALL_GW_LISTENERS(listener) {
184         listener->handle_delete_gw(gw);
185       } OLSR_FOR_ALL_GW_LISTENERS_END(listener)
186
187       avl_delete(&gateway_tree, &gw->node);
188
189       olsr_cookie_free(gw_mem_cookie, gw);
190     }
191     else if (change) {
192       OLSR_FOR_ALL_GW_LISTENERS(listener) {
193         listener->handle_update_gw(gw);
194       } OLSR_FOR_ALL_GW_LISTENERS_END(listener)
195     }
196   }
197 }
198
199 bool olsr_is_smart_gateway(struct olsr_ip_prefix *prefix, union olsr_ip_addr *mask) {
200   uint8_t *ptr;
201
202   if (!ip_is_inetgw_prefix(prefix)) {
203     return false;
204   }
205
206   ptr = ((uint8_t *)mask) + ((prefix->prefix_len+7)/8);
207   return ptr[GW_HNA_PAD] == 0 && ptr[GW_HNA_FLAGS] != 0;
208 }
209
210 void olsr_modifiy_inetgw_netmask(union olsr_ip_addr *mask, int prefixlen) {
211   uint8_t *ptr = ((uint8_t *)mask) + ((prefixlen+7)/8);
212
213   memcpy(ptr, &smart_gateway_netmask, sizeof(smart_gateway_netmask) - prefixlen/8);
214   if (olsr_cnf->has_ipv4_gateway) {
215     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4;
216
217     if (olsr_cnf->smart_gw_uplink_nat) {
218       ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV4_NAT;
219     }
220   }
221   if (olsr_cnf->has_ipv6_gateway) {
222     ptr[GW_HNA_FLAGS] |= GW_HNA_FLAG_IPV6;
223   }
224   if (!olsr_cnf->has_ipv6_gateway || prefixlen != ipv6_internet_route.prefix_len){
225     ptr[GW_HNA_FLAGS] &= ~GW_HNA_FLAG_IPV6PREFIX;
226   }
227 }
228
229 void
230 olsr_print_gateway_entries(void) {
231 #ifndef NODEBUG
232   struct ipaddr_str buf;
233   struct gateway_entry *gw;
234   const int addrsize = olsr_cnf->ip_version == AF_INET ? 15 : 39;
235
236   OLSR_PRINTF(0, "\n--- %s ---------------------------------------------------- GATEWAYS\n\n",
237       olsr_wallclock_string());
238   OLSR_PRINTF(0, "%-*s %-6s %-9s %-9s %s\n", addrsize, "IP address", "Type", "Uplink", "Downlink",
239       olsr_cnf->ip_version == AF_INET ? "" : "External Prefix");
240
241   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
242     OLSR_PRINTF(0, "%-*s %s%c%s%c%c %-9u %-9u %s\n", addrsize, olsr_ip_to_string(&buf, &gw->originator),
243         gw->ipv4nat ? "" : "   ",
244         gw->ipv4 ? '4' : ' ',
245         gw->ipv4nat ? "(N)" : "",
246         (gw->ipv4 && gw->ipv6) ? ',' : ' ',
247         gw->ipv6 ? '6' : ' ',
248         gw->uplink, gw->downlink,
249         gw->external_prefix.prefix_len == 0 ? "" : olsr_ip_prefix_to_string(&gw->external_prefix));
250   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)
251 #endif
252 }