Rusty Eddy
Committed by Gerrit Code Review

Added packet handling functions for PIM, Specifically

PIM Hello and PIM Join/Prune messages along with
respective PIM encoded address types

Change-Id: Iaef2e3581e27fa910ad355043bcb3e175238706a
......@@ -35,6 +35,7 @@ public class IPv4 extends BasePacket {
public static final byte PROTOCOL_IGMP = 0x2;
public static final byte PROTOCOL_TCP = 0x6;
public static final byte PROTOCOL_UDP = 0x11;
public static final byte PROTOCOL_PIM = 0x67;
public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
new HashMap<>();
......@@ -43,6 +44,7 @@ public class IPv4 extends BasePacket {
IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_IGMP, IGMP.deserializer());
IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_TCP, TCP.deserializer());
IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_UDP, UDP.deserializer());
IPv4.PROTOCOL_DESERIALIZER_MAP.put(IPv4.PROTOCOL_PIM, PIM.deserializer());
}
private static final byte DSCP_MASK = 0x3f;
......
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet;
import org.onlab.packet.pim.PIMHello;
import org.onlab.packet.pim.PIMJoinPrune;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import static org.onlab.packet.PacketUtils.checkInput;
/**
* Implements PIM control packet format.
*/
public class PIM extends BasePacket {
public static final IpAddress PIM_ADDRESS = IpAddress.valueOf("224.0.0.13");
public static final byte TYPE_HELLO = 0x00;
public static final byte TYPE_REGISTER = 0x01;
public static final byte TYPE_REQUEST_STOP = 0x02;
public static final byte TYPE_JOIN_PRUNE_REQUEST = 0x03;
public static final byte TYPE_BOOTSTRAP = 0x04;
public static final byte TYPE_ASSERT = 0x05;
public static final byte TYPE_GRAFT = 0x06;
public static final byte TYPE_GRAFT_ACK = 0x07;
public static final byte TYPE_CANDIDATE_RP_ADV = 0x08;
public static final int PIM_HEADER_LEN = 4;
public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
new HashMap<>();
static {
PIM.PROTOCOL_DESERIALIZER_MAP.put(PIM.TYPE_HELLO, PIMHello.deserializer());
PIM.PROTOCOL_DESERIALIZER_MAP.put(PIM.TYPE_JOIN_PRUNE_REQUEST, PIMJoinPrune.deserializer());
}
/*
* PIM Header fields
*/
protected byte version;
protected byte type;
protected byte reserved;
protected short checksum;
/**
* Default constructor.
*/
public PIM() {
super();
this.version = 2;
this.reserved = 0;
}
/**
* Return the PIM message type.
*
* @return the pimMsgType
*/
public byte getPimMsgType() {
return this.type;
}
/**
* Set the PIM message type. Currently PIMJoinPrune and PIMHello are
* supported.
*
* @param type PIM message type
* @return PIM Header
*/
public PIM setPIMType(final byte type) {
this.type = type;
return this;
}
/**
* Get the version of PIM.
*
* @return the PIM version. Must be 2.
*/
public byte getVersion() {
return version;
}
/**
* Set the PIM version type. Should not change from 2.
*
* @param version
*/
public void setVersion(byte version) {
this.version = version;
}
/**
* Get the reserved field.
*
* @return the reserved field. Must be ignored.
*/
public byte getReserved() {
return reserved;
}
/**
* Set the reserved field.
*
* @param reserved should be 0
*/
public void setReserved(byte reserved) {
this.reserved = reserved;
}
/**
* Get the checksum of this packet.
*
* @return the checksum
*/
public short getChecksum() {
return checksum;
}
/**
* Set the checksum.
*
* @param checksum the checksum
*/
public void setChecksum(short checksum) {
this.checksum = checksum;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 5807;
int result = super.hashCode();
result = prime * result + this.type;
result = prime * result + this.version;
result = prime * result + this.checksum;
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof PIM)) {
return false;
}
final PIM other = (PIM) obj;
if (this.type != other.type) {
return false;
}
if (this.version != other.version) {
return false;
}
if (this.checksum != other.checksum) {
return false;
}
return true;
}
/**
* Serializes the packet. Will compute and set the following fields if they
* are set to specific values at the time serialize is called: -checksum : 0
* -length : 0
*
* @return will return the serialized packet
*/
@Override
public byte[] serialize() {
int length = 4;
byte[] payloadData = null;
if (this.payload != null) {
this.payload.setParent(this);
payloadData = this.payload.serialize();
length += payloadData.length;
}
final byte[] data = new byte[length];
final ByteBuffer bb = ByteBuffer.wrap(data);
bb.put((byte) ((this.version & 0xf) << 4 | this.type & 0xf));
bb.put(this.reserved);
bb.putShort(this.checksum);
if (payloadData != null) {
bb.put(payloadData);
}
if (this.parent != null && this.parent instanceof PIM) {
((PIM) this.parent).setPIMType(TYPE_JOIN_PRUNE_REQUEST);
}
// compute checksum if needed
if (this.checksum == 0) {
bb.rewind();
int accumulation = 0;
for (int i = 0; i < length / 2; ++i) {
accumulation += 0xffff & bb.getShort();
}
// pad to an even number of shorts
if (length % 2 > 0) {
accumulation += (bb.get() & 0xff) << 8;
}
accumulation = (accumulation >> 16 & 0xffff)
+ (accumulation & 0xffff);
this.checksum = (short) (~accumulation & 0xffff);
bb.putShort(2, this.checksum);
}
return data;
}
/**
* Deserialize the PIM packet.
*
* @param data bytes to deserialize.
* @param offset offset to start deserializing from
* @param length length of the data to deserialize
*
* @return the deserialized PIM packet.
*/
@Override
public IPacket deserialize(final byte[] data, final int offset,
final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
this.type = bb.get();
this.version = bb.get();
this.checksum = bb.getShort();
//this.payload = new Data();
this.payload = this.payload.deserialize(data, bb.position(), bb.limit() - bb.position());
this.payload.setParent(this);
return this;
}
/**
* Deserializer function for IPv4 packets.
*
* @return deserializer function
*/
public static Deserializer<PIM> deserializer() {
return (data, offset, length) -> {
checkInput(data, offset, length, PIM_HEADER_LEN);
PIM pim = new PIM();
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
byte versionByte = bb.get();
pim.version = (byte) (versionByte >> 4 & 0xf);
pim.setPIMType((byte) (versionByte & 0xf));
pim.reserved = bb.get();
pim.checksum = bb.getShort();
Deserializer<? extends IPacket> deserializer;
if (PIM.PROTOCOL_DESERIALIZER_MAP.containsKey(pim.getPimMsgType())) {
deserializer = PIM.PROTOCOL_DESERIALIZER_MAP.get(pim.getPimMsgType());
} else {
deserializer = Data.deserializer();
}
pim.payload = deserializer.deserialize(data, bb.position(), bb.limit() - bb.position());
pim.payload.setParent(pim);
return pim;
};
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet.pim;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.Ip6Address;
import java.nio.ByteBuffer;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMAddrGroup {
private byte family;
private byte encType;
private byte reserved;
private boolean bBit;
private boolean zBit;
private byte masklen;
IpAddress addr;
public static final int ENC_GROUP_IPV4_BYTE_LENGTH = 4 + Ip4Address.BYTE_LENGTH;
public static final int ENC_GROUP_IPV6_BYTE_LENGTH = 4 + Ip6Address.BYTE_LENGTH;
/**
* PIM Encoded Group Address.
*/
public PIMAddrGroup() {
this.family = 4;
this.encType = 0;
this.reserved = 0;
this.bBit = false;
this.zBit = false;
}
/**
* PIM Encoded Source Address.
*
* @param addr IPv4 or IPv6
*/
public PIMAddrGroup(String addr) {
this.setAddr(addr);
}
/**
* PIM Encoded Group Address.
*
* @param gpfx PIM encoded group address.
*/
public PIMAddrGroup(IpPrefix gpfx) {
this.setAddr(gpfx);
}
/**
* PIM encoded source address.
*
* @param addr IPv4 or IPv6
*/
public void setAddr(String addr) {
setAddr(IpPrefix.valueOf(addr));
}
/**
* Set the encoded source address.
*
* @param pfx
*/
public void setAddr(IpPrefix pfx) {
this.addr = pfx.address();
this.masklen = (byte) pfx.prefixLength();
this.family = (byte) ((this.addr.isIp4()) ? 4 : 6);
}
/**
* Get the IP family of this address: 4 or 6.
*
* @return the IP address family
*/
public int getFamily() {
return this.family;
}
/**
* Get the address of this encoded address.
*
* @return source address
*/
public IpAddress getAddr() {
return this.addr;
}
/**
* Get the masklen of the group address.
*
* @return the masklen
*/
public int getMasklen() {
return this.masklen;
}
/**
* Return the z bit for admin scoping. Only used for the Bootstrap router.
*
* @return true or false
*/
public boolean getZBit() {
return this.zBit;
}
/**
* Return the bBit. Used to indicate this is a bidir
*
* @return return true or false.
*/
public boolean getBBit() {
return this.bBit;
}
/**
* The size in bytes of a serialized address.
*
* @return the number of bytes when serialized
*/
public int getByteSize() {
int size = 4;
size += addr.isIp4() ? 4 : 16;
return size;
}
/**
* Serialize this group address.
*
* @return the serialized address in a buffer.
*/
public byte[] serialize() {
int len = getByteSize();
final byte[] data = new byte[len];
final ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(this.family);
bb.put(this.encType);
// Todo: technically we should be setting the B and Z bits, but we'll never use them.
bb.put(reserved);
bb.put(this.masklen);
bb.put(this.addr.toOctets());
return data;
}
/**
* Deserialze from a ByteBuffer.
*
* @param bb the ByteBuffer
* @return an encoded PIM group address.
*/
public PIMAddrGroup deserialize(ByteBuffer bb) throws DeserializationException {
/*
* We need to verify that we have enough buffer space. First we'll assume that
* we are decoding an IPv4 address. After we read the first by (address family),
* we'll determine if we actually need more buffer space for an IPv6 address.
*/
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_GROUP_IPV4_BYTE_LENGTH);
this.family = bb.get();
if (family != 4 && family != 6) {
throw new DeserializationException("Illegal IP version number: " + family + "\n");
} else if (family == 6) {
// Check for one less by since we have already read the first byte of the packet.
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_GROUP_IPV6_BYTE_LENGTH - 1);
}
this.encType = bb.get();
this.reserved = bb.get();
if ((this.reserved & 0x80) != 0) {
this.bBit = true;
}
if ((this.reserved & 0x01) != 0) {
this.zBit = true;
}
// Remove the z and b bits from reserved
this.reserved |= 0x7d;
this.masklen = bb.get();
if (this.family == 4) {
this.addr = IpAddress.valueOf(bb.getInt());
} else if (this.family == 6) {
this.addr = Ip6Address.valueOf(bb.array(), 2);
}
return this;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 2521;
int result = super.hashCode();
result = prime * result + this.family;
result = prime * result + this.encType;
result = prime * result + this.reserved;
result = prime * result + this.masklen;
result = prime * result + this.addr.hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals()
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PIMAddrGroup)) {
return false;
}
final PIMAddrGroup other = (PIMAddrGroup) obj;
if (this.family != this.family) {
return false;
}
if (this.encType != other.encType) {
return false;
}
if (!this.addr.equals(other.addr)) {
return false;
}
return true;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet.pim;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.Ip6Address;
import java.nio.ByteBuffer;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMAddrSource {
private byte family;
private byte encType;
private byte reserved;
private boolean sBit;
private boolean wBit;
private boolean rBit;
private byte masklen;
IpAddress addr;
public static final int ENC_SOURCE_IPV4_BYTE_LENGTH = 4 + Ip4Address.BYTE_LENGTH;
public static final int ENC_SOURCE_IPV6_BYTE_LENGTH = 4 + Ip6Address.BYTE_LENGTH;
/**
* PIM Encoded Source Address.
*
* @param addr IPv4 or IPv6
*/
public PIMAddrSource(String addr) {
this.init();
this.setAddr(addr);
}
/**
* PIM Encoded Source Address.
*
* @param spfx IPv4 or IPv6
*/
public PIMAddrSource(IpPrefix spfx) {
this.init();
this.setAddr(spfx);
}
/**
* PIM Encoded Group Address.
*/
public PIMAddrSource() {
this.init();
}
private void init() {
this.family = 4;
this.encType = 0;
this.reserved = 0;
this.sBit = true;
this.wBit = false;
this.rBit = false;
}
/**
* PIM Encoded Source Address.
*
* @param addr IPv4 or IPv6
*/
public void setAddr(String addr) {
IpPrefix spfx = IpPrefix.valueOf(addr);
setAddr(spfx);
}
/**
* PIM Encoded Source Address.
*
* @param spfx IPv4 or IPv6 address prefix
*/
public void setAddr(IpPrefix spfx) {
this.addr = spfx.address();
this.masklen = (byte) spfx.prefixLength();
this.family = (byte) ((this.addr.isIp4()) ? 4 : 6);
}
/**
* Get the IP family of this address: 4 or 6.
*
* @return the IP address family
*/
public byte getFamily() {
return this.family;
}
/**
* Get the address of this encoded address.
*
* @return source address
*/
public IpAddress getAddr() {
return this.addr;
}
/**
* Get the masklen of the group address.
*
* @return the masklen
*/
public int getMasklen() {
return this.masklen;
}
/**
* Return the sparse bit.
*
* @return true or false
*/
public boolean getSBit() {
return this.sBit;
}
/**
* Return the wBit, used in Join/Prune messages.
*
* @return return true or false.
*/
public boolean getWBit() {
return this.wBit;
}
/**
* Return the rBit. Used by Rendezvous Point.
*
* @return the rBit.
*/
public boolean getRBit() {
return this.rBit;
}
/**
* The size in bytes of a serialized address.
*
* @return the number of bytes when serialized
*/
public int getByteSize() {
int size = 4;
size += addr.isIp4() ? 4 : 16;
return size;
}
public byte[] serialize() {
int len = addr.isIp4() ? ENC_SOURCE_IPV4_BYTE_LENGTH : ENC_SOURCE_IPV6_BYTE_LENGTH;
final byte[] data = new byte[len];
final ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(this.family);
bb.put(this.encType);
// Todo: technically we should be setting the B and Z bits, but we'll never use them.
byte mask = 0x0;
if (this.sBit) {
this.reserved |= 0x4;
}
if (this.wBit) {
this.reserved |= 0x2;
}
if (this.rBit) {
this.reserved |= 0x1;
}
bb.put(reserved);
bb.put(this.masklen);
bb.put(this.addr.toOctets());
return data;
}
public PIMAddrSource deserialize(byte[] data, int offset, int length) throws DeserializationException {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
return deserialize(bb);
}
public PIMAddrSource deserialize(ByteBuffer bb) throws DeserializationException {
/*
* We need to verify that we have enough buffer space. First we'll assume that
* we are decoding an IPv4 address. After we read the first by (address family),
* we'll determine if we actually need more buffer space for an IPv6 address.
*/
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_SOURCE_IPV4_BYTE_LENGTH);
this.family = bb.get();
if (family != 4 && family != 6) {
throw new DeserializationException("Illegal IP version number: " + family + "\n");
} else if (family == 6) {
// Check for one less by since we have already read the first byte of the packet.
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_SOURCE_IPV6_BYTE_LENGTH - 1);
}
this.encType = bb.get();
this.reserved = bb.get();
if ((this.reserved & 0x01) != 0) {
this.rBit = true;
}
if ((this.reserved & 0x02) != 0) {
this.wBit = true;
}
if ((this.reserved & 0x4) != 0) {
this.sBit = true;
}
// Remove the s, reserved
this.reserved &= 0xf8;
this.masklen = bb.get();
if (this.family == 4) {
this.addr = IpAddress.valueOf(bb.getInt());
} else if (this.family == 6) {
this.addr = Ip6Address.valueOf(bb.array(), 2);
}
return this;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 2521;
int result = super.hashCode();
result = prime * result + this.family;
result = prime * result + this.encType;
result = prime * result + this.reserved;
result = prime * result + this.masklen;
result = prime * result + this.addr.hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PIMAddrSource)) {
return false;
}
final PIMAddrSource other = (PIMAddrSource) obj;
if (this.family != this.family) {
return false;
}
if (this.encType != other.encType) {
return false;
}
if (!this.addr.equals(other.addr)) {
return false;
}
return true;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet.pim;
import org.onlab.packet.DeserializationException;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.Ip6Address;
import java.nio.ByteBuffer;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMAddrUnicast {
private byte family;
private byte encType;
IpAddress addr;
public static final int ENC_UNICAST_IPV4_BYTE_LENGTH = 2 + Ip4Address.BYTE_LENGTH;
public static final int ENC_UNICAST_IPV6_BYTE_LENGTH = 2 + Ip6Address.BYTE_LENGTH;
/**
* PIM Encoded Source Address.
*/
public PIMAddrUnicast() {
this.family = 4;
this.encType = 0;
}
/**
* PIM Encoded Source Address.
*
* @param addr IPv4 or IPv6
*/
public PIMAddrUnicast(String addr) {
this.addr = IpAddress.valueOf(addr);
if (this.addr.isIp4()) {
this.family = 4;
} else {
this.family = 6;
}
this.encType = 0;
}
/**
* PIM Encoded Source Address.
*
* @param addr IPv4 or IPv6
*/
public void setAddr(IpAddress addr) {
this.addr = addr;
if (this.addr.isIp4()) {
this.family = 4;
} else {
this.family = 6;
}
}
/**
* Get the address of this encoded address.
*
* @return source address
*/
public IpAddress getAddr() {
return this.addr;
}
/**
* Get the IP family of this address: 4 or 6.
*
* @return the IP address family
*/
public int getFamily() {
return this.family;
}
/**
* The size in bytes of a serialized address.
*
* @return the number of bytes when serialized
*/
public int getByteSize() {
int size = 2;
if (addr != null) {
size += addr.isIp4() ? 4 : 16;
} else {
size += 4;
}
return size;
}
public byte[] serialize() {
int len = getByteSize();
final byte[] data = new byte[len];
final ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(family);
bb.put(encType);
bb.put(addr.toOctets());
return data;
}
public PIMAddrUnicast deserialize(ByteBuffer bb) throws DeserializationException {
// Assume IPv4 for check length until we read the encoded family.
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_UNICAST_IPV4_BYTE_LENGTH);
this.family = bb.get();
// If we have IPv6 we need to ensure we have adequate buffer space.
if (this.family != 4 && this.family != 6) {
throw new DeserializationException("Invalid address family: " + this.family);
} else if (this.family == 6) {
// Subtract -1 from ENC_UNICAST_IPv6 BYTE_LENGTH because we read one byte for family previously.
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_UNICAST_IPV6_BYTE_LENGTH - 1);
}
this.encType = bb.get();
if (this.family == 4) {
this.addr = IpAddress.valueOf(bb.getInt());
} else if (this.family == 6) {
this.addr = Ip6Address.valueOf(bb.array(), 2);
}
return this;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 2521;
int result = super.hashCode();
result = prime * result + this.family;
result = prime * result + this.encType;
result = prime * result + this.addr.hashCode();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PIMAddrUnicast)) {
return false;
}
final PIMAddrUnicast other = (PIMAddrUnicast) obj;
if (this.family != this.family) {
return false;
}
if (this.encType != other.encType) {
return false;
}
if (!this.addr.equals(other.addr)) {
return false;
}
return true;
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet.pim;
import org.onlab.packet.BasePacket;
import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IpAddress;
import java.nio.ByteBuffer;
import java.util.Random;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMHello extends BasePacket {
private IpAddress nbrIpAddress;
private int holdtime = 105;
private int genid = 0;
private int priority = 1;
private boolean priorityPresent = false;
public static final int MINIMUM_OPTION_LEN_BYTES = 4;
/**
* PIM Option types.
*/
public enum Option {
HOLDTIME (1, 2),
PRUNEDELAY(2, 4),
PRIORITY (19, 4),
GENID (20, 4),
ADDRLIST (24, 0);
private final int optType;
private final int optLen;
Option(int ot, int ol) {
this.optType = ot;
this.optLen = ol;
}
public int optType() {
return this.optType;
}
public int optLen() {
return this.optLen;
}
}
/**
* Add the holdtime to the packet.
*
* @param holdtime the holdtime in seconds
*/
public void addHoldtime(int holdtime) {
this.holdtime = holdtime;
}
/**
* Add the hello priority.
*
* @param priority default is 1, the higher the better
*/
public void addPriority(int priority) {
this.priority = priority;
this.priorityPresent = true;
}
/**
* Add a Gen ID.
*
* @param genid a random generated number, changes only after reset.
*/
public void addGenId(int genid) {
if (genid == 0) {
this.addGenId();
} else {
this.genid = genid;
}
}
/**
* Add the genid. Let this function figure out the number.
*/
public void addGenId() {
Random rand = new Random();
this.genid = rand.nextInt();
}
/**
* Sets all payloads parent packet if applicable, then serializes this
* packet and all payloads.
*
* @return a byte[] containing this packet and payloads
*/
@Override
public byte[] serialize() {
// TODO: Figure out a better way to calculate buffer size
int size = Option.PRIORITY.optLen() + 4 +
Option.GENID.optLen() + 4 +
Option.HOLDTIME.optLen() + 4;
byte[] data = new byte[size]; // Come up with something better
ByteBuffer bb = ByteBuffer.wrap(data);
// Add the priority
bb.putShort((short) Option.PRIORITY.optType);
bb.putShort((short) Option.PRIORITY.optLen);
bb.putInt(this.priority);
// Add the genid
bb.putShort((short) Option.GENID.optType);
bb.putShort((short) Option.GENID.optLen);
bb.putInt(this.genid);
// Add the holdtime
bb.putShort((short) Option.HOLDTIME.optType);
bb.putShort((short) Option.HOLDTIME.optLen);
bb.putShort((short) this.holdtime);
return data;
}
/**
* XXX: This is deprecated, DO NOT USE, use the deserializer() function instead.
*/
// @Override
public IPacket deserialize(final byte[] data, final int offset,
final int length) {
//
return null;
}
/**
* Deserialize this hello message.
*
* @return a deserialized hello message.
*/
public static Deserializer<PIMHello> deserializer() {
return (data, offset, length) -> {
checkInput(data, offset, length, MINIMUM_OPTION_LEN_BYTES);
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
PIMHello hello = new PIMHello();
while (bb.hasRemaining()) {
int optType = bb.getShort();
int optLen = bb.getShort();
// Check that we have enough buffer for the next option.
checkInput(data, bb.position(), bb.limit() - bb.position(), optLen);
if (optType == Option.GENID.optType) {
hello.addGenId(bb.getInt());
} else if (optType == Option.PRIORITY.optType) {
hello.addPriority(bb.getInt());
} else if (optType == Option.HOLDTIME.optType) {
hello.addHoldtime((int) bb.getShort());
}
}
return hello;
};
}
}
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet.pim;
import org.onlab.packet.BasePacket;
import org.onlab.packet.Deserializer;
import org.onlab.packet.IPacket;
import org.onlab.packet.IpPrefix;
import java.nio.ByteBuffer;
import java.util.HashMap;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMJoinPrune extends BasePacket {
private PIMAddrUnicast upstreamAddr = new PIMAddrUnicast();
private short holdTime = (short) 0xffff;
private class JoinPruneGroup {
protected IpPrefix group;
protected HashMap<IpPrefix, IpPrefix> joins = new HashMap<>();
protected HashMap<IpPrefix, IpPrefix> prunes = new HashMap<>();
public JoinPruneGroup(IpPrefix grp) {
group = grp;
}
}
private HashMap<IpPrefix, JoinPruneGroup> joinPrunes = new HashMap<>();
/**
* Get the J/P hold time.
*
* @return specified in seconds.
*/
public short getHoldTime() {
return holdTime;
}
/**
* Set the J/P holdtime in seconds.
*
* @param holdTime return the holdtime.
*/
public void setHoldTime(short holdTime) {
this.holdTime = holdTime;
}
/**
* Get the upstreamAddr for this J/P request.
*
* @return the upstream address.
*/
public PIMAddrUnicast getUpstreamAddr() {
return upstreamAddr;
}
/**
* Set the upstream address of this PIM J/P request.
*
* @param upstr the PIM Upstream unicast address
*/
public void setUpstreamAddr(PIMAddrUnicast upstr) {
this.upstreamAddr = upstr;
}
/**
* Add the specified s,g to join field.
*
* @param saddr the source address of the route
* @param gaddr the group address of the route
* @param join true for a join, false for a prune.
*/
public void addJoinPrune(String saddr, String gaddr, boolean join) {
IpPrefix gpfx = IpPrefix.valueOf(gaddr);
IpPrefix spfx = IpPrefix.valueOf(saddr);
addJoinPrune(spfx, gpfx, join);
}
/**
* Add the specified S, G to the join field.
*
* @param spfx the source prefix of the route
* @param gpfx the group prefix of the route
* @param join true for join, false for prune
*/
public void addJoinPrune(IpPrefix spfx, IpPrefix gpfx, boolean join) {
JoinPruneGroup jpg = joinPrunes.get(gpfx);
if (jpg == null) {
jpg = new JoinPruneGroup(gpfx);
joinPrunes.put(gpfx, jpg);
}
HashMap<IpPrefix, IpPrefix> members = (join) ? jpg.joins : jpg.prunes;
if (members.get(spfx) == null) {
members.put(spfx, spfx);
}
}
/**
* Add a join given strings represending the source and group addresses.
*
* @param saddr source address
* @param gaddr group address
*/
public void addJoin(String saddr, String gaddr) {
this.addJoinPrune(saddr, gaddr, true);
}
/**
* Add a prune given strings represending the source and group addresses.
*
* @param saddr source address
* @param gaddr group address
*/
public void addPrune(String saddr, String gaddr) {
this.addJoinPrune(saddr, gaddr, false);
}
/**
* Sets all payloads parent packet if applicable, then serializes this
* packet and all payloads.
*
* @return a byte[] containing this packet and payloads
*/
@Override
public byte[] serialize() {
byte[] data = new byte[8096]; // Come up with something better
ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(upstreamAddr.serialize());
bb.put((byte) 0); // reserved
int ngrps = joinPrunes.size();
bb.put((byte) ngrps);
bb.putShort(this.holdTime);
// Walk the group list and input all groups
for (JoinPruneGroup jpg : joinPrunes.values()) {
PIMAddrGroup grp = new PIMAddrGroup(jpg.group);
bb.put(grp.serialize());
// put the number of joins and prunes
bb.putShort((short) jpg.joins.size());
bb.putShort((short) jpg.prunes.size());
// Set all of the joins
for (IpPrefix spfx : jpg.joins.values()) {
PIMAddrSource src = new PIMAddrSource(spfx);
bb.put(src.serialize());
}
// Set all of the prunes
for (IpPrefix spfx : jpg.prunes.values()) {
PIMAddrSource src = new PIMAddrSource(spfx);
bb.put(src.serialize());
}
}
int len = bb.position();
byte[] data2 = new byte[len];
bb = ByteBuffer.wrap(data2, 0, len);
bb.put(data, 0, len);
return data2;
}
// TODO: I suppose I really need to implement this?
@Override
public IPacket deserialize(final byte[] data, final int offset,
final int length) {
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
return this;
}
/**
* Return the J/P deserializer function.
*
* @return a function that will deserialize a J/P message.
*/
public static Deserializer<PIMJoinPrune> deserializer() {
return (data, offset, length) -> {
/*
* Delay buffer checks until we read enough of the packet to know how
* much data we will require. Each encoded address deserializer function
* will ensure there is enough data for that address.
*/
PIMJoinPrune jp = new PIMJoinPrune();
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
// We must get a PIM encoded unicast address
PIMAddrUnicast upstream = new PIMAddrUnicast();
upstream.deserialize(bb);
jp.setUpstreamAddr(upstream);
// Use this boolean to determine the buffer space we need according to address sizes
boolean ipv4 = upstream.getAddr().isIp4();
// We need at minimum 4 bytes for reserved(1), ngroups(1) & holdtime(2)
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), 4);
// get and skip the reserved byte
bb.get();
// Get the number of groups.
int ngroups = bb.get();
// Save the holdtime.
jp.setHoldTime(bb.getShort());
for (int i = 0; i < ngroups; i++) {
PIMAddrGroup grp = new PIMAddrGroup();
/*
* grp.deserialize will ensure the buffer has enough data to read the group address.
*/
grp.deserialize(bb);
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), 4);
int njoins = bb.getShort();
int nprunes = bb.getShort();
/*
* Now we'll verify we have enough buffer to read the next
* group of join and prune addresses for this group.
*/
int required = (njoins + nprunes) *
(ipv4 ? PIMAddrSource.ENC_SOURCE_IPV4_BYTE_LENGTH : PIMAddrSource.ENC_SOURCE_IPV6_BYTE_LENGTH);
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), required);
// Now iterate through the joins for this group
for (; njoins > 0; njoins--) {
PIMAddrSource src = new PIMAddrSource();
src.deserialize(bb);
jp.addJoinPrune(
src.getAddr().toIpPrefix(),
grp.getAddr().toIpPrefix(), true);
}
// Now iterate through the prunes for this group
for (; nprunes > 0; nprunes--) {
PIMAddrSource src = new PIMAddrSource();
src.deserialize(bb);
jp.addJoinPrune(
src.getAddr().toIpPrefix(),
grp.getAddr().toIpPrefix(), false);
}
}
return jp;
};
}
}
/*
* Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Utilities for managing PIM packets.
*/
package org.onlab.packet.pim;
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.pim.PIMAddrUnicast;
import org.onlab.packet.pim.PIMHello;
import org.onlab.packet.pim.PIMJoinPrune;
import static junit.framework.Assert.assertTrue;
public final class PIMTest {
public static final String SADDR = "10.2.1.2";
public static final String PIMADDR = "224.0.0.13";
public static final String PIMUADDR = "10.23.3.5";
public static final String SADDR1 = "10.1.1.1/32";
public static final String SADDR2 = "10.1.2.1/32";
public static final String GADDR1 = "232.1.1.1/32";
public static final String GADDR2 = "232.1.2.1/32";
public static final String CPSTR1 = "of:deadbeefball/8";
public static final String CPSTR2 = "of:deadbeefcafe/3";
public static final String CPSTR3 = "of:2badcafef00d/3";
private Deserializer<PIM> deserializer;
private PIM pimHello;
private PIMHello hello;
private PIM pimJoinPrune;
private PIMJoinPrune joinPrune;
@Before
public void setUp() throws Exception {
// Create a PIM Hello
pimHello = new PIM();
pimHello.setVersion((byte) 2);
pimHello.setPIMType((byte) PIM.TYPE_HELLO);
pimHello.setChecksum((short) 0);
hello = new PIMHello();
hello.addHoldtime(0xd2);
hello.addPriority(44);
hello.addGenId(0xf00d);
pimHello.setPayload(hello);
hello.setParent(pimHello);
// Create PIM Join Prune
pimJoinPrune = new PIM();
pimJoinPrune.setVersion((byte) 2);
pimJoinPrune.setPIMType((byte) PIM.TYPE_JOIN_PRUNE_REQUEST);
pimJoinPrune.setChecksum((short) 0);
joinPrune = new PIMJoinPrune();
joinPrune.setUpstreamAddr(new PIMAddrUnicast(SADDR));
joinPrune.addJoin(GADDR1, SADDR1);
joinPrune.addJoin(GADDR2, SADDR2);
joinPrune.addPrune(GADDR1, SADDR2);
joinPrune.addPrune(GADDR2, SADDR1);
pimJoinPrune.setPayload(joinPrune);
joinPrune.setParent(pimJoinPrune);
deserializer = PIM.deserializer();
}
@Test
public void testDerserializeBadInput() throws Exception {
PacketTestUtils.testDeserializeBadInput(deserializer);
}
@Test
public void testDeserializeTruncated() throws Exception {
//byte [] bits = pimHello.serialize();
//PacketTestUtils.testDeserializeTruncated(deserializer, bits);
byte [] bits = pimJoinPrune.serialize();
PacketTestUtils.testDeserializeTruncated(deserializer, bits);
}
@Test
public void testDeserializeHello() throws Exception {
byte [] data = pimHello.serialize();
PIM pim = deserializer.deserialize(data, 0, data.length);
assertTrue(pim.equals(pimHello));
}
@Test
public void testDeserializeJoinPrune() throws Exception {
byte [] data = pimJoinPrune.serialize();
PIM pim = deserializer.deserialize(data, 0, data.length);
assertTrue(pim.equals(pimJoinPrune));
}
}
\ No newline at end of file