mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-26 07:36:19 +00:00
62bc956786
Use "virNWFilterObj" as a prefix for any external API in virnwfilterobj
389 lines
9.7 KiB
C
389 lines
9.7 KiB
C
/*
|
|
* virnwfilterobj.c: network filter object processing
|
|
* (derived from nwfilter_conf.c)
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <dirent.h>
|
|
|
|
#include "datatypes.h"
|
|
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "virlog.h"
|
|
#include "virnwfilterobj.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NWFILTER
|
|
|
|
VIR_LOG_INIT("conf.virnwfilterobj");
|
|
|
|
|
|
void
|
|
virNWFilterObjFree(virNWFilterObjPtr obj)
|
|
{
|
|
if (!obj)
|
|
return;
|
|
|
|
virNWFilterDefFree(obj->def);
|
|
virNWFilterDefFree(obj->newDef);
|
|
|
|
virMutexDestroy(&obj->lock);
|
|
|
|
VIR_FREE(obj);
|
|
}
|
|
|
|
|
|
void
|
|
virNWFilterObjListFree(virNWFilterObjListPtr nwfilters)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < nwfilters->count; i++)
|
|
virNWFilterObjFree(nwfilters->objs[i]);
|
|
VIR_FREE(nwfilters->objs);
|
|
nwfilters->count = 0;
|
|
}
|
|
|
|
|
|
void
|
|
virNWFilterObjRemove(virNWFilterObjListPtr nwfilters,
|
|
virNWFilterObjPtr nwfilter)
|
|
{
|
|
size_t i;
|
|
|
|
virNWFilterObjUnlock(nwfilter);
|
|
|
|
for (i = 0; i < nwfilters->count; i++) {
|
|
virNWFilterObjLock(nwfilters->objs[i]);
|
|
if (nwfilters->objs[i] == nwfilter) {
|
|
virNWFilterObjUnlock(nwfilters->objs[i]);
|
|
virNWFilterObjFree(nwfilters->objs[i]);
|
|
|
|
VIR_DELETE_ELEMENT(nwfilters->objs, i, nwfilters->count);
|
|
break;
|
|
}
|
|
virNWFilterObjUnlock(nwfilters->objs[i]);
|
|
}
|
|
}
|
|
|
|
|
|
virNWFilterObjPtr
|
|
virNWFilterObjFindByUUID(virNWFilterObjListPtr nwfilters,
|
|
const unsigned char *uuid)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < nwfilters->count; i++) {
|
|
virNWFilterObjLock(nwfilters->objs[i]);
|
|
if (!memcmp(nwfilters->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN))
|
|
return nwfilters->objs[i];
|
|
virNWFilterObjUnlock(nwfilters->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virNWFilterObjPtr
|
|
virNWFilterObjFindByName(virNWFilterObjListPtr nwfilters,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < nwfilters->count; i++) {
|
|
virNWFilterObjLock(nwfilters->objs[i]);
|
|
if (STREQ_NULLABLE(nwfilters->objs[i]->def->name, name))
|
|
return nwfilters->objs[i];
|
|
virNWFilterObjUnlock(nwfilters->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
_virNWFilterObjDefLoopDetect(virNWFilterObjListPtr nwfilters,
|
|
virNWFilterDefPtr def,
|
|
const char *filtername)
|
|
{
|
|
int rc = 0;
|
|
size_t i;
|
|
virNWFilterEntryPtr entry;
|
|
virNWFilterObjPtr obj;
|
|
|
|
if (!def)
|
|
return 0;
|
|
|
|
for (i = 0; i < def->nentries; i++) {
|
|
entry = def->filterEntries[i];
|
|
if (entry->include) {
|
|
|
|
if (STREQ(filtername, entry->include->filterref)) {
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
obj = virNWFilterObjFindByName(nwfilters,
|
|
entry->include->filterref);
|
|
if (obj) {
|
|
rc = _virNWFilterObjDefLoopDetect(nwfilters,
|
|
obj->def, filtername);
|
|
|
|
virNWFilterObjUnlock(obj);
|
|
if (rc < 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* virNWFilterObjDefLoopDetect:
|
|
* @nwfilters : the nwfilters to search
|
|
* @def : the filter definition that may add a loop and is to be tested
|
|
*
|
|
* Detect a loop introduced through the filters being able to
|
|
* reference each other.
|
|
*
|
|
* Returns 0 in case no loop was detected, -1 otherwise.
|
|
*/
|
|
static int
|
|
virNWFilterObjDefLoopDetect(virNWFilterObjListPtr nwfilters,
|
|
virNWFilterDefPtr def)
|
|
{
|
|
return _virNWFilterObjDefLoopDetect(nwfilters, def, def->name);
|
|
}
|
|
|
|
|
|
int
|
|
virNWFilterObjTestUnassignDef(virNWFilterObjPtr nwfilter)
|
|
{
|
|
int rc = 0;
|
|
|
|
nwfilter->wantRemoved = 1;
|
|
/* trigger the update on VMs referencing the filter */
|
|
if (virNWFilterTriggerVMFilterRebuild())
|
|
rc = -1;
|
|
|
|
nwfilter->wantRemoved = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static bool
|
|
virNWFilterDefEqual(const virNWFilterDef *def1,
|
|
virNWFilterDefPtr def2,
|
|
bool cmpUUIDs)
|
|
{
|
|
bool ret = false;
|
|
unsigned char rem_uuid[VIR_UUID_BUFLEN];
|
|
char *xml1, *xml2 = NULL;
|
|
|
|
if (!cmpUUIDs) {
|
|
/* make sure the UUIDs are equal */
|
|
memcpy(rem_uuid, def2->uuid, sizeof(rem_uuid));
|
|
memcpy(def2->uuid, def1->uuid, sizeof(def2->uuid));
|
|
}
|
|
|
|
if (!(xml1 = virNWFilterDefFormat(def1)) ||
|
|
!(xml2 = virNWFilterDefFormat(def2)))
|
|
goto cleanup;
|
|
|
|
ret = STREQ(xml1, xml2);
|
|
|
|
cleanup:
|
|
if (!cmpUUIDs)
|
|
memcpy(def2->uuid, rem_uuid, sizeof(rem_uuid));
|
|
|
|
VIR_FREE(xml1);
|
|
VIR_FREE(xml2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
virNWFilterObjPtr
|
|
virNWFilterObjAssignDef(virNWFilterObjListPtr nwfilters,
|
|
virNWFilterDefPtr def)
|
|
{
|
|
virNWFilterObjPtr nwfilter;
|
|
|
|
nwfilter = virNWFilterObjFindByUUID(nwfilters, def->uuid);
|
|
|
|
if (nwfilter) {
|
|
if (STRNEQ(def->name, nwfilter->def->name)) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("filter with same UUID but different name "
|
|
"('%s') already exists"),
|
|
nwfilter->def->name);
|
|
virNWFilterObjUnlock(nwfilter);
|
|
return NULL;
|
|
}
|
|
virNWFilterObjUnlock(nwfilter);
|
|
} else {
|
|
nwfilter = virNWFilterObjFindByName(nwfilters, def->name);
|
|
if (nwfilter) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
virUUIDFormat(nwfilter->def->uuid, uuidstr);
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("filter '%s' already exists with uuid %s"),
|
|
def->name, uuidstr);
|
|
virNWFilterObjUnlock(nwfilter);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (virNWFilterObjDefLoopDetect(nwfilters, def) < 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
"%s", _("filter would introduce a loop"));
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if ((nwfilter = virNWFilterObjFindByName(nwfilters, def->name))) {
|
|
|
|
if (virNWFilterDefEqual(def, nwfilter->def, false)) {
|
|
virNWFilterDefFree(nwfilter->def);
|
|
nwfilter->def = def;
|
|
return nwfilter;
|
|
}
|
|
|
|
nwfilter->newDef = def;
|
|
/* trigger the update on VMs referencing the filter */
|
|
if (virNWFilterTriggerVMFilterRebuild()) {
|
|
nwfilter->newDef = NULL;
|
|
virNWFilterObjUnlock(nwfilter);
|
|
return NULL;
|
|
}
|
|
|
|
virNWFilterDefFree(nwfilter->def);
|
|
nwfilter->def = def;
|
|
nwfilter->newDef = NULL;
|
|
return nwfilter;
|
|
}
|
|
|
|
if (VIR_ALLOC(nwfilter) < 0)
|
|
return NULL;
|
|
|
|
if (virMutexInitRecursive(&nwfilter->lock) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot initialize mutex"));
|
|
VIR_FREE(nwfilter);
|
|
return NULL;
|
|
}
|
|
virNWFilterObjLock(nwfilter);
|
|
nwfilter->active = 0;
|
|
|
|
if (VIR_APPEND_ELEMENT_COPY(nwfilters->objs,
|
|
nwfilters->count, nwfilter) < 0) {
|
|
virNWFilterObjUnlock(nwfilter);
|
|
virNWFilterObjFree(nwfilter);
|
|
return NULL;
|
|
}
|
|
nwfilter->def = def;
|
|
|
|
return nwfilter;
|
|
}
|
|
|
|
|
|
static virNWFilterObjPtr
|
|
virNWFilterObjLoadConfig(virNWFilterObjListPtr nwfilters,
|
|
const char *configDir,
|
|
const char *name)
|
|
{
|
|
virNWFilterDefPtr def = NULL;
|
|
virNWFilterObjPtr nwfilter;
|
|
char *configFile = NULL;
|
|
|
|
if (!(configFile = virFileBuildPath(configDir, name, ".xml")))
|
|
goto error;
|
|
|
|
if (!(def = virNWFilterDefParseFile(configFile)))
|
|
goto error;
|
|
|
|
if (STRNEQ(name, def->name)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("network filter config filename '%s' "
|
|
"does not match name '%s'"),
|
|
configFile, def->name);
|
|
goto error;
|
|
}
|
|
|
|
/* We generated a UUID, make it permanent by saving the config to disk */
|
|
if (!def->uuid_specified &&
|
|
virNWFilterSaveConfig(configDir, def) < 0)
|
|
goto error;
|
|
|
|
if (!(nwfilter = virNWFilterObjAssignDef(nwfilters, def)))
|
|
goto error;
|
|
|
|
VIR_FREE(configFile);
|
|
return nwfilter;
|
|
|
|
error:
|
|
VIR_FREE(configFile);
|
|
virNWFilterDefFree(def);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
virNWFilterObjLoadAllConfigs(virNWFilterObjListPtr nwfilters,
|
|
const char *configDir)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
int ret = -1;
|
|
int rc;
|
|
|
|
if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
|
|
return rc;
|
|
|
|
while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
|
|
virNWFilterObjPtr nwfilter;
|
|
|
|
if (!virFileStripSuffix(entry->d_name, ".xml"))
|
|
continue;
|
|
|
|
nwfilter = virNWFilterObjLoadConfig(nwfilters, configDir, entry->d_name);
|
|
if (nwfilter)
|
|
virNWFilterObjUnlock(nwfilter);
|
|
}
|
|
|
|
VIR_DIR_CLOSE(dir);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
virNWFilterObjLock(virNWFilterObjPtr obj)
|
|
{
|
|
virMutexLock(&obj->lock);
|
|
}
|
|
|
|
|
|
void
|
|
virNWFilterObjUnlock(virNWFilterObjPtr obj)
|
|
{
|
|
virMutexUnlock(&obj->lock);
|
|
}
|