A Discrete-Event Network Simulator
API
openflow-interface.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation;
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * Author: Blake Hurd <naimorai@gmail.com>
16  */
17 
18 #include "openflow-interface.h"
19 
21 
22 namespace ns3
23 {
24 
25 NS_LOG_COMPONENT_DEFINE("OpenFlowInterface");
26 
27 namespace ofi
28 {
29 
30 Stats::Stats(ofp_stats_types _type, size_t body_len)
31 {
32  type = _type;
33  size_t min_body = 0;
34  size_t max_body = 0;
35 
36  switch (type)
37  {
38  case OFPST_DESC:
39  break;
40  case OFPST_FLOW:
41  min_body = max_body = sizeof(ofp_flow_stats_request);
42  break;
43  case OFPST_AGGREGATE:
44  min_body = max_body = sizeof(ofp_aggregate_stats_request);
45  break;
46  case OFPST_TABLE:
47  break;
48  case OFPST_PORT:
49  min_body = 0;
50  max_body =
51  std::numeric_limits<size_t>::max(); // Not sure about this one. This would guarantee
52  // that the body_len is always acceptable.
53  break;
54  case OFPST_PORT_TABLE:
55  break;
56  default:
57  NS_LOG_ERROR("received stats request of unknown type " << type);
58  return; // -EINVAL;
59  }
60 
61  if ((min_body != 0 || max_body != 0) && (body_len < min_body || body_len > max_body))
62  {
63  NS_LOG_ERROR("stats request type " << type << " with bad body length " << body_len);
64  return; // -EINVAL;
65  }
66 }
67 
68 int
69 Stats::DoInit(const void* body, int body_len, void** state)
70 {
71  switch (type)
72  {
73  case OFPST_DESC:
74  return 0;
75  case OFPST_FLOW:
76  return FlowStatsInit(body, body_len, state);
77  case OFPST_AGGREGATE:
78  return AggregateStatsInit(body, body_len, state);
79  case OFPST_TABLE:
80  return 0;
81  case OFPST_PORT:
82  return PortStatsInit(body, body_len, state);
83  case OFPST_PORT_TABLE:
84  return 0;
85  case OFPST_VENDOR:
86  return 0;
87  }
88 
89  return 0;
90 }
91 
92 int
93 Stats::DoDump(Ptr<OpenFlowSwitchNetDevice> swtch, void* state, ofpbuf* buffer)
94 {
95  switch (type)
96  {
97  case OFPST_DESC:
98  return DescStatsDump(state, buffer);
99  case OFPST_FLOW:
100  return FlowStatsDump(swtch, (FlowStatsState*)state, buffer);
101  case OFPST_AGGREGATE:
102  return AggregateStatsDump(swtch, (ofp_aggregate_stats_request*)state, buffer);
103  case OFPST_TABLE:
104  return TableStatsDump(swtch, state, buffer);
105  case OFPST_PORT:
106  return PortStatsDump(swtch, (PortStatsState*)state, buffer);
107  case OFPST_PORT_TABLE:
108  return PortTableStatsDump(swtch, state, buffer);
109  case OFPST_VENDOR:
110  return 0;
111  }
112 
113  return 0;
114 }
115 
116 void
117 Stats::DoCleanup(void* state)
118 {
119  switch (type)
120  {
121  case OFPST_DESC:
122  break;
123  case OFPST_FLOW:
124  free((FlowStatsState*)state);
125  break;
126  case OFPST_AGGREGATE:
127  free((ofp_aggregate_stats_request*)state);
128  break;
129  case OFPST_TABLE:
130  break;
131  case OFPST_PORT:
132  free(((PortStatsState*)state)->ports);
133  free((PortStatsState*)state);
134  break;
135  case OFPST_PORT_TABLE:
136  break;
137  case OFPST_VENDOR:
138  break;
139  }
140 }
141 
142 int
143 Stats::DescStatsDump(void* state, ofpbuf* buffer)
144 {
145  ofp_desc_stats* ods = (ofp_desc_stats*)ofpbuf_put_zeros(buffer, sizeof *ods);
146  strncpy(ods->mfr_desc,
148  sizeof ods->mfr_desc);
149  strncpy(ods->hw_desc, OpenFlowSwitchNetDevice::GetHardwareDescription(), sizeof ods->hw_desc);
150  strncpy(ods->sw_desc, OpenFlowSwitchNetDevice::GetSoftwareDescription(), sizeof ods->sw_desc);
151  strncpy(ods->serial_num, OpenFlowSwitchNetDevice::GetSerialNumber(), sizeof ods->serial_num);
152  return 0;
153 }
154 
155 #define MAX_FLOW_STATS_BYTES 4096
156 
157 int
158 Stats::FlowStatsInit(const void* body, int body_len, void** state)
159 {
160  const ofp_flow_stats_request* fsr = (ofp_flow_stats_request*)body;
161  auto s = (FlowStatsState*)xmalloc(sizeof(FlowStatsState));
162 
163  s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id;
164  memset(&s->position, 0, sizeof s->position);
165  s->rq = *fsr;
166  *state = s;
167  return 0;
168 }
169 
170 int
171 Stats_FlowDumpCallback(sw_flow* flow, void* state)
172 {
173  auto s = (Stats::FlowStatsState*)state;
174 
175  // Fill Flow Stats
176  ofp_flow_stats* ofs;
177  int length = sizeof *ofs + flow->sf_acts->actions_len;
178  ofs = (ofp_flow_stats*)ofpbuf_put_zeros(s->buffer, length);
179  ofs->length = htons(length);
180  ofs->table_id = s->table_idx;
181  ofs->match.wildcards = htonl(flow->key.wildcards);
182  ofs->match.in_port = flow->key.flow.in_port;
183  memcpy(ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN);
184  memcpy(ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN);
185  ofs->match.dl_vlan = flow->key.flow.dl_vlan;
186  ofs->match.dl_type = flow->key.flow.dl_type;
187  ofs->match.nw_src = flow->key.flow.nw_src;
188  ofs->match.nw_dst = flow->key.flow.nw_dst;
189  ofs->match.nw_proto = flow->key.flow.nw_proto;
190  ofs->match.tp_src = flow->key.flow.tp_src;
191  ofs->match.tp_dst = flow->key.flow.tp_dst;
192  ofs->duration = htonl(s->now - flow->created);
193  ofs->priority = htons(flow->priority);
194  ofs->idle_timeout = htons(flow->idle_timeout);
195  ofs->hard_timeout = htons(flow->hard_timeout);
196  ofs->packet_count = htonll(flow->packet_count);
197  ofs->byte_count = htonll(flow->byte_count);
198  memcpy(ofs->actions, flow->sf_acts->actions, flow->sf_acts->actions_len);
199 
200  return s->buffer->size >= MAX_FLOW_STATS_BYTES;
201 }
202 
203 int
205 {
206  sw_flow_key match_key;
207 
208  flow_extract_match(&match_key, &s->rq.match);
209 
210  s->buffer = buffer;
211  s->now = time_now();
212  while (s->table_idx < swtch->GetChain()->n_tables &&
213  (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx))
214  {
215  sw_table* table = swtch->GetChain()->tables[s->table_idx];
216 
217  if (table->iterate(table,
218  &match_key,
219  s->rq.out_port,
220  &s->position,
222  s))
223  {
224  break;
225  }
226 
227  s->table_idx++;
228  memset(&s->position, 0, sizeof s->position);
229  }
230  return s->buffer->size >= MAX_FLOW_STATS_BYTES;
231 }
232 
233 int
234 Stats::AggregateStatsInit(const void* body, int body_len, void** state)
235 {
236  // ofp_aggregate_stats_request *s = (ofp_aggregate_stats_request*)body;
237  *state = (ofp_aggregate_stats_request*)body;
238  return 0;
239 }
240 
241 int
242 Stats_AggregateDumpCallback(sw_flow* flow, void* state)
243 {
244  ofp_aggregate_stats_reply* s = (ofp_aggregate_stats_reply*)state;
245  s->packet_count += flow->packet_count;
246  s->byte_count += flow->byte_count;
247  s->flow_count++;
248  return 0;
249 }
250 
251 int
253  ofp_aggregate_stats_request* s,
254  ofpbuf* buffer)
255 {
256  ofp_aggregate_stats_request* rq = s;
257  ofp_aggregate_stats_reply* rpy =
258  (ofp_aggregate_stats_reply*)ofpbuf_put_zeros(buffer, sizeof *rpy);
259  sw_flow_key match_key;
260  flow_extract_match(&match_key, &rq->match);
261  int table_idx = rq->table_id == 0xff ? 0 : rq->table_id;
262 
263  sw_table_position position;
264  memset(&position, 0, sizeof position);
265 
266  while (table_idx < swtch->GetChain()->n_tables &&
267  (rq->table_id == 0xff || rq->table_id == table_idx))
268  {
269  sw_table* table = swtch->GetChain()->tables[table_idx];
270  int error = table->iterate(table,
271  &match_key,
272  rq->out_port,
273  &position,
275  rpy);
276  if (error)
277  {
278  return error;
279  }
280 
281  table_idx++;
282  memset(&position, 0, sizeof position);
283  }
284 
285  rpy->packet_count = htonll(rpy->packet_count);
286  rpy->byte_count = htonll(rpy->byte_count);
287  rpy->flow_count = htonl(rpy->flow_count);
288  return 0;
289 }
290 
291 int
293 {
294  sw_chain* ft = swtch->GetChain();
295  for (int i = 0; i < ft->n_tables; i++)
296  {
297  ofp_table_stats* ots = (ofp_table_stats*)ofpbuf_put_zeros(buffer, sizeof *ots);
298  sw_table_stats stats;
299  ft->tables[i]->stats(ft->tables[i], &stats);
300  strncpy(ots->name, stats.name, sizeof ots->name);
301  ots->table_id = i;
302  ots->wildcards = htonl(stats.wildcards);
303  ots->max_entries = htonl(stats.max_flows);
304  ots->active_count = htonl(stats.n_flows);
305  ots->lookup_count = htonll(stats.n_lookup);
306  ots->matched_count = htonll(stats.n_matched);
307  }
308  return 0;
309 }
310 
311 // stats for the port table which is similar to stats for the flow tables
312 int
314 {
315  ofp_vport_table_stats* opts = (ofp_vport_table_stats*)ofpbuf_put_zeros(buffer, sizeof *opts);
316  opts->max_vports = htonl(swtch->GetVPortTable().max_vports);
317  opts->active_vports = htonl(swtch->GetVPortTable().active_vports);
318  opts->lookup_count = htonll(swtch->GetVPortTable().lookup_count);
319  opts->port_match_count = htonll(swtch->GetVPortTable().port_match_count);
320  opts->chain_match_count = htonll(swtch->GetVPortTable().chain_match_count);
321 
322  return 0;
323 }
324 
325 int
326 Stats::PortStatsInit(const void* body, int body_len, void** state)
327 {
328  auto s = (PortStatsState*)xmalloc(sizeof(PortStatsState));
329 
330  // the body contains a list of port numbers
331  s->ports = (uint32_t*)xmalloc(body_len);
332  memcpy(s->ports, body, body_len);
333  s->num_ports = body_len / sizeof(uint32_t);
334 
335  *state = s;
336  return 0;
337 }
338 
339 int
341 {
342  ofp_port_stats* ops;
343  uint32_t port;
344 
345  // port stats are different depending on whether port is physical or virtual
346  for (size_t i = 0; i < s->num_ports; i++)
347  {
348  port = ntohl(s->ports[i]);
349  // physical port?
350  if (port <= OFPP_MAX)
351  {
352  Port p = swtch->GetSwitchPort(port);
353 
354  if (!p.netdev)
355  {
356  continue;
357  }
358 
359  ops = (ofp_port_stats*)ofpbuf_put_zeros(buffer, sizeof *ops);
360  ops->port_no = htonl(swtch->GetSwitchPortIndex(p));
361  ops->rx_packets = htonll(p.rx_packets);
362  ops->tx_packets = htonll(p.tx_packets);
363  ops->rx_bytes = htonll(p.rx_bytes);
364  ops->tx_bytes = htonll(p.tx_bytes);
365  ops->rx_dropped = htonll(-1);
366  ops->tx_dropped = htonll(p.tx_dropped);
367  ops->rx_errors = htonll(-1);
368  ops->tx_errors = htonll(-1);
369  ops->rx_frame_err = htonll(-1);
370  ops->rx_over_err = htonll(-1);
371  ops->rx_crc_err = htonll(-1);
372  ops->collisions = htonll(-1);
373  ops->mpls_ttl0_dropped = htonll(p.mpls_ttl0_dropped);
374  ops++;
375  }
376  else if (port >= OFPP_VP_START && port <= OFPP_VP_END) // virtual port?
377  {
378  // lookup the virtual port
379  vport_table_t vt = swtch->GetVPortTable();
380  vport_table_entry* vpe = vport_table_lookup(&vt, port);
381  if (!vpe)
382  {
383  NS_LOG_ERROR("vport entry not found!");
384  continue;
385  }
386  // only tx_packets and tx_bytes are really relevant for virtual ports
387  ops = (ofp_port_stats*)ofpbuf_put_zeros(buffer, sizeof *ops);
388  ops->port_no = htonl(vpe->vport);
389  ops->rx_packets = htonll(-1);
390  ops->tx_packets = htonll(vpe->packet_count);
391  ops->rx_bytes = htonll(-1);
392  ops->tx_bytes = htonll(vpe->byte_count);
393  ops->rx_dropped = htonll(-1);
394  ops->tx_dropped = htonll(-1);
395  ops->rx_errors = htonll(-1);
396  ops->tx_errors = htonll(-1);
397  ops->rx_frame_err = htonll(-1);
398  ops->rx_over_err = htonll(-1);
399  ops->rx_crc_err = htonll(-1);
400  ops->collisions = htonll(-1);
401  ops->mpls_ttl0_dropped = htonll(-1);
402  ops++;
403  }
404  }
405  return 0;
406 }
407 
408 bool
409 Action::IsValidType(ofp_action_type type)
410 {
411  switch (type)
412  {
413  case OFPAT_OUTPUT:
414  case OFPAT_SET_VLAN_VID:
415  case OFPAT_SET_VLAN_PCP:
416  case OFPAT_STRIP_VLAN:
417  case OFPAT_SET_DL_SRC:
418  case OFPAT_SET_DL_DST:
419  case OFPAT_SET_NW_SRC:
420  case OFPAT_SET_NW_DST:
421  case OFPAT_SET_TP_SRC:
422  case OFPAT_SET_TP_DST:
423  case OFPAT_SET_MPLS_LABEL:
424  case OFPAT_SET_MPLS_EXP:
425  return true;
426  default:
427  return false;
428  }
429 }
430 
431 uint16_t
432 Action::Validate(ofp_action_type type,
433  size_t len,
434  const sw_flow_key* key,
435  const ofp_action_header* ah)
436 {
437  size_t size = 0;
438 
439  switch (type)
440  {
441  case OFPAT_OUTPUT: {
442  if (len != sizeof(ofp_action_output))
443  {
444  return OFPBAC_BAD_LEN;
445  }
446 
447  ofp_action_output* oa = (ofp_action_output*)ah;
448 
449  // To prevent loops, make sure there's no action to send to the OFP_TABLE virtual port.
450 
451  // port is now 32-bit
452  if (oa->port == OFPP_NONE || oa->port == key->flow.in_port) // htonl(OFPP_NONE);
453  { // if (oa->port == htons(OFPP_NONE) || oa->port == key->flow.in_port)
454  return OFPBAC_BAD_OUT_PORT;
455  }
456 
457  return ACT_VALIDATION_OK;
458  }
459  case OFPAT_SET_VLAN_VID:
460  size = sizeof(ofp_action_vlan_vid);
461  break;
462  case OFPAT_SET_VLAN_PCP:
463  size = sizeof(ofp_action_vlan_pcp);
464  break;
465  case OFPAT_STRIP_VLAN:
466  size = sizeof(ofp_action_header);
467  break;
468  case OFPAT_SET_DL_SRC:
469  case OFPAT_SET_DL_DST:
470  size = sizeof(ofp_action_dl_addr);
471  break;
472  case OFPAT_SET_NW_SRC:
473  case OFPAT_SET_NW_DST:
474  size = sizeof(ofp_action_nw_addr);
475  break;
476  case OFPAT_SET_TP_SRC:
477  case OFPAT_SET_TP_DST:
478  size = sizeof(ofp_action_tp_port);
479  break;
480  case OFPAT_SET_MPLS_LABEL:
481  size = sizeof(ofp_action_mpls_label);
482  break;
483  case OFPAT_SET_MPLS_EXP:
484  size = sizeof(ofp_action_mpls_exp);
485  break;
486  default:
487  break;
488  }
489 
490  if (len != size)
491  {
492  return OFPBAC_BAD_LEN;
493  }
494  return ACT_VALIDATION_OK;
495 }
496 
497 void
498 Action::Execute(ofp_action_type type, ofpbuf* buffer, sw_flow_key* key, const ofp_action_header* ah)
499 {
500  switch (type)
501  {
502  case OFPAT_OUTPUT:
503  break;
504  case OFPAT_SET_VLAN_VID:
505  set_vlan_vid(buffer, key, ah);
506  break;
507  case OFPAT_SET_VLAN_PCP:
508  set_vlan_pcp(buffer, key, ah);
509  break;
510  case OFPAT_STRIP_VLAN:
511  strip_vlan(buffer, key, ah);
512  break;
513  case OFPAT_SET_DL_SRC:
514  case OFPAT_SET_DL_DST:
515  set_dl_addr(buffer, key, ah);
516  break;
517  case OFPAT_SET_NW_SRC:
518  case OFPAT_SET_NW_DST:
519  set_nw_addr(buffer, key, ah);
520  break;
521  case OFPAT_SET_TP_SRC:
522  case OFPAT_SET_TP_DST:
523  set_tp_port(buffer, key, ah);
524  break;
525  case OFPAT_SET_MPLS_LABEL:
526  set_mpls_label(buffer, key, ah);
527  break;
528  case OFPAT_SET_MPLS_EXP:
529  set_mpls_exp(buffer, key, ah);
530  break;
531  default:
532  break;
533  }
534 }
535 
536 bool
537 VPortAction::IsValidType(ofp_vport_action_type type)
538 {
539  switch (type)
540  {
541  case OFPPAT_POP_MPLS:
542  case OFPPAT_PUSH_MPLS:
543  case OFPPAT_SET_MPLS_LABEL:
544  case OFPPAT_SET_MPLS_EXP:
545  return true;
546  default:
547  return false;
548  }
549 }
550 
551 uint16_t
552 VPortAction::Validate(ofp_vport_action_type type, size_t len, const ofp_action_header* ah)
553 {
554  size_t size = 0;
555 
556  switch (type)
557  {
558  case OFPPAT_POP_MPLS:
559  size = sizeof(ofp_vport_action_pop_mpls);
560  break;
561  case OFPPAT_PUSH_MPLS:
562  size = sizeof(ofp_vport_action_push_mpls);
563  break;
564  case OFPPAT_SET_MPLS_LABEL:
565  size = sizeof(ofp_vport_action_set_mpls_label);
566  break;
567  case OFPPAT_SET_MPLS_EXP:
568  size = sizeof(ofp_vport_action_set_mpls_exp);
569  break;
570  default:
571  break;
572  }
573 
574  if (len != size)
575  {
576  return OFPBAC_BAD_LEN;
577  }
578  return ACT_VALIDATION_OK;
579 }
580 
581 void
582 VPortAction::Execute(ofp_vport_action_type type,
583  ofpbuf* buffer,
584  const sw_flow_key* key,
585  const ofp_action_header* ah)
586 {
587  switch (type)
588  {
589  case OFPPAT_POP_MPLS: {
590  ofp_vport_action_pop_mpls* opapm = (ofp_vport_action_pop_mpls*)ah;
591  pop_mpls_act(nullptr, buffer, key, &opapm->apm);
592  break;
593  }
594  case OFPPAT_PUSH_MPLS: {
595  ofp_vport_action_push_mpls* opapm = (ofp_vport_action_push_mpls*)ah;
596  push_mpls_act(nullptr, buffer, key, &opapm->apm);
597  break;
598  }
599  case OFPPAT_SET_MPLS_LABEL: {
600  ofp_vport_action_set_mpls_label* oparml = (ofp_vport_action_set_mpls_label*)ah;
601  set_mpls_label_act(buffer, key, oparml->label_out);
602  break;
603  }
604  case OFPPAT_SET_MPLS_EXP: {
605  ofp_vport_action_set_mpls_exp* oparme = (ofp_vport_action_set_mpls_exp*)ah;
606  set_mpls_exp_act(buffer, key, oparme->exp);
607  break;
608  }
609  default:
610  break;
611  }
612 }
613 
614 bool
616 {
617  switch (type)
618  {
619  case ERXT_POP_MPLS:
620  case ERXT_PUSH_MPLS:
621  return true;
622  default:
623  return false;
624  }
625 }
626 
627 uint16_t
628 EricssonAction::Validate(er_action_type type, size_t len)
629 {
630  size_t size = 0;
631 
632  switch (type)
633  {
634  case ERXT_POP_MPLS:
635  size = sizeof(er_action_pop_mpls);
636  break;
637  case ERXT_PUSH_MPLS:
638  size = sizeof(er_action_push_mpls);
639  break;
640  default:
641  break;
642  }
643 
644  if (len != size)
645  {
646  return OFPBAC_BAD_LEN;
647  }
648  return ACT_VALIDATION_OK;
649 }
650 
651 void
653  ofpbuf* buffer,
654  const sw_flow_key* key,
655  const er_action_header* ah)
656 {
657  switch (type)
658  {
659  case ERXT_POP_MPLS: {
660  er_action_pop_mpls* erapm = (er_action_pop_mpls*)ah;
661  pop_mpls_act(nullptr, buffer, key, &erapm->apm);
662  break;
663  }
664  case ERXT_PUSH_MPLS: {
665  er_action_push_mpls* erapm = (er_action_push_mpls*)ah;
666  push_mpls_act(nullptr, buffer, key, &erapm->apm);
667  break;
668  }
669  default:
670  break;
671  }
672 }
673 
674 /* static */
675 TypeId
677 {
678  static TypeId tid = TypeId("ns3::ofi::Controller")
679  .SetParent<Object>()
680  .SetGroupName("OpenFlow")
681  .AddConstructor<Controller>();
682  return tid;
683 }
684 
686 {
687  m_switches.clear();
688 }
689 
690 void
692 {
693  if (m_switches.find(swtch) != m_switches.end())
694  {
695  NS_LOG_INFO("This Controller has already registered this switch!");
696  }
697  else
698  {
699  m_switches.insert(swtch);
700  }
701 }
702 
703 void
705 {
706  if (m_switches.find(swtch) == m_switches.end())
707  {
708  NS_LOG_ERROR("Can't send to this switch, not registered to the Controller.");
709  return;
710  }
711 
712  swtch->ForwardControlInput(msg, length);
713 }
714 
715 ofp_flow_mod*
716 Controller::BuildFlow(sw_flow_key key,
717  uint32_t buffer_id,
718  uint16_t command,
719  void* acts,
720  size_t actions_len,
721  int idle_timeout,
722  int hard_timeout)
723 {
724  ofp_flow_mod* ofm = (ofp_flow_mod*)malloc(sizeof(ofp_flow_mod) + actions_len);
725  ofm->header.version = OFP_VERSION;
726  ofm->header.type = OFPT_FLOW_MOD;
727  ofm->header.length = htons(sizeof(ofp_flow_mod) + actions_len);
728  ofm->command = htons(command);
729  ofm->idle_timeout = htons(idle_timeout);
730  ofm->hard_timeout = htons(hard_timeout);
731  ofm->buffer_id = htonl(buffer_id);
732  ofm->priority = OFP_DEFAULT_PRIORITY;
733  memcpy(ofm->actions, acts, actions_len);
734 
735  ofm->match.wildcards = key.wildcards; // Wildcard fields
736  ofm->match.in_port = key.flow.in_port; // Input switch port
737  memcpy(ofm->match.dl_src,
738  key.flow.dl_src,
739  sizeof ofm->match.dl_src); // Ethernet source address.
740  memcpy(ofm->match.dl_dst,
741  key.flow.dl_dst,
742  sizeof ofm->match.dl_dst); // Ethernet destination address.
743  ofm->match.dl_vlan = key.flow.dl_vlan; // Input VLAN OFP_VLAN_NONE;
744  ofm->match.dl_type = key.flow.dl_type; // Ethernet frame type ETH_TYPE_IP;
745  ofm->match.nw_proto = key.flow.nw_proto; // IP Protocol
746  ofm->match.nw_src = key.flow.nw_src; // IP source address
747  ofm->match.nw_dst = key.flow.nw_dst; // IP destination address
748  ofm->match.tp_src = key.flow.tp_src; // TCP/UDP source port
749  ofm->match.tp_dst = key.flow.tp_dst; // TCP/UDP destination port
750  ofm->match.mpls_label1 = key.flow.mpls_label1; // Top of label stack htonl(MPLS_INVALID_LABEL);
751  ofm->match.mpls_label2 =
752  key.flow.mpls_label1; // Second label (if available) htonl(MPLS_INVALID_LABEL);
753 
754  return ofm;
755 }
756 
757 uint8_t
759 {
760  ofp_header* hdr = (ofp_header*)ofpbuf_try_pull(buffer, sizeof(ofp_header));
761  uint8_t type = hdr->type;
762  ofpbuf_push_uninit(buffer, sizeof(ofp_header));
763  return type;
764 }
765 
766 void
768 {
769  if (cb)
770  {
771  int error = 1;
772  while (error > 0) // Switch's StatsDump returns 1 if the reply isn't complete.
773  {
774  error = cb->swtch->StatsDump(cb);
775  }
776 
777  if (error != 0) // When the reply is complete, error will equal zero if there's no errors.
778  {
779  NS_LOG_WARN("Dump Callback Error: " << strerror(-error));
780  }
781 
782  // Clean up
783  cb->swtch->StatsDone(cb);
784  }
785 }
786 
787 /* static */
788 TypeId
790 {
791  static TypeId tid = TypeId("ns3::ofi::DropController")
793  .SetGroupName("OpenFlow")
794  .AddConstructor<DropController>();
795  return tid;
796 }
797 
798 void
800 {
801  if (m_switches.find(swtch) == m_switches.end())
802  {
803  NS_LOG_ERROR("Can't receive from this switch, not registered to the Controller.");
804  return;
805  }
806 
807  // We have received any packet at this point, so we pull the header to figure out what type of
808  // packet we're handling.
809  uint8_t type = GetPacketType(buffer);
810 
811  if (type == OFPT_PACKET_IN) // The switch didn't understand the packet it received, so it
812  // forwarded it to the controller.
813  {
814  ofp_packet_in* opi = (ofp_packet_in*)ofpbuf_try_pull(buffer, offsetof(ofp_packet_in, data));
815  int port = ntohs(opi->in_port);
816 
817  // Create matching key.
818  sw_flow_key key;
819  key.wildcards = 0;
820  flow_extract(buffer, port != -1 ? port : OFPP_NONE, &key.flow);
821 
822  ofp_flow_mod* ofm = BuildFlow(key,
823  opi->buffer_id,
824  OFPFC_ADD,
825  nullptr,
826  0,
827  OFP_FLOW_PERMANENT,
828  OFP_FLOW_PERMANENT);
829  SendToSwitch(swtch, ofm, ofm->header.length);
830  }
831 }
832 
833 TypeId
835 {
836  static TypeId tid =
837  TypeId("ns3::ofi::LearningController")
839  .SetGroupName("Openflow")
840  .AddConstructor<LearningController>()
841  .AddAttribute("ExpirationTime",
842  "Time it takes for learned MAC state entry/created flow to expire.",
843  TimeValue(Seconds(0)),
845  MakeTimeChecker());
846  return tid;
847 }
848 
849 void
851 {
852  if (m_switches.find(swtch) == m_switches.end())
853  {
854  NS_LOG_ERROR("Can't receive from this switch, not registered to the Controller.");
855  return;
856  }
857 
858  // We have received any packet at this point, so we pull the header to figure out what type of
859  // packet we're handling.
860  uint8_t type = GetPacketType(buffer);
861 
862  if (type == OFPT_PACKET_IN) // The switch didn't understand the packet it received, so it
863  // forwarded it to the controller.
864  {
865  ofp_packet_in* opi = (ofp_packet_in*)ofpbuf_try_pull(buffer, offsetof(ofp_packet_in, data));
866  int port = ntohs(opi->in_port);
867 
868  // Create matching key.
869  sw_flow_key key;
870  key.wildcards = 0;
871  flow_extract(buffer, port != -1 ? port : OFPP_NONE, &key.flow);
872 
873  uint16_t out_port = OFPP_FLOOD;
874  uint16_t in_port = ntohs(key.flow.in_port);
875 
876  // If the destination address is learned to a specific port, find it.
877  Mac48Address dst_addr;
878  dst_addr.CopyFrom(key.flow.dl_dst);
879  if (!dst_addr.IsBroadcast())
880  {
881  auto st = m_learnState.find(dst_addr);
882  if (st != m_learnState.end())
883  {
884  out_port = st->second.port;
885  }
886  else
887  {
888  NS_LOG_INFO("Setting to flood; don't know yet what port " << dst_addr
889  << " is connected to");
890  }
891  }
892  else
893  {
894  NS_LOG_INFO("Setting to flood; this packet is a broadcast");
895  }
896 
897  // Create output-to-port action
898  ofp_action_output x[1];
899  x[0].type = htons(OFPAT_OUTPUT);
900  x[0].len = htons(sizeof(ofp_action_output));
901  x[0].port = out_port;
902 
903  // Create a new flow that outputs matched packets to a learned port, OFPP_FLOOD if there's
904  // no learned port.
905  ofp_flow_mod* ofm = BuildFlow(key,
906  opi->buffer_id,
907  OFPFC_ADD,
908  x,
909  sizeof(x),
910  OFP_FLOW_PERMANENT,
911  m_expirationTime.IsZero() ? OFP_FLOW_PERMANENT
913  SendToSwitch(swtch, ofm, ofm->header.length);
914 
915  // We can learn a specific port for the source address for future use.
916  Mac48Address src_addr;
917  src_addr.CopyFrom(key.flow.dl_src);
918  auto st = m_learnState.find(src_addr);
919  if (st == m_learnState.end()) // We haven't learned our source MAC yet.
920  {
921  LearnedState ls;
922  ls.port = in_port;
923  m_learnState.insert(std::make_pair(src_addr, ls));
924  NS_LOG_INFO("Learned that " << src_addr << " can be found over port " << in_port);
925 
926  // Learn src_addr goes to a certain port.
927  ofp_action_output x2[1];
928  x2[0].type = htons(OFPAT_OUTPUT);
929  x2[0].len = htons(sizeof(ofp_action_output));
930  x2[0].port = in_port;
931 
932  // Switch MAC Addresses and ports to the flow we're modifying
933  src_addr.CopyTo(key.flow.dl_dst);
934  dst_addr.CopyTo(key.flow.dl_src);
935  key.flow.in_port = out_port;
936  ofp_flow_mod* ofm2 = BuildFlow(
937  key,
938  -1,
939  OFPFC_MODIFY,
940  x2,
941  sizeof(x2),
942  OFP_FLOW_PERMANENT,
943  m_expirationTime.IsZero() ? OFP_FLOW_PERMANENT : m_expirationTime.GetSeconds());
944  SendToSwitch(swtch, ofm2, ofm2->header.length);
945  }
946  }
947 }
948 
949 void
951  uint64_t packet_uid,
952  ofpbuf* buffer,
953  sw_flow_key* key,
954  const ofp_action_header* actions,
955  size_t actions_len,
956  int ignore_no_fwd)
957 {
959  /* Every output action needs a separate clone of 'buffer', but the common
960  * case is just a single output action, so that doing a clone and then
961  * freeing the original buffer is wasteful. So the following code is
962  * slightly obscure just to avoid that. */
963  int prev_port;
964  size_t max_len = 0; // Initialize to make compiler happy
965  uint16_t in_port = key->flow.in_port; // ntohs(key->flow.in_port);
966  auto p = (uint8_t*)actions;
967 
968  prev_port = -1;
969 
970  if (actions_len == 0)
971  {
972  NS_LOG_INFO("No actions set to this flow. Dropping packet.");
973  return;
974  }
975 
976  /* The action list was already validated, so we can be a bit looser
977  * in our sanity-checking. */
978  while (actions_len > 0)
979  {
980  ofp_action_header* ah = (ofp_action_header*)p;
981  size_t len = htons(ah->len);
982 
983  if (prev_port != -1)
984  {
985  swtch->DoOutput(packet_uid, in_port, max_len, prev_port, ignore_no_fwd);
986  prev_port = -1;
987  }
988 
989  if (ah->type == htons(OFPAT_OUTPUT))
990  {
991  ofp_action_output* oa = (ofp_action_output*)p;
992 
993  // port is now 32-bits
994  prev_port = oa->port; // ntohl(oa->port);
995  // prev_port = ntohs(oa->port);
996  max_len = ntohs(oa->max_len);
997  }
998  else
999  {
1000  uint16_t type = ntohs(ah->type);
1001  if (Action::IsValidType(
1002  (ofp_action_type)type)) // Execute a built-in OpenFlow action against 'buffer'.
1003  {
1004  Action::Execute((ofp_action_type)type, buffer, key, ah);
1005  }
1006  else if (type == OFPAT_VENDOR)
1007  {
1008  ExecuteVendor(buffer, key, ah);
1009  }
1010  }
1011 
1012  p += len;
1013  actions_len -= len;
1014  }
1015 
1016  if (prev_port != -1)
1017  {
1018  swtch->DoOutput(packet_uid, in_port, max_len, prev_port, ignore_no_fwd);
1019  }
1020 }
1021 
1022 uint16_t
1023 ValidateActions(const sw_flow_key* key, const ofp_action_header* actions, size_t actions_len)
1024 {
1025  auto p = (uint8_t*)actions;
1026  int err;
1027 
1028  while (actions_len >= sizeof(ofp_action_header))
1029  {
1030  ofp_action_header* ah = (ofp_action_header*)p;
1031  size_t len = ntohs(ah->len);
1032  uint16_t type;
1033 
1034  /* Make there's enough remaining data for the specified length
1035  * and that the action length is a multiple of 64 bits. */
1036  if ((actions_len < len) || (len % 8) != 0)
1037  {
1038  return OFPBAC_BAD_LEN;
1039  }
1040 
1041  type = ntohs(ah->type);
1042  if (Action::IsValidType((ofp_action_type)type)) // Validate built-in OpenFlow actions.
1043  {
1044  err = Action::Validate((ofp_action_type)type, len, key, ah);
1045  if (err != ACT_VALIDATION_OK)
1046  {
1047  return err;
1048  }
1049  }
1050  else if (type == OFPAT_VENDOR)
1051  {
1052  err = ValidateVendor(key, ah, len);
1053  if (err != ACT_VALIDATION_OK)
1054  {
1055  return err;
1056  }
1057  }
1058  else
1059  {
1060  return OFPBAC_BAD_TYPE;
1061  }
1062 
1063  p += len;
1064  actions_len -= len;
1065  }
1066 
1067  // Check if there's any trailing garbage.
1068  if (actions_len != 0)
1069  {
1070  return OFPBAC_BAD_LEN;
1071  }
1072 
1073  return ACT_VALIDATION_OK;
1074 }
1075 
1076 void
1078  uint64_t packet_uid,
1079  ofpbuf* buffer,
1080  sw_flow_key* key,
1081  const ofp_action_header* actions,
1082  size_t actions_len)
1083 {
1084  /* Every output action needs a separate clone of 'buffer', but the common
1085  * case is just a single output action, so that doing a clone and then
1086  * freeing the original buffer is wasteful. So the following code is
1087  * slightly obscure just to avoid that. */
1088  int prev_port;
1089  size_t max_len = 0; // Initialize to make compiler happy
1090  uint16_t in_port = ntohs(key->flow.in_port);
1091  auto p = (uint8_t*)actions;
1092  uint16_t type;
1093  ofp_action_output* oa;
1094 
1095  prev_port = -1;
1096  /* The action list was already validated, so we can be a bit looser
1097  * in our sanity-checking. */
1098  while (actions_len > 0)
1099  {
1100  ofp_action_header* ah = (ofp_action_header*)p;
1101  size_t len = htons(ah->len);
1102  if (prev_port != -1)
1103  {
1104  swtch->DoOutput(packet_uid, in_port, max_len, prev_port, false);
1105  prev_port = -1;
1106  }
1107 
1108  if (ah->type == htons(OFPAT_OUTPUT))
1109  {
1110  oa = (ofp_action_output*)p;
1111  prev_port = ntohl(oa->port);
1112  max_len = ntohs(oa->max_len);
1113  }
1114  else
1115  {
1116  type = ah->type; // ntohs(ah->type);
1117  VPortAction::Execute((ofp_vport_action_type)type, buffer, key, ah);
1118  }
1119 
1120  p += len;
1121  actions_len -= len;
1122  }
1123 
1124  if (prev_port != -1)
1125  {
1126  swtch->DoOutput(packet_uid, in_port, max_len, prev_port, false);
1127  }
1128 }
1129 
1130 uint16_t
1131 ValidateVPortActions(const ofp_action_header* actions, size_t actions_len)
1132 {
1133  auto p = (uint8_t*)actions;
1134  int err;
1135 
1136  while (actions_len >= sizeof(ofp_action_header))
1137  {
1138  ofp_action_header* ah = (ofp_action_header*)p;
1139  size_t len = ntohs(ah->len);
1140  uint16_t type;
1141 
1142  /* Make there's enough remaining data for the specified length
1143  * and that the action length is a multiple of 64 bits. */
1144  if ((actions_len < len) || (len % 8) != 0)
1145  {
1146  return OFPBAC_BAD_LEN;
1147  }
1148 
1149  type = ntohs(ah->type);
1151  (ofp_vport_action_type)type)) // Validate "built-in" OpenFlow port table actions.
1152  {
1153  err = VPortAction::Validate((ofp_vport_action_type)type, len, ah);
1154  if (err != ACT_VALIDATION_OK)
1155  {
1156  return err;
1157  }
1158  }
1159  else
1160  {
1161  return OFPBAC_BAD_TYPE;
1162  }
1163 
1164  p += len;
1165  actions_len -= len;
1166  }
1167 
1168  // Check if there's any trailing garbage.
1169  if (actions_len != 0)
1170  {
1171  return OFPBAC_BAD_LEN;
1172  }
1173 
1174  return ACT_VALIDATION_OK;
1175 }
1176 
1177 void
1178 ExecuteVendor(ofpbuf* buffer, const sw_flow_key* key, const ofp_action_header* ah)
1179 {
1180  ofp_action_vendor_header* avh = (ofp_action_vendor_header*)ah;
1181 
1182  switch (ntohl(avh->vendor))
1183  {
1184  case NX_VENDOR_ID:
1185  // Nothing to execute yet.
1186  break;
1187  case ER_VENDOR_ID: {
1188  const er_action_header* erah = (const er_action_header*)avh;
1189  EricssonAction::Execute((er_action_type)ntohs(erah->subtype), buffer, key, erah);
1190  break;
1191  }
1192  default:
1193  // This should not be possible due to prior validation.
1194  NS_LOG_INFO("attempt to execute action with unknown vendor: " << ntohl(avh->vendor));
1195  break;
1196  }
1197 }
1198 
1199 uint16_t
1200 ValidateVendor(const sw_flow_key* key, const ofp_action_header* ah, uint16_t len)
1201 {
1202  ofp_action_vendor_header* avh;
1203  int ret = ACT_VALIDATION_OK;
1204 
1205  if (len < sizeof(ofp_action_vendor_header))
1206  {
1207  return OFPBAC_BAD_LEN;
1208  }
1209 
1210  avh = (ofp_action_vendor_header*)ah;
1211 
1212  switch (ntohl(avh->vendor))
1213  {
1214  case NX_VENDOR_ID: // Validate Nicara OpenFlow actions.
1215  ret = OFPBAC_BAD_VENDOR_TYPE; // Nothing to validate yet.
1216  break;
1217  case ER_VENDOR_ID: // Validate Ericsson OpenFlow actions.
1218  {
1219  const er_action_header* erah = (const er_action_header*)avh;
1220  ret = EricssonAction::Validate((er_action_type)ntohs(erah->subtype), len);
1221  break;
1222  }
1223  default:
1224  return OFPBAC_BAD_VENDOR;
1225  }
1226 
1227  return ret;
1228 }
1229 
1230 } // namespace ofi
1231 
1232 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:42
an EUI-48 address
Definition: mac48-address.h:46
void CopyFrom(const uint8_t buffer[6])
void CopyTo(uint8_t buffer[6]) const
bool IsBroadcast() const
A base class which provides memory management and object aggregation.
Definition: object.h:89
static const char * GetManufacturerDescription()
void StatsDone(ofi::StatsDumpCallback *cb_)
Stats callback is done.
int StatsDump(ofi::StatsDumpCallback *cb_)
Stats callback is ready for a dump.
static const char * GetSoftwareDescription()
static const char * GetHardwareDescription()
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:403
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
An interface for a Controller of OpenFlowSwitchNetDevices.
virtual void SendToSwitch(Ptr< OpenFlowSwitchNetDevice > swtch, void *msg, size_t length)
However the controller is implemented, this method is to be used to pass a message on to a switch.
uint8_t GetPacketType(ofpbuf *buffer)
Get the packet type on the buffer, which can then be used to determine how to handle the buffer.
Switches_t m_switches
The collection of switches registered to this controller.
~Controller() override
Destructor.
static TypeId GetTypeId()
Register this type.
ofp_flow_mod * BuildFlow(sw_flow_key key, uint32_t buffer_id, uint16_t command, void *acts, size_t actions_len, int idle_timeout, int hard_timeout)
Construct flow data from a matching key to build a flow entry for adding, modifying,...
void StartDump(StatsDumpCallback *cb)
Starts a callback-based, reliable, possibly multi-message reply to a request made by the controller.
virtual void AddSwitch(Ptr< OpenFlowSwitchNetDevice > swtch)
Adds a switch to the controller.
Demonstration of a Drop controller.
void ReceiveFromSwitch(Ptr< OpenFlowSwitchNetDevice > swtch, ofpbuf *buffer) override
A switch calls this method to pass a message on to the Controller.
static TypeId GetTypeId()
Register this type.
Demonstration of a Learning controller.
static TypeId GetTypeId()
Register this type.
void ReceiveFromSwitch(Ptr< OpenFlowSwitchNetDevice > swtch, ofpbuf *buffer) override
A switch calls this method to pass a message on to the Controller.
Time m_expirationTime
Time it takes for learned MAC state entry/created flow to expire.
LearnState_t m_learnState
Learned state data.
int PortStatsInit(const void *body, int body_len, void **state)
Initialize the stats.
int PortTableStatsDump(Ptr< OpenFlowSwitchNetDevice > dp, void *state, ofpbuf *buffer)
Dump the stats.
int FlowStatsDump(Ptr< OpenFlowSwitchNetDevice > dp, FlowStatsState *state, ofpbuf *buffer)
Dump the stats.
int(* AggregateDumpCallback)(sw_flow *flow, void *state)
Aggregate dump callback functor.
Stats(ofp_stats_types _type, size_t body_len)
Constructor.
void DoCleanup(void *state)
Cleans any state created by the init or dump functions.
int AggregateStatsInit(const void *body, int body_len, void **state)
Initialize the stats.
int DoDump(Ptr< OpenFlowSwitchNetDevice > swtch, void *state, ofpbuf *buffer)
Appends statistics for OpenFlowSwitchNetDevice to 'buffer'.
int DoInit(const void *body, int body_len, void **state)
Prepares to dump some kind of statistics on the connected OpenFlowSwitchNetDevice.
int AggregateStatsDump(Ptr< OpenFlowSwitchNetDevice > dp, ofp_aggregate_stats_request *state, ofpbuf *buffer)
Dump the stats.
int TableStatsDump(Ptr< OpenFlowSwitchNetDevice > dp, void *state, ofpbuf *buffer)
Dump the stats.
int PortStatsDump(Ptr< OpenFlowSwitchNetDevice > dp, PortStatsState *state, ofpbuf *buffer)
Dump the stats.
ofp_stats_types type
Status type.
int FlowStatsInit(const void *body, int body_len, void **state)
Initialize the stats.
int DescStatsDump(void *state, ofpbuf *buffer)
Dumps the stats description.
int(* FlowDumpCallback)(sw_flow *flow, void *state)
Flow dump callback functor.
uint16_t port
Definition: dsdv-manet.cc:44
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:254
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
void ExecuteVPortActions(Ptr< OpenFlowSwitchNetDevice > swtch, uint64_t packet_uid, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len)
Executes a list of virtual port table entry actions.
void ExecuteActions(Ptr< OpenFlowSwitchNetDevice > swtch, uint64_t packet_uid, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len, int ignore_no_fwd)
Executes a list of flow table actions.
uint16_t ValidateVendor(const sw_flow_key *key, const ofp_action_header *ah, uint16_t len)
Validates a vendor-defined action.
void ExecuteVendor(ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah)
Executes a vendor-defined action.
uint16_t ValidateActions(const sw_flow_key *key, const ofp_action_header *actions, size_t actions_len)
Validates a list of flow table actions.
int Stats_AggregateDumpCallback(sw_flow *flow, void *state)
int Stats_FlowDumpCallback(sw_flow *flow, void *state)
uint16_t ValidateVPortActions(const ofp_action_header *actions, size_t actions_len)
Validates a list of virtual port table entry actions.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
#define MAX_FLOW_STATS_BYTES
void strip_vlan(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_mpls_label(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_dl_addr(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_nw_addr(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_vlan_pcp(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_tp_port(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_mpls_exp(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
void set_vlan_vid(ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
uint8_t data[writeSize]
static bool IsValidType(ofp_action_type type)
static void Execute(ofp_action_type type, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah)
Executes the action.
static uint16_t Validate(ofp_action_type type, size_t len, const sw_flow_key *key, const ofp_action_header *ah)
Validates the action on whether its data is valid or not.
static bool IsValidType(er_action_type type)
static void Execute(er_action_type type, ofpbuf *buffer, const sw_flow_key *key, const er_action_header *ah)
Executes the action.
static uint16_t Validate(er_action_type type, size_t len)
Validates the action on whether its data is valid or not.
Port and its metadata.
Ptr< NetDevice > netdev
NetDevice pointer.
unsigned long long int mpls_ttl0_dropped
Counter of packets dropped due to MPLS TTL.
unsigned long long int tx_packets
Counter of Tx packets.
unsigned long long int tx_bytes
Counter of Tx bytes.
unsigned long long int rx_packets
Counter of Rx packets.
unsigned long long int rx_bytes
Counter of Rx bytes.
unsigned long long int tx_dropped
Counter of Tx dropped packets.
State of the FlowStats request/reply.
ofp_flow_stats_request rq
Stats requests.
sw_table_position position
Table position.
State of the PortStats request/reply.
uint32_t * ports
Array of ports in network byte order.
uint32_t num_ports
Number of ports in host byte order.
Callback for a stats dump request.
Ptr< OpenFlowSwitchNetDevice > swtch
The switch that we're requesting data from.
static uint16_t Validate(ofp_vport_action_type type, size_t len, const ofp_action_header *ah)
Validates the action on whether its data is valid or not.
static void Execute(ofp_vport_action_type type, ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah)
Executes the action.
static bool IsValidType(ofp_vport_action_type type)