Index: ip_fw.h =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/netinet/ip_fw.h,v retrieving revision 1.108 diff -u -r1.108 ip_fw.h --- ip_fw.h 18 Aug 2006 22:36:04 -0000 1.108 +++ ip_fw.h 15 Dec 2006 02:34:39 -0000 @@ -515,7 +515,7 @@ struct sockaddr_in *next_hop; /* forward address */ struct ip_fw *rule; /* matching rule */ struct ether_header *eh; /* for bridged packets */ - + int vlan_hlen; /* length of vlan header to skip */ struct ipfw_flow_id f_id; /* grabbed from IP header */ u_int32_t cookie; /* a cookie depending on rule action */ struct inpcb *inp; Index: ip_fw2.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/netinet/ip_fw2.c,v retrieving revision 1.155 diff -u -r1.155 ip_fw2.c --- ip_fw2.c 12 Dec 2006 12:17:56 -0000 1.155 +++ ip_fw2.c 15 Dec 2006 02:34:39 -0000 @@ -169,6 +169,7 @@ }; static int fw_debug = 1; +static int fw_stripvlans = 1; static int autoinc_step = 100; /* bounded to 1..1000 in add_rule() */ extern int ipfw_chg_hook(SYSCTL_HANDLER_ARGS); @@ -191,6 +192,8 @@ &fw_verbose, 0, "Log matches to ipfw rules"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &verbose_limit, 0, "Set upper limit of matches of ipfw rules logged"); +SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, stripvlan, CTLFLAG_RW, + &fw_stripvlans, 0, "strip off vlan headers if working at layer 2"); /* * Description of dynamic rules. @@ -579,7 +582,7 @@ struct in6_ifaddr *fdm; struct in6_addr copia; - TAILQ_FOREACH(mdc, &ifnet, if_link) + TAILQ_FOREACH(mdc, &ifnet, if_link) TAILQ_FOREACH(mdc2, &mdc->if_addrlist, ifa_list) { if (mdc2->ifa_addr->sa_family == AF_INET6) { fdm = (struct in6_ifaddr *)mdc2; @@ -667,10 +670,12 @@ } static void -send_reject6(struct ip_fw_args *args, int code, u_int hlen) +send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6) { + struct mbuf *m; + + m = args->m; if (code == ICMP6_UNREACH_RST && args->f_id.proto == IPPROTO_TCP) { - struct ip6_hdr *ip6; struct tcphdr *tcp; tcp_seq ack, seq; int flags; @@ -678,18 +683,11 @@ struct ip6_hdr ip6; struct tcphdr th; } ti; - - if (args->m->m_len < (hlen+sizeof(struct tcphdr))) { - args->m = m_pullup(args->m, hlen+sizeof(struct tcphdr)); - if (args->m == NULL) - return; - } - - ip6 = mtod(args->m, struct ip6_hdr *); - tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); + tcp = (struct tcphdr *)((char *)ip6 + hlen); if ((tcp->th_flags & TH_RST) != 0) { - m_freem(args->m); + m_freem(m); + args->m = NULL; return; } @@ -705,14 +703,21 @@ flags = TH_RST; } else { ack = ti.th.th_seq; - if (((args->m)->m_flags & M_PKTHDR) != 0) { - ack += (args->m)->m_pkthdr.len - hlen + if ((m->m_flags & M_PKTHDR) != 0) { + /* + * total new data to ACK is: + * total packet length, + * minus the header length, + * minus the tcp header length. + */ + ack += m->m_pkthdr.len - args->vlan_hlen - hlen - (ti.th.th_off << 2); } else if (ip6->ip6_plen) { - ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) - - hlen - (ti.th.th_off << 2); + ack += ntohs(ip6->ip6_plen) + + sizeof(*ip6) - + hlen - (ti.th.th_off << 2); } else { - m_freem(args->m); + m_freem(m); return; } if (tcp->th_flags & TH_SYN) @@ -721,14 +726,20 @@ flags = TH_RST|TH_ACK; } bcopy(&ti, ip6, sizeof(ti)); - tcp_respond(NULL, ip6, (struct tcphdr *)(ip6 + 1), - args->m, ack, seq, flags); + /* m only used to recycle the mbuf */ + tcp_respond(NULL, ip6, tcp, m, ack, seq, flags); } else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */ - icmp6_error(args->m, ICMP6_DST_UNREACH, code, 0); - + /* + * m is interpreted as if the ip6 header is the + * first thing in it, so skip any vlan header. + * m is consumed so we can modify it. + */ + if (args->vlan_hlen) + m_adj(m, args->vlan_hlen); + icmp6_error(m, ICMP6_DST_UNREACH, code, 0); } else - m_freem(args->m); + m_freem(m); args->m = NULL; } @@ -746,7 +757,8 @@ */ static void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args, - struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg) + struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg, + char *baseaddr) { struct ether_header *eh = args->eh; char *action; @@ -892,13 +904,13 @@ snprintf(dst, sizeof(dst), "[%s]", ip6_sprintf(ip6buf, &args->f_id.dst_ip6)); - ip6 = (struct ip6_hdr *)mtod(m, struct ip6_hdr *); - tcp = (struct tcphdr *)(mtod(args->m, char *) + hlen); - udp = (struct udphdr *)(mtod(args->m, char *) + hlen); + ip6 = (struct ip6_hdr *)baseaddr; + tcp = (struct tcphdr *)(baseaddr + hlen); + udp = (struct udphdr *)(baseaddr + hlen); } else #endif { - ip = mtod(m, struct ip *); + ip = (struct ip *)baseaddr; tcp = L3HDR(struct tcphdr, ip); udp = L3HDR(struct udphdr, ip); @@ -942,7 +954,7 @@ break; #ifdef INET6 case IPPROTO_ICMPV6: - icmp6 = (struct icmp6_hdr *)(mtod(args->m, char *) + hlen); + icmp6 = (struct icmp6_hdr *)(baseaddr + hlen); if (offset == 0) len = snprintf(SNPARGS(proto, 0), "ICMPv6:%u.%u ", @@ -1679,6 +1691,11 @@ if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */ /* We need the IP header in host order for icmp_error(). */ if (args->eh != NULL) { + /* if we had a vlan header, skip past it */ + if (args->vlan_hlen) { + m_adj(args->m, args->vlan_hlen); + args->vlan_hlen = 0; + } struct ip *ip = mtod(args->m, struct ip *); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); @@ -2039,6 +2056,7 @@ * 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, or NULL for layer3 packet. + * args->vlan_hlen Vlan header lenght bypassed. * args->oif Outgoing interface, or NULL if packet is incoming. * The incoming interface is in the mbuf. (in) * args->divert_rule (in/out) @@ -2060,6 +2078,10 @@ * IP_FW_NETGRAPH into netgraph, cookie args->cookie * */ +struct vlhdr { + u_int16_t evl_tag; + u_int16_t evl_proto; +}; int ipfw_chk(struct ip_fw_args *args) @@ -2154,6 +2176,9 @@ struct in_addr src_ip, dst_ip; /* NOTE: network format */ u_int16_t ip_len=0; int pktlen; + u_int16_t etype = 0; /* Host order stored ether type */ + int vltag; /* Host order stored vlan tag */ + /* -1 == no tag */ /* * dyn_dir = MATCH_UNKNOWN when rules unchecked, @@ -2202,14 +2227,38 @@ p = (mtod(m, char *) + (len)); \ } while (0) + /* + * if we have an ether header, + * then check if we also have a vlan header. + */ + if (args->eh && + (etype = (ntohs(args->eh->ether_type)) == ETHERTYPE_VLAN) && + fw_stripvlans) { + struct vlhdr *vlt; + if (pktlen >= sizeof(struct vlhdr)) { + PULLUP_TO(0, ulp, struct vlhdr); + } else { + return IP_FW_DENY; + } + vlt = (struct vlhdr *)ulp; + etype = ntohs(vlt->evl_proto); + vltag = ntohs(vlt->evl_tag); + args->vlan_hlen = 4; + ip = (struct ip *)((char *)ulp + args->vlan_hlen); + } else { + args->vlan_hlen = 0; + vltag = -1; + ip = (struct ip *)mtod(m, char *); + } + /* Identify IP packets and fill up variables. */ if (pktlen >= sizeof(struct ip6_hdr) && - (args->eh == NULL || ntohs(args->eh->ether_type)==ETHERTYPE_IPV6) && - mtod(m, struct ip *)->ip_v == 6) { + (args->eh == NULL || 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 = mtod(m, struct ip6_hdr *)->ip6_nxt; + hlen = args->vlan_hlen + sizeof(struct ip6_hdr); + proto = ip6->ip6_nxt; /* Search extension headers to find upper layer protocols */ while (ulp == NULL) { @@ -2354,17 +2403,17 @@ break; } /*switch */ } - args->f_id.src_ip6 = mtod(m,struct ip6_hdr *)->ip6_src; - args->f_id.dst_ip6 = mtod(m,struct ip6_hdr *)->ip6_dst; + ip = (struct ip *)(mtod(m, char *) + args->vlan_hlen); + ip6 = (struct ip6_hdr *)ip; + 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(mtod(m, struct ip6_hdr *)->ip6_flow); + args->f_id.flow_id6 = ntohl(ip6->ip6_flow); } else if (pktlen >= sizeof(struct ip) && - (args->eh == NULL || ntohs(args->eh->ether_type) == ETHERTYPE_IP) && - mtod(m, struct ip *)->ip_v == 4) { + (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) { is_ipv4 = 1; - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2; + hlen = args->vlan_hlen + (ip->ip_hl << 2); args->f_id.addr_type = 4; /* @@ -2407,9 +2456,11 @@ } } + ip = (struct ip *)(mtod(m, char *) + args->vlan_hlen); args->f_id.src_ip = ntohl(src_ip.s_addr); args->f_id.dst_ip = ntohl(dst_ip.s_addr); } + hlen -= args->vlan_hlen; #undef PULLUP_TO if (proto) { /* we may have port numbers, store them */ args->f_id.proto = proto; @@ -2573,8 +2624,7 @@ case O_MAC_TYPE: if (args->eh != NULL) { - u_int16_t t = - ntohs(args->eh->ether_type); + u_int16_t t = etype; u_int16_t *p = ((ipfw_insn_u16 *)cmd)->ports; int i; @@ -2733,12 +2783,12 @@ case O_IPOPT: match = (is_ipv4 && - ipopts_match(mtod(m, struct ip *), cmd) ); + ipopts_match(ip, cmd) ); break; case O_IPVER: match = (is_ipv4 && - cmd->arg1 == mtod(m, struct ip *)->ip_v); + cmd->arg1 == ip->ip_v); break; case O_IPID: @@ -2752,9 +2802,9 @@ if (cmd->opcode == O_IPLEN) x = ip_len; else if (cmd->opcode == O_IPTTL) - x = mtod(m, struct ip *)->ip_ttl; + x = ip->ip_ttl; else /* must be IPID */ - x = ntohs(mtod(m, struct ip *)->ip_id); + x = ntohs(ip->ip_id); if (cmdlen == 1) { match = (cmd->arg1 == x); break; @@ -2769,12 +2819,12 @@ case O_IPPRECEDENCE: match = (is_ipv4 && - (cmd->arg1 == (mtod(m, struct ip *)->ip_tos & 0xe0)) ); + (cmd->arg1 == (ip->ip_tos & 0xe0)) ); break; case O_IPTOS: match = (is_ipv4 && - flags_match(cmd, mtod(m, struct ip *)->ip_tos)); + flags_match(cmd, ip->ip_tos)); break; case O_TCPDATALEN: @@ -2866,7 +2916,7 @@ case O_LOG: if (fw_verbose) ipfw_log(f, hlen, args, m, - oif, offset, tablearg); + oif, offset, tablearg, (char *)ip); match = 1; break; @@ -3204,6 +3254,8 @@ is_icmp_query(ICMP(ulp))) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN_MULTICAST(ntohl(dst_ip.s_addr))) { + if (args->vlan_hlen) + m_adj(m, args->vlan_hlen); send_reject(args, cmd->arg1, ip_len); m = args->m; } @@ -3216,7 +3268,10 @@ (is_icmp6_query(args->f_id.flags) == 1)) && !(m->m_flags & (M_BCAST|M_MCAST)) && !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) { - send_reject6(args, cmd->arg1, hlen); + if (args->vlan_hlen) + m_adj(m, args->vlan_hlen); + send_reject6( + args, cmd->arg1, hlen, (struct ip6_hdr *)ip); m = args->m; } /* FALLTHROUGH */