blob: f8070004d0544f1eaa582e49372da6115ef993b5 [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;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000026import org.onlab.packet.dhcp.Dhcp6IaPdOption;
Yi Tsengc7403c22017-06-19 16:23:22 -070027import org.onlab.packet.dhcp.Dhcp6Option;
Yi Tsengca34e1d2017-07-18 16:16:25 -070028import org.onlab.packet.dhcp.Dhcp6RelayOption;
Kalhee Kim1b5094f2017-09-05 19:05:06 +000029import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
Yi Tseng60bf35a2017-06-02 17:05:48 -070030
31import java.nio.ByteBuffer;
32import java.util.List;
Yi Tsengca34e1d2017-07-18 16:16:25 -070033import java.util.Map;
Yi Tseng60bf35a2017-06-02 17:05:48 -070034import java.util.Set;
35
Yi Tsengca34e1d2017-07-18 16:16:25 -070036import static com.google.common.base.MoreObjects.toStringHelper;
Yi Tseng60bf35a2017-06-02 17:05:48 -070037import static com.google.common.base.Preconditions.checkNotNull;
38
39/**
40 * Representation of an DHCPv6 Packet.
41 * Base on RFC-3315.
42 */
43public class DHCP6 extends BasePacket {
Yi Tsengb4fdb042017-08-07 13:32:32 -070044 private static final int UNSIGNED_SHORT_MASK = 0xffff;
Yi Tseng60bf35a2017-06-02 17:05:48 -070045 // size of different field of option
46 private static final int OPT_CODE_SIZE = 2;
47 private static final int OPT_LEN_SIZE = 2;
48
49 // default size of DHCPv6 payload (without options)
50 private static final int DHCP6_DEFAULT_SIZE = 4;
51
52 // default size of DHCPv6 relay message payload (without options)
53 private static final int DHCP6_RELAY_MSG_SIZE = 34;
54 private static final int IPV6_ADDR_LEN = 16;
55
56 // masks & offsets for default DHCPv6 header
57 private static final int MSG_TYPE_OFFSET = 24;
58 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
59
60 // Relay message types
Yi Tsengca34e1d2017-07-18 16:16:25 -070061 public static final Set<Byte> RELAY_MSG_TYPES =
Yi Tseng60bf35a2017-06-02 17:05:48 -070062 ImmutableSet.of(MsgType.RELAY_FORW.value,
63 MsgType.RELAY_REPL.value);
64
65 /**
66 * DHCPv6 message type.
67 */
68 public enum MsgType {
69 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
70 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
71 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
72 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
73 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
74
75 protected byte value;
76 MsgType(final byte value) {
77 this.value = value;
78 }
79 public byte value() {
80 return this.value;
81 }
82 }
83
84 /**
85 * DHCPv6 option code.
86 */
87 public enum OptionCode {
88 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
89 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
90 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
91 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
92 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
Kalhee Kim1b5094f2017-09-05 19:05:06 +000093 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20), IA_PD((short) 25), IAPREFIX((short) 26),
94 SUBSCRIBER_ID((short) 38);
Yi Tseng60bf35a2017-06-02 17:05:48 -070095
96 protected short value;
97 OptionCode(final short value) {
98 this.value = value;
99 }
100 public short value() {
101 return this.value;
102 }
103 }
104
Yi Tsengca34e1d2017-07-18 16:16:25 -0700105 private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
Kalhee Kim1b5094f2017-09-05 19:05:06 +0000106 ImmutableMap.<Short, Deserializer<Dhcp6Option>>builder()
107 .put(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer())
108 .put(OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer())
109 .put(OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer())
110 .put(OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer())
111 .put(OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer())
112 .put(OptionCode.IA_PD.value, Dhcp6IaPdOption.deserializer())
113 .put(OptionCode.INTERFACE_ID.value, Dhcp6InterfaceIdOption.deserializer())
114 .build();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700115
Yi Tseng60bf35a2017-06-02 17:05:48 -0700116 // general field
117 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -0700118 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -0700119
120 // non-relay field
121 private int transactionId; // 3 bytes
122
123 // relay field
124 private byte hopCount; // 1 byte
125 private byte[] linkAddress; // 16 bytes
126 private byte[] peerAddress; // 16 bytes
127
128 /**
129 * Creates new DHCPv6 object.
130 */
131 public DHCP6() {
132 options = Lists.newArrayList();
133 }
134
135 @Override
136 public byte[] serialize() {
137 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700138 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700139 .sum();
140
141 // 2 bytes code and 2 bytes length
142 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
143
144 if (RELAY_MSG_TYPES.contains(msgType)) {
145 payloadLength += DHCP6_RELAY_MSG_SIZE;
146 } else {
147 payloadLength += DHCP6_DEFAULT_SIZE;
148 }
149
150 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
151
152 if (RELAY_MSG_TYPES.contains(msgType)) {
153 bb.put(msgType);
154 bb.put(hopCount);
155 bb.put(linkAddress);
156 bb.put(peerAddress);
157 } else {
158 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
159 bb.putInt(defaultHeader);
160 }
161
162 // serialize options
163 options.forEach(option -> {
Yi Tsengca34e1d2017-07-18 16:16:25 -0700164 bb.put(option.serialize());
Yi Tseng60bf35a2017-06-02 17:05:48 -0700165 });
166
167 return bb.array();
168 }
169
170 /**
171 * Returns a deserializer for DHCPv6.
172 *
173 * @return the deserializer for DHCPv6
174 */
175 public static Deserializer<DHCP6> deserializer() {
176 return (data, offset, length) -> {
177 DHCP6 dhcp6 = new DHCP6();
178
179 checkNotNull(data);
180
181 if (offset < 0 || length < 0 ||
182 length > data.length || offset >= data.length ||
183 offset + length > data.length) {
184 throw new DeserializationException("Illegal offset or length");
185 }
186
187 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
188 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
189 throw new DeserializationException(
190 "Buffer underflow while reading DHCPv6 option");
191 }
192
193 // peek message type
Yi Tsengb4fdb042017-08-07 13:32:32 -0700194 dhcp6.msgType = bb.array()[offset];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700195 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
196 bb.get(); // drop message type
197 dhcp6.hopCount = bb.get();
198 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
199 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
200
201 bb.get(dhcp6.linkAddress);
202 bb.get(dhcp6.peerAddress);
203 } else {
204 // get msg type + transaction id (1 + 3 bytes)
205 int defaultHeader = bb.getInt();
206 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
207 }
208
209 dhcp6.options = Lists.newArrayList();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700210 while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
211 // create temporary byte buffer for reading code and length
212 ByteBuffer optByteBuffer =
Yi Tsengb4fdb042017-08-07 13:32:32 -0700213 ByteBuffer.wrap(data, bb.position(), bb.limit() - bb.position());
Yi Tsengca34e1d2017-07-18 16:16:25 -0700214 short code = optByteBuffer.getShort();
Yi Tsengb4fdb042017-08-07 13:32:32 -0700215 int optionLen = UNSIGNED_SHORT_MASK & optByteBuffer.getShort();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700216 if (optByteBuffer.remaining() < optionLen) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700217 throw new DeserializationException(
218 "Buffer underflow while reading DHCPv6 option");
219 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700220 Dhcp6Option option;
221 byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700222 bb.get(optionData);
Yi Tsengca34e1d2017-07-18 16:16:25 -0700223 if (OPT_DESERIALIZERS.containsKey(code)) {
224 option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
225 } else {
226 option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
227 }
228 option.setParent(dhcp6);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700229 dhcp6.options.add(option);
230 }
231
232 return dhcp6;
233 };
234 }
235
236 @Override
237 public IPacket deserialize(byte[] data, int offset, int length) {
238 try {
239 return deserializer().deserialize(data, offset, length);
240 } catch (DeserializationException e) {
241 return null;
242 }
243 }
244
245 /**
246 * Gets the message type of this DHCPv6 packet.
247 *
248 * @return the message type
249 */
250 public byte getMsgType() {
251 return msgType;
252 }
253
254 /**
255 * Gets options from this DHCPv6 packet.
256 *
257 * @return DHCPv6 options
258 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700259 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700260 return options;
261 }
262
263 /**
264 * Gets the transaction ID of this DHCPv6 packet.
265 *
266 * @return the transaction ID
267 */
268 public int getTransactionId() {
269 return transactionId;
270 }
271
272 /**
273 * Gets the hop count of this DHCPv6 relay message.
274 *
275 * @return the hop count
276 */
277 public byte getHopCount() {
278 return hopCount;
279 }
280
281 /**
282 * Gets the link address of this DHCPv6 relay message.
283 *
284 * @return the link address
285 */
286 public byte[] getLinkAddress() {
287 return linkAddress;
288 }
289
290 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700291 * Gets IPv6 link address.
292 *
293 * @return the IPv6 link address
294 */
295 public Ip6Address getIp6LinkAddress() {
296 return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
297 }
298
299 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700300 * Gets the peer address of this DHCPv6 relay message.
301 *
302 * @return the link address
303 */
304 public byte[] getPeerAddress() {
305 return peerAddress;
306 }
307
308 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700309 * Gets IPv6 peer address.
310 *
311 * @return the IPv6 peer address
312 */
313 public Ip6Address getIp6PeerAddress() {
314 return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
315 }
316
317 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700318 * Sets message type.
319 *
320 * @param msgType the message type
321 */
322 public void setMsgType(byte msgType) {
323 this.msgType = msgType;
324 }
325
326 /**
327 * Sets options.
328 *
329 * @param options the options
330 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700331 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700332 this.options = options;
333 }
334
335 /**
336 * Sets transaction id.
337 *
338 * @param transactionId the transaction id
339 */
340 public void setTransactionId(int transactionId) {
341 this.transactionId = transactionId;
342 }
343
344 /**
345 * Sets hop count.
346 *
347 * @param hopCount the hop count
348 */
349 public void setHopCount(byte hopCount) {
350 this.hopCount = hopCount;
351 }
352
353 /**
354 * Sets link address.
355 *
356 * @param linkAddress the link address
357 */
358 public void setLinkAddress(byte[] linkAddress) {
359 this.linkAddress = linkAddress;
360 }
361
362 /**
363 * Sets peer address.
364 *
365 * @param peerAddress the peer address
366 */
367 public void setPeerAddress(byte[] peerAddress) {
368 this.peerAddress = peerAddress;
369 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700370
371 @Override
372 public String toString() {
373 if (RELAY_MSG_TYPES.contains(msgType)) {
374 // relay message
375 return toStringHelper(getClass())
376 .add("msgType", msgType)
377 .add("hopCount", hopCount)
378 .add("linkAddress", Ip6Address.valueOf(linkAddress))
379 .add("peerAddress", Ip6Address.valueOf(peerAddress))
380 .add("options", options)
381 .toString();
382 } else {
383 return toStringHelper(getClass())
384 .add("msgType", msgType)
385 .add("transactionId", transactionId)
386 .add("options", options)
387 .toString();
388 }
389 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700390}