Rusty Eddy
Committed by Gerrit Code Review

Improvided PIM Hello option handling by creating the

PIMHelloOption class.  Also fixed a typo for pim regsiter
stop message type.

Change-Id: Iff06ce7d2746ebc34811205f4c4a4d4784e2740c
......@@ -33,7 +33,7 @@ public class PIM extends BasePacket {
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_REGISTER_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;
......
......@@ -19,88 +19,41 @@ 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 java.util.HashMap;
import java.util.Map;
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;
}
private Map<Short, PIMHelloOption> options = new HashMap<>();
/**
* Add the hello priority.
*
* @param priority default is 1, the higher the better
* Create a PIM Hello packet with the most common hello options and default
* values. The values of any options can be easily changed by modifying the value of
* the option with the desired change.
*/
public void addPriority(int priority) {
this.priority = priority;
this.priorityPresent = true;
public void createDefaultOptions() {
options.put(PIMHelloOption.OPT_HOLDTIME, new PIMHelloOption(PIMHelloOption.OPT_HOLDTIME));
options.put(PIMHelloOption.OPT_PRIORITY, new PIMHelloOption(PIMHelloOption.OPT_PRIORITY));
options.put(PIMHelloOption.OPT_GENID, new PIMHelloOption(PIMHelloOption.OPT_GENID));
}
/**
* Add a Gen ID.
* Add a PIM Hello option to this hello message. Note
*
* @param genid a random generated number, changes only after reset.
* @param opt the PIM Hello option we are adding
*/
public void addGenId(int genid) {
if (genid == 0) {
this.addGenId();
} else {
this.genid = genid;
}
public void addOption(PIMHelloOption opt) {
this.options.put(opt.getOptType(), opt);
}
/**
* Add the genid. Let this function figure out the number.
*/
public void addGenId() {
Random rand = new Random();
this.genid = rand.nextInt();
public Map<Short, PIMHelloOption> getOptions() {
return this.options;
}
/**
......@@ -111,68 +64,54 @@ public class PIMHello extends BasePacket {
*/
@Override
public byte[] serialize() {
int totalLen = 0;
// 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);
// Since we are likely to only have 3-4 options, go head and walk the
// hashmap twice, once to calculate the space needed to allocate a
// buffer, the second time serialize the options into the buffer. This
// saves us from allocating an over sized buffer the re-allocating and
// copying.
for (Short optType : options.keySet()) {
PIMHelloOption opt = options.get(optType);
totalLen += PIMHelloOption.MINIMUM_OPTION_LEN_BYTES + opt.getOptLength();
}
// Add the genid
bb.putShort((short) Option.GENID.optType);
bb.putShort((short) Option.GENID.optLen);
bb.putInt(this.genid);
byte[] data = new byte[totalLen];
ByteBuffer bb = ByteBuffer.wrap(data);
// Add the holdtime
bb.putShort((short) Option.HOLDTIME.optType);
bb.putShort((short) Option.HOLDTIME.optLen);
bb.putShort((short) this.holdtime);
// Now serialize the data.
for (Short optType : options.keySet()) {
PIMHelloOption opt = options.get(optType);
bb.put(opt.serialize());
}
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) {
//
// TODO: throw an expection?
return null;
}
/**
* Deserialize this hello message.
*
* @return a deserialized hello message.
* @return a deserialized hello message
*/
public static Deserializer<PIMHello> deserializer() {
return (data, offset, length) -> {
checkInput(data, offset, length, MINIMUM_OPTION_LEN_BYTES);
checkInput(data, offset, length, PIMHelloOption.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());
}
PIMHelloOption opt = PIMHelloOption.deserialize(bb);
hello.addOption(opt);
}
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.DeserializationException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import static org.onlab.packet.PacketUtils.checkBufferLength;
import static org.onlab.packet.PacketUtils.checkInput;
public class PIMHelloOption {
/**
* PIM Option types.
*/
public static final short OPT_HOLDTIME = 1;
public static final short OPT_PRUNEDELAY = 2;
public static final short OPT_PRIORITY = 19;
public static final short OPT_GENID = 20;
public static final short OPT_ADDRLIST = 24;
public static final short DEFAULT_HOLDTIME = 105;
public static final int DEFAULT_PRUNEDELAY = 2000; // 2,000 ms
public static final int DEFAULT_PRIORITY = 1;
public static final int DEFAULT_GENID = 0;
public static final int MINIMUM_OPTION_LEN_BYTES = 4;
// Values for this particular hello option.
private short optType;
private short optLength;
private byte[] optValue;
public PIMHelloOption() {
}
/**
* Set a PIM Hello option by type. The length and default value of the
* type will be auto filled in by default.
*
* @param type hello option type
*/
public PIMHelloOption(short type) {
this.optType = type;
switch (type) {
case OPT_HOLDTIME:
this.optLength = 2;
this.optValue = new byte[optLength];
ByteBuffer.wrap(this.optValue).putShort(PIMHelloOption.DEFAULT_HOLDTIME);
break;
case OPT_PRUNEDELAY:
this.optLength = 4;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_PRUNEDELAY);
break;
case OPT_PRIORITY:
this.optLength = 4;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_PRIORITY);
break;
case OPT_GENID:
this.optLength = 4;
this.optValue = new byte[this.optLength];
ByteBuffer.wrap(this.optValue).putInt(PIMHelloOption.DEFAULT_GENID);
break;
case OPT_ADDRLIST:
this.optLength = 0; // We don't know what the length will be yet.
this.optValue = null;
default:
//log.error("Unkown option type: " + type + "\n" );
return;
}
}
public void setOptType(short type) {
this.optType = type;
}
public short getOptType() {
return this.optType;
}
public void setOptLength(short len) {
this.optLength = len;
}
public short getOptLength() {
return this.optLength;
}
public void setValue(ByteBuffer bb) throws DeserializationException {
this.optValue = new byte[this.optLength];
bb.get(this.optValue, 0, this.optLength);
}
public byte[] getValue() {
return this.optValue;
}
public static PIMHelloOption deserialize(ByteBuffer bb) throws DeserializationException {
checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), 4);
PIMHelloOption opt = new PIMHelloOption();
opt.setOptType(bb.getShort());
opt.setOptLength(bb.getShort());
checkBufferLength(bb.limit(), bb.position(), opt.getOptLength());
opt.setValue(bb);
return opt;
}
public byte [] serialize() {
int len = 4 + this.optLength;
ByteBuffer bb = ByteBuffer.allocate(len);
bb.putShort(this.optType);
bb.putShort(this.optLength);
bb.put(this.optValue);
return bb.array();
}
public String toString() {
return MessageFormat.format("Type: {0}, len: {1} value: {2}", this.optType, this.optLength,
(this.optValue == null) ? "null" : this.optValue.toString());
}
}
......@@ -46,6 +46,11 @@ public final class PIMTest {
private PIM pimJoinPrune;
private PIMJoinPrune joinPrune;
/**
* Create PIM Hello and Join/Prune packets to be used in testing.
*
* @throws Exception if packet creation fails
*/
@Before
public void setUp() throws Exception {
......@@ -56,9 +61,7 @@ public final class PIMTest {
pimHello.setChecksum((short) 0);
hello = new PIMHello();
hello.addHoldtime(0xd2);
hello.addPriority(44);
hello.addGenId(0xf00d);
hello.createDefaultOptions();
pimHello.setPayload(hello);
hello.setParent(pimHello);
......@@ -81,20 +84,32 @@ public final class PIMTest {
deserializer = PIM.deserializer();
}
/**
* Make sure our deserializer throws an exception if we recieve bad input.
*
* @throws Exception if we are given bad input.
*/
@Test
public void testDerserializeBadInput() throws Exception {
public void testDeserializeBadInput() throws Exception {
PacketTestUtils.testDeserializeBadInput(deserializer);
}
/**
* Verify we throw an exception if we receive a truncated Join/Prune message.
*
* @throws Exception if we receive a truncated Join/Prune message.
*/
@Test
public void testDeserializeTruncated() throws Exception {
//byte [] bits = pimHello.serialize();
//PacketTestUtils.testDeserializeTruncated(deserializer, bits);
byte [] bits = pimJoinPrune.serialize();
PacketTestUtils.testDeserializeTruncated(deserializer, bits);
}
/**
* Verify that we correctly deserialize hello messages.
*
* @throws Exception if our input is bad or truncated.
*/
@Test
public void testDeserializeHello() throws Exception {
byte [] data = pimHello.serialize();
......@@ -102,6 +117,11 @@ public final class PIMTest {
assertTrue(pim.equals(pimHello));
}
/**
* Verify that we correctly deserialize Join/Prune messages.
*
* @throws Exception if our input is bad or truncated.
*/
@Test
public void testDeserializeJoinPrune() throws Exception {
byte [] data = pimJoinPrune.serialize();
......