* Add support for batched add/delete for ipfw tables
* Add support for atomic batches add (all or none). * Fix panic on deleting non-existing entry in radix algo. Examples: # si is empty # ipfw table si add 1.1.1.1/32 1111 2.2.2.2/32 2222 added: 1.1.1.1/32 1111 added: 2.2.2.2/32 2222 # ipfw table si add 2.2.2.2/32 2200 4.4.4.4/32 4444 exists: 2.2.2.2/32 2200 added: 4.4.4.4/32 4444 ipfw: Adding record failed: record already exists ^^^^^ Returns error but keeps inserted items # ipfw table si list +++ table(si), set(0) +++ 1.1.1.1/32 1111 2.2.2.2/32 2222 4.4.4.4/32 4444 # ipfw table si atomic add 3.3.3.3/32 3333 4.4.4.4/32 4400 5.5.5.5/32 5555 added(reverted): 3.3.3.3/32 3333 exists: 4.4.4.4/32 4400 ignored: 5.5.5.5/32 5555 ipfw: Adding record failed: record already exists ^^^^^ Returns error and reverts added records # ipfw table si list +++ table(si), set(0) +++ 1.1.1.1/32 1111 2.2.2.2/32 2222 4.4.4.4/32 4444
This commit is contained in:
parent
030b184f10
commit
3a845e1076
sbin/ipfw
sys
@ -224,6 +224,7 @@ enum tokens {
|
||||
TOK_ALGO,
|
||||
TOK_TALIST,
|
||||
TOK_FTYPE,
|
||||
TOK_ATOMIC,
|
||||
};
|
||||
/*
|
||||
* the following macro returns an error message if we run out of
|
||||
|
@ -50,7 +50,7 @@
|
||||
|
||||
static void table_list(ipfw_xtable_info *i, int need_header);
|
||||
static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
|
||||
int add, int update);
|
||||
int add, int quiet, int update, int atomic);
|
||||
static int table_flush(ipfw_obj_header *oh);
|
||||
static int table_destroy(ipfw_obj_header *oh);
|
||||
static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
|
||||
@ -114,6 +114,7 @@ static struct _s_x tablecmds[] = {
|
||||
{ "detail", TOK_DETAIL },
|
||||
{ "list", TOK_LIST },
|
||||
{ "lookup", TOK_LOOKUP },
|
||||
{ "atomic", TOK_ATOMIC },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
@ -144,7 +145,7 @@ void
|
||||
ipfw_table_handler(int ac, char *av[])
|
||||
{
|
||||
int do_add, is_all;
|
||||
int error, tcmd;
|
||||
int atomic, error, tcmd;
|
||||
ipfw_xtable_info i;
|
||||
ipfw_obj_header oh;
|
||||
char *tablename;
|
||||
@ -176,6 +177,21 @@ ipfw_table_handler(int ac, char *av[])
|
||||
|
||||
if ((tcmd = match_token(tablecmds, *av)) == -1)
|
||||
errx(EX_USAGE, "invalid table command %s", *av);
|
||||
/* Check if atomic operation was requested */
|
||||
atomic = 0;
|
||||
if (tcmd == TOK_ATOMIC) {
|
||||
ac--; av++;
|
||||
NEED1("atomic needs command");
|
||||
if ((tcmd = match_token(tablecmds, *av)) == -1)
|
||||
errx(EX_USAGE, "invalid table command %s", *av);
|
||||
switch (tcmd) {
|
||||
case TOK_ADD:
|
||||
break;
|
||||
default:
|
||||
errx(EX_USAGE, "atomic is not compatible with %s", *av);
|
||||
}
|
||||
atomic = 1;
|
||||
}
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_LIST:
|
||||
@ -193,7 +209,8 @@ ipfw_table_handler(int ac, char *av[])
|
||||
case TOK_DEL:
|
||||
do_add = **av == 'a';
|
||||
ac--; av++;
|
||||
table_modify_record(&oh, ac, av, do_add, co.do_quiet);
|
||||
table_modify_record(&oh, ac, av, do_add, co.do_quiet,
|
||||
co.do_quiet, atomic);
|
||||
break;
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
@ -785,68 +802,68 @@ table_flush_one(ipfw_xtable_info *i, void *arg)
|
||||
|
||||
static int
|
||||
table_do_modify_record(int cmd, ipfw_obj_header *oh,
|
||||
ipfw_obj_tentry *tent, int update)
|
||||
ipfw_obj_tentry *tent, int count, int atomic)
|
||||
{
|
||||
ipfw_obj_ctlv *ctlv;
|
||||
ipfw_obj_tentry *tent_base;
|
||||
caddr_t pbuf;
|
||||
char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
|
||||
int error;
|
||||
int error, i;
|
||||
size_t sz;
|
||||
|
||||
memset(xbuf, 0, sizeof(xbuf));
|
||||
memcpy(xbuf, oh, sizeof(*oh));
|
||||
oh = (ipfw_obj_header *)xbuf;
|
||||
sz = sizeof(*ctlv) + sizeof(*tent) * count;
|
||||
if (count == 1) {
|
||||
memset(xbuf, 0, sizeof(xbuf));
|
||||
pbuf = xbuf;
|
||||
} else {
|
||||
if ((pbuf = calloc(1, sizeof(*oh) + sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
memcpy(pbuf, oh, sizeof(*oh));
|
||||
oh = (ipfw_obj_header *)pbuf;
|
||||
oh->opheader.version = 1;
|
||||
|
||||
ctlv = (ipfw_obj_ctlv *)(oh + 1);
|
||||
ctlv->count = 1;
|
||||
ctlv->head.length = sizeof(*ctlv) + sizeof(*tent);
|
||||
ctlv->count = count;
|
||||
ctlv->head.length = sz;
|
||||
if (atomic != 0)
|
||||
ctlv->flags |= IPFW_CTF_ATOMIC;
|
||||
|
||||
memcpy(ctlv + 1, tent, sizeof(*tent));
|
||||
tent_base = tent;
|
||||
memcpy(ctlv + 1, tent, sizeof(*tent) * count);
|
||||
tent = (ipfw_obj_tentry *)(ctlv + 1);
|
||||
if (update != 0)
|
||||
tent->head.flags |= IPFW_TF_UPDATE;
|
||||
tent->head.length = sizeof(ipfw_obj_tentry);
|
||||
for (i = 0; i < count; i++, tent++) {
|
||||
tent->head.length = sizeof(ipfw_obj_tentry);
|
||||
tent->idx = oh->idx;
|
||||
}
|
||||
|
||||
error = do_set3(cmd, &oh->opheader, sizeof(xbuf));
|
||||
sz += sizeof(*oh);
|
||||
error = do_get3(cmd, &oh->opheader, &sz);
|
||||
tent = (ipfw_obj_tentry *)(ctlv + 1);
|
||||
/* Copy result back to provided buffer */
|
||||
memcpy(tent_base, ctlv + 1, sizeof(*tent) * count);
|
||||
|
||||
if (pbuf != xbuf)
|
||||
free(pbuf);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update)
|
||||
table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
|
||||
int quiet, int update, int atomic)
|
||||
{
|
||||
ipfw_obj_tentry tent;
|
||||
ipfw_obj_tentry *ptent, tent, *tent_buf;
|
||||
ipfw_xtable_info xi;
|
||||
uint8_t type, vtype;
|
||||
int cmd, error;
|
||||
char *texterr, *etxt;
|
||||
int cmd, count, error, i, ignored;
|
||||
char *texterr, *etxt, *px;
|
||||
|
||||
if (ac == 0)
|
||||
errx(EX_USAGE, "address required");
|
||||
|
||||
memset(&tent, 0, sizeof(tent));
|
||||
tent.head.length = sizeof(tent);
|
||||
tent.idx = 1;
|
||||
|
||||
tentry_fill_key(oh, &tent, *av, &type, &vtype, &xi);
|
||||
|
||||
/*
|
||||
* compability layer: auto-create table if not exists
|
||||
*/
|
||||
if (xi.tablename[0] == '\0') {
|
||||
xi.type = type;
|
||||
xi.vtype = vtype;
|
||||
strlcpy(xi.tablename, oh->ntlv.name, sizeof(xi.tablename));
|
||||
fprintf(stderr, "DEPRECATED: inserting data info non-existent "
|
||||
"table %s. (auto-created)\n", xi.tablename);
|
||||
table_do_create(oh, &xi);
|
||||
}
|
||||
|
||||
oh->ntlv.type = type;
|
||||
ac--; av++;
|
||||
|
||||
if (add != 0) {
|
||||
if (ac > 0)
|
||||
tentry_fill_value(oh, &tent, *av, type, vtype);
|
||||
cmd = IP_FW_TABLE_XADD;
|
||||
texterr = "Adding record failed";
|
||||
} else {
|
||||
@ -854,7 +871,126 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update
|
||||
texterr = "Deleting record failed";
|
||||
}
|
||||
|
||||
if ((error = table_do_modify_record(cmd, oh, &tent, update)) == 0)
|
||||
/*
|
||||
* Calculate number of entries:
|
||||
* Assume [key val] x N for add
|
||||
* and
|
||||
* key x N for delete
|
||||
*/
|
||||
count = (add != 0) ? ac / 2 + 1 : ac;
|
||||
|
||||
if (count <= 1) {
|
||||
/* Adding single entry with/without value */
|
||||
memset(&tent, 0, sizeof(tent));
|
||||
tent_buf = &tent;
|
||||
} else {
|
||||
|
||||
if ((tent_buf = calloc(count, sizeof(tent))) == NULL)
|
||||
errx(EX_OSERR,
|
||||
"Unable to allocate memory for all entries");
|
||||
}
|
||||
ptent = tent_buf;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
count = 0;
|
||||
while (ac > 0) {
|
||||
tentry_fill_key(oh, ptent, *av, &type, &vtype, &xi);
|
||||
|
||||
/*
|
||||
* compability layer: auto-create table if not exists
|
||||
*/
|
||||
if (xi.tablename[0] == '\0') {
|
||||
xi.type = type;
|
||||
xi.vtype = vtype;
|
||||
strlcpy(xi.tablename, oh->ntlv.name,
|
||||
sizeof(xi.tablename));
|
||||
fprintf(stderr, "DEPRECATED: inserting data info "
|
||||
"non-existent table %s. (auto-created)\n",
|
||||
xi.tablename);
|
||||
table_do_create(oh, &xi);
|
||||
}
|
||||
|
||||
oh->ntlv.type = type;
|
||||
ac--; av++;
|
||||
|
||||
if (add != 0 && ac > 0) {
|
||||
tentry_fill_value(oh, ptent, *av, type, vtype);
|
||||
ac--; av++;
|
||||
}
|
||||
|
||||
if (update != 0)
|
||||
ptent->head.flags |= IPFW_TF_UPDATE;
|
||||
|
||||
count++;
|
||||
ptent++;
|
||||
}
|
||||
|
||||
error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
|
||||
|
||||
/*
|
||||
* Compatibility stuff: do not yell on duplicate keys or
|
||||
* failed deletions.
|
||||
*/
|
||||
if (error == 0 || (error == EEXIST && add != 0) ||
|
||||
(error == ENOENT && add == 0)) {
|
||||
if (quiet != 0) {
|
||||
if (tent_buf != &tent)
|
||||
free(tent_buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Report results back */
|
||||
ptent = tent_buf;
|
||||
for (i = 0; i < count; ptent++, i++) {
|
||||
ignored = 0;
|
||||
switch (ptent->result) {
|
||||
case IPFW_TR_ADDED:
|
||||
px = "added";
|
||||
break;
|
||||
case IPFW_TR_DELETED:
|
||||
px = "deleted";
|
||||
break;
|
||||
case IPFW_TR_UPDATED:
|
||||
px = "updated";
|
||||
break;
|
||||
case IPFW_TR_LIMIT:
|
||||
px = "limit";
|
||||
ignored = 1;
|
||||
break;
|
||||
case IPFW_TR_ERROR:
|
||||
px = "error";
|
||||
ignored = 1;
|
||||
break;
|
||||
case IPFW_TR_NOTFOUND:
|
||||
px = "notfound";
|
||||
ignored = 1;
|
||||
break;
|
||||
case IPFW_TR_EXISTS:
|
||||
px = "exists";
|
||||
ignored = 1;
|
||||
break;
|
||||
case IPFW_TR_IGNORED:
|
||||
px = "ignored";
|
||||
ignored = 1;
|
||||
break;
|
||||
default:
|
||||
px = "unknown";
|
||||
ignored = 1;
|
||||
}
|
||||
|
||||
if (error != 0 && atomic != 0 && ignored == 0)
|
||||
printf("%s(reverted): ", px);
|
||||
else
|
||||
printf("%s: ", px);
|
||||
|
||||
table_show_entry(&xi, ptent);
|
||||
}
|
||||
|
||||
if (tent_buf != &tent)
|
||||
free(tent_buf);
|
||||
|
||||
if (error == 0)
|
||||
return;
|
||||
|
||||
/* Try to provide more human-readable error */
|
||||
@ -924,6 +1060,7 @@ table_lookup(ipfw_obj_header *oh, int ac, char *av[])
|
||||
|
||||
strlcpy(key, *av, sizeof(key));
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
error = table_do_lookup(oh, key, &xi, &xtent);
|
||||
|
||||
switch (error) {
|
||||
@ -1144,7 +1281,10 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
|
||||
tflags = 0;
|
||||
vtype = 0;
|
||||
|
||||
error = table_get_info(oh, xi);
|
||||
if (xi->tablename[0] == '\0')
|
||||
error = table_get_info(oh, xi);
|
||||
else
|
||||
error = 0;
|
||||
|
||||
if (error == 0) {
|
||||
/* Table found. */
|
||||
|
@ -587,7 +587,7 @@ struct ip_fw {
|
||||
uint16_t act_ofs; /* offset of action in 32-bit units */
|
||||
uint16_t cmd_len; /* # of 32-bit words in cmd */
|
||||
uint16_t rulenum; /* rule number */
|
||||
uint8_t set; /* rule set (0..31) */
|
||||
uint8_t set; /* rule set (0..31) */
|
||||
uint8_t _pad; /* padding */
|
||||
uint32_t id; /* rule id */
|
||||
|
||||
@ -784,7 +784,10 @@ typedef struct _ipfw_obj_tentry {
|
||||
uint8_t masklen; /* mask length */
|
||||
uint16_t idx; /* Table name index */
|
||||
uint32_t value; /* value */
|
||||
uint64_t spare;
|
||||
uint8_t result; /* request result */
|
||||
uint8_t spare0;
|
||||
uint16_t spare1;
|
||||
uint32_t spare2;
|
||||
union {
|
||||
/* Longest field needs to be aligned by 8-byte boundary */
|
||||
struct in_addr addr; /* IPv4 address */
|
||||
@ -795,6 +798,17 @@ typedef struct _ipfw_obj_tentry {
|
||||
} k;
|
||||
} ipfw_obj_tentry;
|
||||
#define IPFW_TF_UPDATE 0x01 /* Update record if exists */
|
||||
/* Container TLV */
|
||||
#define IPFW_CTF_ATOMIC 0x01 /* Perform atomic operation */
|
||||
/* Operation results */
|
||||
#define IPFW_TR_IGNORED 0 /* Entry was ignored (rollback) */
|
||||
#define IPFW_TR_ADDED 1 /* Entry was succesfully added */
|
||||
#define IPFW_TR_UPDATED 2 /* Entry was succesfully updated*/
|
||||
#define IPFW_TR_DELETED 3 /* Entry was succesfully deleted*/
|
||||
#define IPFW_TR_LIMIT 4 /* Entry was ignored (limit) */
|
||||
#define IPFW_TR_NOTFOUND 5 /* Entry was not found */
|
||||
#define IPFW_TR_EXISTS 6 /* Entry already exists */
|
||||
#define IPFW_TR_ERROR 7 /* Request has failed (unknown) */
|
||||
|
||||
typedef struct _ipfw_obj_dyntlv {
|
||||
ipfw_obj_tlv head;
|
||||
@ -808,7 +822,7 @@ typedef struct _ipfw_obj_ctlv {
|
||||
uint32_t count; /* Number of sub-TLVs */
|
||||
uint16_t objsize; /* Single object size */
|
||||
uint8_t version; /* TLV version */
|
||||
uint8_t spare;
|
||||
uint8_t flags; /* TLV-specific flags */
|
||||
} ipfw_obj_ctlv;
|
||||
|
||||
/* Range TLV */
|
||||
|
@ -427,15 +427,18 @@ struct obj_idx {
|
||||
};
|
||||
|
||||
struct rule_check_info {
|
||||
uint16_t flags; /* rule-specific check flags */
|
||||
uint16_t table_opcodes; /* count of opcodes referencing table */
|
||||
uint16_t new_tables; /* count of opcodes referencing table */
|
||||
uint16_t urule_numoff; /* offset of rulenum in bytes */
|
||||
uint8_t version; /* rule version */
|
||||
uint8_t spare;
|
||||
ipfw_obj_ctlv *ctlv; /* name TLV containter */
|
||||
struct ip_fw *krule; /* resulting rule pointer */
|
||||
caddr_t urule; /* original rule pointer */
|
||||
struct obj_idx obuf[8]; /* table references storage */
|
||||
};
|
||||
#define IPFW_RCF_TABLES 0x01 /* Has table-referencing opcode */
|
||||
|
||||
|
||||
/* Legacy interface support */
|
||||
/*
|
||||
|
@ -2267,7 +2267,7 @@ ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
|
||||
int
|
||||
ipfw_ctl3(struct sockopt *sopt)
|
||||
{
|
||||
int error;
|
||||
int error, ctype;
|
||||
size_t bsize_max, size, valsize;
|
||||
struct ip_fw_chain *chain;
|
||||
uint32_t opt;
|
||||
@ -2297,25 +2297,33 @@ ipfw_ctl3(struct sockopt *sopt)
|
||||
sopt->sopt_valsize = valsize;
|
||||
|
||||
/*
|
||||
* Disallow modifications in really-really secure mode, but still allow
|
||||
* the logging counters to be reset.
|
||||
*/
|
||||
if (opt == IP_FW_XADD || opt == IP_FW_XDEL ||
|
||||
(sopt->sopt_dir == SOPT_SET && opt != IP_FW_XRESETLOG)) {
|
||||
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine buffer size:
|
||||
* Determine opcode type/buffer size:
|
||||
* use on-stack xbuf for short request,
|
||||
* allocate sliding-window buf for data export or
|
||||
* contigious buffer for special ops.
|
||||
*/
|
||||
bsize_max = IP_FW3_WRITEBUF;
|
||||
if (opt == IP_FW_ADD)
|
||||
ctype = (sopt->sopt_dir == SOPT_GET) ? SOPT_GET : SOPT_SET;
|
||||
switch (opt) {
|
||||
case IP_FW_XADD:
|
||||
case IP_FW_XDEL:
|
||||
case IP_FW_TABLE_XADD:
|
||||
case IP_FW_TABLE_XDEL:
|
||||
ctype = SOPT_SET;
|
||||
bsize_max = IP_FW3_READBUF;
|
||||
break;
|
||||
default:
|
||||
bsize_max = IP_FW3_WRITEBUF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disallow modifications in really-really secure mode, but still allow
|
||||
* the logging counters to be reset.
|
||||
*/
|
||||
if (ctype == SOPT_SET && opt != IP_FW_XRESETLOG) {
|
||||
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in sockopt_data structure that may be useful for
|
||||
@ -2664,8 +2672,8 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
ti.type = IPFW_TABLE_CIDR;
|
||||
|
||||
error = (opt == IP_FW_TABLE_ADD) ?
|
||||
add_table_entry(chain, &ti, &tei, 1) :
|
||||
del_table_entry(chain, &ti, &tei, 1);
|
||||
add_table_entry(chain, &ti, &tei, 0, 1) :
|
||||
del_table_entry(chain, &ti, &tei, 0, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -143,19 +143,58 @@ static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype
|
||||
|
||||
#define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */
|
||||
|
||||
/*
|
||||
* Checks if we're able to insert/update entry @tei into table
|
||||
* w.r.t @tc limits.
|
||||
* May alter @tei to indicate insertion error / insert
|
||||
* options.
|
||||
*
|
||||
* Returns 0 if operation can be performed/
|
||||
*/
|
||||
static int
|
||||
check_table_limit(struct table_config *tc, struct tentry_info *tei)
|
||||
{
|
||||
|
||||
if (tc->limit == 0 || tc->count < tc->limit)
|
||||
return (0);
|
||||
|
||||
if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
|
||||
/* Notify userland on error cause */
|
||||
tei->flags |= TEI_FLAGS_LIMIT;
|
||||
return (EFBIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have UPDATE flag set.
|
||||
* Permit updating record (if found),
|
||||
* but restrict adding new one since we've
|
||||
* already hit the limit.
|
||||
*/
|
||||
tei->flags |= TEI_FLAGS_DONTADD;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds/updates one or more entries in table @ti.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct tentry_info *tei, uint32_t count)
|
||||
struct tentry_info *tei, uint8_t flags, uint32_t count)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
struct namedobj_instance *ni;
|
||||
uint16_t kidx;
|
||||
int error;
|
||||
uint32_t num;
|
||||
int error, first_error, i, j, rerror, rollback;
|
||||
uint32_t num, numadd;
|
||||
ipfw_xtable_info *xi;
|
||||
struct tentry_info *ptei;
|
||||
char ta_buf[TA_BUF_SZ];
|
||||
size_t ta_buf_sz;
|
||||
caddr_t ta_buf_m, v, vv;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
@ -172,8 +211,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
}
|
||||
|
||||
/* Try to exit early on limit hit */
|
||||
if (tc->limit != 0 && tc->count >= tc->limit &&
|
||||
(tei->flags & TEI_FLAGS_UPDATE) == 0) {
|
||||
if ((error = check_table_limit(tc, tei)) != 0 && count == 1) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EFBIG);
|
||||
}
|
||||
@ -218,10 +256,34 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
}
|
||||
|
||||
/* Prepare record (allocate memory) */
|
||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||
error = ta->prepare_add(ch, tei, &ta_buf);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
ta_buf_sz = ta->ta_buf_size;
|
||||
rollback = 0;
|
||||
if (count == 1) {
|
||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||
ta_buf_m = ta_buf;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Multiple adds, allocate larger buffer
|
||||
* sufficient to hold both ADD state
|
||||
* and DELETE state (this may be needed
|
||||
* if we need to rollback all changes)
|
||||
*/
|
||||
ta_buf_m = malloc(2 * count * ta_buf_sz, M_TEMP,
|
||||
M_WAITOK | M_ZERO);
|
||||
}
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz) {
|
||||
error = ta->prepare_add(ch, &tei[i], v);
|
||||
|
||||
/*
|
||||
* Some syntax error (incorrect mask, or address, or
|
||||
* anything). Return error regardless of atomicity
|
||||
* settings.
|
||||
*/
|
||||
if (error != 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
@ -233,67 +295,142 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
|
||||
if (error != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
ta->flush_entry(ch, tei, &ta_buf);
|
||||
return (error);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
|
||||
/* Drop reference we've used in first search */
|
||||
tc->no.refcnt--;
|
||||
|
||||
/* Check limit before adding */
|
||||
if (tc->limit != 0 && tc->count >= tc->limit) {
|
||||
if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
ta->flush_entry(ch, tei, &ta_buf);
|
||||
return (EFBIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have UPDATE flag set.
|
||||
* Permit updating record (if found),
|
||||
* but restrict adding new one since we've
|
||||
* already hit the limit.
|
||||
*/
|
||||
tei->flags |= TEI_FLAGS_DONTADD;
|
||||
}
|
||||
|
||||
/* We've got valid table in @tc. Let's add data */
|
||||
/* We've got valid table in @tc. Let's try to add data */
|
||||
kidx = tc->no.kidx;
|
||||
ta = tc->ta;
|
||||
num = 0;
|
||||
numadd = 0;
|
||||
first_error = 0;
|
||||
|
||||
IPFW_WLOCK(ch);
|
||||
error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
|
||||
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz) {
|
||||
ptei = &tei[i];
|
||||
num = 0;
|
||||
/* check limit before adding */
|
||||
if ((error = check_table_limit(tc, ptei)) == 0) {
|
||||
error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx),
|
||||
ptei, v, &num);
|
||||
/* Set status flag to inform userland */
|
||||
if (error == 0 && num != 0)
|
||||
ptei->flags |= TEI_FLAGS_ADDED;
|
||||
else if (error == ENOENT)
|
||||
ptei->flags |= TEI_FLAGS_NOTFOUND;
|
||||
else if (error == EEXIST)
|
||||
ptei->flags |= TEI_FLAGS_EXISTS;
|
||||
else
|
||||
ptei->flags |= TEI_FLAGS_ERROR;
|
||||
}
|
||||
if (error == 0) {
|
||||
/* Update number of records to ease limit checking */
|
||||
tc->count += num;
|
||||
numadd += num;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first_error == 0)
|
||||
first_error = error;
|
||||
|
||||
/*
|
||||
* Some error have happened. Check our atomicity
|
||||
* settings: continue if atomicity is not required,
|
||||
* rollback changes otherwise.
|
||||
*/
|
||||
if ((flags & IPFW_CTF_ATOMIC) == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We need to rollback changes.
|
||||
* This is tricky since some entries may have been
|
||||
* updated, so we need to change their value back
|
||||
* instead of deletion.
|
||||
*/
|
||||
rollback = 1;
|
||||
v = ta_buf_m;
|
||||
vv = v + count * ta_buf_sz;
|
||||
for (j = 0; j < i; j++, v += ta_buf_sz, vv += ta_buf_sz) {
|
||||
ptei = &tei[j];
|
||||
if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) {
|
||||
|
||||
/*
|
||||
* We have old value stored by previous
|
||||
* call in @ptei->value. Do add once again
|
||||
* to restore it.
|
||||
*/
|
||||
rerror = ta->add(tc->astate,
|
||||
KIDX_TO_TI(ch, kidx), ptei, v, &num);
|
||||
KASSERT(rerror == 0, ("rollback UPDATE fail"));
|
||||
KASSERT(num == 0, ("rollback UPDATE fail2"));
|
||||
continue;
|
||||
}
|
||||
|
||||
rerror = ta->prepare_del(ch, ptei, vv);
|
||||
KASSERT(rerror == 0, ("pre-rollback INSERT failed"));
|
||||
rerror = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei,
|
||||
vv, &num);
|
||||
KASSERT(rerror == 0, ("rollback INSERT failed"));
|
||||
tc->count -= num;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
IPFW_WUNLOCK(ch);
|
||||
|
||||
/* Update number of records. */
|
||||
if (error == 0) {
|
||||
tc->count += num;
|
||||
/* Permit post-add algorithm grow/rehash. */
|
||||
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
|
||||
}
|
||||
/* Permit post-add algorithm grow/rehash. */
|
||||
if (numadd != 0)
|
||||
check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
/* Return first error to user, if any */
|
||||
error = first_error;
|
||||
|
||||
cleanup:
|
||||
/* Run cleaning callback anyway */
|
||||
ta->flush_entry(ch, tei, &ta_buf);
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz)
|
||||
ta->flush_entry(ch, &tei[i], v);
|
||||
|
||||
/* Clean up "deleted" state in case of rollback */
|
||||
if (rollback != 0) {
|
||||
vv = ta_buf_m + count * ta_buf_sz;
|
||||
for (i = 0; i < count; i++, vv += ta_buf_sz)
|
||||
ta->flush_entry(ch, &tei[i], vv);
|
||||
}
|
||||
|
||||
if (ta_buf_m != ta_buf)
|
||||
free(ta_buf_m, M_TEMP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes one or more entries in table @ti.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct tentry_info *tei, uint32_t count)
|
||||
struct tentry_info *tei, uint8_t flags, uint32_t count)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
struct namedobj_instance *ni;
|
||||
struct tentry_info *ptei;
|
||||
uint16_t kidx;
|
||||
int error;
|
||||
uint32_t num;
|
||||
int error, first_error, i;
|
||||
uint32_t num, numdel;
|
||||
char ta_buf[TA_BUF_SZ];
|
||||
size_t ta_buf_sz;
|
||||
caddr_t ta_buf_m, v;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
@ -307,8 +444,6 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
ta = tc->ta;
|
||||
|
||||
/*
|
||||
* Give a chance for algorithm to shrink.
|
||||
* May release/reacquire UH_WLOCK.
|
||||
@ -317,36 +452,89 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
|
||||
if (error != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
ta->flush_entry(ch, tei, &ta_buf);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume ta_buf size is enough for storing
|
||||
* prepare_del() key, so we're running under UH_WLOCK here.
|
||||
*/
|
||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||
if ((error = ta->prepare_del(ch, tei, &ta_buf)) != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (error);
|
||||
/* Reference and unlock */
|
||||
tc->no.refcnt++;
|
||||
ta = tc->ta;
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
/* Prepare record (allocate memory) */
|
||||
ta_buf_sz = ta->ta_buf_size;
|
||||
if (count == 1) {
|
||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||
ta_buf_m = ta_buf;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Multiple deletes, allocate larger buffer
|
||||
* sufficient to hold delete state.
|
||||
*/
|
||||
ta_buf_m = malloc(count * ta_buf_sz, M_TEMP,
|
||||
M_WAITOK | M_ZERO);
|
||||
}
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz) {
|
||||
error = ta->prepare_del(ch, &tei[i], v);
|
||||
|
||||
/*
|
||||
* Some syntax error (incorrect mask, or address, or
|
||||
* anything). Return error immediately.
|
||||
*/
|
||||
if (error != 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
/* Drop reference we've used in first search */
|
||||
tc->no.refcnt--;
|
||||
|
||||
kidx = tc->no.kidx;
|
||||
num = 0;
|
||||
numdel = 0;
|
||||
first_error = 0;
|
||||
|
||||
IPFW_WLOCK(ch);
|
||||
error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz) {
|
||||
ptei = &tei[i];
|
||||
num = 0;
|
||||
error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v,
|
||||
&num);
|
||||
/* Save state for userland */
|
||||
if (error == 0)
|
||||
ptei->flags |= TEI_FLAGS_DELETED;
|
||||
else if (error == ENOENT)
|
||||
ptei->flags |= TEI_FLAGS_NOTFOUND;
|
||||
else
|
||||
ptei->flags |= TEI_FLAGS_ERROR;
|
||||
if (error != 0 && first_error == 0)
|
||||
first_error = error;
|
||||
tc->count -= num;
|
||||
numdel += num;
|
||||
}
|
||||
IPFW_WUNLOCK(ch);
|
||||
|
||||
if (error == 0) {
|
||||
tc->count -= num;
|
||||
if (numdel != 0) {
|
||||
/* Run post-del hook to permit shrinking */
|
||||
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
|
||||
}
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
ta->flush_entry(ch, tei, &ta_buf);
|
||||
/* Return first error to user, if any */
|
||||
error = first_error;
|
||||
|
||||
cleanup:
|
||||
/* Run cleaning callback anyway */
|
||||
v = ta_buf_m;
|
||||
for (i = 0; i < count; i++, v += ta_buf_sz)
|
||||
ta->flush_entry(ch, &tei[i], v);
|
||||
|
||||
if (ta_buf_m != ta_buf)
|
||||
free(ta_buf_m, M_TEMP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -432,8 +620,10 @@ check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Selects appropriate table operation handler
|
||||
* depending on opcode version.
|
||||
*/
|
||||
int
|
||||
ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
@ -501,8 +691,8 @@ ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
ti.type = xent->type;
|
||||
|
||||
error = (op3->opcode == IP_FW_TABLE_XADD) ?
|
||||
add_table_entry(ch, &ti, &tei, 1) :
|
||||
del_table_entry(ch, &ti, &tei, 1);
|
||||
add_table_entry(ch, &ti, &tei, 0, 1) :
|
||||
del_table_entry(ch, &ti, &tei, 0, 1);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -520,12 +710,12 @@ static int
|
||||
ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_tentry *tent;
|
||||
ipfw_obj_tentry *tent, *ptent;
|
||||
ipfw_obj_ctlv *ctlv;
|
||||
ipfw_obj_header *oh;
|
||||
struct tentry_info tei;
|
||||
struct tentry_info *ptei, tei, *tei_buf;
|
||||
struct tid_info ti;
|
||||
int error, read;
|
||||
int error, i, kidx, read;
|
||||
|
||||
/* Check minimum header size */
|
||||
if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
|
||||
@ -547,37 +737,81 @@ ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
if (ctlv->head.length + read != sd->valsize)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* TODO: permit adding multiple entries for given table
|
||||
* at once
|
||||
*/
|
||||
if (ctlv->count != 1)
|
||||
return (EOPNOTSUPP);
|
||||
|
||||
read += sizeof(*ctlv);
|
||||
|
||||
/* Assume tentry may grow to support larger keys */
|
||||
tent = (ipfw_obj_tentry *)(ctlv + 1);
|
||||
if (tent->head.length < sizeof(*tent) ||
|
||||
tent->head.length + read > sd->valsize)
|
||||
if (ctlv->count * sizeof(*tent) + read != sd->valsize)
|
||||
return (EINVAL);
|
||||
|
||||
/* Convert data into kernel request objects */
|
||||
memset(&tei, 0, sizeof(tei));
|
||||
tei.paddr = &tent->k;
|
||||
tei.subtype = tent->subtype;
|
||||
tei.masklen = tent->masklen;
|
||||
if (tent->head.flags & IPFW_TF_UPDATE)
|
||||
tei.flags |= TEI_FLAGS_UPDATE;
|
||||
tei.value = tent->value;
|
||||
if (ctlv->count == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Mark entire buffer as "read".
|
||||
* This makes sopt api write it back
|
||||
* after function return.
|
||||
*/
|
||||
ipfw_get_sopt_header(sd, sd->valsize);
|
||||
|
||||
/* Perform basic checks for each entry */
|
||||
ptent = tent;
|
||||
kidx = tent->idx;
|
||||
for (i = 0; i < ctlv->count; i++, ptent++) {
|
||||
if (ptent->head.length != sizeof(*ptent))
|
||||
return (EINVAL);
|
||||
if (ptent->idx != kidx)
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
/* Convert data into kernel request objects */
|
||||
objheader_to_ti(oh, &ti);
|
||||
ti.type = oh->ntlv.type;
|
||||
ti.uidx = tent->idx;
|
||||
ti.uidx = kidx;
|
||||
|
||||
/* Use on-stack buffer for single add/del */
|
||||
if (ctlv->count == 1) {
|
||||
memset(&tei, 0, sizeof(tei));
|
||||
tei_buf = &tei;
|
||||
} else
|
||||
tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
ptei = tei_buf;
|
||||
ptent = tent;
|
||||
for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
|
||||
ptei->paddr = &ptent->k;
|
||||
ptei->subtype = ptent->subtype;
|
||||
ptei->masklen = ptent->masklen;
|
||||
if (ptent->head.flags & IPFW_TF_UPDATE)
|
||||
ptei->flags |= TEI_FLAGS_UPDATE;
|
||||
ptei->value = ptent->value;
|
||||
}
|
||||
|
||||
error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
|
||||
add_table_entry(ch, &ti, &tei, 1) :
|
||||
del_table_entry(ch, &ti, &tei, 1);
|
||||
add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) :
|
||||
del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count);
|
||||
|
||||
/* Translate result back to userland */
|
||||
ptei = tei_buf;
|
||||
ptent = tent;
|
||||
for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
|
||||
if (ptei->flags & TEI_FLAGS_ADDED)
|
||||
ptent->result = IPFW_TR_ADDED;
|
||||
else if (ptei->flags & TEI_FLAGS_DELETED)
|
||||
ptent->result = IPFW_TR_DELETED;
|
||||
else if (ptei->flags & TEI_FLAGS_UPDATED)
|
||||
ptent->result = IPFW_TR_UPDATED;
|
||||
else if (ptei->flags & TEI_FLAGS_LIMIT)
|
||||
ptent->result = IPFW_TR_LIMIT;
|
||||
else if (ptei->flags & TEI_FLAGS_ERROR)
|
||||
ptent->result = IPFW_TR_ERROR;
|
||||
else if (ptei->flags & TEI_FLAGS_NOTFOUND)
|
||||
ptent->result = IPFW_TR_NOTFOUND;
|
||||
else if (ptei->flags & TEI_FLAGS_EXISTS)
|
||||
ptent->result = IPFW_TR_EXISTS;
|
||||
}
|
||||
|
||||
if (tei_buf != &tei)
|
||||
free(tei_buf, M_TEMP);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -2364,7 +2598,7 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
|
||||
break;
|
||||
}
|
||||
|
||||
ci->new_tables++;
|
||||
ci->flags |= IPFW_RCF_TABLES;
|
||||
pidx->new = 1;
|
||||
pidx++;
|
||||
}
|
||||
@ -2742,7 +2976,7 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
|
||||
* Stage 2: allocate table configs for every non-existent table
|
||||
*/
|
||||
|
||||
if (ci->new_tables > 0) {
|
||||
if ((ci->flags & IPFW_RCF_TABLES) != 0) {
|
||||
for (p = pidx_first; p < pidx_last; p++) {
|
||||
if (p->new == 0)
|
||||
continue;
|
||||
@ -2789,7 +3023,7 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
|
||||
|
||||
IPFW_UH_WLOCK(chain);
|
||||
|
||||
if (ci->new_tables > 0) {
|
||||
if ((ci->flags & IPFW_RCF_TABLES) != 0) {
|
||||
/*
|
||||
* Stage 3: link & reference new table configs
|
||||
*/
|
||||
|
@ -58,10 +58,16 @@ struct tentry_info {
|
||||
uint16_t flags; /* record flags */
|
||||
uint32_t value; /* value */
|
||||
};
|
||||
#define TEI_FLAGS_UPDATE 0x01 /* Add or update rec if exists */
|
||||
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
|
||||
#define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */
|
||||
#define TEI_FLAGS_DONTADD 0x08 /* Do not create new rec */
|
||||
#define TEI_FLAGS_UPDATE 0x0001 /* Add or update rec if exists */
|
||||
#define TEI_FLAGS_UPDATED 0x0002 /* Entry has been updated */
|
||||
#define TEI_FLAGS_COMPAT 0x0004 /* Called from old ABI */
|
||||
#define TEI_FLAGS_DONTADD 0x0008 /* Do not create new rec */
|
||||
#define TEI_FLAGS_ADDED 0x0010 /* Entry was added */
|
||||
#define TEI_FLAGS_DELETED 0x0020 /* Entry was deleted */
|
||||
#define TEI_FLAGS_LIMIT 0x0040 /* Limit was hit */
|
||||
#define TEI_FLAGS_ERROR 0x0080 /* Unknown request error */
|
||||
#define TEI_FLAGS_NOTFOUND 0x0100 /* Entry was not found */
|
||||
#define TEI_FLAGS_EXISTS 0x0200 /* Entry already exists */
|
||||
|
||||
typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
|
||||
struct table_info *ti, char *data, uint8_t tflags);
|
||||
@ -157,9 +163,9 @@ int ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd);
|
||||
/* Exported to support legacy opcodes */
|
||||
int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct tentry_info *tei, uint32_t count);
|
||||
struct tentry_info *tei, uint8_t flags, uint32_t count);
|
||||
int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct tentry_info *tei, uint32_t count);
|
||||
struct tentry_info *tei, uint8_t flags, uint32_t count);
|
||||
int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
|
||||
|
||||
int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
|
||||
|
@ -554,6 +554,9 @@ ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
|
||||
|
||||
rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
|
||||
|
||||
if (rn == NULL)
|
||||
return (ENOENT);
|
||||
|
||||
/* Save entry value to @tei */
|
||||
if (tei->subtype == AF_INET)
|
||||
tei->value = ((struct radix_cidr_entry *)rn)->value;
|
||||
@ -562,9 +565,6 @@ ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
|
||||
|
||||
tb->ent_ptr = rn;
|
||||
|
||||
if (rn == NULL)
|
||||
return (ENOENT);
|
||||
|
||||
if (tei->subtype == AF_INET)
|
||||
cfg->count4--;
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user