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