blob: 6ce8697b2ce5c8f04732b0fc47a4b1540e5f8335 [file] [log] [blame]
Andrea Campanella31bcdcd2017-09-19 16:35:44 +09001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.pi.demo.app.tor;
18
Carmelo Casconef22e8e42017-09-28 20:22:37 +020019import com.google.common.collect.Lists;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090020import org.apache.felix.scr.annotations.Component;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020021import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090023import org.onosproject.net.DeviceId;
24import org.onosproject.net.Host;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020025import org.onosproject.net.Path;
26import org.onosproject.net.Port;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090027import org.onosproject.net.PortNumber;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020028import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090030import org.onosproject.net.flow.FlowRule;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020031import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.pi.runtime.PiAction;
33import org.onosproject.net.pi.runtime.PiActionId;
34import org.onosproject.net.pi.runtime.PiActionParam;
35import org.onosproject.net.topology.DefaultTopologyVertex;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090036import org.onosproject.net.topology.Topology;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020037import org.onosproject.net.topology.TopologyGraph;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090038import org.onosproject.pi.demo.app.common.AbstractUpgradableFabricApp;
39
40import java.util.Collection;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020041import java.util.Collections;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090042import java.util.List;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090043import java.util.Set;
Carmelo Casconef22e8e42017-09-28 20:22:37 +020044import java.util.stream.Collectors;
45
46import static com.google.common.collect.Collections2.permutations;
47import static org.onlab.util.ImmutableByteSequence.copyFrom;
48import static org.onosproject.pi.demo.app.tor.Combination.combinations;
49import static org.onosproject.pi.demo.app.tor.TorInterpreter.*;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090050
51
52/**
53 * Implementation of an upgradable fabric app for TOR configuration.
54 */
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090055@Component(immediate = true)
56public class TorApp extends AbstractUpgradableFabricApp {
57
58 private static final String APP_NAME = "org.onosproject.pi-tor";
59
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090060 public TorApp() {
61 super(APP_NAME, TorPipeconfFactory.getAll());
62 }
63
64 @Override
65 public boolean initDevice(DeviceId deviceId) {
66 // Nothing to do.
67 return true;
68 }
69
70 @Override
Carmelo Casconef22e8e42017-09-28 20:22:37 +020071 public List<FlowRule> generateLeafRules(DeviceId leaf, Host localHost, Set<Host> remoteHosts,
Andrea Campanella31bcdcd2017-09-19 16:35:44 +090072 Collection<DeviceId> availableSpines, Topology topo)
73 throws FlowRuleGeneratorException {
74
Carmelo Casconef22e8e42017-09-28 20:22:37 +020075 // Get ports which connect this leaf switch to hosts.
76 Set<PortNumber> hostPorts = deviceService.getPorts(leaf)
77 .stream()
78 .filter(port -> !isFabricPort(port, topo))
79 .map(Port::number)
80 .collect(Collectors.toSet());
81
82 // Get ports which connect this leaf to the given available spines.
83 TopologyGraph graph = topologyService.getGraph(topo);
84 Set<PortNumber> fabricPorts = graph.getEdgesFrom(new DefaultTopologyVertex(leaf))
85 .stream()
86 .filter(e -> availableSpines.contains(e.dst().deviceId()))
87 .map(e -> e.link().src().port())
88 .collect(Collectors.toSet());
89
90 if (hostPorts.size() != 1 || fabricPorts.size() == 0) {
91 log.error("Leaf switch has invalid port configuration: hostPorts={}, fabricPorts={}",
92 hostPorts.size(), fabricPorts.size());
93 throw new FlowRuleGeneratorException();
94 }
95 PortNumber hostPort = hostPorts.iterator().next();
96
97 List<FlowRule> rules = Lists.newArrayList();
98
99 // Filtering rules
100 rules.addAll(generateFilteringRules(leaf, remoteHosts));
101 rules.addAll(generateFilteringRules(leaf, Collections.singleton(localHost)));
102
103 // FIXME: ignore ECMP for the moment
104 // if (fabricPorts.size() > 1) {
105 // // Do ECMP.
106 // Pair<PiTableAction, List<FlowRule>> result = provisionEcmpPiTableAction(leaf, fabricPorts);
107 // rules.addAll(result.getRight());
108 // treatment = DefaultTrafficTreatment.builder().piTableAction(result.getLeft()).build();
109 // } else {
110 // // Output on port.
111 // PortNumber outPort = fabricPorts.iterator().next();
112 // treatment = DefaultTrafficTreatment.builder().setOutput(outPort).build();
113 // }
114
115
116 PortNumber outPort = fabricPorts.iterator().next();
117
118 // From local host to remote ones.
119 for (Host remoteHost : remoteHosts) {
120 for (IpAddress ipAddr : remoteHost.ipAddresses()) {
121 FlowRule rule = flowRuleBuilder(leaf, L3_FWD_TBL_ID)
122 .withSelector(
123 DefaultTrafficSelector.builder()
124 .matchIPDst(ipAddr.toIpPrefix())
125 .build())
126 .withTreatment(
127 DefaultTrafficTreatment.builder()
128 .piTableAction(nextHopAction(outPort, localHost.mac(), remoteHost.mac()))
129 .build())
130 .build();
131 rules.add(rule);
132 }
133 }
134
135 // From remote hosts to the local one
136 for (IpAddress dstIpAddr : localHost.ipAddresses()) {
137 for (Host remoteHost : remoteHosts) {
138 FlowRule rule = flowRuleBuilder(leaf, L3_FWD_TBL_ID)
139 .withSelector(
140 DefaultTrafficSelector.builder()
141 .matchIPDst(dstIpAddr.toIpPrefix())
142 .build())
143 .withTreatment(
144 DefaultTrafficTreatment.builder()
145 .piTableAction(nextHopAction(hostPort, remoteHost.mac(), localHost.mac()))
146 .build())
147 .build();
148 rules.add(rule);
149 }
150 }
151
152 return rules;
153 }
154
155 private PiAction nextHopAction(PortNumber port, MacAddress smac, MacAddress dmac) {
156 return PiAction.builder()
157 .withId(SET_NEXT_HOP_ACT_ID)
158 .withParameter(new PiActionParam(PORT_ACT_PRM_ID, copyFrom(port.toLong())))
159 // Ignore L3 routing behaviour by keeping the original host mac addresses at each hop.
160 .withParameter(new PiActionParam(SMAC_ACT_PRM_ID, copyFrom(smac.toBytes())))
161 .withParameter(new PiActionParam(DMAC_ACT_PRM_ID, copyFrom(dmac.toBytes())))
162 .build();
Andrea Campanella31bcdcd2017-09-19 16:35:44 +0900163 }
164
165 @Override
Carmelo Casconef22e8e42017-09-28 20:22:37 +0200166 public List<FlowRule> generateSpineRules(DeviceId spine, Set<Host> hosts, Topology topo)
Andrea Campanella31bcdcd2017-09-19 16:35:44 +0900167 throws FlowRuleGeneratorException {
168
Carmelo Casconef22e8e42017-09-28 20:22:37 +0200169 List<FlowRule> rules = Lists.newArrayList();
170
171 rules.addAll(generateFilteringRules(spine, hosts));
172
173 // For each host pair (src -> dst)
174 for (Set<Host> hostCombs : combinations(hosts, 2)) {
175 for (List<Host> hostPair : permutations(hostCombs)) {
176
177 Host srcHost = hostPair.get(0);
178 Host dstHost = hostPair.get(1);
179
180 Set<Path> paths = topologyService.getPaths(topo, spine, dstHost.location().deviceId());
181
182 if (paths.size() == 0) {
183 log.warn("Can't find any path between spine {} and host {}", spine, dstHost);
184 throw new FlowRuleGeneratorException();
185 }
186
187 TrafficTreatment treatment;
188
189 // FIXME: ingore ECMP for the moment
190 // if (paths.size() == 1) {
191 // // Only one path, do output on port.
192 // PortNumber port = paths.iterator().next().src().port();
193 // treatment = DefaultTrafficTreatment.builder().setOutput(port).build();
194 // } else {
195 // // Multiple paths, do ECMP.
196 // Set<PortNumber> portNumbers = paths.stream().map(p -> p.src().port()).collect(toSet());
197 // Pair<PiTableAction, List<FlowRule>> result = provisionEcmpPiTableAction(deviceId, portNumbers);
198 // rules.addAll(result.getRight());
199 // treatment = DefaultTrafficTreatment.builder().piTableAction(result.getLeft()).build();
200 // }
201
202 PortNumber port = paths.iterator().next().src().port();
203 treatment = DefaultTrafficTreatment.builder()
204 .piTableAction(nextHopAction(port, srcHost.mac(), dstHost.mac()))
205 .build();
206
207 for (IpAddress dstIpAddr : dstHost.ipAddresses()) {
208 FlowRule rule = flowRuleBuilder(spine, L3_FWD_TBL_ID)
209 .withSelector(
210 DefaultTrafficSelector.builder()
211 .matchIPDst(dstIpAddr.toIpPrefix())
212 .build())
213 .withTreatment(treatment)
214 .build();
215
216 rules.add(rule);
217 }
218 }
219 }
220
221 return rules;
222 }
223
224 private List<FlowRule> generateFilteringRules(DeviceId deviceId, Collection<Host> dstHosts)
225 throws FlowRuleGeneratorException {
226
227 List<FlowRule> rules = Lists.newArrayList();
228 for (Host host : dstHosts) {
229 MacAddress ethAddr = host.mac();
230 FlowRule rule = flowRuleBuilder(deviceId, L3_FILTER_TBL_ID)
231 .withSelector(DefaultTrafficSelector.builder()
232 .matchEthDst(ethAddr)
233 .build())
234 .withTreatment(DefaultTrafficTreatment.builder()
235 .piTableAction(PiAction.builder()
236 .withId(PiActionId.of("NoAction"))
237 .build())
238 .build())
239 .build();
240 rules.add(rule);
241 }
242 return rules;
Andrea Campanella31bcdcd2017-09-19 16:35:44 +0900243 }
244
245}