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. */
60 private String dlVlan;
62 /** The network Source. */
66 /** The network Destination */
72 private String protocol;
74 /** The transport source */
78 /** The transport destination */
82 /* unidirectional flag
83 do not include this flag in equality check
85 private static boolean unidirectional = false;
89 * Instantiates a new container flow config.
91 public ContainerFlowConfig() {
95 * Instantiates a new container flow config.
97 * @param name Flow Spec configuration name
98 * @param container Container Name
99 * @param srcIP Source IP Address
100 * @param dstIP Destination IP Address
101 * @param proto Protocol
102 * @param srcPort Source Layer4 Port
103 * @param dstPort Destination Layer4 Port
105 public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
111 this.protocol = proto;
112 this.tpSrc = srcPort;
113 this.tpDst = dstPort;
114 //this.unidirectional = false;
118 public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
119 this.name = containerFlowConfig.name;
120 this.dlVlan = containerFlowConfig.dlVlan;
121 this.nwSrc = containerFlowConfig.nwSrc;
122 this.nwDst = containerFlowConfig.nwDst;
123 this.protocol = containerFlowConfig.protocol;
124 this.tpSrc = containerFlowConfig.tpSrc;
125 this.tpDst = containerFlowConfig.tpDst;
126 //this.unidirectional = containerFlowConfig.unidirectional;
130 * Returns the name of this Flow Specification
132 * @return the name of the Flow Specification
134 public String getName() {
140 * Returns the vlan id.
142 * @return the Vlan Id
144 public String getVlan() {
145 return (dlVlan == null || dlVlan.isEmpty()) ? null : dlVlan;
149 * Returns the Source IP Address.
151 * @return the Source IP Address
153 public String getSrcIP() {
154 return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
158 * Returns the Destination IP Address.
160 * @return the Destination IP Address
162 public String getDstIP() {
163 return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
167 * Returns the protocol.
169 * @return the protocol
171 public String getProtocol() {
176 * Returns Source Layer4 Port.
178 * @return Source Layer4 Port
180 public String getSrcPort() {
181 return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
185 * Returns Destination Layer4 Port.
187 * @return Destination Layer4 Port
189 public String getDstPort() {
190 return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
194 * @return the unidirectional flag
196 public boolean isUnidirectional() {
197 return unidirectional;
201 * @see java.lang.Object#hashCode()
204 public int hashCode() {
205 final int prime = 31;
207 result = prime * result
208 + ((protocol == null) ? 0 : protocol.hashCode());
209 result = prime * result + ((name == null) ? 0 : name.hashCode());
210 result = prime * result + ((dlVlan == null) ? 0 : dlVlan.hashCode());
211 result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
212 result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
213 result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
214 result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
219 * For comparison, consider that container flow can have empty fields
222 * @see java.lang.Object#equals(java.lang.Object)
225 public boolean equals(Object obj) {
227 * Configuration will be stored in collection only if it is valid
228 * Hence we don't check here for uninitialized fields
236 if (getClass() != obj.getClass()) {
239 ContainerFlowConfig other = (ContainerFlowConfig) obj;
240 if (matchName(other) && matchDlVlan(other) && matchSrcIP(other)
241 && matchDstIP(other) && matchProtocol(other)
242 && matchSrcPort(other) && matchDstPort(other)) {
249 * Equals by Flow Spec name.
251 * @param name flow spec name for comparison
252 * @return true, if successful
254 public boolean equalsByName(String name) {
255 return this.name.equals(name);
261 * @param that ContainerFlowConfig for comparison
262 * @return true, if any match is equal
264 public boolean equalsByMatch(ContainerFlowConfig that) {
266 // the match is equal if any of the match is equal
267 List<Match> thisMatch = this.getMatches();
268 List<Match> otherMatch = that.getMatches();
269 // both the lists cannot be null
270 for(Match m1 : thisMatch) {
271 for(Match m2 : otherMatch) {
277 // if you have reached here without a match
284 * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
286 * @param o the ContainerFlowConfig parameter
287 * @return true, if successful
289 private boolean matchName(ContainerFlowConfig flowSpec) {
290 if (name == flowSpec.name) {
293 if (name == null || flowSpec.name == null) {
296 return name.equals(flowSpec.name);
300 * Match Source IP Address.
302 * @param flowSpec Flow Specification
303 * @return true, if successful
305 private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
306 if (dlVlan == flowSpec.dlVlan) {
309 if (dlVlan == null || flowSpec.dlVlan == null) {
312 return dlVlan.equals(flowSpec.dlVlan);
316 * Match Source IP Address.
318 * @param flowSpec Flow Specification
319 * @return true, if successful
321 private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
322 if (nwSrc == flowSpec.nwSrc) {
325 if (nwSrc == null || flowSpec.nwSrc == null) {
328 return nwSrc.equals(flowSpec.nwSrc);
332 * Match Destination IP Address.
334 * @param flowSpec Flow Specification
335 * @return true, if successful
337 private boolean matchDstIP(ContainerFlowConfig flowSpec) {
338 if (nwDst == flowSpec.nwDst) {
341 if (nwDst == null || flowSpec.nwDst == null) {
344 return this.nwDst.equals(flowSpec.nwDst);
350 * @param flowSpec Flow Specification
351 * @return true, if successful
353 private boolean matchProtocol(ContainerFlowConfig flowSpec) {
354 if (protocol == flowSpec.protocol) {
357 if (protocol == null || flowSpec.protocol == null) {
360 return this.protocol.equals(flowSpec.protocol);
364 * Match Source Layer4 Port.
366 * @param flowSpec Flow Specification
367 * @return true, if successful
369 private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
370 if (tpSrc == flowSpec.tpSrc) {
373 if (tpSrc == null || flowSpec.tpSrc == null) {
376 return tpSrc.equals(flowSpec.tpSrc);
380 * Match Destination Layer4 Port.
382 * @param flowSpec Flow Specification
383 * @return true, if successful
385 private boolean matchDstPort(ContainerFlowConfig flowSpec) {
386 if (tpDst == flowSpec.tpDst) {
389 if (tpDst == null || flowSpec.tpDst == null) {
392 return this.tpDst.equals(flowSpec.tpDst);
396 * Returns the vlan id number
398 * @return the vlan id number
400 public Short getVlanId() {
403 vlan = Short.parseShort(dlVlan);
404 } catch (NumberFormatException e) {
411 * Returns the Source IP Address mask length.
413 * @return the Source IP Address mask length
415 public Short getSrcIPMaskLen() {
418 if (nwSrc != null && !nwSrc.isEmpty()) {
419 String[] s = nwSrc.split("/");
422 maskLen = Short.valueOf(s[1]);
423 } catch (Exception e) {
424 // no mask or bad mask
427 InetAddress ip = this.getSrcIPNum();
428 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
435 * Returns the Destination IP Address mask length.
437 * @return the Destination IP Address mask length
439 public Short getDstIPMaskLen() {
441 if (nwDst != null && !nwDst.isEmpty()) {
442 String[] s = nwDst.split("/");
445 maskLen = Short.valueOf(s[1]);
446 } catch (Exception e) {
447 // no mask or bad mask
450 InetAddress ip = this.getDstIPNum();
451 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
458 * Returns the Source IP Address.
460 * @return the Source IP Address
462 public InetAddress getSrcIPNum() {
463 InetAddress ip = null;
464 if (nwSrc == null || nwSrc.isEmpty()) {
466 ip = InetAddress.getByAddress(new byte[16]);
468 } catch (UnknownHostException e) {
474 ip = InetAddress.getByName(nwSrc.split("/")[0]);
475 } catch (UnknownHostException e1) {
483 * Returns the Destination IP Address.
485 * @return the Destination IP Address
487 public InetAddress getDstIPNum() {
488 InetAddress ip = null;
489 if (nwDst == null || nwDst.isEmpty()) {
491 ip = InetAddress.getByAddress(new byte[16]);
493 } catch (UnknownHostException e) {
499 ip = InetAddress.getByName(nwDst.split("/")[0]);
500 } catch (UnknownHostException e1) {
508 * Returns Source Layer4 Port number.
510 * @return Source Layer4 Port number
512 public Short getSrcPortNum() {
513 return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
514 : Short.valueOf(tpSrc);
518 * Returns Destination Layer4 Port number.
520 * @return Destination Layer4 Port number
522 public Short getDstPortNum() {
523 return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
524 : Short.valueOf(tpDst);
528 * Get the IP protocol value
530 * @return the protocol
532 public Short getProtoNum() {
533 return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
537 * Returns whether this container flow overlap with the passed one This is
538 * true when any two of the resulting matches for the two container flow
539 * configurations intersect.
542 * the other container flow config with which checking the
544 * @return true if the two configurations overlap, false otherwise
546 public boolean overlap(ContainerFlowConfig other) {
550 List<Match> myMathes = this.getMatches();
551 List<Match> hisMatches = other.getMatches();
552 for (Match mine : myMathes) {
553 for (Match his : hisMatches) {
554 if (mine.intersetcs(his)) {
563 * Checks if this flow specification configuration is valid.
565 * @return true, if is valid
567 public Status validate() {
568 if (!hasValidName()) {
569 return new Status(StatusCode.BADREQUEST, "Invalid name");
571 Status status = validateVlan();
572 if (!status.isSuccess()) {
575 status = validateIPs();
576 if (!status.isSuccess()) {
579 if(!hasValidProtocol()) {
580 return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
582 if (!hasValidPorts()) {
583 return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
585 if (this.getMatches().get(0).getMatches() == 0) {
586 return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
588 return new Status(StatusCode.SUCCESS);
592 * Checks if this flow specification configuration has a valid name.
594 * @return true, if successful
596 private boolean hasValidName() {
597 return (name != null && !name.isEmpty() && name.matches(regexName));
601 * Validates the vlan number
603 * @return the result of the check as Status object
605 private Status validateVlan() {
606 if (dlVlan != null) {
609 vlanId = Short.parseShort(dlVlan);
610 } catch (NumberFormatException e) {
611 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
613 if (vlanId < 0 || vlanId > 0xfff) {
614 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
617 return new Status(StatusCode.SUCCESS);
621 * Validates the network addresses, checks syntax and semantic
623 * @return the result of the check as Status object, if successful
625 private Status validateIPs() {
627 if (!NetUtils.isIPAddressValid(nwSrc)) {
628 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
630 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
631 long prefix = BitBufferHelper.getLong(bytePrefix);
633 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
637 if (!NetUtils.isIPAddressValid(nwDst)) {
638 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
640 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
641 long prefix = BitBufferHelper.getLong(bytePrefix);
643 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
646 return new Status(StatusCode.SUCCESS);
650 * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
651 * or a valid IP proto value between 0 and 255, see:
652 * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
655 * @return true if a valid protocol value
657 private boolean hasValidProtocol() {
658 IPProtocols p = IPProtocols.fromString(protocol);
665 * String representing the transport protocol port number
666 * @return true if tpPort contains a decimal value between 0 and 65535
668 private boolean hasValidPort(String tpPort) {
670 int port = Integer.decode(tpPort);
671 return ((port >= 0) && (port <= 0xffff));
672 } catch (NumberFormatException e) {
678 * Validate the transport protocol source and destination ports as
681 * @return true if ports are defined and are in valid range
683 private boolean hasValidPorts() {
684 if (tpSrc !=null && !tpSrc.isEmpty()) {
685 if (!hasValidPort(tpSrc)) {
690 if (tpDst !=null && !tpDst.isEmpty()) {
691 return hasValidPort(tpDst);
697 * Returns the matches.
698 * If unidirectional flag is set, there will be only one match in the list
699 * If unidirectional flag is unset there will be two matches in the list,
700 * only if the specified flow has an intrinsic direction.
701 * For Ex. if the cFlow only has the protocol field configured, no matter
702 * if unidirectional flag is set or not, only one match will be returned
703 * The client just has to iterate over the returned list
704 * @return the matches
706 public List<Match> getMatches() {
707 List<Match> matches = new ArrayList<Match>();
708 Match match = new Match();
710 if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
711 String parts[] = this.nwSrc.split("/");
712 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
713 InetAddress mask = null;
715 if (parts.length > 1) {
716 maskLen = Integer.parseInt(parts[1]);
718 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
720 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
721 match.setField(MatchType.NW_SRC, ip, mask);
723 if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
724 String parts[] = this.nwDst.split("/");
725 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
726 InetAddress mask = null;
728 if (parts.length > 1) {
729 maskLen = Integer.parseInt(parts[1]);
731 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
733 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
734 match.setField(MatchType.NW_DST, ip, mask);
736 if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
737 match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
739 if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
740 match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
742 if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
743 match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
747 if(!ContainerFlowConfig.unidirectional) {
748 Match reverse = match.reverse();
749 if (!match.equals(reverse)) {
750 matches.add(reverse);
759 * @see java.lang.Object#toString()
762 public String toString() {
763 return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
764 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";