9d34bd45c845d3c1648fb791508625da0e2cc9b2
[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.util.Set;
12
13 import org.opendaylight.vtn.manager.VTNException;
14 import org.opendaylight.vtn.manager.util.NumberUtils;
15
16 import org.opendaylight.vtn.manager.internal.PacketContext;
17 import org.opendaylight.vtn.manager.internal.util.flow.action.VTNSetPortDstAction;
18 import org.opendaylight.vtn.manager.internal.util.flow.action.VTNSetPortSrcAction;
19 import org.opendaylight.vtn.manager.internal.util.flow.match.FlowMatchType;
20 import org.opendaylight.vtn.manager.internal.util.flow.match.VTNLayer4PortMatch;
21 import org.opendaylight.vtn.manager.internal.util.flow.match.VTNPortRange;
22 import org.opendaylight.vtn.manager.internal.util.packet.Layer4PortHeader;
23 import org.opendaylight.vtn.manager.internal.util.rpc.RpcException;
24
25 import org.opendaylight.controller.sal.packet.Packet;
26
27 /**
28  * {@code PortProtoPacket} class implements a cache for layer 4 protocol
29  * header, which identifies the service using 16-bit port number.
30  *
31  * @param <T>  Type of packet.
32  */
33 public abstract class PortProtoPacket<T extends Packet>
34     implements L4Packet, Layer4PortHeader {
35     /**
36      * A pseudo port number which indicates the port number is not specified.
37      */
38     private static final int  PORT_NONE = -1;
39
40     /**
41      * Computed checksum that indicates the packet verification succeeded.
42      */
43     private static final int  CKSUM_OK = 0xffff;
44
45     /**
46      * The number of octets in TCP/UDP checksum.
47      */
48     private static final int  CKSUM_BYTES = Short.SIZE / Byte.SIZE;
49
50     /**
51      * A mask value used to clear LSB.
52      */
53     private static final int  MASK_CLEAR_LSB = ~1;
54
55     /**
56      * Cached values in a protocol header.
57      */
58     private Values  values = new Values();
59
60     /**
61      * Protocol header values to be set.
62      */
63     private Values  modifiedValues;
64
65     /**
66      * Set {@code true} if this instance is created by {@link #clone()}.
67      */
68     private boolean  cloned;
69
70     /**
71      * This class describes modifiable fields in a protocol hedaer.
72      */
73     private static final class Values implements Cloneable {
74         /**
75          * The source port number.
76          */
77         private int  sourcePort = PORT_NONE;
78
79         /**
80          * The destination port number.
81          */
82         private int  destinationPort = PORT_NONE;
83
84         /**
85          * Constructor.
86          */
87         private Values() {
88         }
89
90         /**
91          * Return the source port number.
92          *
93          * @return  An integer value which indicates the source port.
94          *          {@link PortProtoPacket#PORT_NONE} is returned if not
95          *          configured.
96          */
97         private int getSourcePort() {
98             return sourcePort;
99         }
100
101         /**
102          * Set the source port number.
103          *
104          * @param port  An integer value which indicates the source port.
105          */
106         private void setSourcePort(int port) {
107             sourcePort = port;
108         }
109
110         /**
111          * Set the source port number.
112          *
113          * @param port  A short integer value which indicates the source port.
114          * @return  An integer value which indicates the source port.
115          */
116         private int setSourcePort(short port) {
117             sourcePort = NumberUtils.getUnsigned(port);
118             return sourcePort;
119         }
120
121         /**
122          * Return the destination port number.
123          *
124          * @return  An integer value which indicates the destination port.
125          *          {@link PortProtoPacket#PORT_NONE} is returned if not
126          *          configured.
127          */
128         private int getDestinationPort() {
129             return destinationPort;
130         }
131
132         /**
133          * Set the destination port number.
134          *
135          * @param port  An integer value which indicates the destination port.
136          */
137         private void setDestinationPort(int port) {
138             destinationPort = port;
139         }
140
141         /**
142          * Set the destination port number.
143          *
144          * @param port  A short integer value which indicates the destination
145          *              port.
146          * @return  An integer value which indicates the destination port.
147          */
148         private int setDestinationPort(short port) {
149             destinationPort = NumberUtils.getUnsigned(port);
150             return destinationPort;
151         }
152
153         /**
154          * Fetch all modifiable field values from the given packet.
155          *
156          * <p>
157          *   Field values already cached in this instance are preserved.
158          * </p>
159          *
160          * @param packet  A {@link PortProtoPacket} instance.
161          */
162         private void fill(PortProtoPacket packet) {
163             if (sourcePort == PORT_NONE) {
164                 setSourcePort(packet.getRawSourcePort());
165             }
166             if (destinationPort == PORT_NONE) {
167                 setDestinationPort(packet.getRawDestinationPort());
168             }
169         }
170
171         /**
172          * Return a shallow copy of this instance.
173          *
174          * @return  A shallow copy of this instance.
175          */
176         @Override
177         public Values clone() {
178             try {
179                 return (Values)super.clone();
180             } catch (CloneNotSupportedException e) {
181                 // This should never happen.
182                 throw new IllegalStateException("clone() failed", e);
183             }
184         }
185     }
186
187     /**
188      * Compute the one's complement sum of all 16-bit words in the given
189      * packet.
190      *
191      * @param ipv4  An {@link Inet4Packet} instance that contains the given
192      *              packet.
193      * @param data  A serialized packet data.
194      * @return  A computed checksum.
195      */
196     private static int computeChecksum(Inet4Packet ipv4, byte[] data) {
197         // Create a pseudo IPv4 header.
198         byte proto = (byte)ipv4.getProtocol();
199         byte[] header = ipv4.getHeaderForChecksum(proto, (short)data.length);
200         int sum = 0;
201         for (int i = 0; i < header.length; i += CKSUM_BYTES) {
202             int v = ((header[i] & NumberUtils.MASK_BYTE) << Byte.SIZE) |
203                 (header[i + 1] & NumberUtils.MASK_BYTE);
204             sum += v;
205         }
206
207         int rsize = (data.length & MASK_CLEAR_LSB);
208         for (int i = 0; i < rsize; i += CKSUM_BYTES) {
209             int v = ((data[i] & NumberUtils.MASK_BYTE) << Byte.SIZE) |
210                 (data[i + 1] & NumberUtils.MASK_BYTE);
211             sum += v;
212         }
213         if (rsize < data.length) {
214             // Zero padding is needed.
215             int v = (data[rsize] & NumberUtils.MASK_BYTE) << Byte.SIZE;
216             sum += v;
217         }
218
219         int carry = (sum >>> Short.SIZE);
220         return (sum & NumberUtils.MASK_SHORT) + carry;
221     }
222
223     /**
224      * Compute the checksum of the given packet.
225      *
226      * @param ipv4    An {@link Inet4Packet} instance that contains the given
227      *                packet.
228      * @param packet  A {@link Packet} instance.
229      * @param sumOff  Offset in bytes to the checksum field.
230      * @return  A computed checksum.
231      * @throws VTNException
232      *    An error occurred.
233      */
234     protected static final short computeChecksum(Inet4Packet ipv4,
235                                                  Packet packet, int sumOff)
236         throws VTNException {
237         // Serialize the given packet.
238         byte[] data;
239         try {
240             data = packet.serialize();
241         } catch (Exception e) {
242             // This should never happen.
243             throw new VTNException("Failed to serialize the packet.", e);
244         }
245
246         // Clear checksum field.
247         NumberUtils.setShort(data, sumOff, (short)0);
248
249         // Compute checksum.
250         return (short)~computeChecksum(ipv4, data);
251     }
252
253     /**
254      * Verify the packet using the checksum.
255      *
256      * @param ipv4    An {@link Inet4Packet} instance that contains the given
257      *                packet.
258      * @param packet  A {@link Packet} to be verified.
259      * @return  {@code true} is returned only if the verification succeeded.
260      * @throws VTNException
261      *    An error occurred.
262      */
263     protected static final boolean verifyChecksum(Inet4Packet ipv4,
264                                                   Packet packet)
265         throws VTNException {
266         // Serialize the given packet.
267         byte[] data;
268         try {
269             data = packet.serialize();
270         } catch (Exception e) {
271             // This should never happen.
272             throw new VTNException("Failed to serialize the packet.", e);
273         }
274
275         // Compute checksum.
276         int sum = computeChecksum(ipv4, data);
277         return (sum == CKSUM_OK);
278     }
279
280     /**
281      * Construct a new instance.
282      */
283     protected PortProtoPacket() {
284     }
285
286     /**
287      * Return a {@link Packet} instance to set modified values.
288      *
289      * @return  A {@link Packet} instance.
290      * @throws VTNException
291      *    Failed to copy the packet.
292      */
293     protected final T getPacketForWrite() throws VTNException {
294         T pkt = getPacketForWrite(cloned);
295         cloned = false;
296         return pkt;
297     }
298
299     /**
300      * Derive the source port number from the packet.
301      *
302      * @return  A short integer value which represents the source port number.
303      */
304     protected abstract short getRawSourcePort();
305
306     /**
307      * Derive the destination port number from the packet.
308      *
309      * @return  A short integer value which represents the destination port
310      *          number.
311      */
312     protected abstract short getRawDestinationPort();
313
314     /**
315      * Set the source port number to the given packet.
316      *
317      * @param pkt   A {@link Packet} instance.
318      * @param port  A short integer value which indicates the source port.
319      */
320     protected abstract void setRawSourcePort(T pkt, short port);
321
322     /**
323      * Set the destination port number to the given packet.
324      *
325      * @param pkt   A {@link Packet} instance.
326      * @param port  A short integer value which indicates the destination port.
327      */
328     protected abstract void setRawDestinationPort(T pkt, short port);
329
330     /**
331      * Return a {@link Packet} instance to set modified values.
332      *
333      * @param doCopy {@code true} is passed if the packet configured in this
334      *               instance needs to be copied.
335      * @return  A {@link Packet} instance.
336      * @throws VTNException
337      *    Failed to copy the packet.
338      */
339     protected abstract T getPacketForWrite(boolean doCopy) throws VTNException;
340
341     /**
342      * Return the name of the protocol.
343      *
344      * @return  The protocol name.
345      */
346     protected abstract String getProtocolName();
347
348     /**
349      * Construct flow match fields.
350      *
351      * @param src  A {@link VTNPortRange} instance which specifies the
352      *             source port.
353      * @param dst  A {@link VTNPortRange} instance which specifies the
354      *             destination port.
355      * @return  A {@link VTNLayer4PortMatch} instance.
356      * @throws RpcException  Invalid value is specified.
357      */
358     protected abstract VTNLayer4PortMatch createMatch(VTNPortRange src,
359                                                       VTNPortRange dst)
360         throws RpcException;
361
362     /**
363      * Return a flow match type corresponding to the source port.
364      *
365      * @return  A {@link FlowMatchType} instance.
366      */
367     public abstract FlowMatchType getSourceMatchType();
368
369     /**
370      * Return a flow match type corresponding to the destination port.
371      *
372      * @return  A {@link FlowMatchType} instance.
373      */
374     public abstract FlowMatchType getDestinationMatchType();
375
376     /**
377      * Return a {@link Values} instance that keeps current values for
378      * protocol header fields.
379      *
380      * @return  A {@link Values} instance.
381      */
382     private Values getValues() {
383         return (modifiedValues == null) ? values : modifiedValues;
384     }
385
386     /**
387      * Return a {@link Values} instance that keeps protocol header field values
388      * to be set.
389      *
390      * @return  A {@link Values} instance.
391      */
392     private Values getModifiedValues() {
393         if (modifiedValues == null) {
394             values.fill(this);
395             modifiedValues = values.clone();
396         }
397
398         return modifiedValues;
399     }
400
401     // CachedPacket
402
403     /**
404      * {@inheritDoc}
405      */
406     @Override
407     public final boolean commit(PacketContext pctx) throws VTNException {
408         boolean mod = false;
409         T pkt = null;
410         if (modifiedValues != null) {
411             // At least one flow action that modifies TCP or UDP header is
412             // configured.
413             pctx.addMatchField(FlowMatchType.DL_TYPE);
414             pctx.addMatchField(FlowMatchType.IP_PROTO);
415
416             int src = modifiedValues.getSourcePort();
417             if (values.getSourcePort() != src) {
418                 // Source port was modified.
419                 pkt = getPacketForWrite();
420                 setRawSourcePort(pkt, (short)src);
421                 mod = true;
422             } else if (pctx.hasMatchField(getSourceMatchType())) {
423                 // Source port in the original packet is unchanged and it will
424                 // be specified in flow match. So we don't need to configure
425                 // SET_TP_SRC action.
426                 pctx.removeFilterAction(VTNSetPortSrcAction.class);
427             }
428
429             int dst = modifiedValues.getDestinationPort();
430             if (values.getDestinationPort() != dst) {
431                 // Destination port was modified.
432                 if (pkt == null) {
433                     pkt = getPacketForWrite();
434                 }
435                 setRawDestinationPort(pkt, (short)dst);
436                 mod = true;
437             } else if (pctx.hasMatchField(getDestinationMatchType())) {
438                 // Destination port in the original packet is unchanged and
439                 // it will be specified in flow match. So we don't need to
440                 // configure SET_TP_DST action.
441                 pctx.removeFilterAction(VTNSetPortDstAction.class);
442             }
443         }
444
445         return mod;
446     }
447
448     /**
449      * {@inheritDoc}
450      */
451     @Override
452     public final PortProtoPacket clone() {
453         try {
454             PortProtoPacket p = (PortProtoPacket)super.clone();
455             Values v = p.values;
456             p.values = v.clone();
457
458             v = p.modifiedValues;
459             if (v != null) {
460                 p.modifiedValues = v.clone();
461             }
462             p.cloned = true;
463
464             return p;
465         } catch (CloneNotSupportedException e) {
466             // This should never happen.
467             throw new IllegalStateException("clone() failed", e);
468         }
469     }
470
471     // L4Packet
472
473     /**
474      * {@inheritDoc}
475      */
476     @Override
477     public final VTNLayer4PortMatch createMatch(Set<FlowMatchType> fields)
478         throws RpcException {
479         Values v = values;
480         v.fill(this);
481
482         VTNPortRange src = (fields.contains(getSourceMatchType()))
483             ? new VTNPortRange(v.getSourcePort())
484             : null;
485         VTNPortRange dst = (fields.contains(getDestinationMatchType()))
486             ? new VTNPortRange(v.getDestinationPort())
487             : null;
488
489         return createMatch(src, dst);
490     }
491
492     // Layer4PortHeader
493
494     /**
495      * {@inheritDoc}
496      */
497     @Override
498     public final int getSourcePort() {
499         Values v = getValues();
500         int port = v.getSourcePort();
501         if (port == PORT_NONE) {
502             short p = getRawSourcePort();
503             port = v.setSourcePort(p);
504         }
505
506         return port;
507     }
508
509     /**
510      * {@inheritDoc}
511      */
512     @Override
513     public final void setSourcePort(int port) {
514         Values v = getModifiedValues();
515         v.setSourcePort(port);
516     }
517
518     /**
519      * {@inheritDoc}
520      */
521     @Override
522     public final int getDestinationPort() {
523         Values v = getValues();
524         int port = v.getDestinationPort();
525         if (port == PORT_NONE) {
526             short p = getRawDestinationPort();
527             port = v.setDestinationPort(p);
528         }
529
530         return port;
531     }
532
533     /**
534      * {@inheritDoc}
535      */
536     @Override
537     public final void setDestinationPort(int port) {
538         Values v = getModifiedValues();
539         v.setDestinationPort(port);
540     }
541
542     // ProtocolHeader
543
544     /**
545      * {@inheritDoc}
546      */
547     @Override
548     public void setDescription(StringBuilder builder) {
549         int src = getSourcePort();
550         int dst = getDestinationPort();
551         builder.append(getProtocolName()).
552             append("[src=").append(src).
553             append(",dst=").append(dst).append(']');
554     }
555 }