blob: 0da27335485136317b66f3ac1484ca4ec602c28d [file] [log] [blame]
alshabibb5522ff2014-09-29 19:20:00 -07001package org.onlab.onos.net.proxyarp.impl;
2
3import static com.google.common.base.Preconditions.checkArgument;
4import static com.google.common.base.Preconditions.checkNotNull;
5import static org.slf4j.LoggerFactory.getLogger;
6
7import java.nio.ByteBuffer;
8import java.util.List;
9import java.util.Map.Entry;
10import java.util.Set;
11
12import org.apache.felix.scr.annotations.Activate;
13import org.apache.felix.scr.annotations.Component;
14import org.apache.felix.scr.annotations.Deactivate;
15import org.apache.felix.scr.annotations.Reference;
16import org.apache.felix.scr.annotations.ReferenceCardinality;
17import org.apache.felix.scr.annotations.Service;
18import org.onlab.onos.net.Device;
19import org.onlab.onos.net.Host;
20import org.onlab.onos.net.HostId;
21import org.onlab.onos.net.Link;
22import org.onlab.onos.net.Port;
23import org.onlab.onos.net.PortNumber;
24import org.onlab.onos.net.device.DeviceEvent;
25import org.onlab.onos.net.device.DeviceListener;
26import org.onlab.onos.net.device.DeviceService;
27import org.onlab.onos.net.flow.DefaultTrafficTreatment;
28import org.onlab.onos.net.flow.TrafficTreatment;
29import org.onlab.onos.net.host.HostService;
30import org.onlab.onos.net.link.LinkEvent;
31import org.onlab.onos.net.link.LinkListener;
32import org.onlab.onos.net.link.LinkService;
33import org.onlab.onos.net.packet.DefaultOutboundPacket;
alshabibc274c902014-10-03 14:58:27 -070034import org.onlab.onos.net.packet.InboundPacket;
35import org.onlab.onos.net.packet.PacketContext;
alshabibb5522ff2014-09-29 19:20:00 -070036import org.onlab.onos.net.packet.PacketService;
37import org.onlab.onos.net.proxyarp.ProxyArpService;
38import org.onlab.packet.ARP;
39import org.onlab.packet.Ethernet;
40import org.onlab.packet.IpPrefix;
41import org.onlab.packet.VlanId;
42import org.slf4j.Logger;
43
44import com.google.common.collect.HashMultimap;
45import com.google.common.collect.Lists;
46import com.google.common.collect.Multimap;
47
alshabibb5522ff2014-09-29 19:20:00 -070048@Component(immediate = true)
49@Service
50public class ProxyArpManager implements ProxyArpService {
51
52 private final Logger log = getLogger(getClass());
53
54 private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
55 private static final String REQUEST_NULL = "Arp request cannot be null.";
56 private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
57 private static final String NOT_ARP_REQUEST = "ARP is not a request.";
58
59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
60 protected HostService hostService;
61
62 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 protected PacketService packetService;
64
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected LinkService linkService;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected DeviceService deviceService;
70
71 private final Multimap<Device, PortNumber> internalPorts =
72 HashMultimap.<Device, PortNumber>create();
73
74 private final Multimap<Device, PortNumber> externalPorts =
75 HashMultimap.<Device, PortNumber>create();
76
77 /**
78 * Listens to both device service and link service to determine
79 * whether a port is internal or external.
80 */
81 @Activate
82 public void activate() {
83 deviceService.addListener(new InternalDeviceListener());
84 linkService.addListener(new InternalLinkListener());
85 determinePortLocations();
86 log.info("Started");
87 }
88
89
90 @Deactivate
91 public void deactivate() {
92 log.info("Stopped");
93 }
94
95 @Override
96 public boolean known(IpPrefix addr) {
97 checkNotNull(MAC_ADDR_NULL, addr);
98 Set<Host> hosts = hostService.getHostsByIp(addr);
99 return !hosts.isEmpty();
100 }
101
102 @Override
103 public void reply(Ethernet eth) {
104 checkNotNull(REQUEST_NULL, eth);
105 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
106 REQUEST_NOT_ARP);
107 ARP arp = (ARP) eth.getPayload();
108 checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
109
110 VlanId vlan = VlanId.vlanId(eth.getVlanID());
111 Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp
112 .getTargetProtocolAddress()));
113
114 Host dst = null;
115 Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
116 VlanId.vlanId(eth.getVlanID())));
117
118 for (Host host : hosts) {
119 if (host.vlan().equals(vlan)) {
120 dst = host;
121 break;
122 }
123 }
124
125 if (src == null || dst == null) {
126 flood(eth);
127 return;
128 }
129
130 Ethernet arpReply = buildArpReply(dst, eth);
131 // TODO: check send status with host service.
tom9a693fd2014-10-03 11:32:19 -0700132 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700133 builder.setOutput(src.location().port());
134 packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
135 builder.build(), ByteBuffer.wrap(arpReply.serialize())));
136 }
137
138 @Override
139 public void forward(Ethernet eth) {
140 checkNotNull(REQUEST_NULL, eth);
141 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
142 REQUEST_NOT_ARP);
143 ARP arp = (ARP) eth.getPayload();
144 checkArgument(arp.getOpCode() == ARP.OP_REPLY, NOT_ARP_REQUEST);
145
146 Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(),
147 VlanId.vlanId(eth.getVlanID())));
148
149 if (h == null) {
150 flood(eth);
151 } else {
tom9a693fd2014-10-03 11:32:19 -0700152 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700153 builder.setOutput(h.location().port());
154 packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
155 builder.build(), ByteBuffer.wrap(eth.serialize())));
156 }
157
158 }
159
alshabibc274c902014-10-03 14:58:27 -0700160 @Override
161 public boolean handleArp(PacketContext context) {
162 InboundPacket pkt = context.inPacket();
163 Ethernet ethPkt = pkt.parsed();
164 if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
165 ARP arp = (ARP) ethPkt.getPayload();
166 if (arp.getOpCode() == ARP.OP_REPLY) {
167 forward(ethPkt);
168 } else if (arp.getOpCode() == ARP.OP_REQUEST) {
169 reply(ethPkt);
170 }
171 context.block();
172 return true;
173 }
174 return false;
175 }
176
alshabibb5522ff2014-09-29 19:20:00 -0700177 /**
178 * Flood the arp request at all edges in the network.
179 * @param request the arp request.
180 */
181 private void flood(Ethernet request) {
182 TrafficTreatment.Builder builder = null;
183 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
184
185 synchronized (externalPorts) {
186 for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
tom9a693fd2014-10-03 11:32:19 -0700187 builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700188 builder.setOutput(entry.getValue());
189 packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
190 builder.build(), buf));
191 }
192
193 }
194 }
195
196 /**
197 * Determines the location of all known ports in the system.
198 */
199 private void determinePortLocations() {
200 Iterable<Device> devices = deviceService.getDevices();
201 Iterable<Link> links = null;
202 List<PortNumber> ports = null;
203 for (Device d : devices) {
204 ports = buildPortNumberList(deviceService.getPorts(d.id()));
205 links = linkService.getLinks();
206 for (Link l : links) {
207 // for each link, mark the concerned ports as internal
208 // and the remaining ports are therefore external.
209 if (l.src().deviceId().equals(d)
210 && ports.contains(l.src().port())) {
211 ports.remove(l.src().port());
212 internalPorts.put(d, l.src().port());
213 }
214 if (l.dst().deviceId().equals(d)
215 && ports.contains(l.dst().port())) {
216 ports.remove(l.dst().port());
217 internalPorts.put(d, l.dst().port());
218 }
219 }
220 synchronized (externalPorts) {
221 externalPorts.putAll(d, ports);
222 }
223 }
224
225 }
226
227 private List<PortNumber> buildPortNumberList(List<Port> ports) {
228 List<PortNumber> portNumbers = Lists.newLinkedList();
229 for (Port p : ports) {
230 portNumbers.add(p.number());
231 }
232 return portNumbers;
233 }
234
235 /**
236 * Builds an arp reply based on a request.
237 * @param h the host we want to send to
238 * @param request the arp request we got
239 * @return an ethernet frame containing the arp reply
240 */
241 private Ethernet buildArpReply(Host h, Ethernet request) {
242 Ethernet eth = new Ethernet();
243 eth.setDestinationMACAddress(request.getSourceMACAddress());
244 eth.setSourceMACAddress(h.mac().getAddress());
245 eth.setEtherType(Ethernet.TYPE_ARP);
246 eth.setVlanID(request.getVlanID());
247
248 ARP arp = new ARP();
249 arp.setOpCode(ARP.OP_REPLY);
250 arp.setProtocolType(ARP.PROTO_TYPE_IP);
251 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
alshabib6eb438a2014-10-01 16:39:37 -0700252
alshabibb5522ff2014-09-29 19:20:00 -0700253 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
254 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
255 arp.setSenderHardwareAddress(h.mac().getAddress());
256 arp.setTargetHardwareAddress(request.getSourceMACAddress());
257
258 arp.setTargetProtocolAddress(((ARP) request.getPayload())
259 .getSenderProtocolAddress());
alshabib6eb438a2014-10-01 16:39:37 -0700260 arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt());
alshabibb5522ff2014-09-29 19:20:00 -0700261 eth.setPayload(arp);
262 return eth;
263 }
264
265 public class InternalLinkListener implements LinkListener {
266
267 @Override
268 public void event(LinkEvent event) {
269 Link link = event.subject();
270 Device src = deviceService.getDevice(link.src().deviceId());
271 Device dst = deviceService.getDevice(link.dst().deviceId());
272 switch (event.type()) {
273 case LINK_ADDED:
274 synchronized (externalPorts) {
275 externalPorts.remove(src, link.src().port());
276 externalPorts.remove(dst, link.dst().port());
277 internalPorts.put(src, link.src().port());
278 internalPorts.put(dst, link.dst().port());
279 }
280
281 break;
282 case LINK_REMOVED:
283 synchronized (externalPorts) {
284 externalPorts.put(src, link.src().port());
285 externalPorts.put(dst, link.dst().port());
286 internalPorts.remove(src, link.src().port());
287 internalPorts.remove(dst, link.dst().port());
288 }
289
290 break;
291 case LINK_UPDATED:
292 // don't care about links being updated.
293 break;
294 default:
295 break;
296 }
297
298 }
299
300 }
301
302 public class InternalDeviceListener implements DeviceListener {
303
304 @Override
305 public void event(DeviceEvent event) {
306 Device device = event.subject();
307 switch (event.type()) {
308 case DEVICE_ADDED:
309 case DEVICE_AVAILABILITY_CHANGED:
310 case DEVICE_MASTERSHIP_CHANGED:
311 case DEVICE_SUSPENDED:
312 case DEVICE_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700313 // nothing to do in these cases; handled when links get reported
314 break;
315 case DEVICE_REMOVED:
316 synchronized (externalPorts) {
317 externalPorts.removeAll(device);
318 internalPorts.removeAll(device);
319 }
320 break;
321 case PORT_ADDED:
alshabib6eb438a2014-10-01 16:39:37 -0700322 case PORT_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700323 synchronized (externalPorts) {
alshabib6eb438a2014-10-01 16:39:37 -0700324 if (event.port().isEnabled()) {
325 externalPorts.put(device, event.port().number());
326 internalPorts.remove(device, event.port().number());
327 }
alshabibb5522ff2014-09-29 19:20:00 -0700328 }
329 break;
330 case PORT_REMOVED:
331 synchronized (externalPorts) {
332 externalPorts.remove(device, event.port().number());
333 internalPorts.remove(device, event.port().number());
334 }
335 break;
336 default:
337 break;
338
339 }
340
341 }
342
alshabibc274c902014-10-03 14:58:27 -0700343 }
alshabibb5522ff2014-09-29 19:20:00 -0700344
345}