- }
- if (soft_vlan) {
- /*
- * Packet had an in-line encapsulation header;
- * remove it. The original header has already
- * been fixed up above.
- */
- m->m_len -= ETHER_VLAN_ENCAP_LEN;
- m->m_data += ETHER_VLAN_ENCAP_LEN;
- m->m_pkthdr.len -= ETHER_VLAN_ENCAP_LEN;
- m->m_pkthdr.csum_flags = 0; /* can't trust hardware checksum */
- }
- if (tag != 0) {
- m->m_pkthdr.rcvif = ifp;
- (void)ifnet_stat_increment_in(ifp, 1,
- m->m_pkthdr.len + ETHER_HDR_LEN, 0);
- vlan_bpf_input(ifp, m, bpf_func, frame_header, ETHER_HDR_LEN,
- soft_vlan ? ETHER_VLAN_ENCAP_LEN : 0);
- /* We found a vlan interface, inject on that interface. */
- dlil_input_packet(ifp, m, frame_header);
- } else {
- /* Send priority-tagged packet up through the parent */
- dlil_input_packet(p, m, frame_header);
- }
- return 0;
-}
-
-#define VLAN_CONFIG_PROGRESS_VLP_RETAINED 0x1
-#define VLAN_CONFIG_PROGRESS_IN_LIST 0x2
+
+ ifvlan_release(ifv);
+ vlan_parent_release(vlp);
+
+ bpf_tap_out(ifp, DLT_EN10MB, m, NULL, 0);
+
+ /* do not run parent's if_output() if the parent is not up */
+ if ((ifnet_flags(p) & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) {
+ m_freem(m);
+ atomic_add_64(&ifp->if_collisions, 1);
+ return 0;
+ }
+ /*
+ * If underlying interface can do VLAN tag insertion itself,
+ * just pass the packet along. However, we need some way to
+ * tell the interface where the packet came from so that it
+ * knows how to find the VLAN tag to use. We use a field in
+ * the mbuf header to store the VLAN tag, and a bit in the
+ * csum_flags field to mark the field as valid.
+ */
+ if (soft_vlan == 0) {
+ m->m_pkthdr.csum_flags |= CSUM_VLAN_TAG_VALID;
+ m->m_pkthdr.vlan_tag = tag;
+ } else {
+ M_PREPEND(m, encaplen, M_DONTWAIT, 1);
+ if (m == NULL) {
+ printf("%s%d: unable to prepend VLAN header\n", ifnet_name(ifp),
+ ifnet_unit(ifp));
+ atomic_add_64(&ifp->if_oerrors, 1);
+ return 0;
+ }
+ /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
+ if (m->m_len < (int)sizeof(*evl)) {
+ m = m_pullup(m, sizeof(*evl));
+ if (m == NULL) {
+ printf("%s%d: unable to pullup VLAN header\n", ifnet_name(ifp),
+ ifnet_unit(ifp));
+ atomic_add_64(&ifp->if_oerrors, 1);
+ return 0;
+ }
+ }
+
+ /*
+ * Transform the Ethernet header into an Ethernet header
+ * with 802.1Q encapsulation.
+ */
+ bcopy(mtod(m, char *) + encaplen,
+ mtod(m, char *), ETHER_HDR_LEN);
+ evl = mtod(m, struct ether_vlan_header *);
+ evl->evl_proto = evl->evl_encap_proto;
+ evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
+ evl->evl_tag = htons(tag);
+
+ /* adjust partial checksum offload offsets */
+ if ((m->m_pkthdr.csum_flags & (CSUM_DATA_VALID |
+ CSUM_PARTIAL)) == (CSUM_DATA_VALID | CSUM_PARTIAL)) {
+ m->m_pkthdr.csum_tx_start += ETHER_VLAN_ENCAP_LEN;
+ m->m_pkthdr.csum_tx_stuff += ETHER_VLAN_ENCAP_LEN;
+ }
+ }
+
+ err = dlil_output(p, PF_VLAN, m, NULL, NULL, 1, &adv);
+
+ if (err == 0) {
+ if (adv.code == FADV_FLOW_CONTROLLED) {
+ err = EQFULL;
+ } else if (adv.code == FADV_SUSPENDED) {
+ err = EQSUSPENDED;
+ }
+ }
+
+ return err;
+
+unlock_done:
+ vlan_unlock();
+ if (ifv != NULL) {
+ ifvlan_release(ifv);
+ }
+ if (vlp != NULL) {
+ vlan_parent_release(vlp);
+ }
+ m_freem_list(m);
+ return 0;
+}
+
+static int
+vlan_input(ifnet_t p, __unused protocol_family_t protocol,
+ mbuf_t m, char *frame_header)
+{
+ struct ether_vlan_header * evl;
+ struct ifnet * ifp = NULL;
+ int soft_vlan = 0;
+ u_int tag = 0;
+
+ if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) {
+ /*
+ * Packet is tagged, m contains a normal
+ * Ethernet frame; the tag is stored out-of-band.
+ */
+ m->m_pkthdr.csum_flags &= ~CSUM_VLAN_TAG_VALID;
+ tag = EVL_VLANOFTAG(m->m_pkthdr.vlan_tag);
+ m->m_pkthdr.vlan_tag = 0;
+ } else {
+ soft_vlan = 1;
+ switch (ifnet_type(p)) {
+ case IFT_ETHER:
+ case IFT_IEEE8023ADLAG:
+ if (m->m_len < ETHER_VLAN_ENCAP_LEN) {
+ m_freem(m);
+ return 0;
+ }
+ evl = (struct ether_vlan_header *)(void *)frame_header;
+ if (ntohs(evl->evl_proto) == ETHERTYPE_VLAN) {
+ /* don't allow VLAN within VLAN */
+ m_freem(m);
+ return 0;
+ }
+ tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+
+ /*
+ * Restore the original ethertype. We'll remove
+ * the encapsulation after we've found the vlan
+ * interface corresponding to the tag.
+ */
+ evl->evl_encap_proto = evl->evl_proto;
+ break;
+ default:
+ printf("vlan_demux: unsupported if type %u",
+ ifnet_type(p));
+ m_freem(m);
+ return 0;
+ }
+ }
+ if (tag != 0) {
+ ifvlan_ref ifv;
+
+ if ((ifnet_eflags(p) & IFEF_VLAN) == 0) {
+ /* don't bother looking through the VLAN list */
+ m_freem(m);
+ return 0;
+ }
+ vlan_lock();
+ ifv = vlan_lookup_parent_and_tag(p, tag);
+ if (ifv != NULL) {
+ ifp = ifv->ifv_ifp;
+ }
+ if (ifv == NULL
+ || ifvlan_flags_ready(ifv) == 0
+ || (ifnet_flags(ifp) & IFF_UP) == 0) {
+ vlan_unlock();
+ m_freem(m);
+ return 0;
+ }
+ vlan_unlock();
+ }
+ if (soft_vlan) {
+ /*
+ * Packet had an in-line encapsulation header;
+ * remove it. The original header has already
+ * been fixed up above.
+ */
+ m->m_len -= ETHER_VLAN_ENCAP_LEN;
+ m->m_data += ETHER_VLAN_ENCAP_LEN;
+ m->m_pkthdr.len -= ETHER_VLAN_ENCAP_LEN;
+ m->m_pkthdr.csum_flags = 0; /* can't trust hardware checksum */
+ }
+ if (tag != 0) {
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.pkt_hdr = frame_header;
+ (void)ifnet_stat_increment_in(ifp, 1,
+ m->m_pkthdr.len + ETHER_HDR_LEN, 0);
+ bpf_tap_in(ifp, DLT_EN10MB, m, frame_header, ETHER_HDR_LEN);
+ /* We found a vlan interface, inject on that interface. */
+ dlil_input_packet_list(ifp, m);
+ } else {
+ m->m_pkthdr.pkt_hdr = frame_header;
+ /* Send priority-tagged packet up through the parent */
+ dlil_input_packet_list(p, m);
+ }
+ return 0;
+}