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;
11 import java.net.InetAddress;
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;
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;
29 import org.opendaylight.controller.sal.packet.IPv4;
32 * {@code Inet4Packet} class implements a cache for an {@link IPv4} instance.
34 public final class Inet4Packet implements CachedPacket, InetHeader {
36 * A pseudo IP protocol number which indicates the IP protocol number is
39 private static final short PROTO_NONE = -1;
42 * A pseudo DSCP value which indicates the DSCP value is not specified.
44 private static final short DSCP_NONE = -1;
47 * Byte offset to the source address in a pseudo IPv4 header used for
48 * computing TCP/UDP checksum.
50 private static final int CKSUM_OFF_SRC = 0;
53 * Byte offset to the destination address in a pseudo IPv4 header used for
54 * computing TCP/UDP checksum.
56 private static final int CKSUM_OFF_DST = 4;
59 * Byte offset to the IP protocol number in a pseudo IPv4 header used for
60 * computing TCP/UDP checksum.
62 private static final int CKSUM_OFF_PROTO = 9;
65 * Byte offset to the payload length in a pseudo IPv4 header used for
66 * computing TCP/UDP checksum.
68 private static final int CKSUM_OFF_LEN = 10;
71 * The number of bytes in a pseudo IPv4 header used for computing
74 private static final int CKSUM_HEADER_SIZE = 12;
77 * An {@link IPv4} packet.
82 * The IP protocol number in the IPv4 packet.
84 private short protocol = PROTO_NONE;
87 * Cached values in IPv4 header.
89 private Values values = new Values();
92 * IPv4 header values to be set.
94 private Values modifiedValues;
97 * Set {@code true} if this instance is created by {@link #clone()}.
99 private boolean cloned;
102 * This class describes modifiable fields in IPv4 header.
104 private static final class Values implements Cloneable {
106 * The source IP address.
108 private Ip4Network sourceAddress;
111 * The destination IP address.
113 private Ip4Network destinationAddress;
116 * The DSCP field value in the IPv4 packet.
118 private short dscp = DSCP_NONE;
127 * Return the source IP address.
129 * @return An {@link Ip4Network} instance which represents the
131 * {@code null} is returned if not configured.
133 private Ip4Network getSourceAddress() {
134 return sourceAddress;
138 * Set the source IP address.
140 * @param addr An {@link Ip4Network} instance which represents the
141 * source IPv4 address.
143 private void setSourceAddress(Ip4Network addr) {
144 sourceAddress = addr;
148 * Return the destination IP address.
150 * @return An {@link Ip4Network} instance which represents the
151 * destination IP address.
152 * {@code null} is returned if not configured.
154 private Ip4Network getDestinationAddress() {
155 return destinationAddress;
159 * Set the destination IP address.
161 * @param addr An {@link Ip4Network} instance which represents the
162 * destination IPv4 address.
164 private void setDestinationAddress(Ip4Network addr) {
165 destinationAddress = addr;
169 * Return the DSCP field value.
171 * @return A short value which indicates the DSCP field value.
172 * {@link Inet4Packet#DSCP_NONE} is returned if not
175 private short getDscp() {
180 * Set the DSCP field value.
182 * @param value A short value which indicates the DSCP field value.
184 private void setDscp(short value) {
189 * Fetch all modifiable field values from the given packet.
192 * Field values already cached in this instance are preserved.
195 * @param ipv4 An {@link IPv4} instance.
197 private void fill(IPv4 ipv4) {
198 if (sourceAddress == null) {
199 sourceAddress = new Ip4Network(ipv4.getSourceAddress());
201 if (destinationAddress == null) {
203 new Ip4Network(ipv4.getDestinationAddress());
205 if (dscp == DSCP_NONE) {
206 dscp = ipv4.getDiffServ();
211 * Return a shallow copy of this instance.
213 * @return A shallow copy of this instance.
216 public Values clone() {
218 return (Values)super.clone();
219 } catch (CloneNotSupportedException e) {
220 // This should never happen.
221 throw new IllegalStateException("clone() failed", e);
227 * Construct a new instance.
229 * @param ipv4 An {@link IPv4} instance.
231 public Inet4Packet(IPv4 ipv4) {
236 * Determine whether the source or destination address is modified or not.
238 * @return {@code true} only if the source or destination address is
241 public boolean isAddressModified() {
242 if (modifiedValues == null) {
246 Ip4Network oldIp = values.getSourceAddress();
247 Ip4Network newIp = modifiedValues.getSourceAddress();
248 if (oldIp.getAddress() != newIp.getAddress()) {
252 oldIp = values.getDestinationAddress();
253 newIp = modifiedValues.getDestinationAddress();
254 return (oldIp.getAddress() != newIp.getAddress());
258 * Create a pseudo IPv4 header used for computing TCP/UDP checksum.
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.
264 public byte[] getHeaderForChecksum(byte proto, short len) {
265 byte[] header = new byte[CKSUM_HEADER_SIZE];
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);
278 * Construct match fields to test IP header in this packet.
281 * Note that this method creates match fields that matches the original
282 * packet. Any modification to the packet is ignored.
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.
290 public VTNInet4Match createMatch(Set<FlowMatchType> fields)
291 throws RpcException {
295 Ip4Network src = (fields.contains(FlowMatchType.IP_SRC))
296 ? v.getSourceAddress()
298 Ip4Network dst = (fields.contains(FlowMatchType.IP_DST))
299 ? v.getDestinationAddress()
301 Short proto = (fields.contains(FlowMatchType.IP_PROTO))
302 ? Short.valueOf(getProtocol())
304 Short dscp = (fields.contains(FlowMatchType.IP_DSCP))
305 ? Short.valueOf(v.getDscp())
308 return new VTNInet4Match(src, dst, proto, dscp);
312 * Return a {@link Values} instance that keeps current values for
313 * IPv4 header fields.
315 * @return A {@link Values} instance.
317 private Values getValues() {
318 return (modifiedValues == null) ? values : modifiedValues;
322 * Return a {@link Values} instance that keeps IPv4 header field values to
325 * @return A {@link Values} instance.
327 private Values getModifiedValues() {
328 if (modifiedValues == null) {
330 modifiedValues = values.clone();
333 return modifiedValues;
337 * Return an {@link IPv4} instance to set modified values.
339 * @return An {@link IPv4} instance.
340 * @throws VTNException
341 * Failed to copy the packet.
343 private IPv4 getPacketForWrite() throws VTNException {
345 // Create a copy of the original packet.
346 packet = MiscUtils.copy(packet, new IPv4());
356 * Return an {@link IPv4} instance configured in this instance.
359 * Note that modification to the IPv4 header is not applied to the
360 * returned until {@link #commit(PacketContext)} is called.
363 * @return An {@link IPv4} instance.
366 public IPv4 getPacket() {
374 public boolean commit(PacketContext pctx) throws VTNException {
377 if (modifiedValues != null) {
378 // At least one flow action that modifies IPv4 header is
380 pctx.addMatchField(FlowMatchType.DL_TYPE);
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);
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);
397 oldIp = values.getDestinationAddress();
398 newIp = modifiedValues.getDestinationAddress();
399 if (oldIp.getAddress() != newIp.getAddress()) {
400 // Destination address was modified.
401 InetAddress iaddr = newIp.getInetAddress();
403 ipv4 = getPacketForWrite();
405 ipv4.setDestinationAddress(iaddr);
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);
414 short dscp = modifiedValues.getDscp();
415 if (values.getDscp() != dscp) {
416 // DSCP field was modified.
418 ipv4 = getPacketForWrite();
420 ipv4.setDiffServ((byte)dscp);
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);
437 public Inet4Packet clone() {
439 Inet4Packet ip = (Inet4Packet)super.clone();
440 Values v = ip.values;
441 ip.values = v.clone();
443 v = ip.modifiedValues;
445 ip.modifiedValues = v.clone();
450 } catch (CloneNotSupportedException e) {
451 // This should never happen.
452 throw new IllegalStateException("clone() failed", e);
462 public Ip4Network getSourceAddress() {
463 Values v = getValues();
464 Ip4Network ipn = v.getSourceAddress();
466 ipn = new Ip4Network(packet.getSourceAddress());
467 v.setSourceAddress(ipn);
477 public boolean setSourceAddress(IpNetwork ipn) {
479 Ip4Network ip4 = Ip4Network.toIp4Address(ipn);
483 Values v = getModifiedValues();
484 v.setSourceAddress(ip4);
495 public Ip4Network getDestinationAddress() {
496 Values v = getValues();
497 Ip4Network ipn = v.getDestinationAddress();
499 ipn = new Ip4Network(packet.getDestinationAddress());
500 v.setDestinationAddress(ipn);
510 public boolean setDestinationAddress(IpNetwork ipn) {
512 Ip4Network ip4 = Ip4Network.toIp4Address(ipn);
516 Values v = getModifiedValues();
517 v.setDestinationAddress(ip4);
528 public short getProtocol() {
529 if (protocol == PROTO_NONE) {
530 protocol = (short)NumberUtils.getUnsigned(packet.getProtocol());
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());
555 public void setDscp(short dscp) {
556 Values v = getModifiedValues();
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(']');