summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2011-05-03 07:32:58 +0000
committertrasz <trasz@FreeBSD.org>2011-05-03 07:32:58 +0000
commit752ffacc695ca593f052934a2881be57dec92419 (patch)
tree83f996220eb5127d50beee996ac337678876fccc /sys/kern
parenta857ffe8345f7868b2fb1f9f4b0606558ecba6ca (diff)
downloadFreeBSD-src-752ffacc695ca593f052934a2881be57dec92419.zip
FreeBSD-src-752ffacc695ca593f052934a2881be57dec92419.tar.gz
Change the way rctl interfaces with jails by introducing prison_racct
structure, which acts as a proxy between them. This makes jail rules persistent, i.e. they can be added before jail gets created, and they don't disappear when the jail gets destroyed.
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_jail.c116
-rw-r--r--sys/kern/kern_racct.c14
-rw-r--r--sys/kern/kern_rctl.c95
3 files changed, 153 insertions, 72 deletions
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index 22b97e8..5850ad1 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/racct.h>
-#include <sys/rctl.h>
+#include <sys/refcount.h>
#include <sys/sx.h>
#include <sys/sysent.h>
#include <sys/namei.h>
@@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$");
#define DEFAULT_HOSTUUID "00000000-0000-0000-0000-000000000000"
MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
+MALLOC_DEFINE(M_PRISON_RACCT, "prison_racct", "Prison racct structures");
/* Keep struct prison prison0 and some code in kern_jail_set() readable. */
#ifdef INET
@@ -114,10 +115,11 @@ struct prison prison0 = {
};
MTX_SYSINIT(prison0, &prison0.pr_mtx, "jail mutex", MTX_DEF);
-/* allprison and lastprid are protected by allprison_lock. */
+/* allprison, allprison_racct and lastprid are protected by allprison_lock. */
struct sx allprison_lock;
SX_SYSINIT(allprison_lock, &allprison_lock, "allprison");
struct prisonlist allprison = TAILQ_HEAD_INITIALIZER(allprison);
+LIST_HEAD(, prison_racct) allprison_racct;
int lastprid = 0;
static int do_jail_attach(struct thread *td, struct prison *pr);
@@ -125,6 +127,10 @@ static void prison_complete(void *context, int pending);
static void prison_deref(struct prison *pr, int flags);
static char *prison_path(struct prison *pr1, struct prison *pr2);
static void prison_remove_one(struct prison *pr);
+#ifdef RACCT
+static void prison_racct_attach(struct prison *pr);
+static void prison_racct_detach(struct prison *pr);
+#endif
#ifdef INET
static int _prison_check_ip4(struct prison *pr, struct in_addr *ia);
static int prison_restrict_ip4(struct prison *pr, struct in_addr *newip4);
@@ -1197,7 +1203,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
root = mypr->pr_root;
vref(root);
}
- racct_create(&pr->pr_racct);
strlcpy(pr->pr_hostuuid, DEFAULT_HOSTUUID, HOSTUUIDLEN);
pr->pr_flags |= PR_HOST;
#if defined(INET) || defined(INET6)
@@ -1657,6 +1662,11 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags;
mtx_unlock(&pr->pr_mtx);
+#ifdef RACCT
+ if (created)
+ prison_racct_attach(pr);
+#endif
+
/* Locks may have prevented a complete restriction of child IP
* addresses. If so, allocate some more memory and try again.
*/
@@ -2533,10 +2543,9 @@ prison_deref(struct prison *pr, int flags)
if (pr->pr_cpuset != NULL)
cpuset_rel(pr->pr_cpuset);
osd_jail_exit(pr);
-#ifdef RCTL
- rctl_racct_release(pr->pr_racct);
+#ifdef RACCT
+ prison_racct_detach(pr);
#endif
- racct_destroy(&pr->pr_racct);
free(pr, M_PRISON);
/* Removing a prison frees a reference on its parent. */
@@ -4277,14 +4286,103 @@ void
prison_racct_foreach(void (*callback)(struct racct *racct,
void *arg2, void *arg3), void *arg2, void *arg3)
{
- struct prison *pr;
+ struct prison_racct *prr;
sx_slock(&allprison_lock);
- TAILQ_FOREACH(pr, &allprison, pr_list)
- (callback)(pr->pr_racct, arg2, arg3);
+ LIST_FOREACH(prr, &allprison_racct, prr_next)
+ (callback)(prr->prr_racct, arg2, arg3);
sx_sunlock(&allprison_lock);
}
+static struct prison_racct *
+prison_racct_find_locked(const char *name)
+{
+ struct prison_racct *prr;
+
+ sx_assert(&allprison_lock, SA_XLOCKED);
+
+ if (name[0] == '\0' || strlen(name) >= MAXHOSTNAMELEN)
+ return (NULL);
+
+ LIST_FOREACH(prr, &allprison_racct, prr_next) {
+ if (strcmp(name, prr->prr_name) != 0)
+ continue;
+
+ /* Found prison_racct with a matching name? */
+ prison_racct_hold(prr);
+ return (prr);
+ }
+
+ /* Add new prison_racct. */
+ prr = malloc(sizeof(*prr), M_PRISON_RACCT, M_ZERO | M_WAITOK);
+ racct_create(&prr->prr_racct);
+
+ strcpy(prr->prr_name, name);
+ refcount_init(&prr->prr_refcount, 1);
+ LIST_INSERT_HEAD(&allprison_racct, prr, prr_next);
+
+ return (prr);
+}
+
+struct prison_racct *
+prison_racct_find(const char *name)
+{
+ struct prison_racct *prr;
+
+ sx_xlock(&allprison_lock);
+ prr = prison_racct_find_locked(name);
+ sx_xunlock(&allprison_lock);
+ return (prr);
+}
+
+void
+prison_racct_hold(struct prison_racct *prr)
+{
+
+ refcount_acquire(&prr->prr_refcount);
+}
+
+void
+prison_racct_free(struct prison_racct *prr)
+{
+ int old;
+
+ old = prr->prr_refcount;
+ if (old > 1 && atomic_cmpset_int(&prr->prr_refcount, old, old - 1))
+ return;
+
+ sx_xlock(&allprison_lock);
+ if (refcount_release(&prr->prr_refcount)) {
+ racct_destroy(&prr->prr_racct);
+ LIST_REMOVE(prr, prr_next);
+ sx_xunlock(&allprison_lock);
+ free(prr, M_PRISON_RACCT);
+
+ return;
+ }
+ sx_xunlock(&allprison_lock);
+}
+
+#ifdef RACCT
+static void
+prison_racct_attach(struct prison *pr)
+{
+ struct prison_racct *prr;
+
+ prr = prison_racct_find_locked(pr->pr_name);
+ KASSERT(prr != NULL, ("cannot find prison_racct"));
+
+ pr->pr_prison_racct = prr;
+}
+
+static void
+prison_racct_detach(struct prison *pr)
+{
+ prison_racct_free(pr->pr_prison_racct);
+ pr->pr_prison_racct = NULL;
+}
+#endif /* RACCT */
+
#ifdef DDB
static void
diff --git a/sys/kern/kern_racct.c b/sys/kern/kern_racct.c
index 5778bfe..98bd9c5 100644
--- a/sys/kern/kern_racct.c
+++ b/sys/kern/kern_racct.c
@@ -313,7 +313,8 @@ racct_add_cred_locked(struct ucred *cred, int resource, uint64_t amount)
racct_alloc_resource(cred->cr_ruidinfo->ui_racct, resource, amount);
for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent)
- racct_alloc_resource(pr->pr_racct, resource, amount);
+ racct_alloc_resource(pr->pr_prison_racct->prr_racct, resource,
+ amount);
racct_alloc_resource(cred->cr_loginclass->lc_racct, resource, amount);
}
@@ -522,7 +523,8 @@ racct_sub_cred_locked(struct ucred *cred, int resource, uint64_t amount)
racct_alloc_resource(cred->cr_ruidinfo->ui_racct, resource, -amount);
for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent)
- racct_alloc_resource(pr->pr_racct, resource, -amount);
+ racct_alloc_resource(pr->pr_prison_racct->prr_racct, resource,
+ -amount);
racct_alloc_resource(cred->cr_loginclass->lc_racct, resource, -amount);
}
@@ -669,9 +671,11 @@ racct_proc_ucred_changed(struct proc *p, struct ucred *oldcred,
}
if (newpr != oldpr) {
for (pr = oldpr; pr != NULL; pr = pr->pr_parent)
- racct_sub_racct(pr->pr_racct, p->p_racct);
+ racct_sub_racct(pr->pr_prison_racct->prr_racct,
+ p->p_racct);
for (pr = newpr; pr != NULL; pr = pr->pr_parent)
- racct_add_racct(pr->pr_racct, p->p_racct);
+ racct_add_racct(pr->pr_prison_racct->prr_racct,
+ p->p_racct);
}
mtx_unlock(&racct_lock);
@@ -744,7 +748,7 @@ racct_init(void)
/*
* XXX: Move this somewhere.
*/
- racct_create(&prison0.pr_racct);
+ prison0.pr_prison_racct = prison_racct_find("0");
}
SYSINIT(racct, SI_SUB_RACCT, SI_ORDER_FIRST, racct_init, NULL);
diff --git a/sys/kern/kern_rctl.c b/sys/kern/kern_rctl.c
index 88a971c..2d43bdc 100644
--- a/sys/kern/kern_rctl.c
+++ b/sys/kern/kern_rctl.c
@@ -241,7 +241,8 @@ rctl_available_resource(const struct proc *p, const struct rctl_rule *rule)
break;
case RCTL_SUBJECT_TYPE_JAIL:
available = rule->rr_amount -
- cred->cr_prison->pr_racct->r_resources[resource];
+ cred->cr_prison->pr_prison_racct->prr_racct->
+ r_resources[resource];
break;
default:
panic("rctl_compute_available: unknown per %d",
@@ -327,7 +328,7 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount)
printf("rctl: rule \"%s\" matched by pid %d "
"(%s), uid %d, jail %s\n", sbuf_data(&sb),
p->p_pid, p->p_comm, p->p_ucred->cr_uid,
- p->p_ucred->cr_prison->pr_name);
+ p->p_ucred->cr_prison->pr_prison_racct->prr_name);
sbuf_delete(&sb);
free(buf, M_RCTL);
link->rrl_exceeded = 1;
@@ -346,7 +347,7 @@ rctl_enforce(struct proc *p, int resource, uint64_t amount)
rctl_rule_to_sbuf(&sb, rule);
sbuf_printf(&sb, " pid=%d ruid=%d jail=%s",
p->p_pid, p->p_ucred->cr_ruid,
- p->p_ucred->cr_prison->pr_name);
+ p->p_ucred->cr_prison->pr_prison_racct->prr_name);
sbuf_finish(&sb);
devctl_notify_f("RCTL", "rule", "matched",
sbuf_data(&sb), M_NOWAIT);
@@ -481,9 +482,9 @@ rctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter)
return (0);
break;
case RCTL_SUBJECT_TYPE_JAIL:
- if (filter->rr_subject.rs_prison != NULL &&
- rule->rr_subject.rs_prison !=
- filter->rr_subject.rs_prison)
+ if (filter->rr_subject.rs_prison_racct != NULL &&
+ rule->rr_subject.rs_prison_racct !=
+ filter->rr_subject.rs_prison_racct)
return (0);
break;
default:
@@ -635,7 +636,10 @@ rctl_rule_acquire_subject(struct rctl_rule *rule)
switch (rule->rr_subject_type) {
case RCTL_SUBJECT_TYPE_UNDEFINED:
case RCTL_SUBJECT_TYPE_PROCESS:
+ break;
case RCTL_SUBJECT_TYPE_JAIL:
+ if (rule->rr_subject.rs_prison_racct != NULL)
+ prison_racct_hold(rule->rr_subject.rs_prison_racct);
break;
case RCTL_SUBJECT_TYPE_USER:
if (rule->rr_subject.rs_uip != NULL)
@@ -658,7 +662,10 @@ rctl_rule_release_subject(struct rctl_rule *rule)
switch (rule->rr_subject_type) {
case RCTL_SUBJECT_TYPE_UNDEFINED:
case RCTL_SUBJECT_TYPE_PROCESS:
+ break;
case RCTL_SUBJECT_TYPE_JAIL:
+ if (rule->rr_subject.rs_prison_racct != NULL)
+ prison_racct_free(rule->rr_subject.rs_prison_racct);
break;
case RCTL_SUBJECT_TYPE_USER:
if (rule->rr_subject.rs_uip != NULL)
@@ -686,7 +693,7 @@ rctl_rule_alloc(int flags)
rule->rr_subject.rs_proc = NULL;
rule->rr_subject.rs_uip = NULL;
rule->rr_subject.rs_loginclass = NULL;
- rule->rr_subject.rs_prison = NULL;
+ rule->rr_subject.rs_prison_racct = NULL;
rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED;
rule->rr_resource = RACCT_UNDEFINED;
rule->rr_action = RCTL_ACTION_UNDEFINED;
@@ -708,7 +715,7 @@ rctl_rule_duplicate(const struct rctl_rule *rule, int flags)
copy->rr_subject.rs_proc = rule->rr_subject.rs_proc;
copy->rr_subject.rs_uip = rule->rr_subject.rs_uip;
copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass;
- copy->rr_subject.rs_prison = rule->rr_subject.rs_prison;
+ copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct;
copy->rr_per = rule->rr_per;
copy->rr_resource = rule->rr_resource;
copy->rr_action = rule->rr_action;
@@ -784,7 +791,7 @@ rctl_rule_fully_specified(const struct rctl_rule *rule)
return (0);
break;
case RCTL_SUBJECT_TYPE_JAIL:
- if (rule->rr_subject.rs_prison == NULL)
+ if (rule->rr_subject.rs_prison_racct == NULL)
return (0);
break;
default:
@@ -833,7 +840,7 @@ rctl_string_to_rule(char *rulestr, struct rctl_rule **rulep)
rule->rr_subject.rs_proc = NULL;
rule->rr_subject.rs_uip = NULL;
rule->rr_subject.rs_loginclass = NULL;
- rule->rr_subject.rs_prison = NULL;
+ rule->rr_subject.rs_prison_racct = NULL;
} else {
switch (rule->rr_subject_type) {
case RCTL_SUBJECT_TYPE_UNDEFINED:
@@ -866,23 +873,12 @@ rctl_string_to_rule(char *rulestr, struct rctl_rule **rulep)
}
break;
case RCTL_SUBJECT_TYPE_JAIL:
- rule->rr_subject.rs_prison =
- prison_find_name(&prison0, subject_idstr);
- if (rule->rr_subject.rs_prison == NULL) {
- /*
- * No jail with that name; try with the JID.
- */
- error = str2id(subject_idstr, &id);
- if (error != 0)
- goto out;
- rule->rr_subject.rs_prison = prison_find(id);
- if (rule->rr_subject.rs_prison == NULL) {
- error = ESRCH;
- goto out;
- }
+ rule->rr_subject.rs_prison_racct =
+ prison_racct_find(subject_idstr);
+ if (rule->rr_subject.rs_prison_racct == NULL) {
+ error = ENAMETOOLONG;
+ goto out;
}
- /* prison_find() returns with mutex held. */
- mtx_unlock(&rule->rr_subject.rs_prison->pr_mtx);
break;
default:
panic("rctl_string_to_rule: unknown subject type %d",
@@ -944,6 +940,7 @@ rctl_rule_add(struct rctl_rule *rule)
struct ucred *cred;
struct uidinfo *uip;
struct prison *pr;
+ struct prison_racct *prr;
struct loginclass *lc;
struct rctl_rule *rule2;
int match;
@@ -1008,9 +1005,9 @@ rctl_rule_add(struct rctl_rule *rule)
break;
case RCTL_SUBJECT_TYPE_JAIL:
- pr = rule->rr_subject.rs_prison;
- KASSERT(pr != NULL, ("rctl_rule_add: NULL pr"));
- rctl_racct_add_rule(pr->pr_racct, rule);
+ prr = rule->rr_subject.rs_prison_racct;
+ KASSERT(prr != NULL, ("rctl_rule_add: NULL pr"));
+ rctl_racct_add_rule(prr->prr_racct, rule);
break;
default:
@@ -1040,7 +1037,7 @@ rctl_rule_add(struct rctl_rule *rule)
case RCTL_SUBJECT_TYPE_JAIL:
match = 0;
for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) {
- if (pr == rule->rr_subject.rs_prison) {
+ if (pr->pr_prison_racct == rule->rr_subject.rs_prison_racct) {
match = 1;
break;
}
@@ -1144,11 +1141,11 @@ rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule)
rule->rr_subject.rs_loginclass->lc_name);
break;
case RCTL_SUBJECT_TYPE_JAIL:
- if (rule->rr_subject.rs_prison == NULL)
+ if (rule->rr_subject.rs_prison_racct == NULL)
sbuf_printf(sb, ":");
else
sbuf_printf(sb, "%s:",
- rule->rr_subject.rs_prison->pr_name);
+ rule->rr_subject.rs_prison_racct->prr_name);
break;
default:
panic("rctl_rule_to_sbuf: unknown subject type %d",
@@ -1245,7 +1242,7 @@ rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap)
struct proc *p;
struct uidinfo *uip;
struct loginclass *lc;
- struct prison *pr;
+ struct prison_racct *prr;
error = priv_check(td, PRIV_RCTL_GET_RACCT);
if (error != 0)
@@ -1256,11 +1253,9 @@ rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap)
return (error);
sx_slock(&allproc_lock);
- sx_slock(&allprison_lock);
error = rctl_string_to_rule(inputstr, &filter);
free(inputstr, M_RCTL);
if (error != 0) {
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
@@ -1295,19 +1290,18 @@ rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap)
outputsbuf = rctl_racct_to_sbuf(lc->lc_racct, 1);
break;
case RCTL_SUBJECT_TYPE_JAIL:
- pr = filter->rr_subject.rs_prison;
- if (pr == NULL) {
+ prr = filter->rr_subject.rs_prison_racct;
+ if (prr == NULL) {
error = EINVAL;
goto out;
}
- outputsbuf = rctl_racct_to_sbuf(pr->pr_racct, 1);
+ outputsbuf = rctl_racct_to_sbuf(prr->prr_racct, 1);
break;
default:
error = EINVAL;
}
out:
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
if (error != 0)
return (error);
@@ -1354,11 +1348,9 @@ rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap)
return (error);
sx_slock(&allproc_lock);
- sx_slock(&allprison_lock);
error = rctl_string_to_rule(inputstr, &filter);
free(inputstr, M_RCTL);
if (error != 0) {
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
@@ -1406,7 +1398,6 @@ again:
error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen);
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
free(buf, M_RCTL);
return (error);
@@ -1431,30 +1422,25 @@ rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap)
return (error);
sx_slock(&allproc_lock);
- sx_slock(&allprison_lock);
error = rctl_string_to_rule(inputstr, &filter);
free(inputstr, M_RCTL);
if (error != 0) {
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_UNDEFINED) {
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (EINVAL);
}
if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_PROCESS) {
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (EOPNOTSUPP);
}
if (filter->rr_subject.rs_proc == NULL) {
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (EINVAL);
}
@@ -1486,7 +1472,6 @@ again:
error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen);
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
free(buf, M_RCTL);
return (error);
@@ -1508,11 +1493,9 @@ rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap)
return (error);
sx_slock(&allproc_lock);
- sx_slock(&allprison_lock);
error = rctl_string_to_rule(inputstr, &rule);
free(inputstr, M_RCTL);
if (error != 0) {
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
@@ -1532,7 +1515,6 @@ rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap)
out:
rctl_rule_release(rule);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
@@ -1553,18 +1535,15 @@ rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap)
return (error);
sx_slock(&allproc_lock);
- sx_slock(&allprison_lock);
error = rctl_string_to_rule(inputstr, &filter);
free(inputstr, M_RCTL);
if (error != 0) {
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
}
error = rctl_rule_remove(filter);
rctl_rule_release(filter);
- sx_sunlock(&allprison_lock);
sx_sunlock(&allproc_lock);
return (error);
@@ -1580,12 +1559,12 @@ rctl_proc_ucred_changed(struct proc *p, struct ucred *newcred)
struct rctl_rule_link *link, *newlink;
struct uidinfo *newuip;
struct loginclass *newlc;
- struct prison *newpr;
+ struct prison_racct *newprr;
LIST_HEAD(, rctl_rule_link) newrules;
newuip = newcred->cr_ruidinfo;
newlc = newcred->cr_loginclass;
- newpr = newcred->cr_prison;
+ newprr = newcred->cr_prison->pr_prison_racct;
LIST_INIT(&newrules);
@@ -1605,7 +1584,7 @@ again:
rulecnt++;
LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next)
rulecnt++;
- LIST_FOREACH(link, &newpr->pr_racct->r_rule_links, rrl_next)
+ LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next)
rulecnt++;
rw_runlock(&rctl_lock);
@@ -1655,7 +1634,7 @@ again:
rulecnt--;
}
- LIST_FOREACH(link, &newpr->pr_racct->r_rule_links, rrl_next) {
+ LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) {
if (newlink == NULL)
goto goaround;
rctl_rule_acquire(link->rrl_rule);
OpenPOWER on IntegriCloud