blob: 675e63d75d07c7e4d21664ebe719df58cac9c551 [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sanghob35a6192015-04-01 13:05:26 -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 */
16package org.onosproject.segmentrouting;
17
18import org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
Pier Ventre10bd8d12016-11-26 21:05:22 -080022import org.onlab.packet.IpPrefix;
sanghob35a6192015-04-01 13:05:26 -070023import org.onlab.packet.MacAddress;
Charles Chan68aa62d2015-11-09 16:37:23 -080024import org.onlab.packet.VlanId;
Pier Ventre10bd8d12016-11-26 21:05:22 -080025import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
sanghob35a6192015-04-01 13:05:26 -070026import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.Host;
Pier Ventre10bd8d12016-11-26 21:05:22 -080029import org.onosproject.net.HostId;
sanghob35a6192015-04-01 13:05:26 -070030import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficTreatment;
Pier Ventre10bd8d12016-11-26 21:05:22 -080032import org.onosproject.net.host.HostService;
sanghob35a6192015-04-01 13:05:26 -070033import org.onosproject.net.packet.DefaultOutboundPacket;
Charles Chan0b4e6182015-11-03 10:42:14 -080034import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
35import org.onosproject.segmentrouting.config.DeviceConfiguration;
Charles Chan03a73e02016-10-24 14:52:01 -070036import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
sanghob35a6192015-04-01 13:05:26 -070037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
40import java.nio.ByteBuffer;
Saurav Das837e0bb2015-10-30 17:45:38 -070041import java.util.Set;
Pier Ventre10bd8d12016-11-26 21:05:22 -080042import java.util.stream.Collectors;
Saurav Das837e0bb2015-10-30 17:45:38 -070043
sanghob35a6192015-04-01 13:05:26 -070044import static com.google.common.base.Preconditions.checkNotNull;
Pier Ventre10bd8d12016-11-26 21:05:22 -080045import static org.onosproject.incubator.net.neighbour.NeighbourMessageType.REQUEST;
sanghob35a6192015-04-01 13:05:26 -070046
Charles Chane849c192016-01-11 18:28:54 -080047/**
48 * Handler of ARP packets that responses or forwards ARP packets that
49 * are sent to the controller.
50 */
sanghob35a6192015-04-01 13:05:26 -070051public class ArpHandler {
52
53 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
54
55 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070056 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070057
58 /**
59 * Creates an ArpHandler object.
60 *
61 * @param srManager SegmentRoutingManager object
62 */
63 public ArpHandler(SegmentRoutingManager srManager) {
64 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070065 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070066 }
67
68 /**
69 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080070 *
sanghob35a6192015-04-01 13:05:26 -070071 * If it is an ARP request to router itself or known hosts,
72 * then it sends ARP response.
73 * If it is an ARP request to unknown hosts in its own subnet,
74 * then it flood the ARP request to the ports.
75 * If it is an ARP response, then set a flow rule for the host
76 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080077 * <p>
78 * Note: We handles all ARP packet in, even for those ARP packets between
79 * hosts in the same subnet.
80 * For an ARP packet with broadcast destination MAC,
81 * some switches pipelines will send it to the controller due to table miss,
Saurav Das49831642016-02-05 13:15:20 -080082 * other switches will flood the packets directly in the data plane without
Charles Chan68aa62d2015-11-09 16:37:23 -080083 * packet in.
84 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070085 *
Pier Ventre10bd8d12016-11-26 21:05:22 -080086 * @param pkt incoming ARP packet and context information
87 * @param hostService the host service
sanghob35a6192015-04-01 13:05:26 -070088 */
Pier Ventre10bd8d12016-11-26 21:05:22 -080089 public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
Charles Chand2edd472016-10-17 18:03:37 -070090
Charles Chan03a73e02016-10-24 14:52:01 -070091 SegmentRoutingAppConfig appConfig = srManager.cfgService
92 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Pier Ventre10bd8d12016-11-26 21:05:22 -080093 if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
Charles Chan03a73e02016-10-24 14:52:01 -070094 // Ignore ARP packets come from suppressed ports
Pier Ventref4b5fce2016-11-28 16:48:06 -080095 pkt.drop();
Charles Chan03a73e02016-10-24 14:52:01 -070096 return;
97 }
98
Pier Ventre10bd8d12016-11-26 21:05:22 -080099 if (!validateArpSpa(pkt)) {
Charles Chan505dfd82016-11-08 16:32:13 -0800100 log.debug("Ignore ARP packet discovered on {} with unexpected src protocol address {}.",
Pier Ventre10bd8d12016-11-26 21:05:22 -0800101 pkt.inPort(), pkt.sender().getIp4Address());
Pier Ventref4b5fce2016-11-28 16:48:06 -0800102 pkt.drop();
Charles Chand2edd472016-10-17 18:03:37 -0700103 return;
104 }
105
Pier Ventre10bd8d12016-11-26 21:05:22 -0800106 if (pkt.type() == REQUEST) {
107 handleArpRequest(pkt, hostService);
sanghob35a6192015-04-01 13:05:26 -0700108 } else {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800109 handleArpReply(pkt, hostService);
sanghob35a6192015-04-01 13:05:26 -0700110 }
111 }
112
Pier Ventre10bd8d12016-11-26 21:05:22 -0800113 private void handleArpRequest(NeighbourMessageContext pkt, HostService hostService) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800114 // ARP request for router. Send ARP reply.
Pier Ventre10bd8d12016-11-26 21:05:22 -0800115 if (isArpForRouter(pkt)) {
116 MacAddress targetMac = config.getRouterMacForAGatewayIp(pkt.target().getIp4Address());
117 sendArpResponse(pkt, targetMac, hostService);
Charles Chaneb088e62015-10-15 10:48:13 -0700118 } else {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800119 Set<Host> hosts = hostService.getHostsByIp(pkt.target());
120 if (hosts.size() > 1) {
121 log.warn("More than one host with the same ip {}", pkt.target());
122 }
123 Host targetHost = hosts.stream().findFirst().orElse(null);
Charles Chan68aa62d2015-11-09 16:37:23 -0800124 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700125 if (targetHost != null) {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800126 pkt.forward(targetHost.location());
Charles Chan68aa62d2015-11-09 16:37:23 -0800127 // ARP request for unknown host in the subnet. Flood in the subnet.
128 } else {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800129 flood(pkt);
Charles Chan68aa62d2015-11-09 16:37:23 -0800130 }
131 }
132 }
sanghob35a6192015-04-01 13:05:26 -0700133
Pier Ventre10bd8d12016-11-26 21:05:22 -0800134 private void handleArpReply(NeighbourMessageContext pkt, HostService hostService) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800135 // ARP reply for router. Process all pending IP packets.
Pier Ventre10bd8d12016-11-26 21:05:22 -0800136 if (isArpForRouter(pkt)) {
137 Ip4Address hostIpAddress = pkt.sender().getIp4Address();
138 srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
Charles Chan68aa62d2015-11-09 16:37:23 -0800139 } else {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800140 HostId targetHostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
141 Host targetHost = hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800142 // ARP reply for known hosts. Forward to the host.
143 if (targetHost != null) {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800144 pkt.forward(targetHost.location());
Charles Chan68aa62d2015-11-09 16:37:23 -0800145 // ARP reply for unknown host, Flood in the subnet.
146 } else {
147 // Don't flood to non-edge ports
Pier Ventre10bd8d12016-11-26 21:05:22 -0800148 if (pkt.vlan().equals(
Saurav Das4ce45962015-11-24 23:21:05 -0800149 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800150 return;
151 }
Pier Ventre10bd8d12016-11-26 21:05:22 -0800152 flood(pkt);
Charles Chaneb088e62015-10-15 10:48:13 -0700153 }
sanghob35a6192015-04-01 13:05:26 -0700154 }
155 }
156
Charles Chand2edd472016-10-17 18:03:37 -0700157 /**
158 * Check if the source protocol address of an ARP packet belongs to the same
159 * subnet configured on the port it is seen.
160 *
Pier Ventre10bd8d12016-11-26 21:05:22 -0800161 * @param pkt ARP packet and context information
Charles Chand2edd472016-10-17 18:03:37 -0700162 * @return true if the source protocol address belongs to the configured subnet
163 */
Pier Ventre10bd8d12016-11-26 21:05:22 -0800164 private boolean validateArpSpa(NeighbourMessageContext pkt) {
165 Ip4Address spa = pkt.sender().getIp4Address();
166 Set<IpPrefix> subnet = config.getPortSubnets(pkt.inPort().deviceId(), pkt.inPort().port())
167 .stream()
168 .filter(ipPrefix -> ipPrefix.isIp4() && ipPrefix.contains(spa))
169 .collect(Collectors.toSet());
170 return !subnet.isEmpty();
Charles Chand2edd472016-10-17 18:03:37 -0700171 }
172
sanghob35a6192015-04-01 13:05:26 -0700173
Pier Ventre10bd8d12016-11-26 21:05:22 -0800174 private boolean isArpForRouter(NeighbourMessageContext pkt) {
175 Ip4Address targetProtocolAddress = pkt.target().getIp4Address();
176 Set<IpAddress> gatewayIpAddresses = null;
Saurav Das4ce45962015-11-24 23:21:05 -0800177 try {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800178 if (targetProtocolAddress.equals(config.getRouterIpv4(pkt.inPort().deviceId()))) {
sanghob35a6192015-04-01 13:05:26 -0700179 return true;
180 }
Pier Ventre10bd8d12016-11-26 21:05:22 -0800181 gatewayIpAddresses = config.getPortIPs(pkt.inPort().deviceId());
Saurav Das4ce45962015-11-24 23:21:05 -0800182 } catch (DeviceConfigNotFoundException e) {
183 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
184 }
185 if (gatewayIpAddresses != null &&
186 gatewayIpAddresses.contains(targetProtocolAddress)) {
187 return true;
sanghob35a6192015-04-01 13:05:26 -0700188 }
189 return false;
190 }
191
sanghob35a6192015-04-01 13:05:26 -0700192 /**
193 * Sends an APR request for the target IP address to all ports except in-port.
194 *
195 * @param deviceId Switch device ID
196 * @param targetAddress target IP address for ARP
197 * @param inPort in-port
198 */
199 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800200 byte[] senderMacAddress;
201 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700202
Charles Chan0b4e6182015-11-03 10:42:14 -0800203 try {
204 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
Pier Ventre10bd8d12016-11-26 21:05:22 -0800205 senderIpAddress = config.getRouterIpAddressForASubnetHost(targetAddress.getIp4Address())
206 .toOctets();
Charles Chan0b4e6182015-11-03 10:42:14 -0800207 } catch (DeviceConfigNotFoundException e) {
208 log.warn(e.getMessage() + " Aborting sendArpRequest.");
209 return;
210 }
sanghob35a6192015-04-01 13:05:26 -0700211
212 ARP arpRequest = new ARP();
213 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
214 .setProtocolType(ARP.PROTO_TYPE_IP)
215 .setHardwareAddressLength(
216 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
217 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
218 .setOpCode(ARP.OP_REQUEST)
219 .setSenderHardwareAddress(senderMacAddress)
220 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
221 .setSenderProtocolAddress(senderIpAddress)
222 .setTargetProtocolAddress(targetAddress.toOctets());
223
224 Ethernet eth = new Ethernet();
225 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
226 .setSourceMACAddress(senderMacAddress)
227 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
228
Pier Ventre10bd8d12016-11-26 21:05:22 -0800229 flood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700230 }
231
Pier Ventre10bd8d12016-11-26 21:05:22 -0800232 private void sendArpResponse(NeighbourMessageContext pkt, MacAddress targetMac, HostService hostService) {
233 HostId dstId = HostId.hostId(pkt.srcMac(), pkt.vlan());
234 Host dst = hostService.getHost(dstId);
sanghob35a6192015-04-01 13:05:26 -0700235 if (dst == null) {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800236 log.warn("Cannot send ARP response to host {} - does not exist in the store",
237 dstId);
sanghob35a6192015-04-01 13:05:26 -0700238 return;
239 }
Pier Ventre10bd8d12016-11-26 21:05:22 -0800240 pkt.reply(targetMac);
sanghob35a6192015-04-01 13:05:26 -0700241 }
242
Charles Chan68aa62d2015-11-09 16:37:23 -0800243 /**
244 * Remove VLAN tag and flood to all ports in the same subnet.
245 *
246 * @param packet packet to be flooded
247 * @param inPort where the packet comes from
248 */
Pier Ventre10bd8d12016-11-26 21:05:22 -0800249 private void flood(Ethernet packet, ConnectPoint inPort) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800250 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
251 ((ARP) packet.getPayload()).getTargetProtocolAddress()
252 );
sanghob35a6192015-04-01 13:05:26 -0700253
Saurav Das7a1ffca2016-03-28 19:00:18 -0700254 try {
255 srManager.deviceConfiguration
Pier Ventre10bd8d12016-11-26 21:05:22 -0800256 .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
257 if (subnet.contains(targetProtocolAddress)) {
258 ports.stream()
259 .filter(port -> port != inPort.port())
260 .forEach(port -> {
261 forward(packet, new ConnectPoint(inPort.deviceId(), port));
262 });
263 }
264 });
265 } catch (DeviceConfigNotFoundException e) {
266 log.warn(e.getMessage()
267 + " Cannot flood in subnet as device config not available"
268 + " for device: " + inPort.deviceId());
269 }
270 }
271
272 /**
273 * Remove VLAN tag and flood to all ports in the same subnet.
274 *
275 * @param pkt arp packet to be flooded
276 */
277 private void flood(NeighbourMessageContext pkt) {
278 try {
279 srManager.deviceConfiguration
280 .getSubnetPortsMap(pkt.inPort().deviceId()).forEach((subnet, ports) -> {
281 if (subnet.contains(pkt.target())) {
Saurav Das7a1ffca2016-03-28 19:00:18 -0700282 ports.stream()
Pier Ventre10bd8d12016-11-26 21:05:22 -0800283 .filter(port -> port != pkt.inPort().port())
Saurav Das7a1ffca2016-03-28 19:00:18 -0700284 .forEach(port -> {
Pier Ventre10bd8d12016-11-26 21:05:22 -0800285 ConnectPoint outPoint = new ConnectPoint(
286 pkt.inPort().deviceId(),
287 port
288 );
289 pkt.forward(outPoint);
Saurav Das7a1ffca2016-03-28 19:00:18 -0700290 });
291 }
292 });
293 } catch (DeviceConfigNotFoundException e) {
294 log.warn(e.getMessage()
295 + " Cannot flood in subnet as device config not available"
Pier Ventre10bd8d12016-11-26 21:05:22 -0800296 + " for device: " + pkt.inPort().deviceId());
Saurav Das7a1ffca2016-03-28 19:00:18 -0700297 }
sanghob35a6192015-04-01 13:05:26 -0700298 }
299
Charles Chan68aa62d2015-11-09 16:37:23 -0800300 /**
301 * Remove VLAN tag and packet out to given port.
302 *
303 * Note: In current implementation, we expect all communication with
304 * end hosts within a subnet to be untagged.
305 * <p>
306 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
307 * removed before egress.
308 * <p>
309 * For those pipelines that do not assign internal VLAN, the packet remains
310 * untagged.
311 *
312 * @param packet packet to be forwarded
313 * @param outPort where the packet should be forwarded
314 */
Pier Ventre10bd8d12016-11-26 21:05:22 -0800315 private void forward(Ethernet packet, ConnectPoint outPort) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800316 packet.setEtherType(Ethernet.TYPE_ARP);
Charles Chan68aa62d2015-11-09 16:37:23 -0800317 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
318
319 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
320 tbuilder.setOutput(outPort.port());
321 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
322 tbuilder.build(), buf));
323 }
sanghob35a6192015-04-01 13:05:26 -0700324}