pre_test.cpp revision 21d1e70d
1/*
2  Ido Barnea
3  Cisco Systems, Inc.
4*/
5
6/*
7  Copyright (c) 2016-2017 Cisco Systems, Inc.
8
9  Licensed under the Apache License, Version 2.0 (the "License");
10  you may not use this file except in compliance with the License.
11  You may obtain a copy of the License at
12
13  http://www.apache.org/licenses/LICENSE-2.0
14
15  Unless required by applicable law or agreed to in writing, software
16  distributed under the License is distributed on an "AS IS" BASIS,
17  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  See the License for the specific language governing permissions and
19  limitations under the License.
20*/
21
22#include <rte_ethdev.h>
23#include <arpa/inet.h>
24#include <common/Network/Packet/EthernetHeader.h>
25#include <common/Network/Packet/Arp.h>
26#include "common/Network/Packet/VLANHeader.h"
27#include "common/basic_utils.h"
28#include "bp_sim.h"
29#include "main_dpdk.h"
30#include "pkt_gen.h"
31#include "pre_test.h"
32
33CPretestOnePortInfo::CPretestOnePortInfo() {
34    m_state = RESOLVE_NOT_NEEDED;
35    m_is_loopback = false;
36    m_stats.clear();
37}
38
39CPretestOnePortInfo::~CPretestOnePortInfo() {
40    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
41        delete *it;
42    }
43    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
44        delete *it;
45    }
46}
47
48void CPretestOnePortInfo::add_src(uint32_t ip, uint16_t vlan, MacAddress mac) {
49    COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, mac);
50    assert(one_ip);
51    m_src_info.push_back(one_ip);
52}
53
54void CPretestOnePortInfo::add_dst(uint32_t ip, uint16_t vlan) {
55    MacAddress default_mac;
56    COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, default_mac);
57    assert(one_ip);
58    m_dst_info.push_back(one_ip);
59    m_state = RESOLVE_NEEDED;
60}
61
62void CPretestOnePortInfo::add_src(uint16_t ip[8], uint16_t vlan, MacAddress mac) {
63    COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, mac);
64    assert(one_ip);
65    m_src_info.push_back(one_ip);
66}
67
68void CPretestOnePortInfo::add_dst(uint16_t ip[8], uint16_t vlan) {
69    MacAddress default_mac;
70    COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, default_mac);
71    assert(one_ip);
72    m_dst_info.push_back(one_ip);
73    m_state = RESOLVE_NEEDED;
74}
75
76void CPretestOnePortInfo::dump(FILE *fd, char *offset) {
77    std::string new_offset = std::string(offset) + "  ";
78
79    if (m_is_loopback) {
80        fprintf(fd, "%sPort connected in loopback\n", offset);
81    }
82    fprintf(fd, "%sSources:\n", offset);
83    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
84        (*it)->dump(fd, new_offset.c_str());
85    }
86    fprintf(fd, "%sDestinations:\n", offset);
87    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
88        (*it)->dump(fd, new_offset.c_str());
89    }
90}
91
92/*
93 * Get appropriate source for given vlan and ip version.
94 */
95COneIPInfo *CPretestOnePortInfo::get_src(uint16_t vlan, uint8_t ip_ver) {
96    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
97        if ((ip_ver == (*it)->ip_ver()) && (vlan == (*it)->get_vlan()))
98            return (*it);
99    }
100
101    return NULL;
102}
103
104COneIPv4Info *CPretestOnePortInfo::find_ip(uint32_t ip, uint16_t vlan) {
105    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
106        if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip))
107            return (COneIPv4Info *) *it;
108    }
109
110    return NULL;
111}
112
113COneIPv4Info *CPretestOnePortInfo::find_next_hop(uint32_t ip, uint16_t vlan) {
114
115    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
116        if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip))
117            return (COneIPv4Info *) *it;
118    }
119
120    return NULL;
121}
122
123COneIPv6Info *CPretestOnePortInfo::find_ipv6(uint16_t ip[8], uint16_t vlan) {
124    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
125        if (((*it)->ip_ver() == COneIPInfo::IP6_VER) && ((*it)->get_vlan() == vlan)
126            && (! memcmp((uint8_t *) ((COneIPv6Info *) (*it))->get_ipv6(), (uint8_t *)ip, 2*8 /* ???*/ ) ) )
127            return (COneIPv6Info *) *it;
128    }
129
130    return NULL;
131}
132
133bool CPretestOnePortInfo::get_mac(COneIPInfo *ip, uint8_t *mac) {
134    MacAddress defaultmac;
135
136    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
137        if (ip->ip_ver() != (*it)->ip_ver())
138            continue;
139
140        switch(ip->ip_ver()) {
141        case 4:
142            if (*((COneIPv4Info *) (*it)) != *((COneIPv4Info *) ip))
143                continue;
144            break;
145        case 6:
146            if (*((COneIPv6Info *) (*it)) != *((COneIPv6Info *) ip))
147                continue;
148            break;
149        default:
150            assert(0);
151        }
152
153        (*it)->get_mac(mac);
154        if (! memcmp(mac, defaultmac.GetConstBuffer(), ETHER_ADDR_LEN)) {
155            return false;
156        } else {
157            return true;
158        }
159    }
160
161    return false;
162}
163
164bool CPretestOnePortInfo::get_mac(uint32_t ip, uint16_t vlan, uint8_t *mac) {
165    COneIPv4Info one_ip(ip, vlan);
166
167    return get_mac(&one_ip, mac);
168}
169
170bool CPretestOnePortInfo::get_mac(uint16_t ip[8], uint16_t vlan, uint8_t *mac) {
171    COneIPv6Info one_ip(ip, vlan);
172
173    return get_mac(&one_ip, mac);
174}
175
176// return true if there are still any addresses to resolve on this port
177bool CPretestOnePortInfo::resolve_needed() {
178    if (m_state == RESOLVE_NOT_NEEDED)
179        return false;
180
181    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
182        if ((*it)->resolve_needed())
183            return true;
184    }
185
186    m_state = RESOLVE_NOT_NEEDED;
187    return false;
188}
189
190void CPretestOnePortInfo::send_arp_req_all() {
191    for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) {
192        rte_mbuf_t *m[1];
193        int num_sent;
194        int verbose = CGlobalInfo::m_options.preview.getVMode();
195
196        m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id);
197        if ( unlikely(m[0] == 0) )  {
198            fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", m_port_id);
199            exit(1);
200        }
201
202        uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_arp_req_len());
203        // We need source on the same VLAN of the dest in order to send
204        COneIPInfo *sip = get_src((*it)->get_vlan(), (*it)->ip_ver());
205        if (sip == NULL) {
206            fprintf(stderr, "Failed finding matching source for - ");
207            (*it)->dump(stderr);
208            exit(1);
209        }
210        (*it)->fill_arp_req_buf(p, m_port_id, sip);
211
212        if (verbose >= 3) {
213            fprintf(stdout, "TX ARP request on port %d - " , m_port_id);
214            (*it)->dump(stdout, "");
215            if (verbose >= 7) {
216                utl_DumpBuffer(stdout, p, rte_pktmbuf_pkt_len(m[0]), 0);
217            }
218        }
219
220        num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1);
221        if (num_sent < 1) {
222            fprintf(stderr, "Failed sending ARP to port:%d\n", m_port_id);
223            exit(1);
224        } else {
225            m_stats.m_tx_arp++;
226        }
227    }
228}
229
230void CPretestOnePortInfo::send_grat_arp_all() {
231    for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) {
232        rte_mbuf_t *m[1];
233        int num_sent;
234        int verbose = CGlobalInfo::m_options.preview.getVMode();
235
236        m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id);
237        if ( unlikely(m[0] == 0) )  {
238            fprintf(stderr, "ERROR: Could not allocate mbuf for sending grat ARP on port:%d\n", m_port_id);
239            exit(1);
240        }
241
242        uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_grat_arp_len());
243        (*it)->fill_grat_arp_buf(p);
244
245
246        if (verbose >= 3) {
247            fprintf(stdout, "TX grat ARP on port %d - " , m_port_id);
248            (*it)->dump(stdout, "");
249        }
250
251        num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1);
252        if (num_sent < 1) {
253            fprintf(stderr, "Failed sending grat ARP on port:%d\n", m_port_id);
254            exit(1);
255        } else {
256            m_stats.m_tx_arp++;
257        }
258    }
259}
260
261// IPv4 functions
262void CPretest::add_ip(uint16_t port, uint32_t ip, uint16_t vlan, MacAddress src_mac) {
263    assert(port < m_max_ports);
264    m_port_info[port].add_src(ip, vlan, src_mac);
265}
266
267void CPretest::add_ip(uint16_t port, uint32_t ip, MacAddress src_mac) {
268    assert(port < m_max_ports);
269    add_ip(port, ip, 0, src_mac);
270}
271
272void CPretest::add_next_hop(uint16_t port, uint32_t ip, uint16_t vlan) {
273    assert(port < m_max_ports);
274    m_port_info[port].add_dst(ip, vlan);
275}
276
277void CPretest::add_next_hop(uint16_t port, uint32_t ip) {
278    assert(port < m_max_ports);
279    add_next_hop(port, ip, 0);
280}
281
282// IPv6 functions
283void CPretest::add_ip(uint16_t port, uint16_t ip[8], uint16_t vlan, MacAddress src_mac) {
284    assert(port < m_max_ports);
285    m_port_info[port].add_src(ip, vlan, src_mac);
286}
287
288void CPretest::add_ip(uint16_t port, uint16_t ip[8], MacAddress src_mac) {
289    assert(port < m_max_ports);
290    add_ip(port, ip, 0, src_mac);
291}
292
293void CPretest::add_next_hop(uint16_t port, uint16_t ip[8], uint16_t vlan) {
294    assert(port < m_max_ports);
295    m_port_info[port].add_dst(ip, vlan);
296}
297
298void CPretest::add_next_hop(uint16_t port, uint16_t ip[8]) {
299    assert(port < m_max_ports);
300    add_next_hop(port, ip, 0);
301}
302
303// put in mac, the relevant mac address for the tupple port_id, ip, vlan
304bool CPretest::get_mac(uint16_t port_id, uint32_t ip, uint16_t vlan, uint8_t *mac) {
305    assert(port_id < m_max_ports);
306
307    return m_port_info[port_id].get_mac(ip, vlan, mac);
308}
309
310// IPv6 version of above
311bool CPretest::get_mac(uint16_t port_id, uint16_t ip[8], uint16_t vlan, uint8_t *mac) {
312    assert(port_id < m_max_ports);
313
314    return m_port_info[port_id].get_mac(ip, vlan, mac);
315}
316
317CPreTestStats CPretest::get_stats(uint16_t port_id) {
318    assert(port_id < m_max_ports);
319
320    return m_port_info[port_id].get_stats();
321}
322
323bool CPretest::is_loopback(uint16_t port) {
324    assert(port < m_max_ports);
325
326    return m_port_info[port].is_loopback();
327}
328
329bool CPretest::resolve_all() {
330    uint16_t port;
331
332    // send ARP request on all ports
333    for (port = 0; port < m_max_ports; port++) {
334        m_port_info[port].send_arp_req_all();
335    }
336
337    int max_tries = 1000;
338    int i;
339    for (i = 0; i < max_tries; i++) {
340        bool all_resolved = true;
341        for (port = 0; port < m_max_ports; port++) {
342            if (m_port_info[port].resolve_needed()) {
343                // We need to stop reading packets only if all ports are resolved.
344                // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state
345                all_resolved = false;
346            }
347            for (uint16_t queue = 0; queue < m_num_q; queue++) {
348                handle_rx(port, queue);
349            }
350        }
351        if (all_resolved) {
352            break;
353        } else {
354            delay(1);
355        }
356    }
357
358    if (i == max_tries) {
359        return false;
360    } else {
361        return true;
362    }
363
364    return true;
365}
366
367void CPretest::send_arp_req_all() {
368    for (uint16_t port = 0; port < m_max_ports; port++) {
369        m_port_info[port].send_arp_req_all();
370    }
371}
372
373void CPretest::send_grat_arp_all() {
374    for (uint16_t port = 0; port < m_max_ports; port++) {
375        m_port_info[port].send_grat_arp_all();
376    }
377}
378
379bool CPretest::is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp, uint16_t &vlan_tag) {
380    EthernetHeader *m_ether = (EthernetHeader *)p;
381    vlan_tag = 0;
382    uint16_t min_size = sizeof(EthernetHeader);
383    VLANHeader *vlan;
384
385    if (pkt_size < min_size)
386        return false;
387
388    switch(m_ether->getNextProtocol()) {
389    case EthernetHeader::Protocol::ARP:
390        arp = (ArpHdr *)(p + 14);
391        min_size += sizeof(ArpHdr);
392        break;
393    case EthernetHeader::Protocol::VLAN:
394        vlan = (VLANHeader *)(p + 14);
395
396        min_size += sizeof(VLANHeader);
397        if (pkt_size < min_size)
398            return false;
399
400        if (vlan->getNextProtocolHostOrder() != EthernetHeader::Protocol::ARP) {
401            return false;
402        } else {
403            vlan_tag = vlan->getVlanTag();
404            arp = (ArpHdr *)(p + 14 + sizeof(VLANHeader));
405        }
406        min_size += sizeof(ArpHdr);
407        break;
408    default:
409        return false;
410    }
411
412    if (pkt_size < min_size)
413        return false;
414    else
415        return true;
416}
417
418int CPretest::handle_rx(int port_id, int queue_id) {
419    rte_mbuf_t * rx_pkts[32];
420    uint16_t cnt;
421    int i;
422    int verbose = CGlobalInfo::m_options.preview.getVMode();
423    int tries = 0;
424
425    do {
426        cnt = rte_eth_rx_burst(port_id, queue_id, rx_pkts, sizeof(rx_pkts)/sizeof(rx_pkts[0]));
427        tries++;
428        bool free_pkt;
429        for (i = 0; i < cnt; i++) {
430            rte_mbuf_t * m = rx_pkts[i];
431            free_pkt = true;
432            int pkt_size = rte_pktmbuf_pkt_len(m);
433            uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *);
434            ArpHdr *arp;
435            uint16_t vlan_tag;
436            CPretestOnePortInfo *port = &m_port_info[port_id];
437            if (is_arp(p, pkt_size, arp, vlan_tag)) {
438                port->m_stats.m_rx_arp++;
439                if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REQUEST)) {
440                    if (verbose >= 3) {
441                        bool is_grat = false;
442                        if (arp->m_arp_sip == arp->m_arp_tip) {
443                            is_grat = true;
444                        }
445                        fprintf(stdout, "RX %s on port %d queue %d sip:%s tip:%s vlan:%d\n"
446                                , is_grat ? "grat ARP" : "ARP request"
447                                , port_id, queue_id
448                                , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
449                                , ip_to_str(ntohl(arp->m_arp_tip)).c_str()
450                                , vlan_tag);
451                        if (verbose >= 7)
452                            utl_DumpBuffer(stdout, p, rte_pktmbuf_pkt_len(m), 0);
453                    }
454                    // is this request for our IP?
455                    COneIPv4Info *src_addr;
456                    COneIPv4Info *rcv_addr;
457                    if ((src_addr = port->find_ip(ntohl(arp->m_arp_tip), vlan_tag))) {
458                        // If our request(i.e. we are connected in loopback)
459                        // , do a shortcut, and write info directly to asking port
460                        uint8_t magic[5] = {0x1, 0x3, 0x5, 0x7, 0x9};
461                        if (! memcmp((uint8_t *)&arp->m_arp_tha.data, magic, 5)) {
462                            uint8_t sent_port_id = arp->m_arp_tha.data[5];
463                            if ((sent_port_id < m_max_ports) &&
464                                (rcv_addr = m_port_info[sent_port_id].find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) {
465                                uint8_t mac[ETHER_ADDR_LEN];
466                                src_addr->get_mac(mac);
467                                rcv_addr->set_mac(mac);
468                                port->m_is_loopback = true;
469                                m_port_info[sent_port_id].m_is_loopback = true;
470                            }
471                        } else {
472                            // Not our request. Answer.
473                            uint8_t src_mac[ETHER_ADDR_LEN];
474                            free_pkt = false; // We use the same mbuf to send response. Don't free it twice.
475                            arp->m_arp_op = htons(ArpHdr::ARP_HDR_OP_REPLY);
476                            uint32_t tmp_ip = arp->m_arp_sip;
477                            arp->m_arp_sip = arp->m_arp_tip;
478                            arp->m_arp_tip = tmp_ip;
479                            memcpy((uint8_t *)&arp->m_arp_tha, (uint8_t *)&arp->m_arp_sha, ETHER_ADDR_LEN);
480                            src_addr->get_mac(src_mac);
481                            memcpy((uint8_t *)&arp->m_arp_sha, src_mac, ETHER_ADDR_LEN);
482                            EthernetHeader *m_ether = (EthernetHeader *)p;
483                            memcpy((uint8_t *)&m_ether->myDestination, (uint8_t *)&m_ether->mySource, ETHER_ADDR_LEN);
484                            memcpy((uint8_t *)&m_ether->mySource, src_mac, ETHER_ADDR_LEN);
485                            int num_sent = rte_eth_tx_burst(port_id, 0, &m, 1);
486                            if (num_sent < 1) {
487                                fprintf(stderr, "Failed sending ARP reply to port:%d\n", port_id);
488                                rte_pktmbuf_free(m);
489                            } else {
490                                if (verbose >= 3) {
491                                    fprintf(stdout, "TX ARP reply on port:%d sip:%s, tip:%s\n"
492                                            , port_id
493                                            , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
494                                            , ip_to_str(ntohl(arp->m_arp_tip)).c_str());
495
496                                }
497                                m_port_info[port_id].m_stats.m_tx_arp++;
498                            }
499                        }
500                    } else {
501                        // ARP request not to our IP. Check if this is gratitues ARP for something we need.
502                        if ((arp->m_arp_tip == arp->m_arp_sip)
503                            && (rcv_addr = port->find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) {
504                            rcv_addr->set_mac((uint8_t *)&arp->m_arp_sha);
505                        }
506                    }
507                } else {
508                    if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REPLY)) {
509                        if (verbose >= 3) {
510                            fprintf(stdout, "RX ARP reply on port %d queue %d sip:%s tip:%s\n"
511                                    , port_id, queue_id
512                                    , ip_to_str(ntohl(arp->m_arp_sip)).c_str()
513                                    , ip_to_str(ntohl(arp->m_arp_tip)).c_str());
514                        }
515
516                        // If this is response to our request, update our tables
517                        COneIPv4Info *addr;
518                        if ((addr = port->find_next_hop(ntohl(arp->m_arp_sip), vlan_tag))) {
519                            addr->set_mac((uint8_t *)&arp->m_arp_sha);
520                        }
521                    }
522                }
523            }
524            if (free_pkt)
525                rte_pktmbuf_free(m);
526        }
527    } while ((cnt != 0) && (tries < 1000));
528
529    return 0;
530}
531
532void CPretest::get_results(CManyIPInfo &resolved_ips) {
533    for (int port = 0; port < m_max_ports; port++) {
534        for (std::vector<COneIPInfo *>::iterator it = m_port_info[port].m_dst_info.begin()
535                 ; it != m_port_info[port].m_dst_info.end(); ++it) {
536            uint8_t ip_type = (*it)->ip_ver();
537            (*it)->set_port(port);
538            switch(ip_type) {
539            case COneIPInfo::IP4_VER:
540                resolved_ips.insert(*(COneIPv4Info *)(*it));
541                break;
542#if 0
543                //??? fix for ipv6
544            case COneIPInfo::IP6_VER:
545                ipv6_tmp = (uint8_t *)((COneIPv6Info *)(*it))->get_ipv6();
546                memcpy((uint8_t *)ipv6, (uint8_t *)ipv6_tmp, 16);
547                v6_list.insert(std::pair<std::pair<uint16_t[8], uint16_t>, COneIPv6Info>
548                               (std::pair<uint16_t[8], uint16_t>(ipv6, vlan), *(COneIPv6Info *)(*it)));
549                break;
550#endif
551            default:
552                break;
553            }
554        }
555    }
556}
557
558void CPretest::dump(FILE *fd) {
559    fprintf(fd, "Pre test info start ===================\n");
560    for (int port = 0; port < m_max_ports; port++) {
561        fprintf(fd, "Port %d:\n", port);
562        m_port_info[port].dump(fd, (char *)"  ");
563    }
564    fprintf(fd, "Pre test info end ===================\n");
565}
566
567void CPretest::test() {
568    uint8_t found_mac[ETHER_ADDR_LEN];
569    uint8_t mac0[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd0};
570    uint8_t mac1[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd1};
571    uint8_t mac2[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd2};
572    uint32_t ip0  = 0x0f000002;
573    uint32_t ip01 = 0x0f000003;
574    uint32_t ip1  = 0x0f000001;
575    uint16_t ipv6_0[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2220};
576    uint16_t ipv6_1[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2221};
577    uint16_t vlan=1;
578    uint8_t port_0 = 0;
579    uint8_t port_1 = 3;
580
581    add_ip(port_0, ip0, vlan, mac0);
582    add_ip(port_0, ip01, vlan, mac1);
583    add_ip(port_0, ipv6_0, vlan, mac1);
584    add_next_hop(port_0, ip1, vlan);
585    add_next_hop(port_0, ipv6_1, vlan);
586
587    add_ip(port_1, ip1, vlan, mac2);
588    add_ip(port_1, ipv6_1, vlan, mac2);
589    add_next_hop(port_1, ip0, vlan);
590    add_next_hop(port_1, ip01, vlan);
591    add_next_hop(port_1, ipv6_0, vlan);
592
593    dump(stdout);
594    send_grat_arp_all();
595    resolve_all();
596    dump(stdout);
597
598    if (!get_mac(port_0, ip1, vlan, found_mac)) {
599        fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip1, port_0);
600        exit(1);
601    }
602    if (memcmp(found_mac, mac2, ETHER_ADDR_LEN)) {
603        fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip1, port_0);
604        exit(1);
605    }
606
607    if (!get_mac(port_1, ip0, vlan, found_mac)) {
608        fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip0, port_1);
609        exit(1);
610    }
611    if (memcmp(found_mac, mac0, ETHER_ADDR_LEN)) {
612        fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip0, port_1);
613        exit(1);
614    }
615
616    printf("Test passed\n");
617    exit(0);
618}
619