diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 9f1ba49d780..a11a2b7c458 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -1191,6 +1191,7 @@ set_match(struct ip_fw_args *args, int slot, args->rule.slot = slot + 1; /* we use 0 as a marker */ args->rule.rule_id = 1 + chain->map[slot]->id; args->rule.rulenum = chain->map[slot]->rulenum; + args->flags |= IPFW_ARGS_REF; } #ifndef LINEAR_SKIPTO @@ -1269,10 +1270,10 @@ jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num, * upon return, non-zero port number for divert or tee. * * args->rule Pointer to the last matching rule (in/out) - * args->next_hop Socket we are forwarding to (out). - * args->next_hop6 IPv6 next hop we are forwarding to (out). + * args->next_hop Socket we are forwarding to (out). + * args->next_hop6 IPv6 next hop we are forwarding to (out). * args->f_id Addresses grabbed from the packet (out) - * args->rule.info a cookie depending on rule action + * args->rule.info a cookie depending on rule action * * Return value: * @@ -1377,17 +1378,12 @@ ipfw_chk(struct ip_fw_args *args) * Only valid for IPv4 packets. */ uint8_t proto; - uint16_t src_port = 0, dst_port = 0; /* NOTE: host format */ + uint16_t src_port, dst_port; /* NOTE: host format */ struct in_addr src_ip, dst_ip; /* NOTE: network format */ int iplen = 0; int pktlen; - uint16_t etype = 0; /* Host order stored ether type */ + uint16_t etype; /* Host order stored ether type */ - /* - * dyn_dir = MATCH_UNKNOWN when rules unchecked, - * MATCH_NONE when checked and not matched (q = NULL), - * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) - */ struct ipfw_dyn_info dyn_info; struct ip_fw *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; @@ -1416,9 +1412,6 @@ ipfw_chk(struct ip_fw_args *args) dst_ip.s_addr = 0; /* make sure it is initialized */ src_ip.s_addr = 0; /* make sure it is initialized */ pktlen = m->m_pkthdr.len; - args->f_id.fib = M_GETFIB(m); /* note mbuf not altered) */ - proto = args->f_id.proto = 0; /* mark f_id invalid */ - /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */ DYN_INFO_INIT(&dyn_info); /* @@ -1442,18 +1435,19 @@ do { \ /* * if we have an ether header, */ - if (args->eh) + 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) && - (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { + (etype == 0 || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) { struct ip6_hdr *ip6 = (struct ip6_hdr *)ip; + is_ipv6 = 1; - args->f_id.addr_type = 6; hlen = sizeof(struct ip6_hdr); proto = ip6->ip6_nxt; - /* Search extension headers to find upper layer protocols */ while (ulp == NULL && offset == 0) { switch (proto) { @@ -1628,20 +1622,18 @@ do { \ } ip = mtod(m, struct ip *); ip6 = (struct ip6_hdr *)ip; + args->f_id.addr_type = 6; args->f_id.src_ip6 = ip6->ip6_src; args->f_id.dst_ip6 = ip6->ip6_dst; - args->f_id.src_ip = 0; - args->f_id.dst_ip = 0; args->f_id.flow_id6 = ntohl(ip6->ip6_flow); iplen = ntohs(ip6->ip6_plen) + sizeof(*ip6); } else if (pktlen >= sizeof(struct ip) && - (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) { - is_ipv4 = 1; + (etype == 0 || etype == ETHERTYPE_IP) && ip->ip_v == 4) { + is_ipv4 = 1; hlen = ip->ip_hl << 2; - args->f_id.addr_type = 4; - /* - * Collect parameters into local variables for faster matching. + * Collect parameters into local variables for faster + * matching. */ proto = ip->ip_p; src_ip = ip->ip_src; @@ -1694,23 +1686,31 @@ do { \ } ip = mtod(m, struct ip *); + 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); + } else { + proto = 0; + src_port = dst_port = 0; + dst_ip.s_addr = src_ip.s_addr = 0; + + args->f_id.addr_type = 1; /* XXX */ } #undef PULLUP_TO pktlen = iplen < pktlen ? iplen: pktlen; - if (proto) { /* we may have port numbers, store them */ - 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); - } + + /* Properly initialize the reset of f_id */ + 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. */ IPFW_PF_RUNLOCK(chain); return (IP_FW_PASS); /* accept */ } - if (args->rule.slot) { + if (args->flags & IPFW_ARGS_REF) { /* * Packet has already been tagged as a result of a previous * match on rule args->rule aka args->rule_id (PIPE, QUEUE, @@ -1839,7 +1839,7 @@ do { \ break; case O_MACADDR2: - if (args->eh != NULL) { /* have MAC header */ + if (args->flags & IPFW_ARGS_ETHER) { u_int32_t *want = (u_int32_t *) ((ipfw_insn_mac *)cmd)->addr; u_int32_t *mask = (u_int32_t *) @@ -1854,7 +1854,7 @@ do { \ break; case O_MAC_TYPE: - if (args->eh != NULL) { + if (args->flags & IPFW_ARGS_ETHER) { u_int16_t *p = ((ipfw_insn_u16 *)cmd)->ports; int i; @@ -1875,19 +1875,21 @@ do { \ break; case O_LAYER2: - match = (args->eh != NULL); + match = (args->flags & IPFW_ARGS_ETHER); break; case O_DIVERTED: - { - /* For diverted packets, args->rule.info + if ((args->flags & IPFW_ARGS_REF) == 0) + break; + /* + * For diverted packets, args->rule.info * contains the divert port (in host format) * reason and direction. */ - uint32_t i = args->rule.info; - match = (i&IPFW_IS_MASK) == IPFW_IS_DIVERT && - cmd->arg1 & ((i & IPFW_INFO_IN) ? 1 : 2); - } + match = ((args->rule.info & IPFW_IS_MASK) == + IPFW_IS_DIVERT) && ( + ((args->rule.info & IPFW_INFO_IN) ? + 1: 2) & cmd->arg1); break; case O_PROTO: @@ -2062,7 +2064,8 @@ do { \ #ifdef INET6 /* FALLTHROUGH */ case O_IP6_SRC_ME: - match= is_ipv6 && ipfw_localip6(&args->f_id.src_ip6); + match = is_ipv6 && + ipfw_localip6(&args->f_id.src_ip6); #endif break; @@ -2101,7 +2104,8 @@ do { \ #ifdef INET6 /* FALLTHROUGH */ case O_IP6_DST_ME: - match= is_ipv6 && ipfw_localip6(&args->f_id.dst_ip6); + match = is_ipv6 && + ipfw_localip6(&args->f_id.dst_ip6); #endif break; @@ -2699,8 +2703,8 @@ do { \ case O_DIVERT: case O_TEE: - if (args->eh) /* not on layer 2 */ - break; + if (args->flags & IPFW_ARGS_ETHER) + break; /* not on layer 2 */ /* otherwise this is terminal */ l = 0; /* exit inner loop */ done = 1; /* exit outer loop */ @@ -2876,8 +2880,8 @@ do { \ break; case O_FORWARD_IP: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (args->flags & IPFW_ARGS_ETHER) + break; /* not valid on layer2 pkts */ if (q != f || dyn_info.direction == MATCH_FORWARD) { struct sockaddr_in *sa; @@ -2896,8 +2900,8 @@ do { \ if (is_ipv6) { struct sockaddr_in6 *sa6; - sa6 = args->next_hop6 = - &args->hopstore6; + args->flags |= IPFW_ARGS_NH6; + sa6 = &args->hopstore6; sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(*sa6); sa6->sin6_addr = TARG_VAL( @@ -2916,10 +2920,10 @@ do { \ } else #endif { + args->flags |= IPFW_ARGS_NH4; args->hopstore.sin_port = sa->sin_port; - sa = args->next_hop = - &args->hopstore; + sa = &args->hopstore; sa->sin_family = AF_INET; sa->sin_len = sizeof(*sa); sa->sin_addr.s_addr = htonl( @@ -2927,7 +2931,8 @@ do { \ nh4)); } } else { - args->next_hop = sa; + args->flags |= IPFW_ARGS_NH4PTR; + args->next_hop = sa; } } retval = IP_FW_PASS; @@ -2937,13 +2942,14 @@ do { \ #ifdef INET6 case O_FORWARD_IP6: - if (args->eh) /* not valid on layer2 pkts */ - break; + if (args->flags & IPFW_ARGS_ETHER) + break; /* not valid on layer2 pkts */ if (q != f || dyn_info.direction == MATCH_FORWARD) { struct sockaddr_in6 *sin6; sin6 = &(((ipfw_insn_sa6 *)cmd)->sa); + args->flags |= IPFW_ARGS_NH6PTR; args->next_hop6 = sin6; } retval = IP_FW_PASS; @@ -2972,7 +2978,7 @@ do { \ if (fib >= rt_numfibs) fib = 0; M_SETFIB(m, fib); - args->f_id.fib = fib; + args->f_id.fib = fib; /* XXX */ l = 0; /* exit inner loop */ break; } @@ -3019,6 +3025,7 @@ do { \ struct cfg_nat *t; int nat_id; + args->rule.info = 0; set_match(args, f_pos, chain); /* Check if this is 'global' nat rule */ if (cmd->arg1 == IP_FW_NAT44_GLOBAL) { @@ -3071,6 +3078,7 @@ do { \ else ip->ip_sum = in_cksum(m, hlen); retval = IP_FW_REASS; + args->rule.info = 0; set_match(args, f_pos, chain); } done = 1; /* exit outer loop */ diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c index f7b30aa927c..41899adffd9 100644 --- a/sys/netpfil/ipfw/ip_fw_log.c +++ b/sys/netpfil/ipfw/ip_fw_log.c @@ -107,7 +107,7 @@ ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen, char action2[92], proto[128], fragment[32]; if (V_fw_verbose == 0) { - if (args->eh) /* layer2, use orig hdr */ + if (args->flags & IPFW_ARGS_ETHER) /* layer2, use orig hdr */ ipfw_bpf_mtap2(args->eh, ETHER_HDR_LEN, m); else { /* Add fake header. Later we will store diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index 4316526df80..3665da3ddb3 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -124,13 +124,12 @@ ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, { struct ip_fw_args args; struct m_tag *tag; - int ipfw; - int ret; + void *psa; + int ipfw, ret; /* convert dir to IPFW values */ dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT; - bzero(&args, sizeof(args)); - + args.flags = 0; again: /* * extract and remove the tag if present. If we are left @@ -142,6 +141,7 @@ again: m_tag_delete(*m0, tag); if (args.rule.info & IPFW_ONEPASS) return (0); + args.flags |= IPFW_ARGS_REF; } args.m = *m0; @@ -155,77 +155,77 @@ again: __func__)); /* breaking out of the switch means drop */ - ret = 0; /* default return value for pass */ switch (ipfw) { case IP_FW_PASS: /* next_hop may be set by ipfw_chk */ - if (args.next_hop == NULL && args.next_hop6 == NULL) - break; /* pass */ -#if (!defined(INET6) && !defined(INET)) - ret = EACCES; -#else - { - struct m_tag *fwd_tag; - size_t len; - - KASSERT(args.next_hop == NULL || args.next_hop6 == NULL, - ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__, - args.next_hop, args.next_hop6)); -#ifdef INET6 - if (args.next_hop6 != NULL) - len = sizeof(struct sockaddr_in6); -#endif + if ((args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR | + IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) == 0) { + ret = 0; + break; + } #ifdef INET - if (args.next_hop != NULL) - len = sizeof(struct sockaddr_in); -#endif - - /* Incoming packets should not be tagged so we do not - * m_tag_find. Outgoing packets may be tagged, so we - * reuse the tag if present. - */ - fwd_tag = (dir == DIR_IN) ? NULL : - m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); - if (fwd_tag != NULL) { - m_tag_unlink(*m0, fwd_tag); - } else { - fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len, - M_NOWAIT); - if (fwd_tag == NULL) { - ret = EACCES; - break; /* i.e. drop */ - } + if (args.flags & (IPFW_ARGS_NH4 | IPFW_ARGS_NH4PTR)) { + MPASS((args.flags & (IPFW_ARGS_NH4 | + IPFW_ARGS_NH4PTR)) != (IPFW_ARGS_NH4 | + IPFW_ARGS_NH4PTR)); + MPASS((args.flags & (IPFW_ARGS_NH6 | + IPFW_ARGS_NH6PTR)) == 0); + ret = sizeof(struct sockaddr_in); + psa = ((args.flags & IPFW_ARGS_NH4) ? + &args.hopstore : args.next_hop); + if (in_localip(satosin(psa)->sin_addr)) + (*m0)->m_flags |= M_FASTFWD_OURS; + (*m0)->m_flags |= M_IP_NEXTHOP; } +#endif #ifdef INET6 - if (args.next_hop6 != NULL) { - struct sockaddr_in6 *sa6; - - sa6 = (struct sockaddr_in6 *)(fwd_tag + 1); - bcopy(args.next_hop6, sa6, len); + if (args.flags & (IPFW_ARGS_NH6 | IPFW_ARGS_NH6PTR)) { + MPASS((args.flags & (IPFW_ARGS_NH6 | + IPFW_ARGS_NH6PTR)) != (IPFW_ARGS_NH6 | + IPFW_ARGS_NH6PTR)); + MPASS((args.flags & (IPFW_ARGS_NH4 | + IPFW_ARGS_NH4PTR)) == 0); + ret = sizeof(struct sockaddr_in6); + psa = (args.flags & IPFW_ARGS_NH6) ? + &args.hopstore6 : args.next_hop6; /* * If nh6 address is link-local we should convert * it to kernel internal form before doing any * comparisons. */ - if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) { + if (sa6_embedscope(satosin6(psa), + V_ip6_use_defzone) != 0) { ret = EACCES; break; } - if (in6_localip(&sa6->sin6_addr)) + if (in6_localip(&satosin6(psa)->sin6_addr)) (*m0)->m_flags |= M_FASTFWD_OURS; (*m0)->m_flags |= M_IP6_NEXTHOP; } #endif -#ifdef INET - if (args.next_hop != NULL) { - bcopy(args.next_hop, (fwd_tag+1), len); - if (in_localip(args.next_hop->sin_addr)) - (*m0)->m_flags |= M_FASTFWD_OURS; - (*m0)->m_flags |= M_IP_NEXTHOP; +#if (!defined(INET6) && !defined(INET)) + ret = EACCES; +#else + /* + * Incoming packets should not be tagged so we do not + * m_tag_find. Outgoing packets may be tagged, so we + * reuse the tag if present. + */ + tag = (dir == DIR_IN) ? NULL : + m_tag_find(*m0, PACKET_TAG_IPFORWARD, NULL); + if (tag != NULL) { + m_tag_unlink(*m0, tag); + } else { + tag = m_tag_get(PACKET_TAG_IPFORWARD, ret, + M_NOWAIT); + if (tag == NULL) { + ret = EACCES; + break; /* i.e. drop */ + } } -#endif - m_tag_prepend(*m0, fwd_tag); - } + bcopy(psa, tag + 1, ret); + m_tag_prepend(*m0, tag); + ret = 0; #endif /* INET || INET6 */ break; @@ -237,6 +237,7 @@ again: ret = EACCES; if (ip_dn_io_ptr == NULL) break; /* i.e. drop */ + MPASS(args->flags & IPFW_ARGS_REF); if (mtod(*m0, struct ip *)->ip_v == 4) ret = ip_dn_io_ptr(m0, dir, &args); else if (mtod(*m0, struct ip *)->ip_v == 6) @@ -260,6 +261,7 @@ again: ret = EACCES; break; /* i.e. drop */ } + MPASS(args->flags & IPFW_ARGS_REF); ret = ipfw_divert(m0, dir, &args.rule, (ipfw == IP_FW_TEE) ? 1 : 0); /* continue processing for the original packet (tee). */ @@ -273,6 +275,7 @@ again: ret = EACCES; break; /* i.e. drop */ } + MPASS(args->flags & IPFW_ARGS_REF); ret = ng_ipfw_input_p(m0, dir, &args, (ipfw == IP_FW_NGTEE) ? 1 : 0); if (ipfw == IP_FW_NGTEE) /* ignore errors for NGTEE */ @@ -281,13 +284,15 @@ again: case IP_FW_NAT: /* honor one-pass in case of successful nat */ - if (V_fw_one_pass) - break; /* ret is already 0 */ + if (V_fw_one_pass) { + ret = 0; + break; + } goto again; case IP_FW_REASS: goto again; /* continue with packet */ - + default: KASSERT(0, ("%s: unknown retval", __func__)); } @@ -298,7 +303,7 @@ again: *m0 = NULL; } - return ret; + return (ret); } /* @@ -308,22 +313,22 @@ int ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, struct inpcb *inp) { - struct ether_header *eh; + struct ip_fw_args args; struct ether_header save_eh; + struct ether_header *eh; struct mbuf *m; - int i, ret; - struct ip_fw_args args; struct m_tag *mtag; + int i, ret; + args.flags = IPFW_ARGS_ETHER; /* 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.slot = 0; - } else { + 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; } /* I need some amt of data to be contiguous */ @@ -342,10 +347,8 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, args.m = m; /* the packet we are looking at */ args.oif = dir == PFIL_OUT ? ifp: NULL; /* 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.inp = inp; /* used by ipfw uid/gid/jail rules */ i = ipfw_chk(&args); m = args.m; if (m != NULL) { diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index 0d276cbab9c..d348903cc26 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -81,11 +81,36 @@ struct _ip6dn_args { * efficient to pass variables around and extend the interface. */ struct ip_fw_args { - struct mbuf *m; /* the mbuf chain */ - struct ifnet *oif; /* output interface */ - struct sockaddr_in *next_hop; /* forward address */ - struct sockaddr_in6 *next_hop6; /* ipv6 forward address */ - + struct mbuf *m; /* the mbuf chain */ + struct ifnet *oif; /* output interface */ + + uint32_t flags; +#define IPFW_ARGS_ETHER 0x0001 /* has valid ethernet header */ +#define IPFW_ARGS_NH4 0x0002 /* has IPv4 next hop in hopstore */ +#define IPFW_ARGS_NH6 0x0004 /* has IPv6 next hop in hopstore */ +#define IPFW_ARGS_NH4PTR 0x0008 /* has IPv4 next hop in next_hop */ +#define IPFW_ARGS_NH6PTR 0x0010 /* has IPv6 next hop in next_hop6 */ +#define IPFW_ARGS_REF 0x0020 /* has valid ipfw_rule_ref */ + struct ipfw_flow_id f_id; /* grabbed from IP header */ + + 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 */ + struct sockaddr_in hopstore; + struct sockaddr_in6 hopstore6; + }; /* * On return, it points to the matching rule. * On entry, rule.slot > 0 means the info is valid and @@ -93,19 +118,7 @@ struct ip_fw_args { * If chain_id == chain->id && slot >0 then jump to that slot. * Otherwise, we locate the first rule >= rulenum:rule_id */ - struct ipfw_rule_ref rule; /* match/restart info */ - - struct ether_header *eh; /* for bridged packets */ - - struct ipfw_flow_id f_id; /* grabbed from IP header */ - //uint32_t cookie; /* a cookie depending on rule action */ - struct inpcb *inp; - - struct _ip6dn_args dummypar; /* dummynet->ip6_output */ - union { /* store here if cannot use a pointer */ - struct sockaddr_in hopstore; - struct sockaddr_in6 hopstore6; - }; + struct ipfw_rule_ref rule; /* match/restart info */ }; MALLOC_DECLARE(M_IPFW);