├── .gitignore
├── LICENSE
├── README.md
├── client
├── nc_socket.py
├── receiver.py
└── set_arp.sh
├── controller
├── config
│ ├── commands.txt
│ ├── commands_1.txt
│ ├── commands_2.txt
│ ├── commands_3.txt
│ ├── config.xml
│ ├── topo.txt
│ └── vring.txt
├── exe_cmd.py
├── fail_recovery.py
├── nc_config.py
├── p4_mininet.py
├── plot.py
├── run_test.py
└── topo.py
├── logs
└── .gitignore
└── p4src
├── includes
├── checksum.p4
├── defines.p4
├── headers.p4
└── parsers.p4
├── netchain.p4
└── routing.p4
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pcap
2 | *.bak
3 | *.pyc
4 |
--------------------------------------------------------------------------------
/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 |
203 | --------------------------------------------------------------------------------
204 |
205 | Code in python/ray/rllib/{evolution_strategies, dqn} adapted from
206 | https://github.com/openai (MIT License)
207 |
208 | Copyright (c) 2016 OpenAI (http://openai.com)
209 |
210 | Permission is hereby granted, free of charge, to any person obtaining a copy
211 | of this software and associated documentation files (the "Software"), to deal
212 | in the Software without restriction, including without limitation the rights
213 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
214 | copies of the Software, and to permit persons to whom the Software is
215 | furnished to do so, subject to the following conditions:
216 |
217 | The above copyright notice and this permission notice shall be included in
218 | all copies or substantial portions of the Software.
219 |
220 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
221 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
222 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
223 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
224 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
225 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
226 | THE SOFTWARE.
227 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NetChain
2 | ## 0. Introduction
3 |
4 | BMV2-based implementation of NetChain for paper ["NetChain: Scale-Free Sub-RTT Coordination"](https://www.usenix.org/conference/nsdi18/presentation/jin) published in NSDI 2018.
5 |
6 | ## 1. Obtain required software
7 | - Firstly, you need to get the p4 compiler from Github, and install required dependencies.
8 | > git clone https://github.com/p4lang/p4c-bm.git p4c-bmv2
9 | > cd p4c-bmv2
10 | > sudo pip install -r requirements.txt
11 | - Secondly, you need to get the behavior model of p4 from github, install dependencies and compile the behavior model.
12 | > git clone https://github.com/p4lang/behavioral-model.git bmv2
13 | > cd bmv2
14 | > install_deps.sh
15 | > ./autogen.sh
16 | > ./configure
17 | > make
18 | - Finally, you need to install some other tools which are used in this simulation.
19 | > sudo apt-get install mininet python-ipaddr
20 | > sudo pip install scapy thrift networkx
21 |
22 | ## 2. Content
23 | - client
24 | - client/nc_socket.py
25 | - client/receiver.py
26 | - clinet/set_arp.sh
27 | - controller
28 | - controller/config
29 | - controller/config/topo.txt
30 | - controller/config/vring.txt
31 | - controller/config/config.xml
32 | - controller/config/commands[_1,_2,_3]*.txt
33 | - controller/nc_config.py
34 | - controller/p4_mininet.py
35 | - controller/run_test.py
36 | - controller/exe_cmd.py
37 | - controller/fail_recovery.py
38 | - controller/plot.py
39 | - p4src
40 | - p4src/includes
41 | - p4src/includes/checksum.p4
42 | - p4src/includes/defines.p4
43 | - p4src/includes/headers.p4
44 | - p4src/includes/parsers.p4
45 | - p4src/netchain.p4
46 | - p4src/routing.p4
47 | - logs
48 | - .gitignore
49 | - README.md
50 |
51 | ## 3. How to run
52 | - Install required software and dependency for P4 (P4-14).
53 | - Make sure the directory informations are correct in
54 | > controller/config/config.xml.
55 | - Go to directory controller, you can simply run program in normal mode:
56 | > sudo python run_test.py normal.
57 | - Or you can run program in failure recovery mode:
58 | > sudo python run_test.py failure.
59 | - The program produces results for failure recovery at logs/. The format of the files is
60 | > [number of virtual groups].tmp_[read/write]_[send/receive].log.
61 | - You can draw figures by running:
62 | > python plot.py [number of virtual groups].
63 |
--------------------------------------------------------------------------------
/client/nc_socket.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import struct
3 | import time
4 | import thread
5 | import sys
6 | import os
7 | lib_path = os.path.abspath(os.path.join('../controller'))
8 | sys.path.append(lib_path)
9 | from nc_config import *
10 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
11 |
12 | counter = 0
13 | def counting():
14 | last_counter = 0
15 | while True:
16 | print (counter - last_counter), counter
17 | sys.stdout.flush()
18 | last_counter = counter
19 | time.sleep(1)
20 | thread.start_new_thread(counting, ())
21 |
22 | def get_chain_of_key(key, chains):
23 | for i in chains:
24 | if key in ENTRY[i]:
25 | return chains[i]
26 | return []
27 |
28 | def read_req(chains, num_kv, size_vgroup):
29 | global counter
30 | value = 0
31 | request_type = NC_READ_REQUEST
32 |
33 | op_field = struct.pack("B", request_type)
34 | sc = 4
35 | sc_field = struct.pack("B", sc)
36 | seq = 0
37 | seq_field = struct.pack("H", seq)
38 |
39 | index = 0
40 | while (1):
41 | key = ENTRY[1][index]
42 | value = 0
43 |
44 | vgroup = (key - 2018) / (num_kv/size_vgroup)
45 | vgroup_field = struct.pack(">H", vgroup)
46 |
47 | chain = get_chain_of_key(key, chains)
48 | ipx = [[] for i in range(len(chain))]
49 | for i in range(len(chain)):
50 | ipx[i] = [int(j) for j in chain[i].strip().split('.')]
51 |
52 | s0_field = struct.pack("BBBB", ipx[0][0], ipx[0][1], ipx[0][2], ipx[0][3])
53 |
54 | s1_field = struct.pack("BBBB", ipx[1][0], ipx[1][1], ipx[1][2], ipx[1][3])
55 |
56 | s2_field = struct.pack("BBBB", ipx[2][0], ipx[2][1], ipx[2][2], ipx[2][3])
57 |
58 | end_field = struct.pack("BBBB", 0,0,0,0)
59 |
60 | key_highBytes = key >> 64
61 | key_lowBytes = key ^ (key_highBytes << 64)
62 | key_field = struct.pack(">Q", key_highBytes) + struct.pack(">Q", key_lowBytes)
63 |
64 | value_highBytes = value >> 64
65 | value_lowBytes = value ^ (value_highBytes << 64)
66 | value_field = struct.pack(">Q", value_highBytes) + struct.pack(">Q", value_lowBytes)
67 |
68 | packet_to_send = s2_field + s1_field + s0_field + end_field + op_field + sc_field + seq_field + key_field + value_field + vgroup_field
69 |
70 | read_ip = chain[len(chain) - 1]
71 | #print "send out."
72 | s.sendto(packet_to_send, (read_ip, NC_PORT))
73 | counter = counter + 1
74 | time.sleep(0.01)
75 | index = (index + 1) % num_kv
76 | def write_req(chains, num_kv, size_vgroup):
77 | global counter
78 | request_type = NC_WRITE_REQUEST
79 | op_field = struct.pack("B", request_type)
80 | sc = 4
81 | sc_field = struct.pack("B", sc)
82 | seq = 0
83 | seq_field = struct.pack("H", seq)
84 |
85 | index = 0
86 | while (1):
87 | key = ENTRY[1][index]
88 | value = 222
89 |
90 | vgroup = (key - 2018) / (num_kv/size_vgroup)
91 | vgroup_field = struct.pack(">H", vgroup)
92 |
93 | chain = get_chain_of_key(key, chains)
94 | ipx = [[] for i in range(len(chain))]
95 | for i in range(len(chain)):
96 | ipx[i] = [int(j) for j in chain[i].strip().split('.')]
97 |
98 | s0_field = struct.pack("BBBB", ipx[0][0], ipx[0][1], ipx[0][2], ipx[0][3])
99 |
100 | s1_field = struct.pack("BBBB", ipx[1][0], ipx[1][1], ipx[1][2], ipx[1][3])
101 |
102 | s2_field = struct.pack("BBBB", ipx[2][0], ipx[2][1], ipx[2][2], ipx[2][3])
103 |
104 | end_field = struct.pack("BBBB", 0,0,0,0)
105 |
106 | key_highBytes = key >> 64
107 | key_lowBytes = key ^ (key_highBytes << 64)
108 | key_field = struct.pack(">Q", key_highBytes) + struct.pack(">Q", key_lowBytes)
109 |
110 | value_highBytes = value >> 64
111 | value_lowBytes = value ^ (value_highBytes << 64)
112 | value_field = struct.pack(">Q", value_highBytes) + struct.pack(">Q", value_lowBytes)
113 |
114 | packet = s0_field + s1_field + s2_field + end_field + op_field + sc_field + seq_field + key_field + value_field + vgroup_field
115 | write_ip = chain[0]
116 |
117 | s.sendto(packet, (write_ip, NC_PORT))
118 | counter = counter + 1
119 | time.sleep(0.01)
120 | index = (index + 1) % num_kv
121 |
122 | if __name__ == "__main__":
123 | op = sys.argv[1]
124 | if (op == "read"):
125 | vnode_num = int(sys.argv[2])
126 | vring_file = sys.argv[3]
127 | elif (op == "write"):
128 | vnode_num = int(sys.argv[2])
129 | vring_file = sys.argv[3]
130 | num_kv = int(sys.argv[4])
131 | size_vgroup = int(sys.argv[5])
132 | ## Set vring...
133 | chains = {}
134 | with open(vring_file, "r") as f:
135 | for i in range(vnode_num):
136 | line = f.readline().split()
137 | chains[int(line[0])] = line[1:]
138 |
139 | if (op == "read"):
140 | read_req(chains, num_kv, size_vgroup)
141 | elif (op == "write"):
142 | write_req(chains, num_kv, size_vgroup)
--------------------------------------------------------------------------------
/client/receiver.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import struct
3 | import time
4 | import thread
5 | import sys
6 | import os
7 | lib_path = os.path.abspath(os.path.join('../controller'))
8 | sys.path.append(lib_path)
9 | from nc_config import *
10 |
11 | rs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
12 | rs.bind((sys.argv[1], REPLY_PORT))
13 |
14 | counter = 0
15 | def counting():
16 | last_counter = 0
17 | while True:
18 | print (counter - last_counter), counter
19 | sys.stdout.flush()
20 | last_counter = counter
21 | time.sleep(1)
22 | thread.start_new_thread(counting, ())
23 |
24 | def main():
25 | global counter
26 | last_count = 0
27 | current_time = time.time()
28 | while (1):
29 | packet_received, addr = rs.recvfrom(2048)
30 | op_field = int(struct.unpack("B", packet_received[0])[0])
31 | counter = counter + 1
32 | continue
33 |
34 | if __name__ == "__main__":
35 | main()
--------------------------------------------------------------------------------
/client/set_arp.sh:
--------------------------------------------------------------------------------
1 | arp -s 10.0.100.3 aa:bb:cc:dd:ee:31
2 | arp -s 10.0.100.2 aa:bb:cc:dd:ee:21
3 | arp -s 10.0.100.1 aa:bb:cc:dd:ee:11
4 | arp -s 10.0.100.4 aa:bb:cc:dd:ee:41
5 | echo "ARP set."
--------------------------------------------------------------------------------
/controller/config/commands.txt:
--------------------------------------------------------------------------------
1 | table_add ipv4_route set_egress 10.0.0.1 => 1
2 | table_add ipv4_route set_egress 10.0.0.2 => 1
3 | table_add ipv4_route set_egress 10.0.100.2 => 2
4 | table_add ipv4_route set_egress 10.0.100.3 => 1
5 | table_add ipv4_route set_egress 10.0.100.4 => 1
6 | table_add ethernet_set_mac ethernet_set_mac_act 1 => aa:bb:cc:dd:ee:11 aa:bb:cc:dd:ee:43
7 | table_add ethernet_set_mac ethernet_set_mac_act 2 => aa:bb:cc:dd:ee:12 aa:bb:cc:dd:ee:22
8 | table_add gen_reply gen_reply_act 10 => 11
9 | table_add gen_reply gen_reply_act 12 => 13
10 | table_set_default get_sequence get_sequence_act
11 | table_set_default maintain_sequence maintain_sequence_act
12 | table_set_default read_value read_value_act
13 | table_set_default assign_value assign_value_act
14 | table_set_default pop_chain pop_chain_act
15 | table_set_default drop_packet drop_packet_act
16 | table_set_default get_next_hop get_next_hop_act
17 | table_set_default failure_recovery nop
--------------------------------------------------------------------------------
/controller/config/commands_1.txt:
--------------------------------------------------------------------------------
1 | table_add ipv4_route set_egress 10.0.0.1 => 1
2 | table_add ipv4_route set_egress 10.0.0.2 => 1
3 | table_add ipv4_route set_egress 10.0.100.1 => 2
4 | table_add ipv4_route set_egress 10.0.100.3 => 3
5 | table_add ipv4_route set_egress 10.0.100.4 => 1
6 | table_add ethernet_set_mac ethernet_set_mac_act 1 => aa:bb:cc:dd:ee:21 aa:bb:cc:dd:ee:44
7 | table_add ethernet_set_mac ethernet_set_mac_act 2 => aa:bb:cc:dd:ee:22 aa:bb:cc:dd:ee:12
8 | table_add ethernet_set_mac ethernet_set_mac_act 3 => aa:bb:cc:dd:ee:23 aa:bb:cc:dd:ee:32
9 | table_add gen_reply gen_reply_act 10 => 11
10 | table_add gen_reply gen_reply_act 12 => 13
11 | table_set_default get_sequence get_sequence_act
12 | table_set_default maintain_sequence maintain_sequence_act
13 | table_set_default read_value read_value_act
14 | table_set_default assign_value assign_value_act
15 | table_set_default pop_chain pop_chain_act
16 | table_set_default drop_packet drop_packet_act
17 | table_set_default get_next_hop get_next_hop_act
18 | table_set_default failure_recovery nop
--------------------------------------------------------------------------------
/controller/config/commands_2.txt:
--------------------------------------------------------------------------------
1 | table_add ipv4_route set_egress 10.0.0.1 => 1
2 | table_add ipv4_route set_egress 10.0.0.2 => 1
3 | table_add ipv4_route set_egress 10.0.100.1 => 1
4 | table_add ipv4_route set_egress 10.0.100.2 => 2
5 | table_add ipv4_route set_egress 10.0.100.4 => 1
6 | table_add ethernet_set_mac ethernet_set_mac_act 1 => aa:bb:cc:dd:ee:31 aa:bb:cc:dd:ee:45
7 | table_add ethernet_set_mac ethernet_set_mac_act 2 => aa:bb:cc:dd:ee:32 aa:bb:cc:dd:ee:23
8 | table_add gen_reply gen_reply_act 10 => 11
9 | table_add gen_reply gen_reply_act 12 => 13
10 | table_set_default get_sequence get_sequence_act
11 | table_set_default maintain_sequence maintain_sequence_act
12 | table_set_default read_value read_value_act
13 | table_set_default assign_value assign_value_act
14 | table_set_default pop_chain pop_chain_act
15 | table_set_default drop_packet drop_packet_act
16 | table_set_default get_next_hop get_next_hop_act
17 | table_set_default pop_chain_again pop_chain_act
18 | table_set_default failure_recovery nop
--------------------------------------------------------------------------------
/controller/config/commands_3.txt:
--------------------------------------------------------------------------------
1 | table_add ipv4_route set_egress 10.0.0.1 => 1
2 | table_add ipv4_route set_egress 10.0.0.2 => 2
3 | table_add ipv4_route set_egress 10.0.100.1 => 3
4 | table_add ipv4_route set_egress 10.0.100.2 => 4
5 | table_add ipv4_route set_egress 10.0.100.3 => 5
6 | table_add ipv4_route set_egress 10.0.100.4 => 4
7 | table_add ethernet_set_mac ethernet_set_mac_act 1 => aa:bb:cc:dd:ee:41 aa:bb:cc:dd:ee:01
8 | table_add ethernet_set_mac ethernet_set_mac_act 2 => aa:bb:cc:dd:ee:42 aa:bb:cc:dd:ee:02
9 | table_add ethernet_set_mac ethernet_set_mac_act 3 => aa:bb:cc:dd:ee:43 aa:bb:cc:dd:ee:11
10 | table_add ethernet_set_mac ethernet_set_mac_act 4 => aa:bb:cc:dd:ee:44 aa:bb:cc:dd:ee:21
11 | table_add ethernet_set_mac ethernet_set_mac_act 5 => aa:bb:cc:dd:ee:45 aa:bb:cc:dd:ee:31
12 | table_add gen_reply gen_reply_act 10 => 11
13 | table_add gen_reply gen_reply_act 12 => 13
14 | table_set_default get_sequence get_sequence_act
15 | table_set_default maintain_sequence maintain_sequence_act
16 | table_set_default read_value read_value_act
17 | table_set_default assign_value assign_value_act
18 | table_set_default pop_chain pop_chain_act
19 | table_set_default drop_packet drop_packet_act
20 | table_set_default get_next_hop get_next_hop_act
21 | table_set_default failure_recovery nop
--------------------------------------------------------------------------------
/controller/config/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | /home/user/zhuolong/p4/netchain
4 | /home/user/zhuolong/p4/bmv2
5 | /home/user/zhuolong/p4/p4c-bmv2
6 |
7 | controller/config/topo.txt
8 | controller/config/vring.txt
9 |
10 | 1
11 | 3
12 | 120
13 | 20
14 | 22222
15 | controller/config/switch.json
16 | p4src/netchain.p4
17 | targets/simple_switch/sswitch_CLI
18 | 10
19 |
--------------------------------------------------------------------------------
/controller/config/topo.txt:
--------------------------------------------------------------------------------
1 | switches 4
2 | hosts 2
3 | h1 s4
4 | h2 s4
5 | s1 s4
6 | s1 s2
7 | s2 s4
8 | s3 s4
9 | s2 s3
--------------------------------------------------------------------------------
/controller/config/vring.txt:
--------------------------------------------------------------------------------
1 | 1 10.0.100.1 10.0.100.2 10.0.100.3
2 | 2 10.0.100.1 10.0.100.4 10.0.100.4
--------------------------------------------------------------------------------
/controller/exe_cmd.py:
--------------------------------------------------------------------------------
1 | import subprocess, os
2 |
3 | def exe_cmd(cmd):
4 | subprocess.call(cmd, shell=True)
5 | return
6 |
7 | def send_cmd_to_port(parameters, cmd_to_send, dst_port, extra_cmd, interprete = False):
8 | file_to_send = "tmp_send_cmd.txt"
9 | fout = open(file_to_send, "w")
10 | fout.write(cmd_to_send)
11 | fout.close()
12 |
13 | st = "%s --thrift-port " % parameters.runtime_CLI + str(dst_port) + " < tmp_send_cmd.txt" + extra_cmd
14 | return_value = os.popen(st)
15 | return return_value
16 |
17 | def send_cmd_to_port_noreply(parameters, cmd_to_send, dst_port):
18 | file_to_send = "tmp_send_cmd_noreply.txt"
19 | fout = open(file_to_send, "w")
20 | fout.write(cmd_to_send)
21 | fout.close()
22 |
23 | cmd = [parameters.runtime_CLI, parameters.switch_json, str(dst_port)]
24 | with open(file_to_send, "r") as f:
25 | try:
26 | output = subprocess.check_output(cmd, stdin = f)
27 | except subprocess.CalledProcessError as e:
28 | print e
29 | print e.output
--------------------------------------------------------------------------------
/controller/fail_recovery.py:
--------------------------------------------------------------------------------
1 | from nc_config import *
2 | from exe_cmd import *
3 |
4 | def failover(parameters, fail_switch_id, chains):
5 | fail_ip = IP_PREFIX + str(fail_switch_id + 1)
6 | fail_sw_port = THRIFT_PORT_OF_SWITCH[fail_switch_id]
7 | for i in chains:
8 | chain = chains[i]
9 | pos = chain.index(fail_ip)
10 | if (pos == 0): ## if the failed switch is a head switch in the chain, get a new head
11 | newhead_ip = chain[pos + 1]
12 | newhead_sw_id = int(newhead_ip.strip().split('.')[3]) - 1
13 | newhead_sw_port = THRIFT_PORT_OF_SWITCH[newhead_sw_id]
14 |
15 | table_dump_getAddress_cmd = "table_dump get_my_address"
16 | extra_process_cmd_1 = " | grep 'Action entry' | awk -F ['-']+ '{print $2}'"
17 | action_pm = send_cmd_to_port(parameters, table_dump_getAddress_cmd, fail_sw_port, extra_process_cmd_1)
18 |
19 | extra_process_cmd_2 = " | grep 'Dumping entry' | awk -F [' ']+ '{print $3}'"
20 | table_entry = send_cmd_to_port(parameters, table_dump_getAddress_cmd, fail_sw_port, extra_process_cmd_2)
21 |
22 | extra_process_cmd_3 = " | grep 'nc_hdr' | awk -F [' ']+ '{print $5}'"
23 | match_key = send_cmd_to_port(parameters, table_dump_getAddress_cmd, fail_sw_port, extra_process_cmd_3)
24 |
25 | for j in range(len(match_key)):
26 | if match_key[j] in ENTRY[i]:
27 |
28 | table_modify_getAddress_cmd = "table_modify get_my_address get_my_address_act " + str(int(table_entry[j].strip, 16)) + " " + newhead_ip + " " + str(HEAD_NODE)
29 | extra_process_cmd = ""
30 | send_cmd_to_port(parameters, table_modify_getAddress_cmd, newhead_sw_port, extra_process_cmd)
31 | elif (pos == len(chain) - 1): ## if the failed switch is a tail switch in the chain
32 | neighbor_ip = chain[pos - 1]
33 | neighbor_sw_id = int(neighbor_ip.strip().split('.')[3]) - 1
34 | neighbor_sw_port = THRIFT_PORT_OF_SWITCH[neighbor_sw_id]
35 |
36 | table_add_failover_cmd = "table_add failure_recovery failover_write_reply_act %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 0&&&0 => 3" % fail_ip
37 | extra_process_cmd = ""
38 | send_cmd_to_port_noreply(parameters, table_add_failover_cmd, neighbor_sw_port)
39 | else: ## if the failed switch is a middle switch in the chain
40 | neighbor_ip = chain[pos - 1]
41 | neighbor_sw_id = int(neighbor_ip.strip().split('.')[3]) - 1
42 | neighbor_sw_port = THRIFT_PORT_OF_SWITCH[neighbor_sw_id]
43 |
44 | table_add_failover_cmd = "table_add failure_recovery failover_act %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 0&&&0 => 3" % fail_ip
45 | extra_process_cmd = ""
46 | send_cmd_to_port_noreply(parameters, table_add_failover_cmd, neighbor_sw_port)
47 |
48 | return
49 |
50 | def disable_read_write(parameters, neighbor_sw_port, fail_ip, v_group_id):
51 | table_add_failrecovery_cmd = "table_add failure_recovery drop_packet_act %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 %d&&&255 => 2" % (fail_ip, v_group_id)
52 | extra_process_cmd = ""
53 | send_cmd_to_port_noreply(parameters, table_add_failrecovery_cmd, neighbor_sw_port)
54 | return
55 |
56 |
57 | def enable_read_write(parameters, neighbor_sw_port, fail_ip, backup_ip, v_group_id):
58 |
59 | table_add_failrecovery_cmd = "table_add failure_recovery failure_recovery_act %s&&&255.255.255.255 0.0.0.0&&&0.0.0.0 %d&&&255 => %s 1" % (fail_ip, v_group_id, backup_ip)
60 | extra_process_cmd = ""
61 | send_cmd_to_port_noreply(parameters, table_add_failrecovery_cmd, neighbor_sw_port)
62 | return
63 |
64 | def failure_recovery(parameters, fail_switch_id, chains):
65 | fail_ip = IP_PREFIX + str(fail_switch_id + 1)
66 | backup_sw_id = BACKUP_SW_ID
67 | backup_ip = IP_PREFIX + str(backup_sw_id + 1)
68 | backup_sw_port = THRIFT_PORT_OF_SWITCH[backup_sw_id]
69 |
70 | vgroup = [[] for v_group_id in range(parameters.size_vgroup)]
71 | s_key = 2018
72 | for v_group_id in range(parameters.size_vgroup):
73 | count_key = 0
74 | while count_key " + backup_ip + " " + str(role)
123 | vgroup_cmd = vgroup_cmd + table_add_getAddress_cmd + "\n"
124 |
125 |
126 | # copy table find_index
127 | tmp_index_group = []
128 | for j in range(len(match_key_findindex)):
129 | match_key_j = int(match_key_findindex[j], 16)
130 | action_j = int(action_pm_findindex[j], 16)
131 |
132 | if (match_key_j in vgroup[v_group_id]):
133 | tmp_index_group.append(action_j)
134 | table_add_findindex_cmd = "table_add find_index find_index_act " + str(match_key_j) + " => " + str(action_j)
135 | vgroup_cmd = vgroup_cmd + table_add_findindex_cmd + "\n"
136 | # copy sequence register
137 | for j in tmp_index_group:
138 | register_read_sequence_cmd = "register_read sequence_reg " + str(j)
139 | extra_process_cmd = " | grep 'sequence_reg' | awk -F [' ']+ '{print $3}'"
140 | register_sequence = send_cmd_to_port(parameters, register_read_sequence_cmd, target_sw_port, extra_process_cmd).readlines()
141 | if (register_sequence):
142 | register_write_sequence_cmd = "register_write sequence_reg " + str(j) + " " + register_sequence[0].strip('\n')
143 | vgroup_cmd = vgroup_cmd + register_write_sequence_cmd + "\n"
144 | #copy value register
145 | for j in tmp_index_group:
146 | register_read_value_cmd = "register_read value_reg " + str(j)
147 | extra_process_cmd = " | grep 'value_reg' | awk -F [' ']+ '{print $3}'"
148 | register_value = send_cmd_to_port(parameters, register_read_value_cmd, target_sw_port, extra_process_cmd).readlines()
149 | if (register_value):
150 | register_write_value_cmd = "register_write value_reg " + str(j) + " " + register_value[0].strip('\n')
151 | vgroup_cmd = vgroup_cmd + register_write_value_cmd + "\n"
152 | send_cmd_to_port_noreply(parameters, vgroup_cmd, backup_sw_port)
153 | enable_read_write(parameters, neighbor_sw_port, fail_ip, backup_ip, v_group_id)
154 | return
155 |
--------------------------------------------------------------------------------
/controller/nc_config.py:
--------------------------------------------------------------------------------
1 | NC_READ_REQUEST = 10
2 | NC_READ_REPLY = 11
3 | NC_WRITE_REQUEST = 12
4 | NC_WRITE_REPLY = 13
5 | NC_PORT = 8888
6 | REPLY_PORT = 8889
7 | HEAD_NODE = 100
8 | REPLICA_NODE = 101
9 | TAIL_NODE = 102
10 | TENSECONDS = 10
11 | IP_PREFIX = "10.0.100."
12 | BACKUP_SW_ID = 3
13 | FAIL_SW_ID = 1
14 | ENTRY = {1: [(2018+i) for i in range(200)]}
15 | THRIFT_PORT_OF_SWITCH = [22222, 22223, 22224, 22225]
--------------------------------------------------------------------------------
/controller/p4_mininet.py:
--------------------------------------------------------------------------------
1 | # Copyright 2013-present Barefoot Networks, Inc.
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 implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | from mininet.net import Mininet
17 | from mininet.node import Switch, Host
18 | from mininet.log import setLogLevel, info, error, debug
19 | from mininet.moduledeps import pathCheck
20 | from sys import exit
21 | import os
22 | import tempfile
23 | import socket
24 |
25 | class P4Host(Host):
26 | def config(self, **params):
27 | r = super(Host, self).config(**params)
28 |
29 | self.defaultIntf().rename("eth0")
30 |
31 | for off in ["rx", "tx", "sg"]:
32 | cmd = "/sbin/ethtool --offload eth0 %s off" % off
33 | self.cmd(cmd)
34 |
35 | # disable IPv6
36 | self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
37 | self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
38 | self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
39 |
40 | return r
41 |
42 | def describe(self):
43 | print "**********"
44 | print self.name
45 | print "default interface: %s\t%s\t%s" %(
46 | self.defaultIntf().name,
47 | self.defaultIntf().IP(),
48 | self.defaultIntf().MAC()
49 | )
50 | print "**********"
51 |
52 | class P4Switch(Switch):
53 | """P4 virtual switch"""
54 | device_id = 0
55 |
56 | def __init__(self, name, sw_path = None, json_path = None,
57 | thrift_port = None,
58 | pcap_dump = False,
59 | log_console = True,
60 | verbose = False,
61 | device_id = None,
62 | enable_debugger = False,
63 | **kwargs):
64 | Switch.__init__(self, name, **kwargs)
65 | assert(sw_path)
66 | assert(json_path)
67 | # make sure that the provided sw_path is valid
68 | pathCheck(sw_path)
69 | # make sure that the provided JSON file exists
70 | if not os.path.isfile(json_path):
71 | error("Invalid JSON file.\n")
72 | exit(1)
73 | self.sw_path = sw_path
74 | self.json_path = json_path
75 | self.verbose = verbose
76 | logfile = "/tmp/p4s.{}.log".format(self.name)
77 | self.output = open(logfile, 'w')
78 | self.thrift_port = thrift_port
79 | self.pcap_dump = pcap_dump
80 | self.enable_debugger = enable_debugger
81 | self.log_console = log_console
82 | if device_id is not None:
83 | self.device_id = device_id
84 | P4Switch.device_id = max(P4Switch.device_id, device_id)
85 | else:
86 | self.device_id = P4Switch.device_id
87 | P4Switch.device_id += 1
88 | self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id)
89 |
90 | @classmethod
91 | def setup(cls):
92 | pass
93 |
94 | def check_switch_started(self, pid):
95 | """While the process is running (pid exists), we check if the Thrift
96 | server has been started. If the Thrift server is ready, we assume that
97 | the switch was started successfully. This is only reliable if the Thrift
98 | server is started at the end of the init process"""
99 | while True:
100 | if not os.path.exists(os.path.join("/proc", str(pid))):
101 | return False
102 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
103 | sock.settimeout(0.5)
104 | result = sock.connect_ex(("localhost", self.thrift_port))
105 | if result == 0:
106 | return True
107 |
108 | def start(self, controllers):
109 | "Start up a new P4 switch"
110 | info("Starting P4 switch {}.\n".format(self.name))
111 | args = [self.sw_path]
112 | for port, intf in self.intfs.items():
113 | if not intf.IP():
114 | args.extend(['-i', str(port) + "@" + intf.name])
115 | if self.pcap_dump:
116 | args.append("--pcap")
117 | # args.append("--useFiles")
118 | if self.thrift_port:
119 | args.extend(['--thrift-port', str(self.thrift_port)])
120 | if self.nanomsg:
121 | args.extend(['--nanolog', self.nanomsg])
122 | args.extend(['--device-id', str(self.device_id)])
123 | P4Switch.device_id += 1
124 | args.append(self.json_path)
125 | if self.enable_debugger:
126 | args.append("--debugger")
127 |
128 | logfile = "p4s.{}.log".format(self.name)
129 | if self.log_console:
130 | #args.append("--log-console")
131 | args.append("--log-file "+logfile)
132 | info(' '.join(args) + "\n")
133 |
134 | pid = None
135 | with tempfile.NamedTemporaryFile() as f:
136 | # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &')
137 | self.cmd(' '.join(args) + ' 2>&1 & echo $! >> ' + f.name)
138 | pid = int(f.read())
139 | debug("P4 switch {} PID is {}.\n".format(self.name, pid))
140 | if not self.check_switch_started(pid):
141 | error("P4 switch {} did not start correctly.\n".format(self.name))
142 | exit(1)
143 | info("P4 switch {} has been started.\n".format(self.name))
144 |
145 | def stop(self):
146 | "Terminate P4 switch."
147 | self.output.flush()
148 | self.cmd('kill %' + self.sw_path)
149 | self.cmd('wait')
150 | self.deleteIntfs()
151 |
152 | def attach(self, intf):
153 | "Connect a data port"
154 | assert(0)
155 |
156 | def detach(self, intf):
157 | "Disconnect a data port"
158 | assert(0)
159 |
--------------------------------------------------------------------------------
/controller/plot.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 |
5 | def draw(num_vgroup, plot_id):
6 | plt.figure(plot_id)
7 | plt.ylim(0.0, 210.0)
8 |
9 | fin = open("../logs/"+num_vgroup+".tmp_write_receive.log", "r")
10 | count = 0
11 | y = []
12 | for line in fin.readlines():
13 | s_line = line.strip('\n').split()
14 | count = count + 1
15 | y.append(int(s_line[0]))
16 | fin.close
17 | fin = open("../logs/"+num_vgroup+".tmp_read_receive.log", "r")
18 | count = 0
19 | for line in fin.readlines():
20 | s_line = line.strip('\n').split()
21 | if (count < len(y)):
22 | y[count] = y[count] + int(s_line[0])
23 | count = count + 1
24 |
25 |
26 | x = range(1,len(y)-1)
27 | y = y[1:len(y)-1]
28 | print x
29 | print y
30 | plt.plot(x,y)
31 | plt.title(str(num_vgroup)+" Virtual Group")
32 | plt.xlabel('Time (s)')
33 | plt.ylabel('Throughput (QPS)')
34 |
35 | def usage():
36 | print "Usage:"
37 | print " python plot.py [number of virtual groups] (e.g. python plot.py 20)"
38 | return
39 |
40 | if __name__ == "__main__":
41 | if (len(sys.argv) != 2):
42 | usage()
43 | quit()
44 | draw(sys.argv[1], 1)
45 | plt.show()
--------------------------------------------------------------------------------
/controller/run_test.py:
--------------------------------------------------------------------------------
1 | import os, logging, sys, subprocess, argparse, time
2 | import xml.etree.ElementTree as xmlparse
3 | from nc_config import *
4 | from exe_cmd import *
5 | from topo import *
6 | from fail_recovery import *
7 |
8 |
9 | ###########################################
10 | ## get parameters
11 | ###########################################
12 | class Parameters:
13 | def __init__(self, config_file):
14 | root_dir = xmlparse.parse(config_file).getroot()
15 | self.config_file = config_file
16 | self.project_dir = root_dir.find("projectDir").text
17 | self.topology_file = self.project_dir + "/" + root_dir.find("topology").find("topologyFileName").text
18 | self.thrift_base_port = int(root_dir.find("thriftBasePort").text)
19 | self.bmv2 = root_dir.find("bmv2").text
20 | self.p4c_bmv2 = root_dir.find("p4cBmv2").text
21 | self.switch_json = self.project_dir + "/" + root_dir.find("switchJson").text
22 | self.num_vnode = int(root_dir.find("numVirtualNode").text)
23 | self.num_replica = int(root_dir.find("numReplica").text)
24 | self.num_kv = int(root_dir.find("numKeyValue").text)
25 | self.switch_p4 = self.project_dir + "/" + root_dir.find("p4src").text
26 | self.runtime_CLI = root_dir.find("bmv2").text + "/" + root_dir.find("runtimeCLI").text
27 | self.vring_file = self.project_dir + "/" + root_dir.find("topology").find("vringFileName").text
28 | self.register_size = int(root_dir.find("registerSize").text)
29 | self.size_vgroup = int(root_dir.find("sizeVirtualGroup").text)
30 |
31 | def compile_p4_switch(parameters):
32 | logging.info("Generate switch json...")
33 | exe_cmd("%s/p4c_bm/__main__.py %s --json %s" % (parameters.p4c_bmv2, parameters.switch_p4, parameters.switch_json))
34 | return
35 |
36 | def compile_all(parameters):
37 | compile_p4_switch(parameters)
38 | return
39 |
40 |
41 | def run(parameters):
42 | compile_all(parameters)
43 | logging.info("Warm up...")
44 | exe_cmd("%s/targets/simple_switch/simple_switch > /dev/null 2>&1" % parameters.bmv2)
45 | exe_cmd("mkdir -p %s/logs/switches" % parameters.project_dir)
46 |
47 | logging.info("Start mininet...")
48 | (switches, hosts, net) = config_mininet(parameters)
49 |
50 | logging.info("Get chain informations...")
51 | chains = {}
52 | with open(parameters.vring_file, "r") as f:
53 | for i in range(parameters.num_vnode):
54 | line = f.readline().split()
55 | chains[int(line[0])] = line[1:]
56 |
57 | return (switches, hosts, net, chains)
58 |
59 | def clean():
60 | print "Clean environment..."
61 | exe_cmd("ps -ef | grep nc_socket.py | grep -v grep | awk '{print $2}' | xargs kill -9")
62 | exe_cmd("ps -ef | grep NetKVController.jar | grep -v grep | awk '{print $2}' | xargs kill -9")
63 | exe_cmd("ps -ef | grep tcpdump | grep -v grep | awk '{print $2}' | xargs kill -9")
64 | exe_cmd("ps -ef | grep dist_txn | grep -v grep | awk '{print $2}' | xargs kill -9")
65 | exe_cmd("rm -f *.pcap")
66 | exe_cmd("rm -f *.out *.pyc")
67 | exe_cmd("rm -f *.log.txt *.log.*.txt")
68 | exe_cmd("rm -f tmp_send_cmd_noreply.txt tmp_send_cmd.txt")
69 | exe_cmd("killall lt-simple_switch >/dev/null 2>&1")
70 | exe_cmd("mn -c >/dev/null 2>&1")
71 | #exe_cmd("ps -ef | grep run.py | grep -v grep | awk '{print $2}' | xargs kill -9")
72 | exe_cmd("killall -9 redis-server > /dev/null 2>&1")
73 | exe_cmd("killall -9 redis_proxy > /dev/null 2>&1")
74 | exe_cmd("killall -9 cr_backend > /dev/null 2>&1")
75 |
76 | def init_flowtable(parameters):
77 | role = [100, 101, 102]
78 | for switch_id in range(3):
79 | switch_ip = IP_PREFIX + str(switch_id + 1)
80 | switch_port = THRIFT_PORT_OF_SWITCH[switch_id]
81 | logging.info(switch_port)
82 | init_cmd = ""
83 | for i in range(parameters.num_kv):
84 | key = ENTRY[1][i]
85 | table_add_getAddress_cmd = "table_add get_my_address get_my_address_act " + str(key) + " => " + switch_ip + " " + str(role[switch_id])
86 |
87 | table_add_findindex_cmd = "table_add find_index find_index_act " + str(key) + " => " + str(i)
88 |
89 | register_write_value_cmd = "register_write value_reg " + str(i) + " " + str(i)
90 | init_cmd = init_cmd + table_add_getAddress_cmd + "\n" + table_add_findindex_cmd + "\n" + register_write_value_cmd + "\n"
91 | send_cmd_to_port_noreply(parameters, init_cmd, switch_port)
92 |
93 |
94 | def send_traffic(parameters, switches, hosts, net):
95 | read_host_id = 0
96 | read_host = net.get('h%d' % (read_host_id + 1))
97 | read_host.sendCmd("sh %s/client/set_arp.sh" % (parameters.project_dir))
98 | print read_host.waitOutput()
99 | read_host.sendCmd("python %s/client/receiver.py 10.0.0.1 > %s/logs/%d.tmp_read_receive.log & python %s/client/nc_socket.py read %d %s %d %d > %s/logs/tmp_read_send.log &"
100 | % (parameters.project_dir, parameters.project_dir, parameters.size_vgroup, parameters.project_dir, parameters.num_vnode, parameters.vring_file, parameters.num_kv, parameters.size_vgroup, parameters.project_dir))
101 | write_host_id = 1
102 | write_host = net.get('h%d' % (write_host_id + 1))
103 | write_host.sendCmd("sh %s/client/set_arp.sh" % (parameters.project_dir))
104 | print write_host.waitOutput()
105 | write_host.sendCmd("python %s/client/receiver.py 10.0.0.2 > %s/logs/%d.tmp_write_receive.log & python %s/client/nc_socket.py write %d %s %d %d > %s/logs/tmp_write_send.log &"
106 | % (parameters.project_dir, parameters.project_dir, parameters.size_vgroup, parameters.project_dir, parameters.num_vnode, parameters.vring_file, parameters.num_kv, parameters.size_vgroup, parameters.project_dir))
107 |
108 | return (read_host,write_host)
109 |
110 | def stop_switch(fail_switch_id, switches, hosts, net):
111 | fail_switch = net.get('s%d' % (fail_switch_id + 1))
112 | fail_switch.stop()
113 | return
114 |
115 |
116 | ###########################################
117 | ## run test in normal case
118 | ###########################################
119 | def test_normal(parameters):
120 | (switches, hosts, net, chains) = run(parameters)
121 | init_flowtable(parameters)
122 | logging.info("Run for 60 seconds...")
123 | time.sleep(60)
124 | net.stop()
125 | clean()
126 | return
127 |
128 | ###########################################
129 | ## run test in failure case
130 | ###########################################
131 | def test_failure(parameters, fail_switch_id):
132 | ### install initial rules
133 | (switches, hosts, net, chains) = run(parameters)
134 | init_flowtable(parameters)
135 | ### start sending traffic on host0
136 | logging.info("Sending traffic...")
137 | (read_host, write_host) = send_traffic(parameters, switches, hosts, net)
138 |
139 | ### wait for 10 seconds
140 | logging.info("Wait for 10 seconds...")
141 | time.sleep(TENSECONDS)
142 |
143 | ### stop one switch
144 | logging.info("Stop a switch...")
145 | stop_switch(fail_switch_id, switches, hosts, net)
146 |
147 | ### wait for 10 seconds
148 | logging.info("Assume the failure is discovered in 0.5s...")
149 | time.sleep(0.5)
150 |
151 | ### update rules for fast failover
152 | logging.info("Start failover...")
153 | failover(parameters, fail_switch_id, chains)
154 |
155 | ### wait for 10 seconds
156 | logging.info("Wait for 10 seconds...")
157 | time.sleep(TENSECONDS)
158 |
159 | ### update rules for failure recovery
160 | logging.info("Start failrecovering...")
161 | failure_recovery(parameters, fail_switch_id, chains)
162 |
163 | ### wait for 10 seconds
164 | logging.info("Wait for 10 seconds...")
165 | time.sleep(TENSECONDS)
166 |
167 | ### clean environment
168 | read_host.monitor()
169 | write_host.monitor()
170 | net.stop()
171 | clean()
172 | return
173 |
174 | def usage():
175 | print "Usage:"
176 | print " To run test in normal case: python run_test.py normal"
177 | print " To run test in failure case: python run_test.py failure"
178 | return
179 |
180 | if __name__ == "__main__":
181 | exe_cmd("rm -f *.log.txt")
182 | logging.basicConfig(level=logging.INFO)
183 | if (len(sys.argv) != 2):
184 | usage()
185 | quit()
186 | config_file = "config/config.xml"
187 | parameters = Parameters(config_file)
188 | if sys.argv[1] == "normal":
189 | test_normal(parameters)
190 | elif sys.argv[1] == "failure":
191 | test_failure(parameters, 1)
192 | else:
193 | usage()
194 | quit()
195 |
--------------------------------------------------------------------------------
/controller/topo.py:
--------------------------------------------------------------------------------
1 | import os, logging, sys, subprocess, argparse, time
2 |
3 | from mininet.net import Mininet
4 | from mininet.topo import Topo
5 | from mininet.log import setLogLevel, info
6 | from mininet.cli import CLI
7 | from mininet.node import CPULimitedHost
8 | from mininet.link import TCLink
9 |
10 | from p4_mininet import P4Switch, P4Host
11 | from nc_config import *
12 | from exe_cmd import *
13 |
14 |
15 | ###########################################
16 | ## get parameters
17 | ###########################################
18 | class MyTopo(Topo):
19 | def __init__(self, sw_path, json_path, switches, thrift_base_port,
20 | pcap_dump_flag, log_dir, hosts, links, **opts):
21 | Topo.__init__(self, **opts)
22 |
23 | for i in xrange(switches):
24 | self.addSwitch("s%d" % (i+1),
25 | sw_path = sw_path,
26 | json_path = json_path,
27 | thrift_port = thrift_base_port+i,
28 | pcap_dump = pcap_dump_flag,
29 | device_id = i,
30 | verbose = True,
31 | log_dir = log_dir)
32 |
33 | for i in xrange(hosts):
34 | self.addHost("h%d" % (i+1))
35 |
36 | for a,b in links:
37 | self.addLink(a, b)
38 |
39 | def read_topo(topology_file):
40 | nb_hosts = 0
41 | nb_switches = 0
42 | links = []
43 | with open(topology_file, "r") as f:
44 | line = f.readline()[:-1]
45 | w, nb_switches = line.split()
46 | assert(w == "switches")
47 | line = f.readline()[:-1]
48 | w, nb_hosts = line.split()
49 | assert(w == "hosts")
50 | for line in f:
51 | if not f: break
52 | a, b = line.split()
53 | links.append( (a, b) )
54 | return int(nb_switches), int(nb_hosts), links
55 |
56 | def config_mininet(parameters):
57 | switches, hosts, links = read_topo(parameters.topology_file)
58 | topo = MyTopo("%s/targets/simple_switch/simple_switch" % parameters.bmv2,
59 | parameters.switch_json,
60 | switches,
61 | parameters.thrift_base_port,
62 | False,
63 | parameters.project_dir + '/logs/switches',
64 | hosts,
65 | links)
66 |
67 | net = Mininet(topo = topo,
68 | host = P4Host,
69 | switch = P4Switch,
70 | controller = None,
71 | autoStaticArp=True )
72 |
73 | net.start()
74 |
75 | for n in range(hosts):
76 | h = net.get('h%d' % (n + 1))
77 | for off in ["rx", "tx", "sg"]:
78 | cmd = "/sbin/ethtool --offload eth0 %s off" % off
79 | print cmd
80 | h.cmd(cmd)
81 | print "disable ipv6"
82 | h.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
83 | h.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
84 | h.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
85 | h.cmd("sysctl -w net.ipv4.tcp_congestion_control=reno")
86 | h.cmd("iptables -I OUTPUT -p icmp --icmp-type destination-unreachable -j DROP")
87 | h.setIP("10.0.0.%d" % (n + 1))
88 | h.setMAC("aa:bb:cc:dd:ee:0%d" % (n + 1))
89 | for i in range(hosts):
90 | if (i != n):
91 | h.setARP("10.0.0.%d" % (i + 1), "aa:bb:cc:dd:ee:0%d" % (i + 1))
92 | net.get('s1').setMAC("aa:bb:cc:dd:ee:11","s1-eth1")
93 | net.get('s1').setMAC("aa:bb:cc:dd:ee:12","s1-eth2")
94 |
95 | net.get('s2').setMAC("aa:bb:cc:dd:ee:21","s2-eth1")
96 | net.get('s2').setMAC("aa:bb:cc:dd:ee:22","s2-eth2")
97 | net.get('s2').setMAC("aa:bb:cc:dd:ee:23","s2-eth3")
98 |
99 | net.get('s3').setMAC("aa:bb:cc:dd:ee:31","s3-eth1")
100 | net.get('s3').setMAC("aa:bb:cc:dd:ee:32","s3-eth2")
101 |
102 | net.get('s4').setMAC("aa:bb:cc:dd:ee:41","s4-eth1")
103 | net.get('s4').setMAC("aa:bb:cc:dd:ee:42","s4-eth2")
104 | net.get('s4').setMAC("aa:bb:cc:dd:ee:43","s4-eth3")
105 | net.get('s4').setMAC("aa:bb:cc:dd:ee:44","s4-eth4")
106 | net.get('s4').setMAC("aa:bb:cc:dd:ee:45","s4-eth5")
107 |
108 | time.sleep(1)
109 | commands_list = ["config/commands.txt", "config/commands_1.txt", "config/commands_2.txt", "config/commands_3.txt"]
110 | file_index = 0
111 | for i in range(switches):
112 | cmd = [parameters.runtime_CLI, parameters.switch_json, str(parameters.thrift_base_port + i)]
113 | with open(commands_list[file_index], "r") as f:
114 | file_index = file_index + 1
115 | print " ".join(cmd)
116 | try:
117 | output = subprocess.check_output(cmd, stdin = f)
118 | print output
119 | except subprocess.CalledProcessError as e:
120 | print e
121 | print e.output
122 |
123 | time.sleep(1)
124 |
125 | logging.info("Ready !")
126 |
127 | return (switches, hosts, net)
128 |
--------------------------------------------------------------------------------
/logs/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/p4src/includes/checksum.p4:
--------------------------------------------------------------------------------
1 | field_list ipv4_field_list {
2 | ipv4.version;
3 | ipv4.ihl;
4 | ipv4.diffserv;
5 | ipv4.totalLen;
6 | ipv4.identification;
7 | ipv4.flags;
8 | ipv4.fragOffset;
9 | ipv4.ttl;
10 | ipv4.protocol;
11 | ipv4.srcAddr;
12 | ipv4.dstAddr;
13 | }
14 | field_list_calculation ipv4_chksum_calc {
15 | input {
16 | ipv4_field_list;
17 | }
18 | algorithm : csum16;
19 | output_width: 16;
20 | }
21 | calculated_field ipv4.hdrChecksum {
22 | update ipv4_chksum_calc;
23 | }
24 |
25 | field_list udp_checksum_list {
26 | // IPv4 Pseudo Header Format. Must modify for IPv6 support.
27 | ipv4.srcAddr;
28 | ipv4.dstAddr;
29 | 8'0;
30 | ipv4.protocol;
31 | udp.len;
32 | udp.srcPort;
33 | udp.dstPort;
34 | udp.len;
35 | // udp.checksum;
36 | payload;
37 | }
38 | field_list_calculation udp_checksum {
39 | input {
40 | udp_checksum_list;
41 | }
42 | algorithm : csum16;
43 | output_width : 16;
44 | }
45 | calculated_field udp.checksum {
46 | update udp_checksum;
47 | }
48 |
--------------------------------------------------------------------------------
/p4src/includes/defines.p4:
--------------------------------------------------------------------------------
1 | #define SEQUENCE_REG_SIZE 4096
2 | #define VALUE_REG_SIZE 4096
3 |
4 | #define NUM_CACHE 128
5 | #define NC_PORT 8888
6 | #define REPLY_PORT 8889
7 | #define NC_READ_REQUEST 10
8 | #define NC_READ_REPLY 11
9 | #define NC_WRITE_REQUEST 12
10 | #define NC_WRITE_REPLY 13
11 | #define DROP_PORT 9999
12 | #define END_OF_CHAIN 0
13 | #define MAX_LENGTH_OF_CHAIN 10
14 | #define HEAD_NODE 100
15 | #define REPLICA_NODE 101
16 | #define TAIL_NODE 102
17 | #define DROPPED 100
18 |
--------------------------------------------------------------------------------
/p4src/includes/headers.p4:
--------------------------------------------------------------------------------
1 | header_type ethernet_t {
2 | fields {
3 | dstAddr : 48;
4 | srcAddr : 48;
5 | etherType : 16;
6 | }
7 | }
8 | header ethernet_t ethernet;
9 |
10 | header_type ipv4_t {
11 | fields {
12 | version : 4;
13 | ihl : 4;
14 | diffserv : 8;
15 | totalLen : 16;
16 | identification : 16;
17 | flags : 3;
18 | fragOffset : 13;
19 | ttl : 8;
20 | protocol : 8;
21 | hdrChecksum : 16;
22 | srcAddr : 32;
23 | dstAddr: 32;
24 | }
25 | }
26 | header ipv4_t ipv4;
27 |
28 | header_type tcp_t {
29 | fields {
30 | srcPort : 16;
31 | dstPort : 16;
32 | seqNo : 32;
33 | ackNo : 32;
34 | dataOffset : 4;
35 | res : 3;
36 | ecn : 3;
37 | ctrl : 6;
38 | window : 16;
39 | checksum : 16;
40 | urgentPtr : 16;
41 | }
42 | }
43 | header tcp_t tcp;
44 |
45 | header_type udp_t {
46 | fields {
47 | srcPort : 16;
48 | dstPort : 16;
49 | len : 16;
50 | checksum : 16;
51 | }
52 | }
53 | header udp_t udp;
54 |
55 | header_type overlay_t {
56 | fields {
57 | swip: 32;
58 | }
59 | }
60 | header overlay_t overlay [MAX_LENGTH_OF_CHAIN];
61 |
62 | header_type nc_hdr_t {
63 | fields {
64 | op: 8;
65 | sc: 8;
66 | seq: 16;
67 | key: 128;
68 | value: 128;
69 | vgroup: 16;
70 | }
71 |
72 | }
73 | header nc_hdr_t nc_hdr;
--------------------------------------------------------------------------------
/p4src/includes/parsers.p4:
--------------------------------------------------------------------------------
1 | parser start {
2 | return parse_ethernet;
3 | }
4 |
5 | #define ETHER_TYPE_IPV4 0x0800
6 | parser parse_ethernet {
7 | extract (ethernet);
8 | return select (latest.etherType) {
9 | ETHER_TYPE_IPV4: parse_ipv4;
10 | default: ingress;
11 | }
12 | }
13 |
14 | #define IPV4_PROTOCOL_TCP 6
15 | #define IPV4_PROTOCOL_UDP 17
16 | parser parse_ipv4 {
17 | extract(ipv4);
18 | return select (latest.protocol) {
19 | IPV4_PROTOCOL_TCP: parse_tcp;
20 | IPV4_PROTOCOL_UDP: parse_udp;
21 | default: ingress;
22 | }
23 | }
24 |
25 | parser parse_tcp {
26 | extract (tcp);
27 | return ingress;
28 | }
29 |
30 | parser parse_udp {
31 | extract (udp);
32 | return select (latest.dstPort) {
33 | NC_PORT: parse_overlay;
34 | REPLY_PORT: parse_overlay;
35 | default: ingress;
36 | }
37 | }
38 |
39 | parser parse_overlay {
40 | extract (overlay[next]);
41 | return select (latest.swip) {
42 | END_OF_CHAIN: parse_nc_hdr;
43 | default: parse_overlay;
44 | }
45 | }
46 |
47 | parser parse_nc_hdr {
48 | extract (nc_hdr);
49 | return select(latest.op) {
50 | NC_READ_REQUEST: ingress;
51 | NC_WRITE_REQUEST: ingress;
52 | default: ingress;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/p4src/netchain.p4:
--------------------------------------------------------------------------------
1 | #include "includes/defines.p4"
2 | #include "includes/headers.p4"
3 | #include "includes/parsers.p4"
4 | #include "includes/checksum.p4"
5 | #include "routing.p4"
6 |
7 |
8 |
9 | register sequence_reg {
10 | width: 16;
11 | instance_count: SEQUENCE_REG_SIZE;
12 | }
13 |
14 | register value_reg {
15 | width: 128;
16 | instance_count: VALUE_REG_SIZE;
17 | }
18 |
19 |
20 | header_type location_t {
21 | fields {
22 | index: 16;
23 | }
24 | }
25 | metadata location_t location;
26 |
27 | header_type sequence_md_t {
28 | fields {
29 | seq: 16;
30 | tmp: 16;
31 | }
32 | }
33 | metadata sequence_md_t sequence_md;
34 |
35 | header_type my_md_t {
36 | fields {
37 | ipaddress: 32;
38 | role: 16;
39 | failed: 16;
40 | }
41 | }
42 | metadata my_md_t my_md;
43 |
44 | header_type reply_addr_t {
45 | fields {
46 | ipv4_srcAddr: 32;
47 | ipv4_dstAddr: 32;
48 | }
49 | }
50 | metadata reply_addr_t reply_to_client_md;
51 |
52 | field_list rec_fl {
53 | standard_metadata;
54 | location;
55 | sequence_md;
56 | my_md;
57 | reply_to_client_md;
58 | }
59 |
60 |
61 | action find_index_act(index) {
62 | modify_field(location.index, index);
63 | }
64 |
65 | table find_index {
66 | reads {
67 | nc_hdr.key: exact;
68 | }
69 | actions {
70 | find_index_act;
71 | }
72 | }
73 |
74 |
75 | action get_sequence_act() {
76 | register_read(sequence_md.seq, sequence_reg, location.index);
77 | }
78 |
79 | table get_sequence {
80 | actions {
81 | get_sequence_act;
82 | }
83 | }
84 |
85 | action maintain_sequence_act() {
86 | add_to_field(sequence_md.seq, 1);
87 | register_write(sequence_reg, location.index, sequence_md.seq);
88 | register_read(nc_hdr.seq, sequence_reg, location.index);
89 | }
90 |
91 | table maintain_sequence {
92 | actions {
93 | maintain_sequence_act;
94 | }
95 | }
96 |
97 | action read_value_act() {
98 | register_read (nc_hdr.value, value_reg, location.index);
99 | }
100 |
101 | table read_value {
102 | actions {
103 | read_value_act;
104 | }
105 | }
106 |
107 | action assign_value_act() {
108 | register_write(sequence_reg, location.index, nc_hdr.seq);
109 | register_write(value_reg, location.index, nc_hdr.value);
110 | }
111 |
112 | table assign_value {
113 | actions {
114 | assign_value_act;
115 | }
116 | }
117 |
118 | action pop_chain_act() {
119 | add_to_field(nc_hdr.sc, -1);
120 | pop(overlay, 1);
121 | add_to_field(udp.len, -4);
122 | add_to_field(ipv4.totalLen, -4);
123 | }
124 |
125 | table pop_chain {
126 | actions {
127 | pop_chain_act;
128 | }
129 | }
130 |
131 | table pop_chain_again {
132 | actions {
133 | pop_chain_act;
134 | }
135 | }
136 |
137 | action gen_reply_act(message_type) {
138 | modify_field(reply_to_client_md.ipv4_srcAddr, ipv4.dstAddr);
139 | modify_field(reply_to_client_md.ipv4_dstAddr, ipv4.srcAddr);
140 | modify_field(ipv4.srcAddr, reply_to_client_md.ipv4_srcAddr);
141 | modify_field(ipv4.dstAddr, reply_to_client_md.ipv4_dstAddr);
142 | modify_field(nc_hdr.op, message_type);
143 | modify_field(udp.dstPort, REPLY_PORT);
144 | }
145 |
146 | table gen_reply {
147 | reads {
148 | nc_hdr.op: exact;
149 | }
150 | actions {
151 | gen_reply_act;
152 | }
153 | }
154 |
155 | action drop_packet_act() {
156 | drop();
157 | }
158 |
159 | table drop_packet {
160 | actions {
161 | drop_packet_act;
162 | }
163 | }
164 |
165 | action get_my_address_act(sw_ip, sw_role) {
166 | modify_field(my_md.ipaddress, sw_ip);
167 | modify_field(my_md.role, sw_role);
168 | }
169 |
170 | table get_my_address {
171 | reads {
172 | nc_hdr.key: exact;
173 | }
174 | actions {
175 | get_my_address_act;
176 | }
177 | }
178 |
179 | action get_next_hop_act() {
180 | modify_field(ipv4.dstAddr, overlay[0].swip);
181 | }
182 |
183 | table get_next_hop {
184 | actions {
185 | get_next_hop_act;
186 | }
187 | }
188 |
189 | action failover_act() {
190 | modify_field(ipv4.dstAddr, overlay[1].swip);
191 | pop_chain_act();
192 | }
193 |
194 | action failover_write_reply_act() {
195 | gen_reply_act(NC_WRITE_REPLY);
196 | }
197 |
198 | action failure_recovery_act(nexthop) {
199 | modify_field(overlay[0].swip, nexthop);
200 | modify_field(ipv4.dstAddr, nexthop);
201 | }
202 |
203 | action nop() {
204 | no_op();
205 | }
206 |
207 | table failure_recovery {
208 | reads {
209 | ipv4.dstAddr: ternary;
210 | overlay[1].swip: ternary;
211 | nc_hdr.vgroup: ternary;
212 | }
213 | actions {
214 | failover_act;
215 | failover_write_reply_act;
216 | failure_recovery_act;
217 | nop;
218 | drop_packet_act;
219 | }
220 | }
221 |
222 | control ingress {
223 | if (valid(nc_hdr)) {
224 | apply (get_my_address);
225 | if (ipv4.dstAddr == my_md.ipaddress) {
226 | apply (find_index);
227 | apply (get_sequence);
228 | if (nc_hdr.op == NC_READ_REQUEST) {
229 | apply (read_value);
230 | }
231 | else if (nc_hdr.op == NC_WRITE_REQUEST) {
232 | if (my_md.role == HEAD_NODE) {
233 | apply (maintain_sequence);
234 | }
235 | if ((my_md.role == HEAD_NODE) or (nc_hdr.seq > sequence_md.seq)) {
236 | apply (assign_value);
237 | apply (pop_chain);
238 | }
239 | else {
240 | apply (drop_packet);
241 | }
242 |
243 | }
244 | if (my_md.role == TAIL_NODE) {
245 | apply (pop_chain_again);
246 | apply (gen_reply);
247 | }
248 | else {
249 | apply (get_next_hop);
250 | }
251 | }
252 | }
253 | if (valid(nc_hdr)) {
254 | apply (failure_recovery);
255 |
256 | }
257 | if (valid(tcp) or valid(udp)) {
258 | apply (ipv4_route);
259 | }
260 | }
261 |
262 | control egress {
263 | apply (ethernet_set_mac);
264 | }
265 |
266 |
--------------------------------------------------------------------------------
/p4src/routing.p4:
--------------------------------------------------------------------------------
1 | action set_egress(egress_spec) {
2 | modify_field(standard_metadata.egress_spec, egress_spec);
3 | add_to_field(ipv4.ttl, -1);
4 | }
5 |
6 | @pragma stage 11
7 | table ipv4_route {
8 | reads {
9 | ipv4.dstAddr : exact;
10 | }
11 | actions {
12 | set_egress;
13 | }
14 | size : 8192;
15 | }
16 |
17 | action ethernet_set_mac_act (smac, dmac) {
18 | modify_field (ethernet.srcAddr, smac);
19 | modify_field (ethernet.dstAddr, dmac);
20 | }
21 | table ethernet_set_mac {
22 | reads {
23 | standard_metadata.egress_port: exact;
24 | }
25 | actions {
26 | ethernet_set_mac_act;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------