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.configuration.ConfigurationObject;
28 import org.opendaylight.controller.sal.match.Match;
29 import org.opendaylight.controller.sal.match.MatchType;
30 import org.opendaylight.controller.sal.packet.BitBufferHelper;
31 import org.opendaylight.controller.sal.utils.IPProtocols;
32 import org.opendaylight.controller.sal.utils.NetUtils;
33 import org.opendaylight.controller.sal.utils.Status;
34 import org.opendaylight.controller.sal.utils.StatusCode;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * Flow Specification Java Object for Container Manager
40 * Represents a container flow configuration information for Container Manager.
42 * Objects of this class are serialized to and de-serialized from binary files through
43 * java serialization API when saving to/reading from Container manager startup
46 @XmlRootElement (name = "flow-spec-config")
47 @XmlAccessorType(XmlAccessType.NONE)
48 public class ContainerFlowConfig extends ConfigurationObject implements Serializable {
49 private static Logger log = LoggerFactory.getLogger(ContainerFlowConfig.class);
51 /** The Constant serialVersionUID. */
52 private static final long serialVersionUID = 1L;
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;
117 public ContainerFlowConfig(String name, String dlVlan, String srcIP, String dstIP, String proto, String srcPort,
120 this.dlVlan = dlVlan;
123 this.protocol = proto;
124 this.tpSrc = srcPort;
125 this.tpDst = dstPort;
129 public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
130 this.name = containerFlowConfig.name;
131 this.dlVlan = containerFlowConfig.dlVlan;
132 this.nwSrc = containerFlowConfig.nwSrc;
133 this.nwDst = containerFlowConfig.nwDst;
134 this.protocol = containerFlowConfig.protocol;
135 this.tpSrc = containerFlowConfig.tpSrc;
136 this.tpDst = containerFlowConfig.tpDst;
137 //this.unidirectional = containerFlowConfig.unidirectional;
141 * Returns the name of this Flow Specification
143 * @return the name of the Flow Specification
145 public String getName() {
151 * Returns the vlan id.
153 * @return the Vlan Id
155 public String getVlan() {
156 return (dlVlan == null || dlVlan.isEmpty()) ? null : dlVlan;
160 * Returns the Source IP Address.
162 * @return the Source IP Address
164 public String getSrcIP() {
165 return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
169 * Returns the Destination IP Address.
171 * @return the Destination IP Address
173 public String getDstIP() {
174 return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
178 * Returns the protocol.
180 * @return the protocol
182 public String getProtocol() {
187 * Returns Source Layer4 Port.
189 * @return Source Layer4 Port
191 public String getSrcPort() {
192 return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
196 * Returns Destination Layer4 Port.
198 * @return Destination Layer4 Port
200 public String getDstPort() {
201 return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
205 * @return the unidirectional flag
207 public boolean isUnidirectional() {
208 return unidirectional;
212 * @see java.lang.Object#hashCode()
215 public int hashCode() {
216 final int prime = 31;
218 result = prime * result
219 + ((protocol == null) ? 0 : protocol.hashCode());
220 result = prime * result + ((name == null) ? 0 : name.hashCode());
221 result = prime * result + ((dlVlan == null) ? 0 : dlVlan.hashCode());
222 result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
223 result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
224 result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
225 result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
230 * For comparison, consider that container flow can have empty fields
233 * @see java.lang.Object#equals(java.lang.Object)
236 public boolean equals(Object obj) {
238 * Configuration will be stored in collection only if it is valid
239 * Hence we don't check here for uninitialized fields
247 if (getClass() != obj.getClass()) {
250 ContainerFlowConfig other = (ContainerFlowConfig) obj;
251 if (matchName(other) && matchDlVlan(other) && matchSrcIP(other)
252 && matchDstIP(other) && matchProtocol(other)
253 && matchSrcPort(other) && matchDstPort(other)) {
260 * Equals by Flow Spec name.
262 * @param name flow spec name for comparison
263 * @return true, if successful
265 public boolean equalsByName(String name) {
266 return this.name.equals(name);
272 * @param that ContainerFlowConfig for comparison
273 * @return true, if any match is equal
275 public boolean equalsByMatch(ContainerFlowConfig that) {
277 // the match is equal if any of the match is equal
278 List<Match> thisMatch = this.getMatches();
279 List<Match> otherMatch = that.getMatches();
280 // both the lists cannot be null
281 for(Match m1 : thisMatch) {
282 for(Match m2 : otherMatch) {
288 // if you have reached here without a match
295 * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
297 * @param o the ContainerFlowConfig parameter
298 * @return true, if successful
300 private boolean matchName(ContainerFlowConfig flowSpec) {
301 if (name == flowSpec.name) {
304 if (name == null || flowSpec.name == null) {
307 return name.equals(flowSpec.name);
311 * Match the set of these vlans with that of flowSpec's vlans.
315 * @return true, if successful
317 private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
318 if (dlVlan == flowSpec.dlVlan) {
321 if (dlVlan == null || flowSpec.dlVlan == null) {
325 return this.getVlanList().equals(flowSpec.getVlanList());
329 * Match Source IP Address.
331 * @param flowSpec Flow Specification
332 * @return true, if successful
334 private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
335 if (nwSrc == flowSpec.nwSrc) {
338 if (nwSrc == null || flowSpec.nwSrc == null) {
341 return nwSrc.equals(flowSpec.nwSrc);
345 * Match Destination IP Address.
347 * @param flowSpec Flow Specification
348 * @return true, if successful
350 private boolean matchDstIP(ContainerFlowConfig flowSpec) {
351 if (nwDst == flowSpec.nwDst) {
354 if (nwDst == null || flowSpec.nwDst == null) {
357 return this.nwDst.equals(flowSpec.nwDst);
363 * @param flowSpec Flow Specification
364 * @return true, if successful
366 private boolean matchProtocol(ContainerFlowConfig flowSpec) {
367 if (protocol == flowSpec.protocol) {
370 if (protocol == null || flowSpec.protocol == null) {
373 return this.protocol.equals(flowSpec.protocol);
377 * Match Source Layer4 Port.
379 * @param flowSpec Flow Specification
380 * @return true, if successful
382 private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
383 if (tpSrc == flowSpec.tpSrc) {
386 if (tpSrc == null || flowSpec.tpSrc == null) {
389 return tpSrc.equals(flowSpec.tpSrc);
393 * Match Destination Layer4 Port.
395 * @param flowSpec Flow Specification
396 * @return true, if successful
398 private boolean matchDstPort(ContainerFlowConfig flowSpec) {
399 if (tpDst == flowSpec.tpDst) {
402 if (tpDst == null || flowSpec.tpDst == null) {
405 return this.tpDst.equals(flowSpec.tpDst);
409 * Returns the vlan id number for all vlans specified
411 * @return the vlan id number for all vlans specified
413 public Set<Short> getVlanList() {
415 * example: Vlan = "1,3,5-12"
416 * elemArray = ["1" "3" "5-12"]
417 * elem[2] = "5-12" --> limits = ["5" "12"]
418 * vlanList = [1 3 5 6 7 8 9 10 11 12]
420 Set<Short> vlanList = new HashSet<Short>();
422 String[] elemArray = dlVlan.split(",");
423 for (String elem : elemArray) {
424 if (elem.contains("-")) {
425 String[] limits = elem.split("-");
426 for (short j = Short.valueOf(limits[0]); j <= Short.valueOf(limits[1]); j++) {
427 vlanList.add(Short.valueOf(j));
430 vlanList.add(Short.valueOf(elem));
433 } catch (NumberFormatException e) {
440 * Returns the Source IP Address mask length.
442 * @return the Source IP Address mask length
444 public Short getSrcIPMaskLen() {
447 if (nwSrc != null && !nwSrc.isEmpty()) {
448 String[] s = nwSrc.split("/");
451 maskLen = Short.valueOf(s[1]);
452 } catch (Exception e) {
453 // no mask or bad mask
456 InetAddress ip = this.getSrcIPNum();
457 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
464 * Returns the Destination IP Address mask length.
466 * @return the Destination IP Address mask length
468 public Short getDstIPMaskLen() {
470 if (nwDst != null && !nwDst.isEmpty()) {
471 String[] s = nwDst.split("/");
474 maskLen = Short.valueOf(s[1]);
475 } catch (Exception e) {
476 // no mask or bad mask
479 InetAddress ip = this.getDstIPNum();
480 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
487 * Returns the Source IP Address.
489 * @return the Source IP Address
491 public InetAddress getSrcIPNum() {
492 InetAddress ip = null;
493 if (nwSrc == null || nwSrc.isEmpty()) {
495 ip = InetAddress.getByAddress(new byte[16]);
497 } catch (UnknownHostException e) {
503 ip = InetAddress.getByName(nwSrc.split("/")[0]);
504 } catch (UnknownHostException e1) {
512 * Returns the Destination IP Address.
514 * @return the Destination IP Address
516 public InetAddress getDstIPNum() {
517 InetAddress ip = null;
518 if (nwDst == null || nwDst.isEmpty()) {
520 ip = InetAddress.getByAddress(new byte[16]);
522 } catch (UnknownHostException e) {
528 ip = InetAddress.getByName(nwDst.split("/")[0]);
529 } catch (UnknownHostException e1) {
537 * Returns Source Layer4 Port number.
539 * @return Source Layer4 Port number
541 public Short getSrcPortNum() {
542 return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
543 : Short.valueOf(tpSrc);
547 * Returns Destination Layer4 Port number.
549 * @return Destination Layer4 Port number
551 public Short getDstPortNum() {
552 return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
553 : Short.valueOf(tpDst);
557 * Get the IP protocol value
559 * @return the protocol
561 public Short getProtoNum() {
562 return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
566 * Returns whether this container flow overlap with the passed one This is
567 * true when any two of the resulting matches for the two container flow
568 * configurations intersect.
571 * the other container flow config with which checking the
573 * @return true if the two configurations overlap, false otherwise
575 public boolean overlap(ContainerFlowConfig other) {
579 List<Match> myMathes = this.getMatches();
580 List<Match> hisMatches = other.getMatches();
581 for (Match mine : myMathes) {
582 for (Match his : hisMatches) {
583 if (mine.intersetcs(his)) {
592 * Checks if this flow specification configuration is valid.
594 * @return true, if is valid
596 public Status validate() {
597 if (!isValidResourceName(name)) {
598 return new Status(StatusCode.BADREQUEST, "Invalid name");
600 Status status = validateVlan();
601 if (!status.isSuccess()) {
604 status = validateIPs();
605 if (!status.isSuccess()) {
608 if(!hasValidProtocol()) {
609 return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
611 if (!hasValidPorts()) {
612 return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
614 if (this.getMatches().get(0).getMatches() == 0) {
615 return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
617 return new Status(StatusCode.SUCCESS);
621 * Validates the vlan number
623 * @return the result of the check as Status object
625 private Status validateVlan() {
626 if (dlVlan != null) {
629 String[] elemArray = dlVlan.split(",");
630 for (String elem : elemArray) {
631 if (elem.contains("-")) {
632 String[] limits = elem.split("-");
633 if (Short.parseShort(limits[0]) < 0
634 || Short.parseShort(limits[0]) >= Short.parseShort(limits[1])
635 || Short.parseShort(limits[1]) > 0xfff) {
636 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
639 vlanId = Short.parseShort(elem);
640 if (vlanId < 0 || vlanId > 0xfff) {
641 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
645 } catch (NumberFormatException e) {
646 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
649 return new Status(StatusCode.SUCCESS);
653 * Validates the network addresses, checks syntax and semantic
655 * @return the result of the check as Status object, if successful
657 private Status validateIPs() {
659 if (!NetUtils.isIPAddressValid(nwSrc)) {
660 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
662 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
663 long prefix = BitBufferHelper.getLong(bytePrefix);
665 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
669 if (!NetUtils.isIPAddressValid(nwDst)) {
670 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
672 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
673 long prefix = BitBufferHelper.getLong(bytePrefix);
675 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
678 return new Status(StatusCode.SUCCESS);
682 * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
683 * or a valid IP proto value between 0 and 255, see:
684 * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
687 * @return true if a valid protocol value
689 private boolean hasValidProtocol() {
690 IPProtocols p = IPProtocols.fromString(protocol);
697 * String representing the transport protocol port number
698 * @return true if tpPort contains a decimal value between 0 and 65535
700 private boolean hasValidPort(String tpPort) {
702 int port = Integer.decode(tpPort);
703 return ((port >= 0) && (port <= 0xffff));
704 } catch (NumberFormatException e) {
710 * Validate the transport protocol source and destination ports as
713 * @return true if ports are defined and are in valid range
715 private boolean hasValidPorts() {
716 if (tpSrc !=null && !tpSrc.isEmpty()) {
717 if (!hasValidPort(tpSrc)) {
722 if (tpDst !=null && !tpDst.isEmpty()) {
723 return hasValidPort(tpDst);
729 * Returns the matches.
730 * If unidirectional flag is set, there will be only one match per vlan in the list
731 * If unidirectional flag is unset there will be two matches per vlan in the list,
732 * only if the specified flow has an intrinsic direction.
733 * For Ex. if the cFlow only has the protocol field configured, no matter
734 * if unidirectional flag is set or not, only one match per vlan will be returned
735 * The client just has to iterate over the returned list
736 * @return the matches
738 public List<Match> getMatches() {
739 List<Match> matches = new ArrayList<Match>();
741 if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
742 for(Short vlan:getVlanList()){
743 Match match = getMatch(vlan);
748 Match match = getMatch(null);
752 if (!ContainerFlowConfig.unidirectional) {
753 List<Match> forwardMatches = new ArrayList<Match>(matches);
754 for (Match match : forwardMatches) {
755 Match reverse = match.reverse();
756 if (!match.equals(reverse)) {
757 matches.add(reverse);
765 private Match getMatch(Short vlan){
766 Match match = new Match();
769 match.setField(MatchType.DL_VLAN, vlan);
771 if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
772 String parts[] = this.nwSrc.split("/");
773 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
774 InetAddress mask = null;
776 if (parts.length > 1) {
777 maskLen = Integer.parseInt(parts[1]);
779 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
781 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
782 match.setField(MatchType.NW_SRC, ip, mask);
784 if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
785 String parts[] = this.nwDst.split("/");
786 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
787 InetAddress mask = null;
789 if (parts.length > 1) {
790 maskLen = Integer.parseInt(parts[1]);
792 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
794 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
795 match.setField(MatchType.NW_DST, ip, mask);
797 if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
798 match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
800 if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
801 match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
803 if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
804 match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
812 * @see java.lang.Object#toString()
815 public String toString() {
816 return "Container Flow={name:" + name + " dlVlan:" + dlVlan + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
817 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";