blob: 6ca76edffaf19b95f9ed26081523729e59873ef8 [file] [log] [blame]
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +08003 *
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 */
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080016package org.onlab.packet.ndp;
17
18import org.onlab.packet.BasePacket;
Jonathan Hart2a655752015-04-07 16:46:33 -070019import org.onlab.packet.Deserializer;
Pier Ventre78e73f62016-12-02 19:59:28 -080020import org.onlab.packet.Ethernet;
21import org.onlab.packet.ICMP6;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080022import org.onlab.packet.IPacket;
Pier Ventre78e73f62016-12-02 19:59:28 -080023import org.onlab.packet.IPv6;
Charles M.C. Chanea5aa472015-01-03 13:40:39 +080024import org.onlab.packet.Ip6Address;
Pier Ventre78e73f62016-12-02 19:59:28 -080025import org.onlab.packet.MacAddress;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080026
27import java.nio.ByteBuffer;
28import java.util.Arrays;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080029import java.util.List;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080030
Jian Li5fc14292015-12-04 11:30:46 -080031import static com.google.common.base.MoreObjects.toStringHelper;
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +020032import static com.google.common.base.Preconditions.checkArgument;
33import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hart2a655752015-04-07 16:46:33 -070034import static org.onlab.packet.PacketUtils.checkInput;
35
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080036/**
Dusan Pajina22b9702015-02-12 16:25:23 +010037 * Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080038 */
39public class NeighborAdvertisement extends BasePacket {
40 public static final byte HEADER_LENGTH = 20; // bytes
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +020041 // Constants for NDP reply
42 protected static final byte NDP_HOP_LIMIT = (byte) 0x255;
43 protected static final byte NDP_SOLICITED_FLAG = (byte) 0x1;
44 protected static final byte NDP_OVERRIDE_FLAG = (byte) 0x1;
45 protected static final byte RESERVED_CODE = (byte) 0x0;
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080046
47 protected byte routerFlag;
48 protected byte solicitedFlag;
49 protected byte overrideFlag;
50 protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
51
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080052 private final NeighborDiscoveryOptions options =
53 new NeighborDiscoveryOptions();
54
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080055 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080056 * Gets router flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080057 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080058 * @return the router flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080059 */
60 public byte getRouterFlag() {
61 return this.routerFlag;
62 }
63
64 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080065 * Sets router flag.
66 *
67 * @param routerFlag the router flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080068 * @return this
69 */
70 public NeighborAdvertisement setRouterFlag(final byte routerFlag) {
71 this.routerFlag = routerFlag;
72 return this;
73 }
74
75 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080076 * Gets solicited flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080077 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080078 * @return the solicited flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080079 */
80 public byte getSolicitedFlag() {
81 return this.solicitedFlag;
82 }
83
84 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080085 * Sets solicited flag.
86 *
87 * @param solicitedFlag the solicited flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080088 * @return this
89 */
90 public NeighborAdvertisement setSolicitedFlag(final byte solicitedFlag) {
91 this.solicitedFlag = solicitedFlag;
92 return this;
93 }
94
95 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080096 * Gets override flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080097 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080098 * @return the override flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080099 */
100 public byte getOverrideFlag() {
101 return this.overrideFlag;
102 }
103
104 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800105 * Sets override flag.
106 *
107 * @param overrideFlag the override flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800108 * @return this
109 */
110 public NeighborAdvertisement setOverrideFlag(final byte overrideFlag) {
111 this.overrideFlag = overrideFlag;
112 return this;
113 }
114
115 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800116 * Gets target address.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800117 *
118 * @return the target IPv6 address
119 */
120 public byte[] getTargetAddress() {
121 return this.targetAddress;
122 }
123
124 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800125 * Sets target address.
126 *
127 * @param targetAddress the target IPv6 address to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800128 * @return this
129 */
130 public NeighborAdvertisement setTargetAddress(final byte[] targetAddress) {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800131 this.targetAddress =
132 Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
133 return this;
134 }
135
136 /**
137 * Gets the Neighbor Discovery Protocol packet options.
138 *
139 * @return the Neighbor Discovery Protocol packet options
140 */
141 public List<NeighborDiscoveryOptions.Option> getOptions() {
142 return this.options.options();
143 }
144
145 /**
146 * Adds a Neighbor Discovery Protocol packet option.
147 *
148 * @param type the option type
149 * @param data the option data
150 * @return this
151 */
152 public NeighborAdvertisement addOption(final byte type,
153 final byte[] data) {
154 this.options.addOption(type, data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800155 return this;
156 }
157
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800158 @Override
159 public byte[] serialize() {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800160 byte[] optionsData = null;
161 if (this.options.hasOptions()) {
162 optionsData = this.options.serialize();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800163 }
164
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800165 int optionsLength = 0;
166 if (optionsData != null) {
167 optionsLength = optionsData.length;
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800168 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800169
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800170 final byte[] data = new byte[HEADER_LENGTH + optionsLength];
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800171 final ByteBuffer bb = ByteBuffer.wrap(data);
172
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800173 bb.putInt((this.routerFlag & 0x1) << 31 |
174 (this.solicitedFlag & 0x1) << 30 |
175 (this.overrideFlag & 0x1) << 29);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800176 bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800177 if (optionsData != null) {
178 bb.put(optionsData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800179 }
180
181 return data;
182 }
183
184 @Override
185 public IPacket deserialize(byte[] data, int offset, int length) {
186 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
187 int iscratch;
188
189 iscratch = bb.getInt();
190 this.routerFlag = (byte) (iscratch >> 31 & 0x1);
191 this.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
192 this.overrideFlag = (byte) (iscratch >> 29 & 0x1);
193 bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
194
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800195 this.options.deserialize(data, bb.position(),
196 bb.limit() - bb.position());
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800197
198 return this;
199 }
200
201 /*
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800202 * (non-Javadoc)
203 *
204 * @see java.lang.Object#hashCode()
205 */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800206 @Override
207 public int hashCode() {
208 final int prime = 5807;
209 int result = super.hashCode();
210 ByteBuffer bb;
211 result = prime * result + this.routerFlag;
212 result = prime * result + this.solicitedFlag;
213 result = prime * result + this.overrideFlag;
214 bb = ByteBuffer.wrap(this.targetAddress);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800215 for (int i = 0; i < this.targetAddress.length / 4; i++) {
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800216 result = prime * result + bb.getInt();
217 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800218 result = prime * result + this.options.hashCode();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800219 return result;
220 }
221
222 /*
223 * (non-Javadoc)
224 *
225 * @see java.lang.Object#equals(java.lang.Object)
226 */
227 @Override
228 public boolean equals(final Object obj) {
229 if (this == obj) {
230 return true;
231 }
232 if (!super.equals(obj)) {
233 return false;
234 }
235 if (!(obj instanceof NeighborAdvertisement)) {
236 return false;
237 }
238 final NeighborAdvertisement other = (NeighborAdvertisement) obj;
239 if (this.routerFlag != other.routerFlag) {
240 return false;
241 }
242 if (this.solicitedFlag != other.solicitedFlag) {
243 return false;
244 }
245 if (this.overrideFlag != other.overrideFlag) {
246 return false;
247 }
248 if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
249 return false;
250 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800251 if (!this.options.equals(other.options)) {
252 return false;
253 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800254 return true;
255 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700256
257 /**
258 * Deserializer function for neighbor advertisement packets.
259 *
260 * @return deserializer function
261 */
262 public static Deserializer<NeighborAdvertisement> deserializer() {
263 return (data, offset, length) -> {
264 checkInput(data, offset, length, HEADER_LENGTH);
265
266 NeighborAdvertisement neighborAdvertisement = new NeighborAdvertisement();
267
268 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
269
270 int iscratch;
271
272 iscratch = bb.getInt();
273 neighborAdvertisement.routerFlag = (byte) (iscratch >> 31 & 0x1);
274 neighborAdvertisement.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
275 neighborAdvertisement.overrideFlag = (byte) (iscratch >> 29 & 0x1);
276 bb.get(neighborAdvertisement.targetAddress, 0, Ip6Address.BYTE_LENGTH);
277
Charles Chan3599d632015-09-05 14:47:51 +0800278 if (bb.limit() - bb.position() > 0) {
279 NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
280 .deserialize(data, bb.position(), bb.limit() - bb.position());
Jonathan Hart2a655752015-04-07 16:46:33 -0700281
Charles Chan3599d632015-09-05 14:47:51 +0800282 for (NeighborDiscoveryOptions.Option option : options.options()) {
283 neighborAdvertisement.addOption(option.type(), option.data());
284 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700285 }
286
287 return neighborAdvertisement;
288 };
289 }
Jian Li5fc14292015-12-04 11:30:46 -0800290
291 @Override
292 public String toString() {
293 return toStringHelper(getClass())
294 .add("routerFlag", Byte.toString(routerFlag))
295 .add("solicitedFlag", Byte.toString(solicitedFlag))
296 .add("overrideFlag", Byte.toString(overrideFlag))
297 .add("targetAddress", Arrays.toString(targetAddress))
298 .toString();
299 }
Pier Ventre78e73f62016-12-02 19:59:28 -0800300
301 /**
302 * Builds an NDP reply based on a request.
303 *
304 * @param srcIp the IP address to use as the reply source
305 * @param srcMac the MAC address to use as the reply source
306 * @param request the Neighbor Solicitation request we got
307 * @return an Ethernet frame containing the Neighbor Advertisement reply
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200308 * @deprecated since 1.11.0
Pier Ventre78e73f62016-12-02 19:59:28 -0800309 */
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200310 @Deprecated
Pier Ventre78e73f62016-12-02 19:59:28 -0800311 public static Ethernet buildNdpAdv(byte[] srcIp,
312 byte[] srcMac,
313 Ethernet request) {
314
315 if (srcIp.length != Ip6Address.BYTE_LENGTH ||
316 srcMac.length != MacAddress.MAC_ADDRESS_LENGTH) {
317 return null;
318 }
319
320 if (request.getEtherType() != Ethernet.TYPE_IPV6) {
321 return null;
322 }
323
324 IPv6 ipv6Request = (IPv6) request.getPayload();
325
326 if (ipv6Request.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
327 return null;
328 }
329
330 ICMP6 icmpv6 = (ICMP6) ipv6Request.getPayload();
331
332 if (icmpv6.getIcmpType() != ICMP6.NEIGHBOR_SOLICITATION) {
333 return null;
334 }
335
336 Ethernet eth = new Ethernet();
337 eth.setDestinationMACAddress(request.getSourceMAC());
338 eth.setSourceMACAddress(srcMac);
339 eth.setEtherType(Ethernet.TYPE_IPV6);
340 eth.setVlanID(request.getVlanID());
341
342 IPv6 ipv6 = new IPv6();
343 ipv6.setSourceAddress(srcIp);
344 ipv6.setDestinationAddress(ipv6Request.getSourceAddress());
345 ipv6.setHopLimit((byte) 255);
346
347 ICMP6 icmp6 = new ICMP6();
348 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
349 icmp6.setIcmpCode((byte) 0);
350
351 NeighborAdvertisement nadv = new NeighborAdvertisement();
352 nadv.setTargetAddress(srcIp);
353 nadv.setSolicitedFlag((byte) 1);
354 nadv.setOverrideFlag((byte) 1);
355 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
356 srcMac);
357
358 icmp6.setPayload(nadv);
359 ipv6.setPayload(icmp6);
360 eth.setPayload(ipv6);
361 return eth;
362 }
363
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200364 /**
365 * Builds an NDP reply based on a request.
366 *
367 * @param srcIp the IP address to use as the reply source
368 * @param srcMac the MAC address to use as the reply source
369 * @param request the Neighbor Solicitation request we got
370 * @return an Ethernet frame containing the Neighbor Advertisement reply
371 */
372 public static Ethernet buildNdpAdv(Ip6Address srcIp,
373 MacAddress srcMac,
374 Ethernet request) {
375
376 checkNotNull(srcIp, "IP address cannot be null");
377 checkNotNull(srcMac, "MAC address cannot be null");
378 checkNotNull(request, "Request cannot be null");
379 checkArgument(request.getEtherType() == Ethernet.TYPE_IPV6,
380 "EtherType must be IPv6");
381
382 final IPv6 ipv6Request = (IPv6) request.getPayload();
383
384 checkArgument(ipv6Request.getNextHeader() == IPv6.PROTOCOL_ICMP6,
385 "Protocol must be ICMP6");
386
387 final ICMP6 icmpv6 = (ICMP6) ipv6Request.getPayload();
388
389 checkArgument(icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION,
390 "ICMP6 type must be NEIGHBOR_SOLICITATION");
391
392 Ethernet eth = new Ethernet();
393 eth.setDestinationMACAddress(request.getSourceMAC());
394 eth.setSourceMACAddress(srcMac);
395 eth.setEtherType(Ethernet.TYPE_IPV6);
396 eth.setVlanID(request.getVlanID());
397
398 IPv6 ipv6 = new IPv6();
399 ipv6.setSourceAddress(srcIp.toOctets());
400 ipv6.setDestinationAddress(ipv6Request.getSourceAddress());
401 ipv6.setHopLimit(NDP_HOP_LIMIT);
402 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
403
404 ICMP6 icmp6 = new ICMP6();
405 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
406 icmp6.setIcmpCode(RESERVED_CODE);
407
408 NeighborAdvertisement nadv = new NeighborAdvertisement();
409 nadv.setTargetAddress(srcIp.toOctets());
410 nadv.setSolicitedFlag(NDP_SOLICITED_FLAG);
411 nadv.setOverrideFlag(NDP_OVERRIDE_FLAG);
412 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
413 srcMac.toBytes());
414
415 icmp6.setPayload(nadv);
416 ipv6.setPayload(icmp6);
417 eth.setPayload(ipv6);
418 return eth;
419 }
420
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800421}