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 | ||
50b2ff1b PM |
259 | entry = netlbl_af4list_search_exact(addr, mask, head); |
260 | if (entry == NULL) | |
261 | return NULL; | |
262 | netlbl_af4list_remove_entry(entry); | |
263 | return entry; | |
61e10682 PM |
264 | } |
265 | ||
266 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
267 | /** | |
268 | * netlbl_af6list_remove_entry - Remove an IPv6 address entry | |
269 | * @entry: address entry | |
270 | * | |
271 | * Description: | |
272 | * Remove the specified IP address entry. The caller is responsible for | |
273 | * calling the necessary locking functions. | |
274 | * | |
275 | */ | |
276 | void netlbl_af6list_remove_entry(struct netlbl_af6list *entry) | |
277 | { | |
278 | entry->valid = 0; | |
279 | list_del_rcu(&entry->list); | |
280 | } | |
281 | ||
282 | /** | |
283 | * netlbl_af6list_remove - Remove an IPv6 address entry | |
284 | * @addr: IP address | |
285 | * @mask: IP address mask | |
286 | * @head: the list head | |
287 | * | |
288 | * Description: | |
289 | * Remove an IP address entry from the list pointed to by @head. Returns the | |
290 | * entry on success, NULL on failure. The caller is responsible for calling | |
291 | * the necessary locking functions. | |
292 | * | |
293 | */ | |
294 | struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr, | |
295 | const struct in6_addr *mask, | |
296 | struct list_head *head) | |
297 | { | |
298 | struct netlbl_af6list *entry; | |
299 | ||
50b2ff1b PM |
300 | entry = netlbl_af6list_search_exact(addr, mask, head); |
301 | if (entry == NULL) | |
302 | return NULL; | |
303 | netlbl_af6list_remove_entry(entry); | |
304 | return entry; | |
61e10682 PM |
305 | } |
306 | #endif /* IPv6 */ | |
63c41688 PM |
307 | |
308 | /* | |
309 | * Audit Helper Functions | |
310 | */ | |
311 | ||
47b676c0 | 312 | #ifdef CONFIG_AUDIT |
63c41688 PM |
313 | /** |
314 | * netlbl_af4list_audit_addr - Audit an IPv4 address | |
315 | * @audit_buf: audit buffer | |
316 | * @src: true if source address, false if destination | |
317 | * @dev: network interface | |
318 | * @addr: IP address | |
319 | * @mask: IP address mask | |
320 | * | |
321 | * Description: | |
322 | * Write the IPv4 address and address mask, if necessary, to @audit_buf. | |
323 | * | |
324 | */ | |
325 | void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf, | |
326 | int src, const char *dev, | |
327 | __be32 addr, __be32 mask) | |
328 | { | |
329 | u32 mask_val = ntohl(mask); | |
330 | char *dir = (src ? "src" : "dst"); | |
331 | ||
332 | if (dev != NULL) | |
333 | audit_log_format(audit_buf, " netif=%s", dev); | |
21454aaa | 334 | audit_log_format(audit_buf, " %s=%pI4", dir, &addr); |
63c41688 PM |
335 | if (mask_val != 0xffffffff) { |
336 | u32 mask_len = 0; | |
337 | while (mask_val > 0) { | |
338 | mask_val <<= 1; | |
339 | mask_len++; | |
340 | } | |
341 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
342 | } | |
343 | } | |
344 | ||
345 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | |
346 | /** | |
347 | * netlbl_af6list_audit_addr - Audit an IPv6 address | |
348 | * @audit_buf: audit buffer | |
349 | * @src: true if source address, false if destination | |
350 | * @dev: network interface | |
351 | * @addr: IP address | |
352 | * @mask: IP address mask | |
353 | * | |
354 | * Description: | |
355 | * Write the IPv6 address and address mask, if necessary, to @audit_buf. | |
356 | * | |
357 | */ | |
358 | void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf, | |
359 | int src, | |
360 | const char *dev, | |
361 | const struct in6_addr *addr, | |
362 | const struct in6_addr *mask) | |
363 | { | |
364 | char *dir = (src ? "src" : "dst"); | |
365 | ||
366 | if (dev != NULL) | |
367 | audit_log_format(audit_buf, " netif=%s", dev); | |
5b095d98 | 368 | audit_log_format(audit_buf, " %s=%pI6", dir, addr); |
63c41688 PM |
369 | if (ntohl(mask->s6_addr32[3]) != 0xffffffff) { |
370 | u32 mask_len = 0; | |
371 | u32 mask_val; | |
372 | int iter = -1; | |
373 | while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff) | |
374 | mask_len += 32; | |
375 | mask_val = ntohl(mask->s6_addr32[iter]); | |
376 | while (mask_val > 0) { | |
377 | mask_val <<= 1; | |
378 | mask_len++; | |
379 | } | |
380 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
381 | } | |
382 | } | |
383 | #endif /* IPv6 */ | |
47b676c0 | 384 | #endif /* CONFIG_AUDIT */ |