blob: ac103849141143ec1960e2da909745ebcf5b554f [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.";
Jonathan Hart704ca142014-10-09 09:34:39 -070058 private static final String NOT_ARP_REPLY = "ARP is not a reply.";
alshabibb5522ff2014-09-29 19:20:00 -070059
60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 protected HostService hostService;
62
63 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 protected PacketService packetService;
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected LinkService linkService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected DeviceService deviceService;
71
72 private final Multimap<Device, PortNumber> internalPorts =
73 HashMultimap.<Device, PortNumber>create();
74
75 private final Multimap<Device, PortNumber> externalPorts =
76 HashMultimap.<Device, PortNumber>create();
77
78 /**
79 * Listens to both device service and link service to determine
80 * whether a port is internal or external.
81 */
82 @Activate
83 public void activate() {
84 deviceService.addListener(new InternalDeviceListener());
85 linkService.addListener(new InternalLinkListener());
86 determinePortLocations();
87 log.info("Started");
88 }
89
90
91 @Deactivate
92 public void deactivate() {
93 log.info("Stopped");
94 }
95
96 @Override
97 public boolean known(IpPrefix addr) {
Yuta HIGUCHI59718042014-10-04 22:04:56 -070098 checkNotNull(addr, MAC_ADDR_NULL);
alshabibb5522ff2014-09-29 19:20:00 -070099 Set<Host> hosts = hostService.getHostsByIp(addr);
100 return !hosts.isEmpty();
101 }
102
103 @Override
104 public void reply(Ethernet eth) {
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700105 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700106 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
107 REQUEST_NOT_ARP);
108 ARP arp = (ARP) eth.getPayload();
109 checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
110
111 VlanId vlan = VlanId.vlanId(eth.getVlanID());
112 Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp
113 .getTargetProtocolAddress()));
114
115 Host dst = null;
116 Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
117 VlanId.vlanId(eth.getVlanID())));
118
119 for (Host host : hosts) {
120 if (host.vlan().equals(vlan)) {
121 dst = host;
122 break;
123 }
124 }
125
126 if (src == null || dst == null) {
127 flood(eth);
128 return;
129 }
130
131 Ethernet arpReply = buildArpReply(dst, eth);
132 // TODO: check send status with host service.
tom9a693fd2014-10-03 11:32:19 -0700133 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700134 builder.setOutput(src.location().port());
135 packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
136 builder.build(), ByteBuffer.wrap(arpReply.serialize())));
137 }
138
139 @Override
140 public void forward(Ethernet eth) {
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700141 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700142 checkArgument(eth.getEtherType() == Ethernet.TYPE_ARP,
143 REQUEST_NOT_ARP);
144 ARP arp = (ARP) eth.getPayload();
Jonathan Hart704ca142014-10-09 09:34:39 -0700145 checkArgument(arp.getOpCode() == ARP.OP_REPLY, NOT_ARP_REPLY);
alshabibb5522ff2014-09-29 19:20:00 -0700146
147 Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(),
148 VlanId.vlanId(eth.getVlanID())));
149
150 if (h == null) {
151 flood(eth);
152 } else {
tom9a693fd2014-10-03 11:32:19 -0700153 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700154 builder.setOutput(h.location().port());
155 packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
156 builder.build(), ByteBuffer.wrap(eth.serialize())));
157 }
158
159 }
160
alshabibc274c902014-10-03 14:58:27 -0700161 @Override
162 public boolean handleArp(PacketContext context) {
163 InboundPacket pkt = context.inPacket();
164 Ethernet ethPkt = pkt.parsed();
165 if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
166 ARP arp = (ARP) ethPkt.getPayload();
167 if (arp.getOpCode() == ARP.OP_REPLY) {
168 forward(ethPkt);
169 } else if (arp.getOpCode() == ARP.OP_REQUEST) {
170 reply(ethPkt);
171 }
172 context.block();
173 return true;
174 }
175 return false;
176 }
177
alshabibb5522ff2014-09-29 19:20:00 -0700178 /**
179 * Flood the arp request at all edges in the network.
180 * @param request the arp request.
181 */
182 private void flood(Ethernet request) {
183 TrafficTreatment.Builder builder = null;
184 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
185
186 synchronized (externalPorts) {
187 for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
tom9a693fd2014-10-03 11:32:19 -0700188 builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700189 builder.setOutput(entry.getValue());
190 packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
191 builder.build(), buf));
192 }
193
194 }
195 }
196
197 /**
198 * Determines the location of all known ports in the system.
199 */
200 private void determinePortLocations() {
201 Iterable<Device> devices = deviceService.getDevices();
202 Iterable<Link> links = null;
203 List<PortNumber> ports = null;
204 for (Device d : devices) {
205 ports = buildPortNumberList(deviceService.getPorts(d.id()));
206 links = linkService.getLinks();
207 for (Link l : links) {
208 // for each link, mark the concerned ports as internal
209 // and the remaining ports are therefore external.
Yuta HIGUCHI3541bf22014-10-04 22:06:19 -0700210 if (l.src().deviceId().equals(d.id())
alshabibb5522ff2014-09-29 19:20:00 -0700211 && ports.contains(l.src().port())) {
212 ports.remove(l.src().port());
213 internalPorts.put(d, l.src().port());
214 }
Yuta HIGUCHI3541bf22014-10-04 22:06:19 -0700215 if (l.dst().deviceId().equals(d.id())
alshabibb5522ff2014-09-29 19:20:00 -0700216 && ports.contains(l.dst().port())) {
217 ports.remove(l.dst().port());
218 internalPorts.put(d, l.dst().port());
219 }
220 }
221 synchronized (externalPorts) {
222 externalPorts.putAll(d, ports);
223 }
224 }
225
226 }
227
228 private List<PortNumber> buildPortNumberList(List<Port> ports) {
229 List<PortNumber> portNumbers = Lists.newLinkedList();
230 for (Port p : ports) {
231 portNumbers.add(p.number());
232 }
233 return portNumbers;
234 }
235
236 /**
237 * Builds an arp reply based on a request.
238 * @param h the host we want to send to
239 * @param request the arp request we got
240 * @return an ethernet frame containing the arp reply
241 */
242 private Ethernet buildArpReply(Host h, Ethernet request) {
243 Ethernet eth = new Ethernet();
244 eth.setDestinationMACAddress(request.getSourceMACAddress());
245 eth.setSourceMACAddress(h.mac().getAddress());
246 eth.setEtherType(Ethernet.TYPE_ARP);
247 eth.setVlanID(request.getVlanID());
248
249 ARP arp = new ARP();
250 arp.setOpCode(ARP.OP_REPLY);
251 arp.setProtocolType(ARP.PROTO_TYPE_IP);
252 arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
alshabib6eb438a2014-10-01 16:39:37 -0700253
alshabibb5522ff2014-09-29 19:20:00 -0700254 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
255 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
256 arp.setSenderHardwareAddress(h.mac().getAddress());
257 arp.setTargetHardwareAddress(request.getSourceMACAddress());
258
259 arp.setTargetProtocolAddress(((ARP) request.getPayload())
260 .getSenderProtocolAddress());
alshabib6eb438a2014-10-01 16:39:37 -0700261 arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toRealInt());
alshabibb5522ff2014-09-29 19:20:00 -0700262 eth.setPayload(arp);
263 return eth;
264 }
265
266 public class InternalLinkListener implements LinkListener {
267
268 @Override
269 public void event(LinkEvent event) {
270 Link link = event.subject();
271 Device src = deviceService.getDevice(link.src().deviceId());
272 Device dst = deviceService.getDevice(link.dst().deviceId());
273 switch (event.type()) {
274 case LINK_ADDED:
275 synchronized (externalPorts) {
276 externalPorts.remove(src, link.src().port());
277 externalPorts.remove(dst, link.dst().port());
278 internalPorts.put(src, link.src().port());
279 internalPorts.put(dst, link.dst().port());
280 }
281
282 break;
283 case LINK_REMOVED:
284 synchronized (externalPorts) {
285 externalPorts.put(src, link.src().port());
286 externalPorts.put(dst, link.dst().port());
287 internalPorts.remove(src, link.src().port());
288 internalPorts.remove(dst, link.dst().port());
289 }
290
291 break;
292 case LINK_UPDATED:
293 // don't care about links being updated.
294 break;
295 default:
296 break;
297 }
298
299 }
300
301 }
302
303 public class InternalDeviceListener implements DeviceListener {
304
305 @Override
306 public void event(DeviceEvent event) {
307 Device device = event.subject();
308 switch (event.type()) {
309 case DEVICE_ADDED:
310 case DEVICE_AVAILABILITY_CHANGED:
311 case DEVICE_MASTERSHIP_CHANGED:
312 case DEVICE_SUSPENDED:
313 case DEVICE_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700314 // nothing to do in these cases; handled when links get reported
315 break;
316 case DEVICE_REMOVED:
317 synchronized (externalPorts) {
318 externalPorts.removeAll(device);
319 internalPorts.removeAll(device);
320 }
321 break;
322 case PORT_ADDED:
alshabib6eb438a2014-10-01 16:39:37 -0700323 case PORT_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700324 synchronized (externalPorts) {
alshabib6eb438a2014-10-01 16:39:37 -0700325 if (event.port().isEnabled()) {
326 externalPorts.put(device, event.port().number());
327 internalPorts.remove(device, event.port().number());
328 }
alshabibb5522ff2014-09-29 19:20:00 -0700329 }
330 break;
331 case PORT_REMOVED:
332 synchronized (externalPorts) {
333 externalPorts.remove(device, event.port().number());
334 internalPorts.remove(device, event.port().number());
335 }
336 break;
337 default:
338 break;
339
340 }
341
342 }
343
alshabibc274c902014-10-03 14:58:27 -0700344 }
alshabibb5522ff2014-09-29 19:20:00 -0700345
346}