Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw_eaction.c
| Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
| * | * | ||||
| * The module that implements external action should register its | * The module that implements external action should register its | ||||
| * handler and name with ipfw_add_eaction() function. | * handler and name with ipfw_add_eaction() function. | ||||
| * This function will return eaction_id, that can be used by module. | * This function will return eaction_id, that can be used by module. | ||||
| * | * | ||||
| * It is possible to pass some additional information to external | * It is possible to pass some additional information to external | ||||
| * action handler using O_EXTERNAL_INSTANCE and O_EXTERNAL_DATA opcodes. | * action handler using O_EXTERNAL_INSTANCE and O_EXTERNAL_DATA opcodes. | ||||
| * Such opcodes should be next after the O_EXTERNAL_ACTION opcode. | * Such opcodes should be next after the O_EXTERNAL_ACTION opcode. | ||||
| * For the O_EXTERNAL_INSTANCE opcode the cmd->arg1 contains index of named | * For the O_EXTERNAL_INSTANCE opcode the cmd->kidx contains index of named | ||||
| * object related to an instance of external action. | * object related to an instance of external action. | ||||
| * For the O_EXTERNAL_DATA opcode the cmd contains the data that can be used | * For the O_EXTERNAL_DATA opcode the cmd contains the data that can be used | ||||
| * by external action handler without needing to create named instance. | * by external action handler without needing to create named instance. | ||||
| * | * | ||||
| * In case when eaction module uses named instances, it should register | * In case when eaction module uses named instances, it should register | ||||
| * opcode rewriting routines for O_EXTERNAL_INSTANCE opcode. The | * opcode rewriting routines for O_EXTERNAL_INSTANCE opcode. The | ||||
| * classifier callback can look back into O_EXTERNAL_ACTION opcode (it | * classifier callback can look back into O_EXTERNAL_ACTION opcode (it | ||||
| * must be in the (ipfw_insn *)(cmd - 1)). By arg1 from O_EXTERNAL_ACTION | * must be in the (ipfw_insn *)(cmd - 2)). By kidx from O_EXTERNAL_ACTION | ||||
| * it can deteremine eaction_id and compare it with its own. | * it can deteremine eaction_id and compare it with its own. | ||||
| * The macro IPFW_TLV_EACTION_NAME(eaction_id) can be used to deteremine | * The macro IPFW_TLV_EACTION_NAME(eaction_id) can be used to deteremine | ||||
| * the type of named_object related to external action instance. | * the type of named_object related to external action instance. | ||||
| * | * | ||||
| * On module unload handler should be deregistered with ipfw_del_eaction() | * On module unload handler should be deregistered with ipfw_del_eaction() | ||||
| * function using known eaction_id. | * function using known eaction_id. | ||||
| */ | */ | ||||
| struct eaction_obj { | struct eaction_obj { | ||||
| struct named_object no; | struct named_object no; | ||||
| ipfw_eaction_t *handler; | ipfw_eaction_t *handler; | ||||
| char name[64]; | char name[64]; | ||||
| }; | }; | ||||
| #define EACTION_OBJ(ch, cmd) \ | #define EACTION_OBJ(ch, cmd) \ | ||||
| ((struct eaction_obj *)SRV_OBJECT((ch), (cmd)->arg1)) | ((struct eaction_obj *)SRV_OBJECT((ch), insntod((cmd), kidx)->kidx)) | ||||
| #if 0 | #if 0 | ||||
| #define EACTION_DEBUG(fmt, ...) do { \ | #define EACTION_DEBUG(fmt, ...) do { \ | ||||
| printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ | printf("%s: " fmt "\n", __func__, ## __VA_ARGS__); \ | ||||
| } while (0) | } while (0) | ||||
| #else | #else | ||||
| #define EACTION_DEBUG(fmt, ...) | #define EACTION_DEBUG(fmt, ...) | ||||
| #endif | #endif | ||||
| const char *default_eaction_typename = "drop"; | const char *default_eaction_typename = "drop"; | ||||
| static int | static int | ||||
| default_eaction(struct ip_fw_chain *ch, struct ip_fw_args *args, | default_eaction(struct ip_fw_chain *ch, struct ip_fw_args *args, | ||||
| ipfw_insn *cmd, int *done) | ipfw_insn *cmd, int *done) | ||||
| { | { | ||||
| *done = 1; /* terminate the search */ | *done = 1; /* terminate the search */ | ||||
| return (IP_FW_DENY); | return (IP_FW_DENY); | ||||
| } | } | ||||
| /* | /* | ||||
| * Opcode rewriting callbacks. | * Opcode rewriting callbacks. | ||||
| */ | */ | ||||
| static int | static int | ||||
| eaction_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) | eaction_classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) | ||||
| { | { | ||||
| ipfw_insn_kidx *cmd; | |||||
| EACTION_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); | if (F_LEN(cmd0) <= 1) | ||||
| *puidx = cmd->arg1; | return (EINVAL); | ||||
| cmd = insntod(cmd0, kidx); | |||||
| EACTION_DEBUG("opcode %u, kidx %u", cmd0->opcode, cmd->kidx); | |||||
| *puidx = cmd->kidx; | |||||
| *ptype = 0; | *ptype = 0; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| eaction_update(ipfw_insn *cmd, uint16_t idx) | eaction_update(ipfw_insn *cmd0, uint32_t idx) | ||||
| { | { | ||||
| ipfw_insn_kidx *cmd; | |||||
| cmd->arg1 = idx; | cmd = insntod(cmd0, kidx); | ||||
| EACTION_DEBUG("opcode %d, arg1 -> %d", cmd->opcode, cmd->arg1); | cmd->kidx = idx; | ||||
| EACTION_DEBUG("opcode %u, kidx -> %u", cmd0->opcode, cmd->kidx); | |||||
| } | } | ||||
| static int | static int | ||||
| eaction_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | eaction_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | ||||
| struct named_object **pno) | struct named_object **pno) | ||||
| { | { | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| Show All 15 Lines | eaction_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | ||||
| *pno = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch), | *pno = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch), | ||||
| 0, IPFW_TLV_EACTION, ntlv->name); | 0, IPFW_TLV_EACTION, ntlv->name); | ||||
| if (*pno == NULL) | if (*pno == NULL) | ||||
| return (ESRCH); | return (ESRCH); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static struct named_object * | static struct named_object * | ||||
| eaction_findbykidx(struct ip_fw_chain *ch, uint16_t idx) | eaction_findbykidx(struct ip_fw_chain *ch, uint32_t idx) | ||||
| { | { | ||||
| EACTION_DEBUG("kidx %u", idx); | EACTION_DEBUG("kidx %u", idx); | ||||
| return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); | return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); | ||||
| } | } | ||||
| static struct opcode_obj_rewrite eaction_opcodes[] = { | static struct opcode_obj_rewrite eaction_opcodes[] = { | ||||
| { | { | ||||
| .opcode = O_EXTERNAL_ACTION, | .opcode = O_EXTERNAL_ACTION, | ||||
| .etlv = IPFW_TLV_EACTION, | .etlv = IPFW_TLV_EACTION, | ||||
| .classifier = eaction_classify, | .classifier = eaction_classify, | ||||
| .update = eaction_update, | .update = eaction_update, | ||||
| .find_byname = eaction_findbyname, | .find_byname = eaction_findbyname, | ||||
| .find_bykidx = eaction_findbykidx, | .find_bykidx = eaction_findbykidx, | ||||
| }, | }, | ||||
| }; | }; | ||||
| static int | static int | ||||
| create_eaction_obj(struct ip_fw_chain *ch, ipfw_eaction_t handler, | create_eaction_obj(struct ip_fw_chain *ch, ipfw_eaction_t handler, | ||||
| const char *name, uint16_t *eaction_id) | const char *name, uint32_t *eaction_id) | ||||
| { | { | ||||
| struct namedobj_instance *ni; | struct namedobj_instance *ni; | ||||
| struct eaction_obj *obj; | struct eaction_obj *obj; | ||||
| IPFW_UH_UNLOCK_ASSERT(ch); | IPFW_UH_UNLOCK_ASSERT(ch); | ||||
| ni = CHAIN_TO_SRV(ch); | ni = CHAIN_TO_SRV(ch); | ||||
| obj = malloc(sizeof(*obj), M_IPFW, M_WAITOK | M_ZERO); | obj = malloc(sizeof(*obj), M_IPFW, M_WAITOK | M_ZERO); | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | destroy_eaction_obj(struct ip_fw_chain *ch, struct named_object *no) | ||||
| ipfw_objhash_free_idx(ni, no->kidx); | ipfw_objhash_free_idx(ni, no->kidx); | ||||
| free(obj, M_IPFW); | free(obj, M_IPFW); | ||||
| } | } | ||||
| /* | /* | ||||
| * Resets all eaction opcodes to default handlers. | * Resets all eaction opcodes to default handlers. | ||||
| */ | */ | ||||
| static void | static void | ||||
| reset_eaction_rules(struct ip_fw_chain *ch, uint16_t eaction_id, | reset_eaction_rules(struct ip_fw_chain *ch, uint32_t eaction_id, | ||||
| uint16_t instance_id, bool reset_rules) | uint32_t instance_id, bool reset_rules) | ||||
| { | { | ||||
| struct named_object *no; | struct named_object *no; | ||||
| int i; | int i; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| no = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch), 0, | no = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch), 0, | ||||
| IPFW_TLV_EACTION, default_eaction_typename); | IPFW_TLV_EACTION, default_eaction_typename); | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | ipfw_eaction_uninit(struct ip_fw_chain *ch, int last) | ||||
| IPFW_DEL_OBJ_REWRITER(last, eaction_opcodes); | IPFW_DEL_OBJ_REWRITER(last, eaction_opcodes); | ||||
| EACTION_DEBUG("External actions support uninitialized"); | EACTION_DEBUG("External actions support uninitialized"); | ||||
| } | } | ||||
| /* | /* | ||||
| * Registers external action handler to the global array. | * Registers external action handler to the global array. | ||||
| * On success it returns eaction id, otherwise - zero. | * On success it returns eaction id, otherwise - zero. | ||||
| */ | */ | ||||
| uint16_t | uint32_t | ||||
| ipfw_add_eaction(struct ip_fw_chain *ch, ipfw_eaction_t handler, | ipfw_add_eaction(struct ip_fw_chain *ch, ipfw_eaction_t handler, | ||||
| const char *name) | const char *name) | ||||
| { | { | ||||
| uint16_t eaction_id; | uint32_t eaction_id; | ||||
| eaction_id = 0; | eaction_id = 0; | ||||
| if (ipfw_check_object_name_generic(name) == 0) { | if (ipfw_check_object_name_generic(name) == 0) { | ||||
| create_eaction_obj(ch, handler, name, &eaction_id); | create_eaction_obj(ch, handler, name, &eaction_id); | ||||
| EACTION_DEBUG("Registered external action '%s' with id %u", | EACTION_DEBUG("Registered external action '%s' with id %u", | ||||
| name, eaction_id); | name, eaction_id); | ||||
| } | } | ||||
| return (eaction_id); | return (eaction_id); | ||||
| } | } | ||||
| /* | /* | ||||
| * Deregisters external action handler with id eaction_id. | * Deregisters external action handler with id eaction_id. | ||||
| */ | */ | ||||
| int | int | ||||
| ipfw_del_eaction(struct ip_fw_chain *ch, uint16_t eaction_id) | ipfw_del_eaction(struct ip_fw_chain *ch, uint32_t eaction_id) | ||||
| { | { | ||||
| struct named_object *no; | struct named_object *no; | ||||
| IPFW_UH_WLOCK(ch); | IPFW_UH_WLOCK(ch); | ||||
| no = ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), eaction_id); | no = ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), eaction_id); | ||||
| if (no == NULL || no->etlv != IPFW_TLV_EACTION) { | if (no == NULL || no->etlv != IPFW_TLV_EACTION) { | ||||
| IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
| return (EINVAL); | return (EINVAL); | ||||
| } | } | ||||
| reset_eaction_rules(ch, eaction_id, 0, (no->refcnt > 1)); | reset_eaction_rules(ch, eaction_id, 0, (no->refcnt > 1)); | ||||
| EACTION_DEBUG("External action '%s' with id %u unregistered", | EACTION_DEBUG("External action '%s' with id %u unregistered", | ||||
| no->name, eaction_id); | no->name, eaction_id); | ||||
| destroy_eaction_obj(ch, no); | destroy_eaction_obj(ch, no); | ||||
| IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ipfw_reset_eaction(struct ip_fw_chain *ch, struct ip_fw *rule, | ipfw_reset_eaction(struct ip_fw_chain *ch, struct ip_fw *rule, | ||||
| uint16_t eaction_id, uint16_t default_id, uint16_t instance_id) | uint32_t eaction_id, uint32_t default_id, uint32_t instance_id) | ||||
| { | { | ||||
| ipfw_insn *cmd, *icmd; | ipfw_insn *cmd, *icmd; | ||||
| int l; | int l; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| IPFW_WLOCK_ASSERT(ch); | IPFW_WLOCK_ASSERT(ch); | ||||
| /* | /* | ||||
| * Return if there is not O_EXTERNAL_ACTION or its id is | * Return if there is not O_EXTERNAL_ACTION or its id is | ||||
| * different. | * different. | ||||
| */ | */ | ||||
| cmd = ipfw_get_action(rule); | cmd = ipfw_get_action(rule); | ||||
| if (cmd->opcode != O_EXTERNAL_ACTION || | if (cmd->opcode != O_EXTERNAL_ACTION || | ||||
| cmd->arg1 != eaction_id) | insntod(cmd, kidx)->kidx != eaction_id) | ||||
| return (0); | return (0); | ||||
| /* | /* | ||||
| * Check if there is O_EXTERNAL_INSTANCE opcode, we need | * Check if there is O_EXTERNAL_INSTANCE opcode, we need | ||||
| * to truncate the rule length. | * to truncate the rule length. | ||||
| * | * | ||||
| * NOTE: F_LEN(cmd) must be 1 for O_EXTERNAL_ACTION opcode, | * NOTE: F_LEN(cmd) must be 2 for O_EXTERNAL_ACTION opcode, | ||||
| * and rule length should be enough to keep O_EXTERNAL_INSTANCE | * and rule length should be enough to keep O_EXTERNAL_INSTANCE | ||||
| * opcode, thus we do check for l > 1. | * opcode, thus we do check for l > 2. | ||||
| */ | */ | ||||
| l = rule->cmd + rule->cmd_len - cmd; | l = rule->cmd + rule->cmd_len - cmd; | ||||
| if (l > 1) { | if (l > 2) { | ||||
| MPASS(F_LEN(cmd) == 1); | MPASS(F_LEN(cmd) == 2); | ||||
| icmd = cmd + 1; | icmd = cmd + F_LEN(cmd); | ||||
| if (icmd->opcode == O_EXTERNAL_INSTANCE && | if (icmd->opcode == O_EXTERNAL_INSTANCE && | ||||
| instance_id != 0 && icmd->arg1 != instance_id) | instance_id != 0 && | ||||
| insntod(icmd, kidx)->kidx != instance_id) | |||||
| return (0); | return (0); | ||||
| /* | /* | ||||
| * Since named_object related to this instance will be | * Since named_object related to this instance will be | ||||
| * destroyed, truncate the chain of opcodes to remove | * destroyed, truncate the chain of opcodes to remove | ||||
| * the rest of cmd chain just after O_EXTERNAL_ACTION | * the rest of cmd chain just after O_EXTERNAL_ACTION | ||||
| * opcode. | * opcode. | ||||
| */ | */ | ||||
| EACTION_DEBUG("truncate rule %d: len %u -> %u", | EACTION_DEBUG("truncate rule %u: len %u -> %u", | ||||
| rule->rulenum, rule->cmd_len, | rule->rulenum, rule->cmd_len, | ||||
| rule->cmd_len - F_LEN(icmd)); | rule->cmd_len - F_LEN(icmd)); | ||||
| rule->cmd_len -= F_LEN(icmd); | rule->cmd_len -= F_LEN(icmd); | ||||
| MPASS(((uint32_t *)icmd - | MPASS(((uint32_t *)icmd - | ||||
| (uint32_t *)rule->cmd) == rule->cmd_len); | (uint32_t *)rule->cmd) == rule->cmd_len); | ||||
| } | } | ||||
| cmd->arg1 = default_id; /* Set to default id */ | insntod(cmd, kidx)->kidx = default_id; /* Set to default id */ | ||||
| /* | /* | ||||
| * Return 1 when reset successfully happened. | * Return 1 when reset successfully happened. | ||||
| */ | */ | ||||
| return (1); | return (1); | ||||
| } | } | ||||
| /* | /* | ||||
| * This function should be called before external action instance is | * This function should be called before external action instance is | ||||
| * destroyed. It will reset eaction_id to default_id for rules, where | * destroyed. It will reset eaction_id to default_id for rules, where | ||||
| * eaction has instance with id == kidx. | * eaction has instance with id == kidx. | ||||
| */ | */ | ||||
| int | int | ||||
| ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint16_t eaction_id, | ipfw_reset_eaction_instance(struct ip_fw_chain *ch, uint32_t eaction_id, | ||||
| uint16_t kidx) | uint32_t kidx) | ||||
| { | { | ||||
| struct named_object *no; | struct named_object *no; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| no = ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), eaction_id); | no = ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), eaction_id); | ||||
| if (no == NULL || no->etlv != IPFW_TLV_EACTION) | if (no == NULL || no->etlv != IPFW_TLV_EACTION) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| reset_eaction_rules(ch, eaction_id, kidx, 0); | reset_eaction_rules(ch, eaction_id, kidx, 0); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| int | int | ||||
| ipfw_run_eaction(struct ip_fw_chain *ch, struct ip_fw_args *args, | ipfw_run_eaction(struct ip_fw_chain *ch, struct ip_fw_args *args, | ||||
| ipfw_insn *cmd, int *done) | ipfw_insn *cmd, int *done) | ||||
| { | { | ||||
| MPASS(F_LEN(cmd) == 2); | |||||
| return (EACTION_OBJ(ch, cmd)->handler(ch, args, cmd, done)); | return (EACTION_OBJ(ch, cmd)->handler(ch, args, cmd, done)); | ||||
| } | } | ||||