blob: a93c414545d3ecc0d4b80479d0202eebf391356d [file] [log] [blame]
Carmelo Casconefc85dd12018-03-23 18:03:15 -07001/*
2 * Copyright 2018-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.intdemo;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableMap;
21import org.apache.commons.lang.ArrayUtils;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.Ip4Address;
28import org.onlab.util.ImmutableByteSequence;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.device.DeviceEvent;
33import org.onosproject.net.device.DeviceListener;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.flow.DefaultFlowRule;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.FlowRuleService;
40import org.onosproject.net.flow.criteria.PiCriterion;
41import org.onosproject.net.pi.model.PiActionId;
42import org.onosproject.net.pi.model.PiActionParamId;
43import org.onosproject.net.pi.model.PiMatchFieldId;
44import org.onosproject.net.pi.model.PiPipeconfId;
45import org.onosproject.net.pi.model.PiTableId;
46import org.onosproject.net.pi.runtime.PiAction;
47import org.onosproject.net.pi.runtime.PiActionParam;
48import org.onosproject.net.pi.service.PiPipeconfService;
49import org.slf4j.Logger;
50
51import java.util.Arrays;
52import java.util.Collection;
53import java.util.Map;
54import java.util.Optional;
55
56import static org.onlab.util.ImmutableByteSequence.copyFrom;
57import static org.slf4j.LoggerFactory.getLogger;
58
59/**
60 * Implementation of an upgradable fabric app for the Basic pipeconf (basic.p4)
61 * with ECMP support.
62 */
63@Component(immediate = true)
64public class IntDemoApp {
65
66 private static final int DEFAULT_TEID = 0;
Carmelo Cascone43ac7652018-03-24 00:50:55 -070067 private final Logger log = getLogger(getClass());
Carmelo Casconefc85dd12018-03-23 18:03:15 -070068
69 private static final String SPGW_INT_PIPECONF_KEYWORD = "spgw-int";
70 private static final String SPGW_DEVICE_KEYWORD = "leaf2";
71 private static final Ip4Address S1U_SGW_ADDR = Ip4Address.valueOf("140.0.0.2");
72
73 private static final Collection<String> UE_ADDRS = ImmutableList.of(
74 "160.0.2.1", "160.0.2.2", "160.0.2.3", "160.0.2.4", "160.0.2.5");
75
76 private static final Map<String, Integer> SWITCH_ID_MAP = new ImmutableMap.Builder<String, Integer>()
Carmelo Cascone43ac7652018-03-24 00:50:55 -070077 .put("leaf1", 100)
78 .put("leaf2", 200)
Carmelo Casconefc85dd12018-03-23 18:03:15 -070079 .build();
Carmelo Casconefc85dd12018-03-23 18:03:15 -070080 private static final PiTableId TBL_ID_INT_PREP = PiTableId
81 .of("int_egress.int_prep");
82
Carmelo Casconefc85dd12018-03-23 18:03:15 -070083 private static final PiActionId ACT_ID_INT_TRANSIT = PiActionId
84 .of("int_egress.int_transit");
85
86 private static final PiActionParamId ACTP_ID_SWITCH_ID = PiActionParamId
87 .of("switch_id");
88
89 private static final PiTableId TBL_ID_UE_FILTER = PiTableId
90 .of("spgw_ingress.ue_filter_table");
91 private static final PiTableId TBL_ID_DL_SESS_LOOKUP = PiTableId
92 .of("spgw_ingress.dl_sess_lookup");
93 private static final PiMatchFieldId MF_ID_IPV4_DST = PiMatchFieldId
94 .of("ipv4.dst_addr");
95 private static final PiActionId ACT_ID_SET_DL_SESS_INFO = PiActionId
96 .of("spgw_ingress.set_dl_sess_info");
97 private static final PiActionParamId PARAM_S1U_ENB_ADDR = PiActionParamId
98 .of("s1u_enb_addr");
99 private static final PiActionParamId PARAM_ID_S1U_SGW_ADDR = PiActionParamId
100 .of("s1u_sgw_addr");
101 private static final PiAction NO_ACTION = PiAction.builder()
102 .withId(PiActionId.of("NoAction"))
103 .build();
104 private static final PiActionParamId ACTP_TEID = PiActionParamId
105 .of("teid");
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 private DeviceService deviceService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 private FlowRuleService flowRuleService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 private CoreService coreService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 private PiPipeconfService piPipeconfService;
118
119 private final DeviceListener deviceListener = new InternalDeviceListener();
120
121 private ApplicationId appId;
122
123 @Activate
124 public void activate() {
125 log.info("Starting...");
126 appId = coreService.registerApplication("org.onosproject.int-demo");
127 deviceService.getDevices().forEach(d -> checkDevice(d.id()));
128 deviceService.addListener(deviceListener);
129 log.info("STARTED", appId.id());
130 }
131
132 @Deactivate
133 public void deactivate() {
134 log.info("Stopping...");
135 deviceService.removeListener(deviceListener);
136 flowRuleService.removeFlowRulesById(appId);
137 log.info("STOPPED");
138 }
139
140 private void confIntSwitch(DeviceId deviceId) {
141 Optional<Integer> switchId = SWITCH_ID_MAP.keySet().stream()
142 .filter(k -> deviceId.toString().contains(k))
143 .findFirst()
144 .map(SWITCH_ID_MAP::get);
145
146 if (!switchId.isPresent()) {
147 log.error("Unknown INT switch ID for device {}", deviceId);
148 return;
149 }
150
151 log.info("Configuring device {} for INT transit capability: switchId={}...", deviceId, switchId.get());
152
153 FlowRule intPrepRule = flowRuleBuilder(deviceId, TBL_ID_INT_PREP)
154 .withSelector(DefaultTrafficSelector.emptySelector())
155 .withTreatment(
156 DefaultTrafficTreatment.builder()
157 .piTableAction(
158 PiAction.builder()
159 .withId(ACT_ID_INT_TRANSIT)
160 .withParameter(new PiActionParam(
Carmelo Cascone43ac7652018-03-24 00:50:55 -0700161 ACTP_ID_SWITCH_ID, copyFrom(switchId.get())))
Carmelo Casconefc85dd12018-03-23 18:03:15 -0700162 .build())
163 .build())
164 .build();
165
Carmelo Cascone43ac7652018-03-24 00:50:55 -0700166 for (String instMask : ImmutableList.of("0003", "0407")) {
167 for (int i = 0; i < 16; i++) {
168 PiTableId tableId = PiTableId
169 .of("int_egress.int_metadata_insert.int_inst_" + instMask);
170 PiMatchFieldId fmId = PiMatchFieldId
171 .of("hdr.int_header.instruction_mask_" + instMask);
172 PiActionId actionId = PiActionId
173 .of("int_egress.int_metadata_insert.int_set_header_" + instMask + "_i" + String.valueOf(i));
174 FlowRule intIntrRule = flowRuleBuilder(deviceId, tableId)
175 .withSelector(
176 DefaultTrafficSelector.builder()
177 .matchPi(
178 PiCriterion.builder()
179 .matchExact(fmId, (byte) i)
180 .build())
181 .build())
182 .withTreatment(
183 DefaultTrafficTreatment.builder()
184 .piTableAction(PiAction.builder()
185 .withId(actionId)
186 .build())
187 .build())
188 .build();
Carmelo Casconefc85dd12018-03-23 18:03:15 -0700189
Carmelo Cascone43ac7652018-03-24 00:50:55 -0700190 flowRuleService.applyFlowRules(intPrepRule, intIntrRule);
191 }
192 }
Carmelo Casconefc85dd12018-03-23 18:03:15 -0700193 }
194
195 private void confSpgw(DeviceId deviceId) {
196 log.info("Configuring device {} for SPGW downlink processing...", deviceId);
197 UE_ADDRS.stream()
198 .map(Ip4Address::valueOf)
199 .forEach(ueAddr -> {
200 flowRuleService.applyFlowRules(ueFilterRule(deviceId, ueAddr));
201 flowRuleService.applyFlowRules(
202 dlSessLookupRule(deviceId, ueAddr, DEFAULT_TEID, ueAddr, S1U_SGW_ADDR)
203 );
204 });
205 }
206
207 private FlowRule ueFilterRule(DeviceId deviceId, Ip4Address ueAddr) {
208 final PiCriterion piCriterion = PiCriterion.builder()
209 .matchLpm(MF_ID_IPV4_DST, ueAddr.toOctets(), 32)
210 .build();
211
212 return flowRuleBuilder(deviceId, TBL_ID_UE_FILTER)
213 .withSelector(DefaultTrafficSelector.builder()
214 .matchPi(piCriterion)
215 .build())
216 .withTreatment(DefaultTrafficTreatment.builder()
217 .piTableAction(NO_ACTION)
218 .build())
219 .build();
220 }
221
222 private FlowRule dlSessLookupRule(DeviceId deviceId, Ip4Address ueAddr, long teid,
223 Ip4Address enbAddr, Ip4Address s1uAddr) {
224
225 // FIXME: the long teid is obtained from the wrong byte order
226 ImmutableByteSequence teidBs = copyFrom(teid);
227 byte[] byteArray = teidBs.asArray();
228 ArrayUtils.reverse(byteArray);
229 byte[] trimmedArray = Arrays.copyOfRange(byteArray, 0, 4);
230 ImmutableByteSequence revTeidBs = copyFrom(trimmedArray);
231
232 // Add rule to
233 final PiAction action = PiAction.builder()
234 .withId(ACT_ID_SET_DL_SESS_INFO)
235 .withParameter(new PiActionParam(ACTP_TEID, revTeidBs))
236 .withParameter(new PiActionParam(PARAM_S1U_ENB_ADDR,
237 copyFrom(enbAddr.toOctets())))
238 .withParameter(new PiActionParam(PARAM_ID_S1U_SGW_ADDR,
239 copyFrom(s1uAddr.toOctets())))
240 .build();
241
242 final PiCriterion piCriterion = PiCriterion.builder()
243 .matchExact(MF_ID_IPV4_DST, ueAddr.toOctets())
244 .build();
245
246 return flowRuleBuilder(deviceId, TBL_ID_DL_SESS_LOOKUP)
247 .withSelector(DefaultTrafficSelector.builder()
248 .matchPi(piCriterion)
249 .build())
250 .withTreatment(DefaultTrafficTreatment.builder()
251 .piTableAction(action)
252 .build())
253 .build();
254 }
255
256 /**
257 * Returns a new, pre-configured flow rule builder.
258 *
259 * @param did a device id
260 * @param tableId a table id
261 * @return a new flow rule builder
262 */
263 private FlowRule.Builder flowRuleBuilder(DeviceId did, PiTableId tableId) {
264 return DefaultFlowRule.builder()
265 .forDevice(did)
266 .forTable(tableId)
267 .fromApp(appId)
268 .withPriority(0)
269 .makePermanent();
270 }
271
272 private void checkDevice(DeviceId deviceId) {
273 // If device has pipeconf spgw-int
274 Optional<PiPipeconfId> pipeconf = piPipeconfService.ofDevice(deviceId);
275
276 if (!pipeconf.isPresent() || !pipeconf.get().id().contains(SPGW_INT_PIPECONF_KEYWORD)) {
277 log.info("Device {} has no pipeconf associated or pipeconf is not SPGW/INT-capable, ignoring", deviceId);
278 return;
279 }
280
281 confIntSwitch(deviceId);
282
283 if (deviceId.toString().contains(SPGW_DEVICE_KEYWORD)) {
284 confSpgw(deviceId);
285 }
286 }
287
288 private class InternalDeviceListener implements DeviceListener {
289
290 @Override
291 public void event(DeviceEvent event) {
292 if (event.type() != DeviceEvent.Type.DEVICE_ADDED) {
293 return;
294 }
295 DeviceId deviceId = event.subject().id();
296 checkDevice(deviceId);
297
298 }
299 }
300}