inet.cc revision 13449
12SN/A/*
29955SGeoffrey.Blake@arm.com * Copyright (c) 2013 ARM Limited
39955SGeoffrey.Blake@arm.com * All rights reserved
49955SGeoffrey.Blake@arm.com *
59955SGeoffrey.Blake@arm.com * The license below extends only to copyright in the software and shall
69955SGeoffrey.Blake@arm.com * not be construed as granting a license to any other intellectual
79955SGeoffrey.Blake@arm.com * property including but not limited to intellectual property relating
89955SGeoffrey.Blake@arm.com * to a hardware implementation of the functionality of the software
99955SGeoffrey.Blake@arm.com * licensed hereunder.  You may use the software subject to the license
109955SGeoffrey.Blake@arm.com * terms below provided that you ensure that this notice is replicated
119955SGeoffrey.Blake@arm.com * unmodified and in its entirety in all distributions of the software,
129955SGeoffrey.Blake@arm.com * modified or unmodified, in source code or in binary form.
139955SGeoffrey.Blake@arm.com *
141762SN/A * Copyright (c) 2002-2005 The Regents of The University of Michigan
157778Sgblack@eecs.umich.edu * Copyright (c) 2010 Advanced Micro Devices, Inc.
162SN/A * All rights reserved.
172SN/A *
182SN/A * Redistribution and use in source and binary forms, with or without
192SN/A * modification, are permitted provided that the following conditions are
202SN/A * met: redistributions of source code must retain the above copyright
212SN/A * notice, this list of conditions and the following disclaimer;
222SN/A * redistributions in binary form must reproduce the above copyright
232SN/A * notice, this list of conditions and the following disclaimer in the
242SN/A * documentation and/or other materials provided with the distribution;
252SN/A * neither the name of the copyright holders nor the names of its
262SN/A * contributors may be used to endorse or promote products derived from
272SN/A * this software without specific prior written permission.
282SN/A *
292SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
302SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
312SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
322SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
332SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
342SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
352SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
362SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
372SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
382SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
392SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
402665Ssaidi@eecs.umich.edu *
412665Ssaidi@eecs.umich.edu * Authors: Nathan Binkert
427778Sgblack@eecs.umich.edu *          Gabe Black
439955SGeoffrey.Blake@arm.com *          Geoffrey Blake
442SN/A */
452SN/A
4611793Sbrandon.potter@amd.com#include "base/inet.hh"
4711793Sbrandon.potter@amd.com
488607Sgblack@eecs.umich.edu#include <cstddef>
496712Snate@binkert.org#include <cstdio>
502SN/A#include <sstream>
512SN/A#include <string>
522SN/A
5356SN/A#include "base/cprintf.hh"
5413449Sgabeblack@google.com#include "base/logging.hh"
556214Snate@binkert.org#include "base/types.hh"
562SN/A
571078SN/Ausing namespace std;
581114SN/Anamespace Net {
591114SN/A
601114SN/AEthAddr::EthAddr()
611114SN/A{
621114SN/A    memset(data, 0, ETH_ADDR_LEN);
631114SN/A}
641114SN/A
651114SN/AEthAddr::EthAddr(const uint8_t ea[ETH_ADDR_LEN])
661114SN/A{
6710251Satgutier@umich.edu    for (int i = 0; i < ETH_ADDR_LEN; ++i)
6810251Satgutier@umich.edu        data[i] = ea[i];
691114SN/A}
701114SN/A
711114SN/AEthAddr::EthAddr(const eth_addr &ea)
721114SN/A{
7310251Satgutier@umich.edu    for (int i = 0; i < ETH_ADDR_LEN; ++i)
7410251Satgutier@umich.edu        data[i] = ea.data[i];
751114SN/A}
761114SN/A
771114SN/AEthAddr::EthAddr(const std::string &addr)
781114SN/A{
791114SN/A    parse(addr);
801114SN/A}
811114SN/A
821114SN/Aconst EthAddr &
831114SN/AEthAddr::operator=(const eth_addr &ea)
841114SN/A{
851114SN/A    *data = *ea.data;
861114SN/A    return *this;
871114SN/A}
881114SN/A
891114SN/Aconst EthAddr &
901114SN/AEthAddr::operator=(const std::string &addr)
911114SN/A{
921114SN/A    parse(addr);
931114SN/A    return *this;
941114SN/A}
951114SN/A
961114SN/Avoid
971114SN/AEthAddr::parse(const std::string &addr)
981114SN/A{
991114SN/A    // the hack below is to make sure that ETH_ADDR_LEN is 6 otherwise
1001114SN/A    // the sscanf function won't work.
1011114SN/A    int bytes[ETH_ADDR_LEN == 6 ? ETH_ADDR_LEN : -1];
1021114SN/A    if (sscanf(addr.c_str(), "%x:%x:%x:%x:%x:%x", &bytes[0], &bytes[1],
1031114SN/A               &bytes[2], &bytes[3], &bytes[4], &bytes[5]) != ETH_ADDR_LEN) {
1041114SN/A        memset(data, 0xff, ETH_ADDR_LEN);
1051114SN/A        return;
1061114SN/A    }
1071114SN/A
1081114SN/A    for (int i = 0; i < ETH_ADDR_LEN; ++i) {
1091114SN/A        if (bytes[i] & ~0xff) {
1101114SN/A            memset(data, 0xff, ETH_ADDR_LEN);
1111114SN/A            return;
1121114SN/A        }
1131114SN/A
1141114SN/A        data[i] = bytes[i];
1151114SN/A    }
1161114SN/A}
1171114SN/A
1182SN/Astring
1191114SN/AEthAddr::string() const
1202SN/A{
1212SN/A    stringstream stream;
1221114SN/A    stream << *this;
1232SN/A    return stream.str();
1242SN/A}
1252SN/A
1261114SN/Abool
1271114SN/Aoperator==(const EthAddr &left, const EthAddr &right)
1282SN/A{
12910252Satgutier@umich.edu    return !memcmp(left.bytes(), right.bytes(), ETH_ADDR_LEN);
1301114SN/A}
1311114SN/A
1321114SN/Aostream &
1331114SN/Aoperator<<(ostream &stream, const EthAddr &ea)
1341114SN/A{
1351114SN/A    const uint8_t *a = ea.addr();
1361114SN/A    ccprintf(stream, "%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5]);
1371114SN/A    return stream;
1382SN/A}
1392SN/A
1407777Sgblack@eecs.umich.edustring
1417777Sgblack@eecs.umich.eduIpAddress::string() const
1427777Sgblack@eecs.umich.edu{
1437777Sgblack@eecs.umich.edu    stringstream stream;
1447777Sgblack@eecs.umich.edu    stream << *this;
1457777Sgblack@eecs.umich.edu    return stream.str();
1467777Sgblack@eecs.umich.edu}
1477777Sgblack@eecs.umich.edu
1487777Sgblack@eecs.umich.edubool
1497777Sgblack@eecs.umich.eduoperator==(const IpAddress &left, const IpAddress &right)
1507777Sgblack@eecs.umich.edu{
1517777Sgblack@eecs.umich.edu    return left.ip() == right.ip();
1527777Sgblack@eecs.umich.edu}
1537777Sgblack@eecs.umich.edu
1547777Sgblack@eecs.umich.eduostream &
1557777Sgblack@eecs.umich.eduoperator<<(ostream &stream, const IpAddress &ia)
1567777Sgblack@eecs.umich.edu{
1577777Sgblack@eecs.umich.edu    uint32_t ip = ia.ip();
1587777Sgblack@eecs.umich.edu    ccprintf(stream, "%x.%x.%x.%x",
1597814Sgblack@eecs.umich.edu            (uint8_t)(ip >> 24), (uint8_t)(ip >> 16),
1607814Sgblack@eecs.umich.edu            (uint8_t)(ip >> 8),  (uint8_t)(ip >> 0));
1617777Sgblack@eecs.umich.edu    return stream;
1627777Sgblack@eecs.umich.edu}
1637777Sgblack@eecs.umich.edu
1647777Sgblack@eecs.umich.edustring
1657777Sgblack@eecs.umich.eduIpNetmask::string() const
1667777Sgblack@eecs.umich.edu{
1677777Sgblack@eecs.umich.edu    stringstream stream;
1687777Sgblack@eecs.umich.edu    stream << *this;
1697777Sgblack@eecs.umich.edu    return stream.str();
1707777Sgblack@eecs.umich.edu}
1717777Sgblack@eecs.umich.edu
1727777Sgblack@eecs.umich.edubool
1737777Sgblack@eecs.umich.eduoperator==(const IpNetmask &left, const IpNetmask &right)
1747777Sgblack@eecs.umich.edu{
1757777Sgblack@eecs.umich.edu    return (left.ip() == right.ip()) &&
1767777Sgblack@eecs.umich.edu        (left.netmask() == right.netmask());
1777777Sgblack@eecs.umich.edu}
1787777Sgblack@eecs.umich.edu
1797777Sgblack@eecs.umich.eduostream &
1807777Sgblack@eecs.umich.eduoperator<<(ostream &stream, const IpNetmask &in)
1817777Sgblack@eecs.umich.edu{
1827777Sgblack@eecs.umich.edu    ccprintf(stream, "%s/%d", (const IpAddress &)in, in.netmask());
1837777Sgblack@eecs.umich.edu    return stream;
1847777Sgblack@eecs.umich.edu}
1857777Sgblack@eecs.umich.edu
1867777Sgblack@eecs.umich.edustring
1877777Sgblack@eecs.umich.eduIpWithPort::string() const
1887777Sgblack@eecs.umich.edu{
1897777Sgblack@eecs.umich.edu    stringstream stream;
1907777Sgblack@eecs.umich.edu    stream << *this;
1917777Sgblack@eecs.umich.edu    return stream.str();
1927777Sgblack@eecs.umich.edu}
1937777Sgblack@eecs.umich.edu
1947777Sgblack@eecs.umich.edubool
1957777Sgblack@eecs.umich.eduoperator==(const IpWithPort &left, const IpWithPort &right)
1967777Sgblack@eecs.umich.edu{
1977777Sgblack@eecs.umich.edu    return (left.ip() == right.ip()) && (left.port() == right.port());
1987777Sgblack@eecs.umich.edu}
1997777Sgblack@eecs.umich.edu
2007777Sgblack@eecs.umich.eduostream &
2017777Sgblack@eecs.umich.eduoperator<<(ostream &stream, const IpWithPort &iwp)
2027777Sgblack@eecs.umich.edu{
2037777Sgblack@eecs.umich.edu    ccprintf(stream, "%s:%d", (const IpAddress &)iwp, iwp.port());
2047777Sgblack@eecs.umich.edu    return stream;
2057777Sgblack@eecs.umich.edu}
2067777Sgblack@eecs.umich.edu
2071078SN/Auint16_t
2081114SN/Acksum(const IpPtr &ptr)
2091078SN/A{
2101114SN/A    int sum = ip_cksum_add(ptr->bytes(), ptr->hlen(), 0);
2111114SN/A    return ip_cksum_carry(sum);
2121078SN/A}
2131114SN/A
2141114SN/Auint16_t
2151114SN/A__tu_cksum(const IpPtr &ip)
2161114SN/A{
2171114SN/A    int tcplen = ip->len() - ip->hlen();
2181114SN/A    int sum = ip_cksum_add(ip->payload(), tcplen, 0);
2191114SN/A    sum = ip_cksum_add(&ip->ip_src, 8, sum); // source and destination
2201114SN/A    sum += htons(ip->ip_p + tcplen);
2211114SN/A    return ip_cksum_carry(sum);
2221114SN/A}
2231114SN/A
2241114SN/Auint16_t
2259955SGeoffrey.Blake@arm.com__tu_cksum6(const Ip6Ptr &ip6)
2269955SGeoffrey.Blake@arm.com{
2279955SGeoffrey.Blake@arm.com   int tcplen = ip6->plen() - ip6->extensionLength();
2289955SGeoffrey.Blake@arm.com   int sum = ip_cksum_add(ip6->payload(), tcplen, 0);
2299955SGeoffrey.Blake@arm.com   sum = ip_cksum_add(ip6->src(), 32, sum);
2309955SGeoffrey.Blake@arm.com   sum += htons(ip6->proto() + tcplen);
2319955SGeoffrey.Blake@arm.com   return ip_cksum_carry(sum);
2329955SGeoffrey.Blake@arm.com}
2339955SGeoffrey.Blake@arm.com
2349955SGeoffrey.Blake@arm.comuint16_t
2351114SN/Acksum(const TcpPtr &tcp)
2369955SGeoffrey.Blake@arm.com{
2379955SGeoffrey.Blake@arm.com    if (IpPtr(tcp.packet())) {
2389955SGeoffrey.Blake@arm.com        return __tu_cksum(IpPtr(tcp.packet()));
2399955SGeoffrey.Blake@arm.com    } else if (Ip6Ptr(tcp.packet())) {
2409955SGeoffrey.Blake@arm.com        return __tu_cksum6(Ip6Ptr(tcp.packet()));
2419955SGeoffrey.Blake@arm.com    } else {
24213449Sgabeblack@google.com        panic("Unrecognized IP packet format");
2439955SGeoffrey.Blake@arm.com    }
2449955SGeoffrey.Blake@arm.com    // Should never reach here
2459955SGeoffrey.Blake@arm.com    return 0;
2469955SGeoffrey.Blake@arm.com}
2471114SN/A
2481114SN/Auint16_t
2491114SN/Acksum(const UdpPtr &udp)
2509955SGeoffrey.Blake@arm.com{
2519955SGeoffrey.Blake@arm.com    if (IpPtr(udp.packet())) {
2529955SGeoffrey.Blake@arm.com        return __tu_cksum(IpPtr(udp.packet()));
2539955SGeoffrey.Blake@arm.com    } else if (Ip6Ptr(udp.packet())) {
2549955SGeoffrey.Blake@arm.com        return __tu_cksum6(Ip6Ptr(udp.packet()));
2559955SGeoffrey.Blake@arm.com    } else {
25613449Sgabeblack@google.com        panic("Unrecognized IP packet format");
2579955SGeoffrey.Blake@arm.com    }
2589955SGeoffrey.Blake@arm.com    return 0;
2599955SGeoffrey.Blake@arm.com}
2601114SN/A
2611114SN/Abool
2621114SN/AIpHdr::options(vector<const IpOpt *> &vec) const
2631114SN/A{
2641114SN/A    vec.clear();
2651114SN/A
2661114SN/A    const uint8_t *data = bytes() + sizeof(struct ip_hdr);
2671114SN/A    int all = hlen() - sizeof(struct ip_hdr);
2681114SN/A    while (all > 0) {
2691114SN/A        const IpOpt *opt = (const IpOpt *)data;
2701114SN/A        int len = opt->len();
2711114SN/A        if (all < len)
2721114SN/A            return false;
2731114SN/A
2741114SN/A        vec.push_back(opt);
2751114SN/A        all -= len;
2761114SN/A        data += len;
2771114SN/A    }
2781114SN/A
2791114SN/A    return true;
2801114SN/A}
2811114SN/A
2829955SGeoffrey.Blake@arm.com#define IP6_EXTENSION(nxt) (nxt == IP_PROTO_HOPOPTS) ? true : \
2839955SGeoffrey.Blake@arm.com                           (nxt == IP_PROTO_ROUTING) ? true : \
2849955SGeoffrey.Blake@arm.com                           (nxt == IP_PROTO_FRAGMENT) ? true : \
2859955SGeoffrey.Blake@arm.com                           (nxt == IP_PROTO_AH) ? true : \
2869955SGeoffrey.Blake@arm.com                           (nxt == IP_PROTO_ESP) ? true: \
2879955SGeoffrey.Blake@arm.com                           (nxt == IP_PROTO_DSTOPTS) ? true : false
2889955SGeoffrey.Blake@arm.com
2899955SGeoffrey.Blake@arm.com/* Scan the IP6 header for all header extensions
2909955SGeoffrey.Blake@arm.com * and return the number of headers found
2919955SGeoffrey.Blake@arm.com */
2929955SGeoffrey.Blake@arm.comint
2939955SGeoffrey.Blake@arm.comIp6Hdr::extensionLength() const
2949955SGeoffrey.Blake@arm.com{
2959955SGeoffrey.Blake@arm.com    const uint8_t *data = bytes() + IP6_HDR_LEN;
2969955SGeoffrey.Blake@arm.com    uint8_t nxt = ip6_nxt;
2979955SGeoffrey.Blake@arm.com    int len = 0;
2989955SGeoffrey.Blake@arm.com    int all = plen();
2999955SGeoffrey.Blake@arm.com
3009955SGeoffrey.Blake@arm.com    while (IP6_EXTENSION(nxt)) {
3019955SGeoffrey.Blake@arm.com        const Ip6Opt *ext = (const Ip6Opt *)data;
3029955SGeoffrey.Blake@arm.com        nxt = ext->nxt();
3039955SGeoffrey.Blake@arm.com        len += ext->len();
3049955SGeoffrey.Blake@arm.com        data += ext->len();
3059955SGeoffrey.Blake@arm.com        all -= ext->len();
3069955SGeoffrey.Blake@arm.com        assert(all >= 0);
3079955SGeoffrey.Blake@arm.com    }
3089955SGeoffrey.Blake@arm.com    return len;
3099955SGeoffrey.Blake@arm.com}
3109955SGeoffrey.Blake@arm.com
3119955SGeoffrey.Blake@arm.com/* Scan the IP6 header for a particular extension
3129955SGeoffrey.Blake@arm.com * header type and return a pointer to it if it
3139955SGeoffrey.Blake@arm.com * exists, otherwise return NULL
3149955SGeoffrey.Blake@arm.com */
3159955SGeoffrey.Blake@arm.comconst Ip6Opt*
3169955SGeoffrey.Blake@arm.comIp6Hdr::getExt(uint8_t ext_type) const
3179955SGeoffrey.Blake@arm.com{
3189955SGeoffrey.Blake@arm.com    const uint8_t *data = bytes() + IP6_HDR_LEN;
3199955SGeoffrey.Blake@arm.com    uint8_t nxt = ip6_nxt;
3209955SGeoffrey.Blake@arm.com    Ip6Opt* opt = NULL;
3219955SGeoffrey.Blake@arm.com    int all = plen();
3229955SGeoffrey.Blake@arm.com
3239955SGeoffrey.Blake@arm.com    while (IP6_EXTENSION(nxt)) {
3249955SGeoffrey.Blake@arm.com        opt = (Ip6Opt *)data;
3259955SGeoffrey.Blake@arm.com        if (nxt == ext_type) {
3269955SGeoffrey.Blake@arm.com            break;
3279955SGeoffrey.Blake@arm.com        }
3289955SGeoffrey.Blake@arm.com        nxt = opt->nxt();
3299955SGeoffrey.Blake@arm.com        data += opt->len();
3309955SGeoffrey.Blake@arm.com        all -= opt->len();
3319955SGeoffrey.Blake@arm.com        opt = NULL;
3329955SGeoffrey.Blake@arm.com        assert(all >= 0);
3339955SGeoffrey.Blake@arm.com    }
3349955SGeoffrey.Blake@arm.com    return (const Ip6Opt*)opt;
3359955SGeoffrey.Blake@arm.com}
3369955SGeoffrey.Blake@arm.com
3379955SGeoffrey.Blake@arm.com/* Scan the IP6 header and any extension headers
3389955SGeoffrey.Blake@arm.com * to find what type of Layer 4 header exists
3399955SGeoffrey.Blake@arm.com * after this header
3409955SGeoffrey.Blake@arm.com */
3419955SGeoffrey.Blake@arm.comuint8_t
3429955SGeoffrey.Blake@arm.comIp6Hdr::proto() const
3439955SGeoffrey.Blake@arm.com{
3449955SGeoffrey.Blake@arm.com    const uint8_t *data = bytes() + IP6_HDR_LEN;
3459955SGeoffrey.Blake@arm.com    uint8_t nxt = ip6_nxt;
3469955SGeoffrey.Blake@arm.com    int all = plen();
3479955SGeoffrey.Blake@arm.com
3489955SGeoffrey.Blake@arm.com    while (IP6_EXTENSION(nxt)) {
3499955SGeoffrey.Blake@arm.com        const Ip6Opt *ext = (const Ip6Opt *)data;
3509955SGeoffrey.Blake@arm.com        nxt = ext->nxt();
3519955SGeoffrey.Blake@arm.com        data += ext->len();
3529955SGeoffrey.Blake@arm.com        all -= ext->len();
3539955SGeoffrey.Blake@arm.com        assert(all >= 0);
3549955SGeoffrey.Blake@arm.com    }
3559955SGeoffrey.Blake@arm.com    return nxt;
3569955SGeoffrey.Blake@arm.com}
3579955SGeoffrey.Blake@arm.com
3581114SN/Abool
3591114SN/ATcpHdr::options(vector<const TcpOpt *> &vec) const
3601114SN/A{
3611114SN/A    vec.clear();
3621114SN/A
3631114SN/A    const uint8_t *data = bytes() + sizeof(struct tcp_hdr);
3641114SN/A    int all = off() - sizeof(struct tcp_hdr);
3651114SN/A    while (all > 0) {
3661114SN/A        const TcpOpt *opt = (const TcpOpt *)data;
3671114SN/A        int len = opt->len();
3681114SN/A        if (all < len)
3691114SN/A            return false;
3701114SN/A
3711114SN/A        vec.push_back(opt);
3721114SN/A        all -= len;
3731114SN/A        data += len;
3741114SN/A    }
3751114SN/A
3761114SN/A    return true;
3771114SN/A}
3781114SN/A
37911320Ssteve.reinhardt@amd.comint
3805782Ssaidi@eecs.umich.eduhsplit(const EthPacketPtr &ptr)
3815782Ssaidi@eecs.umich.edu{
3825782Ssaidi@eecs.umich.edu    int split_point = 0;
3835782Ssaidi@eecs.umich.edu
3845782Ssaidi@eecs.umich.edu    IpPtr ip(ptr);
3859955SGeoffrey.Blake@arm.com    Ip6Ptr ip6(ptr);
3865782Ssaidi@eecs.umich.edu    if (ip) {
3875782Ssaidi@eecs.umich.edu        split_point = ip.pstart();
3885782Ssaidi@eecs.umich.edu
3895782Ssaidi@eecs.umich.edu        TcpPtr tcp(ip);
3905782Ssaidi@eecs.umich.edu        if (tcp)
3915782Ssaidi@eecs.umich.edu            split_point = tcp.pstart();
3925782Ssaidi@eecs.umich.edu
3935782Ssaidi@eecs.umich.edu        UdpPtr udp(ip);
3945782Ssaidi@eecs.umich.edu        if (udp)
3955782Ssaidi@eecs.umich.edu            split_point = udp.pstart();
3969955SGeoffrey.Blake@arm.com    } else if (ip6) {
3979955SGeoffrey.Blake@arm.com        split_point = ip6.pstart();
3989955SGeoffrey.Blake@arm.com
3999955SGeoffrey.Blake@arm.com        TcpPtr tcp(ip6);
4009955SGeoffrey.Blake@arm.com        if (tcp)
4019955SGeoffrey.Blake@arm.com            split_point = tcp.pstart();
4029955SGeoffrey.Blake@arm.com        UdpPtr udp(ip6);
4039955SGeoffrey.Blake@arm.com        if (udp)
4049955SGeoffrey.Blake@arm.com            split_point = udp.pstart();
4055782Ssaidi@eecs.umich.edu    }
4065782Ssaidi@eecs.umich.edu    return split_point;
4075782Ssaidi@eecs.umich.edu}
4085782Ssaidi@eecs.umich.edu
4095782Ssaidi@eecs.umich.edu
4107811Ssteve.reinhardt@amd.com} // namespace Net
411