blob: 6310ec59ff3fb2908e0b1382793ffb3039c6b8eb [file] [log] [blame]
Yi Tseng60bf35a2017-06-02 17:05:48 -07001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
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
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Lists;
Yi Tsengc7403c22017-06-19 16:23:22 -070021import org.onlab.packet.dhcp.Dhcp6Option;
Yi Tseng60bf35a2017-06-02 17:05:48 -070022
23import java.nio.ByteBuffer;
24import java.util.List;
25import java.util.Set;
26
27import static com.google.common.base.Preconditions.checkNotNull;
28
29/**
30 * Representation of an DHCPv6 Packet.
31 * Base on RFC-3315.
32 */
33public class DHCP6 extends BasePacket {
34 // size of different field of option
35 private static final int OPT_CODE_SIZE = 2;
36 private static final int OPT_LEN_SIZE = 2;
37
38 // default size of DHCPv6 payload (without options)
39 private static final int DHCP6_DEFAULT_SIZE = 4;
40
41 // default size of DHCPv6 relay message payload (without options)
42 private static final int DHCP6_RELAY_MSG_SIZE = 34;
43 private static final int IPV6_ADDR_LEN = 16;
44
45 // masks & offsets for default DHCPv6 header
46 private static final int MSG_TYPE_OFFSET = 24;
47 private static final int TRANSACTION_ID_MASK = 0x00ffffff;
48
49 // Relay message types
50 private static final Set<Byte> RELAY_MSG_TYPES =
51 ImmutableSet.of(MsgType.RELAY_FORW.value,
52 MsgType.RELAY_REPL.value);
53
54 /**
55 * DHCPv6 message type.
56 */
57 public enum MsgType {
58 SOLICIT((byte) 1), ADVERTISE((byte) 2), REQUEST((byte) 3),
59 CONFIRM((byte) 4), RENEW((byte) 5), REBIND((byte) 6),
60 REPLY((byte) 7), RELEASE((byte) 8), DECLINE((byte) 9),
61 RECONFIGURE((byte) 10), INFORMATION_REQUEST((byte) 11),
62 RELAY_FORW((byte) 12), RELAY_REPL((byte) 13);
63
64 protected byte value;
65 MsgType(final byte value) {
66 this.value = value;
67 }
68 public byte value() {
69 return this.value;
70 }
71 }
72
73 /**
74 * DHCPv6 option code.
75 */
76 public enum OptionCode {
77 CLIENTID((short) 1), SERVERID((short) 2), IA_NA((short) 3), IA_TA((short) 4),
78 IAADDR((short) 5), ORO((short) 6), PREFERENCE((short) 7), ELAPSED_TIME((short) 8),
79 RELAY_MSG((short) 9), AUTH((short) 11), UNICAST((short) 12),
80 STATUS_CODE((short) 13), RAPID_COMMIT((short) 14), USER_CLASS((short) 15),
81 VENDOR_CLASS((short) 16), VENDOR_OPTS((short) 17), INTERFACE_ID((short) 18),
82 RECONF_MSG((short) 19), RECONF_ACCEPT((short) 20);
83
84 protected short value;
85 OptionCode(final short value) {
86 this.value = value;
87 }
88 public short value() {
89 return this.value;
90 }
91 }
92
93 // general field
94 private byte msgType; // 1 byte
Yi Tsengc7403c22017-06-19 16:23:22 -070095 private List<Dhcp6Option> options;
Yi Tseng60bf35a2017-06-02 17:05:48 -070096
97 // non-relay field
98 private int transactionId; // 3 bytes
99
100 // relay field
101 private byte hopCount; // 1 byte
102 private byte[] linkAddress; // 16 bytes
103 private byte[] peerAddress; // 16 bytes
104
105 /**
106 * Creates new DHCPv6 object.
107 */
108 public DHCP6() {
109 options = Lists.newArrayList();
110 }
111
112 @Override
113 public byte[] serialize() {
114 int payloadLength = options.stream()
Yi Tsengc7403c22017-06-19 16:23:22 -0700115 .mapToInt(Dhcp6Option::getLength)
Yi Tseng60bf35a2017-06-02 17:05:48 -0700116 .sum();
117
118 // 2 bytes code and 2 bytes length
119 payloadLength += options.size() * (OPT_CODE_SIZE + OPT_LEN_SIZE);
120
121 if (RELAY_MSG_TYPES.contains(msgType)) {
122 payloadLength += DHCP6_RELAY_MSG_SIZE;
123 } else {
124 payloadLength += DHCP6_DEFAULT_SIZE;
125 }
126
127 ByteBuffer bb = ByteBuffer.allocate(payloadLength);
128
129 if (RELAY_MSG_TYPES.contains(msgType)) {
130 bb.put(msgType);
131 bb.put(hopCount);
132 bb.put(linkAddress);
133 bb.put(peerAddress);
134 } else {
135 int defaultHeader = ((int) msgType) << MSG_TYPE_OFFSET | (transactionId & TRANSACTION_ID_MASK);
136 bb.putInt(defaultHeader);
137 }
138
139 // serialize options
140 options.forEach(option -> {
141 bb.putShort(option.getCode());
142 bb.putShort(option.getLength());
143 bb.put(option.getData());
144 });
145
146 return bb.array();
147 }
148
149 /**
150 * Returns a deserializer for DHCPv6.
151 *
152 * @return the deserializer for DHCPv6
153 */
154 public static Deserializer<DHCP6> deserializer() {
155 return (data, offset, length) -> {
156 DHCP6 dhcp6 = new DHCP6();
157
158 checkNotNull(data);
159
160 if (offset < 0 || length < 0 ||
161 length > data.length || offset >= data.length ||
162 offset + length > data.length) {
163 throw new DeserializationException("Illegal offset or length");
164 }
165
166 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
167 if (bb.remaining() < DHCP6.DHCP6_DEFAULT_SIZE) {
168 throw new DeserializationException(
169 "Buffer underflow while reading DHCPv6 option");
170 }
171
172 // peek message type
173 dhcp6.msgType = bb.array()[0];
174 if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
175 bb.get(); // drop message type
176 dhcp6.hopCount = bb.get();
177 dhcp6.linkAddress = new byte[IPV6_ADDR_LEN];
178 dhcp6.peerAddress = new byte[IPV6_ADDR_LEN];
179
180 bb.get(dhcp6.linkAddress);
181 bb.get(dhcp6.peerAddress);
182 } else {
183 // get msg type + transaction id (1 + 3 bytes)
184 int defaultHeader = bb.getInt();
185 dhcp6.transactionId = defaultHeader & TRANSACTION_ID_MASK;
186 }
187
188 dhcp6.options = Lists.newArrayList();
189 while (bb.remaining() >= OPT_CODE_SIZE) {
Yi Tsengc7403c22017-06-19 16:23:22 -0700190 Dhcp6Option option = new Dhcp6Option();
Yi Tseng60bf35a2017-06-02 17:05:48 -0700191 short code = bb.getShort();
192 if (bb.remaining() < OPT_LEN_SIZE) {
193 throw new DeserializationException(
194 "Buffer underflow while reading DHCPv6 option");
195 }
196
197 short optionLen = bb.getShort();
198 if (bb.remaining() < optionLen) {
199 throw new DeserializationException(
200 "Buffer underflow while reading DHCPv6 option");
201 }
202
203 byte[] optionData = new byte[optionLen];
204 bb.get(optionData);
205
206 option.setCode(code);
207 option.setLength(optionLen);
208 option.setData(optionData);
209 dhcp6.options.add(option);
210 }
211
212 return dhcp6;
213 };
214 }
215
216 @Override
217 public IPacket deserialize(byte[] data, int offset, int length) {
218 try {
219 return deserializer().deserialize(data, offset, length);
220 } catch (DeserializationException e) {
221 return null;
222 }
223 }
224
225 /**
226 * Gets the message type of this DHCPv6 packet.
227 *
228 * @return the message type
229 */
230 public byte getMsgType() {
231 return msgType;
232 }
233
234 /**
235 * Gets options from this DHCPv6 packet.
236 *
237 * @return DHCPv6 options
238 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700239 public List<Dhcp6Option> getOptions() {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700240 return options;
241 }
242
243 /**
244 * Gets the transaction ID of this DHCPv6 packet.
245 *
246 * @return the transaction ID
247 */
248 public int getTransactionId() {
249 return transactionId;
250 }
251
252 /**
253 * Gets the hop count of this DHCPv6 relay message.
254 *
255 * @return the hop count
256 */
257 public byte getHopCount() {
258 return hopCount;
259 }
260
261 /**
262 * Gets the link address of this DHCPv6 relay message.
263 *
264 * @return the link address
265 */
266 public byte[] getLinkAddress() {
267 return linkAddress;
268 }
269
270 /**
271 * Gets the peer address of this DHCPv6 relay message.
272 *
273 * @return the link address
274 */
275 public byte[] getPeerAddress() {
276 return peerAddress;
277 }
278
279 /**
280 * Sets message type.
281 *
282 * @param msgType the message type
283 */
284 public void setMsgType(byte msgType) {
285 this.msgType = msgType;
286 }
287
288 /**
289 * Sets options.
290 *
291 * @param options the options
292 */
Yi Tsengc7403c22017-06-19 16:23:22 -0700293 public void setOptions(List<Dhcp6Option> options) {
Yi Tseng60bf35a2017-06-02 17:05:48 -0700294 this.options = options;
295 }
296
297 /**
298 * Sets transaction id.
299 *
300 * @param transactionId the transaction id
301 */
302 public void setTransactionId(int transactionId) {
303 this.transactionId = transactionId;
304 }
305
306 /**
307 * Sets hop count.
308 *
309 * @param hopCount the hop count
310 */
311 public void setHopCount(byte hopCount) {
312 this.hopCount = hopCount;
313 }
314
315 /**
316 * Sets link address.
317 *
318 * @param linkAddress the link address
319 */
320 public void setLinkAddress(byte[] linkAddress) {
321 this.linkAddress = linkAddress;
322 }
323
324 /**
325 * Sets peer address.
326 *
327 * @param peerAddress the peer address
328 */
329 public void setPeerAddress(byte[] peerAddress) {
330 this.peerAddress = peerAddress;
331 }
332}