├── LICENSE ├── README.md ├── imgs ├── group_table.png ├── group_table1.png ├── pipeline.png ├── topo1.png ├── topo2.png └── topo3.png ├── mininet_part1.md ├── mininet_part2.md ├── mininet_part3.md ├── mininet_topologies ├── README.md ├── example1.py ├── example2.py ├── example3.py ├── example4.py ├── example5.py ├── group_table_lb.py └── group_table_topo.py ├── openflow_overview.md ├── overview.md ├── ryu-exercises ├── README.md ├── ex2_L3Match_switch.py ├── ex3_L4Match_switch.py ├── ex4_flow_timeout.py ├── ex5_flow_priority.py ├── ex6_multiple_tables.py ├── ex7_group_tables.py ├── ex8_arp_proxy.py ├── ex9_topology_discovery.py ├── hub.py ├── load_balancer.py └── simple_switch_13.py ├── ryu_hub_exercise.md ├── ryu_part1.md ├── ryu_part10.md ├── ryu_part2.md ├── ryu_part3.md ├── ryu_part4.md ├── ryu_part5.md ├── ryu_part6.md ├── ryu_part7.md ├── ryu_part8.md ├── ryu_part9.md ├── sdn_test_bed_setup.md └── wireshark_traces └── Openflow13_GoodMsg.pcapng /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # SDN Crash Course with RYU SDN Controller - Updated(2019) - 11 Hours Video 3 | - Course Link 4 | https://www.udemy.com/course/beginners-sdn-course-with-ryu-controller-practical-handson/?referralCode=3C4DD08C0DB3F68704B7 5 | 6 | Topics: 7 | 1. SDN Theory and Lab 8 | 2. RYU Controller 9 | 3. RYU Controller Programming 10 | 4. VxLAN (Network Overlay) 11 | 5. Data Traffic Tests 12 | 6. Mininet WiFI 13 | 14 | - Extensive documentations(step by step guide) and sample programs 15 | 16 | https://www.youtube.com/playlist?list=PLFY0hc9nGyb3Oduw6gl7F1L_UIpk9fZiE 17 | 18 | # SDN Academic project assistance: 19 | 20 | we do provide SDN Project assistance. 21 | 22 | Example Projects videos in Youtube 23 | 24 | https://www.youtube.com/watch?v=gL6z60SF9V4&list=PLFY0hc9nGyb3B1uqdy4-k-xj-m0aSU5Sv 25 | 26 | More details 27 | 28 | http://knetsolutions.in/ 29 | 30 | Whatsapp: +919445042007 31 | 32 | -------------------------------------------------------------------------------- /imgs/group_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/group_table.png -------------------------------------------------------------------------------- /imgs/group_table1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/group_table1.png -------------------------------------------------------------------------------- /imgs/pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/pipeline.png -------------------------------------------------------------------------------- /imgs/topo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/topo1.png -------------------------------------------------------------------------------- /imgs/topo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/topo2.png -------------------------------------------------------------------------------- /imgs/topo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/imgs/topo3.png -------------------------------------------------------------------------------- /mininet_part1.md: -------------------------------------------------------------------------------- 1 | Mininet Basic Commands: 2 | ========================= 3 | 4 | 5 | ## 1. To check the mininet version 6 | 7 | 8 | ``` 9 | mn --version 10 | ``` 11 | 12 | 13 | ## 2. To clean up the existing ovs bridges and namespaces 14 | 15 | 16 | Note: sometime we mistakenly closed the mininet shell, or mininet crashed. But the topology components will continue to exists. To clean such stuff, cleanup command is used. 17 | 18 | 19 | ``` 20 | mn -c 21 | ``` 22 | 23 | 24 | ## 3. Our First Topology (Single) 25 | 26 | 27 | 28 | Topology with Single Switch and 4 Nodes. 29 | 30 | ![Alt text](imgs/topo1.png?raw=true "Single Topology") 31 | 32 | RYU SDN Controller 33 | 34 | ``` 35 | ryu-manager ryu.app.simple_switch_13 36 | ``` 37 | 38 | Mininet Topology 39 | 40 | ``` 41 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=single,4 42 | ``` 43 | 44 | 45 | | options | Description | 46 | |-------------|-----------------------------------------------------------------------| 47 | |--controller | type of controller local/remote and remote controller ip. | 48 | | | | 49 | |--mac | mac address starts with 00:00:00:00:00:01 | 50 | | | | 51 | |-i | IP Subnets for the Topology | 52 | | | | 53 | |--switch | Switch type (ovsk - openvswitch kernel module), and openflow version. | 54 | | | | 55 | |--topo | topology type(linear,minimal,reversed,single,torus,tree) and params. | 56 | 57 | 58 | 59 | ## 4. Mininet Basic Shell Commands 60 | 61 | 62 | 63 | Informative commands 64 | 65 | ``` 66 | help 67 | dump 68 | net 69 | links 70 | ``` 71 | 72 | Action commands 73 | ``` 74 | pingall 75 | {args} 76 | h1 ifconfig 77 | h1 ping h2 78 | h1 ip route 79 | ``` 80 | 81 | ## 5. Linear Topology 82 | 83 | linear topology (where each switch has one host, and all switches connect in a line) 84 | 85 | 86 | ![Alt text](imgs/topo2.png?raw=true "Linear Topology") 87 | 88 | 89 | ``` 90 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 91 | ``` 92 | 93 | 94 | 95 | ## 6. Tree Topology 96 | 97 | 98 | ![Alt text](imgs/topo3.png?raw=true "Tree Topology") 99 | 100 | ``` 101 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --topo=tree,depth=2,fanout=3 102 | 103 | ``` 104 | 105 | 106 | fanout : each switch is connected to these many childs 107 | depth : depth of the tree 108 | 109 | 110 | 111 | References: 112 | -------------- 113 | 114 | 1. http://mininet.org/walkthrough/ 115 | 116 | 2. https://en.wikipedia.org/wiki/Torus_interconnect 117 | 118 | -------------------------------------------------------------------------------- /mininet_part2.md: -------------------------------------------------------------------------------- 1 | # Mininet Commands: 2 | 3 | 4 | ## 1. TCP/UDP Traffic Tests 5 | 6 | 7 | ### a. Setup the Topology with xterms options (open terminal for each Node) 8 | 9 | ![Alt text](imgs/topo2.png?raw=true "Linear Topology") 10 | 11 | 12 | ``` 13 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 -x 14 | ``` 15 | 16 | ### a. TCP Traffic Test between h1 to h4 17 | 18 | 19 | Run IPERF TCP Server in h4 20 | 21 | ``` 22 | iperf -s 23 | 24 | ``` 25 | 26 | 27 | RUN IPERF TCP Client in h1 28 | 29 | 30 | ``` 31 | iperf -c 10.1.1.4 -i 10 -t 30 32 | iperf -c 10.1.1.4 -i 10 -P 10 -t 30 33 | 34 | ``` 35 | 36 | 37 | ### b. UDP Traffic Test between h1 to h4 38 | 39 | 40 | Run IPERF UDP Server in h4 41 | 42 | ``` 43 | iperf -s -u 44 | 45 | ``` 46 | 47 | 48 | RUN IPERF UDP Client in h1 49 | 50 | 51 | ``` 52 | iperf -c 10.1.1.4 -b 10m -i 10 -t 30 53 | iperf -c 10.1.1.4 -b 10m -i 10 -P 10 -t 30 54 | 55 | ``` 56 | 57 | 58 | 59 | 60 | ## 2. Torus Topology 61 | 62 | >Torus Topology is mesh interconnect with nodes arranged in a rectilinear array of N = 3, or more dimensions 63 | >3,3 = 3 Rows and 3 columns (Matrix) of Nodes and Switches, and inter connected. 64 | 65 | This topology forms a Loop. 66 | 67 | 68 | ``` 69 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=torus,3,3 70 | 71 | ``` 72 | 73 | References: 74 | -------------- 75 | 76 | 1. http://mininet.org/walkthrough/ 77 | 78 | 2. https://en.wikipedia.org/wiki/Torus_interconnect 79 | 80 | -------------------------------------------------------------------------------- /mininet_part3.md: -------------------------------------------------------------------------------- 1 | # Writing Custom Topology in Mininet 2 | 3 | This tutorial helps to learn on writing mininet custom topology. 4 | 5 | ## Introduction 6 | 7 | mininet exposes the python API. We can create a custom topologies using the python API with few lines of code. 8 | 9 | 10 | ## How to write Custom Topology in Mininet 11 | 12 | Steps are below. 13 | 14 | 15 | ### 1. Import the python required libraries 16 | 17 | ``` 18 | from mininet.topo import Topo 19 | from mininet.net import Mininet 20 | ``` 21 | 22 | ### 2. Write the Topology definition class 23 | 24 | ``` 25 | class CustomTopo(Topo): 26 | def build(self): 27 | s1 = self.addSwitch('s1') 28 | h1 = self.addHost('h1') 29 | h2 = self.addHost('h2') 30 | self.addLink(h1, s1) 31 | self.addLink(h2, s2) 32 | 33 | ``` 34 | 35 | Important Topology definition APIs: 36 | 37 | a. addSwitch, 38 | 39 | b. addHost, 40 | 41 | c. addLink 42 | 43 | 44 | 45 | ### 3. Start the Topology 46 | 47 | 48 | a. Create the Topology object 49 | 50 | b. Create the Mininet with Topology object 51 | 52 | c. Start the Mininet 53 | 54 | 55 | ``` 56 | if __name__ == '__main__': 57 | topo = SingleSwitchTopo() 58 | net = Mininet(topo) 59 | net.start() 60 | ``` 61 | 62 | 63 | ## References: 64 | 65 | 1. https://github.com/mininet/mininet/wiki/Introduction-to-Mininet 66 | 67 | 2. https://github.com/mininet/mininet/tree/master/examples 68 | 69 | 3. http://mininet.org/api/annotated.html 70 | 71 | # To read 72 | 4. https://kiranvemuri.info/dynamic-topology-changes-in-mininet-advanced-users-5c452f7f302a 73 | 74 | 4. https://stackoverflow.com/questions/43070043/mininet-wifi-custom-topology 75 | https://stackoverflow.com/questions/43070043/mininet-wifi-custom-topology -------------------------------------------------------------------------------- /mininet_topologies/README.md: -------------------------------------------------------------------------------- 1 | # mininet-custom topologies 2 | 3 | ## Example1 4 | 5 | In the example1, we creates a Linear Topology with two hosts and two switches with local controller and runs the pingall command and exits it. 6 | 7 | 8 | Run the RYU app 9 | 10 | ``` 11 | ryu-manager ryu.app.simple_switch_13 12 | ``` 13 | 14 | 15 | Run the Mininet Custome Topology file 16 | 17 | ``` 18 | python example1.py 19 | ``` 20 | 21 | 22 | 23 | 24 | ## Example2 25 | 26 | In the example2, we creates a Linear Topology 27 | 1. Two hosts and two switches. 28 | 2. Assigns the custom MAC Address, IP address for each Host. 29 | 3. Using external controller 30 | 4. start the CLI Prompt 31 | 32 | 33 | Run the RYU app 34 | 35 | ``` 36 | ryu-manager ryu.app.simple_switch_13 37 | ``` 38 | 39 | 40 | Run the Mininet Custome Topology file 41 | 42 | ``` 43 | python example2.py 44 | ``` 45 | 46 | 47 | 48 | ## Example3 49 | 50 | In the example3, we creates a RING Topology 51 | 1. Four hosts and Four switches 52 | 2. Switches are connected in RING model. 53 | 2. Assigns the custom MAC Address, IP address for each Host. 54 | 3. Using external controller 55 | 4. start the CLI Prompt 56 | 57 | Note: We must use SDN STP application. 58 | 59 | 60 | Run the RYU app 61 | 62 | ``` 63 | ryu-manager ryu.app.simple_switch_stp 64 | ``` 65 | 66 | 67 | Run the Mininet Custome Topology file 68 | 69 | ``` 70 | python example1.py 71 | ``` 72 | 73 | 74 | ## Example4 75 | 76 | In the example4, we creates a Linear Topology and runs IPERF TCP Test between the hosts 77 | 1. Two hosts and two switches. 78 | 2. Assigns the custom MAC Address, IP address for each Host. 79 | 3. Using external controller 80 | 4. Run the iperf server command in the h1 host 81 | 5. Run the iperf client command in the h2 host 82 | 6. Start the CLI Prompt 83 | 84 | 85 | 86 | 87 | Run the RYU app 88 | 89 | ``` 90 | ryu-manager ryu.app.simple_switch_13 91 | ``` 92 | 93 | 94 | Run the Mininet Custome Topology file 95 | 96 | ``` 97 | python example4.py 98 | ``` 99 | 100 | 101 | ## Example5 102 | 103 | In the example4, we creates a Linear Topology and switch off and on the links. 104 | 1. Four hosts and Four switches. 105 | 2. Assigns the custom MAC Address, IP address for each Host. 106 | 3. Using external controller 107 | 4. pingall 108 | 5. shutdown s3 to s4 link 109 | 6. pingall 110 | 7. bringback s3 to s4 link 111 | 8. pingall 112 | 113 | 114 | 115 | 116 | Run the RYU app 117 | 118 | ``` 119 | ryu-manager ryu.app.simple_switch_13 120 | ``` 121 | 122 | 123 | Run the Mininet Custome Topology file 124 | 125 | ``` 126 | python example5.py 127 | ``` 128 | 129 | 130 | -------------------------------------------------------------------------------- /mininet_topologies/example1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Custom topology example 4 | 5 | Two directly connected switches plus a host for each switch: 6 | 7 | host --- switch --- switch --- host 8 | 9 | """ 10 | 11 | from mininet.topo import Topo 12 | from mininet.net import Mininet 13 | from mininet.log import setLogLevel 14 | from mininet.cli import CLI 15 | from time import sleep 16 | 17 | 18 | 19 | class SingleSwitchTopo(Topo): 20 | "Single switch connected to n hosts." 21 | def build(self): 22 | s1 = self.addSwitch('s1') 23 | s2 = self.addSwitch('s2') 24 | h1 = self.addHost('h1') 25 | h2 = self.addHost('h2') 26 | 27 | self.addLink(h1, s1) 28 | self.addLink(h2, s2) 29 | self.addLink(s1, s2) 30 | 31 | if __name__ == '__main__': 32 | setLogLevel('info') 33 | topo = SingleSwitchTopo() 34 | net = Mininet(topo) 35 | net.start() 36 | sleep(5) 37 | net.pingAll() 38 | #CLI(net) 39 | net.stop() 40 | -------------------------------------------------------------------------------- /mininet_topologies/example2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Custom topology example 5 | 6 | Two directly connected switches plus a host for each switch: 7 | 8 | host --- switch --- switch --- host 9 | 10 | MAC,IP, Controller, CLI stuff configured 11 | 12 | """ 13 | 14 | from mininet.topo import Topo 15 | from mininet.net import Mininet 16 | from mininet.log import setLogLevel 17 | from mininet.cli import CLI 18 | from mininet.node import OVSSwitch, Controller, RemoteController 19 | 20 | class SingleSwitchTopo(Topo): 21 | "Single switch connected to n hosts." 22 | def build(self): 23 | s1 = self.addSwitch('s1') 24 | s2 = self.addSwitch('s2') 25 | h1 = self.addHost('h1', mac="00:00:00:00:11:11", ip="192.168.1.1/24") 26 | h2 = self.addHost('h2', mac="00:00:00:00:11:12", ip="192.168.1.2/24") 27 | 28 | self.addLink(h1, s1) 29 | self.addLink(h2, s2) 30 | self.addLink(s1, s2) 31 | 32 | if __name__ == '__main__': 33 | setLogLevel('info') 34 | topo = SingleSwitchTopo() 35 | c1 = RemoteController('c1', ip='127.0.0.1') 36 | net = Mininet(topo=topo, controller=c1) 37 | net.start() 38 | #net.pingAll() 39 | CLI(net) 40 | net.stop() 41 | -------------------------------------------------------------------------------- /mininet_topologies/example3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Custom topology example 5 | 6 | RING Topology(Loop), 7 | 8 | Switch1-----Switch2------Switch3-----Switch4-----Switch1 9 | 10 | 11 | Switch1-----Host1 12 | Switch2-----Host2 13 | Switch3-----Host3 14 | Switch4-----Host4 15 | 16 | MAC,IP, Controller, CLI stuff configured 17 | 18 | # run RYU SDN STP Application for this topology 19 | 20 | ryu-manager ryu.app.simple_switch_stp 21 | 22 | """ 23 | 24 | from mininet.topo import Topo 25 | from mininet.net import Mininet 26 | from mininet.log import setLogLevel 27 | from mininet.cli import CLI 28 | from mininet.node import OVSSwitch, Controller, RemoteController 29 | 30 | class SingleSwitchTopo(Topo): 31 | "Single switch connected to n hosts." 32 | def build(self): 33 | s1 = self.addSwitch('s1') 34 | s2 = self.addSwitch('s2') 35 | s3 = self.addSwitch('s3') 36 | s4 = self.addSwitch('s4') 37 | 38 | h1 = self.addHost('h1', mac="00:00:00:00:11:11", ip="192.168.1.1/24") 39 | h2 = self.addHost('h2', mac="00:00:00:00:11:12", ip="192.168.1.2/24") 40 | h3 = self.addHost('h3', mac="00:00:00:00:11:13", ip="192.168.1.3/24") 41 | h4 = self.addHost('h4', mac="00:00:00:00:11:14", ip="192.168.1.4/24") 42 | 43 | self.addLink(h1, s1) 44 | self.addLink(h2, s2) 45 | self.addLink(h3, s3) 46 | self.addLink(h4, s4) 47 | 48 | self.addLink(s1, s2) 49 | self.addLink(s2, s3) 50 | self.addLink(s3, s4) 51 | self.addLink(s4, s1) 52 | 53 | if __name__ == '__main__': 54 | setLogLevel('info') 55 | topo = SingleSwitchTopo() 56 | c1 = RemoteController('c1', ip='127.0.0.1') 57 | net = Mininet(topo=topo, controller=c1) 58 | net.start() 59 | #net.pingAll() 60 | CLI(net) 61 | net.stop() 62 | -------------------------------------------------------------------------------- /mininet_topologies/example4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Custom topology example 5 | 6 | Two directly connected switches plus a host for each switch: 7 | 8 | host1 --- switch --- switch --- host2 9 | 10 | Triggers the IPERF Traffic from host1 to host2 11 | 12 | """ 13 | 14 | from mininet.topo import Topo 15 | from mininet.net import Mininet 16 | from mininet.log import setLogLevel 17 | from mininet.cli import CLI 18 | from mininet.node import RemoteController 19 | from time import sleep 20 | 21 | 22 | class SingleSwitchTopo(Topo): 23 | "Single switch connected to n hosts." 24 | def build(self): 25 | s1 = self.addSwitch('s1') 26 | s2 = self.addSwitch('s2') 27 | 28 | h1 = self.addHost('h1', mac="00:00:00:00:11:11", ip="192.168.1.1/24") 29 | h2 = self.addHost('h2', mac="00:00:00:00:11:12", ip="192.168.1.2/24") 30 | 31 | self.addLink(h1, s1) 32 | self.addLink(h2, s2) 33 | 34 | self.addLink(s1, s2) 35 | 36 | 37 | if __name__ == '__main__': 38 | setLogLevel('info') 39 | topo = SingleSwitchTopo() 40 | c1 = RemoteController('c1', ip='127.0.0.1') 41 | net = Mininet(topo=topo, controller=c1) 42 | net.start() 43 | sleep(5) 44 | net.pingAll() 45 | 46 | # get the host objects 47 | h1 = net.get('h1') 48 | h2 = net.get('h2') 49 | h1.cmd('iperf -s &') 50 | result = h2.cmd('iperf -c 192.168.1.1') 51 | print result 52 | CLI(net) 53 | net.stop() 54 | -------------------------------------------------------------------------------- /mininet_topologies/example5.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Custom topology example 5 | 6 | Linear Topology, 7 | 8 | Switch1-----Switch2------Switch3-----Switch4 9 | 10 | 11 | Switch1-----Host1 12 | Switch2-----Host2 13 | Switch3-----Host3 14 | Switch4-----Host4 15 | 16 | 1) pingall 17 | 2) bring down link switch3 to Switch4 18 | h4 will not ping 19 | 3) do pingall and check 20 | 4) bring up link switch3 to switch4 again 21 | all should ping 22 | 5) do pingall and check 23 | 24 | 25 | ryu stuff: 26 | 27 | ryu-manager ryu.app.simple_switch_13 28 | 29 | """ 30 | 31 | from mininet.topo import Topo 32 | from mininet.net import Mininet 33 | from mininet.log import setLogLevel 34 | from mininet.cli import CLI 35 | from mininet.node import OVSSwitch, Controller, RemoteController 36 | from time import sleep 37 | 38 | 39 | class SingleSwitchTopo(Topo): 40 | "Single switch connected to n hosts." 41 | def build(self): 42 | s1 = self.addSwitch('s1') 43 | s2 = self.addSwitch('s2') 44 | s3 = self.addSwitch('s3') 45 | s4 = self.addSwitch('s4') 46 | 47 | h1 = self.addHost('h1', mac="00:00:00:00:11:11", ip="192.168.1.1/24") 48 | h2 = self.addHost('h2', mac="00:00:00:00:11:12", ip="192.168.1.2/24") 49 | h3 = self.addHost('h3', mac="00:00:00:00:11:13", ip="192.168.1.3/24") 50 | h4 = self.addHost('h4', mac="00:00:00:00:11:14", ip="192.168.1.4/24") 51 | 52 | self.addLink(h1, s1) 53 | self.addLink(h2, s2) 54 | self.addLink(h3, s3) 55 | self.addLink(h4, s4) 56 | 57 | self.addLink(s1, s2) 58 | self.addLink(s2, s3) 59 | self.addLink(s3, s4) 60 | 61 | if __name__ == '__main__': 62 | setLogLevel('info') 63 | topo = SingleSwitchTopo() 64 | c1 = RemoteController('c1', ip='127.0.0.1') 65 | net = Mininet(topo=topo, controller=c1) 66 | net.start() 67 | sleep(5) 68 | print("Topology is up, lets ping") 69 | net.pingAll() 70 | print("Link S3 to S4 - bringing down - h4 will not be reachable(ping)") 71 | net.configLinkStatus('s3', 's4', 'down') 72 | net.pingAll() 73 | print("Link S3 to S4 - bringing up again - all nodes will be reachable") 74 | net.configLinkStatus('s3', 's4', 'up') 75 | net.pingAll() 76 | CLI(net) 77 | net.stop() 78 | -------------------------------------------------------------------------------- /mininet_topologies/group_table_lb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Grouptable example 5 | 6 | Switch2 7 | / \ 8 | h1 ---Switch1 Switch4-----h2 9 | \ / 10 | Switch3 11 | 12 | 13 | 14 | # static arp entry addition 15 | 16 | h1 arp -s 192.168.1.2 00:00:00:00:00:02 17 | h2 arp -s 192.168.1.1 00:00:00:00:00:01 18 | 19 | 20 | 21 | ryu stuff: 22 | 23 | ryu-manager group_table_lb.py 24 | 25 | """ 26 | 27 | from mininet.topo import Topo 28 | from mininet.net import Mininet 29 | from mininet.log import setLogLevel 30 | from mininet.cli import CLI 31 | from mininet.node import OVSSwitch, Controller, RemoteController 32 | from time import sleep 33 | 34 | 35 | class SingleSwitchTopo(Topo): 36 | "Single switch connected to n hosts." 37 | def build(self): 38 | s1 = self.addSwitch('s1') 39 | s2 = self.addSwitch('s2') 40 | s3 = self.addSwitch('s3') 41 | s4 = self.addSwitch('s4') 42 | 43 | h1 = self.addHost('h1', mac="00:00:00:00:00:01", ip="192.168.1.1/24") 44 | h2 = self.addHost('h2', mac="00:00:00:00:00:02", ip="192.168.1.2/24") 45 | self.addLink(s1,s2,1,1) 46 | self.addLink(s1,s3,2,1) 47 | self.addLink(s4,s2,1,2) 48 | self.addLink(s4,s3,2,2) 49 | self.addLink(s1,h1,3,1) 50 | self.addLink(s4,h2,3,1) 51 | 52 | if __name__ == '__main__': 53 | setLogLevel('info') 54 | topo = SingleSwitchTopo() 55 | c1 = RemoteController('c1', ip='127.0.0.1') 56 | net = Mininet(topo=topo, controller=c1) 57 | net.start() 58 | CLI(net) 59 | net.stop() -------------------------------------------------------------------------------- /mininet_topologies/group_table_topo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | """Grouptable example 5 | 6 | Ring Topology(Tree), 7 | 8 | Switch1-----Switch2 9 | Switch1------Switch3- 10 | 11 | Switch1----- 12 | Switch4-----Host4 13 | 14 | 15 | ryu stuff: 16 | 17 | ryu-manager ryu.app.simple_switch_13 18 | 19 | """ 20 | 21 | from mininet.topo import Topo 22 | from mininet.net import Mininet 23 | from mininet.log import setLogLevel 24 | from mininet.cli import CLI 25 | from mininet.node import OVSSwitch, Controller, RemoteController 26 | from time import sleep 27 | 28 | 29 | class SingleSwitchTopo(Topo): 30 | "Single switch connected to n hosts." 31 | def build(self): 32 | s1 = self.addSwitch('s1') 33 | s2 = self.addSwitch('s2') 34 | s3 = self.addSwitch('s3') 35 | 36 | a1 = self.addHost('a1', mac="00:00:00:00:11:11", ip="192.168.1.1/24") 37 | b1 = self.addHost('b1', mac="00:00:00:00:11:12", ip="192.168.1.2/24") 38 | sniffer = self.addHost('sniffer', mac="00:00:00:00:11:13", ip="192.168.1.3/24") 39 | 40 | self.addLink(s2, a1, 1, 1) 41 | self.addLink(s3, b1, 1, 1) 42 | self.addLink(s1, sniffer, 1, 1) 43 | 44 | self.addLink(s2, s1, 2, 2) 45 | self.addLink(s3, s1, 2, 3) 46 | 47 | 48 | 49 | if __name__ == '__main__': 50 | setLogLevel('info') 51 | topo = SingleSwitchTopo() 52 | c1 = RemoteController('c1', ip='127.0.0.1') 53 | net = Mininet(topo=topo, controller=c1) 54 | net.start() 55 | #sleep(5) 56 | #print("Topology is up, lets ping") 57 | #net.pingAll() 58 | CLI(net) 59 | net.stop() 60 | -------------------------------------------------------------------------------- /openflow_overview.md: -------------------------------------------------------------------------------- 1 | # How Traditional L2 Switch Works (Technical details) : 2 | 3 | 1. Traditional Switch have built in Control Plane + Data Plane. 4 | 2. Mac Table( a.k.a Forwarding table) is Empty when the switch starts. 5 | 3. Control Plane updates the Mac Table with the MAC and the PORT Number. This information is extracted from incoming Packet. 6 | 4. Control Plane keeps on building/updating the Mac Table. 7 | 5. When the packet arrives, Switch Data plane looks the Mac table, if the destination MAC matches in the Mac table, it forwards the packet to the respective Port. 8 | 9 | 10 | # How SDN Switch(Openflow Switch) Works: 11 | 12 | 13 | 1. RYU L3 Application 14 | 15 | ``` 16 | ryu-manager ryu.app.simple_switch_13 17 | ``` 18 | 19 | 20 | 2. Run the Linear Mininet Topology, 21 | 22 | ``` 23 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 24 | ``` 25 | 26 | 27 | 3. Start Wireshark Capture 28 | 29 | 30 | ## overview 31 | 32 | 1. Switch is configured with SDN Controller IP and Openflow protocol version. 33 | 2. Switch will establish the communication with SDN Controller. 34 | 3. Initialy the Switch flow table will be empty, When the Packet arrives Switch will send the packets to the Controller. 35 | 4. Controller build the Switch Logic with the packets. 36 | 5. Controller adds the Flow table to the switch. 37 | 6. Now, Switch data path is built with flows. So when the Packet arrive it will look the Flow table and forward the packet to respective port. 38 | 39 | 40 | 41 | # Openflow Messages in Detail: 42 | 43 | 44 | ## 1.Hello Message: 45 | 46 | a. Switch sends Openflow Hello Message(includes version number) to the Controller (port number 6653) 47 | 48 | b. Controller responds with the Hello Message if version is supported. 49 | 50 | Failure Case(Version MisMatch): 51 | 52 | If different Openflow Version is user between the Controller and Switch, Hello Message will fail. 53 | 54 | You will see similar error msg in the controller. 55 | 56 | Error: 57 | unsupported version 0x1. If possible, set the switch to use one of the versions [3] 58 | 59 | 60 | ## 2.Features Request/Reply Message: 61 | 62 | a. Controller will send the Feature Request Message to the Switch and asking for the Switch supported features. 63 | 64 | b. Switch will reply with 65 | datapath ID, buffers, tables size, 66 | stats reporting : flow stats, port stats, table stats etc. 67 | 68 | 69 | ## 3.Port Desc/Status Message: 70 | 71 | a. Controller will ask for the port description/ status message. 72 | 73 | b. Switch will send the Port details of each port. 74 | 75 | 76 | ## 4. Packet In/Packet Out Message: 77 | 78 | a. If Switch want to send a data packet to the Controller, it uses the PACKET IN message 79 | 80 | b.Controller process the Packet in message, and decides what to do(which port to forward/ or drop etc) with this packet. 81 | 82 | c. Controller respond with Packet Out Message with actions (FLOOD etc.) 83 | 84 | ## 5. Flow Modification(add,delete) Message: 85 | 86 | a. To add, remove, modify the flow in the switch, controller using this message. 87 | 88 | b. Controller Sends the Flow Modification message to the switch with this important params. 89 | i). Command, 90 | ii). Match 91 | iii). Instruction, action. 92 | 93 | At the time of switch starts(initial negotiatian), Controller adds TABLE MISS entry in to the switch. 94 | ### Table Miss Entry: 95 | 96 | The flow entry that wildcards all fields (all fields omitted) and has priority equal to 0 is called the table-miss flow entry 97 | RYU installs the table miss entry to forward the packets to the controller port(action = output:Controller port). 98 | 99 | 100 | ## 6. Echo Request/Reply Message: 101 | 102 | To identify the liveliness of the Controller, Switch will send periodic health Check message to the Controller and expects the response. (default: 5sec interval) 103 | 104 | A. Switch sends Echo Request to the Controller. 105 | 106 | B. Controller responds back with Echo Reply. 107 | 108 | 109 | ## 7. Stats Message: 110 | 111 | 1. flow(individual,aggregate),table,port,queue stats message will send by the controller. 112 | 113 | 2. Switch will respond with the relavent statistics. 114 | 115 | This is used for monitoring the switches (with variour counter packets/sec, errors, etc) 116 | 117 | 118 | # Important Openvswitch Commands 119 | 120 | ``` 121 | sudo ovs-vsctl show 122 | sudo ovs-ofctl -O show 123 | sudo ovs-ofctl -O OpenFlow13 show s1 124 | sudo ovs-ofctl -O Openflow13 dump-flows s1 125 | ``` 126 | 127 | 128 | # Take aways: 129 | 130 | 1) Openflow version should match between the switch and Controller 131 | 132 | 2) Our Controller Application(our RYU project/exercise) should process Packet IN (Message), to build the Switching/Routing logic. 133 | 134 | 3) Our Controller Application(our RYU project/exercise) should use Flow Modifcation message to add/modify/delete the flows in the switch. 135 | 136 | 4) Our Controller Application(our RYU project/exercise) should use Flow Stats, Port Stats request message to get the statistics(Packets Sent/Received , etc) of the flows, Ports . 137 | 138 | 139 | 140 | # References 141 | 142 | https://www.opennetworking.org/software-defined-standards/specifications/ 143 | 144 | https://osrg.github.io/ryu/resources.html 145 | -------------------------------------------------------------------------------- /overview.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | # Learn_SDN_with_RYU_Controller 4 | 5 | This is a crash course to learn Software Defined Networking with hands on exercises. 6 | This course is primarily built for University Students/Beginners, who wants to learn SDN and RYU Controller 7 | with practical exercises and writes simple SDN applications. 8 | 9 | In this course, we uses the MiniNet and RYU SDN Controller for SDN Test bed.. 10 | 11 | This course DOESNOT cover OpenDayLight, ONOS SDN, FLOODLIGHT controllers . 12 | 13 | # Course Overview 14 | 15 | 1) How do you setup the SDN Test environment in your laptop , its step by step explanations. 16 | 17 | 2) Mininet in details 18 | - create in built topology 19 | - running TCP/UDP tests 20 | - How to write your own topology using simple python script 21 | 22 | 3) Openflow concepts in Detail 23 | 24 | 4) Step by Step guide to write the simple SDN applications/exercises in RYU Controller. 25 | L3 Switch, 26 | L4 Switch, 27 | Flow timeouts 28 | Flow Priority 29 | Multiple Tables 30 | ARP Proxy 31 | . 32 | 33 | ## About Myself: 34 | 35 | Myself Suresh Kumar, Instructor of this Course. 36 | I have been working in the Networking,Telecommunication Software industry for past 15 Years. 37 | I am engaged with SDN, Cloud Projects for last 5 years. you can checkout my profile in linkedin. 38 | 39 | linkedin.com/in/sureshkvl 40 | 41 | 42 | Once you finish this course, 43 | You will get the fair understanding of SDN and RYU controllor. 44 | you would be able to run the RYU SDN apps, and you can start writing the simple RYU applications. 45 | -------------------------------------------------------------------------------- /ryu-exercises/README.md: -------------------------------------------------------------------------------- 1 | # ryu-exercises 2 | ryu simple exercises 3 | 4 | 5 | Exercise1 - Layer2 Switch : 6 | ========================= 7 | 8 | Description: Layer2 Switch 9 | 10 | This sample application available in the RYU apps. 11 | Simple Layer2 Switch application, supports Openflow 1.3 Protocol. 12 | 13 | The Openflow flow table Match constraints are 14 | in_port, eth_dst, eth_src 15 | 16 | 17 | Mininet topology: 18 | ------------------------ 19 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 20 | 21 | 22 | 23 | Ryu app: 24 | ----------- 25 | ryu-manager ex1_simple_switch_13.py 26 | 27 | 28 | 29 | Exercise2 - Layer3 Switch : 30 | ============================= 31 | 32 | Description: Layer3 Switch 33 | 34 | Update the simple layer2 switch application(Exercise1) with Layer3 Match constraints. 35 | 36 | The Openflow flow table Match constraints are 37 | eth_type, ipv4_src, ipv4_dst 38 | 39 | 40 | 41 | Mininet topology: 42 | ------------------------ 43 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 44 | 45 | 46 | Ryu app: 47 | ----------- 48 | ryu-manager ex2_L3Match_switch.py 49 | 50 | 51 | 52 | 53 | 54 | Exercise3- Layer2 Switch : 55 | ============================= 56 | 57 | Description: Layer4 Switch 58 | 59 | Update the simple layer2 switch application(Exercise1) with Layer4 Match constraints. 60 | 61 | The Openflow flow table Match constraints are 62 | eth_type, ipv4_src, ipv4_dst, ip_proto, tcp_src/udp_src, tcp_dst/udp_dst 63 | 64 | 65 | 66 | Mininet topology: 67 | ------------------------ 68 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 69 | 70 | Ryu app: 71 | ----------- 72 | ryu-manager ex3_L4Match_switch.py 73 | 74 | 75 | 76 | 77 | Exercise4 - Flow Timeout: 78 | =========================== 79 | 80 | Description: Flow Timeout exercise 81 | 82 | ex3_L4Match_switch.py is updated with the flow timeouts. 83 | 84 | hard_timeout = 30 85 | idle_timeout = 10 86 | 87 | 88 | Mininet topology: 89 | ------------------------ 90 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 91 | 92 | Ryu app: 93 | ----------- 94 | ryu-manager ex4_flow_timeout.py 95 | 96 | 97 | 98 | 99 | Exercise5 - Flow priority: 100 | ============================ 101 | 102 | Description: Flow Priority exercise 103 | 104 | 105 | 106 | Mininet topology: 107 | ------------------------ 108 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 109 | 110 | Ryu app: 111 | ----------- 112 | ryu-manager ex5_flow_priority.py 113 | 114 | 115 | Exercise6 - Multiple Tables: 116 | ============================== 117 | 118 | Description: Multiple Tables 119 | 120 | Mininet topology: 121 | ------------------------ 122 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 123 | 124 | Ryu app: 125 | ----------- 126 | ryu-manager ex6_multiple_tables.py 127 | 128 | 129 | 130 | Exercise7 - ARP Proxy: 131 | ======================= 132 | 133 | Description: ARP Proxy. This application, builds and responds the ARP Response packets. 134 | 135 | 136 | 137 | Mininet topology: 138 | ------------------------ 139 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 140 | 141 | Ryu app: 142 | ----------- 143 | ryu-manager ex7_arp_proxy.py 144 | 145 | -------------------------------------------------------------------------------- /ryu-exercises/ex2_L3Match_switch.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | from ryu.lib.packet import ipv4 25 | 26 | 27 | class SimpleSwitch13(app_manager.RyuApp): 28 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 29 | 30 | def __init__(self, *args, **kwargs): 31 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 32 | self.mac_to_port = {} 33 | 34 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 35 | def switch_features_handler(self, ev): 36 | datapath = ev.msg.datapath 37 | ofproto = datapath.ofproto 38 | parser = datapath.ofproto_parser 39 | 40 | # install table-miss flow entry 41 | # 42 | # We specify NO BUFFER to max_len of the output action due to 43 | # OVS bug. At this moment, if we specify a lesser number, e.g., 44 | # 128, OVS will send Packet-In with invalid buffer_id and 45 | # truncated packet data. In that case, we cannot output packets 46 | # correctly. The bug has been fixed in OVS v2.1.0. 47 | match = parser.OFPMatch() 48 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 49 | ofproto.OFPCML_NO_BUFFER)] 50 | self.add_flow(datapath, 0, match, actions) 51 | 52 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 53 | ofproto = datapath.ofproto 54 | parser = datapath.ofproto_parser 55 | 56 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 57 | actions)] 58 | if buffer_id: 59 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 60 | priority=priority, match=match, 61 | instructions=inst) 62 | else: 63 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 64 | match=match, instructions=inst) 65 | datapath.send_msg(mod) 66 | 67 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 68 | def _packet_in_handler(self, ev): 69 | # If you hit this you might want to increase 70 | # the "miss_send_length" of your switch 71 | if ev.msg.msg_len < ev.msg.total_len: 72 | self.logger.debug("packet truncated: only %s of %s bytes", 73 | ev.msg.msg_len, ev.msg.total_len) 74 | msg = ev.msg 75 | datapath = msg.datapath 76 | ofproto = datapath.ofproto 77 | parser = datapath.ofproto_parser 78 | in_port = msg.match['in_port'] 79 | 80 | pkt = packet.Packet(msg.data) 81 | eth = pkt.get_protocols(ethernet.ethernet)[0] 82 | 83 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 84 | # ignore lldp packet 85 | return 86 | dst = eth.dst 87 | src = eth.src 88 | 89 | dpid = datapath.id 90 | self.mac_to_port.setdefault(dpid, {}) 91 | 92 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 93 | 94 | # learn a mac address to avoid FLOOD next time. 95 | self.mac_to_port[dpid][src] = in_port 96 | 97 | if dst in self.mac_to_port[dpid]: 98 | out_port = self.mac_to_port[dpid][dst] 99 | else: 100 | out_port = ofproto.OFPP_FLOOD 101 | 102 | actions = [parser.OFPActionOutput(out_port)] 103 | 104 | # install a flow to avoid packet_in next time 105 | if out_port != ofproto.OFPP_FLOOD: 106 | 107 | # check IP Protocol and create a match for IP 108 | if eth.ethertype == ether_types.ETH_TYPE_IP: 109 | ip = pkt.get_protocol(ipv4.ipv4) 110 | srcip = ip.src 111 | dstip = ip.dst 112 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, 113 | ipv4_src=srcip, 114 | ipv4_dst=dstip 115 | ) 116 | # verify if we have a valid buffer_id, if yes avoid to send both 117 | # flow_mod & packet_out 118 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 119 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 120 | return 121 | else: 122 | self.add_flow(datapath, 1, match, actions) 123 | data = None 124 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 125 | data = msg.data 126 | 127 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 128 | in_port=in_port, actions=actions, data=data) 129 | datapath.send_msg(out) 130 | -------------------------------------------------------------------------------- /ryu-exercises/ex3_L4Match_switch.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | from ryu.lib.packet import in_proto 26 | from ryu.lib.packet import ipv4 27 | from ryu.lib.packet import icmp 28 | from ryu.lib.packet import tcp 29 | from ryu.lib.packet import udp 30 | 31 | class SimpleSwitch13(app_manager.RyuApp): 32 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 33 | 34 | def __init__(self, *args, **kwargs): 35 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 36 | self.mac_to_port = {} 37 | 38 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 39 | def switch_features_handler(self, ev): 40 | datapath = ev.msg.datapath 41 | ofproto = datapath.ofproto 42 | parser = datapath.ofproto_parser 43 | 44 | # install table-miss flow entry 45 | # 46 | # We specify NO BUFFER to max_len of the output action due to 47 | # OVS bug. At this moment, if we specify a lesser number, e.g., 48 | # 128, OVS will send Packet-In with invalid buffer_id and 49 | # truncated packet data. In that case, we cannot output packets 50 | # correctly. The bug has been fixed in OVS v2.1.0. 51 | match = parser.OFPMatch() 52 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 53 | ofproto.OFPCML_NO_BUFFER)] 54 | self.add_flow(datapath, 0, match, actions) 55 | 56 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 57 | ofproto = datapath.ofproto 58 | parser = datapath.ofproto_parser 59 | 60 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 61 | actions)] 62 | if buffer_id: 63 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 64 | priority=priority, match=match, 65 | instructions=inst) 66 | else: 67 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 68 | match=match, instructions=inst) 69 | datapath.send_msg(mod) 70 | 71 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 72 | def _packet_in_handler(self, ev): 73 | # If you hit this you might want to increase 74 | # the "miss_send_length" of your switch 75 | if ev.msg.msg_len < ev.msg.total_len: 76 | self.logger.debug("packet truncated: only %s of %s bytes", 77 | ev.msg.msg_len, ev.msg.total_len) 78 | msg = ev.msg 79 | datapath = msg.datapath 80 | ofproto = datapath.ofproto 81 | parser = datapath.ofproto_parser 82 | in_port = msg.match['in_port'] 83 | 84 | pkt = packet.Packet(msg.data) 85 | eth = pkt.get_protocols(ethernet.ethernet)[0] 86 | 87 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 88 | # ignore lldp packet 89 | return 90 | dst = eth.dst 91 | src = eth.src 92 | 93 | dpid = datapath.id 94 | self.mac_to_port.setdefault(dpid, {}) 95 | 96 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 97 | 98 | # learn a mac address to avoid FLOOD next time. 99 | self.mac_to_port[dpid][src] = in_port 100 | 101 | if dst in self.mac_to_port[dpid]: 102 | out_port = self.mac_to_port[dpid][dst] 103 | else: 104 | out_port = ofproto.OFPP_FLOOD 105 | 106 | actions = [parser.OFPActionOutput(out_port)] 107 | 108 | # install a flow to avoid packet_in next time 109 | if out_port != ofproto.OFPP_FLOOD: 110 | 111 | # check IP Protocol and create a match for IP 112 | if eth.ethertype == ether_types.ETH_TYPE_IP: 113 | ip = pkt.get_protocol(ipv4.ipv4) 114 | srcip = ip.src 115 | dstip = ip.dst 116 | protocol = ip.proto 117 | 118 | # if ICMP Protocol 119 | if protocol == in_proto.IPPROTO_ICMP: 120 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol) 121 | 122 | # if TCP Protocol 123 | elif protocol == in_proto.IPPROTO_TCP: 124 | t = pkt.get_protocol(tcp.tcp) 125 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol, tcp_src=t.src_port, tcp_dst=t.dst_port,) 126 | 127 | # If UDP Protocol 128 | elif protocol == in_proto.IPPROTO_UDP: 129 | u = pkt.get_protocol(udp.udp) 130 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol, udp_src=u.src_port, udp_dst=u.dst_port,) 131 | 132 | # verify if we have a valid buffer_id, if yes avoid to send both 133 | # flow_mod & packet_out 134 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 135 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 136 | return 137 | else: 138 | self.add_flow(datapath, 1, match, actions) 139 | data = None 140 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 141 | data = msg.data 142 | 143 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 144 | in_port=in_port, actions=actions, data=data) 145 | datapath.send_msg(out) 146 | -------------------------------------------------------------------------------- /ryu-exercises/ex4_flow_timeout.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | 26 | class SimpleSwitch13(app_manager.RyuApp): 27 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 31 | self.mac_to_port = {} 32 | 33 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 34 | def switch_features_handler(self, ev): 35 | datapath = ev.msg.datapath 36 | ofproto = datapath.ofproto 37 | parser = datapath.ofproto_parser 38 | 39 | # install table-miss flow entry 40 | # 41 | # We specify NO BUFFER to max_len of the output action due to 42 | # OVS bug. At this moment, if we specify a lesser number, e.g., 43 | # 128, OVS will send Packet-In with invalid buffer_id and 44 | # truncated packet data. In that case, we cannot output packets 45 | # correctly. The bug has been fixed in OVS v2.1.0. 46 | match = parser.OFPMatch() 47 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 48 | ofproto.OFPCML_NO_BUFFER)] 49 | self.add_flow(datapath, 0, match, actions) 50 | 51 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 52 | ofproto = datapath.ofproto 53 | parser = datapath.ofproto_parser 54 | 55 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 56 | actions)] 57 | if buffer_id: 58 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 59 | idle_timeout=10, hard_timeout=30, priority=priority, match=match, 60 | instructions=inst) 61 | else: 62 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 63 | idle_timeout=10, hard_timeout=30, match=match, instructions=inst) 64 | datapath.send_msg(mod) 65 | 66 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 67 | def _packet_in_handler(self, ev): 68 | # If you hit this you might want to increase 69 | # the "miss_send_length" of your switch 70 | if ev.msg.msg_len < ev.msg.total_len: 71 | self.logger.debug("packet truncated: only %s of %s bytes", 72 | ev.msg.msg_len, ev.msg.total_len) 73 | msg = ev.msg 74 | datapath = msg.datapath 75 | ofproto = datapath.ofproto 76 | parser = datapath.ofproto_parser 77 | in_port = msg.match['in_port'] 78 | 79 | pkt = packet.Packet(msg.data) 80 | eth = pkt.get_protocols(ethernet.ethernet)[0] 81 | 82 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 83 | # ignore lldp packet 84 | return 85 | dst = eth.dst 86 | src = eth.src 87 | 88 | dpid = datapath.id 89 | self.mac_to_port.setdefault(dpid, {}) 90 | 91 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 92 | 93 | # learn a mac address to avoid FLOOD next time. 94 | self.mac_to_port[dpid][src] = in_port 95 | 96 | if dst in self.mac_to_port[dpid]: 97 | out_port = self.mac_to_port[dpid][dst] 98 | else: 99 | out_port = ofproto.OFPP_FLOOD 100 | 101 | actions = [parser.OFPActionOutput(out_port)] 102 | 103 | # install a flow to avoid packet_in next time 104 | if out_port != ofproto.OFPP_FLOOD: 105 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 106 | # verify if we have a valid buffer_id, if yes avoid to send both 107 | # flow_mod & packet_out 108 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 109 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 110 | return 111 | else: 112 | self.add_flow(datapath, 1, match, actions) 113 | data = None 114 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 115 | data = msg.data 116 | 117 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 118 | in_port=in_port, actions=actions, data=data) 119 | datapath.send_msg(out) 120 | -------------------------------------------------------------------------------- /ryu-exercises/ex5_flow_priority.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | from ryu.lib.packet import ipv4 25 | from ryu.lib.packet import in_proto 26 | 27 | 28 | 29 | class SimpleSwitch13(app_manager.RyuApp): 30 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 31 | 32 | def __init__(self, *args, **kwargs): 33 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 34 | self.mac_to_port = {} 35 | 36 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 37 | def switch_features_handler(self, ev): 38 | datapath = ev.msg.datapath 39 | ofproto = datapath.ofproto 40 | parser = datapath.ofproto_parser 41 | 42 | # install table-miss flow entry 43 | # 44 | # We specify NO BUFFER to max_len of the output action due to 45 | # OVS bug. At this moment, if we specify a lesser number, e.g., 46 | # 128, OVS will send Packet-In with invalid buffer_id and 47 | # truncated packet data. In that case, we cannot output packets 48 | # correctly. The bug has been fixed in OVS v2.1.0. 49 | match = parser.OFPMatch() 50 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 51 | ofproto.OFPCML_NO_BUFFER)] 52 | self.add_flow(datapath, 0, match, actions) 53 | 54 | #TCP drop flow 55 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=in_proto.IPPROTO_TCP) 56 | mod = parser.OFPFlowMod(datapath=datapath, table_id=0, priority=10000, match=match) 57 | datapath.send_msg(mod) 58 | 59 | 60 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 61 | ofproto = datapath.ofproto 62 | parser = datapath.ofproto_parser 63 | 64 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 65 | actions)] 66 | if buffer_id: 67 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 68 | priority=priority, match=match, 69 | instructions=inst) 70 | else: 71 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 72 | match=match, instructions=inst) 73 | datapath.send_msg(mod) 74 | 75 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 76 | def _packet_in_handler(self, ev): 77 | # If you hit this you might want to increase 78 | # the "miss_send_length" of your switch 79 | if ev.msg.msg_len < ev.msg.total_len: 80 | self.logger.debug("packet truncated: only %s of %s bytes", 81 | ev.msg.msg_len, ev.msg.total_len) 82 | msg = ev.msg 83 | datapath = msg.datapath 84 | ofproto = datapath.ofproto 85 | parser = datapath.ofproto_parser 86 | in_port = msg.match['in_port'] 87 | 88 | pkt = packet.Packet(msg.data) 89 | eth = pkt.get_protocols(ethernet.ethernet)[0] 90 | 91 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 92 | # ignore lldp packet 93 | return 94 | dst = eth.dst 95 | src = eth.src 96 | 97 | dpid = datapath.id 98 | self.mac_to_port.setdefault(dpid, {}) 99 | 100 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 101 | 102 | # learn a mac address to avoid FLOOD next time. 103 | self.mac_to_port[dpid][src] = in_port 104 | 105 | if dst in self.mac_to_port[dpid]: 106 | out_port = self.mac_to_port[dpid][dst] 107 | else: 108 | out_port = ofproto.OFPP_FLOOD 109 | 110 | actions = [parser.OFPActionOutput(out_port)] 111 | 112 | # install a flow to avoid packet_in next time 113 | if out_port != ofproto.OFPP_FLOOD: 114 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 115 | # verify if we have a valid buffer_id, if yes avoid to send both 116 | # flow_mod & packet_out 117 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 118 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 119 | return 120 | else: 121 | self.add_flow(datapath, 1, match, actions) 122 | data = None 123 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 124 | data = msg.data 125 | 126 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 127 | in_port=in_port, actions=actions, data=data) 128 | datapath.send_msg(out) 129 | -------------------------------------------------------------------------------- /ryu-exercises/ex6_multiple_tables.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | from ryu.lib.packet import ipv4 25 | from ryu.lib.packet import in_proto 26 | 27 | FILTER_TABLE = 5 28 | FORWARD_TABLE = 10 29 | 30 | class SimpleSwitch13(app_manager.RyuApp): 31 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 32 | 33 | def __init__(self, *args, **kwargs): 34 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 35 | self.mac_to_port = {} 36 | 37 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 38 | def switch_features_handler(self, ev): 39 | datapath = ev.msg.datapath 40 | ofproto = datapath.ofproto 41 | parser = datapath.ofproto_parser 42 | 43 | # install table-miss flow entry 44 | # 45 | # We specify NO BUFFER to max_len of the output action due to 46 | # OVS bug. At this moment, if we specify a lesser number, e.g., 47 | # 128, OVS will send Packet-In with invalid buffer_id and 48 | # truncated packet data. In that case, we cannot output packets 49 | # correctly. The bug has been fixed in OVS v2.1.0. 50 | match = parser.OFPMatch() 51 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 52 | ofproto.OFPCML_NO_BUFFER)] 53 | self.add_flow(datapath, 0, match, actions) 54 | 55 | # adding default tables/rules in the startup 56 | self.add_default_table(datapath) 57 | self.add_filter_table(datapath) 58 | self.apply_filter_table_rules(datapath) 59 | 60 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 61 | ofproto = datapath.ofproto 62 | parser = datapath.ofproto_parser 63 | 64 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 65 | actions)] 66 | if buffer_id: 67 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 68 | priority=priority, table_id=FORWARD_TABLE, 69 | match=match, instructions=inst) 70 | else: 71 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 72 | match=match, table_id=FORWARD_TABLE, 73 | instructions=inst) 74 | datapath.send_msg(mod) 75 | 76 | def add_default_table(self, datapath): 77 | ofproto = datapath.ofproto 78 | parser = datapath.ofproto_parser 79 | inst = [parser.OFPInstructionGotoTable(FILTER_TABLE)] 80 | mod = parser.OFPFlowMod(datapath=datapath, table_id=0, instructions=inst) 81 | datapath.send_msg(mod) 82 | 83 | def add_filter_table(self, datapath): 84 | ofproto = datapath.ofproto 85 | parser = datapath.ofproto_parser 86 | inst = [parser.OFPInstructionGotoTable(FORWARD_TABLE)] 87 | mod = parser.OFPFlowMod(datapath=datapath, table_id=FILTER_TABLE, 88 | priority=1, instructions=inst) 89 | datapath.send_msg(mod) 90 | 91 | def apply_filter_table_rules(self, datapath): 92 | ofproto = datapath.ofproto 93 | parser = datapath.ofproto_parser 94 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=in_proto.IPPROTO_ICMP) 95 | mod = parser.OFPFlowMod(datapath=datapath, table_id=FILTER_TABLE, 96 | priority=10000, match=match) 97 | datapath.send_msg(mod) 98 | 99 | 100 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 101 | def _packet_in_handler(self, ev): 102 | # If you hit this you might want to increase 103 | # the "miss_send_length" of your switch 104 | if ev.msg.msg_len < ev.msg.total_len: 105 | self.logger.debug("packet truncated: only %s of %s bytes", 106 | ev.msg.msg_len, ev.msg.total_len) 107 | msg = ev.msg 108 | datapath = msg.datapath 109 | ofproto = datapath.ofproto 110 | parser = datapath.ofproto_parser 111 | in_port = msg.match['in_port'] 112 | 113 | pkt = packet.Packet(msg.data) 114 | eth = pkt.get_protocols(ethernet.ethernet)[0] 115 | 116 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 117 | # ignore lldp packet 118 | return 119 | dst = eth.dst 120 | src = eth.src 121 | 122 | dpid = datapath.id 123 | self.mac_to_port.setdefault(dpid, {}) 124 | 125 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 126 | 127 | # learn a mac address to avoid FLOOD next time. 128 | self.mac_to_port[dpid][src] = in_port 129 | 130 | if dst in self.mac_to_port[dpid]: 131 | out_port = self.mac_to_port[dpid][dst] 132 | else: 133 | out_port = ofproto.OFPP_FLOOD 134 | 135 | actions = [parser.OFPActionOutput(out_port)] 136 | 137 | # install a flow to avoid packet_in next time 138 | if out_port != ofproto.OFPP_FLOOD: 139 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 140 | # verify if we have a valid buffer_id, if yes avoid to send both 141 | # flow_mod & packet_out 142 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 143 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 144 | return 145 | else: 146 | self.add_flow(datapath, 1, match, actions) 147 | data = None 148 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 149 | data = msg.data 150 | 151 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 152 | in_port=in_port, actions=actions, data=data) 153 | datapath.send_msg(out) 154 | -------------------------------------------------------------------------------- /ryu-exercises/ex7_group_tables.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | 26 | class SimpleSwitch13(app_manager.RyuApp): 27 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 31 | self.mac_to_port = {} 32 | 33 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 34 | def switch_features_handler(self, ev): 35 | datapath = ev.msg.datapath 36 | ofproto = datapath.ofproto 37 | parser = datapath.ofproto_parser 38 | 39 | # install table-miss flow entry 40 | # 41 | # We specify NO BUFFER to max_len of the output action due to 42 | # OVS bug. At this moment, if we specify a lesser number, e.g., 43 | # 128, OVS will send Packet-In with invalid buffer_id and 44 | # truncated packet data. In that case, we cannot output packets 45 | # correctly. The bug has been fixed in OVS v2.1.0. 46 | match = parser.OFPMatch() 47 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 48 | ofproto.OFPCML_NO_BUFFER)] 49 | self.add_flow(datapath, 0, match, actions) 50 | 51 | # switch s1 52 | if datapath.id == 1: 53 | # add group tables 54 | self.send_group_mod(datapath) 55 | actions = [parser.OFPActionGroup(group_id=50)] 56 | match = parser.OFPMatch(in_port=2) 57 | self.add_flow(datapath, 10, match, actions) 58 | # entry 2 59 | actions = [parser.OFPActionGroup(group_id=51)] 60 | match = parser.OFPMatch(in_port=3) 61 | self.add_flow(datapath, 10, match, actions) 62 | 63 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 64 | ofproto = datapath.ofproto 65 | parser = datapath.ofproto_parser 66 | 67 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 68 | actions)] 69 | if buffer_id: 70 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 71 | priority=priority, match=match, 72 | instructions=inst) 73 | else: 74 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 75 | match=match, instructions=inst) 76 | datapath.send_msg(mod) 77 | 78 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 79 | def _packet_in_handler(self, ev): 80 | # If you hit this you might want to increase 81 | # the "miss_send_length" of your switch 82 | if ev.msg.msg_len < ev.msg.total_len: 83 | self.logger.debug("packet truncated: only %s of %s bytes", 84 | ev.msg.msg_len, ev.msg.total_len) 85 | msg = ev.msg 86 | datapath = msg.datapath 87 | ofproto = datapath.ofproto 88 | parser = datapath.ofproto_parser 89 | in_port = msg.match['in_port'] 90 | 91 | pkt = packet.Packet(msg.data) 92 | eth = pkt.get_protocols(ethernet.ethernet)[0] 93 | 94 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 95 | # ignore lldp packet 96 | return 97 | dst = eth.dst 98 | src = eth.src 99 | 100 | dpid = datapath.id 101 | 102 | self.mac_to_port.setdefault(dpid, {}) 103 | 104 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 105 | # learn a mac address to avoid FLOOD next time. 106 | self.mac_to_port[dpid][src] = in_port 107 | 108 | if dst in self.mac_to_port[dpid]: 109 | out_port = self.mac_to_port[dpid][dst] 110 | else: 111 | out_port = ofproto.OFPP_FLOOD 112 | 113 | actions = [parser.OFPActionOutput(out_port)] 114 | 115 | # install a flow to avoid packet_in next time 116 | if out_port != ofproto.OFPP_FLOOD: 117 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 118 | # verify if we have a valid buffer_id, if yes avoid to send both 119 | # flow_mod & packet_out 120 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 121 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 122 | return 123 | else: 124 | self.add_flow(datapath, 1, match, actions) 125 | data = None 126 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 127 | data = msg.data 128 | 129 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 130 | in_port=in_port, actions=actions, data=data) 131 | datapath.send_msg(out) 132 | 133 | 134 | 135 | def send_group_mod(self, datapath): 136 | ofproto = datapath.ofproto 137 | parser = datapath.ofproto_parser 138 | 139 | # Hardcoding the stuff, as we already know the topology diagram. 140 | # Group table1 141 | # Receiver port2, forward it to port1 and Port3 142 | 143 | actions1 = [parser.OFPActionOutput(1)] 144 | actions2 = [parser.OFPActionOutput(3)] 145 | buckets = [parser.OFPBucket(actions=actions1), 146 | parser.OFPBucket(actions=actions2)] 147 | req = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, 148 | ofproto.OFPGT_ALL, 50, buckets) 149 | datapath.send_msg(req) 150 | 151 | # Group table2 152 | # Receive Port3, forward it to port1 and Port2 153 | actions1 = [parser.OFPActionOutput(1)] 154 | actions2 = [parser.OFPActionOutput(2)] 155 | buckets = [parser.OFPBucket(actions=actions1), 156 | parser.OFPBucket(actions=actions2)] 157 | req = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, 158 | ofproto.OFPGT_ALL, 51, buckets) 159 | datapath.send_msg(req) 160 | 161 | -------------------------------------------------------------------------------- /ryu-exercises/ex8_arp_proxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Mininet topology for this app 17 | # sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 18 | 19 | from ryu.base import app_manager 20 | from ryu.controller import ofp_event 21 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 22 | from ryu.controller.handler import set_ev_cls 23 | from ryu.ofproto import ofproto_v1_3 24 | from ryu.lib.packet import packet 25 | from ryu.lib.packet import ethernet 26 | from ryu.lib.packet import ether_types 27 | from ryu.lib.packet import arp 28 | 29 | arp_table = {"10.0.0.1": "00:00:00:00:00:01", 30 | "10.0.0.2": "00:00:00:00:00:02", 31 | "10.0.0.3": "00:00:00:00:00:03", 32 | "10.0.0.4": "00:00:00:00:00:04" 33 | } 34 | 35 | 36 | class SimpleSwitch13(app_manager.RyuApp): 37 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 38 | 39 | def __init__(self, *args, **kwargs): 40 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 41 | self.mac_to_port = {} 42 | 43 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 44 | def switch_features_handler(self, ev): 45 | datapath = ev.msg.datapath 46 | ofproto = datapath.ofproto 47 | parser = datapath.ofproto_parser 48 | 49 | # install table-miss flow entry 50 | # 51 | # We specify NO BUFFER to max_len of the output action due to 52 | # OVS bug. At this moment, if we specify a lesser number, e.g., 53 | # 128, OVS will send Packet-In with invalid buffer_id and 54 | # truncated packet data. In that case, we cannot output packets 55 | # correctly. The bug has been fixed in OVS v2.1.0. 56 | match = parser.OFPMatch() 57 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 58 | ofproto.OFPCML_NO_BUFFER)] 59 | self.add_flow(datapath, 0, match, actions) 60 | 61 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 62 | ofproto = datapath.ofproto 63 | parser = datapath.ofproto_parser 64 | 65 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 66 | actions)] 67 | if buffer_id: 68 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 69 | priority=priority, match=match, 70 | instructions=inst) 71 | else: 72 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 73 | match=match, instructions=inst) 74 | datapath.send_msg(mod) 75 | 76 | def arp_process(self, datapath, eth, a, in_port): 77 | r = arp_table.get(a.dst_ip) 78 | if r: 79 | self.logger.info("Matched MAC %s ", r) 80 | arp_resp = packet.Packet() 81 | arp_resp.add_protocol(ethernet.ethernet(ethertype=eth.ethertype, 82 | dst=eth.src, src=r)) 83 | arp_resp.add_protocol(arp.arp(opcode=arp.ARP_REPLY, 84 | src_mac=r, src_ip=a.dst_ip, 85 | dst_mac=a.src_mac, 86 | dst_ip=a.src_ip)) 87 | 88 | arp_resp.serialize() 89 | actions = [] 90 | actions.append(datapath.ofproto_parser.OFPActionOutput(in_port)) 91 | parser = datapath.ofproto_parser 92 | ofproto = datapath.ofproto 93 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, 94 | in_port=ofproto.OFPP_CONTROLLER, actions=actions, data=arp_resp) 95 | datapath.send_msg(out) 96 | self.logger.info("Proxied ARP Response packet") 97 | 98 | 99 | 100 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 101 | def _packet_in_handler(self, ev): 102 | # If you hit this you might want to increase 103 | # the "miss_send_length" of your switch 104 | if ev.msg.msg_len < ev.msg.total_len: 105 | self.logger.debug("packet truncated: only %s of %s bytes", 106 | ev.msg.msg_len, ev.msg.total_len) 107 | msg = ev.msg 108 | datapath = msg.datapath 109 | ofproto = datapath.ofproto 110 | parser = datapath.ofproto_parser 111 | in_port = msg.match['in_port'] 112 | 113 | pkt = packet.Packet(msg.data) 114 | eth = pkt.get_protocols(ethernet.ethernet)[0] 115 | 116 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 117 | # ignore lldp packet 118 | return 119 | dst = eth.dst 120 | src = eth.src 121 | 122 | dpid = datapath.id 123 | self.mac_to_port.setdefault(dpid, {}) 124 | 125 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 126 | 127 | # learn a mac address to avoid FLOOD next time. 128 | self.mac_to_port[dpid][src] = in_port 129 | 130 | # Check whether is it arp packet 131 | if eth.ethertype == ether_types.ETH_TYPE_ARP: 132 | self.logger.info("Received ARP Packet %s %s %s ", dpid, src, dst) 133 | a = pkt.get_protocol(arp.arp) 134 | self.arp_process(datapath, eth, a, in_port) 135 | return 136 | 137 | if dst in self.mac_to_port[dpid]: 138 | out_port = self.mac_to_port[dpid][dst] 139 | else: 140 | out_port = ofproto.OFPP_FLOOD 141 | 142 | actions = [parser.OFPActionOutput(out_port)] 143 | 144 | # install a flow to avoid packet_in next time 145 | if out_port != ofproto.OFPP_FLOOD: 146 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 147 | # verify if we have a valid buffer_id, if yes avoid to send both 148 | # flow_mod & packet_out 149 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 150 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 151 | return 152 | else: 153 | self.add_flow(datapath, 1, match, actions) 154 | data = None 155 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 156 | data = msg.data 157 | 158 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 159 | in_port=in_port, actions=actions, data=data) 160 | datapath.send_msg(out) 161 | -------------------------------------------------------------------------------- /ryu-exercises/ex9_topology_discovery.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | from ryu.topology import event, switches 26 | from ryu.topology.api import get_switch, get_link 27 | 28 | 29 | class SimpleSwitch13(app_manager.RyuApp): 30 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 31 | 32 | def __init__(self, *args, **kwargs): 33 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 34 | self.mac_to_port = {} 35 | self.topology_api_app = self 36 | 37 | 38 | @set_ev_cls(event.EventSwitchEnter) 39 | def get_topology_data(self, ev): 40 | switch_list = get_switch(self.topology_api_app, None) 41 | switches = [switch.dp.id for switch in switch_list] 42 | links_list = get_link(self.topology_api_app, None) 43 | links = [(link.src.dpid, link.dst.dpid, {'port': link.src.port_no}) for link in links_list] 44 | print "switches ", switches 45 | print "links ", links 46 | 47 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 48 | def switch_features_handler(self, ev): 49 | datapath = ev.msg.datapath 50 | ofproto = datapath.ofproto 51 | parser = datapath.ofproto_parser 52 | 53 | # install table-miss flow entry 54 | # 55 | # We specify NO BUFFER to max_len of the output action due to 56 | # OVS bug. At this moment, if we specify a lesser number, e.g., 57 | # 128, OVS will send Packet-In with invalid buffer_id and 58 | # truncated packet data. In that case, we cannot output packets 59 | # correctly. The bug has been fixed in OVS v2.1.0. 60 | match = parser.OFPMatch() 61 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 62 | ofproto.OFPCML_NO_BUFFER)] 63 | self.add_flow(datapath, 0, match, actions) 64 | 65 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 66 | ofproto = datapath.ofproto 67 | parser = datapath.ofproto_parser 68 | 69 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 70 | actions)] 71 | if buffer_id: 72 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 73 | priority=priority, match=match, 74 | instructions=inst) 75 | else: 76 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 77 | match=match, instructions=inst) 78 | datapath.send_msg(mod) 79 | 80 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 81 | def _packet_in_handler(self, ev): 82 | # If you hit this you might want to increase 83 | # the "miss_send_length" of your switch 84 | if ev.msg.msg_len < ev.msg.total_len: 85 | self.logger.debug("packet truncated: only %s of %s bytes", 86 | ev.msg.msg_len, ev.msg.total_len) 87 | msg = ev.msg 88 | datapath = msg.datapath 89 | ofproto = datapath.ofproto 90 | parser = datapath.ofproto_parser 91 | in_port = msg.match['in_port'] 92 | 93 | pkt = packet.Packet(msg.data) 94 | eth = pkt.get_protocols(ethernet.ethernet)[0] 95 | 96 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 97 | # ignore lldp packet 98 | return 99 | dst = eth.dst 100 | src = eth.src 101 | 102 | dpid = datapath.id 103 | self.mac_to_port.setdefault(dpid, {}) 104 | 105 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 106 | 107 | # learn a mac address to avoid FLOOD next time. 108 | self.mac_to_port[dpid][src] = in_port 109 | 110 | if dst in self.mac_to_port[dpid]: 111 | out_port = self.mac_to_port[dpid][dst] 112 | else: 113 | out_port = ofproto.OFPP_FLOOD 114 | 115 | actions = [parser.OFPActionOutput(out_port)] 116 | 117 | # install a flow to avoid packet_in next time 118 | if out_port != ofproto.OFPP_FLOOD: 119 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 120 | # verify if we have a valid buffer_id, if yes avoid to send both 121 | # flow_mod & packet_out 122 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 123 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 124 | return 125 | else: 126 | self.add_flow(datapath, 1, match, actions) 127 | data = None 128 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 129 | data = msg.data 130 | 131 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 132 | in_port=in_port, actions=actions, data=data) 133 | datapath.send_msg(out) 134 | -------------------------------------------------------------------------------- /ryu-exercises/hub.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | 26 | class SimpleSwitch13(app_manager.RyuApp): 27 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 31 | self.mac_to_port = {} 32 | 33 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 34 | def switch_features_handler(self, ev): 35 | datapath = ev.msg.datapath 36 | ofproto = datapath.ofproto 37 | parser = datapath.ofproto_parser 38 | 39 | # install table-miss flow entry 40 | # 41 | # We specify NO BUFFER to max_len of the output action due to 42 | # OVS bug. At this moment, if we specify a lesser number, e.g., 43 | # 128, OVS will send Packet-In with invalid buffer_id and 44 | # truncated packet data. In that case, we cannot output packets 45 | # correctly. The bug has been fixed in OVS v2.1.0. 46 | match = parser.OFPMatch() 47 | actions = [parser.OFPActionOutput(port=ofproto.OFPP_FLOOD)] 48 | self.add_flow(datapath, 0, match, actions) 49 | 50 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 51 | ofproto = datapath.ofproto 52 | parser = datapath.ofproto_parser 53 | 54 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 55 | actions)] 56 | if buffer_id: 57 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 58 | priority=priority, match=match, 59 | instructions=inst) 60 | else: 61 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 62 | match=match, instructions=inst) 63 | datapath.send_msg(mod) 64 | 65 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 66 | def _packet_in_handler(self, ev): 67 | # If you hit this you might want to increase 68 | # the "miss_send_length" of your switch 69 | if ev.msg.msg_len < ev.msg.total_len: 70 | self.logger.debug("packet truncated: only %s of %s bytes", 71 | ev.msg.msg_len, ev.msg.total_len) 72 | msg = ev.msg 73 | datapath = msg.datapath 74 | ofproto = datapath.ofproto 75 | parser = datapath.ofproto_parser 76 | in_port = msg.match['in_port'] 77 | 78 | pkt = packet.Packet(msg.data) 79 | eth = pkt.get_protocols(ethernet.ethernet)[0] 80 | 81 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 82 | # ignore lldp packet 83 | return 84 | dst = eth.dst 85 | src = eth.src 86 | 87 | dpid = datapath.id 88 | self.mac_to_port.setdefault(dpid, {}) 89 | 90 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 91 | 92 | # learn a mac address to avoid FLOOD next time. 93 | self.mac_to_port[dpid][src] = in_port 94 | 95 | if dst in self.mac_to_port[dpid]: 96 | out_port = self.mac_to_port[dpid][dst] 97 | else: 98 | out_port = ofproto.OFPP_FLOOD 99 | 100 | actions = [parser.OFPActionOutput(out_port)] 101 | 102 | # install a flow to avoid packet_in next time 103 | if out_port != ofproto.OFPP_FLOOD: 104 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 105 | # verify if we have a valid buffer_id, if yes avoid to send both 106 | # flow_mod & packet_out 107 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 108 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 109 | return 110 | else: 111 | self.add_flow(datapath, 1, match, actions) 112 | data = None 113 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 114 | data = msg.data 115 | 116 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 117 | in_port=in_port, actions=actions, data=data) 118 | datapath.send_msg(out) 119 | -------------------------------------------------------------------------------- /ryu-exercises/load_balancer.py: -------------------------------------------------------------------------------- 1 | #http://docs.openvswitch.org/en/latest/faq/openflow/ 2 | #https://mail.openvswitch.org/pipermail/ovs-discuss/2016-August/042394.html 3 | #https://stackoverflow.com/questions/36949861/group-table-issue-openflow-mininet 4 | 5 | from ryu.base import app_manager 6 | from ryu.controller import ofp_event 7 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 8 | from ryu.controller.handler import set_ev_cls 9 | from ryu.ofproto import ofproto_v1_3 10 | from ryu.lib.packet import packet 11 | from ryu.lib.packet import ethernet 12 | from ryu.lib.packet import ether_types 13 | 14 | 15 | class SimpleSwitch13(app_manager.RyuApp): 16 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 17 | 18 | def __init__(self, *args, **kwargs): 19 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 20 | self.mac_to_port = {} 21 | 22 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 23 | def switch_features_handler(self, ev): 24 | datapath = ev.msg.datapath 25 | ofproto = datapath.ofproto 26 | parser = datapath.ofproto_parser 27 | 28 | # install table-miss flow entry 29 | ''' 30 | match = parser.OFPMatch() 31 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 32 | ofproto.OFPCML_NO_BUFFER)] 33 | self.add_flow(datapath, 0, match, actions) 34 | ''' 35 | # switch s1 36 | if datapath.id == 1: 37 | # add group tables 38 | self.send_group_mod(datapath) 39 | actions = [parser.OFPActionGroup(group_id=50)] 40 | match = parser.OFPMatch(in_port=3) 41 | self.add_flow(datapath, 10, match, actions) 42 | 43 | #add the return flow for h1 in s1. 44 | # h1 is connected to port 3. 45 | actions = [parser.OFPActionOutput(3)] 46 | match = parser.OFPMatch(in_port=1) 47 | self.add_flow(datapath, 10, match, actions) 48 | 49 | actions = [parser.OFPActionOutput(3)] 50 | match = parser.OFPMatch(in_port=2) 51 | self.add_flow(datapath, 10, match, actions) 52 | 53 | 54 | # switch s4 55 | if datapath.id == 4: 56 | # add group tables 57 | self.send_group_mod(datapath) 58 | actions = [parser.OFPActionGroup(group_id=50)] 59 | match = parser.OFPMatch(in_port=3) 60 | self.add_flow(datapath, 10, match, actions) 61 | 62 | 63 | #add the return flow for h2 in s4. 64 | # h2 is connected to port 3. 65 | actions = [parser.OFPActionOutput(3)] 66 | match = parser.OFPMatch(in_port=1) 67 | self.add_flow(datapath, 10, match, actions) 68 | 69 | actions = [parser.OFPActionOutput(3)] 70 | match = parser.OFPMatch(in_port=2) 71 | self.add_flow(datapath, 10, match, actions) 72 | 73 | 74 | # switch s2 75 | if datapath.id == 2: 76 | # h1 is connected to port 3. 77 | actions = [parser.OFPActionOutput(2)] 78 | match = parser.OFPMatch(in_port=1) 79 | self.add_flow(datapath, 10, match, actions) 80 | 81 | actions = [parser.OFPActionOutput(1)] 82 | match = parser.OFPMatch(in_port=2) 83 | self.add_flow(datapath, 10, match, actions) 84 | 85 | 86 | # switch s3 87 | if datapath.id == 3: 88 | # h1 is connected to port 3. 89 | actions = [parser.OFPActionOutput(2)] 90 | match = parser.OFPMatch(in_port=1) 91 | self.add_flow(datapath, 10, match, actions) 92 | 93 | actions = [parser.OFPActionOutput(1)] 94 | match = parser.OFPMatch(in_port=2) 95 | self.add_flow(datapath, 10, match, actions) 96 | 97 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 98 | ofproto = datapath.ofproto 99 | parser = datapath.ofproto_parser 100 | 101 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 102 | actions)] 103 | if buffer_id: 104 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 105 | priority=priority, match=match, 106 | instructions=inst) 107 | else: 108 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 109 | match=match, instructions=inst) 110 | datapath.send_msg(mod) 111 | 112 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 113 | def _packet_in_handler(self, ev): 114 | # If you hit this you might want to increase 115 | # the "miss_send_length" of your switch 116 | if ev.msg.msg_len < ev.msg.total_len: 117 | self.logger.debug("packet truncated: only %s of %s bytes", 118 | ev.msg.msg_len, ev.msg.total_len) 119 | msg = ev.msg 120 | datapath = msg.datapath 121 | ofproto = datapath.ofproto 122 | parser = datapath.ofproto_parser 123 | in_port = msg.match['in_port'] 124 | 125 | pkt = packet.Packet(msg.data) 126 | eth = pkt.get_protocols(ethernet.ethernet)[0] 127 | 128 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 129 | # ignore lldp packet 130 | return 131 | dst = eth.dst 132 | src = eth.src 133 | 134 | dpid = datapath.id 135 | if dst[:5] == "33:33": 136 | self.logger.info("drop ipv6 multicast packet %s", dst) 137 | return 138 | 139 | self.mac_to_port.setdefault(dpid, {}) 140 | 141 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 142 | 143 | # learn a mac address to avoid FLOOD next time. 144 | self.mac_to_port[dpid][src] = in_port 145 | 146 | if dst in self.mac_to_port[dpid]: 147 | out_port = self.mac_to_port[dpid][dst] 148 | else: 149 | out_port = ofproto.OFPP_FLOOD 150 | 151 | actions = [parser.OFPActionOutput(out_port)] 152 | 153 | # install a flow to avoid packet_in next time 154 | if out_port != ofproto.OFPP_FLOOD: 155 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 156 | # verify if we have a valid buffer_id, if yes avoid to send both 157 | # flow_mod & packet_out 158 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 159 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 160 | return 161 | else: 162 | self.add_flow(datapath, 1, match, actions) 163 | data = None 164 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 165 | data = msg.data 166 | 167 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 168 | in_port=in_port, actions=actions, data=data) 169 | datapath.send_msg(out) 170 | 171 | 172 | 173 | def send_group_mod(self, datapath): 174 | ofproto = datapath.ofproto 175 | parser = datapath.ofproto_parser 176 | 177 | # Hardcoding the stuff, as we already know the topology diagram. 178 | # Group table1 179 | # Receiver port3 (host connected), forward it to port1(switch) and Port2(switch) 180 | LB_WEIGHT1 = 30 #percentage 181 | LB_WEIGHT2 = 70 #percentage 182 | 183 | watch_port = ofproto_v1_3.OFPP_ANY 184 | watch_group = ofproto_v1_3.OFPQ_ALL 185 | 186 | actions1 = [parser.OFPActionOutput(1)] 187 | actions2 = [parser.OFPActionOutput(2)] 188 | buckets = [parser.OFPBucket(LB_WEIGHT1, watch_port, watch_group, actions=actions1), 189 | parser.OFPBucket(LB_WEIGHT2, watch_port, watch_group, actions=actions2)] 190 | 191 | req = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, 192 | ofproto.OFPGT_SELECT, 50, buckets) 193 | datapath.send_msg(req) 194 | 195 | 196 | -------------------------------------------------------------------------------- /ryu-exercises/simple_switch_13.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | from ryu.lib.packet import ether_types 24 | 25 | 26 | class SimpleSwitch13(app_manager.RyuApp): 27 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 31 | self.mac_to_port = {} 32 | 33 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 34 | def switch_features_handler(self, ev): 35 | datapath = ev.msg.datapath 36 | ofproto = datapath.ofproto 37 | parser = datapath.ofproto_parser 38 | 39 | # install table-miss flow entry 40 | # 41 | # We specify NO BUFFER to max_len of the output action due to 42 | # OVS bug. At this moment, if we specify a lesser number, e.g., 43 | # 128, OVS will send Packet-In with invalid buffer_id and 44 | # truncated packet data. In that case, we cannot output packets 45 | # correctly. The bug has been fixed in OVS v2.1.0. 46 | match = parser.OFPMatch() 47 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 48 | ofproto.OFPCML_NO_BUFFER)] 49 | self.add_flow(datapath, 0, match, actions) 50 | 51 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 52 | ofproto = datapath.ofproto 53 | parser = datapath.ofproto_parser 54 | 55 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 56 | actions)] 57 | if buffer_id: 58 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 59 | priority=priority, match=match, 60 | instructions=inst) 61 | else: 62 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 63 | match=match, instructions=inst) 64 | datapath.send_msg(mod) 65 | 66 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 67 | def _packet_in_handler(self, ev): 68 | # If you hit this you might want to increase 69 | # the "miss_send_length" of your switch 70 | if ev.msg.msg_len < ev.msg.total_len: 71 | self.logger.debug("packet truncated: only %s of %s bytes", 72 | ev.msg.msg_len, ev.msg.total_len) 73 | msg = ev.msg 74 | datapath = msg.datapath 75 | ofproto = datapath.ofproto 76 | parser = datapath.ofproto_parser 77 | in_port = msg.match['in_port'] 78 | 79 | pkt = packet.Packet(msg.data) 80 | eth = pkt.get_protocols(ethernet.ethernet)[0] 81 | 82 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 83 | # ignore lldp packet 84 | return 85 | dst = eth.dst 86 | src = eth.src 87 | 88 | dpid = datapath.id 89 | self.mac_to_port.setdefault(dpid, {}) 90 | 91 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 92 | 93 | # learn a mac address to avoid FLOOD next time. 94 | self.mac_to_port[dpid][src] = in_port 95 | 96 | if dst in self.mac_to_port[dpid]: 97 | out_port = self.mac_to_port[dpid][dst] 98 | else: 99 | out_port = ofproto.OFPP_FLOOD 100 | 101 | actions = [parser.OFPActionOutput(out_port)] 102 | 103 | # install a flow to avoid packet_in next time 104 | if out_port != ofproto.OFPP_FLOOD: 105 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) 106 | # verify if we have a valid buffer_id, if yes avoid to send both 107 | # flow_mod & packet_out 108 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 109 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 110 | return 111 | else: 112 | self.add_flow(datapath, 1, match, actions) 113 | data = None 114 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 115 | data = msg.data 116 | 117 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 118 | in_port=in_port, actions=actions, data=data) 119 | datapath.send_msg(out) 120 | -------------------------------------------------------------------------------- /ryu_hub_exercise.md: -------------------------------------------------------------------------------- 1 | RYU Exercise : HUB Implementation 2 | =================================== 3 | 4 | ## Hub: 5 | 6 | When a packet arrives at one port, it is copied to the other ports so that all segments of the LAN can see all packets. 7 | 8 | 9 | In this exercise, We are going to modify the simple_switch_13.py(Layer 2 Switch) application to Hub application. 10 | 11 | 12 | ## High level Steps: 13 | 14 | In the switch feature handler, instead of table miss entry, intall the proactive flood(all ports) flow 15 | 16 | 1. Add the proactive flow with empty match(All packets will match) with action FLOOD to all ports. 17 | 18 | 19 | 20 | 21 | ## Code changes: 22 | 23 | Copy the simple_switch_13.py to hub.py file 24 | 25 | ``` 26 | cp simple_switch_13.py hub.py 27 | ``` 28 | 29 | Modify the hub.py file as below, 30 | 31 | 32 | 33 | Populate the IP Match (replace the ethernet match) 34 | 35 | ``` 36 | actions = [parser.OFPActionOutput(port=ofproto.OFPP_FLOOD)] 37 | 38 | ``` 39 | 40 | 41 | ## Test: 42 | 43 | Ryu Application 44 | 45 | ``` 46 | ryu-manager hub.py 47 | 48 | ``` 49 | 50 | Mininet Topology 51 | 52 | ``` 53 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=simple,4 54 | 55 | ``` 56 | 57 | Check the OVS flows 58 | 59 | 60 | ``` 61 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /ryu_part1.md: -------------------------------------------------------------------------------- 1 | # RYU Basic Usage: 2 | 3 | 4 | ## 1. How to start the ryu controller 5 | 6 | ``` 7 | ryu-manager 8 | 9 | ``` 10 | Example applications are available in ryu.app. or we can specify the filename(with absolute path). 11 | 12 | 13 | 14 | Check the running process details. 15 | 16 | ``` 17 | ps -ef | grep ryu-manager 18 | ``` 19 | 20 | RYU Manager listens on openflow ports(6653,6633) are in listening state. 21 | 22 | 23 | Check the port statistics 24 | 25 | ``` 26 | netstat -ap 27 | netstat -ap | grep python 28 | 29 | ``` 30 | 31 | 32 | ## 2. How to stop the ryu controller 33 | 34 | ``` 35 | CTRL + C (Kill the Process) 36 | 37 | ( or ) 38 | pkill -9 ryu-manager 39 | 40 | ``` 41 | 42 | Please check the running process by ps -ef | grep ryu-manager 43 | 44 | 45 | ## 3. RYU Controller command line options 46 | 47 | 48 | ``` 49 | ryu-manager --help 50 | 51 | ``` 52 | 53 | ``` 54 | ryu-manager --verbose 55 | 56 | ``` 57 | 58 | 59 | ## 4. How to run the Application 60 | 61 | Example Application is installed in ryu/app folder. 62 | 63 | Specify the filename as ryu.app. 64 | 65 | Example: 66 | 67 | 68 | ``` 69 | ryu-manager --verbose ryu.app.simple_switch_13 70 | 71 | ``` 72 | 73 | Download the file and run it. 74 | 75 | ``` 76 | ryu-manager --verbose simple_switch_13.py 77 | 78 | ``` 79 | 80 | 81 | # References 82 | 83 | https://en.wikipedia.org/wiki/OpenFlow 84 | 85 | http://ryu.readthedocs.io/en/latest/ 86 | 87 | http://ryu.readthedocs.io/en/latest/writing_ryu_app.html 88 | 89 | https://osrg.github.io/ryu/resources.html 90 | 91 | https://github.com/osrg/ryu/tree/master/ryu 92 | 93 | -------------------------------------------------------------------------------- /ryu_part10.md: -------------------------------------------------------------------------------- 1 | Topology Discovery 2 | =================== 3 | 4 | In this section, we discuss how RYU controller discovers the topology. 5 | 6 | 7 | 1. Switch Discovery 8 | 9 | LLDP(Link Layer Discovery protocol) 10 | 11 | 12 | 2. Host Discovery 13 | 14 | (write our algo in the packet_in handler to discover the hosts, connected in the switch) 15 | 16 | 17 | ## Program overview: 18 | 19 | 20 | 21 | 22 | 23 | ## Testing 24 | 25 | 26 | 27 | 28 | 29 | 30 | # References: 31 | 32 | 33 | 1. https://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol 34 | 35 | 2. http://vlkan.com/blog/post/2013/08/06/sdn-discovery/ 36 | 37 | 3. https://sdn-lab.com/2014/12/25/shortest-path-forwarding-with-openflow-on-ryu/ 38 | 39 | 4. https://github.com/castroflavio/ryu/blob/master/ryu/app/shortestpath.py -------------------------------------------------------------------------------- /ryu_part2.md: -------------------------------------------------------------------------------- 1 | RYU Part2: 2 | ========= 3 | 4 | In this part , we are going to start writing the sample RYU applications step by step. 5 | 6 | Base Rule for beginners: 7 | Please copy the example application file and modify as per your needs. 8 | 9 | In this ryu exercises, i am going to use Openflow 1.3 Version, and use simple_switch_13.py as example program to start with. 10 | 11 | 12 | # RYU Application overview: 13 | 14 | 15 | ## Step1 : Import the base classes / library 16 | 17 | ``` 18 | from ryu.base import app_manager 19 | 20 | from ryu.controller import ofp_event 21 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 22 | from ryu.controller.handler import set_ev_cls 23 | 24 | from ryu.ofproto import ofproto_v1_3 25 | 26 | from ryu.lib.packet import packet 27 | from ryu.lib.packet import ethernet 28 | from ryu.lib.packet import ether_types 29 | ``` 30 | 31 | **app_manager :** main entry point for the application. 32 | 33 | **set_ev_cls, ofp_event, Dispatcher :** used for capturing openflow event, when the openflow packet received 34 | 35 | **ofproto_v1_3 :** Specifies which Openflow version to be used. 36 | 37 | **packet, ethernet, ether_types :** packet processing library 38 | 39 | 40 | 41 | ## step2: Define your application class (derived from app_manager) and write the base stuff: 42 | 43 | 1. 44 | ``` 45 | class SimpleSwitch13(app_manager.RyuApp): 46 | ``` 47 | 2. 48 | sets the openflow version 49 | 50 | ``` 51 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 52 | ``` 53 | 3. define the constructor . 54 | ``` 55 | def __init__(self, *args, **kwargs): 56 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 57 | ``` 58 | 59 | ## step3: Handle the relavent Openflow events (when the openflow feature request message received, When the openflow packet in message received): 60 | 61 | In the Event handler, write your application logic. 62 | 63 | 64 | 65 | # How to Implement Openflow L2 Switch in RYU : 66 | 67 | 1. mac_to_port dictionary to store the mac table. 68 | 69 | 2. In Switch Feature Request Event handler Insert table miss entry (using add_flow function). 70 | Table Miss entry flow: Match all packets, priority 0, forward it to controller port. 71 | 72 | 3. In Packet_In Handler, 73 | Inspect the incoming packet, 74 | Update the mac_to_port dictionary wth "soruce mac" and " incoming port" 75 | Check the destination mac is present in the mac_to_port dictionary. 76 | if present, 77 | match = inport, srcmac, dstmac 78 | action = forward ot the port number 79 | Add a flow (with match, action). 80 | else 81 | action = flood a packet 82 | call packet out with action. 83 | 84 | 85 | # Execute the Openflow L2 Switch : 86 | 87 | ``` 88 | ryu-manager simple_switch_13.py 89 | ``` 90 | 91 | Run the Mininet Topology 92 | ``` 93 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 94 | ``` 95 | 96 | Check the output. 97 | 98 | 99 | 100 | # References 101 | 102 | https://en.wikipedia.org/wiki/OpenFlow 103 | 104 | http://ryu.readthedocs.io/en/latest/ 105 | 106 | https://osrg.github.io/ryu/resources.html 107 | 108 | https://github.com/osrg/ryu/tree/master/ryu 109 | -------------------------------------------------------------------------------- /ryu_part3.md: -------------------------------------------------------------------------------- 1 | RYU Part3: 2 | ========= 3 | 4 | In this exercise, We are going to modify the simple_switch_13.py(Layer 2 Switch) application to Layer 3 Switch. 5 | 6 | We will populate the flow tables based on Layer 3 information( source ip and destination ip). 7 | 8 | ## High level Steps: 9 | 10 | 1. In the ethernet packet, check the ether frame type field is IP. 11 | 2. if it is IP, load the IP Packet from the packet. 12 | 3. extract the srcip , and dst ip from IP Packet. 13 | 4. Prepare the Openflow Match with eth_type, ipv4_src, ipv4_dst ertype 14 | 15 | 16 | ## Code changes: 17 | 18 | Copy the simple_switch_13.py to ex2_L3Match_switch.py file 19 | 20 | ``` 21 | cp simple_switch_13.py ex2_L3Match_switch.py 22 | ``` 23 | Modify the ex2_L3Match_switch.py file as below, 24 | 25 | Include the required 26 | ``` 27 | from ryu.lib.packet import ipv4 28 | ``` 29 | 30 | Populate the IP Match (replace the ethernet match) 31 | 32 | ``` 33 | # check IP Protocol and create a match for IP 34 | if eth.ethertype == ether_types.ETH_TYPE_IP: 35 | ip = pkt.get_protocol(ipv4.ipv4) 36 | srcip = ip.src 37 | dstip = ip.dst 38 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, 39 | ipv4_src=srcip, 40 | ipv4_dst=dstip 41 | ) 42 | ``` 43 | 44 | 45 | ## Test: 46 | 47 | Ryu Application 48 | 49 | ``` 50 | ryu-manager ex2_L3Match_switch.py 51 | 52 | ``` 53 | 54 | Mininet Topology 55 | 56 | ``` 57 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 58 | 59 | ``` 60 | 61 | Check the OVS flows 62 | 63 | 64 | ``` 65 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 66 | ``` 67 | 68 | 69 | # Reference: 70 | 71 | 1. https://www.opennetworking.org/software-defined-standards/specifications/ 72 | 73 | page 62 contains match fields. 74 | 75 | 2. https://en.wikipedia.org/wiki/Ethernet_frame 76 | -------------------------------------------------------------------------------- /ryu_part4.md: -------------------------------------------------------------------------------- 1 | RYU Part4: 2 | ========= 3 | 4 | In this exercise, We are going to modify the simple_switch_13.py(Layer 2 Switch) application to Layer 4 Switch. 5 | 6 | We will populate the flow tables based on Layer 4 information( source ip and destination ip, transport protocol(udp or tcp), src port and destinaton port). 7 | 8 | ## High level Steps: 9 | 10 | 1. In the ethernet packet, check the ether frame type field is IP. 11 | 2. if it is IP, load the IP Packet from the packet. 12 | 3. extract the srcip , and dst ip from IP Packet. 13 | 4. Check the IP Protocol 14 | 5. If it is ICMP 15 | Prepare the openflow match with IP Src, IP dst and Protocol. 16 | 6. If it is TCP, 17 | extract the tcp src port and tcp dst port fied 18 | Prepare the openflow match with IP Src, IP dst and Protocol, TCP Src Port and TCP dst port.. 19 | 7. If it is UDP 20 | Extract the udp src port and tcp dst port fied 21 | Prepare the openflow match with IP src, IP dst , protcol, udp src and udp dst port. 22 | 23 | 24 | 25 | ## Code changes: 26 | 27 | Copy the simple_switch_13.py to ex3_L4Match_switch.py file 28 | ``` 29 | cp simple_switch_13.py ex3_L4Match_switch.py 30 | ``` 31 | 32 | Modify the ex3_L4Match_switch.py file as below, 33 | 34 | Include the required library modules 35 | ``` 36 | from ryu.lib.packet import in_proto 37 | from ryu.lib.packet import ipv4 38 | from ryu.lib.packet import icmp 39 | from ryu.lib.packet import tcp 40 | from ryu.lib.packet import udp 41 | ``` 42 | 43 | Populate the IP Match (replace the ethernet match) 44 | 45 | ``` 46 | 47 | # check IP Protocol and create a match for IP 48 | if eth.ethertype == ether_types.ETH_TYPE_IP: 49 | ip = pkt.get_protocol(ipv4.ipv4) 50 | srcip = ip.src 51 | dstip = ip.dst 52 | protocol = ip.proto 53 | 54 | # if ICMP Protocol 55 | if protocol == in_proto.IPPROTO_ICMP: 56 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol) 57 | 58 | # if TCP Protocol 59 | elif protocol == in_proto.IPPROTO_TCP: 60 | t = pkt.get_protocol(tcp.tcp) 61 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol, tcp_src=t.src_port, tcp_dst=t.dst_port,) 62 | 63 | # If UDP Protocol 64 | elif protocol == in_proto.IPPROTO_UDP: 65 | u = pkt.get_protocol(udp.udp) 66 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ipv4_src=srcip, ipv4_dst=dstip, ip_proto=protocol, udp_src=u.src_port, udp_dst=u.dst_port,) 67 | 68 | 69 | ``` 70 | 71 | ## Test 72 | 73 | We need to send TCP/UDP Traffic to see TCP/UDP Flows. 74 | 75 | 76 | Ryu Application 77 | 78 | ``` 79 | ryu-manager ex3_L4Match_switch.py 80 | ``` 81 | 82 | Mininet Topology 83 | 84 | ``` 85 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 -x 86 | ``` 87 | 88 | IPERF Test 89 | 90 | h4 node 91 | ``` 92 | iperf -s 93 | ``` 94 | h1 node 95 | ``` 96 | iperf -c 10.1.1.4 -P 10 97 | ``` 98 | 99 | 100 | OVS flows 101 | ``` 102 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 103 | ``` 104 | 105 | 106 | 107 | 108 | 109 | # Reference: 110 | 111 | 1. https://www.opennetworking.org/software-defined-standards/specifications/ 112 | 113 | page 62 contains match fields. 114 | 115 | 2. https://en.wikipedia.org/wiki/Ethernet_frame 116 | 117 | 3. https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers 118 | -------------------------------------------------------------------------------- /ryu_part5.md: -------------------------------------------------------------------------------- 1 | RYU Part5: 2 | ========= 3 | 4 | In this exercise, We are going to include the flow expiry/timeouts (hard timeout and idle timeout) in the simple_switch_13.py(Layer 2 Switch). 5 | 6 | 7 | 8 | *Idle Timeout:* 9 | 10 | If the flow is Idle(no packets hitting this flow) for the specified time(Idle Time), the flow will be expired. 11 | 12 | 13 | *Hard Timeout:* 14 | 15 | Flow entry must be expired in the specified number of seconds regardless of whether or not packets are hitting the entry 16 | 17 | 18 | In simple_switch.py, the default timeout values(for idle and hard) are not set(0). it means, the flows are permanent. it will never expiry. 19 | 20 | 21 | 22 | ## High level Steps: 23 | 24 | 1. in the flow addition function, specify the timeouts 25 | 26 | 27 | ## Code changes: 28 | 29 | Copy the simple_switch_13.py to ex4_flow_timeout.py file 30 | ``` 31 | cp simple_switch_13.py ex4_flow_timeout.py 32 | ``` 33 | 34 | Modify the ex4_flow_timeout.py file as below, 35 | 36 | 1. In the add_flow function, include idle, hard parameters with default value as 0. 37 | 2. OFPFlowMod API include the idle_timeout,hard_timeout parameters. 38 | 39 | ``` 40 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 41 | idle_timeout=idle, hard_timeout=hard, priority=priority, match=match, 42 | instructions=inst) 43 | 44 | ``` 45 | 3. Call the add_flow function with idle, hard value 46 | 47 | 48 | Note : The default command is command=ofproto.OFPFC_ADD 49 | 50 | 51 | 52 | ## Test 53 | 54 | 55 | 56 | Ryu Application 57 | 58 | ``` 59 | ryu-manager ex4_flow_timeout.py 60 | ``` 61 | 62 | Mininet Topology 63 | 64 | ``` 65 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=single,4 66 | ``` 67 | 68 | Lets Ping and watch the flows 69 | 70 | 71 | OVS flows 72 | ``` 73 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 74 | ``` 75 | 76 | 77 | 78 | # Reference: 79 | 80 | 1. https://www.opennetworking.org/software-defined-standards/specifications/ 81 | 82 | page 155 contains timeout details. 83 | 84 | 2. API details 85 | http://ryu.readthedocs.io/en/latest/ofproto_v1_3_ref.html?highlight=OFPFlowMod 86 | -------------------------------------------------------------------------------- /ryu_part6.md: -------------------------------------------------------------------------------- 1 | RYU Part6: 2 | ========= 3 | 4 | In this exercise, We are going to include the flow priority in the simple_switch_13.py(Layer 2 Switch). 5 | 6 | In this exercise, we will add the TCP DROP flow( Match ALL Traffic) with higher priority. Remaining traffic will go via the L2 Switch flows. 7 | 8 | 9 | 10 | *Flow Priority* 11 | 12 | priority: matching precedence of the flow entry 13 | 14 | size: 2 bytes 15 | 16 | The packet is matched against flow entries in the flow table and only the highest priority flow entry that matches the packet must be selected 17 | 18 | 19 | 20 | 21 | 22 | ## High level Steps: 23 | 24 | 1. Add the ICMP Drop flow with priority 10000 in Openflow Feature Request event 25 | 26 | 27 | ## Code changes: 28 | 29 | Copy the simple_switch_13.py to ex5_flow_priority.py file 30 | ``` 31 | cp simple_switch_13.py ex5_flow_priority.py 32 | ``` 33 | Modify the ex5_flow_priority.py file as below, 34 | 35 | 36 | include the libs 37 | ``` 38 | from ryu.lib.packet import in_proto 39 | from ryu.lib.packet import ipv4 40 | ``` 41 | 42 | In the switch_features_handler function, Add the TCP drop flow 43 | 44 | Note: Default Action is DROP. 45 | 46 | ``` 47 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=in_proto.IPPROTO_TCP) 48 | mod = parser.OFPFlowMod(datapath=datapath, table_id=0, priority=10000, match=match) 49 | datapath.send_msg(mod) 50 | 51 | ``` 52 | 53 | 54 | ## Test 55 | 56 | 57 | Ryu Application 58 | 59 | ``` 60 | ryu-manager ex5_flow_priority.py 61 | ``` 62 | 63 | Mininet Topology 64 | 65 | ``` 66 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=single,4 -x 67 | ``` 68 | 69 | Check the flows 70 | 71 | ``` 72 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 73 | ``` 74 | 75 | Lets Ping and watch the flows 76 | 77 | 78 | Perform IPERF Test 79 | 80 | h4 node 81 | ``` 82 | iperf -s 83 | ``` 84 | h1 node 85 | ``` 86 | iperf -c 10.1.1.4 -P 10 87 | ``` 88 | 89 | 90 | OVS flows 91 | ``` 92 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 93 | ``` 94 | 95 | 96 | 97 | 98 | # Reference: 99 | 100 | 1. https://www.opennetworking.org/software-defined-standards/specifications/ 101 | 102 | page 155 contains timeout details. 103 | 104 | 2. API details 105 | http://ryu.readthedocs.io/en/latest/ofproto_v1_3_ref.html?highlight=OFPFlowMod 106 | -------------------------------------------------------------------------------- /ryu_part7.md: -------------------------------------------------------------------------------- 1 | RYU Part7: 2 | ========= 3 | 4 | In this exercise, We are going to update the simple_switch_13.py(Layer 2 Switch)with PIPELINE PROCESSING (multiple tables) . 5 | 6 | 7 | ![Alt text](imgs/pipeline.png?raw=true "PipeLine Processing") 8 | 9 | We will create two tables 10 | FILTER TABLE(Table 5) and FORWARD TABLE(Table 10). 11 | 12 | FILTER TABLE : will add packet filter rules (ACL rules). Block ICMP traffic. 13 | 14 | FORWARD TABLE: Here, our forwarding rules (L2 switch) 15 | 16 | 17 | 18 | ## High level Steps: 19 | 20 | In Switch Feature Event Handler, add the table creation/initialization stuff as below. 21 | 22 | 1. In Table 0(default), Add the Match all Flow with action GO TO - Filter Table. 23 | 24 | 2. In Table 5(Filter Table), Add the Match All Flow with the Action GO TO - Forward Table 25 | 26 | 3. In Table 10(Forward Table), Add the Table Miss Entry. 27 | 28 | 4. Apply the filter rules(Drop ICMP Traffic) in the filter table. 29 | 30 | 31 | The Layer2 switching logic, will update the Flow rules in FORWARD Table. 32 | 33 | 34 | 35 | ## Code changes: 36 | 37 | 38 | Library inclusion 39 | ``` 40 | from ryu.lib.packet import ipv4 41 | from ryu.lib.packet import in_proto 42 | 43 | FILTER_TABLE = 5 44 | FORWARD_TABLE = 10 45 | 46 | ``` 47 | 48 | Create the table initialization. 49 | 50 | ``` 51 | def add_default_table(self, datapath): 52 | ofproto = datapath.ofproto 53 | parser = datapath.ofproto_parser 54 | inst = [parser.OFPInstructionGotoTable(FILTER_TABLE)] 55 | mod = parser.OFPFlowMod(datapath=datapath, table_id=0, instructions=inst) 56 | datapath.send_msg(mod) 57 | 58 | def add_filter_table(self, datapath): 59 | ofproto = datapath.ofproto 60 | parser = datapath.ofproto_parser 61 | inst = [parser.OFPInstructionGotoTable(FORWARD_TABLE)] 62 | mod = parser.OFPFlowMod(datapath=datapath, table_id=FILTER_TABLE, 63 | priority=1, instructions=inst) 64 | datapath.send_msg(mod) 65 | 66 | def apply_filter_table_rules(self, datapath): 67 | ofproto = datapath.ofproto 68 | parser = datapath.ofproto_parser 69 | match = parser.OFPMatch(eth_type=ether_types.ETH_TYPE_IP, ip_proto=in_proto.IPPROTO_ICMP) 70 | mod = parser.OFPFlowMod(datapath=datapath, table_id=FILTER_TABLE, 71 | priority=10000, match=match) 72 | datapath.send_msg(mod) 73 | 74 | ``` 75 | 76 | This table intialization routine will be placed in the switch_features_handler routine. 77 | 78 | 79 | In the add_flow function, we specify the tableid as FORWARD Table. 80 | 81 | 82 | 83 | ## Test 84 | 85 | 86 | Ryu Application 87 | 88 | ``` 89 | ryu-manager ex6_multiple_tables.py 90 | ``` 91 | 92 | Mininet Topology 93 | 94 | ``` 95 | sudo mn --controller=remote,ip=127.0.0.1 --mac -i 10.1.1.0/24 --switch=ovsk,protocols=OpenFlow13 --topo=single,4 -x 96 | ``` 97 | 98 | Lets Ping and watch the flows 99 | 100 | 101 | IPERF Test 102 | 103 | h4 node 104 | ``` 105 | iperf -s 106 | ``` 107 | h1 node 108 | ``` 109 | iperf -c 10.1.1.4 -P 10 110 | ``` 111 | 112 | 113 | OVS flows 114 | ``` 115 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 116 | ``` 117 | 118 | 119 | 120 | 121 | # Reference: 122 | 123 | 1. https://www.opennetworking.org/software-defined-standards/specifications/ 124 | 125 | page 16 contains pipeline details (multiple . 126 | 127 | 2. API details 128 | http://ryu.readthedocs.io/en/latest/ofproto_v1_3_ref.html?highlight=OFPFlowMod 129 | -------------------------------------------------------------------------------- /ryu_part8.md: -------------------------------------------------------------------------------- 1 | RYU Part8 - Group Table 2 | ========================= 3 | 4 | 5 | ### What is group table? 6 | 7 | The ability for a flow entry to point to a group enables OpenFlow 8 | to represent additional methods of forwarding (e.g. select and all). 9 | 10 | ### Usecase1: 11 | 12 | Copy the packet to ALL buckets and process it. (Ex: Sniffer/Port Monitor) 13 | 14 | ![Alt text](imgs/group_table.png?raw=true "Group table example") 15 | 16 | 17 | ### Usecase2: 18 | 19 | Forward the packet to 1 bucket(out of N buckets) and process it. (Load Balancer) 20 | 21 | ![Alt text](imgs/group_table1.png?raw=true "Group table example") 22 | 23 | 24 | 25 | # 1. Sniffer Demo 26 | 27 | 28 | We wants to capture all the traffic travels via switch S1, in the sniffer host. 29 | 30 | 31 | ## Topology Diagram 32 | 33 | ![Alt text](imgs/group_table.png?raw=true "Group table example") 34 | 35 | Topology file: mininet_topologies/group_table_topo.py 36 | 37 | ### Logic: 38 | 39 | S1 has three ports. port1 connected to sniffer host, port2 connected to S2, Port3 connected to S3. 40 | 41 | ### Switching Logic to applied in S1 : 42 | 43 | 1. The packets received from Port2 will be forwarded to Port3 and Port1 44 | 2. The packets received from Port3 will be forwarded to Port2 and Port1 45 | 46 | 47 | How to achieve this, 48 | 49 | ### Group table1(Group Table ID 50): 50 | 51 | Create a Group table with TYPE=ALL(it means, copy a packet for each bucket. and each bucket will be processed). create two buckets. one bucket will send the packet to Port3, another bucket will send the packet to Port1 52 | 53 | 54 | ### Group table2(Group ID 51): 55 | 56 | Create a Group table with TYPE=ALL(it means, copy a packet for each bucket. and each bucket will be processed). create two buckets. one bucket will send the packet to Port2, another bucket will send the packet to Port1 57 | 58 | 59 | Create a proactive flows in Switch S1: 60 | 61 | 1. All the packets received from port2 will be forwarded to Group table1(Group table ID 50) 62 | 63 | 2. All the packets received from port3 will be forwarded to Group table2(Group table ID 51) 64 | 65 | 66 | 67 | 68 | 69 | ## Code changes: 70 | 71 | 72 | Creat a function for creating the group 73 | 74 | ``` 75 | def send_group_mod(self, datapath): 76 | ofproto = datapath.ofproto 77 | parser = datapath.ofproto_parser 78 | 79 | # Hardcoding the stuff, as we already know the topology diagram. 80 | # Group table1 81 | # Receiver port2, forward it to port1 and Port3 82 | 83 | actions1 = [parser.OFPActionOutput(1)] 84 | actions2 = [parser.OFPActionOutput(3)] 85 | buckets = [parser.OFPBucket(actions=actions1), 86 | parser.OFPBucket(actions=actions2)] 87 | req = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, 88 | ofproto.OFPGT_ALL, 50, buckets) 89 | datapath.send_msg(req) 90 | 91 | # Group table2 92 | # Receive Port3, forward it to port1 and Port2 93 | actions1 = [parser.OFPActionOutput(1)] 94 | actions2 = [parser.OFPActionOutput(2)] 95 | buckets = [parser.OFPBucket(actions=actions1), 96 | parser.OFPBucket(actions=actions2)] 97 | req = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, 98 | ofproto.OFPGT_ALL, 51, buckets) 99 | datapath.send_msg(req) 100 | ``` 101 | 102 | 103 | Add Proactive Flows for switch1 (in switch feature event) 104 | 105 | 106 | 107 | ``` 108 | # switch s1 109 | if datapath.id == 1: 110 | # add group tables 111 | self.send_group_mod(datapath) 112 | actions = [parser.OFPActionGroup(group_id=50)] 113 | match = parser.OFPMatch(in_port=2) 114 | self.add_flow(datapath, 10, match, actions) 115 | # entry 2 116 | actions = [parser.OFPActionGroup(group_id=51)] 117 | match = parser.OFPMatch(in_port=3) 118 | self.add_flow(datapath, 10, match, actions) 119 | 120 | ``` 121 | 122 | 123 | ## Testing: 124 | 125 | 1. start the RYU controller 126 | 127 | ``` 128 | ryu-manager ex7_group_tables.py 129 | ``` 130 | 131 | 2. start the mininet topology 132 | 133 | ``` 134 | sudo python group_table_topo.py 135 | ``` 136 | 137 | 3. verify the group tables and proactive flows in switch S1 138 | 139 | ``` 140 | sudo ovs-ofctl -O OpenFlow13 dump-groups s1 141 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 142 | ``` 143 | 144 | 4. pingll, and ping h1 to h6 continuously 145 | 146 | 147 | 5. capture the packets in sniffer host (tcpdump). we can see the ping packets in sniffer host. 148 | 149 | 150 | 151 | # 2. Loadbalancer Demo 152 | 153 | 154 | Note: 155 | 156 | **This is not the full fledged the Loadbalancer project, I just want to demonstrate the Group Table Load balancer functionality. Hence I hardcoded the topology information in the RYU load balancer application with proactive flows.** 157 | 158 | 159 | ## Topology Diagram 160 | 161 | ![Alt text](imgs/group_table1.png?raw=true "Group table example") 162 | 163 | Topology file: mininet_topologies/group_table_lb.py 164 | 165 | 166 | ##  Application Logic: 167 | 168 | 169 | 1. In the switch S1, and S4 170 | 171 | Add the group table 50 - 172 | This group table is type OFPGT_SELECT with two buckets. 173 | Bucket1 - Output to Port1 174 | Bucket2 - Output to Port2 175 | 176 | So , when the packet enters in this group table, it will select(switch implementation specific algorithm) any one bucket and send this packet. 177 | 178 | 179 | 2. In Switch S1, When the packet enters from port 3, send it to group table 50. 180 | 181 | 3. In switch S1, when the packet enters from port 1 or 2, send it to port3. 182 | 183 | 4. In Switch S4, When the packet enters from port 3, send it to group table 50. 184 | 185 | 5. In switch S4, when the packet enters from port 1 or 2, send it to port3. 186 | 187 | 6. In Switch S2 and S3, it just need to forward the packet to other port. 188 | 189 | ## Code overview: 190 | 191 | 192 | ryu-exercises/load-balancer.py 193 | 194 | 195 | ## Testing: 196 | 197 | 198 | 199 | 1. start the RYU controller 200 | 201 | ``` 202 | ryu-manager load_balancer.py 203 | ``` 204 | 205 | 2. start the mininet topology 206 | 207 | ``` 208 | sudo python group_table_lb.py 209 | ``` 210 | 211 | 3. verify the group tables and proactive flows in switch S1,S4,S2,S3 212 | 213 | ``` 214 | sudo ovs-ofctl -O OpenFlow13 dump-groups s1 215 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 216 | sudo ovs-ofctl -O OpenFlow13 dump-groups s4 217 | sudo ovs-ofctl -O OpenFlow13 dump-flows s4 218 | sudo ovs-ofctl -O OpenFlow13 dump-flows s2 219 | sudo ovs-ofctl -O OpenFlow13 dump-flows s3 220 | ``` 221 | 222 | 4. Add a arp entry: ?? 223 | 224 | 225 | 226 | 5. Test the ping and verify the status of load balancing 227 | 228 | ``` 229 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s1 230 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s4 231 | sudo ovs-ofctl -O OpenFlow13 dump-flows s2 232 | sudo ovs-ofctl -O OpenFlow13 dump-flows s3 233 | 234 | ``` 235 | 236 | 237 | 6. Test the TCP Traffic between h1 and h2 238 | 239 | 240 | ``` 241 | mininet> h2 iperf -s & 242 | mininet> h1 iperf -c h2 -t 30 243 | ``` 244 | 245 | Check the stats 246 | 247 | ``` 248 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s1 249 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s4 250 | sudo ovs-ofctl -O OpenFlow13 dump-flows s2 251 | sudo ovs-ofctl -O OpenFlow13 dump-flows s3 252 | 253 | ``` 254 | 255 | 256 | 7. Openvswitch Group table implementation details 257 | 258 | http://docs.openvswitch.org/en/latest/faq/openflow/ 259 | 260 | 8. Test the TCP Traffic with parallel streams beween h1 and h2 and check the status 261 | 262 | ``` 263 | mininet> h2 iperf -s & 264 | mininet> h1 iperf -c h2 -P 4 -t 30 265 | ``` 266 | 267 | Check the stats 268 | 269 | ``` 270 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s1 271 | sudo ovs-ofctl -O OpenFlow13 dump-group-stats s4 272 | sudo ovs-ofctl -O OpenFlow13 dump-flows s2 273 | sudo ovs-ofctl -O OpenFlow13 dump-flows s3 274 | 275 | ``` 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | # References: 302 | 303 | 304 | 1. openflow 1.3 specification documemnt, 5.6 Group table 305 | 306 | 2. http://ryu.readthedocs.io/en/latest/ofproto_v1_3_ref.html 307 | 308 | 3. https://www.quora.com/Why-dose-the-Openflow-protocol-exist-the-group-table-And-what-the-relationship-between-pipeline-and-group-table 309 | 310 | 311 | 4. http://www.muzixing.com/pages/2014/11/07/load-balancemultipath-application-on-ryu.html 312 | -------------------------------------------------------------------------------- /ryu_part9.md: -------------------------------------------------------------------------------- 1 | ARP Proxy example 2 | =================== 3 | 4 | This exercise helps to understand, generating packets from the controller. 5 | This is ARP Proxy application. This application answers the ARP requests(instead of forwarding the ARP broadcasts to all hosts) 6 | 7 | 8 | ## Objective 9 | 10 | This Application act as ARP Proxy. It captures the ARP Packets, builds the ARP response and send it to the Src. 11 | 12 | 13 | ## code changes : 14 | 15 | 1. include the lib 16 | 17 | ``` 18 | from ryu.lib.packet import arp 19 | ``` 20 | 21 | 2. define your table 22 | 23 | ``` 24 | arp_table = {"10.0.0.1": "00:00:00:00:00:01", 25 | "10.0.0.2": "00:00:00:00:00:02", 26 | "10.0.0.3": "00:00:00:00:00:03", 27 | "10.0.0.4": "00:00:00:00:00:04" 28 | } 29 | ``` 30 | 31 | 32 | 3. Write your arp proxy function. 33 | 34 | 35 | ``` 36 | def arp_process(self, datapath, eth, a, in_port): 37 | r = arp_table.get(a.dst_ip) 38 | if r: 39 | self.logger.info("Matched MAC %s ", r) 40 | arp_resp = packet.Packet() 41 | arp_resp.add_protocol(ethernet.ethernet(ethertype=eth.ethertype, 42 | dst=eth.src, src=r)) 43 | arp_resp.add_protocol(arp.arp(opcode=arp.ARP_REPLY, 44 | src_mac=r, src_ip=a.dst_ip, 45 | dst_mac=a.src_mac, 46 | dst_ip=a.src_ip)) 47 | 48 | arp_resp.serialize() 49 | actions = [] 50 | actions.append(datapath.ofproto_parser.OFPActionOutput(in_port)) 51 | parser = datapath.ofproto_parser 52 | ofproto = datapath.ofproto 53 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, 54 | in_port=ofproto.OFPP_CONTROLLER, actions=actions, data=arp_resp) 55 | datapath.send_msg(out) 56 | self.logger.info("Proxied ARP Response packet") 57 | ``` 58 | 59 | 60 | 4. In the packet handler function, if it arp packet, call the arp_proxy function. 61 | 62 | ``` 63 | # Check whether is it arp packet 64 | if eth.ethertype == ether_types.ETH_TYPE_ARP: 65 | self.logger.info("Received ARP Packet %s %s %s ", dpid, src, dst) 66 | a = pkt.get_protocol(arp.arp) 67 | self.arp_process(datapath, eth, a, in_port) 68 | return 69 | ``` 70 | 71 | 72 | 73 | ## Testing 74 | 75 | 1. Run your topology ( Linear topology) 76 | 77 | ``` 78 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=linear,4 79 | ``` 80 | 81 | 2. Run the RYU controller application 82 | 83 | 84 | ``` 85 | ryu-manager ex8_arp_proxy.py 86 | ``` 87 | 88 | 3. run tcpdump in the nodes 89 | 90 | In mininet 91 | 92 | ``` 93 | xterm h1 94 | xterm h2 95 | ``` 96 | in the xterminal, 97 | ``` 98 | tcpdump -i any -v 99 | ``` 100 | 101 | 102 | 4. ping host1 to host2 from the mininet 103 | 104 | ``` 105 | h1 ping h2 106 | ``` 107 | 108 | 5. check the tcpdump output 109 | 110 | 111 | # References: 112 | 113 | https://github.com/osrg/ryu/blob/master/ryu/lib/packet/arp.py 114 | -------------------------------------------------------------------------------- /sdn_test_bed_setup.md: -------------------------------------------------------------------------------- 1 | How to setup SDN Test bed: 2 | ========================= 3 | 4 | 5 | # OS: Ubuntu 16.04.04 LTS 6 | 7 | As first step, please run this command. 8 | 9 | ``` 10 | sudo apt-get update 11 | sudo apt-get upgrade 12 | sudo apt-get install git gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev python-pip 13 | ``` 14 | 15 | 16 | At the time of writing, Ubuntu 16.04 official repository has the stable releases for the tools as, 17 | 18 | ``` 19 | Tool Name Version 20 | ************************* 21 | Openvswitch : 2.5.2 22 | Wireshark : 2.2.6 23 | IPERF : 2.0.5 24 | ``` 25 | 26 | Mininet will be installed from the script, and RYU will be installed using PIP. 27 | 28 | ``` 29 | Tool Name Version 30 | ************************* 31 | Mininet : 2.2.2 32 | RYU : 4.23 33 | ``` 34 | 35 | 36 | 1.Openvswitch Installation 37 | ----------------------- 38 | 39 | ``` 40 | sudo apt-get install openvswitch-switch 41 | ``` 42 | 43 | *To verify :* 44 | 45 | ``` 46 | ovs-vsctl --version 47 | ``` 48 | 49 | 2.Wireshark Installation 50 | ------------------------- 51 | 52 | 53 | ``` 54 | sudo apt-get install wireshark 55 | 56 | ``` 57 | 58 | *To verify :* 59 | 60 | ``` 61 | sudo wireshark & 62 | 63 | ``` 64 | 65 | 3.IPERF installation 66 | --------------------- 67 | 68 | 69 | ``` 70 | sudo apt-get install iperf 71 | ``` 72 | 73 | *To verify :* 74 | 75 | 76 | ``` 77 | iperf --version 78 | 79 | ``` 80 | 81 | 4.RYU installation 82 | ------------------ 83 | 84 | 85 | ``` 86 | sudo pip install ryu 87 | 88 | ``` 89 | 90 | *To verify :* 91 | 92 | 93 | ``` 94 | ryu-manager --version 95 | 96 | ``` 97 | 98 | 5.Mininet Installation 99 | ------------------------ 100 | 101 | The default mininet install option installs openvswitch, wireshark, pox, ryu, nox ,openflow reference implemenation, etc. we dont require all these packages now. 102 | So we specify the option(-n) to install only mininet. 103 | 104 | ``` 105 | git clone git://github.com/mininet/mininet 106 | cd mininet 107 | git tag 108 | git checkout 2.2.2 109 | cd .. 110 | mininet/util/install.sh --help 111 | mininet/util/install.sh -n 112 | 113 | ``` 114 | 115 | *To verify :* 116 | 117 | ``` 118 | sudo mn --version 119 | 120 | ``` 121 | 122 | 123 | Quick Verify: 124 | ============= 125 | 126 | Open 4 Terminals: 127 | 128 | 129 | 1. In Terminal1, 130 | 131 | ``` 132 | sudo wireshark & 133 | ``` 134 | And start the capture for any interface. 135 | 136 | 137 | 2. In Terminal2, 138 | 139 | ``` 140 | ryu-manager ryu.app.simple_switch_13 141 | 142 | ``` 143 | 144 | 3. In Terminal3, 145 | 146 | 147 | ``` 148 | sudo mn --controller=remote,ip=127.0.0.1 --mac --switch=ovsk,protocols=OpenFlow13 --topo=single,4 149 | pingall 150 | 151 | ``` 152 | 153 | 4. In Terminal 4, 154 | 155 | ``` 156 | sudo ovs-vsctl show 157 | 158 | sudo ovs-ofctl -O OpenFlow13 dump-flows s1 159 | ``` 160 | 161 | 5. check the openflow messages in wireshark 162 | 163 | 164 | References: 165 | ------------- 166 | https://osrg.github.io/ryu/ 167 | 168 | http://ryu.readthedocs.io/en/latest/getting_started.html 169 | 170 | http://mininet.org/ 171 | 172 | https://iperf.fr/ 173 | 174 | https://www.wireshark.org 175 | 176 | https://www.openvswitch.org/ 177 | -------------------------------------------------------------------------------- /wireshark_traces/Openflow13_GoodMsg.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knetsolutions/learn-sdn-with-ryu/404fc7a8626c8956992ef65ebc3a256df4f5c986/wireshark_traces/Openflow13_GoodMsg.pcapng --------------------------------------------------------------------------------