├── .gitignore ├── .gitmodules ├── LICENSE ├── Readme.md ├── p4src ├── Pkt.p4 ├── Pkt_W.p4 ├── Pkt_WC.p4 ├── includes │ └── p4 │ │ ├── counter_pkt.p4 │ │ ├── egress.p4 │ │ ├── egress_W.p4 │ │ ├── egress_WC.p4 │ │ ├── headers.p4 │ │ ├── ingress.p4 │ │ ├── ingress_W.p4 │ │ ├── ingress_WC.p4 │ │ ├── notify.p4 │ │ ├── notify_C.p4 │ │ └── parser.p4 ├── primitives │ ├── .gitignore │ ├── primitives.append │ └── primitives.json └── utilities │ ├── generate_p4vars.py │ └── generate_rules.py ├── setup.sh ├── snapshot_init ├── snapListener.py └── startsnap.cpp ├── start_listening.sh ├── start_snapshot.sh └── start_switch.sh /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/behavioral-model"] 2 | path = third_party/behavioral-model 3 | url = https://github.com/p4lang/behavioral-model.git 4 | [submodule "third_party/p4c-bm"] 5 | path = third_party/p4c-bm 6 | url = https://github.com/p4lang/p4c-bm.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 University of Pennsylvania | Distributed Systems Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Speedlight BMv2 # 2 | 3 | This repository contains a BMv2 adaptation of the Speedlight P4 dataplane, originally implemented for the Barefoot Tofino. 4 | 5 | ### Contents ### 6 | 7 | - `p4src/` -- The P4 source code of Speedlight. 8 | - `includes/p4/` -- P4 includes that are shared amongst all variants. 9 | - `primitives/` -- Stateful primitives for Speedlight. 10 | - `snapshot_init/` -- Snapshot initiation program. 11 | - `third_party/` -- bmv2 and p4v-bm submodules. 12 | 13 | ### Naming Conventions ### 14 | 15 | For file names: 16 | - `*_W` -- Contains code for handling wrapping of the snapshot ID back to zero. 17 | - `*_C` -- Contains code for capturing channel state, not just local processing unit state. 18 | 19 | For function names: 20 | - `t*` -- match table 21 | - `a*` -- action definition 22 | - `r*` -- stateful register operation 23 | - `i` or `e` as a second letter indicates ingress or egress. None indicates it is used for both. 24 | For example, `tiCheckRollover` is a table used during ingress, `reUpdateSnapshotId` is a stateful operation used during egress, and `aCheckSnapshotCase` is an action used in both directions. 25 | 26 | ### Compiling the dataplane ### 27 | 28 | This release has been tested on Ubuntu 16.04 LTS, but should work on Ubuntu 14.04+. 29 | p4 simluator has a large memory requirement. We used it in 32gb RAM. 30 | The following instructions assume 3 windows: (1) switch behavioral model, (2) notification listener, and (3) snapshot initiation script. 31 | 32 | 1. Setup. 33 | ``` 34 | # Installs all prerequisites and compiles the Speedlight dataplane, including: 35 | # https://github.com/p4lang/behavioral-model/commit/66cefc5e901eafcebb0e1a8f681a05795463215a 36 | # https://github.com/p4lang/p4c-bm/commit/d75624e18f4ae79e9e5cb478c33d221711f76574 37 | # Also sets up the virtual network interfaces for BMv2 38 | ./setup.sh 39 | ``` 40 | 41 | 2. Compile and run the Speedlight dataplane. 42 | ``` 43 | # {Pkt, Pkt_W, Pkt_WC} 44 | # Number of ports in the switch. 45 | # Highest valid snapshot ID. Without wraparound (*_W), 46 | # behavior above this value is undefined. With wraparound, 47 | # this number determines the maximum number of outstanding 48 | # snapshots. 49 | ./start_switch.sh 50 | # Leave this window open 51 | ``` 52 | 53 | 3. In a new window, install the match-action rules and start listening for notifications. 54 | ``` 55 | # {Pkt, Pkt_W, Pkt_WC}. Must match parameter given to 56 | # start_switch.sh. 57 | ./start_listening.sh 58 | # Leave this window open. Notifications will output here. 59 | ``` 60 | 61 | 4. In a third window, initiate a snapshot. 62 | ``` 63 | # Hour of snapshot according to the Unix date command 64 | # Minute of snapshot according to the Unix date command 65 | # Number of ports in the switch. Must match parameter given 66 | # to start_switch.sh. 67 | # This will initiate a single snapshot with ID = 1 in a parallel fashion. 68 | # Port responsibilities are spread across available cores to increase the 69 | # speed at which we can issue a sequence of snapshot initiations. 70 | # Example: ./start_snapshot 17 09 10 71 | # This will take snapshot for 10 ports at time 5:09 pm. 72 | ./start_snapshot.sh 73 | 74 | # You can also access the original, more flexible snapshot initiation 75 | # that is compiled to the following program. This one is required for 76 | # subsequent snapshots, etc. 77 | out/startsnap 78 | ``` 79 | 80 | ### Troubleshooting ### 81 | 82 | 1. If the start_listening.sh output includes the following: 83 | ``` 84 | Invalid table operation (DUPLICATE_ENTRY) 85 | ``` 86 | Please make sure to restart the switch between invocations of start_listening.sh 87 | 88 | 2. If start_listening.sh prints nonsensical values, ensure that the VARIANT parameters of start_switch.sh and start_listening.sh match. 89 | 90 | 3. If the start_switch.sh crashes with: 91 | ``` 92 | lt-simple_switch: ../../include/bm/bm_sim/stateful.h:111: bm::Register& bm::RegisterArray::operator[](size_t): Assertion `idx < size()' failed. 93 | ``` 94 | Ensure that you are running start_snapshot.sh with NUM_PORTS <= start_switch.sh's NUM_PORTS 95 | 96 | 97 | ### Limitations ### 98 | 99 | Note that this BMv2 version can only handle a small number of snapshot packets at a time---the behavioral model simply can't keep up with the pace of packets. At higher port counts, particularly with channel state notifications, initiation and notification drops occur. Our Tofino version does not have this limitation. 100 | -------------------------------------------------------------------------------- /p4src/Pkt.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Top-level ingress/egress control flow for a simple packet counter snapshot. 18 | * Does not include logic for wraparound of counters or snapshotting of channel 19 | * state. Also does not include forwarding logic, which can occur in parallel. 20 | **/ 21 | 22 | 23 | #include "../out/config.p4" 24 | #include "./includes/p4/headers.p4" 25 | #include "./includes/p4/parser.p4" 26 | #include "./includes/p4/ingress.p4" 27 | #include "./includes/p4/egress.p4" 28 | #include "./includes/p4/counter_pkt.p4" 29 | #include "./includes/p4/notify.p4" 30 | 31 | 32 | /*========================================== 33 | = Ingress = 34 | ==========================================*/ 35 | 36 | control ingress { 37 | if (ethernet.etherType == ETHERTYPE_IPV4) { 38 | // Handle a (potentially SS) IPv4 packet 39 | ciHandleIPv4(); 40 | } 41 | } 42 | 43 | control ciHandleIPv4 { 44 | /* 45 | * Input: snapshot_header.port_id, ingress_port 46 | * Output: effective_port, snapshot_feature 47 | * (in ingress.p4) 48 | */ 49 | ciInitializeSS(); 50 | /* 51 | * Input: effective_port 52 | * Output: current_reading 53 | * (in counter_pkt.p4) 54 | */ 55 | apply(tiReadAndUpdateCounter); 56 | 57 | if (snapshot_metadata.snapshot_feature == 0) { 58 | /* 59 | * Input: effective_port 60 | * Output: current_id 61 | * Postcondition: packet.addIpv4Option(); packet.addSnapshotHeader(); 62 | * (in ingress.p4) 63 | */ 64 | ciAddHeader(); 65 | } else { 66 | /* 67 | * Input: effective_port, snapshot_header.snapshot_id 68 | * Output: snapshot_case 69 | * (in ingress.p4) 70 | */ 71 | ciSetSnapshotCase(); 72 | /* 73 | * Input: snapshot_case, snapshot_header.snapshot_id, current_reading 74 | * Output: current_id 75 | * Notification: effective_port, snapshot_header.snapshot_id, 76 | * current_reading, ingress_global_tstamp 77 | * (in ingress.p4) 78 | */ 79 | ciTakeSnapshot(); 80 | } 81 | if (snapshot_metadata.snapshot_feature == 2) { 82 | /* 83 | * Input: effective_port 84 | * Postcondition: packet destined for effective_port's egress 85 | * (in ingress.p4) 86 | */ 87 | apply(tiForwardInitiation); 88 | } else { 89 | /* 90 | * Input: current_id, effective_port 91 | * Postcondition: packet set with above parameters 92 | * (in ingress.p4) 93 | */ 94 | apply(tiSetSnapHeader); 95 | } 96 | } 97 | 98 | 99 | /*========================================== 100 | = Egress = 101 | ==========================================*/ 102 | 103 | control egress { 104 | if (standard_metadata.egress_port == CPU_PORT) { 105 | // If packet is destined for CPU, remove all headers after eth 106 | // and stuff the last seen notification into the packet. 107 | // (in egress.p4) 108 | apply(teFormatForCpu); 109 | } else if (ethernet.etherType == ETHERTYPE_IPV4) { 110 | ceHandleIPv4(); 111 | } 112 | } 113 | 114 | control ceHandleIPv4 { 115 | /* 116 | * Input: egress_port 117 | * Output: effective_port 118 | * (in egress.p4) 119 | */ 120 | apply(teSetEffectivePort); 121 | /* 122 | * Input: effective_port 123 | * Output: current_reading 124 | * (in counter_pkt.p4) 125 | */ 126 | apply(teReadAndUpdateCounter); 127 | /* 128 | * Input: effective_port, snapshot_header.snapshot_id 129 | * Output: snapshot_case 130 | * (in egress.p4) 131 | */ 132 | ceSetSnapshotCase(); 133 | /* 134 | * Input: snapshot_case, snapshot_header.snapshot_id, current_reading 135 | * Output: current_id 136 | * Notification: effective_port, snapshot_header.snapshot_id, 137 | * current_reading, ingress_global_tstamp 138 | * (in ingress.p4) 139 | */ 140 | ceTakeSnapshot(); 141 | /* 142 | * Postcondition: packet either forwarded, dropped, or stripped of its 143 | * snapshot header. 144 | * (in egress.p4) 145 | */ 146 | apply(teFinalizePacket); 147 | } 148 | -------------------------------------------------------------------------------- /p4src/Pkt_W.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Top-level ingress/egress control flow for a packet counter snapshot with 18 | * wraparound of the snapshot ID. Does not include logic for snapshotting of 19 | * channel state. Also does not include forwarding logic, which can occur in 20 | * parallel. 21 | **/ 22 | 23 | 24 | #include "../out/config.p4" 25 | #include "./includes/p4/headers.p4" 26 | #include "./includes/p4/parser.p4" 27 | #include "./includes/p4/ingress_W.p4" 28 | #include "./includes/p4/egress_W.p4" 29 | #include "./includes/p4/counter_pkt.p4" 30 | #include "./includes/p4/notify.p4" 31 | 32 | 33 | /*========================================== 34 | = Ingress = 35 | ==========================================*/ 36 | 37 | control ingress { 38 | if (ethernet.etherType == ETHERTYPE_IPV4) { 39 | // Handle a (potentially SS) IPv4 packet 40 | ciHandleIPv4(); 41 | } 42 | } 43 | 44 | control ciHandleIPv4 { 45 | /* 46 | * Input: snapshot_header.port_id, ingress_port 47 | * Output: effective_port, snapshot_feature 48 | * (in ingress.p4) 49 | */ 50 | ciInitializeSS(); 51 | /* 52 | * Input: effective_port 53 | * Output: current_reading 54 | * (in counter_pkt.p4) 55 | */ 56 | apply(tiReadAndUpdateCounter); 57 | /* 58 | * Input: effective_port, snapshot_feature, snapshot_header.snapshot_id 59 | * Output: former_last_seen 60 | * Postcondition: reg_last_seen_in_ingress[index] = snapshot_header.snapshot_id 61 | * (in ingress_W.p4) 62 | */ 63 | apply(tiUpdateLastSeen); 64 | /* 65 | * Input: former_last_seen, snapshot_header.snapshot_id 66 | * Output: snapshot_changed_check, snapshot_rollover_check 67 | * (in ingress_W.p4) 68 | */ 69 | apply(tiCheckRollover); 70 | 71 | if (snapshot_metadata.snapshot_feature == 0) { 72 | /* 73 | * Input: effective_port 74 | * Output: current_id 75 | * Postcondition: packet.addIpv4Option(); packet.addSnapshotHeader(); 76 | * (in ingress.p4) 77 | */ 78 | ciAddHeader(); 79 | } else { 80 | /* 81 | * Input: effective_port, former_id, former_last_seen, 82 | * snapshot_header.snapshot_id 83 | * Output: snapshot_case 84 | * (in ingress_W.p4) 85 | */ 86 | if (snapshot_metadata.snapshot_rollover_check == 0) { 87 | ciSetSnapshotCaseNoRollover(); 88 | } else { 89 | ciSetSnapshotCaseRollover(); 90 | } 91 | /* 92 | * Input: snapshot_case, snapshot_header.snapshot_id, current_reading 93 | * Output: current_id 94 | * Notification: effective_port, snapshot_header.snapshot_id, 95 | * current_reading, ingress_global_tstamp 96 | * (in ingress.p4) 97 | */ 98 | ciTakeSnapshot(); 99 | } 100 | if (snapshot_metadata.snapshot_feature == 2) { 101 | /* 102 | * Input: effective_port 103 | * Postcondition: packet destined for effective_port's egress 104 | * (in ingress.p4) 105 | */ 106 | apply(tiForwardInitiation); 107 | } else { 108 | /* 109 | * Input: current_id, effective_port 110 | * Postcondition: packet set with above parameters 111 | * (in ingress.p4) 112 | */ 113 | apply(tiSetSnapHeader); 114 | } 115 | } 116 | 117 | 118 | /*========================================== 119 | = Egress = 120 | ==========================================*/ 121 | 122 | control egress { 123 | if (standard_metadata.egress_port == CPU_PORT) { 124 | // If packet is destined for CPU, remove all headers after eth 125 | // and stuff the last seen notification into the packet. 126 | // (in egress.p4) 127 | apply(teFormatForCpu); 128 | } else if (ethernet.etherType == ETHERTYPE_IPV4) { 129 | ceHandleIPv4(); 130 | } 131 | } 132 | 133 | control ceHandleIPv4 { 134 | /* 135 | * Input: egress_port 136 | * Output: effective_port 137 | * (in egress.p4) 138 | */ 139 | apply(teSetEffectivePort); 140 | /* 141 | * Input: effective_port 142 | * Output: current_reading 143 | * (in counter_pkt.p4) 144 | */ 145 | apply(teReadAndUpdateCounter); 146 | /* 147 | * Input: effective_port, snapshot_header.port_id, ipv4_option.option, 148 | * snapshot_header.snapshot_id 149 | * Output: former_last_seen 150 | * (in egress_W.p4) 151 | */ 152 | apply(teUpdateLastSeen); 153 | /* 154 | * Input: former_last_seen, snapshot_header.snapshot_id 155 | * Output: snapshot_changed_check, snapshot_rollover_check 156 | * (in egress_W.p4) 157 | */ 158 | apply(teCheckRollover); 159 | /* 160 | * Input: effective_port, former_last_seen, snapshot_header.snapshot_id 161 | * Output: snapshot_case 162 | * (in egress_W.p4) 163 | */ 164 | if (snapshot_metadata.snapshot_rollover_check == 0) { 165 | ceSetSnapshotCaseNoRollover(); 166 | } else { 167 | ceSetSnapshotCaseRollover(); 168 | } 169 | /* 170 | * Input: snapshot_case, snapshot_header.snapshot_id, current_reading 171 | * Output: current_id 172 | * Notification: effective_port, snapshot_header.snapshot_id, 173 | * current_reading, ingress_global_tstamp 174 | * (in egress.p4) 175 | */ 176 | ceTakeSnapshot(); 177 | /* 178 | * Postcondition: packet either forwarded, dropped, or stripped of its 179 | * snapshot header. 180 | * (in egress.p4) 181 | */ 182 | apply(teFinalizePacket); 183 | } -------------------------------------------------------------------------------- /p4src/Pkt_WC.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Top-level ingress/egress control flow for a packet counter snapshot with 18 | * wraparound of the snapshot ID and snapshotting of channel state. Does not 19 | * include forwarding logic, which can occur in parallel. 20 | **/ 21 | 22 | 23 | #include "../out/config.p4" 24 | #include "./includes/p4/headers.p4" 25 | #include "./includes/p4/parser.p4" 26 | #include "./includes/p4/ingress_WC.p4" 27 | #include "./includes/p4/egress_WC.p4" 28 | #include "./includes/p4/counter_pkt.p4" 29 | #include "./includes/p4/notify_C.p4" 30 | 31 | 32 | /*========================================== 33 | = Ingress = 34 | ==========================================*/ 35 | 36 | control ingress { 37 | if (ethernet.etherType == ETHERTYPE_IPV4) { 38 | // Handle a (potentially SS) IPv4 packet 39 | ciHandleIPv4(); 40 | } 41 | } 42 | 43 | control ciHandleIPv4 { 44 | /* 45 | * Input: snapshot_header.port_id, ingress_port 46 | * Output: effective_port, snapshot_feature 47 | * (in ingress.p4) 48 | */ 49 | ciInitializeSS(); 50 | /* 51 | * Input: effective_port 52 | * Output: current_reading 53 | * (in counter_pkt.p4) 54 | */ 55 | apply(tiReadAndUpdateCounter); 56 | /* 57 | * Input: effective_port, snapshot_feature, snapshot_header.snapshot_id 58 | * Output: former_last_seen 59 | * Postcondition: reg_last_seen_in_ingress[index] = snapshot_header.snapshot_id 60 | * (in ingress_W.p4) 61 | */ 62 | apply(tiUpdateLastSeen); 63 | /* 64 | * Input: former_last_seen, snapshot_header.snapshot_id 65 | * Output: snapshot_changed_check, snapshot_rollover_check 66 | * (in ingress_W.p4) 67 | */ 68 | apply(tiCheckRollover); 69 | 70 | if (snapshot_metadata.snapshot_feature == 0) { 71 | /* 72 | * Input: effective_port 73 | * Output: current_id 74 | * Postcondition: packet.addIpv4Option(); packet.addSnapshotHeader(); 75 | * (in ingress.p4) 76 | */ 77 | ciAddHeader(); 78 | } else { 79 | /* 80 | * Input: effective_port, former_id, former_last_seen, 81 | * snapshot_header.snapshot_id 82 | * Output: snapshot_case 83 | * (in ingress_W.p4) 84 | */ 85 | if (snapshot_metadata.snapshot_rollover_check == 0) { 86 | ciSetSnapshotCaseNoRollover(); 87 | } else { 88 | ciSetSnapshotCaseRollover(); 89 | } 90 | /* 91 | * Input: snapshot_case, snapshot_header.snapshot_id, former_id, 92 | * current_reading 93 | * Output: current_id 94 | * Notification: effective_port, snapshot_header.snapshot_id, 95 | * current_reading, ingress_global_tstamp 96 | * (in ingress_WC.p4) 97 | */ 98 | ciTakeSnapshotWithChannelState(); 99 | } 100 | if (snapshot_metadata.snapshot_feature == 2) { 101 | /* 102 | * Input: effective_port 103 | * Postcondition: packet destined for effective_port's egress 104 | * (in ingress.p4) 105 | */ 106 | apply(tiForwardInitiation); 107 | } else { 108 | /* 109 | * Input: current_id, effective_port 110 | * Postcondition: packet set with above parameters 111 | * (in ingress.p4) 112 | */ 113 | apply(tiSetSnapHeader); 114 | } 115 | } 116 | 117 | 118 | /*========================================== 119 | = Egress = 120 | ==========================================*/ 121 | 122 | control egress { 123 | if (standard_metadata.egress_port == CPU_PORT) { 124 | // If packet is destined for CPU, remove all headers after eth 125 | // and stuff the last seen notification into the packet. 126 | // (in egress.p4) 127 | apply(teFormatForCpu); 128 | } else if (ethernet.etherType == ETHERTYPE_IPV4) { 129 | ceHandleIPv4(); 130 | } 131 | } 132 | 133 | control ceHandleIPv4 { 134 | /* 135 | * Input: egress_port 136 | * Output: effective_port 137 | * (in egress.p4) 138 | */ 139 | apply(teSetEffectivePort); 140 | /* 141 | * Input: effective_port 142 | * Output: current_reading 143 | * (in counter_pkt.p4) 144 | */ 145 | apply(teReadAndUpdateCounter); 146 | /* 147 | * Input: effective_port, snapshot_header.port_id, ipv4_option.option, 148 | * snapshot_header.snapshot_id 149 | * Output: former_last_seen 150 | * (in egress_W.p4) 151 | */ 152 | apply(teUpdateLastSeen); 153 | /* 154 | * Input: former_last_seen, snapshot_header.snapshot_id 155 | * Output: snapshot_changed_check, snapshot_rollover_check 156 | * (in egress_W.p4) 157 | */ 158 | apply(teCheckRollover); 159 | /* 160 | * Input: effective_port, former_last_seen, snapshot_header.snapshot_id 161 | * Output: snapshot_case 162 | * (in egress_W.p4) 163 | */ 164 | if (snapshot_metadata.snapshot_rollover_check == 0) { 165 | ceSetSnapshotCaseNoRollover(); 166 | } else { 167 | ceSetSnapshotCaseRollover(); 168 | } 169 | /* 170 | * Input: snapshot_case, snapshot_header.snapshot_id, former_id, 171 | * current_reading 172 | * Output: current_id 173 | * Notification: effective_port, snapshot_header.snapshot_id, 174 | * current_reading, ingress_global_tstamp 175 | * (in egress_WC.p4) 176 | */ 177 | ceTakeSnapshotWithChannelState(); 178 | /* 179 | * Postcondition: packet either forwarded, dropped, or stripped of its 180 | * snapshot header. 181 | * (in egress.p4) 182 | */ 183 | apply(teFinalizePacket); 184 | } -------------------------------------------------------------------------------- /p4src/includes/p4/counter_pkt.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Packet counter code. 18 | **/ 19 | 20 | 21 | // Gets and post-increments ingress counter. 22 | // 23 | // Input: effective_port 24 | // Output: current_reading 25 | // Postcondition: reg_counter_ingress[effective_port]++ 26 | table tiReadAndUpdateCounter { 27 | actions { aiReadAndUpdateCounter; } 28 | default_action: aiReadAndUpdateCounter(); 29 | size : 0; 30 | } 31 | 32 | action aiReadAndUpdateCounter() { 33 | riReadAndUpdateCounter(reg_counter_ingress, 34 | snapshot_metadata.effective_port); 35 | } 36 | 37 | action aiUpdateInflight(index_val) { 38 | riUpdateInflight(reg_snapshot_value_ingress, index_val); 39 | modify_field(snapshot_metadata.current_id, snapshot_metadata.former_id); 40 | } 41 | 42 | // Gets and post-increments egress counter. 43 | // 44 | // Input: effective_port 45 | // Output: current_reading 46 | // Postcondition: reg_counter_egress[effective_port]++ 47 | table teReadAndUpdateCounter { 48 | actions { aeReadAndUpdateCounter; } 49 | default_action: aeReadAndUpdateCounter(); 50 | size : 0; 51 | } 52 | 53 | action aeReadAndUpdateCounter() { 54 | reReadAndUpdateCounter(reg_counter_egress, 55 | snapshot_metadata.effective_port); 56 | } 57 | 58 | action aeUpdateInflight(index_val) { 59 | reUpdateInflight(reg_snapshot_value_egress, index_val); 60 | modify_field(snapshot_metadata.current_id, snapshot_metadata.former_id); 61 | } 62 | -------------------------------------------------------------------------------- /p4src/includes/p4/egress.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the base egress pipeline of Speedlight. 18 | **/ 19 | 20 | 21 | // STAGE DOESNT MATTER (only used in one branch) 22 | register reg_counter_egress { 23 | width : COUNTER_WIDTH; 24 | instance_count : PORTS_TBL_SIZE; 25 | } 26 | 27 | // STAGE DOESNT MATTER (only used in one branch) 28 | register reg_last_seen_in_egress { 29 | width : SNAPSHOT_ID_WIDTH; 30 | instance_count : NEIGHBORS_TBL_SIZE_EGR; 31 | } 32 | 33 | // STAGE 5 ONLY 34 | register reg_snapshot_id_egress { 35 | width : SNAPSHOT_ID_WIDTH; 36 | instance_count : PORTS_TBL_SIZE; 37 | } 38 | 39 | // STAGE DOESNT MATTER (only used in one branch) 40 | register reg_snapshot_value_egress { 41 | width : COUNTER_WIDTH; 42 | instance_count: SSTABLE_SIZE; 43 | } 44 | 45 | 46 | /*============================================================================== 47 | = Format for CPU = 48 | = Strips the necessary headers and adds the notification header. Runs when = 49 | = the egress port is CPU_PORT. = 50 | ==============================================================================*/ 51 | 52 | /** 53 | * Load packet destined for CPU, after it is cloned. 54 | * Packet Format: Ethernet header | last_seen_notif 55 | */ 56 | table teFormatForCpu { 57 | actions { aeFormatForCpu; } 58 | default_action: aeFormatForCpu(); 59 | size : 1; 60 | } 61 | 62 | 63 | /*============================================================================== 64 | = Set Effective Port = 65 | = Translates actual port number to a contiguous 0-indexed representation to = 66 | = make table indexing easier. = 67 | ==============================================================================*/ 68 | 69 | /** 70 | * Set effective port. Assumes that the operator has configured this table to 71 | * map correctly. Also stores a decremented IPv4 IHL value (hack to avoid 72 | * another stage). 73 | */ 74 | table teSetEffectivePort { 75 | reads { 76 | standard_metadata.egress_port : exact; 77 | } 78 | actions { 79 | aeSetEffectivePort; 80 | } 81 | size : NUM_PORTS; 82 | } 83 | 84 | action aeSetEffectivePort(portIndex){ 85 | subtract(snapshot_metadata.temp_ihl, ipv4.ihl, 2); 86 | modify_field(snapshot_metadata.effective_port, portIndex); 87 | } 88 | 89 | 90 | /*============================================================================== 91 | = Set Snapshot Case = 92 | = Sets snapshot_case based on the current snapshot ID and the packet's ID. = 93 | = Similar to ingress pipeline. This does not take rollover into account. = 94 | ==============================================================================*/ 95 | 96 | control ceSetSnapshotCase { 97 | /* 98 | * former_id = reg_snapshot_id_egress[effective_port] 99 | * if (snapshot_header.snapshot_id > former_id) 100 | * reg_snapshot_id_egress[effective_port] = snapshot_header.snapshot_id 101 | */ 102 | apply(teUpdateSnapshotId); 103 | /* 104 | * switch_greater = former_id - snapshot_header.snapshot_id 105 | * packet_greater = snapshot_header.snapshot_id - former_id 106 | */ 107 | apply(teCheckSnapshotCase); 108 | /* 109 | * if (switch_greater == 0 and packet_greater == 0) 110 | * snapshot_case = 0 111 | * else if (switch_greater == 0) 112 | * snapshot_case = 1 113 | * else if (packet_greater == 0) 114 | * snapshot_case = 2 115 | */ 116 | apply(teSetSnapshotCase); 117 | } 118 | 119 | /** 120 | * Loads and possibly updates our current snapshot ID. This should only update 121 | * if the packet's snapshot ID is larger than our snapshot ID. 122 | * 123 | * Input: snapshot_metadata.effective_port 124 | * Output: metadata.former_id = reg_snapshot_id_egress[snapshot_metadata.effective_port] 125 | **/ 126 | table teUpdateSnapshotId { 127 | actions { aeUpdateSnapshotId; } 128 | default_action: aeUpdateSnapshotId(); 129 | size : 0; 130 | } 131 | 132 | action aeUpdateSnapshotId() { 133 | reUpdateSnapshotId(reg_snapshot_id_egress, snapshot_metadata.effective_port); 134 | } 135 | 136 | /** 137 | * See tiCheckSnapshotCase 138 | **/ 139 | table teCheckSnapshotCase { 140 | actions { aCheckSnapshotCase; } 141 | default_action: aCheckSnapshotCase(); 142 | size : 0; 143 | } 144 | 145 | /** 146 | * See tiSetSnapshotCaseNoRollover 147 | **/ 148 | table teSetSnapshotCase { 149 | reads { 150 | snapshot_metadata.switch_greater : ternary; 151 | snapshot_metadata.packet_greater : ternary; 152 | } 153 | actions { 154 | aSetSnapshotCase; 155 | } 156 | size : 8; 157 | } 158 | 159 | 160 | /*============================================================================== 161 | = Take Snapshot = 162 | = Similar to ingress. Takes a snapshot if snapshot_case == 1. This check is = 163 | = performed in the match tables and extern functions to minimize branches. = 164 | = Note that the snapshot does not include channel state. = 165 | ==============================================================================*/ 166 | 167 | control ceTakeSnapshot { 168 | /* 169 | * if (snapshot_case == 1) 170 | * reg_snapshot_value_egress[index] = current_reading 171 | * current_id = snapshot_header.snapshot_id 172 | */ 173 | apply(teTakeSnapshot); 174 | /* 175 | * if (snapshot_case == 1) 176 | * Notification: effective_port, snapshot_header.snapshot_id, 177 | * current_reading, ingress_global_tstamp 178 | */ 179 | apply(teSendNotification); 180 | } 181 | 182 | /** 183 | * Stores counter (pre-increment) into the snapshot register 184 | * 185 | * Input: current_index, current_reading 186 | * Postcondition: reg_snapshot_value_egress[current_index] = current_reading 187 | */ 188 | table teTakeSnapshot { 189 | reads { 190 | snapshot_metadata.snapshot_case : exact; 191 | snapshot_header.snapshot_id : exact; 192 | snapshot_metadata.effective_port : exact; 193 | } 194 | actions { 195 | aeTakeSnapshot; 196 | } 197 | size : DOUBLE_SSTABLE_SIZE; 198 | } 199 | 200 | action aeTakeSnapshot(index_val) { 201 | register_write(reg_snapshot_value_egress, index_val, snapshot_metadata.current_reading); 202 | modify_field(snapshot_metadata.current_id, snapshot_header.snapshot_id); 203 | } 204 | 205 | /** 206 | * Sends a notification to the CPU when a neighbor sends you a new snapshot ID. 207 | * In the traditional algorithm, we send a notification any time we get a new 208 | * snapshot from a neighbor. That's essentially what we're doing here. 209 | * 210 | * This should be after the register state is consistent 211 | * 212 | * if snapshot_case == 1: 213 | * CPU_NEWSS_EGRESS 214 | * else 215 | * aNoOp 216 | * 217 | * Input: snapshot_changed_check, snapshot_feature, snapshot_case 218 | * Postcondition: Notification has been sent to the CPU 219 | **/ 220 | table teSendNotification { 221 | reads { 222 | snapshot_metadata.snapshot_case : exact; 223 | } 224 | actions { 225 | aeSendNotification; // in notify(_C).p4 226 | } 227 | default_action: aNoOp(); 228 | size : 2; 229 | } 230 | 231 | 232 | /*============================================================================== 233 | = Finalize Packet = 234 | = Modify the header or drop to complete egress processing. = 235 | ==============================================================================*/ 236 | 237 | /** 238 | * Either strips the snapshot header and IPv4 Option from the packet, forwards 239 | * it, or drops it. 240 | * 241 | * if (ipv4_option.option == IPV4_OPTION_SS_CPU) { 242 | * aeDropPacket(); 243 | * } else if (standard_metadata.egress_port is host-facing) { 244 | * aeRemoveSnapshotHeader(); 245 | * } else { 246 | * aeUpdateSnapshotHeader(); 247 | * } 248 | */ 249 | table teFinalizePacket { 250 | reads { 251 | ipv4_option.option : exact; 252 | standard_metadata.egress_port : ternary; 253 | } 254 | actions { 255 | aeDropPacket; 256 | aeRemoveSnapshotHeader; 257 | aeUpdateSnapshotHeader; 258 | } 259 | default_action: aeUpdateSnapshotHeader(); 260 | size : NUM_PORTS; 261 | } 262 | 263 | action aeDropPacket() { 264 | drop(); 265 | } 266 | 267 | action aeRemoveSnapshotHeader() { 268 | remove_header(snapshot_header); 269 | remove_header(ipv4_option); 270 | modify_field(ipv4.ihl, snapshot_metadata.temp_ihl); 271 | subtract_from_field(ipv4.totalLen, 8); 272 | 273 | modify_field(ipv4_option.copyFlag, 0); 274 | modify_field(ipv4_option.optClass, 0); 275 | modify_field(ipv4_option.option, 0); 276 | modify_field(ipv4_option.optionLength, 0); 277 | modify_field(snapshot_header.snapshot_id, 0); 278 | modify_field(snapshot_header.port_id, 0); 279 | 280 | subtract_from_field(ipv4.ttl, 1); 281 | } 282 | 283 | action aeUpdateSnapshotHeader() { 284 | modify_field(snapshot_header.snapshot_id, snapshot_metadata.current_id); 285 | modify_field(snapshot_header.port_id, snapshot_metadata.effective_port); 286 | 287 | subtract_from_field(ipv4.ttl, 1); 288 | } 289 | -------------------------------------------------------------------------------- /p4src/includes/p4/egress_W.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the wraparound functions of the egress pipeline of 18 | * Speedlight. 19 | **/ 20 | 21 | 22 | #include "egress.p4" 23 | 24 | 25 | /*============================================================================== 26 | = Get Last Seen and Check Rollover = 27 | = Similar to ingress. Fetches and updates the former last seen register, then = 28 | = uses it to check if the neighbor's snapshot id has rolled over since the = 29 | = last time they sent us a message. = 30 | ==============================================================================*/ 31 | 32 | /** 33 | * Get and update the last seen snapshot ID from my neighbor. 34 | * 35 | * Input: effective_port, snapshot_header.port_id, ipv4_option.option, 36 | * snapshot_header.snapshot_id 37 | * Output: former_last_seen 38 | * Postcondition: reg_last_seen_in_egress[current_index] = snapshot_header.snapshot_id 39 | **/ 40 | table teUpdateLastSeen { 41 | reads { 42 | snapshot_metadata.effective_port: exact; 43 | snapshot_header.port_id : ternary; 44 | ipv4_option.option : ternary; 45 | } 46 | actions { 47 | aeUpdateLastSeen; 48 | } 49 | size : NEIGHBORS_TBL_SIZE_EGR; 50 | } 51 | 52 | action aeUpdateLastSeen(index_val) { 53 | reUpdateLastSeen(reg_last_seen_in_egress, index_val); 54 | } 55 | 56 | /** 57 | * See tiCheckRollover 58 | **/ 59 | table teCheckRollover { 60 | reads { 61 | snapshot_metadata.snapshot_feature : exact; 62 | } 63 | actions { 64 | aCheckRollover; 65 | } 66 | size : 2; 67 | } 68 | 69 | 70 | /*============================================================================== 71 | = Set Snapshot Case = 72 | = Similar to ingress. Sets snapshot_case based on the current snapshot ID and = 73 | = the packet's ID. Only runs when the packet has a snapshot header and the ID = 74 | = has not rolled over. = 75 | ==============================================================================*/ 76 | 77 | control ceSetSnapshotCaseNoRollover { 78 | /* 79 | * former_id = reg_snapshot_id_egress[effective_port] 80 | * if (former_id >= former_last_seen) 81 | * // port's snapshot ID hasn't wrapped either. compare normally 82 | * if (snapshot_header.snapshot_id > former_id) 83 | * reg_snapshot_id_egress[effective_port] = snapshot_header.snapshot_id 84 | * else 85 | * // former_id has wrapped and it should win 86 | */ 87 | apply(teUpdateSnapshotIdNoRollover); 88 | /* 89 | * switch_rollover_check = former_last_seen - former_id 90 | * switch_greater = former_id - snapshot_header.snapshot_id 91 | * packet_greater = snapshot_header.snapshot_id - former_id 92 | * snapshot_changed_check = snapshot_header.snapshot_id - former_last_seen 93 | */ 94 | apply(teCheckSnapshotCaseNoRollover); 95 | /* 96 | * if (switch_rollover_check) 97 | * // they rolled back, but we haven't yet 98 | * snapshot_case = 2 99 | * else 100 | * // neither of us has rolled back 101 | * if (switch_greater == 0 and packet_greater == 0) 102 | * snapshot_case = 0 103 | * else if (switch_greater == 0) 104 | * snapshot_case = 1 105 | * else if (packet_greater == 0) 106 | * snapshot_case = 2 107 | */ 108 | apply(teSetSnapshotCaseNoRollover); 109 | } 110 | 111 | /** 112 | * STAGE 5 113 | * Loads and possibly updates our current snapshot ID. This should only update 114 | * if the packet's snapshot ID is larger than our snapshot ID. 115 | * 116 | * Input: snapshot_metadata.effective_port 117 | * Output: metadata.former_id = reg_snapshot_id_egress[snapshot_metadata.effective_port] 118 | **/ 119 | @pragma stage 5 120 | table teUpdateSnapshotIdNoRollover { 121 | actions { aeUpdateSnapshotIdNoRollover; } 122 | default_action: aeUpdateSnapshotIdNoRollover(); 123 | size : 0; 124 | } 125 | 126 | action aeUpdateSnapshotIdNoRollover() { 127 | reUpdateSnapshotIdNoRollover(reg_snapshot_id_egress, snapshot_metadata.effective_port); 128 | } 129 | 130 | /** 131 | * See tiCheckSnapshotCaseNoRollover 132 | **/ 133 | table teCheckSnapshotCaseNoRollover { 134 | actions { aCheckSnapshotCaseRollover; } 135 | default_action: aCheckSnapshotCaseRollover(); 136 | size : 0; 137 | } 138 | 139 | /** 140 | * See tiSetSnapshotCaseNoRollover 141 | **/ 142 | table teSetSnapshotCaseNoRollover { 143 | reads { 144 | snapshot_metadata.switch_rollover_check : ternary; 145 | snapshot_metadata.switch_greater : ternary; 146 | snapshot_metadata.packet_greater : ternary; 147 | } 148 | actions { 149 | aSetSnapshotCase; 150 | } 151 | size : 8; 152 | } 153 | 154 | 155 | /*============================================================================== 156 | = Set Snapshot Case = 157 | = Similar to ingress. Sets snapshot_case based on the current snapshot ID and = 158 | = the packet's ID. Only runs when the packet has a snapshot header and the ID = 159 | = has rolled over. = 160 | ==============================================================================*/ 161 | 162 | control ceSetSnapshotCaseRollover { 163 | /* 164 | * former_id = reg_snapshot_id_ingress[effective_port] 165 | * if (former_id < former_last_seen) 166 | * // former_id wrapped too! compare normally 167 | * if (snapshot_header.snapshot_id > former_id) 168 | * reg_snapshot_id_ingress[effective_port] = snapshot_header.snapshot_id 169 | * else 170 | * // they haven't wrapped yet. We should win 171 | */ 172 | apply(teUpdateSnapshotIdRollover); // STAGE 5 173 | /* 174 | * switch_rollover_check = former_last_seen - former_id 175 | * switch_greater = former_id - snapshot_header.snapshot_id 176 | * packet_greater = snapshot_header.snapshot_id - former_id 177 | * snapshot_changed_check = snapshot_header.snapshot_id - former_last_seen 178 | */ 179 | apply(teCheckSnapshotCaseRollover); 180 | /* 181 | * if (switch_rollover_check) 182 | * if (switch_greater == 0 and packet_greater == 0) 183 | * snapshot_case = 0 184 | * else if (switch_greater == 0) 185 | * snapshot_case = 1 186 | * else if (packet_greater == 0) 187 | * snapshot_case = 2 188 | * else 189 | * // we rolled back but they haven't 190 | * snapshot_case = 1 191 | */ 192 | apply(teSetSnapshotCaseRollover); 193 | } 194 | 195 | /** 196 | * STAGE 5 197 | * See tiUpdateSnapshotIdRollover 198 | * 199 | * Input: snapshot_metadata.effective_port 200 | * Output: former_id = reg_snapshot_id_egress[snapshot_metadata.effective_port] 201 | * Postcondition: reg_snapshot_id_egress[snapshot_metadata.effective_port] is max seen 202 | * snapshot ID including rollover 203 | **/ 204 | @pragma stage 5 205 | table teUpdateSnapshotIdRollover { 206 | actions { aeUpdateSnapshotIdRollover; } 207 | default_action:aeUpdateSnapshotIdRollover(); 208 | size : 0; 209 | } 210 | 211 | action aeUpdateSnapshotIdRollover() { 212 | reUpdateSnapshotIdRollover(reg_snapshot_id_egress, snapshot_metadata.effective_port); 213 | } 214 | 215 | /** 216 | * See tiCheckSnapshotCaseNoRollover 217 | **/ 218 | table teCheckSnapshotCaseRollover { 219 | actions { aCheckSnapshotCaseRollover; } 220 | default_action: aCheckSnapshotCaseRollover(); 221 | size : 0; 222 | } 223 | 224 | /** 225 | * See tiSetSnapshotCaseRollover 226 | **/ 227 | table teSetSnapshotCaseRollover { 228 | reads { 229 | snapshot_metadata.switch_rollover_check : ternary; 230 | snapshot_metadata.switch_greater : ternary; 231 | snapshot_metadata.packet_greater : ternary; 232 | } 233 | actions { 234 | aSetSnapshotCase; 235 | } 236 | size : 8; 237 | } 238 | -------------------------------------------------------------------------------- /p4src/includes/p4/egress_WC.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the channel state functions of the egress pipeline of 18 | * Speedlight. 19 | **/ 20 | 21 | 22 | #include "egress_W.p4" 23 | 24 | 25 | /*============================================================================== 26 | = Take Snapshot = 27 | = Similar to ingress. Takes a snapshot if snapshot_case == 1 or 2. This check = 28 | = is performed in the match tables and extern functions to minimize branches. = 29 | ==============================================================================*/ 30 | 31 | control ceTakeSnapshotWithChannelState { 32 | /* 33 | * if (snapshot_case == 1) 34 | * reg_snapshot_value_egress[index] = current_reading 35 | * current_id = snapshot_header.snapshot_id 36 | * else if (snapshot_case == 2) 37 | * reg_snapshot_value_egress[index]++ 38 | * current_id = former_id 39 | */ 40 | apply(teUpdateSnapshotValue); 41 | /* 42 | * Notification: effective_port, snapshot_header.snapshot_id, 43 | * snapshot_metadata.current_reading, ingress_global_tstamp 44 | */ 45 | apply(teSendNotificationWithChannelState); 46 | } 47 | 48 | /** 49 | * Increments snapshot counter to account for the in-flight packet. 50 | * only run if snapshot_case == 2 and snapshot_feature != 2 51 | * second condition is taken care in the control block 52 | * 53 | * 1 _ x _ y: aeTakeSnapshot(x * MAX_PORT_NUM + y - 1) 54 | * 2 2 _ _: aNoOp 55 | * 2 _ _ x y: aeUpdateInflight(x * MAX_PORT_NUM + y - 1) 56 | 57 | * Input: current_index 58 | * Postcondition: reg_snapshot_value_egress[current_index]++ 59 | */ 60 | table teUpdateSnapshotValue { 61 | reads { 62 | snapshot_metadata.snapshot_case: exact; 63 | snapshot_metadata.snapshot_feature: ternary; 64 | snapshot_header.snapshot_id: ternary; 65 | snapshot_metadata.former_id: ternary; 66 | snapshot_metadata.effective_port : ternary; 67 | } 68 | actions { 69 | aeTakeSnapshot; 70 | aeUpdateInflight; 71 | aNoOp; 72 | } 73 | size : DOUBLE_SSTABLE_SIZE; 74 | } 75 | 76 | /** 77 | * Sends a notification to the CPU when a neighbor sends you a new snapshot ID. 78 | * In the traditional algorithm, we send a notification any time we get a new 79 | * snapshot from a neighbor. That's essentially what we're doing here. 80 | * 81 | * This should be after the register state is consistent 82 | * 83 | * if snapshot_changed_check == 0 84 | * aNoOp 85 | * elif snapshot_case == 1 && snapshot_feature == 1: 86 | * CPU_NEWSS_NEWNBR_EGRESS 87 | * elif snapshot_case == 1: 88 | * CPU_NEWSS_EGRESS 89 | * elif snapshot_feature == 1: 90 | * CPU_NEWNBR_EGRESS 91 | * else (snapshot_changed_check == 1 && snapshot_feature == 2) 92 | * aNoOp 93 | * 94 | * Input: snapshot_changed_check, snapshot_feature, snapshot_case 95 | * Postcondition: Notification has been sent to the CPU 96 | **/ 97 | table teSendNotificationWithChannelState { 98 | reads { 99 | snapshot_metadata.snapshot_changed_check : ternary; 100 | snapshot_metadata.snapshot_feature : ternary; 101 | snapshot_metadata.snapshot_case : ternary; 102 | } 103 | actions { 104 | aNoOp; 105 | aeSendNotification; 106 | } 107 | size : 8; 108 | } 109 | -------------------------------------------------------------------------------- /p4src/includes/p4/headers.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Headers and metadata for Ethernet, IPv4, and Speedlight. Metadata is not 18 | * forwarded between switches. 19 | * 20 | * Note that notify.p4 and notify_C.p4 contain the notification headers, which 21 | * which depend on the variant. 22 | **/ 23 | 24 | 25 | // Intrinsic metadata from the behavioral model 26 | header_type intrinsic_metadata_t { 27 | fields { 28 | mcast_grp : 4; 29 | egress_rid : 4; 30 | mcast_hash : 16; 31 | lf_field_list: 32; 32 | ingress_global_tstamp_msb16 : 16; 33 | ingress_global_tstamp_lsb32 : 32; 34 | resubmit_flag : 16; 35 | recirculate_flag : 16; 36 | } 37 | } 38 | 39 | metadata intrinsic_metadata_t intrinsic_metadata; 40 | 41 | 42 | // Normal Ethernet and IPv4 header definitions 43 | header_type ethernet_t { 44 | fields { 45 | dstAddr : 48; 46 | srcAddr : 48; 47 | etherType : 16; 48 | } 49 | } 50 | header ethernet_t ethernet; 51 | 52 | header_type ipv4_t { 53 | fields { 54 | version : 4; 55 | ihl : 4; 56 | diffserv : 8; 57 | totalLen : 16; 58 | identification : 16; 59 | flags : 3; 60 | fragOffset : 13; 61 | ttl : 8; 62 | protocol : 8; 63 | hdrChecksum : 16; 64 | srcAddr : 32; 65 | dstAddr: 32; 66 | } 67 | } 68 | header ipv4_t ipv4; 69 | 70 | header_type ipv4_option_t { 71 | fields { 72 | copyFlag : 1; 73 | optClass : 2; 74 | option: 5; 75 | optionLength: 8; 76 | } 77 | } 78 | header ipv4_option_t ipv4_option; 79 | 80 | 81 | // IPv4 Checksum computation 82 | field_list ipv4_field_list_snapshot { 83 | ipv4.version; 84 | ipv4.ihl; 85 | ipv4.diffserv; 86 | ipv4.totalLen; 87 | ipv4.identification; 88 | ipv4.flags; 89 | ipv4.fragOffset; 90 | ipv4.ttl; 91 | ipv4.protocol; 92 | ipv4.srcAddr; 93 | ipv4.dstAddr; 94 | ipv4_option.copyFlag; 95 | ipv4_option.optClass; 96 | ipv4_option.option; 97 | ipv4_option.optionLength; 98 | snapshot_header.snapshot_id; 99 | snapshot_header.pad_1; 100 | snapshot_header.port_id; 101 | snapshot_header.pad_2; 102 | } 103 | 104 | field_list_calculation ipv4_chksum_calc_snapshot { 105 | input { 106 | ipv4_field_list_snapshot; 107 | } 108 | algorithm : csum16; 109 | output_width: 16; 110 | } 111 | 112 | calculated_field ipv4.hdrChecksum { 113 | update ipv4_chksum_calc_snapshot; 114 | } 115 | 116 | 117 | // Snapshot headers 118 | header_type snapshot_header_t { 119 | fields { 120 | snapshot_id : 16; 121 | pad_1 : 7; 122 | port_id : 9; 123 | pad_2 : 16; 124 | } 125 | } 126 | header snapshot_header_t snapshot_header; 127 | 128 | header_type snapshot_metadata_t { 129 | fields { 130 | current_id : 16; 131 | current_reading: 32; 132 | pad_1 : 7; 133 | effective_port : 9; 134 | pad_2 : 6; 135 | snapshot_feature: 2; 136 | switch_rollover_check : 16 (saturating); 137 | switch_greater : 16 (saturating); 138 | packet_greater : 16 (saturating); 139 | pad_3 : 6; 140 | snapshot_case : 2; 141 | pad_4 : 4; 142 | temp_ihl : 4; 143 | former_id : 16; 144 | former_last_seen : 16; 145 | snapshot_changed_check : 16; 146 | snapshot_rollover_check : 16 (saturating); 147 | } 148 | } 149 | metadata snapshot_metadata_t snapshot_metadata; 150 | -------------------------------------------------------------------------------- /p4src/includes/p4/ingress.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the base ingress pipeline of Speedlight. 18 | **/ 19 | 20 | 21 | // STAGE DOESNT MATTER (only used in one branch) 22 | register reg_counter_ingress { 23 | width : COUNTER_WIDTH; 24 | instance_count : PORTS_TBL_SIZE; 25 | } 26 | 27 | // STAGE DOESNT MATTER (only used in one branch) 28 | register reg_last_seen_in_ingress { 29 | width : SNAPSHOT_ID_WIDTH; 30 | instance_count : NEIGHBORS_TBL_SIZE_ING; 31 | } 32 | 33 | // STAGE 5 ONLY 34 | register reg_snapshot_id_ingress { 35 | width : SNAPSHOT_ID_WIDTH; 36 | instance_count : PORTS_TBL_SIZE; 37 | } 38 | 39 | // STAGE DOESNT MATTER (only used in one branch) 40 | register reg_snapshot_value_ingress { 41 | width : COUNTER_WIDTH; 42 | instance_count: SSTABLE_SIZE; 43 | } 44 | 45 | action aNoOp() {} 46 | 47 | 48 | /*============================================================================== 49 | = SS Initialization = 50 | = Always runs and sets up effective_port and snapshot_feature = 51 | ==============================================================================*/ 52 | 53 | control ciInitializeSS { 54 | if (valid(ipv4_option)){ 55 | apply(tiCheckOption); 56 | } 57 | /* 58 | * if (snapshot_feature == 2) { 59 | * effective_port = snapshot_header.port_id 60 | * } else { 61 | * effective_port = lookup(ingress_port) 62 | * } 63 | */ 64 | apply(tiSetEffectivePort); 65 | } 66 | 67 | /* 68 | * Checks if the packet has a snapshot header and/or is a control plane snapshot 69 | * initiation message. Only packets from hosts should be missing the header. 70 | * 71 | * Input: ipv4_option.option 72 | * Output: if option == 31, snapshot_feature = 1 73 | * if option == 30, snapshot_feature = 2 74 | * otherwise, snapshot_feature = 0 75 | */ 76 | table tiCheckOption { 77 | reads { 78 | ipv4_option.option : exact; 79 | } 80 | actions { 81 | aiCheckOption; 82 | } 83 | max_size : 2; 84 | } 85 | 86 | action aiCheckOption(feature_val) { 87 | modify_field(snapshot_metadata.snapshot_feature, feature_val); 88 | } 89 | 90 | /* 91 | * Sets the effective port. 92 | * 93 | * -If the ingress port is from the CPU, we fake the effective port to be the 94 | * snapshot header's port_id. 95 | * -Otherwise, we look up the ingress port in a table that compresses the ids to 96 | * start at 0. 97 | * 98 | * Input: ingress_port, snapshot_header.port_id 99 | * Output: effective_port 100 | */ 101 | table tiSetEffectivePort { 102 | reads { 103 | snapshot_metadata.snapshot_feature : exact; 104 | standard_metadata.ingress_port : ternary; 105 | } 106 | actions { 107 | aiSetEffectivePort; 108 | aiFakeEffectivePort; 109 | } 110 | size : TWO_X_PORTS_PLUS_CPU; 111 | } 112 | 113 | action aiSetEffectivePort(portIndex){ 114 | modify_field(snapshot_metadata.effective_port, portIndex); 115 | } 116 | 117 | action aiFakeEffectivePort() { 118 | modify_field(snapshot_metadata.effective_port, snapshot_header.port_id); 119 | } 120 | 121 | 122 | /*============================================================================== 123 | = Add SS Header = 124 | = Runs when the packet does not have an SS header, i.e., snapshot_feature == 0 = 125 | ==============================================================================*/ 126 | 127 | control ciAddHeader { 128 | /* 129 | * current_id = reg_snapshot_id_ingress[effective_port] 130 | */ 131 | apply(tiGetSnapshotId); // STAGE 5 132 | /* 133 | * packet.addIpv4Option() 134 | * // => [Copy = 1, Class = 2, Number = 31, Length = 8] 135 | * packet.addSnapshotHeader() 136 | * // => [ID = TBD, port_id = TBD] 137 | * packet.IPv4.IHL += 2 138 | * packet.IPv4.totalLen += 8 139 | */ 140 | apply(tiAddHeader); 141 | } 142 | 143 | /** 144 | * STAGE 5 145 | * Loads the port's snapshot ID from a register. 146 | * 147 | * Input: snapshot_metadata.effective_port 148 | * Output: metadata.current_id = reg_snapshot_id_ingress[effective_port] 149 | **/ 150 | @pragma stage 5 151 | table tiGetSnapshotId { 152 | actions { aiGetSnapshotId; } 153 | default_action: aiGetSnapshotId(); 154 | size : 0; 155 | } 156 | 157 | action aiGetSnapshotId() { 158 | register_read(snapshot_metadata.current_id, reg_snapshot_id_ingress, 159 | snapshot_metadata.effective_port); 160 | } 161 | 162 | /** 163 | * Adds the snapshot header to the packet. Requires adding the IP option and 164 | * inserting the snapshot header after the IP header. 165 | * Note: snapshot_header values will be set at the end 166 | * 167 | * Preconditions: 168 | * Packet does not have snapshot header 169 | * Postconditions: 170 | * IPv4 IHL +2 and Total length +8 171 | * IPv4 header option [Copy = 1, Class = 2, Number = 31, Length = 8] 172 | * Snapshot header [ID = TBD, port_id = TBD, in = TBD] 173 | **/ 174 | table tiAddHeader { 175 | actions { aiAddHeader; } 176 | default_action: aiAddHeader(); 177 | size : 0; 178 | } 179 | 180 | action aiAddHeader() { 181 | add_header(ipv4_option); 182 | modify_field(ipv4_option.copyFlag, 1); 183 | modify_field(ipv4_option.option, 31); 184 | modify_field(ipv4_option.optionLength, 8); 185 | modify_field(ipv4_option.optClass, 2); 186 | 187 | add_to_field(ipv4.totalLen, 8); 188 | add_to_field(ipv4.ihl, 2); 189 | 190 | add_header(snapshot_header); 191 | } 192 | 193 | 194 | /*============================================================================== 195 | = Set Snapshot Case = 196 | = Sets snapshot_case based on the current snapshot ID and the packet's ID. = 197 | = Only runs when the packet has a snapshot header. This does not take rollover = 198 | = into account. = 199 | ==============================================================================*/ 200 | 201 | control ciSetSnapshotCase { 202 | /* 203 | * former_id = reg_snapshot_id_ingress[effective_port] 204 | * if (snapshot_header.snapshot_id > former_id) 205 | * reg_snapshot_id_ingress[effective_port] = snapshot_header.snapshot_id 206 | */ 207 | apply(tiUpdateSnapshotId); // stage 5 208 | /* 209 | * switch_greater = former_id - snapshot_header.snapshot_id 210 | * packet_greater = snapshot_header.snapshot_id - former_id 211 | */ 212 | apply(tiCheckSnapshotCase); 213 | /* 214 | * if (switch_greater == 0 and packet_greater == 0) 215 | * snapshot_case = 0 216 | * else if (switch_greater == 0) 217 | * snapshot_case = 1 218 | * else if (packet_greater == 0) 219 | * snapshot_case = 2 220 | */ 221 | apply(tiSetSnapshotCase); 222 | } 223 | 224 | /** 225 | * STAGE 5 226 | * Loads and possibly updates our current snapshot ID. This should only update 227 | * if the packet's snapshot ID is further than our snapshot ID. 228 | * 229 | * former_id = reg_snapshot_id_ingress[effective_port] 230 | * if (snapshot_header.snapshot_id > former_id) 231 | * reg_snapshot_id_ingress[effective_port] = snapshot_header.snapshot_id 232 | * 233 | * Input: effective_port, snapshot_header.snapshot_id 234 | * Output: former_id = reg_snapshot_id_ingress[effective_port] 235 | * Postcondition: reg_snapshot_id_ingress[effective_port] = 236 | * max(snapshot_header.snapshot_id, 237 | * reg_snapshot_id_ingress[effective_port]) 238 | **/ 239 | @pragma stage 5 240 | table tiUpdateSnapshotId { 241 | actions { aiUpdateSnapshotId; } 242 | default_action: aiUpdateSnapshotId(); 243 | size : 0; 244 | } 245 | 246 | action aiUpdateSnapshotId() { 247 | // signature of all custom functions: 248 | // all other metadata fields are loaded internally. 249 | riUpdateSnapshotId(reg_snapshot_id_ingress, 250 | snapshot_metadata.effective_port); 251 | } 252 | 253 | /** 254 | * Compare switch and packet. Subtraction is saturating. At most one should be 255 | * non-zero. 256 | * 257 | * Input: former_last_seen, former_id, snapshot_id 258 | * Output: switch_greater, packet_greater 259 | **/ 260 | table tiCheckSnapshotCase { 261 | actions { aCheckSnapshotCase; } 262 | default_action: aCheckSnapshotCase(); 263 | size : 0; 264 | } 265 | 266 | action aCheckSnapshotCase() { 267 | subtract(snapshot_metadata.switch_greater, 268 | snapshot_metadata.former_id, snapshot_header.snapshot_id); 269 | subtract(snapshot_metadata.packet_greater, 270 | snapshot_header.snapshot_id, snapshot_metadata.former_id); 271 | modify_field(snapshot_metadata.current_id, snapshot_metadata.former_id); 272 | } 273 | 274 | /** 275 | * Based on the difference between the node's snapshot ID and the packets 276 | * snapshot ID, decide what to do. We will never see 1+ 1+ because it's 277 | * saturating. 278 | * 279 | * 0 0 = forward (0) 280 | * 0 1+ = new ss (1) 281 | * 1+ 0 = in flight (2) 282 | * 283 | * For new SS, a value above 1 indicates that we skipped a snapshot. Rely on 284 | * the control plane to ignore any snapshot we skipped. Just worry about the 285 | * next one. 286 | * For in flight, a value above 1 also indicates we missed a packet in a 287 | * previous snapshot. That snapshot is now inconsistent. Again, only worry 288 | * about the last snapshot and rely on the control plane to ignore any 289 | * inconsistent snapshots. 290 | * 291 | * Input: snapshot_metadata.switch_greater, snapshot_metadata.packet_greater 292 | * Output: snapshot_metadata.snapshot_case (see below) 293 | **/ 294 | table tiSetSnapshotCase { 295 | reads { 296 | snapshot_metadata.switch_greater : ternary; 297 | snapshot_metadata.packet_greater : ternary; 298 | } 299 | actions { 300 | aSetSnapshotCase; 301 | } 302 | size : 8; 303 | } 304 | 305 | action aSetSnapshotCase(x) { 306 | modify_field(snapshot_metadata.snapshot_case, x); 307 | } 308 | 309 | 310 | /*============================================================================== 311 | = Take Snapshot = 312 | = Takes a snapshot if snapshot_case == 1. This check is performed in the match = 313 | = tables and extern functions to minimize branches. Note that the snapshot = 314 | = does not include channel state. = 315 | ==============================================================================*/ 316 | 317 | control ciTakeSnapshot { 318 | /* 319 | * if (snapshot_case == 1) 320 | * reg_snapshot_value_ingress[index] = current_reading 321 | * current_id = snapshot_header.snapshot_id 322 | */ 323 | apply(tiTakeSnapshot); 324 | /* 325 | * if (snapshot_case == 1) 326 | * Notification: effective_port, snapshot_header.snapshot_id, 327 | * current_reading, ingress_global_tstamp 328 | */ 329 | apply(tiSendNotification); 330 | } 331 | 332 | /** 333 | * Stores counter (pre-increment) into the snapshot register 334 | * Only executed if snapshot_case == 1 335 | * Input: effective_port, snapshot_id, current_reading 336 | * Postcondition: reg_snapshot_value_ingress[current_index] = current_reading 337 | */ 338 | table tiTakeSnapshot { 339 | reads { 340 | snapshot_metadata.snapshot_case : exact; 341 | snapshot_header.snapshot_id : exact; 342 | snapshot_metadata.effective_port : exact; 343 | } 344 | actions { 345 | aiTakeSnapshot; 346 | } 347 | size : DOUBLE_SSTABLE_SIZE; 348 | } 349 | 350 | action aiTakeSnapshot(index_val) { 351 | register_write(reg_snapshot_value_ingress, index_val, 352 | snapshot_metadata.current_reading); 353 | modify_field(snapshot_metadata.current_id, snapshot_header.snapshot_id); 354 | } 355 | 356 | /** 357 | * Sends a notification to the CPU when a neighbor sends you a new snapshot ID. 358 | * In the traditional algorithm, we send a notification any time we get a new 359 | * snapshot from a neighbor. That's essentially what we're doing here. 360 | * 361 | * This should be after the register state is consistent 362 | * 363 | * if snapshot_case == 1: 364 | * CPU_NEWSS_INGRESS 365 | * else 366 | * aNoOp 367 | * 368 | * Input: snapshot_changed_check, snapshot_feature, snapshot_case 369 | * Postcondition: Notification has been sent to the CPU 370 | **/ 371 | table tiSendNotification { 372 | reads { 373 | snapshot_metadata.snapshot_case : exact; 374 | } 375 | actions { 376 | aiSendNotification; // in notify(_C).p4 377 | aNoOp; 378 | } 379 | default_action: aNoOp(); 380 | size : 2; 381 | } 382 | 383 | 384 | /*============================================================================== 385 | = Forward CPU Initiation = 386 | = Runs when the packet is a initiation from the CPU. If so, forward it = 387 | = without modification to the egress processing unit of the same port. = 388 | ==============================================================================*/ 389 | 390 | /** 391 | * Just forward the raw packet to this port's egress port. Don't update the 392 | * snapshot ID or port id 393 | */ 394 | table tiForwardInitiation { 395 | reads { 396 | snapshot_metadata.effective_port : exact; 397 | } 398 | actions { 399 | aiForwardInitiation; 400 | } 401 | size : NUM_PORTS; 402 | } 403 | 404 | action aiForwardInitiation(port) { 405 | modify_field(standard_metadata.egress_spec, port); 406 | } 407 | 408 | 409 | /*============================================================================== 410 | = Update Snapshot Header = 411 | = If the packet is NOT an initiation from the CPU, i.e., a normal packet, we = 412 | = need to update the snapshot header. = 413 | ==============================================================================*/ 414 | 415 | /** 416 | * Update the packet's snapshot header. 417 | * The snapshot header 418 | * 419 | * Input: egress_port, ingress_port, current_id 420 | * Postcondition: snapshot header = [snapshot_id: current_id, 421 | * port_id: ingress_port] 422 | */ 423 | table tiSetSnapHeader { 424 | actions { aiSetSnapInfo; } 425 | default_action: aiSetSnapInfo(); 426 | size : 0; 427 | } 428 | 429 | action aiSetSnapInfo() { 430 | modify_field(snapshot_header.snapshot_id, snapshot_metadata.current_id); 431 | modify_field(snapshot_header.port_id, snapshot_metadata.effective_port); 432 | } 433 | -------------------------------------------------------------------------------- /p4src/includes/p4/ingress_W.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the wraparound functions of the ingress pipeline of 18 | * Speedlight. 19 | **/ 20 | 21 | 22 | #include "ingress.p4" 23 | 24 | 25 | /*============================================================================== 26 | = Get Last Seen and Check Rollover = 27 | = Fetches and updates the former last seen register, then uses it to check if = 28 | = the neighbor's snapshot id has rolled over since the last time they sent us = 29 | = a message. = 30 | ==============================================================================*/ 31 | 32 | /** 33 | * Get and update the last seen snapshot ID from my neighbor. The index that 34 | * gets read and updated is given by: 35 | * 36 | * if (snapshot_feature = 2) 37 | * current_index = (effective_port - 1) * 2 38 | * else 39 | * current_index = (effective_port - 1) * 2 + 1 40 | * 41 | * 1 2 : 0 42 | * 1 1 : 1 43 | * 2 2 : 2 44 | * 2 1 : 3 45 | * 3 2 : 4 46 | * 3 1 : 5 47 | * 48 | * Input: effective_port, snapshot_feature, snapshot_header.snapshot_id 49 | * Output: former_last_seen 50 | * Postcondition: reg_last_seen_in_ingress[index] = snapshot_id 51 | */ 52 | table tiUpdateLastSeen { 53 | reads { 54 | snapshot_metadata.effective_port : ternary; 55 | snapshot_metadata.snapshot_feature : ternary; 56 | } 57 | actions { 58 | aiUpdateLastSeen; 59 | } 60 | size : TWO_X_PORTS; 61 | } 62 | 63 | action aiUpdateLastSeen(index_val) { 64 | riUpdateLastSeen(reg_last_seen_in_ingress, index_val); 65 | } 66 | 67 | /** 68 | * Computes helper values to check if the neighbor has rolled over its SS ID. 69 | * Ex: former_last_seen = 1, snapshot_id = 1 70 | * => snapshot_changed_check = 0, snapshot_rollover_check = 0 71 | * former_last_seen = 3, snapshot_id = 4 72 | * => snapshot_changed_check = 1, snapshot_rollover_check = 0 73 | * former_last_seen = 256, snapshot_id = 0 74 | * => snapshot_changed_check = -256, snapshot_rollover_check = 256 75 | * 76 | * Note that snapshot_header may not exist, but in such cases, we never use 77 | * these values. This needs to be outside the if condition or else the compiler 78 | * won't be able to place it. 79 | * 80 | * Input: former_last_seen, snapshot_header.snapshot_id 81 | * Output: snapshot_changed_check, snapshot_rollover_check 82 | **/ 83 | table tiCheckRollover { 84 | reads { 85 | snapshot_metadata.snapshot_feature : exact; 86 | } 87 | actions { 88 | aCheckRollover; 89 | } 90 | size : 2; 91 | } 92 | 93 | action aCheckRollover() { 94 | subtract(snapshot_metadata.snapshot_changed_check, 95 | snapshot_header.snapshot_id, snapshot_metadata.former_last_seen); 96 | subtract(snapshot_metadata.snapshot_rollover_check, 97 | snapshot_metadata.former_last_seen, snapshot_header.snapshot_id); 98 | } 99 | 100 | 101 | /*============================================================================== 102 | = Set Snapshot Case = 103 | = Sets snapshot_case based on the current snapshot ID and the packet's ID. = 104 | = Only runs when the packet has a snapshot header and the ID has not rolled = 105 | = over. = 106 | ==============================================================================*/ 107 | 108 | control ciSetSnapshotCaseNoRollover { 109 | /* 110 | * former_id = reg_snapshot_id_ingress[effective_port] 111 | * if (former_id >= former_last_seen) 112 | * // port's snapshot ID hasn't wrapped either. compare normally 113 | * if (snapshot_header.snapshot_id > former_id) 114 | * reg_snapshot_id_ingress[effective_port] 115 | * = snapshot_header.snapshot_id 116 | * else 117 | * // former_id has wrapped and it should win 118 | */ 119 | apply(tiUpdateSnapshotIdNoRollover); // STAGE 5 120 | /* 121 | * switch_rollover_check = former_last_seen - former_id 122 | * switch_greater = former_id - snapshot_header.snapshot_id 123 | * packet_greater = snapshot_header.snapshot_id - former_id 124 | * snapshot_changed_check = snapshot_header.snapshot_id - former_last_seen 125 | */ 126 | apply(tiCheckSnapshotCaseNoRollover); 127 | /* 128 | * if (switch_rollover_check) 129 | * // they rolled back, but we haven't yet 130 | * snapshot_case = 2 131 | * else 132 | * // neither of us has rolled back 133 | * if (switch_greater == 0 and packet_greater == 0) 134 | * snapshot_case = 0 135 | * else if (switch_greater == 0) 136 | * snapshot_case = 1 137 | * else if (packet_greater == 0) 138 | * snapshot_case = 2 139 | */ 140 | apply(tiSetSnapshotCaseNoRollover); 141 | } 142 | 143 | /** 144 | * STAGE 5 145 | * Loads and possibly updates our current snapshot ID. This should only update 146 | * if the packet's snapshot ID is further than our snapshot ID. 147 | * 148 | * former_id = reg_snapshot_id_ingress[effective_port] 149 | * if (former_id >= former_last_seen) 150 | * // port's snapshot ID hasn't wrapped either. compare normally 151 | * if (snapshot_header.snapshot_id > former_id) 152 | * reg_snapshot_id_ingress[effective_port] = snapshot_header.snapshot_id 153 | * else 154 | * // former_id has wrapped and it should win 155 | * 156 | * Input: effective_port, snapshot_header.snapshot_id 157 | * Output: former_id = reg_snapshot_id_ingress[effective_port] 158 | * Postcondition: reg_snapshot_id_ingress[effective_port] = 159 | * max(snapshot_header.snapshot_id, 160 | * reg_snapshot_id_ingress[effective_port]) 161 | * includes wrapping/rollover 162 | **/ 163 | @pragma stage 5 164 | table tiUpdateSnapshotIdNoRollover { 165 | actions { aiUpdateSnapshotIdNoRollover; } 166 | default_action: aiUpdateSnapshotIdNoRollover(); 167 | size : 0; 168 | } 169 | 170 | action aiUpdateSnapshotIdNoRollover() { 171 | riUpdateSnapshotIdNoRollover(reg_snapshot_id_ingress, 172 | snapshot_metadata.effective_port); 173 | } 174 | 175 | /** 176 | * General calculations that doesn't fit anywhere else. This does 2 things: 177 | * 1. calculates the relationship between the packet's ID and the port's ID 178 | * 2. calculates IHL value after removal of TCP Options header. This MUST 179 | * be calculated indirectly like this or else the compiler will complain 180 | * about dependencies 181 | * 182 | * Input: former_last_seen, former_id, snapshot_id 183 | **/ 184 | table tiCheckSnapshotCaseNoRollover { 185 | actions { aCheckSnapshotCaseRollover; } 186 | default_action: aCheckSnapshotCaseRollover(); 187 | size : 0; 188 | } 189 | 190 | action aCheckSnapshotCaseRollover() { 191 | subtract(snapshot_metadata.switch_rollover_check, 192 | snapshot_metadata.former_last_seen, snapshot_metadata.former_id); 193 | subtract(snapshot_metadata.switch_greater, 194 | snapshot_metadata.former_id, snapshot_header.snapshot_id); 195 | subtract(snapshot_metadata.packet_greater, 196 | snapshot_header.snapshot_id, snapshot_metadata.former_id); 197 | subtract(snapshot_metadata.snapshot_changed_check, 198 | snapshot_header.snapshot_id, snapshot_metadata.former_last_seen); 199 | modify_field(snapshot_metadata.current_id, snapshot_metadata.former_id); 200 | } 201 | 202 | /** 203 | * Based on the difference between the node's snapshot ID and the packets 204 | * snapshot ID, decide what to do. We will never see 1+ 1+ because it's 205 | * saturating. 206 | * 207 | * 0 0 0 = forward (0) 208 | * 0 0 1+ = new ss (1) 209 | * 0 1+ 0 = in flight (2) 210 | * 1+ _ _ = in flight (2) // switch has rolled back, but we haven't 211 | * 212 | * For new SS, a value above 1 indicates that we skipped a snapshot. Rely on 213 | * the control plane to ignore any snapshot we skipped. Just worry about the 214 | * next one. 215 | * For in flight, a value above 1 also indicates we missed a packet in a 216 | * previous snapshot. That snapshot is now inconsistent. Again, only worry 217 | * about the last snapshot and rely on the control plane to ignore any 218 | * inconsistent snapshots. 219 | * 220 | * Input: snapshot_metadata.switch_greater, snapshot_metadata.packet_greater 221 | * Output: snapshot_metadata.snapshot_on (see below) 222 | **/ 223 | table tiSetSnapshotCaseNoRollover { 224 | reads { 225 | snapshot_metadata.switch_rollover_check : ternary; 226 | snapshot_metadata.switch_greater : ternary; 227 | snapshot_metadata.packet_greater : ternary; 228 | } 229 | actions { 230 | aSetSnapshotCase; 231 | } 232 | size : 8; 233 | } 234 | 235 | 236 | /*============================================================================== 237 | = Set Snapshot Case = 238 | = Sets snapshot_case based on the current snapshot ID and the packet's ID. = 239 | = Only runs when the packet has a snapshot header and the ID has rolled over. = 240 | ==============================================================================*/ 241 | 242 | control ciSetSnapshotCaseRollover { 243 | /* 244 | * former_id = reg_snapshot_id_ingress[effective_port] 245 | * if (former_id < former_last_seen) 246 | * // former_id wrapped too! compare normally 247 | * if (snapshot_header.snapshot_id > former_id) 248 | * reg_snapshot_id_ingress[effective_port] 249 | * = snapshot_header.snapshot_id 250 | * else 251 | * // they haven't wrapped yet. We should win 252 | */ 253 | apply(tiUpdateSnapshotIdRollover); // STAGE 5 254 | /* 255 | * switch_rollover_check = former_last_seen - former_id 256 | * switch_greater = former_id - snapshot_header.snapshot_id 257 | * packet_greater = snapshot_header.snapshot_id - former_id 258 | * snapshot_changed_check = snapshot_header.snapshot_id - former_last_seen 259 | */ 260 | apply(tiCheckSnapshotCaseRollover); 261 | /* 262 | * if (switch_rollover_check) 263 | * if (switch_greater == 0 and packet_greater == 0) 264 | * snapshot_case = 0 265 | * else if (switch_greater == 0) 266 | * snapshot_case = 1 267 | * else if (packet_greater == 0) 268 | * snapshot_case = 2 269 | * else 270 | * // we rolled back but they haven't 271 | * snapshot_case = 1 272 | */ 273 | apply(tiSetSnapshotCaseRollover); 274 | } 275 | 276 | /** 277 | * STAGE 5 278 | * Loads and possibly updates our current snapshot ID. This should only update 279 | * if the packet's snapshot ID is larger than our snapshot ID. 280 | * 281 | * former_id = reg_snapshot_id_ingress[effective_port] 282 | * if (former_id < former_last_seen) 283 | * // port's snapshot ID wrapped too! compare normally 284 | * if (snapshot_header.snapshot_id > former_id) 285 | * reg_snapshot_id_ingress[effective_port] = snapshot_header.snapshot_id 286 | * else 287 | * // they haven't wrapped yet. We should win 288 | * 289 | * Input: effective_port, snapshot_header.snapshot_id 290 | * Output: former_id = reg_snapshot_id_ingress[effective_port] 291 | * Postcondition: reg_snapshot_id_ingress[effective_port] = 292 | * max(snapshot_header.snapshot_id, 293 | * reg_snapshot_id_ingress[effective_port]) 294 | * includes wrapping/rollover 295 | **/ 296 | @pragma stage 5 297 | table tiUpdateSnapshotIdRollover { 298 | actions { aiUpdateSnapshotIdRollover; } 299 | default_action: aiUpdateSnapshotIdRollover(); 300 | size : 0; 301 | } 302 | 303 | action aiUpdateSnapshotIdRollover() { 304 | riUpdateSnapshotIdRollover(reg_snapshot_id_ingress, 305 | snapshot_metadata.effective_port); 306 | } 307 | 308 | /** 309 | * See tiCheckSnapshotCaseNoRollover 310 | **/ 311 | table tiCheckSnapshotCaseRollover { 312 | actions { aCheckSnapshotCaseRollover; } 313 | default_action: aCheckSnapshotCaseRollover(); 314 | size : 0; 315 | } 316 | 317 | /** 318 | * Similar to tiSetSnapshotCaseNoRollover, but we have rolled over. 319 | * 320 | * 0 _ _ = new ss (1) // switch has rolled back, but we haven't 321 | * 1+ 0 0 = forward (0) 322 | * 1+ 0 1+ = new ss (1) 323 | * 1+ 1+ 0 = in flight (2) 324 | * 325 | * Input: snapshot_metadata.switch_greater, snapshot_metadata.packet_greater 326 | * Output: snapshot_metadata.snapshot_on (see below) 327 | **/ 328 | table tiSetSnapshotCaseRollover { 329 | reads { 330 | snapshot_metadata.switch_rollover_check : ternary; 331 | snapshot_metadata.switch_greater : ternary; 332 | snapshot_metadata.packet_greater : ternary; 333 | } 334 | actions { 335 | aSetSnapshotCase; 336 | } 337 | size : 8; 338 | } 339 | -------------------------------------------------------------------------------- /p4src/includes/p4/ingress_WC.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Tables and actions for the channel state functions of the ingress pipeline of 18 | * Speedlight. 19 | **/ 20 | 21 | 22 | #include "ingress_W.p4" 23 | 24 | 25 | /*============================================================================== 26 | = Take Snapshot = 27 | = Takes a snapshot if snapshot_case == 1 or 2. This check is performed in the = 28 | = match tables and extern functions to minimize branches. = 29 | ==============================================================================*/ 30 | 31 | control ciTakeSnapshotWithChannelState { 32 | /* 33 | * if (snapshot_case == 1) 34 | * reg_snapshot_value_ingress[index] = current_reading 35 | * current_id = snapshot_header.snapshot_id 36 | * else if (snapshot_case == 2) 37 | * reg_snapshot_value_ingress[index]++ 38 | * current_id = former_id 39 | */ 40 | apply(tiUpdateSnapshotValue); 41 | /* 42 | * Notification: effective_port, snapshot_header.snapshot_id, 43 | * snapshot_metadata.current_reading, ingress_global_tstamp 44 | */ 45 | apply(tiSendNotificationWithChannelState); 46 | } 47 | 48 | /** 49 | * Increments snapshot counter to account for the in-flight packet. 50 | * only run if snapshot_case == 2 and snapshot_feature != 2 51 | * second condition is taken care in the control block 52 | * 53 | * 1 _ x _ y: aiTakeSnapshot(x * MAX_PORT_NUM + y - 1) 54 | * 2 2 _ _ _: aNoOp 55 | * 2 _ _ x y: aiUpdateInflight(x * MAX_PORT_NUM + y - 1) 56 | * 57 | * Input: current_index 58 | * Postcondition: [case==1] reg_snapshot_value_ingress[index] = current_reading 59 | * [case==2] reg_snapshot_value_ingress[index]++ 60 | */ 61 | table tiUpdateSnapshotValue { 62 | reads { 63 | snapshot_metadata.snapshot_case: exact; 64 | snapshot_metadata.snapshot_feature: ternary; 65 | snapshot_header.snapshot_id: ternary; 66 | snapshot_metadata.former_id: ternary; 67 | snapshot_metadata.effective_port : ternary; 68 | } 69 | actions { 70 | aiTakeSnapshot; 71 | aiUpdateInflight; 72 | aNoOp; 73 | } 74 | size : DOUBLE_SSTABLE_SIZE; 75 | } 76 | 77 | /** 78 | * Sends a notification to the CPU when a neighbor sends you a new snapshot ID. 79 | * In the traditional algorithm, we send a notification any time we get a new 80 | * snapshot from a neighbor. That's essentially what we're doing here. 81 | * 82 | * This should be after the register state is consistent 83 | * 84 | * if snapshot_changed_check == 0 85 | * aNoOp 86 | * elif snapshot_case == 1 && snapshot_feature == 1: 87 | * CPU_NEWSS_NEWNBR_INGRESS 88 | * elif snapshot_case == 1: 89 | * CPU_NEWSS_INGRESS 90 | * elif snapshot_feature == 1: 91 | * CPU_NEWNBR_INGRESS 92 | * else (snapshot_changed_check == 1 && snapshot_feature == 2) 93 | * aNoOp 94 | * 95 | * Input: snapshot_changed_check, snapshot_feature, snapshot_case 96 | * Postcondition: Notification has been sent to the CPU 97 | **/ 98 | table tiSendNotificationWithChannelState { 99 | reads { 100 | snapshot_metadata.snapshot_changed_check : ternary; 101 | snapshot_metadata.snapshot_feature : ternary; 102 | snapshot_metadata.snapshot_case : ternary; 103 | } 104 | actions { 105 | aNoOp; 106 | aiSendNotification; 107 | } 108 | size : 8; 109 | } 110 | -------------------------------------------------------------------------------- /p4src/includes/p4/notify.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Non-channel state version of notification. We generate the notification's 18 | * metadata but don't add the header immediately because we don't want to modify 19 | * the original packet. On final egress, we add the header and format it for 20 | * the CPU. 21 | **/ 22 | 23 | header_type tocpu_t { 24 | fields { 25 | message_type : 8; 26 | current_port : 16; 27 | current_id : 16; 28 | } 29 | } 30 | metadata tocpu_t tocpu_metadata; 31 | header tocpu_t tocpu_notif; 32 | 33 | field_list flLastSeen { 34 | tocpu_metadata.message_type; 35 | tocpu_metadata.current_port; 36 | tocpu_metadata.current_id; 37 | } 38 | 39 | 40 | action aiSendNotification(message_type) { 41 | modify_field(tocpu_metadata.message_type, message_type); 42 | modify_field(tocpu_metadata.current_port, snapshot_metadata.effective_port); 43 | modify_field(tocpu_metadata.current_id, snapshot_header.snapshot_id); 44 | 45 | clone_ingress_pkt_to_egress(CPU_INGRESS_MIRROR_ID, flLastSeen); 46 | } 47 | 48 | action aeSendNotification(message_type) { 49 | modify_field(tocpu_metadata.message_type, message_type); 50 | modify_field(tocpu_metadata.current_port, snapshot_metadata.effective_port); 51 | modify_field(tocpu_metadata.current_id, snapshot_header.snapshot_id); 52 | 53 | clone_egress_pkt_to_egress(CPU_EGRESS_MIRROR_ID, flLastSeen); 54 | } 55 | 56 | action aeFormatForCpu() { 57 | // remove unnecessary headers 58 | remove_header(snapshot_header); 59 | remove_header(ipv4_option); 60 | remove_header(ipv4); 61 | 62 | // add export header 63 | add_header(tocpu_notif); 64 | 65 | // fill export header 66 | modify_field(tocpu_notif.message_type, tocpu_metadata.message_type); 67 | modify_field(tocpu_notif.current_port, tocpu_metadata.current_port); 68 | modify_field(tocpu_notif.current_id, tocpu_metadata.current_id); 69 | 70 | // change ethertype to snapshot ether 71 | modify_field(ethernet.etherType, ETHERTYPE_TOCPU); 72 | } 73 | -------------------------------------------------------------------------------- /p4src/includes/p4/notify_C.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Channel state version of notification. We generate the notification's 18 | * metadata but don't add the header immediately because we don't want to modify 19 | * the original packet. On final egress, we add the header and format it for 20 | * the CPU. 21 | **/ 22 | 23 | header_type tocpu_t { 24 | fields { 25 | message_type : 8; 26 | current_port : 16; 27 | neighbor_port : 16; 28 | former_id : 16; 29 | current_id : 16; 30 | former_last_seen : 16; 31 | current_last_seen : 16; 32 | } 33 | } 34 | metadata tocpu_t tocpu_metadata; 35 | header tocpu_t tocpu_notif; 36 | 37 | field_list flLastSeen { 38 | tocpu_metadata.message_type; 39 | tocpu_metadata.current_port; 40 | tocpu_metadata.neighbor_port; 41 | tocpu_metadata.former_id; 42 | tocpu_metadata.current_id; 43 | tocpu_metadata.former_last_seen; 44 | tocpu_metadata.current_last_seen; 45 | } 46 | 47 | 48 | action aiSendNotification(message_type) { 49 | modify_field(tocpu_metadata.message_type, message_type); 50 | modify_field(tocpu_metadata.current_port, snapshot_metadata.effective_port); 51 | modify_field(tocpu_metadata.neighbor_port, snapshot_header.port_id); 52 | modify_field(tocpu_metadata.former_id, snapshot_metadata.former_id); 53 | modify_field(tocpu_metadata.current_id, snapshot_header.snapshot_id); 54 | modify_field(tocpu_metadata.former_last_seen, 55 | snapshot_metadata.former_last_seen); 56 | modify_field(tocpu_metadata.current_last_seen, snapshot_header.snapshot_id); 57 | 58 | clone_ingress_pkt_to_egress(CPU_INGRESS_MIRROR_ID, flLastSeen); 59 | } 60 | 61 | action aeSendNotification(message_type) { 62 | modify_field(tocpu_metadata.message_type, message_type); 63 | modify_field(tocpu_metadata.current_port, snapshot_metadata.effective_port); 64 | modify_field(tocpu_metadata.neighbor_port , snapshot_header.port_id); 65 | modify_field(tocpu_metadata.former_id, snapshot_metadata.former_id); 66 | modify_field(tocpu_metadata.current_id, snapshot_header.snapshot_id); 67 | modify_field(tocpu_metadata.former_last_seen, 68 | snapshot_metadata.former_last_seen); 69 | modify_field(tocpu_metadata.current_last_seen, snapshot_header.snapshot_id); 70 | 71 | clone_egress_pkt_to_egress(CPU_EGRESS_MIRROR_ID, flLastSeen); 72 | } 73 | 74 | action aeFormatForCpu() { 75 | // remove unnecessary headers 76 | remove_header(snapshot_header); 77 | remove_header(ipv4_option); 78 | remove_header(ipv4); 79 | 80 | // add export header 81 | add_header(tocpu_notif); 82 | 83 | // fill export header 84 | modify_field(tocpu_notif.message_type, tocpu_metadata.message_type); 85 | modify_field(tocpu_notif.current_port, tocpu_metadata.current_port); 86 | modify_field(tocpu_notif.neighbor_port, tocpu_metadata.neighbor_port); 87 | modify_field(tocpu_notif.former_id, tocpu_metadata.former_id); 88 | modify_field(tocpu_notif.current_id, tocpu_metadata.current_id); 89 | modify_field(tocpu_notif.former_last_seen, tocpu_metadata.former_last_seen); 90 | modify_field(tocpu_notif.current_last_seen, 91 | tocpu_metadata.current_last_seen); 92 | 93 | // change ethertype to snapshot ether 94 | modify_field(ethernet.etherType, ETHERTYPE_TOCPU); 95 | } 96 | -------------------------------------------------------------------------------- /p4src/includes/p4/parser.p4: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Parser for every incoming packet. We handle the following formats: 18 | * - Ethernet 19 | * - Ethernet | IPv4 20 | * - Ethernet | IPv4 | IPv4 Option | Snapshot 21 | * We create the following, but should never see it in the parser: 22 | * - Ethernet | Snapshot Notification 23 | **/ 24 | 25 | #define ETHERTYPE_IPV4 0x0800 26 | #define IPV4_HEADER_LEN 5 27 | #define IPV4_OPTION_SS 31 28 | #define IPV4_OPTION_SS_CPU 30 29 | 30 | parser start { 31 | return parse_ethernet; 32 | } 33 | 34 | parser parse_ethernet { 35 | extract(ethernet); 36 | return select(latest.etherType) { 37 | ETHERTYPE_IPV4 : parse_ipv4; 38 | 0x0000 mask 0xFFFF : ingress; 39 | default: parse_tocpu; 40 | // default never fires because the 41 | // mask case matches all. Default is just 42 | // here to tell the deparser where the 43 | // tocpu header goes. 44 | } 45 | } 46 | 47 | parser parse_ipv4 { 48 | extract(ipv4); 49 | return select(latest.ihl) { 50 | IPV4_HEADER_LEN : ingress; 51 | default: parse_ipv4_option; 52 | } 53 | } 54 | 55 | parser parse_ipv4_option { 56 | extract(ipv4_option); 57 | return select(latest.option) { 58 | IPV4_OPTION_SS : parse_snapshot; 59 | IPV4_OPTION_SS_CPU : parse_snapshot; 60 | default: ingress; 61 | } 62 | } 63 | 64 | parser parse_snapshot { 65 | extract(snapshot_header); 66 | return ingress; 67 | } 68 | 69 | // Again, should never be called. Just here to specify header order. 70 | parser parse_tocpu { 71 | extract(tocpu_notif); 72 | return ingress; 73 | } -------------------------------------------------------------------------------- /p4src/primitives/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /simple_switch 3 | /*.pcap 4 | sswitch_runtime 5 | simple_switch_CLI 6 | -------------------------------------------------------------------------------- /p4src/primitives/primitives.append: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Stateful speedlight externs. See relevant p4 files for documentation of 18 | * functionality. 19 | **/ 20 | 21 | class riUpdateSnapshotId 22 | : public ActionPrimitive { 23 | void operator ()(RegisterArray ®, const Data &idx) { 24 | auto i = idx.get_uint(); 25 | 26 | get_field("snapshot_metadata.former_id").set(reg[i]); 27 | // test condition. 28 | if (get_field("snapshot_header.snapshot_id") > reg[i]) { 29 | reg[i].set(get_field("snapshot_header.snapshot_id")); 30 | } 31 | 32 | BMLOG_TRACE_PKT(get_packet(), 33 | "riUpdateSnapshotId '{}' at index {} read value {}", 34 | reg.get_name(), i, reg[i]); 35 | } 36 | }; 37 | 38 | REGISTER_PRIMITIVE(riUpdateSnapshotId); 39 | 40 | 41 | class riUpdateSnapshotIdNoRollover 42 | : public ActionPrimitive { 43 | void operator ()(RegisterArray ®, const Data &idx) { 44 | auto i = idx.get_uint(); 45 | 46 | get_field("snapshot_metadata.former_id").set(reg[i]); 47 | // test condition. 48 | if ((reg[i] >= get_field("snapshot_metadata.former_last_seen")) && 49 | (get_field("snapshot_header.snapshot_id") > reg[i]) 50 | ) 51 | { 52 | reg[i].set(get_field("snapshot_header.snapshot_id")); 53 | } 54 | 55 | BMLOG_TRACE_PKT(get_packet(), 56 | "riUpdateSnapshotIdNoRollover '{}' at index {} read value {}", 57 | reg.get_name(), i, reg[i]); 58 | } 59 | }; 60 | 61 | REGISTER_PRIMITIVE(riUpdateSnapshotIdNoRollover); 62 | 63 | 64 | class riUpdateSnapshotIdRollover 65 | : public ActionPrimitive { 66 | void operator ()(RegisterArray ®, const Data &idx) { 67 | auto i = idx.get_uint(); 68 | 69 | get_field("snapshot_metadata.former_id").set(reg[i]); 70 | // test condition. 71 | if ((reg[i] < get_field("snapshot_metadata.former_last_seen")) && 72 | (get_field("snapshot_header.snapshot_id") > reg[i]) 73 | ) 74 | { 75 | reg[i].set(get_field("snapshot_header.snapshot_id")); 76 | } else { 77 | if (reg[i] >= get_field("snapshot_metadata.former_last_seen")) { 78 | reg[i].set(get_field("snapshot_header.snapshot_id")); 79 | } 80 | } 81 | 82 | BMLOG_TRACE_PKT(get_packet(), 83 | "riUpdateSnapshotIdRollover '{}' at index {} read value {}", 84 | reg.get_name(), i, reg[i]); 85 | } 86 | }; 87 | 88 | REGISTER_PRIMITIVE(riUpdateSnapshotIdRollover); 89 | 90 | 91 | class riUpdateLastSeen 92 | : public ActionPrimitive { 93 | void operator ()(RegisterArray ®, const Data &idx) { 94 | auto i = idx.get_uint(); 95 | 96 | get_field("snapshot_metadata.former_last_seen").set(reg[i]); 97 | 98 | reg[i].set(get_field("snapshot_header.snapshot_id")); 99 | 100 | BMLOG_TRACE_PKT(get_packet(), 101 | "riUpdateLastSeen '{}' at index {} read value {}", 102 | reg.get_name(), i, reg[i]); 103 | } 104 | }; 105 | 106 | REGISTER_PRIMITIVE(riUpdateLastSeen); 107 | 108 | 109 | class riReadAndUpdateNotification 110 | : public ActionPrimitive { 111 | void operator ()(RegisterArray ®, const Data &idx) { 112 | auto i = idx.get_uint(); 113 | 114 | auto rVal = reg[i].get(); 115 | reg[i].set(rVal + 1); 116 | 117 | BMLOG_TRACE_PKT(get_packet(), 118 | "riReadAndUpdateNotification '{}' at index {} read value {}", 119 | reg.get_name(), i, reg[i]); 120 | } 121 | }; 122 | 123 | REGISTER_PRIMITIVE(riReadAndUpdateNotification); 124 | 125 | 126 | class reUpdateLastSeen 127 | : public ActionPrimitive { 128 | void operator ()(RegisterArray ®, const Data &idx) { 129 | auto i = idx.get_uint(); 130 | 131 | get_field("snapshot_metadata.former_last_seen").set(reg[i]); 132 | 133 | reg[i].set(get_field("snapshot_header.snapshot_id")); 134 | 135 | BMLOG_TRACE_PKT(get_packet(), 136 | "reUpdateLastSeen '{}' at index {} read value {}", 137 | reg.get_name(), i, reg[i]); 138 | } 139 | }; 140 | REGISTER_PRIMITIVE(reUpdateLastSeen); 141 | 142 | 143 | class reUpdateSnapshotId 144 | : public ActionPrimitive { 145 | void operator ()(RegisterArray ®, const Data &idx) { 146 | auto i = idx.get_uint(); 147 | 148 | get_field("snapshot_metadata.former_id").set(reg[i]); 149 | // test condition. 150 | if (get_field("snapshot_header.snapshot_id") > reg[i]) { 151 | reg[i].set(get_field("snapshot_header.snapshot_id")); 152 | } 153 | 154 | BMLOG_TRACE_PKT(get_packet(), 155 | "reUpdateSnapshotId '{}' at index {} read value {}", 156 | reg.get_name(), i, reg[i]); 157 | } 158 | }; 159 | 160 | REGISTER_PRIMITIVE(reUpdateSnapshotId); 161 | 162 | 163 | class reUpdateSnapshotIdNoRollover 164 | : public ActionPrimitive { 165 | void operator ()(RegisterArray ®, const Data &idx) { 166 | auto i = idx.get_uint(); 167 | 168 | get_field("snapshot_metadata.former_id").set(reg[i]); 169 | // test condition. 170 | if ((reg[i] >= get_field("snapshot_metadata.former_last_seen")) && 171 | (get_field("snapshot_header.snapshot_id") > reg[i]) 172 | ) 173 | { 174 | reg[i].set(get_field("snapshot_header.snapshot_id")); 175 | } 176 | 177 | BMLOG_TRACE_PKT(get_packet(), 178 | "reUpdateSnapshotIdNoRollover '{}' at index {} read value {}", 179 | reg.get_name(), i, reg[i]); 180 | } 181 | }; 182 | 183 | REGISTER_PRIMITIVE(reUpdateSnapshotIdNoRollover); 184 | 185 | 186 | class reUpdateSnapshotIdRollover 187 | : public ActionPrimitive { 188 | void operator ()(RegisterArray ®, const Data &idx) { 189 | auto i = idx.get_uint(); 190 | 191 | get_field("snapshot_metadata.former_id").set(reg[i]); 192 | // test condition. 193 | if ((reg[i] < get_field("snapshot_metadata.former_last_seen")) && 194 | (get_field("snapshot_header.snapshot_id") > reg[i]) 195 | ) 196 | { 197 | reg[i].set(get_field("snapshot_header.snapshot_id")); 198 | } else { 199 | if (reg[i] >= get_field("snapshot_metadata.former_last_seen")) { 200 | reg[i].set(get_field("snapshot_header.snapshot_id")); 201 | } 202 | } 203 | 204 | BMLOG_TRACE_PKT(get_packet(), 205 | "reUpdateSnapshotIdRollover '{}' at index {} read value {}", 206 | reg.get_name(), i, reg[i]); 207 | } 208 | }; 209 | 210 | REGISTER_PRIMITIVE(reUpdateSnapshotIdRollover); 211 | 212 | 213 | class riReadAndUpdateCounter 214 | : public ActionPrimitive { 215 | void operator ()(RegisterArray ®, const Data &idx) { 216 | auto i = idx.get_uint(); 217 | 218 | get_field("snapshot_metadata.current_reading").set(reg[i]); 219 | 220 | auto rVal = reg[i].get(); 221 | reg[i].set(rVal + 1); 222 | 223 | BMLOG_TRACE_PKT(get_packet(), 224 | "riReadAndUpdateCounter '{}' at index {} read value {}", 225 | reg.get_name(), i, reg[i]); 226 | } 227 | }; 228 | 229 | REGISTER_PRIMITIVE(riReadAndUpdateCounter); 230 | 231 | 232 | class riUpdateInflight 233 | : public ActionPrimitive { 234 | void operator ()(RegisterArray ®, const Data &idx) { 235 | auto i = idx.get_uint(); 236 | 237 | auto rVal = reg[i].get(); 238 | reg[i].set(rVal + 1); 239 | 240 | // dst.set(reg[i]); 241 | BMLOG_TRACE_PKT(get_packet(), 242 | "riUpdateInflight '{}' at index {} read value {}", 243 | reg.get_name(), i, reg[i]); 244 | } 245 | }; 246 | 247 | REGISTER_PRIMITIVE(riUpdateInflight); 248 | 249 | 250 | class reReadAndUpdateCounter 251 | : public ActionPrimitive { 252 | void operator ()(RegisterArray ®, const Data &idx) { 253 | auto i = idx.get_uint(); 254 | 255 | get_field("snapshot_metadata.current_reading").set(reg[i]); 256 | 257 | auto rVal = reg[i].get(); 258 | reg[i].set(rVal + 1); 259 | 260 | BMLOG_TRACE_PKT(get_packet(), 261 | "reReadAndUpdateCounter '{}' at index {} read value {}", 262 | reg.get_name(), i, reg[i]); 263 | } 264 | }; 265 | 266 | REGISTER_PRIMITIVE(reReadAndUpdateCounter); 267 | 268 | 269 | class reUpdateInflight 270 | : public ActionPrimitive { 271 | void operator ()(RegisterArray ®, const Data &idx) { 272 | auto i = idx.get_uint(); 273 | 274 | auto rVal = reg[i].get(); 275 | reg[i].set(rVal + 1); 276 | 277 | BMLOG_TRACE_PKT(get_packet(), 278 | "reUpdateInflight '{}' at index {} read value {}", 279 | reg.get_name(), i, reg[i]); 280 | } 281 | }; 282 | 283 | REGISTER_PRIMITIVE(reUpdateInflight); 284 | -------------------------------------------------------------------------------- /p4src/primitives/primitives.json: -------------------------------------------------------------------------------- 1 | { 2 | "riUpdateSnapshotId": 3 | { 4 | "num_args" : 2, 5 | "args" : ["reg", "idx"], 6 | "properties" : { 7 | "reg" : { 8 | "type" : ["register"], 9 | "access" : "write", 10 | "data_width" : 32 11 | }, 12 | "idx" : { 13 | "type" : ["field", "int", "table_entry_data"], 14 | "access" : "read", 15 | "data_width" : 32 16 | } 17 | } 18 | }, 19 | "riUpdateSnapshotIdNoRollover": 20 | { 21 | "num_args" : 2, 22 | "args" : ["reg", "idx"], 23 | "properties" : { 24 | "reg" : { 25 | "type" : ["register"], 26 | "access" : "write", 27 | "data_width" : 32 28 | }, 29 | "idx" : { 30 | "type" : ["field", "int", "table_entry_data"], 31 | "access" : "read", 32 | "data_width" : 32 33 | } 34 | } 35 | }, 36 | "riUpdateSnapshotIdRollover": 37 | { 38 | "num_args" : 2, 39 | "args" : ["reg", "idx"], 40 | "properties" : { 41 | "reg" : { 42 | "type" : ["register"], 43 | "access" : "write", 44 | "data_width" : 32 45 | }, 46 | "idx" : { 47 | "type" : ["field", "int", "table_entry_data"], 48 | "access" : "read", 49 | "data_width" : 32 50 | } 51 | } 52 | } 53 | , 54 | "riUpdateLastSeen": 55 | { 56 | "num_args" : 2, 57 | "args" : ["reg", "idx"], 58 | "properties" : { 59 | "reg" : { 60 | "type" : ["register"], 61 | "access" : "write", 62 | "data_width" : 32 63 | }, 64 | "idx" : { 65 | "type" : ["int", "table_entry_data"], 66 | "access" : "read", 67 | "data_width" : 32 68 | } 69 | } 70 | } 71 | , 72 | "riReadAndUpdateNotification": 73 | { 74 | "num_args" : 2, 75 | "args" : ["reg", "idx"], 76 | "properties" : { 77 | "reg" : { 78 | "type" : ["register"], 79 | "access" : "write", 80 | "data_width" : 32 81 | }, 82 | "idx" : { 83 | "type" : ["field", "int", "table_entry_data"], 84 | "access" : "read", 85 | "data_width" : 32 86 | } 87 | } 88 | } 89 | , 90 | "reUpdateLastSeen": 91 | { 92 | "num_args" : 2, 93 | "args" : ["reg", "idx"], 94 | "properties" : { 95 | "reg" : { 96 | "type" : ["register"], 97 | "access" : "write", 98 | "data_width" : 32 99 | }, 100 | "idx" : { 101 | "type" : ["int", "table_entry_data"], 102 | "access" : "read", 103 | "data_width" : 32 104 | } 105 | } 106 | } 107 | , 108 | "reUpdateSnapshotId": 109 | { 110 | "num_args" : 2, 111 | "args" : ["reg", "idx"], 112 | "properties" : { 113 | "reg" : { 114 | "type" : ["register"], 115 | "access" : "write", 116 | "data_width" : 32 117 | }, 118 | "idx" : { 119 | "type" : ["field", "int", "table_entry_data"], 120 | "access" : "read", 121 | "data_width" : 32 122 | } 123 | } 124 | } 125 | , 126 | "reUpdateSnapshotIdNoRollover": 127 | { 128 | "num_args" : 2, 129 | "args" : ["reg", "idx"], 130 | "properties" : { 131 | "reg" : { 132 | "type" : ["register"], 133 | "access" : "write", 134 | "data_width" : 32 135 | }, 136 | "idx" : { 137 | "type" : ["field", "int", "table_entry_data"], 138 | "access" : "read", 139 | "data_width" : 32 140 | } 141 | } 142 | } 143 | , 144 | "reUpdateSnapshotIdRollover": 145 | { 146 | "num_args" : 2, 147 | "args" : ["reg", "idx"], 148 | "properties" : { 149 | "reg" : { 150 | "type" : ["register"], 151 | "access" : "write", 152 | "data_width" : 32 153 | }, 154 | "idx" : { 155 | "type" : ["field", "int", "table_entry_data"], 156 | "access" : "read", 157 | "data_width" : 32 158 | } 159 | } 160 | } 161 | , 162 | "riReadAndUpdateCounter": 163 | { 164 | "num_args" : 2, 165 | "args" : ["reg", "idx"], 166 | "properties" : { 167 | "reg" : { 168 | "type" : ["register"], 169 | "access" : "write", 170 | "data_width" : 32 171 | }, 172 | "idx" : { 173 | "type" : ["field", "int", "table_entry_data"], 174 | "access" : "read", 175 | "data_width" : 32 176 | } 177 | } 178 | } 179 | , 180 | "riUpdateInflight": 181 | { 182 | "num_args" : 2, 183 | "args" : ["reg", "idx"], 184 | "properties" : { 185 | "reg" : { 186 | "type" : ["register"], 187 | "access" : "write", 188 | "data_width" : 32 189 | }, 190 | "idx" : { 191 | "type" : ["int", "table_entry_data"], 192 | "access" : "read", 193 | "data_width" : 32 194 | } 195 | } 196 | } 197 | , 198 | "reReadAndUpdateCounter": 199 | { 200 | "num_args" : 2, 201 | "args" : ["reg", "idx"], 202 | "properties" : { 203 | "reg" : { 204 | "type" : ["register"], 205 | "access" : "write", 206 | "data_width" : 32 207 | }, 208 | "idx" : { 209 | "type" : ["field", "int", "table_entry_data"], 210 | "access" : "read", 211 | "data_width" : 32 212 | } 213 | } 214 | } 215 | , 216 | "reUpdateInflight": 217 | { 218 | "num_args" : 2, 219 | "args" : ["reg", "idx"], 220 | "properties" : { 221 | "reg" : { 222 | "type" : ["register"], 223 | "access" : "write", 224 | "data_width" : 32 225 | }, 226 | "idx" : { 227 | "type" : ["int", "table_entry_data"], 228 | "access" : "read", 229 | "data_width" : 32 230 | } 231 | } 232 | } 233 | } -------------------------------------------------------------------------------- /p4src/utilities/generate_p4vars.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) < 4: 4 | print("Usage: generate_config.py outdir numports maxsnapshots") 5 | 6 | numports = int(sys.argv[2]) 7 | max_snapshots = int(sys.argv[3]) 8 | 9 | with open(sys.argv[1] + "/config.p4", 'w') as outfile: 10 | # CPU constants must line up with control plane 11 | outfile.write("#define CPU_PORT 0\n") 12 | outfile.write("#define CPU_INGRESS_MIRROR_ID 98\n") 13 | outfile.write("#define CPU_EGRESS_MIRROR_ID 99\n") 14 | outfile.write("#define ETHERTYPE_TOCPU 0x88B6\n") 15 | 16 | # Bit width of variables 17 | # Change with header.p4 to accommodate larger or smaller counters/snapshots 18 | outfile.write("#define COUNTER_WIDTH 32\n") 19 | outfile.write("#define SNAPSHOT_ID_WIDTH 16\n") 20 | 21 | # Configuration variables 22 | outfile.write("#define NUM_PORTS %d\n" % (numports)) 23 | outfile.write("#define MAX_SNAPSHOTS %d\n" % (max_snapshots)) 24 | 25 | outfile.write("#define TWO_X_PORTS %d\n" % (numports * 2)) 26 | outfile.write("#define TWO_X_PORTS_PLUS_CPU %d\n" % ((numports + 1) * 2)) 27 | 28 | # PORTS_TBL_SIZE is the total number of ingress or egress counters 29 | # Must be NUM_PORTS + 1 because its hard to specify register[ingress_port - 1] 30 | # It's much easier to specify register[ingress_port] 31 | outfile.write("#define PORTS_TBL_SIZE %d\n" % (numports + 1)) 32 | 33 | # NEIGHBORS_TBL_SIZE_ING/EGR is the last_seen neighbor table 34 | outfile.write("#define NEIGHBORS_TBL_SIZE_ING %d\n" % (numports * 2)) 35 | outfile.write("#define NEIGHBORS_TBL_SIZE_EGR %d\n" % (numports * numports)) 36 | 37 | # SSTABLE_SIZE is the number of entries in the snapshot table 38 | outfile.write("#define SSTABLE_SIZE %d\n" % (max_snapshots * numports)) 39 | outfile.write("#define DOUBLE_SSTABLE_SIZE %d\n" % (((max_snapshots + 1) * (numports)*2) + 1)) 40 | -------------------------------------------------------------------------------- /p4src/utilities/generate_rules.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # from config import * 3 | # Flags for CPU notifications. There are 3 flag bits: 4 | # 1: New Snapshot 5 | # 2: New Neighbor Last Seen 6 | # 3: Ingress = 0 / Egress = 1 7 | CPU_NEWSS_NEWNBR_INGRESS = 0b110 8 | CPU_NEWSS_INGRESS = 0b100 9 | CPU_NEWNBR_INGRESS = 0b010 10 | 11 | CPU_NEWSS_NEWNBR_EGRESS = 0b111 12 | CPU_NEWSS_EGRESS = 0b101 13 | CPU_NEWNBR_EGRESS = 0b011 14 | 15 | def main(PROGRAM, ports, snapshots): 16 | output = open('out/commands.txt','w') 17 | install_ingress_rules(PROGRAM, ports, snapshots, output) 18 | install_egress_rules(PROGRAM, ports, snapshots, output) 19 | install_mirror_rules(output) 20 | 21 | def is_newss_notification(flag): 22 | return flag & 0b100 23 | def is_newls_notification(flag): 24 | return flag & 0b010 25 | def is_egress_notification(flag): 26 | return flag & 0b001 27 | 28 | def install_mirror_rules(output): 29 | output.write("mirroring_add 98 0\n") 30 | output.write("mirroring_add 99 0\n") 31 | 32 | def install_ingress_rules(PROGRAM, ports, snapshots, output): 33 | tiCheckOption.setup(PROGRAM, ports, snapshots, output) 34 | tiSetEffectivePort.setup(PROGRAM, ports, snapshots, output) 35 | if ('_W' in PROGRAM) or ('_WC' in PROGRAM): 36 | tiUpdateLastSeen.setup(PROGRAM, ports, snapshots, output) 37 | tiCheckRollover.setup(PROGRAM, ports, snapshots, output) 38 | tiSetSnapshotCaseNoRollover.setup(PROGRAM, ports, snapshots, output) 39 | tiSetSnapshotCaseRollover.setup(PROGRAM, ports, snapshots, output) 40 | else: 41 | tiSetSnapshotCase.setup(PROGRAM, ports, snapshots, output) 42 | 43 | if '_WC' in PROGRAM: 44 | tiUpdateSnapshotValue.setup(PROGRAM, ports, snapshots, output) 45 | tiSendNotificationWithChannelState.setup(PROGRAM, ports, snapshots, output) 46 | else: 47 | tiTakeSnapshot.setup(PROGRAM, ports, snapshots, output) 48 | tiSendNotification.setup(PROGRAM, ports, snapshots, output) 49 | 50 | tiForwardInitiation.setup(PROGRAM, ports, snapshots, output) 51 | 52 | def install_egress_rules(PROGRAM, ports, snapshots, output): 53 | teSetEffectivePort.setup(PROGRAM, ports, snapshots, output) 54 | if ('_W' in PROGRAM) or ('_WC' in PROGRAM): 55 | teUpdateLastSeen.setup(PROGRAM, ports, snapshots, output) 56 | teCheckRollover.setup(PROGRAM, ports, snapshots, output) 57 | teSetSnapshotCaseNoRollover.setup(PROGRAM, ports, snapshots, output) 58 | teSetSnapshotCaseRollover.setup(PROGRAM, ports, snapshots, output) 59 | else: 60 | teSetSnapshotCase.setup(PROGRAM, ports, snapshots, output) 61 | 62 | if '_WC' in PROGRAM: 63 | teUpdateSnapshotValue.setup(PROGRAM, ports, snapshots, output) 64 | teSendNotificationWithChannelState.setup(PROGRAM, ports, snapshots, output) 65 | else: 66 | teTakeSnapshot.setup(PROGRAM, ports, snapshots, output) 67 | teSendNotification.setup(PROGRAM, ports, snapshots, output) 68 | 69 | teFinalizePacket.setup(PROGRAM, ports, snapshots, output) 70 | 71 | class tiCheckOption(object): 72 | @staticmethod 73 | def setup(PROGRAM, ports, snapshots, output): 74 | output.write('table_set_default tiCheckOption aiCheckOption 0\n') 75 | output.write('table_add tiCheckOption aiCheckOption 31 => 1\n') 76 | output.write('table_add tiCheckOption aiCheckOption 30 => 2\n') 77 | 78 | 79 | class tiSetEffectivePort(object): 80 | @staticmethod 81 | def setup(PROGRAM, ports, snapshots, output): 82 | for x in xrange(1, ports + 1): 83 | output.write('table_add tiSetEffectivePort aiSetEffectivePort 1 ' + str(x) + '&&&0xFFFF => ' + str(x) + ' 1\n') 84 | output.write('table_add tiSetEffectivePort aiSetEffectivePort 0 ' + str(x) + '&&&0xFFFF => ' + str(x) + ' 1\n') 85 | 86 | output.write('table_add tiSetEffectivePort aiFakeEffectivePort 2 0&&&0x0000 => 1\n') 87 | 88 | class tiUpdateLastSeen(object): 89 | @staticmethod 90 | def setup(PROGRAM, ports, snapshots, output): 91 | for x in xrange(1, ports + 1): 92 | output.write('table_add tiUpdateLastSeen aiUpdateLastSeen ' + str(x) + '&&&0xFF 2&&&0xFF => ' + str(tiUpdateLastSeen.get_index(x, 2)) + ' 1\n') 93 | output.write('table_add tiUpdateLastSeen aiUpdateLastSeen ' + str(x) + '&&&0xFF 1&&&0xFF => ' + str(tiUpdateLastSeen.get_index(x, 1)) + ' 1\n') 94 | 95 | @staticmethod 96 | def get_index(port_num, snapshot_feature): 97 | if snapshot_feature == 2: 98 | return ((port_num - 1) * 2) 99 | else: 100 | return ((port_num - 1) * 2 + 1) 101 | 102 | class tiCheckRollover(object): 103 | @staticmethod 104 | def setup(PROGRAM, ports, snapshots, output): 105 | output.write('table_add tiCheckRollover aCheckRollover 1 => \n') 106 | output.write('table_add tiCheckRollover aCheckRollover 2 => \n') 107 | 108 | class tiSetSnapshotCaseNoRollover(object): 109 | @staticmethod 110 | def setup(PROGRAM, ports, snapshots, output): 111 | output.write('table_set_default tiSetSnapshotCaseNoRollover aSetSnapshotCase 2\n') 112 | output.write('table_add tiSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF 0&&&0xFFFF => 0 1\n') 113 | output.write('table_add tiSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF 1&&&0x0000 => 1 2\n') 114 | output.write('table_add tiSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 1&&&0x0000 0&&&0xFFFF => 2 3\n') 115 | 116 | class tiSetSnapshotCaseRollover(object): 117 | @staticmethod 118 | def setup(PROGRAM, ports, snapshots, output): 119 | output.write('table_add tiSetSnapshotCaseRollover aSetSnapshotCase 0&&&0xFFFF 1&&&0x0000 1&&&0x0000 => 1 1\n') 120 | output.write('table_add tiSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF 0&&&0xFFFF => 0 2\n') 121 | output.write('table_add tiSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF 1&&&0x0000 => 1 3\n') 122 | output.write('table_add tiSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 1&&&0x0000 0&&&0xFFFF => 2 4\n') 123 | 124 | class tiSetSnapshotCase(object): 125 | @staticmethod 126 | def setup(PROGRAM, ports, snapshots, output): 127 | output.write('table_add tiSetSnapshotCase aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF => 0 1\n') 128 | output.write('table_add tiSetSnapshotCase aSetSnapshotCase 0&&&0xFFFF 1&&&0x0000 => 1 2\n') 129 | output.write('table_add tiSetSnapshotCase aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF => 2 3\n') 130 | 131 | class tiTakeSnapshot(object): 132 | @staticmethod 133 | def setup(PROGRAM, ports, snapshots, output): 134 | for snapshot_id in xrange(0, snapshots + 1): 135 | for port in xrange(1, ports + 1): 136 | output.write('table_add tiTakeSnapshot aiTakeSnapshot 1 ' + str(snapshot_id) + ' ' + str(port) + ' => ' + \ 137 | str(tiTakeSnapshot.get_index(snapshot_id, ports, port)) + '\n') 138 | 139 | @staticmethod 140 | def get_index(snapshot_id, max_port_num, port): 141 | return snapshot_id * max_port_num + port - 1 142 | 143 | class tiUpdateSnapshotValue(object): 144 | @staticmethod 145 | def setup(PROGRAM, ports, snapshots, output): 146 | output.write('table_add tiUpdateSnapshotValue aNoOp 2 2&&&0xFF 0&&&0x0000 0&&&0x0000 0&&&0x0000 => 1\n') 147 | for snapshot_id in xrange(0, snapshots + 1): 148 | for port in xrange(1, ports + 1): 149 | output.write('table_add tiUpdateSnapshotValue aiTakeSnapshot 1 0&&&0x0000 ' + str(snapshot_id) + '&&&0xFFFF 0&&&0x0000 ' + str(port) + '&&&0xFFFF => ' + str(tiUpdateSnapshotValue.get_index(snapshot_id, ports, port)) + ' 2\n') 150 | output.write('table_add tiUpdateSnapshotValue aiUpdateInflight 2 0&&&0x0000 0&&&0x0000 ' + str(snapshot_id) + '&&&0xFFFF ' + str(port) + '&&&0xFFFF => ' + str(tiUpdateSnapshotValue.get_index(snapshot_id, ports, port)) + ' 3\n') 151 | 152 | @staticmethod 153 | def get_index(snapshot_id, max_port_num, port): 154 | return snapshot_id * max_port_num + port - 1 155 | 156 | class tiSendNotification(object): 157 | @staticmethod 158 | def setup(PROGRAM, ports, snapshots, output): 159 | global CPU_NEWSS_INGRESS 160 | output.write('table_add tiSendNotification aiSendNotification 1 => ' + str(CPU_NEWSS_INGRESS) + '\n') 161 | 162 | class tiSendNotificationWithChannelState(object): 163 | @staticmethod 164 | def setup(PROGRAM, ports, snapshots, output): 165 | global CPU_NEWSS_NEWNBR_INGRESS 166 | global CPU_NEWSS_INGRESS 167 | global CPU_NEWNBR_INGRESS 168 | 169 | output.write('table_set_default tiSendNotificationWithChannelState aNoOp\n') 170 | output.write('table_add tiSendNotificationWithChannelState aNoOp 0&&&0xFFFF 0&&&0x00 0&&&0x00 => 1\n') 171 | output.write('table_add tiSendNotificationWithChannelState aiSendNotification 0&&&0x0000 1&&&0xFF 1&&&0xFF => ' + str(CPU_NEWSS_NEWNBR_INGRESS) + ' 2\n') 172 | output.write('table_add tiSendNotificationWithChannelState aiSendNotification 0&&&0x0000 0&&&0x00 1&&&0xFF => ' + str(CPU_NEWSS_INGRESS) + ' 3\n') 173 | output.write('table_add tiSendNotificationWithChannelState aiSendNotification 0&&&0x0000 1&&&0xFF 0&&&0x00 => ' + str(CPU_NEWNBR_INGRESS) + ' 4\n') 174 | 175 | class tiForwardInitiation(object): 176 | @staticmethod 177 | def setup(PROGRAM, ports, snapshots, output): 178 | for port in xrange(1, ports+ 1): 179 | output.write('table_add tiForwardInitiation aiForwardInitiation ' + str(port) + ' => ' + str(port) + "\n") 180 | 181 | class teSetEffectivePort(object): 182 | @staticmethod 183 | def setup(PROGRAM, ports, snapshots, output): 184 | for x in xrange(1, ports + 1): 185 | output.write('table_add teSetEffectivePort aeSetEffectivePort ' + str(x) + ' => ' + str(x) + ' \n') 186 | 187 | class teUpdateLastSeen(object): 188 | @staticmethod 189 | def setup(PROGRAM, ports, snapshots, output): 190 | total_entries = ports * ports 191 | for current_port in xrange(1, ports + 1): 192 | index = teUpdateLastSeen.get_index(current_port, ports, 0) 193 | output.write('table_add teUpdateLastSeen aeUpdateLastSeen ' + str(current_port) + ' 0&&&0x0000 30&&&0xFF => ' + str(index) + ' ' + str(total_entries - index) + '\n') 194 | for from_port in xrange(1, ports+ 1): 195 | if (current_port == from_port): 196 | continue 197 | index = teUpdateLastSeen.get_index(current_port, ports, from_port) 198 | output.write('table_add teUpdateLastSeen aeUpdateLastSeen ' + str(current_port) + ' ' + str(from_port) + '&&&0xFFFF 0&&&0x00 => ' + str(index) + ' ' + str(total_entries - index) + ' \n') 199 | @staticmethod 200 | def get_index(current_port, max_port_num, from_port): 201 | if (from_port < current_port): 202 | return (current_port - 1) * max_port_num + from_port 203 | else: 204 | return (current_port - 1) * max_port_num + from_port - 1 205 | 206 | class teCheckRollover(object): 207 | @staticmethod 208 | def setup(PROGRAM, ports, snapshots, output): 209 | output.write('table_add teCheckRollover aCheckRollover 1 => \n') 210 | output.write('table_add teCheckRollover aCheckRollover 2 => \n') 211 | 212 | class teSetSnapshotCaseNoRollover(object): 213 | @staticmethod 214 | def setup(PROGRAM, ports, snapshots, output): 215 | output.write('table_set_default teSetSnapshotCaseNoRollover aSetSnapshotCase 2\n') 216 | output.write('table_add teSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF 0&&&0xFFFF => 0 1\n') 217 | output.write('table_add teSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF 0&&&0x0000 => 1 2\n') 218 | output.write('table_add teSetSnapshotCaseNoRollover aSetSnapshotCase 0&&&0xFFFF 0&&&0x0000 0&&&0xFFFF => 2 3\n') 219 | 220 | class teSetSnapshotCaseRollover(object): 221 | @staticmethod 222 | def setup(PROGRAM, ports, snapshots, output): 223 | output.write('table_add teSetSnapshotCaseRollover aSetSnapshotCase 0&&&0xFFFF 1&&&0x0000 1&&&0x0000 => 1 1\n') 224 | output.write('table_add teSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF 0&&&0xFFFF => 0 2\n') 225 | output.write('table_add teSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF 1&&&0x0000 => 1 3\n') 226 | output.write('table_add teSetSnapshotCaseRollover aSetSnapshotCase 1&&&0x0000 1&&&0x0000 0&&&0xFFFF => 2 4\n') 227 | 228 | class teSetSnapshotCase(object): 229 | @staticmethod 230 | def setup(PROGRAM, ports, snapshots, output): 231 | output.write('table_add teSetSnapshotCase aSetSnapshotCase 0&&&0xFFFF 0&&&0xFFFF => 0 1\n') 232 | output.write('table_add teSetSnapshotCase aSetSnapshotCase 0&&&0xFFFF 1&&&0x0000 => 1 2\n') 233 | output.write('table_add teSetSnapshotCase aSetSnapshotCase 1&&&0x0000 0&&&0xFFFF => 2 3\n') 234 | 235 | class teTakeSnapshot(object): 236 | @staticmethod 237 | def setup(PROGRAM, ports, snapshots, output): 238 | for snapshot_id in xrange(0, snapshots + 1): 239 | for port in xrange(1, ports + 1): 240 | output.write('table_add teTakeSnapshot aeTakeSnapshot 1 ' + str(snapshot_id) + ' ' + str(port) + ' => ' + \ 241 | str(teTakeSnapshot.get_index(snapshot_id, ports, port)) + '\n') 242 | 243 | @staticmethod 244 | def get_index(snapshot_id, max_port_num, port): 245 | return snapshot_id * max_port_num + port - 1 246 | 247 | class teSendNotification(object): 248 | @staticmethod 249 | def setup(PROGRAM, ports, snapshots, output): 250 | global CPU_NEWSS_EGRESS 251 | output.write('table_add teSendNotification aeSendNotification 1 => ' + str(CPU_NEWSS_EGRESS) + '\n') 252 | 253 | class teSendNotificationWithChannelState(object): 254 | @staticmethod 255 | def setup(PROGRAM, ports, snapshots, output): 256 | global CPU_NEWSS_NEWNBR_EGRESS 257 | global CPU_NEWSS_EGRESS 258 | global CPU_NEWNBR_EGRESS 259 | 260 | output.write('table_set_default teSendNotificationWithChannelState aeSendNotification ' + str(CPU_NEWNBR_EGRESS) + ' \n') 261 | output.write('table_add teSendNotificationWithChannelState aNoOp 0&&&0xFFFF 0&&&0x00 0&&&0x00 => 1\n') 262 | output.write('table_add teSendNotificationWithChannelState aeSendNotification 0&&&0x0000 30&&&0xFF 1&&&0xFF => ' + str(CPU_NEWSS_EGRESS) + ' 2\n') 263 | output.write('table_add teSendNotificationWithChannelState aeSendNotification 0&&&0x0000 0&&&0x00 1&&&0xFF => ' + str(CPU_NEWSS_NEWNBR_EGRESS) + ' 3\n') 264 | output.write('table_add teSendNotificationWithChannelState aNoOp 0&&&0x0000 30&&&0xFF 0&&&0x00 => 4\n') 265 | 266 | class teUpdateSnapshotValue(object): 267 | @staticmethod 268 | def setup(PROGRAM, ports, snapshots, output): 269 | output.write('table_add teUpdateSnapshotValue aNoOp 2 2&&&0xFF 0&&&0x0000 0&&&0x0000 0&&&0x0000 => 1\n') 270 | for snapshot_id in xrange(0, snapshots + 1): 271 | for port in xrange(1, ports + 1): 272 | output.write('table_add teUpdateSnapshotValue aeTakeSnapshot 1 0&&&0x0000 ' + str(snapshot_id) + '&&&0xFFFF 0&&&0x0000 ' + str(port) + '&&&0xFFFF => ' + str(teUpdateSnapshotValue.get_index(snapshot_id, ports, port)) + ' 2\n') 273 | output.write('table_add teUpdateSnapshotValue aeUpdateInflight 2 0&&&0x0000 0&&&0x0000 ' + str(snapshot_id) + '&&&0xFFFF ' + str(port) + '&&&0xFFFF => ' + str(teUpdateSnapshotValue.get_index(snapshot_id, ports, port)) + ' 3\n') 274 | 275 | @staticmethod 276 | def get_index(snapshot_id, max_port_num, port): 277 | return snapshot_id * max_port_num + port - 1 278 | 279 | class teFinalizePacket(object): 280 | @staticmethod 281 | def setup(PROGRAM, ports, snapshots, output): 282 | output.write('table_set_default teFinalizePacket aeUpdateSnapshotHeader\n') 283 | output.write('table_add teFinalizePacket aeDropPacket 30 0&&&0x0000 => 1\n') 284 | 285 | 286 | 287 | if __name__ == "__main__": 288 | if (len(sys.argv) != 4): 289 | print 'Incorrect input arguments: PROGRAM_NAME, NUMBER OF PORTS, NUMBER OF SNAPSHOTS' 290 | sys.exit(1) 291 | 292 | 293 | PROGRAM = sys.argv[1] 294 | ports = sys.argv[2] 295 | snapshots = sys.argv[3] 296 | main(PROGRAM, int(ports), int(snapshots)) -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | sudo apt-get update 5 | 6 | echo "Cloning submodules" 7 | git submodule update --init --recursive 8 | 9 | # Originally built against (included): https://github.com/p4lang/behavioral-model/commit/66cefc5e901eafcebb0e1a8f681a05795463215a 10 | echo "Installing BMv2..." 11 | cd third_party/behavioral-model 12 | ./install_deps.sh 13 | ./autogen.sh 14 | ./configure 15 | make -j 16 | sudo make install 17 | cd - 18 | 19 | # Originally built against (included): https://github.com/p4lang/p4c-bm/commit/d75624e18f4ae79e9e5cb478c33d221711f76574 20 | echo "Installing p4c-bm..." 21 | cd third_party/p4c-bm 22 | sudo pip install -r requirements.txt 23 | sudo python setup.py install 24 | cd - 25 | 26 | # copy switch to behavioral-model directory, build. 27 | echo "copying ./p4src/primitives/primitives.cpp --> third_party/behavioral-model/targets/speedlight_switch/primitives.cpp" 28 | cp -raf third_party/behavioral-model/targets/simple_switch/. third_party/behavioral-model/targets/speedlight_switch 29 | cat p4src/primitives/primitives.append >> third_party/behavioral-model/targets/speedlight_switch/primitives.cpp 30 | 31 | echo "building third_party/behavioral-model/targets/speedlight_switch" 32 | cd third_party/behavioral-model/targets/speedlight_switch 33 | make 34 | cd - 35 | 36 | echo "compiling snapshot_init/startsnap.cpp" 37 | mkdir -p out 38 | g++ -std=c++11 snapshot_init/startsnap.cpp -o out/startsnap 39 | pip install dpkt 40 | 41 | sudo sysctl -w net.core.wmem_max=16777216 42 | sudo sysctl -w net.core.rmem_max=16777216 43 | sudo sysctl -w net.core.wmem_default=16777216 44 | sudo sysctl -w net.core.rmem_default=16777216 45 | -------------------------------------------------------------------------------- /snapshot_init/snapListener.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | import sys 4 | 5 | import dpkt 6 | 7 | 8 | def createListeningSocket(iface): 9 | try: 10 | s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0003)) 11 | # s.setblocking(0) 12 | s.bind((iface, 0)) 13 | # s.listen(5) 14 | return s 15 | except socket.timeout: 16 | print "socket timed out." 17 | 18 | def parseNotificationChannel(pktBytes): 19 | eth = dpkt.ethernet.Ethernet(pktBytes) 20 | 21 | try: 22 | msgType, port, neighbor, formerId, currentId, formerLastSeen, currentLastSeen \ 23 | = struct.unpack("!BHHHHHH", eth.data[0:13]) 24 | except: 25 | return None, None, None, None, None, None, None 26 | 27 | print "[Notification] type: %s port: %s neighbor: %s former Id: %s current Id: %s former last seen: %s current last seen: %s" \ 28 | %(msgType, port, neighbor, formerId, currentId, formerLastSeen, currentLastSeen) 29 | 30 | return msgType, port, neighbor, formerId, currentId, formerLastSeen, currentLastSeen 31 | 32 | def parseNotification(pktBytes): 33 | eth = dpkt.ethernet.Ethernet(pktBytes) 34 | 35 | try: 36 | msgType, port, currentId \ 37 | = struct.unpack("!BHH", eth.data[0:5]) 38 | except: 39 | return None, None, None 40 | 41 | print "[Notification] type: %s port: %s current Id: %s" %(msgType, port, currentId) 42 | return msgType, port, currentId 43 | 44 | 45 | if __name__ == "__main__": 46 | if len(sys.argv) != 2: 47 | print 'Incorrect input arguments: program_name' 48 | sys.exit(1) 49 | 50 | program_name = sys.argv[1] 51 | 52 | mySocket = createListeningSocket('veth0') 53 | 54 | print "Waiting for notifications..." 55 | 56 | while True: 57 | try: 58 | packet, addr = mySocket.recvfrom(4096) 59 | except socket.timeout: 60 | continue 61 | 62 | if '_WC' in program_name: 63 | notificationData = parseNotificationChannel(packet) 64 | else: 65 | notificationData = parseNotification(packet) 66 | 67 | -------------------------------------------------------------------------------- /snapshot_init/startsnap.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2018-present University of Pennsylvania 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 | /** 17 | * Initializes a snapshot on the local node. See usage notes for parameters. 18 | **/ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | using namespace std; 40 | using namespace std::chrono; 41 | 42 | unsigned short csum(unsigned short *buf, int nwords) 43 | { 44 | unsigned long sum; 45 | for(sum=0; nwords>0; nwords--) 46 | sum += *buf++; 47 | sum = (sum >> 16) + (sum &0xffff); 48 | sum += (sum >> 16); 49 | return (unsigned short)(~sum); 50 | } 51 | 52 | void makePacketBase(struct mmsghdr* sendbuf, char** snapshot_id_ptrs, 53 | unsigned short** ip_hdr_ptrs, unsigned short** cksum_ptrs, 54 | const vector& iterationList, 55 | struct sockaddr_ll* socket_address) { 56 | struct msghdr* headers = new struct msghdr[iterationList.size()]; 57 | struct iovec* contents = new struct iovec[iterationList.size()]; 58 | 59 | memset(sendbuf, 0, sizeof(struct mmsghdr) * iterationList.size()); 60 | memset(headers, 0, sizeof(struct msghdr) * iterationList.size()); 61 | memset(contents, 0, sizeof(struct iovec) * iterationList.size()); 62 | 63 | 64 | // Construct a single header that we'll copy later 65 | char sample_packet[1024]; 66 | memset(sample_packet, 0, 1024); 67 | size_t tx_len = 0; 68 | size_t snapshot_id_offset; 69 | size_t cksum_offset; 70 | char* port_ptr; 71 | 72 | // Ethernet header 73 | struct ether_header *eh = (struct ether_header *) sample_packet; 74 | eh->ether_shost[0] = 0x00; 75 | eh->ether_shost[1] = 0x00; 76 | eh->ether_shost[2] = 0x00; 77 | eh->ether_shost[3] = 0x00; 78 | eh->ether_shost[4] = 0x00; 79 | eh->ether_shost[5] = 0x00; 80 | eh->ether_dhost[0] = 0x00; 81 | eh->ether_dhost[1] = 0x00; 82 | eh->ether_dhost[2] = 0x00; 83 | eh->ether_dhost[3] = 0x00; 84 | eh->ether_dhost[4] = 0x00; 85 | eh->ether_dhost[5] = 0x00; 86 | eh->ether_type = htons(ETH_P_IP); 87 | tx_len += sizeof(struct ether_header); 88 | 89 | // IP Header 90 | struct iphdr *iph = (struct iphdr *) (sample_packet + sizeof(struct ether_header)); 91 | iph->ihl = 7; 92 | iph->version = 4; 93 | iph->tos = 0; // Low delay 94 | iph->id = htons(11111); 95 | iph->frag_off = 0x00; 96 | iph->ttl = 0xff; // hops 97 | iph->protocol = IPPROTO_TCP; 98 | /* Source IP address */ 99 | iph->saddr = inet_addr("0.0.0.0"); 100 | /* Destination IP address */ 101 | iph->daddr = inet_addr("0.0.0.0"); 102 | tx_len += sizeof(struct iphdr); 103 | 104 | // IP options (snapshot header) 105 | char *ip_options = sample_packet + tx_len; 106 | ip_options[0] = 0xde; 107 | ip_options[1] = 0x30; 108 | ip_options[2] = 0x00; 109 | ip_options[3] = 0x00; 110 | snapshot_id_offset = &ip_options[3] - sample_packet; 111 | ip_options[4] = 0x00; 112 | ip_options[5] = 0x00; 113 | port_ptr = &ip_options[5]; 114 | ip_options[6] = 0x00; 115 | ip_options[7] = 0x00; 116 | tx_len += 8; 117 | 118 | // UDP header 119 | struct udphdr *udph = (struct udphdr *) (sample_packet + tx_len); 120 | udph->source = htons(11111); 121 | udph->dest = htons(80); 122 | udph->check = 0; // skip 123 | tx_len += sizeof(struct udphdr); 124 | 125 | sample_packet[tx_len++] = 0xde; 126 | sample_packet[tx_len++] = 0xad; 127 | sample_packet[tx_len++] = 0xbe; 128 | sample_packet[tx_len++] = 0xef; 129 | 130 | udph->len = sizeof(struct udphdr) + 4; 131 | iph->tot_len = htons(tx_len - sizeof(struct ether_header)); 132 | 133 | cksum_offset = (char*)&iph->check - sample_packet; 134 | 135 | 136 | // Create all packets at once 137 | for (int i = 0; i < iterationList.size(); ++i) { 138 | *port_ptr = 0x00 + iterationList[i]; 139 | char* packet = new char[tx_len]; 140 | memcpy(packet, sample_packet, tx_len); 141 | contents[i].iov_base = packet; 142 | contents[i].iov_len = tx_len; 143 | 144 | headers[i].msg_iov = &contents[i]; 145 | headers[i].msg_iovlen = 1; 146 | headers[i].msg_name = socket_address; 147 | headers[i].msg_namelen = sizeof(struct sockaddr_ll); 148 | sendbuf[i].msg_hdr = headers[i]; 149 | 150 | snapshot_id_ptrs[i] = packet + snapshot_id_offset; 151 | ip_hdr_ptrs[i] = (unsigned short*) packet + sizeof(struct ether_header); 152 | cksum_ptrs[i] = (unsigned short*) packet + cksum_offset; 153 | } 154 | } 155 | 156 | void setupMessages(char** snapshot_id_ptrs, unsigned short** ip_hdr_ptrs, 157 | unsigned short** cksum_ptrs, 158 | const int snapshot_id, const int num_messages) { 159 | for (int i = 0; i < num_messages; ++i) { 160 | *snapshot_id_ptrs[i] = 0x00 + snapshot_id; 161 | *cksum_ptrs[i] = csum(ip_hdr_ptrs[i], (sizeof(struct iphdr)+8)/2); 162 | } 163 | } 164 | 165 | int main(int argc, char *argv[]) 166 | { 167 | //cout << "Starting startsnap.cpp" << endl; 168 | 169 | string if_name = "veth1"; 170 | int interval_ms = 30000; 171 | int ss_begin = 1; 172 | int ss_end = 2; 173 | 174 | int c; 175 | while ((c = getopt(argc, argv, "d:i:b:e:")) != -1) { 176 | switch (c) { 177 | case 'd': 178 | if_name = optarg; 179 | break; 180 | case 'i': 181 | interval_ms = atoi(optarg); 182 | break; 183 | case 'b': 184 | ss_begin = atoi(optarg); 185 | break; 186 | case 'e': 187 | ss_end = atoi(optarg); 188 | break; 189 | } 190 | } 191 | 192 | if (argc < optind + 4) { 193 | cout << "Usage ./startsnap [-dibe] HH MM port_begin port_end\n" 194 | << "\t-d pci_intf\n" 195 | << "\t-i interval (ms)\n" 196 | << "\t-b ss_begin\n" 197 | << "\t-e ss_end\n"; 198 | exit(1); 199 | } 200 | 201 | int target_hours = atoi(argv[optind++]); 202 | int target_minutes = atoi(argv[optind++]); 203 | int port_begin = atoi(argv[optind++]); 204 | int port_end = atoi(argv[optind++]); 205 | 206 | assert(port_end > port_begin); 207 | assert(ss_end > ss_begin); 208 | 209 | struct sched_param sp; 210 | memset( &sp, 0, sizeof(sp) ); 211 | sp.sched_priority = 99; 212 | if (sched_setscheduler( 0, SCHED_FIFO, &sp ) < 0) { 213 | perror("sched_setscheduler"); 214 | } 215 | 216 | vector iterationList; 217 | for (int i = port_begin; i < port_end; ++i) { 218 | iterationList.push_back(i); 219 | } 220 | 221 | /* 222 | * Create the raw socket to the switch 223 | */ 224 | int sock; 225 | struct sockaddr_ll socket_address; 226 | // Open RAW socket to send on 227 | if ((sock = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) { 228 | perror("socket"); 229 | } 230 | 231 | int optval = 7; // valid values are in the range [1,7] 232 | // 1- low priority, 7 - high priority 233 | if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &optval, sizeof(optval)) < 0) { 234 | perror("setsockopt"); 235 | } 236 | 237 | /* 238 | * Craft the raw socket address 239 | */ 240 | struct ifreq if_idx; 241 | memset(&if_idx, 0, sizeof(struct ifreq)); 242 | strncpy(if_idx.ifr_name, if_name.c_str(), IFNAMSIZ-1); 243 | 244 | if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0) { 245 | perror("SIOCGIFINDEX"); 246 | } 247 | 248 | // Set up the destination socket info 249 | socket_address.sll_ifindex = if_idx.ifr_ifindex; 250 | socket_address.sll_halen = ETH_ALEN; 251 | socket_address.sll_addr[0] = 0x00; 252 | socket_address.sll_addr[1] = 0x00; 253 | socket_address.sll_addr[2] = 0x00; 254 | socket_address.sll_addr[3] = 0x00; 255 | socket_address.sll_addr[4] = 0x00; 256 | socket_address.sll_addr[5] = 0x00; 257 | 258 | /* 259 | * Craft the base packet. Can fill in the snapshot ID, port, and cksum 260 | * later using pointers into the sendbuf. 261 | */ 262 | struct mmsghdr sendbuf[iterationList.size()]; 263 | char* snapshot_id_ptrs[iterationList.size()]; 264 | unsigned short* ip_hdr_ptrs[iterationList.size()]; 265 | unsigned short* cksum_ptrs[iterationList.size()]; 266 | 267 | makePacketBase(sendbuf, snapshot_id_ptrs, ip_hdr_ptrs, cksum_ptrs, 268 | iterationList, &socket_address); 269 | 270 | /* 271 | * Set up timestamps for accurate sending. 272 | */ 273 | time_t t = time(NULL); 274 | struct tm lt = {0}; 275 | localtime_r(&t, <); 276 | long long utc_offset = lt.tm_gmtoff * 1000; 277 | 278 | auto interval = chrono::milliseconds(interval_ms); 279 | auto target_time = duration_cast(system_clock::now().time_since_epoch()); 280 | target_time -= milliseconds((target_time.count() + utc_offset) % 86400000); 281 | 282 | target_time += hours(target_hours); 283 | target_time += minutes(target_minutes); 284 | 285 | target_time -= interval; 286 | cout << "startsnap waiting for " << (target_time - system_clock::now().time_since_epoch()).count()/1000000000 << endl; 287 | while (target_time > system_clock::now().time_since_epoch()) { 288 | sched_yield(); 289 | } 290 | target_time += interval; 291 | 292 | for (int i = ss_begin; i < ss_end; ++i) { 293 | setupMessages(snapshot_id_ptrs, ip_hdr_ptrs, cksum_ptrs, 294 | i, iterationList.size()); 295 | 296 | while (target_time > system_clock::now().time_since_epoch()) { 297 | asm volatile("pause"); 298 | } 299 | 300 | if (sendmmsg(sock, sendbuf, iterationList.size(), 0) < 0) { 301 | perror("sendmmsg"); 302 | } 303 | target_time += interval; 304 | 305 | sched_yield(); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /start_listening.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ -z $1 ]; then 6 | echo "Usage: ./start_listener.sh variant" 7 | exit 1 8 | fi 9 | echo "Variant: ${1}" 10 | 11 | # install rules 12 | ./third_party/behavioral-model/targets/speedlight_switch/sswitch_CLI --thrift-port 9090 < out/commands.txt 13 | 14 | # start listener 15 | sudo python snapshot_init/snapListener.py $1 16 | 17 | -------------------------------------------------------------------------------- /start_snapshot.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | if [ $# -lt 3 ]; then 6 | echo "Usage: ./start_snapshot.sh HH MM numports [passed arguments]" 7 | exit 1 8 | fi 9 | 10 | HH=$1 11 | MM=$2 12 | NUMPORTS=$3 13 | 14 | echo "Current Time is `date +%H:%M`." 15 | echo "Snapshots will start at ${HH}:${MM}." 16 | echo "Initiating on ports 1 - ${NUMPORTS}" 17 | 18 | shift 3 19 | 20 | CORES=$((`nproc` - 1)) 21 | STEP=$((${NUMPORTS}/${CORES})) 22 | 23 | NUM_HIGH=$((${NUMPORTS} - (${STEP} * ${CORES}))) 24 | NUM_LOW=$((${CORES} - ${NUM_HIGH})) 25 | 26 | PORT=1 27 | for (( i=0; i < ${NUM_HIGH}; i++)) 28 | do 29 | #echo "high starting ${PORT}, ending inclusive: $((${PORT} + ${STEP}))" 30 | sudo out/startsnap -d veth1 ${HH} ${MM} ${PORT} $((${PORT} + ${STEP} + 1)) $@ & 31 | PORT=$((${PORT} + ${STEP} + 1)) 32 | done 33 | if [ "$STEP" != 0 ]; then 34 | for (( i=0; i < ${NUM_LOW}; i++)) 35 | do 36 | #echo "starting: ${PORT}, ending inclusive: $((${PORT} + ${STEP} - 1))" 37 | sudo out/startsnap -d veth1 ${HH} ${MM} ${PORT} $((${PORT} + ${STEP})) $@ & 38 | PORT=$((${PORT} + ${STEP})) 39 | done 40 | fi 41 | 42 | 43 | wait 44 | -------------------------------------------------------------------------------- /start_switch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ $# -lt 3 ] 6 | then echo "Usage: start_switch.sh variant numports maxsnapshots" 7 | exit 8 | fi 9 | 10 | echo "Variant: ${1}" 11 | echo "Number of ports: ${2}" 12 | echo "Max snapshots: ${3}" 13 | 14 | echo "setting up veth interfaces" 15 | 16 | port_config="" 17 | 18 | for i in `seq 0 $2`; 19 | do 20 | if [ ${i} == 0 ]; then 21 | echo "CPU port" 22 | else 23 | echo "Port" ${i} 24 | fi 25 | 26 | port1=$(( ${i} * 2 )) 27 | port2=$(( ${i} * 2 + 1 )) 28 | 29 | port_config="${port_config} -i ${i}@veth${port1}" 30 | 31 | sudo ip link delete veth${port1} >& /dev/null || true 32 | sudo ip link delete veth${port2} >& /dev/null || true 33 | sudo ip link add veth${port1} type veth peer name veth${port2} 34 | sudo sysctl -q net.ipv6.conf.veth${port1}.disable_ipv6=1 35 | sudo sysctl -q net.ipv6.conf.veth${port2}.disable_ipv6=1 36 | sudo ifconfig veth${port1} up promisc 37 | sudo ifconfig veth${port2} up promisc 38 | done 39 | 40 | mkdir -p out 41 | 42 | # write the config files 43 | python p4src/utilities/generate_p4vars.py out $2 $3 44 | 45 | # generate rule install commands 46 | python p4src/utilities/generate_rules.py $1 $2 $3 47 | 48 | # compile the switch 49 | p4c-bmv2 --json out/$1.json p4src/$1.p4 --primitives p4src/primitives/primitives.json 50 | 51 | # start simple_switch running the P4 code. port 0 = CPU port 52 | sudo ./third_party/behavioral-model/targets/speedlight_switch/simple_switch ${port_config} out/$1.json 53 | --------------------------------------------------------------------------------