diff options
Diffstat (limited to 'usr/src/uts/common/io/mac')
-rw-r--r-- | usr/src/uts/common/io/mac/mac_provider.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/mac/mac_provider.c b/usr/src/uts/common/io/mac/mac_provider.c index 0f917cd8ca..7f193f68eb 100644 --- a/usr/src/uts/common/io/mac/mac_provider.c +++ b/usr/src/uts/common/io/mac/mac_provider.c @@ -58,6 +58,10 @@ #include <sys/pattr.h> #include <sys/strsun.h> #include <sys/vlan.h> +#include <inet/ip.h> +#include <inet/tcp.h> +#include <netinet/udp.h> +#include <netinet/sctp.h> /* * MAC Provider Interface. @@ -1653,3 +1657,155 @@ mac_transceiver_info_set_usable(mac_transceiver_info_t *infop, { infop->mti_usable = usable; } + +/* + * We should really keep track of our offset and not walk everything every + * time. I can't imagine that this will be kind to us at high packet rates; + * however, for the moment, let's leave that. + * + * This walks a message block chain without pulling up to fill in the context + * information. Note that the data we care about could be hidden across more + * than one mblk_t. + */ +static int +mac_meoi_get_uint8(mblk_t *mp, off_t off, uint8_t *out) +{ + size_t mpsize; + uint8_t *bp; + + mpsize = msgsize(mp); + /* Check for overflow */ + if (off + sizeof (uint16_t) > mpsize) + return (-1); + + mpsize = MBLKL(mp); + while (off >= mpsize) { + mp = mp->b_cont; + off -= mpsize; + mpsize = MBLKL(mp); + } + + bp = mp->b_rptr + off; + *out = *bp; + return (0); + +} + +static int +mac_meoi_get_uint16(mblk_t *mp, off_t off, uint16_t *out) +{ + size_t mpsize; + uint8_t *bp; + + mpsize = msgsize(mp); + /* Check for overflow */ + if (off + sizeof (uint16_t) > mpsize) + return (-1); + + mpsize = MBLKL(mp); + while (off >= mpsize) { + mp = mp->b_cont; + off -= mpsize; + mpsize = MBLKL(mp); + } + + /* + * Data is in network order. Note the second byte of data might be in + * the next mp. + */ + bp = mp->b_rptr + off; + *out = *bp << 8; + if (off + 1 == mpsize) { + mp = mp->b_cont; + bp = mp->b_rptr; + } else { + bp++; + } + + *out |= *bp; + return (0); + +} + + +int +mac_ether_offload_info(mblk_t *mp, mac_ether_offload_info_t *meoi) +{ + size_t off; + uint16_t ether; + uint8_t ipproto, iplen, l4len, maclen; + + bzero(meoi, sizeof (mac_ether_offload_info_t)); + + meoi->meoi_len = msgsize(mp); + off = offsetof(struct ether_header, ether_type); + if (mac_meoi_get_uint16(mp, off, ðer) != 0) + return (-1); + + if (ether == ETHERTYPE_VLAN) { + off = offsetof(struct ether_vlan_header, ether_type); + if (mac_meoi_get_uint16(mp, off, ðer) != 0) + return (-1); + meoi->meoi_flags |= MEOI_VLAN_TAGGED; + maclen = sizeof (struct ether_vlan_header); + } else { + maclen = sizeof (struct ether_header); + } + meoi->meoi_flags |= MEOI_L2INFO_SET; + meoi->meoi_l2hlen = maclen; + meoi->meoi_l3proto = ether; + + switch (ether) { + case ETHERTYPE_IP: + /* + * For IPv4 we need to get the length of the header, as it can + * be variable. + */ + off = offsetof(ipha_t, ipha_version_and_hdr_length) + maclen; + if (mac_meoi_get_uint8(mp, off, &iplen) != 0) + return (-1); + iplen &= 0x0f; + if (iplen < 5 || iplen > 0x0f) + return (-1); + iplen *= 4; + off = offsetof(ipha_t, ipha_protocol) + maclen; + if (mac_meoi_get_uint8(mp, off, &ipproto) == -1) + return (-1); + break; + case ETHERTYPE_IPV6: + iplen = 40; + off = offsetof(ip6_t, ip6_nxt) + maclen; + if (mac_meoi_get_uint8(mp, off, &ipproto) == -1) + return (-1); + break; + default: + return (0); + } + meoi->meoi_l3hlen = iplen; + meoi->meoi_l4proto = ipproto; + meoi->meoi_flags |= MEOI_L3INFO_SET; + + switch (ipproto) { + case IPPROTO_TCP: + off = offsetof(tcph_t, th_offset_and_rsrvd) + maclen + iplen; + if (mac_meoi_get_uint8(mp, off, &l4len) == -1) + return (-1); + l4len = (l4len & 0xf0) >> 4; + if (l4len < 5 || l4len > 0xf) + return (-1); + l4len *= 4; + break; + case IPPROTO_UDP: + l4len = sizeof (struct udphdr); + break; + case IPPROTO_SCTP: + l4len = sizeof (sctp_hdr_t); + break; + default: + return (0); + } + + meoi->meoi_l4hlen = l4len; + meoi->meoi_flags |= MEOI_L4INFO_SET; + return (0); +} |