blob: ada38f4f88a93974569d57ffdd27dd3e675f18cd [file] [log] [blame]
Andreas Pantelopouloscd339592018-02-23 14:18:00 -08001/*
2 * Copyright 2015-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.segmentrouting.pwaas;
18
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080019import org.onlab.packet.MplsLabel;
20import org.onlab.packet.VlanId;
21import org.onosproject.cli.AbstractShellCommand;
22import org.onosproject.net.ConnectPoint;
23
24import java.util.HashMap;
25import java.util.HashSet;
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -070026import java.util.List;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080027import java.util.Map;
28import java.util.Set;
29
30import org.onosproject.net.device.DeviceService;
31import org.onosproject.net.intf.InterfaceService;
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070035import static com.google.common.base.Preconditions.checkArgument;
36
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080037/**
38 * Utility class with static methods that help
39 * parse pseudowire related information and also
40 * verify that a pseudowire combination is valid.
41 */
42public final class PwaasUtil {
43
44 private static final Logger log = LoggerFactory.getLogger(PwaasUtil.class);
45
Sangyeok Sim7ff935e2018-06-11 10:29:14 +090046 private static DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080047
Ray Milkey35d748c2018-02-27 08:56:30 -080048 private static InterfaceService intfService = AbstractShellCommand.get(InterfaceService.class);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080049
50 private PwaasUtil() {
51 return;
52 }
53
54 /**
55 * Parses a vlan as a string. Returns the VlanId if
56 * provided String can be parsed as an integer or is '' / '*'
57 *
58 * @param vlan string as read from configuration
59 * @return VlanId null if error
60 */
61 public static VlanId parseVlan(String vlan) {
62
63 if (vlan.equals("*") || vlan.equals("Any")) {
64 return VlanId.vlanId("Any");
65 } else if (vlan.equals("") || vlan.equals("None")) {
66 return VlanId.vlanId("None");
67 } else {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070068 return VlanId.vlanId(vlan);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080069 }
70 }
71
72 /**
73 *
74 * @param mode RAW or TAGGED
75 * @return the L2Mode if input is correct
76 */
77 public static L2Mode parseMode(String mode) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070078 checkArgument(mode.equals("RAW") || mode.equals("TAGGED"),
79 "Invalid pseudowire mode of operation, should be TAGGED or RAW.");
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080080 return L2Mode.valueOf(mode);
81 }
82
83 /**
84 *
85 * @param label the mpls label of the pseudowire
86 * @return the MplsLabel
87 * @throws IllegalArgumentException if label is invalid
88 */
89 public static MplsLabel parsePWLabel(String label) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -070090 return MplsLabel.mplsLabel(label);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -080091 }
92
93 /**
94 * Parses a string as a pseudowire id - which is an integer.
95 *
96 * @param id The id of pw in string form
97 * @return The id of pw as an Integer or null if it failed the conversion.
98 */
99 public static Integer parsePwId(String id) {
Andreas Pantelopoulos5bf13662018-04-10 19:34:47 -0700100 return Integer.parseInt(id);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800101 }
102
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800103 /**
104 * Helper method to verify if the tunnel is whether or not
105 * supported.
106 *
107 * @param l2Tunnel the tunnel to verify
108 */
109 private static void verifyTunnel(L2Tunnel l2Tunnel) {
110
111 // Service delimiting tag not supported yet.
112 if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
113 throw new IllegalArgumentException(String.format("Service delimiting tag not supported yet for " +
114 "pseudowire %d.", l2Tunnel.tunnelId()));
115 }
116
117 // Tag mode not supported yet.
118 if (l2Tunnel.pwMode() == L2Mode.TAGGED) {
119 throw new IllegalArgumentException(String.format("Tagged mode not supported yet for pseudowire %d.",
120 l2Tunnel.tunnelId()));
121 }
122
123 // Raw mode without service delimiting tag
124 // is the only mode supported for now.
125 }
126
127 /**
128 * Helper method to verify if the policy is whether or not
129 * supported and if policy will be successfully instantiated in the
130 * network.
131 *
132 * @param ingressInner the ingress inner tag
133 * @param ingressOuter the ingress outer tag
134 * @param egressInner the egress inner tag
135 * @param egressOuter the egress outer tag
136 */
137 private static void verifyPolicy(ConnectPoint cP1,
138 ConnectPoint cP2,
139 VlanId ingressInner,
140 VlanId ingressOuter,
141 VlanId egressInner,
142 VlanId egressOuter,
143 Long tunnelId) {
144
145 if (cP1.deviceId().equals(cP2.deviceId())) {
146 throw new IllegalArgumentException(String.format("Pseudowire connection points can not reside in the " +
147 "same node, in pseudowire %d.", tunnelId));
148 }
149
150 // We can have multiple tags, all of them can be NONE,
151 // indicating untagged traffic, however, the outer tag can
152 // not have value if the inner tag is None
153 if (ingressInner.equals(VlanId.NONE) && !ingressOuter.equals(VlanId.NONE)) {
154 throw new IllegalArgumentException(String.format("Inner tag should not be empty when " +
155 "outer tag is set for pseudowire %d for cP1.",
156 tunnelId));
157 }
158
159 if (egressInner.equals(VlanId.NONE) && !egressOuter.equals(VlanId.NONE)) {
160 throw new IllegalArgumentException(String.valueOf(String.format("Inner tag should not be empty when" +
161 " outer tag is set for " +
162 "pseudowire %d " +
163 "for cP2.", tunnelId)));
164 }
165
166 if (ingressInner.equals(VlanId.ANY) ||
167 ingressOuter.equals(VlanId.ANY) ||
168 egressInner.equals(VlanId.ANY) ||
169 egressOuter.equals(VlanId.ANY)) {
170 throw new IllegalArgumentException(String.valueOf(String.format("Wildcard VLAN matching not yet " +
171 "supported for pseudowire %d.",
172 tunnelId)));
173 }
174
175 if (((!ingressOuter.equals(VlanId.NONE) && !ingressInner.equals(VlanId.NONE)) &&
176 (egressOuter.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE)))
177 || ((ingressOuter.equals(VlanId.NONE) && ingressInner.equals(VlanId.NONE)) &&
178 (!egressOuter.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE)))) {
179 throw new IllegalArgumentException(String.valueOf(String.format("Support for double tag <-> untag is not" +
180 "supported for pseudowire %d.",
181 tunnelId)));
182 }
183 if ((!ingressInner.equals(VlanId.NONE) &&
184 ingressOuter.equals(VlanId.NONE) &&
185 !egressOuter.equals(VlanId.NONE))
186 || (egressOuter.equals(VlanId.NONE) &&
187 !egressInner.equals(VlanId.NONE) &&
188 !ingressOuter.equals(VlanId.NONE))) {
189 throw new IllegalArgumentException(String.valueOf(String.format("Support for double-tag<->" +
190 "single-tag is not supported" +
191 " for pseudowire %d.", tunnelId)));
192 }
193
194 if ((ingressInner.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE))
195 || (!ingressInner.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE))) {
196 throw new IllegalArgumentException(String.valueOf(String.format("single-tag <-> untag is not supported" +
197 " for pseudowire %d.", tunnelId)));
198 }
199
200
201 if (!ingressInner.equals(egressInner) && !ingressOuter.equals(egressOuter)) {
202 throw new IllegalArgumentException(String.valueOf(String.format("We do not support changing both tags " +
203 "in double tagged pws, only the " +
204 "outer," +
205 " for pseudowire %d.", tunnelId)));
206 }
207
208 // check if cp1 and port of cp1 exist
209 if (deviceService.getDevice(cP1.deviceId()) == null) {
210 throw new IllegalArgumentException(String.valueOf(String.format("cP1 device %s does not exist for" +
211 " pseudowire %d.", cP1.deviceId(),
212 tunnelId)));
213 }
214
215 if (deviceService.getPort(cP1) == null) {
216 throw new IllegalArgumentException(String.valueOf(String.format("Port %s for cP1 device %s does not" +
217 " exist for pseudowire %d.",
218 cP1.port(),
219 cP1.deviceId(), tunnelId)));
220 }
221
222 // check if cp2 and port of cp2 exist
223 if (deviceService.getDevice(cP2.deviceId()) == null) {
224 throw new IllegalArgumentException(String.valueOf(String.format("cP2 device %s does not exist for" +
225 " pseudowire %d.", cP2.deviceId(),
226 tunnelId)));
227 }
228
229 if (deviceService.getPort(cP2) == null) {
230 throw new IllegalArgumentException(String.valueOf(String.format("Port %s for cP2 device %s does " +
231 "not exist for pseudowire %d.",
232 cP2.port(), cP2.deviceId(), tunnelId)));
233 }
234 }
235
236 /**
237 * Verifies that the pseudowires will not conflict with each other.
238 *
239 * Further, check if vlans for connect points are already used.
240 *
241 * @param tunnel Tunnel for pw
242 * @param policy Policy for pw
243 * @param labelSet Label set used so far with this configuration
244 * @param vlanSet Vlan set used with this configuration
245 * @param tunnelSet Tunnel set used with this configuration
246 */
247 private static void verifyGlobalValidity(L2Tunnel tunnel,
248 L2TunnelPolicy policy,
249 Set<MplsLabel> labelSet,
250 Map<ConnectPoint, Set<VlanId>> vlanSet,
251 Set<Long> tunnelSet) {
252
253 if (tunnelSet.contains(tunnel.tunnelId())) {
254 throw new IllegalArgumentException(String.valueOf(String.format("Tunnel Id %d already used by" +
255 " another pseudowire, in " +
256 "pseudowire %d!",
257 tunnel.tunnelId(),
258 tunnel.tunnelId())));
259 }
260 tunnelSet.add(tunnel.tunnelId());
261
262 // check if tunnel id is used again
263 ConnectPoint cP1 = policy.cP1();
264 ConnectPoint cP2 = policy.cP2();
265
266 // insert cps to hashmap if this is the first time seen
267 if (!vlanSet.containsKey(cP1)) {
268 vlanSet.put(cP1, new HashSet<VlanId>());
269 }
270 if (!vlanSet.containsKey(cP2)) {
271 vlanSet.put(cP2, new HashSet<VlanId>());
272 }
273
274 // if single tagged or untagged vlan is the inner
275 // if double tagged vlan is the outer
276 VlanId vlanToCheckCP1;
277 if (policy.cP1OuterTag().equals(VlanId.NONE)) {
278 vlanToCheckCP1 = policy.cP1InnerTag();
279 } else {
280 vlanToCheckCP1 = policy.cP1OuterTag();
281 }
282
283 VlanId vlanToCheckCP2;
284 if (policy.cP2OuterTag().equals(VlanId.NONE)) {
285 vlanToCheckCP2 = policy.cP2InnerTag();
286 } else {
287 vlanToCheckCP2 = policy.cP2OuterTag();
288 }
289
290 if (labelSet.contains(tunnel.pwLabel())) {
291 throw new IllegalArgumentException(String.valueOf(String.format("Label %s already used by another" +
292 " pseudowire, in pseudowire %d!",
293 tunnel.pwLabel(), tunnel.tunnelId())));
294 }
295 labelSet.add(tunnel.pwLabel());
296
297 if (vlanSet.get(cP1).contains(vlanToCheckCP1)) {
298 throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already used " +
299 "by another pseudowire, in " +
300 "pseudowire" +
301 " %d!", vlanToCheckCP1, cP1,
302 tunnel.tunnelId())));
303 }
304 vlanSet.get(cP1).add(vlanToCheckCP1);
305
306 if (vlanSet.get(cP2).contains(vlanToCheckCP2)) {
307 throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s already used" +
308 " by another pseudowire, in" +
309 " pseudowire %d!", vlanToCheckCP2,
310 cP2,
311 tunnel.tunnelId())));
312 }
313 vlanSet.get(cP2).add(vlanToCheckCP2);
314
315 // check that vlans for the connect points are not used
316 intfService.getInterfacesByPort(cP1).stream()
317 .forEach(intf -> {
318
319 // check if tagged pw affects tagged interface
320 if (intf.vlanTagged().contains(vlanToCheckCP1)) {
321 throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already" +
322 " used for this" +
323 " interface, in" +
324 " pseudowire %d!",
325 vlanToCheckCP1, cP1,
326 tunnel.tunnelId())));
327 }
328
329 // if vlanNative != null this interface is configured with untagged traffic also
330 // check if it collides with untagged interface
331 if ((intf.vlanNative() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
332 throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP1 " +
333 "%s already used " +
334 "for this " +
335 "interface, in " +
336 "pseudowire " +
337 "%d!", cP1,
338 tunnel.tunnelId())));
339 }
340
341 // if vlanUntagged != null this interface is configured only with untagged traffic
342 // check if it collides with untagged interface
343 if ((intf.vlanUntagged() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
344 throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for " +
345 "cP1 %s already" +
346 " used for this" +
347 " interface," +
348 " in pseudowire %d!",
349 cP1, tunnel.tunnelId())));
350 }
351 });
352
353 intfService.getInterfacesByPort(cP2).stream()
354 .forEach(intf -> {
355 if (intf.vlanTagged().contains(vlanToCheckCP2)) {
356 throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s " +
357 " used for " +
358 "this interface, " +
359 "in pseudowire %d!",
360 vlanToCheckCP2, cP2,
361 tunnel.tunnelId())));
362 }
363
364 // if vlanNative != null this interface is configured with untagged traffic also
365 // check if it collides with untagged interface
366 if ((intf.vlanNative() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
367 throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic " +
368 "for cP2 %s " +
369 "already " +
370 "used for this" +
371 " interface, " +
372 "in pseudowire %d!",
373 cP2, tunnel.tunnelId())));
374 }
375
376 // if vlanUntagged != null this interface is configured only with untagged traffic
377 // check if it collides with untagged interface
378 if ((intf.vlanUntagged() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
379 throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP2 %s" +
380 " already" +
381 " used for " +
382 "this interface, " +
383 "in pseudowire %d!",
384 cP2, tunnel.tunnelId())));
385 }
386 });
387
388 }
389
390 /**
391 * Helper method to verify the integrity of the pseudo wire.
392 *
393 * @param l2TunnelDescription the pseudo wire description
394 */
395 private static void verifyPseudoWire(L2TunnelDescription l2TunnelDescription,
396 Set<MplsLabel> labelSet,
397 Map<ConnectPoint, Set<VlanId>> vlanset,
398 Set<Long> tunnelSet) {
399
400 L2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
401 L2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
402
403 verifyTunnel(l2Tunnel);
404
405 verifyPolicy(
406 l2TunnelPolicy.cP1(),
407 l2TunnelPolicy.cP2(),
408 l2TunnelPolicy.cP1InnerTag(),
409 l2TunnelPolicy.cP1OuterTag(),
410 l2TunnelPolicy.cP2InnerTag(),
411 l2TunnelPolicy.cP2OuterTag(),
412 l2Tunnel.tunnelId()
413 );
414
415 verifyGlobalValidity(l2Tunnel,
416 l2TunnelPolicy,
417 labelSet,
418 vlanset,
419 tunnelSet);
420
421 }
422
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700423 public static L2TunnelHandler.Result configurationValidity(List<L2TunnelDescription> pseudowires) {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800424
425 // structures to keep pw information
426 // in order to see if instantiating them will create
427 // problems
428 Set<Long> tunIds = new HashSet<>();
429 Set<MplsLabel> labelsUsed = new HashSet<>();
430 Map<ConnectPoint, Set<VlanId>> vlanIds = new HashMap<>();
431
432 // TODO : I know we should not use exceptions for flow control,
433 // however this code was originally implemented in the configuration
434 // addition where the exceptions were propagated and the configuration was
435 // deemed not valid. I plan in the future to refactor the parts that
436 // check the pseudowire validity.
437 //
438 // Ideally we would like to return a String which could also return to
439 // the user issuing the rest request for adding the pseudowire.
440 try {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800441 // check that pseudowires can be instantiated in the network
442 // we try to guarantee that all the pws will work before
443 // instantiating any of them
444 for (L2TunnelDescription pw : pseudowires) {
Andreas Pantelopoulos811bbae2018-03-15 16:56:09 -0700445 log.debug("Verifying pseudowire {}", pw);
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800446 verifyPseudoWire(pw, labelsUsed, vlanIds, tunIds);
447 }
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700448
449 return L2TunnelHandler.Result.SUCCESS;
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800450 } catch (Exception e) {
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800451 log.error("Caught exception while validating pseudowire : {}", e.getMessage());
Andreas Pantelopoulosffe69742018-03-20 13:58:49 -0700452 return L2TunnelHandler.Result.CONFIGURATION_ERROR
453 .appendError(e.getMessage());
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800454 }
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800455 }
Andreas Pantelopouloscd339592018-02-23 14:18:00 -0800456}