summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net/if_ethersubr.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/net/if_ethersubr.c')
-rw-r--r--freebsd/sys/net/if_ethersubr.c144
1 files changed, 131 insertions, 13 deletions
diff --git a/freebsd/sys/net/if_ethersubr.c b/freebsd/sys/net/if_ethersubr.c
index 24f11436..78ff2385 100644
--- a/freebsd/sys/net/if_ethersubr.c
+++ b/freebsd/sys/net/if_ethersubr.c
@@ -49,6 +49,7 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mbuf.h>
+#include <sys/priv.h>
#include <sys/random.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@@ -313,7 +314,13 @@ ether_output(struct ifnet *ifp, struct mbuf *m,
if (lle == NULL) {
/* if we lookup, keep cache */
addref = 1;
- }
+ } else
+ /*
+ * Notify LLE code that
+ * the entry was used
+ * by datapath.
+ */
+ llentry_mark_used(lle);
}
if (lle != NULL) {
phdr = lle->r_linkdata;
@@ -433,6 +440,19 @@ bad: if (m != NULL)
return ether_output_frame(ifp, m);
}
+static bool
+ether_set_pcp(struct mbuf **mp, struct ifnet *ifp, uint8_t pcp)
+{
+ struct ether_header *eh;
+
+ eh = mtod(*mp, struct ether_header *);
+ if (ntohs(eh->ether_type) == ETHERTYPE_VLAN ||
+ ether_8021q_frame(mp, ifp, ifp, 0, pcp))
+ return (true);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (false);
+}
+
/*
* Ethernet link layer output routine to send a raw frame to the device.
*
@@ -442,12 +462,17 @@ bad: if (m != NULL)
int
ether_output_frame(struct ifnet *ifp, struct mbuf *m)
{
- int i;
+ int error;
+ uint8_t pcp;
- if (PFIL_HOOKED(&V_link_pfil_hook)) {
- i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_OUT, NULL);
+ pcp = ifp->if_pcp;
+ if (pcp != IFNET_PCP_NONE && !ether_set_pcp(&m, ifp, pcp))
+ return (0);
- if (i != 0)
+ if (PFIL_HOOKED(&V_link_pfil_hook)) {
+ error = pfil_run_hooks(&V_link_pfil_hook, &m, ifp,
+ PFIL_OUT, 0, NULL);
+ if (error != 0)
return (EACCES);
if (m == NULL)
@@ -778,7 +803,8 @@ ether_demux(struct ifnet *ifp, struct mbuf *m)
/* Do not grab PROMISC frames in case we are re-entered. */
if (PFIL_HOOKED(&V_link_pfil_hook) && !(m->m_flags & M_PROMISC)) {
- i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_IN, NULL);
+ i = pfil_run_hooks(&V_link_pfil_hook, &m, ifp, PFIL_IN, 0,
+ NULL);
if (i != 0 || m == NULL)
return;
@@ -1084,13 +1110,8 @@ ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
break;
case SIOCGIFADDR:
- {
- struct sockaddr *sa;
-
- sa = (struct sockaddr *) & ifr->ifr_data;
- bcopy(IF_LLADDR(ifp),
- (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
- }
+ bcopy(IF_LLADDR(ifp), &ifr->ifr_addr.sa_data[0],
+ ETHER_ADDR_LEN);
break;
case SIOCSIFMTU:
@@ -1103,6 +1124,22 @@ ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_mtu = ifr->ifr_mtu;
}
break;
+
+ case SIOCSLANPCP:
+ error = priv_check(curthread, PRIV_NET_SETLANPCP);
+ if (error != 0)
+ break;
+ if (ifr->ifr_lan_pcp > 7 &&
+ ifr->ifr_lan_pcp != IFNET_PCP_NONE)
+ error = EINVAL;
+ else
+ ifp->if_pcp = ifr->ifr_lan_pcp;
+ break;
+
+ case SIOCGLANPCP:
+ ifr->ifr_lan_pcp = ifp->if_pcp;
+ break;
+
default:
error = EINVAL; /* XXX netbsd has ENOTTY??? */
break;
@@ -1251,5 +1288,86 @@ ether_vlanencap(struct mbuf *m, uint16_t tag)
return (m);
}
+static SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0,
+ "IEEE 802.1Q VLAN");
+static SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0,
+ "for consistency");
+
+static VNET_DEFINE(int, soft_pad);
+#define V_soft_pad VNET(soft_pad)
+SYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW | CTLFLAG_VNET,
+ &VNET_NAME(soft_pad), 0,
+ "pad short frames before tagging");
+
+/*
+ * For now, make preserving PCP via an mbuf tag optional, as it increases
+ * per-packet memory allocations and frees. In the future, it would be
+ * preferable to reuse ether_vtag for this, or similar.
+ */
+int vlan_mtag_pcp = 0;
+SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW,
+ &vlan_mtag_pcp, 0,
+ "Retain VLAN PCP information as packets are passed up the stack");
+
+bool
+ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p,
+ uint16_t vid, uint8_t pcp)
+{
+ struct m_tag *mtag;
+ int n;
+ uint16_t tag;
+ static const char pad[8]; /* just zeros */
+
+ /*
+ * Pad the frame to the minimum size allowed if told to.
+ * This option is in accord with IEEE Std 802.1Q, 2003 Ed.,
+ * paragraph C.4.4.3.b. It can help to work around buggy
+ * bridges that violate paragraph C.4.4.3.a from the same
+ * document, i.e., fail to pad short frames after untagging.
+ * E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but
+ * untagging it will produce a 62-byte frame, which is a runt
+ * and requires padding. There are VLAN-enabled network
+ * devices that just discard such runts instead or mishandle
+ * them somehow.
+ */
+ if (V_soft_pad && p->if_type == IFT_ETHER) {
+ for (n = ETHERMIN + ETHER_HDR_LEN - (*mp)->m_pkthdr.len;
+ n > 0; n -= sizeof(pad)) {
+ if (!m_append(*mp, min(n, sizeof(pad)), pad))
+ break;
+ }
+ if (n > 0) {
+ m_freem(*mp);
+ *mp = NULL;
+ if_printf(ife, "cannot pad short frame");
+ return (false);
+ }
+ }
+
+ /*
+ * 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, so we attach a
+ * packet tag that holds it.
+ */
+ if (vlan_mtag_pcp && (mtag = m_tag_locate(*mp, MTAG_8021Q,
+ MTAG_8021Q_PCP_OUT, NULL)) != NULL)
+ tag = EVL_MAKETAG(vid, *(uint8_t *)(mtag + 1), 0);
+ else
+ tag = EVL_MAKETAG(vid, pcp, 0);
+ if (p->if_capenable & IFCAP_VLAN_HWTAGGING) {
+ (*mp)->m_pkthdr.ether_vtag = tag;
+ (*mp)->m_flags |= M_VLANTAG;
+ } else {
+ *mp = ether_vlanencap(*mp, tag);
+ if (*mp == NULL) {
+ if_printf(ife, "unable to prepend 802.1Q header");
+ return (false);
+ }
+ }
+ return (true);
+}
+
DECLARE_MODULE(ether, ether_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
MODULE_VERSION(ether, 1);