Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw_sockopt.c
| /*- | /*- | ||||
| * SPDX-License-Identifier: BSD-2-Clause | * SPDX-License-Identifier: BSD-2-Clause | ||||
| * | * | ||||
| * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa | * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa | ||||
| * Copyright (c) 2014 Yandex LLC | * Copyright (c) 2014-2024 Yandex LLC | ||||
| * Copyright (c) 2014 Alexander V. Chernikov | * Copyright (c) 2014 Alexander V. Chernikov | ||||
| * | * | ||||
| * Supported by: Valeria Paoli | * Supported by: Valeria Paoli | ||||
| * | * | ||||
| * Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
| * modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
| * are met: | * are met: | ||||
| * 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| #include <netpfil/ipfw/ip_fw_private.h> | #include <netpfil/ipfw/ip_fw_private.h> | ||||
| #include <netpfil/ipfw/ip_fw_table.h> | #include <netpfil/ipfw/ip_fw_table.h> | ||||
| #ifdef MAC | #ifdef MAC | ||||
| #include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
| #endif | #endif | ||||
| static int ipfw_ctl(struct sockopt *sopt); | static enum ipfw_opcheck_result | ||||
| check_opcode_compat_nop(ipfw_insn **pcmd, int *plen, | |||||
| struct rule_check_info *ci) | |||||
| { | |||||
| /* Compatibility code is not registered */ | |||||
| return (FAILED); | |||||
| } | |||||
| static ipfw_check_opcode_t check_opcode_f = check_opcode_compat_nop; | |||||
| static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, | static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, | ||||
| struct rule_check_info *ci); | struct rule_check_info *ci); | ||||
| static int check_ipfw_rule1(struct ip_fw_rule *rule, int size, | |||||
| struct rule_check_info *ci); | |||||
| static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, | |||||
| struct rule_check_info *ci); | |||||
| static int rewrite_rule_uidx(struct ip_fw_chain *chain, | static int rewrite_rule_uidx(struct ip_fw_chain *chain, | ||||
| struct rule_check_info *ci); | struct rule_check_info *ci); | ||||
| #define NAMEDOBJ_HASH_SIZE 32 | |||||
| struct namedobj_instance { | struct namedobj_instance { | ||||
| struct namedobjects_head *names; | struct namedobjects_head *names; | ||||
| struct namedobjects_head *values; | struct namedobjects_head *values; | ||||
| uint32_t nn_size; /* names hash size */ | uint32_t nn_size; /* names hash size */ | ||||
| uint32_t nv_size; /* number hash size */ | uint32_t nv_size; /* number hash size */ | ||||
| u_long *idx_mask; /* used items bitmask */ | u_long *idx_mask; /* used items bitmask */ | ||||
| uint32_t max_blocks; /* number of "long" blocks in bitmask */ | uint32_t max_blocks; /* number of "long" blocks in bitmask */ | ||||
| uint32_t count; /* number of items */ | uint32_t count; /* number of items */ | ||||
| uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */ | uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */ | ||||
| objhash_hash_f *hash_f; | objhash_hash_f *hash_f; | ||||
| objhash_cmp_f *cmp_f; | objhash_cmp_f *cmp_f; | ||||
| }; | }; | ||||
| #define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */ | #define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */ | ||||
| static uint32_t objhash_hash_name(struct namedobj_instance *ni, | static uint32_t objhash_hash_name(struct namedobj_instance *ni, | ||||
| const void *key, uint32_t kopt); | const void *key, uint32_t kopt); | ||||
| static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val); | static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val); | ||||
| static int objhash_cmp_name(struct named_object *no, const void *name, | static int objhash_cmp_name(struct named_object *no, const void *name, | ||||
| uint32_t set); | uint32_t set); | ||||
| MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); | MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); | ||||
| static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd); | |||||
| /* ctl3 handler data */ | /* ctl3 handler data */ | ||||
| struct mtx ctl3_lock; | static struct mtx ctl3_lock; | ||||
| #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF) | #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF) | ||||
| #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock) | #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock) | ||||
| #define CTL3_LOCK() mtx_lock(&ctl3_lock) | #define CTL3_LOCK() mtx_lock(&ctl3_lock) | ||||
| #define CTL3_UNLOCK() mtx_unlock(&ctl3_lock) | #define CTL3_UNLOCK() mtx_unlock(&ctl3_lock) | ||||
| static struct ipfw_sopt_handler *ctl3_handlers; | static struct ipfw_sopt_handler *ctl3_handlers; | ||||
| static size_t ctl3_hsize; | static size_t ctl3_hsize; | ||||
| static uint64_t ctl3_refct, ctl3_gencnt; | static uint64_t ctl3_refct, ctl3_gencnt; | ||||
| #define CTL3_SMALLBUF 4096 /* small page-size write buffer */ | #define CTL3_SMALLBUF 4096 /* small page-size write buffer */ | ||||
| #define CTL3_LARGEBUF 16 * 1024 * 1024 /* handle large rulesets */ | #define CTL3_LARGEBUF (16 * 1024 * 1024) /* handle large rulesets */ | ||||
| static int ipfw_flush_sopt_data(struct sockopt_data *sd); | static int ipfw_flush_sopt_data(struct sockopt_data *sd); | ||||
| static sopt_handler_f dump_config, add_rules, del_rules, clear_rules, | |||||
| move_rules, manage_sets, dump_soptcodes, dump_srvobjects, | |||||
| manage_skiptocache; | |||||
| static struct ipfw_sopt_handler scodes[] = { | static struct ipfw_sopt_handler scodes[] = { | ||||
| { IP_FW_XGET, 0, HDIR_GET, dump_config }, | { IP_FW_XGET, IP_FW3_OPVER, HDIR_GET, dump_config }, | ||||
| { IP_FW_XADD, 0, HDIR_BOTH, add_rules }, | { IP_FW_XADD, IP_FW3_OPVER, HDIR_BOTH, add_rules }, | ||||
| { IP_FW_XDEL, 0, HDIR_BOTH, del_rules }, | { IP_FW_XDEL, IP_FW3_OPVER, HDIR_BOTH, del_rules }, | ||||
| { IP_FW_XZERO, 0, HDIR_SET, clear_rules }, | { IP_FW_XZERO, IP_FW3_OPVER, HDIR_SET, clear_rules }, | ||||
| { IP_FW_XRESETLOG, 0, HDIR_SET, clear_rules }, | { IP_FW_XRESETLOG, IP_FW3_OPVER, HDIR_SET, clear_rules }, | ||||
| { IP_FW_XMOVE, 0, HDIR_SET, move_rules }, | { IP_FW_XMOVE, IP_FW3_OPVER, HDIR_SET, move_rules }, | ||||
| { IP_FW_SET_SWAP, 0, HDIR_SET, manage_sets }, | { IP_FW_SET_SWAP, IP_FW3_OPVER, HDIR_SET, manage_sets }, | ||||
| { IP_FW_SET_MOVE, 0, HDIR_SET, manage_sets }, | { IP_FW_SET_MOVE, IP_FW3_OPVER, HDIR_SET, manage_sets }, | ||||
| { IP_FW_SET_ENABLE, 0, HDIR_SET, manage_sets }, | { IP_FW_SET_ENABLE, IP_FW3_OPVER, HDIR_SET, manage_sets }, | ||||
| { IP_FW_DUMP_SOPTCODES, 0, HDIR_GET, dump_soptcodes }, | { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER, HDIR_GET, dump_soptcodes }, | ||||
| { IP_FW_DUMP_SRVOBJECTS,0, HDIR_GET, dump_srvobjects }, | { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER, HDIR_GET, dump_srvobjects }, | ||||
| { IP_FW_SKIPTO_CACHE, IP_FW3_OPVER, HDIR_BOTH, manage_skiptocache }, | |||||
| }; | }; | ||||
| static int | |||||
| set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule); | |||||
| static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd, | static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd, | ||||
| uint16_t *puidx, uint8_t *ptype); | uint32_t *puidx, uint8_t *ptype); | ||||
| static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, | static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, | ||||
| struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti); | struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti); | ||||
| static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, | static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, | ||||
| struct tid_info *ti, struct obj_idx *pidx, int *unresolved); | struct tid_info *ti, struct obj_idx *pidx, int *unresolved); | ||||
| static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule); | static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule); | ||||
| static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, | static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, | ||||
| struct obj_idx *oib, struct obj_idx *end); | struct obj_idx *oib, struct obj_idx *end); | ||||
| static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, | static int export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx, | ||||
| struct sockopt_data *sd); | struct sockopt_data *sd); | ||||
| /* | /* | ||||
| * Opcode object rewriter variables | * Opcode object rewriter variables | ||||
| */ | */ | ||||
| struct opcode_obj_rewrite *ctl3_rewriters; | struct opcode_obj_rewrite *ctl3_rewriters; | ||||
| static size_t ctl3_rsize; | static size_t ctl3_rsize; | ||||
| ▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* | /* | ||||
| * Builds skipto cache on rule set @map. | * Builds skipto cache on rule set @map. | ||||
| */ | */ | ||||
| static void | static void | ||||
| update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map) | update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map) | ||||
| { | { | ||||
| int *smap, rulenum; | uint32_t *smap, rulenum; | ||||
| int i, mi; | int i, mi; | ||||
| IPFW_UH_WLOCK_ASSERT(chain); | IPFW_UH_WLOCK_ASSERT(chain); | ||||
| mi = 0; | mi = 0; | ||||
| rulenum = map[mi]->rulenum; | rulenum = map[mi]->rulenum; | ||||
| smap = chain->idxmap_back; | smap = chain->idxmap_back; | ||||
| if (smap == NULL) | if (smap == NULL) | ||||
| return; | return; | ||||
| for (i = 0; i < 65536; i++) { | for (i = 0; i <= IPFW_DEFAULT_RULE; i++) { | ||||
| smap[i] = mi; | smap[i] = mi; | ||||
| /* Use the same rule index until i < rulenum */ | /* Use the same rule index until i < rulenum */ | ||||
| if (i != rulenum || i == 65535) | if (i != rulenum || i == IPFW_DEFAULT_RULE) | ||||
| continue; | continue; | ||||
| /* Find next rule with num > i */ | /* Find next rule with num > i */ | ||||
| rulenum = map[++mi]->rulenum; | rulenum = map[++mi]->rulenum; | ||||
| while (rulenum == i) | while (rulenum == i) | ||||
| rulenum = map[++mi]->rulenum; | rulenum = map[++mi]->rulenum; | ||||
| } | } | ||||
| } | } | ||||
| /* | /* | ||||
| * Swaps prepared (backup) index with current one. | * Swaps prepared (backup) index with current one. | ||||
| */ | */ | ||||
| static void | static void | ||||
| swap_skipto_cache(struct ip_fw_chain *chain) | swap_skipto_cache(struct ip_fw_chain *chain) | ||||
| { | { | ||||
| int *map; | uint32_t *map; | ||||
| IPFW_UH_WLOCK_ASSERT(chain); | IPFW_UH_WLOCK_ASSERT(chain); | ||||
| IPFW_WLOCK_ASSERT(chain); | IPFW_WLOCK_ASSERT(chain); | ||||
| map = chain->idxmap; | map = chain->idxmap; | ||||
| chain->idxmap = chain->idxmap_back; | chain->idxmap = chain->idxmap_back; | ||||
| chain->idxmap_back = map; | chain->idxmap_back = map; | ||||
| } | } | ||||
| /* | /* | ||||
| * Allocate and initialize skipto cache. | * Allocate and initialize skipto cache. | ||||
| */ | */ | ||||
| void | void | ||||
| ipfw_init_skipto_cache(struct ip_fw_chain *chain) | ipfw_init_skipto_cache(struct ip_fw_chain *chain) | ||||
| { | { | ||||
| int *idxmap, *idxmap_back; | uint32_t *idxmap, *idxmap_back; | ||||
| idxmap = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK | M_ZERO); | idxmap = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), | ||||
| idxmap_back = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK); | M_IPFW, M_WAITOK | M_ZERO); | ||||
| idxmap_back = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), | |||||
| M_IPFW, M_WAITOK | M_ZERO); | |||||
| /* | /* | ||||
| * Note we may be called at any time after initialization, | * Note we may be called at any time after initialization, | ||||
| * for example, on first skipto rule, so we need to | * for example, on first skipto rule, so we need to | ||||
| * provide valid chain->idxmap on return | * provide valid chain->idxmap on return | ||||
| */ | */ | ||||
| IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
| if (chain->idxmap != NULL) { | if (chain->idxmap != NULL) { | ||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| free(idxmap, M_IPFW); | free(idxmap, M_IPFW); | ||||
| free(idxmap_back, M_IPFW); | free(idxmap_back, M_IPFW); | ||||
| return; | return; | ||||
| } | } | ||||
| /* Set backup pointer first to permit building cache */ | /* Set backup pointer first to permit building cache */ | ||||
| chain->idxmap_back = idxmap_back; | chain->idxmap_back = idxmap_back; | ||||
| if (V_skipto_cache != 0) | |||||
| update_skipto_cache(chain, chain->map); | update_skipto_cache(chain, chain->map); | ||||
| IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
| /* It is now safe to set chain->idxmap ptr */ | /* It is now safe to set chain->idxmap ptr */ | ||||
| chain->idxmap = idxmap; | chain->idxmap = idxmap; | ||||
| swap_skipto_cache(chain); | swap_skipto_cache(chain); | ||||
| IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| } | } | ||||
| /* | /* | ||||
| * Destroys skipto cache. | * Destroys skipto cache. | ||||
| */ | */ | ||||
| void | void | ||||
| ipfw_destroy_skipto_cache(struct ip_fw_chain *chain) | ipfw_destroy_skipto_cache(struct ip_fw_chain *chain) | ||||
| { | { | ||||
| if (chain->idxmap != NULL) | |||||
| free(chain->idxmap, M_IPFW); | free(chain->idxmap, M_IPFW); | ||||
| if (chain->idxmap != NULL) | |||||
| free(chain->idxmap_back, M_IPFW); | free(chain->idxmap_back, M_IPFW); | ||||
| } | } | ||||
| /* | /* | ||||
| * allocate a new map, returns the chain locked. extra is the number | * allocate a new map, returns the chain locked. extra is the number | ||||
| * of entries to add or delete. | * of entries to add or delete. | ||||
| */ | */ | ||||
| static struct ip_fw ** | static struct ip_fw ** | ||||
| get_map(struct ip_fw_chain *chain, int extra, int locked) | get_map(struct ip_fw_chain *chain, int extra, int locked) | ||||
| ▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | if (krule->cntr != NULL) { | ||||
| cntr->timestamp = krule->timestamp; | cntr->timestamp = krule->timestamp; | ||||
| } | } | ||||
| if (cntr->timestamp > 0) { | if (cntr->timestamp > 0) { | ||||
| getboottime(&boottime); | getboottime(&boottime); | ||||
| cntr->timestamp += boottime.tv_sec; | cntr->timestamp += boottime.tv_sec; | ||||
| } | } | ||||
| } | } | ||||
| static void | |||||
| export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr) | |||||
| { | |||||
| struct timeval boottime; | |||||
| if (krule->cntr != NULL) { | |||||
| cntr->pcnt = counter_u64_fetch(krule->cntr); | |||||
| cntr->bcnt = counter_u64_fetch(krule->cntr + 1); | |||||
| cntr->timestamp = krule->timestamp; | |||||
| } | |||||
| if (cntr->timestamp > 0) { | |||||
| getboottime(&boottime); | |||||
| cntr->timestamp += boottime.tv_sec; | |||||
| } | |||||
| } | |||||
| /* | /* | ||||
| * Copies rule @urule from v1 userland format (current). | |||||
| * to kernel @krule. | |||||
| * Assume @krule is zeroed. | |||||
| */ | |||||
| static void | |||||
| import_rule1(struct rule_check_info *ci) | |||||
| { | |||||
| struct ip_fw_rule *urule; | |||||
| struct ip_fw *krule; | |||||
| urule = (struct ip_fw_rule *)ci->urule; | |||||
| krule = (struct ip_fw *)ci->krule; | |||||
| /* copy header */ | |||||
| krule->act_ofs = urule->act_ofs; | |||||
| krule->cmd_len = urule->cmd_len; | |||||
| krule->rulenum = urule->rulenum; | |||||
| krule->set = urule->set; | |||||
| krule->flags = urule->flags; | |||||
| /* Save rulenum offset */ | |||||
| ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); | |||||
| /* Copy opcodes */ | |||||
| memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); | |||||
| } | |||||
| /* | |||||
| * Export rule into v1 format (Current). | * Export rule into v1 format (Current). | ||||
| * Layout: | * Layout: | ||||
| * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT) | * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT) | ||||
| * [ ip_fw_rule ] OR | * [ ip_fw_rule ] OR | ||||
| * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs). | * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs). | ||||
| * ] | * ] | ||||
| * Assume @data is zeroed. | * Assume @data is zeroed. | ||||
| */ | */ | ||||
| Show All 25 Lines | export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs) | ||||
| urule->flags = krule->flags; | urule->flags = krule->flags; | ||||
| urule->id = krule->id; | urule->id = krule->id; | ||||
| /* Copy opcodes */ | /* Copy opcodes */ | ||||
| memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); | memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); | ||||
| } | } | ||||
| /* | /* | ||||
| * Copies rule @urule from FreeBSD8 userland format (v0) | |||||
| * to kernel @krule. | |||||
| * Assume @krule is zeroed. | |||||
| */ | |||||
| static void | |||||
| import_rule0(struct rule_check_info *ci) | |||||
| { | |||||
| struct ip_fw_rule0 *urule; | |||||
| struct ip_fw *krule; | |||||
| int cmdlen, l; | |||||
| ipfw_insn *cmd; | |||||
| ipfw_insn_limit *lcmd; | |||||
| ipfw_insn_if *cmdif; | |||||
| urule = (struct ip_fw_rule0 *)ci->urule; | |||||
| krule = (struct ip_fw *)ci->krule; | |||||
| /* copy header */ | |||||
| krule->act_ofs = urule->act_ofs; | |||||
| krule->cmd_len = urule->cmd_len; | |||||
| krule->rulenum = urule->rulenum; | |||||
| krule->set = urule->set; | |||||
| if ((urule->_pad & 1) != 0) | |||||
| krule->flags |= IPFW_RULE_NOOPT; | |||||
| /* Save rulenum offset */ | |||||
| ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum); | |||||
| /* Copy opcodes */ | |||||
| memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); | |||||
| /* | |||||
| * Alter opcodes: | |||||
| * 1) convert tablearg value from 65535 to 0 | |||||
| * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room | |||||
| * for targ). | |||||
| * 3) convert table number in iface opcodes to u16 | |||||
| * 4) convert old `nat global` into new 65535 | |||||
| */ | |||||
| l = krule->cmd_len; | |||||
| cmd = krule->cmd; | |||||
| cmdlen = 0; | |||||
| for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | |||||
| cmdlen = F_LEN(cmd); | |||||
| switch (cmd->opcode) { | |||||
| /* Opcodes supporting tablearg */ | |||||
| case O_TAG: | |||||
| case O_TAGGED: | |||||
| case O_PIPE: | |||||
| case O_QUEUE: | |||||
| case O_DIVERT: | |||||
| case O_TEE: | |||||
| case O_SKIPTO: | |||||
| case O_CALLRETURN: | |||||
| case O_NETGRAPH: | |||||
| case O_NGTEE: | |||||
| case O_NAT: | |||||
| if (cmd->arg1 == IP_FW_TABLEARG) | |||||
| cmd->arg1 = IP_FW_TARG; | |||||
| else if (cmd->arg1 == 0) | |||||
| cmd->arg1 = IP_FW_NAT44_GLOBAL; | |||||
| break; | |||||
| case O_SETFIB: | |||||
| case O_SETDSCP: | |||||
| case O_SETMARK: | |||||
| case O_MARK: | |||||
| if (cmd->arg1 == IP_FW_TABLEARG) | |||||
| cmd->arg1 = IP_FW_TARG; | |||||
| else | |||||
| cmd->arg1 |= 0x8000; | |||||
| break; | |||||
| case O_LIMIT: | |||||
| lcmd = (ipfw_insn_limit *)cmd; | |||||
| if (lcmd->conn_limit == IP_FW_TABLEARG) | |||||
| lcmd->conn_limit = IP_FW_TARG; | |||||
| break; | |||||
| /* Interface tables */ | |||||
| case O_XMIT: | |||||
| case O_RECV: | |||||
| case O_VIA: | |||||
| /* Interface table, possibly */ | |||||
| cmdif = (ipfw_insn_if *)cmd; | |||||
| if (cmdif->name[0] != '\1') | |||||
| break; | |||||
| cmdif->p.kidx = (uint16_t)cmdif->p.glob; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Copies rule @krule from kernel to FreeBSD8 userland format (v0) | |||||
| */ | |||||
| static void | |||||
| export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len) | |||||
| { | |||||
| int cmdlen, l; | |||||
| ipfw_insn *cmd; | |||||
| ipfw_insn_limit *lcmd; | |||||
| ipfw_insn_if *cmdif; | |||||
| /* copy header */ | |||||
| memset(urule, 0, len); | |||||
| urule->act_ofs = krule->act_ofs; | |||||
| urule->cmd_len = krule->cmd_len; | |||||
| urule->rulenum = krule->rulenum; | |||||
| urule->set = krule->set; | |||||
| if ((krule->flags & IPFW_RULE_NOOPT) != 0) | |||||
| urule->_pad |= 1; | |||||
| /* Copy opcodes */ | |||||
| memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); | |||||
| /* Export counters */ | |||||
| export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt); | |||||
| /* | |||||
| * Alter opcodes: | |||||
| * 1) convert tablearg value from 0 to 65535 | |||||
| * 2) Remove highest bit from O_SETFIB/O_SETDSCP values. | |||||
| * 3) convert table number in iface opcodes to int | |||||
| */ | |||||
| l = urule->cmd_len; | |||||
| cmd = urule->cmd; | |||||
| cmdlen = 0; | |||||
| for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | |||||
| cmdlen = F_LEN(cmd); | |||||
| switch (cmd->opcode) { | |||||
| /* Opcodes supporting tablearg */ | |||||
| case O_TAG: | |||||
| case O_TAGGED: | |||||
| case O_PIPE: | |||||
| case O_QUEUE: | |||||
| case O_DIVERT: | |||||
| case O_TEE: | |||||
| case O_SKIPTO: | |||||
| case O_CALLRETURN: | |||||
| case O_NETGRAPH: | |||||
| case O_NGTEE: | |||||
| case O_NAT: | |||||
| if (cmd->arg1 == IP_FW_TARG) | |||||
| cmd->arg1 = IP_FW_TABLEARG; | |||||
| else if (cmd->arg1 == IP_FW_NAT44_GLOBAL) | |||||
| cmd->arg1 = 0; | |||||
| break; | |||||
| case O_SETFIB: | |||||
| case O_SETDSCP: | |||||
| case O_SETMARK: | |||||
| case O_MARK: | |||||
| if (cmd->arg1 == IP_FW_TARG) | |||||
| cmd->arg1 = IP_FW_TABLEARG; | |||||
| else | |||||
| cmd->arg1 &= ~0x8000; | |||||
| break; | |||||
| case O_LIMIT: | |||||
| lcmd = (ipfw_insn_limit *)cmd; | |||||
| if (lcmd->conn_limit == IP_FW_TARG) | |||||
| lcmd->conn_limit = IP_FW_TABLEARG; | |||||
| break; | |||||
| /* Interface tables */ | |||||
| case O_XMIT: | |||||
| case O_RECV: | |||||
| case O_VIA: | |||||
| /* Interface table, possibly */ | |||||
| cmdif = (ipfw_insn_if *)cmd; | |||||
| if (cmdif->name[0] != '\1') | |||||
| break; | |||||
| cmdif->p.glob = cmdif->p.kidx; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* | |||||
| * Add new rule(s) to the list possibly creating rule number for each. | * Add new rule(s) to the list possibly creating rule number for each. | ||||
| * Update the rule_number in the input struct so the caller knows it as well. | * Update the rule_number in the input struct so the caller knows it as well. | ||||
| * Must be called without IPFW_UH held | * Must be called without IPFW_UH held | ||||
| */ | */ | ||||
| static int | int | ||||
| commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count) | ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, | ||||
| int count) | |||||
| { | { | ||||
| int error, i, insert_before, tcount; | int error, i, insert_before, tcount, rule_idx, last_rule_idx; | ||||
| uint16_t rulenum, *pnum; | uint32_t rulenum; | ||||
| struct rule_check_info *ci; | struct rule_check_info *ci; | ||||
| struct ip_fw *krule; | struct ip_fw *krule; | ||||
| struct ip_fw **map; /* the new array of pointers */ | struct ip_fw **map; /* the new array of pointers */ | ||||
| /* Check if we need to do table/obj index remap */ | /* Check if we need to do table/obj index remap */ | ||||
| tcount = 0; | tcount = 0; | ||||
| for (ci = rci, i = 0; i < count; ci++, i++) { | for (ci = rci, i = 0; i < count; ci++, i++) { | ||||
| if (ci->object_opcodes == 0) | if (ci->object_opcodes == 0) | ||||
| continue; | continue; | ||||
| /* | /* | ||||
| * Rule has some object opcodes. | * Rule has some object opcodes. | ||||
| * We need to find (and create non-existing) | * We need to find (and create non-existing) | ||||
| * kernel objects, and reference existing ones. | * kernel objects, and reference existing ones. | ||||
| */ | */ | ||||
| error = rewrite_rule_uidx(chain, ci); | error = rewrite_rule_uidx(chain, ci); | ||||
| if (error != 0) { | if (error != 0) { | ||||
| /* | /* | ||||
| * rewrite failed, state for current rule | * rewrite failed, state for current rule | ||||
| * has been reverted. Check if we need to | * has been reverted. Check if we need to | ||||
| * revert more. | * revert more. | ||||
| */ | */ | ||||
| if (tcount > 0) { | if (tcount > 0) { | ||||
| /* | /* | ||||
| * We have some more table rules | * We have some more table rules | ||||
| * we need to rollback. | * we need to rollback. | ||||
| */ | */ | ||||
| IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
| while (ci != rci) { | while (ci != rci) { | ||||
| ci--; | ci--; | ||||
| if (ci->object_opcodes == 0) | if (ci->object_opcodes == 0) | ||||
| continue; | continue; | ||||
| unref_rule_objects(chain,ci->krule); | unref_rule_objects(chain,ci->krule); | ||||
| } | } | ||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| } | } | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| tcount++; | tcount++; | ||||
| } | } | ||||
| Show All 15 Lines | if (map == NULL) { | ||||
| return (ENOSPC); | return (ENOSPC); | ||||
| } | } | ||||
| if (V_autoinc_step < 1) | if (V_autoinc_step < 1) | ||||
| V_autoinc_step = 1; | V_autoinc_step = 1; | ||||
| else if (V_autoinc_step > 1000) | else if (V_autoinc_step > 1000) | ||||
| V_autoinc_step = 1000; | V_autoinc_step = 1000; | ||||
| /* FIXME: Handle count > 1 */ | last_rule_idx = 0; | ||||
| ci = rci; | for (ci = rci, i = 0; i < count; ci++, i++) { | ||||
| krule = ci->krule; | krule = ci->krule; | ||||
| rulenum = krule->rulenum; | rulenum = krule->rulenum; | ||||
| krule->id = chain->id + 1; | |||||
| /* find the insertion point, we will insert before */ | /* find the insertion point, we will insert before */ | ||||
| insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; | insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; | ||||
| i = ipfw_find_rule(chain, insert_before, 0); | rule_idx = ipfw_find_rule(chain, insert_before, 0); | ||||
| /* duplicate first part */ | /* duplicate the previous part */ | ||||
| if (i > 0) | if (last_rule_idx < rule_idx) | ||||
| bcopy(chain->map, map, i * sizeof(struct ip_fw *)); | bcopy(chain->map + last_rule_idx, map + last_rule_idx + i, | ||||
| map[i] = krule; | (rule_idx - last_rule_idx) * sizeof(struct ip_fw *)); | ||||
| /* duplicate remaining part, we always have the default rule */ | last_rule_idx = rule_idx; | ||||
| bcopy(chain->map + i, map + i + 1, | map[rule_idx + i] = krule; | ||||
| sizeof(struct ip_fw *) *(chain->n_rules - i)); | |||||
| if (rulenum == 0) { | if (rulenum == 0) { | ||||
| /* Compute rule number and write it back */ | /* Compute rule number and write it back */ | ||||
| rulenum = i > 0 ? map[i-1]->rulenum : 0; | rulenum = rule_idx + i > 0 ? map[rule_idx + i - 1]->rulenum : 0; | ||||
| if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) | if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) | ||||
| rulenum += V_autoinc_step; | rulenum += V_autoinc_step; | ||||
| krule->rulenum = rulenum; | krule->rulenum = rulenum; | ||||
| /* Save number to userland rule */ | /* Save number to userland rule */ | ||||
| pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff); | memcpy((char *)ci->urule + ci->urule_numoff, &rulenum, | ||||
| *pnum = rulenum; | sizeof(rulenum)); | ||||
| } | } | ||||
| } | |||||
| krule->id = chain->id + 1; | /* duplicate the remaining part, we always have the default rule */ | ||||
| bcopy(chain->map + last_rule_idx, map + last_rule_idx + count, | |||||
| (chain->n_rules - last_rule_idx) * sizeof(struct ip_fw *)); | |||||
| if (V_skipto_cache != 0) | |||||
| update_skipto_cache(chain, map); | update_skipto_cache(chain, map); | ||||
| map = swap_map(chain, map, chain->n_rules + 1); | map = swap_map(chain, map, chain->n_rules + count); | ||||
| chain->static_len += RULEUSIZE0(krule); | |||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| if (map) | if (map) | ||||
| free(map, M_IPFW); | free(map, M_IPFW); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule, | ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule, | ||||
| int locked) | int locked) | ||||
| { | { | ||||
| struct ip_fw **map; | struct ip_fw **map; | ||||
| map = get_map(chain, 1, locked); | map = get_map(chain, 1, locked); | ||||
| if (map == NULL) | if (map == NULL) | ||||
| return (ENOMEM); | return (ENOMEM); | ||||
| if (chain->n_rules > 0) | if (chain->n_rules > 0) | ||||
| bcopy(chain->map, map, | bcopy(chain->map, map, | ||||
| chain->n_rules * sizeof(struct ip_fw *)); | chain->n_rules * sizeof(struct ip_fw *)); | ||||
| map[chain->n_rules] = rule; | map[chain->n_rules] = rule; | ||||
| rule->rulenum = IPFW_DEFAULT_RULE; | rule->rulenum = IPFW_DEFAULT_RULE; | ||||
| rule->set = RESVD_SET; | rule->set = RESVD_SET; | ||||
| rule->id = chain->id + 1; | rule->id = chain->id + 1; | ||||
| /* We add rule in the end of chain, no need to update skipto cache */ | /* We add rule in the end of chain, no need to update skipto cache */ | ||||
| map = swap_map(chain, map, chain->n_rules + 1); | map = swap_map(chain, map, chain->n_rules + 1); | ||||
| chain->static_len += RULEUSIZE0(rule); | |||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| free(map, M_IPFW); | free(map, M_IPFW); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * Adds @rule to the list of rules to reap | * Adds @rule to the list of rules to reap | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt) | ||||
| if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 && | if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 && | ||||
| (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule)) | (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule)) | ||||
| return (0); | return (0); | ||||
| return (1); | return (1); | ||||
| } | } | ||||
| struct manage_sets_args { | struct manage_sets_args { | ||||
| uint16_t set; | uint32_t set; | ||||
| uint8_t new_set; | uint8_t new_set; | ||||
| }; | }; | ||||
| static int | static int | ||||
| swap_sets_cb(struct namedobj_instance *ni, struct named_object *no, | swap_sets_cb(struct namedobj_instance *ni, struct named_object *no, | ||||
| void *arg) | void *arg) | ||||
| { | { | ||||
| struct manage_sets_args *args; | struct manage_sets_args *args; | ||||
| Show All 33 Lines | test_sets_cb(struct namedobj_instance *ni, struct named_object *no, | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * Generic function to handler moving and swapping sets. | * Generic function to handler moving and swapping sets. | ||||
| */ | */ | ||||
| int | int | ||||
| ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, | ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, | ||||
| uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) | uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd) | ||||
| { | { | ||||
| struct manage_sets_args args; | struct manage_sets_args args; | ||||
| struct named_object *no; | struct named_object *no; | ||||
| args.set = set; | args.set = set; | ||||
| args.new_set = new_set; | args.new_set = new_set; | ||||
| switch (cmd) { | switch (cmd) { | ||||
| case SWAP_ALL: | case SWAP_ALL: | ||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
| * Saves number of deleted rules in @ndel. | * Saves number of deleted rules in @ndel. | ||||
| * | * | ||||
| * Returns 0 on success. | * Returns 0 on success. | ||||
| */ | */ | ||||
| static int | static int | ||||
| delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel) | delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel) | ||||
| { | { | ||||
| struct ip_fw *reap, *rule, **map; | struct ip_fw *reap, *rule, **map; | ||||
| int end, start; | uint32_t end, start; | ||||
| int i, n, ndyn, ofs; | int i, n, ndyn, ofs; | ||||
| reap = NULL; | reap = NULL; | ||||
| IPFW_UH_WLOCK(chain); /* arbitrate writers */ | IPFW_UH_WLOCK(chain); /* arbitrate writers */ | ||||
| /* | /* | ||||
| * Stage 1: Determine range to inspect. | * Stage 1: Determine range to inspect. | ||||
| * Range is half-inclusive, e.g [start, end). | * Range is half-inclusive, e.g [start, end). | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel) | ||||
| /* 6. Remove all dynamic states originated by deleted rules */ | /* 6. Remove all dynamic states originated by deleted rules */ | ||||
| if (ndyn > 0) | if (ndyn > 0) | ||||
| ipfw_expire_dyn_states(chain, rt); | ipfw_expire_dyn_states(chain, rt); | ||||
| /* 7. now remove the rules deleted from the old map */ | /* 7. now remove the rules deleted from the old map */ | ||||
| for (i = start; i < end; i++) { | for (i = start; i < end; i++) { | ||||
| rule = map[i]; | rule = map[i]; | ||||
| if (ipfw_match_range(rule, rt) == 0) | if (ipfw_match_range(rule, rt) == 0) | ||||
| continue; | continue; | ||||
| chain->static_len -= RULEUSIZE0(rule); | |||||
| ipfw_reap_add(chain, &reap, rule); | ipfw_reap_add(chain, &reap, rule); | ||||
| } | } | ||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| ipfw_reap_rules(reap); | ipfw_reap_rules(reap); | ||||
| if (map != NULL) | if (map != NULL) | ||||
| free(map, M_IPFW); | free(map, M_IPFW); | ||||
| *ndel = n; | *ndel = n; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt) | move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw; | struct opcode_obj_rewrite *rw; | ||||
| struct ip_fw *rule; | struct ip_fw *rule; | ||||
| ipfw_insn *cmd; | ipfw_insn *cmd; | ||||
| uint32_t kidx; | |||||
| int cmdlen, i, l, c; | int cmdlen, i, l, c; | ||||
| uint16_t kidx; | |||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| /* Stage 1: count number of references by given rules */ | /* Stage 1: count number of references by given rules */ | ||||
| for (c = 0, i = 0; i < ch->n_rules - 1; i++) { | for (c = 0, i = 0; i < ch->n_rules - 1; i++) { | ||||
| rule = ch->map[i]; | rule = ch->map[i]; | ||||
| if (ipfw_match_range(rule, rt) == 0) | if (ipfw_match_range(rule, rt) == 0) | ||||
| continue; | continue; | ||||
| ▲ Show 20 Lines • Show All 396 Lines • ▼ Show 20 Lines | case IP_FW_SET_ENABLE: | ||||
| enable_sets(chain, &rh->range); | enable_sets(chain, &rh->range); | ||||
| break; | break; | ||||
| } | } | ||||
| IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
| return (ret); | return (ret); | ||||
| } | } | ||||
| /** | /* Check rule format */ | ||||
| * Remove all rules with given number, or do set manipulation. | int | ||||
| * Assumes chain != NULL && *chain != NULL. | ipfw_check_rule(struct ip_fw_rule *rule, size_t size, | ||||
| * | |||||
| * The argument is an uint32_t. The low 16 bit are the rule or set number; | |||||
| * the next 8 bits are the new set; the top 8 bits indicate the command: | |||||
| * | |||||
| * 0 delete rules numbered "rulenum" | |||||
| * 1 delete rules in set "rulenum" | |||||
| * 2 move rules "rulenum" to set "new_set" | |||||
| * 3 move rules from set "rulenum" to set "new_set" | |||||
| * 4 swap sets "rulenum" and "new_set" | |||||
| * 5 delete rules "rulenum" and set "new_set" | |||||
| */ | |||||
| static int | |||||
| del_entry(struct ip_fw_chain *chain, uint32_t arg) | |||||
| { | |||||
| uint32_t num; /* rule number or old_set */ | |||||
| uint8_t cmd, new_set; | |||||
| int do_del, ndel; | |||||
| int error = 0; | |||||
| ipfw_range_tlv rt; | |||||
| num = arg & 0xffff; | |||||
| cmd = (arg >> 24) & 0xff; | |||||
| new_set = (arg >> 16) & 0xff; | |||||
| if (cmd > 5 || new_set > RESVD_SET) | |||||
| return EINVAL; | |||||
| if (cmd == 0 || cmd == 2 || cmd == 5) { | |||||
| if (num >= IPFW_DEFAULT_RULE) | |||||
| return EINVAL; | |||||
| } else { | |||||
| if (num > RESVD_SET) /* old_set */ | |||||
| return EINVAL; | |||||
| } | |||||
| /* Convert old requests into new representation */ | |||||
| memset(&rt, 0, sizeof(rt)); | |||||
| rt.start_rule = num; | |||||
| rt.end_rule = num; | |||||
| rt.set = num; | |||||
| rt.new_set = new_set; | |||||
| do_del = 0; | |||||
| switch (cmd) { | |||||
| case 0: /* delete rules numbered "rulenum" */ | |||||
| if (num == 0) | |||||
| rt.flags |= IPFW_RCFLAG_ALL; | |||||
| else | |||||
| rt.flags |= IPFW_RCFLAG_RANGE; | |||||
| do_del = 1; | |||||
| break; | |||||
| case 1: /* delete rules in set "rulenum" */ | |||||
| rt.flags |= IPFW_RCFLAG_SET; | |||||
| do_del = 1; | |||||
| break; | |||||
| case 5: /* delete rules "rulenum" and set "new_set" */ | |||||
| rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET; | |||||
| rt.set = new_set; | |||||
| rt.new_set = 0; | |||||
| do_del = 1; | |||||
| break; | |||||
| case 2: /* move rules "rulenum" to set "new_set" */ | |||||
| rt.flags |= IPFW_RCFLAG_RANGE; | |||||
| break; | |||||
| case 3: /* move rules from set "rulenum" to set "new_set" */ | |||||
| IPFW_UH_WLOCK(chain); | |||||
| error = swap_sets(chain, &rt, 1); | |||||
| IPFW_UH_WUNLOCK(chain); | |||||
| return (error); | |||||
| case 4: /* swap sets "rulenum" and "new_set" */ | |||||
| IPFW_UH_WLOCK(chain); | |||||
| error = swap_sets(chain, &rt, 0); | |||||
| IPFW_UH_WUNLOCK(chain); | |||||
| return (error); | |||||
| default: | |||||
| return (ENOTSUP); | |||||
| } | |||||
| if (do_del != 0) { | |||||
| if ((error = delete_range(chain, &rt, &ndel)) != 0) | |||||
| return (error); | |||||
| if (ndel == 0 && (cmd != 1 && num != 0)) | |||||
| return (EINVAL); | |||||
| return (0); | |||||
| } | |||||
| return (move_range(chain, &rt)); | |||||
| } | |||||
| /** | |||||
| * Reset some or all counters on firewall rules. | |||||
| * The argument `arg' is an u_int32_t. The low 16 bit are the rule number, | |||||
| * the next 8 bits are the set number, the top 8 bits are the command: | |||||
| * 0 work with rules from all set's; | |||||
| * 1 work with rules only from specified set. | |||||
| * Specified rule number is zero if we want to clear all entries. | |||||
| * log_only is 1 if we only want to reset logs, zero otherwise. | |||||
| */ | |||||
| static int | |||||
| zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) | |||||
| { | |||||
| struct ip_fw *rule; | |||||
| char *msg; | |||||
| int i; | |||||
| uint16_t rulenum = arg & 0xffff; | |||||
| uint8_t set = (arg >> 16) & 0xff; | |||||
| uint8_t cmd = (arg >> 24) & 0xff; | |||||
| if (cmd > 1) | |||||
| return (EINVAL); | |||||
| if (cmd == 1 && set > RESVD_SET) | |||||
| return (EINVAL); | |||||
| IPFW_UH_RLOCK(chain); | |||||
| if (rulenum == 0) { | |||||
| V_norule_counter = 0; | |||||
| for (i = 0; i < chain->n_rules; i++) { | |||||
| rule = chain->map[i]; | |||||
| /* Skip rules not in our set. */ | |||||
| if (cmd == 1 && rule->set != set) | |||||
| continue; | |||||
| clear_counters(rule, log_only); | |||||
| } | |||||
| msg = log_only ? "All logging counts reset" : | |||||
| "Accounting cleared"; | |||||
| } else { | |||||
| int cleared = 0; | |||||
| for (i = 0; i < chain->n_rules; i++) { | |||||
| rule = chain->map[i]; | |||||
| if (rule->rulenum == rulenum) { | |||||
| if (cmd == 0 || rule->set == set) | |||||
| clear_counters(rule, log_only); | |||||
| cleared = 1; | |||||
| } | |||||
| if (rule->rulenum > rulenum) | |||||
| break; | |||||
| } | |||||
| if (!cleared) { /* we did not find any matching rules */ | |||||
| IPFW_UH_RUNLOCK(chain); | |||||
| return (EINVAL); | |||||
| } | |||||
| msg = log_only ? "logging count reset" : "cleared"; | |||||
| } | |||||
| IPFW_UH_RUNLOCK(chain); | |||||
| if (V_fw_verbose) { | |||||
| int lev = LOG_SECURITY | LOG_NOTICE; | |||||
| if (rulenum) | |||||
| log(lev, "ipfw: Entry %d %s.\n", rulenum, msg); | |||||
| else | |||||
| log(lev, "ipfw: %s.\n", msg); | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| /* | |||||
| * Check rule head in FreeBSD11 format | |||||
| * | |||||
| */ | |||||
| static int | |||||
| check_ipfw_rule1(struct ip_fw_rule *rule, int size, | |||||
| struct rule_check_info *ci) | struct rule_check_info *ci) | ||||
| { | { | ||||
| int l; | int l; | ||||
| if (size < sizeof(*rule)) { | if (size < sizeof(*rule)) { | ||||
| printf("ipfw: rule too short\n"); | printf("ipfw: rule too short\n"); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| /* Check for valid cmd_len */ | /* Check for valid cmd_len */ | ||||
| l = roundup2(RULESIZE(rule), sizeof(uint64_t)); | l = roundup2(RULESIZE(rule), sizeof(uint64_t)); | ||||
| if (l != size) { | if (l != size) { | ||||
| printf("ipfw: size mismatch (have %d want %d)\n", size, l); | printf("ipfw: size mismatch (have %zu want %d)\n", size, l); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| if (rule->act_ofs >= rule->cmd_len) { | if (rule->act_ofs >= rule->cmd_len) { | ||||
| printf("ipfw: bogus action offset (%u > %u)\n", | printf("ipfw: bogus action offset (%u > %u)\n", | ||||
| rule->act_ofs, rule->cmd_len - 1); | rule->act_ofs, rule->cmd_len - 1); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| if (rule->rulenum > IPFW_DEFAULT_RULE - 1) | if (rule->rulenum > IPFW_DEFAULT_RULE - 1) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); | return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); | ||||
| } | } | ||||
| /* | enum ipfw_opcheck_result | ||||
| * Check rule head in FreeBSD8 format | ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci) | ||||
| * | |||||
| */ | |||||
| static int | |||||
| check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, | |||||
| struct rule_check_info *ci) | |||||
| { | { | ||||
| int l; | ipfw_insn *cmd; | ||||
| size_t cmdlen; | |||||
| if (size < sizeof(*rule)) { | cmd = *pcmd; | ||||
| printf("ipfw: rule too short\n"); | |||||
| return (EINVAL); | |||||
| } | |||||
| /* Check for valid cmd_len */ | |||||
| l = sizeof(*rule) + rule->cmd_len * 4 - 4; | |||||
| if (l != size) { | |||||
| printf("ipfw: size mismatch (have %d want %d)\n", size, l); | |||||
| return (EINVAL); | |||||
| } | |||||
| if (rule->act_ofs >= rule->cmd_len) { | |||||
| printf("ipfw: bogus action offset (%u > %u)\n", | |||||
| rule->act_ofs, rule->cmd_len - 1); | |||||
| return (EINVAL); | |||||
| } | |||||
| if (rule->rulenum > IPFW_DEFAULT_RULE - 1) | |||||
| return (EINVAL); | |||||
| return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); | |||||
| } | |||||
| static int | |||||
| check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) | |||||
| { | |||||
| int cmdlen, l; | |||||
| int have_action; | |||||
| have_action = 0; | |||||
| /* | |||||
| * Now go for the individual checks. Very simple ones, basically only | |||||
| * instruction sizes. | |||||
| */ | |||||
| for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { | |||||
| cmdlen = F_LEN(cmd); | cmdlen = F_LEN(cmd); | ||||
| if (cmdlen > l) { | |||||
| printf("ipfw: opcode %d size truncated\n", | |||||
| cmd->opcode); | |||||
| return EINVAL; | |||||
| } | |||||
| switch (cmd->opcode) { | switch (cmd->opcode) { | ||||
| case O_PROBE_STATE: | case O_PROBE_STATE: | ||||
| case O_KEEP_STATE: | case O_KEEP_STATE: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| break; | break; | ||||
| case O_PROTO: | case O_PROTO: | ||||
| case O_IP_SRC_ME: | case O_IP_SRC_ME: | ||||
| case O_IP_DST_ME: | case O_IP_DST_ME: | ||||
| case O_LAYER2: | case O_LAYER2: | ||||
| case O_IN: | case O_IN: | ||||
| case O_FRAG: | case O_FRAG: | ||||
| case O_DIVERTED: | case O_DIVERTED: | ||||
| case O_IPOPT: | case O_IPOPT: | ||||
| case O_IPTOS: | case O_IPTOS: | ||||
| case O_IPPRECEDENCE: | case O_IPPRECEDENCE: | ||||
| case O_IPVER: | case O_IPVER: | ||||
| case O_SOCKARG: | case O_SOCKARG: | ||||
| case O_TCPFLAGS: | case O_TCPFLAGS: | ||||
| case O_TCPOPTS: | case O_TCPOPTS: | ||||
| case O_ESTAB: | case O_ESTAB: | ||||
| case O_VERREVPATH: | case O_VERREVPATH: | ||||
| case O_VERSRCREACH: | case O_VERSRCREACH: | ||||
| case O_ANTISPOOF: | case O_ANTISPOOF: | ||||
| case O_IPSEC: | case O_IPSEC: | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| case O_IP6_SRC_ME: | case O_IP6_SRC_ME: | ||||
| case O_IP6_DST_ME: | case O_IP6_DST_ME: | ||||
| case O_EXT_HDR: | case O_EXT_HDR: | ||||
| case O_IP6: | case O_IP6: | ||||
| #endif | #endif | ||||
| case O_IP4: | case O_IP4: | ||||
| case O_TAG: | case O_TAG: | ||||
| case O_SKIP_ACTION: | case O_SKIP_ACTION: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_EXTERNAL_ACTION: | case O_EXTERNAL_ACTION: | ||||
| if (cmd->arg1 == 0 || | if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) | ||||
| cmdlen != F_INSN_SIZE(ipfw_insn)) { | return (BAD_SIZE); | ||||
| printf("ipfw: invalid external " | |||||
| "action opcode\n"); | if (insntod(cmd, kidx)->kidx == 0) | ||||
| return (EINVAL); | return (FAILED); | ||||
| } | |||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| /* | /* | ||||
| * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA | * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA | ||||
| * opcode? | * opcode? | ||||
| */ | */ | ||||
| if (l != cmdlen) { | if (*plen != cmdlen) { | ||||
| l -= cmdlen; | *plen -= cmdlen; | ||||
| cmd += cmdlen; | cmd += cmdlen; | ||||
| *pcmd = cmd; | |||||
| cmdlen = F_LEN(cmd); | cmdlen = F_LEN(cmd); | ||||
| if (cmd->opcode == O_EXTERNAL_DATA) | if (cmd->opcode == O_EXTERNAL_DATA) | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| if (cmd->opcode != O_EXTERNAL_INSTANCE) { | if (cmd->opcode != O_EXTERNAL_INSTANCE) { | ||||
| printf("ipfw: invalid opcode " | printf("ipfw: invalid opcode " | ||||
| "next to external action %u\n", | "next to external action %u\n", | ||||
| cmd->opcode); | cmd->opcode); | ||||
| return (EINVAL); | return (FAILED); | ||||
| } | } | ||||
| if (cmd->arg1 == 0 || | if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) | ||||
| cmdlen != F_INSN_SIZE(ipfw_insn)) { | return (BAD_SIZE); | ||||
| printf("ipfw: invalid external " | if (insntod(cmd, kidx)->kidx == 0) | ||||
| "action instance opcode\n"); | return (FAILED); | ||||
| return (EINVAL); | |||||
| } | |||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| } | } | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| case O_FIB: | case O_FIB: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| if (cmd->arg1 >= rt_numfibs) { | if (cmd->arg1 >= rt_numfibs) { | ||||
| printf("ipfw: invalid fib number %d\n", | printf("ipfw: invalid fib number %d\n", | ||||
| cmd->arg1); | cmd->arg1); | ||||
| return EINVAL; | return (FAILED); | ||||
| } | } | ||||
| break; | break; | ||||
| case O_SETFIB: | case O_SETFIB: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| if ((cmd->arg1 != IP_FW_TARG) && | if ((cmd->arg1 != IP_FW_TARG) && | ||||
| ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) { | ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) { | ||||
| printf("ipfw: invalid fib number %d\n", | printf("ipfw: invalid fib number %d\n", | ||||
| cmd->arg1 & 0x7FFF); | cmd->arg1 & 0x7FFF); | ||||
| return EINVAL; | return (FAILED); | ||||
| } | } | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| case O_UID: | case O_UID: | ||||
| case O_GID: | case O_GID: | ||||
| case O_JAIL: | case O_JAIL: | ||||
| case O_IP_SRC: | case O_IP_SRC: | ||||
| case O_IP_DST: | case O_IP_DST: | ||||
| case O_TCPSEQ: | case O_TCPSEQ: | ||||
| case O_TCPACK: | case O_TCPACK: | ||||
| case O_PROB: | case O_PROB: | ||||
| case O_ICMPTYPE: | case O_ICMPTYPE: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_LIMIT: | case O_LIMIT: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| break; | break; | ||||
| case O_LOG: | case O_LOG: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_log)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| insntod(cmd, log)->log_left = insntod(cmd, log)->max_log; | |||||
| ((ipfw_insn_log *)cmd)->log_left = | |||||
| ((ipfw_insn_log *)cmd)->max_log; | |||||
| break; | break; | ||||
| case O_IP_SRC_MASK: | case O_IP_SRC_MASK: | ||||
| case O_IP_DST_MASK: | case O_IP_DST_MASK: | ||||
| /* only odd command lengths */ | /* only odd command lengths */ | ||||
| if ((cmdlen & 1) == 0) | if ((cmdlen & 1) == 0) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_IP_SRC_SET: | case O_IP_SRC_SET: | ||||
| case O_IP_DST_SET: | case O_IP_DST_SET: | ||||
| if (cmd->arg1 == 0 || cmd->arg1 > 256) { | if (cmd->arg1 == 0 || cmd->arg1 > 256) { | ||||
| printf("ipfw: invalid set size %d\n", | printf("ipfw: invalid set size %d\n", | ||||
| cmd->arg1); | cmd->arg1); | ||||
| return EINVAL; | return (FAILED); | ||||
| } | } | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + | if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + | ||||
| (cmd->arg1+31)/32 ) | (cmd->arg1+31)/32 ) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_IP_SRC_LOOKUP: | case O_IP_SRC_LOOKUP: | ||||
| if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) | |||||
| goto bad_size; | |||||
| case O_IP_DST_LOOKUP: | case O_IP_DST_LOOKUP: | ||||
| if (cmd->arg1 >= V_fw_tables_max) { | |||||
| printf("ipfw: invalid table number %d\n", | |||||
| cmd->arg1); | |||||
| return (EINVAL); | |||||
| } | |||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn) && | |||||
| cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 && | |||||
| cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | |||||
| goto bad_size; | |||||
| ci->object_opcodes++; | |||||
| break; | |||||
| case O_IP_FLOW_LOOKUP: | case O_IP_FLOW_LOOKUP: | ||||
| case O_MAC_DST_LOOKUP: | |||||
| case O_MAC_SRC_LOOKUP: | case O_MAC_SRC_LOOKUP: | ||||
| if (cmd->arg1 >= V_fw_tables_max) { | case O_MAC_DST_LOOKUP: | ||||
| printf("ipfw: invalid table number %d\n", | if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx) && | ||||
| cmd->arg1); | cmdlen != F_INSN_SIZE(ipfw_insn_table)) | ||||
| return (EINVAL); | return (BAD_SIZE); | ||||
| if (insntod(cmd, kidx)->kidx >= V_fw_tables_max) { | |||||
| printf("ipfw: invalid table index %u\n", | |||||
| insntod(cmd, kidx)->kidx); | |||||
| return (FAILED); | |||||
| } | } | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn) && | |||||
| cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | |||||
| goto bad_size; | |||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| break; | break; | ||||
| case O_MACADDR2: | case O_MACADDR2: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_NOP: | case O_NOP: | ||||
| case O_IPID: | case O_IPID: | ||||
| case O_IPTTL: | case O_IPTTL: | ||||
| case O_IPLEN: | case O_IPLEN: | ||||
| case O_TCPDATALEN: | case O_TCPDATALEN: | ||||
| case O_TCPMSS: | case O_TCPMSS: | ||||
| case O_TCPWIN: | case O_TCPWIN: | ||||
| case O_TAGGED: | case O_TAGGED: | ||||
| if (cmdlen < 1 || cmdlen > 31) | if (cmdlen < 1 || cmdlen > 31) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_DSCP: | case O_DSCP: | ||||
| case O_MARK: | case O_MARK: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) | if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_MAC_TYPE: | case O_MAC_TYPE: | ||||
| case O_IP_SRCPORT: | case O_IP_SRCPORT: | ||||
| case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ | case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */ | ||||
| if (cmdlen < 2 || cmdlen > 31) | if (cmdlen < 2 || cmdlen > 31) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_RECV: | case O_RECV: | ||||
| case O_XMIT: | case O_XMIT: | ||||
| case O_VIA: | case O_VIA: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_if)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| ci->object_opcodes++; | ci->object_opcodes++; | ||||
| break; | break; | ||||
| case O_ALTQ: | case O_ALTQ: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_altq)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_PIPE: | case O_PIPE: | ||||
| case O_QUEUE: | case O_QUEUE: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| case O_FORWARD_IP: | case O_FORWARD_IP: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| case O_FORWARD_IP6: | case O_FORWARD_IP6: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| #endif /* INET6 */ | #endif /* INET6 */ | ||||
| case O_DIVERT: | case O_DIVERT: | ||||
| case O_TEE: | case O_TEE: | ||||
| if (ip_divert_ptr == NULL) | if (ip_divert_ptr == NULL) | ||||
| return EINVAL; | return (FAILED); | ||||
| else | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto check_size; | return (BAD_SIZE); | ||||
| return (CHECK_ACTION); | |||||
| case O_NETGRAPH: | case O_NETGRAPH: | ||||
| case O_NGTEE: | case O_NGTEE: | ||||
| if (ng_ipfw_input_p == NULL) | if (ng_ipfw_input_p == NULL) | ||||
| return EINVAL; | return (FAILED); | ||||
| else | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto check_size; | return (BAD_SIZE); | ||||
| return (CHECK_ACTION); | |||||
| case O_NAT: | case O_NAT: | ||||
| if (!IPFW_NAT_LOADED) | if (!IPFW_NAT_LOADED) | ||||
| return EINVAL; | return (FAILED); | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| case O_CHECK_STATE: | |||||
| ci->object_opcodes++; | case O_SKIPTO: | ||||
| goto check_size; | case O_CALLRETURN: | ||||
| case O_SETMARK: | case O_SETMARK: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| goto check_action; | return (CHECK_ACTION); | ||||
| case O_REJECT: | |||||
| /* "unreach needfrag" has variable len. */ | case O_CHECK_STATE: | ||||
| if ((cmdlen == F_INSN_SIZE(ipfw_insn) || | if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) | ||||
| cmdlen == F_INSN_SIZE(ipfw_insn_u16))) | return (BAD_SIZE); | ||||
| goto check_action; | ci->object_opcodes++; | ||||
| /* FALLTHROUGH */ | return (CHECK_ACTION); | ||||
| case O_FORWARD_MAC: /* XXX not implemented yet */ | case O_FORWARD_MAC: /* XXX not implemented yet */ | ||||
| case O_COUNT: | case O_COUNT: | ||||
| case O_ACCEPT: | case O_ACCEPT: | ||||
| case O_DENY: | case O_DENY: | ||||
| case O_REJECT: | |||||
| case O_SETDSCP: | case O_SETDSCP: | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| case O_UNREACH6: | case O_UNREACH6: | ||||
| #endif | #endif | ||||
| case O_SKIPTO: | |||||
| case O_REASS: | case O_REASS: | ||||
| case O_CALLRETURN: | |||||
| check_size: | |||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn)) | if (cmdlen != F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| check_action: | return (CHECK_ACTION); | ||||
| if (have_action) { | |||||
| printf("ipfw: opcode %d, multiple actions" | |||||
| " not allowed\n", | |||||
| cmd->opcode); | |||||
| return (EINVAL); | |||||
| } | |||||
| have_action = 1; | |||||
| if (l != cmdlen) { | |||||
| printf("ipfw: opcode %d, action must be" | |||||
| " last opcode\n", | |||||
| cmd->opcode); | |||||
| return (EINVAL); | |||||
| } | |||||
| break; | |||||
| #ifdef INET6 | #ifdef INET6 | ||||
| case O_IP6_SRC: | case O_IP6_SRC: | ||||
| case O_IP6_DST: | case O_IP6_DST: | ||||
| if (cmdlen != F_INSN_SIZE(struct in6_addr) + | if (cmdlen != F_INSN_SIZE(struct in6_addr) + | ||||
| F_INSN_SIZE(ipfw_insn)) | F_INSN_SIZE(ipfw_insn)) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_FLOW6ID: | case O_FLOW6ID: | ||||
| if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + | if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + | ||||
| ((ipfw_insn_u32 *)cmd)->o.arg1) | ((ipfw_insn_u32 *)cmd)->o.arg1) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_IP6_SRC_MASK: | case O_IP6_SRC_MASK: | ||||
| case O_IP6_DST_MASK: | case O_IP6_DST_MASK: | ||||
| if ( !(cmdlen & 1) || cmdlen > 127) | if ( !(cmdlen & 1) || cmdlen > 127) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| case O_ICMP6TYPE: | case O_ICMP6TYPE: | ||||
| if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) | if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) | ||||
| goto bad_size; | return (BAD_SIZE); | ||||
| break; | break; | ||||
| #endif | #endif | ||||
| default: | default: | ||||
| switch (cmd->opcode) { | switch (cmd->opcode) { | ||||
| #ifndef INET6 | #ifndef INET6 | ||||
| case O_IP6_SRC_ME: | case O_IP6_SRC_ME: | ||||
| case O_IP6_DST_ME: | case O_IP6_DST_ME: | ||||
| case O_EXT_HDR: | case O_EXT_HDR: | ||||
| case O_IP6: | case O_IP6: | ||||
| case O_UNREACH6: | case O_UNREACH6: | ||||
| case O_IP6_SRC: | case O_IP6_SRC: | ||||
| case O_IP6_DST: | case O_IP6_DST: | ||||
| case O_FLOW6ID: | case O_FLOW6ID: | ||||
| case O_IP6_SRC_MASK: | case O_IP6_SRC_MASK: | ||||
| case O_IP6_DST_MASK: | case O_IP6_DST_MASK: | ||||
| case O_ICMP6TYPE: | case O_ICMP6TYPE: | ||||
| printf("ipfw: no IPv6 support in kernel\n"); | printf("ipfw: no IPv6 support in kernel\n"); | ||||
| return (EPROTONOSUPPORT); | return (FAILED); | ||||
| #endif | #endif | ||||
| default: | default: | ||||
| printf("ipfw: opcode %d, unknown opcode\n", | printf("ipfw: opcode %d: unknown opcode\n", | ||||
| cmd->opcode); | cmd->opcode); | ||||
| return (EINVAL); | return (FAILED); | ||||
| } | } | ||||
| } | } | ||||
| return (SUCCESS); | |||||
| } | } | ||||
| if (have_action == 0) { | |||||
| printf("ipfw: missing action\n"); | |||||
| return (EINVAL); | |||||
| } | |||||
| return 0; | |||||
| bad_size: | static __noinline int | ||||
| printf("ipfw: opcode %d size %d wrong\n", | check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) | ||||
| cmd->opcode, cmdlen); | |||||
| return (EINVAL); | |||||
| } | |||||
| /* | |||||
| * Translation of requests for compatibility with FreeBSD 7.2/8. | |||||
| * a static variable tells us if we have an old client from userland, | |||||
| * and if necessary we translate requests and responses between the | |||||
| * two formats. | |||||
| */ | |||||
| static int is7 = 0; | |||||
| struct ip_fw7 { | |||||
| struct ip_fw7 *next; /* linked list of rules */ | |||||
| struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */ | |||||
| /* 'next_rule' is used to pass up 'set_disable' status */ | |||||
| 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) */ | |||||
| // #define RESVD_SET 31 /* set for default and persistent rules */ | |||||
| uint8_t _pad; /* padding */ | |||||
| // uint32_t id; /* rule id, only in v.8 */ | |||||
| /* These fields are present in all rules. */ | |||||
| uint64_t pcnt; /* Packet counter */ | |||||
| uint64_t bcnt; /* Byte counter */ | |||||
| uint32_t timestamp; /* tv_sec of last match */ | |||||
| ipfw_insn cmd[1]; /* storage for commands */ | |||||
| }; | |||||
| static int convert_rule_to_7(struct ip_fw_rule0 *rule); | |||||
| static int convert_rule_to_8(struct ip_fw_rule0 *rule); | |||||
| #ifndef RULESIZE7 | |||||
| #define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ | |||||
| ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4) | |||||
| #endif | |||||
| /* | |||||
| * Copy the static and dynamic rules to the supplied buffer | |||||
| * and return the amount of space actually used. | |||||
| * Must be run under IPFW_UH_RLOCK | |||||
| */ | |||||
| static size_t | |||||
| ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) | |||||
| { | { | ||||
| char *bp = buf; | int cmdlen, l; | ||||
| char *ep = bp + space; | int have_action, ret; | ||||
| struct ip_fw *rule; | |||||
| struct ip_fw_rule0 *dst; | |||||
| struct timeval boottime; | |||||
| int error, i, l, warnflag; | |||||
| time_t boot_seconds; | |||||
| warnflag = 0; | |||||
| getboottime(&boottime); | |||||
| boot_seconds = boottime.tv_sec; | |||||
| for (i = 0; i < chain->n_rules; i++) { | |||||
| rule = chain->map[i]; | |||||
| if (is7) { | |||||
| /* Convert rule to FreeBSd 7.2 format */ | |||||
| l = RULESIZE7(rule); | |||||
| if (bp + l + sizeof(uint32_t) <= ep) { | |||||
| bcopy(rule, bp, l + sizeof(uint32_t)); | |||||
| error = set_legacy_obj_kidx(chain, | |||||
| (struct ip_fw_rule0 *)bp); | |||||
| if (error != 0) | |||||
| return (0); | |||||
| error = convert_rule_to_7((struct ip_fw_rule0 *) bp); | |||||
| if (error) | |||||
| return 0; /*XXX correct? */ | |||||
| /* | /* | ||||
| * XXX HACK. Store the disable mask in the "next" | * Now go for the individual checks. Very simple ones, basically only | ||||
| * pointer in a wild attempt to keep the ABI the same. | * instruction sizes. | ||||
| * Why do we do this on EVERY rule? | |||||
| */ | */ | ||||
| bcopy(&V_set_disable, | have_action = 0; | ||||
| &(((struct ip_fw7 *)bp)->next_rule), | for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { | ||||
| sizeof(V_set_disable)); | cmdlen = F_LEN(cmd); | ||||
| if (((struct ip_fw7 *)bp)->timestamp) | if (cmdlen > l) { | ||||
| ((struct ip_fw7 *)bp)->timestamp += boot_seconds; | printf("ipfw: opcode %d: size truncated\n", | ||||
| bp += l; | cmd->opcode); | ||||
| return (EINVAL); | |||||
| } | } | ||||
| continue; /* go to next rule */ | if (ci->version != IP_FW3_OPVER) | ||||
| } | ret = (*check_opcode_f)(&cmd, &l, ci); | ||||
| else | |||||
| ret = ipfw_check_opcode(&cmd, &l, ci); | |||||
| l = RULEUSIZE0(rule); | if (ret == CHECK_ACTION) { | ||||
| if (bp + l > ep) { /* should not happen */ | if (have_action != 0) { | ||||
| printf("overflow dumping static rules\n"); | printf("ipfw: opcode %d: multiple actions" | ||||
| break; | " not allowed\n", cmd->opcode); | ||||
| } | ret = FAILED; | ||||
| dst = (struct ip_fw_rule0 *)bp; | } else | ||||
| export_rule0(rule, dst, l); | have_action = 1; | ||||
| error = set_legacy_obj_kidx(chain, dst); | |||||
| /* | if (l != F_LEN(cmd)) { | ||||
| * XXX HACK. Store the disable mask in the "next" | printf("ipfw: opcode %d: action must be" | ||||
| * pointer in a wild attempt to keep the ABI the same. | " last opcode\n", cmd->opcode); | ||||
| * Why do we do this on EVERY rule? | ret = FAILED; | ||||
| * | } | ||||
| * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask | } | ||||
| * so we need to fail _after_ saving at least one mask. | switch (ret) { | ||||
| */ | case SUCCESS: | ||||
| bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); | |||||
| if (dst->timestamp) | |||||
| dst->timestamp += boot_seconds; | |||||
| bp += l; | |||||
| if (error != 0) { | |||||
| if (error == 2) { | |||||
| /* Non-fatal table rewrite error. */ | |||||
| warnflag = 1; | |||||
| continue; | continue; | ||||
| case BAD_SIZE: | |||||
| printf("ipfw: opcode %d: wrong size %d\n", | |||||
| cmd->opcode, cmdlen); | |||||
| /* FALLTHROUGH */ | |||||
| case FAILED: | |||||
| return (EINVAL); | |||||
| } | } | ||||
| printf("Stop on rule %d. Fail to convert table\n", | |||||
| rule->rulenum); | |||||
| break; | |||||
| } | } | ||||
| if (have_action == 0) { | |||||
| printf("ipfw: missing action\n"); | |||||
| return (EINVAL); | |||||
| } | } | ||||
| if (warnflag != 0) | return (0); | ||||
| printf("ipfw: process %s is using legacy interfaces," | |||||
| " consider rebuilding\n", ""); | |||||
| ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */ | |||||
| return (bp - (char *)buf); | |||||
| } | } | ||||
| struct dump_args { | struct dump_args { | ||||
| uint32_t b; /* start rule */ | uint32_t b; /* start rule */ | ||||
| uint32_t e; /* end rule */ | uint32_t e; /* end rule */ | ||||
| uint32_t rcount; /* number of rules */ | uint32_t rcount; /* number of rules */ | ||||
| uint32_t rsize; /* rules size */ | uint32_t rsize; /* rules size */ | ||||
| uint32_t tcount; /* number of tables */ | uint32_t tcount; /* number of tables */ | ||||
| Show All 13 Lines | |||||
| /* | /* | ||||
| * Export named object info in instance @ni, identified by @kidx | * Export named object info in instance @ni, identified by @kidx | ||||
| * to ipfw_obj_ntlv. TLV is allocated from @sd space. | * to ipfw_obj_ntlv. TLV is allocated from @sd space. | ||||
| * | * | ||||
| * Returns 0 on success. | * Returns 0 on success. | ||||
| */ | */ | ||||
| static int | static int | ||||
| export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx, | export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx, | ||||
| struct sockopt_data *sd) | struct sockopt_data *sd) | ||||
| { | { | ||||
| struct named_object *no; | struct named_object *no; | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| no = ipfw_objhash_lookup_kidx(ni, kidx); | no = ipfw_objhash_lookup_kidx(ni, kidx); | ||||
| KASSERT(no != NULL, ("invalid object kernel index passed")); | KASSERT(no != NULL, ("invalid object kernel index passed")); | ||||
| ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); | ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); | ||||
| if (ntlv == NULL) | if (ntlv == NULL) | ||||
| return (ENOMEM); | return (ENOMEM); | ||||
| ipfw_export_obj_ntlv(no, ntlv); | ipfw_export_obj_ntlv(no, ntlv); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static int | static int | ||||
| export_named_objects(struct namedobj_instance *ni, struct dump_args *da, | export_named_objects(struct namedobj_instance *ni, struct dump_args *da, | ||||
| struct sockopt_data *sd) | struct sockopt_data *sd) | ||||
| { | { | ||||
| int error, i; | uint32_t i; | ||||
| int error; | |||||
| for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) { | for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) { | ||||
| if ((da->bmask[i / 32] & (1 << (i % 32))) == 0) | if ((da->bmask[i / 32] & (1 << (i % 32))) == 0) | ||||
| continue; | continue; | ||||
| if ((error = export_objhash_ntlv(ni, i, sd)) != 0) | if ((error = export_objhash_ntlv(ni, i, sd)) != 0) | ||||
| return (error); | return (error); | ||||
| da->tcount--; | da->tcount--; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | for (i = da->b; i < da->e; i++) { | ||||
| export_rule1(krule, dst, l, da->rcounters); | export_rule1(krule, dst, l, da->rcounters); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint16_t kidx) | ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint32_t kidx) | ||||
| { | { | ||||
| uint32_t bidx; | uint32_t bidx; | ||||
| /* | /* | ||||
| * Maintain separate bitmasks for table and non-table objects. | * Maintain separate bitmasks for table and non-table objects. | ||||
| */ | */ | ||||
| bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32; | bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32; | ||||
| bidx += kidx / 32; | bidx += kidx / 32; | ||||
| Show All 10 Lines | |||||
| * or its part. | * or its part. | ||||
| */ | */ | ||||
| static void | static void | ||||
| mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, | mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule, | ||||
| struct dump_args *da) | struct dump_args *da) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw; | struct opcode_obj_rewrite *rw; | ||||
| ipfw_insn *cmd; | ipfw_insn *cmd; | ||||
| uint32_t kidx; | |||||
| int cmdlen, l; | int cmdlen, l; | ||||
| uint16_t kidx; | |||||
| uint8_t subtype; | uint8_t subtype; | ||||
| l = rule->cmd_len; | l = rule->cmd_len; | ||||
| cmd = rule->cmd; | cmd = rule->cmd; | ||||
| cmdlen = 0; | cmdlen = 0; | ||||
| for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | ||||
| cmdlen = F_LEN(cmd); | cmdlen = F_LEN(cmd); | ||||
| ▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | |||||
| * Return 0 on success. | * Return 0 on success. | ||||
| */ | */ | ||||
| int | int | ||||
| create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd, | create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd, | ||||
| struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti) | struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw; | struct opcode_obj_rewrite *rw; | ||||
| struct obj_idx *p; | struct obj_idx *p; | ||||
| uint16_t kidx; | uint32_t kidx; | ||||
| int error; | int error; | ||||
| /* | /* | ||||
| * Compatibility stuff: do actual creation for non-existing, | * Compatibility stuff: do actual creation for non-existing, | ||||
| * but referenced objects. | * but referenced objects. | ||||
| */ | */ | ||||
| for (p = oib; p < pidx; p++) { | for (p = oib; p < pidx; p++) { | ||||
| if (p->kidx != 0) | if (p->kidx != 0) | ||||
| Show All 26 Lines | for (p = oib; p < pidx; p++) { | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * Compatibility function for old ipfw(8) binaries. | |||||
| * Rewrites table/nat kernel indices with userland ones. | |||||
| * Convert tables matching '/^\d+$/' to their atoi() value. | |||||
| * Use number 65535 for other tables. | |||||
| * | |||||
| * Returns 0 on success. | |||||
| */ | |||||
| static int | |||||
| set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule) | |||||
| { | |||||
| struct opcode_obj_rewrite *rw; | |||||
| struct named_object *no; | |||||
| ipfw_insn *cmd; | |||||
| char *end; | |||||
| long val; | |||||
| int cmdlen, error, l; | |||||
| uint16_t kidx, uidx; | |||||
| uint8_t subtype; | |||||
| error = 0; | |||||
| l = rule->cmd_len; | |||||
| cmd = rule->cmd; | |||||
| cmdlen = 0; | |||||
| for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | |||||
| cmdlen = F_LEN(cmd); | |||||
| /* Check if is index in given opcode */ | |||||
| rw = find_op_rw(cmd, &kidx, &subtype); | |||||
| if (rw == NULL) | |||||
| continue; | |||||
| /* Try to find referenced kernel object */ | |||||
| no = rw->find_bykidx(ch, kidx); | |||||
| if (no == NULL) | |||||
| continue; | |||||
| val = strtol(no->name, &end, 10); | |||||
| if (*end == '\0' && val < 65535) { | |||||
| uidx = val; | |||||
| } else { | |||||
| /* | |||||
| * We are called via legacy opcode. | |||||
| * Save error and show table as fake number | |||||
| * not to make ipfw(8) hang. | |||||
| */ | |||||
| uidx = 65535; | |||||
| error = 2; | |||||
| } | |||||
| rw->update(cmd, uidx); | |||||
| } | |||||
| return (error); | |||||
| } | |||||
| /* | |||||
| * Unreferences all already-referenced objects in given @cmd rule, | * Unreferences all already-referenced objects in given @cmd rule, | ||||
| * using information in @oib. | * using information in @oib. | ||||
| * | * | ||||
| * Used to rollback partially converted rule on error. | * Used to rollback partially converted rule on error. | ||||
| */ | */ | ||||
| static void | static void | ||||
| unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib, | unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib, | ||||
| struct obj_idx *end) | struct obj_idx *end) | ||||
| Show All 24 Lines | |||||
| * Used at rule removal code. | * Used at rule removal code. | ||||
| */ | */ | ||||
| static void | static void | ||||
| unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule) | unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw; | struct opcode_obj_rewrite *rw; | ||||
| struct named_object *no; | struct named_object *no; | ||||
| ipfw_insn *cmd; | ipfw_insn *cmd; | ||||
| uint32_t kidx; | |||||
| int cmdlen, l; | int cmdlen, l; | ||||
| uint16_t kidx; | |||||
| uint8_t subtype; | uint8_t subtype; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| l = rule->cmd_len; | l = rule->cmd_len; | ||||
| cmd = rule->cmd; | cmd = rule->cmd; | ||||
| cmdlen = 0; | cmdlen = 0; | ||||
| for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { | ||||
| ▲ Show 20 Lines • Show All 185 Lines • ▼ Show 20 Lines | |||||
| free: | free: | ||||
| if (pidx_first != ci->obuf) | if (pidx_first != ci->obuf) | ||||
| free(pidx_first, M_IPFW); | free(pidx_first, M_IPFW); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| * Adds one or more rules to ipfw @chain. | * Parses one or more rules from userland. | ||||
| * Data layout (version 0)(current): | * Data layout (version 1)(current): | ||||
| * Request: | * Request: | ||||
| * [ | * [ | ||||
| * ip_fw3_opheader | * ip_fw3_opheader | ||||
| * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) | * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) | ||||
| * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3) | * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3) | ||||
| * ] | * ] | ||||
| * Reply: | * Reply: | ||||
| * [ | * [ | ||||
| * ip_fw3_opheader | * ip_fw3_opheader | ||||
| * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) | * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) | ||||
| * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] | * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] | ||||
| * ] | * ] | ||||
| * | * | ||||
| * Rules in reply are modified to store their actual ruleset number. | * Rules in reply are modified to store their actual ruleset number. | ||||
| * | * | ||||
| * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending | * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending | ||||
| * according to their idx field and there has to be no duplicates. | * according to their idx field and there has to be no duplicates. | ||||
| * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. | * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. | ||||
| * (*3) Each ip_fw structure needs to be aligned to u64 boundary. | * (*3) Each ip_fw structure needs to be aligned to u64 boundary. | ||||
| * | * | ||||
| * Returns 0 on success. | * Returns 0 on success. | ||||
| */ | */ | ||||
| static int | static __noinline int | ||||
| add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | parse_rules_v1(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
| struct sockopt_data *sd) | struct sockopt_data *sd, ipfw_obj_ctlv **prtlv, | ||||
| struct rule_check_info **pci) | |||||
| { | { | ||||
| ipfw_obj_ctlv *ctlv, *rtlv, *tstate; | ipfw_obj_ctlv *ctlv, *rtlv, *tstate; | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| int clen, error, idx; | struct rule_check_info *ci, *cbuf; | ||||
| uint32_t count, read; | |||||
| struct ip_fw_rule *r; | struct ip_fw_rule *r; | ||||
| struct rule_check_info rci, *ci, *cbuf; | size_t count, clen, read, rsize; | ||||
| int i, rsize; | uint32_t idx, rulenum; | ||||
| int error; | |||||
| op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize); | op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize); | ||||
| ctlv = (ipfw_obj_ctlv *)(op3 + 1); | ctlv = (ipfw_obj_ctlv *)(op3 + 1); | ||||
| read = sizeof(ip_fw3_opheader); | read = sizeof(ip_fw3_opheader); | ||||
| rtlv = NULL; | |||||
| tstate = NULL; | |||||
| cbuf = NULL; | |||||
| memset(&rci, 0, sizeof(struct rule_check_info)); | |||||
| if (read + sizeof(*ctlv) > sd->valsize) | if (read + sizeof(*ctlv) > sd->valsize) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| rtlv = NULL; | |||||
| tstate = NULL; | |||||
| cbuf = NULL; | |||||
| /* Table names or other named objects. */ | |||||
| if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { | if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { | ||||
| /* Check size and alignment. */ | |||||
| clen = ctlv->head.length; | clen = ctlv->head.length; | ||||
| /* Check size and alignment */ | if (read + clen > sd->valsize || clen < sizeof(*ctlv) || | ||||
| if (clen > sd->valsize || clen < sizeof(*ctlv)) | (clen % sizeof(uint64_t)) != 0) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| if ((clen % sizeof(uint64_t)) != 0) | /* Check for validness. */ | ||||
| return (EINVAL); | |||||
| /* | |||||
| * Some table names or other named objects. | |||||
| * Check for validness. | |||||
| */ | |||||
| count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv); | count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv); | ||||
| if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv)) | if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| /* | /* | ||||
| * Check each TLV. | * Check each TLV. | ||||
| * Ensure TLVs are sorted ascending and | * Ensure TLVs are sorted ascending and | ||||
| * there are no duplicates. | * there are no duplicates. | ||||
| */ | */ | ||||
| idx = -1; | idx = 0; | ||||
| ntlv = (ipfw_obj_ntlv *)(ctlv + 1); | ntlv = (ipfw_obj_ntlv *)(ctlv + 1); | ||||
| while (count > 0) { | while (count > 0) { | ||||
| if (ntlv->head.length != sizeof(ipfw_obj_ntlv)) | if (ntlv->head.length != sizeof(ipfw_obj_ntlv)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| error = ipfw_check_object_name_generic(ntlv->name); | error = ipfw_check_object_name_generic(ntlv->name); | ||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | return (error); | ||||
| if (ntlv->idx <= idx) | if (ntlv->idx <= idx) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| idx = ntlv->idx; | idx = ntlv->idx; | ||||
| count--; | count--; | ||||
| ntlv++; | ntlv++; | ||||
| } | } | ||||
| tstate = ctlv; | tstate = ctlv; | ||||
| read += ctlv->head.length; | read += ctlv->head.length; | ||||
| ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); | ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); | ||||
| } | |||||
| if (read + sizeof(*ctlv) > sd->valsize) | if (read + sizeof(*ctlv) > sd->valsize) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | |||||
| /* List of rules. */ | |||||
| if (ctlv->head.type == IPFW_TLV_RULE_LIST) { | if (ctlv->head.type == IPFW_TLV_RULE_LIST) { | ||||
| clen = ctlv->head.length; | clen = ctlv->head.length; | ||||
| if (clen + read > sd->valsize || clen < sizeof(*ctlv)) | if (read + clen > sd->valsize || clen < sizeof(*ctlv) || | ||||
| (clen % sizeof(uint64_t)) != 0) | |||||
| return (EINVAL); | return (EINVAL); | ||||
| if ((clen % sizeof(uint64_t)) != 0) | |||||
| return (EINVAL); | |||||
| /* | |||||
| * TODO: Permit adding multiple rules at once | |||||
| */ | |||||
| if (ctlv->count != 1) | |||||
| return (ENOTSUP); | |||||
| clen -= sizeof(*ctlv); | clen -= sizeof(*ctlv); | ||||
| if (ctlv->count == 0 || | |||||
| if (ctlv->count > clen / sizeof(struct ip_fw_rule)) | ctlv->count > clen / sizeof(struct ip_fw_rule)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| /* Allocate state for each rule or use stack */ | /* Allocate state for each rule */ | ||||
| if (ctlv->count == 1) { | cbuf = malloc(ctlv->count * sizeof(struct rule_check_info), | ||||
| memset(&rci, 0, sizeof(struct rule_check_info)); | M_TEMP, M_WAITOK | M_ZERO); | ||||
| cbuf = &rci; | |||||
| } else | |||||
| cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP, | |||||
| M_WAITOK | M_ZERO); | |||||
| ci = cbuf; | |||||
| /* | /* | ||||
| * Check each rule for validness. | * Check each rule for validness. | ||||
| * Ensure numbered rules are sorted ascending | * Ensure numbered rules are sorted ascending | ||||
| * and properly aligned | * and properly aligned | ||||
| */ | */ | ||||
| idx = 0; | rulenum = 0; | ||||
| r = (struct ip_fw_rule *)(ctlv + 1); | |||||
| count = 0; | count = 0; | ||||
| error = 0; | error = 0; | ||||
| ci = cbuf; | |||||
| r = (struct ip_fw_rule *)(ctlv + 1); | |||||
| while (clen > 0) { | while (clen > 0) { | ||||
| rsize = roundup2(RULESIZE(r), sizeof(uint64_t)); | rsize = RULEUSIZE1(r); | ||||
| if (rsize > clen || ctlv->count <= count) { | if (rsize > clen || count > ctlv->count) { | ||||
| error = EINVAL; | error = EINVAL; | ||||
| break; | break; | ||||
| } | } | ||||
| ci->ctlv = tstate; | ci->ctlv = tstate; | ||||
| error = check_ipfw_rule1(r, rsize, ci); | ci->version = IP_FW3_OPVER; | ||||
| error = ipfw_check_rule(r, rsize, ci); | |||||
| if (error != 0) | if (error != 0) | ||||
| break; | break; | ||||
| /* Check sorting */ | /* Check sorting */ | ||||
| if (r->rulenum != 0 && r->rulenum < idx) { | if (count != 0 && ((rulenum == 0) != (r->rulenum == 0) || | ||||
| printf("rulenum %d idx %d\n", r->rulenum, idx); | r->rulenum < rulenum)) { | ||||
| printf("ipfw: wrong order: rulenum %u" | |||||
| " vs %u\n", r->rulenum, rulenum); | |||||
| error = EINVAL; | error = EINVAL; | ||||
| break; | break; | ||||
| } | } | ||||
| idx = r->rulenum; | rulenum = r->rulenum; | ||||
| ci->urule = (caddr_t)r; | ci->urule = (caddr_t)r; | ||||
| rsize = roundup2(rsize, sizeof(uint64_t)); | |||||
| clen -= rsize; | clen -= rsize; | ||||
| r = (struct ip_fw_rule *)((caddr_t)r + rsize); | r = (struct ip_fw_rule *)((caddr_t)r + rsize); | ||||
| count++; | count++; | ||||
| ci++; | ci++; | ||||
| } | } | ||||
| if (ctlv->count != count || error != 0) { | if (ctlv->count != count || error != 0) { | ||||
| if (cbuf != &rci) | |||||
| free(cbuf, M_TEMP); | free(cbuf, M_TEMP); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| rtlv = ctlv; | rtlv = ctlv; | ||||
| read += ctlv->head.length; | read += ctlv->head.length; | ||||
| ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); | ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); | ||||
| } | } | ||||
| if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) { | if (read != sd->valsize || rtlv == NULL) { | ||||
| if (cbuf != NULL && cbuf != &rci) | |||||
| free(cbuf, M_TEMP); | free(cbuf, M_TEMP); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| *prtlv = rtlv; | |||||
| *pci = cbuf; | |||||
| return (0); | |||||
| } | |||||
| /* | /* | ||||
| * Passed rules seems to be valid. | * Copy rule @urule from v1 userland format (current) to kernel @krule. | ||||
| * Allocate storage and try to add them to chain. | |||||
| */ | */ | ||||
| for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) { | static void | ||||
| clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule); | import_rule_v1(struct ip_fw_chain *chain, struct rule_check_info *ci) | ||||
| ci->krule = ipfw_alloc_rule(chain, clen); | { | ||||
| import_rule1(ci); | struct ip_fw_rule *urule; | ||||
| } | struct ip_fw *krule; | ||||
| if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) { | urule = (struct ip_fw_rule *)ci->urule; | ||||
| /* Free allocate krules */ | krule = ci->krule = ipfw_alloc_rule(chain, RULEKSIZE1(urule)); | ||||
| for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) | |||||
| ipfw_free_rule(ci->krule); | krule->act_ofs = urule->act_ofs; | ||||
| krule->cmd_len = urule->cmd_len; | |||||
| krule->rulenum = urule->rulenum; | |||||
| krule->set = urule->set; | |||||
| krule->flags = urule->flags; | |||||
| /* Save rulenum offset */ | |||||
| ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); | |||||
| /* Copy opcodes */ | |||||
| memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); | |||||
| } | } | ||||
| if (cbuf != NULL && cbuf != &rci) | /* | ||||
| free(cbuf, M_TEMP); | * Adds one or more rules to ipfw @chain. | ||||
| */ | |||||
| static int | |||||
| add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd) | |||||
| { | |||||
| ipfw_obj_ctlv *rtlv; | |||||
| struct rule_check_info *ci, *nci; | |||||
| int i, ret; | |||||
| return (error); | /* | ||||
| * Check rules buffer for validness. | |||||
| */ | |||||
| ret = parse_rules_v1(chain, op3, sd, &rtlv, &nci); | |||||
| if (ret != 0) | |||||
| return (ret); | |||||
| /* | |||||
| * Allocate storage for the kernel representation of rules. | |||||
| */ | |||||
| for (i = 0, ci = nci; i < rtlv->count; i++, ci++) | |||||
| import_rule_v1(chain, ci); | |||||
| /* | |||||
| * Try to add new rules to the chain. | |||||
| */ | |||||
| if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) { | |||||
| for (i = 0, ci = nci; i < rtlv->count; i++, ci++) | |||||
| ipfw_free_rule(ci->krule); | |||||
| } | } | ||||
| /* Cleanup after parse_rules() */ | |||||
| free(nci, M_TEMP); | |||||
| return (ret); | |||||
| } | |||||
| /* | /* | ||||
| * Lists all sopts currently registered. | * Lists all sopts currently registered. | ||||
| * Data layout (v0)(current): | * Data layout (v1)(current): | ||||
| * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size | * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size | ||||
| * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ] | * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ] | ||||
| * | * | ||||
| * Returns 0 on success | * Returns 0 on success | ||||
| */ | */ | ||||
| static int | static int | ||||
| dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
| struct sockopt_data *sd) | struct sockopt_data *sd) | ||||
| { | { | ||||
| struct _ipfw_obj_lheader *olh; | struct _ipfw_obj_lheader *olh; | ||||
| ipfw_sopt_info *i; | ipfw_sopt_info *i; | ||||
| struct ipfw_sopt_handler *sh; | struct ipfw_sopt_handler *sh; | ||||
| uint32_t count, n, size; | uint32_t count, n, size; | ||||
| olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); | olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd, | ||||
| sizeof(*olh)); | |||||
| if (olh == NULL) | if (olh == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| if (sd->valsize < olh->size) | if (sd->valsize < olh->size) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| CTL3_LOCK(); | CTL3_LOCK(); | ||||
| count = ctl3_hsize; | count = ctl3_hsize; | ||||
| size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader); | size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader); | ||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* | /* | ||||
| * Finds opcode object rewriter based on @code. | * Finds opcode object rewriter based on @code. | ||||
| * | * | ||||
| * Returns pointer to handler or NULL. | * Returns pointer to handler or NULL. | ||||
| */ | */ | ||||
| static struct opcode_obj_rewrite * | static struct opcode_obj_rewrite * | ||||
| find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) | find_op_rw(ipfw_insn *cmd, uint32_t *puidx, uint8_t *ptype) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw, *lo, *hi; | struct opcode_obj_rewrite *rw, *lo, *hi; | ||||
| uint16_t uidx; | uint32_t uidx; | ||||
| uint8_t subtype; | uint8_t subtype; | ||||
| if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0) | if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0) | ||||
| return (NULL); | return (NULL); | ||||
| for (rw = lo; rw <= hi; rw++) { | for (rw = lo; rw <= hi; rw++) { | ||||
| if (rw->classifier(cmd, &uidx, &subtype) == 0) { | if (rw->classifier(cmd, &uidx, &subtype) == 0) { | ||||
| if (puidx != NULL) | if (puidx != NULL) | ||||
| *puidx = uidx; | *puidx = uidx; | ||||
| if (ptype != NULL) | if (ptype != NULL) | ||||
| *ptype = subtype; | *ptype = subtype; | ||||
| return (rw); | return (rw); | ||||
| } | } | ||||
| } | } | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| int | int | ||||
| classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx) | classify_opcode_kidx(ipfw_insn *cmd, uint32_t *puidx) | ||||
| { | { | ||||
| if (find_op_rw(cmd, puidx, NULL) == NULL) | if (find_op_rw(cmd, puidx, NULL) == NULL) | ||||
| return (1); | return (1); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| void | void | ||||
| update_opcode_kidx(ipfw_insn *cmd, uint16_t idx) | update_opcode_kidx(ipfw_insn *cmd, uint32_t idx) | ||||
| { | { | ||||
| struct opcode_obj_rewrite *rw; | struct opcode_obj_rewrite *rw; | ||||
| rw = find_op_rw(cmd, NULL, NULL); | rw = find_op_rw(cmd, NULL, NULL); | ||||
| KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode)); | KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode)); | ||||
| rw->update(cmd, idx); | rw->update(cmd, idx); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_init_obj_rewriter(void) | ipfw_init_obj_rewriter(void) | ||||
| { | { | ||||
| ctl3_rewriters = NULL; | ctl3_rewriters = NULL; | ||||
| ctl3_rsize = 0; | ctl3_rsize = 0; | ||||
| } | } | ||||
| void | void | ||||
| ipfw_destroy_obj_rewriter(void) | ipfw_destroy_obj_rewriter(void) | ||||
| { | { | ||||
| if (ctl3_rewriters != NULL) | if (ctl3_rewriters != NULL) | ||||
| free(ctl3_rewriters, M_IPFW); | free(ctl3_rewriters, M_IPFW); | ||||
| ctl3_rewriters = NULL; | ctl3_rewriters = NULL; | ||||
| ctl3_rsize = 0; | ctl3_rsize = 0; | ||||
| } | } | ||||
| /* | /* | ||||
| * Adds one or more opcode object rewrite handlers to the global array. | * Adds one or more opcode object rewrite handlers to the global array. | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
| hdr->objsize = sizeof(ipfw_obj_ntlv); | hdr->objsize = sizeof(ipfw_obj_ntlv); | ||||
| if (count > 0) | if (count > 0) | ||||
| ipfw_objhash_foreach(CHAIN_TO_SRV(chain), | ipfw_objhash_foreach(CHAIN_TO_SRV(chain), | ||||
| export_objhash_ntlv_internal, sd); | export_objhash_ntlv_internal, sd); | ||||
| IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| void | |||||
| ipfw_enable_skipto_cache(struct ip_fw_chain *chain) | |||||
| { | |||||
| IPFW_UH_WLOCK_ASSERT(chain); | |||||
| update_skipto_cache(chain, chain->map); | |||||
| IPFW_WLOCK(chain); | |||||
| swap_skipto_cache(chain); | |||||
| V_skipto_cache = 1; | |||||
| IPFW_WUNLOCK(chain); | |||||
| } | |||||
| /* | /* | ||||
| * Enables or disable skipto cache. | |||||
| * Request: [ ipfw_cmd_header ] size = ipfw_cmd_header.size | |||||
| * Reply: [ ipfw_cmd_header ] | |||||
| * Returns 0 on success | |||||
| */ | |||||
| static int | |||||
| manage_skiptocache(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | |||||
| struct sockopt_data *sd) | |||||
| { | |||||
| ipfw_cmd_header *hdr; | |||||
| if (sd->valsize != sizeof(*hdr)) | |||||
| return (EINVAL); | |||||
| hdr = (ipfw_cmd_header *)ipfw_get_sopt_space(sd, sd->valsize); | |||||
| if (hdr->cmd != SKIPTO_CACHE_DISABLE && | |||||
| hdr->cmd != SKIPTO_CACHE_ENABLE) | |||||
| return (EOPNOTSUPP); | |||||
| IPFW_UH_WLOCK(chain); | |||||
| if (hdr->cmd != V_skipto_cache) { | |||||
| if (hdr->cmd == SKIPTO_CACHE_ENABLE) | |||||
| ipfw_enable_skipto_cache(chain); | |||||
| V_skipto_cache = hdr->cmd; | |||||
| } | |||||
| IPFW_UH_WUNLOCK(chain); | |||||
| return (0); | |||||
| } | |||||
| /* | |||||
| * Compares two sopt handlers (code, version and handler ptr). | * Compares two sopt handlers (code, version and handler ptr). | ||||
| * Used both as qsort() and bsearch(). | * Used both as qsort() and bsearch(). | ||||
| * Does not compare handler for latter case. | * Does not compare handler for latter case. | ||||
| * | * | ||||
| * Returns 0 if match is found. | * Returns 0 if match is found. | ||||
| */ | */ | ||||
| static int | static int | ||||
| compare_sh(const void *_a, const void *_b) | compare_sh(const void *_a, const void *_b) | ||||
| ▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | find_unref_sh(struct ipfw_sopt_handler *psh) | ||||
| sh->refcnt--; | sh->refcnt--; | ||||
| ctl3_refct--; | ctl3_refct--; | ||||
| CTL3_UNLOCK(); | CTL3_UNLOCK(); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_init_sopt_handler(void) | ipfw_init_sopt_handler(void) | ||||
| { | { | ||||
| CTL3_LOCK_INIT(); | CTL3_LOCK_INIT(); | ||||
| IPFW_ADD_SOPT_HANDLER(1, scodes); | IPFW_ADD_SOPT_HANDLER(1, scodes); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_destroy_sopt_handler(void) | ipfw_destroy_sopt_handler(void) | ||||
| { | { | ||||
| IPFW_DEL_SOPT_HANDLER(1, scodes); | IPFW_DEL_SOPT_HANDLER(1, scodes); | ||||
| CTL3_LOCK_DESTROY(); | CTL3_LOCK_DESTROY(); | ||||
| } | } | ||||
| void | |||||
| ipfw_register_compat(ipfw_check_opcode_t f) | |||||
| { | |||||
| check_opcode_f = f; | |||||
| } | |||||
| void | |||||
| ipfw_unregister_compat(void) | |||||
| { | |||||
| check_opcode_f = check_opcode_compat_nop; | |||||
| } | |||||
| /* | /* | ||||
| * Adds one or more sockopt handlers to the global array. | * Adds one or more sockopt handlers to the global array. | ||||
| * Function may sleep. | * Function may sleep. | ||||
| */ | */ | ||||
| void | void | ||||
| ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count) | ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count) | ||||
| { | { | ||||
| size_t sz; | size_t sz; | ||||
| ▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | ipfw_ctl3(struct sockopt *sopt) | ||||
| struct ipfw_sopt_handler h; | struct ipfw_sopt_handler h; | ||||
| ip_fw3_opheader *op3 = NULL; | ip_fw3_opheader *op3 = NULL; | ||||
| error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); | error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); | ||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | return (error); | ||||
| if (sopt->sopt_name != IP_FW3) | if (sopt->sopt_name != IP_FW3) | ||||
| return (ipfw_ctl(sopt)); | return (EOPNOTSUPP); | ||||
| chain = &V_layer3_chain; | chain = &V_layer3_chain; | ||||
| error = 0; | error = 0; | ||||
| /* Save original valsize before it is altered via sooptcopyin() */ | /* Save original valsize before it is altered via sooptcopyin() */ | ||||
| valsize = sopt->sopt_valsize; | valsize = sopt->sopt_valsize; | ||||
| memset(&sdata, 0, sizeof(sdata)); | memset(&sdata, 0, sizeof(sdata)); | ||||
| /* Read op3 header first to determine actual operation */ | /* Read op3 header first to determine actual operation */ | ||||
| ▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | ipfw_ctl3(struct sockopt *sopt) | ||||
| sopt->sopt_val = sdata.sopt_val; | sopt->sopt_val = sdata.sopt_val; | ||||
| sopt->sopt_valsize = sdata.ktotal; | sopt->sopt_valsize = sdata.ktotal; | ||||
| if (sdata.kbuf != xbuf) | if (sdata.kbuf != xbuf) | ||||
| free(sdata.kbuf, M_TEMP); | free(sdata.kbuf, M_TEMP); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /** | |||||
| * {set|get}sockopt parser. | |||||
| */ | |||||
| int | |||||
| ipfw_ctl(struct sockopt *sopt) | |||||
| { | |||||
| #define RULE_MAXSIZE (512*sizeof(u_int32_t)) | |||||
| int error; | |||||
| size_t size; | |||||
| struct ip_fw *buf; | |||||
| struct ip_fw_rule0 *rule; | |||||
| struct ip_fw_chain *chain; | |||||
| u_int32_t rulenum[2]; | |||||
| uint32_t opt; | |||||
| struct rule_check_info ci; | |||||
| IPFW_RLOCK_TRACKER; | |||||
| chain = &V_layer3_chain; | |||||
| error = 0; | |||||
| opt = sopt->sopt_name; | |||||
| /* | /* | ||||
| * Disallow modifications in really-really secure mode, but still allow | |||||
| * the logging counters to be reset. | |||||
| */ | |||||
| if (opt == IP_FW_ADD || | |||||
| (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) { | |||||
| error = securelevel_ge(sopt->sopt_td->td_ucred, 3); | |||||
| if (error != 0) | |||||
| return (error); | |||||
| } | |||||
| switch (opt) { | |||||
| case IP_FW_GET: | |||||
| /* | |||||
| * pass up a copy of the current rules. Static rules | |||||
| * come first (the last of which has number IPFW_DEFAULT_RULE), | |||||
| * followed by a possibly empty list of dynamic rule. | |||||
| * The last dynamic rule has NULL in the "next" field. | |||||
| * | |||||
| * Note that the calculated size is used to bound the | |||||
| * amount of data returned to the user. The rule set may | |||||
| * change between calculating the size and returning the | |||||
| * data in which case we'll just return what fits. | |||||
| */ | |||||
| for (;;) { | |||||
| int len = 0, want; | |||||
| size = chain->static_len; | |||||
| size += ipfw_dyn_len(); | |||||
| if (size >= sopt->sopt_valsize) | |||||
| break; | |||||
| buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO); | |||||
| IPFW_UH_RLOCK(chain); | |||||
| /* check again how much space we need */ | |||||
| want = chain->static_len + ipfw_dyn_len(); | |||||
| if (size >= want) | |||||
| len = ipfw_getrules(chain, buf, size); | |||||
| IPFW_UH_RUNLOCK(chain); | |||||
| if (size >= want) | |||||
| error = sooptcopyout(sopt, buf, len); | |||||
| free(buf, M_TEMP); | |||||
| if (size >= want) | |||||
| break; | |||||
| } | |||||
| break; | |||||
| case IP_FW_FLUSH: | |||||
| /* locking is done within del_entry() */ | |||||
| error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ | |||||
| break; | |||||
| case IP_FW_ADD: | |||||
| rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK); | |||||
| error = sooptcopyin(sopt, rule, RULE_MAXSIZE, | |||||
| sizeof(struct ip_fw7) ); | |||||
| memset(&ci, 0, sizeof(struct rule_check_info)); | |||||
| /* | |||||
| * If the size of commands equals RULESIZE7 then we assume | |||||
| * a FreeBSD7.2 binary is talking to us (set is7=1). | |||||
| * is7 is persistent so the next 'ipfw list' command | |||||
| * will use this format. | |||||
| * NOTE: If wrong version is guessed (this can happen if | |||||
| * the first ipfw command is 'ipfw [pipe] list') | |||||
| * the ipfw binary may crash or loop infinitly... | |||||
| */ | |||||
| size = sopt->sopt_valsize; | |||||
| if (size == RULESIZE7(rule)) { | |||||
| is7 = 1; | |||||
| error = convert_rule_to_8(rule); | |||||
| if (error) { | |||||
| free(rule, M_TEMP); | |||||
| return error; | |||||
| } | |||||
| size = RULESIZE(rule); | |||||
| } else | |||||
| is7 = 0; | |||||
| if (error == 0) | |||||
| error = check_ipfw_rule0(rule, size, &ci); | |||||
| if (error == 0) { | |||||
| /* locking is done within add_rule() */ | |||||
| struct ip_fw *krule; | |||||
| krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule)); | |||||
| ci.urule = (caddr_t)rule; | |||||
| ci.krule = krule; | |||||
| import_rule0(&ci); | |||||
| error = commit_rules(chain, &ci, 1); | |||||
| if (error != 0) | |||||
| ipfw_free_rule(ci.krule); | |||||
| else if (sopt->sopt_dir == SOPT_GET) { | |||||
| if (is7) { | |||||
| error = convert_rule_to_7(rule); | |||||
| size = RULESIZE7(rule); | |||||
| if (error) { | |||||
| free(rule, M_TEMP); | |||||
| return error; | |||||
| } | |||||
| } | |||||
| error = sooptcopyout(sopt, rule, size); | |||||
| } | |||||
| } | |||||
| free(rule, M_TEMP); | |||||
| break; | |||||
| case IP_FW_DEL: | |||||
| /* | |||||
| * IP_FW_DEL is used for deleting single rules or sets, | |||||
| * and (ab)used to atomically manipulate sets. Argument size | |||||
| * is used to distinguish between the two: | |||||
| * sizeof(u_int32_t) | |||||
| * delete single rule or set of rules, | |||||
| * or reassign rules (or sets) to a different set. | |||||
| * 2*sizeof(u_int32_t) | |||||
| * atomic disable/enable sets. | |||||
| * first u_int32_t contains sets to be disabled, | |||||
| * second u_int32_t contains sets to be enabled. | |||||
| */ | |||||
| error = sooptcopyin(sopt, rulenum, | |||||
| 2*sizeof(u_int32_t), sizeof(u_int32_t)); | |||||
| if (error) | |||||
| break; | |||||
| size = sopt->sopt_valsize; | |||||
| if (size == sizeof(u_int32_t) && rulenum[0] != 0) { | |||||
| /* delete or reassign, locking done in del_entry() */ | |||||
| error = del_entry(chain, rulenum[0]); | |||||
| } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */ | |||||
| IPFW_UH_WLOCK(chain); | |||||
| V_set_disable = | |||||
| (V_set_disable | rulenum[0]) & ~rulenum[1] & | |||||
| ~(1<<RESVD_SET); /* set RESVD_SET always enabled */ | |||||
| IPFW_UH_WUNLOCK(chain); | |||||
| } else | |||||
| error = EINVAL; | |||||
| break; | |||||
| case IP_FW_ZERO: | |||||
| case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */ | |||||
| rulenum[0] = 0; | |||||
| if (sopt->sopt_val != 0) { | |||||
| error = sooptcopyin(sopt, rulenum, | |||||
| sizeof(u_int32_t), sizeof(u_int32_t)); | |||||
| if (error) | |||||
| break; | |||||
| } | |||||
| error = zero_entry(chain, rulenum[0], | |||||
| sopt->sopt_name == IP_FW_RESETLOG); | |||||
| break; | |||||
| /*--- TABLE opcodes ---*/ | |||||
| case IP_FW_TABLE_ADD: | |||||
| case IP_FW_TABLE_DEL: | |||||
| { | |||||
| ipfw_table_entry ent; | |||||
| struct tentry_info tei; | |||||
| struct tid_info ti; | |||||
| struct table_value v; | |||||
| error = sooptcopyin(sopt, &ent, | |||||
| sizeof(ent), sizeof(ent)); | |||||
| if (error) | |||||
| break; | |||||
| memset(&tei, 0, sizeof(tei)); | |||||
| tei.paddr = &ent.addr; | |||||
| tei.subtype = AF_INET; | |||||
| tei.masklen = ent.masklen; | |||||
| ipfw_import_table_value_legacy(ent.value, &v); | |||||
| tei.pvalue = &v; | |||||
| memset(&ti, 0, sizeof(ti)); | |||||
| ti.uidx = ent.tbl; | |||||
| ti.type = IPFW_TABLE_CIDR; | |||||
| error = (opt == IP_FW_TABLE_ADD) ? | |||||
| add_table_entry(chain, &ti, &tei, 0, 1) : | |||||
| del_table_entry(chain, &ti, &tei, 0, 1); | |||||
| } | |||||
| break; | |||||
| case IP_FW_TABLE_FLUSH: | |||||
| { | |||||
| u_int16_t tbl; | |||||
| struct tid_info ti; | |||||
| error = sooptcopyin(sopt, &tbl, | |||||
| sizeof(tbl), sizeof(tbl)); | |||||
| if (error) | |||||
| break; | |||||
| memset(&ti, 0, sizeof(ti)); | |||||
| ti.uidx = tbl; | |||||
| error = flush_table(chain, &ti); | |||||
| } | |||||
| break; | |||||
| case IP_FW_TABLE_GETSIZE: | |||||
| { | |||||
| u_int32_t tbl, cnt; | |||||
| struct tid_info ti; | |||||
| if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl), | |||||
| sizeof(tbl)))) | |||||
| break; | |||||
| memset(&ti, 0, sizeof(ti)); | |||||
| ti.uidx = tbl; | |||||
| IPFW_RLOCK(chain); | |||||
| error = ipfw_count_table(chain, &ti, &cnt); | |||||
| IPFW_RUNLOCK(chain); | |||||
| if (error) | |||||
| break; | |||||
| error = sooptcopyout(sopt, &cnt, sizeof(cnt)); | |||||
| } | |||||
| break; | |||||
| case IP_FW_TABLE_LIST: | |||||
| { | |||||
| ipfw_table *tbl; | |||||
| struct tid_info ti; | |||||
| if (sopt->sopt_valsize < sizeof(*tbl)) { | |||||
| error = EINVAL; | |||||
| break; | |||||
| } | |||||
| size = sopt->sopt_valsize; | |||||
| tbl = malloc(size, M_TEMP, M_WAITOK); | |||||
| error = sooptcopyin(sopt, tbl, size, sizeof(*tbl)); | |||||
| if (error) { | |||||
| free(tbl, M_TEMP); | |||||
| break; | |||||
| } | |||||
| tbl->size = (size - sizeof(*tbl)) / | |||||
| sizeof(ipfw_table_entry); | |||||
| memset(&ti, 0, sizeof(ti)); | |||||
| ti.uidx = tbl->tbl; | |||||
| IPFW_RLOCK(chain); | |||||
| error = ipfw_dump_table_legacy(chain, &ti, tbl); | |||||
| IPFW_RUNLOCK(chain); | |||||
| if (error) { | |||||
| free(tbl, M_TEMP); | |||||
| break; | |||||
| } | |||||
| error = sooptcopyout(sopt, tbl, size); | |||||
| free(tbl, M_TEMP); | |||||
| } | |||||
| break; | |||||
| /*--- NAT operations are protected by the IPFW_LOCK ---*/ | |||||
| case IP_FW_NAT_CFG: | |||||
| if (IPFW_NAT_LOADED) | |||||
| error = ipfw_nat_cfg_ptr(sopt); | |||||
| else { | |||||
| printf("IP_FW_NAT_CFG: %s\n", | |||||
| "ipfw_nat not present, please load it"); | |||||
| error = EINVAL; | |||||
| } | |||||
| break; | |||||
| case IP_FW_NAT_DEL: | |||||
| if (IPFW_NAT_LOADED) | |||||
| error = ipfw_nat_del_ptr(sopt); | |||||
| else { | |||||
| printf("IP_FW_NAT_DEL: %s\n", | |||||
| "ipfw_nat not present, please load it"); | |||||
| error = EINVAL; | |||||
| } | |||||
| break; | |||||
| case IP_FW_NAT_GET_CONFIG: | |||||
| if (IPFW_NAT_LOADED) | |||||
| error = ipfw_nat_get_cfg_ptr(sopt); | |||||
| else { | |||||
| printf("IP_FW_NAT_GET_CFG: %s\n", | |||||
| "ipfw_nat not present, please load it"); | |||||
| error = EINVAL; | |||||
| } | |||||
| break; | |||||
| case IP_FW_NAT_GET_LOG: | |||||
| if (IPFW_NAT_LOADED) | |||||
| error = ipfw_nat_get_log_ptr(sopt); | |||||
| else { | |||||
| printf("IP_FW_NAT_GET_LOG: %s\n", | |||||
| "ipfw_nat not present, please load it"); | |||||
| error = EINVAL; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name); | |||||
| error = EINVAL; | |||||
| } | |||||
| return (error); | |||||
| #undef RULE_MAXSIZE | |||||
| } | |||||
| #define RULE_MAXSIZE (256*sizeof(u_int32_t)) | |||||
| /* Functions to convert rules 7.2 <==> 8.0 */ | |||||
| static int | |||||
| convert_rule_to_7(struct ip_fw_rule0 *rule) | |||||
| { | |||||
| /* Used to modify original rule */ | |||||
| struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; | |||||
| /* copy of original rule, version 8 */ | |||||
| struct ip_fw_rule0 *tmp; | |||||
| /* Used to copy commands */ | |||||
| ipfw_insn *ccmd, *dst; | |||||
| int ll = 0, ccmdlen = 0; | |||||
| tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); | |||||
| if (tmp == NULL) { | |||||
| return 1; //XXX error | |||||
| } | |||||
| bcopy(rule, tmp, RULE_MAXSIZE); | |||||
| /* Copy fields */ | |||||
| //rule7->_pad = tmp->_pad; | |||||
| rule7->set = tmp->set; | |||||
| rule7->rulenum = tmp->rulenum; | |||||
| rule7->cmd_len = tmp->cmd_len; | |||||
| rule7->act_ofs = tmp->act_ofs; | |||||
| rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; | |||||
| rule7->cmd_len = tmp->cmd_len; | |||||
| rule7->pcnt = tmp->pcnt; | |||||
| rule7->bcnt = tmp->bcnt; | |||||
| rule7->timestamp = tmp->timestamp; | |||||
| /* Copy commands */ | |||||
| for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; | |||||
| ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { | |||||
| ccmdlen = F_LEN(ccmd); | |||||
| bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); | |||||
| if (dst->opcode > O_NAT) | |||||
| /* O_REASS doesn't exists in 7.2 version, so | |||||
| * decrement opcode if it is after O_REASS | |||||
| */ | |||||
| dst->opcode--; | |||||
| if (ccmdlen > ll) { | |||||
| printf("ipfw: opcode %d size truncated\n", | |||||
| ccmd->opcode); | |||||
| return EINVAL; | |||||
| } | |||||
| } | |||||
| free(tmp, M_TEMP); | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| convert_rule_to_8(struct ip_fw_rule0 *rule) | |||||
| { | |||||
| /* Used to modify original rule */ | |||||
| struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; | |||||
| /* Used to copy commands */ | |||||
| ipfw_insn *ccmd, *dst; | |||||
| int ll = 0, ccmdlen = 0; | |||||
| /* Copy of original rule */ | |||||
| struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO); | |||||
| if (tmp == NULL) { | |||||
| return 1; //XXX error | |||||
| } | |||||
| bcopy(rule7, tmp, RULE_MAXSIZE); | |||||
| for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ; | |||||
| ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) { | |||||
| ccmdlen = F_LEN(ccmd); | |||||
| bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t)); | |||||
| if (dst->opcode > O_NAT) | |||||
| /* O_REASS doesn't exists in 7.2 version, so | |||||
| * increment opcode if it is after O_REASS | |||||
| */ | |||||
| dst->opcode++; | |||||
| if (ccmdlen > ll) { | |||||
| printf("ipfw: opcode %d size truncated\n", | |||||
| ccmd->opcode); | |||||
| return EINVAL; | |||||
| } | |||||
| } | |||||
| rule->_pad = tmp->_pad; | |||||
| rule->set = tmp->set; | |||||
| rule->rulenum = tmp->rulenum; | |||||
| rule->cmd_len = tmp->cmd_len; | |||||
| rule->act_ofs = tmp->act_ofs; | |||||
| rule->next_rule = (struct ip_fw *)tmp->next_rule; | |||||
| rule->cmd_len = tmp->cmd_len; | |||||
| rule->id = 0; /* XXX see if is ok = 0 */ | |||||
| rule->pcnt = tmp->pcnt; | |||||
| rule->bcnt = tmp->bcnt; | |||||
| rule->timestamp = tmp->timestamp; | |||||
| free (tmp, M_TEMP); | |||||
| return 0; | |||||
| } | |||||
| /* | |||||
| * Named object api | * Named object api | ||||
| * | * | ||||
| */ | */ | ||||
| void | void | ||||
| ipfw_init_srv(struct ip_fw_chain *ch) | ipfw_init_srv(struct ip_fw_chain *ch) | ||||
| { | { | ||||
| ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT, | |||||
| ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT); | DEFAULT_OBJHASH_SIZE); | ||||
| ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT, | ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT, | ||||
| M_IPFW, M_WAITOK | M_ZERO); | M_IPFW, M_WAITOK | M_ZERO); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_destroy_srv(struct ip_fw_chain *ch) | ipfw_destroy_srv(struct ip_fw_chain *ch) | ||||
| { | { | ||||
| free(ch->srvstate, M_IPFW); | free(ch->srvstate, M_IPFW); | ||||
| ipfw_objhash_destroy(ch->srvmap); | ipfw_objhash_destroy(ch->srvmap); | ||||
| } | } | ||||
| /* | /* | ||||
| * Allocate new bitmask which can be used to enlarge/shrink | * Allocate new bitmask which can be used to enlarge/shrink | ||||
| * named instance index. | * named instance index. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks) | ||||
| /* Save old values */ | /* Save old values */ | ||||
| *idx = old_idx; | *idx = old_idx; | ||||
| *blocks = old_blocks; | *blocks = old_blocks; | ||||
| } | } | ||||
| void | void | ||||
| ipfw_objhash_bitmap_free(void *idx, int blocks) | ipfw_objhash_bitmap_free(void *idx, int blocks) | ||||
| { | { | ||||
| free(idx, M_IPFW); | free(idx, M_IPFW); | ||||
| } | } | ||||
| /* | /* | ||||
| * Creates named hash instance. | * Creates named hash instance. | ||||
| * Must be called without holding any locks. | * Must be called without holding any locks. | ||||
| * Return pointer to new instance. | * Return pointer to new instance. | ||||
| */ | */ | ||||
| struct namedobj_instance * | struct namedobj_instance * | ||||
| ipfw_objhash_create(uint32_t items) | ipfw_objhash_create(uint32_t items, size_t hash_size) | ||||
| { | { | ||||
| struct namedobj_instance *ni; | struct namedobj_instance *ni; | ||||
| int i; | int i; | ||||
| size_t size; | size_t size; | ||||
| size = sizeof(struct namedobj_instance) + | size = sizeof(struct namedobj_instance) + | ||||
| sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE + | sizeof(struct namedobjects_head) * hash_size + | ||||
| sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE; | sizeof(struct namedobjects_head) * hash_size; | ||||
| ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO); | ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO); | ||||
| ni->nn_size = NAMEDOBJ_HASH_SIZE; | ni->nn_size = hash_size; | ||||
| ni->nv_size = NAMEDOBJ_HASH_SIZE; | ni->nv_size = hash_size; | ||||
| ni->names = (struct namedobjects_head *)(ni +1); | ni->names = (struct namedobjects_head *)(ni +1); | ||||
| ni->values = &ni->names[ni->nn_size]; | ni->values = &ni->names[ni->nn_size]; | ||||
| for (i = 0; i < ni->nn_size; i++) | for (i = 0; i < ni->nn_size; i++) | ||||
| TAILQ_INIT(&ni->names[i]); | TAILQ_INIT(&ni->names[i]); | ||||
| for (i = 0; i < ni->nv_size; i++) | for (i = 0; i < ni->nv_size; i++) | ||||
| TAILQ_INIT(&ni->values[i]); | TAILQ_INIT(&ni->values[i]); | ||||
| /* Set default hashing/comparison functions */ | /* Set default hashing/comparison functions */ | ||||
| ni->hash_f = objhash_hash_name; | ni->hash_f = objhash_hash_name; | ||||
| ni->cmp_f = objhash_cmp_name; | ni->cmp_f = objhash_cmp_name; | ||||
| /* Allocate bitmask separately due to possible resize */ | /* Allocate bitmask separately due to possible resize */ | ||||
| ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks); | ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks); | ||||
| return (ni); | return (ni); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_objhash_destroy(struct namedobj_instance *ni) | ipfw_objhash_destroy(struct namedobj_instance *ni) | ||||
| { | { | ||||
| free(ni->idx_mask, M_IPFW); | free(ni->idx_mask, M_IPFW); | ||||
| free(ni, M_IPFW); | free(ni, M_IPFW); | ||||
| } | } | ||||
| void | void | ||||
| ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f, | ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f, | ||||
| objhash_cmp_f *cmp_f) | objhash_cmp_f *cmp_f) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
| /* | /* | ||||
| * Find named object by @uid. | * Find named object by @uid. | ||||
| * Check @tlvs for valid data inside. | * Check @tlvs for valid data inside. | ||||
| * | * | ||||
| * Returns pointer to found TLV or NULL. | * Returns pointer to found TLV or NULL. | ||||
| */ | */ | ||||
| ipfw_obj_ntlv * | ipfw_obj_ntlv * | ||||
| ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv) | ipfw_find_name_tlv_type(void *tlvs, int len, uint32_t uidx, uint32_t etlv) | ||||
| { | { | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| uintptr_t pa, pe; | uintptr_t pa, pe; | ||||
| int l; | int l; | ||||
| pa = (uintptr_t)tlvs; | pa = (uintptr_t)tlvs; | ||||
| pe = pa + len; | pe = pa + len; | ||||
| l = 0; | l = 0; | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | if (ni->cmp_f(no, name, set) == 0 && | ||||
| no->etlv == (uint16_t)type) | no->etlv == (uint16_t)type) | ||||
| return (no); | return (no); | ||||
| } | } | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| struct named_object * | struct named_object * | ||||
| ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx) | ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint32_t kidx) | ||||
| { | { | ||||
| struct named_object *no; | struct named_object *no; | ||||
| uint32_t hash; | uint32_t hash; | ||||
| hash = objhash_hash_idx(ni, kidx); | hash = objhash_hash_idx(ni, kidx); | ||||
| TAILQ_FOREACH(no, &ni->values[hash], nv_next) { | TAILQ_FOREACH(no, &ni->values[hash], nv_next) { | ||||
| if (no->kidx == kidx) | if (no->kidx == kidx) | ||||
| ▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f, | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * Removes index from given set. | * Removes index from given set. | ||||
| * Returns 0 on success. | * Returns 0 on success. | ||||
| */ | */ | ||||
| int | int | ||||
| ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx) | ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx) | ||||
| { | { | ||||
| u_long *mask; | u_long *mask; | ||||
| int i, v; | int i, v; | ||||
| i = idx / BLOCK_ITEMS; | i = idx / BLOCK_ITEMS; | ||||
| v = idx % BLOCK_ITEMS; | v = idx % BLOCK_ITEMS; | ||||
| if (i >= ni->max_blocks) | if (i >= ni->max_blocks) | ||||
| Show All 14 Lines | ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx) | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | /* | ||||
| * Allocate new index in given instance and stores in in @pidx. | * Allocate new index in given instance and stores in in @pidx. | ||||
| * Returns 0 on success. | * Returns 0 on success. | ||||
| */ | */ | ||||
| int | int | ||||
| ipfw_objhash_alloc_idx(void *n, uint16_t *pidx) | ipfw_objhash_alloc_idx(void *n, uint32_t *pidx) | ||||
| { | { | ||||
| struct namedobj_instance *ni; | struct namedobj_instance *ni; | ||||
| u_long *mask; | u_long *mask; | ||||
| int i, off, v; | int i, off, v; | ||||
| ni = (struct namedobj_instance *)n; | ni = (struct namedobj_instance *)n; | ||||
| off = ni->free_off[0]; | off = ni->free_off[0]; | ||||
| Show All 21 Lines | |||||