blob: 6411a4d5d6193d61c3010f04c51dfef629ddfaa1 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.proxyarp.impl;
alshabibb5522ff2014-09-29 19:20:00 -070017
Jonathan Hart6cd2f352015-01-13 17:44:45 -080018import com.google.common.collect.HashMultimap;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Multimap;
alshabibb5522ff2014-09-29 19:20:00 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
Jonathan Harte8600eb2015-01-12 10:30:45 -080027import org.onlab.packet.ARP;
28import org.onlab.packet.Ethernet;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080029import org.onlab.packet.ICMP6;
30import org.onlab.packet.IPv6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080031import org.onlab.packet.Ip4Address;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080032import org.onlab.packet.Ip6Address;
Jonathan Harte8600eb2015-01-12 10:30:45 -080033import org.onlab.packet.IpAddress;
34import org.onlab.packet.MacAddress;
35import org.onlab.packet.VlanId;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080036import org.onlab.packet.ndp.NeighborAdvertisement;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080037import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080038import org.onlab.packet.ndp.NeighborSolicitation;
Changhoon Yoon541ef712015-05-23 17:18:34 +090039import org.onosproject.core.Permission;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.ConnectPoint;
41import org.onosproject.net.Device;
42import org.onosproject.net.Host;
43import org.onosproject.net.HostId;
44import org.onosproject.net.Link;
45import org.onosproject.net.Port;
46import org.onosproject.net.PortNumber;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
51import org.onosproject.net.flow.TrafficTreatment;
52import org.onosproject.net.host.HostService;
53import org.onosproject.net.host.InterfaceIpAddress;
54import org.onosproject.net.host.PortAddresses;
55import org.onosproject.net.link.LinkEvent;
56import org.onosproject.net.link.LinkListener;
57import org.onosproject.net.link.LinkService;
58import org.onosproject.net.packet.DefaultOutboundPacket;
59import org.onosproject.net.packet.InboundPacket;
60import org.onosproject.net.packet.PacketContext;
61import org.onosproject.net.packet.PacketService;
62import org.onosproject.net.proxyarp.ProxyArpService;
alshabibb5522ff2014-09-29 19:20:00 -070063import org.slf4j.Logger;
64
Jonathan Hart6cd2f352015-01-13 17:44:45 -080065import java.nio.ByteBuffer;
66import java.util.HashSet;
67import java.util.List;
68import java.util.Map.Entry;
69import java.util.Set;
70
71import static com.google.common.base.Preconditions.checkArgument;
72import static com.google.common.base.Preconditions.checkNotNull;
73import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoon541ef712015-05-23 17:18:34 +090074import static org.onosproject.security.AppGuard.checkPermission;
75
alshabibb5522ff2014-09-29 19:20:00 -070076
alshabibb5522ff2014-09-29 19:20:00 -070077@Component(immediate = true)
78@Service
79public class ProxyArpManager implements ProxyArpService {
80
81 private final Logger log = getLogger(getClass());
82
83 private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080084 private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
alshabibb5522ff2014-09-29 19:20:00 -070085 private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
86 private static final String NOT_ARP_REQUEST = "ARP is not a request.";
Jonathan Hart704ca142014-10-09 09:34:39 -070087 private static final String NOT_ARP_REPLY = "ARP is not a reply.";
alshabibb5522ff2014-09-29 19:20:00 -070088
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected HostService hostService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected PacketService packetService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected LinkService linkService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceService deviceService;
100
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700101 private final Multimap<Device, PortNumber> internalPorts = HashMultimap.create();
102 private final Multimap<Device, PortNumber> externalPorts = HashMultimap.create();
alshabibb5522ff2014-09-29 19:20:00 -0700103
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700104 private final DeviceListener deviceListener = new InternalDeviceListener();
105 private final InternalLinkListener linkListener = new InternalLinkListener();
alshabibb5522ff2014-09-29 19:20:00 -0700106
107 /**
108 * Listens to both device service and link service to determine
109 * whether a port is internal or external.
110 */
111 @Activate
112 public void activate() {
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700113 deviceService.addListener(deviceListener);
114 linkService.addListener(linkListener);
alshabibb5522ff2014-09-29 19:20:00 -0700115 determinePortLocations();
116 log.info("Started");
117 }
118
119
120 @Deactivate
121 public void deactivate() {
Thomas Vachuskafc52fec2015-05-18 19:13:56 -0700122 deviceService.removeListener(deviceListener);
123 linkService.removeListener(linkListener);
alshabibb5522ff2014-09-29 19:20:00 -0700124 log.info("Stopped");
125 }
126
127 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800128 public boolean isKnown(IpAddress addr) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900129 checkPermission(Permission.PACKET_READ);
130
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700131 checkNotNull(addr, MAC_ADDR_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700132 Set<Host> hosts = hostService.getHostsByIp(addr);
133 return !hosts.isEmpty();
134 }
135
136 @Override
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700137 public void reply(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900138 checkPermission(Permission.PACKET_WRITE);
139
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700140 checkNotNull(eth, REQUEST_NULL);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800141
142 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
143 replyArp(eth, inPort);
144 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
145 replyNdp(eth, inPort);
146 }
147 }
148
149 private void replyArp(Ethernet eth, ConnectPoint inPort) {
alshabibb5522ff2014-09-29 19:20:00 -0700150 ARP arp = (ARP) eth.getPayload();
151 checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700152 checkNotNull(inPort);
Dusan Pajina22b9702015-02-12 16:25:23 +0100153 Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700154
Jonathan Hart6cd2f352015-01-13 17:44:45 -0800155 VlanId vlan = VlanId.vlanId(eth.getVlanID());
156
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700157 if (isOutsidePort(inPort)) {
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700158 // If the request came from outside the network, only reply if it was
159 // for one of our external addresses.
Jonathan Harta887ba82014-11-03 15:20:52 -0800160 Set<PortAddresses> addressSet =
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700161 hostService.getAddressBindingsForPort(inPort);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700162
Jonathan Harta887ba82014-11-03 15:20:52 -0800163 for (PortAddresses addresses : addressSet) {
164 for (InterfaceIpAddress ia : addresses.ipAddresses()) {
Dusan Pajina22b9702015-02-12 16:25:23 +0100165 if (ia.ipAddress().equals(targetAddress)) {
Jonathan Harta887ba82014-11-03 15:20:52 -0800166 Ethernet arpReply =
Pingping Linc9e16bf2015-04-10 14:42:41 -0700167 ARP.buildArpReply(targetAddress, addresses.mac(), eth);
Jonathan Harta887ba82014-11-03 15:20:52 -0800168 sendTo(arpReply, inPort);
169 }
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700170 }
171 }
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700172 return;
173 }
174
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700175 // See if we have the target host in the host store
alshabibb5522ff2014-09-29 19:20:00 -0700176
Dusan Pajina22b9702015-02-12 16:25:23 +0100177 Set<Host> hosts = hostService.getHostsByIp(targetAddress);
alshabibb5522ff2014-09-29 19:20:00 -0700178
179 Host dst = null;
180 Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
181 VlanId.vlanId(eth.getVlanID())));
182
183 for (Host host : hosts) {
184 if (host.vlan().equals(vlan)) {
185 dst = host;
186 break;
187 }
188 }
189
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700190 if (src != null && dst != null) {
191 // We know the target host so we can respond
Pingping Linc9e16bf2015-04-10 14:42:41 -0700192 Ethernet arpReply = ARP.buildArpReply(targetAddress, dst.mac(), eth);
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700193 sendTo(arpReply, inPort);
194 return;
195 }
196
197 // If the source address matches one of our external addresses
198 // it could be a request from an internal host to an external
199 // address. Forward it over to the correct port.
200 Ip4Address source =
201 Ip4Address.valueOf(arp.getSenderProtocolAddress());
202 Set<PortAddresses> sourceAddresses = findPortsInSubnet(source);
203 boolean matched = false;
204 for (PortAddresses pa : sourceAddresses) {
205 for (InterfaceIpAddress ia : pa.ipAddresses()) {
206 if (ia.ipAddress().equals(source) &&
207 pa.vlan().equals(vlan)) {
208 matched = true;
209 sendTo(eth, pa.connectPoint());
210 break;
211 }
212 }
213 }
214
215 if (matched) {
alshabibb5522ff2014-09-29 19:20:00 -0700216 return;
217 }
218
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800219 //
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700220 // The request couldn't be resolved.
221 // Flood the request on all ports except the incoming port.
Pavlin Radoslavov5b5dc482014-11-05 14:48:08 -0800222 //
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700223 flood(eth, inPort);
224 return;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700225 }
226
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800227 private void replyNdp(Ethernet eth, ConnectPoint inPort) {
228
229 IPv6 ipv6 = (IPv6) eth.getPayload();
230 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
231 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
Dusan Pajina22b9702015-02-12 16:25:23 +0100232 Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800233
234 VlanId vlan = VlanId.vlanId(eth.getVlanID());
235
236 // If the request came from outside the network, only reply if it was
237 // for one of our external addresses.
238 if (isOutsidePort(inPort)) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800239 Set<PortAddresses> addressSet =
240 hostService.getAddressBindingsForPort(inPort);
241
242 for (PortAddresses addresses : addressSet) {
243 for (InterfaceIpAddress ia : addresses.ipAddresses()) {
Dusan Pajina22b9702015-02-12 16:25:23 +0100244 if (ia.ipAddress().equals(targetAddress)) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800245 Ethernet ndpReply =
Pavlin Radoslavovcef52062015-02-12 16:57:17 -0800246 buildNdpReply(targetAddress, addresses.mac(), eth);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800247 sendTo(ndpReply, inPort);
248 }
249 }
250 }
251 return;
252 } else {
253 // If the source address matches one of our external addresses
254 // it could be a request from an internal host to an external
255 // address. Forward it over to the correct ports.
256 Ip6Address source =
Pavlin Radoslavovcef52062015-02-12 16:57:17 -0800257 Ip6Address.valueOf(ipv6.getSourceAddress());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800258 Set<PortAddresses> sourceAddresses = findPortsInSubnet(source);
259 boolean matched = false;
260 for (PortAddresses pa : sourceAddresses) {
261 for (InterfaceIpAddress ia : pa.ipAddresses()) {
262 if (ia.ipAddress().equals(source) &&
263 pa.vlan().equals(vlan)) {
264 matched = true;
265 sendTo(eth, pa.connectPoint());
Pavlin Radoslavovcef52062015-02-12 16:57:17 -0800266 break;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800267 }
268 }
269 }
270
271 if (matched) {
272 return;
273 }
274 }
275
276 // Continue with normal proxy ARP case
277
Dusan Pajina22b9702015-02-12 16:25:23 +0100278 Set<Host> hosts = hostService.getHostsByIp(targetAddress);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800279
280 Host dst = null;
281 Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
282 VlanId.vlanId(eth.getVlanID())));
283
284 for (Host host : hosts) {
285 if (host.vlan().equals(vlan)) {
286 dst = host;
287 break;
288 }
289 }
290
291 if (src == null || dst == null) {
Dusan Pajina22b9702015-02-12 16:25:23 +0100292 //
293 // The request couldn't be resolved.
294 // Flood the request on all ports except the incoming ports.
295 //
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800296 flood(eth, inPort);
297 return;
298 }
299
300 //
Dusan Pajina22b9702015-02-12 16:25:23 +0100301 // Reply on the port the request was received on
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800302 //
Dusan Pajina22b9702015-02-12 16:25:23 +0100303 Ethernet ndpReply = buildNdpReply(targetAddress, dst.mac(), eth);
304 sendTo(ndpReply, inPort);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800305 }
306
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700307 /**
308 * Outputs the given packet out the given port.
309 *
310 * @param packet the packet to send
311 * @param outPort the port to send it out
312 */
313 private void sendTo(Ethernet packet, ConnectPoint outPort) {
314 if (internalPorts.containsEntry(
315 deviceService.getDevice(outPort.deviceId()), outPort.port())) {
316 // Sanity check to make sure we don't send the packet out an
317 // internal port and create a loop (could happen due to
318 // misconfiguration).
319 return;
320 }
321
tom9a693fd2014-10-03 11:32:19 -0700322 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700323 builder.setOutput(outPort.port());
324 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
325 builder.build(), ByteBuffer.wrap(packet.serialize())));
326 }
327
328 /**
Jonathan Hart1f793a72014-11-12 23:22:02 -0800329 * Finds ports with an address in the subnet of the target address.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700330 *
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700331 * @param target the target address to find a matching port for
Jonathan Hart1f793a72014-11-12 23:22:02 -0800332 * @return a set of PortAddresses describing ports in the subnet
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700333 */
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800334 private Set<PortAddresses> findPortsInSubnet(IpAddress target) {
Jonathan Hart1f793a72014-11-12 23:22:02 -0800335 Set<PortAddresses> result = new HashSet<PortAddresses>();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700336 for (PortAddresses addresses : hostService.getAddressBindings()) {
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700337 for (InterfaceIpAddress ia : addresses.ipAddresses()) {
338 if (ia.subnetAddress().contains(target)) {
Jonathan Hart1f793a72014-11-12 23:22:02 -0800339 result.add(addresses);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700340 }
341 }
342 }
Jonathan Hart1f793a72014-11-12 23:22:02 -0800343 return result;
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700344 }
345
346 /**
347 * Returns whether the given port is an outside-facing port with an IP
348 * address configured.
349 *
350 * @param port the port to check
351 * @return true if the port is an outside-facing port, otherwise false
352 */
353 private boolean isOutsidePort(ConnectPoint port) {
Pavlin Radoslavov76b0ae22014-10-27 15:33:19 -0700354 //
355 // TODO: Is this sufficient to identify outside-facing ports: just
356 // having IP addresses on a port?
357 //
Jonathan Harta887ba82014-11-03 15:20:52 -0800358 return !hostService.getAddressBindingsForPort(port).isEmpty();
alshabibb5522ff2014-09-29 19:20:00 -0700359 }
360
361 @Override
Jonathan Hartf84591d2015-01-16 14:33:43 -0800362 public void forward(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900363 checkPermission(Permission.PACKET_WRITE);
364
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700365 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700366
367 Host h = hostService.getHost(HostId.hostId(eth.getDestinationMAC(),
368 VlanId.vlanId(eth.getVlanID())));
369
370 if (h == null) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800371 flood(eth, inPort);
alshabibb5522ff2014-09-29 19:20:00 -0700372 } else {
tom9a693fd2014-10-03 11:32:19 -0700373 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700374 builder.setOutput(h.location().port());
375 packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
376 builder.build(), ByteBuffer.wrap(eth.serialize())));
377 }
378
379 }
380
alshabibc274c902014-10-03 14:58:27 -0700381 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800382 public boolean handlePacket(PacketContext context) {
Changhoon Yoon541ef712015-05-23 17:18:34 +0900383 checkPermission(Permission.PACKET_WRITE);
384
alshabibc274c902014-10-03 14:58:27 -0700385 InboundPacket pkt = context.inPacket();
386 Ethernet ethPkt = pkt.parsed();
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800387
388 if (ethPkt == null) {
389 return false;
390 }
391 if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
392 return handleArp(context, ethPkt);
393 } else if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
394 return handleNdp(context, ethPkt);
alshabibc274c902014-10-03 14:58:27 -0700395 }
396 return false;
397 }
398
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800399 private boolean handleArp(PacketContext context, Ethernet ethPkt) {
400 ARP arp = (ARP) ethPkt.getPayload();
401
402 if (arp.getOpCode() == ARP.OP_REPLY) {
403 forward(ethPkt, context.inPacket().receivedFrom());
404 } else if (arp.getOpCode() == ARP.OP_REQUEST) {
405 reply(ethPkt, context.inPacket().receivedFrom());
406 } else {
407 return false;
408 }
409 context.block();
410 return true;
411 }
412
413 private boolean handleNdp(PacketContext context, Ethernet ethPkt) {
414 IPv6 ipv6 = (IPv6) ethPkt.getPayload();
415
416 if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
417 return false;
418 }
419 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
420 if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
421 forward(ethPkt, context.inPacket().receivedFrom());
422 } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
423 reply(ethPkt, context.inPacket().receivedFrom());
424 } else {
425 return false;
426 }
427 context.block();
428 return true;
429 }
430
alshabibb5522ff2014-09-29 19:20:00 -0700431 /**
432 * Flood the arp request at all edges in the network.
Dusan Pajina22b9702015-02-12 16:25:23 +0100433 *
434 * @param request the arp request
435 * @param inPort the connect point the arp request was received on
alshabibb5522ff2014-09-29 19:20:00 -0700436 */
Jonathan Hartf84591d2015-01-16 14:33:43 -0800437 private void flood(Ethernet request, ConnectPoint inPort) {
alshabibb5522ff2014-09-29 19:20:00 -0700438 TrafficTreatment.Builder builder = null;
439 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
440
441 synchronized (externalPorts) {
442 for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700443 ConnectPoint cp = new ConnectPoint(entry.getKey().id(), entry.getValue());
Jonathan Hartf84591d2015-01-16 14:33:43 -0800444 if (isOutsidePort(cp) || cp.equals(inPort)) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700445 continue;
446 }
447
tom9a693fd2014-10-03 11:32:19 -0700448 builder = DefaultTrafficTreatment.builder();
alshabibb5522ff2014-09-29 19:20:00 -0700449 builder.setOutput(entry.getValue());
450 packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
451 builder.build(), buf));
452 }
alshabibb5522ff2014-09-29 19:20:00 -0700453 }
454 }
455
456 /**
457 * Determines the location of all known ports in the system.
458 */
459 private void determinePortLocations() {
460 Iterable<Device> devices = deviceService.getDevices();
461 Iterable<Link> links = null;
462 List<PortNumber> ports = null;
463 for (Device d : devices) {
464 ports = buildPortNumberList(deviceService.getPorts(d.id()));
465 links = linkService.getLinks();
466 for (Link l : links) {
467 // for each link, mark the concerned ports as internal
468 // and the remaining ports are therefore external.
Yuta HIGUCHI3541bf22014-10-04 22:06:19 -0700469 if (l.src().deviceId().equals(d.id())
alshabibb5522ff2014-09-29 19:20:00 -0700470 && ports.contains(l.src().port())) {
471 ports.remove(l.src().port());
472 internalPorts.put(d, l.src().port());
473 }
Yuta HIGUCHI3541bf22014-10-04 22:06:19 -0700474 if (l.dst().deviceId().equals(d.id())
alshabibb5522ff2014-09-29 19:20:00 -0700475 && ports.contains(l.dst().port())) {
476 ports.remove(l.dst().port());
477 internalPorts.put(d, l.dst().port());
478 }
479 }
480 synchronized (externalPorts) {
481 externalPorts.putAll(d, ports);
482 }
483 }
484
485 }
486
487 private List<PortNumber> buildPortNumberList(List<Port> ports) {
488 List<PortNumber> portNumbers = Lists.newLinkedList();
489 for (Port p : ports) {
490 portNumbers.add(p.number());
491 }
492 return portNumbers;
493 }
494
495 /**
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800496 * Builds an Neighbor Discovery reply based on a request.
497 *
498 * @param srcIp the IP address to use as the reply source
499 * @param srcMac the MAC address to use as the reply source
500 * @param request the Neighbor Solicitation request we got
501 * @return an Ethernet frame containing the Neighbor Advertisement reply
502 */
503 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
504 Ethernet request) {
505
506 Ethernet eth = new Ethernet();
507 eth.setDestinationMACAddress(request.getSourceMAC());
508 eth.setSourceMACAddress(srcMac);
509 eth.setEtherType(Ethernet.TYPE_IPV6);
510 eth.setVlanID(request.getVlanID());
511
512 IPv6 requestIp = (IPv6) request.getPayload();
513 IPv6 ipv6 = new IPv6();
514 ipv6.setSourceAddress(srcIp.toOctets());
515 ipv6.setDestinationAddress(requestIp.getSourceAddress());
516 ipv6.setHopLimit((byte) 255);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800517
518 ICMP6 icmp6 = new ICMP6();
519 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
520 icmp6.setIcmpCode((byte) 0);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800521
522 NeighborAdvertisement nadv = new NeighborAdvertisement();
Dusan Pajina22b9702015-02-12 16:25:23 +0100523 nadv.setTargetAddress(srcIp.toOctets());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800524 nadv.setSolicitedFlag((byte) 1);
525 nadv.setOverrideFlag((byte) 1);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800526 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
527 srcMac.toBytes());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800528
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800529 icmp6.setPayload(nadv);
Dusan Pajina22b9702015-02-12 16:25:23 +0100530 ipv6.setPayload(icmp6);
531 eth.setPayload(ipv6);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800532 return eth;
533 }
534
alshabibb5522ff2014-09-29 19:20:00 -0700535 public class InternalLinkListener implements LinkListener {
536
537 @Override
538 public void event(LinkEvent event) {
539 Link link = event.subject();
540 Device src = deviceService.getDevice(link.src().deviceId());
541 Device dst = deviceService.getDevice(link.dst().deviceId());
542 switch (event.type()) {
543 case LINK_ADDED:
544 synchronized (externalPorts) {
545 externalPorts.remove(src, link.src().port());
546 externalPorts.remove(dst, link.dst().port());
547 internalPorts.put(src, link.src().port());
548 internalPorts.put(dst, link.dst().port());
549 }
550
551 break;
552 case LINK_REMOVED:
553 synchronized (externalPorts) {
554 externalPorts.put(src, link.src().port());
555 externalPorts.put(dst, link.dst().port());
556 internalPorts.remove(src, link.src().port());
557 internalPorts.remove(dst, link.dst().port());
558 }
559
560 break;
561 case LINK_UPDATED:
562 // don't care about links being updated.
563 break;
564 default:
565 break;
566 }
567
568 }
569
570 }
571
572 public class InternalDeviceListener implements DeviceListener {
573
574 @Override
575 public void event(DeviceEvent event) {
576 Device device = event.subject();
577 switch (event.type()) {
578 case DEVICE_ADDED:
579 case DEVICE_AVAILABILITY_CHANGED:
alshabibb5522ff2014-09-29 19:20:00 -0700580 case DEVICE_SUSPENDED:
581 case DEVICE_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700582 // nothing to do in these cases; handled when links get reported
583 break;
584 case DEVICE_REMOVED:
585 synchronized (externalPorts) {
586 externalPorts.removeAll(device);
587 internalPorts.removeAll(device);
588 }
589 break;
590 case PORT_ADDED:
alshabib6eb438a2014-10-01 16:39:37 -0700591 case PORT_UPDATED:
alshabibb5522ff2014-09-29 19:20:00 -0700592 synchronized (externalPorts) {
alshabib6eb438a2014-10-01 16:39:37 -0700593 if (event.port().isEnabled()) {
594 externalPorts.put(device, event.port().number());
595 internalPorts.remove(device, event.port().number());
596 }
alshabibb5522ff2014-09-29 19:20:00 -0700597 }
598 break;
599 case PORT_REMOVED:
600 synchronized (externalPorts) {
601 externalPorts.remove(device, event.port().number());
602 internalPorts.remove(device, event.port().number());
603 }
604 break;
605 default:
606 break;
607
608 }
609
610 }
611
alshabibc274c902014-10-03 14:58:27 -0700612 }
alshabibb5522ff2014-09-29 19:20:00 -0700613
614}