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