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;
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 Source IP Address.
313 * @param flowSpec Flow Specification
314 * @return true, if successful
316 private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
317 if (dlVlan == flowSpec.dlVlan) {
320 if (dlVlan == null || flowSpec.dlVlan == null) {
323 return dlVlan.equals(flowSpec.dlVlan);
327 * Match Source IP Address.
329 * @param flowSpec Flow Specification
330 * @return true, if successful
332 private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
333 if (nwSrc == flowSpec.nwSrc) {
336 if (nwSrc == null || flowSpec.nwSrc == null) {
339 return nwSrc.equals(flowSpec.nwSrc);
343 * Match Destination IP Address.
345 * @param flowSpec Flow Specification
346 * @return true, if successful
348 private boolean matchDstIP(ContainerFlowConfig flowSpec) {
349 if (nwDst == flowSpec.nwDst) {
352 if (nwDst == null || flowSpec.nwDst == null) {
355 return this.nwDst.equals(flowSpec.nwDst);
361 * @param flowSpec Flow Specification
362 * @return true, if successful
364 private boolean matchProtocol(ContainerFlowConfig flowSpec) {
365 if (protocol == flowSpec.protocol) {
368 if (protocol == null || flowSpec.protocol == null) {
371 return this.protocol.equals(flowSpec.protocol);
375 * Match Source Layer4 Port.
377 * @param flowSpec Flow Specification
378 * @return true, if successful
380 private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
381 if (tpSrc == flowSpec.tpSrc) {
384 if (tpSrc == null || flowSpec.tpSrc == null) {
387 return tpSrc.equals(flowSpec.tpSrc);
391 * Match Destination Layer4 Port.
393 * @param flowSpec Flow Specification
394 * @return true, if successful
396 private boolean matchDstPort(ContainerFlowConfig flowSpec) {
397 if (tpDst == flowSpec.tpDst) {
400 if (tpDst == null || flowSpec.tpDst == null) {
403 return this.tpDst.equals(flowSpec.tpDst);
407 * Returns the vlan id number
409 * @return the vlan id number
411 public Short getVlanId() {
414 vlan = Short.parseShort(dlVlan);
415 } catch (NumberFormatException e) {
422 * Returns the Source IP Address mask length.
424 * @return the Source IP Address mask length
426 public Short getSrcIPMaskLen() {
429 if (nwSrc != null && !nwSrc.isEmpty()) {
430 String[] s = nwSrc.split("/");
433 maskLen = Short.valueOf(s[1]);
434 } catch (Exception e) {
435 // no mask or bad mask
438 InetAddress ip = this.getSrcIPNum();
439 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
446 * Returns the Destination IP Address mask length.
448 * @return the Destination IP Address mask length
450 public Short getDstIPMaskLen() {
452 if (nwDst != null && !nwDst.isEmpty()) {
453 String[] s = nwDst.split("/");
456 maskLen = Short.valueOf(s[1]);
457 } catch (Exception e) {
458 // no mask or bad mask
461 InetAddress ip = this.getDstIPNum();
462 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
469 * Returns the Source IP Address.
471 * @return the Source IP Address
473 public InetAddress getSrcIPNum() {
474 InetAddress ip = null;
475 if (nwSrc == null || nwSrc.isEmpty()) {
477 ip = InetAddress.getByAddress(new byte[16]);
479 } catch (UnknownHostException e) {
485 ip = InetAddress.getByName(nwSrc.split("/")[0]);
486 } catch (UnknownHostException e1) {
494 * Returns the Destination IP Address.
496 * @return the Destination IP Address
498 public InetAddress getDstIPNum() {
499 InetAddress ip = null;
500 if (nwDst == null || nwDst.isEmpty()) {
502 ip = InetAddress.getByAddress(new byte[16]);
504 } catch (UnknownHostException e) {
510 ip = InetAddress.getByName(nwDst.split("/")[0]);
511 } catch (UnknownHostException e1) {
519 * Returns Source Layer4 Port number.
521 * @return Source Layer4 Port number
523 public Short getSrcPortNum() {
524 return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
525 : Short.valueOf(tpSrc);
529 * Returns Destination Layer4 Port number.
531 * @return Destination Layer4 Port number
533 public Short getDstPortNum() {
534 return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
535 : Short.valueOf(tpDst);
539 * Get the IP protocol value
541 * @return the protocol
543 public Short getProtoNum() {
544 return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
548 * Returns whether this container flow overlap with the passed one This is
549 * true when any two of the resulting matches for the two container flow
550 * configurations intersect.
553 * the other container flow config with which checking the
555 * @return true if the two configurations overlap, false otherwise
557 public boolean overlap(ContainerFlowConfig other) {
561 List<Match> myMathes = this.getMatches();
562 List<Match> hisMatches = other.getMatches();
563 for (Match mine : myMathes) {
564 for (Match his : hisMatches) {
565 if (mine.intersetcs(his)) {
574 * Checks if this flow specification configuration is valid.
576 * @return true, if is valid
578 public Status validate() {
579 if (!hasValidName()) {
580 return new Status(StatusCode.BADREQUEST, "Invalid name");
582 Status status = validateVlan();
583 if (!status.isSuccess()) {
586 status = validateIPs();
587 if (!status.isSuccess()) {
590 if(!hasValidProtocol()) {
591 return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
593 if (!hasValidPorts()) {
594 return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
596 if (this.getMatches().get(0).getMatches() == 0) {
597 return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
599 return new Status(StatusCode.SUCCESS);
603 * Checks if this flow specification configuration has a valid name.
605 * @return true, if successful
607 private boolean hasValidName() {
608 return (name != null && !name.isEmpty() && name.matches(regexName));
612 * Validates the vlan number
614 * @return the result of the check as Status object
616 private Status validateVlan() {
617 if (dlVlan != null) {
620 vlanId = Short.parseShort(dlVlan);
621 } catch (NumberFormatException e) {
622 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
624 if (vlanId < 0 || vlanId > 0xfff) {
625 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
628 return new Status(StatusCode.SUCCESS);
632 * Validates the network addresses, checks syntax and semantic
634 * @return the result of the check as Status object, if successful
636 private Status validateIPs() {
638 if (!NetUtils.isIPAddressValid(nwSrc)) {
639 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
641 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
642 long prefix = BitBufferHelper.getLong(bytePrefix);
644 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
648 if (!NetUtils.isIPAddressValid(nwDst)) {
649 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
651 byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
652 long prefix = BitBufferHelper.getLong(bytePrefix);
654 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
657 return new Status(StatusCode.SUCCESS);
661 * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
662 * or a valid IP proto value between 0 and 255, see:
663 * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
666 * @return true if a valid protocol value
668 private boolean hasValidProtocol() {
669 IPProtocols p = IPProtocols.fromString(protocol);
676 * String representing the transport protocol port number
677 * @return true if tpPort contains a decimal value between 0 and 65535
679 private boolean hasValidPort(String tpPort) {
681 int port = Integer.decode(tpPort);
682 return ((port >= 0) && (port <= 0xffff));
683 } catch (NumberFormatException e) {
689 * Validate the transport protocol source and destination ports as
692 * @return true if ports are defined and are in valid range
694 private boolean hasValidPorts() {
695 if (tpSrc !=null && !tpSrc.isEmpty()) {
696 if (!hasValidPort(tpSrc)) {
701 if (tpDst !=null && !tpDst.isEmpty()) {
702 return hasValidPort(tpDst);
708 * Returns the matches.
709 * If unidirectional flag is set, there will be only one match in the list
710 * If unidirectional flag is unset there will be two matches in the list,
711 * only if the specified flow has an intrinsic direction.
712 * For Ex. if the cFlow only has the protocol field configured, no matter
713 * if unidirectional flag is set or not, only one match will be returned
714 * The client just has to iterate over the returned list
715 * @return the matches
717 public List<Match> getMatches() {
718 List<Match> matches = new ArrayList<Match>();
719 Match match = new Match();
721 if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
722 match.setField(MatchType.DL_VLAN, this.getVlanId());
724 if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
725 String parts[] = this.nwSrc.split("/");
726 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
727 InetAddress mask = null;
729 if (parts.length > 1) {
730 maskLen = Integer.parseInt(parts[1]);
732 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
734 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
735 match.setField(MatchType.NW_SRC, ip, mask);
737 if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
738 String parts[] = this.nwDst.split("/");
739 InetAddress ip = NetUtils.parseInetAddress(parts[0]);
740 InetAddress mask = null;
742 if (parts.length > 1) {
743 maskLen = Integer.parseInt(parts[1]);
745 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
747 mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
748 match.setField(MatchType.NW_DST, ip, mask);
750 if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
751 match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
753 if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
754 match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
756 if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
757 match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
761 if(!ContainerFlowConfig.unidirectional) {
762 Match reverse = match.reverse();
763 if (!match.equals(reverse)) {
764 matches.add(reverse);
773 * @see java.lang.Object#toString()
776 public String toString() {
777 return "Container Flow={name:" + name + " dlVlan:" + dlVlan + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
778 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";