diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 577d644..7d82d34 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -4115,8 +4115,9 @@ ipfw_flush(int force) } +static void table_list_entry(ipfw_table_xentry *); static void table_list(uint16_t num, int need_header); -static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); +static void table_fill_xentry(int ac, char *av[], ipfw_table_xentry *xent); /* * This one handles all table-related commands @@ -4169,29 +4170,9 @@ ipfw_table_handler(int ac, char *av[]) if (_substrcmp(*av, "add") == 0 || _substrcmp(*av, "delete") == 0) { do_add = **av == 'a'; - ac--; av++; - if (!ac) - errx(EX_USAGE, "address required"); - table_fill_xentry(*av, &xent); + table_fill_xentry(ac, av, &xent); - ac--; av++; - if (do_add && ac) { - unsigned int tval; - /* isdigit is a bit of a hack here.. */ - if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { - xent.value = strtoul(*av, NULL, 0); - } else { - if (lookup_host(*av, (struct in_addr *)&tval) == 0) { - /* The value must be stored in host order * - * so that the values < 65k can be distinguished */ - xent.value = ntohl(tval); - } else { - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); - } - } - } else - xent.value = 0; if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL, &xent, xent.len) < 0) { /* If running silent, don't bomb out on these errors. */ @@ -4218,19 +4199,41 @@ ipfw_table_handler(int ac, char *av[]) do { table_list(xent.tbl, is_all); } while (++xent.tbl < a); + } else if (_substrcmp(*av, "entrystats") == 0) { + table_fill_xentry(ac, av, &xent); + + if (do_setcmd3(IP_FW_TABLE_XLISTENTRY, &xent, xent.len) < 0) { + /* If running silent, don't bomb out on these errors. */ + if (!(co.do_quiet)) + err(EX_OSERR, "setsockopt(IP_FW_TABLE_XLISTENTRY)"); + } else + table_list_entry(&xent); + } else if (_substrcmp(*av, "entryzerostats") == 0) { + table_fill_xentry(ac, av, &xent); + + if (do_setcmd3(IP_FW_TABLE_XZEROENTRY, &xent, xent.len) < 0) { + /* If running silent, don't bomb out on these errors. */ + if (!(co.do_quiet)) + err(EX_OSERR, "setsockopt(IP_FW_TABLE_XZEROENTRY)"); + } } else errx(EX_USAGE, "invalid table command %s", *av); } static void -table_fill_xentry(char *arg, ipfw_table_xentry *xent) +table_fill_xentry(int ac, char *av[], ipfw_table_xentry *xent) { - int addrlen, mask, masklen, type; + int addrlen, mask, masklen, type, do_add; struct in6_addr *paddr; uint32_t *pkey; - char *p; + char *p, *arg; uint32_t key; + do_add = **av == 'a'; + ac--; av++; + if (!ac) + errx(EX_USAGE, "address required"); + mask = 0; type = 0; addrlen = 0; @@ -4245,7 +4248,23 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent) * 4) port, uid/gid or other u32 key (base 10 format) * 5) hostname */ - paddr = &xent->k.addr6; + if (ac > 1 && av) { + if (_substrcmp(*av, "mac") == 0) { + uint8_t _mask[8]; + + type = IPFW_TABLE_MIX; + get_mac_addr_mask(av[1], (uint8_t *)xent->k.mix.mac, _mask); + ac-=2; av+=2; + if (ac <= 0) + errx(EX_DATAERR, "wrong argument passed."); + + paddr = (struct in6_addr *)&xent->k.mix.addr; + } else + errx(EX_DATAERR, "wrong argument passed."); + } else + paddr = &xent->k.addr6; + + arg = *av; if (ishexnumber(*arg) != 0 || *arg == ':') { /* Remove / if exists */ if ((p = strchr(arg, '/')) != NULL) { @@ -4258,7 +4277,8 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent) errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); - type = IPFW_TABLE_CIDR; + if (type == 0) + type = IPFW_TABLE_CIDR; masklen = p ? mask : 32; addrlen = sizeof(struct in_addr); } else if (inet_pton(AF_INET6, arg, paddr) == 1) { @@ -4269,10 +4289,14 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent) errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); - type = IPFW_TABLE_CIDR; + if (type == 0) + type = IPFW_TABLE_CIDR; masklen = p ? mask : 128; addrlen = sizeof(struct in6_addr); } else { + if (type != 0 && type != IPFW_TABLE_MIX) + errx(EX_DATAERR, "Wrong value passed as address"); + /* Port or any other key */ key = strtol(arg, &p, 10); /* Skip non-base 10 entries like 'fa1' */ @@ -4304,9 +4328,103 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent) addrlen = sizeof(struct in_addr); } + ac--; av++; + if (ac > 1 && av) { + if (_substrcmp(*av, "mac") == 0) { + uint8_t _mask[8]; + + if (type == 0) + type = IPFW_TABLE_CIDR; + get_mac_addr_mask(av[1], (uint8_t *)xent->mac_addr, _mask); + ac-=2; av+=2; + } + } + + if (do_add && ac > 0) { + unsigned int tval; + /* isdigit is a bit of a hack here.. */ + if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { + xent->value = strtoul(*av, NULL, 0); + } else { + if (lookup_host(*av, (struct in_addr *)&tval) == 0) { + /* The value must be stored in host order * + * so that the values < 65k can be distinguished */ + xent->value = ntohl(tval); + } else { + errx(EX_NOHOST, "hostname ``%s'' unknown", *av); + } + } + } else + xent->value = 0; + xent->type = type; xent->masklen = masklen; - xent->len = offsetof(ipfw_table_xentry, k) + addrlen; + if (type == IPFW_TABLE_MIX) + xent->len = offsetof(ipfw_table_xentry, k) + addrlen + ETHER_ADDR_LEN; + else + xent->len = offsetof(ipfw_table_xentry, k) + addrlen; +} + +static void +table_list_entry(ipfw_table_xentry *xent) +{ + struct in6_addr *addr6; + uint32_t tval; + char tbuf[128]; + + switch (xent->type) { + case IPFW_TABLE_CIDR: + /* IPv4 or IPv6 prefixes */ + tval = xent->value; + addr6 = &xent->k.addr6; + + + if (IN6_IS_ADDR_V4COMPAT(addr6)) { + /* IPv4 address */ + inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); + } else { + /* IPv6 address */ + inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf)); + } + + if (co.do_value_as_ip) { + tval = htonl(tval); + printf("%s/%u %s %d %d %u\n", tbuf, xent->masklen, + inet_ntoa(*(struct in_addr *)&tval), pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + } else + printf("%s/%u %u %d %d %u\n", tbuf, xent->masklen, tval, + pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + break; + case IPFW_TABLE_INTERFACE: + /* Interface names */ + tval = xent->value; + if (co.do_value_as_ip) { + tval = htonl(tval); + printf("%s %u %s %d %d %u\n", xent->k.iface, xent->masklen, + inet_ntoa(*(struct in_addr *)&tval), pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + } else + printf("%s %u %u %d %d %u\n", xent->k.iface, xent->masklen, tval, + pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + + break; + + case IPFW_TABLE_MIX: + /* mix of ip+mac */ + tval = xent->value; + + /* IPv4 address */ + inet_ntop(AF_INET, &xent->k.mix.addr, tbuf, sizeof(tbuf)); + + if (co.do_value_as_ip) { + tval = htonl(tval); + printf("%s/%u %s %s %d %d %u\n", tbuf, xent->masklen - (8 * ETHER_ADDR_LEN), ether_ntoa((struct ether_addr *)xent->k.mix.mac), + inet_ntoa(*(struct in_addr *)&tval), + pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + } else + printf("%s/%u %s %u %d %d %u\n", tbuf, xent->masklen - (8 * ETHER_ADDR_LEN), ether_ntoa((struct ether_addr *)xent->k.mix.mac), tval, + pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp); + break; + } } static void @@ -4338,6 +4456,7 @@ table_list(uint16_t num, int need_header) l = *a; tbl = safe_calloc(1, l); tbl->opheader.opcode = IP_FW_TABLE_XLIST; + tbl->opheader.ctxid = co.ctx; tbl->tbl = num; if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0) err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)"); @@ -4377,6 +4496,23 @@ table_list(uint16_t num, int need_header) inet_ntoa(*(struct in_addr *)&tval)); } else printf("%s %u\n", xent->k.iface, tval); + + break; + + case IPFW_TABLE_MIX: + /* mix of ip+mac */ + tval = xent->value; + + /* IPv4 address */ + inet_ntop(AF_INET, &xent->k.mix.addr, tbuf, sizeof(tbuf)); + + if (co.do_value_as_ip) { + tval = htonl(tval); + printf("%s/%u %s %s\n", tbuf, xent->masklen - (8 * ETHER_ADDR_LEN), ether_ntoa((struct ether_addr *)xent->k.mix.mac), + inet_ntoa(*(struct in_addr *)&tval)); + } else + printf("%s/%u %s %u\n", tbuf, xent->masklen - (8 * ETHER_ADDR_LEN), ether_ntoa((struct ether_addr *)xent->k.mix.mac), tval); + break; } if (sz < xent->len) diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 1a6ac08..226bb21 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -742,6 +742,8 @@ ether_demux(struct ifnet *ifp, struct mbuf *m) if (i != 0 || m == NULL) return; + + i = m->m_flags & M_FASTFWD_OURS; } eh = mtod(m, struct ether_header *); @@ -781,6 +783,8 @@ ether_demux(struct ifnet *ifp, struct mbuf *m) */ m->m_flags &= ~M_VLANTAG; m_clrprotoflags(m); + if (i) + m->m_flags |= M_FASTFWD_OURS; m_adj(m, ETHER_HDR_LEN); /* diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 14b08f5..f868bc4 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -74,6 +74,8 @@ typedef struct _ip_fw3_opheader { #define IP_FW_TABLE_XDEL 87 /* delete entry */ #define IP_FW_TABLE_XGETSIZE 88 /* get table size */ #define IP_FW_TABLE_XLIST 89 /* list table contents */ +#define IP_FW_TABLE_XLISTENTRY 90 /* list one table entry contents */ +#define IP_FW_TABLE_XZEROENTRY 91 /* zero one table entry stats */ /* * The kernel representation of ipfw rules is made of a list of @@ -600,7 +602,9 @@ struct _ipfw_dyn_rule { #define IPFW_TABLE_CIDR 1 /* Table for holding IPv4/IPv6 prefixes */ #define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */ -#define IPFW_TABLE_MAXTYPE 2 /* Maximum valid number */ +#define IPFW_TABLE_MIX 3 /* Table for holding IPv4/mac entries */ +#define IPFW_TABLE_MAC 4 /* Table for holding mac entries */ +#define IPFW_TABLE_MAXTYPE 5 /* Maximum valid number */ typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ @@ -617,9 +621,26 @@ typedef struct _ipfw_table_xentry { uint32_t value; /* value */ union { /* Longest field needs to be aligned by 4-byte boundary */ +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN 6 +#endif +#if 0 + struct { + struct ether_addr addr; + struct ether_addr mask; + } mac; +#endif + struct { + struct in_addr addr; + u_char mac[ETHER_ADDR_LEN]; + } mix; struct in6_addr addr6; /* IPv6 address */ char iface[IF_NAMESIZE]; /* interface name */ } k; + uint64_t mac_addr; + uint64_t bytes; + uint64_t packets; + uint32_t timestamp; } ipfw_table_xentry; typedef struct _ipfw_table { diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index cd466bd..f2f117e 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -358,8 +358,8 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uin /* Check by name or by IP address */ if (cmd->name[0] != '\0') { /* match by name */ if (cmd->name[0] == '\1') /* use tablearg to match */ - return ipfw_lookup_table_extended(chain, cmd->p.glob, - ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE); + return (ipfw_lookup_table_extended(chain, cmd->p.glob, + ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE, NULL) != NULL); /* Check name */ if (cmd->p.glob) { if (fnmatch(cmd->name, ifp->if_xname, 0) == 0) @@ -955,6 +955,7 @@ ipfw_chk(struct ip_fw_args *args) int dyn_dir = MATCH_UNKNOWN; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; + void *tblent = NULL; /* * We store in ulp a pointer to the upper layer protocol header. @@ -1288,6 +1289,7 @@ do { \ continue; skip_or = 0; + tblent = NULL; for (l = f->cmd_len, cmd = f->cmd ; l > 0 ; l -= cmdlen, cmd += cmdlen) { int match; @@ -1402,7 +1404,7 @@ do { \ break; case O_IN: /* "out" is "not in" */ - match = (oif == NULL); + match = (args->dir == DIR_IN); break; case O_LAYER2: @@ -1438,11 +1440,18 @@ do { \ case O_IP_SRC_LOOKUP: case O_IP_DST_LOOKUP: if (is_ipv4) { + struct ether_addr *ea = NULL; + uint32_t key = (cmd->opcode == O_IP_DST_LOOKUP) ? dst_ip.s_addr : src_ip.s_addr; uint32_t v = 0; + if (args->eh) { + ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ? + args->eh->ether_dhost : + args->eh->ether_shost); + } if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) { /* generic lookup. The key must be * in 32bit big-endian format. @@ -1484,22 +1493,37 @@ do { \ } else break; } - match = ipfw_lookup_table(chain, - cmd->arg1, key, &v); - if (!match) + tblent = ipfw_lookup_table_extended(chain, + cmd->arg1, &key, &v, IPFW_TABLE_CIDR, ea); + if (tblent == NULL) { + match = 0; break; + } else + match = 1; if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) match = ((ipfw_insn_u32 *)cmd)->d[0] == v; - else + if (match) tablearg = v; } else if (is_ipv6) { + struct ether_addr *ea = NULL; uint32_t v = 0; + + if (args->eh) { + ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ? + args->eh->ether_dhost : + args->eh->ether_shost); + } void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ? &args->f_id.dst_ip6: &args->f_id.src_ip6; - match = ipfw_lookup_table_extended(chain, + tblent = ipfw_lookup_table_extended(chain, cmd->arg1, pkey, &v, - IPFW_TABLE_CIDR); + IPFW_TABLE_CIDR, ea); + if (tblent == NULL) { + match = 0; + break; + } else + match = 1; if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) match = ((ipfw_insn_u32 *)cmd)->d[0] == v; if (match) @@ -2314,8 +2338,7 @@ do { \ break; case O_FORWARD_IP: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (!args->eh) {/* not valid on layer2 pkts */ if (q == NULL || q->rule != f || dyn_dir == MATCH_FORWARD) { struct sockaddr_in *sa; @@ -2330,6 +2353,48 @@ do { \ args->next_hop = sa; } } + } else if (args->eh) { + struct m_tag *fwd_tag; + struct sockaddr_in *sa; + u_short sum; + + /* + * Checksum correct? (from ip_fastfwd.c) + */ + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) + sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); + else { + if (hlen == sizeof(struct ip)) + sum = in_cksum_hdr(ip); + else + sum = in_cksum(m, hlen); + } + if (sum) { + IPSTAT_INC(ips_badsum); + retval = IP_FW_DENY; + break; + } + + /* + * Remember that we have checked the IP header and found it valid. + */ + m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID); + + sa = &(((ipfw_insn_sa *)cmd)->sa); + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in), M_NOWAIT); + if (fwd_tag == NULL) + retval = IP_FW_DENY; + else { + bcopy(sa, (fwd_tag+1), sizeof(struct sockaddr_in)); + m_tag_prepend(m, fwd_tag); + + if (in_localip(sa->sin_addr)) + m->m_flags |= M_FASTFWD_OURS; + m->m_flags |= M_IP_NEXTHOP; + } + } + retval = IP_FW_PASS; l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -2337,8 +2402,7 @@ do { \ #ifdef INET6 case O_FORWARD_IP6: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (!args->eh) {/* not valid on layer2 pkts */ if (q == NULL || q->rule != f || dyn_dir == MATCH_FORWARD) { struct sockaddr_in6 *sin6; @@ -2346,6 +2410,24 @@ do { \ sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); args->next_hop6 = sin6; } + } else if (args->eh) { + struct m_tag *fwd_tag; + struct sockaddr_in6 *sin6; + + sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); + fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, + sizeof(struct sockaddr_in6), M_NOWAIT); + if (fwd_tag == NULL) + retval = IP_FW_DENY; + else { + bcopy(sin6, (fwd_tag+1), sizeof(struct sockaddr_in6)); + m_tag_prepend(m, fwd_tag); + + if (in6_localip(&sin6->sin6_addr)) + m->m_flags |= M_FASTFWD_OURS; + m->m_flags |= M_IP6_NEXTHOP; + } + } retval = IP_FW_PASS; l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -2505,6 +2587,8 @@ do { \ struct ip_fw *rule = chain->map[f_pos]; /* Update statistics */ IPFW_INC_RULE_COUNTER(rule, pktlen); + if (tblent != NULL) + ipfw_count_table_xentry_stats(tblent, pktlen); } else { retval = IP_FW_DENY; printf("ipfw: ouch!, skip past end of rules, denying packet\n"); diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index d1202ff..bf225b8 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -143,8 +143,9 @@ again: } args.m = *m0; - args.oif = dir == DIR_OUT ? ifp : NULL; + args.oif = ifp; args.inp = inp; + args.dir = dir; ipfw = ipfw_chk(&args); *m0 = args.m; @@ -314,9 +315,8 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, /* XXX can we free it after use ? */ mtag->m_tag_id = PACKET_TAG_NONE; r = (struct ipfw_rule_ref *)(mtag + 1); - if (r->info & IPFW_ONEPASS) - return (0); - args.rule = *r; + m_tag_delete(*m0, mtag); + return (0); } /* I need some amt of data to be contiguous */ @@ -333,12 +333,15 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, save_eh = *eh; /* save copy for restore below */ m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ + dir = dir == PFIL_IN ? DIR_IN : DIR_OUT; + args.m = m; /* the packet we are looking at */ - args.oif = dir == PFIL_OUT ? dst: NULL; /* destination, if any */ + args.oif = dst; /* destination, if any */ args.next_hop = NULL; /* we do not support forward yet */ args.next_hop6 = NULL; /* we do not support forward yet */ args.eh = &save_eh; /* MAC header for bridged/MAC packets */ args.inp = NULL; /* used by ipfw uid/gid/jail rules */ + args.dir = dir; /* pfSense addition */ i = ipfw_chk(&args); m = args.m; if (m != NULL) { @@ -369,13 +372,12 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir, case IP_FW_DUMMYNET: ret = EACCES; - int dir; if (ip_dn_io_ptr == NULL) break; /* i.e. drop */ *m0 = NULL; - dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN); + dir = PROTO_LAYER2 | dir; ip_dn_io_ptr(&m, dir, &args); return 0; diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index a8d7eea..9504249 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -101,6 +101,7 @@ struct ip_fw_args { struct ipfw_flow_id f_id; /* grabbed from IP header */ //uint32_t cookie; /* a cookie depending on rule action */ + uint32_t dir; /* direction */ struct inpcb *inp; struct _ip6dn_args dummypar; /* dummynet->ip6_output */ @@ -304,8 +305,11 @@ void ipfw_reap_rules(struct ip_fw *head); struct radix_node; int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint32_t *val); -int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type); +void *ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint32_t *val, int type, struct ether_addr *); +void ipfw_count_table_xentry_stats(void *, int); +int ipfw_zero_table_xentry_stats(struct ip_fw_chain *, ipfw_table_xentry *); +int ipfw_lookup_table_xentry(struct ip_fw_chain *, ipfw_table_xentry *); int ipfw_init_tables(struct ip_fw_chain *ch); void ipfw_destroy_tables(struct ip_fw_chain *ch); int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl); diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index cb9c89c..e55f455 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -1162,7 +1162,7 @@ ipfw_ctl(struct sockopt *sopt) error = (opt == IP_FW_TABLE_XADD) ? ipfw_add_table_entry(chain, xent->tbl, &xent->k, - len, xent->masklen, xent->type, xent->value) : + len, xent->masklen, xent->type, xentry->mac_addr, xent->value) : ipfw_del_table_entry(chain, xent->tbl, &xent->k, len, xent->masklen, xent->type); } @@ -1245,6 +1245,47 @@ ipfw_ctl(struct sockopt *sopt) } break; + case IP_FW_TABLE_XZEROENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_zero_table_xentry_stats(chain, xent); + } + break; + + case IP_FW_TABLE_XLISTENTRY: /* IP_FW3 */ + { + ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error = EINVAL; + break; + } + + /* Check if len field is valid */ + if (xent->len > sizeof(ipfw_table_xentry)) { + error = EINVAL; + break; + } + + error = ipfw_lookup_table_xentry(chain, xent); + xent->timestamp += boottime.tv_sec; + } + break; + case IP_FW_TABLE_XLIST: /* IP_FW3 */ { ipfw_xtable *tbl; diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 95cff5c..7b8b961 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -74,7 +74,11 @@ static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); struct table_entry { struct radix_node rn[2]; struct sockaddr_in addr, mask; + u_int64_t mac_addr; u_int32_t value; + u_int32_t timestamp; + u_int64_t bytes; + u_int64_t packets; }; struct xaddr_iface { @@ -83,6 +87,22 @@ struct xaddr_iface { char ifname[IF_NAMESIZE]; /* Interface name */ }; +#if 0 +struct xaddr_mac { + uint8_t mac_len; /* length of this struct */ + uint8_t pad[7]; /* Align name */ + struct ether_addr mac; +}; +#endif + +struct xaddr_mix { + uint8_t mix_len; /* length of this struct */ + sa_family_t sin_family; + uint8_t pad[6]; + struct in_addr sin_addr; + u_char mac[ETHER_ADDR_LEN]; +}; + struct table_xentry { struct radix_node rn[2]; union { @@ -90,14 +110,26 @@ struct table_xentry { struct sockaddr_in6 addr6; #endif struct xaddr_iface iface; +#if 0 + struct xaddr_mac mac; +#endif + struct xaddr_mix mix; } a; union { #ifdef INET6 struct sockaddr_in6 mask6; #endif struct xaddr_iface ifmask; +#if 0 + struct xaddr_mac macmask; +#endif + struct xaddr_mix mixmask; } m; + u_int64_t mac_addr; u_int32_t value; + u_int32_t timestamp; + u_int64_t bytes; + u_int64_t packets; }; /* @@ -117,10 +149,17 @@ struct table_xentry { #define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) #define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) #define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) +#define KEY_LEN_MIX (offsetof(struct xaddr_mix, sin_addr) + sizeof(in_addr_t) + ETHER_ADDR_LEN) +#if 0 +#define KEY_LEN_MAC (offsetof(struct xaddr_mac, mac) + ETHER_ADDR_LEN) +#endif #define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) #define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) #define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) +#if 0 +#define OFF_LEN_MAC (8 * offsetof(struct xaddr_mac, mac)) +#endif #ifdef INET6 @@ -137,7 +176,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask) int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) + uint8_t plen, uint8_t mlen, uint8_t type, u_int64_t mac_addr, uint32_t value) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; @@ -161,6 +200,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, return (EINVAL); ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); ent->value = value; + ent->mac_addr = mac_addr; /* Set 'total' structure length */ KEY_LEN(ent->addr) = KEY_LEN_INET; KEY_LEN(ent->mask) = KEY_LEN_INET; @@ -182,6 +222,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, return (EINVAL); xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; + ent->mac_addr = mac_addr; /* Set 'total' structure length */ KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; @@ -233,6 +274,52 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, mask_ptr = NULL; break; +#if 0 + case IPFW_TABLE_MAC: + int i; + + xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value = value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.mac) = KEY_LEN_MAC; + KEY_LEN(xent->m.macmask) = KEY_LEN_MAC; + /* Set offset of address in bits */ + offset = OFF_LEN_MAC; + xent->a.mac = (struct ether_addr)(*paddr); + xent->m.mac = (struct ether_addr)(*(((struct ether_addr *)paddr) + 1)); + for (i = 0; i < ETHER_ADDR_LEN; i++) + xent->a.mac.octet[i] &= xent->m.mac.octet[i]; + /* Set pointers */ + rnh_ptr = &ch->xtables[tbl]; + ent_ptr = xent; + addr_ptr = (struct sockaddr *)&xent->a.mac; + mask_ptr = (struct sockaddr *)&xent->m.macmask; + break; +#endif + + case IPFW_TABLE_MIX: + if ((plen - ETHER_ADDR_LEN) != sizeof(in_addr_t)) + return (EINVAL); + + xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value = value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.mix) = KEY_LEN_MIX; + KEY_LEN(xent->m.mixmask) = KEY_LEN_MIX; + /* Set offset of IPv4 address in bits */ + offset = OFF_LEN_INET; + /* XXX: Needs to be fixed */ + memcpy(&xent->a.mix.sin_addr, paddr, ETHER_ADDR_LEN + sizeof(struct in_addr)); + /* Only full ips /32 and full masks supported for mac */ + memset(&xent->m.mixmask.sin_addr, 0xFF, sizeof(struct in_addr)); + memset(xent->m.mixmask.mac, 0xFF, ETHER_ADDR_LEN); + /* Set pointers */ + rnh_ptr = &ch->xtables[tbl]; + ent_ptr = xent; + addr_ptr = (struct sockaddr *)&xent->a.mix; + mask_ptr = NULL; + break; + default: return (EINVAL); } @@ -281,6 +368,18 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, IPFW_WUNLOCK(ch); if (rn == NULL) { + if (type == IPFW_TABLE_CIDR) { + /* Just update if any new value needed */ + struct table_entry *ent2; + + ent2 = (struct table_entry *)(rnh->rnh_lookup(&ent->addr, NULL, rnh)); + if (ent2 != NULL) { + if (ent2->mac_addr) { + if (!bcmp(&mac_addr, &ent2->mac_addr, ETHER_ADDR_LEN)) + ent2->value = value; + } else + ent2->value = value; + } free(ent_ptr, M_IPFW_TBL); return (EEXIST); } @@ -367,6 +466,41 @@ ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, break; +#if 0 + case IPFW_TABLE_MAC: + struct xaddr_mac mac, macmask; + memset(&mac, 0, sizeof(mac)); + memset(&macmask, 0, sizeof(macmask)); + + /* Set 'total' structure length */ + KEY_LEN(mac) = KEY_LEN_MAC; + mac.mac.mac = (struct ether_addr)(*paddr); + KEY_LEN(macmask) = KEY_LEN_MAC; + macmask.mac.macmask = (struct ether_addr)(*(((struct ether_addr *)paddr) + 1)); + for (i = 0; i < ETHER_ADDR_LEN; i++) + mac.mac.octet[i] &= macmask.mac.octet[i]; + rnh_ptr = &ch->xtables[tbl]; + sa_ptr = (struct sockaddr *)&mac; + mask_ptr = (struct sockaddr *)&macmask; + + break; +#endif + + case IPFW_TABLE_MIX: + if (mlen > (32 + ETHER_ADDR_LEN)) + return (EINVAL); + struct xaddr_mix mix; + memset(&mix, 0, sizeof(mix)); + + /* Set 'total' structure length */ + KEY_LEN(mix) = KEY_LEN_MIX; + memcpy(&mix.sin_addr, paddr, sizeof(struct in_addr) + ETHER_ADDR_LEN); + rnh_ptr = &ch->xtables[tbl]; + sa_ptr = (struct sockaddr *)&mix; + mask_ptr = NULL; + + break; + default: return (EINVAL); } @@ -552,9 +686,152 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, return (0); } +void +ipfw_count_table_xentry_stats(void *arg, int pktlen) +{ + ipfw_table_xentry *xent= arg; + + xent->packets++; + xent->bytes += pktlen; + xent->timestamp = time_uptime; +} + +int +ipfw_zero_table_xentry_stats(struct ip_fw_chain *ch, ipfw_table_xentry *arg) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + struct sockaddr_in6 sa6; + struct xaddr_iface iface; + struct xaddr_mix xm; + + if (arg->tbl >= V_fw_tables_max) + return (0); + if ((rnh = ch->xtables[arg->tbl]) == NULL) + return (0); + + switch (arg->type) { + case IPFW_TABLE_CIDR: + KEY_LEN(sa6) = KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr)); + xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + break; + + case IPFW_TABLE_INTERFACE: + KEY_LEN(iface) = KEY_LEN_IFACE + + strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + break; + +#if 0 + case IPFW_TABLE_MAC: + { + struct xaddr_mac mac; + + KEY_LEN(mac) = KEY_LEN_MAC; + &mac.mac = arg->k.mac; + xent = (struct table_xentry *)(rnh->rnh_lookup(&mac, NULL, rnh)); + } + break; +#endif + + case IPFW_TABLE_MIX: + KEY_LEN(xm) = KEY_LEN_MIX; + memcpy(&xm.sin_addr, &arg->k.mix.addr, sizeof(struct in_addr)); + memcpy(&xm.mac, arg->k.mix.mac, ETHER_ADDR_LEN); + xent = (struct table_xentry *)(rnh->rnh_lookup(&xm, NULL, rnh)); + break; + + default: + return (0); + } + + if (xent != NULL) { + xent->bytes = 0; + xent->packets = 0; + xent->timestamp = time_uptime; + + return (1); + } + return (0); +} + int +ipfw_lookup_table_xentry(struct ip_fw_chain *ch, ipfw_table_xentry *arg) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + + if (arg->tbl >= V_fw_tables_max) + return (0); + if ((rnh = ch->xtables[arg->tbl]) == NULL) + return (0); + + switch (arg->type) { + case IPFW_TABLE_CIDR: + { + struct sockaddr_in6 sa6; + KEY_LEN(sa6) = KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr)); + xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + } + break; + + case IPFW_TABLE_INTERFACE: + { + struct xaddr_iface iface; + + KEY_LEN(iface) = KEY_LEN_IFACE + + strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + } + break; + +#if 0 + case IPFW_TABLE_MAC: + { + struct xaddr_mac mac; + + KEY_LEN(mac) = KEY_LEN_MAC; + mac.mac = arg->k.mac.addr; + xent = (struct table_xentry *)(rnh->rnh_lookup(&mac, NULL, rnh)); + } + break; +#endif + + case IPFW_TABLE_MIX: + { + struct xaddr_mix xm; + + KEY_LEN(xm) = KEY_LEN_MIX; + memcpy(&xm.sin_addr, &arg->k.mix.addr, sizeof(struct in_addr)); + memcpy(&xm.mac, arg->k.mix.mac, ETHER_ADDR_LEN); + xent = (struct table_xentry *)(rnh->rnh_lookup(&xm, NULL, rnh)); + } + break; + + default: + return (0); + } + + if (xent != NULL) { + arg->bytes = xent->bytes; + arg->packets = xent->packets; + arg->value = xent->value; + arg->timestamp = xent->timestamp; + + return (1); + } + return (0); +} + +void * ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, - uint32_t *val, int type) + uint32_t *val, int type, struct ether_addr *ea) { struct radix_node_head *rnh; struct table_xentry *xent; @@ -562,15 +839,21 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, struct xaddr_iface iface; if (tbl >= V_fw_tables_max) - return (0); + return (NULL); if ((rnh = ch->xtables[tbl]) == NULL) - return (0); + return (NULL); switch (type) { case IPFW_TABLE_CIDR: KEY_LEN(sa6) = KEY_LEN_INET6; memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + if (xent != NULL) { + if (ea && xent->mac_addr) { + if (bcmp((u_char *)&xent->mac_addr, ea->octet, ETHER_ADDR_LEN) != 0) + xent = NULL; + } + } break; case IPFW_TABLE_INTERFACE: @@ -581,15 +864,37 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); break; +#if 0 + case IPFW_TABLE_MAC: + { + struct xaddr_mac mac; + + KEY_LEN(mac) = KEY_LEN_MAC; + mac.mac = (struct ether_addr)(*paddr); + xent = (struct table_xentry *)(rnh->rnh_lookup(&mac, NULL, rnh)); + } + break; +#endif + + case IPFW_TABLE_MIX: + { + struct xaddr_mix xm; + + KEY_LEN(xm) = KEY_LEN_MIX; + memcpy(((char *)&xm.sin_addr), paddr, sizeof(struct in_addr) + ETHER_ADDR_LEN); + xent = (struct table_xentry *)(rnh->rnh_lookup(&xm, NULL, rnh)); + } + break; + default: - return (0); + return (NULL); } if (xent != NULL) { *val = xent->value; - return (1); + return (xent); } - return (0); + return (NULL); } static int @@ -698,6 +1003,9 @@ dump_table_xentry_base(struct radix_node *rn, void *arg) /* Save IPv4 address as deprecated IPv6 compatible */ xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; xent->value = n->value; + xent->bytes = n->bytes; + xent->packets = n->packets; + xent->timestamp = n->timestamp; tbl->cnt++; return (0); } @@ -735,12 +1043,31 @@ dump_table_xentry_extended(struct radix_node *rn, void *arg) memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); break; +#if 0 + case IPFW_TABLE_MAC: + /* Assume exact mask */ + xent->masklen = 8 * ETHER_ADDR_LEN; + xent->k.mac.addr = n->a.mac.mac; + xent->k.mac.mask = n->m.mac.mac; + break; +#endif + + case IPFW_TABLE_MIX: + /* Assume exact mask */ + xent->masklen = 8 * (ETHER_ADDR_LEN + sizeof(struct in_addr)); + memcpy(&xent->k.mix.addr, &n->a.mix.sin_addr, sizeof(struct in_addr)); + memcpy(xent->k.mix.mac, &n->a.mix.mac, ETHER_ADDR_LEN); + break; + default: /* unknown, skip entry */ return (0); } xent->value = n->value; + xent->bytes = n->bytes; + xent->packets = n->packets; + xent->timestamp = n->timestamp; tbl->cnt++; return (0); }