blob: 96e2fd73ab5c8e68d2b548512b74acb8e4db077d [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;
Jonathan Hart2a655752015-04-07 16:46:33 -070032import static org.onlab.packet.PacketUtils.checkInput;
33
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080034/**
Dusan Pajina22b9702015-02-12 16:25:23 +010035 * Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080036 */
37public class NeighborAdvertisement extends BasePacket {
38 public static final byte HEADER_LENGTH = 20; // bytes
39
40 protected byte routerFlag;
41 protected byte solicitedFlag;
42 protected byte overrideFlag;
43 protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
44
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080045 private final NeighborDiscoveryOptions options =
46 new NeighborDiscoveryOptions();
47
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080048 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080049 * Gets router flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080050 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080051 * @return the router flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080052 */
53 public byte getRouterFlag() {
54 return this.routerFlag;
55 }
56
57 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080058 * Sets router flag.
59 *
60 * @param routerFlag the router flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080061 * @return this
62 */
63 public NeighborAdvertisement setRouterFlag(final byte routerFlag) {
64 this.routerFlag = routerFlag;
65 return this;
66 }
67
68 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080069 * Gets solicited flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080070 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080071 * @return the solicited flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080072 */
73 public byte getSolicitedFlag() {
74 return this.solicitedFlag;
75 }
76
77 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080078 * Sets solicited flag.
79 *
80 * @param solicitedFlag the solicited flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080081 * @return this
82 */
83 public NeighborAdvertisement setSolicitedFlag(final byte solicitedFlag) {
84 this.solicitedFlag = solicitedFlag;
85 return this;
86 }
87
88 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080089 * Gets override flag.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080090 *
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080091 * @return the override flag
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +080092 */
93 public byte getOverrideFlag() {
94 return this.overrideFlag;
95 }
96
97 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080098 * Sets override flag.
99 *
100 * @param overrideFlag the override flag to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800101 * @return this
102 */
103 public NeighborAdvertisement setOverrideFlag(final byte overrideFlag) {
104 this.overrideFlag = overrideFlag;
105 return this;
106 }
107
108 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800109 * Gets target address.
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800110 *
111 * @return the target IPv6 address
112 */
113 public byte[] getTargetAddress() {
114 return this.targetAddress;
115 }
116
117 /**
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800118 * Sets target address.
119 *
120 * @param targetAddress the target IPv6 address to set
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800121 * @return this
122 */
123 public NeighborAdvertisement setTargetAddress(final byte[] targetAddress) {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800124 this.targetAddress =
125 Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
126 return this;
127 }
128
129 /**
130 * Gets the Neighbor Discovery Protocol packet options.
131 *
132 * @return the Neighbor Discovery Protocol packet options
133 */
134 public List<NeighborDiscoveryOptions.Option> getOptions() {
135 return this.options.options();
136 }
137
138 /**
139 * Adds a Neighbor Discovery Protocol packet option.
140 *
141 * @param type the option type
142 * @param data the option data
143 * @return this
144 */
145 public NeighborAdvertisement addOption(final byte type,
146 final byte[] data) {
147 this.options.addOption(type, data);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800148 return this;
149 }
150
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800151 @Override
152 public byte[] serialize() {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800153 byte[] optionsData = null;
154 if (this.options.hasOptions()) {
155 optionsData = this.options.serialize();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800156 }
157
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800158 int optionsLength = 0;
159 if (optionsData != null) {
160 optionsLength = optionsData.length;
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800161 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800162
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800163 final byte[] data = new byte[HEADER_LENGTH + optionsLength];
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800164 final ByteBuffer bb = ByteBuffer.wrap(data);
165
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800166 bb.putInt((this.routerFlag & 0x1) << 31 |
167 (this.solicitedFlag & 0x1) << 30 |
168 (this.overrideFlag & 0x1) << 29);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800169 bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800170 if (optionsData != null) {
171 bb.put(optionsData);
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800172 }
173
174 return data;
175 }
176
177 @Override
178 public IPacket deserialize(byte[] data, int offset, int length) {
179 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
180 int iscratch;
181
182 iscratch = bb.getInt();
183 this.routerFlag = (byte) (iscratch >> 31 & 0x1);
184 this.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
185 this.overrideFlag = (byte) (iscratch >> 29 & 0x1);
186 bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
187
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800188 this.options.deserialize(data, bb.position(),
189 bb.limit() - bb.position());
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800190
191 return this;
192 }
193
194 /*
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800195 * (non-Javadoc)
196 *
197 * @see java.lang.Object#hashCode()
198 */
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800199 @Override
200 public int hashCode() {
201 final int prime = 5807;
202 int result = super.hashCode();
203 ByteBuffer bb;
204 result = prime * result + this.routerFlag;
205 result = prime * result + this.solicitedFlag;
206 result = prime * result + this.overrideFlag;
207 bb = ByteBuffer.wrap(this.targetAddress);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800208 for (int i = 0; i < this.targetAddress.length / 4; i++) {
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800209 result = prime * result + bb.getInt();
210 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800211 result = prime * result + this.options.hashCode();
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800212 return result;
213 }
214
215 /*
216 * (non-Javadoc)
217 *
218 * @see java.lang.Object#equals(java.lang.Object)
219 */
220 @Override
221 public boolean equals(final Object obj) {
222 if (this == obj) {
223 return true;
224 }
225 if (!super.equals(obj)) {
226 return false;
227 }
228 if (!(obj instanceof NeighborAdvertisement)) {
229 return false;
230 }
231 final NeighborAdvertisement other = (NeighborAdvertisement) obj;
232 if (this.routerFlag != other.routerFlag) {
233 return false;
234 }
235 if (this.solicitedFlag != other.solicitedFlag) {
236 return false;
237 }
238 if (this.overrideFlag != other.overrideFlag) {
239 return false;
240 }
241 if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
242 return false;
243 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800244 if (!this.options.equals(other.options)) {
245 return false;
246 }
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800247 return true;
248 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700249
250 /**
251 * Deserializer function for neighbor advertisement packets.
252 *
253 * @return deserializer function
254 */
255 public static Deserializer<NeighborAdvertisement> deserializer() {
256 return (data, offset, length) -> {
257 checkInput(data, offset, length, HEADER_LENGTH);
258
259 NeighborAdvertisement neighborAdvertisement = new NeighborAdvertisement();
260
261 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
262
263 int iscratch;
264
265 iscratch = bb.getInt();
266 neighborAdvertisement.routerFlag = (byte) (iscratch >> 31 & 0x1);
267 neighborAdvertisement.solicitedFlag = (byte) (iscratch >> 30 & 0x1);
268 neighborAdvertisement.overrideFlag = (byte) (iscratch >> 29 & 0x1);
269 bb.get(neighborAdvertisement.targetAddress, 0, Ip6Address.BYTE_LENGTH);
270
Charles Chan3599d632015-09-05 14:47:51 +0800271 if (bb.limit() - bb.position() > 0) {
272 NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
273 .deserialize(data, bb.position(), bb.limit() - bb.position());
Jonathan Hart2a655752015-04-07 16:46:33 -0700274
Charles Chan3599d632015-09-05 14:47:51 +0800275 for (NeighborDiscoveryOptions.Option option : options.options()) {
276 neighborAdvertisement.addOption(option.type(), option.data());
277 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700278 }
279
280 return neighborAdvertisement;
281 };
282 }
Jian Li5fc14292015-12-04 11:30:46 -0800283
284 @Override
285 public String toString() {
286 return toStringHelper(getClass())
287 .add("routerFlag", Byte.toString(routerFlag))
288 .add("solicitedFlag", Byte.toString(solicitedFlag))
289 .add("overrideFlag", Byte.toString(overrideFlag))
290 .add("targetAddress", Arrays.toString(targetAddress))
291 .toString();
292 }
Pier Ventre78e73f62016-12-02 19:59:28 -0800293
294 /**
295 * Builds an NDP reply based on a request.
296 *
297 * @param srcIp the IP address to use as the reply source
298 * @param srcMac the MAC address to use as the reply source
299 * @param request the Neighbor Solicitation request we got
300 * @return an Ethernet frame containing the Neighbor Advertisement reply
301 */
302 public static Ethernet buildNdpAdv(byte[] srcIp,
303 byte[] srcMac,
304 Ethernet request) {
305
306 if (srcIp.length != Ip6Address.BYTE_LENGTH ||
307 srcMac.length != MacAddress.MAC_ADDRESS_LENGTH) {
308 return null;
309 }
310
311 if (request.getEtherType() != Ethernet.TYPE_IPV6) {
312 return null;
313 }
314
315 IPv6 ipv6Request = (IPv6) request.getPayload();
316
317 if (ipv6Request.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
318 return null;
319 }
320
321 ICMP6 icmpv6 = (ICMP6) ipv6Request.getPayload();
322
323 if (icmpv6.getIcmpType() != ICMP6.NEIGHBOR_SOLICITATION) {
324 return null;
325 }
326
327 Ethernet eth = new Ethernet();
328 eth.setDestinationMACAddress(request.getSourceMAC());
329 eth.setSourceMACAddress(srcMac);
330 eth.setEtherType(Ethernet.TYPE_IPV6);
331 eth.setVlanID(request.getVlanID());
332
333 IPv6 ipv6 = new IPv6();
334 ipv6.setSourceAddress(srcIp);
335 ipv6.setDestinationAddress(ipv6Request.getSourceAddress());
336 ipv6.setHopLimit((byte) 255);
337
338 ICMP6 icmp6 = new ICMP6();
339 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
340 icmp6.setIcmpCode((byte) 0);
341
342 NeighborAdvertisement nadv = new NeighborAdvertisement();
343 nadv.setTargetAddress(srcIp);
344 nadv.setSolicitedFlag((byte) 1);
345 nadv.setOverrideFlag((byte) 1);
346 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
347 srcMac);
348
349 icmp6.setPayload(nadv);
350 ipv6.setPayload(icmp6);
351 eth.setPayload(ipv6);
352 return eth;
353 }
354
Charles M.C. Chan93b7fb02014-12-28 03:59:36 +0800355}