All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onlab.packet.DHCP6 Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright 2017-present Open Networking Foundation
 *
 * 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 com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.onlab.packet.dhcp.Dhcp6ClientDataOption;
import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
import org.onlab.packet.dhcp.Dhcp6IaNaOption;
import org.onlab.packet.dhcp.Dhcp6IaTaOption;
import org.onlab.packet.dhcp.Dhcp6IaPdOption;
import org.onlab.packet.dhcp.Dhcp6LeaseQueryOption;
import org.onlab.packet.dhcp.Dhcp6Option;
import org.onlab.packet.dhcp.Dhcp6RelayOption;
import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Representation of an DHCPv6 Packet.
 * Base on RFC-3315.
 */
public class DHCP6 extends BasePacket {
    private static final int UNSIGNED_SHORT_MASK = 0xffff;
    // size of different field of option
    private static final int OPT_CODE_SIZE = 2;
    private static final int OPT_LEN_SIZE = 2;

    // default size of DHCPv6 payload (without options)
    private static final int DHCP6_DEFAULT_SIZE = 4;

    // default size of DHCPv6 relay message payload (without options)
    private static final int DHCP6_RELAY_MSG_SIZE = 34;
    private static final int IPV6_ADDR_LEN = 16;

    // masks & offsets for default DHCPv6 header
    private static final int MSG_TYPE_OFFSET = 24;
    private static final int TRANSACTION_ID_MASK = 0x00ffffff;

    // Relay message types
    public static final Set RELAY_MSG_TYPES =
            ImmutableSet.of(MsgType.RELAY_FORW.value,
                            MsgType.RELAY_REPL.value
            );
    public static final Set LEASEQUERY_MSG_TYPES =
            ImmutableSet.of(MsgType.LEASEQUERY.value,
                            MsgType.LEASEQUERY_REPLY.value
            );

    /**
     * DHCPv6 message type.
     */
    public enum MsgType {
        SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
        CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
        REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
        RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
        RELAY_FORW((byte) 12), RELAY_REPL((byte) 13), LEASEQUERY((byte) 14),
        LEASEQUERY_REPLY((byte) 15);

        protected byte value;
        MsgType(final byte value) {
            this.value = value;
        }
        public byte value() {
            return this.value;
        }
        public static MsgType getType(final int value) {
            switch (value) {
                case 1:
                    return SOLICIT;
                case 2:
                    return ADVERTISE;
                case 3:
                    return REQUEST;
                case 4:
                    return CONFIRM;
                case 5:
                    return RENEW;
                case 6:
                    return REBIND;
                case 7:
                    return REPLY;
                case 8:
                    return RELEASE;
                case 9:
                    return DECLINE;
                case 10:
                    return RECONFIGURE;
                case 11:
                    return INFORMATION_REQUEST;
                case 12:
                    return RELAY_FORW;
                case 13:
                    return RELAY_REPL;
                case 14:
                    return LEASEQUERY;
                case 15:
                    return LEASEQUERY_REPLY;
                default:
                    return null;
            }
        }
        public static String  getMsgTypeStr(final MsgType msgType) {
            switch (msgType) {
                case SOLICIT:
                    return "SOLICIT";
                case ADVERTISE:
                    return "ADVERTISE";
                case REQUEST:
                    return "REQUEST";
                case CONFIRM:
                    return "CONFIRM";
                case RENEW:
                    return "RENEW";
                case REBIND:
                    return "REBIND";
                case REPLY:
                    return "REPLY";
                case RELEASE:
                    return "RELEASE";
                case DECLINE:
                    return "DECLINE";
                case RECONFIGURE:
                    return "RECONFIGURE";
                case INFORMATION_REQUEST:
                    return "INFORMATION_REQUEST";
                case RELAY_FORW:
                    return "RELAY_FORW";
                case RELAY_REPL:
                    return "RELAY_REPL";
                default:
                    return "NULL";
            }
        }
    }

    /**
     * DHCPv6 option code.
     */
    public enum OptionCode {
        CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
        IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
        RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
        STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
        VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
        RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20), IA_PD((short) 25), IAPREFIX((short) 26),
        SUBSCRIBER_ID((short) 38), OPTION_ERO((short) 43), LEASE_QUERY((short) 44),
        CLIENT_DATA((short) 45),  CLIENT_LT((short) 48);

