blob: 388e3131a97bbd4c00d697d6ee6349b06413d809 [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
sanghob35a6192015-04-01 13:05:26 -070022import org.onlab.packet.MacAddress;
Charles Chan68aa62d2015-11-09 16:37:23 -080023import org.onlab.packet.VlanId;
sanghob35a6192015-04-01 13:05:26 -070024import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.Host;
sanghob35a6192015-04-01 13:05:26 -070027import org.onosproject.net.PortNumber;
28import 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) {
86
87 Ethernet ethernet = pkt.parsed();
88 ARP arp = (ARP) ethernet.getPayload();
89
90 ConnectPoint connectPoint = pkt.receivedFrom();
91 PortNumber inPort = connectPoint.port();
92 DeviceId deviceId = connectPoint.deviceId();
93 byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
94 Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
sanghob35a6192015-04-01 13:05:26 -070095 if (arp.getOpCode() == ARP.OP_REQUEST) {
96 handleArpRequest(deviceId, connectPoint, ethernet);
97 } else {
Charles Chan68aa62d2015-11-09 16:37:23 -080098 handleArpReply(deviceId, connectPoint, ethernet);
sanghob35a6192015-04-01 13:05:26 -070099 }
100 }
101
102 private void handleArpRequest(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
sanghob35a6192015-04-01 13:05:26 -0700103 ARP arpRequest = (ARP) payload.getPayload();
Charles Chan68aa62d2015-11-09 16:37:23 -0800104 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
sanghob35a6192015-04-01 13:05:26 -0700105 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
Charles Chan68aa62d2015-11-09 16:37:23 -0800106 arpRequest.getTargetHardwareAddress()),
107 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700108
Charles Chan68aa62d2015-11-09 16:37:23 -0800109 // ARP request for router. Send ARP reply.
Saurav Das4ce45962015-11-24 23:21:05 -0800110 if (isArpForRouter(deviceId, arpRequest)) {
sanghob35a6192015-04-01 13:05:26 -0700111 Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
Charles Chan68aa62d2015-11-09 16:37:23 -0800112 sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
Charles Chaneb088e62015-10-15 10:48:13 -0700113 } else {
114 Host targetHost = srManager.hostService.getHost(targetHostId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800115 // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
Charles Chaneb088e62015-10-15 10:48:13 -0700116 if (targetHost != null) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800117 removeVlanAndForward(payload, targetHost.location());
118 // ARP request for unknown host in the subnet. Flood in the subnet.
119 } else {
120 removeVlanAndFlood(payload, inPort);
121 }
122 }
123 }
sanghob35a6192015-04-01 13:05:26 -0700124
Charles Chan68aa62d2015-11-09 16:37:23 -0800125 private void handleArpReply(DeviceId deviceId, ConnectPoint inPort, Ethernet payload) {
126 ARP arpReply = (ARP) payload.getPayload();
127 VlanId vlanId = VlanId.vlanId(payload.getVlanID());
128 HostId targetHostId = HostId.hostId(MacAddress.valueOf(
129 arpReply.getTargetHardwareAddress()),
130 vlanId);
131
132 // ARP reply for router. Process all pending IP packets.
Saurav Das4ce45962015-11-24 23:21:05 -0800133 if (isArpForRouter(deviceId, arpReply)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800134 Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
135 srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
136 } else {
137 Host targetHost = srManager.hostService.getHost(targetHostId);
138 // ARP reply for known hosts. Forward to the host.
139 if (targetHost != null) {
140 removeVlanAndForward(payload, targetHost.location());
141 // ARP reply for unknown host, Flood in the subnet.
142 } else {
143 // Don't flood to non-edge ports
Saurav Das4ce45962015-11-24 23:21:05 -0800144 if (vlanId.equals(
145 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800146 return;
147 }
148 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700149 }
sanghob35a6192015-04-01 13:05:26 -0700150 }
151 }
152
153
Saurav Das4ce45962015-11-24 23:21:05 -0800154 private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
155 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
156 arpMsg.getTargetProtocolAddress());
157 Set<Ip4Address> gatewayIpAddresses = null;
158 try {
159 if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
sanghob35a6192015-04-01 13:05:26 -0700160 return true;
161 }
Saurav Das4ce45962015-11-24 23:21:05 -0800162 gatewayIpAddresses = config.getPortIPs(deviceId);
163 } catch (DeviceConfigNotFoundException e) {
164 log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
165 }
166 if (gatewayIpAddresses != null &&
167 gatewayIpAddresses.contains(targetProtocolAddress)) {
168 return true;
sanghob35a6192015-04-01 13:05:26 -0700169 }
170 return false;
171 }
172
sanghob35a6192015-04-01 13:05:26 -0700173 /**
174 * Sends an APR request for the target IP address to all ports except in-port.
175 *
176 * @param deviceId Switch device ID
177 * @param targetAddress target IP address for ARP
178 * @param inPort in-port
179 */
180 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800181 byte[] senderMacAddress;
182 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700183
Charles Chan0b4e6182015-11-03 10:42:14 -0800184 try {
185 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
186 senderIpAddress = config.getRouterIp(deviceId).toOctets();
187 } catch (DeviceConfigNotFoundException e) {
188 log.warn(e.getMessage() + " Aborting sendArpRequest.");
189 return;
190 }
sanghob35a6192015-04-01 13:05:26 -0700191
192 ARP arpRequest = new ARP();
193 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
194 .setProtocolType(ARP.PROTO_TYPE_IP)
195 .setHardwareAddressLength(
196 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
197 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
198 .setOpCode(ARP.OP_REQUEST)
199 .setSenderHardwareAddress(senderMacAddress)
200 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
201 .setSenderProtocolAddress(senderIpAddress)
202 .setTargetProtocolAddress(targetAddress.toOctets());
203
204 Ethernet eth = new Ethernet();
205 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
206 .setSourceMACAddress(senderMacAddress)
207 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
208
Charles Chan68aa62d2015-11-09 16:37:23 -0800209 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700210 }
211
Charles Chan68aa62d2015-11-09 16:37:23 -0800212 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700213 ARP arpReply = new ARP();
214 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
215 .setProtocolType(ARP.PROTO_TYPE_IP)
216 .setHardwareAddressLength(
217 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
218 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
219 .setOpCode(ARP.OP_REPLY)
220 .setSenderHardwareAddress(targetMac.toBytes())
221 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
222 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
223 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
224
225 Ethernet eth = new Ethernet();
226 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
227 .setSourceMACAddress(targetMac.toBytes())
228 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
229
Flavio Castro6816f252016-01-14 14:53:33 -0800230 MacAddress hostMac = MacAddress.valueOf(arpReply.getTargetHardwareAddress());
231 HostId dstId = HostId.hostId(hostMac, vlanId);
sanghob35a6192015-04-01 13:05:26 -0700232 Host dst = srManager.hostService.getHost(dstId);
233 if (dst == null) {
Flavio Castro6816f252016-01-14 14:53:33 -0800234 log.warn("Cannot send ARP response to host {}", dstId);
sanghob35a6192015-04-01 13:05:26 -0700235 return;
236 }
237
238 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
239 setOutput(dst.location().port()).build();
240 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
241 treatment, ByteBuffer.wrap(eth.serialize()));
242
243 srManager.packetService.emit(packet);
244 }
245
Charles Chan68aa62d2015-11-09 16:37:23 -0800246 /**
247 * Remove VLAN tag and flood to all ports in the same subnet.
248 *
249 * @param packet packet to be flooded
250 * @param inPort where the packet comes from
251 */
252 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
253 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
254 ((ARP) packet.getPayload()).getTargetProtocolAddress()
255 );
sanghob35a6192015-04-01 13:05:26 -0700256
Charles Chan68aa62d2015-11-09 16:37:23 -0800257 srManager.deviceConfiguration.getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
258 if (subnet.contains(targetProtocolAddress)) {
259 ports.stream()
260 .filter(port -> port != inPort.port())
261 .forEach(port -> {
262 removeVlanAndForward(packet, new ConnectPoint(inPort.deviceId(), port));
263 });
sanghob35a6192015-04-01 13:05:26 -0700264 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800265 });
sanghob35a6192015-04-01 13:05:26 -0700266 }
267
Charles Chan68aa62d2015-11-09 16:37:23 -0800268 /**
269 * Remove VLAN tag and packet out to given port.
270 *
271 * Note: In current implementation, we expect all communication with
272 * end hosts within a subnet to be untagged.
273 * <p>
274 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
275 * removed before egress.
276 * <p>
277 * For those pipelines that do not assign internal VLAN, the packet remains
278 * untagged.
279 *
280 * @param packet packet to be forwarded
281 * @param outPort where the packet should be forwarded
282 */
283 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
284 packet.setEtherType(Ethernet.TYPE_ARP);
285 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
286 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
287
288 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
289 tbuilder.setOutput(outPort.port());
290 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
291 tbuilder.build(), buf));
292 }
sanghob35a6192015-04-01 13:05:26 -0700293}