blob: 2c6412cf4824f908029e81800bec232b0fd43814 [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
44public class ArpHandler {
45
46 private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
47
48 private SegmentRoutingManager srManager;
sangho666cd6d2015-04-14 16:27:13 -070049 private DeviceConfiguration config;
sanghob35a6192015-04-01 13:05:26 -070050
51 /**
52 * Creates an ArpHandler object.
53 *
54 * @param srManager SegmentRoutingManager object
55 */
56 public ArpHandler(SegmentRoutingManager srManager) {
57 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070058 this.config = checkNotNull(srManager.deviceConfiguration);
sanghob35a6192015-04-01 13:05:26 -070059 }
60
61 /**
62 * Processes incoming ARP packets.
Charles Chan68aa62d2015-11-09 16:37:23 -080063 *
sanghob35a6192015-04-01 13:05:26 -070064 * If it is an ARP request to router itself or known hosts,
65 * then it sends ARP response.
66 * If it is an ARP request to unknown hosts in its own subnet,
67 * then it flood the ARP request to the ports.
68 * If it is an ARP response, then set a flow rule for the host
69 * and forward any IP packets to the host in the packet buffer to the host.
Charles Chan68aa62d2015-11-09 16:37:23 -080070 * <p>
71 * Note: We handles all ARP packet in, even for those ARP packets between
72 * hosts in the same subnet.
73 * For an ARP packet with broadcast destination MAC,
74 * some switches pipelines will send it to the controller due to table miss,
75 * other swithches will flood the packets directly in the data plane without
76 * packet in.
77 * We can deal with both cases.
sanghob35a6192015-04-01 13:05:26 -070078 *
79 * @param pkt incoming packet
80 */
81 public void processPacketIn(InboundPacket pkt) {
82
83 Ethernet ethernet = pkt.parsed();
84 ARP arp = (ARP) ethernet.getPayload();
85
86 ConnectPoint connectPoint = pkt.receivedFrom();
87 PortNumber inPort = connectPoint.port();
88 DeviceId deviceId = connectPoint.deviceId();
89 byte[] senderMacAddressByte = arp.getSenderHardwareAddress();
90 Ip4Address hostIpAddress = Ip4Address.valueOf(arp.getSenderProtocolAddress());
91
92 srManager.routingRulePopulator.populateIpRuleForHost(deviceId, hostIpAddress, MacAddress.
93 valueOf(senderMacAddressByte), inPort);
94
95 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.
sanghob35a6192015-04-01 13:05:26 -0700110 if (isArpReqForRouter(deviceId, arpRequest)) {
111 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.
133 if (isArpReqForRouter(deviceId, arpReply)) {
134 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
144 if (vlanId.equals(VlanId.vlanId(srManager.ASSIGNED_VLAN_NO_SUBNET))) {
145 return;
146 }
147 removeVlanAndFlood(payload, inPort);
Charles Chaneb088e62015-10-15 10:48:13 -0700148 }
sanghob35a6192015-04-01 13:05:26 -0700149 }
150 }
151
152
153 private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
Saurav Das837e0bb2015-10-30 17:45:38 -0700154 Set<Ip4Address> gatewayIpAddresses = config.getPortIPs(deviceId);
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700155 if (gatewayIpAddresses != null) {
sanghob35a6192015-04-01 13:05:26 -0700156 Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
157 .getTargetProtocolAddress());
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700158 if (gatewayIpAddresses.contains(targetProtocolAddress)) {
sanghob35a6192015-04-01 13:05:26 -0700159 return true;
160 }
161 }
162 return false;
163 }
164
sanghob35a6192015-04-01 13:05:26 -0700165 /**
166 * Sends an APR request for the target IP address to all ports except in-port.
167 *
168 * @param deviceId Switch device ID
169 * @param targetAddress target IP address for ARP
170 * @param inPort in-port
171 */
172 public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800173 byte[] senderMacAddress;
174 byte[] senderIpAddress;
sanghob35a6192015-04-01 13:05:26 -0700175
Charles Chan0b4e6182015-11-03 10:42:14 -0800176 try {
177 senderMacAddress = config.getDeviceMac(deviceId).toBytes();
178 senderIpAddress = config.getRouterIp(deviceId).toOctets();
179 } catch (DeviceConfigNotFoundException e) {
180 log.warn(e.getMessage() + " Aborting sendArpRequest.");
181 return;
182 }
sanghob35a6192015-04-01 13:05:26 -0700183
184 ARP arpRequest = new ARP();
185 arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
186 .setProtocolType(ARP.PROTO_TYPE_IP)
187 .setHardwareAddressLength(
188 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
189 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
190 .setOpCode(ARP.OP_REQUEST)
191 .setSenderHardwareAddress(senderMacAddress)
192 .setTargetHardwareAddress(MacAddress.ZERO.toBytes())
193 .setSenderProtocolAddress(senderIpAddress)
194 .setTargetProtocolAddress(targetAddress.toOctets());
195
196 Ethernet eth = new Ethernet();
197 eth.setDestinationMACAddress(MacAddress.BROADCAST.toBytes())
198 .setSourceMACAddress(senderMacAddress)
199 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
200
Charles Chan68aa62d2015-11-09 16:37:23 -0800201 removeVlanAndFlood(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700202 }
203
Charles Chan68aa62d2015-11-09 16:37:23 -0800204 private void sendArpResponse(ARP arpRequest, MacAddress targetMac, VlanId vlanId) {
sanghob35a6192015-04-01 13:05:26 -0700205 ARP arpReply = new ARP();
206 arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
207 .setProtocolType(ARP.PROTO_TYPE_IP)
208 .setHardwareAddressLength(
209 (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
210 .setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH)
211 .setOpCode(ARP.OP_REPLY)
212 .setSenderHardwareAddress(targetMac.toBytes())
213 .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
214 .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
215 .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
216
217 Ethernet eth = new Ethernet();
218 eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
219 .setSourceMACAddress(targetMac.toBytes())
220 .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
221
222
Charles Chan68aa62d2015-11-09 16:37:23 -0800223 HostId dstId = HostId.hostId(
224 MacAddress.valueOf(arpReply.getTargetHardwareAddress()),
225 vlanId);
sanghob35a6192015-04-01 13:05:26 -0700226 Host dst = srManager.hostService.getHost(dstId);
227 if (dst == null) {
228 log.warn("Cannot send ARP response to unknown device");
229 return;
230 }
231
232 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
233 setOutput(dst.location().port()).build();
234 OutboundPacket packet = new DefaultOutboundPacket(dst.location().deviceId(),
235 treatment, ByteBuffer.wrap(eth.serialize()));
236
237 srManager.packetService.emit(packet);
238 }
239
Charles Chan68aa62d2015-11-09 16:37:23 -0800240 /**
241 * Remove VLAN tag and flood to all ports in the same subnet.
242 *
243 * @param packet packet to be flooded
244 * @param inPort where the packet comes from
245 */
246 private void removeVlanAndFlood(Ethernet packet, ConnectPoint inPort) {
247 Ip4Address targetProtocolAddress = Ip4Address.valueOf(
248 ((ARP) packet.getPayload()).getTargetProtocolAddress()
249 );
sanghob35a6192015-04-01 13:05:26 -0700250
Charles Chan68aa62d2015-11-09 16:37:23 -0800251 srManager.deviceConfiguration.getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
252 if (subnet.contains(targetProtocolAddress)) {
253 ports.stream()
254 .filter(port -> port != inPort.port())
255 .forEach(port -> {
256 removeVlanAndForward(packet, new ConnectPoint(inPort.deviceId(), port));
257 });
sanghob35a6192015-04-01 13:05:26 -0700258 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800259 });
sanghob35a6192015-04-01 13:05:26 -0700260 }
261
Charles Chan68aa62d2015-11-09 16:37:23 -0800262 /**
263 * Remove VLAN tag and packet out to given port.
264 *
265 * Note: In current implementation, we expect all communication with
266 * end hosts within a subnet to be untagged.
267 * <p>
268 * For those pipelines that internally assigns a VLAN, the VLAN tag will be
269 * removed before egress.
270 * <p>
271 * For those pipelines that do not assign internal VLAN, the packet remains
272 * untagged.
273 *
274 * @param packet packet to be forwarded
275 * @param outPort where the packet should be forwarded
276 */
277 private void removeVlanAndForward(Ethernet packet, ConnectPoint outPort) {
278 packet.setEtherType(Ethernet.TYPE_ARP);
279 packet.setVlanID(Ethernet.VLAN_UNTAGGED);
280 ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
281
282 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
283 tbuilder.setOutput(outPort.port());
284 srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
285 tbuilder.build(), buf));
286 }
sanghob35a6192015-04-01 13:05:26 -0700287}