Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw_log.c
| Show All 40 Lines | |||||
| #include <sys/systm.h> | #include <sys/systm.h> | ||||
| #include <sys/kernel.h> | #include <sys/kernel.h> | ||||
| #include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||
| #include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
| #include <sys/syslog.h> | #include <sys/syslog.h> | ||||
| #include <net/ethernet.h> /* for ETHERTYPE_IP */ | #include <net/ethernet.h> /* for ETHERTYPE_IP */ | ||||
| #include <net/if.h> | #include <net/if.h> | ||||
| #include <net/if_dl.h> | |||||
| #include <net/if_var.h> | #include <net/if_var.h> | ||||
| #include <net/if_private.h> | #include <net/if_private.h> | ||||
| #include <net/vnet.h> | #include <net/vnet.h> | ||||
| #include <net/route.h> | |||||
| #include <net/route/route_var.h> | |||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||
| #include <netinet/ip.h> | #include <netinet/ip.h> | ||||
| #include <netinet/ip_icmp.h> | #include <netinet/ip_icmp.h> | ||||
| #include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
| #include <netinet/ip_fw.h> | #include <netinet/ip_fw.h> | ||||
| #include <netinet/udp.h> | #include <netinet/udp.h> | ||||
| #include <netinet/tcp.h> | #include <netinet/tcp.h> | ||||
| Show All 27 Lines | |||||
| #define SNPARGS(buf, len) buf + len | #define SNPARGS(buf, len) buf + len | ||||
| #define SNP(buf) buf | #define SNP(buf) buf | ||||
| #else /* !__APPLE__ */ | #else /* !__APPLE__ */ | ||||
| #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 | #define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0 | ||||
| #define SNP(buf) buf, sizeof(buf) | #define SNP(buf) buf, sizeof(buf) | ||||
| #endif /* !__APPLE__ */ | #endif /* !__APPLE__ */ | ||||
| #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) | #define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f) | ||||
| /* | |||||
| * We enter here when we have a rule with O_LOG. | |||||
| * XXX this function alone takes about 2Kbytes of code! | |||||
| */ | |||||
| void | |||||
| ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, | |||||
| struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip) | |||||
| { | |||||
| char *action; | |||||
| int limit_reached = 0; | |||||
| char action2[92], proto[128], fragment[32], mark_str[24]; | |||||
| if (V_fw_verbose == 0) { | static void | ||||
| ipfw_log_ipfw0(struct ip_fw_args *args, struct ip *ip) | |||||
| { | |||||
| if (args->flags & IPFW_ARGS_LENMASK) | if (args->flags & IPFW_ARGS_LENMASK) | ||||
| ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); | ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); | ||||
| else if (args->flags & IPFW_ARGS_ETHER) | else if (args->flags & IPFW_ARGS_ETHER) | ||||
| /* layer2, use orig hdr */ | /* layer2, use orig hdr */ | ||||
| ipfw_bpf_mtap(args->m); | ipfw_bpf_mtap(args->m); | ||||
| else { | else { | ||||
| /* Add fake header. Later we will store | /* Add fake header. Later we will store | ||||
| * more info in the header. | * more info in the header. | ||||
| */ | */ | ||||
| if (ip->ip_v == 4) | if (ip->ip_v == 4) | ||||
| ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", | ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", | ||||
| ETHER_HDR_LEN, args->m); | ETHER_HDR_LEN, args->m); | ||||
| else if (ip->ip_v == 6) | else if (ip->ip_v == 6) | ||||
| ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", | ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", | ||||
| ETHER_HDR_LEN, args->m); | ETHER_HDR_LEN, args->m); | ||||
| else | else | ||||
| /* Obviously bogus EtherType. */ | /* Obviously bogus EtherType. */ | ||||
| ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", | ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", | ||||
| ETHER_HDR_LEN, args->m); | ETHER_HDR_LEN, args->m); | ||||
| } | } | ||||
| return; | |||||
| } | } | ||||
| /* the old 'log' function */ | |||||
| /* | |||||
| * XXX this function alone takes about 2Kbytes of code! | |||||
| */ | |||||
| static void | |||||
| ipfw_log_syslog(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, | |||||
| struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip) | |||||
| { | |||||
| char *action; | |||||
| int limit_reached = 0; | |||||
| char action2[92], proto[128], fragment[32], mark_str[24]; | |||||
| fragment[0] = '\0'; | fragment[0] = '\0'; | ||||
| proto[0] = '\0'; | proto[0] = '\0'; | ||||
| if (f == NULL) { /* bogus pkt */ | if (f == NULL) { /* bogus pkt */ | ||||
| if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit) | if (V_verbose_limit != 0 && V_norule_counter >= V_verbose_limit) | ||||
| return; | return; | ||||
| V_norule_counter++; | V_norule_counter++; | ||||
| if (V_norule_counter == V_verbose_limit) | if (V_norule_counter == V_verbose_limit) | ||||
| ▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | case O_SETDSCP: | ||||
| TARG(cmd->arg1, dscp) & 0x3F); | TARG(cmd->arg1, dscp) & 0x3F); | ||||
| break; | break; | ||||
| case O_SETFIB: | case O_SETFIB: | ||||
| snprintf(SNPARGS(action2, 0), "SetFib %d", | snprintf(SNPARGS(action2, 0), "SetFib %d", | ||||
| TARG(cmd->arg1, fib) & 0x7FFF); | TARG(cmd->arg1, fib) & 0x7FFF); | ||||
| break; | break; | ||||
| case O_SKIPTO: | case O_SKIPTO: | ||||
| snprintf(SNPARGS(action2, 0), "SkipTo %d", | snprintf(SNPARGS(action2, 0), "SkipTo %d", | ||||
| TARG(cmd->arg1, skipto)); | TARG(insntod(cmd, u32)->d[0], skipto)); | ||||
| break; | break; | ||||
| case O_PIPE: | case O_PIPE: | ||||
| snprintf(SNPARGS(action2, 0), "Pipe %d", | snprintf(SNPARGS(action2, 0), "Pipe %d", | ||||
| TARG(cmd->arg1, pipe)); | TARG(cmd->arg1, pipe)); | ||||
| break; | break; | ||||
| case O_QUEUE: | case O_QUEUE: | ||||
| snprintf(SNPARGS(action2, 0), "Queue %d", | snprintf(SNPARGS(action2, 0), "Queue %d", | ||||
| TARG(cmd->arg1, pipe)); | TARG(cmd->arg1, pipe)); | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | #endif | ||||
| case O_NAT: | case O_NAT: | ||||
| action = "Nat"; | action = "Nat"; | ||||
| break; | break; | ||||
| case O_REASS: | case O_REASS: | ||||
| action = "Reass"; | action = "Reass"; | ||||
| break; | break; | ||||
| case O_CALLRETURN: | case O_CALLRETURN: | ||||
| if (cmd->len & F_NOT) | if (cmd->len & F_NOT) | ||||
| action = "Return"; | snprintf(SNPARGS(action2, 0), "Return %s", | ||||
| cmd->arg1 == RETURN_NEXT_RULENUM ? | |||||
| "next-rulenum": "next-rule"); | |||||
| else | else | ||||
| snprintf(SNPARGS(action2, 0), "Call %d", | snprintf(SNPARGS(action2, 0), "Call %d", | ||||
| cmd->arg1); | TARG(insntod(cmd, u32)->d[0], skipto)); | ||||
| break; | break; | ||||
| case O_SETMARK: | case O_SETMARK: | ||||
| if (cmd->arg1 == IP_FW_TARG) | if (cmd->arg1 == IP_FW_TARG) | ||||
| snprintf(SNPARGS(action2, 0), "SetMark %#x", | snprintf(SNPARGS(action2, 0), "SetMark %#010x", | ||||
| TARG(cmd->arg1, mark)); | TARG(cmd->arg1, mark)); | ||||
| else | else | ||||
| snprintf(SNPARGS(action2, 0), "SetMark %#x", | snprintf(SNPARGS(action2, 0), "SetMark %#010x", | ||||
| ((ipfw_insn_u32 *)cmd)->d[0]); | insntoc(cmd, u32)->d[0]); | ||||
| break; | break; | ||||
| case O_EXTERNAL_ACTION: | case O_EXTERNAL_ACTION: | ||||
| snprintf(SNPARGS(action2, 0), "Eaction %s", | snprintf(SNPARGS(action2, 0), "Eaction %s", | ||||
| ((struct named_object *)SRV_OBJECT(chain, | ((struct named_object *)SRV_OBJECT(chain, | ||||
| cmd->arg1))->name); | insntod(cmd, kidx)->kidx))->name); | ||||
| break; | break; | ||||
| default: | default: | ||||
| action = "UNKNOWN"; | action = "UNKNOWN"; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (hlen == 0) { /* non-ip */ | if (hlen == 0) { /* non-ip */ | ||||
| ▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | |||||
| #else | #else | ||||
| log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s%s [no if info]%s\n", | log(LOG_SECURITY | LOG_INFO, "ipfw: %d %s %s%s [no if info]%s\n", | ||||
| f ? f->rulenum : -1, action, proto, mark_str, fragment); | f ? f->rulenum : -1, action, proto, mark_str, fragment); | ||||
| #endif | #endif | ||||
| if (limit_reached) | if (limit_reached) | ||||
| log(LOG_SECURITY | LOG_NOTICE, | log(LOG_SECURITY | LOG_NOTICE, | ||||
| "ipfw: limit %d reached on entry %d\n", | "ipfw: limit %d reached on entry %d\n", | ||||
| limit_reached, f ? f->rulenum : -1); | limit_reached, f ? f->rulenum : -1); | ||||
| } | |||||
| static void | |||||
| ipfw_rtsocklog_fill_l3(struct ip_fw_args *args, | |||||
| char **buf, struct sockaddr **src, struct sockaddr **dst) | |||||
| { | |||||
| struct sockaddr_in *v4src, *v4dst; | |||||
| #ifdef INET6 | |||||
| struct sockaddr_in6 *v6src, *v6dst; | |||||
| if (IS_IP6_FLOW_ID(&(args->f_id))) { | |||||
| v6src = (struct sockaddr_in6 *)*buf; | |||||
| *buf += sizeof(*v6src); | |||||
| v6dst = (struct sockaddr_in6 *)*buf; | |||||
| *buf += sizeof(*v6dst); | |||||
| v6src->sin6_len = v6dst->sin6_len = sizeof(*v6src); | |||||
| v6src->sin6_family = v6dst->sin6_family = AF_INET6; | |||||
| v6src->sin6_addr = args->f_id.src_ip6; | |||||
| v6dst->sin6_addr = args->f_id.dst_ip6; | |||||
| *src = (struct sockaddr *)v6src; | |||||
| *dst = (struct sockaddr *)v6dst; | |||||
| } else | |||||
| #endif | |||||
| { | |||||
| v4src = (struct sockaddr_in *)*buf; | |||||
| *buf += sizeof(*v4src); | |||||
| v4dst = (struct sockaddr_in *)*buf; | |||||
| *buf += sizeof(*v4dst); | |||||
| v4src->sin_len = v4dst->sin_len = sizeof(*v4src); | |||||
| v4src->sin_family = v4dst->sin_family = AF_INET; | |||||
| v4src->sin_addr.s_addr = htonl(args->f_id.src_ip); | |||||
| v4dst->sin_addr.s_addr = htonl(args->f_id.dst_ip); | |||||
| *src = (struct sockaddr *)v4src; | |||||
| *dst = (struct sockaddr *)v4dst; | |||||
| } | |||||
| } | |||||
| static struct sockaddr * | |||||
| ipfw_rtsocklog_handle_tablearg(struct ip_fw_chain *chain, ipfw_insn *cmd, | |||||
| uint32_t tablearg, uint32_t *targ_value, char **buf) | |||||
| { | |||||
| struct sockaddr_in *v4nh = NULL; | |||||
| /* handle tablearg now */ | |||||
| switch (cmd->opcode) { | |||||
| case O_DIVERT: | |||||
| case O_TEE: | |||||
| *targ_value = TARG(cmd->arg1, divert); | |||||
| break; | |||||
| case O_NETGRAPH: | |||||
| case O_NGTEE: | |||||
| *targ_value = TARG(cmd->arg1, netgraph); | |||||
| break; | |||||
| case O_SETDSCP: | |||||
| *targ_value = (TARG(cmd->arg1, dscp) & 0x3F); | |||||
| break; | |||||
| case O_SETFIB: | |||||
| *targ_value = (TARG(cmd->arg1, fib) & 0x7FFF); | |||||
| break; | |||||
| case O_SKIPTO: | |||||
| case O_CALLRETURN: | |||||
| if (cmd->opcode == O_CALLRETURN && (cmd->len & F_NOT)) | |||||
| break; | |||||
| *targ_value = (TARG(insntod(cmd, u32)->d[0], skipto)); | |||||
| break; | |||||
| case O_PIPE: | |||||
| case O_QUEUE: | |||||
| *targ_value = TARG(cmd->arg1, pipe); | |||||
| break; | |||||
| case O_MARK: | |||||
| *targ_value = TARG(cmd->arg1, mark); | |||||
| break; | |||||
| case O_FORWARD_IP: | |||||
| v4nh = (struct sockaddr_in *)buf; | |||||
| buf += sizeof(*v4nh); | |||||
| *v4nh = ((ipfw_insn_sa *)cmd)->sa; | |||||
| if (v4nh->sin_addr.s_addr == INADDR_ANY) | |||||
| v4nh->sin_addr.s_addr = htonl(tablearg); | |||||
| return (struct sockaddr *)v4nh; | |||||
| #ifdef INET6 | |||||
| case O_FORWARD_IP6: | |||||
| return (struct sockaddr *)&(((ipfw_insn_sa6 *)cmd)->sa); | |||||
| #endif | |||||
| default: | |||||
| break; | |||||
| } | |||||
| return (NULL); | |||||
| } | |||||
| #define MAX_COMMENT_LEN 80 | |||||
| static size_t | |||||
| ipfw_copy_rule_comment(struct ip_fw *f, char *dst) | |||||
| { | |||||
| ipfw_insn *cmd; | |||||
| size_t rcomment_len = 0; | |||||
| int l, cmdlen; | |||||
| for (l = f->cmd_len, cmd = f->cmd; l > 0; l -= cmdlen, cmd += cmdlen) { | |||||
| cmdlen = F_LEN(cmd); | |||||
| if (cmd->opcode != O_NOP) { | |||||
| continue; | |||||
| } else if (cmd->len == 1) { | |||||
| return (0); | |||||
| } | |||||
| break; | |||||
| } | |||||
| if (l <= 0) { | |||||
| return (0); | |||||
| } | |||||
| rcomment_len = strnlen((char *)(cmd + 1), MAX_COMMENT_LEN - 1) + 1; | |||||
| strlcpy(dst, (char *)(cmd + 1), rcomment_len); | |||||
| return (rcomment_len); | |||||
| } | |||||
| static void | |||||
| ipfw_log_rtsock(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, | |||||
| struct ip_fw_args *args, u_short offset, uint32_t tablearg, | |||||
| void *_eh) | |||||
| { | |||||
| struct sockaddr_dl *sdl_ipfwcmd; | |||||
| struct ether_header *eh = _eh; | |||||
| struct rt_addrinfo *info; | |||||
| uint32_t *targ_value; | |||||
| ipfwlog_rtsock_hdr_v2 *hdr; | |||||
| ipfw_insn *cmd; | |||||
| ipfw_insn_log *l; | |||||
| char *buf, *orig_buf; | |||||
| /* at least 4 x sizeof(struct sockaddr_dl) + rule comment (80) */ | |||||
| size_t buflen = 512; | |||||
| /* Should we log? O_LOG is the first one */ | |||||
| cmd = ACTION_PTR(f); | |||||
| l = (ipfw_insn_log *)cmd; | |||||
| if (l->max_log != 0 && l->log_left == 0) | |||||
| return; | |||||
| l->log_left--; | |||||
| if (V_fw_verbose != 0 && l->log_left == 0) { | |||||
| log(LOG_SECURITY | LOG_NOTICE, | |||||
| "ipfw: limit %d reached on entry %d\n", | |||||
| l->max_log, f ? f->rulenum : -1); | |||||
| } | |||||
| buf = orig_buf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); | |||||
| if (buf == NULL) | |||||
| return; | |||||
| info = (struct rt_addrinfo *)buf; | |||||
| buf += sizeof (*info); | |||||
| cmd = ipfw_get_action(f); | |||||
| sdl_ipfwcmd = (struct sockaddr_dl *)buf; | |||||
| sdl_ipfwcmd->sdl_family = AF_IPFWLOG; | |||||
| sdl_ipfwcmd->sdl_index = f->set; | |||||
| sdl_ipfwcmd->sdl_type = 2; /* version */ | |||||
| sdl_ipfwcmd->sdl_alen = sizeof(*hdr); | |||||
| hdr = (ipfwlog_rtsock_hdr_v2 *)(sdl_ipfwcmd->sdl_data); | |||||
| /* fill rule comment in if any */ | |||||
| sdl_ipfwcmd->sdl_nlen = ipfw_copy_rule_comment(f, hdr->comment); | |||||
| targ_value = &hdr->tablearg; | |||||
| hdr->rulenum = f->rulenum; | |||||
| hdr->mark = args->rule.pkt_mark; | |||||
| hdr->cmd = *cmd; | |||||
| sdl_ipfwcmd->sdl_len = sizeof(*sdl_ipfwcmd); | |||||
| if (sizeof(*hdr) + sdl_ipfwcmd->sdl_nlen > sizeof(sdl_ipfwcmd->sdl_data)) { | |||||
| sdl_ipfwcmd->sdl_len += sizeof(*hdr) + sdl_ipfwcmd->sdl_nlen - | |||||
| sizeof(sdl_ipfwcmd->sdl_data); | |||||
| } | |||||
| buf += sdl_ipfwcmd->sdl_len; | |||||
| /* fill L2 in if present */ | |||||
| if (args->flags & IPFW_ARGS_ETHER && eh != NULL) { | |||||
| sdl_ipfwcmd->sdl_slen = sizeof(eh->ether_shost); | |||||
| memcpy(hdr->ether_shost, eh->ether_shost, | |||||
| sdl_ipfwcmd->sdl_slen); | |||||
| memcpy(hdr->ether_dhost, eh->ether_dhost, | |||||
| sdl_ipfwcmd->sdl_slen); | |||||
| } | |||||
| info->rti_info[RTAX_DST] = (struct sockaddr *)sdl_ipfwcmd; | |||||
| /* Warn if we're about to stop sending messages */ | |||||
| if (l->max_log != 0 && l->log_left < (l->max_log >> 1)) { | |||||
| info->rti_flags |= RTF_PROTO1; | |||||
| } | |||||
| /* handle tablearg */ | |||||
| info->rti_info[RTAX_GENMASK] = ipfw_rtsocklog_handle_tablearg( | |||||
| chain, cmd, tablearg, targ_value, &buf); | |||||
| /* L3 */ | |||||
| ipfw_rtsocklog_fill_l3(args, &buf, | |||||
| &info->rti_info[RTAX_GATEWAY], | |||||
| &info->rti_info[RTAX_NETMASK]); | |||||
| info->rti_ifp = args->ifp; | |||||
| rtsock_routemsg_info(RTM_IPFWLOG, info, RT_ALL_FIBS); | |||||
| free(orig_buf, M_TEMP); | |||||
| } | |||||
| /* | |||||
| * We enter here when we have a rule with O_LOG. | |||||
| */ | |||||
| void | |||||
| ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, | |||||
| struct ip_fw_args *args, u_short offset, uint32_t tablearg, | |||||
| struct ip *ip, void *eh) | |||||
| { | |||||
| ipfw_insn *cmd; | |||||
| if (f == NULL || hlen == 0) | |||||
| return; | |||||
| /* O_LOG is the first action */ | |||||
| cmd = ACTION_PTR(f); | |||||
| if (cmd->arg1 == IPFW_LOG_DEFAULT) { | |||||
| if (V_fw_verbose == 0) { | |||||
| ipfw_log_ipfw0(args, ip); | |||||
| return; | |||||
| } | |||||
| ipfw_log_syslog(chain, f, hlen, args, offset, tablearg, ip); | |||||
| return; | |||||
| } | |||||
| if (cmd->arg1 & IPFW_LOG_SYSLOG) | |||||
| ipfw_log_syslog(chain, f, hlen, args, offset, tablearg, ip); | |||||
| if (cmd->arg1 & IPFW_LOG_RTSOCK) | |||||
| ipfw_log_rtsock(chain, f, hlen, args, offset, tablearg, eh); | |||||
| if (cmd->arg1 & IPFW_LOG_IPFW0) | |||||
| ipfw_log_ipfw0(args, ip); | |||||
| } | } | ||||
| /* end of file */ | /* end of file */ | ||||