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