3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.containermanager;
12 import java.io.Serializable;
13 import java.net.Inet4Address;
14 import java.net.Inet6Address;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.List;
20 import javax.xml.bind.annotation.XmlAccessType;
21 import javax.xml.bind.annotation.XmlAccessorType;
22 import javax.xml.bind.annotation.XmlElement;
23 import javax.xml.bind.annotation.XmlRootElement;
25 import org.opendaylight.controller.sal.match.Match;
26 import org.opendaylight.controller.sal.match.MatchType;
27 import org.opendaylight.controller.sal.packet.BitBufferHelper;
28 import org.opendaylight.controller.sal.utils.IPProtocols;
29 import org.opendaylight.controller.sal.utils.NetUtils;
30 import org.opendaylight.controller.sal.utils.Status;
31 import org.opendaylight.controller.sal.utils.StatusCode;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * Flow Specification Java Object for Container Manager
37 * Represents a container flow configuration information for Container Manager.
39 * Objects of this class are serialized to and de-serialized from binary files through
40 * java serialization API when saving to/reading from Container manager startup
43 @XmlRootElement (name = "flow-spec-config")
44 @XmlAccessorType(XmlAccessType.NONE)
45 public class ContainerFlowConfig implements Serializable {
46 private static Logger log = LoggerFactory.getLogger(ContainerFlowConfig.class);
48 /** The Constant serialVersionUID. */
49 private static final long serialVersionUID = 1L;
51 /** The Constant regexName. */
52 private static final String regexName = "^[\\w-+.@]+$";
54 /** Flow Spec name. */
58 /** The network Source. */
62 /** The network Destination */
68 private String protocol;
70 /** The transport source */
74 /** The transport destination */
78 /* unidirectional flag
79 do not include this flag in equality check
81 private static boolean unidirectional = false;
85 * Instantiates a new container flow config.
87 public ContainerFlowConfig() {
91 * Instantiates a new container flow config.
93 * @param name Flow Spec configuration name
94 * @param container Container Name
95 * @param srcIP Source IP Address
96 * @param dstIP Destination IP Address
97 * @param proto Protocol
98 * @param srcPort Source Layer4 Port
99 * @param dstPort Destination Layer4 Port
101 public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
106 this.protocol = proto;
107 this.tpSrc = srcPort;
108 this.tpDst = dstPort;
109 //this.unidirectional = false;
113 public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
114 this.name = containerFlowConfig.name;
115 this.nwSrc = containerFlowConfig.nwSrc;
116 this.nwDst = containerFlowConfig.nwDst;
117 this.protocol = containerFlowConfig.protocol;
118 this.tpSrc = containerFlowConfig.tpSrc;
119 this.tpDst = containerFlowConfig.tpDst;
120 //this.unidirectional = containerFlowConfig.unidirectional;
124 * Returns the name of this Flow Specification
126 * @return the name of the Flow Specification
128 public String getName() {
134 * Returns the Source IP Address.
136 * @return the Source IP Address
138 public String getSrcIP() {
139 return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
143 * Returns the Destination IP Address.
145 * @return the Destination IP Address
147 public String getDstIP() {
148 return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
152 * Returns the protocol.
154 * @return the protocol
156 public String getProtocol() {
161 * Returns Source Layer4 Port.
163 * @return Source Layer4 Port
165 public String getSrcPort() {
166 return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
170 * Returns Destination Layer4 Port.
172 * @return Destination Layer4 Port
174 public String getDstPort() {
175 return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
179 * @return the unidirectional flag
181 public boolean isUnidirectional() {
182 return unidirectional;
186 * @see java.lang.Object#hashCode()
189 public int hashCode() {
190 final int prime = 31;
192 result = prime * result
193 + ((protocol == null) ? 0 : protocol.hashCode());
194 result = prime * result + ((name == null) ? 0 : name.hashCode());
195 result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
196 result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
197 result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
198 result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
203 * For comparison, consider that container flow can have empty fields
206 * @see java.lang.Object#equals(java.lang.Object)
209 public boolean equals(Object obj) {
211 * Configuration will be stored in collection only if it is valid
212 * Hence we don't check here for uninitialized fields
220 if (getClass() != obj.getClass()) {
223 ContainerFlowConfig other = (ContainerFlowConfig) obj;
224 if (matchName(other) && matchSrcIP(other)
225 && matchDstIP(other) && matchProtocol(other)
226 && matchSrcPort(other) && matchDstPort(other)) {
233 * Equals by Flow Spec name.
235 * @param name flow spec name for comparison
236 * @return true, if successful
238 public boolean equalsByName(String name) {
239 return this.name.equals(name);
245 * @param that ContainerFlowConfig for comparison
246 * @return true, if any match is equal
248 public boolean equalsByMatch(ContainerFlowConfig that) {
250 // the match is equal if any of the match is equal
251 List<Match> thisMatch = this.getMatches();
252 List<Match> otherMatch = that.getMatches();
253 // both the lists cannot be null
254 for(Match m1 : thisMatch) {
255 for(Match m2 : otherMatch) {
261 // if you have reached here without a match
268 * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
270 * @param o the ContainerFlowConfig parameter
271 * @return true, if successful
273 private boolean matchName(ContainerFlowConfig flowSpec) {
274 if (name == flowSpec.name) {
277 if (name == null || flowSpec.name == null) {
280 return name.equals(flowSpec.name);
285 * Match Source IP Address.
287 * @param flowSpec Flow Specification
288 * @return true, if successful
290 private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
291 if (nwSrc == flowSpec.nwSrc) {
294 if (nwSrc == null || flowSpec.nwSrc == null) {
297 return nwSrc.equals(flowSpec.nwSrc);
301 * Match Destination IP Address.
303 * @param flowSpec Flow Specification
304 * @return true, if successful
306 private boolean matchDstIP(ContainerFlowConfig flowSpec) {
307 if (nwDst == flowSpec.nwDst) {
310 if (nwDst == null || flowSpec.nwDst == null) {
313 return this.nwDst.equals(flowSpec.nwDst);
319 * @param flowSpec Flow Specification
320 * @return true, if successful
322 private boolean matchProtocol(ContainerFlowConfig flowSpec) {
323 if (protocol == flowSpec.protocol) {
326 if (protocol == null || flowSpec.protocol == null) {
329 return this.protocol.equals(flowSpec.protocol);
333 * Match Source Layer4 Port.
335 * @param flowSpec Flow Specification
336 * @return true, if successful
338 private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
339 if (tpSrc == flowSpec.tpSrc) {
342 if (tpSrc == null || flowSpec.tpSrc == null) {
345 return tpSrc.equals(flowSpec.tpSrc);
349 * Match Destination Layer4 Port.
351 * @param flowSpec Flow Specification
352 * @return true, if successful
354 private boolean matchDstPort(ContainerFlowConfig flowSpec) {
355 if (tpDst == flowSpec.tpDst) {
358 if (tpDst == null || flowSpec.tpDst == null) {
361 return this.tpDst.equals(flowSpec.tpDst);
365 * Returns the Source IP Address mask length.
367 * @return the Source IP Address mask length
369 public Short getSrcIPMaskLen() {
372 if (nwSrc != null && !nwSrc.isEmpty()) {
373 String[] s = nwSrc.split("/");
376 maskLen = Short.valueOf(s[1]);
377 } catch (Exception e) {
378 // no mask or bad mask
381 InetAddress ip = this.getSrcIPNum();
382 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
389 * Returns the Destination IP Address mask length.
391 * @return the Destination IP Address mask length
393 public Short getDstIPMaskLen() {
395 if (nwDst != null && !nwDst.isEmpty()) {
396 String[] s = nwDst.split("/");
399 maskLen = Short.valueOf(s[1]);
400 } catch (Exception e) {
401 // no mask or bad mask
404 InetAddress ip = this.getDstIPNum();
405 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
412 * Returns the Source IP Address.
414 * @return the Source IP Address
416 public InetAddress getSrcIPNum() {
417 InetAddress ip = null;
418 if (nwSrc == null || nwSrc.isEmpty()) {
420 ip = InetAddress.getByAddress(new byte[16]);
422 } catch (UnknownHostException e) {
428 ip = InetAddress.getByName(nwSrc.split("/")[0]);
429 } catch (UnknownHostException e1) {
437 * Returns the Destination IP Address.
439 * @return the Destination IP Address
441 public InetAddress getDstIPNum() {
442 InetAddress ip = null;
443 if (nwDst == null || nwDst.isEmpty()) {
445 ip = InetAddress.getByAddress(new byte[16]);
447 } catch (UnknownHostException e) {
453 ip = InetAddress.getByName(nwDst.split("/")[0]);
454 } catch (UnknownHostException e1) {
462 * Returns Source Layer4 Port number.
464 * @return Source Layer4 Port number
466 public Short getSrcPortNum() {
467 return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
468 : Short.valueOf(tpSrc);
472 * Returns Destination Layer4 Port number.
474 * @return Destination Layer4 Port number
476 public Short getDstPortNum() {
477 return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
478 : Short.valueOf(tpDst);
482 * Get the IP protocol value
484 * @return the protocol
486 public Short getProtoNum() {
487 return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
491 * Returns whether this container flow overlap with the passed one This is
492 * true when any two of the resulting matches for the two container flow
493 * configurations intersect.
496 * the other container flow config with which checking the
498 * @return true if the two configurations overlap, false otherwise
500 public boolean overlap(ContainerFlowConfig other) {
504 List<Match> myMathes = this.getMatches();
505 List<Match> hisMatches = other.getMatches();
506 for (Match mine : myMathes) {
507 for (Match his : hisMatches) {
508 if (mine.intersetcs(his)) {
517 * Checks if this flow specification configuration is valid.
519 * @return true, if is valid
521 public Status validate() {
522 if (!hasValidName()) {
523 return new Status(StatusCode.BADREQUEST, "Invalid name");
525 Status status = validateIPs();
526 if (!status.isSuccess()) {
529 if(!hasValidProtocol()) {
530 return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
532 if (!hasValidPorts()) {
533 return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
535 if (this.getMatches().get(0).getMatches() == 0) {
536 return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
538 return new Status(StatusCode.SUCCESS);
542 * Checks if this flow specification configuration has a valid name.
544 * @return true, if successful
546 private boolean hasValidName() {
547 return (name != null && !name.isEmpty() && name.matches(regexName));
551 * Validates the network addresses, checks syntax and semantic
553 * @return the result of the check as Status object, if successful
555 private Status validateIPs() {
557 if (!NetUtils.isIPAddressValid(nwSrc)) {
558 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
560 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
561 long prefix = BitBufferHelper.getLong(bytePrefix);
563 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
567 if (!NetUtils.isIPAddressValid(nwDst)) {
568 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
570 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
571 long prefix = BitBufferHelper.getLong(bytePrefix);
573 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
576 return new Status(StatusCode.SUCCESS);
580 * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
581 * or a valid IP proto value between 0 and 255, see:
582 * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
585 * @return true if a valid protocol value
587 private boolean hasValidProtocol() {
588 IPProtocols p = IPProtocols.fromString(protocol);
595 * String representing the transport protocol port number
596 * @return true if tpPort contains a decimal value between 0 and 65535
598 private boolean hasValidPort(String tpPort) {
600 int port = Integer.decode(tpPort);
601 return ((port >= 0) && (port <= 0xffff));
602 } catch (NumberFormatException e) {
608 * Validate the transport protocol source and destination ports as
611 * @return true if ports are defined and are in valid range
613 private boolean hasValidPorts() {
614 if (tpSrc !=null && !tpSrc.isEmpty()) {
615 if (!hasValidPort(tpSrc)) {
620 if (tpDst !=null && !tpDst.isEmpty()) {
621 return hasValidPort(tpDst);
627 * Returns the matches.
628 * If unidirectional flag is set, there will be only one match in the list
629 * If unidirectional flag is unset there will be two matches in the list,
630 * only if the specified flow has an intrinsic direction.
631 * For Ex. if the cFlow only has the protocol field configured, no matter
632 * if unidirectional flag is set or not, only one match will be returned
633 * The client just has to iterate over the returned list
634 * @return the matches
636 public List<Match> getMatches() {
637 List<Match> matches = new ArrayList<Match>();
638 Match match = new Match();
640 if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
641 String parts[] = this.nwSrc.split("/");
642 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
643 InetAddress mask = null;
645 if (parts.length > 1) {
646 maskLen = Integer.parseInt(parts[1]);
648 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
650 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
651 match.setField(MatchType.NW_SRC, ip, mask);
653 if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
654 String parts[] = this.nwDst.split("/");
655 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
656 InetAddress mask = null;
658 if (parts.length > 1) {
659 maskLen = Integer.parseInt(parts[1]);
661 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
663 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
664 match.setField(MatchType.NW_DST, ip, mask);
666 if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
667 match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
669 if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
670 match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
672 if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
673 match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
677 if(!ContainerFlowConfig.unidirectional) {
678 Match reverse = match.reverse();
679 if (!match.equals(reverse)) {
680 matches.add(reverse);
689 * @see java.lang.Object#toString()
692 public String toString() {
693 return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
694 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";