linux/net/netlabel/netlabel_addrlist.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   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 *
  10 * Author: Paul Moore <paul@paul-moore.com>
  11 */
  12
  13/*
  14 * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
  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>
  27#include <linux/audit.h>
  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 */
  46struct 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
  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 */
  70struct 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
  84#if IS_ENABLED(CONFIG_IPV6)
  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 */
  96struct 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}
 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 */
 121struct 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}
 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 */
 148int 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
 173#if IS_ENABLED(CONFIG_IPV6)
 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 */
 185int 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 */
 221void 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 */
 239struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
 240                                             struct list_head *head)
 241{
 242        struct netlbl_af4list *entry;
 243
 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;
 249}
 250
 251#if IS_ENABLED(CONFIG_IPV6)
 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 */
 261void 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 */
 279struct 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
 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;
 290}
 291#endif /* IPv6 */
 292
 293/*
 294 * Audit Helper Functions
 295 */
 296
 297#ifdef CONFIG_AUDIT
 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 */
 310void 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);
 319        audit_log_format(audit_buf, " %s=%pI4", dir, &addr);
 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
 330#if IS_ENABLED(CONFIG_IPV6)
 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 */
 343void 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);
 353        audit_log_format(audit_buf, " %s=%pI6", dir, addr);
 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 */
 369#endif /* CONFIG_AUDIT */
 370