// SPDX-License-Identifier: GPL-2.0-or-later1/*2* NetLabel Network Address Lists3*4* This file contains network address list functions used to manage ordered5* lists of network addresses for use by the NetLabel subsystem. The NetLabel6* system manages static and dynamic label mappings for network protocols such7* as CIPSO and RIPSO.8*9* Author: Paul Moore <[email protected]>10*/1112/*13* (c) Copyright Hewlett-Packard Development Company, L.P., 200814*/1516#include <linux/types.h>17#include <linux/rcupdate.h>18#include <linux/list.h>19#include <linux/spinlock.h>20#include <linux/in.h>21#include <linux/in6.h>22#include <linux/ip.h>23#include <linux/ipv6.h>24#include <net/ip.h>25#include <net/ipv6.h>26#include <linux/audit.h>2728#include "netlabel_addrlist.h"2930/*31* Address List Functions32*/3334/**35* netlbl_af4list_search - Search for a matching IPv4 address entry36* @addr: IPv4 address37* @head: the list head38*39* Description:40* Searches the IPv4 address list given by @head. If a matching address entry41* is found it is returned, otherwise NULL is returned. The caller is42* responsible for calling the rcu_read_[un]lock() functions.43*44*/45struct netlbl_af4list *netlbl_af4list_search(__be32 addr,46struct list_head *head)47{48struct netlbl_af4list *iter;4950list_for_each_entry_rcu(iter, head, list)51if (iter->valid && (addr & iter->mask) == iter->addr)52return iter;5354return NULL;55}5657/**58* netlbl_af4list_search_exact - Search for an exact IPv4 address entry59* @addr: IPv4 address60* @mask: IPv4 address mask61* @head: the list head62*63* Description:64* Searches the IPv4 address list given by @head. If an exact match if found65* it is returned, otherwise NULL is returned. The caller is responsible for66* calling the rcu_read_[un]lock() functions.67*68*/69struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,70__be32 mask,71struct list_head *head)72{73struct netlbl_af4list *iter;7475list_for_each_entry_rcu(iter, head, list)76if (iter->valid && iter->addr == addr && iter->mask == mask)77return iter;7879return NULL;80}818283#if IS_ENABLED(CONFIG_IPV6)84/**85* netlbl_af6list_search - Search for a matching IPv6 address entry86* @addr: IPv6 address87* @head: the list head88*89* Description:90* Searches the IPv6 address list given by @head. If a matching address entry91* is found it is returned, otherwise NULL is returned. The caller is92* responsible for calling the rcu_read_[un]lock() functions.93*94*/95struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,96struct list_head *head)97{98struct netlbl_af6list *iter;99100list_for_each_entry_rcu(iter, head, list)101if (iter->valid &&102ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)103return iter;104105return NULL;106}107108/**109* netlbl_af6list_search_exact - Search for an exact IPv6 address entry110* @addr: IPv6 address111* @mask: IPv6 address mask112* @head: the list head113*114* Description:115* Searches the IPv6 address list given by @head. If an exact match if found116* it is returned, otherwise NULL is returned. The caller is responsible for117* calling the rcu_read_[un]lock() functions.118*119*/120struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,121const struct in6_addr *mask,122struct list_head *head)123{124struct netlbl_af6list *iter;125126list_for_each_entry_rcu(iter, head, list)127if (iter->valid &&128ipv6_addr_equal(&iter->addr, addr) &&129ipv6_addr_equal(&iter->mask, mask))130return iter;131132return NULL;133}134#endif /* IPv6 */135136/**137* netlbl_af4list_add - Add a new IPv4 address entry to a list138* @entry: address entry139* @head: the list head140*141* Description:142* Add a new address entry to the list pointed to by @head. On success zero is143* returned, otherwise a negative value is returned. The caller is responsible144* for calling the necessary locking functions.145*146*/147int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head)148{149struct netlbl_af4list *iter;150151iter = netlbl_af4list_search(entry->addr, head);152if (iter != NULL &&153iter->addr == entry->addr && iter->mask == entry->mask)154return -EEXIST;155156/* in order to speed up address searches through the list (the common157* case) we need to keep the list in order based on the size of the158* address mask such that the entry with the widest mask (smallest159* numerical value) appears first in the list */160list_for_each_entry_rcu(iter, head, list)161if (iter->valid &&162ntohl(entry->mask) > ntohl(iter->mask)) {163__list_add_rcu(&entry->list,164iter->list.prev,165&iter->list);166return 0;167}168list_add_tail_rcu(&entry->list, head);169return 0;170}171172#if IS_ENABLED(CONFIG_IPV6)173/**174* netlbl_af6list_add - Add a new IPv6 address entry to a list175* @entry: address entry176* @head: the list head177*178* Description:179* Add a new address entry to the list pointed to by @head. On success zero is180* returned, otherwise a negative value is returned. The caller is responsible181* for calling the necessary locking functions.182*183*/184int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head)185{186struct netlbl_af6list *iter;187188iter = netlbl_af6list_search(&entry->addr, head);189if (iter != NULL &&190ipv6_addr_equal(&iter->addr, &entry->addr) &&191ipv6_addr_equal(&iter->mask, &entry->mask))192return -EEXIST;193194/* in order to speed up address searches through the list (the common195* case) we need to keep the list in order based on the size of the196* address mask such that the entry with the widest mask (smallest197* numerical value) appears first in the list */198list_for_each_entry_rcu(iter, head, list)199if (iter->valid &&200ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {201__list_add_rcu(&entry->list,202iter->list.prev,203&iter->list);204return 0;205}206list_add_tail_rcu(&entry->list, head);207return 0;208}209#endif /* IPv6 */210211/**212* netlbl_af4list_remove_entry - Remove an IPv4 address entry213* @entry: address entry214*215* Description:216* Remove the specified IP address entry. The caller is responsible for217* calling the necessary locking functions.218*219*/220void netlbl_af4list_remove_entry(struct netlbl_af4list *entry)221{222entry->valid = 0;223list_del_rcu(&entry->list);224}225226/**227* netlbl_af4list_remove - Remove an IPv4 address entry228* @addr: IP address229* @mask: IP address mask230* @head: the list head231*232* Description:233* Remove an IP address entry from the list pointed to by @head. Returns the234* entry on success, NULL on failure. The caller is responsible for calling235* the necessary locking functions.236*237*/238struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,239struct list_head *head)240{241struct netlbl_af4list *entry;242243entry = netlbl_af4list_search_exact(addr, mask, head);244if (entry == NULL)245return NULL;246netlbl_af4list_remove_entry(entry);247return entry;248}249250#if IS_ENABLED(CONFIG_IPV6)251/**252* netlbl_af6list_remove_entry - Remove an IPv6 address entry253* @entry: address entry254*255* Description:256* Remove the specified IP address entry. The caller is responsible for257* calling the necessary locking functions.258*259*/260void netlbl_af6list_remove_entry(struct netlbl_af6list *entry)261{262entry->valid = 0;263list_del_rcu(&entry->list);264}265266/**267* netlbl_af6list_remove - Remove an IPv6 address entry268* @addr: IP address269* @mask: IP address mask270* @head: the list head271*272* Description:273* Remove an IP address entry from the list pointed to by @head. Returns the274* entry on success, NULL on failure. The caller is responsible for calling275* the necessary locking functions.276*277*/278struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,279const struct in6_addr *mask,280struct list_head *head)281{282struct netlbl_af6list *entry;283284entry = netlbl_af6list_search_exact(addr, mask, head);285if (entry == NULL)286return NULL;287netlbl_af6list_remove_entry(entry);288return entry;289}290#endif /* IPv6 */291292/*293* Audit Helper Functions294*/295296#ifdef CONFIG_AUDIT297/**298* netlbl_af4list_audit_addr - Audit an IPv4 address299* @audit_buf: audit buffer300* @src: true if source address, false if destination301* @dev: network interface302* @addr: IP address303* @mask: IP address mask304*305* Description:306* Write the IPv4 address and address mask, if necessary, to @audit_buf.307*308*/309void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,310int src, const char *dev,311__be32 addr, __be32 mask)312{313u32 mask_val = ntohl(mask);314char *dir = (src ? "src" : "dst");315316if (dev != NULL)317audit_log_format(audit_buf, " netif=%s", dev);318audit_log_format(audit_buf, " %s=%pI4", dir, &addr);319if (mask_val != 0xffffffff) {320u32 mask_len = 0;321while (mask_val > 0) {322mask_val <<= 1;323mask_len++;324}325audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);326}327}328329#if IS_ENABLED(CONFIG_IPV6)330/**331* netlbl_af6list_audit_addr - Audit an IPv6 address332* @audit_buf: audit buffer333* @src: true if source address, false if destination334* @dev: network interface335* @addr: IP address336* @mask: IP address mask337*338* Description:339* Write the IPv6 address and address mask, if necessary, to @audit_buf.340*341*/342void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,343int src,344const char *dev,345const struct in6_addr *addr,346const struct in6_addr *mask)347{348char *dir = (src ? "src" : "dst");349350if (dev != NULL)351audit_log_format(audit_buf, " netif=%s", dev);352audit_log_format(audit_buf, " %s=%pI6", dir, addr);353if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {354u32 mask_len = 0;355u32 mask_val;356int iter = -1;357while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)358mask_len += 32;359mask_val = ntohl(mask->s6_addr32[iter]);360while (mask_val > 0) {361mask_val <<= 1;362mask_len++;363}364audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);365}366}367#endif /* IPv6 */368#endif /* CONFIG_AUDIT */369370371