blob: 924d181a5eb78bbb1a3c81ffcc15c2402575465d [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 {
Yi Tsengb4fdb042017-08-07 13:32:32 -070042 private static final int UNSIGNED_SHORT_MASK = 0xffff;
Yi Tseng60bf35a2017-06-02 17:05:48 -070043 // size of different field of option
44 private static final int OPT_CODE_SIZE = 2;
45 private static final int OPT_LEN_SIZE = 2;
46
47 // default size of DHCPv6 payload (without options)
48 private static final int DHCP6_DEFAULT_SIZE = 4;
49
50 // default size of DHCPv6 relay message payload (without options)
51 private static final int DHCP6_RELAY_MSG_SIZE = 34;
52 private static final int IPV6_ADDR_LEN = 16;
53
54 // masks & offsets for default DHCPv6 header
55 private static final int MSG_TYPE_OFFSET = 24;
56 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
57
58 // Relay message types
Yi Tsengca34e1d2017-07-18 16:16:25 -070059 public static final Set<Byte> RELAY_MSG_TYPES =
Yi Tseng60bf35a2017-06-02 17:05:48 -070060 ImmutableSet.of(MsgType.RELAY_FORW.value,
61 MsgType.RELAY_REPL.value);
62
63 /**
64 * DHCPv6 message type.
65 */
66 public enum MsgType {
67 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
68 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
69 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
70 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
71 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
72
73 protected byte value;
74 MsgType(final byte value) {
75 this.value = value;
76 }
77 public byte value() {
78 return this.value;
79 }
80 }
81
82 /**
83 * DHCPv6 option code.
84 */
85 public enum OptionCode {
86 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
87 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
88 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
89 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
90 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
Yi Tsengb4fdb042017-08-07 13:32:32 -070091 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20), SUBSCRIBER_ID((short) 38);
Yi Tseng60bf35a2017-06-02 17:05:48 -070092
93 protected short value;
94 OptionCode(final short value) {
95 this.value = value;
96 }
97 public short value() {
98 return this.value;
99 }
100 }
101
Yi Tsengca34e1d2017-07-18 16:16:25 -0700102 private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
103 ImmutableMap.of(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer(),
104 OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer(),
105 OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer(),
106 OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer(),
107 OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer());
108
Yi Tseng60bf35a2017-06-02 17:05:48 -0700109 // general field
110 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -0700111 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -0700112
113 // non-relay field
114 private int transactionId; // 3 bytes
115
116 // relay field
117 private byte hopCount; // 1 byte
118 private byte[] linkAddress; // 16 bytes
119 private byte[] peerAddress; // 16 bytes
120
121 /**
122 * Creates new DHCPv6 object.
123 */
124 public DHCP6() {
125 options = Lists.newArrayList();
126 }
127
128 @Override
129 public byte[] serialize() {
130 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700131 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700132 .sum();
133
134 // 2 bytes code and 2 bytes length
135 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
136
137 if (RELAY_MSG_TYPES.contains(msgType)) {
138 payloadLength += DHCP6_RELAY_MSG_SIZE;
139 } else {
140 payloadLength += DHCP6_DEFAULT_SIZE;
141 }
142
143 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
144
145 if (RELAY_MSG_TYPES.contains(msgType)) {
146 bb.put(msgType);
147 bb.put(hopCount);
148 bb.put(linkAddress);
149 bb.put(peerAddress);
150 } else {
151 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
152 bb.putInt(defaultHeader);
153 }
154
155 // serialize options
156 options.forEach(option -> {
Yi Tsengca34e1d2017-07-18 16:16:25 -0700157 bb.put(option.serialize());
Yi Tseng60bf35a2017-06-02 17:05:48 -0700158 });
159
160 return bb.array();
161 }
162
163 /**
164 * Returns a deserializer for DHCPv6.
165 *
166 * @return the deserializer for DHCPv6
167 */
168 public static Deserializer<DHCP6> deserializer() {
169 return (data, offset, length) -> {
170 DHCP6 dhcp6 = new DHCP6();
171
172 checkNotNull(data);
173
174 if (offset < 0 || length < 0 ||
175 length > data.length || offset >= data.length ||
176 offset + length > data.length) {
177 throw new DeserializationException("Illegal offset or length");
178 }
179
180 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
181 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
182 throw new DeserializationException(
183 "Buffer underflow while reading DHCPv6 option");
184 }
185
186 // peek message type
Yi Tsengb4fdb042017-08-07 13:32:32 -0700187 dhcp6.msgType = bb.array()[offset];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700188 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
189 bb.get(); // drop message type
190 dhcp6.hopCount = bb.get();
191 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
192 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
193
194 bb.get(dhcp6.linkAddress);
195 bb.get(dhcp6.peerAddress);
196 } else {
197 // get msg type + transaction id (1 + 3 bytes)
198 int defaultHeader = bb.getInt();
199 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
200 }
201
202 dhcp6.options = Lists.newArrayList();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700203 while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
204 // create temporary byte buffer for reading code and length
205 ByteBuffer optByteBuffer =
Yi Tsengb4fdb042017-08-07 13:32:32 -0700206 ByteBuffer.wrap(data, bb.position(), bb.limit() - bb.position());
Yi Tsengca34e1d2017-07-18 16:16:25 -0700207 short code = optByteBuffer.getShort();
Yi Tsengb4fdb042017-08-07 13:32:32 -0700208 int optionLen = UNSIGNED_SHORT_MASK & optByteBuffer.getShort();
Yi Tsengca34e1d2017-07-18 16:16:25 -0700209 if (optByteBuffer.remaining() < optionLen) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700210 throw new DeserializationException(
211 "Buffer underflow while reading DHCPv6 option");
212 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700213 Dhcp6Option option;
214 byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
Yi Tseng60bf35a2017-06-02 17:05:48 -0700215 bb.get(optionData);
Yi Tsengca34e1d2017-07-18 16:16:25 -0700216 if (OPT_DESERIALIZERS.containsKey(code)) {
217 option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
218 } else {
219 option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
220 }
221 option.setParent(dhcp6);
Yi Tseng60bf35a2017-06-02 17:05:48 -0700222 dhcp6.options.add(option);
223 }
224
225 return dhcp6;
226 };
227 }
228
229 @Override
230 public IPacket deserialize(byte[] data, int offset, int length) {
231 try {
232 return deserializer().deserialize(data, offset, length);
233 } catch (DeserializationException e) {
234 return null;
235 }
236 }
237
238 /**
239 * Gets the message type of this DHCPv6 packet.
240 *
241 * @return the message type
242 */
243 public byte getMsgType() {
244 return msgType;
245 }
246
247 /**
248 * Gets options from this DHCPv6 packet.
249 *
250 * @return DHCPv6 options
251 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700252 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700253 return options;
254 }
255
256 /**
257 * Gets the transaction ID of this DHCPv6 packet.
258 *
259 * @return the transaction ID
260 */
261 public int getTransactionId() {
262 return transactionId;
263 }
264
265 /**
266 * Gets the hop count of this DHCPv6 relay message.
267 *
268 * @return the hop count
269 */
270 public byte getHopCount() {
271 return hopCount;
272 }
273
274 /**
275 * Gets the link address of this DHCPv6 relay message.
276 *
277 * @return the link address
278 */
279 public byte[] getLinkAddress() {
280 return linkAddress;
281 }
282
283 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700284 * Gets IPv6 link address.
285 *
286 * @return the IPv6 link address
287 */
288 public Ip6Address getIp6LinkAddress() {
289 return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
290 }
291
292 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700293 * Gets the peer address of this DHCPv6 relay message.
294 *
295 * @return the link address
296 */
297 public byte[] getPeerAddress() {
298 return peerAddress;
299 }
300
301 /**
Yi Tsengca34e1d2017-07-18 16:16:25 -0700302 * Gets IPv6 peer address.
303 *
304 * @return the IPv6 peer address
305 */
306 public Ip6Address getIp6PeerAddress() {
307 return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
308 }
309
310 /**
Yi Tseng60bf35a2017-06-02 17:05:48 -0700311 * Sets message type.
312 *
313 * @param msgType the message type
314 */
315 public void setMsgType(byte msgType) {
316 this.msgType = msgType;
317 }
318
319 /**
320 * Sets options.
321 *
322 * @param options the options
323 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700324 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700325 this.options = options;
326 }
327
328 /**
329 * Sets transaction id.
330 *
331 * @param transactionId the transaction id
332 */
333 public void setTransactionId(int transactionId) {
334 this.transactionId = transactionId;
335 }
336
337 /**
338 * Sets hop count.
339 *
340 * @param hopCount the hop count
341 */
342 public void setHopCount(byte hopCount) {
343 this.hopCount = hopCount;
344 }
345
346 /**
347 * Sets link address.
348 *
349 * @param linkAddress the link address
350 */
351 public void setLinkAddress(byte[] linkAddress) {
352 this.linkAddress = linkAddress;
353 }
354
355 /**
356 * Sets peer address.
357 *
358 * @param peerAddress the peer address
359 */
360 public void setPeerAddress(byte[] peerAddress) {
361 this.peerAddress = peerAddress;
362 }
Yi Tsengca34e1d2017-07-18 16:16:25 -0700363
364 @Override
365 public String toString() {
366 if (RELAY_MSG_TYPES.contains(msgType)) {
367 // relay message
368 return toStringHelper(getClass())
369 .add("msgType", msgType)
370 .add("hopCount", hopCount)
371 .add("linkAddress", Ip6Address.valueOf(linkAddress))
372 .add("peerAddress", Ip6Address.valueOf(peerAddress))
373 .add("options", options)
374 .toString();
375 } else {
376 return toStringHelper(getClass())
377 .add("msgType", msgType)
378 .add("transactionId", transactionId)
379 .add("options", options)
380 .toString();
381 }
382 }
Yi Tseng60bf35a2017-06-02 17:05:48 -0700383}