o change input packet handling to eliminate the pointer to the struct

ether_header; instead drivers are to leave the Ethernet header at the
  front of the packet
o add declarations for netgraph and vlan hooks that were removed from ethernet.h
o change various in-file calling conventions to track change in input API
o fixup bridge support to handle Ethernet header no longer being stripped
o add consistency checks to ether_input to catch problems with the change
  in the API; some of these may want to be moved to #ifdef DIAGNOSTIC at a
  later time (though they are not too expensive to leave as is)
o change ether_demux to eliminate the passing of the Ethernet header; it is
  now expected at the front of the packet a la ether_input
o add ether_sprintf compatibility shim
o change ether_ifattach API to remove "bpf supported param" and add a pointer
  to the MAC address to be installed for the LL address (this is for future
  changes to divest struct arpcom from struct ifnet)
o change ether_ifdetach API to remove "bpf support param"

Reviewed by:	many
Approved by:	re
This commit is contained in:
Sam Leffler 2002-11-14 23:35:06 +00:00
parent eef6f89728
commit c1d93b0588
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=106930

View File

@ -62,6 +62,7 @@
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/bridge.h>
#include <net/if_vlan_var.h>
#if defined(INET) || defined(INET6)
#include <netinet/in.h>
@ -103,17 +104,13 @@ extern u_char aarp_org_code[3];
#endif /* NETATALK */
/* netgraph node hooks for ng_ether(4) */
void (*ng_ether_input_p)(struct ifnet *ifp,
struct mbuf **mp, struct ether_header *eh);
void (*ng_ether_input_orphan_p)(struct ifnet *ifp,
struct mbuf *m, struct ether_header *eh);
void (*ng_ether_input_p)(struct ifnet *ifp, struct mbuf **mp);
void (*ng_ether_input_orphan_p)(struct ifnet *ifp, struct mbuf *m);
int (*ng_ether_output_p)(struct ifnet *ifp, struct mbuf **mp);
void (*ng_ether_attach_p)(struct ifnet *ifp);
void (*ng_ether_detach_p)(struct ifnet *ifp);
int (*vlan_input_p)(struct ether_header *eh, struct mbuf *m);
int (*vlan_input_tag_p)(struct ether_header *eh, struct mbuf *m,
u_int16_t t);
void (*vlan_input_p)(struct ifnet *, struct mbuf *);
/* bridge support */
int do_bridge;
@ -130,7 +127,7 @@ u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int
ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
struct ip_fw **rule, struct ether_header *eh, int shared);
struct ip_fw **rule, int shared);
static int ether_ipfw;
/*
@ -385,11 +382,8 @@ bad: if (m != NULL)
* in the first mbuf (if BRIDGE'ing).
*/
int
ether_output_frame(ifp, m)
struct ifnet *ifp;
struct mbuf *m;
ether_output_frame(struct ifnet *ifp, struct mbuf *m)
{
int error = 0;
struct ip_fw *rule = NULL;
/* Extract info from dummynet tag, ignore others */
@ -397,56 +391,33 @@ ether_output_frame(ifp, m)
if (m->m_flags == PACKET_TAG_DUMMYNET)
rule = ((struct dn_pkt *)m)->rule;
if (rule) /* packet was already bridged */
goto no_bridge;
if (BDG_ACTIVE(ifp) ) {
struct ether_header *eh; /* a ptr suffices */
if (rule == NULL && BDG_ACTIVE(ifp)) {
/*
* Beware, the bridge code notices the null rcvif and
* uses that identify that it's being called from
* ether_output as opposd to ether_input. Yech.
*/
m->m_pkthdr.rcvif = NULL;
eh = mtod(m, struct ether_header *);
m_adj(m, ETHER_HDR_LEN);
m = bdg_forward_ptr(m, eh, ifp);
m = bdg_forward_ptr(m, ifp);
if (m != NULL)
m_freem(m);
return (0);
}
no_bridge:
if (IPFW_LOADED && ether_ipfw != 0) {
struct ether_header save_eh, *eh;
eh = mtod(m, struct ether_header *);
save_eh = *eh;
m_adj(m, ETHER_HDR_LEN);
if (ether_ipfw_chk(&m, ifp, &rule, eh, 0) == 0) {
if (ether_ipfw_chk(&m, ifp, &rule, 0) == 0) {
if (m) {
m_freem(m);
return ENOBUFS; /* pkt dropped */
} else
return 0; /* consumed e.g. in a pipe */
}
/* packet was ok, restore the ethernet header */
if ( (void *)(eh + 1) == (void *)m->m_data) {
m->m_data -= ETHER_HDR_LEN ;
m->m_len += ETHER_HDR_LEN ;
m->m_pkthdr.len += ETHER_HDR_LEN ;
} else {
M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
if (m == NULL) /* nope... */
return ENOBUFS;
bcopy(&save_eh, mtod(m, struct ether_header *),
ETHER_HDR_LEN);
}
}
/*
* Queue message on interface, update output statistics if
* successful, and start output if interface not yet active.
*/
if (! IF_HANDOFF(&ifp->if_snd, m, ifp))
return (ENOBUFS);
return (error);
return (IF_HANDOFF(&ifp->if_snd, m, ifp) ? 0 : ENOBUFS);
}
/*
@ -458,9 +429,11 @@ no_bridge:
*/
int
ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
struct ip_fw **rule, struct ether_header *eh, int shared)
struct ip_fw **rule, int shared)
{
struct ether_header save_eh = *eh; /* might be a ptr in m */
struct ether_header *eh;
struct ether_header save_eh;
struct mbuf *m;
int i;
struct ip_fw_args args;
@ -471,24 +444,45 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
* I need some amt of data to be contiguous, and in case others need
* the packet (shared==1) also better be in the first mbuf.
*/
i = min( (*m0)->m_pkthdr.len, max_protohdr);
if ( shared || (*m0)->m_len < i) {
*m0 = m_pullup(*m0, i);
if (*m0 == NULL)
m = *m0;
i = min( m->m_pkthdr.len, max_protohdr);
if ( shared || m->m_len < i) {
m = m_pullup(m, i);
if (m == NULL) {
*m0 = m;
return 0;
}
}
eh = mtod(m, struct ether_header *);
save_eh = *eh; /* save copy for restore below */
m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */
args.m = *m0; /* the packet we are looking at */
args.m = m; /* the packet we are looking at */
args.oif = dst; /* destination, if any */
args.divert_rule = 0; /* we do not support divert yet */
args.rule = *rule; /* matching rule to restart */
args.next_hop = NULL; /* we do not support forward yet */
args.eh = &save_eh; /* MAC header for bridged/MAC packets */
i = ip_fw_chk_ptr(&args);
*m0 = args.m;
m = args.m;
if (m != NULL) {
/*
* Restore Ethernet header, as needed, in case the
* mbuf chain was replaced by ipfw.
*/
M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
if (m == NULL) {
*m0 = m;
return 0;
}
if (eh != mtod(m, struct ether_header *))
bcopy(&save_eh, mtod(m, struct ether_header *),
ETHER_HDR_LEN);
}
*m0 = m;
*rule = args.rule;
if ( (i & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) /* drop */
if ( (i & IP_FW_PORT_DENY_FLAG) || m == NULL) /* drop */
return 0;
if (i == 0) /* a PASS rule. */
@ -499,30 +493,16 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
* Pass the pkt to dummynet, which consumes it.
* If shared, make a copy and keep the original.
*/
struct mbuf *m ;
if (shared) {
m = m_copypacket(*m0, M_DONTWAIT);
m = m_copypacket(m, M_DONTWAIT);
if (m == NULL)
return 0;
} else {
m = *m0 ; /* pass the original to dummynet */
*m0 = NULL ; /* and nothing back to the caller */
}
/*
* Prepend the header, optimize for the common case of
* eh pointing into the mbuf.
*/
if ( (void *)(eh + 1) == (void *)m->m_data) {
m->m_data -= ETHER_HDR_LEN ;
m->m_len += ETHER_HDR_LEN ;
m->m_pkthdr.len += ETHER_HDR_LEN ;
} else {
M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
if (m == NULL) /* nope... */
return 0;
bcopy(&save_eh, mtod(m, struct ether_header *),
ETHER_HDR_LEN);
/*
* Pass the original to dummynet and
* nothing back to the caller
*/
*m0 = NULL ;
}
ip_dn_io_ptr(m, (i & 0xffff),
dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args);
@ -536,68 +516,89 @@ ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst,
}
/*
* Process a received Ethernet packet. We have two different interfaces:
* one (conventional) assumes the packet in the mbuf, with the ethernet
* header provided separately in *eh. The second one (new) has everything
* in the mbuf, and we can tell it because eh == NULL.
* The caller MUST MAKE SURE that there are at least
* sizeof(struct ether_header) bytes in the first mbuf.
*
* This allows us to concentrate in one place a bunch of code which
* is replicated in all device drivers. Also, many functions called
* from ether_input() try to put the eh back into the mbuf, so we
* can later propagate the 'contiguous packet' interface to them,
* and handle the old interface just here.
*
* NOTA BENE: for many drivers "eh" is a pointer into the first mbuf or
* cluster, right before m_data. So be very careful when working on m,
* as you could destroy *eh !!
*
* First we perform any link layer operations, then continue
* to the upper layers with ether_demux().
* Process a received Ethernet packet; the packet is in the
* mbuf chain m with the ethernet header at the front.
*/
void
ether_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
static void
ether_input(struct ifnet *ifp, struct mbuf *m)
{
struct ether_header save_eh;
struct ether_header *eh;
u_short etype;
if (eh == NULL) {
if (m->m_len < sizeof(struct ether_header)) {
/* XXX error in the caller. */
m_freem(m);
return;
}
if (ifp->if_bpf != NULL)
bpf_mtap(ifp, m);
m->m_pkthdr.rcvif = ifp;
eh = mtod(m, struct ether_header *);
m->m_data += sizeof(struct ether_header);
m->m_len -= sizeof(struct ether_header);
m->m_pkthdr.len = m->m_len;
} else if (ifp->if_bpf != NULL) {
struct m_hdr mh;
/* This kludge is OK; BPF treats the "mbuf" as read-only */
mh.mh_next = m;
mh.mh_data = (char *)eh;
mh.mh_len = ETHER_HDR_LEN;
bpf_mtap(ifp, (struct mbuf *)&mh);
}
if (ifp->if_flags & IFF_MONITOR) {
/*
* Do consistency checks to verify assumptions
* made by code past this point.
*/
if ((m->m_flags & M_PKTHDR) == 0) {
if_printf(ifp, "discard frame w/o packet header\n");
ifp->if_ierrors++;
m_freem(m);
return;
}
if (m->m_len < sizeof (struct ether_header)) {
/* XXX maybe should pullup? */
if_printf(ifp, "discard frame w/o leading ethernet "
"header (len %u pkt len %u)\n",
m->m_len, m->m_pkthdr.len);
ifp->if_ierrors++;
m_freem(m);
return;
}
eh = mtod(m, struct ether_header *);
etype = ntohs(eh->ether_type);
if (m->m_pkthdr.len >
ETHER_MAX_FRAME(ifp, etype, m->m_flags & M_HASFCS)) {
if_printf(ifp, "discard oversize frame "
"(ether type %x flags %x len %u > max %lu)\n",
etype, m->m_flags, m->m_pkthdr.len,
ETHER_MAX_FRAME(ifp, etype,
m->m_flags & M_HASFCS));
ifp->if_ierrors++;
m_freem(m);
return;
}
if (m->m_pkthdr.rcvif == NULL) {
if_printf(ifp, "discard frame w/o interface pointer\n");
ifp->if_ierrors++;
m_freem(m);
return;
}
#ifdef DIAGNOSTIC
if (m->m_pkthdr.rcvif != ifp) {
if_printf(ifp, "Warning, frame marked as received on %s%u\n",
m->m_pkthdr.rcvif->if_name,
m->m_pkthdr.rcvif->if_unit);
}
#endif
/*
* Give bpf a chance at the packet.
*/
BPF_MTAP(ifp, m);
if (ifp->if_flags & IFF_MONITOR) {
/*
* Interface marked for monitoring; discard packet.
*/
m_freem(m);
return;
}
/* If the CRC is still on the packet, trim it off. */
if (m->m_flags & M_HASFCS) {
m_adj(m, -ETHER_CRC_LEN);
m->m_flags &= ~M_HASFCS;
}
#ifdef MAC
mac_create_mbuf_from_ifnet(ifp, m);
#endif
ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
ifp->if_ibytes += m->m_pkthdr.len;
/* Handle ng_ether(4) processing, if any */
if (ng_ether_input_p != NULL) {
(*ng_ether_input_p)(ifp, &m, eh);
(*ng_ether_input_p)(ifp, &m);
if (m == NULL)
return;
}
@ -606,40 +607,53 @@ ether_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
if (BDG_ACTIVE(ifp) ) {
struct ifnet *bif;
/* Check with bridging code */
if ((bif = bridge_in_ptr(ifp, eh)) == BDG_DROP) {
/*
* Check with bridging code to see how the packet
* should be handled. Possibilities are:
*
* BDG_BCAST broadcast
* BDG_MCAST multicast
* BDG_LOCAL for local address, don't forward
* BDG_DROP discard
* ifp forward only to specified interface(s)
*
* Non-local destinations are handled by passing the
* packet back to the bridge code.
*/
bif = bridge_in_ptr(ifp, eh);
if (bif == BDG_DROP) { /* discard packet */
m_freem(m);
return;
}
if (bif != BDG_LOCAL) {
save_eh = *eh ; /* because it might change */
m = bdg_forward_ptr(m, eh, bif); /* needs forwarding */
if (bif != BDG_LOCAL) { /* non-local, forward */
m = bdg_forward_ptr(m, bif);
/*
* Do not continue if bdg_forward_ptr() processed our
* packet (and cleared the mbuf pointer m) or if
* it dropped (m_free'd) the packet itself.
* The bridge may consume the packet if it's not
* supposed to be passed up or if a problem occurred
* while doing its job. This is reflected by it
* returning a NULL mbuf pointer.
*/
if (m == NULL) {
if (bif == BDG_BCAST || bif == BDG_MCAST)
printf("bdg_forward drop MULTICAST PKT\n");
return;
if (bif == BDG_BCAST || bif == BDG_MCAST)
if_printf(ifp,
"bridge dropped %s packet\n",
bif == BDG_BCAST ? "broadcast" :
"multicast");
return;
}
/*
* But in some cases the bridge may return the
* packet for us to free; sigh.
*/
if (bif != BDG_BCAST && bif != BDG_MCAST) {
printf("ether_input: drop bdg packet, bif %p\n", bif);/*XXX*/
m_freem(m);
return;
}
eh = &save_eh ;
}
if (bif == BDG_LOCAL
|| bif == BDG_BCAST
|| bif == BDG_MCAST)
goto recvLocal; /* receive locally */
/* If not local and not multicast, just drop it */
if (m != NULL)
m_freem(m);
return;
}
recvLocal:
/* Continue with upper layer processing */
ether_demux(ifp, eh, m);
ether_demux(ifp, m);
/* First chunk of an mbuf contains good entropy */
if (harvest.ethernet)
random_harvest(m, 16, 3, 0, RANDOM_NET);
@ -649,11 +663,9 @@ recvLocal:
* Upper layer processing for a received Ethernet packet.
*/
void
ether_demux(ifp, eh, m)
struct ifnet *ifp;
struct ether_header *eh;
struct mbuf *m;
ether_demux(struct ifnet *ifp, struct mbuf *m)
{
struct ether_header *eh;
struct ifqueue *inq;
u_short ether_type;
#if defined(NETATALK)
@ -668,6 +680,10 @@ ether_demux(ifp, eh, m)
ifp = m->m_next->m_pkthdr.rcvif;
}
KASSERT(ifp != NULL, ("ether_demux: NULL interface pointer"));
eh = mtod(m, struct ether_header *);
if (rule) /* packet was already bridged */
goto post_stats;
@ -705,15 +721,56 @@ ether_demux(ifp, eh, m)
post_stats:
if (IPFW_LOADED && ether_ipfw != 0) {
if (ether_ipfw_chk(&m, NULL, &rule, eh, 0 ) == 0) {
if (ether_ipfw_chk(&m, NULL, &rule, 0) == 0) {
if (m)
m_freem(m);
return;
}
}
/*
* If VLANs are configured on the interface, check to
* see if the device performed the decapsulation and
* provided us with the tag.
*/
if (ifp->if_nvlans &&
m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL) != NULL) {
/*
* vlan_input() will either recursively call ether_input()
* or drop the packet.
*/
KASSERT(vlan_input_p != NULL,("ether_input: VLAN not loaded!"));
(*vlan_input_p)(ifp, m);
return;
}
ether_type = ntohs(eh->ether_type);
/*
* Handle protocols that expect to have the Ethernet header
* (and possibly FCS) intact.
*/
switch (ether_type) {
case ETHERTYPE_VLAN:
if (ifp->if_nvlans != 0) {
KASSERT(vlan_input_p,("ether_input: VLAN not loaded!"));
(*vlan_input_p)(ifp, m);
} else {
ifp->if_noproto++;
m_freem(m);
}
return;
}
/* Strip off Ethernet header. */
m_adj(m, sizeof (struct ether_header));
/* If the CRC is still on the packet, trim it off. */
if (m->m_flags & M_HASFCS) {
m_adj(m, -ETHER_CRC_LEN);
m->m_flags &= ~M_HASFCS;
}
switch (ether_type) {
#ifdef INET
case ETHERTYPE_IP:
@ -764,16 +821,6 @@ post_stats:
aarpinput(IFP2AC(ifp), m); /* XXX */
return;
#endif /* NETATALK */
case ETHERTYPE_VLAN:
/* XXX lock ? */
if (vlan_input_p != NULL)
(*vlan_input_p)(eh, m);
else {
m->m_pkthdr.rcvif->if_noproto++;
m_freem(m);
}
/* XXX unlock ? */
return;
default:
#ifdef IPX
if (ef_inputp && ef_inputp(ifp, eh, m) == 0)
@ -796,14 +843,14 @@ post_stats:
#endif /* NS */
#if defined(NETATALK)
if (ether_type > ETHERMTU)
goto dropanyway;
goto discard;
l = mtod(m, struct llc *);
switch (l->llc_dsap) {
case LLC_SNAP_LSAP:
switch (l->llc_control) {
case LLC_UI:
if (l->llc_ssap != LLC_SNAP_LSAP)
goto dropanyway;
goto discard;
if (Bcmp(&(l->llc_snap_org_code)[0], at_org_code,
sizeof(at_org_code)) == 0 &&
@ -823,57 +870,88 @@ post_stats:
}
default:
goto dropanyway;
goto discard;
}
break;
dropanyway:
default:
if (ng_ether_input_orphan_p != NULL)
(*ng_ether_input_orphan_p)(ifp, m, eh);
else
m_freem(m);
return;
goto discard;
}
#else /* NETATALK */
if (ng_ether_input_orphan_p != NULL)
(*ng_ether_input_orphan_p)(ifp, m, eh);
else
m_freem(m);
return;
goto discard;
#endif /* NETATALK */
}
(void) IF_HANDOFF(inq, m, NULL);
return;
discard:
/*
* Packet is to be discarded. If netgraph is present,
* hand the packet to it for last chance processing;
* otherwise dispose of it.
*/
if (ng_ether_input_orphan_p != NULL) {
/*
* Put back the ethernet header so netgraph has a
* consistent view of inbound packets.
*/
M_PREPEND(m, sizeof (struct ether_header), M_NOWAIT);
(*ng_ether_input_orphan_p)(ifp, m);
return;
}
m_freem(m);
}
/*
* Convert Ethernet address to printable (loggable) representation.
* This routine is for compatibility; it's better to just use
*
* printf("%6D", <pointer to address>, ":");
*
* since there's no static buffer involved.
*/
char *
ether_sprintf(const u_char *ap)
{
static char etherbuf[18];
snprintf(etherbuf, sizeof (etherbuf), "%6D", ap, ":");
return (etherbuf);
}
/*
* Perform common duties while attaching to interface list
*/
void
ether_ifattach(ifp, bpf)
register struct ifnet *ifp;
int bpf;
ether_ifattach(struct ifnet *ifp, const u_int8_t *llc)
{
register struct ifaddr *ifa;
register struct sockaddr_dl *sdl;
ifp->if_type = IFT_ETHER;
ifp->if_addrlen = 6;
ifp->if_hdrlen = 14;
ifp->if_addrlen = ETHER_ADDR_LEN;
ifp->if_hdrlen = ETHER_HDR_LEN;
if_attach(ifp);
ifp->if_mtu = ETHERMTU;
ifp->if_output = ether_output;
ifp->if_input = ether_input;
ifp->if_resolvemulti = ether_resolvemulti;
if (ifp->if_baudrate == 0)
ifp->if_baudrate = 10000000;
ifp->if_baudrate = IF_Mbps(10); /* just a default */
ifp->if_broadcastaddr = etherbroadcastaddr;
ifa = ifaddr_byindex(ifp->if_index);
KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
sdl = (struct sockaddr_dl *)ifa->ifa_addr;
sdl->sdl_type = IFT_ETHER;
sdl->sdl_alen = ifp->if_addrlen;
bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
if (bpf)
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
bcopy(llc, LLADDR(sdl), ifp->if_addrlen);
/*
* XXX: This doesn't belong here; we do it until
* XXX: all drivers are cleaned up
*/
if (llc != IFP2AC(ifp)->ac_enaddr)
bcopy(llc, IFP2AC(ifp)->ac_enaddr, ifp->if_addrlen);
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
if (ng_ether_attach_p != NULL)
(*ng_ether_attach_p)(ifp);
if (BDG_LOADED)
@ -884,14 +962,11 @@ ether_ifattach(ifp, bpf)
* Perform common duties while detaching an Ethernet interface
*/
void
ether_ifdetach(ifp, bpf)
struct ifnet *ifp;
int bpf;
ether_ifdetach(struct ifnet *ifp)
{
if (ng_ether_detach_p != NULL)
(*ng_ether_detach_p)(ifp);
if (bpf)
bpfdetach(ifp);
bpfdetach(ifp);
if_detach(ifp);
if (BDG_LOADED)
bdgtakeifaces_ptr();
@ -1000,6 +1075,9 @@ ether_ioctl(ifp, command, data)
ifp->if_mtu = ifr->ifr_mtu;
}
break;
default:
error = EINVAL; /* XXX netbsd has ENOTTY??? */
break;
}
return (error);
}