net_switch.cc 11 KB
Newer Older
Antoine Kaufmann's avatar
Antoine Kaufmann committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Copyright 2021 Max Planck Institute for Software Systems, and
 * National University of Singapore
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

25
#include <unistd.h>
Jialin Li's avatar
Jialin Li committed
26
#include <pcap/pcap.h>
27
28
29
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
30
31
32
33

#include <cassert>
#include <climits>
#include <csignal>
Jialin Li's avatar
Jialin Li committed
34
35
36
37
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unordered_map>
38
#include <vector>
Jialin Li's avatar
Jialin Li committed
39
40

extern "C" {
41
#include <simbricks/netif/netif.h>
42
#include <simbricks/proto/base.h>
Jialin Li's avatar
Jialin Li committed
43
44
};

45
//#define NETSWITCH_DEBUG
Hejing Li's avatar
Hejing Li committed
46
#define NETSWITCH_STAT
47

Antoine Kaufmann's avatar
Antoine Kaufmann committed
48
49
static uint64_t sync_period = (500 * 1000ULL);  // 500ns
static uint64_t eth_latency = (500 * 1000ULL);  // 500ns
50
static int sync_mode = SIMBRICKS_PROTO_SYNC_SIMBRICKS;
Jialin Li's avatar
Jialin Li committed
51
static pcap_dumper_t *dumpfile = nullptr;
Jialin Li's avatar
Jialin Li committed
52

Hejing Li's avatar
Hejing Li committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#ifdef NETSWITCH_STAT
#endif

#ifdef NETSWITCH_STAT
static uint64_t d2n_poll_total = 0;
static uint64_t d2n_poll_suc = 0;
static uint64_t d2n_poll_sync = 0;

static uint64_t s_d2n_poll_total = 0;
static uint64_t s_d2n_poll_suc = 0;
static uint64_t s_d2n_poll_sync = 0;

static int stat_flag = 0;
#endif

Jialin Li's avatar
Jialin Li committed
68
69
/* MAC address type */
struct MAC {
70
  const uint8_t *data;
Jialin Li's avatar
Jialin Li committed
71

72
  explicit MAC(const uint8_t *data) : data(data) {
73
  }
Jialin Li's avatar
Jialin Li committed
74

75
76
77
78
79
  bool operator==(const MAC &other) const {
    for (int i = 0; i < 6; i++) {
      if (data[i] != other.data[i]) {
        return false;
      }
Jialin Li's avatar
Jialin Li committed
80
    }
81
82
    return true;
  }
Jialin Li's avatar
Jialin Li committed
83
84
};
namespace std {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
85
template <>
86
87
struct hash<MAC> {
  size_t operator()(const MAC &m) const {
Antoine Kaufmann's avatar
Antoine Kaufmann committed
88
89
    size_t res = 0;
    for (int i = 0; i < 6; i++) {
90
      res = (res << 4) | (res ^ m.data[i]);
Antoine Kaufmann's avatar
Antoine Kaufmann committed
91
92
    }
    return res;
93
  }
Antoine Kaufmann's avatar
Antoine Kaufmann committed
94
95
};
}  // namespace std
Jialin Li's avatar
Jialin Li committed
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
class Port {
 protected:
  struct SimbricksNetIf netif_;
  volatile union SimbricksProtoNetD2N *rx_;
  int sync_;

 public:
  Port() : rx_(nullptr), sync_(0) {
    memset(&netif_, 0, sizeof(netif_));
  }

  Port(const Port &other) : netif_(other.netif_), rx_(other.rx_),
      sync_(other.sync_) {}

  virtual ~Port() = default;

  virtual bool Connect(const char *path, int sync) {
    sync_ = sync;
    return SimbricksNetIfInit(&netif_, path, &sync_) == 0;
  }

  virtual bool IsSync() {
    return sync_;
  }

  virtual void Sync(uint64_t cur_ts) {
    if (SimbricksNetIfN2DSync(&netif_, cur_ts, eth_latency, sync_period,
                              sync_mode) != 0) {
      fprintf(stderr, "SimbricksNetIfN2DSync failed\n");
      abort();
    }
  }

