Add vlan to container flow (container flow-spec)
[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 vlan. */
59     @XmlElement
60     private String dlVlan;
61
62     /** The network Source. */
63     @XmlElement
64     private String nwSrc;
65
66     /** The network Destination */
67     @XmlElement
68     private String nwDst;
69
70     /** The protocol. */
71     @XmlElement
72     private String protocol;
73
74     /** The transport source */
75     @XmlElement
76     private String tpSrc;
77
78     /** The transport destination */
79     @XmlElement
80     private String tpDst;
81
82     /* unidirectional flag
83     do not include this flag in equality check
84     @XmlElement */
85     private static boolean unidirectional = false;
86
87
88     /**
89      * Instantiates a new container flow config.
90      */
91     public ContainerFlowConfig() {
92     }
93
94     /**
95      * Instantiates a new container flow config.
96      *
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
104      */
105     public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
106             String dstPort) {
107         this.name = name;
108         this.dlVlan = null;
109         this.nwSrc = srcIP;
110         this.nwDst = dstIP;
111         this.protocol = proto;
112         this.tpSrc = srcPort;
113         this.tpDst = dstPort;
114         //this.unidirectional = false;
115     }
116
117
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;
127     }
128
129     /**
130      * Returns the name of this Flow Specification
131      *
132      * @return the name of the Flow Specification
133      */
134     public String getName() {
135         // mandatory field
136         return name;
137     }
138
139     /**
140      * Returns the vlan id.
141      *
142      * @return the Vlan Id
143      */
144     public String getVlan() {
145         return (dlVlan == null || dlVlan.isEmpty()) ? null : dlVlan;
146     }
147
148     /**
149      * Returns the Source IP Address.
150      *
151      * @return the Source IP Address
152      */
153     public String getSrcIP() {
154         return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
155     }
156
157     /**
158      * Returns the Destination IP Address.
159      *
160      * @return the Destination IP Address
161      */
162     public String getDstIP() {
163         return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
164     }
165
166     /**
167      * Returns the protocol.
168      *
169      * @return the protocol
170      */
171     public String getProtocol() {
172         return protocol;
173     }
174
175     /**
176      * Returns Source Layer4 Port.
177      *
178      * @return Source Layer4 Port
179      */
180     public String getSrcPort() {
181         return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
182     }
183
184     /**
185      * Returns Destination Layer4 Port.
186      *
187      * @return Destination Layer4 Port
188      */
189     public String getDstPort() {
190         return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
191     }
192
193     /*
194      * @return the unidirectional flag
195      */
196     public boolean isUnidirectional() {
197         return unidirectional;
198     }
199
200     /* (non-Javadoc)
201      * @see java.lang.Object#hashCode()
202      */
203     @Override
204     public int hashCode() {
205         final int prime = 31;
206         int result = 1;
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());
215         return result;
216     }
217
218     /*
219      * For comparison, consider that container flow can have empty fields
220      */
221     /* (non-Javadoc)
222      * @see java.lang.Object#equals(java.lang.Object)
223      */
224     @Override
225     public boolean equals(Object obj) {
226         /*
227          * Configuration will be stored in collection only if it is valid
228          * Hence we don't check here for uninitialized fields
229          */
230         if (this == obj) {
231             return true;
232         }
233         if (obj == null) {
234             return false;
235         }
236         if (getClass() != obj.getClass()) {
237             return false;
238         }
239         ContainerFlowConfig other = (ContainerFlowConfig) obj;
240         if (matchName(other) && matchDlVlan(other) && matchSrcIP(other)
241                 && matchDstIP(other) && matchProtocol(other)
242                 && matchSrcPort(other) && matchDstPort(other)) {
243             return true;
244         }
245         return false;
246     }
247
248     /**
249      * Equals by Flow Spec name.
250      *
251      * @param name flow spec name for comparison
252      * @return true, if successful
253      */
254     public boolean equalsByName(String name) {
255         return this.name.equals(name);
256     }
257
258     /**
259      * equalsByMatch
260      *
261      * @param that ContainerFlowConfig for comparison
262      * @return true, if any match is equal
263      */
264     public boolean equalsByMatch(ContainerFlowConfig that) {
265         // get both matches
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) {
272                   if(m1.equals(m2)) {
273                     return true;
274                 }
275             }
276         }
277         // if you have reached here without a match
278         // being found
279         // return false
280         return false;
281     }
282
283     /**
284      * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
285      *
286      * @param o the ContainerFlowConfig parameter
287      * @return true, if successful
288      */
289     private boolean matchName(ContainerFlowConfig flowSpec) {
290         if (name == flowSpec.name) {
291             return true;
292         }
293         if (name == null || flowSpec.name == null) {
294             return false;
295         }
296         return name.equals(flowSpec.name);
297     }
298
299     /**
300      * Match Source IP Address.
301      *
302      * @param flowSpec Flow Specification
303      * @return true, if successful
304      */
305     private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
306         if (dlVlan == flowSpec.dlVlan) {
307             return true;
308         }
309         if (dlVlan == null || flowSpec.dlVlan == null) {
310             return false;
311         }
312         return dlVlan.equals(flowSpec.dlVlan);
313     }
314
315     /**
316      * Match Source IP Address.
317      *
318      * @param flowSpec Flow Specification
319      * @return true, if successful
320      */
321     private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
322         if (nwSrc == flowSpec.nwSrc) {
323             return true;
324         }
325         if (nwSrc == null || flowSpec.nwSrc == null) {
326             return false;
327         }
328         return nwSrc.equals(flowSpec.nwSrc);
329     }
330
331     /**
332      * Match Destination IP Address.
333      *
334      * @param flowSpec Flow Specification
335      * @return true, if successful
336      */
337     private boolean matchDstIP(ContainerFlowConfig flowSpec) {
338         if (nwDst == flowSpec.nwDst) {
339             return true;
340         }
341         if (nwDst == null || flowSpec.nwDst == null) {
342             return false;
343         }
344         return this.nwDst.equals(flowSpec.nwDst);
345     }
346
347     /**
348      * Match protocol.
349      *
350      * @param flowSpec Flow Specification
351      * @return true, if successful
352      */
353     private boolean matchProtocol(ContainerFlowConfig flowSpec) {
354         if (protocol == flowSpec.protocol) {
355             return true;
356         }
357         if (protocol == null || flowSpec.protocol == null) {
358             return false;
359         }
360         return this.protocol.equals(flowSpec.protocol);
361     }
362
363     /**
364      * Match Source Layer4 Port.
365      *
366      * @param flowSpec Flow Specification
367      * @return true, if successful
368      */
369     private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
370         if (tpSrc == flowSpec.tpSrc) {
371             return true;
372         }
373         if (tpSrc == null || flowSpec.tpSrc == null) {
374             return false;
375         }
376         return tpSrc.equals(flowSpec.tpSrc);
377     }
378
379     /**
380      * Match Destination Layer4 Port.
381      *
382      * @param flowSpec Flow Specification
383      * @return true, if successful
384      */
385     private boolean matchDstPort(ContainerFlowConfig flowSpec) {
386         if (tpDst == flowSpec.tpDst) {
387             return true;
388         }
389         if (tpDst == null || flowSpec.tpDst == null) {
390             return false;
391         }
392         return this.tpDst.equals(flowSpec.tpDst);
393     }
394
395     /**
396      * Returns the vlan id number
397      *
398      * @return the vlan id number
399      */
400     public Short getVlanId() {
401         Short vlan = 0;
402         try {
403             vlan = Short.parseShort(dlVlan);
404         } catch (NumberFormatException e) {
405
406         }
407         return vlan;
408     }
409
410     /**
411      * Returns the Source IP Address mask length.
412      *
413      * @return the Source IP Address mask length
414      */
415     public Short getSrcIPMaskLen() {
416         Short maskLen = 0;
417
418         if (nwSrc != null && !nwSrc.isEmpty()) {
419             String[] s = nwSrc.split("/");
420             if (s.length == 2) {
421                 try {
422                     maskLen = Short.valueOf(s[1]);
423                 } catch (Exception e) {
424                     // no mask or bad mask
425                 }
426             } else {
427                 InetAddress ip = this.getSrcIPNum();
428                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
429             }
430         }
431         return maskLen;
432     }
433
434     /**
435      * Returns the Destination IP Address mask length.
436      *
437      * @return the Destination IP Address mask length
438      */
439     public Short getDstIPMaskLen() {
440         Short maskLen = 0;
441         if (nwDst != null && !nwDst.isEmpty()) {
442             String[] s = nwDst.split("/");
443             if (s.length == 2) {
444                 try {
445                     maskLen = Short.valueOf(s[1]);
446                 } catch (Exception e) {
447                     // no mask or bad mask
448                 }
449             } else {
450                 InetAddress ip = this.getDstIPNum();
451                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
452             }
453         }
454         return maskLen;
455     }
456
457     /**
458      * Returns the Source IP Address.
459      *
460      * @return the Source IP Address
461      */
462     public InetAddress getSrcIPNum() {
463         InetAddress ip = null;
464         if (nwSrc == null || nwSrc.isEmpty()) {
465             try {
466                 ip = InetAddress.getByAddress(new byte[16]);
467                 return ip;
468             } catch (UnknownHostException e) {
469                 log.error("", e);
470                 return null;
471             }
472         }
473         try {
474             ip = InetAddress.getByName(nwSrc.split("/")[0]);
475         } catch (UnknownHostException e1) {
476             log.error("", e1);
477             return null;
478         }
479         return ip;
480     }
481
482     /**
483      * Returns the Destination IP Address.
484      *
485      * @return the Destination IP Address
486      */
487     public InetAddress getDstIPNum() {
488         InetAddress ip = null;
489         if (nwDst == null || nwDst.isEmpty()) {
490             try {
491                 ip = InetAddress.getByAddress(new byte[16]);
492                 return ip;
493             } catch (UnknownHostException e) {
494                 log.error("",e);
495                 return null;
496             }
497         }
498         try {
499             ip = InetAddress.getByName(nwDst.split("/")[0]);
500         } catch (UnknownHostException e1) {
501             log.error("", e1);
502             return null;
503         }
504         return ip;
505     }
506
507     /**
508      * Returns Source Layer4 Port number.
509      *
510      * @return Source Layer4 Port number
511      */
512     public Short getSrcPortNum() {
513         return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
514                 : Short.valueOf(tpSrc);
515     }
516
517     /**
518      * Returns Destination Layer4 Port number.
519      *
520      * @return Destination Layer4 Port number
521      */
522     public Short getDstPortNum() {
523         return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
524                 : Short.valueOf(tpDst);
525     }
526
527     /**
528      * Get the IP protocol value
529      *
530      * @return the protocol
531      */
532     public Short getProtoNum() {
533         return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
534     }
535
536     /**
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.
540      *
541      * @param other
542      *            the other container flow config with which checking the
543      *            overlap
544      * @return true if the two configurations overlap, false otherwise
545      */
546     public boolean overlap(ContainerFlowConfig other) {
547         if (other == null) {
548             return false;
549         }
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)) {
555                     return true;
556                 }
557             }
558         }
559         return false;
560     }
561
562     /**
563      * Checks if this flow specification configuration is valid.
564      *
565      * @return true, if is valid
566      */
567     public Status validate() {
568         if (!hasValidName()) {
569             return new Status(StatusCode.BADREQUEST, "Invalid name");
570         }
571         Status status = validateVlan();
572         if (!status.isSuccess()) {
573             return status;
574         }
575         status = validateIPs();
576         if (!status.isSuccess()) {
577             return status;
578         }
579         if(!hasValidProtocol()) {
580             return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
581         }
582         if (!hasValidPorts()) {
583             return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
584         }
585         if (this.getMatches().get(0).getMatches() == 0) {
586             return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
587         }
588         return new Status(StatusCode.SUCCESS);
589     }
590
591     /**
592      * Checks if this flow specification configuration has a valid name.
593      *
594      * @return true, if successful
595      */
596     private boolean hasValidName() {
597         return (name != null && !name.isEmpty() && name.matches(regexName));
598     }
599
600     /**
601      * Validates the vlan number
602      *
603      * @return the result of the check as Status object
604      */
605     private Status validateVlan() {
606         if (dlVlan != null) {
607             short vlanId = 0;
608             try {
609                 vlanId = Short.parseShort(dlVlan);
610             } catch (NumberFormatException e) {
611                 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
612             }
613             if (vlanId < 0 || vlanId > 0xfff) {
614                 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
615             }
616         }
617         return new Status(StatusCode.SUCCESS);
618     }
619
620     /**
621      * Validates the network addresses, checks syntax and semantic
622      *
623      * @return the result of the check as Status object, if successful
624      */
625     private Status validateIPs() {
626         if (nwSrc != null) {
627             if (!NetUtils.isIPAddressValid(nwSrc)) {
628                 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
629             }
630             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
631             long prefix = BitBufferHelper.getLong(bytePrefix);
632             if (prefix == 0) {
633                 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
634             }
635         }
636         if (nwDst != null) {
637             if (!NetUtils.isIPAddressValid(nwDst)) {
638                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
639             }
640             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
641             long prefix = BitBufferHelper.getLong(bytePrefix);
642             if (prefix == 0) {
643                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
644             }
645         }
646         return new Status(StatusCode.SUCCESS);
647     }
648
649     /**
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
653      * for more details.
654      *
655      * @return true if a valid protocol value
656      */
657     private boolean hasValidProtocol() {
658         IPProtocols p = IPProtocols.fromString(protocol);
659         return p != null;
660     }
661
662     /**
663      *
664      * @param tpPort
665      *               String representing the transport protocol port number
666      * @return true if tpPort contains a decimal value between 0 and 65535
667      */
668     private boolean hasValidPort(String tpPort) {
669         try {
670             int port = Integer.decode(tpPort);
671             return ((port >= 0) && (port <= 0xffff));
672         } catch (NumberFormatException e) {
673             return false;
674         }
675     }
676
677     /**
678      * Validate the transport protocol source and destination ports as
679      * entered by users.
680      *
681      * @return true if ports are defined and are in valid range
682      */
683     private boolean hasValidPorts() {
684         if (tpSrc !=null && !tpSrc.isEmpty()) {
685             if (!hasValidPort(tpSrc)) {
686                 return false;
687             }
688         }
689
690         if (tpDst !=null && !tpDst.isEmpty()) {
691             return hasValidPort(tpDst);
692         }
693         return true;
694     }
695
696     /**
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
705      */
706     public List<Match> getMatches() {
707         List<Match> matches = new ArrayList<Match>();
708         Match match = new Match();
709
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;
714             int maskLen = 0;
715             if (parts.length > 1) {
716                 maskLen = Integer.parseInt(parts[1]);
717             } else {
718                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
719             }
720             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
721             match.setField(MatchType.NW_SRC, ip, mask);
722         }
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;
727             int maskLen = 0;
728             if (parts.length > 1) {
729                 maskLen = Integer.parseInt(parts[1]);
730             } else {
731                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
732             }
733             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
734             match.setField(MatchType.NW_DST, ip, mask);
735         }
736         if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
737             match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
738         }
739         if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
740             match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
741         }
742         if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
743             match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
744         }
745
746         matches.add(match);
747         if(!ContainerFlowConfig.unidirectional) {
748             Match reverse = match.reverse();
749             if (!match.equals(reverse)) {
750                 matches.add(reverse);
751             }
752         }
753         return matches;
754     }
755
756     /*
757      * (non-Javadoc)
758      *
759      * @see java.lang.Object#toString()
760      */
761     @Override
762     public String toString() {
763         return "Container Flow={name:" + name + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
764                 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";
765     }
766 }