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