2 * Copyright (c) 2014, 2015 NEC Corporation. All rights reserved.
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
9 package org.opendaylight.vtn.manager.internal.packet.cache;
13 import org.opendaylight.vtn.manager.VTNException;
14 import org.opendaylight.vtn.manager.util.NumberUtils;
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;
25 import org.opendaylight.controller.sal.packet.Packet;
28 * {@code PortProtoPacket} class implements a cache for layer 4 protocol
29 * header, which identifies the service using 16-bit port number.
31 * @param <T> Type of packet.
33 public abstract class PortProtoPacket<T extends Packet>
34 implements L4Packet, Layer4PortHeader {
36 * A pseudo port number which indicates the port number is not specified.
38 private static final int PORT_NONE = -1;
41 * Computed checksum that indicates the packet verification succeeded.
43 private static final int CKSUM_OK = 0xffff;
46 * The number of octets in TCP/UDP checksum.
48 private static final int CKSUM_BYTES = Short.SIZE / Byte.SIZE;
51 * A mask value used to clear LSB.
53 private static final int MASK_CLEAR_LSB = ~1;
56 * Cached values in a protocol header.
58 private Values values = new Values();
61 * Protocol header values to be set.
63 private Values modifiedValues;
66 * Set {@code true} if this instance is created by {@link #clone()}.
68 private boolean cloned;
71 * This class describes modifiable fields in a protocol hedaer.
73 private static final class Values implements Cloneable {
75 * The source port number.
77 private int sourcePort = PORT_NONE;
80 * The destination port number.
82 private int destinationPort = PORT_NONE;
91 * Return the source port number.
93 * @return An integer value which indicates the source port.
94 * {@link PortProtoPacket#PORT_NONE} is returned if not
97 private int getSourcePort() {
102 * Set the source port number.
104 * @param port An integer value which indicates the source port.
106 private void setSourcePort(int port) {
111 * Set the source port number.
113 * @param port A short integer value which indicates the source port.
114 * @return An integer value which indicates the source port.
116 private int setSourcePort(short port) {
117 sourcePort = NumberUtils.getUnsigned(port);
122 * Return the destination port number.
124 * @return An integer value which indicates the destination port.
125 * {@link PortProtoPacket#PORT_NONE} is returned if not
128 private int getDestinationPort() {
129 return destinationPort;
133 * Set the destination port number.
135 * @param port An integer value which indicates the destination port.
137 private void setDestinationPort(int port) {
138 destinationPort = port;
142 * Set the destination port number.
144 * @param port A short integer value which indicates the destination
146 * @return An integer value which indicates the destination port.
148 private int setDestinationPort(short port) {
149 destinationPort = NumberUtils.getUnsigned(port);
150 return destinationPort;
154 * Fetch all modifiable field values from the given packet.
157 * Field values already cached in this instance are preserved.
160 * @param packet A {@link PortProtoPacket} instance.
162 private void fill(PortProtoPacket packet) {
163 if (sourcePort == PORT_NONE) {
164 setSourcePort(packet.getRawSourcePort());
166 if (destinationPort == PORT_NONE) {
167 setDestinationPort(packet.getRawDestinationPort());
172 * Return a shallow copy of this instance.
174 * @return A shallow copy of this instance.
177 public Values clone() {
179 return (Values)super.clone();
180 } catch (CloneNotSupportedException e) {
181 // This should never happen.
182 throw new IllegalStateException("clone() failed", e);
188 * Compute the one's complement sum of all 16-bit words in the given
191 * @param ipv4 An {@link Inet4Packet} instance that contains the given
193 * @param data A serialized packet data.
194 * @return A computed checksum.
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);
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);
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);
213 if (rsize < data.length) {
214 // Zero padding is needed.
215 int v = (data[rsize] & NumberUtils.MASK_BYTE) << Byte.SIZE;
219 int carry = (sum >>> Short.SIZE);
220 return (sum & NumberUtils.MASK_SHORT) + carry;
224 * Compute the checksum of the given packet.
226 * @param ipv4 An {@link Inet4Packet} instance that contains the given
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
234 protected static final short computeChecksum(Inet4Packet ipv4,
235 Packet packet, int sumOff)
236 throws VTNException {
237 // Serialize the given packet.
240 data = packet.serialize();
241 } catch (Exception e) {
242 // This should never happen.
243 throw new VTNException("Failed to serialize the packet.", e);
246 // Clear checksum field.
247 NumberUtils.setShort(data, sumOff, (short)0);
250 return (short)~computeChecksum(ipv4, data);
254 * Verify the packet using the checksum.
256 * @param ipv4 An {@link Inet4Packet} instance that contains the given
258 * @param packet A {@link Packet} to be verified.
259 * @return {@code true} is returned only if the verification succeeded.
260 * @throws VTNException
263 protected static final boolean verifyChecksum(Inet4Packet ipv4,
265 throws VTNException {
266 // Serialize the given packet.
269 data = packet.serialize();
270 } catch (Exception e) {
271 // This should never happen.
272 throw new VTNException("Failed to serialize the packet.", e);
276 int sum = computeChecksum(ipv4, data);
277 return (sum == CKSUM_OK);
281 * Construct a new instance.
283 protected PortProtoPacket() {
287 * Return a {@link Packet} instance to set modified values.
289 * @return A {@link Packet} instance.
290 * @throws VTNException
291 * Failed to copy the packet.
293 protected final T getPacketForWrite() throws VTNException {
294 T pkt = getPacketForWrite(cloned);
300 * Derive the source port number from the packet.
302 * @return A short integer value which represents the source port number.
304 protected abstract short getRawSourcePort();
307 * Derive the destination port number from the packet.
309 * @return A short integer value which represents the destination port
312 protected abstract short getRawDestinationPort();
315 * Set the source port number to the given packet.
317 * @param pkt A {@link Packet} instance.
318 * @param port A short integer value which indicates the source port.
320 protected abstract void setRawSourcePort(T pkt, short port);
323 * Set the destination port number to the given packet.
325 * @param pkt A {@link Packet} instance.
326 * @param port A short integer value which indicates the destination port.
328 protected abstract void setRawDestinationPort(T pkt, short port);
331 * Return a {@link Packet} instance to set modified values.
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.
339 protected abstract T getPacketForWrite(boolean doCopy) throws VTNException;
342 * Return the name of the protocol.
344 * @return The protocol name.
346 protected abstract String getProtocolName();
349 * Construct flow match fields.
351 * @param src A {@link VTNPortRange} instance which specifies the
353 * @param dst A {@link VTNPortRange} instance which specifies the
355 * @return A {@link VTNLayer4PortMatch} instance.
356 * @throws RpcException Invalid value is specified.
358 protected abstract VTNLayer4PortMatch createMatch(VTNPortRange src,
363 * Return a flow match type corresponding to the source port.
365 * @return A {@link FlowMatchType} instance.
367 public abstract FlowMatchType getSourceMatchType();
370 * Return a flow match type corresponding to the destination port.
372 * @return A {@link FlowMatchType} instance.
374 public abstract FlowMatchType getDestinationMatchType();
377 * Return a {@link Values} instance that keeps current values for
378 * protocol header fields.
380 * @return A {@link Values} instance.
382 private Values getValues() {
383 return (modifiedValues == null) ? values : modifiedValues;
387 * Return a {@link Values} instance that keeps protocol header field values
390 * @return A {@link Values} instance.
392 private Values getModifiedValues() {
393 if (modifiedValues == null) {
395 modifiedValues = values.clone();
398 return modifiedValues;
407 public final boolean commit(PacketContext pctx) throws VTNException {
410 if (modifiedValues != null) {
411 // At least one flow action that modifies TCP or UDP header is
413 pctx.addMatchField(FlowMatchType.DL_TYPE);
414 pctx.addMatchField(FlowMatchType.IP_PROTO);
416 int src = modifiedValues.getSourcePort();
417 if (values.getSourcePort() != src) {
418 // Source port was modified.
419 pkt = getPacketForWrite();
420 setRawSourcePort(pkt, (short)src);
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);
429 int dst = modifiedValues.getDestinationPort();
430 if (values.getDestinationPort() != dst) {
431 // Destination port was modified.
433 pkt = getPacketForWrite();
435 setRawDestinationPort(pkt, (short)dst);
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);
452 public final PortProtoPacket clone() {
454 PortProtoPacket p = (PortProtoPacket)super.clone();
456 p.values = v.clone();
458 v = p.modifiedValues;
460 p.modifiedValues = v.clone();
465 } catch (CloneNotSupportedException e) {
466 // This should never happen.
467 throw new IllegalStateException("clone() failed", e);
477 public final VTNLayer4PortMatch createMatch(Set<FlowMatchType> fields)
478 throws RpcException {
482 VTNPortRange src = (fields.contains(getSourceMatchType()))
483 ? new VTNPortRange(v.getSourcePort())
485 VTNPortRange dst = (fields.contains(getDestinationMatchType()))
486 ? new VTNPortRange(v.getDestinationPort())
489 return createMatch(src, dst);
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);
513 public final void setSourcePort(int port) {
514 Values v = getModifiedValues();
515 v.setSourcePort(port);
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);
537 public final void setDestinationPort(int port) {
538 Values v = getModifiedValues();
539 v.setDestinationPort(port);
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(']');