blob: 4529389136cae21f0118b0158155b494e32aa915 [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;
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;
42
sanghob35a6192015-04-01 13:05:26 -070043import static com.google.common.base.Preconditions.checkNotNull;
44
Charles Chane849c192016-01-11 18:28:54 -080045/**
46 * Handler of ARP packets that responses or forwards ARP packets that
47 * are sent to the controller.
48 */
sanghob35a6192015-04-01 13:05:26 -070049public class ArpHandler {
50
51 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
52
53 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070054 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070055
56 /**
57 * Creates an ArpHandler object.
58 *
59 * @param srManager SegmentRoutingManager object
60 */
61 public ArpHandler(SegmentRoutingManager srManager) {
62 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070063 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070064 }
65
66 /**
67 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080068 *
sanghob35a6192015-04-01 13:05:26 -070069 * If it is an ARP request to router itself or known hosts,
70 * then it sends ARP response.
71 * If it is an ARP request to unknown hosts in its own subnet,
72 * then it flood the ARP request to the ports.
73 * If it is an ARP response, then set a flow rule for the host
74 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080075 * <p>
76 * Note: We handles all ARP packet in, even for those ARP packets between
77 * hosts in the same subnet.
78 * For an ARP packet with broadcast destination MAC,
79 * some switches pipelines will send it to the controller due to table miss,
Saurav Das49831642016-02-05 13:15:20 -080080 * other switches will flood the packets directly in the data plane without
Charles Chan68aa62d2015-11-09 16:37:23 -080081 * packet in.
82 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070083 *
84 * @param pkt incoming packet
85 */
86 public void processPacketIn(InboundPacket pkt) {
sanghob35a6192015-04-01 13:05:26 -070087 Ethernet ethernet = pkt.parsed();
88 ARP arp = (ARP) ethernet.getPayload();
sanghob35a6192015-04-01 13:05:26 -070089 ConnectPoint connectPoint = pkt.receivedFrom();
sanghob35a6192015-04-01 13:05:26 -070090 DeviceId deviceId = connectPoint.deviceId();
Charles Chand2edd472016-10-17 18:03:37 -070091
Charles Chan03a73e02016-10-24 14:52:01 -070092 SegmentRoutingAppConfig appConfig = srManager.cfgService
93 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
94 if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
95 // Ignore ARP packets come from suppressed ports
96 return;
97 }
98
Charles Chand2edd472016-10-17 18:03:37 -070099 if (!validateArpSpa(connectPoint, arp)) {
Charles Chan505dfd82016-11-08 16:32:13 -0800100 log.debug("Ignore ARP packet discovered on {} with unexpected src protocol address {}.",
Charles Chand2edd472016-10-17 18:03:37 -0700101 connectPoint, Ip4Address.valueOf(arp.getSenderProtocolAddress()));
102 return;
103 }
104
sanghob35a6192015-04-01 13:05:26 -0700105 if (arp.getOpCode() == ARP.OP_REQUEST) {
106 handleArpRequest(deviceId, connectPoint, ethernet);
107 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -0800108 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -0700109 }
110 }
111
112 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -0700113 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800114 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700115 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800116 arpRequest.getTargetHardwareAddress()),
117 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700118
Charles Chan68aa62d2015-11-09 16:37:23 -0800119 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800120 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700121 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800122 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700123 } else {
124 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800125 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700126 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800127 removeVlanAndForward(payload, targetHost.location());
128 // ARP request for unknown host in the subnet. Flood in the subnet.
129 } else {
130 removeVlanAndFlood(payload, inPort);
131 }
132 }
133 }
sanghob35a6192015-04-01 13:05:26 -0700134
Charles Chan68aa62d2015-11-09 16:37:23 -0800135 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
136 ARP arpReply = (ARP) payload.getPayload();
137 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
138 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
139 arpReply.getTargetHardwareAddress()),
140 vlanId);
141
142 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800143 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800144 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
145 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
146 } else {
147 Host targetHost = srManager.hostService.getHost(targetHostId);
148 // ARP reply for known hosts. Forward to the host.
149 if (targetHost != null) {
150 removeVlanAndForward(payload, targetHost.location());
151 // ARP reply for unknown host, Flood in the subnet.
152 } else {
153 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800154 if (vlanId.equals(
155 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800156 return;
157 }
158 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700159 }
sanghob35a6192015-04-01 13:05:26 -0700160 }
161 }
162
Charles Chand2edd472016-10-17 18:03:37 -0700163 /**
164 * Check if the source protocol address of an ARP packet belongs to the same
165 * subnet configured on the port it is seen.
166 *
167 * @param connectPoint connect point where the ARP packet is seen
168 * @param arpPacket ARP packet
169 * @return true if the source protocol address belongs to the configured subnet
170 */
171 private boolean validateArpSpa(ConnectPoint connectPoint, ARP arpPacket) {
172 Ip4Address spa = Ip4Address.valueOf(arpPacket.getSenderProtocolAddress());
173 Ip4Prefix subnet = config.getPortSubnet(connectPoint.deviceId(), connectPoint.port());
Charles Chan505dfd82016-11-08 16:32:13 -0800174 return subnet != null && subnet.contains(spa);
Charles Chand2edd472016-10-17 18:03:37 -0700175 }
176
sanghob35a6192015-04-01 13:05:26 -0700177
Saurav Das4ce45962015-11-24 23:21:05 -0800178 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
179 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
180 arpMsg.getTargetProtocolAddress());
181 Set<Ip4Address> gatewayIpAddresses = null;
182 try {
183 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700184 return true;
185 }
Saurav Das4ce45962015-11-24 23:21:05 -0800186 gatewayIpAddresses = config.getPortIPs(deviceId);
187 } catch (DeviceConfigNotFoundException e) {
188 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
189 }
190 if (gatewayIpAddresses != null &&
191 gatewayIpAddresses.contains(targetProtocolAddress)) {
192 return true;
sanghob35a6192015-04-01 13:05:26 -0700193 }
194 return false;
195 }
196
sanghob35a6192015-04-01 13:05:26 -0700197 /**
198 * Sends an APR request for the target IP address to all ports except in-port.
199 *
200 * @param deviceId Switch device ID
201 * @param targetAddress target IP address for ARP
202 * @param inPort in-port
203 */
204 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800205 byte[] senderMacAddress;
206 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700207
Charles Chan0b4e6182015-11-03 10:42:14 -0800208 try {
209 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
210 senderIpAddress = config.getRouterIp(deviceId).toOctets();
211 } catch (DeviceConfigNotFoundException e) {
212 log.warn(e.getMessage() + " Aborting sendArpRequest.");
213 return;
214 }
sanghob35a6192015-04-01 13:05:26 -0700215
216 ARP arpRequest = new ARP();
217 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
218 .setProtocolType(ARP.PROTO_TYPE_IP)
219 .setHardwareAddressLength(
220 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
221 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
222 .setOpCode(ARP.OP_REQUEST)
223 .setSenderHardwareAddress(senderMacAddress)
224 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
225 .setSenderProtocolAddress(senderIpAddress)
226 .setTargetProtocolAddress(targetAddress.toOctets());
227
228 Ethernet eth = new Ethernet();
229 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
230 .setSourceMACAddress(senderMacAddress)
231 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
232
Charles Chan68aa62d2015-11-09 16:37:23 -0800233 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700234 }
235
Charles Chan68aa62d2015-11-09 16:37:23 -0800236 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700237 ARP arpReply = new ARP();
238 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
239 .setProtocolType(ARP.PROTO_TYPE_IP)
240 .setHardwareAddressLength(
241 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
242 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
243 .setOpCode(ARP.OP_REPLY)
244 .setSenderHardwareAddress(targetMac.toBytes())
245 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
246 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
247 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
248
249 Ethernet eth = new Ethernet();
250 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
251 .setSourceMACAddress(targetMac.toBytes())
252 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
253
Flavio Castro6816f252016-01-14 14:53:33 -0800254 MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress());
255 HostId dstId = HostId.hostId(hostMac, vlanId);
sanghob35a6192015-04-01 13:05:26 -0700256 Host dst = srManager.hostService.getHost(dstId);
257 if (dst == null) {
Flavio Castro6816f252016-01-14 14:53:33 -0800258 log.warn("Cannot send ARP response to host {}", dstId);
sanghob35a6192015-04-01 13:05:26 -0700259 return;
260 }
261
262 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
263 setOutput(dst.location().port()).build();
264 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
265 treatment, ByteBuffer.wrap(eth.serialize()));
266
267 srManager.packetService.emit(packet);
268 }
269
Charles Chan68aa62d2015-11-09 16:37:23 -0800270 /**
271 * Remove VLAN tag and flood to all ports in the same subnet.
272 *
273 * @param packet packet to be flooded
274 * @param inPort where the packet comes from
275 */
276 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
277 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
278 ((ARP) packet.getPayload()).getTargetProtocolAddress()
279 );
sanghob35a6192015-04-01 13:05:26 -0700280
Saurav Das7a1ffca2016-03-28 19:00:18 -0700281 try {
282 srManager.deviceConfiguration
283 .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
284 if (subnet.contains(targetProtocolAddress)) {
285 ports.stream()
286 .filter(port -> port != inPort.port())
287 .forEach(port -> {
288 removeVlanAndForward(packet,
289 new ConnectPoint(inPort.deviceId(), port));
290 });
291 }
292 });
293 } catch (DeviceConfigNotFoundException e) {
294 log.warn(e.getMessage()
295 + " Cannot flood in subnet as device config not available"
296 + " for device: " + inPort.deviceId());
297 }
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 */
315 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
316 packet.setEtherType(Ethernet.TYPE_ARP);
317 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
318 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
319
320 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
321 tbuilder.setOutput(outPort.port());
322 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
323 tbuilder.build(), buf));
324 }
sanghob35a6192015-04-01 13:05:26 -0700325}