Rename "subsystems" directory to "base"
[oonf.git] / src / base / os_generic / os_interface_generic.c
1 /*
2  * os_interface_generic_get_bindaddress.c
3  *
4  *  Created on: 07.03.2016
5  *      Author: rogge
6  */
7
8 #include <oonf/libcommon/avl.h>
9 #include <oonf/oonf.h>
10 #include <oonf/base/os_interface.h>
11
12 static const struct netaddr *_get_fixed_prefix(int af_type, struct netaddr_acl *filter);
13 static const struct netaddr *_get_exact_match_bindaddress(
14   int af_type, struct netaddr_acl *filter, struct os_interface *os_if);
15 static const struct netaddr *_get_matching_bindaddress(
16   int af_type, struct netaddr_acl *filter, struct os_interface *os_if);
17
18 /**
19  * Calculate the IP address a socket should bind to
20  * @param af_type address family for result
21  * @param filter filter for IP address to bind on
22  * @param os_if interface to bind to socket on, NULL if not
23  *   bound to an interface.
24  * @return pointer to address, NULL if no valid address was found
25  */
26 const struct netaddr *
27 os_interface_generic_get_bindaddress(int af_type, struct netaddr_acl *filter, struct os_interface *os_if) {
28   const struct netaddr *result;
29
30   result = NULL;
31   if (os_if == NULL || os_if->flags.any) {
32     result = _get_fixed_prefix(af_type, filter);
33   }
34   if (!result) {
35     result = _get_exact_match_bindaddress(af_type, filter, os_if);
36   }
37   if (!result) {
38     result = _get_matching_bindaddress(af_type, filter, os_if);
39   }
40   return result;
41 }
42
43 /**
44  * Search for an interface by its base-index
45  * @param ifindex index of the interface
46  * @return first fitting interface data, NULL if not found
47  */
48 struct os_interface *
49 os_interface_generic_get_data_by_ifbaseindex(unsigned ifindex) {
50   struct os_interface *os_if;
51
52   avl_for_each_element(os_interface_get_tree(), os_if, _node) {
53     if (os_if->base_index == ifindex) {
54       return os_if;
55     }
56   }
57   return NULL;
58 }
59
60 /**
61  * Search for an interface by its index
62  * @param ifindex index of the interface
63  * @return interface data, NULL if not found
64  */
65 struct os_interface *
66 os_interface_generic_get_data_by_ifindex(unsigned ifindex) {
67   struct os_interface *os_if;
68
69   avl_for_each_element(os_interface_get_tree(), os_if, _node) {
70     if (os_if->index == ifindex) {
71       return os_if;
72     }
73   }
74   return NULL;
75 }
76
77 /**
78  * Get the prefix of an interface fitting to a destination address
79  * @param destination destination address
80  * @param os_if interface data, NULL to search over all interfaces
81  * @return network prefix (including full host), NULL if not found
82  */
83 const struct os_interface_ip *
84 os_interface_generic_get_prefix_from_dst(struct netaddr *destination, struct os_interface *os_if) {
85   const struct os_interface_ip *ip;
86
87   if (os_if == NULL) {
88     avl_for_each_element(os_interface_get_tree(), os_if, _node) {
89       ip = os_interface_get_prefix_from_dst(destination, os_if);
90       if (ip) {
91         return ip;
92       }
93     }
94     return NULL;
95   }
96
97   avl_for_each_element(&os_if->addresses, ip, _node) {
98     if (netaddr_is_in_subnet(&ip->prefix, destination)) {
99       return ip;
100     }
101   }
102
103   return NULL;
104 }
105
106 /**
107  * Checks if the whole ACL is one maximum length address
108  * (or two, one for each possible address type).
109  * @param af_type requested address family
110  * @param filter filter to parse
111  * @return pointer to address to bind socket to, NULL if no match
112  */
113 static const struct netaddr *
114 _get_fixed_prefix(int af_type, struct netaddr_acl *filter) {
115   const struct netaddr *first, *second;
116   if (filter->reject_count > 0) {
117     return NULL;
118   }
119
120   if (filter->accept_count == 0 || filter->accept_count > 2) {
121     return NULL;
122   }
123
124   first = &filter->accept[0];
125   if (netaddr_get_prefix_length(first) != netaddr_get_maxprefix(first)) {
126     return NULL;
127   }
128
129   if (filter->accept_count == 2) {
130     second = &filter->accept[1];
131
132     if (netaddr_get_address_family(first) == netaddr_get_address_family(second)) {
133       /* must be two different address families */
134       return NULL;
135     }
136
137     if (netaddr_get_prefix_length(second) != netaddr_get_maxprefix(second)) {
138       return NULL;
139     }
140     if (netaddr_get_address_family(second) == af_type) {
141       return second;
142     }
143   }
144
145   if (netaddr_get_address_family(first) == af_type) {
146     return first;
147   }
148   return NULL;
149 }
150
151 /**
152  * Finds an IP on an/all interfaces that matches an exact (maximum length)
153  * filter rule
154  *
155  * @param af_type address family type to look for
156  * @param filter filter that must be matched
157  * @param os_if interface to look through, NULL for all interfaces
158  * @return pointer to address to bind socket to, NULL if no match
159  */
160 static const struct netaddr *
161 _get_exact_match_bindaddress(int af_type, struct netaddr_acl *filter, struct os_interface *os_if) {
162   struct os_interface_ip *ip;
163   const struct netaddr *result;
164   size_t i;
165
166   /* handle the 'all interfaces' case */
167   if (os_if == NULL) {
168     avl_for_each_element(os_interface_get_tree(), os_if, _node) {
169       if ((result = _get_exact_match_bindaddress(af_type, filter, os_if)) != NULL) {
170         return result;
171       }
172     }
173     return NULL;
174   }
175
176   /* run through all filters */
177   for (i = 0; i < filter->accept_count; i++) {
178     /* look for maximum prefix length filters */
179     if (netaddr_get_prefix_length(&filter->accept[i]) != netaddr_get_af_maxprefix(af_type)) {
180       continue;
181     }
182
183     /* run through all interface addresses and look for match */
184     avl_for_each_element(&os_if->addresses, ip, _node) {
185       if (netaddr_cmp(&ip->address, &filter->accept[i]) == 0) {
186         return &filter->accept[i];
187       }
188     }
189   }
190
191   /* no exact match found */
192   return NULL;
193 }
194
195 /**
196  * Finds an IP on an/all interfaces that matches a filter rule
197  *
198  * @param af_type address family type to look for
199  * @param filter filter that must be matched
200  * @param os_if interface to look through, NULL for all interfaces
201  * @return pointer to address to bind socket to, NULL if no match
202  */
203 static const struct netaddr *
204 _get_matching_bindaddress(int af_type, struct netaddr_acl *filter, struct os_interface *os_if) {
205   struct os_interface_ip *ip;
206   const struct netaddr *result;
207
208   /* handle the 'all interfaces' case */
209   if (os_if == NULL) {
210     avl_for_each_element(os_interface_get_tree(), os_if, _node) {
211       if ((result = _get_matching_bindaddress(af_type, filter, os_if)) != NULL) {
212         return result;
213       }
214     }
215     return NULL;
216   }
217
218   /* run through interface address list looking for filter match */
219   avl_for_each_element(&os_if->addresses, ip, _node) {
220     if (netaddr_get_address_family(&ip->address) != af_type) {
221       continue;
222     }
223
224     if (netaddr_acl_check_accept(filter, &ip->address)) {
225       return &ip->address;
226     }
227   }
228   return NULL;
229 }