Skip to content

Commit 8f18e67

Browse files
hdmdaviespcmoore
authored andcommitted
netlabel: Add an address family to domain hash entries.
The reason is to allow different labelling protocols for different address families with the same domain. This requires the addition of an address family attribute in the netlink communication protocol. It is used in several messages: NLBL_MGMT_C_ADD and NLBL_MGMT_C_ADDDEF take it as an optional attribute for the unlabelled protocol. It may be one of AF_INET, AF_INET6 or AF_UNSPEC (to specify both address families). If it is missing, it defaults to AF_UNSPEC. NLBL_MGMT_C_LISTALL and NLBL_MGMT_C_LISTDEF return it as part of the enumeration of each item. Addtionally, it may be sent to LISTDEF to specify which address family to return. Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent 96a8f7f commit 8f18e67

File tree

6 files changed

+192
-58
lines changed

6 files changed

+192
-58
lines changed

net/netlabel/netlabel_domainhash.c

Lines changed: 137 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ static DEFINE_SPINLOCK(netlbl_domhsh_lock);
5656
#define netlbl_domhsh_rcu_deref(p) \
5757
rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
5858
static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
59-
static struct netlbl_dom_map __rcu *netlbl_domhsh_def;
59+
static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
60+
static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6;
6061

6162
/*
6263
* Domain Hash Table Helper Functions
@@ -126,18 +127,26 @@ static u32 netlbl_domhsh_hash(const char *key)
126127
return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
127128
}
128129

130+
static bool netlbl_family_match(u16 f1, u16 f2)
131+
{
132+
return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC);
133+
}
134+
129135
/**
130136
* netlbl_domhsh_search - Search for a domain entry
131137
* @domain: the domain
138+
* @family: the address family
132139
*
133140
* Description:
134141
* Searches the domain hash table and returns a pointer to the hash table
135-
* entry if found, otherwise NULL is returned. The caller is responsible for
142+
* entry if found, otherwise NULL is returned. @family may be %AF_UNSPEC
143+
* which matches any address family entries. The caller is responsible for
136144
* ensuring that the hash table is protected with either a RCU read lock or the
137145
* hash table lock.
138146
*
139147
*/
140-
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
148+
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain,
149+
u16 family)
141150
{
142151
u32 bkt;
143152
struct list_head *bkt_list;
@@ -147,7 +156,9 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
147156
bkt = netlbl_domhsh_hash(domain);
148157
bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
149158
list_for_each_entry_rcu(iter, bkt_list, list)
150-
if (iter->valid && strcmp(iter->domain, domain) == 0)
159+
if (iter->valid &&
160+
netlbl_family_match(iter->family, family) &&
161+
strcmp(iter->domain, domain) == 0)
151162
return iter;
152163
}
153164

@@ -157,28 +168,37 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
157168
/**
158169
* netlbl_domhsh_search_def - Search for a domain entry
159170
* @domain: the domain
160-
* @def: return default if no match is found
171+
* @family: the address family
161172
*
162173
* Description:
163174
* Searches the domain hash table and returns a pointer to the hash table
164175
* entry if an exact match is found, if an exact match is not present in the
165176
* hash table then the default entry is returned if valid otherwise NULL is
166-
* returned. The caller is responsible ensuring that the hash table is
177+
* returned. @family may be %AF_UNSPEC which matches any address family
178+
* entries. The caller is responsible ensuring that the hash table is
167179
* protected with either a RCU read lock or the hash table lock.
168180
*
169181
*/
170-
static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
182+
static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain,
183+
u16 family)
171184
{
172185
struct netlbl_dom_map *entry;
173186

174-
entry = netlbl_domhsh_search(domain);
175-
if (entry == NULL) {
176-
entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def);
177-
if (entry != NULL && !entry->valid)
178-
entry = NULL;
187+
entry = netlbl_domhsh_search(domain, family);
188+
if (entry != NULL)
189+
return entry;
190+
if (family == AF_INET || family == AF_UNSPEC) {
191+
entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4);
192+
if (entry != NULL && entry->valid)
193+
return entry;
194+
}
195+
if (family == AF_INET6 || family == AF_UNSPEC) {
196+
entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6);
197+
if (entry != NULL && entry->valid)
198+
return entry;
179199
}
180200

181-
return entry;
201+
return NULL;
182202
}
183203

