Commit | Line | Data |
---|---|---|
61e10682 PM |
1 | /* |
2 | * NetLabel Network Address Lists | |
3 | * | |
4 | * This file contains network address list functions used to manage ordered | |
5 | * lists of network addresses for use by the NetLabel subsystem. The NetLabel | |
6 | * system manages static and dynamic label mappings for network protocols such | |
7 | * as CIPSO and RIPSO. | |
8 | * | |
9 | * Author: Paul Moore <paul.moore@hp.com> | |
10 | * | |
11 | */ | |
12 | ||
13 | /* | |
14 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
24 | * the GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
29 | * | |
30 | */ | |
31 | ||
32 | #include <linux/types.h> | |
33 | #include <linux/rcupdate.h> | |
34 | #include <linux/list.h> | |
35 | #include <linux/spinlock.h> | |
36 | #include <linux/in.h> | |
37 | #include <linux/in6.h> | |
38 | #include <linux/ip.h> | |
39 | #include <linux/ipv6.h> | |
40 | #include <net/ip.h> | |
41 | #include <net/ipv6.h> | |
63c41688 | 42 | #include <linux/audit.h> |
61e10682 PM |
43 | |
44 | #include "netlabel_addrlist.h" | |
45 | ||
46 | /* | |
47 | * Address List Functions | |
48 | */ | |
49 | ||
50 | /** | |
51 | * netlbl_af4list_search - Search for a matching IPv4 address entry | |
52 | * @addr: IPv4 address | |
53 | * @head: the list head | |
54 | * | |
55 | * Description: | |
56 | * Searches the IPv4 address list given by @head. If a matching address entry | |
57 | * is found it is returned, otherwise NULL is returned. The caller is | |
58 | * responsible for calling the rcu_read_[un]lock() functions. | |
59 | * | |
60 | */ | |
61 | struct netlbl_af4list *netlbl_af4list_search(__be32 addr, | |
62 | struct list_head *head) | |
63 | { | |
64 | struct netlbl_af4list *iter; | |
65 | ||
66 | list_for_each_entry_rcu(iter, head, list) | |
67 | if (iter->valid && (addr & iter->mask) == iter->addr) | |
68 | return iter; | |
69 | ||
70 | return NULL; | |
71 | } | |
72 | ||
63c41688 PM |
73 | /** |
74 | * netlbl_af4list_search_exact - Search for an exact IPv4 address entry | |
75 | * @addr: IPv4 address | |
76 | * @mask: IPv4 address mask | |
77 | * @head: the list head | |
78 | * | |
79 | * Description: | |
80 | * Searches the IPv4 address list given by @head. If an exact match if found | |
81 | * it is returned, otherwise NULL is returned. The caller is responsible for | |
82 | * calling the rcu_read_[un]lock() functions. | |
83 | * | |
84 | */ | |
85 | struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr, | |
86 | __be32 mask, | |
87 | struct list_head *head) | |
88 | { | |
89 | struct netlbl_af4list *iter; | |
90 | ||
91 | list_for_each_entry_rcu(iter, head, list) | |
92 | if (iter->valid && iter->addr == addr && iter->mask == mask) | |
93 | return iter; | |
94 | ||
95 | return NULL; | |
96 | } | |
97 | ||
98 | ||
61e10682 PM |
99 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
100 | /** | |
101 | * netlbl_af6list_search - Search for a matching IPv6 address entry | |
102 | * @addr: IPv6 address | |
103 | * @head: the list head | |
104 | * | |
105 | * Description: | |
106 | * Searches the IPv6 address list given by @head. If a matching address entry | |
107 | * is found it is returned, otherwise NULL is returned. The caller is | |
108 | * responsible for calling the rcu_read_[un]lock() functions. | |
109 | * | |
110 | */ | |
111 | struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr, | |
112 | struct list_head *head) | |
113 | { | |
114 | struct netlbl_af6list *iter; | |
115 | ||
116 | list_for_each_entry_rcu(iter, head, list) | |
117 | if (iter->valid && | |
118 | ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0) | |
119 | return iter; | |
120 | ||
121 | return NULL; | |
122 | } | |
63c41688 PM |
123 | |
124 | /** | |
125 | * netlbl_af6list_search_exact - Search for an exact IPv6 address entry | |
126 | * @addr: IPv6 address | |
127 | * @mask: IPv6 address mask | |
128 | * @head: the list head | |
129 | * | |
130 | * Description: | |
131 | * Searches the IPv6 address list given by @head. If an exact match if found | |
132 | * it is returned, otherwise NULL is returned. The caller is responsible for | |
133 | * calling the rcu_read_[un]lock() functions. | |
134 | * | |
135 | */ | |
136 | struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr, | |
137 | const struct in6_addr *mask, | |
138 | struct list_head *head) | |
139 | { | |
140 | struct netlbl_af6list *iter; | |
141 | ||
142 | list_for_each_entry_rcu(iter, head, list) | |
143 | if (iter->valid && | |
144 | ipv6_addr_equal(&iter->addr, addr) && | |
145 | ipv6_addr_equal(&iter->mask, mask)) | |
146 | return iter; | |
147 | ||
148 | return NULL; | |
149 | } | |
61e10682 PM |
150 | #endif /* IPv6 */ |
151 | ||
152 | /** | |
153 | * netlbl_af4list_add - Add a new IPv4 address entry to a list | |
154 | * @entry: address entry | |
155 | * @head: the list head | |
156 | * | |
157 | * Description: | |
158 | * Add a new address entry to the list pointed to by @head. On success zero is | |
159 | * returned, otherwise a negative value is returned. The caller is responsible | |
160 | * for calling the necessary locking functions. | |
161 | * | |
162 | */ | |
163 | int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head) | |
164 | { | |
165 | struct netlbl_af4list *iter; | |
166 | ||
167 | iter = netlbl_af4list_search(entry->addr, head); | |
168 | if (iter != NULL && | |
169 | iter->addr == entry->addr && iter->mask == entry->mask) | |
170 | return -EEXIST; | |
171 | ||
172 | /* in order to speed up address searches through the list (the common | |
173 | * case) we need to keep the list in order based on the size of the | |
174 | * address mask such that the entry with the widest mask (smallest | |
175 | * numerical value) appears first in the list */ | |
176 | list_for_each_entry_rcu(iter, head, list) | |
177 | if (iter->valid && | |
178 | ntohl(entry->mask) > ntohl(iter->mask)) { | |
179 | __list_add_rcu(&entry->list, | |
180 | iter->list.prev, | |
181 | &iter->list); | |
182 | return 0; | |
183 | } | |
184 | list_add_tail_rcu(&entry->list, head); | |
185 | return 0; | |
186 | } | |
187 | ||
188 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
189 | /** | |
190 | * netlbl_af6list_add - Add a new IPv6 address entry to a list | |
191 | * @entry: address entry | |
192 | * @head: the list head | |
193 | * | |
194 | * Description: | |
195 | * Add a new address entry to the list pointed to by @head. On success zero is | |
196 | * returned, otherwise a negative value is returned. The caller is responsible | |
197 | * for calling the necessary locking functions. | |
198 | * | |
199 | */ | |
200 | int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head) | |
201 | { | |
202 | struct netlbl_af6list *iter; | |
203 | ||
204 | iter = netlbl_af6list_search(&entry->addr, head); | |
205 | if (iter != NULL && | |
206 | ipv6_addr_equal(&iter->addr, &entry->addr) && | |
207 | ipv6_addr_equal(&iter->mask, &entry->mask)) | |
208 | return -EEXIST; | |
209 | ||
210 | /* in order to speed up address searches through the list (the common | |
211 | * case) we need to keep the list in order based on the size of the | |
212 | * address mask such that the entry with the widest mask (smallest | |
213 | * numerical value) appears first in the list */ | |
214 | list_for_each_entry_rcu(iter, head, list) | |
215 | if (iter->valid && | |
216 | ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) { | |
217 | __list_add_rcu(&entry->list, | |
218 | iter->list.prev, | |
219 | &iter->list); | |
220 | return 0; | |
221 | } | |
222 | list_add_tail_rcu(&entry->list, head); | |
223 | return 0; | |
224 | } | |
225 | #endif /* IPv6 */ | |
226 | ||
227 | /** | |
228 | * netlbl_af4list_remove_entry - Remove an IPv4 address entry | |
229 | * @entry: address entry | |
230 | * | |
231 | * Description: | |
232 | * Remove the specified IP address entry. The caller is responsible for | |
233 | * calling the necessary locking functions. | |
234 | * | |
235 | */ | |
236 | void netlbl_af4list_remove_entry(struct netlbl_af4list *entry) | |
237 | { | |
238 | entry->valid = 0; | |
239 | list_del_rcu(&entry->list); | |
240 | } | |
241 | ||
242 | /** | |
243 | * netlbl_af4list_remove - Remove an IPv4 address entry | |
244 | * @addr: IP address | |
245 | * @mask: IP address mask | |
246 | * @head: the list head | |
247 | * | |
248 | * Description: | |
249 | * Remove an IP address entry from the list pointed to by @head. Returns the | |
250 | * entry on success, NULL on failure. The caller is responsible for calling | |
251 | * the necessary locking functions. | |
252 | * | |
253 | */ | |
254 | struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask, | |
255 | struct list_head *head) | |
256 | { | |
257 | struct netlbl_af4list *entry; | |
258 | ||
259 | entry = netlbl_af4list_search(addr, head); | |
260 | if (entry != NULL && entry->addr == addr && entry->mask == mask) { | |
261 | netlbl_af4list_remove_entry(entry); | |
262 | return entry; | |
263 | } | |
264 | ||
265 | return NULL; | |
266 | } | |
267 | ||
268 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
269 | /** | |
270 | * netlbl_af6list_remove_entry - Remove an IPv6 address entry | |
271 | * @entry: address entry | |
272 | * | |
273 | * Description: | |
274 | * Remove the specified IP address entry. The caller is responsible for | |
275 | * calling the necessary locking functions. | |
276 | * | |
277 | */ | |
278 | void netlbl_af6list_remove_entry(struct netlbl_af6list *entry) | |
279 | { | |
280 | entry->valid = 0; | |
281 | list_del_rcu(&entry->list); | |
282 | } | |
283 | ||
284 | /** | |
285 | * netlbl_af6list_remove - Remove an IPv6 address entry | |
286 | * @addr: IP address | |
287 | * @mask: IP address mask | |
288 | * @head: the list head | |
289 | * | |
290 | * Description: | |
291 | * Remove an IP address entry from the list pointed to by @head. Returns the | |
292 | * entry on success, NULL on failure. The caller is responsible for calling | |
293 | * the necessary locking functions. | |
294 | * | |
295 | */ | |
296 | struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr, | |
297 | const struct in6_addr *mask, | |
298 | struct list_head *head) | |
299 | { | |
300 | struct netlbl_af6list *entry; | |
301 | ||
302 | entry = netlbl_af6list_search(addr, head); | |
303 | if (entry != NULL && | |
304 | ipv6_addr_equal(&entry->addr, addr) && | |
305 | ipv6_addr_equal(&entry->mask, mask)) { | |
306 | netlbl_af6list_remove_entry(entry); | |
307 | return entry; | |
308 | } | |
309 | ||
310 | return NULL; | |
311 | } | |
312 | #endif /* IPv6 */ | |
63c41688 PM |
313 | |
314 | /* | |
315 | * Audit Helper Functions | |
316 | */ | |
317 | ||
318 | /** | |
319 | * netlbl_af4list_audit_addr - Audit an IPv4 address | |
320 | * @audit_buf: audit buffer | |
321 | * @src: true if source address, false if destination | |
322 | * @dev: network interface | |
323 | * @addr: IP address | |
324 | * @mask: IP address mask | |
325 | * | |
326 | * Description: | |
327 | * Write the IPv4 address and address mask, if necessary, to @audit_buf. | |
328 | * | |
329 | */ | |
330 | void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf, | |
331 | int src, const char *dev, | |
332 | __be32 addr, __be32 mask) | |
333 | { | |
334 | u32 mask_val = ntohl(mask); | |
335 | char *dir = (src ? "src" : "dst"); | |
336 | ||
337 | if (dev != NULL) | |
338 | audit_log_format(audit_buf, " netif=%s", dev); | |
339 | audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr)); | |
340 | if (mask_val != 0xffffffff) { | |
341 | u32 mask_len = 0; | |
342 | while (mask_val > 0) { | |
343 | mask_val <<= 1; | |
344 | mask_len++; | |
345 | } | |
346 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
347 | } | |
348 | } | |
349 | ||
350 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
351 | /** | |
352 | * netlbl_af6list_audit_addr - Audit an IPv6 address | |
353 | * @audit_buf: audit buffer | |
354 | * @src: true if source address, false if destination | |
355 | * @dev: network interface | |
356 | * @addr: IP address | |
357 | * @mask: IP address mask | |
358 | * | |
359 | * Description: | |
360 | * Write the IPv6 address and address mask, if necessary, to @audit_buf. | |
361 | * | |
362 | */ | |
363 | void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf, | |
364 | int src, | |
365 | const char *dev, | |
366 | const struct in6_addr *addr, | |
367 | const struct in6_addr *mask) | |
368 | { | |
369 | char *dir = (src ? "src" : "dst"); | |
370 | ||
371 | if (dev != NULL) | |
372 | audit_log_format(audit_buf, " netif=%s", dev); | |
373 | audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr)); | |
374 | if (ntohl(mask->s6_addr32[3]) != 0xffffffff) { | |
375 | u32 mask_len = 0; | |
376 | u32 mask_val; | |
377 | int iter = -1; | |
378 | while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff) | |
379 | mask_len += 32; | |
380 | mask_val = ntohl(mask->s6_addr32[iter]); | |
381 | while (mask_val > 0) { | |
382 | mask_val <<= 1; | |
383 | mask_len++; | |
384 | } | |
385 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
386 | } | |
387 | } | |
388 | #endif /* IPv6 */ |