diff options
author | melifaro <melifaro@FreeBSD.org> | 2014-08-11 17:34:25 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2014-08-11 17:34:25 +0000 |
commit | 377bb9d131699756f65c6ffe103071cfc9944d91 (patch) | |
tree | 3d17132181d2c4538a2d4fe65d184a1e782e46b3 /sbin/ipfw/tables.c | |
parent | 5b47ece0e9bd2fe547ad679e1c249ece5427e9c2 (diff) | |
download | FreeBSD-src-377bb9d131699756f65c6ffe103071cfc9944d91.zip FreeBSD-src-377bb9d131699756f65c6ffe103071cfc9944d91.tar.gz |
* 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
Diffstat (limited to 'sbin/ipfw/tables.c')
-rw-r--r-- | sbin/ipfw/tables.c | 226 |
1 files changed, 183 insertions, 43 deletions
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index b78bbeb..acb280f 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -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,76 +802,195 @@ 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; + + tent_base = tent; + memcpy(ctlv + 1, tent, sizeof(*tent) * count); + tent = (ipfw_obj_tentry *)(ctlv + 1); + for (i = 0; i < count; i++, tent++) { + tent->head.length = sizeof(ipfw_obj_tentry); + tent->idx = oh->idx; + } - memcpy(ctlv + 1, tent, sizeof(*tent)); + sz += sizeof(*oh); + error = do_get3(cmd, &oh->opheader, &sz); tent = (ipfw_obj_tentry *)(ctlv + 1); - if (update != 0) - tent->head.flags |= IPFW_TF_UPDATE; - tent->head.length = sizeof(ipfw_obj_tentry); + /* Copy result back to provided buffer */ + memcpy(tent_base, ctlv + 1, sizeof(*tent) * count); - error = do_set3(cmd, &oh->opheader, sizeof(xbuf)); + 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; + if (add != 0) { + cmd = IP_FW_TABLE_XADD; + texterr = "Adding record failed"; + } else { + cmd = IP_FW_TABLE_XDEL; + texterr = "Deleting record failed"; + } + + /* + * 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++; + } - tentry_fill_key(oh, &tent, *av, &type, &vtype, &xi); + if (update != 0) + ptent->head.flags |= IPFW_TF_UPDATE; + + count++; + ptent++; + } + + error = table_do_modify_record(cmd, oh, tent_buf, count, atomic); /* - * compability layer: auto-create table if not exists + * Compatibility stuff: do not yell on duplicate keys or + * failed deletions. */ - 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); + if (error == 0 || (error == EEXIST && add != 0) || + (error == ENOENT && add == 0)) { + if (quiet != 0) { + if (tent_buf != &tent) + free(tent_buf); + return; + } } - oh->ntlv.type = type; - ac--; av++; + /* 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 (add != 0) { - if (ac > 0) - tentry_fill_value(oh, &tent, *av, type, vtype); - cmd = IP_FW_TABLE_XADD; - texterr = "Adding record failed"; - } else { - cmd = IP_FW_TABLE_XDEL; - texterr = "Deleting record failed"; + if (error != 0 && atomic != 0 && ignored == 0) + printf("%s(reverted): ", px); + else + printf("%s: ", px); + + table_show_entry(&xi, ptent); } - if ((error = table_do_modify_record(cmd, oh, &tent, update)) == 0) + 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. */ |