A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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
23namespace ns3
24{
25
26NS_LOG_COMPONENT_DEFINE("OpenFlowInterface");
27
28namespace ofi
29{
30
31Stats::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
69int
70Stats::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
93int
94Stats::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
117void
118Stats::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
143int
144Stats::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
158int
159Stats::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
171int
172Stats_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
204int
205Stats::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
234int
235Stats::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
242int
243Stats_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
252int
253Stats::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
292int
293Stats::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
313int
314Stats::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
326int
327Stats::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
340int
341Stats::PortStatsDump(Ptr<OpenFlowSwitchNetDevice> swtch, PortStatsState* s, ofpbuf* buffer)
342{
343 ofp_port_stats* ops;
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
409bool
410Action::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
432uint16_t
433Action::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
498void
499Action::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
537bool
538VPortAction::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
552uint16_t
553VPortAction::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
582void
583VPortAction::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
615bool
616EricssonAction::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
628uint16_t
629EricssonAction::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
652void
653EricssonAction::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 */
676TypeId
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
691void
692Controller::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
704void
705Controller::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
716ofp_flow_mod*
717Controller::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
758uint8_t
759Controller::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
767void
768Controller::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 */
789TypeId
791{
792 static TypeId tid = TypeId("ns3::ofi::DropController")
793 .SetParent<Controller>()
794 .SetGroupName("OpenFlow")
795 .AddConstructor<DropController>();
796 return tid;
797}
798
799void
800DropController::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
834TypeId
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)),
847 return tid;
848}
849
850void
851LearningController::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
950void
951ExecuteActions(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);
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
1023uint16_t
1024ValidateActions(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
1077void
1078ExecuteVPortActions(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
1131uint16_t
1132ValidateVPortActions(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
1178void
1179ExecuteVendor(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
1200uint16_t
1201ValidateVendor(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
Object()
Constructor.
Definition: object.cc:95
static const char * GetSoftwareDescription()
static const char * GetManufacturerDescription()
static const char * GetHardwareDescription()
static const char * GetSerialNumber()
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:651
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:936
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:44
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1444
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
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.
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.