184204
/**
@@ -264,13 +284,19 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
264284
if (entry == NULL)
265285
return -EINVAL;
266286

287+
if (entry->family != AF_INET && entry->family != AF_INET6 &&
288+
(entry->family != AF_UNSPEC ||
289+
entry->def.type != NETLBL_NLTYPE_UNLABELED))
290+
return -EINVAL;
291+
267292
switch (entry->def.type) {
268293
case NETLBL_NLTYPE_UNLABELED:
269294
if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
270295
return -EINVAL;
271296
break;
272297
case NETLBL_NLTYPE_CIPSOV4:
273-
if (entry->def.cipso == NULL)
298+
if (entry->family != AF_INET ||
299+
entry->def.cipso == NULL)
274300
return -EINVAL;
275301
break;
276302
case NETLBL_NLTYPE_ADDRSELECT:
@@ -358,15 +384,18 @@ int __init netlbl_domhsh_init(u32 size)
358384
*
359385
* Description:
360386
* Adds a new entry to the domain hash table and handles any updates to the
361-
* lower level protocol handler (i.e. CIPSO). Returns zero on success,
362-
* negative on failure.
387+
* lower level protocol handler (i.e. CIPSO). @entry->family may be set to
388+
* %AF_UNSPEC which will add an entry that matches all address families. This
389+
* is only useful for the unlabelled type and will only succeed if there is no
390+
* existing entry for any address family with the same domain. Returns zero
391+
* on success, negative on failure.
363392
*
364393
*/
365394
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
366395
struct netlbl_audit *audit_info)
367396
{
368397
int ret_val = 0;
369-
struct netlbl_dom_map *entry_old;
398+
struct netlbl_dom_map *entry_old, *entry_b;
370399
struct netlbl_af4list *iter4;
371400
struct netlbl_af4list *tmp4;
372401
#if IS_ENABLED(CONFIG_IPV6)
@@ -385,9 +414,10 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
385414
rcu_read_lock();
386415
spin_lock(&netlbl_domhsh_lock);
387416
if (entry->domain != NULL)
388-
entry_old = netlbl_domhsh_search(entry->domain);
417+
entry_old = netlbl_domhsh_search(entry->domain, entry->family);
389418
else
390-
entry_old = netlbl_domhsh_search_def(entry->domain);
419+
entry_old = netlbl_domhsh_search_def(entry->domain,
420+
entry->family);
391421
if (entry_old == NULL) {
392422
entry->valid = 1;
393423

@@ -397,7 +427,41 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
397427
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
398428
} else {
399429
INIT_LIST_HEAD(&entry->list);
400-
rcu_assign_pointer(netlbl_domhsh_def, entry);
430+
switch (entry->family) {
431+
case AF_INET:
432+
rcu_assign_pointer(netlbl_domhsh_def_ipv4,
433+
entry);
434+
break;
435+
case AF_INET6:
436+
rcu_assign_pointer(netlbl_domhsh_def_ipv6,
437+
entry);
438+
break;
439+
case AF_UNSPEC:
440+
if (entry->def.type !=
441+
NETLBL_NLTYPE_UNLABELED) {
442+
ret_val = -EINVAL;
443+
goto add_return;
444+
}
445+
entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC);
446+
if (entry_b == NULL) {
447+
ret_val = -ENOMEM;
448+
goto add_return;
449+
}
450+
entry_b->family = AF_INET6;
451+
entry_b->def.type = NETLBL_NLTYPE_UNLABELED;
452+
entry_b->valid = 1;
453+
entry->family = AF_INET;
454+
rcu_assign_pointer(netlbl_domhsh_def_ipv4,
455+
entry);
456+
rcu_assign_pointer(netlbl_domhsh_def_ipv6,
457+
entry_b);
458+
break;
459+
default:
460+
/* Already checked in
461+
* netlbl_domhsh_validate(). */
462+
ret_val = -EINVAL;
463+
goto add_return;
464+
}
401465
}
402466

