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