Thomas Vachuska
Committed by Gerrit Code Review

Adding ability to synchronize topology clusters' broadcast trees.

Proxy ARP now supports deferred ARP replies until instance learns of the subject host location.

Change-Id: Ib3ee97c0812858b5b4972d945e9e6d2bd397d4c5
1 +/*
2 + * Copyright 2015 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 +package org.onosproject.net.proxyarp;
17 +
18 +import org.onosproject.net.ConnectPoint;
19 +import org.onosproject.net.Host;
20 +
21 +import java.nio.ByteBuffer;
22 +
23 +/**
24 + * State distribution mechanism for the proxy ARP service.
25 + */
26 +public interface ProxyArpStore {
27 +
28 + /**
29 + * Forwards an ARP or neighbor solicitation request to its destination.
30 + * Floods at the edg the request if the destination is not known.
31 + *
32 + * @param outPort the port the request was received on
33 + * @param subject subject host
34 + * @param packet an ethernet frame containing an ARP or neighbor
35 + * solicitation request
36 + */
37 + void forward(ConnectPoint outPort, Host subject, ByteBuffer packet);
38 +
39 + /**
40 + * Associates the specified delegate with the store.
41 + *
42 + * @param delegate store delegate
43 + */
44 + void setDelegate(ProxyArpStoreDelegate delegate);
45 +}
1 +/*
2 + * Copyright 2015 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 +package org.onosproject.net.proxyarp;
17 +
18 +import org.onosproject.net.ConnectPoint;
19 +
20 +import java.nio.ByteBuffer;
21 +
22 +/**
23 + * Proxy ARP store delegate.
24 + */
25 +public interface ProxyArpStoreDelegate {
26 +
27 + /**
28 + * Emits ARP or neighbour discovery response packet.
29 + *
30 + * @param outPort output connection point
31 + * @param packet packet to emit
32 + */
33 + void emitResponse(ConnectPoint outPort, ByteBuffer packet);
34 +
35 +}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.common; 16 package org.onosproject.common;
17 17
18 +import com.google.common.base.Function;
18 import com.google.common.base.Supplier; 19 import com.google.common.base.Supplier;
19 import com.google.common.base.Suppliers; 20 import com.google.common.base.Suppliers;
20 import com.google.common.collect.ImmutableMap; 21 import com.google.common.collect.ImmutableMap;
...@@ -77,7 +78,7 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -77,7 +78,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
77 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters; 78 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
78 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints; 79 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
79 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets; 80 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
80 - 81 + private final Function<ConnectPoint, Boolean> broadcastFunction;
81 private final Supplier<ClusterIndexes> clusterIndexes; 82 private final Supplier<ClusterIndexes> clusterIndexes;
82 83
83 /** 84 /**
...@@ -85,9 +86,12 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -85,9 +86,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
85 * 86 *
86 * @param providerId identity of the provider 87 * @param providerId identity of the provider
87 * @param description data describing the new topology 88 * @param description data describing the new topology
89 + * @param broadcastFunction broadcast point function
88 */ 90 */
89 - public DefaultTopology(ProviderId providerId, GraphDescription description) { 91 + public DefaultTopology(ProviderId providerId, GraphDescription description,
92 + Function<ConnectPoint, Boolean> broadcastFunction) {
90 super(providerId); 93 super(providerId);
94 + this.broadcastFunction = broadcastFunction;
91 this.time = description.timestamp(); 95 this.time = description.timestamp();
92 this.creationTime = description.creationTime(); 96 this.creationTime = description.creationTime();
93 97
...@@ -106,6 +110,16 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -106,6 +110,16 @@ public class DefaultTopology extends AbstractModel implements Topology {
106 this.computeCost = Math.max(0, System.nanoTime() - time); 110 this.computeCost = Math.max(0, System.nanoTime() - time);
107 } 111 }
108 112
113 + /**
114 + * Creates a topology descriptor attributed to the specified provider.
115 + *
116 + * @param providerId identity of the provider
117 + * @param description data describing the new topology
118 + */
119 + public DefaultTopology(ProviderId providerId, GraphDescription description) {
120 + this(providerId, description, null);
121 + }
122 +
109 @Override 123 @Override
110 public long time() { 124 public long time() {
111 return time; 125 return time;
...@@ -223,6 +237,10 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -223,6 +237,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
223 * @return true if in broadcast set 237 * @return true if in broadcast set
224 */ 238 */
225 public boolean isBroadcastPoint(ConnectPoint connectPoint) { 239 public boolean isBroadcastPoint(ConnectPoint connectPoint) {
240 + if (broadcastFunction != null) {
241 + return broadcastFunction.apply(connectPoint);
242 + }
243 +
226 // Any non-infrastructure, i.e. edge points are assumed to be OK. 244 // Any non-infrastructure, i.e. edge points are assumed to be OK.
227 if (!isInfrastructure(connectPoint)) { 245 if (!isInfrastructure(connectPoint)) {
228 return true; 246 return true;
......
...@@ -36,7 +36,6 @@ import org.onlab.packet.ndp.NeighborSolicitation; ...@@ -36,7 +36,6 @@ import org.onlab.packet.ndp.NeighborSolicitation;
36 import org.onosproject.core.Permission; 36 import org.onosproject.core.Permission;
37 import org.onosproject.net.ConnectPoint; 37 import org.onosproject.net.ConnectPoint;
38 import org.onosproject.net.Host; 38 import org.onosproject.net.Host;
39 -import org.onosproject.net.HostId;
40 import org.onosproject.net.device.DeviceService; 39 import org.onosproject.net.device.DeviceService;
41 import org.onosproject.net.edge.EdgePortService; 40 import org.onosproject.net.edge.EdgePortService;
42 import org.onosproject.net.flow.DefaultTrafficTreatment; 41 import org.onosproject.net.flow.DefaultTrafficTreatment;
...@@ -50,6 +49,7 @@ import org.onosproject.net.packet.InboundPacket; ...@@ -50,6 +49,7 @@ import org.onosproject.net.packet.InboundPacket;
50 import org.onosproject.net.packet.PacketContext; 49 import org.onosproject.net.packet.PacketContext;
51 import org.onosproject.net.packet.PacketService; 50 import org.onosproject.net.packet.PacketService;
52 import org.onosproject.net.proxyarp.ProxyArpService; 51 import org.onosproject.net.proxyarp.ProxyArpService;
52 +import org.onosproject.net.proxyarp.ProxyArpStore;
53 import org.slf4j.Logger; 53 import org.slf4j.Logger;
54 54
55 import java.nio.ByteBuffer; 55 import java.nio.ByteBuffer;
...@@ -59,6 +59,8 @@ import java.util.stream.Collectors; ...@@ -59,6 +59,8 @@ import java.util.stream.Collectors;
59 59
60 import static com.google.common.base.Preconditions.checkArgument; 60 import static com.google.common.base.Preconditions.checkArgument;
61 import static com.google.common.base.Preconditions.checkNotNull; 61 import static com.google.common.base.Preconditions.checkNotNull;
62 +import static org.onlab.packet.VlanId.vlanId;
63 +import static org.onosproject.net.HostId.hostId;
62 import static org.onosproject.security.AppGuard.checkPermission; 64 import static org.onosproject.security.AppGuard.checkPermission;
63 import static org.slf4j.LoggerFactory.getLogger; 65 import static org.slf4j.LoggerFactory.getLogger;
64 66
...@@ -90,25 +92,29 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -90,25 +92,29 @@ public class ProxyArpManager implements ProxyArpService {
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DeviceService deviceService; 93 protected DeviceService deviceService;
92 94
95 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 + protected ProxyArpStore store;
97 +
93 /** 98 /**
94 * Listens to both device service and link service to determine 99 * Listens to both device service and link service to determine
95 * whether a port is internal or external. 100 * whether a port is internal or external.
96 */ 101 */
97 @Activate 102 @Activate
98 public void activate() { 103 public void activate() {
104 + store.setDelegate(this::sendTo);
99 log.info("Started"); 105 log.info("Started");
100 } 106 }
101 107
102 108
103 @Deactivate 109 @Deactivate
104 public void deactivate() { 110 public void deactivate() {
111 + store.setDelegate(null);
105 log.info("Stopped"); 112 log.info("Stopped");
106 } 113 }
107 114
108 @Override 115 @Override
109 public boolean isKnown(IpAddress addr) { 116 public boolean isKnown(IpAddress addr) {
110 checkPermission(Permission.PACKET_READ); 117 checkPermission(Permission.PACKET_READ);
111 -
112 checkNotNull(addr, MAC_ADDR_NULL); 118 checkNotNull(addr, MAC_ADDR_NULL);
113 Set<Host> hosts = hostService.getHostsByIp(addr); 119 Set<Host> hosts = hostService.getHostsByIp(addr);
114 return !hosts.isEmpty(); 120 return !hosts.isEmpty();
...@@ -117,7 +123,6 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -117,7 +123,6 @@ public class ProxyArpManager implements ProxyArpService {
117 @Override 123 @Override
118 public void reply(Ethernet eth, ConnectPoint inPort) { 124 public void reply(Ethernet eth, ConnectPoint inPort) {
119 checkPermission(Permission.PACKET_WRITE); 125 checkPermission(Permission.PACKET_WRITE);
120 -
121 checkNotNull(eth, REQUEST_NULL); 126 checkNotNull(eth, REQUEST_NULL);
122 127
123 if (eth.getEtherType() == Ethernet.TYPE_ARP) { 128 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
...@@ -133,7 +138,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -133,7 +138,7 @@ public class ProxyArpManager implements ProxyArpService {
133 checkNotNull(inPort); 138 checkNotNull(inPort);
134 Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress()); 139 Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
135 140
136 - VlanId vlan = VlanId.vlanId(eth.getVlanID()); 141 + VlanId vlan = vlanId(eth.getVlanID());
137 142
138 if (isOutsidePort(inPort)) { 143 if (isOutsidePort(inPort)) {
139 // If the request came from outside the network, only reply if it was 144 // If the request came from outside the network, only reply if it was
...@@ -158,8 +163,8 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -158,8 +163,8 @@ public class ProxyArpManager implements ProxyArpService {
158 Set<Host> hosts = hostService.getHostsByIp(targetAddress); 163 Set<Host> hosts = hostService.getHostsByIp(targetAddress);
159 164
160 Host dst = null; 165 Host dst = null;
161 - Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(), 166 + Host src = hostService.getHost(hostId(eth.getSourceMAC(),
162 - VlanId.vlanId(eth.getVlanID()))); 167 + vlanId(eth.getVlanID())));
163 168
164 for (Host host : hosts) { 169 for (Host host : hosts) {
165 if (host.vlan().equals(vlan)) { 170 if (host.vlan().equals(vlan)) {
...@@ -202,17 +207,15 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -202,17 +207,15 @@ public class ProxyArpManager implements ProxyArpService {
202 // Flood the request on all ports except the incoming port. 207 // Flood the request on all ports except the incoming port.
203 // 208 //
204 flood(eth, inPort); 209 flood(eth, inPort);
205 - return;
206 } 210 }
207 211
208 private void replyNdp(Ethernet eth, ConnectPoint inPort) { 212 private void replyNdp(Ethernet eth, ConnectPoint inPort) {
209 -
210 IPv6 ipv6 = (IPv6) eth.getPayload(); 213 IPv6 ipv6 = (IPv6) eth.getPayload();
211 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload(); 214 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
212 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload(); 215 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
213 Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress()); 216 Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress());
214 217
215 - VlanId vlan = VlanId.vlanId(eth.getVlanID()); 218 + VlanId vlan = vlanId(eth.getVlanID());
216 219
217 // If the request came from outside the network, only reply if it was 220 // If the request came from outside the network, only reply if it was
218 // for one of our external addresses. 221 // for one of our external addresses.
...@@ -259,8 +262,8 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -259,8 +262,8 @@ public class ProxyArpManager implements ProxyArpService {
259 Set<Host> hosts = hostService.getHostsByIp(targetAddress); 262 Set<Host> hosts = hostService.getHostsByIp(targetAddress);
260 263
261 Host dst = null; 264 Host dst = null;
262 - Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(), 265 + Host src = hostService.getHost(hostId(eth.getSourceMAC(),
263 - VlanId.vlanId(eth.getVlanID()))); 266 + vlanId(eth.getVlanID())));
264 267
265 for (Host host : hosts) { 268 for (Host host : hosts) {
266 if (host.vlan().equals(vlan)) { 269 if (host.vlan().equals(vlan)) {
...@@ -293,6 +296,10 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -293,6 +296,10 @@ public class ProxyArpManager implements ProxyArpService {
293 * @param outPort the port to send it out 296 * @param outPort the port to send it out
294 */ 297 */
295 private void sendTo(Ethernet packet, ConnectPoint outPort) { 298 private void sendTo(Ethernet packet, ConnectPoint outPort) {
299 + sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
300 + }
301 +
302 + private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
296 if (!edgeService.isEdgePoint(outPort)) { 303 if (!edgeService.isEdgePoint(outPort)) {
297 // Sanity check to make sure we don't send the packet out an 304 // Sanity check to make sure we don't send the packet out an
298 // internal port and create a loop (could happen due to 305 // internal port and create a loop (could happen due to
...@@ -303,7 +310,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -303,7 +310,7 @@ public class ProxyArpManager implements ProxyArpService {
303 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); 310 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
304 builder.setOutput(outPort.port()); 311 builder.setOutput(outPort.port());
305 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), 312 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
306 - builder.build(), ByteBuffer.wrap(packet.serialize()))); 313 + builder.build(), packet));
307 } 314 }
308 315
309 /** 316 /**
...@@ -329,31 +336,25 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -329,31 +336,25 @@ public class ProxyArpManager implements ProxyArpService {
329 * @return true if the port is an outside-facing port, otherwise false 336 * @return true if the port is an outside-facing port, otherwise false
330 */ 337 */
331 private boolean isOutsidePort(ConnectPoint port) { 338 private boolean isOutsidePort(ConnectPoint port) {
332 - // 339 + // TODO: Is this sufficient to identify outside-facing ports: just having IP addresses on a port?
333 - // TODO: Is this sufficient to identify outside-facing ports: just
334 - // having IP addresses on a port?
335 - //
336 return !hostService.getAddressBindingsForPort(port).isEmpty(); 340 return !hostService.getAddressBindingsForPort(port).isEmpty();
337 } 341 }
338 342
339 @Override 343 @Override
340 public void forward(Ethernet eth, ConnectPoint inPort) { 344 public void forward(Ethernet eth, ConnectPoint inPort) {
341 checkPermission(Permission.PACKET_WRITE); 345 checkPermission(Permission.PACKET_WRITE);
342 -
343 checkNotNull(eth, REQUEST_NULL); 346 checkNotNull(eth, REQUEST_NULL);
344 347
345 - Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(), 348 + Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
346 - VlanId.vlanId(eth.getVlanID()))); 349 + vlanId(eth.getVlanID())));
347 350
348 if (h == null) { 351 if (h == null) {
349 flood(eth, inPort); 352 flood(eth, inPort);
350 } else { 353 } else {
351 - TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); 354 + Host subject = hostService.getHost(hostId(eth.getSourceMAC(),
352 - builder.setOutput(h.location().port()); 355 + vlanId(eth.getVlanID())));
353 - packetService.emit(new DefaultOutboundPacket(h.location().deviceId(), 356 + store.forward(h.location(), subject, ByteBuffer.wrap(eth.serialize()));
354 - builder.build(), ByteBuffer.wrap(eth.serialize())));
355 } 357 }
356 -
357 } 358 }
358 359
359 @Override 360 @Override
...@@ -439,7 +440,6 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -439,7 +440,6 @@ public class ProxyArpManager implements ProxyArpService {
439 */ 440 */
440 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac, 441 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
441 Ethernet request) { 442 Ethernet request) {
442 -
443 Ethernet eth = new Ethernet(); 443 Ethernet eth = new Ethernet();
444 eth.setDestinationMACAddress(request.getSourceMAC()); 444 eth.setDestinationMACAddress(request.getSourceMAC());
445 eth.setSourceMACAddress(srcMac); 445 eth.setSourceMACAddress(srcMac);
......
...@@ -38,6 +38,8 @@ import org.onosproject.net.PortNumber; ...@@ -38,6 +38,8 @@ import org.onosproject.net.PortNumber;
38 import org.onosproject.net.device.DeviceListener; 38 import org.onosproject.net.device.DeviceListener;
39 import org.onosproject.net.device.DeviceService; 39 import org.onosproject.net.device.DeviceService;
40 import org.onosproject.net.edgeservice.impl.EdgeManager; 40 import org.onosproject.net.edgeservice.impl.EdgeManager;
41 +import org.onosproject.net.flow.DefaultTrafficTreatment;
42 +import org.onosproject.net.flow.TrafficTreatment;
41 import org.onosproject.net.flow.instructions.Instruction; 43 import org.onosproject.net.flow.instructions.Instruction;
42 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; 44 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
43 import org.onosproject.net.host.HostService; 45 import org.onosproject.net.host.HostService;
...@@ -45,24 +47,22 @@ import org.onosproject.net.host.InterfaceIpAddress; ...@@ -45,24 +47,22 @@ import org.onosproject.net.host.InterfaceIpAddress;
45 import org.onosproject.net.host.PortAddresses; 47 import org.onosproject.net.host.PortAddresses;
46 import org.onosproject.net.link.LinkListener; 48 import org.onosproject.net.link.LinkListener;
47 import org.onosproject.net.link.LinkService; 49 import org.onosproject.net.link.LinkService;
50 +import org.onosproject.net.packet.DefaultOutboundPacket;
48 import org.onosproject.net.packet.OutboundPacket; 51 import org.onosproject.net.packet.OutboundPacket;
49 import org.onosproject.net.packet.PacketServiceAdapter; 52 import org.onosproject.net.packet.PacketServiceAdapter;
50 import org.onosproject.net.provider.ProviderId; 53 import org.onosproject.net.provider.ProviderId;
54 +import org.onosproject.net.proxyarp.ProxyArpStore;
55 +import org.onosproject.net.proxyarp.ProxyArpStoreDelegate;
51 56
57 +import java.nio.ByteBuffer;
52 import java.util.ArrayList; 58 import java.util.ArrayList;
53 import java.util.Collections; 59 import java.util.Collections;
54 import java.util.Comparator; 60 import java.util.Comparator;
55 import java.util.List; 61 import java.util.List;
56 import java.util.Set; 62 import java.util.Set;
57 63
58 -import static org.easymock.EasyMock.anyObject; 64 +import static org.easymock.EasyMock.*;
59 -import static org.easymock.EasyMock.createMock; 65 +import static org.junit.Assert.*;
60 -import static org.easymock.EasyMock.expect;
61 -import static org.easymock.EasyMock.replay;
62 -import static org.junit.Assert.assertArrayEquals;
63 -import static org.junit.Assert.assertEquals;
64 -import static org.junit.Assert.assertFalse;
65 -import static org.junit.Assert.assertTrue;
66 66
67 /** 67 /**
68 * Tests for the {@link ProxyArpManager} class. 68 * Tests for the {@link ProxyArpManager} class.
...@@ -110,6 +110,7 @@ public class ProxyArpManagerTest { ...@@ -110,6 +110,7 @@ public class ProxyArpManagerTest {
110 proxyArp = new ProxyArpManager(); 110 proxyArp = new ProxyArpManager();
111 packetService = new TestPacketService(); 111 packetService = new TestPacketService();
112 proxyArp.packetService = packetService; 112 proxyArp.packetService = packetService;
113 + proxyArp.store = new TestProxyArpStoreAdapter();
113 114
114 proxyArp.edgeService = new TestEdgePortService(); 115 proxyArp.edgeService = new TestEdgePortService();
115 116
...@@ -455,8 +456,11 @@ public class ProxyArpManagerTest { ...@@ -455,8 +456,11 @@ public class ProxyArpManagerTest {
455 public void testForwardToHost() { 456 public void testForwardToHost() {
456 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1, 457 Host host1 = new DefaultHost(PID, HID1, MAC1, VLAN1, LOC1,
457 Collections.singleton(IP1)); 458 Collections.singleton(IP1));
459 + Host host2 = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC2,
460 + Collections.singleton(IP2));
458 461
459 expect(hostService.getHost(HID1)).andReturn(host1); 462 expect(hostService.getHost(HID1)).andReturn(host1);
463 + expect(hostService.getHost(HID2)).andReturn(host2);
460 replay(hostService); 464 replay(hostService);
461 465
462 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1); 466 Ethernet arpRequest = buildArp(ARP.OP_REPLY, MAC2, MAC1, IP2, IP1);
...@@ -625,4 +629,16 @@ public class ProxyArpManagerTest { ...@@ -625,4 +629,16 @@ public class ProxyArpManagerTest {
625 return getEdgePointsNoArg; 629 return getEdgePointsNoArg;
626 } 630 }
627 } 631 }
632 +
633 + private class TestProxyArpStoreAdapter implements ProxyArpStore {
634 + @Override
635 + public void forward(ConnectPoint outPort, Host subject, ByteBuffer packet) {
636 + TrafficTreatment tt = DefaultTrafficTreatment.builder().setOutput(outPort.port()).build();
637 + packetService.emit(new DefaultOutboundPacket(outPort.deviceId(), tt, packet));
638 + }
639 +
640 + @Override
641 + public void setDelegate(ProxyArpStoreDelegate delegate) {
642 + }
643 + }
628 } 644 }
......
...@@ -4,6 +4,7 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -4,6 +4,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
4 import static org.onosproject.net.DefaultAnnotations.merge; 4 import static org.onosproject.net.DefaultAnnotations.merge;
5 import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; 5 import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
6 import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; 6 import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED;
7 +import static org.onosproject.net.host.HostEvent.Type.HOST_UPDATED;
7 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT; 8 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
8 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE; 9 import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE;
9 import static org.slf4j.LoggerFactory.getLogger; 10 import static org.slf4j.LoggerFactory.getLogger;
...@@ -246,14 +247,17 @@ public class ECHostStore ...@@ -246,14 +247,17 @@ public class ECHostStore
246 } 247 }
247 248
248 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> { 249 private class HostLocationTracker implements EventuallyConsistentMapListener<HostId, DefaultHost> {
249 -
250 @Override 250 @Override
251 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) { 251 public void event(EventuallyConsistentMapEvent<HostId, DefaultHost> event) {
252 DefaultHost host = checkNotNull(event.value()); 252 DefaultHost host = checkNotNull(event.value());
253 if (event.type() == PUT) { 253 if (event.type() == PUT) {
254 - locations.put(host.location(), host); 254 + boolean isNew = locations.put(host.location(), host);
255 + notifyDelegate(new HostEvent(isNew ? HOST_ADDED : HOST_UPDATED, host));
255 } else if (event.type() == REMOVE) { 256 } else if (event.type() == REMOVE) {
256 - locations.remove(host.location(), host); 257 + if (locations.remove(host.location(), host)) {
258 + notifyDelegate(new HostEvent(HOST_REMOVED, host));
259 + }
260 +
257 } 261 }
258 } 262 }
259 } 263 }
......
1 +/*
2 + * Copyright 2015 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 +package org.onosproject.store.proxyarp.impl;
17 +
18 +import com.google.common.collect.Maps;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Reference;
23 +import org.apache.felix.scr.annotations.ReferenceCardinality;
24 +import org.apache.felix.scr.annotations.Service;
25 +import org.onlab.util.KryoNamespace;
26 +import org.onosproject.cluster.ClusterService;
27 +import org.onosproject.cluster.NodeId;
28 +import org.onosproject.mastership.MastershipService;
29 +import org.onosproject.net.ConnectPoint;
30 +import org.onosproject.net.Host;
31 +import org.onosproject.net.HostId;
32 +import org.onosproject.net.host.HostEvent;
33 +import org.onosproject.net.host.HostListener;
34 +import org.onosproject.net.host.HostService;
35 +import org.onosproject.net.proxyarp.ProxyArpStore;
36 +import org.onosproject.net.proxyarp.ProxyArpStoreDelegate;
37 +import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
38 +import org.onosproject.store.cluster.messaging.MessageSubject;
39 +import org.onosproject.store.serializers.KryoNamespaces;
40 +import org.onosproject.store.serializers.KryoSerializer;
41 +import org.slf4j.Logger;
42 +import org.slf4j.LoggerFactory;
43 +
44 +import java.nio.ByteBuffer;
45 +import java.util.Map;
46 +import java.util.concurrent.ExecutorService;
47 +
48 +import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
49 +import static org.onlab.util.Tools.groupedThreads;
50 +
51 +/**
52 + * Implementation of proxy ARP distribution mechanism.
53 + */
54 +@Component(immediate = true)
55 +@Service
56 +public class DistributedProxyArpStore implements ProxyArpStore {
57 +
58 + private Logger log = LoggerFactory.getLogger(getClass());
59 +
60 + private static final MessageSubject ARP_RESPONSE_MESSAGE =
61 + new MessageSubject("onos-arp-response");
62 +
63 + protected final KryoSerializer serializer = new KryoSerializer() {
64 + @Override
65 + protected void setupKryoPool() {
66 + serializerPool = KryoNamespace.newBuilder()
67 + .register(KryoNamespaces.API)
68 + .register(ArpResponseMessage.class)
69 + .register(ByteBuffer.class)
70 + .build();
71 + }
72 + };
73 +
74 + private ProxyArpStoreDelegate delegate;
75 +
76 + private Map<HostId, ArpResponseMessage> pendingMessages = Maps.newConcurrentMap();
77 +
78 + private ExecutorService executor =
79 + newFixedThreadPool(4, groupedThreads("onos/arp", "sender-%d"));
80 +
81 + private NodeId localNodeId;
82 +
83 + private HostListener hostListener = new InternalHostListener();
84 +
85 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 + protected MastershipService mastershipService;
87 +
88 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 + protected ClusterService clusterService;
90 +
91 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 + protected ClusterCommunicationService commService;
93 +
94 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 + protected HostService hostService;
96 +
97 +
98 + @Activate
99 + protected void activate() {
100 + localNodeId = clusterService.getLocalNode().id();
101 + hostService.addListener(hostListener);
102 + commService.addSubscriber(ARP_RESPONSE_MESSAGE, serializer::decode,
103 + this::processArpResponse, executor);
104 + log.info("Started");
105 + }
106 +
107 + @Deactivate
108 + protected void deactivate() {
109 + commService.removeSubscriber(ARP_RESPONSE_MESSAGE);
110 + hostService.removeListener(hostListener);
111 + log.info("Stopped");
112 + }
113 +
114 + @Override
115 + public void forward(ConnectPoint outPort, Host subject, ByteBuffer packet) {
116 + NodeId nodeId = mastershipService.getMasterFor(outPort.deviceId());
117 + if (nodeId.equals(localNodeId)) {
118 + if (delegate != null) {
119 + delegate.emitResponse(outPort, packet);
120 + }
121 + } else {
122 + log.info("Forwarding ARP response from {} to {}", subject.id(), outPort);
123 + commService.unicast(new ArpResponseMessage(outPort, subject, packet.array()),
124 + ARP_RESPONSE_MESSAGE, serializer::encode, nodeId);
125 + }
126 + }
127 +
128 + @Override
129 + public void setDelegate(ProxyArpStoreDelegate delegate) {
130 + this.delegate = delegate;
131 + }
132 +
133 + // Processes the incoming ARP response message.
134 + private void processArpResponse(ArpResponseMessage msg) {
135 + pendingMessages.put(msg.subject.id(), msg);
136 + if (hostService.getHost(msg.subject.id()) != null) {
137 + checkPendingArps(msg.subject.id());
138 + }
139 + // FIXME: figure out pruning so stuff does not build up
140 + }
141 +
142 + // Checks for pending ARP response message for the specified host.
143 + // If one exists, emit response via delegate.
144 + private void checkPendingArps(HostId id) {
145 + ArpResponseMessage msg = pendingMessages.remove(id);
146 + if (msg != null && delegate != null) {
147 + log.info("Emitting ARP response from {} to {}", id, msg.outPort);
148 + delegate.emitResponse(msg.outPort, ByteBuffer.wrap(msg.packet));
149 + }
150 + }
151 +
152 + // Message carrying an ARP response.
153 + private static class ArpResponseMessage {
154 + private ConnectPoint outPort;
155 + private Host subject;
156 + private byte[] packet;
157 +
158 + public ArpResponseMessage(ConnectPoint outPort, Host subject, byte[] packet) {
159 + this.outPort = outPort;
160 + this.subject = subject;
161 + this.packet = packet;
162 + }
163 +
164 + private ArpResponseMessage() {
165 + }
166 + }
167 +
168 + private class InternalHostListener implements HostListener {
169 + @Override
170 + public void event(HostEvent event) {
171 + checkPendingArps(event.subject().id());
172 + }
173 + }
174 +}
...@@ -16,19 +16,25 @@ ...@@ -16,19 +16,25 @@
16 package org.onosproject.store.topology.impl; 16 package org.onosproject.store.topology.impl;
17 17
18 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkArgument;
19 +import static org.onlab.util.Tools.isNullOrEmpty;
19 import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED; 20 import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED;
20 import static org.slf4j.LoggerFactory.getLogger; 21 import static org.slf4j.LoggerFactory.getLogger;
21 22
22 import java.util.Collections; 23 import java.util.Collections;
23 import java.util.List; 24 import java.util.List;
24 import java.util.Set; 25 import java.util.Set;
26 +import java.util.stream.Collectors;
25 27
26 import org.apache.felix.scr.annotations.Activate; 28 import org.apache.felix.scr.annotations.Activate;
27 import org.apache.felix.scr.annotations.Component; 29 import org.apache.felix.scr.annotations.Component;
28 import org.apache.felix.scr.annotations.Deactivate; 30 import org.apache.felix.scr.annotations.Deactivate;
31 +import org.apache.felix.scr.annotations.Reference;
32 +import org.apache.felix.scr.annotations.ReferenceCardinality;
29 import org.apache.felix.scr.annotations.Service; 33 import org.apache.felix.scr.annotations.Service;
34 +import org.onlab.util.KryoNamespace;
30 import org.onosproject.common.DefaultTopology; 35 import org.onosproject.common.DefaultTopology;
31 import org.onosproject.event.Event; 36 import org.onosproject.event.Event;
37 +import org.onosproject.mastership.MastershipService;
32 import org.onosproject.net.ConnectPoint; 38 import org.onosproject.net.ConnectPoint;
33 import org.onosproject.net.Device; 39 import org.onosproject.net.Device;
34 import org.onosproject.net.DeviceId; 40 import org.onosproject.net.DeviceId;
...@@ -46,6 +52,12 @@ import org.onosproject.net.topology.TopologyGraph; ...@@ -46,6 +52,12 @@ import org.onosproject.net.topology.TopologyGraph;
46 import org.onosproject.net.topology.TopologyStore; 52 import org.onosproject.net.topology.TopologyStore;
47 import org.onosproject.net.topology.TopologyStoreDelegate; 53 import org.onosproject.net.topology.TopologyStoreDelegate;
48 import org.onosproject.store.AbstractStore; 54 import org.onosproject.store.AbstractStore;
55 +import org.onosproject.store.serializers.KryoNamespaces;
56 +import org.onosproject.store.service.EventuallyConsistentMap;
57 +import org.onosproject.store.service.EventuallyConsistentMapEvent;
58 +import org.onosproject.store.service.EventuallyConsistentMapListener;
59 +import org.onosproject.store.service.LogicalClockService;
60 +import org.onosproject.store.service.StorageService;
49 import org.slf4j.Logger; 61 import org.slf4j.Logger;
50 62
51 /** 63 /**
...@@ -69,13 +81,41 @@ public class DistributedTopologyStore ...@@ -69,13 +81,41 @@ public class DistributedTopologyStore
69 Collections.<Device>emptyList(), 81 Collections.<Device>emptyList(),
70 Collections.<Link>emptyList())); 82 Collections.<Link>emptyList()));
71 83
84 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 + protected StorageService storageService;
86 +
87 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 + protected LogicalClockService clockService;
89 +
90 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 + protected MastershipService mastershipService;
92 +
93 + // Cluster root to broadcast points bindings to allow convergence to
94 + // a shared broadcast tree; node that is the master of the cluster root
95 + // is the primary.
96 + private EventuallyConsistentMap<DeviceId, Set<ConnectPoint>> broadcastPoints;
97 +
98 + private EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> listener =
99 + new InternalBroadcastPointListener();
100 +
72 @Activate 101 @Activate
73 public void activate() { 102 public void activate() {
103 + KryoNamespace.Builder hostSerializer = KryoNamespace.newBuilder()
104 + .register(KryoNamespaces.API);
105 +
106 + broadcastPoints = storageService.<DeviceId, Set<ConnectPoint>>eventuallyConsistentMapBuilder()
107 + .withName("onos-broadcast-trees")
108 + .withSerializer(hostSerializer)
109 + .withTimestampProvider((k, v) -> clockService.getTimestamp())
110 + .build();
111 + broadcastPoints.addListener(listener);
74 log.info("Started"); 112 log.info("Started");
75 } 113 }
76 114
77 @Deactivate 115 @Deactivate
78 public void deactivate() { 116 public void deactivate() {
117 + broadcastPoints.removeListener(listener);
118 + broadcastPoints.destroy();
79 log.info("Stopped"); 119 log.info("Stopped");
80 } 120 }
81 121
...@@ -136,6 +176,22 @@ public class DistributedTopologyStore ...@@ -136,6 +176,22 @@ public class DistributedTopologyStore
136 return defaultTopology(topology).isBroadcastPoint(connectPoint); 176 return defaultTopology(topology).isBroadcastPoint(connectPoint);
137 } 177 }
138 178
179 + private boolean isBroadcastPoint(ConnectPoint connectPoint) {
180 + // Any non-infrastructure, i.e. edge points are assumed to be OK.
181 + if (!current.isInfrastructure(connectPoint)) {
182 + return true;
183 + }
184 +
185 + // Find the cluster to which the device belongs.
186 + TopologyCluster cluster = current.getCluster(connectPoint.deviceId());
187 + checkArgument(cluster != null, "No cluster found for device %s", connectPoint.deviceId());
188 +
189 + // If the broadcast set is null or empty, or if the point explicitly
190 + // belongs to it, return true;
191 + Set<ConnectPoint> points = broadcastPoints.get(cluster.root().deviceId());
192 + return isNullOrEmpty(points) || points.contains(connectPoint);
193 + }
194 +
139 @Override 195 @Override
140 public TopologyEvent updateTopology(ProviderId providerId, 196 public TopologyEvent updateTopology(ProviderId providerId,
141 GraphDescription graphDescription, 197 GraphDescription graphDescription,
...@@ -147,7 +203,9 @@ public class DistributedTopologyStore ...@@ -147,7 +203,9 @@ public class DistributedTopologyStore
147 } 203 }
148 204
149 // Have the default topology construct self from the description data. 205 // Have the default topology construct self from the description data.
150 - DefaultTopology newTopology = new DefaultTopology(providerId, graphDescription); 206 + DefaultTopology newTopology =
207 + new DefaultTopology(providerId, graphDescription, this::isBroadcastPoint);
208 + updateBroadcastPoints(newTopology);
151 209
152 // Promote the new topology to current and return a ready-to-send event. 210 // Promote the new topology to current and return a ready-to-send event.
153 synchronized (this) { 211 synchronized (this) {
...@@ -156,6 +214,24 @@ public class DistributedTopologyStore ...@@ -156,6 +214,24 @@ public class DistributedTopologyStore
156 } 214 }
157 } 215 }
158 216
217 + private void updateBroadcastPoints(DefaultTopology topology) {
218 + // Remove any broadcast trees rooted by devices for which we are master.
219 + Set<DeviceId> toRemove = broadcastPoints.keySet().stream()
220 + .filter(mastershipService::isLocalMaster)
221 + .collect(Collectors.toSet());
222 +
223 + // Update the broadcast trees rooted by devices for which we are master.
224 + topology.getClusters().forEach(c -> {
225 + toRemove.remove(c.root().deviceId());
226 + if (mastershipService.isLocalMaster(c.root().deviceId())) {
227 + broadcastPoints.put(c.root().deviceId(),
228 + topology.broadcastPoints(c.id()));
229 + }
230 + });
231 +
232 + toRemove.forEach(broadcastPoints::remove);
233 + }
234 +
159 // Validates the specified topology and returns it as a default 235 // Validates the specified topology and returns it as a default
160 private DefaultTopology defaultTopology(Topology topology) { 236 private DefaultTopology defaultTopology(Topology topology) {
161 checkArgument(topology instanceof DefaultTopology, 237 checkArgument(topology instanceof DefaultTopology,
...@@ -163,4 +239,16 @@ public class DistributedTopologyStore ...@@ -163,4 +239,16 @@ public class DistributedTopologyStore
163 return (DefaultTopology) topology; 239 return (DefaultTopology) topology;
164 } 240 }
165 241
242 + private class InternalBroadcastPointListener
243 + implements EventuallyConsistentMapListener<DeviceId, Set<ConnectPoint>> {
244 + @Override
245 + public void event(EventuallyConsistentMapEvent<DeviceId, Set<ConnectPoint>> event) {
246 + if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
247 + if (!event.value().isEmpty()) {
248 + log.info("Cluster rooted at {} has {} broadcast-points; #{}",
249 + event.key(), event.value().size(), event.value().hashCode());
250 + }
251 + }
252 + }
253 + }
166 } 254 }
......