403467
if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
@@ -513,10 +577,12 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
513577
spin_lock(&netlbl_domhsh_lock);
514578
if (entry->valid) {
515579
entry->valid = 0;
516-
if (entry != rcu_dereference(netlbl_domhsh_def))
517-
list_del_rcu(&entry->list);
580+
if (entry == rcu_dereference(netlbl_domhsh_def_ipv4))
581+
RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL);
582+
else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6))
583+
RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL);
518584
else
519-
RCU_INIT_POINTER(netlbl_domhsh_def, NULL);
585+
list_del_rcu(&entry->list);
520586
} else
521587
ret_val = -ENOENT;
522588
spin_unlock(&netlbl_domhsh_lock);
@@ -583,9 +649,9 @@ int netlbl_domhsh_remove_af4(const char *domain,
583649
rcu_read_lock();
584650

585651
if (domain)
586-
entry_map = netlbl_domhsh_search(domain);
652+
entry_map = netlbl_domhsh_search(domain, AF_INET);
587653
else
588-
entry_map = netlbl_domhsh_search_def(domain);
654+
entry_map = netlbl_domhsh_search_def(domain, AF_INET);
589655
if (entry_map == NULL ||
590656
entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
591657
goto remove_af4_failure;
@@ -625,58 +691,84 @@ int netlbl_domhsh_remove_af4(const char *domain,
625691
/**
626692
* netlbl_domhsh_remove - Removes an entry from the domain hash table
627693
* @domain: the domain to remove
694+
* @family: address family
628695
* @audit_info: NetLabel audit information
629696
*
630697
* Description:
631698
* Removes an entry from the domain hash table and handles any updates to the
632-
* lower level protocol handler (i.e. CIPSO). Returns zero on success,
633-
* negative on failure.
699+
* lower level protocol handler (i.e. CIPSO). @family may be %AF_UNSPEC which
700+
* removes all address family entries. Returns zero on success, negative on
701+
* failure.
634702
*
635703
*/
636-
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
704+
int netlbl_domhsh_remove(const char *domain, u16 family,
705+
struct netlbl_audit *audit_info)
637706
{
638-
int ret_val;
707+
int ret_val = -EINVAL;
639708
struct netlbl_dom_map *entry;
640709

641710
rcu_read_lock();
642-
if (domain)
643-
entry = netlbl_domhsh_search(domain);
644-
else
645-
entry = netlbl_domhsh_search_def(domain);
646-
ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
711+
712+
if (family == AF_INET || family == AF_UNSPEC) {
713+
if (domain)
714+
entry = netlbl_domhsh_search(domain, AF_INET);
715+
else
716+
entry = netlbl_domhsh_search_def(domain, AF_INET);
717+
ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
718+
if (ret_val && ret_val != -ENOENT)
719+
goto done;
720+
}
721+
if (family == AF_INET6 || family == AF_UNSPEC) {
722+
int ret_val2;
723+
724+
if (domain)
725+
entry = netlbl_domhsh_search(domain, AF_INET6);
726+
else
727+
entry = netlbl_domhsh_search_def(domain, AF_INET6);
728+
ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
729+
if (ret_val2 != -ENOENT)
730+
ret_val = ret_val2;
731+
}
732+
done:
647733
rcu_read_unlock();
648734

649735
return ret_val;
650736
}
651737

652738
/**
653739
* netlbl_domhsh_remove_default - Removes the default entry from the table
740+
* @family: address family
654741
* @audit_info: NetLabel audit information
655742
*
656743
* Description:
657-
* Removes/resets the default entry for the domain hash table and handles any
658-
* updates to the lower level protocol handler (i.e. CIPSO). Returns zero on
659-
* success, non-zero on failure.
744+
* Removes/resets the default entry corresponding to @family from the domain
745+
* hash table and handles any updates to the lower level protocol handler
746+
* (i.e. CIPSO). @family may be %AF_UNSPEC which removes all address family
747+
* entries. Returns zero on success, negative on failure.
660748
*
661749
*/
662-
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
750+
int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
663751
{
664-
return netlbl_domhsh_remove(NULL, audit_info);
752+
return netlbl_domhsh_remove(NULL, family, audit_info);
665753
}
666754

667755
/**
668756
* netlbl_domhsh_getentry - Get an entry from the domain hash table
669757
* @domain: the domain name to search for
758+
* @family: address family
670759
*
671760
* Description:
672761
* Look through the domain hash table searching for an entry to match @domain,
673-
* return a pointer to a copy of the entry or NULL. The caller is responsible
674-
* for ensuring that rcu_read_[un]lock() is called.
762+
* with address family @family, return a pointer to a copy of the entry or
763+
* NULL. The caller is responsible for ensuring that rcu_read_[un]lock() is
764+
* called.
675765
*
676766
*/
677-
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
767+
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
678768
{
679-
return netlbl_domhsh_search_def(domain);
769+
if (family == AF_UNSPEC)
770+
return NULL;
771+
return netlbl_domhsh_search_def(domain, family);
680772
}
681773

682774
/**
@@ -696,7 +788,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
696788
struct netlbl_dom_map *dom_iter;
697789
struct netlbl_af4list *addr_iter;
698790

699-
dom_iter = netlbl_domhsh_search_def(domain);
791+
dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
700792
if (dom_iter == NULL)
701793
return NULL;
702794

@@ -726,7 +818,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
726818
struct netlbl_dom_map *dom_iter;
727819
struct netlbl_af6list *addr_iter;
728820

729-
dom_iter = netlbl_domhsh_search_def(domain);
821+
dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
730822
if (dom_iter == NULL)
731823
return NULL;
732824

net/netlabel/netlabel_domainhash.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct netlbl_domaddr6_map {
7070

7171
struct netlbl_dom_map {
7272
char *domain;
73+
u16 family;
7374
struct netlbl_dommap_def def;
7475

7576
u32 valid;
@@ -91,9 +92,10 @@ int netlbl_domhsh_remove_af4(const char *domain,
9192
const struct in_addr *addr,
9293
const struct in_addr *mask,
9394
struct netlbl_audit *audit_info);
94-
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
95-
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
96-
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
95+
int netlbl_domhsh_remove(const char *domain, u16 family,
96+
struct netlbl_audit *audit_info);
97+
int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info);
98+
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family);
9799
struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
98100
__be32 addr);
99101
#if IS_ENABLED(CONFIG_IPV6)

0 commit comments

Comments
 (0)