blob: 9c05c6fc64e195699614bdaf7f82368deceb7508 [file] [log] [blame]
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +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;
26import org.onlab.packet.VlanId;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080027
28import java.nio.ByteBuffer;
29import java.util.Arrays;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080030import java.util.List;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080031
Jian Li5fc14292015-12-04 11:30:46 -080032import static com.google.common.base.MoreObjects.toStringHelper;
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +020033import 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. Chan7fee36a2014-12-31 00:19:59 +080036/**
37 * Implements ICMPv6 Neighbor Solicitation packet format. (RFC 4861)
38 */
39public class NeighborSolicitation extends BasePacket {
40 public static final byte HEADER_LENGTH = 20; // bytes
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +020041 // Constants for NDP reply
Charles Chan823bda52017-12-01 17:44:35 -080042 protected static final byte NDP_HOP_LIMIT = (byte) 255;
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +020043 protected static final byte RESERVED_CODE = (byte) 0x0;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080044
45 protected byte[] targetAddress = new byte[Ip6Address.BYTE_LENGTH];
46
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080047 private final NeighborDiscoveryOptions options =
48 new NeighborDiscoveryOptions();
49
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080050 /**
51 * Gets target address.
52 *
53 * @return the target IPv6 address
54 */
55 public byte[] getTargetAddress() {
56 return this.targetAddress;
57 }
58
59 /**
60 * Sets target address.
61 *
62 * @param targetAddress the target IPv6 address to set
63 * @return this
64 */
65 public NeighborSolicitation setTargetAddress(final byte[] targetAddress) {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080066 this.targetAddress =
67 Arrays.copyOfRange(targetAddress, 0, Ip6Address.BYTE_LENGTH);
68 return this;
69 }
70
71 /**
72 * Gets the Neighbor Discovery Protocol packet options.
73 *
74 * @return the Neighbor Discovery Protocol packet options
75 */
76 public List<NeighborDiscoveryOptions.Option> getOptions() {
77 return this.options.options();
78 }
79
80 /**
81 * Adds a Neighbor Discovery Protocol packet option.
82 *
83 * @param type the option type
84 * @param data the option data
85 * @return this
86 */
87 public NeighborSolicitation addOption(final byte type,
88 final byte[] data) {
89 this.options.addOption(type, data);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080090 return this;
91 }
92
93 @Override
94 public byte[] serialize() {
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080095 byte[] optionsData = null;
96 if (this.options.hasOptions()) {
97 optionsData = this.options.serialize();
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +080098 }
99
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800100 int optionsLength = 0;
101 if (optionsData != null) {
102 optionsLength = optionsData.length;
Charles M.C. Chan5c0b4762015-01-10 18:38:37 +0800103 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800104
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800105 final byte[] data = new byte[HEADER_LENGTH + optionsLength];
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800106 final ByteBuffer bb = ByteBuffer.wrap(data);
107
108 bb.putInt(0);
109 bb.put(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800110 if (optionsData != null) {
111 bb.put(optionsData);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800112 }
113
114 return data;
115 }
116
117 @Override
118 public IPacket deserialize(byte[] data, int offset, int length) {
119 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
120
121 bb.getInt();
122 bb.get(this.targetAddress, 0, Ip6Address.BYTE_LENGTH);
123
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800124 this.options.deserialize(data, bb.position(),
125 bb.limit() - bb.position());
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800126
127 return this;
128 }
129
130 /*
131 * (non-Javadoc)
132 *
133 * @see java.lang.Object#hashCode()
134 */
135 @Override
136 public int hashCode() {
137 final int prime = 5807;
138 int result = super.hashCode();
139 ByteBuffer bb;
140 bb = ByteBuffer.wrap(this.targetAddress);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800141 for (int i = 0; i < this.targetAddress.length / 4; i++) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800142 result = prime * result + bb.getInt();
143 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800144 result = prime * result + this.options.hashCode();
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800145 return result;
146 }
147
148 /*
149 * (non-Javadoc)
150 *
151 * @see java.lang.Object#equals(java.lang.Object)
152 */
153 @Override
154 public boolean equals(final Object obj) {
155 if (this == obj) {
156 return true;
157 }
158 if (!super.equals(obj)) {
159 return false;
160 }
161 if (!(obj instanceof NeighborSolicitation)) {
162 return false;
163 }
164 final NeighborSolicitation other = (NeighborSolicitation) obj;
165 if (!Arrays.equals(this.targetAddress, other.targetAddress)) {
166 return false;
167 }
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800168 if (!this.options.equals(other.options)) {
169 return false;
170 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800171 return true;
172 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700173
174 /**
175 * Deserializer function for neighbor solicitation packets.
176 *
177 * @return deserializer function
178 */
179 public static Deserializer<NeighborSolicitation> deserializer() {
180 return (data, offset, length) -> {
181 checkInput(data, offset, length, HEADER_LENGTH);
182
183 NeighborSolicitation neighborSolicitation = new NeighborSolicitation();
184
185 ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
186
187 bb.getInt();
188 bb.get(neighborSolicitation.targetAddress, 0, Ip6Address.BYTE_LENGTH);
189
Charles Chan3599d632015-09-05 14:47:51 +0800190 if (bb.limit() - bb.position() > 0) {
191 NeighborDiscoveryOptions options = NeighborDiscoveryOptions.deserializer()
192 .deserialize(data, bb.position(), bb.limit() - bb.position());
Jonathan Hart2a655752015-04-07 16:46:33 -0700193
Charles Chan3599d632015-09-05 14:47:51 +0800194 for (NeighborDiscoveryOptions.Option option : options.options()) {
195 neighborSolicitation.addOption(option.type(), option.data());
196 }
Jonathan Hart2a655752015-04-07 16:46:33 -0700197 }
198
199 return neighborSolicitation;
200 };
201 }
Jian Li5fc14292015-12-04 11:30:46 -0800202
203 @Override
204 public String toString() {
205 return toStringHelper(getClass())
206 .add("targetAddress", Arrays.toString(targetAddress))
207 .toString();
208 // TODO: need to handle options
209 }
Pier Ventre78e73f62016-12-02 19:59:28 -0800210
211 /**
212 * Builds a NDP solicitation using the supplied parameters.
213 *
214 * @param targetIp the target ip
215 * @param sourceIp the source ip
216 * @param destinationIp the destination ip
217 * @param sourceMac the source mac address
218 * @param destinationMac the destination mac address
219 * @param vlan the vlan id
220 * @return the ethernet packet containing the ndp solicitation
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200221 * @deprecated since 1.11.0
Pier Ventre78e73f62016-12-02 19:59:28 -0800222 */
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200223 @Deprecated
Pier Ventre78e73f62016-12-02 19:59:28 -0800224 public static Ethernet buildNdpSolicit(byte[] targetIp,
225 byte[] sourceIp,
226 byte[] destinationIp,
227 byte[] sourceMac,
228 byte[] destinationMac,
229 VlanId vlan) {
230
231 if (targetIp.length != Ip6Address.BYTE_LENGTH ||
232 sourceIp.length != Ip6Address.BYTE_LENGTH ||
233 destinationIp.length != Ip6Address.BYTE_LENGTH ||
234 sourceMac.length != MacAddress.MAC_ADDRESS_LENGTH ||
235 destinationMac.length != MacAddress.MAC_ADDRESS_LENGTH) {
236 return null;
237 }
238
Charles Chan4b873892017-05-25 18:30:09 -0700239 // Here we craft the Ethernet packet.
Pier Ventre78e73f62016-12-02 19:59:28 -0800240 Ethernet ethernet = new Ethernet();
241 ethernet.setEtherType(Ethernet.TYPE_IPV6)
242 .setDestinationMACAddress(destinationMac)
243 .setSourceMACAddress(sourceMac);
244 ethernet.setVlanID(vlan.id());
Charles Chan4b873892017-05-25 18:30:09 -0700245 // IPv6 packet is created.
Pier Ventre78e73f62016-12-02 19:59:28 -0800246 IPv6 ipv6 = new IPv6();
247 ipv6.setSourceAddress(sourceIp);
248 ipv6.setDestinationAddress(destinationIp);
249 ipv6.setHopLimit((byte) 255);
Charles Chan4b873892017-05-25 18:30:09 -0700250 // Create the ICMPv6 packet.
Pier Ventre78e73f62016-12-02 19:59:28 -0800251 ICMP6 icmp6 = new ICMP6();
252 icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
253 icmp6.setIcmpCode((byte) 0);
Charles Chan4b873892017-05-25 18:30:09 -0700254 // Create the Neighbor Solicitation packet.
Pier Ventre78e73f62016-12-02 19:59:28 -0800255 NeighborSolicitation ns = new NeighborSolicitation();
256 ns.setTargetAddress(targetIp);
Charles Chan4b873892017-05-25 18:30:09 -0700257 // DAD packets should not contain SRC_LL_ADDR option
258 if (!Arrays.equals(sourceIp, Ip6Address.ZERO.toOctets())) {
259 ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS, sourceMac);
260 }
261 // Set the payloads
Pier Ventre78e73f62016-12-02 19:59:28 -0800262 icmp6.setPayload(ns);
263 ipv6.setPayload(icmp6);
264 ethernet.setPayload(ipv6);
265
266 return ethernet;
267 }
Bartlomiej Goluszka08537a82017-06-12 15:18:32 +0200268
269 /**
270 * Builds a NDP solicitation using the supplied parameters.
271 *
272 * @param targetIp the target ip
273 * @param sourceIp the source ip
274 * @param destinationIp the destination ip
275 * @param sourceMac the source mac address
276 * @param destinationMac the destination mac address
277 * @param vlan the vlan id
278 * @return the ethernet packet containing the ndp solicitation
279 */
280 public static Ethernet buildNdpSolicit(Ip6Address targetIp,
281 Ip6Address sourceIp,
282 Ip6Address destinationIp,
283 MacAddress sourceMac,
284 MacAddress destinationMac,
285 VlanId vlan) {
286
287 checkNotNull(targetIp, "Target IP address cannot be null");
288 checkNotNull(sourceIp, "Source IP address cannot be null");
289 checkNotNull(destinationIp, "Destination IP address cannot be null");
290 checkNotNull(sourceMac, "Source MAC address cannot be null");
291 checkNotNull(destinationMac, "Destination MAC address cannot be null");
292 checkNotNull(vlan, "Vlan cannot be null");
293
294 // Here we craft the Ethernet packet.
295 Ethernet ethernet = new Ethernet();
296 ethernet.setEtherType(Ethernet.TYPE_IPV6)
297 .setDestinationMACAddress(destinationMac)
298 .setSourceMACAddress(sourceMac);
299 ethernet.setVlanID(vlan.id());
300 // IPv6 packet is created.
301 IPv6 ipv6 = new IPv6();
302 ipv6.setSourceAddress(sourceIp.toOctets());
303 ipv6.setDestinationAddress(destinationIp.toOctets());
304 ipv6.setHopLimit(NDP_HOP_LIMIT);
305 // Create the ICMPv6 packet.
306 ICMP6 icmp6 = new ICMP6();
307 icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
308 icmp6.setIcmpCode(RESERVED_CODE);
309 // Create the Neighbor Solicitation packet.
310 NeighborSolicitation ns = new NeighborSolicitation();
311 ns.setTargetAddress(targetIp.toOctets());
312 // DAD packets should not contain SRC_LL_ADDR option
313 if (!Arrays.equals(sourceIp.toOctets(), Ip6Address.ZERO.toOctets())) {
314 ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS, sourceMac.toBytes());
315 }
316 // Set the payloads
317 icmp6.setPayload(ns);
318 ipv6.setPayload(icmp6);
319 ethernet.setPayload(ipv6);
320
321 return ethernet;
322 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800323}