/* * fw-ipfw.c * * Copyright (c) 2001 Dug Song * * $Id$ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "dnet.h" struct fw_handle { int fd; }; static void fr_to_ipfw_device(const char *device, char *name, short *unit) { char *p; p = strpbrk(device, "0123456789"); *unit = atoi(p); strlcpy(name, device, p - device + 1); } static void fr_to_ipfw(const struct fw_rule *fr, struct ip_fw *ipfw) { int i; memset(ipfw, 0, sizeof(*ipfw)); if (fr->fw_dir == FW_DIR_IN) { if (*fr->fw_device != '\0') { fr_to_ipfw_device(fr->fw_device, ipfw->fw_in_if.fu_via_if.name, &ipfw->fw_in_if.fu_via_if.unit); ipfw->fw_flg |= IP_FW_F_IIFNAME; } ipfw->fw_flg |= IP_FW_F_IN; } else { if (*fr->fw_device != '\0') { fr_to_ipfw_device(fr->fw_device, ipfw->fw_out_if.fu_via_if.name, &ipfw->fw_out_if.fu_via_if.unit); ipfw->fw_flg |= IP_FW_F_OIFNAME; } ipfw->fw_flg |= IP_FW_F_OUT; } if (fr->fw_op == FW_OP_ALLOW) ipfw->fw_flg |= IP_FW_F_ACCEPT; else ipfw->fw_flg |= IP_FW_F_DENY; ipfw->fw_prot = fr->fw_proto; ipfw->fw_src.s_addr = fr->fw_src.addr_ip; ipfw->fw_dst.s_addr = fr->fw_dst.addr_ip; addr_btom(fr->fw_src.addr_bits, &ipfw->fw_smsk.s_addr, IP_ADDR_LEN); addr_btom(fr->fw_dst.addr_bits, &ipfw->fw_dmsk.s_addr, IP_ADDR_LEN); switch (fr->fw_proto) { case IP_PROTO_TCP: case IP_PROTO_UDP: i = 0; if (fr->fw_sport[0] != fr->fw_sport[1]) { ipfw->fw_flg |= IP_FW_F_SRNG; ipfw->fw_uar.fw_pts[i++] = fr->fw_sport[0]; ipfw->fw_uar.fw_pts[i++] = fr->fw_sport[1]; IP_FW_SETNSRCP(ipfw, 2); } else if (fr->fw_sport[0] > 0) { ipfw->fw_uar.fw_pts[i++] = fr->fw_sport[0]; IP_FW_SETNSRCP(ipfw, 1); } if (fr->fw_dport[0] != fr->fw_dport[1]) { ipfw->fw_flg |= IP_FW_F_DRNG; ipfw->fw_uar.fw_pts[i++] = fr->fw_dport[0]; ipfw->fw_uar.fw_pts[i++] = fr->fw_dport[1]; IP_FW_SETNDSTP(ipfw, 2); } else if (fr->fw_dport[0] > 0) { ipfw->fw_uar.fw_pts[i++] = fr->fw_dport[0]; IP_FW_SETNDSTP(ipfw, 1); } break; case IP_PROTO_ICMP: if (fr->fw_sport[1]) { ipfw->fw_uar.fw_icmptypes[fr->fw_sport[0] / 32] |= 1 << (fr->fw_sport[0] % 32); ipfw->fw_flg |= IP_FW_F_ICMPBIT; } /* XXX - no support for ICMP code. */ break; } } static void ipfw_to_fr(const struct ip_fw *ipfw, struct fw_rule *fr) { int i; memset(fr, 0, sizeof(*fr)); if ((ipfw->fw_flg & IP_FW_F_IN) && *ipfw->fw_in_if.fu_via_if.name) snprintf(fr->fw_device, sizeof(fr->fw_device), "%s%d", ipfw->fw_in_if.fu_via_if.name, ipfw->fw_in_if.fu_via_if.unit); else if ((ipfw->fw_flg & IP_FW_F_OUT) && *ipfw->fw_out_if.fu_via_if.name) snprintf(fr->fw_device, sizeof(fr->fw_device), "%s%d", ipfw->fw_out_if.fu_via_if.name, ipfw->fw_out_if.fu_via_if.unit); fr->fw_op = (ipfw->fw_flg & IP_FW_F_ACCEPT) ? FW_OP_ALLOW : FW_OP_BLOCK; fr->fw_dir = (ipfw->fw_flg & IP_FW_F_IN) ? FW_DIR_IN : FW_DIR_OUT; fr->fw_proto = ipfw->fw_prot; fr->fw_src.addr_type = fr->fw_dst.addr_type = ADDR_TYPE_IP; fr->fw_src.addr_ip = ipfw->fw_src.s_addr; fr->fw_dst.addr_ip = ipfw->fw_dst.s_addr; addr_mtob(&ipfw->fw_smsk.s_addr, IP_ADDR_LEN, &fr->fw_src.addr_bits); addr_mtob(&ipfw->fw_dmsk.s_addr, IP_ADDR_LEN, &fr->fw_dst.addr_bits); switch (fr->fw_proto) { case IP_PROTO_TCP: case IP_PROTO_UDP: if ((ipfw->fw_flg & IP_FW_F_SRNG) && IP_FW_GETNSRCP(ipfw) == 2) { fr->fw_sport[0] = ipfw->fw_uar.fw_pts[0]; fr->fw_sport[1] = ipfw->fw_uar.fw_pts[1]; } else if (IP_FW_GETNSRCP(ipfw) == 1) { fr->fw_sport[0] = fr->fw_sport[1] = ipfw->fw_uar.fw_pts[0]; } else if (IP_FW_GETNSRCP(ipfw) == 0) { fr->fw_sport[0] = 0; fr->fw_sport[1] = TCP_PORT_MAX; } if ((ipfw->fw_flg & IP_FW_F_DRNG) && IP_FW_GETNDSTP(ipfw) == 2) { i = IP_FW_GETNSRCP(ipfw); fr->fw_dport[0] = ipfw->fw_uar.fw_pts[i]; fr->fw_dport[1] = ipfw->fw_uar.fw_pts[i + 1]; } else if (IP_FW_GETNDSTP(ipfw) == 1) { i = IP_FW_GETNSRCP(ipfw); fr->fw_dport[0] = fr->fw_dport[1] = ipfw->fw_uar.fw_pts[i]; } else if (IP_FW_GETNDSTP(ipfw) == 0) { fr->fw_dport[0] = 0; fr->fw_dport[1] = TCP_PORT_MAX; } break; case IP_PROTO_ICMP: if (ipfw->fw_flg & IP_FW_F_ICMPBIT) { for (i = 0; i < IP_FW_ICMPTYPES_DIM * 32; i++) { if (ipfw->fw_uar.fw_icmptypes[i / 32] & (1U << (i % 32))) { fr->fw_sport[0] = i; fr->fw_sport[1] = 0xff; break; } } } /* XXX - no support for ICMP code. */ break; } } fw_t * fw_open(void) { fw_t *fw; if ((fw = calloc(1, sizeof(*fw))) != NULL) { if ((fw->fd = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) < 0) return (fw_close(fw)); } return (fw); } int fw_add(fw_t *fw, const struct fw_rule *rule) { struct ip_fw ipfw; assert(fw != NULL && rule != NULL); fr_to_ipfw(rule, &ipfw); return (setsockopt(fw->fd, IPPROTO_IP, IP_FW_ADD, &ipfw, sizeof(ipfw))); } static int fw_cmp(const struct fw_rule *a, const struct fw_rule *b) { if (strcmp(a->fw_device, b->fw_device) != 0 || a->fw_op != b->fw_op || a->fw_dir != b->fw_dir || a->fw_proto != b->fw_proto || addr_cmp(&a->fw_src, &b->fw_src) != 0 || addr_cmp(&a->fw_dst, &b->fw_dst) != 0 || memcmp(a->fw_sport, b->fw_sport, sizeof(a->fw_sport)) != 0 || memcmp(a->fw_dport, b->fw_dport, sizeof(a->fw_dport)) != 0) return (-1); return (0); } int fw_delete(fw_t *fw, const struct fw_rule *rule) { struct ip_fw *ipfw; struct fw_rule fr; int nbytes, nalloc, ret; u_char *buf, *new; assert(rule != NULL); nbytes = nalloc = sizeof(*ipfw); if ((buf = malloc(nbytes)) == NULL) return (-1); while (nbytes >= nalloc) { nalloc = nalloc * 2 + 200; nbytes = nalloc; if ((new = realloc(buf, nbytes)) == NULL) { if (buf) free(buf); return (-1); } buf = new; if (getsockopt(fw->fd, IPPROTO_IP, IP_FW_GET, buf, &nbytes) < 0) { free(buf); return (-1); } } ret = -1; /* XXX - 65535 is the fixed ipfw default rule. */ for (ipfw = (struct ip_fw *)buf; ipfw->fw_number < 65535; ipfw++) { ipfw_to_fr(ipfw, &fr); if (fw_cmp(&fr, rule) == 0) { if (setsockopt(fw->fd, IPPROTO_IP, IP_FW_DEL, ipfw, sizeof(*ipfw)) < 0) ret = -2; else ret = 0; break; } } free(buf); if (ret < 0) { if (ret == -1) errno = ESRCH; return (-1); } return (0); } int fw_loop(fw_t *fw, fw_handler callback, void *arg) { struct ip_fw *ipfw; struct fw_rule fr; int i, cnt, nbytes, nalloc, ret; u_char *buf, *new; nbytes = nalloc = sizeof(*ipfw); if ((buf = malloc(nbytes)) == NULL) return (-1); while (nbytes >= nalloc) { nalloc = nalloc * 2 + 200; nbytes = nalloc; if ((new = realloc(buf, nbytes)) == NULL) { if (buf) free(buf); return (-1); } buf = new; if (getsockopt(fw->fd, IPPROTO_IP, IP_FW_GET, buf, &nbytes) < 0) { free(buf); return (-1); } } cnt = nbytes / sizeof(*ipfw); ipfw = (struct ip_fw *)buf; ret = 0; for (i = 0; i < cnt; i++) { ipfw_to_fr(&ipfw[i], &fr); if ((ret = callback(&fr, arg)) != 0) break; } free(buf); return (ret); } fw_t * fw_close(fw_t *fw) { if (fw != NULL) { if (fw->fd >= 0) close(fw->fd); free(fw); } return (NULL); }