+ if (mbuf_pkthdr_len(m) < minlen) {
+ BRIDGE_HF_DROP(brhf_arp_too_small, __func__, __LINE__);
+ goto done;
+ }
+ if (mbuf_len(m) < minlen && mbuf_pullup(data, minlen) != 0) {
+ BRIDGE_HF_DROP(brhf_arp_pullup_failed,
+ __func__, __LINE__);
+ goto done;
+ }
+ m = *data;
+
+ /*
+ * Verify this is an ethernet/ip arp
+ */
+ eh = mtod(m, struct ether_header *);
+ ea = (struct ether_arp *)(eh + 1);
+ if (ea->arp_hrd != htons(ARPHRD_ETHER)) {
+ BRIDGE_HF_DROP(brhf_arp_bad_hw_type,
+ __func__, __LINE__);
+ goto done;
+ }
+ if (ea->arp_pro != htons(ETHERTYPE_IP)) {
+ BRIDGE_HF_DROP(brhf_arp_bad_pro_type,
+ __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Verify the address lengths are correct
+ */
+ if (ea->arp_hln != ETHER_ADDR_LEN) {
+ BRIDGE_HF_DROP(brhf_arp_bad_hw_len, __func__, __LINE__);
+ goto done;
+ }
+ if (ea->arp_pln != sizeof(struct in_addr)) {
+ BRIDGE_HF_DROP(brhf_arp_bad_pro_len,
+ __func__, __LINE__);
+ goto done;
+ }
+
+ /*
+ * Allow only ARP request or ARP reply
+ */
+ if (ea->arp_op != htons(ARPOP_REQUEST) &&
+ ea->arp_op != htons(ARPOP_REPLY)) {
+ BRIDGE_HF_DROP(brhf_arp_bad_op, __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Verify source hardware address matches
+ */
+ if (bcmp(ea->arp_sha, bif->bif_hf_hwsrc,
+ ETHER_ADDR_LEN) != 0) {
+ BRIDGE_HF_DROP(brhf_arp_bad_sha, __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Verify source protocol address:
+ * May be null for an ARP probe
+ */
+ if (bcmp(ea->arp_spa, &bif->bif_hf_ipsrc.s_addr,
+ sizeof(struct in_addr)) != 0 &&
+ bcmp(ea->arp_spa, &inaddr_any,
+ sizeof(struct in_addr)) != 0) {
+ BRIDGE_HF_DROP(brhf_arp_bad_spa, __func__, __LINE__);
+ goto done;
+ }
+ bridge_hostfilter_stats.brhf_arp_ok += 1;
+ error = 0;
+ } else if (eh->ether_type == htons(ETHERTYPE_IP)) {
+ size_t minlen = sizeof(struct ether_header) + sizeof(struct ip);
+ struct ip iphdr;
+ size_t offset;
+
+ /*
+ * Make the Ethernet and IP headers contiguous
+ */
+ if (mbuf_pkthdr_len(m) < minlen) {
+ BRIDGE_HF_DROP(brhf_ip_too_small, __func__, __LINE__);
+ goto done;
+ }
+ offset = sizeof(struct ether_header);
+ error = mbuf_copydata(m, offset, sizeof(struct ip), &iphdr);
+ if (error != 0) {
+ BRIDGE_HF_DROP(brhf_ip_too_small, __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Verify the source IP address
+ */
+ if (iphdr.ip_p == IPPROTO_UDP) {
+ struct udphdr udp;
+
+ minlen += sizeof(struct udphdr);
+ if (mbuf_pkthdr_len(m) < minlen) {
+ BRIDGE_HF_DROP(brhf_ip_too_small,
+ __func__, __LINE__);
+ goto done;
+ }
+
+ /*
+ * Allow all zero addresses for DHCP requests
+ */
+ if (iphdr.ip_src.s_addr != bif->bif_hf_ipsrc.s_addr &&
+ iphdr.ip_src.s_addr != INADDR_ANY) {
+ BRIDGE_HF_DROP(brhf_ip_bad_srcaddr,
+ __func__, __LINE__);
+ goto done;
+ }
+ offset = sizeof(struct ether_header) +
+ (IP_VHL_HL(iphdr.ip_vhl) << 2);
+ error = mbuf_copydata(m, offset,
+ sizeof(struct udphdr), &udp);
+ if (error != 0) {
+ BRIDGE_HF_DROP(brhf_ip_too_small,
+ __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Either it's a Bootp/DHCP packet that we like or
+ * it's a UDP packet from the host IP as source address
+ */
+ if (udp.uh_sport == htons(IPPORT_BOOTPC) &&
+ udp.uh_dport == htons(IPPORT_BOOTPS)) {
+ minlen += sizeof(struct dhcp);
+ if (mbuf_pkthdr_len(m) < minlen) {
+ BRIDGE_HF_DROP(brhf_ip_too_small,
+ __func__, __LINE__);
+ goto done;
+ }
+ offset += sizeof(struct udphdr);
+ error = bridge_dhcp_filter(bif, m, offset);
+ if (error != 0) {
+ goto done;
+ }
+ } else if (iphdr.ip_src.s_addr == INADDR_ANY) {
+ BRIDGE_HF_DROP(brhf_ip_bad_srcaddr,
+ __func__, __LINE__);
+ goto done;
+ }
+ } else if (iphdr.ip_src.s_addr != bif->bif_hf_ipsrc.s_addr ||
+ bif->bif_hf_ipsrc.s_addr == INADDR_ANY) {
+ BRIDGE_HF_DROP(brhf_ip_bad_srcaddr, __func__, __LINE__);
+ goto done;
+ }
+ /*
+ * Allow only boring IP protocols
+ */
+ if (iphdr.ip_p != IPPROTO_TCP &&
+ iphdr.ip_p != IPPROTO_UDP &&
+ iphdr.ip_p != IPPROTO_ICMP &&
+ iphdr.ip_p != IPPROTO_ESP &&
+ iphdr.ip_p != IPPROTO_AH &&
+ iphdr.ip_p != IPPROTO_GRE) {
+ BRIDGE_HF_DROP(brhf_ip_bad_proto, __func__, __LINE__);
+ goto done;
+ }
+ bridge_hostfilter_stats.brhf_ip_ok += 1;
+ error = 0;
+ } else {
+ BRIDGE_HF_DROP(brhf_bad_ether_type, __func__, __LINE__);
+ goto done;
+ }
+done:
+ if (error != 0) {
+ if (IF_BRIDGE_DEBUG(BR_DBGF_HOSTFILTER)) {
+ if (m) {
+ printf_mbuf_data(m, 0,
+ sizeof(struct ether_header) +
+ sizeof(struct ip));
+ }
+ printf("\n");
+ }
+
+ if (m != NULL) {
+ m_freem(m);
+ }
+ }
+ return error;
+}
+
+/*
+ * MAC NAT
+ */
+
+static errno_t
+bridge_mac_nat_enable(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+ errno_t error = 0;
+
+ BRIDGE_LOCK_ASSERT_HELD(sc);
+
+ if (sc->sc_mac_nat_bif != NULL) {
+ if (sc->sc_mac_nat_bif != bif) {
+ error = EBUSY;
+ }
+ goto done;
+ }
+ sc->sc_mac_nat_bif = bif;
+ bif->bif_ifflags |= IFBIF_MAC_NAT;
+ bridge_mac_nat_populate_entries(sc);
+
+done:
+ return error;
+}
+
+static void
+bridge_mac_nat_disable(struct bridge_softc *sc)
+{
+ struct bridge_iflist *mac_nat_bif = sc->sc_mac_nat_bif;
+
+ assert(mac_nat_bif != NULL);
+ bridge_mac_nat_flush_entries(sc, mac_nat_bif);
+ mac_nat_bif->bif_ifflags &= ~IFBIF_MAC_NAT;
+ sc->sc_mac_nat_bif = NULL;
+ return;
+}
+
+static void
+mac_nat_entry_print2(struct mac_nat_entry *mne,
+ char *ifname, const char *msg1, const char *msg2)
+{
+ int af;
+ char etopbuf[24];
+ char ntopbuf[MAX_IPv6_STR_LEN];
+ const char *space;
+
+ af = ((mne->mne_flags & MNE_FLAGS_IPV6) != 0) ? AF_INET6 : AF_INET;
+ ether_ntop(etopbuf, sizeof(etopbuf), mne->mne_mac);
+ (void)inet_ntop(af, &mne->mne_u, ntopbuf, sizeof(ntopbuf));
+ if (msg2 == NULL) {
+ msg2 = "";
+ space = "";
+ } else {
+ space = " ";
+ }
+ printf("%s %s%s%s %p (%s, %s, %s)\n",
+ ifname, msg1, space, msg2, mne, mne->mne_bif->bif_ifp->if_xname,
+ ntopbuf, etopbuf);
+}
+
+static void
+mac_nat_entry_print(struct mac_nat_entry *mne,
+ char *ifname, const char *msg)
+{
+ mac_nat_entry_print2(mne, ifname, msg, NULL);
+}
+
+static struct mac_nat_entry *
+bridge_lookup_mac_nat_entry(struct bridge_softc *sc, int af, void * ip)
+{
+ struct mac_nat_entry *mne;
+ struct mac_nat_entry *ret_mne = NULL;
+
+ if (af == AF_INET) {
+ in_addr_t s_addr = ((struct in_addr *)ip)->s_addr;
+
+ LIST_FOREACH(mne, &sc->sc_mne_list, mne_list) {
+ if (mne->mne_ip.s_addr == s_addr) {
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ mac_nat_entry_print(mne, sc->sc_if_xname,
+ "found");
+ }
+ ret_mne = mne;
+ break;
+ }
+ }
+ } else {
+ const struct in6_addr *ip6 = (const struct in6_addr *)ip;
+
+ LIST_FOREACH(mne, &sc->sc_mne_list_v6, mne_list) {
+ if (IN6_ARE_ADDR_EQUAL(&mne->mne_ip6, ip6)) {
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ mac_nat_entry_print(mne, sc->sc_if_xname,
+ "found");
+ }
+ ret_mne = mne;
+ break;
+ }
+ }
+ }
+ return ret_mne;
+}
+
+static void
+bridge_destroy_mac_nat_entry(struct bridge_softc *sc,
+ struct mac_nat_entry *mne, const char *reason)
+{
+ LIST_REMOVE(mne, mne_list);
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ mac_nat_entry_print(mne, sc->sc_if_xname, reason);
+ }
+ zfree(bridge_mne_pool, mne);
+ sc->sc_mne_count--;
+}
+
+static struct mac_nat_entry *
+bridge_create_mac_nat_entry(struct bridge_softc *sc,
+ struct bridge_iflist *bif, int af, const void *ip, uint8_t *eaddr)
+{
+ struct mac_nat_entry_list *list;
+ struct mac_nat_entry *mne;
+
+ if (sc->sc_mne_count >= sc->sc_mne_max) {
+ sc->sc_mne_allocation_failures++;
+ return NULL;
+ }
+ mne = zalloc_noblock(bridge_mne_pool);
+ if (mne == NULL) {
+ sc->sc_mne_allocation_failures++;
+ return NULL;
+ }
+ sc->sc_mne_count++;
+ bzero(mne, sizeof(*mne));
+ bcopy(eaddr, mne->mne_mac, sizeof(mne->mne_mac));
+ mne->mne_bif = bif;
+ if (af == AF_INET) {
+ bcopy(ip, &mne->mne_ip, sizeof(mne->mne_ip));
+ list = &sc->sc_mne_list;
+ } else {
+ bcopy(ip, &mne->mne_ip6, sizeof(mne->mne_ip6));
+ mne->mne_flags |= MNE_FLAGS_IPV6;
+ list = &sc->sc_mne_list_v6;
+ }
+ LIST_INSERT_HEAD(list, mne, mne_list);
+ mne->mne_expire = (unsigned long)net_uptime() + sc->sc_brttimeout;
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ mac_nat_entry_print(mne, sc->sc_if_xname, "created");
+ }
+ return mne;
+}
+
+static struct mac_nat_entry *
+bridge_update_mac_nat_entry(struct bridge_softc *sc,
+ struct bridge_iflist *bif, int af, void *ip, uint8_t *eaddr)
+{
+ struct mac_nat_entry *mne;
+
+ mne = bridge_lookup_mac_nat_entry(sc, af, ip);
+ if (mne != NULL) {
+ struct bridge_iflist *mac_nat_bif = sc->sc_mac_nat_bif;
+
+ if (mne->mne_bif == mac_nat_bif) {
+ /* the MAC NAT interface takes precedence */
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ if (mne->mne_bif != bif) {
+ mac_nat_entry_print2(mne,
+ sc->sc_if_xname, "reject",
+ bif->bif_ifp->if_xname);
+ }
+ }
+ } else if (mne->mne_bif != bif) {
+ const char *old_if = mne->mne_bif->bif_ifp->if_xname;
+
+ mne->mne_bif = bif;
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ mac_nat_entry_print2(mne,
+ sc->sc_if_xname, "replaced",
+ old_if);
+ }
+ bcopy(eaddr, mne->mne_mac, sizeof(mne->mne_mac));
+ }
+ mne->mne_expire = (unsigned long)net_uptime() +
+ sc->sc_brttimeout;
+ } else {
+ mne = bridge_create_mac_nat_entry(sc, bif, af, ip, eaddr);
+ }
+ return mne;
+}
+
+static void
+bridge_mac_nat_flush_entries_common(struct bridge_softc *sc,
+ struct mac_nat_entry_list *list, struct bridge_iflist *bif)
+{
+ struct mac_nat_entry *mne;
+ struct mac_nat_entry *tmne;
+
+ LIST_FOREACH_SAFE(mne, list, mne_list, tmne) {
+ if (bif != NULL && mne->mne_bif != bif) {
+ continue;
+ }
+ bridge_destroy_mac_nat_entry(sc, mne, "flushed");
+ }
+}
+
+/*
+ * bridge_mac_nat_flush_entries:
+ *
+ * Flush MAC NAT entries for the specified member. Flush all entries if
+ * the member is the one that requires MAC NAT, otherwise just flush the
+ * ones for the specified member.
+ */
+static void
+bridge_mac_nat_flush_entries(struct bridge_softc *sc, struct bridge_iflist * bif)
+{
+ struct bridge_iflist *flush_bif;
+
+ flush_bif = (bif == sc->sc_mac_nat_bif) ? NULL : bif;
+ bridge_mac_nat_flush_entries_common(sc, &sc->sc_mne_list, flush_bif);
+ bridge_mac_nat_flush_entries_common(sc, &sc->sc_mne_list_v6, flush_bif);
+}
+
+static void
+bridge_mac_nat_populate_entries(struct bridge_softc *sc)
+{
+ errno_t error;
+ ifnet_t ifp;
+ ifaddr_t *list;
+ struct bridge_iflist *mac_nat_bif = sc->sc_mac_nat_bif;
+
+ assert(mac_nat_bif != NULL);
+ ifp = mac_nat_bif->bif_ifp;
+ error = ifnet_get_address_list(ifp, &list);
+ if (error != 0) {
+ printf("%s: ifnet_get_address_list(%s) failed %d\n",
+ __func__, ifp->if_xname, error);
+ return;
+ }
+ for (ifaddr_t *scan = list; *scan != NULL; scan++) {
+ sa_family_t af;
+ void *ip;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } u;
+ af = ifaddr_address_family(*scan);
+ switch (af) {
+ case AF_INET:
+ case AF_INET6:
+ error = ifaddr_address(*scan, &u.sa, sizeof(u));
+ if (error != 0) {
+ printf("%s: ifaddr_address failed %d\n",
+ __func__, error);
+ break;
+ }
+ if (af == AF_INET) {
+ ip = (void *)&u.sin.sin_addr;
+ } else {
+ if (IN6_IS_ADDR_LINKLOCAL(&u.sin6.sin6_addr)) {
+ /* remove scope ID */
+ u.sin6.sin6_addr.s6_addr16[1] = 0;
+ }
+ ip = (void *)&u.sin6.sin6_addr;
+ }
+ bridge_create_mac_nat_entry(sc, mac_nat_bif, af, ip,
+ (uint8_t *)IF_LLADDR(ifp));
+ break;
+ default:
+ break;
+ }
+ }
+ ifnet_free_address_list(list);
+ return;
+}
+
+static void
+bridge_mac_nat_age_entries_common(struct bridge_softc *sc,
+ struct mac_nat_entry_list *list, unsigned long now)
+{
+ struct mac_nat_entry *mne;
+ struct mac_nat_entry *tmne;
+
+ LIST_FOREACH_SAFE(mne, list, mne_list, tmne) {
+ if (now >= mne->mne_expire) {
+ bridge_destroy_mac_nat_entry(sc, mne, "aged out");
+ }
+ }
+}
+
+static void
+bridge_mac_nat_age_entries(struct bridge_softc *sc, unsigned long now)
+{
+ if (sc->sc_mac_nat_bif == NULL) {
+ return;
+ }
+ bridge_mac_nat_age_entries_common(sc, &sc->sc_mne_list, now);
+ bridge_mac_nat_age_entries_common(sc, &sc->sc_mne_list_v6, now);
+}
+
+static const char *
+get_in_out_string(boolean_t is_output)
+{
+ return is_output ? "OUT" : "IN";
+}
+
+/*
+ * is_valid_arp_packet:
+ * Verify that this is a valid ARP packet.
+ *
+ * Returns TRUE if the packet is valid, FALSE otherwise.
+ */
+static boolean_t
+is_valid_arp_packet(mbuf_t *data, boolean_t is_output,
+ struct ether_header **eh_p, struct ether_arp **ea_p)
+{
+ struct ether_arp *ea;
+ struct ether_header *eh;
+ size_t minlen = sizeof(struct ether_header) + sizeof(struct ether_arp);
+ boolean_t is_valid = FALSE;
+ int flags = is_output ? BR_DBGF_OUTPUT : BR_DBGF_INPUT;
+
+ if (mbuf_pkthdr_len(*data) < minlen) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s short frame %lu < %lu\n",
+ __func__,
+ get_in_out_string(is_output),
+ mbuf_pkthdr_len(*data), minlen);
+ }
+ goto done;
+ }
+ if (mbuf_len(*data) < minlen && mbuf_pullup(data, minlen) != 0) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s size %lu mbuf_pullup fail\n",
+ __func__,
+ get_in_out_string(is_output),
+ minlen);
+ }
+ *data = NULL;
+ goto done;
+ }
+
+ /* validate ARP packet */
+ eh = mtod(*data, struct ether_header *);
+ ea = (struct ether_arp *)(eh + 1);
+ if (ntohs(ea->arp_hrd) != ARPHRD_ETHER) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s htype not ethernet\n",
+ __func__,
+ get_in_out_string(is_output));
+ }
+ goto done;
+ }
+ if (ea->arp_hln != ETHER_ADDR_LEN) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s hlen not ethernet\n",
+ __func__,
+ get_in_out_string(is_output));
+ }
+ goto done;
+ }
+ if (ntohs(ea->arp_pro) != ETHERTYPE_IP) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s ptype not IP\n",
+ __func__,
+ get_in_out_string(is_output));
+ }
+ goto done;
+ }
+ if (ea->arp_pln != sizeof(struct in_addr)) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: ARP %s plen not IP\n",
+ __func__,
+ get_in_out_string(is_output));
+ }
+ goto done;
+ }
+ is_valid = TRUE;
+ *ea_p = ea;
+ *eh_p = eh;
+done:
+ return is_valid;
+}
+
+static struct mac_nat_entry *
+bridge_mac_nat_arp_input(struct bridge_softc *sc, mbuf_t *data)
+{
+ struct ether_arp *ea;
+ struct ether_header *eh;
+ struct mac_nat_entry *mne = NULL;
+ u_short op;
+ struct in_addr tpa;
+
+ if (!is_valid_arp_packet(data, FALSE, &eh, &ea)) {
+ goto done;
+ }
+ op = ntohs(ea->arp_op);
+ switch (op) {
+ case ARPOP_REQUEST:
+ case ARPOP_REPLY:
+ /* only care about REQUEST and REPLY */
+ break;
+ default:
+ goto done;
+ }
+
+ /* check the target IP address for a NAT entry */
+ bcopy(ea->arp_tpa, &tpa, sizeof(tpa));
+ if (tpa.s_addr != 0) {
+ mne = bridge_lookup_mac_nat_entry(sc, AF_INET, &tpa);
+ }
+ if (mne != NULL) {
+ if (op == ARPOP_REPLY) {
+ /* translate the MAC address */
+ if (IF_BRIDGE_DEBUG(BR_DBGF_MAC_NAT)) {
+ char mac_src[24];
+ char mac_dst[24];
+
+ ether_ntop(mac_src, sizeof(mac_src),
+ ea->arp_tha);
+ ether_ntop(mac_dst, sizeof(mac_dst),
+ mne->mne_mac);
+ printf("%s %s ARP %s -> %s\n",
+ sc->sc_if_xname,
+ mne->mne_bif->bif_ifp->if_xname,
+ mac_src, mac_dst);
+ }
+ bcopy(mne->mne_mac, ea->arp_tha, sizeof(ea->arp_tha));
+ }
+ } else {
+ /* handle conflicting ARP (sender matches mne) */
+ struct in_addr spa;
+
+ bcopy(ea->arp_spa, &spa, sizeof(spa));
+ if (spa.s_addr != 0 && spa.s_addr != tpa.s_addr) {
+ /* check the source IP for a NAT entry */
+ mne = bridge_lookup_mac_nat_entry(sc, AF_INET, &spa);
+ }
+ }
+
+done:
+ return mne;
+}
+
+static boolean_t
+bridge_mac_nat_arp_output(struct bridge_softc *sc,
+ struct bridge_iflist *bif, mbuf_t *data, struct mac_nat_record *mnr)
+{
+ struct ether_arp *ea;
+ struct ether_header *eh;
+ struct in_addr ip;
+ struct mac_nat_entry *mne = NULL;
+ u_short op;
+ boolean_t translate = FALSE;
+
+ if (!is_valid_arp_packet(data, TRUE, &eh, &ea)) {
+ goto done;
+ }
+ op = ntohs(ea->arp_op);
+ switch (op) {
+ case ARPOP_REQUEST:
+ case ARPOP_REPLY:
+ /* only care about REQUEST and REPLY */
+ break;
+ default:
+ goto done;
+ }
+
+ bcopy(ea->arp_spa, &ip, sizeof(ip));
+ if (ip.s_addr == 0) {
+ goto done;
+ }
+ /* XXX validate IP address: no multicast/broadcast */
+ mne = bridge_update_mac_nat_entry(sc, bif, AF_INET, &ip, ea->arp_sha);
+ if (mnr != NULL && mne != NULL) {
+ /* record the offset to do the replacement */
+ translate = TRUE;
+ mnr->mnr_arp_offset = (char *)ea->arp_sha - (char *)eh;
+ }
+
+done:
+ return translate;
+}
+
+#define ETHER_IPV4_HEADER_LEN (sizeof(struct ether_header) + \
+ + sizeof(struct ip))
+static struct ether_header *
+get_ether_ip_header(mbuf_t *data, boolean_t is_output)
+{
+ struct ether_header *eh = NULL;
+ int flags = is_output ? BR_DBGF_OUTPUT : BR_DBGF_INPUT;
+ size_t minlen = ETHER_IPV4_HEADER_LEN;
+
+ if (mbuf_pkthdr_len(*data) < minlen) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: IP %s short frame %lu < %lu\n",
+ __func__,
+ get_in_out_string(is_output),
+ mbuf_pkthdr_len(*data), minlen);
+ }
+ goto done;
+ }
+ if (mbuf_len(*data) < minlen && mbuf_pullup(data, minlen) != 0) {
+ if (IF_BRIDGE_DEBUG(flags)) {
+ printf("%s: IP %s size %lu mbuf_pullup fail\n",
+ __func__,
+ get_in_out_string(is_output),
+ minlen);
+ }
+ *data = NULL;
+ goto done;
+ }
+ eh = mtod(*data, struct ether_header *);
+done:
+ return eh;
+}
+
+static boolean_t
+is_broadcast_ip_packet(mbuf_t *data)
+{
+ struct ether_header *eh;
+ uint16_t ether_type;
+ boolean_t is_broadcast = FALSE;
+
+ eh = mtod(*data, struct ether_header *);
+ ether_type = ntohs(eh->ether_type);
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ eh = get_ether_ip_header(data, FALSE);
+ if (eh != NULL) {
+ struct in_addr dst;
+ struct ip *iphdr;
+
+ iphdr = (struct ip *)(void *)(eh + 1);
+ bcopy(&iphdr->ip_dst, &dst, sizeof(dst));
+ is_broadcast = (dst.s_addr == INADDR_BROADCAST);
+ }
+ break;
+ default:
+ break;
+ }
+ return is_broadcast;
+}