blob: e7e08fcc353a4ed5a0ba4563699b5eb55b9182a1 [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);
234 arp.setProtocolAddressLength((byte) IpPrefix.INET_LEN);
235 arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
236 arp.setSenderHardwareAddress(h.mac().getAddress());
237 arp.setTargetHardwareAddress(request.getSourceMACAddress());
238
239 arp.setTargetProtocolAddress(((ARP) request.getPayload())
240 .getSenderProtocolAddress());
241 arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toInt());
242 eth.setPayload(arp);
243 return eth;
244 }
245
246 public class InternalLinkListener implements LinkListener {
247
248 @Override
249 public void event(LinkEvent event) {
250 Link link = event.subject();
251 Device src = deviceService.getDevice(link.src().deviceId());
252 Device dst = deviceService.getDevice(link.dst().deviceId());
253 switch (event.type()) {
254 case LINK_ADDED:
255 synchronized (externalPorts) {
256 externalPorts.remove(src, link.src().port());
257 externalPorts.remove(dst, link.dst().port());
258 internalPorts.put(src, link.src().port());
259 internalPorts.put(dst, link.dst().port());
260 }
261
262 break;
263 case LINK_REMOVED:
264 synchronized (externalPorts) {
265 externalPorts.put(src, link.src().port());
266 externalPorts.put(dst, link.dst().port());
267 internalPorts.remove(src, link.src().port());
268 internalPorts.remove(dst, link.dst().port());
269 }
270
271 break;
272 case LINK_UPDATED:
273 // don't care about links being updated.
274 break;
275 default:
276 break;
277 }
278
279 }
280
281 }
282
283 public class InternalDeviceListener implements DeviceListener {
284
285 @Override
286 public void event(DeviceEvent event) {
287 Device device = event.subject();
288 switch (event.type()) {
289 case DEVICE_ADDED:
290 case DEVICE_AVAILABILITY_CHANGED:
291 case DEVICE_MASTERSHIP_CHANGED:
292 case DEVICE_SUSPENDED:
293 case DEVICE_UPDATED:
294 case PORT_UPDATED:
295 // 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:
304 synchronized (externalPorts) {
305 externalPorts.put(device, event.port().number());
306 internalPorts.remove(device, event.port().number());
307 }
308 break;
309 case PORT_REMOVED:
310 synchronized (externalPorts) {
311 externalPorts.remove(device, event.port().number());
312 internalPorts.remove(device, event.port().number());
313 }
314 break;
315 default:
316 break;
317
318 }
319
320 }
321
322}
323
324
325}