blob: 7605a5e0aa7fae2cfe110b22cc673278ee7503b8 [file] [log] [blame]
Jian Li4aa17642019-01-30 00:01:11 +09001/*
2 * Copyright 2019-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 */
16package org.onosproject.k8snetworking.util;
17
18import org.onlab.packet.Ip4Address;
Jian Li2cc2b632019-02-18 00:56:40 +090019import org.onlab.packet.IpAddress;
20import org.onlab.packet.TpPort;
Jian Li4aa17642019-01-30 00:01:11 +090021import org.onosproject.net.Device;
22import org.onosproject.net.DeviceId;
Jian Li2cc2b632019-02-18 00:56:40 +090023import org.onosproject.net.behaviour.ExtensionSelectorResolver;
Jian Li4aa17642019-01-30 00:01:11 +090024import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
25import org.onosproject.net.device.DeviceService;
Jian Li2cc2b632019-02-18 00:56:40 +090026import org.onosproject.net.driver.DriverHandler;
27import org.onosproject.net.driver.DriverService;
28import org.onosproject.net.flow.TrafficTreatment;
29import org.onosproject.net.flow.criteria.ExtensionSelector;
30import org.onosproject.net.flow.criteria.ExtensionSelectorType;
Jian Li4aa17642019-01-30 00:01:11 +090031import org.onosproject.net.flow.instructions.ExtensionPropertyException;
32import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li2cc2b632019-02-18 00:56:40 +090033import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
34import org.onosproject.net.group.DefaultGroupBucket;
35import org.onosproject.net.group.GroupBucket;
36import org.onosproject.net.group.GroupDescription.Type;
Jian Li4aa17642019-01-30 00:01:11 +090037import org.slf4j.Logger;
38
Jian Li2cc2b632019-02-18 00:56:40 +090039import java.util.ArrayList;
40import java.util.List;
41
Jian Li004526d2019-02-25 16:26:27 +090042import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_LOAD;
Jian Li2cc2b632019-02-18 00:56:40 +090043import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
Jian Li4aa17642019-01-30 00:01:11 +090044import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
45import static org.slf4j.LoggerFactory.getLogger;
46
47/**
48 * Provides common methods to help populating flow rules for SONA applications.
49 */
50public final class RulePopulatorUtil {
51
52 private static final Logger log = getLogger(RulePopulatorUtil.class);
53
54 private static final String TUNNEL_DST = "tunnelDst";
55
Jian Li2cc2b632019-02-18 00:56:40 +090056 private static final String CT_FLAGS = "flags";
57 private static final String CT_ZONE = "zone";
58 private static final String CT_TABLE = "recircTable";
59 private static final String CT_STATE = "ctState";
60 private static final String CT_STATE_MASK = "ctStateMask";
61 private static final String CT_PRESENT_FLAGS = "presentFlags";
62 private static final String CT_IPADDRESS_MIN = "ipAddressMin";
63 private static final String CT_IPADDRESS_MAX = "ipAddressMax";
64 private static final String CT_PORT_MIN = "portMin";
65 private static final String CT_PORT_MAX = "portMax";
66 private static final String CT_NESTED_ACTIONS = "nestedActions";
67
68 public static final int CT_NAT_SRC_FLAG = 0;
69 public static final int CT_NAT_DST_FLAG = 1;
70 public static final int CT_NAT_PERSISTENT_FLAG = 2;
71 public static final int CT_NAT_PROTO_HASH_FLAG = 3;
72 public static final int CT_NAT_PROTO_RANDOM_FLAG = 4;
73
74 private static final int ADDRESS_V4_MIN_FLAG = 0;
75 private static final int ADDRESS_V4_MAX_FLAG = 1;
76 private static final int ADDRESS_V6_MIN_FLAG = 2;
77 private static final int ADDRESS_V6_MAX_FLAG = 3;
78 private static final int PORT_MIN_FLAG = 4;
79 private static final int PORT_MAX_FLAG = 5;
80
Jian Li004526d2019-02-25 16:26:27 +090081 private static final long CT_STATE_NONE = 0;
82 private static final long CT_STATE_NEW = 0x01;
83 private static final long CT_STATE_EST = 0x02;
84 private static final long CT_STATE_NOT_TRK = 0x20;
85 private static final long CT_STATE_TRK = 0x20;
Jian Li2cc2b632019-02-18 00:56:40 +090086
87 private static final String TABLE_EXTENSION = "table";
88
Jian Li004526d2019-02-25 16:26:27 +090089 private static final String OFF_SET_N_BITS = "ofsNbits";
90 private static final String DESTINATION = "dst";
91 private static final String VALUE = "value";
92
93 private static final int SRC_IP = 0x00000e04;
94 private static final int DST_IP = 0x00001004;
95
96 private static final String SRC = "src";
97 private static final String DST = "dst";
98
99 private static final int OFF_SET_BIT = 16;
100 private static final int REMAINDER_BIT = 16;
101
Jian Li2cc2b632019-02-18 00:56:40 +0900102 // not intended for direct invocation from external
Jian Li4aa17642019-01-30 00:01:11 +0900103 private RulePopulatorUtil() {
104 }
105
106 /**
Jian Li2cc2b632019-02-18 00:56:40 +0900107 * Returns a builder for OVS Connection Tracking feature actions.
108 *
109 * @param ds DriverService
110 * @param id DeviceId
111 * @return a builder for OVS Connection Tracking feature actions
112 */
113 public static NiciraConnTrackTreatmentBuilder
114 niciraConnTrackTreatmentBuilder(DriverService ds, DeviceId id) {
115 return new NiciraConnTrackTreatmentBuilder(ds, id);
116 }
117
118 /**
119 * Builds OVS ConnTrack matches.
120 *
121 * @param driverService driver service
122 * @param deviceId device ID
123 * @param ctState connection tracking sate masking value
124 * @param ctSateMask connection tracking sate masking value
125 * @return OVS ConnTrack extension match
126 */
127 public static ExtensionSelector buildCtExtensionSelector(DriverService driverService,
128 DeviceId deviceId,
129 long ctState,
130 long ctSateMask) {
131 DriverHandler handler = driverService.createHandler(deviceId);
132 ExtensionSelectorResolver esr = handler.behaviour(ExtensionSelectorResolver.class);
133
134 ExtensionSelector extensionSelector = esr.getExtensionSelector(
135 ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_CONNTRACK_STATE.type());
136 try {
137 extensionSelector.setPropertyValue(CT_STATE, ctState);
138 extensionSelector.setPropertyValue(CT_STATE_MASK, ctSateMask);
139 } catch (Exception e) {
140 log.error("Failed to set nicira match CT state because of {}", e);
141 return null;
142 }
143
144 return extensionSelector;
145 }
146
147 /**
148 * Computes ConnTack State flag values.
149 *
150 * @param isTracking true for +trk, false for -trk
151 * @param isNew true for +new, false for -new
152 * @param isEstablished true for +est, false for -est
153 * @return ConnTrack State flags
154 */
155 public static long computeCtStateFlag(boolean isTracking,
156 boolean isNew,
157 boolean isEstablished) {
158 long ctStateFlag = 0x00;
159
160 if (isTracking) {
161 ctStateFlag = ctStateFlag | CT_STATE_TRK;
162 }
163
164 if (isNew) {
165 ctStateFlag = ctStateFlag | CT_STATE_TRK;
166 ctStateFlag = ctStateFlag | CT_STATE_NEW;
167 }
168
169 if (isEstablished) {
170 ctStateFlag = ctStateFlag | CT_STATE_TRK;
171 ctStateFlag = ctStateFlag | CT_STATE_EST;
172 }
173
174 return ctStateFlag;
175 }
176
177 /**
178 * Computes ConnTrack State mask values.
179 *
180 * @param isTracking true for setting +trk/-trk value, false for otherwise
181 * @param isNew true for setting +new/-new value, false for otherwise
182 * @param isEstablished true for setting +est/-est value, false for otherwise
183 * @return ConnTrack State Mask value
184 */
185 public static long computeCtMaskFlag(boolean isTracking,
186 boolean isNew,
187 boolean isEstablished) {
188 long ctMaskFlag = 0x00;
189
190 if (isTracking) {
191 ctMaskFlag = ctMaskFlag | CT_STATE_TRK;
192 }
193
194 if (isNew) {
195 ctMaskFlag = ctMaskFlag | CT_STATE_TRK;
196 ctMaskFlag = ctMaskFlag | CT_STATE_NEW;
197 }
198
199 if (isEstablished) {
200 ctMaskFlag = ctMaskFlag | CT_STATE_TRK;
201 ctMaskFlag = ctMaskFlag | CT_STATE_EST;
202 }
203
204 return ctMaskFlag;
205 }
206
207 /**
Jian Li4aa17642019-01-30 00:01:11 +0900208 * Returns tunnel destination extension treatment object.
209 *
210 * @param deviceService driver service
211 * @param deviceId device id to apply this treatment
212 * @param remoteIp tunnel destination ip address
213 * @return extension treatment
214 */
215 public static ExtensionTreatment buildExtension(DeviceService deviceService,
216 DeviceId deviceId,
217 Ip4Address remoteIp) {
218 Device device = deviceService.getDevice(deviceId);
219 if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
220 log.error("The extension treatment is not supported");
221 return null;
222 }
223
224 if (device == null) {
225 return null;
226 }
227
228 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
229 ExtensionTreatment treatment =
230 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
231 try {
232 treatment.setPropertyValue(TUNNEL_DST, remoteIp);
233 return treatment;
234 } catch (ExtensionPropertyException e) {
235 log.warn("Failed to get tunnelDst extension treatment for {} " +
236 "because of {}", deviceId, e);
237 return null;
238 }
239 }
Jian Li2cc2b632019-02-18 00:56:40 +0900240
241 /**
242 * Returns the group bucket with given traffic treatment and group type.
243 *
244 * @param treatment traffic treatment
245 * @param type group type
246 * @param weight weight (only for select type)
247 * @return group bucket
248 */
249 public static GroupBucket buildGroupBucket(TrafficTreatment treatment,
250 Type type, short weight) {
251 switch (type) {
252 case ALL:
253 return DefaultGroupBucket.createAllGroupBucket(treatment);
254 case SELECT:
255 if (weight == -1) {
256 return DefaultGroupBucket.createSelectGroupBucket(treatment);
257 } else {
258 return DefaultGroupBucket.createSelectGroupBucket(treatment, weight);
259 }
260 case INDIRECT:
261 return DefaultGroupBucket.createIndirectGroupBucket(treatment);
262 default:
263 return null;
264 }
265 }
266
267 /**
268 * Returns the nicira resubmit extension treatment with given table ID.
269 *
Jian Li004526d2019-02-25 16:26:27 +0900270 * @param device device instance
Jian Li2cc2b632019-02-18 00:56:40 +0900271 * @param tableId table identifier
272 * @return resubmit extension treatment
273 */
274 public static ExtensionTreatment buildResubmitExtension(Device device, int tableId) {
275 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
276 log.warn("Nicira extension treatment is not supported");
277 return null;
278 }
279
280 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
281 ExtensionTreatment treatment =
282 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
283
284 try {
285 treatment.setPropertyValue(TABLE_EXTENSION, ((short) tableId));
286 return treatment;
287 } catch (ExtensionPropertyException e) {
288 log.error("Failed to set nicira resubmit extension treatment for {}",
289 device.id());
290 return null;
291 }
292 }
293
294 /**
Jian Li004526d2019-02-25 16:26:27 +0900295 * Returns the nicira load extension treatment.
296 *
297 * @param device device instance
298 * @param ipType IP type (src|dst)
299 * @param shift shift (e.g., 10.10., 20.20.,)
300 * @return load extension treatment
301 */
302 public static ExtensionTreatment buildLoadExtension(Device device,
303 String ipType,
304 String shift) {
305 if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
306 log.warn("Nicira extension treatment is not supported");
307 return null;
308 }
309
310 ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
311 ExtensionTreatment treatment =
312 resolver.getExtensionInstruction(NICIRA_LOAD.type());
313
314 long dst = 0L;
315
316 if (SRC.equalsIgnoreCase(ipType)) {
317 dst = SRC_IP;
318 } else if (DST.equals(ipType)) {
319 dst = DST_IP;
320 }
321
322 long value = calculateUpperBit(shift);
323
324 // we only rewrite the upper 16 bits with value (A.B.X.Y -> C.D.X.Y)
325 int ofsNbits = OFF_SET_BIT << 6 | (REMAINDER_BIT - 1);
326
327 try {
328 treatment.setPropertyValue(OFF_SET_N_BITS, ofsNbits);
329 treatment.setPropertyValue(DESTINATION, dst);
330 treatment.setPropertyValue(VALUE, value);
331 return treatment;
332 } catch (ExtensionPropertyException e) {
333 log.error("Failed to set nicira load extension treatment for {}",
334 device.id());
335 return null;
336 }
337 }
338
339 /**
340 * Calculate IP address upper string into integer.
341 *
342 * @param shift IP address upper two octets with dot
343 * @return calculated integer
344 */
345 private static int calculateUpperBit(String shift) {
346 String[] strArray = shift.split("\\.");
347
348 int firstOctet = Integer.valueOf(strArray[0]);
349 int secondOctet = Integer.valueOf(strArray[1]);
350
351 return firstOctet << 8 | secondOctet;
352 }
353
354 /**
Jian Li2cc2b632019-02-18 00:56:40 +0900355 * Builder class for OVS Connection Tracking feature actions.
356 */
357 public static final class NiciraConnTrackTreatmentBuilder {
358
359 private DriverService driverService;
360 private DeviceId deviceId;
361 private IpAddress natAddress = null;
362 private TpPort natPort = null;
363 private int zone;
364 private boolean commit;
365 private short table = -1;
366 private boolean natAction;
367 private int natFlag;
368
369 // private constructor
370 private NiciraConnTrackTreatmentBuilder(DriverService driverService,
371 DeviceId deviceId) {
372 this.driverService = driverService;
373 this.deviceId = deviceId;
374 }
375
376 /**
377 * Sets commit flag.
378 *
379 * @param c true if commit, false if not.
380 * @return NiciraConnTrackTreatmentBuilder object
381 */
382 public NiciraConnTrackTreatmentBuilder commit(boolean c) {
383 this.commit = c;
384 return this;
385 }
386
387 /**
388 * Sets zone number.
389 *
390 * @param z zone number
391 * @return NiciraConnTrackTreatmentBuilder object
392 */
393 public NiciraConnTrackTreatmentBuilder zone(int z) {
394 this.zone = z;
395 return this;
396 }
397
398 /**
399 * Sets recirculation table number.
400 *
401 * @param t table number to restart
402 * @return NiciraConnTrackTreatmentBuilder object
403 */
404 public NiciraConnTrackTreatmentBuilder table(short t) {
405 this.table = t;
406 return this;
407 }
408
409 /**
410 * Sets IP address for NAT.
411 *
412 * @param ip NAT IP address
413 * @return NiciraConnTrackTreatmentBuilder object
414 */
415 public NiciraConnTrackTreatmentBuilder natIp(IpAddress ip) {
416 this.natAddress = ip;
417 return this;
418 }
419
420 /**
421 * Sets port for NAT.
422 *
423 * @param port port number
424 * @return NiciraConnTrackTreatmentBuilder object
425 */
426 public NiciraConnTrackTreatmentBuilder natPort(TpPort port) {
427 this.natPort = port;
428 return this;
429 }
430
431 /**
432 * Sets NAT flags.
433 * SRC NAT: 1 << 0
434 * DST NAT: 1 << 1
435 * PERSISTENT NAT: 1 << 2
436 * PROTO_HASH NAT: 1 << 3
437 * PROTO_RANDOM NAT : 1 << 4
438 *
439 * @param flag flag value
440 * @return NiciraConnTrackTreatmentBuilder object
441 */
442 public NiciraConnTrackTreatmentBuilder natFlag(int flag) {
443 this.natFlag = 1 << flag;
444 return this;
445 }
446
447 /**
448 * Sets the flag for NAT action.
449 *
450 * @param nat nat action is included if true, no nat action otherwise
451 * @return NiciraConnTrackTreatmentBuilder object
452 */
453 public NiciraConnTrackTreatmentBuilder natAction(boolean nat) {
454 this.natAction = nat;
455 return this;
456 }
457
458 /**
459 * Builds extension treatment for OVS ConnTack and NAT feature.
460 *
461 * @return ExtensionTreatment object
462 */
463 public ExtensionTreatment build() {
464 DriverHandler handler = driverService.createHandler(deviceId);
465 ExtensionTreatmentResolver etr =
466 handler.behaviour(ExtensionTreatmentResolver.class);
467
468 ExtensionTreatment natTreatment = etr.getExtensionInstruction(
469 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NAT.type());
470 try {
471
472 natTreatment.setPropertyValue(CT_FLAGS, this.natFlag);
473
474 natTreatment.setPropertyValue(CT_PRESENT_FLAGS,
475 buildPresentFlag(natPort != null, natAddress != null));
476
477 if (natAddress != null) {
478 natTreatment.setPropertyValue(CT_IPADDRESS_MIN, natAddress);
479 natTreatment.setPropertyValue(CT_IPADDRESS_MAX, natAddress);
480 }
481
482 if (natPort != null) {
483 natTreatment.setPropertyValue(CT_PORT_MIN, natPort.toInt());
484 natTreatment.setPropertyValue(CT_PORT_MAX, natPort.toInt());
485 }
486
487 } catch (Exception e) {
488 log.error("Failed to set NAT due to error : {}", e);
489 return null;
490 }
491
492 ExtensionTreatment ctTreatment = etr.getExtensionInstruction(
493 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_CT.type());
494 try {
495 List<ExtensionTreatment> nat = new ArrayList<>();
496 if (natAction) {
497 nat.add(natTreatment);
498 }
499 ctTreatment.setPropertyValue(CT_FLAGS, commit ? 1 : 0);
500 ctTreatment.setPropertyValue(CT_ZONE, zone);
501 ctTreatment.setPropertyValue(CT_TABLE, table > -1 ? table : 0xff);
502 ctTreatment.setPropertyValue(CT_NESTED_ACTIONS, nat);
503 } catch (Exception e) {
504 log.error("Failed to set CT due to error : {}", e);
505 return null;
506 }
507
508 return ctTreatment;
509 }
510
511 private int buildPresentFlag(boolean isPortPresent, boolean isAddressPresent) {
512
513 int presentFlag = 0;
514
515 if (isPortPresent) {
516 presentFlag = 1 << PORT_MIN_FLAG | 1 << PORT_MAX_FLAG;
517 }
518
519 if (isAddressPresent) {
520 // TODO: need to support IPv6 address
521 presentFlag = presentFlag | 1 << ADDRESS_V4_MIN_FLAG | 1 << ADDRESS_V4_MAX_FLAG;
522 }
523
524 return presentFlag;
525 }
526 }
Jian Li4aa17642019-01-30 00:01:11 +0900527}