blob: 2cde3b9ace58d4f7710d8e3f66bb7d86a11c7fe5 [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;
Charles Chand2edd472016-10-17 18:03:37 -070021import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070022import org.onlab.packet.IpAddress;
sanghob35a6192015-04-01 13:05:26 -070023import org.onlab.packet.MacAddress;
Charles Chan68aa62d2015-11-09 16:37:23 -080024import org.onlab.packet.VlanId;
sanghob35a6192015-04-01 13:05:26 -070025import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.Host;
sanghob35a6192015-04-01 13:05:26 -070028import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.packet.DefaultOutboundPacket;
31import org.onosproject.net.packet.InboundPacket;
32import org.onosproject.net.HostId;
33import org.onosproject.net.packet.OutboundPacket;
Charles Chan0b4e6182015-11-03 10:42:14 -080034import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
35import org.onosproject.segmentrouting.config.DeviceConfiguration;
sanghob35a6192015-04-01 13:05:26 -070036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.nio.ByteBuffer;
Saurav Das837e0bb2015-10-30 17:45:38 -070040import java.util.Set;
41
sanghob35a6192015-04-01 13:05:26 -070042import static com.google.common.base.Preconditions.checkNotNull;
43
Charles Chane849c192016-01-11 18:28:54 -080044/**
45 * Handler of ARP packets that responses or forwards ARP packets that
46 * are sent to the controller.
47 */
sanghob35a6192015-04-01 13:05:26 -070048public class ArpHandler {
49
50 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
51
52 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070053 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070054
55 /**
56 * Creates an ArpHandler object.
57 *
58 * @param srManager SegmentRoutingManager object
59 */
60 public ArpHandler(SegmentRoutingManager srManager) {
61 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070062 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070063 }
64
65 /**
66 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080067 *
sanghob35a6192015-04-01 13:05:26 -070068 * If it is an ARP request to router itself or known hosts,
69 * then it sends ARP response.
70 * If it is an ARP request to unknown hosts in its own subnet,
71 * then it flood the ARP request to the ports.
72 * If it is an ARP response, then set a flow rule for the host
73 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080074 * <p>
75 * Note: We handles all ARP packet in, even for those ARP packets between
76 * hosts in the same subnet.
77 * For an ARP packet with broadcast destination MAC,
78 * some switches pipelines will send it to the controller due to table miss,
Saurav Das49831642016-02-05 13:15:20 -080079 * other switches will flood the packets directly in the data plane without
Charles Chan68aa62d2015-11-09 16:37:23 -080080 * packet in.
81 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070082 *
83 * @param pkt incoming packet
84 */
85 public void processPacketIn(InboundPacket pkt) {
sanghob35a6192015-04-01 13:05:26 -070086 Ethernet ethernet = pkt.parsed();
87 ARP arp = (ARP) ethernet.getPayload();
sanghob35a6192015-04-01 13:05:26 -070088 ConnectPoint connectPoint = pkt.receivedFrom();
sanghob35a6192015-04-01 13:05:26 -070089 DeviceId deviceId = connectPoint.deviceId();
Charles Chand2edd472016-10-17 18:03:37 -070090
91 if (!validateArpSpa(connectPoint, arp)) {
Charles Chan505dfd82016-11-08 16:32:13 -080092 log.debug("Ignore ARP packet discovered on {} with unexpected src protocol address {}.",
Charles Chand2edd472016-10-17 18:03:37 -070093 connectPoint, Ip4Address.valueOf(arp.getSenderProtocolAddress()));
94 return;
95 }
96
sanghob35a6192015-04-01 13:05:26 -070097 if (arp.getOpCode() == ARP.OP_REQUEST) {
98 handleArpRequest(deviceId, connectPoint, ethernet);
99 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -0800100 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -0700101 }
102 }
103
104 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -0700105 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800106 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700107 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800108 arpRequest.getTargetHardwareAddress()),
109 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700110
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800112 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700113 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800114 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700115 } else {
116 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800117 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700118 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800119 removeVlanAndForward(payload, targetHost.location());
120 // ARP request for unknown host in the subnet. Flood in the subnet.
121 } else {
122 removeVlanAndFlood(payload, inPort);
123 }
124 }
125 }
sanghob35a6192015-04-01 13:05:26 -0700126
Charles Chan68aa62d2015-11-09 16:37:23 -0800127 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
128 ARP arpReply = (ARP) payload.getPayload();
129 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
130 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
131 arpReply.getTargetHardwareAddress()),
132 vlanId);
133
134 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800135 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800136 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
137 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
138 } else {
139 Host targetHost = srManager.hostService.getHost(targetHostId);
140 // ARP reply for known hosts. Forward to the host.
141 if (targetHost != null) {
142 removeVlanAndForward(payload, targetHost.location());
143 // ARP reply for unknown host, Flood in the subnet.
144 } else {
145 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800146 if (vlanId.equals(
147 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800148 return;
149 }
150 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700151 }
sanghob35a6192015-04-01 13:05:26 -0700152 }
153 }
154
Charles Chand2edd472016-10-17 18:03:37 -0700155 /**
156 * Check if the source protocol address of an ARP packet belongs to the same
157 * subnet configured on the port it is seen.
158 *
159 * @param connectPoint connect point where the ARP packet is seen
160 * @param arpPacket ARP packet
161 * @return true if the source protocol address belongs to the configured subnet
162 */
163 private boolean validateArpSpa(ConnectPoint connectPoint, ARP arpPacket) {
164 Ip4Address spa = Ip4Address.valueOf(arpPacket.getSenderProtocolAddress());
165 Ip4Prefix subnet = config.getPortSubnet(connectPoint.deviceId(), connectPoint.port());
Charles Chan505dfd82016-11-08 16:32:13 -0800166 return subnet != null && subnet.contains(spa);
Charles Chand2edd472016-10-17 18:03:37 -0700167 }
168
sanghob35a6192015-04-01 13:05:26 -0700169
Saurav Das4ce45962015-11-24 23:21:05 -0800170 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
171 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
172 arpMsg.getTargetProtocolAddress());
173 Set<Ip4Address> gatewayIpAddresses = null;
174 try {
175 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700176 return true;
177 }
Saurav Das4ce45962015-11-24 23:21:05 -0800178 gatewayIpAddresses = config.getPortIPs(deviceId);
179 } catch (DeviceConfigNotFoundException e) {
180 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
181 }
182 if (gatewayIpAddresses != null &&
183 gatewayIpAddresses.contains(targetProtocolAddress)) {
184 return true;
sanghob35a6192015-04-01 13:05:26 -0700185 }
186 return false;
187 }
188
sanghob35a6192015-04-01 13:05:26 -0700189 /**
190 * Sends an APR request for the target IP address to all ports except in-port.
191 *
192 * @param deviceId Switch device ID
193 * @param targetAddress target IP address for ARP
194 * @param inPort in-port
195 */
196 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800197 byte[] senderMacAddress;
198 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700199
Charles Chan0b4e6182015-11-03 10:42:14 -0800200 try {
201 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
202 senderIpAddress = config.getRouterIp(deviceId).toOctets();
203 } catch (DeviceConfigNotFoundException e) {
204 log.warn(e.getMessage() + " Aborting sendArpRequest.");
205 return;
206 }
sanghob35a6192015-04-01 13:05:26 -0700207
208 ARP arpRequest = new ARP();
209 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
210 .setProtocolType(ARP.PROTO_TYPE_IP)
211 .setHardwareAddressLength(
212 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
213 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
214 .setOpCode(ARP.OP_REQUEST)
215 .setSenderHardwareAddress(senderMacAddress)
216 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
217 .setSenderProtocolAddress(senderIpAddress)
218 .setTargetProtocolAddress(targetAddress.toOctets());
219
220 Ethernet eth = new Ethernet();
221 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
222 .setSourceMACAddress(senderMacAddress)
223 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
224
Charles Chan68aa62d2015-11-09 16:37:23 -0800225 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700226 }
227
Charles Chan68aa62d2015-11-09 16:37:23 -0800228 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700229 ARP arpReply = new ARP();
230 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
231 .setProtocolType(ARP.PROTO_TYPE_IP)
232 .setHardwareAddressLength(
233 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
234 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
235 .setOpCode(ARP.OP_REPLY)
236 .setSenderHardwareAddress(targetMac.toBytes())
237 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
238 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
239 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
240
241 Ethernet eth = new Ethernet();
242 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
243 .setSourceMACAddress(targetMac.toBytes())
244 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
245
Flavio Castro6816f252016-01-14 14:53:33 -0800246 MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress());
247 HostId dstId = HostId.hostId(hostMac, vlanId);
sanghob35a6192015-04-01 13:05:26 -0700248 Host dst = srManager.hostService.getHost(dstId);
249 if (dst == null) {
Flavio Castro6816f252016-01-14 14:53:33 -0800250 log.warn("Cannot send ARP response to host {}", dstId);
sanghob35a6192015-04-01 13:05:26 -0700251 return;
252 }
253
254 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
255 setOutput(dst.location().port()).build();
256 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
257 treatment, ByteBuffer.wrap(eth.serialize()));
258
259 srManager.packetService.emit(packet);
260 }
261
Charles Chan68aa62d2015-11-09 16:37:23 -0800262 /**
263 * Remove VLAN tag and flood to all ports in the same subnet.
264 *
265 * @param packet packet to be flooded
266 * @param inPort where the packet comes from
267 */
268 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
269 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
270 ((ARP) packet.getPayload()).getTargetProtocolAddress()
271 );
sanghob35a6192015-04-01 13:05:26 -0700272
Saurav Das7a1ffca2016-03-28 19:00:18 -0700273 try {
274 srManager.deviceConfiguration
275 .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
276 if (subnet.contains(targetProtocolAddress)) {
277 ports.stream()
278 .filter(port -> port != inPort.port())
279 .forEach(port -> {
280 removeVlanAndForward(packet,
281 new ConnectPoint(inPort.deviceId(), port));
282 });
283 }
284 });
285 } catch (DeviceConfigNotFoundException e) {
286 log.warn(e.getMessage()
287 + " Cannot flood in subnet as device config not available"
288 + " for device: " + inPort.deviceId());
289 }
sanghob35a6192015-04-01 13:05:26 -0700290 }
291
Charles Chan68aa62d2015-11-09 16:37:23 -0800292 /**
293 * Remove VLAN tag and packet out to given port.
294 *
295 * Note: In current implementation, we expect all communication with
296 * end hosts within a subnet to be untagged.
297 * <p>
298 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
299 * removed before egress.
300 * <p>
301 * For those pipelines that do not assign internal VLAN, the packet remains
302 * untagged.
303 *
304 * @param packet packet to be forwarded
305 * @param outPort where the packet should be forwarded
306 */
307 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
308 packet.setEtherType(Ethernet.TYPE_ARP);
309 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
310 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
311
312 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
313 tbuilder.setOutput(outPort.port());
314 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
315 tbuilder.build(), buf));
316 }
sanghob35a6192015-04-01 13:05:26 -0700317}