        protected short value;
        OptionCode(final short value) {
            this.value = value;
        }
        public short value() {
            return this.value;
        }
    }

    private static final Map> OPT_DESERIALIZERS =
            ImmutableMap.>builder()
                            .put(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer())
                            .put(OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer())
                            .put(OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer())
                            .put(OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer())
                            .put(OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer())
                            .put(OptionCode.IA_PD.value, Dhcp6IaPdOption.deserializer())
                            .put(OptionCode.INTERFACE_ID.value, Dhcp6InterfaceIdOption.deserializer())
                            .put(OptionCode.LEASE_QUERY.value, Dhcp6LeaseQueryOption.deserializer())
                            .put(OptionCode.CLIENT_DATA.value, Dhcp6ClientDataOption.deserializer())
                    .build();

    // general field
    private byte msgType; // 1 byte
    private List options;

    // non-relay field
    private int transactionId; // 3 bytes

    // relay field
    private byte hopCount; // 1 byte
    private byte[] linkAddress; // 16 bytes
    private byte[] peerAddress; // 16 bytes

    /**
     * Creates new DHCPv6 object.
     */
    public DHCP6() {
        options = Lists.newArrayList();
    }

    @Override
    public byte[] serialize() {
        int payloadLength = options.stream()
                .mapToInt(Dhcp6Option::getLength)
                .sum();

        // 2 bytes code and 2 bytes length
        payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);

        if (RELAY_MSG_TYPES.contains(msgType)) {
            payloadLength += DHCP6_RELAY_MSG_SIZE;
        } else {
            payloadLength += DHCP6_DEFAULT_SIZE;
        }

        ByteBuffer bb = ByteBuffer.allocate(payloadLength);

        if (RELAY_MSG_TYPES.contains(msgType)) {
            bb.put(msgType);
            bb.put(hopCount);
            bb.put(linkAddress);
            bb.put(peerAddress);
        } else {
            int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
            bb.putInt(defaultHeader);
        }

        // serialize options
        options.forEach(option -> {
            bb.put(option.serialize());
        });

        return bb.array();
    }

    /**
     * Returns a deserializer for DHCPv6.
     *
     * @return the deserializer for DHCPv6
     */
    public static Deserializer deserializer() {
        return (data, offset, length) -> {
            DHCP6 dhcp6 = new DHCP6();

            checkNotNull(data);

            if (offset < 0 || length < 0 ||
                    length > data.length || offset >= data.length ||
                    offset + length > data.length) {
                throw new DeserializationException("Illegal offset or length");
            }

            final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
            if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
                throw new DeserializationException(
                        "Buffer underflow while reading DHCPv6 option");
            }

            // peek message type
            dhcp6.msgType = bb.array()[offset];
            if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
                bb.get(); // drop message type
                dhcp6.hopCount = bb.get();
                dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
                dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];

                bb.get(dhcp6.linkAddress);
                bb.get(dhcp6.peerAddress);
            } else {
                // get msg type + transaction id (1 + 3 bytes)
                int defaultHeader = bb.getInt();
                dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
            }

            dhcp6.options = Lists.newArrayList();
            while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
                // create temporary byte buffer for reading code and length
                ByteBuffer optByteBuffer =
                        ByteBuffer.wrap(data, bb.position(), bb.limit() - bb.position());
                short code = optByteBuffer.getShort();
                int optionLen = UNSIGNED_SHORT_MASK & optByteBuffer.getShort();
                if (optByteBuffer.remaining() < optionLen) {
                    throw new DeserializationException(
                            "Buffer underflow while reading DHCPv6 option");
                }
                Dhcp6Option option;
                byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
                bb.get(optionData);
                if (OPT_DESERIALIZERS.containsKey(code)) {
                    option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
                } else {
                    option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
                }
                option.setParent(dhcp6);
                dhcp6.options.add(option);
            }

            return dhcp6;
        };
    }


    /**
     * Gets the message type of this DHCPv6 packet.
     *
     * @return the message type
     */
    public byte getMsgType() {
        return msgType;
    }

    /**
     * Gets options from this DHCPv6 packet.
     *
     * @return DHCPv6 options
     */
    public List getOptions() {
        return options;
    }

    /**
     * Gets the transaction ID of this DHCPv6 packet.
     *
     * @return the transaction ID
     */
    public int getTransactionId() {
        return transactionId;
    }

    /**
     * Gets the hop count of this DHCPv6 relay message.
     *
     * @return the hop count
     */
    public byte getHopCount() {
        return hopCount;
    }

    /**
     * Gets the link address of this DHCPv6 relay message.
     *
     * @return the link address
     */
    public byte[] getLinkAddress() {
        return linkAddress;
    }

    /**
     * Gets IPv6 link address.
     *
     * @return the IPv6 link address
     */
    public Ip6Address getIp6LinkAddress() {
        return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
    }

    /**
     * Gets the peer address of this DHCPv6 relay message.
     *
     * @return the link address
     */
    public byte[] getPeerAddress() {
        return peerAddress;
    }

    /**
     * Gets IPv6 peer address.
     *
     * @return the IPv6 peer address
     */
    public Ip6Address getIp6PeerAddress() {
        return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
    }

    /**
     * Sets message type.
     *
     * @param msgType the message type
     */
    public void setMsgType(byte msgType) {
        this.msgType = msgType;
    }

    /**
     * Sets options.
     *
     * @param options the options
     */
    public void setOptions(List options) {
        this.options = options;
    }

    /**
     * Sets transaction id.
     *
     * @param transactionId the transaction id
     */
    public void setTransactionId(int transactionId) {
        this.transactionId = transactionId;
    }

    /**
     * Sets hop count.
     *
     * @param hopCount the hop count
     */
    public void setHopCount(byte hopCount) {
        this.hopCount = hopCount;
    }

    /**
     * Sets link address.
     *
     * @param linkAddress the link address
     */
    public void setLinkAddress(byte[] linkAddress) {
        this.linkAddress = linkAddress;
    }

    /**
     * Sets peer address.
     *
     * @param peerAddress the peer address
     */
    public void setPeerAddress(byte[] peerAddress) {
        this.peerAddress = peerAddress;
    }

    @Override
    public String toString() {
        if (RELAY_MSG_TYPES.contains(msgType)) {
            // relay message
            return toStringHelper(getClass())
                    .add("msgType", msgType)
                    .add("hopCount", hopCount)
                    .add("linkAddress", Ip6Address.valueOf(linkAddress))
                    .add("peerAddress", Ip6Address.valueOf(peerAddress))
                    .add("options", options)
                    .toString();
        } else {
            return toStringHelper(getClass())
                    .add("msgType", msgType)
                    .add("transactionId", transactionId)
                    .add("options", options)
                    .toString();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy