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