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
23namespace ns3 {
24
25NS_LOG_COMPONENT_DEFINE ("OpenFlowInterface");
26
27namespace ofi {
28
29Stats::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
64int
65Stats::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
88int
89Stats::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
112void
113Stats::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
138int
139Stats::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
151int
152Stats::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
164int
165Stats_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
197int
198Stats::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
222int
223Stats::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
230int
231Stats_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
240int
241Stats::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
272int
273Stats::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
293int
294Stats::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
306int
307Stats::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
320int
321Stats::PortStatsDump (Ptr<OpenFlowSwitchNetDevice> swtch, PortStatsState *s, ofpbuf *buffer)
322{
323 ofp_port_stats *ops;
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
389bool
390Action::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
412uint16_t
413Action::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
476void
477Action::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
515bool
516VPortAction::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
530uint16_t
531VPortAction::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
560void
561VPortAction::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
594bool
595EricssonAction::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
607uint16_t
608EricssonAction::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
631void
632EricssonAction::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 */
654TypeId
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
670void
671Controller::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
683void
684Controller::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
695ofp_flow_mod*
696Controller::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
726uint8_t
727Controller::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
735void
736Controller::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 */
757TypeId
759{
760 static TypeId tid = TypeId ("ns3::ofi::DropController")
761 .SetParent<Controller> ()
762 .SetGroupName ("OpenFlow")
763 .AddConstructor<DropController> ()
764 ;
765 return tid;
766}
767
768void
769DropController::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
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)),
806 ;
807 return tid;
808}
809
810void
811LearningController::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
892void
893ExecuteActions (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
958uint16_t
959ValidateActions (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
1012void
1013ExecuteVPortActions (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
1061uint16_t
1062ValidateVPortActions (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
1107void
1108ExecuteVendor (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
1130uint16_t
1131ValidateVendor (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
#define max(a, b)
Definition: 80211b.c:43
Object()
Constructor.
Definition: object.cc:96
static const char * GetSoftwareDescription()
static const char * GetManufacturerDescription()
static const char * GetHardwareDescription()
static const char * GetSerialNumber()
double GetSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:379
bool IsZero(void) const
Exactly equivalent to t == 0.
Definition: nstime.h:300
TypeId AddConstructor(void)
Record in this TypeId the fact that the default constructor is accessible.
Definition: type-id.h:638
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:922
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.
static TypeId GetTypeId(void)
Register this type.
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,...
virtual ~Controller()
Destructor.
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.
void ReceiveFromSwitch(Ptr< OpenFlowSwitchNetDevice > swtch, ofpbuf *buffer)
A switch calls this method to pass a message on to the Controller.
static TypeId GetTypeId(void)
Register this type.
static TypeId GetTypeId(void)
Register this type.
void ReceiveFromSwitch(Ptr< OpenFlowSwitchNetDevice > swtch, ofpbuf *buffer)
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)
Definition: nstime.h:1309
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:257
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#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:265
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1244
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:536
list x
Random number samples.
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.