  virtual void AdvanceEpoch(uint64_t cur_ts) {
    SimbricksNetIfAdvanceEpoch(cur_ts, sync_period, sync_mode);
  }

  virtual uint64_t NextTimestamp() {
    return SimbricksNetIfD2NTimestamp(&netif_);
  }

  enum RxPollState {
    kRxPollSuccess = 0,
    kRxPollFail = 1,
    kRxPollSync = 2,
  };

  virtual enum RxPollState RxPacket(
      const void *& data, size_t &len, uint64_t cur_ts) {
    assert(rx_ == nullptr);

    rx_ = SimbricksNetIfD2NPoll(&netif_, cur_ts);
    if (!rx_)
      return kRxPollFail;

    uint8_t type = rx_->dummy.own_type & SIMBRICKS_PROTO_NET_D2N_MSG_MASK;
    if (type == SIMBRICKS_PROTO_NET_D2N_MSG_SEND) {
      data = (const void *)rx_->send.data;
      len = rx_->send.len;
      return kRxPollSuccess;
    } else if (type == SIMBRICKS_PROTO_NET_D2N_MSG_SYNC) {
      return kRxPollSync;
    } else {
      fprintf(stderr, "switch_pkt: unsupported type=%u\n", type);
      abort();
    }
  }

  virtual void RxDone() {
    assert(rx_ != nullptr);

    SimbricksNetIfD2NDone(&netif_, rx_);
    rx_ = nullptr;
  }

  virtual bool TxPacket(const void *data, size_t len, uint64_t cur_ts) {
    volatile union SimbricksProtoNetN2D *msg_to =
      SimbricksNetIfN2DAlloc(&netif_, cur_ts, eth_latency);
    if (!msg_to)
      return false;

    volatile struct SimbricksProtoNetN2DRecv *rx;
    rx = &msg_to->recv;
    rx->len = len;
    rx->port = 0;
    memcpy((void *)rx->data, data, len);

    // WMB();
    rx->own_type =
        SIMBRICKS_PROTO_NET_N2D_MSG_RECV | SIMBRICKS_PROTO_NET_N2D_OWN_DEV;
    return true;
  }
};

Jialin Li's avatar
Jialin Li committed
191
192
193
/* Global variables */
static uint64_t cur_ts = 0;
static int exiting = 0;
194
static const uint8_t bcast[6] = {0xFF};
Jialin Li's avatar
Jialin Li committed
195
static const MAC bcast_addr(bcast);
196
static std::vector<Port> ports;
Jialin Li's avatar
Jialin Li committed
197
198
static std::unordered_map<MAC, int> mac_table;

199
200
static void sigint_handler(int dummy) {
  exiting = 1;
Jialin Li's avatar
Jialin Li committed
201
202
}

Hejing Li's avatar
Hejing Li committed
203
204
205
206
207
208
209
210
211
212
static void sigusr1_handler(int dummy) {
  fprintf(stderr, "main_time = %lu\n", cur_ts);
}

#ifdef NETSWITCH_STAT
static void sigusr2_handler(int dummy) {
  stat_flag = 1;
}
#endif

213
static void forward_pkt(const void *pkt_data, size_t pkt_len, size_t port_id) {
Jialin Li's avatar
Jialin Li committed
214
  struct pcap_pkthdr ph;
215
  Port &dest_port = ports[port_id];
216

Jialin Li's avatar
Jialin Li committed
217
218
219
220
221
  // log to pcap file if initialized
  if (dumpfile) {
      memset(&ph, 0, sizeof(ph));
      ph.ts.tv_sec = cur_ts / 1000000000000ULL;
      ph.ts.tv_usec = (cur_ts % 1000000000000ULL) / 1000ULL;
222
223
224
      ph.caplen = pkt_len;
      ph.len = pkt_len;
      pcap_dump((unsigned char *)dumpfile, &ph, (unsigned char *)pkt_data);
Jialin Li's avatar
Jialin Li committed
225
  }
226
  // print sending tick: [packet type] source_IP -> dest_IP len:
227

228
229
230
231
#ifdef NETSWITCH_DEBUG
  uint16_t eth_proto;
  struct ethhdr *hdr;
  struct iphdr *iph;
232
  hdr = (struct ethhdr*)pkt_data;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  eth_proto = ntohs(hdr->h_proto);
  iph = (struct iphdr *)(hdr + 1);
  fprintf(stderr, "%20lu: ", cur_ts);
  if (eth_proto == ETH_P_IP){
    fprintf(stderr, "[ IP] ");
    
  } 
  else if(eth_proto == ETH_P_ARP){
    fprintf(stderr, "[ARP] ");
  } 
  else{
    fprintf(stderr, "unkwon eth type\n");
  }

  fprintf(stderr, "%8X -> %8X len: %lu\n ", iph->saddr, iph->daddr, iph->tot_len + sizeof(struct ethhdr));
#endif

250
251
  if (!dest_port.TxPacket(pkt_data, pkt_len, cur_ts))
    fprintf(stderr, "forward_pkt: dropping packet on port %zu\n", port_id);
Jialin Li's avatar
Jialin Li committed
252
253
}

