ProxyArpManager - fix IPv6 ND Adv issue with Option fields and ARP/NDP Reply
with first IP address from Host service
This fixes ONOS-1010
Also, add protocol-related constants to class NeighborAdvertisement.
Change-Id: Iacf9e48a8a03a86e1cc4e89e7e2b2b4ccc4a7e3a
diff --git a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
index 35e0e31..5369d2c 100644
--- a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -25,6 +25,7 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.packet.ARP;
+import org.onlab.packet.Data;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP6;
import org.onlab.packet.IPv6;
@@ -141,22 +142,21 @@
ARP arp = (ARP) eth.getPayload();
checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
checkNotNull(inPort);
+ Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
VlanId vlan = VlanId.vlanId(eth.getVlanID());
// If the request came from outside the network, only reply if it was
// for one of our external addresses.
if (isOutsidePort(inPort)) {
- Ip4Address target =
- Ip4Address.valueOf(arp.getTargetProtocolAddress());
Set<PortAddresses> addressSet =
hostService.getAddressBindingsForPort(inPort);
for (PortAddresses addresses : addressSet) {
for (InterfaceIpAddress ia : addresses.ipAddresses()) {
- if (ia.ipAddress().equals(target)) {
+ if (ia.ipAddress().equals(targetAddress)) {
Ethernet arpReply =
- buildArpReply(target, addresses.mac(), eth);
+ buildArpReply(targetAddress, addresses.mac(), eth);
sendTo(arpReply, inPort);
}
}
@@ -187,8 +187,7 @@
// Continue with normal proxy ARP case
- Set<Host> hosts = hostService.getHostsByIp(
- Ip4Address.valueOf(arp.getTargetProtocolAddress()));
+ Set<Host> hosts = hostService.getHostsByIp(targetAddress);
Host dst = null;
Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
@@ -202,23 +201,19 @@
}
if (src == null || dst == null) {
+ //
+ // The request couldn't be resolved.
+ // Flood the request on all ports except the incoming ports.
+ //
flood(eth, inPort);
return;
}
//
- // TODO find the correct IP address.
- // Right now we use the first IPv4 address that is found.
+ // Reply on the port the request was received on
//
- for (IpAddress ipAddress : dst.ipAddresses()) {
- Ip4Address ip4Address = ipAddress.getIp4Address();
- if (ip4Address != null) {
- Ethernet arpReply = buildArpReply(ip4Address, dst.mac(), eth);
- // TODO: check send status with host service.
- sendTo(arpReply, src.location());
- break;
- }
- }
+ Ethernet arpReply = buildArpReply(targetAddress, dst.mac(), eth);
+ sendTo(arpReply, inPort);
}
private void replyNdp(Ethernet eth, ConnectPoint inPort) {
@@ -226,22 +221,21 @@
IPv6 ipv6 = (IPv6) eth.getPayload();
ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
+ Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress());
VlanId vlan = VlanId.vlanId(eth.getVlanID());
// If the request came from outside the network, only reply if it was
// for one of our external addresses.
if (isOutsidePort(inPort)) {
- Ip6Address target =
- Ip6Address.valueOf(nsol.getTargetAddress());
Set<PortAddresses> addressSet =
hostService.getAddressBindingsForPort(inPort);
for (PortAddresses addresses : addressSet) {
for (InterfaceIpAddress ia : addresses.ipAddresses()) {
- if (ia.ipAddress().equals(target)) {
+ if (ia.ipAddress().equals(targetAddress)) {
Ethernet ndpReply =
- buildNdpReply(target, addresses.mac(), eth);
+ buildNdpReply(targetAddress, addresses.mac(), eth);
sendTo(ndpReply, inPort);
}
}
@@ -272,8 +266,7 @@
// Continue with normal proxy ARP case
- Set<Host> hosts = hostService.getHostsByIp(
- Ip6Address.valueOf(nsol.getTargetAddress()));
+ Set<Host> hosts = hostService.getHostsByIp(targetAddress);
Host dst = null;
Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
@@ -287,23 +280,19 @@
}
if (src == null || dst == null) {
+ //
+ // The request couldn't be resolved.
+ // Flood the request on all ports except the incoming ports.
+ //
flood(eth, inPort);
return;
}
//
- // TODO find the correct IP address.
- // Right now we use the first IPv4 address that is found.
+ // Reply on the port the request was received on
//
- for (IpAddress ipAddress : dst.ipAddresses()) {
- Ip6Address ip6Address = ipAddress.getIp6Address();
- if (ip6Address != null) {
- Ethernet arpReply = buildNdpReply(ip6Address, dst.mac(), eth);
- // TODO: check send status with host service.
- sendTo(arpReply, src.location());
- break;
- }
- }
+ Ethernet ndpReply = buildNdpReply(targetAddress, dst.mac(), eth);
+ sendTo(ndpReply, inPort);
}
@@ -429,7 +418,9 @@
/**
* Flood the arp request at all edges in the network.
- * @param request the arp request.
+ *
+ * @param request the arp request
+ * @param inPort the connect point the arp request was received on
*/
private void flood(Ethernet request, ConnectPoint inPort) {
TrafficTreatment.Builder builder = null;
@@ -519,6 +510,7 @@
arp.setTargetProtocolAddress(((ARP) request.getPayload())
.getSenderProtocolAddress());
arp.setSenderProtocolAddress(srcIp.toInt());
+
eth.setPayload(arp);
return eth;
}
@@ -545,19 +537,32 @@
ipv6.setSourceAddress(srcIp.toOctets());
ipv6.setDestinationAddress(requestIp.getSourceAddress());
ipv6.setHopLimit((byte) 255);
- eth.setPayload(ipv6);
ICMP6 icmp6 = new ICMP6();
icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
icmp6.setIcmpCode((byte) 0);
- ipv6.setPayload(icmp6);
NeighborAdvertisement nadv = new NeighborAdvertisement();
- nadv.setTargetAddress(srcMac.toBytes());
+ nadv.setTargetAddress(srcIp.toOctets());
nadv.setSolicitedFlag((byte) 1);
nadv.setOverrideFlag((byte) 1);
+ byte[] nadvData =
+ new byte[NeighborAdvertisement.OPTION_LENGTH_IEEE802_ADDRESS];
+ ByteBuffer bbNadv = ByteBuffer.wrap(nadvData);
+ byte nadvOptionType =
+ NeighborAdvertisement.OPTION_TYPE_TARGET_LL_ADDRESS;
+ // The Option length in 8-octets units
+ byte nadvOptionLength =
+ (NeighborAdvertisement.OPTION_LENGTH_IEEE802_ADDRESS + 7) / 8;
+ bbNadv.put(nadvOptionType);
+ bbNadv.put(nadvOptionLength);
+ bbNadv.put(srcMac.toBytes());
+ Data nadvPayload = new Data();
+ nadv.setPayload(nadvPayload.deserialize(nadvData, 0, nadvData.length));
icmp6.setPayload(nadv);
+ ipv6.setPayload(icmp6);
+ eth.setPayload(ipv6);
return eth;
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
index 113107a..515dd4e 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ndp/NeighborAdvertisement.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,9 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
-
package org.onlab.packet.ndp;
import org.onlab.packet.BasePacket;
@@ -27,10 +24,16 @@
import java.util.Arrays;
/**
- * Implements ICMPv6 Neighbor Advertisement packet format. (RFC 4861)
+ * Implements ICMPv6 Neighbor Advertisement packet format (RFC 4861).
*/
public class NeighborAdvertisement extends BasePacket {
public static final byte HEADER_LENGTH = 20; // bytes
+ public static final byte OPTION_TYPE_SOURCE_LL_ADDRESS = 1;
+ public static final byte OPTION_TYPE_TARGET_LL_ADDRESS = 2;
+ public static final byte OPTION_TYPE_PREFIX_INFORMATION = 3;
+ public static final byte OPTION_TYPE_REDIRECTED_HEADER = 4;
+ public static final byte OPTION_TYPE_MTU = 5;
+ public static final byte OPTION_LENGTH_IEEE802_ADDRESS = 8;
protected byte routerFlag;
protected byte solicitedFlag;