blob: e883fdc03122eb9ec962f928524f3a154ee12e07 [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;
67 protected final Logger log = getLogger(getClass());
68
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>()
77 .put("leaf1", 0)
78 .put("leaf2", 1)
79 .build();
80
81 private static final PiTableId TBL_ID_INT_INST = PiTableId
82 .of("int_egress.int_metadata_insert.int_inst_0003");
83 private static final PiTableId TBL_ID_INT_PREP = PiTableId
84 .of("int_egress.int_prep");
85
86 private static final PiMatchFieldId FM_ID_INST_MASK = PiMatchFieldId
87 .of("hdr.int_header.instruction_mask_0003");
88
89 private static final PiActionId ACT_ID_INST_ALL = PiActionId
90 .of("int_egress.int_metadata_insert.int_set_header_0003_i15");
91 private static final PiActionId ACT_ID_INT_TRANSIT = PiActionId
92 .of("int_egress.int_transit");
93
94 private static final PiActionParamId ACTP_ID_SWITCH_ID = PiActionParamId
95 .of("switch_id");
96
97 private static final PiTableId TBL_ID_UE_FILTER = PiTableId
98 .of("spgw_ingress.ue_filter_table");
99 private static final PiTableId TBL_ID_DL_SESS_LOOKUP = PiTableId
100 .of("spgw_ingress.dl_sess_lookup");
101 private static final PiMatchFieldId MF_ID_IPV4_DST = PiMatchFieldId
102 .of("ipv4.dst_addr");
103 private static final PiActionId ACT_ID_SET_DL_SESS_INFO = PiActionId
104 .of("spgw_ingress.set_dl_sess_info");
105 private static final PiActionParamId PARAM_S1U_ENB_ADDR = PiActionParamId
106 .of("s1u_enb_addr");
107 private static final PiActionParamId PARAM_ID_S1U_SGW_ADDR = PiActionParamId
108 .of("s1u_sgw_addr");
109 private static final PiAction NO_ACTION = PiAction.builder()
110 .withId(PiActionId.of("NoAction"))
111 .build();
112 private static final PiActionParamId ACTP_TEID = PiActionParamId
113 .of("teid");
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 private DeviceService deviceService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 private FlowRuleService flowRuleService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 private CoreService coreService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 private PiPipeconfService piPipeconfService;
126
127 private final DeviceListener deviceListener = new InternalDeviceListener();
128
129 private ApplicationId appId;
130
131 @Activate
132 public void activate() {
133 log.info("Starting...");
134 appId = coreService.registerApplication("org.onosproject.int-demo");
135 deviceService.getDevices().forEach(d -> checkDevice(d.id()));
136 deviceService.addListener(deviceListener);
137 log.info("STARTED", appId.id());
138 }
139
140 @Deactivate
141 public void deactivate() {
142 log.info("Stopping...");
143 deviceService.removeListener(deviceListener);
144 flowRuleService.removeFlowRulesById(appId);
145 log.info("STOPPED");
146 }
147
148 private void confIntSwitch(DeviceId deviceId) {
149 Optional<Integer> switchId = SWITCH_ID_MAP.keySet().stream()
150 .filter(k -> deviceId.toString().contains(k))
151 .findFirst()
152 .map(SWITCH_ID_MAP::get);
153
154 if (!switchId.isPresent()) {
155 log.error("Unknown INT switch ID for device {}", deviceId);
156 return;
157 }
158
159 log.info("Configuring device {} for INT transit capability: switchId={}...", deviceId, switchId.get());
160
161 FlowRule intPrepRule = flowRuleBuilder(deviceId, TBL_ID_INT_PREP)
162 .withSelector(DefaultTrafficSelector.emptySelector())
163 .withTreatment(
164 DefaultTrafficTreatment.builder()
165 .piTableAction(
166 PiAction.builder()
167 .withId(ACT_ID_INT_TRANSIT)
168 .withParameter(new PiActionParam(
169 ACTP_ID_SWITCH_ID, copyFrom(0)))
170 .build())
171 .build())
172 .build();
173
174 FlowRule intIntrRule = flowRuleBuilder(deviceId, TBL_ID_INT_INST)
175 .withSelector(
176 DefaultTrafficSelector.builder()
177 .matchPi(
178 PiCriterion.builder()
179 .matchExact(FM_ID_INST_MASK, (byte) 0x0F)
180 .build())
181 .build())
182 .withTreatment(
183 DefaultTrafficTreatment.builder()
184 .piTableAction(PiAction.builder()
185 .withId(ACT_ID_INST_ALL)
186 .build())
187 .build())
188 .build();
189
190 flowRuleService.applyFlowRules(intPrepRule, intIntrRule);
191 }
192
193 private void confSpgw(DeviceId deviceId) {
194 log.info("Configuring device {} for SPGW downlink processing...", deviceId);
195 UE_ADDRS.stream()
196 .map(Ip4Address::valueOf)
197 .forEach(ueAddr -> {
198 flowRuleService.applyFlowRules(ueFilterRule(deviceId, ueAddr));
199 flowRuleService.applyFlowRules(
200 dlSessLookupRule(deviceId, ueAddr, DEFAULT_TEID, ueAddr, S1U_SGW_ADDR)
201 );
202 });
203 }
204
205 private FlowRule ueFilterRule(DeviceId deviceId, Ip4Address ueAddr) {
206 final PiCriterion piCriterion = PiCriterion.builder()
207 .matchLpm(MF_ID_IPV4_DST, ueAddr.toOctets(), 32)
208 .build();
209
210 return flowRuleBuilder(deviceId, TBL_ID_UE_FILTER)
211 .withSelector(DefaultTrafficSelector.builder()
212 .matchPi(piCriterion)
213 .build())
214 .withTreatment(DefaultTrafficTreatment.builder()
215 .piTableAction(NO_ACTION)
216 .build())
217 .build();
218 }
219
220 private FlowRule dlSessLookupRule(DeviceId deviceId, Ip4Address ueAddr, long teid,
221 Ip4Address enbAddr, Ip4Address s1uAddr) {
222
223 // FIXME: the long teid is obtained from the wrong byte order
224 ImmutableByteSequence teidBs = copyFrom(teid);
225 byte[] byteArray = teidBs.asArray();
226 ArrayUtils.reverse(byteArray);
227 byte[] trimmedArray = Arrays.copyOfRange(byteArray, 0, 4);
228 ImmutableByteSequence revTeidBs = copyFrom(trimmedArray);
229
230 // Add rule to
231 final PiAction action = PiAction.builder()
232 .withId(ACT_ID_SET_DL_SESS_INFO)
233 .withParameter(new PiActionParam(ACTP_TEID, revTeidBs))
234 .withParameter(new PiActionParam(PARAM_S1U_ENB_ADDR,
235 copyFrom(enbAddr.toOctets())))
236 .withParameter(new PiActionParam(PARAM_ID_S1U_SGW_ADDR,
237 copyFrom(s1uAddr.toOctets())))
238 .build();
239
240 final PiCriterion piCriterion = PiCriterion.builder()
241 .matchExact(MF_ID_IPV4_DST, ueAddr.toOctets())
242 .build();
243
244 return flowRuleBuilder(deviceId, TBL_ID_DL_SESS_LOOKUP)
245 .withSelector(DefaultTrafficSelector.builder()
246 .matchPi(piCriterion)
247 .build())
248 .withTreatment(DefaultTrafficTreatment.builder()
249 .piTableAction(action)
250 .build())
251 .build();
252 }
253
254 /**
255 * Returns a new, pre-configured flow rule builder.
256 *
257 * @param did a device id
258 * @param tableId a table id
259 * @return a new flow rule builder
260 */
261 private FlowRule.Builder flowRuleBuilder(DeviceId did, PiTableId tableId) {
262 return DefaultFlowRule.builder()
263 .forDevice(did)
264 .forTable(tableId)
265 .fromApp(appId)
266 .withPriority(0)
267 .makePermanent();
268 }
269
270 private void checkDevice(DeviceId deviceId) {
271 // If device has pipeconf spgw-int
272 Optional<PiPipeconfId> pipeconf = piPipeconfService.ofDevice(deviceId);
273
274 if (!pipeconf.isPresent() || !pipeconf.get().id().contains(SPGW_INT_PIPECONF_KEYWORD)) {
275 log.info("Device {} has no pipeconf associated or pipeconf is not SPGW/INT-capable, ignoring", deviceId);
276 return;
277 }
278
279 confIntSwitch(deviceId);
280
281 if (deviceId.toString().contains(SPGW_DEVICE_KEYWORD)) {
282 confSpgw(deviceId);
283 }
284 }
285
286 private class InternalDeviceListener implements DeviceListener {
287
288 @Override
289 public void event(DeviceEvent event) {
290 if (event.type() != DeviceEvent.Type.DEVICE_ADDED) {
291 return;
292 }
293 DeviceId deviceId = event.subject().id();
294 checkDevice(deviceId);
295
296 }
297 }
298}