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