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.HashSet;
19 import java.util.List;
22 import javax.xml.bind.annotation.XmlAccessType;
23 import javax.xml.bind.annotation.XmlAccessorType;
24 import javax.xml.bind.annotation.XmlElement;
25 import javax.xml.bind.annotation.XmlRootElement;
27 import org.opendaylight.controller.sal.match.Match;
28 import org.opendaylight.controller.sal.match.MatchType;
29 import org.opendaylight.controller.sal.packet.BitBufferHelper;
30 import org.opendaylight.controller.sal.utils.IPProtocols;
31 import org.opendaylight.controller.sal.utils.NetUtils;
32 import org.opendaylight.controller.sal.utils.Status;
33 import org.opendaylight.controller.sal.utils.StatusCode;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * Flow Specification Java Object for Container Manager
39 * Represents a container flow configuration information for Container Manager.
41 * Objects of this class are serialized to and de-serialized from binary files through
42 * java serialization API when saving to/reading from Container manager startup
45 @XmlRootElement (name = "flow-spec-config")
46 @XmlAccessorType(XmlAccessType.NONE)
47 public class ContainerFlowConfig implements Serializable {
48 private static Logger log = LoggerFactory.getLogger(ContainerFlowConfig.class);
50 /** The Constant serialVersionUID. */
51 private static final long serialVersionUID = 1L;
53 /** The Constant regexName. */
54 private static final String regexName = "^[\\w-+.@]+$";
56 /** Flow Spec name. */
62 private String dlVlan;
64 /** The network Source. */
68 /** The network Destination */
74 private String protocol;
76 /** The transport source */
80 /** The transport destination */
84 /* unidirectional flag
85 do not include this flag in equality check
87 private static boolean unidirectional = false;
91 * Instantiates a new container flow config.
93 public ContainerFlowConfig() {
97 * Instantiates a new container flow config.
99 * @param name Flow Spec configuration name
100 * @param container Container Name
101 * @param srcIP Source IP Address
102 * @param dstIP Destination IP Address
103 * @param proto Protocol
104 * @param srcPort Source Layer4 Port
105 * @param dstPort Destination Layer4 Port
107 public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
113 this.protocol = proto;
114 this.tpSrc = srcPort;
115 this.tpDst = dstPort;
116 //this.unidirectional = false;
119 public ContainerFlowConfig(String name, String dlVlan, String srcIP, String dstIP, String proto, String srcPort,
122 this.dlVlan = dlVlan;
125 this.protocol = proto;
126 this.tpSrc = srcPort;
127 this.tpDst = dstPort;
131 public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
132 this.name = containerFlowConfig.name;
133 this.dlVlan = containerFlowConfig.dlVlan;
134 this.nwSrc = containerFlowConfig.nwSrc;
135 this.nwDst = containerFlowConfig.nwDst;
136 this.protocol = containerFlowConfig.protocol;
137 this.tpSrc = containerFlowConfig.tpSrc;
138 this.tpDst = containerFlowConfig.tpDst;
139 //this.unidirectional = containerFlowConfig.unidirectional;
143 * Returns the name of this Flow Specification
145 * @return the name of the Flow Specification
147 public String getName() {
153 * Returns the vlan id.
155 * @return the Vlan Id
157 public String getVlan() {
158 return (dlVlan == null || dlVlan.isEmpty()) ? null : dlVlan;
162 * Returns the Source IP Address.
164 * @return the Source IP Address
166 public String getSrcIP() {
167 return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
171 * Returns the Destination IP Address.
173 * @return the Destination IP Address
175 public String getDstIP() {
176 return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
180 * Returns the protocol.
182 * @return the protocol
184 public String getProtocol() {
189 * Returns Source Layer4 Port.
191 * @return Source Layer4 Port
193 public String getSrcPort() {
194 return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
198 * Returns Destination Layer4 Port.
200 * @return Destination Layer4 Port
202 public String getDstPort() {
203 return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
207 * @return the unidirectional flag
209 public boolean isUnidirectional() {
210 return unidirectional;
214 * @see java.lang.Object#hashCode()
217 public int hashCode() {
218 final int prime = 31;
220 result = prime * result
221 + ((protocol == null) ? 0 : protocol.hashCode());
222 result = prime * result + ((name == null) ? 0 : name.hashCode());
223 result = prime * result + ((dlVlan == null) ? 0 : dlVlan.hashCode());
224 result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
225 result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
226 result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
227 result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
232 * For comparison, consider that container flow can have empty fields
235 * @see java.lang.Object#equals(java.lang.Object)
238 public boolean equals(Object obj) {
240 * Configuration will be stored in collection only if it is valid
241 * Hence we don't check here for uninitialized fields
249 if (getClass() != obj.getClass()) {
252 ContainerFlowConfig other = (ContainerFlowConfig) obj;
253 if (matchName(other) && matchDlVlan(other) && matchSrcIP(other)
254 && matchDstIP(other) && matchProtocol(other)
255 && matchSrcPort(other) && matchDstPort(other)) {
262 * Equals by Flow Spec name.
264 * @param name flow spec name for comparison
265 * @return true, if successful
267 public boolean equalsByName(String name) {
268 return this.name.equals(name);
274 * @param that ContainerFlowConfig for comparison
275 * @return true, if any match is equal
277 public boolean equalsByMatch(ContainerFlowConfig that) {
279 // the match is equal if any of the match is equal
280 List<Match> thisMatch = this.getMatches();
281 List<Match> otherMatch = that.getMatches();
282 // both the lists cannot be null
283 for(Match m1 : thisMatch) {
284 for(Match m2 : otherMatch) {
290 // if you have reached here without a match
297 * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
299 * @param o the ContainerFlowConfig parameter
300 * @return true, if successful
302 private boolean matchName(ContainerFlowConfig flowSpec) {
303 if (name == flowSpec.name) {
306 if (name == null || flowSpec.name == null) {
309 return name.equals(flowSpec.name);
313 * Match the set of these vlans with that of flowSpec's vlans.
317 * @return true, if successful
319 private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
320 if (dlVlan == flowSpec.dlVlan) {
323 if (dlVlan == null || flowSpec.dlVlan == null) {
327 return this.getVlanList().equals(flowSpec.getVlanList());
331 * Match Source IP Address.
333 * @param flowSpec Flow Specification
334 * @return true, if successful
336 private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
337 if (nwSrc == flowSpec.nwSrc) {
340 if (nwSrc == null || flowSpec.nwSrc == null) {
343 return nwSrc.equals(flowSpec.nwSrc);
347 * Match Destination IP Address.
349 * @param flowSpec Flow Specification
350 * @return true, if successful
352 private boolean matchDstIP(ContainerFlowConfig flowSpec) {
353 if (nwDst == flowSpec.nwDst) {
356 if (nwDst == null || flowSpec.nwDst == null) {
359 return this.nwDst.equals(flowSpec.nwDst);
365 * @param flowSpec Flow Specification
366 * @return true, if successful
368 private boolean matchProtocol(ContainerFlowConfig flowSpec) {
369 if (protocol == flowSpec.protocol) {
372 if (protocol == null || flowSpec.protocol == null) {
375 return this.protocol.equals(flowSpec.protocol);
379 * Match Source Layer4 Port.
381 * @param flowSpec Flow Specification
382 * @return true, if successful
384 private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
385 if (tpSrc == flowSpec.tpSrc) {
388 if (tpSrc == null || flowSpec.tpSrc == null) {
391 return tpSrc.equals(flowSpec.tpSrc);
395 * Match Destination Layer4 Port.
397 * @param flowSpec Flow Specification
398 * @return true, if successful
400 private boolean matchDstPort(ContainerFlowConfig flowSpec) {
401 if (tpDst == flowSpec.tpDst) {
404 if (tpDst == null || flowSpec.tpDst == null) {
407 return this.tpDst.equals(flowSpec.tpDst);
411 * Returns the vlan id number for all vlans specified
413 * @return the vlan id number for all vlans specified
415 public Set<Short> getVlanList() {
417 * example: Vlan = "1,3,5-12"
418 * elemArray = ["1" "3" "5-12"]
419 * elem[2] = "5-12" --> limits = ["5" "12"]
420 * vlanList = [1 3 5 6 7 8 9 10 11 12]
422 Set<Short> vlanList = new HashSet<Short>();
424 String[] elemArray = dlVlan.split(",");
425 for (String elem : elemArray) {
426 if (elem.contains("-")) {
427 String[] limits = elem.split("-");
428 for (short j = Short.valueOf(limits[0]); j <= Short.valueOf(limits[1]); j++) {
429 vlanList.add(Short.valueOf(j));
432 vlanList.add(Short.valueOf(elem));
435 } catch (NumberFormatException e) {
442 * Returns the Source IP Address mask length.
444 * @return the Source IP Address mask length
446 public Short getSrcIPMaskLen() {
449 if (nwSrc != null && !nwSrc.isEmpty()) {
450 String[] s = nwSrc.split("/");
453 maskLen = Short.valueOf(s[1]);
454 } catch (Exception e) {
455 // no mask or bad mask
458 InetAddress ip = this.getSrcIPNum();
459 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
466 * Returns the Destination IP Address mask length.
468 * @return the Destination IP Address mask length
470 public Short getDstIPMaskLen() {
472 if (nwDst != null && !nwDst.isEmpty()) {
473 String[] s = nwDst.split("/");
476 maskLen = Short.valueOf(s[1]);
477 } catch (Exception e) {
478 // no mask or bad mask
481 InetAddress ip = this.getDstIPNum();
482 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
489 * Returns the Source IP Address.
491 * @return the Source IP Address
493 public InetAddress getSrcIPNum() {
494 InetAddress ip = null;
495 if (nwSrc == null || nwSrc.isEmpty()) {
497 ip = InetAddress.getByAddress(new byte[16]);
499 } catch (UnknownHostException e) {
505 ip = InetAddress.getByName(nwSrc.split("/")[0]);
506 } catch (UnknownHostException e1) {
514 * Returns the Destination IP Address.
516 * @return the Destination IP Address
518 public InetAddress getDstIPNum() {
519 InetAddress ip = null;
520 if (nwDst == null || nwDst.isEmpty()) {
522 ip = InetAddress.getByAddress(new byte[16]);
524 } catch (UnknownHostException e) {
530 ip = InetAddress.getByName(nwDst.split("/")[0]);
531 } catch (UnknownHostException e1) {
539 * Returns Source Layer4 Port number.
541 * @return Source Layer4 Port number
543 public Short getSrcPortNum() {
544 return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
545 : Short.valueOf(tpSrc);
549 * Returns Destination Layer4 Port number.
551 * @return Destination Layer4 Port number
553 public Short getDstPortNum() {
554 return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
555 : Short.valueOf(tpDst);
559 * Get the IP protocol value
561 * @return the protocol
563 public Short getProtoNum() {
564 return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
568 * Returns whether this container flow overlap with the passed one This is
569 * true when any two of the resulting matches for the two container flow
570 * configurations intersect.
573 * the other container flow config with which checking the
575 * @return true if the two configurations overlap, false otherwise
577 public boolean overlap(ContainerFlowConfig other) {
581 List<Match> myMathes = this.getMatches();
582 List<Match> hisMatches = other.getMatches();
583 for (Match mine : myMathes) {
584 for (Match his : hisMatches) {
585 if (mine.intersetcs(his)) {
594 * Checks if this flow specification configuration is valid.
596 * @return true, if is valid
598 public Status validate() {
599 if (!hasValidName()) {
600 return new Status(StatusCode.BADREQUEST, "Invalid name");
602 Status status = validateVlan();
603 if (!status.isSuccess()) {
606 status = validateIPs();
607 if (!status.isSuccess()) {
610 if(!hasValidProtocol()) {
611 return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
613 if (!hasValidPorts()) {
614 return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
616 if (this.getMatches().get(0).getMatches() == 0) {
617 return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
619 return new Status(StatusCode.SUCCESS);
623 * Checks if this flow specification configuration has a valid name.
625 * @return true, if successful
627 private boolean hasValidName() {
628 return (name != null && !name.isEmpty() && name.matches(regexName));
632 * Validates the vlan number
634 * @return the result of the check as Status object
636 private Status validateVlan() {
637 if (dlVlan != null) {
640 String[] elemArray = dlVlan.split(",");
641 for (String elem : elemArray) {
642 if (elem.contains("-")) {
643 String[] limits = elem.split("-");
644 if (Short.parseShort(limits[0]) < 0
645 || Short.parseShort(limits[0]) >= Short.parseShort(limits[1])
646 || Short.parseShort(limits[1]) > 0xfff) {
647 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
650 vlanId = Short.parseShort(elem);
651 if (vlanId < 0 || vlanId > 0xfff) {
652 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
656 } catch (NumberFormatException e) {
657 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
660 return new Status(StatusCode.SUCCESS);
664 * Validates the network addresses, checks syntax and semantic
666 * @return the result of the check as Status object, if successful
668 private Status validateIPs() {
670 if (!NetUtils.isIPAddressValid(nwSrc)) {
671 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
673 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
674 long prefix = BitBufferHelper.getLong(bytePrefix);
676 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
680 if (!NetUtils.isIPAddressValid(nwDst)) {
681 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
683 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
684 long prefix = BitBufferHelper.getLong(bytePrefix);
686 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
689 return new Status(StatusCode.SUCCESS);
693 * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
694 * or a valid IP proto value between 0 and 255, see:
695 * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
698 * @return true if a valid protocol value
700 private boolean hasValidProtocol() {
701 IPProtocols p = IPProtocols.fromString(protocol);
708 * String representing the transport protocol port number
709 * @return true if tpPort contains a decimal value between 0 and 65535
711 private boolean hasValidPort(String tpPort) {
713 int port = Integer.decode(tpPort);
714 return ((port >= 0) && (port <= 0xffff));
715 } catch (NumberFormatException e) {
721 * Validate the transport protocol source and destination ports as
724 * @return true if ports are defined and are in valid range
726 private boolean hasValidPorts() {
727 if (tpSrc !=null && !tpSrc.isEmpty()) {
728 if (!hasValidPort(tpSrc)) {
733 if (tpDst !=null && !tpDst.isEmpty()) {
734 return hasValidPort(tpDst);
740 * Returns the matches.
741 * If unidirectional flag is set, there will be only one match per vlan in the list
742 * If unidirectional flag is unset there will be two matches per vlan in the list,
743 * only if the specified flow has an intrinsic direction.
744 * For Ex. if the cFlow only has the protocol field configured, no matter
745 * if unidirectional flag is set or not, only one match per vlan will be returned
746 * The client just has to iterate over the returned list
747 * @return the matches
749 public List<Match> getMatches() {
750 List<Match> matches = new ArrayList<Match>();
752 if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
753 for(Short vlan:getVlanList()){
754 Match match = getMatch(vlan);
759 Match match = getMatch(null);
763 if (!ContainerFlowConfig.unidirectional) {
764 List<Match> forwardMatches = new ArrayList<Match>(matches);
765 for (Match match : forwardMatches) {
766 Match reverse = match.reverse();
767 if (!match.equals(reverse)) {
768 matches.add(reverse);
776 private Match getMatch(Short vlan){
777 Match match = new Match();
780 match.setField(MatchType.DL_VLAN, vlan);
782 if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
783 String parts[] = this.nwSrc.split("/");
784 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
785 InetAddress mask = null;
787 if (parts.length > 1) {
788 maskLen = Integer.parseInt(parts[1]);
790 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
792 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
793 match.setField(MatchType.NW_SRC, ip, mask);
795 if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
796 String parts[] = this.nwDst.split("/");
797 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
798 InetAddress mask = null;
800 if (parts.length > 1) {
801 maskLen = Integer.parseInt(parts[1]);
803 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
805 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
806 match.setField(MatchType.NW_DST, ip, mask);
808 if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
809 match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
811 if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
812 match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
814 if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
815 match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
823 * @see java.lang.Object#toString()
826 public String toString() {
827 return "Container Flow={name:" + name + " dlVlan:" + dlVlan + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
828 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";