254
255
256
257
static void switch_pkt(Port &port, size_t iport) {
  const void *pkt_data;
  size_t pkt_len;

Hejing Li's avatar
Hejing Li committed
258
259
260
261
262
263
264
#ifdef NETSWITCH_STAT
  d2n_poll_total += 1;
  if (stat_flag){
    s_d2n_poll_total += 1;
  }
#endif

265
266
  enum Port::RxPollState poll = port.RxPacket(pkt_data, pkt_len, cur_ts);
  if (poll == Port::kRxPollFail) {
267
268
269
    return;
  }

Hejing Li's avatar
Hejing Li committed
270
271
272
273
274
275
276
#ifdef NETSWITCH_STAT
  d2n_poll_suc += 1;
  if (stat_flag){
    s_d2n_poll_suc += 1;
  }
#endif

277
  if (poll == Port::kRxPollSuccess) {
278
    // Get MAC addresses
279
    MAC dst((const uint8_t *)pkt_data), src((const uint8_t *)pkt_data + 6);
280
281
282
    // MAC learning
    if (!(src == bcast_addr)) {
      mac_table[src] = iport;
Jialin Li's avatar
Jialin Li committed
283
    }
284
285
    // L2 forwarding
    if (mac_table.count(dst) > 0) {
286
      size_t eport = mac_table.at(dst);
287
      forward_pkt(pkt_data, pkt_len, eport);
Jialin Li's avatar
Jialin Li committed
288
    } else {
289
      // Broadcast
290
      for (size_t eport = 0; eport < ports.size(); eport++) {
291
292
        if (eport != iport) {
          // Do not forward to ingress port
293
          forward_pkt(pkt_data, pkt_len, eport);
294
295
        }
      }
Jialin Li's avatar
Jialin Li committed
296
    }
297
  } else if (poll == Port::kRxPollSync) {
Hejing Li's avatar
Hejing Li committed
298
299
300
301
302
303
#ifdef NETSWITCH_STAT
    d2n_poll_sync += 1;
    if (stat_flag){
      s_d2n_poll_sync += 1;
    }
#endif
304
  } else {
305
    fprintf(stderr, "switch_pkt: unsupported poll result=%u\n", poll);
306
307
    abort();
  }
308
  port.RxDone();
Jialin Li's avatar
Jialin Li committed
309
310
}

