2520172d8959a62f5a7fb2af37826c0a27a59dd9
[controller.git] / opendaylight / containermanager / api / src / main / java / org / opendaylight / controller / containermanager / ContainerFlowConfig.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 package org.opendaylight.controller.containermanager;
11
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;
19
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;
24
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;
34
35 /**
36  * Flow Specification Java Object for Container Manager
37  * Represents a container flow configuration information for Container Manager.
38  *
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
41  * configuration file.
42  */
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);
47
48     /** The Constant serialVersionUID. */
49     private static final long serialVersionUID = 1L;
50
51     /** The Constant regexName. */
52     private static final String regexName = "^[\\w-+.@]+$";
53
54     /** Flow Spec name. */
55     @XmlElement
56     private String name;
57
58     /** The network Source. */
59     @XmlElement
60     private String nwSrc;
61
62     /** The network Destination */
63     @XmlElement
64     private String nwDst;
65
66     /** The protocol. */
67     @XmlElement
68     private String protocol;
69
70     /** The transport source */
71     @XmlElement
72     private String tpSrc;
73
74     /** The transport destination */
75     @XmlElement
76     private String tpDst;
77
78     /* unidirectional flag
79     do not include this flag in equality check
80     @XmlElement */
81     private static boolean unidirectional = false;
82
83
84     /**
85      * Instantiates a new container flow config.
86      */
87     public ContainerFlowConfig() {
88     }
89
90     /**
91      * Instantiates a new container flow config.
92      *
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
100      */
101     public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
102             String dstPort) {
103         this.name = name;
104         this.nwSrc = srcIP;
105         this.nwDst = dstIP;
106         this.protocol = proto;
107         this.tpSrc = srcPort;
108         this.tpDst = dstPort;
109         //this.unidirectional = false;
110     }
111
112
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;
121     }
122
123     /**
124      * Returns the name of this Flow Specification
125      *
126      * @return the name of the Flow Specification
127      */
128     public String getName() {
129         // mandatory field
130         return name;
131     }
132
133     /**
134      * Returns the Source IP Address.
135      *
136      * @return the Source IP Address
137      */
138     public String getSrcIP() {
139         return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
140     }
141
142     /**
143      * Returns the Destination IP Address.
144      *
145      * @return the Destination IP Address
146      */
147     public String getDstIP() {
148         return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
149     }
150
151     /**
152      * Returns the protocol.
153      *
154      * @return the protocol
155      */
156     public String getProtocol() {
157         return protocol;
158     }
159
160     /**
161      * Returns Source Layer4 Port.
162      *
163      * @return Source Layer4 Port
164      */
165     public String getSrcPort() {
166         return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
167     }
168
169     /**
170      * Returns Destination Layer4 Port.
171      *
172      * @return Destination Layer4 Port
173      */
174     public String getDstPort() {
175         return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
176     }
177
178     /*
179      * @return the unidirectional flag
180      */
181     public boolean isUnidirectional() {
182         return unidirectional;
183     }
184
185     /* (non-Javadoc)
186      * @see java.lang.Object#hashCode()
187      */
188     @Override
189     public int hashCode() {
190         final int prime = 31;
191         int result = 1;
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());
199         return result;
200     }
201
202     /*
203      * For comparison, consider that container flow can have empty fields
204      */
205     /* (non-Javadoc)
206      * @see java.lang.Object#equals(java.lang.Object)
207      */
208     @Override
209     public boolean equals(Object obj) {
210         /*
211          * Configuration will be stored in collection only if it is valid
212          * Hence we don't check here for uninitialized fields
213          */
214         if (this == obj) {
215             return true;
216         }
217         if (obj == null) {
218             return false;
219         }
220         if (getClass() != obj.getClass()) {
221             return false;
222         }
223         ContainerFlowConfig other = (ContainerFlowConfig) obj;
224         if (matchName(other) && matchSrcIP(other)
225                 && matchDstIP(other) && matchProtocol(other)
226                 && matchSrcPort(other) && matchDstPort(other)) {
227             return true;
228         }
229         return false;
230     }
231
232     /**
233      * Equals by Flow Spec name.
234      *
235      * @param name flow spec name for comparison
236      * @return true, if successful
237      */
238     public boolean equalsByName(String name) {
239         return this.name.equals(name);
240     }
241
242     /**
243      * equalsByMatch
244      *
245      * @param that ContainerFlowConfig for comparison
246      * @return true, if any match is equal
247      */
248     public boolean equalsByMatch(ContainerFlowConfig that) {
249         // get both matches
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) {
256                   if(m1.equals(m2)) {
257                     return true;
258                 }
259             }
260         }
261         // if you have reached here without a match
262         // being found
263         // return false
264         return false;
265     }
266
267     /**
268      * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
269      *
270      * @param o the ContainerFlowConfig parameter
271      * @return true, if successful
272      */
273     private boolean matchName(ContainerFlowConfig flowSpec) {
274         if (name == flowSpec.name) {
275             return true;
276         }
277         if (name == null || flowSpec.name == null) {
278             return false;
279         }
280         return name.equals(flowSpec.name);
281     }
282
283
284     /**
285      * Match Source IP Address.
286      *
287      * @param flowSpec Flow Specification
288      * @return true, if successful
289      */
290     private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
291         if (nwSrc == flowSpec.nwSrc) {
292             return true;
293         }
294         if (nwSrc == null || flowSpec.nwSrc == null) {
295             return false;
296         }
297         return nwSrc.equals(flowSpec.nwSrc);
298     }
299
300     /**
301      * Match Destination IP Address.
302      *
303      * @param flowSpec Flow Specification
304      * @return true, if successful
305      */
306     private boolean matchDstIP(ContainerFlowConfig flowSpec) {
307         if (nwDst == flowSpec.nwDst) {
308             return true;
309         }
310         if (nwDst == null || flowSpec.nwDst == null) {
311             return false;
312         }
313         return this.nwDst.equals(flowSpec.nwDst);
314     }
315
316     /**
317      * Match protocol.
318      *
319      * @param flowSpec Flow Specification
320      * @return true, if successful
321      */
322     private boolean matchProtocol(ContainerFlowConfig flowSpec) {
323         if (protocol == flowSpec.protocol) {
324             return true;
325         }
326         if (protocol == null || flowSpec.protocol == null) {
327             return false;
328         }
329         return this.protocol.equals(flowSpec.protocol);
330     }
331
332     /**
333      * Match Source Layer4 Port.
334      *
335      * @param flowSpec Flow Specification
336      * @return true, if successful
337      */
338     private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
339         if (tpSrc == flowSpec.tpSrc) {
340             return true;
341         }
342         if (tpSrc == null || flowSpec.tpSrc == null) {
343             return false;
344         }
345         return tpSrc.equals(flowSpec.tpSrc);
346     }
347
348     /**
349      * Match Destination Layer4 Port.
350      *
351      * @param flowSpec Flow Specification
352      * @return true, if successful
353      */
354     private boolean matchDstPort(ContainerFlowConfig flowSpec) {
355         if (tpDst == flowSpec.tpDst) {
356             return true;
357         }
358         if (tpDst == null || flowSpec.tpDst == null) {
359             return false;
360         }
361         return this.tpDst.equals(flowSpec.tpDst);
362     }
363
364     /**
365      * Returns the Source IP Address mask length.
366      *
367      * @return the Source IP Address mask length
368      */
369     public Short getSrcIPMaskLen() {
370         Short maskLen = 0;
371
372         if (nwSrc != null && !nwSrc.isEmpty()) {
373             String[] s = nwSrc.split("/");
374             if (s.length == 2) {
375                 try {
376                     maskLen = Short.valueOf(s[1]);
377                 } catch (Exception e) {
378                     // no mask or bad mask
379                 }
380             } else {
381                 InetAddress ip = this.getSrcIPNum();
382                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
383             }
384         }
385         return maskLen;
386     }
387
388     /**
389      * Returns the Destination IP Address mask length.
390      *
391      * @return the Destination IP Address mask length
392      */
393     public Short getDstIPMaskLen() {
394         Short maskLen = 0;
395         if (nwDst != null && !nwDst.isEmpty()) {
396             String[] s = nwDst.split("/");
397             if (s.length == 2) {
398                 try {
399                     maskLen = Short.valueOf(s[1]);
400                 } catch (Exception e) {
401                     // no mask or bad mask
402                 }
403             } else {
404                 InetAddress ip = this.getDstIPNum();
405                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
406             }
407         }
408         return maskLen;
409     }
410
411     /**
412      * Returns the Source IP Address.
413      *
414      * @return the Source IP Address
415      */
416     public InetAddress getSrcIPNum() {
417         InetAddress ip = null;
418         if (nwSrc == null || nwSrc.isEmpty()) {
419             try {
420                 ip = InetAddress.getByAddress(new byte[16]);
421                 return ip;
422             } catch (UnknownHostException e) {
423                 log.error("", e);
424                 return null;
425             }
426         }
427         try {
428             ip = InetAddress.getByName(nwSrc.split("/")[0]);
429         } catch (UnknownHostException e1) {
430             log.error("", e1);
431             return null;
432         }
433         return ip;
434     }
435
436     /**
437      * Returns the Destination IP Address.
438      *
439      * @return the Destination IP Address
440      */
441     public InetAddress getDstIPNum() {
442         InetAddress ip = null;
443         if (nwDst == null || nwDst.isEmpty()) {
444             try {
445                 ip = InetAddress.getByAddress(new byte[16]);
446                 return ip;
447             } catch (UnknownHostException e) {
448                 log.error("",e);
449                 return null;
450             }
451         }
452         try {
453             ip = InetAddress.getByName(nwDst.split("/")[0]);
454         } catch (UnknownHostException e1) {
455             log.error("", e1);
456             return null;
457         }
458         return ip;
459     }
460
461     /**
462      * Returns Source Layer4 Port number.
463      *
464      * @return Source Layer4 Port number
465      */
466     public Short getSrcPortNum() {
467         return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
468                 : Short.valueOf(tpSrc);
469     }
470
471     /**
472      * Returns Destination Layer4 Port number.
473      *
474      * @return Destination Layer4 Port number
475      */
476     public Short getDstPortNum() {
477         return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
478                 : Short.valueOf(tpDst);
479     }
480
481     /**
482      * Get the IP protocol value
483      *
484      * @return the protocol
485      */
486     public Short getProtoNum() {
487         return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
488     }
489
490     /**
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.
494      *
495      * @param other
496      *            the other container flow config with which checking the
497      *            overlap
498      * @return true if the two configurations overlap, false otherwise
499      */
500     public boolean overlap(ContainerFlowConfig other) {
501         if (other == null) {
502             return false;
503         }
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)) {
509                     return true;
510                 }
511             }
512         }
513         return false;
514     }
515
516     /**
517      * Checks if this flow specification configuration is valid.
518      *
519      * @return true, if is valid
520      */
521     public Status validate() {
522         if (!hasValidName()) {
523             return new Status(StatusCode.BADREQUEST, "Invalid name");
524         }
525         Status status = validateIPs();
526         if (!status.isSuccess()) {
527             return status;
528         }
529         if(!hasValidProtocol()) {
530             return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
531         }
532         if (!hasValidPorts()) {
533             return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
534         }
535         if (this.getMatches().get(0).getMatches() == 0) {
536             return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
537         }
538         return new Status(StatusCode.SUCCESS);
539     }
540
541     /**
542      * Checks if this flow specification configuration has a valid name.
543      *
544      * @return true, if successful
545      */
546     private boolean hasValidName() {
547         return (name != null && !name.isEmpty() && name.matches(regexName));
548     }
549
550     /**
551      * Validates the network addresses, checks syntax and semantic
552      *
553      * @return the result of the check as Status object, if successful
554      */
555     private Status validateIPs() {
556         if (nwSrc != null) {
557             if (!NetUtils.isIPAddressValid(nwSrc)) {
558                 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
559             }
560             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
561             long prefix = BitBufferHelper.getLong(bytePrefix);
562             if (prefix == 0) {
563                 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
564             }
565         }
566         if (nwDst != null) {
567             if (!NetUtils.isIPAddressValid(nwDst)) {
568                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
569             }
570             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
571             long prefix = BitBufferHelper.getLong(bytePrefix);
572             if (prefix == 0) {
573                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
574             }
575         }
576         return new Status(StatusCode.SUCCESS);
577     }
578
579     /**
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
583      * for more details.
584      *
585      * @return true if a valid protocol value
586      */
587     private boolean hasValidProtocol() {
588         IPProtocols p = IPProtocols.fromString(protocol);
589         return p != null;
590     }
591
592     /**
593      *
594      * @param tpPort
595      *               String representing the transport protocol port number
596      * @return true if tpPort contains a decimal value between 0 and 65535
597      */
598     private boolean hasValidPort(String tpPort) {
599         try {
600             int port = Integer.decode(tpPort);
601             return ((port >= 0) && (port <= 0xffff));
602         } catch (NumberFormatException e) {
603             return false;
604         }
605     }
606
607     /**
608      * Validate the transport protocol source and destination ports as
609      * entered by users.
610      *
611      * @return true if ports are defined and are in valid range
612      */
613     private boolean hasValidPorts() {
614         if (tpSrc !=null && !tpSrc.isEmpty()) {
615             if (!hasValidPort(tpSrc)) {
616                 return false;
617             }
618         }
619
620         if (tpDst !=null && !tpDst.isEmpty()) {
621             return hasValidPort(tpDst);
622         }
623         return true;
624     }
625
626     /**
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
635      */
636     public List<Match> getMatches() {
637         List<Match> matches = new ArrayList<Match>();
638         Match match = new Match();
639
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;
644             int maskLen = 0;
645             if (parts.length > 1) {
646                 maskLen = Integer.parseInt(parts[1]);
647             } else {
648                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
649             }
650             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
651             match.setField(MatchType.NW_SRC, ip, mask);
652         }
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;
657             int maskLen = 0;
658             if (parts.length > 1) {
659                 maskLen = Integer.parseInt(parts[1]);
660             } else {
661                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
662             }
663             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
664             match.setField(MatchType.NW_DST, ip, mask);
665         }
666         if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
667             match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
668         }
669         if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
670             match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
671         }
672         if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
673             match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
674         }
675
676         matches.add(match);
677         if(!ContainerFlowConfig.unidirectional) {
678             Match reverse = match.reverse();
679             if (!match.equals(reverse)) {
680                 matches.add(reverse);
681             }
682         }
683         return matches;
684     }
685
686     /*
687      * (non-Javadoc)
688      *
689      * @see java.lang.Object#toString()
690      */
691     @Override
692     public String toString() {
693         return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
694                 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";
695     }
696 }