blob: 0c84651a379e75e0cb3d69c543c8e230d794b5aa [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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
alshabibb5522ff2014-09-29 19:20:00 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
Jonathan Harte8600eb2015-01-12 10:30:45 -080024import org.onlab.packet.ARP;
25import org.onlab.packet.Ethernet;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080026import org.onlab.packet.ICMP6;
27import org.onlab.packet.IPv6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080028import org.onlab.packet.Ip4Address;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080029import org.onlab.packet.Ip6Address;
Jonathan Harte8600eb2015-01-12 10:30:45 -080030import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.packet.VlanId;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080033import org.onlab.packet.ndp.NeighborAdvertisement;
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -080034import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080035import org.onlab.packet.ndp.NeighborSolicitation;
Jonathan Hart4cb39882015-08-12 23:50:55 -040036import org.onosproject.incubator.net.intf.Interface;
37import org.onosproject.incubator.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.ConnectPoint;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.Host;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.device.DeviceService;
Aaron Kruglikovd8123832015-07-06 14:20:25 -070041import org.onosproject.net.edge.EdgePortService;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.host.HostService;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.link.LinkService;
46import org.onosproject.net.packet.DefaultOutboundPacket;
47import org.onosproject.net.packet.InboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketService;
50import org.onosproject.net.proxyarp.ProxyArpService;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070051import org.onosproject.net.proxyarp.ProxyArpStore;
alshabibb5522ff2014-09-29 19:20:00 -070052import org.slf4j.Logger;
53
Jonathan Hart6cd2f352015-01-13 17:44:45 -080054import java.nio.ByteBuffer;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080055import java.util.Set;
Luca Pretef70d3992015-10-30 16:24:14 -070056import java.util.stream.Collectors;
Jonathan Hart6cd2f352015-01-13 17:44:45 -080057
58import static com.google.common.base.Preconditions.checkArgument;
59import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070060import static org.onlab.packet.VlanId.vlanId;
61import static org.onosproject.net.HostId.hostId;
Changhoon Yoon541ef712015-05-23 17:18:34 +090062import static org.onosproject.security.AppGuard.checkPermission;
Aaron Kruglikovd8123832015-07-06 14:20:25 -070063import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoonb856b812015-08-10 03:47:19 +090064import static org.onosproject.security.AppPermission.Type.*;
Changhoon Yoon541ef712015-05-23 17:18:34 +090065
alshabibb5522ff2014-09-29 19:20:00 -070066
alshabibb5522ff2014-09-29 19:20:00 -070067@Component(immediate = true)
68@Service
69public class ProxyArpManager implements ProxyArpService {
70
71 private final Logger log = getLogger(getClass());
72
Jonathan Hart39ee6482015-08-31 16:00:19 +020073 private static final String MAC_ADDR_NULL = "MAC address cannot be null.";
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -080074 private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
Jonathan Hart39ee6482015-08-31 16:00:19 +020075 private static final String MSG_NOT_REQUEST = "Message is not an ARP or NDP request";
alshabibb5522ff2014-09-29 19:20:00 -070076
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Aaron Kruglikovd8123832015-07-06 14:20:25 -070078 protected EdgePortService edgeService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb5522ff2014-09-29 19:20:00 -070081 protected HostService hostService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected PacketService packetService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected LinkService linkService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected DeviceService deviceService;
91
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected ProxyArpStore store;
94
Jonathan Hart4cb39882015-08-12 23:50:55 -040095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected InterfaceService interfaceService;
97
Jonathan Hart39ee6482015-08-31 16:00:19 +020098 private enum Protocol {
99 ARP, NDP
100 }
101
102 private enum MessageType {
103 REQUEST, REPLY
104 }
105
alshabibb5522ff2014-09-29 19:20:00 -0700106 @Activate
107 public void activate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700108 store.setDelegate(this::sendTo);
alshabibb5522ff2014-09-29 19:20:00 -0700109 log.info("Started");
110 }
111
alshabibb5522ff2014-09-29 19:20:00 -0700112 @Deactivate
113 public void deactivate() {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700114 store.setDelegate(null);
alshabibb5522ff2014-09-29 19:20:00 -0700115 log.info("Stopped");
116 }
117
118 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800119 public boolean isKnown(IpAddress addr) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900120 checkPermission(PACKET_READ);
121
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700122 checkNotNull(addr, MAC_ADDR_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700123 Set<Host> hosts = hostService.getHostsByIp(addr);
124 return !hosts.isEmpty();
125 }
126
127 @Override
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700128 public void reply(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900129 checkPermission(PACKET_WRITE);
130
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700131 checkNotNull(eth, REQUEST_NULL);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800132
Jonathan Hart39ee6482015-08-31 16:00:19 +0200133 MessageContext context = createContext(eth, inPort);
134 if (context != null) {
135 replyInternal(context);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800136 }
137 }
138
Jonathan Hart39ee6482015-08-31 16:00:19 +0200139 /**
140 * Handles a request message.
141 *
142 * If the MAC address of the target is known, we can reply directly to the
143 * requestor. Otherwise, we forward the request out other ports in an
144 * attempt to find the correct host.
145 *
146 * @param context request message context to process
147 */
148 private void replyInternal(MessageContext context) {
149 checkNotNull(context);
150 checkArgument(context.type() == MessageType.REQUEST, MSG_NOT_REQUEST);
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700151
Jonathan Hart39ee6482015-08-31 16:00:19 +0200152 if (hasIpAddress(context.inPort())) {
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700153 // If the request came from outside the network, only reply if it was
154 // for one of our external addresses.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700155
Jonathan Hart39ee6482015-08-31 16:00:19 +0200156 interfaceService.getInterfacesByPort(context.inPort())
Jonathan Hart4cb39882015-08-12 23:50:55 -0400157 .stream()
158 .filter(intf -> intf.ipAddresses()
159 .stream()
Jonathan Hart39ee6482015-08-31 16:00:19 +0200160 .anyMatch(ia -> ia.ipAddress().equals(context.target())))
161 .forEach(intf -> buildAndSendReply(context, intf.mac()));
Jonathan Hart4cb39882015-08-12 23:50:55 -0400162
163 // Stop here and don't proxy ARPs if the port has an IP address
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700164 return;
165 }
166
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700167 // See if we have the target host in the host store
Jonathan Hart39ee6482015-08-31 16:00:19 +0200168 Set<Host> hosts = hostService.getHostsByIp(context.target());
alshabibb5522ff2014-09-29 19:20:00 -0700169
170 Host dst = null;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200171 Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
alshabibb5522ff2014-09-29 19:20:00 -0700172
173 for (Host host : hosts) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200174 if (host.vlan().equals(context.vlan())) {
alshabibb5522ff2014-09-29 19:20:00 -0700175 dst = host;
176 break;
177 }
178 }
179
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700180 if (src != null && dst != null) {
181 // We know the target host so we can respond
Jonathan Hart39ee6482015-08-31 16:00:19 +0200182 buildAndSendReply(context, dst.mac());
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700183 return;
184 }
185
186 // If the source address matches one of our external addresses
187 // it could be a request from an internal host to an external
188 // address. Forward it over to the correct port.
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700189 boolean matched = false;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200190 Set<Interface> interfaces = interfaceService.getInterfacesByIp(context.sender());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400191 for (Interface intf : interfaces) {
Jonathan Hart39ee6482015-08-31 16:00:19 +0200192 if (intf.vlan().equals(context.vlan())) {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400193 matched = true;
Jonathan Hart39ee6482015-08-31 16:00:19 +0200194 sendTo(context.packet(), intf.connectPoint());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400195 break;
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700196 }
197 }
198
199 if (matched) {
alshabibb5522ff2014-09-29 19:20:00 -0700200 return;
201 }
202
Luca Pretef70d3992015-10-30 16:24:14 -0700203 // If the packets has a vlanId look if there are some other
204 // interfaces in the configuration on the same vlan and broadcast
205 // the packet out just of through those interfaces.
206 VlanId vlanId = context.vlan();
207
208 Set<Interface> filteredVlanInterfaces =
209 filterVlanInterfacesNoIp(interfaceService.getInterfacesByVlan(vlanId));
210
211 if (vlanId != null
212 && !vlanId.equals(VlanId.NONE)
213 && confContainsVlans(vlanId, context.inPort())) {
214 vlanFlood(context.packet(), filteredVlanInterfaces, context.inPort);
215 return;
216 }
217
Jonathan Hart7d1496b2015-03-10 22:00:48 -0700218 // The request couldn't be resolved.
219 // Flood the request on all ports except the incoming port.
Jonathan Hart39ee6482015-08-31 16:00:19 +0200220 flood(context.packet(), context.inPort());
Jonathan Hart4cb39882015-08-12 23:50:55 -0400221 }
222
Luca Pretef70d3992015-10-30 16:24:14 -0700223 private Set<Interface> filterVlanInterfacesNoIp(Set<Interface> vlanInterfaces) {
224 return vlanInterfaces
225 .stream()
226 .filter(intf -> intf.ipAddresses().isEmpty())
227 .collect(Collectors.toSet());
228 }
229
230 /**
231 * States if the interface configuration contains more than one interface configured
232 * on a specific vlan, including the interface passed as argument.
233 *
234 * @param vlanId the vlanid to look for in the interface configuration
235 * @param connectPoint the connect point to exclude from the search
236 * @return true if interfaces are found. False otherwise
237 */
238 private boolean confContainsVlans(VlanId vlanId, ConnectPoint connectPoint) {
239 Set<Interface> vlanInterfaces = interfaceService.getInterfacesByVlan(vlanId);
240 return interfaceService.getInterfacesByVlan(vlanId)
241 .stream()
242 .anyMatch(intf -> intf.connectPoint().equals(connectPoint) && intf.ipAddresses().isEmpty())
243 && vlanInterfaces.size() > 1;
244 }
245
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700246 /**
Jonathan Hart39ee6482015-08-31 16:00:19 +0200247 * Builds and sends a reply message given a request context and the resolved
248 * MAC address to answer with.
249 *
250 * @param context message context of request
251 * @param targetMac MAC address to be given in the response
252 */
253 private void buildAndSendReply(MessageContext context, MacAddress targetMac) {
254 switch (context.protocol()) {
255 case ARP:
256 sendTo(ARP.buildArpReply((Ip4Address) context.target(),
257 targetMac, context.packet()), context.inPort());
258 break;
259 case NDP:
260 sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
261 context.packet()), context.inPort());
262 break;
263 default:
264 break;
265 }
266 }
267
268 /**
269 * Outputs a packet out a specific port.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700270 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700271 * @param packet the packet to send
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700272 * @param outPort the port to send it out
273 */
274 private void sendTo(Ethernet packet, ConnectPoint outPort) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700275 sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
276 }
277
Jonathan Hart39ee6482015-08-31 16:00:19 +0200278 /**
279 * Outputs a packet out a specific port.
280 *
281 * @param outPort port to send it out
282 * @param packet packet to send
283 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700284 private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700285 if (!edgeService.isEdgePoint(outPort)) {
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700286 // Sanity check to make sure we don't send the packet out an
287 // internal port and create a loop (could happen due to
288 // misconfiguration).
289 return;
290 }
291
tom9a693fd2014-10-03 11:32:19 -0700292 TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700293 builder.setOutput(outPort.port());
294 packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200295 builder.build(), packet));
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700296 }
297
298 /**
Jonathan Hart4cb39882015-08-12 23:50:55 -0400299 * Returns whether the given port has any IP addresses configured or not.
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700300 *
Luca Pretef70d3992015-10-30 16:24:14 -0700301 * @param connectPoint the port to check
Jonathan Hart4cb39882015-08-12 23:50:55 -0400302 * @return true if the port has at least one IP address configured,
Luca Pretef70d3992015-10-30 16:24:14 -0700303 * false otherwise
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700304 */
Luca Pretef70d3992015-10-30 16:24:14 -0700305 private boolean hasIpAddress(ConnectPoint connectPoint) {
306 return interfaceService.getInterfacesByPort(connectPoint)
Jonathan Hart4cb39882015-08-12 23:50:55 -0400307 .stream()
Luca Pretef70d3992015-10-30 16:24:14 -0700308 .flatMap(intf -> intf.ipAddresses().stream())
309 .findAny()
310 .isPresent();
311 }
312
313 /**
314 * Returns whether the given port has any VLAN configured or not.
315 *
316 * @param connectPoint the port to check
317 * @return true if the port has at least one VLAN configured,
318 * false otherwise
319 */
320 private boolean hasVlan(ConnectPoint connectPoint) {
321 return interfaceService.getInterfacesByPort(connectPoint)
322 .stream()
323 .filter(intf -> !intf.vlan().equals(VlanId.NONE))
Jonathan Hart4cb39882015-08-12 23:50:55 -0400324 .findAny()
325 .isPresent();
alshabibb5522ff2014-09-29 19:20:00 -0700326 }
327
328 @Override
Jonathan Hartf84591d2015-01-16 14:33:43 -0800329 public void forward(Ethernet eth, ConnectPoint inPort) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900330 checkPermission(PACKET_WRITE);
331
Yuta HIGUCHI59718042014-10-04 22:04:56 -0700332 checkNotNull(eth, REQUEST_NULL);
alshabibb5522ff2014-09-29 19:20:00 -0700333
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700334 Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
Jonathan Hart39ee6482015-08-31 16:00:19 +0200335 vlanId(eth.getVlanID())));
alshabibb5522ff2014-09-29 19:20:00 -0700336
337 if (h == null) {
Jonathan Hartf84591d2015-01-16 14:33:43 -0800338 flood(eth, inPort);
alshabibb5522ff2014-09-29 19:20:00 -0700339 } else {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700340 Host subject = hostService.getHost(hostId(eth.getSourceMAC(),
341 vlanId(eth.getVlanID())));
342 store.forward(h.location(), subject, ByteBuffer.wrap(eth.serialize()));
alshabibb5522ff2014-09-29 19:20:00 -0700343 }
alshabibb5522ff2014-09-29 19:20:00 -0700344 }
345
alshabibc274c902014-10-03 14:58:27 -0700346 @Override
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800347 public boolean handlePacket(PacketContext context) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900348 checkPermission(PACKET_WRITE);
Changhoon Yoon541ef712015-05-23 17:18:34 +0900349
alshabibc274c902014-10-03 14:58:27 -0700350 InboundPacket pkt = context.inPacket();
351 Ethernet ethPkt = pkt.parsed();
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800352
353 if (ethPkt == null) {
354 return false;
355 }
alshabibc274c902014-10-03 14:58:27 -0700356
Jonathan Hart39ee6482015-08-31 16:00:19 +0200357 MessageContext msgContext = createContext(ethPkt, pkt.receivedFrom());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800358
Jonathan Hart39ee6482015-08-31 16:00:19 +0200359 if (msgContext == null) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800360 return false;
361 }
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800362
Jonathan Hart39ee6482015-08-31 16:00:19 +0200363 switch (msgContext.type()) {
364 case REPLY:
365 forward(msgContext.packet(), msgContext.inPort());
366 break;
367 case REQUEST:
368 replyInternal(msgContext);
369 break;
370 default:
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800371 return false;
372 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200373
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800374 context.block();
375 return true;
376 }
377
alshabibb5522ff2014-09-29 19:20:00 -0700378 /**
Luca Pretef70d3992015-10-30 16:24:14 -0700379 * Flood the arp request at all edges on a specifc VLAN.
380 *
381 * @param request the arp request
382 * @param dsts the destination interfaces
383 * @param inPort the connect point the arp request was received on
384 */
385 private void vlanFlood(Ethernet request, Set<Interface> dsts, ConnectPoint inPort) {
386 TrafficTreatment.Builder builder = null;
387 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
388
389 for (Interface intf : dsts) {
390 ConnectPoint cPoint = intf.connectPoint();
391 if (cPoint.equals(inPort)) {
392 continue;
393 }
394
395 builder = DefaultTrafficTreatment.builder();
396 builder.setOutput(cPoint.port());
397 packetService.emit(new DefaultOutboundPacket(cPoint.deviceId(),
398 builder.build(), buf));
399 }
400 }
401
402 /**
alshabibb5522ff2014-09-29 19:20:00 -0700403 * Flood the arp request at all edges in the network.
Dusan Pajina22b9702015-02-12 16:25:23 +0100404 *
405 * @param request the arp request
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700406 * @param inPort the connect point the arp request was received on
alshabibb5522ff2014-09-29 19:20:00 -0700407 */
Jonathan Hartf84591d2015-01-16 14:33:43 -0800408 private void flood(Ethernet request, ConnectPoint inPort) {
alshabibb5522ff2014-09-29 19:20:00 -0700409 TrafficTreatment.Builder builder = null;
410 ByteBuffer buf = ByteBuffer.wrap(request.serialize());
411
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700412 for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
Luca Pretef70d3992015-10-30 16:24:14 -0700413 if (hasIpAddress(connectPoint)
414 || hasVlan(connectPoint)
415 || connectPoint.equals(inPort)) {
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700416 continue;
417 }
Jonathan Hartdbdbdbb2014-10-06 18:35:30 -0700418
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700419 builder = DefaultTrafficTreatment.builder();
420 builder.setOutput(connectPoint.port());
421 packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700422 builder.build(), buf));
alshabibb5522ff2014-09-29 19:20:00 -0700423 }
alshabibb5522ff2014-09-29 19:20:00 -0700424 }
425
alshabibb5522ff2014-09-29 19:20:00 -0700426 /**
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800427 * Builds an Neighbor Discovery reply based on a request.
428 *
Aaron Kruglikovd8123832015-07-06 14:20:25 -0700429 * @param srcIp the IP address to use as the reply source
430 * @param srcMac the MAC address to use as the reply source
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800431 * @param request the Neighbor Solicitation request we got
432 * @return an Ethernet frame containing the Neighbor Advertisement reply
433 */
434 private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
435 Ethernet request) {
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800436 Ethernet eth = new Ethernet();
437 eth.setDestinationMACAddress(request.getSourceMAC());
438 eth.setSourceMACAddress(srcMac);
439 eth.setEtherType(Ethernet.TYPE_IPV6);
440 eth.setVlanID(request.getVlanID());
441
442 IPv6 requestIp = (IPv6) request.getPayload();
443 IPv6 ipv6 = new IPv6();
444 ipv6.setSourceAddress(srcIp.toOctets());
445 ipv6.setDestinationAddress(requestIp.getSourceAddress());
446 ipv6.setHopLimit((byte) 255);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800447
448 ICMP6 icmp6 = new ICMP6();
449 icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
450 icmp6.setIcmpCode((byte) 0);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800451
452 NeighborAdvertisement nadv = new NeighborAdvertisement();
Dusan Pajina22b9702015-02-12 16:25:23 +0100453 nadv.setTargetAddress(srcIp.toOctets());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800454 nadv.setSolicitedFlag((byte) 1);
455 nadv.setOverrideFlag((byte) 1);
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800456 nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700457 srcMac.toBytes());
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800458
Pavlin Radoslavova2626ef2015-02-18 18:33:25 -0800459 icmp6.setPayload(nadv);
Dusan Pajina22b9702015-02-12 16:25:23 +0100460 ipv6.setPayload(icmp6);
461 eth.setPayload(ipv6);
Kunihiro Ishigurof1bff502015-01-30 15:47:06 -0800462 return eth;
463 }
Jonathan Hart39ee6482015-08-31 16:00:19 +0200464
465 /**
466 * Attempts to create a MessageContext for the given Ethernet frame. If the
467 * frame is a valid ARP or NDP request or response, a context will be
468 * created.
469 *
470 * @param eth input Ethernet frame
471 * @param inPort in port
472 * @return MessageContext if the packet was ARP or NDP, otherwise null
473 */
474 private MessageContext createContext(Ethernet eth, ConnectPoint inPort) {
475 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
476 return createArpContext(eth, inPort);
477 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
478 return createNdpContext(eth, inPort);
479 }
480
481 return null;
482 }
483
484 /**
485 * Extracts context information from ARP packets.
486 *
487 * @param eth input Ethernet frame that is thought to be ARP
488 * @param inPort in port
489 * @return MessageContext object if the packet was a valid ARP packet,
490 * otherwise null
491 */
492 private MessageContext createArpContext(Ethernet eth, ConnectPoint inPort) {
493 if (eth.getEtherType() != Ethernet.TYPE_ARP) {
494 return null;
495 }
496
497 ARP arp = (ARP) eth.getPayload();
498
499 IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
500 IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
501
502 MessageType type;
503 if (arp.getOpCode() == ARP.OP_REQUEST) {
504 type = MessageType.REQUEST;
505 } else if (arp.getOpCode() == ARP.OP_REPLY) {
506 type = MessageType.REPLY;
507 } else {
508 return null;
509 }
510
511 return new MessageContext(eth, inPort, Protocol.ARP, type, target, sender);
512 }
513
514 /**
515 * Extracts context information from NDP packets.
516 *
517 * @param eth input Ethernet frame that is thought to be NDP
518 * @param inPort in port
519 * @return MessageContext object if the packet was a valid NDP packet,
520 * otherwise null
521 */
522 private MessageContext createNdpContext(Ethernet eth, ConnectPoint inPort) {
523 if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
524 return null;
525 }
526 IPv6 ipv6 = (IPv6) eth.getPayload();
527
528 if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
529 return null;
530 }
531 ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
532
533 IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
534 IpAddress target = null;
535
536 MessageType type;
537 if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
538 type = MessageType.REQUEST;
539 NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
540 target = Ip6Address.valueOf(nsol.getTargetAddress());
541 } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
542 type = MessageType.REPLY;
543 } else {
544 return null;
545 }
546
547 return new MessageContext(eth, inPort, Protocol.NDP, type, target, sender);
548 }
549
550 /**
551 * Provides context information for a particular ARP or NDP message, with
552 * a unified interface to access data regardless of protocol.
553 */
554 private class MessageContext {
555 private Protocol protocol;
556 private MessageType type;
557
558 private IpAddress target;
559 private IpAddress sender;
560
561 private Ethernet eth;
562 private ConnectPoint inPort;
563
564
565 public MessageContext(Ethernet eth, ConnectPoint inPort,
566 Protocol protocol, MessageType type,
567 IpAddress target, IpAddress sender) {
568 this.eth = eth;
569 this.inPort = inPort;
570 this.protocol = protocol;
571 this.type = type;
572 this.target = target;
573 this.sender = sender;
574 }
575
576 public ConnectPoint inPort() {
577 return inPort;
578 }
579
580 public Ethernet packet() {
581 return eth;
582 }
583
584 public Protocol protocol() {
585 return protocol;
586 }
587
588 public MessageType type() {
589 return type;
590 }
591
592 public VlanId vlan() {
593 return VlanId.vlanId(eth.getVlanID());
594 }
595
596 public MacAddress srcMac() {
597 return MacAddress.valueOf(eth.getSourceMACAddress());
598 }
599
600 public IpAddress target() {
601 return target;
602 }
603
604 public IpAddress sender() {
605 return sender;
606 }
607 }
alshabibb5522ff2014-09-29 19:20:00 -0700608}