311
312
313
int main(int argc, char *argv[]) {
  int c;
  int bad_option = 0;
314
  int sync_eth = 1;
Jialin Li's avatar
Jialin Li committed
315
  pcap_t *pc = nullptr;
316
317

  // Parse command line argument
318
  while ((c = getopt(argc, argv, "s:uS:E:m:p:")) != -1 && !bad_option) {
319
320
    switch (c) {
      case 's': {
321
        Port port;
322
        fprintf(stderr, "Switch connecting to: %s\n", optarg);
323
        if (!port.Connect(optarg, sync_eth)) {
324
325
          fprintf(stderr, "connecting to %s failed\n", optarg);
          return EXIT_FAILURE;
Jialin Li's avatar
Jialin Li committed
326
        }
327
        ports.push_back(port);
328
329
330
        break;
      }

331
332
333
334
      case 'u':
        sync_eth = 0;
        break;

335
336
337
338
339
340
341
342
343
344
      case 'S':
        sync_period = strtoull(optarg, NULL, 0) * 1000ULL;
        break;

      case 'E':
        eth_latency = strtoull(optarg, NULL, 0) * 1000ULL;
        break;

      case 'm':
        sync_mode = strtol(optarg, NULL, 0);
345
        assert(sync_mode == SIMBRICKS_PROTO_SYNC_SIMBRICKS ||
346
               sync_mode == SIMBRICKS_PROTO_SYNC_BARRIER);
347
348
        break;

Jialin Li's avatar
Jialin Li committed
349
350
351
352
353
354
355
356
357
358
359
      case 'p':
        pc = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, 65535,
                                                  PCAP_TSTAMP_PRECISION_NANO);
        if (pc == nullptr) {
            perror("pcap_open_dead failed");
            return EXIT_FAILURE;
        }

        dumpfile = pcap_dump_open(pc, optarg);
        break;

360
361
362
363
      default:
        fprintf(stderr, "unknown option %c\n", c);
        bad_option = 1;
        break;
Jialin Li's avatar
Jialin Li committed
364
    }
365
366
  }

367
  if (ports.empty() || bad_option) {
368
369
370
371
372
373
374
375
    fprintf(stderr,
            "Usage: net_switch [-S SYNC-PERIOD] [-E ETH-LATENCY] "
            "-s SOCKET-A [-s SOCKET-B ...]\n");
    return EXIT_FAILURE;
  }

  signal(SIGINT, sigint_handler);
  signal(SIGTERM, sigint_handler);
Hejing Li's avatar
Hejing Li committed
376
377
378
379
380
381
  signal(SIGUSR1, sigusr1_handler);

#ifdef NETSWITCH_STAT
  signal(SIGUSR2, sigusr2_handler);
#endif

382
383
384
385

  printf("start polling\n");
  while (!exiting) {
    // Sync all interfaces
386
387
388
389
    for (auto &port : ports)
      port.Sync(cur_ts);
    for (auto &port : ports)
      port.AdvanceEpoch(cur_ts);
390
391
392
393
394

    // Switch packets
    uint64_t min_ts;
    do {
      min_ts = ULLONG_MAX;
395
396
397
398
399
      for (size_t port_i = 0; port_i < ports.size(); port_i++) {
        auto &port = ports.at(port_i);
        switch_pkt(port, port_i);
        if (port.IsSync()) {
          uint64_t ts = port.NextTimestamp();
400
          min_ts = ts < min_ts ? ts : min_ts;
Jialin Li's avatar
Jialin Li committed
401
        }
402
403
404
405
406
      }
    } while (!exiting && (min_ts <= cur_ts));

    // Update cur_ts
    if (min_ts < ULLONG_MAX) {
407
      // a bit broken but should probably do
408
      cur_ts = SimbricksNetIfAdvanceTime(min_ts, sync_period, sync_mode);
Jialin Li's avatar
Jialin Li committed
409
    }
410
  }
Jialin Li's avatar
Jialin Li committed
411

Hejing Li's avatar
Hejing Li committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
#ifdef NETSWITCH_STAT
  fprintf(stderr, "%20s: %22lu %20s: %22lu  poll_suc_rate: %f\n",
          "d2n_poll_total", d2n_poll_total, "d2n_poll_suc", d2n_poll_suc,
          (double)d2n_poll_suc / d2n_poll_total);
  fprintf(stderr, "%65s: %22lu  sync_rate: %f\n", "d2n_poll_sync",
          d2n_poll_sync, (double)d2n_poll_sync / d2n_poll_suc);

  fprintf(stderr, "%20s: %22lu %20s: %22lu  poll_suc_rate: %f\n",
          "s_d2n_poll_total", s_d2n_poll_total, "s_d2n_poll_suc", s_d2n_poll_suc,
          (double)s_d2n_poll_suc / s_d2n_poll_total);
  fprintf(stderr, "%65s: %22lu  sync_rate: %f\n", "s_d2n_poll_sync",
          s_d2n_poll_sync, (double)s_d2n_poll_sync / s_d2n_poll_suc);
#endif

426
  return 0;
Jialin Li's avatar
Jialin Li committed
427
}