497e41312911b9a09c77d50a8daf2dda48ddf6d7
[vtn.git] /
1 /*
2  * Copyright (c) 2014, 2015 NEC Corporation.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.vtn.manager.internal.packet.cache;
10
11 import java.net.InetAddress;
12 import java.util.Set;
13
14 import org.opendaylight.vtn.manager.VTNException;
15 import org.opendaylight.vtn.manager.util.Ip4Network;
16 import org.opendaylight.vtn.manager.util.IpNetwork;
17 import org.opendaylight.vtn.manager.util.NumberUtils;
18
19 import org.opendaylight.vtn.manager.internal.PacketContext;
20 import org.opendaylight.vtn.manager.internal.util.MiscUtils;
21 import org.opendaylight.vtn.manager.internal.util.flow.action.VTNSetInetDscpAction;
22 import org.opendaylight.vtn.manager.internal.util.flow.action.VTNSetInetDstAction;
23 import org.opendaylight.vtn.manager.internal.util.flow.action.VTNSetInetSrcAction;
24 import org.opendaylight.vtn.manager.internal.util.flow.match.FlowMatchType;
25 import org.opendaylight.vtn.manager.internal.util.flow.match.VTNInet4Match;
26 import org.opendaylight.vtn.manager.internal.util.packet.InetHeader;
27 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
28
29 import org.opendaylight.controller.sal.packet.IPv4;
30
31 /**
32  * {@code Inet4Packet} class implements a cache for an {@link IPv4} instance.
33  */
34 public final class Inet4Packet implements CachedPacket, InetHeader {
35     /**
36      * A pseudo IP protocol number which indicates the IP protocol number is
37      * not specified.
38      */
39     private static final short  PROTO_NONE = -1;
40
41     /**
42      * A pseudo DSCP value which indicates the DSCP value is not specified.
43      */
44     private static final short  DSCP_NONE = -1;
45
46     /**
47      * Byte offset to the source address in a pseudo IPv4 header used for
48      * computing TCP/UDP checksum.
49      */
50     private static final int CKSUM_OFF_SRC = 0;
51
52     /**
53      * Byte offset to the destination address in a pseudo IPv4 header used for
54      * computing TCP/UDP checksum.
55      */
56     private static final int CKSUM_OFF_DST = 4;
57
58     /**
59      * Byte offset to the IP protocol number in a pseudo IPv4 header used for
60      * computing TCP/UDP checksum.
61      */
62     private static final int CKSUM_OFF_PROTO = 9;
63
64     /**
65      * Byte offset to the payload length in a pseudo IPv4 header used for
66      * computing TCP/UDP checksum.
67      */
68     private static final int CKSUM_OFF_LEN = 10;
69
70     /**
71      * The number of bytes in a pseudo IPv4 header used for computing
72      * TCP/UDP checksum.
73      */
74     private static final int CKSUM_HEADER_SIZE = 12;
75
76     /**
77      * An {@link IPv4} packet.
78      */
79     private IPv4  packet;
80
81     /**
82      * The IP protocol number in the IPv4 packet.
83      */
84     private short  protocol = PROTO_NONE;
85
86     /**
87      * Cached values in IPv4 header.
88      */
89     private Values  values = new Values();
90
91     /**
92      * IPv4 header values to be set.
93      */
94     private Values  modifiedValues;
95
96     /**
97      * Set {@code true} if this instance is created by {@link #clone()}.
98      */
99     private boolean  cloned;
100
101     /**
102      * This class describes modifiable fields in IPv4 header.
103      */
104     private static final class Values implements Cloneable {
105         /**
106          * The source IP address.
107          */
108         private Ip4Network  sourceAddress;
109
110         /**
111          * The destination IP address.
112          */
113         private Ip4Network  destinationAddress;
114
115         /**
116          * The DSCP field value in the IPv4 packet.
117          */
118         private short  dscp = DSCP_NONE;
119
120         /**
121          * Constructor.
122          */
123         private Values() {
124         }
125
126         /**
127          * Return the source IP address.
128          *
129          * @return  An {@link Ip4Network} instance which represents the
130          *          source IP address.
131          *          {@code null} is returned if not configured.
132          */
133         private Ip4Network getSourceAddress() {
134             return sourceAddress;
135         }
136
137         /**
138          * Set the source IP address.
139          *
140          * @param addr  An {@link Ip4Network} instance which represents the
141          *              source IPv4 address.
142          */
143         private void setSourceAddress(Ip4Network addr) {
144             sourceAddress = addr;
145         }
146
147         /**
148          * Return the destination IP address.
149          *
150          * @return  An {@link Ip4Network} instance which represents the
151          *          destination IP address.
152          *          {@code null} is returned if not configured.
153          */
154         private Ip4Network getDestinationAddress() {
155             return destinationAddress;
156         }
157
158         /**
159          * Set the destination IP address.
160          *
161          * @param addr  An {@link Ip4Network} instance which represents the
162          *              destination IPv4 address.
163          */
164         private void setDestinationAddress(Ip4Network addr) {
165             destinationAddress = addr;
166         }
167
168         /**
169          * Return the DSCP field value.
170          *
171          * @return  A short value which indicates the DSCP field value.
172          *          {@link Inet4Packet#DSCP_NONE} is returned if not
173          *          configured.
174          */
175         private short getDscp() {
176             return dscp;
177         }
178
179         /**
180          * Set the DSCP field value.
181          *
182          * @param value  A short value which indicates the DSCP field value.
183          */
184         private void setDscp(short value) {
185             dscp = value;
186         }
187
188         /**
189          * Fetch all modifiable field values from the given packet.
190          *
191          * <p>
192          *   Field values already cached in this instance are preserved.
193          * </p>
194          *
195          * @param ipv4  An {@link IPv4} instance.
196          */
197         private void fill(IPv4 ipv4) {
198             if (sourceAddress == null) {
199                 sourceAddress = new Ip4Network(ipv4.getSourceAddress());
200             }
201             if (destinationAddress == null) {
202                 destinationAddress =
203                     new Ip4Network(ipv4.getDestinationAddress());
204             }
205             if (dscp == DSCP_NONE) {
206                 dscp = ipv4.getDiffServ();
207             }
208         }
209
210         /**
211          * Return a shallow copy of this instance.
212          *
213          * @return  A shallow copy of this instance.
214          */
215         @Override
216         public Values clone() {
217             try {
218                 return (Values)super.clone();
219             } catch (CloneNotSupportedException e) {
220                 // This should never happen.
221                 throw new IllegalStateException("clone() failed", e);
222             }
223         }
224     }
225
226     /**
227      * Construct a new instance.
228      *
229      * @param ipv4  An {@link IPv4} instance.
230      */
231     public Inet4Packet(IPv4 ipv4) {
232         packet = ipv4;
233     }
234
235     /**
236      * Determine whether the source or destination address is modified or not.
237      *
238      * @return  {@code true} only if the source or destination address is
239      *          modified.
240      */
241     public boolean isAddressModified() {
242         if (modifiedValues == null) {
243             return false;
244         }
245
246         Ip4Network oldIp = values.getSourceAddress();
247         Ip4Network newIp = modifiedValues.getSourceAddress();
248         if (oldIp.getAddress() != newIp.getAddress()) {
249             return true;
250         }
251
252         oldIp = values.getDestinationAddress();
253         newIp = modifiedValues.getDestinationAddress();
254         return (oldIp.getAddress() != newIp.getAddress());
255     }
256
257     /**
258      * Create a pseudo IPv4 header used for computing TCP/UDP checksum.
259      *
260      * @param proto  An IP protocol number.
261      * @param len    The number of octets in a payload.
262      * @return  A byte array which represents the pseudo IPv4 header.
263      */
264     public byte[] getHeaderForChecksum(byte proto, short len) {
265         byte[] header = new byte[CKSUM_HEADER_SIZE];
266
267         int src = getSourceAddress().getAddress();
268         int dst = getDestinationAddress().getAddress();
269         NumberUtils.setInt(header, CKSUM_OFF_SRC, src);
270         NumberUtils.setInt(header, CKSUM_OFF_DST, dst);
271         header[CKSUM_OFF_PROTO] = proto;
272         NumberUtils.setShort(header, CKSUM_OFF_LEN, len);
273
274         return header;
275     }
276
277     /**
278      * Construct match fields to test IP header in this packet.
279      *
280      * <p>
281      *   Note that this method creates match fields that matches the original
282      *   packet. Any modification to the packet is ignored.
283      * </p>
284      *
285      * @param fields  A set of {@link FlowMatchType} instances corresponding to
286      *                match fields to be tested.
287      * @return  A {@link VTNInet4Match} instance.
288      * @throws RpcException  This packet is broken.
289      */
290     public VTNInet4Match createMatch(Set<FlowMatchType> fields)
291         throws RpcException {
292         Values v = values;
293         v.fill(packet);
294
295         Ip4Network src = (fields.contains(FlowMatchType.IP_SRC))
296             ? v.getSourceAddress()
297             : null;
298         Ip4Network dst = (fields.contains(FlowMatchType.IP_DST))
299             ? v.getDestinationAddress()
300             : null;
301         Short proto = (fields.contains(FlowMatchType.IP_PROTO))
302             ? Short.valueOf(getProtocol())
303             : null;
304         Short dscp = (fields.contains(FlowMatchType.IP_DSCP))
305             ? Short.valueOf(v.getDscp())
306             : null;
307
308         return new VTNInet4Match(src, dst, proto, dscp);
309     }
310
311     /**
312      * Return a {@link Values} instance that keeps current values for
313      * IPv4 header fields.
314      *
315      * @return  A {@link Values} instance.
316      */
317     private Values getValues() {
318         return (modifiedValues == null) ? values : modifiedValues;
319     }
320
321     /**
322      * Return a {@link Values} instance that keeps IPv4 header field values to
323      * be set.
324      *
325      * @return  A {@link Values} instance.
326      */
327     private Values getModifiedValues() {
328         if (modifiedValues == null) {
329             values.fill(packet);
330             modifiedValues = values.clone();
331         }
332
333         return modifiedValues;
334     }
335
336     /**
337      * Return an {@link IPv4} instance to set modified values.
338      *
339      * @return  An {@link IPv4} instance.
340      * @throws VTNException
341      *    Failed to copy the packet.
342      */
343     private IPv4 getPacketForWrite() throws VTNException {
344         if (cloned) {
345             // Create a copy of the original packet.
346             packet = MiscUtils.copy(packet, new IPv4());
347             cloned = false;
348         }
349
350         return packet;
351     }
352
353     // CachedPacket
354
355     /**
356      * Return an {@link IPv4} instance configured in this instance.
357      *
358      * <p>
359      *   Note that modification to the IPv4 header is not applied to the
360      *   returned until {@link #commit(PacketContext)} is called.
361      * </p>
362      *
363      * @return  An {@link IPv4} instance.
364      */
365     @Override
366     public IPv4 getPacket() {
367         return packet;
368     }
369
370     /**
371      * {@inheritDoc}
372      */
373     @Override
374     public boolean commit(PacketContext pctx) throws VTNException {
375         boolean mod = false;
376         IPv4 ipv4 = null;
377         if (modifiedValues != null) {
378             // At least one flow action that modifies IPv4 header is
379             // configured.
380             pctx.addMatchField(FlowMatchType.DL_TYPE);
381
382             Ip4Network oldIp = values.getSourceAddress();
383             Ip4Network newIp = modifiedValues.getSourceAddress();
384             if (oldIp.getAddress() != newIp.getAddress()) {
385                 // Source address was modified.
386                 InetAddress iaddr = newIp.getInetAddress();
387                 ipv4 = getPacketForWrite();
388                 ipv4.setSourceAddress(iaddr);
389                 mod = true;
390             } else if (pctx.hasMatchField(FlowMatchType.IP_SRC)) {
391                 // Source IP address in the original packet is unchanged and
392                 // it will be specified in flow match. So we don't need to
393                 // configure SET_NW_SRC action.
394                 pctx.removeFilterAction(VTNSetInetSrcAction.class);
395             }
396
397             oldIp = values.getDestinationAddress();
398             newIp = modifiedValues.getDestinationAddress();
399             if (oldIp.getAddress() != newIp.getAddress()) {
400                 // Destination address was modified.
401                 InetAddress iaddr = newIp.getInetAddress();
402                 if (ipv4 == null) {
403                     ipv4 = getPacketForWrite();
404                 }
405                 ipv4.setDestinationAddress(iaddr);
406                 mod = true;
407             } else if (pctx.hasMatchField(FlowMatchType.IP_DST)) {
408                 // Destination IP address in the original packet is unchanged
409                 // and it will be specified in flow match. So we don't need to
410                 // configure SET_NW_DST action.
411                 pctx.removeFilterAction(VTNSetInetDstAction.class);
412             }
413
414             short dscp = modifiedValues.getDscp();
415             if (values.getDscp() != dscp) {
416                 // DSCP field was modified.
417                 if (ipv4 == null) {
418                     ipv4 = getPacketForWrite();
419                 }
420                 ipv4.setDiffServ((byte)dscp);
421                 mod = true;
422             } else if (pctx.hasMatchField(FlowMatchType.IP_DSCP)) {
423                 // DSCP value in the original packet is unchanged and it will
424                 // be specified in flow match. So we don't need to configure
425                 // SET_NW_TOS action.
426                 pctx.removeFilterAction(VTNSetInetDscpAction.class);
427             }
428         }
429
430         return mod;
431     }
432
433     /**
434      * {@inheritDoc}
435      */
436     @Override
437     public Inet4Packet clone() {
438         try {
439             Inet4Packet ip = (Inet4Packet)super.clone();
440             Values v = ip.values;
441             ip.values = v.clone();
442
443             v = ip.modifiedValues;
444             if (v != null) {
445                 ip.modifiedValues = v.clone();
446             }
447             ip.cloned = true;
448
449             return ip;
450         } catch (CloneNotSupportedException e) {
451             // This should never happen.
452             throw new IllegalStateException("clone() failed", e);
453         }
454     }
455
456     // InetHeader
457
458     /**
459      * {@inheritDoc}
460      */
461     @Override
462     public Ip4Network getSourceAddress() {
463         Values v = getValues();
464         Ip4Network ipn = v.getSourceAddress();
465         if (ipn == null) {
466             ipn = new Ip4Network(packet.getSourceAddress());
467             v.setSourceAddress(ipn);
468         }
469
470         return ipn;
471     }
472
473     /**
474      * {@inheritDoc}
475      */
476     @Override
477     public boolean setSourceAddress(IpNetwork ipn) {
478         boolean result;
479         Ip4Network ip4 = Ip4Network.toIp4Address(ipn);
480         if (ip4 == null) {
481             result = false;
482         } else {
483             Values v = getModifiedValues();
484             v.setSourceAddress(ip4);
485             result = true;
486         }
487
488         return result;
489     }
490
491     /**
492      * {@inheritDoc}
493      */
494     @Override
495     public Ip4Network getDestinationAddress() {
496         Values v = getValues();
497         Ip4Network ipn = v.getDestinationAddress();
498         if (ipn == null) {
499             ipn = new Ip4Network(packet.getDestinationAddress());
500             v.setDestinationAddress(ipn);
501         }
502
503         return ipn;
504     }
505
506     /**
507      * {@inheritDoc}
508      */
509     @Override
510     public boolean setDestinationAddress(IpNetwork ipn) {
511         boolean result;
512         Ip4Network ip4 = Ip4Network.toIp4Address(ipn);
513         if (ip4 == null) {
514             result = false;
515         } else {
516             Values v = getModifiedValues();
517             v.setDestinationAddress(ip4);
518             result = true;
519         }
520
521         return result;
522     }
523
524     /**
525      * {@inheritDoc}
526      */
527     @Override
528     public short getProtocol() {
529         if (protocol == PROTO_NONE) {
530             protocol = (short)NumberUtils.getUnsigned(packet.getProtocol());
531         }
532
533         return protocol;
534     }
535
536     /**
537      * {@inheritDoc}
538      */
539     @Override
540     public short getDscp() {
541         Values v = getValues();
542         short dscp = v.getDscp();
543         if (dscp == DSCP_NONE) {
544             dscp = (short)NumberUtils.getUnsigned(packet.getDiffServ());
545             v.setDscp(dscp);
546         }
547
548         return dscp;
549     }
550
551     /**
552      * {@inheritDoc}
553      */
554     @Override
555     public void setDscp(short dscp) {
556         Values v = getModifiedValues();
557         v.setDscp(dscp);
558     }
559
560     // ProtocolHeader
561
562     /**
563      * {@inheritDoc}
564      */
565     @Override
566     public void setDescription(StringBuilder builder) {
567         Ip4Network src = getSourceAddress();
568         Ip4Network dst = getDestinationAddress();
569         int proto = (int)getProtocol();
570         int dscp = (int)getDscp();
571         builder.append("Inet4[src=").append(src.getText()).
572             append(",dst=").append(dst.getText()).
573             append(",proto=").append(proto).
574             append(",dscp=").append(dscp).append(']');
575     }
576 }