blob: fe6debd6e41add1d5eb1379aac158cbc9c59c2ce [file] [log] [blame]
Yi Tseng60bf35a2017-06-02 17:05:48 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yi Tseng60bf35a2017-06-02 17:05:48 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onlab.packet;
18
Yi Tsengca34e1d2017-07-18 16:16:25 -070019import com.google.common.collect.ImmutableMap;
Yi Tseng60bf35a2017-06-02 17:05:48 -070020import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Lists;
Yi Tsengca34e1d2017-07-18 16:16:25 -070022import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
23import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
24import org.onlab.packet.dhcp.Dhcp6IaNaOption;
25import org.onlab.packet.dhcp.Dhcp6IaTaOption;
Yi Tsengc7403c22017-06-19 16:23:22 -070026import org.onlab.packet.dhcp.Dhcp6Option;
Yi Tsengca34e1d2017-07-18 16:16:25 -070027import org.onlab.packet.dhcp.Dhcp6RelayOption;
Yi Tseng60bf35a2017-06-02 17:05:48 -070028
29import java.nio.ByteBuffer;
30import java.util.List;
Yi Tsengca34e1d2017-07-18 16:16:25 -070031import java.util.Map;
Yi Tseng60bf35a2017-06-02 17:05:48 -070032import java.util.Set;
33
Yi Tsengca34e1d2017-07-18 16:16:25 -070034import static com.google.common.base.MoreObjects.toStringHelper;
Yi Tseng60bf35a2017-06-02 17:05:48 -070035import static com.google.common.base.Preconditions.checkNotNull;
36
37/**
38 * Representation of an DHCPv6 Packet.
39 * Base on RFC-3315.
40 */
41public class DHCP6 extends BasePacket {
42 // size of different field of option
43 private static final int OPT_CODE_SIZE = 2;
44 private static final int OPT_LEN_SIZE = 2;
45
46 // default size of DHCPv6 payload (without options)
47 private static final int DHCP6_DEFAULT_SIZE = 4;
48
49 // default size of DHCPv6 relay message payload (without options)
50 private static final int DHCP6_RELAY_MSG_SIZE = 34;
51 private static final int IPV6_ADDR_LEN = 16;
52
53 // masks & offsets for default DHCPv6 header
54 private static final int MSG_TYPE_OFFSET = 24;
55 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
56
57 // Relay message types
Yi Tsengca34e1d2017-07-18 16:16:25 -070058 public static final Set<Byte> RELAY_MSG_TYPES =
Yi Tseng60bf35a2017-06-02 17:05:48 -070059 ImmutableSet.of(MsgType.RELAY_FORW.value,
60 MsgType.RELAY_REPL.value);
61
62 /**
63 * DHCPv6 message type.
64 */
65 public enum MsgType {
66 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
67 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
68 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
69 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
70 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
71
72 protected byte value;
73 MsgType(final byte value) {
74 this.value = value;
75 }
76 public byte value() {
77 return this.value;
78 }
79 }
80
81 /**
82 * DHCPv6 option code.
83 */
84 public enum OptionCode {
85 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
86 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
87 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
88 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
89 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
90 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20);
91
92 protected short value;
93 OptionCode(final short value) {
94 this.value = value;
95 }
96 public short value() {
97 return this.value;
98 }
99 }
100
Yi Tsengca34e1d2017-07-18 16:16:25 -0700101 private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
102 ImmutableMap.of(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer(),
103 OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer(),
104 OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer(),
105 OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer(),
106 OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer());
107
Yi Tseng60bf35a2017-06-02 17:05:48 -0700108 // general field
109 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -0700110 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -0700111
112 // non-relay field
113 private int transactionId; // 3 bytes
114
115 // relay field
116 private byte hopCount; // 1 byte
117 private byte[] linkAddress; // 16 bytes
118 private byte[] peerAddress; // 16 bytes
119
120 /**
121 * Creates new DHCPv6 object.
122 */
123 public DHCP6() {
124 options = Lists.newArrayList();
125 }
126
127 @Override
128 public byte[] serialize() {
129 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700130 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700131 .sum();
132
133 // 2 bytes code and 2 bytes length
134 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
135
136 if (RELAY_MSG_TYPES.contains(msgType)) {
137 payloadLength += DHCP6_RELAY_MSG_SIZE;
138 } else {
139 payloadLength += DHCP6_DEFAULT_SIZE;
140 }
141
142 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
143
144 if (RELAY_MSG_TYPES.contains(msgType)) {
145 bb.put(msgType);
146 bb.put(hopCount);
147 bb.put(linkAddress);
148 bb.put(peerAddress);
149 } else {
150 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
151 bb.putInt(defaultHeader);
152 }
153
154 // serialize options
155 options.forEach(option -> {
Yi Tsengca34e1d2017-07-18 16:16:25 -0700156 bb.put(option.serialize());
Yi Tseng60bf35a2017-06-02 17:05:48 -0700157 });
158
159 return bb.array();
160 }
161
162 /**
163 * Returns a deserializer for DHCPv6.
164 *
165 * @return the deserializer for DHCPv6
166 */
167 public static Deserializer<DHCP6> deserializer() {
168 return (data, offset, length) -> {
169 DHCP6 dhcp6 = new DHCP6();
170
171 checkNotNull(data);
172
173 if (offset < 0 || length < 0 ||
174 length > data.length || offset >= data.length ||
175 offset + length > data.length) {
176 throw new DeserializationException("Illegal offset or length");
177 }
178
179 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
180 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
181 throw new DeserializationException(
182 "Buffer underflow while reading DHCPv6 option");
183 }
184
185 // peek message type
Yi Tsengca34e1d2017-07-18 16:16:25 -0700186 dhcp6.msgType = (byte) (0xff & bb.array()[offset]);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700187 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
188 bb.get(); // drop message type
189 dhcp6.hopCount = bb.get();
190 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
191 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
192
193 bb.get(dhcp6.linkAddress);
194 bb.get(dhcp6.peerAddress);
195 } else {
196 // get msg type + transaction id (1 + 3 bytes)
197 int defaultHeader = bb.getInt();
198 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
199 }
200
201 dhcp6.options = Lists.newArrayList();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700202 while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
203 // create temporary byte buffer for reading code and length
204 ByteBuffer optByteBuffer =
205 ByteBuffer.wrap(data, bb.position(), length - bb.position());
206 short code = optByteBuffer.getShort();
207 short optionLen = (short) (0xffff & optByteBuffer.getShort());
208 if (optByteBuffer.remaining() < optionLen) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700209 throw new DeserializationException(
210 "Buffer underflow while reading DHCPv6 option");
211 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700212 Dhcp6Option option;
213 byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700214 bb.get(optionData);
Yi Tsengca34e1d2017-07-18 16:16:25 -0700215 if (OPT_DESERIALIZERS.containsKey(code)) {
216 option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
217 } else {
218 option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
219 }
220 option.setParent(dhcp6);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700221 dhcp6.options.add(option);
222 }
223
224 return dhcp6;
225 };
226 }
227
228 @Override
229 public IPacket deserialize(byte[] data, int offset, int length) {
230 try {
231 return deserializer().deserialize(data, offset, length);
232 } catch (DeserializationException e) {
233 return null;
234 }
235 }
236
237 /**
238 * Gets the message type of this DHCPv6 packet.
239 *
240 * @return the message type
241 */
242 public byte getMsgType() {
243 return msgType;
244 }
245
246 /**
247 * Gets options from this DHCPv6 packet.
248 *
249 * @return DHCPv6 options
250 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700251 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700252 return options;
253 }
254
255 /**
256 * Gets the transaction ID of this DHCPv6 packet.
257 *
258 * @return the transaction ID
259 */
260 public int getTransactionId() {
261 return transactionId;
262 }
263
264 /**
265 * Gets the hop count of this DHCPv6 relay message.
266 *
267 * @return the hop count
268 */
269 public byte getHopCount() {
270 return hopCount;
271 }
272
273 /**
274 * Gets the link address of this DHCPv6 relay message.
275 *
276 * @return the link address
277 */
278 public byte[] getLinkAddress() {
279 return linkAddress;
280 }
281
282 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700283 * Gets IPv6 link address.
284 *
285 * @return the IPv6 link address
286 */
287 public Ip6Address getIp6LinkAddress() {
288 return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
289 }
290
291 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700292 * Gets the peer address of this DHCPv6 relay message.
293 *
294 * @return the link address
295 */
296 public byte[] getPeerAddress() {
297 return peerAddress;
298 }
299
300 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700301 * Gets IPv6 peer address.
302 *
303 * @return the IPv6 peer address
304 */
305 public Ip6Address getIp6PeerAddress() {
306 return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
307 }
308
309 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700310 * Sets message type.
311 *
312 * @param msgType the message type
313 */
314 public void setMsgType(byte msgType) {
315 this.msgType = msgType;
316 }
317
318 /**
319 * Sets options.
320 *
321 * @param options the options
322 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700323 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700324 this.options = options;
325 }
326
327 /**
328 * Sets transaction id.
329 *
330 * @param transactionId the transaction id
331 */
332 public void setTransactionId(int transactionId) {
333 this.transactionId = transactionId;
334 }
335
336 /**
337 * Sets hop count.
338 *
339 * @param hopCount the hop count
340 */
341 public void setHopCount(byte hopCount) {
342 this.hopCount = hopCount;
343 }
344
345 /**
346 * Sets link address.
347 *
348 * @param linkAddress the link address
349 */
350 public void setLinkAddress(byte[] linkAddress) {
351 this.linkAddress = linkAddress;
352 }
353
354 /**
355 * Sets peer address.
356 *
357 * @param peerAddress the peer address
358 */
359 public void setPeerAddress(byte[] peerAddress) {
360 this.peerAddress = peerAddress;
361 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700362
363 @Override
364 public String toString() {
365 if (RELAY_MSG_TYPES.contains(msgType)) {
366 // relay message
367 return toStringHelper(getClass())
368 .add("msgType", msgType)
369 .add("hopCount", hopCount)
370 .add("linkAddress", Ip6Address.valueOf(linkAddress))
371 .add("peerAddress", Ip6Address.valueOf(peerAddress))
372 .add("options", options)
373 .toString();
374 } else {
375 return toStringHelper(getClass())
376 .add("msgType", msgType)
377 .add("transactionId", transactionId)
378 .add("options", options)
379 .toString();
380 }
381 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700382}