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 | * | |
82c21bfa | 9 | * Author: Paul Moore <paul@paul-moore.com> |
61e10682 PM |
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 | |
d484ff15 | 27 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
61e10682 PM |
28 | * |
29 | */ | |
30 | ||
31 | #include <linux/types.h> | |
32 | #include <linux/rcupdate.h> | |
33 | #include <linux/list.h> | |
34 | #include <linux/spinlock.h> | |
35 | #include <linux/in.h> | |
36 | #include <linux/in6.h> | |
37 | #include <linux/ip.h> | |
38 | #include <linux/ipv6.h> | |
39 | #include <net/ip.h> | |
40 | #include <net/ipv6.h> | |
63c41688 | 41 | #include <linux/audit.h> |
61e10682 PM |
42 | |
43 | #include "netlabel_addrlist.h" | |
44 | ||
45 | /* | |
46 | * Address List Functions | |
47 | */ | |
48 | ||
49 | /** | |
50 | * netlbl_af4list_search - Search for a matching IPv4 address entry | |
51 | * @addr: IPv4 address | |
52 | * @head: the list head | |
53 | * | |
54 | * Description: | |
55 | * Searches the IPv4 address list given by @head. If a matching address entry | |
56 | * is found it is returned, otherwise NULL is returned. The caller is | |
57 | * responsible for calling the rcu_read_[un]lock() functions. | |
58 | * | |
59 | */ | |
60 | struct netlbl_af4list *netlbl_af4list_search(__be32 addr, | |
61 | struct list_head *head) | |
62 | { | |
63 | struct netlbl_af4list *iter; | |
64 | ||
65 | list_for_each_entry_rcu(iter, head, list) | |
66 | if (iter->valid && (addr & iter->mask) == iter->addr) | |
67 | return iter; | |
68 | ||
69 | return NULL; | |
70 | } | |
71 | ||
63c41688 PM |
72 | /** |
73 | * netlbl_af4list_search_exact - Search for an exact IPv4 address entry | |
74 | * @addr: IPv4 address | |
75 | * @mask: IPv4 address mask | |
76 | * @head: the list head | |
77 | * | |
78 | * Description: | |
79 | * Searches the IPv4 address list given by @head. If an exact match if found | |
80 | * it is returned, otherwise NULL is returned. The caller is responsible for | |
81 | * calling the rcu_read_[un]lock() functions. | |
82 | * | |
83 | */ | |
84 | struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr, | |
85 | __be32 mask, | |
86 | struct list_head *head) | |
87 | { | |
88 | struct netlbl_af4list *iter; | |
89 | ||
90 | list_for_each_entry_rcu(iter, head, list) | |
91 | if (iter->valid && iter->addr == addr && iter->mask == mask) | |
92 | return iter; | |
93 | ||
94 | return NULL; | |
95 | } | |
96 | ||
97 | ||
dfd56b8b | 98 | #if IS_ENABLED(CONFIG_IPV6) |
61e10682 PM |
99 | /** |
100 | * netlbl_af6list_search - Search for a matching IPv6 address entry | |
101 | * @addr: IPv6 address | |
102 | * @head: the list head | |
103 | * | |
104 | * Description: | |
105 | * Searches the IPv6 address list given by @head. If a matching address entry | |
106 | * is found it is returned, otherwise NULL is returned. The caller is | |
107 | * responsible for calling the rcu_read_[un]lock() functions. | |
108 | * | |
109 | */ | |
110 | struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr, | |
111 | struct list_head *head) | |
112 | { | |
113 | struct netlbl_af6list *iter; | |
114 | ||
115 | list_for_each_entry_rcu(iter, head, list) | |
116 | if (iter->valid && | |
117 | ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0) | |
118 | return iter; | |
119 | ||
120 | return NULL; | |
121 | } | |
63c41688 PM |
122 | |
123 | /** | |
124 | * netlbl_af6list_search_exact - Search for an exact IPv6 address entry | |
125 | * @addr: IPv6 address | |
126 | * @mask: IPv6 address mask | |
127 | * @head: the list head | |
128 | * | |
129 | * Description: | |
130 | * Searches the IPv6 address list given by @head. If an exact match if found | |
131 | * it is returned, otherwise NULL is returned. The caller is responsible for | |
132 | * calling the rcu_read_[un]lock() functions. | |
133 | * | |
134 | */ | |
135 | struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr, | |
136 | const struct in6_addr *mask, | |
137 | struct list_head *head) | |
138 | { | |
139 | struct netlbl_af6list *iter; | |
140 | ||
141 | list_for_each_entry_rcu(iter, head, list) | |
142 | if (iter->valid && | |
143 | ipv6_addr_equal(&iter->addr, addr) && | |
144 | ipv6_addr_equal(&iter->mask, mask)) | |
145 | return iter; | |
146 | ||
147 | return NULL; | |
148 | } | |
61e10682 PM |
149 | #endif /* IPv6 */ |
150 | ||
151 | /** | |
152 | * netlbl_af4list_add - Add a new IPv4 address entry to a list | |
153 | * @entry: address entry | |
154 | * @head: the list head | |
155 | * | |
156 | * Description: | |
157 | * Add a new address entry to the list pointed to by @head. On success zero is | |
158 | * returned, otherwise a negative value is returned. The caller is responsible | |
159 | * for calling the necessary locking functions. | |
160 | * | |
161 | */ | |
162 | int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head) | |
163 | { | |
164 | struct netlbl_af4list *iter; | |
165 | ||
166 | iter = netlbl_af4list_search(entry->addr, head); | |
167 | if (iter != NULL && | |
168 | iter->addr == entry->addr && iter->mask == entry->mask) | |
169 | return -EEXIST; | |
170 | ||
171 | /* in order to speed up address searches through the list (the common | |
172 | * case) we need to keep the list in order based on the size of the | |
173 | * address mask such that the entry with the widest mask (smallest | |
174 | * numerical value) appears first in the list */ | |
175 | list_for_each_entry_rcu(iter, head, list) | |
176 | if (iter->valid && | |
177 | ntohl(entry->mask) > ntohl(iter->mask)) { | |
178 | __list_add_rcu(&entry->list, | |
179 | iter->list.prev, | |
180 | &iter->list); | |
181 | return 0; | |
182 | } | |
183 | list_add_tail_rcu(&entry->list, head); | |
184 | return 0; | |
185 | } | |
186 | ||
dfd56b8b | 187 | #if IS_ENABLED(CONFIG_IPV6) |
61e10682 PM |
188 | /** |
189 | * netlbl_af6list_add - Add a new IPv6 address entry to a list | |
190 | * @entry: address entry | |
191 | * @head: the list head | |
192 | * | |
193 | * Description: | |
194 | * Add a new address entry to the list pointed to by @head. On success zero is | |
195 | * returned, otherwise a negative value is returned. The caller is responsible | |
196 | * for calling the necessary locking functions. | |
197 | * | |
198 | */ | |
199 | int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head) | |
200 | { | |
201 | struct netlbl_af6list *iter; | |
202 | ||
203 | iter = netlbl_af6list_search(&entry->addr, head); | |
204 | if (iter != NULL && | |
205 | ipv6_addr_equal(&iter->addr, &entry->addr) && | |
206 | ipv6_addr_equal(&iter->mask, &entry->mask)) | |
207 | return -EEXIST; | |
208 | ||
209 | /* in order to speed up address searches through the list (the common | |
210 | * case) we need to keep the list in order based on the size of the | |
211 | * address mask such that the entry with the widest mask (smallest | |
212 | * numerical value) appears first in the list */ | |
213 | list_for_each_entry_rcu(iter, head, list) | |
214 | if (iter->valid && | |
215 | ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) { | |
216 | __list_add_rcu(&entry->list, | |
217 | iter->list.prev, | |
218 | &iter->list); | |
219 | return 0; | |
220 | } | |
221 | list_add_tail_rcu(&entry->list, head); | |
222 | return 0; | |
223 | } | |
224 | #endif /* IPv6 */ | |
225 | ||
226 | /** | |
227 | * netlbl_af4list_remove_entry - Remove an IPv4 address entry | |
228 | * @entry: address entry | |
229 | * | |
230 | * Description: | |
231 | * Remove the specified IP address entry. The caller is responsible for | |
232 | * calling the necessary locking functions. | |
233 | * | |
234 | */ | |
235 | void netlbl_af4list_remove_entry(struct netlbl_af4list *entry) | |
236 | { | |
237 | entry->valid = 0; | |
238 | list_del_rcu(&entry->list); | |
239 | } | |
240 | ||
241 | /** | |
242 | * netlbl_af4list_remove - Remove an IPv4 address entry | |
243 | * @addr: IP address | |
244 | * @mask: IP address mask | |
245 | * @head: the list head | |
246 | * | |
247 | * Description: | |
248 | * Remove an IP address entry from the list pointed to by @head. Returns the | |
249 | * entry on success, NULL on failure. The caller is responsible for calling | |
250 | * the necessary locking functions. | |
251 | * | |
252 | */ | |
253 | struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask, | |
254 | struct list_head *head) | |
255 | { | |
256 | struct netlbl_af4list *entry; | |
257 | ||
50b2ff1b PM |
258 | entry = netlbl_af4list_search_exact(addr, mask, head); |
259 | if (entry == NULL) | |
260 | return NULL; | |
261 | netlbl_af4list_remove_entry(entry); | |
262 | return entry; | |
61e10682 PM |
263 | } |
264 | ||
dfd56b8b | 265 | #if IS_ENABLED(CONFIG_IPV6) |
61e10682 PM |
266 | /** |
267 | * netlbl_af6list_remove_entry - Remove an IPv6 address entry | |
268 | * @entry: address entry | |
269 | * | |
270 | * Description: | |
271 | * Remove the specified IP address entry. The caller is responsible for | |
272 | * calling the necessary locking functions. | |
273 | * | |
274 | */ | |
275 | void netlbl_af6list_remove_entry(struct netlbl_af6list *entry) | |
276 | { | |
277 | entry->valid = 0; | |
278 | list_del_rcu(&entry->list); | |
279 | } | |
280 | ||
281 | /** | |
282 | * netlbl_af6list_remove - Remove an IPv6 address entry | |
283 | * @addr: IP address | |
284 | * @mask: IP address mask | |
285 | * @head: the list head | |
286 | * | |
287 | * Description: | |
288 | * Remove an IP address entry from the list pointed to by @head. Returns the | |
289 | * entry on success, NULL on failure. The caller is responsible for calling | |
290 | * the necessary locking functions. | |
291 | * | |
292 | */ | |
293 | struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr, | |
294 | const struct in6_addr *mask, | |
295 | struct list_head *head) | |
296 | { | |
297 | struct netlbl_af6list *entry; | |
298 | ||
50b2ff1b PM |
299 | entry = netlbl_af6list_search_exact(addr, mask, head); |
300 | if (entry == NULL) | |
301 | return NULL; | |
302 | netlbl_af6list_remove_entry(entry); | |
303 | return entry; | |
61e10682 PM |
304 | } |
305 | #endif /* IPv6 */ | |
63c41688 PM |
306 | |
307 | /* | |
308 | * Audit Helper Functions | |
309 | */ | |
310 | ||
47b676c0 | 311 | #ifdef CONFIG_AUDIT |
63c41688 PM |
312 | /** |
313 | * netlbl_af4list_audit_addr - Audit an IPv4 address | |
314 | * @audit_buf: audit buffer | |
315 | * @src: true if source address, false if destination | |
316 | * @dev: network interface | |
317 | * @addr: IP address | |
318 | * @mask: IP address mask | |
319 | * | |
320 | * Description: | |
321 | * Write the IPv4 address and address mask, if necessary, to @audit_buf. | |
322 | * | |
323 | */ | |
324 | void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf, | |
325 | int src, const char *dev, | |
326 | __be32 addr, __be32 mask) | |
327 | { | |
328 | u32 mask_val = ntohl(mask); | |
329 | char *dir = (src ? "src" : "dst"); | |
330 | ||
331 | if (dev != NULL) | |
332 | audit_log_format(audit_buf, " netif=%s", dev); | |
21454aaa | 333 | audit_log_format(audit_buf, " %s=%pI4", dir, &addr); |
63c41688 PM |
334 | if (mask_val != 0xffffffff) { |
335 | u32 mask_len = 0; | |
336 | while (mask_val > 0) { | |
337 | mask_val <<= 1; | |
338 | mask_len++; | |
339 | } | |
340 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
341 | } | |
342 | } | |
343 | ||
dfd56b8b | 344 | #if IS_ENABLED(CONFIG_IPV6) |
63c41688 PM |
345 | /** |
346 | * netlbl_af6list_audit_addr - Audit an IPv6 address | |
347 | * @audit_buf: audit buffer | |
348 | * @src: true if source address, false if destination | |
349 | * @dev: network interface | |
350 | * @addr: IP address | |
351 | * @mask: IP address mask | |
352 | * | |
353 | * Description: | |
354 | * Write the IPv6 address and address mask, if necessary, to @audit_buf. | |
355 | * | |
356 | */ | |
357 | void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf, | |
358 | int src, | |
359 | const char *dev, | |
360 | const struct in6_addr *addr, | |
361 | const struct in6_addr *mask) | |
362 | { | |
363 | char *dir = (src ? "src" : "dst"); | |
364 | ||
365 | if (dev != NULL) | |
366 | audit_log_format(audit_buf, " netif=%s", dev); | |
5b095d98 | 367 | audit_log_format(audit_buf, " %s=%pI6", dir, addr); |
63c41688 PM |
368 | if (ntohl(mask->s6_addr32[3]) != 0xffffffff) { |
369 | u32 mask_len = 0; | |
370 | u32 mask_val; | |
371 | int iter = -1; | |
372 | while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff) | |
373 | mask_len += 32; | |
374 | mask_val = ntohl(mask->s6_addr32[iter]); | |
375 | while (mask_val > 0) { | |
376 | mask_val <<= 1; | |
377 | mask_len++; | |
378 | } | |
379 | audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); | |
380 | } | |
381 | } | |
382 | #endif /* IPv6 */ | |
47b676c0 | 383 | #endif /* CONFIG_AUDIT */ |