blob: b4ae36c6b89d06a1be4ea3b3d669f225f51d4316 [file] [log] [blame]
Hyunsun Moonb974fca2016-06-30 21:20:39 -07001/*
2* Copyright 2016-present 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*/
Hyunsun Moon05400872017-02-07 17:11:25 +090016package org.onosproject.openstacknetworking.impl;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070017
18import com.google.common.base.Strings;
19import com.google.common.collect.Sets;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.ARP;
28import org.onlab.packet.Ethernet;
29import org.onlab.packet.Ip4Address;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.MacAddress;
32import org.onlab.util.Tools;
33import org.onosproject.net.Host;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficTreatment;
36import org.onosproject.net.packet.DefaultOutboundPacket;
37import org.onosproject.net.packet.PacketContext;
38import org.onosproject.net.packet.PacketProcessor;
39import org.onosproject.net.packet.PacketService;
40import org.onosproject.openstackinterface.OpenstackInterfaceService;
41import org.onosproject.openstackinterface.OpenstackNetwork;
42import org.onosproject.openstackinterface.OpenstackPort;
43import org.osgi.service.component.ComponentContext;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46import java.nio.ByteBuffer;
47import java.util.Dictionary;
48import java.util.Set;
49
50import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moon05400872017-02-07 17:11:25 +090051import static org.onosproject.openstacknetworking.api.Constants.*;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070052
53/**
54 * Handles ARP packet from VMs.
55 */
56@Component(immediate = true)
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070057public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
Hyunsun Moonb974fca2016-06-30 21:20:39 -070058
59 private final Logger log = LoggerFactory.getLogger(getClass());
60
61 private static final String GATEWAY_MAC = "gatewayMac";
Hyunsun Moonb974fca2016-06-30 21:20:39 -070062
63 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 protected PacketService packetService;
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected OpenstackInterfaceService openstackService;
68
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070069 @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
Hyunsun Moonb974fca2016-06-30 21:20:39 -070070 label = "Fake MAC address for virtual network subnet gateway")
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070071 private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
Hyunsun Moonb974fca2016-06-30 21:20:39 -070072
73 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
74 private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
75
76 @Activate
77 protected void activate() {
78 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
79 super.activate();
80 }
81
82 @Deactivate
83 protected void deactivate() {
84 packetService.removeProcessor(packetProcessor);
85 super.deactivate();
86 }
87
88 @Modified
89 protected void modified(ComponentContext context) {
90 Dictionary<?, ?> properties = context.getProperties();
91 String updatedMac;
92
93 updatedMac = Tools.get(properties, GATEWAY_MAC);
94 if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(gatewayMac)) {
95 gatewayMac = updatedMac;
96 }
97
98 log.info("Modified");
99 }
100
101 /**
102 * Processes ARP request packets.
103 * It checks if the target IP is owned by a known host first and then ask to
104 * OpenStack if it's not. This ARP proxy does not support overlapping IP.
105 *
106 * @param context packet context
107 * @param ethPacket ethernet packet
108 */
109 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
110 ARP arpPacket = (ARP) ethPacket.getPayload();
111 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
112 return;
113 }
114
115 Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
116 MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
117 getMacFromHostService(targetIp);
118 if (replyMac.equals(MacAddress.NONE)) {
119 replyMac = getMacFromOpenstack(targetIp);
120 }
121
122 if (replyMac == MacAddress.NONE) {
123 log.debug("Failed to find MAC address for {}", targetIp.toString());
124 return;
125 }
126
127 Ethernet ethReply = ARP.buildArpReply(
128 targetIp.getIp4Address(),
129 replyMac,
130 ethPacket);
131
132 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
133 .setOutput(context.inPacket().receivedFrom().port())
134 .build();
135
136 packetService.emit(new DefaultOutboundPacket(
137 context.inPacket().receivedFrom().deviceId(),
138 treatment,
139 ByteBuffer.wrap(ethReply.serialize())));
140 }
141
142 /**
143 * Returns MAC address of a host with a given target IP address by asking to
144 * OpenStack. It does not support overlapping IP.
145 *
146 * @param targetIp target ip address
147 * @return mac address, or null if it fails to fetch the mac
148 */
149 private MacAddress getMacFromOpenstack(IpAddress targetIp) {
150 checkNotNull(targetIp);
151
152 OpenstackPort openstackPort = openstackService.ports()
153 .stream()
154 .filter(port -> port.fixedIps().containsValue(targetIp.getIp4Address()))
155 .findFirst()
156 .orElse(null);
157
158 if (openstackPort != null) {
159 log.debug("Found MAC from OpenStack for {}", targetIp.toString());
160 return openstackPort.macAddress();
161 } else {
162 return MacAddress.NONE;
163 }
164 }
165
166 /**
167 * Returns MAC address of a host with a given target IP address by asking to
168 * host service. It does not support overlapping IP.
169 *
170 * @param targetIp target ip
171 * @return mac address, or null if it fails to find the mac
172 */
173 private MacAddress getMacFromHostService(IpAddress targetIp) {
174 checkNotNull(targetIp);
175
176 Host host = hostService.getHostsByIp(targetIp)
177 .stream()
178 .findFirst()
179 .orElse(null);
180
181 if (host != null) {
182 log.debug("Found MAC from host service for {}", targetIp.toString());
183 return host.mac();
184 } else {
185 return MacAddress.NONE;
186 }
187 }
188
189 @Override
190 protected void hostDetected(Host host) {
191 OpenstackNetwork osNet = openstackService.network(host.annotations().value(NETWORK_ID));
192 if (osNet == null) {
193 log.warn("Failed to get OpenStack network for {}", host);
194 return;
195 }
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700196 osNet.subnets().forEach(subnet -> gateways.add(Ip4Address.valueOf(subnet.gatewayIp())));
Hyunsun Moonb974fca2016-06-30 21:20:39 -0700197 }
198
199 @Override
200 protected void hostRemoved(Host host) {
201 // TODO remove subnet gateway from gateways if no hosts exists on that subnet
202 }
203
204 private class InternalPacketProcessor implements PacketProcessor {
205
206 @Override
207 public void process(PacketContext context) {
208 if (context.isHandled()) {
209 return;
210 }
211
212 Ethernet ethPacket = context.inPacket().parsed();
213 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
214 return;
215 }
216 processPacketIn(context, ethPacket);
217 }
218 }
219}