Index: head/sys/netpfil/ipfw/ip_fw2.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw2.c +++ head/sys/netpfil/ipfw/ip_fw2.c @@ -1258,7 +1258,6 @@ * * args->m (in/out) The packet; we set to NULL when/if we nuke it. * Starts with the IP header. - * args->eh (in) Mac header if present, NULL for layer3 packet. * args->L3offset Number of bytes bypassed if we came from L2. * e.g. often sizeof(eh) ** NOTYET ** * args->ifp Incoming or outgoing interface. @@ -1297,23 +1296,19 @@ * the implementation of the various instructions to make sure * that they still work. * - * args->eh The MAC header. It is non-null for a layer2 - * packet, it is NULL for a layer-3 packet. - * **notyet** - * args->L3offset Offset in the packet to the L3 (IP or equiv.) header. - * * m | args->m Pointer to the mbuf, as received from the caller. * It may change if ipfw_chk() does an m_pullup, or if it * consumes the packet because it calls send_reject(). * XXX This has to change, so that ipfw_chk() never modifies * or consumes the buffer. - * ip is the beginning of the ip(4 or 6) header. - * Calculated by adding the L3offset to the start of data. - * (Until we start using L3offset, the packet is - * supposed to start with the ip header). + * OR + * args->mem Pointer to contigous memory chunk. + * ip Is the beginning of the ip(4 or 6) header. + * eh Ethernet header in case if input is Layer2. */ - struct mbuf *m = args->m; - struct ip *ip = mtod(m, struct ip *); + struct mbuf *m; + struct ip *ip; + struct ether_header *eh; /* * For rules which contain uid/gid or jail constraints, cache @@ -1370,7 +1365,6 @@ struct in_addr src_ip, dst_ip; /* NOTE: network format */ int iplen = 0; int pktlen; - uint16_t etype; /* Host order stored ether type */ struct ipfw_dyn_info dyn_info; struct ip_fw *q = NULL; @@ -1394,14 +1388,45 @@ int done = 0; /* flag to exit the outer loop */ IPFW_RLOCK_TRACKER; + bool mem; - if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) - return (IP_FW_PASS); /* accept */ + if ((mem = (args->flags & IPFW_ARGS_LENMASK))) { + if (args->flags & IPFW_ARGS_ETHER) { + eh = (struct ether_header *)args->mem; + if (eh->ether_type == htons(ETHERTYPE_VLAN)) + ip = (struct ip *) + ((struct ether_vlan_header *)eh + 1); + else + ip = (struct ip *)(eh + 1); + } else { + eh = NULL; + ip = (struct ip *)args->mem; + } + pktlen = IPFW_ARGS_LENGTH(args->flags); + args->f_id.fib = args->ifp->if_fib; /* best guess */ + } else { + m = args->m; + if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready)) + return (IP_FW_PASS); /* accept */ + if (args->flags & IPFW_ARGS_ETHER) { + /* We need some amount of data to be contiguous. */ + if (m->m_len < min(m->m_pkthdr.len, max_protohdr) && + (args->m = m = m_pullup(m, min(m->m_pkthdr.len, + max_protohdr))) == NULL) + goto pullup_failed; + eh = mtod(m, struct ether_header *); + ip = (struct ip *)(eh + 1); + } else { + eh = NULL; + ip = mtod(m, struct ip *); + } + pktlen = m->m_pkthdr.len; + args->f_id.fib = M_GETFIB(m); /* mbuf not altered */ + } dst_ip.s_addr = 0; /* make sure it is initialized */ src_ip.s_addr = 0; /* make sure it is initialized */ src_port = dst_port = 0; - pktlen = m->m_pkthdr.len; DYN_INFO_INIT(&dyn_info); /* @@ -1411,28 +1436,41 @@ * this way). */ #define PULLUP_TO(_len, p, T) PULLUP_LEN(_len, p, sizeof(T)) +#define EHLEN (eh != NULL ? ((char *)ip - (char *)eh) : 0) #define PULLUP_LEN(_len, p, T) \ do { \ - int x = (_len) + T; \ - if ((m)->m_len < x) { \ - args->m = m = m_pullup(m, x); \ - if (m == NULL) \ - goto pullup_failed; \ + int x = (_len) + T + EHLEN; \ + if (mem) { \ + MPASS(pktlen >= x); \ + p = (char *)args->mem + (_len) + EHLEN; \ + } else { \ + if (__predict_false((m)->m_len < x)) { \ + args->m = m = m_pullup(m, x); \ + if (m == NULL) \ + goto pullup_failed; \ + } \ + p = mtod(m, char *) + (_len) + EHLEN; \ } \ - p = (mtod(m, char *) + (_len)); \ } while (0) +/* + * In case pointers got stale after pullups, update them. + */ +#define UPDATE_POINTERS() \ +do { \ + if (!mem) { \ + if (eh != NULL) { \ + eh = mtod(m, struct ether_header *); \ + ip = (struct ip *)(eh + 1); \ + } else \ + ip = mtod(m, struct ip *); \ + args->m = m; \ + } \ +} while (0) - /* - * if we have an ether header, - */ - if (args->flags & IPFW_ARGS_ETHER) - etype = ntohs(args->eh->ether_type); - else - etype = 0; - /* Identify IP packets and fill up variables. */ if (pktlen >= sizeof(struct ip6_hdr) && - (etype == 0 || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { + (eh == NULL || eh->ether_type == htons(ETHERTYPE_IPV6)) && + ip->ip_v == 6) { struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; is_ipv6 = 1; @@ -1609,7 +1647,7 @@ break; } /*switch */ } - ip = mtod(m, struct ip *); + UPDATE_POINTERS(); ip6 = (struct ip6_hdr *)ip; args->f_id.addr_type = 6; args->f_id.src_ip6 = ip6->ip6_src; @@ -1617,7 +1655,8 @@ args->f_id.flow_id6 = ntohl(ip6->ip6_flow); iplen = ntohs(ip6->ip6_plen) + sizeof(*ip6); } else if (pktlen >= sizeof(struct ip) && - (etype == 0 || etype == ETHERTYPE_IP) && ip->ip_v == 4) { + (eh == NULL || eh->ether_type == htons(ETHERTYPE_IP)) && + ip->ip_v == 4) { is_ipv4 = 1; args->flags |= IPFW_ARGS_IP4; hlen = ip->ip_hl << 2; @@ -1675,7 +1714,7 @@ } } - ip = mtod(m, struct ip *); + UPDATE_POINTERS(); args->f_id.addr_type = 4; args->f_id.src_ip = ntohl(src_ip.s_addr); args->f_id.dst_ip = ntohl(dst_ip.s_addr); @@ -1692,7 +1731,6 @@ args->f_id.proto = proto; args->f_id.src_port = src_port = ntohs(src_port); args->f_id.dst_port = dst_port = ntohs(dst_port); - args->f_id.fib = M_GETFIB(m); IPFW_PF_RLOCK(chain); if (! V_ipfw_vnet_ready) { /* shutting down, leave NOW. */ @@ -1720,7 +1758,7 @@ oif = NULL; } else { MPASS(args->flags & IPFW_ARGS_OUT); - iif = m->m_pkthdr.rcvif; + iif = mem ? NULL : m->m_pkthdr.rcvif; oif = args->ifp; } @@ -1840,7 +1878,7 @@ ((ipfw_insn_mac *)cmd)->addr; u_int32_t *mask = (u_int32_t *) ((ipfw_insn_mac *)cmd)->mask; - u_int32_t *hdr = (u_int32_t *)args->eh; + u_int32_t *hdr = (u_int32_t *)eh; match = ( want[0] == (hdr[0] & mask[0]) && @@ -1857,8 +1895,11 @@ for (i = cmdlen - 1; !match && i>0; i--, p += 2) - match = (etype >= p[0] && - etype <= p[1]); + match = + (ntohs(eh->ether_type) >= + p[0] && + ntohs(eh->ether_type) <= + p[1]); } break; @@ -2332,7 +2373,7 @@ } case O_LOG: - ipfw_log(chain, f, hlen, args, m, + ipfw_log(chain, f, hlen, args, offset | ip6f_mf, tablearg, ip); match = 1; break; Index: head/sys/netpfil/ipfw/ip_fw_bpf.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw_bpf.c +++ head/sys/netpfil/ipfw/ip_fw_bpf.c @@ -161,6 +161,28 @@ } void +ipfw_bpf_tap(u_char *pkt, u_int pktlen) +{ + LOGIF_RLOCK_TRACKER; + + LOGIF_RLOCK(); + if (V_log_if != NULL) + BPF_TAP(V_log_if, pkt, pktlen); + LOGIF_RUNLOCK(); +} + +void +ipfw_bpf_mtap(struct mbuf *m) +{ + LOGIF_RLOCK_TRACKER; + + LOGIF_RLOCK(); + if (V_log_if != NULL) + BPF_MTAP(V_log_if, m); + LOGIF_RUNLOCK(); +} + +void ipfw_bpf_mtap2(void *data, u_int dlen, struct mbuf *m) { struct ifnet *logif; Index: head/sys/netpfil/ipfw/ip_fw_log.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw_log.c +++ head/sys/netpfil/ipfw/ip_fw_log.c @@ -99,30 +99,32 @@ */ void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, - struct ip_fw_args *args, struct mbuf *m, - u_short offset, uint32_t tablearg, struct ip *ip) + 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]; if (V_fw_verbose == 0) { - if (args->flags & IPFW_ARGS_ETHER) /* layer2, use orig hdr */ - ipfw_bpf_mtap2(args->eh, ETHER_HDR_LEN, m); + if (args->flags & IPFW_ARGS_LENMASK) + ipfw_bpf_tap(args->mem, IPFW_ARGS_LENGTH(args->flags)); + else if (args->flags & IPFW_ARGS_ETHER) + /* layer2, use orig hdr */ + ipfw_bpf_mtap(args->m); else { /* Add fake header. Later we will store * more info in the header. */ if (ip->ip_v == 4) ipfw_bpf_mtap2("DDDDDDSSSSSS\x08\x00", - ETHER_HDR_LEN, m); + ETHER_HDR_LEN, args->m); else if (ip->ip_v == 6) ipfw_bpf_mtap2("DDDDDDSSSSSS\x86\xdd", - ETHER_HDR_LEN, m); + ETHER_HDR_LEN, args->m); else /* Obviously bogus EtherType. */ ipfw_bpf_mtap2("DDDDDDSSSSSS\xff\xff", - ETHER_HDR_LEN, m); + ETHER_HDR_LEN, args->m); } return; } Index: head/sys/netpfil/ipfw/ip_fw_pfil.c =================================================================== --- head/sys/netpfil/ipfw/ip_fw_pfil.c +++ head/sys/netpfil/ipfw/ip_fw_pfil.c @@ -328,69 +328,50 @@ * ipfw processing for ethernet packets (in and out). */ static pfil_return_t -ipfw_check_frame(struct mbuf **m0, struct ifnet *ifp, int flags, +ipfw_check_frame(pfil_packet_t p, struct ifnet *ifp, int flags, void *ruleset __unused, struct inpcb *inp) { struct ip_fw_args args; - struct ether_header save_eh; - struct ether_header *eh; - struct m_tag *mtag; - struct mbuf *m; pfil_return_t ret; - int i; + bool mem, realloc; + int ipfw; - args.flags = IPFW_ARGS_ETHER; - args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT; -again: - /* fetch start point from rule, if any. remove the tag if present. */ - mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL); - if (mtag != NULL) { - args.rule = *((struct ipfw_rule_ref *)(mtag+1)); - m_tag_delete(*m0, mtag); - if (args.rule.info & IPFW_ONEPASS) - return (0); - args.flags |= IPFW_ARGS_REF; + if (flags & PFIL_MEMPTR) { + mem = true; + realloc = false; + args.flags = PFIL_LENGTH(flags) | IPFW_ARGS_ETHER; + args.mem = p.mem; + } else { + mem = realloc = false; + args.flags = IPFW_ARGS_ETHER; } - - /* I need some amt of data to be contiguous */ - m = *m0; - i = min(m->m_pkthdr.len, max_protohdr); - if (m->m_len < i) { - m = m_pullup(m, i); - if (m == NULL) { - *m0 = m; - return (0); - } - } - eh = mtod(m, struct ether_header *); - save_eh = *eh; /* save copy for restore below */ - m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */ - - args.m = m; /* the packet we are looking at */ + args.flags |= (flags & PFIL_IN) ? IPFW_ARGS_IN : IPFW_ARGS_OUT; args.ifp = ifp; - args.eh = &save_eh; /* MAC header for bridged/MAC packets */ - args.inp = inp; /* used by ipfw uid/gid/jail rules */ - i = ipfw_chk(&args); - m = args.m; - if (m != NULL) { + args.inp = inp; + +again: + if (!mem) { /* - * Restore Ethernet header, as needed, in case the - * mbuf chain was replaced by ipfw. + * Fetch start point from rule, if any. + * Remove the tag if present. */ - M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); - if (m == NULL) { - *m0 = NULL; - return (0); + struct m_tag *mtag; + + mtag = m_tag_locate(*p.m, MTAG_IPFW_RULE, 0, NULL); + if (mtag != NULL) { + args.rule = *((struct ipfw_rule_ref *)(mtag+1)); + m_tag_delete(*p.m, mtag); + if (args.rule.info & IPFW_ONEPASS) + return (PFIL_PASS); + args.flags |= IPFW_ARGS_REF; } - if (eh != mtod(m, struct ether_header *)) - bcopy(&save_eh, mtod(m, struct ether_header *), - ETHER_HDR_LEN); + args.m = *p.m; } - *m0 = m; + ipfw = ipfw_chk(&args); + ret = PFIL_PASS; - /* Check result of ipfw_chk() */ - switch (i) { + switch (ipfw) { case IP_FW_PASS: break; @@ -403,9 +384,16 @@ ret = PFIL_DROPPED; break; } - *m0 = NULL; + if (mem) { + if (pfil_realloc(&p, flags, ifp) != 0) { + ret = PFIL_DROPPED; + break; + } + mem = false; + realloc = true; + } MPASS(args.flags & IPFW_ARGS_REF); - ip_dn_io_ptr(&m, &args); + ip_dn_io_ptr(p.m, &args); return (PFIL_CONSUMED); case IP_FW_NGTEE: @@ -414,9 +402,17 @@ ret = PFIL_DROPPED; break; } + if (mem) { + if (pfil_realloc(&p, flags, ifp) != 0) { + ret = PFIL_DROPPED; + break; + } + mem = false; + realloc = true; + } MPASS(args.flags & IPFW_ARGS_REF); - (void )ng_ipfw_input_p(m0, &args, i == IP_FW_NGTEE); - if (i == IP_FW_NGTEE) /* ignore errors for NGTEE */ + (void )ng_ipfw_input_p(p.m, &args, ipfw == IP_FW_NGTEE); + if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ goto again; /* continue with packet */ ret = PFIL_CONSUMED; break; @@ -425,12 +421,15 @@ KASSERT(0, ("%s: unknown retval", __func__)); } - if (ret != PFIL_PASS) { - if (*m0) - FREE_PKT(*m0); - *m0 = NULL; + if (!mem && ret != PFIL_PASS) { + if (*p.m) + FREE_PKT(*p.m); + *p.m = NULL; } + if (realloc && ret == PFIL_PASS) + ret = PFIL_REALLOCED; + return (ret); } @@ -545,7 +544,7 @@ pfil_hook_t *h; pha.pa_version = PFIL_VERSION; - pha.pa_flags = PFIL_IN | PFIL_OUT; + pha.pa_flags = PFIL_IN | PFIL_OUT | PFIL_MEMPTR; pha.pa_modname = "ipfw"; pha.pa_ruleset = NULL; Index: head/sys/netpfil/ipfw/ip_fw_private.h =================================================================== --- head/sys/netpfil/ipfw/ip_fw_private.h +++ head/sys/netpfil/ipfw/ip_fw_private.h @@ -111,14 +111,11 @@ struct inpcb *inp; union { /* - * We don't support forwarding on layer2, thus we can - * keep eh pointer in this union. * next_hop[6] pointers can be used to point to next hop * stored in rule's opcode to avoid copying into hopstore. * Also, it is expected that all 0x1-0x10 flags are mutually * exclusive. */ - struct ether_header *eh; /* for bridged packets */ struct sockaddr_in *next_hop; struct sockaddr_in6 *next_hop6; /* ipfw next hop storage */ @@ -129,8 +126,10 @@ uint16_t sin6_port; } hopstore6; }; - - struct mbuf *m; /* the mbuf chain */ + union { + struct mbuf *m; /* the mbuf chain */ + void *mem; /* or memory pointer */ + }; struct ipfw_flow_id f_id; /* grabbed from IP header */ }; @@ -164,10 +163,11 @@ void ipfw_bpf_init(int); void ipfw_bpf_uninit(int); +void ipfw_bpf_tap(u_char *, u_int); +void ipfw_bpf_mtap(struct mbuf *); void ipfw_bpf_mtap2(void *, u_int, struct mbuf *); void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, - struct ip_fw_args *args, struct mbuf *m, - u_short offset, uint32_t tablearg, struct ip *ip); + struct ip_fw_args *args, u_short offset, uint32_t tablearg, struct ip *ip); VNET_DECLARE(u_int64_t, norule_counter); #define V_norule_counter VNET(norule_counter) VNET_DECLARE(int, verbose_limit);